From fb183076a3a6580a4aba435c53ce033ef89e7fe6 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 13 Mar 2014 12:52:43 +0100 Subject: ash,hush: improve randomness of $RANDOM, add easy-ish way to test it function old new delta next_random 68 113 +45 change_random 103 121 +18 Signed-off-by: Denys Vlasenko --- shell/random.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++------ shell/random.h | 16 +++++-- 2 files changed, 135 insertions(+), 17 deletions(-) diff --git a/shell/random.c b/shell/random.c index 853ab085a..9a64f54b0 100644 --- a/shell/random.c +++ b/shell/random.c @@ -6,17 +6,51 @@ * * Licensed under GPLv2, see file LICENSE in this source tree. */ -#include "libbb.h" -#include "random.h" + +/* For testing against dieharder, you need only random.{c,h} + * Howto: + * gcc -O2 -Wall -DRANDTEST random.c -o random + * ./random | dieharder -g 200 -a + */ + +#if !defined RANDTEST + +# include "libbb.h" +# include "random.h" +# define RAND_BASH_MASK 0x7fff + +#else +# include +# include +# include +# include +# define RAND_BASH_MASK 0xffffffff /* off */ +# define FAST_FUNC /* nothing */ +# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* nothing */ +# define POP_SAVED_FUNCTION_VISIBILITY /* nothing */ +# define monotonic_us() time(NULL) +# include "random.h" +#endif uint32_t FAST_FUNC next_random(random_t *rnd) { - /* Galois LFSR parameter */ - /* Taps at 32 31 29 1: */ + /* Galois LFSR parameter: + * Taps at 32 31 29 1: + */ enum { MASK = 0x8000000b }; /* Another example - taps at 32 31 30 10: */ - /* MASK = 0x00400007 */ + /* enum { MASK = 0x00400007 }; */ + + /* Xorshift parameters: + * Choices for a,b,c: 10,13,10; 8,9,22; 2,7,3; 23,3,24 + * (given by algorithm author) + */ + enum { + a = 2, + b = 7, + c = 3, + }; uint32_t t; @@ -27,18 +61,94 @@ next_random(random_t *rnd) INIT_RANDOM_T(rnd, getpid(), monotonic_us()); } - /* LCG has period of 2^32 and alternating lowest bit */ + /* LCG: period of 2^32, but quite weak: + * bit 0 alternates beetween 0 and 1 (pattern of length 2) + * bit 1 has a repeating pattern of length 4 + * bit 2 has a repeating pattern of length 8 + * etc... + */ rnd->LCG = 1664525 * rnd->LCG + 1013904223; - /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */ + + /* Galois LFSR: + * period of 2^32-1 = 3 * 5 * 17 * 257 * 65537. + * Successive values are right-shifted one bit + * and possibly xored with a sparse constant. + */ t = (rnd->galois_LFSR << 1); if (rnd->galois_LFSR < 0) /* if we just shifted 1 out of msb... */ t ^= MASK; rnd->galois_LFSR = t; - /* Both are weak, combining them gives better randomness - * and ~2^64 period. & 0x7fff is probably bash compat - * for $RANDOM range. Combining with subtraction is - * just for fun. + and ^ would work equally well. */ - t = (t - rnd->LCG) & 0x7fff; - return t; + /* http://en.wikipedia.org/wiki/Xorshift + * Period 2^64-1 = 3 * 715827883 * 2147483647 + * Moderately good statistical properties: + * fails the following "dieharder -g 200 -a" tests: + * diehard_operm5| 0 + * diehard_oqso| 0 + * diehard_count_1s_byt| 0 + * diehard_3dsphere| 3 + * diehard_squeeze| 0 + * diehard_runs| 0 + * diehard_runs| 0 + * diehard_craps| 0 + * diehard_craps| 0 + * rgb_minimum_distance| 3 + * rgb_minimum_distance| 4 + * rgb_minimum_distance| 5 + * rgb_permutations| 3 + * rgb_permutations| 4 + * rgb_permutations| 5 + * dab_filltree| 32 + * dab_filltree| 32 + * dab_monobit2| 12 + */ + t = rnd->xs64_x ^ (rnd->xs64_x << a); + rnd->xs64_x = rnd->xs64_y; + rnd->xs64_y = rnd->xs64_y ^ (rnd->xs64_y >> c) ^ t ^ (t >> b); + + /* Combined LCG + Galois LFSR have 2^32 * 2^32-1 period. + * Strength: + * individually, both are extremely weak cryptographycally; + * when combined, they fail the following "dieharder -g 200 -a" tests: + * diehard_rank_6x8| 0 + * diehard_oqso| 0 + * diehard_dna| 0 + * diehard_count_1s_byt| 0 + * rgb_bitdist| 2 + * dab_monobit2| 12 + * + * Combining them with xorshift-64 increases period to + * 2^32 * 2^32-1 * 2^64-1 / 3 + * (2^32-1 and 2^64-1 have one common divisor 3, hence "/ 3" part), + * which is about 2^128 / 3, or in base 10 ~1.13*10^38. + * Strength of the combination: + * passes all "dieharder -g 200 -a" tests. + * + * Combining with subtraction and addition is just for fun. + * It does not add meaningful strength, could use xor operation instead. + */ + t = rnd->galois_LFSR - rnd->LCG + rnd->xs64_y; + + /* bash compat $RANDOM range: */ + return t & RAND_BASH_MASK; } + +#ifdef RANDTEST +static random_t rnd; + +int main(int argc, char **argv) +{ + int i; + int buf[4096]; + + for (;;) { + for (i = 0; i < sizeof(buf) / sizeof(buf[0]); i++) { + buf[i] = next_random(&rnd); + } + write(1, buf, sizeof(buf)); + } + + return 0; +} + +#endif diff --git a/shell/random.h b/shell/random.h index 180c48abb..c4eb44c13 100644 --- a/shell/random.h +++ b/shell/random.h @@ -12,16 +12,24 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN typedef struct random_t { - /* Random number generators */ - int32_t galois_LFSR; /* Galois LFSR (fast but weak). signed! */ - uint32_t LCG; /* LCG (fast but weak) */ + /* State of random number generators: */ + + /* Galois LFSR (fast but weak) */ + int32_t galois_LFSR; /* must be signed! */ + + /* LCG (fast but weak) */ + uint32_t LCG; + + /* 64-bit xorshift (fast, moderate strength) */ + uint32_t xs64_x; + uint32_t xs64_y; } random_t; #define UNINITED_RANDOM_T(rnd) \ ((rnd)->galois_LFSR == 0) #define INIT_RANDOM_T(rnd, nonzero, v) \ - ((rnd)->galois_LFSR = (nonzero), (rnd)->LCG = (v)) + ((rnd)->galois_LFSR = (rnd)->xs64_x = (nonzero), (rnd)->LCG = (rnd)->xs64_y = (v)) #define CLEAR_RANDOM_T(rnd) \ ((rnd)->galois_LFSR = 0) -- cgit v1.2.3-55-g6feb From f93187512d0a8db6b7160088d3d0b325dd83c937 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 13 Mar 2014 13:03:27 +0100 Subject: random: cosmetic tweaks in debug code Signed-off-by: Denys Vlasenko --- shell/random.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/random.c b/shell/random.c index 9a64f54b0..fc2dfc26d 100644 --- a/shell/random.c +++ b/shell/random.c @@ -24,12 +24,12 @@ # include # include # include -# define RAND_BASH_MASK 0xffffffff /* off */ # define FAST_FUNC /* nothing */ # define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* nothing */ # define POP_SAVED_FUNCTION_VISIBILITY /* nothing */ # define monotonic_us() time(NULL) # include "random.h" +# define RAND_BASH_MASK 0xffffffff /* off */ #endif uint32_t FAST_FUNC @@ -139,7 +139,7 @@ static random_t rnd; int main(int argc, char **argv) { int i; - int buf[4096]; + uint32_t buf[4096]; for (;;) { for (i = 0; i < sizeof(buf) / sizeof(buf[0]); i++) { -- cgit v1.2.3-55-g6feb From 2bba591991f5ac9b97582e37375dd49492c63df0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 14 Mar 2014 12:43:57 +0100 Subject: hush: make "true" built-in Signed-off-by: Denys Vlasenko --- shell/hush.c | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/hush.c b/shell/hush.c index 927193450..7b0ea8b0c 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -944,6 +944,7 @@ static const struct built_in_command bltins1[] = { BLTIN("source" , builtin_source , "Run commands in a file"), #endif BLTIN("trap" , builtin_trap , "Trap signals"), + BLTIN("true" , builtin_true , NULL), BLTIN("type" , builtin_type , "Show command type"), BLTIN("ulimit" , shell_builtin_ulimit , "Control resource limits"), BLTIN("umask" , builtin_umask , "Set file creation mask"), -- cgit v1.2.3-55-g6feb From ac03a40cbac4545909b6529d358c253f8a6d3b45 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 15 Mar 2014 09:25:46 +0100 Subject: ash,hush: fix a thinko about 2^64-1 factorization function old new delta next_random 113 119 +6 Signed-off-by: Denys Vlasenko --- shell/random.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/shell/random.c b/shell/random.c index fc2dfc26d..5d3620516 100644 --- a/shell/random.c +++ b/shell/random.c @@ -80,7 +80,6 @@ next_random(random_t *rnd) rnd->galois_LFSR = t; /* http://en.wikipedia.org/wiki/Xorshift - * Period 2^64-1 = 3 * 715827883 * 2147483647 * Moderately good statistical properties: * fails the following "dieharder -g 200 -a" tests: * diehard_operm5| 0 @@ -102,11 +101,19 @@ next_random(random_t *rnd) * dab_filltree| 32 * dab_monobit2| 12 */ + again: t = rnd->xs64_x ^ (rnd->xs64_x << a); rnd->xs64_x = rnd->xs64_y; rnd->xs64_y = rnd->xs64_y ^ (rnd->xs64_y >> c) ^ t ^ (t >> b); + /* + * Period 2^64-1 = 2^32+1 * 2^32-1 has a common divisor with Galois LFSR. + * By skipping two possible states (0x1 and 0x2) we reduce period to + * 2^64-3 = 13 * 3889 * 364870227143809 which has no common divisors: + */ + if (rnd->xs64_y == 0 && rnd->xs64_x <= 2) + goto again; - /* Combined LCG + Galois LFSR have 2^32 * 2^32-1 period. + /* Combined LCG + Galois LFSR rng has 2^32 * 2^32-1 period. * Strength: * individually, both are extremely weak cryptographycally; * when combined, they fail the following "dieharder -g 200 -a" tests: @@ -118,9 +125,8 @@ next_random(random_t *rnd) * dab_monobit2| 12 * * Combining them with xorshift-64 increases period to - * 2^32 * 2^32-1 * 2^64-1 / 3 - * (2^32-1 and 2^64-1 have one common divisor 3, hence "/ 3" part), - * which is about 2^128 / 3, or in base 10 ~1.13*10^38. + * 2^32 * 2^32-1 * 2^64-3 + * which is about 2^128, or in base 10 ~3.40*10^38. * Strength of the combination: * passes all "dieharder -g 200 -a" tests. * -- cgit v1.2.3-55-g6feb From 0e3334c8d8f021ed87d873ac95e165cde9da5e4d Mon Sep 17 00:00:00 2001 From: Romain Naour Date: Sun, 16 Mar 2014 11:18:19 +0100 Subject: su: add a delay if the password is incorrect Signed-off-by: Romain Naour Signed-off-by: Denys Vlasenko --- loginutils/su.c | 1 + 1 file changed, 1 insertion(+) diff --git a/loginutils/su.c b/loginutils/su.c index c51f26f70..f8125054a 100644 --- a/loginutils/su.c +++ b/loginutils/su.c @@ -101,6 +101,7 @@ int su_main(int argc UNUSED_PARAM, char **argv) if (ENABLE_FEATURE_SU_SYSLOG) syslog(LOG_NOTICE, "%c %s %s:%s", '-', tty, old_user, opt_username); + bb_do_delay(LOGIN_FAIL_DELAY); bb_error_msg_and_die("incorrect password"); } -- cgit v1.2.3-55-g6feb From 29f352604868c5d17c81948ea30e332491cecada Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 16 Mar 2014 12:05:58 +0100 Subject: build system: for "find", use POSIX not operator (!) instead of -not Signed-off-by: Denys Vlasenko --- scripts/gen_build_files.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/gen_build_files.sh b/scripts/gen_build_files.sh index e8fa831be..ebee17c64 100755 --- a/scripts/gen_build_files.sh +++ b/scripts/gen_build_files.sh @@ -71,7 +71,7 @@ sed -n -e 's@^//usage:\([ '"$TAB"'].*\)$@\1 \\@p' \ # (Re)generate */Kbuild and */Config.in # We skip .dotdirs - makes git/svn/etc users happier -{ cd -- "$srctree" && find . -type d -not '(' -name '.?*' -prune ')'; } \ +{ cd -- "$srctree" && find . -type d ! '(' -name '.?*' -prune ')'; } \ | while read -r d; do d="${d#./}" -- cgit v1.2.3-55-g6feb From 438b4ac2860f4aec5b5fb82c47f75a0cb80d9cb4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 16 Mar 2014 12:34:53 +0100 Subject: script: make it work even if fd 0 is closed Testcase: script -q -c "echo hey" /dev/null 0>&- Signed-off-by: Denys Vlasenko --- libbb/getpty.c | 2 +- util-linux/script.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libbb/getpty.c b/libbb/getpty.c index 435e4d09f..391d729f2 100644 --- a/libbb/getpty.c +++ b/libbb/getpty.c @@ -16,7 +16,7 @@ int FAST_FUNC xgetpty(char *line) #if ENABLE_FEATURE_DEVPTS p = open("/dev/ptmx", O_RDWR); - if (p > 0) { + if (p >= 0) { grantpt(p); /* chmod+chown corresponding slave pty */ unlockpt(p); /* (what does this do?) */ # ifndef HAVE_PTSNAME_R diff --git a/util-linux/script.c b/util-linux/script.c index 8fb991d15..abcd73bff 100644 --- a/util-linux/script.c +++ b/util-linux/script.c @@ -77,8 +77,15 @@ int script_main(int argc UNUSED_PARAM, char **argv) if (!(opt & OPT_q)) { printf("Script started, file is %s\n", fname); } + shell = get_shell_name(); + /* Some people run "script ... 0>&-". + * Our code assumes that STDIN_FILENO != pty. + * Ensure STDIN_FILENO is not closed: + */ + bb_sanitize_stdio(); + pty = xgetpty(pty_line); /* get current stdin's tty params */ -- cgit v1.2.3-55-g6feb From 109ee5d33694a03cda3424b4846584250832ba8e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 16 Mar 2014 18:41:11 +0100 Subject: ash: make "locak VAR" unset VAR (bash does that) Signed-off-by: Denys Vlasenko --- shell/ash.c | 3 +++ shell/ash_test/ash-misc/local1.right | 4 ++++ shell/ash_test/ash-misc/local1.tests | 11 +++++++++++ 3 files changed, 18 insertions(+) create mode 100644 shell/ash_test/ash-misc/local1.right create mode 100755 shell/ash_test/ash-misc/local1.tests diff --git a/shell/ash.c b/shell/ash.c index 04ba447b1..7a097c814 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -8960,6 +8960,9 @@ mklocal(char *name) vp->flags |= VSTRFIXED|VTEXTFIXED; if (eq) setvareq(name, 0); + else + /* "local VAR" unsets VAR: */ + setvar(name, NULL, 0); } } lvp->vp = vp; diff --git a/shell/ash_test/ash-misc/local1.right b/shell/ash_test/ash-misc/local1.right new file mode 100644 index 000000000..a2d121df6 --- /dev/null +++ b/shell/ash_test/ash-misc/local1.right @@ -0,0 +1,4 @@ +A1:'A' +A2:'' +A3:'' +A4:'A' diff --git a/shell/ash_test/ash-misc/local1.tests b/shell/ash_test/ash-misc/local1.tests new file mode 100755 index 000000000..b1e675059 --- /dev/null +++ b/shell/ash_test/ash-misc/local1.tests @@ -0,0 +1,11 @@ +a=A +f() { + local a + # the above line unsets $a + echo "A2:'$a'" + unset a + echo "A3:'$a'" +} +echo "A1:'$a'" +f +echo "A4:'$a'" -- cgit v1.2.3-55-g6feb From 9dbe4d054703ac96355c90bd7794d14d206e10c5 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 12 Mar 2014 22:43:50 +0100 Subject: docs: update new-applet-HOWTO.txt This patch adds some information about the gen_build_files.sh script and how it allows to keep the Kbuild, Config.in etc. declarations in .c files. Signed-off-by: Bartosz Golaszewski Signed-off-by: Denys Vlasenko --- docs/new-applet-HOWTO.txt | 97 ++++++++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 35 deletions(-) diff --git a/docs/new-applet-HOWTO.txt b/docs/new-applet-HOWTO.txt index 6a8054d0e..28970a50a 100644 --- a/docs/new-applet-HOWTO.txt +++ b/docs/new-applet-HOWTO.txt @@ -16,10 +16,10 @@ Initial Write First, write your applet. Be sure to include copyright information at the top, such as who you stole the code from and so forth. Also include the mini-GPL -boilerplate. Be sure to name the main function _main instead of main. -And be sure to put it in .c. Usage does not have to be taken care of by -your applet. -Make sure to #include "libbb.h" as the first include file in your applet. +boilerplate and Config.in/Kbuild/usage/applet.h snippets (more on that below +in this document). Be sure to name the main function _main instead +of main. And be sure to put it in .c. Make sure to #include "libbb.h" +as the first include file in your applet. For a new applet mu, here is the code that would go in mu.c: @@ -41,6 +41,23 @@ For a new applet mu, here is the code that would go in mu.c: #include "libbb.h" #include "other.h" +//config:config MU +//config: bool "MU" +//config: default n +//config: help +//config: Returns an indeterminate value. + +//kbuild:lib-$(CONFIG_MU) += mu.o +//applet:IF_MU(APPLET(mu, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//usage:#define mu_trivial_usage +//usage: "-[abcde] FILES" +//usage:#define mu_full_usage +//usage: "Returns an indeterminate value.\n\n" +//usage: "Options:\n" +//usage: "\t-a\t\tfirst function\n" +//usage: "\t-b\t\tsecond function\n" + int mu_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mu_main(int argc, char **argv) { @@ -90,6 +107,8 @@ Make a new file named .c #include "libbb.h" #include "other.h" +//kbuild:lib-y += function.o + int function(char *a) { return *a; @@ -97,9 +116,7 @@ int function(char *a) ----end example code------ -Add .o in the right alphabetically sorted place -in libbb/Kbuild.src. You should look at the conditional part of -libbb/Kbuild.src as well. +Remember about the kbuild snippet. You should also try to find a suitable place in include/libbb.h for the function declaration. If not, add it somewhere anyway, with or without @@ -109,41 +126,55 @@ You can look at libbb/Config.src and try to find out if the function is tunable and add it there if it is. +Kbuild/Config.in/usage/applets.h snippets in .c files +----------------------------------------------------- + +The old way of adding new applets was to put all the information needed by the +configuration and build system into appropriate files (namely: Kbuild.src and +Config.src in new applet's directory) and to add the applet declaration and +usage info text to include/applets.src.h and include/usage.src.h respectively. + +Since the scripts/gen_build_files.sh script had been introduced, the preferred +way is to have all these declarations contained within the applet .c files. + +Every line intended to be processed by gen_build_files.sh should start as a +comment without any preceding whitespaces and be followed by an appropriate +keyword - kbuild, config, usage or applet - and a colon, just like shown in the +first example above. + + Placement / Directory --------------------- Find the appropriate directory for your new applet. -Make sure you find the appropriate places in the files, the applets are -sorted alphabetically. +Add the kbuild snippet to the .c file: -Add the applet to Kbuild.src in the chosen directory: +//kbuild:lib-$(CONFIG_MU) += mu.o -lib-$(CONFIG_MU) += mu.o +Add the config snippet to the .c file: -Add the applet to Config.src in the chosen directory: - -config MU - bool "MU" - default n - help - Returns an indeterminate value. +//config:config MU +//config: bool "MU" +//config: default n +//config: help +//config: Returns an indeterminate value. Usage String(s) --------------- -Next, add usage information for you applet to include/usage.src.h. +Next, add usage information for your applet to the .c file. This should look like the following: - #define mu_trivial_usage \ - "-[abcde] FILES" - #define mu_full_usage \ - "Returns an indeterminate value.\n\n" \ - "Options:\n" \ - "\t-a\t\tfirst function\n" \ - "\t-b\t\tsecond function\n" \ - ... +//usage:#define mu_trivial_usage +//usage: "-[abcde] FILES" +//usage:#define mu_full_usage +//usage: "Returns an indeterminate value.\n\n" +//usage: "Options:\n" +//usage: "\t-a\t\tfirst function\n" +//usage: "\t-b\t\tsecond function\n" +//usage: ... If your program supports flags, the flags should be mentioned on the first line (-[abcde]) and a detailed description of each flag should go in the @@ -154,15 +185,11 @@ currently exist in usage.src.h.) Header Files ------------ -Next, add an entry to include/applets.src.h. Be *sure* to keep the list -in alphabetical order, or else it will break the binary-search lookup -algorithm in busybox.c and the Gods of BusyBox smite you. Yea, verily: - -Be sure to read the top of applets.src.h before adding your applet. +Finally add the applet declaration snippet. Be sure to read the top of +applets.src.h before adding your applet - it contains important info +on applet macros and conventions. - /* all programs above here are alphabetically "less than" 'mu' */ - IF_MU(APPLET(mu, BB_DIR_USR_BIN, BB_SUID_DROP)) - /* all programs below here are alphabetically "greater than" 'mu' */ +//applet:IF_MU(APPLET(mu, BB_DIR_USR_BIN, BB_SUID_DROP)) The Grand Announcement -- cgit v1.2.3-55-g6feb From c50493854a78b28b3642a402d1a0bbd57c1b3c1b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 16 Mar 2014 20:53:40 +0100 Subject: libbb: fix a bad check for uclibc >= 0.9.31 Signed-off-by: Denys Vlasenko --- libbb/xreadlink.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libbb/xreadlink.c b/libbb/xreadlink.c index bb63da08c..7d4cb60a5 100644 --- a/libbb/xreadlink.c +++ b/libbb/xreadlink.c @@ -1,14 +1,14 @@ /* vi: set sw=4 ts=4: */ /* * xreadlink.c - safe implementation of readlink. - * Returns a NULL on failure... + * Returns a NULL on failure. * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" -/* some systems (eg Hurd) does not have MAXSYMLINKS definition, +/* Some systems (eg Hurd) do not have MAXSYMLINKS definition, * set it to some reasonable value if it isn't defined */ #ifndef MAXSYMLINKS # define MAXSYMLINKS 20 @@ -108,8 +108,11 @@ char* FAST_FUNC xmalloc_readlink_or_warn(const char *path) char* FAST_FUNC xmalloc_realpath(const char *path) { -#if defined(__GLIBC__) || \ - (defined(__UCLIBC__) && UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 31)) +/* NB: uclibc also defines __GLIBC__ + * Therefore the test "if glibc, or uclibc >= 0.9.31" looks a bit weird: + */ +#if defined(__GLIBC__) && \ + (!defined(__UCLIBC__) || UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 31)) /* glibc provides a non-standard extension */ /* new: POSIX.1-2008 specifies this behavior as well */ return realpath(path, NULL); -- cgit v1.2.3-55-g6feb From f332617fbd9248f34cff183a2a4caa961730625d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 17 Mar 2014 15:06:29 +0100 Subject: docs/new-applet-HOWTO.txt: tweak a bit Signed-off-by: Denys Vlasenko --- docs/new-applet-HOWTO.txt | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/docs/new-applet-HOWTO.txt b/docs/new-applet-HOWTO.txt index 28970a50a..078e77bce 100644 --- a/docs/new-applet-HOWTO.txt +++ b/docs/new-applet-HOWTO.txt @@ -6,7 +6,7 @@ This document details the steps you must take to add a new applet to BusyBox. Credits: Matt Kraai - initial writeup Mark Whitley - the remix -Thomas Lundquist - Trying to keep it updated. +Thomas Lundquist - trying to keep it updated When doing this you should consider using the latest git HEAD. This is a good thing if you plan to getting it committed into mainline. @@ -23,7 +23,7 @@ as the first include file in your applet. For a new applet mu, here is the code that would go in mu.c: -(busybox.h already includes most usual header files. You do not need +(libbb.h already includes most usual header files. You do not need #include etc...) @@ -43,7 +43,7 @@ For a new applet mu, here is the code that would go in mu.c: //config:config MU //config: bool "MU" -//config: default n +//config: default y //config: help //config: Returns an indeterminate value. @@ -51,12 +51,11 @@ For a new applet mu, here is the code that would go in mu.c: //applet:IF_MU(APPLET(mu, BB_DIR_USR_BIN, BB_SUID_DROP)) //usage:#define mu_trivial_usage -//usage: "-[abcde] FILES" +//usage: "[-abcde] FILE..." //usage:#define mu_full_usage -//usage: "Returns an indeterminate value.\n\n" -//usage: "Options:\n" -//usage: "\t-a\t\tfirst function\n" -//usage: "\t-b\t\tsecond function\n" +//usage: "Returns an indeterminate value\n" +//usage: "\n -a First function" +//usage: "\n -b Second function" int mu_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mu_main(int argc, char **argv) @@ -150,13 +149,13 @@ Find the appropriate directory for your new applet. Add the kbuild snippet to the .c file: -//kbuild:lib-$(CONFIG_MU) += mu.o +//kbuild:lib-$(CONFIG_MU) += mu.o Add the config snippet to the .c file: //config:config MU //config: bool "MU" -//config: default n +//config: default y //config: help //config: Returns an indeterminate value. @@ -168,18 +167,16 @@ Next, add usage information for your applet to the .c file. This should look like the following: //usage:#define mu_trivial_usage -//usage: "-[abcde] FILES" +//usage: "[-abcde] FILE..." //usage:#define mu_full_usage -//usage: "Returns an indeterminate value.\n\n" -//usage: "Options:\n" -//usage: "\t-a\t\tfirst function\n" -//usage: "\t-b\t\tsecond function\n" +//usage: "Returns an indeterminate value\n" +//usage: "\n -a First function" +//usage: "\n -b Second function" //usage: ... If your program supports flags, the flags should be mentioned on the first -line (-[abcde]) and a detailed description of each flag should go in the -mu_full_usage section, one flag per line. (Numerous examples of this -currently exist in usage.src.h.) +line ([-abcde]) and a detailed description of each flag should go in the +mu_full_usage section, one flag per line. Header Files -- cgit v1.2.3-55-g6feb From 504fe45f35bab29752d39cc2d39d2e8c43fe6eac Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 23 Mar 2014 15:06:38 +0100 Subject: ntpd: add optional support for /etc/ntp.conf function old new delta add_peers - 98 +98 packed_usage 29470 29511 +41 ntp_init 407 428 +21 pw_encrypt 14 27 +13 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 3/0 up/down: 173/0) Total: 173 bytes Signed-off-by: Denys Vlasenko --- networking/Config.src | 8 ++++++++ networking/ntpd.c | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/networking/Config.src b/networking/Config.src index ca0ddcdd9..fbad7ecb2 100644 --- a/networking/Config.src +++ b/networking/Config.src @@ -664,6 +664,14 @@ config FEATURE_NTPD_SERVER Make ntpd usable as a NTP server. If you disable this option ntpd will be usable only as a NTP client. +config FEATURE_NTPD_CONF + bool "Make ntpd understand /etc/ntp.conf" + default y + depends on NTPD + help + Make ntpd look in /etc/ntp.conf for peers. Only "server address" + is supported. + config PSCAN bool "pscan" default y diff --git a/networking/ntpd.c b/networking/ntpd.c index 44592ce54..adda6e5b0 100644 --- a/networking/ntpd.c +++ b/networking/ntpd.c @@ -42,6 +42,13 @@ //usage: ) //usage: "\n -S PROG Run PROG after stepping time, stratum change, and every 11 mins" //usage: "\n -p PEER Obtain time from PEER (may be repeated)" +//usage: IF_FEATURE_NTPD_CONF( +//usage: "\n If -p is not given, read /etc/ntp.conf" +//usage: ) + +// -l and -p options are not compatible with "standard" ntpd: +// it has them as "-l logfile" and "-p pidfile". +// -S and -w are not compat either, "standard" ntpd has no such opts. #include "libbb.h" #include @@ -730,7 +737,7 @@ reset_peer_stats(peer_t *p, double offset) } static void -add_peers(char *s) +add_peers(const char *s) { peer_t *p; @@ -2087,14 +2094,34 @@ static NOINLINE void ntp_init(char **argv) "d" /* compat */ "46aAbgL", /* compat, ignored */ &peers, &G.script_name, &G.verbose); - if (!(opts & (OPT_p|OPT_l))) - bb_show_usage(); + // if (opts & OPT_x) /* disable stepping, only slew is allowed */ // G.time_was_stepped = 1; if (peers) { while (peers) add_peers(llist_pop(&peers)); - } else { + } +#if ENABLE_FEATURE_NTPD_CONF + else { + parser_t *parser; + char *token[3]; + + parser = config_open("/etc/ntp.conf"); + while (config_read(parser, token, 3, 1, "# \t", PARSE_NORMAL)) { + if (strcmp(token[0], "server") == 0 && token[1]) { + add_peers(token[1]); + continue; + } + bb_error_msg("skipping %s:%u: unimplemented command '%s'", + "/etc/ntp.conf", parser->lineno, token[0] + ); + } + config_close(parser); + } +#endif + if (G.peer_cnt == 0) { + if (!(opts & OPT_l)) + bb_show_usage(); /* -l but no peers: "stratum 1 server" mode */ G.stratum = 1; } -- cgit v1.2.3-55-g6feb From c3a27b0bfdf758f649caab2c474e690067af3402 Mon Sep 17 00:00:00 2001 From: Matt Whitlock Date: Sat, 22 Mar 2014 18:54:24 -0400 Subject: avoid calling bb_strtou twice in MIN macro expansion Also, the maximum allowable value of swap priority is technically SWAP_FLAG_PRIO_MASK >> SWAP_FLAG_PRIO_SHIFT. Signed-off-by: Matt Whitlock Signed-off-by: Denys Vlasenko --- util-linux/swaponoff.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/util-linux/swaponoff.c b/util-linux/swaponoff.c index 3f223343e..bcceff772 100644 --- a/util-linux/swaponoff.c +++ b/util-linux/swaponoff.c @@ -100,12 +100,12 @@ static int do_em_all(void) g_flags = 0; /* each swap space might have different flags */ p = hasmntopt(m, "pri"); if (p) { - /* Max allowed 32767 (==SWAP_FLAG_PRIO_MASK) */ - unsigned int swap_prio = MIN(bb_strtou(p + 4 , NULL, 10), SWAP_FLAG_PRIO_MASK); + /* Max allowed 32767 (== SWAP_FLAG_PRIO_MASK) */ + unsigned prio = bb_strtou(p + 4, NULL, 10); /* We want to allow "NNNN,foo", thus errno == EINVAL is allowed too */ if (errno != ERANGE) { g_flags = SWAP_FLAG_PREFER | - (swap_prio << SWAP_FLAG_PRIO_SHIFT); + MIN(prio, SWAP_FLAG_PRIO_MASK); } } #endif @@ -124,6 +124,9 @@ int swap_on_off_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int swap_on_off_main(int argc UNUSED_PARAM, char **argv) { int ret; +#if ENABLE_FEATURE_SWAPON_PRI + unsigned prio; +#endif INIT_G(); @@ -132,11 +135,11 @@ int swap_on_off_main(int argc UNUSED_PARAM, char **argv) #else if (applet_name[5] == 'n') opt_complementary = "p+"; - ret = getopt32(argv, (applet_name[5] == 'n') ? "ap:" : "a", &g_flags); + ret = getopt32(argv, (applet_name[5] == 'n') ? "ap:" : "a", &prio); if (ret & 2) { // -p g_flags = SWAP_FLAG_PREFER | - ((g_flags & SWAP_FLAG_PRIO_MASK) << SWAP_FLAG_PRIO_SHIFT); + MIN(prio, SWAP_FLAG_PRIO_MASK); ret &= 1; } #endif -- cgit v1.2.3-55-g6feb From b9bbd4ddf622bc365726af4f2424210762e74b32 Mon Sep 17 00:00:00 2001 From: Matt Whitlock Date: Sat, 22 Mar 2014 19:10:08 -0400 Subject: fix interaction of -a and -p options in swapon Swap entries in /etc/fstab inherit the priority specified on the command line unless they have 'pri' in their mount options. Signed-off-by: Matt Whitlock Signed-off-by: Denys Vlasenko --- util-linux/swaponoff.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/util-linux/swaponoff.c b/util-linux/swaponoff.c index bcceff772..d5bfe1888 100644 --- a/util-linux/swaponoff.c +++ b/util-linux/swaponoff.c @@ -82,6 +82,9 @@ static int do_em_all(void) struct mntent *m; FILE *f; int err; +#ifdef G + int cl_flags = g_flags; +#endif f = setmntent("/etc/fstab", "r"); if (f == NULL) @@ -97,14 +100,14 @@ static int do_em_all(void) ) { #if ENABLE_FEATURE_SWAPON_PRI char *p; - g_flags = 0; /* each swap space might have different flags */ + g_flags = cl_flags; /* each swap space might have different flags */ p = hasmntopt(m, "pri"); if (p) { /* Max allowed 32767 (== SWAP_FLAG_PRIO_MASK) */ unsigned prio = bb_strtou(p + 4, NULL, 10); /* We want to allow "NNNN,foo", thus errno == EINVAL is allowed too */ if (errno != ERANGE) { - g_flags = SWAP_FLAG_PREFER | + g_flags = (g_flags & ~SWAP_FLAG_PRIO_MASK) | SWAP_FLAG_PREFER | MIN(prio, SWAP_FLAG_PRIO_MASK); } } -- cgit v1.2.3-55-g6feb From 0a53b203f9e7157136c8ab4e8753008a63a56660 Mon Sep 17 00:00:00 2001 From: Matt Whitlock Date: Sat, 22 Mar 2014 19:21:01 -0400 Subject: add discard option -d to swapon Signed-off-by: Matt Whitlock Signed-off-by: Denys Vlasenko --- util-linux/Config.src | 9 ++++++ util-linux/swaponoff.c | 85 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 85 insertions(+), 9 deletions(-) diff --git a/util-linux/Config.src b/util-linux/Config.src index 5a8b0063b..c1cd6daa4 100644 --- a/util-linux/Config.src +++ b/util-linux/Config.src @@ -599,6 +599,15 @@ config SWAPONOFF space. If you are not using any swap space, you can leave this option disabled. +config FEATURE_SWAPON_DISCARD + bool "Support discard option -d" + default y + depends on SWAPONOFF + help + Enable support for discarding swap area blocks at swapon and/or as + the kernel frees them. This option enables both the -d option on + 'swapon' and the 'discard' option for swap entries in /etc/fstab. + config FEATURE_SWAPON_PRI bool "Support priority option -p" default y diff --git a/util-linux/swaponoff.c b/util-linux/swaponoff.c index d5bfe1888..a7ad6db79 100644 --- a/util-linux/swaponoff.c +++ b/util-linux/swaponoff.c @@ -8,10 +8,14 @@ */ //usage:#define swapon_trivial_usage -//usage: "[-a]" IF_FEATURE_SWAPON_PRI(" [-p PRI]") " [DEVICE]" +//usage: "[-a]" IF_FEATURE_SWAPON_DISCARD(" [-d[POL]]") IF_FEATURE_SWAPON_PRI(" [-p PRI]") " [DEVICE]" //usage:#define swapon_full_usage "\n\n" //usage: "Start swapping on DEVICE\n" //usage: "\n -a Start swapping on all swap devices" +//usage: IF_FEATURE_SWAPON_DISCARD( +//usage: "\n -d[POL] Discard blocks at swapon (POL=once)," +//usage: "\n as freed (POL=pages), or both (POL omitted)" +//usage: ) //usage: IF_FEATURE_SWAPON_PRI( //usage: "\n -p PRI Set swap device priority" //usage: ) @@ -38,7 +42,22 @@ # define MNTTYPE_SWAP "swap" #endif -#if ENABLE_FEATURE_SWAPON_PRI +#if ENABLE_FEATURE_SWAPON_DISCARD +#ifndef SWAP_FLAG_DISCARD +#define SWAP_FLAG_DISCARD 0x10000 +#endif +#ifndef SWAP_FLAG_DISCARD_ONCE +#define SWAP_FLAG_DISCARD_ONCE 0x20000 +#endif +#ifndef SWAP_FLAG_DISCARD_PAGES +#define SWAP_FLAG_DISCARD_PAGES 0x40000 +#endif +#define SWAP_FLAG_DISCARD_MASK \ + (SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE | SWAP_FLAG_DISCARD_PAGES) +#endif + + +#if ENABLE_FEATURE_SWAPON_DISCARD || ENABLE_FEATURE_SWAPON_PRI struct globals { int flags; } FIX_ALIASING; @@ -98,9 +117,23 @@ static int do_em_all(void) if (applet_name[5] != 'n' || hasmntopt(m, MNTOPT_NOAUTO) == NULL ) { -#if ENABLE_FEATURE_SWAPON_PRI +#if ENABLE_FEATURE_SWAPON_DISCARD || ENABLE_FEATURE_SWAPON_PRI char *p; g_flags = cl_flags; /* each swap space might have different flags */ +#if ENABLE_FEATURE_SWAPON_DISCARD + p = hasmntopt(m, "discard"); + if (p) { + if (p[7] == '=') { + if (strncmp(p + 8, "once", 4) == 0 && (p[12] == ',' || p[12] == '\0')) + g_flags = (g_flags & ~SWAP_FLAG_DISCARD_MASK) | SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE; + else if (strncmp(p + 8, "pages", 5) == 0 && (p[13] == ',' || p[13] == '\0')) + g_flags = (g_flags & ~SWAP_FLAG_DISCARD_MASK) | SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_PAGES; + } + else if (p[7] == ',' || p[7] == '\0') + g_flags = (g_flags & ~SWAP_FLAG_DISCARD_MASK) | SWAP_FLAG_DISCARD; + } +#endif +#if ENABLE_FEATURE_SWAPON_PRI p = hasmntopt(m, "pri"); if (p) { /* Max allowed 32767 (== SWAP_FLAG_PRIO_MASK) */ @@ -111,6 +144,7 @@ static int do_em_all(void) MIN(prio, SWAP_FLAG_PRIO_MASK); } } +#endif #endif err += swap_enable_disable(m->mnt_fsname); } @@ -127,24 +161,57 @@ int swap_on_off_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int swap_on_off_main(int argc UNUSED_PARAM, char **argv) { int ret; +#if ENABLE_FEATURE_SWAPON_DISCARD + char *discard = NULL; +#endif #if ENABLE_FEATURE_SWAPON_PRI unsigned prio; #endif INIT_G(); -#if !ENABLE_FEATURE_SWAPON_PRI +#if !ENABLE_FEATURE_SWAPON_DISCARD && !ENABLE_FEATURE_SWAPON_PRI ret = getopt32(argv, "a"); #else +#if ENABLE_FEATURE_SWAPON_PRI if (applet_name[5] == 'n') opt_complementary = "p+"; - ret = getopt32(argv, (applet_name[5] == 'n') ? "ap:" : "a", &prio); +#endif + ret = getopt32(argv, (applet_name[5] == 'n') ? +#if ENABLE_FEATURE_SWAPON_DISCARD + "d::" +#endif +#if ENABLE_FEATURE_SWAPON_PRI + "p:" +#endif + "a" : "a" +#if ENABLE_FEATURE_SWAPON_DISCARD + , &discard +#endif +#if ENABLE_FEATURE_SWAPON_PRI + , &prio +#endif + ); +#endif - if (ret & 2) { // -p - g_flags = SWAP_FLAG_PREFER | - MIN(prio, SWAP_FLAG_PRIO_MASK); - ret &= 1; +#if ENABLE_FEATURE_SWAPON_DISCARD + if (ret & 1) { // -d + if (!discard) + g_flags |= SWAP_FLAG_DISCARD; + else if (strcmp(discard, "once") == 0) + g_flags |= SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE; + else if (strcmp(discard, "pages") == 0) + g_flags |= SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_PAGES; + else + bb_show_usage(); } + ret >>= 1; +#endif +#if ENABLE_FEATURE_SWAPON_PRI + if (ret & 1) // -p + g_flags |= SWAP_FLAG_PREFER | + MIN(prio, SWAP_FLAG_PRIO_MASK); + ret >>= 1; #endif if (ret /* & 1: not needed */) // -a -- cgit v1.2.3-55-g6feb From faa9e94db619d1110061687278cde93a651e69de Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 27 Mar 2014 16:50:29 +0100 Subject: wget: fix a case where progress bar isn't updated if writes are very slow Signed-off-by: Denys Vlasenko --- networking/wget.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/networking/wget.c b/networking/wget.c index 62a5fcc02..1013f66cb 100644 --- a/networking/wget.c +++ b/networking/wget.c @@ -640,7 +640,7 @@ static void NOINLINE retrieve_file_data(FILE *dfp) #if ENABLE_FEATURE_WGET_TIMEOUT second_cnt = G.timeout_seconds; #endif - continue; + goto bump; } /* n <= 0. @@ -673,11 +673,12 @@ static void NOINLINE retrieve_file_data(FILE *dfp) * to try reading anyway. */ } +#endif + bump: /* Need to do it _every_ second for "stalled" indicator * to be shown properly. */ progress_meter(PROGRESS_BUMP); -#endif } /* while (reading data) */ #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT -- cgit v1.2.3-55-g6feb From a3f326cd6619cadb44cb4bb051b1d4e54bcd3477 Mon Sep 17 00:00:00 2001 From: Tito Ragusa Date: Mon, 31 Mar 2014 16:39:26 +0200 Subject: swapon/swapoff: size reduction, cleanup, fixes, improvements 1) real swapon/swapoff handles also devices on the commandline with -a; 2) xstat(device) in swap_enable_disable aborts on error when cycling through fstab so some devices are not handled; 3) duplicated code for ENABLE_FEATURE_SWAPON_DISCARD and ENABLE_FEATURE_SWAPON_PRI was moved to functions. 4) silence some error messages with -a; 5) minor cleanups and code refactoring reduced the size as per bloat-check: 6) I also added support for /proc/swaps handling to swapoff: "When the -a flag is given, swapping is disabled on all known swap devices and files (as found in /proc/swaps or /etc/fstab)." So now swapoff first cycles through /proc/swaps and then through fstab to swapoff all devices. function old new delta set_discard_flag - 106 +106 swap_enable_disable 147 238 +91 set_priority_flag - 79 +79 retrieve_file_data 470 467 -3 swap_on_off_main 638 418 -220 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 1/2 up/down: 276/-223) Total: 53 bytes Signed-off-by: Tito Ragusa Signed-off-by: Denys Vlasenko --- util-linux/swaponoff.c | 276 ++++++++++++++++++++++++++++--------------------- 1 file changed, 161 insertions(+), 115 deletions(-) diff --git a/util-linux/swaponoff.c b/util-linux/swaponoff.c index a7ad6db79..acdb67729 100644 --- a/util-linux/swaponoff.c +++ b/util-linux/swaponoff.c @@ -63,90 +63,142 @@ struct globals { } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define g_flags (G.flags) +#define save_g_flags() int save_g_flags = g_flags +#define restore_g_flags() g_flags = save_g_flags #else #define g_flags 0 +#define save_g_flags() ((void)0) +#define restore_g_flags() ((void)0) #endif #define INIT_G() do { } while (0) +#define do_swapoff (applet_name[5] == 'f') + +/* Command line options */ +enum { + OPTBIT_a, /* -a all */ + IF_FEATURE_SWAPON_DISCARD( OPTBIT_d ,) /* -d discard */ + IF_FEATURE_SWAPON_PRI ( OPTBIT_p ,) /* -p priority */ + OPT_a = 1 << OPTBIT_a, + OPT_d = IF_FEATURE_SWAPON_DISCARD((1 << OPTBIT_d)) + 0, + OPT_p = IF_FEATURE_SWAPON_PRI ((1 << OPTBIT_p)) + 0, +}; + +#define OPT_ALL (option_mask32 & OPT_a) +#define OPT_DISCARD (option_mask32 & OPT_d) +#define OPT_PRIO (option_mask32 & OPT_p) + static int swap_enable_disable(char *device) { - int status; + int err = 0; + int quiet = 0; struct stat st; resolve_mount_spec(&device); - xstat(device, &st); - -#if ENABLE_DESKTOP - /* test for holes */ - if (S_ISREG(st.st_mode)) - if (st.st_blocks * (off_t)512 < st.st_size) - bb_error_msg("warning: swap file has holes"); -#endif - if (applet_name[5] == 'n') - status = swapon(device, g_flags); - else - status = swapoff(device); + if (do_swapoff) { + err = swapoff(device); + /* Don't complain on OPT_ALL if not a swap device or if it doesn't exist */ + quiet = (OPT_ALL && (errno == EINVAL || errno == ENOENT)); + } else { + /* swapon */ + err = stat(device, &st); + if (!err) { + if (ENABLE_DESKTOP && S_ISREG(st.st_mode)) { + if (st.st_blocks * (off_t)512 < st.st_size) { + bb_error_msg("%s: file has holes", device); + return 1; + } + } + err = swapon(device, g_flags); + /* Don't complain on swapon -a if device is already in use */ + quiet = (OPT_ALL && errno == EBUSY); + } + } - if (status != 0) { - bb_simple_perror_msg(device); + if (err) { + if (!quiet) + bb_simple_perror_msg(device); return 1; } - return 0; } -static int do_em_all(void) +#if ENABLE_FEATURE_SWAPON_DISCARD +static void set_discard_flag(char *s) { - struct mntent *m; - FILE *f; - int err; -#ifdef G - int cl_flags = g_flags; + /* Unset the flag first to allow fstab options to override */ + /* options set on the command line */ + g_flags = (g_flags & ~SWAP_FLAG_DISCARD_MASK) | SWAP_FLAG_DISCARD; + + if (!s) /* No optional policy value on the commandline */ + return; + /* Skip prepended '=' */ + if (*s == '=') + s++; + /* For fstab parsing: remove other appended options */ + *strchrnul(s, ',') = '\0'; + + if (strcmp(s, "once") == 0) + g_flags |= SWAP_FLAG_DISCARD_ONCE; + if (strcmp(s, "pages") == 0) + g_flags |= SWAP_FLAG_DISCARD_PAGES; +} +#else +#define set_discard_flag(s) ((void)0) +#endif + +#if ENABLE_FEATURE_SWAPON_PRI +static void set_priority_flag(char *s) +{ + unsigned prio; + + /* For fstab parsing: remove other appended options */ + *strchrnul(s, ',') = '\0'; + /* Max allowed 32767 (== SWAP_FLAG_PRIO_MASK) */ + prio = bb_strtou(s, NULL, 10); + if (!errno) { + /* Unset the flag first to allow fstab options to override */ + /* options set on the command line */ + g_flags = (g_flags & ~SWAP_FLAG_PRIO_MASK) | SWAP_FLAG_PREFER | + MIN(prio, SWAP_FLAG_PRIO_MASK); + } +} +#else +#define set_priority_flag(s) ((void)0) #endif - f = setmntent("/etc/fstab", "r"); - if (f == NULL) - bb_perror_msg_and_die("/etc/fstab"); +static int do_em_all_in_fstab(void) +{ + struct mntent *m; + int err = 0; + FILE *f = xfopen_for_read("/etc/fstab"); - err = 0; while ((m = getmntent(f)) != NULL) { if (strcmp(m->mnt_type, MNTTYPE_SWAP) == 0) { /* swapon -a should ignore entries with noauto, - * but swapoff -a should process them */ - if (applet_name[5] != 'n' - || hasmntopt(m, MNTOPT_NOAUTO) == NULL - ) { -#if ENABLE_FEATURE_SWAPON_DISCARD || ENABLE_FEATURE_SWAPON_PRI - char *p; - g_flags = cl_flags; /* each swap space might have different flags */ -#if ENABLE_FEATURE_SWAPON_DISCARD - p = hasmntopt(m, "discard"); - if (p) { - if (p[7] == '=') { - if (strncmp(p + 8, "once", 4) == 0 && (p[12] == ',' || p[12] == '\0')) - g_flags = (g_flags & ~SWAP_FLAG_DISCARD_MASK) | SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE; - else if (strncmp(p + 8, "pages", 5) == 0 && (p[13] == ',' || p[13] == '\0')) - g_flags = (g_flags & ~SWAP_FLAG_DISCARD_MASK) | SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_PAGES; + * but swapoff -a should process them + */ + if (do_swapoff || hasmntopt(m, MNTOPT_NOAUTO) == NULL) { + /* each swap space might have different flags */ + /* save global flags for the next round */ + save_g_flags(); + if (ENABLE_FEATURE_SWAPON_DISCARD) { + char *p = hasmntopt(m, "discard"); + if (p) { + /* move to '=' or to end of string */ + p += 7; + set_discard_flag(p); } - else if (p[7] == ',' || p[7] == '\0') - g_flags = (g_flags & ~SWAP_FLAG_DISCARD_MASK) | SWAP_FLAG_DISCARD; } -#endif -#if ENABLE_FEATURE_SWAPON_PRI - p = hasmntopt(m, "pri"); - if (p) { - /* Max allowed 32767 (== SWAP_FLAG_PRIO_MASK) */ - unsigned prio = bb_strtou(p + 4, NULL, 10); - /* We want to allow "NNNN,foo", thus errno == EINVAL is allowed too */ - if (errno != ERANGE) { - g_flags = (g_flags & ~SWAP_FLAG_PRIO_MASK) | SWAP_FLAG_PREFER | - MIN(prio, SWAP_FLAG_PRIO_MASK); + if (ENABLE_FEATURE_SWAPON_PRI) { + char *p = hasmntopt(m, "pri"); + if (p) { + set_priority_flag(p + 4); } } -#endif -#endif - err += swap_enable_disable(m->mnt_fsname); + err |= swap_enable_disable(m->mnt_fsname); + restore_g_flags(); } } } @@ -157,74 +209,68 @@ static int do_em_all(void) return err; } +static int do_all_in_proc_swaps(void) +{ + char *line; + int err = 0; + FILE *f = fopen_for_read("/proc/swaps"); + /* Don't complain if missing */ + if (f) { + while ((line = xmalloc_fgetline(f)) != NULL) { + if (line[0] == '/') { + *strchrnul(line, ' ') = '\0'; + err |= swap_enable_disable(line); + } + free(line); + } + if (ENABLE_FEATURE_CLEAN_UP) + fclose(f); + } + + return err; +} + +#define OPTSTR_SWAPON "a" \ + IF_FEATURE_SWAPON_DISCARD("d::") \ + IF_FEATURE_SWAPON_PRI("p:") + int swap_on_off_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int swap_on_off_main(int argc UNUSED_PARAM, char **argv) { - int ret; -#if ENABLE_FEATURE_SWAPON_DISCARD - char *discard = NULL; -#endif -#if ENABLE_FEATURE_SWAPON_PRI - unsigned prio; -#endif + IF_FEATURE_SWAPON_PRI(char *prio;) + IF_FEATURE_SWAPON_DISCARD(char *discard = NULL;) + int ret = 0; INIT_G(); -#if !ENABLE_FEATURE_SWAPON_DISCARD && !ENABLE_FEATURE_SWAPON_PRI - ret = getopt32(argv, "a"); -#else -#if ENABLE_FEATURE_SWAPON_PRI - if (applet_name[5] == 'n') - opt_complementary = "p+"; -#endif - ret = getopt32(argv, (applet_name[5] == 'n') ? -#if ENABLE_FEATURE_SWAPON_DISCARD - "d::" -#endif -#if ENABLE_FEATURE_SWAPON_PRI - "p:" -#endif - "a" : "a" -#if ENABLE_FEATURE_SWAPON_DISCARD - , &discard -#endif -#if ENABLE_FEATURE_SWAPON_PRI - , &prio -#endif - ); -#endif - -#if ENABLE_FEATURE_SWAPON_DISCARD - if (ret & 1) { // -d - if (!discard) - g_flags |= SWAP_FLAG_DISCARD; - else if (strcmp(discard, "once") == 0) - g_flags |= SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE; - else if (strcmp(discard, "pages") == 0) - g_flags |= SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_PAGES; - else - bb_show_usage(); - } - ret >>= 1; -#endif -#if ENABLE_FEATURE_SWAPON_PRI - if (ret & 1) // -p - g_flags |= SWAP_FLAG_PREFER | - MIN(prio, SWAP_FLAG_PRIO_MASK); - ret >>= 1; -#endif - - if (ret /* & 1: not needed */) // -a - return do_em_all(); + getopt32(argv, do_swapoff ? "a" : OPTSTR_SWAPON + IF_FEATURE_SWAPON_DISCARD(, &discard) + IF_FEATURE_SWAPON_PRI(, &prio) + ); argv += optind; - if (!*argv) - bb_show_usage(); - /* ret = 0; redundant */ - do { - ret += swap_enable_disable(*argv); - } while (*++argv); + if (OPT_DISCARD) { + set_discard_flag(discard); + } + if (OPT_PRIO) { + set_priority_flag(prio); + } + if (OPT_ALL) { + /* swapoff -a does also /proc/swaps */ + if (do_swapoff) + ret = do_all_in_proc_swaps(); + ret |= do_em_all_in_fstab(); + } else if (!*argv) { + /* if not -a we need at least one arg */ + bb_show_usage(); + } + /* Unset -a now to allow for more messages in swap_enable_disable */ + option_mask32 = option_mask32 & ~OPT_a; + /* Now process devices on the commandline if any */ + while (*argv) { + ret |= swap_enable_disable(*argv++); + } return ret; } -- cgit v1.2.3-55-g6feb From c9a67133d6a947532b560c7f2b9391ec299dd97f Mon Sep 17 00:00:00 2001 From: Tito Ragusa Date: Tue, 1 Apr 2014 09:51:27 +0200 Subject: swapon/swapoff: -a returns 0 on ignored errors Signed-off-by: Tito Ragusa Signed-off-by: Denys Vlasenko --- util-linux/swaponoff.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/util-linux/swaponoff.c b/util-linux/swaponoff.c index acdb67729..75487267b 100644 --- a/util-linux/swaponoff.c +++ b/util-linux/swaponoff.c @@ -116,9 +116,8 @@ static int swap_enable_disable(char *device) } } - if (err) { - if (!quiet) - bb_simple_perror_msg(device); + if (err && !quiet) { + bb_simple_perror_msg(device); return 1; } return 0; -- cgit v1.2.3-55-g6feb From 7537406edd3e0adf11d117379cac4e519e746d35 Mon Sep 17 00:00:00 2001 From: Drew Moseley Date: Wed, 2 Apr 2014 12:12:46 +0200 Subject: build system: specify '-nostldlib' when linking to .o files For certain cross build scenarios the LD variable is overridden to use the gcc executive to ensure all the target tuning parameters are used. In these cases, the executive errors out as shown below but since this step is only linking to a .o file the standard libs are not needed. $ make LD=gcc applets/built-in.o LD applets/built-in.o /usr/bin/ld: cannot find -lgcc_s /usr/bin/ld: cannot find -lgcc_s collect2: ld returned 1 exit status make[1]: *** [applets/built-in.o] Error 1 make: *** [applets_dir] Error 2 Signed-off-by: Drew Moseley Signed-off-by: Denys Vlasenko --- scripts/Makefile.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 5685b5bcc..5eac45f91 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -255,8 +255,9 @@ $(sort $(subdir-obj-y)): $(subdir-ym) ; ifdef builtin-target quiet_cmd_link_o_target = LD $@ # If the list of objects to link is empty, just create an empty built-in.o +# -nostdlib is added to make "make LD=gcc ..." work (some people use that) cmd_link_o_target = $(if $(strip $(obj-y)),\ - $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^),\ + $(LD) -nostdlib $(ld_flags) -r -o $@ $(filter $(obj-y), $^),\ rm -f $@; $(AR) rcs $@) $(builtin-target): $(obj-y) FORCE -- cgit v1.2.3-55-g6feb From a8d6f9bee43aba077f7a3a9bcb6ac64e5d877278 Mon Sep 17 00:00:00 2001 From: Jody Bruchon Date: Wed, 2 Apr 2014 13:49:26 +0200 Subject: vi: undo support for vi with intermediate queuing function old new delta undo_push - 411 +411 undo_pop - 288 +288 do_cmd 4160 4426 +266 char_insert 363 483 +120 undo_queue_commit - 61 +61 text_hole_delete 108 163 +55 string_insert 94 127 +33 colon 2864 2882 +18 yank_delete 92 101 +9 vi_main 273 280 +7 dot_scroll 88 93 +5 dot_right 29 34 +5 dot_prev 20 25 +5 dot_next 20 25 +5 dot_left 24 29 +5 dot_end 20 25 +5 dot_begin 20 25 +5 init_text_buffer 154 156 +2 text_hole_make 145 142 -3 file_insert 333 318 -15 ------------------------------------------------------------------------------ (add/remove: 3/0 grow/shrink: 15/2 up/down: 1305/-18) Total: 1287 bytes (without queuing it's ~870 bytes) Signed-off-by: Jody Bruchon Signed-off-by: Denys Vlasenko --- editors/vi.c | 502 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 448 insertions(+), 54 deletions(-) diff --git a/editors/vi.c b/editors/vi.c index 097f309c8..e38667ad1 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -17,7 +17,6 @@ * it would be easier to change the mark when add/delete lines * More intelligence in refresh() * ":r !cmd" and "!cmd" to filter text through an external command - * A true "undo" facility * An "ex" line oriented mode- maybe using "cmdedit" */ @@ -136,6 +135,36 @@ //config: cursor position using "ESC [ 6 n" escape sequence, then read stdin. //config: //config: This is not clean but helps a lot on serial lines and such. +//config:config FEATURE_VI_UNDO +//config: bool "Support undo command 'u'" +//config: default y +//config: depends on VI +//config: help +//config: Support the 'u' command to undo insertion, deletion, and replacement +//config: of text. +//config:config FEATURE_VI_UNDO_QUEUE +//config: bool "Enable undo operation queuing" +//config: default y +//config: depends on FEATURE_VI_UNDO +//config: help +//config: The vi undo functions can use an intermediate queue to greatly lower +//config: malloc() calls and overhead. When the maximum size of this queue is +//config: reached, the contents of the queue are committed to the undo stack. +//config: This increases the size of the undo code and allows some undo +//config: operations (especially un-typing/backspacing) to be far more useful. +//config:config FEATURE_VI_UNDO_QUEUE_MAX +//config: int "Maximum undo character queue size" +//config: default 256 +//config: range 32 65536 +//config: depends on FEATURE_VI_UNDO_QUEUE +//config: help +//config: This option sets the number of bytes used at runtime for the queue. +//config: Smaller values will create more undo objects and reduce the amount +//config: of typed or backspaced characters that are grouped into one undo +//config: operation; larger values increase the potential size of each undo +//config: and will generally malloc() larger objects and less frequently. +//config: Unless you want more (or less) frequent "undo points" while typing, +//config: you should probably leave this unchanged. //applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP)) @@ -347,6 +376,42 @@ struct globals { char get_input_line__buf[MAX_INPUT_LEN]; /* former static */ char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2]; +#if ENABLE_FEATURE_VI_UNDO +// undo_push() operations +#define UNDO_INS 0 +#define UNDO_DEL 1 +#define UNDO_INS_CHAIN 2 +#define UNDO_DEL_CHAIN 3 +// UNDO_*_QUEUED must be equal to UNDO_xxx ORed with UNDO_QUEUED_FLAG +#define UNDO_QUEUED_FLAG 4 +#define UNDO_INS_QUEUED 4 +#define UNDO_DEL_QUEUED 5 +#define UNDO_USE_SPOS 32 +#define UNDO_EMPTY 64 +// Pass-through flags for functions that can be undone +#define NO_UNDO 0 +#define ALLOW_UNDO 1 +#define ALLOW_UNDO_CHAIN 2 +#if ENABLE_FEATURE_VI_UNDO_QUEUE +#define ALLOW_UNDO_QUEUED 3 + char undo_queue_state; + int undo_q; + char *undo_queue_spos; // Start position of queued operation + char undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX]; +#else +// If undo queuing disabled, don't invoke the missing queue logic +#define ALLOW_UNDO_QUEUED 1 +#endif /* ENABLE_FEATURE_VI_UNDO_QUEUE */ + + struct undo_object { + struct undo_object *prev; // Linking back avoids list traversal (LIFO) + int u_type; // 0=deleted, 1=inserted, 2=swapped + int start; // Offset where the data should be restored/deleted + int length; // total data size + char *undo_text; // ptr to text that will be inserted + } *undo_stack_tail; +#endif /* ENABLE_FEATURE_VI_UNDO */ + }; #define G (*ptr_to_globals) #define text (G.text ) @@ -408,6 +473,16 @@ struct globals { #define last_modifying_cmd (G.last_modifying_cmd ) #define get_input_line__buf (G.get_input_line__buf) +#if ENABLE_FEATURE_VI_UNDO +#define undo_stack_tail (G.undo_stack_tail ) +# if ENABLE_FEATURE_VI_UNDO_QUEUE +#define undo_queue_state (G.undo_queue_state) +#define undo_q (G.undo_q ) +#define undo_queue (G.undo_queue ) +#define undo_queue_spos (G.undo_queue_spos ) +# endif +#endif + #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ last_file_modified = -1; \ @@ -437,10 +512,12 @@ static void dot_next(void); // move dot to next line B-o-l static void dot_prev(void); // move dot to prev line B-o-l static void dot_scroll(int, int); // move the screen up or down static void dot_skip_over_ws(void); // move dot pat WS -static void dot_delete(void); // delete the char at 'dot' static char *bound_dot(char *); // make sure text[0] <= P < "end" static char *new_screen(int, int); // malloc virtual screen memory -static char *char_insert(char *, char); // insert the char c at 'p' +#if !ENABLE_FEATURE_VI_UNDO +#define char_insert(a,b,c) char_insert(a,b) +#endif +static char *char_insert(char *, char, int); // insert the char c at 'p' // might reallocate text[]! use p += stupid_insert(p, ...), // and be careful to not use pointers into potentially freed text[]! static uintptr_t stupid_insert(char *, char); // stupidly insert the char c at 'p' @@ -448,11 +525,17 @@ static int find_range(char **, char **, char); // return pointers for an object static int st_test(char *, int, int, char *); // helper for skip_thing() static char *skip_thing(char *, int, int, int); // skip some object static char *find_pair(char *, char); // find matching pair () [] {} -static char *text_hole_delete(char *, char *); // at "p", delete a 'size' byte hole +#if !ENABLE_FEATURE_VI_UNDO +#define text_hole_delete(a,b,c) text_hole_delete(a,b) +#endif +static char *text_hole_delete(char *, char *, int); // at "p", delete a 'size' byte hole // might reallocate text[]! use p += text_hole_make(p, ...), // and be careful to not use pointers into potentially freed text[]! static uintptr_t text_hole_make(char *, int); // at "p", make a 'size' byte hole -static char *yank_delete(char *, char *, int, int); // yank text[] into register then delete +#if !ENABLE_FEATURE_VI_UNDO +#define yank_delete(a,b,c,d,e) yank_delete(a,b,c,d) +#endif +static char *yank_delete(char *, char *, int, int, int); // yank text[] into register then delete static void show_help(void); // display some help info static void rawmode(void); // set "raw" mode on tty static void cookmode(void); // return to "cooked" mode on tty @@ -514,20 +597,34 @@ static void showmatching(char *); // show the matching pair () [] {} #if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME // might reallocate text[]! use p += string_insert(p, ...), // and be careful to not use pointers into potentially freed text[]! -static uintptr_t string_insert(char *, const char *); // insert the string at 'p' +# if !ENABLE_FEATURE_VI_UNDO +#define string_insert(a,b,c) string_insert(a,b) +# endif +static uintptr_t string_insert(char *, const char *, int); // insert the string at 'p' #endif #if ENABLE_FEATURE_VI_YANKMARK static char *text_yank(char *, char *, int); // save copy of "p" into a register static char what_reg(void); // what is letter of current YDreg static void check_context(char); // remember context for '' command #endif +#if ENABLE_FEATURE_VI_UNDO +static void undo_push(char *, unsigned int, unsigned char); // Push an operation on the undo stack +static void undo_pop(void); // Undo the last operation +# if ENABLE_FEATURE_VI_UNDO_QUEUE +static void undo_queue_commit(void); // Flush any queued objects to the undo stack +# else +# define undo_queue_commit() ((void)0) +# endif +#else +#define undo_queue_commit() ((void)0) +#endif + #if ENABLE_FEATURE_VI_CRASHME static void crash_dummy(); static void crash_test(); static int crashme = 0; #endif - static void write1(const char *out) { fputs(out, stdout); @@ -540,6 +637,14 @@ int vi_main(int argc, char **argv) INIT_G(); +#if ENABLE_FEATURE_VI_UNDO + /* undo_stack_tail = NULL; - already is */ +#if ENABLE_FEATURE_VI_UNDO_QUEUE + undo_queue_state = UNDO_EMPTY; + /* undo_q = 0; - already is */ +#endif +#endif + #if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME my_pid = getpid(); #endif @@ -631,7 +736,7 @@ static int init_text_buffer(char *fn) } if (size < 0) { // file dont exist. Start empty buf with dummy line - char_insert(text, '\n'); + char_insert(text, '\n', NO_UNDO); rc = 0; } else { rc = file_insert(fn, text, 1); @@ -756,7 +861,7 @@ static void edit_file(char *fn) crash_dummy(); // generate a random command } else { crashme = 0; - string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string + string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n", NO_UNDO); // insert the string dot = text; refresh(FALSE); } @@ -1015,7 +1120,7 @@ static void colon(char *buf) q = begin_line(dot); // assume .,. for the range r = end_line(dot); } - dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines + dot = yank_delete(q, r, 1, YANKDEL, ALLOW_UNDO); // save, then delete lines dot_skip_over_ws(); } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file // don't edit, if the current file has been modified @@ -1175,7 +1280,7 @@ static void colon(char *buf) // if the insert is before "dot" then we need to update if (q <= dot) dot += ch; - /*file_modified++; - done by file_insert */ + // file_modified++; } } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args if (file_modified && !useforce) { @@ -1235,6 +1340,9 @@ static void colon(char *buf) char *F, *R, *flags; size_t len_F, len_R; int gflag; // global replace flag +#if ENABLE_FEATURE_VI_UNDO + int dont_chain_first_item = ALLOW_UNDO; +#endif // F points to the "find" pattern // R points to the "replace" pattern @@ -1269,9 +1377,13 @@ static void colon(char *buf) if (found) { uintptr_t bias; // we found the "find" pattern - delete it - text_hole_delete(found, found + len_F - 1); - // inset the "replace" patern - bias = string_insert(found, R); // insert the string + // For undo support, the first item should not be chained + text_hole_delete(found, found + len_F - 1, dont_chain_first_item); +#if ENABLE_FEATURE_VI_UNDO + dont_chain_first_item = ALLOW_UNDO_CHAIN; +#endif + // insert the "replace" patern + bias = string_insert(found, R, ALLOW_UNDO_CHAIN); found += bias; ls += bias; /*q += bias; - recalculated anyway */ @@ -1572,23 +1684,27 @@ static char *find_line(int li) // find begining of line #li //----- Dot Movement Routines ---------------------------------- static void dot_left(void) { + undo_queue_commit(); if (dot > text && dot[-1] != '\n') dot--; } static void dot_right(void) { + undo_queue_commit(); if (dot < end - 1 && *dot != '\n') dot++; } static void dot_begin(void) { + undo_queue_commit(); dot = begin_line(dot); // return pointer to first char cur line } static void dot_end(void) { + undo_queue_commit(); dot = end_line(dot); // return pointer to last char cur line } @@ -1614,11 +1730,13 @@ static char *move_to_col(char *p, int l) static void dot_next(void) { + undo_queue_commit(); dot = next_line(dot); } static void dot_prev(void) { + undo_queue_commit(); dot = prev_line(dot); } @@ -1626,6 +1744,7 @@ static void dot_scroll(int cnt, int dir) { char *q; + undo_queue_commit(); for (; cnt > 0; cnt--) { if (dir < 0) { // scroll Backwards @@ -1653,11 +1772,6 @@ static void dot_skip_over_ws(void) dot++; } -static void dot_delete(void) // delete the char at 'dot' -{ - text_hole_delete(dot, dot); -} - static char *bound_dot(char *p) // make sure text[0] <= P < "end" { if (p >= end && end > text) { @@ -1804,17 +1918,34 @@ static char *char_search(char *p, const char *pat, int dir, int range) #endif /* FEATURE_VI_SEARCH */ -static char *char_insert(char *p, char c) // insert the char c at 'p' +static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' { if (c == 22) { // Is this an ctrl-V? p += stupid_insert(p, '^'); // use ^ to indicate literal next refresh(FALSE); // show the ^ c = get_one_char(); *p = c; - p++; +#if ENABLE_FEATURE_VI_UNDO + switch (undo) { + case ALLOW_UNDO: + undo_push(p, 1, UNDO_INS); + break; + case ALLOW_UNDO_CHAIN: + undo_push(p, 1, UNDO_INS_CHAIN); + break; +# if ENABLE_FEATURE_VI_UNDO_QUEUE + case ALLOW_UNDO_QUEUED: + undo_push(p, 1, UNDO_INS_QUEUED); + break; +# endif + } +#else file_modified++; +#endif /* ENABLE_FEATURE_VI_UNDO */ + p++; } else if (c == 27) { // Is this an ESC? cmd_mode = 0; + undo_queue_commit(); cmdcnt = 0; end_cmd_q(); // stop adding to q last_status_cksum = 0; // force status update @@ -1825,7 +1956,7 @@ static char *char_insert(char *p, char c) // insert the char c at 'p' // 123456789 if ((p[-1] != '\n') && (dot>text)) { p--; - p = text_hole_delete(p, p); // shrink buffer 1 char + p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED); // shrink buffer 1 char } } else { #if ENABLE_FEATURE_VI_SETOPTS @@ -1838,6 +1969,27 @@ static char *char_insert(char *p, char c) // insert the char c at 'p' #if ENABLE_FEATURE_VI_SETOPTS sp = p; // remember addr of insert #endif +#if ENABLE_FEATURE_VI_UNDO +# if ENABLE_FEATURE_VI_UNDO_QUEUE + if (c == '\n') + undo_queue_commit(); +# endif + switch (undo) { + case ALLOW_UNDO: + undo_push(p, 1, UNDO_INS); + break; + case ALLOW_UNDO_CHAIN: + undo_push(p, 1, UNDO_INS_CHAIN); + break; +# if ENABLE_FEATURE_VI_UNDO_QUEUE + case ALLOW_UNDO_QUEUED: + undo_push(p, 1, UNDO_INS_QUEUED); + break; +# endif + } +#else + file_modified++; +#endif /* ENABLE_FEATURE_VI_UNDO */ p += 1 + stupid_insert(p, c); // insert the char #if ENABLE_FEATURE_VI_SETOPTS if (showmatch && strchr(")]}", *sp) != NULL) { @@ -1853,6 +2005,9 @@ static char *char_insert(char *p, char c) // insert the char c at 'p' bias = text_hole_make(p, len); p += bias; q += bias; +#if ENABLE_FEATURE_VI_UNDO + undo_push(p, len, UNDO_INS); +#endif memcpy(p, q, len); p += len; } @@ -1870,7 +2025,6 @@ static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at bias = text_hole_make(p, 1); p += bias; *p = c; - //file_modified++; - done by text_hole_make() return bias; } @@ -2051,6 +2205,185 @@ static void showmatching(char *p) } #endif /* FEATURE_VI_SETOPTS */ +#if ENABLE_FEATURE_VI_UNDO +// Undo functions and hooks added by Jody Bruchon (jody@jodybruchon.com) +static void undo_push(char *src, unsigned int length, unsigned char u_type) // Add to the undo stack +{ + struct undo_object *undo_temp; + // "u_type" values + // UNDO_INS: insertion, undo will remove from buffer + // UNDO_DEL: deleted text, undo will restore to buffer + // UNDO_{INS,DEL}_CHAIN: Same as above but also calls undo_pop() when complete + // The CHAIN operations are for handling multiple operations that the user + // performs with a single action, i.e. REPLACE mode or find-and-replace commands + // UNDO_{INS,DEL}_QUEUED: If queuing feature is enabled, allow use of the queue + // for the INS/DEL operation. The raw values should be equal to the values of + // UNDO_{INS,DEL} ORed with UNDO_QUEUED_FLAG + +#if ENABLE_FEATURE_VI_UNDO_QUEUE + // This undo queuing functionality groups multiple character typing or backspaces + // into a single large undo object. This greatly reduces calls to malloc() for + // single-character operations while typing and has the side benefit of letting + // an undo operation remove chunks of text rather than a single character. + switch (u_type) { + case UNDO_EMPTY: // Just in case this ever happens... + return; + case UNDO_DEL_QUEUED: + if (length != 1) + return; // Only queue single characters + switch (undo_queue_state) { + case UNDO_EMPTY: + undo_queue_state = UNDO_DEL; + case UNDO_DEL: + undo_queue_spos = src; + undo_q++; + undo_queue[(CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q)] = *src; + // If queue is full, dump it into an object + if (undo_q == CONFIG_FEATURE_VI_UNDO_QUEUE_MAX) + undo_queue_commit(); + return; + case UNDO_INS: + // Switch from storing inserted text to deleted text + undo_queue_commit(); + undo_push(src, length, UNDO_DEL_QUEUED); + return; + } + break; + case UNDO_INS_QUEUED: + if (length != 1) + return; + switch (undo_queue_state) { + case UNDO_EMPTY: + undo_queue_state = UNDO_INS; + undo_queue_spos = src; + case UNDO_INS: + undo_q++; // Don't need to save any data for insertions + if (undo_q == CONFIG_FEATURE_VI_UNDO_QUEUE_MAX) + undo_queue_commit(); + return; + case UNDO_DEL: + // Switch from storing deleted text to inserted text + undo_queue_commit(); + undo_push(src, length, UNDO_INS_QUEUED); + return; + } + break; + } +#else + // If undo queuing is disabled, ignore the queuing flag entirely + u_type = u_type & ~UNDO_QUEUED_FLAG; +#endif + + // Allocate a new undo object and use it as the stack tail + undo_temp = undo_stack_tail; + undo_stack_tail = xmalloc(sizeof(struct undo_object)); +#if ENABLE_FEATURE_VI_UNDO_QUEUE + if ((u_type & UNDO_USE_SPOS) != 0) { + undo_stack_tail->start = undo_queue_spos - text; // use start position from queue + } else { + undo_stack_tail->start = src - text; // use offset from start of text buffer + } + u_type = (u_type & ~UNDO_USE_SPOS); +#else + undo_stack_tail->start = src - text; +#endif /* ENABLE_FEATURE_VI_UNDO_QUEUE */ + // For UNDO_DEL objects, copy the deleted text somewhere + switch (u_type) { + case UNDO_DEL: + case UNDO_DEL_CHAIN: + if ((src + length) == end) + length--; + // If this deletion empties text[], strip the newline. When the buffer becomes + // zero-length, a newline is added back, which requires this to compensate. + undo_stack_tail->undo_text = xmalloc(length); + memcpy(undo_stack_tail->undo_text, src, length); + break; + } + undo_stack_tail->prev = undo_temp; + undo_stack_tail->length = length; + undo_stack_tail->u_type = u_type; + file_modified++; +} + +static void undo_pop(void) // Undo the last operation +{ + int repeat = 0; + char *u_start, *u_end; + struct undo_object *undo_temp; + + // Commit pending undo queue before popping (should be unnecessary) + undo_queue_commit(); + + // Check for an empty undo stack + if (undo_stack_tail == NULL) { + status_line("Already at oldest change"); + return; + } + + switch (undo_stack_tail->u_type) { + case UNDO_DEL: + case UNDO_DEL_CHAIN: + // make hole and put in text that was deleted; deallocate text + u_start = text + undo_stack_tail->start; + text_hole_make(u_start, undo_stack_tail->length); + memcpy(u_start, undo_stack_tail->undo_text, undo_stack_tail->length); + free(undo_stack_tail->undo_text); + status_line("Undo [%d] %s %d chars at position %d", + file_modified, "restored", + undo_stack_tail->length, undo_stack_tail->start); + break; + case UNDO_INS: + case UNDO_INS_CHAIN: + // delete what was inserted + u_start = undo_stack_tail->start + text; + u_end = u_start - 1 + undo_stack_tail->length; + text_hole_delete(u_start, u_end, NO_UNDO); + status_line("Undo [%d] %s %d chars at position %d", + file_modified, "deleted", + undo_stack_tail->length, undo_stack_tail->start); + break; + } + // For chained operations, continue popping all the way down the chain. + // If this is the end of a chain, lower modification count and refresh display + switch (undo_stack_tail->u_type) { + case UNDO_DEL: + case UNDO_INS: + dot = (text + undo_stack_tail->start); + refresh(FALSE); + break; + case UNDO_DEL_CHAIN: + case UNDO_INS_CHAIN: + repeat = 1; + break; + } + // Deallocate the undo object we just processed + undo_temp = undo_stack_tail->prev; + free(undo_stack_tail); + undo_stack_tail = undo_temp; + file_modified--; + if (repeat == 1) { + undo_pop(); // Follow the undo chain if one exists + } +} + +#if ENABLE_FEATURE_VI_UNDO_QUEUE +static void undo_queue_commit(void) // Flush any queued objects to the undo stack +{ + // Pushes the queue object onto the undo stack + if (undo_q > 0) { + // Deleted character undo events grow from the end + undo_push((undo_queue + CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q), + undo_q, + (undo_queue_state | UNDO_USE_SPOS) + ); + undo_queue_state = UNDO_EMPTY; + undo_q = 0; + } +} +#endif + +#endif /* ENABLE_FEATURE_VI_UNDO */ + // open a hole in text[] // might reallocate text[]! use p += text_hole_make(p, ...), // and be careful to not use pointers into potentially freed text[]! @@ -2082,12 +2415,12 @@ static uintptr_t text_hole_make(char *p, int size) // at "p", make a 'size' byte } memmove(p + size, p, end - size - p); memset(p, ' ', size); // clear new hole - file_modified++; return bias; } // close a hole in text[] -static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclusive +// "undo" value indicates if this operation should be undo-able +static char *text_hole_delete(char *p, char *q, int undo) // delete "p" through "q", inclusive { char *src, *dest; int cnt, hole_size; @@ -2102,10 +2435,29 @@ static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclu } hole_size = q - p + 1; cnt = end - src; +#if ENABLE_FEATURE_VI_UNDO + switch (undo) { + case NO_UNDO: + break; + case ALLOW_UNDO: + undo_push(p, hole_size, UNDO_DEL); + break; + case ALLOW_UNDO_CHAIN: + undo_push(p, hole_size, UNDO_DEL_CHAIN); + break; +# if ENABLE_FEATURE_VI_UNDO_QUEUE + case ALLOW_UNDO_QUEUED: + undo_push(p, hole_size, UNDO_DEL_QUEUED); + break; +# endif + } + file_modified--; +#endif if (src < text || src > end) goto thd0; if (dest < text || dest >= end) goto thd0; + file_modified++; if (src >= end) goto thd_atend; // just delete the end of the buffer memmove(dest, src, cnt); @@ -2115,7 +2467,6 @@ static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclu dest = end - 1; // make sure dest in below end-1 if (end <= text) dest = end = text; // keep pointers valid - file_modified++; thd0: return dest; } @@ -2123,7 +2474,7 @@ static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclu // copy text into register, then delete text. // if dist <= 0, do not include, or go past, a NewLine // -static char *yank_delete(char *start, char *stop, int dist, int yf) +static char *yank_delete(char *start, char *stop, int dist, int yf, int undo) { char *p; @@ -2152,7 +2503,7 @@ static char *yank_delete(char *start, char *stop, int dist, int yf) text_yank(start, stop, YDreg); #endif if (yf == YANKDEL) { - p = text_hole_delete(start, stop); + p = text_hole_delete(start, stop, undo); } // delete lines return p; } @@ -2218,12 +2569,22 @@ static void end_cmd_q(void) || ENABLE_FEATURE_VI_CRASHME // might reallocate text[]! use p += string_insert(p, ...), // and be careful to not use pointers into potentially freed text[]! -static uintptr_t string_insert(char *p, const char *s) // insert the string at 'p' +static uintptr_t string_insert(char *p, const char *s, int undo) // insert the string at 'p' { uintptr_t bias; int i; i = strlen(s); +#if ENABLE_FEATURE_VI_UNDO + switch (undo) { + case ALLOW_UNDO: + undo_push(p, i, UNDO_INS); + break; + case ALLOW_UNDO_CHAIN: + undo_push(p, i, UNDO_INS_CHAIN); + break; + } +#endif bias = text_hole_make(p, i); p += bias; memcpy(p, s, i); @@ -2516,14 +2877,14 @@ static int file_insert(const char *fn, char *p, int update_ro_status) cnt = safe_read(fd, p, size); if (cnt < 0) { status_line_bold_errno(fn); - p = text_hole_delete(p, p + size - 1); // un-do buffer insert + p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert } else if (cnt < size) { // There was a partial read, shrink unused space text[] - p = text_hole_delete(p + cnt, p + size - 1); // un-do buffer insert + p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO); // un-do buffer insert status_line_bold("can't read '%s'", fn); } - if (cnt >= size) - file_modified++; +// if (cnt >= size) +// file_modified++; close(fd); fi0: #if ENABLE_FEATURE_VI_READONLY @@ -3048,11 +3409,12 @@ static void do_cmd(int c) if (*dot == '\n') { // don't Replace past E-o-l cmd_mode = 1; // convert to insert + undo_queue_commit(); } else { if (1 <= c || Isprint(c)) { if (c != 27) - dot = yank_delete(dot, dot, 0, YANKDEL); // delete char - dot = char_insert(dot, c); // insert new char + dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char + dot = char_insert(dot, c, ALLOW_UNDO_CHAIN); // insert new char } goto dc1; } @@ -3062,7 +3424,7 @@ static void do_cmd(int c) if (c == KEYCODE_INSERT) goto dc5; // insert the char c at "dot" if (1 <= c || Isprint(c)) { - dot = char_insert(dot, c); + dot = char_insert(dot, c, ALLOW_UNDO_QUEUED); } goto dc1; } @@ -3108,7 +3470,6 @@ static void do_cmd(int c) //case ']': // ]- //case '_': // _- //case '`': // `- - //case 'u': // u- FIXME- there is no undo //case 'v': // v- default: // unrecognized command buf[0] = c; @@ -3177,6 +3538,7 @@ static void do_cmd(int c) if (cmd_mode == 0) indicate_error(c); cmd_mode = 0; // stop insrting + undo_queue_commit(); end_cmd_q(); last_status_cksum = 0; // force status update break; @@ -3251,15 +3613,20 @@ static void do_cmd(int c) if (c == 'p') dot_right(); // move to right, can move to NL } - string_insert(dot, p); // insert the string + string_insert(dot, p, ALLOW_UNDO); // insert the string end_cmd_q(); // stop adding to q break; +#if ENABLE_FEATURE_VI_UNDO + case 'u': // u- undo last operation + undo_pop(); + break; +#endif case 'U': // U- Undo; replace current line with original version if (reg[Ureg] != NULL) { p = begin_line(dot); q = end_line(dot); - p = text_hole_delete(p, q); // delete cur line - p += string_insert(p, reg[Ureg]); // insert orig line + p = text_hole_delete(p, q, ALLOW_UNDO); // delete cur line + p += string_insert(p, reg[Ureg], ALLOW_UNDO_CHAIN); // insert orig line dot = p; dot_skip_over_ws(); } @@ -3485,7 +3852,7 @@ static void do_cmd(int c) cnt = count_lines(text, dot); // remember what line we are on c1 = get_one_char(); // get the type of thing to delete find_range(&p, &q, c1); - yank_delete(p, q, 1, YANKONLY); // save copy before change + yank_delete(p, q, 1, YANKONLY, NO_UNDO); // save copy before change p = begin_line(p); q = end_line(q); i = count_lines(p, q); // # of lines we are shifting @@ -3494,16 +3861,16 @@ static void do_cmd(int c) // shift left- remove tab or 8 spaces if (*p == '\t') { // shrink buffer 1 char - text_hole_delete(p, p); + text_hole_delete(p, p, NO_UNDO); } else if (*p == ' ') { // we should be calculating columns, not just SPACE for (j = 0; *p == ' ' && j < tabstop; j++) { - text_hole_delete(p, p); + text_hole_delete(p, p, NO_UNDO); } } } else if (c == '>') { // shift right -- add tab or 8 spaces - char_insert(p, '\t'); + char_insert(p, '\t', ALLOW_UNDO); } } dot = find_line(cnt); // what line were we on @@ -3538,7 +3905,7 @@ static void do_cmd(int c) save_dot = dot; dot = dollar_line(dot); // move to before NL // copy text into a register and delete - dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l + dot = yank_delete(save_dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete to e-o-l if (c == 'C') goto dc_i; // start inserting #if ENABLE_FEATURE_VI_DOT_CMD @@ -3583,15 +3950,22 @@ static void do_cmd(int c) case KEYCODE_INSERT: // Cursor Key Insert dc_i: cmd_mode = 1; // start inserting + undo_queue_commit(); // commit queue when cmd_mode changes break; case 'J': // J- join current and next lines together do { dot_end(); // move to NL if (dot < end - 1) { // make sure not last char in text[] +#if ENABLE_FEATURE_VI_UNDO + undo_push(dot, 1, UNDO_DEL); *dot++ = ' '; // replace NL with space + undo_push((dot - 1), 1, UNDO_INS_CHAIN); +#else + *dot++ = ' '; file_modified++; +#endif while (isblank(*dot)) { // delete leading WS - dot_delete(); + text_hole_delete(dot, dot, ALLOW_UNDO_CHAIN); } } } while (--cmdcnt > 0); @@ -3620,10 +3994,10 @@ static void do_cmd(int c) dot_prev(); case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..." dot_end(); - dot = char_insert(dot, '\n'); + dot = char_insert(dot, '\n', ALLOW_UNDO); } else { dot_begin(); // 0 - dot = char_insert(dot, '\n'); // i\n ESC + dot = char_insert(dot, '\n', ALLOW_UNDO); // i\n ESC dot_prev(); // - } goto dc_i; @@ -3631,6 +4005,7 @@ static void do_cmd(int c) case 'R': // R- continuous Replace char dc5: cmd_mode = 2; + undo_queue_commit(); break; case KEYCODE_DELETE: c = 'x'; @@ -3645,7 +4020,7 @@ static void do_cmd(int c) if (dot[dir] != '\n') { if (c == 'X') dot--; // delete prev char - dot = yank_delete(dot, dot, 0, YANKDEL); // delete char + dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char } } while (--cmdcnt > 0); end_cmd_q(); // stop adding to q @@ -3716,6 +4091,7 @@ static void do_cmd(int c) c1 = get_one_char(); // get the type of thing to delete // determine range, and whether it spans lines ml = find_range(&p, &q, c1); + place_cursor(0, 0); if (c1 == 27) { // ESC- user changed mind and wants out c = c1 = 27; // Escape- do nothing } else if (strchr("wW", c1)) { @@ -3727,13 +4103,13 @@ static void do_cmd(int c) q--; } } - dot = yank_delete(p, q, ml, yf); // delete word + dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word } else if (strchr("^0bBeEft%$ lh\b\177", c1)) { // partial line copy text into a register and delete - dot = yank_delete(p, q, ml, yf); // delete word + dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word } else if (strchr("cdykjHL+-{}\r\n", c1)) { // whole line copy text into a register and delete - dot = yank_delete(p, q, ml, yf); // delete lines + dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete lines whole = 1; } else { // could not recognize object @@ -3743,7 +4119,7 @@ static void do_cmd(int c) } if (ml && whole) { if (c == 'c') { - dot = char_insert(dot, '\n'); + dot = char_insert(dot, '\n', ALLOW_UNDO_CHAIN); // on the last line of file don't move to prev line if (whole && dot != (end-1)) { dot_prev(); @@ -3789,8 +4165,14 @@ static void do_cmd(int c) case 'r': // r- replace the current char with user input c1 = get_one_char(); // get the replacement char if (*dot != '\n') { +#if ENABLE_FEATURE_VI_UNDO + undo_push(dot, 1, UNDO_DEL); + *dot = c1; + undo_push(dot, 1, UNDO_INS_CHAIN); +#else *dot = c1; file_modified++; +#endif } end_cmd_q(); // stop adding to q break; @@ -3830,6 +4212,17 @@ static void do_cmd(int c) break; case '~': // ~- flip the case of letters a-z -> A-Z do { +#if ENABLE_FEATURE_VI_UNDO + if (islower(*dot)) { + undo_push(dot, 1, UNDO_DEL); + *dot = toupper(*dot); + undo_push(dot, 1, UNDO_INS_CHAIN); + } else if (isupper(*dot)) { + undo_push(dot, 1, UNDO_DEL); + *dot = tolower(*dot); + undo_push(dot, 1, UNDO_INS_CHAIN); + } +#else if (islower(*dot)) { *dot = toupper(*dot); file_modified++; @@ -3837,6 +4230,7 @@ static void do_cmd(int c) *dot = tolower(*dot); file_modified++; } +#endif dot_right(); } while (--cmdcnt > 0); end_cmd_q(); // stop adding to q @@ -3866,7 +4260,7 @@ static void do_cmd(int c) dc1: // if text[] just became empty, add back an empty line if (end == text) { - char_insert(text, '\n'); // start empty buf with dummy line + char_insert(text, '\n', NO_UNDO); // start empty buf with dummy line dot = text; } // it is OK for dot to exactly equal to end, otherwise check dot validity -- cgit v1.2.3-55-g6feb From 2c51202aece44d0f72b7d496ac94823e10d08d54 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 3 Apr 2014 01:45:05 +0200 Subject: vi: undo code shrink function old new delta undo_push 414 395 -19 do_cmd 4803 4761 -42 Signed-off-by: Denys Vlasenko --- editors/vi.c | 83 ++++++++++++++++++++++++++++++------------------------------ 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/editors/vi.c b/editors/vi.c index e38667ad1..71086c901 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -2209,7 +2209,8 @@ static void showmatching(char *p) // Undo functions and hooks added by Jody Bruchon (jody@jodybruchon.com) static void undo_push(char *src, unsigned int length, unsigned char u_type) // Add to the undo stack { - struct undo_object *undo_temp; + struct undo_object *undo_entry; + // "u_type" values // UNDO_INS: insertion, undo will remove from buffer // UNDO_DEL: deleted text, undo will restore to buffer @@ -2237,7 +2238,7 @@ static void undo_push(char *src, unsigned int length, unsigned char u_type) // A case UNDO_DEL: undo_queue_spos = src; undo_q++; - undo_queue[(CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q)] = *src; + undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q] = *src; // If queue is full, dump it into an object if (undo_q == CONFIG_FEATURE_VI_UNDO_QUEUE_MAX) undo_queue_commit(); @@ -2275,80 +2276,80 @@ static void undo_push(char *src, unsigned int length, unsigned char u_type) // A #endif // Allocate a new undo object and use it as the stack tail - undo_temp = undo_stack_tail; - undo_stack_tail = xmalloc(sizeof(struct undo_object)); + undo_entry = xzalloc(sizeof(*undo_entry)); + undo_entry->prev = undo_stack_tail; + undo_stack_tail = undo_entry; #if ENABLE_FEATURE_VI_UNDO_QUEUE if ((u_type & UNDO_USE_SPOS) != 0) { - undo_stack_tail->start = undo_queue_spos - text; // use start position from queue + undo_entry->start = undo_queue_spos - text; // use start position from queue } else { - undo_stack_tail->start = src - text; // use offset from start of text buffer + undo_entry->start = src - text; // use offset from start of text buffer } u_type = (u_type & ~UNDO_USE_SPOS); #else - undo_stack_tail->start = src - text; -#endif /* ENABLE_FEATURE_VI_UNDO_QUEUE */ + undo_entry->start = src - text; +#endif // For UNDO_DEL objects, copy the deleted text somewhere - switch (u_type) { - case UNDO_DEL: - case UNDO_DEL_CHAIN: - if ((src + length) == end) - length--; - // If this deletion empties text[], strip the newline. When the buffer becomes - // zero-length, a newline is added back, which requires this to compensate. - undo_stack_tail->undo_text = xmalloc(length); - memcpy(undo_stack_tail->undo_text, src, length); - break; + undo_entry->u_type = u_type; + if (u_type == UNDO_DEL || u_type == UNDO_DEL_CHAIN) { + if ((src + length) == end) + length--; + // If this deletion empties text[], strip the newline. When the buffer becomes + // zero-length, a newline is added back, which requires this to compensate. + undo_entry->undo_text = xmalloc(length); + memcpy(undo_entry->undo_text, src, length); } - undo_stack_tail->prev = undo_temp; - undo_stack_tail->length = length; - undo_stack_tail->u_type = u_type; + undo_entry->length = length; file_modified++; } static void undo_pop(void) // Undo the last operation { - int repeat = 0; + int repeat; char *u_start, *u_end; - struct undo_object *undo_temp; + struct undo_object *undo_entry; // Commit pending undo queue before popping (should be unnecessary) undo_queue_commit(); + undo_entry = undo_stack_tail; // Check for an empty undo stack - if (undo_stack_tail == NULL) { + if (!undo_entry) { status_line("Already at oldest change"); return; } - switch (undo_stack_tail->u_type) { + switch (undo_entry->u_type) { case UNDO_DEL: case UNDO_DEL_CHAIN: // make hole and put in text that was deleted; deallocate text - u_start = text + undo_stack_tail->start; - text_hole_make(u_start, undo_stack_tail->length); - memcpy(u_start, undo_stack_tail->undo_text, undo_stack_tail->length); - free(undo_stack_tail->undo_text); + u_start = text + undo_entry->start; + text_hole_make(u_start, undo_entry->length); + memcpy(u_start, undo_entry->undo_text, undo_entry->length); + free(undo_entry->undo_text); status_line("Undo [%d] %s %d chars at position %d", file_modified, "restored", - undo_stack_tail->length, undo_stack_tail->start); + undo_entry->length, undo_entry->start + ); break; case UNDO_INS: case UNDO_INS_CHAIN: // delete what was inserted - u_start = undo_stack_tail->start + text; - u_end = u_start - 1 + undo_stack_tail->length; + u_start = undo_entry->start + text; + u_end = u_start - 1 + undo_entry->length; text_hole_delete(u_start, u_end, NO_UNDO); status_line("Undo [%d] %s %d chars at position %d", file_modified, "deleted", - undo_stack_tail->length, undo_stack_tail->start); + undo_entry->length, undo_entry->start + ); break; } - // For chained operations, continue popping all the way down the chain. + repeat = 0; + switch (undo_entry->u_type) { // If this is the end of a chain, lower modification count and refresh display - switch (undo_stack_tail->u_type) { case UNDO_DEL: case UNDO_INS: - dot = (text + undo_stack_tail->start); + dot = (text + undo_entry->start); refresh(FALSE); break; case UNDO_DEL_CHAIN: @@ -2357,11 +2358,11 @@ static void undo_pop(void) // Undo the last operation break; } // Deallocate the undo object we just processed - undo_temp = undo_stack_tail->prev; - free(undo_stack_tail); - undo_stack_tail = undo_temp; + undo_stack_tail = undo_entry->prev; + free(undo_entry); file_modified--; - if (repeat == 1) { + // For chained operations, continue popping all the way down the chain. + if (repeat) { undo_pop(); // Follow the undo chain if one exists } } @@ -2372,7 +2373,7 @@ static void undo_queue_commit(void) // Flush any queued objects to the undo stac // Pushes the queue object onto the undo stack if (undo_q > 0) { // Deleted character undo events grow from the end - undo_push((undo_queue + CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q), + undo_push(undo_queue + CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q, undo_q, (undo_queue_state | UNDO_USE_SPOS) ); -- cgit v1.2.3-55-g6feb From e7430867a8ad61bbea20d05fc2f9fc5094e211dc Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 3 Apr 2014 12:47:48 +0200 Subject: vi: clear undo buffer when we change to another file function old new delta init_text_buffer 156 190 +34 undo_push 360 382 +22 count_lines 74 72 -2 undo_pop 246 222 -24 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/2 up/down: 56/-26) Total: 30 bytes Signed-off-by: Denys Vlasenko --- editors/vi.c | 156 +++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 87 insertions(+), 69 deletions(-) diff --git a/editors/vi.c b/editors/vi.c index 71086c901..a978e0fcb 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -306,8 +306,8 @@ struct globals { smallint editing; // >0 while we are editing a file // [code audit says "can be 0, 1 or 2 only"] smallint cmd_mode; // 0=command 1=insert 2=replace - int file_modified; // buffer contents changed (counter, not flag!) - int last_file_modified; // = -1; + int modified_count; // buffer contents changed if !0 + int last_modified_count; // = -1; int save_argc; // how many file names on cmd line int cmdcnt; // repetition count unsigned rows, columns; // the terminal screen is this size @@ -378,37 +378,37 @@ struct globals { char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2]; #if ENABLE_FEATURE_VI_UNDO // undo_push() operations -#define UNDO_INS 0 -#define UNDO_DEL 1 -#define UNDO_INS_CHAIN 2 -#define UNDO_DEL_CHAIN 3 +#define UNDO_INS 0 +#define UNDO_DEL 1 +#define UNDO_INS_CHAIN 2 +#define UNDO_DEL_CHAIN 3 // UNDO_*_QUEUED must be equal to UNDO_xxx ORed with UNDO_QUEUED_FLAG #define UNDO_QUEUED_FLAG 4 -#define UNDO_INS_QUEUED 4 -#define UNDO_DEL_QUEUED 5 -#define UNDO_USE_SPOS 32 -#define UNDO_EMPTY 64 +#define UNDO_INS_QUEUED 4 +#define UNDO_DEL_QUEUED 5 +#define UNDO_USE_SPOS 32 +#define UNDO_EMPTY 64 // Pass-through flags for functions that can be undone -#define NO_UNDO 0 -#define ALLOW_UNDO 1 +#define NO_UNDO 0 +#define ALLOW_UNDO 1 #define ALLOW_UNDO_CHAIN 2 -#if ENABLE_FEATURE_VI_UNDO_QUEUE +# if ENABLE_FEATURE_VI_UNDO_QUEUE #define ALLOW_UNDO_QUEUED 3 char undo_queue_state; int undo_q; char *undo_queue_spos; // Start position of queued operation char undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX]; -#else +# else // If undo queuing disabled, don't invoke the missing queue logic #define ALLOW_UNDO_QUEUED 1 -#endif /* ENABLE_FEATURE_VI_UNDO_QUEUE */ +# endif struct undo_object { struct undo_object *prev; // Linking back avoids list traversal (LIFO) - int u_type; // 0=deleted, 1=inserted, 2=swapped int start; // Offset where the data should be restored/deleted int length; // total data size - char *undo_text; // ptr to text that will be inserted + uint8_t u_type; // 0=deleted, 1=inserted, 2=swapped + char undo_text[1]; // text that was deleted (if deletion) } *undo_stack_tail; #endif /* ENABLE_FEATURE_VI_UNDO */ @@ -423,8 +423,8 @@ struct globals { #define vi_setops (G.vi_setops ) #define editing (G.editing ) #define cmd_mode (G.cmd_mode ) -#define file_modified (G.file_modified ) -#define last_file_modified (G.last_file_modified ) +#define modified_count (G.modified_count ) +#define last_modified_count (G.last_modified_count) #define save_argc (G.save_argc ) #define cmdcnt (G.cmdcnt ) #define rows (G.rows ) @@ -485,7 +485,7 @@ struct globals { #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ - last_file_modified = -1; \ + last_modified_count = -1; \ /* "" but has space for 2 chars: */ \ IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \ } while (0) @@ -608,6 +608,7 @@ static char what_reg(void); // what is letter of current YDreg static void check_context(char); // remember context for '' command #endif #if ENABLE_FEATURE_VI_UNDO +static void flush_undo_data(void); static void undo_push(char *, unsigned int, unsigned char); // Push an operation on the undo stack static void undo_pop(void); // Undo the last operation # if ENABLE_FEATURE_VI_UNDO_QUEUE @@ -616,6 +617,7 @@ static void undo_queue_commit(void); // Flush any queued objects to the undo sta # define undo_queue_commit() ((void)0) # endif #else +#define flush_undo_data() ((void)0) #define undo_queue_commit() ((void)0) #endif @@ -725,6 +727,8 @@ static int init_text_buffer(char *fn) int rc; int size = file_size(fn); // file size. -1 means does not exist. + flush_undo_data(); + /* allocate/reallocate text buffer */ free(text); text_size = size + 10240; @@ -735,14 +739,14 @@ static int init_text_buffer(char *fn) current_filename = xstrdup(fn); } if (size < 0) { - // file dont exist. Start empty buf with dummy line + // file doesnt exist. Start empty buf with dummy line char_insert(text, '\n', NO_UNDO); rc = 0; } else { rc = file_insert(fn, text, 1); } - file_modified = 0; - last_file_modified = -1; + modified_count = 0; + last_modified_count = -1; #if ENABLE_FEATURE_VI_YANKMARK /* init the marks. */ memset(mark, 0, sizeof(mark)); @@ -1124,7 +1128,7 @@ static void colon(char *buf) dot_skip_over_ws(); } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file // don't edit, if the current file has been modified - if (file_modified && !useforce) { + if (modified_count && !useforce) { status_line_bold("No write since last change (:%s! overrides)", cmd); goto ret; } @@ -1227,7 +1231,7 @@ static void colon(char *buf) goto ret; } // don't exit if the file been modified - if (file_modified) { + if (modified_count) { status_line_bold("No write since last change (:%s! overrides)", cmd); goto ret; } @@ -1280,10 +1284,10 @@ static void colon(char *buf) // if the insert is before "dot" then we need to update if (q <= dot) dot += ch; - // file_modified++; + // modified_count++; } } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args - if (file_modified && !useforce) { + if (modified_count && !useforce) { status_line_bold("No write since last change (:%s! overrides)", cmd); } else { // reset the filenames to edit @@ -1438,8 +1442,8 @@ static void colon(char *buf) } else { status_line("'%s' %dL, %dC", fn, li, l); if (q == text && r == end - 1 && l == ch) { - file_modified = 0; - last_file_modified = -1; + modified_count = 0; + last_modified_count = -1; } if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' || cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N' @@ -1940,7 +1944,7 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' # endif } #else - file_modified++; + modified_count++; #endif /* ENABLE_FEATURE_VI_UNDO */ p++; } else if (c == 27) { // Is this an ESC? @@ -1988,7 +1992,7 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' # endif } #else - file_modified++; + modified_count++; #endif /* ENABLE_FEATURE_VI_UNDO */ p += 1 + stupid_insert(p, c); // insert the char #if ENABLE_FEATURE_VI_SETOPTS @@ -2206,8 +2210,19 @@ static void showmatching(char *p) #endif /* FEATURE_VI_SETOPTS */ #if ENABLE_FEATURE_VI_UNDO +static void flush_undo_data(void) +{ + struct undo_object *undo_entry; + + while (undo_stack_tail) { + undo_entry = undo_stack_tail; + undo_stack_tail = undo_entry->prev; + free(undo_entry); + } +} + // Undo functions and hooks added by Jody Bruchon (jody@jodybruchon.com) -static void undo_push(char *src, unsigned int length, unsigned char u_type) // Add to the undo stack +static void undo_push(char *src, unsigned int length, uint8_t u_type) // Add to the undo stack { struct undo_object *undo_entry; @@ -2275,10 +2290,19 @@ static void undo_push(char *src, unsigned int length, unsigned char u_type) // A u_type = u_type & ~UNDO_QUEUED_FLAG; #endif - // Allocate a new undo object and use it as the stack tail - undo_entry = xzalloc(sizeof(*undo_entry)); - undo_entry->prev = undo_stack_tail; - undo_stack_tail = undo_entry; + // Allocate a new undo object + if (u_type == UNDO_DEL || u_type == UNDO_DEL_CHAIN) { + // For UNDO_DEL objects, save deleted text + if ((src + length) == end) + length--; + // If this deletion empties text[], strip the newline. When the buffer becomes + // zero-length, a newline is added back, which requires this to compensate. + undo_entry = xzalloc(offsetof(struct undo_object, undo_text) + length); + memcpy(undo_entry->undo_text, src, length); + } else { + undo_entry = xzalloc(sizeof(*undo_entry)); + } + undo_entry->length = length; #if ENABLE_FEATURE_VI_UNDO_QUEUE if ((u_type & UNDO_USE_SPOS) != 0) { undo_entry->start = undo_queue_spos - text; // use start position from queue @@ -2289,18 +2313,12 @@ static void undo_push(char *src, unsigned int length, unsigned char u_type) // A #else undo_entry->start = src - text; #endif - // For UNDO_DEL objects, copy the deleted text somewhere undo_entry->u_type = u_type; - if (u_type == UNDO_DEL || u_type == UNDO_DEL_CHAIN) { - if ((src + length) == end) - length--; - // If this deletion empties text[], strip the newline. When the buffer becomes - // zero-length, a newline is added back, which requires this to compensate. - undo_entry->undo_text = xmalloc(length); - memcpy(undo_entry->undo_text, src, length); - } - undo_entry->length = length; - file_modified++; + + // Push it on undo stack + undo_entry->prev = undo_stack_tail; + undo_stack_tail = undo_entry; + modified_count++; } static void undo_pop(void) // Undo the last operation @@ -2326,9 +2344,8 @@ static void undo_pop(void) // Undo the last operation u_start = text + undo_entry->start; text_hole_make(u_start, undo_entry->length); memcpy(u_start, undo_entry->undo_text, undo_entry->length); - free(undo_entry->undo_text); status_line("Undo [%d] %s %d chars at position %d", - file_modified, "restored", + modified_count, "restored", undo_entry->length, undo_entry->start ); break; @@ -2339,7 +2356,7 @@ static void undo_pop(void) // Undo the last operation u_end = u_start - 1 + undo_entry->length; text_hole_delete(u_start, u_end, NO_UNDO); status_line("Undo [%d] %s %d chars at position %d", - file_modified, "deleted", + modified_count, "deleted", undo_entry->length, undo_entry->start ); break; @@ -2360,7 +2377,7 @@ static void undo_pop(void) // Undo the last operation // Deallocate the undo object we just processed undo_stack_tail = undo_entry->prev; free(undo_entry); - file_modified--; + modified_count--; // For chained operations, continue popping all the way down the chain. if (repeat) { undo_pop(); // Follow the undo chain if one exists @@ -2452,13 +2469,13 @@ static char *text_hole_delete(char *p, char *q, int undo) // delete "p" through break; # endif } - file_modified--; + modified_count--; #endif if (src < text || src > end) goto thd0; if (dest < text || dest >= end) goto thd0; - file_modified++; + modified_count++; if (src >= end) goto thd_atend; // just delete the end of the buffer memmove(dest, src, cnt); @@ -2885,7 +2902,7 @@ static int file_insert(const char *fn, char *p, int update_ro_status) status_line_bold("can't read '%s'", fn); } // if (cnt >= size) -// file_modified++; +// modified_count++; close(fd); fi0: #if ENABLE_FEATURE_VI_READONLY @@ -2922,7 +2939,7 @@ static int file_write(char *fn, char *first, char *last) ftruncate(fd, charcnt); if (charcnt == cnt) { // good write - //file_modified = FALSE; + //modified_count = FALSE; } else { charcnt = 0; } @@ -3144,7 +3161,7 @@ static int format_edit_status(void) int cur, percent, ret, trunc_at; - // file_modified is now a counter rather than a flag. this + // modified_count is now a counter rather than a flag. this // helps reduce the amount of line counting we need to do. // (this will cause a mis-reporting of modified status // once every MAXINT editing operations.) @@ -3154,11 +3171,12 @@ static int format_edit_status(void) // we're on, then we shouldn't have to do this count_lines() cur = count_lines(text, dot); - // reduce counting -- the total lines can't have - // changed if we haven't done any edits. - if (file_modified != last_file_modified) { + // count_lines() is expensive. + // Call it only if something was changed since last time + // we were here: + if (modified_count != last_modified_count) { tot = cur + count_lines(dot, end - 1) - 1; - last_file_modified = file_modified; + last_modified_count = modified_count; } // current line percent @@ -3185,7 +3203,7 @@ static int format_edit_status(void) #if ENABLE_FEATURE_VI_READONLY (readonly_mode ? " [Readonly]" : ""), #endif - (file_modified ? " [Modified]" : ""), + (modified_count ? " [Modified]" : ""), cur, tot, percent); if (ret >= 0 && ret < trunc_at) @@ -3814,7 +3832,7 @@ static void do_cmd(int c) if (strncmp(p, "quit", cnt) == 0 || strncmp(p, "q!", cnt) == 0 // delete lines ) { - if (file_modified && p[1] != '!') { + if (modified_count && p[1] != '!') { status_line_bold("No write since last change (:%s! overrides)", p); } else { editing = 0; @@ -3829,8 +3847,8 @@ static void do_cmd(int c) if (cnt == -1) status_line_bold("Write error: %s", strerror(errno)); } else { - file_modified = 0; - last_file_modified = -1; + modified_count = 0; + last_modified_count = -1; status_line("'%s' %dL, %dC", current_filename, count_lines(text, end - 1), cnt); if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n' || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N' @@ -3963,7 +3981,7 @@ static void do_cmd(int c) undo_push((dot - 1), 1, UNDO_INS_CHAIN); #else *dot++ = ' '; - file_modified++; + modified_count++; #endif while (isblank(*dot)) { // delete leading WS text_hole_delete(dot, dot, ALLOW_UNDO_CHAIN); @@ -4035,7 +4053,7 @@ static void do_cmd(int c) indicate_error(c); break; } - if (file_modified) { + if (modified_count) { if (ENABLE_FEATURE_VI_READONLY && readonly_mode) { status_line_bold("'%s' is read only", current_filename); break; @@ -4172,7 +4190,7 @@ static void do_cmd(int c) undo_push(dot, 1, UNDO_INS_CHAIN); #else *dot = c1; - file_modified++; + modified_count++; #endif } end_cmd_q(); // stop adding to q @@ -4226,10 +4244,10 @@ static void do_cmd(int c) #else if (islower(*dot)) { *dot = toupper(*dot); - file_modified++; + modified_count++; } else if (isupper(*dot)) { *dot = tolower(*dot); - file_modified++; + modified_count++; } #endif dot_right(); -- cgit v1.2.3-55-g6feb From 32afd3aa6039b911816a68972b2366095cb777de Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 5 Apr 2014 22:57:46 +0200 Subject: vi: some simplifications function old new delta file_insert 301 315 +14 init_text_buffer 179 171 -8 colon 2889 2878 -11 file_size 37 - -37 ------------------------------------------------------------------------------ (add/remove: 0/1 grow/shrink: 1/2 up/down: 14/-56) Total: -42 bytes Signed-off-by: Denys Vlasenko --- editors/vi.c | 236 ++++++++++++++++++++++++++++++----------------------------- 1 file changed, 120 insertions(+), 116 deletions(-) diff --git a/editors/vi.c b/editors/vi.c index a978e0fcb..24a9f60a8 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -491,7 +491,6 @@ struct globals { } while (0) -static int init_text_buffer(char *); // init from file or create new static void edit_file(char *); // edit one file static void do_cmd(int); // execute a command static int next_tabstop(int); @@ -543,7 +542,6 @@ static void cookmode(void); // return to "cooked" mode on tty static int mysleep(int); static int readit(void); // read (maybe cursor) key from stdin static int get_one_char(void); // read 1 char from stdin -static int file_size(const char *); // what is the byte size of "fn" #if !ENABLE_FEATURE_VI_READONLY #define file_insert(fn, p, update_ro_status) file_insert(fn, p) #endif @@ -578,8 +576,8 @@ static char *char_search(char *, const char *, int, int); // search for pattern #if ENABLE_FEATURE_VI_COLON static char *get_one_address(char *, int *); // get colon addr, if present static char *get_address(char *, int *, int *); // get two colon addrs, if present -static void colon(char *); // execute the "colon" mode cmds #endif +static void colon(char *); // execute the "colon" mode cmds #if ENABLE_FEATURE_VI_USE_SIGNALS static void winch_sig(int); // catch window size changes static void suspend_sig(int); // catch ctrl-Z @@ -725,32 +723,29 @@ int vi_main(int argc, char **argv) static int init_text_buffer(char *fn) { int rc; - int size = file_size(fn); // file size. -1 means does not exist. flush_undo_data(); + modified_count = 0; + last_modified_count = -1; +#if ENABLE_FEATURE_VI_YANKMARK + /* init the marks */ + memset(mark, 0, sizeof(mark)); +#endif /* allocate/reallocate text buffer */ free(text); - text_size = size + 10240; + text_size = 10240; screenbegin = dot = end = text = xzalloc(text_size); if (fn != current_filename) { free(current_filename); current_filename = xstrdup(fn); } - if (size < 0) { + rc = file_insert(fn, text, 1); + if (rc < 0) { // file doesnt exist. Start empty buf with dummy line char_insert(text, '\n', NO_UNDO); - rc = 0; - } else { - rc = file_insert(fn, text, 1); } - modified_count = 0; - last_modified_count = -1; -#if ENABLE_FEATURE_VI_YANKMARK - /* init the marks. */ - memset(mark, 0, sizeof(mark)); -#endif return rc; } @@ -1018,13 +1013,71 @@ static void setops(const char *args, const char *opname, int flg_no, } #endif +#endif /* FEATURE_VI_COLON */ + // buf must be no longer than MAX_INPUT_LEN! static void colon(char *buf) { +#if !ENABLE_FEATURE_VI_COLON + /* Simple ":cmd" handler with minimal set of commands */ + char *p = buf; + int cnt; + + if (*p == ':') + p++; + cnt = strlen(p); + if (cnt == 0) + return; + if (strncmp(p, "quit", cnt) == 0 + || strncmp(p, "q!", cnt) == 0 + ) { + if (modified_count && p[1] != '!') { + status_line_bold("No write since last change (:%s! overrides)", p); + } else { + editing = 0; + } + return; + } + if (strncmp(p, "write", cnt) == 0 + || strncmp(p, "wq", cnt) == 0 + || strncmp(p, "wn", cnt) == 0 + || (p[0] == 'x' && !p[1]) + ) { + cnt = file_write(current_filename, text, end - 1); + if (cnt < 0) { + if (cnt == -1) + status_line_bold("Write error: %s", strerror(errno)); + } else { + modified_count = 0; + last_modified_count = -1; + status_line("'%s' %dL, %dC", + current_filename, + count_lines(text, end - 1), cnt + ); + if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n' + || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N' + ) { + editing = 0; + } + } + return; + } + if (strncmp(p, "file", cnt) == 0) { + last_status_cksum = 0; // force status update + return; + } + if (sscanf(p, "%d", &cnt) > 0) { + dot = find_line(cnt); + dot_skip_over_ws(); + return; + } + not_implemented(p); +#else + char c, *orig_buf, *buf1, *q, *r; char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN]; - int i, l, li, ch, b, e; - int useforce, forced = FALSE; + int i, l, li, b, e; + int useforce; // :3154 // if (-e line 3154) goto it else stay put // :4,33w! foo // write a portion of buffer to file "foo" @@ -1046,7 +1099,7 @@ static void colon(char *buf) if (*buf == ':') buf++; // move past the ':' - li = ch = i = 0; + li = i = 0; b = e = -1; q = text; // assume 1,$ for the range r = end - 1; @@ -1127,6 +1180,8 @@ static void colon(char *buf) dot = yank_delete(q, r, 1, YANKDEL, ALLOW_UNDO); // save, then delete lines dot_skip_over_ws(); } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file + int size; + // don't edit, if the current file has been modified if (modified_count && !useforce) { status_line_bold("No write since last change (:%s! overrides)", cmd); @@ -1144,8 +1199,7 @@ static void colon(char *buf) goto ret; } - if (init_text_buffer(fn) < 0) - goto ret; + size = init_text_buffer(fn); #if ENABLE_FEATURE_VI_YANKMARK if (Ureg >= 0 && Ureg < 28) { @@ -1161,12 +1215,14 @@ static void colon(char *buf) li = count_lines(text, end - 1); status_line("'%s'%s" IF_FEATURE_VI_READONLY("%s") - " %dL, %dC", current_filename, - (file_size(fn) < 0 ? " [New file]" : ""), + " %dL, %dC", + current_filename, + (size < 0 ? " [New file]" : ""), IF_FEATURE_VI_READONLY( ((readonly_mode) ? " [Readonly]" : ""), ) - li, ch); + li, (int)(end - text) + ); } else if (strncmp(cmd, "file", i) == 0) { // what File is this if (b != -1 || e != -1) { status_line_bold("No address allowed on this command"); @@ -1255,6 +1311,8 @@ static void colon(char *buf) } editing = 0; } else if (strncmp(cmd, "read", i) == 0) { // read file into text[] + int size; + fn = args; if (!fn[0]) { status_line_bold("No filename given"); @@ -1268,23 +1326,24 @@ static void colon(char *buf) q = next_line(q); { // dance around potentially-reallocated text[] uintptr_t ofs = q - text; - ch = file_insert(fn, q, 0); + size = file_insert(fn, q, /*update_ro:*/ 0); q = text + ofs; } - if (ch < 0) + if (size < 0) goto ret; // nothing was inserted // how many lines in text[]? - li = count_lines(q, q + ch - 1); + li = count_lines(q, q + size - 1); status_line("'%s'" IF_FEATURE_VI_READONLY("%s") - " %dL, %dC", fn, + " %dL, %dC", + fn, IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),) - li, ch); - if (ch > 0) { + li, size + ); + if (size > 0) { // if the insert is before "dot" then we need to update if (q <= dot) - dot += ch; - // modified_count++; + dot += size; } } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args if (modified_count && !useforce) { @@ -1409,6 +1468,9 @@ static void colon(char *buf) || strncmp(cmd, "wn", i) == 0 || (cmd[0] == 'x' && !cmd[1]) ) { + int size; + //int forced = FALSE; + // is there a file name to write to? if (args[0]) { fn = args; @@ -1421,34 +1483,33 @@ static void colon(char *buf) #endif // how many lines in text[]? li = count_lines(q, r); - ch = r - q + 1; - // see if file exists- if not, its just a new file request - if (useforce) { + size = r - q + 1; + //if (useforce) { // if "fn" is not write-able, chmod u+w // sprintf(syscmd, "chmod u+w %s", fn); // system(syscmd); - forced = TRUE; - } + // forced = TRUE; + //} l = file_write(fn, q, r); - if (useforce && forced) { + //if (useforce && forced) { // chmod u-w // sprintf(syscmd, "chmod u-w %s", fn); // system(syscmd); - forced = FALSE; - } + // forced = FALSE; + //} if (l < 0) { if (l == -1) status_line_bold_errno(fn); } else { status_line("'%s' %dL, %dC", fn, li, l); - if (q == text && r == end - 1 && l == ch) { + if (q == text && r == end - 1 && l == size) { modified_count = 0; last_modified_count = -1; } if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' || cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N' ) - && l == ch + && l == size ) { editing = 0; } @@ -1475,9 +1536,8 @@ static void colon(char *buf) colon_s_fail: status_line(":s expression missing delimiters"); #endif -} - #endif /* FEATURE_VI_COLON */ +} static void Hit_Return(void) { @@ -2851,17 +2911,6 @@ static char *get_input_line(const char *prompt) #undef buf } -static int file_size(const char *fn) // what is the byte size of "fn" -{ - struct stat st_buf; - int cnt; - - cnt = -1; - if (fn && stat(fn, &st_buf) == 0) // see if file exists - cnt = (int) st_buf.st_size; - return cnt; -} - // might reallocate text[]! static int file_insert(const char *fn, char *p, int update_ro_status) { @@ -2869,42 +2918,40 @@ static int file_insert(const char *fn, char *p, int update_ro_status) int fd, size; struct stat statbuf; - /* Validate file */ - if (stat(fn, &statbuf) < 0) { - status_line_bold_errno(fn); - goto fi0; - } - if (!S_ISREG(statbuf.st_mode)) { - // This is not a regular file - status_line_bold("'%s' is not a regular file", fn); - goto fi0; - } if (p < text || p > end) { status_line_bold("Trying to insert file outside of memory"); - goto fi0; + return cnt; } - // read file to buffer fd = open(fn, O_RDONLY); if (fd < 0) { status_line_bold_errno(fn); - goto fi0; + return cnt; + } + + /* Validate file */ + if (fstat(fd, &statbuf) < 0) { + status_line_bold_errno(fn); + goto fi; + } + if (!S_ISREG(statbuf.st_mode)) { + status_line_bold("'%s' is not a regular file", fn); + goto fi; } size = (statbuf.st_size < INT_MAX ? (int)statbuf.st_size : INT_MAX); p += text_hole_make(p, size); - cnt = safe_read(fd, p, size); + cnt = full_read(fd, p, size); if (cnt < 0) { status_line_bold_errno(fn); p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert } else if (cnt < size) { - // There was a partial read, shrink unused space text[] - p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO); // un-do buffer insert + // There was a partial read, shrink unused space + p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO); status_line_bold("can't read '%s'", fn); } -// if (cnt >= size) -// modified_count++; + fi: close(fd); - fi0: + #if ENABLE_FEATURE_VI_READONLY if (update_ro_status && ((access(fn, W_OK) < 0) || @@ -3821,50 +3868,7 @@ static void do_cmd(int c) break; case ':': // :- the colon mode commands p = get_input_line(":"); // get input line- use "status line" -#if ENABLE_FEATURE_VI_COLON colon(p); // execute the command -#else - if (*p == ':') - p++; // move past the ':' - cnt = strlen(p); - if (cnt <= 0) - break; - if (strncmp(p, "quit", cnt) == 0 - || strncmp(p, "q!", cnt) == 0 // delete lines - ) { - if (modified_count && p[1] != '!') { - status_line_bold("No write since last change (:%s! overrides)", p); - } else { - editing = 0; - } - } else if (strncmp(p, "write", cnt) == 0 - || strncmp(p, "wq", cnt) == 0 - || strncmp(p, "wn", cnt) == 0 - || (p[0] == 'x' && !p[1]) - ) { - cnt = file_write(current_filename, text, end - 1); - if (cnt < 0) { - if (cnt == -1) - status_line_bold("Write error: %s", strerror(errno)); - } else { - modified_count = 0; - last_modified_count = -1; - status_line("'%s' %dL, %dC", current_filename, count_lines(text, end - 1), cnt); - if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n' - || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N' - ) { - editing = 0; - } - } - } else if (strncmp(p, "file", cnt) == 0) { - last_status_cksum = 0; // force status update - } else if (sscanf(p, "%d", &j) > 0) { - dot = find_line(j); // go to line # j - dot_skip_over_ws(); - } else { // unrecognized cmd - not_implemented(p); - } -#endif /* !FEATURE_VI_COLON */ break; case '<': // <- Left shift something case '>': // >- Right shift something -- cgit v1.2.3-55-g6feb From 69b114fb8a2566e14ce125f7736add9dacf6e18d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 7 Apr 2014 23:32:29 +0200 Subject: less: fix bugs discovered with "git log -p | less -m" on kernel tree function old new delta read_lines 685 733 +48 Signed-off-by: Denys Vlasenko --- miscutils/less.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/miscutils/less.c b/miscutils/less.c index 574f222e0..36d0a0bd9 100644 --- a/miscutils/less.c +++ b/miscutils/less.c @@ -404,6 +404,9 @@ static void fill_match_lines(unsigned pos); * last_line_pos - screen line position of next char to be read * (takes into account tabs and backspaces) * eof_error - < 0 error, == 0 EOF, > 0 not EOF/error + * + * "git log -p | less -m" on the kernel git tree is a good test for EAGAINs, + * "/search on very long input" and "reaching max line count" corner cases. */ static void read_lines(void) { @@ -414,9 +417,13 @@ static void read_lines(void) #if ENABLE_FEATURE_LESS_REGEXP unsigned old_max_fline = max_fline; time_t last_time = 0; - unsigned seconds_p1 = 3; /* seconds_to_loop + 1 */ + int had_progress = 2; #endif + /* (careful: max_fline can be -1) */ + if (max_fline + 1 > MAXLINES) + return; + if (option_mask32 & FLAG_N) w -= 8; @@ -441,6 +448,7 @@ static void read_lines(void) char c; /* if no unprocessed chars left, eat more */ if (readpos >= readeof) { + errno = 0; ndelay_on(0); eof_error = safe_read(STDIN_FILENO, readbuf, sizeof(readbuf)); ndelay_off(0); @@ -448,6 +456,7 @@ static void read_lines(void) readeof = eof_error; if (eof_error <= 0) goto reached_eof; + had_progress = 1; } c = readbuf[readpos]; /* backspace? [needed for manpages] */ @@ -519,31 +528,23 @@ static void read_lines(void) #endif } if (eof_error <= 0) { - if (eof_error < 0) { - if (errno == EAGAIN) { - /* not yet eof or error, reset flag (or else - * we will hog CPU - select() will return - * immediately */ - eof_error = 1; - } else { - print_statusline(bb_msg_read_error); - } - } #if !ENABLE_FEATURE_LESS_REGEXP break; #else if (wanted_match < num_matches) { break; - } else { /* goto_match called us */ + } /* else: goto_match() called us */ + if (errno == EAGAIN) { time_t t = time(NULL); if (t != last_time) { last_time = t; - if (--seconds_p1 == 0) + if (--had_progress < 0) break; } sched_yield(); - goto again0; /* go loop again (max 2 seconds) */ + goto again0; } + break; #endif } max_fline++; @@ -551,6 +552,15 @@ static void read_lines(void) p = current_line; last_line_pos = 0; } /* end of "read lines until we reach cur_fline" loop */ + + if (eof_error < 0) { + if (errno == EAGAIN) { + eof_error = 1; + } else { + print_statusline(bb_msg_read_error); + } + } + fill_match_lines(old_max_fline); #if ENABLE_FEATURE_LESS_REGEXP /* prevent us from being stuck in search for a match */ -- cgit v1.2.3-55-g6feb From ad16741ccd8a8587644d88fb8fdfc41ada1928a6 Mon Sep 17 00:00:00 2001 From: Bernhard Reutner-Fischer Date: Sun, 13 Apr 2014 16:37:57 +0200 Subject: libbb: provide usleep() fallback implementation POSIX.1-2008 removed the usleep function, provide a fallback implementaion using the recommended nanosleep(). Signed-off-by: Bernhard Reutner-Fischer Signed-off-by: Denys Vlasenko --- include/platform.h | 16 ++++++++++++++-- libbb/platform.c | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/include/platform.h b/include/platform.h index bd11ad69a..92f775551 100644 --- a/include/platform.h +++ b/include/platform.h @@ -373,6 +373,7 @@ typedef unsigned smalluint; #define HAVE_STRSIGNAL 1 #define HAVE_STRVERSCMP 1 #define HAVE_VASPRINTF 1 +#define HAVE_USLEEP 1 #define HAVE_UNLOCKED_STDIO 1 #define HAVE_UNLOCKED_LINE_OPS 1 #define HAVE_GETLINE 1 @@ -381,8 +382,15 @@ typedef unsigned smalluint; #define HAVE_NET_ETHERNET_H 1 #define HAVE_SYS_STATFS_H 1 -#if defined(__UCLIBC__) && UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32) -# undef HAVE_STRVERSCMP +#if defined(__UCLIBC__) +# if UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32) +# undef HAVE_STRVERSCMP +# endif +# if UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 30) +# ifndef __UCLIBC_SUSV3_LEGACY__ +# undef HAVE_USLEEP +# endif +# endif #endif #if defined(__WATCOMC__) @@ -519,6 +527,10 @@ extern char *strsep(char **stringp, const char *delim) FAST_FUNC; # define strsignal(sig) get_signame(sig) #endif +#ifndef HAVE_USLEEP +extern int usleep(unsigned) FAST_FUNC; +#endif + #ifndef HAVE_VASPRINTF extern int vasprintf(char **string_ptr, const char *format, va_list p) FAST_FUNC; #endif diff --git a/libbb/platform.c b/libbb/platform.c index 19734517b..8d90ca4e9 100644 --- a/libbb/platform.c +++ b/libbb/platform.c @@ -17,6 +17,24 @@ char* FAST_FUNC strchrnul(const char *s, int c) } #endif +#ifndef HAVE_USLEEP +int FAST_FUNC usleep(unsigned usec) +{ + struct timespec ts; + ts.tv_sec = usec / 1000000u; + ts.tv_nsec = (usec % 1000000u) * 1000u; + /* + * If a signal has non-default handler, nanosleep returns early. + * Our version of usleep doesn't return early + * if interrupted by such signals: + * + */ + while (nanosleep(&ts, &ts) != 0) + continue; + return 0; +} +#endif + #ifndef HAVE_VASPRINTF int FAST_FUNC vasprintf(char **string_ptr, const char *format, va_list p) { -- cgit v1.2.3-55-g6feb From 3fa97af7ccc75264fb237f279f253eddf0ba4da1 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 15 Apr 2014 11:43:29 +0200 Subject: ash,hush: set $HOSTNAME is bash compat. Closes 7028 function old new delta hush_main 1056 1128 +72 ash_main 1442 1487 +45 Signed-off-by: Denys Vlasenko --- shell/ash.c | 6 ++++++ shell/hush.c | 11 ++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/shell/ash.c b/shell/ash.c index 7a097c814..cabeb40c5 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -41,6 +41,7 @@ #include #include #include +#include /* for setting $HOSTNAME */ #include "busybox.h" /* for applet_names */ #include "unicode.h" @@ -13018,6 +13019,11 @@ init(void) #if ENABLE_ASH_BASH_COMPAT p = lookupvar("SHLVL"); setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT); + if (!lookupvar("HOSTNAME")) { + struct utsname uts; + uname(&uts); + setvar2("HOSTNAME", uts.nodename); + } #endif p = lookupvar("PWD"); if (p) { diff --git a/shell/hush.c b/shell/hush.c index 7b0ea8b0c..e1d0ece29 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -91,6 +91,7 @@ #if ENABLE_HUSH_CASE # include #endif +#include /* for setting $HOSTNAME */ #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ #include "unicode.h" @@ -7786,6 +7787,14 @@ int hush_main(int argc, char **argv) /* Export PWD */ set_pwd_var(/*exp:*/ 1); + +#if ENABLE_HUSH_BASH_COMPAT + /* Set (but not export) HOSTNAME unless already set */ + if (!get_local_var_value("HOSTNAME")) { + struct utsname uts; + uname(&uts); + set_local_var_from_halves("HOSTNAME", uts.nodename); + } /* bash also exports SHLVL and _, * and sets (but doesn't export) the following variables: * BASH=/bin/bash @@ -7794,7 +7803,6 @@ int hush_main(int argc, char **argv) * HOSTTYPE=i386 * MACHTYPE=i386-pc-linux-gnu * OSTYPE=linux-gnu - * HOSTNAME= * PPID= - we also do it elsewhere * EUID= * UID= @@ -7822,6 +7830,7 @@ int hush_main(int argc, char **argv) * PS2='> ' * PS4='+ ' */ +#endif #if ENABLE_FEATURE_EDITING G.line_input_state = new_line_input_t(FOR_SHELL); -- cgit v1.2.3-55-g6feb From eb9f485b07b1823efbfddc773c899bd35dee62a6 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 16 Apr 2014 19:51:34 +0200 Subject: libbb/obscure.c: code shrink. Suggested by Tito. function old new delta string_checker 97 92 -5 Signed-off-by: Denys Vlasenko --- libbb/obscure.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbb/obscure.c b/libbb/obscure.c index 9ecc1f672..24c4ac917 100644 --- a/libbb/obscure.c +++ b/libbb/obscure.c @@ -76,7 +76,7 @@ static int string_checker(const char *p1, const char *p2) ret |= string_checker_helper(p, p2); /* clean up */ - memset(p, 0, size); + nuke_str(p); free(p); return ret; -- cgit v1.2.3-55-g6feb From e152cb818903f219a641c03b7c3ef290ed39a49d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 17 Apr 2014 11:29:34 +0200 Subject: adjtimex: make its help text and printed data a bit less cryptic Signed-off-by: Denys Vlasenko --- miscutils/adjtimex.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/miscutils/adjtimex.c b/miscutils/adjtimex.c index c8816e9e7..534364a69 100644 --- a/miscutils/adjtimex.c +++ b/miscutils/adjtimex.c @@ -14,12 +14,12 @@ //usage:#define adjtimex_trivial_usage //usage: "[-q] [-o OFF] [-f FREQ] [-p TCONST] [-t TICK]" //usage:#define adjtimex_full_usage "\n\n" -//usage: "Read and optionally set system timebase parameters. See adjtimex(2)\n" +//usage: "Read or set kernel time variables. See adjtimex(2)\n" //usage: "\n -q Quiet" //usage: "\n -o OFF Time offset, microseconds" //usage: "\n -f FREQ Frequency adjust, integer kernel units (65536 is 1ppm)" -//usage: "\n (positive values make clock run faster)" //usage: "\n -t TICK Microseconds per tick, usually 10000" +//usage: "\n (positive -t or -f values make clock run faster)" //usage: "\n -p TCONST" #include "libbb.h" @@ -111,13 +111,13 @@ int adjtimex_main(int argc UNUSED_PARAM, char **argv) } if (!(opt & OPT_quiet)) { - int sep; + const char *sep; const char *name; printf( " mode: %d\n" - "-o offset: %ld\n" - "-f frequency: %ld\n" + "-o offset: %ld us\n" + "-f freq.adjust: %ld (65536 = 1ppm)\n" " maxerror: %ld\n" " esterror: %ld\n" " status: %d (", @@ -125,15 +125,14 @@ int adjtimex_main(int argc UNUSED_PARAM, char **argv) txc.esterror, txc.status); /* representative output of next code fragment: - "PLL | PPSTIME" */ + * "PLL | PPSTIME" + */ name = statlist_name; - sep = 0; + sep = ""; for (i = 0; statlist_bit[i]; i++) { if (txc.status & statlist_bit[i]) { - if (sep) - fputs(" | ", stdout); - fputs(name, stdout); - sep = 1; + printf("%s%s", sep, name); + sep = " | "; } name += strlen(name) + 1; } @@ -143,9 +142,9 @@ int adjtimex_main(int argc UNUSED_PARAM, char **argv) descript = nth_string(ret_code_descript, ret); printf(")\n" "-p timeconstant: %ld\n" - " precision: %ld\n" + " precision: %ld us\n" " tolerance: %ld\n" - "-t tick: %ld\n" + "-t tick: %ld us\n" " time.tv_sec: %ld\n" " time.tv_usec: %ld\n" " return value: %d (%s)\n", -- cgit v1.2.3-55-g6feb From 5fd3ddfb243f8f3a8ef471ff8c323a76cf815574 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 19 Apr 2014 15:04:39 +0200 Subject: modprobe-small: fix help messages for aliased module-related applets Signed-off-by: Denys Vlasenko --- include/applets.src.h | 50 +++++++++++++++++++++++------------------------ modutils/insmod.c | 2 +- modutils/modprobe-small.c | 18 +++++++---------- modutils/modprobe.c | 2 +- scripts/trylink | 2 +- 5 files changed, 35 insertions(+), 39 deletions(-) diff --git a/include/applets.src.h b/include/applets.src.h index 7dbd4c7f3..aedcf22dc 100644 --- a/include/applets.src.h +++ b/include/applets.src.h @@ -8,7 +8,7 @@ /* name - applet name as it is typed on command line -name2 - applet name, converted to C (ether-wake: name2 = ether_wake) +help - applet name, converted to C (ether-wake: help = ether_wake) main - corresponding _main to call (bzcat: main = bunzip2) l - location to install link to: [/usr]/[s]bin s - suid type: @@ -24,46 +24,46 @@ s - suid type: #if defined(PROTOTYPES) # define APPLET(name,l,s) int name##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -# define APPLET_ODDNAME(name,main,l,s,name2) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -# define APPLET_NOEXEC(name,main,l,s,name2) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -# define APPLET_NOFORK(name,main,l,s,name2) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +# define APPLET_ODDNAME(name,main,l,s,help) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +# define APPLET_NOEXEC(name,main,l,s,help) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +# define APPLET_NOFORK(name,main,l,s,help) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -#elif defined(NAME_MAIN_CNAME) -# define APPLET(name,l,s) name name##_main name -# define APPLET_ODDNAME(name,main,l,s,name2) name main##_main name2 -# define APPLET_NOEXEC(name,main,l,s,name2) name main##_main name2 -# define APPLET_NOFORK(name,main,l,s,name2) name main##_main name2 +#elif defined(NAME_MAIN) +# define APPLET(name,l,s) name name##_main +# define APPLET_ODDNAME(name,main,l,s,help) name main##_main +# define APPLET_NOEXEC(name,main,l,s,help) name main##_main +# define APPLET_NOFORK(name,main,l,s,help) name main##_main #elif defined(MAKE_USAGE) && ENABLE_FEATURE_VERBOSE_USAGE # define APPLET(name,l,s) MAKE_USAGE(#name, name##_trivial_usage name##_full_usage) -# define APPLET_ODDNAME(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage name2##_full_usage) -# define APPLET_NOEXEC(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage name2##_full_usage) -# define APPLET_NOFORK(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage name2##_full_usage) +# define APPLET_ODDNAME(name,main,l,s,help) MAKE_USAGE(#name, help##_trivial_usage help##_full_usage) +# define APPLET_NOEXEC(name,main,l,s,help) MAKE_USAGE(#name, help##_trivial_usage help##_full_usage) +# define APPLET_NOFORK(name,main,l,s,help) MAKE_USAGE(#name, help##_trivial_usage help##_full_usage) #elif defined(MAKE_USAGE) && !ENABLE_FEATURE_VERBOSE_USAGE # define APPLET(name,l,s) MAKE_USAGE(#name, name##_trivial_usage) -# define APPLET_ODDNAME(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage) -# define APPLET_NOEXEC(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage) -# define APPLET_NOFORK(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage) +# define APPLET_ODDNAME(name,main,l,s,help) MAKE_USAGE(#name, help##_trivial_usage) +# define APPLET_NOEXEC(name,main,l,s,help) MAKE_USAGE(#name, help##_trivial_usage) +# define APPLET_NOFORK(name,main,l,s,help) MAKE_USAGE(#name, help##_trivial_usage) #elif defined(MAKE_LINKS) # define APPLET(name,l,c) LINK l name -# define APPLET_ODDNAME(name,main,l,s,name2) LINK l name -# define APPLET_NOEXEC(name,main,l,s,name2) LINK l name -# define APPLET_NOFORK(name,main,l,s,name2) LINK l name +# define APPLET_ODDNAME(name,main,l,s,help) LINK l name +# define APPLET_NOEXEC(name,main,l,s,help) LINK l name +# define APPLET_NOFORK(name,main,l,s,help) LINK l name #elif defined(MAKE_SUID) # define APPLET(name,l,s) SUID s l name -# define APPLET_ODDNAME(name,main,l,s,name2) SUID s l name -# define APPLET_NOEXEC(name,main,l,s,name2) SUID s l name -# define APPLET_NOFORK(name,main,l,s,name2) SUID s l name +# define APPLET_ODDNAME(name,main,l,s,help) SUID s l name +# define APPLET_NOEXEC(name,main,l,s,help) SUID s l name +# define APPLET_NOFORK(name,main,l,s,help) SUID s l name #else static struct bb_applet applets[] = { /* name, main, location, need_suid */ # define APPLET(name,l,s) { #name, #name, l, s }, -# define APPLET_ODDNAME(name,main,l,s,name2) { #name, #main, l, s }, -# define APPLET_NOEXEC(name,main,l,s,name2) { #name, #main, l, s, 1 }, -# define APPLET_NOFORK(name,main,l,s,name2) { #name, #main, l, s, 1, 1 }, +# define APPLET_ODDNAME(name,main,l,s,help) { #name, #main, l, s }, +# define APPLET_NOEXEC(name,main,l,s,help) { #name, #main, l, s, 1 }, +# define APPLET_NOFORK(name,main,l,s,help) { #name, #main, l, s, 1, 1 }, #endif #if ENABLE_INSTALL_NO_USR @@ -391,7 +391,7 @@ IF_WHOAMI(APPLET_NOFORK(whoami, whoami, BB_DIR_USR_BIN, BB_SUID_DROP, whoami)) IF_YES(APPLET_NOFORK(yes, yes, BB_DIR_USR_BIN, BB_SUID_DROP, yes)) IF_ZCIP(APPLET(zcip, BB_DIR_SBIN, BB_SUID_DROP)) -#if !defined(PROTOTYPES) && !defined(NAME_MAIN_CNAME) && !defined(MAKE_USAGE) \ +#if !defined(PROTOTYPES) && !defined(NAME_MAIN) && !defined(MAKE_USAGE) \ && !defined(MAKE_LINKS) && !defined(MAKE_SUID) }; #endif diff --git a/modutils/insmod.c b/modutils/insmod.c index 887d9f2a3..9c3c992a5 100644 --- a/modutils/insmod.c +++ b/modutils/insmod.c @@ -21,7 +21,7 @@ //usage: IF_NOT_FEATURE_2_4_MODULES("FILE ") //usage: "[SYMBOL=VALUE]..." //usage:#define insmod_full_usage "\n\n" -//usage: "Load the specified kernel modules into the kernel" +//usage: "Load kernel module" //usage: IF_FEATURE_2_4_MODULES( "\n" //usage: "\n -f Force module to load into the wrong kernel version" //usage: "\n -k Make module autoclean-able" diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c index 5b7836344..023755db3 100644 --- a/modutils/modprobe-small.c +++ b/modutils/modprobe-small.c @@ -9,10 +9,10 @@ */ //applet:IF_MODPROBE_SMALL(APPLET(modprobe, BB_DIR_SBIN, BB_SUID_DROP)) -//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(depmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe)) -//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(insmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe)) -//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(lsmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe)) -//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(rmmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe)) +//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(depmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, depmod)) +//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(insmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, insmod)) +//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(lsmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, lsmod)) +//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(rmmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, rmmod)) #include "libbb.h" /* After libbb.h, since it needs sys/types.h on some systems */ @@ -583,7 +583,7 @@ static void process_module(char *name, const char *cmdline_options) // for the given alias. For example, // "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches // ata_piix because it has an alias "pci:v00008086d00007010sv*sd*bc*sc*i*" -// and ata_generic, it has an alias "alias=pci:v*d*sv*sd*bc01sc01i*" +// and ata_generic, it has an alias "pci:v*d*sv*sd*bc01sc01i*" // Standard modprobe would load them both. // In this code, find_alias() returns only the first matching module. @@ -703,10 +703,6 @@ The following options are useful for people managing distributions: //usage:#if ENABLE_MODPROBE_SMALL -//// Note: currently, help system shows modprobe --help text for all aliased cmds -//// (see APPLET_ODDNAME macro definition). -//// All other help texts defined below are not used. FIXME? - //usage:#define depmod_trivial_usage NOUSAGE_STR //usage:#define depmod_full_usage "" @@ -720,7 +716,7 @@ The following options are useful for people managing distributions: //usage: IF_NOT_FEATURE_2_4_MODULES("FILE ") //usage: "[SYMBOL=VALUE]..." //usage:#define insmod_full_usage "\n\n" -//usage: "Load the specified kernel modules into the kernel" +//usage: "Load kernel module" //usage: IF_FEATURE_2_4_MODULES( "\n" //usage: "\n -f Force module to load into the wrong kernel version" //usage: "\n -k Make module autoclean-able" @@ -745,7 +741,7 @@ The following options are useful for people managing distributions: //usage: "$ rmmod tulip\n" //usage:#define modprobe_trivial_usage -//usage: "[-qfwrsv] MODULE [symbol=value]..." +//usage: "[-qfwrsv] MODULE [SYMBOL=VALUE]..." //usage:#define modprobe_full_usage "\n\n" //usage: " -r Remove MODULE (stacks) or do autoclean" //usage: "\n -q Quiet" diff --git a/modutils/modprobe.c b/modutils/modprobe.c index 7f7446d8e..f08f0850d 100644 --- a/modutils/modprobe.c +++ b/modutils/modprobe.c @@ -87,7 +87,7 @@ //usage: //usage:#define modprobe_trivial_usage //usage: "[-alrqvsD" IF_FEATURE_MODPROBE_BLACKLIST("b") "]" -//usage: " MODULE [symbol=value]..." +//usage: " MODULE [SYMBOL=VALUE]..." //usage:#define modprobe_full_usage "\n\n" //usage: " -a Load multiple MODULEs" //usage: "\n -l List (MODULE is a pattern)" diff --git a/scripts/trylink b/scripts/trylink index e47169917..85095235c 100755 --- a/scripts/trylink +++ b/scripts/trylink @@ -268,7 +268,7 @@ fi if test "$CONFIG_FEATURE_INDIVIDUAL" = y; then echo "Linking individual applets against libbusybox (see $sharedlib_dir/*)" - gcc -DNAME_MAIN_CNAME -E -include include/autoconf.h include/applets.h \ + gcc -DNAME_MAIN -E -include include/autoconf.h include/applets.h \ | grep -v "^#" \ | grep -v "^$" \ > applet_lst.tmp -- cgit v1.2.3-55-g6feb From 6116cb23cc2074f232397b406fabab1606b28fb6 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 19 Apr 2014 16:17:27 +0200 Subject: modprobe-small: remove redundant aliases from modules.dep.bb function old new delta parse_module 309 395 +86 Signed-off-by: Denys Vlasenko --- modutils/modprobe-small.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c index 023755db3..223eba915 100644 --- a/modutils/modprobe-small.c +++ b/modutils/modprobe-small.c @@ -212,6 +212,7 @@ static void parse_module(module_info *info, const char *pathname) reset_stringbuf(); pos = 0; while (1) { + unsigned start = stringbuf_idx; ptr = find_keyword(module_image + pos, len - pos, "alias="); if (!ptr) { ptr = find_keyword(module_image + pos, len - pos, "__ksymtab_"); @@ -228,6 +229,31 @@ static void parse_module(module_info *info, const char *pathname) } append(ptr); appendc(' '); + /* + * Don't add redundant aliases, such as: + * libcrc32c.ko symbol:crc32c symbol:crc32c + */ + if (start) { /* "if we aren't the first alias" */ + char *found, *last; + stringbuf[stringbuf_idx] = '\0'; + last = stringbuf + start; + /* + * String at last-1 is " symbol:crc32c " + * (with both leading and trailing spaces). + */ + if (strncmp(stringbuf, last, stringbuf_idx - start) == 0) + /* First alias matches us */ + found = stringbuf; + else + /* Does any other alias match? */ + found = strstr(stringbuf, last-1); + if (found < last-1) { + /* There is absolutely the same string before us */ + dbg2_error_msg("redundant:'%s'", last); + stringbuf_idx = start; + goto skip; + } + } skip: pos = (ptr - module_image); } -- cgit v1.2.3-55-g6feb From d531f93f646f4347b464730b80dfb34b8744a984 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 19 Apr 2014 19:00:16 +0200 Subject: ntpd: truly ignore high delay packet Before this cahnge, sometimes they were used after the next packet from another peer was received, because we did updare some peer stats from high delay packet before dropping it. function old new delta recv_and_process_peer_pkt 922 966 +44 Signed-off-by: Denys Vlasenko --- networking/ntpd.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/networking/ntpd.c b/networking/ntpd.c index adda6e5b0..2eec99f99 100644 --- a/networking/ntpd.c +++ b/networking/ntpd.c @@ -252,6 +252,9 @@ typedef struct { * or when receive times out (if p_fd >= 0): */ double next_action_time; double p_xmttime; + double p_raw_delay; + /* p_raw_delay is set even by "high delay" packets */ + /* lastpkt_delay isn't */ double lastpkt_recv_time; double lastpkt_delay; double lastpkt_rootdelay; @@ -1685,7 +1688,8 @@ recv_and_process_peer_pkt(peer_t *p) ssize_t size; msg_t msg; double T1, T2, T3, T4; - double dv, offset; + double offset; + double prev_delay, delay; unsigned interval; datapoint_t *datapoint; peer_t *q; @@ -1745,12 +1749,6 @@ recv_and_process_peer_pkt(peer_t *p) // if (msg.m_rootdelay / 2 + msg.m_rootdisp >= MAXDISP || p->lastpkt_reftime > msg.m_xmt) // return; /* invalid header values */ - p->lastpkt_status = msg.m_status; - p->lastpkt_stratum = msg.m_stratum; - p->lastpkt_rootdelay = sfp_to_d(msg.m_rootdelay); - p->lastpkt_rootdisp = sfp_to_d(msg.m_rootdisp); - p->lastpkt_refid = msg.m_refid; - /* * From RFC 2030 (with a correction to the delay math): * @@ -1770,28 +1768,35 @@ recv_and_process_peer_pkt(peer_t *p) T3 = lfp_to_d(msg.m_xmttime); T4 = G.cur_time; - p->lastpkt_recv_time = T4; - VERB6 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time); - /* The delay calculation is a special case. In cases where the * server and client clocks are running at different rates and * with very fast networks, the delay can appear negative. In * order to avoid violating the Principle of Least Astonishment, * the delay is clamped not less than the system precision. */ - dv = p->lastpkt_delay; - p->lastpkt_delay = (T4 - T1) - (T3 - T2); - if (p->lastpkt_delay < G_precision_sec) - p->lastpkt_delay = G_precision_sec; + delay = (T4 - T1) - (T3 - T2); + if (delay < G_precision_sec) + delay = G_precision_sec; /* * If this packet's delay is much bigger than the last one, * it's better to just ignore it than use its much less precise value. */ - if (p->reachable_bits && p->lastpkt_delay > dv * BAD_DELAY_GROWTH) { + prev_delay = p->p_raw_delay; + p->p_raw_delay = delay; + if (p->reachable_bits && delay > prev_delay * BAD_DELAY_GROWTH) { bb_error_msg("reply from %s: delay %f is too high, ignoring", p->p_dotted, p->lastpkt_delay); goto pick_normal_interval; } + p->lastpkt_delay = delay; + p->lastpkt_recv_time = T4; + VERB6 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time); + p->lastpkt_status = msg.m_status; + p->lastpkt_stratum = msg.m_stratum; + p->lastpkt_rootdelay = sfp_to_d(msg.m_rootdelay); + p->lastpkt_rootdisp = sfp_to_d(msg.m_rootdisp); + p->lastpkt_refid = msg.m_refid; + p->datapoint_idx = p->reachable_bits ? (p->datapoint_idx + 1) % NUM_DATAPOINTS : 0; datapoint = &p->filter_datapoint[p->datapoint_idx]; datapoint->d_recv_time = T4; -- cgit v1.2.3-55-g6feb From 5a21c8550ef9ed9cfcc4abed7d59e58017a306e5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 20 Apr 2014 13:04:23 +0200 Subject: ntpd: fix wrong delay value in one of the printed messages Signed-off-by: Denys Vlasenko --- networking/ntpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/networking/ntpd.c b/networking/ntpd.c index 2eec99f99..59607ed23 100644 --- a/networking/ntpd.c +++ b/networking/ntpd.c @@ -1784,7 +1784,7 @@ recv_and_process_peer_pkt(peer_t *p) prev_delay = p->p_raw_delay; p->p_raw_delay = delay; if (p->reachable_bits && delay > prev_delay * BAD_DELAY_GROWTH) { - bb_error_msg("reply from %s: delay %f is too high, ignoring", p->p_dotted, p->lastpkt_delay); + bb_error_msg("reply from %s: delay %f is too high, ignoring", p->p_dotted, delay); goto pick_normal_interval; } -- cgit v1.2.3-55-g6feb From 07e5555a8f7469f6f45cacd7fc188816ae644f74 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 21 Apr 2014 16:59:36 +0200 Subject: modprobe-small: (un)load all modules which match the alias, not only first one Closes 627 and 7034. Commonly seen case is (un)loading of an alias which matches ata_generic and a more specific ata module. For example: modprobe [-r] pci:v00008086d00007010sv00000000sd00000000bc01sc01i80 (ata_generic and pata_acpi) modprobe [-r] pci:v00001106d00000571sv00001509sd00009022bc01sc01i8a (ata_generic and pata_via) function old new delta process_module 615 728 +113 parse_module 309 395 +86 find_alias 621 653 +32 pathname_matches_modname 78 79 +1 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 4/0 up/down: 232/0) Total: 232 bytes Signed-off-by: Denys Vlasenko --- modutils/modprobe-small.c | 162 +++++++++++++++++++++++++++++----------------- 1 file changed, 101 insertions(+), 61 deletions(-) diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c index 223eba915..91e0c1380 100644 --- a/modutils/modprobe-small.c +++ b/modutils/modprobe-small.c @@ -22,6 +22,9 @@ extern int init_module(void *module, unsigned long len, const char *options); extern int delete_module(const char *module, unsigned flags); extern int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret); +/* linux/include/linux/module.h has limit of 64 chars on module names */ +#undef MODULE_NAME_LEN +#define MODULE_NAME_LEN 64 #if 1 @@ -143,6 +146,19 @@ static void replace(char *s, char what, char with) } } +static char *filename2modname(const char *filename, char *modname) +{ + int i; + char *from; + + from = bb_get_last_path_component_nostrip(filename); + for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++) + modname[i] = (from[i] == '-') ? '_' : from[i]; + modname[i] = '\0'; + + return modname; +} + /* Take "word word", return malloced "word",NUL,"word",NUL,NUL */ static char* str_2_list(const char *str) { @@ -277,14 +293,13 @@ static void parse_module(module_info *info, const char *pathname) static int pathname_matches_modname(const char *pathname, const char *modname) { + int r; + char name[MODULE_NAME_LEN]; const char *fname = bb_get_last_path_component_nostrip(pathname); const char *suffix = strrstr(fname, ".ko"); -//TODO: can do without malloc? - char *name = xstrndup(fname, suffix - fname); - int r; + safe_strncpy(name, fname, suffix - fname); replace(name, '-', '_'); r = (strcmp(name, modname) == 0); - free(name); return r; } @@ -447,11 +462,12 @@ static void write_out_dep_bb(int fd) } } -static module_info* find_alias(const char *alias) +static module_info** find_alias(const char *alias) { int i; int dep_bb_fd; - module_info *result; + int infoidx; + module_info **infovec; dbg1_error_msg("find_alias('%s')", alias); try_again: @@ -464,7 +480,9 @@ static module_info* find_alias(const char *alias) if (!modinfo[i].aliases) { parse_module(&modinfo[i], modinfo[i].pathname); } - return &modinfo[i]; + infovec = xzalloc(2 * sizeof(infovec[0])); + infovec[0] = &modinfo[i]; + return infovec; } i++; } @@ -477,16 +495,13 @@ static module_info* find_alias(const char *alias) /* Scan all module bodies, extract modinfo (it contains aliases) */ i = 0; - result = NULL; + infoidx = 0; + infovec = NULL; while (modinfo[i].pathname) { char *desc, *s; if (!modinfo[i].aliases) { parse_module(&modinfo[i], modinfo[i].pathname); } - if (result) { - i++; - continue; - } /* "alias1 symbol:sym1 alias2 symbol:sym2" */ desc = str_2_list(modinfo[i].aliases); /* Does matching substring exist? */ @@ -498,13 +513,12 @@ static module_info* find_alias(const char *alias) if (fnmatch(s, alias, 0) == 0) { dbg1_error_msg("found alias '%s' in module '%s'", alias, modinfo[i].pathname); - result = &modinfo[i]; + infovec = xrealloc_vector(infovec, 1, infoidx); + infovec[infoidx++] = &modinfo[i]; break; } } free(desc); - if (result && dep_bb_fd < 0) - return result; i++; } @@ -513,8 +527,8 @@ static module_info* find_alias(const char *alias) write_out_dep_bb(dep_bb_fd); } - dbg1_error_msg("find_alias '%s' returns %p", alias, result); - return result; + dbg1_error_msg("find_alias '%s' returns %d results", alias, infoidx); + return infovec; } #if ENABLE_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED @@ -550,14 +564,23 @@ static int already_loaded(const char *name) static void process_module(char *name, const char *cmdline_options) { char *s, *deps, *options; + module_info **infovec; module_info *info; + int infoidx; int is_rmmod = (option_mask32 & OPT_r) != 0; + dbg1_error_msg("process_module('%s','%s')", name, cmdline_options); replace(name, '-', '_'); dbg1_error_msg("already_loaded:%d is_rmmod:%d", already_loaded(name), is_rmmod); - if (already_loaded(name) != is_rmmod) { + /* + * We used to have "is_rmmod != already_loaded(name)" check here, but + * modprobe -r pci:v00008086d00007010sv00000000sd00000000bc01sc01i80 + * won't unload modules (there are more than one) + * which have this alias. + */ + if (!is_rmmod && already_loaded(name)) { dbg1_error_msg("nothing to do for '%s'", name); return; } @@ -586,39 +609,51 @@ static void process_module(char *name, const char *cmdline_options) if (!module_count) { /* Scan module directory. This is done only once. * It will attempt module load, and will exit(EXIT_SUCCESS) - * on success. */ + * on success. + */ module_found_idx = -1; recursive_action(".", ACTION_RECURSE, /* flags */ fileAction, /* file action */ NULL, /* dir action */ name, /* user data */ - 0); /* depth */ + 0 /* depth */ + ); dbg1_error_msg("dirscan complete"); /* Module was not found, or load failed, or is_rmmod */ if (module_found_idx >= 0) { /* module was found */ - info = &modinfo[module_found_idx]; + infovec = xzalloc(2 * sizeof(infovec[0])); + infovec[0] = &modinfo[module_found_idx]; } else { /* search for alias, not a plain module name */ - info = find_alias(name); + infovec = find_alias(name); } } else { - info = find_alias(name); + infovec = find_alias(name); } -// Problem here: there can be more than one module -// for the given alias. For example, -// "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches -// ata_piix because it has an alias "pci:v00008086d00007010sv*sd*bc*sc*i*" -// and ata_generic, it has an alias "pci:v*d*sv*sd*bc01sc01i*" -// Standard modprobe would load them both. -// In this code, find_alias() returns only the first matching module. + /* There can be more than one module for the given alias. For example, + * "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches + * ata_piix because it has alias "pci:v00008086d00007010sv*sd*bc*sc*i*" + * and ata_generic, it has alias "pci:v*d*sv*sd*bc01sc01i*" + * Standard modprobe loads them both. We achieve it by returning + * a *list* of modinfo pointers from find_alias(). + */ - /* rmmod? unload it by name */ + /* rmmod or modprobe -r? unload module(s) */ if (is_rmmod) { - if (delete_module(name, O_NONBLOCK | O_EXCL) != 0) { - if (!(option_mask32 & OPT_q)) - bb_perror_msg("remove '%s'", name); - goto ret; + infoidx = 0; + while ((info = infovec[infoidx++]) != NULL) { + int r; + char modname[MODULE_NAME_LEN]; + + filename2modname(info->pathname, modname); + r = delete_module(modname, O_NONBLOCK | O_EXCL); + dbg1_error_msg("delete_module('%s', O_NONBLOCK | O_EXCL):%d", modname, r); + if (r != 0) { + if (!(option_mask32 & OPT_q)) + bb_perror_msg("remove '%s'", modname); + goto ret; + } } if (applet_name[0] == 'r') { @@ -634,7 +669,7 @@ static void process_module(char *name, const char *cmdline_options) */ } - if (!info) { + if (!infovec) { /* both dirscan and find_alias found nothing */ if (!is_rmmod && applet_name[0] != 'd') /* it wasn't rmmod or depmod */ bb_error_msg("module '%s' not found", name); @@ -642,36 +677,41 @@ static void process_module(char *name, const char *cmdline_options) goto ret; } - /* Iterate thru dependencies, trying to (un)load them */ - deps = str_2_list(info->deps); - for (s = deps; *s; s += strlen(s) + 1) { - //if (strcmp(name, s) != 0) // N.B. do loops exist? - dbg1_error_msg("recurse on dep '%s'", s); - process_module(s, NULL); - dbg1_error_msg("recurse on dep '%s' done", s); - } - free(deps); + infoidx = 0; + while ((info = infovec[infoidx++]) != NULL) { + /* Iterate thru dependencies, trying to (un)load them */ + deps = str_2_list(info->deps); + for (s = deps; *s; s += strlen(s) + 1) { + //if (strcmp(name, s) != 0) // N.B. do loops exist? + dbg1_error_msg("recurse on dep '%s'", s); + process_module(s, NULL); + dbg1_error_msg("recurse on dep '%s' done", s); + } + free(deps); - /* modprobe -> load it */ - if (!is_rmmod) { - if (!options || strstr(options, "blacklist") == NULL) { - errno = 0; - if (load_module(info->pathname, options) != 0) { - if (EEXIST != errno) { - bb_error_msg("'%s': %s", - info->pathname, - moderror(errno)); - } else { - dbg1_error_msg("'%s': %s", - info->pathname, - moderror(errno)); - } - } - } else { + if (is_rmmod) + continue; + + /* We are modprobe: load it */ + if (options && strstr(options, "blacklist")) { dbg1_error_msg("'%s': blacklisted", info->pathname); + continue; + } + errno = 0; + if (load_module(info->pathname, options) != 0) { + if (EEXIST != errno) { + bb_error_msg("'%s': %s", + info->pathname, + moderror(errno)); + } else { + dbg1_error_msg("'%s': %s", + info->pathname, + moderror(errno)); + } } } ret: + free(infovec); free(options); //TODO: return load attempt result from process_module. //If dep didn't load ok, continuing makes little sense. -- cgit v1.2.3-55-g6feb From 01a1a967c99d6ea75387c6ca8041e9061ea4a111 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 30 Apr 2014 14:47:28 +0200 Subject: crond: tweak help text, make course less cryptic function old new delta packed_usage 29736 29740 +4 crond_main 1485 1486 +1 load_crontab 712 706 -6 Signed-off-by: Denys Vlasenko --- include/applets.src.h | 1 - miscutils/Config.src | 34 ------------------- miscutils/Kbuild.src | 1 - miscutils/crond.c | 94 +++++++++++++++++++++++++++++++++++++-------------- 4 files changed, 68 insertions(+), 62 deletions(-) diff --git a/include/applets.src.h b/include/applets.src.h index aedcf22dc..cb36628b5 100644 --- a/include/applets.src.h +++ b/include/applets.src.h @@ -104,7 +104,6 @@ IF_CKSUM(APPLET_NOEXEC(cksum, cksum, BB_DIR_USR_BIN, BB_SUID_DROP, cksum)) IF_CLEAR(APPLET(clear, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_COMM(APPLET(comm, BB_DIR_USR_BIN, BB_SUID_DROP)) IF_CP(APPLET_NOEXEC(cp, cp, BB_DIR_BIN, BB_SUID_DROP, cp)) -IF_CROND(APPLET(crond, BB_DIR_USR_SBIN, BB_SUID_DROP)) /* Needs to be run by root or be suid root - needs to change /var/spool/cron* files: */ IF_CRONTAB(APPLET(crontab, BB_DIR_USR_BIN, BB_SUID_REQUIRE)) IF_CRYPTPW(APPLET(cryptpw, BB_DIR_USR_BIN, BB_SUID_DROP)) diff --git a/miscutils/Config.src b/miscutils/Config.src index 1da9800bd..1b2a3ae9a 100644 --- a/miscutils/Config.src +++ b/miscutils/Config.src @@ -133,40 +133,6 @@ config CHRT manipulate real-time attributes of a process. This requires sched_{g,s}etparam support in your libc. -config CROND - bool "crond" - default y - select FEATURE_SYSLOG - help - Crond is a background daemon that parses individual crontab - files and executes commands on behalf of the users in question. - This is a port of dcron from slackware. It uses files of the - format /var/spool/cron/crontabs/ files, for example: - $ cat /var/spool/cron/crontabs/root - # Run daily cron jobs at 4:40 every day: - 40 4 * * * /etc/cron/daily > /dev/null 2>&1 - -config FEATURE_CROND_D - bool "Support option -d to redirect output to stderr" - depends on CROND - default y - help - -d sets loglevel to 0 (most verbose) and directs all output to stderr. - -config FEATURE_CROND_CALL_SENDMAIL - bool "Report command output via email (using sendmail)" - default y - depends on CROND - help - Command output will be sent to corresponding user via email. - -config FEATURE_CROND_DIR - string "crond spool directory" - default "/var/spool/cron" - depends on CROND || CRONTAB - help - Location of crond spool. - config CRONTAB bool "crontab" default y diff --git a/miscutils/Kbuild.src b/miscutils/Kbuild.src index 9e164f16e..8eaa82de9 100644 --- a/miscutils/Kbuild.src +++ b/miscutils/Kbuild.src @@ -12,7 +12,6 @@ lib-$(CONFIG_BBCONFIG) += bbconfig.o lib-$(CONFIG_BEEP) += beep.o lib-$(CONFIG_CHAT) += chat.o lib-$(CONFIG_CHRT) += chrt.o -lib-$(CONFIG_CROND) += crond.o lib-$(CONFIG_CRONTAB) += crontab.o lib-$(CONFIG_DC) += dc.o lib-$(CONFIG_DEVFSD) += devfsd.o diff --git a/miscutils/crond.c b/miscutils/crond.c index 582dc991a..995ed9e0a 100644 --- a/miscutils/crond.c +++ b/miscutils/crond.c @@ -1,7 +1,5 @@ /* vi: set sw=4 ts=4: */ /* - * crond -d[#] -c -f -b - * * run as root, but NOT setuid root * * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com) @@ -10,6 +8,43 @@ * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ +//config:config CROND +//config: bool "crond" +//config: default y +//config: select FEATURE_SYSLOG +//config: help +//config: Crond is a background daemon that parses individual crontab +//config: files and executes commands on behalf of the users in question. +//config: This is a port of dcron from slackware. It uses files of the +//config: format /var/spool/cron/crontabs/ files, for example: +//config: $ cat /var/spool/cron/crontabs/root +//config: # Run daily cron jobs at 4:40 every day: +//config: 40 4 * * * /etc/cron/daily > /dev/null 2>&1 +//config: +//config:config FEATURE_CROND_D +//config: bool "Support option -d to redirect output to stderr" +//config: depends on CROND +//config: default y +//config: help +//config: -d N sets loglevel (0:most verbose) and directs all output to stderr. +//config: +//config:config FEATURE_CROND_CALL_SENDMAIL +//config: bool "Report command output via email (using sendmail)" +//config: default y +//config: depends on CROND +//config: help +//config: Command output will be sent to corresponding user via email. +//config: +//config:config FEATURE_CROND_DIR +//config: string "crond spool directory" +//config: default "/var/spool/cron" +//config: depends on CROND || CRONTAB +//config: help +//config: Location of crond spool. + +//applet:IF_CROND(APPLET(crond, BB_DIR_USR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_CROND) += crond.o //usage:#define crond_trivial_usage //usage: "-fbS -l N " IF_FEATURE_CROND_D("-d N ") "-L LOGFILE -c DIR" @@ -17,12 +52,12 @@ //usage: " -f Foreground" //usage: "\n -b Background (default)" //usage: "\n -S Log to syslog (default)" -//usage: "\n -l Set log level. 0 is the most verbose, default 8" +//usage: "\n -l N Set log level. Most verbose:0, default:8" //usage: IF_FEATURE_CROND_D( -//usage: "\n -d Set log level, log to stderr" +//usage: "\n -d N Set log level, log to stderr" //usage: ) -//usage: "\n -L Log to file" -//usage: "\n -c Working dir" +//usage: "\n -L FILE Log to FILE" +//usage: "\n -c DIR Cron dir. Default:"CONFIG_FEATURE_CROND_DIR"/crontabs" #include "libbb.h" #include @@ -36,7 +71,7 @@ #endif -#define TMPDIR CONFIG_FEATURE_CROND_DIR +#define CRON_DIR CONFIG_FEATURE_CROND_DIR #define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs" #ifndef SENDMAIL # define SENDMAIL "sendmail" @@ -158,12 +193,10 @@ static void crondlog(const char *ctl, ...) static const char DowAry[] ALIGN1 = "sun""mon""tue""wed""thu""fri""sat" - /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */ ; static const char MonAry[] ALIGN1 = "jan""feb""mar""apr""may""jun""jul""aug""sep""oct""nov""dec" - /* "Jan""Feb""Mar""Apr""May""Jun""Jul""Aug""Sep""Oct""Nov""Dec" */ ; static void ParseField(char *user, char *ary, int modvalue, int off, @@ -393,8 +426,11 @@ static void load_crontab(const char *fileName) while (1) { CronLine *line; - if (!--maxLines) + if (!--maxLines) { + crondlog(WARN9 "user %s: too many lines", fileName); break; + } + n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY); if (!n) break; @@ -410,6 +446,18 @@ static void load_crontab(const char *fileName) #endif /* otherwise just ignore such lines */ continue; } +//TODO: handle SHELL=, HOME= too? "man crontab" says: +//name = value +// +//where the spaces around the equal-sign (=) are optional, and any subsequent +//non-leading spaces in value will be part of the value assigned to name. +//The value string may be placed in quotes (single or double, but matching) +//to preserve leading or trailing blanks. +// +//Several environment variables are set up automatically by the cron(8) daemon. +//SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd +//line of the crontab's owner. HOME and SHELL may be overridden by settings +//in the crontab; LOGNAME may not. /* check if a minimum of tokens is specified */ if (n < 6) continue; @@ -441,10 +489,6 @@ static void load_crontab(const char *fileName) file->cf_next = G.cron_files; G.cron_files = file; - - if (maxLines == 0) { - crondlog(WARN9 "user %s: too many lines", fileName); - } } config_close(parser); } @@ -540,8 +584,8 @@ static void change_user(struct passwd *pas) change_identity(pas); /* - initgroups, setgid, setuid */ if (chdir(pas->pw_dir) < 0) { crondlog(WARN9 "chdir(%s)", pas->pw_dir); - if (chdir(TMPDIR) < 0) { - crondlog(DIE9 "chdir(%s)", TMPDIR); /* exits */ + if (chdir(CRON_DIR) < 0) { + crondlog(DIE9 "chdir(%s)", CRON_DIR); /* exits */ } } } @@ -568,7 +612,7 @@ fork_job(const char *user, int mailFd, pid = vfork(); if (pid == 0) { /* CHILD */ - /* initgroups, setgid, setuid, and chdir to home or TMPDIR */ + /* initgroups, setgid, setuid, and chdir to home or CRON_DIR */ change_user(pas); if (DebugOpt) { crondlog(LVL5 "child running %s", prog); @@ -588,7 +632,6 @@ fork_job(const char *user, int mailFd, } if (pid < 0) { - /* FORK FAILED */ crondlog(ERR20 "can't vfork"); err: pid = 0; @@ -614,7 +657,7 @@ static void start_one_job(const char *user, CronLine *line) if (line->cl_mailto) { /* Open mail file (owner is root so nobody can screw with it) */ - snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, getpid()); + snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", CRON_DIR, user, getpid()); mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600); if (mailFd >= 0) { @@ -633,7 +676,7 @@ static void start_one_job(const char *user, CronLine *line) unlink(mailFile); } else { /* rename mail-file based on pid of process */ - char *mailFile2 = xasprintf("%s/cron.%s.%d", TMPDIR, user, (int)line->cl_pid); + char *mailFile2 = xasprintf("%s/cron.%s.%d", CRON_DIR, user, (int)line->cl_pid); rename(mailFile, mailFile2); // TODO: xrename? free(mailFile2); } @@ -665,7 +708,7 @@ static void process_finished_job(const char *user, CronLine *line) * End of primary job - check for mail file. * If size has changed and the file is still valid, we send it. */ - snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, (int)pid); + snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", CRON_DIR, user, (int)pid); mailFd = open(mailFile, O_RDONLY); unlink(mailFile); if (mailFd < 0) { @@ -706,7 +749,7 @@ static void start_one_job(const char *user, CronLine *line) pid = vfork(); if (pid == 0) { /* CHILD */ - /* initgroups, setgid, setuid, and chdir to home or TMPDIR */ + /* initgroups, setgid, setuid, and chdir to home or CRON_DIR */ change_user(pas); if (DebugOpt) { crondlog(LVL5 "child running %s", DEFAULT_SHELL); @@ -853,8 +896,8 @@ int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int crond_main(int argc UNUSED_PARAM, char **argv) { time_t t2; - int rescan; - int sleep_time; + unsigned rescan; + unsigned sleep_time; unsigned opts; INIT_G(); @@ -940,11 +983,10 @@ int crond_main(int argc UNUSED_PARAM, char **argv) /* Usual case: time advances forward, as expected */ flag_starting_jobs(t1, t2); start_jobs(); + sleep_time = 60; if (check_completions() > 0) { /* some jobs are still running */ sleep_time = 10; - } else { - sleep_time = 60; } } /* else: time jumped back, do not run any jobs */ -- cgit v1.2.3-55-g6feb From 3dfe0ae5a8c4f3a75f61df157db157ac0e044a51 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 30 Apr 2014 14:48:28 +0200 Subject: libbb: make syslog level for bb_error_msg's configurable. use it in crond function old new delta bb_verror_msg 380 386 +6 syslog_level - 1 +1 crondlog 165 108 -57 Signed-off-by: Denys Vlasenko --- include/libbb.h | 1 + libbb/verror_msg.c | 3 ++- miscutils/crond.c | 23 ++++++++--------------- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/include/libbb.h b/include/libbb.h index 1cbe2c8b4..29cf6bc6d 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -1074,6 +1074,7 @@ enum { LOGMODE_BOTH = LOGMODE_SYSLOG + LOGMODE_STDIO, }; extern const char *msg_eol; +extern smallint syslog_level; extern smallint logmode; extern int die_sleep; extern uint8_t xfunc_error_retval; diff --git a/libbb/verror_msg.c b/libbb/verror_msg.c index ee95be3e3..87efb56b3 100644 --- a/libbb/verror_msg.c +++ b/libbb/verror_msg.c @@ -11,6 +11,7 @@ # include #endif +smallint syslog_level = LOG_ERR; smallint logmode = LOGMODE_STDIO; const char *msg_eol = "\n"; @@ -70,7 +71,7 @@ void FAST_FUNC bb_verror_msg(const char *s, va_list p, const char* strerr) } #if ENABLE_FEATURE_SYSLOG if (logmode & LOGMODE_SYSLOG) { - syslog(LOG_ERR, "%s", msg + applet_len); + syslog(syslog_level, "%s", msg + applet_len); } #endif free(msg); diff --git a/miscutils/crond.c b/miscutils/crond.c index 995ed9e0a..8441b6cc5 100644 --- a/miscutils/crond.c +++ b/miscutils/crond.c @@ -163,28 +163,21 @@ static void crondlog(const char *ctl, ...) __attribute__ ((format (printf, 1, 2) static void crondlog(const char *ctl, ...) { va_list va; - int level = (ctl[0] & 0x1f); + unsigned level = (ctl[0] & 0x1f); va_start(va, ctl); - if (level >= (int)G.log_level) { - /* Debug mode: all to (non-redirected) stderr, */ - /* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */ - if (!DebugOpt && G.log_filename) { - /* Otherwise (log to file): we reopen log file at every write: */ + if (level >= G.log_level) { + if (G.log_filename) { + /* If log to file, reopen log file at every write: */ int logfd = open_or_warn(G.log_filename, O_WRONLY | O_CREAT | O_APPEND); if (logfd >= 0) xmove_fd(logfd, STDERR_FILENO); } /* When we log to syslog, level > 8 is logged at LOG_ERR - * syslog level, level <= 8 is logged at LOG_INFO. */ - if (level > 8) { - bb_verror_msg(ctl + 1, va, /* strerr: */ NULL); - } else { - char *msg = NULL; - vasprintf(&msg, ctl + 1, va); - bb_info_msg("%s: %s", applet_name, msg); - free(msg); - } + * syslog level, level <= 8 is logged at LOG_INFO. + */ + syslog_level = (level > 8) ? LOG_ERR : LOG_INFO; + bb_verror_msg(ctl + 1, va, /* strerr: */ NULL); } va_end(va); if (ctl[0] & 0x80) -- cgit v1.2.3-55-g6feb From d5929d602ee952d2f2468b4957a4ebd219108b2f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 30 Apr 2014 14:49:52 +0200 Subject: crond: simplify logging code Slight change in behavior: now -l 5 will enable the same messages formerly only enabled for -d 5. (Old behavior was non-sensical). function old new delta reopen_logfile_to_stderr - 34 +34 log8 - 26 +26 log7 - 26 +26 log5 - 26 +26 load_crontab 706 688 -18 rescan_crontab_dir 157 137 -20 crond_main 1486 1456 -30 fork_job 389 343 -46 crondlog 108 38 -70 ------------------------------------------------------------------------------ (add/remove: 4/0 grow/shrink: 0/5 up/down: 112/-184) Total: -72 bytes Signed-off-by: Denys Vlasenko --- miscutils/crond.c | 175 +++++++++++++++++++++++++++--------------------------- 1 file changed, 88 insertions(+), 87 deletions(-) diff --git a/miscutils/crond.c b/miscutils/crond.c index 8441b6cc5..210da5f47 100644 --- a/miscutils/crond.c +++ b/miscutils/crond.c @@ -125,12 +125,6 @@ enum { OPT_c = (1 << 5), OPT_d = (1 << 6) * ENABLE_FEATURE_CROND_D, }; -#if ENABLE_FEATURE_CROND_D -# define DebugOpt (option_mask32 & OPT_d) -#else -# define DebugOpt 0 -#endif - struct globals { unsigned log_level; /* = 8; */ @@ -149,41 +143,50 @@ struct globals { G.crontab_dir_name = CRONTABS; \ } while (0) +/* Log levels: + * 0 is the most verbose, default 8. + * For some reason, in fact only 5, 7 and 8 are used. + */ +static void crondlog(unsigned level, const char *msg, va_list va) +{ + if (level >= G.log_level) { + /* + * We are called only for info meesages. + * Warnings/errors use plain bb_[p]error_msg's, which + * need not touch syslog_level + * (they are ok with LOG_ERR default). + */ + syslog_level = LOG_INFO; + bb_verror_msg(msg, va, /* strerr: */ NULL); + syslog_level = LOG_ERR; + } +} -/* 0 is the most verbose, default 8 */ -#define LVL5 "\x05" -#define LVL7 "\x07" -#define LVL8 "\x08" -#define WARN9 "\x49" -#define DIE9 "\xc9" -/* level >= 20 is "error" */ -#define ERR20 "\x14" +static void log5(const char *msg, ...) +{ + va_list va; + va_start(va, msg); + crondlog(4, msg, va); + va_end(va); +} -static void crondlog(const char *ctl, ...) __attribute__ ((format (printf, 1, 2))); -static void crondlog(const char *ctl, ...) +static void log7(const char *msg, ...) { va_list va; - unsigned level = (ctl[0] & 0x1f); + va_start(va, msg); + crondlog(7, msg, va); + va_end(va); +} - va_start(va, ctl); - if (level >= G.log_level) { - if (G.log_filename) { - /* If log to file, reopen log file at every write: */ - int logfd = open_or_warn(G.log_filename, O_WRONLY | O_CREAT | O_APPEND); - if (logfd >= 0) - xmove_fd(logfd, STDERR_FILENO); - } - /* When we log to syslog, level > 8 is logged at LOG_ERR - * syslog level, level <= 8 is logged at LOG_INFO. - */ - syslog_level = (level > 8) ? LOG_ERR : LOG_INFO; - bb_verror_msg(ctl + 1, va, /* strerr: */ NULL); - } +static void log8(const char *msg, ...) +{ + va_list va; + va_start(va, msg); + crondlog(8, msg, va); va_end(va); - if (ctl[0] & 0x80) - exit(20); } + static const char DowAry[] ALIGN1 = "sun""mon""tue""wed""thu""fri""sat" ; @@ -293,12 +296,12 @@ static void ParseField(char *user, char *ary, int modvalue, int off, if (*ptr) { err: - crondlog(WARN9 "user %s: parse error at %s", user, base); + bb_error_msg("user %s: parse error at %s", user, base); return; } - if (DebugOpt && (G.log_level <= 5)) { /* like LVL5 */ - /* can't use crondlog, it inserts '\n' */ + /* can't use log5 (it inserts newlines), open-coding it */ + if (G.log_level <= 5 && logmode != LOGMODE_SYSLOG) { int i; for (i = 0; i < modvalue; ++i) fprintf(stderr, "%d", (unsigned char)ary[i]); @@ -398,7 +401,7 @@ static void load_crontab(const char *fileName) delete_cronfile(fileName); if (!getpwnam(fileName)) { - crondlog(LVL7 "ignoring file '%s' (no such user)", fileName); + log7("ignoring file '%s' (no such user)", fileName); return; } @@ -420,7 +423,7 @@ static void load_crontab(const char *fileName) CronLine *line; if (!--maxLines) { - crondlog(WARN9 "user %s: too many lines", fileName); + bb_error_msg("user %s: too many lines", fileName); break; } @@ -428,8 +431,7 @@ static void load_crontab(const char *fileName) if (!n) break; - if (DebugOpt) - crondlog(LVL5 "user:%s entry:%s", fileName, parser->data); + log5("user:%s entry:%s", fileName, parser->data); /* check if line is setting MAILTO= */ if (0 == strncmp(tokens[0], "MAILTO=", 7)) { @@ -472,9 +474,7 @@ static void load_crontab(const char *fileName) #endif /* copy command */ line->cl_cmd = xstrdup(tokens[5]); - if (DebugOpt) { - crondlog(LVL5 " command:%s", tokens[5]); - } + log5(" command:%s", tokens[5]); pline = &line->cl_next; //bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]); } @@ -519,17 +519,16 @@ static void rescan_crontab_dir(void) /* Remove cron update file */ unlink(CRONUPDATE); /* Re-chdir, in case directory was renamed & deleted */ - if (chdir(G.crontab_dir_name) < 0) { - crondlog(DIE9 "chdir(%s)", G.crontab_dir_name); - } + xchdir(G.crontab_dir_name); /* Scan directory and add associated users */ { DIR *dir = opendir("."); struct dirent *den; + /* xopendir exists, but "can't open '.'" is not informative */ if (!dir) - crondlog(DIE9 "chdir(%s)", "."); /* exits */ + bb_error_msg_and_die("can't open '%s'", G.crontab_dir_name); while ((den = readdir(dir)) != NULL) { if (strchr(den->d_name, '.') != NULL) { continue; @@ -576,10 +575,8 @@ static void change_user(struct passwd *pas) /* careful: we're after vfork! */ change_identity(pas); /* - initgroups, setgid, setuid */ if (chdir(pas->pw_dir) < 0) { - crondlog(WARN9 "chdir(%s)", pas->pw_dir); - if (chdir(CRON_DIR) < 0) { - crondlog(DIE9 "chdir(%s)", CRON_DIR); /* exits */ - } + bb_error_msg("can't change directory to '%s'", pas->pw_dir); + xchdir(CRON_DIR); } } @@ -591,25 +588,25 @@ fork_job(const char *user, int mailFd, const char *prog, const char *shell_cmd /* if NULL, we run sendmail */ ) { + smallint sv_logmode; struct passwd *pas; pid_t pid; /* prepare things before vfork */ pas = getpwnam(user); if (!pas) { - crondlog(WARN9 "can't get uid for %s", user); + bb_error_msg("can't get uid for %s", user); goto err; } set_env_vars(pas); + sv_logmode = logmode; pid = vfork(); if (pid == 0) { /* CHILD */ /* initgroups, setgid, setuid, and chdir to home or CRON_DIR */ change_user(pas); - if (DebugOpt) { - crondlog(LVL5 "child running %s", prog); - } + log5("child running %s", prog); if (mailFd >= 0) { xmove_fd(mailFd, shell_cmd ? 1 : 0); dup2(1, 2); @@ -617,15 +614,17 @@ fork_job(const char *user, int mailFd, /* crond 3.0pl1-100 puts tasks in separate process groups */ bb_setpgrp(); execlp(prog, prog, (shell_cmd ? "-c" : SENDMAIL_ARGS), shell_cmd, (char *) NULL); - crondlog(ERR20 "can't execute '%s' for user %s", prog, user); - if (shell_cmd) { - fdprintf(1, "Exec failed: %s -c %s\n", prog, shell_cmd); - } - _exit(EXIT_SUCCESS); + /* + * I want this error message on stderr too, + * even if other messages go only to syslog: + */ + logmode |= LOGMODE_STDIO; + bb_error_msg_and_die("can't execute '%s' for user %s", prog, user); } + logmode = sv_logmode; if (pid < 0) { - crondlog(ERR20 "can't vfork"); + bb_perror_msg("vfork"); err: pid = 0; } /* else: PARENT, FORK SUCCESS */ @@ -658,7 +657,7 @@ static void start_one_job(const char *user, CronLine *line) line->cl_cmd); line->cl_empty_mail_size = lseek(mailFd, 0, SEEK_CUR); } else { - crondlog(ERR20 "can't create mail file %s for user %s, " + bb_error_msg("can't create mail file %s for user %s, " "discarding output", mailFile, user); } } @@ -731,7 +730,7 @@ static void start_one_job(const char *user, CronLine *line) pas = getpwnam(user); if (!pas) { - crondlog(WARN9 "can't get uid for %s", user); + bb_error_msg("can't get uid for %s", user); goto err; } @@ -744,18 +743,14 @@ static void start_one_job(const char *user, CronLine *line) /* CHILD */ /* initgroups, setgid, setuid, and chdir to home or CRON_DIR */ change_user(pas); - if (DebugOpt) { - crondlog(LVL5 "child running %s", DEFAULT_SHELL); - } + log5("child running %s", DEFAULT_SHELL); /* crond 3.0pl1-100 puts tasks in separate process groups */ bb_setpgrp(); execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_cmd, (char *) NULL); - crondlog(ERR20 "can't execute '%s' for user %s", DEFAULT_SHELL, user); - _exit(EXIT_SUCCESS); + bb_error_msg_and_die("can't execute '%s' for user %s", DEFAULT_SHELL, user); } if (pid < 0) { - /* FORK FAILED */ - crondlog(ERR20 "can't vfork"); + bb_perror_msg("vfork"); err: pid = 0; } @@ -787,24 +782,20 @@ static void flag_starting_jobs(time_t t1, time_t t2) ptm = localtime(&t); for (file = G.cron_files; file; file = file->cf_next) { - if (DebugOpt) - crondlog(LVL5 "file %s:", file->cf_username); + log5("file %s:", file->cf_username); if (file->cf_deleted) continue; for (line = file->cf_lines; line; line = line->cl_next) { - if (DebugOpt) - crondlog(LVL5 " line %s", line->cl_cmd); + log5(" line %s", line->cl_cmd); if (line->cl_Mins[ptm->tm_min] && line->cl_Hrs[ptm->tm_hour] && (line->cl_Days[ptm->tm_mday] || line->cl_Dow[ptm->tm_wday]) && line->cl_Mons[ptm->tm_mon] ) { - if (DebugOpt) { - crondlog(LVL5 " job: %d %s", + log5(" job: %d %s", (int)line->cl_pid, line->cl_cmd); - } if (line->cl_pid > 0) { - crondlog(LVL8 "user %s: process already running: %s", + log8("user %s: process already running: %s", file->cf_username, line->cl_cmd); } else if (line->cl_pid == 0) { line->cl_pid = -1; @@ -833,7 +824,7 @@ static void start_jobs(void) start_one_job(file->cf_username, line); pid = line->cl_pid; - crondlog(LVL8 "USER %s pid %3d cmd %s", + log8("USER %s pid %3d cmd %s", file->cf_username, (int)pid, line->cl_cmd); if (pid < 0) { file->cf_wants_starting = 1; @@ -885,6 +876,15 @@ static int check_completions(void) return num_still_running; } +static void reopen_logfile_to_stderr(void) +{ + if (G.log_filename) { + int logfd = open_or_warn(G.log_filename, O_WRONLY | O_CREAT | O_APPEND); + if (logfd >= 0) + xmove_fd(logfd, STDERR_FILENO); + } +} + int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int crond_main(int argc UNUSED_PARAM, char **argv) { @@ -916,10 +916,12 @@ int crond_main(int argc UNUSED_PARAM, char **argv) logmode = LOGMODE_SYSLOG; } + reopen_logfile_to_stderr(); + xchdir(G.crontab_dir_name); //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */ - crondlog(LVL8 "crond (busybox "BB_VER") started, log level %d", G.log_level); + log8("crond (busybox "BB_VER") started, log level %d", G.log_level); rescan_crontab_dir(); write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid"); @@ -932,14 +934,14 @@ int crond_main(int argc UNUSED_PARAM, char **argv) time_t t1; long dt; - t1 = t2; - /* Synchronize to 1 minute, minimum 1 second */ - sleep(sleep_time - (time(NULL) % sleep_time) + 1); - + t1 = t2; + sleep(sleep_time - (time(NULL) % sleep_time)); t2 = time(NULL); dt = (long)t2 - (long)t1; + reopen_logfile_to_stderr(); + /* * The file 'cron.update' is checked to determine new cron * jobs. The directory is rescanned once an hour to deal @@ -967,10 +969,9 @@ int crond_main(int argc UNUSED_PARAM, char **argv) rescan_crontab_dir(); } process_cron_update_file(); - if (DebugOpt) - crondlog(LVL5 "wakeup dt=%ld", dt); + log5("wakeup dt=%ld", dt); if (dt < -60 * 60 || dt > 60 * 60) { - crondlog(WARN9 "time disparity of %ld minutes detected", dt / 60); + bb_error_msg("time disparity of %ld minutes detected", dt / 60); /* and we do not run any jobs in this case */ } else if (dt > 0) { /* Usual case: time advances forward, as expected */ -- cgit v1.2.3-55-g6feb From 43b8a1cc3825eedb85e10cbe2f91a20fbe7c4434 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 30 Apr 2014 17:38:27 +0200 Subject: crond: implement "SHELL=prog" in crontab. Export LOGNAME as POSIX wants function old new delta fork_job 343 456 +113 load_crontab 688 777 +89 crond_main 1456 1434 -22 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/1 up/down: 202/-22) Total: 180 bytes Signed-off-by: Denys Vlasenko --- miscutils/crond.c | 72 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/miscutils/crond.c b/miscutils/crond.c index 210da5f47..3659b9a6f 100644 --- a/miscutils/crond.c +++ b/miscutils/crond.c @@ -104,6 +104,7 @@ typedef struct CronLine { int cl_empty_mail_size; /* size of mail header only, 0 if no mailfile */ char *cl_mailto; /* whom to mail results, may be NULL */ #endif + char *cl_shell; /* ordered by size, not in natural order. makes code smaller: */ char cl_Dow[7]; /* 0-6, beginning sunday */ char cl_Mons[12]; /* 0-11 */ @@ -135,6 +136,8 @@ struct globals { #if SETENV_LEAKS char *env_var_user; char *env_var_home; + char *env_var_shell; + char *env_var_logname; #endif } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) @@ -397,6 +400,7 @@ static void load_crontab(const char *fileName) #if ENABLE_FEATURE_CROND_CALL_SENDMAIL char *mailTo = NULL; #endif + char *shell = NULL; delete_cronfile(fileName); @@ -441,7 +445,12 @@ static void load_crontab(const char *fileName) #endif /* otherwise just ignore such lines */ continue; } -//TODO: handle SHELL=, HOME= too? "man crontab" says: + if (0 == strncmp(tokens[0], "SHELL=", 6)) { + free(shell); + shell = xstrdup(&tokens[0][6]); + continue; + } +//TODO: handle HOME= too? "man crontab" says: //name = value // //where the spaces around the equal-sign (=) are optional, and any subsequent @@ -453,6 +462,7 @@ static void load_crontab(const char *fileName) //SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd //line of the crontab's owner. HOME and SHELL may be overridden by settings //in the crontab; LOGNAME may not. + /* check if a minimum of tokens is specified */ if (n < 6) continue; @@ -472,9 +482,9 @@ static void load_crontab(const char *fileName) /* copy mailto (can be NULL) */ line->cl_mailto = xstrdup(mailTo); #endif + line->cl_shell = xstrdup(shell); /* copy command */ line->cl_cmd = xstrdup(tokens[5]); - log5(" command:%s", tokens[5]); pline = &line->cl_next; //bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]); } @@ -484,6 +494,10 @@ static void load_crontab(const char *fileName) G.cron_files = file; } config_close(parser); +#if ENABLE_FEATURE_CROND_CALL_SENDMAIL + free(mailTo); +#endif + free(shell); } static void process_cron_update_file(void) @@ -555,19 +569,22 @@ static void safe_setenv(char **pvar_val, const char *var, const char *val) } #endif -static void set_env_vars(struct passwd *pas) +static void set_env_vars(struct passwd *pas, const char *shell) { + /* POSIX requires crond to set up at least HOME, LOGNAME, PATH, SHELL. + * We assume crond inherited suitable PATH. + */ #if SETENV_LEAKS + safe_setenv(&G.env_var_logname, "LOGNAME", pas->pw_name); safe_setenv(&G.env_var_user, "USER", pas->pw_name); safe_setenv(&G.env_var_home, "HOME", pas->pw_dir); - /* if we want to set user's shell instead: */ - /*safe_setenv(G.env_var_shell, "SHELL", pas->pw_shell);*/ + safe_setenv(&G.env_var_shell, "SHELL", shell); #else + xsetenv("LOGNAME", pas->pw_name); xsetenv("USER", pas->pw_name); xsetenv("HOME", pas->pw_dir); + xsetenv("SHELL", shell); #endif - /* currently, we use constant one: */ - /*setenv("SHELL", DEFAULT_SHELL, 1); - done earlier */ } static void change_user(struct passwd *pas) @@ -584,12 +601,11 @@ static void change_user(struct passwd *pas) #if ENABLE_FEATURE_CROND_CALL_SENDMAIL static pid_t -fork_job(const char *user, int mailFd, - const char *prog, - const char *shell_cmd /* if NULL, we run sendmail */ -) { - smallint sv_logmode; +fork_job(const char *user, int mailFd, CronLine *line, bool run_sendmail) +{ struct passwd *pas; + const char *shell, *prog; + smallint sv_logmode; pid_t pid; /* prepare things before vfork */ @@ -598,7 +614,11 @@ fork_job(const char *user, int mailFd, bb_error_msg("can't get uid for %s", user); goto err; } - set_env_vars(pas); + + shell = line->cl_shell ? line->cl_shell : DEFAULT_SHELL; + prog = run_sendmail ? SENDMAIL : shell; + + set_env_vars(pas, shell); sv_logmode = logmode; pid = vfork(); @@ -608,12 +628,15 @@ fork_job(const char *user, int mailFd, change_user(pas); log5("child running %s", prog); if (mailFd >= 0) { - xmove_fd(mailFd, shell_cmd ? 1 : 0); + xmove_fd(mailFd, run_sendmail ? 0 : 1); dup2(1, 2); } /* crond 3.0pl1-100 puts tasks in separate process groups */ bb_setpgrp(); - execlp(prog, prog, (shell_cmd ? "-c" : SENDMAIL_ARGS), shell_cmd, (char *) NULL); + if (!run_sendmail) + execlp(prog, prog, "-c", line->cl_cmd, (char *) NULL); + else + execlp(prog, prog, SENDMAIL_ARGS, (char *) NULL); /* * I want this error message on stderr too, * even if other messages go only to syslog: @@ -662,7 +685,7 @@ static void start_one_job(const char *user, CronLine *line) } } - line->cl_pid = fork_job(user, mailFd, DEFAULT_SHELL, line->cl_cmd); + line->cl_pid = fork_job(user, mailFd, line, /*sendmail?*/ 0); if (mailFd >= 0) { if (line->cl_pid <= 0) { unlink(mailFile); @@ -718,13 +741,14 @@ static void process_finished_job(const char *user, CronLine *line) } line->cl_empty_mail_size = 0; /* if (line->cl_mailto) - always true if cl_empty_mail_size was nonzero */ - line->cl_pid = fork_job(user, mailFd, SENDMAIL, NULL); + line->cl_pid = fork_job(user, mailFd, line, /*sendmail?*/ 1); } #else /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */ static void start_one_job(const char *user, CronLine *line) { + const char *shell; struct passwd *pas; pid_t pid; @@ -735,7 +759,8 @@ static void start_one_job(const char *user, CronLine *line) } /* Prepare things before vfork */ - set_env_vars(pas); + shell = line->cl_shell ? line->cl_shell : DEFAULT_SHELL; + set_env_vars(pas, shell); /* Fork as the user in question and run program */ pid = vfork(); @@ -743,11 +768,11 @@ static void start_one_job(const char *user, CronLine *line) /* CHILD */ /* initgroups, setgid, setuid, and chdir to home or CRON_DIR */ change_user(pas); - log5("child running %s", DEFAULT_SHELL); + log5("child running %s", shell); /* crond 3.0pl1-100 puts tasks in separate process groups */ bb_setpgrp(); - execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_cmd, (char *) NULL); - bb_error_msg_and_die("can't execute '%s' for user %s", DEFAULT_SHELL, user); + execl(shell, shell, "-c", line->cl_cmd, (char *) NULL); + bb_error_msg_and_die("can't execute '%s' for user %s", shell, user); } if (pid < 0) { bb_perror_msg("vfork"); @@ -916,11 +941,10 @@ int crond_main(int argc UNUSED_PARAM, char **argv) logmode = LOGMODE_SYSLOG; } - reopen_logfile_to_stderr(); + //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ + reopen_logfile_to_stderr(); xchdir(G.crontab_dir_name); - //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ - xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */ log8("crond (busybox "BB_VER") started, log level %d", G.log_level); rescan_crontab_dir(); write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid"); -- cgit v1.2.3-55-g6feb From fe33683c8bad841a1b5e947fcc6a502b85ca53cf Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 2 May 2014 07:18:55 +0200 Subject: Fix compile failures Signed-off-by: Denys Vlasenko --- libbb/verror_msg.c | 2 ++ miscutils/less.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libbb/verror_msg.c b/libbb/verror_msg.c index 87efb56b3..0ef2a311f 100644 --- a/libbb/verror_msg.c +++ b/libbb/verror_msg.c @@ -11,7 +11,9 @@ # include #endif +#if ENABLE_FEATURE_SYSLOG smallint syslog_level = LOG_ERR; +#endif smallint logmode = LOGMODE_STDIO; const char *msg_eol = "\n"; diff --git a/miscutils/less.c b/miscutils/less.c index 36d0a0bd9..d84df469c 100644 --- a/miscutils/less.c +++ b/miscutils/less.c @@ -456,7 +456,7 @@ static void read_lines(void) readeof = eof_error; if (eof_error <= 0) goto reached_eof; - had_progress = 1; + IF_FEATURE_LESS_REGEXP(had_progress = 1;) } c = readbuf[readpos]; /* backspace? [needed for manpages] */ -- cgit v1.2.3-55-g6feb From 528808fa7b0778931fb624e95c38cf8cbc2fff90 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 2 May 2014 09:05:03 +0200 Subject: libbb: make rtc_xopen try harder on EBUSY function old new delta rtc_xopen 77 139 +62 Signed-off-by: Denys Vlasenko --- libbb/rtc.c | 52 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/libbb/rtc.c b/libbb/rtc.c index 97455e86a..f84da09bb 100644 --- a/libbb/rtc.c +++ b/libbb/rtc.c @@ -33,23 +33,55 @@ int FAST_FUNC rtc_adjtime_is_utc(void) return utc; } +/* rtc opens are exclusive. + * Try to run two "hwclock -w" at the same time to see it. + * Users wouldn't expect that to fail merely because /dev/rtc + * was momentarily busy, let's try a bit harder on errno == EBUSY. + */ +static int open_loop_on_busy(const char *name, int flags) +{ + int rtc; + /* + * Tested with two parallel "hwclock -w" loops. + * With try = 10, no failures with 2x1000000 loop iterations. + */ + int try = 1000 / 20; + again: + errno = 0; + rtc = open(name, flags); + if (errno == EBUSY) { + usleep(20 * 1000); + if (--try != 0) + goto again; + /* EBUSY. Last try, exit on error instead of returning -1 */ + return xopen(name, flags); + } + return rtc; +} + +/* Never fails */ int FAST_FUNC rtc_xopen(const char **default_rtc, int flags) { int rtc; + const char *name = + "/dev/rtc""\0" + "/dev/rtc0""\0" + "/dev/misc/rtc""\0"; - if (!*default_rtc) { - *default_rtc = "/dev/rtc"; - rtc = open(*default_rtc, flags); - if (rtc >= 0) - return rtc; - *default_rtc = "/dev/rtc0"; - rtc = open(*default_rtc, flags); + if (!*default_rtc) + goto try_name; + name = ""; /*else: we have rtc name, don't try other names */ + + for (;;) { + rtc = open_loop_on_busy(*default_rtc, flags); if (rtc >= 0) return rtc; - *default_rtc = "/dev/misc/rtc"; + name += strlen(name) + 1; + if (!name[0]) + return xopen(*default_rtc, flags); + try_name: + *default_rtc = name; } - - return xopen(*default_rtc, flags); } void FAST_FUNC rtc_read_tm(struct tm *ptm, int fd) -- cgit v1.2.3-55-g6feb From d8290c2ef039c389b2f39704543277865f3693fb Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 2 May 2014 09:07:16 +0200 Subject: libbb: fix thinko in rtc_xopen() Signed-off-by: Denys Vlasenko --- libbb/rtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbb/rtc.c b/libbb/rtc.c index f84da09bb..6d06d57f9 100644 --- a/libbb/rtc.c +++ b/libbb/rtc.c @@ -76,11 +76,11 @@ int FAST_FUNC rtc_xopen(const char **default_rtc, int flags) rtc = open_loop_on_busy(*default_rtc, flags); if (rtc >= 0) return rtc; - name += strlen(name) + 1; if (!name[0]) return xopen(*default_rtc, flags); try_name: *default_rtc = name; + name += strlen(name) + 1; } } -- cgit v1.2.3-55-g6feb From a4476eb6543505f8685b59b138cb868b32347d71 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 2 May 2014 12:46:15 +0200 Subject: rtcwake: fix incorrect (reversed) rtc/sys adjuestment; code shrink function old new delta rtcwake_main 482 462 -20 Signed-off-by: Denys Vlasenko --- util-linux/rtcwake.c | 55 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/util-linux/rtcwake.c b/util-linux/rtcwake.c index 735a29822..33cdbfad4 100644 --- a/util-linux/rtcwake.c +++ b/util-linux/rtcwake.c @@ -51,7 +51,6 @@ #define SYS_RTC_PATH "/sys/class/rtc/%s/device/power/wakeup" #define SYS_POWER_PATH "/sys/power/state" -#define DEFAULT_MODE "standby" static NOINLINE bool may_wakeup(const char *rtcname) { @@ -122,17 +121,16 @@ static NOINLINE void setup_alarm(int fd, time_t *wakeup, time_t rtc_time) int rtcwake_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int rtcwake_main(int argc UNUSED_PARAM, char **argv) { - time_t rtc_time; - unsigned opt; const char *rtcname = NULL; - const char *suspend; + const char *suspend = "standby"; const char *opt_seconds; const char *opt_time; + time_t rtc_time; time_t sys_time; - time_t alarm_time = 0; - unsigned seconds = 0; + time_t alarm_time = alarm_time; + unsigned seconds = seconds; /* for compiler */ int utc = -1; int fd; @@ -148,6 +146,8 @@ int rtcwake_main(int argc UNUSED_PARAM, char **argv) ; applet_long_options = rtcwake_longopts; #endif + /* Must have -s or -t, exclusive */ + opt_complementary = "s:t:s--t:t--s"; opt = getopt32(argv, "alud:m:s:t:", &rtcname, &suspend, &opt_seconds, &opt_time); /* this is the default @@ -156,17 +156,17 @@ int rtcwake_main(int argc UNUSED_PARAM, char **argv) */ if (opt & (RTCWAKE_OPT_UTC | RTCWAKE_OPT_LOCAL)) utc = opt & RTCWAKE_OPT_UTC; - if (!(opt & RTCWAKE_OPT_SUSPEND_MODE)) - suspend = DEFAULT_MODE; - if (opt & RTCWAKE_OPT_SECONDS) + if (opt & RTCWAKE_OPT_SECONDS) { /* alarm time, seconds-to-sleep (relative) */ - seconds = xatoi(opt_seconds); - if (opt & RTCWAKE_OPT_TIME) + seconds = xatou(opt_seconds); + } else { + /* RTCWAKE_OPT_TIME */ /* alarm time, time_t (absolute, seconds since 1/1 1970 UTC) */ - alarm_time = xatol(opt_time); - - if (!alarm_time && !seconds) - bb_error_msg_and_die("must provide wake time"); + if (sizeof(alarm_time) <= sizeof(long)) + alarm_time = xatol(opt_time); + else + alarm_time = xatoll(opt_time); + } if (utc == -1) utc = rtc_adjtime_is_utc(); @@ -177,8 +177,9 @@ int rtcwake_main(int argc UNUSED_PARAM, char **argv) /* this RTC must exist and (if we'll sleep) be wakeup-enabled */ fd = rtc_xopen(&rtcname, O_RDONLY); - if (strcmp(suspend, "on") && !may_wakeup(rtcname)) - bb_error_msg_and_die("%s not enabled for wakeup events", rtcname); + if (strcmp(suspend, "on") != 0) + if (!may_wakeup(rtcname)) + bb_error_msg_and_die("%s not enabled for wakeup events", rtcname); /* relative or absolute alarm time, normalized to time_t */ sys_time = time(NULL); @@ -188,21 +189,29 @@ int rtcwake_main(int argc UNUSED_PARAM, char **argv) rtc_time = rtc_tm2time(&tm_time, utc); } - - if (alarm_time) { - if (alarm_time < sys_time) + if (opt & RTCWAKE_OPT_TIME) { + /* Correct for RTC<->system clock difference */ + alarm_time += rtc_time - sys_time; + if (alarm_time < rtc_time) + /* + * Compat message text. + * I'd say "RTC time is already ahead of ..." instead. + */ bb_error_msg_and_die("time doesn't go backward to %s", ctime(&alarm_time)); - alarm_time += sys_time - rtc_time; } else alarm_time = rtc_time + seconds + 1; - setup_alarm(fd, &alarm_time, rtc_time); + setup_alarm(fd, &alarm_time, rtc_time); sync(); +#if 0 /*debug*/ + printf("sys_time: %s", ctime(&sys_time)); + printf("rtc_time: %s", ctime(&rtc_time)); +#endif printf("wakeup from \"%s\" at %s", suspend, ctime(&alarm_time)); fflush_all(); usleep(10 * 1000); - if (strcmp(suspend, "on")) + if (strcmp(suspend, "on") != 0) xopen_xwrite_close(SYS_POWER_PATH, suspend); else { /* "fake" suspend ... we'll do the delay ourselves */ -- cgit v1.2.3-55-g6feb From 15a357e5962634c94ee322fee4da897312090a89 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 2 May 2014 17:08:29 +0200 Subject: libbb: fix empty PATH components handling function old new delta find_execable 81 86 +5 exists_execable 71 66 -5 Signed-off-by: Denys Vlasenko --- libbb/execable.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/libbb/execable.c b/libbb/execable.c index 178a00a5f..a3caea6f9 100644 --- a/libbb/execable.c +++ b/libbb/execable.c @@ -30,6 +30,14 @@ int FAST_FUNC execable_file(const char *name) */ char* FAST_FUNC find_execable(const char *filename, char **PATHp) { + /* About empty components in $PATH: + * http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html + * 8.3 Other Environment Variables - PATH + * A zero-length prefix is a legacy feature that indicates the current + * working directory. It appears as two adjacent colons ( "::" ), as an + * initial colon preceding the rest of the list, or as a trailing colon + * following the rest of the list. + */ char *p, *n; p = *PATHp; @@ -37,14 +45,15 @@ char* FAST_FUNC find_execable(const char *filename, char **PATHp) n = strchr(p, ':'); if (n) *n++ = '\0'; - if (*p != '\0') { /* it's not a PATH="foo::bar" situation */ - p = concat_path_file(p, filename); - if (execable_file(p)) { - *PATHp = n; - return p; - } - free(p); + p = concat_path_file( + p[0] ? p : ".", /* handle "::" case */ + filename + ); + if (execable_file(p)) { + *PATHp = n; + return p; } + free(p); p = n; } /* on loop exit p == NULL */ return p; @@ -60,11 +69,8 @@ int FAST_FUNC exists_execable(const char *filename) char *tmp = path; char *ret = find_execable(filename, &tmp); free(path); - if (ret) { - free(ret); - return 1; - } - return 0; + free(ret); + return ret != NULL; } #if ENABLE_FEATURE_PREFER_APPLETS -- cgit v1.2.3-55-g6feb From e765b5ac349a8f9305e52b3ab2c3ac78c17bf283 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 2 May 2014 17:15:58 +0200 Subject: libbb: rename execable -> executable. No code changes English speakers complained that it sounded awfully broken. Signed-off-by: Denys Vlasenko --- debianutils/which.c | 10 +++--- docs/ifupdown_design.txt | 2 +- include/libbb.h | 6 ++-- libbb/Kbuild.src | 2 +- libbb/execable.c | 92 ------------------------------------------------ libbb/executable.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ networking/ifupdown.c | 4 +-- 7 files changed, 104 insertions(+), 104 deletions(-) delete mode 100644 libbb/execable.c create mode 100644 libbb/executable.c diff --git a/debianutils/which.c b/debianutils/which.c index 15fd598b7..760bcdcad 100644 --- a/debianutils/which.c +++ b/debianutils/which.c @@ -46,7 +46,7 @@ int which_main(int argc UNUSED_PARAM, char **argv) #if ENABLE_DESKTOP /* Much bloat just to support -a */ if (strchr(*argv, '/')) { - if (execable_file(*argv)) { + if (file_is_executable(*argv)) { puts(*argv); continue; } @@ -55,7 +55,7 @@ int which_main(int argc UNUSED_PARAM, char **argv) char *path2 = xstrdup(path); char *tmp = path2; - p = find_execable(*argv, &tmp); + p = find_executable(*argv, &tmp); if (!p) status = EXIT_FAILURE; else { @@ -65,7 +65,7 @@ int which_main(int argc UNUSED_PARAM, char **argv) if (opt) { /* -a: show matches in all PATH components */ if (tmp) { - p = find_execable(*argv, &tmp); + p = find_executable(*argv, &tmp); if (p) goto print; } @@ -76,14 +76,14 @@ int which_main(int argc UNUSED_PARAM, char **argv) #else /* Just ignoring -a */ if (strchr(*argv, '/')) { - if (execable_file(*argv)) { + if (file_is_executable(*argv)) { puts(*argv); continue; } } else { char *path2 = xstrdup(path); char *tmp = path2; - p = find_execable(*argv, &tmp); + p = find_executable(*argv, &tmp); free(path2); if (p) { puts(p); diff --git a/docs/ifupdown_design.txt b/docs/ifupdown_design.txt index 8ab4e51ad..39e28a9f4 100644 --- a/docs/ifupdown_design.txt +++ b/docs/ifupdown_design.txt @@ -21,7 +21,7 @@ static int dhcp_down(struct interface_defn_t *ifd, execfn *exec) #if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP int i ; for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) { - if (exists_execable(ext_dhcp_clients[i].name)) + if (executable_exists(ext_dhcp_clients[i].name)) return execute(ext_dhcp_clients[i].stopcmd, ifd, exec); } bb_error_msg("no dhcp clients found, using static interface shutdown"); diff --git a/include/libbb.h b/include/libbb.h index 29cf6bc6d..afdee38c4 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -912,9 +912,9 @@ void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const #endif -int execable_file(const char *name) FAST_FUNC; -char *find_execable(const char *filename, char **PATHp) FAST_FUNC; -int exists_execable(const char *filename) FAST_FUNC; +int file_is_executable(const char *name) FAST_FUNC; +char *find_executable(const char *filename, char **PATHp) FAST_FUNC; +int executable_exists(const char *filename) FAST_FUNC; /* BB_EXECxx always execs (it's not doing NOFORK/NOEXEC stuff), * but it may exec busybox and call applet instead of searching PATH. diff --git a/libbb/Kbuild.src b/libbb/Kbuild.src index a6468f171..6578d1171 100644 --- a/libbb/Kbuild.src +++ b/libbb/Kbuild.src @@ -30,7 +30,7 @@ lib-y += crc32.o lib-y += default_error_retval.o lib-y += device_open.o lib-y += dump.o -lib-y += execable.o +lib-y += executable.o lib-y += fclose_nonstdin.o lib-y += fflush_stdout_and_exit.o lib-y += fgets_str.o diff --git a/libbb/execable.c b/libbb/execable.c deleted file mode 100644 index a3caea6f9..000000000 --- a/libbb/execable.c +++ /dev/null @@ -1,92 +0,0 @@ -/* vi: set sw=4 ts=4: */ -/* - * Utility routines. - * - * Copyright (C) 2006 Gabriel Somlo - * - * Licensed under GPLv2 or later, see file LICENSE in this source tree. - */ - -#include "libbb.h" - -/* check if path points to an executable file; - * return 1 if found; - * return 0 otherwise; - */ -int FAST_FUNC execable_file(const char *name) -{ - struct stat s; - return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode)); -} - -/* search (*PATHp) for an executable file; - * return allocated string containing full path if found; - * PATHp points to the component after the one where it was found - * (or NULL), - * you may call find_execable again with this PATHp to continue - * (if it's not NULL). - * return NULL otherwise; (PATHp is undefined) - * in all cases (*PATHp) contents will be trashed (s/:/NUL/). - */ -char* FAST_FUNC find_execable(const char *filename, char **PATHp) -{ - /* About empty components in $PATH: - * http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html - * 8.3 Other Environment Variables - PATH - * A zero-length prefix is a legacy feature that indicates the current - * working directory. It appears as two adjacent colons ( "::" ), as an - * initial colon preceding the rest of the list, or as a trailing colon - * following the rest of the list. - */ - char *p, *n; - - p = *PATHp; - while (p) { - n = strchr(p, ':'); - if (n) - *n++ = '\0'; - p = concat_path_file( - p[0] ? p : ".", /* handle "::" case */ - filename - ); - if (execable_file(p)) { - *PATHp = n; - return p; - } - free(p); - p = n; - } /* on loop exit p == NULL */ - return p; -} - -/* search $PATH for an executable file; - * return 1 if found; - * return 0 otherwise; - */ -int FAST_FUNC exists_execable(const char *filename) -{ - char *path = xstrdup(getenv("PATH")); - char *tmp = path; - char *ret = find_execable(filename, &tmp); - free(path); - free(ret); - return ret != NULL; -} - -#if ENABLE_FEATURE_PREFER_APPLETS -/* just like the real execvp, but try to launch an applet named 'file' first */ -int FAST_FUNC BB_EXECVP(const char *file, char *const argv[]) -{ - if (find_applet_by_name(file) >= 0) - execvp(bb_busybox_exec_path, argv); - return execvp(file, argv); -} -#endif - -int FAST_FUNC BB_EXECVP_or_die(char **argv) -{ - BB_EXECVP(argv[0], argv); - /* SUSv3-mandated exit codes */ - xfunc_error_retval = (errno == ENOENT) ? 127 : 126; - bb_perror_msg_and_die("can't execute '%s'", argv[0]); -} diff --git a/libbb/executable.c b/libbb/executable.c new file mode 100644 index 000000000..85ecc3e6c --- /dev/null +++ b/libbb/executable.c @@ -0,0 +1,92 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2006 Gabriel Somlo + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" + +/* check if path points to an executable file; + * return 1 if found; + * return 0 otherwise; + */ +int FAST_FUNC file_is_executable(const char *name) +{ + struct stat s; + return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode)); +} + +/* search (*PATHp) for an executable file; + * return allocated string containing full path if found; + * PATHp points to the component after the one where it was found + * (or NULL), + * you may call find_executable again with this PATHp to continue + * (if it's not NULL). + * return NULL otherwise; (PATHp is undefined) + * in all cases (*PATHp) contents will be trashed (s/:/NUL/). + */ +char* FAST_FUNC find_executable(const char *filename, char **PATHp) +{ + /* About empty components in $PATH: + * http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html + * 8.3 Other Environment Variables - PATH + * A zero-length prefix is a legacy feature that indicates the current + * working directory. It appears as two adjacent colons ( "::" ), as an + * initial colon preceding the rest of the list, or as a trailing colon + * following the rest of the list. + */ + char *p, *n; + + p = *PATHp; + while (p) { + n = strchr(p, ':'); + if (n) + *n++ = '\0'; + p = concat_path_file( + p[0] ? p : ".", /* handle "::" case */ + filename + ); + if (file_is_executable(p)) { + *PATHp = n; + return p; + } + free(p); + p = n; + } /* on loop exit p == NULL */ + return p; +} + +/* search $PATH for an executable file; + * return 1 if found; + * return 0 otherwise; + */ +int FAST_FUNC executable_exists(const char *filename) +{ + char *path = xstrdup(getenv("PATH")); + char *tmp = path; + char *ret = find_executable(filename, &tmp); + free(path); + free(ret); + return ret != NULL; +} + +#if ENABLE_FEATURE_PREFER_APPLETS +/* just like the real execvp, but try to launch an applet named 'file' first */ +int FAST_FUNC BB_EXECVP(const char *file, char *const argv[]) +{ + if (find_applet_by_name(file) >= 0) + execvp(bb_busybox_exec_path, argv); + return execvp(file, argv); +} +#endif + +int FAST_FUNC BB_EXECVP_or_die(char **argv) +{ + BB_EXECVP(argv[0], argv); + /* SUSv3-mandated exit codes */ + xfunc_error_retval = (errno == ENOENT) ? 127 : 126; + bb_perror_msg_and_die("can't execute '%s'", argv[0]); +} diff --git a/networking/ifupdown.c b/networking/ifupdown.c index e1ea351a4..c35d97a1a 100644 --- a/networking/ifupdown.c +++ b/networking/ifupdown.c @@ -555,7 +555,7 @@ static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec) return 0; # endif for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) { - if (exists_execable(ext_dhcp_clients[i].name)) + if (executable_exists(ext_dhcp_clients[i].name)) return execute(ext_dhcp_clients[i].startcmd, ifd, exec); } bb_error_msg("no dhcp clients found"); @@ -592,7 +592,7 @@ static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec) unsigned i; for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) { - if (exists_execable(ext_dhcp_clients[i].name)) { + if (executable_exists(ext_dhcp_clients[i].name)) { result = execute(ext_dhcp_clients[i].stopcmd, ifd, exec); if (result) break; -- cgit v1.2.3-55-g6feb From a875d59d24dc4e501131a2d5cb6a4afdb48745c1 Mon Sep 17 00:00:00 2001 From: Tito Ragusa Date: Sat, 3 May 2014 16:34:36 +0200 Subject: which: rewrite function old new delta which_main 237 212 -25 Signed-off-by: Tito Ragusa Signed-off-by: Denys Vlasenko --- debianutils/which.c | 83 +++++++++++++++-------------------------------------- 1 file changed, 23 insertions(+), 60 deletions(-) diff --git a/debianutils/which.c b/debianutils/which.c index 760bcdcad..d50e7a0d3 100644 --- a/debianutils/which.c +++ b/debianutils/which.c @@ -1,13 +1,9 @@ /* vi: set sw=4 ts=4: */ /* - * Which implementation for busybox - * * Copyright (C) 1999-2004 by Erik Andersen * Copyright (C) 2006 Gabriel Somlo * * Licensed under GPLv2 or later, see file LICENSE in this source tree. - * - * Based on which from debianutils */ //usage:#define which_trivial_usage @@ -24,76 +20,43 @@ int which_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int which_main(int argc UNUSED_PARAM, char **argv) { - IF_DESKTOP(int opt;) - int status = EXIT_SUCCESS; - char *path; - char *p; + const char *env_path; + int status = 0; + + env_path = getenv("PATH"); + if (!env_path) + env_path = bb_default_root_path; opt_complementary = "-1"; /* at least one argument */ - IF_DESKTOP(opt =) getopt32(argv, "a"); + getopt32(argv, "a"); argv += optind; - /* This matches what is seen on e.g. ubuntu. - * "which" there is a shell script. */ - path = getenv("PATH"); - if (!path) { - path = (char*)bb_PATH_root_path; - putenv(path); - path += 5; /* skip "PATH=" */ - } - do { -#if ENABLE_DESKTOP -/* Much bloat just to support -a */ - if (strchr(*argv, '/')) { - if (file_is_executable(*argv)) { - puts(*argv); - continue; - } - status = EXIT_FAILURE; - } else { - char *path2 = xstrdup(path); - char *tmp = path2; + int missing = 1; - p = find_executable(*argv, &tmp); - if (!p) - status = EXIT_FAILURE; - else { - print: - puts(p); - free(p); - if (opt) { - /* -a: show matches in all PATH components */ - if (tmp) { - p = find_executable(*argv, &tmp); - if (p) - goto print; - } - } - } - free(path2); - } -#else -/* Just ignoring -a */ + /* If file contains a slash don't use PATH */ if (strchr(*argv, '/')) { if (file_is_executable(*argv)) { + missing = 0; puts(*argv); - continue; } } else { - char *path2 = xstrdup(path); - char *tmp = path2; - p = find_executable(*argv, &tmp); - free(path2); - if (p) { + char *path; + char *tmp; + char *p; + + path = tmp = xstrdup(env_path); + while ((p = find_executable(*argv, &tmp)) != NULL) { + missing = 0; puts(p); free(p); - continue; + if (!option_mask32) /* -a not set */ + break; } + free(path); } - status = EXIT_FAILURE; -#endif - } while (*(++argv) != NULL); + status |= missing; + } while (*++argv); - fflush_stdout_and_exit(status); + return status; } -- cgit v1.2.3-55-g6feb From d257608a8429b64e1a04c7cb6d99975eeb2c3955 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 3 May 2014 16:35:15 +0200 Subject: trylink: emit names of linked executables Signed-off-by: Denys Vlasenko --- scripts/trylink | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/trylink b/scripts/trylink index 85095235c..5da494fbb 100755 --- a/scripts/trylink +++ b/scripts/trylink @@ -300,6 +300,8 @@ int main(int argc, char **argv) } rm -- "$sharedlib_dir/applet.c" $EXE.out $STRIP -s --remove-section=.note --remove-section=.comment $EXE + # Let user see that we do something - list the names of created binaries: + echo "$EXE" done