]> git.ipfire.org Git - thirdparty/squid.git/blame - src/tools.cc
Boilerplate: update copyright blurbs on Squid helpers
[thirdparty/squid.git] / src / tools.cc
CommitLineData
30a4f2a8 1/*
30a4f2a8 2 * DEBUG: section 21 Misc Functions
3 * AUTHOR: Harvest Derived
4 *
2b6662ba 5 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 6 * ----------------------------------------------------------
30a4f2a8 7 *
2b6662ba 8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
30a4f2a8 16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
26ac0430 21 *
30a4f2a8 22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26ac0430 26 *
30a4f2a8 27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
cbdec147 29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 30 *
019dd986 31 */
44a47c6e 32
582c2af2 33#include "squid.h"
602d9612 34#include "anyp/PortCfg.h"
e0d28505 35#include "base/Subscription.h"
93da1f99 36#include "client_side.h"
438b04d4 37#include "disk.h"
528b2c61 38#include "fde.h"
95e6d864 39#include "fqdncache.h"
65d448bc 40#include "htcp.h"
e0d28505 41#include "ICP.h"
96d89ea0 42#include "ip/Intercept.h"
425de4c8 43#include "ip/QosConfig.h"
602d9612
A
44#include "ipc/Coordinator.h"
45#include "ipc/Kids.h"
46#include "ipcache.h"
0eb49b6d 47#include "MemBuf.h"
4d5904f7 48#include "SquidConfig.h"
a98bcbee 49#include "SquidMath.h"
985c86bc 50#include "SquidTime.h"
27bc2077 51#include "SwapDir.h"
602d9612 52#include "tools.h"
27bc2077 53#include "wordlist.h"
44a47c6e 54
1a30fdf5 55#include <cerrno>
62ae0622 56#if HAVE_SYS_PRCTL_H
57#include <sys/prctl.h>
58#endif
e30fe60a
AJ
59#if HAVE_WIN32_PSAPI
60#include <psapi.h>
61#endif
582c2af2
FC
62#if HAVE_SYS_STAT_H
63#include <sys/stat.h>
64#endif
65#if HAVE_SYS_WAIT_H
66#include <sys/wait.h>
67#endif
68#if HAVE_GRP_H
69#include <grp.h>
70#endif
62ae0622 71
090089c4 72#define DEAD_MSG "\
c5c666ab 73The Squid Cache (version %s) died.\n\
090089c4 74\n\
c5c666ab 75You've encountered a fatal error in the Squid Cache version %s.\n\
090089c4 76If a core file was created (possibly in the swap directory),\n\
b8de7ebe 77please execute 'gdb squid core' or 'dbx squid core', then type 'where',\n\
784219b8 78and report the trace back to squid-bugs@squid-cache.org.\n\
090089c4 79\n\
80Thanks!\n"
81
f5b8bbc4 82static void mail_warranty(void);
329ce686 83static void restoreCapabilities(int keep);
a2c48c98 84int DebugSignal = -1;
8b505ba9 85SBuf service_name(APP_SHORTNAME);
24382924 86
1191b93b 87#if _SQUID_LINUX_
941a3077 88/* Workaround for crappy glic header files */
e6ccf245 89SQUIDCEXTERN int backtrace(void *, int);
90SQUIDCEXTERN void backtrace_symbols_fd(void *, int, int);
91SQUIDCEXTERN int setresuid(uid_t, uid_t, uid_t);
10da753f
FC
92#else /* _SQUID_LINUX_ */
93/* needed on Opensolaris for backtrace_symbols_fd */
94#if HAVE_EXECINFO_H
95#include <execinfo.h>
96#endif /* HAVE_EXECINFO_H */
97
941a3077 98#endif /* _SQUID_LINUX */
99
4a5a6c62 100void
429fdbec 101releaseServerSockets(void)
102{
65d448bc 103 // Release the main ports as early as possible
62e76326 104
e7ce227f 105 // clear http_port, https_port, and ftp_port lists
434a79b0 106 clientConnectionsClose();
62e76326 107
e0d28505 108 // clear icp_port's
65d448bc 109 icpClosePorts();
62e76326 110
e0d28505 111 // XXX: Why not the HTCP, SNMP, DNS ports as well?
65d448bc 112 // XXX: why does this differ from main closeServerConnections() anyway ?
429fdbec 113}
114
b8d8561b 115static char *
0673c0ba 116dead_msg(void)
090089c4 117{
95d659f0 118 LOCAL_ARRAY(char, msg, 1024);
56878878 119 snprintf(msg, 1024, DEAD_MSG, version_string, version_string);
090089c4 120 return msg;
121}
122
24382924 123static void
0673c0ba 124mail_warranty(void)
090089c4 125{
019dd986 126 FILE *fp = NULL;
6e40f263 127 static char command[256];
50536c47 128
a2c13db1
AJ
129 /*
130 * NP: umask() takes the mask of bits we DONT want set.
131 *
132 * We want the current user to have read/write access
133 * and since this file will be passed to mailsystem,
134 * the group and other must have read access.
135 */
136 const mode_t prev_umask=umask(S_IXUSR|S_IXGRP|S_IWGRP|S_IWOTH|S_IXOTH);
62e76326 137
50536c47 138#if HAVE_MKSTEMP
b99a6dec 139 char filename[] = "/tmp/squid-XXXXXX";
140 int tfd = mkstemp(filename);
50536c47
FC
141 if (tfd < 0 || (fp = fdopen(tfd, "w")) == NULL) {
142 umask(prev_umask);
62e76326 143 return;
50536c47 144 }
b99a6dec 145#else
146 char *filename;
50536c47
FC
147 // XXX tempnam is obsolete since POSIX.2008-1
148 // tmpfile is not an option, we want the created files to stick around
149 if ((filename = tempnam(NULL, APP_SHORTNAME)) == NULL ||
6a1a5b78 150 (fp = fopen(filename, "w")) == NULL) {
50536c47 151 umask(prev_umask);
62e76326 152 return;
50536c47 153 }
b99a6dec 154#endif
50536c47 155 umask(prev_umask);
62e76326 156
abacf776 157 if (Config.EmailFrom)
158 fprintf(fp, "From: %s\n", Config.EmailFrom);
159 else
7dbca7a4 160 fprintf(fp, "From: %s@%s\n", APP_SHORTNAME, uniqueHostname());
62e76326 161
edae7bc0 162 fprintf(fp, "To: %s\n", Config.adminEmail);
6e40f263 163 fprintf(fp, "Subject: %s\n", dead_msg());
164 fclose(fp);
62e76326 165
d084bf20 166 snprintf(command, 256, "%s %s < %s", Config.EmailProgram, Config.adminEmail, filename);
26ac0430 167 if (system(command)) {} /* XXX should avoid system(3) */
6e40f263 168 unlink(filename);
87cedd31
AJ
169#if !HAVE_MKSTEMP
170 xfree(filename); // tempnam() requires us to free its allocation
171#endif
090089c4 172}
173
4a5a6c62 174void
43c3424b 175dumpMallocStats(void)
3f43b19b 176{
88738790 177#if HAVE_MSTATS && HAVE_GNUMALLOC_H
62e76326 178
88738790 179 struct mstats ms = mstats();
180 fprintf(debug_log, "\ttotal space in arena: %6d KB\n",
62e76326 181 (int) (ms.bytes_total >> 10));
88738790 182 fprintf(debug_log, "\tTotal free: %6d KB %d%%\n",
62e76326 183 (int) (ms.bytes_free >> 10),
a98bcbee 184 Math::intPercent(ms.bytes_free, ms.bytes_total));
4572073a 185#endif
3f43b19b 186}
30a4f2a8 187
f2908497 188void
189squid_getrusage(struct rusage *r)
f0f81709 190{
8da94b66 191 memset(r, '\0', sizeof(struct rusage));
e30fe60a 192#if HAVE_GETRUSAGE && defined(RUSAGE_SELF) && !_SQUID_WINDOWS_
1da023b5 193#if _SQUID_SOLARIS_
cd19f0b6 194 /* Solaris 2.5 has getrusage() permission bug -- Arjan de Vet */
195 enter_suid();
196#endif
62e76326 197
f2908497 198 getrusage(RUSAGE_SELF, r);
62e76326 199
e30fe60a 200#if _SQUID_SOLARIS_
cd19f0b6 201 leave_suid();
202#endif
e30fe60a
AJ
203
204#elif _SQUID_WINDOWS_ && HAVE_WIN32_PSAPI
205 // Windows has an alternative method if there is no POSIX getrusage defined.
206 if (WIN32_OS_version >= _WIN_OS_WINNT) {
207 /* On Windows NT and later call PSAPI.DLL for process Memory */
208 /* informations -- Guido Serassio */
209 HANDLE hProcess;
210 PROCESS_MEMORY_COUNTERS pmc;
211 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
212 PROCESS_VM_READ,
213 FALSE, GetCurrentProcessId());
214 {
215 /* Microsoft CRT doesn't have getrusage function, */
216 /* so we get process CPU time information from PSAPI.DLL. */
217 FILETIME ftCreate, ftExit, ftKernel, ftUser;
218 if (GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) {
219 int64_t *ptUser = (int64_t *)&ftUser;
220 int64_t tUser64 = *ptUser / 10;
221 int64_t *ptKernel = (int64_t *)&ftKernel;
222 int64_t tKernel64 = *ptKernel / 10;
223 r->ru_utime.tv_sec =(long)(tUser64 / 1000000);
224 r->ru_stime.tv_sec =(long)(tKernel64 / 1000000);
225 r->ru_utime.tv_usec =(long)(tUser64 % 1000000);
226 r->ru_stime.tv_usec =(long)(tKernel64 % 1000000);
227 } else {
228 CloseHandle( hProcess );
229 return;
230 }
231 }
232 if (GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc))) {
233 r->ru_maxrss=(DWORD)(pmc.WorkingSetSize / getpagesize());
234 r->ru_majflt=pmc.PageFaultCount;
235 } else {
236 CloseHandle( hProcess );
237 return;
238 }
239
240 CloseHandle( hProcess );
241 }
56983a01 242#endif
f2908497 243}
244
245double
62e76326 246
f2908497 247rusage_cputime(struct rusage *r)
248{
249 return (double) r->ru_stime.tv_sec +
62e76326 250 (double) r->ru_utime.tv_sec +
251 (double) r->ru_stime.tv_usec / 1000000.0 +
252 (double) r->ru_utime.tv_usec / 1000000.0;
f2908497 253}
254
ec603b25 255/* Hack for some HP-UX preprocessors */
256#ifndef HAVE_GETPAGESIZE
257#define HAVE_GETPAGESIZE 0
258#endif
259
f2908497 260int
62e76326 261
f2908497 262rusage_maxrss(struct rusage *r)
263{
8a09e810 264#if _SQUID_SGI_ && _ABIAPI
4fdc3986 265 return r->ru_pad[0];
8a09e810 266#elif _SQUID_SGI_|| _SQUID_OSF_ || _SQUID_AIX_ || defined(BSD4_4)
62e76326 267
f2908497 268 return r->ru_maxrss;
6b8e7481 269#elif defined(HAVE_GETPAGESIZE) && HAVE_GETPAGESIZE != 0
62e76326 270
f2908497 271 return (r->ru_maxrss * getpagesize()) >> 10;
c7a1083d 272#elif defined(PAGESIZE)
62e76326 273
c7a1083d 274 return (r->ru_maxrss * PAGESIZE) >> 10;
8505e57b 275#else
62e76326 276
8505e57b 277 return r->ru_maxrss;
f2908497 278#endif
279}
280
281int
62e76326 282
f2908497 283rusage_pagefaults(struct rusage *r)
284{
8a09e810 285#if _SQUID_SGI_ && _ABIAPI
4fdc3986 286 return r->ru_pad[5];
287#else
62e76326 288
f2908497 289 return r->ru_majflt;
4fdc3986 290#endif
f2908497 291}
292
4a5a6c62 293void
f2908497 294PrintRusage(void)
295{
62e76326 296
f2908497 297 struct rusage rusage;
298 squid_getrusage(&rusage);
2b906e48 299 fprintf(debug_log, "CPU Usage: %.3f seconds = %.3f user + %.3f sys\n",
62e76326 300 rusage_cputime(&rusage),
301 rusage.ru_utime.tv_sec + ((double) rusage.ru_utime.tv_usec / 1000000.0),
302 rusage.ru_stime.tv_sec + ((double) rusage.ru_stime.tv_usec / 1000000.0));
f2908497 303 fprintf(debug_log, "Maximum Resident Size: %d KB\n",
62e76326 304 rusage_maxrss(&rusage));
f2908497 305 fprintf(debug_log, "Page faults with physical i/o: %d\n",
62e76326 306 rusage_pagefaults(&rusage));
f0f81709 307}
308
b8d8561b 309void
310death(int sig)
44a47c6e 311{
0e08ce4b 312 if (sig == SIGSEGV)
62e76326 313 fprintf(debug_log, "FATAL: Received Segment Violation...dying.\n");
0e08ce4b 314 else if (sig == SIGBUS)
62e76326 315 fprintf(debug_log, "FATAL: Received Bus Error...dying.\n");
0e08ce4b 316 else
62e76326 317 fprintf(debug_log, "FATAL: Received signal %d...dying.\n", sig);
0b4639af 318
0a081cb0 319#if PRINT_STACK_TRACE
1191b93b 320#if _SQUID_HPUX_
0b4639af 321 {
62e76326 322 extern void U_STACK_TRACE(void); /* link with -lcl */
323 fflush(debug_log);
324 dup2(fileno(debug_log), 2);
325 U_STACK_TRACE();
0b4639af 326 }
62e76326 327
0b4639af 328#endif /* _SQUID_HPUX_ */
8a09e810 329#if _SQUID_SOLARIS_ && HAVE_LIBOPCOM_STACK
4f07153c 330 { /* get ftp://opcom.sun.ca/pub/tars/opcom_stack.tar.gz and */
62e76326 331 extern void opcom_stack_trace(void); /* link with -lopcom_stack */
332 fflush(debug_log);
333 dup2(fileno(debug_log), fileno(stdout));
334 opcom_stack_trace();
335 fflush(stdout);
0b4639af 336 }
62e76326 337
10da753f 338#endif /* _SQUID_SOLARIS_and HAVE_LIBOPCOM_STACK */
ce3d30fb 339#if HAVE_BACKTRACE_SYMBOLS_FD
340 {
62e76326 341 static void *(callarray[8192]);
342 int n;
343 n = backtrace(callarray, 8192);
344 backtrace_symbols_fd(callarray, n, fileno(debug_log));
ce3d30fb 345 }
62e76326 346
ce3d30fb 347#endif
0b4639af 348#endif /* PRINT_STACK_TRACE */
349
7aa9bb3e 350#if SA_RESETHAND == 0 && !_SQUID_WINDOWS_
44a47c6e 351 signal(SIGSEGV, SIG_DFL);
62e76326 352
44a47c6e 353 signal(SIGBUS, SIG_DFL);
62e76326 354
0e08ce4b 355 signal(sig, SIG_DFL);
62e76326 356
30a4f2a8 357#endif
62e76326 358
429fdbec 359 releaseServerSockets();
62e76326 360
e3ef2b09 361 storeDirWriteCleanLogs(0);
62e76326 362
4b0f5de8 363 if (!shutting_down) {
364 PrintRusage();
62e76326 365
4b0f5de8 366 dumpMallocStats();
367 }
62e76326 368
6e40f263 369 if (squid_curtime - SQUID_RELEASE_TIME < 864000) {
62e76326 370 /* skip if more than 10 days old */
371
372 if (Config.adminEmail)
373 mail_warranty();
329ce686 374
375 puts(dead_msg());
6e40f263 376 }
62e76326 377
44a47c6e 378 abort();
379}
380
a2c48c98
AR
381void
382BroadcastSignalIfAny(int& sig)
383{
384 if (sig > 0) {
7de94c8c
AR
385 if (IamCoordinatorProcess())
386 Ipc::Coordinator::Instance()->broadcastSignal(sig);
a2c48c98
AR
387 sig = -1;
388 }
389}
44a47c6e 390
b8d8561b 391void
23d92c64 392sigusr2_handle(int sig)
090089c4 393{
30a4f2a8 394 static int state = 0;
96e03dd8 395 /* no debugs() here; bad things happen if the signal is delivered during _db_print() */
62e76326 396
a2c48c98
AR
397 DebugSignal = sig;
398
30a4f2a8 399 if (state == 0) {
d7298d24 400 Debug::parseOptions("ALL,7");
62e76326 401 state = 1;
30a4f2a8 402 } else {
62493678 403 Debug::parseOptions(Debug::debugOptions);
62e76326 404 state = 0;
30a4f2a8 405 }
62e76326 406
30a4f2a8 407#if !HAVE_SIGACTION
e504b0a7 408 if (signal(sig, sigusr2_handle) == SIG_ERR) /* reinstall */
fa84c01d 409 debugs(50, DBG_CRITICAL, "signal: sig=" << sig << " func=sigusr2_handle: " << xstrerror());
62e76326 410
090089c4 411#endif
412}
413
b8d8561b 414void
af6a12ee
AJ
415debug_trap(const char *message)
416{
4a69c163 417 if (!opt_catch_signals)
62e76326 418 fatal_dump(message);
419
a3d5953d 420 _db_print("WARNING: %s\n", message);
85b701ed 421}
422
b8d8561b 423void
af6a12ee
AJ
424sig_child(int sig)
425{
7aa9bb3e 426#if !_SQUID_WINDOWS_
1191b93b 427#if _SQUID_NEXT_
30a4f2a8 428 union wait status;
429#else
62e76326 430
090089c4 431 int status;
090089c4 432#endif
62e76326 433
ff8d0ea6 434 pid_t pid;
090089c4 435
30a4f2a8 436 do {
1191b93b 437#if _SQUID_NEXT_
62e76326 438 pid = wait3(&status, WNOHANG, NULL);
090089c4 439#else
62e76326 440
441 pid = waitpid(-1, &status, WNOHANG);
090089c4 442#endif
96e03dd8 443 /* no debugs() here; bad things happen if the signal is delivered during _db_print() */
30a4f2a8 444#if HAVE_SIGACTION
62e76326 445
30a4f2a8 446 } while (pid > 0);
62e76326 447
234967c9 448#else
62e76326 449
450 }
451
3d0ac046 452 while (pid > 0 || (pid < 0 && errno == EINTR));
30a4f2a8 453 signal(sig, sig_child);
62e76326 454
a50bfe93 455#endif
234967c9 456#endif
30a4f2a8 457}
44a47c6e 458
ca4b9ee6
DK
459void
460sig_shutdown(int sig)
461{
462 shutting_down = 1;
463}
464
0ee4272b 465const char *
0673c0ba 466getMyHostname(void)
44a47c6e 467{
95d659f0 468 LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN + 1);
44a47c6e 469 static int present = 0;
cc192b50 470 struct addrinfo *AI = NULL;
b7ac5457 471 Ip::Address sa;
62e76326 472
af85f811 473 if (Config.visibleHostname != NULL)
62e76326 474 return Config.visibleHostname;
475
d20b1cd0 476 if (present)
62e76326 477 return host;
478
d20b1cd0 479 host[0] = '\0';
62e76326 480
fa720bfb
AJ
481 if (HttpPortList != NULL && sa.isAnyAddr())
482 sa = HttpPortList->s;
62e76326 483
cb4f4424 484#if USE_OPENSSL
62e76326 485
fa720bfb
AJ
486 if (HttpsPortList != NULL && sa.isAnyAddr())
487 sa = HttpsPortList->s;
62e76326 488
aa2b5399 489#endif
cc192b50 490
aa2b5399 491 /*
492 * If the first http_port address has a specific address, try a
493 * reverse DNS lookup on it.
494 */
4dd643d5 495 if ( !sa.isAnyAddr() ) {
62e76326 496
4dd643d5 497 sa.getAddrInfo(AI);
cc192b50 498 /* we are looking for a name. */
27bc2077 499 if (getnameinfo(AI->ai_addr, AI->ai_addrlen, host, SQUIDHOSTNAMELEN, NULL, 0, NI_NAMEREQD ) == 0) {
62e76326 500 /* DNS lookup successful */
501 /* use the official name from DNS lookup */
cc192b50 502 debugs(50, 4, "getMyHostname: resolved " << sa << " to '" << host << "'");
bf8fe701 503
62e76326 504 present = 1;
505
4dd643d5 506 Ip::Address::FreeAddrInfo(AI);
cc192b50 507
62e76326 508 if (strchr(host, '.'))
509 return host;
62e76326 510 }
511
4dd643d5 512 Ip::Address::FreeAddrInfo(AI);
c19d1320
AJ
513 debugs(50, 2, "WARNING: failed to resolve " << sa << " to a fully qualified hostname");
514 }
515
516 // still no host. fallback to gethostname()
517 if (gethostname(host, SQUIDHOSTNAMELEN) < 0) {
518 debugs(50, DBG_IMPORTANT, "WARNING: gethostname failed: " << xstrerror());
26ac0430 519 } else {
c19d1320
AJ
520 /* Verify that the hostname given resolves properly */
521 struct addrinfo hints;
522 memset(&hints, 0, sizeof(addrinfo));
523 hints.ai_flags = AI_CANONNAME;
62e76326 524
c19d1320
AJ
525 if (getaddrinfo(host, NULL, NULL, &AI) == 0) {
526 /* DNS lookup successful */
527 /* use the official name from DNS lookup */
528 debugs(50, 6, "getMyHostname: '" << host << "' has DNS resolution.");
529 present = 1;
530
531 /* AYJ: do we want to flag AI_ALL and cache the result anywhere. ie as our local host IPs? */
4dd643d5 532 if (AI)
c19d1320 533 freeaddrinfo(AI);
cc192b50 534
c19d1320 535 return host;
cc192b50 536 }
c19d1320 537
4dd643d5
AJ
538 if (AI)
539 freeaddrinfo(AI);
c19d1320 540 debugs(50, DBG_IMPORTANT, "WARNING: '" << host << "' rDNS test failed: " << xstrerror());
44a47c6e 541 }
62e76326 542
c19d1320
AJ
543 /* throw a configuration error when the Host/IP given has bad DNS/rDNS. */
544 debugs(50, DBG_CRITICAL, "WARNING: Could not determine this machines public hostname. " <<
545 "Please configure one or set 'visible_hostname'.");
a572d8be 546
c19d1320 547 return ("localhost");
44a47c6e 548}
549
98829f69 550const char *
551uniqueHostname(void)
552{
23c6ebc4 553 debugs(21, 3, HERE << " Config: '" << Config.uniqueHostname << "'");
98829f69 554 return Config.uniqueHostname ? Config.uniqueHostname : getMyHostname();
555}
556
23c6ebc4 557/** leave a priviliged section. (Give up any privilegies)
30a4f2a8 558 * Routines that need privilegies can rap themselves in enter_suid()
559 * and leave_suid()
560 * To give upp all posibilites to gain privilegies use no_suid()
12b9e9b1 561 */
b8d8561b 562void
0673c0ba 563leave_suid(void)
12b9e9b1 564{
36584579 565 debugs(21, 3, "leave_suid: PID " << getpid() << " called");
62e76326 566
e3d74828 567 if (Config.effectiveGroup) {
568
569#if HAVE_SETGROUPS
570
571 setgroups(1, &Config2.effectiveGroupID);
572
573#endif
574
575 if (setgid(Config2.effectiveGroupID) < 0)
fa84c01d 576 debugs(50, DBG_CRITICAL, "ALERT: setgid: " << xstrerror());
e3d74828 577
578 }
579
12b9e9b1 580 if (geteuid() != 0)
62e76326 581 return;
582
12b9e9b1 583 /* Started as a root, check suid option */
b6f794d6 584 if (Config.effectiveUser == NULL)
62e76326 585 return;
586
36584579 587 debugs(21, 3, "leave_suid: PID " << getpid() << " giving up root, becoming '" << Config.effectiveUser << "'");
62e76326 588
e3d74828 589 if (!Config.effectiveGroup) {
62e76326 590
e3d74828 591 if (setgid(Config2.effectiveGroupID) < 0)
fa84c01d 592 debugs(50, DBG_CRITICAL, "ALERT: setgid: " << xstrerror());
62e76326 593
e3d74828 594 if (initgroups(Config.effectiveUser, Config2.effectiveGroupID) < 0) {
fa84c01d 595 debugs(50, DBG_CRITICAL, "ALERT: initgroups: unable to set groups for User " <<
bf8fe701 596 Config.effectiveUser << " and Group " <<
597 (unsigned) Config2.effectiveGroupID << "");
e3d74828 598 }
599 }
62e76326 600
234967c9 601#if HAVE_SETRESUID
62e76326 602
d20b1cd0 603 if (setresuid(Config2.effectiveUserID, Config2.effectiveUserID, 0) < 0)
fa84c01d 604 debugs(50, DBG_CRITICAL, "ALERT: setresuid: " << xstrerror());
62e76326 605
234967c9 606#elif HAVE_SETEUID
62e76326 607
d20b1cd0 608 if (seteuid(Config2.effectiveUserID) < 0)
fa84c01d 609 debugs(50, DBG_CRITICAL, "ALERT: seteuid: " << xstrerror());
62e76326 610
234967c9 611#else
62e76326 612
d20b1cd0 613 if (setuid(Config2.effectiveUserID) < 0)
fa84c01d 614 debugs(50, DBG_CRITICAL, "ALERT: setuid: " << xstrerror());
62e76326 615
fc68f6b1 616#endif
fc68f6b1 617
329ce686 618 restoreCapabilities(1);
fc68f6b1 619
62ae0622 620#if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
621 /* Set Linux DUMPABLE flag */
622 if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0)
bf8fe701 623 debugs(50, 2, "ALERT: prctl: " << xstrerror());
62ae0622 624
234967c9 625#endif
626}
627
30a4f2a8 628/* Enter a privilegied section */
b8d8561b 629void
0673c0ba 630enter_suid(void)
234967c9 631{
77468ee5 632 debugs(21, 3, "enter_suid: PID " << getpid() << " taking root privileges");
234967c9 633#if HAVE_SETRESUID
77468ee5 634 if (setresuid((uid_t)-1, 0, (uid_t)-1) < 0)
6595704b 635 debugs (21, 3, "enter_suid: setresuid failed: " << xstrerror ());
234967c9 636#else
62e76326 637
234967c9 638 setuid(0);
639#endif
62ae0622 640#if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
641 /* Set Linux DUMPABLE flag */
642
643 if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0)
bf8fe701 644 debugs(50, 2, "ALERT: prctl: " << xstrerror());
62ae0622 645
646#endif
234967c9 647}
648
30a4f2a8 649/* Give up the posibility to gain privilegies.
650 * this should be used before starting a sub process
651 */
b8d8561b 652void
0673c0ba 653no_suid(void)
234967c9 654{
655 uid_t uid;
30a4f2a8 656 leave_suid();
234967c9 657 uid = geteuid();
329ce686 658 debugs(21, 3, "no_suid: PID " << getpid() << " giving up root priveleges forever");
62e76326 659
77468ee5 660 if (setuid(0) < 0)
6595704b 661 debugs(50, DBG_IMPORTANT, "WARNING: no_suid: setuid(0): " << xstrerror());
62e76326 662
429fdbec 663 if (setuid(uid) < 0)
77468ee5 664 debugs(50, DBG_IMPORTANT, "ERROR: no_suid: setuid(" << uid << "): " << xstrerror());
62e76326 665
329ce686 666 restoreCapabilities(0);
667
62ae0622 668#if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
669 /* Set Linux DUMPABLE flag */
670 if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0)
bf8fe701 671 debugs(50, 2, "ALERT: prctl: " << xstrerror());
62ae0622 672
234967c9 673#endif
12b9e9b1 674}
ccff9601 675
a2c48c98 676bool
7de94c8c 677IamMasterProcess()
a2c48c98 678{
7de94c8c
AR
679 return KidIdentifier == 0;
680}
681
682bool
683IamWorkerProcess()
684{
685 // when there is only one process, it has to be the worker
13aeac35 686 if (opt_no_daemon || Config.workers == 0)
7de94c8c
AR
687 return true;
688
095ec2b1
AR
689 return TheProcessKind == pkWorker;
690}
691
692bool
693IamDiskProcess()
694{
695 return TheProcessKind == pkDisker;
7de94c8c
AR
696}
697
96c2bb61
AR
698bool
699InDaemonMode()
700{
701 return !opt_no_daemon && Config.workers > 0;
702}
703
7de94c8c
AR
704bool
705UsingSmp()
706{
095ec2b1 707 return InDaemonMode() && NumberOfKids() > 1;
7de94c8c
AR
708}
709
710bool
711IamCoordinatorProcess()
712{
095ec2b1 713 return TheProcessKind == pkCoordinator;
7de94c8c
AR
714}
715
716bool
717IamPrimaryProcess()
718{
719 // when there is only one process, it has to be primary
13aeac35 720 if (opt_no_daemon || Config.workers == 0)
7de94c8c
AR
721 return true;
722
723 // when there is a master and worker process, the master delegates
724 // primary functions to its only kid
254912f3 725 if (NumberOfKids() == 1)
7de94c8c 726 return IamWorkerProcess();
a2c48c98 727
7de94c8c
AR
728 // in SMP mode, multiple kids delegate primary functions to the coordinator
729 return IamCoordinatorProcess();
a2c48c98
AR
730}
731
96c2bb61
AR
732int
733NumberOfKids()
734{
735 // no kids in no-daemon mode
736 if (!InDaemonMode())
737 return 0;
738
095ec2b1
AR
739 // XXX: detect and abort when called before workers/cache_dirs are parsed
740
14911a4e 741 const int rockDirs = Config.cacheSwap.n_strands;
095ec2b1
AR
742
743 const bool needCoord = Config.workers > 1 || rockDirs > 0;
744 return (needCoord ? 1 : 0) + Config.workers + rockDirs;
745}
96c2bb61 746
095ec2b1
AR
747String
748ProcessRoles()
749{
750 String roles = "";
751 if (IamMasterProcess())
752 roles.append(" master");
753 if (IamCoordinatorProcess())
754 roles.append(" coordinator");
755 if (IamWorkerProcess())
756 roles.append(" worker");
757 if (IamDiskProcess())
758 roles.append(" disker");
759 return roles;
96c2bb61
AR
760}
761
b8d8561b 762void
0673c0ba 763writePidFile(void)
ccff9601 764{
9e4ad609 765 int fd;
0b4639af 766 const char *f = NULL;
973e9fe1 767 mode_t old_umask;
9e4ad609 768 char buf[32];
62e76326 769
7de94c8c 770 if (!IamPrimaryProcess())
a2c48c98
AR
771 return;
772
9e4ad609 773 if ((f = Config.pidFilename) == NULL)
62e76326 774 return;
775
9e4ad609 776 if (!strcmp(Config.pidFilename, "none"))
62e76326 777 return;
778
30a4f2a8 779 enter_suid();
62e76326 780
973e9fe1 781 old_umask = umask(022);
62e76326 782
c4aefe96 783 fd = file_open(f, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT);
62e76326 784
973e9fe1 785 umask(old_umask);
62e76326 786
30a4f2a8 787 leave_suid();
62e76326 788
9e4ad609 789 if (fd < 0) {
fa84c01d 790 debugs(50, DBG_CRITICAL, "" << f << ": " << xstrerror());
62e76326 791 debug_trap("Could not write pid file");
792 return;
ccff9601 793 }
62e76326 794
56878878 795 snprintf(buf, 32, "%d\n", (int) getpid());
1f7c9178 796 FD_WRITE_METHOD(fd, buf, strlen(buf));
9e4ad609 797 file_close(fd);
ccff9601 798}
c4fb974e 799
ff8d0ea6 800pid_t
0673c0ba 801readPidFile(void)
7690e8eb 802{
803 FILE *pid_fp = NULL;
9e4ad609 804 const char *f = Config.pidFilename;
1ee742a2 805 char *chroot_f = NULL;
ff8d0ea6 806 pid_t pid = -1;
807 int i;
7690e8eb 808
9e4ad609 809 if (f == NULL || !strcmp(Config.pidFilename, "none")) {
7dbca7a4 810 fprintf(stderr, APP_SHORTNAME ": ERROR: No pid file name defined\n");
62e76326 811 exit(1);
7690e8eb 812 }
62e76326 813
1ee742a2 814 if (Config.chroot_dir && geteuid() == 0) {
815 int len = strlen(Config.chroot_dir) + 1 + strlen(f) + 1;
816 chroot_f = (char *)xmalloc(strlen(Config.chroot_dir) + 1 + strlen(f) + 1);
817 snprintf(chroot_f, len, "%s/%s", Config.chroot_dir, f);
818 f = chroot_f;
819 }
820
7690e8eb 821 pid_fp = fopen(f, "r");
62e76326 822
7690e8eb 823 if (pid_fp != NULL) {
62e76326 824 pid = 0;
825
826 if (fscanf(pid_fp, "%d", &i) == 1)
827 pid = (pid_t) i;
828
829 fclose(pid_fp);
7690e8eb 830 } else {
62e76326 831 if (errno != ENOENT) {
7dbca7a4 832 fprintf(stderr, APP_SHORTNAME ": ERROR: Could not read pid file\n");
62e76326 833 fprintf(stderr, "\t%s: %s\n", f, xstrerror());
834 exit(1);
835 }
7690e8eb 836 }
62e76326 837
1ee742a2 838 safe_free(chroot_f);
7690e8eb 839 return pid;
840}
841
f3f0f563
AJ
842/* A little piece of glue for odd systems */
843#ifndef RLIMIT_NOFILE
844#ifdef RLIMIT_OFILE
845#define RLIMIT_NOFILE RLIMIT_OFILE
846#endif
847#endif
7690e8eb 848
f3f0f563 849/** Figure out the number of supported filedescriptors */
b8d8561b 850void
0673c0ba 851setMaxFD(void)
c4fb974e 852{
f3f0f563 853#if HAVE_SETRLIMIT && defined(RLIMIT_NOFILE)
8527a56f 854
9064ed81
A
855 /* On Linux with 64-bit file support the sys/resource.h header
856 * uses #define to change the function definition to require rlimit64
857 */
8527a56f
AJ
858#if defined(getrlimit)
859 struct rlimit64 rl; // Assume its a 64-bit redefine anyways.
860#else
c4fb974e 861 struct rlimit rl;
8527a56f
AJ
862#endif
863
c4fb974e 864 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
f3f0f563
AJ
865 debugs(50, DBG_CRITICAL, "setrlimit: RLIMIT_NOFILE: " << xstrerror());
866 } else if (Config.max_filedescriptors > 0) {
0979b3a6
AJ
867#if USE_SELECT || USE_SELECT_WIN32
868 /* select() breaks if this gets set too big */
1b7fae06 869 if (Config.max_filedescriptors > FD_SETSIZE) {
0979b3a6 870 rl.rlim_cur = FD_SETSIZE;
1b7fae06
AJ
871 debugs(50, DBG_CRITICAL, "WARNING: 'max_filedescriptors " << Config.max_filedescriptors << "' does not work with select()");
872 } else
0979b3a6
AJ
873#endif
874 rl.rlim_cur = Config.max_filedescriptors;
62e76326 875 if (rl.rlim_cur > rl.rlim_max)
f3f0f563
AJ
876 rl.rlim_max = rl.rlim_cur;
877 if (setrlimit(RLIMIT_NOFILE, &rl)) {
1b7fae06 878 debugs(50, DBG_CRITICAL, "ERROR: setrlimit: RLIMIT_NOFILE: " << xstrerror());
f3f0f563
AJ
879 getrlimit(RLIMIT_NOFILE, &rl);
880 rl.rlim_cur = rl.rlim_max;
881 if (setrlimit(RLIMIT_NOFILE, &rl)) {
1b7fae06 882 debugs(50, DBG_CRITICAL, "ERROR: setrlimit: RLIMIT_NOFILE: " << xstrerror());
f3f0f563 883 }
62e76326 884 }
c4fb974e 885 }
f3f0f563 886 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
1b7fae06 887 debugs(50, DBG_CRITICAL, "ERROR: getrlimit: RLIMIT_NOFILE: " << xstrerror());
c4fb974e 888 } else {
f3f0f563
AJ
889 Squid_MaxFD = rl.rlim_cur;
890 }
62e76326 891
f3f0f563
AJ
892#endif /* HAVE_SETRLIMIT */
893}
62e76326 894
f3f0f563
AJ
895void
896setSystemLimits(void)
897{
be266cb2 898#if HAVE_SETRLIMIT && defined(RLIMIT_NOFILE) && !_SQUID_CYGWIN_
f3f0f563 899 /* limit system filedescriptors to our own limit */
8527a56f
AJ
900
901 /* On Linux with 64-bit file support the sys/resource.h header
902 * uses #define to change the function definition to require rlimit64
903 */
904#if defined(getrlimit)
905 struct rlimit64 rl; // Assume its a 64-bit redefine anyways.
906#else
f3f0f563 907 struct rlimit rl;
8527a56f
AJ
908#endif
909
f3f0f563
AJ
910 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
911 debugs(50, DBG_CRITICAL, "setrlimit: RLIMIT_NOFILE: " << xstrerror());
912 } else {
913 rl.rlim_cur = Squid_MaxFD;
914 if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
915 snprintf(tmp_error_buf, ERROR_BUF_SZ, "setrlimit: RLIMIT_NOFILE: %s", xstrerror());
62e76326 916 fatal_dump(tmp_error_buf);
917 }
c4fb974e 918 }
30a4f2a8 919#endif /* HAVE_SETRLIMIT */
920
921#if HAVE_SETRLIMIT && defined(RLIMIT_DATA)
922 if (getrlimit(RLIMIT_DATA, &rl) < 0) {
f3f0f563 923 debugs(50, DBG_CRITICAL, "getrlimit: RLIMIT_DATA: " << xstrerror());
88738790 924 } else if (rl.rlim_max > rl.rlim_cur) {
62e76326 925 rl.rlim_cur = rl.rlim_max; /* set it to the max */
926
927 if (setrlimit(RLIMIT_DATA, &rl) < 0) {
f3f0f563 928 snprintf(tmp_error_buf, ERROR_BUF_SZ, "setrlimit: RLIMIT_DATA: %s", xstrerror());
62e76326 929 fatal_dump(tmp_error_buf);
930 }
30a4f2a8 931 }
932#endif /* RLIMIT_DATA */
f3f0f563
AJ
933 if (Config.max_filedescriptors > Squid_MaxFD) {
934 debugs(50, DBG_IMPORTANT, "NOTICE: Could not increase the number of filedescriptors");
935 }
936
ad7ef91a 937#if HAVE_SETRLIMIT && defined(RLIMIT_VMEM)
938 if (getrlimit(RLIMIT_VMEM, &rl) < 0) {
30c48b1a 939 debugs(50, DBG_CRITICAL, "getrlimit: RLIMIT_VMEM: " << xstrerror());
88738790 940 } else if (rl.rlim_max > rl.rlim_cur) {
62e76326 941 rl.rlim_cur = rl.rlim_max; /* set it to the max */
942
943 if (setrlimit(RLIMIT_VMEM, &rl) < 0) {
f3f0f563 944 snprintf(tmp_error_buf, ERROR_BUF_SZ, "setrlimit: RLIMIT_VMEM: %s", xstrerror());
62e76326 945 fatal_dump(tmp_error_buf);
946 }
ad7ef91a 947 }
948#endif /* RLIMIT_VMEM */
c4fb974e 949}
3a57089e 950
b8d8561b 951void
ea3a2a69 952squid_signal(int sig, SIGHDLR * func, int flags)
30a4f2a8 953{
954#if HAVE_SIGACTION
62e76326 955
30a4f2a8 956 struct sigaction sa;
957 sa.sa_handler = func;
958 sa.sa_flags = flags;
959 sigemptyset(&sa.sa_mask);
62e76326 960
30a4f2a8 961 if (sigaction(sig, &sa, NULL) < 0)
30c48b1a 962 debugs(50, DBG_CRITICAL, "sigaction: sig=" << sig << " func=" << func << ": " << xstrerror());
62e76326 963
30a4f2a8 964#else
7aa9bb3e 965#if _SQUID_WINDOWS_
06648839 966 /*
967 On Windows, only SIGINT, SIGILL, SIGFPE, SIGTERM, SIGBREAK, SIGABRT and SIGSEGV signals
968 are supported, so we must care of don't call signal() for other value.
969 The SIGILL, SIGSEGV, and SIGTERM signals are not generated under Windows. They are defined
970 for ANSI compatibility, so both SIGSEGV and SIGBUS are emulated with an Exception Handler.
971 */
972 switch (sig) {
973
974 case SIGINT:
975
976 case SIGILL:
977
978 case SIGFPE:
979
980 case SIGTERM:
981
982 case SIGBREAK:
983
984 case SIGABRT:
985 break;
986
987 case SIGSEGV:
988 WIN32_ExceptionHandlerInit();
989 break;
990
991 case SIGBUS:
992 WIN32_ExceptionHandlerInit();
993 return;
994 break; /* Nor reached */
995
996 default:
997 return;
998 break; /* Nor reached */
999 }
1000
1001#endif
62e76326 1002
06648839 1003 signal(sig, func);
62e76326 1004
30a4f2a8 1005#endif
1006}
396c5745 1007
70364f29 1008void
1009logsFlush(void)
1010{
1011 if (debug_log)
62e76326 1012 fflush(debug_log);
70364f29 1013}
88738790 1014
16300b58 1015void
0e473d70 1016kb_incr(kb_t * k, size_t v)
a7c05555 1017{
0e473d70 1018 k->bytes += v;
1019 k->kb += (k->bytes >> 10);
1020 k->bytes &= 0x3FF;
a7c05555 1021}
c1dabf04 1022
a00a7c85 1023void
6bbf9cc4 1024debugObj(int section, int level, const char *label, void *obj, ObjPackMethod pm)
a00a7c85 1025{
1026 MemBuf mb;
1027 Packer p;
6bbf9cc4 1028 assert(label && obj && pm);
2fe7eff9 1029 mb.init();
a00a7c85 1030 packerToMemInit(&p, &mb);
0cdcddb9 1031 (*pm) (obj, &p);
bf8fe701 1032 debugs(section, level, "" << label << "" << mb.buf << "");
a00a7c85 1033 packerClean(&p);
2fe7eff9 1034 mb.clean();
a00a7c85 1035}
d548ee64 1036
0e70aa1e 1037void
1038parseEtcHosts(void)
1039{
1040 FILE *fp;
1041 char buf[1024];
1042 char buf2[512];
1043 char *nt = buf;
1044 char *lt = buf;
fa2bfbab 1045
0e70aa1e 1046 if (NULL == Config.etcHostsPath)
62e76326 1047 return;
1048
0e70aa1e 1049 if (0 == strcmp(Config.etcHostsPath, "none"))
62e76326 1050 return;
1051
0e70aa1e 1052 fp = fopen(Config.etcHostsPath, "r");
62e76326 1053
0e70aa1e 1054 if (fp == NULL) {
e0236918 1055 debugs(1, DBG_IMPORTANT, "parseEtcHosts: " << Config.etcHostsPath << ": " << xstrerror());
62e76326 1056 return;
0e70aa1e 1057 }
62e76326 1058
be266cb2 1059#if _SQUID_WINDOWS_
c4aefe96 1060 setmode(fileno(fp), O_TEXT);
1061#endif
62e76326 1062
0e70aa1e 1063 while (fgets(buf, 1024, fp)) { /* for each line */
62e76326 1064 wordlist *hosts = NULL;
1065 char *addr;
1066
c31616ae 1067 if (buf[0] == '#') /* MS-windows likes to add comments */
1068 continue;
1069
7a10c315 1070 strtok(buf, "#"); /* chop everything following a comment marker */
62e76326 1071
1072 lt = buf;
1073
1074 addr = buf;
1075
bf8fe701 1076 debugs(1, 5, "etc_hosts: line is '" << buf << "'");
62e76326 1077
1078 nt = strpbrk(lt, w_space);
1079
1080 if (nt == NULL) /* empty line */
1081 continue;
1082
1083 *nt = '\0'; /* null-terminate the address */
1084
bf8fe701 1085 debugs(1, 5, "etc_hosts: address is '" << addr << "'");
62e76326 1086
1087 lt = nt + 1;
1088
1089 while ((nt = strpbrk(lt, w_space))) {
1090 char *host = NULL;
1091
1092 if (nt == lt) { /* multiple spaces */
bf8fe701 1093 debugs(1, 5, "etc_hosts: multiple spaces, skipping");
62e76326 1094 lt = nt + 1;
1095 continue;
1096 }
1097
1098 *nt = '\0';
bf8fe701 1099 debugs(1, 5, "etc_hosts: got hostname '" << lt << "'");
62e76326 1100
532e5dd4
AJ
1101 /* For IPV6 addresses also check for a colon */
1102 if (Config.appendDomain && !strchr(lt, '.') && !strchr(lt, ':')) {
62e76326 1103 /* I know it's ugly, but it's only at reconfig */
0a84e4fb
AJ
1104 strncpy(buf2, lt, sizeof(buf2)-1);
1105 strncat(buf2, Config.appendDomain, sizeof(buf2) - strlen(lt) - 1);
50e746f3 1106 buf2[sizeof(buf2)-1] = '\0';
62e76326 1107 host = buf2;
1108 } else {
1109 host = lt;
1110 }
1111
f6a72b33
AJ
1112 if (ipcacheAddEntryFromHosts(host, addr) != 0) {
1113 /* invalid address, continuing is useless */
1114 wordlistDestroy(&hosts);
1115 hosts = NULL;
1116 break;
1117 }
62e76326 1118 wordlistAdd(&hosts, host);
1119
1120 lt = nt + 1;
1121 }
1122
9e008dda 1123 if (hosts) {
f6a72b33
AJ
1124 fqdncacheAddEntryFromHosts(addr, hosts);
1125 wordlistDestroy(&hosts);
1126 }
0e70aa1e 1127 }
62e76326 1128
1f3c7397 1129 fclose (fp);
0e70aa1e 1130}
52f772de 1131
1132int
1133getMyPort(void)
1134{
fa720bfb
AJ
1135 AnyP::PortCfgPointer p;
1136 if ((p = HttpPortList) != NULL) {
c4ac4197 1137 // skip any special interception ports
fa720bfb 1138 while (p != NULL && p->flags.isIntercepted())
af5ebaf4 1139 p = p->next;
fa720bfb 1140 if (p != NULL)
4dd643d5 1141 return p->s.port();
af5ebaf4 1142 }
62e76326 1143
cb4f4424 1144#if USE_OPENSSL
fa720bfb 1145 if ((p = HttpsPortList) != NULL) {
c4ac4197 1146 // skip any special interception ports
fa720bfb 1147 while (p != NULL && p->flags.isIntercepted())
c4ac4197 1148 p = p->next;
fa720bfb 1149 if (p != NULL)
4dd643d5 1150 return p->s.port();
c4ac4197 1151 }
52f772de 1152#endif
62e76326 1153
8ea0d847 1154 if ((p = FtpPortList) != NULL) {
547f3031 1155 // skip any special interception ports
8ea0d847 1156 while (p != NULL && p->flags.isIntercepted())
547f3031 1157 p = p->next;
8ea0d847 1158 if (p != NULL)
547f3031
AR
1159 return p->s.port();
1160 }
1161
af5ebaf4
AJ
1162 debugs(21, DBG_CRITICAL, "ERROR: No forward-proxy ports configured.");
1163 return 0; // Invalid port. This will result in invalid URLs on bad configurations.
52f772de 1164}
d860a1aa 1165
c642c141
AJ
1166/*
1167 * Set the umask to at least the given mask. This is in addition
1168 * to the umask set at startup
1169 */
1170void
1171setUmask(mode_t mask)
1172{
1173 // No way to get the current umask value without setting it.
1174 static const mode_t orig_umask = umask(mask); // once, to get
1175 umask(mask | orig_umask); // always, to set
1176}
d860a1aa 1177
1178/*
1179 * Inverse of strwordtok. Quotes a word if needed
1180 */
42419a95 1181void
d860a1aa 1182strwordquote(MemBuf * mb, const char *str)
1183{
1184 int quoted = 0;
62e76326 1185
d860a1aa 1186 if (strchr(str, ' ')) {
62e76326 1187 quoted = 1;
2fe7eff9 1188 mb->append("\"", 1);
d860a1aa 1189 }
62e76326 1190
d860a1aa 1191 while (*str) {
dc1af3cf 1192 int l = strcspn(str, "\"\\\n\r");
2fe7eff9 1193 mb->append(str, l);
62e76326 1194 str += l;
1195
26ac0430 1196 switch (*str) {
dc1af3cf 1197
1198 case '\n':
2fe7eff9 1199 mb->append("\\n", 2);
5db6bf73 1200 ++str;
dc1af3cf 1201 break;
1202
1203 case '\r':
2fe7eff9 1204 mb->append("\\r", 2);
5db6bf73 1205 ++str;
dc1af3cf 1206 break;
1207
1208 case '\0':
1209 break;
1210
1211 default:
2fe7eff9 1212 mb->append("\\", 1);
1213 mb->append(str, 1);
5db6bf73 1214 ++str;
dc1af3cf 1215 break;
62e76326 1216 }
d860a1aa 1217 }
62e76326 1218
d860a1aa 1219 if (quoted)
2fe7eff9 1220 mb->append("\"", 1);
d860a1aa 1221}
fc68f6b1 1222
1223void
1224keepCapabilities(void)
1225{
b7ac5457 1226#if USE_LIBCAP && HAVE_PRCTL && defined(PR_SET_KEEPCAPS)
329ce686 1227
1228 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
b7ac5457 1229 Ip::Interceptor.StopTransparency("capability setting has failed.");
fc68f6b1 1230 }
fc68f6b1 1231#endif
1232}
329ce686 1233
1234static void
1235restoreCapabilities(int keep)
1236{
9e008dda 1237 /* NP: keep these two if-endif separate. Non-Linux work perfectly well without Linux syscap support. */
b64b6030 1238#if USE_LIBCAP
c3487a77
HN
1239 cap_t caps;
1240 if (keep)
1241 caps = cap_get_proc();
1242 else
1243 caps = cap_init();
1244 if (!caps) {
b7ac5457 1245 Ip::Interceptor.StopTransparency("Can't get current capabilities");
9e008dda 1246 } else {
c3487a77
HN
1247 int ncaps = 0;
1248 int rc = 0;
1249 cap_value_t cap_list[10];
5db6bf73
FC
1250 cap_list[ncaps] = CAP_NET_BIND_SERVICE;
1251 ++ncaps;
425de4c8 1252 if (Ip::Interceptor.TransparentActive() || Ip::Qos::TheConfig.isHitNfmarkActive() || Ip::Qos::TheConfig.isAclNfmarkActive()) {
5db6bf73
FC
1253 cap_list[ncaps] = CAP_NET_ADMIN;
1254 ++ncaps;
fc32ce2e 1255 }
329ce686 1256
c3487a77
HN
1257 cap_clear_flag(caps, CAP_EFFECTIVE);
1258 rc |= cap_set_flag(caps, CAP_EFFECTIVE, ncaps, cap_list, CAP_SET);
1259 rc |= cap_set_flag(caps, CAP_PERMITTED, ncaps, cap_list, CAP_SET);
329ce686 1260
c3487a77 1261 if (rc || cap_set_proc(caps) != 0) {
b7ac5457 1262 Ip::Interceptor.StopTransparency("Error enabling needed capabilities.");
b5bd96ce 1263 }
c3487a77 1264 cap_free(caps);
329ce686 1265 }
8a09e810 1266#elif _SQUID_LINUX_
b7ac5457 1267 Ip::Interceptor.StopTransparency("Missing needed capability support.");
b5bd96ce 1268#endif /* HAVE_SYS_CAPABILITY_H */
329ce686 1269}