diff options
author | Ron Yorston <rmy@pobox.com> | 2012-03-22 15:21:20 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2012-03-22 15:21:20 +0000 |
commit | 0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2 (patch) | |
tree | 6709ddd6071a9c238ba69233540bbcfe560c6a44 | |
parent | 67758035a4fe040c6ac69b39d61bcd6bddd7b827 (diff) | |
parent | 56a3b82e9692a25ef9c9269e88feac0d579ce8e8 (diff) | |
download | busybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.tar.gz busybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.tar.bz2 busybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.zip |
Merge commit '56a3b82e9692a25ef9c9269e88feac0d579ce8e8' into merge
Conflicts:
coreutils/ls.c
include/platform.h
libbb/bb_basename.c
97 files changed, 3706 insertions, 2309 deletions
diff --git a/.gitignore b/.gitignore index 7d2cca67c..0a0c65bc3 100644 --- a/.gitignore +++ b/.gitignore | |||
@@ -18,6 +18,7 @@ Config.in | |||
18 | # Normal output | 18 | # Normal output |
19 | # | 19 | # |
20 | /busybox | 20 | /busybox |
21 | /busybox_old | ||
21 | /busybox_unstripped* | 22 | /busybox_unstripped* |
22 | 23 | ||
23 | # | 24 | # |
@@ -271,8 +271,9 @@ config UNICODE_PRESERVE_BROKEN | |||
271 | default n | 271 | default n |
272 | depends on UNICODE_SUPPORT | 272 | depends on UNICODE_SUPPORT |
273 | help | 273 | help |
274 | With this option on, invalid UTF-8 bytes are not substituted | 274 | With this option on, on line-editing input (such as used by shells) |
275 | with the selected substitution character. | 275 | invalid UTF-8 bytes are not substituted with the selected |
276 | substitution character. | ||
276 | For example, this means that entering 'l', 's', ' ', 0xff, [Enter] | 277 | For example, this means that entering 'l', 's', ' ', 0xff, [Enter] |
277 | at shell prompt will list file named 0xff (single char name | 278 | at shell prompt will list file named 0xff (single char name |
278 | with char value 255), not file named '?'. | 279 | with char value 255), not file named '?'. |
@@ -365,14 +366,24 @@ config FEATURE_SUID | |||
365 | 366 | ||
366 | config FEATURE_SUID_CONFIG | 367 | config FEATURE_SUID_CONFIG |
367 | bool "Runtime SUID/SGID configuration via /etc/busybox.conf" | 368 | bool "Runtime SUID/SGID configuration via /etc/busybox.conf" |
368 | default y if FEATURE_SUID | 369 | default y |
369 | depends on FEATURE_SUID | 370 | depends on FEATURE_SUID |
370 | help | 371 | help |
371 | Allow the SUID / SGID state of an applet to be determined at runtime | 372 | Allow the SUID / SGID state of an applet to be determined at runtime |
372 | by checking /etc/busybox.conf. (This is sort of a poor man's sudo.) | 373 | by checking /etc/busybox.conf. (This is sort of a poor man's sudo.) |
373 | The format of this file is as follows: | 374 | The format of this file is as follows: |
374 | 375 | ||
375 | <applet> = [Ssx-][Ssx-][x-] (<username>|<uid>).(<groupname>|<gid>) | 376 | APPLET = [Ssx-][Ssx-][x-] [USER.GROUP] |
377 | |||
378 | s: USER or GROUP is allowed to execute APPLET. | ||
379 | APPLET will run under USER or GROUP | ||
380 | (reagardless of who's running it). | ||
381 | S: USER or GROUP is NOT allowed to execute APPLET. | ||
382 | APPLET will run under USER or GROUP. | ||
383 | This option is not very sensical. | ||
384 | x: USER/GROUP/others are allowed to execute APPLET. | ||
385 | No UID/GID change will be done when it is run. | ||
386 | -: USER/GROUP/others are not allowed to execute APPLET. | ||
376 | 387 | ||
377 | An example might help: | 388 | An example might help: |
378 | 389 | ||
@@ -382,7 +393,8 @@ config FEATURE_SUID_CONFIG | |||
382 | su = ssx # exactly the same | 393 | su = ssx # exactly the same |
383 | 394 | ||
384 | mount = sx- root.disk # applet mount can be run by root and members | 395 | mount = sx- root.disk # applet mount can be run by root and members |
385 | # of group disk and runs with euid=0 | 396 | # of group disk (but not anyone else) |
397 | # and runs with euid=0 (egid is not changed) | ||
386 | 398 | ||
387 | cp = --- # disable applet cp for everyone | 399 | cp = --- # disable applet cp for everyone |
388 | 400 | ||
diff --git a/Makefile.flags b/Makefile.flags index c7a805877..513b6ebb2 100644 --- a/Makefile.flags +++ b/Makefile.flags | |||
@@ -58,14 +58,14 @@ CFLAGS += $(call cc-option,-falign-functions=1 -falign-jumps=1 -falign-labels=1 | |||
58 | #CFLAGS += $(call cc-option,-Wconversion,) | 58 | #CFLAGS += $(call cc-option,-Wconversion,) |
59 | 59 | ||
60 | ifneq ($(CONFIG_DEBUG),y) | 60 | ifneq ($(CONFIG_DEBUG),y) |
61 | CFLAGS += $(call cc-option,-Os,) | 61 | CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,)) |
62 | else | 62 | else |
63 | CFLAGS += $(call cc-option,-g,) | 63 | CFLAGS += $(call cc-option,-g,) |
64 | #CFLAGS += "-D_FORTIFY_SOURCE=2" | 64 | #CFLAGS += "-D_FORTIFY_SOURCE=2" |
65 | ifeq ($(CONFIG_DEBUG_PESSIMIZE),y) | 65 | ifeq ($(CONFIG_DEBUG_PESSIMIZE),y) |
66 | CFLAGS += $(call cc-option,-O0,) | 66 | CFLAGS += $(call cc-option,-O0,) |
67 | else | 67 | else |
68 | CFLAGS += $(call cc-option,-Os,) | 68 | CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,)) |
69 | endif | 69 | endif |
70 | endif | 70 | endif |
71 | 71 | ||
diff --git a/archival/dpkg.c b/archival/dpkg.c index f8e349d09..8899d1719 100644 --- a/archival/dpkg.c +++ b/archival/dpkg.c | |||
@@ -702,28 +702,21 @@ static unsigned get_status(const unsigned status_node, const int num) | |||
702 | 702 | ||
703 | static void set_status(const unsigned status_node_num, const char *new_value, const int position) | 703 | static void set_status(const unsigned status_node_num, const char *new_value, const int position) |
704 | { | 704 | { |
705 | const unsigned new_value_len = strlen(new_value); | ||
706 | const unsigned new_value_num = search_name_hashtable(new_value); | 705 | const unsigned new_value_num = search_name_hashtable(new_value); |
707 | unsigned want = get_status(status_node_num, 1); | 706 | unsigned want = get_status(status_node_num, 1); |
708 | unsigned flag = get_status(status_node_num, 2); | 707 | unsigned flag = get_status(status_node_num, 2); |
709 | unsigned status = get_status(status_node_num, 3); | 708 | unsigned status = get_status(status_node_num, 3); |
710 | int want_len = strlen(name_hashtable[want]); | ||
711 | int flag_len = strlen(name_hashtable[flag]); | ||
712 | int status_len = strlen(name_hashtable[status]); | ||
713 | char *new_status; | 709 | char *new_status; |
714 | 710 | ||
715 | switch (position) { | 711 | switch (position) { |
716 | case 1: | 712 | case 1: |
717 | want = new_value_num; | 713 | want = new_value_num; |
718 | want_len = new_value_len; | ||
719 | break; | 714 | break; |
720 | case 2: | 715 | case 2: |
721 | flag = new_value_num; | 716 | flag = new_value_num; |
722 | flag_len = new_value_len; | ||
723 | break; | 717 | break; |
724 | case 3: | 718 | case 3: |
725 | status = new_value_num; | 719 | status = new_value_num; |
726 | status_len = new_value_len; | ||
727 | break; | 720 | break; |
728 | default: | 721 | default: |
729 | bb_error_msg_and_die("DEBUG ONLY: this shouldnt happen"); | 722 | bb_error_msg_and_die("DEBUG ONLY: this shouldnt happen"); |
diff --git a/archival/libarchive/bz/compress.c b/archival/libarchive/bz/compress.c index 6f1c70a08..f93671742 100644 --- a/archival/libarchive/bz/compress.c +++ b/archival/libarchive/bz/compress.c | |||
@@ -251,7 +251,7 @@ void sendMTFValues(EState* s) | |||
251 | { | 251 | { |
252 | int32_t v, t, i, j, gs, ge, totc, bt, bc, iter; | 252 | int32_t v, t, i, j, gs, ge, totc, bt, bc, iter; |
253 | int32_t nSelectors, alphaSize, minLen, maxLen, selCtr; | 253 | int32_t nSelectors, alphaSize, minLen, maxLen, selCtr; |
254 | int32_t nGroups, nBytes; | 254 | int32_t nGroups; |
255 | 255 | ||
256 | /* | 256 | /* |
257 | * uint8_t len[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; | 257 | * uint8_t len[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; |
@@ -512,7 +512,6 @@ void sendMTFValues(EState* s) | |||
512 | } | 512 | } |
513 | } | 513 | } |
514 | 514 | ||
515 | nBytes = s->numZ; | ||
516 | bsW(s, 16, inUse16); | 515 | bsW(s, 16, inUse16); |
517 | 516 | ||
518 | inUse16 <<= (sizeof(int)*8 - 16); /* move 15th bit into sign bit */ | 517 | inUse16 <<= (sizeof(int)*8 - 16); /* move 15th bit into sign bit */ |
@@ -528,7 +527,6 @@ void sendMTFValues(EState* s) | |||
528 | } | 527 | } |
529 | 528 | ||
530 | /*--- Now the selectors. ---*/ | 529 | /*--- Now the selectors. ---*/ |
531 | nBytes = s->numZ; | ||
532 | bsW(s, 3, nGroups); | 530 | bsW(s, 3, nGroups); |
533 | bsW(s, 15, nSelectors); | 531 | bsW(s, 15, nSelectors); |
534 | for (i = 0; i < nSelectors; i++) { | 532 | for (i = 0; i < nSelectors; i++) { |
@@ -538,8 +536,6 @@ void sendMTFValues(EState* s) | |||
538 | } | 536 | } |
539 | 537 | ||
540 | /*--- Now the coding tables. ---*/ | 538 | /*--- Now the coding tables. ---*/ |
541 | nBytes = s->numZ; | ||
542 | |||
543 | for (t = 0; t < nGroups; t++) { | 539 | for (t = 0; t < nGroups; t++) { |
544 | int32_t curr = s->len[t][0]; | 540 | int32_t curr = s->len[t][0]; |
545 | bsW(s, 5, curr); | 541 | bsW(s, 5, curr); |
@@ -551,7 +547,6 @@ void sendMTFValues(EState* s) | |||
551 | } | 547 | } |
552 | 548 | ||
553 | /*--- And finally, the block data proper ---*/ | 549 | /*--- And finally, the block data proper ---*/ |
554 | nBytes = s->numZ; | ||
555 | selCtr = 0; | 550 | selCtr = 0; |
556 | gs = 0; | 551 | gs = 0; |
557 | while (1) { | 552 | while (1) { |
diff --git a/archival/tar.c b/archival/tar.c index 52f3c364c..fca2d068b 100644 --- a/archival/tar.c +++ b/archival/tar.c | |||
@@ -264,7 +264,8 @@ static int writeTarHeader(struct TarBallInfo *tbInfo, | |||
264 | PUT_OCTAL(header.uid, statbuf->st_uid); | 264 | PUT_OCTAL(header.uid, statbuf->st_uid); |
265 | PUT_OCTAL(header.gid, statbuf->st_gid); | 265 | PUT_OCTAL(header.gid, statbuf->st_gid); |
266 | memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */ | 266 | memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */ |
267 | PUT_OCTAL(header.mtime, statbuf->st_mtime); | 267 | /* users report that files with negative st_mtime cause trouble, so: */ |
268 | PUT_OCTAL(header.mtime, statbuf->st_mtime >= 0 ? statbuf->st_mtime : 0); | ||
268 | 269 | ||
269 | /* Enter the user and group names */ | 270 | /* Enter the user and group names */ |
270 | safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname)); | 271 | safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname)); |
@@ -316,15 +317,42 @@ static int writeTarHeader(struct TarBallInfo *tbInfo, | |||
316 | } else if (S_ISFIFO(statbuf->st_mode)) { | 317 | } else if (S_ISFIFO(statbuf->st_mode)) { |
317 | header.typeflag = FIFOTYPE; | 318 | header.typeflag = FIFOTYPE; |
318 | } else if (S_ISREG(statbuf->st_mode)) { | 319 | } else if (S_ISREG(statbuf->st_mode)) { |
319 | if (sizeof(statbuf->st_size) > 4 | 320 | /* header.size field is 12 bytes long */ |
320 | && statbuf->st_size > (off_t)0777777777777LL | 321 | /* Does octal-encoded size fit? */ |
322 | uoff_t filesize = statbuf->st_size; | ||
323 | if (sizeof(filesize) <= 4 | ||
324 | || filesize <= (uoff_t)0777777777777LL | ||
321 | ) { | 325 | ) { |
326 | PUT_OCTAL(header.size, filesize); | ||
327 | } | ||
328 | /* Does base256-encoded size fit? | ||
329 | * It always does unless off_t is wider than 64 bits. | ||
330 | */ | ||
331 | else if (ENABLE_FEATURE_TAR_GNU_EXTENSIONS | ||
332 | #if ULLONG_MAX > 0xffffffffffffffffLL /* 2^64-1 */ | ||
333 | && (filesize <= 0x3fffffffffffffffffffffffLL) | ||
334 | #endif | ||
335 | ) { | ||
336 | /* GNU tar uses "base-256 encoding" for very large numbers. | ||
337 | * Encoding is binary, with highest bit always set as a marker | ||
338 | * and sign in next-highest bit: | ||
339 | * 80 00 .. 00 - zero | ||
340 | * bf ff .. ff - largest positive number | ||
341 | * ff ff .. ff - minus 1 | ||
342 | * c0 00 .. 00 - smallest negative number | ||
343 | */ | ||
344 | char *p8 = header.size + sizeof(header.size); | ||
345 | do { | ||
346 | *--p8 = (uint8_t)filesize; | ||
347 | filesize >>= 8; | ||
348 | } while (p8 != header.size); | ||
349 | *p8 |= 0x80; | ||
350 | } else { | ||
322 | bb_error_msg_and_die("can't store file '%s' " | 351 | bb_error_msg_and_die("can't store file '%s' " |
323 | "of size %"OFF_FMT"u, aborting", | 352 | "of size %"OFF_FMT"u, aborting", |
324 | fileName, statbuf->st_size); | 353 | fileName, statbuf->st_size); |
325 | } | 354 | } |
326 | header.typeflag = REGTYPE; | 355 | header.typeflag = REGTYPE; |
327 | PUT_OCTAL(header.size, statbuf->st_size); | ||
328 | } else { | 356 | } else { |
329 | bb_error_msg("%s: unknown file type", fileName); | 357 | bb_error_msg("%s: unknown file type", fileName); |
330 | return FALSE; | 358 | return FALSE; |
diff --git a/console-tools/setconsole.c b/console-tools/setconsole.c index 59c83361c..771974ae3 100644 --- a/console-tools/setconsole.c +++ b/console-tools/setconsole.c | |||
@@ -41,6 +41,6 @@ int setconsole_main(int argc UNUSED_PARAM, char **argv) | |||
41 | device = DEV_CONSOLE; | 41 | device = DEV_CONSOLE; |
42 | } | 42 | } |
43 | 43 | ||
44 | xioctl(xopen(device, O_RDONLY), TIOCCONS, NULL); | 44 | xioctl(xopen(device, O_WRONLY), TIOCCONS, NULL); |
45 | return EXIT_SUCCESS; | 45 | return EXIT_SUCCESS; |
46 | } | 46 | } |
diff --git a/coreutils/ls.c b/coreutils/ls.c index 09b9f101a..c967fd462 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c | |||
@@ -39,7 +39,7 @@ | |||
39 | //usage: IF_FEATURE_LS_SORTFILES("rSXv") | 39 | //usage: IF_FEATURE_LS_SORTFILES("rSXv") |
40 | //usage: IF_FEATURE_LS_TIMESTAMPS("ctu") | 40 | //usage: IF_FEATURE_LS_TIMESTAMPS("ctu") |
41 | //usage: IF_SELINUX("kKZ") "]" | 41 | //usage: IF_SELINUX("kKZ") "]" |
42 | //usage: IF_FEATURE_AUTOWIDTH(" -w WIDTH") " [FILE]..." | 42 | //usage: IF_FEATURE_AUTOWIDTH(" [-w WIDTH]") " [FILE]..." |
43 | //usage:#define ls_full_usage "\n\n" | 43 | //usage:#define ls_full_usage "\n\n" |
44 | //usage: "List directory contents\n" | 44 | //usage: "List directory contents\n" |
45 | //usage: "\nOptions:" | 45 | //usage: "\nOptions:" |
@@ -51,14 +51,14 @@ | |||
51 | //usage: "\n -d List directory entries instead of contents" | 51 | //usage: "\n -d List directory entries instead of contents" |
52 | //usage: IF_FEATURE_LS_FOLLOWLINKS( | 52 | //usage: IF_FEATURE_LS_FOLLOWLINKS( |
53 | //usage: "\n -L Follow symlinks" | 53 | //usage: "\n -L Follow symlinks" |
54 | //usage: "\n -H Follow symlinks on command line only" | 54 | //usage: "\n -H Follow symlinks on command line" |
55 | //usage: ) | 55 | //usage: ) |
56 | //usage: IF_FEATURE_LS_RECURSIVE( | 56 | //usage: IF_FEATURE_LS_RECURSIVE( |
57 | //usage: "\n -R Recurse" | 57 | //usage: "\n -R Recurse" |
58 | //usage: ) | 58 | //usage: ) |
59 | //usage: IF_FEATURE_LS_FILETYPES( | 59 | //usage: IF_FEATURE_LS_FILETYPES( |
60 | //usage: "\n -p Append / to dir entries" | ||
60 | //usage: "\n -F Append indicator (one of */=@|) to entries" | 61 | //usage: "\n -F Append indicator (one of */=@|) to entries" |
61 | //usage: "\n -p Append indicator (one of /=@|) to entries" | ||
62 | //usage: ) | 62 | //usage: ) |
63 | //usage: "\n -l Long listing format" | 63 | //usage: "\n -l Long listing format" |
64 | //usage: "\n -i List inode numbers" | 64 | //usage: "\n -i List inode numbers" |
@@ -118,11 +118,11 @@ | |||
118 | enum { | 118 | enum { |
119 | TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ | 119 | TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ |
120 | 120 | ||
121 | SPLIT_DIR = 1, | ||
122 | SPLIT_FILE = 0, | 121 | SPLIT_FILE = 0, |
122 | SPLIT_DIR = 1, | ||
123 | SPLIT_SUBDIR = 2, | 123 | SPLIT_SUBDIR = 2, |
124 | 124 | ||
125 | /* Bits in all_fmt: */ | 125 | /* Bits in G.all_fmt: */ |
126 | 126 | ||
127 | /* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ | 127 | /* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ |
128 | /* what file information will be listed */ | 128 | /* what file information will be listed */ |
@@ -137,9 +137,9 @@ LIST_SIZE = 1 << 7, | |||
137 | LIST_DATE_TIME = 1 << 8, | 137 | LIST_DATE_TIME = 1 << 8, |
138 | LIST_FULLTIME = 1 << 9, | 138 | LIST_FULLTIME = 1 << 9, |
139 | LIST_SYMLINK = 1 << 10, | 139 | LIST_SYMLINK = 1 << 10, |
140 | LIST_FILETYPE = 1 << 11, | 140 | LIST_FILETYPE = 1 << 11, /* show / suffix for dirs */ |
141 | LIST_EXEC = 1 << 12, | 141 | LIST_CLASSIFY = 1 << 12, /* requires LIST_FILETYPE, also show *,|,@,= suffixes */ |
142 | LIST_MASK = (LIST_EXEC << 1) - 1, | 142 | LIST_MASK = (LIST_CLASSIFY << 1) - 1, |
143 | 143 | ||
144 | /* what files will be displayed */ | 144 | /* what files will be displayed */ |
145 | DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */ | 145 | DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */ |
@@ -288,7 +288,7 @@ static const uint32_t opt_flags[] = { | |||
288 | SORT_VERSION, /* v */ | 288 | SORT_VERSION, /* v */ |
289 | #endif | 289 | #endif |
290 | #if ENABLE_FEATURE_LS_FILETYPES | 290 | #if ENABLE_FEATURE_LS_FILETYPES |
291 | LIST_FILETYPE | LIST_EXEC, /* F */ | 291 | LIST_FILETYPE | LIST_CLASSIFY, /* F */ |
292 | LIST_FILETYPE, /* p */ | 292 | LIST_FILETYPE, /* p */ |
293 | #endif | 293 | #endif |
294 | #if ENABLE_FEATURE_LS_RECURSIVE | 294 | #if ENABLE_FEATURE_LS_RECURSIVE |
@@ -304,25 +304,65 @@ static const uint32_t opt_flags[] = { | |||
304 | 304 | ||
305 | 305 | ||
306 | /* | 306 | /* |
307 | * a directory entry and its stat info are stored here | 307 | * a directory entry and its stat info |
308 | */ | 308 | */ |
309 | struct dnode { | 309 | struct dnode { |
310 | const char *name; /* the dir entry name */ | 310 | const char *name; /* usually basename, but think "ls -l dir/file" */ |
311 | const char *fullname; /* the dir entry name */ | 311 | const char *fullname; /* full name (usable for stat etc) */ |
312 | struct dnode *next; /* point at the next node */ | 312 | struct dnode *dn_next; /* for linked list */ |
313 | smallint fname_allocated; | ||
314 | struct stat dstat; /* the file stat info */ | ||
315 | IF_SELINUX(security_context_t sid;) | 313 | IF_SELINUX(security_context_t sid;) |
314 | smallint fname_allocated; | ||
315 | |||
316 | /* Used to avoid re-doing [l]stat at printout stage | ||
317 | * if we already collected needed data in scan stage: | ||
318 | */ | ||
319 | mode_t dn_mode_lstat; /* obtained with lstat, or 0 */ | ||
320 | mode_t dn_mode_stat; /* obtained with stat, or 0 */ | ||
321 | |||
322 | // struct stat dstat; | ||
323 | // struct stat is huge. We don't need it in full. | ||
324 | // At least we don't need st_dev and st_blksize, | ||
325 | // but there are invisible fields as well | ||
326 | // (such as nanosecond-resolution timespamps) | ||
327 | // and padding, which we also don't want to store. | ||
328 | // We also can pre-parse dev_t dn_rdev (in glibc, it's huge). | ||
329 | // On 32-bit uclibc: dnode size went from 112 to 84 bytes. | ||
330 | // | ||
331 | /* Same names as in struct stat, but with dn_ instead of st_ pfx: */ | ||
332 | mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */ | ||
333 | off_t dn_size; | ||
334 | #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES | ||
335 | time_t dn_atime; | ||
336 | time_t dn_mtime; | ||
337 | time_t dn_ctime; | ||
338 | #endif | ||
339 | ino_t dn_ino; | ||
340 | #if !ENABLE_PLATFORM_MINGW32 | ||
341 | blkcnt_t dn_blocks; | ||
342 | #endif | ||
343 | nlink_t dn_nlink; | ||
344 | uid_t dn_uid; | ||
345 | gid_t dn_gid; | ||
346 | int dn_rdev_maj; | ||
347 | int dn_rdev_min; | ||
348 | // dev_t dn_dev; | ||
349 | // blksize_t dn_blksize; | ||
316 | }; | 350 | }; |
317 | 351 | ||
318 | struct globals { | 352 | struct globals { |
319 | #if ENABLE_FEATURE_LS_COLOR | 353 | #if ENABLE_FEATURE_LS_COLOR |
320 | smallint show_color; | 354 | smallint show_color; |
355 | # define G_show_color (G.show_color) | ||
356 | #else | ||
357 | # define G_show_color 0 | ||
321 | #endif | 358 | #endif |
322 | smallint exit_code; | 359 | smallint exit_code; |
323 | unsigned all_fmt; | 360 | unsigned all_fmt; |
324 | #if ENABLE_FEATURE_AUTOWIDTH | 361 | #if ENABLE_FEATURE_AUTOWIDTH |
325 | unsigned terminal_width; // = TERMINAL_WIDTH; | 362 | unsigned terminal_width; |
363 | # define G_terminal_width (G.terminal_width) | ||
364 | #else | ||
365 | # define G_terminal_width TERMINAL_WIDTH | ||
326 | #endif | 366 | #endif |
327 | #if ENABLE_FEATURE_LS_TIMESTAMPS | 367 | #if ENABLE_FEATURE_LS_TIMESTAMPS |
328 | /* Do time() just once. Saves one syscall per file for "ls -l" */ | 368 | /* Do time() just once. Saves one syscall per file for "ls -l" */ |
@@ -330,74 +370,24 @@ struct globals { | |||
330 | #endif | 370 | #endif |
331 | } FIX_ALIASING; | 371 | } FIX_ALIASING; |
332 | #define G (*(struct globals*)&bb_common_bufsiz1) | 372 | #define G (*(struct globals*)&bb_common_bufsiz1) |
333 | #if ENABLE_FEATURE_LS_COLOR | ||
334 | # define show_color (G.show_color ) | ||
335 | #else | ||
336 | enum { show_color = 0 }; | ||
337 | #endif | ||
338 | #define exit_code (G.exit_code ) | ||
339 | #define all_fmt (G.all_fmt ) | ||
340 | #if ENABLE_FEATURE_AUTOWIDTH | ||
341 | # define terminal_width (G.terminal_width) | ||
342 | #else | ||
343 | enum { | ||
344 | terminal_width = TERMINAL_WIDTH, | ||
345 | }; | ||
346 | #endif | ||
347 | #define current_time_t (G.current_time_t) | ||
348 | #define INIT_G() do { \ | 373 | #define INIT_G() do { \ |
349 | /* we have to zero it out because of NOEXEC */ \ | 374 | /* we have to zero it out because of NOEXEC */ \ |
350 | memset(&G, 0, sizeof(G)); \ | 375 | memset(&G, 0, sizeof(G)); \ |
351 | IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \ | 376 | IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \ |
352 | IF_FEATURE_LS_TIMESTAMPS(time(¤t_time_t);) \ | 377 | IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \ |
353 | } while (0) | 378 | } while (0) |
354 | 379 | ||
355 | 380 | ||
356 | static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) | 381 | /*** Output code ***/ |
357 | { | ||
358 | struct stat dstat; | ||
359 | struct dnode *cur; | ||
360 | IF_SELINUX(security_context_t sid = NULL;) | ||
361 | 382 | ||
362 | if ((option_mask32 & OPT_L) || force_follow) { | ||
363 | #if ENABLE_SELINUX | ||
364 | if (is_selinux_enabled()) { | ||
365 | getfilecon(fullname, &sid); | ||
366 | } | ||
367 | #endif | ||
368 | if (stat(fullname, &dstat)) { | ||
369 | bb_simple_perror_msg(fullname); | ||
370 | exit_code = EXIT_FAILURE; | ||
371 | return 0; | ||
372 | } | ||
373 | } else { | ||
374 | #if ENABLE_SELINUX | ||
375 | if (is_selinux_enabled()) { | ||
376 | lgetfilecon(fullname, &sid); | ||
377 | } | ||
378 | #endif | ||
379 | if (lstat(fullname, &dstat)) { | ||
380 | bb_simple_perror_msg(fullname); | ||
381 | exit_code = EXIT_FAILURE; | ||
382 | return 0; | ||
383 | } | ||
384 | } | ||
385 | |||
386 | cur = xmalloc(sizeof(*cur)); | ||
387 | cur->fullname = fullname; | ||
388 | cur->name = name; | ||
389 | cur->dstat = dstat; | ||
390 | IF_SELINUX(cur->sid = sid;) | ||
391 | return cur; | ||
392 | } | ||
393 | 383 | ||
394 | /* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket | 384 | /* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket |
395 | * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file | 385 | * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file |
396 | * 3/7:multiplexed char/block device) | 386 | * 3/7:multiplexed char/block device) |
397 | * and we use 0 for unknown and 15 for executables (see below) */ | 387 | * and we use 0 for unknown and 15 for executables (see below) */ |
398 | #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) | 388 | #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) |
399 | #define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) | 389 | /* un fi chr - dir - blk - file - link - sock - - exe */ |
400 | #define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)]) | 390 | #define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)]) |
401 | /* 036 black foreground 050 black background | 391 | /* 036 black foreground 050 black background |
402 | 037 red foreground 051 red background | 392 | 037 red foreground 051 red background |
403 | 040 green foreground 052 green background | 393 | 040 green foreground 052 green background |
@@ -408,7 +398,7 @@ static struct dnode *my_stat(const char *fullname, const char *name, int force_f | |||
408 | 045 gray foreground 057 white background | 398 | 045 gray foreground 057 white background |
409 | */ | 399 | */ |
410 | #define COLOR(mode) ( \ | 400 | #define COLOR(mode) ( \ |
411 | /*un fi chr dir blk file link sock exe */ \ | 401 | /*un fi chr - dir - blk - file - link - sock - - exe */ \ |
412 | "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \ | 402 | "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \ |
413 | [TYPEINDEX(mode)]) | 403 | [TYPEINDEX(mode)]) |
414 | /* Select normal (0) [actually "reset all"] or bold (1) | 404 | /* Select normal (0) [actually "reset all"] or bold (1) |
@@ -417,7 +407,7 @@ static struct dnode *my_stat(const char *fullname, const char *name, int force_f | |||
417 | * Note: coreutils 6.9 uses inverted red for setuid binaries. | 407 | * Note: coreutils 6.9 uses inverted red for setuid binaries. |
418 | */ | 408 | */ |
419 | #define ATTR(mode) ( \ | 409 | #define ATTR(mode) ( \ |
420 | /*un fi chr dir blk file link sock exe */ \ | 410 | /*un fi chr - dir - blk - file- link- sock- - exe */ \ |
421 | "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \ | 411 | "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \ |
422 | [TYPEINDEX(mode)]) | 412 | [TYPEINDEX(mode)]) |
423 | 413 | ||
@@ -437,14 +427,14 @@ static char bold(mode_t mode) | |||
437 | } | 427 | } |
438 | #endif | 428 | #endif |
439 | 429 | ||
440 | #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR | 430 | #if ENABLE_FEATURE_LS_FILETYPES |
441 | static char append_char(mode_t mode) | 431 | static char append_char(mode_t mode) |
442 | { | 432 | { |
443 | if (!(all_fmt & LIST_FILETYPE)) | 433 | if (!(G.all_fmt & LIST_FILETYPE)) |
444 | return '\0'; | 434 | return '\0'; |
445 | if (S_ISDIR(mode)) | 435 | if (S_ISDIR(mode)) |
446 | return '/'; | 436 | return '/'; |
447 | if (!(all_fmt & LIST_EXEC)) | 437 | if (!(G.all_fmt & LIST_CLASSIFY)) |
448 | return '\0'; | 438 | return '\0'; |
449 | if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) | 439 | if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) |
450 | return '*'; | 440 | return '*'; |
@@ -452,149 +442,6 @@ static char append_char(mode_t mode) | |||
452 | } | 442 | } |
453 | #endif | 443 | #endif |
454 | 444 | ||
455 | static unsigned count_dirs(struct dnode **dn, int which) | ||
456 | { | ||
457 | unsigned dirs, all; | ||
458 | |||
459 | if (!dn) | ||
460 | return 0; | ||
461 | |||
462 | dirs = all = 0; | ||
463 | for (; *dn; dn++) { | ||
464 | const char *name; | ||
465 | |||
466 | all++; | ||
467 | if (!S_ISDIR((*dn)->dstat.st_mode)) | ||
468 | continue; | ||
469 | name = (*dn)->name; | ||
470 | if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */ | ||
471 | /* or if it's not . or .. */ | ||
472 | || name[0] != '.' || (name[1] && (name[1] != '.' || name[2])) | ||
473 | ) { | ||
474 | dirs++; | ||
475 | } | ||
476 | } | ||
477 | return which != SPLIT_FILE ? dirs : all - dirs; | ||
478 | } | ||
479 | |||
480 | /* get memory to hold an array of pointers */ | ||
481 | static struct dnode **dnalloc(unsigned num) | ||
482 | { | ||
483 | if (num < 1) | ||
484 | return NULL; | ||
485 | |||
486 | num++; /* so that we have terminating NULL */ | ||
487 | return xzalloc(num * sizeof(struct dnode *)); | ||
488 | } | ||
489 | |||
490 | #if ENABLE_FEATURE_LS_RECURSIVE | ||
491 | static void dfree(struct dnode **dnp) | ||
492 | { | ||
493 | unsigned i; | ||
494 | |||
495 | if (dnp == NULL) | ||
496 | return; | ||
497 | |||
498 | for (i = 0; dnp[i]; i++) { | ||
499 | struct dnode *cur = dnp[i]; | ||
500 | if (cur->fname_allocated) | ||
501 | free((char*)cur->fullname); | ||
502 | free(cur); | ||
503 | } | ||
504 | free(dnp); | ||
505 | } | ||
506 | #else | ||
507 | #define dfree(...) ((void)0) | ||
508 | #endif | ||
509 | |||
510 | /* Returns NULL-terminated malloced vector of pointers (or NULL) */ | ||
511 | static struct dnode **splitdnarray(struct dnode **dn, int which) | ||
512 | { | ||
513 | unsigned dncnt, d; | ||
514 | struct dnode **dnp; | ||
515 | |||
516 | if (dn == NULL) | ||
517 | return NULL; | ||
518 | |||
519 | /* count how many dirs or files there are */ | ||
520 | dncnt = count_dirs(dn, which); | ||
521 | |||
522 | /* allocate a file array and a dir array */ | ||
523 | dnp = dnalloc(dncnt); | ||
524 | |||
525 | /* copy the entrys into the file or dir array */ | ||
526 | for (d = 0; *dn; dn++) { | ||
527 | if (S_ISDIR((*dn)->dstat.st_mode)) { | ||
528 | const char *name; | ||
529 | |||
530 | if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) | ||
531 | continue; | ||
532 | name = (*dn)->name; | ||
533 | if ((which & SPLIT_DIR) | ||
534 | || name[0]!='.' || (name[1] && (name[1]!='.' || name[2])) | ||
535 | ) { | ||
536 | dnp[d++] = *dn; | ||
537 | } | ||
538 | } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) { | ||
539 | dnp[d++] = *dn; | ||
540 | } | ||
541 | } | ||
542 | return dnp; | ||
543 | } | ||
544 | |||
545 | #if ENABLE_FEATURE_LS_SORTFILES | ||
546 | static int sortcmp(const void *a, const void *b) | ||
547 | { | ||
548 | struct dnode *d1 = *(struct dnode **)a; | ||
549 | struct dnode *d2 = *(struct dnode **)b; | ||
550 | unsigned sort_opts = all_fmt & SORT_MASK; | ||
551 | off_t dif; | ||
552 | |||
553 | dif = 0; /* assume SORT_NAME */ | ||
554 | // TODO: use pre-initialized function pointer | ||
555 | // instead of branch forest | ||
556 | if (sort_opts == SORT_SIZE) { | ||
557 | dif = (d2->dstat.st_size - d1->dstat.st_size); | ||
558 | } else if (sort_opts == SORT_ATIME) { | ||
559 | dif = (d2->dstat.st_atime - d1->dstat.st_atime); | ||
560 | } else if (sort_opts == SORT_CTIME) { | ||
561 | dif = (d2->dstat.st_ctime - d1->dstat.st_ctime); | ||
562 | } else if (sort_opts == SORT_MTIME) { | ||
563 | dif = (d2->dstat.st_mtime - d1->dstat.st_mtime); | ||
564 | } else if (sort_opts == SORT_DIR) { | ||
565 | dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode); | ||
566 | /* } else if (sort_opts == SORT_VERSION) { */ | ||
567 | /* } else if (sort_opts == SORT_EXT) { */ | ||
568 | } | ||
569 | if (dif == 0) { | ||
570 | /* sort by name, or tie_breaker for other sorts */ | ||
571 | if (ENABLE_LOCALE_SUPPORT) | ||
572 | dif = strcoll(d1->name, d2->name); | ||
573 | else | ||
574 | dif = strcmp(d1->name, d2->name); | ||
575 | } | ||
576 | |||
577 | /* Make dif fit into an int */ | ||
578 | if (sizeof(dif) > sizeof(int)) { | ||
579 | enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) }; | ||
580 | /* shift leaving only "int" worth of bits */ | ||
581 | if (dif != 0) { | ||
582 | dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT); | ||
583 | } | ||
584 | } | ||
585 | |||
586 | return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif; | ||
587 | } | ||
588 | |||
589 | static void dnsort(struct dnode **dn, int size) | ||
590 | { | ||
591 | qsort(dn, size, sizeof(*dn), sortcmp); | ||
592 | } | ||
593 | #else | ||
594 | #define dnsort(dn, size) ((void)0) | ||
595 | #endif | ||
596 | |||
597 | |||
598 | static unsigned calc_name_len(const char *name) | 445 | static unsigned calc_name_len(const char *name) |
599 | { | 446 | { |
600 | unsigned len; | 447 | unsigned len; |
@@ -617,7 +464,6 @@ static unsigned calc_name_len(const char *name) | |||
617 | return len; | 464 | return len; |
618 | } | 465 | } |
619 | 466 | ||
620 | |||
621 | /* Return the number of used columns. | 467 | /* Return the number of used columns. |
622 | * Note that only STYLE_COLUMNAR uses return value. | 468 | * Note that only STYLE_COLUMNAR uses return value. |
623 | * STYLE_SINGLE and STYLE_LONG don't care. | 469 | * STYLE_SINGLE and STYLE_LONG don't care. |
@@ -656,98 +502,94 @@ static unsigned print_name(const char *name) | |||
656 | * Note that only STYLE_COLUMNAR uses return value, | 502 | * Note that only STYLE_COLUMNAR uses return value, |
657 | * STYLE_SINGLE and STYLE_LONG don't care. | 503 | * STYLE_SINGLE and STYLE_LONG don't care. |
658 | */ | 504 | */ |
659 | static NOINLINE unsigned list_single(const struct dnode *dn) | 505 | static NOINLINE unsigned display_single(const struct dnode *dn) |
660 | { | 506 | { |
661 | unsigned column = 0; | 507 | unsigned column = 0; |
662 | char *lpath = lpath; /* for compiler */ | 508 | char *lpath; |
663 | #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR | 509 | #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR |
664 | struct stat info; | 510 | struct stat statbuf; |
665 | char append; | 511 | char append; |
666 | #endif | 512 | #endif |
667 | 513 | ||
668 | /* Never happens: | ||
669 | if (dn->fullname == NULL) | ||
670 | return 0; | ||
671 | */ | ||
672 | |||
673 | #if ENABLE_FEATURE_LS_FILETYPES | 514 | #if ENABLE_FEATURE_LS_FILETYPES |
674 | append = append_char(dn->dstat.st_mode); | 515 | append = append_char(dn->dn_mode); |
675 | #endif | 516 | #endif |
676 | 517 | ||
677 | /* Do readlink early, so that if it fails, error message | 518 | /* Do readlink early, so that if it fails, error message |
678 | * does not appear *inside* the "ls -l" line */ | 519 | * does not appear *inside* the "ls -l" line */ |
679 | if (all_fmt & LIST_SYMLINK) | 520 | lpath = NULL; |
680 | if (S_ISLNK(dn->dstat.st_mode)) | 521 | if (G.all_fmt & LIST_SYMLINK) |
522 | if (S_ISLNK(dn->dn_mode)) | ||
681 | lpath = xmalloc_readlink_or_warn(dn->fullname); | 523 | lpath = xmalloc_readlink_or_warn(dn->fullname); |
682 | 524 | ||
683 | if (all_fmt & LIST_INO) | 525 | if (G.all_fmt & LIST_INO) |
684 | column += printf("%7llu ", (long long) dn->dstat.st_ino); | 526 | column += printf("%7llu ", (long long) dn->dn_ino); |
685 | //TODO: -h should affect -s too: | 527 | //TODO: -h should affect -s too: |
686 | if (all_fmt & LIST_BLOCKS) | 528 | if (G.all_fmt & LIST_BLOCKS) |
687 | #if ENABLE_PLATFORM_MINGW32 | 529 | #if ENABLE_PLATFORM_MINGW32 |
688 | /* MinGW does not have st_blocks */ | 530 | /* MinGW does not have st_blocks */ |
689 | column += printf("%6"OFF_FMT"u ", (off_t)0); | 531 | column += printf("%6"OFF_FMT"u ", (off_t)0); |
690 | #else | 532 | #else |
691 | column += printf("%6"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1)); | 533 | column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1)); |
692 | #endif | 534 | #endif |
693 | if (all_fmt & LIST_MODEBITS) | 535 | if (G.all_fmt & LIST_MODEBITS) |
694 | column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode)); | 536 | column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode)); |
695 | if (all_fmt & LIST_NLINKS) | 537 | if (G.all_fmt & LIST_NLINKS) |
696 | column += printf("%4lu ", (long) dn->dstat.st_nlink); | 538 | column += printf("%4lu ", (long) dn->dn_nlink); |
697 | if (all_fmt & LIST_ID_NUMERIC) { | 539 | if (G.all_fmt & LIST_ID_NUMERIC) { |
698 | if (option_mask32 & OPT_g) | 540 | if (option_mask32 & OPT_g) |
699 | column += printf("%-8u ", (int) dn->dstat.st_gid); | 541 | column += printf("%-8u ", (int) dn->dn_gid); |
700 | else | 542 | else |
701 | column += printf("%-8u %-8u ", | 543 | column += printf("%-8u %-8u ", |
702 | (int) dn->dstat.st_uid, | 544 | (int) dn->dn_uid, |
703 | (int) dn->dstat.st_gid); | 545 | (int) dn->dn_gid); |
704 | } | 546 | } |
705 | #if ENABLE_FEATURE_LS_USERNAME | 547 | #if ENABLE_FEATURE_LS_USERNAME |
706 | else if (all_fmt & LIST_ID_NAME) { | 548 | else if (G.all_fmt & LIST_ID_NAME) { |
707 | if (option_mask32 & OPT_g) { | 549 | if (option_mask32 & OPT_g) { |
708 | column += printf("%-8.8s ", | 550 | column += printf("%-8.8s ", |
709 | get_cached_groupname(dn->dstat.st_gid)); | 551 | get_cached_groupname(dn->dn_gid)); |
710 | } else { | 552 | } else { |
711 | column += printf("%-8.8s %-8.8s ", | 553 | column += printf("%-8.8s %-8.8s ", |
712 | get_cached_username(dn->dstat.st_uid), | 554 | get_cached_username(dn->dn_uid), |
713 | get_cached_groupname(dn->dstat.st_gid)); | 555 | get_cached_groupname(dn->dn_gid)); |
714 | } | 556 | } |
715 | } | 557 | } |
716 | #endif | 558 | #endif |
717 | if (all_fmt & LIST_SIZE) { | 559 | if (G.all_fmt & LIST_SIZE) { |
718 | if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { | 560 | if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) { |
719 | column += printf("%4u, %3u ", | 561 | column += printf("%4u, %3u ", |
720 | (int) major(dn->dstat.st_rdev), | 562 | dn->dn_rdev_maj, |
721 | (int) minor(dn->dstat.st_rdev)); | 563 | dn->dn_rdev_min); |
722 | } else { | 564 | } else { |
723 | if (option_mask32 & OPT_h) { | 565 | if (option_mask32 & OPT_h) { |
724 | column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ", | 566 | column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ", |
725 | /* print st_size, show one fractional, use suffixes */ | 567 | /* print size, show one fractional, use suffixes */ |
726 | make_human_readable_str(dn->dstat.st_size, 1, 0) | 568 | make_human_readable_str(dn->dn_size, 1, 0) |
727 | ); | 569 | ); |
728 | } else { | 570 | } else { |
729 | column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size); | 571 | column += printf("%9"OFF_FMT"u ", dn->dn_size); |
730 | } | 572 | } |
731 | } | 573 | } |
732 | } | 574 | } |
733 | #if ENABLE_FEATURE_LS_TIMESTAMPS | 575 | #if ENABLE_FEATURE_LS_TIMESTAMPS |
734 | if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) { | 576 | if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) { |
735 | char *filetime; | 577 | char *filetime; |
736 | time_t ttime = dn->dstat.st_mtime; | 578 | time_t ttime = dn->dn_mtime; |
737 | if (all_fmt & TIME_ACCESS) | 579 | if (G.all_fmt & TIME_ACCESS) |
738 | ttime = dn->dstat.st_atime; | 580 | ttime = dn->dn_atime; |
739 | if (all_fmt & TIME_CHANGE) | 581 | if (G.all_fmt & TIME_CHANGE) |
740 | ttime = dn->dstat.st_ctime; | 582 | ttime = dn->dn_ctime; |
741 | filetime = ctime(&ttime); | 583 | filetime = ctime(&ttime); |
742 | /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */ | 584 | /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */ |
743 | if (all_fmt & LIST_FULLTIME) { /* -e */ | 585 | if (G.all_fmt & LIST_FULLTIME) { /* -e */ |
744 | /* Note: coreutils 8.4 ls --full-time prints: | 586 | /* Note: coreutils 8.4 ls --full-time prints: |
745 | * 2009-07-13 17:49:27.000000000 +0200 | 587 | * 2009-07-13 17:49:27.000000000 +0200 |
746 | */ | 588 | */ |
747 | column += printf("%.24s ", filetime); | 589 | column += printf("%.24s ", filetime); |
748 | } else { /* LIST_DATE_TIME */ | 590 | } else { /* LIST_DATE_TIME */ |
749 | /* current_time_t ~== time(NULL) */ | 591 | /* G.current_time_t ~== time(NULL) */ |
750 | time_t age = current_time_t - ttime; | 592 | time_t age = G.current_time_t - ttime; |
751 | printf("%.6s ", filetime + 4); /* "Jun 30" */ | 593 | printf("%.6s ", filetime + 4); /* "Jun 30" */ |
752 | if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { | 594 | if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { |
753 | /* hh:mm if less than 6 months old */ | 595 | /* hh:mm if less than 6 months old */ |
@@ -760,51 +602,52 @@ static NOINLINE unsigned list_single(const struct dnode *dn) | |||
760 | } | 602 | } |
761 | #endif | 603 | #endif |
762 | #if ENABLE_SELINUX | 604 | #if ENABLE_SELINUX |
763 | if (all_fmt & LIST_CONTEXT) { | 605 | if (G.all_fmt & LIST_CONTEXT) { |
764 | column += printf("%-32s ", dn->sid ? dn->sid : "unknown"); | 606 | column += printf("%-32s ", dn->sid ? dn->sid : "unknown"); |
765 | freecon(dn->sid); | 607 | freecon(dn->sid); |
766 | } | 608 | } |
767 | #endif | 609 | #endif |
768 | 610 | ||
769 | #if ENABLE_FEATURE_LS_COLOR | 611 | #if ENABLE_FEATURE_LS_COLOR |
770 | if (show_color) { | 612 | if (G_show_color) { |
771 | info.st_mode = 0; /* for fgcolor() */ | 613 | mode_t mode = dn->dn_mode_lstat; |
772 | lstat(dn->fullname, &info); | 614 | if (!mode) |
773 | printf("\033[%u;%um", bold(info.st_mode), | 615 | if (lstat(dn->fullname, &statbuf) == 0) |
774 | fgcolor(info.st_mode)); | 616 | mode = statbuf.st_mode; |
617 | printf("\033[%u;%um", bold(mode), fgcolor(mode)); | ||
775 | } | 618 | } |
776 | #endif | 619 | #endif |
777 | column += print_name(dn->name); | 620 | column += print_name(dn->name); |
778 | if (show_color) { | 621 | if (G_show_color) { |
779 | printf("\033[0m"); | 622 | printf("\033[0m"); |
780 | } | 623 | } |
781 | 624 | ||
782 | if (all_fmt & LIST_SYMLINK) { | 625 | if (lpath) { |
783 | if (S_ISLNK(dn->dstat.st_mode) && lpath) { | 626 | printf(" -> "); |
784 | printf(" -> "); | ||
785 | #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR | 627 | #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR |
786 | #if ENABLE_FEATURE_LS_COLOR | 628 | if ((G.all_fmt & LIST_FILETYPE) || G_show_color) { |
787 | info.st_mode = 0; /* for fgcolor() */ | 629 | mode_t mode = dn->dn_mode_stat; |
788 | #endif | 630 | if (!mode) |
789 | if (stat(dn->fullname, &info) == 0) { | 631 | if (stat(dn->fullname, &statbuf) == 0) |
790 | append = append_char(info.st_mode); | 632 | mode = statbuf.st_mode; |
791 | } | 633 | # if ENABLE_FEATURE_LS_FILETYPES |
792 | #endif | 634 | append = append_char(mode); |
793 | #if ENABLE_FEATURE_LS_COLOR | 635 | # endif |
794 | if (show_color) { | 636 | # if ENABLE_FEATURE_LS_COLOR |
795 | printf("\033[%u;%um", bold(info.st_mode), | 637 | if (G_show_color) { |
796 | fgcolor(info.st_mode)); | 638 | printf("\033[%u;%um", bold(mode), fgcolor(mode)); |
797 | } | 639 | } |
640 | # endif | ||
641 | } | ||
798 | #endif | 642 | #endif |
799 | column += print_name(lpath) + 4; | 643 | column += print_name(lpath) + 4; |
800 | if (show_color) { | 644 | free(lpath); |
801 | printf("\033[0m"); | 645 | if (G_show_color) { |
802 | } | 646 | printf("\033[0m"); |
803 | free(lpath); | ||
804 | } | 647 | } |
805 | } | 648 | } |
806 | #if ENABLE_FEATURE_LS_FILETYPES | 649 | #if ENABLE_FEATURE_LS_FILETYPES |
807 | if (all_fmt & LIST_FILETYPE) { | 650 | if (G.all_fmt & LIST_FILETYPE) { |
808 | if (append) { | 651 | if (append) { |
809 | putchar(append); | 652 | putchar(append); |
810 | column++; | 653 | column++; |
@@ -815,14 +658,14 @@ static NOINLINE unsigned list_single(const struct dnode *dn) | |||
815 | return column; | 658 | return column; |
816 | } | 659 | } |
817 | 660 | ||
818 | static void showfiles(struct dnode **dn, unsigned nfiles) | 661 | static void display_files(struct dnode **dn, unsigned nfiles) |
819 | { | 662 | { |
820 | unsigned i, ncols, nrows, row, nc; | 663 | unsigned i, ncols, nrows, row, nc; |
821 | unsigned column; | 664 | unsigned column; |
822 | unsigned nexttab; | 665 | unsigned nexttab; |
823 | unsigned column_width = 0; /* used only by STYLE_COLUMNAR */ | 666 | unsigned column_width = 0; /* used only by STYLE_COLUMNAR */ |
824 | 667 | ||
825 | if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */ | 668 | if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */ |
826 | ncols = 1; | 669 | ncols = 1; |
827 | } else { | 670 | } else { |
828 | /* find the longest file name, use that as the column width */ | 671 | /* find the longest file name, use that as the column width */ |
@@ -832,10 +675,10 @@ static void showfiles(struct dnode **dn, unsigned nfiles) | |||
832 | column_width = len; | 675 | column_width = len; |
833 | } | 676 | } |
834 | column_width += 1 + | 677 | column_width += 1 + |
835 | IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + ) | 678 | IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + ) |
836 | ((all_fmt & LIST_INO) ? 8 : 0) + | 679 | ((G.all_fmt & LIST_INO) ? 8 : 0) + |
837 | ((all_fmt & LIST_BLOCKS) ? 5 : 0); | 680 | ((G.all_fmt & LIST_BLOCKS) ? 5 : 0); |
838 | ncols = (int) (terminal_width / column_width); | 681 | ncols = (unsigned)G_terminal_width / column_width; |
839 | } | 682 | } |
840 | 683 | ||
841 | if (ncols > 1) { | 684 | if (ncols > 1) { |
@@ -852,7 +695,7 @@ static void showfiles(struct dnode **dn, unsigned nfiles) | |||
852 | for (row = 0; row < nrows; row++) { | 695 | for (row = 0; row < nrows; row++) { |
853 | for (nc = 0; nc < ncols; nc++) { | 696 | for (nc = 0; nc < ncols; nc++) { |
854 | /* reach into the array based on the column and row */ | 697 | /* reach into the array based on the column and row */ |
855 | if (all_fmt & DISP_ROWS) | 698 | if (G.all_fmt & DISP_ROWS) |
856 | i = (row * ncols) + nc; /* display across row */ | 699 | i = (row * ncols) + nc; /* display across row */ |
857 | else | 700 | else |
858 | i = (nc * nrows) + row; /* display by column */ | 701 | i = (nc * nrows) + row; /* display by column */ |
@@ -863,7 +706,7 @@ static void showfiles(struct dnode **dn, unsigned nfiles) | |||
863 | column += nexttab + 1; | 706 | column += nexttab + 1; |
864 | } | 707 | } |
865 | nexttab = column + column_width; | 708 | nexttab = column + column_width; |
866 | column += list_single(dn[i]); | 709 | column += display_single(dn[i]); |
867 | } | 710 | } |
868 | } | 711 | } |
869 | putchar('\n'); | 712 | putchar('\n'); |
@@ -872,102 +715,233 @@ static void showfiles(struct dnode **dn, unsigned nfiles) | |||
872 | } | 715 | } |
873 | 716 | ||
874 | 717 | ||
875 | #if !ENABLE_PLATFORM_MINGW32 | 718 | /*** Dir scanning code ***/ |
876 | #if ENABLE_DESKTOP | 719 | |
877 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html | 720 | static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) |
878 | * If any of the -l, -n, -s options is specified, each list | ||
879 | * of files within the directory shall be preceded by a | ||
880 | * status line indicating the number of file system blocks | ||
881 | * occupied by files in the directory in 512-byte units if | ||
882 | * the -k option is not specified, or 1024-byte units if the | ||
883 | * -k option is specified, rounded up to the next integral | ||
884 | * number of units. | ||
885 | */ | ||
886 | /* by Jorgen Overgaard (jorgen AT antistaten.se) */ | ||
887 | static off_t calculate_blocks(struct dnode **dn) | ||
888 | { | 721 | { |
889 | uoff_t blocks = 1; | 722 | struct stat statbuf; |
890 | if (dn) { | 723 | struct dnode *cur; |
891 | while (*dn) { | 724 | |
892 | /* st_blocks is in 512 byte blocks */ | 725 | cur = xzalloc(sizeof(*cur)); |
893 | blocks += (*dn)->dstat.st_blocks; | 726 | cur->fullname = fullname; |
894 | dn++; | 727 | cur->name = name; |
728 | |||
729 | if ((option_mask32 & OPT_L) || force_follow) { | ||
730 | #if ENABLE_SELINUX | ||
731 | if (is_selinux_enabled()) { | ||
732 | getfilecon(fullname, &cur->sid); | ||
733 | } | ||
734 | #endif | ||
735 | if (stat(fullname, &statbuf)) { | ||
736 | bb_simple_perror_msg(fullname); | ||
737 | G.exit_code = EXIT_FAILURE; | ||
738 | free(cur); | ||
739 | return NULL; | ||
740 | } | ||
741 | cur->dn_mode_stat = statbuf.st_mode; | ||
742 | } else { | ||
743 | #if ENABLE_SELINUX | ||
744 | if (is_selinux_enabled()) { | ||
745 | lgetfilecon(fullname, &cur->sid); | ||
746 | } | ||
747 | #endif | ||
748 | if (lstat(fullname, &statbuf)) { | ||
749 | bb_simple_perror_msg(fullname); | ||
750 | G.exit_code = EXIT_FAILURE; | ||
751 | free(cur); | ||
752 | return NULL; | ||
895 | } | 753 | } |
754 | cur->dn_mode_lstat = statbuf.st_mode; | ||
896 | } | 755 | } |
897 | 756 | ||
898 | /* Even though standard says use 512 byte blocks, coreutils use 1k */ | 757 | /* cur->dstat = statbuf: */ |
899 | /* Actually, we round up by calculating (blocks + 1) / 2, | 758 | cur->dn_mode = statbuf.st_mode ; |
900 | * "+ 1" was done when we initialized blocks to 1 */ | 759 | cur->dn_size = statbuf.st_size ; |
901 | return blocks >> 1; | 760 | #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES |
902 | } | 761 | cur->dn_atime = statbuf.st_atime ; |
762 | cur->dn_mtime = statbuf.st_mtime ; | ||
763 | cur->dn_ctime = statbuf.st_ctime ; | ||
903 | #endif | 764 | #endif |
765 | cur->dn_ino = statbuf.st_ino ; | ||
766 | #if !ENABLE_PLATFORM_MINGW32 | ||
767 | cur->dn_blocks = statbuf.st_blocks; | ||
904 | #endif | 768 | #endif |
769 | cur->dn_nlink = statbuf.st_nlink ; | ||
770 | cur->dn_uid = statbuf.st_uid ; | ||
771 | cur->dn_gid = statbuf.st_gid ; | ||
772 | cur->dn_rdev_maj = major(statbuf.st_rdev); | ||
773 | cur->dn_rdev_min = minor(statbuf.st_rdev); | ||
905 | 774 | ||
775 | return cur; | ||
776 | } | ||
906 | 777 | ||
907 | static struct dnode **list_dir(const char *, unsigned *); | 778 | static unsigned count_dirs(struct dnode **dn, int which) |
908 | |||
909 | static void showdirs(struct dnode **dn, int first) | ||
910 | { | 779 | { |
911 | unsigned nfiles; | 780 | unsigned dirs, all; |
912 | unsigned dndirs; | ||
913 | struct dnode **subdnp; | ||
914 | struct dnode **dnd; | ||
915 | 781 | ||
782 | if (!dn) | ||
783 | return 0; | ||
784 | |||
785 | dirs = all = 0; | ||
916 | for (; *dn; dn++) { | 786 | for (; *dn; dn++) { |
917 | if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) { | 787 | const char *name; |
918 | if (!first) | 788 | |
919 | bb_putchar('\n'); | 789 | all++; |
920 | first = 0; | 790 | if (!S_ISDIR((*dn)->dn_mode)) |
921 | printf("%s:\n", (*dn)->fullname); | 791 | continue; |
792 | |||
793 | name = (*dn)->name; | ||
794 | if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */ | ||
795 | /* or if it's not . or .. */ | ||
796 | || name[0] != '.' | ||
797 | || (name[1] && (name[1] != '.' || name[2])) | ||
798 | ) { | ||
799 | dirs++; | ||
922 | } | 800 | } |
923 | subdnp = list_dir((*dn)->fullname, &nfiles); | 801 | } |
924 | #if !ENABLE_PLATFORM_MINGW32 | 802 | return which != SPLIT_FILE ? dirs : all - dirs; |
925 | #if ENABLE_DESKTOP | 803 | } |
926 | if ((all_fmt & STYLE_MASK) == STYLE_LONG) | 804 | |
927 | printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp)); | 805 | /* get memory to hold an array of pointers */ |
928 | #endif | 806 | static struct dnode **dnalloc(unsigned num) |
807 | { | ||
808 | if (num < 1) | ||
809 | return NULL; | ||
810 | |||
811 | num++; /* so that we have terminating NULL */ | ||
812 | return xzalloc(num * sizeof(struct dnode *)); | ||
813 | } | ||
814 | |||
815 | #if ENABLE_FEATURE_LS_RECURSIVE | ||
816 | static void dfree(struct dnode **dnp) | ||
817 | { | ||
818 | unsigned i; | ||
819 | |||
820 | if (dnp == NULL) | ||
821 | return; | ||
822 | |||
823 | for (i = 0; dnp[i]; i++) { | ||
824 | struct dnode *cur = dnp[i]; | ||
825 | if (cur->fname_allocated) | ||
826 | free((char*)cur->fullname); | ||
827 | free(cur); | ||
828 | } | ||
829 | free(dnp); | ||
830 | } | ||
831 | #else | ||
832 | #define dfree(...) ((void)0) | ||
929 | #endif | 833 | #endif |
930 | if (nfiles > 0) { | 834 | |
931 | /* list all files at this level */ | 835 | /* Returns NULL-terminated malloced vector of pointers (or NULL) */ |
932 | dnsort(subdnp, nfiles); | 836 | static struct dnode **splitdnarray(struct dnode **dn, int which) |
933 | showfiles(subdnp, nfiles); | 837 | { |
934 | if (ENABLE_FEATURE_LS_RECURSIVE | 838 | unsigned dncnt, d; |
935 | && (all_fmt & DISP_RECURSIVE) | 839 | struct dnode **dnp; |
840 | |||
841 | if (dn == NULL) | ||
842 | return NULL; | ||
843 | |||
844 | /* count how many dirs or files there are */ | ||
845 | dncnt = count_dirs(dn, which); | ||
846 | |||
847 | /* allocate a file array and a dir array */ | ||
848 | dnp = dnalloc(dncnt); | ||
849 | |||
850 | /* copy the entrys into the file or dir array */ | ||
851 | for (d = 0; *dn; dn++) { | ||
852 | if (S_ISDIR((*dn)->dn_mode)) { | ||
853 | const char *name; | ||
854 | |||
855 | if (which == SPLIT_FILE) | ||
856 | continue; | ||
857 | |||
858 | name = (*dn)->name; | ||
859 | if ((which & SPLIT_DIR) /* any dir... */ | ||
860 | /* ... or not . or .. */ | ||
861 | || name[0] != '.' | ||
862 | || (name[1] && (name[1] != '.' || name[2])) | ||
936 | ) { | 863 | ) { |
937 | /* recursive - list the sub-dirs */ | 864 | dnp[d++] = *dn; |
938 | dnd = splitdnarray(subdnp, SPLIT_SUBDIR); | ||
939 | dndirs = count_dirs(subdnp, SPLIT_SUBDIR); | ||
940 | if (dndirs > 0) { | ||
941 | dnsort(dnd, dndirs); | ||
942 | showdirs(dnd, 0); | ||
943 | /* free the array of dnode pointers to the dirs */ | ||
944 | free(dnd); | ||
945 | } | ||
946 | } | 865 | } |
947 | /* free the dnodes and the fullname mem */ | 866 | } else |
948 | dfree(subdnp); | 867 | if (which == SPLIT_FILE) { |
868 | dnp[d++] = *dn; | ||
949 | } | 869 | } |
950 | } | 870 | } |
871 | return dnp; | ||
951 | } | 872 | } |
952 | 873 | ||
874 | #if ENABLE_FEATURE_LS_SORTFILES | ||
875 | static int sortcmp(const void *a, const void *b) | ||
876 | { | ||
877 | struct dnode *d1 = *(struct dnode **)a; | ||
878 | struct dnode *d2 = *(struct dnode **)b; | ||
879 | unsigned sort_opts = G.all_fmt & SORT_MASK; | ||
880 | off_t dif; | ||
881 | |||
882 | dif = 0; /* assume SORT_NAME */ | ||
883 | // TODO: use pre-initialized function pointer | ||
884 | // instead of branch forest | ||
885 | if (sort_opts == SORT_SIZE) { | ||
886 | dif = (d2->dn_size - d1->dn_size); | ||
887 | } else if (sort_opts == SORT_ATIME) { | ||
888 | dif = (d2->dn_atime - d1->dn_atime); | ||
889 | } else if (sort_opts == SORT_CTIME) { | ||
890 | dif = (d2->dn_ctime - d1->dn_ctime); | ||
891 | } else if (sort_opts == SORT_MTIME) { | ||
892 | dif = (d2->dn_mtime - d1->dn_mtime); | ||
893 | } else if (sort_opts == SORT_DIR) { | ||
894 | dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode); | ||
895 | /* } else if (sort_opts == SORT_VERSION) { */ | ||
896 | /* } else if (sort_opts == SORT_EXT) { */ | ||
897 | } | ||
898 | if (dif == 0) { | ||
899 | /* sort by name, or tie_breaker for other sorts */ | ||
900 | if (ENABLE_LOCALE_SUPPORT) | ||
901 | dif = strcoll(d1->name, d2->name); | ||
902 | else | ||
903 | dif = strcmp(d1->name, d2->name); | ||
904 | } | ||
905 | |||
906 | /* Make dif fit into an int */ | ||
907 | if (sizeof(dif) > sizeof(int)) { | ||
908 | enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) }; | ||
909 | /* shift leaving only "int" worth of bits */ | ||
910 | if (dif != 0) { | ||
911 | dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT); | ||
912 | } | ||
913 | } | ||
914 | |||
915 | return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif; | ||
916 | } | ||
917 | |||
918 | static void dnsort(struct dnode **dn, int size) | ||
919 | { | ||
920 | qsort(dn, size, sizeof(*dn), sortcmp); | ||
921 | } | ||
922 | |||
923 | static void sort_and_display_files(struct dnode **dn, unsigned nfiles) | ||
924 | { | ||
925 | dnsort(dn, nfiles); | ||
926 | display_files(dn, nfiles); | ||
927 | } | ||
928 | #else | ||
929 | # define dnsort(dn, size) ((void)0) | ||
930 | # define sort_and_display_files(dn, nfiles) display_files(dn, nfiles) | ||
931 | #endif | ||
953 | 932 | ||
954 | /* Returns NULL-terminated malloced vector of pointers (or NULL) */ | 933 | /* Returns NULL-terminated malloced vector of pointers (or NULL) */ |
955 | static struct dnode **list_dir(const char *path, unsigned *nfiles_p) | 934 | static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p) |
956 | { | 935 | { |
957 | struct dnode *dn, *cur, **dnp; | 936 | struct dnode *dn, *cur, **dnp; |
958 | struct dirent *entry; | 937 | struct dirent *entry; |
959 | DIR *dir; | 938 | DIR *dir; |
960 | unsigned i, nfiles; | 939 | unsigned i, nfiles; |
961 | 940 | ||
962 | /* Never happens: | ||
963 | if (path == NULL) | ||
964 | return NULL; | ||
965 | */ | ||
966 | |||
967 | *nfiles_p = 0; | 941 | *nfiles_p = 0; |
968 | dir = warn_opendir(path); | 942 | dir = warn_opendir(path); |
969 | if (dir == NULL) { | 943 | if (dir == NULL) { |
970 | exit_code = EXIT_FAILURE; | 944 | G.exit_code = EXIT_FAILURE; |
971 | return NULL; /* could not open the dir */ | 945 | return NULL; /* could not open the dir */ |
972 | } | 946 | } |
973 | dn = NULL; | 947 | dn = NULL; |
@@ -978,11 +952,11 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p) | |||
978 | /* are we going to list the file- it may be . or .. or a hidden file */ | 952 | /* are we going to list the file- it may be . or .. or a hidden file */ |
979 | if (entry->d_name[0] == '.') { | 953 | if (entry->d_name[0] == '.') { |
980 | if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2])) | 954 | if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2])) |
981 | && !(all_fmt & DISP_DOT) | 955 | && !(G.all_fmt & DISP_DOT) |
982 | ) { | 956 | ) { |
983 | continue; | 957 | continue; |
984 | } | 958 | } |
985 | if (!(all_fmt & DISP_HIDDEN)) | 959 | if (!(G.all_fmt & DISP_HIDDEN)) |
986 | continue; | 960 | continue; |
987 | } | 961 | } |
988 | fullname = concat_path_file(path, entry->d_name); | 962 | fullname = concat_path_file(path, entry->d_name); |
@@ -992,7 +966,7 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p) | |||
992 | continue; | 966 | continue; |
993 | } | 967 | } |
994 | cur->fname_allocated = 1; | 968 | cur->fname_allocated = 1; |
995 | cur->next = dn; | 969 | cur->dn_next = dn; |
996 | dn = cur; | 970 | dn = cur; |
997 | nfiles++; | 971 | nfiles++; |
998 | } | 972 | } |
@@ -1008,7 +982,7 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p) | |||
1008 | dnp = dnalloc(nfiles); | 982 | dnp = dnalloc(nfiles); |
1009 | for (i = 0; /* i < nfiles - detected via !dn below */; i++) { | 983 | for (i = 0; /* i < nfiles - detected via !dn below */; i++) { |
1010 | dnp[i] = dn; /* save pointer to node in array */ | 984 | dnp[i] = dn; /* save pointer to node in array */ |
1011 | dn = dn->next; | 985 | dn = dn->dn_next; |
1012 | if (!dn) | 986 | if (!dn) |
1013 | break; | 987 | break; |
1014 | } | 988 | } |
@@ -1016,6 +990,77 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p) | |||
1016 | return dnp; | 990 | return dnp; |
1017 | } | 991 | } |
1018 | 992 | ||
993 | #if ENABLE_DESKTOP && !ENABLE_PLATFORM_MINGW32 | ||
994 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html | ||
995 | * If any of the -l, -n, -s options is specified, each list | ||
996 | * of files within the directory shall be preceded by a | ||
997 | * status line indicating the number of file system blocks | ||
998 | * occupied by files in the directory in 512-byte units if | ||
999 | * the -k option is not specified, or 1024-byte units if the | ||
1000 | * -k option is specified, rounded up to the next integral | ||
1001 | * number of units. | ||
1002 | */ | ||
1003 | /* by Jorgen Overgaard (jorgen AT antistaten.se) */ | ||
1004 | static off_t calculate_blocks(struct dnode **dn) | ||
1005 | { | ||
1006 | uoff_t blocks = 1; | ||
1007 | if (dn) { | ||
1008 | while (*dn) { | ||
1009 | /* st_blocks is in 512 byte blocks */ | ||
1010 | blocks += (*dn)->dn_blocks; | ||
1011 | dn++; | ||
1012 | } | ||
1013 | } | ||
1014 | |||
1015 | /* Even though standard says use 512 byte blocks, coreutils use 1k */ | ||
1016 | /* Actually, we round up by calculating (blocks + 1) / 2, | ||
1017 | * "+ 1" was done when we initialized blocks to 1 */ | ||
1018 | return blocks >> 1; | ||
1019 | } | ||
1020 | #endif | ||
1021 | |||
1022 | static void scan_and_display_dirs_recur(struct dnode **dn, int first) | ||
1023 | { | ||
1024 | unsigned nfiles; | ||
1025 | struct dnode **subdnp; | ||
1026 | |||
1027 | for (; *dn; dn++) { | ||
1028 | if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) { | ||
1029 | if (!first) | ||
1030 | bb_putchar('\n'); | ||
1031 | first = 0; | ||
1032 | printf("%s:\n", (*dn)->fullname); | ||
1033 | } | ||
1034 | subdnp = scan_one_dir((*dn)->fullname, &nfiles); | ||
1035 | #if ENABLE_DESKTOP && !ENABLE_PLATFORM_MINGW32 | ||
1036 | if ((G.all_fmt & STYLE_MASK) == STYLE_LONG) | ||
1037 | printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp)); | ||
1038 | #endif | ||
1039 | if (nfiles > 0) { | ||
1040 | /* list all files at this level */ | ||
1041 | sort_and_display_files(subdnp, nfiles); | ||
1042 | |||
1043 | if (ENABLE_FEATURE_LS_RECURSIVE | ||
1044 | && (G.all_fmt & DISP_RECURSIVE) | ||
1045 | ) { | ||
1046 | struct dnode **dnd; | ||
1047 | unsigned dndirs; | ||
1048 | /* recursive - list the sub-dirs */ | ||
1049 | dnd = splitdnarray(subdnp, SPLIT_SUBDIR); | ||
1050 | dndirs = count_dirs(subdnp, SPLIT_SUBDIR); | ||
1051 | if (dndirs > 0) { | ||
1052 | dnsort(dnd, dndirs); | ||
1053 | scan_and_display_dirs_recur(dnd, 0); | ||
1054 | /* free the array of dnode pointers to the dirs */ | ||
1055 | free(dnd); | ||
1056 | } | ||
1057 | } | ||
1058 | /* free the dnodes and the fullname mem */ | ||
1059 | dfree(subdnp); | ||
1060 | } | ||
1061 | } | ||
1062 | } | ||
1063 | |||
1019 | 1064 | ||
1020 | int ls_main(int argc UNUSED_PARAM, char **argv) | 1065 | int ls_main(int argc UNUSED_PARAM, char **argv) |
1021 | { | 1066 | { |
@@ -1054,13 +1099,13 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
1054 | init_unicode(); | 1099 | init_unicode(); |
1055 | 1100 | ||
1056 | if (ENABLE_FEATURE_LS_SORTFILES) | 1101 | if (ENABLE_FEATURE_LS_SORTFILES) |
1057 | all_fmt = SORT_NAME; | 1102 | G.all_fmt = SORT_NAME; |
1058 | 1103 | ||
1059 | #if ENABLE_FEATURE_AUTOWIDTH | 1104 | #if ENABLE_FEATURE_AUTOWIDTH |
1060 | /* obtain the terminal width */ | 1105 | /* obtain the terminal width */ |
1061 | get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL); | 1106 | get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL); |
1062 | /* go one less... */ | 1107 | /* go one less... */ |
1063 | terminal_width--; | 1108 | G_terminal_width--; |
1064 | #endif | 1109 | #endif |
1065 | 1110 | ||
1066 | /* process options */ | 1111 | /* process options */ |
@@ -1081,7 +1126,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
1081 | /* -w NUM: */ | 1126 | /* -w NUM: */ |
1082 | IF_FEATURE_AUTOWIDTH(":w+"); | 1127 | IF_FEATURE_AUTOWIDTH(":w+"); |
1083 | opt = getopt32(argv, ls_options | 1128 | opt = getopt32(argv, ls_options |
1084 | IF_FEATURE_AUTOWIDTH(, NULL, &terminal_width) | 1129 | IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width) |
1085 | IF_FEATURE_LS_COLOR(, &color_opt) | 1130 | IF_FEATURE_LS_COLOR(, &color_opt) |
1086 | ); | 1131 | ); |
1087 | for (i = 0; opt_flags[i] != (1U << 31); i++) { | 1132 | for (i = 0; opt_flags[i] != (1U << 31); i++) { |
@@ -1089,27 +1134,27 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
1089 | uint32_t flags = opt_flags[i]; | 1134 | uint32_t flags = opt_flags[i]; |
1090 | 1135 | ||
1091 | if (flags & STYLE_MASK) | 1136 | if (flags & STYLE_MASK) |
1092 | all_fmt &= ~STYLE_MASK; | 1137 | G.all_fmt &= ~STYLE_MASK; |
1093 | if (flags & SORT_MASK) | 1138 | if (flags & SORT_MASK) |
1094 | all_fmt &= ~SORT_MASK; | 1139 | G.all_fmt &= ~SORT_MASK; |
1095 | if (flags & TIME_MASK) | 1140 | if (flags & TIME_MASK) |
1096 | all_fmt &= ~TIME_MASK; | 1141 | G.all_fmt &= ~TIME_MASK; |
1097 | 1142 | ||
1098 | all_fmt |= flags; | 1143 | G.all_fmt |= flags; |
1099 | } | 1144 | } |
1100 | } | 1145 | } |
1101 | 1146 | ||
1102 | #if ENABLE_FEATURE_LS_COLOR | 1147 | #if ENABLE_FEATURE_LS_COLOR |
1103 | /* set show_color = 1/0 */ | 1148 | /* set G_show_color = 1/0 */ |
1104 | if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) { | 1149 | if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) { |
1105 | char *p = getenv("LS_COLORS"); | 1150 | char *p = getenv("LS_COLORS"); |
1106 | /* LS_COLORS is unset, or (not empty && not "none") ? */ | 1151 | /* LS_COLORS is unset, or (not empty && not "none") ? */ |
1107 | if (!p || (p[0] && strcmp(p, "none") != 0)) | 1152 | if (!p || (p[0] && strcmp(p, "none") != 0)) |
1108 | show_color = 1; | 1153 | G_show_color = 1; |
1109 | } | 1154 | } |
1110 | if (opt & OPT_color) { | 1155 | if (opt & OPT_color) { |
1111 | if (color_opt[0] == 'n') | 1156 | if (color_opt[0] == 'n') |
1112 | show_color = 0; | 1157 | G_show_color = 0; |
1113 | else switch (index_in_substrings(color_str, color_opt)) { | 1158 | else switch (index_in_substrings(color_str, color_opt)) { |
1114 | case 3: | 1159 | case 3: |
1115 | case 4: | 1160 | case 4: |
@@ -1118,34 +1163,34 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
1118 | case 0: | 1163 | case 0: |
1119 | case 1: | 1164 | case 1: |
1120 | case 2: | 1165 | case 2: |
1121 | show_color = 1; | 1166 | G_show_color = 1; |
1122 | } | 1167 | } |
1123 | } | 1168 | } |
1124 | } | 1169 | } |
1125 | #endif | 1170 | #endif |
1126 | 1171 | ||
1127 | /* sort out which command line options take precedence */ | 1172 | /* sort out which command line options take precedence */ |
1128 | if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST)) | 1173 | if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST)) |
1129 | all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ | 1174 | G.all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ |
1130 | if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { | 1175 | if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { |
1131 | if (all_fmt & TIME_CHANGE) | 1176 | if (G.all_fmt & TIME_CHANGE) |
1132 | all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME; | 1177 | G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME; |
1133 | if (all_fmt & TIME_ACCESS) | 1178 | if (G.all_fmt & TIME_ACCESS) |
1134 | all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME; | 1179 | G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME; |
1135 | } | 1180 | } |
1136 | if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */ | 1181 | if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */ |
1137 | all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME); | 1182 | G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME); |
1138 | 1183 | ||
1139 | /* choose a display format if one was not already specified by an option */ | 1184 | /* choose a display format if one was not already specified by an option */ |
1140 | if (!(all_fmt & STYLE_MASK)) | 1185 | if (!(G.all_fmt & STYLE_MASK)) |
1141 | all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE); | 1186 | G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE); |
1142 | 1187 | ||
1143 | argv += optind; | 1188 | argv += optind; |
1144 | if (!argv[0]) | 1189 | if (!argv[0]) |
1145 | *--argv = (char*)"."; | 1190 | *--argv = (char*)"."; |
1146 | 1191 | ||
1147 | if (argv[1]) | 1192 | if (argv[1]) |
1148 | all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */ | 1193 | G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */ |
1149 | 1194 | ||
1150 | /* stuff the command line file names into a dnode array */ | 1195 | /* stuff the command line file names into a dnode array */ |
1151 | dn = NULL; | 1196 | dn = NULL; |
@@ -1153,25 +1198,26 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
1153 | do { | 1198 | do { |
1154 | cur = my_stat(*argv, *argv, | 1199 | cur = my_stat(*argv, *argv, |
1155 | /* follow links on command line unless -l, -s or -F: */ | 1200 | /* follow links on command line unless -l, -s or -F: */ |
1156 | !((all_fmt & STYLE_MASK) == STYLE_LONG | 1201 | !((G.all_fmt & STYLE_MASK) == STYLE_LONG |
1157 | || (all_fmt & LIST_BLOCKS) | 1202 | || (G.all_fmt & LIST_BLOCKS) |
1158 | || (option_mask32 & OPT_F) | 1203 | || (option_mask32 & OPT_F) |
1159 | ) | 1204 | ) |
1160 | /* ... or if -H: */ | 1205 | /* ... or if -H: */ |
1161 | || (option_mask32 & OPT_H) | 1206 | || (option_mask32 & OPT_H) |
1207 | /* ... or if -L, but my_stat always follows links if -L */ | ||
1162 | ); | 1208 | ); |
1163 | argv++; | 1209 | argv++; |
1164 | if (!cur) | 1210 | if (!cur) |
1165 | continue; | 1211 | continue; |
1166 | cur->fname_allocated = 0; | 1212 | /*cur->fname_allocated = 0; - already is */ |
1167 | cur->next = dn; | 1213 | cur->dn_next = dn; |
1168 | dn = cur; | 1214 | dn = cur; |
1169 | nfiles++; | 1215 | nfiles++; |
1170 | } while (*argv); | 1216 | } while (*argv); |
1171 | 1217 | ||
1172 | /* nfiles _may_ be 0 here - try "ls doesnt_exist" */ | 1218 | /* nfiles _may_ be 0 here - try "ls doesnt_exist" */ |
1173 | if (nfiles == 0) | 1219 | if (nfiles == 0) |
1174 | return exit_code; | 1220 | return G.exit_code; |
1175 | 1221 | ||
1176 | /* now that we know how many files there are | 1222 | /* now that we know how many files there are |
1177 | * allocate memory for an array to hold dnode pointers | 1223 | * allocate memory for an array to hold dnode pointers |
@@ -1179,33 +1225,32 @@ int ls_main(int argc UNUSED_PARAM, char **argv) | |||
1179 | dnp = dnalloc(nfiles); | 1225 | dnp = dnalloc(nfiles); |
1180 | for (i = 0; /* i < nfiles - detected via !dn below */; i++) { | 1226 | for (i = 0; /* i < nfiles - detected via !dn below */; i++) { |
1181 | dnp[i] = dn; /* save pointer to node in array */ | 1227 | dnp[i] = dn; /* save pointer to node in array */ |
1182 | dn = dn->next; | 1228 | dn = dn->dn_next; |
1183 | if (!dn) | 1229 | if (!dn) |
1184 | break; | 1230 | break; |
1185 | } | 1231 | } |
1186 | 1232 | ||
1187 | if (all_fmt & DISP_NOLIST) { | 1233 | if (G.all_fmt & DISP_NOLIST) { |
1188 | dnsort(dnp, nfiles); | 1234 | sort_and_display_files(dnp, nfiles); |
1189 | showfiles(dnp, nfiles); | ||
1190 | } else { | 1235 | } else { |
1191 | dnd = splitdnarray(dnp, SPLIT_DIR); | 1236 | dnd = splitdnarray(dnp, SPLIT_DIR); |
1192 | dnf = splitdnarray(dnp, SPLIT_FILE); | 1237 | dnf = splitdnarray(dnp, SPLIT_FILE); |
1193 | dndirs = count_dirs(dnp, SPLIT_DIR); | 1238 | dndirs = count_dirs(dnp, SPLIT_DIR); |
1194 | dnfiles = nfiles - dndirs; | 1239 | dnfiles = nfiles - dndirs; |
1195 | if (dnfiles > 0) { | 1240 | if (dnfiles > 0) { |
1196 | dnsort(dnf, dnfiles); | 1241 | sort_and_display_files(dnf, dnfiles); |
1197 | showfiles(dnf, dnfiles); | ||
1198 | if (ENABLE_FEATURE_CLEAN_UP) | 1242 | if (ENABLE_FEATURE_CLEAN_UP) |
1199 | free(dnf); | 1243 | free(dnf); |
1200 | } | 1244 | } |
1201 | if (dndirs > 0) { | 1245 | if (dndirs > 0) { |
1202 | dnsort(dnd, dndirs); | 1246 | dnsort(dnd, dndirs); |
1203 | showdirs(dnd, dnfiles == 0); | 1247 | scan_and_display_dirs_recur(dnd, dnfiles == 0); |
1204 | if (ENABLE_FEATURE_CLEAN_UP) | 1248 | if (ENABLE_FEATURE_CLEAN_UP) |
1205 | free(dnd); | 1249 | free(dnd); |
1206 | } | 1250 | } |
1207 | } | 1251 | } |
1252 | |||
1208 | if (ENABLE_FEATURE_CLEAN_UP) | 1253 | if (ENABLE_FEATURE_CLEAN_UP) |
1209 | dfree(dnp); | 1254 | dfree(dnp); |
1210 | return exit_code; | 1255 | return G.exit_code; |
1211 | } | 1256 | } |
diff --git a/coreutils/od.c b/coreutils/od.c index 31ebde210..fb11fcfe3 100644 --- a/coreutils/od.c +++ b/coreutils/od.c | |||
@@ -11,11 +11,12 @@ | |||
11 | * Original copyright notice is retained at the end of this file. | 11 | * Original copyright notice is retained at the end of this file. |
12 | */ | 12 | */ |
13 | 13 | ||
14 | //usage:#if !ENABLE_DESKTOP | ||
14 | //usage:#define od_trivial_usage | 15 | //usage:#define od_trivial_usage |
15 | //usage: "[-aBbcDdeFfHhIiLlOovXx] " IF_DESKTOP("[-t TYPE] ") "[FILE]" | 16 | //usage: "[-aBbcDdeFfHhIiLlOovXx] [FILE]" |
16 | //usage:#define od_full_usage "\n\n" | 17 | //usage:#define od_full_usage "\n\n" |
17 | //usage: "Write an unambiguous representation, octal bytes by default, of FILE\n" | 18 | //usage: "Print FILE (or stdin) unambiguously, as octal bytes by default" |
18 | //usage: "(or stdin) to stdout" | 19 | //usage:#endif |
19 | 20 | ||
20 | #include "libbb.h" | 21 | #include "libbb.h" |
21 | #if ENABLE_DESKTOP | 22 | #if ENABLE_DESKTOP |
diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c index 4c6b64d5e..8013f483c 100644 --- a/coreutils/od_bloaty.c +++ b/coreutils/od_bloaty.c | |||
@@ -13,48 +13,64 @@ | |||
13 | 13 | ||
14 | You should have received a copy of the GNU General Public License | 14 | You should have received a copy of the GNU General Public License |
15 | along with this program; if not, write to the Free Software Foundation, | 15 | along with this program; if not, write to the Free Software Foundation, |
16 | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | 16 | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
17 | 17 | */ | |
18 | /* Written by Jim Meyering. */ | 18 | /* Written by Jim Meyering. */ |
19 | /* Busyboxed by Denys Vlasenko, based on od.c from coreutils-5.2.1 */ | ||
19 | 20 | ||
20 | /* Busyboxed by Denys Vlasenko | ||
21 | |||
22 | Based on od.c from coreutils-5.2.1 | ||
23 | Top bloat sources: | ||
24 | 21 | ||
25 | 00000073 t parse_old_offset | 22 | /* #include "libbb.h" - done in od.c */ |
26 | 0000007b t get_lcm | 23 | #define assert(a) ((void)0) |
27 | 00000090 r long_options | ||
28 | 00000092 t print_named_ascii | ||
29 | 000000bf t print_ascii | ||
30 | 00000168 t write_block | ||
31 | 00000366 t decode_format_string | ||
32 | 00000a71 T od_main | ||
33 | |||
34 | Tested for compat with coreutils 6.3 | ||
35 | using this script. Minor differences fixed. | ||
36 | 24 | ||
37 | #!/bin/sh | ||
38 | echo STD | ||
39 | time /path/to/coreutils/od \ | ||
40 | ...params... \ | ||
41 | >std | ||
42 | echo Exit code $? | ||
43 | echo BBOX | ||
44 | time ./busybox od \ | ||
45 | ...params... \ | ||
46 | >bbox | ||
47 | echo Exit code $? | ||
48 | diff -u -a std bbox >bbox.diff || { echo Different!; sleep 1; } | ||
49 | 25 | ||
50 | */ | 26 | //usage:#if ENABLE_DESKTOP |
27 | //usage:#define od_trivial_usage | ||
28 | //usage: "[-abcdfhilovxs] [-t TYPE] [-A RADIX] [-N SIZE] [-j SKIP] [-S MINSTR] [-w WIDTH] [FILE...]" | ||
29 | // We don't support: | ||
30 | // ... [FILE] [[+]OFFSET[.][b]] | ||
31 | // Support is buggy for: | ||
32 | // od --traditional [OPTION]... [FILE] [[+]OFFSET[.][b] [+][LABEL][.][b]] | ||
33 | |||
34 | //usage:#define od_full_usage "\n\n" | ||
35 | //usage: "Print FILEs (or stdin) unambiguously, as octal bytes by default" | ||
36 | //usage:#endif | ||
37 | |||
38 | enum { | ||
39 | OPT_A = 1 << 0, | ||
40 | OPT_N = 1 << 1, | ||
41 | OPT_a = 1 << 2, | ||
42 | OPT_b = 1 << 3, | ||
43 | OPT_c = 1 << 4, | ||
44 | OPT_d = 1 << 5, | ||
45 | OPT_f = 1 << 6, | ||
46 | OPT_h = 1 << 7, | ||
47 | OPT_i = 1 << 8, | ||
48 | OPT_j = 1 << 9, | ||
49 | OPT_l = 1 << 10, | ||
50 | OPT_o = 1 << 11, | ||
51 | OPT_t = 1 << 12, | ||
52 | /* When zero and two or more consecutive blocks are equal, format | ||
53 | only the first block and output an asterisk alone on the following | ||
54 | line to indicate that identical blocks have been elided: */ | ||
55 | OPT_v = 1 << 13, | ||
56 | OPT_x = 1 << 14, | ||
57 | OPT_s = 1 << 15, | ||
58 | OPT_S = 1 << 16, | ||
59 | OPT_w = 1 << 17, | ||
60 | OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS, | ||
61 | }; | ||
51 | 62 | ||
52 | #include "libbb.h" | 63 | #define OD_GETOPT32() getopt32(argv, \ |
64 | "A:N:abcdfhij:lot:vxsS:w::", \ | ||
65 | /* -w with optional param */ \ | ||
66 | /* -S was -s and also had optional parameter */ \ | ||
67 | /* but in coreutils 6.3 it was renamed and now has */ \ | ||
68 | /* _mandatory_ parameter */ \ | ||
69 | &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block) | ||
53 | 70 | ||
54 | #define assert(a) ((void)0) | ||
55 | 71 | ||
56 | /* Check for 0x7f is a coreutils 6.3 addition */ | 72 | /* Check for 0x7f is a coreutils 6.3 addition */ |
57 | #define ISPRINT(c) (((c)>=' ') && (c) != 0x7f) | 73 | #define ISPRINT(c) (((c) >= ' ') && (c) < 0x7f) |
58 | 74 | ||
59 | typedef long double longdouble_t; | 75 | typedef long double longdouble_t; |
60 | typedef unsigned long long ulonglong_t; | 76 | typedef unsigned long long ulonglong_t; |
@@ -165,17 +181,9 @@ struct ERR_width_bytes_has_bad_size { | |||
165 | char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1]; | 181 | char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1]; |
166 | }; | 182 | }; |
167 | 183 | ||
168 | static smallint flag_dump_strings; | 184 | static smallint exit_code; |
169 | /* Non-zero if an old-style 'pseudo-address' was specified. */ | ||
170 | static smallint flag_pseudo_start; | ||
171 | static smallint limit_bytes_to_format; | ||
172 | /* When zero and two or more consecutive blocks are equal, format | ||
173 | only the first block and output an asterisk alone on the following | ||
174 | line to indicate that identical blocks have been elided. */ | ||
175 | static smallint verbose; | ||
176 | static smallint ioerror; | ||
177 | 185 | ||
178 | static size_t string_min; | 186 | static unsigned string_min; |
179 | 187 | ||
180 | /* An array of specs describing how to format each input block. */ | 188 | /* An array of specs describing how to format each input block. */ |
181 | static size_t n_specs; | 189 | static size_t n_specs; |
@@ -186,7 +194,11 @@ static struct tspec *spec; | |||
186 | static void (*format_address)(off_t, char); | 194 | static void (*format_address)(off_t, char); |
187 | /* The difference between the old-style pseudo starting address and | 195 | /* The difference between the old-style pseudo starting address and |
188 | the number of bytes to skip. */ | 196 | the number of bytes to skip. */ |
197 | #if ENABLE_LONG_OPTS | ||
189 | static off_t pseudo_offset; | 198 | static off_t pseudo_offset; |
199 | #else | ||
200 | enum { pseudo_offset = 0 }; | ||
201 | #endif | ||
190 | /* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all | 202 | /* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all |
191 | input is formatted. */ | 203 | input is formatted. */ |
192 | 204 | ||
@@ -221,7 +233,7 @@ static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 | |||
221 | 233 | ||
222 | #define MAX_FP_TYPE_SIZE sizeof(longdouble_t) | 234 | #define MAX_FP_TYPE_SIZE sizeof(longdouble_t) |
223 | static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = { | 235 | static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = { |
224 | /* gcc seems to allow repeated indexes. Last one stays */ | 236 | /* gcc seems to allow repeated indexes. Last one wins */ |
225 | [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE, | 237 | [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE, |
226 | [sizeof(double)] = FLOAT_DOUBLE, | 238 | [sizeof(double)] = FLOAT_DOUBLE, |
227 | [sizeof(float)] = FLOAT_SINGLE | 239 | [sizeof(float)] = FLOAT_SINGLE |
@@ -383,7 +395,7 @@ print_named_ascii(size_t n_bytes, const char *block, | |||
383 | }; | 395 | }; |
384 | // buf[N] pos: 01234 56789 | 396 | // buf[N] pos: 01234 56789 |
385 | char buf[12] = " x\0 0xx\0"; | 397 | char buf[12] = " x\0 0xx\0"; |
386 | // actually " x\0 xxx\0", but I want to share the string with below. | 398 | // actually " x\0 xxx\0", but want to share string with print_ascii. |
387 | // [12] because we take three 32bit stack slots anyway, and | 399 | // [12] because we take three 32bit stack slots anyway, and |
388 | // gcc is too dumb to initialize with constant stores, | 400 | // gcc is too dumb to initialize with constant stores, |
389 | // it copies initializer from rodata. Oh well. | 401 | // it copies initializer from rodata. Oh well. |
@@ -479,10 +491,10 @@ open_next_file(void) | |||
479 | if (in_stream) { | 491 | if (in_stream) { |
480 | break; | 492 | break; |
481 | } | 493 | } |
482 | ioerror = 1; | 494 | exit_code = 1; |
483 | } | 495 | } |
484 | 496 | ||
485 | if (limit_bytes_to_format && !flag_dump_strings) | 497 | if ((option_mask32 & (OPT_N|OPT_S)) == OPT_N) |
486 | setbuf(in_stream, NULL); | 498 | setbuf(in_stream, NULL); |
487 | } | 499 | } |
488 | 500 | ||
@@ -502,15 +514,14 @@ check_and_close(void) | |||
502 | ? bb_msg_standard_input | 514 | ? bb_msg_standard_input |
503 | : file_list[-1] | 515 | : file_list[-1] |
504 | ); | 516 | ); |
505 | ioerror = 1; | 517 | exit_code = 1; |
506 | } | 518 | } |
507 | fclose_if_not_stdin(in_stream); | 519 | fclose_if_not_stdin(in_stream); |
508 | in_stream = NULL; | 520 | in_stream = NULL; |
509 | } | 521 | } |
510 | 522 | ||
511 | if (ferror(stdout)) { | 523 | if (ferror(stdout)) { |
512 | bb_error_msg(bb_msg_write_error); | 524 | bb_error_msg_and_die(bb_msg_write_error); |
513 | ioerror = 1; | ||
514 | } | 525 | } |
515 | } | 526 | } |
516 | 527 | ||
@@ -542,7 +553,6 @@ decode_one_format(const char *s_orig, const char *s, struct tspec *tspec) | |||
542 | unsigned field_width = 0; | 553 | unsigned field_width = 0; |
543 | int pos; | 554 | int pos; |
544 | 555 | ||
545 | |||
546 | switch (*s) { | 556 | switch (*s) { |
547 | case 'd': | 557 | case 'd': |
548 | case 'o': | 558 | case 'o': |
@@ -788,7 +798,7 @@ skip(off_t n_skip) | |||
788 | /* take "check & close / open_next" route */ | 798 | /* take "check & close / open_next" route */ |
789 | } else { | 799 | } else { |
790 | if (fseeko(in_stream, n_skip, SEEK_CUR) != 0) | 800 | if (fseeko(in_stream, n_skip, SEEK_CUR) != 0) |
791 | ioerror = 1; | 801 | exit_code = 1; |
792 | return; | 802 | return; |
793 | } | 803 | } |
794 | } else { | 804 | } else { |
@@ -889,7 +899,8 @@ write_block(off_t current_offset, size_t n_bytes, | |||
889 | static char prev_pair_equal = 0; | 899 | static char prev_pair_equal = 0; |
890 | size_t i; | 900 | size_t i; |
891 | 901 | ||
892 | if (!verbose && !first | 902 | if (!(option_mask32 & OPT_v) |
903 | && !first | ||
893 | && n_bytes == bytes_per_block | 904 | && n_bytes == bytes_per_block |
894 | && memcmp(prev_block, curr_block, bytes_per_block) == 0 | 905 | && memcmp(prev_block, curr_block, bytes_per_block) == 0 |
895 | ) { | 906 | ) { |
@@ -911,9 +922,9 @@ write_block(off_t current_offset, size_t n_bytes, | |||
911 | (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string); | 922 | (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string); |
912 | if (spec[i].hexl_mode_trailer) { | 923 | if (spec[i].hexl_mode_trailer) { |
913 | /* space-pad out to full line width, then dump the trailer */ | 924 | /* space-pad out to full line width, then dump the trailer */ |
914 | int datum_width = width_bytes[spec[i].size]; | 925 | unsigned datum_width = width_bytes[spec[i].size]; |
915 | int blank_fields = (bytes_per_block - n_bytes) / datum_width; | 926 | unsigned blank_fields = (bytes_per_block - n_bytes) / datum_width; |
916 | int field_width = spec[i].field_width + 1; | 927 | unsigned field_width = spec[i].field_width + 1; |
917 | printf("%*s", blank_fields * field_width, ""); | 928 | printf("%*s", blank_fields * field_width, ""); |
918 | dump_hexl_mode_trailer(n_bytes, curr_block); | 929 | dump_hexl_mode_trailer(n_bytes, curr_block); |
919 | } | 930 | } |
@@ -961,42 +972,6 @@ get_lcm(void) | |||
961 | return l_c_m; | 972 | return l_c_m; |
962 | } | 973 | } |
963 | 974 | ||
964 | #if ENABLE_LONG_OPTS | ||
965 | /* If S is a valid traditional offset specification with an optional | ||
966 | leading '+' return nonzero and set *OFFSET to the offset it denotes. */ | ||
967 | |||
968 | static int | ||
969 | parse_old_offset(const char *s, off_t *offset) | ||
970 | { | ||
971 | static const struct suffix_mult Bb[] = { | ||
972 | { "B", 1024 }, | ||
973 | { "b", 512 }, | ||
974 | { "", 0 } | ||
975 | }; | ||
976 | char *p; | ||
977 | int radix; | ||
978 | |||
979 | /* Skip over any leading '+'. */ | ||
980 | if (s[0] == '+') ++s; | ||
981 | |||
982 | /* Determine the radix we'll use to interpret S. If there is a '.', | ||
983 | * it's decimal, otherwise, if the string begins with '0X'or '0x', | ||
984 | * it's hexadecimal, else octal. */ | ||
985 | p = strchr(s, '.'); | ||
986 | radix = 8; | ||
987 | if (p) { | ||
988 | p[0] = '\0'; /* cheating */ | ||
989 | radix = 10; | ||
990 | } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) | ||
991 | radix = 16; | ||
992 | |||
993 | *offset = xstrtooff_sfx(s, radix, Bb); | ||
994 | if (p) p[0] = '.'; | ||
995 | |||
996 | return (*offset >= 0); | ||
997 | } | ||
998 | #endif | ||
999 | |||
1000 | /* Read a chunk of size BYTES_PER_BLOCK from the input files, write the | 975 | /* Read a chunk of size BYTES_PER_BLOCK from the input files, write the |
1001 | formatted block to standard output, and repeat until the specified | 976 | formatted block to standard output, and repeat until the specified |
1002 | maximum number of bytes has been read or until all input has been | 977 | maximum number of bytes has been read or until all input has been |
@@ -1014,27 +989,25 @@ dump(off_t current_offset, off_t end_offset) | |||
1014 | int idx; | 989 | int idx; |
1015 | size_t n_bytes_read; | 990 | size_t n_bytes_read; |
1016 | 991 | ||
1017 | block[0] = xmalloc(2*bytes_per_block); | 992 | block[0] = xmalloc(2 * bytes_per_block); |
1018 | block[1] = block[0] + bytes_per_block; | 993 | block[1] = block[0] + bytes_per_block; |
1019 | 994 | ||
1020 | idx = 0; | 995 | idx = 0; |
1021 | if (limit_bytes_to_format) { | 996 | if (option_mask32 & OPT_N) { |
1022 | while (1) { | 997 | while (1) { |
1023 | size_t n_needed; | 998 | size_t n_needed; |
1024 | if (current_offset >= end_offset) { | 999 | if (current_offset >= end_offset) { |
1025 | n_bytes_read = 0; | 1000 | n_bytes_read = 0; |
1026 | break; | 1001 | break; |
1027 | } | 1002 | } |
1028 | n_needed = MIN(end_offset - current_offset, | 1003 | n_needed = MIN(end_offset - current_offset, (off_t) bytes_per_block); |
1029 | (off_t) bytes_per_block); | ||
1030 | read_block(n_needed, block[idx], &n_bytes_read); | 1004 | read_block(n_needed, block[idx], &n_bytes_read); |
1031 | if (n_bytes_read < bytes_per_block) | 1005 | if (n_bytes_read < bytes_per_block) |
1032 | break; | 1006 | break; |
1033 | assert(n_bytes_read == bytes_per_block); | 1007 | assert(n_bytes_read == bytes_per_block); |
1034 | write_block(current_offset, n_bytes_read, | 1008 | write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]); |
1035 | block[!idx], block[idx]); | ||
1036 | current_offset += n_bytes_read; | 1009 | current_offset += n_bytes_read; |
1037 | idx = !idx; | 1010 | idx ^= 1; |
1038 | } | 1011 | } |
1039 | } else { | 1012 | } else { |
1040 | while (1) { | 1013 | while (1) { |
@@ -1042,10 +1015,9 @@ dump(off_t current_offset, off_t end_offset) | |||
1042 | if (n_bytes_read < bytes_per_block) | 1015 | if (n_bytes_read < bytes_per_block) |
1043 | break; | 1016 | break; |
1044 | assert(n_bytes_read == bytes_per_block); | 1017 | assert(n_bytes_read == bytes_per_block); |
1045 | write_block(current_offset, n_bytes_read, | 1018 | write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]); |
1046 | block[!idx], block[idx]); | ||
1047 | current_offset += n_bytes_read; | 1019 | current_offset += n_bytes_read; |
1048 | idx = !idx; | 1020 | idx ^= 1; |
1049 | } | 1021 | } |
1050 | } | 1022 | } |
1051 | 1023 | ||
@@ -1061,41 +1033,18 @@ dump(off_t current_offset, off_t end_offset) | |||
1061 | 1033 | ||
1062 | memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read); | 1034 | memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read); |
1063 | write_block(current_offset, bytes_to_write, | 1035 | write_block(current_offset, bytes_to_write, |
1064 | block[!idx], block[idx]); | 1036 | block[idx ^ 1], block[idx]); |
1065 | current_offset += n_bytes_read; | 1037 | current_offset += n_bytes_read; |
1066 | } | 1038 | } |
1067 | 1039 | ||
1068 | format_address(current_offset, '\n'); | 1040 | format_address(current_offset, '\n'); |
1069 | 1041 | ||
1070 | if (limit_bytes_to_format && current_offset >= end_offset) | 1042 | if ((option_mask32 & OPT_N) && current_offset >= end_offset) |
1071 | check_and_close(); | 1043 | check_and_close(); |
1072 | 1044 | ||
1073 | free(block[0]); | 1045 | free(block[0]); |
1074 | } | 1046 | } |
1075 | 1047 | ||
1076 | /* Read a single byte into *C from the concatenation of the input files | ||
1077 | named in the global array FILE_LIST. On the first call to this | ||
1078 | function, the global variable IN_STREAM is expected to be an open | ||
1079 | stream associated with the input file INPUT_FILENAME. If IN_STREAM | ||
1080 | is at end-of-file, close it and update the global variables IN_STREAM | ||
1081 | and INPUT_FILENAME so they correspond to the next file in the list. | ||
1082 | Then try to read a byte from the newly opened file. Repeat if | ||
1083 | necessary until EOF is reached for the last file in FILE_LIST, then | ||
1084 | set *C to EOF and return. Subsequent calls do likewise. */ | ||
1085 | |||
1086 | static void | ||
1087 | read_char(int *c) | ||
1088 | { | ||
1089 | while (in_stream) { /* !EOF */ | ||
1090 | *c = fgetc(in_stream); | ||
1091 | if (*c != EOF) | ||
1092 | return; | ||
1093 | check_and_close(); | ||
1094 | open_next_file(); | ||
1095 | } | ||
1096 | *c = EOF; | ||
1097 | } | ||
1098 | |||
1099 | /* Read N bytes into BLOCK from the concatenation of the input files | 1048 | /* Read N bytes into BLOCK from the concatenation of the input files |
1100 | named in the global array FILE_LIST. On the first call to this | 1049 | named in the global array FILE_LIST. On the first call to this |
1101 | function, the global variable IN_STREAM is expected to be an open | 1050 | function, the global variable IN_STREAM is expected to be an open |
@@ -1119,8 +1068,8 @@ read_char(int *c) | |||
1119 | static void | 1068 | static void |
1120 | dump_strings(off_t address, off_t end_offset) | 1069 | dump_strings(off_t address, off_t end_offset) |
1121 | { | 1070 | { |
1122 | size_t bufsize = MAX(100, string_min); | 1071 | unsigned bufsize = MAX(100, string_min); |
1123 | char *buf = xmalloc(bufsize); | 1072 | unsigned char *buf = xmalloc(bufsize); |
1124 | 1073 | ||
1125 | while (1) { | 1074 | while (1) { |
1126 | size_t i; | 1075 | size_t i; |
@@ -1128,19 +1077,25 @@ dump_strings(off_t address, off_t end_offset) | |||
1128 | 1077 | ||
1129 | /* See if the next 'string_min' chars are all printing chars. */ | 1078 | /* See if the next 'string_min' chars are all printing chars. */ |
1130 | tryline: | 1079 | tryline: |
1131 | if (limit_bytes_to_format && (end_offset - string_min <= address)) | 1080 | if ((option_mask32 & OPT_N) && (end_offset - string_min <= address)) |
1132 | break; | 1081 | break; |
1133 | i = 0; | 1082 | i = 0; |
1134 | while (!limit_bytes_to_format || address < end_offset) { | 1083 | while (!(option_mask32 & OPT_N) || address < end_offset) { |
1135 | if (i == bufsize) { | 1084 | if (i == bufsize) { |
1136 | bufsize += bufsize/8; | 1085 | bufsize += bufsize/8; |
1137 | buf = xrealloc(buf, bufsize); | 1086 | buf = xrealloc(buf, bufsize); |
1138 | } | 1087 | } |
1139 | read_char(&c); | 1088 | |
1140 | if (c < 0) { /* EOF */ | 1089 | while (in_stream) { /* !EOF */ |
1141 | free(buf); | 1090 | c = fgetc(in_stream); |
1142 | return; | 1091 | if (c != EOF) |
1092 | goto got_char; | ||
1093 | check_and_close(); | ||
1094 | open_next_file(); | ||
1143 | } | 1095 | } |
1096 | /* EOF */ | ||
1097 | goto ret; | ||
1098 | got_char: | ||
1144 | address++; | 1099 | address++; |
1145 | if (!c) | 1100 | if (!c) |
1146 | break; | 1101 | break; |
@@ -1152,8 +1107,7 @@ dump_strings(off_t address, off_t end_offset) | |||
1152 | if (i < string_min) /* Too short! */ | 1107 | if (i < string_min) /* Too short! */ |
1153 | goto tryline; | 1108 | goto tryline; |
1154 | 1109 | ||
1155 | /* If we get here, the string is all printable and NUL-terminated, | 1110 | /* If we get here, the string is all printable and NUL-terminated */ |
1156 | * so print it. It is all in 'buf' and 'i' is its length. */ | ||
1157 | buf[i] = 0; | 1111 | buf[i] = 0; |
1158 | format_address(address - i - 1, ' '); | 1112 | format_address(address - i - 1, ' '); |
1159 | 1113 | ||
@@ -1174,13 +1128,50 @@ dump_strings(off_t address, off_t end_offset) | |||
1174 | 1128 | ||
1175 | /* We reach this point only if we search through | 1129 | /* We reach this point only if we search through |
1176 | (max_bytes_to_format - string_min) bytes before reaching EOF. */ | 1130 | (max_bytes_to_format - string_min) bytes before reaching EOF. */ |
1131 | check_and_close(); | ||
1132 | ret: | ||
1177 | free(buf); | 1133 | free(buf); |
1134 | } | ||
1178 | 1135 | ||
1179 | check_and_close(); | 1136 | #if ENABLE_LONG_OPTS |
1137 | /* If S is a valid traditional offset specification with an optional | ||
1138 | leading '+' return nonzero and set *OFFSET to the offset it denotes. */ | ||
1139 | |||
1140 | static int | ||
1141 | parse_old_offset(const char *s, off_t *offset) | ||
1142 | { | ||
1143 | static const struct suffix_mult Bb[] = { | ||
1144 | { "B", 1024 }, | ||
1145 | { "b", 512 }, | ||
1146 | { "", 0 } | ||
1147 | }; | ||
1148 | char *p; | ||
1149 | int radix; | ||
1150 | |||
1151 | /* Skip over any leading '+'. */ | ||
1152 | if (s[0] == '+') ++s; | ||
1153 | if (!isdigit(s[0])) return 0; /* not a number */ | ||
1154 | |||
1155 | /* Determine the radix we'll use to interpret S. If there is a '.', | ||
1156 | * it's decimal, otherwise, if the string begins with '0X'or '0x', | ||
1157 | * it's hexadecimal, else octal. */ | ||
1158 | p = strchr(s, '.'); | ||
1159 | radix = 8; | ||
1160 | if (p) { | ||
1161 | p[0] = '\0'; /* cheating */ | ||
1162 | radix = 10; | ||
1163 | } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) | ||
1164 | radix = 16; | ||
1165 | |||
1166 | *offset = xstrtooff_sfx(s, radix, Bb); | ||
1167 | if (p) p[0] = '.'; | ||
1168 | |||
1169 | return (*offset >= 0); | ||
1180 | } | 1170 | } |
1171 | #endif | ||
1181 | 1172 | ||
1182 | int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 1173 | int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1183 | int od_main(int argc, char **argv) | 1174 | int od_main(int argc UNUSED_PARAM, char **argv) |
1184 | { | 1175 | { |
1185 | static const struct suffix_mult bkm[] = { | 1176 | static const struct suffix_mult bkm[] = { |
1186 | { "b", 512 }, | 1177 | { "b", 512 }, |
@@ -1188,27 +1179,6 @@ int od_main(int argc, char **argv) | |||
1188 | { "m", 1024*1024 }, | 1179 | { "m", 1024*1024 }, |
1189 | { "", 0 } | 1180 | { "", 0 } |
1190 | }; | 1181 | }; |
1191 | enum { | ||
1192 | OPT_A = 1 << 0, | ||
1193 | OPT_N = 1 << 1, | ||
1194 | OPT_a = 1 << 2, | ||
1195 | OPT_b = 1 << 3, | ||
1196 | OPT_c = 1 << 4, | ||
1197 | OPT_d = 1 << 5, | ||
1198 | OPT_f = 1 << 6, | ||
1199 | OPT_h = 1 << 7, | ||
1200 | OPT_i = 1 << 8, | ||
1201 | OPT_j = 1 << 9, | ||
1202 | OPT_l = 1 << 10, | ||
1203 | OPT_o = 1 << 11, | ||
1204 | OPT_t = 1 << 12, | ||
1205 | OPT_v = 1 << 13, | ||
1206 | OPT_x = 1 << 14, | ||
1207 | OPT_s = 1 << 15, | ||
1208 | OPT_S = 1 << 16, | ||
1209 | OPT_w = 1 << 17, | ||
1210 | OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS, | ||
1211 | }; | ||
1212 | #if ENABLE_LONG_OPTS | 1182 | #if ENABLE_LONG_OPTS |
1213 | static const char od_longopts[] ALIGN1 = | 1183 | static const char od_longopts[] ALIGN1 = |
1214 | "skip-bytes\0" Required_argument "j" | 1184 | "skip-bytes\0" Required_argument "j" |
@@ -1216,18 +1186,18 @@ int od_main(int argc, char **argv) | |||
1216 | "read-bytes\0" Required_argument "N" | 1186 | "read-bytes\0" Required_argument "N" |
1217 | "format\0" Required_argument "t" | 1187 | "format\0" Required_argument "t" |
1218 | "output-duplicates\0" No_argument "v" | 1188 | "output-duplicates\0" No_argument "v" |
1189 | /* Yes, it's true: -S NUM, but --strings[=NUM]! | ||
1190 | * that is, NUM is mandatory for -S but optional for --strings! | ||
1191 | */ | ||
1219 | "strings\0" Optional_argument "S" | 1192 | "strings\0" Optional_argument "S" |
1220 | "width\0" Optional_argument "w" | 1193 | "width\0" Optional_argument "w" |
1221 | "traditional\0" No_argument "\xff" | 1194 | "traditional\0" No_argument "\xff" |
1222 | ; | 1195 | ; |
1223 | #endif | 1196 | #endif |
1224 | char *str_A, *str_N, *str_j, *str_S; | 1197 | const char *str_A, *str_N, *str_j, *str_S = "3"; |
1225 | llist_t *lst_t = NULL; | 1198 | llist_t *lst_t = NULL; |
1226 | unsigned opt; | 1199 | unsigned opt; |
1227 | int l_c_m; | 1200 | int l_c_m; |
1228 | /* The old-style 'pseudo starting address' to be printed in parentheses | ||
1229 | after any true address. */ | ||
1230 | off_t pseudo_start = pseudo_start; // for gcc | ||
1231 | /* The number of input bytes to skip before formatting and writing. */ | 1201 | /* The number of input bytes to skip before formatting and writing. */ |
1232 | off_t n_bytes_to_skip = 0; | 1202 | off_t n_bytes_to_skip = 0; |
1233 | /* The offset of the first byte after the last byte to be formatted. */ | 1203 | /* The offset of the first byte after the last byte to be formatted. */ |
@@ -1239,20 +1209,13 @@ int od_main(int argc, char **argv) | |||
1239 | format_address = format_address_std; | 1209 | format_address = format_address_std; |
1240 | address_base_char = 'o'; | 1210 | address_base_char = 'o'; |
1241 | address_pad_len_char = '7'; | 1211 | address_pad_len_char = '7'; |
1242 | /* flag_dump_strings = 0; - already is */ | ||
1243 | 1212 | ||
1244 | /* Parse command line */ | 1213 | /* Parse command line */ |
1245 | opt_complementary = "w+:t::"; /* -w N, -t is a list */ | 1214 | opt_complementary = "w+:t::"; /* -w N, -t is a list */ |
1246 | #if ENABLE_LONG_OPTS | 1215 | #if ENABLE_LONG_OPTS |
1247 | applet_long_options = od_longopts; | 1216 | applet_long_options = od_longopts; |
1248 | #endif | 1217 | #endif |
1249 | opt = getopt32(argv, "A:N:abcdfhij:lot:vxsS:" | 1218 | opt = OD_GETOPT32(); |
1250 | "w::", // -w with optional param | ||
1251 | // -S was -s and also had optional parameter | ||
1252 | // but in coreutils 6.3 it was renamed and now has | ||
1253 | // _mandatory_ parameter | ||
1254 | &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block); | ||
1255 | argc -= optind; | ||
1256 | argv += optind; | 1219 | argv += optind; |
1257 | if (opt & OPT_A) { | 1220 | if (opt & OPT_A) { |
1258 | static const char doxn[] ALIGN1 = "doxn"; | 1221 | static const char doxn[] ALIGN1 = "doxn"; |
@@ -1274,7 +1237,6 @@ int od_main(int argc, char **argv) | |||
1274 | address_pad_len_char = doxn_address_pad_len_char[pos]; | 1237 | address_pad_len_char = doxn_address_pad_len_char[pos]; |
1275 | } | 1238 | } |
1276 | if (opt & OPT_N) { | 1239 | if (opt & OPT_N) { |
1277 | limit_bytes_to_format = 1; | ||
1278 | max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm); | 1240 | max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm); |
1279 | } | 1241 | } |
1280 | if (opt & OPT_a) decode_format_string("a"); | 1242 | if (opt & OPT_a) decode_format_string("a"); |
@@ -1287,28 +1249,23 @@ int od_main(int argc, char **argv) | |||
1287 | if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm); | 1249 | if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm); |
1288 | if (opt & OPT_l) decode_format_string("d4"); | 1250 | if (opt & OPT_l) decode_format_string("d4"); |
1289 | if (opt & OPT_o) decode_format_string("o2"); | 1251 | if (opt & OPT_o) decode_format_string("o2"); |
1290 | //if (opt & OPT_t)... | ||
1291 | while (lst_t) { | 1252 | while (lst_t) { |
1292 | decode_format_string(llist_pop(&lst_t)); | 1253 | decode_format_string(llist_pop(&lst_t)); |
1293 | } | 1254 | } |
1294 | if (opt & OPT_v) verbose = 1; | ||
1295 | if (opt & OPT_x) decode_format_string("x2"); | 1255 | if (opt & OPT_x) decode_format_string("x2"); |
1296 | if (opt & OPT_s) decode_format_string("d2"); | 1256 | if (opt & OPT_s) decode_format_string("d2"); |
1297 | if (opt & OPT_S) { | 1257 | if (opt & OPT_S) { |
1298 | string_min = 3; | ||
1299 | string_min = xstrtou_sfx(str_S, 0, bkm); | 1258 | string_min = xstrtou_sfx(str_S, 0, bkm); |
1300 | flag_dump_strings = 1; | ||
1301 | } | 1259 | } |
1302 | //if (opt & OPT_w)... | ||
1303 | //if (opt & OPT_traditional)... | ||
1304 | 1260 | ||
1305 | if (flag_dump_strings && n_specs > 0) | 1261 | // Bloat: |
1306 | bb_error_msg_and_die("no type may be specified when dumping strings"); | 1262 | //if ((option_mask32 & OPT_S) && n_specs > 0) |
1263 | // bb_error_msg_and_die("no type may be specified when dumping strings"); | ||
1307 | 1264 | ||
1308 | /* If the --traditional option is used, there may be from | 1265 | /* If the --traditional option is used, there may be from |
1309 | * 0 to 3 remaining command line arguments; handle each case | 1266 | * 0 to 3 remaining command line arguments; handle each case |
1310 | * separately. | 1267 | * separately. |
1311 | * od [file] [[+]offset[.][b] [[+]label[.][b]]] | 1268 | * od [FILE] [[+]OFFSET[.][b] [[+]LABEL[.][b]]] |
1312 | * The offset and pseudo_start have the same syntax. | 1269 | * The offset and pseudo_start have the same syntax. |
1313 | * | 1270 | * |
1314 | * FIXME: POSIX 1003.1-2001 with XSI requires support for the | 1271 | * FIXME: POSIX 1003.1-2001 with XSI requires support for the |
@@ -1316,93 +1273,91 @@ int od_main(int argc, char **argv) | |||
1316 | 1273 | ||
1317 | #if ENABLE_LONG_OPTS | 1274 | #if ENABLE_LONG_OPTS |
1318 | if (opt & OPT_traditional) { | 1275 | if (opt & OPT_traditional) { |
1319 | off_t o1, o2; | 1276 | if (argv[0]) { |
1320 | 1277 | off_t pseudo_start = -1; | |
1321 | if (argc == 1) { | 1278 | off_t o1, o2; |
1322 | if (parse_old_offset(argv[0], &o1)) { | 1279 | |
1323 | n_bytes_to_skip = o1; | 1280 | if (!argv[1]) { /* one arg */ |
1324 | --argc; | 1281 | if (parse_old_offset(argv[0], &o1)) { |
1325 | ++argv; | 1282 | /* od --traditional OFFSET */ |
1326 | } | 1283 | n_bytes_to_skip = o1; |
1327 | } else if (argc == 2) { | 1284 | argv++; |
1328 | if (parse_old_offset(argv[0], &o1) | 1285 | } |
1329 | && parse_old_offset(argv[1], &o2) | 1286 | /* od --traditional FILE */ |
1330 | ) { | 1287 | } else if (!argv[2]) { /* two args */ |
1331 | n_bytes_to_skip = o1; | 1288 | if (parse_old_offset(argv[0], &o1) |
1332 | flag_pseudo_start = 1; | 1289 | && parse_old_offset(argv[1], &o2) |
1333 | pseudo_start = o2; | 1290 | ) { |
1334 | argv += 2; | 1291 | /* od --traditional OFFSET LABEL */ |
1335 | argc -= 2; | 1292 | n_bytes_to_skip = o1; |
1336 | } else if (parse_old_offset(argv[1], &o2)) { | 1293 | pseudo_start = o2; |
1337 | n_bytes_to_skip = o2; | 1294 | argv += 2; |
1338 | --argc; | 1295 | } else if (parse_old_offset(argv[1], &o2)) { |
1339 | argv[1] = argv[0]; | 1296 | /* od --traditional FILE OFFSET */ |
1340 | ++argv; | 1297 | n_bytes_to_skip = o2; |
1341 | } else { | 1298 | argv[1] = NULL; |
1342 | bb_error_msg_and_die("invalid second operand " | 1299 | } else { |
1343 | "in compatibility mode '%s'", argv[1]); | 1300 | bb_error_msg_and_die("invalid second argument '%s'", argv[1]); |
1344 | } | 1301 | } |
1345 | } else if (argc == 3) { | 1302 | } else if (!argv[3]) { /* three args */ |
1346 | if (parse_old_offset(argv[1], &o1) | 1303 | if (parse_old_offset(argv[1], &o1) |
1347 | && parse_old_offset(argv[2], &o2) | 1304 | && parse_old_offset(argv[2], &o2) |
1348 | ) { | 1305 | ) { |
1349 | n_bytes_to_skip = o1; | 1306 | /* od --traditional FILE OFFSET LABEL */ |
1350 | flag_pseudo_start = 1; | 1307 | n_bytes_to_skip = o1; |
1351 | pseudo_start = o2; | 1308 | pseudo_start = o2; |
1352 | argv[2] = argv[0]; | 1309 | argv[1] = NULL; |
1353 | argv += 2; | 1310 | } else { |
1354 | argc -= 2; | 1311 | bb_error_msg_and_die("the last two arguments must be offsets"); |
1355 | } else { | 1312 | } |
1356 | bb_error_msg_and_die("in compatibility mode " | 1313 | } else { /* >3 args */ |
1357 | "the last two arguments must be offsets"); | 1314 | bb_error_msg_and_die("too many arguments"); |
1358 | } | 1315 | } |
1359 | } else if (argc > 3) { | ||
1360 | bb_error_msg_and_die("compatibility mode supports " | ||
1361 | "at most three arguments"); | ||
1362 | } | ||
1363 | 1316 | ||
1364 | if (flag_pseudo_start) { | 1317 | if (pseudo_start >= 0) { |
1365 | if (format_address == format_address_none) { | 1318 | if (format_address == format_address_none) { |
1366 | address_base_char = 'o'; | 1319 | address_base_char = 'o'; |
1367 | address_pad_len_char = '7'; | 1320 | address_pad_len_char = '7'; |
1368 | format_address = format_address_paren; | 1321 | format_address = format_address_paren; |
1369 | } else | 1322 | } else { |
1370 | format_address = format_address_label; | 1323 | format_address = format_address_label; |
1324 | } | ||
1325 | pseudo_offset = pseudo_start - n_bytes_to_skip; | ||
1326 | } | ||
1371 | } | 1327 | } |
1328 | /* else: od --traditional (without args) */ | ||
1372 | } | 1329 | } |
1373 | #endif | 1330 | #endif |
1374 | 1331 | ||
1375 | if (limit_bytes_to_format) { | 1332 | if (option_mask32 & OPT_N) { |
1376 | end_offset = n_bytes_to_skip + max_bytes_to_format; | 1333 | end_offset = n_bytes_to_skip + max_bytes_to_format; |
1377 | if (end_offset < n_bytes_to_skip) | 1334 | if (end_offset < n_bytes_to_skip) |
1378 | bb_error_msg_and_die("skip-bytes + read-bytes is too large"); | 1335 | bb_error_msg_and_die("SKIP + SIZE is too large"); |
1379 | } | 1336 | } |
1380 | 1337 | ||
1381 | if (n_specs == 0) { | 1338 | if (n_specs == 0) { |
1382 | decode_format_string("o2"); | 1339 | decode_format_string("o2"); |
1383 | n_specs = 1; | 1340 | /*n_specs = 1; - done by decode_format_string */ |
1384 | } | 1341 | } |
1385 | 1342 | ||
1386 | /* If no files were listed on the command line, | 1343 | /* If no files were listed on the command line, |
1387 | set the global pointer FILE_LIST so that it | 1344 | set the global pointer FILE_LIST so that it |
1388 | references the null-terminated list of one name: "-". */ | 1345 | references the null-terminated list of one name: "-". */ |
1389 | file_list = bb_argv_dash; | 1346 | file_list = bb_argv_dash; |
1390 | if (argc > 0) { | 1347 | if (argv[0]) { |
1391 | /* Set the global pointer FILE_LIST so that it | 1348 | /* Set the global pointer FILE_LIST so that it |
1392 | references the first file-argument on the command-line. */ | 1349 | references the first file-argument on the command-line. */ |
1393 | file_list = (char const *const *) argv; | 1350 | file_list = (char const *const *) argv; |
1394 | } | 1351 | } |
1395 | 1352 | ||
1396 | /* open the first input file */ | 1353 | /* Open the first input file */ |
1397 | open_next_file(); | 1354 | open_next_file(); |
1398 | /* skip over any unwanted header bytes */ | 1355 | /* Skip over any unwanted header bytes */ |
1399 | skip(n_bytes_to_skip); | 1356 | skip(n_bytes_to_skip); |
1400 | if (!in_stream) | 1357 | if (!in_stream) |
1401 | return EXIT_FAILURE; | 1358 | return EXIT_FAILURE; |
1402 | 1359 | ||
1403 | pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0); | 1360 | /* Compute output block length */ |
1404 | |||
1405 | /* Compute output block length. */ | ||
1406 | l_c_m = get_lcm(); | 1361 | l_c_m = get_lcm(); |
1407 | 1362 | ||
1408 | if (opt & OPT_w) { /* -w: width */ | 1363 | if (opt & OPT_w) { /* -w: width */ |
@@ -1424,13 +1379,13 @@ int od_main(int argc, char **argv) | |||
1424 | } | 1379 | } |
1425 | #endif | 1380 | #endif |
1426 | 1381 | ||
1427 | if (flag_dump_strings) | 1382 | if (option_mask32 & OPT_S) |
1428 | dump_strings(n_bytes_to_skip, end_offset); | 1383 | dump_strings(n_bytes_to_skip, end_offset); |
1429 | else | 1384 | else |
1430 | dump(n_bytes_to_skip, end_offset); | 1385 | dump(n_bytes_to_skip, end_offset); |
1431 | 1386 | ||
1432 | if (fclose(stdin) == EOF) | 1387 | if (fclose(stdin)) |
1433 | bb_perror_msg_and_die(bb_msg_standard_input); | 1388 | bb_perror_msg_and_die(bb_msg_standard_input); |
1434 | 1389 | ||
1435 | return ioerror; | 1390 | return exit_code; |
1436 | } | 1391 | } |
diff --git a/coreutils/tail.c b/coreutils/tail.c index eac982735..4b42ebc52 100644 --- a/coreutils/tail.c +++ b/coreutils/tail.c | |||
@@ -59,7 +59,8 @@ static const struct suffix_mult tail_suffixes[] = { | |||
59 | }; | 59 | }; |
60 | 60 | ||
61 | struct globals { | 61 | struct globals { |
62 | bool status; | 62 | bool from_top; |
63 | bool exitcode; | ||
63 | } FIX_ALIASING; | 64 | } FIX_ALIASING; |
64 | #define G (*(struct globals*)&bb_common_bufsiz1) | 65 | #define G (*(struct globals*)&bb_common_bufsiz1) |
65 | 66 | ||
@@ -85,7 +86,7 @@ static ssize_t tail_read(int fd, char *buf, size_t count) | |||
85 | r = full_read(fd, buf, count); | 86 | r = full_read(fd, buf, count); |
86 | if (r < 0) { | 87 | if (r < 0) { |
87 | bb_perror_msg(bb_msg_read_error); | 88 | bb_perror_msg(bb_msg_read_error); |
88 | G.status = EXIT_FAILURE; | 89 | G.exitcode = EXIT_FAILURE; |
89 | } | 90 | } |
90 | 91 | ||
91 | return r; | 92 | return r; |
@@ -99,7 +100,7 @@ static unsigned eat_num(const char *p) | |||
99 | p++; | 100 | p++; |
100 | else if (*p == '+') { | 101 | else if (*p == '+') { |
101 | p++; | 102 | p++; |
102 | G.status = 1; /* mark that we saw "+" */ | 103 | G.from_top = 1; |
103 | } | 104 | } |
104 | return xatou_sfx(p, tail_suffixes); | 105 | return xatou_sfx(p, tail_suffixes); |
105 | } | 106 | } |
@@ -109,7 +110,6 @@ int tail_main(int argc, char **argv) | |||
109 | { | 110 | { |
110 | unsigned count = 10; | 111 | unsigned count = 10; |
111 | unsigned sleep_period = 1; | 112 | unsigned sleep_period = 1; |
112 | bool from_top; | ||
113 | const char *str_c, *str_n; | 113 | const char *str_c, *str_n; |
114 | 114 | ||
115 | char *tailbuf; | 115 | char *tailbuf; |
@@ -152,8 +152,6 @@ int tail_main(int argc, char **argv) | |||
152 | #endif | 152 | #endif |
153 | argc -= optind; | 153 | argc -= optind; |
154 | argv += optind; | 154 | argv += optind; |
155 | from_top = G.status; /* 1 if there was "-c +N" or "-n +N" */ | ||
156 | G.status = EXIT_SUCCESS; | ||
157 | 155 | ||
158 | /* open all the files */ | 156 | /* open all the files */ |
159 | fds = xmalloc(sizeof(fds[0]) * (argc + 1)); | 157 | fds = xmalloc(sizeof(fds[0]) * (argc + 1)); |
@@ -171,7 +169,7 @@ int tail_main(int argc, char **argv) | |||
171 | do { | 169 | do { |
172 | int fd = open_or_warn_stdin(argv[i]); | 170 | int fd = open_or_warn_stdin(argv[i]); |
173 | if (fd < 0 && !FOLLOW_RETRY) { | 171 | if (fd < 0 && !FOLLOW_RETRY) { |
174 | G.status = EXIT_FAILURE; | 172 | G.exitcode = EXIT_FAILURE; |
175 | continue; | 173 | continue; |
176 | } | 174 | } |
177 | fds[nfiles] = fd; | 175 | fds[nfiles] = fd; |
@@ -183,15 +181,19 @@ int tail_main(int argc, char **argv) | |||
183 | 181 | ||
184 | /* prepare the buffer */ | 182 | /* prepare the buffer */ |
185 | tailbufsize = BUFSIZ; | 183 | tailbufsize = BUFSIZ; |
186 | if (!from_top && COUNT_BYTES) { | 184 | if (!G.from_top && COUNT_BYTES) { |
187 | if (tailbufsize < count + BUFSIZ) { | 185 | if (tailbufsize < count + BUFSIZ) { |
188 | tailbufsize = count + BUFSIZ; | 186 | tailbufsize = count + BUFSIZ; |
189 | } | 187 | } |
190 | } | 188 | } |
191 | tailbuf = xmalloc(tailbufsize); | 189 | /* tail -c1024m REGULAR_FILE doesn't really need 1G mem block. |
190 | * (In fact, it doesn't need ANY memory). So delay allocation. | ||
191 | */ | ||
192 | tailbuf = NULL; | ||
192 | 193 | ||
193 | /* tail the files */ | 194 | /* tail the files */ |
194 | fmt = header_fmt_str + 1; /* skip header leading newline on first output */ | 195 | |
196 | fmt = header_fmt_str + 1; /* skip leading newline in the header on the first output */ | ||
195 | i = 0; | 197 | i = 0; |
196 | do { | 198 | do { |
197 | char *buf; | 199 | char *buf; |
@@ -209,7 +211,7 @@ int tail_main(int argc, char **argv) | |||
209 | fmt = header_fmt_str; | 211 | fmt = header_fmt_str; |
210 | } | 212 | } |
211 | 213 | ||
212 | if (!from_top) { | 214 | if (!G.from_top) { |
213 | off_t current = lseek(fd, 0, SEEK_END); | 215 | off_t current = lseek(fd, 0, SEEK_END); |
214 | if (current > 0) { | 216 | if (current > 0) { |
215 | unsigned off; | 217 | unsigned off; |
@@ -242,6 +244,9 @@ int tail_main(int argc, char **argv) | |||
242 | } | 244 | } |
243 | } | 245 | } |
244 | 246 | ||
247 | if (!tailbuf) | ||
248 | tailbuf = xmalloc(tailbufsize); | ||
249 | |||
245 | buf = tailbuf; | 250 | buf = tailbuf; |
246 | taillen = 0; | 251 | taillen = 0; |
247 | /* "We saw 1st line/byte". | 252 | /* "We saw 1st line/byte". |
@@ -249,7 +254,7 @@ int tail_main(int argc, char **argv) | |||
249 | seen = 1; | 254 | seen = 1; |
250 | newlines_seen = 0; | 255 | newlines_seen = 0; |
251 | while ((nread = tail_read(fd, buf, tailbufsize-taillen)) > 0) { | 256 | while ((nread = tail_read(fd, buf, tailbufsize-taillen)) > 0) { |
252 | if (from_top) { | 257 | if (G.from_top) { |
253 | int nwrite = nread; | 258 | int nwrite = nread; |
254 | if (seen < count) { | 259 | if (seen < count) { |
255 | /* We need to skip a few more bytes/lines */ | 260 | /* We need to skip a few more bytes/lines */ |
@@ -313,7 +318,7 @@ int tail_main(int argc, char **argv) | |||
313 | buf = tailbuf + taillen; | 318 | buf = tailbuf + taillen; |
314 | } | 319 | } |
315 | } /* while (tail_read() > 0) */ | 320 | } /* while (tail_read() > 0) */ |
316 | if (!from_top) { | 321 | if (!G.from_top) { |
317 | xwrite(STDOUT_FILENO, tailbuf, taillen); | 322 | xwrite(STDOUT_FILENO, tailbuf, taillen); |
318 | } | 323 | } |
319 | } while (++i < nfiles); | 324 | } while (++i < nfiles); |
@@ -368,10 +373,11 @@ int tail_main(int argc, char **argv) | |||
368 | xwrite(STDOUT_FILENO, tailbuf, nread); | 373 | xwrite(STDOUT_FILENO, tailbuf, nread); |
369 | } | 374 | } |
370 | } while (++i < nfiles); | 375 | } while (++i < nfiles); |
371 | } | 376 | } /* while (1) */ |
377 | |||
372 | if (ENABLE_FEATURE_CLEAN_UP) { | 378 | if (ENABLE_FEATURE_CLEAN_UP) { |
373 | free(fds); | 379 | free(fds); |
374 | free(tailbuf); | 380 | free(tailbuf); |
375 | } | 381 | } |
376 | return G.status; | 382 | return G.exitcode; |
377 | } | 383 | } |
diff --git a/docs/style-guide.txt b/docs/style-guide.txt index fdf6cfe4d..10ed893dc 100644 --- a/docs/style-guide.txt +++ b/docs/style-guide.txt | |||
@@ -679,11 +679,10 @@ line in the midst of your #includes, if you need to parse long options: | |||
679 | 679 | ||
680 | Then have long options defined: | 680 | Then have long options defined: |
681 | 681 | ||
682 | static const struct option <applet>_long_options[] = { | 682 | static const char <applet>_longopts[] ALIGN1 = |
683 | { "list", 0, NULL, 't' }, | 683 | "list\0" No_argument "t" |
684 | { "extract", 0, NULL, 'x' }, | 684 | "extract\0" No_argument "x" |
685 | { NULL, 0, NULL, 0 } | 685 | ; |
686 | }; | ||
687 | 686 | ||
688 | And a code block similar to the following near the top of your applet_main() | 687 | And a code block similar to the following near the top of your applet_main() |
689 | routine: | 688 | routine: |
@@ -691,7 +690,7 @@ routine: | |||
691 | char *str_b; | 690 | char *str_b; |
692 | 691 | ||
693 | opt_complementary = "cryptic_string"; | 692 | opt_complementary = "cryptic_string"; |
694 | applet_long_options = <applet>_long_options; /* if you have them */ | 693 | applet_long_options = <applet>_longopts; /* if you have them */ |
695 | opt = getopt32(argc, argv, "ab:c", &str_b); | 694 | opt = getopt32(argc, argv, "ab:c", &str_b); |
696 | if (opt & 1) { | 695 | if (opt & 1) { |
697 | handle_option_a(); | 696 | handle_option_a(); |
diff --git a/e2fsprogs/fsck.c b/e2fsprogs/fsck.c index b4257a2ad..fcbdb5989 100644 --- a/e2fsprogs/fsck.c +++ b/e2fsprogs/fsck.c | |||
@@ -317,7 +317,6 @@ static void load_fs_info(const char *filename) | |||
317 | { | 317 | { |
318 | FILE *fstab; | 318 | FILE *fstab; |
319 | struct mntent mte; | 319 | struct mntent mte; |
320 | struct fs_info *fs; | ||
321 | 320 | ||
322 | fstab = setmntent(filename, "r"); | 321 | fstab = setmntent(filename, "r"); |
323 | if (!fstab) { | 322 | if (!fstab) { |
@@ -330,7 +329,7 @@ static void load_fs_info(const char *filename) | |||
330 | //bb_info_msg("CREATE[%s][%s][%s][%s][%d]", mte.mnt_fsname, mte.mnt_dir, | 329 | //bb_info_msg("CREATE[%s][%s][%s][%s][%d]", mte.mnt_fsname, mte.mnt_dir, |
331 | // mte.mnt_type, mte.mnt_opts, | 330 | // mte.mnt_type, mte.mnt_opts, |
332 | // mte.mnt_passno); | 331 | // mte.mnt_passno); |
333 | fs = create_fs_device(mte.mnt_fsname, mte.mnt_dir, | 332 | create_fs_device(mte.mnt_fsname, mte.mnt_dir, |
334 | mte.mnt_type, mte.mnt_opts, | 333 | mte.mnt_type, mte.mnt_opts, |
335 | mte.mnt_passno); | 334 | mte.mnt_passno); |
336 | } | 335 | } |
diff --git a/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c b/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c index 941efa42c..e1f6ba6d2 100644 --- a/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c +++ b/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c | |||
@@ -96,9 +96,9 @@ blkid_loff_t blkid_get_dev_size(int fd) | |||
96 | 96 | ||
97 | #ifdef BLKGETSIZE64 | 97 | #ifdef BLKGETSIZE64 |
98 | #ifdef __linux__ | 98 | #ifdef __linux__ |
99 | if ((uname(&ut) == 0) && | 99 | uname(&ut); |
100 | ((ut.release[0] == '2') && (ut.release[1] == '.') && | 100 | if ((ut.release[0] == '2') && (ut.release[1] == '.') && |
101 | (ut.release[2] < '6') && (ut.release[3] == '.'))) | 101 | (ut.release[2] < '6') && (ut.release[3] == '.')) |
102 | valid_blkgetsize64 = 0; | 102 | valid_blkgetsize64 = 0; |
103 | #endif | 103 | #endif |
104 | if (valid_blkgetsize64 && | 104 | if (valid_blkgetsize64 && |
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c b/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c index ff11fe98c..ee4bbb7b0 100644 --- a/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c +++ b/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c | |||
@@ -174,9 +174,9 @@ errcode_t ext2fs_get_device_size(const char *file, int blocksize, | |||
174 | 174 | ||
175 | #ifdef BLKGETSIZE64 | 175 | #ifdef BLKGETSIZE64 |
176 | #ifdef __linux__ | 176 | #ifdef __linux__ |
177 | if ((uname(&ut) == 0) && | 177 | uname(&ut); |
178 | ((ut.release[0] == '2') && (ut.release[1] == '.') && | 178 | if ((ut.release[0] == '2') && (ut.release[1] == '.') && |
179 | (ut.release[2] < '6') && (ut.release[3] == '.'))) | 179 | (ut.release[2] < '6') && (ut.release[3] == '.')) |
180 | valid_blkgetsize64 = 0; | 180 | valid_blkgetsize64 = 0; |
181 | #endif | 181 | #endif |
182 | if (valid_blkgetsize64 && | 182 | if (valid_blkgetsize64 && |
diff --git a/editors/diff.c b/editors/diff.c index daa58af9b..8b1e92783 100644 --- a/editors/diff.c +++ b/editors/diff.c | |||
@@ -101,9 +101,9 @@ | |||
101 | #include "libbb.h" | 101 | #include "libbb.h" |
102 | 102 | ||
103 | #if 0 | 103 | #if 0 |
104 | //#define dbg_error_msg(...) bb_error_msg(__VA_ARGS__) | 104 | # define dbg_error_msg(...) bb_error_msg(__VA_ARGS__) |
105 | #else | 105 | #else |
106 | #define dbg_error_msg(...) ((void)0) | 106 | # define dbg_error_msg(...) ((void)0) |
107 | #endif | 107 | #endif |
108 | 108 | ||
109 | enum { /* print_status() and diffreg() return values */ | 109 | enum { /* print_status() and diffreg() return values */ |
diff --git a/editors/patch.c b/editors/patch.c index a90252a03..6d3f319b0 100644 --- a/editors/patch.c +++ b/editors/patch.c | |||
@@ -239,7 +239,7 @@ static int apply_one_hunk(void) | |||
239 | plist = TT.current_hunk; | 239 | plist = TT.current_hunk; |
240 | buf = NULL; | 240 | buf = NULL; |
241 | if (reverse ? TT.oldlen : TT.newlen) for (;;) { | 241 | if (reverse ? TT.oldlen : TT.newlen) for (;;) { |
242 | char *data = xmalloc_reads(TT.filein, NULL, NULL); | 242 | char *data = xmalloc_reads(TT.filein, NULL); |
243 | 243 | ||
244 | TT.linenum++; | 244 | TT.linenum++; |
245 | 245 | ||
diff --git a/editors/sed.c b/editors/sed.c index 9ab758bd7..99e56ff52 100644 --- a/editors/sed.c +++ b/editors/sed.c | |||
@@ -14,49 +14,47 @@ | |||
14 | */ | 14 | */ |
15 | 15 | ||
16 | /* Code overview. | 16 | /* Code overview. |
17 | * | ||
18 | * Files are laid out to avoid unnecessary function declarations. So for | ||
19 | * example, every function add_cmd calls occurs before add_cmd in this file. | ||
20 | * | ||
21 | * add_cmd() is called on each line of sed command text (from a file or from | ||
22 | * the command line). It calls get_address() and parse_cmd_args(). The | ||
23 | * resulting sed_cmd_t structures are appended to a linked list | ||
24 | * (G.sed_cmd_head/G.sed_cmd_tail). | ||
25 | * | ||
26 | * add_input_file() adds a FILE* to the list of input files. We need to | ||
27 | * know all input sources ahead of time to find the last line for the $ match. | ||
28 | * | ||
29 | * process_files() does actual sedding, reading data lines from each input FILE * | ||
30 | * (which could be stdin) and applying the sed command list (sed_cmd_head) to | ||
31 | * each of the resulting lines. | ||
32 | * | ||
33 | * sed_main() is where external code calls into this, with a command line. | ||
34 | */ | ||
17 | 35 | ||
18 | Files are laid out to avoid unnecessary function declarations. So for | 36 | /* Supported features and commands in this version of sed: |
19 | example, every function add_cmd calls occurs before add_cmd in this file. | 37 | * |
20 | 38 | * - comments ('#') | |
21 | add_cmd() is called on each line of sed command text (from a file or from | 39 | * - address matching: num|/matchstr/[,num|/matchstr/|$]command |
22 | the command line). It calls get_address() and parse_cmd_args(). The | 40 | * - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags) |
23 | resulting sed_cmd_t structures are appended to a linked list | 41 | * - edit commands: (a)ppend, (i)nsert, (c)hange |
24 | (G.sed_cmd_head/G.sed_cmd_tail). | 42 | * - file commands: (r)ead |
25 | 43 | * - backreferences in substitution expressions (\0, \1, \2...\9) | |
26 | add_input_file() adds a FILE* to the list of input files. We need to | 44 | * - grouped commands: {cmd1;cmd2} |
27 | know all input sources ahead of time to find the last line for the $ match. | 45 | * - transliteration (y/source-chars/dest-chars/) |
28 | 46 | * - pattern space hold space storing / swapping (g, h, x) | |
29 | process_files() does actual sedding, reading data lines from each input FILE * | 47 | * - labels / branching (: label, b, t, T) |
30 | (which could be stdin) and applying the sed command list (sed_cmd_head) to | 48 | * |
31 | each of the resulting lines. | 49 | * (Note: Specifying an address (range) to match is *optional*; commands |
32 | 50 | * default to the whole pattern space if no specific address match was | |
33 | sed_main() is where external code calls into this, with a command line. | 51 | * requested.) |
34 | */ | 52 | * |
35 | 53 | * Todo: | |
36 | 54 | * - Create a wrapper around regex to make libc's regex conform with sed | |
37 | /* | 55 | * |
38 | Supported features and commands in this version of sed: | 56 | * Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html |
39 | 57 | */ | |
40 | - comments ('#') | ||
41 | - address matching: num|/matchstr/[,num|/matchstr/|$]command | ||
42 | - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags) | ||
43 | - edit commands: (a)ppend, (i)nsert, (c)hange | ||
44 | - file commands: (r)ead | ||
45 | - backreferences in substitution expressions (\0, \1, \2...\9) | ||
46 | - grouped commands: {cmd1;cmd2} | ||
47 | - transliteration (y/source-chars/dest-chars/) | ||
48 | - pattern space hold space storing / swapping (g, h, x) | ||
49 | - labels / branching (: label, b, t, T) | ||
50 | |||
51 | (Note: Specifying an address (range) to match is *optional*; commands | ||
52 | default to the whole pattern space if no specific address match was | ||
53 | requested.) | ||
54 | |||
55 | Todo: | ||
56 | - Create a wrapper around regex to make libc's regex conform with sed | ||
57 | |||
58 | Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html | ||
59 | */ | ||
60 | 58 | ||
61 | //usage:#define sed_trivial_usage | 59 | //usage:#define sed_trivial_usage |
62 | //usage: "[-efinr] SED_CMD [FILE]..." | 60 | //usage: "[-efinr] SED_CMD [FILE]..." |
@@ -217,11 +215,16 @@ static void parse_escapes(char *dest, const char *string, int len, char from, ch | |||
217 | 215 | ||
218 | static char *copy_parsing_escapes(const char *string, int len) | 216 | static char *copy_parsing_escapes(const char *string, int len) |
219 | { | 217 | { |
218 | const char *s; | ||
220 | char *dest = xmalloc(len + 1); | 219 | char *dest = xmalloc(len + 1); |
221 | 220 | ||
222 | parse_escapes(dest, string, len, 'n', '\n'); | 221 | /* sed recognizes \n */ |
223 | /* GNU sed also recognizes \t */ | 222 | /* GNU sed also recognizes \t and \r */ |
224 | parse_escapes(dest, dest, strlen(dest), 't', '\t'); | 223 | for (s = "\nn\tt\rr"; *s; s += 2) { |
224 | parse_escapes(dest, string, len, s[1], s[0]); | ||
225 | string = dest; | ||
226 | len = strlen(dest); | ||
227 | } | ||
225 | return dest; | 228 | return dest; |
226 | } | 229 | } |
227 | 230 | ||
@@ -244,11 +247,13 @@ static int index_of_next_unescaped_regexp_delim(int delimiter, const char *str) | |||
244 | delimiter = -delimiter; | 247 | delimiter = -delimiter; |
245 | } | 248 | } |
246 | 249 | ||
247 | for (; (ch = str[idx]); idx++) { | 250 | for (; (ch = str[idx]) != '\0'; idx++) { |
248 | if (bracket >= 0) { | 251 | if (bracket >= 0) { |
249 | if (ch == ']' && !(bracket == idx - 1 || (bracket == idx - 2 | 252 | if (ch == ']' |
250 | && str[idx - 1] == '^'))) | 253 | && !(bracket == idx - 1 || (bracket == idx - 2 && str[idx - 1] == '^')) |
254 | ) { | ||
251 | bracket = -1; | 255 | bracket = -1; |
256 | } | ||
252 | } else if (escaped) | 257 | } else if (escaped) |
253 | escaped = 0; | 258 | escaped = 0; |
254 | else if (ch == '\\') | 259 | else if (ch == '\\') |
@@ -434,11 +439,47 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr) | |||
434 | */ | 439 | */ |
435 | static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) | 440 | static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) |
436 | { | 441 | { |
442 | static const char cmd_letters[] = "saicrw:btTydDgGhHlnNpPqx={}"; | ||
443 | enum { | ||
444 | IDX_s = 0, | ||
445 | IDX_a, | ||
446 | IDX_i, | ||
447 | IDX_c, | ||
448 | IDX_r, | ||
449 | IDX_w, | ||
450 | IDX_colon, | ||
451 | IDX_b, | ||
452 | IDX_t, | ||
453 | IDX_T, | ||
454 | IDX_y, | ||
455 | IDX_d, | ||
456 | IDX_D, | ||
457 | IDX_g, | ||
458 | IDX_G, | ||
459 | IDX_h, | ||
460 | IDX_H, | ||
461 | IDX_l, | ||
462 | IDX_n, | ||
463 | IDX_N, | ||
464 | IDX_p, | ||
465 | IDX_P, | ||
466 | IDX_q, | ||
467 | IDX_x, | ||
468 | IDX_equal, | ||
469 | IDX_lbrace, | ||
470 | IDX_rbrace, | ||
471 | IDX_nul | ||
472 | }; | ||
473 | struct chk { char chk[sizeof(cmd_letters)-1 == IDX_nul ? 1 : -1]; }; | ||
474 | |||
475 | unsigned idx = strchrnul(cmd_letters, sed_cmd->cmd) - cmd_letters; | ||
476 | |||
437 | /* handle (s)ubstitution command */ | 477 | /* handle (s)ubstitution command */ |
438 | if (sed_cmd->cmd == 's') | 478 | if (idx == IDX_s) { |
439 | cmdstr += parse_subst_cmd(sed_cmd, cmdstr); | 479 | cmdstr += parse_subst_cmd(sed_cmd, cmdstr); |
480 | } | ||
440 | /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */ | 481 | /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */ |
441 | else if (strchr("aic", sed_cmd->cmd)) { | 482 | else if (idx <= IDX_c) { /* a,i,c */ |
442 | if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c') | 483 | if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c') |
443 | bb_error_msg_and_die("only a beginning address can be specified for edit commands"); | 484 | bb_error_msg_and_die("only a beginning address can be specified for edit commands"); |
444 | for (;;) { | 485 | for (;;) { |
@@ -454,8 +495,9 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) | |||
454 | /* "\anychar" -> "anychar" */ | 495 | /* "\anychar" -> "anychar" */ |
455 | parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), '\0', '\0'); | 496 | parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), '\0', '\0'); |
456 | cmdstr += strlen(cmdstr); | 497 | cmdstr += strlen(cmdstr); |
498 | } | ||
457 | /* handle file cmds: (r)ead */ | 499 | /* handle file cmds: (r)ead */ |
458 | } else if (strchr("rw", sed_cmd->cmd)) { | 500 | else if (idx <= IDX_w) { /* r,w */ |
459 | if (sed_cmd->end_line || sed_cmd->end_match) | 501 | if (sed_cmd->end_line || sed_cmd->end_match) |
460 | bb_error_msg_and_die("command only uses one address"); | 502 | bb_error_msg_and_die("command only uses one address"); |
461 | cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string); | 503 | cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string); |
@@ -463,8 +505,9 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) | |||
463 | sed_cmd->sw_file = xfopen_for_write(sed_cmd->string); | 505 | sed_cmd->sw_file = xfopen_for_write(sed_cmd->string); |
464 | sed_cmd->sw_last_char = '\n'; | 506 | sed_cmd->sw_last_char = '\n'; |
465 | } | 507 | } |
508 | } | ||
466 | /* handle branch commands */ | 509 | /* handle branch commands */ |
467 | } else if (strchr(":btT", sed_cmd->cmd)) { | 510 | else if (idx <= IDX_T) { /* :,b,t,T */ |
468 | int length; | 511 | int length; |
469 | 512 | ||
470 | cmdstr = skip_whitespace(cmdstr); | 513 | cmdstr = skip_whitespace(cmdstr); |
@@ -475,7 +518,7 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) | |||
475 | } | 518 | } |
476 | } | 519 | } |
477 | /* translation command */ | 520 | /* translation command */ |
478 | else if (sed_cmd->cmd == 'y') { | 521 | else if (idx == IDX_y) { |
479 | char *match, *replace; | 522 | char *match, *replace; |
480 | int i = cmdstr[0]; | 523 | int i = cmdstr[0]; |
481 | 524 | ||
@@ -495,7 +538,7 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) | |||
495 | /* if it wasnt a single-letter command that takes no arguments | 538 | /* if it wasnt a single-letter command that takes no arguments |
496 | * then it must be an invalid command. | 539 | * then it must be an invalid command. |
497 | */ | 540 | */ |
498 | else if (strchr("dDgGhHlnNpPqx={}", sed_cmd->cmd) == 0) { | 541 | else if (idx >= IDX_nul) { /* not d,D,g,G,h,H,l,n,N,p,P,q,x,=,{,} */ |
499 | bb_error_msg_and_die("unsupported command %c", sed_cmd->cmd); | 542 | bb_error_msg_and_die("unsupported command %c", sed_cmd->cmd); |
500 | } | 543 | } |
501 | 544 | ||
@@ -966,9 +1009,9 @@ static void process_files(void) | |||
966 | } | 1009 | } |
967 | sed_cmd->in_match = !( | 1010 | sed_cmd->in_match = !( |
968 | /* has the ending line come, or is this a single address command? */ | 1011 | /* has the ending line come, or is this a single address command? */ |
969 | (sed_cmd->end_line ? | 1012 | (sed_cmd->end_line |
970 | sed_cmd->end_line == -1 ? | 1013 | ? sed_cmd->end_line == -1 |
971 | !next_line | 1014 | ? !next_line |
972 | : (sed_cmd->end_line <= linenum) | 1015 | : (sed_cmd->end_line <= linenum) |
973 | : !sed_cmd->end_match | 1016 | : !sed_cmd->end_match |
974 | ) | 1017 | ) |
diff --git a/editors/vi.c b/editors/vi.c index 712af0326..9d74acc91 100644 --- a/editors/vi.c +++ b/editors/vi.c | |||
@@ -536,7 +536,6 @@ static void edit_file(char *fn) | |||
536 | #define cur_line edit_file__cur_line | 536 | #define cur_line edit_file__cur_line |
537 | #endif | 537 | #endif |
538 | int c; | 538 | int c; |
539 | int size; | ||
540 | #if ENABLE_FEATURE_VI_USE_SIGNALS | 539 | #if ENABLE_FEATURE_VI_USE_SIGNALS |
541 | int sig; | 540 | int sig; |
542 | #endif | 541 | #endif |
@@ -545,7 +544,6 @@ static void edit_file(char *fn) | |||
545 | rawmode(); | 544 | rawmode(); |
546 | rows = 24; | 545 | rows = 24; |
547 | columns = 80; | 546 | columns = 80; |
548 | size = 0; | ||
549 | IF_FEATURE_VI_ASK_TERMINAL(G.get_rowcol_error =) query_screen_dimensions(); | 547 | IF_FEATURE_VI_ASK_TERMINAL(G.get_rowcol_error =) query_screen_dimensions(); |
550 | #if ENABLE_FEATURE_VI_ASK_TERMINAL | 548 | #if ENABLE_FEATURE_VI_ASK_TERMINAL |
551 | if (G.get_rowcol_error /* TODO? && no input on stdin */) { | 549 | if (G.get_rowcol_error /* TODO? && no input on stdin */) { |
@@ -1690,12 +1688,16 @@ static char *char_insert(char *p, char c) // insert the char c at 'p' | |||
1690 | p = text_hole_delete(p, p); // shrink buffer 1 char | 1688 | p = text_hole_delete(p, p); // shrink buffer 1 char |
1691 | } | 1689 | } |
1692 | } else { | 1690 | } else { |
1691 | #if ENABLE_FEATURE_VI_SETOPTS | ||
1693 | // insert a char into text[] | 1692 | // insert a char into text[] |
1694 | char *sp; // "save p" | 1693 | char *sp; // "save p" |
1694 | #endif | ||
1695 | 1695 | ||
1696 | if (c == 13) | 1696 | if (c == 13) |
1697 | c = '\n'; // translate \r to \n | 1697 | c = '\n'; // translate \r to \n |
1698 | #if ENABLE_FEATURE_VI_SETOPTS | ||
1698 | sp = p; // remember addr of insert | 1699 | sp = p; // remember addr of insert |
1700 | #endif | ||
1699 | p += 1 + stupid_insert(p, c); // insert the char | 1701 | p += 1 + stupid_insert(p, c); // insert the char |
1700 | #if ENABLE_FEATURE_VI_SETOPTS | 1702 | #if ENABLE_FEATURE_VI_SETOPTS |
1701 | if (showmatch && strchr(")]}", *sp) != NULL) { | 1703 | if (showmatch && strchr(")]}", *sp) != NULL) { |
diff --git a/examples/udhcp/udhcpd.conf b/examples/udhcp/udhcpd.conf index 23fc834b7..cd2957ccc 100644 --- a/examples/udhcp/udhcpd.conf +++ b/examples/udhcp/udhcpd.conf | |||
@@ -14,11 +14,6 @@ interface eth0 | |||
14 | # smaller than lease block. | 14 | # smaller than lease block. |
15 | #max_leases 254 | 15 | #max_leases 254 |
16 | 16 | ||
17 | # The time period at which udhcpd will write out a dhcpd.leases | ||
18 | # file. If this is 0, udhcpd will never automatically write a | ||
19 | # lease file. Specified in seconds. | ||
20 | #auto_time 7200 | ||
21 | |||
22 | # The amount of time that an IP will be reserved (leased to nobody) | 17 | # The amount of time that an IP will be reserved (leased to nobody) |
23 | # if a DHCP decline message is received (seconds) | 18 | # if a DHCP decline message is received (seconds) |
24 | #decline_time 3600 | 19 | #decline_time 3600 |
@@ -34,11 +29,16 @@ interface eth0 | |||
34 | # to this value (seconds) | 29 | # to this value (seconds) |
35 | #min_lease 60 | 30 | #min_lease 60 |
36 | 31 | ||
32 | # The location of the pid file | ||
33 | #pidfile /var/run/udhcpd.pid | ||
34 | |||
37 | # The location of the leases file | 35 | # The location of the leases file |
38 | #lease_file /var/lib/misc/udhcpd.leases | 36 | #lease_file /var/lib/misc/udhcpd.leases |
39 | 37 | ||
40 | # The location of the pid file | 38 | # The time period at which udhcpd will write out leases file. |
41 | #pidfile /var/run/udhcpd.pid | 39 | # If this is 0, udhcpd will never automatically write leases file. |
40 | # Specified in seconds. | ||
41 | #auto_time 7200 | ||
42 | 42 | ||
43 | # Every time udhcpd writes a leases file, the below script will be called | 43 | # Every time udhcpd writes a leases file, the below script will be called |
44 | #notify_file # default: no script | 44 | #notify_file # default: no script |
@@ -90,6 +90,8 @@ option 0x08 01020304 # option 8: "cookie server IP addr: 1.2.3.4" | |||
90 | #opt wpad STRING | 90 | #opt wpad STRING |
91 | #opt serverid IP # default: server's IP | 91 | #opt serverid IP # default: server's IP |
92 | #opt message STRING # error message (udhcpd sends it on success too) | 92 | #opt message STRING # error message (udhcpd sends it on success too) |
93 | #opt vlanid NUM # 802.1P VLAN ID | ||
94 | #opt vlanpriority NUM # 802.1Q VLAN priority | ||
93 | # Options specifying server(s) | 95 | # Options specifying server(s) |
94 | #opt dns IP_LIST | 96 | #opt dns IP_LIST |
95 | #opt wins IP_LIST | 97 | #opt wins IP_LIST |
@@ -97,8 +99,15 @@ option 0x08 01020304 # option 8: "cookie server IP addr: 1.2.3.4" | |||
97 | #opt ntpsrv IP_LIST | 99 | #opt ntpsrv IP_LIST |
98 | #opt lprsrv IP_LIST | 100 | #opt lprsrv IP_LIST |
99 | #opt swapsrv IP | 101 | #opt swapsrv IP |
102 | # Options specifying routes | ||
103 | #opt routes IP_PAIR_LIST | ||
100 | # Obsolete options, no longer supported | 104 | # Obsolete options, no longer supported |
101 | #opt logsrv IP_LIST # 704/UDP log server (not syslog!) | 105 | #opt logsrv IP_LIST # 704/UDP log server (not syslog!) |
102 | #opt namesrv IP_LIST # IEN 116 name server, obsolete (August 1979!!!) | 106 | #opt namesrv IP_LIST # IEN 116 name server, obsolete (August 1979!!!) |
103 | #opt cookiesrv IP_LIST # RFC 865 "quote of the day" server, rarely (never?) used | 107 | #opt cookiesrv IP_LIST # RFC 865 "quote of the day" server, rarely (never?) used |
104 | #opt timesrv IP_LIST # RFC 868 time server, rarely (never?) used | 108 | #opt timesrv IP_LIST # RFC 868 time server, rarely (never?) used |
109 | # TODO: in development | ||
110 | #opt userclass STRING # RFC 3004. set of LASCII strings. "I am a printer" etc | ||
111 | #opt sipserv STRING LIST # RFC 3361. flag byte, then: 0: domain names, 1: IP addrs | ||
112 | #opt staticroutes STATIC_ROUTES | ||
113 | #opt msstaticroutes STATIC_ROUTES | ||
diff --git a/findutils/find.c b/findutils/find.c index 9ae84fa0d..050d6373e 100644 --- a/findutils/find.c +++ b/findutils/find.c | |||
@@ -264,6 +264,7 @@ | |||
264 | //usage: "\n -iname PATTERN Case insensitive -name" | 264 | //usage: "\n -iname PATTERN Case insensitive -name" |
265 | //usage: IF_FEATURE_FIND_PATH( | 265 | //usage: IF_FEATURE_FIND_PATH( |
266 | //usage: "\n -path PATTERN Match path to PATTERN" | 266 | //usage: "\n -path PATTERN Match path to PATTERN" |
267 | //usage: "\n -ipath PATTERN Case insensitive -path" | ||
267 | //usage: ) | 268 | //usage: ) |
268 | //usage: IF_FEATURE_FIND_REGEX( | 269 | //usage: IF_FEATURE_FIND_REGEX( |
269 | //usage: "\n -regex PATTERN Match path to regex PATTERN" | 270 | //usage: "\n -regex PATTERN Match path to regex PATTERN" |
@@ -329,7 +330,11 @@ | |||
329 | #include <fnmatch.h> | 330 | #include <fnmatch.h> |
330 | #include "libbb.h" | 331 | #include "libbb.h" |
331 | #if ENABLE_FEATURE_FIND_REGEX | 332 | #if ENABLE_FEATURE_FIND_REGEX |
332 | #include "xregex.h" | 333 | # include "xregex.h" |
334 | #endif | ||
335 | /* GNUism: */ | ||
336 | #ifndef FNM_CASEFOLD | ||
337 | # define FNM_CASEFOLD 0 | ||
333 | #endif | 338 | #endif |
334 | 339 | ||
335 | /* This is a NOEXEC applet. Be very careful! */ | 340 | /* This is a NOEXEC applet. Be very careful! */ |
@@ -352,7 +357,7 @@ typedef struct { | |||
352 | 357 | ||
353 | ACTS(print) | 358 | ACTS(print) |
354 | ACTS(name, const char *pattern; bool iname;) | 359 | ACTS(name, const char *pattern; bool iname;) |
355 | IF_FEATURE_FIND_PATH( ACTS(path, const char *pattern;)) | 360 | IF_FEATURE_FIND_PATH( ACTS(path, const char *pattern; bool ipath;)) |
356 | IF_FEATURE_FIND_REGEX( ACTS(regex, regex_t compiled_pattern;)) | 361 | IF_FEATURE_FIND_REGEX( ACTS(regex, regex_t compiled_pattern;)) |
357 | IF_FEATURE_FIND_PRINT0( ACTS(print0)) | 362 | IF_FEATURE_FIND_PRINT0( ACTS(print0)) |
358 | IF_FEATURE_FIND_TYPE( ACTS(type, int type_mask;)) | 363 | IF_FEATURE_FIND_TYPE( ACTS(type, int type_mask;)) |
@@ -473,6 +478,22 @@ static int exec_actions(action ***appp, const char *fileName, const struct stat | |||
473 | } | 478 | } |
474 | 479 | ||
475 | 480 | ||
481 | #if !FNM_CASEFOLD | ||
482 | static char *strcpy_upcase(char *dst, const char *src) | ||
483 | { | ||
484 | char *d = dst; | ||
485 | while (1) { | ||
486 | unsigned char ch = *src++; | ||
487 | if (ch >= 'a' && ch <= 'z') | ||
488 | ch -= ('a' - 'A'); | ||
489 | *d++ = ch; | ||
490 | if (ch == '\0') | ||
491 | break; | ||
492 | } | ||
493 | return dst; | ||
494 | } | ||
495 | #endif | ||
496 | |||
476 | ACTF(name) | 497 | ACTF(name) |
477 | { | 498 | { |
478 | const char *tmp = bb_basename(fileName); | 499 | const char *tmp = bb_basename(fileName); |
@@ -488,13 +509,25 @@ ACTF(name) | |||
488 | * but somewhere between 4.1.20 and 4.4.0 GNU find stopped using it. | 509 | * but somewhere between 4.1.20 and 4.4.0 GNU find stopped using it. |
489 | * find -name '*foo' should match .foo too: | 510 | * find -name '*foo' should match .foo too: |
490 | */ | 511 | */ |
512 | #if FNM_CASEFOLD | ||
491 | return fnmatch(ap->pattern, tmp, (ap->iname ? FNM_CASEFOLD : 0)) == 0; | 513 | return fnmatch(ap->pattern, tmp, (ap->iname ? FNM_CASEFOLD : 0)) == 0; |
514 | #else | ||
515 | if (ap->iname) | ||
516 | tmp = strcpy_upcase(alloca(strlen(tmp) + 1), tmp); | ||
517 | return fnmatch(ap->pattern, tmp, 0) == 0; | ||
518 | #endif | ||
492 | } | 519 | } |
493 | 520 | ||
494 | #if ENABLE_FEATURE_FIND_PATH | 521 | #if ENABLE_FEATURE_FIND_PATH |
495 | ACTF(path) | 522 | ACTF(path) |
496 | { | 523 | { |
524 | # if FNM_CASEFOLD | ||
525 | return fnmatch(ap->pattern, fileName, (ap->ipath ? FNM_CASEFOLD : 0)) == 0; | ||
526 | # else | ||
527 | if (ap->ipath) | ||
528 | fileName = strcpy_upcase(alloca(strlen(fileName) + 1), fileName); | ||
497 | return fnmatch(ap->pattern, fileName, 0) == 0; | 529 | return fnmatch(ap->pattern, fileName, 0) == 0; |
530 | # endif | ||
498 | } | 531 | } |
499 | #endif | 532 | #endif |
500 | #if ENABLE_FEATURE_FIND_REGEX | 533 | #if ENABLE_FEATURE_FIND_REGEX |
@@ -794,6 +827,7 @@ static action*** parse_params(char **argv) | |||
794 | PARM_name , | 827 | PARM_name , |
795 | PARM_iname , | 828 | PARM_iname , |
796 | IF_FEATURE_FIND_PATH( PARM_path ,) | 829 | IF_FEATURE_FIND_PATH( PARM_path ,) |
830 | IF_FEATURE_FIND_PATH( PARM_ipath ,) | ||
797 | IF_FEATURE_FIND_REGEX( PARM_regex ,) | 831 | IF_FEATURE_FIND_REGEX( PARM_regex ,) |
798 | IF_FEATURE_FIND_TYPE( PARM_type ,) | 832 | IF_FEATURE_FIND_TYPE( PARM_type ,) |
799 | IF_FEATURE_FIND_PERM( PARM_perm ,) | 833 | IF_FEATURE_FIND_PERM( PARM_perm ,) |
@@ -831,6 +865,7 @@ static action*** parse_params(char **argv) | |||
831 | "-name\0" | 865 | "-name\0" |
832 | "-iname\0" | 866 | "-iname\0" |
833 | IF_FEATURE_FIND_PATH( "-path\0" ) | 867 | IF_FEATURE_FIND_PATH( "-path\0" ) |
868 | IF_FEATURE_FIND_PATH( "-ipath\0" ) | ||
834 | IF_FEATURE_FIND_REGEX( "-regex\0" ) | 869 | IF_FEATURE_FIND_REGEX( "-regex\0" ) |
835 | IF_FEATURE_FIND_TYPE( "-type\0" ) | 870 | IF_FEATURE_FIND_TYPE( "-type\0" ) |
836 | IF_FEATURE_FIND_PERM( "-perm\0" ) | 871 | IF_FEATURE_FIND_PERM( "-perm\0" ) |
@@ -1018,10 +1053,11 @@ static action*** parse_params(char **argv) | |||
1018 | ap->iname = (parm == PARM_iname); | 1053 | ap->iname = (parm == PARM_iname); |
1019 | } | 1054 | } |
1020 | #if ENABLE_FEATURE_FIND_PATH | 1055 | #if ENABLE_FEATURE_FIND_PATH |
1021 | else if (parm == PARM_path) { | 1056 | else if (parm == PARM_path || parm == PARM_ipath) { |
1022 | action_path *ap; | 1057 | action_path *ap; |
1023 | ap = ALLOC_ACTION(path); | 1058 | ap = ALLOC_ACTION(path); |
1024 | ap->pattern = arg1; | 1059 | ap->pattern = arg1; |
1060 | ap->ipath = (parm == PARM_ipath); | ||
1025 | } | 1061 | } |
1026 | #endif | 1062 | #endif |
1027 | #if ENABLE_FEATURE_FIND_REGEX | 1063 | #if ENABLE_FEATURE_FIND_REGEX |
diff --git a/include/archive.h b/include/archive.h index b139dc5be..9e176d335 100644 --- a/include/archive.h +++ b/include/archive.h | |||
@@ -125,7 +125,7 @@ typedef struct archive_handle_t { | |||
125 | #define TAR_BLOCK_SIZE 512 | 125 | #define TAR_BLOCK_SIZE 512 |
126 | #define NAME_SIZE 100 | 126 | #define NAME_SIZE 100 |
127 | #define NAME_SIZE_STR "100" | 127 | #define NAME_SIZE_STR "100" |
128 | typedef struct tar_header_t { /* byte offset */ | 128 | typedef struct tar_header_t { /* byte offset */ |
129 | char name[NAME_SIZE]; /* 0-99 */ | 129 | char name[NAME_SIZE]; /* 0-99 */ |
130 | char mode[8]; /* 100-107 */ | 130 | char mode[8]; /* 100-107 */ |
131 | char uid[8]; /* 108-115 */ | 131 | char uid[8]; /* 108-115 */ |
diff --git a/include/libbb.h b/include/libbb.h index 4f46cf8c5..ac5722c89 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -20,6 +20,12 @@ | |||
20 | #include <netdb.h> | 20 | #include <netdb.h> |
21 | #include <setjmp.h> | 21 | #include <setjmp.h> |
22 | #include <signal.h> | 22 | #include <signal.h> |
23 | #if defined __UCLIBC__ /* TODO: and glibc? */ | ||
24 | /* use inlined versions of these: */ | ||
25 | # define sigfillset(s) __sigfillset(s) | ||
26 | # define sigemptyset(s) __sigemptyset(s) | ||
27 | # define sigisemptyset(s) __sigisemptyset(s) | ||
28 | #endif | ||
23 | #include <stdint.h> | 29 | #include <stdint.h> |
24 | #include <stdio.h> | 30 | #include <stdio.h> |
25 | #include <stdlib.h> | 31 | #include <stdlib.h> |
@@ -363,6 +369,7 @@ extern void bb_copyfd_exact_size(int fd1, int fd2, off_t size) FAST_FUNC; | |||
363 | /* "short" copy can be detected by return value < size */ | 369 | /* "short" copy can be detected by return value < size */ |
364 | /* this helper yells "short read!" if param is not -1 */ | 370 | /* this helper yells "short read!" if param is not -1 */ |
365 | extern void complain_copyfd_and_die(off_t sz) NORETURN FAST_FUNC; | 371 | extern void complain_copyfd_and_die(off_t sz) NORETURN FAST_FUNC; |
372 | |||
366 | extern char bb_process_escape_sequence(const char **ptr) FAST_FUNC; | 373 | extern char bb_process_escape_sequence(const char **ptr) FAST_FUNC; |
367 | char* strcpy_and_process_escape_sequences(char *dst, const char *src) FAST_FUNC; | 374 | char* strcpy_and_process_escape_sequences(char *dst, const char *src) FAST_FUNC; |
368 | /* xxxx_strip version can modify its parameter: | 375 | /* xxxx_strip version can modify its parameter: |
@@ -371,9 +378,13 @@ char* strcpy_and_process_escape_sequences(char *dst, const char *src) FAST_FUNC; | |||
371 | * "abc/def" -> "def" | 378 | * "abc/def" -> "def" |
372 | * "abc/def/" -> "def" !! | 379 | * "abc/def/" -> "def" !! |
373 | */ | 380 | */ |
374 | extern char *bb_get_last_path_component_strip(char *path) FAST_FUNC; | 381 | char *bb_get_last_path_component_strip(char *path) FAST_FUNC; |
375 | /* "abc/def/" -> "" and it never modifies 'path' */ | 382 | /* "abc/def/" -> "" and it never modifies 'path' */ |
376 | extern char *bb_get_last_path_component_nostrip(const char *path) FAST_FUNC; | 383 | char *bb_get_last_path_component_nostrip(const char *path) FAST_FUNC; |
384 | /* Simpler version: does not special case "/" string */ | ||
385 | const char *bb_basename(const char *name) FAST_FUNC; | ||
386 | /* NB: can violate const-ness (similarly to strchr) */ | ||
387 | char *last_char_is(const char *s, int c) FAST_FUNC; | ||
377 | 388 | ||
378 | void ndelay_on(int fd) FAST_FUNC; | 389 | void ndelay_on(int fd) FAST_FUNC; |
379 | void ndelay_off(int fd) FAST_FUNC; | 390 | void ndelay_off(int fd) FAST_FUNC; |
@@ -685,7 +696,7 @@ void* xrealloc_vector_helper(void *vector, unsigned sizeof_and_shift, int idx) F | |||
685 | 696 | ||
686 | 697 | ||
687 | extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC; | 698 | extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC; |
688 | extern ssize_t nonblock_safe_read(int fd, void *buf, size_t count) FAST_FUNC; | 699 | extern ssize_t nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR) FAST_FUNC; |
689 | // NB: will return short read on error, not -1, | 700 | // NB: will return short read on error, not -1, |
690 | // if some data was read before error occurred | 701 | // if some data was read before error occurred |
691 | extern ssize_t full_read(int fd, void *buf, size_t count) FAST_FUNC; | 702 | extern ssize_t full_read(int fd, void *buf, size_t count) FAST_FUNC; |
@@ -696,7 +707,7 @@ extern ssize_t open_read_close(const char *filename, void *buf, size_t maxsz) FA | |||
696 | // Reads one line a-la fgets (but doesn't save terminating '\n'). | 707 | // Reads one line a-la fgets (but doesn't save terminating '\n'). |
697 | // Reads byte-by-byte. Useful when it is important to not read ahead. | 708 | // Reads byte-by-byte. Useful when it is important to not read ahead. |
698 | // Bytes are appended to pfx (which must be malloced, or NULL). | 709 | // Bytes are appended to pfx (which must be malloced, or NULL). |
699 | extern char *xmalloc_reads(int fd, char *pfx, size_t *maxsz_p) FAST_FUNC; | 710 | extern char *xmalloc_reads(int fd, size_t *maxsz_p) FAST_FUNC; |
700 | /* Reads block up to *maxsz_p (default: INT_MAX - 4095) */ | 711 | /* Reads block up to *maxsz_p (default: INT_MAX - 4095) */ |
701 | extern void *xmalloc_read(int fd, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; | 712 | extern void *xmalloc_read(int fd, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; |
702 | /* Returns NULL if file can't be opened (default max size: INT_MAX - 4095) */ | 713 | /* Returns NULL if file can't be opened (default max size: INT_MAX - 4095) */ |
@@ -997,9 +1008,13 @@ extern uint32_t option_mask32; | |||
997 | extern uint32_t getopt32(char **argv, const char *applet_opts, ...) FAST_FUNC; | 1008 | extern uint32_t getopt32(char **argv, const char *applet_opts, ...) FAST_FUNC; |
998 | 1009 | ||
999 | 1010 | ||
1011 | /* Having next pointer as a first member allows easy creation | ||
1012 | * of "llist-compatible" structs, and using llist_FOO functions | ||
1013 | * on them. | ||
1014 | */ | ||
1000 | typedef struct llist_t { | 1015 | typedef struct llist_t { |
1001 | char *data; | ||
1002 | struct llist_t *link; | 1016 | struct llist_t *link; |
1017 | char *data; | ||
1003 | } llist_t; | 1018 | } llist_t; |
1004 | void llist_add_to(llist_t **old_head, void *data) FAST_FUNC; | 1019 | void llist_add_to(llist_t **old_head, void *data) FAST_FUNC; |
1005 | void llist_add_to_end(llist_t **list_head, void *data) FAST_FUNC; | 1020 | void llist_add_to_end(llist_t **list_head, void *data) FAST_FUNC; |
@@ -1198,10 +1213,8 @@ void config_close(parser_t *parser) FAST_FUNC; | |||
1198 | * If path is NULL, it is assumed to be "/". | 1213 | * If path is NULL, it is assumed to be "/". |
1199 | * filename should not be NULL. */ | 1214 | * filename should not be NULL. */ |
1200 | char *concat_path_file(const char *path, const char *filename) FAST_FUNC; | 1215 | char *concat_path_file(const char *path, const char *filename) FAST_FUNC; |
1216 | /* Returns NULL on . and .. */ | ||
1201 | char *concat_subpath_file(const char *path, const char *filename) FAST_FUNC; | 1217 | char *concat_subpath_file(const char *path, const char *filename) FAST_FUNC; |
1202 | const char *bb_basename(const char *name) FAST_FUNC; | ||
1203 | /* NB: can violate const-ness (similarly to strchr) */ | ||
1204 | char *last_char_is(const char *s, int c) FAST_FUNC; | ||
1205 | 1218 | ||
1206 | 1219 | ||
1207 | int bb_make_directory(char *path, long mode, int flags) FAST_FUNC; | 1220 | int bb_make_directory(char *path, long mode, int flags) FAST_FUNC; |
@@ -1269,14 +1282,19 @@ extern int correct_password(const struct passwd *pw) FAST_FUNC; | |||
1269 | #endif | 1282 | #endif |
1270 | extern char *pw_encrypt(const char *clear, const char *salt, int cleanup) FAST_FUNC; | 1283 | extern char *pw_encrypt(const char *clear, const char *salt, int cleanup) FAST_FUNC; |
1271 | extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC; | 1284 | extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC; |
1272 | /* rnd is additional random input. New one is returned. | 1285 | /* |
1286 | * rnd is additional random input. New one is returned. | ||
1273 | * Useful if you call crypt_make_salt many times in a row: | 1287 | * Useful if you call crypt_make_salt many times in a row: |
1274 | * rnd = crypt_make_salt(buf1, 4, 0); | 1288 | * rnd = crypt_make_salt(buf1, 4, 0); |
1275 | * rnd = crypt_make_salt(buf2, 4, rnd); | 1289 | * rnd = crypt_make_salt(buf2, 4, rnd); |
1276 | * rnd = crypt_make_salt(buf3, 4, rnd); | 1290 | * rnd = crypt_make_salt(buf3, 4, rnd); |
1277 | * (otherwise we risk having same salt generated) | 1291 | * (otherwise we risk having same salt generated) |
1278 | */ | 1292 | */ |
1279 | extern int crypt_make_salt(char *p, int cnt, int rnd) FAST_FUNC; | 1293 | extern int crypt_make_salt(char *p, int cnt /*, int rnd*/) FAST_FUNC; |
1294 | /* "$N$" + sha_salt_16_bytes + NUL */ | ||
1295 | #define MAX_PW_SALT_LEN (3 + 16 + 1) | ||
1296 | extern char* crypt_make_pw_salt(char p[MAX_PW_SALT_LEN], const char *algo) FAST_FUNC; | ||
1297 | |||
1280 | 1298 | ||
1281 | /* Returns number of lines changed, or -1 on error */ | 1299 | /* Returns number of lines changed, or -1 on error */ |
1282 | #if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP) | 1300 | #if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP) |
diff --git a/include/mingw.h b/include/mingw.h index 49de6cb5e..69bf05574 100644 --- a/include/mingw.h +++ b/include/mingw.h | |||
@@ -209,6 +209,8 @@ NOIMPL(mingw_bind,SOCKET s UNUSED_PARAM,const struct sockaddr* sa UNUSED_PARAM,i | |||
209 | /* | 209 | /* |
210 | * sys/stat.h | 210 | * sys/stat.h |
211 | */ | 211 | */ |
212 | typedef int nlink_t; | ||
213 | |||
212 | #define S_ISUID 04000 | 214 | #define S_ISUID 04000 |
213 | #define S_ISGID 02000 | 215 | #define S_ISGID 02000 |
214 | #define S_ISVTX 01000 | 216 | #define S_ISVTX 01000 |
diff --git a/include/platform.h b/include/platform.h index eb9f8032b..48e256aa0 100644 --- a/include/platform.h +++ b/include/platform.h | |||
@@ -17,24 +17,6 @@ | |||
17 | # endif | 17 | # endif |
18 | #endif | 18 | #endif |
19 | 19 | ||
20 | /* Assume all these functions exist by default. Platforms where it is not | ||
21 | * true will #undef them below. | ||
22 | */ | ||
23 | #define HAVE_CLEARENV 1 | ||
24 | #define HAVE_FDATASYNC 1 | ||
25 | #define HAVE_DPRINTF 1 | ||
26 | #define HAVE_MEMRCHR 1 | ||
27 | #define HAVE_MKDTEMP 1 | ||
28 | #define HAVE_PTSNAME_R 1 | ||
29 | #define HAVE_SETBIT 1 | ||
30 | #define HAVE_SIGHANDLER_T 1 | ||
31 | #define HAVE_STPCPY 1 | ||
32 | #define HAVE_STRCASESTR 1 | ||
33 | #define HAVE_STRCHRNUL 1 | ||
34 | #define HAVE_STRSEP 1 | ||
35 | #define HAVE_STRSIGNAL 1 | ||
36 | #define HAVE_VASPRINTF 1 | ||
37 | |||
38 | /* Convenience macros to test the version of gcc. */ | 20 | /* Convenience macros to test the version of gcc. */ |
39 | #undef __GNUC_PREREQ | 21 | #undef __GNUC_PREREQ |
40 | #if defined __GNUC__ && defined __GNUC_MINOR__ | 22 | #if defined __GNUC__ && defined __GNUC_MINOR__ |
@@ -51,10 +33,6 @@ | |||
51 | # endif | 33 | # endif |
52 | #endif | 34 | #endif |
53 | 35 | ||
54 | /* Define macros for some gcc attributes. This permits us to use the | ||
55 | macros freely, and know that they will come into play for the | ||
56 | version of gcc in which they are supported. */ | ||
57 | |||
58 | #if !__GNUC_PREREQ(2,7) | 36 | #if !__GNUC_PREREQ(2,7) |
59 | # ifndef __attribute__ | 37 | # ifndef __attribute__ |
60 | # define __attribute__(x) | 38 | # define __attribute__(x) |
@@ -108,7 +86,7 @@ | |||
108 | #endif | 86 | #endif |
109 | 87 | ||
110 | /* -fwhole-program makes all symbols local. The attribute externally_visible | 88 | /* -fwhole-program makes all symbols local. The attribute externally_visible |
111 | forces a symbol global. */ | 89 | * forces a symbol global. */ |
112 | #if __GNUC_PREREQ(4,1) | 90 | #if __GNUC_PREREQ(4,1) |
113 | # define EXTERNALLY_VISIBLE __attribute__(( visibility("default") )) | 91 | # define EXTERNALLY_VISIBLE __attribute__(( visibility("default") )) |
114 | //__attribute__ ((__externally_visible__)) | 92 | //__attribute__ ((__externally_visible__)) |
@@ -124,22 +102,14 @@ | |||
124 | #endif | 102 | #endif |
125 | 103 | ||
126 | /* We use __extension__ in some places to suppress -pedantic warnings | 104 | /* We use __extension__ in some places to suppress -pedantic warnings |
127 | about GCC extensions. This feature didn't work properly before | 105 | * about GCC extensions. This feature didn't work properly before |
128 | gcc 2.8. */ | 106 | * gcc 2.8. */ |
129 | #if !__GNUC_PREREQ(2,8) | 107 | #if !__GNUC_PREREQ(2,8) |
130 | # ifndef __extension__ | 108 | # ifndef __extension__ |
131 | # define __extension__ | 109 | # define __extension__ |
132 | # endif | 110 | # endif |
133 | #endif | 111 | #endif |
134 | 112 | ||
135 | /* gcc-2.95 had no va_copy but only __va_copy. */ | ||
136 | #if !__GNUC_PREREQ(3,0) | ||
137 | # include <stdarg.h> | ||
138 | # if !defined va_copy && defined __va_copy | ||
139 | # define va_copy(d,s) __va_copy((d),(s)) | ||
140 | # endif | ||
141 | #endif | ||
142 | |||
143 | /* FAST_FUNC is a qualifier which (possibly) makes function call faster | 113 | /* FAST_FUNC is a qualifier which (possibly) makes function call faster |
144 | * and/or smaller by using modified ABI. It is usually only needed | 114 | * and/or smaller by using modified ABI. It is usually only needed |
145 | * on non-static, busybox internal functions. Recent versions of gcc | 115 | * on non-static, busybox internal functions. Recent versions of gcc |
@@ -163,6 +133,15 @@ | |||
163 | # define POP_SAVED_FUNCTION_VISIBILITY | 133 | # define POP_SAVED_FUNCTION_VISIBILITY |
164 | #endif | 134 | #endif |
165 | 135 | ||
136 | /* gcc-2.95 had no va_copy but only __va_copy. */ | ||
137 | #if !__GNUC_PREREQ(3,0) | ||
138 | # include <stdarg.h> | ||
139 | # if !defined va_copy && defined __va_copy | ||
140 | # define va_copy(d,s) __va_copy((d),(s)) | ||
141 | # endif | ||
142 | #endif | ||
143 | |||
144 | |||
166 | /* ---- Endian Detection ------------------------------------ */ | 145 | /* ---- Endian Detection ------------------------------------ */ |
167 | 146 | ||
168 | #include <limits.h> | 147 | #include <limits.h> |
@@ -238,6 +217,7 @@ | |||
238 | # define IF_LITTLE_ENDIAN(...) __VA_ARGS__ | 217 | # define IF_LITTLE_ENDIAN(...) __VA_ARGS__ |
239 | #endif | 218 | #endif |
240 | 219 | ||
220 | |||
241 | /* ---- Unaligned access ------------------------------------ */ | 221 | /* ---- Unaligned access ------------------------------------ */ |
242 | 222 | ||
243 | #include <stdint.h> | 223 | #include <stdint.h> |
@@ -270,37 +250,9 @@ typedef uint32_t bb__aliased_uint32_t FIX_ALIASING; | |||
270 | } while (0) | 250 | } while (0) |
271 | #endif | 251 | #endif |
272 | 252 | ||
273 | /* ---- Compiler dependent settings ------------------------- */ | ||
274 | 253 | ||
275 | #if (defined __digital__ && defined __unix__) \ | 254 | /* ---- Size-saving "small" ints (arch-dependent) ----------- */ |
276 | || defined __APPLE__ \ | ||
277 | || defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ | ||
278 | # undef HAVE_CLEARENV | ||
279 | # undef HAVE_FDATASYNC | ||
280 | # undef HAVE_MNTENT_H | ||
281 | # undef HAVE_PTSNAME_R | ||
282 | # undef HAVE_SYS_STATFS_H | ||
283 | # undef HAVE_SIGHANDLER_T | ||
284 | # undef HAVE_XTABS | ||
285 | # undef HAVE_DPRINTF | ||
286 | #else | ||
287 | # define HAVE_MNTENT_H 1 | ||
288 | # define HAVE_SYS_STATFS_H 1 | ||
289 | # define HAVE_XTABS 1 | ||
290 | #endif | ||
291 | |||
292 | /*----- Kernel versioning ------------------------------------*/ | ||
293 | |||
294 | #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) | ||
295 | |||
296 | /* ---- Miscellaneous --------------------------------------- */ | ||
297 | 255 | ||
298 | #if defined __GLIBC__ || defined __UCLIBC__ \ | ||
299 | || defined __dietlibc__ || defined _NEWLIB_VERSION | ||
300 | # include <features.h> | ||
301 | #endif | ||
302 | |||
303 | /* Size-saving "small" ints (arch-dependent) */ | ||
304 | #if defined(i386) || defined(__x86_64__) || defined(__mips__) || defined(__cris__) | 256 | #if defined(i386) || defined(__x86_64__) || defined(__mips__) || defined(__cris__) |
305 | /* add other arches which benefit from this... */ | 257 | /* add other arches which benefit from this... */ |
306 | typedef signed char smallint; | 258 | typedef signed char smallint; |
@@ -320,7 +272,34 @@ typedef unsigned smalluint; | |||
320 | # include <stdbool.h> | 272 | # include <stdbool.h> |
321 | #endif | 273 | #endif |
322 | 274 | ||
323 | /* Try to defeat gcc's alignment of "char message[]"-like data */ | 275 | |
276 | /*----- Kernel versioning ------------------------------------*/ | ||
277 | |||
278 | #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) | ||
279 | |||
280 | |||
281 | /* ---- Miscellaneous --------------------------------------- */ | ||
282 | |||
283 | #if defined __GLIBC__ \ | ||
284 | || defined __UCLIBC__ \ | ||
285 | || defined __dietlibc__ \ | ||
286 | || defined _NEWLIB_VERSION | ||
287 | # include <features.h> | ||
288 | #endif | ||
289 | |||
290 | /* Define bb_setpgrp */ | ||
291 | #if defined(__digital__) && defined(__unix__) | ||
292 | /* use legacy setpgrp(pid_t, pid_t) for now. move to platform.c */ | ||
293 | # define bb_setpgrp() do { pid_t __me = getpid(); setpgrp(__me, __me); } while (0) | ||
294 | #else | ||
295 | # define bb_setpgrp() setpgrp() | ||
296 | #endif | ||
297 | |||
298 | /* fdprintf is more readable, we used it before dprintf was standardized */ | ||
299 | #include <unistd.h> | ||
300 | #define fdprintf dprintf | ||
301 | |||
302 | /* Useful for defeating gcc's alignment of "char message[]"-like data */ | ||
324 | #if 1 /* if needed: !defined(arch1) && !defined(arch2) */ | 303 | #if 1 /* if needed: !defined(arch1) && !defined(arch2) */ |
325 | # define ALIGN1 __attribute__((aligned(1))) | 304 | # define ALIGN1 __attribute__((aligned(1))) |
326 | # define ALIGN2 __attribute__((aligned(2))) | 305 | # define ALIGN2 __attribute__((aligned(2))) |
@@ -332,8 +311,7 @@ typedef unsigned smalluint; | |||
332 | # define ALIGN4 | 311 | # define ALIGN4 |
333 | #endif | 312 | #endif |
334 | 313 | ||
335 | 314 | /* | |
336 | /* uclibc does not implement daemon() for no-mmu systems. | ||
337 | * For 0.9.29 and svn, __ARCH_USE_MMU__ indicates no-mmu reliably. | 315 | * For 0.9.29 and svn, __ARCH_USE_MMU__ indicates no-mmu reliably. |
338 | * For earlier versions there is no reliable way to check if we are building | 316 | * For earlier versions there is no reliable way to check if we are building |
339 | * for a mmu-less system. | 317 | * for a mmu-less system. |
@@ -351,12 +329,9 @@ typedef unsigned smalluint; | |||
351 | #endif | 329 | #endif |
352 | 330 | ||
353 | #if defined(__digital__) && defined(__unix__) | 331 | #if defined(__digital__) && defined(__unix__) |
354 | |||
355 | # include <standards.h> | 332 | # include <standards.h> |
356 | # include <inttypes.h> | 333 | # include <inttypes.h> |
357 | # define PRIu32 "u" | 334 | # define PRIu32 "u" |
358 | /* use legacy setpgrp(pid_t,pid_t) for now. move to platform.c */ | ||
359 | # define bb_setpgrp() do { pid_t __me = getpid(); setpgrp(__me, __me); } while (0) | ||
360 | # if !defined ADJ_OFFSET_SINGLESHOT && defined MOD_CLKA && defined MOD_OFFSET | 335 | # if !defined ADJ_OFFSET_SINGLESHOT && defined MOD_CLKA && defined MOD_OFFSET |
361 | # define ADJ_OFFSET_SINGLESHOT (MOD_CLKA | MOD_OFFSET) | 336 | # define ADJ_OFFSET_SINGLESHOT (MOD_CLKA | MOD_OFFSET) |
362 | # endif | 337 | # endif |
@@ -369,17 +344,31 @@ typedef unsigned smalluint; | |||
369 | # if !defined ADJ_TICK && defined MOD_CLKB | 344 | # if !defined ADJ_TICK && defined MOD_CLKB |
370 | # define ADJ_TICK MOD_CLKB | 345 | # define ADJ_TICK MOD_CLKB |
371 | # endif | 346 | # endif |
347 | #endif | ||
372 | 348 | ||
373 | # undef HAVE_STPCPY | ||
374 | |||
375 | #else | ||
376 | |||
377 | # define bb_setpgrp() setpgrp() | ||
378 | 349 | ||
379 | #endif | 350 | /* ---- Who misses what? ------------------------------------ */ |
380 | 351 | ||
381 | #include <unistd.h> | 352 | /* Assume all these functions exist by default. Platforms where it is not |
382 | #define fdprintf dprintf | 353 | * true will #undef them below. |
354 | */ | ||
355 | #define HAVE_CLEARENV 1 | ||
356 | #define HAVE_FDATASYNC 1 | ||
357 | #define HAVE_DPRINTF 1 | ||
358 | #define HAVE_MEMRCHR 1 | ||
359 | #define HAVE_MKDTEMP 1 | ||
360 | #define HAVE_PTSNAME_R 1 | ||
361 | #define HAVE_SETBIT 1 | ||
362 | #define HAVE_SIGHANDLER_T 1 | ||
363 | #define HAVE_STPCPY 1 | ||
364 | #define HAVE_STRCASESTR 1 | ||
365 | #define HAVE_STRCHRNUL 1 | ||
366 | #define HAVE_STRSEP 1 | ||
367 | #define HAVE_STRSIGNAL 1 | ||
368 | #define HAVE_VASPRINTF 1 | ||
369 | #define HAVE_MNTENT_H 1 | ||
370 | #define HAVE_SYS_STATFS_H 1 | ||
371 | #define HAVE_XTABS 1 | ||
383 | 372 | ||
384 | #if defined(__dietlibc__) | 373 | #if defined(__dietlibc__) |
385 | # undef HAVE_STRCHRNUL | 374 | # undef HAVE_STRCHRNUL |
@@ -416,6 +405,23 @@ typedef unsigned smalluint; | |||
416 | # undef HAVE_STRCHRNUL | 405 | # undef HAVE_STRCHRNUL |
417 | #endif | 406 | #endif |
418 | 407 | ||
408 | #if (defined __digital__ && defined __unix__) \ | ||
409 | || defined __APPLE__ \ | ||
410 | || defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ | ||
411 | # undef HAVE_CLEARENV | ||
412 | # undef HAVE_FDATASYNC | ||
413 | # undef HAVE_MNTENT_H | ||
414 | # undef HAVE_PTSNAME_R | ||
415 | # undef HAVE_SYS_STATFS_H | ||
416 | # undef HAVE_SIGHANDLER_T | ||
417 | # undef HAVE_XTABS | ||
418 | # undef HAVE_DPRINTF | ||
419 | #endif | ||
420 | |||
421 | #if defined(__digital__) && defined(__unix__) | ||
422 | # undef HAVE_STPCPY | ||
423 | #endif | ||
424 | |||
419 | /* | 425 | /* |
420 | * Now, define prototypes for all the functions defined in platform.c | 426 | * Now, define prototypes for all the functions defined in platform.c |
421 | * These must come after all the HAVE_* macros are defined (or not) | 427 | * These must come after all the HAVE_* macros are defined (or not) |
diff --git a/libbb/Kbuild.src b/libbb/Kbuild.src index 3c9493f7e..d110a3265 100644 --- a/libbb/Kbuild.src +++ b/libbb/Kbuild.src | |||
@@ -13,7 +13,6 @@ INSERT | |||
13 | lib-y += appletlib.o | 13 | lib-y += appletlib.o |
14 | lib-y += ask_confirmation.o | 14 | lib-y += ask_confirmation.o |
15 | lib-y += bb_askpass.o | 15 | lib-y += bb_askpass.o |
16 | lib-y += bb_basename.o | ||
17 | lib-y += bb_bswap_64.o | 16 | lib-y += bb_bswap_64.o |
18 | lib-y += bb_do_delay.o | 17 | lib-y += bb_do_delay.o |
19 | lib-y += bb_pwd.o | 18 | lib-y += bb_pwd.o |
diff --git a/libbb/appletlib.c b/libbb/appletlib.c index a934f11bb..d4025b9c7 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c | |||
@@ -27,12 +27,13 @@ | |||
27 | * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :( | 27 | * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :( |
28 | */ | 28 | */ |
29 | #include "busybox.h" | 29 | #include "busybox.h" |
30 | #include <assert.h> | 30 | |
31 | #if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ | 31 | #if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ |
32 | || defined(__APPLE__) \ | 32 | || defined(__APPLE__) \ |
33 | ) | 33 | ) |
34 | # include <malloc.h> /* for mallopt */ | 34 | # include <malloc.h> /* for mallopt */ |
35 | #endif | 35 | #endif |
36 | |||
36 | /* Try to pull in PAGE_SIZE */ | 37 | /* Try to pull in PAGE_SIZE */ |
37 | #ifdef __linux__ | 38 | #ifdef __linux__ |
38 | # include <sys/user.h> | 39 | # include <sys/user.h> |
@@ -40,6 +41,9 @@ | |||
40 | #ifdef __GNU__ /* Hurd */ | 41 | #ifdef __GNU__ /* Hurd */ |
41 | # include <mach/vm_param.h> | 42 | # include <mach/vm_param.h> |
42 | #endif | 43 | #endif |
44 | #ifndef PAGE_SIZE | ||
45 | # define PAGE_SIZE (4*1024) /* guess */ | ||
46 | #endif | ||
43 | 47 | ||
44 | 48 | ||
45 | /* Declare <applet>_main() */ | 49 | /* Declare <applet>_main() */ |
@@ -47,7 +51,6 @@ | |||
47 | #include "applets.h" | 51 | #include "applets.h" |
48 | #undef PROTOTYPES | 52 | #undef PROTOTYPES |
49 | 53 | ||
50 | |||
51 | /* Include generated applet names, pointers to <applet>_main, etc */ | 54 | /* Include generated applet names, pointers to <applet>_main, etc */ |
52 | #include "applet_tables.h" | 55 | #include "applet_tables.h" |
53 | /* ...and if applet_tables generator says we have only one applet... */ | 56 | /* ...and if applet_tables generator says we have only one applet... */ |
@@ -58,9 +61,9 @@ | |||
58 | # define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__ | 61 | # define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__ |
59 | #endif | 62 | #endif |
60 | 63 | ||
61 | |||
62 | #include "usage_compressed.h" | 64 | #include "usage_compressed.h" |
63 | 65 | ||
66 | |||
64 | #if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE | 67 | #if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE |
65 | static const char usage_messages[] ALIGN1 = UNPACKED_USAGE; | 68 | static const char usage_messages[] ALIGN1 = UNPACKED_USAGE; |
66 | #else | 69 | #else |
@@ -233,13 +236,12 @@ IF_FEATURE_SUID(static uid_t ruid;) /* real uid */ | |||
233 | 236 | ||
234 | # if ENABLE_FEATURE_SUID_CONFIG | 237 | # if ENABLE_FEATURE_SUID_CONFIG |
235 | 238 | ||
236 | /* applets[] is const, so we have to define this "override" structure */ | 239 | static struct suid_config_t { |
237 | static struct BB_suid_config { | 240 | /* next ptr must be first: this struct needs to be llist-compatible */ |
241 | struct suid_config_t *m_next; | ||
242 | struct bb_uidgid_t m_ugid; | ||
238 | int m_applet; | 243 | int m_applet; |
239 | uid_t m_uid; | ||
240 | gid_t m_gid; | ||
241 | mode_t m_mode; | 244 | mode_t m_mode; |
242 | struct BB_suid_config *m_next; | ||
243 | } *suid_config; | 245 | } *suid_config; |
244 | 246 | ||
245 | static bool suid_cfg_readable; | 247 | static bool suid_cfg_readable; |
@@ -248,13 +250,10 @@ static bool suid_cfg_readable; | |||
248 | static int ingroup(uid_t u, gid_t g) | 250 | static int ingroup(uid_t u, gid_t g) |
249 | { | 251 | { |
250 | struct group *grp = getgrgid(g); | 252 | struct group *grp = getgrgid(g); |
251 | |||
252 | if (grp) { | 253 | if (grp) { |
253 | char **mem; | 254 | char **mem; |
254 | |||
255 | for (mem = grp->gr_mem; *mem; mem++) { | 255 | for (mem = grp->gr_mem; *mem; mem++) { |
256 | struct passwd *pwd = getpwnam(*mem); | 256 | struct passwd *pwd = getpwnam(*mem); |
257 | |||
258 | if (pwd && (pwd->pw_uid == u)) | 257 | if (pwd && (pwd->pw_uid == u)) |
259 | return 1; | 258 | return 1; |
260 | } | 259 | } |
@@ -262,9 +261,7 @@ static int ingroup(uid_t u, gid_t g) | |||
262 | return 0; | 261 | return 0; |
263 | } | 262 | } |
264 | 263 | ||
265 | /* This should probably be a libbb routine. In that case, | 264 | /* libbb candidate */ |
266 | * I'd probably rename it to something like bb_trimmed_slice. | ||
267 | */ | ||
268 | static char *get_trimmed_slice(char *s, char *e) | 265 | static char *get_trimmed_slice(char *s, char *e) |
269 | { | 266 | { |
270 | /* First, consider the value at e to be nul and back up until we | 267 | /* First, consider the value at e to be nul and back up until we |
@@ -282,38 +279,19 @@ static char *get_trimmed_slice(char *s, char *e) | |||
282 | return skip_whitespace(s); | 279 | return skip_whitespace(s); |
283 | } | 280 | } |
284 | 281 | ||
285 | /* Don't depend on the tools to combine strings. */ | ||
286 | static const char config_file[] ALIGN1 = "/etc/busybox.conf"; | ||
287 | |||
288 | /* We don't supply a value for the nul, so an index adjustment is | ||
289 | * necessary below. Also, we use unsigned short here to save some | ||
290 | * space even though these are really mode_t values. */ | ||
291 | static const unsigned short mode_mask[] ALIGN2 = { | ||
292 | /* SST sst xxx --- */ | ||
293 | S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */ | ||
294 | S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */ | ||
295 | 0, S_IXOTH, S_IXOTH, 0 /* other */ | ||
296 | }; | ||
297 | |||
298 | #define parse_error(x) do { errmsg = x; goto pe_label; } while (0) | ||
299 | |||
300 | static void parse_config_file(void) | 282 | static void parse_config_file(void) |
301 | { | 283 | { |
302 | struct BB_suid_config *sct_head; | 284 | /* Don't depend on the tools to combine strings. */ |
303 | struct BB_suid_config *sct; | 285 | static const char config_file[] ALIGN1 = "/etc/busybox.conf"; |
286 | |||
287 | struct suid_config_t *sct_head; | ||
304 | int applet_no; | 288 | int applet_no; |
305 | FILE *f; | 289 | FILE *f; |
306 | const char *errmsg; | 290 | const char *errmsg; |
307 | char *s; | ||
308 | char *e; | ||
309 | int i; | ||
310 | unsigned lc; | 291 | unsigned lc; |
311 | smallint section; | 292 | smallint section; |
312 | char buffer[256]; | ||
313 | struct stat st; | 293 | struct stat st; |
314 | 294 | ||
315 | assert(!suid_config); /* Should be set to NULL by bss init. */ | ||
316 | |||
317 | ruid = getuid(); | 295 | ruid = getuid(); |
318 | if (ruid == 0) /* run by root - don't need to even read config file */ | 296 | if (ruid == 0) /* run by root - don't need to even read config file */ |
319 | return; | 297 | return; |
@@ -322,7 +300,7 @@ static void parse_config_file(void) | |||
322 | || !S_ISREG(st.st_mode) /* Not a regular file? */ | 300 | || !S_ISREG(st.st_mode) /* Not a regular file? */ |
323 | || (st.st_uid != 0) /* Not owned by root? */ | 301 | || (st.st_uid != 0) /* Not owned by root? */ |
324 | || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */ | 302 | || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */ |
325 | || !(f = fopen_for_read(config_file)) /* Cannot open? */ | 303 | || !(f = fopen_for_read(config_file)) /* Cannot open? */ |
326 | ) { | 304 | ) { |
327 | return; | 305 | return; |
328 | } | 306 | } |
@@ -332,18 +310,21 @@ static void parse_config_file(void) | |||
332 | section = lc = 0; | 310 | section = lc = 0; |
333 | 311 | ||
334 | while (1) { | 312 | while (1) { |
335 | s = buffer; | 313 | char buffer[256]; |
336 | 314 | char *s; | |
337 | if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */ | 315 | |
338 | // why? | 316 | if (!fgets(buffer, sizeof(buffer), f)) { /* Are we done? */ |
339 | if (ferror(f)) { /* Make sure it wasn't a read error. */ | 317 | // Looks like bloat |
340 | parse_error("reading"); | 318 | //if (ferror(f)) { /* Make sure it wasn't a read error. */ |
341 | } | 319 | // errmsg = "reading"; |
320 | // goto pe_label; | ||
321 | //} | ||
342 | fclose(f); | 322 | fclose(f); |
343 | suid_config = sct_head; /* Success, so set the pointer. */ | 323 | suid_config = sct_head; /* Success, so set the pointer. */ |
344 | return; | 324 | return; |
345 | } | 325 | } |
346 | 326 | ||
327 | s = buffer; | ||
347 | lc++; /* Got a (partial) line. */ | 328 | lc++; /* Got a (partial) line. */ |
348 | 329 | ||
349 | /* If a line is too long for our buffer, we consider it an error. | 330 | /* If a line is too long for our buffer, we consider it an error. |
@@ -355,7 +336,8 @@ static void parse_config_file(void) | |||
355 | * we do err on the side of caution. Besides, the line would be | 336 | * we do err on the side of caution. Besides, the line would be |
356 | * too long if it did end with a newline. */ | 337 | * too long if it did end with a newline. */ |
357 | if (!strchr(s, '\n') && !feof(f)) { | 338 | if (!strchr(s, '\n') && !feof(f)) { |
358 | parse_error("line too long"); | 339 | errmsg = "line too long"; |
340 | goto pe_label; | ||
359 | } | 341 | } |
360 | 342 | ||
361 | /* Trim leading and trailing whitespace, ignoring comments, and | 343 | /* Trim leading and trailing whitespace, ignoring comments, and |
@@ -371,12 +353,13 @@ static void parse_config_file(void) | |||
371 | /* Unlike the old code, we ignore leading and trailing | 353 | /* Unlike the old code, we ignore leading and trailing |
372 | * whitespace for the section name. We also require that | 354 | * whitespace for the section name. We also require that |
373 | * there are no stray characters after the closing bracket. */ | 355 | * there are no stray characters after the closing bracket. */ |
374 | e = strchr(s, ']'); | 356 | char *e = strchr(s, ']'); |
375 | if (!e /* Missing right bracket? */ | 357 | if (!e /* Missing right bracket? */ |
376 | || e[1] /* Trailing characters? */ | 358 | || e[1] /* Trailing characters? */ |
377 | || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */ | 359 | || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */ |
378 | ) { | 360 | ) { |
379 | parse_error("section header"); | 361 | errmsg = "section header"; |
362 | goto pe_label; | ||
380 | } | 363 | } |
381 | /* Right now we only have one section so just check it. | 364 | /* Right now we only have one section so just check it. |
382 | * If more sections are added in the future, please don't | 365 | * If more sections are added in the future, please don't |
@@ -401,12 +384,13 @@ static void parse_config_file(void) | |||
401 | * where both key and value could contain inner whitespace. */ | 384 | * where both key and value could contain inner whitespace. */ |
402 | 385 | ||
403 | /* First get the key (an applet name in our case). */ | 386 | /* First get the key (an applet name in our case). */ |
404 | e = strchr(s, '='); | 387 | char *e = strchr(s, '='); |
405 | if (e) { | 388 | if (e) { |
406 | s = get_trimmed_slice(s, e); | 389 | s = get_trimmed_slice(s, e); |
407 | } | 390 | } |
408 | if (!e || !*s) { /* Missing '=' or empty key. */ | 391 | if (!e || !*s) { /* Missing '=' or empty key. */ |
409 | parse_error("keyword"); | 392 | errmsg = "keyword"; |
393 | goto pe_label; | ||
410 | } | 394 | } |
411 | 395 | ||
412 | /* Ok, we have an applet name. Process the rhs if this | 396 | /* Ok, we have an applet name. Process the rhs if this |
@@ -415,13 +399,16 @@ static void parse_config_file(void) | |||
415 | * up when the busybox configuration is changed. */ | 399 | * up when the busybox configuration is changed. */ |
416 | applet_no = find_applet_by_name(s); | 400 | applet_no = find_applet_by_name(s); |
417 | if (applet_no >= 0) { | 401 | if (applet_no >= 0) { |
402 | unsigned i; | ||
403 | struct suid_config_t *sct; | ||
404 | |||
418 | /* Note: We currently don't check for duplicates! | 405 | /* Note: We currently don't check for duplicates! |
419 | * The last config line for each applet will be the | 406 | * The last config line for each applet will be the |
420 | * one used since we insert at the head of the list. | 407 | * one used since we insert at the head of the list. |
421 | * I suppose this could be considered a feature. */ | 408 | * I suppose this could be considered a feature. */ |
422 | sct = xmalloc(sizeof(struct BB_suid_config)); | 409 | sct = xzalloc(sizeof(*sct)); |
423 | sct->m_applet = applet_no; | 410 | sct->m_applet = applet_no; |
424 | sct->m_mode = 0; | 411 | /*sct->m_mode = 0;*/ |
425 | sct->m_next = sct_head; | 412 | sct->m_next = sct_head; |
426 | sct_head = sct; | 413 | sct_head = sct; |
427 | 414 | ||
@@ -430,48 +417,41 @@ static void parse_config_file(void) | |||
430 | e = skip_whitespace(e+1); | 417 | e = skip_whitespace(e+1); |
431 | 418 | ||
432 | for (i = 0; i < 3; i++) { | 419 | for (i = 0; i < 3; i++) { |
433 | /* There are 4 chars + 1 nul for each of user/group/other. */ | 420 | /* There are 4 chars for each of user/group/other. |
434 | static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-"; | 421 | * "x-xx" instead of "x-" are to make |
435 | 422 | * "idx > 3" check catch invalid chars. | |
436 | const char *q; | 423 | */ |
437 | q = strchrnul(mode_chars + 5*i, *e++); | 424 | static const char mode_chars[] ALIGN1 = "Ssx-" "Ssx-" "x-xx"; |
438 | if (!*q) { | 425 | static const unsigned short mode_mask[] ALIGN2 = { |
439 | parse_error("mode"); | 426 | S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* Ssx- */ |
427 | S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* Ssx- */ | ||
428 | S_IXOTH, 0 /* x- */ | ||
429 | }; | ||
430 | const char *q = strchrnul(mode_chars + 4*i, *e); | ||
431 | unsigned idx = q - (mode_chars + 4*i); | ||
432 | if (idx > 3) { | ||
433 | errmsg = "mode"; | ||
434 | goto pe_label; | ||
440 | } | 435 | } |
441 | /* Adjust by -i to account for nul. */ | 436 | sct->m_mode |= mode_mask[q - mode_chars]; |
442 | sct->m_mode |= mode_mask[(q - mode_chars) - i]; | 437 | e++; |
443 | } | 438 | } |
444 | 439 | ||
445 | /* Now get the user/group info. */ | 440 | /* Now get the user/group info. */ |
446 | 441 | ||
447 | s = skip_whitespace(e); | 442 | s = skip_whitespace(e); |
448 | 443 | /* Default is 0.0, else parse USER.GROUP: */ | |
449 | /* Note: we require whitespace between the mode and the | 444 | if (*s) { |
450 | * user/group info. */ | 445 | /* We require whitespace between mode and USER.GROUP */ |
451 | if ((s == e) || !(e = strchr(s, '.'))) { | 446 | if ((s == e) || !(e = strchr(s, '.'))) { |
452 | parse_error("<uid>.<gid>"); | 447 | errmsg = "uid.gid"; |
453 | } | 448 | goto pe_label; |
454 | *e++ = '\0'; | ||
455 | |||
456 | /* We can't use get_ug_id here since it would exit() | ||
457 | * if a uid or gid was not found. Oh well... */ | ||
458 | sct->m_uid = bb_strtoul(s, NULL, 10); | ||
459 | if (errno) { | ||
460 | struct passwd *pwd = getpwnam(s); | ||
461 | if (!pwd) { | ||
462 | parse_error("user"); | ||
463 | } | 449 | } |
464 | sct->m_uid = pwd->pw_uid; | 450 | *e = ':'; /* get_uidgid needs USER:GROUP syntax */ |
465 | } | 451 | if (get_uidgid(&sct->m_ugid, s, /*allow_numeric:*/ 1) == 0) { |
466 | 452 | errmsg = "unknown user/group"; | |
467 | sct->m_gid = bb_strtoul(e, NULL, 10); | 453 | goto pe_label; |
468 | if (errno) { | ||
469 | struct group *grp; | ||
470 | grp = getgrnam(e); | ||
471 | if (!grp) { | ||
472 | parse_error("group"); | ||
473 | } | 454 | } |
474 | sct->m_gid = grp->gr_gid; | ||
475 | } | 455 | } |
476 | } | 456 | } |
477 | continue; | 457 | continue; |
@@ -485,22 +465,18 @@ static void parse_config_file(void) | |||
485 | * We may want to simply ignore such lines in case they | 465 | * We may want to simply ignore such lines in case they |
486 | * are used in some future version of busybox. */ | 466 | * are used in some future version of busybox. */ |
487 | if (!section) { | 467 | if (!section) { |
488 | parse_error("keyword outside section"); | 468 | errmsg = "keyword outside section"; |
469 | goto pe_label; | ||
489 | } | 470 | } |
490 | 471 | ||
491 | } /* while (1) */ | 472 | } /* while (1) */ |
492 | 473 | ||
493 | pe_label: | 474 | pe_label: |
494 | fprintf(stderr, "Parse error in %s, line %d: %s\n", | ||
495 | config_file, lc, errmsg); | ||
496 | |||
497 | fclose(f); | 475 | fclose(f); |
476 | bb_error_msg("parse error in %s, line %u: %s", config_file, lc, errmsg); | ||
477 | |||
498 | /* Release any allocated memory before returning. */ | 478 | /* Release any allocated memory before returning. */ |
499 | while (sct_head) { | 479 | llist_free((llist_t*)sct_head, NULL); |
500 | sct = sct_head->m_next; | ||
501 | free(sct_head); | ||
502 | sct_head = sct; | ||
503 | } | ||
504 | } | 480 | } |
505 | # else | 481 | # else |
506 | static inline void parse_config_file(void) | 482 | static inline void parse_config_file(void) |
@@ -522,7 +498,7 @@ static void check_suid(int applet_no) | |||
522 | # if ENABLE_FEATURE_SUID_CONFIG | 498 | # if ENABLE_FEATURE_SUID_CONFIG |
523 | if (suid_cfg_readable) { | 499 | if (suid_cfg_readable) { |
524 | uid_t uid; | 500 | uid_t uid; |
525 | struct BB_suid_config *sct; | 501 | struct suid_config_t *sct; |
526 | mode_t m; | 502 | mode_t m; |
527 | 503 | ||
528 | for (sct = suid_config; sct; sct = sct->m_next) { | 504 | for (sct = suid_config; sct; sct = sct->m_next) { |
@@ -531,36 +507,40 @@ static void check_suid(int applet_no) | |||
531 | } | 507 | } |
532 | goto check_need_suid; | 508 | goto check_need_suid; |
533 | found: | 509 | found: |
510 | /* Is this user allowed to run this applet? */ | ||
534 | m = sct->m_mode; | 511 | m = sct->m_mode; |
535 | if (sct->m_uid == ruid) | 512 | if (sct->m_ugid.uid == ruid) |
536 | /* same uid */ | 513 | /* same uid */ |
537 | m >>= 6; | 514 | m >>= 6; |
538 | else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid)) | 515 | else if ((sct->m_ugid.gid == rgid) || ingroup(ruid, sct->m_ugid.gid)) |
539 | /* same group / in group */ | 516 | /* same group / in group */ |
540 | m >>= 3; | 517 | m >>= 3; |
541 | 518 | if (!(m & S_IXOTH)) /* is x bit not set? */ | |
542 | if (!(m & S_IXOTH)) /* is x bit not set ? */ | 519 | bb_error_msg_and_die("you have no permission to run this applet"); |
543 | bb_error_msg_and_die("you have no permission to run this applet!"); | ||
544 | |||
545 | /* _both_ sgid and group_exec have to be set for setegid */ | ||
546 | if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) | ||
547 | rgid = sct->m_gid; | ||
548 | /* else (no setegid) we will set egid = rgid */ | ||
549 | 520 | ||
550 | /* We set effective AND saved ids. If saved-id is not set | 521 | /* We set effective AND saved ids. If saved-id is not set |
551 | * like we do below, seteiud(0) can still later succeed! */ | 522 | * like we do below, seteuid(0) can still later succeed! */ |
523 | |||
524 | /* Are we directed to change gid | ||
525 | * (APPLET = *s* USER.GROUP or APPLET = *S* USER.GROUP)? | ||
526 | */ | ||
527 | if (sct->m_mode & S_ISGID) | ||
528 | rgid = sct->m_ugid.gid; | ||
529 | /* else: we will set egid = rgid, thus dropping sgid effect */ | ||
552 | if (setresgid(-1, rgid, rgid)) | 530 | if (setresgid(-1, rgid, rgid)) |
553 | bb_perror_msg_and_die("setresgid"); | 531 | bb_perror_msg_and_die("setresgid"); |
554 | 532 | ||
555 | /* do we have to set effective uid? */ | 533 | /* Are we directed to change uid |
534 | * (APPLET = s** USER.GROUP or APPLET = S** USER.GROUP)? | ||
535 | */ | ||
556 | uid = ruid; | 536 | uid = ruid; |
557 | if (sct->m_mode & S_ISUID) | 537 | if (sct->m_mode & S_ISUID) |
558 | uid = sct->m_uid; | 538 | uid = sct->m_ugid.uid; |
559 | /* else (no seteuid) we will set euid = ruid */ | 539 | /* else: we will set euid = ruid, thus dropping suid effect */ |
560 | |||
561 | if (setresuid(-1, uid, uid)) | 540 | if (setresuid(-1, uid, uid)) |
562 | bb_perror_msg_and_die("setresuid"); | 541 | bb_perror_msg_and_die("setresuid"); |
563 | return; | 542 | |
543 | goto ret; | ||
564 | } | 544 | } |
565 | # if !ENABLE_FEATURE_SUID_CONFIG_QUIET | 545 | # if !ENABLE_FEATURE_SUID_CONFIG_QUIET |
566 | { | 546 | { |
@@ -568,7 +548,7 @@ static void check_suid(int applet_no) | |||
568 | 548 | ||
569 | if (!onetime) { | 549 | if (!onetime) { |
570 | onetime = 1; | 550 | onetime = 1; |
571 | fprintf(stderr, "Using fallback suid method\n"); | 551 | bb_error_msg("using fallback suid method"); |
572 | } | 552 | } |
573 | } | 553 | } |
574 | # endif | 554 | # endif |
@@ -583,6 +563,10 @@ static void check_suid(int applet_no) | |||
583 | xsetgid(rgid); /* drop all privileges */ | 563 | xsetgid(rgid); /* drop all privileges */ |
584 | xsetuid(ruid); | 564 | xsetuid(ruid); |
585 | } | 565 | } |
566 | # if ENABLE_FEATURE_SUID_CONFIG | ||
567 | ret: ; | ||
568 | llist_free((llist_t*)suid_config, NULL); | ||
569 | # endif | ||
586 | } | 570 | } |
587 | # else | 571 | # else |
588 | # define check_suid(x) ((void)0) | 572 | # define check_suid(x) ((void)0) |
@@ -799,9 +783,6 @@ int main(int argc UNUSED_PARAM, char **argv) | |||
799 | #endif | 783 | #endif |
800 | { | 784 | { |
801 | /* Tweak malloc for reduced memory consumption */ | 785 | /* Tweak malloc for reduced memory consumption */ |
802 | #ifndef PAGE_SIZE | ||
803 | # define PAGE_SIZE (4*1024) /* guess */ | ||
804 | #endif | ||
805 | #ifdef M_TRIM_THRESHOLD | 786 | #ifdef M_TRIM_THRESHOLD |
806 | /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory | 787 | /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory |
807 | * to keep before releasing to the OS | 788 | * to keep before releasing to the OS |
diff --git a/libbb/bb_basename.c b/libbb/bb_basename.c deleted file mode 100644 index 92fdb6545..000000000 --- a/libbb/bb_basename.c +++ /dev/null | |||
@@ -1,23 +0,0 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 2007 Denys Vlasenko | ||
6 | * | ||
7 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | const char* FAST_FUNC bb_basename(const char *name) | ||
13 | { | ||
14 | const char *cp = strrchr(name, '/'); | ||
15 | if (cp) | ||
16 | return cp + 1; | ||
17 | if (ENABLE_PLATFORM_MINGW32) { | ||
18 | cp = strrchr(name, '\\'); | ||
19 | if (cp) | ||
20 | return cp + 1; | ||
21 | } | ||
22 | return name; | ||
23 | } | ||
diff --git a/libbb/dump.c b/libbb/dump.c index 1b1d03a66..919fe135c 100644 --- a/libbb/dump.c +++ b/libbb/dump.c | |||
@@ -99,7 +99,7 @@ static NOINLINE int bb_dump_size(FS *fs) | |||
99 | static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs) | 99 | static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs) |
100 | { | 100 | { |
101 | enum { NOTOKAY, USEBCNT, USEPREC } sokay; | 101 | enum { NOTOKAY, USEBCNT, USEPREC } sokay; |
102 | PR *pr, **nextpr = NULL; | 102 | PR *pr; |
103 | FU *fu; | 103 | FU *fu; |
104 | char *p1, *p2, *p3; | 104 | char *p1, *p2, *p3; |
105 | char savech, *fmtp; | 105 | char savech, *fmtp; |
@@ -111,15 +111,12 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs) | |||
111 | * break each format unit into print units; each | 111 | * break each format unit into print units; each |
112 | * conversion character gets its own. | 112 | * conversion character gets its own. |
113 | */ | 113 | */ |
114 | for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) { | 114 | for (nconv = 0, fmtp = fu->fmt; *fmtp; ) { |
115 | /* NOSTRICT */ | 115 | /* NOSTRICT */ |
116 | /* DBU:[dvae@cray.com] zalloc so that forward ptrs start out NULL*/ | 116 | /* DBU:[dvae@cray.com] zalloc so that forward ptrs start out NULL*/ |
117 | pr = xzalloc(sizeof(PR)); | 117 | pr = xzalloc(sizeof(PR)); |
118 | if (!fu->nextpr) | 118 | if (!fu->nextpr) |
119 | fu->nextpr = pr; | 119 | fu->nextpr = pr; |
120 | /* ignore nextpr -- its unused inside the loop and is | ||
121 | * uninitialized 1st time through. | ||
122 | */ | ||
123 | 120 | ||
124 | /* skip preceding text and up to the next % sign */ | 121 | /* skip preceding text and up to the next % sign */ |
125 | for (p1 = fmtp; *p1 && *p1 != '%'; ++p1) | 122 | for (p1 = fmtp; *p1 && *p1 != '%'; ++p1) |
diff --git a/libbb/get_last_path_component.c b/libbb/get_last_path_component.c index 72598d22e..db4be68ad 100644 --- a/libbb/get_last_path_component.c +++ b/libbb/get_last_path_component.c | |||
@@ -6,8 +6,21 @@ | |||
6 | * | 6 | * |
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
8 | */ | 8 | */ |
9 | |||
10 | #include "libbb.h" | 9 | #include "libbb.h" |
10 | |||
11 | const char* FAST_FUNC bb_basename(const char *name) | ||
12 | { | ||
13 | const char *cp = strrchr(name, '/'); | ||
14 | if (cp) | ||
15 | return cp + 1; | ||
16 | #if ENABLE_PLATFORM_MINGW32 | ||
17 | cp = strrchr(name, '\\'); | ||
18 | if (cp) | ||
19 | return cp + 1; | ||
20 | #endif | ||
21 | return name; | ||
22 | } | ||
23 | |||
11 | /* | 24 | /* |
12 | * "/" -> "/" | 25 | * "/" -> "/" |
13 | * "abc" -> "abc" | 26 | * "abc" -> "abc" |
diff --git a/libbb/getopt32.c b/libbb/getopt32.c index 8087b3ba6..02cbefec9 100644 --- a/libbb/getopt32.c +++ b/libbb/getopt32.c | |||
@@ -7,7 +7,9 @@ | |||
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include <getopt.h> | 10 | #if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG |
11 | # include <getopt.h> | ||
12 | #endif | ||
11 | #include "libbb.h" | 13 | #include "libbb.h" |
12 | 14 | ||
13 | /* Documentation | 15 | /* Documentation |
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 67c5e0baa..e443c7a3e 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -41,6 +41,10 @@ | |||
41 | */ | 41 | */ |
42 | #include "libbb.h" | 42 | #include "libbb.h" |
43 | #include "unicode.h" | 43 | #include "unicode.h" |
44 | #ifndef _POSIX_VDISABLE | ||
45 | # define _POSIX_VDISABLE '\0' | ||
46 | #endif | ||
47 | |||
44 | 48 | ||
45 | #ifdef TEST | 49 | #ifdef TEST |
46 | # define ENABLE_FEATURE_EDITING 0 | 50 | # define ENABLE_FEATURE_EDITING 0 |
@@ -184,6 +188,7 @@ extern struct lineedit_statics *const lineedit_ptr_to_statics; | |||
184 | IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \ | 188 | IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \ |
185 | IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \ | 189 | IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \ |
186 | } while (0) | 190 | } while (0) |
191 | |||
187 | static void deinit_S(void) | 192 | static void deinit_S(void) |
188 | { | 193 | { |
189 | #if ENABLE_FEATURE_EDITING_FANCY_PROMPT | 194 | #if ENABLE_FEATURE_EDITING_FANCY_PROMPT |
@@ -1684,7 +1689,7 @@ static void ask_terminal(void) | |||
1684 | * write(1, "~/srcdevel/bbox/fix/busybox.t4 # ", 33) = 33 | 1689 | * write(1, "~/srcdevel/bbox/fix/busybox.t4 # ", 33) = 33 |
1685 | * poll([{fd=0, events=POLLIN}], 1, 0) = 0 (Timeout) <-- no input exists | 1690 | * poll([{fd=0, events=POLLIN}], 1, 0) = 0 (Timeout) <-- no input exists |
1686 | * write(1, "\33[6n", 4) = 4 <-- send the ESC sequence, quick! | 1691 | * write(1, "\33[6n", 4) = 4 <-- send the ESC sequence, quick! |
1687 | * poll([{fd=0, events=POLLIN}], 1, 4294967295) = 1 ([{fd=0, revents=POLLIN}]) | 1692 | * poll([{fd=0, events=POLLIN}], 1, -1) = 1 ([{fd=0, revents=POLLIN}]) |
1688 | * read(0, "\n", 1) = 1 <-- oh crap, user's input got in first | 1693 | * read(0, "\n", 1) = 1 <-- oh crap, user's input got in first |
1689 | */ | 1694 | */ |
1690 | struct pollfd pfd; | 1695 | struct pollfd pfd; |
@@ -1842,10 +1847,11 @@ static void win_changed(int nsig) | |||
1842 | { | 1847 | { |
1843 | int sv_errno = errno; | 1848 | int sv_errno = errno; |
1844 | unsigned width; | 1849 | unsigned width; |
1850 | |||
1845 | get_terminal_width_height(0, &width, NULL); | 1851 | get_terminal_width_height(0, &width, NULL); |
1846 | cmdedit_setwidth(width, nsig /* - just a yes/no flag */); | 1852 | //FIXME: cmdedit_setwidth() -> redraw() -> printf() -> KABOOM! (we are in signal handler!) |
1847 | if (nsig == SIGWINCH) | 1853 | cmdedit_setwidth(width, /*redraw_flg:*/ nsig); |
1848 | signal(SIGWINCH, win_changed); /* rearm ourself */ | 1854 | |
1849 | errno = sv_errno; | 1855 | errno = sv_errno; |
1850 | } | 1856 | } |
1851 | 1857 | ||
@@ -2031,14 +2037,9 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman | |||
2031 | new_settings.c_cc[VMIN] = 1; | 2037 | new_settings.c_cc[VMIN] = 1; |
2032 | new_settings.c_cc[VTIME] = 0; | 2038 | new_settings.c_cc[VTIME] = 0; |
2033 | /* Turn off CTRL-C, so we can trap it */ | 2039 | /* Turn off CTRL-C, so we can trap it */ |
2034 | #ifndef _POSIX_VDISABLE | ||
2035 | # define _POSIX_VDISABLE '\0' | ||
2036 | #endif | ||
2037 | new_settings.c_cc[VINTR] = _POSIX_VDISABLE; | 2040 | new_settings.c_cc[VINTR] = _POSIX_VDISABLE; |
2038 | tcsetattr_stdin_TCSANOW(&new_settings); | 2041 | tcsetattr_stdin_TCSANOW(&new_settings); |
2039 | 2042 | ||
2040 | previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); | ||
2041 | win_changed(0); /* do initial resizing */ | ||
2042 | #if ENABLE_USERNAME_OR_HOMEDIR | 2043 | #if ENABLE_USERNAME_OR_HOMEDIR |
2043 | { | 2044 | { |
2044 | struct passwd *entry; | 2045 | struct passwd *entry; |
@@ -2061,6 +2062,11 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman | |||
2061 | parse_and_put_prompt(prompt); | 2062 | parse_and_put_prompt(prompt); |
2062 | ask_terminal(); | 2063 | ask_terminal(); |
2063 | 2064 | ||
2065 | /* Install window resize handler (NB: after *all* init is complete) */ | ||
2066 | //FIXME: save entire sigaction! | ||
2067 | previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); | ||
2068 | win_changed(0); /* get initial window size */ | ||
2069 | |||
2064 | read_key_buffer[0] = 0; | 2070 | read_key_buffer[0] = 0; |
2065 | while (1) { | 2071 | while (1) { |
2066 | /* | 2072 | /* |
diff --git a/libbb/llist.c b/libbb/llist.c index ed84e6472..032e9fac8 100644 --- a/libbb/llist.c +++ b/libbb/llist.c | |||
@@ -62,7 +62,7 @@ void FAST_FUNC llist_unlink(llist_t **head, llist_t *elm) | |||
62 | 62 | ||
63 | /* Recursively free all elements in the linked list. If freeit != NULL | 63 | /* Recursively free all elements in the linked list. If freeit != NULL |
64 | * call it on each datum in the list */ | 64 | * call it on each datum in the list */ |
65 | void FAST_FUNC llist_free(llist_t *elm, void (*freeit) (void *data)) | 65 | void FAST_FUNC llist_free(llist_t *elm, void (*freeit)(void *data)) |
66 | { | 66 | { |
67 | while (elm) { | 67 | while (elm) { |
68 | void *data = llist_pop(&elm); | 68 | void *data = llist_pop(&elm); |
diff --git a/libbb/pw_encrypt.c b/libbb/pw_encrypt.c index c6c04d44a..39ffa084f 100644 --- a/libbb/pw_encrypt.c +++ b/libbb/pw_encrypt.c | |||
@@ -27,9 +27,10 @@ static int i64c(int i) | |||
27 | return ('a' - 38 + i); | 27 | return ('a' - 38 + i); |
28 | } | 28 | } |
29 | 29 | ||
30 | int FAST_FUNC crypt_make_salt(char *p, int cnt, int x) | 30 | int FAST_FUNC crypt_make_salt(char *p, int cnt /*, int x */) |
31 | { | 31 | { |
32 | x += getpid() + time(NULL); | 32 | /* was: x += ... */ |
33 | int x = getpid() + monotonic_us(); | ||
33 | do { | 34 | do { |
34 | /* x = (x*1664525 + 1013904223) % 2^32 generator is lame | 35 | /* x = (x*1664525 + 1013904223) % 2^32 generator is lame |
35 | * (low-order bit is not "random", etc...), | 36 | * (low-order bit is not "random", etc...), |
@@ -47,6 +48,26 @@ int FAST_FUNC crypt_make_salt(char *p, int cnt, int x) | |||
47 | return x; | 48 | return x; |
48 | } | 49 | } |
49 | 50 | ||
51 | char* FAST_FUNC crypt_make_pw_salt(char salt[MAX_PW_SALT_LEN], const char *algo) | ||
52 | { | ||
53 | int len = 2/2; | ||
54 | char *salt_ptr = salt; | ||
55 | if (algo[0] != 'd') { /* not des */ | ||
56 | len = 8/2; /* so far assuming md5 */ | ||
57 | *salt_ptr++ = '$'; | ||
58 | *salt_ptr++ = '1'; | ||
59 | *salt_ptr++ = '$'; | ||
60 | #if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA | ||
61 | if (algo[0] == 's') { /* sha */ | ||
62 | salt[1] = '5' + (strcmp(algo, "sha512") == 0); | ||
63 | len = 16/2; | ||
64 | } | ||
65 | #endif | ||
66 | } | ||
67 | crypt_make_salt(salt_ptr, len); | ||
68 | return salt_ptr; | ||
69 | } | ||
70 | |||
50 | #if ENABLE_USE_BB_CRYPT | 71 | #if ENABLE_USE_BB_CRYPT |
51 | 72 | ||
52 | static char* | 73 | static char* |
diff --git a/libbb/read_printf.c b/libbb/read_printf.c index 8664bc625..192f83d6e 100644 --- a/libbb/read_printf.c +++ b/libbb/read_printf.c | |||
@@ -55,32 +55,35 @@ | |||
55 | * which detects EAGAIN and uses poll() to wait on the fd. | 55 | * which detects EAGAIN and uses poll() to wait on the fd. |
56 | * Thankfully, poll() doesn't care about O_NONBLOCK flag. | 56 | * Thankfully, poll() doesn't care about O_NONBLOCK flag. |
57 | */ | 57 | */ |
58 | ssize_t FAST_FUNC nonblock_safe_read(int fd, void *buf, size_t count) | 58 | ssize_t FAST_FUNC nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR) |
59 | { | 59 | { |
60 | struct pollfd pfd[1]; | 60 | struct pollfd pfd[1]; |
61 | ssize_t n; | 61 | ssize_t n; |
62 | 62 | ||
63 | while (1) { | 63 | while (1) { |
64 | n = safe_read(fd, buf, count); | 64 | n = loop_on_EINTR ? safe_read(fd, buf, count) : read(fd, buf, count); |
65 | if (n >= 0 || errno != EAGAIN) | 65 | if (n >= 0 || errno != EAGAIN) |
66 | return n; | 66 | return n; |
67 | /* fd is in O_NONBLOCK mode. Wait using poll and repeat */ | 67 | /* fd is in O_NONBLOCK mode. Wait using poll and repeat */ |
68 | pfd[0].fd = fd; | 68 | pfd[0].fd = fd; |
69 | pfd[0].events = POLLIN; | 69 | pfd[0].events = POLLIN; |
70 | safe_poll(pfd, 1, -1); /* note: this pulls in printf */ | 70 | /* note: safe_poll pulls in printf */ |
71 | loop_on_EINTR ? safe_poll(pfd, 1, -1) : poll(pfd, 1, -1); | ||
71 | } | 72 | } |
72 | } | 73 | } |
73 | 74 | ||
74 | // Reads one line a-la fgets (but doesn't save terminating '\n'). | 75 | // Reads one line a-la fgets (but doesn't save terminating '\n'). |
75 | // Reads byte-by-byte. Useful when it is important to not read ahead. | 76 | // Reads byte-by-byte. Useful when it is important to not read ahead. |
76 | // Bytes are appended to pfx (which must be malloced, or NULL). | 77 | // Bytes are appended to pfx (which must be malloced, or NULL). |
77 | char* FAST_FUNC xmalloc_reads(int fd, char *buf, size_t *maxsz_p) | 78 | char* FAST_FUNC xmalloc_reads(int fd, size_t *maxsz_p) |
78 | { | 79 | { |
79 | char *p; | 80 | char *p; |
80 | size_t sz = buf ? strlen(buf) : 0; | 81 | char *buf = NULL; |
82 | size_t sz = 0; | ||
81 | size_t maxsz = maxsz_p ? *maxsz_p : (INT_MAX - 4095); | 83 | size_t maxsz = maxsz_p ? *maxsz_p : (INT_MAX - 4095); |
82 | 84 | ||
83 | goto jump_in; | 85 | goto jump_in; |
86 | |||
84 | while (sz < maxsz) { | 87 | while (sz < maxsz) { |
85 | if ((size_t)(p - buf) == sz) { | 88 | if ((size_t)(p - buf) == sz) { |
86 | jump_in: | 89 | jump_in: |
@@ -88,8 +91,8 @@ char* FAST_FUNC xmalloc_reads(int fd, char *buf, size_t *maxsz_p) | |||
88 | p = buf + sz; | 91 | p = buf + sz; |
89 | sz += 128; | 92 | sz += 128; |
90 | } | 93 | } |
91 | /* nonblock_safe_read() because we are used by e.g. shells */ | 94 | if (nonblock_immune_read(fd, p, 1, /*loop_on_EINTR:*/ 1) != 1) { |
92 | if (nonblock_safe_read(fd, p, 1) != 1) { /* EOF/error */ | 95 | /* EOF/error */ |
93 | if (p == buf) { /* we read nothing */ | 96 | if (p == buf) { /* we read nothing */ |
94 | free(buf); | 97 | free(buf); |
95 | return NULL; | 98 | return NULL; |
diff --git a/libpwdgrp/uidgid_get.c b/libpwdgrp/uidgid_get.c index 92290bfdb..8388be0da 100644 --- a/libpwdgrp/uidgid_get.c +++ b/libpwdgrp/uidgid_get.c | |||
@@ -71,7 +71,8 @@ int FAST_FUNC get_uidgid(struct bb_uidgid_t *u, const char *ug, int numeric_ok) | |||
71 | } | 71 | } |
72 | } | 72 | } |
73 | gr = getgrnam(group); | 73 | gr = getgrnam(group); |
74 | if (!gr) return 0; | 74 | if (!gr) |
75 | return 0; | ||
75 | u->gid = gr->gr_gid; | 76 | u->gid = gr->gr_gid; |
76 | } | 77 | } |
77 | return 1; | 78 | return 1; |
diff --git a/loginutils/adduser.c b/loginutils/adduser.c index 1944d9d56..47c674bf4 100644 --- a/loginutils/adduser.c +++ b/loginutils/adduser.c | |||
@@ -67,6 +67,7 @@ static void passwd_study(struct passwd *p) | |||
67 | } | 67 | } |
68 | if (p->pw_uid == max) { | 68 | if (p->pw_uid == max) { |
69 | bb_error_msg_and_die("no %cids left", 'u'); | 69 | bb_error_msg_and_die("no %cids left", 'u'); |
70 | /* this format string is reused in adduser and addgroup */ | ||
70 | } | 71 | } |
71 | p->pw_uid++; | 72 | p->pw_uid++; |
72 | } | 73 | } |
@@ -82,21 +83,32 @@ static void passwd_study(struct passwd *p) | |||
82 | 83 | ||
83 | static void addgroup_wrapper(struct passwd *p, const char *group_name) | 84 | static void addgroup_wrapper(struct passwd *p, const char *group_name) |
84 | { | 85 | { |
85 | char *argv[5]; | 86 | char *argv[6]; |
86 | 87 | ||
87 | argv[0] = (char*)"addgroup"; | 88 | argv[0] = (char*)"addgroup"; |
88 | if (group_name) { | 89 | if (group_name) { |
89 | /* Add user to existing group */ | 90 | /* Add user to existing group */ |
90 | argv[1] = p->pw_name; | 91 | argv[1] = (char*)"--"; |
91 | argv[2] = (char*)group_name; | 92 | argv[2] = p->pw_name; |
92 | argv[3] = NULL; | 93 | argv[3] = (char*)group_name; |
94 | argv[4] = NULL; | ||
93 | } else { | 95 | } else { |
94 | /* Add user to his own group with the first free gid found in passwd_study */ | 96 | /* Add user to his own group with the first free gid |
95 | //TODO: to be compatible with external addgroup programs we should use --gid instead... | 97 | * found in passwd_study. |
98 | */ | ||
99 | #if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS || !ENABLE_ADDGROUP | ||
100 | /* We try to use --gid, not -g, because "standard" addgroup | ||
101 | * has no short option -g, it has only long --gid. | ||
102 | */ | ||
103 | argv[1] = (char*)"--gid"; | ||
104 | #else | ||
105 | /* Breaks if system in fact does NOT use busybox addgroup */ | ||
96 | argv[1] = (char*)"-g"; | 106 | argv[1] = (char*)"-g"; |
107 | #endif | ||
97 | argv[2] = utoa(p->pw_gid); | 108 | argv[2] = utoa(p->pw_gid); |
98 | argv[3] = p->pw_name; | 109 | argv[3] = (char*)"--"; |
99 | argv[4] = NULL; | 110 | argv[4] = p->pw_name; |
111 | argv[5] = NULL; | ||
100 | } | 112 | } |
101 | 113 | ||
102 | spawn_and_wait(argv); | 114 | spawn_and_wait(argv); |
@@ -106,7 +118,7 @@ static void passwd_wrapper(const char *login_name) NORETURN; | |||
106 | 118 | ||
107 | static void passwd_wrapper(const char *login_name) | 119 | static void passwd_wrapper(const char *login_name) |
108 | { | 120 | { |
109 | BB_EXECLP("passwd", "passwd", login_name, NULL); | 121 | BB_EXECLP("passwd", "passwd", "--", login_name, NULL); |
110 | bb_error_msg_and_die("can't execute passwd, you must set password manually"); | 122 | bb_error_msg_and_die("can't execute passwd, you must set password manually"); |
111 | } | 123 | } |
112 | 124 | ||
@@ -228,9 +240,11 @@ int adduser_main(int argc UNUSED_PARAM, char **argv) | |||
228 | if (mkdir_err == 0) { | 240 | if (mkdir_err == 0) { |
229 | /* New home. Copy /etc/skel to it */ | 241 | /* New home. Copy /etc/skel to it */ |
230 | const char *args[] = { | 242 | const char *args[] = { |
231 | "chown", "-R", | 243 | "chown", |
244 | "-R", | ||
232 | xasprintf("%u:%u", (int)pw.pw_uid, (int)pw.pw_gid), | 245 | xasprintf("%u:%u", (int)pw.pw_uid, (int)pw.pw_gid), |
233 | pw.pw_dir, NULL | 246 | pw.pw_dir, |
247 | NULL | ||
234 | }; | 248 | }; |
235 | /* Be silent on any errors (like: no /etc/skel) */ | 249 | /* Be silent on any errors (like: no /etc/skel) */ |
236 | logmode = LOGMODE_NONE; | 250 | logmode = LOGMODE_NONE; |
diff --git a/loginutils/chpasswd.c b/loginutils/chpasswd.c index 6c4296faa..f4718c829 100644 --- a/loginutils/chpasswd.c +++ b/loginutils/chpasswd.c | |||
@@ -37,9 +37,8 @@ int chpasswd_main(int argc UNUSED_PARAM, char **argv) | |||
37 | char *name, *pass; | 37 | char *name, *pass; |
38 | char salt[sizeof("$N$XXXXXXXX")]; | 38 | char salt[sizeof("$N$XXXXXXXX")]; |
39 | int opt, rc; | 39 | int opt, rc; |
40 | int rnd = rnd; /* we *want* it to be non-initialized! */ | ||
41 | 40 | ||
42 | if (getuid()) | 41 | if (getuid() != 0) |
43 | bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); | 42 | bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); |
44 | 43 | ||
45 | opt_complementary = "m--e:e--m"; | 44 | opt_complementary = "m--e:e--m"; |
@@ -55,10 +54,12 @@ int chpasswd_main(int argc UNUSED_PARAM, char **argv) | |||
55 | xuname2uid(name); /* dies if there is no such user */ | 54 | xuname2uid(name); /* dies if there is no such user */ |
56 | 55 | ||
57 | if (!(opt & OPT_ENC)) { | 56 | if (!(opt & OPT_ENC)) { |
58 | rnd = crypt_make_salt(salt, 1, rnd); | 57 | crypt_make_salt(salt, 1); |
59 | if (opt & OPT_MD5) { | 58 | if (opt & OPT_MD5) { |
60 | strcpy(salt, "$1$"); | 59 | salt[0] = '$'; |
61 | rnd = crypt_make_salt(salt + 3, 4, rnd); | 60 | salt[1] = '1'; |
61 | salt[2] = '$'; | ||
62 | crypt_make_salt(salt + 3, 4); | ||
62 | } | 63 | } |
63 | pass = pw_encrypt(pass, salt, 0); | 64 | pass = pw_encrypt(pass, salt, 0); |
64 | } | 65 | } |
diff --git a/loginutils/cryptpw.c b/loginutils/cryptpw.c index 72388c492..b25a39ac9 100644 --- a/loginutils/cryptpw.c +++ b/loginutils/cryptpw.c | |||
@@ -14,12 +14,12 @@ | |||
14 | //usage: "[OPTIONS] [PASSWORD] [SALT]" | 14 | //usage: "[OPTIONS] [PASSWORD] [SALT]" |
15 | /* We do support -s, we just don't mention it */ | 15 | /* We do support -s, we just don't mention it */ |
16 | //usage:#define cryptpw_full_usage "\n\n" | 16 | //usage:#define cryptpw_full_usage "\n\n" |
17 | //usage: "Crypt the PASSWORD using crypt(3)\n" | 17 | //usage: "Crypt PASSWORD using crypt(3)\n" |
18 | //usage: "\nOptions:" | 18 | //usage: "\nOptions:" |
19 | //usage: IF_LONG_OPTS( | 19 | //usage: IF_LONG_OPTS( |
20 | //usage: "\n -P,--password-fd=N Read password from fd N" | 20 | //usage: "\n -P,--password-fd=N Read password from fd N" |
21 | /* //usage: "\n -s,--stdin Use stdin; like -P0" */ | 21 | /* //usage: "\n -s,--stdin Use stdin; like -P0" */ |
22 | //usage: "\n -m,--method=TYPE Encryption method TYPE" | 22 | //usage: "\n -m,--method=TYPE Encryption method" |
23 | //usage: "\n -S,--salt=SALT" | 23 | //usage: "\n -S,--salt=SALT" |
24 | //usage: ) | 24 | //usage: ) |
25 | //usage: IF_NOT_LONG_OPTS( | 25 | //usage: IF_NOT_LONG_OPTS( |
@@ -34,12 +34,12 @@ | |||
34 | //usage: "[OPTIONS] [PASSWORD] [SALT]" | 34 | //usage: "[OPTIONS] [PASSWORD] [SALT]" |
35 | /* We do support -s, we just don't mention it */ | 35 | /* We do support -s, we just don't mention it */ |
36 | //usage:#define mkpasswd_full_usage "\n\n" | 36 | //usage:#define mkpasswd_full_usage "\n\n" |
37 | //usage: "Crypt the PASSWORD using crypt(3)\n" | 37 | //usage: "Crypt PASSWORD using crypt(3)\n" |
38 | //usage: "\nOptions:" | 38 | //usage: "\nOptions:" |
39 | //usage: IF_LONG_OPTS( | 39 | //usage: IF_LONG_OPTS( |
40 | //usage: "\n -P,--password-fd=N Read password from fd N" | 40 | //usage: "\n -P,--password-fd=N Read password from fd N" |
41 | /* //usage: "\n -s,--stdin Use stdin; like -P0" */ | 41 | /* //usage: "\n -s,--stdin Use stdin; like -P0" */ |
42 | //usage: "\n -m,--method=TYPE Encryption method TYPE" | 42 | //usage: "\n -m,--method=TYPE Encryption method" |
43 | //usage: "\n -S,--salt=SALT" | 43 | //usage: "\n -S,--salt=SALT" |
44 | //usage: ) | 44 | //usage: ) |
45 | //usage: IF_NOT_LONG_OPTS( | 45 | //usage: IF_NOT_LONG_OPTS( |
@@ -92,11 +92,9 @@ to cryptpw. -a option (alias for -m) came from cryptpw. | |||
92 | int cryptpw_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 92 | int cryptpw_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
93 | int cryptpw_main(int argc UNUSED_PARAM, char **argv) | 93 | int cryptpw_main(int argc UNUSED_PARAM, char **argv) |
94 | { | 94 | { |
95 | /* $N$ + sha_salt_16_bytes + NUL */ | 95 | char salt[MAX_PW_SALT_LEN]; |
96 | char salt[3 + 16 + 1]; | ||
97 | char *salt_ptr; | 96 | char *salt_ptr; |
98 | const char *opt_m, *opt_S; | 97 | const char *opt_m, *opt_S; |
99 | int len; | ||
100 | int fd; | 98 | int fd; |
101 | 99 | ||
102 | #if ENABLE_LONG_OPTS | 100 | #if ENABLE_LONG_OPTS |
@@ -121,24 +119,9 @@ int cryptpw_main(int argc UNUSED_PARAM, char **argv) | |||
121 | if (argv[0] && !opt_S) | 119 | if (argv[0] && !opt_S) |
122 | opt_S = argv[1]; | 120 | opt_S = argv[1]; |
123 | 121 | ||
124 | len = 2/2; | 122 | salt_ptr = crypt_make_pw_salt(salt, opt_m); |
125 | salt_ptr = salt; | ||
126 | if (opt_m[0] != 'd') { /* not des */ | ||
127 | len = 8/2; /* so far assuming md5 */ | ||
128 | *salt_ptr++ = '$'; | ||
129 | *salt_ptr++ = '1'; | ||
130 | *salt_ptr++ = '$'; | ||
131 | #if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA | ||
132 | if (opt_m[0] == 's') { /* sha */ | ||
133 | salt[1] = '5' + (strcmp(opt_m, "sha512") == 0); | ||
134 | len = 16/2; | ||
135 | } | ||
136 | #endif | ||
137 | } | ||
138 | if (opt_S) | 123 | if (opt_S) |
139 | safe_strncpy(salt_ptr, opt_S, sizeof(salt) - 3); | 124 | safe_strncpy(salt_ptr, opt_S, sizeof(salt) - (sizeof("$N$")-1)); |
140 | else | ||
141 | crypt_make_salt(salt_ptr, len, 0); | ||
142 | 125 | ||
143 | xmove_fd(fd, STDIN_FILENO); | 126 | xmove_fd(fd, STDIN_FILENO); |
144 | 127 | ||
diff --git a/loginutils/passwd.c b/loginutils/passwd.c index 810644e61..b3ce646f1 100644 --- a/loginutils/passwd.c +++ b/loginutils/passwd.c | |||
@@ -6,13 +6,13 @@ | |||
6 | //usage:#define passwd_trivial_usage | 6 | //usage:#define passwd_trivial_usage |
7 | //usage: "[OPTIONS] [USER]" | 7 | //usage: "[OPTIONS] [USER]" |
8 | //usage:#define passwd_full_usage "\n\n" | 8 | //usage:#define passwd_full_usage "\n\n" |
9 | //usage: "Change USER's password. If no USER is specified,\n" | 9 | //usage: "Change USER's password (default: current user)" |
10 | //usage: "changes the password for the current user.\n" | 10 | //usage: "\n" |
11 | //usage: "\nOptions:" | 11 | //usage: "\nOptions:" |
12 | //usage: "\n -a ALG Algorithm to use for password (des, md5)" /* ", sha1)" */ | 12 | //usage: "\n -a ALG Encryption method" |
13 | //usage: "\n -d Delete password for the account" | 13 | //usage: "\n -d Set password to ''" |
14 | //usage: "\n -l Lock (disable) account" | 14 | //usage: "\n -l Lock (disable) account" |
15 | //usage: "\n -u Unlock (re-enable) account" | 15 | //usage: "\n -u Unlock (enable) account" |
16 | 16 | ||
17 | #include "libbb.h" | 17 | #include "libbb.h" |
18 | #include <syslog.h> | 18 | #include <syslog.h> |
@@ -22,15 +22,15 @@ static void nuke_str(char *str) | |||
22 | if (str) memset(str, 0, strlen(str)); | 22 | if (str) memset(str, 0, strlen(str)); |
23 | } | 23 | } |
24 | 24 | ||
25 | static char* new_password(const struct passwd *pw, uid_t myuid, int algo) | 25 | static char* new_password(const struct passwd *pw, uid_t myuid, const char *algo) |
26 | { | 26 | { |
27 | char salt[sizeof("$N$XXXXXXXX")]; /* "$N$XXXXXXXX" or "XX" */ | 27 | char salt[MAX_PW_SALT_LEN]; |
28 | char *orig = (char*)""; | 28 | char *orig = (char*)""; |
29 | char *newp = NULL; | 29 | char *newp = NULL; |
30 | char *cp = NULL; | 30 | char *cp = NULL; |
31 | char *ret = NULL; /* failure so far */ | 31 | char *ret = NULL; /* failure so far */ |
32 | 32 | ||
33 | if (myuid && pw->pw_passwd[0]) { | 33 | if (myuid != 0 && pw->pw_passwd[0]) { |
34 | char *encrypted; | 34 | char *encrypted; |
35 | 35 | ||
36 | orig = bb_ask_stdin("Old password: "); /* returns ptr to static */ | 36 | orig = bb_ask_stdin("Old password: "); /* returns ptr to static */ |
@@ -38,13 +38,13 @@ static char* new_password(const struct passwd *pw, uid_t myuid, int algo) | |||
38 | goto err_ret; | 38 | goto err_ret; |
39 | encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */ | 39 | encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */ |
40 | if (strcmp(encrypted, pw->pw_passwd) != 0) { | 40 | if (strcmp(encrypted, pw->pw_passwd) != 0) { |
41 | syslog(LOG_WARNING, "incorrect password for %s", | 41 | syslog(LOG_WARNING, "incorrect password for %s", pw->pw_name); |
42 | pw->pw_name); | ||
43 | bb_do_delay(LOGIN_FAIL_DELAY); | 42 | bb_do_delay(LOGIN_FAIL_DELAY); |
44 | puts("Incorrect password"); | 43 | puts("Incorrect password"); |
45 | goto err_ret; | 44 | goto err_ret; |
46 | } | 45 | } |
47 | if (ENABLE_FEATURE_CLEAN_UP) free(encrypted); | 46 | if (ENABLE_FEATURE_CLEAN_UP) |
47 | free(encrypted); | ||
48 | } | 48 | } |
49 | orig = xstrdup(orig); /* or else bb_ask_stdin() will destroy it */ | 49 | orig = xstrdup(orig); /* or else bb_ask_stdin() will destroy it */ |
50 | newp = bb_ask_stdin("New password: "); /* returns ptr to static */ | 50 | newp = bb_ask_stdin("New password: "); /* returns ptr to static */ |
@@ -52,22 +52,22 @@ static char* new_password(const struct passwd *pw, uid_t myuid, int algo) | |||
52 | goto err_ret; | 52 | goto err_ret; |
53 | newp = xstrdup(newp); /* we are going to bb_ask_stdin() again, so save it */ | 53 | newp = xstrdup(newp); /* we are going to bb_ask_stdin() again, so save it */ |
54 | if (ENABLE_FEATURE_PASSWD_WEAK_CHECK | 54 | if (ENABLE_FEATURE_PASSWD_WEAK_CHECK |
55 | && obscure(orig, newp, pw) && myuid) | 55 | && obscure(orig, newp, pw) |
56 | && myuid != 0 | ||
57 | ) { | ||
56 | goto err_ret; /* non-root is not allowed to have weak passwd */ | 58 | goto err_ret; /* non-root is not allowed to have weak passwd */ |
59 | } | ||
57 | 60 | ||
58 | cp = bb_ask_stdin("Retype password: "); | 61 | cp = bb_ask_stdin("Retype password: "); |
59 | if (!cp) | 62 | if (!cp) |
60 | goto err_ret; | 63 | goto err_ret; |
61 | if (strcmp(cp, newp)) { | 64 | if (strcmp(cp, newp) != 0) { |
62 | puts("Passwords don't match"); | 65 | puts("Passwords don't match"); |
63 | goto err_ret; | 66 | goto err_ret; |
64 | } | 67 | } |
65 | 68 | ||
66 | crypt_make_salt(salt, 1, 0); /* des */ | 69 | crypt_make_pw_salt(salt, algo); |
67 | if (algo) { /* MD5 */ | 70 | |
68 | strcpy(salt, "$1$"); | ||
69 | crypt_make_salt(salt + 3, 4, 0); | ||
70 | } | ||
71 | /* pw_encrypt returns malloced str */ | 71 | /* pw_encrypt returns malloced str */ |
72 | ret = pw_encrypt(newp, salt, 1); | 72 | ret = pw_encrypt(newp, salt, 1); |
73 | /* whee, success! */ | 73 | /* whee, success! */ |
@@ -75,8 +75,10 @@ static char* new_password(const struct passwd *pw, uid_t myuid, int algo) | |||
75 | err_ret: | 75 | err_ret: |
76 | nuke_str(orig); | 76 | nuke_str(orig); |
77 | if (ENABLE_FEATURE_CLEAN_UP) free(orig); | 77 | if (ENABLE_FEATURE_CLEAN_UP) free(orig); |
78 | |||
78 | nuke_str(newp); | 79 | nuke_str(newp); |
79 | if (ENABLE_FEATURE_CLEAN_UP) free(newp); | 80 | if (ENABLE_FEATURE_CLEAN_UP) free(newp); |
81 | |||
80 | nuke_str(cp); | 82 | nuke_str(cp); |
81 | return ret; | 83 | return ret; |
82 | } | 84 | } |
@@ -85,17 +87,15 @@ int passwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | |||
85 | int passwd_main(int argc UNUSED_PARAM, char **argv) | 87 | int passwd_main(int argc UNUSED_PARAM, char **argv) |
86 | { | 88 | { |
87 | enum { | 89 | enum { |
88 | OPT_algo = 0x1, /* -a - password algorithm */ | 90 | OPT_algo = (1 << 0), /* -a - password algorithm */ |
89 | OPT_lock = 0x2, /* -l - lock account */ | 91 | OPT_lock = (1 << 1), /* -l - lock account */ |
90 | OPT_unlock = 0x4, /* -u - unlock account */ | 92 | OPT_unlock = (1 << 2), /* -u - unlock account */ |
91 | OPT_delete = 0x8, /* -d - delete password */ | 93 | OPT_delete = (1 << 3), /* -d - delete password */ |
92 | OPT_lud = 0xe, | 94 | OPT_lud = OPT_lock | OPT_unlock | OPT_delete, |
93 | STATE_ALGO_md5 = 0x10, | ||
94 | //STATE_ALGO_des = 0x20, not needed yet | ||
95 | }; | 95 | }; |
96 | unsigned opt; | 96 | unsigned opt; |
97 | int rc; | 97 | int rc; |
98 | const char *opt_a = ""; | 98 | const char *opt_a = "d"; /* des */ |
99 | const char *filename; | 99 | const char *filename; |
100 | char *myname; | 100 | char *myname; |
101 | char *name; | 101 | char *name; |
@@ -116,13 +116,9 @@ int passwd_main(int argc UNUSED_PARAM, char **argv) | |||
116 | //argc -= optind; | 116 | //argc -= optind; |
117 | argv += optind; | 117 | argv += optind; |
118 | 118 | ||
119 | if (strcasecmp(opt_a, "des") != 0) /* -a */ | ||
120 | opt |= STATE_ALGO_md5; | ||
121 | //else | ||
122 | // opt |= STATE_ALGO_des; | ||
123 | myuid = getuid(); | 119 | myuid = getuid(); |
124 | /* -l, -u, -d require root priv and username argument */ | 120 | /* -l, -u, -d require root priv and username argument */ |
125 | if ((opt & OPT_lud) && (myuid || !argv[0])) | 121 | if ((opt & OPT_lud) && (myuid != 0 || !argv[0])) |
126 | bb_show_usage(); | 122 | bb_show_usage(); |
127 | 123 | ||
128 | /* Will complain and die if username not found */ | 124 | /* Will complain and die if username not found */ |
@@ -130,7 +126,7 @@ int passwd_main(int argc UNUSED_PARAM, char **argv) | |||
130 | name = argv[0] ? argv[0] : myname; | 126 | name = argv[0] ? argv[0] : myname; |
131 | 127 | ||
132 | pw = xgetpwnam(name); | 128 | pw = xgetpwnam(name); |
133 | if (myuid && pw->pw_uid != myuid) { | 129 | if (myuid != 0 && pw->pw_uid != myuid) { |
134 | /* LOGMODE_BOTH */ | 130 | /* LOGMODE_BOTH */ |
135 | bb_error_msg_and_die("%s can't change password for %s", myname, name); | 131 | bb_error_msg_and_die("%s can't change password for %s", myname, name); |
136 | } | 132 | } |
@@ -164,27 +160,29 @@ int passwd_main(int argc UNUSED_PARAM, char **argv) | |||
164 | newp = NULL; | 160 | newp = NULL; |
165 | c = pw->pw_passwd[0] - '!'; | 161 | c = pw->pw_passwd[0] - '!'; |
166 | if (!(opt & OPT_lud)) { | 162 | if (!(opt & OPT_lud)) { |
167 | if (myuid && !c) { /* passwd starts with '!' */ | 163 | if (myuid != 0 && !c) { /* passwd starts with '!' */ |
168 | /* LOGMODE_BOTH */ | 164 | /* LOGMODE_BOTH */ |
169 | bb_error_msg_and_die("can't change " | 165 | bb_error_msg_and_die("can't change " |
170 | "locked password for %s", name); | 166 | "locked password for %s", name); |
171 | } | 167 | } |
172 | printf("Changing password for %s\n", name); | 168 | printf("Changing password for %s\n", name); |
173 | newp = new_password(pw, myuid, opt & STATE_ALGO_md5); | 169 | newp = new_password(pw, myuid, opt_a); |
174 | if (!newp) { | 170 | if (!newp) { |
175 | logmode = LOGMODE_STDIO; | 171 | logmode = LOGMODE_STDIO; |
176 | bb_error_msg_and_die("password for %s is unchanged", name); | 172 | bb_error_msg_and_die("password for %s is unchanged", name); |
177 | } | 173 | } |
178 | } else if (opt & OPT_lock) { | 174 | } else if (opt & OPT_lock) { |
179 | if (!c) goto skip; /* passwd starts with '!' */ | 175 | if (!c) |
176 | goto skip; /* passwd starts with '!' */ | ||
180 | newp = xasprintf("!%s", pw->pw_passwd); | 177 | newp = xasprintf("!%s", pw->pw_passwd); |
181 | } else if (opt & OPT_unlock) { | 178 | } else if (opt & OPT_unlock) { |
182 | if (c) goto skip; /* not '!' */ | 179 | if (c) |
180 | goto skip; /* not '!' */ | ||
183 | /* pw->pw_passwd points to static storage, | 181 | /* pw->pw_passwd points to static storage, |
184 | * strdup'ing to avoid nasty surprizes */ | 182 | * strdup'ing to avoid nasty surprizes */ |
185 | newp = xstrdup(&pw->pw_passwd[1]); | 183 | newp = xstrdup(&pw->pw_passwd[1]); |
186 | } else if (opt & OPT_delete) { | 184 | } else if (opt & OPT_delete) { |
187 | newp = (char*)""; //xstrdup(""); | 185 | newp = (char*)""; |
188 | } | 186 | } |
189 | 187 | ||
190 | rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000; | 188 | rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000; |
@@ -202,7 +200,7 @@ int passwd_main(int argc UNUSED_PARAM, char **argv) | |||
202 | rc = update_passwd(bb_path_shadow_file, name, newp, NULL); | 200 | rc = update_passwd(bb_path_shadow_file, name, newp, NULL); |
203 | if (rc > 0) | 201 | if (rc > 0) |
204 | /* password in /etc/shadow was updated */ | 202 | /* password in /etc/shadow was updated */ |
205 | newp = (char*) "x"; //xstrdup("x"); | 203 | newp = (char*) "x"; |
206 | if (rc >= 0) | 204 | if (rc >= 0) |
207 | /* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */ | 205 | /* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */ |
208 | #endif | 206 | #endif |
@@ -212,16 +210,17 @@ int passwd_main(int argc UNUSED_PARAM, char **argv) | |||
212 | } | 210 | } |
213 | /* LOGMODE_BOTH */ | 211 | /* LOGMODE_BOTH */ |
214 | if (rc < 0) | 212 | if (rc < 0) |
215 | bb_error_msg_and_die("can't update password file %s", | 213 | bb_error_msg_and_die("can't update password file %s", filename); |
216 | filename); | ||
217 | bb_info_msg("Password for %s changed by %s", name, myname); | 214 | bb_info_msg("Password for %s changed by %s", name, myname); |
218 | 215 | ||
219 | //if (ENABLE_FEATURE_CLEAN_UP) free(newp); | 216 | /*if (ENABLE_FEATURE_CLEAN_UP) free(newp); - can't, it may be non-malloced */ |
220 | skip: | 217 | skip: |
221 | if (!newp) { | 218 | if (!newp) { |
222 | bb_error_msg_and_die("password for %s is already %slocked", | 219 | bb_error_msg_and_die("password for %s is already %slocked", |
223 | name, (opt & OPT_unlock) ? "un" : ""); | 220 | name, (opt & OPT_unlock) ? "un" : ""); |
224 | } | 221 | } |
225 | if (ENABLE_FEATURE_CLEAN_UP) free(myname); | 222 | |
223 | if (ENABLE_FEATURE_CLEAN_UP) | ||
224 | free(myname); | ||
226 | return 0; | 225 | return 0; |
227 | } | 226 | } |
diff --git a/mailutils/Kbuild.src b/mailutils/Kbuild.src index 20220dac8..6b4fb7470 100644 --- a/mailutils/Kbuild.src +++ b/mailutils/Kbuild.src | |||
@@ -7,7 +7,3 @@ | |||
7 | lib-y:= | 7 | lib-y:= |
8 | 8 | ||
9 | INSERT | 9 | INSERT |
10 | lib-$(CONFIG_MAKEMIME) += mime.o mail.o | ||
11 | lib-$(CONFIG_POPMAILDIR) += popmaildir.o mail.o | ||
12 | lib-$(CONFIG_REFORMIME) += mime.o mail.o | ||
13 | lib-$(CONFIG_SENDMAIL) += sendmail.o mail.o | ||
diff --git a/mailutils/mail.c b/mailutils/mail.c index 44957016f..66c79471f 100644 --- a/mailutils/mail.c +++ b/mailutils/mail.c | |||
@@ -172,8 +172,8 @@ void FAST_FUNC get_cred_or_die(int fd) | |||
172 | G.user = xstrdup(bb_ask(fd, /* timeout: */ 0, "User: ")); | 172 | G.user = xstrdup(bb_ask(fd, /* timeout: */ 0, "User: ")); |
173 | G.pass = xstrdup(bb_ask(fd, /* timeout: */ 0, "Password: ")); | 173 | G.pass = xstrdup(bb_ask(fd, /* timeout: */ 0, "Password: ")); |
174 | } else { | 174 | } else { |
175 | G.user = xmalloc_reads(fd, /* pfx: */ NULL, /* maxsize: */ NULL); | 175 | G.user = xmalloc_reads(fd, /* maxsize: */ NULL); |
176 | G.pass = xmalloc_reads(fd, /* pfx: */ NULL, /* maxsize: */ NULL); | 176 | G.pass = xmalloc_reads(fd, /* maxsize: */ NULL); |
177 | } | 177 | } |
178 | if (!G.user || !*G.user || !G.pass) | 178 | if (!G.user || !*G.user || !G.pass) |
179 | bb_error_msg_and_die("no username or password"); | 179 | bb_error_msg_and_die("no username or password"); |
diff --git a/mailutils/mime.c b/mailutils/makemime.c index 0aff8b1d7..628619bb8 100644 --- a/mailutils/mime.c +++ b/mailutils/makemime.c | |||
@@ -8,35 +8,17 @@ | |||
8 | * Licensed under GPLv2, see file LICENSE in this source tree. | 8 | * Licensed under GPLv2, see file LICENSE in this source tree. |
9 | */ | 9 | */ |
10 | 10 | ||
11 | //usage:#define makemime_trivial_usage | 11 | //kbuild:lib-$(CONFIG_MAKEMIME) += makemime.o mail.o |
12 | //usage: "[OPTIONS] [FILE]..." | ||
13 | //usage:#define makemime_full_usage "\n\n" | ||
14 | //usage: "Create multipart MIME-encoded message from FILEs\n" | ||
15 | /* //usage: "Transfer encoding is base64, disposition is inline (not attachment)\n" */ | ||
16 | //usage: "\nOptions:" | ||
17 | //usage: "\n -o FILE Output. Default: stdout" | ||
18 | //usage: "\n -a HDR Add header. Examples:" | ||
19 | //usage: "\n \"From: user@host.org\", \"Date: `date -R`\"" | ||
20 | //usage: "\n -c CT Content type. Default: text/plain" | ||
21 | //usage: "\n -C CS Charset. Default: " CONFIG_FEATURE_MIME_CHARSET | ||
22 | /* //usage: "\n -e ENC Transfer encoding. Ignored. base64 is assumed" */ | ||
23 | //usage: "\n" | ||
24 | //usage: "\nOther options are silently ignored" | ||
25 | |||
26 | //usage:#define reformime_trivial_usage | ||
27 | //usage: "[OPTIONS] [FILE]..." | ||
28 | //usage:#define reformime_full_usage "\n\n" | ||
29 | //usage: "Parse MIME-encoded message\n" | ||
30 | //usage: "\nOptions:" | ||
31 | //usage: "\n -x PREFIX Extract content of MIME sections to files" | ||
32 | //usage: "\n -X PROG ARGS Filter content of MIME sections through PROG" | ||
33 | //usage: "\n Must be the last option" | ||
34 | //usage: "\n" | ||
35 | //usage: "\nOther options are silently ignored" | ||
36 | 12 | ||
37 | #include "libbb.h" | 13 | #include "libbb.h" |
38 | #include "mail.h" | 14 | #include "mail.h" |
39 | 15 | ||
16 | #if 0 | ||
17 | # define dbg_error_msg(...) bb_error_msg(__VA_ARGS__) | ||
18 | #else | ||
19 | # define dbg_error_msg(...) ((void)0) | ||
20 | #endif | ||
21 | |||
40 | /* | 22 | /* |
41 | makemime -c type [-o file] [-e encoding] [-C charset] [-N name] \ | 23 | makemime -c type [-o file] [-e encoding] [-C charset] [-N name] \ |
42 | [-a "Header: Contents"] file | 24 | [-a "Header: Contents"] file |
@@ -50,7 +32,6 @@ | |||
50 | \( opts \) - read from child process, that generates [ opts ] | 32 | \( opts \) - read from child process, that generates [ opts ] |
51 | 33 | ||
52 | Options: | 34 | Options: |
53 | |||
54 | -c type - create a new MIME section from "file" with this | 35 | -c type - create a new MIME section from "file" with this |
55 | Content-Type: (default is application/octet-stream). | 36 | Content-Type: (default is application/octet-stream). |
56 | -C charset - MIME charset of a new text/plain section. | 37 | -C charset - MIME charset of a new text/plain section. |
@@ -70,6 +51,28 @@ Options: | |||
70 | value on each line. | 51 | value on each line. |
71 | {which version of makemime is this? What do we support?} | 52 | {which version of makemime is this? What do we support?} |
72 | */ | 53 | */ |
54 | /* man makemime: | ||
55 | |||
56 | * -c TYPE: create a (non-multipart) MIME section with Content-Type: TYPE | ||
57 | * makemime -c TYPE [-e ENCODING] [-o OUTFILE] [-C CHARSET] [-N NAME] [-a HEADER...] FILE | ||
58 | * The -C option sets the MIME charset attribute for text/plain content. | ||
59 | * The -N option sets the name attribute for Content-Type: | ||
60 | * Encoding must be one of the following: 7bit, 8bit, quoted-printable, or base64. | ||
61 | |||
62 | * -m multipart/TYPE: create a multipart MIME collection with Content-Type: multipart/TYPE | ||
63 | * makemime -m multipart/TYPE [-e ENCODING] [-o OUTFILE] [-a HEADER...] FILE | ||
64 | * Type must be either "multipart/mixed", "multipart/alternative", or some other MIME multipart content type. | ||
65 | * Additionally, encoding can only be "7bit" or "8bit", and will default to "8bit" if not specified. | ||
66 | * Finally, filename must be a MIME-formatted section, NOT a regular file. | ||
67 | * The -m option creates an initial multipart MIME collection, that contains only one MIME section, taken from filename. | ||
68 | * The collection is written to standard output, or the pipe or to outputfile. | ||
69 | |||
70 | * -j FILE1: add a section to a multipart MIME collection | ||
71 | * makemime -j FILE1 [-o OUTFILE] FILE2 | ||
72 | * FILE1 must be a MIME collection that was previously created by the -m option. | ||
73 | * FILE2 must be a MIME section that was previously created by the -c option. | ||
74 | * The -j options adds the MIME section in FILE2 to the MIME collection in FILE1. | ||
75 | */ | ||
73 | 76 | ||
74 | 77 | ||
75 | /* In busybox 1.15.0.svn, makemime generates output like this | 78 | /* In busybox 1.15.0.svn, makemime generates output like this |
@@ -92,10 +95,8 @@ Content-Transfer-Encoding: base64 | |||
92 | ...file B contents... | 95 | ...file B contents... |
93 | --24269534-2145583448-1655890676-- | 96 | --24269534-2145583448-1655890676-- |
94 | 97 | ||
95 | */ | 98 | * |
96 | 99 | * For reference: here is an example email to LKML which has | |
97 | |||
98 | /* For reference: here is an example email to LKML which has | ||
99 | * 1st unnamed part (so it serves as an email body) | 100 | * 1st unnamed part (so it serves as an email body) |
100 | * and one attached file: | 101 | * and one attached file: |
101 | ...other headers... | 102 | ...other headers... |
@@ -126,28 +127,21 @@ Content-Transfer-Encoding: 7bit | |||
126 | ...random junk added by mailing list robots and such... | 127 | ...random junk added by mailing list robots and such... |
127 | */ | 128 | */ |
128 | 129 | ||
129 | /* man makemime: | 130 | //usage:#define makemime_trivial_usage |
130 | 131 | //usage: "[OPTIONS] [FILE]..." | |
131 | * -c TYPE: create a (non-multipart) MIME section with Content-Type: TYPE | 132 | //usage:#define makemime_full_usage "\n\n" |
132 | * makemime -c TYPE [-e ENCODING] [-o OUTFILE] [-C CHARSET] [-N NAME] [-a HEADER...] FILE | 133 | //usage: "Create multipart MIME-encoded message from FILEs\n" |
133 | * The -C option sets the MIME charset attribute for text/plain content. | 134 | /* //usage: "Transfer encoding is base64, disposition is inline (not attachment)\n" */ |
134 | * The -N option sets the name attribute for Content-Type: | 135 | //usage: "\nOptions:" |
135 | * Encoding must be one of the following: 7bit, 8bit, quoted-printable, or base64. | 136 | //usage: "\n -o FILE Output. Default: stdout" |
136 | 137 | //usage: "\n -a HDR Add header. Examples:" | |
137 | * -m multipart/TYPE: create a multipart MIME collection with Content-Type: multipart/TYPE | 138 | //usage: "\n \"From: user@host.org\", \"Date: `date -R`\"" |
138 | * makemime -m multipart/TYPE [-e ENCODING] [-o OUTFILE] [-a HEADER...] FILE | 139 | //usage: "\n -c CT Content type. Default: text/plain" |
139 | * Type must be either "multipart/mixed", "multipart/alternative", or some other MIME multipart content type. | 140 | //usage: "\n -C CS Charset. Default: " CONFIG_FEATURE_MIME_CHARSET |
140 | * Additionally, encoding can only be "7bit" or "8bit", and will default to "8bit" if not specified. | 141 | /* //usage: "\n -e ENC Transfer encoding. Ignored. base64 is assumed" */ |
141 | * Finally, filename must be a MIME-formatted section, NOT a regular file. | 142 | //usage: "\n" |
142 | * The -m option creates an initial multipart MIME collection, that contains only one MIME section, taken from filename. | 143 | //usage: "\nOther options are silently ignored" |
143 | * The collection is written to standard output, or the pipe or to outputfile. | ||
144 | 144 | ||
145 | * -j FILE1: add a section to a multipart MIME collection | ||
146 | * makemime -j FILE1 [-o OUTFILE] FILE2 | ||
147 | * FILE1 must be a MIME collection that was previously created by the -m option. | ||
148 | * FILE2 must be a MIME section that was previously created by the -c option. | ||
149 | * The -j options adds the MIME section in FILE2 to the MIME collection in FILE1. | ||
150 | */ | ||
151 | int makemime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 145 | int makemime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
152 | int makemime_main(int argc UNUSED_PARAM, char **argv) | 146 | int makemime_main(int argc UNUSED_PARAM, char **argv) |
153 | { | 147 | { |
@@ -222,244 +216,3 @@ int makemime_main(int argc UNUSED_PARAM, char **argv) | |||
222 | return EXIT_SUCCESS; | 216 | return EXIT_SUCCESS; |
223 | #undef boundary | 217 | #undef boundary |
224 | } | 218 | } |
225 | |||
226 | static const char *find_token(const char *const string_array[], const char *key, const char *defvalue) | ||
227 | { | ||
228 | const char *r = NULL; | ||
229 | int i; | ||
230 | for (i = 0; string_array[i] != NULL; i++) { | ||
231 | if (strcasecmp(string_array[i], key) == 0) { | ||
232 | r = (char *)string_array[i+1]; | ||
233 | break; | ||
234 | } | ||
235 | } | ||
236 | return (r) ? r : defvalue; | ||
237 | } | ||
238 | |||
239 | static const char *xfind_token(const char *const string_array[], const char *key) | ||
240 | { | ||
241 | const char *r = find_token(string_array, key, NULL); | ||
242 | if (r) | ||
243 | return r; | ||
244 | bb_error_msg_and_die("header: %s", key); | ||
245 | } | ||
246 | |||
247 | enum { | ||
248 | OPT_x = 1 << 0, | ||
249 | OPT_X = 1 << 1, | ||
250 | #if ENABLE_FEATURE_REFORMIME_COMPAT | ||
251 | OPT_d = 1 << 2, | ||
252 | OPT_e = 1 << 3, | ||
253 | OPT_i = 1 << 4, | ||
254 | OPT_s = 1 << 5, | ||
255 | OPT_r = 1 << 6, | ||
256 | OPT_c = 1 << 7, | ||
257 | OPT_m = 1 << 8, | ||
258 | OPT_h = 1 << 9, | ||
259 | OPT_o = 1 << 10, | ||
260 | OPT_O = 1 << 11, | ||
261 | #endif | ||
262 | }; | ||
263 | |||
264 | static int parse(const char *boundary, char **argv) | ||
265 | { | ||
266 | char *line, *s, *p; | ||
267 | const char *type; | ||
268 | int boundary_len = strlen(boundary); | ||
269 | const char *delims = " ;\"\t\r\n"; | ||
270 | const char *uniq; | ||
271 | int ntokens; | ||
272 | const char *tokens[32]; // 32 is enough | ||
273 | |||
274 | // prepare unique string pattern | ||
275 | uniq = xasprintf("%%llu.%u.%s", (unsigned)getpid(), safe_gethostname()); | ||
276 | |||
277 | //bb_info_msg("PARSE[%s]", uniq); | ||
278 | |||
279 | while ((line = xmalloc_fgets_str(stdin, "\r\n\r\n")) != NULL) { | ||
280 | |||
281 | // seek to start of MIME section | ||
282 | // N.B. to avoid false positives let us seek to the _last_ occurance | ||
283 | p = NULL; | ||
284 | s = line; | ||
285 | while ((s = strcasestr(s, "Content-Type:")) != NULL) | ||
286 | p = s++; | ||
287 | if (!p) | ||
288 | goto next; | ||
289 | //bb_info_msg("L[%s]", p); | ||
290 | |||
291 | // split to tokens | ||
292 | // TODO: strip of comments which are of form: (comment-text) | ||
293 | ntokens = 0; | ||
294 | tokens[ntokens] = NULL; | ||
295 | for (s = strtok(p, delims); s; s = strtok(NULL, delims)) { | ||
296 | tokens[ntokens] = s; | ||
297 | if (ntokens < ARRAY_SIZE(tokens) - 1) | ||
298 | ntokens++; | ||
299 | //bb_info_msg("L[%d][%s]", ntokens, s); | ||
300 | } | ||
301 | tokens[ntokens] = NULL; | ||
302 | //bb_info_msg("N[%d]", ntokens); | ||
303 | |||
304 | // analyse tokens | ||
305 | type = find_token(tokens, "Content-Type:", "text/plain"); | ||
306 | //bb_info_msg("T[%s]", type); | ||
307 | if (0 == strncasecmp(type, "multipart/", 10)) { | ||
308 | if (0 == strcasecmp(type+10, "mixed")) { | ||
309 | parse(xfind_token(tokens, "boundary="), argv); | ||
310 | } else | ||
311 | bb_error_msg_and_die("no support of content type '%s'", type); | ||
312 | } else { | ||
313 | pid_t pid = pid; | ||
314 | int rc; | ||
315 | FILE *fp; | ||
316 | // fetch charset | ||
317 | const char *charset = find_token(tokens, "charset=", CONFIG_FEATURE_MIME_CHARSET); | ||
318 | // fetch encoding | ||
319 | const char *encoding = find_token(tokens, "Content-Transfer-Encoding:", "7bit"); | ||
320 | // compose target filename | ||
321 | char *filename = (char *)find_token(tokens, "filename=", NULL); | ||
322 | if (!filename) | ||
323 | filename = xasprintf(uniq, monotonic_us()); | ||
324 | else | ||
325 | filename = bb_get_last_path_component_strip(xstrdup(filename)); | ||
326 | |||
327 | // start external helper, if any | ||
328 | if (opts & OPT_X) { | ||
329 | int fd[2]; | ||
330 | xpipe(fd); | ||
331 | pid = vfork(); | ||
332 | if (0 == pid) { | ||
333 | // child reads from fd[0] | ||
334 | close(fd[1]); | ||
335 | xmove_fd(fd[0], STDIN_FILENO); | ||
336 | xsetenv("CONTENT_TYPE", type); | ||
337 | xsetenv("CHARSET", charset); | ||
338 | xsetenv("ENCODING", encoding); | ||
339 | xsetenv("FILENAME", filename); | ||
340 | BB_EXECVP_or_die(argv); | ||
341 | } | ||
342 | // parent dumps to fd[1] | ||
343 | close(fd[0]); | ||
344 | fp = xfdopen_for_write(fd[1]); | ||
345 | signal(SIGPIPE, SIG_IGN); // ignore EPIPE | ||
346 | // or create a file for dump | ||
347 | } else { | ||
348 | char *fname = xasprintf("%s%s", *argv, filename); | ||
349 | fp = xfopen_for_write(fname); | ||
350 | free(fname); | ||
351 | } | ||
352 | |||
353 | // housekeeping | ||
354 | free(filename); | ||
355 | |||
356 | // dump to fp | ||
357 | if (0 == strcasecmp(encoding, "base64")) { | ||
358 | read_base64(stdin, fp, '-'); | ||
359 | } else if (0 != strcasecmp(encoding, "7bit") | ||
360 | && 0 != strcasecmp(encoding, "8bit") | ||
361 | ) { | ||
362 | // quoted-printable, binary, user-defined are unsupported so far | ||
363 | bb_error_msg_and_die("no support of encoding '%s'", encoding); | ||
364 | } else { | ||
365 | // N.B. we have written redundant \n. so truncate the file | ||
366 | // The following weird 2-tacts reading technique is due to | ||
367 | // we have to not write extra \n at the end of the file | ||
368 | // In case of -x option we could truncate the resulting file as | ||
369 | // fseek(fp, -1, SEEK_END); | ||
370 | // if (ftruncate(fileno(fp), ftell(fp))) | ||
371 | // bb_perror_msg("ftruncate"); | ||
372 | // But in case of -X we have to be much more careful. There is | ||
373 | // no means to truncate what we already have sent to the helper. | ||
374 | p = xmalloc_fgets_str(stdin, "\r\n"); | ||
375 | while (p) { | ||
376 | s = xmalloc_fgets_str(stdin, "\r\n"); | ||
377 | if (s == NULL) | ||
378 | break; | ||
379 | if ('-' == s[0] | ||
380 | && '-' == s[1] | ||
381 | && 0 == strncmp(s+2, boundary, boundary_len) | ||
382 | ) { | ||
383 | break; | ||
384 | } | ||
385 | fputs(p, fp); | ||
386 | p = s; | ||
387 | } | ||
388 | |||
389 | /* | ||
390 | while ((s = xmalloc_fgetline_str(stdin, "\r\n")) != NULL) { | ||
391 | if ('-' == s[0] && '-' == s[1] | ||
392 | && 0 == strncmp(s+2, boundary, boundary_len)) | ||
393 | break; | ||
394 | fprintf(fp, "%s\n", s); | ||
395 | } | ||
396 | // N.B. we have written redundant \n. so truncate the file | ||
397 | fseek(fp, -1, SEEK_END); | ||
398 | if (ftruncate(fileno(fp), ftell(fp))) | ||
399 | bb_perror_msg("ftruncate"); | ||
400 | */ | ||
401 | } | ||
402 | fclose(fp); | ||
403 | |||
404 | // finalize helper | ||
405 | if (opts & OPT_X) { | ||
406 | signal(SIGPIPE, SIG_DFL); | ||
407 | // exit if helper exited >0 | ||
408 | rc = (wait4pid(pid) & 0xff); | ||
409 | if (rc) | ||
410 | return rc+20; | ||
411 | } | ||
412 | |||
413 | // check multipart finalized | ||
414 | if (s && '-' == s[2+boundary_len] && '-' == s[2+boundary_len+1]) { | ||
415 | free(line); | ||
416 | break; | ||
417 | } | ||
418 | } | ||
419 | next: | ||
420 | free(line); | ||
421 | } | ||
422 | |||
423 | //bb_info_msg("ENDPARSE[%s]", boundary); | ||
424 | |||
425 | return EXIT_SUCCESS; | ||
426 | } | ||
427 | |||
428 | /* | ||
429 | Usage: reformime [options] | ||
430 | -d - parse a delivery status notification. | ||
431 | -e - extract contents of MIME section. | ||
432 | -x - extract MIME section to a file. | ||
433 | -X - pipe MIME section to a program. | ||
434 | -i - show MIME info. | ||
435 | -s n.n.n.n - specify MIME section. | ||
436 | -r - rewrite message, filling in missing MIME headers. | ||
437 | -r7 - also convert 8bit/raw encoding to quoted-printable, if possible. | ||
438 | -r8 - also convert quoted-printable encoding to 8bit, if possible. | ||
439 | -c charset - default charset for rewriting, -o, and -O. | ||
440 | -m [file] [file]... - create a MIME message digest. | ||
441 | -h "header" - decode RFC 2047-encoded header. | ||
442 | -o "header" - encode unstructured header using RFC 2047. | ||
443 | -O "header" - encode address list header using RFC 2047. | ||
444 | */ | ||
445 | |||
446 | int reformime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
447 | int reformime_main(int argc UNUSED_PARAM, char **argv) | ||
448 | { | ||
449 | const char *opt_prefix = ""; | ||
450 | |||
451 | INIT_G(); | ||
452 | |||
453 | // parse options | ||
454 | // N.B. only -x and -X are supported so far | ||
455 | opt_complementary = "x--X:X--x" IF_FEATURE_REFORMIME_COMPAT(":m::"); | ||
456 | opts = getopt32(argv, | ||
457 | "x:X" IF_FEATURE_REFORMIME_COMPAT("deis:r:c:m:h:o:O:"), | ||
458 | &opt_prefix | ||
459 | IF_FEATURE_REFORMIME_COMPAT(, NULL, NULL, &G.opt_charset, NULL, NULL, NULL, NULL) | ||
460 | ); | ||
461 | //argc -= optind; | ||
462 | argv += optind; | ||
463 | |||
464 | return parse("", (opts & OPT_X) ? argv : (char **)&opt_prefix); | ||
465 | } | ||
diff --git a/mailutils/popmaildir.c b/mailutils/popmaildir.c index 642657919..c9d22aa28 100644 --- a/mailutils/popmaildir.c +++ b/mailutils/popmaildir.c | |||
@@ -10,6 +10,8 @@ | |||
10 | * Licensed under GPLv2, see file LICENSE in this source tree. | 10 | * Licensed under GPLv2, see file LICENSE in this source tree. |
11 | */ | 11 | */ |
12 | 12 | ||
13 | //kbuild:lib-$(CONFIG_POPMAILDIR) += popmaildir.o mail.o | ||
14 | |||
13 | //usage:#define popmaildir_trivial_usage | 15 | //usage:#define popmaildir_trivial_usage |
14 | //usage: "[OPTIONS] MAILDIR [CONN_HELPER ARGS]" | 16 | //usage: "[OPTIONS] MAILDIR [CONN_HELPER ARGS]" |
15 | //usage:#define popmaildir_full_usage "\n\n" | 17 | //usage:#define popmaildir_full_usage "\n\n" |
diff --git a/mailutils/reformime.c b/mailutils/reformime.c new file mode 100644 index 000000000..aa5e3b1c0 --- /dev/null +++ b/mailutils/reformime.c | |||
@@ -0,0 +1,280 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * makemime: create MIME-encoded message | ||
4 | * reformime: parse MIME-encoded message | ||
5 | * | ||
6 | * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com> | ||
7 | * | ||
8 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
9 | */ | ||
10 | |||
11 | //kbuild:lib-$(CONFIG_REFORMIME) += reformime.o mail.o | ||
12 | |||
13 | #include "libbb.h" | ||
14 | #include "mail.h" | ||
15 | |||
16 | #if 0 | ||
17 | # define dbg_error_msg(...) bb_error_msg(__VA_ARGS__) | ||
18 | #else | ||
19 | # define dbg_error_msg(...) ((void)0) | ||
20 | #endif | ||
21 | |||
22 | static const char *find_token(const char *const string_array[], const char *key, const char *defvalue) | ||
23 | { | ||
24 | const char *r = NULL; | ||
25 | int i; | ||
26 | for (i = 0; string_array[i] != NULL; i++) { | ||
27 | if (strcasecmp(string_array[i], key) == 0) { | ||
28 | r = (char *)string_array[i+1]; | ||
29 | break; | ||
30 | } | ||
31 | } | ||
32 | return (r) ? r : defvalue; | ||
33 | } | ||
34 | |||
35 | static const char *xfind_token(const char *const string_array[], const char *key) | ||
36 | { | ||
37 | const char *r = find_token(string_array, key, NULL); | ||
38 | if (r) | ||
39 | return r; | ||
40 | bb_error_msg_and_die("not found: '%s'", key); | ||
41 | } | ||
42 | |||
43 | enum { | ||
44 | OPT_x = 1 << 0, | ||
45 | OPT_X = 1 << 1, | ||
46 | #if ENABLE_FEATURE_REFORMIME_COMPAT | ||
47 | OPT_d = 1 << 2, | ||
48 | OPT_e = 1 << 3, | ||
49 | OPT_i = 1 << 4, | ||
50 | OPT_s = 1 << 5, | ||
51 | OPT_r = 1 << 6, | ||
52 | OPT_c = 1 << 7, | ||
53 | OPT_m = 1 << 8, | ||
54 | OPT_h = 1 << 9, | ||
55 | OPT_o = 1 << 10, | ||
56 | OPT_O = 1 << 11, | ||
57 | #endif | ||
58 | }; | ||
59 | |||
60 | static int parse(const char *boundary, char **argv) | ||
61 | { | ||
62 | int boundary_len = strlen(boundary); | ||
63 | char uniq[sizeof("%%llu.%u") + sizeof(int)*3]; | ||
64 | |||
65 | dbg_error_msg("BOUNDARY[%s]", boundary); | ||
66 | |||
67 | // prepare unique string pattern | ||
68 | sprintf(uniq, "%%llu.%u", (unsigned)getpid()); | ||
69 | dbg_error_msg("UNIQ[%s]", uniq); | ||
70 | |||
71 | while (1) { | ||
72 | char *header; | ||
73 | const char *tokens[32]; /* 32 is enough */ | ||
74 | const char *type; | ||
75 | |||
76 | /* Read the header (everything up to two \n) */ | ||
77 | { | ||
78 | unsigned header_idx = 0; | ||
79 | int last_ch = 0; | ||
80 | header = NULL; | ||
81 | while (1) { | ||
82 | int ch = fgetc(stdin); | ||
83 | if (ch == '\r') /* Support both line endings */ | ||
84 | continue; | ||
85 | if (ch == EOF) | ||
86 | break; | ||
87 | if (ch == '\n' && last_ch == ch) | ||
88 | break; | ||
89 | if (!(header_idx & 0xff)) | ||
90 | header = xrealloc(header, header_idx + 0x101); | ||
91 | header[header_idx++] = last_ch = ch; | ||
92 | } | ||
93 | if (!header) { | ||
94 | dbg_error_msg("EOF"); | ||
95 | break; | ||
96 | } | ||
97 | header[header_idx] = '\0'; | ||
98 | dbg_error_msg("H:'%s'", p); | ||
99 | } | ||
100 | |||
101 | /* Split to tokens */ | ||
102 | { | ||
103 | char *s, *p; | ||
104 | unsigned ntokens; | ||
105 | const char *delims = ";=\" \t\n"; | ||
106 | |||
107 | /* Skip to last Content-Type: */ | ||
108 | s = p = header; | ||
109 | while ((p = strchr(p, '\n')) != NULL) { | ||
110 | p++; | ||
111 | if (strncasecmp(p, "Content-Type:", sizeof("Content-Type:")-1) == 0) | ||
112 | s = p; | ||
113 | } | ||
114 | dbg_error_msg("L:'%s'", p); | ||
115 | ntokens = 0; | ||
116 | s = strtok(s, delims); | ||
117 | while (s) { | ||
118 | tokens[ntokens] = s; | ||
119 | if (ntokens < ARRAY_SIZE(tokens) - 1) | ||
120 | ntokens++; | ||
121 | dbg_error_msg("L[%d]='%s'", ntokens, s); | ||
122 | s = strtok(NULL, delims); | ||
123 | } | ||
124 | tokens[ntokens] = NULL; | ||
125 | dbg_error_msg("EMPTYLINE, ntokens:%d", ntokens); | ||
126 | if (ntokens == 0) | ||
127 | break; | ||
128 | } | ||
129 | |||
130 | /* Is it multipart? */ | ||
131 | type = find_token(tokens, "Content-Type:", "text/plain"); | ||
132 | dbg_error_msg("TYPE:'%s'", type); | ||
133 | if (0 == strncasecmp(type, "multipart/", 10)) { | ||
134 | /* Yes, recurse */ | ||
135 | if (strcasecmp(type + 10, "mixed") != 0) | ||
136 | bb_error_msg_and_die("no support of content type '%s'", type); | ||
137 | parse(xfind_token(tokens, "boundary"), argv); | ||
138 | |||
139 | } else { | ||
140 | /* No, process one non-multipart section */ | ||
141 | char *end; | ||
142 | pid_t pid = pid; | ||
143 | FILE *fp; | ||
144 | |||
145 | const char *charset = find_token(tokens, "charset", CONFIG_FEATURE_MIME_CHARSET); | ||
146 | const char *encoding = find_token(tokens, "Content-Transfer-Encoding:", "7bit"); | ||
147 | |||
148 | /* Compose target filename */ | ||
149 | char *filename = (char *)find_token(tokens, "filename", NULL); | ||
150 | if (!filename) | ||
151 | filename = xasprintf(uniq, monotonic_us()); | ||
152 | else | ||
153 | filename = bb_get_last_path_component_strip(xstrdup(filename)); | ||
154 | |||
155 | if (opts & OPT_X) { | ||
156 | int fd[2]; | ||
157 | |||
158 | /* start external helper */ | ||
159 | xpipe(fd); | ||
160 | pid = vfork(); | ||
161 | if (0 == pid) { | ||
162 | /* child reads from fd[0] */ | ||
163 | close(fd[1]); | ||
164 | xmove_fd(fd[0], STDIN_FILENO); | ||
165 | xsetenv("CONTENT_TYPE", type); | ||
166 | xsetenv("CHARSET", charset); | ||
167 | xsetenv("ENCODING", encoding); | ||
168 | xsetenv("FILENAME", filename); | ||
169 | BB_EXECVP_or_die(argv); | ||
170 | } | ||
171 | /* parent will write to fd[1] */ | ||
172 | close(fd[0]); | ||
173 | fp = xfdopen_for_write(fd[1]); | ||
174 | signal(SIGPIPE, SIG_IGN); | ||
175 | } else { | ||
176 | /* write to file */ | ||
177 | char *fname = xasprintf("%s%s", *argv, filename); | ||
178 | fp = xfopen_for_write(fname); | ||
179 | free(fname); | ||
180 | } | ||
181 | free(filename); | ||
182 | |||
183 | /* write to fp */ | ||
184 | end = NULL; | ||
185 | if (0 == strcasecmp(encoding, "base64")) { | ||
186 | read_base64(stdin, fp, '-'); | ||
187 | } else | ||
188 | if (0 != strcasecmp(encoding, "7bit") | ||
189 | && 0 != strcasecmp(encoding, "8bit") | ||
190 | ) { | ||
191 | /* quoted-printable, binary, user-defined are unsupported so far */ | ||
192 | bb_error_msg_and_die("encoding '%s' not supported", encoding); | ||
193 | } else { | ||
194 | /* plain 7bit or 8bit */ | ||
195 | while ((end = xmalloc_fgets(stdin)) != NULL) { | ||
196 | if ('-' == end[0] | ||
197 | && '-' == end[1] | ||
198 | && strncmp(end + 2, boundary, boundary_len) == 0 | ||
199 | ) { | ||
200 | break; | ||
201 | } | ||
202 | fputs(end, fp); | ||
203 | } | ||
204 | } | ||
205 | fclose(fp); | ||
206 | |||
207 | /* Wait for child */ | ||
208 | if (opts & OPT_X) { | ||
209 | int rc; | ||
210 | signal(SIGPIPE, SIG_DFL); | ||
211 | rc = (wait4pid(pid) & 0xff); | ||
212 | if (rc != 0) | ||
213 | return rc + 20; | ||
214 | } | ||
215 | |||
216 | /* Multipart ended? */ | ||
217 | if (end && '-' == end[2 + boundary_len] && '-' == end[2 + boundary_len + 1]) { | ||
218 | dbg_error_msg("FINISHED MPART:'%s'", end); | ||
219 | break; | ||
220 | } | ||
221 | dbg_error_msg("FINISHED:'%s'", end); | ||
222 | free(end); | ||
223 | } /* end of "handle one non-multipart block" */ | ||
224 | |||
225 | free(header); | ||
226 | } /* while (1) */ | ||
227 | |||
228 | dbg_error_msg("ENDPARSE[%s]", boundary); | ||
229 | |||
230 | return EXIT_SUCCESS; | ||
231 | } | ||
232 | |||
233 | //usage:#define reformime_trivial_usage | ||
234 | //usage: "[OPTIONS]" | ||
235 | //usage:#define reformime_full_usage "\n\n" | ||
236 | //usage: "Parse MIME-encoded message on stdin\n" | ||
237 | //usage: "\nOptions:" | ||
238 | //usage: "\n -x PREFIX Extract content of MIME sections to files" | ||
239 | //usage: "\n -X PROG ARGS Filter content of MIME sections through PROG" | ||
240 | //usage: "\n Must be the last option" | ||
241 | //usage: "\n" | ||
242 | //usage: "\nOther options are silently ignored" | ||
243 | |||
244 | /* | ||
245 | Usage: reformime [options] | ||
246 | -d - parse a delivery status notification. | ||
247 | -e - extract contents of MIME section. | ||
248 | -x - extract MIME section to a file. | ||
249 | -X - pipe MIME section to a program. | ||
250 | -i - show MIME info. | ||
251 | -s n.n.n.n - specify MIME section. | ||
252 | -r - rewrite message, filling in missing MIME headers. | ||
253 | -r7 - also convert 8bit/raw encoding to quoted-printable, if possible. | ||
254 | -r8 - also convert quoted-printable encoding to 8bit, if possible. | ||
255 | -c charset - default charset for rewriting, -o, and -O. | ||
256 | -m [file] [file]... - create a MIME message digest. | ||
257 | -h "header" - decode RFC 2047-encoded header. | ||
258 | -o "header" - encode unstructured header using RFC 2047. | ||
259 | -O "header" - encode address list header using RFC 2047. | ||
260 | */ | ||
261 | |||
262 | int reformime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
263 | int reformime_main(int argc UNUSED_PARAM, char **argv) | ||
264 | { | ||
265 | const char *opt_prefix = ""; | ||
266 | |||
267 | INIT_G(); | ||
268 | |||
269 | // parse options | ||
270 | // N.B. only -x and -X are supported so far | ||
271 | opt_complementary = "x--X:X--x" IF_FEATURE_REFORMIME_COMPAT(":m::"); | ||
272 | opts = getopt32(argv, | ||
273 | "x:X" IF_FEATURE_REFORMIME_COMPAT("deis:r:c:m:h:o:O:"), | ||
274 | &opt_prefix | ||
275 | IF_FEATURE_REFORMIME_COMPAT(, NULL, NULL, &G.opt_charset, NULL, NULL, NULL, NULL) | ||
276 | ); | ||
277 | argv += optind; | ||
278 | |||
279 | return parse("", (opts & OPT_X) ? argv : (char **)&opt_prefix); | ||
280 | } | ||
diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c index 8096288ef..e0aff20fb 100644 --- a/mailutils/sendmail.c +++ b/mailutils/sendmail.c | |||
@@ -7,6 +7,8 @@ | |||
7 | * Licensed under GPLv2, see file LICENSE in this source tree. | 7 | * Licensed under GPLv2, see file LICENSE in this source tree. |
8 | */ | 8 | */ |
9 | 9 | ||
10 | //kbuild:lib-$(CONFIG_SENDMAIL) += sendmail.o mail.o | ||
11 | |||
10 | //usage:#define sendmail_trivial_usage | 12 | //usage:#define sendmail_trivial_usage |
11 | //usage: "[OPTIONS] [RECIPIENT_EMAIL]..." | 13 | //usage: "[OPTIONS] [RECIPIENT_EMAIL]..." |
12 | //usage:#define sendmail_full_usage "\n\n" | 14 | //usage:#define sendmail_full_usage "\n\n" |
diff --git a/miscutils/dc.c b/miscutils/dc.c index 777ec1654..b080ba133 100644 --- a/miscutils/dc.c +++ b/miscutils/dc.c | |||
@@ -11,7 +11,7 @@ | |||
11 | //usage: | 11 | //usage: |
12 | //usage:#define dc_full_usage "\n\n" | 12 | //usage:#define dc_full_usage "\n\n" |
13 | //usage: "Tiny RPN calculator. Operations:\n" | 13 | //usage: "Tiny RPN calculator. Operations:\n" |
14 | //usage: "+, add, -, sub, *, mul, /, div, %, mod, **, exp, and, or, not, eor,\n" | 14 | //usage: "+, add, -, sub, *, mul, /, div, %, mod, "IF_FEATURE_DC_LIBM("**, exp, ")"and, or, not, eor,\n" |
15 | //usage: "p - print top of the stack (without popping),\n" | 15 | //usage: "p - print top of the stack (without popping),\n" |
16 | //usage: "f - print entire stack,\n" | 16 | //usage: "f - print entire stack,\n" |
17 | //usage: "o - pop the value and set output radix (must be 10, 16, 8 or 2).\n" | 17 | //usage: "o - pop the value and set output radix (must be 10, 16, 8 or 2).\n" |
diff --git a/miscutils/last_fancy.c b/miscutils/last_fancy.c index 7e61b7691..dc09b65fb 100644 --- a/miscutils/last_fancy.c +++ b/miscutils/last_fancy.c | |||
@@ -161,11 +161,10 @@ int last_main(int argc UNUSED_PARAM, char **argv) | |||
161 | time_t boot_time; | 161 | time_t boot_time; |
162 | time_t down_time; | 162 | time_t down_time; |
163 | int file; | 163 | int file; |
164 | unsigned opt; | ||
165 | smallint going_down; | 164 | smallint going_down; |
166 | smallint boot_down; | 165 | smallint boot_down; |
167 | 166 | ||
168 | opt = getopt32(argv, "Wf:" /* "H" */, &filename); | 167 | /*opt =*/ getopt32(argv, "Wf:" /* "H" */, &filename); |
169 | #ifdef BUT_UTIL_LINUX_LAST_HAS_NO_SUCH_OPT | 168 | #ifdef BUT_UTIL_LINUX_LAST_HAS_NO_SUCH_OPT |
170 | if (opt & LAST_OPT_H) { | 169 | if (opt & LAST_OPT_H) { |
171 | /* Print header line */ | 170 | /* Print header line */ |
diff --git a/miscutils/makedevs.c b/miscutils/makedevs.c index 8cca83882..c945a1352 100644 --- a/miscutils/makedevs.c +++ b/miscutils/makedevs.c | |||
@@ -38,7 +38,7 @@ | |||
38 | //usage:#define makedevs_full_usage "\n\n" | 38 | //usage:#define makedevs_full_usage "\n\n" |
39 | //usage: "Create a range of special files as specified in a device table.\n" | 39 | //usage: "Create a range of special files as specified in a device table.\n" |
40 | //usage: "Device table entries take the form of:\n" | 40 | //usage: "Device table entries take the form of:\n" |
41 | //usage: "<type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>\n" | 41 | //usage: "<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>\n" |
42 | //usage: "Where name is the file name, type can be one of:\n" | 42 | //usage: "Where name is the file name, type can be one of:\n" |
43 | //usage: " f Regular file\n" | 43 | //usage: " f Regular file\n" |
44 | //usage: " d Directory\n" | 44 | //usage: " d Directory\n" |
diff --git a/miscutils/setserial.c b/miscutils/setserial.c new file mode 100644 index 000000000..2951b987d --- /dev/null +++ b/miscutils/setserial.c | |||
@@ -0,0 +1,763 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * setserial implementation for busybox | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2011 Marek Bečka <yuen@klacno.sk> | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
9 | */ | ||
10 | |||
11 | //config:config SETSERIAL | ||
12 | //config: bool "setserial" | ||
13 | //config: default y | ||
14 | //config: depends on PLATFORM_LINUX | ||
15 | //config: help | ||
16 | //config: Retrieve or set Linux serial port. | ||
17 | |||
18 | //applet:IF_SETSERIAL(APPLET(setserial, BB_DIR_BIN, BB_SUID_DROP)) | ||
19 | |||
20 | //kbuild:lib-$(CONFIG_SETSERIAL) += setserial.o | ||
21 | |||
22 | #include "libbb.h" | ||
23 | #include <assert.h> | ||
24 | |||
25 | #ifndef PORT_UNKNOWN | ||
26 | # define PORT_UNKNOWN 0 | ||
27 | #endif | ||
28 | #ifndef PORT_8250 | ||
29 | # define PORT_8250 1 | ||
30 | #endif | ||
31 | #ifndef PORT_16450 | ||
32 | # define PORT_16450 2 | ||
33 | #endif | ||
34 | #ifndef PORT_16550 | ||
35 | # define PORT_16550 3 | ||
36 | #endif | ||
37 | #ifndef PORT_16550A | ||
38 | # define PORT_16550A 4 | ||
39 | #endif | ||
40 | #ifndef PORT_CIRRUS | ||
41 | # define PORT_CIRRUS 5 | ||
42 | #endif | ||
43 | #ifndef PORT_16650 | ||
44 | # define PORT_16650 6 | ||
45 | #endif | ||
46 | #ifndef PORT_16650V2 | ||
47 | # define PORT_16650V2 7 | ||
48 | #endif | ||
49 | #ifndef PORT_16750 | ||
50 | # define PORT_16750 8 | ||
51 | #endif | ||
52 | #ifndef PORT_STARTECH | ||
53 | # define PORT_STARTECH 9 | ||
54 | #endif | ||
55 | #ifndef PORT_16C950 | ||
56 | # define PORT_16C950 10 | ||
57 | #endif | ||
58 | #ifndef PORT_16654 | ||
59 | # define PORT_16654 11 | ||
60 | #endif | ||
61 | #ifndef PORT_16850 | ||
62 | # define PORT_16850 12 | ||
63 | #endif | ||
64 | #ifndef PORT_RSA | ||
65 | # define PORT_RSA 13 | ||
66 | #endif | ||
67 | #ifndef PORT_NS16550A | ||
68 | # define PORT_NS16550A 14 | ||
69 | #endif | ||
70 | #ifndef PORT_XSCALE | ||
71 | # define PORT_XSCALE 15 | ||
72 | #endif | ||
73 | #ifndef PORT_RM9000 | ||
74 | # define PORT_RM9000 16 | ||
75 | #endif | ||
76 | #ifndef PORT_OCTEON | ||
77 | # define PORT_OCTEON 17 | ||
78 | #endif | ||
79 | #ifndef PORT_AR7 | ||
80 | # define PORT_AR7 18 | ||
81 | #endif | ||
82 | #ifndef PORT_U6_16550A | ||
83 | # define PORT_U6_16550A 19 | ||
84 | #endif | ||
85 | |||
86 | #ifndef ASYNCB_HUP_NOTIFY | ||
87 | # define ASYNCB_HUP_NOTIFY 0 | ||
88 | #endif | ||
89 | #ifndef ASYNCB_FOURPORT | ||
90 | # define ASYNCB_FOURPORT 1 | ||
91 | #endif | ||
92 | #ifndef ASYNCB_SAK | ||
93 | # define ASYNCB_SAK 2 | ||
94 | #endif | ||
95 | #ifndef ASYNCB_SPLIT_TERMIOS | ||
96 | # define ASYNCB_SPLIT_TERMIOS 3 | ||
97 | #endif | ||
98 | #ifndef ASYNCB_SPD_HI | ||
99 | # define ASYNCB_SPD_HI 4 | ||
100 | #endif | ||
101 | #ifndef ASYNCB_SPD_VHI | ||
102 | # define ASYNCB_SPD_VHI 5 | ||
103 | #endif | ||
104 | #ifndef ASYNCB_SKIP_TEST | ||
105 | # define ASYNCB_SKIP_TEST 6 | ||
106 | #endif | ||
107 | #ifndef ASYNCB_AUTO_IRQ | ||
108 | # define ASYNCB_AUTO_IRQ 7 | ||
109 | #endif | ||
110 | #ifndef ASYNCB_SESSION_LOCKOUT | ||
111 | # define ASYNCB_SESSION_LOCKOUT 8 | ||
112 | #endif | ||
113 | #ifndef ASYNCB_PGRP_LOCKOUT | ||
114 | # define ASYNCB_PGRP_LOCKOUT 9 | ||
115 | #endif | ||
116 | #ifndef ASYNCB_CALLOUT_NOHUP | ||
117 | # define ASYNCB_CALLOUT_NOHUP 10 | ||
118 | #endif | ||
119 | #ifndef ASYNCB_SPD_SHI | ||
120 | # define ASYNCB_SPD_SHI 12 | ||
121 | #endif | ||
122 | #ifndef ASYNCB_LOW_LATENCY | ||
123 | # define ASYNCB_LOW_LATENCY 13 | ||
124 | #endif | ||
125 | #ifndef ASYNCB_BUGGY_UART | ||
126 | # define ASYNCB_BUGGY_UART 14 | ||
127 | #endif | ||
128 | |||
129 | #ifndef ASYNC_HUP_NOTIFY | ||
130 | # define ASYNC_HUP_NOTIFY (1U << ASYNCB_HUP_NOTIFY) | ||
131 | #endif | ||
132 | #ifndef ASYNC_FOURPORT | ||
133 | # define ASYNC_FOURPORT (1U << ASYNCB_FOURPORT) | ||
134 | #endif | ||
135 | #ifndef ASYNC_SAK | ||
136 | # define ASYNC_SAK (1U << ASYNCB_SAK) | ||
137 | #endif | ||
138 | #ifndef ASYNC_SPLIT_TERMIOS | ||
139 | # define ASYNC_SPLIT_TERMIOS (1U << ASYNCB_SPLIT_TERMIOS) | ||
140 | #endif | ||
141 | #ifndef ASYNC_SPD_HI | ||
142 | # define ASYNC_SPD_HI (1U << ASYNCB_SPD_HI) | ||
143 | #endif | ||
144 | #ifndef ASYNC_SPD_VHI | ||
145 | # define ASYNC_SPD_VHI (1U << ASYNCB_SPD_VHI) | ||
146 | #endif | ||
147 | #ifndef ASYNC_SKIP_TEST | ||
148 | # define ASYNC_SKIP_TEST (1U << ASYNCB_SKIP_TEST) | ||
149 | #endif | ||
150 | #ifndef ASYNC_AUTO_IRQ | ||
151 | # define ASYNC_AUTO_IRQ (1U << ASYNCB_AUTO_IRQ) | ||
152 | #endif | ||
153 | #ifndef ASYNC_SESSION_LOCKOUT | ||
154 | # define ASYNC_SESSION_LOCKOUT (1U << ASYNCB_SESSION_LOCKOUT) | ||
155 | #endif | ||
156 | #ifndef ASYNC_PGRP_LOCKOUT | ||
157 | # define ASYNC_PGRP_LOCKOUT (1U << ASYNCB_PGRP_LOCKOUT) | ||
158 | #endif | ||
159 | #ifndef ASYNC_CALLOUT_NOHUP | ||
160 | # define ASYNC_CALLOUT_NOHUP (1U << ASYNCB_CALLOUT_NOHUP) | ||
161 | #endif | ||
162 | #ifndef ASYNC_SPD_SHI | ||
163 | # define ASYNC_SPD_SHI (1U << ASYNCB_SPD_SHI) | ||
164 | #endif | ||
165 | #ifndef ASYNC_LOW_LATENCY | ||
166 | # define ASYNC_LOW_LATENCY (1U << ASYNCB_LOW_LATENCY) | ||
167 | #endif | ||
168 | #ifndef ASYNC_BUGGY_UART | ||
169 | # define ASYNC_BUGGY_UART (1U << ASYNCB_BUGGY_UART) | ||
170 | #endif | ||
171 | |||
172 | #ifndef ASYNC_SPD_CUST | ||
173 | # define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI) | ||
174 | #endif | ||
175 | #ifndef ASYNC_SPD_WARP | ||
176 | # define ASYNC_SPD_WARP (ASYNC_SPD_HI|ASYNC_SPD_SHI) | ||
177 | #endif | ||
178 | #ifndef ASYNC_SPD_MASK | ||
179 | # define ASYNC_SPD_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI) | ||
180 | #endif | ||
181 | |||
182 | #ifndef ASYNC_CLOSING_WAIT_INF | ||
183 | # define ASYNC_CLOSING_WAIT_INF 0 | ||
184 | #endif | ||
185 | #ifndef ASYNC_CLOSING_WAIT_NONE | ||
186 | # define ASYNC_CLOSING_WAIT_NONE 65535 | ||
187 | #endif | ||
188 | |||
189 | #ifndef _LINUX_SERIAL_H | ||
190 | struct serial_struct { | ||
191 | int type; | ||
192 | int line; | ||
193 | unsigned int port; | ||
194 | int irq; | ||
195 | int flags; | ||
196 | int xmit_fifo_size; | ||
197 | int custom_divisor; | ||
198 | int baud_base; | ||
199 | unsigned short close_delay; | ||
200 | char io_type; | ||
201 | char reserved_char[1]; | ||
202 | int hub6; | ||
203 | unsigned short closing_wait; /* time to wait before closing */ | ||
204 | unsigned short closing_wait2; /* no longer used... */ | ||
205 | unsigned char *iomem_base; | ||
206 | unsigned short iomem_reg_shift; | ||
207 | unsigned int port_high; | ||
208 | unsigned long iomap_base; /* cookie passed into ioremap */ | ||
209 | }; | ||
210 | #endif | ||
211 | |||
212 | //usage:#define setserial_trivial_usage | ||
213 | //usage: "[-gabGvzV] DEVICE [PARAMETER [ARG]]..." | ||
214 | //usage:#define setserial_full_usage "\n\n" | ||
215 | //usage: "Request or set Linux serial port information\n\n" | ||
216 | //usage: "Options:\n" | ||
217 | //usage: " -g Interpret parameters as list of devices for reporting" | ||
218 | //usage: " -a Print all available information\n" | ||
219 | //usage: " -b Print summary information\n" | ||
220 | //usage: " -G Print in form which can be fed back\n" | ||
221 | //usage: " to setserial as command line parameters\n" | ||
222 | //usage: " -z Zero out serial flags before setting\n" | ||
223 | //usage: " -v Verbose\n" | ||
224 | //usage: "\n" | ||
225 | //usage: "Parameters: (* = takes an argument, ^ = can be turned off by preceding ^)\n" | ||
226 | //usage: " *port, *irq, *divisor, *uart, *baund_base, *close_delay, *closing_wait,\n" | ||
227 | //usage: " ^fourport, ^auto_irq, ^skip_test, ^sak, ^session_lockout, ^pgrp_lockout,\n" | ||
228 | //usage: " ^callout_nohup, ^split_termios, ^hup_notify, ^low_latency, autoconfig,\n" | ||
229 | //usage: " spd_normal, spd_hi, spd_vhi, spd_shi, spd_warp, spd_cust\n" | ||
230 | //usage: "\n" | ||
231 | //usage: "UART types:\n" | ||
232 | //usage: " unknown, 8250, 16450, 16550, 16550A, Cirrus, 16650, 16650V2, 16750,\n" | ||
233 | //usage: " 16950, 16954, 16654, 16850, RSA, NS16550A, XSCALE, RM9000, OCTEON, AR7,\n" | ||
234 | //usage: " U6_16550A" | ||
235 | |||
236 | #define OPT_PRINT_SUMMARY (1 << 0) | ||
237 | #define OPT_PRINT_FEDBACK (1 << 1) | ||
238 | #define OPT_PRINT_ALL (1 << 2) | ||
239 | #define OPT_VERBOSE (1 << 3) | ||
240 | #define OPT_ZERO (1 << 4) | ||
241 | #define OPT_GET (1 << 5) | ||
242 | |||
243 | #define OPT_MODE_MASK \ | ||
244 | (OPT_PRINT_ALL | OPT_PRINT_SUMMARY | OPT_PRINT_FEDBACK) | ||
245 | |||
246 | enum print_mode | ||
247 | { | ||
248 | PRINT_NORMAL = 0, | ||
249 | PRINT_SUMMARY = (1 << 0), | ||
250 | PRINT_FEDBACK = (1 << 1), | ||
251 | PRINT_ALL = (1 << 2), | ||
252 | }; | ||
253 | |||
254 | #define CTL_SET (1 << 0) | ||
255 | #define CTL_CONFIG (1 << 1) | ||
256 | #define CTL_GET (1 << 2) | ||
257 | #define CTL_CLOSE (1 << 3) | ||
258 | #define CTL_NODIE (1 << 4) | ||
259 | |||
260 | static const char serial_types[] = | ||
261 | "unknown\0" /* 0 */ | ||
262 | "8250\0" /* 1 */ | ||
263 | "16450\0" /* 2 */ | ||
264 | "16550\0" /* 3 */ | ||
265 | "16550A\0" /* 4 */ | ||
266 | "Cirrus\0" /* 5 */ | ||
267 | "16650\0" /* 6 */ | ||
268 | "16650V2\0" /* 7 */ | ||
269 | "16750\0" /* 8 */ | ||
270 | "16950\0" /* 9 UNIMPLEMENTED: also know as "16950/954" */ | ||
271 | "16954\0" /* 10 */ | ||
272 | "16654\0" /* 11 */ | ||
273 | "16850\0" /* 12 */ | ||
274 | "RSA\0" /* 13 */ | ||
275 | #ifndef SETSERIAL_BASE | ||
276 | "NS16550A\0" /* 14 */ | ||
277 | "XSCALE\0" /* 15 */ | ||
278 | "RM9000\0" /* 16 */ | ||
279 | "OCTEON\0" /* 17 */ | ||
280 | "AR7\0" /* 18 */ | ||
281 | "U6_16550A\0" /* 19 */ | ||
282 | #endif | ||
283 | ; | ||
284 | |||
285 | #ifndef SETSERIAL_BASE | ||
286 | # define MAX_SERIAL_TYPE 19 | ||
287 | #else | ||
288 | # define MAX_SERIAL_TYPE 13 | ||
289 | #endif | ||
290 | |||
291 | static const char commands[] = | ||
292 | "spd_normal\0" | ||
293 | "spd_hi\0" | ||
294 | "spd_vhi\0" | ||
295 | "spd_shi\0" | ||
296 | "spd_warp\0" | ||
297 | "spd_cust\0" | ||
298 | |||
299 | "sak\0" | ||
300 | "fourport\0" | ||
301 | "hup_notify\0" | ||
302 | "skip_test\0" | ||
303 | "auto_irq\0" | ||
304 | "split_termios\0" | ||
305 | "session_lockout\0" | ||
306 | "pgrp_lockout\0" | ||
307 | "callout_nohup\0" | ||
308 | "low_latency\0" | ||
309 | |||
310 | "port\0" | ||
311 | "irq\0" | ||
312 | "divisor\0" | ||
313 | "uart\0" | ||
314 | "baund_base\0" | ||
315 | "close_delay\0" | ||
316 | "closing_wait\0" | ||
317 | |||
318 | "autoconfig\0" | ||
319 | ; | ||
320 | |||
321 | enum | ||
322 | { | ||
323 | CMD_SPD_NORMAL = 0, | ||
324 | CMD_SPD_HI, | ||
325 | CMD_SPD_VHI, | ||
326 | CMD_SPD_SHI, | ||
327 | CMD_SPD_WARP, | ||
328 | CMD_SPD_CUST, | ||
329 | |||
330 | CMD_FLAG_SAK, | ||
331 | CMD_FLAG_FOURPORT, | ||
332 | CMD_FLAG_NUP_NOTIFY, | ||
333 | CMD_FLAG_SKIP_TEST, | ||
334 | CMD_FLAG_AUTO_IRQ, | ||
335 | CMD_FLAG_SPLIT_TERMIOS, | ||
336 | CMD_FLAG_SESSION_LOCKOUT, | ||
337 | CMD_FLAG_PGRP_LOCKOUT, | ||
338 | CMD_FLAG_CALLOUT_NOHUP, | ||
339 | CMD_FLAG_LOW_LATENCY, | ||
340 | |||
341 | CMD_PORT, | ||
342 | CMD_IRQ, | ||
343 | CMD_DIVISOR, | ||
344 | CMD_UART, | ||
345 | CMD_BASE, | ||
346 | CMD_DELAY, | ||
347 | CMD_WAIT, | ||
348 | |||
349 | CMD_AUTOCONFIG, | ||
350 | |||
351 | CMD_FLAG_FIRST = CMD_FLAG_SAK, | ||
352 | CMD_FLAG_LAST = CMD_FLAG_LOW_LATENCY, | ||
353 | }; | ||
354 | |||
355 | static bool cmd_noprint(int cmd) | ||
356 | { | ||
357 | return (cmd >= CMD_FLAG_SKIP_TEST && cmd <= CMD_FLAG_CALLOUT_NOHUP); | ||
358 | } | ||
359 | |||
360 | static bool cmd_is_flag(int cmd) | ||
361 | { | ||
362 | return (cmd >= CMD_FLAG_FIRST && cmd <= CMD_FLAG_LAST); | ||
363 | } | ||
364 | |||
365 | static bool cmd_need_arg(int cmd) | ||
366 | { | ||
367 | return (cmd >= CMD_PORT && cmd <= CMD_WAIT); | ||
368 | } | ||
369 | |||
370 | #define ALL_SPD ( \ | ||
371 | ASYNC_SPD_HI | ASYNC_SPD_VHI | ASYNC_SPD_SHI | \ | ||
372 | ASYNC_SPD_WARP | ASYNC_SPD_CUST \ | ||
373 | ) | ||
374 | |||
375 | #define ALL_FLAGS ( \ | ||
376 | ASYNC_SAK | ASYNC_FOURPORT | ASYNC_HUP_NOTIFY | \ | ||
377 | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ | ASYNC_SPLIT_TERMIOS | \ | ||
378 | ASYNC_SESSION_LOCKOUT | ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP | \ | ||
379 | ASYNC_LOW_LATENCY \ | ||
380 | ) | ||
381 | |||
382 | #if (ALL_SPD | ALL_FLAGS) > 0xffff | ||
383 | # error "Unexpected flags size" | ||
384 | #endif | ||
385 | |||
386 | static const uint16_t setbits[CMD_FLAG_LAST + 1] = | ||
387 | { | ||
388 | 0, | ||
389 | ASYNC_SPD_HI, | ||
390 | ASYNC_SPD_VHI, | ||
391 | ASYNC_SPD_SHI, | ||
392 | ASYNC_SPD_WARP, | ||
393 | ASYNC_SPD_CUST, | ||
394 | |||
395 | ASYNC_SAK, | ||
396 | ASYNC_FOURPORT, | ||
397 | ASYNC_HUP_NOTIFY, | ||
398 | ASYNC_SKIP_TEST, | ||
399 | ASYNC_AUTO_IRQ, | ||
400 | ASYNC_SPLIT_TERMIOS, | ||
401 | ASYNC_SESSION_LOCKOUT, | ||
402 | ASYNC_PGRP_LOCKOUT, | ||
403 | ASYNC_CALLOUT_NOHUP, | ||
404 | ASYNC_LOW_LATENCY | ||
405 | }; | ||
406 | |||
407 | static const char STR_INFINITE[] = "infinite"; | ||
408 | static const char STR_NONE[] = "none"; | ||
409 | |||
410 | static const char *uart_type(int type) | ||
411 | { | ||
412 | if (type > MAX_SERIAL_TYPE) | ||
413 | return "undefined"; | ||
414 | |||
415 | return nth_string(serial_types, type); | ||
416 | } | ||
417 | |||
418 | /* libbb candidate */ | ||
419 | static int index_in_strings_case_insensitive(const char *strings, const char *key) | ||
420 | { | ||
421 | int idx = 0; | ||
422 | |||
423 | while (*strings) { | ||
424 | if (strcasecmp(strings, key) == 0) { | ||
425 | return idx; | ||
426 | } | ||
427 | strings += strlen(strings) + 1; /* skip NUL */ | ||
428 | idx++; | ||
429 | } | ||
430 | return -1; | ||
431 | } | ||
432 | |||
433 | static int uart_id(const char *name) | ||
434 | { | ||
435 | return index_in_strings_case_insensitive(serial_types, name); | ||
436 | } | ||
437 | |||
438 | static const char *get_spd(int flags, enum print_mode mode) | ||
439 | { | ||
440 | int idx; | ||
441 | |||
442 | switch (flags & ASYNC_SPD_MASK) { | ||
443 | case ASYNC_SPD_HI: | ||
444 | idx = CMD_SPD_HI; | ||
445 | break; | ||
446 | case ASYNC_SPD_VHI: | ||
447 | idx = CMD_SPD_VHI; | ||
448 | break; | ||
449 | case ASYNC_SPD_SHI: | ||
450 | idx = CMD_SPD_SHI; | ||
451 | break; | ||
452 | case ASYNC_SPD_WARP: | ||
453 | idx = CMD_SPD_WARP; | ||
454 | break; | ||
455 | case ASYNC_SPD_CUST: | ||
456 | idx = CMD_SPD_CUST; | ||
457 | break; | ||
458 | default: | ||
459 | if (mode < PRINT_FEDBACK) | ||
460 | return NULL; | ||
461 | idx = CMD_SPD_NORMAL; | ||
462 | } | ||
463 | |||
464 | return nth_string(commands, idx); | ||
465 | } | ||
466 | |||
467 | static int get_numeric(const char *arg) | ||
468 | { | ||
469 | return bb_strtol(arg, NULL, 0); | ||
470 | } | ||
471 | |||
472 | static int get_wait(const char *arg) | ||
473 | { | ||
474 | if (strcasecmp(arg, STR_NONE) == 0) | ||
475 | return ASYNC_CLOSING_WAIT_NONE; | ||
476 | |||
477 | if (strcasecmp(arg, STR_INFINITE) == 0) | ||
478 | return ASYNC_CLOSING_WAIT_INF; | ||
479 | |||
480 | return get_numeric(arg); | ||
481 | } | ||
482 | |||
483 | static int get_uart(const char *arg) | ||
484 | { | ||
485 | int uart = uart_id(arg); | ||
486 | |||
487 | if (uart < 0) | ||
488 | bb_error_msg_and_die("illegal UART type: %s", arg); | ||
489 | |||
490 | return uart; | ||
491 | } | ||
492 | |||
493 | static int serial_open(const char *dev, bool quiet) | ||
494 | { | ||
495 | int fd; | ||
496 | |||
497 | fd = device_open(dev, O_RDWR | O_NONBLOCK); | ||
498 | if (fd < 0 && !quiet) | ||
499 | bb_simple_perror_msg(dev); | ||
500 | |||
501 | return fd; | ||
502 | } | ||
503 | |||
504 | static int serial_ctl(int fd, int ops, struct serial_struct *serinfo) | ||
505 | { | ||
506 | int ret = 0; | ||
507 | const char *err; | ||
508 | |||
509 | if (ops & CTL_SET) { | ||
510 | ret = ioctl(fd, TIOCSSERIAL, serinfo); | ||
511 | if (ret < 0) { | ||
512 | err = "can't set serial info"; | ||
513 | goto fail; | ||
514 | } | ||
515 | } | ||
516 | |||
517 | if (ops & CTL_CONFIG) { | ||
518 | ret = ioctl(fd, TIOCSERCONFIG); | ||
519 | if (ret < 0) { | ||
520 | err = "can't autoconfigure port"; | ||
521 | goto fail; | ||
522 | } | ||
523 | } | ||
524 | |||
525 | if (ops & CTL_GET) { | ||
526 | ret = ioctl(fd, TIOCGSERIAL, serinfo); | ||
527 | if (ret < 0) { | ||
528 | err = "can't get serial info"; | ||
529 | goto fail; | ||
530 | } | ||
531 | } | ||
532 | nodie: | ||
533 | if (ops & CTL_CLOSE) | ||
534 | close(fd); | ||
535 | |||
536 | return ret; | ||
537 | fail: | ||
538 | bb_simple_perror_msg(err); | ||
539 | if (ops & CTL_NODIE) | ||
540 | goto nodie; | ||
541 | exit(EXIT_FAILURE); | ||
542 | } | ||
543 | |||
544 | static void print_flag(const char **prefix, const char *flag) | ||
545 | { | ||
546 | printf("%s%s", *prefix, flag); | ||
547 | *prefix = " "; | ||
548 | } | ||
549 | |||
550 | static void print_serial_flags(int serial_flags, enum print_mode mode, | ||
551 | const char *prefix, const char *postfix) | ||
552 | { | ||
553 | int i; | ||
554 | const char *spd, *pr; | ||
555 | |||
556 | pr = prefix; | ||
557 | |||
558 | spd = get_spd(serial_flags, mode); | ||
559 | if (spd) | ||
560 | print_flag(&pr, spd); | ||
561 | |||
562 | for (i = CMD_FLAG_FIRST; i <= CMD_FLAG_LAST; i++) { | ||
563 | if ((serial_flags & setbits[i]) | ||
564 | && (mode > PRINT_SUMMARY || !cmd_noprint(i)) | ||
565 | ) { | ||
566 | print_flag(&pr, nth_string(commands, i)); | ||
567 | } | ||
568 | } | ||
569 | |||
570 | puts(pr == prefix ? "" : postfix); | ||
571 | } | ||
572 | |||
573 | static void print_closing_wait(unsigned int closing_wait) | ||
574 | { | ||
575 | switch (closing_wait) { | ||
576 | case ASYNC_CLOSING_WAIT_NONE: | ||
577 | puts(STR_NONE); | ||
578 | break; | ||
579 | case ASYNC_CLOSING_WAIT_INF: | ||
580 | puts(STR_INFINITE); | ||
581 | break; | ||
582 | default: | ||
583 | printf("%u\n", closing_wait); | ||
584 | } | ||
585 | } | ||
586 | |||
587 | static void serial_get(const char *device, enum print_mode mode) | ||
588 | { | ||
589 | int fd, ret; | ||
590 | const char *uart, *prefix, *postfix; | ||
591 | struct serial_struct serinfo; | ||
592 | |||
593 | fd = serial_open(device, /*quiet:*/ mode == PRINT_SUMMARY); | ||
594 | if (fd < 0) | ||
595 | return; | ||
596 | |||
597 | ret = serial_ctl(fd, CTL_GET | CTL_CLOSE | CTL_NODIE, &serinfo); | ||
598 | if (ret < 0) | ||
599 | return; | ||
600 | |||
601 | uart = uart_type(serinfo.type); | ||
602 | prefix = ", Flags: "; | ||
603 | postfix = ""; | ||
604 | |||
605 | switch (mode) { | ||
606 | case PRINT_NORMAL: | ||
607 | printf("%s, UART: %s, Port: 0x%.4x, IRQ: %d", | ||
608 | device, uart, serinfo.port, serinfo.irq); | ||
609 | break; | ||
610 | case PRINT_SUMMARY: | ||
611 | if (!serinfo.type) | ||
612 | return; | ||
613 | printf("%s at 0x%.4x (irq = %d) is a %s", | ||
614 | device, serinfo.port, serinfo.irq, uart); | ||
615 | prefix = " ("; | ||
616 | postfix = ")"; | ||
617 | break; | ||
618 | case PRINT_FEDBACK: | ||
619 | printf("%s uart %s port 0x%.4x irq %d baud_base %d", device, | ||
620 | uart, serinfo.port, serinfo.irq, serinfo.baud_base); | ||
621 | prefix = " "; | ||
622 | break; | ||
623 | case PRINT_ALL: | ||
624 | printf("%s, Line %d, UART: %s, Port: 0x%.4x, IRQ: %d\n", | ||
625 | device, serinfo.line, uart, serinfo.port, serinfo.irq); | ||
626 | printf("\tBaud_base: %d, close_delay: %u, divisor: %d\n", | ||
627 | serinfo.baud_base, serinfo.close_delay, | ||
628 | serinfo.custom_divisor); | ||
629 | printf("\tclosing_wait: "); | ||
630 | print_closing_wait(serinfo.closing_wait); | ||
631 | prefix = "\tFlags: "; | ||
632 | postfix = "\n"; | ||
633 | break; | ||
634 | default: | ||
635 | assert(0); | ||
636 | } | ||
637 | |||
638 | print_serial_flags(serinfo.flags, mode, prefix, postfix); | ||
639 | } | ||
640 | |||
641 | static int find_cmd(const char *cmd) | ||
642 | { | ||
643 | int idx; | ||
644 | |||
645 | idx = index_in_strings_case_insensitive(commands, cmd); | ||
646 | if (idx < 0) | ||
647 | bb_error_msg_and_die("invalid flag: %s", cmd); | ||
648 | |||
649 | return idx; | ||
650 | } | ||
651 | |||
652 | static void serial_set(char **arg, int opts) | ||
653 | { | ||
654 | struct serial_struct serinfo; | ||
655 | int cmd; | ||
656 | const char *word; | ||
657 | int fd; | ||
658 | |||
659 | fd = serial_open(*arg++, /*quiet:*/ false); | ||
660 | if (fd < 0) | ||
661 | exit(201); | ||
662 | |||
663 | serial_ctl(fd, CTL_GET, &serinfo); | ||
664 | |||
665 | if (opts & OPT_ZERO) | ||
666 | serinfo.flags = 0; | ||
667 | |||
668 | while (*arg) { | ||
669 | int invert; | ||
670 | |||
671 | word = *arg++; | ||
672 | invert = (*word == '^'); | ||
673 | word += invert; | ||
674 | |||
675 | cmd = find_cmd(word); | ||
676 | |||
677 | if (*arg == NULL && cmd_need_arg(cmd)) | ||
678 | bb_error_msg_and_die(bb_msg_requires_arg, word); | ||
679 | |||
680 | if (invert && !cmd_is_flag(cmd)) | ||
681 | bb_error_msg_and_die("can't invert %s", word); | ||
682 | |||
683 | switch (cmd) { | ||
684 | case CMD_SPD_NORMAL: | ||
685 | case CMD_SPD_HI: | ||
686 | case CMD_SPD_VHI: | ||
687 | case CMD_SPD_SHI: | ||
688 | case CMD_SPD_WARP: | ||
689 | case CMD_SPD_CUST: | ||
690 | serinfo.flags &= ~ASYNC_SPD_MASK; | ||
691 | /* fallthrough */ | ||
692 | case CMD_FLAG_SAK: | ||
693 | case CMD_FLAG_FOURPORT: | ||
694 | case CMD_FLAG_NUP_NOTIFY: | ||
695 | case CMD_FLAG_SKIP_TEST: | ||
696 | case CMD_FLAG_AUTO_IRQ: | ||
697 | case CMD_FLAG_SPLIT_TERMIOS: | ||
698 | case CMD_FLAG_SESSION_LOCKOUT: | ||
699 | case CMD_FLAG_PGRP_LOCKOUT: | ||
700 | case CMD_FLAG_CALLOUT_NOHUP: | ||
701 | case CMD_FLAG_LOW_LATENCY: | ||
702 | if (invert) | ||
703 | serinfo.flags &= ~setbits[cmd]; | ||
704 | else | ||
705 | serinfo.flags |= setbits[cmd]; | ||
706 | break; | ||
707 | case CMD_PORT: | ||
708 | serinfo.port = get_numeric(*arg++); | ||
709 | break; | ||
710 | case CMD_IRQ: | ||
711 | serinfo.irq = get_numeric(*arg++); | ||
712 | break; | ||
713 | case CMD_DIVISOR: | ||
714 | serinfo.custom_divisor = get_numeric(*arg++); | ||
715 | break; | ||
716 | case CMD_UART: | ||
717 | serinfo.type = get_uart(*arg++); | ||
718 | break; | ||
719 | case CMD_BASE: | ||
720 | serinfo.baud_base = get_numeric(*arg++); | ||
721 | break; | ||
722 | case CMD_DELAY: | ||
723 | serinfo.close_delay = get_numeric(*arg++); | ||
724 | break; | ||
725 | case CMD_WAIT: | ||
726 | serinfo.closing_wait = get_wait(*arg++); | ||
727 | break; | ||
728 | case CMD_AUTOCONFIG: | ||
729 | serial_ctl(fd, CTL_SET | CTL_CONFIG | CTL_GET, &serinfo); | ||
730 | break; | ||
731 | default: | ||
732 | assert(0); | ||
733 | } | ||
734 | } | ||
735 | |||
736 | serial_ctl(fd, CTL_SET | CTL_CLOSE, &serinfo); | ||
737 | } | ||
738 | |||
739 | int setserial_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
740 | int setserial_main(int argc UNUSED_PARAM, char **argv) | ||
741 | { | ||
742 | int opts; | ||
743 | |||
744 | opt_complementary = "-1:b-aG:G-ab:a-bG"; | ||
745 | opts = getopt32(argv, "bGavzg"); | ||
746 | argv += optind; | ||
747 | |||
748 | if (!argv[1]) /* one arg only? */ | ||
749 | opts |= OPT_GET; | ||
750 | |||
751 | if (!(opts & OPT_GET)) { | ||
752 | serial_set(argv, opts); | ||
753 | argv[1] = NULL; | ||
754 | } | ||
755 | |||
756 | if (opts & (OPT_VERBOSE | OPT_GET)) { | ||
757 | do { | ||
758 | serial_get(*argv++, opts & OPT_MODE_MASK); | ||
759 | } while (*argv); | ||
760 | } | ||
761 | |||
762 | return EXIT_SUCCESS; | ||
763 | } | ||
diff --git a/miscutils/ubi_attach_detach.c b/miscutils/ubi_tools.c index 9007f8c3f..fc7f38c5d 100644 --- a/miscutils/ubi_attach_detach.c +++ b/miscutils/ubi_tools.c | |||
@@ -37,18 +37,27 @@ | |||
37 | //config: select PLATFORM_LINUX | 37 | //config: select PLATFORM_LINUX |
38 | //config: help | 38 | //config: help |
39 | //config: Resize a UBI volume. | 39 | //config: Resize a UBI volume. |
40 | //config: | ||
41 | //config:config UBIUPDATEVOL | ||
42 | //config: bool "ubiupdatevol" | ||
43 | //config: default y | ||
44 | //config: select PLATFORM_LINUX | ||
45 | //config: help | ||
46 | //config: Update a UBI volume. | ||
40 | 47 | ||
41 | //applet:IF_UBIATTACH(APPLET_ODDNAME(ubiattach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiattach)) | 48 | //applet:IF_UBIATTACH(APPLET_ODDNAME(ubiattach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiattach)) |
42 | //applet:IF_UBIDETACH(APPLET_ODDNAME(ubidetach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubidetach)) | 49 | //applet:IF_UBIDETACH(APPLET_ODDNAME(ubidetach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubidetach)) |
43 | //applet:IF_UBIMKVOL(APPLET_ODDNAME(ubimkvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubimkvol)) | 50 | //applet:IF_UBIMKVOL(APPLET_ODDNAME(ubimkvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubimkvol)) |
44 | //applet:IF_UBIRMVOL(APPLET_ODDNAME(ubirmvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirmvol)) | 51 | //applet:IF_UBIRMVOL(APPLET_ODDNAME(ubirmvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirmvol)) |
45 | //applet:IF_UBIRSVOL(APPLET_ODDNAME(ubirsvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirsvol)) | 52 | //applet:IF_UBIRSVOL(APPLET_ODDNAME(ubirsvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirsvol)) |
53 | //applet:IF_UBIUPDATEVOL(APPLET_ODDNAME(ubiupdatevol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiupdatevol)) | ||
46 | 54 | ||
47 | //kbuild:lib-$(CONFIG_UBIATTACH) += ubi_attach_detach.o | 55 | //kbuild:lib-$(CONFIG_UBIATTACH) += ubi_tools.o |
48 | //kbuild:lib-$(CONFIG_UBIDETACH) += ubi_attach_detach.o | 56 | //kbuild:lib-$(CONFIG_UBIDETACH) += ubi_tools.o |
49 | //kbuild:lib-$(CONFIG_UBIMKVOL) += ubi_attach_detach.o | 57 | //kbuild:lib-$(CONFIG_UBIMKVOL) += ubi_tools.o |
50 | //kbuild:lib-$(CONFIG_UBIRMVOL) += ubi_attach_detach.o | 58 | //kbuild:lib-$(CONFIG_UBIRMVOL) += ubi_tools.o |
51 | //kbuild:lib-$(CONFIG_UBIRSVOL) += ubi_attach_detach.o | 59 | //kbuild:lib-$(CONFIG_UBIRSVOL) += ubi_tools.o |
60 | //kbuild:lib-$(CONFIG_UBIUPDATEVOL) += ubi_tools.o | ||
52 | 61 | ||
53 | #include "libbb.h" | 62 | #include "libbb.h" |
54 | #include <mtd/ubi-user.h> | 63 | #include <mtd/ubi-user.h> |
@@ -66,6 +75,7 @@ | |||
66 | #define do_mkvol (ENABLE_UBIMKVOL && applet_name[3] == 'm') | 75 | #define do_mkvol (ENABLE_UBIMKVOL && applet_name[3] == 'm') |
67 | #define do_rmvol (ENABLE_UBIRMVOL && applet_name[4] == 'm') | 76 | #define do_rmvol (ENABLE_UBIRMVOL && applet_name[4] == 'm') |
68 | #define do_rsvol (ENABLE_UBIRSVOL && applet_name[4] == 's') | 77 | #define do_rsvol (ENABLE_UBIRSVOL && applet_name[4] == 's') |
78 | #define do_update (ENABLE_UBIUPDATEVOL && applet_name[3] == 'u') | ||
69 | 79 | ||
70 | //usage:#define ubiattach_trivial_usage | 80 | //usage:#define ubiattach_trivial_usage |
71 | //usage: "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV" | 81 | //usage: "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV" |
@@ -85,7 +95,7 @@ | |||
85 | //usage:#define ubimkvol_trivial_usage | 95 | //usage:#define ubimkvol_trivial_usage |
86 | //usage: "UBI_DEVICE -N NAME -s SIZE" | 96 | //usage: "UBI_DEVICE -N NAME -s SIZE" |
87 | //usage:#define ubimkvol_full_usage "\n\n" | 97 | //usage:#define ubimkvol_full_usage "\n\n" |
88 | //usage: "Create UBI Volume\n" | 98 | //usage: "Create UBI volume\n" |
89 | //usage: "\nOptions:" | 99 | //usage: "\nOptions:" |
90 | //usage: "\n -a ALIGNMENT Volume alignment (default 1)" | 100 | //usage: "\n -a ALIGNMENT Volume alignment (default 1)" |
91 | //usage: "\n -n VOLID Volume ID, if not specified, it" | 101 | //usage: "\n -n VOLID Volume ID, if not specified, it" |
@@ -97,17 +107,26 @@ | |||
97 | //usage:#define ubirmvol_trivial_usage | 107 | //usage:#define ubirmvol_trivial_usage |
98 | //usage: "UBI_DEVICE -n VOLID" | 108 | //usage: "UBI_DEVICE -n VOLID" |
99 | //usage:#define ubirmvol_full_usage "\n\n" | 109 | //usage:#define ubirmvol_full_usage "\n\n" |
100 | //usage: "Remove UBI Volume\n" | 110 | //usage: "Remove UBI volume\n" |
101 | //usage: "\nOptions:" | 111 | //usage: "\nOptions:" |
102 | //usage: "\n -n VOLID Volume ID" | 112 | //usage: "\n -n VOLID Volume ID" |
103 | //usage: | 113 | //usage: |
104 | //usage:#define ubirsvol_trivial_usage | 114 | //usage:#define ubirsvol_trivial_usage |
105 | //usage: "UBI_DEVICE -N NAME -s SIZE" | 115 | //usage: "UBI_DEVICE -n VOLID -s SIZE" |
106 | //usage:#define ubirsvol_full_usage "\n\n" | 116 | //usage:#define ubirsvol_full_usage "\n\n" |
107 | //usage: "Resize UBI Volume\n" | 117 | //usage: "Resize UBI volume\n" |
108 | //usage: "\nOptions:" | 118 | //usage: "\nOptions:" |
109 | //usage: "\n -N NAME Volume name" | 119 | //usage: "\n -n VOLID Volume ID to resize" |
110 | //usage: "\n -s SIZE Size in bytes" | 120 | //usage: "\n -s SIZE Size in bytes" |
121 | //usage: | ||
122 | //usage:#define ubiupdatevol_trivial_usage | ||
123 | //usage: "UBI_DEVICE [IMG_FILE]" | ||
124 | //usage:#define ubiupdatevol_full_usage "\n\n" | ||
125 | //usage: "Update UBI volume\n" | ||
126 | //usage: "\nOptions:" | ||
127 | //usage: "\n -t Truncate UBI volume" | ||
128 | //usage: "\n -s SIZE Bytes in input (if reading stdin)" | ||
129 | |||
111 | 130 | ||
112 | int ubi_tools_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 131 | int ubi_tools_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
113 | int ubi_tools_main(int argc UNUSED_PARAM, char **argv) | 132 | int ubi_tools_main(int argc UNUSED_PARAM, char **argv) |
@@ -124,8 +143,8 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv) | |||
124 | int alignment = 1; | 143 | int alignment = 1; |
125 | char *type = NULL; | 144 | char *type = NULL; |
126 | 145 | ||
127 | opt_complementary = "=1:m+:d+:n+:s+:a+"; | 146 | opt_complementary = "-1:m+:d+:n+:s+:a+"; |
128 | opts = getopt32(argv, "m:d:n:N:s:a:t:", | 147 | opts = getopt32(argv, "m:d:n:N:s:a:t::", |
129 | &mtd_num, &dev_num, &vol_id, | 148 | &mtd_num, &dev_num, &vol_id, |
130 | &vol_name, &size_bytes, &alignment, &type | 149 | &vol_name, &size_bytes, &alignment, &type |
131 | ); | 150 | ); |
@@ -166,7 +185,7 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv) | |||
166 | 185 | ||
167 | memset(&req, 0, sizeof(req)); | 186 | memset(&req, 0, sizeof(req)); |
168 | req.vol_id = vol_id; | 187 | req.vol_id = vol_id; |
169 | if (opts & OPTION_t) { | 188 | if ((opts & OPTION_t) && type) { |
170 | if (type[0] == 's') | 189 | if (type[0] == 's') |
171 | req.vol_type = UBI_STATIC_VOLUME; | 190 | req.vol_type = UBI_STATIC_VOLUME; |
172 | else | 191 | else |
@@ -199,6 +218,57 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv) | |||
199 | req.vol_id = vol_id; | 218 | req.vol_id = vol_id; |
200 | 219 | ||
201 | xioctl(fd, UBI_IOCRSVOL, &req); | 220 | xioctl(fd, UBI_IOCRSVOL, &req); |
221 | } else | ||
222 | if (do_update) { | ||
223 | long long bytes; | ||
224 | |||
225 | if (opts & OPTION_t) { | ||
226 | // truncate the volume by starting an update for size 0 | ||
227 | bytes = 0; | ||
228 | xioctl(fd, UBI_IOCVOLUP, &bytes); | ||
229 | } | ||
230 | else { | ||
231 | struct stat st; | ||
232 | char buf[sizeof("/sys/class/ubi/ubi%d_%d/usable_eb_size") + 2 * sizeof(int)*3]; | ||
233 | int input_fd; | ||
234 | unsigned ubinum, volnum; | ||
235 | unsigned leb_size; | ||
236 | ssize_t len; | ||
237 | char *input_data; | ||
238 | |||
239 | // Make assumption that device not is in normal format. | ||
240 | // Removes need for scanning sysfs tree as full libubi does | ||
241 | if (sscanf(ubi_ctrl, "/dev/ubi%u_%u", &ubinum, &volnum) != 2) | ||
242 | bb_error_msg_and_die("%s volume node not in correct format", "UBI"); | ||
243 | |||
244 | sprintf(buf, "/sys/class/ubi/ubi%u_%u/usable_eb_size", ubinum, volnum); | ||
245 | if (open_read_close(buf, buf, sizeof(buf)) <= 0) | ||
246 | bb_error_msg_and_die("%s could not get LEB size", "UBI"); | ||
247 | if (sscanf(buf, "%u", &leb_size) != 1) | ||
248 | bb_error_msg_and_die("%s could not get LEB size", "UBI"); | ||
249 | |||
250 | if (opts & OPTION_s) { | ||
251 | input_fd = 0; | ||
252 | } else { | ||
253 | if (!argv[optind+1]) | ||
254 | bb_show_usage(); | ||
255 | xstat(argv[optind+1], &st); | ||
256 | size_bytes = st.st_size; | ||
257 | input_fd = xopen(argv[optind+1], O_RDONLY); | ||
258 | } | ||
259 | |||
260 | bytes = size_bytes; | ||
261 | xioctl(fd, UBI_IOCVOLUP, &bytes); | ||
262 | |||
263 | input_data = xmalloc(leb_size); | ||
264 | while ((len = full_read(input_fd, input_data, leb_size)) > 0) { | ||
265 | xwrite(fd, input_data, len); | ||
266 | } | ||
267 | if (len < 0) | ||
268 | bb_error_msg_and_die("%s volume update failed", "UBI"); | ||
269 | if (ENABLE_FEATURE_CLEAN_UP) | ||
270 | close(input_fd); | ||
271 | } | ||
202 | } | 272 | } |
203 | 273 | ||
204 | if (ENABLE_FEATURE_CLEAN_UP) | 274 | if (ENABLE_FEATURE_CLEAN_UP) |
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c index 66316e21f..f63df55f4 100644 --- a/networking/ftpgetput.c +++ b/networking/ftpgetput.c | |||
@@ -60,7 +60,7 @@ struct globals { | |||
60 | FILE *control_stream; | 60 | FILE *control_stream; |
61 | int verbose_flag; | 61 | int verbose_flag; |
62 | int do_continue; | 62 | int do_continue; |
63 | char buf[1]; /* actually [BUFSZ] */ | 63 | char buf[4]; /* actually [BUFSZ] */ |
64 | } FIX_ALIASING; | 64 | } FIX_ALIASING; |
65 | #define G (*(struct globals*)&bb_common_bufsiz1) | 65 | #define G (*(struct globals*)&bb_common_bufsiz1) |
66 | enum { BUFSZ = COMMON_BUFSIZE - offsetof(struct globals, buf) }; | 66 | enum { BUFSZ = COMMON_BUFSIZE - offsetof(struct globals, buf) }; |
@@ -105,7 +105,7 @@ static int ftpcmd(const char *s1, const char *s2) | |||
105 | } | 105 | } |
106 | 106 | ||
107 | do { | 107 | do { |
108 | strcpy(buf, "EOF"); | 108 | strcpy(buf, "EOF"); /* for ftp_die */ |
109 | if (fgets(buf, BUFSZ - 2, control_stream) == NULL) { | 109 | if (fgets(buf, BUFSZ - 2, control_stream) == NULL) { |
110 | ftp_die(NULL); | 110 | ftp_die(NULL); |
111 | } | 111 | } |
@@ -316,7 +316,6 @@ static const char ftpgetput_longopts[] ALIGN1 = | |||
316 | int ftpgetput_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 316 | int ftpgetput_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
317 | int ftpgetput_main(int argc UNUSED_PARAM, char **argv) | 317 | int ftpgetput_main(int argc UNUSED_PARAM, char **argv) |
318 | { | 318 | { |
319 | unsigned opt; | ||
320 | const char *port = "ftp"; | 319 | const char *port = "ftp"; |
321 | /* socket to ftp server */ | 320 | /* socket to ftp server */ |
322 | 321 | ||
@@ -345,7 +344,7 @@ int ftpgetput_main(int argc UNUSED_PARAM, char **argv) | |||
345 | applet_long_options = ftpgetput_longopts; | 344 | applet_long_options = ftpgetput_longopts; |
346 | #endif | 345 | #endif |
347 | opt_complementary = "-2:vv:cc"; /* must have 2 to 3 params; -v and -c count */ | 346 | opt_complementary = "-2:vv:cc"; /* must have 2 to 3 params; -v and -c count */ |
348 | opt = getopt32(argv, "cvu:p:P:", &user, &password, &port, | 347 | getopt32(argv, "cvu:p:P:", &user, &password, &port, |
349 | &verbose_flag, &do_continue); | 348 | &verbose_flag, &do_continue); |
350 | argv += optind; | 349 | argv += optind; |
351 | 350 | ||
diff --git a/networking/httpd.c b/networking/httpd.c index d6157aca2..d77342a2a 100644 --- a/networking/httpd.c +++ b/networking/httpd.c | |||
@@ -2424,7 +2424,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv) | |||
2424 | salt[0] = '$'; | 2424 | salt[0] = '$'; |
2425 | salt[1] = '1'; | 2425 | salt[1] = '1'; |
2426 | salt[2] = '$'; | 2426 | salt[2] = '$'; |
2427 | crypt_make_salt(salt + 3, 4, 0); | 2427 | crypt_make_salt(salt + 3, 4); |
2428 | puts(pw_encrypt(pass, salt, 1)); | 2428 | puts(pw_encrypt(pass, salt, 1)); |
2429 | return 0; | 2429 | return 0; |
2430 | } | 2430 | } |
diff --git a/networking/inetd.c b/networking/inetd.c index 226a6491c..58ae089d1 100644 --- a/networking/inetd.c +++ b/networking/inetd.c | |||
@@ -1164,12 +1164,17 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) | |||
1164 | sigaddset(&sa.sa_mask, SIGALRM); | 1164 | sigaddset(&sa.sa_mask, SIGALRM); |
1165 | sigaddset(&sa.sa_mask, SIGCHLD); | 1165 | sigaddset(&sa.sa_mask, SIGCHLD); |
1166 | sigaddset(&sa.sa_mask, SIGHUP); | 1166 | sigaddset(&sa.sa_mask, SIGHUP); |
1167 | //FIXME: explain why no SA_RESTART | ||
1168 | //FIXME: retry_network_setup is unsafe to run in signal handler (many reasons)! | ||
1167 | sa.sa_handler = retry_network_setup; | 1169 | sa.sa_handler = retry_network_setup; |
1168 | sigaction_set(SIGALRM, &sa); | 1170 | sigaction_set(SIGALRM, &sa); |
1171 | //FIXME: reread_config_file is unsafe to run in signal handler(many reasons)! | ||
1169 | sa.sa_handler = reread_config_file; | 1172 | sa.sa_handler = reread_config_file; |
1170 | sigaction_set(SIGHUP, &sa); | 1173 | sigaction_set(SIGHUP, &sa); |
1174 | //FIXME: reap_child is unsafe to run in signal handler (uses stdio)! | ||
1171 | sa.sa_handler = reap_child; | 1175 | sa.sa_handler = reap_child; |
1172 | sigaction_set(SIGCHLD, &sa); | 1176 | sigaction_set(SIGCHLD, &sa); |
1177 | //FIXME: clean_up_and_exit is unsafe to run in signal handler (uses stdio)! | ||
1173 | sa.sa_handler = clean_up_and_exit; | 1178 | sa.sa_handler = clean_up_and_exit; |
1174 | sigaction_set(SIGTERM, &sa); | 1179 | sigaction_set(SIGTERM, &sa); |
1175 | sa.sa_handler = clean_up_and_exit; | 1180 | sa.sa_handler = clean_up_and_exit; |
diff --git a/networking/ntpd.c b/networking/ntpd.c index e27dbaa6b..165673a1e 100644 --- a/networking/ntpd.c +++ b/networking/ntpd.c | |||
@@ -1745,7 +1745,7 @@ static NOINLINE void | |||
1745 | recv_and_process_client_pkt(void /*int fd*/) | 1745 | recv_and_process_client_pkt(void /*int fd*/) |
1746 | { | 1746 | { |
1747 | ssize_t size; | 1747 | ssize_t size; |
1748 | uint8_t version; | 1748 | //uint8_t version; |
1749 | len_and_sockaddr *to; | 1749 | len_and_sockaddr *to; |
1750 | struct sockaddr *from; | 1750 | struct sockaddr *from; |
1751 | msg_t msg; | 1751 | msg_t msg; |
@@ -1793,7 +1793,7 @@ recv_and_process_client_pkt(void /*int fd*/) | |||
1793 | msg.m_rootdelay = d_to_sfp(G.rootdelay); | 1793 | msg.m_rootdelay = d_to_sfp(G.rootdelay); |
1794 | //simple code does not do this, fix simple code! | 1794 | //simple code does not do this, fix simple code! |
1795 | msg.m_rootdisp = d_to_sfp(G.rootdisp); | 1795 | msg.m_rootdisp = d_to_sfp(G.rootdisp); |
1796 | version = (query_status & VERSION_MASK); /* ... >> VERSION_SHIFT - done below instead */ | 1796 | //version = (query_status & VERSION_MASK); /* ... >> VERSION_SHIFT - done below instead */ |
1797 | msg.m_refid = G.refid; // (version > (3 << VERSION_SHIFT)) ? G.refid : G.refid3; | 1797 | msg.m_refid = G.refid; // (version > (3 << VERSION_SHIFT)) ? G.refid : G.refid3; |
1798 | 1798 | ||
1799 | /* We reply from the local address packet was sent to, | 1799 | /* We reply from the local address packet was sent to, |
diff --git a/networking/traceroute.c b/networking/traceroute.c index 96f9d3472..85181ab8d 100644 --- a/networking/traceroute.c +++ b/networking/traceroute.c | |||
@@ -398,18 +398,23 @@ static len_and_sockaddr* dup_sockaddr(const len_and_sockaddr *lsa) | |||
398 | 398 | ||
399 | 399 | ||
400 | static int | 400 | static int |
401 | wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to) | 401 | wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to, unsigned *timestamp_us, int *left_ms) |
402 | { | 402 | { |
403 | struct pollfd pfd[1]; | 403 | struct pollfd pfd[1]; |
404 | int read_len = 0; | 404 | int read_len = 0; |
405 | 405 | ||
406 | pfd[0].fd = rcvsock; | 406 | pfd[0].fd = rcvsock; |
407 | pfd[0].events = POLLIN; | 407 | pfd[0].events = POLLIN; |
408 | if (safe_poll(pfd, 1, waittime * 1000) > 0) { | 408 | if (*left_ms >= 0 && safe_poll(pfd, 1, *left_ms) > 0) { |
409 | unsigned t; | ||
410 | |||
409 | read_len = recv_from_to(rcvsock, | 411 | read_len = recv_from_to(rcvsock, |
410 | recv_pkt, sizeof(recv_pkt), | 412 | recv_pkt, sizeof(recv_pkt), |
411 | /*flags:*/ 0, | 413 | /*flags:*/ MSG_DONTWAIT, |
412 | &from_lsa->u.sa, to, from_lsa->len); | 414 | &from_lsa->u.sa, to, from_lsa->len); |
415 | t = monotonic_us(); | ||
416 | *left_ms -= (t - *timestamp_us) / 1000; | ||
417 | *timestamp_us = t; | ||
413 | } | 418 | } |
414 | 419 | ||
415 | return read_len; | 420 | return read_len; |
@@ -730,7 +735,7 @@ packet_ok(int read_len, len_and_sockaddr *from_lsa, | |||
730 | type, pr_type(type), icp->icmp6_code); | 735 | type, pr_type(type), icp->icmp6_code); |
731 | 736 | ||
732 | read_len -= sizeof(struct icmp6_hdr); | 737 | read_len -= sizeof(struct icmp6_hdr); |
733 | for (i = 0; i < read_len ; i++) { | 738 | for (i = 0; i < read_len; i++) { |
734 | if (i % 16 == 0) | 739 | if (i % 16 == 0) |
735 | printf("%04x:", i); | 740 | printf("%04x:", i); |
736 | if (i % 4 == 0) | 741 | if (i % 4 == 0) |
@@ -819,7 +824,6 @@ print_delta_ms(unsigned t1p, unsigned t2p) | |||
819 | static int | 824 | static int |
820 | common_traceroute_main(int op, char **argv) | 825 | common_traceroute_main(int op, char **argv) |
821 | { | 826 | { |
822 | int i; | ||
823 | int minpacket; | 827 | int minpacket; |
824 | int tos = 0; | 828 | int tos = 0; |
825 | int max_ttl = 30; | 829 | int max_ttl = 30; |
@@ -973,6 +977,7 @@ common_traceroute_main(int op, char **argv) | |||
973 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE && defined IP_OPTIONS | 977 | #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE && defined IP_OPTIONS |
974 | if (lsrr > 0) { | 978 | if (lsrr > 0) { |
975 | unsigned char optlist[MAX_IPOPTLEN]; | 979 | unsigned char optlist[MAX_IPOPTLEN]; |
980 | unsigned size; | ||
976 | 981 | ||
977 | /* final hop */ | 982 | /* final hop */ |
978 | gwlist[lsrr] = dest_lsa->u.sin.sin_addr.s_addr; | 983 | gwlist[lsrr] = dest_lsa->u.sin.sin_addr.s_addr; |
@@ -982,14 +987,14 @@ common_traceroute_main(int op, char **argv) | |||
982 | optlist[0] = IPOPT_NOP; | 987 | optlist[0] = IPOPT_NOP; |
983 | /* loose source route option */ | 988 | /* loose source route option */ |
984 | optlist[1] = IPOPT_LSRR; | 989 | optlist[1] = IPOPT_LSRR; |
985 | i = lsrr * sizeof(gwlist[0]); | 990 | size = lsrr * sizeof(gwlist[0]); |
986 | optlist[2] = i + 3; | 991 | optlist[2] = size + 3; |
987 | /* pointer to LSRR addresses */ | 992 | /* pointer to LSRR addresses */ |
988 | optlist[3] = IPOPT_MINOFF; | 993 | optlist[3] = IPOPT_MINOFF; |
989 | memcpy(optlist + 4, gwlist, i); | 994 | memcpy(optlist + 4, gwlist, size); |
990 | 995 | ||
991 | if (setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS, | 996 | if (setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS, |
992 | (char *)optlist, i + sizeof(gwlist[0])) < 0) { | 997 | (char *)optlist, size + sizeof(gwlist[0])) < 0) { |
993 | bb_perror_msg_and_die("IP_OPTIONS"); | 998 | bb_perror_msg_and_die("IP_OPTIONS"); |
994 | } | 999 | } |
995 | } | 1000 | } |
@@ -1103,28 +1108,34 @@ common_traceroute_main(int op, char **argv) | |||
1103 | int unreachable = 0; /* counter */ | 1108 | int unreachable = 0; /* counter */ |
1104 | int gotlastaddr = 0; /* flags */ | 1109 | int gotlastaddr = 0; /* flags */ |
1105 | int got_there = 0; | 1110 | int got_there = 0; |
1106 | int first = 1; | ||
1107 | 1111 | ||
1108 | printf("%2d", ttl); | 1112 | printf("%2d", ttl); |
1109 | for (probe = 0; probe < nprobes; ++probe) { | 1113 | for (probe = 0; probe < nprobes; ++probe) { |
1110 | int read_len; | 1114 | int read_len; |
1111 | unsigned t1; | 1115 | unsigned t1; |
1112 | unsigned t2; | 1116 | unsigned t2; |
1117 | int left_ms; | ||
1113 | struct ip *ip; | 1118 | struct ip *ip; |
1114 | 1119 | ||
1115 | if (!first && pausemsecs > 0) | ||
1116 | usleep(pausemsecs * 1000); | ||
1117 | fflush_all(); | 1120 | fflush_all(); |
1121 | if (probe != 0 && pausemsecs > 0) | ||
1122 | usleep(pausemsecs * 1000); | ||
1118 | 1123 | ||
1119 | t1 = monotonic_us(); | ||
1120 | send_probe(++seq, ttl); | 1124 | send_probe(++seq, ttl); |
1125 | t2 = t1 = monotonic_us(); | ||
1126 | |||
1127 | left_ms = waittime * 1000; | ||
1128 | while ((read_len = wait_for_reply(from_lsa, to, &t2, &left_ms)) != 0) { | ||
1129 | int icmp_code; | ||
1130 | |||
1131 | /* Recv'ed a packet, or read error */ | ||
1132 | /* t2 = monotonic_us() - set by wait_for_reply */ | ||
1121 | 1133 | ||
1122 | first = 0; | 1134 | if (read_len < 0) |
1123 | while ((read_len = wait_for_reply(from_lsa, to)) != 0) { | 1135 | continue; |
1124 | t2 = monotonic_us(); | 1136 | icmp_code = packet_ok(read_len, from_lsa, to, seq); |
1125 | i = packet_ok(read_len, from_lsa, to, seq); | ||
1126 | /* Skip short packet */ | 1137 | /* Skip short packet */ |
1127 | if (i == 0) | 1138 | if (icmp_code == 0) |
1128 | continue; | 1139 | continue; |
1129 | 1140 | ||
1130 | if (!gotlastaddr | 1141 | if (!gotlastaddr |
@@ -1143,10 +1154,10 @@ common_traceroute_main(int op, char **argv) | |||
1143 | printf(" (%d)", ip->ip_ttl); | 1154 | printf(" (%d)", ip->ip_ttl); |
1144 | 1155 | ||
1145 | /* time exceeded in transit */ | 1156 | /* time exceeded in transit */ |
1146 | if (i == -1) | 1157 | if (icmp_code == -1) |
1147 | break; | 1158 | break; |
1148 | i--; | 1159 | icmp_code--; |
1149 | switch (i) { | 1160 | switch (icmp_code) { |
1150 | #if ENABLE_TRACEROUTE6 | 1161 | #if ENABLE_TRACEROUTE6 |
1151 | case ICMP6_DST_UNREACH_NOPORT << 8: | 1162 | case ICMP6_DST_UNREACH_NOPORT << 8: |
1152 | got_there = 1; | 1163 | got_there = 1; |
@@ -1219,16 +1230,18 @@ common_traceroute_main(int op, char **argv) | |||
1219 | ++unreachable; | 1230 | ++unreachable; |
1220 | break; | 1231 | break; |
1221 | default: | 1232 | default: |
1222 | printf(" !<%d>", i); | 1233 | printf(" !<%d>", icmp_code); |
1223 | ++unreachable; | 1234 | ++unreachable; |
1224 | break; | 1235 | break; |
1225 | } | 1236 | } |
1226 | break; | 1237 | break; |
1227 | } | 1238 | } /* while (wait and read a packet) */ |
1239 | |||
1228 | /* there was no packet at all? */ | 1240 | /* there was no packet at all? */ |
1229 | if (read_len == 0) | 1241 | if (read_len == 0) |
1230 | printf(" *"); | 1242 | printf(" *"); |
1231 | } | 1243 | } /* for (nprobes) */ |
1244 | |||
1232 | bb_putchar('\n'); | 1245 | bb_putchar('\n'); |
1233 | if (got_there | 1246 | if (got_there |
1234 | || (unreachable > 0 && unreachable >= nprobes - 1) | 1247 | || (unreachable > 0 && unreachable >= nprobes - 1) |
diff --git a/networking/udhcp/Config.src b/networking/udhcp/Config.src index 9cd8cbbae..6bfa398ea 100644 --- a/networking/udhcp/Config.src +++ b/networking/udhcp/Config.src | |||
@@ -113,6 +113,14 @@ config FEATURE_UDHCP_RFC3397 | |||
113 | search lists via option 119, specified in RFC 3397, | 113 | search lists via option 119, specified in RFC 3397, |
114 | and SIP servers option 120, specified in RFC 3361. | 114 | and SIP servers option 120, specified in RFC 3361. |
115 | 115 | ||
116 | config FEATURE_UDHCP_8021Q | ||
117 | bool "Support for 802.1Q VLAN parameters" | ||
118 | default y | ||
119 | depends on UDHCPD || UDHCPC | ||
120 | help | ||
121 | If selected, both client and server will support passing of VLAN | ||
122 | ID and priority via options 132 and 133 as per 802.1Q. | ||
123 | |||
116 | config UDHCPC_DEFAULT_SCRIPT | 124 | config UDHCPC_DEFAULT_SCRIPT |
117 | string "Absolute path to config script" | 125 | string "Absolute path to config script" |
118 | default "/usr/share/udhcpc/default.script" | 126 | default "/usr/share/udhcpc/default.script" |
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c index 0a60261ab..70e34614c 100644 --- a/networking/udhcp/common.c +++ b/networking/udhcp/common.c | |||
@@ -55,6 +55,10 @@ const struct dhcp_optflag dhcp_optflags[] = { | |||
55 | { OPTION_SIP_SERVERS , 0x78 }, /* DHCP_SIP_SERVERS */ | 55 | { OPTION_SIP_SERVERS , 0x78 }, /* DHCP_SIP_SERVERS */ |
56 | #endif | 56 | #endif |
57 | { OPTION_STATIC_ROUTES , 0x79 }, /* DHCP_STATIC_ROUTES */ | 57 | { OPTION_STATIC_ROUTES , 0x79 }, /* DHCP_STATIC_ROUTES */ |
58 | #if ENABLE_FEATURE_UDHCP_8021Q | ||
59 | { OPTION_U16 , 0x84 }, /* DHCP_VLAN_ID */ | ||
60 | { OPTION_U8 , 0x85 }, /* DHCP_VLAN_PRIORITY */ | ||
61 | #endif | ||
58 | { OPTION_STATIC_ROUTES , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */ | 62 | { OPTION_STATIC_ROUTES , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */ |
59 | { OPTION_STRING , 0xfc }, /* DHCP_WPAD */ | 63 | { OPTION_STRING , 0xfc }, /* DHCP_WPAD */ |
60 | 64 | ||
@@ -118,6 +122,10 @@ const char dhcp_option_strings[] ALIGN1 = | |||
118 | // doesn't work in udhcpd.conf since OPTION_STATIC_ROUTES | 122 | // doesn't work in udhcpd.conf since OPTION_STATIC_ROUTES |
119 | // is not handled yet by "string->option" conversion code: | 123 | // is not handled yet by "string->option" conversion code: |
120 | "staticroutes" "\0"/* DHCP_STATIC_ROUTES */ | 124 | "staticroutes" "\0"/* DHCP_STATIC_ROUTES */ |
125 | #if ENABLE_FEATURE_UDHCP_8021Q | ||
126 | "vlanid" "\0" /* DHCP_VLAN_ID */ | ||
127 | "vlanpriority" "\0"/* DHCP_VLAN_PRIORITY */ | ||
128 | #endif | ||
121 | "msstaticroutes""\0"/* DHCP_MS_STATIC_ROUTES */ | 129 | "msstaticroutes""\0"/* DHCP_MS_STATIC_ROUTES */ |
122 | "wpad" "\0" /* DHCP_WPAD */ | 130 | "wpad" "\0" /* DHCP_WPAD */ |
123 | ; | 131 | ; |
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h index f8f18ff01..ad6991c94 100644 --- a/networking/udhcp/common.h +++ b/networking/udhcp/common.h | |||
@@ -145,6 +145,8 @@ enum { | |||
145 | //#define DHCP_DOMAIN_SEARCH 0x77 /* RFC 3397. set of ASCIZ string, DNS-style compressed */ | 145 | //#define DHCP_DOMAIN_SEARCH 0x77 /* RFC 3397. set of ASCIZ string, DNS-style compressed */ |
146 | //#define DHCP_SIP_SERVERS 0x78 /* RFC 3361. flag byte, then: 0: domain names, 1: IP addrs */ | 146 | //#define DHCP_SIP_SERVERS 0x78 /* RFC 3361. flag byte, then: 0: domain names, 1: IP addrs */ |
147 | //#define DHCP_STATIC_ROUTES 0x79 /* RFC 3442. (mask,ip,router) tuples */ | 147 | //#define DHCP_STATIC_ROUTES 0x79 /* RFC 3442. (mask,ip,router) tuples */ |
148 | #define DHCP_VLAN_ID 0x84 /* 802.1P VLAN ID */ | ||
149 | #define DHCP_VLAN_PRIORITY 0x85 /* 802.1Q VLAN priority */ | ||
148 | //#define DHCP_MS_STATIC_ROUTES 0xf9 /* Microsoft's pre-RFC 3442 code for 0x79? */ | 150 | //#define DHCP_MS_STATIC_ROUTES 0xf9 /* Microsoft's pre-RFC 3442 code for 0x79? */ |
149 | //#define DHCP_WPAD 0xfc /* MSIE's Web Proxy Autodiscovery Protocol */ | 151 | //#define DHCP_WPAD 0xfc /* MSIE's Web Proxy Autodiscovery Protocol */ |
150 | #define DHCP_END 0xff | 152 | #define DHCP_END 0xff |
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index ca82d37e6..510c3a1d0 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c | |||
@@ -300,6 +300,14 @@ static char **fill_envp(struct dhcp_packet *packet) | |||
300 | uint8_t *temp; | 300 | uint8_t *temp; |
301 | uint8_t overload = 0; | 301 | uint8_t overload = 0; |
302 | 302 | ||
303 | #define BITMAP unsigned | ||
304 | #define BBITS (sizeof(BITMAP) * 8) | ||
305 | #define BMASK(i) (1 << (i & (sizeof(BITMAP) * 8 - 1))) | ||
306 | #define FOUND_OPTS(i) (found_opts[(unsigned)i / BBITS]) | ||
307 | BITMAP found_opts[256 / BBITS]; | ||
308 | |||
309 | memset(found_opts, 0, sizeof(found_opts)); | ||
310 | |||
303 | /* We need 6 elements for: | 311 | /* We need 6 elements for: |
304 | * "interface=IFACE" | 312 | * "interface=IFACE" |
305 | * "ip=N.N.N.N" from packet->yiaddr | 313 | * "ip=N.N.N.N" from packet->yiaddr |
@@ -311,18 +319,21 @@ static char **fill_envp(struct dhcp_packet *packet) | |||
311 | envc = 6; | 319 | envc = 6; |
312 | /* +1 element for each option, +2 for subnet option: */ | 320 | /* +1 element for each option, +2 for subnet option: */ |
313 | if (packet) { | 321 | if (packet) { |
314 | for (i = 0; dhcp_optflags[i].code; i++) { | 322 | /* note: do not search for "pad" (0) and "end" (255) options */ |
315 | if (udhcp_get_option(packet, dhcp_optflags[i].code)) { | 323 | for (i = 1; i < 255; i++) { |
316 | if (dhcp_optflags[i].code == DHCP_SUBNET) | 324 | temp = udhcp_get_option(packet, i); |
325 | if (temp) { | ||
326 | if (i == DHCP_OPTION_OVERLOAD) | ||
327 | overload = *temp; | ||
328 | else if (i == DHCP_SUBNET) | ||
317 | envc++; /* for mton */ | 329 | envc++; /* for mton */ |
318 | envc++; | 330 | envc++; |
331 | /*if (i != DHCP_MESSAGE_TYPE)*/ | ||
332 | FOUND_OPTS(i) |= BMASK(i); | ||
319 | } | 333 | } |
320 | } | 334 | } |
321 | temp = udhcp_get_option(packet, DHCP_OPTION_OVERLOAD); | ||
322 | if (temp) | ||
323 | overload = *temp; | ||
324 | } | 335 | } |
325 | curr = envp = xzalloc(sizeof(char *) * envc); | 336 | curr = envp = xzalloc(sizeof(envp[0]) * envc); |
326 | 337 | ||
327 | *curr = xasprintf("interface=%s", client_config.interface); | 338 | *curr = xasprintf("interface=%s", client_config.interface); |
328 | putenv(*curr++); | 339 | putenv(*curr++); |
@@ -337,12 +348,16 @@ static char **fill_envp(struct dhcp_packet *packet) | |||
337 | opt_name = dhcp_option_strings; | 348 | opt_name = dhcp_option_strings; |
338 | i = 0; | 349 | i = 0; |
339 | while (*opt_name) { | 350 | while (*opt_name) { |
340 | temp = udhcp_get_option(packet, dhcp_optflags[i].code); | 351 | uint8_t code = dhcp_optflags[i].code; |
341 | if (!temp) | 352 | BITMAP *found_ptr = &FOUND_OPTS(code); |
353 | BITMAP found_mask = BMASK(code); | ||
354 | if (!(*found_ptr & found_mask)) | ||
342 | goto next; | 355 | goto next; |
356 | *found_ptr &= ~found_mask; /* leave only unknown options */ | ||
357 | temp = udhcp_get_option(packet, code); | ||
343 | *curr = xmalloc_optname_optval(temp, &dhcp_optflags[i], opt_name); | 358 | *curr = xmalloc_optname_optval(temp, &dhcp_optflags[i], opt_name); |
344 | putenv(*curr++); | 359 | putenv(*curr++); |
345 | if (dhcp_optflags[i].code == DHCP_SUBNET) { | 360 | if (code == DHCP_SUBNET) { |
346 | /* Subnet option: make things like "$ip/$mask" possible */ | 361 | /* Subnet option: make things like "$ip/$mask" possible */ |
347 | uint32_t subnet; | 362 | uint32_t subnet; |
348 | move_from_unaligned32(subnet, temp); | 363 | move_from_unaligned32(subnet, temp); |
@@ -368,6 +383,28 @@ static char **fill_envp(struct dhcp_packet *packet) | |||
368 | *curr = xasprintf("sname=%."DHCP_PKT_SNAME_LEN_STR"s", packet->sname); | 383 | *curr = xasprintf("sname=%."DHCP_PKT_SNAME_LEN_STR"s", packet->sname); |
369 | putenv(*curr++); | 384 | putenv(*curr++); |
370 | } | 385 | } |
386 | /* Handle unknown options */ | ||
387 | for (i = 0; i < 256;) { | ||
388 | BITMAP bitmap = FOUND_OPTS(i); | ||
389 | if (!bitmap) { | ||
390 | i += BBITS; | ||
391 | continue; | ||
392 | } | ||
393 | if (bitmap & BMASK(i)) { | ||
394 | unsigned len, ofs; | ||
395 | |||
396 | temp = udhcp_get_option(packet, i); | ||
397 | /* udhcp_get_option returns ptr to data portion, | ||
398 | * need to go back to get len | ||
399 | */ | ||
400 | len = temp[-OPT_DATA + OPT_LEN]; | ||
401 | *curr = xmalloc(sizeof("optNNN=") + 1 + len*2); | ||
402 | ofs = sprintf(*curr, "opt%u=", i); | ||
403 | bin2hex(*curr + ofs, (void*) temp, len)[0] = '\0'; | ||
404 | putenv(*curr++); | ||
405 | } | ||
406 | i++; | ||
407 | } | ||
371 | return envp; | 408 | return envp; |
372 | } | 409 | } |
373 | 410 | ||
diff --git a/networking/udhcp/files.c b/networking/udhcp/files.c index 49bcafb9c..6840f3c25 100644 --- a/networking/udhcp/files.c +++ b/networking/udhcp/files.c | |||
@@ -80,9 +80,9 @@ static const struct config_keyword keywords[] = { | |||
80 | /* keywords with no defaults must be last! */ | 80 | /* keywords with no defaults must be last! */ |
81 | {"option" , udhcp_str2optset, &server_config.options , ""}, | 81 | {"option" , udhcp_str2optset, &server_config.options , ""}, |
82 | {"opt" , udhcp_str2optset, &server_config.options , ""}, | 82 | {"opt" , udhcp_str2optset, &server_config.options , ""}, |
83 | {"notify_file" , read_str , &server_config.notify_file , ""}, | 83 | {"notify_file" , read_str , &server_config.notify_file , NULL}, |
84 | {"sname" , read_str , &server_config.sname , ""}, | 84 | {"sname" , read_str , &server_config.sname , NULL}, |
85 | {"boot_file" , read_str , &server_config.boot_file , ""}, | 85 | {"boot_file" , read_str , &server_config.boot_file , NULL}, |
86 | {"static_lease" , read_staticlease, &server_config.static_leases, ""}, | 86 | {"static_lease" , read_staticlease, &server_config.static_leases, ""}, |
87 | }; | 87 | }; |
88 | enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 }; | 88 | enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 }; |
diff --git a/printutils/lpd.c b/printutils/lpd.c index 115552e0b..642e8a89e 100644 --- a/printutils/lpd.c +++ b/printutils/lpd.c | |||
@@ -102,7 +102,7 @@ static char *xmalloc_read_stdin(void) | |||
102 | { | 102 | { |
103 | // SECURITY: | 103 | // SECURITY: |
104 | size_t max = 4 * 1024; // more than enough for commands! | 104 | size_t max = 4 * 1024; // more than enough for commands! |
105 | return xmalloc_reads(STDIN_FILENO, NULL, &max); | 105 | return xmalloc_reads(STDIN_FILENO, &max); |
106 | } | 106 | } |
107 | 107 | ||
108 | int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; | 108 | int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; |
diff --git a/procps/fuser.c b/procps/fuser.c index 7837ff883..8d63a7313 100644 --- a/procps/fuser.c +++ b/procps/fuser.c | |||
@@ -37,33 +37,18 @@ typedef struct inode_list { | |||
37 | dev_t dev; | 37 | dev_t dev; |
38 | } inode_list; | 38 | } inode_list; |
39 | 39 | ||
40 | typedef struct pid_list { | ||
41 | struct pid_list *next; | ||
42 | pid_t pid; | ||
43 | } pid_list; | ||
44 | |||
45 | |||
46 | struct globals { | 40 | struct globals { |
47 | pid_list *pid_list_head; | 41 | int recursion_depth; |
42 | pid_t mypid; | ||
48 | inode_list *inode_list_head; | 43 | inode_list *inode_list_head; |
44 | smallint kill_failed; | ||
45 | int killsig; | ||
49 | } FIX_ALIASING; | 46 | } FIX_ALIASING; |
50 | #define G (*(struct globals*)&bb_common_bufsiz1) | 47 | #define G (*(struct globals*)&bb_common_bufsiz1) |
51 | #define INIT_G() do { } while (0) | 48 | #define INIT_G() do { \ |
52 | 49 | G.mypid = getpid(); \ | |
53 | 50 | G.killsig = SIGKILL; \ | |
54 | static void add_pid(const pid_t pid) | 51 | } while (0) |
55 | { | ||
56 | pid_list **curr = &G.pid_list_head; | ||
57 | |||
58 | while (*curr) { | ||
59 | if ((*curr)->pid == pid) | ||
60 | return; | ||
61 | curr = &(*curr)->next; | ||
62 | } | ||
63 | |||
64 | *curr = xzalloc(sizeof(pid_list)); | ||
65 | (*curr)->pid = pid; | ||
66 | } | ||
67 | 52 | ||
68 | static void add_inode(const struct stat *st) | 53 | static void add_inode(const struct stat *st) |
69 | { | 54 | { |
@@ -83,48 +68,7 @@ static void add_inode(const struct stat *st) | |||
83 | (*curr)->inode = st->st_ino; | 68 | (*curr)->inode = st->st_ino; |
84 | } | 69 | } |
85 | 70 | ||
86 | static void scan_proc_net(const char *path, unsigned port) | 71 | static smallint search_dev_inode(const struct stat *st) |
87 | { | ||
88 | char line[MAX_LINE + 1]; | ||
89 | long long uint64_inode; | ||
90 | unsigned tmp_port; | ||
91 | FILE *f; | ||
92 | struct stat st; | ||
93 | int fd; | ||
94 | |||
95 | /* find socket dev */ | ||
96 | st.st_dev = 0; | ||
97 | fd = socket(AF_INET, SOCK_DGRAM, 0); | ||
98 | if (fd >= 0) { | ||
99 | fstat(fd, &st); | ||
100 | close(fd); | ||
101 | } | ||
102 | |||
103 | f = fopen_for_read(path); | ||
104 | if (!f) | ||
105 | return; | ||
106 | |||
107 | while (fgets(line, MAX_LINE, f)) { | ||
108 | char addr[68]; | ||
109 | if (sscanf(line, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x " | ||
110 | "%*x:%*x %*x %*d %*d %llu", | ||
111 | addr, &tmp_port, &uint64_inode) == 3 | ||
112 | ) { | ||
113 | int len = strlen(addr); | ||
114 | if (len == 8 && (option_mask32 & OPT_IP6)) | ||
115 | continue; | ||
116 | if (len > 8 && (option_mask32 & OPT_IP4)) | ||
117 | continue; | ||
118 | if (tmp_port == port) { | ||
119 | st.st_ino = uint64_inode; | ||
120 | add_inode(&st); | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | fclose(f); | ||
125 | } | ||
126 | |||
127 | static int search_dev_inode(const struct stat *st) | ||
128 | { | 72 | { |
129 | inode_list *ilist = G.inode_list_head; | 73 | inode_list *ilist = G.inode_list_head; |
130 | 74 | ||
@@ -140,130 +84,202 @@ static int search_dev_inode(const struct stat *st) | |||
140 | return 0; | 84 | return 0; |
141 | } | 85 | } |
142 | 86 | ||
143 | static void scan_pid_maps(const char *fname, pid_t pid) | 87 | enum { |
88 | PROC_NET = 0, | ||
89 | PROC_DIR, | ||
90 | PROC_DIR_LINKS, | ||
91 | PROC_SUBDIR_LINKS, | ||
92 | }; | ||
93 | |||
94 | static smallint scan_proc_net_or_maps(const char *path, unsigned port) | ||
144 | { | 95 | { |
145 | FILE *file; | 96 | FILE *f; |
146 | char line[MAX_LINE + 1]; | 97 | char line[MAX_LINE + 1], addr[68]; |
147 | int major, minor; | 98 | int major, minor, r; |
148 | long long uint64_inode; | 99 | long long uint64_inode; |
149 | struct stat st; | 100 | unsigned tmp_port; |
101 | smallint retval; | ||
102 | struct stat statbuf; | ||
103 | const char *fmt; | ||
104 | void *fag, *sag; | ||
150 | 105 | ||
151 | file = fopen_for_read(fname); | 106 | f = fopen_for_read(path); |
152 | if (!file) | 107 | if (!f) |
153 | return; | 108 | return 0; |
154 | 109 | ||
155 | while (fgets(line, MAX_LINE, file)) { | 110 | if (G.recursion_depth == PROC_NET) { |
156 | if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3) | 111 | int fd; |
157 | continue; | ||
158 | st.st_ino = uint64_inode; | ||
159 | if (major == 0 && minor == 0 && st.st_ino == 0) | ||
160 | continue; | ||
161 | st.st_dev = makedev(major, minor); | ||
162 | if (search_dev_inode(&st)) | ||
163 | add_pid(pid); | ||
164 | } | ||
165 | fclose(file); | ||
166 | } | ||
167 | 112 | ||
168 | static void scan_link(const char *lname, pid_t pid) | 113 | /* find socket dev */ |
169 | { | 114 | statbuf.st_dev = 0; |
170 | struct stat st; | 115 | fd = socket(AF_INET, SOCK_DGRAM, 0); |
116 | if (fd >= 0) { | ||
117 | fstat(fd, &statbuf); | ||
118 | close(fd); | ||
119 | } | ||
171 | 120 | ||
172 | if (stat(lname, &st) >= 0) { | 121 | fmt = "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x " |
173 | if (search_dev_inode(&st)) | 122 | "%*x:%*x %*x:%*x %*x %*d %*d %llu"; |
174 | add_pid(pid); | 123 | fag = addr; |
124 | sag = &tmp_port; | ||
125 | } else { | ||
126 | fmt = "%*s %*s %*s %x:%x %llu"; | ||
127 | fag = &major; | ||
128 | sag = &minor; | ||
175 | } | 129 | } |
176 | } | ||
177 | |||
178 | static void scan_dir_links(const char *dname, pid_t pid) | ||
179 | { | ||
180 | DIR *d; | ||
181 | struct dirent *de; | ||
182 | char *lname; | ||
183 | |||
184 | d = opendir(dname); | ||
185 | if (!d) | ||
186 | return; | ||
187 | 130 | ||
188 | while ((de = readdir(d)) != NULL) { | 131 | retval = 0; |
189 | lname = concat_subpath_file(dname, de->d_name); | 132 | while (fgets(line, MAX_LINE, f)) { |
190 | if (lname == NULL) | 133 | r = sscanf(line, fmt, fag, sag, &uint64_inode); |
134 | if (r != 3) | ||
191 | continue; | 135 | continue; |
192 | scan_link(lname, pid); | 136 | |
193 | free(lname); | 137 | statbuf.st_ino = uint64_inode; |
138 | if (G.recursion_depth == PROC_NET) { | ||
139 | r = strlen(addr); | ||
140 | if (r == 8 && (option_mask32 & OPT_IP6)) | ||
141 | continue; | ||
142 | if (r > 8 && (option_mask32 & OPT_IP4)) | ||
143 | continue; | ||
144 | if (tmp_port == port) | ||
145 | add_inode(&statbuf); | ||
146 | } else { | ||
147 | if (major != 0 && minor != 0 && statbuf.st_ino != 0) { | ||
148 | statbuf.st_dev = makedev(major, minor); | ||
149 | retval = search_dev_inode(&statbuf); | ||
150 | if (retval) | ||
151 | break; | ||
152 | } | ||
153 | } | ||
194 | } | 154 | } |
195 | closedir(d); | 155 | fclose(f); |
156 | |||
157 | return retval; | ||
196 | } | 158 | } |
197 | 159 | ||
198 | /* NB: does chdir internally */ | 160 | static smallint scan_recursive(const char *path) |
199 | static void scan_proc_pids(void) | ||
200 | { | 161 | { |
201 | DIR *d; | 162 | DIR *d; |
202 | struct dirent *de; | 163 | struct dirent *d_ent; |
203 | pid_t pid; | 164 | smallint stop_scan; |
204 | 165 | smallint retval; | |
205 | xchdir("/proc"); | 166 | |
206 | d = opendir("/proc"); | 167 | d = opendir(path); |
207 | if (!d) | 168 | if (d == NULL) |
208 | return; | 169 | return 0; |
209 | 170 | ||
210 | while ((de = readdir(d)) != NULL) { | 171 | G.recursion_depth++; |
211 | pid = (pid_t)bb_strtou(de->d_name, NULL, 10); | 172 | retval = 0; |
212 | if (errno) | 173 | stop_scan = 0; |
213 | continue; | 174 | while (!stop_scan && (d_ent = readdir(d)) != NULL) { |
214 | if (chdir(de->d_name) < 0) | 175 | struct stat statbuf; |
215 | continue; | 176 | pid_t pid; |
216 | scan_link("cwd", pid); | 177 | char *subpath; |
217 | scan_link("exe", pid); | 178 | |
218 | scan_link("root", pid); | 179 | subpath = concat_subpath_file(path, d_ent->d_name); |
219 | 180 | if (subpath == NULL) | |
220 | scan_dir_links("fd", pid); | 181 | continue; /* . or .. */ |
221 | scan_dir_links("lib", pid); | 182 | |
222 | scan_dir_links("mmap", pid); | 183 | switch (G.recursion_depth) { |
184 | case PROC_DIR: | ||
185 | pid = (pid_t)bb_strtou(d_ent->d_name, NULL, 10); | ||
186 | if (errno != 0 | ||
187 | || pid == G.mypid | ||
188 | /* "this PID doesn't use specified FILEs or PORT/PROTO": */ | ||
189 | || scan_recursive(subpath) == 0 | ||
190 | ) { | ||
191 | break; | ||
192 | } | ||
193 | if (option_mask32 & OPT_KILL) { | ||
194 | if (kill(pid, G.killsig) != 0) { | ||
195 | bb_perror_msg("kill pid %s", d_ent->d_name); | ||
196 | G.kill_failed = 1; | ||
197 | } | ||
198 | } | ||
199 | if (!(option_mask32 & OPT_SILENT)) | ||
200 | printf("%s ", d_ent->d_name); | ||
201 | retval = 1; | ||
202 | break; | ||
223 | 203 | ||
224 | scan_pid_maps("maps", pid); | 204 | case PROC_DIR_LINKS: |
225 | xchdir("/proc"); | 205 | switch ( |
206 | index_in_substrings( | ||
207 | "cwd" "\0" "exe" "\0" | ||
208 | "root" "\0" "fd" "\0" | ||
209 | "lib" "\0" "mmap" "\0" | ||
210 | "maps" "\0", | ||
211 | d_ent->d_name | ||
212 | ) | ||
213 | ) { | ||
214 | enum { | ||
215 | CWD_LINK, | ||
216 | EXE_LINK, | ||
217 | ROOT_LINK, | ||
218 | FD_DIR_LINKS, | ||
219 | LIB_DIR_LINKS, | ||
220 | MMAP_DIR_LINKS, | ||
221 | MAPS, | ||
222 | }; | ||
223 | case CWD_LINK: | ||
224 | case EXE_LINK: | ||
225 | case ROOT_LINK: | ||
226 | goto scan_link; | ||
227 | case FD_DIR_LINKS: | ||
228 | case LIB_DIR_LINKS: | ||
229 | case MMAP_DIR_LINKS: | ||
230 | stop_scan = scan_recursive(subpath); | ||
231 | if (stop_scan) | ||
232 | retval = stop_scan; | ||
233 | break; | ||
234 | case MAPS: | ||
235 | stop_scan = scan_proc_net_or_maps(subpath, 0); | ||
236 | if (stop_scan) | ||
237 | retval = stop_scan; | ||
238 | default: | ||
239 | break; | ||
240 | } | ||
241 | break; | ||
242 | case PROC_SUBDIR_LINKS: | ||
243 | scan_link: | ||
244 | if (stat(subpath, &statbuf) < 0) | ||
245 | break; | ||
246 | stop_scan = search_dev_inode(&statbuf); | ||
247 | if (stop_scan) | ||
248 | retval = stop_scan; | ||
249 | default: | ||
250 | break; | ||
251 | } | ||
252 | free(subpath); | ||
226 | } | 253 | } |
227 | closedir(d); | 254 | closedir(d); |
255 | G.recursion_depth--; | ||
256 | return retval; | ||
228 | } | 257 | } |
229 | 258 | ||
230 | int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 259 | int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
231 | int fuser_main(int argc UNUSED_PARAM, char **argv) | 260 | int fuser_main(int argc UNUSED_PARAM, char **argv) |
232 | { | 261 | { |
233 | pid_list *plist; | ||
234 | pid_t mypid; | ||
235 | char **pp; | 262 | char **pp; |
236 | struct stat st; | 263 | |
237 | unsigned port; | 264 | INIT_G(); |
238 | int opt; | 265 | |
239 | int exitcode; | ||
240 | int killsig; | ||
241 | /* | ||
242 | fuser [OPTIONS] FILE or PORT/PROTO | ||
243 | Find processes which use FILEs or PORTs | ||
244 | -m Find processes which use same fs as FILEs | ||
245 | -4 Search only IPv4 space | ||
246 | -6 Search only IPv6 space | ||
247 | -s Don't display PIDs | ||
248 | -k Kill found processes | ||
249 | -SIGNAL Signal to send (default: KILL) | ||
250 | */ | ||
251 | /* Handle -SIGNAL. Oh my... */ | 266 | /* Handle -SIGNAL. Oh my... */ |
252 | killsig = SIGKILL; /* yes, the default is not SIGTERM */ | ||
253 | pp = argv; | 267 | pp = argv; |
254 | while (*++pp) { | 268 | while (*++pp) { |
269 | int sig; | ||
255 | char *arg = *pp; | 270 | char *arg = *pp; |
271 | |||
256 | if (arg[0] != '-') | 272 | if (arg[0] != '-') |
257 | continue; | 273 | continue; |
258 | if (arg[1] == '-' && arg[2] == '\0') /* "--" */ | 274 | if (arg[1] == '-' && arg[2] == '\0') /* "--" */ |
259 | break; | 275 | break; |
260 | if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0') | 276 | if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0') |
261 | continue; /* it's "-4" or "-6" */ | 277 | continue; /* it's "-4" or "-6" */ |
262 | opt = get_signum(&arg[1]); | 278 | sig = get_signum(&arg[1]); |
263 | if (opt < 0) | 279 | if (sig < 0) |
264 | continue; | 280 | continue; |
265 | /* "-SIGNAL" option found. Remove it and bail out */ | 281 | /* "-SIGNAL" option found. Remove it and bail out */ |
266 | killsig = opt; | 282 | G.killsig = sig; |
267 | do { | 283 | do { |
268 | pp[0] = arg = pp[1]; | 284 | pp[0] = arg = pp[1]; |
269 | pp++; | 285 | pp++; |
@@ -272,57 +288,35 @@ Find processes which use FILEs or PORTs | |||
272 | } | 288 | } |
273 | 289 | ||
274 | opt_complementary = "-1"; /* at least one param */ | 290 | opt_complementary = "-1"; /* at least one param */ |
275 | opt = getopt32(argv, OPTION_STRING); | 291 | getopt32(argv, OPTION_STRING); |
276 | argv += optind; | 292 | argv += optind; |
277 | 293 | ||
278 | pp = argv; | 294 | pp = argv; |
279 | while (*pp) { | 295 | while (*pp) { |
280 | /* parse net arg */ | 296 | /* parse net arg */ |
281 | char path[20], tproto[5]; | 297 | unsigned port; |
282 | if (sscanf(*pp, "%u/%4s", &port, tproto) != 2) | 298 | char path[sizeof("/proc/net/TCP6")]; |
283 | goto file; | 299 | |
284 | sprintf(path, "/proc/net/%s", tproto); | 300 | strcpy(path, "/proc/net/"); |
285 | if (access(path, R_OK) != 0) { /* PORT/PROTO */ | 301 | if (sscanf(*pp, "%u/%4s", &port, path + sizeof("/proc/net/")-1) == 2 |
286 | scan_proc_net(path, port); | 302 | && access(path, R_OK) == 0 |
287 | } else { /* FILE */ | 303 | ) { |
288 | file: | 304 | /* PORT/PROTO */ |
289 | xstat(*pp, &st); | 305 | scan_proc_net_or_maps(path, port); |
290 | add_inode(&st); | 306 | } else { |
307 | /* FILE */ | ||
308 | struct stat statbuf; | ||
309 | xstat(*pp, &statbuf); | ||
310 | add_inode(&statbuf); | ||
291 | } | 311 | } |
292 | pp++; | 312 | pp++; |
293 | } | 313 | } |
294 | 314 | ||
295 | scan_proc_pids(); /* changes dir to "/proc" */ | 315 | if (scan_recursive("/proc")) { |
296 | 316 | if (!(option_mask32 & OPT_SILENT)) | |
297 | mypid = getpid(); | 317 | bb_putchar('\n'); |
298 | plist = G.pid_list_head; | 318 | return G.kill_failed; |
299 | while (1) { | ||
300 | if (!plist) | ||
301 | return EXIT_FAILURE; | ||
302 | if (plist->pid != mypid) | ||
303 | break; | ||
304 | plist = plist->next; | ||
305 | } | ||
306 | |||
307 | exitcode = EXIT_SUCCESS; | ||
308 | do { | ||
309 | if (plist->pid != mypid) { | ||
310 | if (opt & OPT_KILL) { | ||
311 | if (kill(plist->pid, killsig) != 0) { | ||
312 | bb_perror_msg("kill pid %u", (unsigned)plist->pid); | ||
313 | exitcode = EXIT_FAILURE; | ||
314 | } | ||
315 | } | ||
316 | if (!(opt & OPT_SILENT)) { | ||
317 | printf("%u ", (unsigned)plist->pid); | ||
318 | } | ||
319 | } | ||
320 | plist = plist->next; | ||
321 | } while (plist); | ||
322 | |||
323 | if (!(opt & (OPT_SILENT))) { | ||
324 | bb_putchar('\n'); | ||
325 | } | 319 | } |
326 | 320 | ||
327 | return exitcode; | 321 | return EXIT_FAILURE; |
328 | } | 322 | } |
diff --git a/procps/iostat.c b/procps/iostat.c index 06a33eb1d..cd233c72f 100644 --- a/procps/iostat.c +++ b/procps/iostat.c | |||
@@ -24,8 +24,6 @@ | |||
24 | #define debug(fmt, ...) ((void)0) | 24 | #define debug(fmt, ...) ((void)0) |
25 | 25 | ||
26 | #define MAX_DEVICE_NAME 12 | 26 | #define MAX_DEVICE_NAME 12 |
27 | #define CURRENT 0 | ||
28 | #define LAST 1 | ||
29 | 27 | ||
30 | #if 1 | 28 | #if 1 |
31 | typedef unsigned long long cputime_t; | 29 | typedef unsigned long long cputime_t; |
@@ -39,18 +37,33 @@ typedef long icputime_t; | |||
39 | # define CPUTIME_MAX (~0UL) | 37 | # define CPUTIME_MAX (~0UL) |
40 | #endif | 38 | #endif |
41 | 39 | ||
42 | struct stats_cpu { | 40 | enum { |
43 | cputime_t cpu_user; | 41 | STATS_CPU_USER, |
44 | cputime_t cpu_nice; | 42 | STATS_CPU_NICE, |
45 | cputime_t cpu_system; | 43 | STATS_CPU_SYSTEM, |
46 | cputime_t cpu_idle; | 44 | STATS_CPU_IDLE, |
47 | cputime_t cpu_iowait; | 45 | STATS_CPU_IOWAIT, |
48 | cputime_t cpu_steal; | 46 | STATS_CPU_IRQ, |
49 | cputime_t cpu_irq; | 47 | STATS_CPU_SOFTIRQ, |
50 | cputime_t cpu_softirq; | 48 | STATS_CPU_STEAL, |
51 | cputime_t cpu_guest; | 49 | STATS_CPU_GUEST, |
50 | |||
51 | GLOBAL_UPTIME, | ||
52 | SMP_UPTIME, | ||
53 | |||
54 | N_STATS_CPU, | ||
52 | }; | 55 | }; |
53 | 56 | ||
57 | typedef struct { | ||
58 | cputime_t vector[N_STATS_CPU]; | ||
59 | } stats_cpu_t; | ||
60 | |||
61 | typedef struct { | ||
62 | stats_cpu_t *prev; | ||
63 | stats_cpu_t *curr; | ||
64 | cputime_t itv; | ||
65 | } stats_cpu_pair_t; | ||
66 | |||
54 | struct stats_dev { | 67 | struct stats_dev { |
55 | char dname[MAX_DEVICE_NAME]; | 68 | char dname[MAX_DEVICE_NAME]; |
56 | unsigned long long rd_sectors; | 69 | unsigned long long rd_sectors; |
@@ -59,24 +72,24 @@ struct stats_dev { | |||
59 | unsigned long wr_ops; | 72 | unsigned long wr_ops; |
60 | }; | 73 | }; |
61 | 74 | ||
62 | /* List of devices entered on the command line */ | ||
63 | struct device_list { | ||
64 | char dname[MAX_DEVICE_NAME]; | ||
65 | }; | ||
66 | |||
67 | /* Globals. Sort by size and access frequency. */ | 75 | /* Globals. Sort by size and access frequency. */ |
68 | struct globals { | 76 | struct globals { |
69 | smallint show_all; | 77 | smallint show_all; |
70 | unsigned devlist_i; /* Index to the list of devices */ | ||
71 | unsigned total_cpus; /* Number of CPUs */ | 78 | unsigned total_cpus; /* Number of CPUs */ |
72 | unsigned clk_tck; /* Number of clock ticks per second */ | 79 | unsigned clk_tck; /* Number of clock ticks per second */ |
73 | struct device_list *dlist; | 80 | llist_t *dev_list; /* List of devices entered on the command line */ |
74 | struct stats_dev *saved_stats_dev; | 81 | struct stats_dev *saved_stats_dev; |
75 | struct tm tmtime; | 82 | struct tm tmtime; |
83 | struct { | ||
84 | const char *str; | ||
85 | unsigned div; | ||
86 | } unit; | ||
76 | }; | 87 | }; |
77 | #define G (*ptr_to_globals) | 88 | #define G (*ptr_to_globals) |
78 | #define INIT_G() do { \ | 89 | #define INIT_G() do { \ |
79 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ | 90 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ |
91 | G.unit.str = "Blk"; \ | ||
92 | G.unit.div = 1; \ | ||
80 | } while (0) | 93 | } while (0) |
81 | 94 | ||
82 | /* Must match option string! */ | 95 | /* Must match option string! */ |
@@ -104,12 +117,12 @@ static void print_header(void) | |||
104 | char buf[16]; | 117 | char buf[16]; |
105 | struct utsname uts; | 118 | struct utsname uts; |
106 | 119 | ||
107 | if (uname(&uts) < 0) | 120 | uname(&uts); /* never fails */ |
108 | bb_perror_msg_and_die("uname"); | ||
109 | 121 | ||
122 | /* Date representation for the current locale */ | ||
110 | strftime(buf, sizeof(buf), "%x", &G.tmtime); | 123 | strftime(buf, sizeof(buf), "%x", &G.tmtime); |
111 | 124 | ||
112 | printf("%s %s (%s) \t%s \t_%s_\t(%d CPU)\n\n", | 125 | printf("%s %s (%s) \t%s \t_%s_\t(%u CPU)\n\n", |
113 | uts.sysname, uts.release, uts.nodename, | 126 | uts.sysname, uts.release, uts.nodename, |
114 | buf, uts.machine, G.total_cpus); | 127 | buf, uts.machine, G.total_cpus); |
115 | } | 128 | } |
@@ -124,61 +137,58 @@ static void get_localtime(struct tm *ptm) | |||
124 | static void print_timestamp(void) | 137 | static void print_timestamp(void) |
125 | { | 138 | { |
126 | char buf[20]; | 139 | char buf[20]; |
140 | /* %x: date representation for the current locale */ | ||
141 | /* %X: time representation for the current locale */ | ||
127 | strftime(buf, sizeof(buf), "%x %X", &G.tmtime); | 142 | strftime(buf, sizeof(buf), "%x %X", &G.tmtime); |
128 | printf("%s\n", buf); | 143 | printf("%s\n", buf); |
129 | } | 144 | } |
130 | 145 | ||
131 | /* Fetch CPU statistics from /proc/stat */ | 146 | static cputime_t get_smp_uptime(void) |
132 | static void get_cpu_statistics(struct stats_cpu *sc) | ||
133 | { | 147 | { |
134 | FILE *fp; | 148 | FILE *fp; |
135 | char buf[1024]; | 149 | unsigned long sec, dec; |
136 | |||
137 | fp = xfopen_for_read("/proc/stat"); | ||
138 | 150 | ||
139 | memset(sc, 0, sizeof(*sc)); | 151 | fp = xfopen_for_read("/proc/uptime"); |
140 | 152 | ||
141 | while (fgets(buf, sizeof(buf), fp)) { | 153 | if (fscanf(fp, "%lu.%lu", &sec, &dec) != 2) |
142 | /* Does the line starts with "cpu "? */ | 154 | bb_error_msg_and_die("can't read '%s'", "/proc/uptime"); |
143 | if (starts_with_cpu(buf) && buf[3] == ' ') { | ||
144 | sscanf(buf + 4 + 1, | ||
145 | "%"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %" | ||
146 | FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u", | ||
147 | &sc->cpu_user, &sc->cpu_nice, &sc->cpu_system, | ||
148 | &sc->cpu_idle, &sc->cpu_iowait, &sc->cpu_irq, | ||
149 | &sc->cpu_softirq, &sc->cpu_steal, &sc->cpu_guest); | ||
150 | } | ||
151 | } | ||
152 | 155 | ||
153 | fclose(fp); | 156 | fclose(fp); |
157 | |||
158 | return (cputime_t)sec * G.clk_tck + dec * G.clk_tck / 100; | ||
154 | } | 159 | } |
155 | 160 | ||
156 | static cputime_t get_smp_uptime(void) | 161 | /* Fetch CPU statistics from /proc/stat */ |
162 | static void get_cpu_statistics(stats_cpu_t *sc) | ||
157 | { | 163 | { |
158 | FILE *fp; | 164 | FILE *fp; |
159 | char buf[sizeof(long)*3 * 2 + 4]; | 165 | char buf[1024], *ibuf = buf + 4; |
160 | unsigned long sec, dec; | ||
161 | 166 | ||
162 | fp = xfopen_for_read("/proc/uptime"); | 167 | fp = xfopen_for_read("/proc/stat"); |
163 | 168 | ||
164 | if (fgets(buf, sizeof(buf), fp)) | 169 | memset(sc, 0, sizeof(*sc)); |
165 | if (sscanf(buf, "%lu.%lu", &sec, &dec) != 2) | ||
166 | bb_error_msg_and_die("can't read /proc/uptime"); | ||
167 | 170 | ||
168 | fclose(fp); | 171 | while (fgets(buf, sizeof(buf), fp)) { |
172 | /* Does the line starts with "cpu "? */ | ||
173 | if (!starts_with_cpu(buf) || buf[3] != ' ') { | ||
174 | continue; | ||
175 | } | ||
176 | for (int i = STATS_CPU_USER; i <= STATS_CPU_GUEST; i++) { | ||
177 | ibuf = skip_whitespace(ibuf); | ||
178 | sscanf(ibuf, "%"FMT_DATA"u", &sc->vector[i]); | ||
179 | if (i != STATS_CPU_GUEST) { | ||
180 | sc->vector[GLOBAL_UPTIME] += sc->vector[i]; | ||
181 | } | ||
182 | ibuf = skip_non_whitespace(ibuf); | ||
183 | } | ||
184 | break; | ||
185 | } | ||
169 | 186 | ||
170 | return (cputime_t)sec * G.clk_tck + dec * G.clk_tck / 100; | 187 | if (this_is_smp()) { |
171 | } | 188 | sc->vector[SMP_UPTIME] = get_smp_uptime(); |
189 | } | ||
172 | 190 | ||
173 | /* | 191 | fclose(fp); |
174 | * Obtain current uptime in jiffies. | ||
175 | * Uptime is sum of individual CPUs' uptimes. | ||
176 | */ | ||
177 | static cputime_t get_uptime(const struct stats_cpu *sc) | ||
178 | { | ||
179 | /* NB: Don't include cpu_guest, it is already in cpu_user */ | ||
180 | return sc->cpu_user + sc->cpu_nice + sc->cpu_system + sc->cpu_idle + | ||
181 | + sc->cpu_iowait + sc->cpu_irq + sc->cpu_steal + sc->cpu_softirq; | ||
182 | } | 192 | } |
183 | 193 | ||
184 | static ALWAYS_INLINE cputime_t get_interval(cputime_t old, cputime_t new) | 194 | static ALWAYS_INLINE cputime_t get_interval(cputime_t old, cputime_t new) |
@@ -219,65 +229,51 @@ static double percent_value(cputime_t prev, cputime_t curr, cputime_t itv) | |||
219 | return ((double)overflow_safe_sub(prev, curr)) / itv * 100; | 229 | return ((double)overflow_safe_sub(prev, curr)) / itv * 100; |
220 | } | 230 | } |
221 | 231 | ||
222 | static void print_stats_cpu_struct(const struct stats_cpu *p, | 232 | static void print_stats_cpu_struct(stats_cpu_pair_t *stats) |
223 | const struct stats_cpu *c, cputime_t itv) | ||
224 | { | 233 | { |
234 | cputime_t *p = stats->prev->vector; | ||
235 | cputime_t *c = stats->curr->vector; | ||
225 | printf(" %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f\n", | 236 | printf(" %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f\n", |
226 | percent_value(p->cpu_user , c->cpu_user , itv), | 237 | percent_value(p[STATS_CPU_USER] , c[STATS_CPU_USER] , stats->itv), |
227 | percent_value(p->cpu_nice , c->cpu_nice , itv), | 238 | percent_value(p[STATS_CPU_NICE] , c[STATS_CPU_NICE] , stats->itv), |
228 | percent_value(p->cpu_system + p->cpu_softirq + p->cpu_irq, | 239 | percent_value(p[STATS_CPU_SYSTEM] + p[STATS_CPU_SOFTIRQ] + p[STATS_CPU_IRQ], |
229 | c->cpu_system + c->cpu_softirq + c->cpu_irq, itv), | 240 | c[STATS_CPU_SYSTEM] + c[STATS_CPU_SOFTIRQ] + c[STATS_CPU_IRQ], stats->itv), |
230 | percent_value(p->cpu_iowait , c->cpu_iowait , itv), | 241 | percent_value(p[STATS_CPU_IOWAIT], c[STATS_CPU_IOWAIT], stats->itv), |
231 | percent_value(p->cpu_steal , c->cpu_steal , itv), | 242 | percent_value(p[STATS_CPU_STEAL] , c[STATS_CPU_STEAL] , stats->itv), |
232 | percent_value(p->cpu_idle , c->cpu_idle , itv) | 243 | percent_value(p[STATS_CPU_IDLE] , c[STATS_CPU_IDLE] , stats->itv) |
233 | ); | 244 | ); |
234 | } | 245 | } |
235 | 246 | ||
236 | static void print_stats_dev_struct(const struct stats_dev *p, | 247 | static void print_stats_dev_struct(const struct stats_dev *p, |
237 | const struct stats_dev *c, cputime_t itv) | 248 | const struct stats_dev *c, cputime_t itv) |
238 | { | 249 | { |
239 | int unit = 1; | ||
240 | |||
241 | if (option_mask32 & OPT_k) | ||
242 | unit = 2; | ||
243 | else if (option_mask32 & OPT_m) | ||
244 | unit = 2048; | ||
245 | |||
246 | if (option_mask32 & OPT_z) | 250 | if (option_mask32 & OPT_z) |
247 | if (p->rd_ops == c->rd_ops && p->wr_ops == c->wr_ops) | 251 | if (p->rd_ops == c->rd_ops && p->wr_ops == c->wr_ops) |
248 | return; | 252 | return; |
249 | 253 | ||
250 | printf("%-13s", c->dname); | 254 | printf("%-13s %8.2f %12.2f %12.2f %10llu %10llu \n", c->dname, |
251 | printf(" %8.2f %12.2f %12.2f %10llu %10llu \n", | ||
252 | percent_value(p->rd_ops + p->wr_ops , | 255 | percent_value(p->rd_ops + p->wr_ops , |
253 | /**/ c->rd_ops + c->wr_ops , itv), | 256 | /**/ c->rd_ops + c->wr_ops , itv), |
254 | percent_value(p->rd_sectors, c->rd_sectors, itv) / unit, | 257 | percent_value(p->rd_sectors, c->rd_sectors, itv) / G.unit.div, |
255 | percent_value(p->wr_sectors, c->wr_sectors, itv) / unit, | 258 | percent_value(p->wr_sectors, c->wr_sectors, itv) / G.unit.div, |
256 | (c->rd_sectors - p->rd_sectors) / unit, | 259 | (c->rd_sectors - p->rd_sectors) / G.unit.div, |
257 | (c->wr_sectors - p->wr_sectors) / unit); | 260 | (c->wr_sectors - p->wr_sectors) / G.unit.div); |
258 | } | 261 | } |
259 | 262 | ||
260 | static void cpu_report(const struct stats_cpu *last, | 263 | static void cpu_report(stats_cpu_pair_t *stats) |
261 | const struct stats_cpu *cur, | ||
262 | cputime_t itv) | ||
263 | { | 264 | { |
264 | /* Always print a header */ | 265 | /* Always print a header */ |
265 | puts("avg-cpu: %user %nice %system %iowait %steal %idle"); | 266 | puts("avg-cpu: %user %nice %system %iowait %steal %idle"); |
266 | 267 | ||
267 | /* Print current statistics */ | 268 | /* Print current statistics */ |
268 | print_stats_cpu_struct(last, cur, itv); | 269 | print_stats_cpu_struct(stats); |
269 | } | 270 | } |
270 | 271 | ||
271 | static void print_devstat_header(void) | 272 | static void print_devstat_header(void) |
272 | { | 273 | { |
273 | printf("Device: tps"); | 274 | printf("Device:%15s%6s%s/s%6s%s/s%6s%s%6s%s\n", "tps", |
274 | 275 | G.unit.str, "_read", G.unit.str, "_wrtn", | |
275 | if (option_mask32 & OPT_m) | 276 | G.unit.str, "_read", G.unit.str, "_wrtn"); |
276 | puts(" MB_read/s MB_wrtn/s MB_read MB_wrtn"); | ||
277 | else if (option_mask32 & OPT_k) | ||
278 | puts(" kB_read/s kB_wrtn/s kB_read kB_wrtn"); | ||
279 | else | ||
280 | puts(" Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn"); | ||
281 | } | 277 | } |
282 | 278 | ||
283 | /* | 279 | /* |
@@ -289,37 +285,6 @@ static int is_partition(const char *dev) | |||
289 | return ((dev[0] - 's') | (dev[1] - 'd') | (dev[2] - 'a')) == 0 && isdigit(dev[3]); | 285 | return ((dev[0] - 's') | (dev[1] - 'd') | (dev[2] - 'a')) == 0 && isdigit(dev[3]); |
290 | } | 286 | } |
291 | 287 | ||
292 | /* | ||
293 | * Return number of numbers on cmdline. | ||
294 | * Reasonable values are only 0 (no interval/count specified), | ||
295 | * 1 (interval specified) and 2 (both interval and count specified) | ||
296 | */ | ||
297 | static int numbers_on_cmdline(int argc, char *argv[]) | ||
298 | { | ||
299 | int sum = 0; | ||
300 | |||
301 | if (isdigit(argv[argc-1][0])) | ||
302 | sum++; | ||
303 | if (argc > 2 && isdigit(argv[argc-2][0])) | ||
304 | sum++; | ||
305 | |||
306 | return sum; | ||
307 | } | ||
308 | |||
309 | static int is_dev_in_dlist(const char *dev) | ||
310 | { | ||
311 | int i; | ||
312 | |||
313 | /* Go through the device list */ | ||
314 | for (i = 0; i < G.devlist_i; i++) | ||
315 | if (strcmp(G.dlist[i].dname, dev) == 0) | ||
316 | /* Found a match */ | ||
317 | return 1; | ||
318 | |||
319 | /* No match found */ | ||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | static void do_disk_statistics(cputime_t itv) | 288 | static void do_disk_statistics(cputime_t itv) |
324 | { | 289 | { |
325 | FILE *fp; | 290 | FILE *fp; |
@@ -356,7 +321,7 @@ static void do_disk_statistics(cputime_t itv) | |||
356 | break; | 321 | break; |
357 | } | 322 | } |
358 | 323 | ||
359 | if (!G.devlist_i && !is_partition(sd.dname)) { | 324 | if (!G.dev_list && !is_partition(sd.dname)) { |
360 | /* User didn't specify device */ | 325 | /* User didn't specify device */ |
361 | if (!G.show_all && !sd.rd_ops && !sd.wr_ops) { | 326 | if (!G.show_all && !sd.rd_ops && !sd.wr_ops) { |
362 | /* Don't print unused device */ | 327 | /* Don't print unused device */ |
@@ -367,7 +332,7 @@ static void do_disk_statistics(cputime_t itv) | |||
367 | i++; | 332 | i++; |
368 | } else { | 333 | } else { |
369 | /* Is device in device list? */ | 334 | /* Is device in device list? */ |
370 | if (is_dev_in_dlist(sd.dname)) { | 335 | if (llist_find_str(G.dev_list, sd.dname)) { |
371 | /* Print current statistics */ | 336 | /* Print current statistics */ |
372 | print_stats_dev_struct(&G.saved_stats_dev[i], &sd, itv); | 337 | print_stats_dev_struct(&G.saved_stats_dev[i], &sd, itv); |
373 | G.saved_stats_dev[i] = sd; | 338 | G.saved_stats_dev[i] = sd; |
@@ -389,28 +354,6 @@ static void dev_report(cputime_t itv) | |||
389 | do_disk_statistics(itv); | 354 | do_disk_statistics(itv); |
390 | } | 355 | } |
391 | 356 | ||
392 | static void save_to_devlist(const char *dname) | ||
393 | { | ||
394 | int i; | ||
395 | struct device_list *tmp = G.dlist; | ||
396 | |||
397 | if (strncmp(dname, "/dev/", 5) == 0) | ||
398 | /* We'll ignore prefix '/dev/' */ | ||
399 | dname += 5; | ||
400 | |||
401 | /* Go through the list */ | ||
402 | for (i = 0; i < G.devlist_i; i++, tmp++) | ||
403 | if (strcmp(tmp->dname, dname) == 0) | ||
404 | /* Already in the list */ | ||
405 | return; | ||
406 | |||
407 | /* Add device name to the list */ | ||
408 | strncpy(tmp->dname, dname, MAX_DEVICE_NAME - 1); | ||
409 | |||
410 | /* Update device list index */ | ||
411 | G.devlist_i++; | ||
412 | } | ||
413 | |||
414 | static unsigned get_number_of_devices(void) | 357 | static unsigned get_number_of_devices(void) |
415 | { | 358 | { |
416 | FILE *fp; | 359 | FILE *fp; |
@@ -440,18 +383,6 @@ static unsigned get_number_of_devices(void) | |||
440 | return n; | 383 | return n; |
441 | } | 384 | } |
442 | 385 | ||
443 | static int number_of_ALL_on_cmdline(char **argv) | ||
444 | { | ||
445 | int alls = 0; | ||
446 | |||
447 | /* Iterate over cmd line arguments, count "ALL" */ | ||
448 | while (*argv) | ||
449 | if (strcmp(*argv++, "ALL") == 0) | ||
450 | alls++; | ||
451 | |||
452 | return alls; | ||
453 | } | ||
454 | |||
455 | //usage:#define iostat_trivial_usage | 386 | //usage:#define iostat_trivial_usage |
456 | //usage: "[-c] [-d] [-t] [-z] [-k|-m] [ALL|BLOCKDEV...] [INTERVAL [COUNT]]" | 387 | //usage: "[-c] [-d] [-t] [-z] [-k|-m] [ALL|BLOCKDEV...] [INTERVAL [COUNT]]" |
457 | //usage:#define iostat_full_usage "\n\n" | 388 | //usage:#define iostat_full_usage "\n\n" |
@@ -465,19 +396,17 @@ static int number_of_ALL_on_cmdline(char **argv) | |||
465 | //usage: "\n -m Use Mb/s" | 396 | //usage: "\n -m Use Mb/s" |
466 | 397 | ||
467 | int iostat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 398 | int iostat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
468 | int iostat_main(int argc, char **argv) | 399 | int iostat_main(int argc UNUSED_PARAM, char **argv) |
469 | { | 400 | { |
470 | int opt, dev_num; | 401 | int opt, dev_num; |
471 | unsigned interval = 0; | 402 | unsigned interval; |
472 | int count; | 403 | int count; |
473 | cputime_t global_uptime[2] = { 0 }; | 404 | stats_cpu_t stats_data[2]; |
474 | cputime_t smp_uptime[2] = { 0 }; | 405 | smallint current_stats; |
475 | cputime_t itv; | ||
476 | struct stats_cpu stats_cur, stats_last; | ||
477 | 406 | ||
478 | INIT_G(); | 407 | INIT_G(); |
479 | 408 | ||
480 | memset(&stats_last, 0, sizeof(stats_last)); | 409 | memset(&stats_data, 0, sizeof(stats_data)); |
481 | 410 | ||
482 | /* Get number of clock ticks per sec */ | 411 | /* Get number of clock ticks per sec */ |
483 | G.clk_tck = get_user_hz(); | 412 | G.clk_tck = get_user_hz(); |
@@ -496,25 +425,24 @@ int iostat_main(int argc, char **argv) | |||
496 | opt |= OPT_c + OPT_d; | 425 | opt |= OPT_c + OPT_d; |
497 | 426 | ||
498 | argv += optind; | 427 | argv += optind; |
499 | argc -= optind; | ||
500 | |||
501 | dev_num = argc - numbers_on_cmdline(argc, argv); | ||
502 | /* We don't want to allocate space for 'ALL' */ | ||
503 | dev_num -= number_of_ALL_on_cmdline(argv); | ||
504 | if (dev_num > 0) | ||
505 | /* Make space for device list */ | ||
506 | G.dlist = xzalloc(sizeof(G.dlist[0]) * dev_num); | ||
507 | 428 | ||
508 | /* Store device names into device list */ | 429 | /* Store device names into device list */ |
430 | dev_num = 0; | ||
509 | while (*argv && !isdigit(*argv[0])) { | 431 | while (*argv && !isdigit(*argv[0])) { |
510 | if (strcmp(*argv, "ALL") != 0) | 432 | if (strcmp(*argv, "ALL") != 0) { |
511 | /* If not ALL, save device name */ | 433 | /* If not ALL, save device name */ |
512 | save_to_devlist(*argv); | 434 | char *dev_name = skip_dev_pfx(*argv); |
513 | else | 435 | if (!llist_find_str(G.dev_list, dev_name)) { |
436 | llist_add_to(&G.dev_list, dev_name); | ||
437 | dev_num++; | ||
438 | } | ||
439 | } else { | ||
514 | G.show_all = 1; | 440 | G.show_all = 1; |
441 | } | ||
515 | argv++; | 442 | argv++; |
516 | } | 443 | } |
517 | 444 | ||
445 | interval = 0; | ||
518 | count = 1; | 446 | count = 1; |
519 | if (*argv) { | 447 | if (*argv) { |
520 | /* Get interval */ | 448 | /* Get interval */ |
@@ -533,28 +461,46 @@ int iostat_main(int argc, char **argv) | |||
533 | ); | 461 | ); |
534 | } | 462 | } |
535 | 463 | ||
464 | if (opt & OPT_m) { | ||
465 | G.unit.str = " MB"; | ||
466 | G.unit.div = 2048; | ||
467 | } | ||
468 | |||
469 | if (opt & OPT_k) { | ||
470 | G.unit.str = " kB"; | ||
471 | G.unit.div = 2; | ||
472 | } | ||
473 | |||
474 | get_localtime(&G.tmtime); | ||
475 | |||
536 | /* Display header */ | 476 | /* Display header */ |
537 | print_header(); | 477 | print_header(); |
538 | 478 | ||
479 | current_stats = 0; | ||
539 | /* Main loop */ | 480 | /* Main loop */ |
540 | for (;;) { | 481 | for (;;) { |
482 | stats_cpu_pair_t stats; | ||
483 | |||
484 | stats.prev = &stats_data[current_stats ^ 1]; | ||
485 | stats.curr = &stats_data[current_stats]; | ||
486 | |||
541 | /* Fill the time structure */ | 487 | /* Fill the time structure */ |
542 | get_localtime(&G.tmtime); | 488 | get_localtime(&G.tmtime); |
543 | 489 | ||
544 | /* Fetch current CPU statistics */ | 490 | /* Fetch current CPU statistics */ |
545 | get_cpu_statistics(&stats_cur); | 491 | get_cpu_statistics(stats.curr); |
546 | |||
547 | /* Fetch current uptime */ | ||
548 | global_uptime[CURRENT] = get_uptime(&stats_cur); | ||
549 | 492 | ||
550 | /* Get interval */ | 493 | /* Get interval */ |
551 | itv = get_interval(global_uptime[LAST], global_uptime[CURRENT]); | 494 | stats.itv = get_interval( |
495 | stats.prev->vector[GLOBAL_UPTIME], | ||
496 | stats.curr->vector[GLOBAL_UPTIME] | ||
497 | ); | ||
552 | 498 | ||
553 | if (opt & OPT_t) | 499 | if (opt & OPT_t) |
554 | print_timestamp(); | 500 | print_timestamp(); |
555 | 501 | ||
556 | if (opt & OPT_c) { | 502 | if (opt & OPT_c) { |
557 | cpu_report(&stats_last, &stats_cur, itv); | 503 | cpu_report(&stats); |
558 | if (opt & OPT_d) | 504 | if (opt & OPT_d) |
559 | /* Separate outputs by a newline */ | 505 | /* Separate outputs by a newline */ |
560 | bb_putchar('\n'); | 506 | bb_putchar('\n'); |
@@ -562,32 +508,31 @@ int iostat_main(int argc, char **argv) | |||
562 | 508 | ||
563 | if (opt & OPT_d) { | 509 | if (opt & OPT_d) { |
564 | if (this_is_smp()) { | 510 | if (this_is_smp()) { |
565 | smp_uptime[CURRENT] = get_smp_uptime(); | 511 | stats.itv = get_interval( |
566 | itv = get_interval(smp_uptime[LAST], smp_uptime[CURRENT]); | 512 | stats.prev->vector[SMP_UPTIME], |
567 | smp_uptime[LAST] = smp_uptime[CURRENT]; | 513 | stats.curr->vector[SMP_UPTIME] |
514 | ); | ||
568 | } | 515 | } |
569 | dev_report(itv); | 516 | dev_report(stats.itv); |
570 | } | 517 | } |
571 | 518 | ||
519 | bb_putchar('\n'); | ||
520 | |||
572 | if (count > 0) { | 521 | if (count > 0) { |
573 | if (--count == 0) | 522 | if (--count == 0) |
574 | break; | 523 | break; |
575 | } | 524 | } |
576 | 525 | ||
577 | /* Backup current stats */ | 526 | /* Swap stats */ |
578 | global_uptime[LAST] = global_uptime[CURRENT]; | 527 | current_stats ^= 1; |
579 | stats_last = stats_cur; | ||
580 | 528 | ||
581 | bb_putchar('\n'); | ||
582 | sleep(interval); | 529 | sleep(interval); |
583 | } | 530 | } |
584 | 531 | ||
585 | bb_putchar('\n'); | ||
586 | |||
587 | if (ENABLE_FEATURE_CLEAN_UP) { | 532 | if (ENABLE_FEATURE_CLEAN_UP) { |
588 | free(&G); | 533 | llist_free(G.dev_list, NULL); |
589 | free(G.dlist); | ||
590 | free(G.saved_stats_dev); | 534 | free(G.saved_stats_dev); |
535 | free(&G); | ||
591 | } | 536 | } |
592 | 537 | ||
593 | return EXIT_SUCCESS; | 538 | return EXIT_SUCCESS; |
diff --git a/procps/mpstat.c b/procps/mpstat.c index d643c999f..da8f34dab 100644 --- a/procps/mpstat.c +++ b/procps/mpstat.c | |||
@@ -36,11 +36,10 @@ | |||
36 | * We are printing headers in the " IRQNAME/s" form, experimentally | 36 | * We are printing headers in the " IRQNAME/s" form, experimentally |
37 | * anything smaller than 10 chars looks ugly for /proc/softirqs stats. | 37 | * anything smaller than 10 chars looks ugly for /proc/softirqs stats. |
38 | */ | 38 | */ |
39 | #define INTRATE_SCRWIDTH 10 | 39 | #define INTRATE_SCRWIDTH 10 |
40 | #define INTRATE_SCRWIDTH_STR "10" | 40 | #define INTRATE_SCRWIDTH_STR "10" |
41 | 41 | ||
42 | /* System files */ | 42 | /* System files */ |
43 | #define SYSFS_DEVCPU "/sys/devices/system/cpu" | ||
44 | #define PROCFS_STAT "/proc/stat" | 43 | #define PROCFS_STAT "/proc/stat" |
45 | #define PROCFS_INTERRUPTS "/proc/interrupts" | 44 | #define PROCFS_INTERRUPTS "/proc/interrupts" |
46 | #define PROCFS_SOFTIRQS "/proc/softirqs" | 45 | #define PROCFS_SOFTIRQS "/proc/softirqs" |
diff --git a/procps/nmeter.c b/procps/nmeter.c index 48b56a399..999955982 100644 --- a/procps/nmeter.c +++ b/procps/nmeter.c | |||
@@ -24,20 +24,20 @@ | |||
24 | //usage: "\n -d MSEC Milliseconds between updates (default:1000)" | 24 | //usage: "\n -d MSEC Milliseconds between updates (default:1000)" |
25 | //usage: "\n" | 25 | //usage: "\n" |
26 | //usage: "\nFormat specifiers:" | 26 | //usage: "\nFormat specifiers:" |
27 | //usage: "\n %Nc or %[cN] Monitor CPU. N - bar size (default:10)" | 27 | //usage: "\n %Nc or %[cN] CPU. N - bar size (default:10)" |
28 | //usage: "\n (displays: S:system U:user N:niced D:iowait I:irq i:softirq)" | 28 | //usage: "\n (displays: S:system U:user N:niced D:iowait I:irq i:softirq)" |
29 | //usage: "\n %[niface] Monitor network interface 'iface'" | 29 | //usage: "\n %[nINTERFACE] Network INTERFACE" |
30 | //usage: "\n %m Monitor allocated memory" | 30 | //usage: "\n %m Allocated memory" |
31 | //usage: "\n %[mf] Monitor free memory" | 31 | //usage: "\n %[mf] Free memory" |
32 | //usage: "\n %[mt] Monitor total memory" | 32 | //usage: "\n %[mt] Total memory" |
33 | //usage: "\n %s Monitor allocated swap" | 33 | //usage: "\n %s Allocated swap" |
34 | //usage: "\n %f Monitor number of used file descriptors" | 34 | //usage: "\n %f Number of used file descriptors" |
35 | //usage: "\n %Ni Monitor total/specific IRQ rate" | 35 | //usage: "\n %Ni Total/specific IRQ rate" |
36 | //usage: "\n %x Monitor context switch rate" | 36 | //usage: "\n %x Context switch rate" |
37 | //usage: "\n %p Monitor forks" | 37 | //usage: "\n %p Forks" |
38 | //usage: "\n %[pn] Monitor # of processes" | 38 | //usage: "\n %[pn] # of processes" |
39 | //usage: "\n %b Monitor block io" | 39 | //usage: "\n %b Block io" |
40 | //usage: "\n %Nt Show time (with N decimal points)" | 40 | //usage: "\n %Nt Time (with N decimal points)" |
41 | //usage: "\n %r Print <cr> instead of <lf> at EOL" | 41 | //usage: "\n %r Print <cr> instead of <lf> at EOL" |
42 | 42 | ||
43 | //TODO: | 43 | //TODO: |
diff --git a/procps/powertop.c b/procps/powertop.c index bfe5a9568..008cdfca4 100644 --- a/procps/powertop.c +++ b/procps/powertop.c | |||
@@ -393,11 +393,9 @@ static NOINLINE int process_timer_stats(void) | |||
393 | char buf[128]; | 393 | char buf[128]; |
394 | char line[15 + 3 + 128]; | 394 | char line[15 + 3 + 128]; |
395 | int n; | 395 | int n; |
396 | ullong totalticks; | ||
397 | FILE *fp; | 396 | FILE *fp; |
398 | 397 | ||
399 | buf[0] = '\0'; | 398 | buf[0] = '\0'; |
400 | totalticks = 0; | ||
401 | 399 | ||
402 | n = 0; | 400 | n = 0; |
403 | fp = NULL; | 401 | fp = NULL; |
diff --git a/procps/top.c b/procps/top.c index ee6555188..011bbf183 100644 --- a/procps/top.c +++ b/procps/top.c | |||
@@ -92,9 +92,9 @@ enum { SORT_DEPTH = 3 }; | |||
92 | struct globals { | 92 | struct globals { |
93 | top_status_t *top; | 93 | top_status_t *top; |
94 | int ntop; | 94 | int ntop; |
95 | smallint inverted; | ||
95 | #if ENABLE_FEATURE_TOPMEM | 96 | #if ENABLE_FEATURE_TOPMEM |
96 | smallint sort_field; | 97 | smallint sort_field; |
97 | smallint inverted; | ||
98 | #endif | 98 | #endif |
99 | #if ENABLE_FEATURE_TOP_SMP_CPU | 99 | #if ENABLE_FEATURE_TOP_SMP_CPU |
100 | smallint smp_cpu_info; /* one/many cpu info lines? */ | 100 | smallint smp_cpu_info; /* one/many cpu info lines? */ |
@@ -194,9 +194,9 @@ static int mult_lvl_cmp(void* a, void* b) | |||
194 | for (i = 0; i < SORT_DEPTH; i++) { | 194 | for (i = 0; i < SORT_DEPTH; i++) { |
195 | cmp_val = (*sort_function[i])(a, b); | 195 | cmp_val = (*sort_function[i])(a, b); |
196 | if (cmp_val != 0) | 196 | if (cmp_val != 0) |
197 | return cmp_val; | 197 | break; |
198 | } | 198 | } |
199 | return 0; | 199 | return inverted ? -cmp_val : cmp_val; |
200 | } | 200 | } |
201 | 201 | ||
202 | static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif) | 202 | static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif) |
@@ -797,7 +797,7 @@ static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width) | |||
797 | 797 | ||
798 | display_topmem_header(scr_width, &lines_rem); | 798 | display_topmem_header(scr_width, &lines_rem); |
799 | strcpy(line_buf, HDR_STR " COMMAND"); | 799 | strcpy(line_buf, HDR_STR " COMMAND"); |
800 | line_buf[5 + sort_field * 6] = '*'; | 800 | line_buf[11 + sort_field * 6] = "^_"[inverted]; |
801 | printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf); | 801 | printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf); |
802 | lines_rem--; | 802 | lines_rem--; |
803 | 803 | ||
@@ -850,8 +850,114 @@ enum { | |||
850 | | PSSCAN_PID | 850 | | PSSCAN_PID |
851 | | PSSCAN_SMAPS | 851 | | PSSCAN_SMAPS |
852 | | PSSCAN_COMM, | 852 | | PSSCAN_COMM, |
853 | EXIT_MASK = (unsigned)-1, | ||
853 | }; | 854 | }; |
854 | 855 | ||
856 | #if ENABLE_FEATURE_USE_TERMIOS | ||
857 | static unsigned handle_input(unsigned scan_mask, unsigned interval) | ||
858 | { | ||
859 | unsigned char c; | ||
860 | struct pollfd pfd[1]; | ||
861 | |||
862 | pfd[0].fd = 0; | ||
863 | pfd[0].events = POLLIN; | ||
864 | |||
865 | while (1) { | ||
866 | if (safe_poll(pfd, 1, interval * 1000) <= 0) | ||
867 | return scan_mask; | ||
868 | interval = 0; | ||
869 | |||
870 | if (safe_read(STDIN_FILENO, &c, 1) != 1) { /* error/EOF? */ | ||
871 | option_mask32 |= OPT_EOF; | ||
872 | return scan_mask; | ||
873 | } | ||
874 | |||
875 | if (c == initial_settings.c_cc[VINTR]) | ||
876 | return EXIT_MASK; | ||
877 | if (c == initial_settings.c_cc[VEOF]) | ||
878 | return EXIT_MASK; | ||
879 | c |= 0x20; /* lowercase */ | ||
880 | if (c == 'q') | ||
881 | return EXIT_MASK; | ||
882 | |||
883 | if (c == 'n') { | ||
884 | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) | ||
885 | sort_function[0] = pid_sort; | ||
886 | continue; | ||
887 | } | ||
888 | if (c == 'm') { | ||
889 | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) | ||
890 | sort_function[0] = mem_sort; | ||
891 | # if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | ||
892 | sort_function[1] = pcpu_sort; | ||
893 | sort_function[2] = time_sort; | ||
894 | # endif | ||
895 | continue; | ||
896 | } | ||
897 | # if ENABLE_FEATURE_SHOW_THREADS | ||
898 | if (c == 'h' | ||
899 | IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK) | ||
900 | ) { | ||
901 | scan_mask ^= PSSCAN_TASKS; | ||
902 | continue; | ||
903 | } | ||
904 | # endif | ||
905 | # if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | ||
906 | if (c == 'p') { | ||
907 | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) | ||
908 | sort_function[0] = pcpu_sort; | ||
909 | sort_function[1] = mem_sort; | ||
910 | sort_function[2] = time_sort; | ||
911 | continue; | ||
912 | } | ||
913 | if (c == 't') { | ||
914 | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) | ||
915 | sort_function[0] = time_sort; | ||
916 | sort_function[1] = mem_sort; | ||
917 | sort_function[2] = pcpu_sort; | ||
918 | continue; | ||
919 | } | ||
920 | # if ENABLE_FEATURE_TOPMEM | ||
921 | if (c == 's') { | ||
922 | scan_mask = TOPMEM_MASK; | ||
923 | free(prev_hist); | ||
924 | prev_hist = NULL; | ||
925 | prev_hist_count = 0; | ||
926 | sort_field = (sort_field + 1) % NUM_SORT_FIELD; | ||
927 | continue; | ||
928 | } | ||
929 | # endif | ||
930 | if (c == 'r') { | ||
931 | inverted ^= 1; | ||
932 | continue; | ||
933 | } | ||
934 | # if ENABLE_FEATURE_TOP_SMP_CPU | ||
935 | /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ | ||
936 | if (c == 'c' || c == '1') { | ||
937 | /* User wants to toggle per cpu <> aggregate */ | ||
938 | if (smp_cpu_info) { | ||
939 | free(cpu_prev_jif); | ||
940 | free(cpu_jif); | ||
941 | cpu_jif = &cur_jif; | ||
942 | cpu_prev_jif = &prev_jif; | ||
943 | } else { | ||
944 | /* Prepare for xrealloc() */ | ||
945 | cpu_jif = cpu_prev_jif = NULL; | ||
946 | } | ||
947 | num_cpus = 0; | ||
948 | smp_cpu_info = !smp_cpu_info; | ||
949 | get_jiffy_counts(); | ||
950 | continue; | ||
951 | } | ||
952 | # endif | ||
953 | # endif | ||
954 | break; /* unknown key -> force refresh */ | ||
955 | } | ||
956 | |||
957 | return scan_mask; | ||
958 | } | ||
959 | #endif | ||
960 | |||
855 | //usage:#if ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_TOP_SMP_CPU | 961 | //usage:#if ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_TOP_SMP_CPU |
856 | //usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...) __VA_ARGS__ | 962 | //usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...) __VA_ARGS__ |
857 | //usage:#else | 963 | //usage:#else |
@@ -871,8 +977,9 @@ enum { | |||
871 | //usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/cpu") | 977 | //usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/cpu") |
872 | //usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/time") | 978 | //usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/time") |
873 | //usage: IF_FEATURE_TOPMEM( | 979 | //usage: IF_FEATURE_TOPMEM( |
874 | //usage: "\n"" S: show memory, R: reverse memory sort" | 980 | //usage: "\n"" S: show memory" |
875 | //usage: ) | 981 | //usage: ) |
982 | //usage: "\n"" R: reverse sort" | ||
876 | //usage: IF_SHOW_THREADS_OR_TOP_SMP( | 983 | //usage: IF_SHOW_THREADS_OR_TOP_SMP( |
877 | //usage: "\n"" " | 984 | //usage: "\n"" " |
878 | //usage: IF_FEATURE_SHOW_THREADS("H: toggle threads") | 985 | //usage: IF_FEATURE_SHOW_THREADS("H: toggle threads") |
@@ -880,23 +987,36 @@ enum { | |||
880 | //usage: IF_FEATURE_TOP_SMP_CPU("1: toggle SMP") | 987 | //usage: IF_FEATURE_TOP_SMP_CPU("1: toggle SMP") |
881 | //usage: ) | 988 | //usage: ) |
882 | //usage: "\n"" Q,^C: exit" | 989 | //usage: "\n"" Q,^C: exit" |
990 | //usage: "\n" | ||
991 | //usage: "\n""Options:" | ||
992 | //usage: "\n"" -b Batch mode" | ||
993 | //usage: "\n"" -n N Exit after N iterations" | ||
994 | //usage: "\n"" -d N Delay between updates" | ||
995 | //usage: IF_FEATURE_TOPMEM( | ||
996 | //usage: "\n"" -m Same as 's' key" | ||
997 | //usage: ) | ||
998 | |||
999 | /* Interactive testing: | ||
1000 | * echo sss | ./busybox top | ||
1001 | * - shows memory screen | ||
1002 | * echo sss | ./busybox top -bn1 >mem | ||
1003 | * - saves memory screen - the *whole* list, not first NROWS processes! | ||
1004 | * echo .m.s.s.s.s.s.s.q | ./busybox top -b >z | ||
1005 | * - saves several different screens, and exits | ||
1006 | * | ||
1007 | * TODO: -i STRING param as a better alternative? | ||
1008 | */ | ||
883 | 1009 | ||
884 | int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 1010 | int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
885 | int top_main(int argc UNUSED_PARAM, char **argv) | 1011 | int top_main(int argc UNUSED_PARAM, char **argv) |
886 | { | 1012 | { |
887 | int iterations; | 1013 | int iterations; |
888 | unsigned lines, col; | 1014 | unsigned lines, col; |
889 | int lines_rem; | ||
890 | unsigned interval; | 1015 | unsigned interval; |
891 | char *str_interval, *str_iterations; | 1016 | char *str_interval, *str_iterations; |
892 | unsigned scan_mask = TOP_MASK; | 1017 | unsigned scan_mask = TOP_MASK; |
893 | #if ENABLE_FEATURE_USE_TERMIOS | 1018 | #if ENABLE_FEATURE_USE_TERMIOS |
894 | struct termios new_settings; | 1019 | struct termios new_settings; |
895 | struct pollfd pfd[1]; | ||
896 | unsigned char c; | ||
897 | |||
898 | pfd[0].fd = 0; | ||
899 | pfd[0].events = POLLIN; | ||
900 | #endif | 1020 | #endif |
901 | 1021 | ||
902 | INIT_G(); | 1022 | INIT_G(); |
@@ -933,15 +1053,6 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
933 | 1053 | ||
934 | /* change to /proc */ | 1054 | /* change to /proc */ |
935 | xchdir("/proc"); | 1055 | xchdir("/proc"); |
936 | #if ENABLE_FEATURE_USE_TERMIOS | ||
937 | tcgetattr(0, (void *) &initial_settings); | ||
938 | memcpy(&new_settings, &initial_settings, sizeof(new_settings)); | ||
939 | /* unbuffered input, turn off echo */ | ||
940 | new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL); | ||
941 | |||
942 | bb_signals(BB_FATAL_SIGS, sig_catcher); | ||
943 | tcsetattr_stdin_TCSANOW(&new_settings); | ||
944 | #endif | ||
945 | 1056 | ||
946 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | 1057 | #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
947 | sort_function[0] = pcpu_sort; | 1058 | sort_function[0] = pcpu_sort; |
@@ -951,21 +1062,41 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
951 | sort_function[0] = mem_sort; | 1062 | sort_function[0] = mem_sort; |
952 | #endif | 1063 | #endif |
953 | 1064 | ||
954 | while (1) { | 1065 | #if ENABLE_FEATURE_USE_TERMIOS |
1066 | tcgetattr(0, (void *) &initial_settings); | ||
1067 | memcpy(&new_settings, &initial_settings, sizeof(new_settings)); | ||
1068 | if (!OPT_BATCH_MODE) { | ||
1069 | /* unbuffered input, turn off echo */ | ||
1070 | new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL); | ||
1071 | tcsetattr_stdin_TCSANOW(&new_settings); | ||
1072 | } | ||
1073 | |||
1074 | bb_signals(BB_FATAL_SIGS, sig_catcher); | ||
1075 | |||
1076 | /* Eat initial input, if any */ | ||
1077 | scan_mask = handle_input(scan_mask, 0); | ||
1078 | #endif | ||
1079 | |||
1080 | while (scan_mask != EXIT_MASK) { | ||
955 | procps_status_t *p = NULL; | 1081 | procps_status_t *p = NULL; |
956 | 1082 | ||
957 | lines = 24; /* default */ | 1083 | if (OPT_BATCH_MODE) { |
958 | col = 79; | 1084 | lines = INT_MAX; |
1085 | col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */ | ||
1086 | } else { | ||
1087 | lines = 24; /* default */ | ||
1088 | col = 79; | ||
959 | #if ENABLE_FEATURE_USE_TERMIOS | 1089 | #if ENABLE_FEATURE_USE_TERMIOS |
960 | /* We output to stdout, we need size of stdout (not stdin)! */ | 1090 | /* We output to stdout, we need size of stdout (not stdin)! */ |
961 | get_terminal_width_height(STDOUT_FILENO, &col, &lines); | 1091 | get_terminal_width_height(STDOUT_FILENO, &col, &lines); |
962 | if (lines < 5 || col < 10) { | 1092 | if (lines < 5 || col < 10) { |
963 | sleep(interval); | 1093 | sleep(interval); |
964 | continue; | 1094 | continue; |
965 | } | 1095 | } |
966 | #endif | 1096 | #endif |
967 | if (col > LINE_BUF_SIZE-2) /* +2 bytes for '\n', NUL, */ | 1097 | if (col > LINE_BUF_SIZE - 2) |
968 | col = LINE_BUF_SIZE-2; | 1098 | col = LINE_BUF_SIZE - 2; |
1099 | } | ||
969 | 1100 | ||
970 | /* read process IDs & status for all the processes */ | 1101 | /* read process IDs & status for all the processes */ |
971 | while ((p = procps_scan(p, scan_mask)) != NULL) { | 1102 | while ((p = procps_scan(p, scan_mask)) != NULL) { |
@@ -1033,15 +1164,11 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
1033 | qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); | 1164 | qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); |
1034 | } | 1165 | } |
1035 | #endif | 1166 | #endif |
1036 | lines_rem = lines; | ||
1037 | if (OPT_BATCH_MODE) { | ||
1038 | lines_rem = INT_MAX; | ||
1039 | } | ||
1040 | if (scan_mask != TOPMEM_MASK) | 1167 | if (scan_mask != TOPMEM_MASK) |
1041 | display_process_list(lines_rem, col); | 1168 | display_process_list(lines, col); |
1042 | #if ENABLE_FEATURE_TOPMEM | 1169 | #if ENABLE_FEATURE_TOPMEM |
1043 | else | 1170 | else |
1044 | display_topmem_process_list(lines_rem, col); | 1171 | display_topmem_process_list(lines, col); |
1045 | #endif | 1172 | #endif |
1046 | clearmems(); | 1173 | clearmems(); |
1047 | if (iterations >= 0 && !--iterations) | 1174 | if (iterations >= 0 && !--iterations) |
@@ -1049,84 +1176,13 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
1049 | #if !ENABLE_FEATURE_USE_TERMIOS | 1176 | #if !ENABLE_FEATURE_USE_TERMIOS |
1050 | sleep(interval); | 1177 | sleep(interval); |
1051 | #else | 1178 | #else |
1052 | if (option_mask32 & (OPT_b|OPT_EOF)) | 1179 | if (option_mask32 & OPT_EOF) |
1053 | /* batch mode, or EOF on stdin ("top </dev/null") */ | 1180 | /* EOF on stdin ("top </dev/null") */ |
1054 | sleep(interval); | 1181 | sleep(interval); |
1055 | else if (safe_poll(pfd, 1, interval * 1000) > 0) { | 1182 | else |
1056 | if (safe_read(STDIN_FILENO, &c, 1) != 1) { /* error/EOF? */ | 1183 | scan_mask = handle_input(scan_mask, interval); |
1057 | option_mask32 |= OPT_EOF; | ||
1058 | continue; | ||
1059 | } | ||
1060 | if (c == initial_settings.c_cc[VINTR]) | ||
1061 | break; | ||
1062 | c |= 0x20; /* lowercase */ | ||
1063 | if (c == 'q') | ||
1064 | break; | ||
1065 | if (c == 'n') { | ||
1066 | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) | ||
1067 | sort_function[0] = pid_sort; | ||
1068 | } | ||
1069 | if (c == 'm') { | ||
1070 | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) | ||
1071 | sort_function[0] = mem_sort; | ||
1072 | # if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | ||
1073 | sort_function[1] = pcpu_sort; | ||
1074 | sort_function[2] = time_sort; | ||
1075 | # endif | ||
1076 | } | ||
1077 | # if ENABLE_FEATURE_SHOW_THREADS | ||
1078 | if (c == 'h' | ||
1079 | IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK) | ||
1080 | ) { | ||
1081 | scan_mask ^= PSSCAN_TASKS; | ||
1082 | } | ||
1083 | # endif | ||
1084 | # if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | ||
1085 | if (c == 'p') { | ||
1086 | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) | ||
1087 | sort_function[0] = pcpu_sort; | ||
1088 | sort_function[1] = mem_sort; | ||
1089 | sort_function[2] = time_sort; | ||
1090 | } | ||
1091 | if (c == 't') { | ||
1092 | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) | ||
1093 | sort_function[0] = time_sort; | ||
1094 | sort_function[1] = mem_sort; | ||
1095 | sort_function[2] = pcpu_sort; | ||
1096 | } | ||
1097 | # if ENABLE_FEATURE_TOPMEM | ||
1098 | if (c == 's') { | ||
1099 | scan_mask = TOPMEM_MASK; | ||
1100 | free(prev_hist); | ||
1101 | prev_hist = NULL; | ||
1102 | prev_hist_count = 0; | ||
1103 | sort_field = (sort_field + 1) % NUM_SORT_FIELD; | ||
1104 | } | ||
1105 | if (c == 'r') | ||
1106 | inverted ^= 1; | ||
1107 | # endif | ||
1108 | # if ENABLE_FEATURE_TOP_SMP_CPU | ||
1109 | /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ | ||
1110 | if (c == 'c' || c == '1') { | ||
1111 | /* User wants to toggle per cpu <> aggregate */ | ||
1112 | if (smp_cpu_info) { | ||
1113 | free(cpu_prev_jif); | ||
1114 | free(cpu_jif); | ||
1115 | cpu_jif = &cur_jif; | ||
1116 | cpu_prev_jif = &prev_jif; | ||
1117 | } else { | ||
1118 | /* Prepare for xrealloc() */ | ||
1119 | cpu_jif = cpu_prev_jif = NULL; | ||
1120 | } | ||
1121 | num_cpus = 0; | ||
1122 | smp_cpu_info = !smp_cpu_info; | ||
1123 | get_jiffy_counts(); | ||
1124 | } | ||
1125 | # endif | ||
1126 | # endif | ||
1127 | } | ||
1128 | #endif /* FEATURE_USE_TERMIOS */ | 1184 | #endif /* FEATURE_USE_TERMIOS */ |
1129 | } /* end of "while (1)" */ | 1185 | } /* end of "while (not Q)" */ |
1130 | 1186 | ||
1131 | bb_putchar('\n'); | 1187 | bb_putchar('\n'); |
1132 | #if ENABLE_FEATURE_USE_TERMIOS | 1188 | #if ENABLE_FEATURE_USE_TERMIOS |
diff --git a/runit/sv.c b/runit/sv.c index 322688a36..5b01c875c 100644 --- a/runit/sv.c +++ b/runit/sv.c | |||
@@ -437,7 +437,6 @@ static int control(const char *a) | |||
437 | int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 437 | int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
438 | int sv_main(int argc UNUSED_PARAM, char **argv) | 438 | int sv_main(int argc UNUSED_PARAM, char **argv) |
439 | { | 439 | { |
440 | unsigned opt; | ||
441 | char *x; | 440 | char *x; |
442 | char *action; | 441 | char *action; |
443 | const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR; | 442 | const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR; |
@@ -458,7 +457,7 @@ int sv_main(int argc UNUSED_PARAM, char **argv) | |||
458 | if (x) waitsec = xatou(x); | 457 | if (x) waitsec = xatou(x); |
459 | 458 | ||
460 | opt_complementary = "w+:vv"; /* -w N, -v is a counter */ | 459 | opt_complementary = "w+:vv"; /* -w N, -v is a counter */ |
461 | opt = getopt32(argv, "w:v", &waitsec, &verbose); | 460 | getopt32(argv, "w:v", &waitsec, &verbose); |
462 | argv += optind; | 461 | argv += optind; |
463 | action = *argv++; | 462 | action = *argv++; |
464 | if (!action || !*argv) bb_show_usage(); | 463 | if (!action || !*argv) bb_show_usage(); |
diff --git a/scripts/defconfig.tig b/scripts/defconfig.tig index ab9dc4728..e19217f08 100644 --- a/scripts/defconfig.tig +++ b/scripts/defconfig.tig | |||
@@ -1,7 +1,7 @@ | |||
1 | # | 1 | # |
2 | # Automatically generated make config: don't edit | 2 | # Automatically generated make config: don't edit |
3 | # Busybox version: 1.19.0.git | 3 | # Busybox version: 1.19.0.git |
4 | # Thu Mar 22 14:00:24 2012 | 4 | # Thu Mar 22 15:00:34 2012 |
5 | # | 5 | # |
6 | CONFIG_HAVE_DOT_CONFIG=y | 6 | CONFIG_HAVE_DOT_CONFIG=y |
7 | # CONFIG_PLATFORM_POSIX is not set | 7 | # CONFIG_PLATFORM_POSIX is not set |
@@ -615,11 +615,13 @@ CONFIG_HD=y | |||
615 | # CONFIG_CONSPY is not set | 615 | # CONFIG_CONSPY is not set |
616 | # CONFIG_NANDWRITE is not set | 616 | # CONFIG_NANDWRITE is not set |
617 | # CONFIG_NANDDUMP is not set | 617 | # CONFIG_NANDDUMP is not set |
618 | # CONFIG_SETSERIAL is not set | ||
618 | # CONFIG_UBIATTACH is not set | 619 | # CONFIG_UBIATTACH is not set |
619 | # CONFIG_UBIDETACH is not set | 620 | # CONFIG_UBIDETACH is not set |
620 | # CONFIG_UBIMKVOL is not set | 621 | # CONFIG_UBIMKVOL is not set |
621 | # CONFIG_UBIRMVOL is not set | 622 | # CONFIG_UBIRMVOL is not set |
622 | # CONFIG_UBIRSVOL is not set | 623 | # CONFIG_UBIRSVOL is not set |
624 | # CONFIG_UBIUPDATEVOL is not set | ||
623 | # CONFIG_ADJTIMEX is not set | 625 | # CONFIG_ADJTIMEX is not set |
624 | CONFIG_BBCONFIG=y | 626 | CONFIG_BBCONFIG=y |
625 | CONFIG_FEATURE_COMPRESS_BBCONFIG=y | 627 | CONFIG_FEATURE_COMPRESS_BBCONFIG=y |
@@ -826,6 +828,7 @@ CONFIG_DHCPD_LEASES_FILE="" | |||
826 | # CONFIG_FEATURE_UDHCP_PORT is not set | 828 | # CONFIG_FEATURE_UDHCP_PORT is not set |
827 | CONFIG_UDHCP_DEBUG=0 | 829 | CONFIG_UDHCP_DEBUG=0 |
828 | # CONFIG_FEATURE_UDHCP_RFC3397 is not set | 830 | # CONFIG_FEATURE_UDHCP_RFC3397 is not set |
831 | # CONFIG_FEATURE_UDHCP_8021Q is not set | ||
829 | CONFIG_UDHCPC_DEFAULT_SCRIPT="" | 832 | CONFIG_UDHCPC_DEFAULT_SCRIPT="" |
830 | CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0 | 833 | CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0 |
831 | CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="" | 834 | CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="" |
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 6e097889f..ea2446a89 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c | |||
@@ -173,7 +173,7 @@ static void conf_askvalue(struct symbol *sym, const char *def) | |||
173 | int conf_string(struct menu *menu) | 173 | int conf_string(struct menu *menu) |
174 | { | 174 | { |
175 | struct symbol *sym = menu->sym; | 175 | struct symbol *sym = menu->sym; |
176 | const char *def, *help; | 176 | const char *def; |
177 | 177 | ||
178 | while (1) { | 178 | while (1) { |
179 | printf("%*s%s ", indent - 1, "", menu->prompt->text); | 179 | printf("%*s%s ", indent - 1, "", menu->prompt->text); |
@@ -188,10 +188,7 @@ int conf_string(struct menu *menu) | |||
188 | case '?': | 188 | case '?': |
189 | /* print help */ | 189 | /* print help */ |
190 | if (line[1] == '\n') { | 190 | if (line[1] == '\n') { |
191 | help = nohelp_text; | 191 | printf("\n%s\n", menu->sym->help ? menu->sym->help : nohelp_text); |
192 | if (menu->sym->help) | ||
193 | help = menu->sym->help; | ||
194 | printf("\n%s\n", menu->sym->help); | ||
195 | def = NULL; | 192 | def = NULL; |
196 | break; | 193 | break; |
197 | } | 194 | } |
@@ -207,7 +204,6 @@ int conf_string(struct menu *menu) | |||
207 | static int conf_sym(struct menu *menu) | 204 | static int conf_sym(struct menu *menu) |
208 | { | 205 | { |
209 | struct symbol *sym = menu->sym; | 206 | struct symbol *sym = menu->sym; |
210 | int type; | ||
211 | tristate oldval, newval; | 207 | tristate oldval, newval; |
212 | const char *help; | 208 | const char *help; |
213 | 209 | ||
@@ -215,7 +211,6 @@ static int conf_sym(struct menu *menu) | |||
215 | printf("%*s%s ", indent - 1, "", menu->prompt->text); | 211 | printf("%*s%s ", indent - 1, "", menu->prompt->text); |
216 | if (sym->name) | 212 | if (sym->name) |
217 | printf("(%s) ", sym->name); | 213 | printf("(%s) ", sym->name); |
218 | type = sym_get_type(sym); | ||
219 | putchar('['); | 214 | putchar('['); |
220 | oldval = sym_get_tristate_value(sym); | 215 | oldval = sym_get_tristate_value(sym); |
221 | switch (oldval) { | 216 | switch (oldval) { |
@@ -282,11 +277,9 @@ static int conf_choice(struct menu *menu) | |||
282 | { | 277 | { |
283 | struct symbol *sym, *def_sym; | 278 | struct symbol *sym, *def_sym; |
284 | struct menu *child; | 279 | struct menu *child; |
285 | int type; | ||
286 | bool is_new; | 280 | bool is_new; |
287 | 281 | ||
288 | sym = menu->sym; | 282 | sym = menu->sym; |
289 | type = sym_get_type(sym); | ||
290 | is_new = !sym_has_value(sym); | 283 | is_new = !sym_has_value(sym); |
291 | if (sym_is_changable(sym)) { | 284 | if (sym_is_changable(sym)) { |
292 | conf_sym(menu); | 285 | conf_sym(menu); |
diff --git a/scripts/kconfig/lxdialog/textbox.c b/scripts/kconfig/lxdialog/textbox.c index 77848bb8e..de4ae41d7 100644 --- a/scripts/kconfig/lxdialog/textbox.c +++ b/scripts/kconfig/lxdialog/textbox.c | |||
@@ -38,11 +38,8 @@ int dialog_textbox(const char *title, const char *file, int height, int width) | |||
38 | { | 38 | { |
39 | int i, x, y, cur_x, cur_y, fpos, key = 0; | 39 | int i, x, y, cur_x, cur_y, fpos, key = 0; |
40 | int passed_end; | 40 | int passed_end; |
41 | char search_term[MAX_LEN + 1]; | ||
42 | WINDOW *dialog, *text; | 41 | WINDOW *dialog, *text; |
43 | 42 | ||
44 | search_term[0] = '\0'; /* no search term entered yet */ | ||
45 | |||
46 | /* Open input file for reading */ | 43 | /* Open input file for reading */ |
47 | if ((fd = open(file, O_RDONLY)) == -1) { | 44 | if ((fd = open(file, O_RDONLY)) == -1) { |
48 | endwin(); | 45 | endwin(); |
@@ -437,7 +434,6 @@ static void print_page(WINDOW * win, int height, int width) | |||
437 | */ | 434 | */ |
438 | static void print_line(WINDOW * win, int row, int width) | 435 | static void print_line(WINDOW * win, int row, int width) |
439 | { | 436 | { |
440 | int y, x; | ||
441 | char *line; | 437 | char *line; |
442 | 438 | ||
443 | line = get_line(); | 439 | line = get_line(); |
@@ -446,11 +442,13 @@ static void print_line(WINDOW * win, int row, int width) | |||
446 | waddch(win, ' '); | 442 | waddch(win, ' '); |
447 | waddnstr(win, line, MIN(strlen(line), width - 2)); | 443 | waddnstr(win, line, MIN(strlen(line), width - 2)); |
448 | 444 | ||
449 | getyx(win, y, x); | ||
450 | /* Clear 'residue' of previous line */ | 445 | /* Clear 'residue' of previous line */ |
451 | #if OLD_NCURSES | 446 | #if OLD_NCURSES |
452 | { | 447 | { |
453 | int i; | 448 | int i; |
449 | int y, x; | ||
450 | |||
451 | getyx(win, y, x); | ||
454 | for (i = 0; i < width - x; i++) | 452 | for (i = 0; i < width - x; i++) |
455 | waddch(win, ' '); | 453 | waddch(win, ' '); |
456 | } | 454 | } |
diff --git a/scripts/trylink b/scripts/trylink index 5994a757b..a8b0b2e03 100755 --- a/scripts/trylink +++ b/scripts/trylink | |||
@@ -255,6 +255,7 @@ if test "$CONFIG_FEATURE_SHARED_BUSYBOX" = y; then | |||
255 | $GC_SECTIONS \ | 255 | $GC_SECTIONS \ |
256 | $START_GROUP $O_FILES $END_GROUP \ | 256 | $START_GROUP $O_FILES $END_GROUP \ |
257 | -L"$sharedlib_dir" -lbusybox \ | 257 | -L"$sharedlib_dir" -lbusybox \ |
258 | $l_list \ | ||
258 | $INFO_OPTS \ | 259 | $INFO_OPTS \ |
259 | || { | 260 | || { |
260 | echo "Linking $EXE failed" | 261 | echo "Linking $EXE failed" |
diff --git a/selinux/chcon.c b/selinux/chcon.c index 8644502b5..88d0cfec6 100644 --- a/selinux/chcon.c +++ b/selinux/chcon.c | |||
@@ -40,7 +40,6 @@ | |||
40 | //usage: "\n -R Recurse" | 40 | //usage: "\n -R Recurse" |
41 | //usage: ) | 41 | //usage: ) |
42 | 42 | ||
43 | #include <getopt.h> | ||
44 | #include <selinux/context.h> | 43 | #include <selinux/context.h> |
45 | 44 | ||
46 | #include "libbb.h" | 45 | #include "libbb.h" |
diff --git a/selinux/runcon.c b/selinux/runcon.c index f0b21269f..3183a2274 100644 --- a/selinux/runcon.c +++ b/selinux/runcon.c | |||
@@ -50,7 +50,6 @@ | |||
50 | //usage: "\n -l RNG Levelrange" | 50 | //usage: "\n -l RNG Levelrange" |
51 | //usage: ) | 51 | //usage: ) |
52 | 52 | ||
53 | #include <getopt.h> | ||
54 | #include <selinux/context.h> | 53 | #include <selinux/context.h> |
55 | #include <selinux/flask.h> | 54 | #include <selinux/flask.h> |
56 | 55 | ||
diff --git a/shell/ash.c b/shell/ash.c index eaaa71967..a809bf181 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -6276,7 +6276,7 @@ expbackq(union node *cmd, int quoted, int quotes) | |||
6276 | read: | 6276 | read: |
6277 | if (in.fd < 0) | 6277 | if (in.fd < 0) |
6278 | break; | 6278 | break; |
6279 | i = nonblock_safe_read(in.fd, buf, sizeof(buf)); | 6279 | i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1); |
6280 | TRACE(("expbackq: read returns %d\n", i)); | 6280 | TRACE(("expbackq: read returns %d\n", i)); |
6281 | if (i <= 0) | 6281 | if (i <= 0) |
6282 | break; | 6282 | break; |
@@ -10120,7 +10120,7 @@ preadfd(void) | |||
10120 | #if ENABLE_FEATURE_EDITING | 10120 | #if ENABLE_FEATURE_EDITING |
10121 | retry: | 10121 | retry: |
10122 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) | 10122 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) |
10123 | nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); | 10123 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1); |
10124 | else { | 10124 | else { |
10125 | int timeout = -1; | 10125 | int timeout = -1; |
10126 | # if ENABLE_ASH_IDLE_TIMEOUT | 10126 | # if ENABLE_ASH_IDLE_TIMEOUT |
@@ -10166,10 +10166,10 @@ preadfd(void) | |||
10166 | } | 10166 | } |
10167 | } | 10167 | } |
10168 | #else | 10168 | #else |
10169 | nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); | 10169 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1); |
10170 | #endif | 10170 | #endif |
10171 | 10171 | ||
10172 | #if 0 /* disabled: nonblock_safe_read() handles this problem */ | 10172 | #if 0 /* disabled: nonblock_immune_read() handles this problem */ |
10173 | if (nr < 0) { | 10173 | if (nr < 0) { |
10174 | if (parsefile->fd == 0 && errno == EWOULDBLOCK) { | 10174 | if (parsefile->fd == 0 && errno == EWOULDBLOCK) { |
10175 | int flags = fcntl(0, F_GETFL); | 10175 | int flags = fcntl(0, F_GETFL); |
diff --git a/shell/hush.c b/shell/hush.c index d3e957c2f..1082738a2 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -106,6 +106,10 @@ | |||
106 | # define PIPE_BUF 4096 /* amount of buffering in a pipe */ | 106 | # define PIPE_BUF 4096 /* amount of buffering in a pipe */ |
107 | #endif | 107 | #endif |
108 | 108 | ||
109 | /* Not every libc has sighandler_t. Fix it */ | ||
110 | typedef void (*hush_sighandler_t)(int); | ||
111 | #define sighandler_t hush_sighandler_t | ||
112 | |||
109 | //config:config HUSH | 113 | //config:config HUSH |
110 | //config: bool "hush" | 114 | //config: bool "hush" |
111 | //config: default y | 115 | //config: default y |
@@ -256,7 +260,7 @@ | |||
256 | * therefore we don't show them either. | 260 | * therefore we don't show them either. |
257 | */ | 261 | */ |
258 | //usage:#define hush_trivial_usage | 262 | //usage:#define hush_trivial_usage |
259 | //usage: "[-nx] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" | 263 | //usage: "[-nxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" |
260 | //usage:#define hush_full_usage "\n\n" | 264 | //usage:#define hush_full_usage "\n\n" |
261 | //usage: "Unix shell interpreter" | 265 | //usage: "Unix shell interpreter" |
262 | 266 | ||
@@ -445,6 +449,15 @@ enum { | |||
445 | /* Used for initialization: o_string foo = NULL_O_STRING; */ | 449 | /* Used for initialization: o_string foo = NULL_O_STRING; */ |
446 | #define NULL_O_STRING { NULL } | 450 | #define NULL_O_STRING { NULL } |
447 | 451 | ||
452 | #ifndef debug_printf_parse | ||
453 | static const char *const assignment_flag[] = { | ||
454 | "MAYBE_ASSIGNMENT", | ||
455 | "DEFINITELY_ASSIGNMENT", | ||
456 | "NOT_ASSIGNMENT", | ||
457 | "WORD_IS_KEYWORD", | ||
458 | }; | ||
459 | #endif | ||
460 | |||
448 | typedef struct in_str { | 461 | typedef struct in_str { |
449 | const char *p; | 462 | const char *p; |
450 | /* eof_flag=1: last char in ->p is really an EOF */ | 463 | /* eof_flag=1: last char in ->p is really an EOF */ |
@@ -764,7 +777,6 @@ struct globals { | |||
764 | smalluint last_exitcode; | 777 | smalluint last_exitcode; |
765 | /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ | 778 | /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ |
766 | smalluint global_args_malloced; | 779 | smalluint global_args_malloced; |
767 | smalluint inherited_set_is_saved; | ||
768 | /* how many non-NULL argv's we have. NB: $# + 1 */ | 780 | /* how many non-NULL argv's we have. NB: $# + 1 */ |
769 | int global_argc; | 781 | int global_argc; |
770 | char **global_argv; | 782 | char **global_argv; |
@@ -792,15 +804,27 @@ struct globals { | |||
792 | unsigned handled_SIGCHLD; | 804 | unsigned handled_SIGCHLD; |
793 | smallint we_have_children; | 805 | smallint we_have_children; |
794 | #endif | 806 | #endif |
795 | /* which signals have non-DFL handler (even with no traps set)? */ | 807 | /* Which signals have non-DFL handler (even with no traps set)? |
796 | unsigned non_DFL_mask; | 808 | * Set at the start to: |
809 | * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) | ||
810 | * SPECIAL_INTERACTIVE_SIGS are cleared after fork. | ||
811 | * The rest is cleared right before execv syscalls. | ||
812 | * Other than these two times, never modified. | ||
813 | */ | ||
814 | unsigned special_sig_mask; | ||
815 | #if ENABLE_HUSH_JOB | ||
816 | unsigned fatal_sig_mask; | ||
817 | # define G_fatal_sig_mask G.fatal_sig_mask | ||
818 | #else | ||
819 | # define G_fatal_sig_mask 0 | ||
820 | #endif | ||
797 | char **traps; /* char *traps[NSIG] */ | 821 | char **traps; /* char *traps[NSIG] */ |
798 | sigset_t blocked_set; | 822 | sigset_t pending_set; |
799 | sigset_t inherited_set; | ||
800 | #if HUSH_DEBUG | 823 | #if HUSH_DEBUG |
801 | unsigned long memleak_value; | 824 | unsigned long memleak_value; |
802 | int debug_indent; | 825 | int debug_indent; |
803 | #endif | 826 | #endif |
827 | struct sigaction sa; | ||
804 | char user_input_buf[ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 2]; | 828 | char user_input_buf[ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 2]; |
805 | }; | 829 | }; |
806 | #define G (*ptr_to_globals) | 830 | #define G (*ptr_to_globals) |
@@ -809,6 +833,9 @@ struct globals { | |||
809 | * is global, thus "G." prefix is a useful hint */ | 833 | * is global, thus "G." prefix is a useful hint */ |
810 | #define INIT_G() do { \ | 834 | #define INIT_G() do { \ |
811 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ | 835 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ |
836 | /* memset(&G.sa, 0, sizeof(G.sa)); */ \ | ||
837 | sigfillset(&G.sa.sa_mask); \ | ||
838 | G.sa.sa_flags = SA_RESTART; \ | ||
812 | } while (0) | 839 | } while (0) |
813 | 840 | ||
814 | 841 | ||
@@ -1319,12 +1346,14 @@ static void restore_G_args(save_arg_t *sv, char **argv) | |||
1319 | * "echo $$; sleep 5 & wait; ls -l" + "kill -INT <pid>" | 1346 | * "echo $$; sleep 5 & wait; ls -l" + "kill -INT <pid>" |
1320 | * Example 3: this does not wait 5 sec, but executes ls: | 1347 | * Example 3: this does not wait 5 sec, but executes ls: |
1321 | * "sleep 5; ls -l" + press ^C | 1348 | * "sleep 5; ls -l" + press ^C |
1349 | * Example 4: this does not wait and does not execute ls: | ||
1350 | * "sleep 5 & wait; ls -l" + press ^C | ||
1322 | * | 1351 | * |
1323 | * (What happens to signals which are IGN on shell start?) | 1352 | * (What happens to signals which are IGN on shell start?) |
1324 | * (What happens with signal mask on shell start?) | 1353 | * (What happens with signal mask on shell start?) |
1325 | * | 1354 | * |
1326 | * Implementation in hush | 1355 | * Old implementation |
1327 | * ====================== | 1356 | * ================== |
1328 | * We use in-kernel pending signal mask to determine which signals were sent. | 1357 | * We use in-kernel pending signal mask to determine which signals were sent. |
1329 | * We block all signals which we don't want to take action immediately, | 1358 | * We block all signals which we don't want to take action immediately, |
1330 | * i.e. we block all signals which need to have special handling as described | 1359 | * i.e. we block all signals which need to have special handling as described |
@@ -1332,11 +1361,11 @@ static void restore_G_args(save_arg_t *sv, char **argv) | |||
1332 | * After each pipe execution, we extract any pending signals via sigtimedwait() | 1361 | * After each pipe execution, we extract any pending signals via sigtimedwait() |
1333 | * and act on them. | 1362 | * and act on them. |
1334 | * | 1363 | * |
1335 | * unsigned non_DFL_mask: a mask of such "special" signals | 1364 | * unsigned special_sig_mask: a mask of such "special" signals |
1336 | * sigset_t blocked_set: current blocked signal set | 1365 | * sigset_t blocked_set: current blocked signal set |
1337 | * | 1366 | * |
1338 | * "trap - SIGxxx": | 1367 | * "trap - SIGxxx": |
1339 | * clear bit in blocked_set unless it is also in non_DFL_mask | 1368 | * clear bit in blocked_set unless it is also in special_sig_mask |
1340 | * "trap 'cmd' SIGxxx": | 1369 | * "trap 'cmd' SIGxxx": |
1341 | * set bit in blocked_set (even if 'cmd' is '') | 1370 | * set bit in blocked_set (even if 'cmd' is '') |
1342 | * after [v]fork, if we plan to be a shell: | 1371 | * after [v]fork, if we plan to be a shell: |
@@ -1355,6 +1384,49 @@ static void restore_G_args(save_arg_t *sv, char **argv) | |||
1355 | * Standard says "When a subshell is entered, traps that are not being ignored | 1384 | * Standard says "When a subshell is entered, traps that are not being ignored |
1356 | * are set to the default actions". bash interprets it so that traps which | 1385 | * are set to the default actions". bash interprets it so that traps which |
1357 | * are set to '' (ignore) are NOT reset to defaults. We do the same. | 1386 | * are set to '' (ignore) are NOT reset to defaults. We do the same. |
1387 | * | ||
1388 | * Problem: the above approach makes it unwieldy to catch signals while | ||
1389 | * we are in read builtin, of while we read commands from stdin: | ||
1390 | * masked signals are not visible! | ||
1391 | * | ||
1392 | * New implementation | ||
1393 | * ================== | ||
1394 | * We record each signal we are interested in by installing signal handler | ||
1395 | * for them - a bit like emulating kernel pending signal mask in userspace. | ||
1396 | * We are interested in: signals which need to have special handling | ||
1397 | * as described above, and all signals which have traps set. | ||
1398 | * Signals are rocorded in pending_set. | ||
1399 | * After each pipe execution, we extract any pending signals | ||
1400 | * and act on them. | ||
1401 | * | ||
1402 | * unsigned special_sig_mask: a mask of shell-special signals. | ||
1403 | * unsigned fatal_sig_mask: a mask of signals on which we restore tty pgrp. | ||
1404 | * char *traps[sig] if trap for sig is set (even if it's ''). | ||
1405 | * sigset_t pending_set: set of sigs we received. | ||
1406 | * | ||
1407 | * "trap - SIGxxx": | ||
1408 | * if sig is in special_sig_mask, set handler back to: | ||
1409 | * record_pending_signo, or to IGN if it's a tty stop signal | ||
1410 | * if sig is in fatal_sig_mask, set handler back to sigexit. | ||
1411 | * else: set handler back to SIG_DFL | ||
1412 | * "trap 'cmd' SIGxxx": | ||
1413 | * set handler to record_pending_signo. | ||
1414 | * "trap '' SIGxxx": | ||
1415 | * set handler to SIG_IGN. | ||
1416 | * after [v]fork, if we plan to be a shell: | ||
1417 | * set signals with special interactive handling to SIG_DFL | ||
1418 | * (because child shell is not interactive), | ||
1419 | * unset all traps except '' (note: regardless of child shell's type - {}, (), etc) | ||
1420 | * after [v]fork, if we plan to exec: | ||
1421 | * POSIX says fork clears pending signal mask in child - no need to clear it. | ||
1422 | * | ||
1423 | * To make wait builtin interruptible, we handle SIGCHLD as special signal, | ||
1424 | * otherwise (if we leave it SIG_DFL) sigsuspend in wait builtin will not wake up on it. | ||
1425 | * | ||
1426 | * Note (compat): | ||
1427 | * Standard says "When a subshell is entered, traps that are not being ignored | ||
1428 | * are set to the default actions". bash interprets it so that traps which | ||
1429 | * are set to '' (ignore) are NOT reset to defaults. We do the same. | ||
1358 | */ | 1430 | */ |
1359 | enum { | 1431 | enum { |
1360 | SPECIAL_INTERACTIVE_SIGS = 0 | 1432 | SPECIAL_INTERACTIVE_SIGS = 0 |
@@ -1362,21 +1434,43 @@ enum { | |||
1362 | | (1 << SIGINT) | 1434 | | (1 << SIGINT) |
1363 | | (1 << SIGHUP) | 1435 | | (1 << SIGHUP) |
1364 | , | 1436 | , |
1365 | SPECIAL_JOB_SIGS = 0 | 1437 | SPECIAL_JOBSTOP_SIGS = 0 |
1366 | #if ENABLE_HUSH_JOB | 1438 | #if ENABLE_HUSH_JOB |
1367 | | (1 << SIGTTIN) | 1439 | | (1 << SIGTTIN) |
1368 | | (1 << SIGTTOU) | 1440 | | (1 << SIGTTOU) |
1369 | | (1 << SIGTSTP) | 1441 | | (1 << SIGTSTP) |
1370 | #endif | 1442 | #endif |
1443 | , | ||
1371 | }; | 1444 | }; |
1372 | 1445 | ||
1373 | #if ENABLE_HUSH_FAST | 1446 | static void record_pending_signo(int sig) |
1374 | static void SIGCHLD_handler(int sig UNUSED_PARAM) | ||
1375 | { | 1447 | { |
1376 | G.count_SIGCHLD++; | 1448 | sigaddset(&G.pending_set, sig); |
1449 | #if ENABLE_HUSH_FAST | ||
1450 | if (sig == SIGCHLD) { | ||
1451 | G.count_SIGCHLD++; | ||
1377 | //bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); | 1452 | //bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); |
1378 | } | 1453 | } |
1379 | #endif | 1454 | #endif |
1455 | } | ||
1456 | |||
1457 | static sighandler_t install_sighandler(int sig, sighandler_t handler) | ||
1458 | { | ||
1459 | struct sigaction old_sa; | ||
1460 | |||
1461 | /* We could use signal() to install handlers... almost: | ||
1462 | * except that we need to mask ALL signals while handlers run. | ||
1463 | * I saw signal nesting in strace, race window isn't small. | ||
1464 | * SA_RESTART is also needed, but in Linux, signal() | ||
1465 | * sets SA_RESTART too. | ||
1466 | */ | ||
1467 | /* memset(&G.sa, 0, sizeof(G.sa)); - already done */ | ||
1468 | /* sigfillset(&G.sa.sa_mask); - already done */ | ||
1469 | /* G.sa.sa_flags = SA_RESTART; - already done */ | ||
1470 | G.sa.sa_handler = handler; | ||
1471 | sigaction(sig, &G.sa, &old_sa); | ||
1472 | return old_sa.sa_handler; | ||
1473 | } | ||
1380 | 1474 | ||
1381 | #if ENABLE_HUSH_JOB | 1475 | #if ENABLE_HUSH_JOB |
1382 | 1476 | ||
@@ -1393,13 +1487,15 @@ static void SIGCHLD_handler(int sig UNUSED_PARAM) | |||
1393 | static void sigexit(int sig) NORETURN; | 1487 | static void sigexit(int sig) NORETURN; |
1394 | static void sigexit(int sig) | 1488 | static void sigexit(int sig) |
1395 | { | 1489 | { |
1396 | /* Disable all signals: job control, SIGPIPE, etc. */ | ||
1397 | sigprocmask_allsigs(SIG_BLOCK); | ||
1398 | |||
1399 | /* Careful: we can end up here after [v]fork. Do not restore | 1490 | /* Careful: we can end up here after [v]fork. Do not restore |
1400 | * tty pgrp then, only top-level shell process does that */ | 1491 | * tty pgrp then, only top-level shell process does that */ |
1401 | if (G_saved_tty_pgrp && getpid() == G.root_pid) | 1492 | if (G_saved_tty_pgrp && getpid() == G.root_pid) { |
1493 | /* Disable all signals: job control, SIGPIPE, etc. | ||
1494 | * Mostly paranoid measure, to prevent infinite SIGTTOU. | ||
1495 | */ | ||
1496 | sigprocmask_allsigs(SIG_BLOCK); | ||
1402 | tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); | 1497 | tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); |
1498 | } | ||
1403 | 1499 | ||
1404 | /* Not a signal, just exit */ | 1500 | /* Not a signal, just exit */ |
1405 | if (sig <= 0) | 1501 | if (sig <= 0) |
@@ -1414,15 +1510,39 @@ static void sigexit(int sig) | |||
1414 | 1510 | ||
1415 | #endif | 1511 | #endif |
1416 | 1512 | ||
1513 | static sighandler_t pick_sighandler(unsigned sig) | ||
1514 | { | ||
1515 | sighandler_t handler = SIG_DFL; | ||
1516 | if (sig < sizeof(unsigned)*8) { | ||
1517 | unsigned sigmask = (1 << sig); | ||
1518 | |||
1519 | #if ENABLE_HUSH_JOB | ||
1520 | /* is sig fatal? */ | ||
1521 | if (G_fatal_sig_mask & sigmask) | ||
1522 | handler = sigexit; | ||
1523 | else | ||
1524 | #endif | ||
1525 | /* sig has special handling? */ | ||
1526 | if (G.special_sig_mask & sigmask) { | ||
1527 | handler = record_pending_signo; | ||
1528 | /* TTIN/TTOU/TSTP can't be set to record_pending_signo | ||
1529 | * in order to ignore them: they will be raised | ||
1530 | * in an endless loop when we try to do some | ||
1531 | * terminal ioctls! We do have to _ignore_ these. | ||
1532 | */ | ||
1533 | if (SPECIAL_JOBSTOP_SIGS & sigmask) | ||
1534 | handler = SIG_IGN; | ||
1535 | } | ||
1536 | } | ||
1537 | return handler; | ||
1538 | } | ||
1539 | |||
1417 | /* Restores tty foreground process group, and exits. */ | 1540 | /* Restores tty foreground process group, and exits. */ |
1418 | static void hush_exit(int exitcode) NORETURN; | 1541 | static void hush_exit(int exitcode) NORETURN; |
1419 | static void hush_exit(int exitcode) | 1542 | static void hush_exit(int exitcode) |
1420 | { | 1543 | { |
1421 | fflush_all(); | 1544 | fflush_all(); |
1422 | if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) { | 1545 | if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) { |
1423 | /* Prevent recursion: | ||
1424 | * trap "echo Hi; exit" EXIT; exit | ||
1425 | */ | ||
1426 | char *argv[3]; | 1546 | char *argv[3]; |
1427 | /* argv[0] is unused */ | 1547 | /* argv[0] is unused */ |
1428 | argv[1] = G.traps[0]; | 1548 | argv[1] = G.traps[0]; |
@@ -1459,28 +1579,30 @@ static void hush_exit(int exitcode) | |||
1459 | } | 1579 | } |
1460 | 1580 | ||
1461 | 1581 | ||
1462 | static int check_and_run_traps(int sig) | 1582 | //TODO: return a mask of ALL handled sigs? |
1583 | static int check_and_run_traps(void) | ||
1463 | { | 1584 | { |
1464 | /* I want it in rodata, not in bss. | ||
1465 | * gcc 4.2.1 puts it in rodata only if it has { 0, 0 } | ||
1466 | * initializer. But other compilers may still use bss. | ||
1467 | * TODO: find more portable solution. | ||
1468 | */ | ||
1469 | static const struct timespec zero_timespec = { 0, 0 }; | ||
1470 | smalluint save_rcode; | ||
1471 | int last_sig = 0; | 1585 | int last_sig = 0; |
1472 | 1586 | ||
1473 | if (sig) | ||
1474 | goto jump_in; | ||
1475 | while (1) { | 1587 | while (1) { |
1476 | sig = sigtimedwait(&G.blocked_set, NULL, &zero_timespec); | 1588 | int sig; |
1477 | if (sig <= 0) | 1589 | |
1590 | if (sigisemptyset(&G.pending_set)) | ||
1478 | break; | 1591 | break; |
1479 | jump_in: | 1592 | sig = 0; |
1480 | last_sig = sig; | 1593 | do { |
1594 | sig++; | ||
1595 | if (sigismember(&G.pending_set, sig)) { | ||
1596 | sigdelset(&G.pending_set, sig); | ||
1597 | goto got_sig; | ||
1598 | } | ||
1599 | } while (sig < NSIG); | ||
1600 | break; | ||
1601 | got_sig: | ||
1481 | if (G.traps && G.traps[sig]) { | 1602 | if (G.traps && G.traps[sig]) { |
1482 | if (G.traps[sig][0]) { | 1603 | if (G.traps[sig][0]) { |
1483 | /* We have user-defined handler */ | 1604 | /* We have user-defined handler */ |
1605 | smalluint save_rcode; | ||
1484 | char *argv[3]; | 1606 | char *argv[3]; |
1485 | /* argv[0] is unused */ | 1607 | /* argv[0] is unused */ |
1486 | argv[1] = G.traps[sig]; | 1608 | argv[1] = G.traps[sig]; |
@@ -1488,21 +1610,17 @@ static int check_and_run_traps(int sig) | |||
1488 | save_rcode = G.last_exitcode; | 1610 | save_rcode = G.last_exitcode; |
1489 | builtin_eval(argv); | 1611 | builtin_eval(argv); |
1490 | G.last_exitcode = save_rcode; | 1612 | G.last_exitcode = save_rcode; |
1613 | last_sig = sig; | ||
1491 | } /* else: "" trap, ignoring signal */ | 1614 | } /* else: "" trap, ignoring signal */ |
1492 | continue; | 1615 | continue; |
1493 | } | 1616 | } |
1494 | /* not a trap: special action */ | 1617 | /* not a trap: special action */ |
1495 | switch (sig) { | 1618 | switch (sig) { |
1496 | #if ENABLE_HUSH_FAST | ||
1497 | case SIGCHLD: | ||
1498 | G.count_SIGCHLD++; | ||
1499 | //bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); | ||
1500 | break; | ||
1501 | #endif | ||
1502 | case SIGINT: | 1619 | case SIGINT: |
1503 | /* Builtin was ^C'ed, make it look prettier: */ | 1620 | /* Builtin was ^C'ed, make it look prettier: */ |
1504 | bb_putchar('\n'); | 1621 | bb_putchar('\n'); |
1505 | G.flag_SIGINT = 1; | 1622 | G.flag_SIGINT = 1; |
1623 | last_sig = sig; | ||
1506 | break; | 1624 | break; |
1507 | #if ENABLE_HUSH_JOB | 1625 | #if ENABLE_HUSH_JOB |
1508 | case SIGHUP: { | 1626 | case SIGHUP: { |
@@ -1519,8 +1637,23 @@ static int check_and_run_traps(int sig) | |||
1519 | sigexit(SIGHUP); | 1637 | sigexit(SIGHUP); |
1520 | } | 1638 | } |
1521 | #endif | 1639 | #endif |
1640 | #if ENABLE_HUSH_FAST | ||
1641 | case SIGCHLD: | ||
1642 | G.count_SIGCHLD++; | ||
1643 | //bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); | ||
1644 | /* Note: | ||
1645 | * We dont do 'last_sig = sig' here -> NOT returning this sig. | ||
1646 | * This simplifies wait builtin a bit. | ||
1647 | */ | ||
1648 | break; | ||
1649 | #endif | ||
1522 | default: /* ignored: */ | 1650 | default: /* ignored: */ |
1523 | /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ | 1651 | /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ |
1652 | /* Note: | ||
1653 | * We dont do 'last_sig = sig' here -> NOT returning this sig. | ||
1654 | * Example: wait is not interrupted by TERM | ||
1655 | * in interactive shell, because TERM is ignored. | ||
1656 | */ | ||
1524 | break; | 1657 | break; |
1525 | } | 1658 | } |
1526 | } | 1659 | } |
@@ -1911,7 +2044,7 @@ static void get_user_input(struct in_str *i) | |||
1911 | * only after <Enter>. (^C will work) */ | 2044 | * only after <Enter>. (^C will work) */ |
1912 | r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1); | 2045 | r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1); |
1913 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ | 2046 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ |
1914 | check_and_run_traps(0); | 2047 | check_and_run_traps(); |
1915 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ | 2048 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ |
1916 | i->eof_flag = (r < 0); | 2049 | i->eof_flag = (r < 0); |
1917 | if (i->eof_flag) { /* EOF/error detected */ | 2050 | if (i->eof_flag) { /* EOF/error detected */ |
@@ -1921,11 +2054,18 @@ static void get_user_input(struct in_str *i) | |||
1921 | # else | 2054 | # else |
1922 | do { | 2055 | do { |
1923 | G.flag_SIGINT = 0; | 2056 | G.flag_SIGINT = 0; |
1924 | fputs(prompt_str, stdout); | 2057 | if (i->last_char == '\0' || i->last_char == '\n') { |
2058 | /* Why check_and_run_traps here? Try this interactively: | ||
2059 | * $ trap 'echo INT' INT; (sleep 2; kill -INT $$) & | ||
2060 | * $ <[enter], repeatedly...> | ||
2061 | * Without check_and_run_traps, handler never runs. | ||
2062 | */ | ||
2063 | check_and_run_traps(); | ||
2064 | fputs(prompt_str, stdout); | ||
2065 | } | ||
1925 | fflush_all(); | 2066 | fflush_all(); |
1926 | G.user_input_buf[0] = r = fgetc(i->file); | 2067 | G.user_input_buf[0] = r = fgetc(i->file); |
1927 | /*G.user_input_buf[1] = '\0'; - already is and never changed */ | 2068 | /*G.user_input_buf[1] = '\0'; - already is and never changed */ |
1928 | //do we need check_and_run_traps(0)? (maybe only if stdin) | ||
1929 | } while (G.flag_SIGINT); | 2069 | } while (G.flag_SIGINT); |
1930 | i->eof_flag = (r == EOF); | 2070 | i->eof_flag = (r == EOF); |
1931 | # endif | 2071 | # endif |
@@ -2902,24 +3042,24 @@ static const struct reserved_combo* match_reserved_word(o_string *word) | |||
2902 | */ | 3042 | */ |
2903 | static const struct reserved_combo reserved_list[] = { | 3043 | static const struct reserved_combo reserved_list[] = { |
2904 | # if ENABLE_HUSH_IF | 3044 | # if ENABLE_HUSH_IF |
2905 | { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, | 3045 | { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, |
2906 | { "if", RES_IF, WORD_IS_KEYWORD, FLAG_THEN | FLAG_START }, | 3046 | { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START }, |
2907 | { "then", RES_THEN, WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, | 3047 | { "then", RES_THEN, MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, |
2908 | { "elif", RES_ELIF, WORD_IS_KEYWORD, FLAG_THEN }, | 3048 | { "elif", RES_ELIF, MAYBE_ASSIGNMENT, FLAG_THEN }, |
2909 | { "else", RES_ELSE, WORD_IS_KEYWORD, FLAG_FI }, | 3049 | { "else", RES_ELSE, MAYBE_ASSIGNMENT, FLAG_FI }, |
2910 | { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, | 3050 | { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, |
2911 | # endif | 3051 | # endif |
2912 | # if ENABLE_HUSH_LOOPS | 3052 | # if ENABLE_HUSH_LOOPS |
2913 | { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, | 3053 | { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, |
2914 | { "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, | 3054 | { "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START }, |
2915 | { "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, | 3055 | { "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START }, |
2916 | { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, | 3056 | { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, |
2917 | { "do", RES_DO, WORD_IS_KEYWORD, FLAG_DONE }, | 3057 | { "do", RES_DO, MAYBE_ASSIGNMENT, FLAG_DONE }, |
2918 | { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, | 3058 | { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, |
2919 | # endif | 3059 | # endif |
2920 | # if ENABLE_HUSH_CASE | 3060 | # if ENABLE_HUSH_CASE |
2921 | { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, | 3061 | { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, |
2922 | { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, | 3062 | { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, |
2923 | # endif | 3063 | # endif |
2924 | }; | 3064 | }; |
2925 | const struct reserved_combo *r; | 3065 | const struct reserved_combo *r; |
@@ -2985,6 +3125,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
2985 | ctx->ctx_res_w = r->res; | 3125 | ctx->ctx_res_w = r->res; |
2986 | ctx->old_flag = r->flag; | 3126 | ctx->old_flag = r->flag; |
2987 | word->o_assignment = r->assignment_flag; | 3127 | word->o_assignment = r->assignment_flag; |
3128 | debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]); | ||
2988 | 3129 | ||
2989 | if (ctx->old_flag & FLAG_END) { | 3130 | if (ctx->old_flag & FLAG_END) { |
2990 | struct parse_context *old; | 3131 | struct parse_context *old; |
@@ -3051,18 +3192,6 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3051 | debug_printf_parse("word stored in rd_filename: '%s'\n", word->data); | 3192 | debug_printf_parse("word stored in rd_filename: '%s'\n", word->data); |
3052 | ctx->pending_redirect = NULL; | 3193 | ctx->pending_redirect = NULL; |
3053 | } else { | 3194 | } else { |
3054 | /* If this word wasn't an assignment, next ones definitely | ||
3055 | * can't be assignments. Even if they look like ones. */ | ||
3056 | if (word->o_assignment != DEFINITELY_ASSIGNMENT | ||
3057 | && word->o_assignment != WORD_IS_KEYWORD | ||
3058 | ) { | ||
3059 | word->o_assignment = NOT_ASSIGNMENT; | ||
3060 | } else { | ||
3061 | if (word->o_assignment == DEFINITELY_ASSIGNMENT) | ||
3062 | command->assignment_cnt++; | ||
3063 | word->o_assignment = MAYBE_ASSIGNMENT; | ||
3064 | } | ||
3065 | |||
3066 | #if HAS_KEYWORDS | 3195 | #if HAS_KEYWORDS |
3067 | # if ENABLE_HUSH_CASE | 3196 | # if ENABLE_HUSH_CASE |
3068 | if (ctx->ctx_dsemicolon | 3197 | if (ctx->ctx_dsemicolon |
@@ -3082,8 +3211,9 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3082 | && ctx->ctx_res_w != RES_CASE | 3211 | && ctx->ctx_res_w != RES_CASE |
3083 | # endif | 3212 | # endif |
3084 | ) { | 3213 | ) { |
3085 | debug_printf_parse("checking '%s' for reserved-ness\n", word->data); | 3214 | int reserved = reserved_word(word, ctx); |
3086 | if (reserved_word(word, ctx)) { | 3215 | debug_printf_parse("checking for reserved-ness: %d\n", reserved); |
3216 | if (reserved) { | ||
3087 | o_reset_to_empty_unquoted(word); | 3217 | o_reset_to_empty_unquoted(word); |
3088 | debug_printf_parse("done_word return %d\n", | 3218 | debug_printf_parse("done_word return %d\n", |
3089 | (ctx->ctx_res_w == RES_SNTX)); | 3219 | (ctx->ctx_res_w == RES_SNTX)); |
@@ -3104,6 +3234,23 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3104 | "groups and arglists don't mix\n"); | 3234 | "groups and arglists don't mix\n"); |
3105 | return 1; | 3235 | return 1; |
3106 | } | 3236 | } |
3237 | |||
3238 | /* If this word wasn't an assignment, next ones definitely | ||
3239 | * can't be assignments. Even if they look like ones. */ | ||
3240 | if (word->o_assignment != DEFINITELY_ASSIGNMENT | ||
3241 | && word->o_assignment != WORD_IS_KEYWORD | ||
3242 | ) { | ||
3243 | word->o_assignment = NOT_ASSIGNMENT; | ||
3244 | } else { | ||
3245 | if (word->o_assignment == DEFINITELY_ASSIGNMENT) { | ||
3246 | command->assignment_cnt++; | ||
3247 | debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt); | ||
3248 | } | ||
3249 | debug_printf_parse("word->o_assignment was:'%s'\n", assignment_flag[word->o_assignment]); | ||
3250 | word->o_assignment = MAYBE_ASSIGNMENT; | ||
3251 | } | ||
3252 | debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]); | ||
3253 | |||
3107 | if (word->has_quoted_part | 3254 | if (word->has_quoted_part |
3108 | /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ | 3255 | /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ |
3109 | && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) | 3256 | && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) |
@@ -3322,6 +3469,7 @@ static char *fetch_till_str(o_string *as_string, | |||
3322 | int ch; | 3469 | int ch; |
3323 | 3470 | ||
3324 | goto jump_in; | 3471 | goto jump_in; |
3472 | |||
3325 | while (1) { | 3473 | while (1) { |
3326 | ch = i_getch(input); | 3474 | ch = i_getch(input); |
3327 | if (ch != EOF) | 3475 | if (ch != EOF) |
@@ -4127,6 +4275,7 @@ static struct pipe *parse_stream(char **pstring, | |||
4127 | && is_well_formed_var_name(dest.data, '=') | 4275 | && is_well_formed_var_name(dest.data, '=') |
4128 | ) { | 4276 | ) { |
4129 | dest.o_assignment = DEFINITELY_ASSIGNMENT; | 4277 | dest.o_assignment = DEFINITELY_ASSIGNMENT; |
4278 | debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); | ||
4130 | } | 4279 | } |
4131 | continue; | 4280 | continue; |
4132 | } | 4281 | } |
@@ -4176,6 +4325,7 @@ static struct pipe *parse_stream(char **pstring, | |||
4176 | heredoc_cnt = 0; | 4325 | heredoc_cnt = 0; |
4177 | } | 4326 | } |
4178 | dest.o_assignment = MAYBE_ASSIGNMENT; | 4327 | dest.o_assignment = MAYBE_ASSIGNMENT; |
4328 | debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); | ||
4179 | ch = ';'; | 4329 | ch = ';'; |
4180 | /* note: if (is_blank) continue; | 4330 | /* note: if (is_blank) continue; |
4181 | * will still trigger for us */ | 4331 | * will still trigger for us */ |
@@ -4225,6 +4375,7 @@ static struct pipe *parse_stream(char **pstring, | |||
4225 | } | 4375 | } |
4226 | done_pipe(&ctx, PIPE_SEQ); | 4376 | done_pipe(&ctx, PIPE_SEQ); |
4227 | dest.o_assignment = MAYBE_ASSIGNMENT; | 4377 | dest.o_assignment = MAYBE_ASSIGNMENT; |
4378 | debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); | ||
4228 | /* Do we sit outside of any if's, loops or case's? */ | 4379 | /* Do we sit outside of any if's, loops or case's? */ |
4229 | if (!HAS_KEYWORDS | 4380 | if (!HAS_KEYWORDS |
4230 | IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0)) | 4381 | IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0)) |
@@ -4331,6 +4482,7 @@ static struct pipe *parse_stream(char **pstring, | |||
4331 | /* ch is a special char and thus this word | 4482 | /* ch is a special char and thus this word |
4332 | * cannot be an assignment */ | 4483 | * cannot be an assignment */ |
4333 | dest.o_assignment = NOT_ASSIGNMENT; | 4484 | dest.o_assignment = NOT_ASSIGNMENT; |
4485 | debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); | ||
4334 | } | 4486 | } |
4335 | 4487 | ||
4336 | /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ | 4488 | /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ |
@@ -4385,11 +4537,11 @@ static struct pipe *parse_stream(char **pstring, | |||
4385 | break; | 4537 | break; |
4386 | #if ENABLE_HUSH_TICK | 4538 | #if ENABLE_HUSH_TICK |
4387 | case '`': { | 4539 | case '`': { |
4388 | unsigned pos; | 4540 | USE_FOR_NOMMU(unsigned pos;) |
4389 | 4541 | ||
4390 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | 4542 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); |
4391 | o_addchr(&dest, '`'); | 4543 | o_addchr(&dest, '`'); |
4392 | pos = dest.length; | 4544 | USE_FOR_NOMMU(pos = dest.length;) |
4393 | if (!add_till_backquote(&dest, input, /*in_dquote:*/ 0)) | 4545 | if (!add_till_backquote(&dest, input, /*in_dquote:*/ 0)) |
4394 | goto parse_error; | 4546 | goto parse_error; |
4395 | # if !BB_MMU | 4547 | # if !BB_MMU |
@@ -4429,6 +4581,7 @@ static struct pipe *parse_stream(char **pstring, | |||
4429 | /* We just finished a cmd. New one may start | 4581 | /* We just finished a cmd. New one may start |
4430 | * with an assignment */ | 4582 | * with an assignment */ |
4431 | dest.o_assignment = MAYBE_ASSIGNMENT; | 4583 | dest.o_assignment = MAYBE_ASSIGNMENT; |
4584 | debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); | ||
4432 | break; | 4585 | break; |
4433 | case '&': | 4586 | case '&': |
4434 | if (done_word(&dest, &ctx)) { | 4587 | if (done_word(&dest, &ctx)) { |
@@ -5323,6 +5476,25 @@ void re_execute_shell(char ***to_free, const char *s, | |||
5323 | char *g_argv0, char **g_argv, | 5476 | char *g_argv0, char **g_argv, |
5324 | char **builtin_argv) NORETURN; | 5477 | char **builtin_argv) NORETURN; |
5325 | 5478 | ||
5479 | static void switch_off_special_sigs(unsigned mask) | ||
5480 | { | ||
5481 | unsigned sig = 0; | ||
5482 | while ((mask >>= 1) != 0) { | ||
5483 | sig++; | ||
5484 | if (!(mask & 1)) | ||
5485 | continue; | ||
5486 | if (G.traps) { | ||
5487 | if (G.traps[sig] && !G.traps[sig][0]) | ||
5488 | /* trap is '', has to remain SIG_IGN */ | ||
5489 | continue; | ||
5490 | free(G.traps[sig]); | ||
5491 | G.traps[sig] = NULL; | ||
5492 | } | ||
5493 | /* We are here only if no trap or trap was not '' */ | ||
5494 | install_sighandler(sig, SIG_DFL); | ||
5495 | } | ||
5496 | } | ||
5497 | |||
5326 | static void reset_traps_to_defaults(void) | 5498 | static void reset_traps_to_defaults(void) |
5327 | { | 5499 | { |
5328 | /* This function is always called in a child shell | 5500 | /* This function is always called in a child shell |
@@ -5336,47 +5508,36 @@ static void reset_traps_to_defaults(void) | |||
5336 | * Testcase: (while :; do :; done) + ^Z should background. | 5508 | * Testcase: (while :; do :; done) + ^Z should background. |
5337 | * Same goes for SIGTERM, SIGHUP, SIGINT. | 5509 | * Same goes for SIGTERM, SIGHUP, SIGINT. |
5338 | */ | 5510 | */ |
5339 | if (!G.traps && !(G.non_DFL_mask & SPECIAL_INTERACTIVE_SIGS)) | 5511 | mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask; |
5340 | return; /* already no traps and no SPECIAL_INTERACTIVE_SIGS */ | 5512 | if (!G.traps && !mask) |
5341 | 5513 | return; /* already no traps and no special sigs */ | |
5342 | /* Switching off SPECIAL_INTERACTIVE_SIGS. | ||
5343 | * Stupid. It can be done with *single* &= op, but we can't use | ||
5344 | * the fact that G.blocked_set is implemented as a bitmask | ||
5345 | * in libc... */ | ||
5346 | mask = (SPECIAL_INTERACTIVE_SIGS >> 1); | ||
5347 | sig = 1; | ||
5348 | while (1) { | ||
5349 | if (mask & 1) { | ||
5350 | /* Careful. Only if no trap or trap is not "" */ | ||
5351 | if (!G.traps || !G.traps[sig] || G.traps[sig][0]) | ||
5352 | sigdelset(&G.blocked_set, sig); | ||
5353 | } | ||
5354 | mask >>= 1; | ||
5355 | if (!mask) | ||
5356 | break; | ||
5357 | sig++; | ||
5358 | } | ||
5359 | /* Our homegrown sig mask is saner to work with :) */ | ||
5360 | G.non_DFL_mask &= ~SPECIAL_INTERACTIVE_SIGS; | ||
5361 | 5514 | ||
5362 | /* Resetting all traps to default except empty ones */ | 5515 | /* Switch off special sigs */ |
5363 | mask = G.non_DFL_mask; | 5516 | switch_off_special_sigs(mask); |
5364 | if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) { | 5517 | #if ENABLE_HUSH_JOB |
5365 | if (!G.traps[sig] || !G.traps[sig][0]) | 5518 | G_fatal_sig_mask = 0; |
5366 | continue; | 5519 | #endif |
5520 | G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS; | ||
5521 | /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS | ||
5522 | * remain set in G.special_sig_mask */ | ||
5523 | |||
5524 | if (!G.traps) | ||
5525 | return; | ||
5526 | |||
5527 | /* Reset all sigs to default except ones with empty traps */ | ||
5528 | for (sig = 0; sig < NSIG; sig++) { | ||
5529 | if (!G.traps[sig]) | ||
5530 | continue; /* no trap: nothing to do */ | ||
5531 | if (!G.traps[sig][0]) | ||
5532 | continue; /* empty trap: has to remain SIG_IGN */ | ||
5533 | /* sig has non-empty trap, reset it: */ | ||
5367 | free(G.traps[sig]); | 5534 | free(G.traps[sig]); |
5368 | G.traps[sig] = NULL; | 5535 | G.traps[sig] = NULL; |
5369 | /* There is no signal for 0 (EXIT) */ | 5536 | /* There is no signal for trap 0 (EXIT) */ |
5370 | if (sig == 0) | 5537 | if (sig == 0) |
5371 | continue; | 5538 | continue; |
5372 | /* There was a trap handler, we just removed it. | 5539 | install_sighandler(sig, pick_sighandler(sig)); |
5373 | * But if sig still has non-DFL handling, | ||
5374 | * we should not unblock the sig. */ | ||
5375 | if (mask & 1) | ||
5376 | continue; | ||
5377 | sigdelset(&G.blocked_set, sig); | ||
5378 | } | 5540 | } |
5379 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
5380 | } | 5541 | } |
5381 | 5542 | ||
5382 | #else /* !BB_MMU */ | 5543 | #else /* !BB_MMU */ |
@@ -5487,9 +5648,6 @@ static void re_execute_shell(char ***to_free, const char *s, | |||
5487 | * _inside_ group (just before echo 1), it works. | 5648 | * _inside_ group (just before echo 1), it works. |
5488 | * | 5649 | * |
5489 | * I conclude it means we don't need to pass active traps here. | 5650 | * I conclude it means we don't need to pass active traps here. |
5490 | * Even if we would use signal handlers instead of signal masking | ||
5491 | * in order to implement trap handling, | ||
5492 | * exec syscall below resets signals to SIG_DFL for us. | ||
5493 | */ | 5651 | */ |
5494 | *pp++ = (char *) "-c"; | 5652 | *pp++ = (char *) "-c"; |
5495 | *pp++ = (char *) s; | 5653 | *pp++ = (char *) s; |
@@ -5506,7 +5664,9 @@ static void re_execute_shell(char ***to_free, const char *s, | |||
5506 | 5664 | ||
5507 | do_exec: | 5665 | do_exec: |
5508 | debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s); | 5666 | debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s); |
5509 | sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); | 5667 | /* Don't propagate SIG_IGN to the child */ |
5668 | if (SPECIAL_JOBSTOP_SIGS != 0) | ||
5669 | switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); | ||
5510 | execve(bb_busybox_exec_path, argv, pp); | 5670 | execve(bb_busybox_exec_path, argv, pp); |
5511 | /* Fallback. Useful for init=/bin/hush usage etc */ | 5671 | /* Fallback. Useful for init=/bin/hush usage etc */ |
5512 | if (argv[0][0] == '/') | 5672 | if (argv[0][0] == '/') |
@@ -6160,7 +6320,9 @@ static void execvp_or_die(char **argv) NORETURN; | |||
6160 | static void execvp_or_die(char **argv) | 6320 | static void execvp_or_die(char **argv) |
6161 | { | 6321 | { |
6162 | debug_printf_exec("execing '%s'\n", argv[0]); | 6322 | debug_printf_exec("execing '%s'\n", argv[0]); |
6163 | sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); | 6323 | /* Don't propagate SIG_IGN to the child */ |
6324 | if (SPECIAL_JOBSTOP_SIGS != 0) | ||
6325 | switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); | ||
6164 | execvp(argv[0], argv); | 6326 | execvp(argv[0], argv); |
6165 | bb_perror_msg("can't execute '%s'", argv[0]); | 6327 | bb_perror_msg("can't execute '%s'", argv[0]); |
6166 | _exit(127); /* bash compat */ | 6328 | _exit(127); /* bash compat */ |
@@ -6292,7 +6454,9 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
6292 | # endif | 6454 | # endif |
6293 | /* Re-exec ourselves */ | 6455 | /* Re-exec ourselves */ |
6294 | debug_printf_exec("re-execing applet '%s'\n", argv[0]); | 6456 | debug_printf_exec("re-execing applet '%s'\n", argv[0]); |
6295 | sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); | 6457 | /* Don't propagate SIG_IGN to the child */ |
6458 | if (SPECIAL_JOBSTOP_SIGS != 0) | ||
6459 | switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); | ||
6296 | execv(bb_busybox_exec_path, argv); | 6460 | execv(bb_busybox_exec_path, argv); |
6297 | /* If they called chroot or otherwise made the binary no longer | 6461 | /* If they called chroot or otherwise made the binary no longer |
6298 | * executable, fall through */ | 6462 | * executable, fall through */ |
@@ -6991,9 +7155,6 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
6991 | if (setup_redirects(command, NULL)) | 7155 | if (setup_redirects(command, NULL)) |
6992 | _exit(1); | 7156 | _exit(1); |
6993 | 7157 | ||
6994 | /* Restore default handlers just prior to exec */ | ||
6995 | /*signal(SIGCHLD, SIG_DFL); - so far we don't have any handlers */ | ||
6996 | |||
6997 | /* Stores to nommu_save list of env vars putenv'ed | 7158 | /* Stores to nommu_save list of env vars putenv'ed |
6998 | * (NOMMU, on MMU we don't need that) */ | 7159 | * (NOMMU, on MMU we don't need that) */ |
6999 | /* cast away volatility... */ | 7160 | /* cast away volatility... */ |
@@ -7271,7 +7432,7 @@ static int run_list(struct pipe *pi) | |||
7271 | * and we don't need to wait for anything. */ | 7432 | * and we don't need to wait for anything. */ |
7272 | G.last_exitcode = rcode; | 7433 | G.last_exitcode = rcode; |
7273 | debug_printf_exec(": builtin/func exitcode %d\n", rcode); | 7434 | debug_printf_exec(": builtin/func exitcode %d\n", rcode); |
7274 | check_and_run_traps(0); | 7435 | check_and_run_traps(); |
7275 | #if ENABLE_HUSH_LOOPS | 7436 | #if ENABLE_HUSH_LOOPS |
7276 | /* Was it "break" or "continue"? */ | 7437 | /* Was it "break" or "continue"? */ |
7277 | if (G.flag_break_continue) { | 7438 | if (G.flag_break_continue) { |
@@ -7303,7 +7464,7 @@ static int run_list(struct pipe *pi) | |||
7303 | /* even bash 3.2 doesn't do that well with nested bg: | 7464 | /* even bash 3.2 doesn't do that well with nested bg: |
7304 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". | 7465 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". |
7305 | * I'm NOT treating inner &'s as jobs */ | 7466 | * I'm NOT treating inner &'s as jobs */ |
7306 | check_and_run_traps(0); | 7467 | check_and_run_traps(); |
7307 | #if ENABLE_HUSH_JOB | 7468 | #if ENABLE_HUSH_JOB |
7308 | if (G.run_list_level == 1) | 7469 | if (G.run_list_level == 1) |
7309 | insert_bg_job(pi); | 7470 | insert_bg_job(pi); |
@@ -7318,13 +7479,13 @@ static int run_list(struct pipe *pi) | |||
7318 | /* Waits for completion, then fg's main shell */ | 7479 | /* Waits for completion, then fg's main shell */ |
7319 | rcode = checkjobs_and_fg_shell(pi); | 7480 | rcode = checkjobs_and_fg_shell(pi); |
7320 | debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode); | 7481 | debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode); |
7321 | check_and_run_traps(0); | 7482 | check_and_run_traps(); |
7322 | } else | 7483 | } else |
7323 | #endif | 7484 | #endif |
7324 | { /* This one just waits for completion */ | 7485 | { /* This one just waits for completion */ |
7325 | rcode = checkjobs(pi); | 7486 | rcode = checkjobs(pi); |
7326 | debug_printf_exec(": checkjobs exitcode %d\n", rcode); | 7487 | debug_printf_exec(": checkjobs exitcode %d\n", rcode); |
7327 | check_and_run_traps(0); | 7488 | check_and_run_traps(); |
7328 | } | 7489 | } |
7329 | G.last_exitcode = rcode; | 7490 | G.last_exitcode = rcode; |
7330 | } | 7491 | } |
@@ -7337,7 +7498,10 @@ static int run_list(struct pipe *pi) | |||
7337 | #endif | 7498 | #endif |
7338 | #if ENABLE_HUSH_LOOPS | 7499 | #if ENABLE_HUSH_LOOPS |
7339 | /* Beware of "while false; true; do ..."! */ | 7500 | /* Beware of "while false; true; do ..."! */ |
7340 | if (pi->next && pi->next->res_word == RES_DO) { | 7501 | if (pi->next |
7502 | && (pi->next->res_word == RES_DO || pi->next->res_word == RES_DONE) | ||
7503 | /* check for RES_DONE is needed for "while ...; do \n done" case */ | ||
7504 | ) { | ||
7341 | if (rword == RES_WHILE) { | 7505 | if (rword == RES_WHILE) { |
7342 | if (rcode) { | 7506 | if (rcode) { |
7343 | /* "while false; do...done" - exitcode 0 */ | 7507 | /* "while false; do...done" - exitcode 0 */ |
@@ -7395,88 +7559,81 @@ static int run_and_free_list(struct pipe *pi) | |||
7395 | } | 7559 | } |
7396 | 7560 | ||
7397 | 7561 | ||
7562 | static void install_sighandlers(unsigned mask) | ||
7563 | { | ||
7564 | sighandler_t old_handler; | ||
7565 | unsigned sig = 0; | ||
7566 | while ((mask >>= 1) != 0) { | ||
7567 | sig++; | ||
7568 | if (!(mask & 1)) | ||
7569 | continue; | ||
7570 | old_handler = install_sighandler(sig, pick_sighandler(sig)); | ||
7571 | /* POSIX allows shell to re-enable SIGCHLD | ||
7572 | * even if it was SIG_IGN on entry. | ||
7573 | * Therefore we skip IGN check for it: | ||
7574 | */ | ||
7575 | if (sig == SIGCHLD) | ||
7576 | continue; | ||
7577 | if (old_handler == SIG_IGN) { | ||
7578 | /* oops... restore back to IGN, and record this fact */ | ||
7579 | install_sighandler(sig, old_handler); | ||
7580 | if (!G.traps) | ||
7581 | G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); | ||
7582 | free(G.traps[sig]); | ||
7583 | G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ | ||
7584 | } | ||
7585 | } | ||
7586 | } | ||
7587 | |||
7398 | /* Called a few times only (or even once if "sh -c") */ | 7588 | /* Called a few times only (or even once if "sh -c") */ |
7399 | static void init_sigmasks(void) | 7589 | static void install_special_sighandlers(void) |
7400 | { | 7590 | { |
7401 | unsigned sig; | ||
7402 | unsigned mask; | 7591 | unsigned mask; |
7403 | sigset_t old_blocked_set; | ||
7404 | |||
7405 | if (!G.inherited_set_is_saved) { | ||
7406 | sigprocmask(SIG_SETMASK, NULL, &G.blocked_set); | ||
7407 | G.inherited_set = G.blocked_set; | ||
7408 | } | ||
7409 | old_blocked_set = G.blocked_set; | ||
7410 | 7592 | ||
7411 | mask = (1 << SIGQUIT); | 7593 | /* Which signals are shell-special? */ |
7594 | mask = (1 << SIGQUIT) | (1 << SIGCHLD); | ||
7412 | if (G_interactive_fd) { | 7595 | if (G_interactive_fd) { |
7413 | mask = (1 << SIGQUIT) | SPECIAL_INTERACTIVE_SIGS; | 7596 | mask |= SPECIAL_INTERACTIVE_SIGS; |
7414 | if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */ | 7597 | if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */ |
7415 | mask |= SPECIAL_JOB_SIGS; | 7598 | mask |= SPECIAL_JOBSTOP_SIGS; |
7416 | } | 7599 | } |
7417 | G.non_DFL_mask = mask; | 7600 | /* Careful, do not re-install handlers we already installed */ |
7418 | 7601 | if (G.special_sig_mask != mask) { | |
7419 | sig = 0; | 7602 | unsigned diff = mask & ~G.special_sig_mask; |
7420 | while (mask) { | 7603 | G.special_sig_mask = mask; |
7421 | if (mask & 1) | 7604 | install_sighandlers(diff); |
7422 | sigaddset(&G.blocked_set, sig); | ||
7423 | mask >>= 1; | ||
7424 | sig++; | ||
7425 | } | 7605 | } |
7426 | sigdelset(&G.blocked_set, SIGCHLD); | ||
7427 | |||
7428 | if (memcmp(&old_blocked_set, &G.blocked_set, sizeof(old_blocked_set)) != 0) | ||
7429 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
7430 | |||
7431 | /* POSIX allows shell to re-enable SIGCHLD | ||
7432 | * even if it was SIG_IGN on entry */ | ||
7433 | #if ENABLE_HUSH_FAST | ||
7434 | G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ | ||
7435 | if (!G.inherited_set_is_saved) | ||
7436 | signal(SIGCHLD, SIGCHLD_handler); | ||
7437 | #else | ||
7438 | if (!G.inherited_set_is_saved) | ||
7439 | signal(SIGCHLD, SIG_DFL); | ||
7440 | #endif | ||
7441 | |||
7442 | G.inherited_set_is_saved = 1; | ||
7443 | } | 7606 | } |
7444 | 7607 | ||
7445 | #if ENABLE_HUSH_JOB | 7608 | #if ENABLE_HUSH_JOB |
7446 | /* helper */ | 7609 | /* helper */ |
7447 | static void maybe_set_to_sigexit(int sig) | ||
7448 | { | ||
7449 | void (*handler)(int); | ||
7450 | /* non_DFL_mask'ed signals are, well, masked, | ||
7451 | * no need to set handler for them. | ||
7452 | */ | ||
7453 | if (!((G.non_DFL_mask >> sig) & 1)) { | ||
7454 | handler = signal(sig, sigexit); | ||
7455 | if (handler == SIG_IGN) /* oops... restore back to IGN! */ | ||
7456 | signal(sig, handler); | ||
7457 | } | ||
7458 | } | ||
7459 | /* Set handlers to restore tty pgrp and exit */ | 7610 | /* Set handlers to restore tty pgrp and exit */ |
7460 | static void set_fatal_handlers(void) | 7611 | static void install_fatal_sighandlers(void) |
7461 | { | 7612 | { |
7462 | /* We _must_ restore tty pgrp on fatal signals */ | 7613 | unsigned mask; |
7463 | if (HUSH_DEBUG) { | 7614 | |
7464 | maybe_set_to_sigexit(SIGILL ); | 7615 | /* We will restore tty pgrp on these signals */ |
7465 | maybe_set_to_sigexit(SIGFPE ); | 7616 | mask = 0 |
7466 | maybe_set_to_sigexit(SIGBUS ); | 7617 | + (1 << SIGILL ) * HUSH_DEBUG |
7467 | maybe_set_to_sigexit(SIGSEGV); | 7618 | + (1 << SIGFPE ) * HUSH_DEBUG |
7468 | maybe_set_to_sigexit(SIGTRAP); | 7619 | + (1 << SIGBUS ) * HUSH_DEBUG |
7469 | } /* else: hush is perfect. what SEGV? */ | 7620 | + (1 << SIGSEGV) * HUSH_DEBUG |
7470 | maybe_set_to_sigexit(SIGABRT); | 7621 | + (1 << SIGTRAP) * HUSH_DEBUG |
7622 | + (1 << SIGABRT) | ||
7471 | /* bash 3.2 seems to handle these just like 'fatal' ones */ | 7623 | /* bash 3.2 seems to handle these just like 'fatal' ones */ |
7472 | maybe_set_to_sigexit(SIGPIPE); | 7624 | + (1 << SIGPIPE) |
7473 | maybe_set_to_sigexit(SIGALRM); | 7625 | + (1 << SIGALRM) |
7474 | /* if we are interactive, SIGHUP, SIGTERM and SIGINT are masked. | 7626 | /* if we are interactive, SIGHUP, SIGTERM and SIGINT are special sigs. |
7475 | * if we aren't interactive... but in this case | 7627 | * if we aren't interactive... but in this case |
7476 | * we never want to restore pgrp on exit, and this fn is not called */ | 7628 | * we never want to restore pgrp on exit, and this fn is not called |
7477 | /*maybe_set_to_sigexit(SIGHUP );*/ | 7629 | */ |
7478 | /*maybe_set_to_sigexit(SIGTERM);*/ | 7630 | /*+ (1 << SIGHUP )*/ |
7479 | /*maybe_set_to_sigexit(SIGINT );*/ | 7631 | /*+ (1 << SIGTERM)*/ |
7632 | /*+ (1 << SIGINT )*/ | ||
7633 | ; | ||
7634 | G_fatal_sig_mask = mask; | ||
7635 | |||
7636 | install_sighandlers(mask); | ||
7480 | } | 7637 | } |
7481 | #endif | 7638 | #endif |
7482 | 7639 | ||
@@ -7522,6 +7679,10 @@ static int set_mode(int state, char mode, const char *o_opt) | |||
7522 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 7679 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
7523 | int hush_main(int argc, char **argv) | 7680 | int hush_main(int argc, char **argv) |
7524 | { | 7681 | { |
7682 | enum { | ||
7683 | OPT_login = (1 << 0), | ||
7684 | }; | ||
7685 | unsigned flags; | ||
7525 | int opt; | 7686 | int opt; |
7526 | unsigned builtin_argc; | 7687 | unsigned builtin_argc; |
7527 | char **e; | 7688 | char **e; |
@@ -7529,8 +7690,11 @@ int hush_main(int argc, char **argv) | |||
7529 | struct variable *shell_ver; | 7690 | struct variable *shell_ver; |
7530 | 7691 | ||
7531 | INIT_G(); | 7692 | INIT_G(); |
7532 | if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */ | 7693 | if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ |
7533 | G.last_exitcode = EXIT_SUCCESS; | 7694 | G.last_exitcode = EXIT_SUCCESS; |
7695 | #if ENABLE_HUSH_FAST | ||
7696 | G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ | ||
7697 | #endif | ||
7534 | #if !BB_MMU | 7698 | #if !BB_MMU |
7535 | G.argv0_for_re_execing = argv[0]; | 7699 | G.argv0_for_re_execing = argv[0]; |
7536 | #endif | 7700 | #endif |
@@ -7622,8 +7786,6 @@ int hush_main(int argc, char **argv) | |||
7622 | # endif | 7786 | # endif |
7623 | #endif | 7787 | #endif |
7624 | 7788 | ||
7625 | G.global_argc = argc; | ||
7626 | G.global_argv = argv; | ||
7627 | /* Initialize some more globals to non-zero values */ | 7789 | /* Initialize some more globals to non-zero values */ |
7628 | cmdedit_update_prompt(); | 7790 | cmdedit_update_prompt(); |
7629 | 7791 | ||
@@ -7635,17 +7797,18 @@ int hush_main(int argc, char **argv) | |||
7635 | } | 7797 | } |
7636 | 7798 | ||
7637 | /* Shell is non-interactive at first. We need to call | 7799 | /* Shell is non-interactive at first. We need to call |
7638 | * init_sigmasks() if we are going to execute "sh <script>", | 7800 | * install_special_sighandlers() if we are going to execute "sh <script>", |
7639 | * "sh -c <cmds>" or login shell's /etc/profile and friends. | 7801 | * "sh -c <cmds>" or login shell's /etc/profile and friends. |
7640 | * If we later decide that we are interactive, we run init_sigmasks() | 7802 | * If we later decide that we are interactive, we run install_special_sighandlers() |
7641 | * in order to intercept (more) signals. | 7803 | * in order to intercept (more) signals. |
7642 | */ | 7804 | */ |
7643 | 7805 | ||
7644 | /* Parse options */ | 7806 | /* Parse options */ |
7645 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ | 7807 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ |
7808 | flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; | ||
7646 | builtin_argc = 0; | 7809 | builtin_argc = 0; |
7647 | while (1) { | 7810 | while (1) { |
7648 | opt = getopt(argc, argv, "+c:xins" | 7811 | opt = getopt(argc, argv, "+c:xinsl" |
7649 | #if !BB_MMU | 7812 | #if !BB_MMU |
7650 | "<:$:R:V:" | 7813 | "<:$:R:V:" |
7651 | # if ENABLE_HUSH_FUNCTIONS | 7814 | # if ENABLE_HUSH_FUNCTIONS |
@@ -7677,7 +7840,7 @@ int hush_main(int argc, char **argv) | |||
7677 | /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */ | 7840 | /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */ |
7678 | const struct built_in_command *x; | 7841 | const struct built_in_command *x; |
7679 | 7842 | ||
7680 | init_sigmasks(); | 7843 | install_special_sighandlers(); |
7681 | x = find_builtin(optarg); | 7844 | x = find_builtin(optarg); |
7682 | if (x) { /* paranoia */ | 7845 | if (x) { /* paranoia */ |
7683 | G.global_argc -= builtin_argc; /* skip [BARGV...] "" */ | 7846 | G.global_argc -= builtin_argc; /* skip [BARGV...] "" */ |
@@ -7694,7 +7857,7 @@ int hush_main(int argc, char **argv) | |||
7694 | G.global_argv[0] = argv[0]; | 7857 | G.global_argv[0] = argv[0]; |
7695 | G.global_argc++; | 7858 | G.global_argc++; |
7696 | } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */ | 7859 | } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */ |
7697 | init_sigmasks(); | 7860 | install_special_sighandlers(); |
7698 | parse_and_run_string(optarg); | 7861 | parse_and_run_string(optarg); |
7699 | goto final_return; | 7862 | goto final_return; |
7700 | case 'i': | 7863 | case 'i': |
@@ -7706,6 +7869,9 @@ int hush_main(int argc, char **argv) | |||
7706 | /* "-s" means "read from stdin", but this is how we always | 7869 | /* "-s" means "read from stdin", but this is how we always |
7707 | * operate, so simply do nothing here. */ | 7870 | * operate, so simply do nothing here. */ |
7708 | break; | 7871 | break; |
7872 | case 'l': | ||
7873 | flags |= OPT_login; | ||
7874 | break; | ||
7709 | #if !BB_MMU | 7875 | #if !BB_MMU |
7710 | case '<': /* "big heredoc" support */ | 7876 | case '<': /* "big heredoc" support */ |
7711 | full_write1_str(optarg); | 7877 | full_write1_str(optarg); |
@@ -7726,15 +7892,14 @@ int hush_main(int argc, char **argv) | |||
7726 | empty_trap_mask = bb_strtoull(optarg, &optarg, 16); | 7892 | empty_trap_mask = bb_strtoull(optarg, &optarg, 16); |
7727 | if (empty_trap_mask != 0) { | 7893 | if (empty_trap_mask != 0) { |
7728 | int sig; | 7894 | int sig; |
7729 | init_sigmasks(); | 7895 | install_special_sighandlers(); |
7730 | G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); | 7896 | G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); |
7731 | for (sig = 1; sig < NSIG; sig++) { | 7897 | for (sig = 1; sig < NSIG; sig++) { |
7732 | if (empty_trap_mask & (1LL << sig)) { | 7898 | if (empty_trap_mask & (1LL << sig)) { |
7733 | G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ | 7899 | G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ |
7734 | sigaddset(&G.blocked_set, sig); | 7900 | install_sighandler(sig, SIG_IGN); |
7735 | } | 7901 | } |
7736 | } | 7902 | } |
7737 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
7738 | } | 7903 | } |
7739 | # if ENABLE_HUSH_LOOPS | 7904 | # if ENABLE_HUSH_LOOPS |
7740 | optarg++; | 7905 | optarg++; |
@@ -7772,19 +7937,24 @@ int hush_main(int argc, char **argv) | |||
7772 | } | 7937 | } |
7773 | } /* option parsing loop */ | 7938 | } /* option parsing loop */ |
7774 | 7939 | ||
7940 | /* Skip options. Try "hush -l": $1 should not be "-l"! */ | ||
7941 | G.global_argc = argc - (optind - 1); | ||
7942 | G.global_argv = argv + (optind - 1); | ||
7943 | G.global_argv[0] = argv[0]; | ||
7944 | |||
7775 | if (!G.root_pid) { | 7945 | if (!G.root_pid) { |
7776 | G.root_pid = getpid(); | 7946 | G.root_pid = getpid(); |
7777 | G.root_ppid = getppid(); | 7947 | G.root_ppid = getppid(); |
7778 | } | 7948 | } |
7779 | 7949 | ||
7780 | /* If we are login shell... */ | 7950 | /* If we are login shell... */ |
7781 | if (argv[0] && argv[0][0] == '-') { | 7951 | if (flags & OPT_login) { |
7782 | FILE *input; | 7952 | FILE *input; |
7783 | debug_printf("sourcing /etc/profile\n"); | 7953 | debug_printf("sourcing /etc/profile\n"); |
7784 | input = fopen_for_read("/etc/profile"); | 7954 | input = fopen_for_read("/etc/profile"); |
7785 | if (input != NULL) { | 7955 | if (input != NULL) { |
7786 | close_on_exec_on(fileno(input)); | 7956 | close_on_exec_on(fileno(input)); |
7787 | init_sigmasks(); | 7957 | install_special_sighandlers(); |
7788 | parse_and_run_file(input); | 7958 | parse_and_run_file(input); |
7789 | fclose(input); | 7959 | fclose(input); |
7790 | } | 7960 | } |
@@ -7797,19 +7967,19 @@ int hush_main(int argc, char **argv) | |||
7797 | */ | 7967 | */ |
7798 | } | 7968 | } |
7799 | 7969 | ||
7800 | if (argv[optind]) { | 7970 | if (G.global_argv[1]) { |
7801 | FILE *input; | 7971 | FILE *input; |
7802 | /* | 7972 | /* |
7803 | * "bash <script>" (which is never interactive (unless -i?)) | 7973 | * "bash <script>" (which is never interactive (unless -i?)) |
7804 | * sources $BASH_ENV here (without scanning $PATH). | 7974 | * sources $BASH_ENV here (without scanning $PATH). |
7805 | * If called as sh, does the same but with $ENV. | 7975 | * If called as sh, does the same but with $ENV. |
7806 | */ | 7976 | */ |
7807 | debug_printf("running script '%s'\n", argv[optind]); | 7977 | G.global_argc--; |
7808 | G.global_argv = argv + optind; | 7978 | G.global_argv++; |
7809 | G.global_argc = argc - optind; | 7979 | debug_printf("running script '%s'\n", G.global_argv[0]); |
7810 | input = xfopen_for_read(argv[optind]); | 7980 | input = xfopen_for_read(G.global_argv[0]); |
7811 | close_on_exec_on(fileno(input)); | 7981 | close_on_exec_on(fileno(input)); |
7812 | init_sigmasks(); | 7982 | install_special_sighandlers(); |
7813 | parse_and_run_file(input); | 7983 | parse_and_run_file(input); |
7814 | #if ENABLE_FEATURE_CLEAN_UP | 7984 | #if ENABLE_FEATURE_CLEAN_UP |
7815 | fclose(input); | 7985 | fclose(input); |
@@ -7818,7 +7988,7 @@ int hush_main(int argc, char **argv) | |||
7818 | } | 7988 | } |
7819 | 7989 | ||
7820 | /* Up to here, shell was non-interactive. Now it may become one. | 7990 | /* Up to here, shell was non-interactive. Now it may become one. |
7821 | * NB: don't forget to (re)run init_sigmasks() as needed. | 7991 | * NB: don't forget to (re)run install_special_sighandlers() as needed. |
7822 | */ | 7992 | */ |
7823 | 7993 | ||
7824 | /* A shell is interactive if the '-i' flag was given, | 7994 | /* A shell is interactive if the '-i' flag was given, |
@@ -7870,12 +8040,12 @@ int hush_main(int argc, char **argv) | |||
7870 | } | 8040 | } |
7871 | } | 8041 | } |
7872 | 8042 | ||
7873 | /* Block some signals */ | 8043 | /* Install more signal handlers */ |
7874 | init_sigmasks(); | 8044 | install_special_sighandlers(); |
7875 | 8045 | ||
7876 | if (G_saved_tty_pgrp) { | 8046 | if (G_saved_tty_pgrp) { |
7877 | /* Set other signals to restore saved_tty_pgrp */ | 8047 | /* Set other signals to restore saved_tty_pgrp */ |
7878 | set_fatal_handlers(); | 8048 | install_fatal_sighandlers(); |
7879 | /* Put ourselves in our own process group | 8049 | /* Put ourselves in our own process group |
7880 | * (bash, too, does this only if ctty is available) */ | 8050 | * (bash, too, does this only if ctty is available) */ |
7881 | bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ | 8051 | bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ |
@@ -7886,7 +8056,7 @@ int hush_main(int argc, char **argv) | |||
7886 | * (we reset die_sleep = 0 whereever we [v]fork) */ | 8056 | * (we reset die_sleep = 0 whereever we [v]fork) */ |
7887 | enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */ | 8057 | enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */ |
7888 | } else { | 8058 | } else { |
7889 | init_sigmasks(); | 8059 | install_special_sighandlers(); |
7890 | } | 8060 | } |
7891 | #elif ENABLE_HUSH_INTERACTIVE | 8061 | #elif ENABLE_HUSH_INTERACTIVE |
7892 | /* No job control compiled in, only prompt/line editing */ | 8062 | /* No job control compiled in, only prompt/line editing */ |
@@ -7903,10 +8073,10 @@ int hush_main(int argc, char **argv) | |||
7903 | if (G_interactive_fd) { | 8073 | if (G_interactive_fd) { |
7904 | close_on_exec_on(G_interactive_fd); | 8074 | close_on_exec_on(G_interactive_fd); |
7905 | } | 8075 | } |
7906 | init_sigmasks(); | 8076 | install_special_sighandlers(); |
7907 | #else | 8077 | #else |
7908 | /* We have interactiveness code disabled */ | 8078 | /* We have interactiveness code disabled */ |
7909 | init_sigmasks(); | 8079 | install_special_sighandlers(); |
7910 | #endif | 8080 | #endif |
7911 | /* bash: | 8081 | /* bash: |
7912 | * if interactive but not a login shell, sources ~/.bashrc | 8082 | * if interactive but not a login shell, sources ~/.bashrc |
@@ -8040,7 +8210,7 @@ static int FAST_FUNC builtin_exec(char **argv) | |||
8040 | tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); | 8210 | tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); |
8041 | 8211 | ||
8042 | /* TODO: if exec fails, bash does NOT exit! We do. | 8212 | /* TODO: if exec fails, bash does NOT exit! We do. |
8043 | * We'll need to undo sigprocmask (it's inside execvp_or_die) | 8213 | * We'll need to undo trap cleanup (it's inside execvp_or_die) |
8044 | * and tcsetpgrp, and this is inherently racy. | 8214 | * and tcsetpgrp, and this is inherently racy. |
8045 | */ | 8215 | */ |
8046 | execvp_or_die(argv); | 8216 | execvp_or_die(argv); |
@@ -8237,6 +8407,8 @@ static int FAST_FUNC builtin_trap(char **argv) | |||
8237 | process_sig_list: | 8407 | process_sig_list: |
8238 | ret = EXIT_SUCCESS; | 8408 | ret = EXIT_SUCCESS; |
8239 | while (*argv) { | 8409 | while (*argv) { |
8410 | sighandler_t handler; | ||
8411 | |||
8240 | sig = get_signum(*argv++); | 8412 | sig = get_signum(*argv++); |
8241 | if (sig < 0 || sig >= NSIG) { | 8413 | if (sig < 0 || sig >= NSIG) { |
8242 | ret = EXIT_FAILURE; | 8414 | ret = EXIT_FAILURE; |
@@ -8255,18 +8427,13 @@ static int FAST_FUNC builtin_trap(char **argv) | |||
8255 | if (sig == 0) | 8427 | if (sig == 0) |
8256 | continue; | 8428 | continue; |
8257 | 8429 | ||
8258 | if (new_cmd) { | 8430 | if (new_cmd) |
8259 | sigaddset(&G.blocked_set, sig); | 8431 | handler = (new_cmd[0] ? record_pending_signo : SIG_IGN); |
8260 | } else { | 8432 | else |
8261 | /* There was a trap handler, we are removing it | 8433 | /* We are removing trap handler */ |
8262 | * (if sig has non-DFL handling, | 8434 | handler = pick_sighandler(sig); |
8263 | * we don't need to do anything) */ | 8435 | install_sighandler(sig, handler); |
8264 | if (sig < 32 && (G.non_DFL_mask & (1 << sig))) | ||
8265 | continue; | ||
8266 | sigdelset(&G.blocked_set, sig); | ||
8267 | } | ||
8268 | } | 8436 | } |
8269 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
8270 | return ret; | 8437 | return ret; |
8271 | } | 8438 | } |
8272 | 8439 | ||
@@ -8467,6 +8634,27 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM) | |||
8467 | return EXIT_SUCCESS; | 8634 | return EXIT_SUCCESS; |
8468 | } | 8635 | } |
8469 | 8636 | ||
8637 | /* Interruptibility of read builtin in bash | ||
8638 | * (tested on bash-4.2.8 by sending signals (not by ^C)): | ||
8639 | * | ||
8640 | * Empty trap makes read ignore corresponding signal, for any signal. | ||
8641 | * | ||
8642 | * SIGINT: | ||
8643 | * - terminates non-interactive shell; | ||
8644 | * - interrupts read in interactive shell; | ||
8645 | * if it has non-empty trap: | ||
8646 | * - executes trap and returns to command prompt in interactive shell; | ||
8647 | * - executes trap and returns to read in non-interactive shell; | ||
8648 | * SIGTERM: | ||
8649 | * - is ignored (does not interrupt) read in interactive shell; | ||
8650 | * - terminates non-interactive shell; | ||
8651 | * if it has non-empty trap: | ||
8652 | * - executes trap and returns to read; | ||
8653 | * SIGHUP: | ||
8654 | * - terminates shell (regardless of interactivity); | ||
8655 | * if it has non-empty trap: | ||
8656 | * - executes trap and returns to read; | ||
8657 | */ | ||
8470 | static int FAST_FUNC builtin_read(char **argv) | 8658 | static int FAST_FUNC builtin_read(char **argv) |
8471 | { | 8659 | { |
8472 | const char *r; | 8660 | const char *r; |
@@ -8474,6 +8662,7 @@ static int FAST_FUNC builtin_read(char **argv) | |||
8474 | char *opt_p = NULL; | 8662 | char *opt_p = NULL; |
8475 | char *opt_t = NULL; | 8663 | char *opt_t = NULL; |
8476 | char *opt_u = NULL; | 8664 | char *opt_u = NULL; |
8665 | const char *ifs; | ||
8477 | int read_flags; | 8666 | int read_flags; |
8478 | 8667 | ||
8479 | /* "!": do not abort on errors. | 8668 | /* "!": do not abort on errors. |
@@ -8483,10 +8672,12 @@ static int FAST_FUNC builtin_read(char **argv) | |||
8483 | if (read_flags == (uint32_t)-1) | 8672 | if (read_flags == (uint32_t)-1) |
8484 | return EXIT_FAILURE; | 8673 | return EXIT_FAILURE; |
8485 | argv += optind; | 8674 | argv += optind; |
8675 | ifs = get_local_var_value("IFS"); /* can be NULL */ | ||
8486 | 8676 | ||
8677 | again: | ||
8487 | r = shell_builtin_read(set_local_var_from_halves, | 8678 | r = shell_builtin_read(set_local_var_from_halves, |
8488 | argv, | 8679 | argv, |
8489 | get_local_var_value("IFS"), /* can be NULL */ | 8680 | ifs, |
8490 | read_flags, | 8681 | read_flags, |
8491 | opt_n, | 8682 | opt_n, |
8492 | opt_p, | 8683 | opt_p, |
@@ -8494,6 +8685,12 @@ static int FAST_FUNC builtin_read(char **argv) | |||
8494 | opt_u | 8685 | opt_u |
8495 | ); | 8686 | ); |
8496 | 8687 | ||
8688 | if ((uintptr_t)r == 1 && errno == EINTR) { | ||
8689 | unsigned sig = check_and_run_traps(); | ||
8690 | if (sig && sig != SIGINT) | ||
8691 | goto again; | ||
8692 | } | ||
8693 | |||
8497 | if ((uintptr_t)r > 1) { | 8694 | if ((uintptr_t)r > 1) { |
8498 | bb_error_msg("%s", r); | 8695 | bb_error_msg("%s", r); |
8499 | r = (char*)(uintptr_t)1; | 8696 | r = (char*)(uintptr_t)1; |
@@ -8726,7 +8923,7 @@ static int FAST_FUNC builtin_unset(char **argv) | |||
8726 | static int FAST_FUNC builtin_wait(char **argv) | 8923 | static int FAST_FUNC builtin_wait(char **argv) |
8727 | { | 8924 | { |
8728 | int ret = EXIT_SUCCESS; | 8925 | int ret = EXIT_SUCCESS; |
8729 | int status, sig; | 8926 | int status; |
8730 | 8927 | ||
8731 | argv = skip_dash_dash(argv); | 8928 | argv = skip_dash_dash(argv); |
8732 | if (argv[0] == NULL) { | 8929 | if (argv[0] == NULL) { |
@@ -8746,25 +8943,53 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
8746 | * ^C <-- after ~4 sec from keyboard | 8943 | * ^C <-- after ~4 sec from keyboard |
8747 | * $ | 8944 | * $ |
8748 | */ | 8945 | */ |
8749 | sigaddset(&G.blocked_set, SIGCHLD); | ||
8750 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
8751 | while (1) { | 8946 | while (1) { |
8752 | checkjobs(NULL); | 8947 | int sig; |
8753 | if (errno == ECHILD) | 8948 | sigset_t oldset, allsigs; |
8949 | |||
8950 | /* waitpid is not interruptible by SA_RESTARTed | ||
8951 | * signals which we use. Thus, this ugly dance: | ||
8952 | */ | ||
8953 | |||
8954 | /* Make sure possible SIGCHLD is stored in kernel's | ||
8955 | * pending signal mask before we call waitpid. | ||
8956 | * Or else we may race with SIGCHLD, lose it, | ||
8957 | * and get stuck in sigwaitinfo... | ||
8958 | */ | ||
8959 | sigfillset(&allsigs); | ||
8960 | sigprocmask(SIG_SETMASK, &allsigs, &oldset); | ||
8961 | |||
8962 | if (!sigisemptyset(&G.pending_set)) { | ||
8963 | /* Crap! we raced with some signal! */ | ||
8964 | // sig = 0; | ||
8965 | goto restore; | ||
8966 | } | ||
8967 | |||
8968 | checkjobs(NULL); /* waitpid(WNOHANG) inside */ | ||
8969 | if (errno == ECHILD) { | ||
8970 | sigprocmask(SIG_SETMASK, &oldset, NULL); | ||
8971 | break; | ||
8972 | } | ||
8973 | |||
8974 | /* Wait for SIGCHLD or any other signal */ | ||
8975 | //sig = sigwaitinfo(&allsigs, NULL); | ||
8976 | /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */ | ||
8977 | /* Note: sigsuspend invokes signal handler */ | ||
8978 | sigsuspend(&oldset); | ||
8979 | restore: | ||
8980 | sigprocmask(SIG_SETMASK, &oldset, NULL); | ||
8981 | |||
8982 | /* So, did we get a signal? */ | ||
8983 | //if (sig > 0) | ||
8984 | // raise(sig); /* run handler */ | ||
8985 | sig = check_and_run_traps(); | ||
8986 | if (sig /*&& sig != SIGCHLD - always true */) { | ||
8987 | /* see note 2 */ | ||
8988 | ret = 128 + sig; | ||
8754 | break; | 8989 | break; |
8755 | /* Wait for SIGCHLD or any other signal of interest */ | ||
8756 | /* sigtimedwait with infinite timeout: */ | ||
8757 | sig = sigwaitinfo(&G.blocked_set, NULL); | ||
8758 | if (sig > 0) { | ||
8759 | sig = check_and_run_traps(sig); | ||
8760 | if (sig && sig != SIGCHLD) { /* see note 2 */ | ||
8761 | ret = 128 + sig; | ||
8762 | break; | ||
8763 | } | ||
8764 | } | 8990 | } |
8991 | /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */ | ||
8765 | } | 8992 | } |
8766 | sigdelset(&G.blocked_set, SIGCHLD); | ||
8767 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
8768 | return ret; | 8993 | return ret; |
8769 | } | 8994 | } |
8770 | 8995 | ||
diff --git a/shell/hush_test/hush-misc/assignment4.right b/shell/hush_test/hush-misc/assignment4.right new file mode 100644 index 000000000..31c896f62 --- /dev/null +++ b/shell/hush_test/hush-misc/assignment4.right | |||
@@ -0,0 +1 @@ | |||
Done:0 | |||
diff --git a/shell/hush_test/hush-misc/assignment4.tests b/shell/hush_test/hush-misc/assignment4.tests new file mode 100755 index 000000000..6f46d0a33 --- /dev/null +++ b/shell/hush_test/hush-misc/assignment4.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # There was a bug where we misinterpreted assignments after 'do': | ||
2 | for i in 1; do eval b=; done | ||
3 | echo Done:$? | ||
diff --git a/shell/hush_test/hush-misc/while3.right b/shell/hush_test/hush-misc/while3.right new file mode 100644 index 000000000..7c4d7beb0 --- /dev/null +++ b/shell/hush_test/hush-misc/while3.right | |||
@@ -0,0 +1 @@ | |||
OK:0 | |||
diff --git a/shell/hush_test/hush-misc/while3.tests b/shell/hush_test/hush-misc/while3.tests new file mode 100755 index 000000000..9132b5f4d --- /dev/null +++ b/shell/hush_test/hush-misc/while3.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | while false; do | ||
2 | # bash will require at least ":" here... | ||
3 | done | ||
4 | echo OK:$? | ||
diff --git a/shell/hush_test/hush-trap/signal_read1.right b/shell/hush_test/hush-trap/signal_read1.right new file mode 100644 index 000000000..2870a8e70 --- /dev/null +++ b/shell/hush_test/hush-trap/signal_read1.right | |||
@@ -0,0 +1 @@ | |||
Got HUP:0 | |||
diff --git a/shell/hush_test/hush-trap/signal_read1.tests b/shell/hush_test/hush-trap/signal_read1.tests new file mode 100755 index 000000000..1105479a3 --- /dev/null +++ b/shell/hush_test/hush-trap/signal_read1.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | (sleep 1; kill -HUP $$) & | ||
2 | trap 'echo "Got HUP:$?"; exit' HUP | ||
3 | while true; do | ||
4 | read ignored | ||
5 | done | ||
diff --git a/shell/hush_test/hush-trap/signal_read2.right b/shell/hush_test/hush-trap/signal_read2.right new file mode 100644 index 000000000..71a6bc16d --- /dev/null +++ b/shell/hush_test/hush-trap/signal_read2.right | |||
@@ -0,0 +1,2 @@ | |||
1 | HUP | ||
2 | Done:129 | ||
diff --git a/shell/hush_test/hush-trap/signal_read2.tests b/shell/hush_test/hush-trap/signal_read2.tests new file mode 100755 index 000000000..eab5b9b5b --- /dev/null +++ b/shell/hush_test/hush-trap/signal_read2.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | $THIS_SH -c ' | ||
2 | (sleep 1; kill -HUP $$) & | ||
3 | while true; do | ||
4 | read ignored | ||
5 | done | ||
6 | ' | ||
7 | echo "Done:$?" | ||
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all index 256f152dc..64a7abc47 100755 --- a/shell/hush_test/run-all +++ b/shell/hush_test/run-all | |||
@@ -48,8 +48,9 @@ do_test() | |||
48 | *.orig|*~) ;; | 48 | *.orig|*~) ;; |
49 | #*) echo $x ; sh $x ;; | 49 | #*) echo $x ; sh $x ;; |
50 | *) | 50 | *) |
51 | echo -n "$1/$x:" | ||
51 | sh "$x" >"../$1-$x.fail" 2>&1 && \ | 52 | sh "$x" >"../$1-$x.fail" 2>&1 && \ |
52 | { echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail"; | 53 | { { echo " ok"; rm "../$1-$x.fail"; } || echo " fail"; } |
53 | ;; | 54 | ;; |
54 | esac | 55 | esac |
55 | done | 56 | done |
@@ -60,6 +61,7 @@ do_test() | |||
60 | name="${x%%.tests}" | 61 | name="${x%%.tests}" |
61 | test -f "$name.right" || continue | 62 | test -f "$name.right" || continue |
62 | # echo Running test: "$x" | 63 | # echo Running test: "$x" |
64 | echo -n "$1/$x:" | ||
63 | ( | 65 | ( |
64 | "$THIS_SH" "./$x" >"$name.xx" 2>&1 | 66 | "$THIS_SH" "./$x" >"$name.xx" 2>&1 |
65 | # filter C library differences | 67 | # filter C library differences |
@@ -70,9 +72,9 @@ do_test() | |||
70 | diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" | 72 | diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" |
71 | ) | 73 | ) |
72 | case $? in | 74 | case $? in |
73 | 0) echo "$1/$x: ok";; | 75 | 0) echo " ok";; |
74 | 77) echo "$1/$x: skip (feature disabled)";; | 76 | 77) echo " skip (feature disabled)";; |
75 | *) echo "$1/$x: fail"; tret=1;; | 77 | *) echo " fail"; tret=1;; |
76 | esac | 78 | esac |
77 | done | 79 | done |
78 | exit ${tret} | 80 | exit ${tret} |
diff --git a/shell/shell_common.c b/shell/shell_common.c index 75f4b3e54..4329ca05c 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c | |||
@@ -36,6 +36,10 @@ int FAST_FUNC is_well_formed_var_name(const char *s, char terminator) | |||
36 | 36 | ||
37 | /* read builtin */ | 37 | /* read builtin */ |
38 | 38 | ||
39 | /* Needs to be interruptible: shell mush handle traps and shell-special signals | ||
40 | * while inside read. To implement this, be sure to not loop on EINTR | ||
41 | * and return errno == EINTR reliably. | ||
42 | */ | ||
39 | //TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL" | 43 | //TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL" |
40 | //string. hush naturally has it, and ash has setvareq(). | 44 | //string. hush naturally has it, and ash has setvareq(). |
41 | //Here we can simply store "VAR=" at buffer start and store read data directly | 45 | //Here we can simply store "VAR=" at buffer start and store read data directly |
@@ -51,6 +55,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
51 | const char *opt_u | 55 | const char *opt_u |
52 | ) | 56 | ) |
53 | { | 57 | { |
58 | unsigned err; | ||
54 | unsigned end_ms; /* -t TIMEOUT */ | 59 | unsigned end_ms; /* -t TIMEOUT */ |
55 | int fd; /* -u FD */ | 60 | int fd; /* -u FD */ |
56 | int nchars; /* -n NUM */ | 61 | int nchars; /* -n NUM */ |
@@ -62,6 +67,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
62 | int startword; | 67 | int startword; |
63 | smallint backslash; | 68 | smallint backslash; |
64 | 69 | ||
70 | errno = err = 0; | ||
71 | |||
65 | pp = argv; | 72 | pp = argv; |
66 | while (*pp) { | 73 | while (*pp) { |
67 | if (!is_well_formed_var_name(*pp, '\0')) { | 74 | if (!is_well_formed_var_name(*pp, '\0')) { |
@@ -152,28 +159,40 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
152 | bufpos = 0; | 159 | bufpos = 0; |
153 | do { | 160 | do { |
154 | char c; | 161 | char c; |
162 | struct pollfd pfd[1]; | ||
163 | int timeout; | ||
155 | 164 | ||
156 | if (end_ms) { | 165 | if ((bufpos & 0xff) == 0) |
157 | int timeout; | 166 | buffer = xrealloc(buffer, bufpos + 0x100); |
158 | struct pollfd pfd[1]; | ||
159 | 167 | ||
160 | pfd[0].fd = fd; | 168 | timeout = -1; |
161 | pfd[0].events = POLLIN; | 169 | if (end_ms) { |
162 | timeout = end_ms - (unsigned)monotonic_ms(); | 170 | timeout = end_ms - (unsigned)monotonic_ms(); |
163 | if (timeout <= 0 /* already late? */ | 171 | if (timeout <= 0) { /* already late? */ |
164 | || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */ | ||
165 | ) { /* timed out! */ | ||
166 | retval = (const char *)(uintptr_t)1; | 172 | retval = (const char *)(uintptr_t)1; |
167 | goto ret; | 173 | goto ret; |
168 | } | 174 | } |
169 | } | 175 | } |
170 | 176 | ||
171 | if ((bufpos & 0xff) == 0) | 177 | /* We must poll even if timeout is -1: |
172 | buffer = xrealloc(buffer, bufpos + 0x100); | 178 | * we want to be interrupted if signal arrives, |
173 | if (nonblock_safe_read(fd, &buffer[bufpos], 1) != 1) { | 179 | * regardless of SA_RESTART-ness of that signal! |
180 | */ | ||
181 | errno = 0; | ||
182 | pfd[0].fd = fd; | ||
183 | pfd[0].events = POLLIN; | ||
184 | if (poll(pfd, 1, timeout) != 1) { | ||
185 | /* timed out, or EINTR */ | ||
186 | err = errno; | ||
187 | retval = (const char *)(uintptr_t)1; | ||
188 | goto ret; | ||
189 | } | ||
190 | if (read(fd, &buffer[bufpos], 1) != 1) { | ||
191 | err = errno; | ||
174 | retval = (const char *)(uintptr_t)1; | 192 | retval = (const char *)(uintptr_t)1; |
175 | break; | 193 | break; |
176 | } | 194 | } |
195 | |||
177 | c = buffer[bufpos]; | 196 | c = buffer[bufpos]; |
178 | if (c == '\0' || (ENABLE_PLATFORM_MINGW32 && c == '\r')) | 197 | if (c == '\0' || (ENABLE_PLATFORM_MINGW32 && c == '\r')) |
179 | continue; | 198 | continue; |
@@ -240,6 +259,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
240 | free(buffer); | 259 | free(buffer); |
241 | if (read_flags & BUILTIN_READ_SILENT) | 260 | if (read_flags & BUILTIN_READ_SILENT) |
242 | tcsetattr(fd, TCSANOW, &old_tty); | 261 | tcsetattr(fd, TCSANOW, &old_tty); |
262 | |||
263 | errno = err; | ||
243 | return retval; | 264 | return retval; |
244 | } | 265 | } |
245 | 266 | ||
diff --git a/sysklogd/klogd.c b/sysklogd/klogd.c index 3992081ca..27995e570 100644 --- a/sysklogd/klogd.c +++ b/sysklogd/klogd.c | |||
@@ -22,7 +22,7 @@ | |||
22 | //usage:#define klogd_full_usage "\n\n" | 22 | //usage:#define klogd_full_usage "\n\n" |
23 | //usage: "Kernel logger\n" | 23 | //usage: "Kernel logger\n" |
24 | //usage: "\nOptions:" | 24 | //usage: "\nOptions:" |
25 | //usage: "\n -c N Only messages with level < N are printed to console" | 25 | //usage: "\n -c N Print to console messages more urgent than prio N (1-8)" |
26 | //usage: "\n -n Run in foreground" | 26 | //usage: "\n -n Run in foreground" |
27 | 27 | ||
28 | #include "libbb.h" | 28 | #include "libbb.h" |
diff --git a/sysklogd/syslogd.c b/sysklogd/syslogd.c index b6f409f41..c5c2a2e86 100644 --- a/sysklogd/syslogd.c +++ b/sysklogd/syslogd.c | |||
@@ -16,26 +16,33 @@ | |||
16 | //usage:#define syslogd_trivial_usage | 16 | //usage:#define syslogd_trivial_usage |
17 | //usage: "[OPTIONS]" | 17 | //usage: "[OPTIONS]" |
18 | //usage:#define syslogd_full_usage "\n\n" | 18 | //usage:#define syslogd_full_usage "\n\n" |
19 | //usage: "System logging utility.\n" | 19 | //usage: "System logging utility\n" |
20 | //usage: "This version of syslogd ignores /etc/syslog.conf\n" | 20 | //usage: IF_NOT_FEATURE_SYSLOGD_CFG( |
21 | //usage: "(this version of syslogd ignores /etc/syslog.conf)\n" | ||
22 | //usage: ) | ||
21 | //usage: "\nOptions:" | 23 | //usage: "\nOptions:" |
22 | //usage: "\n -n Run in foreground" | 24 | //usage: "\n -n Run in foreground" |
23 | //usage: "\n -O FILE Log to given file (default:/var/log/messages)" | 25 | //usage: "\n -O FILE Log to FILE (default:/var/log/messages)" |
24 | //usage: "\n -l N Set local log level" | 26 | //usage: "\n -l N Log only messages more urgent than prio N (1-8)" |
25 | //usage: "\n -S Smaller logging output" | 27 | //usage: "\n -S Smaller output" |
26 | //usage: IF_FEATURE_ROTATE_LOGFILE( | 28 | //usage: IF_FEATURE_ROTATE_LOGFILE( |
27 | //usage: "\n -s SIZE Max size (KB) before rotate (default:200KB, 0=off)" | 29 | //usage: "\n -s SIZE Max size (KB) before rotation (default:200KB, 0=off)" |
28 | //usage: "\n -b N N rotated logs to keep (default:1, max=99, 0=purge)") | 30 | //usage: "\n -b N N rotated logs to keep (default:1, max=99, 0=purge)" |
31 | //usage: ) | ||
29 | //usage: IF_FEATURE_REMOTE_LOG( | 32 | //usage: IF_FEATURE_REMOTE_LOG( |
30 | //usage: "\n -R HOST[:PORT] Log to IP or hostname on PORT (default PORT=514/UDP)" | 33 | //usage: "\n -R HOST[:PORT] Log to IP or hostname on PORT (default PORT=514/UDP)" |
31 | //usage: "\n -L Log locally and via network (default is network only if -R)") | 34 | //usage: "\n -L Log locally and via network (default is network only if -R)" |
35 | //usage: ) | ||
32 | //usage: IF_FEATURE_SYSLOGD_DUP( | 36 | //usage: IF_FEATURE_SYSLOGD_DUP( |
33 | //usage: "\n -D Drop duplicates") | 37 | //usage: "\n -D Drop duplicates" |
38 | //usage: ) | ||
34 | //usage: IF_FEATURE_IPC_SYSLOG( | 39 | //usage: IF_FEATURE_IPC_SYSLOG( |
35 | //usage: "\n -C[size(KiB)] Log to shared mem buffer (read it using logread)") | ||
36 | //usage: IF_FEATURE_SYSLOGD_CFG( | ||
37 | //usage: "\n -f FILE Use FILE as config (default is /etc/syslog.conf)") | ||
38 | /* NB: -Csize shouldn't have space (because size is optional) */ | 40 | /* NB: -Csize shouldn't have space (because size is optional) */ |
41 | //usage: "\n -C[size_kb] Log to shared mem buffer (use logread to read it)" | ||
42 | //usage: ) | ||
43 | //usage: IF_FEATURE_SYSLOGD_CFG( | ||
44 | //usage: "\n -f FILE Use FILE as config (default:/etc/syslog.conf)" | ||
45 | //usage: ) | ||
39 | /* //usage: "\n -m MIN Minutes between MARK lines (default:20, 0=off)" */ | 46 | /* //usage: "\n -m MIN Minutes between MARK lines (default:20, 0=off)" */ |
40 | //usage: | 47 | //usage: |
41 | //usage:#define syslogd_example_usage | 48 | //usage:#define syslogd_example_usage |
diff --git a/testsuite/od.tests b/testsuite/od.tests index 138efedea..fa47b4790 100755 --- a/testsuite/od.tests +++ b/testsuite/od.tests | |||
@@ -4,7 +4,7 @@ | |||
4 | 4 | ||
5 | . ./testing.sh | 5 | . ./testing.sh |
6 | 6 | ||
7 | # testing "test name" "options" "expected result" "file input" "stdin" | 7 | # testing "test name" "commands" "expected result" "file input" "stdin" |
8 | 8 | ||
9 | optional DESKTOP | 9 | optional DESKTOP |
10 | testing "od -b" \ | 10 | testing "od -b" \ |
@@ -16,4 +16,24 @@ testing "od -b" \ | |||
16 | "" "HELLO" | 16 | "" "HELLO" |
17 | SKIP= | 17 | SKIP= |
18 | 18 | ||
19 | optional DESKTOP | ||
20 | testing "od -b --traditional" \ | ||
21 | "od -b --traditional" \ | ||
22 | "\ | ||
23 | 0000000 110 105 114 114 117 | ||
24 | 0000005 | ||
25 | " \ | ||
26 | "" "HELLO" | ||
27 | SKIP= | ||
28 | |||
29 | optional DESKTOP | ||
30 | testing "od -b --traditional FILE" \ | ||
31 | "od -b --traditional input" \ | ||
32 | "\ | ||
33 | 0000000 110 105 114 114 117 | ||
34 | 0000005 | ||
35 | " \ | ||
36 | "HELLO" "" | ||
37 | SKIP= | ||
38 | |||
19 | exit $FAILCOUNT | 39 | exit $FAILCOUNT |
diff --git a/testsuite/sed.tests b/testsuite/sed.tests index 395372ae6..e9d0ed601 100755 --- a/testsuite/sed.tests +++ b/testsuite/sed.tests | |||
@@ -287,6 +287,9 @@ testing "sed -i with address modifies all files, not only first" \ | |||
287 | "cp input input2; sed -i -e '1s/foo/bar/' input input2 && cat input input2; rm input2" \ | 287 | "cp input input2; sed -i -e '1s/foo/bar/' input input2 && cat input input2; rm input2" \ |
288 | "bar\nbar\n" "foo\n" "" | 288 | "bar\nbar\n" "foo\n" "" |
289 | 289 | ||
290 | testing "sed understands \r" \ | ||
291 | "sed 's/r/\r/'" \ | ||
292 | "\rrr\n" "" "rrr\n" | ||
290 | 293 | ||
291 | # testing "description" "arguments" "result" "infile" "stdin" | 294 | # testing "description" "arguments" "result" "infile" "stdin" |
292 | 295 | ||
diff --git a/util-linux/acpid.c b/util-linux/acpid.c index c9eed2a7f..4b7e5cacb 100644 --- a/util-linux/acpid.c +++ b/util-linux/acpid.c | |||
@@ -283,7 +283,7 @@ int acpid_main(int argc UNUSED_PARAM, char **argv) | |||
283 | char *buf; | 283 | char *buf; |
284 | int len; | 284 | int len; |
285 | 285 | ||
286 | buf = xmalloc_reads(pfd[i].fd, NULL, NULL); | 286 | buf = xmalloc_reads(pfd[i].fd, NULL); |
287 | /* buf = "button/power PWRB 00000080 00000000" */ | 287 | /* buf = "button/power PWRB 00000080 00000000" */ |
288 | len = strlen(buf) - 9; | 288 | len = strlen(buf) - 9; |
289 | if (len >= 0) | 289 | if (len >= 0) |
diff --git a/util-linux/fbset.c b/util-linux/fbset.c index 75d41b882..3be342481 100644 --- a/util-linux/fbset.c +++ b/util-linux/fbset.c | |||
@@ -402,7 +402,14 @@ int fbset_main(int argc, char **argv) | |||
402 | argv++; | 402 | argv++; |
403 | argc--; | 403 | argc--; |
404 | for (; argc > 0 && (thisarg = *argv) != NULL; argc--, argv++) { | 404 | for (; argc > 0 && (thisarg = *argv) != NULL; argc--, argv++) { |
405 | if (thisarg[0] == '-') for (i = 0; i < ARRAY_SIZE(g_cmdoptions); i++) { | 405 | if (thisarg[0] != '-') { |
406 | if (!ENABLE_FEATURE_FBSET_READMODE || argc != 1) | ||
407 | bb_show_usage(); | ||
408 | mode = thisarg; | ||
409 | options |= OPT_READMODE; | ||
410 | goto contin; | ||
411 | } | ||
412 | for (i = 0; i < ARRAY_SIZE(g_cmdoptions); i++) { | ||
406 | if (strcmp(thisarg + 1, g_cmdoptions[i].name) != 0) | 413 | if (strcmp(thisarg + 1, g_cmdoptions[i].name) != 0) |
407 | continue; | 414 | continue; |
408 | if (argc <= g_cmdoptions[i].param_count) | 415 | if (argc <= g_cmdoptions[i].param_count) |
@@ -471,10 +478,7 @@ int fbset_main(int argc, char **argv) | |||
471 | argv += g_cmdoptions[i].param_count; | 478 | argv += g_cmdoptions[i].param_count; |
472 | goto contin; | 479 | goto contin; |
473 | } | 480 | } |
474 | if (!ENABLE_FEATURE_FBSET_READMODE || argc != 1) | 481 | bb_show_usage(); |
475 | bb_show_usage(); | ||
476 | mode = *argv; | ||
477 | options |= OPT_READMODE; | ||
478 | contin: ; | 482 | contin: ; |
479 | } | 483 | } |
480 | 484 | ||
diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index da03e683e..f4fd4d31d 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c | |||
@@ -2846,13 +2846,37 @@ open_list_and_close(const char *device, int user_specified) | |||
2846 | close_dev_fd(); | 2846 | close_dev_fd(); |
2847 | } | 2847 | } |
2848 | 2848 | ||
2849 | /* Is it a whole disk? The digit check is still useful | ||
2850 | for Xen devices for example. */ | ||
2851 | static int is_whole_disk(const char *disk) | ||
2852 | { | ||
2853 | unsigned len; | ||
2854 | int fd = open(disk, O_RDONLY); | ||
2855 | |||
2856 | if (fd != -1) { | ||
2857 | struct hd_geometry geometry; | ||
2858 | int err = ioctl(fd, HDIO_GETGEO, &geometry); | ||
2859 | close(fd); | ||
2860 | if (!err) | ||
2861 | return (geometry.start == 0); | ||
2862 | } | ||
2863 | |||
2864 | /* Treat "nameN" as a partition name, not whole disk */ | ||
2865 | /* note: mmcblk0 should work from the geometry check above */ | ||
2866 | len = strlen(disk); | ||
2867 | if (len != 0 && isdigit(disk[len - 1])) | ||
2868 | return 0; | ||
2869 | |||
2870 | return 1; | ||
2871 | } | ||
2872 | |||
2849 | /* for fdisk -l: try all things in /proc/partitions | 2873 | /* for fdisk -l: try all things in /proc/partitions |
2850 | that look like a partition name (do not end in a digit) */ | 2874 | that look like a partition name (do not end in a digit) */ |
2851 | static void | 2875 | static void |
2852 | list_devs_in_proc_partititons(void) | 2876 | list_devs_in_proc_partititons(void) |
2853 | { | 2877 | { |
2854 | FILE *procpt; | 2878 | FILE *procpt; |
2855 | char line[100], ptname[100], devname[120], *s; | 2879 | char line[100], ptname[100], devname[120]; |
2856 | int ma, mi, sz; | 2880 | int ma, mi, sz; |
2857 | 2881 | ||
2858 | procpt = fopen_or_warn("/proc/partitions", "r"); | 2882 | procpt = fopen_or_warn("/proc/partitions", "r"); |
@@ -2861,13 +2885,10 @@ list_devs_in_proc_partititons(void) | |||
2861 | if (sscanf(line, " %u %u %u %[^\n ]", | 2885 | if (sscanf(line, " %u %u %u %[^\n ]", |
2862 | &ma, &mi, &sz, ptname) != 4) | 2886 | &ma, &mi, &sz, ptname) != 4) |
2863 | continue; | 2887 | continue; |
2864 | for (s = ptname; *s; s++) | 2888 | |
2865 | continue; | ||
2866 | /* note: excluding '0': e.g. mmcblk0 is not a partition name! */ | ||
2867 | if (s[-1] >= '1' && s[-1] <= '9') | ||
2868 | continue; | ||
2869 | sprintf(devname, "/dev/%s", ptname); | 2889 | sprintf(devname, "/dev/%s", ptname); |
2870 | open_list_and_close(devname, 0); | 2890 | if (is_whole_disk(devname)) |
2891 | open_list_and_close(devname, 0); | ||
2871 | } | 2892 | } |
2872 | #if ENABLE_FEATURE_CLEAN_UP | 2893 | #if ENABLE_FEATURE_CLEAN_UP |
2873 | fclose(procpt); | 2894 | fclose(procpt); |
diff --git a/util-linux/getopt.c b/util-linux/getopt.c index 10e1dc49b..85ff76189 100644 --- a/util-linux/getopt.c +++ b/util-linux/getopt.c | |||
@@ -25,7 +25,7 @@ | |||
25 | * Added NLS support (partly written by Arkadiusz Mickiewicz | 25 | * Added NLS support (partly written by Arkadiusz Mickiewicz |
26 | * <misiek@misiek.eu.org>) | 26 | * <misiek@misiek.eu.org>) |
27 | * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org> | 27 | * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org> |
28 | * Removed --version/-V and --help/-h in | 28 | * Removed --version/-V and --help/-h |
29 | * Removed parse_error(), using bb_error_msg() from Busybox instead | 29 | * Removed parse_error(), using bb_error_msg() from Busybox instead |
30 | * Replaced our_malloc with xmalloc and our_realloc with xrealloc | 30 | * Replaced our_malloc with xmalloc and our_realloc with xrealloc |
31 | * | 31 | * |
@@ -79,7 +79,9 @@ | |||
79 | //usage: " esac\n" | 79 | //usage: " esac\n" |
80 | //usage: "done\n" | 80 | //usage: "done\n" |
81 | 81 | ||
82 | #include <getopt.h> | 82 | #if ENABLE_FEATURE_GETOPT_LONG |
83 | # include <getopt.h> | ||
84 | #endif | ||
83 | #include "libbb.h" | 85 | #include "libbb.h" |
84 | 86 | ||
85 | /* NON_OPT is the code that is returned when a non-option is found in '+' | 87 | /* NON_OPT is the code that is returned when a non-option is found in '+' |
diff --git a/util-linux/mdev.c b/util-linux/mdev.c index 2f225ac0b..7cabb1df6 100644 --- a/util-linux/mdev.c +++ b/util-linux/mdev.c | |||
@@ -292,7 +292,7 @@ static void make_device(char *path, int delete) | |||
292 | * the rest the line unless keep_matching == 1 */ | 292 | * the rest the line unless keep_matching == 1 */ |
293 | 293 | ||
294 | /* 2nd field: uid:gid - device ownership */ | 294 | /* 2nd field: uid:gid - device ownership */ |
295 | if (get_uidgid(&ugid, tokens[1], 1) == 0) | 295 | if (get_uidgid(&ugid, tokens[1], /*allow_numeric:*/ 1) == 0) |
296 | bb_error_msg("unknown user/group %s on line %d", tokens[1], parser->lineno); | 296 | bb_error_msg("unknown user/group %s on line %d", tokens[1], parser->lineno); |
297 | 297 | ||
298 | /* 3rd field: mode - device permissions */ | 298 | /* 3rd field: mode - device permissions */ |
diff --git a/util-linux/mount.c b/util-linux/mount.c index 3e2ba1fab..6a154e2b2 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c | |||
@@ -1195,7 +1195,7 @@ static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts) | |||
1195 | noac = 0; | 1195 | noac = 0; |
1196 | nordirplus = 0; | 1196 | nordirplus = 0; |
1197 | retry = 10000; /* 10000 minutes ~ 1 week */ | 1197 | retry = 10000; /* 10000 minutes ~ 1 week */ |
1198 | tcp = 0; | 1198 | tcp = 1; /* nfs-utils uses tcp per default */ |
1199 | 1199 | ||
1200 | mountprog = MOUNTPROG; | 1200 | mountprog = MOUNTPROG; |
1201 | mountvers = 0; | 1201 | mountvers = 0; |