1 /* vi: set sw=4 ts=4: */
4 * mdev - Mini udev for busybox
6 * Copyright 2005 Rob Landley <rob@landley.net>
7 * Copyright 2005 Frank Sorenson <frank@tuxrocks.com>
9 * Licensed under GPL version 2, see file LICENSE in this tarball for details.
16 int root_major
, root_minor
;
18 #define G (*(struct globals*)&bb_common_bufsiz1)
19 #define root_major (G.root_major)
20 #define root_minor (G.root_minor)
22 #define MAX_SYSFS_DEPTH 3 /* prevent infinite loops in /sys symlinks */
24 /* mknod in /dev based on a path like "/sys/block/hda/hda1" */
25 static void make_device(char *path
, int delete)
27 const char *device_name
;
28 int major
, minor
, type
, len
;
32 char *temp
= path
+ strlen(path
);
35 /* Try to read major/minor string. Note that the kernel puts \n after
36 * the data, so we don't need to worry about null terminating the string
37 * because sscanf() will stop at the first nondigit, which \n is. We
38 * also depend on path having writeable space after it. */
42 len
= open_read_close(path
, temp
+ 1, 64);
47 /* Determine device name, type, major and minor */
49 device_name
= bb_basename(path
);
50 type
= path
[5]=='c' ? S_IFCHR
: S_IFBLK
;
52 /* If we have a config file, look up permissions for this device */
54 if (ENABLE_FEATURE_MDEV_CONF
) {
55 char *conf
, *pos
, *end
;
58 /* mmap the config file */
59 fd
= open("/etc/mdev.conf", O_RDONLY
);
62 len
= xlseek(fd
, 0, SEEK_END
);
63 conf
= mmap(NULL
, len
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
69 /* Loop through lines in mmaped file*/
70 for (pos
=conf
; pos
-conf
<len
;) {
75 /* find end of this line */
76 for (end
=pos
; end
-conf
<len
&& *end
!='\n'; end
++)
79 /* Three fields: regex, uid:gid, mode */
80 for (field
=0; field
< (3 + ENABLE_FEATURE_MDEV_EXEC
);
84 while (pos
<end
&& isspace(*pos
)) pos
++;
85 if (pos
==end
|| *pos
=='#') break;
87 end2
<end
&& !isspace(*end2
) && *end2
!='#'; end2
++)
91 /* Regex to match this device */
93 char *regex
= xstrndup(pos
, end2
-pos
);
99 xregcomp(&match
,regex
, REG_EXTENDED
);
100 result
= regexec(&match
, device_name
, 1, &off
, 0);
104 /* If not this device, skip rest of line */
105 if (result
|| off
.rm_so
106 || off
.rm_eo
!= strlen(device_name
))
115 for (s
=pos
; s
<end2
&& *s
!=':'; s
++)
117 if (s
== end2
) break;
120 uid
= strtoul(pos
, &s2
, 10);
123 char *_unam
= xstrndup(pos
, s
-pos
);
124 pass
= getpwnam(_unam
);
131 gid
= strtoul(s
, &s2
, 10);
134 char *_grnam
= xstrndup(s
, end2
-s
);
135 grp
= getgrnam(_grnam
);
144 mode
= strtoul(pos
, &pos
, 8);
145 if (pos
!= end2
) break;
147 if (ENABLE_FEATURE_MDEV_EXEC
&& field
== 3) {
149 const char *s
= "@$*";
151 s2
= strchr(s
, *pos
++);
157 if ((s2
-s
+1) & (1<<delete))
158 command
= xstrndup(pos
, end
-pos
);
164 /* Did everything parse happily? */
166 if (field
> 2) break;
167 if (field
) bb_error_msg_and_die("bad line %d",line
);
173 end_parse
: /* nothing */ ;
178 if (sscanf(temp
, "%d:%d", &major
, &minor
) != 2) return;
179 if (mknod(device_name
, mode
| type
, makedev(major
, minor
)) && errno
!= EEXIST
)
180 bb_perror_msg_and_die("mknod %s", device_name
);
182 if (major
== root_major
&& minor
== root_minor
)
183 symlink(device_name
, "root");
185 if (ENABLE_FEATURE_MDEV_CONF
) chown(device_name
, uid
, gid
);
188 /* setenv will leak memory, so use putenv */
189 char *s
= xasprintf("MDEV=%s", device_name
);
191 if (system(command
) == -1)
192 bb_perror_msg_and_die("cannot run %s", command
);
198 if (delete) unlink(device_name
);
201 /* File callback for /sys/ traversal */
202 static int fileAction(const char *fileName
, struct stat
*statbuf
,
203 void *userData
, int depth
)
205 size_t len
= strlen(fileName
) - 4;
206 char *scratch
= userData
;
208 if (strcmp(fileName
+ len
, "/dev"))
211 strcpy(scratch
, fileName
);
213 make_device(scratch
, 0);
218 /* Directory callback for /sys/ traversal */
219 static int dirAction(const char *fileName
, struct stat
*statbuf
,
220 void *userData
, int depth
)
222 return (depth
>= MAX_SYSFS_DEPTH
? SKIP
: TRUE
);
225 /* For the full gory details, see linux/Documentation/firmware_class/README
227 * Firmware loading works like this:
228 * - kernel sets FIRMWARE env var
229 * - userspace checks /lib/firmware/$FIRMWARE
230 * - userspace waits for /sys/$DEVPATH/loading to appear
231 * - userspace writes "1" to /sys/$DEVPATH/loading
232 * - userspace copies /lib/firmware/$FIRMWARE into /sys/$DEVPATH/data
233 * - userspace writes "0" (worked) or "-1" (failed) to /sys/$DEVPATH/loading
234 * - kernel loads firmware into device
236 static void load_firmware(const char *const firmware
, const char *const sysfs_path
)
239 int firmware_fd
, loading_fd
, data_fd
;
241 /* check for $FIRMWARE from kernel */
242 /* XXX: dont bother: open(NULL) works same as open("no-such-file")
247 /* check for /lib/firmware/$FIRMWARE */
248 xchdir("/lib/firmware");
249 firmware_fd
= xopen(firmware
, O_RDONLY
);
251 /* in case we goto out ... */
254 /* check for /sys/$DEVPATH/loading ... give 30 seconds to appear */
256 for (cnt
= 0; cnt
< 30; ++cnt
) {
257 loading_fd
= open("loading", O_WRONLY
);
258 if (loading_fd
== -1)
263 if (loading_fd
== -1)
266 /* tell kernel we're loading by `echo 1 > /sys/$DEVPATH/loading` */
267 if (write(loading_fd
, "1", 1) != 1)
270 /* load firmware by `cat /lib/firmware/$FIRMWARE > /sys/$DEVPATH/data */
271 data_fd
= open("data", O_WRONLY
);
274 cnt
= bb_copyfd_eof(firmware_fd
, data_fd
);
276 /* tell kernel result by `echo [0|-1] > /sys/$DEVPATH/loading` */
278 write(loading_fd
, "0", 1);
280 write(loading_fd
, "-1", 2);
283 if (ENABLE_FEATURE_CLEAN_UP
) {
290 int mdev_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
291 int mdev_main(int argc
, char **argv
)
295 RESERVE_CONFIG_BUFFER(temp
,PATH_MAX
);
301 if (argc
== 2 && !strcmp(argv
[1],"-s")) {
305 root_major
= major(st
.st_dev
);
306 root_minor
= minor(st
.st_dev
);
308 recursive_action("/sys/block",
309 ACTION_RECURSE
| ACTION_FOLLOWLINKS
,
310 fileAction
, dirAction
, temp
, 0);
312 recursive_action("/sys/class",
313 ACTION_RECURSE
| ACTION_FOLLOWLINKS
,
314 fileAction
, dirAction
, temp
, 0);
319 action
= getenv("ACTION");
320 env_path
= getenv("DEVPATH");
321 if (!action
|| !env_path
)
324 sprintf(temp
, "/sys%s", env_path
);
325 if (!strcmp(action
, "remove"))
326 make_device(temp
, 1);
327 else if (!strcmp(action
, "add")) {
328 make_device(temp
, 0);
330 if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE
)
331 load_firmware(getenv("FIRMWARE"), temp
);
335 if (ENABLE_FEATURE_CLEAN_UP
) RELEASE_CONFIG_BUFFER(temp
);