aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2006-06-26 14:11:33 +0000
committerRob Landley <rob@landley.net>2006-06-26 14:11:33 +0000
commitef10d52745a72f524e36edc375a6a05d3365ede4 (patch)
treebd1ee024e81262ec2485c4d5f391ef910d85bd38
parentcf7577d4171d517690e95e7225979bfde32fce7c (diff)
downloadbusybox-w32-ef10d52745a72f524e36edc375a6a05d3365ede4.tar.gz
busybox-w32-ef10d52745a72f524e36edc375a6a05d3365ede4.tar.bz2
busybox-w32-ef10d52745a72f524e36edc375a6a05d3365ede4.zip
Upgrade mdev to allow commands to be run on create/delete.
Both Jason Schoon and Giuseppe Ciotta deserve credit for this, I used elements of both.  It's been upgraded so that you can specify that a given command should run at create, at delete, or at both using different special characters (@, $, and * respectively).  It uses the system() method of running command lines which means you can use environment variables on the command line (it sets $MDEV to the name of the current device being created/deleted, which is useful if you matched it via regex), and the documentation warns that you need a /bin/sh to make that work, so you probably want to pick a default shell.
-rw-r--r--include/usage.h22
-rw-r--r--util-linux/Config.in27
-rw-r--r--util-linux/mdev.c186
3 files changed, 130 insertions, 105 deletions
diff --git a/include/usage.h b/include/usage.h
index 8defbd1fc..712eede63 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -1799,19 +1799,25 @@ USE_FEATURE_DATE_ISOFMT( \
1799 "\t-s\tScan /sys and populate /dev during system boot\n\n" \ 1799 "\t-s\tScan /sys and populate /dev during system boot\n\n" \
1800 "Called with no options (via hotplug) it uses environment variables\n" \ 1800 "Called with no options (via hotplug) it uses environment variables\n" \
1801 "to determine which device to add/remove." 1801 "to determine which device to add/remove."
1802#ifdef CONFIG_FEATURE_MDEV_CONFIG 1802#define mdev_notes_usage "" \
1803#define mdev_notes_usage \ 1803USE_FEATURE_MDEV_CONFIG( \
1804 "The mdev config file contains lines that look like:\n" \ 1804 "The mdev config file contains lines that look like:\n" \
1805 " hd[a-z][0-9]* 0:3 660\n\n" \ 1805 " hd[a-z][0-9]* 0:3 660\n\n" \
1806 "That's device name (with regex match), uid:gid, and permissions.\n\n" \ 1806 "That's device name (with regex match), uid:gid, and permissions.\n\n" \
1807 "Optionally, that can be followed (on the same line) by an asterisk\n" \ 1807 USE_FEATURE_MDEV_EXEC( \
1808 "and a command line to run after creating the corresponding device(s),\n"\ 1808 "Optionally, that can be followed (on the same line) by a special character\n" \
1809 "ala:\n\n" \ 1809 "and a command line to run after creating/before deleting the corresponding\n" \
1810 " hdc root:cdrom 660 *ln -s hdc cdrom\n\n" \ 1810 "device(s). The environment variable $MDEV indicates the active device node\n" \
1811 "(which is useful if it's a regex match). For example:\n\n" \
1812 " hdc root:cdrom 660 *ln -s $MDEV cdrom\n\n" \
1813 "The special characters are @ (run after creating), $ (run before deleting),\n" \
1814 "and * (run both after creating and before deleting). The commands run in\n" \
1815 "the /dev directory, and use system() which calls /bin/sh.\n\n" \
1816 ) \
1811 "Config file parsing stops on the first matching line. If no config\n"\ 1817 "Config file parsing stops on the first matching line. If no config\n"\
1812 "entry is matched, devices are created with default 0:0 660. (Make\n"\ 1818 "entry is matched, devices are created with default 0:0 660. (Make\n"\
1813 "the last line match .* to override this.)\n\n" 1819 "the last line match .* to override this.)\n\n" \
1814#endif 1820)
1815 1821
1816#define mesg_trivial_usage \ 1822#define mesg_trivial_usage \
1817 "[y|n]" 1823 "[y|n]"
diff --git a/util-linux/Config.in b/util-linux/Config.in
index a493fbfb4..501ed6bfc 100644
--- a/util-linux/Config.in
+++ b/util-linux/Config.in
@@ -265,16 +265,31 @@ config CONFIG_FEATURE_MDEV_CONF
265 265
266 That's device name (with regex match), uid:gid, and permissions. 266 That's device name (with regex match), uid:gid, and permissions.
267 267
268 Optionally, that can be followed (on the same line) by an asterisk
269 and a command line to run after creating the corresponding device(s),
270 ala:
271
272 hdc root:cdrom 660 *ln -s hdc cdrom
273
274 Config file parsing stops on the first matching line. If no config 268 Config file parsing stops on the first matching line. If no config
275 entry is matched, devices are created with default 0:0 660. (Make 269 entry is matched, devices are created with default 0:0 660. (Make
276 the last line match .* to override this.) 270 the last line match .* to override this.)
277 271
272config CONFIG_FEATURE_MDEV_EXEC
273 bool "Support command execution at device addition/removal"
274 default n
275 depends on CONFIG_FEATURE_MDEV_CONF
276 help
277 This adds support for an optional field to /etc/mdev.conf, consisting
278 of a special character and a command line to run after creating the
279 corresponding device(s) and before removing, ala:
280
281 hdc root:cdrom 660 *ln -s $MDEV cdrom
282
283 The $MDEV environment variable is set to the name of the device.
284
285 The special characters and their meanings are:
286 @ Run after creating the device.
287 $ Run before removing the device.
288 * Run both after creating and before removing the device.
289
290 Commands are executed via system() so you need /bin/sh, meaning you
291 probably want to select a default shell in the Shells menu.
292
278config CONFIG_MKSWAP 293config CONFIG_MKSWAP
279 bool "mkswap" 294 bool "mkswap"
280 default n 295 default n
diff --git a/util-linux/mdev.c b/util-linux/mdev.c
index 73a82314c..3c7cf7bd1 100644
--- a/util-linux/mdev.c
+++ b/util-linux/mdev.c
@@ -8,25 +8,14 @@
8 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 8 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
9 */ 9 */
10 10
11#include "busybox.h"
11#include <ctype.h> 12#include <ctype.h>
12#include <dirent.h>
13#include <errno.h> 13#include <errno.h>
14#include <fcntl.h>
15#include <stdio.h>
16#include <string.h>
17#include <sys/mman.h> 14#include <sys/mman.h>
18#include <sys/stat.h>
19#include <sys/sysmacros.h> 15#include <sys/sysmacros.h>
20#include <sys/types.h>
21#include <unistd.h>
22#include <stdlib.h>
23#include "busybox.h"
24#include "xregex.h" 16#include "xregex.h"
25 17
26#define DEV_PATH "/dev" 18#define DEV_PATH "/dev"
27#define MDEV_CONF "/etc/mdev.conf"
28
29#include <busybox.h>
30 19
31struct mdev_globals 20struct mdev_globals
32{ 21{
@@ -36,14 +25,15 @@ struct mdev_globals
36#define bbg mdev_globals 25#define bbg mdev_globals
37 26
38/* mknod in /dev based on a path like "/sys/block/hda/hda1" */ 27/* mknod in /dev based on a path like "/sys/block/hda/hda1" */
39static void make_device(char *path) 28static void make_device(char *path, int delete)
40{ 29{
41 char *device_name, *s; 30 char *device_name;
42 int major, minor, type, len, fd; 31 int major, minor, type, len, fd;
43 int mode = 0660; 32 int mode = 0660;
44 uid_t uid = 0; 33 uid_t uid = 0;
45 gid_t gid = 0; 34 gid_t gid = 0;
46 char *temp = path + strlen(path); 35 char *temp = path + strlen(path);
36 char *command = NULL;
47 37
48 /* Try to read major/minor string. Note that the kernel puts \n after 38 /* Try to read major/minor string. Note that the kernel puts \n after
49 * the data, so we don't need to worry about null terminating the string 39 * the data, so we don't need to worry about null terminating the string
@@ -69,7 +59,7 @@ static void make_device(char *path)
69 char *conf, *pos, *end; 59 char *conf, *pos, *end;
70 60
71 /* mmap the config file */ 61 /* mmap the config file */
72 if (-1 != (fd=open(MDEV_CONF,O_RDONLY))) { 62 if (-1 != (fd=open("/etc/mdev.conf",O_RDONLY))) {
73 len = lseek(fd, 0, SEEK_END); 63 len = lseek(fd, 0, SEEK_END);
74 conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); 64 conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
75 if (conf) { 65 if (conf) {
@@ -86,88 +76,89 @@ static void make_device(char *path)
86 ; 76 ;
87 77
88 /* Three fields: regex, uid:gid, mode */ 78 /* Three fields: regex, uid:gid, mode */
89 for (field=3; field; field--) { 79 for (field=0; field < (3 + ENABLE_FEATURE_MDEV_EXEC);
80 field++)
81 {
90 /* Skip whitespace */ 82 /* Skip whitespace */
91 while (pos<end && isspace(*pos)) 83 while (pos<end && isspace(*pos)) pos++;
92 pos++; 84 if (pos==end || *pos=='#') break;
93 if (pos==end || *pos=='#') 85 for (end2=pos;
94 break; 86 end2<end && !isspace(*end2) && *end2!='#'; end2++)
95 for (end2=pos; end2<end && !isspace(*end2) && *end2!='#'; end2++)
96 ; 87 ;
97 88
98 switch (field) { 89 if (!field) {
99 /* Regex to match this device */ 90 /* Regex to match this device */
100 case 3:
101 {
102 char *regex = strndupa(pos,end2-pos);
103 regex_t match;
104 regmatch_t off;
105 int result;
106
107 /* Is this it? */
108 xregcomp(&match,regex,REG_EXTENDED);
109 result = regexec(&match,device_name,1,&off,0);
110 regfree(&match);
111
112 /* If not this device, skip rest of line */
113 if (result || off.rm_so || off.rm_eo!=strlen(device_name))
114 goto end_line;
115 91
92 char *regex = strndupa(pos, end2-pos);
93 regex_t match;
94 regmatch_t off;
95 int result;
96
97 /* Is this it? */
98 xregcomp(&match,regex, REG_EXTENDED);
99 result = regexec(&match, device_name, 1, &off, 0);
100 regfree(&match);
101
102 /* If not this device, skip rest of line */
103 if (result || off.rm_so
104 || off.rm_eo != strlen(device_name))
116 break; 105 break;
117 } 106
107 } else if (field == 1) {
118 /* uid:gid */ 108 /* uid:gid */
119 case 2: 109
120 { 110 char *s, *s2;
121 char *s2; 111
122 112 /* Find : */
123 /* Find : */ 113 for(s=pos; s<end2 && *s!=':'; s++)
124 for(s=pos; s<end2 && *s!=':'; s++) 114 ;
125 ; 115 if (s == end2) break;
126 if (s == end2) 116
127 goto end_line; 117 /* Parse UID */
128 118 uid = strtoul(pos,&s2,10);
129 /* Parse UID */ 119 if (s != s2) {
130 uid = strtoul(pos,&s2,10); 120 struct passwd *pass;
131 if (s != s2) { 121 pass = getpwnam(strndupa(pos, s-pos));
132 struct passwd *pass; 122 if (!pass) break;
133 pass = getpwnam(strndupa(pos,s-pos)); 123 uid = pass->pw_uid;
134 if (!pass) 124 }
135 goto end_line; 125 s++;
136 uid = pass->pw_uid; 126 /* parse GID */
137 } 127 gid = strtoul(s, &s2, 10);
138 s++; 128 if (end2 != s2) {
139 /* parse GID */ 129 struct group *grp;
140 gid = strtoul(s,&s2,10); 130 grp = getgrnam(strndupa(s, end2-s));
141 if (end2 != s2) { 131 if (!grp) break;
142 struct group *grp; 132 gid = grp->gr_gid;
143 grp = getgrnam(strndupa(s,end2-s));
144 if (!grp)
145 goto end_line;
146 gid = grp->gr_gid;
147 }
148 break;
149 } 133 }
134 } else if (field == 2) {
150 /* mode */ 135 /* mode */
151 case 1: 136
152 { 137 mode = strtoul(pos, &pos, 8);
153 mode = strtoul(pos,&pos,8); 138 if (pos != end2) break;
154 if (pos != end2) 139 } else if (ENABLE_FEATURE_MDEV_EXEC && field == 3) {
155 goto end_line; 140 // Command to run
156 else 141 char *s = "@$*", *s2;
157 goto found_device; 142 if (!(s2 = strchr(s, *pos++))) {
143 // Force error
144 field = 1;
145 break;
158 } 146 }
147 if ((s2-s+1) & (1<<delete))
148 command = bb_xstrndup(pos, end-pos);
159 } 149 }
150
160 pos = end2; 151 pos = end2;
161 } 152 }
162end_line: 153
163 /* Did everything parse happily? */ 154 /* Did everything parse happily? */
164 if (field && field!=3) 155
165 bb_error_msg_and_die("Bad line %d",line); 156 if (field > 2) break;
157 if (field) bb_error_msg_and_die("Bad line %d",line);
166 158
167 /* Next line */ 159 /* Next line */
168 pos = ++end; 160 pos = ++end;
169 } 161 }
170found_device:
171 munmap(conf, len); 162 munmap(conf, len);
172 } 163 }
173 close(fd); 164 close(fd);
@@ -175,13 +166,29 @@ found_device:
175 } 166 }
176 167
177 umask(0); 168 umask(0);
178 if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST) 169 if (!delete) {
179 bb_perror_msg_and_die("mknod %s failed", device_name); 170 if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST)
171 bb_perror_msg_and_die("mknod %s failed", device_name);
180 172
181 if (major==bbg.root_major && minor==bbg.root_minor) 173 if (major == bbg.root_major && minor == bbg.root_minor)
182 symlink(device_name, "root"); 174 symlink(device_name, "root");
183 175
184 if (ENABLE_FEATURE_MDEV_CONF) chown(device_name, uid, gid); 176 if (ENABLE_FEATURE_MDEV_CONF) chown(device_name, uid, gid);
177 }
178 if (command) {
179 int rc;
180 char *s;
181
182 s=bb_xasprintf("MDEV=%s",device_name);
183 putenv(s);
184 rc = system(command);
185 s[4]=0;
186 putenv(s);
187 free(s);
188 free(command);
189 if (rc == -1) bb_perror_msg_and_die("Couldn't run %s", command);
190 }
191 if (delete) unlink(device_name);
185} 192}
186 193
187/* Recursive search of /sys/block or /sys/class. path must be a writeable 194/* Recursive search of /sys/block or /sys/class. path must be a writeable
@@ -212,7 +219,7 @@ static void find_dev(char *path)
212 219
213 /* If there's a dev entry, mknod it */ 220 /* If there's a dev entry, mknod it */
214 221
215 if (!strcmp(entry->d_name, "dev")) make_device(path); 222 if (!strcmp(entry->d_name, "dev")) make_device(path, 0);
216 } 223 }
217 224
218 closedir(dir); 225 closedir(dir);
@@ -247,12 +254,9 @@ int mdev_main(int argc, char *argv[])
247 if (!action || !env_path) 254 if (!action || !env_path)
248 bb_show_usage(); 255 bb_show_usage();
249 256
250 if (!strcmp(action, "add")) { 257 sprintf(temp, "/sys%s", env_path);
251 sprintf(temp, "/sys%s", env_path); 258 if (!strcmp(action, "add")) make_device(temp,0);
252 make_device(temp); 259 else if (!strcmp(action, "remove")) make_device(temp,1);
253 } else if (!strcmp(action, "remove")) {
254 unlink(strrchr(env_path, '/') + 1);
255 }
256 } 260 }
257 261
258 if (ENABLE_FEATURE_CLEAN_UP) RELEASE_CONFIG_BUFFER(temp); 262 if (ENABLE_FEATURE_CLEAN_UP) RELEASE_CONFIG_BUFFER(temp);