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