xref: /dragonfly/usr.sbin/hotplugd/hotplugd.c (revision 9348a738)
1 /*	$OpenBSD: hotplugd.c,v 1.11 2009/06/26 01:06:04 kurt Exp $	*/
2 /*
3  * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /*
19  * Devices hot plugging daemon.
20  */
21 
22 #include <sys/types.h>
23 #include <sys/device.h>
24 #include <sys/wait.h>
25 
26 #include <err.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <libgen.h>
30 #include <signal.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <syslog.h>
36 #include <unistd.h>
37 #include <devattr.h>
38 
39 #define _PATH_DEV_HOTPLUG		"/dev/hotplug"
40 #define _PATH_ETC_HOTPLUG		"/etc/hotplug"
41 #define _PATH_ETC_HOTPLUG_ATTACH	_PATH_ETC_HOTPLUG "/attach"
42 #define _PATH_ETC_HOTPLUG_DETACH	_PATH_ETC_HOTPLUG "/detach"
43 #define _LOG_TAG			"hotplugd"
44 #define _LOG_FACILITY			LOG_DAEMON
45 #define _LOG_OPT			(LOG_NDELAY | LOG_PID)
46 
47 enum obsd_devclass {
48 	DV_DULL,		/* generic, no special info */
49 	DV_CPU,			/* CPU (carries resource utilization) */
50 	DV_DISK,		/* disk drive (label, etc) */
51 	DV_IFNET,		/* network interface */
52 	DV_TAPE,		/* tape device */
53 	DV_TTY			/* serial line interface (???) */
54 };
55 
56 extern char *__progname;
57 
58 volatile sig_atomic_t quit = 0;
59 
60 void exec_script(const char *, int, const char *);
61 void sigchild(int);
62 void sigquit(int);
63 __dead2 void usage(void);
64 
65 int
66 main(int argc, char *argv[])
67 {
68 	int ch, class, ret;
69 	struct sigaction sact;
70 	struct udev *udev;
71 	struct udev_enumerate *udev_enum;
72 	struct udev_list_entry *udev_le, *udev_le_first;
73 	struct udev_monitor *udev_monitor;
74 	struct udev_device *udev_dev;
75 	enum obsd_devclass devclass;
76 	const char *prop;
77 
78 	while ((ch = getopt(argc, argv, "?")) != -1)
79 		switch (ch) {
80 		case '?':
81 		default:
82 			usage();
83 			/* NOTREACHED */
84 		}
85 
86 	argc -= optind;
87 	argv += optind;
88 	if (argc > 0)
89 		usage();
90 
91 	udev = udev_new();
92 	if (udev == NULL)
93 		err(1, "udev_new");
94 
95 	bzero(&sact, sizeof(sact));
96 	sigemptyset(&sact.sa_mask);
97 	sact.sa_flags = 0;
98 	sact.sa_handler = sigquit;
99 	sigaction(SIGINT, &sact, NULL);
100 	sigaction(SIGQUIT, &sact, NULL);
101 	sigaction(SIGTERM, &sact, NULL);
102 	sact.sa_handler = SIG_IGN;
103 	sigaction(SIGHUP, &sact, NULL);
104 	sact.sa_handler = sigchild;
105 	sact.sa_flags = SA_NOCLDSTOP;
106 	sigaction(SIGCHLD, &sact, NULL);
107 
108 	openlog(_LOG_TAG, _LOG_OPT, _LOG_FACILITY);
109 
110 	if (daemon(0, 0) == -1)
111 		err(1, "daemon");
112 
113 	syslog(LOG_INFO, "started");
114 
115 	udev_enum = udev_enumerate_new(udev);
116 	if (udev_enum == NULL)
117 		err(1, "udev_enumerate_new");
118 
119 	ret = udev_enumerate_scan_devices(udev_enum);
120 	if (ret != 0)
121 		err(1, "udev_enumerate_scan_device ret = %d", ret);
122 
123 	udev_le_first = udev_enumerate_get_list_entry(udev_enum);
124 	if (udev_le_first == NULL)
125 		err(1, "udev_enumerate_get_list_entry error");
126 
127 	udev_list_entry_foreach(udev_le, udev_le_first) {
128 		udev_dev = udev_list_entry_get_device(udev_le);
129 
130 		class = atoi(udev_device_get_property_value(udev_dev, "devtype"));
131 		devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL)));
132 
133 		syslog(LOG_INFO, "%s attached, class %d",
134 		    udev_device_get_devnode(udev_dev), devclass);
135 		exec_script(_PATH_ETC_HOTPLUG_ATTACH, devclass,
136 		   udev_device_get_devnode(udev_dev));
137 	}
138 
139 	udev_enumerate_unref(udev_enum);
140 	udev_monitor = udev_monitor_new(udev);
141 
142 	ret = udev_monitor_enable_receiving(udev_monitor);
143 	if (ret != 0)
144 		err(1, "udev_monitor_enable_receiving ret = %d", ret);
145 
146 	while (!quit) {
147 		if ((udev_dev = udev_monitor_receive_device(udev_monitor)) == NULL) {
148 			syslog(LOG_ERR, "read: %m");
149 			exit(1);
150 		}
151 
152 		prop = udev_device_get_action(udev_dev);
153 		class = atoi(udev_device_get_property_value(udev_dev, "devtype"));
154 		devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL)));
155 
156 		if (strcmp(prop, "attach") == 0) {
157 			syslog(LOG_INFO, "%s attached, class %d",
158 			    udev_device_get_devnode(udev_dev), devclass);
159 			exec_script(_PATH_ETC_HOTPLUG_ATTACH, devclass,
160 			    udev_device_get_devnode(udev_dev));
161 		} else if (strcmp(prop, "detach") == 0) {
162 			syslog(LOG_INFO, "%s detached, class %d",
163 			    udev_device_get_devnode(udev_dev), devclass);
164 			exec_script(_PATH_ETC_HOTPLUG_DETACH, devclass,
165 			    udev_device_get_devnode(udev_dev));
166 		} else {
167 			syslog(LOG_NOTICE, "unknown event (%s)", prop);
168 		}
169 	}
170 
171 	syslog(LOG_INFO, "terminated");
172 
173 	closelog();
174 
175 	udev_monitor_unref(udev_monitor);
176 	udev_unref(udev);
177 
178 	return (0);
179 }
180 
181 void
182 exec_script(const char *file, int class, const char *name)
183 {
184 	char strclass[8];
185 	pid_t pid;
186 
187 	snprintf(strclass, sizeof(strclass), "%d", class);
188 
189 	if (access(file, X_OK | R_OK)) {
190 		syslog(LOG_ERR, "could not access %s", file);
191 		return;
192 	}
193 
194 	if ((pid = fork()) == -1) {
195 		syslog(LOG_ERR, "fork: %m");
196 		return;
197 	}
198 	if (pid == 0) {
199 		/* child process */
200 		execl(file, basename(file), strclass, name, NULL);
201 		syslog(LOG_ERR, "execl %s: %m", file);
202 		_exit(1);
203 		/* NOTREACHED */
204 	}
205 }
206 
207 /* ARGSUSED */
208 void
209 sigchild(int signum __unused)
210 {
211 	int saved_errno, status;
212 	pid_t pid;
213 
214 	saved_errno = errno;
215 
216 	while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) {
217 		if (pid == -1) {
218 			if (errno == EINTR)
219 				continue;
220 			if (errno != ECHILD)
221 				/* XXX syslog_r() */
222 				syslog(LOG_ERR, "waitpid: %m");
223 			break;
224 		}
225 
226 		if (WIFEXITED(status)) {
227 			if (WEXITSTATUS(status) != 0) {
228 				/* XXX syslog_r() */
229 				syslog(LOG_NOTICE, "child exit status: %d",
230 				    WEXITSTATUS(status));
231 			}
232 		} else {
233 			/* XXX syslog_r() */
234 			syslog(LOG_NOTICE, "child is terminated abnormally");
235 		}
236 	}
237 
238 	errno = saved_errno;
239 }
240 
241 /* ARGSUSED */
242 void
243 sigquit(int signum __unused)
244 {
245 	quit = 1;
246 }
247 
248 __dead2 void
249 usage(void)
250 {
251 	fprintf(stderr, "usage: %s [-d device]\n", __progname);
252 	exit(1);
253 }
254