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