Print this page
NEX-13374 NDMP should be able to backup unmounted ZFS filesystems
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5801 Snapshots left over after failed backups
Reviewed by: Rick Mesta <rick.mesta@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Revert "NEX-5801 Snapshots left over after failed backups"
This reverts commit f182fb95f09036db71fbfc6f0a6b90469b761f21.
NEX-5801 Snapshots left over after failed backups
Reviewed by: Rick Mesta <rick.mesta@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-2911 NDMP logging should use syslog and is too chatty (build noise)
NEX-2911 NDMP logging should use syslog and is too chatty
NEX-894 Default location of NDMP log file should be under /var/log
NEX-559 NDMP cannot backup/restore a file which spans multiple tapes
        
*** 1,8 ****
  /*
   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
!  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
   */
  
  /*
   * BSD 3 Clause License
   *
--- 1,8 ----
  /*
   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
!  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
   */
  
  /*
   * BSD 3 Clause License
   *
*** 38,54 ****
   */
  /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
  
  #include <errno.h>
  #include <signal.h>
- #include <libgen.h>
  #include <libscf.h>
  #include <libintl.h>
  #include <sys/wait.h>
  #include <zone.h>
  #include <tsol/label.h>
  #include <dlfcn.h>
  #include "ndmpd.h"
  #include "ndmpd_common.h"
  
  /* zfs library handle & mutex */
  libzfs_handle_t *zlibh;
--- 38,57 ----
   */
  /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
  
  #include <errno.h>
  #include <signal.h>
  #include <libscf.h>
  #include <libintl.h>
  #include <sys/wait.h>
+ #include <syslog.h>
+ #include <syslog.h>
  #include <zone.h>
  #include <tsol/label.h>
  #include <dlfcn.h>
+ #include <sys/mount.h>
+ #include <libzfs.h>
  #include "ndmpd.h"
  #include "ndmpd_common.h"
  
  /* zfs library handle & mutex */
  libzfs_handle_t *zlibh;
*** 79,89 ****
          plname = ndmpd_get_prop(NDMP_PLUGIN_PATH);
          if (plname == NULL || *plname == '\0')
                  return (0);
  
          if ((mod_plp = dlopen(plname, RTLD_LOCAL | RTLD_NOW)) == NULL) {
!                 NDMP_LOG(LOG_ERR, "Error loading the plug-in %s: %s",
                      plname, dlerror());
                  return (0);
          }
  
          plugin_init = (ndmp_plugin_t *(*)(int))dlsym(mod_plp, "_ndmp_init");
--- 82,92 ----
          plname = ndmpd_get_prop(NDMP_PLUGIN_PATH);
          if (plname == NULL || *plname == '\0')
                  return (0);
  
          if ((mod_plp = dlopen(plname, RTLD_LOCAL | RTLD_NOW)) == NULL) {
!                 syslog(LOG_ERR, "Error loading the plug-in %s: %s",
                      plname, dlerror());
                  return (0);
          }
  
          plugin_init = (ndmp_plugin_t *(*)(int))dlsym(mod_plp, "_ndmp_init");
*** 90,100 ****
          if (plugin_init == NULL) {
                  (void) dlclose(mod_plp);
                  return (0);
          }
          if ((ndmp_pl = plugin_init(NDMP_PLUGIN_VERSION)) == NULL) {
!                 NDMP_LOG(LOG_ERR, "Error loading the plug-in %s", plname);
                  return (-1);
          }
          return (0);
  }
  
--- 93,103 ----
          if (plugin_init == NULL) {
                  (void) dlclose(mod_plp);
                  return (0);
          }
          if ((ndmp_pl = plugin_init(NDMP_PLUGIN_VERSION)) == NULL) {
!                 syslog(LOG_ERR, "Error loading the plug-in %s", plname);
                  return (-1);
          }
          return (0);
  }
  
