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