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