]> git.ipfire.org Git - thirdparty/systemd.git/blob - wait_for_sysfs.c
[PATCH] wait_for_sysfs bluetooth class update
[thirdparty/systemd.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 }