xref: /netbsd-src/external/gpl2/lvm2/dist/daemons/dmeventd/libdevmapper-event.c (revision 500db002748d9818288e46e10f026a2b09548086)
1 /*	$NetBSD: libdevmapper-event.c,v 1.1.1.1 2008/12/22 00:18:56 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 	pid = fork();
431 
432 	if (pid < 0)
433 		log_error("Unable to fork.");
434 
435 	else if (!pid) {
436 		execvp(DMEVENTD_PATH, NULL);
437 		exit(EXIT_FAILURE);
438 	} else {
439 		if (waitpid(pid, &status, 0) < 0)
440 			log_error("Unable to start dmeventd: %s",
441 				  strerror(errno));
442 		else if (WEXITSTATUS(status))
443 			log_error("Unable to start dmeventd.");
444 		else
445 			ret = 1;
446 	}
447 
448 	return ret;
449 }
450 
451 /* Initialize client. */
452 static int _init_client(struct dm_event_fifos *fifos)
453 {
454 	/* FIXME? Is fifo the most suitable method? Why not share
455 	   comms/daemon code with something else e.g. multipath? */
456 
457 	/* init fifos */
458 	memset(fifos, 0, sizeof(*fifos));
459 	fifos->client_path = DM_EVENT_FIFO_CLIENT;
460 	fifos->server_path = DM_EVENT_FIFO_SERVER;
461 
462 	if (!_start_daemon(fifos)) {
463 		stack;
464 		return 0;
465 	}
466 
467 	/* Open the fifo used to read from the daemon. */
468 	if ((fifos->server = open(fifos->server_path, O_RDWR)) < 0) {
469 		log_error("%s: open server fifo %s",
470 			  __func__, fifos->server_path);
471 		stack;
472 		return 0;
473 	}
474 
475 	/* Lock out anyone else trying to do communication with the daemon. */
476 	if (flock(fifos->server, LOCK_EX) < 0) {
477 		log_error("%s: flock %s", __func__, fifos->server_path);
478 		close(fifos->server);
479 		return 0;
480 	}
481 
482 /*	if ((fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK)) < 0) {*/
483 	if ((fifos->client = open(fifos->client_path, O_RDWR | O_NONBLOCK)) < 0) {
484 		log_error("%s: Can't open client fifo %s: %s",
485 			  __func__, fifos->client_path, strerror(errno));
486 		close(fifos->server);
487 		stack;
488 		return 0;
489 	}
490 
491 	return 1;
492 }
493 
494 static void _dtr_client(struct dm_event_fifos *fifos)
495 {
496 	if (flock(fifos->server, LOCK_UN))
497 		log_error("flock unlock %s", fifos->server_path);
498 
499 	close(fifos->client);
500 	close(fifos->server);
501 }
502 
503 /* Get uuid of a device */
504 static struct dm_task *_get_device_info(const struct dm_event_handler *dmevh)
505 {
506 	struct dm_task *dmt;
507 	struct dm_info info;
508 
509 	if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
510 		log_error("_get_device_info: dm_task creation for info failed");
511 		return NULL;
512 	}
513 
514 	if (dmevh->uuid)
515 		dm_task_set_uuid(dmt, dmevh->uuid);
516 	else if (dmevh->dev_name)
517 		dm_task_set_name(dmt, dmevh->dev_name);
518 	else if (dmevh->major && dmevh->minor) {
519 		dm_task_set_major(dmt, dmevh->major);
520 		dm_task_set_minor(dmt, dmevh->minor);
521         }
522 
523 	/* FIXME Add name or uuid or devno to messages */
524 	if (!dm_task_run(dmt)) {
525 		log_error("_get_device_info: dm_task_run() failed");
526 		goto failed;
527 	}
528 
529 	if (!dm_task_get_info(dmt, &info)) {
530 		log_error("_get_device_info: failed to get info for device");
531 		goto failed;
532 	}
533 
534 	if (!info.exists) {
535 		log_error("_get_device_info: device not found");
536 		goto failed;
537 	}
538 
539 	return dmt;
540 
541 failed:
542 	dm_task_destroy(dmt);
543 	return NULL;
544 }
545 
546 /* Handle the event (de)registration call and return negative error codes. */
547 static int _do_event(int cmd, struct dm_event_daemon_message *msg,
548 		     const char *dso_name, const char *dev_name,
549 		     enum dm_event_mask evmask, uint32_t timeout)
550 {
551 	int ret;
552 	struct dm_event_fifos fifos;
553 
554 	if (!_init_client(&fifos)) {
555 		stack;
556 		return -ESRCH;
557 	}
558 
559 	ret = _daemon_talk(&fifos, msg, DM_EVENT_CMD_HELLO, 0, 0, 0, 0);
560 
561 	if (msg->data)
562 		dm_free(msg->data);
563 	msg->data = 0;
564 
565 	if (!ret)
566 		ret = _daemon_talk(&fifos, msg, cmd, dso_name, dev_name, evmask, timeout);
567 
568 	/* what is the opposite of init? */
569 	_dtr_client(&fifos);
570 
571 	return ret;
572 }
573 
574 /* External library interface. */
575 int dm_event_register_handler(const struct dm_event_handler *dmevh)
576 {
577 	int ret = 1, err;
578 	const char *uuid;
579 	struct dm_task *dmt;
580 	struct dm_event_daemon_message msg = { 0, 0, NULL };
581 
582 	if (!(dmt = _get_device_info(dmevh))) {
583 		stack;
584 		return 0;
585 	}
586 
587 	uuid = dm_task_get_uuid(dmt);
588 
589 	if ((err = _do_event(DM_EVENT_CMD_REGISTER_FOR_EVENT, &msg,
590 			     dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
591 		log_error("%s: event registration failed: %s",
592 			  dm_task_get_name(dmt),
593 			  msg.data ? msg.data : strerror(-err));
594 		ret = 0;
595 	}
596 
597 	if (msg.data)
598 		dm_free(msg.data);
599 
600 	dm_task_destroy(dmt);
601 
602 	return ret;
603 }
604 
605 int dm_event_unregister_handler(const struct dm_event_handler *dmevh)
606 {
607 	int ret = 1, err;
608 	const char *uuid;
609 	struct dm_task *dmt;
610 	struct dm_event_daemon_message msg = { 0, 0, NULL };
611 
612 	if (!(dmt = _get_device_info(dmevh))) {
613 		stack;
614 		return 0;
615 	}
616 
617 	uuid = dm_task_get_uuid(dmt);
618 
619 	if ((err = _do_event(DM_EVENT_CMD_UNREGISTER_FOR_EVENT, &msg,
620 			    dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
621 		log_error("%s: event deregistration failed: %s",
622 			  dm_task_get_name(dmt),
623 			  msg.data ? msg.data : strerror(-err));
624 		ret = 0;
625 	}
626 
627 	if (msg.data)
628 		dm_free(msg.data);
629 
630 	dm_task_destroy(dmt);
631 
632 	return ret;
633 }
634 
635 /* Fetch a string off src and duplicate it into *dest. */
636 /* FIXME: move to separate module to share with the daemon. */
637 static char *_fetch_string(char **src, const int delimiter)
638 {
639 	char *p, *ret;
640 
641 	if ((p = strchr(*src, delimiter)))
642 		*p = 0;
643 
644 	if ((ret = dm_strdup(*src)))
645 		*src += strlen(ret) + 1;
646 
647 	if (p)
648 		*p = delimiter;
649 
650 	return ret;
651 }
652 
653 /* Parse a device message from the daemon. */
654 static int _parse_message(struct dm_event_daemon_message *msg, char **dso_name,
655 			 char **uuid, enum dm_event_mask *evmask)
656 {
657 	char *id = NULL;
658 	char *p = msg->data;
659 
660 	if ((id = _fetch_string(&p, ' ')) &&
661 	    (*dso_name = _fetch_string(&p, ' ')) &&
662 	    (*uuid = _fetch_string(&p, ' '))) {
663 		*evmask = atoi(p);
664 
665 		dm_free(id);
666 		return 0;
667 	}
668 
669 	if (id)
670 		dm_free(id);
671 	return -ENOMEM;
672 }
673 
674 /*
675  * Returns 0 if handler found; error (-ENOMEM, -ENOENT) otherwise.
676  */
677 int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
678 {
679 	int ret = 0;
680 	const char *uuid = NULL;
681 	char *reply_dso = NULL, *reply_uuid = NULL;
682 	enum dm_event_mask reply_mask = 0;
683 	struct dm_task *dmt = NULL;
684 	struct dm_event_daemon_message msg = { 0, 0, NULL };
685 
686 	if (!(dmt = _get_device_info(dmevh))) {
687 		stack;
688 		return 0;
689 	}
690 
691 	uuid = dm_task_get_uuid(dmt);
692 
693 	if (!(ret = _do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
694 			     DM_EVENT_CMD_GET_REGISTERED_DEVICE,
695 			      &msg, dmevh->dso, uuid, dmevh->mask, 0))) {
696 		/* FIXME this will probably horribly break if we get
697 		   ill-formatted reply */
698 		ret = _parse_message(&msg, &reply_dso, &reply_uuid, &reply_mask);
699 	} else {
700 		ret = -ENOENT;
701 		goto fail;
702 	}
703 
704 	dm_task_destroy(dmt);
705 	dmt = NULL;
706 
707 	if (msg.data) {
708 		dm_free(msg.data);
709 		msg.data = NULL;
710 	}
711 
712 	_dm_event_handler_clear_dev_info(dmevh);
713 	dmevh->uuid = dm_strdup(reply_uuid);
714 	if (!dmevh->uuid) {
715 		ret = -ENOMEM;
716 		goto fail;
717 	}
718 
719 	if (!(dmt = _get_device_info(dmevh))) {
720 		ret = -ENXIO; /* dmeventd probably gave us bogus uuid back */
721 		goto fail;
722 	}
723 
724 	dm_event_handler_set_dso(dmevh, reply_dso);
725 	dm_event_handler_set_event_mask(dmevh, reply_mask);
726 
727 	if (reply_dso) {
728 		dm_free(reply_dso);
729 		reply_dso = NULL;
730 	}
731 
732 	if (reply_uuid) {
733 		dm_free(reply_uuid);
734 		reply_uuid = NULL;
735 	}
736 
737 	dmevh->dev_name = dm_strdup(dm_task_get_name(dmt));
738 	if (!dmevh->dev_name) {
739 		ret = -ENOMEM;
740 		goto fail;
741 	}
742 
743 	struct dm_info info;
744 	if (!dm_task_get_info(dmt, &info)) {
745 		ret = -1;
746 		goto fail;
747 	}
748 
749 	dmevh->major = info.major;
750 	dmevh->minor = info.minor;
751 
752 	dm_task_destroy(dmt);
753 
754 	return ret;
755 
756  fail:
757 	if (msg.data)
758 		dm_free(msg.data);
759 	if (reply_dso)
760 		dm_free(reply_dso);
761 	if (reply_uuid)
762 		dm_free(reply_uuid);
763 	_dm_event_handler_clear_dev_info(dmevh);
764 	if (dmt)
765 		dm_task_destroy(dmt);
766 	return ret;
767 }
768 
769 #if 0				/* left out for now */
770 
771 static char *_skip_string(char *src, const int delimiter)
772 {
773 	src = srtchr(src, delimiter);
774 	if (src && *(src + 1))
775 		return src + 1;
776 	return NULL;
777 }
778 
779 int dm_event_set_timeout(const char *device_path, uint32_t timeout)
780 {
781 	struct dm_event_daemon_message msg = { 0, 0, NULL };
782 
783 	if (!device_exists(device_path))
784 		return -ENODEV;
785 
786 	return _do_event(DM_EVENT_CMD_SET_TIMEOUT, &msg,
787 			 NULL, device_path, 0, timeout);
788 }
789 
790 int dm_event_get_timeout(const char *device_path, uint32_t *timeout)
791 {
792 	int ret;
793 	struct dm_event_daemon_message msg = { 0, 0, NULL };
794 
795 	if (!device_exists(device_path))
796 		return -ENODEV;
797 	if (!(ret = _do_event(DM_EVENT_CMD_GET_TIMEOUT, &msg, NULL, device_path,
798 			     0, 0))) {
799 		char *p = _skip_string(msg.data, ' ');
800 		if (!p) {
801 			log_error("malformed reply from dmeventd '%s'\n",
802 				  msg.data);
803 			return -EIO;
804 		}
805 		*timeout = atoi(p);
806 	}
807 	if (msg.data)
808 		dm_free(msg.data);
809 	return ret;
810 }
811 #endif
812