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 +1,8 @@
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
  * BSD 3 Clause License
  *

@@ -38,17 +38,20 @@
  */
 /* 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 <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,11 +82,11 @@
         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",
+                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,11 +93,11 @@
         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);
+                syslog(LOG_ERR, "Error loading the plug-in %s", plname);
                 return (-1);
         }
         return (0);
 }
 

@@ -182,10 +185,234 @@
         (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,11 +428,10 @@
         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)
          */

@@ -235,20 +461,17 @@
         }
 
         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]);
+                        (void) fprintf(stderr, "Usage: %s [-f]\n", argv[0]);
                         exit(SMF_EXIT_ERR_CONFIG);
                 }
         }
 
         /* set up signal handler */

@@ -269,47 +492,48 @@
         (void) sigdelset(&set, SIGUSR1);
         (void) sigdelset(&set, SIGPIPE);
 
         set_privileges();
         (void) umask(077);
-        openlog(argv[0], LOG_PID | LOG_NDELAY, LOG_DAEMON);
+        openlog(argv[0], LOG_PID | LOG_NDELAY, LOG_LOCAL4);
 
-        /*
-         * 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.");
+                syslog(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.");
+                syslog(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.");
+                syslog(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.");
+                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,11 +549,11 @@
                         break;
 
                 case SIGHUP:
                         /* Refresh SMF properties */
                         if (ndmpd_load_prop())
-                                NDMP_LOG(LOG_ERR,
+                                syslog(LOG_ERR,
                                     "Service properties initialization "
                                     "failed.");
                         break;
 
                 default:

@@ -341,15 +565,14 @@
                 }
 
                 ndmpd.s_sigval = 0;
         }
 
-        (void) mutex_destroy(&ndmpd_zfs_fd_lock);
         libzfs_fini(zlibh);
         mod_fini();
         ndmp_door_fini();
-        ndmp_log_close_file();
+        closelog();
 
         return (SMF_EXIT_OK);
 }
 
 static void