]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/stored/init_dev.c
Big backport from Enterprise
[bacula/bacula] / bacula / src / stored / init_dev.c
diff --git a/bacula/src/stored/init_dev.c b/bacula/src/stored/init_dev.c
new file mode 100644 (file)
index 0000000..01d6eb6
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   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.
+*/
+/*
+ * Initialize a single device.
+ *
+ * This code was split from dev.c in January 2016
+ *
+ *     written by, Kern Sibbald, MM
+ */
+
+#include "bacula.h"
+#include "stored.h"
+#include <dlfcn.h>
+
+/* Define possible extensions */
+#if defined(HAVE_WIN32)
+#define DRV_EXT ".dll"
+#elif defined(HAVE_DARWIN_OS)
+#define DRV_EXT ".dylib"
+#else
+#define DRV_EXT ".so"
+#endif
+
+#ifndef RTLD_NOW
+#define RTLD_NOW 2
+#endif
+
+/* Forward referenced functions */
+extern "C" {
+typedef DEVICE *(*newDriver_t)(JCR *jcr, DEVRES *device);
+}
+
+static DEVICE *load_driver(JCR *jcr, DEVRES *device);
+
+/*
+ * Driver item for driver table
+*/
+struct driver_item {
+   const char *name;
+   void *handle;
+   newDriver_t newDriver;
+   bool builtin;
+   bool loaded;
+};
+
+/*
+ * Driver table. Must be in same order as the B_xxx_DEV type
+ *   name   handle, builtin  loaded
+ */
+static driver_item driver_tab[] = {
+/*   name   handle, newDriver builtin  loaded */
+   {"file",    NULL, NULL,    true,  true},
+   {"tape",    NULL, NULL,    true,  true},
+   {"none",    NULL, NULL,    true,  true}, /* deprecated was DVD */
+   {"fifo",    NULL, NULL,    true,  true},
+   {"vtape",   NULL, NULL,    true,  true},
+   {"ftp",     NULL, NULL,    true,  true},
+   {"vtl",     NULL, NULL,    true,  true},
+   {"none",    NULL, NULL,    true,  true}, /* B_ADATA_DEV */
+   {"aligned", NULL, NULL,    false, false},
+   {"none",    NULL, NULL,    true,  true}, /* deprecated was old dedup */
+   {"null",    NULL, NULL,    true,  true},
+   {"none",    NULL, NULL,    true,  true}, /* deprecated B_VALIGNED_DEV */
+   {"none",    NULL, NULL,    true,  true}, /* deprecated B_VDEDUP_DEV */
+   {"cloud",   NULL, NULL,    false, false},
+   {"none",    NULL, NULL,    false, false},
+   {NULL,      NULL, NULL,    false, false}
+};
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* The alist should be created with not_owned_by_alist argument */
+void sd_list_loaded_drivers(alist *list)
+{
+   for(int i=0 ; driver_tab[i].name != NULL; i++) {
+      if (driver_tab[i].loaded && !driver_tab[i].builtin) {
+         list->append((void*)driver_tab[i].name);
+      }
+   }
+}
+
+/*
+ * Allocate and initialize the DEVICE structure
+ * Note, if dev is non-NULL, it is already allocated,
+ * thus we neither allocate it nor free it. This allows
+ * the caller to put the packet in shared memory.
+ *
+ *  Note, for a tape, the device->device_name is the device name
+ *     (e.g. /dev/nst0), and for a file, the device name
+ *     is the directory in which the file will be placed.
+ *
+ */
+DEVICE *init_dev(JCR *jcr, DEVRES *device, bool adata)
+{
+   struct stat statp;
+   DEVICE *dev = NULL;
+   uint32_t n_drivers;
+
+   generate_global_plugin_event(bsdGlobalEventDeviceInit, device);
+   Dmsg1(150, "init_dev dev_type=%d\n", device->dev_type);
+   /* If no device type specified, try to guess */
+   if (!device->dev_type) {
+      /* Check that device is available */
+      if (stat(device->device_name, &statp) < 0) {
+         berrno be;
+         Jmsg3(jcr, M_ERROR, 0, _("[SE0001] Unable to stat device %s at %s: ERR=%s\n"),
+            device->hdr.name, device->device_name, be.bstrerror());
+         return NULL;
+      }
+      if (S_ISDIR(statp.st_mode)) {
+         device->dev_type = B_FILE_DEV;
+      } else if (S_ISCHR(statp.st_mode)) {
+         device->dev_type = B_TAPE_DEV;
+      } else if (S_ISFIFO(statp.st_mode)) {
+         device->dev_type = B_FIFO_DEV;
+#ifdef USE_VTAPE
+      /* must set DeviceType = Vtape
+       * in normal mode, autodetection is disabled
+       */
+      } else if (S_ISREG(statp.st_mode)) {
+         device->dev_type = B_VTAPE_DEV;
+#endif
+      } else if (!(device->cap_bits & CAP_REQMOUNT)) {
+         Jmsg2(jcr, M_ERROR, 0, _("[SE0002] %s is an unknown device type. Must be tape or directory."
+               " st_mode=%x\n"),
+            device->device_name, statp.st_mode);
+         return NULL;
+      }
+      if (strcmp(device->device_name, "/dev/null") == 0) {
+         device->dev_type = B_NULL_DEV;
+      }
+   }
+
+   /* Count drivers */
+   for (n_drivers=0; driver_tab[n_drivers].name; n_drivers++) { };
+   Dmsg1(100, "Num drivers=%d\n", n_drivers);
+
+   /* If invalid dev_type get out */
+   if (device->dev_type < 0 || device->dev_type > n_drivers) {
+      Jmsg2(jcr, M_FATAL, 0, _("[SF0001] Invalid device type=%d name=\"%s\"\n"),
+         device->dev_type, device->hdr.name);
+      return NULL;
+   }
+   Dmsg5(100, "loadable=%d type=%d loaded=%d name=%s handle=%p\n",
+      !driver_tab[device->dev_type-1].builtin,
+      device->dev_type,
+      driver_tab[device->dev_type-1].loaded,
+      driver_tab[device->dev_type-1].name,
+      driver_tab[device->dev_type-1].handle);
+   if (driver_tab[device->dev_type-1].builtin) {
+      /* Built-in driver */
+      switch (device->dev_type) {
+#ifdef HAVE_WIN32
+      case B_TAPE_DEV:
+//       dev = New(win_tape_dev);
+         break;
+      case B_ADATA_DEV:
+      case B_ALIGNED_DEV:
+      case B_FILE_DEV:
+         dev = New(win_file_dev);
+         dev->capabilities |= CAP_LSEEK;
+         break;
+      case B_NULL_DEV:
+         dev = New(win_file_dev);
+         break;
+#else
+      case B_VTAPE_DEV:
+         dev = New(vtape);
+         break;
+      case B_TAPE_DEV:
+         dev = New(tape_dev);
+         break;
+      case B_FILE_DEV:
+         dev = New(file_dev);
+         dev->capabilities |= CAP_LSEEK;
+         break;
+      case B_NULL_DEV:
+         dev = New(null_dev);
+         break;
+      case B_FIFO_DEV:
+         dev = New(fifo_dev);
+         break;
+#endif
+      default:
+         Jmsg2(jcr, M_FATAL, 0, _("[SF0002] Unknown device type=%d device=\"%s\"\n"),
+            device->dev_type, device->hdr.name);
+         return NULL;
+      }
+   } else {
+      /* Loadable driver */
+      dev = load_driver(jcr, device);
+   }
+   if (!dev) {
+      return NULL;
+   }
+
+   dev->adata = adata;
+
+   /* Keep the device ID in the DEVICE struct to identify the hardware */
+   if (dev->is_file() && stat(dev->archive_name(), &statp) == 0) {
+      dev->devno = statp.st_dev;
+   }
+
+   dev->device_generic_init(jcr, device);
+
+   /* Do device specific initialization */
+   dev->device_specific_init(jcr, device);
+
+   /* ***FIXME*** move to fifo driver */
+   if (dev->is_fifo()) {
+      dev->capabilities |= CAP_STREAM; /* set stream device */
+   }
+
+   return dev;
+}
+
+/*
+ * Do all the generic initialization here. Same for all devices.
+ */
+void DEVICE::device_generic_init(JCR *jcr, DEVRES *device)
+{
+   struct stat statp;
+   DEVICE *dev = this;
+   DCR *dcr = NULL;
+   int errstat;
+   uint32_t max_bs;
+
+   dev->clear_slot();         /* unknown */
+
+   /* Copy user supplied device parameters from Resource */
+   dev->dev_name = get_memory(strlen(device->device_name)+1);
+   pm_strcpy(dev->dev_name, device->device_name);
+   dev->prt_name = get_memory(strlen(device->device_name) + strlen(device->hdr.name) + 20);
+   /* We edit "Resource-name" (physical-name) */
+   Mmsg(dev->prt_name, "\"%s\" (%s)", device->hdr.name, device->device_name);
+   Dmsg1(400, "Allocate dev=%s\n", dev->print_name());
+   dev->capabilities = device->cap_bits;
+   dev->min_free_space = device->min_free_space;
+   dev->min_block_size = device->min_block_size;
+   dev->max_block_size = device->max_block_size;
+   dev->max_volume_size = device->max_volume_size;
+   dev->max_file_size = device->max_file_size;
+   dev->padding_size = device->padding_size;
+   dev->file_alignment = device->file_alignment;
+   dev->max_concurrent_jobs = device->max_concurrent_jobs;
+   dev->volume_capacity = device->volume_capacity;
+   dev->max_rewind_wait = device->max_rewind_wait;
+   dev->max_open_wait = device->max_open_wait;
+   dev->vol_poll_interval = device->vol_poll_interval;
+   dev->max_spool_size = device->max_spool_size;
+   dev->drive_index = device->drive_index;
+   dev->enabled = device->enabled;
+   dev->autoselect = device->autoselect;
+   dev->read_only = device->read_only;
+   dev->dev_type = device->dev_type;
+   dev->device = device;
+   if (dev->is_tape()) { /* No parts on tapes */
+      dev->max_part_size = 0;
+   } else {
+      dev->max_part_size = device->max_part_size;
+   }
+   /* Sanity check */
+   if (dev->vol_poll_interval && dev->vol_poll_interval < 60) {
+      dev->vol_poll_interval = 60;
+   }
+
+   if (!device->dev) {
+      device->dev = dev;
+   }
+
+   /* If the device requires mount :
+    * - Check that the mount point is available
+    * - Check that (un)mount commands are defined
+    */
+   if (dev->is_file() && dev->requires_mount()) {
+      if (!device->mount_point || stat(device->mount_point, &statp) < 0) {
+         berrno be;
+         dev->dev_errno = errno;
+         Jmsg2(jcr, M_ERROR_TERM, 0, _("[SA0003] Unable to stat mount point %s: ERR=%s\n"),
+            device->mount_point, be.bstrerror());
+      }
+
+      if (!device->mount_command || !device->unmount_command) {
+         Jmsg0(jcr, M_ERROR_TERM, 0, _("[SA0004] Mount and unmount commands must defined for a device which requires mount.\n"));
+      }
+   }
+
+
+   /* Sanity check */
+   if (dev->max_block_size == 0) {
+      max_bs = DEFAULT_BLOCK_SIZE;
+   } else {
+      max_bs = dev->max_block_size;
+   }
+   if (dev->min_block_size > max_bs) {
+      Jmsg(jcr, M_ERROR_TERM, 0, _("[SA0005] Min block size > max on device %s\n"),
+           dev->print_name());
+   }
+   if (dev->max_block_size > 4096000) {
+      Jmsg3(jcr, M_ERROR, 0, _("[SA0006] Block size %u on device %s is too large, using default %u\n"),
+         dev->max_block_size, dev->print_name(), DEFAULT_BLOCK_SIZE);
+      dev->max_block_size = 0;
+   }
+   if (dev->max_block_size % TAPE_BSIZE != 0) {
+      Jmsg3(jcr, M_WARNING, 0, _("[SW0007] Max block size %u not multiple of device %s block size=%d.\n"),
+         dev->max_block_size, dev->print_name(), TAPE_BSIZE);
+   }
+   if (dev->max_volume_size != 0 && dev->max_volume_size < (dev->max_block_size << 4)) {
+      Jmsg(jcr, M_ERROR_TERM, 0, _("[SA0008] Max Vol Size < 8 * Max Block Size for device %s\n"),
+           dev->print_name());
+   }
+
+   dev->errmsg = get_pool_memory(PM_EMSG);
+   *dev->errmsg = 0;
+
+   if ((errstat = dev->init_mutex()) != 0) {
+      berrno be;
+      dev->dev_errno = errstat;
+      Mmsg1(dev->errmsg, _("[SA0009] Unable to init mutex: ERR=%s\n"), be.bstrerror(errstat));
+      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
+   }
+   if ((errstat = pthread_cond_init(&dev->wait, NULL)) != 0) {
+      berrno be;
+      dev->dev_errno = errstat;
+      Mmsg1(dev->errmsg, _("[SA0010] Unable to init cond variable: ERR=%s\n"), be.bstrerror(errstat));
+      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
+   }
+   if ((errstat = pthread_cond_init(&dev->wait_next_vol, NULL)) != 0) {
+      berrno be;
+      dev->dev_errno = errstat;
+      Mmsg1(dev->errmsg, _("[SA0011] Unable to init cond variable: ERR=%s\n"), be.bstrerror(errstat));
+      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
+   }
+   if ((errstat = pthread_mutex_init(&dev->spool_mutex, NULL)) != 0) {
+      berrno be;
+      dev->dev_errno = errstat;
+      Mmsg1(dev->errmsg, _("[SA0012] Unable to init spool mutex: ERR=%s\n"), be.bstrerror(errstat));
+      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
+   }
+   if ((errstat = dev->init_acquire_mutex()) != 0) {
+      berrno be;
+      dev->dev_errno = errstat;
+      Mmsg1(dev->errmsg, _("[SA0013] Unable to init acquire mutex: ERR=%s\n"), be.bstrerror(errstat));
+      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
+   }
+   if ((errstat = dev->init_freespace_mutex()) != 0) {
+      berrno be;
+      dev->dev_errno = errstat;
+      Mmsg1(dev->errmsg, _("[SA0014] Unable to init freespace mutex: ERR=%s\n"), be.bstrerror(errstat));
+      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
+   }
+   if ((errstat = dev->init_read_acquire_mutex()) != 0) {
+      berrno be;
+      dev->dev_errno = errstat;
+      Mmsg1(dev->errmsg, _("[SA0015] Unable to init read acquire mutex: ERR=%s\n"), be.bstrerror(errstat));
+      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
+   }
+   if ((errstat = dev->init_volcat_mutex()) != 0) {
+      berrno be;
+      dev->dev_errno = errstat;
+      Mmsg1(dev->errmsg, _("[SA0016] Unable to init volcat mutex: ERR=%s\n"), be.bstrerror(errstat));
+      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
+   }
+   if ((errstat = dev->init_dcrs_mutex()) != 0) {
+      berrno be;
+      dev->dev_errno = errstat;
+      Mmsg1(dev->errmsg, _("[SA0017] Unable to init dcrs mutex: ERR=%s\n"), be.bstrerror(errstat));
+      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
+   }
+
+   dev->set_mutex_priorities();
+
+   dev->clear_opened();
+   dev->attached_dcrs = New(dlist(dcr, &dcr->dev_link));
+   Dmsg2(100, "init_dev: tape=%d dev_name=%s\n", dev->is_tape(), dev->dev_name);
+   dev->initiated = true;
+}
+
+static DEVICE *load_driver(JCR *jcr, DEVRES *device)
+{
+   POOL_MEM fname(PM_FNAME);
+   DEVICE *dev;
+   driver_item *drv;
+   const char *slash;
+   void *pHandle;
+   int len;
+   newDriver_t newDriver;
+
+   P(mutex);
+   if (!me->plugin_directory) {
+      Jmsg2(jcr, M_FATAL, 0,  _("[SF0018] Plugin directory not defined. Cannot load SD %s driver for device %s.\n"),
+         driver_tab[device->dev_type - 1], device->hdr.name);
+      V(mutex);
+      return NULL;
+   }
+   len = strlen(me->plugin_directory);
+   if (len == 0) {
+      Jmsg0(jcr, M_FATAL, 0,  _("[SF0019] Plugin directory not defined. Cannot load drivers.\n"));
+      V(mutex);
+      return NULL;
+   }
+
+   if (IsPathSeparator(me->plugin_directory[len - 1])) {
+      slash = "";
+   } else {
+      slash = "/";
+   }
+
+   Dmsg5(100, "loadable=%d type=%d loaded=%d name=%s handle=%p\n",
+      !driver_tab[device->dev_type-1].builtin,
+      device->dev_type,
+      driver_tab[device->dev_type-1].loaded,
+      driver_tab[device->dev_type-1].name,
+      driver_tab[device->dev_type-1].handle);
+   drv = &driver_tab[device->dev_type - 1];
+   Mmsg(fname, "%s%sbacula-sd-%s-driver%s%s", me->plugin_directory, slash,
+        drv->name, "-" VERSION, DRV_EXT);
+   if (!drv->loaded) {
+      Dmsg1(10, "Open SD driver at %s\n", fname.c_str());
+      pHandle = dlopen(fname.c_str(), RTLD_NOW);
+      if (pHandle) {
+         Dmsg2(100, "Driver=%s handle=%p\n", drv->name, pHandle);
+         /* Get global entry point */
+         Dmsg1(10, "Lookup \"BaculaSDdriver\" in driver=%s\n", drv->name);
+         newDriver = (newDriver_t)dlsym(pHandle, "BaculaSDdriver");
+         Dmsg2(10, "Driver=%s entry point=%p\n", drv->name, newDriver);
+         if (!newDriver) {
+            const char *error = dlerror();
+            Jmsg(NULL, M_ERROR, 0, _("[SE0003] Lookup of symbol \"BaculaSDdriver\" in driver %s for device %s failed: ERR=%s\n"),
+               device->hdr.name, fname.c_str(), NPRT(error));
+            Dmsg2(10, "Lookup of symbol \"BaculaSDdriver\" driver=%s failed: ERR=%s\n",
+               fname.c_str(), NPRT(error));
+            dlclose(pHandle);
+            V(mutex);
+            return NULL;
+         }
+         drv->handle = pHandle;
+         drv->loaded = true;
+         drv->newDriver = newDriver;
+      } else {
+         /* dlopen failed */
+         const char *error = dlerror();
+         Jmsg3(jcr, M_FATAL, 0, _("[SF0020] dlopen of SD driver=%s at %s failed: ERR=%s\n"),
+              drv->name, fname.c_str(), NPRT(error));
+         Dmsg2(000, "dlopen plugin %s failed: ERR=%s\n", fname.c_str(),
+               NPRT(error));
+         V(mutex);
+         return NULL;
+      }
+   } else {
+      Dmsg1(10, "SD driver=%s is already loaded.\n", drv->name);
+   }
+
+   /* Call driver initialization */
+   dev = drv->newDriver(jcr, device);
+   V(mutex);
+   return dev;
+}