]> git.ipfire.org Git - thirdparty/squid.git/blame - src/tools.cc
BUGFIX: max_user_ip was broken: initialising to -1 meant that the ACL appeared
[thirdparty/squid.git] / src / tools.cc
CommitLineData
94e891ea 1
30a4f2a8 2/*
d295d770 3 * $Id: tools.cc,v 1.266 2006/04/23 11:10:32 robertc 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"
c8f4eac4 37#include "SwapDir.h"
528b2c61 38#include "fde.h"
0eb49b6d 39#include "MemBuf.h"
d295d770 40#include "wordlist.h"
44a47c6e 41
62ae0622 42#if HAVE_SYS_PRCTL_H
43#include <sys/prctl.h>
44#endif
45
090089c4 46#define DEAD_MSG "\
c5c666ab 47The Squid Cache (version %s) died.\n\
090089c4 48\n\
c5c666ab 49You've encountered a fatal error in the Squid Cache version %s.\n\
090089c4 50If a core file was created (possibly in the swap directory),\n\
b8de7ebe 51please execute 'gdb squid core' or 'dbx squid core', then type 'where',\n\
784219b8 52and report the trace back to squid-bugs@squid-cache.org.\n\
090089c4 53\n\
54Thanks!\n"
55
f5b8bbc4 56static void fatal_common(const char *);
137ee196 57static void fatalvf(const char *fmt, va_list args);
f5b8bbc4 58static void mail_warranty(void);
63986a85 59#if MEM_GEN_TRACE
399e85ea 60extern void log_trace_done();
63986a85 61extern void log_trace_init(char *);
399e85ea 62#endif
24382924 63
941a3077 64#ifdef _SQUID_LINUX_
65/* Workaround for crappy glic header files */
e6ccf245 66SQUIDCEXTERN int backtrace(void *, int);
67SQUIDCEXTERN void backtrace_symbols_fd(void *, int, int);
68SQUIDCEXTERN int setresuid(uid_t, uid_t, uid_t);
941a3077 69#endif /* _SQUID_LINUX */
70
8fd63c35 71SQUIDCEXTERN void (*failure_notify) (const char *);
7e3ce7b9 72
b001e822 73MemAllocator *dlink_node_pool = NULL;
94439e4e 74
4a5a6c62 75void
429fdbec 76releaseServerSockets(void)
77{
812ed90c 78 int i;
429fdbec 79 /* Release the main ports as early as possible */
62e76326 80
812ed90c 81 for (i = 0; i < NHttpSockets; i++) {
62e76326 82 if (HttpSockets[i] >= 0)
83 close(HttpSockets[i]);
812ed90c 84 }
62e76326 85
429fdbec 86 if (theInIcpConnection >= 0)
62e76326 87 close(theInIcpConnection);
88
429fdbec 89 if (theOutIcpConnection >= 0 && theOutIcpConnection != theInIcpConnection)
62e76326 90 close(theOutIcpConnection);
429fdbec 91}
92
b8d8561b 93static char *
0673c0ba 94dead_msg(void)
090089c4 95{
95d659f0 96 LOCAL_ARRAY(char, msg, 1024);
56878878 97 snprintf(msg, 1024, DEAD_MSG, version_string, version_string);
090089c4 98 return msg;
99}
100
24382924 101static void
0673c0ba 102mail_warranty(void)
090089c4 103{
019dd986 104 FILE *fp = NULL;
6e40f263 105 static char command[256];
b99a6dec 106#if HAVE_MKSTEMP
62e76326 107
b99a6dec 108 char filename[] = "/tmp/squid-XXXXXX";
109 int tfd = mkstemp(filename);
62e76326 110
b99a6dec 111 if (tfd < 0)
62e76326 112 return;
113
b99a6dec 114 if ((fp = fdopen(tfd, "w")) == NULL)
62e76326 115 return;
116
b99a6dec 117#else
62e76326 118
b99a6dec 119 char *filename;
62e76326 120
6e40f263 121 if ((filename = tempnam(NULL, appname)) == NULL)
62e76326 122 return;
123
6e40f263 124 if ((fp = fopen(filename, "w")) == NULL)
62e76326 125 return;
126
b99a6dec 127#endif
62e76326 128
abacf776 129 if (Config.EmailFrom)
130 fprintf(fp, "From: %s\n", Config.EmailFrom);
131 else
132 fprintf(fp, "From: %s@%s\n", appname, uniqueHostname());
62e76326 133
edae7bc0 134 fprintf(fp, "To: %s\n", Config.adminEmail);
62e76326 135
6e40f263 136 fprintf(fp, "Subject: %s\n", dead_msg());
62e76326 137
6e40f263 138 fclose(fp);
62e76326 139
d084bf20 140 snprintf(command, 256, "%s %s < %s", Config.EmailProgram, Config.adminEmail, filename);
62e76326 141
6e40f263 142 system(command); /* XXX should avoid system(3) */
62e76326 143
6e40f263 144 unlink(filename);
090089c4 145}
146
4a5a6c62 147void
43c3424b 148dumpMallocStats(void)
3f43b19b 149{
88738790 150#if HAVE_MSTATS && HAVE_GNUMALLOC_H
62e76326 151
88738790 152 struct mstats ms = mstats();
153 fprintf(debug_log, "\ttotal space in arena: %6d KB\n",
62e76326 154 (int) (ms.bytes_total >> 10));
88738790 155 fprintf(debug_log, "\tTotal free: %6d KB %d%%\n",
62e76326 156 (int) (ms.bytes_free >> 10),
157 percent(ms.bytes_free, ms.bytes_total));
eb824054 158#elif HAVE_MALLINFO && HAVE_STRUCT_MALLINFO
62e76326 159
3f43b19b 160 struct mallinfo mp;
30a4f2a8 161 int t;
62e76326 162
3f43b19b 163 if (!do_mallinfo)
62e76326 164 return;
165
3f43b19b 166 mp = mallinfo();
62e76326 167
9e4ad609 168 fprintf(debug_log, "Memory usage for %s via mallinfo():\n", appname);
62e76326 169
e4049756 170 fprintf(debug_log, "\ttotal space in arena: %6ld KB\n",
171 (long)mp.arena >> 10);
62e76326 172
e4049756 173 fprintf(debug_log, "\tOrdinary blocks: %6ld KB %6ld blks\n",
174 (long)mp.uordblks >> 10, (long)mp.ordblks);
62e76326 175
e4049756 176 fprintf(debug_log, "\tSmall blocks: %6ld KB %6ld blks\n",
177 (long)mp.usmblks >> 10, (long)mp.smblks);
62e76326 178
e4049756 179 fprintf(debug_log, "\tHolding blocks: %6ld KB %6ld blks\n",
180 (long)mp.hblkhd >> 10, (long)mp.hblks);
62e76326 181
e4049756 182 fprintf(debug_log, "\tFree Small blocks: %6ld KB\n",
183 (long)mp.fsmblks >> 10);
62e76326 184
e4049756 185 fprintf(debug_log, "\tFree Ordinary blocks: %6ld KB\n",
186 (long)mp.fordblks >> 10);
62e76326 187
30a4f2a8 188 t = mp.uordblks + mp.usmblks + mp.hblkhd;
62e76326 189
9e4ad609 190 fprintf(debug_log, "\tTotal in use: %6d KB %d%%\n",
62e76326 191 t >> 10, percent(t, mp.arena));
192
30a4f2a8 193 t = mp.fsmblks + mp.fordblks;
62e76326 194
9e4ad609 195 fprintf(debug_log, "\tTotal free: %6d KB %d%%\n",
62e76326 196 t >> 10, percent(t, mp.arena));
197
6a9f6389 198#if HAVE_STRUCT_MALLINFO_MXFAST
62e76326 199
9e4ad609 200 fprintf(debug_log, "\tmax size of small blocks:\t%d\n",
62e76326 201 mp.mxfast);
202
9e4ad609 203 fprintf(debug_log, "\tnumber of small blocks in a holding block:\t%d\n",
62e76326 204 mp.nlblks);
205
9e4ad609 206 fprintf(debug_log, "\tsmall block rounding factor:\t%d\n",
62e76326 207 mp.grain);
208
9e4ad609 209 fprintf(debug_log, "\tspace (including overhead) allocated in ord. blks:\t%d\n",
62e76326 210 mp.uordbytes);
211
9e4ad609 212 fprintf(debug_log, "\tnumber of ordinary blocks allocated:\t%d\n",
62e76326 213 mp.allocated);
214
9e4ad609 215 fprintf(debug_log, "\tbytes used in maintaining the free tree:\t%d\n",
62e76326 216 mp.treeoverhead);
217
6a9f6389 218#endif /* HAVE_STRUCT_MALLINFO_MXFAST */
3f43b19b 219#endif /* HAVE_MALLINFO */
220}
30a4f2a8 221
f2908497 222void
62e76326 223
f2908497 224squid_getrusage(struct rusage *r)
f0f81709 225{
62e76326 226
8da94b66 227 memset(r, '\0', sizeof(struct rusage));
30a4f2a8 228#if HAVE_GETRUSAGE && defined(RUSAGE_SELF)
cd19f0b6 229#ifdef _SQUID_SOLARIS_
230 /* Solaris 2.5 has getrusage() permission bug -- Arjan de Vet */
231 enter_suid();
232#endif
62e76326 233
f2908497 234 getrusage(RUSAGE_SELF, r);
cd19f0b6 235#ifdef _SQUID_SOLARIS_
62e76326 236
cd19f0b6 237 leave_suid();
238#endif
56983a01 239#endif
f2908497 240}
241
242double
62e76326 243
f2908497 244rusage_cputime(struct rusage *r)
245{
246 return (double) r->ru_stime.tv_sec +
62e76326 247 (double) r->ru_utime.tv_sec +
248 (double) r->ru_stime.tv_usec / 1000000.0 +
249 (double) r->ru_utime.tv_usec / 1000000.0;
f2908497 250}
251
ec603b25 252/* Hack for some HP-UX preprocessors */
253#ifndef HAVE_GETPAGESIZE
254#define HAVE_GETPAGESIZE 0
255#endif
256
f2908497 257int
62e76326 258
f2908497 259rusage_maxrss(struct rusage *r)
260{
4fdc3986 261#if defined(_SQUID_SGI_) && _ABIAPI
262 return r->ru_pad[0];
263#elif defined(_SQUID_SGI_)
62e76326 264
f2908497 265 return r->ru_maxrss;
266#elif defined(_SQUID_OSF_)
62e76326 267
a6e0ad1e 268 return r->ru_maxrss;
269#elif defined(_SQUID_AIX_)
270
f2908497 271 return r->ru_maxrss;
272#elif defined(BSD4_4)
62e76326 273
f2908497 274 return r->ru_maxrss;
6b8e7481 275#elif defined(HAVE_GETPAGESIZE) && HAVE_GETPAGESIZE != 0
62e76326 276
f2908497 277 return (r->ru_maxrss * getpagesize()) >> 10;
c7a1083d 278#elif defined(PAGESIZE)
62e76326 279
c7a1083d 280 return (r->ru_maxrss * PAGESIZE) >> 10;
8505e57b 281#else
62e76326 282
8505e57b 283 return r->ru_maxrss;
f2908497 284#endif
285}
286
287int
62e76326 288
f2908497 289rusage_pagefaults(struct rusage *r)
290{
4fdc3986 291#if defined(_SQUID_SGI_) && _ABIAPI
292 return r->ru_pad[5];
293#else
62e76326 294
f2908497 295 return r->ru_majflt;
4fdc3986 296#endif
f2908497 297}
298
299
4a5a6c62 300void
f2908497 301PrintRusage(void)
302{
62e76326 303
f2908497 304 struct rusage rusage;
305 squid_getrusage(&rusage);
2b906e48 306 fprintf(debug_log, "CPU Usage: %.3f seconds = %.3f user + %.3f sys\n",
62e76326 307 rusage_cputime(&rusage),
308 rusage.ru_utime.tv_sec + ((double) rusage.ru_utime.tv_usec / 1000000.0),
309 rusage.ru_stime.tv_sec + ((double) rusage.ru_stime.tv_usec / 1000000.0));
f2908497 310 fprintf(debug_log, "Maximum Resident Size: %d KB\n",
62e76326 311 rusage_maxrss(&rusage));
f2908497 312 fprintf(debug_log, "Page faults with physical i/o: %d\n",
62e76326 313 rusage_pagefaults(&rusage));
f0f81709 314}
315
03d7b07f 316
b8d8561b 317void
318death(int sig)
44a47c6e 319{
0e08ce4b 320 if (sig == SIGSEGV)
62e76326 321 fprintf(debug_log, "FATAL: Received Segment Violation...dying.\n");
0e08ce4b 322 else if (sig == SIGBUS)
62e76326 323 fprintf(debug_log, "FATAL: Received Bus Error...dying.\n");
0e08ce4b 324 else
62e76326 325 fprintf(debug_log, "FATAL: Received signal %d...dying.\n", sig);
0b4639af 326
327#ifdef PRINT_STACK_TRACE
328#ifdef _SQUID_HPUX_
329 {
62e76326 330 extern void U_STACK_TRACE(void); /* link with -lcl */
331 fflush(debug_log);
332 dup2(fileno(debug_log), 2);
333 U_STACK_TRACE();
0b4639af 334 }
62e76326 335
0b4639af 336#endif /* _SQUID_HPUX_ */
337#ifdef _SQUID_SOLARIS_
4f07153c 338 { /* get ftp://opcom.sun.ca/pub/tars/opcom_stack.tar.gz and */
62e76326 339 extern void opcom_stack_trace(void); /* link with -lopcom_stack */
340 fflush(debug_log);
341 dup2(fileno(debug_log), fileno(stdout));
342 opcom_stack_trace();
343 fflush(stdout);
0b4639af 344 }
62e76326 345
0b4639af 346#endif /* _SQUID_SOLARIS_ */
ce3d30fb 347#if HAVE_BACKTRACE_SYMBOLS_FD
348 {
62e76326 349 static void *(callarray[8192]);
350 int n;
351 n = backtrace(callarray, 8192);
352 backtrace_symbols_fd(callarray, n, fileno(debug_log));
ce3d30fb 353 }
62e76326 354
ce3d30fb 355#endif
0b4639af 356#endif /* PRINT_STACK_TRACE */
357
06648839 358#if SA_RESETHAND == 0 && !defined(_SQUID_MSWIN_)
44a47c6e 359 signal(SIGSEGV, SIG_DFL);
62e76326 360
44a47c6e 361 signal(SIGBUS, SIG_DFL);
62e76326 362
0e08ce4b 363 signal(sig, SIG_DFL);
62e76326 364
30a4f2a8 365#endif
62e76326 366
429fdbec 367 releaseServerSockets();
62e76326 368
e3ef2b09 369 storeDirWriteCleanLogs(0);
62e76326 370
4b0f5de8 371 if (!shutting_down) {
372 PrintRusage();
62e76326 373
4b0f5de8 374 dumpMallocStats();
375 }
62e76326 376
6e40f263 377 if (squid_curtime - SQUID_RELEASE_TIME < 864000) {
62e76326 378 /* skip if more than 10 days old */
379
380 if (Config.adminEmail)
381 mail_warranty();
382 else
383 puts(dead_msg());
6e40f263 384 }
62e76326 385
44a47c6e 386 abort();
387}
388
389
b8d8561b 390void
23d92c64 391sigusr2_handle(int sig)
090089c4 392{
a50bfe93 393#if defined(_SQUID_MSWIN_) && defined(_DEBUG)
1fa994ac 394 do_debug_trap = 1;
a50bfe93 395#endif
396#if (defined(_SQUID_MSWIN_) && !defined(_DEBUG)) || !defined(_SQUID_MSWIN_)
397
30a4f2a8 398 static int state = 0;
545f551a 399 /* no debug() here; bad things happen if the signal is delivered during _db_print() */
62e76326 400
30a4f2a8 401 if (state == 0) {
2bf05976 402#ifndef MEM_GEN_TRACE
d7298d24 403 Debug::parseOptions("ALL,7");
2bf05976 404#else
62e76326 405
406 log_trace_done();
63986a85 407#endif
62e76326 408
409 state = 1;
30a4f2a8 410 } else {
2bf05976 411#ifndef MEM_GEN_TRACE
3dc275d6 412 Debug::parseOptions(Config.debugOptions);
2bf05976 413#else
62e76326 414
415 log_trace_init("/tmp/squid.alloc");
63986a85 416#endif
62e76326 417
418 state = 0;
30a4f2a8 419 }
62e76326 420
a50bfe93 421#endif
30a4f2a8 422#if !HAVE_SIGACTION
e504b0a7 423 if (signal(sig, sigusr2_handle) == SIG_ERR) /* reinstall */
424 debug(50, 0) ("signal: sig=%d func=%p: %s\n", sig, sigusr2_handle, xstrerror());
62e76326 425
090089c4 426#endif
427}
428
24382924 429static void
0ee4272b 430fatal_common(const char *message)
090089c4 431{
db40ae20 432#if HAVE_SYSLOG
6b8e7481 433 syslog(LOG_ALERT, "%s", message);
db40ae20 434#endif
62e76326 435
9bc73deb 436 fprintf(debug_log, "FATAL: %s\n", message);
62e76326 437
9bc73deb 438 if (opt_debug_stderr > 0 && debug_log != stderr)
62e76326 439 fprintf(stderr, "FATAL: %s\n", message);
440
c5c666ab 441 fprintf(debug_log, "Squid Cache (Version %s): Terminated abnormally.\n",
62e76326 442 version_string);
443
2c47cf74 444 fflush(debug_log);
62e76326 445
9e4ad609 446 PrintRusage();
62e76326 447
9e4ad609 448 dumpMallocStats();
090089c4 449}
450
451/* fatal */
b8d8561b 452void
0ee4272b 453fatal(const char *message)
090089c4 454{
429fdbec 455 releaseServerSockets();
b2c141d4 456 /* check for store_dirs_rebuilding because fatal() is often
d399be0e 457 * used in early initialization phases, long before we ever
458 * get to the store log. */
62e76326 459
b2c141d4 460 if (0 == store_dirs_rebuilding)
62e76326 461 storeDirWriteCleanLogs(0);
462
090089c4 463 fatal_common(message);
62e76326 464
edd3d24f 465 if (shutting_down)
4b0f5de8 466 exit(1);
edd3d24f 467 else
62e76326 468 abort();
090089c4 469}
470
137ee196 471/* printf-style interface for fatal */
6de2df60 472#if STDC_HEADERS
137ee196 473void
474fatalf(const char *fmt,...)
475{
476 va_list args;
477 va_start(args, fmt);
478#else
479void
480fatalf(va_alist)
62e76326 481va_dcl
137ee196 482{
483 va_list args;
484 const char *fmt = NULL;
485 va_start(args);
486 fmt = va_arg(args, char *);
487#endif
62e76326 488
137ee196 489 fatalvf(fmt, args);
490 va_end(args);
491}
492
493
494/* used by fatalf */
495static void
62e76326 496fatalvf(const char *fmt, va_list args) {
137ee196 497 static char fatal_str[BUFSIZ];
498 vsnprintf(fatal_str, sizeof(fatal_str), fmt, args);
499 fatal(fatal_str);
500}
501
090089c4 502/* fatal with dumping core */
b8d8561b 503void
62e76326 504fatal_dump(const char *message) {
7e3ce7b9 505 failure_notify = NULL;
429fdbec 506 releaseServerSockets();
62e76326 507
090089c4 508 if (message)
62e76326 509 fatal_common(message);
510
4a69c163 511 if (opt_catch_signals)
62e76326 512 storeDirWriteCleanLogs(0);
513
090089c4 514 abort();
515}
516
b8d8561b 517void
62e76326 518debug_trap(const char *message) {
4a69c163 519 if (!opt_catch_signals)
62e76326 520 fatal_dump(message);
521
a3d5953d 522 _db_print("WARNING: %s\n", message);
85b701ed 523}
524
b8d8561b 525void
62e76326 526sig_child(int sig) {
a50bfe93 527#ifndef _SQUID_MSWIN_
30a4f2a8 528#ifdef _SQUID_NEXT_
529 union wait status;
530#else
62e76326 531
090089c4 532 int status;
090089c4 533#endif
62e76326 534
ff8d0ea6 535 pid_t pid;
090089c4 536
30a4f2a8 537 do {
538#ifdef _SQUID_NEXT_
62e76326 539 pid = wait3(&status, WNOHANG, NULL);
090089c4 540#else
62e76326 541
542 pid = waitpid(-1, &status, WNOHANG);
090089c4 543#endif
62e76326 544 /* no debug() here; bad things happen if the signal is delivered during _db_print() */
30a4f2a8 545#if HAVE_SIGACTION
62e76326 546
30a4f2a8 547 } while (pid > 0);
62e76326 548
234967c9 549#else
62e76326 550
551 }
552
553 while (pid > 0 || (pid < 0 && errno == EINTR))
554
555 ;
30a4f2a8 556 signal(sig, sig_child);
62e76326 557
a50bfe93 558#endif
234967c9 559#endif
30a4f2a8 560}
44a47c6e 561
0ee4272b 562const char *
0673c0ba 563getMyHostname(void)
44a47c6e 564{
95d659f0 565 LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN + 1);
44a47c6e 566 static int present = 0;
62e76326 567
0ee4272b 568 const struct hostent *h = NULL;
62e76326 569
ddfcbc22 570 struct IN_ADDR sa;
62e76326 571
af85f811 572 if (Config.visibleHostname != NULL)
62e76326 573 return Config.visibleHostname;
574
d20b1cd0 575 if (present)
62e76326 576 return host;
577
d20b1cd0 578 host[0] = '\0';
62e76326 579
aa2b5399 580 memcpy(&sa, &any_addr, sizeof(sa));
62e76326 581
aa2b5399 582 if (Config.Sockaddr.http && sa.s_addr == any_addr.s_addr)
62e76326 583 memcpy(&sa, &Config.Sockaddr.http->s.sin_addr, sizeof(sa));
584
aa2b5399 585#if USE_SSL
62e76326 586
aa2b5399 587 if (Config.Sockaddr.https && sa.s_addr == any_addr.s_addr)
62e76326 588 memcpy(&sa, &Config.Sockaddr.https->http.s.sin_addr, sizeof(sa));
589
aa2b5399 590#endif
591 /*
592 * If the first http_port address has a specific address, try a
593 * reverse DNS lookup on it.
594 */
595 if (sa.s_addr != any_addr.s_addr) {
62e76326 596 h = gethostbyaddr((char *) &sa,
597 sizeof(sa), AF_INET);
598
599 if (h != NULL) {
600 /* DNS lookup successful */
601 /* use the official name from DNS lookup */
602 xstrncpy(host, h->h_name, SQUIDHOSTNAMELEN);
603 debug(50, 4) ("getMyHostname: resolved %s to '%s'\n",
604 inet_ntoa(sa),
605 host);
606 present = 1;
607
608 if (strchr(host, '.'))
609 return host;
610
611 }
612
613 debug(50, 1) ("WARNING: failed to resolve %s to a fully qualified hostname\n",
614 inet_ntoa(sa));
d20b1cd0 615 }
62e76326 616
d20b1cd0 617 /*
618 * Get the host name and store it in host to return
619 */
620 if (gethostname(host, SQUIDHOSTNAMELEN) < 0) {
62e76326 621 debug(50, 1) ("WARNING: gethostname failed: %s\n", xstrerror());
d20b1cd0 622 } else if ((h = gethostbyname(host)) == NULL) {
62e76326 623 debug(50, 1) ("WARNING: gethostbyname failed for %s\n", host);
7e3ce7b9 624 } else {
62e76326 625 debug(50, 6) ("getMyHostname: '%s' resolved into '%s'\n",
626 host, h->h_name);
627 /* DNS lookup successful */
628 /* use the official name from DNS lookup */
629 xstrncpy(host, h->h_name, SQUIDHOSTNAMELEN);
630 present = 1;
631
632 if (strchr(host, '.'))
633 return host;
44a47c6e 634 }
62e76326 635
a572d8be 636 if (opt_send_signal == -1)
637 fatal("Could not determine fully qualified hostname. Please set 'visible_hostname'\n");
638 else
639 return ("localhost");
640
d20b1cd0 641 return NULL; /* keep compiler happy */
44a47c6e 642}
643
98829f69 644const char *
645uniqueHostname(void)
646{
647 return Config.uniqueHostname ? Config.uniqueHostname : getMyHostname();
648}
649
30a4f2a8 650/* leave a privilegied section. (Give up any privilegies)
651 * Routines that need privilegies can rap themselves in enter_suid()
652 * and leave_suid()
653 * To give upp all posibilites to gain privilegies use no_suid()
12b9e9b1 654 */
b8d8561b 655void
0673c0ba 656leave_suid(void)
12b9e9b1 657{
36584579 658 debugs(21, 3, "leave_suid: PID " << getpid() << " called");
62e76326 659
e3d74828 660 if (Config.effectiveGroup) {
661
662#if HAVE_SETGROUPS
663
664 setgroups(1, &Config2.effectiveGroupID);
665
666#endif
667
668 if (setgid(Config2.effectiveGroupID) < 0)
669 debug(50, 0) ("ALERT: setgid: %s\n", xstrerror());
670
671 }
672
12b9e9b1 673 if (geteuid() != 0)
62e76326 674 return;
675
12b9e9b1 676 /* Started as a root, check suid option */
b6f794d6 677 if (Config.effectiveUser == NULL)
62e76326 678 return;
679
36584579 680 debugs(21, 3, "leave_suid: PID " << getpid() << " giving up root, becoming '" << Config.effectiveUser << "'");
62e76326 681
e3d74828 682 if (!Config.effectiveGroup) {
62e76326 683
e3d74828 684 if (setgid(Config2.effectiveGroupID) < 0)
685 debug(50, 0) ("ALERT: setgid: %s\n", xstrerror());
62e76326 686
e3d74828 687 if (initgroups(Config.effectiveUser, Config2.effectiveGroupID) < 0) {
688 debug(50, 0) ("ALERT: initgroups: unable to set groups for User %s "
689 "and Group %u", Config.effectiveUser,
690 (unsigned) Config2.effectiveGroupID);
691 }
692 }
62e76326 693
234967c9 694#if HAVE_SETRESUID
62e76326 695
d20b1cd0 696 if (setresuid(Config2.effectiveUserID, Config2.effectiveUserID, 0) < 0)
62e76326 697 debug(50, 0) ("ALERT: setresuid: %s\n", xstrerror());
698
234967c9 699#elif HAVE_SETEUID
62e76326 700
d20b1cd0 701 if (seteuid(Config2.effectiveUserID) < 0)
62e76326 702 debug(50, 0) ("ALERT: seteuid: %s\n", xstrerror());
703
234967c9 704#else
62e76326 705
d20b1cd0 706 if (setuid(Config2.effectiveUserID) < 0)
62e76326 707 debug(50, 0) ("ALERT: setuid: %s\n", xstrerror());
708
62ae0622 709#endif
710#if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
711 /* Set Linux DUMPABLE flag */
712 if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0)
bdfd16aa 713 debug(50, 2) ("ALERT: prctl: %s\n", xstrerror());
62ae0622 714
234967c9 715#endif
716}
717
30a4f2a8 718/* Enter a privilegied section */
b8d8561b 719void
0673c0ba 720enter_suid(void)
234967c9 721{
36584579 722 debugs(21, 3, "enter_suid: PID " << getpid() << " taking root priveleges");
234967c9 723#if HAVE_SETRESUID
62e76326 724
e6ccf245 725 setresuid((uid_t)-1, 0, (uid_t)-1);
234967c9 726#else
62e76326 727
234967c9 728 setuid(0);
729#endif
62ae0622 730#if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
731 /* Set Linux DUMPABLE flag */
732
733 if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0)
bdfd16aa 734 debug(50, 2) ("ALERT: prctl: %s\n", xstrerror());
62ae0622 735
736#endif
234967c9 737}
738
30a4f2a8 739/* Give up the posibility to gain privilegies.
740 * this should be used before starting a sub process
741 */
b8d8561b 742void
0673c0ba 743no_suid(void)
234967c9 744{
745 uid_t uid;
30a4f2a8 746 leave_suid();
234967c9 747 uid = geteuid();
36584579 748 debugs(21, 3, "leave_suid: PID " << getpid() << " giving up root priveleges forever");
234967c9 749#if HAVE_SETRESUID
62e76326 750
429fdbec 751 if (setresuid(uid, uid, uid) < 0)
62e76326 752 debug(50, 1) ("no_suid: setresuid: %s\n", xstrerror());
753
234967c9 754#else
62e76326 755
234967c9 756 setuid(0);
62e76326 757
429fdbec 758 if (setuid(uid) < 0)
62e76326 759 debug(50, 1) ("no_suid: setuid: %s\n", xstrerror());
760
62ae0622 761#endif
762#if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
763 /* Set Linux DUMPABLE flag */
764 if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0)
bdfd16aa 765 debug(50, 2) ("ALERT: prctl: %s\n", xstrerror());
62ae0622 766
234967c9 767#endif
12b9e9b1 768}
ccff9601 769
b8d8561b 770void
0673c0ba 771writePidFile(void)
ccff9601 772{
9e4ad609 773 int fd;
0b4639af 774 const char *f = NULL;
973e9fe1 775 mode_t old_umask;
9e4ad609 776 char buf[32];
62e76326 777
9e4ad609 778 if ((f = Config.pidFilename) == NULL)
62e76326 779 return;
780
9e4ad609 781 if (!strcmp(Config.pidFilename, "none"))
62e76326 782 return;
783
30a4f2a8 784 enter_suid();
62e76326 785
973e9fe1 786 old_umask = umask(022);
62e76326 787
c4aefe96 788 fd = file_open(f, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT);
62e76326 789
973e9fe1 790 umask(old_umask);
62e76326 791
30a4f2a8 792 leave_suid();
62e76326 793
9e4ad609 794 if (fd < 0) {
62e76326 795 debug(50, 0) ("%s: %s\n", f, xstrerror());
796 debug_trap("Could not write pid file");
797 return;
ccff9601 798 }
62e76326 799
56878878 800 snprintf(buf, 32, "%d\n", (int) getpid());
1f7c9178 801 FD_WRITE_METHOD(fd, buf, strlen(buf));
9e4ad609 802 file_close(fd);
ccff9601 803}
c4fb974e 804
805
ff8d0ea6 806pid_t
0673c0ba 807readPidFile(void)
7690e8eb 808{
809 FILE *pid_fp = NULL;
9e4ad609 810 const char *f = Config.pidFilename;
1ee742a2 811 char *chroot_f = NULL;
ff8d0ea6 812 pid_t pid = -1;
813 int i;
7690e8eb 814
9e4ad609 815 if (f == NULL || !strcmp(Config.pidFilename, "none")) {
62e76326 816 fprintf(stderr, "%s: ERROR: No pid file name defined\n", appname);
817 exit(1);
7690e8eb 818 }
62e76326 819
1ee742a2 820 if (Config.chroot_dir && geteuid() == 0) {
821 int len = strlen(Config.chroot_dir) + 1 + strlen(f) + 1;
822 chroot_f = (char *)xmalloc(strlen(Config.chroot_dir) + 1 + strlen(f) + 1);
823 snprintf(chroot_f, len, "%s/%s", Config.chroot_dir, f);
824 f = chroot_f;
825 }
826
7690e8eb 827 pid_fp = fopen(f, "r");
62e76326 828
7690e8eb 829 if (pid_fp != NULL) {
62e76326 830 pid = 0;
831
832 if (fscanf(pid_fp, "%d", &i) == 1)
833 pid = (pid_t) i;
834
835 fclose(pid_fp);
7690e8eb 836 } else {
62e76326 837 if (errno != ENOENT) {
838 fprintf(stderr, "%s: ERROR: Could not read pid file\n", appname);
839 fprintf(stderr, "\t%s: %s\n", f, xstrerror());
840 exit(1);
841 }
7690e8eb 842 }
62e76326 843
1ee742a2 844 safe_free(chroot_f);
7690e8eb 845 return pid;
846}
847
848
b8d8561b 849void
0673c0ba 850setMaxFD(void)
c4fb974e 851{
234967c9 852#if HAVE_SETRLIMIT
c4fb974e 853 /* try to use as many file descriptors as possible */
854 /* System V uses RLIMIT_NOFILE and BSD uses RLIMIT_OFILE */
62e76326 855
c4fb974e 856 struct rlimit rl;
857#if defined(RLIMIT_NOFILE)
62e76326 858
c4fb974e 859 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
62e76326 860 debug(50, 0) ("setrlimit: RLIMIT_NOFILE: %s\n", xstrerror());
c4fb974e 861 } else {
62e76326 862 rl.rlim_cur = Squid_MaxFD;
863
864 if (rl.rlim_cur > rl.rlim_max)
865 Squid_MaxFD = rl.rlim_cur = rl.rlim_max;
866
867 if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
868 snprintf(tmp_error_buf, ERROR_BUF_SZ,
869 "setrlimit: RLIMIT_NOFILE: %s", xstrerror());
870 fatal_dump(tmp_error_buf);
871 }
c4fb974e 872 }
62e76326 873
c4fb974e 874#elif defined(RLIMIT_OFILE)
875 if (getrlimit(RLIMIT_OFILE, &rl) < 0) {
62e76326 876 debug(50, 0) ("setrlimit: RLIMIT_NOFILE: %s\n", xstrerror());
c4fb974e 877 } else {
62e76326 878 rl.rlim_cur = Squid_MaxFD;
879
880 if (rl.rlim_cur > rl.rlim_max)
881 Squid_MaxFD = rl.rlim_cur = rl.rlim_max;
882
883 if (setrlimit(RLIMIT_OFILE, &rl) < 0) {
884 snprintf(tmp_error_buf, ERROR_BUF_SZ,
885 "setrlimit: RLIMIT_OFILE: %s", xstrerror());
886 fatal_dump(tmp_error_buf);
887 }
c4fb974e 888 }
62e76326 889
c4fb974e 890#endif
c4fb974e 891#else /* HAVE_SETRLIMIT */
a3d5953d 892 debug(21, 1) ("setMaxFD: Cannot increase: setrlimit() not supported on this system\n");
62e76326 893
30a4f2a8 894#endif /* HAVE_SETRLIMIT */
895
896#if HAVE_SETRLIMIT && defined(RLIMIT_DATA)
62e76326 897
30a4f2a8 898 if (getrlimit(RLIMIT_DATA, &rl) < 0) {
62e76326 899 debug(50, 0) ("getrlimit: RLIMIT_DATA: %s\n", xstrerror());
88738790 900 } else if (rl.rlim_max > rl.rlim_cur) {
62e76326 901 rl.rlim_cur = rl.rlim_max; /* set it to the max */
902
903 if (setrlimit(RLIMIT_DATA, &rl) < 0) {
904 snprintf(tmp_error_buf, ERROR_BUF_SZ,
905 "setrlimit: RLIMIT_DATA: %s", xstrerror());
906 fatal_dump(tmp_error_buf);
907 }
30a4f2a8 908 }
62e76326 909
30a4f2a8 910#endif /* RLIMIT_DATA */
ad7ef91a 911#if HAVE_SETRLIMIT && defined(RLIMIT_VMEM)
912 if (getrlimit(RLIMIT_VMEM, &rl) < 0) {
62e76326 913 debug(50, 0) ("getrlimit: RLIMIT_VMEM: %s\n", xstrerror());
88738790 914 } else if (rl.rlim_max > rl.rlim_cur) {
62e76326 915 rl.rlim_cur = rl.rlim_max; /* set it to the max */
916
917 if (setrlimit(RLIMIT_VMEM, &rl) < 0) {
918 snprintf(tmp_error_buf, ERROR_BUF_SZ,
919 "setrlimit: RLIMIT_VMEM: %s", xstrerror());
920 fatal_dump(tmp_error_buf);
921 }
ad7ef91a 922 }
62e76326 923
ad7ef91a 924#endif /* RLIMIT_VMEM */
c4fb974e 925}
3a57089e 926
b8d8561b 927time_t
0673c0ba 928getCurrentTime(void)
3a57089e 929{
94e891ea 930#if GETTIMEOFDAY_NO_TZP
30a4f2a8 931 gettimeofday(&current_time);
932#else
62e76326 933
d598947a 934 gettimeofday(&current_time, NULL);
5f3f8d0e 935#endif
62e76326 936
52040193 937 current_dtime = (double) current_time.tv_sec +
62e76326 938 (double) current_time.tv_usec / 1000000.0;
30a4f2a8 939 return squid_curtime = current_time.tv_sec;
5f3f8d0e 940}
fe113054 941
b8d8561b 942int
943percent(int a, int b)
30a4f2a8 944{
945 return b ? ((int) (100.0 * a / b + 0.5)) : 0;
946}
947
a7c05555 948double
f2908497 949dpercent(double a, double b)
950{
951 return b ? (100.0 * a / b) : 0.0;
952}
953
b8d8561b 954void
ea3a2a69 955squid_signal(int sig, SIGHDLR * func, int flags)
30a4f2a8 956{
957#if HAVE_SIGACTION
62e76326 958
30a4f2a8 959 struct sigaction sa;
960 sa.sa_handler = func;
961 sa.sa_flags = flags;
962 sigemptyset(&sa.sa_mask);
62e76326 963
30a4f2a8 964 if (sigaction(sig, &sa, NULL) < 0)
62e76326 965 debug(50, 0) ("sigaction: sig=%d func=%p: %s\n", sig, func, xstrerror());
966
30a4f2a8 967#else
06648839 968#ifdef _SQUID_MSWIN_
969 /*
970 On Windows, only SIGINT, SIGILL, SIGFPE, SIGTERM, SIGBREAK, SIGABRT and SIGSEGV signals
971 are supported, so we must care of don't call signal() for other value.
972 The SIGILL, SIGSEGV, and SIGTERM signals are not generated under Windows. They are defined
973 for ANSI compatibility, so both SIGSEGV and SIGBUS are emulated with an Exception Handler.
974 */
975 switch (sig) {
976
977 case SIGINT:
978
979 case SIGILL:
980
981 case SIGFPE:
982
983 case SIGTERM:
984
985 case SIGBREAK:
986
987 case SIGABRT:
988 break;
989
990 case SIGSEGV:
991 WIN32_ExceptionHandlerInit();
992 break;
993
994 case SIGBUS:
995 WIN32_ExceptionHandlerInit();
996 return;
997 break; /* Nor reached */
998
999 default:
1000 return;
1001 break; /* Nor reached */
1002 }
1003
1004#endif
62e76326 1005
06648839 1006 signal(sig, func);
62e76326 1007
30a4f2a8 1008#endif
1009}
396c5745 1010
ddfcbc22 1011struct IN_ADDR
62e76326 1012
1013 inaddrFromHostent(const struct hostent *hp)
16b204c4 1014{
62e76326 1015
ddfcbc22 1016 struct IN_ADDR s;
3c0117c9 1017 xmemcpy(&s.s_addr, hp->h_addr, sizeof(s.s_addr));
cc6a9d2e 1018 return s;
16b204c4 1019}
88738790 1020
9e4ad609 1021double
e6ccf245 1022doubleAverage(double cur, double newD, int N, int max)
9e4ad609 1023{
1d8e0d40 1024 if (N > max)
62e76326 1025 N = max;
1026
e6ccf245 1027 return (cur * (N - 1.0) + newD) / N;
9e4ad609 1028}
1029
1030int
e6ccf245 1031intAverage(int cur, int newI, int n, int max)
9e4ad609 1032{
1033 if (n > max)
62e76326 1034 n = max;
1035
e6ccf245 1036 return (cur * (n - 1) + newI) / n;
9e4ad609 1037}
70364f29 1038
1039void
1040logsFlush(void)
1041{
1042 if (debug_log)
62e76326 1043 fflush(debug_log);
70364f29 1044}
88738790 1045
94439e4e 1046dlink_node *
1047dlinkNodeNew()
1048{
1049 if (dlink_node_pool == NULL)
b001e822 1050 dlink_node_pool = MemPools::GetInstance().create("Dlink list nodes", sizeof(dlink_node));
62e76326 1051
b001e822 1052 /* where should we call delete dlink_node_pool;dlink_node_pool = NULL; */
1053 return (dlink_node *)dlink_node_pool->alloc();
94439e4e 1054}
1055
1056/* the node needs to be unlinked FIRST */
1057void
1058dlinkNodeDelete(dlink_node * m)
1059{
1060 if (m == NULL)
62e76326 1061 return;
1062
b001e822 1063 dlink_node_pool->free(m);
94439e4e 1064}
1065
2ac237e2 1066void
1067dlinkAdd(void *data, dlink_node * m, dlink_list * list)
1068{
1069 m->data = data;
1070 m->prev = NULL;
1071 m->next = list->head;
62e76326 1072
2ac237e2 1073 if (list->head)
62e76326 1074 list->head->prev = m;
1075
2ac237e2 1076 list->head = m;
62e76326 1077
2ac237e2 1078 if (list->tail == NULL)
62e76326 1079 list->tail = m;
2ac237e2 1080}
1081
edce4d98 1082void
1083dlinkAddAfter(void *data, dlink_node * m, dlink_node * n, dlink_list * list)
1084{
1085 m->data = data;
1086 m->prev = n;
1087 m->next = n->next;
62e76326 1088
edce4d98 1089 if (n->next)
62e76326 1090 n->next->prev = m;
edce4d98 1091 else {
62e76326 1092 assert(list->tail == n);
1093 list->tail = m;
edce4d98 1094 }
62e76326 1095
edce4d98 1096 n->next = m;
1097}
1098
b2329b6a 1099void
1100dlinkAddTail(void *data, dlink_node * m, dlink_list * list)
1101{
1102 m->data = data;
1103 m->next = NULL;
1104 m->prev = list->tail;
62e76326 1105
b2329b6a 1106 if (list->tail)
62e76326 1107 list->tail->next = m;
1108
b2329b6a 1109 list->tail = m;
62e76326 1110
b2329b6a 1111 if (list->head == NULL)
62e76326 1112 list->head = m;
b2329b6a 1113}
1114
2ac237e2 1115void
1116dlinkDelete(dlink_node * m, dlink_list * list)
1117{
1118 if (m->next)
62e76326 1119 m->next->prev = m->prev;
1120
2ac237e2 1121 if (m->prev)
62e76326 1122 m->prev->next = m->next;
1123
2ac237e2 1124 if (m == list->head)
62e76326 1125 list->head = m->next;
1126
2ac237e2 1127 if (m == list->tail)
62e76326 1128 list->tail = m->prev;
1129
92b8c095 1130 m->next = m->prev = NULL;
2ac237e2 1131}
a7c05555 1132
16300b58 1133void
0e473d70 1134kb_incr(kb_t * k, size_t v)
a7c05555 1135{
0e473d70 1136 k->bytes += v;
1137 k->kb += (k->bytes >> 10);
1138 k->bytes &= 0x3FF;
a7c05555 1139}
c1dabf04 1140
a00a7c85 1141void
6bbf9cc4 1142debugObj(int section, int level, const char *label, void *obj, ObjPackMethod pm)
a00a7c85 1143{
1144 MemBuf mb;
1145 Packer p;
6bbf9cc4 1146 assert(label && obj && pm);
2fe7eff9 1147 mb.init();
a00a7c85 1148 packerToMemInit(&p, &mb);
0cdcddb9 1149 (*pm) (obj, &p);
6bbf9cc4 1150 debug(section, level) ("%s%s", label, mb.buf);
a00a7c85 1151 packerClean(&p);
2fe7eff9 1152 mb.clean();
a00a7c85 1153}
d548ee64 1154
0e70aa1e 1155void
1156parseEtcHosts(void)
1157{
1158 FILE *fp;
1159 char buf[1024];
1160 char buf2[512];
1161 char *nt = buf;
1162 char *lt = buf;
fa2bfbab 1163
0e70aa1e 1164 if (NULL == Config.etcHostsPath)
62e76326 1165 return;
1166
0e70aa1e 1167 if (0 == strcmp(Config.etcHostsPath, "none"))
62e76326 1168 return;
1169
0e70aa1e 1170 fp = fopen(Config.etcHostsPath, "r");
62e76326 1171
0e70aa1e 1172 if (fp == NULL) {
62e76326 1173 debug(1, 1) ("parseEtcHosts: %s: %s\n",
1174 Config.etcHostsPath, xstrerror());
1175 return;
0e70aa1e 1176 }
62e76326 1177
ec4daaa5 1178#ifdef _SQUID_WIN32_
c4aefe96 1179 setmode(fileno(fp), O_TEXT);
62e76326 1180
c4aefe96 1181#endif
62e76326 1182
0e70aa1e 1183 while (fgets(buf, 1024, fp)) { /* for each line */
62e76326 1184 wordlist *hosts = NULL;
1185 char *addr;
1186
c31616ae 1187 if (buf[0] == '#') /* MS-windows likes to add comments */
1188 continue;
1189
7a10c315 1190 strtok(buf, "#"); /* chop everything following a comment marker */
62e76326 1191
1192 lt = buf;
1193
1194 addr = buf;
1195
1196 debug(1, 5) ("etc_hosts: line is '%s'\n", buf);
1197
1198 nt = strpbrk(lt, w_space);
1199
1200 if (nt == NULL) /* empty line */
1201 continue;
1202
1203 *nt = '\0'; /* null-terminate the address */
1204
1205 debug(1, 5) ("etc_hosts: address is '%s'\n", addr);
1206
1207 lt = nt + 1;
1208
1209 while ((nt = strpbrk(lt, w_space))) {
1210 char *host = NULL;
1211
1212 if (nt == lt) { /* multiple spaces */
1213 debug(1, 5) ("etc_hosts: multiple spaces, skipping\n");
1214 lt = nt + 1;
1215 continue;
1216 }
1217
1218 *nt = '\0';
1219 debug(1, 5) ("etc_hosts: got hostname '%s'\n", lt);
1220
1221 if (Config.appendDomain && !strchr(lt, '.')) {
1222 /* I know it's ugly, but it's only at reconfig */
1223 strncpy(buf2, lt, 512);
1224 strncat(buf2, Config.appendDomain, 512 - strlen(lt));
1225 host = buf2;
1226 } else {
1227 host = lt;
1228 }
1229
1230 if (ipcacheAddEntryFromHosts(host, addr) != 0)
1231 goto skip; /* invalid address, continuing is useless */
1232
1233 wordlistAdd(&hosts, host);
1234
1235 lt = nt + 1;
1236 }
1237
1238 fqdncacheAddEntryFromHosts(addr, hosts);
1239
1240skip:
1241 wordlistDestroy(&hosts);
0e70aa1e 1242 }
62e76326 1243
1f3c7397 1244 fclose (fp);
0e70aa1e 1245}
52f772de 1246
1247int
1248getMyPort(void)
1249{
52f772de 1250 if (Config.Sockaddr.http)
62e76326 1251 return ntohs(Config.Sockaddr.http->s.sin_port);
1252
a2727cdb 1253#if USE_SSL
62e76326 1254
a2727cdb 1255 if (Config.Sockaddr.https)
62e76326 1256 return ntohs(Config.Sockaddr.https->http.s.sin_port);
1257
52f772de 1258#endif
62e76326 1259
a2727cdb 1260 fatal("No port defined");
62e76326 1261
bac6d4bd 1262 return 0; /* NOT REACHED */
52f772de 1263}
d860a1aa 1264
d860a1aa 1265
1266/*
1267 * Inverse of strwordtok. Quotes a word if needed
1268 */
42419a95 1269void
d860a1aa 1270strwordquote(MemBuf * mb, const char *str)
1271{
1272 int quoted = 0;
62e76326 1273
d860a1aa 1274 if (strchr(str, ' ')) {
62e76326 1275 quoted = 1;
2fe7eff9 1276 mb->append("\"", 1);
d860a1aa 1277 }
62e76326 1278
d860a1aa 1279 while (*str) {
dc1af3cf 1280 int l = strcspn(str, "\"\\\n\r");
2fe7eff9 1281 mb->append(str, l);
62e76326 1282 str += l;
1283
dc1af3cf 1284 switch(*str) {
1285
1286 case '\n':
2fe7eff9 1287 mb->append("\\n", 2);
dc1af3cf 1288 str++;
1289 break;
1290
1291 case '\r':
2fe7eff9 1292 mb->append("\\r", 2);
dc1af3cf 1293 str++;
1294 break;
1295
1296 case '\0':
1297 break;
1298
1299 default:
2fe7eff9 1300 mb->append("\\", 1);
1301 mb->append(str, 1);
62e76326 1302 str++;
dc1af3cf 1303 break;
62e76326 1304 }
d860a1aa 1305 }
62e76326 1306
d860a1aa 1307 if (quoted)
2fe7eff9 1308 mb->append("\"", 1);
d860a1aa 1309}