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