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