]> git.ipfire.org Git - thirdparty/squid.git/blob - src/tools.cc
Merged from trunk.
[thirdparty/squid.git] / src / tools.cc
1
2 /*
3 * $Id: tools.cc,v 1.281 2008/02/11 22:44:50 rousskov Exp $
4 *
5 * DEBUG: section 21 Misc Functions
6 * AUTHOR: Harvest Derived
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #include "squid.h"
37 #include "ProtoPort.h"
38 #include "SwapDir.h"
39 #include "fde.h"
40 #include "MemBuf.h"
41 #include "wordlist.h"
42 #include "SquidTime.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, appname)) == 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", appname, 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 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 %s via mallinfo():\n", appname);
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 releaseServerSockets();
465 /* check for store_dirs_rebuilding because fatal() is often
466 * used in early initialization phases, long before we ever
467 * get to the store log. */
468
469 /* XXX: this should be turned into a callback-on-fatal, or
470 * a mandatory-shutdown-event or something like that.
471 * - RBC 20060819
472 */
473
474 /*
475 * DPW 2007-07-06
476 * Call leave_suid() here to make sure that swap.state files
477 * are written as the effective user, rather than root. Squid
478 * may take on root privs during reconfigure. If squid.conf
479 * contains a "Bungled" line, fatal() will be called when the
480 * process still has root privs.
481 */
482 leave_suid();
483
484 if (0 == StoreController::store_dirs_rebuilding)
485 storeDirWriteCleanLogs(0);
486
487 fatal_common(message);
488
489 exit(1);
490 }
491
492 /* printf-style interface for fatal */
493 #if STDC_HEADERS
494 void
495 fatalf(const char *fmt,...)
496 {
497 va_list args;
498 va_start(args, fmt);
499 #else
500 void
501 fatalf(va_alist)
502 va_dcl
503 {
504 va_list args;
505 const char *fmt = NULL;
506 va_start(args);
507 fmt = va_arg(args, char *);
508 #endif
509
510 fatalvf(fmt, args);
511 va_end(args);
512 }
513
514
515 /* used by fatalf */
516 static void
517 fatalvf(const char *fmt, va_list args) {
518 static char fatal_str[BUFSIZ];
519 vsnprintf(fatal_str, sizeof(fatal_str), fmt, args);
520 fatal(fatal_str);
521 }
522
523 /* fatal with dumping core */
524 void
525 fatal_dump(const char *message) {
526 failure_notify = NULL;
527 releaseServerSockets();
528
529 if (message)
530 fatal_common(message);
531
532 /*
533 * Call leave_suid() here to make sure that swap.state files
534 * are written as the effective user, rather than root. Squid
535 * may take on root privs during reconfigure. If squid.conf
536 * contains a "Bungled" line, fatal() will be called when the
537 * process still has root privs.
538 */
539 leave_suid();
540
541 if (opt_catch_signals)
542 storeDirWriteCleanLogs(0);
543
544 abort();
545 }
546
547 void
548 debug_trap(const char *message) {
549 if (!opt_catch_signals)
550 fatal_dump(message);
551
552 _db_print("WARNING: %s\n", message);
553 }
554
555 void
556 sig_child(int sig) {
557 #ifndef _SQUID_MSWIN_
558 #ifdef _SQUID_NEXT_
559 union wait status;
560 #else
561
562 int status;
563 #endif
564
565 pid_t pid;
566
567 do {
568 #ifdef _SQUID_NEXT_
569 pid = wait3(&status, WNOHANG, NULL);
570 #else
571
572 pid = waitpid(-1, &status, WNOHANG);
573 #endif
574 /* no debug() here; bad things happen if the signal is delivered during _db_print() */
575 #if HAVE_SIGACTION
576
577 } while (pid > 0);
578
579 #else
580
581 }
582
583 while (pid > 0 || (pid < 0 && errno == EINTR));
584 signal(sig, sig_child);
585
586 #endif
587 #endif
588 }
589
590 const char *
591 getMyHostname(void)
592 {
593 LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN + 1);
594 static int present = 0;
595 struct addrinfo *AI = NULL;
596 IPAddress sa;
597
598 if (Config.visibleHostname != NULL)
599 return Config.visibleHostname;
600
601 if (present)
602 return host;
603
604 host[0] = '\0';
605
606 if (Config.Sockaddr.http && sa.IsAnyAddr())
607 sa = Config.Sockaddr.http->s;
608
609 #if USE_SSL
610
611 if (Config.Sockaddr.https && sa.IsAnyAddr())
612 sa = Config.Sockaddr.https->http.s;
613
614 #endif
615
616 /*
617 * If the first http_port address has a specific address, try a
618 * reverse DNS lookup on it.
619 */
620 if( !sa.IsAnyAddr() ) {
621
622 sa.GetAddrInfo(AI);
623 /* we are looking for a name. */
624 if(xgetnameinfo(AI->ai_addr, AI->ai_addrlen, host, SQUIDHOSTNAMELEN, NULL, 0, NI_NAMEREQD ) == 0) {
625 /* DNS lookup successful */
626 /* use the official name from DNS lookup */
627 debugs(50, 4, "getMyHostname: resolved " << sa << " to '" << host << "'");
628
629 present = 1;
630
631 sa.FreeAddrInfo(AI);
632
633 if (strchr(host, '.'))
634 return host;
635 }
636
637 sa.FreeAddrInfo(AI);
638 debugs(50, 1, "WARNING: failed to resolve " << sa << " to a fully qualified hostname");
639 }
640 else {
641 if (gethostname(host, SQUIDHOSTNAMELEN) < 0) {
642 debugs(50, 1, "WARNING: gethostname failed: " << xstrerror());
643 }
644 else {
645 /* Verify that the hostname given resolves properly */
646 struct addrinfo hints;
647 memset(&hints, 0, sizeof(addrinfo));
648 hints.ai_flags = AI_CANONNAME;
649
650 if(xgetaddrinfo(host, NULL, NULL, &AI) == 0) {
651 /* DNS lookup successful */
652 /* use the official name from DNS lookup */
653 debugs(50, 6, "getMyHostname: '" << host << "' resolved into '" << AI->ai_canonname << "'");
654 xstrncpy(host, AI->ai_canonname, SQUIDHOSTNAMELEN);
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 xfreeaddrinfo(AI);
660 AI = NULL;
661 }
662
663 return host;
664 }
665
666 if(AI) xfreeaddrinfo(AI);
667 debugs(50, 1, "WARNING: getaddrinfo('" << host << "') failed: " << xstrerror());
668 }
669 }
670
671 /* throw a fatal configuration error when the Host/IP given has bad DNS/rDNS. */
672 if (opt_send_signal == -1)
673 fatal("Could not determine fully qualified hostname. Please set 'visible_hostname'\n");
674 else
675 return ("localhost");
676
677 return NULL; /* keep compiler happy */
678 }
679
680 const char *
681 uniqueHostname(void)
682 {
683 return Config.uniqueHostname ? Config.uniqueHostname : getMyHostname();
684 }
685
686 /* leave a privilegied section. (Give up any privilegies)
687 * Routines that need privilegies can rap themselves in enter_suid()
688 * and leave_suid()
689 * To give upp all posibilites to gain privilegies use no_suid()
690 */
691 void
692 leave_suid(void)
693 {
694 debugs(21, 3, "leave_suid: PID " << getpid() << " called");
695
696 if (Config.effectiveGroup) {
697
698 #if HAVE_SETGROUPS
699
700 setgroups(1, &Config2.effectiveGroupID);
701
702 #endif
703
704 if (setgid(Config2.effectiveGroupID) < 0)
705 debugs(50, 0, "ALERT: setgid: " << xstrerror());
706
707 }
708
709 if (geteuid() != 0)
710 return;
711
712 /* Started as a root, check suid option */
713 if (Config.effectiveUser == NULL)
714 return;
715
716 debugs(21, 3, "leave_suid: PID " << getpid() << " giving up root, becoming '" << Config.effectiveUser << "'");
717
718 if (!Config.effectiveGroup) {
719
720 if (setgid(Config2.effectiveGroupID) < 0)
721 debugs(50, 0, "ALERT: setgid: " << xstrerror());
722
723 if (initgroups(Config.effectiveUser, Config2.effectiveGroupID) < 0) {
724 debugs(50, 0, "ALERT: initgroups: unable to set groups for User " <<
725 Config.effectiveUser << " and Group " <<
726 (unsigned) Config2.effectiveGroupID << "");
727 }
728 }
729
730 #if HAVE_SETRESUID
731
732 if (setresuid(Config2.effectiveUserID, Config2.effectiveUserID, 0) < 0)
733 debugs(50, 0, "ALERT: setresuid: " << xstrerror());
734
735 #elif HAVE_SETEUID
736
737 if (seteuid(Config2.effectiveUserID) < 0)
738 debugs(50, 0, "ALERT: seteuid: " << xstrerror());
739
740 #else
741
742 if (setuid(Config2.effectiveUserID) < 0)
743 debugs(50, 0, "ALERT: setuid: " << xstrerror());
744
745 #endif
746
747 restoreCapabilities(1);
748
749 #if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
750 /* Set Linux DUMPABLE flag */
751 if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0)
752 debugs(50, 2, "ALERT: prctl: " << xstrerror());
753
754 #endif
755 }
756
757 /* Enter a privilegied section */
758 void
759 enter_suid(void)
760 {
761 debugs(21, 3, "enter_suid: PID " << getpid() << " taking root priveleges");
762 #if HAVE_SETRESUID
763
764 setresuid((uid_t)-1, 0, (uid_t)-1);
765 #else
766
767 setuid(0);
768 #endif
769 #if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
770 /* Set Linux DUMPABLE flag */
771
772 if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0)
773 debugs(50, 2, "ALERT: prctl: " << xstrerror());
774
775 #endif
776 }
777
778 /* Give up the posibility to gain privilegies.
779 * this should be used before starting a sub process
780 */
781 void
782 no_suid(void)
783 {
784 uid_t uid;
785 leave_suid();
786 uid = geteuid();
787 debugs(21, 3, "no_suid: PID " << getpid() << " giving up root priveleges forever");
788
789 setuid(0);
790
791 if (setuid(uid) < 0)
792 debugs(50, 1, "no_suid: setuid: " << xstrerror());
793
794 restoreCapabilities(0);
795
796 #if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
797 /* Set Linux DUMPABLE flag */
798 if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0)
799 debugs(50, 2, "ALERT: prctl: " << xstrerror());
800
801 #endif
802 }
803
804 void
805 writePidFile(void)
806 {
807 int fd;
808 const char *f = NULL;
809 mode_t old_umask;
810 char buf[32];
811
812 if ((f = Config.pidFilename) == NULL)
813 return;
814
815 if (!strcmp(Config.pidFilename, "none"))
816 return;
817
818 enter_suid();
819
820 old_umask = umask(022);
821
822 fd = file_open(f, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT);
823
824 umask(old_umask);
825
826 leave_suid();
827
828 if (fd < 0) {
829 debugs(50, 0, "" << f << ": " << xstrerror());
830 debug_trap("Could not write pid file");
831 return;
832 }
833
834 snprintf(buf, 32, "%d\n", (int) getpid());
835 FD_WRITE_METHOD(fd, buf, strlen(buf));
836 file_close(fd);
837 }
838
839
840 pid_t
841 readPidFile(void)
842 {
843 FILE *pid_fp = NULL;
844 const char *f = Config.pidFilename;
845 char *chroot_f = NULL;
846 pid_t pid = -1;
847 int i;
848
849 if (f == NULL || !strcmp(Config.pidFilename, "none")) {
850 fprintf(stderr, "%s: ERROR: No pid file name defined\n", appname);
851 exit(1);
852 }
853
854 if (Config.chroot_dir && geteuid() == 0) {
855 int len = strlen(Config.chroot_dir) + 1 + strlen(f) + 1;
856 chroot_f = (char *)xmalloc(strlen(Config.chroot_dir) + 1 + strlen(f) + 1);
857 snprintf(chroot_f, len, "%s/%s", Config.chroot_dir, f);
858 f = chroot_f;
859 }
860
861 pid_fp = fopen(f, "r");
862
863 if (pid_fp != NULL) {
864 pid = 0;
865
866 if (fscanf(pid_fp, "%d", &i) == 1)
867 pid = (pid_t) i;
868
869 fclose(pid_fp);
870 } else {
871 if (errno != ENOENT) {
872 fprintf(stderr, "%s: ERROR: Could not read pid file\n", appname);
873 fprintf(stderr, "\t%s: %s\n", f, xstrerror());
874 exit(1);
875 }
876 }
877
878 safe_free(chroot_f);
879 return pid;
880 }
881
882
883 void
884 setMaxFD(void)
885 {
886 #if HAVE_SETRLIMIT
887 /* try to use as many file descriptors as possible */
888 /* System V uses RLIMIT_NOFILE and BSD uses RLIMIT_OFILE */
889
890 struct rlimit rl;
891 #if defined(RLIMIT_NOFILE)
892
893 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
894 debugs(50, 0, "setrlimit: RLIMIT_NOFILE: " << xstrerror());
895 } else {
896 rl.rlim_cur = Squid_MaxFD;
897
898 if (rl.rlim_cur > rl.rlim_max)
899 Squid_MaxFD = rl.rlim_cur = rl.rlim_max;
900
901 if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
902 snprintf(tmp_error_buf, ERROR_BUF_SZ,
903 "setrlimit: RLIMIT_NOFILE: %s", xstrerror());
904 fatal_dump(tmp_error_buf);
905 }
906 }
907
908 #elif defined(RLIMIT_OFILE)
909 if (getrlimit(RLIMIT_OFILE, &rl) < 0) {
910 debugs(50, 0, "setrlimit: RLIMIT_NOFILE: " << xstrerror());
911 } else {
912 rl.rlim_cur = Squid_MaxFD;
913
914 if (rl.rlim_cur > rl.rlim_max)
915 Squid_MaxFD = rl.rlim_cur = rl.rlim_max;
916
917 if (setrlimit(RLIMIT_OFILE, &rl) < 0) {
918 snprintf(tmp_error_buf, ERROR_BUF_SZ,
919 "setrlimit: RLIMIT_OFILE: %s", xstrerror());
920 fatal_dump(tmp_error_buf);
921 }
922 }
923
924 #endif
925 #else /* HAVE_SETRLIMIT */
926 debugs(21, 1, "setMaxFD: Cannot increase: setrlimit() not supported on this system");
927
928 #endif /* HAVE_SETRLIMIT */
929
930 #if HAVE_SETRLIMIT && defined(RLIMIT_DATA)
931
932 if (getrlimit(RLIMIT_DATA, &rl) < 0) {
933 debugs(50, 0, "getrlimit: RLIMIT_DATA: " << xstrerror());
934 } else if (rl.rlim_max > rl.rlim_cur) {
935 rl.rlim_cur = rl.rlim_max; /* set it to the max */
936
937 if (setrlimit(RLIMIT_DATA, &rl) < 0) {
938 snprintf(tmp_error_buf, ERROR_BUF_SZ,
939 "setrlimit: RLIMIT_DATA: %s", xstrerror());
940 fatal_dump(tmp_error_buf);
941 }
942 }
943
944 #endif /* RLIMIT_DATA */
945 #if HAVE_SETRLIMIT && defined(RLIMIT_VMEM)
946 if (getrlimit(RLIMIT_VMEM, &rl) < 0) {
947 debugs(50, 0, "getrlimit: RLIMIT_VMEM: " << xstrerror());
948 } else if (rl.rlim_max > rl.rlim_cur) {
949 rl.rlim_cur = rl.rlim_max; /* set it to the max */
950
951 if (setrlimit(RLIMIT_VMEM, &rl) < 0) {
952 snprintf(tmp_error_buf, ERROR_BUF_SZ,
953 "setrlimit: RLIMIT_VMEM: %s", xstrerror());
954 fatal_dump(tmp_error_buf);
955 }
956 }
957
958 #endif /* RLIMIT_VMEM */
959 }
960
961 int
962 percent(int a, int b)
963 {
964 return b ? ((int) (100.0 * a / b + 0.5)) : 0;
965 }
966
967 double
968 dpercent(double a, double b)
969 {
970 return b ? (100.0 * a / b) : 0.0;
971 }
972
973 void
974 squid_signal(int sig, SIGHDLR * func, int flags)
975 {
976 #if HAVE_SIGACTION
977
978 struct sigaction sa;
979 sa.sa_handler = func;
980 sa.sa_flags = flags;
981 sigemptyset(&sa.sa_mask);
982
983 if (sigaction(sig, &sa, NULL) < 0)
984 debugs(50, 0, "sigaction: sig=" << sig << " func=" << func << ": " << xstrerror());
985
986 #else
987 #ifdef _SQUID_MSWIN_
988 /*
989 On Windows, only SIGINT, SIGILL, SIGFPE, SIGTERM, SIGBREAK, SIGABRT and SIGSEGV signals
990 are supported, so we must care of don't call signal() for other value.
991 The SIGILL, SIGSEGV, and SIGTERM signals are not generated under Windows. They are defined
992 for ANSI compatibility, so both SIGSEGV and SIGBUS are emulated with an Exception Handler.
993 */
994 switch (sig) {
995
996 case SIGINT:
997
998 case SIGILL:
999
1000 case SIGFPE:
1001
1002 case SIGTERM:
1003
1004 case SIGBREAK:
1005
1006 case SIGABRT:
1007 break;
1008
1009 case SIGSEGV:
1010 WIN32_ExceptionHandlerInit();
1011 break;
1012
1013 case SIGBUS:
1014 WIN32_ExceptionHandlerInit();
1015 return;
1016 break; /* Nor reached */
1017
1018 default:
1019 return;
1020 break; /* Nor reached */
1021 }
1022
1023 #endif
1024
1025 signal(sig, func);
1026
1027 #endif
1028 }
1029
1030 double
1031 doubleAverage(double cur, double newD, int N, int max)
1032 {
1033 if (N > max)
1034 N = max;
1035
1036 return (cur * (N - 1.0) + newD) / N;
1037 }
1038
1039 int
1040 intAverage(int cur, int newI, int n, int max)
1041 {
1042 if (n > max)
1043 n = max;
1044
1045 return (cur * (n - 1) + newI) / n;
1046 }
1047
1048 void
1049 logsFlush(void)
1050 {
1051 if (debug_log)
1052 fflush(debug_log);
1053 }
1054
1055 void
1056 kb_incr(kb_t * k, size_t v)
1057 {
1058 k->bytes += v;
1059 k->kb += (k->bytes >> 10);
1060 k->bytes &= 0x3FF;
1061 }
1062
1063 void
1064 debugObj(int section, int level, const char *label, void *obj, ObjPackMethod pm)
1065 {
1066 MemBuf mb;
1067 Packer p;
1068 assert(label && obj && pm);
1069 mb.init();
1070 packerToMemInit(&p, &mb);
1071 (*pm) (obj, &p);
1072 debugs(section, level, "" << label << "" << mb.buf << "");
1073 packerClean(&p);
1074 mb.clean();
1075 }
1076
1077 void
1078 parseEtcHosts(void)
1079 {
1080 FILE *fp;
1081 char buf[1024];
1082 char buf2[512];
1083 char *nt = buf;
1084 char *lt = buf;
1085
1086 if (NULL == Config.etcHostsPath)
1087 return;
1088
1089 if (0 == strcmp(Config.etcHostsPath, "none"))
1090 return;
1091
1092 fp = fopen(Config.etcHostsPath, "r");
1093
1094 if (fp == NULL) {
1095 debugs(1, 1, "parseEtcHosts: " << Config.etcHostsPath << ": " << xstrerror());
1096 return;
1097 }
1098
1099 #ifdef _SQUID_WIN32_
1100 setmode(fileno(fp), O_TEXT);
1101
1102 #endif
1103
1104 while (fgets(buf, 1024, fp)) { /* for each line */
1105 wordlist *hosts = NULL;
1106 char *addr;
1107
1108 if (buf[0] == '#') /* MS-windows likes to add comments */
1109 continue;
1110
1111 strtok(buf, "#"); /* chop everything following a comment marker */
1112
1113 lt = buf;
1114
1115 addr = buf;
1116
1117 debugs(1, 5, "etc_hosts: line is '" << buf << "'");
1118
1119 nt = strpbrk(lt, w_space);
1120
1121 if (nt == NULL) /* empty line */
1122 continue;
1123
1124 *nt = '\0'; /* null-terminate the address */
1125
1126 debugs(1, 5, "etc_hosts: address is '" << addr << "'");
1127
1128 lt = nt + 1;
1129
1130 while ((nt = strpbrk(lt, w_space))) {
1131 char *host = NULL;
1132
1133 if (nt == lt) { /* multiple spaces */
1134 debugs(1, 5, "etc_hosts: multiple spaces, skipping");
1135 lt = nt + 1;
1136 continue;
1137 }
1138
1139 *nt = '\0';
1140 debugs(1, 5, "etc_hosts: got hostname '" << lt << "'");
1141
1142 if (Config.appendDomain && !strchr(lt, '.')) {
1143 /* I know it's ugly, but it's only at reconfig */
1144 strncpy(buf2, lt, 512);
1145 strncat(buf2, Config.appendDomain, 512 - strlen(lt) - 1);
1146 host = buf2;
1147 } else {
1148 host = lt;
1149 }
1150
1151 if (ipcacheAddEntryFromHosts(host, addr) != 0)
1152 goto skip; /* invalid address, continuing is useless */
1153
1154 wordlistAdd(&hosts, host);
1155
1156 lt = nt + 1;
1157 }
1158
1159 fqdncacheAddEntryFromHosts(addr, hosts);
1160
1161 skip:
1162 wordlistDestroy(&hosts);
1163 }
1164
1165 fclose (fp);
1166 }
1167
1168 int
1169 getMyPort(void)
1170 {
1171 if (Config.Sockaddr.http)
1172 return Config.Sockaddr.http->s.GetPort();
1173
1174 #if USE_SSL
1175
1176 if (Config.Sockaddr.https)
1177 return Config.Sockaddr.https->http.s.GetPort();
1178
1179 #endif
1180
1181 fatal("No port defined");
1182
1183 return 0; /* NOT REACHED */
1184 }
1185
1186
1187 /*
1188 * Inverse of strwordtok. Quotes a word if needed
1189 */
1190 void
1191 strwordquote(MemBuf * mb, const char *str)
1192 {
1193 int quoted = 0;
1194
1195 if (strchr(str, ' ')) {
1196 quoted = 1;
1197 mb->append("\"", 1);
1198 }
1199
1200 while (*str) {
1201 int l = strcspn(str, "\"\\\n\r");
1202 mb->append(str, l);
1203 str += l;
1204
1205 switch(*str) {
1206
1207 case '\n':
1208 mb->append("\\n", 2);
1209 str++;
1210 break;
1211
1212 case '\r':
1213 mb->append("\\r", 2);
1214 str++;
1215 break;
1216
1217 case '\0':
1218 break;
1219
1220 default:
1221 mb->append("\\", 1);
1222 mb->append(str, 1);
1223 str++;
1224 break;
1225 }
1226 }
1227
1228 if (quoted)
1229 mb->append("\"", 1);
1230 }
1231
1232 void
1233 keepCapabilities(void)
1234 {
1235 #if HAVE_PRCTL && defined(PR_SET_KEEPCAPS) && HAVE_SYS_CAPABILITY_H
1236
1237 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
1238 /* Silent failure unless TPROXY is required. Maybe not started as root */
1239 #if LINUX_TPROXY2 || LINUX_TPROXY4
1240
1241 if (need_linux_tproxy)
1242 debugs(1, 1, "Error - tproxy support requires capability setting which has failed. Continuing without tproxy support");
1243
1244 need_linux_tproxy = 0;
1245
1246 #endif
1247
1248 }
1249 #endif
1250 }
1251
1252 static void
1253 restoreCapabilities(int keep)
1254 {
1255 #if defined(_SQUID_LINUX_) && HAVE_SYS_CAPABILITY_H
1256 cap_user_header_t head = (cap_user_header_t) xcalloc(1, sizeof(cap_user_header_t));
1257 cap_user_data_t cap = (cap_user_data_t) xcalloc(1, sizeof(cap_user_data_t));
1258
1259 head->version = _LINUX_CAPABILITY_VERSION;
1260
1261 if (capget(head, cap) != 0) {
1262 debugs(50, 1, "Can't get current capabilities");
1263 goto nocap;
1264 }
1265
1266 if (head->version != _LINUX_CAPABILITY_VERSION) {
1267 debugs(50, 1, "Invalid capability version " << head->version << " (expected " << _LINUX_CAPABILITY_VERSION << ")");
1268 goto nocap;
1269 }
1270
1271 head->pid = 0;
1272
1273 cap->inheritable = 0;
1274 cap->effective = (1 << CAP_NET_BIND_SERVICE);
1275
1276 #if LINUX_TPROXY2
1277 if (need_linux_tproxy)
1278 cap->effective |= (1 << CAP_NET_ADMIN) | (1 << CAP_NET_BROADCAST);
1279 #elif LINUX_TPROXY4
1280 if (need_linux_tproxy)
1281 cap->effective |= (1 << CAP_NET_ADMIN);
1282
1283 #endif
1284
1285 if (!keep)
1286 cap->permitted &= cap->effective;
1287
1288 if (capset(head, cap) != 0) {
1289 /* Silent failure unless TPROXY is required */
1290 #if LINUX_TPROXY2 || LINUX_TPROXY4
1291
1292 if (need_linux_tproxy)
1293 debugs(50, 1, "Error enabling needed capabilities. Will continue without tproxy support");
1294
1295 need_linux_tproxy = 0;
1296
1297 #endif
1298
1299 }
1300
1301 nocap:
1302 xfree(head);
1303 xfree(cap);
1304 #else /* not defined(_SQUID_LINUX_) && HAVE_SYS_CAPABILITY_H */
1305
1306 #if LINUX_TPROXY2 || LINUX_TPROXY4
1307
1308 if (need_linux_tproxy)
1309 debugs(50, 1, "Missing needed capability support. Will continue without tproxy support");
1310
1311 need_linux_tproxy = 0;
1312 #endif
1313
1314 #endif
1315 }
1316
1317 void *
1318 xmemset(void *dst, int val, size_t sz)
1319 {
1320 // do debugs output
1321 debugs(63, 9, "memset: dst=" << dst << ", val=" << val << ", bytes=" << sz);
1322
1323 // call the system one to do the actual work ~safely.
1324 return memset(dst, val, sz);
1325 }