OpenSolaris_b135/cmd/lvm/md_monitord/md_monitord.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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);
}