2 * Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved.
4 * This file is part of the device-mapper userspace tools.
6 * This copyrighted material is made available to anyone wishing to use,
7 * modify, copy, or redistribute it subject to the terms and conditions
8 * of the GNU Lesser General Public License v.2.1.
10 * You should have received a copy of the GNU Lesser General Public License
11 * along with this program; if not, write to the Free Software Foundation,
12 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 #include "libdevmapper-event.h"
17 //#include "libmultilog.h"
27 #include <sys/types.h>
31 #include <arpa/inet.h> /* for htonl, ntohl */
33 static int _sequence_nr = 0;
35 struct dm_event_handler {
47 enum dm_event_mask mask;
50 static void _dm_event_handler_clear_dev_info(struct dm_event_handler *dmevh)
52 dm_free(dmevh->dev_name);
54 dmevh->dev_name = dmevh->uuid = NULL;
55 dmevh->major = dmevh->minor = 0;
58 struct dm_event_handler *dm_event_handler_create(void)
60 struct dm_event_handler *dmevh = NULL;
62 if (!(dmevh = dm_malloc(sizeof(*dmevh))))
65 dmevh->dmeventd_path = NULL;
66 dmevh->dso = dmevh->dev_name = dmevh->uuid = NULL;
67 dmevh->major = dmevh->minor = 0;
74 void dm_event_handler_destroy(struct dm_event_handler *dmevh)
76 _dm_event_handler_clear_dev_info(dmevh);
78 dm_free(dmevh->dmeventd_path);
82 int dm_event_handler_set_dmeventd_path(struct dm_event_handler *dmevh, const char *dmeventd_path)
84 if (!dmeventd_path) /* noop */
87 dm_free(dmevh->dmeventd_path);
89 dmevh->dmeventd_path = dm_strdup(dmeventd_path);
90 if (!dmevh->dmeventd_path)
96 int dm_event_handler_set_dso(struct dm_event_handler *dmevh, const char *path)
102 dmevh->dso = dm_strdup(path);
109 int dm_event_handler_set_dev_name(struct dm_event_handler *dmevh, const char *dev_name)
114 _dm_event_handler_clear_dev_info(dmevh);
116 dmevh->dev_name = dm_strdup(dev_name);
117 if (!dmevh->dev_name)
122 int dm_event_handler_set_uuid(struct dm_event_handler *dmevh, const char *uuid)
127 _dm_event_handler_clear_dev_info(dmevh);
129 dmevh->uuid = dm_strdup(uuid);
135 void dm_event_handler_set_major(struct dm_event_handler *dmevh, int major)
137 int minor = dmevh->minor;
139 _dm_event_handler_clear_dev_info(dmevh);
141 dmevh->major = major;
142 dmevh->minor = minor;
145 void dm_event_handler_set_minor(struct dm_event_handler *dmevh, int minor)
147 int major = dmevh->major;
149 _dm_event_handler_clear_dev_info(dmevh);
151 dmevh->major = major;
152 dmevh->minor = minor;
155 void dm_event_handler_set_event_mask(struct dm_event_handler *dmevh,
156 enum dm_event_mask evmask)
158 dmevh->mask = evmask;
161 void dm_event_handler_set_timeout(struct dm_event_handler *dmevh, int timeout)
163 dmevh->timeout = timeout;
166 const char *dm_event_handler_get_dso(const struct dm_event_handler *dmevh)
171 const char *dm_event_handler_get_dev_name(const struct dm_event_handler *dmevh)
173 return dmevh->dev_name;
176 const char *dm_event_handler_get_uuid(const struct dm_event_handler *dmevh)
181 int dm_event_handler_get_major(const struct dm_event_handler *dmevh)
186 int dm_event_handler_get_minor(const struct dm_event_handler *dmevh)
191 int dm_event_handler_get_timeout(const struct dm_event_handler *dmevh)
193 return dmevh->timeout;
196 enum dm_event_mask dm_event_handler_get_event_mask(const struct dm_event_handler *dmevh)
201 static int _check_message_id(struct dm_event_daemon_message *msg)
205 if ((sscanf(msg->data, "%d:%d", &pid, &seq_nr) != 2) ||
206 (pid != getpid()) || (seq_nr != _sequence_nr)) {
207 log_error("Ignoring out-of-sequence reply from dmeventd. "
208 "Expected %d:%d but received %s", getpid(),
209 _sequence_nr, msg->data);
221 * Read message from daemon.
223 * Returns: 0 on failure, 1 on success
225 static int _daemon_read(struct dm_event_fifos *fifos,
226 struct dm_event_daemon_message *msg)
231 struct timeval tval = { 0, 0 };
232 size_t size = 2 * sizeof(uint32_t); /* status + size */
233 uint32_t *header = alloca(size);
234 char *buf = (char *)header;
236 while (bytes < size) {
237 for (i = 0, ret = 0; (i < 20) && (ret < 1); i++) {
238 /* Watch daemon read FIFO for input. */
240 FD_SET(fifos->server, &fds);
242 ret = select(fifos->server + 1, &fds, NULL, NULL,
244 if (ret < 0 && errno != EINTR) {
245 log_error("Unable to read from event server");
250 log_error("Unable to read from event server.");
254 ret = read(fifos->server, buf + bytes, size);
256 if ((errno == EINTR) || (errno == EAGAIN))
259 log_error("Unable to read from event server.");
265 if (header && (bytes == 2 * sizeof(uint32_t))) {
266 msg->cmd = ntohl(header[0]);
267 msg->size = ntohl(header[1]);
268 buf = msg->data = dm_malloc(msg->size);
279 return bytes == size;
282 /* Write message to daemon. */
283 static int _daemon_write(struct dm_event_fifos *fifos,
284 struct dm_event_daemon_message *msg)
290 size_t size = 2 * sizeof(uint32_t) + msg->size;
291 uint32_t *header = alloca(size);
292 char *buf = (char *)header;
294 struct timeval tval = { 0, 0 };
296 header[0] = htonl(msg->cmd);
297 header[1] = htonl(msg->size);
298 memcpy(buf + 2 * sizeof(uint32_t), msg->data, msg->size);
300 /* drain the answer fifo */
303 FD_SET(fifos->server, &fds);
305 ret = select(fifos->server + 1, &fds, NULL, NULL, &tval);
306 if ((ret < 0) && (errno != EINTR)) {
307 log_error("Unable to talk to event daemon");
312 read(fifos->server, drainbuf, 127);
315 while (bytes < size) {
317 /* Watch daemon write FIFO to be ready for output. */
319 FD_SET(fifos->client, &fds);
320 ret = select(fifos->client + 1, NULL, &fds, NULL, NULL);
321 if ((ret < 0) && (errno != EINTR)) {
322 log_error("Unable to talk to event daemon");
327 ret = write(fifos->client, buf + bytes, size - bytes);
329 if ((errno == EINTR) || (errno == EAGAIN))
332 log_error("Unable to talk to event daemon");
340 return bytes == size;
343 int daemon_talk(struct dm_event_fifos *fifos,
344 struct dm_event_daemon_message *msg, int cmd,
345 const char *dso_name, const char *dev_name,
346 enum dm_event_mask evmask, uint32_t timeout)
348 const char *dso = dso_name ? dso_name : "-";
349 const char *dev = dev_name ? dev_name : "-";
350 const char *fmt = "%d:%d %s %s %u %" PRIu32;
352 memset(msg, 0, sizeof(*msg));
355 * Set command and pack the arguments
356 * into ASCII message string.
359 if (cmd == DM_EVENT_CMD_HELLO)
361 if ((msg_size = dm_asprintf(&(msg->data), fmt, getpid(), _sequence_nr,
362 dso, dev, evmask, timeout)) < 0) {
363 log_error("_daemon_talk: message allocation failed");
366 msg->size = msg_size;
369 * Write command and message to and
370 * read status return code from daemon.
372 if (!_daemon_write(fifos, msg)) {
384 if (!_daemon_read(fifos, msg)) {
388 } while (!_check_message_id(msg));
392 return (int32_t) msg->cmd;
398 * This function forks off a process (dmeventd) that will handle
399 * the events. I am currently test opening one of the fifos to
400 * ensure that the daemon is running and listening... I thought
401 * this would be less expensive than fork/exec'ing every time.
402 * Perhaps there is an even quicker/better way (no, checking the
403 * lock file is _not_ a better way).
405 * Returns: 1 on success, 0 otherwise
407 static int _start_daemon(char *dmeventd_path, struct dm_event_fifos *fifos)
412 char default_dmeventd_path[] = DMEVENTD_PATH;
413 char *args[] = { dmeventd_path ? : default_dmeventd_path, NULL };
415 if (stat(fifos->client_path, &statbuf))
418 if (!S_ISFIFO(statbuf.st_mode)) {
419 log_error("%s is not a fifo.", fifos->client_path);
423 /* Anyone listening? If not, errno will be ENXIO */
424 fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK);
425 if (fifos->client >= 0) {
426 /* server is running and listening */
428 close(fifos->client);
430 } else if (errno != ENXIO) {
433 log_error("%s: Can't open client fifo %s: %s",
434 __func__, fifos->client_path, strerror(errno));
440 /* server is not running */
442 if (!strncmp(DMEVENTD_PATH, "/", 1) && stat(DMEVENTD_PATH, &statbuf)) {
443 log_error("Unable to find dmeventd.");
450 log_error("Unable to fork.");
453 execvp(args[0], args);
454 log_error("Unable to exec dmeventd: %s", strerror(errno));
457 if (waitpid(pid, &status, 0) < 0)
458 log_error("Unable to start dmeventd: %s",
460 else if (WEXITSTATUS(status))
461 log_error("Unable to start dmeventd.");
469 int init_fifos(struct dm_event_fifos *fifos)
471 /* FIXME? Is fifo the most suitable method? Why not share
472 comms/daemon code with something else e.g. multipath? */
474 /* FIXME Make these either configurable or depend directly on dmeventd_path */
475 fifos->client_path = DM_EVENT_FIFO_CLIENT;
476 fifos->server_path = DM_EVENT_FIFO_SERVER;
478 /* Open the fifo used to read from the daemon. */
479 if ((fifos->server = open(fifos->server_path, O_RDWR)) < 0) {
480 log_error("%s: open server fifo %s",
481 __func__, fifos->server_path);
486 /* Lock out anyone else trying to do communication with the daemon. */
487 if (flock(fifos->server, LOCK_EX) < 0) {
488 log_error("%s: flock %s", __func__, fifos->server_path);
489 close(fifos->server);
493 /* if ((fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK)) < 0) {*/
494 if ((fifos->client = open(fifos->client_path, O_RDWR | O_NONBLOCK)) < 0) {
495 log_error("%s: Can't open client fifo %s: %s",
496 __func__, fifos->client_path, strerror(errno));
497 close(fifos->server);
505 /* Initialize client. */
506 static int _init_client(char *dmeventd_path, struct dm_event_fifos *fifos)
509 memset(fifos, 0, sizeof(*fifos));
511 /* FIXME Make these either configurable or depend directly on dmeventd_path */
512 fifos->client_path = DM_EVENT_FIFO_CLIENT;
513 fifos->server_path = DM_EVENT_FIFO_SERVER;
515 if (!_start_daemon(dmeventd_path, fifos))
518 return init_fifos(fifos);
521 void fini_fifos(struct dm_event_fifos *fifos)
523 if (flock(fifos->server, LOCK_UN))
524 log_error("flock unlock %s", fifos->server_path);
526 close(fifos->client);
527 close(fifos->server);
530 /* Get uuid of a device */
531 static struct dm_task *_get_device_info(const struct dm_event_handler *dmevh)
536 if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
537 log_error("_get_device_info: dm_task creation for info failed");
542 dm_task_set_uuid(dmt, dmevh->uuid);
543 else if (dmevh->dev_name)
544 dm_task_set_name(dmt, dmevh->dev_name);
545 else if (dmevh->major && dmevh->minor) {
546 dm_task_set_major(dmt, dmevh->major);
547 dm_task_set_minor(dmt, dmevh->minor);
550 /* FIXME Add name or uuid or devno to messages */
551 if (!dm_task_run(dmt)) {
552 log_error("_get_device_info: dm_task_run() failed");
556 if (!dm_task_get_info(dmt, &info)) {
557 log_error("_get_device_info: failed to get info for device");
562 log_error("_get_device_info: device not found");
569 dm_task_destroy(dmt);
573 /* Handle the event (de)registration call and return negative error codes. */
574 static int _do_event(int cmd, char *dmeventd_path, struct dm_event_daemon_message *msg,
575 const char *dso_name, const char *dev_name,
576 enum dm_event_mask evmask, uint32_t timeout)
579 struct dm_event_fifos fifos;
581 if (!_init_client(dmeventd_path, &fifos)) {
586 ret = daemon_talk(&fifos, msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0);
592 ret = daemon_talk(&fifos, msg, cmd, dso_name, dev_name, evmask, timeout);
594 /* what is the opposite of init? */
600 /* External library interface. */
601 int dm_event_register_handler(const struct dm_event_handler *dmevh)
606 struct dm_event_daemon_message msg = { 0, 0, NULL };
608 if (!(dmt = _get_device_info(dmevh))) {
613 uuid = dm_task_get_uuid(dmt);
615 if ((err = _do_event(DM_EVENT_CMD_REGISTER_FOR_EVENT, dmevh->dmeventd_path, &msg,
616 dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
617 log_error("%s: event registration failed: %s",
618 dm_task_get_name(dmt),
619 msg.data ? msg.data : strerror(-err));
625 dm_task_destroy(dmt);
630 int dm_event_unregister_handler(const struct dm_event_handler *dmevh)
635 struct dm_event_daemon_message msg = { 0, 0, NULL };
637 if (!(dmt = _get_device_info(dmevh))) {
642 uuid = dm_task_get_uuid(dmt);
644 if ((err = _do_event(DM_EVENT_CMD_UNREGISTER_FOR_EVENT, dmevh->dmeventd_path, &msg,
645 dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
646 log_error("%s: event deregistration failed: %s",
647 dm_task_get_name(dmt),
648 msg.data ? msg.data : strerror(-err));
654 dm_task_destroy(dmt);
659 /* Fetch a string off src and duplicate it into *dest. */
660 /* FIXME: move to separate module to share with the daemon. */
661 static char *_fetch_string(char **src, const int delimiter)
665 if ((p = strchr(*src, delimiter)))
668 if ((ret = dm_strdup(*src)))
669 *src += strlen(ret) + 1;
677 /* Parse a device message from the daemon. */
678 static int _parse_message(struct dm_event_daemon_message *msg, char **dso_name,
679 char **uuid, enum dm_event_mask *evmask)
684 if ((id = _fetch_string(&p, ' ')) &&
685 (*dso_name = _fetch_string(&p, ' ')) &&
686 (*uuid = _fetch_string(&p, ' '))) {
699 * Returns 0 if handler found; error (-ENOMEM, -ENOENT) otherwise.
701 int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
704 const char *uuid = NULL;
705 char *reply_dso = NULL, *reply_uuid = NULL;
706 enum dm_event_mask reply_mask = 0;
707 struct dm_task *dmt = NULL;
708 struct dm_event_daemon_message msg = { 0, 0, NULL };
711 if (!(dmt = _get_device_info(dmevh))) {
716 uuid = dm_task_get_uuid(dmt);
718 if (!(ret = _do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
719 DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path,
720 &msg, dmevh->dso, uuid, dmevh->mask, 0))) {
721 /* FIXME this will probably horribly break if we get
722 ill-formatted reply */
723 ret = _parse_message(&msg, &reply_dso, &reply_uuid, &reply_mask);
729 dm_task_destroy(dmt);
735 _dm_event_handler_clear_dev_info(dmevh);
736 dmevh->uuid = dm_strdup(reply_uuid);
742 if (!(dmt = _get_device_info(dmevh))) {
743 ret = -ENXIO; /* dmeventd probably gave us bogus uuid back */
747 dm_event_handler_set_dso(dmevh, reply_dso);
748 dm_event_handler_set_event_mask(dmevh, reply_mask);
756 dmevh->dev_name = dm_strdup(dm_task_get_name(dmt));
757 if (!dmevh->dev_name) {
762 if (!dm_task_get_info(dmt, &info)) {
767 dmevh->major = info.major;
768 dmevh->minor = info.minor;
770 dm_task_destroy(dmt);
778 _dm_event_handler_clear_dev_info(dmevh);
780 dm_task_destroy(dmt);
784 #if 0 /* left out for now */
786 static char *_skip_string(char *src, const int delimiter)
788 src = srtchr(src, delimiter);
789 if (src && *(src + 1))
794 int dm_event_set_timeout(const char *device_path, uint32_t timeout)
796 struct dm_event_daemon_message msg = { 0, 0, NULL };
798 if (!device_exists(device_path))
801 return _do_event(DM_EVENT_CMD_SET_TIMEOUT, &msg,
802 NULL, device_path, 0, timeout);
805 int dm_event_get_timeout(const char *device_path, uint32_t *timeout)
808 struct dm_event_daemon_message msg = { 0, 0, NULL };
810 if (!device_exists(device_path))
812 if (!(ret = _do_event(DM_EVENT_CMD_GET_TIMEOUT, &msg, NULL, device_path,
814 char *p = _skip_string(msg.data, ' ');
816 log_error("malformed reply from dmeventd '%s'\n",