]> git.ipfire.org Git - thirdparty/squid.git/blob - src/main.cc
2.1 branch merge
[thirdparty/squid.git] / src / main.cc
1
2 /*
3 * $Id: main.cc,v 1.276 1998/11/12 06:28:14 wessels Exp $
4 *
5 * DEBUG: section 1 Startup and Main Loop
6 * AUTHOR: Harvest Derived
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * 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
38 /* for error reporting from xmalloc and friends */
39 extern void (*failure_notify) (const char *);
40
41 static int opt_send_signal = -1;
42 static int opt_no_daemon = 0;
43 static int httpPortNumOverride = 1;
44 static int icpPortNumOverride = 1; /* Want to detect "-u 0" */
45 #if MALLOC_DBG
46 static int malloc_debug_level = 0;
47 #endif
48 static volatile int do_reconfigure = 0;
49 static volatile int do_rotate = 0;
50 static volatile int do_shutdown = 0;
51
52 static void mainRotate(void);
53 static void mainReconfigure(void);
54 static SIGHDLR rotate_logs;
55 static SIGHDLR reconfigure;
56 #if ALARM_UPDATES_TIME
57 static SIGHDLR time_tick;
58 #endif
59 static void mainInitialize(void);
60 static void usage(void);
61 static void mainParseOptions(int, char **);
62 static void sendSignal(void);
63 static void serverConnectionsOpen(void);
64 static void watch_child(char **);
65 static void setEffectiveUser(void);
66 #if MEM_GEN_TRACE
67 extern void log_trace_done();
68 extern void log_trace_init(char *);
69 #endif
70 static EVH SquidShutdown;
71
72 static void
73 usage(void)
74 {
75 fprintf(stderr,
76 "Usage: %s [-dhsvzCDFNRVYX] [-f config-file] [-[au] port] [-k signal]\n"
77 " -a port Specify HTTP port number (default: %d).\n"
78 " -d level Write debugging to stderr also.\n"
79 " -f file Use given config-file instead of\n"
80 " %s\n"
81 " -h Print help message.\n"
82 " -k reconfigure|rotate|shutdown|interrupt|kill|debug|check\n"
83 " Send signal to running copy and exit.\n"
84 " -s Enable logging to syslog.\n"
85 " -u port Specify ICP port number (default: %d), disable with 0.\n"
86 " -v Print version.\n"
87 " -z Create swap directories\n"
88 " -C Do not catch fatal signals.\n"
89 " -D Disable initial DNS tests.\n"
90 " -F Foreground fast store rebuild.\n"
91 " -N No daemon mode.\n"
92 " -R Do not set REUSEADDR on port.\n"
93 " -V Virtual host httpd-accelerator.\n"
94 " -X Force full debugging.\n"
95 " -Y Only return UDP_HIT or UDP_MISS_NOFETCH during fast reload.\n",
96 appname, CACHE_HTTP_PORT, DefaultConfigFile, CACHE_ICP_PORT);
97 exit(1);
98 }
99
100 static void
101 mainParseOptions(int argc, char *argv[])
102 {
103 extern char *optarg;
104 int c;
105
106 while ((c = getopt(argc, argv, "CDFNRSVYXa:d:f:hk:m::su:vz?")) != -1) {
107 switch (c) {
108 case 'C':
109 opt_catch_signals = 0;
110 break;
111 case 'D':
112 opt_dns_tests = 0;
113 break;
114 case 'F':
115 opt_foreground_rebuild = 1;
116 break;
117 case 'N':
118 opt_no_daemon = 1;
119 break;
120 case 'R':
121 opt_reuseaddr = 0;
122 break;
123 case 'S':
124 opt_store_doublecheck = 1;
125 break;
126 case 'V':
127 vhost_mode = 1;
128 break;
129 case 'X':
130 /* force full debugging */
131 sigusr2_handle(SIGUSR2);
132 break;
133 case 'Y':
134 opt_reload_hit_only = 1;
135 break;
136 case 'a':
137 httpPortNumOverride = atoi(optarg);
138 break;
139 case 'd':
140 opt_debug_stderr = atoi(optarg);
141 break;
142 case 'f':
143 xfree(ConfigFile);
144 ConfigFile = xstrdup(optarg);
145 break;
146 case 'h':
147 usage();
148 break;
149 case 'k':
150 if ((int) strlen(optarg) < 1)
151 usage();
152 if (!strncmp(optarg, "reconfigure", strlen(optarg)))
153 opt_send_signal = SIGHUP;
154 else if (!strncmp(optarg, "rotate", strlen(optarg)))
155 #if (defined(_SQUID_LINUX_) && USE_ASYNC_IO)
156 opt_send_signal = SIGQUIT;
157 #else
158 opt_send_signal = SIGUSR1;
159 #endif
160 else if (!strncmp(optarg, "debug", strlen(optarg)))
161 #if (defined(_SQUID_LINUX_) && USE_ASYNC_IO)
162 opt_send_signal = SIGTRAP;
163 #else
164 opt_send_signal = SIGUSR2;
165 #endif
166 else if (!strncmp(optarg, "shutdown", strlen(optarg)))
167 opt_send_signal = SIGTERM;
168 else if (!strncmp(optarg, "interrupt", strlen(optarg)))
169 opt_send_signal = SIGINT;
170 else if (!strncmp(optarg, "kill", strlen(optarg)))
171 opt_send_signal = SIGKILL;
172 else if (!strncmp(optarg, "check", strlen(optarg)))
173 opt_send_signal = 0; /* SIGNULL */
174 else
175 usage();
176 break;
177 case 'm':
178 if (optarg) {
179 #if MALLOC_DBG
180 malloc_debug_level = atoi(optarg);
181 /* NOTREACHED */
182 break;
183 #else
184 fatal("Need to add -DMALLOC_DBG when compiling to use -mX option");
185 /* NOTREACHED */
186 #endif
187 } else {
188 #if XMALLOC_TRACE
189 xmalloc_trace = !xmalloc_trace;
190 #else
191 fatal("Need to configure --enable-xmalloc-debug-trace to use -m option");
192 #endif
193 }
194 case 's':
195 opt_syslog_enable = 1;
196 break;
197 case 'u':
198 icpPortNumOverride = atoi(optarg);
199 if (icpPortNumOverride < 0)
200 icpPortNumOverride = 0;
201 break;
202 case 'v':
203 printf("Squid Cache: Version %s\n", version_string);
204 exit(0);
205 /* NOTREACHED */
206 case 'z':
207 opt_create_swap_dirs = 1;
208 break;
209 case '?':
210 default:
211 usage();
212 break;
213 }
214 }
215 }
216
217 /* ARGSUSED */
218 static void
219 rotate_logs(int sig)
220 {
221 do_rotate = 1;
222 #if !HAVE_SIGACTION
223 signal(sig, rotate_logs);
224 #endif
225 }
226
227 #if ALARM_UPDATES_TIME
228 static void
229 time_tick(int sig)
230 {
231 getCurrentTime();
232 alarm(1);
233 #if !HAVE_SIGACTION
234 signal(sig, time_tick);
235 #endif
236 }
237
238 #endif
239
240 /* ARGSUSED */
241 static void
242 reconfigure(int sig)
243 {
244 do_reconfigure = 1;
245 #if !HAVE_SIGACTION
246 signal(sig, reconfigure);
247 #endif
248 }
249
250 void
251 shut_down(int sig)
252 {
253 do_shutdown = sig == SIGINT ? -1 : 1;
254 #ifdef KILL_PARENT_OPT
255 if (getppid() > 1) {
256 debug(1, 1) ("Killing RunCache, pid %d\n", getppid());
257 kill(getppid(), sig);
258 }
259 #endif
260 #if SA_RESETHAND == 0
261 signal(SIGTERM, SIG_DFL);
262 signal(SIGINT, SIG_DFL);
263 #endif
264 }
265
266 static void
267 serverConnectionsOpen(void)
268 {
269 clientHttpConnectionsOpen();
270 icpConnectionsOpen();
271 #if USE_HTCP
272 htcpInit();
273 #endif
274 #ifdef SQUID_SNMP
275 snmpConnectionOpen();
276 #endif
277 clientdbInit();
278 icmpOpen();
279 netdbInit();
280 asnInit();
281 peerSelectInit();
282 #if USE_CARP
283 carpInit();
284 #endif
285 }
286
287 void
288 serverConnectionsClose(void)
289 {
290 assert(shutting_down || reconfiguring);
291 clientHttpConnectionsClose();
292 icpConnectionShutdown();
293 #if USE_HTCP
294 htcpSocketShutdown();
295 #endif
296 icmpClose();
297 #ifdef SQUID_SNMP
298 snmpConnectionShutdown();
299 #endif
300 asnFreeMemory();
301 }
302
303 static void
304 mainReconfigure(void)
305 {
306 debug(1, 1) ("Restarting Squid Cache (version %s)...\n", version_string);
307 reconfiguring = 1;
308 /* Already called serverConnectionsClose and ipcacheShutdownServers() */
309 serverConnectionsClose();
310 icpConnectionClose();
311 #if USE_HTCP
312 htcpSocketClose();
313 #endif
314 #ifdef SQUID_SNMP
315 snmpConnectionClose();
316 #endif
317 dnsShutdown();
318 redirectShutdown();
319 authenticateShutdown();
320 storeDirCloseSwapLogs();
321 errorClean();
322 parseConfigFile(ConfigFile);
323 _db_init(Config.Log.log, Config.debugOptions);
324 ipcache_restart(); /* clear stuck entries */
325 fqdncache_restart(); /* sigh, fqdncache too */
326 errorInitialize(); /* reload error pages */
327 dnsInit();
328 redirectInit();
329 authenticateInit();
330 serverConnectionsOpen();
331 if (theOutIcpConnection >= 0) {
332 if (!Config2.Accel.on || Config.onoff.accel_with_proxy)
333 neighbors_open(theOutIcpConnection);
334 else
335 debug(1, 1) ("ICP port disabled in httpd_accelerator mode\n");
336 }
337 storeDirOpenSwapLogs();
338 debug(1, 1) ("Ready to serve requests.\n");
339 reconfiguring = 0;
340 }
341
342 static void
343 mainRotate(void)
344 {
345 icmpClose();
346 _db_rotate_log(); /* cache.log */
347 storeDirWriteCleanLogs(1);
348 storeLogRotate(); /* store.log */
349 accessLogRotate(); /* access.log */
350 useragentRotateLog(); /* useragent.log */
351 icmpOpen();
352 }
353
354 static void
355 setEffectiveUser(void)
356 {
357 leave_suid(); /* Run as non privilegied user */
358 if (geteuid() == 0) {
359 debug(0, 0) ("Squid is not safe to run as root! If you must\n");
360 debug(0, 0) ("start Squid as root, then you must configure\n");
361 debug(0, 0) ("it to run as a non-priveledged user with the\n");
362 debug(0, 0) ("'cache_effective_user' option in the config file.\n");
363 fatal("Don't run Squid as root, set 'cache_effective_user'!");
364 }
365 }
366
367 static void
368 mainInitialize(void)
369 {
370 if (opt_catch_signals) {
371 squid_signal(SIGSEGV, death, SA_NODEFER | SA_RESETHAND);
372 squid_signal(SIGBUS, death, SA_NODEFER | SA_RESETHAND);
373 }
374 squid_signal(SIGPIPE, SIG_IGN, SA_RESTART);
375 squid_signal(SIGCHLD, sig_child, SA_NODEFER | SA_RESTART);
376
377 if (!configured_once) {
378 cbdataInit();
379 memInit(); /* memInit must go before config parsing */
380 }
381 if (ConfigFile == NULL)
382 ConfigFile = xstrdup(DefaultConfigFile);
383 parseConfigFile(ConfigFile);
384
385 setEffectiveUser();
386 assert(Config.Port.http);
387 if (httpPortNumOverride != 1)
388 Config.Port.http->i = (u_short) httpPortNumOverride;
389 if (icpPortNumOverride != 1)
390 Config.Port.icp = (u_short) icpPortNumOverride;
391
392 _db_init(Config.Log.log, Config.debugOptions);
393 fd_open(fileno(debug_log), FD_LOG, Config.Log.log);
394 #if MEM_GEN_TRACE
395 log_trace_init("/tmp/squid.alloc");
396 #endif
397 debug(1, 0) ("Starting Squid Cache version %s for %s...\n",
398 version_string,
399 CONFIG_HOST_TYPE);
400 debug(1, 1) ("Process ID %d\n", (int) getpid());
401 debug(1, 1) ("With %d file descriptors available\n", Squid_MaxFD);
402
403 if (!configured_once)
404 disk_init(); /* disk_init must go before ipcache_init() */
405 ipcache_init();
406 fqdncache_init();
407 dnsInit();
408 redirectInit();
409 authenticateInit();
410 useragentOpenLog();
411 httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */
412 httpAnonInitModule(); /* must go before accepting requests */
413 httpReplyInitModule(); /* must go before accepting replies */
414 errorInitialize();
415 accessLogInit();
416 #ifdef SQUID_SNMP
417 snmpInit();
418 #endif
419
420 #if MALLOC_DBG
421 malloc_debug(0, malloc_debug_level);
422 #endif
423
424 if (!configured_once) {
425 unlinkdInit();
426 urlInitialize();
427 cachemgrInit();
428 statInit();
429 storeInit();
430 if (Config.effectiveUser) {
431 /* we were probably started as root, so cd to a swap
432 * directory in case we dump core */
433 if (chdir(storeSwapDir(0)) < 0) {
434 debug(50, 0) ("%s: %s\n", storeSwapDir(0), xstrerror());
435 fatal_dump("Cannot cd to swap directory?");
436 }
437 }
438 /* after this point we want to see the mallinfo() output */
439 do_mallinfo = 1;
440 mimeInit(Config.mimeTablePathname);
441 pconnInit();
442 eventInit();
443 refreshInit();
444 #if DELAY_POOLS
445 delayPoolsInit();
446 #endif
447 }
448 serverConnectionsOpen();
449 if (theOutIcpConnection >= 0) {
450 if (!Config2.Accel.on || Config.onoff.accel_with_proxy)
451 neighbors_open(theOutIcpConnection);
452 else
453 debug(1, 1) ("ICP port disabled in httpd_accelerator mode\n");
454 }
455 if (!configured_once)
456 writePidFile(); /* write PID file */
457
458 #if (defined(_SQUID_LINUX_) && USE_ASYNC_IO)
459 squid_signal(SIGQUIT, rotate_logs, SA_RESTART);
460 squid_signal(SIGTRAP, sigusr2_handle, SA_RESTART);
461 #else
462 squid_signal(SIGUSR1, rotate_logs, SA_RESTART);
463 squid_signal(SIGUSR2, sigusr2_handle, SA_RESTART);
464 #endif
465 squid_signal(SIGHUP, reconfigure, SA_RESTART);
466 squid_signal(SIGTERM, shut_down, SA_NODEFER | SA_RESETHAND | SA_RESTART);
467 squid_signal(SIGINT, shut_down, SA_NODEFER | SA_RESETHAND | SA_RESTART);
468 #if ALARM_UPDATES_TIME
469 squid_signal(SIGALRM, time_tick, SA_RESTART);
470 alarm(1);
471 #endif
472 debug(1, 1) ("Ready to serve requests.\n");
473
474 if (!configured_once) {
475 eventAdd("storeMaintain", storeMaintainSwapSpace, NULL, 1.0, 1);
476 eventAdd("storeDirClean", storeDirClean, NULL, 15.0, 1);
477 if (Config.onoff.announce)
478 eventAdd("start_announce", start_announce, NULL, 3600.0, 1);
479 eventAdd("ipcache_purgelru", ipcache_purgelru, NULL, 10.0, 1);
480 eventAdd("fqdncache_purgelru", fqdncache_purgelru, NULL, 15.0, 1);
481 }
482 configured_once = 1;
483 }
484
485 int
486 main(int argc, char **argv)
487 {
488 int errcount = 0;
489 int n; /* # of GC'd objects */
490 time_t loop_delay;
491
492 debug_log = stderr;
493 if (FD_SETSIZE < Squid_MaxFD)
494 Squid_MaxFD = FD_SETSIZE;
495
496 /* call mallopt() before anything else */
497 #if HAVE_MALLOPT
498 #ifdef M_GRAIN
499 /* Round up all sizes to a multiple of this */
500 mallopt(M_GRAIN, 16);
501 #endif
502 #ifdef M_MXFAST
503 /* biggest size that is considered a small block */
504 mallopt(M_MXFAST, 256);
505 #endif
506 #ifdef M_NBLKS
507 /* allocate this many small blocks at once */
508 mallopt(M_NLBLKS, 32);
509 #endif
510 #endif /* HAVE_MALLOPT */
511
512 memset(&local_addr, '\0', sizeof(struct in_addr));
513 safe_inet_addr(localhost, &local_addr);
514 memset(&any_addr, '\0', sizeof(struct in_addr));
515 safe_inet_addr("0.0.0.0", &any_addr);
516 memset(&no_addr, '\0', sizeof(struct in_addr));
517 safe_inet_addr("255.255.255.255", &no_addr);
518 squid_srandom(time(NULL));
519
520 getCurrentTime();
521 squid_start = current_time;
522 failure_notify = fatal_dump;
523
524 mainParseOptions(argc, argv);
525
526 /* send signal to running copy and exit */
527 if (opt_send_signal != -1) {
528 sendSignal();
529 /* NOTREACHED */
530 }
531 if (opt_create_swap_dirs) {
532 if (ConfigFile == NULL)
533 ConfigFile = xstrdup(DefaultConfigFile);
534 cbdataInit();
535 memInit(); /* memInit is required for config parsing */
536 parseConfigFile(ConfigFile);
537 setEffectiveUser();
538 debug(0, 0) ("Creating Swap Directories\n");
539 storeCreateSwapDirectories();
540 return 0;
541 }
542 if (!opt_no_daemon)
543 watch_child(argv);
544 setMaxFD();
545
546 if (opt_catch_signals)
547 for (n = Squid_MaxFD; n > 2; n--)
548 close(n);
549
550 /*init comm module */
551 comm_init();
552 comm_select_init();
553
554 /* we have to init fdstat here. */
555 fd_open(0, FD_LOG, "stdin");
556 fd_open(1, FD_LOG, "stdout");
557 fd_open(2, FD_LOG, "stderr");
558
559 mainInitialize();
560
561 /* main loop */
562 for (;;) {
563 if (do_reconfigure) {
564 mainReconfigure();
565 do_reconfigure = 0;
566 } else if (do_rotate) {
567 mainRotate();
568 do_rotate = 0;
569 } else if (do_shutdown) {
570 time_t wait = do_shutdown > 0 ? (int) Config.shutdownLifetime : 0;
571 debug(1, 1) ("Preparing for shutdown after %d requests\n",
572 Counter.client_http.requests);
573 debug(1, 1) ("Waiting %d seconds for active connections to finish\n",
574 wait);
575 do_shutdown = 0;
576 shutting_down = 1;
577 serverConnectionsClose();
578 dnsShutdown();
579 redirectShutdown();
580 authenticateShutdown();
581 eventAdd("SquidShutdown", SquidShutdown, NULL, (double) (wait + 1), 1);
582 }
583 eventRun();
584 if ((loop_delay = eventNextTime()) < 0)
585 loop_delay = 0;
586 #if HAVE_POLL
587 switch (comm_poll(loop_delay)) {
588 #else
589 switch (comm_select(loop_delay)) {
590 #endif
591 case COMM_OK:
592 errcount = 0; /* reset if successful */
593 break;
594 case COMM_ERROR:
595 errcount++;
596 debug(1, 0) ("Select loop Error. Retry %d\n", errcount);
597 if (errcount == 10)
598 fatal_dump("Select Loop failed!");
599 break;
600 case COMM_TIMEOUT:
601 break;
602 case COMM_SHUTDOWN:
603 SquidShutdown(NULL);
604 break;
605 default:
606 fatal_dump("MAIN: Internal error -- this should never happen.");
607 break;
608 }
609 }
610 /* NOTREACHED */
611 return 0;
612 }
613
614 static void
615 sendSignal(void)
616 {
617 pid_t pid;
618 debug_log = stderr;
619 if (ConfigFile == NULL)
620 ConfigFile = xstrdup(DefaultConfigFile);
621 cbdataInit();
622 memInit();
623 parseConfigFile(ConfigFile);
624 pid = readPidFile();
625 if (pid > 1) {
626 if (kill(pid, opt_send_signal) &&
627 /* ignore permissions if just running check */
628 !(opt_send_signal == 0 && errno == EPERM)) {
629 fprintf(stderr, "%s: ERROR: Could not send ", appname);
630 fprintf(stderr, "signal %d to process %d: %s\n",
631 opt_send_signal, (int) pid, xstrerror());
632 exit(1);
633 }
634 } else {
635 fprintf(stderr, "%s: ERROR: No running copy\n", appname);
636 exit(1);
637 }
638 /* signal successfully sent */
639 exit(0);
640 }
641
642 static void
643 watch_child(char *argv[])
644 {
645 char *prog;
646 int failcount = 0;
647 time_t start;
648 time_t stop;
649 #ifdef _SQUID_NEXT_
650 union wait status;
651 #else
652 int status;
653 #endif
654 pid_t pid;
655 if (*(argv[0]) == '(')
656 return;
657 for (;;) {
658 if (fork() == 0) {
659 /* child */
660 prog = xstrdup(argv[0]);
661 argv[0] = xstrdup("(squid)");
662 execvp(prog, argv);
663 fatal("execvp failed");
664 }
665 /* parent */ time(&start);
666 do {
667 squid_signal(SIGINT, SIG_IGN, SA_RESTART);
668 #ifdef _SQUID_NEXT_
669 pid = wait3(&status, 0, NULL);
670 #else
671 pid = waitpid(-1, &status, 0);
672 #endif
673 } while (pid > 0);
674 time(&stop);
675 if (stop - start < 10)
676 failcount++;
677 else
678 failcount = 0;
679 if (failcount == 5)
680 exit(1);
681 if (WIFEXITED(status))
682 if (WEXITSTATUS(status) == 0)
683 exit(0);
684 squid_signal(SIGINT, SIG_DFL, SA_RESTART);
685 sleep(3);
686 }
687 /* NOTREACHED */
688 }
689
690 static void
691 SquidShutdown(void *unused)
692 {
693 debug(1, 1) ("Shutting down...\n");
694 if (Config.pidFilename && strcmp(Config.pidFilename, "none")) {
695 enter_suid();
696 safeunlink(Config.pidFilename, 0);
697 leave_suid();
698 }
699 icpConnectionClose();
700 #if USE_HTCP
701 htcpSocketClose();
702 #endif
703 #ifdef SQUID_SNMP
704 snmpConnectionClose();
705 #endif
706 releaseServerSockets();
707 commCloseAllSockets();
708 unlinkdClose();
709 storeDirWriteCleanLogs(0);
710 PrintRusage();
711 dumpMallocStats();
712 storeLogClose();
713 accessLogClose();
714 #if PURIFY || XMALLOC_TRACE
715 configFreeMemory();
716 storeFreeMemory();
717 /*stmemFreeMemory(); */
718 netdbFreeMemory();
719 ipcacheFreeMemory();
720 fqdncacheFreeMemory();
721 asnFreeMemory();
722 clientdbFreeMemory();
723 httpHeaderCleanModule();
724 statFreeMemory();
725 eventFreeMemory();
726 mimeFreeMemory();
727 errorClean();
728 #endif
729 memClean();
730 #if !XMALLOC_TRACE
731 file_close(0);
732 file_close(1);
733 file_close(2);
734 #endif
735 fdDumpOpen();
736 fdFreeMemory();
737 #if XMALLOC_TRACE
738 xmalloc_find_leaks();
739 debug(1, 0) ("Memory used after shutdown: %d\n", xmalloc_total);
740 #endif
741 #if MEM_GEN_TRACE
742 log_trace_done();
743 #endif
744 debug(1, 1) ("Squid Cache (Version %s): Exiting normally.\n",
745 version_string);
746 fclose(debug_log);
747 exit(0);
748 }