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 */
54libzfs_handle_t *zlibh;
55mutex_t	zlib_mtx;
56void *mod_plp;
57
58static void ndmpd_sig_handler(int sig);
59
60typedef struct ndmpd {
61	int s_shutdown_flag;	/* Fields for shutdown control */
62	int s_sigval;
63} ndmpd_t;
64
65ndmpd_t	ndmpd;
66
67
68/*
69 * Load and initialize the plug-in module
70 */
71static int
72mod_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 */
104static void
105mod_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
121static void
122set_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
155static void
156daemonize_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 */
198int
199main(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
355static void
356ndmpd_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
366const char *
367_umem_debug_init(void)
368{
369	return ("default,verbose"); /* $UMEM_DEBUG setting */
370}
371
372const char *
373_umem_logging_init(void)
374{
375	return ("fail,contents"); /* $UMEM_LOGGING setting */
376}
377#endif
378