root/usr/src/cmd/ndmpd/ndmp/ndmpd_main.c
/*
 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
 */

/*
 * BSD 3 Clause License
 *
 * Copyright (c) 2007, The Storage Networking Industry Association.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *      - Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above copyright
 *        notice, this list of conditions and the following disclaimer in
 *        the documentation and/or other materials provided with the
 *        distribution.
 *
 *      - Neither the name of The Storage Networking Industry Association (SNIA)
 *        nor the names of its contributors may be used to endorse or promote
 *        products derived from this software without specific prior written
 *        permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
/* 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;
mutex_t zlib_mtx;
void *mod_plp;

static void ndmpd_sig_handler(int sig);

typedef struct ndmpd {
        int s_shutdown_flag;    /* Fields for shutdown control */
        int s_sigval;
} ndmpd_t;

ndmpd_t ndmpd;


/*
 * Load and initialize the plug-in module
 */
static int
mod_init()
{
        char *plname;
        ndmp_plugin_t *(*plugin_init)(int);

        ndmp_pl = NULL;

        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");
        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);
}

/*
 * Unload
 */
static void
mod_fini()
{
        if (ndmp_pl == NULL)
                return;

        void (*plugin_fini)(ndmp_plugin_t *);

        plugin_fini = (void (*)(ndmp_plugin_t *))dlsym(mod_plp, "_ndmp_fini");
        if (plugin_fini == NULL) {
                (void) dlclose(mod_plp);
                return;
        }
        plugin_fini(ndmp_pl);
        (void) dlclose(mod_plp);
}

static void
set_privileges(void)
{
        priv_set_t *pset = priv_allocset();

        /*
         * Set effective sets privileges to 'least' required. If fails, send
         * error messages to log file and proceed.
         */
        if (pset != NULL) {
                priv_basicset(pset);
                (void) priv_addset(pset, PRIV_PROC_AUDIT);
                (void) priv_addset(pset, PRIV_PROC_SETID);
                (void) priv_addset(pset, PRIV_PROC_OWNER);
                (void) priv_addset(pset, PRIV_FILE_CHOWN);
                (void) priv_addset(pset, PRIV_FILE_CHOWN_SELF);
                (void) priv_addset(pset, PRIV_FILE_DAC_READ);
                (void) priv_addset(pset, PRIV_FILE_DAC_SEARCH);
                (void) priv_addset(pset, PRIV_FILE_DAC_WRITE);
                (void) priv_addset(pset, PRIV_FILE_OWNER);
                (void) priv_addset(pset, PRIV_FILE_SETID);
                (void) priv_addset(pset, PRIV_SYS_LINKDIR);
                (void) priv_addset(pset, PRIV_SYS_DEVICES);
                (void) priv_addset(pset, PRIV_SYS_MOUNT);
                (void) priv_addset(pset, PRIV_SYS_CONFIG);
        }

        if (pset == NULL || setppriv(PRIV_SET, PRIV_EFFECTIVE, pset) != 0) {
                (void) fprintf(stderr,
                    "Failed to set least required privileges to the service\n");
        }
        priv_freeset(pset);
}

static void
daemonize_init(void)
{
        sigset_t set, oset;
        pid_t pid;

        /*
         * Block all signals prior to the fork and leave them blocked in the
         * parent so we don't get in a situation where the parent gets SIGINT
         * and returns non-zero exit status and the child is actually running.
         * In the child, restore the signal mask once we've done our setsid().
         */
        (void) sigfillset(&set);
        (void) sigdelset(&set, SIGABRT);
        (void) sigprocmask(SIG_BLOCK, &set, &oset);

        if ((pid = fork()) == -1) {
                (void) fprintf(stderr,
                    "Failed to start process in background.\n");
                exit(SMF_EXIT_ERR_CONFIG);
        }

        /* If we're the parent process, exit. */
        if (pid != 0) {
                _exit(0);
        }
        (void) setsid();
        (void) sigprocmask(SIG_SETMASK, &oset, NULL);
        (void) chdir("/");
}

/*
 * main
 *
 * The main NDMP daemon function
 *
 * Parameters:
 *   argc (input) - the argument count
 *   argv (input) - command line options
 *
 * Returns:
 *   0
 */
int
main(int argc, char *argv[])
{
        struct sigaction act;
        sigset_t set;
        int 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)
         */
        if (ndmp_door_check()) {
                /* ndmpd is already running, exit. */
                (void) fprintf(stderr, "ndmpd is already running.\n");
                return (0);
        }

        /* Global zone check */
        if (getzoneid() != GLOBAL_ZONEID) {
                (void) fprintf(stderr, "Non-global zone not supported.\n");
                exit(SMF_EXIT_ERR_FATAL);
        }

        /* Trusted Solaris check */
        if (is_system_labeled()) {
                (void) fprintf(stderr, "Trusted Solaris not supported.\n");
                exit(SMF_EXIT_ERR_FATAL);
        }

        /* load SMF configuration */
        if (ndmpd_load_prop()) {
                (void) fprintf(stderr,
                    "SMF properties initialization failed.\n");
                exit(SMF_EXIT_ERR_CONFIG);
        }

        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 */
        (void) sigfillset(&set);
        (void) sigdelset(&set, SIGABRT); /* always unblocked for ASSERT() */
        (void) sigfillset(&act.sa_mask);
        act.sa_handler = ndmpd_sig_handler;
        act.sa_flags = 0;

        (void) sigaction(SIGTERM, &act, NULL);
        (void) sigaction(SIGHUP, &act, NULL);
        (void) sigaction(SIGINT, &act, NULL);
        (void) sigaction(SIGUSR1, &act, NULL);
        (void) sigaction(SIGPIPE, &act, NULL);
        (void) sigdelset(&set, SIGTERM);
        (void) sigdelset(&set, SIGHUP);
        (void) sigdelset(&set, SIGINT);
        (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);

        while (!ndmpd.s_shutdown_flag) {
                (void) sigsuspend(&set);

                switch (ndmpd.s_sigval) {
                case 0:
                        break;

                case SIGPIPE:
                        break;

                case SIGHUP:
                        /* Refresh SMF properties */
                        if (ndmpd_load_prop())
                                NDMP_LOG(LOG_ERR,
                                    "Service properties initialization "
                                    "failed.");
                        break;

                default:
                        /*
                         * Typically SIGINT or SIGTERM.
                         */
                        ndmpd.s_shutdown_flag = 1;
                        break;
                }

                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
ndmpd_sig_handler(int sig)
{
        if (ndmpd.s_sigval == 0)
                ndmpd.s_sigval = sig;
}

/*
 * Enable libumem debugging by default on DEBUG builds.
 */
#ifdef DEBUG
const char *
_umem_debug_init(void)
{
        return ("default,verbose"); /* $UMEM_DEBUG setting */
}

const char *
_umem_logging_init(void)
{
        return ("fail,contents"); /* $UMEM_LOGGING setting */
}
#endif