1 /*
   2  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
   4  */
   5 
   6 /*
   7  * BSD 3 Clause License
   8  *
   9  * Copyright (c) 2007, The Storage Networking Industry Association.
  10  *
  11  * Redistribution and use in source and binary forms, with or without
  12  * modification, are permitted provided that the following conditions
  13  * are met:
  14  *      - Redistributions of source code must retain the above copyright
  15  *        notice, this list of conditions and the following disclaimer.
  16  *
  17  *      - Redistributions in binary form must reproduce the above copyright
  18  *        notice, this list of conditions and the following disclaimer in
  19  *        the documentation and/or other materials provided with the
  20  *        distribution.
  21  *
  22  *      - Neither the name of The Storage Networking Industry Association (SNIA)
  23  *        nor the names of its contributors may be used to endorse or promote
  24  *        products derived from this software without specific prior written
  25  *        permission.
  26  *
  27  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  37  * POSSIBILITY OF SUCH DAMAGE.
  38  */
  39 /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
  40 
  41 #include <errno.h>
  42 #include <signal.h>
  43 #include <libscf.h>
  44 #include <libintl.h>
  45 #include <sys/wait.h>
  46 #include <syslog.h>
  47 #include <syslog.h>
  48 #include <zone.h>
  49 #include <tsol/label.h>
  50 #include <dlfcn.h>
  51 #include <sys/mount.h>
  52 #include <libzfs.h>
  53 #include "ndmpd.h"
  54 #include "ndmpd_common.h"
  55 
  56 /* zfs library handle & mutex */
  57 libzfs_handle_t *zlibh;
  58 mutex_t zlib_mtx;
  59 void *mod_plp;
  60 
  61 static void ndmpd_sig_handler(int sig);
  62 
  63 typedef struct ndmpd {
  64         int s_shutdown_flag;    /* Fields for shutdown control */
  65         int s_sigval;
  66 } ndmpd_t;
  67 
  68 ndmpd_t ndmpd;
  69 
  70 
  71 /*
  72  * Load and initialize the plug-in module
  73  */
  74 static int
  75 mod_init()
  76 {
  77         char *plname;
  78         ndmp_plugin_t *(*plugin_init)(int);
  79 
  80         ndmp_pl = NULL;
  81 
  82         plname = ndmpd_get_prop(NDMP_PLUGIN_PATH);
  83         if (plname == NULL || *plname == '\0')
  84                 return (0);
  85 
  86         if ((mod_plp = dlopen(plname, RTLD_LOCAL | RTLD_NOW)) == NULL) {
  87                 syslog(LOG_ERR, "Error loading the plug-in %s: %s",
  88                     plname, dlerror());
  89                 return (0);
  90         }
  91 
  92         plugin_init = (ndmp_plugin_t *(*)(int))dlsym(mod_plp, "_ndmp_init");
  93         if (plugin_init == NULL) {
  94                 (void) dlclose(mod_plp);
  95                 return (0);
  96         }
  97         if ((ndmp_pl = plugin_init(NDMP_PLUGIN_VERSION)) == NULL) {
  98                 syslog(LOG_ERR, "Error loading the plug-in %s", plname);
  99                 return (-1);
 100         }
 101         return (0);
 102 }
 103 
 104 /*
 105  * Unload
 106  */
 107 static void
 108 mod_fini()
 109 {
 110         if (ndmp_pl == NULL)
 111                 return;
 112 
 113         void (*plugin_fini)(ndmp_plugin_t *);
 114 
 115         plugin_fini = (void (*)(ndmp_plugin_t *))dlsym(mod_plp, "_ndmp_fini");
 116         if (plugin_fini == NULL) {
 117                 (void) dlclose(mod_plp);
 118                 return;
 119         }
 120         plugin_fini(ndmp_pl);
 121         (void) dlclose(mod_plp);
 122 }
 123 
 124 static void
 125 set_privileges(void)
 126 {
 127         priv_set_t *pset = priv_allocset();
 128 
 129         /*
 130          * Set effective sets privileges to 'least' required. If fails, send
 131          * error messages to log file and proceed.
 132          */
 133         if (pset != NULL) {
 134                 priv_basicset(pset);
 135                 (void) priv_addset(pset, PRIV_PROC_AUDIT);
 136                 (void) priv_addset(pset, PRIV_PROC_SETID);
 137                 (void) priv_addset(pset, PRIV_PROC_OWNER);
 138                 (void) priv_addset(pset, PRIV_FILE_CHOWN);
 139                 (void) priv_addset(pset, PRIV_FILE_CHOWN_SELF);
 140                 (void) priv_addset(pset, PRIV_FILE_DAC_READ);
 141                 (void) priv_addset(pset, PRIV_FILE_DAC_SEARCH);
 142                 (void) priv_addset(pset, PRIV_FILE_DAC_WRITE);
 143                 (void) priv_addset(pset, PRIV_FILE_OWNER);
 144                 (void) priv_addset(pset, PRIV_FILE_SETID);
 145                 (void) priv_addset(pset, PRIV_SYS_LINKDIR);
 146                 (void) priv_addset(pset, PRIV_SYS_DEVICES);
 147                 (void) priv_addset(pset, PRIV_SYS_MOUNT);
 148                 (void) priv_addset(pset, PRIV_SYS_CONFIG);
 149         }
 150 
 151         if (pset == NULL || setppriv(PRIV_SET, PRIV_EFFECTIVE, pset) != 0) {
 152                 (void) fprintf(stderr,
 153                     "Failed to set least required privileges to the service\n");
 154         }
 155         priv_freeset(pset);
 156 }
 157 
 158 static void
 159 daemonize_init(void)
 160 {
 161         sigset_t set, oset;
 162         pid_t pid;
 163 
 164         /*
 165          * Block all signals prior to the fork and leave them blocked in the
 166          * parent so we don't get in a situation where the parent gets SIGINT
 167          * and returns non-zero exit status and the child is actually running.
 168          * In the child, restore the signal mask once we've done our setsid().
 169          */
 170         (void) sigfillset(&set);
 171         (void) sigdelset(&set, SIGABRT);
 172         (void) sigprocmask(SIG_BLOCK, &set, &oset);
 173 
 174         if ((pid = fork()) == -1) {
 175                 (void) fprintf(stderr,
 176                     "Failed to start process in background.\n");
 177                 exit(SMF_EXIT_ERR_CONFIG);
 178         }
 179 
 180         /* If we're the parent process, exit. */
 181         if (pid != 0) {
 182                 _exit(0);
 183         }
 184         (void) setsid();
 185         (void) sigprocmask(SIG_SETMASK, &oset, NULL);
 186         (void) chdir("/");
 187 }
 188 
 189 /*
 190  * Utility routine to check if a zpool is bootable. For the purposes
 191  * of cleaning up ndmp backup clones and snapshots, shouldn't consider
 192  * the 'boot' volume.
 193  *
 194  * Parameters:
 195  *   zhp (input) - the zfs handle of the zpool dataset.
 196  *
 197  * Returns:
 198  *   B_TRUE : If the given zpool has a boot record
 199  *   B_FALSE: otherwise
 200  */
 201 boolean_t
 202 ndmp_zpool_is_bootable(zpool_handle_t *zhp)
 203 {
 204         char bootfs[ZFS_MAX_DATASET_NAME_LEN];
 205 
 206         return (zpool_get_prop(zhp, ZPOOL_PROP_BOOTFS, bootfs,
 207             sizeof (bootfs), NULL, B_FALSE) == 0 && strncmp(bootfs, "-",
 208             sizeof (bootfs)) != 0);
 209 }
 210 
 211 /*
 212  * This is the zpool_iter() callback routine specifically for
 213  * ZFS_TYPE_SNAPSHOTS and is passed in a zfs handle to each one
 214  * it finds during iteration.  If this callback returns zero
 215  * the iterator keeps going, if it returns non-sero the
 216  * iteration stops.
 217  *
 218  * Parameters:
 219  *   zhp (input) - the zfs handle of the ZFS_TYPE_SNAPSHOTS dataset.
 220  *   arg (input) - optional parameter (not used in this case)
 221  *
 222  * Returns:
 223  *   0: on success
 224  *  -1: otherwise
 225  */
 226 /*ARGSUSED*/
 227 int
 228 ndmp_match_and_destroy_snapshot(zfs_handle_t *zhp, void *arg)
 229 {
 230         int err = 0;
 231         char *dataset_name;
 232         char *snap_name;
 233         char *snap_delim;
 234         zfs_handle_t *dszhp;
 235 
 236         dataset_name = strdup(zfs_get_name(zhp));
 237         if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) {
 238                 if (strstr(dataset_name, NDMP_RCF_BASENAME) != NULL) {
 239                         snap_delim = strchr(dataset_name, '@');
 240                         snap_name = snap_delim + 1;
 241                         *snap_delim = '\0';
 242 
 243                         syslog(LOG_DEBUG,
 244                             "Remove snap [%s] from dataset [%s] tag [%s]\n",
 245                             snap_name, dataset_name, NDMP_RCF_BASENAME);
 246 
 247                         if ((dszhp = zfs_open(zlibh, dataset_name,
 248                             ZFS_TYPE_DATASET)) != NULL) {
 249                                 if ((err = zfs_release(dszhp, snap_name,
 250                                     NDMP_RCF_BASENAME, B_FALSE)) != 0) {
 251                                         if (libzfs_errno(zlibh)
 252                                             != EZFS_REFTAG_RELE) {
 253                                                 syslog(LOG_DEBUG,
 254                                                     "(%d) problem zfs_release "
 255                                                     "error:%s action:"
 256                                                     "%s errno:%d\n",
 257                                                     err,
 258                                                     libzfs_error_description(
 259                                                     zlibh),
 260                                                     libzfs_error_action(
 261                                                     zlibh),
 262                                                     libzfs_errno(
 263                                                     zlibh));
 264                                                 zfs_close(dszhp);
 265                                                 goto _out;
 266                                         }
 267                                 }
 268                                 if ((err = zfs_destroy(zhp, B_FALSE)) != 0) {
 269                                         syslog(LOG_DEBUG,
 270                                             "(%d)snapshot: problem zfs_destroy "
 271                                             "error:%s action:%s errno:%d\n",
 272                                             err,
 273                                             libzfs_error_description(zlibh),
 274                                             libzfs_error_action(zlibh),
 275                                             libzfs_errno(zlibh));
 276                                 }
 277                                 zfs_close(dszhp);
 278                         } else {
 279                                 err = -1;
 280                                 goto _out;
 281                         }
 282                 }
 283         }
 284 _out:
 285         free(dataset_name);
 286         zfs_close(zhp);
 287         return (err);
 288 }
 289 
 290 /*
 291  * This is the zpool_iter() callback routine specifically for
 292  * ZFS_TYPE_FILESYSTEM and is passed in a zfs handle to each one
 293  * it finds during iteration.  If this callback returns zero
 294  * the iterator keeps going, if it returns non-sero the
 295  * iteration stops.
 296  *
 297  * Parameters:
 298  *   zhp (input) - the zfs handle of the ZFS_TYPE_FILESYSTEM dataset.
 299  *   arg (input) - optional parameter (not used in this case)
 300  *
 301  * Returns:
 302  *   0: on success
 303  *  -1: otherwise
 304  */
 305 /*ARGSUSED*/
 306 int
 307 ndmp_match_and_destroy_filesystem(zfs_handle_t *zhp, void *arg)
 308 {
 309         int err = 0;
 310         char *mntpt = NULL;
 311         char *dataset_name;
 312 
 313         dataset_name = strdup(zfs_get_name(zhp));
 314         if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM)  {
 315                 if (strstr(dataset_name, NDMP_RCF_BASENAME) != NULL) {
 316 
 317                         syslog(LOG_DEBUG,
 318                             "Remove filesystem [%s]", dataset_name);
 319                         if (zfs_is_mounted(zhp, &mntpt)) {
 320                                 syslog(LOG_DEBUG,
 321                                     "mountpoint for snapshot is [%s]\n", mntpt);
 322                                 if (zfs_unmount(zhp, NULL, MS_FORCE) != 0) {
 323                                         syslog(LOG_DEBUG, "Failed to unmount "
 324                                             "mount point [%s]", mntpt);
 325                                         err = -1;
 326                                         goto _out;
 327                                 }
 328                         }
 329                         if (rmdir(mntpt) != 0) {
 330                                 if (errno != ENOENT) {
 331                                         syslog(LOG_DEBUG, "Failed to remove "
 332                                             "mount point [%s]", mntpt);
 333                                         err = -1;
 334                                         goto _out;
 335                                 }
 336                         }
 337 
 338                         if ((err = zfs_destroy(zhp, B_FALSE)) != 0) {
 339                                 syslog(LOG_DEBUG,
 340                                     "(%d)filesystem: problem zfs_destroy "
 341                                     "error:%s action:%s errno:%d\n",
 342                                     err, libzfs_error_description(zlibh),
 343                                     libzfs_error_action(zlibh),
 344                                     libzfs_errno(zlibh));
 345                         }
 346                 }
 347         }
 348 _out:
 349         free(dataset_name);
 350         zfs_close(zhp);
 351         return (err);
 352 }
 353 
 354 /*
 355  * This is the zpool iterator callback routine.  For each pool on
 356  * the system iterate filesystem dependents first then iterate snapshot
 357  * dependents and run the corresponding ndmp_match_and_destroy_XXX()
 358  * callback. The 'snapshot' are removed second because 'filesystem'
 359  * is dependend on its parent 'snapshot'.  If this callback returns
 360  * zero the iterator keeps going, if it returns non-sero the
 361  * iteration stops.
 362  *
 363  * Parameters:
 364  *   zhp (input) - the zfs handle of the zpool dataset.
 365  *   arg (input) - optional parameter (not used in this case)
 366  *
 367  * Returns:
 368  *   0: on success
 369  *  -1: otherwise
 370  */
 371 /*ARGSUSED*/
 372 int
 373 ndmp_cleanup_snapshots_inpool(zfs_handle_t *zhp, void *arg)
 374 {
 375         const char *zpool_name;
 376         int err = 0;
 377         zpool_handle_t *php;
 378 
 379         /*
 380          * Check for pools with bootfs entries and skip them
 381          */
 382         zpool_name = zfs_get_name(zhp);
 383         if ((php = zpool_open(zlibh, zpool_name)) != NULL) {
 384                 if (!ndmp_zpool_is_bootable(php)) {
 385                         syslog(LOG_DEBUG,
 386                             "Working on pool [%s]\n", zfs_get_name(zhp));
 387 
 388                         err = zfs_iter_dependents(zhp, B_FALSE,
 389                             ndmp_match_and_destroy_filesystem, (void *)NULL);
 390                         if (err) {
 391                                 syslog(LOG_ERR,
 392                                     "cleanup filesystems error: "
 393                                     "%d on pool [%s]",
 394                                     err, zpool_name);
 395                                 goto _out;
 396                         }
 397                         err = zfs_iter_dependents(zhp,
 398                             B_FALSE, ndmp_match_and_destroy_snapshot,
 399                             (void *)NULL);
 400                         if (err) {
 401                                 syslog(LOG_ERR,
 402                                     "cleanup snapshots error: %d on pool",
 403                                     err, zpool_name);
 404                         }
 405                 }
 406         }
 407 _out:
 408         zpool_close(php);
 409         zfs_close(zhp);
 410         return (err);
 411 }
 412 
 413 /*
 414  * main
 415  *
 416  * The main NDMP daemon function
 417  *
 418  * Parameters:
 419  *   argc (input) - the argument count
 420  *   argv (input) - command line options
 421  *
 422  * Returns:
 423  *   0
 424  */
 425 int
 426 main(int argc, char *argv[])
 427 {
 428         struct sigaction act;
 429         sigset_t set;
 430         char c;
 431         void *arg = NULL;
 432         boolean_t run_in_foreground = B_FALSE;
 433 
 434         /*
 435          * Check for existing ndmpd door server (make sure ndmpd is not already
 436          * running)
 437          */
 438         if (ndmp_door_check()) {
 439                 /* ndmpd is already running, exit. */
 440                 (void) fprintf(stderr, "ndmpd is already running.\n");
 441                 return (0);
 442         }
 443 
 444         /* Global zone check */
 445         if (getzoneid() != GLOBAL_ZONEID) {
 446                 (void) fprintf(stderr, "Non-global zone not supported.\n");
 447                 exit(SMF_EXIT_ERR_FATAL);
 448         }
 449 
 450         /* Trusted Solaris check */
 451         if (is_system_labeled()) {
 452                 (void) fprintf(stderr, "Trusted Solaris not supported.\n");
 453                 exit(SMF_EXIT_ERR_FATAL);
 454         }
 455 
 456         /* load SMF configuration */
 457         if (ndmpd_load_prop()) {
 458                 (void) fprintf(stderr,
 459                     "SMF properties initialization failed.\n");
 460                 exit(SMF_EXIT_ERR_CONFIG);
 461         }
 462 
 463         opterr = 0;
 464         while ((c = getopt(argc, argv, "df")) != -1) {
 465                 switch (c) {
 466                 case 'f':
 467                         run_in_foreground = B_TRUE;
 468                         break;
 469                 default:
 470                         (void) fprintf(stderr, "%s: Invalid option -%c.\n",
 471                             argv[0], optopt);
 472                         (void) fprintf(stderr, "Usage: %s [-f]\n", argv[0]);
 473                         exit(SMF_EXIT_ERR_CONFIG);
 474                 }
 475         }
 476 
 477         /* set up signal handler */
 478         (void) sigfillset(&set);
 479         (void) sigdelset(&set, SIGABRT); /* always unblocked for ASSERT() */
 480         (void) sigfillset(&act.sa_mask);
 481         act.sa_handler = ndmpd_sig_handler;
 482         act.sa_flags = 0;
 483 
 484         (void) sigaction(SIGTERM, &act, NULL);
 485         (void) sigaction(SIGHUP, &act, NULL);
 486         (void) sigaction(SIGINT, &act, NULL);
 487         (void) sigaction(SIGUSR1, &act, NULL);
 488         (void) sigaction(SIGPIPE, &act, NULL);
 489         (void) sigdelset(&set, SIGTERM);
 490         (void) sigdelset(&set, SIGHUP);
 491         (void) sigdelset(&set, SIGINT);
 492         (void) sigdelset(&set, SIGUSR1);
 493         (void) sigdelset(&set, SIGPIPE);
 494 
 495         set_privileges();
 496         (void) umask(077);
 497         openlog(argv[0], LOG_PID | LOG_NDELAY, LOG_LOCAL4);
 498 
 499         if (!run_in_foreground)
 500                 daemonize_init();
 501 
 502         if (mod_init() != 0) {
 503                 syslog(LOG_ERR, "Failed to load the plugin module.");
 504                 exit(SMF_EXIT_ERR_CONFIG);
 505         }
 506 
 507         /* libzfs init */
 508         if ((zlibh = libzfs_init()) == NULL) {
 509                 syslog(LOG_ERR, "Failed to initialize ZFS library.");
 510                 exit(SMF_EXIT_ERR_CONFIG);
 511         }
 512 
 513         /* initialize and start the door server */
 514         if (ndmp_door_init()) {
 515                 syslog(LOG_ERR, "Can not start ndmpd door server.");
 516                 exit(SMF_EXIT_ERR_CONFIG);
 517         }
 518 
 519         if (tlm_init() == -1) {
 520                 syslog(LOG_ERR, "Failed to initialize tape manager.");
 521                 exit(SMF_EXIT_ERR_CONFIG);
 522         }
 523 
 524         /*
 525          * Use libzfs iterator routine to list through all the pools and
 526          * invoke cleanup callback routine on each.
 527          */
 528         if (zfs_iter_root(zlibh,
 529             ndmp_cleanup_snapshots_inpool, (void *)NULL) != 0) {
 530                 syslog(LOG_ERR, "Failed to cleanup leftover snapshots.");
 531                 exit(SMF_EXIT_ERR_CONFIG);
 532         }
 533 
 534         /*
 535          * Prior to this point, we are single-threaded. We will be
 536          * multi-threaded from this point on.
 537          */
 538         (void) pthread_create(NULL, NULL, (funct_t)ndmpd_main,
 539             (void *)&arg);
 540 
 541         while (!ndmpd.s_shutdown_flag) {
 542                 (void) sigsuspend(&set);
 543 
 544                 switch (ndmpd.s_sigval) {
 545                 case 0:
 546                         break;
 547 
 548                 case SIGPIPE:
 549                         break;
 550 
 551                 case SIGHUP:
 552                         /* Refresh SMF properties */
 553                         if (ndmpd_load_prop())
 554                                 syslog(LOG_ERR,
 555                                     "Service properties initialization "
 556                                     "failed.");
 557                         break;
 558 
 559                 default:
 560                         /*
 561                          * Typically SIGINT or SIGTERM.
 562                          */
 563                         ndmpd.s_shutdown_flag = 1;
 564                         break;
 565                 }
 566 
 567                 ndmpd.s_sigval = 0;
 568         }
 569 
 570         libzfs_fini(zlibh);
 571         mod_fini();
 572         ndmp_door_fini();
 573         closelog();
 574 
 575         return (SMF_EXIT_OK);
 576 }
 577 
 578 static void
 579 ndmpd_sig_handler(int sig)
 580 {
 581         if (ndmpd.s_sigval == 0)
 582                 ndmpd.s_sigval = sig;
 583 }
 584 
 585 /*
 586  * Enable libumem debugging by default on DEBUG builds.
 587  */
 588 #ifdef DEBUG
 589 const char *
 590 _umem_debug_init(void)
 591 {
 592         return ("default,verbose"); /* $UMEM_DEBUG setting */
 593 }
 594 
 595 const char *
 596 _umem_logging_init(void)
 597 {
 598         return ("fail,contents"); /* $UMEM_LOGGING setting */
 599 }
 600 #endif