]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/stored/stored.c
Backport from Bacula Enterprise
[bacula/bacula] / bacula / src / stored / stored.c
index 94d466168df46ae17270c82db05b2fdc82c0cb52..57820883402fee8d1ec54dca07b6ca526c87a612 100644 (file)
@@ -1,49 +1,45 @@
 /*
-   Bacula® - The Network Backup Solution
-
-   Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
-
-   The main author of Bacula is Kern Sibbald, with contributions from
-   many others, a complete list can be found in the file AUTHORS.
-   This program is Free Software; you can redistribute it and/or
-   modify it under the terms of version two of the GNU General Public
-   License as published by the Free Software Foundation and included
-   in the file LICENSE.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA.
-
-   Bacula® is a registered trademark of John Walker.
-   The licensor of Bacula is the Free Software Foundation Europe
-   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
-   Switzerland, email:ftf@fsfeurope.org.
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2015 Kern Sibbald
+   Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is 
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
 */
 /*
  * Second generation Storage daemon.
  *
- *  Kern Sibbald, MM
+ *  Written by Kern Sibbald, MM
  *
  * It accepts a number of simple commands from the File daemon
  * and acts on them. When a request to append data is made,
  * it opens a data channel and accepts data from the
  * File daemon.
  *
- *   Version $Id$
- *
  */
 
 #include "bacula.h"
 #include "stored.h"
 
-/* Imported functions */
+/* TODO: fix problem with bls, bextract
+ * that use findlib and already declare
+ * filed plugins
+ */
+#include "sd_plugins.h"
 
+/* Imported functions */
+extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
 
 /* Forward referenced functions */
 void terminate_stored(int sig);
@@ -58,6 +54,7 @@ extern "C" void *device_initialization(void *arg);
 char OK_msg[]   = "3000 OK\n";
 char TERM_msg[] = "3999 Terminate\n";
 STORES *me = NULL;                    /* our Global resource */
+
 bool forge_on = false;                /* proceed inspite of I/O errors */
 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
@@ -68,31 +65,36 @@ static uint32_t VolSessionId = 0;
 uint32_t VolSessionTime;
 char *configfile = NULL;
 bool init_done = false;
+static pthread_t server_tid;
+static bool server_tid_valid = false;
 
 /* Global static variables */
 static bool foreground = 0;
 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 static workq_t dird_workq;            /* queue for processing connections */
+static CONFIG *config;
 
 
 static void usage()
 {
    fprintf(stderr, _(
-PROG_COPYRIGHT
-"\nVersion: %s (%s)\n\n"
-"Usage: stored [options] [-c config_file] [config_file]\n"
-"        -c <file>   use <file> as configuration file\n"
-"        -d <nn>     set debug level to <nn>\n"
-"        -dt         print timestamp in debug output\n"
-"        -f          run in foreground (for debugging)\n"
-"        -g <group>  set groupid to group\n"
-"        -p          proceed despite I/O errors\n"
-"        -s          no signals (for debugging)\n"
-"        -t          test - read config and exit\n"
-"        -u <user>   userid to <user>\n"
-"        -v          verbose user messages\n"
-"        -?          print this message.\n"
-"\n"), 2000, VERSION, BDATE);
+      PROG_COPYRIGHT
+      "\n%sVersion: %s (%s)\n\n"
+      "Usage: bacula-sd [options] [-c config_file] [config_file]\n"
+      "     -c <file>         use <file> as configuration file\n"
+      "     -d <nn>[,<tags>]  set debug level to <nn>, debug tags to <tags>\n"
+      "     -dt               print timestamp in debug output\n"
+      "     -T                set trace on\n"
+      "     -f                run in foreground (for debugging)\n"
+      "     -g <group>        set groupid to group\n"
+      "     -m                print kaboom output (for debugging)\n"
+      "     -p                proceed despite I/O errors\n"
+      "     -s                no signals (for debugging)\n"
+      "     -t                test - read config and exit\n"
+      "     -u <user>         userid to <user>\n"
+      "     -v                verbose user messages\n"
+      "     -?                print this message.\n"
+      "\n"), 2000, "", VERSION, BDATE);
    exit(1);
 }
 
