]> git.ipfire.org Git - thirdparty/squid.git/blob - src/tools.cc
c4a5e9c1aeebe67c4bf6daf6cdd65c7911062397
[thirdparty/squid.git] / src / tools.cc
1 /*
2 * Copyright (C) 1996-2020 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 "SquidTime.h"
32 #include "store/Disks.h"
33 #include "tools.h"
34 #include "wordlist.h"
35
36 #include <cerrno>
37 #if HAVE_SYS_PRCTL_H
38 #include <sys/prctl.h>
39 #endif
40 #if HAVE_WIN32_PSAPI
41 #include <psapi.h>
42 #endif
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
52
53 #define DEAD_MSG "\
54 The Squid Cache (version %s) died.\n\
55 \n\
56 You've encountered a fatal error in the Squid Cache version %s.\n\
57 If a core file was created (possibly in the swap directory),\n\
58 please execute 'gdb squid core' or 'dbx squid core', then type 'where',\n\
59 and report the trace back to squid-bugs@lists.squid-cache.org.\n\
60 \n\
61 Thanks!\n"
62
63 static void mail_warranty(void);
64 static void restoreCapabilities(bool keep);
65 int DebugSignal = -1;
66 SBuf service_name(APP_SHORTNAME);
67
68 #if _SQUID_LINUX_
69 /* Workaround for crappy glic header files */
70 SQUIDCEXTERN int backtrace(void *, int);
71 SQUIDCEXTERN void backtrace_symbols_fd(void *, int, int);
72 SQUIDCEXTERN int setresuid(uid_t, uid_t, uid_t);
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
79 #endif /* _SQUID_LINUX */
80
81 void
82 releaseServerSockets(void)
83 {
84 // Release the main ports as early as possible
85
86 // clear http_port, https_port, and ftp_port lists
87 clientConnectionsClose();
88
89 // clear icp_port's
90 icpClosePorts();
91
92 // XXX: Why not the HTCP, SNMP, DNS ports as well?
93 // XXX: why does this differ from main closeServerConnections() anyway ?
94 }
95
96 static char *
97 dead_msg(void)
98 {
99 LOCAL_ARRAY(char, msg, 1024);
100 snprintf(msg, 1024, DEAD_MSG, version_string, version_string);
101 return msg;
102 }
103
104 static void
105 mail_warranty(void)
106 {
107 FILE *fp = NULL;
108 static char command[256];
109
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);
118
119 #if HAVE_MKSTEMP
120 char filename[] = "/tmp/squid-XXXXXX";
121 int tfd = mkstemp(filename);
122 if (tfd < 0 || (fp = fdopen(tfd, "w")) == NULL) {
123 umask(prev_umask);
124 return;
125 }
126 #else
127 char *filename;
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 ||
131 (fp = fopen(filename, "w")) == NULL) {
132 umask(prev_umask);
133 return;
134 }
135 #endif
136 umask(prev_umask);
137
138 if (Config.EmailFrom)
139 fprintf(fp, "From: %s\n", Config.EmailFrom);
140 else
141 fprintf(fp, "From: %s@%s\n", APP_SHORTNAME, uniqueHostname());
142
143 fprintf(fp, "To: %s\n", Config.adminEmail);
144 fprintf(fp, "Subject: %s\n", dead_msg());
145 fclose(fp);
146
147 snprintf(command, 256, "%s %s < %s", Config.EmailProgram, Config.adminEmail, filename);
148 if (system(command)) {} /* XXX should avoid system(3) */
149 unlink(filename);
150 #if !HAVE_MKSTEMP
151 xfree(filename); // tempnam() requires us to free its allocation
152 #endif
153 }
154
155 void
156 dumpMallocStats(void)
157 {
158 #if HAVE_MSTATS && HAVE_GNUMALLOC_H
159
160 struct mstats ms = mstats();
161 fprintf(debug_log, "\ttotal space in arena: %6d KB\n",
162 (int) (ms.bytes_total >> 10));
163 fprintf(debug_log, "\tTotal free: %6d KB %d%%\n",
164 (int) (ms.bytes_free >> 10),
165 Math::intPercent(ms.bytes_free, ms.bytes_total));
166 #endif
167 }
168
169 void
170 squid_getrusage(struct rusage *r)
171 {
172 memset(r, '\0', sizeof(struct rusage));
173 #if HAVE_GETRUSAGE && defined(RUSAGE_SELF) && !_SQUID_WINDOWS_
174 #if _SQUID_SOLARIS_
175 /* Solaris 2.5 has getrusage() permission bug -- Arjan de Vet */
176 enter_suid();
177 #endif
178
179 getrusage(RUSAGE_SELF, r);
180
181 #if _SQUID_SOLARIS_
182 leave_suid();
183 #endif
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 }
223 #endif
224 }
225
226 double
227
228 rusage_cputime(struct rusage *r)
229 {
230 return (double) r->ru_stime.tv_sec +
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;
234 }
235
236 /* Hack for some HP-UX preprocessors */
237 #ifndef HAVE_GETPAGESIZE
238 #define HAVE_GETPAGESIZE 0
239 #endif
240
241 int
242
243 rusage_maxrss(struct rusage *r)
244 {
245 #if _SQUID_SGI_ && _ABIAPI
246 return r->ru_pad[0];
247 #elif _SQUID_SGI_|| _SQUID_OSF_ || _SQUID_AIX_ || defined(BSD4_4)
248
249 return r->ru_maxrss;
250 #elif defined(HAVE_GETPAGESIZE) && HAVE_GETPAGESIZE != 0
251
252 return (r->ru_maxrss * getpagesize()) >> 10;
253 #elif defined(PAGESIZE)
254
255 return (r->ru_maxrss * PAGESIZE) >> 10;
256 #else
257
258 return r->ru_maxrss;
259 #endif
260 }
261
262 int
263
264 rusage_pagefaults(struct rusage *r)
265 {
266 #if _SQUID_SGI_ && _ABIAPI
267 return r->ru_pad[5];
268 #else
269
270 return r->ru_majflt;
271 #endif
272 }
273
274 void
275 PrintRusage(void)
276 {
277
278 struct rusage rusage;
279 squid_getrusage(&rusage);
280 fprintf(debug_log, "CPU Usage: %.3f seconds = %.3f user + %.3f sys\n",
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));
284 fprintf(debug_log, "Maximum Resident Size: %d KB\n",
285 rusage_maxrss(&rusage));
286 fprintf(debug_log, "Page faults with physical i/o: %d\n",
287 rusage_pagefaults(&rusage));
288 }
289
290 void
291 death(int sig)
292 {
293 if (sig == SIGSEGV)
294 debugs(1, DBG_CRITICAL, ForceAlert << "FATAL: Received Segment Violation...dying.");
295 else if (sig == SIGBUS)
296 debugs(1, DBG_CRITICAL, ForceAlert << "FATAL: Received Bus Error...dying.");
297 else
298 debugs(1, DBG_CRITICAL, ForceAlert << "FATAL: Received signal " << sig << "...dying.");
299
300 #if PRINT_STACK_TRACE
301 #if _SQUID_HPUX_
302 {
303 extern void U_STACK_TRACE(void); /* link with -lcl */
304 fflush(debug_log);
305 dup2(fileno(debug_log), 2);
306 U_STACK_TRACE();
307 }
308
309 #endif /* _SQUID_HPUX_ */
310 #if _SQUID_SOLARIS_ && HAVE_LIBOPCOM_STACK
311 { /* get ftp://opcom.sun.ca/pub/tars/opcom_stack.tar.gz and */
312 extern void opcom_stack_trace(void); /* link with -lopcom_stack */
313 fflush(debug_log);
314 dup2(fileno(debug_log), fileno(stdout));
315 opcom_stack_trace();
316 fflush(stdout);
317 }
318
319 #endif /* _SQUID_SOLARIS_and HAVE_LIBOPCOM_STACK */
320 #if HAVE_BACKTRACE_SYMBOLS_FD
321 {
322 static void *callarray[8192];
323 int n;
324 n = backtrace(callarray, 8192);
325 backtrace_symbols_fd(callarray, n, fileno(debug_log));
326 }
327
328 #endif
329 #endif /* PRINT_STACK_TRACE */
330
331 #if SA_RESETHAND == 0 && !_SQUID_WINDOWS_
332 signal(SIGSEGV, SIG_DFL);
333
334 signal(SIGBUS, SIG_DFL);
335
336 signal(sig, SIG_DFL);
337
338 #endif
339
340 releaseServerSockets();
341
342 storeDirWriteCleanLogs(0);
343
344 if (!shutting_down) {
345 PrintRusage();
346
347 dumpMallocStats();
348 }
349
350 if (squid_curtime - SQUID_RELEASE_TIME < 864000) {
351 /* skip if more than 10 days old */
352
353 if (Config.adminEmail)
354 mail_warranty();
355
356 puts(dead_msg());
357 }
358
359 abort();
360 }
361
362 void
363 BroadcastSignalIfAny(int& sig)
364 {
365 if (sig > 0) {
366 if (IamMasterProcess()) {
367 for (int i = TheKids.count() - 1; i >= 0; --i) {
368 const auto &kid = TheKids.get(i);
369 if (kid.running())
370 kill(kid.getPid(), sig);
371 }
372 }
373 sig = -1;
374 }
375 }
376
377 void
378 sigusr2_handle(int sig)
379 {
380 static int state = 0;
381 /* no debugs() here; bad things happen if the signal is delivered during _db_print() */
382
383 DebugSignal = sig;
384
385 if (state == 0) {
386 Debug::parseOptions("ALL,7");
387 state = 1;
388 } else {
389 Debug::parseOptions(Debug::debugOptions);
390 state = 0;
391 }
392
393 #if !HAVE_SIGACTION
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 }
399 #endif
400 }
401
402 void
403 debug_trap(const char *message)
404 {
405 if (!opt_catch_signals)
406 fatal_dump(message);
407
408 debugs(50, DBG_CRITICAL, "WARNING: " << message);
409 }
410
411 const char *
412 getMyHostname(void)
413 {
414 LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN + 1);
415 static int present = 0;
416 struct addrinfo *AI = NULL;
417 Ip::Address sa;
418
419 if (Config.visibleHostname != NULL)
420 return Config.visibleHostname;
421
422 if (present)
423 return host;
424
425 host[0] = '\0';
426
427 if (HttpPortList != NULL && sa.isAnyAddr())
428 sa = HttpPortList->s;
429
430 /*
431 * If the first http_port address has a specific address, try a
432 * reverse DNS lookup on it.
433 */
434 if ( !sa.isAnyAddr() ) {
435
436 sa.getAddrInfo(AI);
437 /* we are looking for a name. */
438 if (getnameinfo(AI->ai_addr, AI->ai_addrlen, host, SQUIDHOSTNAMELEN, NULL, 0, NI_NAMEREQD ) == 0) {
439 /* DNS lookup successful */
440 /* use the official name from DNS lookup */
441 debugs(50, 4, "getMyHostname: resolved " << sa << " to '" << host << "'");
442
443 present = 1;
444
445 Ip::Address::FreeAddr(AI);
446
447 if (strchr(host, '.'))
448 return host;
449 }
450
451 Ip::Address::FreeAddr(AI);
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) {
457 int xerrno = errno;
458 debugs(50, DBG_IMPORTANT, "WARNING: gethostname failed: " << xstrerr(xerrno));
459 } else {
460 /* Verify that the hostname given resolves properly */
461 struct addrinfo hints;
462 memset(&hints, 0, sizeof(addrinfo));
463 hints.ai_flags = AI_CANONNAME;
464
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? */
472 if (AI)
473 freeaddrinfo(AI);
474
475 return host;
476 }
477 int xerrno = errno;
478
479 if (AI)
480 freeaddrinfo(AI);
481 debugs(50, DBG_IMPORTANT, "WARNING: '" << host << "' rDNS test failed: " << xstrerr(xerrno));
482 }
483
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'.");
487
488 return ("localhost");
489 }
490
491 const char *
492 uniqueHostname(void)
493 {
494 debugs(21, 3, HERE << " Config: '" << Config.uniqueHostname << "'");
495 return Config.uniqueHostname ? Config.uniqueHostname : getMyHostname();
496 }
497
498 /** leave a priviliged section. (Give up any privilegies)
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()
502 */
503 void
504 leave_suid(void)
505 {
506 debugs(21, 3, "leave_suid: PID " << getpid() << " called");
507
508 if (Config.effectiveGroup) {
509 #if HAVE_SETGROUPS
510 setgroups(1, &Config2.effectiveGroupID);
511 #endif
512
513 if (setgid(Config2.effectiveGroupID) < 0) {
514 int xerrno = errno;
515 debugs(50, DBG_CRITICAL, "ALERT: setgid: " << xstrerr(xerrno));
516 }
517 }
518
519 if (geteuid() != 0)
520 return;
521
522 /* Started as a root, check suid option */
523 if (Config.effectiveUser == NULL)
524 return;
525
526 debugs(21, 3, "leave_suid: PID " << getpid() << " giving up root, becoming '" << Config.effectiveUser << "'");
527
528 if (!Config.effectiveGroup) {
529
530 if (setgid(Config2.effectiveGroupID) < 0) {
531 int xerrno = errno;
532 debugs(50, DBG_CRITICAL, "ALERT: setgid: " << xstrerr(xerrno));
533 }
534
535 if (initgroups(Config.effectiveUser, Config2.effectiveGroupID) < 0) {
536 debugs(50, DBG_CRITICAL, "ALERT: initgroups: unable to set groups for User " <<
537 Config.effectiveUser << " and Group " <<
538 (unsigned) Config2.effectiveGroupID << "");
539 }
540 }
541
542 #if HAVE_SETRESUID
543 if (setresuid(Config2.effectiveUserID, Config2.effectiveUserID, 0) < 0) {
544 const auto xerrno = errno;
545 fatalf("FATAL: setresuid: %s", xstrerr(xerrno));
546 }
547
548 #elif HAVE_SETEUID
549 if (seteuid(Config2.effectiveUserID) < 0) {
550 const auto xerrno = errno;
551 fatalf("FATAL: seteuid: %s", xstrerr(xerrno));
552 }
553
554 #else
555 if (setuid(Config2.effectiveUserID) < 0) {
556 const auto xerrno = errno;
557 fatalf("FATAL: setuid: %s", xstrerr(xerrno));
558 }
559
560 #endif
561
562 restoreCapabilities(true);
563
564 #if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
565 /* Set Linux DUMPABLE flag */
566 if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0) {
567 int xerrno = errno;
568 debugs(50, 2, "ALERT: prctl: " << xstrerr(xerrno));
569 }
570 #endif
571 }
572
573 /* Enter a privilegied section */
574 void
575 enter_suid(void)
576 {
577 debugs(21, 3, "enter_suid: PID " << getpid() << " taking root privileges");
578 #if HAVE_SETRESUID
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 }
583 #else
584
585 setuid(0);
586 #endif
587 #if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
588 /* Set Linux DUMPABLE flag */
589
590 if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0) {
591 int xerrno = errno;
592 debugs(50, 2, "ALERT: prctl: " << xstrerr(xerrno));
593 }
594 #endif
595 }
596
597 /* Give up the posibility to gain privilegies.
598 * this should be used before starting a sub process
599 */
600 void
601 no_suid(void)
602 {
603 uid_t uid;
604 leave_suid();
605 uid = geteuid();
606 debugs(21, 3, "no_suid: PID " << getpid() << " giving up root privileges forever");
607
608 if (setuid(0) < 0) {
609 int xerrno = errno;
610 debugs(50, DBG_IMPORTANT, "WARNING: no_suid: setuid(0): " << xstrerr(xerrno));
611 }
612
613 if (setuid(uid) < 0) {
614 int xerrno = errno;
615 debugs(50, DBG_IMPORTANT, "ERROR: no_suid: setuid(" << uid << "): " << xstrerr(xerrno));
616 }
617
618 restoreCapabilities(false);
619
620 #if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
621 /* Set Linux DUMPABLE flag */
622 if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0) {
623 int xerrno = errno;
624 debugs(50, 2, "ALERT: prctl: " << xstrerr(xerrno));
625 }
626 #endif
627 }
628
629 bool
630 IamMasterProcess()
631 {
632 return KidIdentifier == 0;
633 }
634
635 bool
636 IamWorkerProcess()
637 {
638 // when there is only one process, it has to be the worker
639 if (opt_no_daemon || Config.workers == 0)
640 return true;
641
642 return TheProcessKind == pkWorker;
643 }
644
645 bool
646 IamDiskProcess()
647 {
648 return TheProcessKind == pkDisker;
649 }
650
651 bool
652 InDaemonMode()
653 {
654 return !opt_no_daemon && Config.workers > 0;
655 }
656
657 bool
658 UsingSmp()
659 {
660 return InDaemonMode() && NumberOfKids() > 1;
661 }
662
663 bool
664 IamCoordinatorProcess()
665 {
666 return TheProcessKind == pkCoordinator;
667 }
668
669 bool
670 IamPrimaryProcess()
671 {
672 // when there is only one process, it has to be primary
673 if (opt_no_daemon || Config.workers == 0)
674 return true;
675
676 // when there is a master and worker process, the master delegates
677 // primary functions to its only kid
678 if (NumberOfKids() == 1)
679 return IamWorkerProcess();
680
681 // in SMP mode, multiple kids delegate primary functions to the coordinator
682 return IamCoordinatorProcess();
683 }
684
685 int
686 NumberOfKids()
687 {
688 // no kids in no-daemon mode
689 if (!InDaemonMode())
690 return 0;
691
692 // XXX: detect and abort when called before workers/cache_dirs are parsed
693
694 const int rockDirs = Config.cacheSwap.n_strands;
695
696 const bool needCoord = Config.workers > 1 || rockDirs > 0;
697 return (needCoord ? 1 : 0) + Config.workers + rockDirs;
698 }
699
700 SBuf
701 ProcessRoles()
702 {
703 SBuf roles;
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;
713 }
714
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
721
722 /** Figure out the number of supported filedescriptors */
723 void
724 setMaxFD(void)
725 {
726 #if HAVE_SETRLIMIT && defined(RLIMIT_NOFILE)
727
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 */
731 #if defined(getrlimit)
732 struct rlimit64 rl; // Assume its a 64-bit redefine anyways.
733 #else
734 struct rlimit rl;
735 #endif
736
737 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
738 int xerrno = errno;
739 debugs(50, DBG_CRITICAL, "getrlimit: RLIMIT_NOFILE: " << xstrerr(xerrno));
740 } else if (Config.max_filedescriptors > 0) {
741 #if USE_SELECT || USE_SELECT_WIN32
742 /* select() breaks if this gets set too big */
743 if (Config.max_filedescriptors > FD_SETSIZE) {
744 rl.rlim_cur = FD_SETSIZE;
745 debugs(50, DBG_CRITICAL, "WARNING: 'max_filedescriptors " << Config.max_filedescriptors << "' does not work with select()");
746 } else
747 #endif
748 rl.rlim_cur = Config.max_filedescriptors;
749 if (rl.rlim_cur > rl.rlim_max)
750 rl.rlim_max = rl.rlim_cur;
751 if (setrlimit(RLIMIT_NOFILE, &rl)) {
752 int xerrno = errno;
753 debugs(50, DBG_CRITICAL, "ERROR: setrlimit: RLIMIT_NOFILE: " << xstrerr(xerrno));
754 getrlimit(RLIMIT_NOFILE, &rl);
755 rl.rlim_cur = rl.rlim_max;
756 if (setrlimit(RLIMIT_NOFILE, &rl)) {
757 xerrno = errno;
758 debugs(50, DBG_CRITICAL, "ERROR: setrlimit: RLIMIT_NOFILE: " << xstrerr(xerrno));
759 }
760 }
761 }
762 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
763 int xerrno = errno;
764 debugs(50, DBG_CRITICAL, "ERROR: getrlimit: RLIMIT_NOFILE: " << xstrerr(xerrno));
765 } else {
766 Squid_MaxFD = rl.rlim_cur;
767 }
768
769 #endif /* HAVE_SETRLIMIT */
770 }
771
772 void
773 setSystemLimits(void)
774 {
775 #if HAVE_SETRLIMIT && defined(RLIMIT_NOFILE) && !_SQUID_CYGWIN_
776 /* limit system filedescriptors to our own limit */
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
784 struct rlimit rl;
785 #endif
786
787 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
788 int xerrno = errno;
789 debugs(50, DBG_CRITICAL, "getrlimit: RLIMIT_NOFILE: " << xstrerr(xerrno));
790 } else {
791 rl.rlim_cur = Squid_MaxFD;
792 if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
793 int xerrno = errno;
794 snprintf(tmp_error_buf, ERROR_BUF_SZ, "setrlimit: RLIMIT_NOFILE: %s", xstrerr(xerrno));
795 fatal_dump(tmp_error_buf);
796 }
797 }
798 #endif /* HAVE_SETRLIMIT */
799
800 #if HAVE_SETRLIMIT && defined(RLIMIT_DATA) && !_SQUID_CYGWIN_
801 if (getrlimit(RLIMIT_DATA, &rl) < 0) {
802 int xerrno = errno;
803 debugs(50, DBG_CRITICAL, "getrlimit: RLIMIT_DATA: " << xstrerr(xerrno));
804 } else if (rl.rlim_max > rl.rlim_cur) {
805 rl.rlim_cur = rl.rlim_max; /* set it to the max */
806
807 if (setrlimit(RLIMIT_DATA, &rl) < 0) {
808 int xerrno = errno;
809 snprintf(tmp_error_buf, ERROR_BUF_SZ, "setrlimit: RLIMIT_DATA: %s", xstrerr(xerrno));
810 fatal_dump(tmp_error_buf);
811 }
812 }
813 #endif /* RLIMIT_DATA */
814 if (Config.max_filedescriptors > Squid_MaxFD) {
815 debugs(50, DBG_IMPORTANT, "NOTICE: Could not increase the number of filedescriptors");
816 }
817
818 #if HAVE_SETRLIMIT && defined(RLIMIT_VMEM) && !_SQUID_CYGWIN_
819 if (getrlimit(RLIMIT_VMEM, &rl) < 0) {
820 int xerrno = errno;
821 debugs(50, DBG_CRITICAL, "getrlimit: RLIMIT_VMEM: " << xstrerr(xerrno));
822 } else if (rl.rlim_max > rl.rlim_cur) {
823 rl.rlim_cur = rl.rlim_max; /* set it to the max */
824
825 if (setrlimit(RLIMIT_VMEM, &rl) < 0) {
826 int xerrno = errno;
827 snprintf(tmp_error_buf, ERROR_BUF_SZ, "setrlimit: RLIMIT_VMEM: %s", xstrerr(xerrno));
828 fatal_dump(tmp_error_buf);
829 }
830 }
831 #endif /* RLIMIT_VMEM */
832 }
833
834 void
835 squid_signal(int sig, SIGHDLR * func, int flags)
836 {
837 #if HAVE_SIGACTION
838
839 struct sigaction sa;
840 sa.sa_handler = func;
841 sa.sa_flags = flags;
842 sigemptyset(&sa.sa_mask);
843
844 if (sigaction(sig, &sa, NULL) < 0) {
845 int xerrno = errno;
846 debugs(50, DBG_CRITICAL, "sigaction: sig=" << sig << " func=" << func << ": " << xstrerr(xerrno));
847 }
848 #else
849 #if _SQUID_WINDOWS_
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
886
887 signal(sig, func);
888
889 #endif
890 }
891
892 void
893 logsFlush(void)
894 {
895 if (debug_log)
896 fflush(debug_log);
897 }
898
899 void
900 debugObj(int section, int level, const char *label, void *obj, ObjPackMethod pm)
901 {
902 assert(label && obj && pm);
903 MemBuf mb;
904 mb.init();
905 (*pm) (obj, &mb);
906 debugs(section, level, "" << label << "" << mb.buf << "");
907 mb.clean();
908 }
909
910 void
911 parseEtcHosts(void)
912 {
913 char buf[1024];
914 char buf2[512];
915 char *nt = buf;
916 char *lt = buf;
917
918 if (!Config.etcHostsPath)
919 return;
920
921 if (0 == strcmp(Config.etcHostsPath, "none"))
922 return;
923
924 FILE *fp = fopen(Config.etcHostsPath, "r");
925
926 if (!fp) {
927 int xerrno = errno;
928 debugs(1, DBG_IMPORTANT, "parseEtcHosts: '" << Config.etcHostsPath << "' : " << xstrerr(xerrno));
929 return;
930 }
931
932 #if _SQUID_WINDOWS_
933 setmode(fileno(fp), O_TEXT);
934 #endif
935
936 while (fgets(buf, 1024, fp)) { /* for each line */
937
938 if (buf[0] == '#') /* MS-windows likes to add comments */
939 continue;
940
941 strtok(buf, "#"); /* chop everything following a comment marker */
942
943 lt = buf;
944
945 char *addr = buf;
946
947 debugs(1, 5, "etc_hosts: line is '" << buf << "'");
948
949 nt = strpbrk(lt, w_space);
950
951 if (nt == NULL) /* empty line */
952 continue;
953
954 *nt = '\0'; /* null-terminate the address */
955
956 debugs(1, 5, "etc_hosts: address is '" << addr << "'");
957
958 lt = nt + 1;
959
960 SBufList hosts;
961
962 while ((nt = strpbrk(lt, w_space))) {
963 char *host = NULL;
964
965 if (nt == lt) { /* multiple spaces */
966 debugs(1, 5, "etc_hosts: multiple spaces, skipping");
967 lt = nt + 1;
968 continue;
969 }
970
971 *nt = '\0';
972 debugs(1, 5, "etc_hosts: got hostname '" << lt << "'");
973
974 /* For IPV6 addresses also check for a colon */
975 if (Config.appendDomain && !strchr(lt, '.') && !strchr(lt, ':')) {
976 /* I know it's ugly, but it's only at reconfig */
977 strncpy(buf2, lt, sizeof(buf2)-1);
978 strncat(buf2, Config.appendDomain, sizeof(buf2) - strlen(lt) - 1);
979 buf2[sizeof(buf2)-1] = '\0';
980 host = buf2;
981 } else {
982 host = lt;
983 }
984
985 if (ipcacheAddEntryFromHosts(host, addr) != 0) {
986 /* invalid address, continuing is useless */
987 hosts.clear();
988 break;
989 }
990 hosts.emplace_back(SBuf(host));
991
992 lt = nt + 1;
993 }
994
995 if (!hosts.empty())
996 fqdncacheAddEntryFromHosts(addr, hosts);
997 }
998
999 fclose (fp);
1000 }
1001
1002 int
1003 getMyPort(void)
1004 {
1005 AnyP::PortCfgPointer p;
1006 if ((p = HttpPortList) != NULL) {
1007 // skip any special interception ports
1008 while (p != NULL && p->flags.isIntercepted())
1009 p = p->next;
1010 if (p != NULL)
1011 return p->s.port();
1012 }
1013
1014 if ((p = FtpPortList) != NULL) {
1015 // skip any special interception ports
1016 while (p != NULL && p->flags.isIntercepted())
1017 p = p->next;
1018 if (p != NULL)
1019 return p->s.port();
1020 }
1021
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.
1024 }
1025
1026 /*
1027 * Set the umask to at least the given mask. This is in addition
1028 * to the umask set at startup
1029 */
1030 void
1031 setUmask(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 }
1037
1038 /*
1039 * Inverse of strwordtok. Quotes a word if needed
1040 */
1041 void
1042 strwordquote(MemBuf * mb, const char *str)
1043 {
1044 int quoted = 0;
1045
1046 if (strchr(str, ' ')) {
1047 quoted = 1;
1048 mb->append("\"", 1);
1049 }
1050
1051 while (*str) {
1052 int l = strcspn(str, "\"\\\n\r");
1053 mb->append(str, l);
1054 str += l;
1055
1056 switch (*str) {
1057
1058 case '\n':
1059 mb->append("\\n", 2);
1060 ++str;
1061 break;
1062
1063 case '\r':
1064 mb->append("\\r", 2);
1065 ++str;
1066 break;
1067
1068 case '\0':
1069 break;
1070
1071 default:
1072 mb->append("\\", 1);
1073 mb->append(str, 1);
1074 ++str;
1075 break;
1076 }
1077 }
1078
1079 if (quoted)
1080 mb->append("\"", 1);
1081 }
1082
1083 void
1084 keepCapabilities(void)
1085 {
1086 #if USE_LIBCAP && HAVE_PRCTL && defined(PR_SET_KEEPCAPS)
1087
1088 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
1089 Ip::Interceptor.StopTransparency("capability setting has failed.");
1090 }
1091 #endif
1092 }
1093
1094 static void
1095 restoreCapabilities(bool keep)
1096 {
1097 /* NP: keep these two if-endif separate. Non-Linux work perfectly well without Linux syscap support. */
1098 #if USE_LIBCAP
1099 cap_t caps;
1100 if (keep)
1101 caps = cap_get_proc();
1102 else
1103 caps = cap_init();
1104 if (!caps) {
1105 Ip::Interceptor.StopTransparency("Can't get current capabilities");
1106 } else {
1107 int ncaps = 0;
1108 int rc = 0;
1109 cap_value_t cap_list[10];
1110 cap_list[ncaps] = CAP_NET_BIND_SERVICE;
1111 ++ncaps;
1112 if (Ip::Interceptor.TransparentActive() ||
1113 #if USE_LIBNETFILTERCONNTRACK
1114 // netfilter_conntrack requires CAP_NET_ADMIN to get client's CONNMARK
1115 Ip::Interceptor.InterceptActive() ||
1116 #endif
1117 Ip::Qos::TheConfig.isHitNfmarkActive() ||
1118 Ip::Qos::TheConfig.isAclNfmarkActive() ||
1119 Ip::Qos::TheConfig.isAclTosActive()) {
1120 cap_list[ncaps] = CAP_NET_ADMIN;
1121 ++ncaps;
1122 }
1123
1124 cap_clear_flag(caps, CAP_EFFECTIVE);
1125 rc |= cap_set_flag(caps, CAP_EFFECTIVE, ncaps, cap_list, CAP_SET);
1126 rc |= cap_set_flag(caps, CAP_PERMITTED, ncaps, cap_list, CAP_SET);
1127
1128 if (rc || cap_set_proc(caps) != 0) {
1129 Ip::Interceptor.StopTransparency("Error enabling needed capabilities.");
1130 }
1131 cap_free(caps);
1132 }
1133 #elif _SQUID_LINUX_
1134 Ip::Interceptor.StopTransparency("Missing needed capability support.");
1135 #endif /* HAVE_SYS_CAPABILITY_H */
1136 }
1137
1138 pid_t
1139 WaitForOnePid(pid_t pid, PidStatus &status, int flags)
1140 {
1141 #if _SQUID_NEXT_
1142 if (pid < 0)
1143 return wait3(&status, flags, NULL);
1144 return wait4(pid, &status, flags, NULL);
1145 #elif _SQUID_WINDOWS_
1146 return 0; // function not used on Windows
1147 #else
1148 return waitpid(pid, &status, flags);
1149 #endif
1150 }
1151
1152 #if _SQUID_WINDOWS_
1153 SBuf
1154 WindowsErrorMessage(DWORD errorId)
1155 {
1156 char *rawMessage = nullptr;
1157 const auto length = FormatMessage(
1158 FORMAT_MESSAGE_ALLOCATE_BUFFER |
1159 FORMAT_MESSAGE_FROM_SYSTEM |
1160 FORMAT_MESSAGE_IGNORE_INSERTS,
1161 nullptr,
1162 errorId,
1163 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
1164 static_cast<LPTSTR>(&rawMessage),
1165 0,
1166 nullptr);
1167 if (!length) {
1168 Must(!rawMessage); // nothing to LocalFree()
1169 return ToSBuf("windows error ", errorId);
1170 }
1171 const auto result = SBuf(rawMessage, length);
1172 LocalFree(rawMessage);
1173 return result;
1174 }
1175 #endif // _SQUID_WINDOWS_
1176