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
mod_init()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
mod_fini()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
set_privileges(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
daemonize_init(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
main(int argc,char * argv[])199 main(int argc, char *argv[])
200 {
201 struct sigaction act;
202 sigset_t set;
203 int 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
ndmpd_sig_handler(int sig)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 *
_umem_debug_init(void)367 _umem_debug_init(void)
368 {
369 return ("default,verbose"); /* $UMEM_DEBUG setting */
370 }
371
372 const char *
_umem_logging_init(void)373 _umem_logging_init(void)
374 {
375 return ("fail,contents"); /* $UMEM_LOGGING setting */
376 }
377 #endif
378