diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-27 22:45:44 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-27 22:45:44 +0000 |
commit | cf26ab70c11416401cd53e6a6a5fb4d5c2583246 (patch) | |
tree | 70c34ca3fb93d01700181279a9875900c02abc08 /util-linux/mdev.c | |
parent | e1caabbb75ecf3537586aa3bbbe5921f3f1d43b7 (diff) | |
download | busybox-w32-cf26ab70c11416401cd53e6a6a5fb4d5c2583246.tar.gz busybox-w32-cf26ab70c11416401cd53e6a6a5fb4d5c2583246.tar.bz2 busybox-w32-cf26ab70c11416401cd53e6a6a5fb4d5c2583246.zip |
mdev: plug a few memory and fd leaks; simplify code a bit
Diffstat (limited to 'util-linux/mdev.c')
-rw-r--r-- | util-linux/mdev.c | 140 |
1 files changed, 69 insertions, 71 deletions
diff --git a/util-linux/mdev.c b/util-linux/mdev.c index 5a8f1d918..f86ce14ce 100644 --- a/util-linux/mdev.c +++ b/util-linux/mdev.c | |||
@@ -21,7 +21,11 @@ struct globals { | |||
21 | 21 | ||
22 | #define MAX_SYSFS_DEPTH 3 /* prevent infinite loops in /sys symlinks */ | 22 | #define MAX_SYSFS_DEPTH 3 /* prevent infinite loops in /sys symlinks */ |
23 | 23 | ||
24 | /* We use additional 64+ bytes in make_device() */ | ||
25 | #define SCRATCH_SIZE 80 | ||
26 | |||
24 | /* 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" */ |
28 | /* NB: "mdev -s" may call us many times, do not leak memory/fds! */ | ||
25 | static void make_device(char *path, int delete) | 29 | static void make_device(char *path, int delete) |
26 | { | 30 | { |
27 | const char *device_name; | 31 | const char *device_name; |
@@ -29,7 +33,7 @@ static void make_device(char *path, int delete) | |||
29 | int mode = 0660; | 33 | int mode = 0660; |
30 | uid_t uid = 0; | 34 | uid_t uid = 0; |
31 | gid_t gid = 0; | 35 | gid_t gid = 0; |
32 | char *temp = path + strlen(path); | 36 | char *dev_maj_min = path + strlen(path); |
33 | char *command = NULL; | 37 | char *command = NULL; |
34 | char *alias = NULL; | 38 | char *alias = NULL; |
35 | 39 | ||
@@ -42,21 +46,20 @@ static void make_device(char *path, int delete) | |||
42 | * also depend on path having writeable space after it. | 46 | * also depend on path having writeable space after it. |
43 | */ | 47 | */ |
44 | if (!delete) { | 48 | if (!delete) { |
45 | strcat(path, "/dev"); | 49 | strcpy(dev_maj_min, "/dev"); |
46 | len = open_read_close(path, temp + 1, 64); | 50 | len = open_read_close(path, dev_maj_min + 1, 64); |
47 | *temp++ = 0; | 51 | *dev_maj_min++ = '\0'; |
48 | if (len < 1) { | 52 | if (len < 1) { |
49 | if (ENABLE_FEATURE_MDEV_EXEC) | 53 | if (!ENABLE_FEATURE_MDEV_EXEC) |
50 | /* no "dev" file, so just try to run script */ | ||
51 | *temp = 0; | ||
52 | else | ||
53 | return; | 54 | return; |
55 | /* no "dev" file, so just try to run script */ | ||
56 | *dev_maj_min = '\0'; | ||
54 | } | 57 | } |
55 | } | 58 | } |
56 | 59 | ||
57 | /* Determine device name, type, major and minor */ | 60 | /* Determine device name, type, major and minor */ |
58 | device_name = bb_basename(path); | 61 | device_name = bb_basename(path); |
59 | type = (path[5] == 'c' ? S_IFCHR : S_IFBLK); | 62 | type = (path[5] == 'c' ? S_IFCHR : S_IFBLK); /* "/sys/[c]lass"? */ |
60 | 63 | ||
61 | if (ENABLE_FEATURE_MDEV_CONF) { | 64 | if (ENABLE_FEATURE_MDEV_CONF) { |
62 | FILE *fp; | 65 | FILE *fp; |
@@ -70,15 +73,18 @@ static void make_device(char *path, int delete) | |||
70 | 73 | ||
71 | while ((vline = line = xmalloc_fgetline(fp)) != NULL) { | 74 | while ((vline = line = xmalloc_fgetline(fp)) != NULL) { |
72 | int field; | 75 | int field; |
73 | |||
74 | /* A pristine copy for command execution. */ | ||
75 | char *orig_line; | 76 | char *orig_line; |
77 | |||
76 | if (ENABLE_FEATURE_MDEV_EXEC) | 78 | if (ENABLE_FEATURE_MDEV_EXEC) |
77 | orig_line = xstrdup(line); | 79 | orig_line = xstrdup(line); /* pristine copy for command execution. */ |
78 | 80 | ||
79 | ++lineno; | 81 | ++lineno; |
80 | 82 | ||
81 | /* Three fields: regex, uid:gid, mode */ | 83 | // TODO: get rid of loop, |
84 | // just linear skip_whitespace() style code will do! | ||
85 | // (will also get rid of orig_line). | ||
86 | |||
87 | /* Fields: regex uid:gid mode [alias] [cmd] */ | ||
82 | for (field = 0; field < (3 + ENABLE_FEATURE_MDEV_RENAME + ENABLE_FEATURE_MDEV_EXEC); ++field) { | 88 | for (field = 0; field < (3 + ENABLE_FEATURE_MDEV_RENAME + ENABLE_FEATURE_MDEV_EXEC); ++field) { |
83 | 89 | ||
84 | /* Find a non-empty field */ | 90 | /* Find a non-empty field */ |
@@ -117,9 +123,9 @@ static void make_device(char *path, int delete) | |||
117 | struct group *grp; | 123 | struct group *grp; |
118 | 124 | ||
119 | char *str_uid = val; | 125 | char *str_uid = val; |
120 | char *str_gid = strchr(val, ':'); | 126 | char *str_gid = strchrnul(val, ':'); |
121 | if (str_gid) | 127 | if (*str_gid) |
122 | *str_gid = '\0', ++str_gid; | 128 | *str_gid++ = '\0'; |
123 | 129 | ||
124 | /* Parse UID */ | 130 | /* Parse UID */ |
125 | pass = getpwnam(str_uid); | 131 | pass = getpwnam(str_uid); |
@@ -128,7 +134,7 @@ static void make_device(char *path, int delete) | |||
128 | else | 134 | else |
129 | uid = strtoul(str_uid, NULL, 10); | 135 | uid = strtoul(str_uid, NULL, 10); |
130 | 136 | ||
131 | /* parse GID */ | 137 | /* Parse GID */ |
132 | grp = getgrnam(str_gid); | 138 | grp = getgrnam(str_gid); |
133 | if (grp) | 139 | if (grp) |
134 | gid = grp->gr_gid; | 140 | gid = grp->gr_gid; |
@@ -144,8 +150,10 @@ static void make_device(char *path, int delete) | |||
144 | 150 | ||
145 | if (*val != '>') | 151 | if (*val != '>') |
146 | ++field; | 152 | ++field; |
147 | else | 153 | else { |
154 | free(alias); /* don't leak in case we matched it on prev line */ | ||
148 | alias = xstrdup(val + 1); | 155 | alias = xstrdup(val + 1); |
156 | } | ||
149 | 157 | ||
150 | } | 158 | } |
151 | 159 | ||
@@ -162,12 +170,17 @@ static void make_device(char *path, int delete) | |||
162 | } | 170 | } |
163 | 171 | ||
164 | /* Correlate the position in the "@$*" with the delete | 172 | /* Correlate the position in the "@$*" with the delete |
165 | * step so that we get the proper behavior. | 173 | * step so that we get the proper behavior: |
174 | * @cmd: run on create | ||
175 | * $cmd: run on delete | ||
176 | * *cmd: run on both | ||
166 | */ | 177 | */ |
167 | if ((s2 - s + 1) & (1 << delete)) | 178 | if ((s2 - s + 1) /*1/2/3*/ & /*1/2*/ (1 + delete)) { |
179 | free(command); /* don't leak in case we matched it on prev line */ | ||
168 | command = xstrdup(orig_line + (val + 1 - line)); | 180 | command = xstrdup(orig_line + (val + 1 - line)); |
181 | } | ||
169 | } | 182 | } |
170 | } | 183 | } /* end of "for every field" */ |
171 | 184 | ||
172 | /* Did everything parse happily? */ | 185 | /* Did everything parse happily? */ |
173 | if (field <= 2) | 186 | if (field <= 2) |
@@ -177,21 +190,13 @@ static void make_device(char *path, int delete) | |||
177 | free(line); | 190 | free(line); |
178 | if (ENABLE_FEATURE_MDEV_EXEC) | 191 | if (ENABLE_FEATURE_MDEV_EXEC) |
179 | free(orig_line); | 192 | free(orig_line); |
180 | } | 193 | } /* end of "while line is read from /etc/mdev.conf" */ |
181 | |||
182 | if (ENABLE_FEATURE_CLEAN_UP) | ||
183 | fclose(fp); | ||
184 | 194 | ||
185 | end_parse: /* nothing */ ; | 195 | fclose(fp); |
186 | } | 196 | } |
197 | end_parse: | ||
187 | 198 | ||
188 | if (!delete) { | 199 | if (!delete && sscanf(dev_maj_min, "%u:%u", &major, &minor) == 2) { |
189 | if (sscanf(temp, "%d:%d", &major, &minor) != 2) { | ||
190 | if (ENABLE_FEATURE_MDEV_EXEC) | ||
191 | goto skip_creation; | ||
192 | else | ||
193 | return; | ||
194 | } | ||
195 | 200 | ||
196 | if (ENABLE_FEATURE_MDEV_RENAME) | 201 | if (ENABLE_FEATURE_MDEV_RENAME) |
197 | unlink(device_name); | 202 | unlink(device_name); |
@@ -208,39 +213,39 @@ static void make_device(char *path, int delete) | |||
208 | if (ENABLE_FEATURE_MDEV_RENAME && alias) { | 213 | if (ENABLE_FEATURE_MDEV_RENAME && alias) { |
209 | char *dest; | 214 | char *dest; |
210 | 215 | ||
211 | temp = strrchr(alias, '/'); | 216 | dest = strrchr(alias, '/'); |
212 | if (temp) { | 217 | if (dest) { |
213 | if (temp[1] != '\0') | 218 | if (dest[1] != '\0') |
214 | /* given a file name, so rename it */ | 219 | /* given a file name, so rename it */ |
215 | *temp = '\0'; | 220 | *dest = '\0'; |
216 | bb_make_directory(alias, 0755, FILEUTILS_RECUR); | 221 | bb_make_directory(alias, 0755, FILEUTILS_RECUR); |
217 | dest = concat_path_file(alias, device_name); | 222 | dest = concat_path_file(alias, device_name); |
223 | free(alias); | ||
218 | } else | 224 | } else |
219 | dest = alias; | 225 | dest = alias; |
220 | 226 | ||
221 | rename(device_name, dest); // TODO: xrename? | 227 | rename(device_name, dest); |
222 | symlink(dest, device_name); | 228 | symlink(dest, device_name); |
223 | 229 | ||
224 | if (alias != dest) | ||
225 | free(alias); | ||
226 | free(dest); | 230 | free(dest); |
227 | } | 231 | } |
228 | } | 232 | } |
229 | skip_creation: /* nothing */ ; | ||
230 | } | 233 | } |
234 | |||
231 | if (ENABLE_FEATURE_MDEV_EXEC && command) { | 235 | if (ENABLE_FEATURE_MDEV_EXEC && command) { |
232 | /* setenv will leak memory, so use putenv */ | 236 | /* setenv will leak memory, use putenv/unsetenv/free */ |
233 | char *s = xasprintf("MDEV=%s", device_name); | 237 | char *s = xasprintf("MDEV=%s", device_name); |
234 | putenv(s); | 238 | putenv(s); |
235 | if (system(command) == -1) | 239 | if (system(command) == -1) |
236 | bb_perror_msg_and_die("cannot run %s", command); | 240 | bb_perror_msg_and_die("can't run '%s'", command); |
237 | s[4] = '\0'; | 241 | s[4] = '\0'; |
238 | unsetenv(s); | 242 | unsetenv(s); |
239 | free(s); | 243 | free(s); |
240 | free(command); | 244 | free(command); |
241 | } | 245 | } |
246 | |||
242 | if (delete) | 247 | if (delete) |
243 | remove_file(device_name, FILEUTILS_FORCE); | 248 | unlink(device_name); |
244 | } | 249 | } |
245 | 250 | ||
246 | /* File callback for /sys/ traversal */ | 251 | /* File callback for /sys/ traversal */ |
@@ -249,14 +254,15 @@ static int fileAction(const char *fileName, | |||
249 | void *userData, | 254 | void *userData, |
250 | int depth ATTRIBUTE_UNUSED) | 255 | int depth ATTRIBUTE_UNUSED) |
251 | { | 256 | { |
252 | size_t len = strlen(fileName) - 4; | 257 | size_t len = strlen(fileName) - 4; /* can't underflow */ |
253 | char *scratch = userData; | 258 | char *scratch = userData; |
254 | 259 | ||
255 | if (strcmp(fileName + len, "/dev")) | 260 | /* len check is for paranoid reasons */ |
261 | if (strcmp(fileName + len, "/dev") || len >= PATH_MAX) | ||
256 | return FALSE; | 262 | return FALSE; |
257 | 263 | ||
258 | strcpy(scratch, fileName); | 264 | strcpy(scratch, fileName); |
259 | scratch[len] = 0; | 265 | scratch[len] = '\0'; |
260 | make_device(scratch, 0); | 266 | make_device(scratch, 0); |
261 | 267 | ||
262 | return TRUE; | 268 | return TRUE; |
@@ -287,12 +293,6 @@ static void load_firmware(const char *const firmware, const char *const sysfs_pa | |||
287 | int cnt; | 293 | int cnt; |
288 | int firmware_fd, loading_fd, data_fd; | 294 | int firmware_fd, loading_fd, data_fd; |
289 | 295 | ||
290 | /* check for $FIRMWARE from kernel */ | ||
291 | /* XXX: dont bother: open(NULL) works same as open("no-such-file") | ||
292 | * if (!firmware) | ||
293 | * return; | ||
294 | */ | ||
295 | |||
296 | /* check for /lib/firmware/$FIRMWARE */ | 296 | /* check for /lib/firmware/$FIRMWARE */ |
297 | xchdir("/lib/firmware"); | 297 | xchdir("/lib/firmware"); |
298 | firmware_fd = xopen(firmware, O_RDONLY); | 298 | firmware_fd = xopen(firmware, O_RDONLY); |
@@ -304,16 +304,15 @@ static void load_firmware(const char *const firmware, const char *const sysfs_pa | |||
304 | xchdir(sysfs_path); | 304 | xchdir(sysfs_path); |
305 | for (cnt = 0; cnt < 30; ++cnt) { | 305 | for (cnt = 0; cnt < 30; ++cnt) { |
306 | loading_fd = open("loading", O_WRONLY); | 306 | loading_fd = open("loading", O_WRONLY); |
307 | if (loading_fd == -1) | 307 | if (loading_fd != -1) |
308 | sleep(1); | 308 | goto loading; |
309 | else | 309 | sleep(1); |
310 | break; | ||
311 | } | 310 | } |
312 | if (loading_fd == -1) | 311 | goto out; |
313 | goto out; | ||
314 | 312 | ||
313 | loading: | ||
315 | /* tell kernel we're loading by `echo 1 > /sys/$DEVPATH/loading` */ | 314 | /* tell kernel we're loading by `echo 1 > /sys/$DEVPATH/loading` */ |
316 | if (write(loading_fd, "1", 1) != 1) | 315 | if (full_write(loading_fd, "1", 1) != 1) |
317 | goto out; | 316 | goto out; |
318 | 317 | ||
319 | /* load firmware by `cat /lib/firmware/$FIRMWARE > /sys/$DEVPATH/data */ | 318 | /* load firmware by `cat /lib/firmware/$FIRMWARE > /sys/$DEVPATH/data */ |
@@ -324,9 +323,9 @@ static void load_firmware(const char *const firmware, const char *const sysfs_pa | |||
324 | 323 | ||
325 | /* tell kernel result by `echo [0|-1] > /sys/$DEVPATH/loading` */ | 324 | /* tell kernel result by `echo [0|-1] > /sys/$DEVPATH/loading` */ |
326 | if (cnt > 0) | 325 | if (cnt > 0) |
327 | write(loading_fd, "0", 1); | 326 | full_write(loading_fd, "0", 1); |
328 | else | 327 | else |
329 | write(loading_fd, "-1", 2); | 328 | full_write(loading_fd, "-1", 2); |
330 | 329 | ||
331 | out: | 330 | out: |
332 | if (ENABLE_FEATURE_CLEAN_UP) { | 331 | if (ENABLE_FEATURE_CLEAN_UP) { |
@@ -341,16 +340,14 @@ int mdev_main(int argc, char **argv) | |||
341 | { | 340 | { |
342 | char *action; | 341 | char *action; |
343 | char *env_path; | 342 | char *env_path; |
344 | RESERVE_CONFIG_BUFFER(temp,PATH_MAX); | 343 | RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE); |
345 | 344 | ||
346 | xchdir("/dev"); | 345 | xchdir("/dev"); |
347 | 346 | ||
348 | if (argc == 2 && !strcmp(argv[1],"-s")) { | 347 | if (argc == 2 && !strcmp(argv[1], "-s")) { |
349 | |||
350 | /* Scan: | 348 | /* Scan: |
351 | * mdev -s | 349 | * mdev -s |
352 | */ | 350 | */ |
353 | |||
354 | struct stat st; | 351 | struct stat st; |
355 | 352 | ||
356 | xstat("/", &st); | 353 | xstat("/", &st); |
@@ -366,26 +363,27 @@ int mdev_main(int argc, char **argv) | |||
366 | fileAction, dirAction, temp, 0); | 363 | fileAction, dirAction, temp, 0); |
367 | 364 | ||
368 | } else { | 365 | } else { |
369 | |||
370 | /* Hotplug: | 366 | /* Hotplug: |
371 | * env ACTION=... DEVPATH=... mdev | 367 | * env ACTION=... DEVPATH=... mdev |
372 | * ACTION can be "add" or "remove" | 368 | * ACTION can be "add" or "remove" |
373 | * DEVPATH is like "/block/sda" or "/class/input/mice" | 369 | * DEVPATH is like "/block/sda" or "/class/input/mice" |
374 | */ | 370 | */ |
375 | |||
376 | action = getenv("ACTION"); | 371 | action = getenv("ACTION"); |
377 | env_path = getenv("DEVPATH"); | 372 | env_path = getenv("DEVPATH"); |
378 | if (!action || !env_path) | 373 | if (!action || !env_path) |
379 | bb_show_usage(); | 374 | bb_show_usage(); |
380 | 375 | ||
381 | sprintf(temp, "/sys%s", env_path); | 376 | snprintf(temp, PATH_MAX, "/sys%s", env_path); |
382 | if (!strcmp(action, "remove")) | 377 | if (!strcmp(action, "remove")) |
383 | make_device(temp, 1); | 378 | make_device(temp, 1); |
384 | else if (!strcmp(action, "add")) { | 379 | else if (!strcmp(action, "add")) { |
385 | make_device(temp, 0); | 380 | make_device(temp, 0); |
386 | 381 | ||
387 | if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) | 382 | if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) { |
388 | load_firmware(getenv("FIRMWARE"), temp); | 383 | char *fw = getenv("FIRMWARE"); |
384 | if (fw) | ||
385 | load_firmware(fw, temp); | ||
386 | } | ||
389 | } | 387 | } |
390 | } | 388 | } |
391 | 389 | ||