xref: /openbsd-src/usr.sbin/hotplugd/hotplugd.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: hotplugd.c,v 1.9 2008/05/25 16:49:04 jasper 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/hotplug.h>
25 #include <sys/wait.h>
26 
27 #include <err.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <libgen.h>
31 #include <signal.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <syslog.h>
37 #include <unistd.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 volatile sig_atomic_t quit = 0;
48 char *device = _PATH_DEV_HOTPLUG;
49 int devfd = -1;
50 
51 void exec_script(const char *, int, char *);
52 
53 void sigchild(int);
54 void sigquit(int);
55 __dead void usage(void);
56 
57 int
58 main(int argc, char *argv[])
59 {
60 	int ch;
61 	struct sigaction sact;
62 	struct hotplug_event he;
63 
64 	while ((ch = getopt(argc, argv, "d:")) != -1)
65 		switch (ch) {
66 		case 'd':
67 			device = optarg;
68 			break;
69 		case '?':
70 		default:
71 			usage();
72 			/* NOTREACHED */
73 		}
74 
75 	argc -= optind;
76 	argv += optind;
77 	if (argc > 0)
78 		usage();
79 
80 	if ((devfd = open(device, O_RDONLY)) == -1)
81 		err(1, "%s", device);
82 
83 	bzero(&sact, sizeof(sact));
84 	sigemptyset(&sact.sa_mask);
85 	sact.sa_flags = 0;
86 	sact.sa_handler = sigquit;
87 	sigaction(SIGINT, &sact, NULL);
88 	sigaction(SIGQUIT, &sact, NULL);
89 	sigaction(SIGTERM, &sact, NULL);
90 	sact.sa_handler = SIG_IGN;
91 	sigaction(SIGHUP, &sact, NULL);
92 	sact.sa_handler = sigchild;
93 	sigaction(SIGCHLD, &sact, NULL);
94 
95 	openlog(_LOG_TAG, _LOG_OPT, _LOG_FACILITY);
96 	if (daemon(0, 0) == -1)
97 		err(1, "daemon");
98 
99 	syslog(LOG_INFO, "started");
100 
101 	while (!quit) {
102 		if (read(devfd, &he, sizeof(he)) == -1) {
103 			if (errno == EINTR)
104 				/* ignore */
105 				continue;
106 			syslog(LOG_ERR, "read: %m");
107 			exit(1);
108 		}
109 
110 		switch (he.he_type) {
111 		case HOTPLUG_DEVAT:
112 			syslog(LOG_INFO, "%s attached, class %d",
113 			    he.he_devname, he.he_devclass);
114 			exec_script(_PATH_ETC_HOTPLUG_ATTACH, he.he_devclass,
115 			    he.he_devname);
116 			break;
117 		case HOTPLUG_DEVDT:
118 			syslog(LOG_INFO, "%s detached, class %d",
119 			    he.he_devname, he.he_devclass);
120 			exec_script(_PATH_ETC_HOTPLUG_DETACH, he.he_devclass,
121 			    he.he_devname);
122 			break;
123 		default:
124 			syslog(LOG_NOTICE, "unknown event (0x%x)", he.he_type);
125 		}
126 	}
127 
128 	syslog(LOG_INFO, "terminated");
129 
130 	closelog();
131 	close(devfd);
132 
133 	return (0);
134 }
135 
136 void
137 exec_script(const char *file, int class, char *name)
138 {
139 	char strclass[8];
140 	pid_t pid;
141 
142 	snprintf(strclass, sizeof(strclass), "%d", class);
143 
144 	if (access(file, X_OK | R_OK)) {
145 		syslog(LOG_ERR, "could not access %s", file);
146 		return;
147 	}
148 
149 	if ((pid = fork()) == -1) {
150 		syslog(LOG_ERR, "fork: %m");
151 		return;
152 	}
153 	if (pid == 0) {
154 		/* child process */
155 		execl(file, basename(file), strclass, name, (char *)NULL);
156 		syslog(LOG_ERR, "execl %s: %m", file);
157 		_exit(1);
158 		/* NOTREACHED */
159 	}
160 }
161 
162 /* ARGSUSED */
163 void
164 sigchild(int signum)
165 {
166 	struct syslog_data sdata = SYSLOG_DATA_INIT;
167 	int saved_errno, status;
168 	pid_t pid;
169 
170 	saved_errno = errno;
171 
172 	sdata.log_tag = _LOG_TAG;
173 	sdata.log_fac = _LOG_FACILITY;
174 	sdata.log_stat = _LOG_OPT;
175 
176 	pid = waitpid(WAIT_ANY, &status, 0);
177 	if (pid != -1) {
178 		if (WIFEXITED(status)) {
179 			if (WEXITSTATUS(status) != 0) {
180 				syslog_r(LOG_NOTICE, &sdata,
181 				    "child exit status: %d",
182 				    WEXITSTATUS(status));
183 			}
184 		} else {
185 			syslog_r(LOG_NOTICE, &sdata,
186 			    "child is terminated abnormally");
187 		}
188 	} else {
189 		syslog_r(LOG_ERR, &sdata, "waitpid: %m");
190 	}
191 
192 	errno = saved_errno;
193 }
194 
195 /* ARGSUSED */
196 void
197 sigquit(int signum)
198 {
199 	quit = 1;
200 }
201 
202 __dead void
203 usage(void)
204 {
205 	extern char *__progname;
206 
207 	fprintf(stderr, "usage: %s [-d device]\n", __progname);
208 	exit(1);
209 }
210