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