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