]> git.ipfire.org Git - thirdparty/squid.git/blame - src/tools.cc
Docs: Copyright updates for 2018 (#114)
[thirdparty/squid.git] / src / tools.cc
CommitLineData
30a4f2a8 1/*
5b74111a 2 * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
019dd986 7 */
44a47c6e 8
bbc27441
AJ
9/* DEBUG: section 21 Misc Functions */
10
582c2af2 11#include "squid.h"
602d9612 12#include "anyp/PortCfg.h"
e0d28505 13#include "base/Subscription.h"
93da1f99 14#include "client_side.h"
2745fea5 15#include "fatal.h"
528b2c61 16#include "fde.h"
95e6d864 17#include "fqdncache.h"
b3f7fd88 18#include "fs_io.h"
65d448bc 19#include "htcp.h"
d3dddfb5 20#include "http/Stream.h"
e0d28505 21#include "ICP.h"
96d89ea0 22#include "ip/Intercept.h"
425de4c8 23#include "ip/QosConfig.h"
602d9612
A
24#include "ipc/Coordinator.h"
25#include "ipc/Kids.h"
26#include "ipcache.h"
0eb49b6d 27#include "MemBuf.h"
e99fa721 28#include "sbuf/Stream.h"
4d5904f7 29#include "SquidConfig.h"
a98bcbee 30#include "SquidMath.h"
985c86bc 31#include "SquidTime.h"
2745fea5 32#include "store/Disks.h"
602d9612 33#include "tools.h"
27bc2077 34#include "wordlist.h"
44a47c6e 35
1a30fdf5 36#include <cerrno>
62ae0622 37#if HAVE_SYS_PRCTL_H
38#include <sys/prctl.h>
39#endif
e30fe60a
AJ
40#if HAVE_WIN32_PSAPI
41#include <psapi.h>
42#endif
582c2af2
FC
43#if HAVE_SYS_STAT_H
44#include <sys/stat.h>
45#endif
46#if HAVE_SYS_WAIT_H
47#include <sys/wait.h>
48#endif
49#if HAVE_GRP_H
50#include <grp.h>
51#endif
62ae0622 52
090089c4 53#define DEAD_MSG "\
c5c666ab 54The Squid Cache (version %s) died.\n\
090089c4 55\n\
c5c666ab 56You've encountered a fatal error in the Squid Cache version %s.\n\
090089c4 57If a core file was created (possibly in the swap directory),\n\
b8de7ebe 58please execute 'gdb squid core' or 'dbx squid core', then type 'where',\n\
8311b837 59and report the trace back to squid-bugs@lists.squid-cache.org.\n\
090089c4 60\n\
61Thanks!\n"
62
f5b8bbc4 63static void mail_warranty(void);
ff2afd7f 64static void restoreCapabilities(bool keep);
a2c48c98 65int DebugSignal = -1;
8b505ba9 66SBuf service_name(APP_SHORTNAME);
24382924 67
1191b93b 68#if _SQUID_LINUX_
941a3077 69/* Workaround for crappy glic header files */
e6ccf245 70SQUIDCEXTERN int backtrace(void *, int);
71SQUIDCEXTERN void backtrace_symbols_fd(void *, int, int);
72SQUIDCEXTERN int setresuid(uid_t, uid_t, uid_t);
10da753f
FC
73#else /* _SQUID_LINUX_ */
74/* needed on Opensolaris for backtrace_symbols_fd */
75#if HAVE_EXECINFO_H
76#include <execinfo.h>
77#endif /* HAVE_EXECINFO_H */
78
941a3077 79#endif /* _SQUID_LINUX */
80
4a5a6c62 81void
429fdbec 82releaseServerSockets(void)
83{
65d448bc 84 // Release the main ports as early as possible
62e76326 85
e7ce227f 86 // clear http_port, https_port, and ftp_port lists
434a79b0 87 clientConnectionsClose();
62e76326 88
e0d28505 89 // clear icp_port's
65d448bc 90 icpClosePorts();
62e76326 91
e0d28505 92 // XXX: Why not the HTCP, SNMP, DNS ports as well?
65d448bc 93 // XXX: why does this differ from main closeServerConnections() anyway ?
429fdbec 94}
95
b8d8561b 96static char *
0673c0ba 97dead_msg(void)
090089c4 98{
95d659f0 99 LOCAL_ARRAY(char, msg, 1024);
56878878 100 snprintf(msg, 1024, DEAD_MSG, version_string, version_string);
090089c4 101 return msg;
102}
103
24382924 104static void
0673c0ba 105mail_warranty(void)
090089c4 106{
019dd986 107 FILE *fp = NULL;
6e40f263 108 static char command[256];
50536c47 109
a2c13db1
AJ
110 /*
111 * NP: umask() takes the mask of bits we DONT want set.
112 *
113 * We want the current user to have read/write access
114 * and since this file will be passed to mailsystem,
115 * the group and other must have read access.
116 */
117 const mode_t prev_umask=umask(S_IXUSR|S_IXGRP|S_IWGRP|S_IWOTH|S_IXOTH);
62e76326 118
50536c47 119#if HAVE_MKSTEMP
b99a6dec 120 char filename[] = "/tmp/squid-XXXXXX";
121 int tfd = mkstemp(filename);
50536c47
FC
122 if (tfd < 0 || (fp = fdopen(tfd, "w")) == NULL) {
123 umask(prev_umask);
62e76326 124 return;
50536c47 125 }
b99a6dec 126#else
127 char *filename;
50536c47
FC
128 // XXX tempnam is obsolete since POSIX.2008-1
129 // tmpfile is not an option, we want the created files to stick around
130 if ((filename = tempnam(NULL, APP_SHORTNAME)) == NULL ||
6a1a5b78 131 (fp = fopen(filename, "w")) == NULL) {
50536c47 132 umask(prev_umask);
62e76326 133 return;
50536c47 134 }
b99a6dec 135#endif
50536c47 136 umask(prev_umask);
62e76326 137
abacf776 138 if (Config.EmailFrom)
139 fprintf(fp, "From: %s\n", Config.EmailFrom);
140 else
7dbca7a4 141 fprintf(fp, "From: %s@%s\n", APP_SHORTNAME, uniqueHostname());
62e76326 142
edae7bc0 143 fprintf(fp, "To: %s\n", Config.adminEmail);
6e40f263 144 fprintf(fp, "Subject: %s\n", dead_msg());
145 fclose(fp);
62e76326 146
d084bf20 147 snprintf(command, 256, "%s %s < %s", Config.EmailProgram, Config.adminEmail, filename);
f53969cc 148 if (system(command)) {} /* XXX should avoid system(3) */
6e40f263 149 unlink(filename);
87cedd31
AJ
150#if !HAVE_MKSTEMP
151 xfree(filename); // tempnam() requires us to free its allocation
152#endif
090089c4 153}
154
4a5a6c62 155void
43c3424b 156dumpMallocStats(void)
3f43b19b 157{
88738790 158#if HAVE_MSTATS && HAVE_GNUMALLOC_H
62e76326 159
88738790 160 struct mstats ms = mstats();
161 fprintf(debug_log, "\ttotal space in arena: %6d KB\n",
62e76326 162 (int) (ms.bytes_total >> 10));
88738790 163 fprintf(debug_log, "\tTotal free: %6d KB %d%%\n",
62e76326 164 (int) (ms.bytes_free >> 10),
a98bcbee 165 Math::intPercent(ms.bytes_free, ms.bytes_total));
4572073a 166#endif
3f43b19b 167}
30a4f2a8 168
f2908497 169void
170squid_getrusage(struct rusage *r)
f0f81709 171{
8da94b66 172 memset(r, '\0', sizeof(struct rusage));
e30fe60a 173#if HAVE_GETRUSAGE && defined(RUSAGE_SELF) && !_SQUID_WINDOWS_
1da023b5 174#if _SQUID_SOLARIS_
cd19f0b6 175 /* Solaris 2.5 has getrusage() permission bug -- Arjan de Vet */
176 enter_suid();
177#endif
62e76326 178
f2908497 179 getrusage(RUSAGE_SELF, r);
62e76326 180
e30fe60a 181#if _SQUID_SOLARIS_
cd19f0b6 182 leave_suid();
183#endif
e30fe60a
AJ
184
185#elif _SQUID_WINDOWS_ && HAVE_WIN32_PSAPI
186 // Windows has an alternative method if there is no POSIX getrusage defined.
187 if (WIN32_OS_version >= _WIN_OS_WINNT) {
188 /* On Windows NT and later call PSAPI.DLL for process Memory */
189 /* informations -- Guido Serassio */
190 HANDLE hProcess;
191 PROCESS_MEMORY_COUNTERS pmc;
192 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
193 PROCESS_VM_READ,
194 FALSE, GetCurrentProcessId());
195 {
196 /* Microsoft CRT doesn't have getrusage function, */
197 /* so we get process CPU time information from PSAPI.DLL. */
198 FILETIME ftCreate, ftExit, ftKernel, ftUser;
199 if (GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) {
200 int64_t *ptUser = (int64_t *)&ftUser;
201 int64_t tUser64 = *ptUser / 10;
202 int64_t *ptKernel = (int64_t *)&ftKernel;
203 int64_t tKernel64 = *ptKernel / 10;
204 r->ru_utime.tv_sec =(long)(tUser64 / 1000000);
205 r->ru_stime.tv_sec =(long)(tKernel64 / 1000000);
206 r->ru_utime.tv_usec =(long)(tUser64 % 1000000);
207 r->ru_stime.tv_usec =(long)(tKernel64 % 1000000);
208 } else {
209 CloseHandle( hProcess );
210 return;
211 }
212 }
213 if (GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc))) {
214 r->ru_maxrss=(DWORD)(pmc.WorkingSetSize / getpagesize());
215 r->ru_majflt=pmc.PageFaultCount;
216 } else {
217 CloseHandle( hProcess );
218 return;
219 }
220
221 CloseHandle( hProcess );
222 }
56983a01 223#endif
f2908497 224}
225
226double
62e76326 227
f2908497 228rusage_cputime(struct rusage *r)
229{
230 return (double) r->ru_stime.tv_sec +
62e76326 231 (double) r->ru_utime.tv_sec +
232 (double) r->ru_stime.tv_usec / 1000000.0 +
233 (double) r->ru_utime.tv_usec / 1000000.0;
f2908497 234}
235
ec603b25 236/* Hack for some HP-UX preprocessors */
237#ifndef HAVE_GETPAGESIZE
238#define HAVE_GETPAGESIZE 0
239#endif
240
f2908497 241int
62e76326 242
f2908497 243rusage_maxrss(struct rusage *r)
244{
8a09e810 245#if _SQUID_SGI_ && _ABIAPI
4fdc3986 246 return r->ru_pad[0];
8a09e810 247#elif _SQUID_SGI_|| _SQUID_OSF_ || _SQUID_AIX_ || defined(BSD4_4)
62e76326 248
f2908497 249 return r->ru_maxrss;
6b8e7481 250#elif defined(HAVE_GETPAGESIZE) && HAVE_GETPAGESIZE != 0
62e76326 251
f2908497 252 return (r->ru_maxrss * getpagesize()) >> 10;
c7a1083d 253#elif defined(PAGESIZE)
62e76326 254
c7a1083d 255 return (r->ru_maxrss * PAGESIZE) >> 10;
8505e57b 256#else
62e76326 257
8505e57b 258 return r->ru_maxrss;
f2908497 259#endif
260}
261
262int
62e76326 263
f2908497 264rusage_pagefaults(struct rusage *r)
265{
8a09e810 266#if _SQUID_SGI_ && _ABIAPI
4fdc3986 267 return r->ru_pad[5];
268#else
62e76326 269
f2908497 270 return r->ru_majflt;
4fdc3986 271#endif
f2908497 272}
273
4a5a6c62 274void
f2908497 275PrintRusage(void)
276{
62e76326 277
f2908497 278 struct rusage rusage;
279 squid_getrusage(&rusage);
2b906e48 280 fprintf(debug_log, "CPU Usage: %.3f seconds = %.3f user + %.3f sys\n",
62e76326 281 rusage_cputime(&rusage),
282 rusage.ru_utime.tv_sec + ((double) rusage.ru_utime.tv_usec / 1000000.0),
283 rusage.ru_stime.tv_sec + ((double) rusage.ru_stime.tv_usec / 1000000.0));
f2908497 284 fprintf(debug_log, "Maximum Resident Size: %d KB\n",
62e76326 285 rusage_maxrss(&rusage));
f2908497 286 fprintf(debug_log, "Page faults with physical i/o: %d\n",
62e76326 287 rusage_pagefaults(&rusage));
f0f81709 288}
289
b8d8561b 290void
291death(int sig)
44a47c6e 292{
0e08ce4b 293 if (sig == SIGSEGV)
62e76326 294 fprintf(debug_log, "FATAL: Received Segment Violation...dying.\n");
0e08ce4b 295 else if (sig == SIGBUS)
62e76326 296 fprintf(debug_log, "FATAL: Received Bus Error...dying.\n");
0e08ce4b 297 else
62e76326 298 fprintf(debug_log, "FATAL: Received signal %d...dying.\n", sig);
0b4639af 299
0a081cb0 300#if PRINT_STACK_TRACE
1191b93b 301#if _SQUID_HPUX_
0b4639af 302 {
f53969cc 303 extern void U_STACK_TRACE(void); /* link with -lcl */
62e76326 304 fflush(debug_log);
305 dup2(fileno(debug_log), 2);
306 U_STACK_TRACE();
0b4639af 307 }
62e76326 308
0b4639af 309#endif /* _SQUID_HPUX_ */
8a09e810 310#if _SQUID_SOLARIS_ && HAVE_LIBOPCOM_STACK
f53969cc
SM
311 { /* get ftp://opcom.sun.ca/pub/tars/opcom_stack.tar.gz and */
312 extern void opcom_stack_trace(void); /* link with -lopcom_stack */
62e76326 313 fflush(debug_log);
314 dup2(fileno(debug_log), fileno(stdout));
315 opcom_stack_trace();
316 fflush(stdout);
0b4639af 317 }
62e76326 318
10da753f 319#endif /* _SQUID_SOLARIS_and HAVE_LIBOPCOM_STACK */
ce3d30fb 320#if HAVE_BACKTRACE_SYMBOLS_FD
321 {
62e76326 322 static void *(callarray[8192]);
323 int n;
324 n = backtrace(callarray, 8192);
325 backtrace_symbols_fd(callarray, n, fileno(debug_log));
ce3d30fb 326 }
62e76326 327
ce3d30fb 328#endif
0b4639af 329#endif /* PRINT_STACK_TRACE */
330
7aa9bb3e 331#if SA_RESETHAND == 0 && !_SQUID_WINDOWS_
44a47c6e 332 signal(SIGSEGV, SIG_DFL);
62e76326 333
44a47c6e 334 signal(SIGBUS, SIG_DFL);
62e76326 335
0e08ce4b 336 signal(sig, SIG_DFL);
62e76326 337
30a4f2a8 338#endif
62e76326 339
429fdbec 340 releaseServerSockets();
62e76326 341
e3ef2b09 342 storeDirWriteCleanLogs(0);
62e76326 343
4b0f5de8 344 if (!shutting_down) {
345 PrintRusage();
62e76326 346
4b0f5de8 347 dumpMallocStats();
348 }
62e76326 349
6e40f263 350 if (squid_curtime - SQUID_RELEASE_TIME < 864000) {
62e76326 351 /* skip if more than 10 days old */
352
353 if (Config.adminEmail)
354 mail_warranty();
329ce686 355
356 puts(dead_msg());
6e40f263 357 }
62e76326 358
44a47c6e 359 abort();
360}
361
a2c48c98
AR
362void
363BroadcastSignalIfAny(int& sig)
364{
365 if (sig > 0) {
dbf55289
CT
366 if (IamMasterProcess()) {
367 for (int i = TheKids.count() - 1; i >= 0; --i) {
00e2479d
AR
368 const auto &kid = TheKids.get(i);
369 if (kid.running())
370 kill(kid.getPid(), sig);
dbf55289
CT
371 }
372 }
a2c48c98
AR
373 sig = -1;
374 }
375}
44a47c6e 376
b8d8561b 377void
23d92c64 378sigusr2_handle(int sig)
090089c4 379{
30a4f2a8 380 static int state = 0;
96e03dd8 381 /* no debugs() here; bad things happen if the signal is delivered during _db_print() */
62e76326 382
a2c48c98
AR
383 DebugSignal = sig;
384
30a4f2a8 385 if (state == 0) {
d7298d24 386 Debug::parseOptions("ALL,7");
62e76326 387 state = 1;
30a4f2a8 388 } else {
62493678 389 Debug::parseOptions(Debug::debugOptions);
62e76326 390 state = 0;
30a4f2a8 391 }
62e76326 392
30a4f2a8 393#if !HAVE_SIGACTION
b69e9ffa
AJ
394 /* reinstall */
395 if (signal(sig, sigusr2_handle) == SIG_ERR) {
396 int xerrno = errno;
397 debugs(50, DBG_CRITICAL, "signal: sig=" << sig << " func=sigusr2_handle: " << xstrerr(xerrno));
398 }
090089c4 399#endif
400}
401
b8d8561b 402void
af6a12ee
AJ
403debug_trap(const char *message)
404{
4a69c163 405 if (!opt_catch_signals)
62e76326 406 fatal_dump(message);
407
a3d5953d 408 _db_print("WARNING: %s\n", message);
85b701ed 409}
410
0ee4272b 411const char *
0673c0ba 412getMyHostname(void)
44a47c6e 413{
95d659f0 414 LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN + 1);
44a47c6e 415 static int present = 0;
cc192b50 416 struct addrinfo *AI = NULL;
b7ac5457 417 Ip::Address sa;
62e76326 418
af85f811 419 if (Config.visibleHostname != NULL)
62e76326 420 return Config.visibleHostname;
421
d20b1cd0 422 if (present)
62e76326 423 return host;
424
d20b1cd0 425 host[0] = '\0';
62e76326 426
fa720bfb
AJ
427 if (HttpPortList != NULL && sa.isAnyAddr())
428 sa = HttpPortList->s;
62e76326 429
aa2b5399 430 /*
431 * If the first http_port address has a specific address, try a
432 * reverse DNS lookup on it.
433 */
4dd643d5 434 if ( !sa.isAnyAddr() ) {
62e76326 435
4dd643d5 436 sa.getAddrInfo(AI);
cc192b50 437 /* we are looking for a name. */
27bc2077 438 if (getnameinfo(AI->ai_addr, AI->ai_addrlen, host, SQUIDHOSTNAMELEN, NULL, 0, NI_NAMEREQD ) == 0) {
62e76326 439 /* DNS lookup successful */
440 /* use the official name from DNS lookup */
cc192b50 441 debugs(50, 4, "getMyHostname: resolved " << sa << " to '" << host << "'");
bf8fe701 442
62e76326 443 present = 1;
444
851614a8 445 Ip::Address::FreeAddr(AI);
cc192b50 446
62e76326 447 if (strchr(host, '.'))
448 return host;
62e76326 449 }
450
851614a8 451 Ip::Address::FreeAddr(AI);
c19d1320
AJ
452 debugs(50, 2, "WARNING: failed to resolve " << sa << " to a fully qualified hostname");
453 }
454
455 // still no host. fallback to gethostname()
456 if (gethostname(host, SQUIDHOSTNAMELEN) < 0) {
b69e9ffa
AJ
457 int xerrno = errno;
458 debugs(50, DBG_IMPORTANT, "WARNING: gethostname failed: " << xstrerr(xerrno));
26ac0430 459 } else {
c19d1320
AJ
460 /* Verify that the hostname given resolves properly */
461 struct addrinfo hints;
462 memset(&hints, 0, sizeof(addrinfo));
463 hints.ai_flags = AI_CANONNAME;
62e76326 464
c19d1320
AJ
465 if (getaddrinfo(host, NULL, NULL, &AI) == 0) {
466 /* DNS lookup successful */
467 /* use the official name from DNS lookup */
468 debugs(50, 6, "getMyHostname: '" << host << "' has DNS resolution.");
469 present = 1;
470
471 /* AYJ: do we want to flag AI_ALL and cache the result anywhere. ie as our local host IPs? */
4dd643d5 472 if (AI)
c19d1320 473 freeaddrinfo(AI);
cc192b50 474
c19d1320 475 return host;
cc192b50 476 }
b69e9ffa 477 int xerrno = errno;
c19d1320 478
4dd643d5
AJ
479 if (AI)
480 freeaddrinfo(AI);
b69e9ffa 481 debugs(50, DBG_IMPORTANT, "WARNING: '" << host << "' rDNS test failed: " << xstrerr(xerrno));
44a47c6e 482 }
62e76326 483
c19d1320
AJ
484 /* throw a configuration error when the Host/IP given has bad DNS/rDNS. */
485 debugs(50, DBG_CRITICAL, "WARNING: Could not determine this machines public hostname. " <<
486 "Please configure one or set 'visible_hostname'.");
a572d8be 487
c19d1320 488 return ("localhost");
44a47c6e 489}
490
98829f69 491const char *
492uniqueHostname(void)
493{
23c6ebc4 494 debugs(21, 3, HERE << " Config: '" << Config.uniqueHostname << "'");
98829f69 495 return Config.uniqueHostname ? Config.uniqueHostname : getMyHostname();
496}
497
23c6ebc4 498/** leave a priviliged section. (Give up any privilegies)
30a4f2a8 499 * Routines that need privilegies can rap themselves in enter_suid()
500 * and leave_suid()
501 * To give upp all posibilites to gain privilegies use no_suid()
12b9e9b1 502 */
b8d8561b 503void
0673c0ba 504leave_suid(void)
12b9e9b1 505{
36584579 506 debugs(21, 3, "leave_suid: PID " << getpid() << " called");
62e76326 507
e3d74828 508 if (Config.effectiveGroup) {
e3d74828 509#if HAVE_SETGROUPS
e3d74828 510 setgroups(1, &Config2.effectiveGroupID);
e3d74828 511#endif
512
b69e9ffa
AJ
513 if (setgid(Config2.effectiveGroupID) < 0) {
514 int xerrno = errno;
515 debugs(50, DBG_CRITICAL, "ALERT: setgid: " << xstrerr(xerrno));
516 }
e3d74828 517 }
518
12b9e9b1 519 if (geteuid() != 0)
62e76326 520 return;
521
12b9e9b1 522 /* Started as a root, check suid option */
b6f794d6 523 if (Config.effectiveUser == NULL)
62e76326 524 return;
525
36584579 526 debugs(21, 3, "leave_suid: PID " << getpid() << " giving up root, becoming '" << Config.effectiveUser << "'");
62e76326 527
e3d74828 528 if (!Config.effectiveGroup) {
62e76326 529
b69e9ffa
AJ
530 if (setgid(Config2.effectiveGroupID) < 0) {
531 int xerrno = errno;
532 debugs(50, DBG_CRITICAL, "ALERT: setgid: " << xstrerr(xerrno));
533 }
62e76326 534
e3d74828 535 if (initgroups(Config.effectiveUser, Config2.effectiveGroupID) < 0) {
fa84c01d 536 debugs(50, DBG_CRITICAL, "ALERT: initgroups: unable to set groups for User " <<
bf8fe701 537 Config.effectiveUser << " and Group " <<
538 (unsigned) Config2.effectiveGroupID << "");
e3d74828 539 }
540 }
62e76326 541
234967c9 542#if HAVE_SETRESUID
989393f2 543 if (setresuid(Config2.effectiveUserID, Config2.effectiveUserID, 0) < 0) {
544 const auto xerrno = errno;
545 fatalf("FATAL: setresuid: %s", xstrerr(xerrno));
546 }
62e76326 547
234967c9 548#elif HAVE_SETEUID
989393f2 549 if (seteuid(Config2.effectiveUserID) < 0) {
550 const auto xerrno = errno;
551 fatalf("FATAL: seteuid: %s", xstrerr(xerrno));
552 }
62e76326 553
234967c9 554#else
989393f2 555 if (setuid(Config2.effectiveUserID) < 0) {
556 const auto xerrno = errno;
557 fatalf("FATAL: setuid: %s", xstrerr(xerrno));
558 }
62e76326 559
fc68f6b1 560#endif
fc68f6b1 561
ff2afd7f 562 restoreCapabilities(true);
fc68f6b1 563
62ae0622 564#if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
565 /* Set Linux DUMPABLE flag */
b69e9ffa
AJ
566 if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0) {
567 int xerrno = errno;
568 debugs(50, 2, "ALERT: prctl: " << xstrerr(xerrno));
569 }
234967c9 570#endif
571}
572
30a4f2a8 573/* Enter a privilegied section */
b8d8561b 574void
0673c0ba 575enter_suid(void)
234967c9 576{
77468ee5 577 debugs(21, 3, "enter_suid: PID " << getpid() << " taking root privileges");
234967c9 578#if HAVE_SETRESUID
989393f2 579 if (setresuid((uid_t)-1, 0, (uid_t)-1) < 0) {
580 const auto xerrno = errno;
581 debugs (21, 3, "enter_suid: setresuid failed: " << xstrerr(xerrno));
582 }
234967c9 583#else
62e76326 584
234967c9 585 setuid(0);
586#endif
62ae0622 587#if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
588 /* Set Linux DUMPABLE flag */
589
b69e9ffa
AJ
590 if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0) {
591 int xerrno = errno;
592 debugs(50, 2, "ALERT: prctl: " << xstrerr(xerrno));
593 }
62ae0622 594#endif
234967c9 595}
596
30a4f2a8 597/* Give up the posibility to gain privilegies.
598 * this should be used before starting a sub process
599 */
b8d8561b 600void
0673c0ba 601no_suid(void)
234967c9 602{
603 uid_t uid;
30a4f2a8 604 leave_suid();
234967c9 605 uid = geteuid();
329ce686 606 debugs(21, 3, "no_suid: PID " << getpid() << " giving up root priveleges forever");
62e76326 607
b69e9ffa
AJ
608 if (setuid(0) < 0) {
609 int xerrno = errno;
610 debugs(50, DBG_IMPORTANT, "WARNING: no_suid: setuid(0): " << xstrerr(xerrno));
611 }
62e76326 612
b69e9ffa
AJ
613 if (setuid(uid) < 0) {
614 int xerrno = errno;
615 debugs(50, DBG_IMPORTANT, "ERROR: no_suid: setuid(" << uid << "): " << xstrerr(xerrno));
616 }
62e76326 617
ff2afd7f 618 restoreCapabilities(false);
329ce686 619
62ae0622 620#if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
621 /* Set Linux DUMPABLE flag */
b69e9ffa
AJ
622 if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0) {
623 int xerrno = errno;
624 debugs(50, 2, "ALERT: prctl: " << xstrerr(xerrno));
625 }
234967c9 626#endif
12b9e9b1 627}
ccff9601 628
a2c48c98 629bool
7de94c8c 630IamMasterProcess()
a2c48c98 631{
7de94c8c
AR
632 return KidIdentifier == 0;
633}
634
635bool
636IamWorkerProcess()
637{
638 // when there is only one process, it has to be the worker
13aeac35 639 if (opt_no_daemon || Config.workers == 0)
7de94c8c
AR
640 return true;
641
095ec2b1
AR
642 return TheProcessKind == pkWorker;
643}
644
645bool
646IamDiskProcess()
647{
648 return TheProcessKind == pkDisker;
7de94c8c
AR
649}
650
96c2bb61
AR
651bool
652InDaemonMode()
653{
654 return !opt_no_daemon && Config.workers > 0;
655}
656
7de94c8c
AR
657bool
658UsingSmp()
659{
095ec2b1 660 return InDaemonMode() && NumberOfKids() > 1;
7de94c8c
AR
661}
662
663bool
664IamCoordinatorProcess()
665{
095ec2b1 666 return TheProcessKind == pkCoordinator;
7de94c8c
AR
667}
668
669bool
670IamPrimaryProcess()
671{
672 // when there is only one process, it has to be primary
13aeac35 673 if (opt_no_daemon || Config.workers == 0)
7de94c8c
AR
674 return true;
675
676 // when there is a master and worker process, the master delegates
677 // primary functions to its only kid
254912f3 678 if (NumberOfKids() == 1)
7de94c8c 679 return IamWorkerProcess();
a2c48c98 680
7de94c8c
AR
681 // in SMP mode, multiple kids delegate primary functions to the coordinator
682 return IamCoordinatorProcess();
a2c48c98
AR
683}
684
96c2bb61
AR
685int
686NumberOfKids()
687{
688 // no kids in no-daemon mode
689 if (!InDaemonMode())
690 return 0;
691
095ec2b1
AR
692 // XXX: detect and abort when called before workers/cache_dirs are parsed
693
14911a4e 694 const int rockDirs = Config.cacheSwap.n_strands;
095ec2b1
AR
695
696 const bool needCoord = Config.workers > 1 || rockDirs > 0;
697 return (needCoord ? 1 : 0) + Config.workers + rockDirs;
698}
96c2bb61 699
1fdc4f42 700SBuf
095ec2b1
AR
701ProcessRoles()
702{
1fdc4f42 703 SBuf roles;
095ec2b1
AR
704 if (IamMasterProcess())
705 roles.append(" master");
706 if (IamCoordinatorProcess())
707 roles.append(" coordinator");
708 if (IamWorkerProcess())
709 roles.append(" worker");
710 if (IamDiskProcess())
711 roles.append(" disker");
712 return roles;
96c2bb61
AR
713}
714
f3f0f563
AJ
715/* A little piece of glue for odd systems */
716#ifndef RLIMIT_NOFILE
717#ifdef RLIMIT_OFILE
718#define RLIMIT_NOFILE RLIMIT_OFILE
719#endif
720#endif
7690e8eb 721
f3f0f563 722/** Figure out the number of supported filedescriptors */
b8d8561b 723void
0673c0ba 724setMaxFD(void)
c4fb974e 725{
f3f0f563 726#if HAVE_SETRLIMIT && defined(RLIMIT_NOFILE)
8527a56f 727
9064ed81
A
728 /* On Linux with 64-bit file support the sys/resource.h header
729 * uses #define to change the function definition to require rlimit64
730 */
8527a56f
AJ
731#if defined(getrlimit)
732 struct rlimit64 rl; // Assume its a 64-bit redefine anyways.
733#else
c4fb974e 734 struct rlimit rl;
8527a56f
AJ
735#endif
736
c4fb974e 737 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
b69e9ffa
AJ
738 int xerrno = errno;
739 debugs(50, DBG_CRITICAL, "getrlimit: RLIMIT_NOFILE: " << xstrerr(xerrno));
f3f0f563 740 } else if (Config.max_filedescriptors > 0) {
0979b3a6
AJ
741#if USE_SELECT || USE_SELECT_WIN32
742 /* select() breaks if this gets set too big */
1b7fae06 743 if (Config.max_filedescriptors > FD_SETSIZE) {
0979b3a6 744 rl.rlim_cur = FD_SETSIZE;
1b7fae06
AJ
745 debugs(50, DBG_CRITICAL, "WARNING: 'max_filedescriptors " << Config.max_filedescriptors << "' does not work with select()");
746 } else
0979b3a6
AJ
747#endif
748 rl.rlim_cur = Config.max_filedescriptors;
62e76326 749 if (rl.rlim_cur > rl.rlim_max)
f3f0f563
AJ
750 rl.rlim_max = rl.rlim_cur;
751 if (setrlimit(RLIMIT_NOFILE, &rl)) {
b69e9ffa
AJ
752 int xerrno = errno;
753 debugs(50, DBG_CRITICAL, "ERROR: setrlimit: RLIMIT_NOFILE: " << xstrerr(xerrno));
f3f0f563
AJ
754 getrlimit(RLIMIT_NOFILE, &rl);
755 rl.rlim_cur = rl.rlim_max;
756 if (setrlimit(RLIMIT_NOFILE, &rl)) {
b69e9ffa
AJ
757 xerrno = errno;
758 debugs(50, DBG_CRITICAL, "ERROR: setrlimit: RLIMIT_NOFILE: " << xstrerr(xerrno));
f3f0f563 759 }
62e76326 760 }
c4fb974e 761 }
f3f0f563 762 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
b69e9ffa
AJ
763 int xerrno = errno;
764 debugs(50, DBG_CRITICAL, "ERROR: getrlimit: RLIMIT_NOFILE: " << xstrerr(xerrno));
c4fb974e 765 } else {
f3f0f563
AJ
766 Squid_MaxFD = rl.rlim_cur;
767 }
62e76326 768
f3f0f563
AJ
769#endif /* HAVE_SETRLIMIT */
770}
62e76326 771
f3f0f563
AJ
772void
773setSystemLimits(void)
774{
be266cb2 775#if HAVE_SETRLIMIT && defined(RLIMIT_NOFILE) && !_SQUID_CYGWIN_
f3f0f563 776 /* limit system filedescriptors to our own limit */
8527a56f
AJ
777
778 /* On Linux with 64-bit file support the sys/resource.h header
779 * uses #define to change the function definition to require rlimit64
780 */
781#if defined(getrlimit)
782 struct rlimit64 rl; // Assume its a 64-bit redefine anyways.
783#else
f3f0f563 784 struct rlimit rl;
8527a56f
AJ
785#endif
786
f3f0f563 787 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
b69e9ffa
AJ
788 int xerrno = errno;
789 debugs(50, DBG_CRITICAL, "getrlimit: RLIMIT_NOFILE: " << xstrerr(xerrno));
f3f0f563
AJ
790 } else {
791 rl.rlim_cur = Squid_MaxFD;
792 if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
b69e9ffa
AJ
793 int xerrno = errno;
794 snprintf(tmp_error_buf, ERROR_BUF_SZ, "setrlimit: RLIMIT_NOFILE: %s", xstrerr(xerrno));
62e76326 795 fatal_dump(tmp_error_buf);
796 }
c4fb974e 797 }
30a4f2a8 798#endif /* HAVE_SETRLIMIT */
799
a396d1f8 800#if HAVE_SETRLIMIT && defined(RLIMIT_DATA) && !_SQUID_CYGWIN_
30a4f2a8 801 if (getrlimit(RLIMIT_DATA, &rl) < 0) {
b69e9ffa
AJ
802 int xerrno = errno;
803 debugs(50, DBG_CRITICAL, "getrlimit: RLIMIT_DATA: " << xstrerr(xerrno));
88738790 804 } else if (rl.rlim_max > rl.rlim_cur) {
f53969cc 805 rl.rlim_cur = rl.rlim_max; /* set it to the max */
62e76326 806
807 if (setrlimit(RLIMIT_DATA, &rl) < 0) {
b69e9ffa
AJ
808 int xerrno = errno;
809 snprintf(tmp_error_buf, ERROR_BUF_SZ, "setrlimit: RLIMIT_DATA: %s", xstrerr(xerrno));
62e76326 810 fatal_dump(tmp_error_buf);
811 }
30a4f2a8 812 }
813#endif /* RLIMIT_DATA */
f3f0f563
AJ
814 if (Config.max_filedescriptors > Squid_MaxFD) {
815 debugs(50, DBG_IMPORTANT, "NOTICE: Could not increase the number of filedescriptors");
816 }
817
a396d1f8 818#if HAVE_SETRLIMIT && defined(RLIMIT_VMEM) && !_SQUID_CYGWIN_
ad7ef91a 819 if (getrlimit(RLIMIT_VMEM, &rl) < 0) {
b69e9ffa
AJ
820 int xerrno = errno;
821 debugs(50, DBG_CRITICAL, "getrlimit: RLIMIT_VMEM: " << xstrerr(xerrno));
88738790 822 } else if (rl.rlim_max > rl.rlim_cur) {
f53969cc 823 rl.rlim_cur = rl.rlim_max; /* set it to the max */
62e76326 824
825 if (setrlimit(RLIMIT_VMEM, &rl) < 0) {
20c05acd 826 int xerrno = errno;
b69e9ffa 827 snprintf(tmp_error_buf, ERROR_BUF_SZ, "setrlimit: RLIMIT_VMEM: %s", xstrerr(xerrno));
62e76326 828 fatal_dump(tmp_error_buf);
829 }
ad7ef91a 830 }
831#endif /* RLIMIT_VMEM */
c4fb974e 832}
3a57089e 833
b8d8561b 834void
ea3a2a69 835squid_signal(int sig, SIGHDLR * func, int flags)
30a4f2a8 836{
837#if HAVE_SIGACTION
62e76326 838
30a4f2a8 839 struct sigaction sa;
840 sa.sa_handler = func;
841 sa.sa_flags = flags;
842 sigemptyset(&sa.sa_mask);
62e76326 843
b69e9ffa
AJ
844 if (sigaction(sig, &sa, NULL) < 0) {
845 int xerrno = errno;
846 debugs(50, DBG_CRITICAL, "sigaction: sig=" << sig << " func=" << func << ": " << xstrerr(xerrno));
847 }
30a4f2a8 848#else
7aa9bb3e 849#if _SQUID_WINDOWS_
06648839 850 /*
851 On Windows, only SIGINT, SIGILL, SIGFPE, SIGTERM, SIGBREAK, SIGABRT and SIGSEGV signals
852 are supported, so we must care of don't call signal() for other value.
853 The SIGILL, SIGSEGV, and SIGTERM signals are not generated under Windows. They are defined
854 for ANSI compatibility, so both SIGSEGV and SIGBUS are emulated with an Exception Handler.
855 */
856 switch (sig) {
857
858 case SIGINT:
859
860 case SIGILL:
861
862 case SIGFPE:
863
864 case SIGTERM:
865
866 case SIGBREAK:
867
868 case SIGABRT:
869 break;
870
871 case SIGSEGV:
872 WIN32_ExceptionHandlerInit();
873 break;
874
875 case SIGBUS:
876 WIN32_ExceptionHandlerInit();
877 return;
878 break; /* Nor reached */
879
880 default:
881 return;
882 break; /* Nor reached */
883 }
884
885#endif
62e76326 886
06648839 887 signal(sig, func);
62e76326 888
30a4f2a8 889#endif
890}
396c5745 891
70364f29 892void
893logsFlush(void)
894{
895 if (debug_log)
62e76326 896 fflush(debug_log);
70364f29 897}
88738790 898
a00a7c85 899void
6bbf9cc4 900debugObj(int section, int level, const char *label, void *obj, ObjPackMethod pm)
a00a7c85 901{
6bbf9cc4 902 assert(label && obj && pm);
10201568 903 MemBuf mb;
2fe7eff9 904 mb.init();
10201568 905 (*pm) (obj, &mb);
bf8fe701 906 debugs(section, level, "" << label << "" << mb.buf << "");
2fe7eff9 907 mb.clean();
a00a7c85 908}
d548ee64 909
0e70aa1e 910void
911parseEtcHosts(void)
912{
0e70aa1e 913 char buf[1024];
914 char buf2[512];
915 char *nt = buf;
916 char *lt = buf;
fa2bfbab 917
b69e9ffa 918 if (!Config.etcHostsPath)
62e76326 919 return;
920
0e70aa1e 921 if (0 == strcmp(Config.etcHostsPath, "none"))
62e76326 922 return;
923
b69e9ffa 924 FILE *fp = fopen(Config.etcHostsPath, "r");
62e76326 925
b69e9ffa
AJ
926 if (!fp) {
927 int xerrno = errno;
1bff41b7 928 debugs(1, DBG_IMPORTANT, "parseEtcHosts: '" << Config.etcHostsPath << "' : " << xstrerr(xerrno));
62e76326 929 return;
0e70aa1e 930 }
62e76326 931
be266cb2 932#if _SQUID_WINDOWS_
c4aefe96 933 setmode(fileno(fp), O_TEXT);
934#endif
62e76326 935
f53969cc 936 while (fgets(buf, 1024, fp)) { /* for each line */
62e76326 937
f53969cc 938 if (buf[0] == '#') /* MS-windows likes to add comments */
c31616ae 939 continue;
940
f53969cc 941 strtok(buf, "#"); /* chop everything following a comment marker */
62e76326 942
943 lt = buf;
944
1bff41b7 945 char *addr = buf;
62e76326 946
bf8fe701 947 debugs(1, 5, "etc_hosts: line is '" << buf << "'");
62e76326 948
949 nt = strpbrk(lt, w_space);
950
f53969cc 951 if (nt == NULL) /* empty line */
62e76326 952 continue;
953
f53969cc 954 *nt = '\0'; /* null-terminate the address */
62e76326 955
bf8fe701 956 debugs(1, 5, "etc_hosts: address is '" << addr << "'");
62e76326 957
958 lt = nt + 1;
959
1bff41b7
AJ
960 SBufList hosts;
961
62e76326 962 while ((nt = strpbrk(lt, w_space))) {
963 char *host = NULL;
964
f53969cc 965 if (nt == lt) { /* multiple spaces */
bf8fe701 966 debugs(1, 5, "etc_hosts: multiple spaces, skipping");
62e76326 967 lt = nt + 1;
968 continue;
969 }
970
971 *nt = '\0';
bf8fe701 972 debugs(1, 5, "etc_hosts: got hostname '" << lt << "'");
62e76326 973
532e5dd4
AJ
974 /* For IPV6 addresses also check for a colon */
975 if (Config.appendDomain && !strchr(lt, '.') && !strchr(lt, ':')) {
62e76326 976 /* I know it's ugly, but it's only at reconfig */
0a84e4fb
AJ
977 strncpy(buf2, lt, sizeof(buf2)-1);
978 strncat(buf2, Config.appendDomain, sizeof(buf2) - strlen(lt) - 1);
50e746f3 979 buf2[sizeof(buf2)-1] = '\0';
62e76326 980 host = buf2;
981 } else {
982 host = lt;
983 }
984
f6a72b33
AJ
985 if (ipcacheAddEntryFromHosts(host, addr) != 0) {
986 /* invalid address, continuing is useless */
1bff41b7 987 hosts.clear();
f6a72b33
AJ
988 break;
989 }
1bff41b7 990 hosts.emplace_back(SBuf(host));
62e76326 991
992 lt = nt + 1;
993 }
994
1bff41b7 995 if (!hosts.empty())
f6a72b33 996 fqdncacheAddEntryFromHosts(addr, hosts);
0e70aa1e 997 }
62e76326 998
1f3c7397 999 fclose (fp);
0e70aa1e 1000}
52f772de 1001
1002int
1003getMyPort(void)
1004{
fa720bfb
AJ
1005 AnyP::PortCfgPointer p;
1006 if ((p = HttpPortList) != NULL) {
c4ac4197 1007 // skip any special interception ports
fa720bfb 1008 while (p != NULL && p->flags.isIntercepted())
af5ebaf4 1009 p = p->next;
fa720bfb 1010 if (p != NULL)
4dd643d5 1011 return p->s.port();
af5ebaf4 1012 }
62e76326 1013
8ea0d847 1014 if ((p = FtpPortList) != NULL) {
547f3031 1015 // skip any special interception ports
8ea0d847 1016 while (p != NULL && p->flags.isIntercepted())
547f3031 1017 p = p->next;
8ea0d847 1018 if (p != NULL)
547f3031
AR
1019 return p->s.port();
1020 }
1021
af5ebaf4
AJ
1022 debugs(21, DBG_CRITICAL, "ERROR: No forward-proxy ports configured.");
1023 return 0; // Invalid port. This will result in invalid URLs on bad configurations.
52f772de 1024}
d860a1aa 1025
c642c141
AJ
1026/*
1027 * Set the umask to at least the given mask. This is in addition
1028 * to the umask set at startup
1029 */
1030void
1031setUmask(mode_t mask)
1032{
1033 // No way to get the current umask value without setting it.
1034 static const mode_t orig_umask = umask(mask); // once, to get
1035 umask(mask | orig_umask); // always, to set
1036}
d860a1aa 1037
1038/*
1039 * Inverse of strwordtok. Quotes a word if needed
1040 */
42419a95 1041void
d860a1aa 1042strwordquote(MemBuf * mb, const char *str)
1043{
1044 int quoted = 0;
62e76326 1045
d860a1aa 1046 if (strchr(str, ' ')) {
62e76326 1047 quoted = 1;
2fe7eff9 1048 mb->append("\"", 1);
d860a1aa 1049 }
62e76326 1050
d860a1aa 1051 while (*str) {
dc1af3cf 1052 int l = strcspn(str, "\"\\\n\r");
2fe7eff9 1053 mb->append(str, l);
62e76326 1054 str += l;
1055
26ac0430 1056 switch (*str) {
dc1af3cf 1057
1058 case '\n':
2fe7eff9 1059 mb->append("\\n", 2);
5db6bf73 1060 ++str;
dc1af3cf 1061 break;
1062
1063 case '\r':
2fe7eff9 1064 mb->append("\\r", 2);
5db6bf73 1065 ++str;
dc1af3cf 1066 break;
1067
1068 case '\0':
1069 break;
1070
1071 default:
2fe7eff9 1072 mb->append("\\", 1);
1073 mb->append(str, 1);
5db6bf73 1074 ++str;
dc1af3cf 1075 break;
62e76326 1076 }
d860a1aa 1077 }
62e76326 1078
d860a1aa 1079 if (quoted)
2fe7eff9 1080 mb->append("\"", 1);
d860a1aa 1081}
fc68f6b1 1082
1083void
1084keepCapabilities(void)
1085{
b7ac5457 1086#if USE_LIBCAP && HAVE_PRCTL && defined(PR_SET_KEEPCAPS)
329ce686 1087
1088 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
b7ac5457 1089 Ip::Interceptor.StopTransparency("capability setting has failed.");
fc68f6b1 1090 }
fc68f6b1 1091#endif
1092}
329ce686 1093
1094static void
ff2afd7f 1095restoreCapabilities(bool keep)
329ce686 1096{
9e008dda 1097 /* NP: keep these two if-endif separate. Non-Linux work perfectly well without Linux syscap support. */
b64b6030 1098#if USE_LIBCAP
c3487a77
HN
1099 cap_t caps;
1100 if (keep)
1101 caps = cap_get_proc();
1102 else
1103 caps = cap_init();
1104 if (!caps) {
b7ac5457 1105 Ip::Interceptor.StopTransparency("Can't get current capabilities");
9e008dda 1106 } else {
c3487a77
HN
1107 int ncaps = 0;
1108 int rc = 0;
1109 cap_value_t cap_list[10];
5db6bf73
FC
1110 cap_list[ncaps] = CAP_NET_BIND_SERVICE;
1111 ++ncaps;
4a751e2a 1112 if (Ip::Interceptor.TransparentActive() ||
9e27e206
SM
1113 Ip::Qos::TheConfig.isHitNfmarkActive() ||
1114 Ip::Qos::TheConfig.isAclNfmarkActive() ||
1115 Ip::Qos::TheConfig.isAclTosActive()) {
5db6bf73
FC
1116 cap_list[ncaps] = CAP_NET_ADMIN;
1117 ++ncaps;
fc32ce2e 1118 }
329ce686 1119
c3487a77
HN
1120 cap_clear_flag(caps, CAP_EFFECTIVE);
1121 rc |= cap_set_flag(caps, CAP_EFFECTIVE, ncaps, cap_list, CAP_SET);
1122 rc |= cap_set_flag(caps, CAP_PERMITTED, ncaps, cap_list, CAP_SET);
329ce686 1123
c3487a77 1124 if (rc || cap_set_proc(caps) != 0) {
b7ac5457 1125 Ip::Interceptor.StopTransparency("Error enabling needed capabilities.");
b5bd96ce 1126 }
c3487a77 1127 cap_free(caps);
329ce686 1128 }
8a09e810 1129#elif _SQUID_LINUX_
b7ac5457 1130 Ip::Interceptor.StopTransparency("Missing needed capability support.");
b5bd96ce 1131#endif /* HAVE_SYS_CAPABILITY_H */
329ce686 1132}
f53969cc 1133
dbf55289
CT
1134pid_t
1135WaitForOnePid(pid_t pid, PidStatus &status, int flags)
1136{
1137#if _SQUID_NEXT_
1138 if (pid < 0)
1139 return wait3(&status, flags, NULL);
7def3beb 1140 return wait4(pid, &status, flags, NULL);
c92fcbad
AJ
1141#elif _SQUID_WINDOWS_
1142 return 0; // function not used on Windows
dbf55289
CT
1143#else
1144 return waitpid(pid, &status, flags);
1145#endif
1146}
5c634111 1147
e99fa721
EB
1148#if _SQUID_WINDOWS_
1149SBuf
1150WindowsErrorMessage(DWORD errorId)
1151{
1152 char *rawMessage = nullptr;
1153 const auto length = FormatMessage(
1154 FORMAT_MESSAGE_ALLOCATE_BUFFER |
1155 FORMAT_MESSAGE_FROM_SYSTEM |
1156 FORMAT_MESSAGE_IGNORE_INSERTS,
1157 nullptr,
1158 errorId,
1159 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
1160 static_cast<LPTSTR>(&rawMessage),
1161 0,
1162 nullptr);
1163 if (!length) {
1164 Must(!rawMessage); // nothing to LocalFree()
1165 return ToSBuf("windows error ", errorId);
1166 }
1167 const auto result = SBuf(rawMessage, length);
1168 LocalFree(rawMessage);
1169 return result;
1170}
1171#endif // _SQUID_WINDOWS_
1172