1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2021 Bareos GmbH & Co. KG
7 
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    Affero 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 /*
24  * Kern Sibbald, MM
25  */
26 /**
27  * @file
28  * Second generation Storage daemon.
29  *
30  * It accepts a number of simple commands from the File daemon
31  * and acts on them. When a request to append data is made,
32  * it opens a data channel and accepts data from the
33  * File daemon.
34  */
35 
36 #include "include/bareos.h"
37 #include "stored/stored.h"
38 #include "lib/crypto_cache.h"
39 #include "stored/acquire.h"
40 #include "stored/autochanger.h"
41 #include "stored/bsr.h"
42 #include "stored/device.h"
43 #include "stored/jcr_private.h"
44 #include "stored/job.h"
45 #include "stored/label.h"
46 #include "stored/ndmp_tape.h"
47 #include "stored/sd_backends.h"
48 #include "stored/sd_device_control_record.h"
49 #include "stored/sd_stats.h"
50 #include "stored/socket_server.h"
51 #include "stored/stored_globals.h"
52 #include "stored/wait.h"
53 #include "lib/berrno.h"
54 #include "lib/bsock.h"
55 #include "lib/bnet_network_dump.h"
56 #include "lib/daemon.h"
57 #include "lib/bsignal.h"
58 #include "lib/parse_conf.h"
59 #include "lib/thread_specific_data.h"
60 #include "lib/util.h"
61 #include "lib/watchdog.h"
62 #include "include/jcr.h"
63 
64 
65 namespace storagedaemon {
66 extern bool ParseSdConfig(const char* configfile, int exit_code);
67 }
68 
69 using namespace storagedaemon;
70 
71 /* Imported functions */
72 extern bool PrintMessage(void* sock, const char* fmt, ...);
73 
74 /* Forward referenced functions */
75 namespace storagedaemon {
76 #if !defined(HAVE_WIN32)
77 static
78 #endif
79     void
80     TerminateStored(int sig);
81 }  // namespace storagedaemon
82 static int CheckResources();
83 static void CleanUpOldFiles();
84 
85 extern "C" void* device_initialization(void* arg);
86 
87 /* Global static variables */
88 static bool foreground = 0;
89 
usage()90 static void usage()
91 {
92   kBareosVersionStrings.PrintCopyrightWithFsfAndPlanets(stderr, 2000);
93   fprintf(
94       stderr,
95       _("Usage: bareos-sd [options]\n"
96         "        -c <path>   use <path> as configuration file or directory\n"
97         "        -d <nn>     set debug level to <nn>\n"
98         "        -dt         print timestamp in debug output\n"
99         "        -f          run in foreground (for debugging)\n"
100         "        -g <group>  run as group <group>\n"
101         "        -m          print kaboom output (for debugging)\n"
102         "        -p          proceed despite I/O errors\n"
103         "        -s          no signals (for debugging)\n"
104         "        -t          test - read configuration and exit\n"
105         "        -u <user>   run as user <user>\n"
106         "        -v          verbose user messages\n"
107         "        -xc         print configuration and exit\n"
108         "        -xs         print configuration file schema in JSON format "
109         "and exit\n"
110         "        -?          print this message.\n"
111         "\n"));
112   exit(1);
113 }
114 
115 /*********************************************************************
116  *
117  *  Main Bareos Storage Daemon
118  *
119  */
120 #if defined(HAVE_WIN32)
121 #  define main BareosMain
122 #endif
123 
main(int argc,char * argv[])124 int main(int argc, char* argv[])
125 {
126   int ch;
127   bool no_signals = false;
128   bool test_config = false;
129   bool export_config = false;
130   bool export_config_schema = false;
131   pthread_t thid;
132   char* uid = NULL;
133   char* gid = NULL;
134 
135   setlocale(LC_ALL, "");
136   tzset();
137   bindtextdomain("bareos", LOCALEDIR);
138   textdomain("bareos");
139 
140   InitStackDump();
141   MyNameIs(argc, argv, "bareos-sd");
142   InitMsg(NULL, NULL);
143   daemon_start_time = time(NULL);
144 
145   /*
146    * Sanity checks
147    */
148   if (TAPE_BSIZE % B_DEV_BSIZE != 0 || TAPE_BSIZE / B_DEV_BSIZE == 0) {
149     Emsg2(M_ABORT, 0,
150           _("Tape block size (%d) not multiple of system size (%d)\n"),
151           TAPE_BSIZE, B_DEV_BSIZE);
152   }
153   if (TAPE_BSIZE != (1 << (ffs(TAPE_BSIZE) - 1))) {
154     Emsg1(M_ABORT, 0, _("Tape block size (%d) is not a power of 2\n"),
155           TAPE_BSIZE);
156   }
157 
158   while ((ch = getopt(argc, argv, "c:d:fg:mpstu:vx:z:?")) != -1) {
159     switch (ch) {
160       case 'c': /* configuration file */
161         if (configfile != NULL) { free(configfile); }
162         configfile = strdup(optarg);
163         break;
164 
165       case 'd': /* debug level */
166         if (*optarg == 't') {
167           dbg_timestamp = true;
168         } else {
169           debug_level = atoi(optarg);
170           if (debug_level <= 0) { debug_level = 1; }
171         }
172         break;
173 
174       case 'f': /* run in foreground */
175         foreground = true;
176         break;
177 
178       case 'g': /* set group id */
179         gid = optarg;
180         break;
181 
182       case 'm': /* print kaboom output */
183         prt_kaboom = true;
184         break;
185 
186       case 'p': /* proceed in spite of I/O errors */
187         forge_on = true;
188         break;
189 
190       case 's': /* no signals */
191         no_signals = true;
192         break;
193 
194       case 't':
195         test_config = true;
196         break;
197 
198       case 'u': /* set uid */
199         uid = optarg;
200         break;
201 
202       case 'v': /* verbose */
203         verbose++;
204         break;
205 
206       case 'x': /* export configuration/schema and exit */
207         if (*optarg == 's') {
208           export_config_schema = true;
209         } else if (*optarg == 'c') {
210           export_config = true;
211         } else {
212           usage();
213         }
214         break;
215 
216       case 'z': /* switch network debugging on */
217         if (!BnetDump::EvaluateCommandLineArgs(optarg)) { exit(1); }
218         break;
219 
220       case '?':
221       default:
222         usage();
223         break;
224     }
225   }
226   argc -= optind;
227   argv += optind;
228 
229   if (!no_signals) { InitSignals(TerminateStored); }
230 
231   if (argc) {
232     if (configfile != NULL) { free(configfile); }
233     configfile = strdup(*argv);
234     argc--;
235     argv++;
236   }
237   if (argc) { usage(); }
238 
239   /*
240    * See if we want to drop privs.
241    */
242   if (geteuid() == 0) { drop(uid, gid, false); }
243 
244   if (export_config_schema) {
245     PoolMem buffer;
246 
247     my_config = InitSdConfig(configfile, M_ERROR_TERM);
248     PrintConfigSchemaJson(buffer);
249     printf("%s\n", buffer.c_str());
250     goto bail_out;
251   }
252 
253   my_config = InitSdConfig(configfile, M_ERROR_TERM);
254   ParseSdConfig(configfile, M_ERROR_TERM);
255 
256   if (forge_on) {
257     my_config->AddWarning(
258         "Running with '-p' is for testing and emergency recovery purposes "
259         "only");
260   }
261 
262   if (export_config) {
263     my_config->DumpResources(PrintMessage, NULL);
264     goto bail_out;
265   }
266 
267   if (!foreground && !test_config) {
268     daemon_start();  /* become daemon */
269     InitStackDump(); /* pick up new pid */
270   }
271 
272   if (InitCrypto() != 0) {
273     Jmsg((JobControlRecord*)NULL, M_ERROR_TERM, 0,
274          _("Cryptography library initialization failed.\n"));
275   }
276 
277   if (!CheckResources()) {
278     Jmsg((JobControlRecord*)NULL, M_ERROR_TERM, 0,
279          _("Please correct the configuration in %s\n"),
280          my_config->get_base_config_path().c_str());
281   }
282 
283   InitReservationsLock();
284 
285   if (test_config) {
286     if (my_config->HasWarnings()) {
287       /* messaging not initialized, so Jmsg with  M_WARNING doesn't work */
288       fprintf(stderr, _("There are configuration warnings:\n"));
289       for (auto& warning : my_config->GetWarnings()) {
290         fprintf(stderr, " * %s\n", warning.c_str());
291       }
292     }
293     TerminateStored(0);
294   }
295 
296   MyNameIs(0, (char**)NULL, me->resource_name_); /* Set our real name */
297 
298   CreatePidFile(me->pid_directory, "bareos-sd",
299                 GetFirstPortHostOrder(me->SDaddrs));
300   ReadStateFile(me->working_directory, "bareos-sd",
301                 GetFirstPortHostOrder(me->SDaddrs));
302   ReadCryptoCache(me->working_directory, "bareos-sd",
303                   GetFirstPortHostOrder(me->SDaddrs));
304 
305   SetJcrInThreadSpecificData(nullptr);
306 
307   LoadSdPlugins(me->plugin_directory, me->plugin_names);
308 
309   CleanUpOldFiles();
310 
311   /* Ensure that Volume Session Time and Id are both
312    * set and are both non-zero.
313    */
314   vol_session_time = (uint32_t)daemon_start_time;
315   if (vol_session_time == 0) { /* paranoid */
316     Jmsg0(NULL, M_ABORT, 0, _("Volume Session Time is ZERO!\n"));
317   }
318 
319   /*
320    * Start the device allocation thread
321    */
322   CreateVolumeLists(); /* do before device_init */
323   if (pthread_create(&thid, NULL, device_initialization, NULL) != 0) {
324     BErrNo be;
325     Emsg1(M_ABORT, 0, _("Unable to create thread. ERR=%s\n"), be.bstrerror());
326   }
327 
328   InitJcrChain();
329   StartWatchdog(); /* start watchdog thread */
330   if (me->jcr_watchdog_time) {
331     InitJcrSubsystem(
332         me->jcr_watchdog_time); /* start JobControlRecord watchdogs etc. */
333   }
334 
335   StartStatisticsThread();
336 
337 #if HAVE_NDMP
338   /*
339    * Separate thread that handles NDMP connections
340    */
341   if (me->ndmp_enable) {
342     StartNdmpThreadServer(me->NDMPaddrs, me->MaxConnections);
343   }
344 #endif
345 
346   /*
347    * Single server used for Director/Storage and File daemon
348    */
349   StartSocketServer(me->SDaddrs);
350 
351   /* to keep compiler quiet */
352   TerminateStored(0);
353 
354 bail_out:
355   return 0;
356 }
357 
358 /* Check Configuration file for necessary info */
CheckResources()359 static int CheckResources()
360 {
361   bool OK = true;
362   const std::string& configfile = my_config->get_base_config_path();
363 
364   if (my_config->GetNextRes(R_STORAGE, (BareosResource*)me) != NULL) {
365     Jmsg1(NULL, M_ERROR, 0, _("Only one Storage resource permitted in %s\n"),
366           configfile.c_str());
367     OK = false;
368   }
369 
370   if (my_config->GetNextRes(R_DIRECTOR, NULL) == NULL) {
371     Jmsg1(NULL, M_ERROR, 0,
372           _("No Director resource defined in %s. Cannot continue.\n"),
373           configfile.c_str());
374     OK = false;
375   }
376 
377   if (my_config->GetNextRes(R_DEVICE, NULL) == NULL) {
378     Jmsg1(NULL, M_ERROR, 0,
379           _("No Device resource defined in %s. Cannot continue.\n"),
380           configfile.c_str());
381     OK = false;
382   }
383 
384   /*
385    * Sanity check.
386    */
387   if (me->MaxConnections < ((2 * me->MaxConcurrentJobs) + 2)) {
388     me->MaxConnections = (2 * me->MaxConcurrentJobs) + 2;
389   }
390 
391   if (!me->messages) {
392     me->messages = (MessagesResource*)my_config->GetNextRes(R_MSGS, NULL);
393     if (!me->messages) {
394       Jmsg1(NULL, M_ERROR, 0,
395             _("No Messages resource defined in %s. Cannot continue.\n"),
396             configfile.c_str());
397       OK = false;
398     }
399   }
400 
401   if (!me->working_directory) {
402     Jmsg1(NULL, M_ERROR, 0,
403           _("No Working Directory defined in %s. Cannot continue.\n"),
404           configfile.c_str());
405     OK = false;
406   }
407 
408   StorageResource* store = me;
409   if (store->IsTlsConfigured()) {
410     if (!have_tls) {
411       Jmsg(NULL, M_FATAL, 0, _("TLS required but not compiled into Bareos.\n"));
412       OK = false;
413     }
414   }
415 
416   DeviceResource* device_resource = nullptr;
417   foreach_res (device_resource, R_DEVICE) {
418     if (device_resource->drive_crypto_enabled
419         && BitIsSet(CAP_LABEL, device_resource->cap_bits)) {
420       Jmsg(NULL, M_FATAL, 0,
421            _("LabelMedia enabled is incompatible with tape crypto on Device "
422              "\"%s\" in %s.\n"),
423            device_resource->resource_name_, configfile.c_str());
424       OK = false;
425     }
426   }
427 
428   if (OK) { OK = InitAutochangers(); }
429 
430   if (OK) {
431     CloseMsg(NULL);              /* close temp message handler */
432     InitMsg(NULL, me->messages); /* open daemon message handler */
433     SetWorkingDirectory(me->working_directory);
434     if (me->secure_erase_cmdline) {
435       SetSecureEraseCmdline(me->secure_erase_cmdline);
436     }
437     if (me->log_timestamp_format) {
438       SetLogTimestampFormat(me->log_timestamp_format);
439     }
440   }
441 
442   return OK;
443 }
444 
445 /**
446  * Remove old .spool files written by me from the working directory.
447  */
CleanUpOldFiles()448 static void CleanUpOldFiles()
449 {
450   DIR* dp;
451   struct dirent* result;
452 #ifdef USE_READDIR_R
453   struct dirent* entry;
454 #endif
455   int rc, name_max;
456   int my_name_len = strlen(my_name);
457   int len = strlen(me->working_directory);
458   POOLMEM* cleanup = GetPoolMemory(PM_MESSAGE);
459   POOLMEM* basename = GetPoolMemory(PM_MESSAGE);
460   regex_t preg1{};
461   char prbuf[500];
462   BErrNo be;
463 
464   /* Look for .spool files but don't allow spaces */
465   const char* pat1 = "^[^ ]+\\.spool$";
466 
467   /* Setup working directory prefix */
468   PmStrcpy(basename, me->working_directory);
469   if (len > 0 && !IsPathSeparator(me->working_directory[len - 1])) {
470     PmStrcat(basename, "/");
471   }
472 
473   /* Compile regex expressions */
474   rc = regcomp(&preg1, pat1, REG_EXTENDED);
475   if (rc != 0) {
476     regerror(rc, &preg1, prbuf, sizeof(prbuf));
477     Pmsg2(000, _("Could not compile regex pattern \"%s\" ERR=%s\n"), pat1,
478           prbuf);
479     goto get_out2;
480   }
481 
482   name_max = pathconf(".", _PC_NAME_MAX);
483   if (name_max < 1024) { name_max = 1024; }
484 
485   if (!(dp = opendir(me->working_directory))) {
486     BErrNo be;
487     Pmsg2(000, "Failed to open working dir %s for cleanup: ERR=%s\n",
488           me->working_directory, be.bstrerror());
489     goto get_out1;
490   }
491 
492 #ifdef USE_READDIR_R
493   entry = (struct dirent*)malloc(sizeof(struct dirent) + name_max + 1000);
494   while (1) {
495     if ((Readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
496 #else
497   while (1) {
498     result = readdir(dp);
499     if (result == NULL) {
500 #endif
501       break;
502     }
503 
504     /* Exclude any name with ., .., not my_name or containing a space */
505     if (strcmp(result->d_name, ".") == 0 || strcmp(result->d_name, "..") == 0
506         || strncmp(result->d_name, my_name, my_name_len) != 0) {
507       Dmsg1(500, "Skipped: %s\n", result->d_name);
508       continue;
509     }
510 
511     /* Unlink files that match regex */
512     if (regexec(&preg1, result->d_name, 0, NULL, 0) == 0) {
513       PmStrcpy(cleanup, basename);
514       PmStrcat(cleanup, result->d_name);
515       Dmsg1(500, "Unlink: %s\n", cleanup);
516       SecureErase(NULL, cleanup);
517     }
518   }
519 #ifdef USE_READDIR_R
520   free(entry);
521 #endif
522   closedir(dp);
523 
524 get_out1:
525   regfree(&preg1);
526 get_out2:
527   FreePoolMemory(cleanup);
528   FreePoolMemory(basename);
529 }
530 
531 
532 /**
533  * Here we attempt to init and open each device. This is done once at startup in
534  * a separate thread.
535  */
536 extern "C" void* device_initialization(void* arg)
537 {
538   DeviceResource* device_resource = nullptr;
539   DeviceControlRecord* dcr;
540   JobControlRecord* jcr;
541   Device* dev;
542   int errstat;
543 
544   LockRes(my_config);
545 
546   pthread_detach(pthread_self());
547   jcr = NewStoredJcr();
548   NewPlugins(jcr); /* instantiate plugins */
549   jcr->setJobType(JT_SYSTEM);
550 
551   /*
552    * Initialize job start condition variable
553    */
554   errstat = pthread_cond_init(&jcr->impl->job_start_wait, NULL);
555   if (errstat != 0) {
556     BErrNo be;
557     Jmsg1(jcr, M_ABORT, 0,
558           _("Unable to init job start cond variable: ERR=%s\n"),
559           be.bstrerror(errstat));
560   }
561 
562   /*
563    * Initialize job end condition variable
564    */
565   errstat = pthread_cond_init(&jcr->impl->job_end_wait, NULL);
566   if (errstat != 0) {
567     BErrNo be;
568     Jmsg1(jcr, M_ABORT, 0,
569           _("Unable to init job endstart cond variable: ERR=%s\n"),
570           be.bstrerror(errstat));
571   }
572 
573   foreach_res (device_resource, R_DEVICE) {
574     Dmsg1(90, "calling FactoryCreateDevice %s\n",
575           device_resource->archive_device_string);
576     dev = FactoryCreateDevice(NULL, device_resource);
577     Dmsg1(10, "SD init done %s\n", device_resource->archive_device_string);
578     if (!dev) {
579       Jmsg1(NULL, M_ERROR, 0, _("Could not initialize %s\n"),
580             device_resource->archive_device_string);
581       continue;
582     }
583 
584     dcr = new StorageDaemonDeviceControlRecord;
585     jcr->impl->dcr = dcr;
586     SetupNewDcrDevice(jcr, dcr, dev, NULL);
587     jcr->impl->dcr->SetWillWrite();
588     GeneratePluginEvent(jcr, bSdEventDeviceInit, dcr);
589     if (dev->AttachedToAutochanger()) {
590       /*
591        * If autochanger set slot in dev structure
592        */
593       GetAutochangerLoadedSlot(dcr);
594     }
595 
596     if (BitIsSet(CAP_ALWAYSOPEN, device_resource->cap_bits)) {
597       Dmsg1(20, "calling FirstOpenDevice %s\n", dev->print_name());
598       if (!FirstOpenDevice(dcr)) {
599         Jmsg1(NULL, M_ERROR, 0, _("Could not open device %s\n"),
600               dev->print_name());
601         Dmsg1(20, "Could not open device %s\n", dev->print_name());
602         FreeDeviceControlRecord(dcr);
603         jcr->impl->dcr = NULL;
604         continue;
605       }
606     }
607 
608     if (BitIsSet(CAP_AUTOMOUNT, device_resource->cap_bits) && dev->IsOpen()) {
609       switch (ReadDevVolumeLabel(dcr)) {
610         case VOL_OK:
611           memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
612           VolumeUnused(dcr); /* mark volume "released" */
613           break;
614         default:
615           Jmsg1(NULL, M_WARNING, 0, _("Could not mount device %s\n"),
616                 dev->print_name());
617           break;
618       }
619     }
620     FreeDeviceControlRecord(dcr);
621     jcr->impl->dcr = NULL;
622   }
623   FreeJcr(jcr);
624   init_done = true;
625   UnlockRes(my_config);
626   return NULL;
627 }
628 
629 /**
630  * Clean up and then exit
631  */
632 namespace storagedaemon {
633 
634 #if !defined(HAVE_WIN32)
635 static
636 #endif
637     void
638     TerminateStored(int sig)
639 {
640   static bool in_here = false;
641   DeviceResource* device_resource = nullptr;
642   JobControlRecord* jcr;
643 
644   if (in_here) {       /* prevent loops */
645     Bmicrosleep(2, 0); /* yield */
646     exit(1);
647   }
648   in_here = true;
649   debug_level = 0; /* turn off any debug */
650   StopStatisticsThread();
651 #if HAVE_NDMP
652   if (me->ndmp_enable) { StopNdmpThreadServer(); }
653 #endif
654   StopSocketServer();
655 
656   StopWatchdog();
657 
658   if (sig == SIGTERM) { /* normal shutdown request? */
659     /*
660      * This is a normal shutdown request. We wiffle through
661      *   all open jobs canceling them and trying to wake
662      *   them up so that they will report back the correct
663      *   volume status.
664      */
665     foreach_jcr (jcr) {
666       BareosSocket* fd;
667       if (jcr->JobId == 0) { continue; /* ignore console */ }
668       jcr->setJobStatus(JS_Canceled);
669       fd = jcr->file_bsock;
670       if (fd) {
671         fd->SetTimedOut();
672         jcr->MyThreadSendSignal(TIMEOUT_SIGNAL);
673         Dmsg1(100, "term_stored killing JobId=%d\n", jcr->JobId);
674         /* ***FIXME*** wiffle through all dcrs */
675         if (jcr->impl->dcr && jcr->impl->dcr->dev
676             && jcr->impl->dcr->dev->blocked()) {
677           pthread_cond_broadcast(&jcr->impl->dcr->dev->wait_next_vol);
678           Dmsg1(100, "JobId=%u broadcast wait_device_release\n",
679                 (uint32_t)jcr->JobId);
680           ReleaseDeviceCond();
681         }
682         if (jcr->impl->read_dcr && jcr->impl->read_dcr->dev
683             && jcr->impl->read_dcr->dev->blocked()) {
684           pthread_cond_broadcast(&jcr->impl->read_dcr->dev->wait_next_vol);
685           Dmsg1(100, "JobId=%u broadcast wait_device_release\n",
686                 (uint32_t)jcr->JobId);
687           ReleaseDeviceCond();
688         }
689         Bmicrosleep(0, 50000);
690       }
691       FreeJcr(jcr);
692     }
693     Bmicrosleep(0, 500000); /* give them 1/2 sec to clean up */
694   }
695 
696   WriteStateFile(me->working_directory, "bareos-sd",
697                  GetFirstPortHostOrder(me->SDaddrs));
698   DeletePidFile(me->pid_directory, "bareos-sd",
699                 GetFirstPortHostOrder(me->SDaddrs));
700 
701   Dmsg1(200, "In TerminateStored() sig=%d\n", sig);
702 
703   UnloadSdPlugins();
704   FlushCryptoCache();
705   FreeVolumeLists();
706 
707   foreach_res (device_resource, R_DEVICE) {
708     Dmsg1(10, "Term device %s\n", device_resource->archive_device_string);
709     if (device_resource->dev) {
710       device_resource->dev->ClearVolhdr();
711       delete device_resource->dev;
712       device_resource->dev = nullptr;
713     } else {
714       Dmsg1(10, "No dev structure %s\n",
715             device_resource->archive_device_string);
716     }
717   }
718 
719 #if defined(HAVE_DYNAMIC_SD_BACKENDS)
720   FlushAndCloseBackendDevices();
721 #endif
722 
723   if (configfile) {
724     free(configfile);
725     configfile = NULL;
726   }
727   if (my_config) {
728     delete my_config;
729     my_config = NULL;
730   }
731 
732   if (debug_level > 10) { PrintMemoryPoolStats(); }
733   TermMsg();
734   CleanupCrypto();
735   TermReservationsLock();
736   CloseMemoryPool();
737 
738   exit(sig);
739 }
740 
741 } /* namespace storagedaemon */
742