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