chiark / gitweb /
[PATCH] cleanup PATCH for extras/chassis_id/Makefile
[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 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 }