*** 182,191 ****
--- 185,418 ----
          (void) sigprocmask(SIG_SETMASK, &oset, NULL);
          (void) chdir("/");
  }
  
  /*
+  * Utility routine to check if a zpool is bootable. For the purposes
+  * of cleaning up ndmp backup clones and snapshots, shouldn't consider
+  * the 'boot' volume.
+  *
+  * Parameters:
+  *   zhp (input) - the zfs handle of the zpool dataset.
+  *
+  * Returns:
+  *   B_TRUE : If the given zpool has a boot record
+  *   B_FALSE: otherwise
+  */
+ boolean_t
+ ndmp_zpool_is_bootable(zpool_handle_t *zhp)
+ {
+         char bootfs[ZFS_MAX_DATASET_NAME_LEN];
+ 
+         return (zpool_get_prop(zhp, ZPOOL_PROP_BOOTFS, bootfs,
+             sizeof (bootfs), NULL, B_FALSE) == 0 && strncmp(bootfs, "-",
+             sizeof (bootfs)) != 0);
+ }
+ 
+ /*
+  * This is the zpool_iter() callback routine specifically for
+  * ZFS_TYPE_SNAPSHOTS and is passed in a zfs handle to each one
+  * it finds during iteration.  If this callback returns zero
+  * the iterator keeps going, if it returns non-sero the
+  * iteration stops.
+  *
+  * Parameters:
+  *   zhp (input) - the zfs handle of the ZFS_TYPE_SNAPSHOTS dataset.
+  *   arg (input) - optional parameter (not used in this case)
+  *
+  * Returns:
+  *   0: on success
+  *  -1: otherwise
+  */
+ /*ARGSUSED*/
+ int
+ ndmp_match_and_destroy_snapshot(zfs_handle_t *zhp, void *arg)
+ {
+         int err = 0;
+         char *dataset_name;
+         char *snap_name;
+         char *snap_delim;
+         zfs_handle_t *dszhp;
+ 
+         dataset_name = strdup(zfs_get_name(zhp));
+         if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) {
+                 if (strstr(dataset_name, NDMP_RCF_BASENAME) != NULL) {
+                         snap_delim = strchr(dataset_name, '@');
+                         snap_name = snap_delim + 1;
+                         *snap_delim = '\0';
+ 
+                         syslog(LOG_DEBUG,
+                             "Remove snap [%s] from dataset [%s] tag [%s]\n",
+                             snap_name, dataset_name, NDMP_RCF_BASENAME);
+ 
+                         if ((dszhp = zfs_open(zlibh, dataset_name,
+                             ZFS_TYPE_DATASET)) != NULL) {
+                                 if ((err = zfs_release(dszhp, snap_name,
+                                     NDMP_RCF_BASENAME, B_FALSE)) != 0) {
+                                         if (libzfs_errno(zlibh)
+                                             != EZFS_REFTAG_RELE) {
+                                                 syslog(LOG_DEBUG,
+                                                     "(%d) problem zfs_release "
+                                                     "error:%s action:"
+                                                     "%s errno:%d\n",
+                                                     err,
+                                                     libzfs_error_description(
+                                                     zlibh),
+                                                     libzfs_error_action(
+                                                     zlibh),
+                                                     libzfs_errno(
+                                                     zlibh));
+                                                 zfs_close(dszhp);
+                                                 goto _out;
+                                         }
+                                 }
+                                 if ((err = zfs_destroy(zhp, B_FALSE)) != 0) {
+                                         syslog(LOG_DEBUG,
+                                             "(%d)snapshot: problem zfs_destroy "
+                                             "error:%s action:%s errno:%d\n",
+                                             err,
+                                             libzfs_error_description(zlibh),
+                                             libzfs_error_action(zlibh),
+                                             libzfs_errno(zlibh));
+                                 }
+                                 zfs_close(dszhp);
+                         } else {
+                                 err = -1;
+                                 goto _out;
+                         }
+                 }
+         }
+ _out:
+         free(dataset_name);
+         zfs_close(zhp);
+         return (err);
+ }
+ 
+ /*
+  * This is the zpool_iter() callback routine specifically for
+  * ZFS_TYPE_FILESYSTEM and is passed in a zfs handle to each one
+  * it finds during iteration.  If this callback returns zero
+  * the iterator keeps going, if it returns non-sero the
+  * iteration stops.
+  *
+  * Parameters:
+  *   zhp (input) - the zfs handle of the ZFS_TYPE_FILESYSTEM dataset.
+  *   arg (input) - optional parameter (not used in this case)
+  *
+  * Returns:
+  *   0: on success
+  *  -1: otherwise
+  */
+ /*ARGSUSED*/
+ int
+ ndmp_match_and_destroy_filesystem(zfs_handle_t *zhp, void *arg)
+ {
+         int err = 0;
+         char *mntpt = NULL;
+         char *dataset_name;
+ 
+         dataset_name = strdup(zfs_get_name(zhp));
+         if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM)  {
+                 if (strstr(dataset_name, NDMP_RCF_BASENAME) != NULL) {
+ 
+                         syslog(LOG_DEBUG,
+                             "Remove filesystem [%s]", dataset_name);
+                         if (zfs_is_mounted(zhp, &mntpt)) {
+                                 syslog(LOG_DEBUG,
+                                     "mountpoint for snapshot is [%s]\n", mntpt);
+                                 if (zfs_unmount(zhp, NULL, MS_FORCE) != 0) {
+                                         syslog(LOG_DEBUG, "Failed to unmount "
+                                             "mount point [%s]", mntpt);
+                                         err = -1;
+                                         goto _out;
+                                 }
+                         }
+                         if (rmdir(mntpt) != 0) {
+                                 if (errno != ENOENT) {
+                                         syslog(LOG_DEBUG, "Failed to remove "
+                                             "mount point [%s]", mntpt);
+                                         err = -1;
+                                         goto _out;
+                                 }
+                         }
+ 
+                         if ((err = zfs_destroy(zhp, B_FALSE)) != 0) {
+                                 syslog(LOG_DEBUG,
+                                     "(%d)filesystem: problem zfs_destroy "
+                                     "error:%s action:%s errno:%d\n",
+                                     err, libzfs_error_description(zlibh),
+                                     libzfs_error_action(zlibh),
+                                     libzfs_errno(zlibh));
+                         }
+                 }
+         }
+ _out:
+         free(dataset_name);
+         zfs_close(zhp);
+         return (err);
+ }
+ 
+ /*
+  * This is the zpool iterator callback routine.  For each pool on
+  * the system iterate filesystem dependents first then iterate snapshot
+  * dependents and run the corresponding ndmp_match_and_destroy_XXX()
+  * callback. The 'snapshot' are removed second because 'filesystem'
+  * is dependend on its parent 'snapshot'.  If this callback returns
+  * zero the iterator keeps going, if it returns non-sero the
+  * iteration stops.
+  *
+  * Parameters:
+  *   zhp (input) - the zfs handle of the zpool dataset.
+  *   arg (input) - optional parameter (not used in this case)
+  *
+  * Returns:
+  *   0: on success
+  *  -1: otherwise
+  */
+ /*ARGSUSED*/
+ int
+ ndmp_cleanup_snapshots_inpool(zfs_handle_t *zhp, void *arg)
+ {
+         const char *zpool_name;
+         int err = 0;
+         zpool_handle_t *php;
+ 
+         /*
+          * Check for pools with bootfs entries and skip them
+          */
+         zpool_name = zfs_get_name(zhp);
+         if ((php = zpool_open(zlibh, zpool_name)) != NULL) {
+                 if (!ndmp_zpool_is_bootable(php)) {
+                         syslog(LOG_DEBUG,
+                             "Working on pool [%s]\n", zfs_get_name(zhp));
+ 
+                         err = zfs_iter_dependents(zhp, B_FALSE,
+                             ndmp_match_and_destroy_filesystem, (void *)NULL);
+                         if (err) {
+                                 syslog(LOG_ERR,
+                                     "cleanup filesystems error: "
+                                     "%d on pool [%s]",
+                                     err, zpool_name);
+                                 goto _out;
+                         }
+                         err = zfs_iter_dependents(zhp,
+                             B_FALSE, ndmp_match_and_destroy_snapshot,
+                             (void *)NULL);
+                         if (err) {
+                                 syslog(LOG_ERR,
+                                     "cleanup snapshots error: %d on pool",
+                                     err, zpool_name);
+                         }
+                 }
+         }
+ _out:
+         zpool_close(php);
+         zfs_close(zhp);
+         return (err);
+ }
+ 
+ /*
   * main
   *
   * The main NDMP daemon function
   *
   * Parameters:
*** 201,211 ****
          struct sigaction act;
          sigset_t set;
          char c;
          void *arg = NULL;
          boolean_t run_in_foreground = B_FALSE;
-         boolean_t override_debug = B_FALSE;
  
          /*
           * Check for existing ndmpd door server (make sure ndmpd is not already
           * running)
           */
