The sanlk_resetd daemon gets events from specified lockspaces. Events are defined to cause the sanlk_resetd to: - use wdmd/watchdog to reset the host (event 1) - use /proc/sysrq-trigger to reboot the host (event 4)
The sanlk_resetd daemon can be used alone, and events passed to it from another host by: sanlock client set_event -s lockspace_name -i host_id -e 1
The sanlk_reset program can also be used to communicate with sanlk_resetd on another host. It will set the event(s), then monitor the host status until the target host is dead.
Signed-off-by: David Teigland teigland@redhat.com --- reset/Makefile | 61 ++++++ reset/sanlk_reset.c | 443 ++++++++++++++++++++++++++++++++++++++++++ reset/sanlk_reset.h | 18 ++ reset/sanlk_resetd.c | 536 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1058 insertions(+) create mode 100644 reset/Makefile create mode 100644 reset/sanlk_reset.c create mode 100644 reset/sanlk_reset.h create mode 100644 reset/sanlk_resetd.c
diff --git a/reset/Makefile b/reset/Makefile new file mode 100644 index 000000000000..69574e3ba84a --- /dev/null +++ b/reset/Makefile @@ -0,0 +1,61 @@ +TARGET1 = sanlk_resetd +TARGET2 = sanlk_reset + +SOURCE1 = sanlk_resetd.c +SOURCE2 = sanlk_reset.c + +DEBUG = 1 +OPTIMIZE_FLAG = -O2 -Wp,-D_FORTIFY_SOURCE=2 +ifeq ($(DEBUG), 1) + OPTIMIZE_FLAG = -O0 + CFLAGS += -g +endif + +CFLAGS += -D_GNU_SOURCE \ + -Wall \ + -Wformat \ + -Wformat-security \ + -Wnested-externs \ + -Wpointer-arith \ + -Wextra -Wshadow \ + -Wcast-align \ + -Wwrite-strings \ + -Waggregate-return \ + -Wstrict-prototypes \ + -Winline \ + -Wredundant-decls \ + -Wno-sign-compare \ + -fexceptions \ + -fasynchronous-unwind-tables \ + -fdiagnostics-show-option \ + $(OPTIMIZE_FLAG) \ + $(NULL) + +VER=$(shell cat ../VERSION) +CFLAGS += -DVERSION="$(VER)" -I../src -I../wdmd + +LDFLAGS = -lsanlock -lwdmd + +all: $(TARGET1) $(TARGET2) + +$(TARGET1): $(SOURCE1) + $(CC) $(CFLAGS) $(LDFLAGS) $(SOURCE1) -o $@ -L. -L../src -L../wdmd + +$(TARGET2): $(SOURCE2) + $(CC) $(CFLAGS) $(LDFLAGS) $(SOURCE2) -o $@ -L. -L../src -L../wdmd + +clean: + rm -f *.o *.so *.so.* $(TARGET1) $(TARGET2) + +INSTALL=$(shell which install) + +DESTDIR= +BINDIR=/usr/sbin +LIBDIR=/usr/lib64 +HEADIR=/usr/include +MANDIR=/usr/share/man + +.PHONY: install +install: all + $(INSTALL) -d $(DESTDIR)/$(BINDIR) + $(INSTALL) -c -m 755 $(TARGET1) $(TARGET2) $(DESTDIR)/$(BINDIR) diff --git a/reset/sanlk_reset.c b/reset/sanlk_reset.c new file mode 100644 index 000000000000..7ea08626492a --- /dev/null +++ b/reset/sanlk_reset.c @@ -0,0 +1,443 @@ +/* + * Copyright 2014 Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v2 or (at your option) any later version. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stddef.h> +#include <unistd.h> +#include <inttypes.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <time.h> +#include <poll.h> +#include <signal.h> +#include <syslog.h> +#include <dirent.h> +#include <getopt.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/signalfd.h> + +#include "sanlock.h" +#include "sanlock_admin.h" +#include "sanlock_resource.h" +#include "sanlock_direct.h" +#include "sanlk_reset.h" + +#define MAX_LS 4 + +static char *prog_name; +static struct pollfd *pollfd; +static char *ls_names[MAX_LS]; +static int ls_fd[MAX_LS]; +static int ls_count; +static int event_reply; +static int use_watchdog = 1; +static int use_sysrq_reboot = 0; + +static int last_status_live; +static int last_status_fail; +static int last_status_unknown; + + +#define log_debug(fmt, args...) \ +do { \ + fprintf(stderr, "%llu " fmt "\n", (unsigned long long)time(NULL), ##args); \ +} while (0) + +#define log_error(fmt, args...) \ +do { \ + log_debug(fmt, ##args); \ + syslog(LOG_ERR, fmt, ##args); \ +} while (0) + +#define log_warn(fmt, args...) \ +do { \ + log_debug(fmt, ##args); \ + syslog(LOG_WARNING, fmt, ##args); \ +} while (0) + + +static void unregister_ls(int i) +{ + sanlock_end_event(ls_fd[i], ls_names[i], 0); + ls_names[i] = NULL; + ls_fd[i] = -1; + pollfd[i].fd = -1; + pollfd[i].events = 0; +} + +static int reset_done(uint64_t host_id) +{ + struct sanlk_host *hss, *hs; + int hss_count; + int found; + int i, j; + int val; + int rv; + int found_count; + int free_count, live_count, fail_count, dead_count, unknown_count; + + found_count = 0; + free_count = 0; + live_count = 0; + fail_count = 0; + dead_count = 0; + unknown_count = 0; + + for (i = 0; i < MAX_LS; i++) { + if (!ls_names[i]) + continue; + + hss_count = 0; + hss = NULL; + hs = NULL; + + rv = sanlock_get_hosts(ls_names[i], 0, &hss, &hss_count, 0); + + if (!hss_count || !hss) { + log_error("sanlock_get_hosts error %d ls %s", rv, ls_names[i]); + continue; + } + + found = 0; + hs = hss; + + for (j = 0; j < hss_count; j++) { + if (hs->host_id != host_id) { + hs++; + continue; + } + + found = 1; + + val = hs->flags & SANLK_HOST_MASK; + + if (val == SANLK_HOST_FREE) + free_count++; + else if (val == SANLK_HOST_LIVE) + live_count++; + else if (val == SANLK_HOST_FAIL) + fail_count++; + else if (val == SANLK_HOST_DEAD) + dead_count++; + else if (val == SANLK_HOST_UNKNOWN) + unknown_count++; + break; + } + + free(hss); + + if (!found) { + log_error("status of host_id %llu not found ls %s", + (unsigned long long)host_id, ls_names[i]); + } else { + found_count++; + } + } + + if (!found_count) { + log_error("status of host_id %llu not found", (unsigned long long)host_id); + return 0; + } + + if (!free_count && !live_count && !fail_count && !dead_count && !unknown_count) { + log_error("status of host_id %llu no status", (unsigned long long)host_id); + return 0; + } + + if (live_count) { + if (!last_status_live) + log_debug("host_id %llu status: live", (unsigned long long)host_id); + last_status_live = 1; + last_status_fail = 0; + last_status_unknown = 0; + return 0; + } + + if (fail_count) { + if (!last_status_fail) + log_debug("host_id %llu status: fail", (unsigned long long)host_id); + last_status_fail = 1; + last_status_live = 0; + last_status_unknown = 0; + return 0; + } + + if (unknown_count) { + if (!last_status_unknown) + log_debug("host_id %llu status: unknown", (unsigned long long)host_id); + last_status_unknown = 1; + last_status_fail = 0; + last_status_live = 0; + return 0; + } + + if (free_count && !fail_count) { + log_debug("host_id %llu status: free", (unsigned long long)host_id); + return 1; + } + + if (dead_count) { + log_debug("host_id %llu status: dead", (unsigned long long)host_id); + return 1; + } + + log_debug("host_id %llu status: %u", (unsigned long long)host_id, val); + return 0; +} + +static void usage(void) +{ + printf("%s [options] -i <host_id> lockspace_name ...\n", prog_name); + printf(" --help | -h\n"); + printf(" Show this help information.\n"); + printf(" --version | -V\n"); + printf(" Show version.\n"); + printf(" --host-id | -i <num>\n"); + printf(" Host id to reset.\n"); + printf(" --generation | -g <num>\n"); + printf(" Generation of host id (default current generation).\n"); + printf(" --watchdog | -w 0|1\n"); + printf(" Disable (0) use of wdmd/watchdog for testing.\n"); + printf(" --sysrq-reboot | -r 0|1\n"); + printf(" Enable/Disable (1/0) use of /proc/sysrq-trigger to reboot (default 0).\n"); + printf("\n"); + printf("The event will be set in each lockspace_name (max %d).\n", MAX_LS); + printf("Use -g 0 to use the current generation.\n"); +} + +int main(int argc, char *argv[]) +{ + struct sanlk_host_event he; + struct sanlk_host_event from_he; + uint64_t from_host, from_gen; + uint32_t flags = 0; + uint32_t begin = time(NULL); + int i, fd, rv; + int done = 0; + + prog_name = argv[0]; + + memset(&he, 0, sizeof(he)); + + static struct option long_options[] = { + {"help", no_argument, 0, 'h' }, + {"version", no_argument, 0, 'V' }, + {"host-id", required_argument, 0, 'i' }, + {"generation", required_argument, 0, 'g' }, + {"watchdog", required_argument, 0, 'w' }, + {"sysrq-reboot", required_argument, 0, 'r' }, + {0, 0, 0, 0 } + }; + + while (1) { + int c; + int option_index = 0; + + c = getopt_long(argc, argv, "hVi:g:w:r:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case '0': + break; + case 'h': + usage(); + exit(EXIT_SUCCESS); + case 'V': + printf("%s version: " VERSION "\n", prog_name); + exit(EXIT_SUCCESS); + case 'i': + he.host_id = strtoull(optarg, NULL, 0); + break; + case 'g': + he.generation = strtoull(optarg, NULL, 0); + break; + case 'w': + use_watchdog = atoi(optarg); + break; + case 'r': + use_sysrq_reboot = atoi(optarg); + break; + case '?': + default: + usage(); + exit(EXIT_FAILURE); + } + } + + if (!he.host_id) { + log_error("host_id is required"); + exit(EXIT_FAILURE); + } + + ls_count = 0; + + for (i = optind; i < argc; i++) { + if (ls_count == MAX_LS) { + fprintf(stderr, "ignore lockspace_name %s", argv[i]); + continue; + } + ls_names[ls_count] = argv[i]; + ls_count++; + } + + if (!ls_count) { + log_error("lockspace_name is required"); + exit(EXIT_FAILURE); + } + + pollfd = malloc(MAX_LS * sizeof(struct pollfd)); + if (!pollfd) + return -ENOMEM; + + for (i = 0; i < MAX_LS; i++) { + ls_fd[i] = -1; + pollfd[i].fd = -1; + pollfd[i].events = 0; + pollfd[i].revents = 0; + } + + openlog(prog_name, LOG_CONS | LOG_PID, LOG_DAEMON); + + ls_count = 0; + + for (i = 0; i < MAX_LS; i++) { + if (!ls_names[i]) + continue; + + fd = sanlock_reg_event(ls_names[i], NULL, 0); + if (fd < 0) { + log_error("reg_event error %d ls %s", fd, ls_names[i]); + ls_names[i] = NULL; + } else { + ls_fd[i] = fd; + pollfd[i].fd = ls_fd[i]; + pollfd[i].events = POLLIN; + ls_count++; + } + } + + if (!ls_count) { + log_error("No lockspaces registered."); + exit(EXIT_FAILURE); + } + + if (use_watchdog) + he.event |= EVENT_RESET; + if (use_sysrq_reboot) + he.event |= EVENT_REBOOT; + if (!he.generation) + flags = SANLK_SETEV_CUR_GENERATION; + + for (i = 0; i < MAX_LS; i++) { + if (!ls_names[i]) + continue; + if (ls_fd[i] == -1) + continue; + + rv = sanlock_set_event(ls_names[i], &he, flags); + if (rv < 0) { + log_error("set_event %s error %d", ls_names[i], rv); + unregister_ls(i); + } else { + log_warn("asked host %llu %llu to %s%s(%llx %llx)", + (unsigned long long)he.host_id, + (unsigned long long)he.generation, + (he.event & EVENT_RESET) ? "reset " : "", + (he.event & EVENT_REBOOT) ? "reboot " : "", + (unsigned long long)he.event, + (unsigned long long)he.data); + } + } + + if (!ls_count) { + log_error("No lockspaces to use after set_event error."); + exit(EXIT_FAILURE); + } + + while (1) { + rv = poll(pollfd, MAX_LS, 1000); + if (rv == -1 && errno == EINTR) + continue; + if (rv < 0) + break; + + done = reset_done(he.host_id); + if (done) + break; + + for (i = 0; i < MAX_LS; i++) { + if (pollfd[i].fd < 0) + continue; + + if (pollfd[i].revents & POLLIN) { + rv = sanlock_get_event(ls_fd[i], 0, &from_he, &from_host, &from_gen); + if (rv < 0) { + log_error("unregister fd %d get_event error %d ls %s", + ls_fd[i], rv, ls_names[i]); + unregister_ls(i); + ls_count--; + continue; + } + + if ((from_host == he.host_id) && + (from_he.event & EVENT_RESETTING)) { + log_warn("notice of %s%s(%llx %llx) from host %llu %llu ls %s", + (from_he.event & EVENT_RESETTING) ? "resetting " : "", + (from_he.event & EVENT_REBOOTING) ? "rebooting " : "", + (unsigned long long)from_he.event, + (unsigned long long)from_he.data, + (unsigned long long)from_host, + (unsigned long long)from_gen, + ls_names[i]); + event_reply = 1; + } else { + log_warn("event ignored %llx %llx from host %llu %llu ls %s", + (unsigned long long)from_he.event, + (unsigned long long)from_he.data, + (unsigned long long)from_host, + (unsigned long long)from_gen, + ls_names[i]); + } + } + + if (pollfd[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { + log_debug("unregister fd %d poll %x ls %s", + ls_fd[i], pollfd[i].revents, ls_names[i]); + unregister_ls(i); + ls_count--; + } + } + + if (!ls_count) + break; + } + + for (i = 0; i < MAX_LS; i++) { + if (!ls_names[i]) + continue; + if (ls_fd[i] == -1) + continue; + unregister_ls(i); + } + + if (done) { + log_debug("reset done in %u seconds", (uint32_t)(time(NULL) - begin)); + exit(EXIT_SUCCESS); + } + + log_debug("reset failed"); + exit(EXIT_FAILURE); +} diff --git a/reset/sanlk_reset.h b/reset/sanlk_reset.h new file mode 100644 index 000000000000..834fc5eb66cb --- /dev/null +++ b/reset/sanlk_reset.h @@ -0,0 +1,18 @@ +/* + * Copyright 2014 Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v2 or (at your option) any later version. + */ + +#ifndef __SANLK_RESET_H__ +#define __SANLK_RESET_H__ + +#define EVENT_RESET 1 +#define EVENT_RESETTING 2 +#define EVENT_REBOOT 4 +#define EVENT_REBOOTING 8 + +#endif + diff --git a/reset/sanlk_resetd.c b/reset/sanlk_resetd.c new file mode 100644 index 000000000000..395c12a144de --- /dev/null +++ b/reset/sanlk_resetd.c @@ -0,0 +1,536 @@ +/* + * Copyright 2014 Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v2 or (at your option) any later version. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stddef.h> +#include <unistd.h> +#include <inttypes.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <time.h> +#include <poll.h> +#include <signal.h> +#include <syslog.h> +#include <dirent.h> +#include <getopt.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/signalfd.h> + +#include "sanlock.h" +#include "sanlock_admin.h" +#include "sanlock_resource.h" +#include "sanlock_direct.h" +#include "wdmd.h" +#include "sanlk_reset.h" + +#define DEFAULT_SYSRQ_DELAY 25 + +static char *daemon_name = (char *)"sanlk_resetd"; +static int daemon_quit; +static int daemon_foreground; +static int daemon_debug; +static int use_watchdog = 1; +static int use_sysrq_reboot = 0; +static int sysrq_delay = DEFAULT_SYSRQ_DELAY; +static int we_are_resetting; +static int we_are_rebooting; +static uint64_t rebooting_time; + +#define MAX_LS 4 + +static struct pollfd *pollfd; +static char *ls_names[MAX_LS]; +static int ls_fd[MAX_LS]; +static int ls_count; +static int signal_fd; +static int wdmd_fd; + + +#define log_debug(fmt, args...) \ +do { \ + if (daemon_debug) \ + fprintf(stderr, "%llu " fmt "\n", (unsigned long long)time(NULL), ##args); \ +} while (0) + +#define log_error(fmt, args...) \ +do { \ + log_debug(fmt, ##args); \ + syslog(LOG_ERR, fmt, ##args); \ +} while (0) + +#define log_warn(fmt, args...) \ +do { \ + log_debug(fmt, ##args); \ + syslog(LOG_WARNING, fmt, ##args); \ +} while (0) + + +static uint64_t monotime(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + return ts.tv_sec; +} + +/* + * By default a 25 second delay is used before using sysrq to give sanlock + * time to write our resetting event in its next lease renewal. + * + * It would not be surprising for sysrq reboot to fail or hang, so it's + * important for the watchdog to also be there to reset us. This + * sysrq reboot is used only as a way to speed up the reset since the + * watchdog requires 60 seconds to fire. + */ + +static void sysrq_reboot(void) +{ + int fd, rv; + + log_error("Rebooting host with sysrq"); + /* give at least a small chance for the log message to be written */ + sleep(1); + + fd = open("/proc/sysrq-trigger", O_WRONLY); + + if (fd < 0) { + log_error("failed to open sysrq-trigger %d %d", fd, errno); + return; + } + + rv = write(fd, "b", 1); + if (rv < 0) { + log_error("failed to write sysrq-trigger %d %d", rv, errno); + } + + close(fd); + + /* If sysrq reboot worked, then I don't think we will get here. */ + /* If sysrq reboot failed, then the watchdog should reset us. */ + log_error("Reboot from sysrq is expected"); +} + +/* + * Use the watchdog to reset the machine as soon as possible. + * Intentionally set the expire time on the connection to + * the current time so that the watchdog will expire and + * reset as soon as possible. + */ + +void watchdog_reset_self(void) +{ + uint64_t now; + int rv; + + if (!use_watchdog) + return; + + now = monotime(); + + rv = wdmd_test_live(wdmd_fd, now, now); + if (rv < 0) { + log_error("watchdog_reset_self test_live failed %d", rv); + return; + } + + log_error("Resetting host with watchdog"); + return; +} + +static int setup_wdmd(void) +{ + char name[WDMD_NAME_SIZE]; + int con; + int rv; + + if (!use_watchdog) + return 0; + + con = wdmd_connect(); + if (con < 0) { + log_error("setup_wdmd connect failed %d", con); + return con; + } + + memset(name, 0, sizeof(name)); + + snprintf(name, WDMD_NAME_SIZE - 1, "sanlk_resetd"); + + rv = wdmd_register(con, name); + if (rv < 0) { + log_error("setup_wdmd register failed %d", rv); + goto fail_close; + } + + /* the refcount tells wdmd that it should not cleanly exit */ + + rv = wdmd_refcount_set(con); + if (rv < 0) { + log_error("setup_wdmd refcount_set failed %d", rv); + goto fail_close; + } + + log_debug("setup_wdmd %d", con); + + wdmd_fd = con; + return 0; + + fail_close: + close(con); + return -1; +} + +static void close_wdmd(void) +{ + if (!use_watchdog) + return; + + wdmd_refcount_clear(wdmd_fd); + close(wdmd_fd); +} + +/* + * This event will be included in the next lease renewal of the lockspace. + * This should be in about the next 20 seconds, unless renewals are + * experiencing some delays. We have about 60 seconds to get the renewal, + * including the event, written before the watchdog fires (or syrq_delay until + * sysrq reboot if that is configured). + */ + +static void set_event_out(char *ls_name, uint64_t event_out, uint64_t from_host, uint64_t from_gen) +{ + struct sanlk_host_event he; + int rv; + + he.host_id = from_host; + he.generation = from_gen; + he.event = event_out; + he.data = 0; + + rv = sanlock_set_event(ls_name, &he, 0); + if (rv < 0) + log_error("set_event error %d ls %s", rv, ls_name); +} + +static void unregister_ls(int i) +{ + sanlock_end_event(ls_fd[i], ls_names[i], 0); + ls_names[i] = NULL; + ls_fd[i] = -1; + pollfd[i].fd = -1; + pollfd[i].events = 0; +} + +static int setup_signals(void) +{ + sigset_t mask; + int rv; + + sigemptyset(&mask); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGHUP); + + rv = sigprocmask(SIG_BLOCK, &mask, NULL); + if (rv < 0) + return rv; + + signal_fd = signalfd(-1, &mask, 0); + if (signal_fd < 0) + return -errno; + + return 0; +} + +static void process_signals(int fd) +{ + struct signalfd_siginfo fdsi; + ssize_t rv; + + rv = read(fd, &fdsi, sizeof(struct signalfd_siginfo)); + if (rv != sizeof(struct signalfd_siginfo)) + return; + + if ((fdsi.ssi_signo == SIGTERM) || (fdsi.ssi_signo == SIGINT)) { + log_debug("daemon_quit signal %d", fdsi.ssi_signo); + daemon_quit = 1; + } +} + +static void usage(void) +{ + printf("%s [options] lockspace_name ...\n", daemon_name); + printf(" --help | -h\n"); + printf(" Show this help information.\n"); + printf(" --version | -V\n"); + printf(" Show version.\n"); + printf(" --foreground | -f\n"); + printf(" Don't fork.\n"); + printf(" --daemon-debug | -D\n"); + printf(" Don't fork and print debugging to stdout.\n"); + printf(" --watchdog | -w 0|1\n"); + printf(" Disable (0) use of wdmd/watchdog for testing.\n"); + printf(" --sysrq-reboot | -r 0|1\n"); + printf(" Enable/Disable (1/0) use of /proc/sysrq-trigger to reboot (default 0).\n"); + printf(" --sysrq-delay | -d <sec>\n"); + printf(" Delay this many seconds before using /proc/sysrq-trigger (default %d).\n", DEFAULT_SYSRQ_DELAY); + printf("\n"); + printf("Get reset events from lockspace_name (max %d).\n", MAX_LS); +} + +int main(int argc, char *argv[]) +{ + struct sanlk_host_event from_he; + uint64_t from_host, from_gen; + uint64_t event, event_out; + int poll_timeout; + int i, rv, fd; + + static struct option long_options[] = { + {"help", no_argument, 0, 'h' }, + {"version", no_argument, 0, 'V' }, + {"foreground", no_argument, 0, 'f' }, + {"daemon-debug", no_argument, 0, 'D' }, + {"watchdog", required_argument, 0, 'w' }, + {"sysrq-reboot", required_argument, 0, 'r' }, + {"sysrq-delay", required_argument, 0, 'd' }, + {0, 0, 0, 0 } + }; + + while (1) { + int c; + int option_index = 0; + + c = getopt_long(argc, argv, "hVfDw:r:d:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case '0': + break; + case 'h': + usage(); + exit(EXIT_SUCCESS); + case 'V': + printf("%s version: " VERSION "\n", daemon_name); + exit(EXIT_SUCCESS); + case 'f': + daemon_foreground = 1; + break; + case 'D': + daemon_foreground = 1; + daemon_debug = 1; + break; + case 'w': + use_watchdog = atoi(optarg); + break; + case 'r': + use_sysrq_reboot = atoi(optarg); + break; + case 'd': + sysrq_delay = atoi(optarg); + break; + case '?': + default: + usage(); + exit(EXIT_FAILURE); + } + } + + ls_count = 0; + + for (i = optind; i < argc; i++) { + if (ls_count == MAX_LS) { + fprintf(stderr, "ignore lockspace_name %s", argv[i]); + continue; + } + ls_names[ls_count] = argv[i]; + ls_count++; + } + + if (!ls_count) { + log_error("lockspace_name is required"); + exit(EXIT_FAILURE); + } + + if (!daemon_foreground) { + if (daemon(0, 0) < 0) { + fprintf(stderr, "cannot fork daemon\n"); + exit(EXIT_FAILURE); + } + } + + openlog(daemon_name, LOG_CONS | LOG_PID, LOG_DAEMON); + + log_warn("%s %s started %s", daemon_name, VERSION, use_watchdog ? "" : "use_watchdog=0"); + + rv = setup_wdmd(); + if (rv < 0) { + log_error("failed to set up wdmd"); + return rv; + } + + rv = setup_signals(); + if (rv < 0) { + log_error("failed to set up signals"); + goto out; + } + + /* Add an extra slot for the signal fd. */ + pollfd = malloc((MAX_LS+1) * sizeof(struct pollfd)); + if (!pollfd) + return -ENOMEM; + + for (i = 0; i < MAX_LS; i++) { + ls_fd[i] = -1; + pollfd[i].fd = -1; + pollfd[i].events = 0; + pollfd[i].revents = 0; + } + pollfd[MAX_LS].fd = signal_fd; + pollfd[MAX_LS].events = POLLIN; + pollfd[MAX_LS].revents = 0; + + ls_count = 0; + + for (i = 0; i < MAX_LS; i++) { + if (!ls_names[i]) + continue; + + fd = sanlock_reg_event(ls_names[i], NULL, 0); + if (fd < 0) { + log_error("reg_event error %d ls %s", fd, ls_names[i]); + ls_names[i] = NULL; + } else { + log_debug("reg_event fd %d ls %s", fd, ls_names[i]); + ls_fd[i] = fd; + pollfd[i].fd = fd; + pollfd[i].events = POLLIN; + ls_count++; + } + } + + if (!ls_count) { + log_error("No lockspaces registered."); + exit(EXIT_FAILURE); + } + + poll_timeout = -1; + + while (1) { + rv = poll(pollfd, MAX_LS+1, poll_timeout); + if (rv == -1 && errno == EINTR) + continue; + if (rv < 0) + break; + + if (pollfd[MAX_LS].revents & POLLIN) + process_signals(pollfd[MAX_LS].fd); + + if (daemon_quit) + break; + + if (we_are_rebooting && (monotime() - rebooting_time >= sysrq_delay)) { + sysrq_reboot(); + } + + for (i = 0; i < MAX_LS; i++) { + if (pollfd[i].fd < 0) + continue; + + if (pollfd[i].revents & POLLIN) { + rv = sanlock_get_event(ls_fd[i], 0, &from_he, &from_host, &from_gen); + if (rv < 0) { + log_error("unregister fd %d get_event error %d ls %s", + ls_fd[i], rv, ls_names[i]); + unregister_ls(i); + ls_count--; + continue; + } + + event = from_he.event; + event_out = 0; + + if (event & (EVENT_RESET | EVENT_REBOOT)) { + log_warn("request to %s%s(%llx %llx) from host %llu %llu ls %s", + (event & EVENT_RESET) ? "reset " : "", + (event & EVENT_REBOOT) ? "reboot " : "", + (unsigned long long)from_he.event, + (unsigned long long)from_he.data, + (unsigned long long)from_host, + (unsigned long long)from_gen, + ls_names[i]); + } + + if (event & (EVENT_RESETTING | EVENT_REBOOTING)) { + log_warn("notice of %s%s(%llx %llx) from host %llu %llu ls %s", + (event & EVENT_RESETTING) ? "resetting " : "", + (event & EVENT_REBOOTING) ? "rebooting " : "", + (unsigned long long)from_he.event, + (unsigned long long)from_he.data, + (unsigned long long)from_host, + (unsigned long long)from_gen, + ls_names[i]); + } + + if ((event & EVENT_REBOOT) && !use_sysrq_reboot) { + event &= ~EVENT_REBOOT; + log_error("ignore reboot request sysrq_reboot not enabled"); + } + + if ((event & EVENT_RESET) && !we_are_resetting) { + we_are_resetting = 1; + poll_timeout = 1000; + watchdog_reset_self(); + event_out |= EVENT_RESETTING; + } + + if ((event & EVENT_REBOOT) && !we_are_rebooting) { + we_are_rebooting = 1; + rebooting_time = monotime(); + poll_timeout = 1000; + event_out |= EVENT_REBOOTING; + } + + if (event_out) + set_event_out(ls_names[i], event_out, from_host, from_gen); + } + + if (pollfd[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { + log_debug("unregister fd %d poll %x ls %s", + ls_fd[i], pollfd[i].revents, ls_names[i]); + unregister_ls(i); + ls_count--; + } + } + + if (!ls_count) + break; + } + + log_debug("unregister daemon_quit=%d ls_count=%d", daemon_quit, ls_count); + + for (i = 0; i < MAX_LS; i++) { + if (!ls_names[i]) + continue; + if (ls_fd[i] == -1) + continue; + unregister_ls(i); + } +out: + close_wdmd(); + return 0; +}
sanlock-devel@lists.fedorahosted.org