chiark / gitweb /
[PATCH] add support for usb interfaces to wait_for_sysfs to keep it quiet.
[elogind.git] / wait_for_sysfs.c
1 /*
2  * wait_for_sysfs.c  - small program to delay the execution
3  *                     of /etc/hotplug.d/ programs, until sysfs
4  *                     is fully populated by the kernel. Depending on
5  *                     the type of device, we wait for all expected
6  *                     directories and then just exit.
7  *
8  * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
9  *
10  *      This program is free software; you can redistribute it and/or modify it
11  *      under the terms of the GNU General Public License as published by the
12  *      Free Software Foundation version 2 of the License.
13  * 
14  *      This program is distributed in the hope that it will be useful, but
15  *      WITHOUT ANY WARRANTY; without even the implied warranty of
16  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *      General Public License for more details.
18  * 
19  *      You should have received a copy of the GNU General Public License along
20  *      with this program; if not, write to the Free Software Foundation, Inc.,
21  *      675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  */
24
25 #include <stdio.h>
26 #include <stddef.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <sys/stat.h>
33
34 #include "logging.h"
35 #include "libsysfs/sysfs/libsysfs.h"
36
37 #ifdef LOG
38 unsigned char logname[LOGNAME_SIZE];
39 void log_message(int level, const char *format, ...)
40 {
41         va_list args;
42
43         va_start(args, format);
44         vsyslog(level, format, args);
45         va_end(args);
46 }
47 #endif
48
49 #define WAIT_MAX_SECONDS                5
50 #define WAIT_LOOP_PER_SECOND            20
51
52 /* wait for specific file to show up, normally the "dev"-file */
53 static int wait_for_class_device_attributes(struct sysfs_class_device *class_dev)
54 {
55         static struct class_file {
56                 char *subsystem;
57                 char *file;
58         } class_files[] = {
59                 { .subsystem = "net",           .file = "ifindex" },
60                 { .subsystem = "scsi_host",     .file = "unique_id" },
61                 { .subsystem = "scsi_device",   .file = NULL },
62                 { .subsystem = "pcmcia_socket", .file = "card_type" },
63                 { .subsystem = "usb_host",      .file = NULL },
64                 { NULL, NULL }
65         };
66         struct class_file *classfile;
67         const char *file = "dev";
68         int loop;
69
70         /* look if we want to look for another file instead of "dev" */
71         for (classfile = class_files; classfile->subsystem != NULL; classfile++) {
72                 if (strcmp(class_dev->classname, classfile->subsystem) == 0) {
73                         if (classfile->file == NULL) {
74                                 dbg("class '%s' has no file to wait for", class_dev->classname);
75                                 return 0;
76                         }
77                         file = classfile->file;
78                         break;
79                 }
80         }
81         dbg("looking at class '%s' for specific file '%s'", class_dev->classname, file);
82
83         loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
84         while (--loop) {
85                 if (sysfs_get_classdev_attr(class_dev, file) != NULL) {
86                         dbg("class '%s' specific file '%s' found", class_dev->classname, file);
87                         return 0;
88                 }
89
90                 usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
91         }
92
93         dbg("error: getting class '%s' specific file '%s'", class_dev->classname, file);
94         return -1;
95 }
96
97 /* skip waiting for physical device */
98 static int class_device_expect_no_device_link(struct sysfs_class_device *class_dev)
99 {
100         static char *devices_without_link[] = {
101                 "nb",
102                 "ram",
103                 "loop",
104                 "fd",
105                 "md",
106                 "dos_cd",
107                 "double",
108                 "flash",
109                 "msd",
110                 "rflash",
111                 "rom",
112                 "rrom",
113                 "sbpcd",
114                 "pcd",
115                 "pf",
116                 "scd",
117                 "sit",
118                 "lp",
119                 "ubd",
120                 "vcs",
121                 "vcsa",
122                 "console",
123                 "tty",
124                 "ttyS",
125                 NULL
126         };
127         char **device;
128
129         for (device = devices_without_link; *device != NULL; device++) {
130                 int len = strlen(*device);
131
132                 /* look if name matches */
133                 if (strncmp(class_dev->name, *device, len) != 0)
134                         continue;
135
136                 /* exact match */
137                 if (strlen(class_dev->name) == len)
138                         return 1;
139
140                 /* instance numbers are matching too */
141                 if (isdigit(class_dev->name[len]))
142                         return 1;
143         }
144
145         return 0;
146 }
147
148 /* skip waiting for the bus */
149 static int class_device_expect_no_bus(struct sysfs_class_device *class_dev)
150 {
151         static char *devices_without_bus[] = {
152                 "scsi_host",
153                 NULL
154         };
155         char **device;
156
157         for (device = devices_without_bus; *device != NULL; device++) {
158                 int len = strlen(*device);
159
160                 if (strncmp(class_dev->classname, *device, len) == 0)
161                         return 1;
162         }
163
164         return 0;
165 }
166
167 /* wait for the bus and for a bus specific file to show up */
168 static int wait_for_bus_device(struct sysfs_device *device_dev)
169 {
170         static struct bus_file {
171                 char *bus;
172                 char *file;
173         } bus_files[] = {
174                 { .bus = "scsi",        .file = "vendor" },
175                 { .bus = "usb",         .file = "idVendor" },
176                 { .bus = "usb",         .file = "iInterface" },
177                 { .bus = "usb",         .file = "bNumEndpoints" },
178                 { .bus = "usb-serial",  .file = "detach_state" },
179                 { .bus = "ide",         .file = "detach_state" },
180                 { .bus = "pci",         .file = "vendor" },
181                 { .bus = "platform",    .file = "detach_state" },
182                 { NULL }
183         };
184         struct bus_file *busfile;
185         int loop;
186
187         /* wait for the /bus-device link to the /device-device */
188         loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
189         while (--loop) {
190                 if (sysfs_get_device_bus(device_dev) == 0)
191                         break;
192
193                 usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
194         }
195         if (loop == 0) {
196                 dbg("error: getting /bus-device link");
197                 return -1;
198         }
199         dbg("/bus-device link found for bus '%s'", device_dev->bus);
200
201         /* wait for a bus specific file to show up */
202         loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
203         while (--loop) {
204                 int found = 0;
205
206                 for (busfile = bus_files; busfile->bus != NULL; busfile++) {
207                         if (strcmp(device_dev->bus, busfile->bus) == 0) {
208                                 found = 1;
209                                 dbg("looking at bus '%s' for specific file '%s'", device_dev->bus, busfile->file);
210                                 if (sysfs_get_device_attr(device_dev, busfile->file) != NULL) {
211                                         dbg("bus '%s' specific file '%s' found", device_dev->bus, busfile->file);
212                                         return 0;
213                                 }
214                         }
215                 }
216                 if (found == 0) {
217                         info("error: unknown bus, please report to "
218                              "<linux-hotplug-devel@lists.sourceforge.net> '%s'", device_dev->bus);
219                         return -1;
220                 }
221                 usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
222         }
223
224         dbg("error: getting bus '%s' specific file '%s'", device_dev->bus, busfile->file);
225         return -1;
226 }
227
228 int main(int argc, char *argv[], char *envp[])
229 {
230         const char *devpath = "";
231         const char *action;
232         const char *subsystem;
233         char sysfs_path[SYSFS_PATH_MAX];
234         char filename[SYSFS_PATH_MAX];
235         struct sysfs_class_device *class_dev;
236         struct sysfs_class_device *class_dev_parent;
237         struct sysfs_device *device_dev = NULL;
238         int loop;
239         int rc = 0;
240
241         init_logging("wait_for_sysfs");
242
243         if (argc != 2) {
244                 dbg("error: subsystem");
245                 return 1;
246         }
247         subsystem = argv[1];
248
249         devpath = getenv ("DEVPATH");
250         if (!devpath) {
251                 dbg("error: no DEVPATH");
252                 return 1;
253         }
254
255         action = getenv ("ACTION");
256         if (!action) {
257                 dbg("error: no ACTION");
258                 return 1;
259         }
260
261         /* we only wait on an add event */
262         if (strcmp(action, "add") != 0)
263                 return 0;
264
265         if (sysfs_get_mnt_path(sysfs_path, SYSFS_PATH_MAX) != 0) {
266                 dbg("error: no sysfs path");
267                 return 2;
268         }
269
270         if ((strncmp(devpath, "/block/", 7) == 0) || (strncmp(devpath, "/class/", 7) == 0)) {
271                 /* open the class device we are called for */
272                 snprintf(filename, SYSFS_PATH_MAX-1, "%s%s", sysfs_path, devpath);
273                 filename[SYSFS_PATH_MAX-1] = '\0';
274
275                 loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
276                 while (--loop) {
277                         class_dev = sysfs_open_class_device_path(filename);
278                         if (class_dev)
279                                 break;
280
281                         usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
282                 }
283                 if (class_dev == NULL) {
284                         dbg("error: getting class_device");
285                         rc = 3;
286                         goto exit;
287                 }
288                 dbg("class_device opened '%s'", filename);
289
290                 if (wait_for_class_device_attributes(class_dev) != 0) {
291                         rc = 4;
292                         goto exit_class;
293                 }
294
295                 /* skip devices without /device-link */
296                 if (class_device_expect_no_device_link(class_dev)) {
297                         dbg("no device symlink expected for '%s', ", class_dev->name);
298                         goto exit_class;
299                 }
300
301                 /* the symlink may be on the parent device */
302                 class_dev_parent = sysfs_get_classdev_parent(class_dev);
303                 if (class_dev_parent)
304                         dbg("looking at parent device for device link '%s'", class_dev_parent->path);
305
306                 /* wait for the symlink to the /device-device */
307                 dbg("waiting for symlink to /device-device");
308                 loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
309                 while (--loop) {
310                         if (class_dev_parent)
311                                 device_dev = sysfs_get_classdev_device(class_dev_parent);
312                         else
313                                 device_dev = sysfs_get_classdev_device(class_dev);
314
315                         if (device_dev)
316                                 break;
317
318                         usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
319                 }
320                 if (device_dev == NULL) {
321                         dbg("error: getting /device-device");
322                         rc = 5;
323                         goto exit_class;
324                 }
325                 dbg("device symlink found pointing to '%s'", device_dev->path);
326
327                 /* wait for the bus value */
328                 if (class_device_expect_no_bus(class_dev)) {
329                         dbg("no bus device expected for '%s', ", class_dev->classname);
330                 } else {
331                         if (wait_for_bus_device(device_dev) != 0)
332                                 rc = 6;
333                 }
334
335 exit_class:
336                 sysfs_close_class_device(class_dev);
337
338         } else if ((strncmp(devpath, "/devices/", 9) == 0)) {
339                 /* open the path we are called for */
340                 snprintf(filename, SYSFS_PATH_MAX-1, "%s%s", sysfs_path, devpath);
341                 filename[SYSFS_PATH_MAX-1] = '\0';
342
343                 loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
344                 while (--loop) {
345                         device_dev = sysfs_open_device_path(filename);
346                         if (device_dev)
347                                 break;
348
349                         usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
350                 }
351                 if (device_dev == NULL) {
352                         dbg("error: getting /device-device");
353                         rc = 7;
354                         goto exit;
355                 }
356                 dbg("device_device opened '%s'", filename);
357
358                 /* wait for the bus value */
359                 if (wait_for_bus_device(device_dev) != 0)
360                         rc = 8;
361
362                 sysfs_close_device(device_dev);
363
364         } else {
365                 dbg("unhandled sysfs path, no need to wait");
366         }
367
368 exit:
369         if (rc == 0)
370                 dbg("result: waiting for sysfs successful '%s'", devpath);
371         else
372                 info("error: wait_for_sysfs needs an update to handle the device '%s' "
373                      "properly, please report to <linux-hotplug-devel@lists.sourceforge.net>",
374                      devpath);
375
376         return rc;
377 }