--- 428,437 ----
*** 235,254 ****
          }
  
          opterr = 0;
          while ((c = getopt(argc, argv, "df")) != -1) {
                  switch (c) {
-                 case 'd':
-                         override_debug = B_TRUE;
-                         break;
                  case 'f':
                          run_in_foreground = B_TRUE;
                          break;
                  default:
                          (void) fprintf(stderr, "%s: Invalid option -%c.\n",
                              argv[0], optopt);
!                         (void) fprintf(stderr, "Usage: %s [-df]\n", argv[0]);
                          exit(SMF_EXIT_ERR_CONFIG);
                  }
          }
  
          /* set up signal handler */
--- 461,477 ----
          }
  
          opterr = 0;
          while ((c = getopt(argc, argv, "df")) != -1) {
                  switch (c) {
                  case 'f':
                          run_in_foreground = B_TRUE;
                          break;
                  default:
                          (void) fprintf(stderr, "%s: Invalid option -%c.\n",
                              argv[0], optopt);
!                         (void) fprintf(stderr, "Usage: %s [-f]\n", argv[0]);
                          exit(SMF_EXIT_ERR_CONFIG);
                  }
          }
  
          /* set up signal handler */
*** 269,315 ****
          (void) sigdelset(&set, SIGUSR1);
          (void) sigdelset(&set, SIGPIPE);
  
          set_privileges();
          (void) umask(077);
