From b9965077e51bb9952c4fd161164e796e5fc66fbf Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 24 Dec 2014 02:06:02 +0100 Subject: Start 1.24.0 development cycle Signed-off-by: Denys Vlasenko --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index b487f0457..cd42ca37b 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 1 -PATCHLEVEL = 23 +PATCHLEVEL = 24 SUBLEVEL = 0 -EXTRAVERSION = +EXTRAVERSION = .git NAME = Unnamed # *DOCUMENTATION* -- cgit v1.2.3-55-g6feb From 8074a6ca4dc4e30e38570b52529b87c547c41b1b Mon Sep 17 00:00:00 2001 From: John Schroeder Date: Thu, 25 Dec 2014 21:12:51 +0100 Subject: udhcpd: account for script delay in lease Prevent nasty surprises if script runs longer than lease time / 2. Signed-off-by: John Schroeder Signed-off-by: Denys Vlasenko --- networking/udhcp/dhcpc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index a34829c3a..9d3d1a31c 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -1752,7 +1752,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) } #endif /* enter bound state */ - timeout = lease_seconds / 2; temp_addr.s_addr = packet.yiaddr; bb_info_msg("Lease of %s obtained, lease time %u", inet_ntoa(temp_addr), (unsigned)lease_seconds); @@ -1761,6 +1760,11 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) start = monotonic_sec(); udhcp_run_script(&packet, state == REQUESTING ? "bound" : "renew"); already_waited_sec = (unsigned)monotonic_sec() - start; + timeout = lease_seconds / 2; + if ((unsigned)timeout < already_waited_sec) { + /* Something went wrong. Back to discover state */ + timeout = already_waited_sec = 0; + } state = BOUND; change_listen_mode(LISTEN_NONE); -- cgit v1.2.3-55-g6feb From c76236fd7d980822a647914c9ff6a3fabdec2390 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 29 Dec 2014 00:04:18 +0100 Subject: ash: fix a SEGV in ${#1} function old new delta varvalue 760 805 +45 evalvar 648 603 -45 Signed-off-by: Denys Vlasenko --- shell/ash.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 90fb00fbd..c5ad96909 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -6746,6 +6746,14 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) len = strlen(p); if (!(subtype == VSPLUS || subtype == VSLENGTH)) memtodest(p, len, syntax, quotes); +#if ENABLE_UNICODE_SUPPORT + if (subtype == VSLENGTH && len > 0) { + reinit_unicode_for_ash(); + if (unicode_status == UNICODE_ON) { + len = unicode_strlen(p); + } + } +#endif return len; } @@ -6829,15 +6837,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) varunset(p, var, 0, 0); if (subtype == VSLENGTH) { - ssize_t n = varlen; - if (n > 0) { - reinit_unicode_for_ash(); - if (unicode_status == UNICODE_ON) { - const char *val = lookupvar(var); - n = unicode_strlen(val); - } - } - cvtnum(n > 0 ? n : 0); + cvtnum(varlen > 0 ? varlen : 0); goto record; } -- cgit v1.2.3-55-g6feb From 6c19d35f689c911b83073ab1faeffb8040c4b3d9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 17 Dec 2014 17:58:23 +0100 Subject: Makefile: fix cscope target This target doesn't work with current directory layout. Just make cscope index all .c and .h files. While we're at it: add cscope output files to .gitignore. Signed-off-by: Bartosz Golaszewski Signed-off-by: Denys Vlasenko --- .gitignore | 8 ++++++++ Makefile | 19 +------------------ 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 73e88fb5b..be1d46199 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,11 @@ core /busybox.links /runtest-tempdir-links /testsuite/echo-ne + +# +# cscope output +# +cscope.files +cscope.in.out +cscope.out +cscope.po.out diff --git a/Makefile b/Makefile index cd42ca37b..e0888578f 100644 --- a/Makefile +++ b/Makefile @@ -1165,24 +1165,7 @@ endif ALLSOURCE_ARCHS := $(ARCH) define all-sources - ( find $(__srctree) $(RCS_FIND_IGNORE) \ - \( -name include -o -name arch \) -prune -o \ - -name '*.[chS]' -print; \ - for ARCH in $(ALLSOURCE_ARCHS) ; do \ - find $(__srctree)arch/$${ARCH} $(RCS_FIND_IGNORE) \ - -name '*.[chS]' -print; \ - done ; \ - find $(__srctree)security/selinux/include $(RCS_FIND_IGNORE) \ - -name '*.[chS]' -print; \ - find $(__srctree)include $(RCS_FIND_IGNORE) \ - \( -name config -o -name 'asm-*' \) -prune \ - -o -name '*.[chS]' -print; \ - for ARCH in $(ALLINCLUDE_ARCHS) ; do \ - find $(__srctree)include/asm-$${ARCH} $(RCS_FIND_IGNORE) \ - -name '*.[chS]' -print; \ - done ; \ - find $(__srctree)include/asm-generic $(RCS_FIND_IGNORE) \ - -name '*.[chS]' -print ) + ( find -regex '.*\.[ch]$$' ) endef quiet_cmd_cscope-file = FILELST cscope.files -- cgit v1.2.3-55-g6feb From 78854520ebecfd24d5c80a266d6779bd1e069016 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 1 Jan 2015 19:02:40 +0100 Subject: modprobe: revert checking for /, stop doing basename() on modprobe args function old new delta process_module 726 719 -7 filename2modname 81 67 -14 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-21) Total: -21 bytes Signed-off-by: Denys Vlasenko --- modutils/modprobe-small.c | 8 ++++++-- modutils/modprobe.c | 11 ----------- modutils/modutils.c | 8 ++++++-- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c index b7990bff1..dafe91ed7 100644 --- a/modutils/modprobe-small.c +++ b/modutils/modprobe-small.c @@ -149,9 +149,13 @@ static void replace(char *s, char what, char with) static char *filename2modname(const char *filename, char *modname) { int i; - char *from; + const char *from; - from = bb_get_last_path_component_nostrip(filename); + // Disabled since otherwise "modprobe dir/name" would work + // as if it is "modprobe name". It is unclear why + // 'basenamization' was here in the first place. + //from = bb_get_last_path_component_nostrip(filename); + from = filename; for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++) modname[i] = (from[i] == '-') ? '_' : from[i]; modname[i] = '\0'; diff --git a/modutils/modprobe.c b/modutils/modprobe.c index f0904285b..f08f0850d 100644 --- a/modutils/modprobe.c +++ b/modutils/modprobe.c @@ -238,17 +238,6 @@ static void add_probe(const char *name) { struct module_entry *m; - /* - * get_or_add_modentry() strips path from name and works - * on remaining basename. - * This would make "rmmod dir/name" and "modprobe dir/name" - * to work like "rmmod name" and "modprobe name", - * which is wrong, and can be abused via implicit modprobing: - * "ifconfig /usbserial up" tries to modprobe netdev-/usbserial. - */ - if (strchr(name, '/')) - bb_error_msg_and_die("malformed module name '%s'", name); - m = get_or_add_modentry(name); if (!(option_mask32 & (OPT_REMOVE | OPT_SHOW_DEPS)) && (m->flags & MODULE_FLAG_LOADED) diff --git a/modutils/modutils.c b/modutils/modutils.c index 6187ca72f..ff79d3fac 100644 --- a/modutils/modutils.c +++ b/modutils/modutils.c @@ -48,13 +48,17 @@ int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim) char* FAST_FUNC filename2modname(const char *filename, char *modname) { int i; - char *from; + const char *from; if (filename == NULL) return NULL; if (modname == NULL) modname = xmalloc(MODULE_NAME_LEN); - from = bb_get_last_path_component_nostrip(filename); + // Disabled since otherwise "modprobe dir/name" would work + // as if it is "modprobe name". It is unclear why + // 'basenamization' was here in the first place. + //from = bb_get_last_path_component_nostrip(filename); + from = filename; for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++) modname[i] = (from[i] == '-') ? '_' : from[i]; modname[i] = '\0'; -- cgit v1.2.3-55-g6feb From 1da09cfacf1c4789cc74322857a098c2ddb06e31 Mon Sep 17 00:00:00 2001 From: Tito Ragusa Date: Fri, 2 Jan 2015 21:37:59 +0100 Subject: libpwdgrp: rewritten to use malloced implementation This removed buffer size limitations. function old new delta convert_to_struct - 269 +269 getXXnam_r - 204 +204 parse_common - 185 +185 getXXnam - 164 +164 tokenize - 126 +126 bb_internal_getpwent_r 102 167 +65 get_S 30 88 +58 getgrouplist_internal 195 240 +45 const_sp_db - 20 +20 const_pw_db - 20 +20 const_gr_db - 20 +20 bb_internal_endpwent 27 36 +9 bb_internal_endgrent 27 36 +9 decode_one_format 726 734 +8 bb_internal_setpwent 17 24 +7 volume_id_probe_iso9660 319 322 +3 scriptreplay_main 204 207 +3 mkfs_minix_main 2684 2687 +3 id_main 478 480 +2 hash_find 233 235 +2 pstree_main 321 322 +1 gr_off 3 4 +1 expand_one_var 1579 1578 -1 pwf 4 - -4 grf 4 - -4 pack_gzip 1787 1780 -7 addattr32 67 56 -11 buffer_fill_and_print 191 178 -13 dpkg_main 2944 2927 -17 bb_internal_setgrent 17 - -17 bb_internal_getpwuid 38 19 -19 bb_internal_getgrgid 44 19 -25 bb_internal_getpwnam 38 11 -27 bb_internal_getgrnam 44 14 -30 bb_internal_fgetpwent_r 51 - -51 bb_internal_fgetgrent_r 51 - -51 bb_internal_getspnam_r 121 42 -79 bb_internal_getpwnam_r 121 39 -82 bb_internal_getgrent_r 102 - -102 bb__parsepwent 110 - -110 bb_internal_getpwuid_r 113 - -113 bb_internal_getgrgid_r 113 - -113 bb__parsespent 120 - -120 bb_internal_getgrnam_r 121 - -121 bb__pgsreader 213 - -213 bb__parsegrent 226 - -226 ------------------------------------------------------------------------------ (add/remove: 8/13 grow/shrink: 14/11 up/down: 1224/-1556) Total: -332 bytes text data bss dec hex filename 923471 928 17684 942083 e6003 busybox_old 923167 928 17676 941771 e5ecb busybox_unstripped Signed-off-by: Tito Ragusa Signed-off-by: Denys Vlasenko Signed-off-by: Denys Vlasenko --- include/grp_.h | 54 +- include/pwd_.h | 25 - libbb/bb_pwd.c | 48 -- libpwdgrp/pwd_grp.c | 1238 ++++++++++++------------------------------ libpwdgrp/pwd_grp_internal.c | 61 --- 5 files changed, 359 insertions(+), 1067 deletions(-) delete mode 100644 libpwdgrp/pwd_grp_internal.c diff --git a/include/grp_.h b/include/grp_.h index e5075e5a0..f7b8d836f 100644 --- a/include/grp_.h +++ b/include/grp_.h @@ -30,17 +30,9 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN * so that function calls are directed to bb_internal_XXX replacements */ #undef endgrent -#define setgrent bb_internal_setgrent #define endgrent bb_internal_endgrent -#define getgrent bb_internal_getgrent -#define fgetgrent bb_internal_fgetgrent -#define putgrent bb_internal_putgrent #define getgrgid bb_internal_getgrgid #define getgrnam bb_internal_getgrnam -#define getgrent_r bb_internal_getgrent_r -#define getgrgid_r bb_internal_getgrgid_r -#define getgrnam_r bb_internal_getgrnam_r -#define fgetgrent_r bb_internal_fgetgrent_r #define getgrouplist bb_internal_getgrouplist #define initgroups bb_internal_initgroups @@ -48,60 +40,16 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* All function names below should be remapped by #defines above * in order to not collide with libc names. */ - -/* Rewind the group-file stream. */ -extern void setgrent(void); - /* Close the group-file stream. */ extern void endgrent(void); -#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS -/* Read an entry from the group-file stream, opening it if necessary. */ -extern struct group *getgrent(void); - -/* Read a group entry from STREAM. */ -extern struct group *fgetgrent(FILE *__stream); - -/* Write the given entry onto the given stream. */ -extern int putgrent(const struct group *__restrict __p, - FILE *__restrict __f); -#endif - /* Search for an entry with a matching group ID. */ extern struct group *getgrgid(gid_t __gid); /* Search for an entry with a matching group name. */ extern struct group *getgrnam(const char *__name); -/* Reentrant versions of some of the functions above. - - PLEASE NOTE: the `getgrent_r' function is not (yet) standardized. - The interface may change in later versions of this library. But - the interface is designed following the principals used for the - other reentrant functions so the chances are good this is what the - POSIX people would choose. */ - -extern int getgrent_r(struct group *__restrict __resultbuf, - char *__restrict __buffer, size_t __buflen, - struct group **__restrict __result); - -/* Search for an entry with a matching group ID. */ -extern int getgrgid_r(gid_t __gid, struct group *__restrict __resultbuf, - char *__restrict __buffer, size_t __buflen, - struct group **__restrict __result); - -/* Search for an entry with a matching group name. */ -extern int getgrnam_r(const char *__restrict __name, - struct group *__restrict __resultbuf, - char *__restrict __buffer, size_t __buflen, - struct group **__restrict __result); - -/* Read a group entry from STREAM. This function is not standardized - an probably never will. */ -extern int fgetgrent_r(FILE *__restrict __stream, - struct group *__restrict __resultbuf, - char *__restrict __buffer, size_t __buflen, - struct group **__restrict __result); +/* Reentrant versions of some of the functions above. */ /* Store at most *NGROUPS members of the group set for USER into *GROUPS. Also include GROUP. The actual number of groups found is diff --git a/include/pwd_.h b/include/pwd_.h index 625b6f5a2..d086f86e3 100644 --- a/include/pwd_.h +++ b/include/pwd_.h @@ -34,20 +34,14 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN #define setpwent bb_internal_setpwent #define endpwent bb_internal_endpwent #define getpwent bb_internal_getpwent -#define fgetpwent bb_internal_fgetpwent -#define putpwent bb_internal_putpwent #define getpwuid bb_internal_getpwuid #define getpwnam bb_internal_getpwnam #define getpwent_r bb_internal_getpwent_r -#define getpwuid_r bb_internal_getpwuid_r #define getpwnam_r bb_internal_getpwnam_r -#define fgetpwent_r bb_internal_fgetpwent_r - /* All function names below should be remapped by #defines above * in order to not collide with libc names. */ - /* Rewind the password-file stream. */ extern void setpwent(void); @@ -57,13 +51,6 @@ extern void endpwent(void); #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS /* Read an entry from the password-file stream, opening it if necessary. */ extern struct passwd *getpwent(void); - -/* Read an entry from STREAM. */ -extern struct passwd *fgetpwent(FILE *__stream); - -/* Write the given entry onto the given stream. */ -extern int putpwent(const struct passwd *__restrict __p, - FILE *__restrict __f); #endif /* Search for an entry with a matching user ID. */ @@ -84,23 +71,11 @@ extern int getpwent_r(struct passwd *__restrict __resultbuf, char *__restrict __buffer, size_t __buflen, struct passwd **__restrict __result); -extern int getpwuid_r(uid_t __uid, - struct passwd *__restrict __resultbuf, - char *__restrict __buffer, size_t __buflen, - struct passwd **__restrict __result); - extern int getpwnam_r(const char *__restrict __name, struct passwd *__restrict __resultbuf, char *__restrict __buffer, size_t __buflen, struct passwd **__restrict __result); -/* Read an entry from STREAM. This function is not standardized and - probably never will. */ -extern int fgetpwent_r(FILE *__restrict __stream, - struct passwd *__restrict __resultbuf, - char *__restrict __buffer, size_t __buflen, - struct passwd **__restrict __result); - POP_SAVED_FUNCTION_VISIBILITY #endif diff --git a/libbb/bb_pwd.c b/libbb/bb_pwd.c index 8250cd446..4829b723a 100644 --- a/libbb/bb_pwd.c +++ b/libbb/bb_pwd.c @@ -110,51 +110,3 @@ unsigned long FAST_FUNC get_ug_id(const char *s, return xname2id(s); return r; } - -/* Experimental "mallocing" API. - * The goal is nice: "we want to support a case when "guests" group is very large" - * but the code is butt-ugly. - */ -#if 0 -static char *find_latest(char last, char *cp) -{ - if (!cp) - return last; - cp += strlen(cp) + 1; - if (last < cp) - last = cp; - return last; -} - -struct group* FAST_FUNC xmalloc_getgrnam(const char *name) -{ - struct { - struct group gr; - // May still be not enough! - char buf[64*1024 - sizeof(struct group) - 16]; - } *s; - struct group *grp; - int r; - char *last; - char **gr_mem; - - s = xmalloc(sizeof(*s)); - r = getgrnam_r(name, &s->gr, s->buf, sizeof(s->buf), &grp); - if (!grp) { - free(s); - return grp; - } - last = find_latest(s->buf, grp->gr_name); - last = find_latest(last, grp->gr_passwd); - gr_mem = grp->gr_mem; - while (*gr_mem) - last = find_latest(last, *gr_mem++); - gr_mem++; /* points past NULL */ - if (last < (char*)gr_mem) - last = (char*)gr_mem; -//FIXME: what if we get not only truncated, but also moved here? -// grp->gr_name pointer and friends are invalid now!!! - s = xrealloc(s, last - (char*)s); - return grp; -} -#endif diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 2060d7811..ed8370124 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -1,598 +1,480 @@ /* vi: set sw=4 ts=4: */ -/* Copyright (C) 2003 Manuel Novoa III +/* Copyright (C) 2014 Tito Ragusa * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ - -/* Nov 6, 2003 Initial version. +/* This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY!! * - * NOTE: This implementation is quite strict about requiring all - * field seperators. It also does not allow leading whitespace - * except when processing the numeric fields. glibc is more - * lenient. See the various glibc difference comments below. + * Rewrite of some parts. Main differences are: * - * TODO: - * Move to dynamic allocation of (currently statically allocated) - * buffers; especially for the group-related functions since - * large group member lists will cause error returns. + * 1) the buffer for getpwuid, getgrgid, getpwnam, getgrnam is dynamically + * allocated and reused by later calls. if ERANGE error pops up it is + * reallocated to the size of the longest line found so far in the + * passwd/group files and reused for later calls. + * If ENABLE_FEATURE_CLEAN_UP is set the buffers are freed at program + * exit using the atexit function to make valgrind happy. + * 2) the passwd/group files: + * a) must contain the expected number of fields (as per count of field + * delimeters ":") or we will complain with a error message. + * b) leading or trailing whitespace in fields is allowed and handled. + * c) some fields are not allowed to be empty (e.g. username, uid/gid, + * homedir, shell) and in this case NULL is returned and errno is + * set to EINVAL. This behaviour could be easily changed by + * modifying PW_DEF, GR_DEF, SP_DEF strings (uppercase + * makes a field mandatory). + * d) the string representing uid/gid must be convertible by strtoXX + * functions or NULL is returned and errno is set to EINVAL. + * e) leading or trailing whitespaces in member names and empty members + * are allowed and handled. + * 3) the internal function for getgrouplist uses a dynamically allocated + * buffer and retries with a bigger one in case it is too small; + * 4) the _r functions use the user supplied buffers that are never reallocated + * but use mostly the same common code as the other functions. + * 5) at the moment only the functions really used by busybox code are + * implemented, if you need a particular missing function it should be + * easy to write it by using the internal common code. */ #include "libbb.h" -#include - -/**********************************************************************/ -/* Sizes for statically allocated buffers. */ -#define PWD_BUFFER_SIZE 256 -#define GRP_BUFFER_SIZE 256 - -/**********************************************************************/ -/* Prototypes for internal functions. */ +/* S = string not empty, s = string maybe empty, */ +/* I = uid,gid, l = long maybe empty, m = members,*/ +/* r = reserved */ +#define PW_DEF "SsIIsSS" +#define GR_DEF "SsIm" +#define SP_DEF "Ssllllllr" + +static const uint8_t pw_off[] ALIGN1 = { + offsetof(struct passwd, pw_name), /* 0 S */ + offsetof(struct passwd, pw_passwd), /* 1 s */ + offsetof(struct passwd, pw_uid), /* 2 I */ + offsetof(struct passwd, pw_gid), /* 3 I */ + offsetof(struct passwd, pw_gecos), /* 4 s */ + offsetof(struct passwd, pw_dir), /* 5 S */ + offsetof(struct passwd, pw_shell) /* 6 S */ +}; +static const uint8_t gr_off[] ALIGN1 = { + offsetof(struct group, gr_name), /* 0 S */ + offsetof(struct group, gr_passwd), /* 1 s */ + offsetof(struct group, gr_gid), /* 2 I */ + offsetof(struct group, gr_mem) /* 3 m (char **) */ +}; +#if ENABLE_USE_BB_SHADOW +static const uint8_t sp_off[] ALIGN1 = { + offsetof(struct spwd, sp_namp), /* 0 S Login name */ + offsetof(struct spwd, sp_pwdp), /* 1 s Encrypted password */ + offsetof(struct spwd, sp_lstchg), /* 2 l */ + offsetof(struct spwd, sp_min), /* 3 l */ + offsetof(struct spwd, sp_max), /* 4 l */ + offsetof(struct spwd, sp_warn), /* 5 l */ + offsetof(struct spwd, sp_inact), /* 6 l */ + offsetof(struct spwd, sp_expire), /* 7 l */ + offsetof(struct spwd, sp_flag) /* 8 r Reserved */ +}; +#endif -static int bb__pgsreader( - int FAST_FUNC (*parserfunc)(void *d, char *line), - void *data, - char *__restrict line_buff, - size_t buflen, - FILE *f); +struct const_passdb { + const char *filename; + const uint8_t *off; + const char def[10]; + uint8_t numfields; + uint8_t size_of; +}; +struct passdb { + const char *filename; + const uint8_t *off; + const char def[10]; + uint8_t numfields; + uint8_t size_of; + FILE *fp; + void *malloced; +}; -static int FAST_FUNC bb__parsepwent(void *pw, char *line); -static int FAST_FUNC bb__parsegrent(void *gr, char *line); +static const struct const_passdb const_pw_db = { _PATH_PASSWD, pw_off, PW_DEF, sizeof(PW_DEF)-1, sizeof(struct passwd) }; +static const struct const_passdb const_gr_db = { _PATH_GROUP , gr_off, GR_DEF, sizeof(GR_DEF)-1, sizeof(struct group) }; #if ENABLE_USE_BB_SHADOW -static int FAST_FUNC bb__parsespent(void *sp, char *line); +static const struct const_passdb const_sp_db = { _PATH_SHADOW, sp_off, SP_DEF, sizeof(SP_DEF)-1, sizeof(struct spwd) }; #endif -/**********************************************************************/ /* We avoid having big global data. */ - struct statics { - /* Smaller things first */ /* It's ok to use one buffer for getpwuid and getpwnam. Manpage says: * "The return value may point to a static area, and may be overwritten * by subsequent calls to getpwent(), getpwnam(), or getpwuid()." */ - struct passwd getpw_resultbuf; - struct group getgr_resultbuf; - - char getpw_buffer[PWD_BUFFER_SIZE]; - char getgr_buffer[GRP_BUFFER_SIZE]; -#if 0 //ENABLE_USE_BB_SHADOW - struct spwd getsp_resultbuf; - char getsp_buffer[PWD_BUFFER_SIZE]; -#endif -// Not converted - too small to bother -//pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER; -//FILE *pwf /*= NULL*/; -//FILE *grf /*= NULL*/; -//FILE *spf /*= NULL*/; + struct passdb db[2 + ENABLE_USE_BB_SHADOW]; + char *tokenize_end; }; static struct statics *ptr_to_statics; +#define S (*ptr_to_statics) +#define has_S (ptr_to_statics) static struct statics *get_S(void) { - if (!ptr_to_statics) - ptr_to_statics = xzalloc(sizeof(*ptr_to_statics)); + if (!ptr_to_statics) { + ptr_to_statics = xzalloc(sizeof(S)); + memcpy(&S.db[0], &const_pw_db, sizeof(const_pw_db)); + memcpy(&S.db[1], &const_gr_db, sizeof(const_gr_db)); +#if ENABLE_USE_BB_SHADOW + memcpy(&S.db[2], &const_sp_db, sizeof(const_sp_db)); +#endif + } return ptr_to_statics; } -/* Always use in this order, get_S() must be called first */ -#define RESULTBUF(name) &((S = get_S())->name##_resultbuf) -#define BUFFER(name) (S->name##_buffer) - /**********************************************************************/ -/* For the various fget??ent_r funcs, return - * - * 0: success - * ENOENT: end-of-file encountered - * ERANGE: buflen too small - * other error values possible. See bb__pgsreader. - * - * Also, *result == resultbuf on success and NULL on failure. - * - * NOTE: glibc difference - For the ENOENT case, glibc also sets errno. - * We do not, as it really isn't an error if we reach the end-of-file. - * Doing so is analogous to having fgetc() set errno on EOF. - */ +/* Internal functions */ /**********************************************************************/ -int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf, - char *__restrict buffer, size_t buflen, - struct passwd **__restrict result) +/* Divide the passwd/group/shadow record in fields + * by substituting the given delimeter + * e.g. ':' or ',' with '\0'. + * Returns the number of fields found. + * Strips leading and trailing whitespace in fields. + */ +static int tokenize(char *buffer, int ch) { - int rv; + char *p = buffer; + char *s = p; + int num_fields = 0; - *result = NULL; - - rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, stream); - if (!rv) { - *result = resultbuf; + for (;;) { + if (isblank(*s)) { + overlapping_strcpy(s, skip_whitespace(s)); + } + if (*p == ch || *p == '\0') { + char *end = p; + while (p != s && isblank(p[-1])) + p--; + if (p != end) + overlapping_strcpy(p, end); + num_fields++; + if (*end == '\0') { + S.tokenize_end = p + 1; + return num_fields; + } + *p = '\0'; + s = p + 1; + } + p++; } - - return rv; } -int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf, - char *__restrict buffer, size_t buflen, - struct group **__restrict result) -{ - int rv; +/* Returns !NULL on success and matching line broken up in fields by '\0' in buf. + * We require the expected number of fields to be found. + */ +static char *parse_common(FILE *fp, const char *filename, + int n_fields, + const char *key, int field_pos) +{ + int count = 0; + char *buf; + + while ((buf = xmalloc_fgetline(fp)) != NULL) { + count++; + /* Skip empty lines, comment lines */ + if (buf[0] == '\0' || buf[0] == '#') + goto free_and_next; + if (tokenize(buf, ':') != n_fields) { + /* number of fields is wrong */ + bb_error_msg("bad record at %s:%u", filename, count); + goto free_and_next; + } - *result = NULL; +/* Ugly hack: group db requires aqdditional buffer space + * for members[] array. If there is only one group, we need space + * for 3 pointers: alignment padding, group name, NULL. + * +1 for every additional group. + */ + if (n_fields == sizeof(GR_DEF)-1) { /* if we read group file */ + int resize = 3; + char *p = buf; + while (*p) + if (*p++ == ',') + resize++; + resize *= sizeof(char**); + resize += S.tokenize_end - buf; + buf = xrealloc(buf, resize); + } - rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, stream); - if (!rv) { - *result = resultbuf; + if (!key) { + /* no key specified: sequential read, return a record */ + break; + } + if (strcmp(key, nth_string(buf, field_pos)) == 0) { + /* record found */ + break; + } + free_and_next: + free(buf); } - return rv; + return buf; } -#if ENABLE_USE_BB_SHADOW -#ifdef UNUSED_FOR_NOW -int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf, - char *__restrict buffer, size_t buflen, - struct spwd **__restrict result) +static char *parse_file(const char *filename, + int n_fields, + const char *key, int field_pos) { - int rv; + char *buf = NULL; + FILE *fp = fopen_for_read(filename); - *result = NULL; - - rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, stream); - if (!rv) { - *result = resultbuf; + if (fp) { + buf = parse_common(fp, filename, n_fields, key, field_pos); + fclose(fp); } - - return rv; -} -#endif -#endif - -/**********************************************************************/ -/* For the various fget??ent funcs, return NULL on failure and a - * pointer to the appropriate struct (statically allocated) on success. - * TODO: audit & stop using these in bbox, they pull in static buffers */ -/**********************************************************************/ - -#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS -struct passwd *fgetpwent(FILE *stream) -{ - struct statics *S; - struct passwd *resultbuf = RESULTBUF(getpw); - char *buffer = BUFFER(getpw); - struct passwd *result; - - fgetpwent_r(stream, resultbuf, buffer, sizeof(BUFFER(getpw)), &result); - return result; + return buf; } -struct group *fgetgrent(FILE *stream) +/* Convert passwd/group/shadow file record in buffer to a struct */ +static void *convert_to_struct(const char *def, const unsigned char *off, + char *buffer, void *result) { - struct statics *S; - struct group *resultbuf = RESULTBUF(getgr); - char *buffer = BUFFER(getgr); - struct group *result; - - fgetgrent_r(stream, resultbuf, buffer, sizeof(BUFFER(getgr)), &result); - return result; -} -#endif + for (;;) { + void *member = (char*)result + (*off++); + if ((*def | 0x20) == 's') { /* s or S */ + *(char **)member = (char*)buffer; + if (!buffer[0] && (*def == 'S')) { + errno = EINVAL; + } + } + if (*def == 'I') { + *(int *)member = bb_strtou(buffer, NULL, 10); + } #if ENABLE_USE_BB_SHADOW -#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS -struct spwd *fgetspent(FILE *stream) -{ - struct statics *S; - struct spwd *resultbuf = RESULTBUF(getsp); - char *buffer = BUFFER(getsp); - struct spwd *result; - - fgetspent_r(stream, resultbuf, buffer, sizeof(BUFFER(getsp)), &result); - return result; -} + if (*def == 'l') { + long n = -1; + if (buffer[0]) + n = bb_strtol(buffer, NULL, 10); + *(long *)member = n; + } #endif - -#ifdef UNUSED_FOR_NOW -int sgetspent_r(const char *string, struct spwd *result_buf, - char *buffer, size_t buflen, struct spwd **result) -{ - int rv = ERANGE; - - *result = NULL; - - if (buflen < PWD_BUFFER_SIZE) { - DO_ERANGE: - errno = rv; - goto DONE; - } - - if (string != buffer) { - if (strlen(string) >= buflen) { - goto DO_ERANGE; + if (*def == 'm') { + char **members; + int i = tokenize(buffer, ','); + + /* Store members[] after buffer's end. + * This is safe ONLY because there is a hack + * in parse_common() which allocates additional space + * at the end of malloced buffer! + */ + members = (char **) + ( ((intptr_t)S.tokenize_end + sizeof(char**)) + & -(intptr_t)sizeof(char**) + ); + + ((struct group *)result)->gr_mem = members; + while (--i >= 0) { + *members++ = buffer; + buffer += strlen(buffer) + 1; + } + *members = NULL; } - strcpy(buffer, string); - } + /* def "r" does nothing */ - rv = bb__parsespent(result_buf, buffer); - if (!rv) { - *result = result_buf; + def++; + if (*def == '\0') + break; + buffer += strlen(buffer) + 1; } - DONE: - return rv; -} -#endif -#endif /* ENABLE_USE_BB_SHADOW */ - -/**********************************************************************/ - -#define GETXXKEY_R_FUNC getpwnam_r -#define GETXXKEY_R_PARSER bb__parsepwent -#define GETXXKEY_R_ENTTYPE struct passwd -#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->pw_name, key)) -#define GETXXKEY_R_KEYTYPE const char *__restrict -#define GETXXKEY_R_PATHNAME _PATH_PASSWD -#include "pwd_grp_internal.c" - -#define GETXXKEY_R_FUNC getgrnam_r -#define GETXXKEY_R_PARSER bb__parsegrent -#define GETXXKEY_R_ENTTYPE struct group -#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->gr_name, key)) -#define GETXXKEY_R_KEYTYPE const char *__restrict -#define GETXXKEY_R_PATHNAME _PATH_GROUP -#include "pwd_grp_internal.c" - -#if ENABLE_USE_BB_SHADOW -#define GETXXKEY_R_FUNC getspnam_r -#define GETXXKEY_R_PARSER bb__parsespent -#define GETXXKEY_R_ENTTYPE struct spwd -#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->sp_namp, key)) -#define GETXXKEY_R_KEYTYPE const char *__restrict -#define GETXXKEY_R_PATHNAME _PATH_SHADOW -#include "pwd_grp_internal.c" -#endif - -#define GETXXKEY_R_FUNC getpwuid_r -#define GETXXKEY_R_PARSER bb__parsepwent -#define GETXXKEY_R_ENTTYPE struct passwd -#define GETXXKEY_R_TEST(ENT) ((ENT)->pw_uid == key) -#define GETXXKEY_R_KEYTYPE uid_t -#define GETXXKEY_R_PATHNAME _PATH_PASSWD -#include "pwd_grp_internal.c" - -#define GETXXKEY_R_FUNC getgrgid_r -#define GETXXKEY_R_PARSER bb__parsegrent -#define GETXXKEY_R_ENTTYPE struct group -#define GETXXKEY_R_TEST(ENT) ((ENT)->gr_gid == key) -#define GETXXKEY_R_KEYTYPE gid_t -#define GETXXKEY_R_PATHNAME _PATH_GROUP -#include "pwd_grp_internal.c" - -/**********************************************************************/ -/* TODO: audit & stop using these in bbox, they pull in static buffers */ - -/* This one has many users */ -struct passwd *getpwuid(uid_t uid) -{ - struct statics *S; - struct passwd *resultbuf = RESULTBUF(getpw); - char *buffer = BUFFER(getpw); - struct passwd *result; - - getpwuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getpw)), &result); + if (errno) + result = NULL; return result; } -/* This one has many users */ -struct group *getgrgid(gid_t gid) -{ - struct statics *S; - struct group *resultbuf = RESULTBUF(getgr); - char *buffer = BUFFER(getgr); - struct group *result; +/****** getXXnam/id_r */ - getgrgid_r(gid, resultbuf, buffer, sizeof(BUFFER(getgr)), &result); - return result; -} - -#if 0 //ENABLE_USE_BB_SHADOW -/* This function is non-standard and is currently not built. It seems - * to have been created as a reentrant version of the non-standard - * functions getspuid. Why getspuid was added, I do not know. */ -int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf, - char *__restrict buffer, size_t buflen, - struct spwd **__restrict result) +static int getXXnam_r(const char *name, uintptr_t db_and_field_pos, char *buffer, size_t buflen, + void *result) { - int rv; - struct passwd *pp; - struct passwd password; - char pwd_buff[PWD_BUFFER_SIZE]; + void *struct_buf = *(void**)result; + char *buf; + struct passdb *db; + get_S(); + db = &S.db[db_and_field_pos >> 2]; - *result = NULL; - rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp); - if (!rv) { - rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result); + *(void**)result = NULL; + buf = parse_file(db->filename, db->numfields, name, db_and_field_pos & 3); + if (buf) { + size_t size = S.tokenize_end - buf; + if (size > buflen) { + errno = ERANGE; + } else { + memcpy(buffer, buf, size); + *(void**)result = convert_to_struct(db->def, db->off, buffer, struct_buf); + } + free(buf); } - - return rv; -} - -/* This function is non-standard and is currently not built. - * Why it was added, I do not know. */ -struct spwd *getspuid(uid_t uid) -{ - struct statics *S; - struct spwd *resultbuf = RESULTBUF(getsp); - char *buffer = BUFFER(getsp); - struct spwd *result; - - getspuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getsp)), &result); - return result; -} -#endif - -/* This one has many users */ -struct passwd *getpwnam(const char *name) -{ - struct statics *S; - struct passwd *resultbuf = RESULTBUF(getpw); - char *buffer = BUFFER(getpw); - struct passwd *result; - - getpwnam_r(name, resultbuf, buffer, sizeof(BUFFER(getpw)), &result); - return result; + /* "The reentrant functions return zero on success. + * In case of error, an error number is returned." + * NB: not finding the record is also a "success" here: + */ + return errno; } -/* This one has many users */ -struct group *getgrnam(const char *name) +int getpwnam_r(const char *name, struct passwd *struct_buf, char *buffer, size_t buflen, + struct passwd **result) { - struct statics *S; - struct group *resultbuf = RESULTBUF(getgr); - char *buffer = BUFFER(getgr); - struct group *result; - - getgrnam_r(name, resultbuf, buffer, sizeof(BUFFER(getgr)), &result); - return result; + /* Why the "store buffer address in result" trick? + * This way, getXXnam_r has the same ABI signature as getpwnam_r, + * hopefully compiler can optimize tall call better in this case. + */ + *result = struct_buf; + return getXXnam_r(name, (0 << 2) + 0, buffer, buflen, result); } - -#if 0 //ENABLE_USE_BB_SHADOW -struct spwd *getspnam(const char *name) +#if ENABLE_USE_BB_SHADOW +int getspnam_r(const char *name, struct spwd *struct_buf, char *buffer, size_t buflen, + struct spwd **result) { - struct statics *S; - struct spwd *resultbuf = RESULTBUF(getsp); - char *buffer = BUFFER(getsp); - struct spwd *result; - - getspnam_r(name, resultbuf, buffer, sizeof(BUFFER(getsp)), &result); - return result; + *result = struct_buf; + return getXXnam_r(name, (2 << 2) + 0, buffer, buflen, result); } #endif -/**********************************************************************/ - -/* FIXME: we don't have such CONFIG_xx - ?! */ - -#if defined CONFIG_USE_BB_THREADSAFE_SHADOW && defined PTHREAD_MUTEX_INITIALIZER -static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER; -# define LOCK pthread_mutex_lock(&mylock) -# define UNLOCK pthread_mutex_unlock(&mylock); -#else -# define LOCK ((void) 0) -# define UNLOCK ((void) 0) -#endif - -static FILE *pwf /*= NULL*/; -void setpwent(void) -{ - LOCK; - if (pwf) { - rewind(pwf); - } - UNLOCK; -} - -void endpwent(void) -{ - LOCK; - if (pwf) { - fclose(pwf); - pwf = NULL; - } - UNLOCK; -} - +/****** getXXent_r */ -int getpwent_r(struct passwd *__restrict resultbuf, - char *__restrict buffer, size_t buflen, - struct passwd **__restrict result) +static int getXXent_r(void *struct_buf, char *buffer, size_t buflen, + void *result, + unsigned db_idx) { - int rv; + char *buf; + struct passdb *db; + get_S(); + db = &S.db[db_idx]; - LOCK; - *result = NULL; /* In case of error... */ + *(void**)result = NULL; - if (!pwf) { - pwf = fopen_for_read(_PATH_PASSWD); - if (!pwf) { - rv = errno; - goto ERR; + if (!db->fp) { + db->fp = fopen_for_read(db->filename); + if (!db->fp) { + return errno; } - close_on_exec_on(fileno(pwf)); + close_on_exec_on(fileno(db->fp)); } - rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, pwf); - if (!rv) { - *result = resultbuf; + buf = parse_common(db->fp, db->filename, db->numfields, /*no search key:*/ NULL, 0); + if (buf) { + size_t size = S.tokenize_end - buf; + if (size > buflen) { + errno = ERANGE; + } else { + memcpy(buffer, buf, size); + *(void**)result = convert_to_struct(db->def, db->off, buffer, struct_buf); + } + free(buf); } - - ERR: - UNLOCK; - return rv; + /* "The reentrant functions return zero on success. + * In case of error, an error number is returned." + * NB: not finding the record is also a "success" here: + */ + return errno; } -static FILE *grf /*= NULL*/; -void setgrent(void) +int getpwent_r(struct passwd *struct_buf, char *buffer, size_t buflen, struct passwd **result) { - LOCK; - if (grf) { - rewind(grf); - } - UNLOCK; + return getXXent_r(struct_buf, buffer, buflen, result, 0); } -void endgrent(void) -{ - LOCK; - if (grf) { - fclose(grf); - grf = NULL; - } - UNLOCK; -} +/****** getXXnam/id */ -int getgrent_r(struct group *__restrict resultbuf, - char *__restrict buffer, size_t buflen, - struct group **__restrict result) +static void *getXXnam(const char *name, unsigned db_and_field_pos) { - int rv; + char *buf; + void *result; + struct passdb *db; + get_S(); + db = &S.db[db_and_field_pos >> 2]; - LOCK; - *result = NULL; /* In case of error... */ + result = NULL; - if (!grf) { - grf = fopen_for_read(_PATH_GROUP); - if (!grf) { - rv = errno; - goto ERR; + if (!db->fp) { + db->fp = fopen_for_read(db->filename); + if (!db->fp) { + return NULL; } - close_on_exec_on(fileno(grf)); + close_on_exec_on(fileno(db->fp)); } - rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, grf); - if (!rv) { - *result = resultbuf; + free(db->malloced); + db->malloced = NULL; + buf = parse_common(db->fp, db->filename, db->numfields, name, db_and_field_pos & 3); + if (buf) { + db->malloced = xzalloc(db->size_of); + result = convert_to_struct(db->def, db->off, buf, db->malloced); } - - ERR: - UNLOCK; - return rv; + return result; } -#ifdef UNUSED_FOR_NOW -#if ENABLE_USE_BB_SHADOW -static FILE *spf /*= NULL*/; -void setspent(void) +struct passwd *getpwnam(const char *name) { - LOCK; - if (spf) { - rewind(spf); - } - UNLOCK; + return getXXnam(name, (0 << 2) + 0); } - -void endspent(void) +struct group *getgrnam(const char *name) { - LOCK; - if (spf) { - fclose(spf); - spf = NULL; - } - UNLOCK; + return getXXnam(name, (1 << 2) + 0); } - -int getspent_r(struct spwd *resultbuf, char *buffer, - size_t buflen, struct spwd **result) +struct passwd *getpwuid(uid_t id) { - int rv; - - LOCK; - *result = NULL; /* In case of error... */ - - if (!spf) { - spf = fopen_for_read(_PATH_SHADOW); - if (!spf) { - rv = errno; - goto ERR; - } - close_on_exec_on(fileno(spf)); - } - - rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, spf); - if (!rv) { - *result = resultbuf; - } - - ERR: - UNLOCK; - return rv; + return getXXnam(utoa(id), (0 << 2) + 2); } -#endif -#endif /* UNUSED_FOR_NOW */ - -#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS -struct passwd *getpwent(void) +struct group *getgrgid(gid_t id) { - static char line_buff[PWD_BUFFER_SIZE]; - static struct passwd pwd; - struct passwd *result; - - getpwent_r(&pwd, line_buff, sizeof(line_buff), &result); - return result; + return getXXnam(utoa(id), (1 << 2) + 2); } -struct group *getgrent(void) -{ - static char line_buff[GRP_BUFFER_SIZE]; - static struct group gr; - struct group *result; +/****** end/setXXend */ - getgrent_r(&gr, line_buff, sizeof(line_buff), &result); - return result; +void endpwent(void) +{ + if (has_S && S.db[0].fp) { + fclose(S.db[0].fp); + S.db[0].fp = NULL; + } } - -#if ENABLE_USE_BB_SHADOW -struct spwd *getspent(void) +void setpwent(void) { - static char line_buff[PWD_BUFFER_SIZE]; - static struct spwd spwd; - struct spwd *result; - - getspent_r(&spwd, line_buff, sizeof(line_buff), &result); - return result; + if (has_S && S.db[0].fp) { + rewind(S.db[0].fp); + } } - -struct spwd *sgetspent(const char *string) +void endgrent(void) { - static char line_buff[PWD_BUFFER_SIZE]; - static struct spwd spwd; - struct spwd *result; - - sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result); - return result; + if (has_S && S.db[1].fp) { + fclose(S.db[1].fp); + S.db[1].fp = NULL; + } } -#endif -#endif /* UNUSED_SINCE_WE_AVOID_STATIC_BUFS */ -static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gid) +/****** initgroups and getgrouplist */ + +static gid_t* FAST_FUNC getgrouplist_internal(int *ngroups_ptr, + const char *user, gid_t gid) { - FILE *grfile; + FILE *fp; gid_t *group_list; int ngroups; - struct group group; - char buff[PWD_BUFFER_SIZE]; + + get_S(); /* We alloc space for 8 gids at a time. */ group_list = xmalloc(8 * sizeof(group_list[0])); group_list[0] = gid; ngroups = 1; - grfile = fopen_for_read(_PATH_GROUP); - if (grfile) { - while (!bb__pgsreader(bb__parsegrent, &group, buff, sizeof(buff), grfile)) { + fp = fopen_for_read(_PATH_GROUP); + if (fp) { + char *buf; + while ((buf = parse_common(fp, _PATH_GROUP, sizeof(GR_DEF)-1, NULL, 0)) != NULL) { char **m; - assert(group.gr_mem); /* Must have at least a NULL terminator. */ + struct group group; + if (!convert_to_struct(GR_DEF, gr_off, buf, &group)) + goto next; if (group.gr_gid == gid) - continue; + goto next; for (m = group.gr_mem; *m; m++) { if (strcmp(*m, user) != 0) continue; @@ -600,8 +482,10 @@ static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gi group_list[ngroups++] = group.gr_gid; break; } + next: + free(buf); } - fclose(grfile); + fclose(fp); } *ngroups_ptr = ngroups; return group_list; @@ -631,409 +515,3 @@ int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups) free(group_list); return ngroups_old; } - -#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS -int putpwent(const struct passwd *__restrict p, FILE *__restrict f) -{ - int rv = -1; - -#if 0 - /* glibc does this check */ - if (!p || !f) { - errno = EINVAL; - return rv; - } -#endif - - /* No extra thread locking is needed above what fprintf does. */ - if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n", - p->pw_name, p->pw_passwd, - (unsigned long)(p->pw_uid), - (unsigned long)(p->pw_gid), - p->pw_gecos, p->pw_dir, p->pw_shell) >= 0 - ) { - rv = 0; - } - - return rv; -} - -int putgrent(const struct group *__restrict p, FILE *__restrict f) -{ - int rv = -1; - -#if 0 - /* glibc does this check */ - if (!p || !f) { - errno = EINVAL; - return rv; - } -#endif - - if (fprintf(f, "%s:%s:%lu:", - p->gr_name, p->gr_passwd, - (unsigned long)(p->gr_gid)) >= 0 - ) { - static const char format[] ALIGN1 = ",%s"; - - char **m; - const char *fmt; - - fmt = format + 1; - - assert(p->gr_mem); - m = p->gr_mem; - - while (1) { - if (!*m) { - if (fputc('\n', f) >= 0) { - rv = 0; - } - break; - } - if (fprintf(f, fmt, *m) < 0) { - break; - } - m++; - fmt = format; - } - } - - return rv; -} -#endif - -#if ENABLE_USE_BB_SHADOW -#ifdef UNUSED_FOR_NOW -static const unsigned char put_sp_off[] ALIGN1 = { - offsetof(struct spwd, sp_lstchg), /* 2 - not a char ptr */ - offsetof(struct spwd, sp_min), /* 3 - not a char ptr */ - offsetof(struct spwd, sp_max), /* 4 - not a char ptr */ - offsetof(struct spwd, sp_warn), /* 5 - not a char ptr */ - offsetof(struct spwd, sp_inact), /* 6 - not a char ptr */ - offsetof(struct spwd, sp_expire) /* 7 - not a char ptr */ -}; - -int putspent(const struct spwd *p, FILE *stream) -{ - const char *fmt; - long x; - int i; - int rv = -1; - - /* Unlike putpwent and putgrent, glibc does not check the args. */ - if (fprintf(stream, "%s:%s:", p->sp_namp, - (p->sp_pwdp ? p->sp_pwdp : "")) < 0 - ) { - goto DO_UNLOCK; - } - - for (i = 0; i < sizeof(put_sp_off); i++) { - fmt = "%ld:"; - x = *(long *)((char *)p + put_sp_off[i]); - if (x == -1) { - fmt += 3; - } - if (fprintf(stream, fmt, x) < 0) { - goto DO_UNLOCK; - } - } - - if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) { - goto DO_UNLOCK; - } - - if (fputc('\n', stream) > 0) { - rv = 0; - } - - DO_UNLOCK: - return rv; -} -#endif -#endif /* USE_BB_SHADOW */ - -/**********************************************************************/ -/* Internal functions */ -/**********************************************************************/ - -static const unsigned char pw_off[] ALIGN1 = { - offsetof(struct passwd, pw_name), /* 0 */ - offsetof(struct passwd, pw_passwd), /* 1 */ - offsetof(struct passwd, pw_uid), /* 2 - not a char ptr */ - offsetof(struct passwd, pw_gid), /* 3 - not a char ptr */ - offsetof(struct passwd, pw_gecos), /* 4 */ - offsetof(struct passwd, pw_dir), /* 5 */ - offsetof(struct passwd, pw_shell) /* 6 */ -}; - -static int FAST_FUNC bb__parsepwent(void *data, char *line) -{ - char *endptr; - char *p; - int i; - - i = 0; - while (1) { - p = (char *) data + pw_off[i]; - - if (i < 2 || i > 3) { - *((char **) p) = line; - if (i == 6) { - return 0; - } - /* NOTE: glibc difference - glibc allows omission of - * ':' seperators after the gid field if all remaining - * entries are empty. We require all separators. */ - line = strchr(line, ':'); - if (!line) { - break; - } - } else { - unsigned long t = strtoul(line, &endptr, 10); - /* Make sure we had at least one digit, and that the - * failing char is the next field seperator ':'. See - * glibc difference note above. */ - /* TODO: Also check for leading whitespace? */ - if ((endptr == line) || (*endptr != ':')) { - break; - } - line = endptr; - if (i & 1) { /* i == 3 -- gid */ - *((gid_t *) p) = t; - } else { /* i == 2 -- uid */ - *((uid_t *) p) = t; - } - } - - *line++ = '\0'; - i++; - } /* while (1) */ - - return -1; -} - -/**********************************************************************/ - -static const unsigned char gr_off[] ALIGN1 = { - offsetof(struct group, gr_name), /* 0 */ - offsetof(struct group, gr_passwd), /* 1 */ - offsetof(struct group, gr_gid) /* 2 - not a char ptr */ -}; - -static int FAST_FUNC bb__parsegrent(void *data, char *line) -{ - char *endptr; - char *p; - int i; - char **members; - char *end_of_buf; - - end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */ - i = 0; - while (1) { - p = (char *) data + gr_off[i]; - - if (i < 2) { - *((char **) p) = line; - line = strchr(line, ':'); - if (!line) { - break; - } - *line++ = '\0'; - i++; - } else { - *((gid_t *) p) = strtoul(line, &endptr, 10); - - /* NOTE: glibc difference - glibc allows omission of the - * trailing colon when there is no member list. We treat - * this as an error. */ - - /* Make sure we had at least one digit, and that the - * failing char is the next field seperator ':'. See - * glibc difference note above. */ - if ((endptr == line) || (*endptr != ':')) { - break; - } - - i = 1; /* Count terminating NULL ptr. */ - p = endptr; - - if (p[1]) { /* We have a member list to process. */ - /* Overwrite the last ':' with a ',' before counting. - * This allows us to (1) test for initial ',' - * and (2) adds one ',' so that the number of commas - * equals the member count. */ - *p = ','; - do { - /* NOTE: glibc difference - glibc allows and trims leading - * (but not trailing) space. We treat this as an error. */ - /* NOTE: glibc difference - glibc allows consecutive and - * trailing commas, and ignores "empty string" users. We - * treat this as an error. */ - if (*p == ',') { - ++i; - *p = 0; /* nul-terminate each member string. */ - if (!*++p || (*p == ',') || isspace(*p)) { - goto ERR; - } - } - } while (*++p); - } - - /* Now align (p+1), rounding up. */ - /* Assumes sizeof(char **) is a power of 2. */ - members = (char **)( (((intptr_t) p) + sizeof(char **)) - & ~((intptr_t)(sizeof(char **) - 1)) ); - - if (((char *)(members + i)) > end_of_buf) { /* No space. */ - break; - } - - ((struct group *) data)->gr_mem = members; - - if (--i) { - p = endptr; /* Pointing to char prior to first member. */ - while (1) { - *members++ = ++p; - if (!--i) - break; - while (*++p) - continue; - } - } - *members = NULL; - - return 0; - } - } /* while (1) */ - - ERR: - return -1; -} - -/**********************************************************************/ - -#if ENABLE_USE_BB_SHADOW -static const unsigned char sp_off[] ALIGN1 = { - offsetof(struct spwd, sp_namp), /* 0: char* */ - offsetof(struct spwd, sp_pwdp), /* 1: char* */ - offsetof(struct spwd, sp_lstchg), /* 2: long */ - offsetof(struct spwd, sp_min), /* 3: long */ - offsetof(struct spwd, sp_max), /* 4: long */ - offsetof(struct spwd, sp_warn), /* 5: long */ - offsetof(struct spwd, sp_inact), /* 6: long */ - offsetof(struct spwd, sp_expire), /* 7: long */ - offsetof(struct spwd, sp_flag) /* 8: unsigned long */ -}; - -static int FAST_FUNC bb__parsespent(void *data, char *line) -{ - char *endptr; - char *p; - int i; - - i = 0; - while (1) { - p = (char *) data + sp_off[i]; - if (i < 2) { - *((char **) p) = line; - line = strchr(line, ':'); - if (!line) { - break; /* error */ - } - } else { - *((long *) p) = strtoul(line, &endptr, 10); - if (endptr == line) { - *((long *) p) = -1L; - } - line = endptr; - if (i == 8) { - if (*line != '\0') { - break; /* error */ - } - return 0; /* all ok */ - } - if (*line != ':') { - break; /* error */ - } - } - *line++ = '\0'; - i++; - } - - return EINVAL; -} -#endif - -/**********************************************************************/ - -/* Reads until EOF, or until it finds a line which fits in the buffer - * and for which the parser function succeeds. - * - * Returns 0 on success and ENOENT for end-of-file (glibc convention). - */ -static int bb__pgsreader( - int FAST_FUNC (*parserfunc)(void *d, char *line), - void *data, - char *__restrict line_buff, - size_t buflen, - FILE *f) -{ - int skip; - int rv = ERANGE; - - if (buflen < PWD_BUFFER_SIZE) { - errno = rv; - return rv; - } - - skip = 0; - while (1) { - if (!fgets(line_buff, buflen, f)) { - if (feof(f)) { - rv = ENOENT; - } - break; - } - - { - int line_len = strlen(line_buff) - 1; - if (line_len >= 0 && line_buff[line_len] == '\n') { - line_buff[line_len] = '\0'; - } else - if (line_len + 2 == buflen) { - /* A start (or continuation) of overlong line */ - skip = 1; - continue; - } /* else: a last line in the file, and it has no '\n' */ - } - - if (skip) { - /* This "line" is a remainder of overlong line, ignore */ - skip = 0; - continue; - } - - /* NOTE: glibc difference - glibc strips leading whitespace from - * records. We do not allow leading whitespace. */ - - /* Skip empty lines, comment lines, and lines with leading - * whitespace. */ - if (line_buff[0] != '\0' && line_buff[0] != '#' && !isspace(line_buff[0])) { - if (parserfunc == bb__parsegrent) { - /* Do evil group hack: - * The group entry parsing function needs to know where - * the end of the buffer is so that it can construct the - * group member ptr table. */ - ((struct group *) data)->gr_name = line_buff + buflen; - } - if (parserfunc(data, line_buff) == 0) { - rv = 0; - break; - } - } - } /* while (1) */ - - return rv; -} diff --git a/libpwdgrp/pwd_grp_internal.c b/libpwdgrp/pwd_grp_internal.c deleted file mode 100644 index d6483be84..000000000 --- a/libpwdgrp/pwd_grp_internal.c +++ /dev/null @@ -1,61 +0,0 @@ -/* vi: set sw=4 ts=4: */ -/* Copyright (C) 2003 Manuel Novoa III - * - * Licensed under GPLv2 or later, see file LICENSE in this source tree. - */ - -/* Nov 6, 2003 Initial version. - * - * NOTE: This implementation is quite strict about requiring all - * field seperators. It also does not allow leading whitespace - * except when processing the numeric fields. glibc is more - * lenient. See the various glibc difference comments below. - * - * TODO: - * Move to dynamic allocation of (currently statically allocated) - * buffers; especially for the group-related functions since - * large group member lists will cause error returns. - */ - -#ifndef GETXXKEY_R_FUNC -#error GETXXKEY_R_FUNC is not defined! -#endif - -int GETXXKEY_R_FUNC(GETXXKEY_R_KEYTYPE key, - GETXXKEY_R_ENTTYPE *__restrict resultbuf, - char *__restrict buffer, size_t buflen, - GETXXKEY_R_ENTTYPE **__restrict result) -{ - FILE *stream; - int rv; - - *result = NULL; - - stream = fopen_for_read(GETXXKEY_R_PATHNAME); - if (!stream) - return errno; - while (1) { - rv = bb__pgsreader(GETXXKEY_R_PARSER, resultbuf, buffer, buflen, stream); - if (!rv) { - if (GETXXKEY_R_TEST(resultbuf)) { /* found key? */ - *result = resultbuf; - break; - } - } else { - if (rv == ENOENT) { /* EOF encountered */ - rv = 0; - } - break; - } - } - fclose(stream); - - return rv; -} - -#undef GETXXKEY_R_FUNC -#undef GETXXKEY_R_PARSER -#undef GETXXKEY_R_ENTTYPE -#undef GETXXKEY_R_TEST -#undef GETXXKEY_R_KEYTYPE -#undef GETXXKEY_R_PATHNAME -- cgit v1.2.3-55-g6feb From 908b6e5dfdbc81322680ce939e5415161c637bb4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 2 Jan 2015 22:31:07 +0100 Subject: libpwdgrp: use FAST_FUNC to make "matching ABI" optimization more likely to succeed See bb_internal_get*nam_r size reduction: function old new delta bb_internal_getpwent_r 167 176 +9 getXXnam_r 204 206 +2 sulogin_main 326 325 -1 su_main 471 470 -1 read_line_input 3832 3831 -1 print_stat 865 864 -1 prepare_socket_fd 283 282 -1 load_crontab 777 776 -1 fork_job 456 455 -1 do_shm 884 883 -1 do_sem 637 636 -1 do_msg 783 782 -1 complete_username 124 123 -1 bb_internal_getgrouplist 71 70 -1 xgetpwuid 27 25 -2 xgetpwnam 27 25 -2 xgetgrnam 27 25 -2 xgetgrgid 27 25 -2 uid2uname 18 16 -2 login_main 980 978 -2 gid2group 18 16 -2 get_shell_name 54 52 -2 change_identity 50 48 -2 bb_internal_initgroups 50 48 -2 argstr 1261 1259 -2 print_perms 177 174 -3 inetd_main 2077 2074 -3 run_applet_no_and_exit 446 442 -4 fileaction_setowngrp 89 85 -4 deluser_main 312 308 -4 bb_internal_getpwuid 19 15 -4 bb_internal_getpwnam 11 7 -4 bb_internal_getgrnam 14 10 -4 bb_internal_getgrgid 19 15 -4 adduser_main 865 861 -4 passwd_main 989 984 -5 get_passwd 97 92 -5 data_extract_all 887 882 -5 check_user_passwd 490 485 -5 get_groups 81 75 -6 ftpd_main 2178 2171 -7 bb_internal_getspnam_r 42 18 -24 bb_internal_getpwnam_r 39 15 -24 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/41 up/down: 11/-153) Total: -142 bytes text data bss dec hex filename 923167 928 17676 941771 e5ecb busybox_old 923023 928 17676 941627 e5e3b busybox_unstripped Signed-off-by: Denys Vlasenko --- include/grp_.h | 11 +++++------ include/pwd_.h | 14 +++++++------- include/shadow_.h | 26 +++++++++++++------------- libpwdgrp/pwd_grp.c | 32 ++++++++++++++++---------------- 4 files changed, 41 insertions(+), 42 deletions(-) diff --git a/include/grp_.h b/include/grp_.h index f7b8d836f..db13ce3b4 100644 --- a/include/grp_.h +++ b/include/grp_.h @@ -36,31 +36,30 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN #define getgrouplist bb_internal_getgrouplist #define initgroups bb_internal_initgroups - /* All function names below should be remapped by #defines above * in order to not collide with libc names. */ /* Close the group-file stream. */ -extern void endgrent(void); +void FAST_FUNC endgrent(void); /* Search for an entry with a matching group ID. */ -extern struct group *getgrgid(gid_t __gid); +struct group* FAST_FUNC getgrgid(gid_t __gid); /* Search for an entry with a matching group name. */ -extern struct group *getgrnam(const char *__name); +struct group* FAST_FUNC getgrnam(const char *__name); /* Reentrant versions of some of the functions above. */ /* Store at most *NGROUPS members of the group set for USER into *GROUPS. Also include GROUP. The actual number of groups found is returned in *NGROUPS. Return -1 if the if *NGROUPS is too small. */ -extern int getgrouplist(const char *__user, gid_t __group, +int FAST_FUNC getgrouplist(const char *__user, gid_t __group, gid_t *__groups, int *__ngroups); /* Initialize the group set for the current user by reading the group database and using all groups of which USER is a member. Also include GROUP. */ -extern int initgroups(const char *__user, gid_t __group); +int FAST_FUNC initgroups(const char *__user, gid_t __group); POP_SAVED_FUNCTION_VISIBILITY diff --git a/include/pwd_.h b/include/pwd_.h index d086f86e3..32b5b366e 100644 --- a/include/pwd_.h +++ b/include/pwd_.h @@ -43,21 +43,21 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN * in order to not collide with libc names. */ /* Rewind the password-file stream. */ -extern void setpwent(void); +void FAST_FUNC setpwent(void); /* Close the password-file stream. */ -extern void endpwent(void); +void FAST_FUNC endpwent(void); #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS /* Read an entry from the password-file stream, opening it if necessary. */ -extern struct passwd *getpwent(void); +struct passwd* FAST_FUNC getpwent(void); #endif /* Search for an entry with a matching user ID. */ -extern struct passwd *getpwuid(uid_t __uid); +struct passwd* FAST_FUNC getpwuid(uid_t __uid); /* Search for an entry with a matching username. */ -extern struct passwd *getpwnam(const char *__name); +struct passwd* FAST_FUNC getpwnam(const char *__name); /* Reentrant versions of some of the functions above. @@ -67,11 +67,11 @@ extern struct passwd *getpwnam(const char *__name); other reentrant functions so the chances are good this is what the POSIX people would choose. */ -extern int getpwent_r(struct passwd *__restrict __resultbuf, +int FAST_FUNC getpwent_r(struct passwd *__restrict __resultbuf, char *__restrict __buffer, size_t __buflen, struct passwd **__restrict __result); -extern int getpwnam_r(const char *__restrict __name, +int FAST_FUNC getpwnam_r(const char *__restrict __name, struct passwd *__restrict __resultbuf, char *__restrict __buffer, size_t __buflen, struct passwd **__restrict __result); diff --git a/include/shadow_.h b/include/shadow_.h index 7babe4f30..8e2581e7c 100644 --- a/include/shadow_.h +++ b/include/shadow_.h @@ -57,48 +57,48 @@ struct spwd { #ifdef UNUSED_FOR_NOW /* Open database for reading */ -extern void setspent(void); +void FAST_FUNC setspent(void); /* Close database */ -extern void endspent(void); +void FAST_FUNC endspent(void); /* Get next entry from database, perhaps after opening the file */ -extern struct spwd *getspent(void); +struct spwd* FAST_FUNC getspent(void); /* Get shadow entry matching NAME */ -extern struct spwd *getspnam(const char *__name); +struct spwd* FAST_FUNC getspnam(const char *__name); /* Read shadow entry from STRING */ -extern struct spwd *sgetspent(const char *__string); +struct spwd* FAST_FUNC sgetspent(const char *__string); /* Read next shadow entry from STREAM */ -extern struct spwd *fgetspent(FILE *__stream); +struct spwd* FAST_FUNC fgetspent(FILE *__stream); /* Write line containing shadow password entry to stream */ -extern int putspent(const struct spwd *__p, FILE *__stream); +int FAST_FUNC putspent(const struct spwd *__p, FILE *__stream); /* Reentrant versions of some of the functions above */ -extern int getspent_r(struct spwd *__result_buf, char *__buffer, +int FAST_FUNC getspent_r(struct spwd *__result_buf, char *__buffer, size_t __buflen, struct spwd **__result); #endif -extern int getspnam_r(const char *__name, struct spwd *__result_buf, +int FAST_FUNC getspnam_r(const char *__name, struct spwd *__result_buf, char *__buffer, size_t __buflen, struct spwd **__result); #ifdef UNUSED_FOR_NOW -extern int sgetspent_r(const char *__string, struct spwd *__result_buf, +int FAST_FUNC sgetspent_r(const char *__string, struct spwd *__result_buf, char *__buffer, size_t __buflen, struct spwd **__result); -extern int fgetspent_r(FILE *__stream, struct spwd *__result_buf, +int FAST_FUNC fgetspent_r(FILE *__stream, struct spwd *__result_buf, char *__buffer, size_t __buflen, struct spwd **__result); /* Protect password file against multi writers */ -extern int lckpwdf(void); +int FAST_FUNC lckpwdf(void); /* Unlock password file */ -extern int ulckpwdf(void); +int FAST_FUNC ulckpwdf(void); #endif POP_SAVED_FUNCTION_VISIBILITY diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index ed8370124..0d8e2bb5c 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -288,7 +288,7 @@ static void *convert_to_struct(const char *def, const unsigned char *off, /****** getXXnam/id_r */ -static int getXXnam_r(const char *name, uintptr_t db_and_field_pos, char *buffer, size_t buflen, +static int FAST_FUNC getXXnam_r(const char *name, uintptr_t db_and_field_pos, char *buffer, size_t buflen, void *result) { void *struct_buf = *(void**)result; @@ -316,18 +316,18 @@ static int getXXnam_r(const char *name, uintptr_t db_and_field_pos, char *buffer return errno; } -int getpwnam_r(const char *name, struct passwd *struct_buf, char *buffer, size_t buflen, +int FAST_FUNC getpwnam_r(const char *name, struct passwd *struct_buf, char *buffer, size_t buflen, struct passwd **result) { /* Why the "store buffer address in result" trick? * This way, getXXnam_r has the same ABI signature as getpwnam_r, - * hopefully compiler can optimize tall call better in this case. + * hopefully compiler can optimize tail call better in this case. */ *result = struct_buf; return getXXnam_r(name, (0 << 2) + 0, buffer, buflen, result); } #if ENABLE_USE_BB_SHADOW -int getspnam_r(const char *name, struct spwd *struct_buf, char *buffer, size_t buflen, +int FAST_FUNC getspnam_r(const char *name, struct spwd *struct_buf, char *buffer, size_t buflen, struct spwd **result) { *result = struct_buf; @@ -337,7 +337,7 @@ int getspnam_r(const char *name, struct spwd *struct_buf, char *buffer, size_t b /****** getXXent_r */ -static int getXXent_r(void *struct_buf, char *buffer, size_t buflen, +static int FAST_FUNC getXXent_r(void *struct_buf, char *buffer, size_t buflen, void *result, unsigned db_idx) { @@ -374,14 +374,14 @@ static int getXXent_r(void *struct_buf, char *buffer, size_t buflen, return errno; } -int getpwent_r(struct passwd *struct_buf, char *buffer, size_t buflen, struct passwd **result) +int FAST_FUNC getpwent_r(struct passwd *struct_buf, char *buffer, size_t buflen, struct passwd **result) { return getXXent_r(struct_buf, buffer, buflen, result, 0); } /****** getXXnam/id */ -static void *getXXnam(const char *name, unsigned db_and_field_pos) +static void* FAST_FUNC getXXnam(const char *name, unsigned db_and_field_pos) { char *buf; void *result; @@ -409,39 +409,39 @@ static void *getXXnam(const char *name, unsigned db_and_field_pos) return result; } -struct passwd *getpwnam(const char *name) +struct passwd* FAST_FUNC getpwnam(const char *name) { return getXXnam(name, (0 << 2) + 0); } -struct group *getgrnam(const char *name) +struct group* FAST_FUNC getgrnam(const char *name) { return getXXnam(name, (1 << 2) + 0); } -struct passwd *getpwuid(uid_t id) +struct passwd* FAST_FUNC getpwuid(uid_t id) { return getXXnam(utoa(id), (0 << 2) + 2); } -struct group *getgrgid(gid_t id) +struct group* FAST_FUNC getgrgid(gid_t id) { return getXXnam(utoa(id), (1 << 2) + 2); } /****** end/setXXend */ -void endpwent(void) +void FAST_FUNC endpwent(void) { if (has_S && S.db[0].fp) { fclose(S.db[0].fp); S.db[0].fp = NULL; } } -void setpwent(void) +void FAST_FUNC setpwent(void) { if (has_S && S.db[0].fp) { rewind(S.db[0].fp); } } -void endgrent(void) +void FAST_FUNC endgrent(void) { if (has_S && S.db[1].fp) { fclose(S.db[1].fp); @@ -491,7 +491,7 @@ static gid_t* FAST_FUNC getgrouplist_internal(int *ngroups_ptr, return group_list; } -int initgroups(const char *user, gid_t gid) +int FAST_FUNC initgroups(const char *user, gid_t gid) { int ngroups; gid_t *group_list = getgrouplist_internal(&ngroups, user, gid); @@ -501,7 +501,7 @@ int initgroups(const char *user, gid_t gid) return ngroups; } -int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups) +int FAST_FUNC getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups) { int ngroups_old = *ngroups; gid_t *group_list = getgrouplist_internal(ngroups, user, gid); -- cgit v1.2.3-55-g6feb From 31d6734457b9cafeeaa862750c2afa80aa67816c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 3 Jan 2015 15:15:47 +0100 Subject: libpwdgrp: tweak comments, replace one xmalloc with xzalloc Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 0d8e2bb5c..823884edc 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -1,5 +1,5 @@ /* vi: set sw=4 ts=4: */ -/* Copyright (C) 2014 Tito Ragusa +/* Copyright (C) 2014 Tito Ragusa * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ @@ -9,38 +9,33 @@ * Rewrite of some parts. Main differences are: * * 1) the buffer for getpwuid, getgrgid, getpwnam, getgrnam is dynamically - * allocated and reused by later calls. if ERANGE error pops up it is - * reallocated to the size of the longest line found so far in the - * passwd/group files and reused for later calls. + * allocated and reused by later calls. * If ENABLE_FEATURE_CLEAN_UP is set the buffers are freed at program * exit using the atexit function to make valgrind happy. * 2) the passwd/group files: * a) must contain the expected number of fields (as per count of field * delimeters ":") or we will complain with a error message. - * b) leading or trailing whitespace in fields is allowed and handled. + * b) leading or trailing whitespace in fields is stripped. * c) some fields are not allowed to be empty (e.g. username, uid/gid, * homedir, shell) and in this case NULL is returned and errno is * set to EINVAL. This behaviour could be easily changed by * modifying PW_DEF, GR_DEF, SP_DEF strings (uppercase * makes a field mandatory). * d) the string representing uid/gid must be convertible by strtoXX - * functions or NULL is returned and errno is set to EINVAL. - * e) leading or trailing whitespaces in member names and empty members - * are allowed and handled. - * 3) the internal function for getgrouplist uses a dynamically allocated - * buffer and retries with a bigger one in case it is too small; - * 4) the _r functions use the user supplied buffers that are never reallocated - * but use mostly the same common code as the other functions. - * 5) at the moment only the functions really used by busybox code are + * functions, or errno is set to EINVAL. + * e) leading or trailing whitespace in group member names are stripped. + * 3) the internal function for getgrouplist uses dynamically allocated buffer. + * 4) at the moment only the functions really used by busybox code are * implemented, if you need a particular missing function it should be * easy to write it by using the internal common code. */ #include "libbb.h" -/* S = string not empty, s = string maybe empty, */ -/* I = uid,gid, l = long maybe empty, m = members,*/ -/* r = reserved */ +/* S = string not empty, s = string maybe empty, + * I = uid,gid, l = long maybe empty, m = members, + * r = reserved + */ #define PW_DEF "SsIIsSS" #define GR_DEF "SsIm" #define SP_DEF "Ssllllllr" @@ -99,7 +94,8 @@ static const struct const_passdb const_sp_db = { _PATH_SHADOW, sp_off, SP_DEF, s /* We avoid having big global data. */ struct statics { - /* It's ok to use one buffer for getpwuid and getpwnam. Manpage says: + /* It's ok to use same buffer (db[0].malloced) for getpwuid and getpwnam. + * Manpage says: * "The return value may point to a static area, and may be overwritten * by subsequent calls to getpwent(), getpwnam(), or getpwuid()." */ @@ -124,14 +120,12 @@ static struct statics *get_S(void) return ptr_to_statics; } -/**********************************************************************/ -/* Internal functions */ -/**********************************************************************/ +/* Internal functions */ /* Divide the passwd/group/shadow record in fields * by substituting the given delimeter * e.g. ':' or ',' with '\0'. - * Returns the number of fields found. + * Returns the number of fields found. * Strips leading and trailing whitespace in fields. */ static int tokenize(char *buffer, int ch) @@ -328,7 +322,7 @@ int FAST_FUNC getpwnam_r(const char *name, struct passwd *struct_buf, char *buff } #if ENABLE_USE_BB_SHADOW int FAST_FUNC getspnam_r(const char *name, struct spwd *struct_buf, char *buffer, size_t buflen, - struct spwd **result) + struct spwd **result) { *result = struct_buf; return getXXnam_r(name, (2 << 2) + 0, buffer, buflen, result); @@ -461,7 +455,7 @@ static gid_t* FAST_FUNC getgrouplist_internal(int *ngroups_ptr, get_S(); /* We alloc space for 8 gids at a time. */ - group_list = xmalloc(8 * sizeof(group_list[0])); + group_list = xzalloc(8 * sizeof(group_list[0])); group_list[0] = gid; ngroups = 1; -- cgit v1.2.3-55-g6feb From 8d547aca75f8b096976a472714241acd4328be46 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 3 Jan 2015 15:54:04 +0100 Subject: libpwdgrp: fix a memory leak in getXXnam (we did not save address of string buf) function old new delta convert_to_struct 261 269 +8 const_sp_db 20 24 +4 const_pw_db 20 24 +4 const_gr_db 20 24 +4 tokenize 144 147 +3 parse_common 185 188 +3 get_S 82 85 +3 bb_internal_getpwent_r 188 185 -3 gr_off 4 - -4 getXXnam 171 165 -6 pw_off 7 - -7 getgrouplist_internal 237 229 -8 getXXnam_r 215 207 -8 sp_off 9 - -9 ------------------------------------------------------------------------------ (add/remove: 0/3 grow/shrink: 7/4 up/down: 29/-45) Total: -16 bytes Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 131 +++++++++++++++++++++++++++++----------------------- 1 file changed, 74 insertions(+), 57 deletions(-) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 823884edc..6d938f621 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -32,69 +32,79 @@ #include "libbb.h" -/* S = string not empty, s = string maybe empty, - * I = uid,gid, l = long maybe empty, m = members, - * r = reserved - */ -#define PW_DEF "SsIIsSS" -#define GR_DEF "SsIm" -#define SP_DEF "Ssllllllr" - -static const uint8_t pw_off[] ALIGN1 = { - offsetof(struct passwd, pw_name), /* 0 S */ - offsetof(struct passwd, pw_passwd), /* 1 s */ - offsetof(struct passwd, pw_uid), /* 2 I */ - offsetof(struct passwd, pw_gid), /* 3 I */ - offsetof(struct passwd, pw_gecos), /* 4 s */ - offsetof(struct passwd, pw_dir), /* 5 S */ - offsetof(struct passwd, pw_shell) /* 6 S */ -}; -static const uint8_t gr_off[] ALIGN1 = { - offsetof(struct group, gr_name), /* 0 S */ - offsetof(struct group, gr_passwd), /* 1 s */ - offsetof(struct group, gr_gid), /* 2 I */ - offsetof(struct group, gr_mem) /* 3 m (char **) */ -}; -#if ENABLE_USE_BB_SHADOW -static const uint8_t sp_off[] ALIGN1 = { - offsetof(struct spwd, sp_namp), /* 0 S Login name */ - offsetof(struct spwd, sp_pwdp), /* 1 s Encrypted password */ - offsetof(struct spwd, sp_lstchg), /* 2 l */ - offsetof(struct spwd, sp_min), /* 3 l */ - offsetof(struct spwd, sp_max), /* 4 l */ - offsetof(struct spwd, sp_warn), /* 5 l */ - offsetof(struct spwd, sp_inact), /* 6 l */ - offsetof(struct spwd, sp_expire), /* 7 l */ - offsetof(struct spwd, sp_flag) /* 8 r Reserved */ -}; -#endif - struct const_passdb { const char *filename; - const uint8_t *off; const char def[10]; + const uint8_t off[9]; uint8_t numfields; - uint8_t size_of; }; struct passdb { const char *filename; - const uint8_t *off; const char def[10]; + const uint8_t off[9]; uint8_t numfields; - uint8_t size_of; FILE *fp; - void *malloced; + char *malloced; + char struct_result[0 + | sizeof(struct passwd) + | sizeof(struct group) + IF_USE_BB_SHADOW( | sizeof(struct spwd) ) + /* bitwise OR above is poor man's max(a,b,c) */ + ]; }; -static const struct const_passdb const_pw_db = { _PATH_PASSWD, pw_off, PW_DEF, sizeof(PW_DEF)-1, sizeof(struct passwd) }; -static const struct const_passdb const_gr_db = { _PATH_GROUP , gr_off, GR_DEF, sizeof(GR_DEF)-1, sizeof(struct group) }; +/* S = string not empty, s = string maybe empty, + * I = uid,gid, l = long maybe empty, m = members, + * r = reserved + */ +#define PW_DEF "SsIIsSS" +#define GR_DEF "SsIm" +#define SP_DEF "Ssllllllr" + +static const struct const_passdb const_pw_db = { + _PATH_PASSWD, PW_DEF, + { + offsetof(struct passwd, pw_name), /* 0 S */ + offsetof(struct passwd, pw_passwd), /* 1 s */ + offsetof(struct passwd, pw_uid), /* 2 I */ + offsetof(struct passwd, pw_gid), /* 3 I */ + offsetof(struct passwd, pw_gecos), /* 4 s */ + offsetof(struct passwd, pw_dir), /* 5 S */ + offsetof(struct passwd, pw_shell) /* 6 S */ + }, + sizeof(PW_DEF)-1 +}; +static const struct const_passdb const_gr_db = { + _PATH_GROUP, GR_DEF, + { + offsetof(struct group, gr_name), /* 0 S */ + offsetof(struct group, gr_passwd), /* 1 s */ + offsetof(struct group, gr_gid), /* 2 I */ + offsetof(struct group, gr_mem) /* 3 m (char **) */ + }, + sizeof(GR_DEF)-1 +}; #if ENABLE_USE_BB_SHADOW -static const struct const_passdb const_sp_db = { _PATH_SHADOW, sp_off, SP_DEF, sizeof(SP_DEF)-1, sizeof(struct spwd) }; +static const struct const_passdb const_sp_db = { + _PATH_SHADOW, SP_DEF, + { + offsetof(struct spwd, sp_namp), /* 0 S Login name */ + offsetof(struct spwd, sp_pwdp), /* 1 s Encrypted password */ + offsetof(struct spwd, sp_lstchg), /* 2 l */ + offsetof(struct spwd, sp_min), /* 3 l */ + offsetof(struct spwd, sp_max), /* 4 l */ + offsetof(struct spwd, sp_warn), /* 5 l */ + offsetof(struct spwd, sp_inact), /* 6 l */ + offsetof(struct spwd, sp_expire), /* 7 l */ + offsetof(struct spwd, sp_flag) /* 8 r Reserved */ + }, + sizeof(SP_DEF)-1 +}; #endif /* We avoid having big global data. */ struct statics { - /* It's ok to use same buffer (db[0].malloced) for getpwuid and getpwnam. + /* It's ok to use same buffer (db[0].struct_result) for getpwuid and getpwnam. * Manpage says: * "The return value may point to a static area, and may be overwritten * by subsequent calls to getpwent(), getpwnam(), or getpwuid()." @@ -223,9 +233,15 @@ static char *parse_file(const char *filename, } /* Convert passwd/group/shadow file record in buffer to a struct */ -static void *convert_to_struct(const char *def, const unsigned char *off, +static void *convert_to_struct(struct passdb *db, char *buffer, void *result) { + const char *def = db->def; + const uint8_t *off = db->off; + +/* TODO? for consistency, zero out all fields */ +/* memset(result, 0, size_of_result);*/ + for (;;) { void *member = (char*)result + (*off++); @@ -282,7 +298,8 @@ static void *convert_to_struct(const char *def, const unsigned char *off, /****** getXXnam/id_r */ -static int FAST_FUNC getXXnam_r(const char *name, uintptr_t db_and_field_pos, char *buffer, size_t buflen, +static int FAST_FUNC getXXnam_r(const char *name, uintptr_t db_and_field_pos, + char *buffer, size_t buflen, void *result) { void *struct_buf = *(void**)result; @@ -299,7 +316,7 @@ static int FAST_FUNC getXXnam_r(const char *name, uintptr_t db_and_field_pos, ch errno = ERANGE; } else { memcpy(buffer, buf, size); - *(void**)result = convert_to_struct(db->def, db->off, buffer, struct_buf); + *(void**)result = convert_to_struct(db, buffer, struct_buf); } free(buf); } @@ -310,8 +327,9 @@ static int FAST_FUNC getXXnam_r(const char *name, uintptr_t db_and_field_pos, ch return errno; } -int FAST_FUNC getpwnam_r(const char *name, struct passwd *struct_buf, char *buffer, size_t buflen, - struct passwd **result) +int FAST_FUNC getpwnam_r(const char *name, struct passwd *struct_buf, + char *buffer, size_t buflen, + struct passwd **result) { /* Why the "store buffer address in result" trick? * This way, getXXnam_r has the same ABI signature as getpwnam_r, @@ -357,7 +375,7 @@ static int FAST_FUNC getXXent_r(void *struct_buf, char *buffer, size_t buflen, errno = ERANGE; } else { memcpy(buffer, buf, size); - *(void**)result = convert_to_struct(db->def, db->off, buffer, struct_buf); + *(void**)result = convert_to_struct(db, buffer, struct_buf); } free(buf); } @@ -393,12 +411,11 @@ static void* FAST_FUNC getXXnam(const char *name, unsigned db_and_field_pos) close_on_exec_on(fileno(db->fp)); } - free(db->malloced); - db->malloced = NULL; buf = parse_common(db->fp, db->filename, db->numfields, name, db_and_field_pos & 3); if (buf) { - db->malloced = xzalloc(db->size_of); - result = convert_to_struct(db->def, db->off, buf, db->malloced); + free(db->malloced); + db->malloced = buf; + result = convert_to_struct(db, buf, db->struct_result); } return result; } @@ -465,7 +482,7 @@ static gid_t* FAST_FUNC getgrouplist_internal(int *ngroups_ptr, while ((buf = parse_common(fp, _PATH_GROUP, sizeof(GR_DEF)-1, NULL, 0)) != NULL) { char **m; struct group group; - if (!convert_to_struct(GR_DEF, gr_off, buf, &group)) + if (!convert_to_struct(&S.db[1], buf, &group)) goto next; if (group.gr_gid == gid) goto next; -- cgit v1.2.3-55-g6feb From 9dca6acaac3a49f1ff8ba9d3ca78853da6f59ae1 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 3 Jan 2015 16:09:05 +0100 Subject: libpwdgrp: reinstate Tito's optional atexit freeing Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 6d938f621..539d2b01f 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -117,6 +117,18 @@ static struct statics *ptr_to_statics; #define S (*ptr_to_statics) #define has_S (ptr_to_statics) +#if ENABLE_FEATURE_CLEAN_UP +static void free_static(void) +{ + free(S.db[0].malloced); + free(S.db[1].malloced); +# if ENABLE_USE_BB_SHADOW + S.db[2].malloced); +# endif + free(ptr_to_statics); +} +#endif + static struct statics *get_S(void) { if (!ptr_to_statics) { @@ -125,6 +137,9 @@ static struct statics *get_S(void) memcpy(&S.db[1], &const_gr_db, sizeof(const_gr_db)); #if ENABLE_USE_BB_SHADOW memcpy(&S.db[2], &const_sp_db, sizeof(const_sp_db)); +#endif +#if ENABLE_FEATURE_CLEAN_UP + atexit(free_static); #endif } return ptr_to_statics; -- cgit v1.2.3-55-g6feb From 5e62a3d016633d4d97906f0f73298dc8e8b6a42b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 3 Jan 2015 17:53:49 +0100 Subject: libpwdgrp: use a better estimate of max struct size Previous code's trick with bitwise OR was giving this on 32-bit x86: sizeof(struct passwd):28 sizeof(struct group):16 sizeof(struct spwd):36 sizeof(struct_result):60 Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 539d2b01f..f3fcec859 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -45,11 +45,10 @@ struct passdb { uint8_t numfields; FILE *fp; char *malloced; - char struct_result[0 - | sizeof(struct passwd) - | sizeof(struct group) - IF_USE_BB_SHADOW( | sizeof(struct spwd) ) - /* bitwise OR above is poor man's max(a,b,c) */ + char struct_result[ + /* Should be max(sizeof passwd,group,spwd), but this will do: */ + IF_NOT_USE_BB_SHADOW(sizeof(struct passwd)) + IF_USE_BB_SHADOW(sizeof(struct spwd)) ]; }; -- cgit v1.2.3-55-g6feb From 20c0a16334e96493077eaaad6f4c5690af0aa6e8 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 3 Jan 2015 19:12:49 +0100 Subject: libpwdgrp: make db->def[] one byte shorter In the future I will need another uint8_t, want to fit it w/o using another word on 32 bits. Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index f3fcec859..1b2418a8d 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -34,13 +34,13 @@ struct const_passdb { const char *filename; - const char def[10]; + const char def[9]; const uint8_t off[9]; uint8_t numfields; }; struct passdb { const char *filename; - const char def[10]; + const char def[9]; const uint8_t off[9]; uint8_t numfields; FILE *fp; @@ -51,6 +51,11 @@ struct passdb { IF_USE_BB_SHADOW(sizeof(struct spwd)) ]; }; +/* Note: for shadow db, def[9] will not contain terminating NUL, + * but convert_to_struct() logic detects def[] end by "less than SP?", + * not by "is it NUL?" condition; and off[0] happens to be zero + * for every db anyway, so there _is_ in fact a terminating NUL there. + */ /* S = string not empty, s = string maybe empty, * I = uid,gid, l = long maybe empty, m = members, @@ -122,7 +127,7 @@ static void free_static(void) free(S.db[0].malloced); free(S.db[1].malloced); # if ENABLE_USE_BB_SHADOW - S.db[2].malloced); + free(S.db[2].malloced); # endif free(ptr_to_statics); } @@ -286,8 +291,8 @@ static void *convert_to_struct(struct passdb *db, * at the end of malloced buffer! */ members = (char **) - ( ((intptr_t)S.tokenize_end + sizeof(char**)) - & -(intptr_t)sizeof(char**) + ( ((intptr_t)S.tokenize_end + sizeof(members[0])) + & -(intptr_t)sizeof(members[0]) ); ((struct group *)result)->gr_mem = members; @@ -300,7 +305,7 @@ static void *convert_to_struct(struct passdb *db, /* def "r" does nothing */ def++; - if (*def == '\0') + if ((unsigned char)*def < (unsigned char)' ') break; buffer += strlen(buffer) + 1; } -- cgit v1.2.3-55-g6feb From 134c53098bdcbf7a0c34b60b97c46280d86eb48f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 3 Jan 2015 20:47:47 +0100 Subject: libpwdgrp: store getXXnam result in a single malloc block This saves a bit of memory but more importantly, allows to create xmalloc_getpwnam() API where result can be deleted simply using free(). function old new delta getXXnam 134 173 +39 parse_common 188 212 +24 convert_to_struct 277 290 +13 get_S 90 88 -2 tokenize 129 126 -3 bb_internal_getpwent_r 175 172 -3 getXXnam_r 208 198 -10 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/4 up/down: 76/-18) Total: 58 bytes Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 58 +++++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 1b2418a8d..4b61b61d2 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -34,24 +34,21 @@ struct const_passdb { const char *filename; - const char def[9]; - const uint8_t off[9]; + const char def[7 + 2*ENABLE_USE_BB_SHADOW]; + const uint8_t off[7 + 2*ENABLE_USE_BB_SHADOW]; uint8_t numfields; + uint8_t size_of; }; struct passdb { const char *filename; - const char def[9]; - const uint8_t off[9]; + const char def[7 + 2*ENABLE_USE_BB_SHADOW]; + const uint8_t off[7 + 2*ENABLE_USE_BB_SHADOW]; uint8_t numfields; + uint8_t size_of; FILE *fp; char *malloced; - char struct_result[ - /* Should be max(sizeof passwd,group,spwd), but this will do: */ - IF_NOT_USE_BB_SHADOW(sizeof(struct passwd)) - IF_USE_BB_SHADOW(sizeof(struct spwd)) - ]; }; -/* Note: for shadow db, def[9] will not contain terminating NUL, +/* Note: for shadow db, def[] will not contain terminating NUL, * but convert_to_struct() logic detects def[] end by "less than SP?", * not by "is it NUL?" condition; and off[0] happens to be zero * for every db anyway, so there _is_ in fact a terminating NUL there. @@ -76,7 +73,7 @@ static const struct const_passdb const_pw_db = { offsetof(struct passwd, pw_dir), /* 5 S */ offsetof(struct passwd, pw_shell) /* 6 S */ }, - sizeof(PW_DEF)-1 + sizeof(PW_DEF)-1, sizeof(struct passwd) }; static const struct const_passdb const_gr_db = { _PATH_GROUP, GR_DEF, @@ -86,7 +83,7 @@ static const struct const_passdb const_gr_db = { offsetof(struct group, gr_gid), /* 2 I */ offsetof(struct group, gr_mem) /* 3 m (char **) */ }, - sizeof(GR_DEF)-1 + sizeof(GR_DEF)-1, sizeof(struct group) }; #if ENABLE_USE_BB_SHADOW static const struct const_passdb const_sp_db = { @@ -102,19 +99,20 @@ static const struct const_passdb const_sp_db = { offsetof(struct spwd, sp_expire), /* 7 l */ offsetof(struct spwd, sp_flag) /* 8 r Reserved */ }, - sizeof(SP_DEF)-1 + sizeof(SP_DEF)-1, sizeof(struct spwd) }; #endif /* We avoid having big global data. */ struct statics { - /* It's ok to use same buffer (db[0].struct_result) for getpwuid and getpwnam. + /* It's ok to use same buffer (db[0].malloced) for getpwuid and getpwnam. * Manpage says: * "The return value may point to a static area, and may be overwritten * by subsequent calls to getpwent(), getpwnam(), or getpwuid()." */ struct passdb db[2 + ENABLE_USE_BB_SHADOW]; char *tokenize_end; + unsigned string_size; }; static struct statics *ptr_to_statics; @@ -205,21 +203,21 @@ static char *parse_common(FILE *fp, const char *filename, bb_error_msg("bad record at %s:%u", filename, count); goto free_and_next; } + S.string_size = S.tokenize_end - buf; -/* Ugly hack: group db requires aqdditional buffer space +/* Ugly hack: group db requires additional buffer space * for members[] array. If there is only one group, we need space * for 3 pointers: alignment padding, group name, NULL. * +1 for every additional group. */ if (n_fields == sizeof(GR_DEF)-1) { /* if we read group file */ - int resize = 3; + int cnt = 3; char *p = buf; while (*p) if (*p++ == ',') - resize++; - resize *= sizeof(char**); - resize += S.tokenize_end - buf; - buf = xrealloc(buf, resize); + cnt++; + S.string_size += cnt * sizeof(char*); + buf = xrealloc(buf, S.string_size); } if (!key) { @@ -258,8 +256,8 @@ static void *convert_to_struct(struct passdb *db, const char *def = db->def; const uint8_t *off = db->off; -/* TODO? for consistency, zero out all fields */ -/* memset(result, 0, size_of_result);*/ + /* For consistency, zero out all fields */ + memset(result, 0, db->size_of); for (;;) { void *member = (char*)result + (*off++); @@ -305,7 +303,7 @@ static void *convert_to_struct(struct passdb *db, /* def "r" does nothing */ def++; - if ((unsigned char)*def < (unsigned char)' ') + if ((unsigned char)*def <= (unsigned char)' ') break; buffer += strlen(buffer) + 1; } @@ -328,7 +326,9 @@ static int FAST_FUNC getXXnam_r(const char *name, uintptr_t db_and_field_pos, db = &S.db[db_and_field_pos >> 2]; *(void**)result = NULL; - buf = parse_file(db->filename, db->numfields, name, db_and_field_pos & 3); + buf = parse_file(db->filename, db->numfields, name, 0 /*db_and_field_pos & 3*/); + /* "db_and_field_pos & 3" is commented out since so far we don't implement + * getXXXid_r() functions which would use that to pass 2 here */ if (buf) { size_t size = S.tokenize_end - buf; if (size > buflen) { @@ -433,8 +433,14 @@ static void* FAST_FUNC getXXnam(const char *name, unsigned db_and_field_pos) buf = parse_common(db->fp, db->filename, db->numfields, name, db_and_field_pos & 3); if (buf) { free(db->malloced); - db->malloced = buf; - result = convert_to_struct(db, buf, db->struct_result); + /* We enlarge buf and move string data up, freeing space + * for struct passwd/group/spwd at the beginning. This way, + * entire result of getXXnam is in a single malloced block. + * This enables easy creation of xmalloc_getpwnam() API. + */ + db->malloced = buf = xrealloc(buf, db->size_of + S.string_size); + memmove(buf + db->size_of, buf, S.string_size); + result = convert_to_struct(db, buf + db->size_of, buf); } return result; } -- cgit v1.2.3-55-g6feb From f9936676bb7fd57ea2256af2247c970d1ed3f906 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 3 Jan 2015 21:03:39 +0100 Subject: libpwdgrp: fix counting of commas in group list Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 4b61b61d2..01058d3a1 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -203,22 +203,6 @@ static char *parse_common(FILE *fp, const char *filename, bb_error_msg("bad record at %s:%u", filename, count); goto free_and_next; } - S.string_size = S.tokenize_end - buf; - -/* Ugly hack: group db requires additional buffer space - * for members[] array. If there is only one group, we need space - * for 3 pointers: alignment padding, group name, NULL. - * +1 for every additional group. - */ - if (n_fields == sizeof(GR_DEF)-1) { /* if we read group file */ - int cnt = 3; - char *p = buf; - while (*p) - if (*p++ == ',') - cnt++; - S.string_size += cnt * sizeof(char*); - buf = xrealloc(buf, S.string_size); - } if (!key) { /* no key specified: sequential read, return a record */ @@ -232,6 +216,24 @@ static char *parse_common(FILE *fp, const char *filename, free(buf); } + S.string_size = S.tokenize_end - buf; +/* + * Ugly hack: group db requires additional buffer space + * for members[] array. If there is only one group, we need space + * for 3 pointers: alignment padding, group name, NULL. + * +1 for every additional group. + */ + if (n_fields == sizeof(GR_DEF)-1) { /* if we read group file... */ + int cnt = 3; + char *p = buf; + while (p < S.tokenize_end) + if (*p++ == ',') + cnt++; + S.string_size += cnt * sizeof(char*); +//bb_error_msg("+%d words = %u key:%s buf:'%s'", cnt, S.string_size, key, buf); + buf = xrealloc(buf, S.string_size); + } + return buf; } -- cgit v1.2.3-55-g6feb From 402451aa19521908125c9aeeb52e91fd4032948b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 3 Jan 2015 21:11:27 +0100 Subject: fix a SEGV in previous commit Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 01058d3a1..2392e7d22 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -223,7 +223,7 @@ static char *parse_common(FILE *fp, const char *filename, * for 3 pointers: alignment padding, group name, NULL. * +1 for every additional group. */ - if (n_fields == sizeof(GR_DEF)-1) { /* if we read group file... */ + if (buf && n_fields == sizeof(GR_DEF)-1) { /* if we read group file... */ int cnt = 3; char *p = buf; while (p < S.tokenize_end) -- cgit v1.2.3-55-g6feb From 12fc86956057ac3e679dd276ba3897260b95157e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 3 Jan 2015 21:16:18 +0100 Subject: libpwdgrp: getgrXXX: empty string is not a member name, do not store it Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 2392e7d22..65fd5261d 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -297,7 +297,10 @@ static void *convert_to_struct(struct passdb *db, ((struct group *)result)->gr_mem = members; while (--i >= 0) { - *members++ = buffer; + if (buffer[0]) { + *members++ = buffer; + // bb_error_msg("member[]='%s'", buffer); + } buffer += strlen(buffer) + 1; } *members = NULL; -- cgit v1.2.3-55-g6feb From 5acf1346b463a41373f5a8c1df424c5f1646a4f9 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 4 Jan 2015 02:02:39 +0100 Subject: libpwdgrp: code shrink function old new delta parse_common 206 205 -1 getgrouplist_internal 229 225 -4 bb_internal_getpwent_r 182 173 -9 getXXnam 232 222 -10 getXXnam_r 216 176 -40 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/5 up/down: 0/-64) Total: -64 bytes Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 65fd5261d..0fd458fdc 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -186,8 +186,7 @@ static int tokenize(char *buffer, int ch) /* Returns !NULL on success and matching line broken up in fields by '\0' in buf. * We require the expected number of fields to be found. */ -static char *parse_common(FILE *fp, const char *filename, - int n_fields, +static char *parse_common(FILE *fp, struct passdb *db, const char *key, int field_pos) { int count = 0; @@ -198,9 +197,9 @@ static char *parse_common(FILE *fp, const char *filename, /* Skip empty lines, comment lines */ if (buf[0] == '\0' || buf[0] == '#') goto free_and_next; - if (tokenize(buf, ':') != n_fields) { + if (tokenize(buf, ':') != db->numfields) { /* number of fields is wrong */ - bb_error_msg("bad record at %s:%u", filename, count); + bb_error_msg("bad record at %s:%u", db->filename, count); goto free_and_next; } @@ -223,7 +222,7 @@ static char *parse_common(FILE *fp, const char *filename, * for 3 pointers: alignment padding, group name, NULL. * +1 for every additional group. */ - if (buf && n_fields == sizeof(GR_DEF)-1) { /* if we read group file... */ + if (buf && db->numfields == sizeof(GR_DEF)-1) { /* if we read group file... */ int cnt = 3; char *p = buf; while (p < S.tokenize_end) @@ -237,15 +236,14 @@ static char *parse_common(FILE *fp, const char *filename, return buf; } -static char *parse_file(const char *filename, - int n_fields, +static char *parse_file(struct passdb *db, const char *key, int field_pos) { char *buf = NULL; - FILE *fp = fopen_for_read(filename); + FILE *fp = fopen_for_read(db->filename); if (fp) { - buf = parse_common(fp, filename, n_fields, key, field_pos); + buf = parse_common(fp, db, key, field_pos); fclose(fp); } return buf; @@ -326,12 +324,10 @@ static int FAST_FUNC getXXnam_r(const char *name, uintptr_t db_and_field_pos, { void *struct_buf = *(void**)result; char *buf; - struct passdb *db; - get_S(); - db = &S.db[db_and_field_pos >> 2]; + struct passdb *db = &get_S()->db[db_and_field_pos >> 2]; *(void**)result = NULL; - buf = parse_file(db->filename, db->numfields, name, 0 /*db_and_field_pos & 3*/); + buf = parse_file(db, name, 0 /*db_and_field_pos & 3*/); /* "db_and_field_pos & 3" is commented out since so far we don't implement * getXXXid_r() functions which would use that to pass 2 here */ if (buf) { @@ -378,9 +374,7 @@ static int FAST_FUNC getXXent_r(void *struct_buf, char *buffer, size_t buflen, unsigned db_idx) { char *buf; - struct passdb *db; - get_S(); - db = &S.db[db_idx]; + struct passdb *db = &get_S()->db[db_idx]; *(void**)result = NULL; @@ -392,7 +386,7 @@ static int FAST_FUNC getXXent_r(void *struct_buf, char *buffer, size_t buflen, close_on_exec_on(fileno(db->fp)); } - buf = parse_common(db->fp, db->filename, db->numfields, /*no search key:*/ NULL, 0); + buf = parse_common(db->fp, db, /*no search key:*/ NULL, 0); if (buf) { size_t size = S.tokenize_end - buf; if (size > buflen) { @@ -421,9 +415,7 @@ static void* FAST_FUNC getXXnam(const char *name, unsigned db_and_field_pos) { char *buf; void *result; - struct passdb *db; - get_S(); - db = &S.db[db_and_field_pos >> 2]; + struct passdb *db = &get_S()->db[db_and_field_pos >> 2]; result = NULL; @@ -435,7 +427,7 @@ static void* FAST_FUNC getXXnam(const char *name, unsigned db_and_field_pos) close_on_exec_on(fileno(db->fp)); } - buf = parse_common(db->fp, db->filename, db->numfields, name, db_and_field_pos & 3); + buf = parse_common(db->fp, db, name, db_and_field_pos & 3); if (buf) { free(db->malloced); /* We enlarge buf and move string data up, freeing space @@ -499,8 +491,6 @@ static gid_t* FAST_FUNC getgrouplist_internal(int *ngroups_ptr, gid_t *group_list; int ngroups; - get_S(); - /* We alloc space for 8 gids at a time. */ group_list = xzalloc(8 * sizeof(group_list[0])); group_list[0] = gid; @@ -508,11 +498,12 @@ static gid_t* FAST_FUNC getgrouplist_internal(int *ngroups_ptr, fp = fopen_for_read(_PATH_GROUP); if (fp) { + struct passdb *db = &get_S()->db[1]; char *buf; - while ((buf = parse_common(fp, _PATH_GROUP, sizeof(GR_DEF)-1, NULL, 0)) != NULL) { + while ((buf = parse_common(fp, db, NULL, 0)) != NULL) { char **m; struct group group; - if (!convert_to_struct(&S.db[1], buf, &group)) + if (!convert_to_struct(db, buf, &group)) goto next; if (group.gr_gid == gid) goto next; -- cgit v1.2.3-55-g6feb From db4d1051ca5e9b4cc1fd2d1872194874f09e3b35 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 4 Jan 2015 02:34:52 +0100 Subject: libpwdgrp: another code shrink function old new delta massage_data_for_r_func - 110 +110 bb_internal_getpwent_r 173 100 -73 getXXnam_r 176 95 -81 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 0/2 up/down: 110/-154) Total: -44 bytes Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 69 ++++++++++++++++++++++++----------------------------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 0fd458fdc..3b96cc4d6 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -316,27 +316,19 @@ static void *convert_to_struct(struct passdb *db, return result; } -/****** getXXnam/id_r */ - -static int FAST_FUNC getXXnam_r(const char *name, uintptr_t db_and_field_pos, +static int massage_data_for_r_func(struct passdb *db, char *buffer, size_t buflen, - void *result) + void **result, + char *buf) { - void *struct_buf = *(void**)result; - char *buf; - struct passdb *db = &get_S()->db[db_and_field_pos >> 2]; - - *(void**)result = NULL; - buf = parse_file(db, name, 0 /*db_and_field_pos & 3*/); - /* "db_and_field_pos & 3" is commented out since so far we don't implement - * getXXXid_r() functions which would use that to pass 2 here */ + void *result_buf = *result; + *result = NULL; if (buf) { - size_t size = S.tokenize_end - buf; - if (size > buflen) { + if (S.string_size > buflen) { errno = ERANGE; } else { - memcpy(buffer, buf, size); - *(void**)result = convert_to_struct(db, buffer, struct_buf); + memcpy(buffer, buf, S.string_size); + *result = convert_to_struct(db, buffer, result_buf); } free(buf); } @@ -347,6 +339,22 @@ static int FAST_FUNC getXXnam_r(const char *name, uintptr_t db_and_field_pos, return errno; } +/****** getXXnam/id_r */ + +static int FAST_FUNC getXXnam_r(const char *name, uintptr_t db_and_field_pos, + char *buffer, size_t buflen, + void *result) +{ + char *buf; + struct passdb *db = &get_S()->db[db_and_field_pos >> 2]; + + buf = parse_file(db, name, 0 /*db_and_field_pos & 3*/); + /* "db_and_field_pos & 3" is commented out since so far we don't implement + * getXXXid_r() functions which would use that to pass 2 here */ + + return massage_data_for_r_func(db, buffer, buflen, result, buf); +} + int FAST_FUNC getpwnam_r(const char *name, struct passwd *struct_buf, char *buffer, size_t buflen, struct passwd **result) @@ -369,15 +377,12 @@ int FAST_FUNC getspnam_r(const char *name, struct spwd *struct_buf, char *buffer /****** getXXent_r */ -static int FAST_FUNC getXXent_r(void *struct_buf, char *buffer, size_t buflen, - void *result, - unsigned db_idx) +static int FAST_FUNC getXXent_r(uintptr_t db_idx, char *buffer, size_t buflen, + void *result) { char *buf; struct passdb *db = &get_S()->db[db_idx]; - *(void**)result = NULL; - if (!db->fp) { db->fp = fopen_for_read(db->filename); if (!db->fp) { @@ -387,26 +392,14 @@ static int FAST_FUNC getXXent_r(void *struct_buf, char *buffer, size_t buflen, } buf = parse_common(db->fp, db, /*no search key:*/ NULL, 0); - if (buf) { - size_t size = S.tokenize_end - buf; - if (size > buflen) { - errno = ERANGE; - } else { - memcpy(buffer, buf, size); - *(void**)result = convert_to_struct(db, buffer, struct_buf); - } - free(buf); - } - /* "The reentrant functions return zero on success. - * In case of error, an error number is returned." - * NB: not finding the record is also a "success" here: - */ - return errno; + return massage_data_for_r_func(db, buffer, buflen, result, buf); } -int FAST_FUNC getpwent_r(struct passwd *struct_buf, char *buffer, size_t buflen, struct passwd **result) +int FAST_FUNC getpwent_r(struct passwd *struct_buf, char *buffer, size_t buflen, + struct passwd **result) { - return getXXent_r(struct_buf, buffer, buflen, result, 0); + *result = struct_buf; + return getXXent_r(0, buffer, buflen, result); } /****** getXXnam/id */ -- cgit v1.2.3-55-g6feb From 4bf88d9094fd208672ba48c809cbcc2753a7d726 Mon Sep 17 00:00:00 2001 From: Adam Tkac Date: Sun, 4 Jan 2015 17:46:08 +0100 Subject: ntpd: relicense ntpd applet to ISC-style license OpenNTPd is licensed under ISC-style license so it's good idea to keep ntpd applet under same license to avoid mess, instead of having our changes to be under GPL. Names of original code's authors are added. Signed-off-by: Adam Tkac Signed-off-by: Denys Vlasenko --- networking/ntpd.c | 51 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/networking/ntpd.c b/networking/ntpd.c index 2d4f076d9..33cb1dcb1 100644 --- a/networking/ntpd.c +++ b/networking/ntpd.c @@ -1,30 +1,43 @@ /* * NTP client/server, based on OpenNTPD 3.9p1 * - * Author: Adam Tkac + * Busybox port author: Adam Tkac (C) 2009 * - * Licensed under GPLv2, see file LICENSE in this source tree. + * OpenNTPd 3.9p1 copyright holders: + * Copyright (c) 2003, 2004 Henning Brauer + * Copyright (c) 2004 Alexander Guy + * + * OpenNTPd code is licensed under ISC-style licence: + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + *********************************************************************** * * Parts of OpenNTPD clock syncronization code is replaced by - * code which is based on ntp-4.2.6, whuch carries the following + * code which is based on ntp-4.2.6, which carries the following * copyright notice: * - *********************************************************************** - * * - * Copyright (c) University of Delaware 1992-2009 * - * * - * Permission to use, copy, modify, and distribute this software and * - * its documentation for any purpose with or without fee is hereby * - * granted, provided that the above copyright notice appears in all * - * copies and that both the copyright notice and this permission * - * notice appear in supporting documentation, and that the name * - * University of Delaware not be used in advertising or publicity * - * pertaining to distribution of the software without specific, * - * written prior permission. The University of Delaware makes no * - * representations about the suitability this software for any * - * purpose. It is provided "as is" without express or implied * - * warranty. * - * * + * Copyright (c) University of Delaware 1992-2009 + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose with or without fee is hereby + * granted, provided that the above copyright notice appears in all + * copies and that both the copyright notice and this permission + * notice appear in supporting documentation, and that the name + * University of Delaware not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. The University of Delaware makes no + * representations about the suitability this software for any + * purpose. It is provided "as is" without express or implied warranty. *********************************************************************** */ -- cgit v1.2.3-55-g6feb From c5d4a04e450b961e354e985058bd69a0f7b7bc2d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 5 Jan 2015 15:09:04 +0100 Subject: libpwdgrp: fixes suggested by Tito, comment tweaks function old new delta bb_internal_getpwent_r 100 121 +21 parse_common 202 203 +1 Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 3b96cc4d6..524acfeab 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -9,13 +9,13 @@ * Rewrite of some parts. Main differences are: * * 1) the buffer for getpwuid, getgrgid, getpwnam, getgrnam is dynamically - * allocated and reused by later calls. + * allocated. * If ENABLE_FEATURE_CLEAN_UP is set the buffers are freed at program * exit using the atexit function to make valgrind happy. * 2) the passwd/group files: * a) must contain the expected number of fields (as per count of field * delimeters ":") or we will complain with a error message. - * b) leading or trailing whitespace in fields is stripped. + * b) leading and trailing whitespace in fields is stripped. * c) some fields are not allowed to be empty (e.g. username, uid/gid, * homedir, shell) and in this case NULL is returned and errno is * set to EINVAL. This behaviour could be easily changed by @@ -23,7 +23,7 @@ * makes a field mandatory). * d) the string representing uid/gid must be convertible by strtoXX * functions, or errno is set to EINVAL. - * e) leading or trailing whitespace in group member names are stripped. + * e) leading and trailing whitespace in group member names is stripped. * 3) the internal function for getgrouplist uses dynamically allocated buffer. * 4) at the moment only the functions really used by busybox code are * implemented, if you need a particular missing function it should be @@ -34,15 +34,15 @@ struct const_passdb { const char *filename; - const char def[7 + 2*ENABLE_USE_BB_SHADOW]; - const uint8_t off[7 + 2*ENABLE_USE_BB_SHADOW]; + char def[7 + 2*ENABLE_USE_BB_SHADOW]; + uint8_t off[7 + 2*ENABLE_USE_BB_SHADOW]; uint8_t numfields; uint8_t size_of; }; struct passdb { const char *filename; - const char def[7 + 2*ENABLE_USE_BB_SHADOW]; - const uint8_t off[7 + 2*ENABLE_USE_BB_SHADOW]; + char def[7 + 2*ENABLE_USE_BB_SHADOW]; + uint8_t off[7 + 2*ENABLE_USE_BB_SHADOW]; uint8_t numfields; uint8_t size_of; FILE *fp; @@ -105,7 +105,7 @@ static const struct const_passdb const_sp_db = { /* We avoid having big global data. */ struct statics { - /* It's ok to use same buffer (db[0].malloced) for getpwuid and getpwnam. + /* We use same buffer (db[0].malloced) for getpwuid and getpwnam. * Manpage says: * "The return value may point to a static area, and may be overwritten * by subsequent calls to getpwent(), getpwnam(), or getpwuid()." @@ -203,7 +203,7 @@ static char *parse_common(FILE *fp, struct passdb *db, goto free_and_next; } - if (!key) { + if (field_pos == -1) { /* no key specified: sequential read, return a record */ break; } @@ -292,7 +292,6 @@ static void *convert_to_struct(struct passdb *db, ( ((intptr_t)S.tokenize_end + sizeof(members[0])) & -(intptr_t)sizeof(members[0]) ); - ((struct group *)result)->gr_mem = members; while (--i >= 0) { if (buffer[0]) { @@ -391,7 +390,9 @@ static int FAST_FUNC getXXent_r(uintptr_t db_idx, char *buffer, size_t buflen, close_on_exec_on(fileno(db->fp)); } - buf = parse_common(db->fp, db, /*no search key:*/ NULL, 0); + buf = parse_common(db->fp, db, /*no search key:*/ NULL, -1); + if (!buf && !errno) + errno = ENOENT; return massage_data_for_r_func(db, buffer, buflen, result, buf); } @@ -493,7 +494,7 @@ static gid_t* FAST_FUNC getgrouplist_internal(int *ngroups_ptr, if (fp) { struct passdb *db = &get_S()->db[1]; char *buf; - while ((buf = parse_common(fp, db, NULL, 0)) != NULL) { + while ((buf = parse_common(fp, db, NULL, -1)) != NULL) { char **m; struct group group; if (!convert_to_struct(db, buf, &group)) @@ -505,7 +506,7 @@ static gid_t* FAST_FUNC getgrouplist_internal(int *ngroups_ptr, continue; group_list = xrealloc_vector(group_list, /*8=2^3:*/ 3, ngroups); group_list[ngroups++] = group.gr_gid; - break; + goto next; } next: free(buf); -- cgit v1.2.3-55-g6feb From 08b90a9d10f2f712c6e16c118328d85930762b92 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 5 Jan 2015 15:12:37 +0100 Subject: deleting unused ntpd_simple.c Signed-off-by: Denys Vlasenko --- networking/ntpd_simple.c | 1005 ---------------------------------------------- 1 file changed, 1005 deletions(-) delete mode 100644 networking/ntpd_simple.c diff --git a/networking/ntpd_simple.c b/networking/ntpd_simple.c deleted file mode 100644 index 2cfbd55d4..000000000 --- a/networking/ntpd_simple.c +++ /dev/null @@ -1,1005 +0,0 @@ -/* - * NTP client/server, based on OpenNTPD 3.9p1 - * - * Author: Adam Tkac - * - * Licensed under GPLv2, see file LICENSE in this source tree. - */ -#include "libbb.h" -#include /* For IPTOS_LOWDELAY definition */ -#include /* setpriority */ -#ifndef IPTOS_LOWDELAY -# define IPTOS_LOWDELAY 0x10 -#endif - - -/* Sync to peers every N secs */ -#define INTERVAL_QUERY_NORMAL 30 -#define INTERVAL_QUERY_PATHETIC 60 -#define INTERVAL_QUERY_AGRESSIVE 5 - -/* Bad if *less than* TRUSTLEVEL_BADPEER */ -#define TRUSTLEVEL_BADPEER 6 -#define TRUSTLEVEL_PATHETIC 2 -#define TRUSTLEVEL_AGRESSIVE 8 -#define TRUSTLEVEL_MAX 10 - -#define QSCALE_OFF_MIN 0.05 -#define QSCALE_OFF_MAX 0.50 - -/* Single query might take N secs max */ -#define QUERYTIME_MAX 15 -/* Min offset for settime at start. "man ntpd" says it's 128 ms */ -#define STEPTIME_MIN_OFFSET 0.128 - -typedef struct { - uint32_t int_partl; - uint32_t fractionl; -} l_fixedpt_t; - -typedef struct { - uint16_t int_parts; - uint16_t fractions; -} s_fixedpt_t; - -enum { - NTP_DIGESTSIZE = 16, - NTP_MSGSIZE_NOAUTH = 48, - NTP_MSGSIZE = (NTP_MSGSIZE_NOAUTH + 4 + NTP_DIGESTSIZE), -}; - -typedef struct { - uint8_t m_status; /* status of local clock and leap info */ - uint8_t m_stratum; /* stratum level */ - uint8_t m_ppoll; /* poll value */ - int8_t m_precision_exp; - s_fixedpt_t m_rootdelay; - s_fixedpt_t m_dispersion; - uint32_t m_refid; - l_fixedpt_t m_reftime; - l_fixedpt_t m_orgtime; - l_fixedpt_t m_rectime; - l_fixedpt_t m_xmttime; - uint32_t m_keyid; - uint8_t m_digest[NTP_DIGESTSIZE]; -} msg_t; - -enum { - NTP_VERSION = 4, - NTP_MAXSTRATUM = 15, - - /* Status Masks */ - MODE_MASK = (7 << 0), - VERSION_MASK = (7 << 3), - VERSION_SHIFT = 3, - LI_MASK = (3 << 6), - - /* Leap Second Codes (high order two bits of m_status) */ - LI_NOWARNING = (0 << 6), /* no warning */ - LI_PLUSSEC = (1 << 6), /* add a second (61 seconds) */ - LI_MINUSSEC = (2 << 6), /* minus a second (59 seconds) */ - LI_ALARM = (3 << 6), /* alarm condition */ - - /* Mode values */ - MODE_RES0 = 0, /* reserved */ - MODE_SYM_ACT = 1, /* symmetric active */ - MODE_SYM_PAS = 2, /* symmetric passive */ - MODE_CLIENT = 3, /* client */ - MODE_SERVER = 4, /* server */ - MODE_BROADCAST = 5, /* broadcast */ - MODE_RES1 = 6, /* reserved for NTP control message */ - MODE_RES2 = 7, /* reserved for private use */ -}; - -#define OFFSET_1900_1970 2208988800UL /* 1970 - 1900 in seconds */ - -typedef struct { - double d_offset; - double d_delay; - //UNUSED: double d_error; - time_t d_rcv_time; - uint32_t d_refid4; - uint8_t d_leap; - uint8_t d_stratum; - uint8_t d_good; -} datapoint_t; - -#define NUM_DATAPOINTS 8 -typedef struct { - len_and_sockaddr *p_lsa; - char *p_dotted; - /* When to send new query (if p_fd == -1) - * or when receive times out (if p_fd >= 0): */ - time_t next_action_time; - int p_fd; - uint8_t p_datapoint_idx; - uint8_t p_trustlevel; - double p_xmttime; - datapoint_t update; - datapoint_t p_datapoint[NUM_DATAPOINTS]; - msg_t p_xmt_msg; -} peer_t; - -enum { - OPT_n = (1 << 0), - OPT_q = (1 << 1), - OPT_N = (1 << 2), - OPT_x = (1 << 3), - /* Insert new options above this line. */ - /* Non-compat options: */ - OPT_p = (1 << 4), - OPT_l = (1 << 5) * ENABLE_FEATURE_NTPD_SERVER, -}; - - -struct globals { - /* total round trip delay to currently selected reference clock */ - double rootdelay; - /* reference timestamp: time when the system clock was last set or corrected */ - double reftime; - llist_t *ntp_peers; -#if ENABLE_FEATURE_NTPD_SERVER - int listen_fd; -#endif - unsigned verbose; - unsigned peer_cnt; - unsigned scale; - uint32_t refid; - uint32_t refid4; - uint8_t synced; - uint8_t leap; -#define G_precision_exp -6 -// int8_t precision_exp; - uint8_t stratum; - uint8_t time_was_stepped; - uint8_t first_adj_done; -}; -#define G (*ptr_to_globals) - -static const int const_IPTOS_LOWDELAY = IPTOS_LOWDELAY; - - -static void -set_next(peer_t *p, unsigned t) -{ - p->next_action_time = time(NULL) + t; -} - -static void -add_peers(char *s) -{ - peer_t *p; - - p = xzalloc(sizeof(*p)); - p->p_lsa = xhost2sockaddr(s, 123); - p->p_dotted = xmalloc_sockaddr2dotted_noport(&p->p_lsa->u.sa); - p->p_fd = -1; - p->p_xmt_msg.m_status = MODE_CLIENT | (NTP_VERSION << 3); - p->p_trustlevel = TRUSTLEVEL_PATHETIC; - p->next_action_time = time(NULL); /* = set_next(p, 0); */ - - llist_add_to(&G.ntp_peers, p); - G.peer_cnt++; -} - -static double -gettime1900d(void) -{ - struct timeval tv; - gettimeofday(&tv, NULL); /* never fails */ - return (tv.tv_sec + 1.0e-6 * tv.tv_usec + OFFSET_1900_1970); -} - -static void -d_to_tv(double d, struct timeval *tv) -{ - tv->tv_sec = (long)d; - tv->tv_usec = (d - tv->tv_sec) * 1000000; -} - -static double -lfp_to_d(l_fixedpt_t lfp) -{ - double ret; - lfp.int_partl = ntohl(lfp.int_partl); - lfp.fractionl = ntohl(lfp.fractionl); - ret = (double)lfp.int_partl + ((double)lfp.fractionl / UINT_MAX); - return ret; -} - -#if 0 //UNUSED -static double -sfp_to_d(s_fixedpt_t sfp) -{ - double ret; - sfp.int_parts = ntohs(sfp.int_parts); - sfp.fractions = ntohs(sfp.fractions); - ret = (double)sfp.int_parts + ((double)sfp.fractions / USHRT_MAX); - return ret; -} -#endif - -#if ENABLE_FEATURE_NTPD_SERVER -static l_fixedpt_t -d_to_lfp(double d) -{ - l_fixedpt_t lfp; - lfp.int_partl = (uint32_t)d; - lfp.fractionl = (uint32_t)((d - lfp.int_partl) * UINT_MAX); - lfp.int_partl = htonl(lfp.int_partl); - lfp.fractionl = htonl(lfp.fractionl); - return lfp; -} - -static s_fixedpt_t -d_to_sfp(double d) -{ - s_fixedpt_t sfp; - sfp.int_parts = (uint16_t)d; - sfp.fractions = (uint16_t)((d - sfp.int_parts) * USHRT_MAX); - sfp.int_parts = htons(sfp.int_parts); - sfp.fractions = htons(sfp.fractions); - return sfp; -} -#endif - -static unsigned -error_interval(void) -{ - unsigned interval, r; - interval = INTERVAL_QUERY_PATHETIC * QSCALE_OFF_MAX / QSCALE_OFF_MIN; - r = (unsigned)rand() % (unsigned)(interval / 10); - return (interval + r); -} - -static int -do_sendto(int fd, - const struct sockaddr *from, const struct sockaddr *to, socklen_t addrlen, - msg_t *msg, ssize_t len) -{ - ssize_t ret; - - errno = 0; - if (!from) { - ret = sendto(fd, msg, len, MSG_DONTWAIT, to, addrlen); - } else { - ret = send_to_from(fd, msg, len, MSG_DONTWAIT, to, from, addrlen); - } - if (ret != len) { - bb_perror_msg("send failed"); - return -1; - } - return 0; -} - -static int -send_query_to_peer(peer_t *p) -{ - // Why do we need to bind()? - // See what happens when we don't bind: - // - // socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3 - // setsockopt(3, SOL_IP, IP_TOS, [16], 4) = 0 - // gettimeofday({1259071266, 327885}, NULL) = 0 - // sendto(3, "xxx", 48, MSG_DONTWAIT, {sa_family=AF_INET, sin_port=htons(123), sin_addr=inet_addr("10.34.32.125")}, 16) = 48 - // ^^^ we sent it from some source port picked by kernel. - // time(NULL) = 1259071266 - // write(2, "ntpd: entering poll 15 secs\n", 28) = 28 - // poll([{fd=3, events=POLLIN}], 1, 15000) = 1 ([{fd=3, revents=POLLIN}]) - // recv(3, "yyy", 68, MSG_DONTWAIT) = 48 - // ^^^ this recv will receive packets to any local port! - // - // Uncomment this and use strace to see it in action: -#define PROBE_LOCAL_ADDR // { len_and_sockaddr lsa; lsa.len = LSA_SIZEOF_SA; getsockname(p->query.fd, &lsa.u.sa, &lsa.len); } - - if (p->p_fd == -1) { - int fd, family; - len_and_sockaddr *local_lsa; - - family = p->p_lsa->u.sa.sa_family; - p->p_fd = fd = xsocket_type(&local_lsa, family, SOCK_DGRAM); - /* local_lsa has "null" address and port 0 now. - * bind() ensures we have a *particular port* selected by kernel - * and remembered in p->p_fd, thus later recv(p->p_fd) - * receives only packets sent to this port. - */ - PROBE_LOCAL_ADDR - xbind(fd, &local_lsa->u.sa, local_lsa->len); - PROBE_LOCAL_ADDR -#if ENABLE_FEATURE_IPV6 - if (family == AF_INET) -#endif - setsockopt(fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY)); - free(local_lsa); - } - - /* - * Send out a random 64-bit number as our transmit time. The NTP - * server will copy said number into the originate field on the - * response that it sends us. This is totally legal per the SNTP spec. - * - * The impact of this is two fold: we no longer send out the current - * system time for the world to see (which may aid an attacker), and - * it gives us a (not very secure) way of knowing that we're not - * getting spoofed by an attacker that can't capture our traffic - * but can spoof packets from the NTP server we're communicating with. - * - * Save the real transmit timestamp locally. - */ - p->p_xmt_msg.m_xmttime.int_partl = rand(); - p->p_xmt_msg.m_xmttime.fractionl = rand(); - p->p_xmttime = gettime1900d(); - - if (do_sendto(p->p_fd, /*from:*/ NULL, /*to:*/ &p->p_lsa->u.sa, /*addrlen:*/ p->p_lsa->len, - &p->p_xmt_msg, NTP_MSGSIZE_NOAUTH) == -1 - ) { - close(p->p_fd); - p->p_fd = -1; - set_next(p, INTERVAL_QUERY_PATHETIC); - return -1; - } - - if (G.verbose) - bb_error_msg("sent query to %s", p->p_dotted); - set_next(p, QUERYTIME_MAX); - - return 0; -} - - -/* Time is stepped only once, when the first packet from a peer is received. - */ -static void -step_time_once(double offset) -{ - double dtime; - llist_t *item; - struct timeval tv; - char buf[80]; - time_t tval; - - if (G.time_was_stepped) - goto bail; - G.time_was_stepped = 1; - - /* if the offset is small, don't step, slew (later) */ - if (offset < STEPTIME_MIN_OFFSET && offset > -STEPTIME_MIN_OFFSET) - goto bail; - - gettimeofday(&tv, NULL); /* never fails */ - dtime = offset + tv.tv_sec; - dtime += 1.0e-6 * tv.tv_usec; - d_to_tv(dtime, &tv); - - if (settimeofday(&tv, NULL) == -1) - bb_perror_msg_and_die("settimeofday"); - - tval = tv.tv_sec; - strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &tval); - - bb_error_msg("setting clock to %s (offset %fs)", buf, offset); - - for (item = G.ntp_peers; item != NULL; item = item->link) { - peer_t *p = (peer_t *) item->data; - p->next_action_time -= (time_t)offset; - } - - bail: - if (option_mask32 & OPT_q) - exit(0); -} - - -/* Time is periodically slewed when we collect enough - * good data points. - */ -static int -compare_offsets(const void *aa, const void *bb) -{ - const peer_t *const *a = aa; - const peer_t *const *b = bb; - if ((*a)->update.d_offset < (*b)->update.d_offset) - return -1; - return ((*a)->update.d_offset > (*b)->update.d_offset); -} -static unsigned -updated_scale(double offset) -{ - if (offset < 0) - offset = -offset; - if (offset > QSCALE_OFF_MAX) - return 1; - if (offset < QSCALE_OFF_MIN) - return QSCALE_OFF_MAX / QSCALE_OFF_MIN; - return QSCALE_OFF_MAX / offset; -} -static void -slew_time(void) -{ - llist_t *item; - double offset_median; - struct timeval tv; - - { - peer_t **peers = xzalloc(sizeof(peers[0]) * G.peer_cnt); - unsigned goodpeer_cnt = 0; - unsigned middle; - - for (item = G.ntp_peers; item != NULL; item = item->link) { - peer_t *p = (peer_t *) item->data; - if (p->p_trustlevel < TRUSTLEVEL_BADPEER) - continue; - if (!p->update.d_good) { - free(peers); - return; - } - peers[goodpeer_cnt++] = p; - } - - if (goodpeer_cnt == 0) { - free(peers); - goto clear_good; - } - - qsort(peers, goodpeer_cnt, sizeof(peers[0]), compare_offsets); - - middle = goodpeer_cnt / 2; - if (middle != 0 && (goodpeer_cnt & 1) == 0) { - offset_median = (peers[middle-1]->update.d_offset + peers[middle]->update.d_offset) / 2; - G.rootdelay = (peers[middle-1]->update.d_delay + peers[middle]->update.d_delay) / 2; - G.stratum = 1 + MAX(peers[middle-1]->update.d_stratum, peers[middle]->update.d_stratum); - } else { - offset_median = peers[middle]->update.d_offset; - G.rootdelay = peers[middle]->update.d_delay; - G.stratum = 1 + peers[middle]->update.d_stratum; - } - G.leap = peers[middle]->update.d_leap; - G.refid4 = peers[middle]->update.d_refid4; - G.refid = -#if ENABLE_FEATURE_IPV6 - peers[middle]->p_lsa->u.sa.sa_family != AF_INET ? - G.refid4 : -#endif - peers[middle]->p_lsa->u.sin.sin_addr.s_addr; - free(peers); - } -//TODO: if (offset_median > BIG) step_time(offset_median)? - - G.scale = updated_scale(offset_median); - - bb_error_msg("adjusting clock by %fs, our stratum is %u, time scale %u", - offset_median, G.stratum, G.scale); - - errno = 0; - d_to_tv(offset_median, &tv); - if (adjtime(&tv, &tv) == -1) - bb_perror_msg_and_die("adjtime failed"); - if (G.verbose >= 2) - bb_error_msg("old adjust: %d.%06u", (int)tv.tv_sec, (unsigned)tv.tv_usec); - - if (G.first_adj_done) { - uint8_t synced = (tv.tv_sec == 0 && tv.tv_usec == 0); - if (synced != G.synced) { - G.synced = synced; - bb_error_msg("clock is %ssynced", synced ? "" : "un"); - } - } - G.first_adj_done = 1; - - G.reftime = gettime1900d(); - - clear_good: - for (item = G.ntp_peers; item != NULL; item = item->link) { - peer_t *p = (peer_t *) item->data; - p->update.d_good = 0; - } -} - -static void -update_peer_data(peer_t *p) -{ - /* Clock filter. - * Find the datapoint with the lowest delay. - * Use that as the peer update. - * Invalidate it and all older ones. - */ - int i; - int best = -1; - int good = 0; - - for (i = 0; i < NUM_DATAPOINTS; i++) { - if (p->p_datapoint[i].d_good) { - good++; - if (best < 0 || p->p_datapoint[i].d_delay < p->p_datapoint[best].d_delay) - best = i; - } - } - - if (good < 8) //FIXME: was it meant to be NUM_DATAPOINTS, not 8? - return; - - p->update = p->p_datapoint[best]; /* struct copy */ - slew_time(); - - for (i = 0; i < NUM_DATAPOINTS; i++) - if (p->p_datapoint[i].d_rcv_time <= p->p_datapoint[best].d_rcv_time) - p->p_datapoint[i].d_good = 0; -} - -static unsigned -scale_interval(unsigned requested) -{ - unsigned interval, r; - interval = requested * G.scale; - r = (unsigned)rand() % (unsigned)(MAX(5, interval / 10)); - return (interval + r); -} -static void -recv_and_process_peer_pkt(peer_t *p) -{ - ssize_t size; - msg_t msg; - double T1, T2, T3, T4; - unsigned interval; - datapoint_t *datapoint; - - /* We can recvfrom here and check from.IP, but some multihomed - * ntp servers reply from their *other IP*. - * TODO: maybe we should check at least what we can: from.port == 123? - */ - size = recv(p->p_fd, &msg, sizeof(msg), MSG_DONTWAIT); - if (size == -1) { - bb_perror_msg("recv(%s) error", p->p_dotted); - if (errno == EHOSTUNREACH || errno == EHOSTDOWN - || errno == ENETUNREACH || errno == ENETDOWN - || errno == ECONNREFUSED || errno == EADDRNOTAVAIL - || errno == EAGAIN - ) { -//TODO: always do this? - set_next(p, error_interval()); - goto close_sock; - } - xfunc_die(); - } - - if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) { - bb_error_msg("malformed packet received from %s", p->p_dotted); - goto bail; - } - - if (msg.m_orgtime.int_partl != p->p_xmt_msg.m_xmttime.int_partl - || msg.m_orgtime.fractionl != p->p_xmt_msg.m_xmttime.fractionl - ) { - goto bail; - } - - if ((msg.m_status & LI_ALARM) == LI_ALARM - || msg.m_stratum == 0 - || msg.m_stratum > NTP_MAXSTRATUM - ) { -// TODO: stratum 0 responses may have commands in 32-bit m_refid field: -// "DENY", "RSTR" - peer does not like us at all -// "RATE" - peer is overloaded, reduce polling freq - interval = error_interval(); - bb_error_msg("reply from %s: not synced, next query in %us", p->p_dotted, interval); - goto close_sock; - } - - /* - * From RFC 2030 (with a correction to the delay math): - * - * Timestamp Name ID When Generated - * ------------------------------------------------------------ - * Originate Timestamp T1 time request sent by client - * Receive Timestamp T2 time request received by server - * Transmit Timestamp T3 time reply sent by server - * Destination Timestamp T4 time reply received by client - * - * The roundtrip delay and local clock offset are defined as - * - * delay = (T4 - T1) - (T3 - T2); offset = ((T2 - T1) + (T3 - T4)) / 2 - */ - T1 = p->p_xmttime; - T2 = lfp_to_d(msg.m_rectime); - T3 = lfp_to_d(msg.m_xmttime); - T4 = gettime1900d(); - - datapoint = &p->p_datapoint[p->p_datapoint_idx]; - - datapoint->d_offset = ((T2 - T1) + (T3 - T4)) / 2; - datapoint->d_delay = (T4 - T1) - (T3 - T2); - if (datapoint->d_delay < 0) { - bb_error_msg("reply from %s: negative delay %f", p->p_dotted, datapoint->d_delay); - interval = error_interval(); - set_next(p, interval); - goto close_sock; - } - //UNUSED: datapoint->d_error = (T2 - T1) - (T3 - T4); - datapoint->d_rcv_time = (time_t)(T4 - OFFSET_1900_1970); /* = time(NULL); */ - datapoint->d_good = 1; - - datapoint->d_leap = (msg.m_status & LI_MASK); - //UNUSED: datapoint->o_precision = msg.m_precision_exp; - //UNUSED: datapoint->o_rootdelay = sfp_to_d(msg.m_rootdelay); - //UNUSED: datapoint->o_rootdispersion = sfp_to_d(msg.m_dispersion); - //UNUSED: datapoint->d_refid = ntohl(msg.m_refid); - datapoint->d_refid4 = msg.m_xmttime.fractionl; - //UNUSED: datapoint->o_reftime = lfp_to_d(msg.m_reftime); - //UNUSED: datapoint->o_poll = msg.m_ppoll; - datapoint->d_stratum = msg.m_stratum; - - if (p->p_trustlevel < TRUSTLEVEL_PATHETIC) - interval = scale_interval(INTERVAL_QUERY_PATHETIC); - else if (p->p_trustlevel < TRUSTLEVEL_AGRESSIVE) - interval = scale_interval(INTERVAL_QUERY_AGRESSIVE); - else - interval = scale_interval(INTERVAL_QUERY_NORMAL); - - set_next(p, interval); - - /* Every received reply which we do not discard increases trust */ - if (p->p_trustlevel < TRUSTLEVEL_MAX) { - p->p_trustlevel++; - if (p->p_trustlevel == TRUSTLEVEL_BADPEER) - bb_error_msg("peer %s now valid", p->p_dotted); - } - - if (G.verbose) - bb_error_msg("reply from %s: offset %f delay %f, next query in %us", p->p_dotted, - datapoint->d_offset, datapoint->d_delay, interval); - - update_peer_data(p); -//TODO: do it after all peers had a chance to return at least one reply? - step_time_once(datapoint->d_offset); - - p->p_datapoint_idx++; - if (p->p_datapoint_idx >= NUM_DATAPOINTS) - p->p_datapoint_idx = 0; - - close_sock: - /* We do not expect any more packets from this peer for now. - * Closing the socket informs kernel about it. - * We open a new socket when we send a new query. - */ - close(p->p_fd); - p->p_fd = -1; - bail: - return; -} - -#if ENABLE_FEATURE_NTPD_SERVER -static void -recv_and_process_client_pkt(void /*int fd*/) -{ - ssize_t size; - uint8_t version; - double rectime; - len_and_sockaddr *to; - struct sockaddr *from; - msg_t msg; - uint8_t query_status; - uint8_t query_ppoll; - l_fixedpt_t query_xmttime; - - to = get_sock_lsa(G.listen_fd); - from = xzalloc(to->len); - - size = recv_from_to(G.listen_fd, &msg, sizeof(msg), MSG_DONTWAIT, from, &to->u.sa, to->len); - if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) { - char *addr; - if (size < 0) { - if (errno == EAGAIN) - goto bail; - bb_perror_msg_and_die("recv"); - } - addr = xmalloc_sockaddr2dotted_noport(from); - bb_error_msg("malformed packet received from %s: size %u", addr, (int)size); - free(addr); - goto bail; - } - - query_status = msg.m_status; - query_ppoll = msg.m_ppoll; - query_xmttime = msg.m_xmttime; - - /* Build a reply packet */ - memset(&msg, 0, sizeof(msg)); - msg.m_status = G.synced ? G.leap : LI_ALARM; - msg.m_status |= (query_status & VERSION_MASK); - msg.m_status |= ((query_status & MODE_MASK) == MODE_CLIENT) ? - MODE_SERVER : MODE_SYM_PAS; - msg.m_stratum = G.stratum; - msg.m_ppoll = query_ppoll; - msg.m_precision_exp = G_precision_exp; - rectime = gettime1900d(); - msg.m_xmttime = msg.m_rectime = d_to_lfp(rectime); - msg.m_reftime = d_to_lfp(G.reftime); - //msg.m_xmttime = d_to_lfp(gettime1900d()); // = msg.m_rectime - msg.m_orgtime = query_xmttime; - msg.m_rootdelay = d_to_sfp(G.rootdelay); - version = (query_status & VERSION_MASK); /* ... >> VERSION_SHIFT - done below instead */ - msg.m_refid = (version > (3 << VERSION_SHIFT)) ? G.refid4 : G.refid; - - /* We reply from the local address packet was sent to, - * this makes to/from look swapped here: */ - do_sendto(G.listen_fd, - /*from:*/ &to->u.sa, /*to:*/ from, /*addrlen:*/ to->len, - &msg, size); - - bail: - free(to); - free(from); -} -#endif - -/* Upstream ntpd's options: - * - * -4 Force DNS resolution of host names to the IPv4 namespace. - * -6 Force DNS resolution of host names to the IPv6 namespace. - * -a Require cryptographic authentication for broadcast client, - * multicast client and symmetric passive associations. - * This is the default. - * -A Do not require cryptographic authentication for broadcast client, - * multicast client and symmetric passive associations. - * This is almost never a good idea. - * -b Enable the client to synchronize to broadcast servers. - * -c conffile - * Specify the name and path of the configuration file, - * default /etc/ntp.conf - * -d Specify debugging mode. This option may occur more than once, - * with each occurrence indicating greater detail of display. - * -D level - * Specify debugging level directly. - * -f driftfile - * Specify the name and path of the frequency file. - * This is the same operation as the "driftfile FILE" - * configuration command. - * -g Normally, ntpd exits with a message to the system log - * if the offset exceeds the panic threshold, which is 1000 s - * by default. This option allows the time to be set to any value - * without restriction; however, this can happen only once. - * If the threshold is exceeded after that, ntpd will exit - * with a message to the system log. This option can be used - * with the -q and -x options. See the tinker command for other options. - * -i jaildir - * Chroot the server to the directory jaildir. This option also implies - * that the server attempts to drop root privileges at startup - * (otherwise, chroot gives very little additional security). - * You may need to also specify a -u option. - * -k keyfile - * Specify the name and path of the symmetric key file, - * default /etc/ntp/keys. This is the same operation - * as the "keys FILE" configuration command. - * -l logfile - * Specify the name and path of the log file. The default - * is the system log file. This is the same operation as - * the "logfile FILE" configuration command. - * -L Do not listen to virtual IPs. The default is to listen. - * -n Don't fork. - * -N To the extent permitted by the operating system, - * run the ntpd at the highest priority. - * -p pidfile - * Specify the name and path of the file used to record the ntpd - * process ID. This is the same operation as the "pidfile FILE" - * configuration command. - * -P priority - * To the extent permitted by the operating system, - * run the ntpd at the specified priority. - * -q Exit the ntpd just after the first time the clock is set. - * This behavior mimics that of the ntpdate program, which is - * to be retired. The -g and -x options can be used with this option. - * Note: The kernel time discipline is disabled with this option. - * -r broadcastdelay - * Specify the default propagation delay from the broadcast/multicast - * server to this client. This is necessary only if the delay - * cannot be computed automatically by the protocol. - * -s statsdir - * Specify the directory path for files created by the statistics - * facility. This is the same operation as the "statsdir DIR" - * configuration command. - * -t key - * Add a key number to the trusted key list. This option can occur - * more than once. - * -u user[:group] - * Specify a user, and optionally a group, to switch to. - * -v variable - * -V variable - * Add a system variable listed by default. - * -x Normally, the time is slewed if the offset is less than the step - * threshold, which is 128 ms by default, and stepped if above - * the threshold. This option sets the threshold to 600 s, which is - * well within the accuracy window to set the clock manually. - * Note: since the slew rate of typical Unix kernels is limited - * to 0.5 ms/s, each second of adjustment requires an amortization - * interval of 2000 s. Thus, an adjustment as much as 600 s - * will take almost 14 days to complete. This option can be used - * with the -g and -q options. See the tinker command for other options. - * Note: The kernel time discipline is disabled with this option. - */ - -/* By doing init in a separate function we decrease stack usage - * in main loop. - */ -static NOINLINE void ntp_init(char **argv) -{ - unsigned opts; - llist_t *peers; - - srand(getpid()); - - if (getuid()) - bb_error_msg_and_die(bb_msg_you_must_be_root); - - peers = NULL; - opt_complementary = "dd:p::"; /* d: counter, p: list */ - opts = getopt32(argv, - "nqNx" /* compat */ - "p:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */ - "d" /* compat */ - "46aAbgL", /* compat, ignored */ - &peers, &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; - while (peers) - add_peers(llist_pop(&peers)); - if (!(opts & OPT_n)) { - bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO, argv); - logmode = LOGMODE_NONE; - } -#if ENABLE_FEATURE_NTPD_SERVER - G.listen_fd = -1; - if (opts & OPT_l) { - G.listen_fd = create_and_bind_dgram_or_die(NULL, 123); - socket_want_pktinfo(G.listen_fd); - setsockopt(G.listen_fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY)); - } -#endif - /* I hesitate to set -20 prio. -15 should be high enough for timekeeping */ - if (opts & OPT_N) - setpriority(PRIO_PROCESS, 0, -15); - - /* Set some globals */ -#if 0 - /* With constant b = 100, G.precision_exp is also constant -6. - * Uncomment this and you'll see */ - { - int prec = 0; - int b; -# if 0 - struct timespec tp; - /* We can use sys_clock_getres but assuming 10ms tick should be fine */ - clock_getres(CLOCK_REALTIME, &tp); - tp.tv_sec = 0; - tp.tv_nsec = 10000000; - b = 1000000000 / tp.tv_nsec; /* convert to Hz */ -# else - b = 100; /* b = 1000000000/10000000 = 100 */ -# endif - while (b > 1) - prec--, b >>= 1; - //G.precision_exp = prec; - bb_error_msg("G.precision_exp:%d", prec); /* -6 */ - } -#endif - G.scale = 1; - - bb_signals((1 << SIGTERM) | (1 << SIGINT), record_signo); - bb_signals((1 << SIGPIPE) | (1 << SIGHUP), SIG_IGN); -} - -int ntpd_main(int argc UNUSED_PARAM, char **argv) MAIN_EXTERNALLY_VISIBLE; -int ntpd_main(int argc UNUSED_PARAM, char **argv) -{ - struct globals g; - struct pollfd *pfd; - peer_t **idx2peer; - - memset(&g, 0, sizeof(g)); - SET_PTR_TO_GLOBALS(&g); - - ntp_init(argv); - - { - /* if ENABLE_FEATURE_NTPD_SERVER, + 1 for listen_fd: */ - unsigned cnt = g.peer_cnt + ENABLE_FEATURE_NTPD_SERVER; - idx2peer = xzalloc(sizeof(idx2peer[0]) * cnt); - pfd = xzalloc(sizeof(pfd[0]) * cnt); - } - - while (!bb_got_signal) { - llist_t *item; - unsigned i, j; - unsigned sent_cnt, trial_cnt; - int nfds, timeout; - time_t cur_time, nextaction; - - /* Nothing between here and poll() blocks for any significant time */ - - cur_time = time(NULL); - nextaction = cur_time + 3600; - - i = 0; -#if ENABLE_FEATURE_NTPD_SERVER - if (g.listen_fd != -1) { - pfd[0].fd = g.listen_fd; - pfd[0].events = POLLIN; - i++; - } -#endif - /* Pass over peer list, send requests, time out on receives */ - sent_cnt = trial_cnt = 0; - for (item = g.ntp_peers; item != NULL; item = item->link) { - peer_t *p = (peer_t *) item->data; - - /* Overflow-safe "if (p->next_action_time <= cur_time) ..." */ - if ((int)(cur_time - p->next_action_time) >= 0) { - if (p->p_fd == -1) { - /* Time to send new req */ - trial_cnt++; - if (send_query_to_peer(p) == 0) - sent_cnt++; - } else { - /* Timed out waiting for reply */ - close(p->p_fd); - p->p_fd = -1; - timeout = error_interval(); - bb_error_msg("timed out waiting for %s, " - "next query in %us", p->p_dotted, timeout); - if (p->p_trustlevel >= TRUSTLEVEL_BADPEER) { - p->p_trustlevel /= 2; - if (p->p_trustlevel < TRUSTLEVEL_BADPEER) - bb_error_msg("peer %s now invalid", p->p_dotted); - } - set_next(p, timeout); - } - } - - if (p->next_action_time < nextaction) - nextaction = p->next_action_time; - - if (p->p_fd >= 0) { - /* Wait for reply from this peer */ - pfd[i].fd = p->p_fd; - pfd[i].events = POLLIN; - idx2peer[i] = p; - i++; - } - } - - if ((trial_cnt > 0 && sent_cnt == 0) || g.peer_cnt == 0) - step_time_once(0); /* no good peers, don't wait */ - - timeout = nextaction - cur_time; - if (timeout < 1) - timeout = 1; - - /* Here we may block */ - if (g.verbose >= 2) - bb_error_msg("poll %us, sockets:%u", timeout, i); - nfds = poll(pfd, i, timeout * 1000); - if (nfds <= 0) - continue; - - /* Process any received packets */ - j = 0; -#if ENABLE_FEATURE_NTPD_SERVER - if (g.listen_fd != -1) { - if (pfd[0].revents /* & (POLLIN|POLLERR)*/) { - nfds--; - recv_and_process_client_pkt(/*g.listen_fd*/); - } - j = 1; - } -#endif - for (; nfds != 0 && j < i; j++) { - if (pfd[j].revents /* & (POLLIN|POLLERR)*/) { - nfds--; - recv_and_process_peer_pkt(idx2peer[j]); - } - } - } /* while (!bb_got_signal) */ - - kill_myself_with_sig(bb_got_signal); -} -- cgit v1.2.3-55-g6feb From da9212667c99f2f2121747c4715d067deb7c155b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 5 Jan 2015 15:37:58 +0100 Subject: libbb: code shrink by factoring out common update_utmp_DEAD_PROCESS function old new delta update_utmp_DEAD_PROCESS - 17 +17 telnetd_main 1685 1674 -11 mark_terminated 56 45 -11 handle_sigchld 74 63 -11 Signed-off-by: Denys Vlasenko --- include/libbb.h | 2 ++ init/init.c | 6 +----- libbb/utmp.c | 14 ++++++++++++++ loginutils/login.c | 2 +- networking/telnetd.c | 12 ++---------- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/include/libbb.h b/include/libbb.h index 68a7cf002..be792d6b2 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -921,9 +921,11 @@ void die_if_bad_username(const char* name) FAST_FUNC; #if ENABLE_FEATURE_UTMP void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); +void FAST_FUNC update_utmp_DEAD_PROCESS(pid_t pid); #else # define write_new_utmp(pid, new_type, tty_name, username, hostname) ((void)0) # define update_utmp(pid, new_type, tty_name, username, hostname) ((void)0) +# define update_utmp_DEAD_PROCESS(pid) ((void)0) #endif diff --git a/init/init.c b/init/init.c index d99d68ce4..b2fe85635 100644 --- a/init/init.c +++ b/init/init.c @@ -538,11 +538,7 @@ static struct init_action *mark_terminated(pid_t pid) struct init_action *a; if (pid > 0) { - update_utmp(pid, DEAD_PROCESS, - /*tty_name:*/ NULL, - /*username:*/ NULL, - /*hostname:*/ NULL - ); + update_utmp_DEAD_PROCESS(pid); for (a = init_action_list; a; a = a->next) { if (a->pid == pid) { a->pid = 0; diff --git a/libbb/utmp.c b/libbb/utmp.c index 09443fb6c..8ad9ba27e 100644 --- a/libbb/utmp.c +++ b/libbb/utmp.c @@ -130,3 +130,17 @@ void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const updwtmp(bb_path_wtmp_file, &utent); #endif } + +/* man utmp: + * When init(8) finds that a process has exited, it locates its utmp entry + * by ut_pid, sets ut_type to DEAD_PROCESS, and clears ut_user, ut_host + * and ut_time with null bytes. + * [same applies to other processes which maintain utmp entries, like telnetd] + * + * We do not bother actually clearing fields: + * it might be interesting to know who was logged in and from where + */ +void FAST_FUNC update_utmp_DEAD_PROCESS(pid_t pid) +{ + update_utmp(pid, DEAD_PROCESS, NULL, NULL, NULL); +} diff --git a/loginutils/login.c b/loginutils/login.c index a4b19ccfc..b9d910331 100644 --- a/loginutils/login.c +++ b/loginutils/login.c @@ -454,7 +454,7 @@ int login_main(int argc UNUSED_PARAM, char **argv) else { if (safe_waitpid(child_pid, NULL, 0) == -1) bb_perror_msg("waitpid"); - update_utmp(child_pid, DEAD_PROCESS, NULL, NULL, NULL); + update_utmp_DEAD_PROCESS(child_pid); } IF_PAM(login_pam_end(pamh);) return 0; diff --git a/networking/telnetd.c b/networking/telnetd.c index 9e7a84cce..6aee95871 100644 --- a/networking/telnetd.c +++ b/networking/telnetd.c @@ -462,15 +462,7 @@ static void handle_sigchld(int sig UNUSED_PARAM) while (ts) { if (ts->shell_pid == pid) { ts->shell_pid = -1; -// man utmp: -// When init(8) finds that a process has exited, it locates its utmp entry -// by ut_pid, sets ut_type to DEAD_PROCESS, and clears ut_user, ut_host -// and ut_time with null bytes. -// [same applies to other processes which maintain utmp entries, like telnetd] -// -// We do not bother actually clearing fields: -// it might be interesting to know who was logged in and from where - update_utmp(pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL); + update_utmp_DEAD_PROCESS(pid); break; } ts = ts->next; @@ -739,7 +731,7 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv) continue; kill_session: if (ts->shell_pid > 0) - update_utmp(ts->shell_pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL); + update_utmp_DEAD_PROCESS(ts->shell_pid); free_session(ts); ts = next; } -- cgit v1.2.3-55-g6feb From 75a1403f266098bc4a498140473e1007075f459d Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Mon, 5 Jan 2015 18:34:53 +0100 Subject: free: handle "cached" value too function old new delta free_main 356 481 +125 Signed-off-by: Guillermo Rodriguez Signed-off-by: Denys Vlasenko --- procps/free.c | 77 +++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/procps/free.c b/procps/free.c index 47f2fc3b2..0d023f740 100644 --- a/procps/free.c +++ b/procps/free.c @@ -44,11 +44,28 @@ static unsigned long long scale(unsigned long d) return ((unsigned long long)d * G.mem_unit) >> G_unit_steps; } +static unsigned long parse_cached_kb(void) +{ + char buf[60]; /* actual lines we expect are ~30 chars or less */ + FILE *fp; + unsigned long cached = 0; + + fp = xfopen_for_read("/proc/meminfo"); + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (sscanf(buf, "Cached: %lu %*s\n", &cached) == 1) + break; + } + if (ENABLE_FEATURE_CLEAN_UP) + fclose(fp); + + return cached; +} int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM)) { struct sysinfo info; + unsigned long long cached; INIT_G(); @@ -73,49 +90,47 @@ int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM)) } } #endif - - sysinfo(&info); - - /* Kernels prior to 2.4.x will return info.mem_unit==0, so cope... */ - G.mem_unit = (info.mem_unit ? info.mem_unit : 1); - - printf(" %13s%13s%13s%13s%13s\n", + printf(" %11s%11s%11s%11s%11s%11s\n" + "Mem: ", "total", "used", "free", - "shared", "buffers" /* swap and total don't have these columns */ - /* procps version 3.2.8 also shows "cached" column, but - * sysinfo() does not provide this value, need to parse - * /proc/meminfo instead and get "Cached: NNN kB" from there. - */ + "shared", "buffers", "cached" /* swap and total don't have these columns */ ); -#define FIELDS_5 "%13llu%13llu%13llu%13llu%13llu\n" -#define FIELDS_3 (FIELDS_5 + 2*6) -#define FIELDS_2 (FIELDS_5 + 3*6) + sysinfo(&info); + /* Kernels prior to 2.4.x will return info.mem_unit==0, so cope... */ + G.mem_unit = (info.mem_unit ? info.mem_unit : 1); + /* Extract cached from /proc/meminfo and convert to mem_units */ + cached = ((unsigned long long) parse_cached_kb() * 1024) / G.mem_unit; + +#define FIELDS_6 "%11llu%11llu%11llu%11llu%11llu%11llu\n" +#define FIELDS_3 (FIELDS_6 + 3*6) +#define FIELDS_2 (FIELDS_6 + 4*6) - printf("Mem: "); - printf(FIELDS_5, - scale(info.totalram), - scale(info.totalram - info.freeram), - scale(info.freeram), - scale(info.sharedram), - scale(info.bufferram) + printf(FIELDS_6, + scale(info.totalram), //total + scale(info.totalram - info.freeram), //used + scale(info.freeram), //free + scale(info.sharedram), //shared + scale(info.bufferram), //buffers + scale(cached) //cached ); /* Show alternate, more meaningful busy/free numbers by counting - * buffer cache as free memory (make it "-/+ buffers/cache" - * if/when we add support for "cached" column): */ - printf("-/+ buffers: "); + * buffer cache as free memory. */ + printf("-/+ buffers/cache:"); + cached += info.freeram; + cached += info.bufferram; printf(FIELDS_2, - scale(info.totalram - info.freeram - info.bufferram), - scale(info.freeram + info.bufferram) + scale(info.totalram - cached), //used + scale(cached) //free ); #if BB_MMU - printf("Swap:"); + printf("Swap: "); printf(FIELDS_3, - scale(info.totalswap), - scale(info.totalswap - info.freeswap), - scale(info.freeswap) + scale(info.totalswap), //total + scale(info.totalswap - info.freeswap), //used + scale(info.freeswap) //free ); #endif return EXIT_SUCCESS; -- cgit v1.2.3-55-g6feb From 1ec49732e2b477c45599f1a33953b4390e8c43e5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 5 Jan 2015 18:39:23 +0100 Subject: mpstat: code shrink function old new delta get_uptime 134 129 -5 get_irqs_from_stat 110 103 -7 Signed-off-by: Denys Vlasenko --- procps/mpstat.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/procps/mpstat.c b/procps/mpstat.c index c628d6215..af3263d67 100644 --- a/procps/mpstat.c +++ b/procps/mpstat.c @@ -522,9 +522,7 @@ static void get_irqs_from_stat(struct stats_irq *irq) FILE *fp; char buf[1024]; - fp = fopen_for_read(PROCFS_STAT); - if (!fp) - return; + fp = xfopen_for_read(PROCFS_STAT); while (fgets(buf, sizeof(buf), fp)) { //bb_error_msg("/proc/stat:'%s'", buf); @@ -644,9 +642,7 @@ static void get_uptime(data_t *uptime) char buf[sizeof(long)*3 * 2 + 4]; /* enough for long.long */ unsigned long uptime_sec, decimal; - fp = fopen_for_read(PROCFS_UPTIME); - if (!fp) - return; + fp = xfopen_for_read(PROCFS_UPTIME); if (fgets(buf, sizeof(buf), fp)) { if (sscanf(buf, "%lu.%lu", &uptime_sec, &decimal) == 2) { *uptime = (data_t)uptime_sec * G.hz + decimal * G.hz / 100; -- cgit v1.2.3-55-g6feb From e76957708b6a158fd25f7298a4bf2cc5583a974a Mon Sep 17 00:00:00 2001 From: René Rhéaume Date: Mon, 5 Jan 2015 20:35:00 +0100 Subject: swaponoff: add support for -e MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: René Rhéaume Signed-off-by: Denys Vlasenko --- util-linux/swaponoff.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/util-linux/swaponoff.c b/util-linux/swaponoff.c index 75487267b..5cd1fbe70 100644 --- a/util-linux/swaponoff.c +++ b/util-linux/swaponoff.c @@ -8,7 +8,7 @@ */ //usage:#define swapon_trivial_usage -//usage: "[-a]" IF_FEATURE_SWAPON_DISCARD(" [-d[POL]]") IF_FEATURE_SWAPON_PRI(" [-p PRI]") " [DEVICE]" +//usage: "[-a] [-e]" 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" @@ -16,15 +16,17 @@ //usage: "\n -d[POL] Discard blocks at swapon (POL=once)," //usage: "\n as freed (POL=pages), or both (POL omitted)" //usage: ) +//usage: "\n -e Silently skip devices that do not exist" //usage: IF_FEATURE_SWAPON_PRI( //usage: "\n -p PRI Set swap device priority" //usage: ) //usage: //usage:#define swapoff_trivial_usage -//usage: "[-a] [DEVICE]" +//usage: "[-a] [-e] [DEVICE]" //usage:#define swapoff_full_usage "\n\n" //usage: "Stop swapping on DEVICE\n" //usage: "\n -a Stop swapping on all swap devices" +//usage: "\n -e Silently skip devices that do not exist" #include "libbb.h" #include @@ -77,15 +79,18 @@ struct globals { /* Command line options */ enum { OPTBIT_a, /* -a all */ + OPTBIT_e, /* -e ifexists */ IF_FEATURE_SWAPON_DISCARD( OPTBIT_d ,) /* -d discard */ IF_FEATURE_SWAPON_PRI ( OPTBIT_p ,) /* -p priority */ OPT_a = 1 << OPTBIT_a, + OPT_e = 1 << OPTBIT_e, 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_IFEXISTS (option_mask32 & OPT_e) #define OPT_PRIO (option_mask32 & OPT_p) static int swap_enable_disable(char *device) @@ -95,6 +100,8 @@ static int swap_enable_disable(char *device) struct stat st; resolve_mount_spec(&device); + if (!OPT_IFEXISTS) + xstat(device, &st); if (do_swapoff) { err = swapoff(device); @@ -112,7 +119,8 @@ static int swap_enable_disable(char *device) } err = swapon(device, g_flags); /* Don't complain on swapon -a if device is already in use */ - quiet = (OPT_ALL && errno == EBUSY); + /* Don't complain if file does not exist with -e option */ + quiet = (OPT_ALL && errno == EBUSY) || (OPT_IFEXISTS && errno == ENOENT); } } @@ -229,7 +237,7 @@ static int do_all_in_proc_swaps(void) return err; } -#define OPTSTR_SWAPON "a" \ +#define OPTSTR_SWAPON "ae" \ IF_FEATURE_SWAPON_DISCARD("d::") \ IF_FEATURE_SWAPON_PRI("p:") @@ -242,7 +250,7 @@ int swap_on_off_main(int argc UNUSED_PARAM, char **argv) INIT_G(); - getopt32(argv, do_swapoff ? "a" : OPTSTR_SWAPON + getopt32(argv, do_swapoff ? "ae" : OPTSTR_SWAPON IF_FEATURE_SWAPON_DISCARD(, &discard) IF_FEATURE_SWAPON_PRI(, &prio) ); -- cgit v1.2.3-55-g6feb From e5cae08f1232e66daca8f47f0f257a83a0cca9fa Mon Sep 17 00:00:00 2001 From: Tito Ragusa Date: Tue, 6 Jan 2015 01:22:36 +0100 Subject: libpwdgrp: line counting in error meesages is broken, remove it Signed-off-by: Tito Ragusa Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 524acfeab..90647e9d3 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -189,17 +189,15 @@ static int tokenize(char *buffer, int ch) static char *parse_common(FILE *fp, struct passdb *db, const char *key, int field_pos) { - int count = 0; char *buf; while ((buf = xmalloc_fgetline(fp)) != NULL) { - count++; /* Skip empty lines, comment lines */ if (buf[0] == '\0' || buf[0] == '#') goto free_and_next; if (tokenize(buf, ':') != db->numfields) { /* number of fields is wrong */ - bb_error_msg("bad record at %s:%u", db->filename, count); + bb_error_msg("%s: bad record", db->filename); goto free_and_next; } -- cgit v1.2.3-55-g6feb From 08364dab752e752ba3b7da30f64d09b72d9da0ad Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 7 Jan 2015 14:39:01 +0100 Subject: depmod: fix recent breakage in modules.dep generation Signed-off-by: Denys Vlasenko --- modutils/depmod.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/modutils/depmod.c b/modutils/depmod.c index aa228ec85..a41b3e440 100644 --- a/modutils/depmod.c +++ b/modutils/depmod.c @@ -51,7 +51,11 @@ static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARA info->dnext = info->dprev = info; info->name = xstrdup(fname + 2); /* skip "./" */ - info->modname = xstrdup(filename2modname(fname, modname)); + info->modname = xstrdup( + filename2modname( + bb_get_last_path_component_nostrip(fname), + modname + )); for (ptr = image; ptr < image + len - 10; ptr++) { if (strncmp(ptr, "depends=", 8) == 0) { char *u; @@ -242,17 +246,18 @@ int depmod_main(int argc UNUSED_PARAM, char **argv) if (!(option_mask32 & OPT_n)) xfreopen_write("modules.alias", stdout); for (m = modules; m != NULL; m = m->next) { + char modname[MODULE_NAME_LEN]; const char *fname = bb_basename(m->name); - int fnlen = strchrnul(fname, '.') - fname; + filename2modname(fname, modname); while (m->aliases) { /* Last word can well be m->modname instead, * but depmod from module-init-tools 3.4 * uses module basename, i.e., no s/-/_/g. * (pathname and .ko.* are still stripped) * Mimicking that... */ - printf("alias %s %.*s\n", + printf("alias %s %s\n", (char*)llist_pop(&m->aliases), - fnlen, fname); + modname); } } #endif @@ -260,12 +265,13 @@ int depmod_main(int argc UNUSED_PARAM, char **argv) if (!(option_mask32 & OPT_n)) xfreopen_write("modules.symbols", stdout); for (m = modules; m != NULL; m = m->next) { + char modname[MODULE_NAME_LEN]; const char *fname = bb_basename(m->name); - int fnlen = strchrnul(fname, '.') - fname; + filename2modname(fname, modname); while (m->symbols) { - printf("alias symbol:%s %.*s\n", + printf("alias symbol:%s %s\n", (char*)llist_pop(&m->symbols), - fnlen, fname); + modname); } } #endif -- cgit v1.2.3-55-g6feb From e5213cee3833a88397cb5e73428efb9a1b886db3 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Sun, 30 Nov 2014 20:39:25 +0000 Subject: vi: failure to open file is not an error when initialising buffer Commit 32afd3a introduced these regressions on the master branch: Starting vi with no filename on the command line gives the status message "'(null)' Bad address" instead of "- No file 1/1 100%". Starting vi with a non-existent file on the command line gives the status message "'new.txt' No such file or directory" instead of "- new.txt 1/1 100%" Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- editors/vi.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/editors/vi.c b/editors/vi.c index 70bdbab07..7b88e8e17 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -542,9 +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 -#if !ENABLE_FEATURE_VI_READONLY -#define file_insert(fn, p, update_ro_status) file_insert(fn, p) -#endif // file_insert might reallocate text[]! static int file_insert(const char *, char *, int); static int file_write(char *, char *, char *); @@ -1325,7 +1322,7 @@ static void colon(char *buf) q = next_line(q); { // dance around potentially-reallocated text[] uintptr_t ofs = q - text; - size = file_insert(fn, q, /*update_ro:*/ 0); + size = file_insert(fn, q, 0); q = text + ofs; } if (size < 0) @@ -2905,7 +2902,7 @@ static char *get_input_line(const char *prompt) } // might reallocate text[]! -static int file_insert(const char *fn, char *p, int update_ro_status) +static int file_insert(const char *fn, char *p, int initial) { int cnt = -1; int fd, size; @@ -2918,7 +2915,8 @@ static int file_insert(const char *fn, char *p, int update_ro_status) fd = open(fn, O_RDONLY); if (fd < 0) { - status_line_bold_errno(fn); + if (!initial) + status_line_bold_errno(fn); return cnt; } @@ -2946,7 +2944,7 @@ static int file_insert(const char *fn, char *p, int update_ro_status) close(fd); #if ENABLE_FEATURE_VI_READONLY - if (update_ro_status + if (initial && ((access(fn, W_OK) < 0) || /* root will always have access() * so we check fileperms too */ -- cgit v1.2.3-55-g6feb From 70f4320b40b7af83525cebabab2bc437a22c41d2 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Sun, 30 Nov 2014 20:39:53 +0000 Subject: vi: fix reading of file after last line If the :r command is used to read a file after the last line of the buffer the last line of the buffer and the first line of the file are joined. An extra blank line appears at the end of the buffer. file 1 file 1 file 1file 2 file 2 file 2 ~ ~ The insertion point is normally at the start of the line following the specified line. When the specified line is the last one the next_line function baulks at moving to the non-existent following line. Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- editors/vi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/editors/vi.c b/editors/vi.c index 7b88e8e17..77535be92 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -1318,8 +1318,12 @@ static void colon(char *buf) q = begin_line(dot); // assume "dot" } // read after current line- unless user said ":0r foo" - if (b != 0) + if (b != 0) { q = next_line(q); + // read after last line + if (q == end-1) + ++q; + } { // dance around potentially-reallocated text[] uintptr_t ofs = q - text; size = file_insert(fn, q, 0); -- cgit v1.2.3-55-g6feb From 39f017293a047ac1063c84263d4d855b86c62ba1 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 11 Jan 2015 16:41:54 +0100 Subject: diff: fix a bug in diffing against stdin. Closes 7784 Signed-off-by: Denys Vlasenko --- editors/diff.c | 3 ++- testsuite/diff.tests | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/editors/diff.c b/editors/diff.c index a78a0ee28..c3ad31bf3 100644 --- a/editors/diff.c +++ b/editors/diff.c @@ -740,9 +740,10 @@ static int diffreg(char *file[2]) unlink(name); if (bb_copyfd_eof(fd, fd_tmp) < 0) xfunc_die(); - if (fd) /* Prevents closing of stdin */ + if (fd != STDIN_FILENO) close(fd); fd = fd_tmp; + xlseek(fd, 0, SEEK_SET); } fp[i] = fdopen(fd, "r"); } diff --git a/testsuite/diff.tests b/testsuite/diff.tests index 6de46483b..84d853852 100755 --- a/testsuite/diff.tests +++ b/testsuite/diff.tests @@ -44,6 +44,17 @@ testing "diff of stdin, twice" \ "" \ "stdin" +testing "diff of empty file against stdin" \ + "diff -u - input | $TRIM_TAB" \ +"\ +--- - ++++ input +@@ -1 +0,0 @@ +-a +" \ + "" \ + "a\n" + testing "diff of empty file against nonempty one" \ "diff -u - input | $TRIM_TAB" \ "\ -- cgit v1.2.3-55-g6feb From 3c75b1c34e56e89aacacc781a97c70eff37bc63d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 11 Jan 2015 17:40:30 +0100 Subject: modprobe-small: fix recent breakage: modprobe -r was using full path "modprobe -r MODNAME", after it found the full module pathname, should strip dirpath before trying to remove the module. Signed-off-by: Denys Vlasenko --- modutils/modprobe-small.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c index dafe91ed7..cf8a3f0fe 100644 --- a/modutils/modprobe-small.c +++ b/modutils/modprobe-small.c @@ -635,6 +635,14 @@ static void process_module(char *name, const char *cmdline_options) infovec = find_alias(name); } + 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); +//TODO: _and_die()? or should we continue (un)loading modules listed on cmdline? + goto ret; + } + /* 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*" @@ -650,7 +658,8 @@ static void process_module(char *name, const char *cmdline_options) int r; char modname[MODULE_NAME_LEN]; - filename2modname(info->pathname, modname); + filename2modname( + bb_get_last_path_component_nostrip(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) { @@ -673,14 +682,6 @@ static void process_module(char *name, const char *cmdline_options) */ } - 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); -//TODO: _and_die()? or should we continue (un)loading modules listed on cmdline? - goto ret; - } - infoidx = 0; while ((info = infovec[infoidx++]) != NULL) { /* Iterate thru dependencies, trying to (un)load them */ -- cgit v1.2.3-55-g6feb From 1b6715345f2ad026482c336d2844c2d7fe64a25c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 11 Jan 2015 17:46:56 +0100 Subject: modprobe-small: code shrink function old new delta pathname_matches_modname 80 42 -38 Signed-off-by: Denys Vlasenko --- modutils/modprobe-small.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c index cf8a3f0fe..cafbdc0b2 100644 --- a/modutils/modprobe-small.c +++ b/modutils/modprobe-small.c @@ -163,6 +163,15 @@ static char *filename2modname(const char *filename, char *modname) return modname; } +static int pathname_matches_modname(const char *pathname, const char *modname) +{ + int r; + char name[MODULE_NAME_LEN]; + filename2modname(bb_get_last_path_component_nostrip(pathname), name); + r = (strcmp(name, modname) == 0); + return r; +} + /* Take "word word", return malloced "word",NUL,"word",NUL,NUL */ static char* str_2_list(const char *str) { @@ -295,18 +304,6 @@ static void parse_module(module_info *info, const char *pathname) free(module_image); } -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"); - safe_strncpy(name, fname, suffix - fname + 1); - replace(name, '-', '_'); - r = (strcmp(name, modname) == 0); - return r; -} - static FAST_FUNC int fileAction(const char *pathname, struct stat *sb UNUSED_PARAM, void *modname_to_match, -- cgit v1.2.3-55-g6feb From e998b08f118a0e485fffaa513bac133df2e3843b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 15 Jan 2015 02:48:36 +0100 Subject: modprobe: fix recent breakage: modules.dep reading code needs to strip dirname Signed-off-by: Denys Vlasenko --- modutils/modprobe.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/modutils/modprobe.c b/modutils/modprobe.c index f08f0850d..d1adaf8e1 100644 --- a/modutils/modprobe.c +++ b/modutils/modprobe.c @@ -15,8 +15,11 @@ #include #include -//#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__) +#if 1 #define DBG(...) ((void)0) +#else +#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__) +#endif /* Note that unlike older versions of modules.dep/depmod (busybox and m-i-t), * we expect the full dependency list to be specified in modules.dep. @@ -204,7 +207,7 @@ static struct module_entry *helper_get_module(const char *module, int create) unsigned i; unsigned hash; - filename2modname(module, modname); + filename2modname(bb_get_last_path_component_nostrip(module), modname); hash = 0; for (i = 0; modname[i]; i++) @@ -546,7 +549,6 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) if (opt & OPT_LIST_ONLY) { int i; - char name[MODULE_NAME_LEN]; char *colon, *tokens[2]; parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read); @@ -558,10 +560,14 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) if (!colon) continue; *colon = '\0'; - filename2modname(tokens[0], name); if (!argv[0]) puts(tokens[0]); else { + char name[MODULE_NAME_LEN]; + filename2modname( + bb_get_last_path_component_nostrip(tokens[0]), + name + ); for (i = 0; argv[i]; i++) { if (fnmatch(argv[i], name, 0) == 0) { puts(tokens[0]); -- cgit v1.2.3-55-g6feb From 76b2262b9b911e1741b9b6d04ed15fcf076b8d2d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 15 Jan 2015 03:04:23 +0100 Subject: modprobe: fix "modprobe devname:snd/timer" case (alias with a slash) Signed-off-by: Denys Vlasenko --- modutils/modprobe.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/modutils/modprobe.c b/modutils/modprobe.c index d1adaf8e1..ce8b0b886 100644 --- a/modutils/modprobe.c +++ b/modutils/modprobe.c @@ -207,7 +207,7 @@ static struct module_entry *helper_get_module(const char *module, int create) unsigned i; unsigned hash; - filename2modname(bb_get_last_path_component_nostrip(module), modname); + filename2modname(module, modname); hash = 0; for (i = 0; modname[i]; i++) @@ -232,9 +232,14 @@ static ALWAYS_INLINE struct module_entry *get_or_add_modentry(const char *module { return helper_get_module(module, 1); } -static ALWAYS_INLINE struct module_entry *get_modentry(const char *module) +/* So far this function always gets a module pathname, never an alias name. + * The crucial difference is that pathname needs dirname stripping, + * while alias name must NOT do it! + * Testcase where dirname stripping is likely to go wrong: "modprobe devname:snd/timer" + */ +static ALWAYS_INLINE struct module_entry *get_modentry(const char *pathname) { - return helper_get_module(module, 0); + return helper_get_module(bb_get_last_path_component_nostrip(pathname), 0); } static void add_probe(const char *name) @@ -502,7 +507,7 @@ static void load_modules_dep(void) colon = last_char_is(tokens[0], ':'); if (colon == NULL) continue; - *colon = 0; + *colon = '\0'; m = get_modentry(tokens[0]); if (m == NULL) -- cgit v1.2.3-55-g6feb From 9de69c024c7c47f3f8733dbc7c9522966fcd73a9 Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Fri, 16 Jan 2015 13:53:05 +0100 Subject: modprobe: fix modprobe -r and parsing of /etc/modprobe.d This fixes a regression introduced with commit 78854520 (modprobe: revert checking for /, stop doing basename() on modprobe args, 2015-01-01) that broke modprobe options stored in /etc/modprobe.conf and /etc/modprobe.d/*.conf. This also fixes modprobe -r Signed-off-by: Natanael Copa Signed-off-by: Denys Vlasenko --- modutils/modprobe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modutils/modprobe.c b/modutils/modprobe.c index ce8b0b886..0e8aa9e85 100644 --- a/modutils/modprobe.c +++ b/modutils/modprobe.c @@ -425,7 +425,7 @@ static int do_modprobe(struct module_entry *m) rc = 0; fn = llist_pop(&m->deps); /* we leak it */ - m2 = get_or_add_modentry(fn); + m2 = get_or_add_modentry(bb_get_last_path_component_nostrip(fn)); if (option_mask32 & OPT_REMOVE) { /* modprobe -r */ -- cgit v1.2.3-55-g6feb From 86031a5ffd106b8128f5763d32c273b96875f707 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 24 Jan 2015 19:46:45 +0100 Subject: trim extra newlines in help texts Signed-off-by: Denys Vlasenko --- console-tools/loadkmap.c | 5 +++-- coreutils/basename.c | 2 +- procps/powertop.c | 2 +- procps/pwdx.c | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/console-tools/loadkmap.c b/console-tools/loadkmap.c index 66ec3b043..6dcf8133f 100644 --- a/console-tools/loadkmap.c +++ b/console-tools/loadkmap.c @@ -10,8 +10,9 @@ //usage:#define loadkmap_trivial_usage //usage: "< keymap" //usage:#define loadkmap_full_usage "\n\n" -//usage: "Load a binary keyboard translation table from stdin\n" -/* //usage: "\n -C TTY Affect TTY instead of /dev/tty" */ +//usage: "Load a binary keyboard translation table from stdin" +////usage: "\n" +////usage: "\n -C TTY Affect TTY instead of /dev/tty" //usage: //usage:#define loadkmap_example_usage //usage: "$ loadkmap < /etc/i18n/lang-keymap\n" diff --git a/coreutils/basename.c b/coreutils/basename.c index 1f7a13713..ab0c972ed 100644 --- a/coreutils/basename.c +++ b/coreutils/basename.c @@ -31,7 +31,7 @@ //usage:#define basename_trivial_usage //usage: "FILE [SUFFIX]" //usage:#define basename_full_usage "\n\n" -//usage: "Strip directory path and .SUFFIX from FILE\n" +//usage: "Strip directory path and .SUFFIX from FILE" //usage: //usage:#define basename_example_usage //usage: "$ basename /usr/local/bin/foo\n" diff --git a/procps/powertop.c b/procps/powertop.c index e3c29d1c3..ddda5bd93 100644 --- a/procps/powertop.c +++ b/procps/powertop.c @@ -674,7 +674,7 @@ static void show_timerstats(void) //usage:#define powertop_trivial_usage //usage: "" //usage:#define powertop_full_usage "\n\n" -//usage: "Analyze power consumption on Intel-based laptops\n" +//usage: "Analyze power consumption on Intel-based laptops" int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv) diff --git a/procps/pwdx.c b/procps/pwdx.c index 781810488..22b892275 100644 --- a/procps/pwdx.c +++ b/procps/pwdx.c @@ -21,7 +21,7 @@ //usage:#define pwdx_trivial_usage //usage: "PID..." //usage:#define pwdx_full_usage "\n\n" -//usage: "Show current directory for PIDs\n" +//usage: "Show current directory for PIDs" #include "libbb.h" -- cgit v1.2.3-55-g6feb From cc70b6f8b6c6441e1c48690c7885700a2d389946 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 24 Jan 2015 22:30:30 +0100 Subject: depmod: simple memory optimization function old new delta filename2modname 67 86 +19 parse_module 374 351 -23 Signed-off-by: Denys Vlasenko --- modutils/depmod.c | 19 +++++++++---------- modutils/modutils.c | 6 +++++- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/modutils/depmod.c b/modutils/depmod.c index a41b3e440..37a8482d9 100644 --- a/modutils/depmod.c +++ b/modutils/depmod.c @@ -33,7 +33,6 @@ typedef struct module_info { static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARAM, void *data, int depth UNUSED_PARAM) { - char modname[MODULE_NAME_LEN]; module_info **first = (module_info **) data; char *image, *ptr; module_info *info; @@ -51,11 +50,10 @@ static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARA info->dnext = info->dprev = info; info->name = xstrdup(fname + 2); /* skip "./" */ - info->modname = xstrdup( - filename2modname( + info->modname = filename2modname( bb_get_last_path_component_nostrip(fname), - modname - )); + NULL + ); for (ptr = image; ptr < image + len - 10; ptr++) { if (strncmp(ptr, "depends=", 8) == 0) { char *u; @@ -250,11 +248,12 @@ int depmod_main(int argc UNUSED_PARAM, char **argv) const char *fname = bb_basename(m->name); filename2modname(fname, modname); while (m->aliases) { - /* Last word can well be m->modname instead, - * but depmod from module-init-tools 3.4 - * uses module basename, i.e., no s/-/_/g. - * (pathname and .ko.* are still stripped) - * Mimicking that... */ + /* + * Last word used to be a basename + * (filename with path and .ko.* stripped) + * at the time of module-init-tools 3.4. + * kmod v.12 uses module name, i.e., s/-/_/g. + */ printf("alias %s %s\n", (char*)llist_pop(&m->aliases), modname); diff --git a/modutils/modutils.c b/modutils/modutils.c index ff79d3fac..84300d931 100644 --- a/modutils/modutils.c +++ b/modutils/modutils.c @@ -47,13 +47,14 @@ int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim) char* FAST_FUNC filename2modname(const char *filename, char *modname) { + char local_modname[MODULE_NAME_LEN]; int i; const char *from; if (filename == NULL) return NULL; if (modname == NULL) - modname = xmalloc(MODULE_NAME_LEN); + modname = local_modname; // Disabled since otherwise "modprobe dir/name" would work // as if it is "modprobe name". It is unclear why // 'basenamization' was here in the first place. @@ -63,6 +64,9 @@ char* FAST_FUNC filename2modname(const char *filename, char *modname) modname[i] = (from[i] == '-') ? '_' : from[i]; modname[i] = '\0'; + if (modname == local_modname) + return xstrdup(modname); + return modname; } -- cgit v1.2.3-55-g6feb From 50a6d86520b2c45a70a3d0e2d19fde37c49d8c90 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 25 Jan 2015 22:08:46 +0100 Subject: platform.h: add compat mempcpy Signed-off-by: Denys Vlasenko --- include/platform.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/platform.h b/include/platform.h index 09c7ccd9c..f00669695 100644 --- a/include/platform.h +++ b/include/platform.h @@ -370,6 +370,7 @@ typedef unsigned smalluint; #define HAVE_SETBIT 1 #define HAVE_SIGHANDLER_T 1 #define HAVE_STPCPY 1 +#define HAVE_MEMPCPY 1 #define HAVE_STRCASESTR 1 #define HAVE_STRCHRNUL 1 #define HAVE_STRSEP 1 @@ -450,6 +451,7 @@ typedef unsigned smalluint; #endif #if defined(__FreeBSD__) +//# undef HAVE_MEMPCPY - not yet confirmed # undef HAVE_CLEARENV # undef HAVE_FDATASYNC # undef HAVE_MNTENT_H @@ -513,6 +515,13 @@ typedef void (*sighandler_t)(int); extern char *stpcpy(char *p, const char *to_add) FAST_FUNC; #endif +#ifndef HAVE_MEMPCPY +static ALWAYS_INLINE void *mempcpy(void *dest, const void *src, size_t len) +{ + return memcpy(dest, src, len) + len; +} +#endif + #ifndef HAVE_STRCASESTR extern char *strcasestr(const char *s, const char *pattern) FAST_FUNC; #endif -- cgit v1.2.3-55-g6feb From 64b744997ef399809dc4c78ec45ddfbebde3e597 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 26 Jan 2015 15:45:48 +0100 Subject: ftpd: prevent zombies. closes 7832 Signed-off-by: Denys Vlasenko --- networking/ftpd.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/networking/ftpd.c b/networking/ftpd.c index 6adcb1dee..0c10e1f25 100644 --- a/networking/ftpd.c +++ b/networking/ftpd.c @@ -1174,8 +1174,13 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) //umask(077); - admin can set umask before starting us - /* Signals. We'll always take -EPIPE rather than a rude signal, thanks */ - signal(SIGPIPE, SIG_IGN); + /* Signals */ + bb_signals(0 + /* We'll always take EPIPE rather than a rude signal, thanks */ + + (1 << SIGPIPE) + /* LIST command spawns chilren. Prevent zombies */ + + (1 << SIGCHLD) + , SIG_IGN); /* Set up options on the command socket (do we need these all? why?) */ setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1)); -- cgit v1.2.3-55-g6feb From 936c401077cc68d9e3dd7f1b0b293ee03ce02bf5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 27 Jan 2015 21:59:40 +0100 Subject: dhcpd: fix an improper widening conversion We wanted to detect when tv_sec = unsigned1 - unsigned2 underflows by looking at tv_sec's sign. But if tv_sec is long and it is wider than unsigned, we get unsigned -> long conversion which is in this case never negative. Signed-off-by: Denys Vlasenko --- networking/udhcp/dhcpd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c index 4b3ed240c..2de074f9b 100644 --- a/networking/udhcp/dhcpd.c +++ b/networking/udhcp/dhcpd.c @@ -413,7 +413,8 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv) max_sock = udhcp_sp_fd_set(&rfds, server_socket); if (server_config.auto_time) { - tv.tv_sec = timeout_end - monotonic_sec(); + /* cast to signed is essential if tv_sec is wider than int */ + tv.tv_sec = (int)(timeout_end - monotonic_sec()); tv.tv_usec = 0; } retval = 0; -- cgit v1.2.3-55-g6feb From ff3f3accc68c84851d1ea8cd995f9491b32bedef Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 29 Jan 2015 16:31:36 +0100 Subject: ntpd: get rid of G.initial_poll_complete This speeds up syncing - now happens only just two replies from a peer. Especially useful for "ntpd -q". Shouldn't have ill effects: if we chose a bad peer, we will discover it later and switch to another one. The code is even smaller this way. Suggested by Miroslav Lichvar Signed-off-by: Denys Vlasenko --- networking/ntpd.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/networking/ntpd.c b/networking/ntpd.c index 33cb1dcb1..c016bfc50 100644 --- a/networking/ntpd.c +++ b/networking/ntpd.c @@ -376,8 +376,6 @@ struct globals { */ #define G_precision_sec 0.002 uint8_t stratum; - /* Bool. After set to 1, never goes back to 0: */ - smallint initial_poll_complete; #define STATE_NSET 0 /* initial state, "nothing is set" */ //#define STATE_FSET 1 /* frequency set from file */ @@ -1084,7 +1082,7 @@ select_and_cluster(void) num_points = 0; item = G.ntp_peers; - if (G.initial_poll_complete) while (item != NULL) { + while (item != NULL) { double rd, offset; p = (peer_t *) item->data; @@ -1649,7 +1647,7 @@ update_local_clock(peer_t *p) if (G.ntp_status & LI_MINUSSEC) tmx.status |= STA_DEL; - tmx.constant = G.poll_exp - 4; + tmx.constant = (int)G.poll_exp - 4 > 0 ? (int)G.poll_exp - 4 : 0; /* EXPERIMENTAL. * The below if statement should be unnecessary, but... * It looks like Linux kernel's PLL is far too gentle in changing @@ -2285,7 +2283,6 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv) VERB4 bb_error_msg("disabling burst mode"); G.polladj_count = 0; G.poll_exp = MINPOLL; - G.initial_poll_complete = 1; } send_query_to_peer(p); } else { -- cgit v1.2.3-55-g6feb From 8c05a74f7efcda6ae5bd83e134edf44af12cb5e4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 29 Jan 2015 16:41:48 +0100 Subject: platform.h: mempcpy needs Signed-off-by: Denys Vlasenko --- include/platform.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/platform.h b/include/platform.h index f00669695..1168a34dc 100644 --- a/include/platform.h +++ b/include/platform.h @@ -516,6 +516,7 @@ extern char *stpcpy(char *p, const char *to_add) FAST_FUNC; #endif #ifndef HAVE_MEMPCPY +#include static ALWAYS_INLINE void *mempcpy(void *dest, const void *src, size_t len) { return memcpy(dest, src, len) + len; -- cgit v1.2.3-55-g6feb From 08f9ffc3f7503eb3dc94f16c615b20d2ae9a059c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 30 Jan 2015 15:15:38 +0100 Subject: dpkg: update supported compression methods Based on a patch by Ron Yorston function old new delta get_header_tar_xz - 60 +60 filter_accept_list_reassign 128 188 +60 unpack_package 585 621 +36 init_archive_deb_control 52 76 +24 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 3/0 up/down: 180/0) Total: 180 bytes Signed-off-by: Denys Vlasenko --- archival/dpkg.c | 11 +++++++++++ archival/dpkg_deb.c | 6 ++++++ archival/libarchive/Kbuild.src | 1 + archival/libarchive/filter_accept_list_reassign.c | 10 ++++++++++ archival/libarchive/get_header_tar_xz.c | 21 +++++++++++++++++++++ include/bb_archive.h | 1 + 6 files changed, 50 insertions(+) create mode 100644 archival/libarchive/get_header_tar_xz.c diff --git a/archival/dpkg.c b/archival/dpkg.c index 2893cfc2d..151f0ca43 100644 --- a/archival/dpkg.c +++ b/archival/dpkg.c @@ -1472,12 +1472,16 @@ static void init_archive_deb_control(archive_handle_t *ar_handle) tar_handle->src_fd = ar_handle->src_fd; /* We don't care about data.tar.* or debian-binary, just control.tar.* */ + llist_add_to(&(ar_handle->accept), (char*)"control.tar"); #if ENABLE_FEATURE_SEAMLESS_GZ llist_add_to(&(ar_handle->accept), (char*)"control.tar.gz"); #endif #if ENABLE_FEATURE_SEAMLESS_BZ2 llist_add_to(&(ar_handle->accept), (char*)"control.tar.bz2"); #endif +#if ENABLE_FEATURE_SEAMLESS_XZ + llist_add_to(&(ar_handle->accept), (char*)"control.tar.xz"); +#endif /* Assign the tar handle as a subarchive of the ar handle */ ar_handle->dpkg__sub_archive = tar_handle; @@ -1492,12 +1496,19 @@ static void init_archive_deb_data(archive_handle_t *ar_handle) tar_handle->src_fd = ar_handle->src_fd; /* We don't care about control.tar.* or debian-binary, just data.tar.* */ + llist_add_to(&(ar_handle->accept), (char*)"data.tar"); #if ENABLE_FEATURE_SEAMLESS_GZ llist_add_to(&(ar_handle->accept), (char*)"data.tar.gz"); #endif #if ENABLE_FEATURE_SEAMLESS_BZ2 llist_add_to(&(ar_handle->accept), (char*)"data.tar.bz2"); #endif +#if ENABLE_FEATURE_SEAMLESS_LZMA + llist_add_to(&(ar_handle->accept), (char*)"data.tar.lzma"); +#endif +#if ENABLE_FEATURE_SEAMLESS_XZ + llist_add_to(&(ar_handle->accept), (char*)"data.tar.xz"); +#endif /* Assign the tar handle as a subarchive of the ar handle */ ar_handle->dpkg__sub_archive = tar_handle; diff --git a/archival/dpkg_deb.c b/archival/dpkg_deb.c index 13f9db991..0285273fe 100644 --- a/archival/dpkg_deb.c +++ b/archival/dpkg_deb.c @@ -70,6 +70,8 @@ int dpkg_deb_main(int argc, char **argv) ar_archive->dpkg__sub_archive = tar_archive; ar_archive->filter = filter_accept_list_reassign; + llist_add_to(&ar_archive->accept, (char*)"data.tar"); + llist_add_to(&control_tar_llist, (char*)"control.tar"); #if ENABLE_FEATURE_SEAMLESS_GZ llist_add_to(&ar_archive->accept, (char*)"data.tar.gz"); llist_add_to(&control_tar_llist, (char*)"control.tar.gz"); @@ -82,6 +84,10 @@ int dpkg_deb_main(int argc, char **argv) llist_add_to(&ar_archive->accept, (char*)"data.tar.lzma"); llist_add_to(&control_tar_llist, (char*)"control.tar.lzma"); #endif +#if ENABLE_FEATURE_SEAMLESS_XZ + llist_add_to(&ar_archive->accept, (char*)"data.tar.xz"); + llist_add_to(&control_tar_llist, (char*)"control.tar.xz"); +#endif opt_complementary = "c--efXx:e--cfXx:f--ceXx:X--cefx:x--cefX"; opt = getopt32(argv, "cefXx"); diff --git a/archival/libarchive/Kbuild.src b/archival/libarchive/Kbuild.src index 968fdf8ab..7e89e9e89 100644 --- a/archival/libarchive/Kbuild.src +++ b/archival/libarchive/Kbuild.src @@ -35,6 +35,7 @@ DPKG_FILES:= \ get_header_tar_gz.o \ get_header_tar_bz2.o \ get_header_tar_lzma.o \ + get_header_tar_xz.o \ INSERT diff --git a/archival/libarchive/filter_accept_list_reassign.c b/archival/libarchive/filter_accept_list_reassign.c index 3d19abe44..b9acfbc05 100644 --- a/archival/libarchive/filter_accept_list_reassign.c +++ b/archival/libarchive/filter_accept_list_reassign.c @@ -28,6 +28,10 @@ char FAST_FUNC filter_accept_list_reassign(archive_handle_t *archive_handle) name_ptr++; /* Modify the subarchive handler based on the extension */ + if (strcmp(name_ptr, "tar") == 0) { + archive_handle->dpkg__action_data_subarchive = get_header_tar; + return EXIT_SUCCESS; + } if (ENABLE_FEATURE_SEAMLESS_GZ && strcmp(name_ptr, "gz") == 0 ) { @@ -46,6 +50,12 @@ char FAST_FUNC filter_accept_list_reassign(archive_handle_t *archive_handle) archive_handle->dpkg__action_data_subarchive = get_header_tar_lzma; return EXIT_SUCCESS; } + if (ENABLE_FEATURE_SEAMLESS_XZ + && strcmp(name_ptr, "xz") == 0 + ) { + archive_handle->dpkg__action_data_subarchive = get_header_tar_xz; + return EXIT_SUCCESS; + } } return EXIT_FAILURE; } diff --git a/archival/libarchive/get_header_tar_xz.c b/archival/libarchive/get_header_tar_xz.c new file mode 100644 index 000000000..7bf3b3b56 --- /dev/null +++ b/archival/libarchive/get_header_tar_xz.c @@ -0,0 +1,21 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "bb_archive.h" + +char FAST_FUNC get_header_tar_xz(archive_handle_t *archive_handle) +{ + /* Can't lseek over pipes */ + archive_handle->seek = seek_by_read; + + fork_transformer_with_sig(archive_handle->src_fd, unpack_xz_stream, "unxz"); + archive_handle->offset = 0; + while (get_header_tar(archive_handle) == EXIT_SUCCESS) + continue; + + /* Can only do one file at a time */ + return EXIT_FAILURE; +} diff --git a/include/bb_archive.h b/include/bb_archive.h index a6b166fe3..5d9e24c17 100644 --- a/include/bb_archive.h +++ b/include/bb_archive.h @@ -184,6 +184,7 @@ char get_header_tar(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC; +char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC; void seek_by_jump(int fd, off_t amount) FAST_FUNC; void seek_by_read(int fd, off_t amount) FAST_FUNC; -- cgit v1.2.3-55-g6feb From 1cbe93cf4e4234629a15cc3038bdb379961ffc2e Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Sun, 2 Nov 2014 23:23:16 +0100 Subject: telnetd: we only need CONFIG_UNIX98_PTYS from the Linux kernel (re-sending this as it got ignored completey and the format of the previous mail was probably not correct - please let me know if there's anything else I can do to get this trivial fix applied) for telnetd to work, we only need CONFIG_UNIX98_PTYS to be enabled in the Linux kernel - DEVPTS_FS has been obsolete for some time Signed-off-by: Martin Kaiser Signed-off-by: Denys Vlasenko --- networking/Config.src | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/networking/Config.src b/networking/Config.src index 15a696876..da36e8627 100644 --- a/networking/Config.src +++ b/networking/Config.src @@ -750,8 +750,7 @@ config TELNETD Note that for busybox telnetd to work you need several things: First of all, your kernel needs: - UNIX98_PTYS=y - DEVPTS_FS=y + CONFIG_UNIX98_PTYS=y Next, you need a /dev/pts directory on your root filesystem: -- cgit v1.2.3-55-g6feb From 07fa09a9527b7c9f864799c4c1cab86b49f232ce Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Sun, 2 Nov 2014 11:08:24 +0100 Subject: ubiattach: add option -O Some images do not have the default VID offset. The option -O must be used to attach such images. Signed-off-by: Micke Prag Signed-off-by: Denys Vlasenko --- miscutils/ubi_tools.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/miscutils/ubi_tools.c b/miscutils/ubi_tools.c index 6c09fe534..dd1bda300 100644 --- a/miscutils/ubi_tools.c +++ b/miscutils/ubi_tools.c @@ -105,6 +105,7 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv) int mtd_num; int dev_num = UBI_DEV_NUM_AUTO; int vol_id = UBI_VOL_NUM_AUTO; + int vid_hdr_offset = 0; char *vol_name; unsigned long long size_bytes = size_bytes; /* for compiler */ char *size_bytes_str; @@ -133,10 +134,11 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv) #define OPTION_a (1 << 5) #define OPTION_t (1 << 6) if (do_mkvol) { - opt_complementary = "-1:d+:n+:a+"; - opts = getopt32(argv, "md:n:N:s:a:t:", + opt_complementary = "-1:d+:n+:a+:O+"; + opts = getopt32(argv, "md:n:N:s:a:t:O:", &dev_num, &vol_id, - &vol_name, &size_bytes_str, &alignment, &type + &vol_name, &size_bytes_str, &alignment, &type, + &vid_hdr_offset ); } else if (do_update) { @@ -162,17 +164,19 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv) // bb_error_msg_and_die("%s: not a char device", ubi_ctrl); //usage:#define ubiattach_trivial_usage -//usage: "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV" +//usage: "-m MTD_NUM [-d UBI_NUM] [-O VID_HDR_OFF] UBI_CTRL_DEV" //usage:#define ubiattach_full_usage "\n\n" //usage: "Attach MTD device to UBI\n" //usage: "\n -m MTD_NUM MTD device number to attach" //usage: "\n -d UBI_NUM UBI device number to assign" +//usage: "\n -O VID_HDR_OFF VID header offset" if (do_attach) { if (!(opts & OPTION_m)) bb_error_msg_and_die("%s device not specified", "MTD"); attach_req.mtd_num = mtd_num; attach_req.ubi_num = dev_num; + attach_req.vid_hdr_offset = vid_hdr_offset; xioctl(fd, UBI_IOCATT, &attach_req); } else -- cgit v1.2.3-55-g6feb From 5104645cf87f8cf37c42666c90bb259011cfac58 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 2 Feb 2015 03:51:47 +0100 Subject: platform.h: enable mempcpy for FreeBSD Signed-off-by: Denys Vlasenko --- include/platform.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/platform.h b/include/platform.h index 1168a34dc..0b0fce182 100644 --- a/include/platform.h +++ b/include/platform.h @@ -451,7 +451,8 @@ typedef unsigned smalluint; #endif #if defined(__FreeBSD__) -//# undef HAVE_MEMPCPY - not yet confirmed +/* users say mempcpy is not present in FreeBSD 9.x */ +# undef HAVE_MEMPCPY # undef HAVE_CLEARENV # undef HAVE_FDATASYNC # undef HAVE_MNTENT_H @@ -517,6 +518,10 @@ extern char *stpcpy(char *p, const char *to_add) FAST_FUNC; #ifndef HAVE_MEMPCPY #include +/* In case we are wrong about !HAVE_MEMPCPY, and toolchain _does_ have + * mempcpy(), avoid colliding with it: + */ +#define mempcpy bb__mempcpy static ALWAYS_INLINE void *mempcpy(void *dest, const void *src, size_t len) { return memcpy(dest, src, len) + len; -- cgit v1.2.3-55-g6feb From 7f7ade1964f61172125d9f4fe92f0b07ce8bc7a4 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Mon, 2 Feb 2015 16:01:16 +0100 Subject: gzip: do not store timestamp in gzip header Storing the original file's modification time in the output file is harmful (precludes deterministic results) and unlike official gzip, the busybox version provides no way to suppress this behavior; the -n option is silently ignored. Rather than trying to make -n work, this patch just removes the timestamp-storing functionality. It should be considered deprecated anyway; it's not Y2038-safe and gunzip ignores it by default. Per RFC 1952, 0 is the correct value to store to indicate that there is no timestamp. Signed-off-by: Rich Felker Signed-off-by: Denys Vlasenko --- archival/gzip.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/archival/gzip.c b/archival/gzip.c index a93d2175a..46367f9e6 100644 --- a/archival/gzip.c +++ b/archival/gzip.c @@ -2007,7 +2007,7 @@ static void ct_init(void) * IN assertions: the input and output buffers are cleared. */ -static void zip(ulg time_stamp) +static void zip(void) { ush deflate_flags = 0; /* pkzip -es, -en or -ex equivalent */ @@ -2018,7 +2018,7 @@ static void zip(ulg time_stamp) /* compression method: 8 (DEFLATED) */ /* general flags: 0 */ put_32bit(0x00088b1f); - put_32bit(time_stamp); + put_32bit(0); /* Unix timestamp */ /* Write deflated file to zip file */ G1.crc = ~0; @@ -2044,8 +2044,6 @@ static void zip(ulg time_stamp) static IF_DESKTOP(long long) int FAST_FUNC pack_gzip(transformer_state_t *xstate UNUSED_PARAM) { - struct stat s; - /* Clear input and output buffers */ G1.outcnt = 0; #ifdef DEBUG @@ -2077,9 +2075,23 @@ IF_DESKTOP(long long) int FAST_FUNC pack_gzip(transformer_state_t *xstate UNUSED G2.bl_desc.max_length = MAX_BL_BITS; //G2.bl_desc.max_code = 0; +#if 0 + /* Saving of timestamp is disabled. Why? + * - it is not Y2038-safe. + * - some people want deterministic results + * (normally they'd use -n, but our -n is a nop). + * - it's bloat. + * Per RFC 1952, gzfile.time=0 is "no timestamp". + * If users will demand this to be reinstated, + * implement -n "don't save timestamp". + */ + struct stat s; s.st_ctime = 0; fstat(STDIN_FILENO, &s); zip(s.st_ctime); +#else + zip(); +#endif return 0; } -- cgit v1.2.3-55-g6feb From f7f70bf1b3025550ea4ad8d13d977b846a868a06 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 2 Feb 2015 16:07:07 +0100 Subject: gzip: speed up and shrink put_16bit() function old new delta put_16bit 104 98 -6 Signed-off-by: Denys Vlasenko --- archival/gzip.c | 41 ++++++++++++++++++++++++++++++++++------- include/platform.h | 2 ++ 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/archival/gzip.c b/archival/gzip.c index 46367f9e6..18d795996 100644 --- a/archival/gzip.c +++ b/archival/gzip.c @@ -417,19 +417,46 @@ static void flush_outbuf(void) #define put_8bit(c) \ do { \ G1.outbuf[G1.outcnt++] = (c); \ - if (G1.outcnt == OUTBUFSIZ) flush_outbuf(); \ + if (G1.outcnt == OUTBUFSIZ) \ + flush_outbuf(); \ } while (0) /* Output a 16 bit value, lsb first */ static void put_16bit(ush w) { - if (G1.outcnt < OUTBUFSIZ - 2) { - G1.outbuf[G1.outcnt++] = w; - G1.outbuf[G1.outcnt++] = w >> 8; - } else { - put_8bit(w); - put_8bit(w >> 8); + /* GCC 4.2.1 won't optimize out redundant loads of G1.outcnt + * (probably because of fear of aliasing with G1.outbuf[] + * stores), do it explicitly: + */ + unsigned outcnt = G1.outcnt; + uch *dst = &G1.outbuf[outcnt]; + +#if BB_UNALIGNED_MEMACCESS_OK && BB_LITTLE_ENDIAN + if (outcnt < OUTBUFSIZ-2) { + /* Common case */ + ush *dst16 = (void*) dst; + *dst16 = w; /* unalinged LSB 16-bit store */ + G1.outcnt = outcnt + 2; + return; + } + *dst = (uch)w; + w >>= 8; +#else + *dst++ = (uch)w; + w >>= 8; + if (outcnt < OUTBUFSIZ-2) { + /* Common case */ + *dst = w; + G1.outcnt = outcnt + 2; + return; } +#endif + + /* Slowpath: we will need to do flush_outbuf() */ + G1.outcnt++; + if (G1.outcnt == OUTBUFSIZ) + flush_outbuf(); + put_8bit(w); } static void put_32bit(ulg n) diff --git a/include/platform.h b/include/platform.h index 0b0fce182..df9594507 100644 --- a/include/platform.h +++ b/include/platform.h @@ -217,6 +217,7 @@ typedef uint64_t bb__aliased_uint64_t FIX_ALIASING; * a lvalue. This makes it more likely to not swap them by mistake */ #if defined(i386) || defined(__x86_64__) || defined(__powerpc__) +# define BB_UNALIGNED_MEMACCESS_OK 1 # define move_from_unaligned_int(v, intp) ((v) = *(bb__aliased_int*)(intp)) # define move_from_unaligned_long(v, longp) ((v) = *(bb__aliased_long*)(longp)) # define move_from_unaligned16(v, u16p) ((v) = *(bb__aliased_uint16_t*)(u16p)) @@ -225,6 +226,7 @@ typedef uint64_t bb__aliased_uint64_t FIX_ALIASING; # define move_to_unaligned32(u32p, v) (*(bb__aliased_uint32_t*)(u32p) = (v)) /* #elif ... - add your favorite arch today! */ #else +# define BB_UNALIGNED_MEMACCESS_OK 0 /* performs reasonably well (gcc usually inlines memcpy here) */ # define move_from_unaligned_int(v, intp) (memcpy(&(v), (intp), sizeof(int))) # define move_from_unaligned_long(v, longp) (memcpy(&(v), (longp), sizeof(long))) -- cgit v1.2.3-55-g6feb From 8e74adab0107658e3dc04ed342206272a284f43e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 3 Feb 2015 12:07:40 +0100 Subject: libbb: make INET[6]_rresolve use sockaddr2{host,dotted}_noport function old new delta INET_rresolve 274 214 -60 INET6_rresolve 168 85 -83 Signed-off-by: Denys Vlasenko --- libbb/inet_common.c | 84 +++++++++++++++++++++-------------------------------- 1 file changed, 33 insertions(+), 51 deletions(-) diff --git a/libbb/inet_common.c b/libbb/inet_common.c index b3e0802ee..5b4a4a10b 100644 --- a/libbb/inet_common.c +++ b/libbb/inet_common.c @@ -32,14 +32,12 @@ int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostf return 0; } /* If we expect this to be a hostname, try hostname database first */ -#ifdef DEBUG if (hostfirst) { +#ifdef DEBUG bb_error_msg("gethostbyname(%s)", name); - } #endif - if (hostfirst) { hp = gethostbyname(name); - if (hp != NULL) { + if (hp) { memcpy(&s_in->sin_addr, hp->h_addr_list[0], sizeof(struct in_addr)); return 0; @@ -51,7 +49,7 @@ int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostf bb_error_msg("getnetbyname(%s)", name); #endif np = getnetbyname(name); - if (np != NULL) { + if (np) { s_in->sin_addr.s_addr = htonl(np->n_net); return 1; } @@ -66,7 +64,7 @@ int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostf bb_error_msg("gethostbyname(%s)", name); #endif hp = gethostbyname(name); - if (hp == NULL) { + if (!hp) { return -1; } memcpy(&s_in->sin_addr, hp->h_addr_list[0], sizeof(struct in_addr)); @@ -74,7 +72,7 @@ int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostf } -/* numeric: & 0x8000: default instead of *, +/* numeric: & 0x8000: "default" instead of "*", * & 0x4000: host instead of net, * & 0x0fff: don't resolve */ @@ -83,16 +81,16 @@ char* FAST_FUNC INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t ne /* addr-to-name cache */ struct addr { struct addr *next; - struct sockaddr_in addr; - int host; + uint32_t nip; + smallint is_host; char name[1]; }; static struct addr *cache = NULL; struct addr *pn; char *name; - uint32_t ad, host_ad; - int host = 0; + uint32_t nip; + smallint is_host; if (s_in->sin_family != AF_INET) { #ifdef DEBUG @@ -102,61 +100,57 @@ char* FAST_FUNC INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t ne errno = EAFNOSUPPORT; return NULL; } - ad = s_in->sin_addr.s_addr; + nip = s_in->sin_addr.s_addr; #ifdef DEBUG - bb_error_msg("rresolve: %08x, mask %08x, num %08x", (unsigned)ad, netmask, numeric); + bb_error_msg("rresolve: %08x mask:%08x num:%08x", (unsigned)nip, netmask, numeric); #endif - if (ad == INADDR_ANY) { - if ((numeric & 0x0FFF) == 0) { - if (numeric & 0x8000) - return xstrdup("default"); - return xstrdup("*"); - } - } if (numeric & 0x0FFF) - return xstrdup(inet_ntoa(s_in->sin_addr)); + return xmalloc_sockaddr2dotted_noport((void*)s_in); + if (nip == INADDR_ANY) { + if (numeric & 0x8000) + return xstrdup("default"); + return xstrdup("*"); + } + + is_host = ((nip & (~netmask)) != 0 || (numeric & 0x4000)); - if ((ad & (~netmask)) != 0 || (numeric & 0x4000)) - host = 1; pn = cache; while (pn) { - if (pn->addr.sin_addr.s_addr == ad && pn->host == host) { + if (pn->nip == nip && pn->is_host == is_host) { #ifdef DEBUG bb_error_msg("rresolve: found %s %08x in cache", - (host ? "host" : "net"), (unsigned)ad); + (is_host ? "host" : "net"), (unsigned)nip); #endif return xstrdup(pn->name); } pn = pn->next; } - host_ad = ntohl(ad); name = NULL; - if (host) { - struct hostent *ent; + if (is_host) { #ifdef DEBUG - bb_error_msg("gethostbyaddr (%08x)", (unsigned)ad); + bb_error_msg("sockaddr2host_noport(%08x)", (unsigned)nip); #endif - ent = gethostbyaddr((char *) &ad, 4, AF_INET); - if (ent) - name = xstrdup(ent->h_name); + name = xmalloc_sockaddr2host_noport((void*)s_in); } else if (ENABLE_FEATURE_ETC_NETWORKS) { struct netent *np; #ifdef DEBUG - bb_error_msg("getnetbyaddr (%08x)", (unsigned)host_ad); + bb_error_msg("getnetbyaddr(%08x)", (unsigned)ntohl(nip)); #endif - np = getnetbyaddr(host_ad, AF_INET); + np = getnetbyaddr(ntohl(nip), AF_INET); if (np) name = xstrdup(np->n_name); } if (!name) - name = xstrdup(inet_ntoa(s_in->sin_addr)); + name = xmalloc_sockaddr2dotted_noport((void*)s_in); + pn = xmalloc(sizeof(*pn) + strlen(name)); /* no '+ 1', it's already accounted for */ pn->next = cache; - pn->addr = *s_in; - pn->host = host; + pn->nip = nip; + pn->is_host = is_host; strcpy(pn->name, name); cache = pn; + return name; } @@ -188,9 +182,6 @@ int FAST_FUNC INET6_resolve(const char *name, struct sockaddr_in6 *sin6) char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric) { - char name[128]; - int s; - if (sin6->sin6_family != AF_INET6) { #ifdef DEBUG bb_error_msg("rresolve: unsupported address family %d!", @@ -200,8 +191,7 @@ char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric) return NULL; } if (numeric & 0x7FFF) { - inet_ntop(AF_INET6, &sin6->sin6_addr, name, sizeof(name)); - return xstrdup(name); + return xmalloc_sockaddr2dotted_noport((void*)sin6); } if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { if (numeric & 0x8000) @@ -209,15 +199,7 @@ char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric) return xstrdup("*"); } - s = getnameinfo((struct sockaddr *) sin6, sizeof(*sin6), - name, sizeof(name), - /*serv,servlen:*/ NULL, 0, - 0); - if (s != 0) { - bb_error_msg("getnameinfo failed"); - return NULL; - } - return xstrdup(name); + return xmalloc_sockaddr2host_noport((void*)sin6); } #endif /* CONFIG_FEATURE_IPV6 */ -- cgit v1.2.3-55-g6feb From a97777889328157bb7d06ec618bad16712a9c345 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 3 Feb 2015 12:11:30 +0100 Subject: ifconfig: fix double free fatal error in INET_sprint Based on the patch by Zheng Junling and Chen Gang Signed-off-by: Denys Vlasenko --- networking/interface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/networking/interface.c b/networking/interface.c index bf7d2b1b4..b0572d04e 100644 --- a/networking/interface.c +++ b/networking/interface.c @@ -91,9 +91,9 @@ static const char* FAST_FUNC INET_sprint(struct sockaddr *sap, int numeric) { static char *buff; /* defaults to NULL */ - free(buff); if (sap->sa_family == 0xFFFF || sap->sa_family == 0) return "[NONE SET]"; + free(buff); buff = INET_rresolve((struct sockaddr_in *) sap, numeric, 0xffffff00); return buff; } @@ -173,9 +173,9 @@ static const char* FAST_FUNC INET6_sprint(struct sockaddr *sap, int numeric) { static char *buff; - free(buff); if (sap->sa_family == 0xFFFF || sap->sa_family == 0) return "[NONE SET]"; + free(buff); buff = INET6_rresolve((struct sockaddr_in6 *) sap, numeric); return buff; } -- cgit v1.2.3-55-g6feb From b62d4d9d5700d4931faac500fca11f440dacd610 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 3 Feb 2015 15:25:17 +0100 Subject: gzip: trivial code shrink -5 bytes Signed-off-by: Denys Vlasenko --- archival/gzip.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/archival/gzip.c b/archival/gzip.c index 18d795996..bc1f9c60b 100644 --- a/archival/gzip.c +++ b/archival/gzip.c @@ -442,19 +442,19 @@ static void put_16bit(ush w) *dst = (uch)w; w >>= 8; #else - *dst++ = (uch)w; + *dst = (uch)w; w >>= 8; if (outcnt < OUTBUFSIZ-2) { /* Common case */ - *dst = w; + dst[1] = w; G1.outcnt = outcnt + 2; return; } #endif /* Slowpath: we will need to do flush_outbuf() */ - G1.outcnt++; - if (G1.outcnt == OUTBUFSIZ) + G1.outcnt = ++outcnt; + if (outcnt == OUTBUFSIZ) flush_outbuf(); put_8bit(w); } -- cgit v1.2.3-55-g6feb From 09020b1aa891afaee865caf5fa46fa4ddb42f264 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 3 Feb 2015 19:32:44 +0100 Subject: route: improve -A inet6 Upstream route command shows non-up routes, it shows reject and nonexthop flags. And we had "ref" and "use" values mixed up... function old new delta flagvals 32 44 +12 flagchars 9 12 +3 INET_setroute 767 768 +1 INET6_setroute 495 496 +1 set_flags 43 39 -4 flags_ipvx 16 8 -8 route_main 632 623 -9 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 4/3 up/down: 17/-21) Total: -4 bytes Signed-off-by: Denys Vlasenko --- networking/route.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/networking/route.c b/networking/route.c index 24cc2eb5a..d82b0132a 100644 --- a/networking/route.c +++ b/networking/route.c @@ -55,6 +55,7 @@ #define RTF_WINDOW 0x0080 /* per route window clamping */ #define RTF_IRTT 0x0100 /* Initial round trip time */ #define RTF_REJECT 0x0200 /* Reject route */ +#define RTF_NONEXTHOP 0x00200000 /* route with no nexthop */ #endif #if defined(SIOCADDRTOLD) || defined(RTF_IRTT) /* route */ @@ -128,7 +129,7 @@ static const char tbl_ipvx[] ALIGN1 = "\013\043reinstate" /* Since last, we can save a byte. */ ; -static const int flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */ +static const uint16_t flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */ #ifdef RTF_REJECT RTF_REJECT, #endif @@ -449,7 +450,11 @@ static NOINLINE void INET6_setroute(int action, char **args) } #endif -static const unsigned flagvals[] = { /* Must agree with flagchars[]. */ +static const +IF_NOT_FEATURE_IPV6(uint16_t) +IF_FEATURE_IPV6(unsigned) +flagvals[] = { /* Must agree with flagchars[]. */ + RTF_UP, RTF_GATEWAY, RTF_HOST, RTF_REINSTATE, @@ -458,27 +463,25 @@ static const unsigned flagvals[] = { /* Must agree with flagchars[]. */ #if ENABLE_FEATURE_IPV6 RTF_DEFAULT, RTF_ADDRCONF, - RTF_CACHE + RTF_CACHE, + RTF_REJECT, + RTF_NONEXTHOP, /* this one doesn't fit into 16 bits */ #endif }; - -#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED) -#define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE) - /* Must agree with flagvals[]. */ static const char flagchars[] ALIGN1 = - "GHRDM" + "UGHRDM" #if ENABLE_FEATURE_IPV6 - "DAC" + "DAC!n" #endif ; +#define IPV4_MASK (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED) +#define IPV6_MASK (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE|RTF_REJECT|RTF_NONEXTHOP) static void set_flags(char *flagstr, int flags) { int i; - *flagstr++ = 'U'; - for (i = 0; (*flagstr = flagchars[i]) != 0; i++) { if (flags & flagvals[i]) { ++flagstr; @@ -574,7 +577,7 @@ static void INET6_displayroutes(void) int r; r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n", addr6x+14, &prefix_len, &slen, addr6x+40+7, - &metric, &use, &refcnt, &iflags, iface); + &metric, &refcnt, &use, &iflags, iface); if (r != 9) { if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */ break; @@ -606,10 +609,6 @@ static void INET6_displayroutes(void) } while (i < 40+28+7); } - if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */ - continue; - } - set_flags(flags, (iflags & IPV6_MASK)); r = 0; -- cgit v1.2.3-55-g6feb From a75dcd4353566f1a2d64baa642f1276f8ffd6ed4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 3 Feb 2015 19:42:28 +0100 Subject: route: fix bogus read error when route table is empty Signed-off-by: Denys Vlasenko --- networking/route.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/networking/route.c b/networking/route.c index d82b0132a..65c2fb7c8 100644 --- a/networking/route.c +++ b/networking/route.c @@ -494,6 +494,7 @@ void FAST_FUNC bb_displayroutes(int noresolve, int netstatfmt) { char devname[64], flags[16], *sdest, *sgw; unsigned long d, g, m; + int r; int flgs, ref, use, metric, mtu, win, ir; struct sockaddr_in s_addr; struct in_addr mask; @@ -504,20 +505,24 @@ void FAST_FUNC bb_displayroutes(int noresolve, int netstatfmt) "Destination Gateway Genmask Flags %s Iface\n", netstatfmt ? " MSS Window irtt" : "Metric Ref Use"); - if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */ - goto ERROR; /* Empty or missing line, or read error. */ + /* Skip the first line. */ + r = fscanf(fp, "%*[^\n]\n"); + if (r < 0) { + /* Empty line, read error, or EOF. Yes, if routing table + * is completely empty, /proc/net/route has no header. + */ + goto ERROR; } while (1) { - int r; r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n", devname, &d, &g, &flgs, &ref, &use, &metric, &m, &mtu, &win, &ir); if (r != 11) { + ERROR: if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */ break; } - ERROR: - bb_error_msg_and_die("fscanf"); + bb_perror_msg_and_die(bb_msg_read_error); } if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */ @@ -583,7 +588,7 @@ static void INET6_displayroutes(void) break; } ERROR: - bb_error_msg_and_die("fscanf"); + bb_perror_msg_and_die(bb_msg_read_error); } /* Do the addr6x shift-and-insert changes to ':'-delimit addresses. -- cgit v1.2.3-55-g6feb From 3c5d133c7def0718e313928c69bde2e9784ce58e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 4 Feb 2015 15:19:38 +0100 Subject: fstrim: fix help text Signed-off-by: Denys Vlasenko --- util-linux/fstrim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util-linux/fstrim.c b/util-linux/fstrim.c index 675a02184..51400ef0b 100644 --- a/util-linux/fstrim.c +++ b/util-linux/fstrim.c @@ -32,7 +32,7 @@ //usage: " -o OFFSET Offset in bytes to discard from" //usage: "\n -l LEN Bytes to discard" //usage: "\n -m MIN Minimum extent length" -//usage: "\n -v, Print number of discarded bytes" +//usage: "\n -v Print number of discarded bytes" //usage: ) #include "libbb.h" -- cgit v1.2.3-55-g6feb From a14fa79592eadec0d5e296c66e79dfe36ce798c7 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 5 Feb 2015 18:35:52 +0100 Subject: deluser: implement --remove-home function old new delta deluser_main 308 355 +47 packed_usage 30149 30156 +7 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 54/0) Total: 54 bytes Signed-off-by: Denys Vlasenko --- loginutils/deluser.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/loginutils/deluser.c b/loginutils/deluser.c index e39ac5506..2d98ecc58 100644 --- a/loginutils/deluser.c +++ b/loginutils/deluser.c @@ -11,9 +11,10 @@ */ //usage:#define deluser_trivial_usage -//usage: "USER" +//usage: IF_LONG_OPTS("[--remove-home] ") "USER" //usage:#define deluser_full_usage "\n\n" //usage: "Delete USER from the system" +// --remove-home is self-explanatory enough to put it in --help //usage:#define delgroup_trivial_usage //usage: IF_FEATURE_DEL_USER_FROM_GROUP("[USER] ")"GROUP" @@ -37,6 +38,19 @@ int deluser_main(int argc, char **argv) /* Are we deluser or delgroup? */ int do_deluser = (ENABLE_DELUSER && (!ENABLE_DELGROUP || applet_name[3] == 'u')); +#if !ENABLE_LONG_OPTS + const int opt_delhome = 0; +#else + int opt_delhome = 0; + if (do_deluser) { + applet_long_options = + "remove-home\0" No_argument "\xff"; + opt_delhome = getopt32(argv, ""); + argv += opt_delhome; + argc -= opt_delhome; + } +#endif + if (geteuid() != 0) bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); @@ -55,10 +69,14 @@ int deluser_main(int argc, char **argv) case 2: if (do_deluser) { /* "deluser USER" */ - xgetpwnam(name); /* bail out if USER is wrong */ + struct passwd *pw; + + pw = xgetpwnam(name); /* bail out if USER is wrong */ pfile = bb_path_passwd_file; if (ENABLE_FEATURE_SHADOWPASSWDS) sfile = bb_path_shadow_file; + if (opt_delhome) + remove_file(pw->pw_dir, FILEUTILS_RECUR); } else { struct group *gr; do_delgroup: -- cgit v1.2.3-55-g6feb From 7e66102f762a7d80715f0c7e5925433256b78cee Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 5 Feb 2015 21:00:17 +0100 Subject: ash: fix a SEGV case in an invalid heredoc Signed-off-by: Denys Vlasenko --- shell/ash.c | 15 +++++++++++---- shell/ash_test/ash-heredoc/heredoc1.right | 1 + shell/ash_test/ash-heredoc/heredoc1.tests | 3 +++ 3 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 shell/ash_test/ash-heredoc/heredoc1.right create mode 100755 shell/ash_test/ash-heredoc/heredoc1.tests diff --git a/shell/ash.c b/shell/ash.c index c5ad96909..0f9f73ec3 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -10556,7 +10556,7 @@ static union node *andor(void); static union node *pipeline(void); static union node *parse_command(void); static void parseheredoc(void); -static char peektoken(void); +static char nexttoken_ends_list(void); static int readtoken(void); static union node * @@ -10566,7 +10566,7 @@ list(int nlflag) int tok; checkkwd = CHKNL | CHKKWD | CHKALIAS; - if (nlflag == 2 && peektoken()) + if (nlflag == 2 && nexttoken_ends_list()) return NULL; n1 = NULL; for (;;) { @@ -10608,8 +10608,15 @@ list(int nlflag) tokpushback = 1; } checkkwd = CHKNL | CHKKWD | CHKALIAS; - if (peektoken()) + if (nexttoken_ends_list()) { + /* Testcase: "< Date: Sat, 7 Feb 2015 01:41:22 +0100 Subject: dd: add optional support for status=noxfer/none While at it, added 'B' number suffixes, upstream compat function old new delta dd_main 1469 1543 +74 dd_suffixes 88 112 +24 packed_usage 30156 30176 +20 dd_output_status 372 388 +16 static.status_words - 13 +13 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 4/0 up/down: 147/0) Total: 147 bytes Signed-off-by: Ari Sundholm Signed-off-by: Denys Vlasenko --- coreutils/Config.src | 38 -------------- coreutils/dd.c | 143 +++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 111 insertions(+), 70 deletions(-) diff --git a/coreutils/Config.src b/coreutils/Config.src index 68c717883..1bbb91ee9 100644 --- a/coreutils/Config.src +++ b/coreutils/Config.src @@ -87,44 +87,6 @@ config CUT cut is used to print selected parts of lines from each file to stdout. -config DD - bool "dd" - default y - help - dd copies a file (from standard input to standard output, - by default) using specific input and output blocksizes, - while optionally performing conversions on it. - -config FEATURE_DD_SIGNAL_HANDLING - bool "Enable DD signal handling for status reporting" - default y - depends on DD - help - Sending a SIGUSR1 signal to a running `dd' process makes it - print to standard error the number of records read and written - so far, then to resume copying. - - $ dd if=/dev/zero of=/dev/null& - $ pid=$! kill -USR1 $pid; sleep 1; kill $pid - 10899206+0 records in - 10899206+0 records out - -config FEATURE_DD_THIRD_STATUS_LINE - bool "Enable the third status line upon signal" - default y - depends on DD && FEATURE_DD_SIGNAL_HANDLING - help - Displays a coreutils-like third status line with transferred bytes, - elapsed time and speed. - -config FEATURE_DD_IBS_OBS - bool "Enable ibs, obs and conv options" - default y - depends on DD - help - Enables support for writing a certain number of bytes in and out, - at a time, and performing conversions on the data stream. - config DF bool "df" default y diff --git a/coreutils/dd.c b/coreutils/dd.c index 2838f6341..302497074 100644 --- a/coreutils/dd.c +++ b/coreutils/dd.c @@ -8,6 +8,51 @@ * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ +//config:config DD +//config: bool "dd" +//config: default y +//config: help +//config: dd copies a file (from standard input to standard output, +//config: by default) using specific input and output blocksizes, +//config: while optionally performing conversions on it. +//config: +//config:config FEATURE_DD_SIGNAL_HANDLING +//config: bool "Enable signal handling for status reporting" +//config: default y +//config: depends on DD +//config: help +//config: Sending a SIGUSR1 signal to a running `dd' process makes it +//config: print to standard error the number of records read and written +//config: so far, then to resume copying. +//config: +//config: $ dd if=/dev/zero of=/dev/null & +//config: $ pid=$!; kill -USR1 $pid; sleep 1; kill $pid +//config: 10899206+0 records in +//config: 10899206+0 records out +//config: +//config:config FEATURE_DD_THIRD_STATUS_LINE +//config: bool "Enable the third status line upon signal" +//config: default y +//config: depends on DD && FEATURE_DD_SIGNAL_HANDLING +//config: help +//config: Displays a coreutils-like third status line with transferred bytes, +//config: elapsed time and speed. +//config: +//config:config FEATURE_DD_IBS_OBS +//config: bool "Enable ibs, obs and conv options" +//config: default y +//config: depends on DD +//config: help +//config: Enables support for writing a certain number of bytes in and out, +//config: at a time, and performing conversions on the data stream. +//config: +//config:config FEATURE_DD_STATUS +//config: bool "Enable status display options" +//config: default y +//config: depends on DD +//config: help +//config: Enables support for status=noxfer/none option. + //usage:#define dd_trivial_usage //usage: "[if=FILE] [of=FILE] " IF_FEATURE_DD_IBS_OBS("[ibs=N] [obs=N] ") "[bs=N] [count=N] [skip=N]\n" //usage: " [seek=N]" IF_FEATURE_DD_IBS_OBS(" [conv=notrunc|noerror|sync|fsync]") @@ -32,8 +77,12 @@ //usage: "\n conv=fsync Physically write data out before finishing" //usage: "\n conv=swab Swap every pair of bytes" //usage: ) +//usage: IF_FEATURE_DD_STATUS( +//usage: "\n status=noxfer Suppress rate output" +//usage: "\n status=none Suppress all output" +//usage: ) //usage: "\n" -//usage: "\nN may be suffixed by c (1), w (2), b (512), kD (1000), k (1024), MD, M, GD, G" +//usage: "\nN may be suffixed by c (1), w (2), b (512), kB (1000), k (1024), MB, M, GB, G" //usage: //usage:#define dd_example_usage //usage: "$ dd if=/dev/zero of=/dev/ram1 bs=1M count=4\n" @@ -54,13 +103,18 @@ static const struct suffix_mult dd_suffixes[] = { { "c", 1 }, { "w", 2 }, { "b", 512 }, + { "kB", 1000 }, { "kD", 1000 }, { "k", 1024 }, - { "K", 1024 }, /* compat with coreutils dd */ + { "K", 1024 }, /* compat with coreutils dd (it also accepts KB and KD, TODO?) */ + { "MB", 1000000 }, { "MD", 1000000 }, - { "M", 1048576 }, + { "M", 1024*1024 }, + { "GB", 1000000000 }, { "GD", 1000000000 }, - { "G", 1073741824 }, + { "G", 1024*1024*1024 }, + /* "D" suffix for decimal is not in coreutils manpage, looks like it's deprecated */ + /* coreutils also understands TPEZY suffixes for tera- and so on, with B suffix for decimal */ { "", 0 } }; @@ -70,6 +124,7 @@ struct globals { unsigned long long total_bytes; unsigned long long begin_time_us; #endif + int flags; } FIX_ALIASING; #define G (*(struct globals*)&bb_common_bufsiz1) #define INIT_G() do { \ @@ -77,6 +132,21 @@ struct globals { memset(&G, 0, sizeof(G)); \ } while (0) +enum { + /* Must be in the same order as OP_conv_XXX! */ + /* (see "flags |= (1 << what)" below) */ + FLAG_NOTRUNC = (1 << 0) * ENABLE_FEATURE_DD_IBS_OBS, + FLAG_SYNC = (1 << 1) * ENABLE_FEATURE_DD_IBS_OBS, + FLAG_NOERROR = (1 << 2) * ENABLE_FEATURE_DD_IBS_OBS, + FLAG_FSYNC = (1 << 3) * ENABLE_FEATURE_DD_IBS_OBS, + FLAG_SWAB = (1 << 4) * ENABLE_FEATURE_DD_IBS_OBS, + /* end of conv flags */ + FLAG_TWOBUFS = (1 << 5) * ENABLE_FEATURE_DD_IBS_OBS, + FLAG_COUNT = 1 << 6, + FLAG_STATUS = 1 << 7, + FLAG_STATUS_NONE = 1 << 7, + FLAG_STATUS_NOXFER = 1 << 8, +}; static void dd_output_status(int UNUSED_PARAM cur_signal) { @@ -93,6 +163,13 @@ static void dd_output_status(int UNUSED_PARAM cur_signal) G.out_full, G.out_part); #if ENABLE_FEATURE_DD_THIRD_STATUS_LINE +# if ENABLE_FEATURE_DD_STATUS + if (G.flags & FLAG_STATUS_NOXFER) /* status=noxfer active? */ + return; + //TODO: should status=none make dd stop reacting to USR1 entirely? + //So far we react to it (we print the stats), + //status=none only suppresses final, non-USR1 generated status message. +# endif fprintf(stderr, "%llu bytes (%sB) copied, ", G.total_bytes, /* show fractional digit, use suffixes */ @@ -148,20 +225,8 @@ static bool write_and_stats(const void *buf, size_t len, size_t obs, int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int dd_main(int argc UNUSED_PARAM, char **argv) { - enum { - /* Must be in the same order as OP_conv_XXX! */ - /* (see "flags |= (1 << what)" below) */ - FLAG_NOTRUNC = (1 << 0) * ENABLE_FEATURE_DD_IBS_OBS, - FLAG_SYNC = (1 << 1) * ENABLE_FEATURE_DD_IBS_OBS, - FLAG_NOERROR = (1 << 2) * ENABLE_FEATURE_DD_IBS_OBS, - FLAG_FSYNC = (1 << 3) * ENABLE_FEATURE_DD_IBS_OBS, - FLAG_SWAB = (1 << 4) * ENABLE_FEATURE_DD_IBS_OBS, - /* end of conv flags */ - FLAG_TWOBUFS = (1 << 5) * ENABLE_FEATURE_DD_IBS_OBS, - FLAG_COUNT = 1 << 6, - }; static const char keywords[] ALIGN1 = - "bs\0""count\0""seek\0""skip\0""if\0""of\0" + "bs\0""count\0""seek\0""skip\0""if\0""of\0"IF_FEATURE_DD_STATUS("status\0") #if ENABLE_FEATURE_DD_IBS_OBS "ibs\0""obs\0""conv\0" #endif @@ -169,6 +234,10 @@ int dd_main(int argc UNUSED_PARAM, char **argv) #if ENABLE_FEATURE_DD_IBS_OBS static const char conv_words[] ALIGN1 = "notrunc\0""sync\0""noerror\0""fsync\0""swab\0"; +#endif +#if ENABLE_FEATURE_DD_STATUS + static const char status_words[] ALIGN1 = + "none\0""noxfer\0"; #endif enum { OP_bs = 0, @@ -177,6 +246,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv) OP_skip, OP_if, OP_of, + IF_FEATURE_DD_STATUS(OP_status,) #if ENABLE_FEATURE_DD_IBS_OBS OP_ibs, OP_obs, @@ -215,14 +285,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv) #endif /* These are all zeroed at once! */ struct { - int flags; size_t oc; ssize_t prev_read_size; /* for detecting swab failure */ off_t count; off_t seek, skip; const char *infile, *outfile; } Z; -#define flags (Z.flags ) #define oc (Z.oc ) #define prev_read_size (Z.prev_read_size) #define count (Z.count ) @@ -278,7 +346,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv) n = index_in_strings(conv_words, val); if (n < 0) bb_error_msg_and_die(bb_msg_invalid_arg, val, "conv"); - flags |= (1 << n); + G.flags |= (1 << n); if (!arg) /* no ',' left, so this was the last specifier */ break; /* *arg = ','; - to preserve ps listing? */ @@ -294,7 +362,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv) } /* These can be large: */ if (what == OP_count) { - flags |= FLAG_COUNT; + G.flags |= FLAG_COUNT; count = XATOU_SFX(val, dd_suffixes); /*continue;*/ } @@ -314,6 +382,16 @@ int dd_main(int argc UNUSED_PARAM, char **argv) outfile = val; /*continue;*/ } +#if ENABLE_FEATURE_DD_STATUS + if (what == OP_status) { + int n; + n = index_in_strings(status_words, val); + if (n < 0) + bb_error_msg_and_die(bb_msg_invalid_arg, val, "status"); + G.flags |= FLAG_STATUS << n; + /*continue;*/ + } +#endif } /* end of "for (argv[i])" */ //XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever @@ -321,7 +399,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv) obuf = ibuf; #if ENABLE_FEATURE_DD_IBS_OBS if (ibs != obs) { - flags |= FLAG_TWOBUFS; + G.flags |= FLAG_TWOBUFS; obuf = xmalloc(obs); } #endif @@ -341,12 +419,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv) if (outfile) { int oflag = O_WRONLY | O_CREAT; - if (!seek && !(flags & FLAG_NOTRUNC)) + if (!seek && !(G.flags & FLAG_NOTRUNC)) oflag |= O_TRUNC; xmove_fd(xopen(outfile, oflag), ofd); - if (seek && !(flags & FLAG_NOTRUNC)) { + if (seek && !(G.flags & FLAG_NOTRUNC)) { if (ftruncate(ofd, seek * obs) < 0) { struct stat st; @@ -377,7 +455,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv) goto die_outfile; } - while (!(flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) { + while (!(G.flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) { ssize_t n; n = safe_read(ifd, ibuf, ibs); @@ -385,7 +463,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv) break; if (n < 0) { /* "Bad block" */ - if (!(flags & FLAG_NOERROR)) + if (!(G.flags & FLAG_NOERROR)) goto die_infile; bb_simple_perror_msg(infile); /* GNU dd with conv=noerror skips over bad blocks */ @@ -394,7 +472,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv) * conv=noerror just ignores input bad blocks */ n = 0; } - if (flags & FLAG_SWAB) { + if (G.flags & FLAG_SWAB) { uint16_t *p16; ssize_t n2; @@ -419,12 +497,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv) G.in_full++; else { G.in_part++; - if (flags & FLAG_SYNC) { + if (G.flags & FLAG_SYNC) { memset(ibuf + n, 0, ibs - n); n = ibs; } } - if (flags & FLAG_TWOBUFS) { + if (G.flags & FLAG_TWOBUFS) { char *tmp = ibuf; while (n) { size_t d = obs - oc; @@ -446,7 +524,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv) goto out_status; } - if (flags & FLAG_FSYNC) { + if (G.flags & FLAG_FSYNC) { if (fsync(ofd) < 0) goto die_outfile; } @@ -468,11 +546,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv) exitcode = EXIT_SUCCESS; out_status: - dd_output_status(0); + if (!ENABLE_FEATURE_DD_STATUS || !(G.flags & FLAG_STATUS_NONE)) + dd_output_status(0); if (ENABLE_FEATURE_CLEAN_UP) { free(obuf); - if (flags & FLAG_TWOBUFS) + if (G.flags & FLAG_TWOBUFS) free(ibuf); } -- cgit v1.2.3-55-g6feb From a03195941b40f0d740f06d23648f672fe81d4ecf Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 7 Feb 2015 18:14:39 +0100 Subject: examples/mdev_fat.conf: document that newer mdev exposes path Signed-off-by: Denys Vlasenko --- examples/mdev_fat.conf | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/mdev_fat.conf b/examples/mdev_fat.conf index f2a15f35d..630d2700a 100644 --- a/examples/mdev_fat.conf +++ b/examples/mdev_fat.conf @@ -19,7 +19,7 @@ # support module loading on hotplug $MODALIAS=.* root:root 660 @modprobe "$MODALIAS" -# null may already exist; therefore ownership has to be changed with command +# null may already exist; therefore mode has to be changed with command null root:root 666 @chmod 666 $MDEV zero root:root 666 full root:root 666 @@ -31,7 +31,7 @@ grsec root:root 660 kmem root:root 640 mem root:root 640 port root:root 640 -# console may already exist; therefore ownership has to be changed with command +# console may already exist; therefore mode has to be changed with command console root:tty 600 @chmod 600 $MDEV ptmx root:tty 666 pty.* root:tty 660 @@ -63,6 +63,12 @@ control.* root:audio 660 =snd/ midi.* root:audio 660 =snd/ seq root:audio 660 =snd/ timer root:audio 660 =snd/ +# for kernels/mdevs which expose full path of alsa devices: +snd/pcm.* root:audio 660 +snd/control.* root:audio 660 +snd/midi.* root:audio 660 +snd/seq root:audio 660 +snd/timer root:audio 660 adsp root:audio 660 >sound/ audio root:audio 660 >sound/ @@ -139,6 +145,6 @@ dahdi!(.*) root:dialout 660 =dahdi/%1 # We are hooking to the last events. To avoid having two scripts running, # we check for DISK_MEDIA_CHANGE=1 (prev to last event has it, # and it's the _only_ difference in env for these two events as of kernel 3.7.7) -# Unfortunately, there is no event for "user pressed [Turn USB storage] btn"! +# Unfortunately, there is no event for "user pressed [Turn USB storage on] btn"! # Script merely backgrounds and tries to rescan partition table for 1 minute: ACTION=change;SUBSYSTEM=block;DISK_MEDIA_CHANGE=1;.* 0:0 660 */etc/mdev.conf.change_blockdev.sh -- cgit v1.2.3-55-g6feb From 782ee2aa0e1646aebc96c8590ddc0a16405b8297 Mon Sep 17 00:00:00 2001 From: Kevin Wallace Date: Tue, 3 Feb 2015 16:17:29 -0800 Subject: libbb/loop: don't try to re-use existing loop device loopinfo.lo_file_name is not enough to uniquely identify a file on a system with multiple mount namespaces. We could conceivably change this to dedup on (lo_rdevice, lo_inode), but, as the comment above the deleted code notes, this whole approach of reusing devices is racy anyway, so it seems better to stop doing it entirely. Signed-off-by: Kevin Wallace Signed-off-by: Denys Vlasenko --- libbb/loop.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/libbb/loop.c b/libbb/loop.c index c96c5e070..d30b378d7 100644 --- a/libbb/loop.c +++ b/libbb/loop.c @@ -154,16 +154,7 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse else ioctl(dfd, LOOP_CLR_FD, 0); } - - /* If this block device already set up right, re-use it. - * (Yes this is racy, but associating two loop devices with the same - * file isn't pretty either. In general, mounting the same file twice - * without using losetup manually is problematic.) - */ - } else - if (strcmp(file, (char *)loopinfo.lo_file_name) != 0 - || offset != loopinfo.lo_offset - ) { + } else { rc = -1; } close(dfd); -- cgit v1.2.3-55-g6feb From 68c048fb23bd8b0831bbd02ec66900b12390cf19 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 7 Feb 2015 20:44:46 +0100 Subject: modprobe-small: fix and simplify rmmod "rmmod OUT_OF_TREE_MODULE" was not working, because module is not in depmod file. In general, rmmod doesn't need scanning, it simply unloads every argv[i]. function old new delta rmmod - 63 +63 modprobe_main 449 465 +16 process_module 705 667 -38 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 1/1 up/down: 79/-38) Total: 41 bytes Signed-off-by: Denys Vlasenko --- modutils/modprobe-small.c | 75 +++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c index cafbdc0b2..6b0a4400c 100644 --- a/modutils/modprobe-small.c +++ b/modutils/modprobe-small.c @@ -549,9 +549,23 @@ static int already_loaded(const char *name) return ret; } #else -#define already_loaded(name) is_rmmod +#define already_loaded(name) 0 #endif +static int rmmod(const char *filename) +{ + int r; + char modname[MODULE_NAME_LEN]; + + filename2modname(filename, 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 && !(option_mask32 & OPT_q)) { + bb_perror_msg("remove '%s'", modname); + } + return r; +} + /* * Given modules definition and module name (or alias, or symbol) * load/remove the module respecting dependencies. @@ -568,26 +582,36 @@ static void process_module(char *name, const char *cmdline_options) module_info **infovec; module_info *info; int infoidx; - int is_rmmod = (option_mask32 & OPT_r) != 0; + int is_remove = (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); + dbg1_error_msg("already_loaded:%d is_remove:%d", already_loaded(name), is_remove); + + if (applet_name[0] == 'r') { + /* rmmod. + * Does not remove dependencies, no need to scan, just remove. + * (compat note: this allows and strips .ko suffix) + */ + rmmod(name); + return; + } + /* - * We used to have "is_rmmod != already_loaded(name)" check here, but + * We used to have "is_remove != 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)) { + if (!is_remove && already_loaded(name)) { dbg1_error_msg("nothing to do for '%s'", name); return; } options = NULL; - if (!is_rmmod) { + if (!is_remove) { char *opt_filename = xasprintf("/etc/modules/%s", name); options = xmalloc_open_read_close(opt_filename, NULL); if (options) @@ -621,7 +645,7 @@ static void process_module(char *name, const char *cmdline_options) 0 /* depth */ ); dbg1_error_msg("dirscan complete"); - /* Module was not found, or load failed, or is_rmmod */ + /* Module was not found, or load failed, or is_remove */ if (module_found_idx >= 0) { /* module was found */ infovec = xzalloc(2 * sizeof(infovec[0])); infovec[0] = &modinfo[module_found_idx]; @@ -634,7 +658,7 @@ static void process_module(char *name, const char *cmdline_options) if (!infovec) { /* both dirscan and find_alias found nothing */ - if (!is_rmmod && applet_name[0] != 'd') /* it wasn't rmmod or depmod */ + if (!is_remove && applet_name[0] != 'd') /* it wasn't rmmod or depmod */ bb_error_msg("module '%s' not found", name); //TODO: _and_die()? or should we continue (un)loading modules listed on cmdline? goto ret; @@ -648,29 +672,15 @@ static void process_module(char *name, const char *cmdline_options) * a *list* of modinfo pointers from find_alias(). */ - /* rmmod or modprobe -r? unload module(s) */ - if (is_rmmod) { + /* modprobe -r? unload module(s) */ + if (is_remove) { infoidx = 0; while ((info = infovec[infoidx++]) != NULL) { - int r; - char modname[MODULE_NAME_LEN]; - - filename2modname( - bb_get_last_path_component_nostrip(info->pathname), modname); - r = delete_module(modname, O_NONBLOCK | O_EXCL); - dbg1_error_msg("delete_module('%s', O_NONBLOCK | O_EXCL):%d", modname, r); + int r = rmmod(bb_get_last_path_component_nostrip(info->pathname)); if (r != 0) { - if (!(option_mask32 & OPT_q)) - bb_perror_msg("remove '%s'", modname); - goto ret; + goto ret; /* error */ } } - - if (applet_name[0] == 'r') { - /* rmmod: do not remove dependencies, exit */ - goto ret; - } - /* modprobe -r: we do not stop here - * continue to unload modules on which the module depends: * "-r --remove: option causes modprobe to remove a module. @@ -691,7 +701,7 @@ static void process_module(char *name, const char *cmdline_options) } free(deps); - if (is_rmmod) + if (is_remove) continue; /* We are modprobe: load it */ @@ -894,10 +904,10 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) } #if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE - /* If not rmmod, parse possible module options given on command line. + /* If not rmmod/-r, parse possible module options given on command line. * insmod/modprobe takes one module name, the rest are parameters. */ options = NULL; - if ('r' != applet0) { + if (!(option_mask32 & OPT_r)) { char **arg = argv; while (*++arg) { /* Enclose options in quotes */ @@ -908,7 +918,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) } } #else - if ('r' != applet0) + if (!(option_mask32 & OPT_r)) argv[1] = NULL; #endif @@ -932,10 +942,11 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) } /* Try to load modprobe.dep.bb */ - load_dep_bb(); + if ('r' != applet0) /* not rmmod */ + load_dep_bb(); /* Load/remove modules. - * Only rmmod loops here, modprobe has only argv[0] */ + * Only rmmod/modprobe -r loops here, insmod/modprobe has only argv[0] */ do { process_module(*argv, options); } while (*++argv); -- cgit v1.2.3-55-g6feb From 23cfaab47de7392c1ba7d601a05fb36da3629b28 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 7 Feb 2015 21:21:02 +0100 Subject: libpwdgrp: use getpwent() instead of getpwent_r() function old new delta massage_data_for_non_r_func - 90 +90 bb_internal_getpwent - 69 +69 getXXnam_r 94 162 +68 fill_bounds 131 128 -3 deluser_main 355 310 -45 complete_username 123 78 -45 getXXnam 163 90 -73 massage_data_for_r_func 103 - -103 bb_internal_getpwent_r 121 - -121 ------------------------------------------------------------------------------ (add/remove: 2/2 grow/shrink: 1/5 up/down: 227/-407) Total: -163 bytes Signed-off-by: Denys Vlasenko --- include/pwd_.h | 16 +-------------- libbb/lineedit.c | 13 +++++------- libpwdgrp/pwd_grp.c | 58 ++++++++++++++++++++++++++++++++++++++-------------- loginutils/deluser.c | 9 ++++---- 4 files changed, 53 insertions(+), 43 deletions(-) diff --git a/include/pwd_.h b/include/pwd_.h index 32b5b366e..17348298a 100644 --- a/include/pwd_.h +++ b/include/pwd_.h @@ -36,7 +36,6 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN #define getpwent bb_internal_getpwent #define getpwuid bb_internal_getpwuid #define getpwnam bb_internal_getpwnam -#define getpwent_r bb_internal_getpwent_r #define getpwnam_r bb_internal_getpwnam_r /* All function names below should be remapped by #defines above @@ -48,10 +47,8 @@ void FAST_FUNC setpwent(void); /* Close the password-file stream. */ void FAST_FUNC endpwent(void); -#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS /* Read an entry from the password-file stream, opening it if necessary. */ struct passwd* FAST_FUNC getpwent(void); -#endif /* Search for an entry with a matching user ID. */ struct passwd* FAST_FUNC getpwuid(uid_t __uid); @@ -59,18 +56,7 @@ struct passwd* FAST_FUNC getpwuid(uid_t __uid); /* Search for an entry with a matching username. */ struct passwd* FAST_FUNC getpwnam(const char *__name); -/* Reentrant versions of some of the functions above. - - PLEASE NOTE: the `getpwent_r' function is not (yet) standardized. - The interface may change in later versions of this library. But - the interface is designed following the principals used for the - other reentrant functions so the chances are good this is what the - POSIX people would choose. */ - -int FAST_FUNC getpwent_r(struct passwd *__restrict __resultbuf, - char *__restrict __buffer, size_t __buflen, - struct passwd **__restrict __result); - +/* Reentrant versions of some of the functions above. */ int FAST_FUNC getpwnam_r(const char *__restrict __name, struct passwd *__restrict __resultbuf, char *__restrict __buffer, size_t __buflen, diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 720a4951e..249b401b4 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -672,23 +672,20 @@ static char *username_path_completion(char *ud) */ static NOINLINE unsigned complete_username(const char *ud) { - /* Using _r function to avoid pulling in static buffers */ - char line_buff[256]; - struct passwd pwd; - struct passwd *result; + struct passwd *pw; unsigned userlen; ud++; /* skip ~ */ userlen = strlen(ud); setpwent(); - while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) { + while ((pw = getpwent()) != NULL) { /* Null usernames should result in all users as possible completions. */ - if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) { - add_match(xasprintf("~%s/", pwd.pw_name)); + if (/*!userlen || */ strncmp(ud, pw->pw_name, userlen) == 0) { + add_match(xasprintf("~%s/", pw->pw_name)); } } - endpwent(); + endpwent(); /* don't keep password file open */ return 1 + userlen; } diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 90647e9d3..7ec704ee4 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -336,6 +336,22 @@ static int massage_data_for_r_func(struct passdb *db, return errno; } +static void* massage_data_for_non_r_func(struct passdb *db, char *buf) +{ + if (!buf) + return NULL; + + free(db->malloced); + /* We enlarge buf and move string data up, freeing space + * for struct passwd/group/spwd at the beginning. This way, + * entire result of getXXnam is in a single malloced block. + * This enables easy creation of xmalloc_getpwnam() API. + */ + db->malloced = buf = xrealloc(buf, db->size_of + S.string_size); + memmove(buf + db->size_of, buf, S.string_size); + return convert_to_struct(db, buf + db->size_of, buf); +} + /****** getXXnam/id_r */ static int FAST_FUNC getXXnam_r(const char *name, uintptr_t db_and_field_pos, @@ -372,6 +388,7 @@ int FAST_FUNC getspnam_r(const char *name, struct spwd *struct_buf, char *buffer } #endif +#ifdef UNUSED /****** getXXent_r */ static int FAST_FUNC getXXent_r(uintptr_t db_idx, char *buffer, size_t buflen, @@ -400,17 +417,39 @@ int FAST_FUNC getpwent_r(struct passwd *struct_buf, char *buffer, size_t buflen, *result = struct_buf; return getXXent_r(0, buffer, buflen, result); } +#endif + +/****** getXXent */ + +static void* FAST_FUNC getXXent(uintptr_t db_idx) +{ + char *buf; + struct passdb *db = &get_S()->db[db_idx]; + + if (!db->fp) { + db->fp = fopen_for_read(db->filename); + if (!db->fp) { + return NULL; + } + close_on_exec_on(fileno(db->fp)); + } + + buf = parse_common(db->fp, db, /*no search key:*/ NULL, -1); + return massage_data_for_non_r_func(db, buf); +} + +struct passwd* FAST_FUNC getpwent(void) +{ + return getXXent(0); +} /****** getXXnam/id */ static void* FAST_FUNC getXXnam(const char *name, unsigned db_and_field_pos) { char *buf; - void *result; struct passdb *db = &get_S()->db[db_and_field_pos >> 2]; - result = NULL; - if (!db->fp) { db->fp = fopen_for_read(db->filename); if (!db->fp) { @@ -420,18 +459,7 @@ static void* FAST_FUNC getXXnam(const char *name, unsigned db_and_field_pos) } buf = parse_common(db->fp, db, name, db_and_field_pos & 3); - if (buf) { - free(db->malloced); - /* We enlarge buf and move string data up, freeing space - * for struct passwd/group/spwd at the beginning. This way, - * entire result of getXXnam is in a single malloced block. - * This enables easy creation of xmalloc_getpwnam() API. - */ - db->malloced = buf = xrealloc(buf, db->size_of + S.string_size); - memmove(buf + db->size_of, buf, S.string_size); - result = convert_to_struct(db, buf + db->size_of, buf); - } - return result; + return massage_data_for_non_r_func(db, buf); } struct passwd* FAST_FUNC getpwnam(const char *name) diff --git a/loginutils/deluser.c b/loginutils/deluser.c index 2d98ecc58..01a9386bc 100644 --- a/loginutils/deluser.c +++ b/loginutils/deluser.c @@ -91,12 +91,11 @@ int deluser_main(int argc, char **argv) if (!member) { /* "delgroup GROUP" */ struct passwd *pw; - struct passwd pwent; /* Check if the group is in use */ -#define passwd_buf bb_common_bufsiz1 - while (!getpwent_r(&pwent, passwd_buf, sizeof(passwd_buf), &pw)) { - if (pwent.pw_gid == gr->gr_gid) - bb_error_msg_and_die("'%s' still has '%s' as their primary group!", pwent.pw_name, name); + while ((pw = getpwent()) != NULL) { + if (pw->pw_gid == gr->gr_gid) + bb_error_msg_and_die("'%s' still has '%s' as their primary group!", + pw->pw_name, name); } //endpwent(); } -- cgit v1.2.3-55-g6feb From 8c06bc6ba14949d945eff0abcabab885f1ef7680 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 10 Feb 2015 01:30:43 +0100 Subject: unzip: prevent attacks via malicious filenames Signed-off-by: Denys Vlasenko --- archival/libarchive/Kbuild.src | 5 +++-- archival/libarchive/get_header_tar.c | 30 ------------------------------ archival/libarchive/unsafe_prefix.c | 36 ++++++++++++++++++++++++++++++++++++ archival/unzip.c | 35 ++++++++++++++++++++++++----------- 4 files changed, 63 insertions(+), 43 deletions(-) create mode 100644 archival/libarchive/unsafe_prefix.c diff --git a/archival/libarchive/Kbuild.src b/archival/libarchive/Kbuild.src index 7e89e9e89..b7faaf77f 100644 --- a/archival/libarchive/Kbuild.src +++ b/archival/libarchive/Kbuild.src @@ -30,6 +30,7 @@ COMMON_FILES:= \ DPKG_FILES:= \ unpack_ar_archive.o \ filter_accept_list_reassign.o \ + unsafe_prefix.o \ get_header_ar.o \ get_header_tar.o \ get_header_tar_gz.o \ @@ -44,7 +45,7 @@ lib-$(CONFIG_DPKG_DEB) += $(DPKG_FILES) lib-$(CONFIG_AR) += get_header_ar.o unpack_ar_archive.o lib-$(CONFIG_CPIO) += get_header_cpio.o -lib-$(CONFIG_TAR) += get_header_tar.o +lib-$(CONFIG_TAR) += get_header_tar.o unsafe_prefix.o lib-$(CONFIG_FEATURE_TAR_TO_COMMAND) += data_extract_to_command.o lib-$(CONFIG_LZOP) += lzo1x_1.o lzo1x_1o.o lzo1x_d.o lib-$(CONFIG_LZOP_COMPR_HIGH) += lzo1x_9x.o @@ -53,7 +54,7 @@ lib-$(CONFIG_UNLZMA) += open_transformer.o decompress_unlzma. lib-$(CONFIG_UNXZ) += open_transformer.o decompress_unxz.o lib-$(CONFIG_GUNZIP) += open_transformer.o decompress_gunzip.o lib-$(CONFIG_UNCOMPRESS) += open_transformer.o decompress_uncompress.o -lib-$(CONFIG_UNZIP) += open_transformer.o decompress_gunzip.o +lib-$(CONFIG_UNZIP) += open_transformer.o decompress_gunzip.o unsafe_prefix.o lib-$(CONFIG_RPM2CPIO) += open_transformer.o decompress_gunzip.o get_header_cpio.o lib-$(CONFIG_RPM) += open_transformer.o decompress_gunzip.o get_header_cpio.o diff --git a/archival/libarchive/get_header_tar.c b/archival/libarchive/get_header_tar.c index ba43bb073..0c663fbd7 100644 --- a/archival/libarchive/get_header_tar.c +++ b/archival/libarchive/get_header_tar.c @@ -17,36 +17,6 @@ typedef uint32_t aliased_uint32_t FIX_ALIASING; typedef off_t aliased_off_t FIX_ALIASING; - -const char* FAST_FUNC strip_unsafe_prefix(const char *str) -{ - const char *cp = str; - while (1) { - char *cp2; - if (*cp == '/') { - cp++; - continue; - } - if (strncmp(cp, "/../"+1, 3) == 0) { - cp += 3; - continue; - } - cp2 = strstr(cp, "/../"); - if (!cp2) - break; - cp = cp2 + 4; - } - if (cp != str) { - static smallint warned = 0; - if (!warned) { - warned = 1; - bb_error_msg("removing leading '%.*s' from member names", - (int)(cp - str), str); - } - } - return cp; -} - /* NB: _DESTROYS_ str[len] character! */ static unsigned long long getOctal(char *str, int len) { diff --git a/archival/libarchive/unsafe_prefix.c b/archival/libarchive/unsafe_prefix.c new file mode 100644 index 000000000..826c673bf --- /dev/null +++ b/archival/libarchive/unsafe_prefix.c @@ -0,0 +1,36 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "bb_archive.h" + +const char* FAST_FUNC strip_unsafe_prefix(const char *str) +{ + const char *cp = str; + while (1) { + char *cp2; + if (*cp == '/') { + cp++; + continue; + } + if (strncmp(cp, "/../"+1, 3) == 0) { + cp += 3; + continue; + } + cp2 = strstr(cp, "/../"); + if (!cp2) + break; + cp = cp2 + 4; + } + if (cp != str) { + static smallint warned = 0; + if (!warned) { + warned = 1; + bb_error_msg("removing leading '%.*s' from member names", + (int)(cp - str), str); + } + } + return cp; +} diff --git a/archival/unzip.c b/archival/unzip.c index 38a07e212..eed225677 100644 --- a/archival/unzip.c +++ b/archival/unzip.c @@ -596,14 +596,18 @@ int unzip_main(int argc, char **argv) /* Skip extra header bytes */ unzip_skip(zip_header.formatted.extra_len); + /* Guard against "/abspath", "/../" and similar attacks */ + overlapping_strcpy(dst_fn, strip_unsafe_prefix(dst_fn)); + /* Filter zip entries */ if (find_list_entry(zreject, dst_fn) || (zaccept && !find_list_entry(zaccept, dst_fn)) ) { /* Skip entry */ i = 'n'; - } else { /* Extract entry */ - if (listing) { /* List entry */ + } else { + if (listing) { + /* List entry */ unsigned dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16); if (!verbose) { // " Length Date Time Name\n" @@ -639,9 +643,11 @@ int unzip_main(int argc, char **argv) total_size += zip_header.formatted.cmpsize; } i = 'n'; - } else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */ + } else if (dst_fd == STDOUT_FILENO) { + /* Extracting to STDOUT */ i = -1; - } else if (last_char_is(dst_fn, '/')) { /* Extract directory */ + } else if (last_char_is(dst_fn, '/')) { + /* Extract directory */ if (stat(dst_fn, &stat_buf) == -1) { if (errno != ENOENT) { bb_perror_msg_and_die("can't stat '%s'", dst_fn); @@ -655,22 +661,27 @@ int unzip_main(int argc, char **argv) } } else { if (!S_ISDIR(stat_buf.st_mode)) { - bb_error_msg_and_die("'%s' exists but is not directory", dst_fn); + bb_error_msg_and_die("'%s' exists but is not a %s", + dst_fn, "directory"); } } i = 'n'; - } else { /* Extract file */ + } else { + /* Extract file */ check_file: - if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */ + if (stat(dst_fn, &stat_buf) == -1) { + /* File does not exist */ if (errno != ENOENT) { bb_perror_msg_and_die("can't stat '%s'", dst_fn); } i = 'y'; - } else { /* File already exists */ + } else { + /* File already exists */ if (overwrite == O_NEVER) { i = 'n'; - } else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */ + } else if (S_ISREG(stat_buf.st_mode)) { + /* File is regular file */ if (overwrite == O_ALWAYS) { i = 'y'; } else { @@ -678,8 +689,10 @@ int unzip_main(int argc, char **argv) my_fgets80(key_buf); i = key_buf[0]; } - } else { /* File is not regular file */ - bb_error_msg_and_die("'%s' exists but is not regular file", dst_fn); + } else { + /* File is not regular file */ + bb_error_msg_and_die("'%s' exists but is not a %s", + dst_fn, "regular file"); } } } -- cgit v1.2.3-55-g6feb From 622a7aab2c4a918c0e71931505f5c38d66d81ad9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Feb 2015 03:16:25 +0100 Subject: i2cget, i2cset, i2cdetect, i2cdump: new applets Add a minimal implementation of i2cget, i2cset, i2cdump and i2cdetect tools. Supports most features of upstream i2c-tools. function old new delta i2cdump_main - 1444 +1444 i2cset_main - 1239 +1239 i2cdetect_main - 611 +611 list_i2c_busses_and_exit - 532 +532 packed_usage 29975 30438 +463 i2cget_main - 380 +380 check_read_funcs - 140 +140 i2c_funcs_tab - 128 +128 confirm_action - 100 +100 i2c_dev_open - 57 +57 i2c_smbus_access - 44 +44 confirm_or_abort - 43 +43 check_funcs_test_end - 39 +39 i2c_smbus_read_word_data - 38 +38 i2c_smbus_read_byte_data - 38 +38 i2c_smbus_read_byte - 37 +37 i2c_set_slave_addr - 32 +32 applet_names 2480 2512 +32 adap_descs - 32 +32 i2c_set_pec - 29 +29 get_funcs_matrix - 21 +21 i2c_parse_data_addr - 18 +18 i2c_parse_bus_addr - 18 +18 i2c_bus_lookup - 18 +18 i2c_smbus_write_byte - 17 +17 applet_main 1440 1456 +16 will_skip - 14 +14 no_support - 11 +11 applet_nameofs 720 728 +8 applet_install_loc 180 182 +2 ------------------------------------------------------------------------------ (add/remove: 26/0 grow/shrink: 5/0 up/down: 5601/0) Total: 5601 bytes Signed-off-by: Bartosz Golaszewski Signed-off-by: Denys Vlasenko --- miscutils/i2c_tools.c | 1396 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1396 insertions(+) create mode 100644 miscutils/i2c_tools.c diff --git a/miscutils/i2c_tools.c b/miscutils/i2c_tools.c new file mode 100644 index 000000000..90d1e1e14 --- /dev/null +++ b/miscutils/i2c_tools.c @@ -0,0 +1,1396 @@ +/* vi: set sw=4 ts=4: */ +/* + * Minimal i2c-tools implementation for busybox. + * Parts of code ported from i2c-tools: + * http://www.lm-sensors.org/wiki/I2CTools. + * + * Copyright (C) 2014 by Bartosz Golaszewski + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//config:config I2CGET +//config: bool "i2cget" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: Read from I2C/SMBus chip registers. +//config: +//config:config I2CSET +//config: bool "i2cset" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: Set I2C registers. +//config: +//config:config I2CDUMP +//config: bool "i2cdump" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: Examine I2C registers. +//config: +//config:config I2CDETECT +//config: bool "i2cdetect" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: Detect I2C chips. +//config: + +//applet:IF_I2CGET(APPLET(i2cget, BB_DIR_USR_SBIN, BB_SUID_DROP)) +//applet:IF_I2CSET(APPLET(i2cset, BB_DIR_USR_SBIN, BB_SUID_DROP)) +//applet:IF_I2CDUMP(APPLET(i2cdump, BB_DIR_USR_SBIN, BB_SUID_DROP)) +//applet:IF_I2CDETECT(APPLET(i2cdetect, BB_DIR_USR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_I2CGET) += i2c_tools.o +//kbuild:lib-$(CONFIG_I2CSET) += i2c_tools.o +//kbuild:lib-$(CONFIG_I2CDUMP) += i2c_tools.o +//kbuild:lib-$(CONFIG_I2CDETECT) += i2c_tools.o + +/* + * Unsupported stuff: + * + * - upstream i2c-tools can also look-up i2c busses by name, we only accept + * numbers, + * - bank and bankreg parameters for i2cdump are not supported because of + * their limited usefulness (see i2cdump manual entry for more info), + * - i2cdetect doesn't look for bus info in /proc as it does in upstream, but + * it shouldn't be a problem in modern kernels. + */ + +#include "libbb.h" + +/* + * /dev/i2c-X ioctl commands. The ioctl's parameter is always an unsigned long, + * except for: + * - I2C_FUNCS, takes pointer to an unsigned long + * - I2C_RDWR, takes pointer to struct i2c_rdwr_ioctl_data + * - I2C_SMBUS, takes pointer to struct i2c_smbus_ioctl_data + */ + +/* + * NOTE: Slave address is 7 or 10 bits, but 10-bit addresses + * are not supported due to code brokenness. + */ + +/* Use this slave address. */ +#define I2C_SLAVE 0x0703 +/* Use this slave address, even if it is already in use by a driver. */ +#define I2C_SLAVE_FORCE 0x0706 +/* 0 for 7 bit addrs, != 0 for 10 bit. */ +#define I2C_TENBIT 0x0704 +/* Get the adapter functionality mask. */ +#define I2C_FUNCS 0x0705 +/* Combined R/W transfer (one STOP only). */ +#define I2C_RDWR 0x0707 +/* != 0 to use PEC with SMBus. */ +#define I2C_PEC 0x0708 +/* SMBus transfer. */ +#define I2C_SMBUS 0x0720 + +/* Structure used in the I2C_SMBUS ioctl call. */ +struct i2c_smbus_ioctl_data { + uint8_t read_write; + uint8_t command; + uint32_t size; + union i2c_smbus_data *data; +}; + +/* Structure used in the I2C_RDWR ioctl call. */ +struct i2c_rdwr_ioctl_data { + struct i2c_msg *msgs; /* Pointers to i2c_msgs. */ + uint32_t nmsgs; /* Number of i2c_msgs. */ +}; + +/* As specified in SMBus standard. */ +#define I2C_SMBUS_BLOCK_MAX 32 +/* Not specified but we use same structure. */ +#define I2C_SMBUS_I2C_BLOCK_MAX 32 + +/* Data for SMBus Messages. */ +union i2c_smbus_data { + uint8_t byte; + uint16_t word; + /* block[0] is used for length and one more for PEC */ + uint8_t block[I2C_SMBUS_BLOCK_MAX + 2]; +}; + +#define I2C_RDRW_IOCTL_MAX_MSGS 42 +#define I2C_MAX_REGS 256 + +/* Smbus_access read or write markers. */ +#define I2C_SMBUS_READ 1 +#define I2C_SMBUS_WRITE 0 + +/* SMBus transaction types (size parameter in the below functions). */ +#define I2C_SMBUS_QUICK 0 +#define I2C_SMBUS_BYTE 1 +#define I2C_SMBUS_BYTE_DATA 2 +#define I2C_SMBUS_WORD_DATA 3 +#define I2C_SMBUS_PROC_CALL 4 +#define I2C_SMBUS_BLOCK_DATA 5 +#define I2C_SMBUS_I2C_BLOCK_BROKEN 6 +#define I2C_SMBUS_BLOCK_PROC_CALL 7 +#define I2C_SMBUS_I2C_BLOCK_DATA 8 + +#define DETECT_MODE_AUTO 0 +#define DETECT_MODE_QUICK 1 +#define DETECT_MODE_READ 2 + +/* Defines to determine what functionality is present. */ +#define I2C_FUNC_I2C 0x00000001 +#define I2C_FUNC_10BIT_ADDR 0x00000002 +#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 +#define I2C_FUNC_SMBUS_PEC 0x00000008 +#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 +#define I2C_FUNC_SMBUS_QUICK 0x00010000 +#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000 +#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000 +#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000 +#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000 +#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000 +#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000 +#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000 +#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000 +#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 +#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 +#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 + +#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \ + I2C_FUNC_SMBUS_WRITE_BYTE) +#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \ + I2C_FUNC_SMBUS_WRITE_BYTE_DATA) +#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \ + I2C_FUNC_SMBUS_WRITE_WORD_DATA) +#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \ + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) +#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \ + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK) + +/* + * This is needed for ioctl_or_perror_and_die() since it only accepts pointers. + */ +static ALWAYS_INLINE void *itoptr(int i) +{ + return (void*)(intptr_t)i; +} + +static int32_t i2c_smbus_access(int fd, char read_write, uint8_t cmd, + int size, union i2c_smbus_data *data) +{ + struct i2c_smbus_ioctl_data args; + + args.read_write = read_write; + args.command = cmd; + args.size = size; + args.data = data; + + return ioctl(fd, I2C_SMBUS, &args); +} + +static int32_t i2c_smbus_read_byte(int fd) +{ + union i2c_smbus_data data; + int err; + + err = i2c_smbus_access(fd, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data); + if (err < 0) + return err; + + return data.byte; +} + +#if ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP +static int32_t i2c_smbus_write_byte(int fd, uint8_t val) +{ + return i2c_smbus_access(fd, I2C_SMBUS_WRITE, + val, I2C_SMBUS_BYTE, NULL); +} + +static int32_t i2c_smbus_read_byte_data(int fd, uint8_t cmd) +{ + union i2c_smbus_data data; + int err; + + err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd, + I2C_SMBUS_BYTE_DATA, &data); + if (err < 0) + return err; + + return data.byte; +} + +static int32_t i2c_smbus_read_word_data(int fd, uint8_t cmd) +{ + union i2c_smbus_data data; + int err; + + err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd, + I2C_SMBUS_WORD_DATA, &data); + if (err < 0) + return err; + + return data.word; +} +#endif /* ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP */ + +#if ENABLE_I2CSET +static int32_t i2c_smbus_write_byte_data(int file, + uint8_t cmd, uint8_t value) +{ + union i2c_smbus_data data; + + data.byte = value; + + return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd, + I2C_SMBUS_BYTE_DATA, &data); +} + +static int32_t i2c_smbus_write_word_data(int file, uint8_t cmd, uint16_t value) +{ + union i2c_smbus_data data; + + data.word = value; + + return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd, + I2C_SMBUS_WORD_DATA, &data); +} + +static int32_t i2c_smbus_write_block_data(int file, uint8_t cmd, + uint8_t length, const uint8_t *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + + memcpy(data.block+1, values, length); + data.block[0] = length; + + return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd, + I2C_SMBUS_BLOCK_DATA, &data); +} + +static int32_t i2c_smbus_write_i2c_block_data(int file, uint8_t cmd, + uint8_t length, const uint8_t *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + + memcpy(data.block+1, values, length); + data.block[0] = length; + + return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd, + I2C_SMBUS_I2C_BLOCK_BROKEN, &data); +} +#endif /* ENABLE_I2CSET */ + +#if ENABLE_I2CDUMP +/* + * Returns the number of bytes read, vals must hold at + * least I2C_SMBUS_BLOCK_MAX bytes. + */ +static int32_t i2c_smbus_read_block_data(int fd, uint8_t cmd, uint8_t *vals) +{ + union i2c_smbus_data data; + int i, err; + + err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd, + I2C_SMBUS_BLOCK_DATA, &data); + if (err < 0) + return err; + + for (i = 1; i <= data.block[0]; i++) + *vals++ = data.block[i]; + return data.block[0]; +} + +static int32_t i2c_smbus_read_i2c_block_data(int fd, uint8_t cmd, + uint8_t len, uint8_t *vals) +{ + union i2c_smbus_data data; + int i, err; + + if (len > I2C_SMBUS_BLOCK_MAX) + len = I2C_SMBUS_BLOCK_MAX; + data.block[0] = len; + + err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd, + len == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN : + I2C_SMBUS_I2C_BLOCK_DATA, &data); + if (err < 0) + return err; + + for (i = 1; i <= data.block[0]; i++) + *vals++ = data.block[i]; + return data.block[0]; +} +#endif /* ENABLE_I2CDUMP */ + +#if ENABLE_I2CDETECT +static int32_t i2c_smbus_write_quick(int fd, uint8_t val) +{ + return i2c_smbus_access(fd, val, 0, I2C_SMBUS_QUICK, NULL); +} +#endif /* ENABLE_I2CDETECT */ + +static int i2c_bus_lookup(const char *bus_str) +{ + return xstrtou_range(bus_str, 10, 0, 0xfffff); +} + +#if ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP +static int i2c_parse_bus_addr(const char *addr_str) +{ + /* Slave address must be in range 0x03 - 0x77. */ + return xstrtou_range(addr_str, 16, 0x03, 0x77); +} + +static void i2c_set_pec(int fd, int pec) +{ + ioctl_or_perror_and_die(fd, I2C_PEC, + itoptr(pec ? 1 : 0), + "can't set PEC"); +} +#endif /* ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP */ + +#if ENABLE_I2CGET || ENABLE_I2CSET +static int i2c_parse_data_addr(const char *data_addr) +{ + /* Data address must be an 8 bit integer. */ + return xstrtou_range(data_addr, 16, 0, 0xff); +} +#endif /* ENABLE_I2CGET || ENABLE_I2CSET */ + +/* + * Opens the device file associated with given i2c bus. + * + * Upstream i2c-tools also support opening devices by i2c bus name + * but we drop it here for size reduction. + */ +static int i2c_dev_open(int i2cbus) +{ + char filename[sizeof("/dev/i2c-%d") + sizeof(int)*3]; + int fd; + + sprintf(filename, "/dev/i2c-%d", i2cbus); + fd = open(filename, O_RDWR); + if (fd < 0) { + filename[8] = '/'; /* change to "/dev/i2c/%d" */ + fd = xopen(filename, O_RDWR); + } + + return fd; +} + +static void i2c_set_slave_addr(int fd, int addr, int force) +{ + ioctl_or_perror_and_die(fd, force ? I2C_SLAVE_FORCE : I2C_SLAVE, + itoptr(addr), + "can't set address to 0x%02x", addr); +} + +/* Size reducing helpers for xxx_check_funcs(). */ +static void get_funcs_matrix(int fd, unsigned long *funcs) +{ + ioctl_or_perror_and_die(fd, I2C_FUNCS, funcs, + "can't get adapter functionality matrix"); +} + +#if ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP +static void check_funcs_test_end(int funcs, int pec, const char *err) +{ + if (pec && !(funcs & (I2C_FUNC_SMBUS_PEC | I2C_FUNC_I2C))) + bb_error_msg("warning: adapter does not support PEC"); + + if (err) + bb_error_msg_and_die( + "adapter has no %s capability", err); +} +#endif /* ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP */ + +/* + * The below functions emit an error message and exit if the adapter doesn't + * support desired functionalities. + */ +#if ENABLE_I2CGET || ENABLE_I2CDUMP +static void check_read_funcs(int fd, int mode, int data_addr, int pec) +{ + unsigned long funcs; + const char *err = NULL; + + get_funcs_matrix(fd, &funcs); + switch (mode) { + case I2C_SMBUS_BYTE: + if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE)) { + err = "SMBus receive byte"; + break; + } + if (data_addr >= 0 && !(funcs & I2C_FUNC_SMBUS_WRITE_BYTE)) + err = "SMBus send byte"; + break; + case I2C_SMBUS_BYTE_DATA: + if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA)) + err = "SMBus read byte"; + break; + case I2C_SMBUS_WORD_DATA: + if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA)) + err = "SMBus read word"; + break; +#if ENABLE_I2CDUMP + case I2C_SMBUS_BLOCK_DATA: + if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + err = "SMBus block read"; + break; + + case I2C_SMBUS_I2C_BLOCK_DATA: + if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + err = "I2C block read"; + break; +#endif /* ENABLE_I2CDUMP */ + default: + bb_error_msg_and_die("Programmer goofed!"); + } + check_funcs_test_end(funcs, pec, err); +} +#endif /* ENABLE_I2CGET || ENABLE_I2CDUMP */ + +#if ENABLE_I2CSET +static void check_write_funcs(int fd, int mode, int pec) +{ + unsigned long funcs; + const char *err = NULL; + + get_funcs_matrix(fd, &funcs); + switch (mode) { + case I2C_SMBUS_BYTE: + if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE)) + err = "SMBus send byte"; + break; + + case I2C_SMBUS_BYTE_DATA: + if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + err = "SMBus write byte"; + break; + + case I2C_SMBUS_WORD_DATA: + if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA)) + err = "SMBus write word"; + break; + + case I2C_SMBUS_BLOCK_DATA: + if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)) + err = "SMBus block write"; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) + err = "I2C block write"; + break; + } + check_funcs_test_end(funcs, pec, err); +} +#endif /* ENABLE_I2CSET */ + +static void confirm_or_abort(void) +{ + fprintf(stderr, "Continue? [y/N] "); + fflush_all(); + if (!bb_ask_confirmation()) + bb_error_msg_and_die("aborting"); +} + +/* + * Return only if user confirms the action, abort otherwise. + * + * The messages displayed here are much less elaborate than their i2c-tools + * counterparts - this is done for size reduction. + */ +static void confirm_action(int bus_addr, int mode, int data_addr, int pec) +{ + bb_error_msg("WARNING! This program can confuse your I2C bus"); + + /* Don't let the user break his/her EEPROMs */ + if (bus_addr >= 0x50 && bus_addr <= 0x57 && pec) { + bb_error_msg_and_die("this is I2C not smbus - using PEC on I2C " + "devices may result in data loss, aborting"); + } + + if (mode == I2C_SMBUS_BYTE && data_addr >= 0 && pec) + bb_error_msg("WARNING! May interpret a write byte command " + "with PEC as a write byte data command"); + + if (pec) + bb_error_msg("PEC checking enabled"); + + confirm_or_abort(); +} + +#if ENABLE_I2CGET +//usage:#define i2cget_trivial_usage +//usage: "[-f] [-y] BUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]" +//usage:#define i2cget_full_usage "\n\n" +//usage: "Read from I2C/SMBus chip registers\n" +//usage: "\n I2CBUS i2c bus number" +//usage: "\n ADDRESS 0x03 - 0x77" +//usage: "\nMODE is:" +//usage: "\n b read byte data (default)" +//usage: "\n w read word data" +//usage: "\n c write byte/read byte" +//usage: "\n Append p for SMBus PEC" +//usage: "\n" +//usage: "\n -f force access" +//usage: "\n -y disable interactive mode" +int i2cget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int i2cget_main(int argc UNUSED_PARAM, char **argv) +{ + const unsigned opt_f = (1 << 0), opt_y = (1 << 1); + const char *const optstr = "fy"; + + int bus_num, bus_addr, data_addr = -1, status; + int mode = I2C_SMBUS_BYTE, pec = 0, fd; + unsigned opts; + + opt_complementary = "-2:?4"; /* from 2 to 4 args */ + opts = getopt32(argv, optstr); + argv += optind; + + bus_num = i2c_bus_lookup(argv[0]); + bus_addr = i2c_parse_bus_addr(argv[1]); + + if (argv[2]) { + data_addr = i2c_parse_data_addr(argv[2]); + mode = I2C_SMBUS_BYTE_DATA; + if (argv[3]) { + switch (argv[3][0]) { + case 'b': /* Already set */ break; + case 'w': mode = I2C_SMBUS_WORD_DATA; break; + case 'c': mode = I2C_SMBUS_BYTE; break; + default: + bb_error_msg("invalid mode"); + bb_show_usage(); + } + pec = argv[3][1] == 'p'; + } + } + + fd = i2c_dev_open(bus_num); + check_read_funcs(fd, mode, data_addr, pec); + i2c_set_slave_addr(fd, bus_addr, opts & opt_f); + + if (!(opts & opt_y)) + confirm_action(bus_addr, mode, data_addr, pec); + + if (pec) + i2c_set_pec(fd, 1); + + switch (mode) { + case I2C_SMBUS_BYTE: + if (data_addr >= 0) { + status = i2c_smbus_write_byte(fd, data_addr); + if (status < 0) + bb_error_msg("warning - write failed"); + } + status = i2c_smbus_read_byte(fd); + break; + case I2C_SMBUS_WORD_DATA: + status = i2c_smbus_read_word_data(fd, data_addr); + break; + default: /* I2C_SMBUS_BYTE_DATA */ + status = i2c_smbus_read_byte_data(fd, data_addr); + } + close(fd); + + if (status < 0) + bb_perror_msg_and_die("read failed"); + + printf("0x%0*x\n", mode == I2C_SMBUS_WORD_DATA ? 4 : 2, status); + + return 0; +} +#endif /* ENABLE_I2CGET */ + +#if ENABLE_I2CSET +//usage:#define i2cset_trivial_usage +//usage: "[-f] [-y] [-m MASK] BUS CHIP-ADDR DATA-ADDR [VALUE] ... [MODE]" +//usage:#define i2cset_full_usage "\n\n" +//usage: "Set I2C registers\n" +//usage: "\n I2CBUS i2c bus number" +//usage: "\n ADDRESS 0x03 - 0x77" +//usage: "\nMODE is:" +//usage: "\n c byte, no value" +//usage: "\n b byte data (default)" +//usage: "\n w word data" +//usage: "\n i I2C block data" +//usage: "\n s SMBus block data" +//usage: "\n Append p for SMBus PEC" +//usage: "\n" +//usage: "\n -f force access" +//usage: "\n -y disable interactive mode" +//usage: "\n -r read back and compare the result" +//usage: "\n -m MASK mask specifying which bits to write" +int i2cset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int i2cset_main(int argc, char **argv) +{ + const unsigned opt_f = (1 << 0), opt_y = (1 << 1), + opt_m = (1 << 2), opt_r = (1 << 3); + const char *const optstr = "fym:r"; + + int bus_num, bus_addr, data_addr, mode = I2C_SMBUS_BYTE, pec = 0; + int val, blen = 0, mask = 0, fd, status; + unsigned char block[I2C_SMBUS_BLOCK_MAX]; + char *opt_m_arg = NULL; + unsigned opts; + + opt_complementary = "-3"; /* from 3 to ? args */ + opts = getopt32(argv, optstr, &opt_m_arg); + argv += optind; + argc -= optind; + + bus_num = i2c_bus_lookup(argv[0]); + bus_addr = i2c_parse_bus_addr(argv[1]); + data_addr = i2c_parse_data_addr(argv[2]); + + if (argv[3]) { + if (!argv[4] && argv[3][0] != 'c') { + mode = I2C_SMBUS_BYTE_DATA; /* Implicit b */ + } else { + switch (argv[argc-1][0]) { + case 'c': /* Already set */ break; + case 'b': mode = I2C_SMBUS_BYTE_DATA; break; + case 'w': mode = I2C_SMBUS_WORD_DATA; break; + case 's': mode = I2C_SMBUS_BLOCK_DATA; break; + case 'i': mode = I2C_SMBUS_I2C_BLOCK_DATA; break; + default: + bb_error_msg("invalid mode"); + bb_show_usage(); + } + + pec = argv[argc-1][1] == 'p'; + if (mode == I2C_SMBUS_BLOCK_DATA || + mode == I2C_SMBUS_I2C_BLOCK_DATA) { + if (pec && mode == I2C_SMBUS_I2C_BLOCK_DATA) + bb_error_msg_and_die( + "PEC not supported for I2C " + "block writes"); + if (opts & opt_m) + bb_error_msg_and_die( + "mask not supported for block " + "writes"); + } + } + } + + /* Prepare the value(s) to be written according to current mode. */ + switch (mode) { + case I2C_SMBUS_BYTE_DATA: + val = xstrtou_range(argv[3], 0, 0, 0xff); + break; + case I2C_SMBUS_WORD_DATA: + val = xstrtou_range(argv[3], 0, 0, 0xffff); + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_DATA: + for (blen = 3; blen < (argc - 1); blen++) + block[blen] = xstrtou_range(argv[blen], 0, 0, 0xff); + val = -1; + break; + default: + val = -1; + break; + } + + if (opts & opt_m) { + mask = xstrtou_range(opt_m_arg, 0, 0, + (mode == I2C_SMBUS_BYTE || + mode == I2C_SMBUS_BYTE_DATA) ? 0xff : 0xffff); + } + + fd = i2c_dev_open(bus_num); + check_write_funcs(fd, mode, pec); + i2c_set_slave_addr(fd, bus_addr, opts & opt_f); + + if (!(opts & opt_y)) + confirm_action(bus_addr, mode, data_addr, pec); + + /* + * If we're using mask - read the current value here and adjust the + * value to be written. + */ + if (opts & opt_m) { + int tmpval; + + switch (mode) { + case I2C_SMBUS_BYTE: + tmpval = i2c_smbus_read_byte(fd); + break; + case I2C_SMBUS_WORD_DATA: + tmpval = i2c_smbus_read_word_data(fd, data_addr); + break; + default: + tmpval = i2c_smbus_read_byte_data(fd, data_addr); + } + + if (tmpval < 0) + bb_perror_msg_and_die("can't read old value"); + + val = (val & mask) | (tmpval & ~mask); + + if (!(opts & opt_y)) { + bb_error_msg("old value 0x%0*x, write mask " + "0x%0*x, will write 0x%0*x to register " + "0x%02x", + mode == I2C_SMBUS_WORD_DATA ? 4 : 2, tmpval, + mode == I2C_SMBUS_WORD_DATA ? 4 : 2, mask, + mode == I2C_SMBUS_WORD_DATA ? 4 : 2, val, + data_addr); + confirm_or_abort(); + } + } + + if (pec) + i2c_set_pec(fd, 1); + + switch (mode) { + case I2C_SMBUS_BYTE: + status = i2c_smbus_write_byte(fd, data_addr); + break; + case I2C_SMBUS_WORD_DATA: + status = i2c_smbus_write_word_data(fd, data_addr, val); + break; + case I2C_SMBUS_BLOCK_DATA: + status = i2c_smbus_write_block_data(fd, data_addr, + blen, block); + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + status = i2c_smbus_write_i2c_block_data(fd, data_addr, + blen, block); + break; + default: /* I2C_SMBUS_BYTE_DATA */ + status = i2c_smbus_write_byte_data(fd, data_addr, val); + break; + } + if (status < 0) + bb_perror_msg_and_die("write failed"); + + if (pec) + i2c_set_pec(fd, 0); /* Clear PEC. */ + + /* No readback required - we're done. */ + if (!(opts & opt_r)) + return 0; + + switch (mode) { + case I2C_SMBUS_BYTE: + status = i2c_smbus_read_byte(fd); + val = data_addr; + break; + case I2C_SMBUS_WORD_DATA: + status = i2c_smbus_read_word_data(fd, data_addr); + break; + default: /* I2C_SMBUS_BYTE_DATA */ + status = i2c_smbus_read_byte_data(fd, data_addr); + } + + if (status < 0) { + printf("Warning - readback failed\n"); + } else + if (status != val) { + printf("Warning - data mismatch - wrote " + "0x%0*x, read back 0x%0*x\n", + mode == I2C_SMBUS_WORD_DATA ? 4 : 2, val, + mode == I2C_SMBUS_WORD_DATA ? 4 : 2, status); + } else { + printf("Value 0x%0*x written, readback matched\n", + mode == I2C_SMBUS_WORD_DATA ? 4 : 2, val); + } + + return 0; +} +#endif /* ENABLE_I2CSET */ + +#if ENABLE_I2CDUMP +//usage:#define i2cdump_trivial_usage +//usage: "[-f] [-r FIRST-LAST] [-y] BUS ADDR [MODE]" +//usage:#define i2cdump_full_usage "\n\n" +//usage: "Examine I2C registers\n" +//usage: "\n I2CBUS i2c bus number" +//usage: "\n ADDRESS 0x03 - 0x77" +//usage: "\nMODE is:" +//usage: "\n b byte (default)" +//usage: "\n w word" +//usage: "\n W word on even register addresses" +//usage: "\n i I2C block" +//usage: "\n s SMBus block" +//usage: "\n c consecutive byte" +//usage: "\n Append p for SMBus PEC" +//usage: "\n" +//usage: "\n -f force access" +//usage: "\n -y disable interactive mode" +//usage: "\n -r limit the number of registers being accessed" +int i2cdump_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int i2cdump_main(int argc UNUSED_PARAM, char **argv) +{ + const unsigned opt_f = (1 << 0), opt_y = (1 << 1), + opt_r = (1 << 2); + const char *const optstr = "fyr:"; + + int bus_num, bus_addr, mode = I2C_SMBUS_BYTE_DATA, even = 0, pec = 0; + unsigned first = 0x00, last = 0xff; + int fd, i, j, res, blen = 0, tmp; + unsigned char cblock[I2C_SMBUS_BLOCK_MAX + I2C_MAX_REGS]; + unsigned char block[I2C_SMBUS_BLOCK_MAX]; + char *opt_r_str, *dash; + unsigned opts; + + opt_complementary = "-2:?3"; /* from 2 to 3 args */ + opts = getopt32(argv, optstr, &opt_r_str); + argv += optind; + + bus_num = i2c_bus_lookup(argv[0]); + bus_addr = i2c_parse_bus_addr(argv[1]); + + if (argv[2]) { + switch (argv[2][0]) { + case 'b': /* Already set */ break; + case 'c': mode = I2C_SMBUS_BYTE; break; + case 'w': mode = I2C_SMBUS_WORD_DATA; break; + case 'W': + mode = I2C_SMBUS_WORD_DATA; + even = 1; + break; + case 's': mode = I2C_SMBUS_BLOCK_DATA; break; + case 'i': mode = I2C_SMBUS_I2C_BLOCK_DATA; break; + default: + bb_error_msg_and_die("invalid mode"); + } + + if (argv[2][1] == 'p') { + if (argv[2][0] == 'W' || argv[2][0] == 'i') { + bb_error_msg_and_die( + "pec not supported for -W and -i"); + } else { + pec = 1; + } + } + } + + if (opts & opt_r) { + first = strtol(opt_r_str, &dash, 0); + if (dash == opt_r_str || *dash != '-' || first > 0xff) + bb_error_msg_and_die("invalid range"); + last = xstrtou_range(++dash, 0, first, 0xff); + + /* Range is not available for every mode */ + switch (mode) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + break; + case I2C_SMBUS_WORD_DATA: + if (!even || (!(first % 2) && last % 2)) + break; + /* Fall through */ + default: + bb_error_msg_and_die( + "range not compatible with selected mode"); + } + } + + fd = i2c_dev_open(bus_num); + check_read_funcs(fd, mode, -1 /* data_addr */, pec); + i2c_set_slave_addr(fd, bus_addr, opts & opt_f); + + if (pec) + i2c_set_pec(fd, 1); + + if (!(opts & opt_y)) + confirm_action(bus_addr, mode, -1 /* data_addr */, pec); + + /* All but word data */ + if (mode != I2C_SMBUS_WORD_DATA || even) { + /* + * FIXME This section has been ported from upstream i2cdump. + * It has been reworked a bit but is still pretty spaghetti + * and needs splitting into several functions. + */ + if (mode == I2C_SMBUS_BLOCK_DATA || + mode == I2C_SMBUS_I2C_BLOCK_DATA) { + res = i2c_smbus_read_block_data(fd, 0, cblock); + blen = res; + } else { + for (res = 0; res < I2C_MAX_REGS; res += tmp) { + tmp = i2c_smbus_read_i2c_block_data( + fd, res, I2C_SMBUS_BLOCK_MAX, + cblock + res); + if (tmp < 0) { + bb_error_msg_and_die( + "block read failed"); + } + } + if (res >= I2C_MAX_REGS) + res = I2C_MAX_REGS; + for (i = 0; i < res; i++) + block[i] = cblock[i]; + if (mode != I2C_SMBUS_BLOCK_DATA) + for (i = res; i < I2C_MAX_REGS; i++) + cblock[i] = -1; + } + + if (mode == I2C_SMBUS_BYTE) { + res = i2c_smbus_write_byte(fd, first); + if (res < 0) + bb_perror_msg_and_die( + "write start address failed"); + } + + printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f" + " 0123456789abcdef\n"); + + for (i = 0; i < I2C_MAX_REGS; i += 0x10) { + if (mode == I2C_SMBUS_BLOCK_DATA && i >= blen) + break; + if (i/16 < first/16) + continue; + if (i/16 > last/16) + break; + + printf("%02x: ", i); + for (j = 0; j < 16; j++) { + fflush_all(); + /* Skip unwanted registers */ + if (i+j < first || i+j > last) { + printf(" "); + if (mode == I2C_SMBUS_WORD_DATA) { + printf(" "); + j++; + } + continue; + } + + switch (mode) { + case I2C_SMBUS_BYTE_DATA: + res = i2c_smbus_read_byte_data(fd, i+j); + block[i+j] = res; + break; + case I2C_SMBUS_WORD_DATA: + res = i2c_smbus_read_word_data(fd, i+j); + if (res < 0) { + block[i+j] = res; + block[i+j+1] = res; + } else { + block[i+j] = res & 0xff; + block[i+j+1] = res >> 8; + } + break; + case I2C_SMBUS_BYTE: + res = i2c_smbus_read_byte(fd); + block[i+j] = res; + break; + default: + res = block[i+j]; + } + + if (mode == I2C_SMBUS_BLOCK_DATA && + i+j >= blen) { + printf(" "); + } else if (res < 0) { + printf("XX "); + if (mode == I2C_SMBUS_WORD_DATA) + printf("XX "); + } else { + printf("%02x ", block[i+j]); + if (mode == I2C_SMBUS_WORD_DATA) + printf("%02x ", block[i+j+1]); + } + + if (mode == I2C_SMBUS_WORD_DATA) + j++; + } + printf(" "); + + for (j = 0; j < 16; j++) { + if (mode == I2C_SMBUS_BLOCK_DATA && i+j >= blen) + break; + /* Skip unwanted registers */ + if (i+j < first || i+j > last) { + printf(" "); + continue; + } + + res = block[i+j]; + if (res < 0) { +//FIXME: impossible, block[] is uchar[] + printf("X"); + } else if (res == 0x00 || res == 0xff) { + printf("."); + } else if (res < 32 || res >= 127) { + printf("?"); + } else { + printf("%c", res); + } + } + printf("\n"); + } + } else { + /* Word data. */ + printf(" 0,8 1,9 2,a 3,b 4,c 5,d 6,e 7,f\n"); + for (i = 0; i < 256; i += 8) { + if (i/8 < first/8) + continue; + if (i/8 > last/8) + break; + + printf("%02x: ", i); + for (j = 0; j < 8; j++) { + /* Skip unwanted registers. */ + if (i+j < first || i+j > last) { + printf(" "); + continue; + } + + res = i2c_smbus_read_word_data(fd, i+j); + if (res < 0) + printf("XXXX "); + else + printf("%04x ", res & 0xffff); + } + printf("\n"); + } + } + + return 0; +} +#endif /* ENABLE_I2CDUMP */ + +#if ENABLE_I2CDETECT +enum adapter_type { + ADT_DUMMY = 0, + ADT_ISA, + ADT_I2C, + ADT_SMBUS, +}; + +struct adap_desc { + const char *funcs; + const char *algo; +}; + +static const struct adap_desc adap_descs[] = { + { .funcs = "dummy", + .algo = "Dummy bus", }, + { .funcs = "isa", + .algo = "ISA bus", }, + { .funcs = "i2c", + .algo = "I2C adapter", }, + { .funcs = "smbus", + .algo = "SMBus adapter", }, +}; + +struct i2c_func +{ + long value; + const char* name; +}; + +static const struct i2c_func i2c_funcs_tab[] = { + { .value = I2C_FUNC_I2C, + .name = "I2C" }, + { .value = I2C_FUNC_SMBUS_QUICK, + .name = "SMBus Quick Command" }, + { .value = I2C_FUNC_SMBUS_WRITE_BYTE, + .name = "SMBus Send Byte" }, + { .value = I2C_FUNC_SMBUS_READ_BYTE, + .name = "SMBus Receive Byte" }, + { .value = I2C_FUNC_SMBUS_WRITE_BYTE_DATA, + .name = "SMBus Write Byte" }, + { .value = I2C_FUNC_SMBUS_READ_BYTE_DATA, + .name = "SMBus Read Byte" }, + { .value = I2C_FUNC_SMBUS_WRITE_WORD_DATA, + .name = "SMBus Write Word" }, + { .value = I2C_FUNC_SMBUS_READ_WORD_DATA, + .name = "SMBus Read Word" }, + { .value = I2C_FUNC_SMBUS_PROC_CALL, + .name = "SMBus Process Call" }, + { .value = I2C_FUNC_SMBUS_WRITE_BLOCK_DATA, + .name = "SMBus Block Write" }, + { .value = I2C_FUNC_SMBUS_READ_BLOCK_DATA, + .name = "SMBus Block Read" }, + { .value = I2C_FUNC_SMBUS_BLOCK_PROC_CALL, + .name = "SMBus Block Process Call" }, + { .value = I2C_FUNC_SMBUS_PEC, + .name = "SMBus PEC" }, + { .value = I2C_FUNC_SMBUS_WRITE_I2C_BLOCK, + .name = "I2C Block Write" }, + { .value = I2C_FUNC_SMBUS_READ_I2C_BLOCK, + .name = "I2C Block Read" }, + { .value = 0, .name = NULL } +}; + +static enum adapter_type i2cdetect_get_funcs(int bus) +{ + enum adapter_type ret; + unsigned long funcs; + int fd; + + fd = i2c_dev_open(bus); + + get_funcs_matrix(fd, &funcs); + if (funcs & I2C_FUNC_I2C) + ret = ADT_I2C; + else if (funcs & (I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + ret = ADT_SMBUS; + else + ret = ADT_DUMMY; + + close(fd); + + return ret; +} + +static void NORETURN list_i2c_busses_and_exit(void) +{ + const char *const i2cdev_path = "/sys/class/i2c-dev"; + + char path[NAME_MAX], name[128]; + struct dirent *de, *subde; + enum adapter_type adt; + DIR *dir, *subdir; + int rv, bus; + char *pos; + FILE *fp; + + /* + * XXX Upstream i2cdetect also looks for i2c bus info in /proc/bus/i2c, + * but we won't bother since it's only useful on older kernels (before + * 2.6.5). We expect sysfs to be present and mounted at /sys/. + */ + + dir = xopendir(i2cdev_path); + while ((de = readdir(dir))) { + if (de->d_name[0] == '.') + continue; + + /* Simple version for ISA chips. */ + snprintf(path, NAME_MAX, "%s/%s/name", + i2cdev_path, de->d_name); + fp = fopen(path, "r"); + if (fp == NULL) { + snprintf(path, NAME_MAX, + "%s/%s/device/name", + i2cdev_path, de->d_name); + fp = fopen(path, "r"); + } + + /* Non-ISA chips require the hard-way. */ + if (fp == NULL) { + snprintf(path, NAME_MAX, + "%s/%s/device/name", + i2cdev_path, de->d_name); + subdir = opendir(path); + if (subdir == NULL) + continue; + + while ((subde = readdir(subdir))) { + if (subde->d_name[0] == '.') + continue; + + if (strncmp(subde->d_name, "i2c-", 4) == 0) { + snprintf(path, NAME_MAX, + "%s/%s/device/%s/name", + i2cdev_path, de->d_name, + subde->d_name); + fp = fopen(path, "r"); + goto found; + } + } + } + +found: + if (fp != NULL) { + /* + * Get the rest of the info and display a line + * for a single bus. + */ + memset(name, 0, sizeof(name)); + pos = fgets(name, sizeof(name), fp); + fclose(fp); + if (pos == NULL) + continue; + + pos = strchr(name, '\n'); + if (pos != NULL) + *pos = '\0'; + + rv = sscanf(de->d_name, "i2c-%d", &bus); + if (rv != 1) + continue; + + if (strncmp(name, "ISA", 3) == 0) + adt = ADT_ISA; + else + adt = i2cdetect_get_funcs(bus); + + printf( + "i2c-%d\t%-10s\t%-32s\t%s\n", + bus, adap_descs[adt].funcs, + name, adap_descs[adt].algo); + } + } + + exit(EXIT_SUCCESS); +} + +static void NORETURN no_support(const char *cmd) +{ + bb_error_msg_and_die("bus doesn't support %s", cmd); +} + +static void will_skip(const char *cmd) +{ + bb_error_msg( + "warning: can't use %s command, " + "will skip some addresses", cmd); +} + +//usage:#define i2cdetect_trivial_usage +//usage: "[-F I2CBUS] [-l] [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]" +//usage:#define i2cdetect_full_usage "\n\n" +//usage: "Detect I2C chips.\n" +//usage: "\n I2CBUS i2c bus number" +//usage: "\n FIRST and LAST limit the probing range" +//usage: "\n" +//usage: "\n -l output list of installed busses" +//usage: "\n -y disable interactive mode" +//usage: "\n -a force scanning of non-regular addresses" +//usage: "\n -q use smbus quick write commands for probing (default)" +//usage: "\n -r use smbus read byte commands for probing" +//usage: "\n -F display list of functionalities" +int i2cdetect_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int i2cdetect_main(int argc UNUSED_PARAM, char **argv) +{ + const unsigned opt_y = (1 << 0), opt_a = (1 << 1), + opt_q = (1 << 2), opt_r = (1 << 3), + opt_F = (1 << 4), opt_l = (1 << 5); + const char *const optstr = "yaqrFl"; + + int fd, bus_num, i, j, mode = DETECT_MODE_AUTO; + int status; + unsigned first = 0x00, last = 0x77; + unsigned long funcs; + unsigned opts; + + opt_complementary = "q--r:r--q:" /* mutually exclusive */ + "-1:?3"; /* from 1 to 3 args */ + opts = getopt32(argv, optstr); + argv += optind; + + if (opts & opt_l) + list_i2c_busses_and_exit(); + + bus_num = i2c_bus_lookup(argv[0]); + fd = i2c_dev_open(bus_num); + get_funcs_matrix(fd, &funcs); + + if (opts & opt_F) { + /* Only list the functionalities. */ + printf("Functionalities implemented by bus #%d\n", bus_num); + for (i = 0; i2c_funcs_tab[i].value; i++) { + printf("%-32s %s\n", i2c_funcs_tab[i].name, + funcs & i2c_funcs_tab[i].value ? "yes" : "no"); + } + + return EXIT_SUCCESS; + } + + if (opts & opt_r) + mode = DETECT_MODE_READ; + else if (opts & opt_q) + mode = DETECT_MODE_QUICK; + + if (opts & opt_a) + last = 0x7f; + + /* Read address range. */ + if (argv[1]) { + first = xstrtou_range(argv[1], 16, first, last); + if (argv[2]) + last = xstrtou_range(argv[2], 16, first, last); + } + + if (!(funcs & (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_READ_BYTE))) { + no_support("detection commands"); + } else + if (mode == DETECT_MODE_QUICK && !(funcs & I2C_FUNC_SMBUS_QUICK)) { + no_support("SMBus Quick Write command"); + } else + if (mode == DETECT_MODE_READ && !(funcs & I2C_FUNC_SMBUS_READ_BYTE)) { + no_support("SMBus Receive Byte command"); + } else { + if (!(funcs & I2C_FUNC_SMBUS_QUICK)) + will_skip("SMBus Quick Write"); + if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE)) + will_skip("SMBus Receive Byte"); + } + + if (!(opts & opt_y)) + confirm_action(-1, -1, -1, 0); + + printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\n"); + for (i = 0; i < 128; i += 16) { + printf("%02x: ", i); + for(j = 0; j < 16; j++) { + fflush_all(); + + if (mode == DETECT_MODE_AUTO) { + if ((i+j >= 0x30 && i+j <= 0x37) || + (i+j >= 0x50 && i+j <= 0x5F)) + mode = DETECT_MODE_READ; + else + mode = DETECT_MODE_QUICK; + } + + /* Skip unwanted addresses. */ + if (i+j < first + || i+j > last + || (mode == DETECT_MODE_READ && !(funcs & I2C_FUNC_SMBUS_READ_BYTE)) + || (mode == DETECT_MODE_QUICK && !(funcs & I2C_FUNC_SMBUS_QUICK))) + { + printf(" "); + continue; + } + + i2c_set_slave_addr(fd, i + j, 0); + + switch (mode) { + case DETECT_MODE_READ: + /* + * This is known to lock SMBus on various + * write-only chips (mainly clock chips). + */ + status = i2c_smbus_read_byte(fd); + break; + default: /* DETECT_MODE_QUICK: */ + /* + * This is known to corrupt the Atmel + * AT24RF08 EEPROM. + */ + status = i2c_smbus_write_quick(fd, + I2C_SMBUS_WRITE); + break; + } + + if (status < 0) + printf("-- "); + else + printf("%02x ", i+j); + } + printf("\n"); + } + + return 0; +} +#endif /* ENABLE_I2CDETECT */ -- cgit v1.2.3-55-g6feb From 1fd844267c3ad60e9122e141768e823e6ad74a28 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 12 Feb 2015 16:18:39 +0100 Subject: modprobe-small: if concurrent module load did not succeed, wait usecase: two sd cards are being mounted in parallel at same time on dual core. example modules which are getting loaded is nls_cp437. While one module is being loaded , it makes state in /proc/modules as 'coming' and then starts doing its module init function (in our case - registering nls). meanwhile on other core, if modprobe returns that is has already been loaded, then it will continue and search for the nls list which is not yet finished from first module init. This fails resulting in not mounting sd card. function old new delta process_module 667 746 +79 Signed-off-by: Denys Vlasenko --- modutils/modprobe-small.c | 50 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c index 6b0a4400c..598965968 100644 --- a/modutils/modprobe-small.c +++ b/modutils/modprobe-small.c @@ -536,17 +536,49 @@ static module_info** find_alias(const char *alias) // TODO: open only once, invent config_rewind() static int already_loaded(const char *name) { - int ret = 0; - char *s; - parser_t *parser = config_open2("/proc/modules", xfopen_for_read); - while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) { - if (strcmp(s, name) == 0) { - ret = 1; - break; + int ret, namelen; + char *line; + FILE *fp; + + ret = 5 * 2; + again: + fp = fopen_for_read("/proc/modules"); + if (!fp) + return 0; + namelen = strlen(name); + while ((line = xmalloc_fgetline(fp)) != NULL) { + char *live; + + // Examples from kernel 3.14.6: + //pcspkr 12718 0 - Live 0xffffffffa017e000 + //snd_timer 28690 2 snd_seq,snd_pcm, Live 0xffffffffa025e000 + //i915 801405 2 - Live 0xffffffffa0096000 + if (strncmp(line, name, namelen) != 0 || line[namelen] != ' ') { + free(line); + continue; } + live = strstr(line, " Live"); + free(line); + if (!live) { + /* State can be Unloading, Loading, or Live. + * modprobe must not return prematurely if we see "Loading": + * it can cause further programs to assume load completed, + * but it did not (yet)! + * Wait up to 5*20 ms for it to resolve. + */ + ret -= 2; + if (ret == 0) + break; /* huh? report as "not loaded" */ + fclose(fp); + usleep(20*1000); + goto again; + } + ret = 1; + break; } - config_close(parser); - return ret; + fclose(fp); + + return ret | 1; } #else #define already_loaded(name) 0 -- cgit v1.2.3-55-g6feb From 402afe1cc69ea505c9bf82ffe06e51ffecf694df Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 13 Feb 2015 11:53:33 +0100 Subject: modprobe-small: fix thinko in previous commit Signed-off-by: Denys Vlasenko --- modutils/modprobe-small.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c index 598965968..ed177bb9b 100644 --- a/modutils/modprobe-small.c +++ b/modutils/modprobe-small.c @@ -578,7 +578,7 @@ static int already_loaded(const char *name) } fclose(fp); - return ret | 1; + return ret & 1; } #else #define already_loaded(name) 0 -- cgit v1.2.3-55-g6feb From 8e92df15b512d108901ce1762e917518553a8aaf Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 16 Feb 2015 15:36:25 +0100 Subject: ls: fix year 9999 bug :) Signed-off-by: Denys Vlasenko --- coreutils/ls.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/coreutils/ls.c b/coreutils/ls.c index 1b63be56d..a30422a8c 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c @@ -581,12 +581,15 @@ static NOINLINE unsigned display_single(const struct dnode *dn) } else { /* LIST_DATE_TIME */ /* G.current_time_t ~== time(NULL) */ time_t age = G.current_time_t - ttime; - printf("%.6s ", filetime + 4); /* "Jun 30" */ if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { - /* hh:mm if less than 6 months old */ - printf("%.5s ", filetime + 11); - } else { /* year. buggy if year > 9999 ;) */ - printf(" %.4s ", filetime + 20); + /* less than 6 months old */ + /* "mmm dd hh:mm " */ + printf("%.12s ", filetime + 4); + } else { + /* "mmm dd yyyy " */ + /* "mmm dd yyyyy " after year 9999 :) */ + strchr(filetime + 20, '\n')[0] = ' '; + printf("%.7s%6s", filetime + 4, filetime + 20); } column += 13; } -- cgit v1.2.3-55-g6feb From 70e30e8eec7f202cb584449e4c7a9176d57d980d Mon Sep 17 00:00:00 2001 From: Bernhard Reutner-Fischer Date: Mon, 16 Feb 2015 17:12:04 +0100 Subject: dc: tweak parsing https://bugs.debian.org/538685 dc requires whitespace between language elements. We were requiring 1 2 + p instead of the abbreviated 1 2+p (for example). function old new delta stack_machine 97 126 +29 dc_main 117 79 -38 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 29/-38) Total: -9 bytes Signed-off-by: Bernhard Reutner-Fischer Signed-off-by: Denys Vlasenko --- miscutils/dc.c | 57 ++++++++++++++++++++++++++---------------------------- testsuite/dc.tests | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 30 deletions(-) create mode 100755 testsuite/dc.tests diff --git a/miscutils/dc.c b/miscutils/dc.c index 6bcfbe249..a7bd360d2 100644 --- a/miscutils/dc.c +++ b/miscutils/dc.c @@ -196,14 +196,6 @@ struct op { }; static const struct op operators[] = { - {"+", add}, - {"add", add}, - {"-", sub}, - {"sub", sub}, - {"*", mul}, - {"mul", mul}, - {"/", divide}, - {"div", divide}, #if ENABLE_FEATURE_DC_LIBM {"**", power}, {"exp", power}, @@ -216,28 +208,47 @@ static const struct op operators[] = { {"not", not}, {"eor", eor}, {"xor", eor}, + {"+", add}, + {"add", add}, + {"-", sub}, + {"sub", sub}, + {"*", mul}, + {"mul", mul}, + {"/", divide}, + {"div", divide}, {"p", print_no_pop}, {"f", print_stack_no_pop}, {"o", set_output_base}, }; +/* Feed the stack machine */ static void stack_machine(const char *argument) { char *end; - double d; + double number; const struct op *o; - d = strtod(argument, &end); - if (end != argument && *end == '\0') { - push(d); - return; + next: + number = strtod(argument, &end); + if (end != argument) { + argument = end; + push(number); + goto next; } + /* We might have matched a digit, eventually advance the argument */ + argument = skip_whitespace(argument); + + if (*argument == '\0') + return; + o = operators; do { - if (strcmp(o->name, argument) == 0) { + const size_t name_len = strlen(o->name); + if (strncmp(o->name, argument, name_len) == 0) { + argument += name_len; o->function(); - return; + goto next; } o++; } while (o != operators + ARRAY_SIZE(operators)); @@ -254,25 +265,11 @@ int dc_main(int argc UNUSED_PARAM, char **argv) if (!argv[0]) { /* take stuff from stdin if no args are given */ char *line; - char *cursor; - char *token; while ((line = xmalloc_fgetline(stdin)) != NULL) { - cursor = line; - while (1) { - token = skip_whitespace(cursor); - if (*token == '\0') - break; - cursor = skip_non_whitespace(token); - if (*cursor != '\0') - *cursor++ = '\0'; - stack_machine(token); - } + stack_machine(line); free(line); } } else { - // why? it breaks "dc -2 2 + p" - //if (argv[0][0] == '-') - // bb_show_usage(); do { stack_machine(*argv); } while (*++argv); diff --git a/testsuite/dc.tests b/testsuite/dc.tests new file mode 100755 index 000000000..a5da5372d --- /dev/null +++ b/testsuite/dc.tests @@ -0,0 +1,56 @@ +#!/bin/sh +# Copyright 2015 by Bernhard Reutner-Fischer +# Licensed under GPLv2 or later, see file LICENSE in this source tree. + +. ./testing.sh + +# testing "test name" "command" "expected result" "file input" "stdin" + +testing "dc basic syntax (stdin, multiple args)" \ + "dc" \ + "30\n" \ + "" "10 20+p" + +testing "dc basic syntax (argv, single arg)" \ + "dc '10 20+p'" \ + "30\n" \ + "" "" + +testing "dc basic syntax (argv, multiple args)" \ + "dc 10 20+p" \ + "30\n" \ + "" "" + +testing "dc complex with spaces (single arg)" \ + "dc '8 8 * 2 2 + / p'" \ + "16\n" \ + "" "" + +testing "dc complex without spaces (single arg)" \ + "dc '8 8*2 2+/p'" \ + "16\n" \ + "" "" + +testing "dc complex with spaces (multiple args)" \ + "dc 8 8 \* 2 2 + / p" \ + "16\n" \ + "" "" + +testing "dc complex without spaces (multiple args)" \ + "dc 8 8\*2 2+/p" \ + "16\n" \ + "" "" + +exit $FAILCOUNT + +# we do not support arguments +testing "dc -e " \ + "dc -e '10 2+f'" \ + "12\n" \ + "" "" + +testing "dc -f " \ + "dc -f input" \ + "12\n" \ + "10 2+f" "" + -- cgit v1.2.3-55-g6feb From 0cb981c5e2f1a955e72dd9d3d2fad075dc9d0da6 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 18 Feb 2015 11:29:07 +0100 Subject: ls: trivial code shrink function old new delta display_single 914 893 -21 Signed-off-by: Denys Vlasenko --- coreutils/ls.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/coreutils/ls.c b/coreutils/ls.c index a30422a8c..366758351 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c @@ -566,12 +566,12 @@ static NOINLINE unsigned display_single(const struct dnode *dn) #if ENABLE_FEATURE_LS_TIMESTAMPS if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) { char *filetime; - time_t ttime = dn->dn_mtime; + time_t *ttime = &dn->dn_mtime; if (G.all_fmt & TIME_ACCESS) - ttime = dn->dn_atime; + ttime = &dn->dn_atime; if (G.all_fmt & TIME_CHANGE) - ttime = dn->dn_ctime; - filetime = ctime(&ttime); + ttime = &dn->dn_ctime; + filetime = ctime(ttime); /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */ if (G.all_fmt & LIST_FULLTIME) { /* -e */ /* Note: coreutils 8.4 ls --full-time prints: @@ -580,7 +580,7 @@ static NOINLINE unsigned display_single(const struct dnode *dn) column += printf("%.24s ", filetime); } else { /* LIST_DATE_TIME */ /* G.current_time_t ~== time(NULL) */ - time_t age = G.current_time_t - ttime; + time_t age = G.current_time_t - *ttime; if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { /* less than 6 months old */ /* "mmm dd hh:mm " */ -- cgit v1.2.3-55-g6feb From 54e9585e106348d6f0129376171fab305ddc0e27 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 18 Feb 2015 13:47:27 +0100 Subject: trivial code shrink function old new delta rdate_main 246 251 +5 show_entry 291 287 -4 daytime_stream 44 39 -5 packed_usage 30176 30168 -8 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/3 up/down: 5/-17) Total: -12 bytes text data bss dec hex filename 929453 932 17684 948069 e7765 busybox_old 929411 932 17684 948027 e773b busybox_unstripped Signed-off-by: Denys Vlasenko --- miscutils/last_fancy.c | 5 +++-- networking/inetd.c | 2 +- util-linux/rdate.c | 8 ++++---- util-linux/rtcwake.c | 12 ++++++------ 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/miscutils/last_fancy.c b/miscutils/last_fancy.c index f687d7e16..16ed9e920 100644 --- a/miscutils/last_fancy.c +++ b/miscutils/last_fancy.c @@ -42,7 +42,7 @@ enum { static void show_entry(struct utmp *ut, int state, time_t dur_secs) { unsigned days, hours, mins; - char duration[32]; + char duration[sizeof("(%u+02:02)") + sizeof(int)*3]; char login_time[17]; char logout_time[8]; const char *logout_str; @@ -53,7 +53,8 @@ static void show_entry(struct utmp *ut, int state, time_t dur_secs) * but some systems have it wrong */ tmp = ut->ut_tv.tv_sec; safe_strncpy(login_time, ctime(&tmp), 17); - snprintf(logout_time, 8, "- %s", ctime(&dur_secs) + 11); + tmp = dur_secs; + snprintf(logout_time, 8, "- %s", ctime(&tmp) + 11); dur_secs = MAX(dur_secs - (time_t)ut->ut_tv.tv_sec, (time_t)0); /* unsigned int is easier to divide than time_t (which may be signed long) */ diff --git a/networking/inetd.c b/networking/inetd.c index 584c5e5e4..b3f2e9fbc 100644 --- a/networking/inetd.c +++ b/networking/inetd.c @@ -1654,7 +1654,7 @@ static void FAST_FUNC daytime_stream(int s, servtab_t *sep UNUSED_PARAM) { time_t t; - t = time(NULL); + time(&t); fdprintf(s, "%.24s\r\n", ctime(&t)); } static void FAST_FUNC daytime_dg(int s, servtab_t *sep) diff --git a/util-linux/rdate.c b/util-linux/rdate.c index 6e35cd519..8075ef6af 100644 --- a/util-linux/rdate.c +++ b/util-linux/rdate.c @@ -11,9 +11,9 @@ //usage:#define rdate_trivial_usage //usage: "[-sp] HOST" //usage:#define rdate_full_usage "\n\n" -//usage: "Get and possibly set the system date/time from a remote HOST\n" -//usage: "\n -s Set the system date/time (default)" -//usage: "\n -p Print the date/time" +//usage: "Get and possibly set system time from a remote HOST\n" +//usage: "\n -s Set system time (default)" +//usage: "\n -p Print time" #include "libbb.h" @@ -36,7 +36,7 @@ static time_t askremotedate(const char *host) fd = create_and_connect_stream_or_die(host, bb_lookup_port("time", "tcp", 37)); if (safe_read(fd, &nett, 4) != 4) /* read time from server */ - bb_error_msg_and_die("%s did not send the complete time", host); + bb_error_msg_and_die("%s: %s", host, "short read"); if (ENABLE_FEATURE_CLEAN_UP) close(fd); diff --git a/util-linux/rtcwake.c b/util-linux/rtcwake.c index 33cdbfad4..53d9384db 100644 --- a/util-linux/rtcwake.c +++ b/util-linux/rtcwake.c @@ -32,18 +32,18 @@ //usage: "\n -l,--local Clock is set to local time" //usage: "\n -u,--utc Clock is set to UTC time" //usage: "\n -d,--device=DEV Specify the RTC device" -//usage: "\n -m,--mode=MODE Set the sleep state (default: standby)" -//usage: "\n -s,--seconds=SEC Set the timeout in SEC seconds from now" -//usage: "\n -t,--time=TIME Set the timeout to TIME seconds from epoch" +//usage: "\n -m,--mode=MODE Set sleep state (default: standby)" +//usage: "\n -s,--seconds=SEC Set timeout in SEC seconds from now" +//usage: "\n -t,--time=TIME Set timeout to TIME seconds from epoch" //usage: ) //usage: IF_NOT_LONG_OPTS( //usage: "\n -a Read clock mode from adjtime" //usage: "\n -l Clock is set to local time" //usage: "\n -u Clock is set to UTC time" //usage: "\n -d DEV Specify the RTC device" -//usage: "\n -m MODE Set the sleep state (default: standby)" -//usage: "\n -s SEC Set the timeout in SEC seconds from now" -//usage: "\n -t TIME Set the timeout to TIME seconds from epoch" +//usage: "\n -m MODE Set sleep state (default: standby)" +//usage: "\n -s SEC Set timeout in SEC seconds from now" +//usage: "\n -t TIME Set timeout to TIME seconds from epoch" //usage: ) #include "libbb.h" -- cgit v1.2.3-55-g6feb From d7bfee1edb551d3503c7b616702a73192ba069a9 Mon Sep 17 00:00:00 2001 From: Bernhard Reutner-Fischer Date: Wed, 18 Feb 2015 20:41:02 +0100 Subject: wget: fix --header to not append duplicate headers function old new delta wget_main 2551 2715 +164 wget_user_headers - 62 +62 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 1/0 up/down: 226/0) Total: 226 bytes Signed-off-by: Bernhard Reutner-Fischer Signed-off-by: Denys Vlasenko --- networking/wget.c | 98 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 20 deletions(-) diff --git a/networking/wget.c b/networking/wget.c index 1013f66cb..6c8bd90a8 100644 --- a/networking/wget.c +++ b/networking/wget.c @@ -38,8 +38,14 @@ #if 0 # define log_io(...) bb_error_msg(__VA_ARGS__) +# define SENDFMT(fp, fmt, ...) \ + do { \ + log_io("> " fmt, ##__VA_ARGS__); \ + fprintf(fp, fmt, ##__VA_ARGS__); \ + } while (0); #else # define log_io(...) ((void)0) +# define SENDFMT(fp, fmt, ...) fprintf(fp, fmt, ##__VA_ARGS__) #endif @@ -55,6 +61,36 @@ static const char P_FTP[] = "ftp"; static const char P_HTTP[] = "http"; static const char P_HTTPS[] = "https"; +#if ENABLE_FEATURE_WGET_LONG_OPTIONS +/* User-specified headers prevent using our corresponding built-in headers. */ +enum { + HDR_HOST = (1<<0), + HDR_USER_AGENT = (1<<1), + HDR_RANGE = (1<<2), + HDR_AUTH = (1<<3) * ENABLE_FEATURE_WGET_AUTHENTICATION, + HDR_PROXY_AUTH = (1<<4) * ENABLE_FEATURE_WGET_AUTHENTICATION, +}; +static const char wget_user_headers[] ALIGN1 = + "Host:\0" + "User-Agent:\0" + "Range:\0" +# if ENABLE_FEATURE_WGET_AUTHENTICATION + "Authorization:\0" + "Proxy-Authorization:\0" +# endif + ; +# define USR_HEADER_HOST (G.user_headers & HDR_HOST) +# define USR_HEADER_USER_AGENT (G.user_headers & HDR_USER_AGENT) +# define USR_HEADER_RANGE (G.user_headers & HDR_RANGE) +# define USR_HEADER_AUTH (G.user_headers & HDR_AUTH) +# define USR_HEADER_PROXY_AUTH (G.user_headers & HDR_PROXY_AUTH) +#else /* No long options, no user-headers :( */ +# define USR_HEADER_HOST 0 +# define USR_HEADER_USER_AGENT 0 +# define USR_HEADER_RANGE 0 +# define USR_HEADER_AUTH 0 +# define USR_HEADER_PROXY_AUTH 0 +#endif /* Globals */ struct globals { @@ -69,6 +105,7 @@ struct globals { #if ENABLE_FEATURE_WGET_LONG_OPTIONS char *post_data; char *extra_headers; + unsigned char user_headers; /* Headers mentioned by the user */ #endif char *fname_out; /* where to direct output (-O) */ const char *proxy_flag; /* Use proxies if env vars are set */ @@ -830,43 +867,46 @@ static void download_one_url(const char *url) #endif /* Send HTTP request */ if (use_proxy) { - fprintf(sfp, "GET %s://%s/%s HTTP/1.1\r\n", + SENDFMT(sfp, "GET %s://%s/%s HTTP/1.1\r\n", target.protocol, target.host, target.path); } else { - fprintf(sfp, "%s /%s HTTP/1.1\r\n", + SENDFMT(sfp, "%s /%s HTTP/1.1\r\n", (option_mask32 & WGET_OPT_POST_DATA) ? "POST" : "GET", target.path); } - - fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n", - target.host, G.user_agent); + if (!USR_HEADER_HOST) + SENDFMT(sfp, "Host: %s\r\n", target.host); + if (!USR_HEADER_USER_AGENT) + SENDFMT(sfp, "User-Agent: %s\r\n", G.user_agent); /* Ask server to close the connection as soon as we are done * (IOW: we do not intend to send more requests) */ - fprintf(sfp, "Connection: close\r\n"); + SENDFMT(sfp, "Connection: close\r\n"); #if ENABLE_FEATURE_WGET_AUTHENTICATION - if (target.user) { - fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6, + if (target.user && !USR_HEADER_AUTH) { + SENDFMT(sfp, "Proxy-Authorization: Basic %s\r\n"+6, base64enc(target.user)); } - if (use_proxy && server.user) { - fprintf(sfp, "Proxy-Authorization: Basic %s\r\n", + if (use_proxy && server.user && !USR_HEADER_PROXY_AUTH) { + SENDFMT(sfp, "Proxy-Authorization: Basic %s\r\n", base64enc(server.user)); } #endif - if (G.beg_range != 0) - fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range); + if (G.beg_range != 0 && !USR_HEADER_RANGE) + SENDFMT(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range); #if ENABLE_FEATURE_WGET_LONG_OPTIONS - if (G.extra_headers) + if (G.extra_headers) { + log_io(G.extra_headers); fputs(G.extra_headers, sfp); + } if (option_mask32 & WGET_OPT_POST_DATA) { - fprintf(sfp, + SENDFMT(sfp, "Content-Type: application/x-www-form-urlencoded\r\n" "Content-Length: %u\r\n" "\r\n" @@ -876,7 +916,7 @@ static void download_one_url(const char *url) } else #endif { - fprintf(sfp, "\r\n"); + SENDFMT(sfp, "\r\n"); } fflush(sfp); @@ -1093,7 +1133,9 @@ int wget_main(int argc UNUSED_PARAM, char **argv) #if ENABLE_FEATURE_WGET_LONG_OPTIONS applet_long_options = wget_longopts; #endif - opt_complementary = "-1" IF_FEATURE_WGET_TIMEOUT(":T+") IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::"); + opt_complementary = "-1" + IF_FEATURE_WGET_TIMEOUT(":T+") + IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::"); getopt32(argv, "csqO:P:Y:U:T:" /*ignored:*/ "t:", &G.fname_out, &G.dir_prefix, &G.proxy_flag, &G.user_agent, @@ -1106,16 +1148,32 @@ int wget_main(int argc UNUSED_PARAM, char **argv) #if ENABLE_FEATURE_WGET_LONG_OPTIONS if (headers_llist) { - int size = 1; - char *cp; + int size = 0; + char *hdr; llist_t *ll = headers_llist; while (ll) { size += strlen(ll->data) + 2; ll = ll->link; } - G.extra_headers = cp = xmalloc(size); + G.extra_headers = hdr = xmalloc(size + 1); while (headers_llist) { - cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist)); + int bit; + const char *words; + + size = sprintf(hdr, "%s\r\n", + (char*)llist_pop(&headers_llist)); + /* a bit like index_in_substrings but don't match full key */ + bit = 1; + words = wget_user_headers; + while (*words) { + if (strstr(hdr, words) == hdr) { + G.user_headers |= bit; + break; + } + bit <<= 1; + words += strlen(words) + 1; + } + hdr += size; } } #endif -- cgit v1.2.3-55-g6feb From cb6a112f09c3b5dfe94b5c365ca50df4dcc05520 Mon Sep 17 00:00:00 2001 From: Tito Ragusa Date: Thu, 19 Feb 2015 22:02:59 +0100 Subject: libpwdgrp: fix thinko in getpwnam function old new delta parse_file - 64 +64 getXXnam_r 162 138 -24 getXXnam 90 50 -40 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 0/2 up/down: 64/-64) Total: 0 bytes Signed-off-by: Tito Ragusa Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 7ec704ee4..3886facf0 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -450,15 +450,7 @@ static void* FAST_FUNC getXXnam(const char *name, unsigned db_and_field_pos) char *buf; struct passdb *db = &get_S()->db[db_and_field_pos >> 2]; - if (!db->fp) { - db->fp = fopen_for_read(db->filename); - if (!db->fp) { - return NULL; - } - close_on_exec_on(fileno(db->fp)); - } - - buf = parse_common(db->fp, db, name, db_and_field_pos & 3); + buf = parse_file(db, name, db_and_field_pos & 3); return massage_data_for_non_r_func(db, buf); } -- cgit v1.2.3-55-g6feb From a88db5c1a99ebc0ae23b5d108113d9b8af7afc3c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 21 Feb 2015 17:08:35 +0100 Subject: modinfo: fix fallout from basename removal Signed-off-by: Denys Vlasenko --- modutils/modinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modutils/modinfo.c b/modutils/modinfo.c index 0ab942890..ee379304c 100644 --- a/modutils/modinfo.c +++ b/modutils/modinfo.c @@ -154,7 +154,7 @@ int modinfo_main(int argc UNUSED_PARAM, char **argv) if (colon == NULL) continue; *colon = '\0'; - filename2modname(tokens[0], name); + filename2modname(bb_basename(tokens[0]), name); for (i = 0; argv[i]; i++) { if (fnmatch(argv[i], name, 0) == 0) { modinfo(tokens[0], uts.release, &env); -- cgit v1.2.3-55-g6feb From c5beaa08eb57c542768eebfdf6532e72c5c0c16b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 23 Feb 2015 15:25:58 +0100 Subject: ls: heed compiler warning Signed-off-by: Denys Vlasenko --- coreutils/ls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreutils/ls.c b/coreutils/ls.c index 366758351..14c8beaff 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c @@ -566,7 +566,7 @@ static NOINLINE unsigned display_single(const struct dnode *dn) #if ENABLE_FEATURE_LS_TIMESTAMPS if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) { char *filetime; - time_t *ttime = &dn->dn_mtime; + const time_t *ttime = &dn->dn_mtime; if (G.all_fmt & TIME_ACCESS) ttime = &dn->dn_atime; if (G.all_fmt & TIME_CHANGE) -- cgit v1.2.3-55-g6feb From 1eafd4494706c21a87dc66e4e0afa8799b576cb5 Mon Sep 17 00:00:00 2001 From: Serj Kalichev Date: Mon, 23 Feb 2015 15:26:47 +0100 Subject: acpid: fix logging Without this patch acpid can't log the events at all. Moreover it tries to truncate log file every time. Signed-off-by: Serj Kalichev Signed-off-by: Denys Vlasenko --- util-linux/acpid.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/util-linux/acpid.c b/util-linux/acpid.c index 38421c2d7..fc8151f6a 100644 --- a/util-linux/acpid.c +++ b/util-linux/acpid.c @@ -121,10 +121,8 @@ static void process_event(const char *event) char *handler = xasprintf("./%s", event); const char *args[] = { "run-parts", handler, NULL }; - // debug info - if (option_mask32 & OPT_d) { - bb_error_msg("%s", event); - } + // log the event + bb_error_msg("%s", event); // spawn handler // N.B. run-parts would require scripts to have #!/bin/sh @@ -256,7 +254,7 @@ int acpid_main(int argc UNUSED_PARAM, char **argv) /* No -d "Debug", we log to log file. * This includes any output from children. */ - xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO); + xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_APPEND), STDOUT_FILENO); xdup2(STDOUT_FILENO, STDERR_FILENO); /* Also, acpid's messages (but not children) will go to syslog too */ openlog(applet_name, LOG_PID, LOG_DAEMON); -- cgit v1.2.3-55-g6feb From fa535f3e485456a7fd85db060532ea6539670af0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 2 Mar 2015 17:37:31 +0100 Subject: runsvdir: (almost) close a signal race We could lose a signal while processing previous one function old new delta runsvdir_main 1088 1077 -11 Signed-off-by: Denys Vlasenko --- runit/runsvdir.c | 53 ++++++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/runit/runsvdir.c b/runit/runsvdir.c index af7e75ba7..809c48a51 100644 --- a/runit/runsvdir.c +++ b/runit/runsvdir.c @@ -59,7 +59,6 @@ struct globals { int svnum; #if ENABLE_FEATURE_RUNSVDIR_LOG char *rplog; - int rploglen; struct fd_pair logpipe; struct pollfd pfd[1]; unsigned stamplog; @@ -70,7 +69,6 @@ struct globals { #define svdir (G.svdir ) #define svnum (G.svnum ) #define rplog (G.rplog ) -#define rploglen (G.rploglen ) #define logpipe (G.logpipe ) #define pfd (G.pfd ) #define stamplog (G.stamplog ) @@ -219,15 +217,11 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv) struct stat s; dev_t last_dev = last_dev; /* for gcc */ ino_t last_ino = last_ino; /* for gcc */ - time_t last_mtime = 0; - int wstat; + time_t last_mtime; int curdir; - pid_t pid; - unsigned deadline; - unsigned now; unsigned stampcheck; int i; - int need_rescan = 1; + int need_rescan; char *opt_s_argv[3]; INIT_G(); @@ -257,8 +251,7 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv) /* setup log */ if (*argv) { rplog = *argv; - rploglen = strlen(rplog); - if (rploglen < 7) { + if (strlen(rplog) < 7) { warnx("log must have at least seven characters"); } else if (piped_pair(logpipe)) { warnx("can't create pipe for log"); @@ -287,11 +280,16 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv) close_on_exec_on(curdir); stampcheck = monotonic_sec(); + need_rescan = 1; + last_mtime = 0; for (;;) { + unsigned now; + unsigned sig; + /* collect children */ for (;;) { - pid = wait_any_nohang(&wstat); + pid_t pid = wait_any_nohang(NULL); if (pid <= 0) break; for (i = 0; i < svnum; i++) { @@ -345,15 +343,17 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv) } pfd[0].revents = 0; #endif - deadline = (need_rescan ? 1 : 5); - sig_block(SIGCHLD); + { + unsigned deadline = (need_rescan ? 1 : 5); + sig_block(SIGCHLD); #if ENABLE_FEATURE_RUNSVDIR_LOG - if (rplog) - poll(pfd, 1, deadline*1000); - else + if (rplog) + poll(pfd, 1, deadline*1000); + else #endif - sleep(deadline); - sig_unblock(SIGCHLD); + sleep(deadline); + sig_unblock(SIGCHLD); + } #if ENABLE_FEATURE_RUNSVDIR_LOG if (pfd[0].revents & POLLIN) { @@ -361,21 +361,25 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv) while (read(logpipe.rd, &ch, 1) > 0) { if (ch < ' ') ch = ' '; - for (i = 6; i < rploglen; i++) + for (i = 6; rplog[i] != '\0'; i++) rplog[i-1] = rplog[i]; - rplog[rploglen-1] = ch; + rplog[i-1] = ch; } } #endif - if (!bb_got_signal) + sig = bb_got_signal; + if (!sig) continue; + bb_got_signal = 0; /* -s SCRIPT: useful if we are init. * In this case typically script never returns, * it halts/powers off/reboots the system. */ if (opt_s_argv[0]) { + pid_t pid; + /* Single parameter: signal# */ - opt_s_argv[1] = utoa(bb_got_signal); + opt_s_argv[1] = utoa(sig); pid = spawn(opt_s_argv); if (pid > 0) { /* Remembering to wait for _any_ children, @@ -385,7 +389,7 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv) } } - if (bb_got_signal == SIGHUP) { + if (sig == SIGHUP) { for (i = 0; i < svnum; i++) if (sv[i].pid) kill(sv[i].pid, SIGTERM); @@ -393,9 +397,8 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv) /* SIGHUP or SIGTERM (or SIGUSRn if we are init) */ /* Exit unless we are init */ if (getpid() != 1) - return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS; + return (SIGHUP == sig) ? 111 : EXIT_SUCCESS; /* init continues to monitor services forever */ - bb_got_signal = 0; } /* for (;;) */ } -- cgit v1.2.3-55-g6feb From dac8d80f77af617effadc50f6be47768685d81b0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 2 Mar 2015 17:38:18 +0100 Subject: runsvdir: do not block SIGCHLD around poll/sleep There is no reason to do so. We do not even have SIGCHLD handler. function old new delta runsvdir_main 1077 1057 -20 Signed-off-by: Denys Vlasenko --- runit/runsvdir.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/runit/runsvdir.c b/runit/runsvdir.c index 809c48a51..a08af3bae 100644 --- a/runit/runsvdir.c +++ b/runit/runsvdir.c @@ -345,14 +345,12 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv) #endif { unsigned deadline = (need_rescan ? 1 : 5); - sig_block(SIGCHLD); #if ENABLE_FEATURE_RUNSVDIR_LOG if (rplog) poll(pfd, 1, deadline*1000); else #endif sleep(deadline); - sig_unblock(SIGCHLD); } #if ENABLE_FEATURE_RUNSVDIR_LOG -- cgit v1.2.3-55-g6feb From 7db2a7c20e8d48ad9cce934c9a6d2ee563d32c84 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 2 Mar 2015 17:39:13 +0100 Subject: runsvdir: if pid==1, also intercept SIGINT for -s SCRIPT function old new delta runsvdir_main 1057 1064 +7 Signed-off-by: Denys Vlasenko --- runit/runsvdir.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/runit/runsvdir.c b/runit/runsvdir.c index a08af3bae..b4c0b2ef0 100644 --- a/runit/runsvdir.c +++ b/runit/runsvdir.c @@ -222,6 +222,7 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv) unsigned stampcheck; int i; int need_rescan; + bool i_am_init; char *opt_s_argv[3]; INIT_G(); @@ -232,18 +233,21 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv) getopt32(argv, "Ps:", &opt_s_argv[0]); argv += optind; + i_am_init = (getpid() == 1); bb_signals(0 | (1 << SIGTERM) | (1 << SIGHUP) /* For busybox's init, SIGTERM == reboot, - * SIGUSR1 == halt - * SIGUSR2 == poweroff - * so we need to intercept SIGUSRn too. + * SIGUSR1 == halt, + * SIGUSR2 == poweroff, + * Ctlr-ALt-Del sends SIGINT to init, + * so we need to intercept SIGUSRn and SIGINT too. * Note that we do not implement actual reboot * (killall(TERM) + umount, etc), we just pause * respawing and avoid exiting (-> making kernel oops). - * The user is responsible for the rest. */ - | (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0) + * The user is responsible for the rest. + */ + | (i_am_init ? ((1 << SIGUSR1) | (1 << SIGUSR2) | (1 << SIGINT)) : 0) , record_signo); svdir = *argv++; @@ -394,7 +398,7 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv) } /* SIGHUP or SIGTERM (or SIGUSRn if we are init) */ /* Exit unless we are init */ - if (getpid() != 1) + if (!i_am_init) return (SIGHUP == sig) ? 111 : EXIT_SUCCESS; /* init continues to monitor services forever */ -- cgit v1.2.3-55-g6feb From 3aef814c0b08d9703280b4772060ce5016c683c4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 2 Mar 2015 20:59:13 +0100 Subject: ntpd: chnage help text about -l to say that it doesn't disable client code Signed-off-by: Denys Vlasenko --- networking/ntpd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/networking/ntpd.c b/networking/ntpd.c index c016bfc50..0247abb6c 100644 --- a/networking/ntpd.c +++ b/networking/ntpd.c @@ -50,15 +50,15 @@ //usage: "\n -q Quit after clock is set" //usage: "\n -N Run at high priority" //usage: "\n -w Do not set time (only query peers), implies -n" -//usage: IF_FEATURE_NTPD_SERVER( -//usage: "\n -l Run as server on port 123" -//usage: "\n -I IFACE Bind server to IFACE, implies -l" -//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: ) +//usage: IF_FEATURE_NTPD_SERVER( +//usage: "\n -l Also run as server on port 123" +//usage: "\n -I IFACE Bind server to IFACE, implies -l" +//usage: ) // -l and -p options are not compatible with "standard" ntpd: // it has them as "-l logfile" and "-p pidfile". -- cgit v1.2.3-55-g6feb From 2ec4f44225f18bc00732cac21f3fde9d3dc2f29f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 3 Mar 2015 13:10:30 +0100 Subject: inetd: fix order of array index check and array access Signed-off-by: Denys Vlasenko --- networking/inetd.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/networking/inetd.c b/networking/inetd.c index b3f2e9fbc..8148925ce 100644 --- a/networking/inetd.c +++ b/networking/inetd.c @@ -645,7 +645,7 @@ static servtab_t *dup_servtab(servtab_t *sep) } /* gcc generates much more code if this is inlined */ -static servtab_t *parse_one_line(void) +static NOINLINE servtab_t *parse_one_line(void) { int argc; char *token[6+MAXARGV]; @@ -675,6 +675,8 @@ static servtab_t *parse_one_line(void) * default host for the following lines. */ free(default_local_hostname); default_local_hostname = sep->se_local_hostname; + /*sep->se_local_hostname = NULL; - redundant */ + /* (we'll overwrite this field anyway) */ goto more; } } else @@ -688,10 +690,10 @@ static servtab_t *parse_one_line(void) parse_err: bb_error_msg("parse error on line %u, line is ignored", parser->lineno); - free_servtab_strings(sep); /* Just "goto more" can make sep to carry over e.g. * "rpc"-ness (by having se_rpcver_lo != 0). * We will be more paranoid: */ + free_servtab_strings(sep); free(sep); goto new; } @@ -815,7 +817,7 @@ static servtab_t *parse_one_line(void) } #endif argc = 0; - while ((arg = token[6+argc]) != NULL && argc < MAXARGV) + while (argc < MAXARGV && (arg = token[6+argc]) != NULL) sep->se_argv[argc++] = xstrdup(arg); /* Some inetd.conf files have no argv's, not even argv[0]. * Fix them up. -- cgit v1.2.3-55-g6feb From 3c31b092ac21c09ce6c76111bceba00debe300c3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 5 Mar 2015 14:04:44 +0100 Subject: ntpd: tweak help text Signed-off-by: Denys Vlasenko --- networking/ntpd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/networking/ntpd.c b/networking/ntpd.c index 0247abb6c..0233ed82c 100644 --- a/networking/ntpd.c +++ b/networking/ntpd.c @@ -53,7 +53,8 @@ //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: "\n If -p is not given, 'server HOST' lines" +//usage: "\n from /etc/ntp.conf are used" //usage: ) //usage: IF_FEATURE_NTPD_SERVER( //usage: "\n -l Also run as server on port 123" -- cgit v1.2.3-55-g6feb From 7b434a67dcaa88047095cf0196941c5456bb1c87 Mon Sep 17 00:00:00 2001 From: Isaac Dunham Date: Wed, 11 Mar 2015 16:07:24 +0100 Subject: mount: -T OTHERTAB support function old new delta mount_main 1221 1241 +20 packed_usage 30616 30610 -6 Signed-off-by: Denys Vlasenko Signed-off-by: Isaac Dunham --- util-linux/Config.src | 88 -------------------------------------- util-linux/mount.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 108 insertions(+), 94 deletions(-) diff --git a/util-linux/Config.src b/util-linux/Config.src index c1cd6daa4..854b3682e 100644 --- a/util-linux/Config.src +++ b/util-linux/Config.src @@ -434,94 +434,6 @@ config MORE you will probably find this utility very helpful. If you don't have any need to reading text files, you can leave this disabled. -config MOUNT - bool "mount" - default y - select PLATFORM_LINUX - help - All files and filesystems in Unix are arranged into one big directory - tree. The 'mount' utility is used to graft a filesystem onto a - particular part of the tree. A filesystem can either live on a block - device, or it can be accessible over the network, as is the case with - NFS filesystems. Most people using BusyBox will also want to enable - the 'mount' utility. - -config FEATURE_MOUNT_FAKE - bool "Support option -f" - default y - depends on MOUNT - help - Enable support for faking a file system mount. - -config FEATURE_MOUNT_VERBOSE - bool "Support option -v" - default y - depends on MOUNT - help - Enable multi-level -v[vv...] verbose messages. Useful if you - debug mount problems and want to see what is exactly passed - to the kernel. - -config FEATURE_MOUNT_HELPERS - bool "Support mount helpers" - default n - depends on MOUNT - help - Enable mounting of virtual file systems via external helpers. - E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call - "obexfs -b00.11.22.33.44.55 /mnt" - Also "mount -t sometype [-o opts] fs /mnt" will try - "sometype [-o opts] fs /mnt" if simple mount syscall fails. - The idea is to use such virtual filesystems in /etc/fstab. - -config FEATURE_MOUNT_LABEL - bool "Support specifying devices by label or UUID" - default y - depends on MOUNT - select VOLUMEID - help - This allows for specifying a device by label or uuid, rather than by - name. This feature utilizes the same functionality as blkid/findfs. - This also enables label or uuid support for swapon. - -config FEATURE_MOUNT_NFS - bool "Support mounting NFS file systems on Linux < 2.6.23" - default n - depends on MOUNT - select FEATURE_HAVE_RPC - select FEATURE_SYSLOG - help - Enable mounting of NFS file systems on Linux kernels prior - to version 2.6.23. Note that in this case mounting of NFS - over IPv6 will not be possible. - - Note that this option links in RPC support from libc, - which is rather large (~10 kbytes on uclibc). - -config FEATURE_MOUNT_CIFS - bool "Support mounting CIFS/SMB file systems" - default y - depends on MOUNT - help - Enable support for samba mounts. - -config FEATURE_MOUNT_FLAGS - depends on MOUNT - bool "Support lots of -o flags in mount" - default y - help - Without this, mount only supports ro/rw/remount. With this, it - supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime, - noatime, diratime, nodiratime, loud, bind, move, shared, slave, - private, unbindable, rshared, rslave, rprivate, and runbindable. - -config FEATURE_MOUNT_FSTAB - depends on MOUNT - bool "Support /etc/fstab and -a" - default y - help - Support mount all and looking for files in /etc/fstab. - config PIVOT_ROOT bool "pivot_root" default y diff --git a/util-linux/mount.c b/util-linux/mount.c index 62fd41fd7..fbc89c862 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c @@ -17,8 +17,103 @@ // mount_it_now() does the actual mount. // +//config:config MOUNT +//config: bool "mount" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: All files and filesystems in Unix are arranged into one big directory +//config: tree. The 'mount' utility is used to graft a filesystem onto a +//config: particular part of the tree. A filesystem can either live on a block +//config: device, or it can be accessible over the network, as is the case with +//config: NFS filesystems. Most people using BusyBox will also want to enable +//config: the 'mount' utility. +//config: +//config:config FEATURE_MOUNT_FAKE +//config: bool "Support option -f" +//config: default y +//config: depends on MOUNT +//config: help +//config: Enable support for faking a file system mount. +//config: +//config:config FEATURE_MOUNT_VERBOSE +//config: bool "Support option -v" +//config: default y +//config: depends on MOUNT +//config: help +//config: Enable multi-level -v[vv...] verbose messages. Useful if you +//config: debug mount problems and want to see what is exactly passed +//config: to the kernel. +//config: +//config:config FEATURE_MOUNT_HELPERS +//config: bool "Support mount helpers" +//config: default n +//config: depends on MOUNT +//config: help +//config: Enable mounting of virtual file systems via external helpers. +//config: E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call +//config: "obexfs -b00.11.22.33.44.55 /mnt" +//config: Also "mount -t sometype [-o opts] fs /mnt" will try +//config: "sometype [-o opts] fs /mnt" if simple mount syscall fails. +//config: The idea is to use such virtual filesystems in /etc/fstab. +//config: +//config:config FEATURE_MOUNT_LABEL +//config: bool "Support specifying devices by label or UUID" +//config: default y +//config: depends on MOUNT +//config: select VOLUMEID +//config: help +//config: This allows for specifying a device by label or uuid, rather than by +//config: name. This feature utilizes the same functionality as blkid/findfs. +//config: This also enables label or uuid support for swapon. +//config: +//config:config FEATURE_MOUNT_NFS +//config: bool "Support mounting NFS file systems on Linux < 2.6.23" +//config: default n +//config: depends on MOUNT +//config: select FEATURE_HAVE_RPC +//config: select FEATURE_SYSLOG +//config: help +//config: Enable mounting of NFS file systems on Linux kernels prior +//config: to version 2.6.23. Note that in this case mounting of NFS +//config: over IPv6 will not be possible. +//config: +//config: Note that this option links in RPC support from libc, +//config: which is rather large (~10 kbytes on uclibc). +//config: +//config:config FEATURE_MOUNT_CIFS +//config: bool "Support mounting CIFS/SMB file systems" +//config: default y +//config: depends on MOUNT +//config: help +//config: Enable support for samba mounts. +//config: +//config:config FEATURE_MOUNT_FLAGS +//config: depends on MOUNT +//config: bool "Support lots of -o flags in mount" +//config: default y +//config: help +//config: Without this, mount only supports ro/rw/remount. With this, it +//config: supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime, +//config: noatime, diratime, nodiratime, loud, bind, move, shared, slave, +//config: private, unbindable, rshared, rslave, rprivate, and runbindable. +//config: +//config:config FEATURE_MOUNT_FSTAB +//config: depends on MOUNT +//config: bool "Support /etc/fstab and -a" +//config: default y +//config: help +//config: Support mount all and looking for files in /etc/fstab. +//config: +//config:config FEATURE_MOUNT_OTHERTAB +//config: depends on FEATURE_MOUNT_FSTAB +//config: bool "Support -T " +//config: default y +//config: help +//config: Support mount -T (specifying an alternate fstab) + //usage:#define mount_trivial_usage -//usage: "[OPTIONS] [-o OPTS] DEVICE NODE" +//usage: "[OPTIONS] [-o OPT] DEVICE NODE" //usage:#define mount_full_usage "\n\n" //usage: "Mount a filesystem. Filesystem autodetection requires /proc.\n" //usage: "\n -a Mount all filesystems in fstab" @@ -41,8 +136,11 @@ //usage: ) ////usage: "\n -s Sloppy (ignored)" //usage: "\n -r Read-only mount" -//usage: "\n -w Read-write mount (default)" +////usage: "\n -w Read-write mount (default)" //usage: "\n -t FSTYPE[,...] Filesystem type(s)" +//usage: IF_FEATURE_MOUNT_OTHERTAB( +//usage: "\n -T FILE Read FILE instead of /etc/fstab" +//usage: ) //usage: "\n -O OPT Mount only filesystems with option OPT (-a only)" //usage: "\n-o OPT:" //usage: IF_FEATURE_MOUNT_LOOP( @@ -64,7 +162,7 @@ //usage: "\n move Relocate an existing mount point" //usage: ) //usage: "\n remount Remount a mounted filesystem, changing flags" -//usage: "\n ro/rw Same as -r/-w" +//usage: "\n ro Same as -r" //usage: "\n" //usage: "\nThere are filesystem-specific -o flags." //usage: @@ -167,7 +265,7 @@ enum { }; -#define OPTION_STR "o:t:rwanfvsiO:" +#define OPTION_STR "o:t:rwanfvsiO:" IF_FEATURE_MOUNT_OTHERTAB("T:") enum { OPT_o = (1 << 0), OPT_t = (1 << 1), @@ -180,6 +278,7 @@ enum { OPT_s = (1 << 8), OPT_i = (1 << 9), OPT_O = (1 << 10), + OPT_T = (1 << 11), }; #if ENABLE_FEATURE_MTAB_SUPPORT @@ -2034,7 +2133,7 @@ int mount_main(int argc UNUSED_PARAM, char **argv) char *O_optmatch = NULL; char *storage_path; llist_t *lst_o = NULL; - const char *fstabname; + const char *fstabname = "/etc/fstab"; FILE *fstab; int i, j; int rc = EXIT_SUCCESS; @@ -2061,6 +2160,7 @@ int mount_main(int argc UNUSED_PARAM, char **argv) // Max 2 params; -o is a list, -v is a counter opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv"); opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch + IF_FEATURE_MOUNT_OTHERTAB(, &fstabname) IF_FEATURE_MOUNT_VERBOSE(, &verbose)); while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r @@ -2128,8 +2228,10 @@ int mount_main(int argc UNUSED_PARAM, char **argv) return rc; } + // A malicious user could overmount /usr without this. + if (ENABLE_FEATURE_MOUNT_OTHERTAB && nonroot) + fstabname = "/etc/fstab"; // Open either fstab or mtab - fstabname = "/etc/fstab"; if (cmdopt_flags & MS_REMOUNT) { // WARNING. I am not sure this matches util-linux's // behavior. It's possible util-linux does not -- cgit v1.2.3-55-g6feb From fe8b5802bafb7bce7af525237d1195a91a3e4af4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 11 Mar 2015 18:01:34 +0100 Subject: patch: segfault fix. Closes 7916 Fix segfault on this case (malformed --- line): -- dwarves.orig 2015-02-25 01:45:27.753000000 +0000 +++ dwarves 2015-02-25 01:46:08.199000000 +0000 @@ -1,7 +1,7 @@ Bashful Doc Dopey -Grouchy +Grumpy Happy Sleepy Sneezy function old new delta patch_main 1903 1957 +54 Signed-off-by: Denys Vlasenko --- editors/patch.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/editors/patch.c b/editors/patch.c index 13785ef46..f86067544 100644 --- a/editors/patch.c +++ b/editors/patch.c @@ -345,6 +345,8 @@ done: // state 1: Found +++ file indicator, look for @@ // state 2: In hunk: counting initial context lines // state 3: In hunk: getting body +// Like GNU patch, we don't require a --- line before the +++, and +// also allow the --- after the +++ line. int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int patch_main(int argc UNUSED_PARAM, char **argv) @@ -462,6 +464,14 @@ int patch_main(int argc UNUSED_PARAM, char **argv) TT.context = 0; state = 2; + // If the --- line is missing or malformed, either oldname + // or (for -R) newname could be NULL -- but not both. Like + // GNU patch, proceed based on the +++ line, and avoid SEGVs. + if (!oldname) + oldname = xstrdup("MISSING_FILENAME"); + if (!newname) + newname = xstrdup("MISSING_FILENAME"); + // If this is the first hunk, open the file. if (TT.filein == -1) { int oldsum, newsum, empty = 0; -- cgit v1.2.3-55-g6feb From 748fb60f274b1ba40aa6ed4c4582185aae8f68f7 Mon Sep 17 00:00:00 2001 From: Alexander Korolkov Date: Thu, 12 Mar 2015 13:05:33 +0100 Subject: arping: fix iface name in error messages arping: interface eth0 not found: No such device ^^^^ This is because error template is formed before parsing command line arguments, so it always uses default interface name "eth0". Signed-off-by: Alexander Korolkov Signed-off-by: Denys Vlasenko --- networking/arping.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/networking/arping.c b/networking/arping.c index a4421edcb..dbfd75ef5 100644 --- a/networking/arping.c +++ b/networking/arping.c @@ -284,7 +284,6 @@ int arping_main(int argc UNUSED_PARAM, char **argv) // Need to remove SUID_NEVER from applets.h for this to work //xsetuid(getuid()); - err_str = xasprintf("interface %s %%s", device); { unsigned opt; char *str_timeout; @@ -302,7 +301,7 @@ int arping_main(int argc UNUSED_PARAM, char **argv) } target = argv[optind]; - + err_str = xasprintf("interface %s %%s", device); xfunc_error_retval = 2; { -- cgit v1.2.3-55-g6feb From 936c8809caea5705e26e5d7e06ea3895c28fffd8 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 12 Mar 2015 15:30:46 +0100 Subject: deluser: also remove user from /etc/group function old new delta update_passwd 1270 1470 +200 deluser_main 310 332 +22 Signed-off-by: Denys Vlasenko --- libbb/update_passwd.c | 38 ++++++++++++++++++++++++++++++++++++-- loginutils/deluser.c | 18 ++++++++++++------ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/libbb/update_passwd.c b/libbb/update_passwd.c index a30af6f72..dc26ebd1d 100644 --- a/libbb/update_passwd.c +++ b/libbb/update_passwd.c @@ -62,6 +62,8 @@ static void check_selinux_update_passwd(const char *username) only if CONFIG_PASSWD=y and applet_name[0] == 'p' like in passwd or if CONFIG_CHPASSWD=y and applet_name[0] == 'c' like in chpasswd + 8) delete a user from all groups: update_passwd(FILE, NULL, NULL, MEMBER) + This function does not validate the arguments fed to it so the calling program should take care of that. @@ -99,12 +101,13 @@ int FAST_FUNC update_passwd(const char *filename, if (filename == NULL) return ret; - check_selinux_update_passwd(name); + if (name) + check_selinux_update_passwd(name); /* New passwd file, "/etc/passwd+" for now */ fnamesfx = xasprintf("%s+", filename); sfx_char = &fnamesfx[strlen(fnamesfx)-1]; - name_colon = xasprintf("%s:", name); + name_colon = xasprintf("%s:", name ? name : ""); user_len = strlen(name_colon); if (shadow) @@ -167,6 +170,37 @@ int FAST_FUNC update_passwd(const char *filename, line = xmalloc_fgetline(old_fp); if (!line) /* EOF/error */ break; + + if (!name && member) { + /* Delete member from all groups */ + /* line is "GROUP:PASSWD:[member1[,member2]...]" */ + unsigned member_len = strlen(member); + char *list = strrchr(line, ':'); + while (list) { + list++; + next_list_element: + if (strncmp(list, member, member_len) == 0) { + char c; + changed_lines++; + c = list[member_len]; + if (c == '\0') { + if (list[-1] == ',') + list--; + *list = '\0'; + break; + } + if (c == ',') { + overlapping_strcpy(list, list + member_len + 1); + goto next_list_element; + } + changed_lines--; + } + list = strchr(list, ','); + } + fprintf(new_fp, "%s\n", line); + goto next; + } + if (strncmp(name_colon, line, user_len) != 0) { fprintf(new_fp, "%s\n", line); goto next; diff --git a/loginutils/deluser.c b/loginutils/deluser.c index 01a9386bc..110cd6310 100644 --- a/loginutils/deluser.c +++ b/loginutils/deluser.c @@ -114,16 +114,22 @@ int deluser_main(int argc, char **argv) } } while (ENABLE_FEATURE_SHADOWPASSWDS && pfile); - if (ENABLE_DELGROUP && do_deluser > 0) { - /* "deluser USER" also should try to delete - * same-named group. IOW: do "delgroup USER" - */ + if (do_deluser > 0) { + /* Delete user from all groups */ + if (update_passwd(bb_path_group_file, NULL, NULL, name) == -1) + return EXIT_FAILURE; + + if (ENABLE_DELGROUP) { + /* "deluser USER" also should try to delete + * same-named group. IOW: do "delgroup USER" + */ // On debian deluser is a perl script that calls userdel. // From man userdel: // If USERGROUPS_ENAB is defined to yes in /etc/login.defs, userdel will // delete the group with the same name as the user. - do_deluser = -1; - goto do_delgroup; + do_deluser = -1; + goto do_delgroup; + } } return EXIT_SUCCESS; } -- cgit v1.2.3-55-g6feb From 8dff01d06a7ebd7330e3a1dd1ba47b3c74ee7dfb Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 12 Mar 2015 17:48:34 +0100 Subject: libbb: introduce and use is_prefixed_with() function old new delta is_prefixed_with - 18 +18 complete_username 78 77 -1 man_main 737 735 -2 fsck_device 429 427 -2 unpack_ar_archive 80 76 -4 strip_unsafe_prefix 105 101 -4 singlemount 1054 1050 -4 rtc_adjtime_is_utc 90 86 -4 resolve_mount_spec 88 84 -4 parse_one_line 1029 1025 -4 parse_conf 1460 1456 -4 may_wakeup 83 79 -4 loadkmap_main 219 215 -4 get_irqs_from_stat 103 99 -4 get_header_cpio 913 909 -4 findfs_main 79 75 -4 fbsplash_main 1230 1226 -4 load_crontab 776 771 -5 expand_vars_to_list 1151 1146 -5 date_main 881 876 -5 skip_dev_pfx 30 24 -6 make_device 2199 2193 -6 complete_cmd_dir_file 773 767 -6 run_applet_and_exit 715 708 -7 uudecode_main 321 313 -8 pwdx_main 197 189 -8 execute 568 560 -8 i2cdetect_main 1186 1176 -10 procps_scan 1242 1230 -12 procps_read_smaps 1017 1005 -12 process_module 746 734 -12 patch_main 1903 1891 -12 nfsmount 3572 3560 -12 stack_machine 126 112 -14 process_timer_stats 449 435 -14 match_fstype 111 97 -14 do_ipaddr 1344 1330 -14 open_list_and_close 359 343 -16 get_header_tar 1795 1779 -16 prepend_new_eth_table 340 323 -17 fsck_main 1811 1794 -17 find_iface_state 56 38 -18 dnsd_main 1321 1303 -18 base_device 179 158 -21 find_keyword 104 82 -22 handle_incoming_and_exit 2785 2762 -23 parse_and_put_prompt 774 746 -28 modinfo 347 317 -30 find_action 204 171 -33 update_passwd 1470 1436 -34 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 0/49 up/down: 18/-540) Total: -522 bytes Signed-off-by: Denys Vlasenko --- archival/libarchive/get_header_cpio.c | 2 +- archival/libarchive/get_header_tar.c | 6 ++--- archival/libarchive/unpack_ar_archive.c | 2 +- archival/libarchive/unsafe_prefix.c | 2 +- console-tools/loadkmap.c | 2 +- coreutils/date.c | 2 +- coreutils/uudecode.c | 4 ++-- e2fsprogs/fsck.c | 40 ++++++++++++++++----------------- editors/patch.c | 4 ++-- include/libbb.h | 1 + libbb/appletlib.c | 8 +++---- libbb/compare_string_array.c | 23 ++++++++++++++++--- libbb/lineedit.c | 17 +++++++------- libbb/match_fstype.c | 7 +++--- libbb/procps.c | 12 +++++----- libbb/rtc.c | 2 +- libbb/skip_whitespace.c | 2 +- libbb/update_passwd.c | 9 ++++---- libbb/xconnect.c | 2 +- miscutils/crond.c | 4 ++-- miscutils/dc.c | 6 ++--- miscutils/devfsd.c | 10 ++++----- miscutils/fbsplash.c | 2 +- miscutils/i2c_tools.c | 4 ++-- miscutils/last.c | 6 ++--- miscutils/man.c | 4 ++-- modutils/depmod.c | 8 +++---- modutils/modinfo.c | 12 +++++----- modutils/modprobe-small.c | 21 ++++++++--------- modutils/modprobe.c | 12 ++++------ modutils/modutils-24.c | 2 +- networking/dnsd.c | 2 +- networking/httpd.c | 8 +++---- networking/ifupdown.c | 10 ++++----- networking/inetd.c | 2 +- networking/libiproute/ipaddress.c | 2 +- networking/nameif.c | 8 +++---- networking/netstat.c | 4 ++-- procps/mpstat.c | 2 +- procps/powertop.c | 4 ++-- procps/pwdx.c | 2 +- shell/hush.c | 2 +- util-linux/acpid.c | 2 +- util-linux/fdisk.c | 6 ++--- util-linux/fdisk_osf.c | 2 +- util-linux/fdisk_sgi.c | 2 +- util-linux/findfs.c | 2 +- util-linux/mdev.c | 2 +- util-linux/mount.c | 10 ++++----- util-linux/rtcwake.c | 2 +- util-linux/volume_id/get_devname.c | 4 ++-- 51 files changed, 165 insertions(+), 151 deletions(-) diff --git a/archival/libarchive/get_header_cpio.c b/archival/libarchive/get_header_cpio.c index 1a0058b63..7861d1f6f 100644 --- a/archival/libarchive/get_header_cpio.c +++ b/archival/libarchive/get_header_cpio.c @@ -37,7 +37,7 @@ char FAST_FUNC get_header_cpio(archive_handle_t *archive_handle) } archive_handle->offset += 110; - if (strncmp(&cpio_header[0], "07070", 5) != 0 + if (!is_prefixed_with(&cpio_header[0], "07070") || (cpio_header[5] != '1' && cpio_header[5] != '2') ) { bb_error_msg_and_die("unsupported cpio format, use newc or crc"); diff --git a/archival/libarchive/get_header_tar.c b/archival/libarchive/get_header_tar.c index 0c663fbd7..2dbcdb50c 100644 --- a/archival/libarchive/get_header_tar.c +++ b/archival/libarchive/get_header_tar.c @@ -105,7 +105,7 @@ static void process_pax_hdr(archive_handle_t *archive_handle, unsigned sz, int g value = end + 1; #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS - if (!global && strncmp(value, "path=", sizeof("path=") - 1) == 0) { + if (!global && is_prefixed_with(value, "path=")) { value += sizeof("path=") - 1; free(archive_handle->tar__longname); archive_handle->tar__longname = xstrdup(value); @@ -118,7 +118,7 @@ static void process_pax_hdr(archive_handle_t *archive_handle, unsigned sz, int g * This is what Red Hat's patched version of tar uses. */ # define SELINUX_CONTEXT_KEYWORD "RHT.security.selinux" - if (strncmp(value, SELINUX_CONTEXT_KEYWORD"=", sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1) == 0) { + if (is_prefixed_with(value, SELINUX_CONTEXT_KEYWORD"=")) { value += sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1; free(archive_handle->tar__sctx[global]); archive_handle->tar__sctx[global] = xstrdup(value); @@ -202,7 +202,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) /* Check header has valid magic, "ustar" is for the proper tar, * five NULs are for the old tar format */ - if (strncmp(tar.magic, "ustar", 5) != 0 + if (!is_prefixed_with(tar.magic, "ustar") && (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY || memcmp(tar.magic, "\0\0\0\0", 5) != 0) ) { diff --git a/archival/libarchive/unpack_ar_archive.c b/archival/libarchive/unpack_ar_archive.c index 214d17e23..0bc030349 100644 --- a/archival/libarchive/unpack_ar_archive.c +++ b/archival/libarchive/unpack_ar_archive.c @@ -12,7 +12,7 @@ void FAST_FUNC unpack_ar_archive(archive_handle_t *ar_archive) char magic[7]; xread(ar_archive->src_fd, magic, AR_MAGIC_LEN); - if (strncmp(magic, AR_MAGIC, AR_MAGIC_LEN) != 0) { + if (!is_prefixed_with(magic, AR_MAGIC)) { bb_error_msg_and_die("invalid ar magic"); } ar_archive->offset += AR_MAGIC_LEN; diff --git a/archival/libarchive/unsafe_prefix.c b/archival/libarchive/unsafe_prefix.c index 826c673bf..9994f4d94 100644 --- a/archival/libarchive/unsafe_prefix.c +++ b/archival/libarchive/unsafe_prefix.c @@ -15,7 +15,7 @@ const char* FAST_FUNC strip_unsafe_prefix(const char *str) cp++; continue; } - if (strncmp(cp, "/../"+1, 3) == 0) { + if (is_prefixed_with(cp, "/../"+1)) { cp += 3; continue; } diff --git a/console-tools/loadkmap.c b/console-tools/loadkmap.c index 6dcf8133f..f525ee5d1 100644 --- a/console-tools/loadkmap.c +++ b/console-tools/loadkmap.c @@ -57,7 +57,7 @@ int loadkmap_main(int argc UNUSED_PARAM, char **argv) */ xread(STDIN_FILENO, flags, 7); - if (strncmp(flags, BINARY_KEYMAP_MAGIC, 7)) + if (!is_prefixed_with(flags, BINARY_KEYMAP_MAGIC)) bb_error_msg_and_die("not a valid binary keymap"); xread(STDIN_FILENO, flags, MAX_NR_KEYMAPS); diff --git a/coreutils/date.c b/coreutils/date.c index 767e0d4a2..7965775fe 100644 --- a/coreutils/date.c +++ b/coreutils/date.c @@ -373,7 +373,7 @@ int date_main(int argc UNUSED_PARAM, char **argv) date_buf[0] = '\0'; } else { /* Handle special conversions */ - if (strncmp(fmt_dt2str, "%f", 2) == 0) { + if (is_prefixed_with(fmt_dt2str, "%f")) { fmt_dt2str = (char*)"%Y.%m.%d-%H:%M:%S"; } /* Generate output string */ diff --git a/coreutils/uudecode.c b/coreutils/uudecode.c index b298fcb95..3f1227306 100644 --- a/coreutils/uudecode.c +++ b/coreutils/uudecode.c @@ -110,10 +110,10 @@ int uudecode_main(int argc UNUSED_PARAM, char **argv) FILE *dst_stream; int mode; - if (strncmp(line, "begin-base64 ", 13) == 0) { + if (is_prefixed_with(line, "begin-base64 ")) { line_ptr = line + 13; decode_fn_ptr = read_base64; - } else if (strncmp(line, "begin ", 6) == 0) { + } else if (is_prefixed_with(line, "begin ")) { line_ptr = line + 6; decode_fn_ptr = read_stduu; } else { diff --git a/e2fsprogs/fsck.c b/e2fsprogs/fsck.c index d32f396e9..d2d312e5c 100644 --- a/e2fsprogs/fsck.c +++ b/e2fsprogs/fsck.c @@ -199,7 +199,7 @@ static char *base_device(const char *device) } /* Handle DAC 960 devices */ - if (strncmp(cp, "rd/", 3) == 0) { + if (is_prefixed_with(cp, "rd/")) { cp += 3; if (cp[0] != 'c' || !isdigit(cp[1]) || cp[2] != 'd' || !isdigit(cp[3])) @@ -224,9 +224,9 @@ static char *base_device(const char *device) #if ENABLE_FEATURE_DEVFS /* Now let's handle devfs (ugh) names */ len = 0; - if (strncmp(cp, "ide/", 4) == 0) + if (is_prefixed_with(cp, "ide/")) len = 4; - if (strncmp(cp, "scsi/", 5) == 0) + if (is_prefixed_with(cp, "scsi/")) len = 5; if (len) { cp += len; @@ -237,38 +237,38 @@ static char *base_device(const char *device) * some number of digits at each level, abort. */ for (hier = devfs_hier; *hier; hier++) { - len = strlen(*hier); - if (strncmp(cp, *hier, len) != 0) + cp = is_prefixed_with(cp, *hier); + if (!cp) goto errout; - cp += len; - while (*cp != '/' && *cp != 0) { + while (*cp != '/' && *cp != '\0') { if (!isdigit(*cp)) goto errout; cp++; } +//FIXME: what if *cp = '\0' now? cp++ moves past it!!! cp++; } - cp[-1] = 0; + cp[-1] = '\0'; return str; } /* Now handle devfs /dev/disc or /dev/disk names */ - disk = 0; - if (strncmp(cp, "discs/", 6) == 0) + disk = NULL; + if (is_prefixed_with(cp, "discs/")) disk = "disc"; - else if (strncmp(cp, "disks/", 6) == 0) + else if (is_prefixed_with(cp, "disks/")) disk = "disk"; if (disk) { cp += 6; - if (strncmp(cp, disk, 4) != 0) + cp = is_prefixed_with(cp, disk); + if (!cp) goto errout; - cp += 4; - while (*cp != '/' && *cp != 0) { + while (*cp != '/' && *cp != '\0') { if (!isdigit(*cp)) goto errout; cp++; } - *cp = 0; + *cp = '\0'; return str; } #endif @@ -593,8 +593,8 @@ static void fsck_device(struct fs_info *fs /*, int interactive */) type, "from fstab"); } else if (fstype && (fstype[0] != 'n' || fstype[1] != 'o') /* != "no" */ - && strncmp(fstype, "opts=", 5) != 0 - && strncmp(fstype, "loop", 4) != 0 + && !is_prefixed_with(fstype, "opts=") + && !is_prefixed_with(fstype, "loop") && !strchr(fstype, ',') ) { type = fstype; @@ -627,8 +627,8 @@ static int device_already_active(char *device) #ifdef BASE_MD /* Don't check a soft raid disk with any other disk */ if (instance_list - && (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1) - || !strncmp(device, BASE_MD, sizeof(BASE_MD)-1)) + && (is_prefixed_with(instance_list->device, BASE_MD) + || is_prefixed_with(device, BASE_MD)) ) { return 1; } @@ -895,7 +895,7 @@ static void compile_fs_type(char *fs_type) if (strcmp(s, "loop") == 0) /* loop is really short-hand for opts=loop */ goto loop_special_case; - if (strncmp(s, "opts=", 5) == 0) { + if (is_prefixed_with(s, "opts=")) { s += 5; loop_special_case: fs_type_flag[num] = negate ? FS_TYPE_FLAG_NEGOPT : FS_TYPE_FLAG_OPT; diff --git a/editors/patch.c b/editors/patch.c index f86067544..cb25e4140 100644 --- a/editors/patch.c +++ b/editors/patch.c @@ -414,7 +414,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv) } // Open a new file? - if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) { + if (is_prefixed_with(patchline, "--- ") || is_prefixed_with(patchline, "+++ ")) { char *s, **name = reverse ? &newname : &oldname; int i; @@ -446,7 +446,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv) // Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@ // but a missing ,value means the value is 1. - } else if (state == 1 && !strncmp("@@ -", patchline, 4)) { + } else if (state == 1 && is_prefixed_with(patchline, "@@ -")) { int i; char *s = patchline+4; diff --git a/include/libbb.h b/include/libbb.h index be792d6b2..c97df6047 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -389,6 +389,7 @@ const char *bb_basename(const char *name) FAST_FUNC; /* NB: can violate const-ness (similarly to strchr) */ char *last_char_is(const char *s, int c) FAST_FUNC; const char* endofname(const char *name) FAST_FUNC; +char *is_prefixed_with(const char *string, const char *key) FAST_FUNC; int ndelay_on(int fd) FAST_FUNC; int ndelay_off(int fd) FAST_FUNC; diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 54300bd87..8fd8fd525 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c @@ -193,7 +193,7 @@ void lbb_prepare(const char *applet if (argv[1] && !argv[2] && strcmp(argv[1], "--help") == 0 - && strncmp(applet, "busybox", 7) != 0 + && !is_prefixed_with(applet, "busybox") ) { /* Special case. POSIX says "test --help" * should be no different from e.g. "test --foo". */ @@ -673,7 +673,7 @@ static int busybox_main(char **argv) return 0; } - if (strncmp(argv[1], "--list", 6) == 0) { + if (is_prefixed_with(argv[1], "--list")) { unsigned i = 0; const char *a = applet_names; dup2(1, 2); @@ -778,7 +778,7 @@ void FAST_FUNC run_applet_and_exit(const char *name, char **argv) int applet = find_applet_by_name(name); if (applet >= 0) run_applet_no_and_exit(applet, argv); - if (strncmp(name, "busybox", 7) == 0) + if (is_prefixed_with(name, "busybox")) exit(busybox_main(argv)); } @@ -817,7 +817,7 @@ int main(int argc UNUSED_PARAM, char **argv) #if defined(SINGLE_APPLET_MAIN) /* Only one applet is selected in .config */ - if (argv[1] && strncmp(argv[0], "busybox", 7) == 0) { + if (argv[1] && is_prefixed_with(argv[0], "busybox")) { /* "busybox " should still work as expected */ argv++; } diff --git a/libbb/compare_string_array.c b/libbb/compare_string_array.c index 4b10cc138..e24815a03 100644 --- a/libbb/compare_string_array.c +++ b/libbb/compare_string_array.c @@ -5,6 +5,24 @@ #include "libbb.h" +char* FAST_FUNC is_prefixed_with(const char *string, const char *key) +{ +#if 0 /* Two passes over key - probably slower */ + int len = strlen(key); + if (strncmp(string, key, len) == 0) + return string + len; + return NULL; +#else /* Open-coded */ + while (*key != '\0') { + if (*key != *string) + return NULL; + key++; + string++; + } + return (char*)string; +#endif +} + /* returns the array index of the string */ /* (index of first match is returned, or -1) */ int FAST_FUNC index_in_str_array(const char *const string_array[], const char *key) @@ -39,10 +57,9 @@ int FAST_FUNC index_in_strings(const char *strings, const char *key) int FAST_FUNC index_in_substr_array(const char *const string_array[], const char *key) { int i; - int len = strlen(key); - if (len) { + if (key[0]) { for (i = 0; string_array[i] != 0; i++) { - if (strncmp(string_array[i], key, len) == 0) { + if (is_prefixed_with(string_array[i], key)) { return i; } } diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 249b401b4..a83e07c0c 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -681,7 +681,7 @@ static NOINLINE unsigned complete_username(const char *ud) setpwent(); while ((pw = getpwent()) != NULL) { /* Null usernames should result in all users as possible completions. */ - if (/*!userlen || */ strncmp(ud, pw->pw_name, userlen) == 0) { + if (/* !ud[0] || */ is_prefixed_with(pw->pw_name, ud)) { add_match(xasprintf("~%s/", pw->pw_name)); } } @@ -792,7 +792,7 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) if (!pfind[0] && DOT_OR_DOTDOT(name_found)) continue; /* match? */ - if (strncmp(name_found, pfind, pf_len) != 0) + if (!is_prefixed_with(name_found, pfind)) continue; /* no */ found = concat_path_file(paths[i], name_found); @@ -1879,15 +1879,16 @@ static void parse_and_put_prompt(const char *prmt_ptr) cwd_buf = xrealloc_getcwd_or_warn(NULL); if (!cwd_buf) cwd_buf = (char *)bb_msg_unknown; - else { + else if (home_pwd_buf[0]) { + char *after_home_user; + /* /home/user[/something] -> ~[/something] */ - l = strlen(home_pwd_buf); - if (l != 0 - && strncmp(home_pwd_buf, cwd_buf, l) == 0 - && (cwd_buf[l] == '/' || cwd_buf[l] == '\0') + after_home_user = is_prefixed_with(cwd_buf, home_pwd_buf); + if (after_home_user + && (*after_home_user == '/' || *after_home_user == '\0') ) { cwd_buf[0] = '~'; - overlapping_strcpy(cwd_buf + 1, cwd_buf + l); + overlapping_strcpy(cwd_buf + 1, after_home_user); } } } diff --git a/libbb/match_fstype.c b/libbb/match_fstype.c index 32c3d7f18..b066b4211 100644 --- a/libbb/match_fstype.c +++ b/libbb/match_fstype.c @@ -17,7 +17,6 @@ int FAST_FUNC match_fstype(const struct mntent *mt, const char *t_fstype) { int match = 1; - int len; if (!t_fstype) return match; @@ -27,10 +26,10 @@ int FAST_FUNC match_fstype(const struct mntent *mt, const char *t_fstype) t_fstype += 2; } - len = strlen(mt->mnt_type); while (1) { - if (strncmp(mt->mnt_type, t_fstype, len) == 0 - && (t_fstype[len] == '\0' || t_fstype[len] == ',') + char *after_mnt_type = is_prefixed_with(t_fstype, mt->mnt_type); + if (after_mnt_type + && (*after_mnt_type == '\0' || *after_mnt_type == ',') ) { return match; } diff --git a/libbb/procps.c b/libbb/procps.c index 5b68d3431..948b91ee6 100644 --- a/libbb/procps.c +++ b/libbb/procps.c @@ -205,11 +205,11 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, // Rss: nnn kB // ..... - char *tp = buf, *p; + char *tp, *p; #define SCAN(S, X) \ - if (strncmp(tp, S, sizeof(S)-1) == 0) { \ - tp = skip_whitespace(tp + sizeof(S)-1); \ + if ((tp = is_prefixed_with(buf, S)) != NULL) { \ + tp = skip_whitespace(tp); \ total->X += currec.X = fast_strtoul_10(&tp); \ continue; \ } @@ -247,7 +247,7 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, // skipping "rw-s FILEOFS M:m INODE " tp = skip_whitespace(skip_fields(tp, 4)); // filter out /dev/something (something != zero) - if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) { + if (!is_prefixed_with(tp, "/dev/") || strcmp(tp, "/dev/zero\n") == 0) { if (currec.smap_mode[1] == 'w') { currec.mapped_rw = currec.smap_size; total->mapped_rw += currec.smap_size; @@ -497,8 +497,8 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) while (fgets(buf, sizeof(buf), file)) { char *tp; #define SCAN_TWO(str, name, statement) \ - if (strncmp(buf, str, sizeof(str)-1) == 0) { \ - tp = skip_whitespace(buf + sizeof(str)-1); \ + if ((tp = is_prefixed_with(buf, str)) != NULL) { \ + tp = skip_whitespace(tp); \ sscanf(tp, "%u", &sp->name); \ statement; \ } diff --git a/libbb/rtc.c b/libbb/rtc.c index 6d06d57f9..c4117ba34 100644 --- a/libbb/rtc.c +++ b/libbb/rtc.c @@ -22,7 +22,7 @@ int FAST_FUNC rtc_adjtime_is_utc(void) char buffer[128]; while (fgets(buffer, sizeof(buffer), f)) { - if (strncmp(buffer, "UTC", 3) == 0) { + if (is_prefixed_with(buffer, "UTC")) { utc = 1; break; } diff --git a/libbb/skip_whitespace.c b/libbb/skip_whitespace.c index 8c7b674c3..b6cfbba4d 100644 --- a/libbb/skip_whitespace.c +++ b/libbb/skip_whitespace.c @@ -33,7 +33,7 @@ char* FAST_FUNC skip_non_whitespace(const char *s) char* FAST_FUNC skip_dev_pfx(const char *tty_name) { - if (strncmp(tty_name, "/dev/", 5) == 0) + if (is_prefixed_with(tty_name, "/dev/")) tty_name += 5; return (char*)tty_name; } diff --git a/libbb/update_passwd.c b/libbb/update_passwd.c index dc26ebd1d..a2004f480 100644 --- a/libbb/update_passwd.c +++ b/libbb/update_passwd.c @@ -84,7 +84,6 @@ int FAST_FUNC update_passwd(const char *filename, char *fnamesfx; char *sfx_char; char *name_colon; - unsigned user_len; int old_fd; int new_fd; int i; @@ -108,7 +107,6 @@ int FAST_FUNC update_passwd(const char *filename, fnamesfx = xasprintf("%s+", filename); sfx_char = &fnamesfx[strlen(fnamesfx)-1]; name_colon = xasprintf("%s:", name ? name : ""); - user_len = strlen(name_colon); if (shadow) old_fp = fopen(filename, "r+"); @@ -179,7 +177,7 @@ int FAST_FUNC update_passwd(const char *filename, while (list) { list++; next_list_element: - if (strncmp(list, member, member_len) == 0) { + if (is_prefixed_with(list, member)) { char c; changed_lines++; c = list[member_len]; @@ -201,13 +199,14 @@ int FAST_FUNC update_passwd(const char *filename, goto next; } - if (strncmp(name_colon, line, user_len) != 0) { + cp = is_prefixed_with(line, name_colon); + if (!cp) { fprintf(new_fp, "%s\n", line); goto next; } /* We have a match with "name:"... */ - cp = line + user_len; /* move past name: */ + /* cp points past "name:" */ #if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP if (member) { diff --git a/libbb/xconnect.c b/libbb/xconnect.c index 1c8bb2b73..2a96e03dc 100644 --- a/libbb/xconnect.c +++ b/libbb/xconnect.c @@ -171,7 +171,7 @@ IF_NOT_FEATURE_IPV6(sa_family_t af = AF_INET;) const char *cp; struct addrinfo hint; - if (ENABLE_FEATURE_UNIX_LOCAL && strncmp(host, "local:", 6) == 0) { + if (ENABLE_FEATURE_UNIX_LOCAL && is_prefixed_with(host, "local:")) { struct sockaddr_un *sun; r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_un)); diff --git a/miscutils/crond.c b/miscutils/crond.c index 3659b9a6f..eb327f855 100644 --- a/miscutils/crond.c +++ b/miscutils/crond.c @@ -438,14 +438,14 @@ static void load_crontab(const char *fileName) log5("user:%s entry:%s", fileName, parser->data); /* check if line is setting MAILTO= */ - if (0 == strncmp(tokens[0], "MAILTO=", 7)) { + if (is_prefixed_with(tokens[0], "MAILTO=")) { #if ENABLE_FEATURE_CROND_CALL_SENDMAIL free(mailTo); mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL; #endif /* otherwise just ignore such lines */ continue; } - if (0 == strncmp(tokens[0], "SHELL=", 6)) { + if (is_prefixed_with(tokens[0], "SHELL=")) { free(shell); shell = xstrdup(&tokens[0][6]); continue; diff --git a/miscutils/dc.c b/miscutils/dc.c index a7bd360d2..f94d6fa6b 100644 --- a/miscutils/dc.c +++ b/miscutils/dc.c @@ -244,9 +244,9 @@ static void stack_machine(const char *argument) o = operators; do { - const size_t name_len = strlen(o->name); - if (strncmp(o->name, argument, name_len) == 0) { - argument += name_len; + char *after_name = is_prefixed_with(argument, o->name); + if (after_name) { + argument = after_name; o->function(); goto next; } diff --git a/miscutils/devfsd.c b/miscutils/devfsd.c index 96ffe0738..5a6aec6bd 100644 --- a/miscutils/devfsd.c +++ b/miscutils/devfsd.c @@ -1405,7 +1405,6 @@ const char *get_old_name(const char *devname, unsigned int namelen, int indexx; const char *pty1; const char *pty2; - size_t len; /* 1 to 5 "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */ static const char *const fmt[] = { NULL , @@ -1425,12 +1424,11 @@ const char *get_old_name(const char *devname, unsigned int namelen, }; for (trans = translate_table; trans->match != NULL; ++trans) { - len = strlen(trans->match); - - if (strncmp(devname, trans->match, len) == 0) { + char *after_match = is_prefixed_with(devname, trans->match); + if (after_match) { if (trans->format == NULL) - return devname + len; - sprintf(buffer, trans->format, devname + len); + return after_match; + sprintf(buffer, trans->format, after_match); return buffer; } } diff --git a/miscutils/fbsplash.c b/miscutils/fbsplash.c index 7b695b26f..77033c258 100644 --- a/miscutils/fbsplash.c +++ b/miscutils/fbsplash.c @@ -516,7 +516,7 @@ int fbsplash_main(int argc UNUSED_PARAM, char **argv) // handle a case when we have many buffered lines // already in the pipe while ((num_buf = xmalloc_fgetline(fp)) != NULL) { - if (strncmp(num_buf, "exit", 4) == 0) { + if (is_prefixed_with(num_buf, "exit")) { DEBUG_MESSAGE("exit"); break; } diff --git a/miscutils/i2c_tools.c b/miscutils/i2c_tools.c index 90d1e1e14..03bb03974 100644 --- a/miscutils/i2c_tools.c +++ b/miscutils/i2c_tools.c @@ -1198,7 +1198,7 @@ static void NORETURN list_i2c_busses_and_exit(void) if (subde->d_name[0] == '.') continue; - if (strncmp(subde->d_name, "i2c-", 4) == 0) { + if (is_prefixed_with(subde->d_name, "i2c-")) { snprintf(path, NAME_MAX, "%s/%s/device/%s/name", i2cdev_path, de->d_name, @@ -1229,7 +1229,7 @@ found: if (rv != 1) continue; - if (strncmp(name, "ISA", 3) == 0) + if (is_prefixed_with(name, "ISA")) adt = ADT_ISA; else adt = i2cdetect_get_funcs(bus); diff --git a/miscutils/last.c b/miscutils/last.c index 24f6e1c78..a144c7e47 100644 --- a/miscutils/last.c +++ b/miscutils/last.c @@ -87,11 +87,11 @@ int last_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) if (++n > 0) ut.ut_type = n != 3 ? n : SHUTDOWN_TIME; #else - if (strncmp(ut.ut_user, "shutdown", 8) == 0) + if (is_prefixed_with(ut.ut_user, "shutdown")) ut.ut_type = SHUTDOWN_TIME; - else if (strncmp(ut.ut_user, "reboot", 6) == 0) + else if (is_prefixed_with(ut.ut_user, "reboot")) ut.ut_type = BOOT_TIME; - else if (strncmp(ut.ut_user, "runlevel", 8) == 0) + else if (is_prefixed_with(ut.ut_user, "runlevel")) ut.ut_type = RUN_LVL; #endif } else { diff --git a/miscutils/man.c b/miscutils/man.c index c39870e67..58ed81955 100644 --- a/miscutils/man.c +++ b/miscutils/man.c @@ -66,7 +66,7 @@ static int run_pipe(const char *pager, char *man_filename, int man, int level) goto ordinary_manpage; line = xmalloc_open_zipped_read_close(man_filename, NULL); - if (!line || strncmp(line, ".so ", 4) != 0) { + if (!line || !is_prefixed_with(line, ".so ")) { free(line); goto ordinary_manpage; } @@ -228,7 +228,7 @@ int man_main(int argc UNUSED_PARAM, char **argv) if (!token[1]) continue; if (strcmp("DEFINE", token[0]) == 0) { - if (strncmp("pager", token[1], 5) == 0) { + if (is_prefixed_with("pager", token[1])) { pager = xstrdup(skip_whitespace(token[1]) + 5); } } else diff --git a/modutils/depmod.c b/modutils/depmod.c index 37a8482d9..9713aef92 100644 --- a/modutils/depmod.c +++ b/modutils/depmod.c @@ -55,7 +55,7 @@ static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARA NULL ); for (ptr = image; ptr < image + len - 10; ptr++) { - if (strncmp(ptr, "depends=", 8) == 0) { + if (is_prefixed_with(ptr, "depends=")) { char *u; ptr += 8; @@ -64,15 +64,15 @@ static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARA *u = '_'; ptr += string_to_llist(ptr, &info->dependencies, ","); } else if (ENABLE_FEATURE_MODUTILS_ALIAS - && strncmp(ptr, "alias=", 6) == 0 + && is_prefixed_with(ptr, "alias=") ) { llist_add_to(&info->aliases, xstrdup(ptr + 6)); ptr += strlen(ptr); } else if (ENABLE_FEATURE_MODUTILS_SYMBOLS - && strncmp(ptr, "__ksymtab_", 10) == 0 + && is_prefixed_with(ptr, "__ksymtab_") ) { ptr += 10; - if (strncmp(ptr, "gpl", 3) == 0 + if (is_prefixed_with(ptr, "gpl") || strcmp(ptr, "strings") == 0 ) { continue; diff --git a/modutils/modinfo.c b/modutils/modinfo.c index ee379304c..8e74b6438 100644 --- a/modutils/modinfo.c +++ b/modutils/modinfo.c @@ -62,7 +62,7 @@ static void modinfo(const char *path, const char *version, "firmware", }; size_t len; - int j, length; + int j; char *ptr, *the_module; const char *field = env->field; int tags = env->tags; @@ -94,16 +94,18 @@ static void modinfo(const char *path, const char *version, pattern = field; if ((1< 0) { char *old = ptr; + char *after_word; + /* search for the first char in word */ - ptr = memchr(ptr, *word, len); + ptr = memchr(ptr, word[0], len); if (ptr == NULL) /* no occurance left, done */ break; - if (strncmp(ptr, word, wlen) == 0) - return ptr + wlen; /* found, return ptr past it */ + after_word = is_prefixed_with(ptr, word); + if (after_word) + return after_word; /* found, return ptr past it */ ++ptr; len -= (ptr - old); } @@ -536,7 +536,7 @@ static module_info** find_alias(const char *alias) // TODO: open only once, invent config_rewind() static int already_loaded(const char *name) { - int ret, namelen; + int ret; char *line; FILE *fp; @@ -545,15 +545,16 @@ static int already_loaded(const char *name) fp = fopen_for_read("/proc/modules"); if (!fp) return 0; - namelen = strlen(name); while ((line = xmalloc_fgetline(fp)) != NULL) { char *live; + char *after_name; // Examples from kernel 3.14.6: //pcspkr 12718 0 - Live 0xffffffffa017e000 //snd_timer 28690 2 snd_seq,snd_pcm, Live 0xffffffffa025e000 //i915 801405 2 - Live 0xffffffffa0096000 - if (strncmp(line, name, namelen) != 0 || line[namelen] != ' ') { + after_name = is_prefixed_with(line, name); + if (!after_name || *after_name != ' ') { free(line); continue; } diff --git a/modutils/modprobe.c b/modutils/modprobe.c index 0e8aa9e85..996de4074 100644 --- a/modutils/modprobe.c +++ b/modutils/modprobe.c @@ -260,7 +260,7 @@ static void add_probe(const char *name) llist_add_to_end(&G.probes, m); G.num_unresolved_deps++; if (ENABLE_FEATURE_MODUTILS_SYMBOLS - && strncmp(m->modname, "symbol:", 7) == 0 + && is_prefixed_with(m->modname, "symbol:") ) { G.need_symbols = 1; } @@ -353,22 +353,18 @@ static char *parse_and_add_kcmdline_module_options(char *options, const char *mo char *kcmdline_buf; char *kcmdline; char *kptr; - int len; kcmdline_buf = xmalloc_open_read_close("/proc/cmdline", NULL); if (!kcmdline_buf) return options; kcmdline = kcmdline_buf; - len = strlen(modulename); while ((kptr = strsep(&kcmdline, "\n\t ")) != NULL) { - if (strncmp(modulename, kptr, len) != 0) - continue; - kptr += len; - if (*kptr != '.') + char *after_modulename = is_prefixed_with(kptr, modulename); + if (!after_modulename || *after_modulename != '.') continue; /* It is "modulename.xxxx" */ - kptr++; + kptr = after_modulename + 1; if (strchr(kptr, '=') != NULL) { /* It is "modulename.opt=[val]" */ options = gather_options_str(options, kptr); diff --git a/modutils/modutils-24.c b/modutils/modutils-24.c index 12cb75c54..fe46fc3fd 100644 --- a/modutils/modutils-24.c +++ b/modutils/modutils-24.c @@ -2255,7 +2255,7 @@ static int add_symbols_from(struct obj_file *f, * symbols so they cannot fudge it by adding the prefix on * their references. */ - if (strncmp((char *)s->name, "GPLONLY_", 8) == 0) { + if (is_prefixed_with((char *)s->name, "GPLONLY_")) { #if ENABLE_FEATURE_CHECK_TAINTED_MODULE if (gpl) s->name += 8; diff --git a/networking/dnsd.c b/networking/dnsd.c index fe98400f7..923ad6bc6 100644 --- a/networking/dnsd.c +++ b/networking/dnsd.c @@ -194,7 +194,7 @@ static char *table_lookup(struct dns_entry *d, if ((len != 1 || d->name[1] != '*') /* we assume (do not check) that query_string * ends in ".in-addr.arpa" */ - && strncmp(d->rip, query_string, strlen(d->rip)) == 0 + && is_prefixed_with(query_string, d->rip) ) { #if DEBUG fprintf(stderr, "Found name:%s\n", d->name); diff --git a/networking/httpd.c b/networking/httpd.c index 9cf080401..7a9065fcc 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -697,7 +697,7 @@ static void parse_conf(const char *path, int flag) goto config_error; } *host_port++ = '\0'; - if (strncmp(host_port, "http://", 7) == 0) + if (is_prefixed_with(host_port, "http://")) host_port += 7; if (*host_port == '\0') { goto config_error; @@ -1894,7 +1894,7 @@ static Htaccess_Proxy *find_proxy_entry(const char *url) { Htaccess_Proxy *p; for (p = proxy; p; p = p->next) { - if (strncmp(url, p->url_from, strlen(p->url_from)) == 0) + if (is_prefixed_with(url, p->url_from)) return p; } return NULL; @@ -2183,7 +2183,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) if (STRNCASECMP(iobuf, "Range:") == 0) { /* We know only bytes=NNN-[MMM] */ char *s = skip_whitespace(iobuf + sizeof("Range:")-1); - if (strncmp(s, "bytes=", 6) == 0) { + if (is_prefixed_with(s, "bytes=") == 0) { s += sizeof("bytes=")-1; range_start = BB_STRTOOFF(s, &s, 10); if (s[0] != '-' || range_start < 0) { @@ -2269,7 +2269,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) tptr = urlcopy + 1; /* skip first '/' */ #if ENABLE_FEATURE_HTTPD_CGI - if (strncmp(tptr, "cgi-bin/", 8) == 0) { + if (is_prefixed_with(tptr, "cgi-bin/")) { if (tptr[8] == '\0') { /* protect listing "cgi-bin/" */ send_headers_and_exit(HTTP_FORBIDDEN); diff --git a/networking/ifupdown.c b/networking/ifupdown.c index c35d97a1a..daabeec0c 100644 --- a/networking/ifupdown.c +++ b/networking/ifupdown.c @@ -289,7 +289,7 @@ static char *parse(const char *command, struct interface_defn_t *ifd) /* "hwaddress
": * unlike ifconfig, ip doesnt want * (usually "ether" keyword). Skip it. */ - if (strncmp(command, "hwaddress", 9) == 0) { + if (is_prefixed_with(command, "hwaddress")) { varvalue = skip_whitespace(skip_non_whitespace(varvalue)); } # endif @@ -298,7 +298,7 @@ static char *parse(const char *command, struct interface_defn_t *ifd) # if ENABLE_FEATURE_IFUPDOWN_IP /* Sigh... Add a special case for 'ip' to convert from * dotted quad to bit count style netmasks. */ - if (strncmp(command, "bnmask", 6) == 0) { + if (is_prefixed_with(command, "bnmask")) { unsigned res; varvalue = get_var("netmask", 7, ifd); if (varvalue) { @@ -1159,12 +1159,12 @@ static char *run_mapping(char *physical, struct mapping_defn_t *map) static llist_t *find_iface_state(llist_t *state_list, const char *iface) { - unsigned iface_len = strlen(iface); llist_t *search = state_list; while (search) { - if ((strncmp(search->data, iface, iface_len) == 0) - && (search->data[iface_len] == '=') + char *after_iface = is_prefixed_with(search->data, iface); + if (after_iface + && *after_iface == '=' ) { return search; } diff --git a/networking/inetd.c b/networking/inetd.c index 8148925ce..dce5a0885 100644 --- a/networking/inetd.c +++ b/networking/inetd.c @@ -727,7 +727,7 @@ static NOINLINE servtab_t *parse_one_line(void) goto parse_err; #endif } - if (strncmp(arg, "rpc/", 4) == 0) { + if (is_prefixed_with(arg, "rpc/")) { #if ENABLE_FEATURE_INETD_RPC unsigned n; arg += 4; diff --git a/networking/libiproute/ipaddress.c b/networking/libiproute/ipaddress.c index aa4779ad1..4072d0626 100644 --- a/networking/libiproute/ipaddress.c +++ b/networking/libiproute/ipaddress.c @@ -701,7 +701,7 @@ static int ipaddr_modify(int cmd, char **argv) /* There was no "dev IFACE", but we need that */ bb_error_msg_and_die("need \"dev IFACE\""); } - if (l && strncmp(d, l, strlen(d)) != 0) { + if (l && !is_prefixed_with(l, d)) { bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s)", d, l); } diff --git a/networking/nameif.c b/networking/nameif.c index 9a8846dc0..9b18a6d16 100644 --- a/networking/nameif.c +++ b/networking/nameif.c @@ -161,19 +161,19 @@ static void nameif_parse_selector(ethtable_t *ch, char *selector) if (*next) *next++ = '\0'; /* Check for selectors, mac= is assumed */ - if (strncmp(selector, "bus=", 4) == 0) { + if (is_prefixed_with(selector, "bus=")) { ch->bus_info = xstrdup(selector + 4); found_selector++; - } else if (strncmp(selector, "driver=", 7) == 0) { + } else if (is_prefixed_with(selector, "driver=")) { ch->driver = xstrdup(selector + 7); found_selector++; - } else if (strncmp(selector, "phyaddr=", 8) == 0) { + } else if (is_prefixed_with(selector, "phyaddr=")) { ch->phy_address = xatoi_positive(selector + 8); found_selector++; } else { #endif lmac = xmalloc(ETH_ALEN); - ch->mac = ether_aton_r(selector + (strncmp(selector, "mac=", 4) != 0 ? 0 : 4), lmac); + ch->mac = ether_aton_r(selector + (is_prefixed_with(selector, "mac=") ? 4 : 0), lmac); if (ch->mac == NULL) bb_error_msg_and_die("can't parse %s", selector); #if ENABLE_FEATURE_NAMEIF_EXTENDED diff --git a/networking/netstat.c b/networking/netstat.c index f80b845bc..02f4cc7cc 100644 --- a/networking/netstat.c +++ b/networking/netstat.c @@ -228,12 +228,12 @@ static long extract_socket_inode(const char *lname) { long inode = -1; - if (strncmp(lname, "socket:[", sizeof("socket:[")-1) == 0) { + if (is_prefixed_with(lname, "socket:[")) { /* "socket:[12345]", extract the "12345" as inode */ inode = bb_strtoul(lname + sizeof("socket:[")-1, (char**)&lname, 0); if (*lname != ']') inode = -1; - } else if (strncmp(lname, "[0000]:", sizeof("[0000]:")-1) == 0) { + } else if (is_prefixed_with(lname, "[0000]:")) { /* "[0000]:12345", extract the "12345" as inode */ inode = bb_strtoul(lname + sizeof("[0000]:")-1, NULL, 0); if (errno) /* not NUL terminated? */ diff --git a/procps/mpstat.c b/procps/mpstat.c index af3263d67..6028903fa 100644 --- a/procps/mpstat.c +++ b/procps/mpstat.c @@ -526,7 +526,7 @@ static void get_irqs_from_stat(struct stats_irq *irq) while (fgets(buf, sizeof(buf), fp)) { //bb_error_msg("/proc/stat:'%s'", buf); - if (strncmp(buf, "intr ", 5) == 0) { + if (is_prefixed_with(buf, "intr ")) { /* Read total number of IRQs since system boot */ sscanf(buf + 5, "%"FMT_DATA"u", &irq->irq_nr); } diff --git a/procps/powertop.c b/procps/powertop.c index ddda5bd93..18affacdd 100644 --- a/procps/powertop.c +++ b/procps/powertop.c @@ -458,9 +458,9 @@ static NOINLINE int process_timer_stats(void) // func = "Load balancing tick"; //} - if (strncmp(func, "tick_nohz_", 10) == 0) + if (is_prefixed_with(func, "tick_nohz_")) continue; - if (strncmp(func, "tick_setup_sched_timer", 20) == 0) + if (is_prefixed_with(func, "tick_setup_sched_timer")) continue; //if (strcmp(process, "powertop") == 0) // continue; diff --git a/procps/pwdx.c b/procps/pwdx.c index 22b892275..4e34149ed 100644 --- a/procps/pwdx.c +++ b/procps/pwdx.c @@ -41,7 +41,7 @@ int pwdx_main(int argc UNUSED_PARAM, char **argv) // Allowed on the command line: // /proc/NUM // NUM - if (strncmp(arg, "/proc/", 6) == 0) + if (is_prefixed_with(arg, "/proc/")) arg += 6; pid = bb_strtou(arg, NULL, 10); diff --git a/shell/hush.c b/shell/hush.c index 92d790180..f2c0a70f2 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -5884,7 +5884,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) * Our solution: ONLY bare $(trap) or `trap` is special. */ s = skip_whitespace(s); - if (strncmp(s, "trap", 4) == 0 + if (is_prefixed_with(s, "trap") && skip_whitespace(s + 4)[0] == '\0' ) { static const char *const argv[] = { NULL, NULL }; diff --git a/util-linux/acpid.c b/util-linux/acpid.c index fc8151f6a..0f2cb6bdc 100644 --- a/util-linux/acpid.c +++ b/util-linux/acpid.c @@ -151,7 +151,7 @@ static const char *find_action(struct input_event *ev, const char *buf) } if (buf) { - if (strncmp(buf, evt_tab[i].desc, strlen(buf)) == 0) { + if (is_prefixed_with(evt_tab[i].desc, buf)) { action = evt_tab[i].desc; break; } diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 39eb27b47..7fe70fb72 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -2781,14 +2781,14 @@ is_ide_cdrom_or_tape(const char *device) the process hangs on the attempt to read a music CD. So try to be careful. This only works since 2.1.73. */ - if (strncmp("/dev/hd", device, 7)) + if (!is_prefixed_with(device, "/dev/hd")) return 0; snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5); procf = fopen_for_read(buf); if (procf != NULL && fgets(buf, sizeof(buf), procf)) - is_ide = (!strncmp(buf, "cdrom", 5) || - !strncmp(buf, "tape", 4)); + is_ide = (is_prefixed_with(buf, "cdrom") || + is_prefixed_with(buf, "tape")); else /* Now when this proc file does not exist, skip the device when it is read-only. */ diff --git a/util-linux/fdisk_osf.c b/util-linux/fdisk_osf.c index ff16389bd..af04cfcc8 100644 --- a/util-linux/fdisk_osf.c +++ b/util-linux/fdisk_osf.c @@ -854,7 +854,7 @@ xbsd_initlabel(struct partition *p) d->d_magic = BSD_DISKMAGIC; - if (strncmp(disk_device, "/dev/sd", 7) == 0) + if (is_prefixed_with(disk_device, "/dev/sd")) d->d_type = BSD_DTYPE_SCSI; else d->d_type = BSD_DTYPE_ST506; diff --git a/util-linux/fdisk_sgi.c b/util-linux/fdisk_sgi.c index 785fc661b..23ebc56ef 100644 --- a/util-linux/fdisk_sgi.c +++ b/util-linux/fdisk_sgi.c @@ -440,7 +440,7 @@ sgi_write_table(void) (unsigned int*)sgilabel, sizeof(*sgilabel)) == 0); write_sector(0, sgilabel); - if (!strncmp((char*)sgilabel->directory[0].vol_file_name, "sgilabel", 8)) { + if (is_prefixed_with((char*)sgilabel->directory[0].vol_file_name, "sgilabel")) { /* * keep this habit of first writing the "sgilabel". * I never tested whether it works without (AN 981002). diff --git a/util-linux/findfs.c b/util-linux/findfs.c index 49e8979ac..07734f359 100644 --- a/util-linux/findfs.c +++ b/util-linux/findfs.c @@ -27,7 +27,7 @@ int findfs_main(int argc UNUSED_PARAM, char **argv) if (!dev) bb_show_usage(); - if (strncmp(dev, "/dev/", 5) == 0) { + if (is_prefixed_with(dev, "/dev/")) { /* Just pass any /dev/xxx name right through. * This might aid in some scripts being able * to call this unconditionally */ diff --git a/util-linux/mdev.c b/util-linux/mdev.c index b2d56575f..ccc00d365 100644 --- a/util-linux/mdev.c +++ b/util-linux/mdev.c @@ -610,7 +610,7 @@ static void make_device(char *device_name, char *path, int operation) * We use strstr("/block/") to forestall future surprises. */ type = S_IFCHR; - if (strstr(path, "/block/") || (G.subsystem && strncmp(G.subsystem, "block", 5) == 0)) + if (strstr(path, "/block/") || (G.subsystem && is_prefixed_with(G.subsystem, "block"))) type = S_IFBLK; #if ENABLE_FEATURE_MDEV_CONF diff --git a/util-linux/mount.c b/util-linux/mount.c index fbc89c862..cb40c802d 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c @@ -641,7 +641,7 @@ static llist_t *get_block_backed_filesystems(void) if (!f) continue; while ((buf = xmalloc_fgetline(f)) != NULL) { - if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5])) + if (is_prefixed_with(buf, "nodev") && isspace(buf[5])) goto next; fs = skip_whitespace(buf); if (*fs == '#' || *fs == '*' || !*fs) @@ -1364,9 +1364,9 @@ static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *fi strcspn(opteq, " \t\n\r,")); continue; case 18: // "proto" - if (!strncmp(opteq, "tcp", 3)) + if (is_prefixed_with(opteq, "tcp")) tcp = 1; - else if (!strncmp(opteq, "udp", 3)) + else if (is_prefixed_with(opteq, "udp")) tcp = 0; else bb_error_msg("warning: unrecognized proto= option"); @@ -1459,7 +1459,7 @@ static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *fi "rdirplus\0" "acl\0"; int val = 1; - if (!strncmp(opt, "no", 2)) { + if (is_prefixed_with(opt, "no")) { val = 0; opt += 2; } @@ -1979,7 +1979,7 @@ static int singlemount(struct mntent *mp, int ignore_busy) } // Might this be an NFS filesystem? - if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0) + if ((!mp->mnt_type || is_prefixed_with(mp->mnt_type, "nfs")) && strchr(mp->mnt_fsname, ':') != NULL ) { if (!mp->mnt_type) diff --git a/util-linux/rtcwake.c b/util-linux/rtcwake.c index 53d9384db..8aee0cfcb 100644 --- a/util-linux/rtcwake.c +++ b/util-linux/rtcwake.c @@ -66,7 +66,7 @@ static NOINLINE bool may_wakeup(const char *rtcname) return false; /* wakeup events could be disabled or not supported */ - return strncmp(buf, "enabled\n", 8) == 0; + return is_prefixed_with(buf, "enabled\n") != NULL; } static NOINLINE void setup_alarm(int fd, time_t *wakeup, time_t rtc_time) diff --git a/util-linux/volume_id/get_devname.c b/util-linux/volume_id/get_devname.c index 0c6bdfddf..53bdbdf09 100644 --- a/util-linux/volume_id/get_devname.c +++ b/util-linux/volume_id/get_devname.c @@ -302,9 +302,9 @@ int resolve_mount_spec(char **fsname) { char *tmp = *fsname; - if (strncmp(*fsname, "UUID=", 5) == 0) + if (is_prefixed_with(*fsname, "UUID=")) tmp = get_devname_from_uuid(*fsname + 5); - else if (strncmp(*fsname, "LABEL=", 6) == 0) + else if (is_prefixed_with(*fsname, "LABEL=") == 0) tmp = get_devname_from_label(*fsname + 6); if (tmp == *fsname) -- cgit v1.2.3-55-g6feb From 53e9c51aded0af85c33d657143ed94c5151ba6f3 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Thu, 12 Mar 2015 20:10:40 +0100 Subject: od: fix printing of high-bit chars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example: echo £ | od -c Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- coreutils/od_bloaty.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c index 2c26dda16..ab7ea997d 100644 --- a/coreutils/od_bloaty.c +++ b/coreutils/od_bloaty.c @@ -387,11 +387,11 @@ print_named_ascii(size_t n_bytes, const char *block, " sp" }; // buf[N] pos: 01234 56789 - char buf[12] = " x\0 0xx\0"; - // actually " x\0 xxx\0", but want to share string with print_ascii. + char buf[12] = " x\0 xxx\0"; // [12] because we take three 32bit stack slots anyway, and // gcc is too dumb to initialize with constant stores, // it copies initializer from rodata. Oh well. + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65410 while (n_bytes--) { unsigned masked_c = *(unsigned char *) block++; @@ -419,7 +419,7 @@ print_ascii(size_t n_bytes, const char *block, const char *unused_fmt_string UNUSED_PARAM) { // buf[N] pos: 01234 56789 - char buf[12] = " x\0 0xx\0"; + char buf[12] = " x\0 xxx\0"; while (n_bytes--) { const char *s; @@ -455,11 +455,9 @@ print_ascii(size_t n_bytes, const char *block, case '\v': s = " \\v"; break; - case '\x7f': - s = " 177"; - break; - default: /* c is never larger than 040 */ - buf[7] = (c >> 3) + '0'; + default: + buf[6] = (c >> 6 & 3) + '0'; + buf[7] = (c >> 3 & 7) + '0'; buf[8] = (c & 7) + '0'; s = buf + 5; } -- cgit v1.2.3-55-g6feb From 8ec1ff350c28ae691ee80a001c7786f4ad8abe47 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Thu, 12 Mar 2015 20:18:51 +0100 Subject: Use chomp to remove newlines function old new delta unix_do_one 548 540 -8 process_timer_stats 508 500 -8 process_irq_counts 532 524 -8 lpd_main 839 831 -8 hwclock_main 502 494 -8 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/6 up/down: 0/-50) Total: -40 bytes Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- networking/netstat.c | 2 +- printutils/lpd.c | 2 +- procps/powertop.c | 4 ++-- util-linux/hwclock.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/networking/netstat.c b/networking/netstat.c index 02f4cc7cc..1303d3f3d 100644 --- a/networking/netstat.c +++ b/networking/netstat.c @@ -622,7 +622,7 @@ static int FAST_FUNC unix_do_one(char *line) /* TODO: currently we stop at first NUL byte. Is it a problem? */ line += path_ofs; - *strchrnul(line, '\n') = '\0'; + chomp(line); while (*line) fputc_printable(*line++, stdout); bb_putchar('\n'); diff --git a/printutils/lpd.c b/printutils/lpd.c index 642e8a89e..eaf42c08b 100644 --- a/printutils/lpd.c +++ b/printutils/lpd.c @@ -204,7 +204,7 @@ int lpd_main(int argc UNUSED_PARAM, char *argv[]) goto err_exit; } // get filename - *strchrnul(s, '\n') = '\0'; + chomp(s); fname = strchr(s, ' '); if (!fname) { // bad_fname: diff --git a/procps/powertop.c b/procps/powertop.c index 18affacdd..1de5d329e 100644 --- a/procps/powertop.c +++ b/procps/powertop.c @@ -360,7 +360,7 @@ static void process_irq_counts(void) } name = p; - strchrnul(name, '\n')[0] = '\0'; + chomp(p); /* Save description of the interrupt */ if (nr >= 20000) sprintf(irq_desc, " : %s", name); @@ -470,7 +470,7 @@ static NOINLINE int process_timer_stats(void) process = idx < 2 ? "[kernel module]" : ""; } - strchrnul(p, '\n')[0] = '\0'; + chomp(p); // 46D\01136\0kondemand/1\0do_dbs_timer (delayed_work_timer_fn) // ^ ^ ^ diff --git a/util-linux/hwclock.c b/util-linux/hwclock.c index 3f531555b..6c99977cf 100644 --- a/util-linux/hwclock.c +++ b/util-linux/hwclock.c @@ -69,7 +69,7 @@ static void show_clock(const char **pp_rtcname, int utc) strftime(cp, sizeof(cp), "%c", ptm); #else char *cp = ctime(&t); - strchrnul(cp, '\n')[0] = '\0'; + chomp(cp); #endif #if !SHOW_HWCLOCK_DIFF -- cgit v1.2.3-55-g6feb From 49acc1a7618a28d34381cbb7661d7c981fcb238f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 12 Mar 2015 21:15:34 +0100 Subject: vi: make BACKSPACE and DELETE join lines at start/end of line Signed-off-by: Denys Vlasenko --- editors/vi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/editors/vi.c b/editors/vi.c index 77535be92..495332a46 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -2017,8 +2017,7 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' p--; } } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS - // 123456789 - if ((p[-1] != '\n') && (dot>text)) { + if (p > text) { p--; p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED); // shrink buffer 1 char } @@ -4026,8 +4025,9 @@ static void do_cmd(int c) undo_queue_commit(); break; case KEYCODE_DELETE: - c = 'x'; - // fall through + if (dot < end - 1) + dot = yank_delete(dot, dot, 1, YANKDEL, ALLOW_UNDO); + break; case 'X': // X- delete char before dot case 'x': // x- delete the current char case 's': // s- substitute the current char -- cgit v1.2.3-55-g6feb