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