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