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