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