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