1 /*	$NetBSD: libdevmapper-event.c,v 1.1.1.2 2009/12/02 00:27:11 haad Exp $	*/
2 
3 /*
4  * Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved.
5  *
6  * This file is part of the device-mapper userspace tools.
7  *
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.
11  *
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
15  */
16 
17 #include "dmlib.h"
18 #include "libdevmapper-event.h"
19 //#include "libmultilog.h"
20 #include "dmeventd.h"
21 
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <stdint.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/file.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <sys/wait.h>
33 #include <arpa/inet.h>		/* for htonl, ntohl */
34 
35 static int _sequence_nr = 0;
36 
37 struct dm_event_handler {
38 	char *dso;
39 
40 	char *dev_name;
41 
42 	char *uuid;
43 	int major;
44 	int minor;
45 	uint32_t timeout;
46 
47 	enum dm_event_mask mask;
48 };
49 
50 static void _dm_event_handler_clear_dev_info(struct dm_event_handler *dmevh)
51 {
52 	if (dmevh->dev_name)
53 		dm_free(dmevh->dev_name);
54 	if (dmevh->uuid)
55 		dm_free(dmevh->uuid);
56 	dmevh->dev_name = dmevh->uuid = NULL;
57 	dmevh->major = dmevh->minor = 0;
58 }
59 
60 struct dm_event_handler *dm_event_handler_create(void)
61 {
62 	struct dm_event_handler *dmevh = NULL;
63 
64 	if (!(dmevh = dm_malloc(sizeof(*dmevh))))
65 		return NULL;
66 
67 	dmevh->dso = dmevh->dev_name = dmevh->uuid = NULL;
68 	dmevh->major = dmevh->minor = 0;
69 	dmevh->mask = 0;
70 	dmevh->timeout = 0;
71 
72 	return dmevh;
73 }
74 
75 void dm_event_handler_destroy(struct dm_event_handler *dmevh)
76 {
77 	_dm_event_handler_clear_dev_info(dmevh);
78 	if (dmevh->dso)
79 		dm_free(dmevh->dso);
80 	dm_free(dmevh);
81 }
82 
83 int dm_event_handler_set_dso(struct dm_event_handler *dmevh, const char *path)
84 {
85 	if (!path) /* noop */
86 		return 0;
87 	if (dmevh->dso)
88 		dm_free(dmevh->dso);
89 
90 	dmevh->dso = dm_strdup(path);
91 	if (!dmevh->dso)
92 		return -ENOMEM;
93 
94 	return 0;
95 }
96 
97 int dm_event_handler_set_dev_name(struct dm_event_handler *dmevh, const char *dev_name)
98 {
99 	if (!dev_name)
100 		return 0;
101 
102 	_dm_event_handler_clear_dev_info(dmevh);
103 
104 	dmevh->dev_name = dm_strdup(dev_name);
105 	if (!dmevh->dev_name)
106 		return -ENOMEM;
107 	return 0;
108 }
109 
110 int dm_event_handler_set_uuid(struct dm_event_handler *dmevh, const char *uuid)
111 {
112 	if (!uuid)
113 		return 0;
114 
115 	_dm_event_handler_clear_dev_info(dmevh);
116 
117 	dmevh->uuid = dm_strdup(uuid);
118 	if (!dmevh->dev_name)
119 		return -ENOMEM;
120 	return 0;
121 }
122 
123 void dm_event_handler_set_major(struct dm_event_handler *dmevh, int major)
124 {
125 	int minor = dmevh->minor;
126 
127 	_dm_event_handler_clear_dev_info(dmevh);
128 
129 	dmevh->major = major;
130 	dmevh->minor = minor;
131 }
132 
133 void dm_event_handler_set_minor(struct dm_event_handler *dmevh, int minor)
134 {
135 	int major = dmevh->major;
136 
137 	_dm_event_handler_clear_dev_info(dmevh);
138 
139 	dmevh->major = major;
140 	dmevh->minor = minor;
141 }
142 
143 void dm_event_handler_set_event_mask(struct dm_event_handler *dmevh,
144 				     enum dm_event_mask evmask)
145 {
146 	dmevh->mask = evmask;
147 }
148 
149 void dm_event_handler_set_timeout(struct dm_event_handler *dmevh, int timeout)
150 {
151 	dmevh->timeout = timeout;
152 }
153 
154 const char *dm_event_handler_get_dso(const struct dm_event_handler *dmevh)
155 {
156 	return dmevh->dso;
157 }
158 
159 const char *dm_event_handler_get_dev_name(const struct dm_event_handler *dmevh)
160 {
161 	return dmevh->dev_name;
162 }
163 
164 const char *dm_event_handler_get_uuid(const struct dm_event_handler *dmevh)
165 {
166 	return dmevh->uuid;
167 }
168 
169 int dm_event_handler_get_major(const struct dm_event_handler *dmevh)
170 {
171 	return dmevh->major;
172 }
173 
174 int dm_event_handler_get_minor(const struct dm_event_handler *dmevh)
175 {
176 	return dmevh->minor;
177 }
178 
179 int dm_event_handler_get_timeout(const struct dm_event_handler *dmevh)
180 {
181 	return dmevh->timeout;
182 }
183 
184 enum dm_event_mask dm_event_handler_get_event_mask(const struct dm_event_handler *dmevh)
185 {
186 	return dmevh->mask;
187 }
188 
189 static int _check_message_id(struct dm_event_daemon_message *msg)
190 {
191 	int pid, seq_nr;
192 
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);
198 		return 0;
199 	}
200 
201 	return 1;
202 }
203 
204 /*
205  * daemon_read
206  * @fifos
207  * @msg
208  *
209  * Read message from daemon.
210  *
211  * Returns: 0 on failure, 1 on success
212  */
213 static int _daemon_read(struct dm_event_fifos *fifos,
214 			struct dm_event_daemon_message *msg)
215 {
216 	unsigned bytes = 0;
217 	int ret, i;
218 	fd_set fds;
219 	struct timeval tval = { 0, 0 };
220 	size_t size = 2 * sizeof(uint32_t);	/* status + size */
221 	char *buf = alloca(size);
222 	int header = 1;
223 
224 	while (bytes < size) {
225 		for (i = 0, ret = 0; (i < 20) && (ret < 1); i++) {
226 			/* Watch daemon read FIFO for input. */
227 			FD_ZERO(&fds);
228 			FD_SET(fifos->server, &fds);
229 			tval.tv_sec = 1;
230 			ret = select(fifos->server + 1, &fds, NULL, NULL,
231 				     &tval);
232 			if (ret < 0 && errno != EINTR) {
233 				log_error("Unable to read from event server");
234 				return 0;
235 			}
236 		}
237 		if (ret < 1) {
238 			log_error("Unable to read from event server.");
239 			return 0;
240 		}
241 
242 		ret = read(fifos->server, buf + bytes, size);
243 		if (ret < 0) {
244 			if ((errno == EINTR) || (errno == EAGAIN))
245 				continue;
246 			else {
247 				log_error("Unable to read from event server.");
248 				return 0;
249 			}
250 		}
251 
252 		bytes += ret;
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);
257 			size = msg->size;
258 			bytes = 0;
259 			header = 0;
260 		}
261 	}
262 
263 	if (bytes != size) {
264 		if (msg->data)
265 			dm_free(msg->data);
266 		msg->data = NULL;
267 	}
268 
269 	return bytes == size;
270 }
271 
272 /* Write message to daemon. */
273 static int _daemon_write(struct dm_event_fifos *fifos,
274 			 struct dm_event_daemon_message *msg)
275 {
276 	unsigned bytes = 0;
277 	int ret = 0;
278 	fd_set fds;
279 
280 	size_t size = 2 * sizeof(uint32_t) + msg->size;
281 	char *buf = alloca(size);
282 	char drainbuf[128];
283 	struct timeval tval = { 0, 0 };
284 
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);
288 
289 	/* drain the answer fifo */
290 	while (1) {
291 		FD_ZERO(&fds);
292 		FD_SET(fifos->server, &fds);
293 		tval.tv_usec = 100;
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");
297 			return 0;
298 		}
299 		if (ret == 0)
300 			break;
301 		read(fifos->server, drainbuf, 127);
302 	}
303 
304 	while (bytes < size) {
305 		do {
306 			/* Watch daemon write FIFO to be ready for output. */
307 			FD_ZERO(&fds);
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");
312 				return 0;
313 			}
314 		} while (ret < 1);
315 
316 		ret = write(fifos->client, ((char *) buf) + bytes,
317 			    size - bytes);
318 		if (ret < 0) {
319 			if ((errno == EINTR) || (errno == EAGAIN))
320 				continue;
321 			else {
322 				log_error("Unable to talk to event daemon");
323 				return 0;
324 			}
325 		}
326 
327 		bytes += ret;
328 	}
329 
330 	return bytes == size;
331 }
332 
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)
337 {
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;
341 	int msg_size;
342 	memset(msg, 0, sizeof(*msg));
343 
344 	/*
345 	 * Set command and pack the arguments
346 	 * into ASCII message string.
347 	 */
348 	msg->cmd = cmd;
349 	if (cmd == DM_EVENT_CMD_HELLO)
350 		fmt = "%d:%d 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");
354 		return -ENOMEM;
355 	}
356 	msg->size = msg_size;
357 
358 	/*
359 	 * Write command and message to and
360 	 * read status return code from daemon.
361 	 */
362 	if (!_daemon_write(fifos, msg)) {
363 		stack;
364 		dm_free(msg->data);
365 		msg->data = 0;
366 		return -EIO;
367 	}
368 
369 	do {
370 
371 		if (msg->data)
372 			dm_free(msg->data);
373 		msg->data = 0;
374 
375 		if (!_daemon_read(fifos, msg)) {
376 			stack;
377 			return -EIO;
378 		}
379 	} while (!_check_message_id(msg));
380 
381 	_sequence_nr++;
382 
383 	return (int32_t) msg->cmd;
384 }
385 
386 /*
387  * start_daemon
388  *
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).
395  *
396  * Returns: 1 on success, 0 otherwise
397  */
398 static int _start_daemon(struct dm_event_fifos *fifos)
399 {
400 	int pid, ret = 0;
401 	int status;
402 	struct stat statbuf;
403 
404 	if (stat(fifos->client_path, &statbuf))
405 		goto start_server;
406 
407 	if (!S_ISFIFO(statbuf.st_mode)) {
408 		log_error("%s is not a fifo.", fifos->client_path);
409 		return 0;
410 	}
411 
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 */
416 
417 		close(fifos->client);
418 		return 1;
419 	} else if (errno != ENXIO) {
420 		/* problem */
421 
422 		log_error("%s: Can't open client fifo %s: %s",
423 			  __func__, fifos->client_path, strerror(errno));
424 		stack;
425 		return 0;
426 	}
427 
428       start_server:
429 	/* server is not running */
430 
431 	if (!strncmp(DMEVENTD_PATH, "/", 1) && stat(DMEVENTD_PATH, &statbuf)) {
432 		log_error("Unable to find dmeventd.");
433 		return_0;
434 	}
435 
436 	pid = fork();
437 
438 	if (pid < 0)
439 		log_error("Unable to fork.");
440 
441 	else if (!pid) {
442 		execvp(DMEVENTD_PATH, NULL);
443 		_exit(EXIT_FAILURE);
444 	} else {
445 		if (waitpid(pid, &status, 0) < 0)
446 			log_error("Unable to start dmeventd: %s",
447 				  strerror(errno));
448 		else if (WEXITSTATUS(status))
449 			log_error("Unable to start dmeventd.");
450 		else
451 			ret = 1;
452 	}
453 
454 	return ret;
455 }
456 
457 /* Initialize client. */
458 static int _init_client(struct dm_event_fifos *fifos)
459 {
460 	/* FIXME? Is fifo the most suitable method? Why not share
461 	   comms/daemon code with something else e.g. multipath? */
462 
463 	/* init fifos */
464 	memset(fifos, 0, sizeof(*fifos));
465 	fifos->client_path = DM_EVENT_FIFO_CLIENT;
466 	fifos->server_path = DM_EVENT_FIFO_SERVER;
467 
468 	if (!_start_daemon(fifos)) {
469 		stack;
470 		return 0;
471 	}
472 
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);
477 		stack;
478 		return 0;
479 	}
480 
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);
485 		return 0;
486 	}
487 
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);
493 		stack;
494 		return 0;
495 	}
496 
497 	return 1;
498 }
499 
500 static void _dtr_client(struct dm_event_fifos *fifos)
501 {
502 	if (flock(fifos->server, LOCK_UN))
503 		log_error("flock unlock %s", fifos->server_path);
504 
505 	close(fifos->client);
506 	close(fifos->server);
507 }
508 
509 /* Get uuid of a device */
510 static struct dm_task *_get_device_info(const struct dm_event_handler *dmevh)
511 {
512 	struct dm_task *dmt;
513 	struct dm_info info;
514 
515 	if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
516 		log_error("_get_device_info: dm_task creation for info failed");
517 		return NULL;
518 	}
519 
520 	if (dmevh->uuid)
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);
527         }
528 
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");
532 		goto failed;
533 	}
534 
535 	if (!dm_task_get_info(dmt, &info)) {
536 		log_error("_get_device_info: failed to get info for device");
537 		goto failed;
538 	}
539 
540 	if (!info.exists) {
541 		log_error("_get_device_info: device not found");
542 		goto failed;
543 	}
544 
545 	return dmt;
546 
547 failed:
548 	dm_task_destroy(dmt);
549 	return NULL;
550 }
551 
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)
556 {
557 	int ret;
558 	struct dm_event_fifos fifos;
559 
560 	if (!_init_client(&fifos)) {
561 		stack;
562 		return -ESRCH;
563 	}
564 
565 	ret = _daemon_talk(&fifos, msg, DM_EVENT_CMD_HELLO, 0, 0, 0, 0);
566 
567 	if (msg->data)
568 		dm_free(msg->data);
569 	msg->data = 0;
570 
571 	if (!ret)
572 		ret = _daemon_talk(&fifos, msg, cmd, dso_name, dev_name, evmask, timeout);
573 
574 	/* what is the opposite of init? */
575 	_dtr_client(&fifos);
576 
577 	return ret;
578 }
579 
580 /* External library interface. */
581 int dm_event_register_handler(const struct dm_event_handler *dmevh)
582 {
583 	int ret = 1, err;
584 	const char *uuid;
585 	struct dm_task *dmt;
586 	struct dm_event_daemon_message msg = { 0, 0, NULL };
587 
588 	if (!(dmt = _get_device_info(dmevh))) {
589 		stack;
590 		return 0;
591 	}
592 
593 	uuid = dm_task_get_uuid(dmt);
594 
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));
600 		ret = 0;
601 	}
602 
603 	if (msg.data)
604 		dm_free(msg.data);
605 
606 	dm_task_destroy(dmt);
607 
608 	return ret;
609 }
610 
611 int dm_event_unregister_handler(const struct dm_event_handler *dmevh)
612 {
613 	int ret = 1, err;
614 	const char *uuid;
615 	struct dm_task *dmt;
616 	struct dm_event_daemon_message msg = { 0, 0, NULL };
617 
618 	if (!(dmt = _get_device_info(dmevh))) {
619 		stack;
620 		return 0;
621 	}
622 
623 	uuid = dm_task_get_uuid(dmt);
624 
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));
630 		ret = 0;
631 	}
632 
633 	if (msg.data)
634 		dm_free(msg.data);
635 
636 	dm_task_destroy(dmt);
637 
638 	return ret;
639 }
640 
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)
644 {
645 	char *p, *ret;
646 
647 	if ((p = strchr(*src, delimiter)))
648 		*p = 0;
649 
650 	if ((ret = dm_strdup(*src)))
651 		*src += strlen(ret) + 1;
652 
653 	if (p)
654 		*p = delimiter;
655 
656 	return ret;
657 }
658 
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)
662 {
663 	char *id = NULL;
664 	char *p = msg->data;
665 
666 	if ((id = _fetch_string(&p, ' ')) &&
667 	    (*dso_name = _fetch_string(&p, ' ')) &&
668 	    (*uuid = _fetch_string(&p, ' '))) {
669 		*evmask = atoi(p);
670 
671 		dm_free(id);
672 		return 0;
673 	}
674 
675 	if (id)
676 		dm_free(id);
677 	return -ENOMEM;
678 }
679 
680 /*
681  * Returns 0 if handler found; error (-ENOMEM, -ENOENT) otherwise.
682  */
683 int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
684 {
685 	int ret = 0;
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 };
691 
692 	if (!(dmt = _get_device_info(dmevh))) {
693 		stack;
694 		return 0;
695 	}
696 
697 	uuid = dm_task_get_uuid(dmt);
698 
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);
705 	} else {
706 		ret = -ENOENT;
707 		goto fail;
708 	}
709 
710 	dm_task_destroy(dmt);
711 	dmt = NULL;
712 
713 	if (msg.data) {
714 		dm_free(msg.data);
715 		msg.data = NULL;
716 	}
717 
718 	_dm_event_handler_clear_dev_info(dmevh);
719 	dmevh->uuid = dm_strdup(reply_uuid);
720 	if (!dmevh->uuid) {
721 		ret = -ENOMEM;
722 		goto fail;
723 	}
724 
725 	if (!(dmt = _get_device_info(dmevh))) {
726 		ret = -ENXIO; /* dmeventd probably gave us bogus uuid back */
727 		goto fail;
728 	}
729 
730 	dm_event_handler_set_dso(dmevh, reply_dso);
731 	dm_event_handler_set_event_mask(dmevh, reply_mask);
732 
733 	if (reply_dso) {
734 		dm_free(reply_dso);
735 		reply_dso = NULL;
736 	}
737 
738 	if (reply_uuid) {
739 		dm_free(reply_uuid);
740 		reply_uuid = NULL;
741 	}
742 
743 	dmevh->dev_name = dm_strdup(dm_task_get_name(dmt));
744 	if (!dmevh->dev_name) {
745 		ret = -ENOMEM;
746 		goto fail;
747 	}
748 
749 	struct dm_info info;
750 	if (!dm_task_get_info(dmt, &info)) {
751 		ret = -1;
752 		goto fail;
753 	}
754 
755 	dmevh->major = info.major;
756 	dmevh->minor = info.minor;
757 
758 	dm_task_destroy(dmt);
759 
760 	return ret;
761 
762  fail:
763 	if (msg.data)
764 		dm_free(msg.data);
765 	if (reply_dso)
766 		dm_free(reply_dso);
767 	if (reply_uuid)
768 		dm_free(reply_uuid);
769 	_dm_event_handler_clear_dev_info(dmevh);
770 	if (dmt)
771 		dm_task_destroy(dmt);
772 	return ret;
773 }
774 
775 #if 0				/* left out for now */
776 
777 static char *_skip_string(char *src, const int delimiter)
778 {
779 	src = srtchr(src, delimiter);
780 	if (src && *(src + 1))
781 		return src + 1;
782 	return NULL;
783 }
784 
785 int dm_event_set_timeout(const char *device_path, uint32_t timeout)
786 {
787 	struct dm_event_daemon_message msg = { 0, 0, NULL };
788 
789 	if (!device_exists(device_path))
790 		return -ENODEV;
791 
792 	return _do_event(DM_EVENT_CMD_SET_TIMEOUT, &msg,
793 			 NULL, device_path, 0, timeout);
794 }
795 
796 int dm_event_get_timeout(const char *device_path, uint32_t *timeout)
797 {
798 	int ret;
799 	struct dm_event_daemon_message msg = { 0, 0, NULL };
800 
801 	if (!device_exists(device_path))
802 		return -ENODEV;
803 	if (!(ret = _do_event(DM_EVENT_CMD_GET_TIMEOUT, &msg, NULL, device_path,
804 			     0, 0))) {
805 		char *p = _skip_string(msg.data, ' ');
806 		if (!p) {
807 			log_error("malformed reply from dmeventd '%s'\n",
808 				  msg.data);
809 			return -EIO;
810 		}
811 		*timeout = atoi(p);
812 	}
813 	if (msg.data)
814 		dm_free(msg.data);
815 	return ret;
816 }
817 #endif
818