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