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