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");
248
if ((ret == 0) && i && !bytes) {
249
log_error("No input from event server.");
254
log_error("Unable to read from event server.");
258
ret = read(fifos->server, buf + bytes, size);
260
if ((errno == EINTR) || (errno == EAGAIN))
263
log_error("Unable to read from event server.");
269
if (header && (bytes == 2 * sizeof(uint32_t))) {
270
msg->cmd = ntohl(header[0]);
271
msg->size = ntohl(header[1]);
272
buf = msg->data = dm_malloc(msg->size);
283
return bytes == size;
286
/* Write message to daemon. */
287
static int _daemon_write(struct dm_event_fifos *fifos,
288
struct dm_event_daemon_message *msg)
294
size_t size = 2 * sizeof(uint32_t) + msg->size;
295
uint32_t *header = alloca(size);
296
char *buf = (char *)header;
298
struct timeval tval = { 0, 0 };
300
header[0] = htonl(msg->cmd);
301
header[1] = htonl(msg->size);
302
memcpy(buf + 2 * sizeof(uint32_t), msg->data, msg->size);
304
/* drain the answer fifo */
307
FD_SET(fifos->server, &fds);
309
ret = select(fifos->server + 1, &fds, NULL, NULL, &tval);
310
if ((ret < 0) && (errno != EINTR)) {
311
log_error("Unable to talk to event daemon");
316
ret = read(fifos->server, drainbuf, 127);
319
while (bytes < size) {
321
/* Watch daemon write FIFO to be ready for output. */
323
FD_SET(fifos->client, &fds);
324
ret = select(fifos->client + 1, NULL, &fds, NULL, NULL);
325
if ((ret < 0) && (errno != EINTR)) {
326
log_error("Unable to talk to event daemon");
331
ret = write(fifos->client, buf + bytes, size - bytes);
333
if ((errno == EINTR) || (errno == EAGAIN))
336
log_error("Unable to talk to event daemon");
344
return bytes == size;
347
int daemon_talk(struct dm_event_fifos *fifos,
348
struct dm_event_daemon_message *msg, int cmd,
349
const char *dso_name, const char *dev_name,
350
enum dm_event_mask evmask, uint32_t timeout)
352
const char *dso = dso_name ? dso_name : "-";
353
const char *dev = dev_name ? dev_name : "-";
354
const char *fmt = "%d:%d %s %s %u %" PRIu32;
356
memset(msg, 0, sizeof(*msg));
359
* Set command and pack the arguments
360
* into ASCII message string.
363
if (cmd == DM_EVENT_CMD_HELLO)
365
if ((msg_size = dm_asprintf(&(msg->data), fmt, getpid(), _sequence_nr,
366
dso, dev, evmask, timeout)) < 0) {
367
log_error("_daemon_talk: message allocation failed");
370
msg->size = msg_size;
373
* Write command and message to and
374
* read status return code from daemon.
376
if (!_daemon_write(fifos, msg)) {
388
if (!_daemon_read(fifos, msg)) {
392
} while (!_check_message_id(msg));
396
return (int32_t) msg->cmd;
402
* This function forks off a process (dmeventd) that will handle
403
* the events. I am currently test opening one of the fifos to
404
* ensure that the daemon is running and listening... I thought
405
* this would be less expensive than fork/exec'ing every time.
406
* Perhaps there is an even quicker/better way (no, checking the
407
* lock file is _not_ a better way).
409
* Returns: 1 on success, 0 otherwise
411
static int _start_daemon(char *dmeventd_path, struct dm_event_fifos *fifos)
416
char default_dmeventd_path[] = DMEVENTD_PATH;
417
char *args[] = { dmeventd_path ? : default_dmeventd_path, NULL };
419
if (stat(fifos->client_path, &statbuf))
422
if (!S_ISFIFO(statbuf.st_mode)) {
423
log_error("%s is not a fifo.", fifos->client_path);
427
/* Anyone listening? If not, errno will be ENXIO */
428
fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK);
429
if (fifos->client >= 0) {
430
/* server is running and listening */
431
if (close(fifos->client))
432
log_sys_error("close", fifos->client_path);
434
} else if (errno != ENXIO) {
436
log_sys_error("open", fifos->client_path);
441
/* server is not running */
443
if (!strncmp(DMEVENTD_PATH, "/", 1) && stat(DMEVENTD_PATH, &statbuf)) {
444
log_sys_error("stat", DMEVENTD_PATH);
451
log_sys_error("fork", "");
454
execvp(args[0], args);
455
log_error("Unable to exec dmeventd: %s", strerror(errno));
458
if (waitpid(pid, &status, 0) < 0)
459
log_error("Unable to start dmeventd: %s",
461
else if (WEXITSTATUS(status))
462
log_error("Unable to start dmeventd.");
470
int init_fifos(struct dm_event_fifos *fifos)
472
/* FIXME? Is fifo the most suitable method? Why not share
473
comms/daemon code with something else e.g. multipath? */
475
/* FIXME Make these either configurable or depend directly on dmeventd_path */
476
fifos->client_path = DM_EVENT_FIFO_CLIENT;
477
fifos->server_path = DM_EVENT_FIFO_SERVER;
479
/* Open the fifo used to read from the daemon. */
480
if ((fifos->server = open(fifos->server_path, O_RDWR)) < 0) {
481
log_sys_error("open", fifos->server_path);
485
/* Lock out anyone else trying to do communication with the daemon. */
486
if (flock(fifos->server, LOCK_EX) < 0) {
487
log_sys_error("flock", fifos->server_path);
488
if (close(fifos->server))
489
log_sys_error("close", fifos->server_path);
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_sys_error("open", fifos->client_path);
496
if (close(fifos->server))
497
log_sys_error("close", fifos->server_path);
504
/* Initialize client. */
505
static int _init_client(char *dmeventd_path, struct dm_event_fifos *fifos)
508
memset(fifos, 0, sizeof(*fifos));
510
/* FIXME Make these either configurable or depend directly on dmeventd_path */
511
fifos->client_path = DM_EVENT_FIFO_CLIENT;
512
fifos->server_path = DM_EVENT_FIFO_SERVER;
514
if (!_start_daemon(dmeventd_path, fifos))
517
return init_fifos(fifos);
520
void fini_fifos(struct dm_event_fifos *fifos)
522
if (flock(fifos->server, LOCK_UN))
523
log_error("flock unlock %s", fifos->server_path);
525
if (close(fifos->client))
526
log_sys_error("close", fifos->client_path);
527
if (close(fifos->server))
528
log_sys_error("close", fifos->server_path);
531
/* Get uuid of a device */
532
static struct dm_task *_get_device_info(const struct dm_event_handler *dmevh)
537
if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
538
log_error("_get_device_info: dm_task creation for info failed");
543
if (!dm_task_set_uuid(dmt, dmevh->uuid))
545
} else if (dmevh->dev_name) {
546
if (!dm_task_set_name(dmt, dmevh->dev_name))
548
} else if (dmevh->major && dmevh->minor) {
549
if (!dm_task_set_major(dmt, dmevh->major) ||
550
!dm_task_set_minor(dmt, dmevh->minor))
554
/* FIXME Add name or uuid or devno to messages */
555
if (!dm_task_run(dmt)) {
556
log_error("_get_device_info: dm_task_run() failed");
560
if (!dm_task_get_info(dmt, &info)) {
561
log_error("_get_device_info: failed to get info for device");
566
log_error("_get_device_info: %s%s%s%.0d%s%.0d%s%s: device not found",
568
(!dmevh->uuid && dmevh->dev_name) ? dmevh->dev_name : "",
569
(!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? "(" : "",
570
(!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? dmevh->major : 0,
571
(!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? ":" : "",
572
(!dmevh->uuid && !dmevh->dev_name && dmevh->minor > 0) ? dmevh->minor : 0,
573
(!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) && dmevh->minor == 0 ? "0" : "",
574
(!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? ") " : "");
582
dm_task_destroy(dmt);
586
/* Handle the event (de)registration call and return negative error codes. */
587
static int _do_event(int cmd, char *dmeventd_path, struct dm_event_daemon_message *msg,
588
const char *dso_name, const char *dev_name,
589
enum dm_event_mask evmask, uint32_t timeout)
592
struct dm_event_fifos fifos;
594
if (!_init_client(dmeventd_path, &fifos)) {
599
ret = daemon_talk(&fifos, msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0);
605
ret = daemon_talk(&fifos, msg, cmd, dso_name, dev_name, evmask, timeout);
607
/* what is the opposite of init? */
613
/* External library interface. */
614
int dm_event_register_handler(const struct dm_event_handler *dmevh)
619
struct dm_event_daemon_message msg = { 0, 0, NULL };
621
if (!(dmt = _get_device_info(dmevh))) {
626
uuid = dm_task_get_uuid(dmt);
628
if ((err = _do_event(DM_EVENT_CMD_REGISTER_FOR_EVENT, dmevh->dmeventd_path, &msg,
629
dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
630
log_error("%s: event registration failed: %s",
631
dm_task_get_name(dmt),
632
msg.data ? msg.data : strerror(-err));
638
dm_task_destroy(dmt);
643
int dm_event_unregister_handler(const struct dm_event_handler *dmevh)
648
struct dm_event_daemon_message msg = { 0, 0, NULL };
650
if (!(dmt = _get_device_info(dmevh))) {
655
uuid = dm_task_get_uuid(dmt);
657
if ((err = _do_event(DM_EVENT_CMD_UNREGISTER_FOR_EVENT, dmevh->dmeventd_path, &msg,
658
dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
659
log_error("%s: event deregistration failed: %s",
660
dm_task_get_name(dmt),
661
msg.data ? msg.data : strerror(-err));
667
dm_task_destroy(dmt);
672
/* Fetch a string off src and duplicate it into *dest. */
673
/* FIXME: move to separate module to share with the daemon. */
674
static char *_fetch_string(char **src, const int delimiter)
678
if ((p = strchr(*src, delimiter)))
681
if ((ret = dm_strdup(*src)))
682
*src += strlen(ret) + 1;
690
/* Parse a device message from the daemon. */
691
static int _parse_message(struct dm_event_daemon_message *msg, char **dso_name,
692
char **uuid, enum dm_event_mask *evmask)
697
if ((id = _fetch_string(&p, ' ')) &&
698
(*dso_name = _fetch_string(&p, ' ')) &&
699
(*uuid = _fetch_string(&p, ' '))) {
712
* Returns 0 if handler found; error (-ENOMEM, -ENOENT) otherwise.
714
int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
717
const char *uuid = NULL;
718
char *reply_dso = NULL, *reply_uuid = NULL;
719
enum dm_event_mask reply_mask = 0;
720
struct dm_task *dmt = NULL;
721
struct dm_event_daemon_message msg = { 0, 0, NULL };
724
if (!(dmt = _get_device_info(dmevh))) {
729
uuid = dm_task_get_uuid(dmt);
731
if (_do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
732
DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path,
733
&msg, dmevh->dso, uuid, dmevh->mask, 0)) {
734
log_debug("%s: device not registered.", dm_task_get_name(dmt));
739
/* FIXME this will probably horribly break if we get
740
ill-formatted reply */
741
ret = _parse_message(&msg, &reply_dso, &reply_uuid, &reply_mask);
743
dm_task_destroy(dmt);
749
_dm_event_handler_clear_dev_info(dmevh);
751
ret = -ENXIO; /* dmeventd probably gave us bogus uuid back */
754
dmevh->uuid = dm_strdup(reply_uuid);
760
if (!(dmt = _get_device_info(dmevh))) {
761
ret = -ENXIO; /* dmeventd probably gave us bogus uuid back */
765
dm_event_handler_set_dso(dmevh, reply_dso);
766
dm_event_handler_set_event_mask(dmevh, reply_mask);
774
dmevh->dev_name = dm_strdup(dm_task_get_name(dmt));
775
if (!dmevh->dev_name) {
780
if (!dm_task_get_info(dmt, &info)) {
785
dmevh->major = info.major;
786
dmevh->minor = info.minor;
788
dm_task_destroy(dmt);
796
_dm_event_handler_clear_dev_info(dmevh);
798
dm_task_destroy(dmt);
803
* You can (and have to) call this at the stage of the protocol where
804
* daemon_talk(fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0)
806
* would be normally sent. This call will parse the version reply from
807
* dmeventd, in addition to above call. It is not safe to call this at any
808
* other place in the protocol.
810
* This is an internal function, not exposed in the public API.
813
int dm_event_get_version(struct dm_event_fifos *fifos, int *version) {
815
struct dm_event_daemon_message msg = { 0, 0, NULL };
817
if (daemon_talk(fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0))
822
p = strchr(p, ' '); /* Message ID */
824
p = strchr(p + 1, ' '); /* HELLO */
826
p = strchr(p + 1, ' '); /* HELLO, once more */
832
#if 0 /* left out for now */
834
static char *_skip_string(char *src, const int delimiter)
836
src = srtchr(src, delimiter);
837
if (src && *(src + 1))
842
int dm_event_set_timeout(const char *device_path, uint32_t timeout)
844
struct dm_event_daemon_message msg = { 0, 0, NULL };
846
if (!device_exists(device_path))
849
return _do_event(DM_EVENT_CMD_SET_TIMEOUT, &msg,
850
NULL, device_path, 0, timeout);
853
int dm_event_get_timeout(const char *device_path, uint32_t *timeout)
856
struct dm_event_daemon_message msg = { 0, 0, NULL };
858
if (!device_exists(device_path))
860
if (!(ret = _do_event(DM_EVENT_CMD_GET_TIMEOUT, &msg, NULL, device_path,
862
char *p = _skip_string(msg.data, ' ');
864
log_error("malformed reply from dmeventd '%s'\n",