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