!         openlog(argv[0], LOG_PID | LOG_NDELAY, LOG_DAEMON);
  
-         /*
-          * Open log file before we detach from terminal in case that open
-          * fails and error message is printed to stderr.
-          */
-         if (ndmp_log_open_file(run_in_foreground, override_debug) != 0)
-                 exit(SMF_EXIT_ERR_FATAL);
- 
          if (!run_in_foreground)
                  daemonize_init();
  
-         (void) mutex_init(&ndmpd_zfs_fd_lock, 0, NULL);
- 
          if (mod_init() != 0) {
!                 NDMP_LOG(LOG_ERR, "Failed to load the plugin module.");
                  exit(SMF_EXIT_ERR_CONFIG);
          }
  
          /* libzfs init */
          if ((zlibh = libzfs_init()) == NULL) {
!                 NDMP_LOG(LOG_ERR, "Failed to initialize ZFS library.");
                  exit(SMF_EXIT_ERR_CONFIG);
          }
  
          /* initialize and start the door server */
          if (ndmp_door_init()) {
!                 NDMP_LOG(LOG_ERR, "Can not start ndmpd door server.");
                  exit(SMF_EXIT_ERR_CONFIG);
          }
  
          if (tlm_init() == -1) {
!                 NDMP_LOG(LOG_ERR, "Failed to initialize tape manager.");
                  exit(SMF_EXIT_ERR_CONFIG);
          }
  
          /*
           * Prior to this point, we are single-threaded. We will be
           * multi-threaded from this point on.
           */
          (void) pthread_create(NULL, NULL, (funct_t)ndmpd_main,
              (void *)&arg);
--- 492,539 ----
          (void) sigdelset(&set, SIGUSR1);
          (void) sigdelset(&set, SIGPIPE);
  
          set_privileges();
          (void) umask(077);
!         openlog(argv[0], LOG_PID | LOG_NDELAY, LOG_LOCAL4);
  
          if (!run_in_foreground)
                  daemonize_init();
  
          if (mod_init() != 0) {
!                 syslog(LOG_ERR, "Failed to load the plugin module.");
                  exit(SMF_EXIT_ERR_CONFIG);
          }
  
          /* libzfs init */
          if ((zlibh = libzfs_init()) == NULL) {
!                 syslog(LOG_ERR, "Failed to initialize ZFS library.");
                  exit(SMF_EXIT_ERR_CONFIG);
          }
  
          /* initialize and start the door server */
          if (ndmp_door_init()) {
!                 syslog(LOG_ERR, "Can not start ndmpd door server.");
                  exit(SMF_EXIT_ERR_CONFIG);
          }
  
          if (tlm_init() == -1) {
!                 syslog(LOG_ERR, "Failed to initialize tape manager.");
                  exit(SMF_EXIT_ERR_CONFIG);
          }
  
          /*
+          * Use libzfs iterator routine to list through all the pools and
+          * invoke cleanup callback routine on each.
+          */
+         if (zfs_iter_root(zlibh,
+             ndmp_cleanup_snapshots_inpool, (void *)NULL) != 0) {
+                 syslog(LOG_ERR, "Failed to cleanup leftover snapshots.");
+                 exit(SMF_EXIT_ERR_CONFIG);
+         }
+ 
+         /*
           * Prior to this point, we are single-threaded. We will be
           * multi-threaded from this point on.
           */
          (void) pthread_create(NULL, NULL, (funct_t)ndmpd_main,
              (void *)&arg);
*** 325,335 ****
                          break;
  
                  case SIGHUP:
                          /* Refresh SMF properties */
                          if (ndmpd_load_prop())
!                                 NDMP_LOG(LOG_ERR,
                                      "Service properties initialization "
                                      "failed.");
                          break;
  
                  default:
--- 549,559 ----
                          break;
  
                  case SIGHUP:
                          /* Refresh SMF properties */
                          if (ndmpd_load_prop())
!                                 syslog(LOG_ERR,
                                      "Service properties initialization "
                                      "failed.");
                          break;
  
                  default:
*** 341,355 ****
                  }
  
                  ndmpd.s_sigval = 0;
          }
  
-         (void) mutex_destroy(&ndmpd_zfs_fd_lock);
          libzfs_fini(zlibh);
          mod_fini();
          ndmp_door_fini();
!         ndmp_log_close_file();
  
          return (SMF_EXIT_OK);
  }
  
  static void
--- 565,578 ----
                  }
  
                  ndmpd.s_sigval = 0;
          }
  
          libzfs_fini(zlibh);
          mod_fini();
          ndmp_door_fini();
!         closelog();
  
          return (SMF_EXIT_OK);
  }
  
  static void