/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * probedev issues ioctls for all the metadevices */ #include "md_monitord.h" #include <sdssc.h> extern char queue_name[]; boolean_e issue_ioctl = True; #define DEBUG_LEVEL_FORK 9 /* will run in background at all */ /* levels less than DEBUG_LEVEL_FORK */ /* function prototypes */ static void usage(void); static void catch_sig(int); static pid_t enter_daemon_lock(void); static void exit_daemon_lock(void); static void probe_all_devs(boolean_e, md_error_t *, boolean_e); #define DAEMON_LOCK_FILE "/etc/lvm/.mdmonitord.lock" /* * Global variable */ mdsetname_t *sp; static int hold_daemon_lock; static const char *daemon_lock_file = DAEMON_LOCK_FILE; static int daemon_lock_fd; static int debug_level; static int logflag; static char *prog; static struct itimerval itimer; static boolean_e probe_started; /* flag to indicate main is probing */ static void usage() { (void) fprintf(stderr, gettext( "usage: mdmonitord [-d <debug_level>] [-t poll time]\n" "higher debug levels get progressively more" "detailed debug information.\n\n" "mdmonitord will run in background if run" "with a debug_level less than %d.\n"), DEBUG_LEVEL_FORK); exit(-1); } /* common exit function which ensures releasing locks */ void monitord_exit(int status) { monitord_print(1, gettext("exit status = %d\n"), status); monitord_print(8, "hold_daemon_lock %d\n", hold_daemon_lock); if (hold_daemon_lock) { exit_daemon_lock(); } md_exit(sp, status); } /* * When SIGHUP is received, reload modules? */ void catch_sig(int sig) { boolean_e startup = False; md_error_t status = mdnullerror; boolean_e sig_verbose = True; if (sig == SIGALRM) { monitord_print(6, gettext("SIGALRM processing")); if (probe_started == True) { monitord_print(6, gettext( " probe_started returning\n")); return; } monitord_print(6, gettext( " starting probe from signal handler\n")); probe_all_devs(startup, &status, sig_verbose); (void) setitimer(ITIMER_REAL, &itimer, NULL); } if (sig == SIGHUP) monitord_exit(sig); } /* * Use an advisory lock to ensure that only one daemon process is * active at any point in time. */ static pid_t check_daemon_lock(void) { struct flock lock; monitord_print(1, gettext("check_daemon_lock: lock file = %s\n"), daemon_lock_file); daemon_lock_fd = open(daemon_lock_file, O_CREAT|O_RDWR, 0644); if (daemon_lock_fd < 0) { monitord_print(0, "open(%s) - %s\n", daemon_lock_file, strerror(errno)); monitord_exit(-1); } lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(daemon_lock_fd, F_GETLK, &lock) == -1) { monitord_print(0, "lock(%s) - %s", daemon_lock_file, strerror(errno)); monitord_exit(-1); } return (lock.l_type == F_UNLCK ? 0 : lock.l_pid); } static pid_t enter_daemon_lock(void) { struct flock lock; monitord_print(1, gettext( "enter_daemon_lock: lock file = %s\n"), daemon_lock_file); daemon_lock_fd = open(daemon_lock_file, O_CREAT|O_RDWR, 0644); if (daemon_lock_fd < 0) { monitord_print(0, "open(%s) - %s\n", daemon_lock_file, strerror(errno)); monitord_exit(-1); } lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) { if (errno == EAGAIN || errno == EDEADLK) { if (fcntl(daemon_lock_fd, F_GETLK, &lock) == -1) { monitord_print(0, "lock(%s) - %s", daemon_lock_file, strerror(errno)); monitord_exit(-1); } return (lock.l_pid); } } hold_daemon_lock = 1; return (0); } /* * Drop the advisory daemon lock, close lock file */ static void exit_daemon_lock(void) { struct flock lock; lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) { monitord_print(0, "unlock(%s) - %s", daemon_lock_file, strerror(errno)); } if (close(daemon_lock_fd) == -1) { monitord_print(0, "close(%s) failed - %s\n", daemon_lock_file, strerror(errno)); monitord_exit(-1); } (void) unlink(daemon_lock_file); } /* * print error messages to the terminal or to syslog */ /*PRINTFLIKE2*/ void monitord_print(int level, char *message, ...) { va_list ap; static int newline = 1; if (level > debug_level) { return; } va_start(ap, message); if (level == 0) { if (logflag) { (void) vsyslog(LOG_ERR, message, ap); } else { (void) vfprintf(stderr, message, ap); } } else { if (logflag) { (void) syslog(LOG_DEBUG, "%s[%ld]: ", prog, getpid()); (void) vsyslog(LOG_DEBUG, message, ap); } else { if (newline) { (void) fprintf(stdout, "%s[%ld]: ", prog, getpid()); (void) vfprintf(stdout, message, ap); } else { (void) vfprintf(stdout, message, ap); } } } if (message[strlen(message)-1] == '\n') { newline = 1; } else { newline = 0; } va_end(ap); } char * int2string(intmap_t *map, int value) { const char *name = (const char *)NULL; char charstr[100]; for (; map->im_name != (const char *)NULL; map++) { if (map->im_int == value) { name = map->im_name; break; } } if (name == (const char *)NULL) { /* No match. Convert the string to an int. */ (void) sprintf(charstr, "%d", value); } else { (void) snprintf(charstr, sizeof (charstr), "%d %s", value, name); } return (strdup(charstr)); } void probe_all_devs(boolean_e startup, md_error_t *statusp, boolean_e verbose) { set_t max_sets, set_idx; probe_started = True; (void) set_snarf(statusp); if ((max_sets = get_max_sets(statusp)) == 0) { mde_perror(statusp, gettext( "Can't find max number of sets\n")); monitord_exit(1); } /* * We delete the FF_Q to avoid recurse errors. Yes we will lose * some but its the corner case. */ if (startup == False && (meta_notify_deleteq(MD_FF_Q, statusp) != 0)) { mde_perror(statusp, gettext( "delete queue failed\n")); monitord_exit(1); } for (set_idx = 0; set_idx < max_sets; set_idx++) { if ((sp = metasetnosetname(set_idx, statusp)) == NULL) { if (mdiserror(statusp, MDE_NO_SET) == 0) { /* * done break the loop */ break; } else { mdclrerror(statusp); continue; } } /* if we dont have ownership or cannot lock it continue. */ if ((meta_check_ownership(sp, statusp) == NULL) && meta_lock(sp, TRUE, statusp)) continue; /* Skip if a MN set */ if (meta_is_mn_set(sp, statusp)) { (void) meta_unlock(sp, statusp); continue; } probe_mirror_devs(verbose); probe_raid_devs(verbose); probe_trans_devs(verbose); probe_hotspare_devs(verbose); (void) meta_unlock(sp, statusp); } if (meta_notify_createq(MD_FF_Q, 0, statusp)) { mde_perror(statusp, gettext( "create queue failed")); monitord_exit(1); } probe_started = False; /* * need to do it here only at startup. * The daemon will restart the alarm. */ if (startup == True) (void) setitimer(ITIMER_REAL, &itimer, NULL); } evid_t wait_for_event(md_error_t *statusp) { md_ev_t event; event.setno = EV_ALLSETS; event.obj = EV_ALLOBJS; do { if (meta_notify_getev(MD_FF_Q, EVFLG_WAIT, &event, statusp) < 0) { monitord_print(8, "meta_notify_getev: errno 0x%x\n", -errno); monitord_exit(-errno); } } while ((event.ev != EV_IOERR && event.ev != EV_ERRED && event.ev != EV_LASTERRED)); return (event.ev); } int main(int argc, char **argv) { boolean_e startup = True; boolean_e verbose = False; int i; char c; md_error_t status = mdnullerror; struct sigaction act; sigset_t mask; unsigned long timerval = 0; /* * Get the locale set up before calling any other routines * with messages to ouput. Just in case we're not in a build * environment, make sure that TEXT_DOMAIN gets set to * something. */ #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); if (sdssc_bind_library() == SDSSC_ERROR) { (void) printf(gettext( "%s: Interface error with libsds_sc.so\n"), argv[0]); exit(1); } if (md_init(argc, argv, 0, 1, &status) != 0 || meta_check_root(&status) != 0) { mde_perror(&status, ""); monitord_exit(1); } (void) sigfillset(&mask); (void) thr_sigsetmask(SIG_BLOCK, &mask, NULL); if (argc > 7) { usage(); } if ((prog = strrchr(argv[0], '/')) == NULL) { prog = argv[0]; } else { prog++; } /* * Reset optind/opterr so that the command line arguments can be * parsed. This is in case anything has already called getopt, * for example sdssc_cmd_proxy which is not currently used but * may be in the future. */ optind = 1; opterr = 1; while ((c = getopt(argc, argv, "ivd:t:")) != EOF) { switch (c) { case 'v': verbose = True; break; case 'i': issue_ioctl = True; break; case 'd': debug_level = atoi(optarg); break; case 't': timerval = atol(optarg); break; default: usage(); exit(0); } } if (timerval == 0) { monitord_print(8, gettext( "operating in interrupt mode\n")); } else { itimer.it_value.tv_sec = timerval; itimer.it_interval.tv_sec = timerval; monitord_print(8, gettext( "set value and interval %lu sec mode\n"), timerval); } /* * set up our signal handler for SIGALRM. The * rest are setup by md_init. */ act.sa_handler = catch_sig; (void) sigemptyset(&act.sa_mask); act.sa_flags = SA_RESTART; (void) sigaction(SIGALRM, &act, NULL); (void) sigaction(SIGHUP, &act, NULL); (void) sigemptyset(&mask); (void) sigaddset(&mask, SIGALRM); (void) sigaddset(&mask, SIGHUP); (void) thr_sigsetmask(SIG_UNBLOCK, &mask, NULL); /* demonize ourselves */ if (debug_level < DEBUG_LEVEL_FORK) { pid_t pid; if ((pid = check_daemon_lock()) != 0) { monitord_print(0, gettext( "mdmonitord daemon pid %ld already running\n"), pid); exit(-1); } if (fork()) { exit(0); } /* only one daemon can run at a time */ if ((pid = enter_daemon_lock()) != 0) { monitord_print(0, gettext( "mdmonitord daemon pid %ld already running\n"), pid); exit(-1); } (void) chdir("/"); (void) setsid(); if (debug_level <= 1) { for (i = 0; i < 3; i++) { (void) close(i); } (void) open("/dev/null", 0); (void) dup2(0, 1); (void) dup2(0, 2); logflag = 1; } } openlog("mdmonitord", LOG_PID, LOG_DAEMON); monitord_print(8, gettext( "mdmonitord started, debug level = %d\n"), debug_level); /* loop forever waiting for events */ do { metaflushnames(1); probe_all_devs(startup, &status, verbose); startup = False; /* since we have gone through once */ } while (wait_for_event(&status)); return (0); }