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
35static int _sequence_nr = 0;
36
37struct 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
50static 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
60struct 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
75void 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
83int 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
97int 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
110int 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
123void 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
133void 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
143void dm_event_handler_set_event_mask(struct dm_event_handler *dmevh,
144				     enum dm_event_mask evmask)
145{
146	dmevh->mask = evmask;
147}
148
149void dm_event_handler_set_timeout(struct dm_event_handler *dmevh, int timeout)
150{
151	dmevh->timeout = timeout;
152}
153
154const char *dm_event_handler_get_dso(const struct dm_event_handler *dmevh)
155{
156	return dmevh->dso;
157}
158
159const char *dm_event_handler_get_dev_name(const struct dm_event_handler *dmevh)
160{
161	return dmevh->dev_name;
162}
163
164const char *dm_event_handler_get_uuid(const struct dm_event_handler *dmevh)
165{
166	return dmevh->uuid;
167}
168
169int dm_event_handler_get_major(const struct dm_event_handler *dmevh)
170{
171	return dmevh->major;
172}
173
174int dm_event_handler_get_minor(const struct dm_event_handler *dmevh)
175{
176	return dmevh->minor;
177}
178
179int dm_event_handler_get_timeout(const struct dm_event_handler *dmevh)
180{
181	return dmevh->timeout;
182}
183
184enum dm_event_mask dm_event_handler_get_event_mask(const struct dm_event_handler *dmevh)
185{
186	return dmevh->mask;
187}
188
189static 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 */
213static 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. */
273static 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
333static 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 */
398static 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. */
458static 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
500static 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 */
510static 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
547failed:
548	dm_task_destroy(dmt);
549	return NULL;
550}
551
552/* Handle the event (de)registration call and return negative error codes. */
553static 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. */
581int 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
611int 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. */
643static 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. */
660static 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 */
683int 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
777static 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
785int 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
796int 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