4 * Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved.
6 * This file is part of the device-mapper userspace tools.
8 * This copyrighted material is made available to anyone wishing to use,
9 * modify, copy, or redistribute it subject to the terms and conditions
10 * of the GNU Lesser General Public License v.2.1.
12 * You should have received a copy of the GNU Lesser General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 #include "libdevmapper-event.h"
19 //#include "libmultilog.h"
29 #include <sys/types.h>
33 #include <arpa/inet.h> /* for htonl, ntohl */
35 static int _sequence_nr
= 0;
37 struct dm_event_handler
{
47 enum dm_event_mask mask
;
50 static void _dm_event_handler_clear_dev_info(struct dm_event_handler
*dmevh
)
53 dm_free(dmevh
->dev_name
);
56 dmevh
->dev_name
= dmevh
->uuid
= NULL
;
57 dmevh
->major
= dmevh
->minor
= 0;
60 struct dm_event_handler
*dm_event_handler_create(void)
62 struct dm_event_handler
*dmevh
= NULL
;
64 if (!(dmevh
= dm_malloc(sizeof(*dmevh
))))
67 dmevh
->dso
= dmevh
->dev_name
= dmevh
->uuid
= NULL
;
68 dmevh
->major
= dmevh
->minor
= 0;
75 void dm_event_handler_destroy(struct dm_event_handler
*dmevh
)
77 _dm_event_handler_clear_dev_info(dmevh
);
83 int dm_event_handler_set_dso(struct dm_event_handler
*dmevh
, const char *path
)
90 dmevh
->dso
= dm_strdup(path
);
97 int dm_event_handler_set_dev_name(struct dm_event_handler
*dmevh
, const char *dev_name
)
102 _dm_event_handler_clear_dev_info(dmevh
);
104 dmevh
->dev_name
= dm_strdup(dev_name
);
105 if (!dmevh
->dev_name
)
110 int dm_event_handler_set_uuid(struct dm_event_handler
*dmevh
, const char *uuid
)
115 _dm_event_handler_clear_dev_info(dmevh
);
117 dmevh
->uuid
= dm_strdup(uuid
);
118 if (!dmevh
->dev_name
)
123 void dm_event_handler_set_major(struct dm_event_handler
*dmevh
, int major
)
125 int minor
= dmevh
->minor
;
127 _dm_event_handler_clear_dev_info(dmevh
);
129 dmevh
->major
= major
;
130 dmevh
->minor
= minor
;
133 void dm_event_handler_set_minor(struct dm_event_handler
*dmevh
, int minor
)
135 int major
= dmevh
->major
;
137 _dm_event_handler_clear_dev_info(dmevh
);
139 dmevh
->major
= major
;
140 dmevh
->minor
= minor
;
143 void dm_event_handler_set_event_mask(struct dm_event_handler
*dmevh
,
144 enum dm_event_mask evmask
)
146 dmevh
->mask
= evmask
;
149 void dm_event_handler_set_timeout(struct dm_event_handler
*dmevh
, int timeout
)
151 dmevh
->timeout
= timeout
;
154 const char *dm_event_handler_get_dso(const struct dm_event_handler
*dmevh
)
159 const char *dm_event_handler_get_dev_name(const struct dm_event_handler
*dmevh
)
161 return dmevh
->dev_name
;
164 const char *dm_event_handler_get_uuid(const struct dm_event_handler
*dmevh
)
169 int dm_event_handler_get_major(const struct dm_event_handler
*dmevh
)
174 int dm_event_handler_get_minor(const struct dm_event_handler
*dmevh
)
179 int dm_event_handler_get_timeout(const struct dm_event_handler
*dmevh
)
181 return dmevh
->timeout
;
184 enum dm_event_mask
dm_event_handler_get_event_mask(const struct dm_event_handler
*dmevh
)
189 static int _check_message_id(struct dm_event_daemon_message
*msg
)
193 if ((sscanf(msg
->data
, "%d:%d", &pid
, &seq_nr
) != 2) ||
194 (pid
!= getpid()) || (seq_nr
!= _sequence_nr
)) {
195 log_error("Ignoring out-of-sequence reply from dmeventd. "
196 "Expected %d:%d but received %s", getpid(),
197 _sequence_nr
, msg
->data
);
209 * Read message from daemon.
211 * Returns: 0 on failure, 1 on success
213 static int _daemon_read(struct dm_event_fifos
*fifos
,
214 struct dm_event_daemon_message
*msg
)
219 struct timeval tval
= { 0, 0 };
220 size_t size
= 2 * sizeof(uint32_t); /* status + size */
221 char *buf
= alloca(size
);
224 while (bytes
< size
) {
225 for (i
= 0, ret
= 0; (i
< 20) && (ret
< 1); i
++) {
226 /* Watch daemon read FIFO for input. */
228 FD_SET(fifos
->server
, &fds
);
230 ret
= select(fifos
->server
+ 1, &fds
, NULL
, NULL
,
232 if (ret
< 0 && errno
!= EINTR
) {
233 log_error("Unable to read from event server");
238 log_error("Unable to read from event server.");
242 ret
= read(fifos
->server
, buf
+ bytes
, size
);
244 if ((errno
== EINTR
) || (errno
== EAGAIN
))
247 log_error("Unable to read from event server.");
253 if (bytes
== 2 * sizeof(uint32_t) && header
) {
254 msg
->cmd
= ntohl(*((uint32_t *)buf
));
255 msg
->size
= ntohl(*((uint32_t *)buf
+ 1));
256 buf
= msg
->data
= dm_malloc(msg
->size
);
269 return bytes
== size
;
272 /* Write message to daemon. */
273 static int _daemon_write(struct dm_event_fifos
*fifos
,
274 struct dm_event_daemon_message
*msg
)
280 size_t size
= 2 * sizeof(uint32_t) + msg
->size
;
281 char *buf
= alloca(size
);
283 struct timeval tval
= { 0, 0 };
285 *((uint32_t *)buf
) = htonl(msg
->cmd
);
286 *((uint32_t *)buf
+ 1) = htonl(msg
->size
);
287 memcpy(buf
+ 2 * sizeof(uint32_t), msg
->data
, msg
->size
);
289 /* drain the answer fifo */
292 FD_SET(fifos
->server
, &fds
);
294 ret
= select(fifos
->server
+ 1, &fds
, NULL
, NULL
, &tval
);
295 if ((ret
< 0) && (errno
!= EINTR
)) {
296 log_error("Unable to talk to event daemon");
301 read(fifos
->server
, drainbuf
, 127);
304 while (bytes
< size
) {
306 /* Watch daemon write FIFO to be ready for output. */
308 FD_SET(fifos
->client
, &fds
);
309 ret
= select(fifos
->client
+ 1, NULL
, &fds
, NULL
, NULL
);
310 if ((ret
< 0) && (errno
!= EINTR
)) {
311 log_error("Unable to talk to event daemon");
316 ret
= write(fifos
->client
, ((char *) buf
) + bytes
,
319 if ((errno
== EINTR
) || (errno
== EAGAIN
))
322 log_error("Unable to talk to event daemon");
330 return bytes
== size
;
333 static int _daemon_talk(struct dm_event_fifos
*fifos
,
334 struct dm_event_daemon_message
*msg
, int cmd
,
335 const char *dso_name
, const char *dev_name
,
336 enum dm_event_mask evmask
, uint32_t timeout
)
338 const char *dso
= dso_name
? dso_name
: "";
339 const char *dev
= dev_name
? dev_name
: "";
340 const char *fmt
= "%d:%d %s %s %u %" PRIu32
;
342 memset(msg
, 0, sizeof(*msg
));
345 * Set command and pack the arguments
346 * into ASCII message string.
349 if (cmd
== DM_EVENT_CMD_HELLO
)
351 if ((msg_size
= dm_asprintf(&(msg
->data
), fmt
, getpid(), _sequence_nr
,
352 dso
, dev
, evmask
, timeout
)) < 0) {
353 log_error("_daemon_talk: message allocation failed");
356 msg
->size
= msg_size
;
359 * Write command and message to and
360 * read status return code from daemon.
362 if (!_daemon_write(fifos
, msg
)) {
375 if (!_daemon_read(fifos
, msg
)) {
379 } while (!_check_message_id(msg
));
383 return (int32_t) msg
->cmd
;
389 * This function forks off a process (dmeventd) that will handle
390 * the events. I am currently test opening one of the fifos to
391 * ensure that the daemon is running and listening... I thought
392 * this would be less expensive than fork/exec'ing every time.
393 * Perhaps there is an even quicker/better way (no, checking the
394 * lock file is _not_ a better way).
396 * Returns: 1 on success, 0 otherwise
398 static int _start_daemon(struct dm_event_fifos
*fifos
)
404 if (stat(fifos
->client_path
, &statbuf
))
407 if (!S_ISFIFO(statbuf
.st_mode
)) {
408 log_error("%s is not a fifo.", fifos
->client_path
);
412 /* Anyone listening? If not, errno will be ENXIO */
413 fifos
->client
= open(fifos
->client_path
, O_WRONLY
| O_NONBLOCK
);
414 if (fifos
->client
>= 0) {
415 /* server is running and listening */
417 close(fifos
->client
);
419 } else if (errno
!= ENXIO
) {
422 log_error("%s: Can't open client fifo %s: %s",
423 __func__
, fifos
->client_path
, strerror(errno
));
429 /* server is not running */
431 if (!strncmp(DMEVENTD_PATH
, "/", 1) && stat(DMEVENTD_PATH
, &statbuf
)) {
432 log_error("Unable to find dmeventd.");
439 log_error("Unable to fork.");
442 execvp(DMEVENTD_PATH
, NULL
);
445 if (waitpid(pid
, &status
, 0) < 0)
446 log_error("Unable to start dmeventd: %s",
448 else if (WEXITSTATUS(status
))
449 log_error("Unable to start dmeventd.");
457 /* Initialize client. */
458 static int _init_client(struct dm_event_fifos
*fifos
)
460 /* FIXME? Is fifo the most suitable method? Why not share
461 comms/daemon code with something else e.g. multipath? */
464 memset(fifos
, 0, sizeof(*fifos
));
465 fifos
->client_path
= DM_EVENT_FIFO_CLIENT
;
466 fifos
->server_path
= DM_EVENT_FIFO_SERVER
;
468 if (!_start_daemon(fifos
)) {
473 /* Open the fifo used to read from the daemon. */
474 if ((fifos
->server
= open(fifos
->server_path
, O_RDWR
)) < 0) {
475 log_error("%s: open server fifo %s",
476 __func__
, fifos
->server_path
);
481 /* Lock out anyone else trying to do communication with the daemon. */
482 if (flock(fifos
->server
, LOCK_EX
) < 0) {
483 log_error("%s: flock %s", __func__
, fifos
->server_path
);
484 close(fifos
->server
);
488 /* if ((fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK)) < 0) {*/
489 if ((fifos
->client
= open(fifos
->client_path
, O_RDWR
| O_NONBLOCK
)) < 0) {
490 log_error("%s: Can't open client fifo %s: %s",
491 __func__
, fifos
->client_path
, strerror(errno
));
492 close(fifos
->server
);
500 static void _dtr_client(struct dm_event_fifos
*fifos
)
502 if (flock(fifos
->server
, LOCK_UN
))
503 log_error("flock unlock %s", fifos
->server_path
);
505 close(fifos
->client
);
506 close(fifos
->server
);
509 /* Get uuid of a device */
510 static struct dm_task
*_get_device_info(const struct dm_event_handler
*dmevh
)
515 if (!(dmt
= dm_task_create(DM_DEVICE_INFO
))) {
516 log_error("_get_device_info: dm_task creation for info failed");
521 dm_task_set_uuid(dmt
, dmevh
->uuid
);
522 else if (dmevh
->dev_name
)
523 dm_task_set_name(dmt
, dmevh
->dev_name
);
524 else if (dmevh
->major
&& dmevh
->minor
) {
525 dm_task_set_major(dmt
, dmevh
->major
);
526 dm_task_set_minor(dmt
, dmevh
->minor
);
529 /* FIXME Add name or uuid or devno to messages */
530 if (!dm_task_run(dmt
)) {
531 log_error("_get_device_info: dm_task_run() failed");
535 if (!dm_task_get_info(dmt
, &info
)) {
536 log_error("_get_device_info: failed to get info for device");
541 log_error("_get_device_info: device not found");
548 dm_task_destroy(dmt
);
552 /* Handle the event (de)registration call and return negative error codes. */
553 static int _do_event(int cmd
, struct dm_event_daemon_message
*msg
,
554 const char *dso_name
, const char *dev_name
,
555 enum dm_event_mask evmask
, uint32_t timeout
)
558 struct dm_event_fifos fifos
;
560 if (!_init_client(&fifos
)) {
565 ret
= _daemon_talk(&fifos
, msg
, DM_EVENT_CMD_HELLO
, 0, 0, 0, 0);
572 ret
= _daemon_talk(&fifos
, msg
, cmd
, dso_name
, dev_name
, evmask
, timeout
);
574 /* what is the opposite of init? */
580 /* External library interface. */
581 int dm_event_register_handler(const struct dm_event_handler
*dmevh
)
586 struct dm_event_daemon_message msg
= { 0, 0, NULL
};
588 if (!(dmt
= _get_device_info(dmevh
))) {
593 uuid
= dm_task_get_uuid(dmt
);
595 if ((err
= _do_event(DM_EVENT_CMD_REGISTER_FOR_EVENT
, &msg
,
596 dmevh
->dso
, uuid
, dmevh
->mask
, dmevh
->timeout
)) < 0) {
597 log_error("%s: event registration failed: %s",
598 dm_task_get_name(dmt
),
599 msg
.data
? msg
.data
: strerror(-err
));
606 dm_task_destroy(dmt
);
611 int dm_event_unregister_handler(const struct dm_event_handler
*dmevh
)
616 struct dm_event_daemon_message msg
= { 0, 0, NULL
};
618 if (!(dmt
= _get_device_info(dmevh
))) {
623 uuid
= dm_task_get_uuid(dmt
);
625 if ((err
= _do_event(DM_EVENT_CMD_UNREGISTER_FOR_EVENT
, &msg
,
626 dmevh
->dso
, uuid
, dmevh
->mask
, dmevh
->timeout
)) < 0) {
627 log_error("%s: event deregistration failed: %s",
628 dm_task_get_name(dmt
),
629 msg
.data
? msg
.data
: strerror(-err
));
636 dm_task_destroy(dmt
);
641 /* Fetch a string off src and duplicate it into *dest. */
642 /* FIXME: move to separate module to share with the daemon. */
643 static char *_fetch_string(char **src
, const int delimiter
)
647 if ((p
= strchr(*src
, delimiter
)))
650 if ((ret
= dm_strdup(*src
)))
651 *src
+= strlen(ret
) + 1;
659 /* Parse a device message from the daemon. */
660 static int _parse_message(struct dm_event_daemon_message
*msg
, char **dso_name
,
661 char **uuid
, enum dm_event_mask
*evmask
)
666 if ((id
= _fetch_string(&p
, ' ')) &&
667 (*dso_name
= _fetch_string(&p
, ' ')) &&
668 (*uuid
= _fetch_string(&p
, ' '))) {
681 * Returns 0 if handler found; error (-ENOMEM, -ENOENT) otherwise.
683 int dm_event_get_registered_device(struct dm_event_handler
*dmevh
, int next
)
686 const char *uuid
= NULL
;
687 char *reply_dso
= NULL
, *reply_uuid
= NULL
;
688 enum dm_event_mask reply_mask
= 0;
689 struct dm_task
*dmt
= NULL
;
690 struct dm_event_daemon_message msg
= { 0, 0, NULL
};
692 if (!(dmt
= _get_device_info(dmevh
))) {
697 uuid
= dm_task_get_uuid(dmt
);
699 if (!(ret
= _do_event(next
? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE
:
700 DM_EVENT_CMD_GET_REGISTERED_DEVICE
,
701 &msg
, dmevh
->dso
, uuid
, dmevh
->mask
, 0))) {
702 /* FIXME this will probably horribly break if we get
703 ill-formatted reply */
704 ret
= _parse_message(&msg
, &reply_dso
, &reply_uuid
, &reply_mask
);
710 dm_task_destroy(dmt
);
718 _dm_event_handler_clear_dev_info(dmevh
);
719 dmevh
->uuid
= dm_strdup(reply_uuid
);
725 if (!(dmt
= _get_device_info(dmevh
))) {
726 ret
= -ENXIO
; /* dmeventd probably gave us bogus uuid back */
730 dm_event_handler_set_dso(dmevh
, reply_dso
);
731 dm_event_handler_set_event_mask(dmevh
, reply_mask
);
743 dmevh
->dev_name
= dm_strdup(dm_task_get_name(dmt
));
744 if (!dmevh
->dev_name
) {
750 if (!dm_task_get_info(dmt
, &info
)) {
755 dmevh
->major
= info
.major
;
756 dmevh
->minor
= info
.minor
;
758 dm_task_destroy(dmt
);
769 _dm_event_handler_clear_dev_info(dmevh
);
771 dm_task_destroy(dmt
);
775 #if 0 /* left out for now */
777 static char *_skip_string(char *src
, const int delimiter
)
779 src
= srtchr(src
, delimiter
);
780 if (src
&& *(src
+ 1))
785 int dm_event_set_timeout(const char *device_path
, uint32_t timeout
)
787 struct dm_event_daemon_message msg
= { 0, 0, NULL
};
789 if (!device_exists(device_path
))
792 return _do_event(DM_EVENT_CMD_SET_TIMEOUT
, &msg
,
793 NULL
, device_path
, 0, timeout
);
796 int dm_event_get_timeout(const char *device_path
, uint32_t *timeout
)
799 struct dm_event_daemon_message msg
= { 0, 0, NULL
};
801 if (!device_exists(device_path
))
803 if (!(ret
= _do_event(DM_EVENT_CMD_GET_TIMEOUT
, &msg
, NULL
, device_path
,
805 char *p
= _skip_string(msg
.data
, ' ');
807 log_error("malformed reply from dmeventd '%s'\n",