Merge branch 'vendor/BINUTILS221'
[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/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 #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 enum obsd_devclass {
49         DV_DULL,                /* generic, no special info */
50         DV_CPU,                 /* CPU (carries resource utilization) */
51         DV_DISK,                /* disk drive (label, etc) */
52         DV_IFNET,               /* network interface */
53         DV_TAPE,                /* tape device */
54         DV_TTY                  /* serial line interface (???) */
55 };
56
57 extern char *__progname;
58
59 volatile sig_atomic_t quit = 0;
60
61 void exec_script(const char *, int, const char *);
62 void sigchild(int);
63 void sigquit(int);
64 __dead void usage(void);
65
66 int
67 main(int argc, char *argv[])
68 {
69         int ch, class, ret;
70         struct sigaction sact;
71         struct udev *udev;
72         struct udev_enumerate *udev_enum;
73         struct udev_list_entry *udev_le, *udev_le_first;
74         struct udev_monitor *udev_monitor;
75         struct udev_device *udev_dev;
76         enum obsd_devclass devclass;
77         const char *prop;
78
79         while ((ch = getopt(argc, argv, "?")) != -1)
80                 switch (ch) {
81                 case '?':
82                 default:
83                         usage();
84                         /* NOTREACHED */
85                 }
86
87         argc -= optind;
88         argv += optind;
89         if (argc > 0)
90                 usage();
91
92         udev = udev_new();
93         if (udev == NULL)
94                 err(1, "udev_new");
95
96         bzero(&sact, sizeof(sact));
97         sigemptyset(&sact.sa_mask);
98         sact.sa_flags = 0;
99         sact.sa_handler = sigquit;
100         sigaction(SIGINT, &sact, NULL);
101         sigaction(SIGQUIT, &sact, NULL);
102         sigaction(SIGTERM, &sact, NULL);
103         sact.sa_handler = SIG_IGN;
104         sigaction(SIGHUP, &sact, NULL);
105         sact.sa_handler = sigchild;
106         sact.sa_flags = SA_NOCLDSTOP;
107         sigaction(SIGCHLD, &sact, NULL);
108
109         openlog(_LOG_TAG, _LOG_OPT, _LOG_FACILITY);
110
111         if (daemon(0, 0) == -1)
112                 err(1, "daemon");
113
114         syslog(LOG_INFO, "started");
115
116         udev_enum = udev_enumerate_new(udev);
117         if (udev_enum == NULL)
118                 err(1, "udev_enumerate_new");
119
120         ret = udev_enumerate_scan_devices(udev_enum);
121         if (ret != 0)
122                 err(1, "udev_enumerate_scan_device ret = %d", ret);
123
124         udev_le_first = udev_enumerate_get_list_entry(udev_enum);
125         if (udev_le_first == NULL)
126                 err(1, "udev_enumerate_get_list_entry error");
127
128         udev_list_entry_foreach(udev_le, udev_le_first) {
129                 udev_dev = udev_list_entry_get_device(udev_le);
130
131                 class = atoi(udev_device_get_property_value(udev_dev, "devtype"));
132                 devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL)));
133
134                 syslog(LOG_INFO, "%s attached, class %d",
135                     udev_device_get_devnode(udev_dev), devclass);
136                 exec_script(_PATH_ETC_HOTPLUG_ATTACH, devclass,
137                    udev_device_get_devnode(udev_dev));
138         }
139
140         udev_enumerate_unref(udev_enum);
141         udev_monitor = udev_monitor_new(udev);
142
143         ret = udev_monitor_enable_receiving(udev_monitor);
144         if (ret != 0)
145                 err(1, "udev_monitor_enable_receiving ret = %d", ret);
146
147         while (!quit) {
148                 if ((udev_dev = udev_monitor_receive_device(udev_monitor)) == NULL) {
149                         syslog(LOG_ERR, "read: %m");
150                         exit(1);
151                 }
152
153                 prop = udev_device_get_action(udev_dev);
154                 class = atoi(udev_device_get_property_value(udev_dev, "devtype"));
155                 devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL)));
156
157                 if (strcmp(prop, "attach") == 0) {
158                         syslog(LOG_INFO, "%s attached, class %d",
159                             udev_device_get_devnode(udev_dev), devclass);
160                         exec_script(_PATH_ETC_HOTPLUG_ATTACH, devclass,
161                             udev_device_get_devnode(udev_dev));
162                 } else if (strcmp(prop, "detach") == 0) {
163                         syslog(LOG_INFO, "%s detached, class %d",
164                             udev_device_get_devnode(udev_dev), devclass);
165                         exec_script(_PATH_ETC_HOTPLUG_DETACH, devclass,
166                             udev_device_get_devnode(udev_dev));
167                 } else {
168                         syslog(LOG_NOTICE, "unknown event (%s)", prop);
169                 }
170         }
171
172         syslog(LOG_INFO, "terminated");
173
174         closelog();
175
176         udev_monitor_unref(udev_monitor);
177         udev_unref(udev);
178
179         return (0);
180 }
181
182 void
183 exec_script(const char *file, int class, const char *name)
184 {
185         char strclass[8];
186         pid_t pid;
187
188         snprintf(strclass, sizeof(strclass), "%d", class);
189
190         if (access(file, X_OK | R_OK)) {
191                 syslog(LOG_ERR, "could not access %s", file);
192                 return;
193         }
194
195         if ((pid = fork()) == -1) {
196                 syslog(LOG_ERR, "fork: %m");
197                 return;
198         }
199         if (pid == 0) {
200                 /* child process */
201                 execl(file, basename(file), strclass, name, (char *)NULL);
202                 syslog(LOG_ERR, "execl %s: %m", file);
203                 _exit(1);
204                 /* NOTREACHED */
205         }
206 }
207
208 /* ARGSUSED */
209 void
210 sigchild(int signum __unused)
211 {
212         struct syslog_data sdata = SYSLOG_DATA_INIT;
213         int saved_errno, status;
214         pid_t pid;
215
216         saved_errno = errno;
217
218         sdata.log_tag = _LOG_TAG;
219         sdata.log_fac = _LOG_FACILITY;
220         sdata.log_stat = _LOG_OPT;
221
222         while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) {
223                 if (pid == -1) {
224                         if (errno == EINTR)
225                                 continue;
226                         if (errno != ECHILD)
227                                 syslog_r(LOG_ERR, &sdata, "waitpid: %m");
228                         break;
229                 }
230
231                 if (WIFEXITED(status)) {
232                         if (WEXITSTATUS(status) != 0) {
233                                 syslog_r(LOG_NOTICE, &sdata,
234                                     "child exit status: %d",
235                                     WEXITSTATUS(status));
236                         }
237                 } else {
238                         syslog_r(LOG_NOTICE, &sdata,
239                             "child is terminated abnormally");
240                 }
241         }
242
243         errno = saved_errno;
244 }
245
246 /* ARGSUSED */
247 void
248 sigquit(int signum __unused)
249 {
250         quit = 1;
251 }
252
253 __dead void
254 usage(void)
255 {
256         fprintf(stderr, "usage: %s [-d device]\n", __progname);
257         exit(1);
258 }