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