]> git.ipfire.org Git - thirdparty/systemd.git/blob - wait_for_sysfs.c
28ff43bdb5370dc07179bcba4751523269e0d1c0
[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 { 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 }