]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/stored.c
b31b8392f2f4dae28869ac442450891beb77749f
[bacula/bacula] / bacula / src / stored / stored.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Second generation Storage daemon.
30  *
31  *  Kern Sibbald, MM
32  *
33  * It accepts a number of simple commands from the File daemon
34  * and acts on them. When a request to append data is made,
35  * it opens a data channel and accepts data from the
36  * File daemon.
37  *
38  */
39
40 #include "bacula.h"
41 #include "stored.h"
42
43 /* TODO: fix problem with bls, bextract
44  * that use findlib and already declare
45  * filed plugins 
46  */
47 #include "sd_plugins.h"         
48
49 /* Imported functions */
50 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
51
52 /* Forward referenced functions */
53 void terminate_stored(int sig);
54 static int check_resources();
55 static void cleanup_old_files();
56
57 extern "C" void *device_initialization(void *arg);
58
59 #define CONFIG_FILE "bacula-sd.conf"  /* Default config file */
60
61 /* Global variables exported */
62 char OK_msg[]   = "3000 OK\n";
63 char TERM_msg[] = "3999 Terminate\n";
64 STORES *me = NULL;                    /* our Global resource */
65 bool forge_on = false;                /* proceed inspite of I/O errors */
66 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
67 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
68 void *start_heap;
69
70
71 static uint32_t VolSessionId = 0;
72 uint32_t VolSessionTime;
73 char *configfile = NULL;
74 bool init_done = false;
75
76 /* Global static variables */
77 static bool foreground = 0;
78 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
79 static workq_t dird_workq;            /* queue for processing connections */
80 static CONFIG *config;
81
82
83 static void usage()
84 {
85    fprintf(stderr, _(
86 PROG_COPYRIGHT
87 "\nVersion: %s (%s)\n\n"
88 "Usage: bacula-sd [options] [-c config_file] [config_file]\n"
89 "        -c <file>   use <file> as configuration file\n"
90 "        -d <nn>     set debug level to <nn>\n"
91 "        -dt         print timestamp in debug output\n"
92 "        -f          run in foreground (for debugging)\n"
93 "        -g <group>  set groupid to group\n"
94 "        -m          print kaboom output (for debugging)\n"
95 "        -p          proceed despite I/O errors\n"
96 "        -s          no signals (for debugging)\n"
97 "        -t          test - read config and exit\n"
98 "        -u <user>   userid to <user>\n"
99 "        -v          verbose user messages\n"
100 "        -?          print this message.\n"
101 "\n"), 2000, VERSION, BDATE);
102
103    exit(1);
104 }
105
106 /*********************************************************************
107  *
108  *  Main Bacula Unix Storage Daemon
109  *
110  */
111 #if defined(HAVE_WIN32)
112 #define main BaculaMain
113 #endif
114
115 int main (int argc, char *argv[])
116 {
117    int ch;
118    bool no_signals = false;
119    bool test_config = false;
120    pthread_t thid;
121    char *uid = NULL;
122    char *gid = NULL;
123
124    start_heap = sbrk(0);
125    setlocale(LC_ALL, "");
126    bindtextdomain("bacula", LOCALEDIR);
127    textdomain("bacula");
128
129    init_stack_dump();
130    my_name_is(argc, argv, "bacula-sd");
131    init_msg(NULL, NULL);
132    daemon_start_time = time(NULL);
133
134    /* Sanity checks */
135    if (TAPE_BSIZE % B_DEV_BSIZE != 0 || TAPE_BSIZE / B_DEV_BSIZE == 0) {
136       Emsg2(M_ABORT, 0, _("Tape block size (%d) not multiple of system size (%d)\n"),
137          TAPE_BSIZE, B_DEV_BSIZE);
138    }
139    if (TAPE_BSIZE != (1 << (ffs(TAPE_BSIZE)-1))) {
140       Emsg1(M_ABORT, 0, _("Tape block size (%d) is not a power of 2\n"), TAPE_BSIZE);
141    }
142
143    while ((ch = getopt(argc, argv, "c:d:fg:mpstu:v?")) != -1) {
144       switch (ch) {
145       case 'c':                    /* configuration file */
146          if (configfile != NULL) {
147             free(configfile);
148          }
149          configfile = bstrdup(optarg);
150          break;
151
152       case 'd':                    /* debug level */
153          if (*optarg == 't') {
154             dbg_timestamp = true;
155          } else {
156             debug_level = atoi(optarg);
157             if (debug_level <= 0) {
158                debug_level = 1;
159             }
160          }
161          break;
162
163       case 'f':                    /* run in foreground */
164          foreground = true;
165          break;
166
167       case 'g':                    /* set group id */
168          gid = optarg;
169          break;
170
171       case 'm':                    /* print kaboom output */
172          prt_kaboom = true;
173          break;
174
175       case 'p':                    /* proceed in spite of I/O errors */
176          forge_on = true;
177          break;
178
179       case 's':                    /* no signals */
180          no_signals = true;
181          break;
182
183       case 't':
184          test_config = true;
185          break;
186
187       case 'u':                    /* set uid */
188          uid = optarg;
189          break;
190
191       case 'v':                    /* verbose */
192          verbose++;
193          break;
194
195       case '?':
196       default:
197          usage();
198          break;
199       }
200    }
201    argc -= optind;
202    argv += optind;
203
204    if (argc) {
205       if (configfile != NULL) {
206          free(configfile);
207       }
208       configfile = bstrdup(*argv);
209       argc--;
210       argv++;
211    }
212    if (argc)
213       usage();
214
215    if (!no_signals) {
216       init_signals(terminate_stored);
217    }
218
219    if (configfile == NULL) {
220       configfile = bstrdup(CONFIG_FILE);
221    }
222
223    config = new_config_parser();
224    parse_sd_config(config, configfile, M_ERROR_TERM);
225
226    if (init_crypto() != 0) {
227       Jmsg((JCR *)NULL, M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
228    }
229
230    if (!check_resources()) {
231       Jmsg((JCR *)NULL, M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
232    }
233
234    init_reservations_lock();
235
236    if (test_config) {
237       terminate_stored(0);
238    }
239
240    my_name_is(0, (char **)NULL, me->hdr.name);     /* Set our real name */
241
242    if (!foreground) {
243       daemon_start();                 /* become daemon */
244       init_stack_dump();              /* pick up new pid */
245    }
246
247    create_pid_file(me->pid_directory, "bacula-sd",
248                    get_first_port_host_order(me->sdaddrs));
249    read_state_file(me->working_directory, "bacula-sd", 
250                    get_first_port_host_order(me->sdaddrs));
251
252    /* Make sure on Solaris we can run concurrent, watch dog + servers + misc */
253    set_thread_concurrency(me->max_concurrent_jobs * 2 + 4);
254    lmgr_init_thread(); /* initialize the lockmanager stack */
255
256    load_sd_plugins(me->plugin_directory);
257
258    drop(uid, gid, false);
259
260    cleanup_old_files();
261
262    /* Ensure that Volume Session Time and Id are both
263     * set and are both non-zero.
264     */
265    VolSessionTime = (uint32_t)daemon_start_time;
266    if (VolSessionTime == 0) { /* paranoid */
267       Jmsg0(NULL, M_ABORT, 0, _("Volume Session Time is ZERO!\n"));
268    }
269
270    /*
271     * Start the device allocation thread
272     */
273    create_volume_lists();             /* do before device_init */
274    if (pthread_create(&thid, NULL, device_initialization, NULL) != 0) {
275       berrno be;
276       Emsg1(M_ABORT, 0, _("Unable to create thread. ERR=%s\n"), be.bstrerror());
277    }
278
279    start_watchdog();                  /* start watchdog thread */
280    init_jcr_subsystem();              /* start JCR watchdogs etc. */
281
282    /* Single server used for Director and File daemon */
283    bnet_thread_server(me->sdaddrs, me->max_concurrent_jobs * 2 + 1,
284                       &dird_workq, handle_connection_request);
285    exit(1);                           /* to keep compiler quiet */
286 }
287
288 /* Return a new Session Id */
289 uint32_t newVolSessionId()
290 {
291    uint32_t Id;
292
293    P(mutex);
294    VolSessionId++;
295    Id = VolSessionId;
296    V(mutex);
297    return Id;
298 }
299
300 /* Check Configuration file for necessary info */
301 static int check_resources()
302 {
303    bool OK = true;
304    bool tls_needed;
305
306
307    me = (STORES *)GetNextRes(R_STORAGE, NULL);
308    if (!me) {
309       Jmsg1(NULL, M_ERROR, 0, _("No Storage resource defined in %s. Cannot continue.\n"),
310          configfile);
311       OK = false;
312    }
313
314    if (GetNextRes(R_STORAGE, (RES *)me) != NULL) {
315       Jmsg1(NULL, M_ERROR, 0, _("Only one Storage resource permitted in %s\n"),
316          configfile);
317       OK = false;
318    }
319    if (GetNextRes(R_DIRECTOR, NULL) == NULL) {
320       Jmsg1(NULL, M_ERROR, 0, _("No Director resource defined in %s. Cannot continue.\n"),
321          configfile);
322       OK = false;
323    }
324    if (GetNextRes(R_DEVICE, NULL) == NULL){
325       Jmsg1(NULL, M_ERROR, 0, _("No Device resource defined in %s. Cannot continue.\n"),
326            configfile);
327       OK = false;
328    }
329
330    if (!me->messages) {
331       me->messages = (MSGS *)GetNextRes(R_MSGS, NULL);
332       if (!me->messages) {
333          Jmsg1(NULL, M_ERROR, 0, _("No Messages resource defined in %s. Cannot continue.\n"),
334             configfile);
335          OK = false;
336       }
337    }
338
339    if (!me->working_directory) {
340       Jmsg1(NULL, M_ERROR, 0, _("No Working Directory defined in %s. Cannot continue.\n"),
341          configfile);
342       OK = false;
343    }
344
345    DIRRES *director;
346    STORES *store;
347    foreach_res(store, R_STORAGE) { 
348       /* tls_require implies tls_enable */
349       if (store->tls_require) {
350          if (have_tls) {
351             store->tls_enable = true;
352          } else {
353             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
354             OK = false;
355             continue;
356          }
357       }
358
359       tls_needed = store->tls_enable || store->tls_authenticate;
360
361       if (!store->tls_certfile && tls_needed) {
362          Jmsg(NULL, M_FATAL, 0, _("\"TLS Certificate\" file not defined for Storage \"%s\" in %s.\n"),
363               store->hdr.name, configfile);
364          OK = false;
365       }
366
367       if (!store->tls_keyfile && tls_needed) {
368          Jmsg(NULL, M_FATAL, 0, _("\"TLS Key\" file not defined for Storage \"%s\" in %s.\n"),
369               store->hdr.name, configfile);
370          OK = false;
371       }
372
373       if ((!store->tls_ca_certfile && !store->tls_ca_certdir) && tls_needed && store->tls_verify_peer) {
374          Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\""
375               " or \"TLS CA Certificate Dir\" are defined for Storage \"%s\" in %s."
376               " At least one CA certificate store is required"
377               " when using \"TLS Verify Peer\".\n"),
378               store->hdr.name, configfile);
379          OK = false;
380       }
381
382       /* If everything is well, attempt to initialize our per-resource TLS context */
383       if (OK && (tls_needed || store->tls_require)) {
384          /* Initialize TLS context:
385           * Args: CA certfile, CA certdir, Certfile, Keyfile,
386           * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
387          store->tls_ctx = new_tls_context(store->tls_ca_certfile,
388             store->tls_ca_certdir, store->tls_certfile,
389             store->tls_keyfile, NULL, NULL, store->tls_dhfile,
390             store->tls_verify_peer);
391
392          if (!store->tls_ctx) { 
393             Jmsg(NULL, M_FATAL, 0, _("Failed to initialize TLS context for Storage \"%s\" in %s.\n"),
394                  store->hdr.name, configfile);
395             OK = false;
396          }
397       }
398    }
399
400    foreach_res(director, R_DIRECTOR) { 
401       /* tls_require implies tls_enable */
402       if (director->tls_require) {
403          director->tls_enable = true;
404       }
405
406       tls_needed = director->tls_enable || director->tls_authenticate;
407
408       if (!director->tls_certfile && tls_needed) {
409          Jmsg(NULL, M_FATAL, 0, _("\"TLS Certificate\" file not defined for Director \"%s\" in %s.\n"),
410               director->hdr.name, configfile);
411          OK = false;
412       }
413
414       if (!director->tls_keyfile && tls_needed) {
415          Jmsg(NULL, M_FATAL, 0, _("\"TLS Key\" file not defined for Director \"%s\" in %s.\n"),
416               director->hdr.name, configfile);
417          OK = false;
418       }
419
420       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed && director->tls_verify_peer) {
421          Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\""
422               " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
423               " At least one CA certificate store is required"
424               " when using \"TLS Verify Peer\".\n"),
425               director->hdr.name, configfile);
426          OK = false;
427       }
428
429       /* If everything is well, attempt to initialize our per-resource TLS context */
430       if (OK && (tls_needed || director->tls_require)) {
431          /* Initialize TLS context:
432           * Args: CA certfile, CA certdir, Certfile, Keyfile,
433           * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
434          director->tls_ctx = new_tls_context(director->tls_ca_certfile,
435             director->tls_ca_certdir, director->tls_certfile,
436             director->tls_keyfile, NULL, NULL, director->tls_dhfile,
437             director->tls_verify_peer);
438
439          if (!director->tls_ctx) { 
440             Jmsg(NULL, M_FATAL, 0, _("Failed to initialize TLS context for Director \"%s\" in %s.\n"),
441                  director->hdr.name, configfile);
442             OK = false;
443          }
444       }
445    }
446
447    OK = init_autochangers();
448
449    
450    if (OK) {
451       close_msg(NULL);                   /* close temp message handler */
452       init_msg(NULL, me->messages);      /* open daemon message handler */
453       set_working_directory(me->working_directory);
454    }
455
456    return OK;
457 }
458
459 /*
460  * Remove old .spool files written by me from the working directory.
461  */
462 static void cleanup_old_files()
463 {
464    DIR* dp;
465    struct dirent *entry, *result;
466    int rc, name_max;
467    int my_name_len = strlen(my_name);
468    int len = strlen(me->working_directory);
469    POOLMEM *cleanup = get_pool_memory(PM_MESSAGE);
470    POOLMEM *basename = get_pool_memory(PM_MESSAGE);
471    regex_t preg1;
472    char prbuf[500];
473    const int nmatch = 30;
474    regmatch_t pmatch[nmatch];
475    berrno be;
476
477    /* Look for .spool files but don't allow spaces */
478    const char *pat1 = "^[^ ]+\\.spool$";
479
480    /* Setup working directory prefix */
481    pm_strcpy(basename, me->working_directory);
482    if (len > 0 && !IsPathSeparator(me->working_directory[len-1])) {
483       pm_strcat(basename, "/");
484    }
485
486    /* Compile regex expressions */
487    rc = regcomp(&preg1, pat1, REG_EXTENDED);
488    if (rc != 0) {
489       regerror(rc, &preg1, prbuf, sizeof(prbuf));
490       Pmsg2(000,  _("Could not compile regex pattern \"%s\" ERR=%s\n"),
491            pat1, prbuf);
492       goto get_out2;
493    }
494
495    name_max = pathconf(".", _PC_NAME_MAX);
496    if (name_max < 1024) {
497       name_max = 1024;
498    }
499       
500    if (!(dp = opendir(me->working_directory))) {
501       berrno be;
502       Pmsg2(000, "Failed to open working dir %s for cleanup: ERR=%s\n", 
503             me->working_directory, be.bstrerror());
504       goto get_out1;
505    }
506
507    entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
508    while (1) {
509       if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
510          break;
511       }
512       /* Exclude any name with ., .., not my_name or containing a space */
513       if (strcmp(result->d_name, ".") == 0 || strcmp(result->d_name, "..") == 0 ||
514           strncmp(result->d_name, my_name, my_name_len) != 0) {
515          Dmsg1(500, "Skipped: %s\n", result->d_name);
516          continue;    
517       }
518
519       /* Unlink files that match regex */
520       if (regexec(&preg1, result->d_name, nmatch, pmatch,  0) == 0) {
521          pm_strcpy(cleanup, basename);
522          pm_strcat(cleanup, result->d_name);
523          Dmsg1(500, "Unlink: %s\n", cleanup);
524          unlink(cleanup);
525       }
526    }
527    free(entry);
528    closedir(dp);
529
530 get_out1:
531    regfree(&preg1);
532 get_out2:
533    free_pool_memory(cleanup);
534    free_pool_memory(basename);
535 }
536
537
538 /*
539  * Here we attempt to init and open each device. This is done
540  *  once at startup in a separate thread.
541  */
542 extern "C"
543 void *device_initialization(void *arg)
544 {
545    DEVRES *device;
546    DCR *dcr;
547    JCR *jcr;
548    DEVICE *dev;
549
550    LockRes();
551
552    pthread_detach(pthread_self());
553    jcr = new_jcr(sizeof(JCR), stored_free_jcr);
554    jcr->setJobType(JT_SYSTEM);
555    /* Initialize FD start condition variable */
556    int errstat = pthread_cond_init(&jcr->job_start_wait, NULL);
557    if (errstat != 0) {
558       berrno be;
559       Jmsg1(jcr, M_ABORT, 0, _("Unable to init job cond variable: ERR=%s\n"), be.bstrerror(errstat));
560    }
561
562    foreach_res(device, R_DEVICE) {
563       Dmsg1(90, "calling init_dev %s\n", device->device_name);
564       dev = init_dev(NULL, device);
565       Dmsg1(10, "SD init done %s\n", device->device_name);
566       if (!dev) {
567          Jmsg1(NULL, M_ERROR, 0, _("Could not initialize %s\n"), device->device_name);
568          continue;
569       }
570
571       jcr->dcr = dcr = new_dcr(jcr, NULL, dev);
572       generate_plugin_event(jcr, bsdEventDeviceInit, dcr);
573       if (dev->is_autochanger()) {
574          /* If autochanger set slot in dev sturcture */
575          get_autochanger_loaded_slot(dcr);
576       }
577
578       if (device->cap_bits & CAP_ALWAYSOPEN) {
579          Dmsg1(20, "calling first_open_device %s\n", dev->print_name());
580          if (!first_open_device(dcr)) {
581             Jmsg1(NULL, M_ERROR, 0, _("Could not open device %s\n"), dev->print_name());
582             Dmsg1(20, "Could not open device %s\n", dev->print_name());
583             free_dcr(dcr);
584             jcr->dcr = NULL;
585             continue;
586          }
587       }
588       if (device->cap_bits & CAP_AUTOMOUNT && dev->is_open()) {
589          switch (read_dev_volume_label(dcr)) {
590          case VOL_OK:
591             memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
592             volume_unused(dcr);             /* mark volume "released" */
593             break;
594          default:
595             Jmsg1(NULL, M_WARNING, 0, _("Could not mount device %s\n"), dev->print_name());
596             break;
597          }
598       }
599       free_dcr(dcr);
600       jcr->dcr = NULL;
601    }
602 #ifdef xxx
603    if (jcr->dcr) {
604       Dmsg1(000, "free_dcr=%p\n", jcr->dcr);
605       free_dcr(jcr->dcr);
606       jcr->dcr = NULL;
607    }
608 #endif
609    free_jcr(jcr); 
610    init_done = true;
611    UnlockRes();
612    return NULL;
613 }
614
615
616 /* Clean up and then exit */
617 void terminate_stored(int sig)
618 {
619    static bool in_here = false;
620    DEVRES *device;
621    JCR *jcr;
622
623    if (in_here) {                     /* prevent loops */
624       bmicrosleep(2, 0);              /* yield */
625       exit(1);
626    }
627    in_here = true;
628    debug_level = 0;                   /* turn off any debug */
629    stop_watchdog();
630
631    if (sig == SIGTERM) {              /* normal shutdown request? */
632       /*
633        * This is a normal shutdown request. We wiffle through
634        *   all open jobs canceling them and trying to wake
635        *   them up so that they will report back the correct
636        *   volume status.
637        */
638       foreach_jcr(jcr) {
639          BSOCK *fd;
640          if (jcr->JobId == 0) {
641             free_jcr(jcr);
642             continue;                 /* ignore console */
643          }
644          jcr->setJobStatus(JS_Canceled);
645          fd = jcr->file_bsock;
646          if (fd) {
647             fd->set_timed_out();
648             jcr->my_thread_send_signal(TIMEOUT_SIGNAL);
649             Dmsg1(100, "term_stored killing JobId=%d\n", jcr->JobId);
650             /* ***FIXME*** wiffle through all dcrs */
651             if (jcr->dcr && jcr->dcr->dev && jcr->dcr->dev->blocked()) {
652                pthread_cond_broadcast(&jcr->dcr->dev->wait_next_vol);
653                Dmsg1(100, "JobId=%u broadcast wait_device_release\n", (uint32_t)jcr->JobId);
654                pthread_cond_broadcast(&wait_device_release);
655             }
656             if (jcr->read_dcr && jcr->read_dcr->dev && jcr->read_dcr->dev->blocked()) {
657                pthread_cond_broadcast(&jcr->read_dcr->dev->wait_next_vol);
658                pthread_cond_broadcast(&wait_device_release);
659             }
660             bmicrosleep(0, 50000);
661          }
662          free_jcr(jcr);
663       }
664       bmicrosleep(0, 500000);         /* give them 1/2 sec to clean up */
665    }
666
667    write_state_file(me->working_directory, "bacula-sd", get_first_port_host_order(me->sdaddrs));
668    delete_pid_file(me->pid_directory, "bacula-sd", get_first_port_host_order(me->sdaddrs));
669
670    Dmsg1(200, "In terminate_stored() sig=%d\n", sig);
671
672    unload_plugins();
673    free_volume_lists();
674
675    foreach_res(device, R_DEVICE) {
676       Dmsg1(10, "Term device %s\n", device->device_name);
677       if (device->dev) {
678          device->dev->clear_volhdr();
679          device->dev->term();
680          device->dev = NULL;
681       } else {
682          Dmsg1(10, "No dev structure %s\n", device->device_name);
683       }
684    }
685
686    if (configfile) {
687       free(configfile);
688       configfile = NULL;
689    }
690    if (config) {
691       config->free_resources();
692       free(config);
693       config = NULL;
694    }
695
696    if (debug_level > 10) {
697       print_memory_pool_stats();
698    }
699    term_msg();
700    cleanup_crypto();
701    term_reservations_lock();
702    close_memory_pool();
703    lmgr_cleanup_main();
704
705    sm_dump(false);                    /* dump orphaned buffers */
706    exit(sig);
707 }