]> git.ipfire.org Git - thirdparty/squid.git/blame - src/tools.cc
Ability to read the configuration file from an external program pipe
[thirdparty/squid.git] / src / tools.cc
CommitLineData
94e891ea 1
30a4f2a8 2/*
fa2bfbab 3 * $Id: tools.cc,v 1.214 2002/03/30 16:29:51 hno Exp $
30a4f2a8 4 *
5 * DEBUG: section 21 Misc Functions
6 * AUTHOR: Harvest Derived
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
30a4f2a8 10 *
2b6662ba 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.
30a4f2a8 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
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
019dd986 34 */
44a47c6e 35
36#include "squid.h"
37
090089c4 38#define DEAD_MSG "\
c5c666ab 39The Squid Cache (version %s) died.\n\
090089c4 40\n\
c5c666ab 41You've encountered a fatal error in the Squid Cache version %s.\n\
090089c4 42If a core file was created (possibly in the swap directory),\n\
b8de7ebe 43please execute 'gdb squid core' or 'dbx squid core', then type 'where',\n\
784219b8 44and report the trace back to squid-bugs@squid-cache.org.\n\
090089c4 45\n\
46Thanks!\n"
47
f5b8bbc4 48static void fatal_common(const char *);
137ee196 49static void fatalvf(const char *fmt, va_list args);
f5b8bbc4 50static void mail_warranty(void);
63986a85 51#if MEM_GEN_TRACE
399e85ea 52extern void log_trace_done();
63986a85 53extern void log_trace_init(char *);
399e85ea 54#endif
24382924 55
941a3077 56#ifdef _SQUID_LINUX_
57/* Workaround for crappy glic header files */
58extern int backtrace(void *, int);
59extern void backtrace_symbols_fd(void *, int, int);
60extern int setresuid(uid_t, uid_t, uid_t);
61#endif /* _SQUID_LINUX */
62
7e3ce7b9 63extern void (*failure_notify) (const char *);
64
94439e4e 65MemPool *dlink_node_pool = NULL;
66
4a5a6c62 67void
429fdbec 68releaseServerSockets(void)
69{
812ed90c 70 int i;
429fdbec 71 /* Release the main ports as early as possible */
812ed90c 72 for (i = 0; i < NHttpSockets; i++) {
73 if (HttpSockets[i] >= 0)
88738790 74 close(HttpSockets[i]);
812ed90c 75 }
429fdbec 76 if (theInIcpConnection >= 0)
88738790 77 close(theInIcpConnection);
429fdbec 78 if (theOutIcpConnection >= 0 && theOutIcpConnection != theInIcpConnection)
88738790 79 close(theOutIcpConnection);
429fdbec 80}
81
b8d8561b 82static char *
0673c0ba 83dead_msg(void)
090089c4 84{
95d659f0 85 LOCAL_ARRAY(char, msg, 1024);
56878878 86 snprintf(msg, 1024, DEAD_MSG, version_string, version_string);
090089c4 87 return msg;
88}
89
24382924 90static void
0673c0ba 91mail_warranty(void)
090089c4 92{
019dd986 93 FILE *fp = NULL;
6e40f263 94 static char command[256];
b99a6dec 95#if HAVE_MKSTEMP
96 char filename[] = "/tmp/squid-XXXXXX";
97 int tfd = mkstemp(filename);
98 if (tfd < 0)
99 return;
100 if ((fp = fdopen(tfd, "w")) == NULL)
101 return;
102#else
103 char *filename;
6e40f263 104 if ((filename = tempnam(NULL, appname)) == NULL)
105 return;
106 if ((fp = fopen(filename, "w")) == NULL)
107 return;
b99a6dec 108#endif
6e40f263 109 fprintf(fp, "From: %s\n", appname);
edae7bc0 110 fprintf(fp, "To: %s\n", Config.adminEmail);
6e40f263 111 fprintf(fp, "Subject: %s\n", dead_msg());
112 fclose(fp);
56878878 113 snprintf(command, 256, "mail %s < %s", Config.adminEmail, filename);
6e40f263 114 system(command); /* XXX should avoid system(3) */
115 unlink(filename);
090089c4 116}
117
4a5a6c62 118void
43c3424b 119dumpMallocStats(void)
3f43b19b 120{
88738790 121#if HAVE_MSTATS && HAVE_GNUMALLOC_H
122 struct mstats ms = mstats();
123 fprintf(debug_log, "\ttotal space in arena: %6d KB\n",
a97cfa48 124 (int) (ms.bytes_total >> 10));
88738790 125 fprintf(debug_log, "\tTotal free: %6d KB %d%%\n",
a97cfa48 126 (int) (ms.bytes_free >> 10),
88738790 127 percent(ms.bytes_free, ms.bytes_total));
eb824054 128#elif HAVE_MALLINFO && HAVE_STRUCT_MALLINFO
3f43b19b 129 struct mallinfo mp;
30a4f2a8 130 int t;
3f43b19b 131 if (!do_mallinfo)
132 return;
3f43b19b 133 mp = mallinfo();
9e4ad609 134 fprintf(debug_log, "Memory usage for %s via mallinfo():\n", appname);
135 fprintf(debug_log, "\ttotal space in arena: %6d KB\n",
30a4f2a8 136 mp.arena >> 10);
9e4ad609 137 fprintf(debug_log, "\tOrdinary blocks: %6d KB %6d blks\n",
30a4f2a8 138 mp.uordblks >> 10, mp.ordblks);
9e4ad609 139 fprintf(debug_log, "\tSmall blocks: %6d KB %6d blks\n",
30a4f2a8 140 mp.usmblks >> 10, mp.smblks);
9e4ad609 141 fprintf(debug_log, "\tHolding blocks: %6d KB %6d blks\n",
30a4f2a8 142 mp.hblkhd >> 10, mp.hblks);
9e4ad609 143 fprintf(debug_log, "\tFree Small blocks: %6d KB\n",
30a4f2a8 144 mp.fsmblks >> 10);
9e4ad609 145 fprintf(debug_log, "\tFree Ordinary blocks: %6d KB\n",
30a4f2a8 146 mp.fordblks >> 10);
147 t = mp.uordblks + mp.usmblks + mp.hblkhd;
9e4ad609 148 fprintf(debug_log, "\tTotal in use: %6d KB %d%%\n",
30a4f2a8 149 t >> 10, percent(t, mp.arena));
150 t = mp.fsmblks + mp.fordblks;
9e4ad609 151 fprintf(debug_log, "\tTotal free: %6d KB %d%%\n",
30a4f2a8 152 t >> 10, percent(t, mp.arena));
46c883ed 153#if HAVE_EXT_MALLINFO
9e4ad609 154 fprintf(debug_log, "\tmax size of small blocks:\t%d\n",
30a4f2a8 155 mp.mxfast);
9e4ad609 156 fprintf(debug_log, "\tnumber of small blocks in a holding block:\t%d\n",
3f43b19b 157 mp.nlblks);
9e4ad609 158 fprintf(debug_log, "\tsmall block rounding factor:\t%d\n",
30a4f2a8 159 mp.grain);
9e4ad609 160 fprintf(debug_log, "\tspace (including overhead) allocated in ord. blks:\t%d\n",
3f43b19b 161 mp.uordbytes);
9e4ad609 162 fprintf(debug_log, "\tnumber of ordinary blocks allocated:\t%d\n",
3f43b19b 163 mp.allocated);
9e4ad609 164 fprintf(debug_log, "\tbytes used in maintaining the free tree:\t%d\n",
3f43b19b 165 mp.treeoverhead);
46c883ed 166#endif /* HAVE_EXT_MALLINFO */
3f43b19b 167#endif /* HAVE_MALLINFO */
168}
30a4f2a8 169
f2908497 170void
171squid_getrusage(struct rusage *r)
f0f81709 172{
8da94b66 173 memset(r, '\0', sizeof(struct rusage));
30a4f2a8 174#if HAVE_GETRUSAGE && defined(RUSAGE_SELF)
cd19f0b6 175#ifdef _SQUID_SOLARIS_
176 /* Solaris 2.5 has getrusage() permission bug -- Arjan de Vet */
177 enter_suid();
178#endif
f2908497 179 getrusage(RUSAGE_SELF, r);
cd19f0b6 180#ifdef _SQUID_SOLARIS_
181 leave_suid();
182#endif
56983a01 183#endif
f2908497 184}
185
186double
187rusage_cputime(struct rusage *r)
188{
189 return (double) r->ru_stime.tv_sec +
190 (double) r->ru_utime.tv_sec +
191 (double) r->ru_stime.tv_usec / 1000000.0 +
192 (double) r->ru_utime.tv_usec / 1000000.0;
193}
194
ec603b25 195/* Hack for some HP-UX preprocessors */
196#ifndef HAVE_GETPAGESIZE
197#define HAVE_GETPAGESIZE 0
198#endif
199
f2908497 200int
201rusage_maxrss(struct rusage *r)
202{
4fdc3986 203#if defined(_SQUID_SGI_) && _ABIAPI
204 return r->ru_pad[0];
205#elif defined(_SQUID_SGI_)
f2908497 206 return r->ru_maxrss;
207#elif defined(_SQUID_OSF_)
208 return r->ru_maxrss;
209#elif defined(BSD4_4)
210 return r->ru_maxrss;
6b8e7481 211#elif defined(HAVE_GETPAGESIZE) && HAVE_GETPAGESIZE != 0
f2908497 212 return (r->ru_maxrss * getpagesize()) >> 10;
c7a1083d 213#elif defined(PAGESIZE)
214 return (r->ru_maxrss * PAGESIZE) >> 10;
8505e57b 215#else
216 return r->ru_maxrss;
f2908497 217#endif
218}
219
220int
221rusage_pagefaults(struct rusage *r)
222{
4fdc3986 223#if defined(_SQUID_SGI_) && _ABIAPI
224 return r->ru_pad[5];
225#else
f2908497 226 return r->ru_majflt;
4fdc3986 227#endif
f2908497 228}
229
230
4a5a6c62 231void
f2908497 232PrintRusage(void)
233{
234 struct rusage rusage;
235 squid_getrusage(&rusage);
2b906e48 236 fprintf(debug_log, "CPU Usage: %.3f seconds = %.3f user + %.3f sys\n",
237 rusage_cputime(&rusage),
238 rusage.ru_utime.tv_sec + ((double) rusage.ru_utime.tv_usec / 1000000.0),
239 rusage.ru_stime.tv_sec + ((double) rusage.ru_stime.tv_usec / 1000000.0));
f2908497 240 fprintf(debug_log, "Maximum Resident Size: %d KB\n",
241 rusage_maxrss(&rusage));
242 fprintf(debug_log, "Page faults with physical i/o: %d\n",
243 rusage_pagefaults(&rusage));
f0f81709 244}
245
03d7b07f 246
b8d8561b 247void
248death(int sig)
44a47c6e 249{
0e08ce4b 250 if (sig == SIGSEGV)
2c47cf74 251 fprintf(debug_log, "FATAL: Received Segment Violation...dying.\n");
0e08ce4b 252 else if (sig == SIGBUS)
6e40f263 253 fprintf(debug_log, "FATAL: Received Bus Error...dying.\n");
0e08ce4b 254 else
2c47cf74 255 fprintf(debug_log, "FATAL: Received signal %d...dying.\n", sig);
0b4639af 256
257#ifdef PRINT_STACK_TRACE
258#ifdef _SQUID_HPUX_
259 {
4f07153c 260 extern void U_STACK_TRACE(void); /* link with -lcl */
0b4639af 261 fflush(debug_log);
262 dup2(fileno(debug_log), 2);
263 U_STACK_TRACE();
264 }
265#endif /* _SQUID_HPUX_ */
266#ifdef _SQUID_SOLARIS_
4f07153c 267 { /* get ftp://opcom.sun.ca/pub/tars/opcom_stack.tar.gz and */
268 extern void opcom_stack_trace(void); /* link with -lopcom_stack */
0b4639af 269 fflush(debug_log);
270 dup2(fileno(debug_log), fileno(stdout));
271 opcom_stack_trace();
272 fflush(stdout);
273 }
274#endif /* _SQUID_SOLARIS_ */
ce3d30fb 275#if HAVE_BACKTRACE_SYMBOLS_FD
276 {
277 static void *(callarray[8192]);
278 int n;
279 n = backtrace(callarray, 8192);
280 backtrace_symbols_fd(callarray, n, fileno(debug_log));
281 }
282#endif
0b4639af 283#endif /* PRINT_STACK_TRACE */
284
6e40f263 285#if SA_RESETHAND == 0
44a47c6e 286 signal(SIGSEGV, SIG_DFL);
287 signal(SIGBUS, SIG_DFL);
0e08ce4b 288 signal(sig, SIG_DFL);
30a4f2a8 289#endif
429fdbec 290 releaseServerSockets();
e3ef2b09 291 storeDirWriteCleanLogs(0);
9e4ad609 292 PrintRusage();
293 dumpMallocStats();
6e40f263 294 if (squid_curtime - SQUID_RELEASE_TIME < 864000) {
295 /* skip if more than 10 days old */
edae7bc0 296 if (Config.adminEmail)
6e40f263 297 mail_warranty();
298 else
299 puts(dead_msg());
300 }
44a47c6e 301 abort();
302}
303
304
b8d8561b 305void
23d92c64 306sigusr2_handle(int sig)
090089c4 307{
30a4f2a8 308 static int state = 0;
545f551a 309 /* no debug() here; bad things happen if the signal is delivered during _db_print() */
30a4f2a8 310 if (state == 0) {
2bf05976 311#ifndef MEM_GEN_TRACE
b6f794d6 312 _db_init(Config.Log.log, "ALL,10");
2bf05976 313#else
63986a85 314 log_trace_done();
315#endif
2bf05976 316 state = 1;
30a4f2a8 317 } else {
2bf05976 318#ifndef MEM_GEN_TRACE
b6f794d6 319 _db_init(Config.Log.log, Config.debugOptions);
2bf05976 320#else
63986a85 321 log_trace_init("/tmp/squid.alloc");
322#endif
2bf05976 323 state = 0;
30a4f2a8 324 }
325#if !HAVE_SIGACTION
326 signal(sig, sigusr2_handle); /* reinstall */
090089c4 327#endif
328}
329
24382924 330static void
0ee4272b 331fatal_common(const char *message)
090089c4 332{
db40ae20 333#if HAVE_SYSLOG
6b8e7481 334 syslog(LOG_ALERT, "%s", message);
db40ae20 335#endif
9bc73deb 336 fprintf(debug_log, "FATAL: %s\n", message);
337 if (opt_debug_stderr > 0 && debug_log != stderr)
338 fprintf(stderr, "FATAL: %s\n", message);
c5c666ab 339 fprintf(debug_log, "Squid Cache (Version %s): Terminated abnormally.\n",
8213067d 340 version_string);
2c47cf74 341 fflush(debug_log);
9e4ad609 342 PrintRusage();
343 dumpMallocStats();
090089c4 344}
345
346/* fatal */
b8d8561b 347void
0ee4272b 348fatal(const char *message)
090089c4 349{
429fdbec 350 releaseServerSockets();
b2c141d4 351 /* check for store_dirs_rebuilding because fatal() is often
d399be0e 352 * used in early initialization phases, long before we ever
353 * get to the store log. */
b2c141d4 354 if (0 == store_dirs_rebuilding)
e3ef2b09 355 storeDirWriteCleanLogs(0);
090089c4 356 fatal_common(message);
edd3d24f 357 if (shutting_down)
358 exit(0);
359 else
360 abort();
090089c4 361}
362
137ee196 363/* printf-style interface for fatal */
6de2df60 364#if STDC_HEADERS
137ee196 365void
366fatalf(const char *fmt,...)
367{
368 va_list args;
369 va_start(args, fmt);
370#else
371void
372fatalf(va_alist)
373 va_dcl
374{
375 va_list args;
376 const char *fmt = NULL;
377 va_start(args);
378 fmt = va_arg(args, char *);
379#endif
380 fatalvf(fmt, args);
381 va_end(args);
382}
383
384
385/* used by fatalf */
386static void
387fatalvf(const char *fmt, va_list args)
388{
389 static char fatal_str[BUFSIZ];
390 vsnprintf(fatal_str, sizeof(fatal_str), fmt, args);
391 fatal(fatal_str);
392}
393
090089c4 394/* fatal with dumping core */
b8d8561b 395void
0ee4272b 396fatal_dump(const char *message)
090089c4 397{
7e3ce7b9 398 failure_notify = NULL;
429fdbec 399 releaseServerSockets();
090089c4 400 if (message)
401 fatal_common(message);
4a69c163 402 if (opt_catch_signals)
e3ef2b09 403 storeDirWriteCleanLogs(0);
090089c4 404 abort();
405}
406
b8d8561b 407void
a3d5953d 408debug_trap(const char *message)
85b701ed 409{
4a69c163 410 if (!opt_catch_signals)
85b701ed 411 fatal_dump(message);
a3d5953d 412 _db_print("WARNING: %s\n", message);
85b701ed 413}
414
b8d8561b 415void
23d92c64 416sig_child(int sig)
090089c4 417{
30a4f2a8 418#ifdef _SQUID_NEXT_
419 union wait status;
420#else
090089c4 421 int status;
090089c4 422#endif
ff8d0ea6 423 pid_t pid;
090089c4 424
30a4f2a8 425 do {
426#ifdef _SQUID_NEXT_
427 pid = wait3(&status, WNOHANG, NULL);
090089c4 428#else
30a4f2a8 429 pid = waitpid(-1, &status, WNOHANG);
090089c4 430#endif
97a88399 431 /* no debug() here; bad things happen if the signal is delivered during _db_print() */
30a4f2a8 432#if HAVE_SIGACTION
433 } while (pid > 0);
234967c9 434#else
30a4f2a8 435 } while (pid > 0 || (pid < 0 && errno == EINTR));
436 signal(sig, sig_child);
234967c9 437#endif
30a4f2a8 438}
44a47c6e 439
0ee4272b 440const char *
0673c0ba 441getMyHostname(void)
44a47c6e 442{
95d659f0 443 LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN + 1);
44a47c6e 444 static int present = 0;
0ee4272b 445 const struct hostent *h = NULL;
af85f811 446 if (Config.visibleHostname != NULL)
447 return Config.visibleHostname;
d20b1cd0 448 if (present)
449 return host;
450 host[0] = '\0';
451 if (Config.Sockaddr.http->s.sin_addr.s_addr != any_addr.s_addr) {
7e3ce7b9 452 /*
453 * If the first http_port address has a specific address, try a
454 * reverse DNS lookup on it.
455 */
7e3ce7b9 456 h = gethostbyaddr((char *) &Config.Sockaddr.http->s.sin_addr,
457 sizeof(Config.Sockaddr.http->s.sin_addr), AF_INET);
af85f811 458 if (h != NULL) {
459 /* DNS lookup successful */
460 /* use the official name from DNS lookup */
d20b1cd0 461 xstrncpy(host, h->h_name, SQUIDHOSTNAMELEN);
462 debug(50, 4) ("getMyHostname: resolved %s to '%s'\n",
463 inet_ntoa(Config.Sockaddr.http->s.sin_addr),
af85f811 464 host);
465 present = 1;
bffee5af 466 if (strchr(host, '.'))
467 return host;
e396d395 468
af85f811 469 }
bffee5af 470 debug(50, 1) ("WARNING: failed to resolve %s to a fully qualified hostname\n",
d20b1cd0 471 inet_ntoa(Config.Sockaddr.http->s.sin_addr));
472 }
473 /*
474 * Get the host name and store it in host to return
475 */
476 if (gethostname(host, SQUIDHOSTNAMELEN) < 0) {
477 debug(50, 1) ("WARNING: gethostname failed: %s\n", xstrerror());
478 } else if ((h = gethostbyname(host)) == NULL) {
479 debug(50, 1) ("WARNING: gethostbyname failed for %s\n", host);
7e3ce7b9 480 } else {
d20b1cd0 481 debug(50, 6) ("getMyHostname: '%s' resolved into '%s'\n",
482 host, h->h_name);
483 /* DNS lookup successful */
484 /* use the official name from DNS lookup */
485 xstrncpy(host, h->h_name, SQUIDHOSTNAMELEN);
af85f811 486 present = 1;
bffee5af 487 if (strchr(host, '.'))
488 return host;
44a47c6e 489 }
d20b1cd0 490 fatal("Could not determine fully qualified hostname. Please set 'visible_hostname'\n");
491 return NULL; /* keep compiler happy */
44a47c6e 492}
493
98829f69 494const char *
495uniqueHostname(void)
496{
497 return Config.uniqueHostname ? Config.uniqueHostname : getMyHostname();
498}
499
0a0bf5db 500void
0ee4272b 501safeunlink(const char *s, int quiet)
44a47c6e 502{
83704487 503 statCounter.syscalls.disk.unlinks++;
0a0bf5db 504 if (unlink(s) < 0 && !quiet)
a3d5953d 505 debug(50, 1) ("safeunlink: Couldn't delete %s: %s\n", s, xstrerror());
0a0bf5db 506}
507
30a4f2a8 508/* leave a privilegied section. (Give up any privilegies)
509 * Routines that need privilegies can rap themselves in enter_suid()
510 * and leave_suid()
511 * To give upp all posibilites to gain privilegies use no_suid()
12b9e9b1 512 */
b8d8561b 513void
0673c0ba 514leave_suid(void)
12b9e9b1 515{
be20dac7 516 debug(21, 3) ("leave_suid: PID %d called\n", (int) getpid());
12b9e9b1 517 if (geteuid() != 0)
518 return;
519 /* Started as a root, check suid option */
b6f794d6 520 if (Config.effectiveUser == NULL)
12b9e9b1 521 return;
efd900cb 522#if HAVE_SETGROUPS
d20b1cd0 523 setgroups(1, &Config2.effectiveGroupID);
efd900cb 524#endif
d20b1cd0 525 if (setgid(Config2.effectiveGroupID) < 0)
526 debug(50, 0) ("ALERT: setgid: %s\n", xstrerror());
a3d5953d 527 debug(21, 3) ("leave_suid: PID %d giving up root, becoming '%s'\n",
be20dac7 528 (int) getpid(), Config.effectiveUser);
234967c9 529#if HAVE_SETRESUID
d20b1cd0 530 if (setresuid(Config2.effectiveUserID, Config2.effectiveUserID, 0) < 0)
531 debug(50, 0) ("ALERT: setresuid: %s\n", xstrerror());
234967c9 532#elif HAVE_SETEUID
d20b1cd0 533 if (seteuid(Config2.effectiveUserID) < 0)
534 debug(50, 0) ("ALERT: seteuid: %s\n", xstrerror());
234967c9 535#else
d20b1cd0 536 if (setuid(Config2.effectiveUserID) < 0)
537 debug(50, 0) ("ALERT: setuid: %s\n", xstrerror());
234967c9 538#endif
539}
540
30a4f2a8 541/* Enter a privilegied section */
b8d8561b 542void
0673c0ba 543enter_suid(void)
234967c9 544{
be20dac7 545 debug(21, 3) ("enter_suid: PID %d taking root priveleges\n", (int) getpid());
234967c9 546#if HAVE_SETRESUID
547 setresuid(-1, 0, -1);
548#else
549 setuid(0);
550#endif
551}
552
30a4f2a8 553/* Give up the posibility to gain privilegies.
554 * this should be used before starting a sub process
555 */
b8d8561b 556void
0673c0ba 557no_suid(void)
234967c9 558{
559 uid_t uid;
30a4f2a8 560 leave_suid();
234967c9 561 uid = geteuid();
be20dac7 562 debug(21, 3) ("leave_suid: PID %d giving up root priveleges forever\n", (int) getpid());
234967c9 563#if HAVE_SETRESUID
429fdbec 564 if (setresuid(uid, uid, uid) < 0)
a3d5953d 565 debug(50, 1) ("no_suid: setresuid: %s\n", xstrerror());
234967c9 566#else
567 setuid(0);
429fdbec 568 if (setuid(uid) < 0)
a3d5953d 569 debug(50, 1) ("no_suid: setuid: %s\n", xstrerror());
234967c9 570#endif
12b9e9b1 571}
ccff9601 572
b8d8561b 573void
0673c0ba 574writePidFile(void)
ccff9601 575{
9e4ad609 576 int fd;
0b4639af 577 const char *f = NULL;
973e9fe1 578 mode_t old_umask;
9e4ad609 579 char buf[32];
580 if ((f = Config.pidFilename) == NULL)
581 return;
582 if (!strcmp(Config.pidFilename, "none"))
ccff9601 583 return;
30a4f2a8 584 enter_suid();
973e9fe1 585 old_umask = umask(022);
c4aefe96 586 fd = file_open(f, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT);
973e9fe1 587 umask(old_umask);
30a4f2a8 588 leave_suid();
9e4ad609 589 if (fd < 0) {
a3d5953d 590 debug(50, 0) ("%s: %s\n", f, xstrerror());
9e4ad609 591 debug_trap("Could not write pid file");
592 return;
ccff9601 593 }
56878878 594 snprintf(buf, 32, "%d\n", (int) getpid());
1f7c9178 595 FD_WRITE_METHOD(fd, buf, strlen(buf));
9e4ad609 596 file_close(fd);
ccff9601 597}
c4fb974e 598
599
ff8d0ea6 600pid_t
0673c0ba 601readPidFile(void)
7690e8eb 602{
603 FILE *pid_fp = NULL;
9e4ad609 604 const char *f = Config.pidFilename;
ff8d0ea6 605 pid_t pid = -1;
606 int i;
7690e8eb 607
9e4ad609 608 if (f == NULL || !strcmp(Config.pidFilename, "none")) {
7690e8eb 609 fprintf(stderr, "%s: ERROR: No pid file name defined\n", appname);
610 exit(1);
611 }
612 pid_fp = fopen(f, "r");
613 if (pid_fp != NULL) {
ff8d0ea6 614 pid = 0;
615 if (fscanf(pid_fp, "%d", &i) == 1)
616 pid = (pid_t) i;
7690e8eb 617 fclose(pid_fp);
618 } else {
619 if (errno != ENOENT) {
620 fprintf(stderr, "%s: ERROR: Could not read pid file\n", appname);
621 fprintf(stderr, "\t%s: %s\n", f, xstrerror());
622 exit(1);
623 }
624 }
625 return pid;
626}
627
628
b8d8561b 629void
0673c0ba 630setMaxFD(void)
c4fb974e 631{
234967c9 632#if HAVE_SETRLIMIT
c4fb974e 633 /* try to use as many file descriptors as possible */
634 /* System V uses RLIMIT_NOFILE and BSD uses RLIMIT_OFILE */
635 struct rlimit rl;
636#if defined(RLIMIT_NOFILE)
637 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
a3d5953d 638 debug(50, 0) ("setrlimit: RLIMIT_NOFILE: %s\n", xstrerror());
c4fb974e 639 } else {
e83892e9 640 rl.rlim_cur = Squid_MaxFD;
30a4f2a8 641 if (rl.rlim_cur > rl.rlim_max)
e83892e9 642 Squid_MaxFD = rl.rlim_cur = rl.rlim_max;
c4fb974e 643 if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
56878878 644 snprintf(tmp_error_buf, ERROR_BUF_SZ,
645 "setrlimit: RLIMIT_NOFILE: %s", xstrerror());
234967c9 646 fatal_dump(tmp_error_buf);
c4fb974e 647 }
648 }
649#elif defined(RLIMIT_OFILE)
650 if (getrlimit(RLIMIT_OFILE, &rl) < 0) {
a3d5953d 651 debug(50, 0) ("setrlimit: RLIMIT_NOFILE: %s\n", xstrerror());
c4fb974e 652 } else {
e83892e9 653 rl.rlim_cur = Squid_MaxFD;
30a4f2a8 654 if (rl.rlim_cur > rl.rlim_max)
e83892e9 655 Squid_MaxFD = rl.rlim_cur = rl.rlim_max;
c4fb974e 656 if (setrlimit(RLIMIT_OFILE, &rl) < 0) {
066031f8 657 snprintf(tmp_error_buf, ERROR_BUF_SZ,
56878878 658 "setrlimit: RLIMIT_OFILE: %s", xstrerror());
234967c9 659 fatal_dump(tmp_error_buf);
c4fb974e 660 }
661 }
662#endif
c4fb974e 663#else /* HAVE_SETRLIMIT */
a3d5953d 664 debug(21, 1) ("setMaxFD: Cannot increase: setrlimit() not supported on this system\n");
30a4f2a8 665#endif /* HAVE_SETRLIMIT */
666
667#if HAVE_SETRLIMIT && defined(RLIMIT_DATA)
668 if (getrlimit(RLIMIT_DATA, &rl) < 0) {
a3d5953d 669 debug(50, 0) ("getrlimit: RLIMIT_DATA: %s\n", xstrerror());
88738790 670 } else if (rl.rlim_max > rl.rlim_cur) {
30a4f2a8 671 rl.rlim_cur = rl.rlim_max; /* set it to the max */
672 if (setrlimit(RLIMIT_DATA, &rl) < 0) {
56878878 673 snprintf(tmp_error_buf, ERROR_BUF_SZ,
674 "setrlimit: RLIMIT_DATA: %s", xstrerror());
30a4f2a8 675 fatal_dump(tmp_error_buf);
676 }
677 }
678#endif /* RLIMIT_DATA */
ad7ef91a 679#if HAVE_SETRLIMIT && defined(RLIMIT_VMEM)
680 if (getrlimit(RLIMIT_VMEM, &rl) < 0) {
a3d5953d 681 debug(50, 0) ("getrlimit: RLIMIT_VMEM: %s\n", xstrerror());
88738790 682 } else if (rl.rlim_max > rl.rlim_cur) {
ad7ef91a 683 rl.rlim_cur = rl.rlim_max; /* set it to the max */
684 if (setrlimit(RLIMIT_VMEM, &rl) < 0) {
56878878 685 snprintf(tmp_error_buf, ERROR_BUF_SZ,
686 "setrlimit: RLIMIT_VMEM: %s", xstrerror());
ad7ef91a 687 fatal_dump(tmp_error_buf);
688 }
689 }
690#endif /* RLIMIT_VMEM */
c4fb974e 691}
3a57089e 692
b8d8561b 693time_t
0673c0ba 694getCurrentTime(void)
3a57089e 695{
94e891ea 696#if GETTIMEOFDAY_NO_TZP
30a4f2a8 697 gettimeofday(&current_time);
698#else
d598947a 699 gettimeofday(&current_time, NULL);
5f3f8d0e 700#endif
52040193 701 current_dtime = (double) current_time.tv_sec +
702 (double) current_time.tv_usec / 1000000.0;
30a4f2a8 703 return squid_curtime = current_time.tv_sec;
5f3f8d0e 704}
fe113054 705
b8d8561b 706int
707percent(int a, int b)
30a4f2a8 708{
709 return b ? ((int) (100.0 * a / b + 0.5)) : 0;
710}
711
a7c05555 712double
f2908497 713dpercent(double a, double b)
714{
715 return b ? (100.0 * a / b) : 0.0;
716}
717
b8d8561b 718void
ea3a2a69 719squid_signal(int sig, SIGHDLR * func, int flags)
30a4f2a8 720{
721#if HAVE_SIGACTION
722 struct sigaction sa;
723 sa.sa_handler = func;
724 sa.sa_flags = flags;
725 sigemptyset(&sa.sa_mask);
726 if (sigaction(sig, &sa, NULL) < 0)
a3d5953d 727 debug(50, 0) ("sigaction: sig=%d func=%p: %s\n", sig, func, xstrerror());
30a4f2a8 728#else
88738790 729 signal(sig, func);
30a4f2a8 730#endif
731}
396c5745 732
16b204c4 733struct in_addr
0ee4272b 734inaddrFromHostent(const struct hostent *hp)
16b204c4 735{
cc6a9d2e 736 struct in_addr s;
3c0117c9 737 xmemcpy(&s.s_addr, hp->h_addr, sizeof(s.s_addr));
cc6a9d2e 738 return s;
16b204c4 739}
88738790 740
9e4ad609 741double
1d8e0d40 742doubleAverage(double cur, double new, int N, int max)
9e4ad609 743{
1d8e0d40 744 if (N > max)
745 N = max;
746 return (cur * (N - 1.0) + new) / N;
9e4ad609 747}
748
749int
750intAverage(int cur, int new, int n, int max)
751{
752 if (n > max)
753 n = max;
b14e03c0 754 return (cur * (n - 1) + new) / n;
9e4ad609 755}
70364f29 756
757void
758logsFlush(void)
759{
760 if (debug_log)
761 fflush(debug_log);
70364f29 762}
88738790 763
a2c963ae 764const char *
765checkNullString(const char *p)
88738790 766{
767 return p ? p : "(NULL)";
768}
2ac237e2 769
94439e4e 770dlink_node *
771dlinkNodeNew()
772{
773 if (dlink_node_pool == NULL)
774 dlink_node_pool = memPoolCreate("Dlink list nodes", sizeof(dlink_node));
775 /* where should we call memPoolDestroy(dlink_node_pool); */
776 return memPoolAlloc(dlink_node_pool);
777}
778
779/* the node needs to be unlinked FIRST */
780void
781dlinkNodeDelete(dlink_node * m)
782{
783 if (m == NULL)
784 return;
785 memPoolFree(dlink_node_pool, m);
786}
787
2ac237e2 788void
789dlinkAdd(void *data, dlink_node * m, dlink_list * list)
790{
791 m->data = data;
792 m->prev = NULL;
793 m->next = list->head;
794 if (list->head)
795 list->head->prev = m;
796 list->head = m;
797 if (list->tail == NULL)
798 list->tail = m;
799}
800
b2329b6a 801void
802dlinkAddTail(void *data, dlink_node * m, dlink_list * list)
803{
804 m->data = data;
805 m->next = NULL;
806 m->prev = list->tail;
807 if (list->tail)
808 list->tail->next = m;
809 list->tail = m;
810 if (list->head == NULL)
811 list->head = m;
812}
813
2ac237e2 814void
815dlinkDelete(dlink_node * m, dlink_list * list)
816{
817 if (m->next)
818 m->next->prev = m->prev;
819 if (m->prev)
820 m->prev->next = m->next;
821 if (m == list->head)
822 list->head = m->next;
823 if (m == list->tail)
824 list->tail = m->prev;
92b8c095 825 m->next = m->prev = NULL;
2ac237e2 826}
a7c05555 827
16300b58 828void
0e473d70 829kb_incr(kb_t * k, size_t v)
a7c05555 830{
0e473d70 831 k->bytes += v;
832 k->kb += (k->bytes >> 10);
833 k->bytes &= 0x3FF;
a7c05555 834}
c1dabf04 835
836void
399e85ea 837gb_flush(gb_t * g)
c1dabf04 838{
839 g->gb += (g->bytes >> 30);
399e85ea 840 g->bytes &= (1 << 30) - 1;
c1dabf04 841}
842
843double
399e85ea 844gb_to_double(const gb_t * g)
c1dabf04 845{
399e85ea 846 return ((double) g->gb) * ((double) (1 << 30)) + ((double) g->bytes);
c1dabf04 847}
848
849const char *
399e85ea 850gb_to_str(const gb_t * g)
c1dabf04 851{
852 /*
853 * it is often convenient to call gb_to_str several times for _one_ printf
854 */
399e85ea 855#define max_cc_calls 5
c1dabf04 856 typedef char GbBuf[32];
857 static GbBuf bufs[max_cc_calls];
858 static int call_id = 0;
859 double value = gb_to_double(g);
860 char *buf = bufs[call_id++];
1ecaa0a0 861 if (call_id >= max_cc_calls)
862 call_id = 0;
c1dabf04 863 /* select format */
864 if (value < 1e9)
399e85ea 865 snprintf(buf, sizeof(GbBuf), "%.2f MB", value / 1e6);
866 else if (value < 1e12)
867 snprintf(buf, sizeof(GbBuf), "%.2f GB", value / 1e9);
c1dabf04 868 else
399e85ea 869 snprintf(buf, sizeof(GbBuf), "%.2f TB", value / 1e12);
c1dabf04 870 return buf;
871}
a00a7c85 872
873void
6bbf9cc4 874debugObj(int section, int level, const char *label, void *obj, ObjPackMethod pm)
a00a7c85 875{
876 MemBuf mb;
877 Packer p;
6bbf9cc4 878 assert(label && obj && pm);
a00a7c85 879 memBufDefInit(&mb);
880 packerToMemInit(&p, &mb);
0cdcddb9 881 (*pm) (obj, &p);
6bbf9cc4 882 debug(section, level) ("%s%s", label, mb.buf);
a00a7c85 883 packerClean(&p);
884 memBufClean(&mb);
885}
d548ee64 886
887int
888stringHasWhitespace(const char *s)
889{
d5419d8e 890 return strpbrk(s, w_space) != NULL;
d548ee64 891}
d03d26fb 892
893void
894linklistPush(link_list ** L, void *p)
895{
58cd5bbd 896 link_list *l = memAllocate(MEM_LINK_LIST);
d03d26fb 897 l->next = NULL;
898 l->ptr = p;
899 while (*L)
900 L = &(*L)->next;
901 *L = l;
902}
903
904void *
905linklistShift(link_list ** L)
906{
907 void *p;
908 link_list *l;
909 if (NULL == *L)
910 return NULL;
911 l = *L;
912 p = l->ptr;
913 *L = (*L)->next;
58cd5bbd 914 memFree(l, MEM_LINK_LIST);
d03d26fb 915 return p;
916}
1f38f50a 917
1f38f50a 918/*
919 * Same as rename(2) but complains if something goes wrong;
920 * the caller is responsible for handing and explaining the
921 * consequences of errors.
922 */
923int
924xrename(const char *from, const char *to)
925{
926 debug(21, 2) ("xrename: renaming %s to %s\n", from, to);
927 if (0 == rename(from, to))
928 return 0;
929 debug(21, errno == ENOENT ? 2 : 1) ("xrename: Cannot rename %s to %s: %s\n",
930 from, to, xstrerror());
931 return -1;
932}
9bc73deb 933
934int
935stringHasCntl(const char *s)
936{
937 unsigned char c;
938 while ((c = (unsigned char) *s++) != '\0') {
939 if (c <= 0x1f)
940 return 1;
941 if (c >= 0x7f && c <= 0x9f)
942 return 1;
943 }
944 return 0;
945}
e4cc2fdf 946
947/*
948 * isPowTen returns true if its argument is an integer power of
949 * 10. Its used for logging of certain error messages that can
950 * occur often, but that we don't want to fill cache.log with.
951 */
952int
953isPowTen(int count)
954{
955 double x = log(count) / log(10.0);
956 if (0.0 != x - (double) (int) x)
957 return 0;
958 return 1;
959}
0e70aa1e 960
961void
962parseEtcHosts(void)
963{
964 FILE *fp;
965 char buf[1024];
966 char buf2[512];
967 char *nt = buf;
968 char *lt = buf;
fa2bfbab 969
0e70aa1e 970 if (NULL == Config.etcHostsPath)
971 return;
972 if (0 == strcmp(Config.etcHostsPath, "none"))
973 return;
974 fp = fopen(Config.etcHostsPath, "r");
975 if (fp == NULL) {
976 debug(1, 1) ("parseEtcHosts: %s: %s\n",
977 Config.etcHostsPath, xstrerror());
978 return;
979 }
b671cc68 980#if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
c4aefe96 981 setmode(fileno(fp), O_TEXT);
982#endif
0e70aa1e 983 while (fgets(buf, 1024, fp)) { /* for each line */
984 wordlist *hosts = NULL;
9ab292d9 985 char *addr;
0e70aa1e 986 if (buf[0] == '#') /* MS-windows likes to add comments */
987 continue;
988 lt = buf;
989 addr = buf;
990 debug(1, 5) ("etc_hosts: line is '%s'\n", buf);
991 nt = strpbrk(lt, w_space);
992 if (nt == NULL) /* empty line */
993 continue;
994 *nt = '\0'; /* null-terminate the address */
995 debug(1, 5) ("etc_hosts: address is '%s'\n", addr);
996 lt = nt + 1;
997 while ((nt = strpbrk(lt, w_space))) {
9ab292d9 998 char *host = NULL;
5d85e005 999 if (nt == lt) { /* multiple spaces */
0e70aa1e 1000 debug(1, 5) ("etc_hosts: multiple spaces, skipping\n");
1001 lt = nt + 1;
1002 continue;
1003 }
1004 *nt = '\0';
1005 debug(1, 5) ("etc_hosts: got hostname '%s'\n", lt);
1006 if (Config.appendDomain && !strchr(lt, '.')) {
1007 /* I know it's ugly, but it's only at reconfig */
1008 strncpy(buf2, lt, 512);
1009 strncat(buf2, Config.appendDomain, 512 - strlen(lt));
1010 host = buf2;
1011 } else {
1012 host = lt;
1013 }
0e70aa1e 1014 if (ipcacheAddEntryFromHosts(host, addr) != 0)
9ab292d9 1015 goto skip; /* invalid address, continuing is useless */
1016 wordlistAdd(&hosts, host);
0e70aa1e 1017 lt = nt + 1;
1018 }
1019 fqdncacheAddEntryFromHosts(addr, hosts);
be20dac7 1020 skip:
0e70aa1e 1021 wordlistDestroy(&hosts);
1022 }
1023}