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