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