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