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