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