1 /*
   2  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright 2014 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 <libgen.h>
  44 #include <libscf.h>
  45 #include <libintl.h>
  46 #include <sys/wait.h>
  47 #include <zone.h>
  48 #include <tsol/label.h>
  49 #include <dlfcn.h>
  50 #include "ndmpd.h"
  51 #include "ndmpd_common.h"
  52 
  53 /* zfs library handle & mutex */
  54 libzfs_handle_t *zlibh;
  55 mutex_t zlib_mtx;
  56 void *mod_plp;
  57 
  58 static void ndmpd_sig_handler(int sig);
  59 
  60 typedef struct ndmpd {
  61         int s_shutdown_flag;    /* Fields for shutdown control */
  62         int s_sigval;
  63 } ndmpd_t;
  64 
  65 ndmpd_t ndmpd;
  66 
  67 
  68 /*
  69  * Load and initialize the plug-in module
  70  */
  71 static int
  72 mod_init()
  73 {
  74         char *plname;
  75         ndmp_plugin_t *(*plugin_init)(int);
  76 
  77         ndmp_pl = NULL;
  78 
  79         plname = ndmpd_get_prop(NDMP_PLUGIN_PATH);
  80         if (plname == NULL || *plname == '\0')
  81                 return (0);
  82 
  83         if ((mod_plp = dlopen(plname, RTLD_LOCAL | RTLD_NOW)) == NULL) {
  84                 NDMP_LOG(LOG_ERR, "Error loading the plug-in %s: %s",
  85                     plname, dlerror());
  86                 return (0);
  87         }
  88 
  89         plugin_init = (ndmp_plugin_t *(*)(int))dlsym(mod_plp, "_ndmp_init");
  90         if (plugin_init == NULL) {
  91                 (void) dlclose(mod_plp);
  92                 return (0);
  93         }
  94         if ((ndmp_pl = plugin_init(NDMP_PLUGIN_VERSION)) == NULL) {
  95                 NDMP_LOG(LOG_ERR, "Error loading the plug-in %s", plname);
  96                 return (-1);
  97         }
  98         return (0);
  99 }
 100 
 101 /*
 102  * Unload
 103  */
 104 static void
 105 mod_fini()
 106 {
 107         if (ndmp_pl == NULL)
 108                 return;
 109 
 110         void (*plugin_fini)(ndmp_plugin_t *);
 111 
 112         plugin_fini = (void (*)(ndmp_plugin_t *))dlsym(mod_plp, "_ndmp_fini");
 113         if (plugin_fini == NULL) {
 114                 (void) dlclose(mod_plp);
 115                 return;
 116         }
 117         plugin_fini(ndmp_pl);
 118         (void) dlclose(mod_plp);
 119 }
 120 
 121 static void
 122 set_privileges(void)
 123 {
 124         priv_set_t *pset = priv_allocset();
 125 
 126         /*
 127          * Set effective sets privileges to 'least' required. If fails, send
 128          * error messages to log file and proceed.
 129          */
 130         if (pset != NULL) {
 131                 priv_basicset(pset);
 132                 (void) priv_addset(pset, PRIV_PROC_AUDIT);
 133                 (void) priv_addset(pset, PRIV_PROC_SETID);
 134                 (void) priv_addset(pset, PRIV_PROC_OWNER);
 135                 (void) priv_addset(pset, PRIV_FILE_CHOWN);
 136                 (void) priv_addset(pset, PRIV_FILE_CHOWN_SELF);
 137                 (void) priv_addset(pset, PRIV_FILE_DAC_READ);
 138                 (void) priv_addset(pset, PRIV_FILE_DAC_SEARCH);
 139                 (void) priv_addset(pset, PRIV_FILE_DAC_WRITE);
 140                 (void) priv_addset(pset, PRIV_FILE_OWNER);
 141                 (void) priv_addset(pset, PRIV_FILE_SETID);
 142                 (void) priv_addset(pset, PRIV_SYS_LINKDIR);
 143                 (void) priv_addset(pset, PRIV_SYS_DEVICES);
 144                 (void) priv_addset(pset, PRIV_SYS_MOUNT);
 145                 (void) priv_addset(pset, PRIV_SYS_CONFIG);
 146         }
 147 
 148         if (pset == NULL || setppriv(PRIV_SET, PRIV_EFFECTIVE, pset) != 0) {
 149                 (void) fprintf(stderr,
 150                     "Failed to set least required privileges to the service\n");
 151         }
 152         priv_freeset(pset);
 153 }
 154 
 155 static void
 156 daemonize_init(void)
 157 {
 158         sigset_t set, oset;
 159         pid_t pid;
 160 
 161         /*
 162          * Block all signals prior to the fork and leave them blocked in the
 163          * parent so we don't get in a situation where the parent gets SIGINT
 164          * and returns non-zero exit status and the child is actually running.
 165          * In the child, restore the signal mask once we've done our setsid().
 166          */
 167         (void) sigfillset(&set);
 168         (void) sigdelset(&set, SIGABRT);
 169         (void) sigprocmask(SIG_BLOCK, &set, &oset);
 170 
 171         if ((pid = fork()) == -1) {
 172                 (void) fprintf(stderr,
 173                     "Failed to start process in background.\n");
 174                 exit(SMF_EXIT_ERR_CONFIG);
 175         }
 176 
 177         /* If we're the parent process, exit. */
 178         if (pid != 0) {
 179                 _exit(0);
 180         }
 181         (void) setsid();
 182         (void) sigprocmask(SIG_SETMASK, &oset, NULL);
 183         (void) chdir("/");
 184 }
 185 
 186 /*
 187  * main
 188  *
 189  * The main NDMP daemon function
 190  *
 191  * Parameters:
 192  *   argc (input) - the argument count
 193  *   argv (input) - command line options
 194  *
 195  * Returns:
 196  *   0
 197  */
 198 int
 199 main(int argc, char *argv[])
 200 {
 201         struct sigaction act;
 202         sigset_t set;
 203         char c;
 204         void *arg = NULL;
 205         boolean_t run_in_foreground = B_FALSE;
 206         boolean_t override_debug = B_FALSE;
 207 
 208         /*
 209          * Check for existing ndmpd door server (make sure ndmpd is not already
 210          * running)
 211          */
 212         if (ndmp_door_check()) {
 213                 /* ndmpd is already running, exit. */
 214                 (void) fprintf(stderr, "ndmpd is already running.\n");
 215                 return (0);
 216         }
 217 
 218         /* Global zone check */
 219         if (getzoneid() != GLOBAL_ZONEID) {
 220                 (void) fprintf(stderr, "Non-global zone not supported.\n");
 221                 exit(SMF_EXIT_ERR_FATAL);
 222         }
 223 
 224         /* Trusted Solaris check */
 225         if (is_system_labeled()) {
 226                 (void) fprintf(stderr, "Trusted Solaris not supported.\n");
 227                 exit(SMF_EXIT_ERR_FATAL);
 228         }
 229 
 230         /* load SMF configuration */
 231         if (ndmpd_load_prop()) {
 232                 (void) fprintf(stderr,
 233                     "SMF properties initialization failed.\n");
 234                 exit(SMF_EXIT_ERR_CONFIG);
 235         }
 236 
 237         opterr = 0;
 238         while ((c = getopt(argc, argv, "df")) != -1) {
 239                 switch (c) {
 240                 case 'd':
 241                         override_debug = B_TRUE;
 242                         break;
 243                 case 'f':
 244                         run_in_foreground = B_TRUE;
 245                         break;
 246                 default:
 247                         (void) fprintf(stderr, "%s: Invalid option -%c.\n",
 248                             argv[0], optopt);
 249                         (void) fprintf(stderr, "Usage: %s [-df]\n", argv[0]);
 250                         exit(SMF_EXIT_ERR_CONFIG);
 251                 }
 252         }
 253 
 254         /* set up signal handler */
 255         (void) sigfillset(&set);
 256         (void) sigdelset(&set, SIGABRT); /* always unblocked for ASSERT() */
 257         (void) sigfillset(&act.sa_mask);
 258         act.sa_handler = ndmpd_sig_handler;
 259         act.sa_flags = 0;
 260 
 261         (void) sigaction(SIGTERM, &act, NULL);
 262         (void) sigaction(SIGHUP, &act, NULL);
 263         (void) sigaction(SIGINT, &act, NULL);
 264         (void) sigaction(SIGUSR1, &act, NULL);
 265         (void) sigaction(SIGPIPE, &act, NULL);
 266         (void) sigdelset(&set, SIGTERM);
 267         (void) sigdelset(&set, SIGHUP);
 268         (void) sigdelset(&set, SIGINT);
 269         (void) sigdelset(&set, SIGUSR1);
 270         (void) sigdelset(&set, SIGPIPE);
 271 
 272         set_privileges();
 273         (void) umask(077);
 274         openlog(argv[0], LOG_PID | LOG_NDELAY, LOG_DAEMON);
 275 
 276         /*
 277          * Open log file before we detach from terminal in case that open
 278          * fails and error message is printed to stderr.
 279          */
 280         if (ndmp_log_open_file(run_in_foreground, override_debug) != 0)
 281                 exit(SMF_EXIT_ERR_FATAL);
 282 
 283         if (!run_in_foreground)
 284                 daemonize_init();
 285 
 286         (void) mutex_init(&ndmpd_zfs_fd_lock, 0, NULL);
 287 
 288         if (mod_init() != 0) {
 289                 NDMP_LOG(LOG_ERR, "Failed to load the plugin module.");
 290                 exit(SMF_EXIT_ERR_CONFIG);
 291         }
 292 
 293         /* libzfs init */
 294         if ((zlibh = libzfs_init()) == NULL) {
 295                 NDMP_LOG(LOG_ERR, "Failed to initialize ZFS library.");
 296                 exit(SMF_EXIT_ERR_CONFIG);
 297         }
 298 
 299         /* initialize and start the door server */
 300         if (ndmp_door_init()) {
 301                 NDMP_LOG(LOG_ERR, "Can not start ndmpd door server.");
 302                 exit(SMF_EXIT_ERR_CONFIG);
 303         }
 304 
 305         if (tlm_init() == -1) {
 306                 NDMP_LOG(LOG_ERR, "Failed to initialize tape manager.");
 307                 exit(SMF_EXIT_ERR_CONFIG);
 308         }
 309 
 310         /*
 311          * Prior to this point, we are single-threaded. We will be
 312          * multi-threaded from this point on.
 313          */
 314         (void) pthread_create(NULL, NULL, (funct_t)ndmpd_main,
 315             (void *)&arg);
 316 
 317         while (!ndmpd.s_shutdown_flag) {
 318                 (void) sigsuspend(&set);
 319 
 320                 switch (ndmpd.s_sigval) {
 321                 case 0:
 322                         break;
 323 
 324                 case SIGPIPE:
 325                         break;
 326 
 327                 case SIGHUP:
 328                         /* Refresh SMF properties */
 329                         if (ndmpd_load_prop())
 330                                 NDMP_LOG(LOG_ERR,
 331                                     "Service properties initialization "
 332                                     "failed.");
 333                         break;
 334 
 335                 default:
 336                         /*
 337                          * Typically SIGINT or SIGTERM.
 338                          */
 339                         ndmpd.s_shutdown_flag = 1;
 340                         break;
 341                 }
 342 
 343                 ndmpd.s_sigval = 0;
 344         }
 345 
 346         (void) mutex_destroy(&ndmpd_zfs_fd_lock);
 347         libzfs_fini(zlibh);
 348         mod_fini();
 349         ndmp_door_fini();
 350         ndmp_log_close_file();
 351 
 352         return (SMF_EXIT_OK);
 353 }
 354 
 355 static void
 356 ndmpd_sig_handler(int sig)
 357 {
 358         if (ndmpd.s_sigval == 0)
 359                 ndmpd.s_sigval = sig;
 360 }
 361 
 362 /*
 363  * Enable libumem debugging by default on DEBUG builds.
 364  */
 365 #ifdef DEBUG
 366 const char *
 367 _umem_debug_init(void)
 368 {
 369         return ("default,verbose"); /* $UMEM_DEBUG setting */
 370 }
 371 
 372 const char *
 373 _umem_logging_init(void)
 374 {
 375         return ("fail,contents"); /* $UMEM_LOGGING setting */
 376 }
 377 #endif