Home | History | Annotate | Line # | Download | only in devpubd
devpubd.c revision 1.2
      1  1.2  joerg /*	$NetBSD: devpubd.c,v 1.2 2011/09/16 15:42:56 joerg Exp $	*/
      2  1.1    mrg 
      3  1.1    mrg /*-
      4  1.1    mrg  * Copyright (c) 2011 Jared D. McNeill <jmcneill (at) invisible.ca>
      5  1.1    mrg  * All rights reserved.
      6  1.1    mrg  *
      7  1.1    mrg  * Redistribution and use in source and binary forms, with or without
      8  1.1    mrg  * modification, are permitted provided that the following conditions
      9  1.1    mrg  * are met:
     10  1.1    mrg  * 1. Redistributions of source code must retain the above copyright
     11  1.1    mrg  *    notice, this list of conditions and the following disclaimer.
     12  1.1    mrg  * 2. Redistributions in binary form must reproduce the above copyright
     13  1.1    mrg  *    notice, this list of conditions and the following disclaimer in the
     14  1.1    mrg  *    documentation and/or other materials provided with the distribution.
     15  1.1    mrg  * 3. All advertising materials mentioning features or use of this software
     16  1.1    mrg  *    must display the following acknowledgement:
     17  1.1    mrg  *        This product includes software developed by the NetBSD
     18  1.1    mrg  *        Foundation, Inc. and its contributors.
     19  1.1    mrg  * 4. Neither the name of The NetBSD Foundation nor the names of its
     20  1.1    mrg  *    contributors may be used to endorse or promote products derived
     21  1.1    mrg  *    from this software without specific prior written permission.
     22  1.1    mrg  *
     23  1.1    mrg  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     24  1.1    mrg  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     25  1.1    mrg  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     26  1.1    mrg  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     27  1.1    mrg  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     28  1.1    mrg  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     29  1.1    mrg  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     30  1.1    mrg  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     31  1.1    mrg  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     32  1.1    mrg  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     33  1.1    mrg  * POSSIBILITY OF SUCH DAMAGE.
     34  1.1    mrg  */
     35  1.1    mrg 
     36  1.1    mrg #include <sys/cdefs.h>
     37  1.1    mrg __COPYRIGHT("@(#) Copyright (c) 2011\
     38  1.1    mrg Jared D. McNeill <jmcneill (at) invisible.ca>. All rights reserved.");
     39  1.2  joerg __RCSID("$NetBSD: devpubd.c,v 1.2 2011/09/16 15:42:56 joerg Exp $");
     40  1.1    mrg 
     41  1.1    mrg #include <sys/types.h>
     42  1.1    mrg #include <sys/ioctl.h>
     43  1.1    mrg #include <sys/drvctlio.h>
     44  1.1    mrg #include <sys/wait.h>
     45  1.1    mrg #include <sys/poll.h>
     46  1.1    mrg 
     47  1.1    mrg #include <assert.h>
     48  1.1    mrg #include <err.h>
     49  1.1    mrg #include <errno.h>
     50  1.1    mrg #include <fcntl.h>
     51  1.1    mrg #include <stdio.h>
     52  1.1    mrg #include <stdlib.h>
     53  1.1    mrg #include <string.h>
     54  1.1    mrg #include <syslog.h>
     55  1.1    mrg #include <unistd.h>
     56  1.1    mrg 
     57  1.1    mrg static int drvctl_fd = -1;
     58  1.2  joerg static const char devpubd_script[] = DEVPUBD_RUN_HOOKS;
     59  1.1    mrg 
     60  1.1    mrg #define	DEVPUBD_ATTACH_EVENT	"device-attach"
     61  1.1    mrg #define	DEVPUBD_DETACH_EVENT	"device-detach"
     62  1.1    mrg 
     63  1.2  joerg __dead static void
     64  1.1    mrg devpubd_exec(const char *path, const char *event, const char *device)
     65  1.1    mrg {
     66  1.1    mrg 	int error;
     67  1.1    mrg 
     68  1.1    mrg 	error = execl(path, path, event, device, NULL);
     69  1.1    mrg 	if (error) {
     70  1.1    mrg 		syslog(LOG_ERR, "couldn't exec '%s %s %s': %m",
     71  1.1    mrg 		    path, event, device);
     72  1.1    mrg 		exit(EXIT_FAILURE);
     73  1.1    mrg 	}
     74  1.1    mrg 
     75  1.1    mrg 	exit(EXIT_SUCCESS);
     76  1.1    mrg }
     77  1.1    mrg 
     78  1.1    mrg static void
     79  1.1    mrg devpubd_eventhandler(const char *event, const char *device)
     80  1.1    mrg {
     81  1.1    mrg 	pid_t pid;
     82  1.1    mrg 	int status;
     83  1.1    mrg 
     84  1.1    mrg 	syslog(LOG_DEBUG, "event = '%s', device = '%s'", event, device);
     85  1.1    mrg 
     86  1.1    mrg 	pid = fork();
     87  1.1    mrg 	switch (pid) {
     88  1.1    mrg 	case -1:
     89  1.1    mrg 		syslog(LOG_ERR, "fork failed: %m");
     90  1.1    mrg 		break;
     91  1.1    mrg 	case 0:
     92  1.1    mrg 		devpubd_exec(devpubd_script, event, device);
     93  1.1    mrg 		/* NOTREACHED */
     94  1.1    mrg 	default:
     95  1.1    mrg 		if (waitpid(pid, &status, 0) == -1) {
     96  1.1    mrg 			syslog(LOG_ERR, "waitpid(%d) failed: %m", pid);
     97  1.1    mrg 			break;
     98  1.1    mrg 		}
     99  1.1    mrg 		if (WIFEXITED(status) && WEXITSTATUS(status)) {
    100  1.1    mrg 			syslog(LOG_WARNING, "%s exited with status %d",
    101  1.1    mrg 			    devpubd_script, WEXITSTATUS(status));
    102  1.1    mrg 		} else if (WIFSIGNALED(status)) {
    103  1.1    mrg 			syslog(LOG_WARNING, "%s received signal %d",
    104  1.1    mrg 			    devpubd_script, WTERMSIG(status));
    105  1.1    mrg 		}
    106  1.1    mrg 		break;
    107  1.1    mrg 	}
    108  1.1    mrg }
    109  1.1    mrg 
    110  1.2  joerg __dead static void
    111  1.1    mrg devpubd_eventloop(void)
    112  1.1    mrg {
    113  1.1    mrg 	const char *event, *device;
    114  1.1    mrg 	prop_dictionary_t ev;
    115  1.1    mrg 	int res;
    116  1.1    mrg 
    117  1.1    mrg 	assert(drvctl_fd != -1);
    118  1.1    mrg 
    119  1.1    mrg 	for (;;) {
    120  1.1    mrg 		res = prop_dictionary_recv_ioctl(drvctl_fd, DRVGETEVENT, &ev);
    121  1.1    mrg 		if (res)
    122  1.1    mrg 			err(EXIT_FAILURE, "DRVGETEVENT failed");
    123  1.1    mrg 		prop_dictionary_get_cstring_nocopy(ev, "event", &event);
    124  1.1    mrg 		prop_dictionary_get_cstring_nocopy(ev, "device", &device);
    125  1.1    mrg 
    126  1.1    mrg 		printf("%s: event='%s', device='%s'\n", __func__,
    127  1.1    mrg 		    event, device);
    128  1.1    mrg 
    129  1.1    mrg 		devpubd_eventhandler(event, device);
    130  1.1    mrg 
    131  1.1    mrg 		prop_object_release(ev);
    132  1.1    mrg 	}
    133  1.1    mrg }
    134  1.1    mrg 
    135  1.1    mrg static void
    136  1.1    mrg devpubd_probe(const char *device)
    137  1.1    mrg {
    138  1.1    mrg 	struct devlistargs laa;
    139  1.1    mrg 	size_t len, children, n;
    140  1.1    mrg 	void *p;
    141  1.1    mrg 	int error;
    142  1.1    mrg 
    143  1.1    mrg 	assert(drvctl_fd != -1);
    144  1.1    mrg 
    145  1.1    mrg 	memset(&laa, 0, sizeof(laa));
    146  1.1    mrg 	if (device)
    147  1.1    mrg 		strlcpy(laa.l_devname, device, sizeof(laa.l_devname));
    148  1.1    mrg 
    149  1.1    mrg 	/* Get the child device count for this device */
    150  1.1    mrg 	error = ioctl(drvctl_fd, DRVLISTDEV, &laa);
    151  1.1    mrg 	if (error) {
    152  1.1    mrg 		syslog(LOG_ERR, "DRVLISTDEV failed: %m");
    153  1.1    mrg 		return;
    154  1.1    mrg 	}
    155  1.1    mrg 
    156  1.1    mrg child_count_changed:
    157  1.1    mrg 	/* If this device has no children, return */
    158  1.1    mrg 	if (laa.l_children == 0)
    159  1.1    mrg 		return;
    160  1.1    mrg 
    161  1.1    mrg 	/* Allocate a buffer large enough to hold the child device names */
    162  1.1    mrg 	p = laa.l_childname;
    163  1.1    mrg 	children = laa.l_children;
    164  1.1    mrg 
    165  1.1    mrg 	len = children * sizeof(laa.l_childname[0]);
    166  1.1    mrg 	laa.l_childname = realloc(laa.l_childname, len);
    167  1.1    mrg 	if (laa.l_childname == NULL) {
    168  1.1    mrg 		syslog(LOG_ERR, "couldn't allocate %zu bytes", len);
    169  1.1    mrg 		laa.l_childname = p;
    170  1.1    mrg 		goto done;
    171  1.1    mrg 	}
    172  1.1    mrg 
    173  1.1    mrg 	/* Get a list of child devices */
    174  1.1    mrg 	error = ioctl(drvctl_fd, DRVLISTDEV, &laa);
    175  1.1    mrg 	if (error) {
    176  1.1    mrg 		syslog(LOG_ERR, "DRVLISTDEV failed: %m");
    177  1.1    mrg 		goto done;
    178  1.1    mrg 	}
    179  1.1    mrg 
    180  1.1    mrg 	/* If the child count changed between DRVLISTDEV calls, retry */
    181  1.1    mrg 	if (children != laa.l_children)
    182  1.1    mrg 		goto child_count_changed;
    183  1.1    mrg 
    184  1.1    mrg 	/*
    185  1.1    mrg 	 * For each child device, first post an attach event and
    186  1.1    mrg 	 * then scan each one for additional devices.
    187  1.1    mrg 	 */
    188  1.1    mrg 	for (n = 0; n < laa.l_children; n++)
    189  1.1    mrg 		devpubd_eventhandler(DEVPUBD_ATTACH_EVENT, laa.l_childname[n]);
    190  1.1    mrg 	for (n = 0; n < laa.l_children; n++)
    191  1.1    mrg 		devpubd_probe(laa.l_childname[n]);
    192  1.1    mrg 
    193  1.1    mrg done:
    194  1.1    mrg 	free(laa.l_childname);
    195  1.1    mrg 	return;
    196  1.1    mrg }
    197  1.1    mrg 
    198  1.2  joerg __dead static void
    199  1.1    mrg usage(void)
    200  1.1    mrg {
    201  1.1    mrg 	fprintf(stderr, "usage: %s [-f]\n", getprogname());
    202  1.1    mrg 	exit(EXIT_FAILURE);
    203  1.1    mrg }
    204  1.1    mrg 
    205  1.1    mrg int
    206  1.1    mrg main(int argc, char *argv[])
    207  1.1    mrg {
    208  1.1    mrg 	bool fflag = false;
    209  1.1    mrg 	int ch;
    210  1.1    mrg 
    211  1.1    mrg 	setprogname(argv[0]);
    212  1.1    mrg 
    213  1.1    mrg 	while ((ch = getopt(argc, argv, "fh")) != -1) {
    214  1.1    mrg 		switch (ch) {
    215  1.1    mrg 		case 'f':
    216  1.1    mrg 			fflag = true;
    217  1.1    mrg 			break;
    218  1.1    mrg 		case 'h':
    219  1.1    mrg 		default:
    220  1.1    mrg 			usage();
    221  1.1    mrg 			/* NOTREACHED */
    222  1.1    mrg 		}
    223  1.1    mrg 	}
    224  1.1    mrg 	argc -= optind;
    225  1.1    mrg 	argv += optind;
    226  1.1    mrg 
    227  1.1    mrg 	if (argc)
    228  1.1    mrg 		usage();
    229  1.1    mrg 		/* NOTREACHED */
    230  1.1    mrg 
    231  1.1    mrg 	drvctl_fd = open(DRVCTLDEV, O_RDWR);
    232  1.1    mrg 	if (drvctl_fd == -1)
    233  1.1    mrg 		err(EXIT_FAILURE, "couldn't open " DRVCTLDEV);
    234  1.1    mrg 
    235  1.1    mrg 	if (!fflag) {
    236  1.1    mrg 		if (daemon(0, 0) == -1) {
    237  1.1    mrg 			err(EXIT_FAILURE, "couldn't fork");
    238  1.1    mrg 		}
    239  1.1    mrg 	}
    240  1.1    mrg 
    241  1.1    mrg 	/* Look for devices that are already present */
    242  1.1    mrg 	devpubd_probe(NULL);
    243  1.1    mrg 
    244  1.1    mrg 	devpubd_eventloop();
    245  1.1    mrg 
    246  1.1    mrg 	return EXIT_SUCCESS;
    247  1.1    mrg }
    248