xref: /openbsd-src/usr.sbin/hotplugd/hotplugd.c (revision 5b133f3f277e80f096764111e64f3a1284acb179)
1*5b133f3fSguenther /*	$OpenBSD: hotplugd.c,v 1.19 2023/03/08 04:43:13 guenther Exp $	*/
2ec953a5bSgrange /*
3ec953a5bSgrange  * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
4ec953a5bSgrange  *
5ec953a5bSgrange  * Permission to use, copy, modify, and distribute this software for any
6ec953a5bSgrange  * purpose with or without fee is hereby granted, provided that the above
7ec953a5bSgrange  * copyright notice and this permission notice appear in all copies.
8ec953a5bSgrange  *
9ec953a5bSgrange  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10ec953a5bSgrange  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11ec953a5bSgrange  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12ec953a5bSgrange  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13ec953a5bSgrange  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14ec953a5bSgrange  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15ec953a5bSgrange  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16ec953a5bSgrange  */
17ec953a5bSgrange 
18ec953a5bSgrange /*
19ec953a5bSgrange  * Devices hot plugging daemon.
20ec953a5bSgrange  */
21ec953a5bSgrange 
22ec953a5bSgrange #include <sys/types.h>
23ec953a5bSgrange #include <sys/device.h>
24ec953a5bSgrange #include <sys/hotplug.h>
25ec953a5bSgrange #include <sys/wait.h>
26ec953a5bSgrange 
27ec953a5bSgrange #include <err.h>
28ec953a5bSgrange #include <errno.h>
29ec953a5bSgrange #include <fcntl.h>
30ec953a5bSgrange #include <libgen.h>
31ca822917Snaddy #include <limits.h>
32ec953a5bSgrange #include <signal.h>
33ec953a5bSgrange #include <stdarg.h>
34ec953a5bSgrange #include <stdio.h>
35ec953a5bSgrange #include <stdlib.h>
36ec953a5bSgrange #include <string.h>
37ec953a5bSgrange #include <syslog.h>
38ec953a5bSgrange #include <unistd.h>
39ec953a5bSgrange 
40ec953a5bSgrange #define _PATH_DEV_HOTPLUG		"/dev/hotplug"
41ec953a5bSgrange #define _PATH_ETC_HOTPLUG		"/etc/hotplug"
42ec953a5bSgrange #define _PATH_ETC_HOTPLUG_ATTACH	_PATH_ETC_HOTPLUG "/attach"
43ec953a5bSgrange #define _PATH_ETC_HOTPLUG_DETACH	_PATH_ETC_HOTPLUG "/detach"
440b44a1d2Sgrange #define _LOG_TAG			"hotplugd"
450b44a1d2Sgrange #define _LOG_FACILITY			LOG_DAEMON
460b44a1d2Sgrange #define _LOG_OPT			(LOG_NDELAY | LOG_PID)
47ec953a5bSgrange 
48ec953a5bSgrange volatile sig_atomic_t quit = 0;
49ec953a5bSgrange char *device = _PATH_DEV_HOTPLUG;
50ec953a5bSgrange int devfd = -1;
51ec953a5bSgrange 
52ff0debf9Smk void exec_script(const char *, int, char *);
53ec953a5bSgrange 
540b44a1d2Sgrange void sigchild(int);
55ec953a5bSgrange void sigquit(int);
56ec953a5bSgrange __dead void usage(void);
57ec953a5bSgrange 
58ec953a5bSgrange int
main(int argc,char * argv[])59ec953a5bSgrange main(int argc, char *argv[])
60ec953a5bSgrange {
61ec953a5bSgrange 	int ch;
62ec953a5bSgrange 	struct sigaction sact;
63ec953a5bSgrange 	struct hotplug_event he;
64ec953a5bSgrange 
65ec953a5bSgrange 	while ((ch = getopt(argc, argv, "d:")) != -1)
66ec953a5bSgrange 		switch (ch) {
67ec953a5bSgrange 		case 'd':
68ec953a5bSgrange 			device = optarg;
69ec953a5bSgrange 			break;
70ec953a5bSgrange 		default:
71ec953a5bSgrange 			usage();
72ec953a5bSgrange 			/* NOTREACHED */
73ec953a5bSgrange 		}
74ec953a5bSgrange 
75fde55bc4Spyr 	argc -= optind;
76fde55bc4Spyr 	argv += optind;
77fde55bc4Spyr 	if (argc > 0)
78fde55bc4Spyr 		usage();
79fde55bc4Spyr 
80a653d5caSmestre 	if (unveil(device, "r") == -1)
81bc5a8259Sbeck 		err(1, "unveil %s", device);
82a653d5caSmestre 	if (unveil(_PATH_ETC_HOTPLUG_ATTACH, "rx") == -1)
83bc5a8259Sbeck 		err(1, "unveil %s", _PATH_ETC_HOTPLUG_ATTACH);
84a653d5caSmestre 	if (unveil(_PATH_ETC_HOTPLUG_DETACH, "rx") == -1)
85bc5a8259Sbeck 		err(1, "unveil %s", _PATH_ETC_HOTPLUG_DETACH);
86a653d5caSmestre 	if (pledge("stdio rpath proc exec", NULL) == -1)
87a653d5caSmestre 		err(1, "pledge");
88a653d5caSmestre 
8990365ab5Snatano 	if ((devfd = open(device, O_RDONLY | O_CLOEXEC)) == -1)
90ec953a5bSgrange 		err(1, "%s", device);
91ec953a5bSgrange 
92ec953a5bSgrange 	bzero(&sact, sizeof(sact));
93ec953a5bSgrange 	sigemptyset(&sact.sa_mask);
94ec953a5bSgrange 	sact.sa_flags = 0;
95ec953a5bSgrange 	sact.sa_handler = sigquit;
96ec953a5bSgrange 	sigaction(SIGINT, &sact, NULL);
97ec953a5bSgrange 	sigaction(SIGQUIT, &sact, NULL);
98ec953a5bSgrange 	sigaction(SIGTERM, &sact, NULL);
99ec953a5bSgrange 	sact.sa_handler = SIG_IGN;
100ec953a5bSgrange 	sigaction(SIGHUP, &sact, NULL);
1010b44a1d2Sgrange 	sact.sa_handler = sigchild;
102b9ddec1aSguenther 	sact.sa_flags = SA_NOCLDSTOP;
1030b44a1d2Sgrange 	sigaction(SIGCHLD, &sact, NULL);
104ec953a5bSgrange 
1050b44a1d2Sgrange 	openlog(_LOG_TAG, _LOG_OPT, _LOG_FACILITY);
106ec953a5bSgrange 	if (daemon(0, 0) == -1)
107ec953a5bSgrange 		err(1, "daemon");
108ec953a5bSgrange 
109ec953a5bSgrange 	syslog(LOG_INFO, "started");
110ec953a5bSgrange 
111ec953a5bSgrange 	while (!quit) {
112ec953a5bSgrange 		if (read(devfd, &he, sizeof(he)) == -1) {
113ec953a5bSgrange 			if (errno == EINTR)
114ec953a5bSgrange 				/* ignore */
115ec953a5bSgrange 				continue;
116ec953a5bSgrange 			syslog(LOG_ERR, "read: %m");
117ec953a5bSgrange 			exit(1);
118ec953a5bSgrange 		}
119ec953a5bSgrange 
120ec953a5bSgrange 		switch (he.he_type) {
121ec953a5bSgrange 		case HOTPLUG_DEVAT:
122ff0debf9Smk 			syslog(LOG_INFO, "%s attached, class %d",
123ff0debf9Smk 			    he.he_devname, he.he_devclass);
124ec953a5bSgrange 			exec_script(_PATH_ETC_HOTPLUG_ATTACH, he.he_devclass,
125ff0debf9Smk 			    he.he_devname);
126ec953a5bSgrange 			break;
127ec953a5bSgrange 		case HOTPLUG_DEVDT:
128ff0debf9Smk 			syslog(LOG_INFO, "%s detached, class %d",
129ff0debf9Smk 			    he.he_devname, he.he_devclass);
130ec953a5bSgrange 			exec_script(_PATH_ETC_HOTPLUG_DETACH, he.he_devclass,
131ff0debf9Smk 			    he.he_devname);
132ec953a5bSgrange 			break;
133ec953a5bSgrange 		default:
134ec953a5bSgrange 			syslog(LOG_NOTICE, "unknown event (0x%x)", he.he_type);
135ec953a5bSgrange 		}
136ec953a5bSgrange 	}
137ec953a5bSgrange 
138ec953a5bSgrange 	syslog(LOG_INFO, "terminated");
139ec953a5bSgrange 
140ec953a5bSgrange 	closelog();
141ec953a5bSgrange 	close(devfd);
142ec953a5bSgrange 
143ec953a5bSgrange 	return (0);
144ec953a5bSgrange }
145ec953a5bSgrange 
146ec953a5bSgrange void
exec_script(const char * file,int class,char * name)147ff0debf9Smk exec_script(const char *file, int class, char *name)
148ec953a5bSgrange {
149ec953a5bSgrange 	char strclass[8];
150ec953a5bSgrange 	pid_t pid;
151ec953a5bSgrange 
152ec953a5bSgrange 	snprintf(strclass, sizeof(strclass), "%d", class);
153ec953a5bSgrange 
154fb3c6b44Sgrange 	if (access(file, X_OK | R_OK) == -1) {
155fb3c6b44Sgrange 		if (errno != ENOENT)
156fb3c6b44Sgrange 			syslog(LOG_ERR, "%s: %m", file);
157ec953a5bSgrange 		return;
1582089fffcSjasper 	}
159ec953a5bSgrange 
160ec953a5bSgrange 	if ((pid = fork()) == -1) {
161ec953a5bSgrange 		syslog(LOG_ERR, "fork: %m");
162ec953a5bSgrange 		return;
163ec953a5bSgrange 	}
164ec953a5bSgrange 	if (pid == 0) {
165ec953a5bSgrange 		/* child process */
166ca822917Snaddy 		char filebuf[PATH_MAX];
167ca822917Snaddy 
168ca822917Snaddy 		strlcpy(filebuf, file, sizeof(filebuf));
169ca822917Snaddy 		execl(file, basename(filebuf), strclass, name, (char *)NULL);
170d74cdc85Sgrange 		syslog(LOG_ERR, "execl %s: %m", file);
171ec953a5bSgrange 		_exit(1);
172ec953a5bSgrange 		/* NOTREACHED */
173ee5b8001Sgrange 	}
1740b44a1d2Sgrange }
1750b44a1d2Sgrange 
1760b44a1d2Sgrange void
sigchild(int signum)1770b44a1d2Sgrange sigchild(int signum)
1780b44a1d2Sgrange {
1790b44a1d2Sgrange 	struct syslog_data sdata = SYSLOG_DATA_INIT;
1800b44a1d2Sgrange 	int saved_errno, status;
1810b44a1d2Sgrange 	pid_t pid;
1820b44a1d2Sgrange 
1830b44a1d2Sgrange 	saved_errno = errno;
1840b44a1d2Sgrange 
1850b44a1d2Sgrange 	sdata.log_tag = _LOG_TAG;
1860b44a1d2Sgrange 	sdata.log_fac = _LOG_FACILITY;
1870b44a1d2Sgrange 	sdata.log_stat = _LOG_OPT;
1880b44a1d2Sgrange 
18926baf480Skurt 	while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) {
19026baf480Skurt 		if (pid == -1) {
19126baf480Skurt 			if (errno == EINTR)
19226baf480Skurt 				continue;
19326baf480Skurt 			if (errno != ECHILD)
19426baf480Skurt 				syslog_r(LOG_ERR, &sdata, "waitpid: %m");
19526baf480Skurt 			break;
19626baf480Skurt 		}
19726baf480Skurt 
198ee5b8001Sgrange 		if (WIFEXITED(status)) {
1990b44a1d2Sgrange 			if (WEXITSTATUS(status) != 0) {
2000b44a1d2Sgrange 				syslog_r(LOG_NOTICE, &sdata,
2010b44a1d2Sgrange 				    "child exit status: %d",
202ee5b8001Sgrange 				    WEXITSTATUS(status));
2030b44a1d2Sgrange 			}
204ee5b8001Sgrange 		} else {
2050b44a1d2Sgrange 			syslog_r(LOG_NOTICE, &sdata,
2060b44a1d2Sgrange 			    "child is terminated abnormally");
207ee5b8001Sgrange 		}
208ec953a5bSgrange 	}
2090b44a1d2Sgrange 
2100b44a1d2Sgrange 	errno = saved_errno;
211ec953a5bSgrange }
212ec953a5bSgrange 
213ec953a5bSgrange void
sigquit(int signum)214ec953a5bSgrange sigquit(int signum)
215ec953a5bSgrange {
216ec953a5bSgrange 	quit = 1;
217ec953a5bSgrange }
218ec953a5bSgrange 
219ec953a5bSgrange __dead void
usage(void)220ec953a5bSgrange usage(void)
221ec953a5bSgrange {
222ec953a5bSgrange 	extern char *__progname;
223ec953a5bSgrange 
224ec953a5bSgrange 	fprintf(stderr, "usage: %s [-d device]\n", __progname);
225ec953a5bSgrange 	exit(1);
226ec953a5bSgrange }
227