]> git.ipfire.org Git - thirdparty/squid.git/blob - src/main.cc
Sync with trunk rev.13542
[thirdparty/squid.git] / src / main.cc
1 /*
2 * DEBUG: section 01 Startup and Main Loop
3 * AUTHOR: Harvest Derived
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
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.
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.
21 *
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.
26 *
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
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 */
32
33 #include "squid.h"
34 #include "AccessLogEntry.h"
35 #include "acl/Acl.h"
36 #include "acl/Asn.h"
37 #include "AuthReg.h"
38 #include "base/RunnersRegistry.h"
39 #include "base/Subscription.h"
40 #include "base/TextException.h"
41 #include "cache_cf.h"
42 #include "carp.h"
43 #include "client_db.h"
44 #include "client_side.h"
45 #include "comm.h"
46 #include "ConfigParser.h"
47 #include "CpuAffinity.h"
48 #include "disk.h"
49 #include "DiskIO/DiskIOModule.h"
50 #include "errorpage.h"
51 #include "event.h"
52 #include "EventLoop.h"
53 #include "ExternalACL.h"
54 #include "fd.h"
55 #include "format/Token.h"
56 #include "fqdncache.h"
57 #include "fs/Module.h"
58 #include "FwdState.h"
59 #include "globals.h"
60 #include "htcp.h"
61 #include "HttpHeader.h"
62 #include "HttpReply.h"
63 #include "icmp/IcmpSquid.h"
64 #include "icmp/net_db.h"
65 #include "ICP.h"
66 #include "ident/Ident.h"
67 #include "ip/tools.h"
68 #include "ipc/Coordinator.h"
69 #include "ipc/Kids.h"
70 #include "ipc/Strand.h"
71 #include "ipcache.h"
72 #include "Mem.h"
73 #include "MemPool.h"
74 #include "mime.h"
75 #include "neighbors.h"
76 #include "parser/Tokenizer.h"
77 #include "pconn.h"
78 #include "peer_sourcehash.h"
79 #include "peer_userhash.h"
80 #include "PeerSelectState.h"
81 #include "profiler/Profiler.h"
82 #include "redirect.h"
83 #include "refresh.h"
84 #include "send-announce.h"
85 #include "SquidConfig.h"
86 #include "SquidDns.h"
87 #include "SquidTime.h"
88 #include "stat.h"
89 #include "StatCounters.h"
90 #include "Store.h"
91 #include "store_log.h"
92 #include "StoreFileSystem.h"
93 #include "SwapDir.h"
94 #include "tools.h"
95 #include "unlinkd.h"
96 #include "URL.h"
97 #include "wccp.h"
98 #include "wccp2.h"
99 #include "WinSvc.h"
100
101 #if USE_ADAPTATION
102 #include "adaptation/Config.h"
103 #endif
104 #if USE_ECAP
105 #include "adaptation/ecap/Config.h"
106 #endif
107 #if ICAP_CLIENT
108 #include "adaptation/icap/Config.h"
109 #include "adaptation/icap/icap_log.h"
110 #endif
111 #if USE_AUTH
112 #include "auth/Gadgets.h"
113 #endif
114 #if USE_DELAY_POOLS
115 #include "ClientDelayConfig.h"
116 #endif
117 #if USE_DELAY_POOLS
118 #include "DelayPools.h"
119 #endif
120 #if USE_LOADABLE_MODULES
121 #include "LoadableModules.h"
122 #endif
123 #if USE_SSL_CRTD
124 #include "ssl/certificate_db.h"
125 #endif
126 #if USE_OPENSSL
127 #include "ssl/context_storage.h"
128 #include "ssl/helper.h"
129 #endif
130 #if ICAP_CLIENT
131 #include "adaptation/icap/Config.h"
132 #endif
133 #if USE_ECAP
134 #include "adaptation/ecap/Config.h"
135 #endif
136 #if USE_ADAPTATION
137 #include "adaptation/Config.h"
138 #endif
139 #if USE_SQUID_ESI
140 #include "esi/Module.h"
141 #endif
142 #if SQUID_SNMP
143 #include "snmp_core.h"
144 #endif
145
146 #include <cerrno>
147 #if HAVE_PATHS_H
148 #include <paths.h>
149 #endif
150 #if HAVE_SYS_WAIT_H
151 #include <sys/wait.h>
152 #endif
153
154 #if USE_WIN32_SERVICE
155 #include <process.h>
156
157 static int opt_install_service = FALSE;
158 static int opt_remove_service = FALSE;
159 static int opt_command_line = FALSE;
160 void WIN32_svcstatusupdate(DWORD, DWORD);
161 void WINAPI WIN32_svcHandler(DWORD);
162 #endif
163
164 static int opt_signal_service = FALSE;
165 static char *opt_syslog_facility = NULL;
166 static int icpPortNumOverride = 1; /* Want to detect "-u 0" */
167 static int configured_once = 0;
168 #if MALLOC_DBG
169 static int malloc_debug_level = 0;
170 #endif
171 static volatile int do_reconfigure = 0;
172 static volatile int do_rotate = 0;
173 static volatile int do_shutdown = 0;
174 static volatile int shutdown_status = 0;
175
176 static int RotateSignal = -1;
177 static int ReconfigureSignal = -1;
178 static int ShutdownSignal = -1;
179
180 static void mainRotate(void);
181 static void mainReconfigureStart(void);
182 static void mainReconfigureFinish(void*);
183 static void mainInitialize(void);
184 static void usage(void);
185 static void mainParseOptions(int argc, char *argv[]);
186 static void sendSignal(void);
187 static void serverConnectionsOpen(void);
188 static void serverConnectionsClose(void);
189 static void watch_child(char **);
190 static void setEffectiveUser(void);
191 static void SquidShutdown(void);
192 static void mainSetCwd(void);
193 static int checkRunningPid(void);
194
195 #if !_SQUID_WINDOWS_
196 static const char *squid_start_script = "squid_start";
197 #endif
198
199 #if TEST_ACCESS
200 #include "test_access.c"
201 #endif
202
203 /** temporary thunk across to the unrefactored store interface */
204
205 class StoreRootEngine : public AsyncEngine
206 {
207
208 public:
209 int checkEvents(int timeout) {
210 Store::Root().callback();
211 return EVENT_IDLE;
212 };
213 };
214
215 class SignalEngine: public AsyncEngine
216 {
217
218 public:
219 virtual int checkEvents(int timeout);
220
221 private:
222 static void StopEventLoop(void *) {
223 if (EventLoop::Running)
224 EventLoop::Running->stop();
225 }
226
227 void doShutdown(time_t wait);
228 };
229
230 int
231 SignalEngine::checkEvents(int timeout)
232 {
233 PROF_start(SignalEngine_checkEvents);
234
235 if (do_reconfigure) {
236 mainReconfigureStart();
237 do_reconfigure = 0;
238 } else if (do_rotate) {
239 mainRotate();
240 do_rotate = 0;
241 } else if (do_shutdown) {
242 doShutdown(do_shutdown > 0 ? (int) Config.shutdownLifetime : 0);
243 do_shutdown = 0;
244 }
245 BroadcastSignalIfAny(DebugSignal);
246 BroadcastSignalIfAny(RotateSignal);
247 BroadcastSignalIfAny(ReconfigureSignal);
248 BroadcastSignalIfAny(ShutdownSignal);
249
250 PROF_stop(SignalEngine_checkEvents);
251 return EVENT_IDLE;
252 }
253
254 void
255 SignalEngine::doShutdown(time_t wait)
256 {
257 debugs(1, DBG_IMPORTANT, "Preparing for shutdown after " << statCounter.client_http.requests << " requests");
258 debugs(1, DBG_IMPORTANT, "Waiting " << wait << " seconds for active connections to finish");
259
260 shutting_down = 1;
261
262 #if USE_WIN32_SERVICE
263 WIN32_svcstatusupdate(SERVICE_STOP_PENDING, (wait + 1) * 1000);
264 #endif
265
266 /* run the closure code which can be shared with reconfigure */
267 serverConnectionsClose();
268 #if USE_AUTH
269 /* detach the auth components (only do this on full shutdown) */
270 Auth::Scheme::FreeAll();
271 #endif
272
273 RunRegisteredHere(RegisteredRunner::startShutdown);
274 eventAdd("SquidShutdown", &StopEventLoop, this, (double) (wait + 1), 1, false);
275 }
276
277 static void
278 usage(void)
279 {
280 fprintf(stderr,
281 "Usage: %s [-cdhvzCFNRVYX] [-n name] [-s | -l facility] [-f config-file] [-[au] port] [-k signal]"
282 #if USE_WIN32_SERVICE
283 "[-ir] [-O CommandLine]"
284 #endif
285 "\n"
286 " -a port Specify HTTP port number (default: %d).\n"
287 " -d level Write debugging to stderr also.\n"
288 " -f file Use given config-file instead of\n"
289 " %s\n"
290 " -h Print help message.\n"
291 #if USE_WIN32_SERVICE
292 " -i Installs as a Windows Service (see -n option).\n"
293 #endif
294 " -k reconfigure|rotate|shutdown|"
295 #ifdef SIGTTIN
296 "restart|"
297 #endif
298 "interrupt|kill|debug|check|parse\n"
299 " Parse configuration file, then send signal to \n"
300 " running copy (except -k parse) and exit.\n"
301 " -n name Specify service name to use for service operations\n"
302 " default is: " APP_SHORTNAME ".\n"
303 #if USE_WIN32_SERVICE
304 " -r Removes a Windows Service (see -n option).\n"
305 #endif
306 " -s | -l facility\n"
307 " Enable logging to syslog.\n"
308 " -u port Specify ICP port number (default: %d), disable with 0.\n"
309 " -v Print version.\n"
310 " -z Create missing swap directories and then exit.\n"
311 " -C Do not catch fatal signals.\n"
312 " -D OBSOLETE. Scheduled for removal.\n"
313 " -F Don't serve any requests until store is rebuilt.\n"
314 " -N No daemon mode.\n"
315 #if USE_WIN32_SERVICE
316 " -O options\n"
317 " Set Windows Service Command line options in Registry.\n"
318 #endif
319 " -R Do not set REUSEADDR on port.\n"
320 " -S Double-check swap during rebuild.\n"
321 " -X Force full debugging.\n"
322 " -Y Only return UDP_HIT or UDP_MISS_NOFETCH during fast reload.\n",
323 APP_SHORTNAME, CACHE_HTTP_PORT, DefaultConfigFile, CACHE_ICP_PORT);
324 exit(1);
325 }
326
327 /**
328 * Parse the parameters received via command line interface.
329 *
330 \param argc Number of options received on command line
331 \param argv List of parameters received on command line
332 */
333 static void
334 mainParseOptions(int argc, char *argv[])
335 {
336 extern char *optarg;
337 int c;
338
339 #if USE_WIN32_SERVICE
340 while ((c = getopt(argc, argv, "CDFNO:RSVYXa:d:f:hik:m::n:rsl:u:vz?")) != -1)
341 #else
342 while ((c = getopt(argc, argv, "CDFNRSYXa:d:f:hk:m::n:sl:u:vz?")) != -1)
343 #endif
344 {
345
346 switch (c) {
347
348 case 'C':
349 /** \par C
350 * Unset/disabel global option for catchign signals. opt_catch_signals */
351 opt_catch_signals = 0;
352 break;
353
354 case 'D':
355 /** \par D
356 * OBSOLETE: WAS: override to prevent optional startup DNS tests. */
357 debugs(1,DBG_CRITICAL, "WARNING: -D command-line option is obsolete.");
358 break;
359
360 case 'F':
361 /** \par F
362 * Set global option for foreground rebuild. opt_foreground_rebuild */
363 opt_foreground_rebuild = 1;
364 break;
365
366 case 'N':
367 /** \par N
368 * Set global option for 'no_daemon' mode. opt_no_daemon */
369 opt_no_daemon = 1;
370 break;
371
372 #if USE_WIN32_SERVICE
373
374 case 'O':
375 /** \par O
376 * Set global option. opt_command_lin and WIN32_Command_Line */
377 opt_command_line = 1;
378 WIN32_Command_Line = xstrdup(optarg);
379 break;
380 #endif
381
382 case 'R':
383 /** \par R
384 * Unset/disable global option opt_reuseaddr */
385 opt_reuseaddr = 0;
386 break;
387
388 case 'S':
389 /** \par S
390 * Set global option opt_store_doublecheck */
391 opt_store_doublecheck = 1;
392 break;
393
394 case 'X':
395 /** \par X
396 * Force full debugging */
397 Debug::parseOptions("rotate=0 ALL,9");
398 Debug::override_X = 1;
399 sigusr2_handle(SIGUSR2);
400 break;
401
402 case 'Y':
403 /** \par Y
404 * Set global option opt_reload_hit_only */
405 opt_reload_hit_only = 1;
406 break;
407
408 #if USE_WIN32_SERVICE
409
410 case 'i':
411 /** \par i
412 * Set global option opt_install_service (to TRUE) */
413 opt_install_service = TRUE;
414 break;
415 #endif
416
417 case 'a':
418 /** \par a
419 * Add optional HTTP port as given following the option */
420 add_http_port(optarg);
421 break;
422
423 case 'd':
424 /** \par d
425 * Set global option Debug::log_stderr to the number given follwoign the option */
426 Debug::log_stderr = atoi(optarg);
427 break;
428
429 case 'f':
430 /** \par f
431 * Load the file given instead of the default squid.conf. */
432 xfree(ConfigFile);
433 ConfigFile = xstrdup(optarg);
434 break;
435
436 case 'k':
437 /** \par k
438 * Run the administrative action given following the option */
439
440 /** \li When its an unknown option display the usage help. */
441 if ((int) strlen(optarg) < 1)
442 usage();
443
444 if (!strncmp(optarg, "reconfigure", strlen(optarg)))
445 /** \li On reconfigure send SIGHUP. */
446 opt_send_signal = SIGHUP;
447 else if (!strncmp(optarg, "rotate", strlen(optarg)))
448 /** \li On rotate send SIGQUIT or SIGUSR1. */
449 #if defined(_SQUID_LINUX_THREADS_)
450 opt_send_signal = SIGQUIT;
451 #else
452 opt_send_signal = SIGUSR1;
453 #endif
454
455 else if (!strncmp(optarg, "debug", strlen(optarg)))
456 /** \li On debug send SIGTRAP or SIGUSR2. */
457 #if defined(_SQUID_LINUX_THREADS_)
458 opt_send_signal = SIGTRAP;
459 #else
460 opt_send_signal = SIGUSR2;
461 #endif
462
463 else if (!strncmp(optarg, "shutdown", strlen(optarg)))
464 /** \li On shutdown send SIGTERM. */
465 opt_send_signal = SIGTERM;
466 else if (!strncmp(optarg, "interrupt", strlen(optarg)))
467 /** \li On interrupt send SIGINT. */
468 opt_send_signal = SIGINT;
469 else if (!strncmp(optarg, "kill", strlen(optarg)))
470 /** \li On kill send SIGKILL. */
471 opt_send_signal = SIGKILL;
472
473 #ifdef SIGTTIN
474
475 else if (!strncmp(optarg, "restart", strlen(optarg)))
476 /** \li On restart send SIGTTIN. (exit and restart by parent) */
477 opt_send_signal = SIGTTIN;
478
479 #endif
480
481 else if (!strncmp(optarg, "check", strlen(optarg)))
482 /** \li On check send 0 / SIGNULL. */
483 opt_send_signal = 0; /* SIGNULL */
484 else if (!strncmp(optarg, "parse", strlen(optarg)))
485 /** \li On parse set global flag to re-parse the config file only. */
486 opt_parse_cfg_only = 1;
487 else
488 usage();
489
490 break;
491
492 case 'm':
493 /** \par m
494 * Set global malloc_debug_level to the value given following the option.
495 * if none is given it toggles the xmalloc_trace option on/off */
496 if (optarg) {
497 #if MALLOC_DBG
498 malloc_debug_level = atoi(optarg);
499 #else
500 fatal("Need to add -DMALLOC_DBG when compiling to use -mX option");
501 #endif
502
503 }
504 break;
505
506 case 'n':
507 /** \par n
508 * Set global option opt_signal_service (to true).
509 * Stores the additional parameter given in global service_name */
510 if (optarg && *optarg != '\0') {
511 const SBuf t(optarg);
512 ::Parser::Tokenizer tok(t);
513 const CharacterSet chr = CharacterSet::ALPHA+CharacterSet::DIGIT;
514 if (!tok.prefix(service_name, chr))
515 fatalf("Expected alphanumeric service name for the -n option but got: %s", optarg);
516 if (!tok.atEnd())
517 fatalf("Garbage after alphanumeric service name in the -n option value: %s", optarg);
518 if (service_name.length() > 32)
519 fatalf("Service name (-n option) must be limited to 32 characters but got %u", service_name.length());
520 opt_signal_service = true;
521 } else {
522 fatal("A service name is required for the -n option");
523 }
524 break;
525
526 #if USE_WIN32_SERVICE
527
528 case 'r':
529 /** \par r
530 * Set global option opt_remove_service (to TRUE) */
531 opt_remove_service = TRUE;
532
533 break;
534
535 #endif
536
537 case 'l':
538 /** \par l
539 * Stores the syslog facility name in global opt_syslog_facility
540 * then performs actions for -s option. */
541 xfree(opt_syslog_facility); // ignore any previous options sent
542 opt_syslog_facility = xstrdup(optarg);
543
544 case 's':
545 /** \par s
546 * Initialize the syslog for output */
547 #if HAVE_SYSLOG
548
549 _db_set_syslog(opt_syslog_facility);
550
551 break;
552
553 #else
554
555 fatal("Logging to syslog not available on this platform");
556
557 /* NOTREACHED */
558 #endif
559
560 case 'u':
561 /** \par u
562 * Store the ICP port number given in global option icpPortNumOverride
563 * ensuring its a positive number. */
564 icpPortNumOverride = atoi(optarg);
565
566 if (icpPortNumOverride < 0)
567 icpPortNumOverride = 0;
568
569 break;
570
571 case 'v':
572 /** \par v
573 * Display squid version and build information. Then exit. */
574 printf("Squid Cache: Version %s\n" ,version_string);
575 printf("Service Name: " SQUIDSBUFPH "\n", SQUIDSBUFPRINT(service_name));
576 if (strlen(SQUID_BUILD_INFO))
577 printf("%s\n",SQUID_BUILD_INFO);
578 printf( "configure options: %s\n", SQUID_CONFIGURE_OPTIONS);
579
580 #if USE_WIN32_SERVICE
581
582 printf("Compiled as Windows System Service.\n");
583
584 #endif
585
586 exit(0);
587
588 /* NOTREACHED */
589
590 case 'z':
591 /** \par z
592 * Set global option Debug::log_stderr and opt_create_swap_dirs */
593 Debug::log_stderr = 1;
594 opt_create_swap_dirs = 1;
595 break;
596
597 case 'h':
598
599 case '?':
600
601 default:
602 /** \par h,?, or unknown
603 * \copydoc usage() */
604 usage();
605
606 break;
607 }
608
609 }
610 }
611
612 /* ARGSUSED */
613 void
614 rotate_logs(int sig)
615 {
616 do_rotate = 1;
617 RotateSignal = sig;
618 #if !_SQUID_WINDOWS_
619 #if !HAVE_SIGACTION
620
621 signal(sig, rotate_logs);
622 #endif
623 #endif
624 }
625
626 /* ARGSUSED */
627 void
628 reconfigure(int sig)
629 {
630 do_reconfigure = 1;
631 ReconfigureSignal = sig;
632 #if !_SQUID_WINDOWS_
633 #if !HAVE_SIGACTION
634
635 signal(sig, reconfigure);
636 #endif
637 #endif
638 }
639
640 void
641 shut_down(int sig)
642 {
643 do_shutdown = sig == SIGINT ? -1 : 1;
644 ShutdownSignal = sig;
645 #ifdef SIGTTIN
646
647 if (SIGTTIN == sig)
648 shutdown_status = 1;
649
650 #endif
651
652 const pid_t ppid = getppid();
653
654 if (!IamMasterProcess() && ppid > 1) {
655 // notify master that we are shutting down
656 if (kill(ppid, SIGUSR1) < 0)
657 debugs(1, DBG_IMPORTANT, "Failed to send SIGUSR1 to master process,"
658 " pid " << ppid << ": " << xstrerror());
659 }
660
661 #if !_SQUID_WINDOWS_
662 #if KILL_PARENT_OPT
663
664 if (!IamMasterProcess() && ppid > 1) {
665 debugs(1, DBG_IMPORTANT, "Killing master process, pid " << ppid);
666
667 if (kill(ppid, sig) < 0)
668 debugs(1, DBG_IMPORTANT, "kill " << ppid << ": " << xstrerror());
669 }
670
671 #endif /* KILL_PARENT_OPT */
672 #if SA_RESETHAND == 0
673 signal(SIGTERM, SIG_DFL);
674
675 signal(SIGINT, SIG_DFL);
676
677 #endif
678 #endif
679 }
680
681 static void
682 serverConnectionsOpen(void)
683 {
684 if (IamPrimaryProcess()) {
685 #if USE_WCCP
686 wccpConnectionOpen();
687 #endif
688
689 #if USE_WCCPv2
690
691 wccp2ConnectionOpen();
692 #endif
693 }
694 // start various proxying services if we are responsible for them
695 if (IamWorkerProcess()) {
696 clientOpenListenSockets();
697 icpOpenPorts();
698 #if USE_HTCP
699 htcpOpenPorts();
700 #endif
701 #if SQUID_SNMP
702 snmpOpenPorts();
703 #endif
704
705 icmpEngine.Open();
706 netdbInit();
707 asnInit();
708 ACL::Initialize();
709 peerSelectInit();
710
711 carpInit();
712 #if USE_AUTH
713 peerUserHashInit();
714 #endif
715 peerSourceHashInit();
716 }
717 }
718
719 static void
720 serverConnectionsClose(void)
721 {
722 assert(shutting_down || reconfiguring);
723
724 if (IamPrimaryProcess()) {
725 #if USE_WCCP
726
727 wccpConnectionClose();
728 #endif
729 #if USE_WCCPv2
730
731 wccp2ConnectionClose();
732 #endif
733 }
734 if (IamWorkerProcess()) {
735 clientConnectionsClose();
736 icpConnectionShutdown();
737 #if USE_HTCP
738 htcpSocketShutdown();
739 #endif
740
741 icmpEngine.Close();
742 #if SQUID_SNMP
743 snmpClosePorts();
744 #endif
745
746 asnFreeMemory();
747 }
748 }
749
750 static void
751 mainReconfigureStart(void)
752 {
753 debugs(1, DBG_IMPORTANT, "Reconfiguring Squid Cache (version " << version_string << ")...");
754 reconfiguring = 1;
755
756 // Initiate asynchronous closing sequence
757 serverConnectionsClose();
758 icpClosePorts();
759 #if USE_HTCP
760 htcpClosePorts();
761 #endif
762 dnsShutdown();
763 #if USE_SSL_CRTD
764 Ssl::Helper::GetInstance()->Shutdown();
765 #endif
766 #if USE_OPENSSL
767 if (Ssl::CertValidationHelper::GetInstance())
768 Ssl::CertValidationHelper::GetInstance()->Shutdown();
769 Ssl::TheGlobalContextStorage.reconfigureStart();
770 #endif
771 redirectShutdown();
772 #if USE_AUTH
773 authenticateReset();
774 #endif
775 externalAclShutdown();
776 storeDirCloseSwapLogs();
777 storeLogClose();
778 accessLogClose();
779 #if ICAP_CLIENT
780 icapLogClose();
781 #endif
782
783 eventAdd("mainReconfigureFinish", &mainReconfigureFinish, NULL, 0, 1,
784 false);
785 }
786
787 static void
788 mainReconfigureFinish(void *)
789 {
790 debugs(1, 3, "finishing reconfiguring");
791
792 errorClean();
793 enter_suid(); /* root to read config file */
794
795 // we may have disabled the need for PURGE
796 if (Config2.onoff.enable_purge)
797 Config2.onoff.enable_purge = 2;
798
799 // parse the config returns a count of errors encountered.
800 const int oldWorkers = Config.workers;
801 if ( parseConfigFile(ConfigFile) != 0) {
802 // for now any errors are a fatal condition...
803 self_destruct();
804 }
805 if (oldWorkers != Config.workers) {
806 debugs(1, DBG_CRITICAL, "WARNING: Changing 'workers' (from " <<
807 oldWorkers << " to " << Config.workers <<
808 ") requires a full restart. It has been ignored by reconfigure.");
809 Config.workers = oldWorkers;
810 }
811
812 RunRegisteredHere(RegisteredRunner::syncConfig);
813
814 if (IamPrimaryProcess())
815 CpuAffinityCheck();
816 CpuAffinityReconfigure();
817
818 setUmask(Config.umask);
819 Mem::Report();
820 setEffectiveUser();
821 _db_init(Debug::cache_log, Debug::debugOptions);
822 ipcache_restart(); /* clear stuck entries */
823 fqdncache_restart(); /* sigh, fqdncache too */
824 parseEtcHosts();
825 errorInitialize(); /* reload error pages */
826 accessLogInit();
827
828 #if USE_LOADABLE_MODULES
829 LoadableModulesConfigure(Config.loadable_module_names);
830 #endif
831
832 #if USE_ADAPTATION
833 bool enableAdaptation = false;
834 #if ICAP_CLIENT
835 Adaptation::Icap::TheConfig.finalize();
836 enableAdaptation = Adaptation::Icap::TheConfig.onoff || enableAdaptation;
837 #endif
838 #if USE_ECAP
839 Adaptation::Ecap::TheConfig.finalize(); // must be after we load modules
840 enableAdaptation = Adaptation::Ecap::TheConfig.onoff || enableAdaptation;
841 #endif
842 Adaptation::Config::Finalize(enableAdaptation);
843 #endif
844
845 #if ICAP_CLIENT
846 icapLogOpen();
847 #endif
848 storeLogOpen();
849 dnsInit();
850 #if USE_SSL_CRTD
851 Ssl::Helper::GetInstance()->Init();
852 #endif
853 #if USE_OPENSSL
854 if (Ssl::CertValidationHelper::GetInstance())
855 Ssl::CertValidationHelper::GetInstance()->Init();
856 #endif
857
858 redirectInit();
859 #if USE_AUTH
860 authenticateInit(&Auth::TheConfig);
861 #endif
862 externalAclInit();
863
864 if (IamPrimaryProcess()) {
865 #if USE_WCCP
866
867 wccpInit();
868 #endif
869 #if USE_WCCPv2
870
871 wccp2Init();
872 #endif
873 }
874
875 serverConnectionsOpen();
876
877 neighbors_init();
878
879 storeDirOpenSwapLogs();
880
881 mimeInit(Config.mimeTablePathname);
882
883 if (unlinkdNeeded())
884 unlinkdInit();
885
886 #if USE_DELAY_POOLS
887 Config.ClientDelay.finalize();
888 #endif
889
890 if (Config.onoff.announce) {
891 if (!eventFind(start_announce, NULL))
892 eventAdd("start_announce", start_announce, NULL, 3600.0, 1);
893 } else {
894 if (eventFind(start_announce, NULL))
895 eventDelete(start_announce, NULL);
896 }
897
898 writePidFile(); /* write PID file */
899
900 reconfiguring = 0;
901 }
902
903 static void
904 mainRotate(void)
905 {
906 icmpEngine.Close();
907 redirectShutdown();
908 #if USE_AUTH
909 authenticateRotate();
910 #endif
911 externalAclShutdown();
912
913 _db_rotate_log(); /* cache.log */
914 storeDirWriteCleanLogs(1);
915 storeLogRotate(); /* store.log */
916 accessLogRotate(); /* access.log */
917 #if ICAP_CLIENT
918 icapLogRotate(); /*icap.log*/
919 #endif
920 icmpEngine.Open();
921 redirectInit();
922 #if USE_AUTH
923 authenticateInit(&Auth::TheConfig);
924 #endif
925 externalAclInit();
926 }
927
928 static void
929 setEffectiveUser(void)
930 {
931 keepCapabilities();
932 leave_suid(); /* Run as non privilegied user */
933 #if _SQUID_OS2_
934
935 return;
936 #endif
937
938 if (geteuid() == 0) {
939 debugs(0, DBG_CRITICAL, "Squid is not safe to run as root! If you must");
940 debugs(0, DBG_CRITICAL, "start Squid as root, then you must configure");
941 debugs(0, DBG_CRITICAL, "it to run as a non-priveledged user with the");
942 debugs(0, DBG_CRITICAL, "'cache_effective_user' option in the config file.");
943 fatal("Don't run Squid as root, set 'cache_effective_user'!");
944 }
945 }
946
947 /// changes working directory, providing error reporting
948 static bool
949 mainChangeDir(const char *dir)
950 {
951 if (chdir(dir) == 0)
952 return true;
953
954 debugs(50, DBG_CRITICAL, "cannot change current directory to " << dir <<
955 ": " << xstrerror());
956 return false;
957 }
958
959 /// set the working directory.
960 static void
961 mainSetCwd(void)
962 {
963 static bool chrooted = false;
964 if (Config.chroot_dir && !chrooted) {
965 chrooted = true;
966
967 if (chroot(Config.chroot_dir) != 0)
968 fatalf("chroot to %s failed: %s", Config.chroot_dir, xstrerror());
969
970 if (!mainChangeDir("/"))
971 fatalf("chdir to / after chroot to %s failed", Config.chroot_dir);
972 }
973
974 if (Config.coredump_dir && strcmp("none", Config.coredump_dir) != 0) {
975 if (mainChangeDir(Config.coredump_dir)) {
976 debugs(0, DBG_IMPORTANT, "Set Current Directory to " << Config.coredump_dir);
977 return;
978 }
979 }
980
981 /* If we don't have coredump_dir or couldn't cd there, report current dir */
982 char pathbuf[MAXPATHLEN];
983 if (getcwd(pathbuf, MAXPATHLEN)) {
984 debugs(0, DBG_IMPORTANT, "Current Directory is " << pathbuf);
985 } else {
986 debugs(50, DBG_CRITICAL, "WARNING: Can't find current directory, getcwd: " << xstrerror());
987 }
988 }
989
990 static void
991 mainInitialize(void)
992 {
993 /* chroot if configured to run inside chroot */
994 mainSetCwd();
995
996 if (opt_catch_signals) {
997 squid_signal(SIGSEGV, death, SA_NODEFER | SA_RESETHAND);
998 squid_signal(SIGBUS, death, SA_NODEFER | SA_RESETHAND);
999 }
1000
1001 squid_signal(SIGPIPE, SIG_IGN, SA_RESTART);
1002 squid_signal(SIGCHLD, sig_child, SA_NODEFER | SA_RESTART);
1003
1004 setEffectiveUser();
1005
1006 if (icpPortNumOverride != 1)
1007 Config.Port.icp = (unsigned short) icpPortNumOverride;
1008
1009 _db_init(Debug::cache_log, Debug::debugOptions);
1010
1011 fd_open(fileno(debug_log), FD_LOG, Debug::cache_log);
1012
1013 debugs(1, DBG_CRITICAL, "Starting Squid Cache version " << version_string << " for " << CONFIG_HOST_TYPE << "...");
1014 debugs(1, DBG_CRITICAL, "Service Name: " << service_name);
1015
1016 #if _SQUID_WINDOWS_
1017 if (WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) {
1018 debugs(1, DBG_CRITICAL, "Service command line is: " << WIN32_Service_Command_Line);
1019 } else
1020 debugs(1, DBG_CRITICAL, "Running on " << WIN32_OS_string);
1021 #endif
1022
1023 debugs(1, DBG_IMPORTANT, "Process ID " << getpid());
1024
1025 debugs(1, DBG_IMPORTANT, "Process Roles:" << ProcessRoles());
1026
1027 setSystemLimits();
1028 debugs(1, DBG_IMPORTANT, "With " << Squid_MaxFD << " file descriptors available");
1029
1030 #if _SQUID_WINDOWS_
1031
1032 debugs(1, DBG_IMPORTANT, "With " << _getmaxstdio() << " CRT stdio descriptors available");
1033
1034 if (WIN32_Socks_initialized)
1035 debugs(1, DBG_IMPORTANT, "Windows sockets initialized");
1036
1037 if (WIN32_OS_version > _WIN_OS_WINNT) {
1038 WIN32_IpAddrChangeMonitorInit();
1039 }
1040
1041 #endif
1042
1043 if (!configured_once)
1044 disk_init(); /* disk_init must go before ipcache_init() */
1045
1046 ipcache_init();
1047
1048 fqdncache_init();
1049
1050 parseEtcHosts();
1051
1052 dnsInit();
1053
1054 #if USE_SSL_CRTD
1055 Ssl::Helper::GetInstance()->Init();
1056 #endif
1057
1058 #if USE_OPENSSL
1059 if (!configured_once)
1060 Ssl::initialize_session_cache();
1061
1062 if (Ssl::CertValidationHelper::GetInstance())
1063 Ssl::CertValidationHelper::GetInstance()->Init();
1064 #endif
1065
1066 redirectInit();
1067 #if USE_AUTH
1068 authenticateInit(&Auth::TheConfig);
1069 #endif
1070 externalAclInit();
1071
1072 httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */
1073
1074 httpReplyInitModule(); /* must go before accepting replies */
1075
1076 errorInitialize();
1077
1078 accessLogInit();
1079
1080 #if ICAP_CLIENT
1081 icapLogOpen();
1082 #endif
1083
1084 #if USE_IDENT
1085 Ident::Init();
1086 #endif
1087
1088 #if SQUID_SNMP
1089
1090 snmpInit();
1091
1092 #endif
1093 #if MALLOC_DBG
1094
1095 malloc_debug(0, malloc_debug_level);
1096
1097 #endif
1098
1099 if (!configured_once) {
1100 if (unlinkdNeeded())
1101 unlinkdInit();
1102
1103 urlInitialize();
1104 statInit();
1105 storeInit();
1106 mainSetCwd();
1107 mimeInit(Config.mimeTablePathname);
1108 refreshInit();
1109 #if USE_DELAY_POOLS
1110 DelayPools::Init();
1111 #endif
1112
1113 FwdState::initModule();
1114 /* register the modules in the cache manager menus */
1115
1116 cbdataRegisterWithCacheManager();
1117 /* These use separate calls so that the comm loops can eventually
1118 * coexist.
1119 */
1120
1121 eventInit();
1122
1123 // TODO: pconn is a good candidate for new-style registration
1124 // PconnModule::GetInstance()->registerWithCacheManager();
1125 // moved to PconnModule::PconnModule()
1126 }
1127
1128 if (IamPrimaryProcess()) {
1129 #if USE_WCCP
1130 wccpInit();
1131
1132 #endif
1133 #if USE_WCCPv2
1134
1135 wccp2Init();
1136
1137 #endif
1138 }
1139
1140 serverConnectionsOpen();
1141
1142 neighbors_init();
1143
1144 // neighborsRegisterWithCacheManager(); //moved to neighbors_init()
1145
1146 if (Config.chroot_dir)
1147 no_suid();
1148
1149 if (!configured_once)
1150 writePidFile(); /* write PID file */
1151
1152 #if defined(_SQUID_LINUX_THREADS_)
1153
1154 squid_signal(SIGQUIT, rotate_logs, SA_RESTART);
1155
1156 squid_signal(SIGTRAP, sigusr2_handle, SA_RESTART);
1157
1158 #else
1159
1160 squid_signal(SIGUSR1, rotate_logs, SA_RESTART);
1161
1162 squid_signal(SIGUSR2, sigusr2_handle, SA_RESTART);
1163
1164 #endif
1165
1166 squid_signal(SIGHUP, reconfigure, SA_RESTART);
1167
1168 squid_signal(SIGTERM, shut_down, SA_NODEFER | SA_RESETHAND | SA_RESTART);
1169
1170 squid_signal(SIGINT, shut_down, SA_NODEFER | SA_RESETHAND | SA_RESTART);
1171
1172 #ifdef SIGTTIN
1173
1174 squid_signal(SIGTTIN, shut_down, SA_NODEFER | SA_RESETHAND | SA_RESTART);
1175
1176 #endif
1177
1178 memCheckInit();
1179
1180 #if USE_LOADABLE_MODULES
1181 LoadableModulesConfigure(Config.loadable_module_names);
1182 #endif
1183
1184 #if USE_ADAPTATION
1185 bool enableAdaptation = false;
1186
1187 // We can remove this dependency on specific adaptation mechanisms
1188 // if we create a generic Registry of such mechanisms. Should we?
1189 #if ICAP_CLIENT
1190 Adaptation::Icap::TheConfig.finalize();
1191 enableAdaptation = Adaptation::Icap::TheConfig.onoff || enableAdaptation;
1192 #endif
1193 #if USE_ECAP
1194 Adaptation::Ecap::TheConfig.finalize(); // must be after we load modules
1195 enableAdaptation = Adaptation::Ecap::TheConfig.onoff || enableAdaptation;
1196 #endif
1197 // must be the last adaptation-related finalize
1198 Adaptation::Config::Finalize(enableAdaptation);
1199 #endif
1200
1201 #if USE_SQUID_ESI
1202 Esi::Init();
1203 #endif
1204
1205 #if USE_DELAY_POOLS
1206 Config.ClientDelay.finalize();
1207 #endif
1208
1209 if (!configured_once) {
1210 eventAdd("storeMaintain", Store::Maintain, NULL, 1.0, 1);
1211
1212 if (Config.onoff.announce)
1213 eventAdd("start_announce", start_announce, NULL, 3600.0, 1);
1214
1215 eventAdd("ipcache_purgelru", ipcache_purgelru, NULL, 10.0, 1);
1216
1217 eventAdd("fqdncache_purgelru", fqdncache_purgelru, NULL, 15.0, 1);
1218
1219 #if USE_XPROF_STATS
1220
1221 eventAdd("cpuProfiling", xprof_event, NULL, 1.0, 1);
1222
1223 #endif
1224
1225 eventAdd("memPoolCleanIdlePools", Mem::CleanIdlePools, NULL, 15.0, 1);
1226 }
1227
1228 configured_once = 1;
1229 }
1230
1231 /// unsafe main routine -- may throw
1232 int SquidMain(int argc, char **argv);
1233 /// unsafe main routine wrapper to catch exceptions
1234 static int SquidMainSafe(int argc, char **argv);
1235
1236 #if USE_WIN32_SERVICE
1237 /* When USE_WIN32_SERVICE is defined, the main function is placed in win32.cc */
1238 extern "C" void WINAPI
1239 SquidWinSvcMain(int argc, char **argv)
1240 {
1241 SquidMainSafe(argc, argv);
1242 }
1243 #else
1244 int
1245 main(int argc, char **argv)
1246 {
1247 return SquidMainSafe(argc, argv);
1248 }
1249 #endif
1250
1251 static int
1252 SquidMainSafe(int argc, char **argv)
1253 {
1254 try {
1255 return SquidMain(argc, argv);
1256 } catch (const std::exception &e) {
1257 debugs(1, DBG_CRITICAL, "FATAL: dying from an unhandled exception: " <<
1258 e.what());
1259 throw;
1260 } catch (...) {
1261 debugs(1, DBG_CRITICAL, "FATAL: dying from an unhandled exception.");
1262 throw;
1263 }
1264 return -1; // not reached
1265 }
1266
1267 /// computes name and ID for the current kid process
1268 static void
1269 ConfigureCurrentKid(const char *processName)
1270 {
1271 // kids are marked with parenthesis around their process names
1272 if (processName && processName[0] == '(') {
1273 if (const char *idStart = strrchr(processName, '-')) {
1274 KidIdentifier = atoi(idStart + 1);
1275 const size_t nameLen = idStart - (processName + 1);
1276 assert(nameLen < sizeof(TheKidName));
1277 xstrncpy(TheKidName, processName + 1, nameLen + 1);
1278 if (!strcmp(TheKidName, "squid-coord"))
1279 TheProcessKind = pkCoordinator;
1280 else if (!strcmp(TheKidName, "squid"))
1281 TheProcessKind = pkWorker;
1282 else if (!strcmp(TheKidName, "squid-disk"))
1283 TheProcessKind = pkDisker;
1284 else
1285 TheProcessKind = pkOther; // including coordinator
1286 }
1287 } else {
1288 xstrncpy(TheKidName, APP_SHORTNAME, sizeof(TheKidName));
1289 KidIdentifier = 0;
1290 }
1291 }
1292
1293 int
1294 SquidMain(int argc, char **argv)
1295 {
1296 ConfigureCurrentKid(argv[0]);
1297
1298 Debug::parseOptions(NULL);
1299 debug_log = stderr;
1300
1301 #if defined(SQUID_MAXFD_LIMIT)
1302
1303 if (SQUID_MAXFD_LIMIT < Squid_MaxFD)
1304 Squid_MaxFD = SQUID_MAXFD_LIMIT;
1305
1306 #endif
1307
1308 /* NOP under non-windows */
1309 int WIN32_init_err=0;
1310 if ((WIN32_init_err = WIN32_Subsystem_Init(&argc, &argv)))
1311 return WIN32_init_err;
1312
1313 /* call mallopt() before anything else */
1314 #if HAVE_MALLOPT
1315 #ifdef M_GRAIN
1316 /* Round up all sizes to a multiple of this */
1317 mallopt(M_GRAIN, 16);
1318
1319 #endif
1320 #ifdef M_MXFAST
1321 /* biggest size that is considered a small block */
1322 mallopt(M_MXFAST, 256);
1323
1324 #endif
1325 #ifdef M_NBLKS
1326 /* allocate this many small blocks at once */
1327 mallopt(M_NLBLKS, 32);
1328
1329 #endif
1330 #endif /* HAVE_MALLOPT */
1331
1332 squid_srandom(time(NULL));
1333
1334 getCurrentTime();
1335
1336 squid_start = current_time;
1337
1338 failure_notify = fatal_dump;
1339
1340 #if USE_WIN32_SERVICE
1341
1342 WIN32_svcstatusupdate(SERVICE_START_PENDING, 10000);
1343
1344 #endif
1345
1346 mainParseOptions(argc, argv);
1347
1348 if (opt_parse_cfg_only) {
1349 Debug::parseOptions("ALL,1");
1350 }
1351
1352 #if USE_WIN32_SERVICE
1353
1354 if (opt_install_service) {
1355 WIN32_InstallService();
1356 return 0;
1357 }
1358
1359 if (opt_remove_service) {
1360 WIN32_RemoveService();
1361 return 0;
1362 }
1363
1364 if (opt_command_line) {
1365 WIN32_SetServiceCommandLine();
1366 return 0;
1367 }
1368
1369 #endif
1370
1371 /* parse configuration file
1372 * note: in "normal" case this used to be called from mainInitialize() */
1373 {
1374 int parse_err;
1375
1376 if (!ConfigFile)
1377 ConfigFile = xstrdup(DefaultConfigFile);
1378
1379 assert(!configured_once);
1380
1381 Mem::Init();
1382
1383 storeFsInit(); /* required for config parsing */
1384
1385 /* TODO: call the FS::Clean() in shutdown to do Fs cleanups */
1386 Fs::Init();
1387
1388 /* May not be needed for parsing, have not audited for such */
1389 DiskIOModule::SetupAllModules();
1390
1391 /* Shouldn't be needed for config parsing, but have not audited for such */
1392 StoreFileSystem::SetupAllFs();
1393
1394 /* we may want the parsing process to set this up in the future */
1395 Store::Root(new StoreController);
1396 Auth::Init(); /* required for config parsing. NOP if !USE_AUTH */
1397 Ip::ProbeTransport(); // determine IPv4 or IPv6 capabilities before parsing.
1398
1399 Format::Token::Init(); // XXX: temporary. Use a runners registry of pre-parse runners instead.
1400
1401 parse_err = parseConfigFile(ConfigFile);
1402
1403 Mem::Report();
1404
1405 if (opt_parse_cfg_only || parse_err > 0)
1406 return parse_err;
1407 }
1408 setUmask(Config.umask);
1409 if (-1 == opt_send_signal)
1410 if (checkRunningPid())
1411 exit(0);
1412
1413 #if TEST_ACCESS
1414
1415 comm_init();
1416
1417 mainInitialize();
1418
1419 test_access();
1420
1421 return 0;
1422
1423 #endif
1424
1425 /* send signal to running copy and exit */
1426 if (opt_send_signal != -1) {
1427 /* chroot if configured to run inside chroot */
1428 mainSetCwd();
1429 if (Config.chroot_dir) {
1430 no_suid();
1431 } else {
1432 leave_suid();
1433 }
1434
1435 sendSignal();
1436 /* NOTREACHED */
1437 }
1438
1439 debugs(1,2, HERE << "Doing post-config initialization\n");
1440 leave_suid();
1441 RunRegisteredHere(RegisteredRunner::finalizeConfig);
1442 RunRegisteredHere(RegisteredRunner::claimMemoryNeeds);
1443 RunRegisteredHere(RegisteredRunner::useConfig);
1444 enter_suid();
1445
1446 if (!opt_no_daemon && Config.workers > 0)
1447 watch_child(argv);
1448
1449 if (opt_create_swap_dirs) {
1450 /* chroot if configured to run inside chroot */
1451 mainSetCwd();
1452
1453 setEffectiveUser();
1454 debugs(0, DBG_CRITICAL, "Creating missing swap directories");
1455 Store::Root().create();
1456
1457 return 0;
1458 }
1459
1460 if (IamPrimaryProcess())
1461 CpuAffinityCheck();
1462 CpuAffinityInit();
1463
1464 setMaxFD();
1465
1466 /* init comm module */
1467 comm_init();
1468
1469 if (opt_no_daemon) {
1470 /* we have to init fdstat here. */
1471 fd_open(0, FD_LOG, "stdin");
1472 fd_open(1, FD_LOG, "stdout");
1473 fd_open(2, FD_LOG, "stderr");
1474 }
1475
1476 #if USE_WIN32_SERVICE
1477
1478 WIN32_svcstatusupdate(SERVICE_START_PENDING, 10000);
1479
1480 #endif
1481
1482 mainInitialize();
1483
1484 #if USE_WIN32_SERVICE
1485
1486 WIN32_svcstatusupdate(SERVICE_RUNNING, 0);
1487
1488 #endif
1489
1490 /* main loop */
1491 EventLoop mainLoop;
1492
1493 SignalEngine signalEngine;
1494
1495 mainLoop.registerEngine(&signalEngine);
1496
1497 /* TODO: stop requiring the singleton here */
1498 mainLoop.registerEngine(EventScheduler::GetInstance());
1499
1500 StoreRootEngine store_engine;
1501
1502 mainLoop.registerEngine(&store_engine);
1503
1504 CommSelectEngine comm_engine;
1505
1506 mainLoop.registerEngine(&comm_engine);
1507
1508 mainLoop.setPrimaryEngine(&comm_engine);
1509
1510 /* use the standard time service */
1511 TimeEngine time_engine;
1512
1513 mainLoop.setTimeService(&time_engine);
1514
1515 if (IamCoordinatorProcess())
1516 AsyncJob::Start(Ipc::Coordinator::Instance());
1517 else if (UsingSmp() && (IamWorkerProcess() || IamDiskProcess()))
1518 AsyncJob::Start(new Ipc::Strand);
1519
1520 /* at this point we are finished the synchronous startup. */
1521 starting_up = 0;
1522
1523 mainLoop.run();
1524
1525 if (mainLoop.errcount == 10)
1526 fatal_dump("Event loop exited with failure.");
1527
1528 /* shutdown squid now */
1529 SquidShutdown();
1530
1531 /* NOTREACHED */
1532 return 0;
1533 }
1534
1535 static void
1536 sendSignal(void)
1537 {
1538 pid_t pid;
1539 debug_log = stderr;
1540
1541 if (strcmp(Config.pidFilename, "none") == 0) {
1542 debugs(0, DBG_IMPORTANT, "No pid_filename specified. Trusting you know what you are doing.");
1543 }
1544
1545 pid = readPidFile();
1546
1547 if (pid > 1) {
1548 #if USE_WIN32_SERVICE
1549 if (opt_signal_service) {
1550 WIN32_sendSignal(opt_send_signal);
1551 exit(0);
1552 } else {
1553 fprintf(stderr, "%s: ERROR: Could not send ", APP_SHORTNAME);
1554 fprintf(stderr, "signal to Squid Service:\n");
1555 fprintf(stderr, "missing -n command line switch.\n");
1556 exit(1);
1557 }
1558 /* NOTREACHED */
1559 #endif
1560
1561 if (kill(pid, opt_send_signal) &&
1562 /* ignore permissions if just running check */
1563 !(opt_send_signal == 0 && errno == EPERM)) {
1564 fprintf(stderr, "%s: ERROR: Could not send ", APP_SHORTNAME);
1565 fprintf(stderr, "signal %d to process %d: %s\n",
1566 opt_send_signal, (int) pid, xstrerror());
1567 exit(1);
1568 }
1569 } else {
1570 if (opt_send_signal != SIGTERM) {
1571 fprintf(stderr, "%s: ERROR: No running copy\n", APP_SHORTNAME);
1572 exit(1);
1573 } else {
1574 fprintf(stderr, "%s: No running copy\n", APP_SHORTNAME);
1575 exit(0);
1576 }
1577 }
1578
1579 /* signal successfully sent */
1580 exit(0);
1581 }
1582
1583 #if !_SQUID_WINDOWS_
1584 /*
1585 * This function is run when Squid is in daemon mode, just
1586 * before the parent forks and starts up the child process.
1587 * It can be used for admin-specific tasks, such as notifying
1588 * someone that Squid is (re)started.
1589 */
1590 static void
1591 mainStartScript(const char *prog)
1592 {
1593 char script[MAXPATHLEN];
1594 char *t;
1595 size_t sl = 0;
1596 pid_t cpid;
1597 pid_t rpid;
1598 xstrncpy(script, prog, MAXPATHLEN);
1599
1600 if ((t = strrchr(script, '/'))) {
1601 *(++t) = '\0';
1602 sl = strlen(script);
1603 }
1604
1605 xstrncpy(&script[sl], squid_start_script, MAXPATHLEN - sl);
1606
1607 if ((cpid = fork()) == 0) {
1608 /* child */
1609 execl(script, squid_start_script, (char *)NULL);
1610 _exit(-1);
1611 } else {
1612 do {
1613 #if _SQUID_NEXT_
1614 union wait status;
1615 rpid = wait4(cpid, &status, 0, NULL);
1616 #else
1617
1618 int status;
1619 rpid = waitpid(cpid, &status, 0);
1620 #endif
1621
1622 } while (rpid != cpid);
1623 }
1624 }
1625
1626 #endif /* _SQUID_WINDOWS_ */
1627
1628 static int
1629 checkRunningPid(void)
1630 {
1631 // master process must start alone, but its kids processes may co-exist
1632 if (!IamMasterProcess())
1633 return 0;
1634
1635 pid_t pid;
1636
1637 if (!debug_log)
1638 debug_log = stderr;
1639
1640 pid = readPidFile();
1641
1642 if (pid < 2)
1643 return 0;
1644
1645 if (kill(pid, 0) < 0)
1646 return 0;
1647
1648 debugs(0, DBG_CRITICAL, "Squid is already running! Process ID " << pid);
1649
1650 return 1;
1651 }
1652
1653 static void
1654 watch_child(char *argv[])
1655 {
1656 #if !_SQUID_WINDOWS_
1657 char *prog;
1658 #if _SQUID_NEXT_
1659
1660 union wait status;
1661 #else
1662
1663 int status;
1664 #endif
1665
1666 pid_t pid;
1667 #ifdef TIOCNOTTY
1668
1669 int i;
1670 #endif
1671
1672 int nullfd;
1673
1674 if (!IamMasterProcess())
1675 return;
1676
1677 openlog(APP_SHORTNAME, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4);
1678
1679 if ((pid = fork()) < 0)
1680 syslog(LOG_ALERT, "fork failed: %s", xstrerror());
1681 else if (pid > 0)
1682 exit(0);
1683
1684 if (setsid() < 0)
1685 syslog(LOG_ALERT, "setsid failed: %s", xstrerror());
1686
1687 closelog();
1688
1689 #ifdef TIOCNOTTY
1690
1691 if ((i = open("/dev/tty", O_RDWR | O_TEXT)) >= 0) {
1692 ioctl(i, TIOCNOTTY, NULL);
1693 close(i);
1694 }
1695
1696 #endif
1697
1698 /*
1699 * RBCOLLINS - if cygwin stackdumps when squid is run without
1700 * -N, check the cygwin1.dll version, it needs to be AT LEAST
1701 * 1.1.3. execvp had a bit overflow error in a loop..
1702 */
1703 /* Connect stdio to /dev/null in daemon mode */
1704 nullfd = open(_PATH_DEVNULL, O_RDWR | O_TEXT);
1705
1706 if (nullfd < 0)
1707 fatalf(_PATH_DEVNULL " %s\n", xstrerror());
1708
1709 dup2(nullfd, 0);
1710
1711 if (Debug::log_stderr < 0) {
1712 dup2(nullfd, 1);
1713 dup2(nullfd, 2);
1714 }
1715
1716 // handle shutdown notifications from kids
1717 squid_signal(SIGUSR1, sig_shutdown, SA_RESTART);
1718
1719 if (Config.workers > 128) {
1720 syslog(LOG_ALERT, "Suspiciously high workers value: %d",
1721 Config.workers);
1722 // but we keep going in hope that user knows best
1723 }
1724 TheKids.init();
1725
1726 syslog(LOG_NOTICE, "Squid Parent: will start %d kids", (int)TheKids.count());
1727
1728 // keep [re]starting kids until it is time to quit
1729 for (;;) {
1730 mainStartScript(argv[0]);
1731
1732 // start each kid that needs to be [re]started; once
1733 for (int i = TheKids.count() - 1; i >= 0; --i) {
1734 Kid& kid = TheKids.get(i);
1735 if (!kid.shouldRestart())
1736 continue;
1737
1738 if ((pid = fork()) == 0) {
1739 /* child */
1740 openlog(APP_SHORTNAME, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4);
1741 prog = argv[0];
1742 argv[0] = const_cast<char*>(kid.name().termedBuf());
1743 execvp(prog, argv);
1744 syslog(LOG_ALERT, "execvp failed: %s", xstrerror());
1745 }
1746
1747 kid.start(pid);
1748 syslog(LOG_NOTICE, "Squid Parent: %s process %d started",
1749 kid.name().termedBuf(), pid);
1750 }
1751
1752 /* parent */
1753 openlog(APP_SHORTNAME, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4);
1754
1755 squid_signal(SIGINT, SIG_IGN, SA_RESTART);
1756
1757 #if _SQUID_NEXT_
1758
1759 pid = wait3(&status, 0, NULL);
1760
1761 #else
1762
1763 pid = waitpid(-1, &status, 0);
1764
1765 #endif
1766 // Loop to collect all stopped kids before we go to sleep below.
1767 do {
1768 Kid* kid = TheKids.find(pid);
1769 if (kid) {
1770 kid->stop(status);
1771 if (kid->calledExit()) {
1772 syslog(LOG_NOTICE,
1773 "Squid Parent: %s process %d exited with status %d",
1774 kid->name().termedBuf(),
1775 kid->getPid(), kid->exitStatus());
1776 } else if (kid->signaled()) {
1777 syslog(LOG_NOTICE,
1778 "Squid Parent: %s process %d exited due to signal %d with status %d",
1779 kid->name().termedBuf(),
1780 kid->getPid(), kid->termSignal(), kid->exitStatus());
1781 } else {
1782 syslog(LOG_NOTICE, "Squid Parent: %s process %d exited",
1783 kid->name().termedBuf(), kid->getPid());
1784 }
1785 if (kid->hopeless()) {
1786 syslog(LOG_NOTICE, "Squid Parent: %s process %d will not"
1787 " be restarted due to repeated, frequent failures",
1788 kid->name().termedBuf(), kid->getPid());
1789 }
1790 } else {
1791 syslog(LOG_NOTICE, "Squid Parent: unknown child process %d exited", pid);
1792 }
1793 #if _SQUID_NEXT_
1794 } while ((pid = wait3(&status, WNOHANG, NULL)) > 0);
1795 #else
1796 }
1797 while ((pid = waitpid(-1, &status, WNOHANG)) > 0);
1798 #endif
1799
1800 if (!TheKids.someRunning() && !TheKids.shouldRestartSome()) {
1801 leave_suid();
1802 // XXX: Master process has no main loop and, hence, should not call
1803 // RegisteredRunner::startShutdown which promises a loop iteration.
1804 RunRegisteredHere(RegisteredRunner::finishShutdown);
1805 enter_suid();
1806
1807 if (TheKids.someSignaled(SIGINT) || TheKids.someSignaled(SIGTERM)) {
1808 syslog(LOG_ALERT, "Exiting due to unexpected forced shutdown");
1809 exit(1);
1810 }
1811
1812 if (TheKids.allHopeless()) {
1813 syslog(LOG_ALERT, "Exiting due to repeated, frequent failures");
1814 exit(1);
1815 }
1816
1817 exit(0);
1818 }
1819
1820 squid_signal(SIGINT, SIG_DFL, SA_RESTART);
1821 sleep(3);
1822 }
1823
1824 /* NOTREACHED */
1825 #endif /* _SQUID_WINDOWS_ */
1826
1827 }
1828
1829 static void
1830 SquidShutdown()
1831 {
1832 /* XXX: This function is called after the main loop has quit, which
1833 * means that no AsyncCalls would be called, including close handlers.
1834 * TODO: We need to close/shut/free everything that needs calls before
1835 * exiting the loop.
1836 */
1837
1838 #if USE_WIN32_SERVICE
1839 WIN32_svcstatusupdate(SERVICE_STOP_PENDING, 10000);
1840 #endif
1841
1842 debugs(1, DBG_IMPORTANT, "Shutting down...");
1843 dnsShutdown();
1844 #if USE_SSL_CRTD
1845 Ssl::Helper::GetInstance()->Shutdown();
1846 #endif
1847 #if USE_OPENSSL
1848 if (Ssl::CertValidationHelper::GetInstance())
1849 Ssl::CertValidationHelper::GetInstance()->Shutdown();
1850 #endif
1851 redirectShutdown();
1852 externalAclShutdown();
1853 icpClosePorts();
1854 #if USE_HTCP
1855 htcpClosePorts();
1856 #endif
1857 #if SQUID_SNMP
1858 snmpClosePorts();
1859 #endif
1860 #if USE_WCCP
1861
1862 wccpConnectionClose();
1863 #endif
1864 #if USE_WCCPv2
1865
1866 wccp2ConnectionClose();
1867 #endif
1868
1869 releaseServerSockets();
1870 commCloseAllSockets();
1871
1872 #if USE_SQUID_ESI
1873 Esi::Clean();
1874 #endif
1875
1876 #if USE_DELAY_POOLS
1877 DelayPools::FreePools();
1878 #endif
1879 #if USE_AUTH
1880 authenticateReset();
1881 #endif
1882 #if USE_WIN32_SERVICE
1883
1884 WIN32_svcstatusupdate(SERVICE_STOP_PENDING, 10000);
1885 #endif
1886
1887 Store::Root().sync(); /* Flush pending object writes/unlinks */
1888
1889 unlinkdClose(); /* after sync/flush. NOP if !USE_UNLINKD */
1890
1891 storeDirWriteCleanLogs(0);
1892 PrintRusage();
1893 dumpMallocStats();
1894 Store::Root().sync(); /* Flush log writes */
1895 storeLogClose();
1896 accessLogClose();
1897 Store::Root().sync(); /* Flush log close */
1898 StoreFileSystem::FreeAllFs();
1899 DiskIOModule::FreeAllModules();
1900 #if LEAK_CHECK_MODE && 0 /* doesn't work at the moment */
1901
1902 configFreeMemory();
1903 storeFreeMemory();
1904 /*stmemFreeMemory(); */
1905 netdbFreeMemory();
1906 ipcacheFreeMemory();
1907 fqdncacheFreeMemory();
1908 asnFreeMemory();
1909 clientdbFreeMemory();
1910 httpHeaderCleanModule();
1911 statFreeMemory();
1912 eventFreeMemory();
1913 mimeFreeMemory();
1914 errorClean();
1915 #endif
1916 // clear StoreController
1917 Store::Root(NULL);
1918
1919 fdDumpOpen();
1920
1921 comm_exit();
1922
1923 RunRegisteredHere(RegisteredRunner::finishShutdown);
1924
1925 memClean();
1926
1927 if (IamPrimaryProcess()) {
1928 if (Config.pidFilename && strcmp(Config.pidFilename, "none") != 0) {
1929 enter_suid();
1930 safeunlink(Config.pidFilename, 0);
1931 leave_suid();
1932 }
1933 }
1934
1935 debugs(1, DBG_IMPORTANT, "Squid Cache (Version " << version_string << "): Exiting normally.");
1936
1937 /*
1938 * DPW 2006-10-23
1939 * We used to fclose(debug_log) here if it was set, but then
1940 * we forgot to set it to NULL. That caused some coredumps
1941 * because exit() ends up calling a bunch of destructors and
1942 * such. So rather than forcing the debug_log to close, we'll
1943 * leave it open so that those destructors can write some
1944 * debugging if necessary. The file will be closed anyway when
1945 * the process truly exits.
1946 */
1947
1948 exit(shutdown_status);
1949 }
1950