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