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