chiark / gitweb /
5e8874cf7c800926ed5db019a03d8768266083cb
[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                 { .subsystem = "bluetooth",     .file = "address" },
65                 { .subsystem = "i2c-adapter",   .file = NULL },
66                 { NULL, NULL }
67         };
68         struct class_file *classfile;
69         const char *file = "dev";
70         int loop;
71
72         /* look if we want to look for another file instead of "dev" */
73         for (classfile = class_files; classfile->subsystem != NULL; classfile++) {
74                 if (strcmp(class_dev->classname, classfile->subsystem) == 0) {
75                         if (classfile->file == NULL) {
76                                 dbg("class '%s' has no file to wait for", class_dev->classname);
77                                 return 0;
78                         }
79                         file = classfile->file;
80                         break;
81                 }
82         }
83         dbg("looking at class '%s' for specific file '%s'", class_dev->classname, file);
84
85         loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
86         while (--loop) {
87                 if (sysfs_get_classdev_attr(class_dev, file) != NULL) {
88                         dbg("class '%s' specific file '%s' found", class_dev->classname, file);
89                         return 0;
90                 }
91
92                 usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
93         }
94
95         dbg("error: getting class '%s' specific file '%s'", class_dev->classname, file);
96         return -1;
97 }
98
99 /* skip waiting for physical device */
100 static int class_device_expect_no_device_link(struct sysfs_class_device *class_dev)
101 {
102         static char *devices_without_link[] = {
103                 "nb",
104                 "ram",
105                 "loop",
106                 "fd",
107                 "md",
108                 "dos_cd",
109                 "double",
110                 "flash",
111                 "msd",
112                 "rflash",
113                 "rom",
114                 "rrom",
115                 "sbpcd",
116                 "pcd",
117                 "pf",
118                 "scd",
119                 "sit",
120                 "lp",
121                 "ubd",
122                 "vcs",
123                 "vcsa",
124                 "console",
125                 "tty",
126                 "ttyS",
127                 NULL
128         };
129         char **device;
130
131         for (device = devices_without_link; *device != NULL; device++) {
132                 int len = strlen(*device);
133
134                 /* look if name matches */
135                 if (strncmp(class_dev->name, *device, len) != 0)
136                         continue;
137
138                 /* exact match */
139                 if (strlen(class_dev->name) == len)
140                         return 1;
141
142                 /* instance numbers are matching too */
143                 if (isdigit(class_dev->name[len]))
144                         return 1;
145         }
146
147         return 0;
148 }
149
150 /* skip waiting for the bus */
151 static int class_device_expect_no_bus(struct sysfs_class_device *class_dev)
152 {
153         static char *devices_without_bus[] = {
154                 "scsi_host",
155                 "i2c-adapter",
156                 NULL
157         };
158         char **device;
159
160         for (device = devices_without_bus; *device != NULL; device++) {
161                 int len = strlen(*device);
162
163                 if (strncmp(class_dev->classname, *device, len) == 0)
164                         return 1;
165         }
166
167         return 0;
168 }
169
170 /* wait for the bus and for a bus specific file to show up */
171 static int wait_for_bus_device(struct sysfs_device *device_dev)
172 {
173         static struct bus_file {
174                 char *bus;
175                 char *file;
176         } bus_files[] = {
177                 { .bus = "scsi",        .file = "vendor" },
178                 { .bus = "usb",         .file = "idVendor" },
179                 { .bus = "usb",         .file = "iInterface" },
180                 { .bus = "usb",         .file = "bNumEndpoints" },
181                 { .bus = "usb-serial",  .file = "detach_state" },
182                 { .bus = "ide",         .file = "detach_state" },
183                 { .bus = "pci",         .file = "vendor" },
184                 { .bus = "platform",    .file = "detach_state" },
185                 { NULL }
186         };
187         struct bus_file *busfile;
188         int loop;
189
190         /* wait for the /bus-device link to the /device-device */
191         loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
192         while (--loop) {
193                 if (sysfs_get_device_bus(device_dev) == 0)
194                         break;
195
196                 usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
197         }
198         if (loop == 0) {
199                 dbg("error: getting /bus-device link");
200                 return -1;
201         }
202         dbg("/bus-device link found for bus '%s'", device_dev->bus);
203
204         /* wait for a bus specific file to show up */
205         loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
206         while (--loop) {
207                 int found = 0;
208
209                 for (busfile = bus_files; busfile->bus != NULL; busfile++) {
210                         if (strcmp(device_dev->bus, busfile->bus) == 0) {
211                                 found = 1;
212                                 dbg("looking at bus '%s' for specific file '%s'", device_dev->bus, busfile->file);
213                                 if (sysfs_get_device_attr(device_dev, busfile->file) != NULL) {
214                                         dbg("bus '%s' specific file '%s' found", device_dev->bus, busfile->file);
215                                         return 0;
216                                 }
217                         }
218                 }
219                 if (found == 0) {
220                         info("error: unknown bus, please report to "
221                              "<linux-hotplug-devel@lists.sourceforge.net> '%s'", device_dev->bus);
222                         return -1;
223                 }
224                 usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
225         }
226
227         dbg("error: getting bus '%s' specific file '%s'", device_dev->bus, busfile->file);
228         return -1;
229 }
230
231 int main(int argc, char *argv[], char *envp[])
232 {
233         const char *devpath = "";
234         const char *action;
235         const char *subsystem;
236         char sysfs_path[SYSFS_PATH_MAX];
237         char filename[SYSFS_PATH_MAX];
238         struct sysfs_class_device *class_dev;
239         struct sysfs_class_device *class_dev_parent;
240         struct sysfs_device *device_dev = NULL;
241         int loop;
242         int rc = 0;
243
244         init_logging("wait_for_sysfs");
245
246         if (argc != 2) {
247                 dbg("error: subsystem");
248                 return 1;
249         }
250         subsystem = argv[1];
251
252         devpath = getenv ("DEVPATH");
253         if (!devpath) {
254                 dbg("error: no DEVPATH");
255                 return 1;
256         }
257
258         action = getenv ("ACTION");
259         if (!action) {
260                 dbg("error: no ACTION");
261                 return 1;
262         }
263
264         /* we only wait on an add event */
265         if (strcmp(action, "add") != 0)
266                 return 0;
267
268         if (sysfs_get_mnt_path(sysfs_path, SYSFS_PATH_MAX) != 0) {
269                 dbg("error: no sysfs path");
270                 return 2;
271         }
272
273         if ((strncmp(devpath, "/block/", 7) == 0) || (strncmp(devpath, "/class/", 7) == 0)) {
274                 /* open the class device we are called for */
275                 snprintf(filename, SYSFS_PATH_MAX-1, "%s%s", sysfs_path, devpath);
276                 filename[SYSFS_PATH_MAX-1] = '\0';
277
278                 loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
279                 while (--loop) {
280                         class_dev = sysfs_open_class_device_path(filename);
281                         if (class_dev)
282                                 break;
283
284                         usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
285                 }
286                 if (class_dev == NULL) {
287                         dbg("error: getting class_device");
288                         rc = 3;
289                         goto exit;
290                 }
291                 dbg("class_device opened '%s'", filename);
292
293                 if (wait_for_class_device_attributes(class_dev) != 0) {
294                         rc = 4;
295                         goto exit_class;
296                 }
297
298                 /* skip devices without /device-link */
299                 if (class_device_expect_no_device_link(class_dev)) {
300                         dbg("no device symlink expected for '%s', ", class_dev->name);
301                         goto exit_class;
302                 }
303
304                 /* the symlink may be on the parent device */
305                 class_dev_parent = sysfs_get_classdev_parent(class_dev);
306                 if (class_dev_parent)
307                         dbg("looking at parent device for device link '%s'", class_dev_parent->path);
308
309                 /* wait for the symlink to the /device-device */
310                 dbg("waiting for symlink to /device-device");
311                 loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
312                 while (--loop) {
313                         if (class_dev_parent)
314                                 device_dev = sysfs_get_classdev_device(class_dev_parent);
315                         else
316                                 device_dev = sysfs_get_classdev_device(class_dev);
317
318                         if (device_dev)
319                                 break;
320
321                         usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
322                 }
323                 if (device_dev == NULL) {
324                         dbg("error: getting /device-device");
325                         rc = 5;
326                         goto exit_class;
327                 }
328                 dbg("device symlink found pointing to '%s'", device_dev->path);
329
330                 /* wait for the bus value */
331                 if (class_device_expect_no_bus(class_dev)) {
332                         dbg("no bus device expected for '%s', ", class_dev->classname);
333                 } else {
334                         if (wait_for_bus_device(device_dev) != 0)
335                                 rc = 6;
336                 }
337
338 exit_class:
339                 sysfs_close_class_device(class_dev);
340
341         } else if ((strncmp(devpath, "/devices/", 9) == 0)) {
342                 /* open the path we are called for */
343                 snprintf(filename, SYSFS_PATH_MAX-1, "%s%s", sysfs_path, devpath);
344                 filename[SYSFS_PATH_MAX-1] = '\0';
345
346                 loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
347                 while (--loop) {
348                         device_dev = sysfs_open_device_path(filename);
349                         if (device_dev)
350                                 break;
351
352                         usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
353                 }
354                 if (device_dev == NULL) {
355                         dbg("error: getting /device-device");
356                         rc = 7;
357                         goto exit;
358                 }
359                 dbg("device_device opened '%s'", filename);
360
361                 /* wait for the bus value */
362                 if (wait_for_bus_device(device_dev) != 0)
363                         rc = 8;
364
365                 sysfs_close_device(device_dev);
366
367         } else {
368                 dbg("unhandled sysfs path, no need to wait");
369         }
370
371 exit:
372         if (rc == 0)
373                 dbg("result: waiting for sysfs successful '%s'", devpath);
374         else
375                 info("error: wait_for_sysfs needs an update to handle the device '%s' "
376                      "properly, please report to <linux-hotplug-devel@lists.sourceforge.net>",
377                      devpath);
378
379         return rc;
380 }