chiark / gitweb /
[PATCH] wait_for_sysfs debug cleanup
[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-serial",  .file = "detach_state" },
178                 { .bus = "ide",         .file = "detach_state" },
179                 { .bus = "pci",         .file = "vendor" },
180                 { .bus = "platform",    .file = "detach_state" },
181                 { NULL }
182         };
183         struct bus_file *busfile;
184         int loop;
185
186         /* wait for the /bus-device link to the /device-device */
187         loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
188         while (--loop) {
189                 if (sysfs_get_device_bus(device_dev) == 0)
190                         break;
191
192                 usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
193         }
194         if (loop == 0) {
195                 dbg("error: getting /bus-device link");
196                 return -1;
197         }
198         dbg("/bus-device link found for bus '%s'", device_dev->bus);
199
200         /* wait for a bus specific file to show up */
201         loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
202         while (--loop) {
203                 int found = 0;
204
205                 for (busfile = bus_files; busfile->bus != NULL; busfile++) {
206                         if (strcmp(device_dev->bus, busfile->bus) == 0) {
207                                 found = 1;
208                                 dbg("looking at bus '%s' for specific file '%s'", device_dev->bus, busfile->file);
209                                 if (sysfs_get_device_attr(device_dev, busfile->file) != NULL) {
210                                         dbg("bus '%s' specific file '%s' found", device_dev->bus, busfile->file);
211                                         return 0;
212                                 }
213                         }
214                 }
215                 if (found == 0) {
216                         info("error: unknown bus, please report to "
217                              "<linux-hotplug-devel@lists.sourceforge.net> '%s'", device_dev->bus);
218                         return -1;
219                 }
220                 usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
221         }
222
223         dbg("error: getting bus '%s' specific file '%s'", device_dev->bus, busfile->file);
224         return -1;
225 }
226
227 int main(int argc, char *argv[], char *envp[])
228 {
229         const char *devpath = "";
230         const char *action;
231         const char *subsystem;
232         char sysfs_path[SYSFS_PATH_MAX];
233         char filename[SYSFS_PATH_MAX];
234         struct sysfs_class_device *class_dev;
235         struct sysfs_class_device *class_dev_parent;
236         struct sysfs_device *device_dev = NULL;
237         int loop;
238         int rc = 0;
239
240         init_logging("wait_for_sysfs");
241
242         if (argc != 2) {
243                 dbg("error: subsystem");
244                 return 1;
245         }
246         subsystem = argv[1];
247
248         devpath = getenv ("DEVPATH");
249         if (!devpath) {
250                 dbg("error: no DEVPATH");
251                 return 1;
252         }
253
254         action = getenv ("ACTION");
255         if (!action) {
256                 dbg("error: no ACTION");
257                 return 1;
258         }
259
260         /* we only wait on an add event */
261         if (strcmp(action, "add") != 0)
262                 return 0;
263
264         if (sysfs_get_mnt_path(sysfs_path, SYSFS_PATH_MAX) != 0) {
265                 dbg("error: no sysfs path");
266                 return 2;
267         }
268
269         if ((strncmp(devpath, "/block/", 7) == 0) || (strncmp(devpath, "/class/", 7) == 0)) {
270                 /* open the class device we are called for */
271                 snprintf(filename, SYSFS_PATH_MAX-1, "%s%s", sysfs_path, devpath);
272                 filename[SYSFS_PATH_MAX-1] = '\0';
273
274                 loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
275                 while (--loop) {
276                         class_dev = sysfs_open_class_device_path(filename);
277                         if (class_dev)
278                                 break;
279
280                         usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
281                 }
282                 if (class_dev == NULL) {
283                         dbg("error: getting class_device");
284                         rc = 3;
285                         goto exit;
286                 }
287                 dbg("class_device opened '%s'", filename);
288
289                 if (wait_for_class_device_attributes(class_dev) != 0) {
290                         rc = 4;
291                         goto exit_class;
292                 }
293
294                 /* skip devices without /device-link */
295                 if (class_device_expect_no_device_link(class_dev)) {
296                         dbg("no device symlink expected for '%s', ", class_dev->name);
297                         goto exit_class;
298                 }
299
300                 /* the symlink may be on the parent device */
301                 class_dev_parent = sysfs_get_classdev_parent(class_dev);
302                 if (class_dev_parent)
303                         dbg("looking at parent device for device link '%s'", class_dev_parent->path);
304
305                 /* wait for the symlink to the /device-device */
306                 dbg("waiting for symlink to /device-device");
307                 loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
308                 while (--loop) {
309                         if (class_dev_parent)
310                                 device_dev = sysfs_get_classdev_device(class_dev_parent);
311                         else
312                                 device_dev = sysfs_get_classdev_device(class_dev);
313
314                         if (device_dev)
315                                 break;
316
317                         usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
318                 }
319                 if (device_dev == NULL) {
320                         dbg("error: getting /device-device");
321                         rc = 5;
322                         goto exit_class;
323                 }
324                 dbg("device symlink found pointing to '%s'", device_dev->path);
325
326                 /* wait for the bus value */
327                 if (class_device_expect_no_bus(class_dev)) {
328                         dbg("no bus device expected for '%s', ", class_dev->classname);
329                 } else {
330                         if (wait_for_bus_device(device_dev) != 0)
331                                 rc = 6;
332                 }
333
334 exit_class:
335                 sysfs_close_class_device(class_dev);
336
337         } else if ((strncmp(devpath, "/devices/", 9) == 0)) {
338                 /* open the path we are called for */
339                 snprintf(filename, SYSFS_PATH_MAX-1, "%s%s", sysfs_path, devpath);
340                 filename[SYSFS_PATH_MAX-1] = '\0';
341
342                 loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
343                 while (--loop) {
344                         device_dev = sysfs_open_device_path(filename);
345                         if (device_dev)
346                                 break;
347
348                         usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
349                 }
350                 if (device_dev == NULL) {
351                         dbg("error: getting /device-device");
352                         rc = 7;
353                         goto exit;
354                 }
355                 dbg("device_device opened '%s'", filename);
356
357                 /* wait for the bus value */
358                 if (wait_for_bus_device(device_dev) != 0)
359                         rc = 8;
360
361                 sysfs_close_device(device_dev);
362
363         } else {
364                 dbg("unhandled sysfs path, no need to wait");
365         }
366
367 exit:
368         if (rc == 0)
369                 dbg("result: waiting for sysfs successful '%s'", devpath);
370         else
371                 info("error: wait_for_sysfs needs an update to handle the device '%s' "
372                      "properly, please report to <linux-hotplug-devel@lists.sourceforge.net>",
373                      devpath);
374
375         return rc;
376 }