diff options
Diffstat (limited to 'daemons/dmeventd')
21 files changed, 1201 insertions, 261 deletions
diff --git a/daemons/dmeventd/.exported_symbols b/daemons/dmeventd/.exported_symbols index 25690c8..fab74dc 100644 --- a/daemons/dmeventd/.exported_symbols +++ b/daemons/dmeventd/.exported_symbols @@ -1,3 +1,4 @@ init_fifos fini_fifos daemon_talk +dm_event_get_version diff --git a/daemons/dmeventd/Makefile.in b/daemons/dmeventd/Makefile.in index e99e089..1302a44 100644 --- a/daemons/dmeventd/Makefile.in +++ b/daemons/dmeventd/Makefile.in @@ -1,5 +1,5 @@ # -# Copyright (C) 2005-2010 Red Hat, Inc. All rights reserved. +# Copyright (C) 2005-2011 Red Hat, Inc. All rights reserved. # # This file is part of the device-mapper userspace tools. # @@ -60,11 +60,11 @@ LIBS += -ldevmapper LVMLIBS += -ldevmapper-event $(PTHREAD_LIBS) dmeventd: $(LIB_SHARED) dmeventd.o - $(CC) $(CFLAGS) $(LDFLAGS) -L. -o $@ dmeventd.o \ + $(CC) $(CFLAGS) $(LDFLAGS) $(ELDFLAGS) -L. -o $@ dmeventd.o \ $(DL_LIBS) $(LVMLIBS) $(LIBS) -rdynamic dmeventd.static: $(LIB_STATIC) dmeventd.o $(interfacebuilddir)/libdevmapper.a - $(CC) $(CFLAGS) $(LDFLAGS) -static -L. -L$(interfacebuilddir) -o $@ \ + $(CC) $(CFLAGS) $(LDFLAGS) $(ELDFLAGS) -static -L. -L$(interfacebuilddir) -o $@ \ dmeventd.o $(DL_LIBS) $(LVMLIBS) $(LIBS) $(STATIC_LIBS) ifeq ("@PKGCONFIG@", "yes") @@ -105,4 +105,4 @@ install: install_include install_lib install_dmeventd install_device-mapper: install_include install_lib install_dmeventd -DISTCLEAN_TARGETS += libdevmapper-event.pc .exported_symbols_generated +DISTCLEAN_TARGETS += libdevmapper-event.pc diff --git a/daemons/dmeventd/dmeventd.c b/daemons/dmeventd/dmeventd.c index 2b454f9..13148c3 100644 --- a/daemons/dmeventd/dmeventd.c +++ b/daemons/dmeventd/dmeventd.c @@ -39,13 +39,27 @@ #include <arpa/inet.h> /* for htonl, ntohl */ #ifdef linux -# include <malloc.h> - -# define OOM_ADJ_FILE "/proc/self/oom_adj" +/* + * Kernel version 2.6.36 and higher has + * new OOM killer adjustment interface. + */ +# define OOM_ADJ_FILE_OLD "/proc/self/oom_adj" +# define OOM_ADJ_FILE "/proc/self/oom_score_adj" /* From linux/oom.h */ +/* Old interface */ # define OOM_DISABLE (-17) # define OOM_ADJUST_MIN (-16) +/* New interface */ +# define OOM_SCORE_ADJ_MIN (-1000) + +/* Systemd on-demand activation support */ +# define SD_ACTIVATION_ENV_VAR_NAME "SD_ACTIVATION" +# define SD_LISTEN_PID_ENV_VAR_NAME "LISTEN_PID" +# define SD_LISTEN_FDS_ENV_VAR_NAME "LISTEN_FDS" +# define SD_LISTEN_FDS_START 3 +# define SD_FD_FIFO_SERVER SD_LISTEN_FDS_START +# define SD_FD_FIFO_CLIENT (SD_LISTEN_FDS_START + 1) #endif @@ -95,9 +109,8 @@ static pthread_mutex_t _global_mutex; #define THREAD_STACK_SIZE (300*1024) -#define DEBUGLOG(fmt, args...) _debuglog(fmt, ## args) - int dmeventd_debug = 0; +static int _systemd_activation = 0; static int _foreground = 0; static int _restart = 0; static char **_initial_registrations = 0; @@ -203,24 +216,6 @@ static DM_LIST_INIT(_timeout_registry); static pthread_mutex_t _timeout_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t _timeout_cond = PTHREAD_COND_INITIALIZER; -static void _debuglog(const char *fmt, ...) -{ - time_t P; - va_list ap; - - if (!_foreground) - return; - - va_start(ap,fmt); - - time(&P); - fprintf(stderr, "dmeventd[%p]: %.15s ", (void *) pthread_self(), ctime(&P)+4 ); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - - va_end(ap); -} - /* Allocate/free the status structure for a monitoring thread. */ static struct thread_status *_alloc_thread_status(struct message_data *data, struct dso_data *dso_data) @@ -407,7 +402,9 @@ static int _fill_device_data(struct thread_status *ts) if (!dmt) return 0; - dm_task_set_uuid(dmt, ts->device.uuid); + if (!dm_task_set_uuid(dmt, ts->device.uuid)) + goto fail; + if (!dm_task_run(dmt)) goto fail; @@ -450,7 +447,7 @@ static int _get_status(struct message_data *message_data) { struct dm_event_daemon_message *msg = message_data->msg; struct thread_status *thread; - int i = 0, j = 0; + int i, j; int ret = -1; int count = dm_list_size(&_thread_registry); int size = 0, current = 0; @@ -585,6 +582,7 @@ static void _unregister_for_timeout(struct thread_status *thread) pthread_mutex_unlock(&_timeout_mutex); } +__attribute__((format(printf, 4, 5))) static void _no_intr_log(int level, const char *file, int line, const char *f, ...) { @@ -740,8 +738,10 @@ static void _monitor_unregister(void *arg) return; } thread->status = DM_THREAD_DONE; + pthread_mutex_lock(&_timeout_mutex); UNLINK_THREAD(thread); LINK(thread, &_thread_registry_unused); + pthread_mutex_unlock(&_timeout_mutex); _unlock_mutex(); } @@ -752,7 +752,10 @@ static struct dm_task *_get_device_status(struct thread_status *ts) if (!dmt) return NULL; - dm_task_set_uuid(dmt, ts->device.uuid); + if (!dm_task_set_uuid(dmt, ts->device.uuid)) { + dm_task_destroy(dmt); + return NULL; + } if (!dm_task_run(dmt)) { dm_task_destroy(dmt); @@ -996,10 +999,8 @@ static int _register_for_event(struct message_data *message_data) almost as good as dead already... */ if (thread_new->events & DM_EVENT_TIMEOUT) { ret = -_register_for_timeout(thread_new); - if (ret) { - _unlock_mutex(); - goto out; - } + if (ret) + goto outth; } if (!(thread = _lookup_thread_status(message_data))) { @@ -1025,6 +1026,7 @@ static int _register_for_event(struct message_data *message_data) /* Or event # into events bitfield. */ thread->events |= message_data->events.field; + outth: _unlock_mutex(); out: @@ -1076,8 +1078,10 @@ static int _unregister_for_event(struct message_data *message_data) * unlink and terminate its monitoring thread. */ if (!thread->events) { + pthread_mutex_lock(&_timeout_mutex); UNLINK_THREAD(thread); LINK(thread, &_thread_registry_unused); + pthread_mutex_unlock(&_timeout_mutex); } _unlock_mutex(); @@ -1099,15 +1103,19 @@ static int _registered_device(struct message_data *message_data, const char *id = message_data->id; const char *dso = thread->dso_data->dso_name; const char *dev = thread->device.uuid; + int r; unsigned events = ((thread->status == DM_THREAD_RUNNING) && (thread->events)) ? thread->events : thread-> events | DM_EVENT_REGISTRATION_PENDING; dm_free(msg->data); - msg->size = dm_asprintf(&(msg->data), fmt, id, dso, dev, events); + if ((r = dm_asprintf(&(msg->data), fmt, id, dso, dev, events)) < 0) { + msg->size = 0; + return -ENOMEM; + } - _unlock_mutex(); + msg->size = (uint32_t) r; return 0; } @@ -1140,6 +1148,7 @@ static int _want_registered_device(char *dso_name, char *device_uuid, static int _get_registered_dev(struct message_data *message_data, int next) { struct thread_status *thread, *hit = NULL; + int ret = -ENOENT; _lock_mutex(); @@ -1156,16 +1165,12 @@ static int _get_registered_dev(struct message_data *message_data, int next) * If we got a registered device and want the next one -> * fetch next conforming element off the list. */ - if (hit && !next) { - _unlock_mutex(); - return _registered_device(message_data, hit); - } + if (hit && !next) + goto reg; if (!hit) goto out; - thread = hit; - while (1) { if (dm_list_end(&_thread_registry, &thread->list)) goto out; @@ -1177,13 +1182,13 @@ static int _get_registered_dev(struct message_data *message_data, int next) } } - _unlock_mutex(); - return _registered_device(message_data, hit); + reg: + ret = _registered_device(message_data, hit); out: _unlock_mutex(); - - return -ENOENT; + + return ret; } static int _get_registered_device(struct message_data *message_data) @@ -1241,68 +1246,66 @@ static void _init_fifos(struct dm_event_fifos *fifos) /* Open fifos used for client communication. */ static int _open_fifos(struct dm_event_fifos *fifos) { - int orig_errno; + struct stat st; /* Create client fifo. */ (void) dm_prepare_selinux_context(fifos->client_path, S_IFIFO); if ((mkfifo(fifos->client_path, 0600) == -1) && errno != EEXIST) { - syslog(LOG_ERR, "%s: Failed to create client fifo.\n", __func__); - orig_errno = errno; + syslog(LOG_ERR, "%s: Failed to create client fifo %s: %m.\n", + __func__, fifos->client_path); (void) dm_prepare_selinux_context(NULL, 0); - stack; - return -orig_errno; + return 0; } /* Create server fifo. */ (void) dm_prepare_selinux_context(fifos->server_path, S_IFIFO); if ((mkfifo(fifos->server_path, 0600) == -1) && errno != EEXIST) { - syslog(LOG_ERR, "%s: Failed to create server fifo.\n", __func__); - orig_errno = errno; + syslog(LOG_ERR, "%s: Failed to create server fifo %s: %m.\n", + __func__, fifos->server_path); (void) dm_prepare_selinux_context(NULL, 0); - stack; - return -orig_errno; + return 0; } (void) dm_prepare_selinux_context(NULL, 0); - struct stat st; - /* Warn about wrong permissions if applicable */ if ((!stat(fifos->client_path, &st)) && (st.st_mode & 0777) != 0600) - syslog(LOG_WARNING, "Fixing wrong permissions on %s", + syslog(LOG_WARNING, "Fixing wrong permissions on %s: %m.\n", fifos->client_path); if ((!stat(fifos->server_path, &st)) && (st.st_mode & 0777) != 0600) - syslog(LOG_WARNING, "Fixing wrong permissions on %s", + syslog(LOG_WARNING, "Fixing wrong permissions on %s: %m.\n", fifos->server_path); /* If they were already there, make sure permissions are ok. */ if (chmod(fifos->client_path, 0600)) { - syslog(LOG_ERR, "Unable to set correct file permissions on %s", + syslog(LOG_ERR, "Unable to set correct file permissions on %s: %m.\n", fifos->client_path); - return -errno; + return 0; } if (chmod(fifos->server_path, 0600)) { - syslog(LOG_ERR, "Unable to set correct file permissions on %s", + syslog(LOG_ERR, "Unable to set correct file permissions on %s: %m.\n", fifos->server_path); - return -errno; + return 0; } /* Need to open read+write or we will block or fail */ if ((fifos->server = open(fifos->server_path, O_RDWR)) < 0) { - stack; - return -errno; + syslog(LOG_ERR, "Failed to open fifo server %s: %m.\n", + fifos->server_path); + return 0; } /* Need to open read+write for select() to work. */ if ((fifos->client = open(fifos->client_path, O_RDWR)) < 0) { - stack; - close(fifos->server); - return -errno; + syslog(LOG_ERR, "Failed to open fifo client %s: %m", fifos->client_path); + if (close(fifos->server)) + syslog(LOG_ERR, "Failed to close fifo server %s: %m", fifos->server_path); + return 0; } - return 0; + return 1; } /* @@ -1405,7 +1408,7 @@ static int _client_write(struct dm_event_fifos *fifos, static int _handle_request(struct dm_event_daemon_message *msg, struct message_data *message_data) { - static struct { + static struct request { unsigned int cmd; int (*f)(struct message_data *); } requests[] = { @@ -1420,7 +1423,7 @@ static int _handle_request(struct dm_event_daemon_message *msg, { DM_EVENT_CMD_GET_STATUS, _get_status}, }, *req; - for (req = requests; req < requests + sizeof(requests); req++) + for (req = requests; req < requests + sizeof(requests) / sizeof(struct request); req++) if (req->cmd == msg->cmd) return req->f(message_data); @@ -1432,17 +1435,16 @@ static int _do_process_request(struct dm_event_daemon_message *msg) { int ret; char *answer; - static struct message_data message_data; + struct message_data message_data = { .msg = msg }; /* Parse the message. */ - memset(&message_data, 0, sizeof(message_data)); - message_data.msg = msg; if (msg->cmd == DM_EVENT_CMD_HELLO || msg->cmd == DM_EVENT_CMD_DIE) { ret = 0; answer = msg->data; if (answer) { - msg->size = dm_asprintf(&(msg->data), "%s %s", answer, - msg->cmd == DM_EVENT_CMD_DIE ? "DYING" : "HELLO"); + msg->size = dm_asprintf(&(msg->data), "%s %s %d", answer, + msg->cmd == DM_EVENT_CMD_DIE ? "DYING" : "HELLO", + DM_EVENT_PROTOCOL_VERSION); dm_free(answer); } else { msg->size = 0; @@ -1467,9 +1469,7 @@ static int _do_process_request(struct dm_event_daemon_message *msg) static void _process_request(struct dm_event_fifos *fifos) { int die = 0; - struct dm_event_daemon_message msg; - - memset(&msg, 0, sizeof(msg)); + struct dm_event_daemon_message msg = { 0 }; /* * Read the request from the client (client_read, client_write @@ -1488,9 +1488,9 @@ static void _process_request(struct dm_event_fifos *fifos) if (!_client_write(fifos, &msg)) stack; - if (die) raise(9); - dm_free(msg.data); + + if (die) raise(9); } static void _process_initial_registrations(void) @@ -1501,9 +1501,10 @@ static void _process_initial_registrations(void) while ((reg = _initial_registrations[i])) { msg.cmd = DM_EVENT_CMD_REGISTER_FOR_EVENT; - msg.size = strlen(reg); - msg.data = reg; - _do_process_request(&msg); + if ((msg.size = strlen(reg))) { + msg.data = reg; + _do_process_request(&msg); + } ++ i; } } @@ -1513,6 +1514,7 @@ static void _cleanup_unused_threads(void) int ret; struct dm_list *l; struct thread_status *thread; + int join_ret = 0; _lock_mutex(); while ((l = dm_list_first(&_thread_registry_unused))) { @@ -1552,12 +1554,15 @@ static void _cleanup_unused_threads(void) if (thread->status == DM_THREAD_DONE) { dm_list_del(l); - pthread_join(thread->thread, NULL); + join_ret = pthread_join(thread->thread, NULL); _free_thread_status(thread); } } _unlock_mutex(); + + if (join_ret) + syslog(LOG_ERR, "Failed pthread_join: %s\n", strerror(join_ret)); } static void _sig_alarm(int signum __attribute__((unused))) @@ -1569,10 +1574,8 @@ static void _sig_alarm(int signum __attribute__((unused))) static void _init_thread_signals(void) { sigset_t my_sigset; - struct sigaction act; + struct sigaction act = { .sa_handler = _sig_alarm }; - memset(&act, 0, sizeof(act)); - act.sa_handler = _sig_alarm; sigaction(SIGALRM, &act, NULL); sigfillset(&my_sigset); @@ -1610,40 +1613,138 @@ static void _exit_handler(int sig __attribute__((unused))) } #ifdef linux +static int _set_oom_adj(const char *oom_adj_path, int val) +{ + FILE *fp; + + if (!(fp = fopen(oom_adj_path, "w"))) { + perror("oom_adj: fopen failed"); + return 0; + } + + fprintf(fp, "%i", val); + + if (dm_fclose(fp)) + perror("oom_adj: fclose failed"); + + return 1; +} + /* * Protection against OOM killer if kernel supports it */ -static int _set_oom_adj(int val) +static int _protect_against_oom_killer(void) { - FILE *fp; - struct stat st; if (stat(OOM_ADJ_FILE, &st) == -1) { - if (errno == ENOENT) - DEBUGLOG(OOM_ADJ_FILE " not found"); - else + if (errno != ENOENT) perror(OOM_ADJ_FILE ": stat failed"); - return 1; + + /* Try old oom_adj interface as a fallback */ + if (stat(OOM_ADJ_FILE_OLD, &st) == -1) { + if (errno == ENOENT) + perror(OOM_ADJ_FILE_OLD " not found"); + else + perror(OOM_ADJ_FILE_OLD ": stat failed"); + return 1; + } + + return _set_oom_adj(OOM_ADJ_FILE_OLD, OOM_DISABLE) || + _set_oom_adj(OOM_ADJ_FILE_OLD, OOM_ADJUST_MIN); } - if (!(fp = fopen(OOM_ADJ_FILE, "w"))) { - perror(OOM_ADJ_FILE ": fopen failed"); + return _set_oom_adj(OOM_ADJ_FILE, OOM_SCORE_ADJ_MIN); +} + +static int _handle_preloaded_fifo(int fd, const char *path) +{ + struct stat st_fd, st_path; + int flags; + + if ((flags = fcntl(fd, F_GETFD)) < 0) return 0; - } - fprintf(fp, "%i", val); - if (dm_fclose(fp)) - perror(OOM_ADJ_FILE ": fclose failed"); + if (flags & FD_CLOEXEC) + return 0; + + if (fstat(fd, &st_fd) < 0 || !S_ISFIFO(st_fd.st_mode)) + return 0; + + if (stat(path, &st_path) < 0 || + st_path.st_dev != st_fd.st_dev || + st_path.st_ino != st_fd.st_ino) + return 0; + + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) + return 0; return 1; } + +static int _systemd_handover(struct dm_event_fifos *fifos) +{ + const char *e; + char *p; + unsigned long env_pid, env_listen_fds; + int r = 0; + + memset(fifos, 0, sizeof(*fifos)); + + /* SD_ACTIVATION must be set! */ + if (!(e = getenv(SD_ACTIVATION_ENV_VAR_NAME)) || strcmp(e, "1")) + goto out; + + /* LISTEN_PID must be equal to our PID! */ + if (!(e = getenv(SD_LISTEN_PID_ENV_VAR_NAME))) + goto out; + + errno = 0; + env_pid = strtoul(e, &p, 10); + if (errno || !p || *p || env_pid <= 0 || + getpid() != (pid_t) env_pid) + goto out; + + /* LISTEN_FDS must be 2 and the fds must be FIFOSs! */ + if (!(e = getenv(SD_LISTEN_FDS_ENV_VAR_NAME))) + goto out; + + errno = 0; + env_listen_fds = strtoul(e, &p, 10); + if (errno || !p || *p || env_listen_fds != 2) + goto out; + + /* Check and handle the FIFOs passed in */ + r = (_handle_preloaded_fifo(SD_FD_FIFO_SERVER, DM_EVENT_FIFO_SERVER) && + _handle_preloaded_fifo(SD_FD_FIFO_CLIENT, DM_EVENT_FIFO_CLIENT)); + + if (r) { + fifos->server = SD_FD_FIFO_SERVER; + fifos->server_path = DM_EVENT_FIFO_SERVER; + fifos->client = SD_FD_FIFO_CLIENT; + fifos->client_path = DM_EVENT_FIFO_CLIENT; + } + +out: + unsetenv(SD_ACTIVATION_ENV_VAR_NAME); + unsetenv(SD_LISTEN_PID_ENV_VAR_NAME); + unsetenv(SD_LISTEN_FDS_ENV_VAR_NAME); + return r; +} #endif -static void remove_lockfile(void) +static void _remove_files_on_exit(void) { if (unlink(DMEVENTD_PIDFILE)) perror(DMEVENTD_PIDFILE ": unlink failed"); + + if (!_systemd_activation) { + if (unlink(DM_EVENT_FIFO_CLIENT)) + perror(DM_EVENT_FIFO_CLIENT " : unlink failed"); + + if (unlink(DM_EVENT_FIFO_SERVER)) + perror(DM_EVENT_FIFO_SERVER " : unlink failed"); + } } static void _daemonize(void) @@ -1703,8 +1804,15 @@ static void _daemonize(void) else fd = rlim.rlim_cur; - for (--fd; fd >= 0; fd--) - close(fd); + for (--fd; fd >= 0; fd--) { +#ifdef linux + /* Do not close fds preloaded by systemd! */ + if (_systemd_activation && + (fd == SD_FD_FIFO_SERVER || fd == SD_FD_FIFO_CLIENT)) + continue; +#endif + (void) close(fd); + } if ((open("/dev/null", O_RDONLY) < 0) || (open("/dev/null", O_WRONLY) < 0) || @@ -1721,16 +1829,25 @@ static void restart(void) int i, count = 0; char *message; int length; + int version; /* Get the list of registrations from the running daemon. */ if (!init_fifos(&fifos)) { - fprintf(stderr, "Could not initiate communication with existing dmeventd.\n"); + fprintf(stderr, "WARNING: Could not initiate communication with existing dmeventd.\n"); + exit(EXIT_FAILURE); + } + + if (!dm_event_get_version(&fifos, &version)) { + fprintf(stderr, "WARNING: Could not communicate with existing dmeventd.\n"); + fini_fifos(&fifos); exit(EXIT_FAILURE); } - if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0)) { - fprintf(stderr, "Could not communicate with existing dmeventd.\n"); + if (version < 1) { + fprintf(stderr, "WARNING: The running dmeventd instance is too old.\n" + "Protocol version %d (required: 1). Action cancelled.\n", + version); exit(EXIT_FAILURE); } @@ -1749,9 +1866,16 @@ static void restart(void) } } - _initial_registrations = dm_malloc(sizeof(char*) * (count + 1)); + if (!(_initial_registrations = dm_malloc(sizeof(char*) * (count + 1)))) { + fprintf(stderr, "Memory allocation registration failed.\n"); + exit(EXIT_FAILURE); + } + for (i = 0; i < count; ++i) { - _initial_registrations[i] = dm_strdup(message); + if (!(_initial_registrations[i] = dm_strdup(message))) { + fprintf(stderr, "Memory allocation for message failed.\n"); + exit(EXIT_FAILURE); + } message += strlen(message) + 1; } _initial_registrations[count] = 0; @@ -1761,17 +1885,28 @@ static void restart(void) exit(EXIT_FAILURE); } + /* + * Wait for daemon to die, detected by sending further DIE messages + * until one fails. + */ + for (i = 0; i < 10; ++i) { + if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_DIE, "-", "-", 0, 0)) + break; /* yep, it's dead probably */ + usleep(10); + } + fini_fifos(&fifos); } static void usage(char *prog, FILE *file) { fprintf(file, "Usage:\n" - "%s [-V] [-h] [-d] [-d] [-d] [-f]\n\n" - " -V Show version of dmeventd\n" - " -h Show this help information\n" + "%s [-d [-d [-d]]] [-f] [-h] [-R] [-V] [-?]\n\n" " -d Log debug messages to syslog (-d, -dd, -ddd)\n" - " -f Don't fork, run in the foreground\n\n", prog); + " -f Don't fork, run in the foreground\n" + " -h -? Show this help information\n" + " -R Restart dmeventd\n" + " -V Show version of dmeventd\n\n", prog); } int main(int argc, char *argv[]) @@ -1803,7 +1938,6 @@ int main(int argc, char *argv[]) case 'V': printf("dmeventd version: %s\n", DM_LIB_VERSION); exit(1); - break; } } @@ -1818,6 +1952,10 @@ int main(int argc, char *argv[]) if (_restart) restart(); +#ifdef linux + _systemd_activation = _systemd_handover(&fifos); +#endif + if (!_foreground) _daemonize(); @@ -1827,17 +1965,19 @@ int main(int argc, char *argv[]) if (dm_create_lockfile(DMEVENTD_PIDFILE) == 0) exit(EXIT_FAILURE); - atexit(remove_lockfile); + atexit(_remove_files_on_exit); (void) dm_prepare_selinux_context(NULL, 0); /* Set the rest of the signals to cause '_exit_now' to be set */ + signal(SIGTERM, &_exit_handler); signal(SIGINT, &_exit_handler); signal(SIGHUP, &_exit_handler); signal(SIGQUIT, &_exit_handler); #ifdef linux - if (!_set_oom_adj(OOM_DISABLE) && !_set_oom_adj(OOM_ADJUST_MIN)) - syslog(LOG_ERR, "Failed to set oom_adj to protect against OOM killer"); + /* Systemd has adjusted oom killer for us already */ + if (!_systemd_activation && !_protect_against_oom_killer()) + syslog(LOG_ERR, "Failed to protect against OOM killer"); #endif _init_thread_signals(); @@ -1847,11 +1987,12 @@ int main(int argc, char *argv[]) //multilog_init_verbose(std_syslog, _LOG_DEBUG); //multilog_async(1); - _init_fifos(&fifos); + if (!_systemd_activation) + _init_fifos(&fifos); pthread_mutex_init(&_global_mutex, NULL); - if (_open_fifos(&fifos)) + if (!_systemd_activation && !_open_fifos(&fifos)) exit(EXIT_FIFO_FAILURE); /* Signal parent, letting them know we are ready to go. */ @@ -1865,11 +2006,13 @@ int main(int argc, char *argv[]) while (!_exit_now) { _process_request(&fifos); _cleanup_unused_threads(); + _lock_mutex(); if (!dm_list_empty(&_thread_registry) || !dm_list_empty(&_thread_registry_unused)) _thread_registries_empty = 0; else _thread_registries_empty = 1; + _unlock_mutex(); } _exit_dm_lib(); diff --git a/daemons/dmeventd/dmeventd.h b/daemons/dmeventd/dmeventd.h index 254758e..e21cf45 100644 --- a/daemons/dmeventd/dmeventd.h +++ b/daemons/dmeventd/dmeventd.h @@ -17,11 +17,8 @@ /* FIXME This stuff must be configurable. */ -#define DM_EVENT_DAEMON "/sbin/dmeventd" -#define DM_EVENT_LOCKFILE "/var/lock/dmeventd" -#define DM_EVENT_FIFO_CLIENT "/var/run/dmeventd-client" -#define DM_EVENT_FIFO_SERVER "/var/run/dmeventd-server" -#define DM_EVENT_PIDFILE "/var/run/dmeventd.pid" +#define DM_EVENT_FIFO_CLIENT DEFAULT_DM_RUN_DIR "/dmeventd-client" +#define DM_EVENT_FIFO_SERVER DEFAULT_DM_RUN_DIR "/dmeventd-server" #define DM_EVENT_DEFAULT_TIMEOUT 10 @@ -66,11 +63,13 @@ struct dm_event_fifos { #define EXIT_CHDIR_FAILURE 7 /* Implemented in libdevmapper-event.c, but not part of public API. */ +// FIXME misuse of bitmask as enum int daemon_talk(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg, int cmd, const char *dso_name, const char *dev_name, enum dm_event_mask evmask, uint32_t timeout); int init_fifos(struct dm_event_fifos *fifos); void fini_fifos(struct dm_event_fifos *fifos); +int dm_event_get_version(struct dm_event_fifos *fifos, int *version); #endif /* __DMEVENTD_DOT_H__ */ diff --git a/daemons/dmeventd/libdevmapper-event.c b/daemons/dmeventd/libdevmapper-event.c index bc8ad99..1f8fbef 100644 --- a/daemons/dmeventd/libdevmapper-event.c +++ b/daemons/dmeventd/libdevmapper-event.c @@ -59,14 +59,10 @@ struct dm_event_handler *dm_event_handler_create(void) { struct dm_event_handler *dmevh = NULL; - if (!(dmevh = dm_malloc(sizeof(*dmevh)))) + if (!(dmevh = dm_zalloc(sizeof(*dmevh)))) { + log_error("Failed to allocate event handler."); return NULL; - - dmevh->dmeventd_path = NULL; - dmevh->dso = dmevh->dev_name = dmevh->uuid = NULL; - dmevh->major = dmevh->minor = 0; - dmevh->mask = 0; - dmevh->timeout = 0; + } return dmevh; } @@ -245,6 +241,10 @@ static int _daemon_read(struct dm_event_fifos *fifos, log_error("Unable to read from event server"); return 0; } + if ((ret == 0) && (i > 4) && !bytes) { + log_error("No input from event server."); + return 0; + } } if (ret < 1) { log_error("Unable to read from event server."); @@ -309,7 +309,7 @@ static int _daemon_write(struct dm_event_fifos *fifos, } if (ret == 0) break; - read(fifos->server, drainbuf, 127); + ret = read(fifos->server, drainbuf, 127); } while (bytes < size) { @@ -424,30 +424,27 @@ static int _start_daemon(char *dmeventd_path, struct dm_event_fifos *fifos) fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK); if (fifos->client >= 0) { /* server is running and listening */ - - close(fifos->client); + if (close(fifos->client)) + log_sys_error("close", fifos->client_path); return 1; } else if (errno != ENXIO) { /* problem */ - - log_error("%s: Can't open client fifo %s: %s", - __func__, fifos->client_path, strerror(errno)); - stack; + log_sys_error("open", fifos->client_path); return 0; } start_server: /* server is not running */ - if (!strncmp(DMEVENTD_PATH, "/", 1) && stat(DMEVENTD_PATH, &statbuf)) { - log_error("Unable to find dmeventd."); - return_0; + if ((args[0][0] == '/') && stat(args[0], &statbuf)) { + log_sys_error("stat", args[0]); + return 0; } pid = fork(); if (pid < 0) - log_error("Unable to fork."); + log_sys_error("fork", ""); else if (!pid) { execvp(args[0], args); @@ -477,25 +474,23 @@ int init_fifos(struct dm_event_fifos *fifos) /* Open the fifo used to read from the daemon. */ if ((fifos->server = open(fifos->server_path, O_RDWR)) < 0) { - log_error("%s: open server fifo %s", - __func__, fifos->server_path); - stack; + log_sys_error("open", fifos->server_path); return 0; } /* Lock out anyone else trying to do communication with the daemon. */ if (flock(fifos->server, LOCK_EX) < 0) { - log_error("%s: flock %s", __func__, fifos->server_path); - close(fifos->server); + log_sys_error("flock", fifos->server_path); + if (close(fifos->server)) + log_sys_error("close", fifos->server_path); return 0; } /* if ((fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK)) < 0) {*/ if ((fifos->client = open(fifos->client_path, O_RDWR | O_NONBLOCK)) < 0) { - log_error("%s: Can't open client fifo %s: %s", - __func__, fifos->client_path, strerror(errno)); - close(fifos->server); - stack; + log_sys_error("open", fifos->client_path); + if (close(fifos->server)) + log_sys_error("close", fifos->server_path); return 0; } @@ -523,8 +518,10 @@ void fini_fifos(struct dm_event_fifos *fifos) if (flock(fifos->server, LOCK_UN)) log_error("flock unlock %s", fifos->server_path); - close(fifos->client); - close(fifos->server); + if (close(fifos->client)) + log_sys_error("close", fifos->client_path); + if (close(fifos->server)) + log_sys_error("close", fifos->server_path); } /* Get uuid of a device */ @@ -538,34 +535,46 @@ static struct dm_task *_get_device_info(const struct dm_event_handler *dmevh) return NULL; } - if (dmevh->uuid) - dm_task_set_uuid(dmt, dmevh->uuid); - else if (dmevh->dev_name) - dm_task_set_name(dmt, dmevh->dev_name); - else if (dmevh->major && dmevh->minor) { - dm_task_set_major(dmt, dmevh->major); - dm_task_set_minor(dmt, dmevh->minor); - } + if (dmevh->uuid) { + if (!dm_task_set_uuid(dmt, dmevh->uuid)) + goto_bad; + } else if (dmevh->dev_name) { + if (!dm_task_set_name(dmt, dmevh->dev_name)) + goto_bad; + } else if (dmevh->major && dmevh->minor) { + if (!dm_task_set_major(dmt, dmevh->major) || + !dm_task_set_minor(dmt, dmevh->minor)) + goto_bad; + } /* FIXME Add name or uuid or devno to messages */ if (!dm_task_run(dmt)) { log_error("_get_device_info: dm_task_run() failed"); - goto failed; + goto bad; } if (!dm_task_get_info(dmt, &info)) { log_error("_get_device_info: failed to get info for device"); - goto failed; + goto bad; } if (!info.exists) { - log_error("_get_device_info: device not found"); - goto failed; + log_error("_get_device_info: %s%s%s%.0d%s%.0d%s%s: device not found", + dmevh->uuid ? : "", + (!dmevh->uuid && dmevh->dev_name) ? dmevh->dev_name : "", + (!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? "(" : "", + (!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? dmevh->major : 0, + (!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? ":" : "", + (!dmevh->uuid && !dmevh->dev_name && dmevh->minor > 0) ? dmevh->minor : 0, + (!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) && dmevh->minor == 0 ? "0" : "", + (!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? ") " : ""); + goto bad; } + return dmt; -failed: + bad: dm_task_destroy(dmt); return NULL; } @@ -715,17 +724,18 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next) uuid = dm_task_get_uuid(dmt); - if (!(ret = _do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE : - DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path, - &msg, dmevh->dso, uuid, dmevh->mask, 0))) { - /* FIXME this will probably horribly break if we get - ill-formatted reply */ - ret = _parse_message(&msg, &reply_dso, &reply_uuid, &reply_mask); - } else { + if (_do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE : + DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path, + &msg, dmevh->dso, uuid, dmevh->mask, 0)) { + log_debug("%s: device not registered.", dm_task_get_name(dmt)); ret = -ENOENT; goto fail; } + /* FIXME this will probably horribly break if we get + ill-formatted reply */ + ret = _parse_message(&msg, &reply_dso, &reply_uuid, &reply_mask); + dm_task_destroy(dmt); dmt = NULL; @@ -733,6 +743,10 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next) msg.data = NULL; _dm_event_handler_clear_dev_info(dmevh); + if (!reply_uuid) { + ret = -ENXIO; /* dmeventd probably gave us bogus uuid back */ + goto fail; + } dmevh->uuid = dm_strdup(reply_uuid); if (!dmevh->uuid) { ret = -ENOMEM; @@ -781,6 +795,36 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next) return ret; } +/* + * You can (and have to) call this at the stage of the protocol where + * daemon_talk(fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0) + * + * would be normally sent. This call will parse the version reply from + * dmeventd, in addition to above call. It is not safe to call this at any + * other place in the protocol. + * + * This is an internal function, not exposed in the public API. + */ + +int dm_event_get_version(struct dm_event_fifos *fifos, int *version) { + char *p; + struct dm_event_daemon_message msg = { 0, 0, NULL }; + + if (daemon_talk(fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0)) + return 0; + p = msg.data; + *version = 0; + + p = strchr(p, ' '); /* Message ID */ + if (!p) return 0; + p = strchr(p + 1, ' '); /* HELLO */ + if (!p) return 0; + p = strchr(p + 1, ' '); /* HELLO, once more */ + if (p) + *version = atoi(p); + return 1; +} + #if 0 /* left out for now */ static char *_skip_string(char *src, const int delimiter) diff --git a/daemons/dmeventd/libdevmapper-event.h b/daemons/dmeventd/libdevmapper-event.h index 0de20c1..7ce3f39 100644 --- a/daemons/dmeventd/libdevmapper-event.h +++ b/daemons/dmeventd/libdevmapper-event.h @@ -46,6 +46,7 @@ enum dm_event_mask { }; #define DM_EVENT_ALL_ERRORS DM_EVENT_ERROR_MASK +#define DM_EVENT_PROTOCOL_VERSION 1 struct dm_event_handler; @@ -81,6 +82,7 @@ void dm_event_handler_set_timeout(struct dm_event_handler *dmevh, int timeout); /* * Specify mask for events to monitor. */ +// FIXME misuse of bitmask as enum void dm_event_handler_set_event_mask(struct dm_event_handler *dmevh, enum dm_event_mask evmask); @@ -90,6 +92,7 @@ const char *dm_event_handler_get_uuid(const struct dm_event_handler *dmevh); int dm_event_handler_get_major(const struct dm_event_handler *dmevh); int dm_event_handler_get_minor(const struct dm_event_handler *dmevh); int dm_event_handler_get_timeout(const struct dm_event_handler *dmevh); +// FIXME misuse of bitmask as enum enum dm_event_mask dm_event_handler_get_event_mask(const struct dm_event_handler *dmevh); /* FIXME Review interface (what about this next thing?) */ @@ -103,6 +106,7 @@ int dm_event_unregister_handler(const struct dm_event_handler *dmevh); /* Prototypes for DSO interface, see dmeventd.c, struct dso_data for detailed descriptions. */ +// FIXME misuse of bitmask as enum void process_event(struct dm_task *dmt, enum dm_event_mask evmask, void **user); int register_device(const char *device_name, const char *uuid, int major, int minor, void **user); int unregister_device(const char *device_name, const char *uuid, int major, diff --git a/daemons/dmeventd/plugins/Makefile.in b/daemons/dmeventd/plugins/Makefile.in index 45176ad..b26e6d8 100644 --- a/daemons/dmeventd/plugins/Makefile.in +++ b/daemons/dmeventd/plugins/Makefile.in @@ -1,6 +1,6 @@ # # Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. -# Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. +# Copyright (C) 2004-2005, 2011 Red Hat, Inc. All rights reserved. # # This file is part of LVM2. # @@ -16,10 +16,31 @@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ top_builddir = @top_builddir@ -SUBDIRS += lvm2 mirror snapshot +SUBDIRS += lvm2 + +ifneq ("@MIRRORS@", "none") + SUBDIRS += mirror +endif + +ifneq ("@SNAPSHOTS@", "none") + SUBDIRS += snapshot +endif + +ifneq ("@RAID@", "none") + SUBDIRS += raid +endif + +ifneq ("@THIN@", "none") + SUBDIRS += thin +endif + +ifeq ($(MAKECMDGOALS),distclean) + SUBDIRS = lvm2 mirror snapshot raid thin +endif include $(top_builddir)/make.tmpl -mirror: lvm2 snapshot: lvm2 - +mirror: lvm2 +raid: lvm2 +thin: lvm2 diff --git a/daemons/dmeventd/plugins/lvm2/.exported_symbols b/daemons/dmeventd/plugins/lvm2/.exported_symbols index ebe3d05..646e4cf 100644 --- a/daemons/dmeventd/plugins/lvm2/.exported_symbols +++ b/daemons/dmeventd/plugins/lvm2/.exported_symbols @@ -4,3 +4,4 @@ dmeventd_lvm2_lock dmeventd_lvm2_unlock dmeventd_lvm2_pool dmeventd_lvm2_run +dmeventd_lvm2_command diff --git a/daemons/dmeventd/plugins/lvm2/Makefile.in b/daemons/dmeventd/plugins/lvm2/Makefile.in index 46247aa..fcb2a0a 100644 --- a/daemons/dmeventd/plugins/lvm2/Makefile.in +++ b/daemons/dmeventd/plugins/lvm2/Makefile.in @@ -1,5 +1,5 @@ # -# Copyright (C) 2010 Red Hat, Inc. All rights reserved. +# Copyright (C) 2010-2011 Red Hat, Inc. All rights reserved. # # This file is part of LVM2. # @@ -24,10 +24,8 @@ LIB_VERSION = $(LIB_VERSION_LVM) include $(top_builddir)/make.tmpl -LIBS += @LVM2CMD_LIB@ -ldevmapper $(PTHREAD_LIBS) +LIBS += @LVM2CMD_LIB@ -ldevmapper $(PTHREAD_LIBS) $(DAEMON_LIBS) install_lvm2: install_lib_shared install: install_lvm2 - -DISTCLEAN_TARGETS += .exported_symbols_generated diff --git a/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c b/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c index 937d81d..5d5a46b 100644 --- a/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c +++ b/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c @@ -16,8 +16,6 @@ #include "log.h" #include "lvm2cmd.h" -#include "errors.h" -#include "libdevmapper-event.h" #include "dmeventd_lvm.h" #include <pthread.h> @@ -82,10 +80,7 @@ static void _temporary_log_fn(int level, void dmeventd_lvm2_lock(void) { - if (pthread_mutex_trylock(&_event_mutex)) { - syslog(LOG_NOTICE, "Another thread is handling an event. Waiting..."); - pthread_mutex_lock(&_event_mutex); - } + pthread_mutex_lock(&_event_mutex); } void dmeventd_lvm2_unlock(void) @@ -113,6 +108,7 @@ int dmeventd_lvm2_init(void) _mem_pool = NULL; goto out; } + lvm2_disable_dmeventd_monitoring(_lvm_handle); /* FIXME Temporary: move to dmeventd core */ lvm2_run(_lvm_handle, "_memlock_inc"); } @@ -150,3 +146,31 @@ int dmeventd_lvm2_run(const char *cmdline) return lvm2_run(_lvm_handle, cmdline); } +int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size, + const char *cmd, const char *device) +{ + char *vg = NULL, *lv = NULL, *layer; + int r; + + if (!dm_split_lvm_name(mem, device, &vg, &lv, &layer)) { + syslog(LOG_ERR, "Unable to determine VG name from %s.\n", + device); + return 0; + } + + /* strip off the mirror component designations */ + layer = strstr(lv, "_mlog"); + if (layer) + *layer = '\0'; + + r = dm_snprintf(buffer, size, "%s %s/%s", cmd, vg, lv); + + dm_pool_free(mem, vg); + + if (r < 0) { + syslog(LOG_ERR, "Unable to form LVM command. (too long).\n"); + return 0; + } + + return 1; +} diff --git a/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h b/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h index 8efcb9b..1960c71 100644 --- a/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h +++ b/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h @@ -36,4 +36,7 @@ void dmeventd_lvm2_unlock(void); struct dm_pool *dmeventd_lvm2_pool(void); +int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size, + const char *cmd, const char *device); + #endif /* _DMEVENTD_LVMWRAP_H */ diff --git a/daemons/dmeventd/plugins/mirror/Makefile.in b/daemons/dmeventd/plugins/mirror/Makefile.in index 7f80629..85b33c9 100644 --- a/daemons/dmeventd/plugins/mirror/Makefile.in +++ b/daemons/dmeventd/plugins/mirror/Makefile.in @@ -1,6 +1,6 @@ # # Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. -# Copyright (C) 2004-2005, 2008-2010 Red Hat, Inc. All rights reserved. +# Copyright (C) 2004-2005, 2008-2011 Red Hat, Inc. All rights reserved. # # This file is part of LVM2. # @@ -30,10 +30,8 @@ CFLOW_LIST_TARGET = $(LIB_NAME).cflow include $(top_builddir)/make.tmpl -LIBS += -ldevmapper-event-lvm2 -ldevmapper +LIBS += -ldevmapper-event-lvm2 -ldevmapper $(DAEMON_LIBS) install_lvm2: install_dm_plugin install: install_lvm2 - -DISTCLEAN_TARGETS += .exported_symbols_generated diff --git a/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c b/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c index 3e97d87..e59feb4 100644 --- a/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c +++ b/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Red Hat, Inc. All rights reserved. + * Copyright (C) 2005-2012 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * @@ -18,6 +18,7 @@ #include "errors.h" #include "libdevmapper-event.h" #include "dmeventd_lvm.h" +#include "defaults.h" #include <syslog.h> /* FIXME Replace syslog with multilog */ /* FIXME Missing openlog? */ @@ -81,7 +82,8 @@ static int _get_mirror_event(char *params) if (!dm_split_words(params, 1, 0, &p)) goto out_parse; - if (!(num_devs = atoi(p))) + if (!(num_devs = atoi(p)) || + (num_devs > DEFAULT_MIRROR_MAX_IMAGES) || (num_devs < 0)) goto out_parse; p += strlen(p) + 1; @@ -90,6 +92,7 @@ static int _get_mirror_event(char *params) if (!args || dm_split_words(p, num_devs + 7, 0, args) < num_devs + 5) goto out_parse; + /* FIXME: Code differs from lib/mirror/mirrored.c */ dev_status_str = args[2 + num_devs]; log_argc = atoi(args[3 + num_devs]); log_status_str = args[3 + num_devs + log_argc]; @@ -121,7 +124,7 @@ static int _get_mirror_event(char *params) out: dm_free(args); return r; - + out_parse: dm_free(args); syslog(LOG_ERR, "Unable to parse mirror status string."); @@ -133,32 +136,15 @@ static int _remove_failed_devices(const char *device) int r; #define CMD_SIZE 256 /* FIXME Use system restriction */ char cmd_str[CMD_SIZE]; - char *vg = NULL, *lv = NULL, *layer = NULL; - - if (strlen(device) > 200) /* FIXME Use real restriction */ - return -ENAMETOOLONG; /* FIXME These return code distinctions are not used so remove them! */ - - if (!dm_split_lvm_name(dmeventd_lvm2_pool(), device, &vg, &lv, &layer)) { - syslog(LOG_ERR, "Unable to determine VG name from %s.", - device); - return -ENOMEM; /* FIXME Replace with generic error return - reason for failure has already got logged */ - } - - /* strip off the mirror component designations */ - layer = strstr(lv, "_mlog"); - if (layer) - *layer = '\0'; - /* FIXME Is any sanity-checking required on %s? */ - if (CMD_SIZE <= snprintf(cmd_str, CMD_SIZE, "lvconvert --config devices{ignore_suspended_devices=1} --repair --use-policies %s/%s", vg, lv)) { - /* this error should be caught above, but doesn't hurt to check again */ - syslog(LOG_ERR, "Unable to form LVM command: Device name too long."); + if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), cmd_str, sizeof(cmd_str), + "lvconvert --config devices{ignore_suspended_devices=1} " + "--repair --use-policies", device)) return -ENAMETOOLONG; /* FIXME Replace with generic error return - reason for failure has already got logged */ - } r = dmeventd_lvm2_run(cmd_str); - syslog(LOG_INFO, "Repair of mirrored LV %s/%s %s.", vg, lv, + syslog(LOG_INFO, "Repair of mirrored device %s %s.", device, (r == ECMD_PROCESSED) ? "finished successfully" : "failed"); return (r == ECMD_PROCESSED) ? 0 : -1; @@ -227,9 +213,12 @@ int register_device(const char *device, int minor __attribute__((unused)), void **unused __attribute__((unused))) { - int r = dmeventd_lvm2_init(); + if (!dmeventd_lvm2_init()) + return 0; + syslog(LOG_INFO, "Monitoring mirror device %s for events.", device); - return r; + + return 1; } int unregister_device(const char *device, @@ -241,5 +230,6 @@ int unregister_device(const char *device, syslog(LOG_INFO, "No longer monitoring mirror device %s for events.", device); dmeventd_lvm2_exit(); + return 1; } diff --git a/daemons/dmeventd/plugins/raid/.exported_symbols b/daemons/dmeventd/plugins/raid/.exported_symbols new file mode 100644 index 0000000..b88c705 --- /dev/null +++ b/daemons/dmeventd/plugins/raid/.exported_symbols @@ -0,0 +1,3 @@ +process_event +register_device +unregister_device diff --git a/daemons/dmeventd/plugins/raid/Makefile.in b/daemons/dmeventd/plugins/raid/Makefile.in new file mode 100644 index 0000000..a6b7788 --- /dev/null +++ b/daemons/dmeventd/plugins/raid/Makefile.in @@ -0,0 +1,36 @@ +# +# Copyright (C) 2011 Red Hat, Inc. All rights reserved. +# +# This file is part of LVM2. +# +# 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 v.2. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +top_builddir = @top_builddir@ + +INCLUDES += -I$(top_srcdir)/tools -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2 +CLDFLAGS += -L$(top_builddir)/tools -L$(top_builddir)/daemons/dmeventd/plugins/lvm2 + +SOURCES = dmeventd_raid.c + +LIB_NAME = libdevmapper-event-lvm2raid +LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX) +LIB_VERSION = $(LIB_VERSION_LVM) + +CFLOW_LIST = $(SOURCES) +CFLOW_LIST_TARGET = $(LIB_NAME).cflow + +include $(top_builddir)/make.tmpl + +LIBS += -ldevmapper-event-lvm2 -ldevmapper + +install_lvm2: install_dm_plugin + +install: install_lvm2 diff --git a/daemons/dmeventd/plugins/raid/dmeventd_raid.c b/daemons/dmeventd/plugins/raid/dmeventd_raid.c new file mode 100644 index 0000000..a3ecdc1 --- /dev/null +++ b/daemons/dmeventd/plugins/raid/dmeventd_raid.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2005-2011 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * 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 Lesser General Public License v.2.1. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "lib.h" + +#include "lvm2cmd.h" +#include "errors.h" +#include "libdevmapper-event.h" +#include "dmeventd_lvm.h" + +#include <syslog.h> /* FIXME Replace syslog with multilog */ +/* FIXME Missing openlog? */ +/* FIXME Replace most syslogs with log_error() style messages and add complete context. */ +/* FIXME Reformat to 80 char lines. */ + +/* + * run_repair is a close copy to + * plugins/mirror/dmeventd_mirror.c:_remove_failed_devices() + */ +static int run_repair(const char *device) +{ + int r; +#define CMD_SIZE 256 /* FIXME Use system restriction */ + char cmd_str[CMD_SIZE]; + + if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), cmd_str, sizeof(cmd_str), + "lvconvert --config devices{ignore_suspended_devices=1} " + "--repair --use-policies", device)) + return -1; + + r = dmeventd_lvm2_run(cmd_str); + + if (r != ECMD_PROCESSED) + syslog(LOG_INFO, "Repair of RAID device %s failed.", device); + + return (r == ECMD_PROCESSED) ? 0 : -1; +} + +static int _process_raid_event(char *params, const char *device) +{ + int i, n, failure = 0; + char *p, *a[4]; + char *raid_type; + char *num_devices; + char *health_chars; + char *resync_ratio; + + /* + * RAID parms: <raid_type> <#raid_disks> \ + * <health chars> <resync ratio> + */ + if (!dm_split_words(params, 4, 0, a)) { + syslog(LOG_ERR, "Failed to process status line for %s\n", + device); + return -EINVAL; + } + raid_type = a[0]; + num_devices = a[1]; + health_chars = a[2]; + resync_ratio = a[3]; + + if (!(n = atoi(num_devices))) { + syslog(LOG_ERR, "Failed to parse number of devices for %s: %s", + device, num_devices); + return -EINVAL; + } + + for (i = 0; i < n; i++) { + switch (health_chars[i]) { + case 'A': + /* Device is 'A'live and well */ + case 'a': + /* Device is 'a'live, but not yet in-sync */ + break; + case 'D': + syslog(LOG_ERR, + "Device #%d of %s array, %s, has failed.", + i, raid_type, device); + failure++; + break; + default: + /* Unhandled character returned from kernel */ + break; + } + if (failure) + return run_repair(device); + } + + p = strstr(resync_ratio, "/"); + if (!p) { + syslog(LOG_ERR, "Failed to parse resync_ratio for %s: %s", + device, resync_ratio); + return -EINVAL; + } + p[0] = '\0'; + syslog(LOG_INFO, "%s array, %s, is %s in-sync.", + raid_type, device, strcmp(resync_ratio, p+1) ? "not" : "now"); + + return 0; +} + +void process_event(struct dm_task *dmt, + enum dm_event_mask event __attribute__((unused)), + void **unused __attribute__((unused))) +{ + void *next = NULL; + uint64_t start, length; + char *target_type = NULL; + char *params; + const char *device = dm_task_get_name(dmt); + + dmeventd_lvm2_lock(); + + do { + next = dm_get_next_target(dmt, next, &start, &length, + &target_type, ¶ms); + + if (!target_type) { + syslog(LOG_INFO, "%s mapping lost.", device); + continue; + } + + if (strcmp(target_type, "raid")) { + syslog(LOG_INFO, "%s has non-raid portion.", device); + continue; + } + + if (_process_raid_event(params, device)) + syslog(LOG_ERR, "Failed to process event for %s", + device); + } while (next); + + dmeventd_lvm2_unlock(); +} + +int register_device(const char *device, + const char *uuid __attribute__((unused)), + int major __attribute__((unused)), + int minor __attribute__((unused)), + void **unused __attribute__((unused))) +{ + if (!dmeventd_lvm2_init()) + return 0; + + syslog(LOG_INFO, "Monitoring RAID device %s for events.", device); + + return 1; +} + +int unregister_device(const char *device, + const char *uuid __attribute__((unused)), + int major __attribute__((unused)), + int minor __attribute__((unused)), + void **unused __attribute__((unused))) +{ + syslog(LOG_INFO, "No longer monitoring RAID device %s for events.", + device); + dmeventd_lvm2_exit(); + + return 1; +} diff --git a/daemons/dmeventd/plugins/snapshot/Makefile.in b/daemons/dmeventd/plugins/snapshot/Makefile.in index b8414b2..a4cff15 100644 --- a/daemons/dmeventd/plugins/snapshot/Makefile.in +++ b/daemons/dmeventd/plugins/snapshot/Makefile.in @@ -1,6 +1,6 @@ # # Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. -# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved. +# Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved. # # This file is part of the LVM2. # @@ -26,10 +26,8 @@ LIB_VERSION = $(LIB_VERSION_LVM) include $(top_builddir)/make.tmpl -LIBS += -ldevmapper-event-lvm2 -ldevmapper +LIBS += -ldevmapper-event-lvm2 -ldevmapper $(DAEMON_LIBS) install_lvm2: install_dm_plugin install: install_lvm2 - -DISTCLEAN_TARGETS += .exported_symbols_generated diff --git a/daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c b/daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c index 6fc9f56..205218a 100644 --- a/daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c +++ b/daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2010 Red Hat, Inc. All rights reserved. + * Copyright (C) 2007-2011 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * @@ -19,8 +19,6 @@ #include "libdevmapper-event.h" #include "dmeventd_lvm.h" -#include "lvm-string.h" - #include <sys/wait.h> #include <syslog.h> /* FIXME Replace syslog with multilog */ /* FIXME Missing openlog? */ @@ -40,6 +38,12 @@ struct snap_status { int max; }; +struct dso_state { + int percent_check; + int known_size; + char cmd_str[1024]; +}; + /* FIXME possibly reconcile this with target_percent when we gain access to regular LVM library here. */ static void _parse_snapshot_params(char *params, struct snap_status *status) @@ -115,26 +119,9 @@ static int _run(const char *cmd, ...) return 1; /* all good */ } -static int _extend(const char *device) +static int _extend(const char *cmd) { - char *vg = NULL, *lv = NULL, *layer = NULL; - char cmd_str[1024]; - int r = 0; - - if (!dm_split_lvm_name(dmeventd_lvm2_pool(), device, &vg, &lv, &layer)) { - syslog(LOG_ERR, "Unable to determine VG name from %s.", device); - return 0; - } - if (sizeof(cmd_str) <= snprintf(cmd_str, sizeof(cmd_str), - "lvextend --use-policies %s/%s", vg, lv)) { - syslog(LOG_ERR, "Unable to form LVM command: Device name too long."); - return 0; - } - - r = dmeventd_lvm2_run(cmd_str); - syslog(LOG_INFO, "Extension of snapshot %s/%s %s.", vg, lv, - (r == ECMD_PROCESSED) ? "finished successfully" : "failed"); - return r == ECMD_PROCESSED; + return dmeventd_lvm2_run(cmd) == ECMD_PROCESSED; } static void _umount(const char *device, int major, int minor) @@ -155,7 +142,8 @@ static void _umount(const char *device, int major, int minor) break; /* eof, likely */ /* words[0] is the mount point and words[1] is the device path */ - dm_split_words(buffer, 3, 0, words); + if (dm_split_words(buffer, 3, 0, words) < 2) + continue; /* find the major/minor of the device */ if (stat(words[0], &st)) @@ -164,9 +152,9 @@ static void _umount(const char *device, int major, int minor) if (S_ISBLK(st.st_mode) && major(st.st_rdev) == major && minor(st.st_rdev) == minor) { - syslog(LOG_ERR, "Unmounting invalid snapshot %s from %s.", device, words[1]); + syslog(LOG_ERR, "Unmounting invalid snapshot %s from %s.\n", device, words[1]); if (!_run(UMOUNT_COMMAND, "-fl", words[1], NULL)) - syslog(LOG_ERR, "Failed to umount snapshot %s from %s: %s.", + syslog(LOG_ERR, "Failed to umount snapshot %s from %s: %s.\n", device, words[1], strerror(errno)); } } @@ -185,10 +173,11 @@ void process_event(struct dm_task *dmt, char *params; struct snap_status status = { 0 }; const char *device = dm_task_get_name(dmt); - int percent, *percent_check = (int*)private; + int percent; + struct dso_state *state = *private; /* No longer monitoring, waiting for remove */ - if (!*percent_check) + if (!state->percent_check) return; dmeventd_lvm2_lock(); @@ -200,7 +189,6 @@ void process_event(struct dm_task *dmt, _parse_snapshot_params(params, &status); if (status.invalid) { - syslog(LOG_ERR, "Trying to umount invalid snapshot %s...\n", device); struct dm_info info; if (dm_task_get_info(dmt, &info)) { dmeventd_lvm2_unlock(); @@ -209,27 +197,35 @@ void process_event(struct dm_task *dmt, } /* else; too bad, but this is best-effort thing... */ } + /* Snapshot size had changed. Clear the threshold. */ + if (state->known_size != status.max) { + state->percent_check = CHECK_MINIMUM; + state->known_size = status.max; + } + /* * If the snapshot has been invalidated or we failed to parse * the status string. Report the full status string to syslog. */ if (status.invalid || !status.max) { syslog(LOG_ERR, "Snapshot %s changed state to: %s\n", device, params); - *percent_check = 0; + state->percent_check = 0; goto out; } percent = 100 * status.used / status.max; - if (percent >= *percent_check) { + if (percent >= state->percent_check) { /* Usage has raised more than CHECK_STEP since the last time. Run actions. */ - *percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP; + state->percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP; + if (percent >= WARNING_THRESH) /* Print a warning to syslog. */ syslog(LOG_WARNING, "Snapshot %s is now %i%% full.\n", device, percent); /* Try to extend the snapshot, in accord with user-set policies */ - if (!_extend(device)) - syslog(LOG_ERR, "Failed to extend snapshot %s.", device); + if (!_extend(state->cmd_str)) + syslog(LOG_ERR, "Failed to extend snapshot %s.\n", device); } + out: dmeventd_lvm2_unlock(); } @@ -240,23 +236,46 @@ int register_device(const char *device, int minor __attribute__((unused)), void **private) { - int *percent_check = (int*)private; - int r = dmeventd_lvm2_init(); + struct dso_state *state; + + if (!dmeventd_lvm2_init()) + goto out; - *percent_check = CHECK_MINIMUM; + if (!(state = dm_zalloc(sizeof(*state)))) + goto bad; + + if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), + state->cmd_str, sizeof(state->cmd_str), + "lvextend --use-policies", device)) + goto bad; + + state->percent_check = CHECK_MINIMUM; + state->known_size = 0; + *private = state; syslog(LOG_INFO, "Monitoring snapshot %s\n", device); - return r; + + return 1; +bad: + dm_free(state); + dmeventd_lvm2_exit(); +out: + syslog(LOG_ERR, "Failed to monitor snapshot %s.\n", device); + + return 0; } int unregister_device(const char *device, const char *uuid __attribute__((unused)), int major __attribute__((unused)), int minor __attribute__((unused)), - void **unused __attribute__((unused))) + void **private) { - syslog(LOG_INFO, "No longer monitoring snapshot %s\n", - device); + struct dso_state *state = *private; + + syslog(LOG_INFO, "No longer monitoring snapshot %s\n", device); + dm_free(state); dmeventd_lvm2_exit(); + return 1; } diff --git a/daemons/dmeventd/plugins/thin/.exported_symbols b/daemons/dmeventd/plugins/thin/.exported_symbols new file mode 100644 index 0000000..b88c705 --- /dev/null +++ b/daemons/dmeventd/plugins/thin/.exported_symbols @@ -0,0 +1,3 @@ +process_event +register_device +unregister_device diff --git a/daemons/dmeventd/plugins/thin/Makefile.in b/daemons/dmeventd/plugins/thin/Makefile.in new file mode 100644 index 0000000..e964ab5 --- /dev/null +++ b/daemons/dmeventd/plugins/thin/Makefile.in @@ -0,0 +1,36 @@ +# +# Copyright (C) 2011 Red Hat, Inc. All rights reserved. +# +# This file is part of LVM2. +# +# 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 v.2. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +top_builddir = @top_builddir@ + +INCLUDES += -I$(top_srcdir)/tools -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2 +CLDFLAGS += -L$(top_builddir)/tools -L$(top_builddir)/daemons/dmeventd/plugins/lvm2 + +SOURCES = dmeventd_thin.c + +LIB_NAME = libdevmapper-event-lvm2thin +LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX) +LIB_VERSION = $(LIB_VERSION_LVM) + +CFLOW_LIST = $(SOURCES) +CFLOW_LIST_TARGET = $(LIB_NAME).cflow + +include $(top_builddir)/make.tmpl + +LIBS += -ldevmapper-event-lvm2 -ldevmapper + +install_lvm2: install_dm_plugin + +install: install_lvm2 diff --git a/daemons/dmeventd/plugins/thin/dmeventd_thin.c b/daemons/dmeventd/plugins/thin/dmeventd_thin.c new file mode 100644 index 0000000..a1af4c0 --- /dev/null +++ b/daemons/dmeventd/plugins/thin/dmeventd_thin.c @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2011-2012 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * 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 Lesser General Public License v.2.1. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "lib.h" + +#include "lvm2cmd.h" +#include "errors.h" +#include "libdevmapper-event.h" +#include "dmeventd_lvm.h" + +#include <sys/wait.h> +#include <syslog.h> /* FIXME Replace syslog with multilog */ +/* FIXME Missing openlog? */ + +/* First warning when thin is 80% full. */ +#define WARNING_THRESH 80 +/* Run a check every 5%. */ +#define CHECK_STEP 5 +/* Do not bother checking thins less than 50% full. */ +#define CHECK_MINIMUM 50 + +#define UMOUNT_COMMAND "/bin/umount" + +#define THIN_DEBUG 0 + +struct dso_state { + struct dm_pool *mem; + int metadata_percent_check; + int data_percent_check; + uint64_t known_metadata_size; + uint64_t known_data_size; + char cmd_str[1024]; +}; + + +/* TODO - move this mountinfo code into library to be reusable */ +#ifdef linux +# include "kdev_t.h" +#else +# define MAJOR(x) major((x)) +# define MINOR(x) minor((x)) +# define MKDEV(x,y) makedev((x),(y)) +#endif + +/* Macros to make string defines */ +#define TO_STRING_EXP(A) #A +#define TO_STRING(A) TO_STRING_EXP(A) + +static int _is_octal(int a) +{ + return (((a) & ~7) == '0'); +} + +/* Convert mangled mountinfo into normal ASCII string */ +static void _unmangle_mountinfo_string(const char *src, char *buf) +{ + if (!src) + return; + + while (*src) { + if ((*src == '\\') && + _is_octal(src[1]) && _is_octal(src[2]) && _is_octal(src[3])) { + *buf++ = 64 * (src[1] & 7) + 8 * (src[2] & 7) + (src[3] & 7); + src += 4; + } else + *buf++ = *src++; + } + *buf = '\0'; +} + +/* Parse one line of mountinfo */ +static int _parse_mountinfo_line(const char *line, unsigned *maj, unsigned *min, char *buf) +{ + char root[PATH_MAX + 1]; + char target[PATH_MAX + 1]; + + /* TODO: maybe detect availability of %ms glib support ? */ + if (sscanf(line, "%*u %*u %u:%u %" TO_STRING(PATH_MAX) + "s %" TO_STRING(PATH_MAX) "s", + maj, min, root, target) < 4) + return 0; + + _unmangle_mountinfo_string(target, buf); + +#if THIN_DEBUG + syslog(LOG_DEBUG, "Mounted %u:%u %s", *maj, *min, buf); +#endif + + return 1; +} + +/* Get dependencies for device, and try to find matching device */ +static int _has_deps(const char *name, int tp_major, int tp_minor, int *dev_minor) +{ + struct dm_task *dmt; + const struct dm_deps *deps; + struct dm_info info; + int major, minor; + int r = 0; + + if (!(dmt = dm_task_create(DM_DEVICE_DEPS))) + return 0; + + if (!dm_task_set_name(dmt, name)) + goto out; + + if (!dm_task_no_open_count(dmt)) + goto out; + + if (!dm_task_run(dmt)) + goto out; + + if (!dm_task_get_info(dmt, &info)) + goto out; + + if (!(deps = dm_task_get_deps(dmt))) + goto out; + + if (!info.exists || deps->count != 1) + goto out; + + major = (int) MAJOR(deps->device[0]); + minor = (int) MINOR(deps->device[0]); + if ((major != tp_major) || (minor != tp_minor)) + goto out; + + *dev_minor = info.minor; + +#if THIN_DEBUG + { + char dev_name[PATH_MAX]; + if (dm_device_get_name(major, minor, 0, dev_name, sizeof(dev_name))) + syslog(LOG_DEBUG, "Found %s (%u:%u) depends on %s", + name, major, *dev_minor, dev_name); + } +#endif + r = 1; +out: + dm_task_destroy(dmt); + + return r; +} + +/* Get all active devices */ +static int _find_all_devs(dm_bitset_t bs, int tp_major, int tp_minor) +{ + struct dm_task *dmt; + struct dm_names *names; + unsigned next = 0; + int minor, r = 1; + + if (!(dmt = dm_task_create(DM_DEVICE_LIST))) + return 0; + + if (!dm_task_run(dmt)) { + r = 0; + goto out; + } + + if (!(names = dm_task_get_names(dmt))) { + r = 0; + goto out; + } + + if (!names->dev) + goto out; + + do { + names = (struct dm_names *)((char *) names + next); + if (_has_deps(names->name, tp_major, tp_minor, &minor)) + dm_bit_set(bs, minor); + next = names->next; + } while (next); + +out: + dm_task_destroy(dmt); + + return r; +} + +static int _extend(struct dso_state *state) +{ +#if THIN_DEBUG + syslog(LOG_INFO, "dmeventd executes: %s.\n", state->cmd_str); +#endif + return (dmeventd_lvm2_run(state->cmd_str) == ECMD_PROCESSED); +} + +static int _run(const char *cmd, ...) +{ + va_list ap; + int argc = 1; /* for argv[0], i.e. cmd */ + int i = 0; + const char **argv; + pid_t pid = fork(); + int status; + + if (pid == 0) { /* child */ + va_start(ap, cmd); + while (va_arg(ap, const char *)) + ++argc; + va_end(ap); + + /* + 1 for the terminating NULL */ + argv = alloca(sizeof(const char *) * (argc + 1)); + + argv[0] = cmd; + va_start(ap, cmd); + while ((argv[++i] = va_arg(ap, const char *))); + va_end(ap); + + execvp(cmd, (char **)argv); + syslog(LOG_ERR, "Failed to execute %s: %s.\n", cmd, strerror(errno)); + exit(127); + } + + if (pid > 0) { /* parent */ + if (waitpid(pid, &status, 0) != pid) + return 0; /* waitpid failed */ + if (!WIFEXITED(status) || WEXITSTATUS(status)) + return 0; /* the child failed */ + } + + if (pid < 0) + return 0; /* fork failed */ + + return 1; /* all good */ +} + +/* + * Find all thin pool users and try to umount them. + * TODO: work with read-only thin pool support + */ +static void _umount(struct dm_task *dmt, const char *device) +{ + static const char mountinfo[] = "/proc/self/mountinfo"; + static const size_t MINORS = 4096; + FILE *minfo; + char buffer[4096]; + char target[PATH_MAX]; + struct dm_info info; + unsigned maj, min; + dm_bitset_t minors; /* Bitset for active thin pool minors */ + + if (!dm_task_get_info(dmt, &info)) + return; + + dmeventd_lvm2_unlock(); + + if (!(minors = dm_bitset_create(NULL, MINORS))) { + syslog(LOG_ERR, "Failed to allocate bitset. Not unmounting %s.\n", device); + goto out; + } + + if (!(minfo = fopen(mountinfo, "r"))) { + syslog(LOG_ERR, "Could not read %s. Not umounting %s.\n", mountinfo, device); + goto out; + } + + if (!_find_all_devs(minors, info.major, info.minor)) { + syslog(LOG_ERR, "Failed to detect mounted volumes for %s.\n", device); + goto out; + } + + while (!feof(minfo)) { + /* read mountinfo line */ + if (!fgets(buffer, sizeof(buffer), minfo)) + break; /* eof, likely */ + + if (_parse_mountinfo_line(buffer, &maj, &min, target) && + (maj == info.major) && dm_bit(minors, min)) { + syslog(LOG_INFO, "Unmounting thin volume %s from %s.\n", + device, target); + if (!_run(UMOUNT_COMMAND, "-fl", target, NULL)) + syslog(LOG_ERR, "Failed to umount thin %s from %s: %s.\n", + device, target, strerror(errno)); + } + } + + if (fclose(minfo)) + syslog(LOG_ERR, "Failed to close %s\n", mountinfo); + + dm_bitset_destroy(minors); +out: + dmeventd_lvm2_lock(); +} + +void process_event(struct dm_task *dmt, + enum dm_event_mask event __attribute__((unused)), + void **private) +{ + const char *device = dm_task_get_name(dmt); + int percent; + struct dso_state *state = *private; + struct dm_status_thin_pool *tps = NULL; + void *next = NULL; + uint64_t start, length; + char *target_type = NULL; + char *params; + +#if 0 + /* No longer monitoring, waiting for remove */ + if (!state->meta_percent_check && !state->data_percent_check) + return; +#endif + dmeventd_lvm2_lock(); + + dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms); + + if (!target_type || (strcmp(target_type, "thin-pool") != 0)) { + syslog(LOG_ERR, "Invalid target type.\n"); + goto out; + } + + if (!dm_get_status_thin_pool(state->mem, params, &tps)) { + syslog(LOG_ERR, "Failed to parse status.\n"); + _umount(dmt, device); + goto out; + } + +#if THIN_DEBUG + syslog(LOG_INFO, "%p: Got status %" PRIu64 " / %" PRIu64 + " %" PRIu64 " / %" PRIu64 ".\n", state, + tps->used_metadata_blocks, tps->total_metadata_blocks, + tps->used_data_blocks, tps->total_data_blocks); +#endif + + /* Thin pool size had changed. Clear the threshold. */ + if (state->known_metadata_size != tps->total_metadata_blocks) { + state->metadata_percent_check = CHECK_MINIMUM; + state->known_metadata_size = tps->total_metadata_blocks; + } + + if (state->known_data_size != tps->total_data_blocks) { + state->data_percent_check = CHECK_MINIMUM; + state->known_data_size = tps->total_data_blocks; + } + + percent = 100 * tps->used_metadata_blocks / tps->total_metadata_blocks; + if (percent >= state->metadata_percent_check) { + /* + * Usage has raised more than CHECK_STEP since the last + * time. Run actions. + */ + state->metadata_percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP; + + /* FIXME: extension of metadata needs to be written! */ + if (percent >= WARNING_THRESH) /* Print a warning to syslog. */ + syslog(LOG_WARNING, "Thin metadata %s is now %i%% full.\n", + device, percent); + /* Try to extend the metadata, in accord with user-set policies */ + if (!_extend(state)) { + syslog(LOG_ERR, "Failed to extend thin metadata %s.\n", + device); + _umount(dmt, device); + } + /* FIXME: hmm READ-ONLY switch should happen in error path */ + } + + percent = 100 * tps->used_data_blocks / tps->total_data_blocks; + if (percent >= state->data_percent_check) { + /* + * Usage has raised more than CHECK_STEP since + * the last time. Run actions. + */ + state->data_percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP; + + if (percent >= WARNING_THRESH) /* Print a warning to syslog. */ + syslog(LOG_WARNING, "Thin %s is now %i%% full.\n", device, percent); + /* Try to extend the thin data, in accord with user-set policies */ + if (!_extend(state)) { + syslog(LOG_ERR, "Failed to extend thin %s.\n", device); + state->data_percent_check = 0; + _umount(dmt, device); + } + /* FIXME: hmm READ-ONLY switch should happen in error path */ + } +out: + if (tps) + dm_pool_free(state->mem, tps); + + dmeventd_lvm2_unlock(); +} + +int register_device(const char *device, + const char *uuid __attribute__((unused)), + int major __attribute__((unused)), + int minor __attribute__((unused)), + void **private) +{ + struct dm_pool *statemem = NULL; + struct dso_state *state; + + if (!dmeventd_lvm2_init()) + goto bad; + + if (!(statemem = dm_pool_create("thin_pool_state", 2048)) || + !(state = dm_pool_zalloc(statemem, sizeof(*state))) || + !dmeventd_lvm2_command(statemem, state->cmd_str, + sizeof(state->cmd_str), + "lvextend --use-policies", + device)) { + if (statemem) + dm_pool_destroy(statemem); + dmeventd_lvm2_exit(); + goto bad; + } + + state->mem = statemem; + state->metadata_percent_check = CHECK_MINIMUM; + state->data_percent_check = CHECK_MINIMUM; + *private = state; + + syslog(LOG_INFO, "Monitoring thin %s.\n", device); + + return 1; +bad: + syslog(LOG_ERR, "Failed to monitor thin %s.\n", device); + + return 0; +} + +int unregister_device(const char *device, + const char *uuid __attribute__((unused)), + int major __attribute__((unused)), + int minor __attribute__((unused)), + void **private) +{ + struct dso_state *state = *private; + + syslog(LOG_INFO, "No longer monitoring thin %s.\n", device); + dm_pool_destroy(state->mem); + dmeventd_lvm2_exit(); + + return 1; +} |