@@ -133,7 +135,7 @@ int main (int argc, char *argv[])
       Emsg1(M_ABORT, 0, _("Tape block size (%d) is not a power of 2\n"), TAPE_BSIZE);
    }
 
-   while ((ch = getopt(argc, argv, "c:d:fg:pstu:v?")) != -1) {
+   while ((ch = getopt(argc, argv, "c:d:fg:mpstu:v?T")) != -1) {
       switch (ch) {
       case 'c':                    /* configuration file */
          if (configfile != NULL) {
@@ -146,13 +148,25 @@ int main (int argc, char *argv[])
          if (*optarg == 't') {
             dbg_timestamp = true;
          } else {
+            char *p;
+            /* We probably find a tag list -d 10,sql,bvfs */
+            if ((p = strchr(optarg, ',')) != NULL) {
+               *p = 0;
+            }
             debug_level = atoi(optarg);
             if (debug_level <= 0) {
                debug_level = 1;
             }
+            if (p) {
+               debug_parse_tags(p+1, &debug_level_tags);
+            }
          }
          break;
 
+      case 'T':
+         set_trace(true);
+         break;
+
       case 'f':                    /* run in foreground */
          foreground = true;
          break;
@@ -161,6 +175,10 @@ int main (int argc, char *argv[])
          gid = optarg;
          break;
 
+      case 'm':                    /* print kaboom output */
+         prt_kaboom = true;
+         break;
+
       case 'p':                    /* proceed in spite of I/O errors */
          forge_on = true;
          break;
@@ -201,6 +219,11 @@ int main (int argc, char *argv[])
    if (argc)
       usage();
 
+   if (!foreground) {
+      daemon_start();                 /* become daemon */
+      init_stack_dump();              /* pick up new pid */
+   }
+
    if (!no_signals) {
       init_signals(terminate_stored);
    }
@@ -209,7 +232,8 @@ int main (int argc, char *argv[])
       configfile = bstrdup(CONFIG_FILE);
    }
 
-   parse_config(configfile);
+   config = new_config_parser();
+   parse_sd_config(config, configfile, M_ERROR_TERM);
 
    if (init_crypto() != 0) {
       Jmsg((JCR *)NULL, M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
@@ -227,18 +251,22 @@ int main (int argc, char *argv[])
 
    my_name_is(0, (char **)NULL, me->hdr.name);     /* Set our real name */
 
-   if (!foreground) {
-      daemon_start();                 /* become daemon */
-      init_stack_dump();              /* pick up new pid */
-   }
 
-   create_pid_file(me->pid_directory, "bacula-sd", get_first_port_host_order(me->sdaddrs));
-   read_state_file(me->working_directory, "bacula-sd", get_first_port_host_order(me->sdaddrs));
+   create_pid_file(me->pid_directory, "bacula-sd",
+                   get_first_port_host_order(me->sdaddrs));
+   read_state_file(me->working_directory, "bacula-sd",
+                   get_first_port_host_order(me->sdaddrs));
 
-   drop(uid, gid);
+   set_jcr_in_tsd(INVALID_JCR);
+   /* Make sure on Solaris we can run concurrent, watch dog + servers + misc */
+   set_thread_concurrency(me->max_concurrent_jobs * 2 + 4);
+   lmgr_init_thread(); /* initialize the lockmanager stack */
 
-   cleanup_old_files();
+   load_sd_plugins(me->plugin_directory);
 
+   drop(uid, gid, false);
+
+   cleanup_old_files();
 
    /* Ensure that Volume Session Time and Id are both
     * set and are both non-zero.
@@ -248,15 +276,10 @@ int main (int argc, char *argv[])
       Jmsg0(NULL, M_ABORT, 0, _("Volume Session Time is ZERO!\n"));
    }
 
-   init_python_interpreter(me->hdr.name, me->scripts_directory, "SDStartUp");
-
-   /* Make sure on Solaris we can run concurrent, watch dog + servers + misc */
-   set_thread_concurrency(me->max_concurrent_jobs * 2 + 4);
-
-    /*
-     * Start the device allocation thread
-     */
-   create_volume_list();              /* do before device_init */
+   /*
+    * Start the device allocation thread
+    */
+   create_volume_lists();             /* do before device_init */
    if (pthread_create(&thid, NULL, device_initialization, NULL) != 0) {
       berrno be;
       Emsg1(M_ABORT, 0, _("Unable to create thread. ERR=%s\n"), be.bstrerror());
@@ -266,6 +289,8 @@ int main (int argc, char *argv[])
    init_jcr_subsystem();              /* start JCR watchdogs etc. */
 
    /* Single server used for Director and File daemon */
+   server_tid = pthread_self();
+   server_tid_valid = true;
    bnet_thread_server(me->sdaddrs, me->max_concurrent_jobs * 2 + 1,
                       &dird_workq, handle_connection_request);
    exit(1);                           /* to keep compiler quiet */
@@ -289,7 +314,6 @@ static int check_resources()
    bool OK = true;
    bool tls_needed;
 
-
    me = (STORES *)GetNextRes(R_STORAGE, NULL);
    if (!me) {
       Jmsg1(NULL, M_ERROR, 0, _("No Storage resource defined in %s. Cannot continue.\n"),
@@ -330,7 +354,7 @@ static int check_resources()
 
    DIRRES *director;
    STORES *store;
-   foreach_res(store, R_STORAGE) { 
+   foreach_res(store, R_STORAGE) {
       /* tls_require implies tls_enable */
       if (store->tls_require) {
          if (have_tls) {
@@ -375,7 +399,7 @@ static int check_resources()
             store->tls_keyfile, NULL, NULL, store->tls_dhfile,
             store->tls_verify_peer);
 
-         if (!store->tls_ctx) { 
+         if (!store->tls_ctx) {
             Jmsg(NULL, M_FATAL, 0, _("Failed to initialize TLS context for Storage \"%s\" in %s.\n"),
                  store->hdr.name, configfile);
             OK = false;
@@ -383,7 +407,7 @@ static int check_resources()
       }
    }
 
-   foreach_res(director, R_DIRECTOR) { 
+   foreach_res(director, R_DIRECTOR) {
       /* tls_require implies tls_enable */
       if (director->tls_require) {
          director->tls_enable = true;
@@ -422,7 +446,7 @@ static int check_resources()
             director->tls_keyfile, NULL, NULL, director->tls_dhfile,
             director->tls_verify_peer);
 
-         if (!director->tls_ctx) { 
+         if (!director->tls_ctx) {
             Jmsg(NULL, M_FATAL, 0, _("Failed to initialize TLS context for Director \"%s\" in %s.\n"),
                  director->hdr.name, configfile);
             OK = false;
@@ -432,7 +456,7 @@ static int check_resources()
 
    OK = init_autochangers();
 
-   
+
    if (OK) {
       close_msg(NULL);                   /* close temp message handler */
       init_msg(NULL, me->messages);      /* open daemon message handler */
@@ -442,26 +466,84 @@ static int check_resources()
    return OK;
 }
 
+/*
+ * Remove old .spool files written by me from the working directory.
+ */
 static void cleanup_old_files()
 {
-   POOLMEM *cleanup = get_pool_memory(PM_MESSAGE);
+   DIR* dp;
+   struct dirent *entry, *result;
+   int rc, name_max;
+   int my_name_len = strlen(my_name);
    int len = strlen(me->working_directory);
-#if defined(HAVE_WIN32)
-   pm_strcpy(cleanup, "del /q ");
-#else
-   pm_strcpy(cleanup, "/bin/rm -f ");
-#endif
-   pm_strcat(cleanup, me->working_directory);
+   POOLMEM *cleanup = get_pool_memory(PM_MESSAGE);
+   POOLMEM *basename = get_pool_memory(PM_MESSAGE);
+   regex_t preg1;
+   char prbuf[500];
+   const int nmatch = 30;
+   regmatch_t pmatch[nmatch];
+   berrno be;
+
+   /* Look for .spool files but don't allow spaces */
+   const char *pat1 = "^[^ ]+\\.spool$";
+
+   /* Setup working directory prefix */
+   pm_strcpy(basename, me->working_directory);
    if (len > 0 && !IsPathSeparator(me->working_directory[len-1])) {
-      pm_strcat(cleanup, "/");
+      pm_strcat(basename, "/");
+   }
+
+   /* Compile regex expressions */
+   rc = regcomp(&preg1, pat1, REG_EXTENDED);
+   if (rc != 0) {
+      regerror(rc, &preg1, prbuf, sizeof(prbuf));
+      Pmsg2(000,  _("Could not compile regex pattern \"%s\" ERR=%s\n"),
+           pat1, prbuf);
+      goto get_out2;
+   }
+
+   name_max = pathconf(".", _PC_NAME_MAX);
+   if (name_max < 1024) {
+      name_max = 1024;
+   }
+
+   if (!(dp = opendir(me->working_directory))) {
+      berrno be;
+      Pmsg2(000, "Failed to open working dir %s for cleanup: ERR=%s\n",
+            me->working_directory, be.bstrerror());
+      goto get_out1;
+   }
+
+   entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
+   while (1) {
+      if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
+         break;
+      }
+      /* Exclude any name with ., .., not my_name or containing a space */
+      if (strcmp(result->d_name, ".") == 0 || strcmp(result->d_name, "..") == 0 ||
+          strncmp(result->d_name, my_name, my_name_len) != 0) {
+         Dmsg1(500, "Skipped: %s\n", result->d_name);
+         continue;
+      }
+
+      /* Unlink files that match regex */
+      if (regexec(&preg1, result->d_name, nmatch, pmatch,  0) == 0) {
+         pm_strcpy(cleanup, basename);
+         pm_strcat(cleanup, result->d_name);
+         Dmsg1(500, "Unlink: %s\n", cleanup);
+         unlink(cleanup);
+      }
    }
-   pm_strcat(cleanup, my_name);
-   pm_strcat(cleanup, "*.spool");
-   run_program(cleanup, 0, NULL);
+   free(entry);
+   closedir(dp);
+
+get_out1:
+   regfree(&preg1);
+get_out2:
    free_pool_memory(cleanup);
+   free_pool_memory(basename);
 }
 
-
 /*
  * Here we attempt to init and open each device. This is done
  *  once at startup in a separate thread.
@@ -478,7 +560,8 @@ void *device_initialization(void *arg)
 
    pthread_detach(pthread_self());
    jcr = new_jcr(sizeof(JCR), stored_free_jcr);
-   jcr->JobType = JT_SYSTEM;
+   new_plugins(jcr);  /* instantiate plugins */
+   jcr->setJobType(JT_SYSTEM);
    /* Initialize FD start condition variable */
    int errstat = pthread_cond_init(&jcr->job_start_wait, NULL);
    if (errstat != 0) {
@@ -496,20 +579,30 @@ void *device_initialization(void *arg)
       }
 
       jcr->dcr = dcr = new_dcr(jcr, NULL, dev);
-      if (dev->is_autochanger()) {
-         /* If autochanger set slot in dev sturcture */
-         get_autochanger_loaded_slot(dcr);
-      }
+      generate_plugin_event(jcr, bsdEventDeviceInit, dcr);
 
       if (device->cap_bits & CAP_ALWAYSOPEN) {
+         if (dev->is_autochanger()) {
+            /* If autochanger set slot in dev sturcture */
+            get_autochanger_loaded_slot(dcr);
+         }
          Dmsg1(20, "calling first_open_device %s\n", dev->print_name());
+         if (generate_plugin_event(jcr, bsdEventDeviceOpen, dcr) != bRC_OK) {
+            Jmsg(jcr, M_FATAL, 0, _("generate_plugin_event(bsdEventDeviceOpen) Failed\n"));
+            continue;
+         }
+
          if (!first_open_device(dcr)) {
             Jmsg1(NULL, M_ERROR, 0, _("Could not open device %s\n"), dev->print_name());
             Dmsg1(20, "Could not open device %s\n", dev->print_name());
+            generate_plugin_event(jcr, bsdEventDeviceClose, dcr);
             free_dcr(dcr);
             jcr->dcr = NULL;
             continue;
          }
+      } else {
+         /* If not always open, we don't know what is in the drive */
+         dev->clear_slot();
       }
       if (device->cap_bits & CAP_AUTOMOUNT && dev->is_open()) {
          switch (read_dev_volume_label(dcr)) {
@@ -522,17 +615,21 @@ void *device_initialization(void *arg)
             break;
          }
       }
+
       free_dcr(dcr);
       jcr->dcr = NULL;
    }
+
+
 #ifdef xxx
    if (jcr->dcr) {
-      Dmsg1(000, "free_dcr=%p\n", jcr->dcr);
+      Pmsg1(000, "free_dcr=%p\n", jcr->dcr);
       free_dcr(jcr->dcr);
       jcr->dcr = NULL;
    }
 #endif
-   free_jcr(jcr); 
+   free_plugins(jcr);
+   free_jcr(jcr);
    init_done = true;
    UnlockRes();
    return NULL;
@@ -551,9 +648,10 @@ void terminate_stored(int sig)
       exit(1);
    }
    in_here = true;
+   debug_level = 0;                   /* turn off any debug */
    stop_watchdog();
 
-   if (sig == SIGTERM) {              /* normal shutdown request? */
+   if (sig == SIGTERM || sig == SIGINT) { /* normal shutdown request? or ^C */
       /*
        * This is a normal shutdown request. We wiffle through
        *   all open jobs canceling them and trying to wake
@@ -566,12 +664,16 @@ void terminate_stored(int sig)
             free_jcr(jcr);
             continue;                 /* ignore console */
          }
-         set_jcr_job_status(jcr, JS_Canceled);
+         if (jcr->dcr) {
+            /* Make sure no device remains locked */
+            generate_plugin_event(jcr, bsdEventDeviceClose, jcr->dcr);
+         }
+         jcr->setJobStatus(JS_Canceled);
          fd = jcr->file_bsock;
          if (fd) {
             fd->set_timed_out();
+            jcr->my_thread_send_signal(TIMEOUT_SIGNAL);
             Dmsg1(100, "term_stored killing JobId=%d\n", jcr->JobId);
-            pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
             /* ***FIXME*** wiffle through all dcrs */
             if (jcr->dcr && jcr->dcr->dev && jcr->dcr->dev->blocked()) {
                pthread_cond_broadcast(&jcr->dcr->dev->wait_next_vol);
@@ -594,6 +696,9 @@ void terminate_stored(int sig)
 
    Dmsg1(200, "In terminate_stored() sig=%d\n", sig);
 
+   unload_plugins();
+   free_volume_lists();
+
    foreach_res(device, R_DEVICE) {
       Dmsg1(10, "Term device %s\n", device->device_name);
       if (device->dev) {
@@ -604,21 +709,28 @@ void terminate_stored(int sig)
          Dmsg1(10, "No dev structure %s\n", device->device_name);
       }
    }
+   if (server_tid_valid) {
+      bnet_stop_thread_server(server_tid);
+   }
 
    if (configfile) {
       free(configfile);
       configfile = NULL;
    }
-   free_config_resources();
+   if (config) {
+      config->free_resources();
+      free(config);
+      config = NULL;
+   }
 
-   if (debug_level > 10) {
+   if (chk_dbglvl(10)) {
       print_memory_pool_stats();
    }
    term_msg();
    cleanup_crypto();
-   free_volume_list();
    term_reservations_lock();
    close_memory_pool();
+   lmgr_cleanup_main();
 
    sm_dump(false);                    /* dump orphaned buffers */
    exit(sig);