aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2015-03-14 20:33:00 +0000
committerRon Yorston <rmy@pobox.com>2015-03-14 20:33:00 +0000
commita4f58436b78fe59e57620c6e0301f213ee25f273 (patch)
tree8355f724926e605280af2d6f2b1ccc6b1bd02dee
parentba0c36cfcf84efbac6f89e27238e04bb57e9cd45 (diff)
parent49acc1a7618a28d34381cbb7661d7c981fcb238f (diff)
downloadbusybox-w32-a4f58436b78fe59e57620c6e0301f213ee25f273.tar.gz
busybox-w32-a4f58436b78fe59e57620c6e0301f213ee25f273.tar.bz2
busybox-w32-a4f58436b78fe59e57620c6e0301f213ee25f273.zip
Merge branch 'busybox' into merge
Conflicts: coreutils/od_bloaty.c libbb/lineedit.c
-rw-r--r--.gitignore8
-rw-r--r--Makefile23
-rw-r--r--archival/dpkg.c11
-rw-r--r--archival/dpkg_deb.c6
-rw-r--r--archival/gzip.c61
-rw-r--r--archival/libarchive/Kbuild.src6
-rw-r--r--archival/libarchive/filter_accept_list_reassign.c10
-rw-r--r--archival/libarchive/get_header_cpio.c2
-rw-r--r--archival/libarchive/get_header_tar.c36
-rw-r--r--archival/libarchive/get_header_tar_xz.c21
-rw-r--r--archival/libarchive/unpack_ar_archive.c2
-rw-r--r--archival/libarchive/unsafe_prefix.c36
-rw-r--r--archival/unzip.c35
-rw-r--r--console-tools/loadkmap.c7
-rw-r--r--coreutils/Config.src38
-rw-r--r--coreutils/basename.c2
-rw-r--r--coreutils/date.c2
-rw-r--r--coreutils/dd.c143
-rw-r--r--coreutils/ls.c23
-rw-r--r--coreutils/od_bloaty.c5
-rw-r--r--coreutils/uudecode.c4
-rw-r--r--e2fsprogs/fsck.c40
-rw-r--r--editors/diff.c3
-rw-r--r--editors/patch.c14
-rw-r--r--editors/vi.c8
-rw-r--r--examples/mdev_fat.conf12
-rw-r--r--include/bb_archive.h1
-rw-r--r--include/grp_.h65
-rw-r--r--include/libbb.h3
-rw-r--r--include/platform.h17
-rw-r--r--include/pwd_.h53
-rw-r--r--include/shadow_.h26
-rw-r--r--init/init.c6
-rw-r--r--libbb/appletlib.c8
-rw-r--r--libbb/bb_pwd.c48
-rw-r--r--libbb/compare_string_array.c23
-rw-r--r--libbb/inet_common.c84
-rw-r--r--libbb/lineedit.c35
-rw-r--r--libbb/loop.c11
-rw-r--r--libbb/match_fstype.c7
-rw-r--r--libbb/procps.c12
-rw-r--r--libbb/rtc.c2
-rw-r--r--libbb/skip_whitespace.c2
-rw-r--r--libbb/update_passwd.c45
-rw-r--r--libbb/utmp.c14
-rw-r--r--libbb/xconnect.c2
-rw-r--r--libpwdgrp/pwd_grp.c1276
-rw-r--r--libpwdgrp/pwd_grp_internal.c61
-rw-r--r--loginutils/deluser.c49
-rw-r--r--loginutils/login.c2
-rw-r--r--miscutils/crond.c4
-rw-r--r--miscutils/dc.c57
-rw-r--r--miscutils/devfsd.c10
-rw-r--r--miscutils/fbsplash.c2
-rw-r--r--miscutils/i2c_tools.c1396
-rw-r--r--miscutils/last.c6
-rw-r--r--miscutils/last_fancy.c5
-rw-r--r--miscutils/man.c4
-rw-r--r--miscutils/ubi_tools.c12
-rw-r--r--modutils/depmod.c39
-rw-r--r--modutils/modinfo.c14
-rw-r--r--modutils/modprobe-small.c182
-rw-r--r--modutils/modprobe.c48
-rw-r--r--modutils/modutils-24.c2
-rw-r--r--modutils/modutils.c14
-rw-r--r--networking/Config.src3
-rw-r--r--networking/arping.c3
-rw-r--r--networking/dnsd.c2
-rw-r--r--networking/ftpd.c9
-rw-r--r--networking/httpd.c8
-rw-r--r--networking/ifupdown.c10
-rw-r--r--networking/inetd.c12
-rw-r--r--networking/interface.c4
-rw-r--r--networking/libiproute/ipaddress.c2
-rw-r--r--networking/nameif.c8
-rw-r--r--networking/netstat.c6
-rw-r--r--networking/ntpd.c69
-rw-r--r--networking/ntpd_simple.c1005
-rw-r--r--networking/route.c48
-rw-r--r--networking/telnetd.c12
-rw-r--r--networking/udhcp/dhcpc.c6
-rw-r--r--networking/udhcp/dhcpd.c3
-rw-r--r--networking/wget.c98
-rw-r--r--printutils/lpd.c2
-rw-r--r--procps/free.c77
-rw-r--r--procps/mpstat.c10
-rw-r--r--procps/powertop.c10
-rw-r--r--procps/pwdx.c4
-rw-r--r--runit/runsvdir.c67
-rw-r--r--shell/ash.c33
-rw-r--r--shell/ash_test/ash-heredoc/heredoc1.right1
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc1.tests3
-rw-r--r--shell/hush.c2
-rwxr-xr-xtestsuite/dc.tests56
-rwxr-xr-xtestsuite/diff.tests11
-rw-r--r--util-linux/Config.src88
-rw-r--r--util-linux/acpid.c10
-rw-r--r--util-linux/fdisk.c6
-rw-r--r--util-linux/fdisk_osf.c2
-rw-r--r--util-linux/fdisk_sgi.c2
-rw-r--r--util-linux/findfs.c2
-rw-r--r--util-linux/fstrim.c2
-rw-r--r--util-linux/hwclock.c2
-rw-r--r--util-linux/mdev.c2
-rw-r--r--util-linux/mount.c124
-rw-r--r--util-linux/rdate.c8
-rw-r--r--util-linux/rtcwake.c14
-rw-r--r--util-linux/swaponoff.c18
-rw-r--r--util-linux/volume_id/get_devname.c4
109 files changed, 3134 insertions, 2939 deletions
diff --git a/.gitignore b/.gitignore
index 01400a678..2a7f2f79b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,11 @@ core
43/busybox.links 43/busybox.links
44/runtest-tempdir-links 44/runtest-tempdir-links
45/testsuite/echo-ne 45/testsuite/echo-ne
46
47#
48# cscope output
49#
50cscope.files
51cscope.in.out
52cscope.out
53cscope.po.out
diff --git a/Makefile b/Makefile
index a64b0c926..0db8631b9 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
1VERSION = 1 1VERSION = 1
2PATCHLEVEL = 23 2PATCHLEVEL = 24
3SUBLEVEL = 0 3SUBLEVEL = 0
4EXTRAVERSION = 4EXTRAVERSION = .git
5NAME = Unnamed 5NAME = Unnamed
6 6
7# *DOCUMENTATION* 7# *DOCUMENTATION*
@@ -1167,24 +1167,7 @@ endif
1167ALLSOURCE_ARCHS := $(ARCH) 1167ALLSOURCE_ARCHS := $(ARCH)
1168 1168
1169define all-sources 1169define all-sources
1170 ( find $(__srctree) $(RCS_FIND_IGNORE) \ 1170 ( find -regex '.*\.[ch]$$' )
1171 \( -name include -o -name arch \) -prune -o \
1172 -name '*.[chS]' -print; \
1173 for ARCH in $(ALLSOURCE_ARCHS) ; do \
1174 find $(__srctree)arch/$${ARCH} $(RCS_FIND_IGNORE) \
1175 -name '*.[chS]' -print; \
1176 done ; \
1177 find $(__srctree)security/selinux/include $(RCS_FIND_IGNORE) \
1178 -name '*.[chS]' -print; \
1179 find $(__srctree)include $(RCS_FIND_IGNORE) \
1180 \( -name config -o -name 'asm-*' \) -prune \
1181 -o -name '*.[chS]' -print; \
1182 for ARCH in $(ALLINCLUDE_ARCHS) ; do \
1183 find $(__srctree)include/asm-$${ARCH} $(RCS_FIND_IGNORE) \
1184 -name '*.[chS]' -print; \
1185 done ; \
1186 find $(__srctree)include/asm-generic $(RCS_FIND_IGNORE) \
1187 -name '*.[chS]' -print )
1188endef 1171endef
1189 1172
1190quiet_cmd_cscope-file = FILELST cscope.files 1173quiet_cmd_cscope-file = FILELST cscope.files
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)
1472 tar_handle->src_fd = ar_handle->src_fd; 1472 tar_handle->src_fd = ar_handle->src_fd;
1473 1473
1474 /* We don't care about data.tar.* or debian-binary, just control.tar.* */ 1474 /* We don't care about data.tar.* or debian-binary, just control.tar.* */
1475 llist_add_to(&(ar_handle->accept), (char*)"control.tar");
1475#if ENABLE_FEATURE_SEAMLESS_GZ 1476#if ENABLE_FEATURE_SEAMLESS_GZ
1476 llist_add_to(&(ar_handle->accept), (char*)"control.tar.gz"); 1477 llist_add_to(&(ar_handle->accept), (char*)"control.tar.gz");
1477#endif 1478#endif
1478#if ENABLE_FEATURE_SEAMLESS_BZ2 1479#if ENABLE_FEATURE_SEAMLESS_BZ2
1479 llist_add_to(&(ar_handle->accept), (char*)"control.tar.bz2"); 1480 llist_add_to(&(ar_handle->accept), (char*)"control.tar.bz2");
1480#endif 1481#endif
1482#if ENABLE_FEATURE_SEAMLESS_XZ
1483 llist_add_to(&(ar_handle->accept), (char*)"control.tar.xz");
1484#endif
1481 1485
1482 /* Assign the tar handle as a subarchive of the ar handle */ 1486 /* Assign the tar handle as a subarchive of the ar handle */
1483 ar_handle->dpkg__sub_archive = tar_handle; 1487 ar_handle->dpkg__sub_archive = tar_handle;
@@ -1492,12 +1496,19 @@ static void init_archive_deb_data(archive_handle_t *ar_handle)
1492 tar_handle->src_fd = ar_handle->src_fd; 1496 tar_handle->src_fd = ar_handle->src_fd;
1493 1497
1494 /* We don't care about control.tar.* or debian-binary, just data.tar.* */ 1498 /* We don't care about control.tar.* or debian-binary, just data.tar.* */
1499 llist_add_to(&(ar_handle->accept), (char*)"data.tar");
1495#if ENABLE_FEATURE_SEAMLESS_GZ 1500#if ENABLE_FEATURE_SEAMLESS_GZ
1496 llist_add_to(&(ar_handle->accept), (char*)"data.tar.gz"); 1501 llist_add_to(&(ar_handle->accept), (char*)"data.tar.gz");
1497#endif 1502#endif
1498#if ENABLE_FEATURE_SEAMLESS_BZ2 1503#if ENABLE_FEATURE_SEAMLESS_BZ2
1499 llist_add_to(&(ar_handle->accept), (char*)"data.tar.bz2"); 1504 llist_add_to(&(ar_handle->accept), (char*)"data.tar.bz2");
1500#endif 1505#endif
1506#if ENABLE_FEATURE_SEAMLESS_LZMA
1507 llist_add_to(&(ar_handle->accept), (char*)"data.tar.lzma");
1508#endif
1509#if ENABLE_FEATURE_SEAMLESS_XZ
1510 llist_add_to(&(ar_handle->accept), (char*)"data.tar.xz");
1511#endif
1501 1512
1502 /* Assign the tar handle as a subarchive of the ar handle */ 1513 /* Assign the tar handle as a subarchive of the ar handle */
1503 ar_handle->dpkg__sub_archive = tar_handle; 1514 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)
70 ar_archive->dpkg__sub_archive = tar_archive; 70 ar_archive->dpkg__sub_archive = tar_archive;
71 ar_archive->filter = filter_accept_list_reassign; 71 ar_archive->filter = filter_accept_list_reassign;
72 72
73 llist_add_to(&ar_archive->accept, (char*)"data.tar");
74 llist_add_to(&control_tar_llist, (char*)"control.tar");
73#if ENABLE_FEATURE_SEAMLESS_GZ 75#if ENABLE_FEATURE_SEAMLESS_GZ
74 llist_add_to(&ar_archive->accept, (char*)"data.tar.gz"); 76 llist_add_to(&ar_archive->accept, (char*)"data.tar.gz");
75 llist_add_to(&control_tar_llist, (char*)"control.tar.gz"); 77 llist_add_to(&control_tar_llist, (char*)"control.tar.gz");
@@ -82,6 +84,10 @@ int dpkg_deb_main(int argc, char **argv)
82 llist_add_to(&ar_archive->accept, (char*)"data.tar.lzma"); 84 llist_add_to(&ar_archive->accept, (char*)"data.tar.lzma");
83 llist_add_to(&control_tar_llist, (char*)"control.tar.lzma"); 85 llist_add_to(&control_tar_llist, (char*)"control.tar.lzma");
84#endif 86#endif
87#if ENABLE_FEATURE_SEAMLESS_XZ
88 llist_add_to(&ar_archive->accept, (char*)"data.tar.xz");
89 llist_add_to(&control_tar_llist, (char*)"control.tar.xz");
90#endif
85 91
86 opt_complementary = "c--efXx:e--cfXx:f--ceXx:X--cefx:x--cefX"; 92 opt_complementary = "c--efXx:e--cfXx:f--ceXx:X--cefx:x--cefX";
87 opt = getopt32(argv, "cefXx"); 93 opt = getopt32(argv, "cefXx");
diff --git a/archival/gzip.c b/archival/gzip.c
index a93d2175a..bc1f9c60b 100644
--- a/archival/gzip.c
+++ b/archival/gzip.c
@@ -417,19 +417,46 @@ static void flush_outbuf(void)
417#define put_8bit(c) \ 417#define put_8bit(c) \
418do { \ 418do { \
419 G1.outbuf[G1.outcnt++] = (c); \ 419 G1.outbuf[G1.outcnt++] = (c); \
420 if (G1.outcnt == OUTBUFSIZ) flush_outbuf(); \ 420 if (G1.outcnt == OUTBUFSIZ) \
421 flush_outbuf(); \
421} while (0) 422} while (0)
422 423
423/* Output a 16 bit value, lsb first */ 424/* Output a 16 bit value, lsb first */
424static void put_16bit(ush w) 425static void put_16bit(ush w)
425{ 426{
426 if (G1.outcnt < OUTBUFSIZ - 2) { 427 /* GCC 4.2.1 won't optimize out redundant loads of G1.outcnt
427 G1.outbuf[G1.outcnt++] = w; 428 * (probably because of fear of aliasing with G1.outbuf[]
428 G1.outbuf[G1.outcnt++] = w >> 8; 429 * stores), do it explicitly:
429 } else { 430 */
430 put_8bit(w); 431 unsigned outcnt = G1.outcnt;
431 put_8bit(w >> 8); 432 uch *dst = &G1.outbuf[outcnt];
433
434#if BB_UNALIGNED_MEMACCESS_OK && BB_LITTLE_ENDIAN
435 if (outcnt < OUTBUFSIZ-2) {
436 /* Common case */
437 ush *dst16 = (void*) dst;
438 *dst16 = w; /* unalinged LSB 16-bit store */
439 G1.outcnt = outcnt + 2;
440 return;
432 } 441 }
442 *dst = (uch)w;
443 w >>= 8;
444#else
445 *dst = (uch)w;
446 w >>= 8;
447 if (outcnt < OUTBUFSIZ-2) {
448 /* Common case */
449 dst[1] = w;
450 G1.outcnt = outcnt + 2;
451 return;
452 }
453#endif
454
455 /* Slowpath: we will need to do flush_outbuf() */
456 G1.outcnt = ++outcnt;
457 if (outcnt == OUTBUFSIZ)
458 flush_outbuf();
459 put_8bit(w);
433} 460}
434 461
435static void put_32bit(ulg n) 462static void put_32bit(ulg n)
@@ -2007,7 +2034,7 @@ static void ct_init(void)
2007 * IN assertions: the input and output buffers are cleared. 2034 * IN assertions: the input and output buffers are cleared.
2008 */ 2035 */
2009 2036
2010static void zip(ulg time_stamp) 2037static void zip(void)
2011{ 2038{
2012 ush deflate_flags = 0; /* pkzip -es, -en or -ex equivalent */ 2039 ush deflate_flags = 0; /* pkzip -es, -en or -ex equivalent */
2013 2040
@@ -2018,7 +2045,7 @@ static void zip(ulg time_stamp)
2018 /* compression method: 8 (DEFLATED) */ 2045 /* compression method: 8 (DEFLATED) */
2019 /* general flags: 0 */ 2046 /* general flags: 0 */
2020 put_32bit(0x00088b1f); 2047 put_32bit(0x00088b1f);
2021 put_32bit(time_stamp); 2048 put_32bit(0); /* Unix timestamp */
2022 2049
2023 /* Write deflated file to zip file */ 2050 /* Write deflated file to zip file */
2024 G1.crc = ~0; 2051 G1.crc = ~0;
@@ -2044,8 +2071,6 @@ static void zip(ulg time_stamp)
2044static 2071static
2045IF_DESKTOP(long long) int FAST_FUNC pack_gzip(transformer_state_t *xstate UNUSED_PARAM) 2072IF_DESKTOP(long long) int FAST_FUNC pack_gzip(transformer_state_t *xstate UNUSED_PARAM)
2046{ 2073{
2047 struct stat s;
2048
2049 /* Clear input and output buffers */ 2074 /* Clear input and output buffers */
2050 G1.outcnt = 0; 2075 G1.outcnt = 0;
2051#ifdef DEBUG 2076#ifdef DEBUG
@@ -2077,9 +2102,23 @@ IF_DESKTOP(long long) int FAST_FUNC pack_gzip(transformer_state_t *xstate UNUSED
2077 G2.bl_desc.max_length = MAX_BL_BITS; 2102 G2.bl_desc.max_length = MAX_BL_BITS;
2078 //G2.bl_desc.max_code = 0; 2103 //G2.bl_desc.max_code = 0;
2079 2104
2105#if 0
2106 /* Saving of timestamp is disabled. Why?
2107 * - it is not Y2038-safe.
2108 * - some people want deterministic results
2109 * (normally they'd use -n, but our -n is a nop).
2110 * - it's bloat.
2111 * Per RFC 1952, gzfile.time=0 is "no timestamp".
2112 * If users will demand this to be reinstated,
2113 * implement -n "don't save timestamp".
2114 */
2115 struct stat s;
2080 s.st_ctime = 0; 2116 s.st_ctime = 0;
2081 fstat(STDIN_FILENO, &s); 2117 fstat(STDIN_FILENO, &s);
2082 zip(s.st_ctime); 2118 zip(s.st_ctime);
2119#else
2120 zip();
2121#endif
2083 return 0; 2122 return 0;
2084} 2123}
2085 2124
diff --git a/archival/libarchive/Kbuild.src b/archival/libarchive/Kbuild.src
index 968fdf8ab..b7faaf77f 100644
--- a/archival/libarchive/Kbuild.src
+++ b/archival/libarchive/Kbuild.src
@@ -30,11 +30,13 @@ COMMON_FILES:= \
30DPKG_FILES:= \ 30DPKG_FILES:= \
31 unpack_ar_archive.o \ 31 unpack_ar_archive.o \
32 filter_accept_list_reassign.o \ 32 filter_accept_list_reassign.o \
33 unsafe_prefix.o \
33 get_header_ar.o \ 34 get_header_ar.o \
34 get_header_tar.o \ 35 get_header_tar.o \
35 get_header_tar_gz.o \ 36 get_header_tar_gz.o \
36 get_header_tar_bz2.o \ 37 get_header_tar_bz2.o \
37 get_header_tar_lzma.o \ 38 get_header_tar_lzma.o \
39 get_header_tar_xz.o \
38 40
39INSERT 41INSERT
40 42
@@ -43,7 +45,7 @@ lib-$(CONFIG_DPKG_DEB) += $(DPKG_FILES)
43 45
44lib-$(CONFIG_AR) += get_header_ar.o unpack_ar_archive.o 46lib-$(CONFIG_AR) += get_header_ar.o unpack_ar_archive.o
45lib-$(CONFIG_CPIO) += get_header_cpio.o 47lib-$(CONFIG_CPIO) += get_header_cpio.o
46lib-$(CONFIG_TAR) += get_header_tar.o 48lib-$(CONFIG_TAR) += get_header_tar.o unsafe_prefix.o
47lib-$(CONFIG_FEATURE_TAR_TO_COMMAND) += data_extract_to_command.o 49lib-$(CONFIG_FEATURE_TAR_TO_COMMAND) += data_extract_to_command.o
48lib-$(CONFIG_LZOP) += lzo1x_1.o lzo1x_1o.o lzo1x_d.o 50lib-$(CONFIG_LZOP) += lzo1x_1.o lzo1x_1o.o lzo1x_d.o
49lib-$(CONFIG_LZOP_COMPR_HIGH) += lzo1x_9x.o 51lib-$(CONFIG_LZOP_COMPR_HIGH) += lzo1x_9x.o
@@ -52,7 +54,7 @@ lib-$(CONFIG_UNLZMA) += open_transformer.o decompress_unlzma.
52lib-$(CONFIG_UNXZ) += open_transformer.o decompress_unxz.o 54lib-$(CONFIG_UNXZ) += open_transformer.o decompress_unxz.o
53lib-$(CONFIG_GUNZIP) += open_transformer.o decompress_gunzip.o 55lib-$(CONFIG_GUNZIP) += open_transformer.o decompress_gunzip.o
54lib-$(CONFIG_UNCOMPRESS) += open_transformer.o decompress_uncompress.o 56lib-$(CONFIG_UNCOMPRESS) += open_transformer.o decompress_uncompress.o
55lib-$(CONFIG_UNZIP) += open_transformer.o decompress_gunzip.o 57lib-$(CONFIG_UNZIP) += open_transformer.o decompress_gunzip.o unsafe_prefix.o
56lib-$(CONFIG_RPM2CPIO) += open_transformer.o decompress_gunzip.o get_header_cpio.o 58lib-$(CONFIG_RPM2CPIO) += open_transformer.o decompress_gunzip.o get_header_cpio.o
57lib-$(CONFIG_RPM) += open_transformer.o decompress_gunzip.o get_header_cpio.o 59lib-$(CONFIG_RPM) += open_transformer.o decompress_gunzip.o get_header_cpio.o
58 60
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)
28 name_ptr++; 28 name_ptr++;
29 29
30 /* Modify the subarchive handler based on the extension */ 30 /* Modify the subarchive handler based on the extension */
31 if (strcmp(name_ptr, "tar") == 0) {
32 archive_handle->dpkg__action_data_subarchive = get_header_tar;
33 return EXIT_SUCCESS;
34 }
31 if (ENABLE_FEATURE_SEAMLESS_GZ 35 if (ENABLE_FEATURE_SEAMLESS_GZ
32 && strcmp(name_ptr, "gz") == 0 36 && strcmp(name_ptr, "gz") == 0
33 ) { 37 ) {
@@ -46,6 +50,12 @@ char FAST_FUNC filter_accept_list_reassign(archive_handle_t *archive_handle)
46 archive_handle->dpkg__action_data_subarchive = get_header_tar_lzma; 50 archive_handle->dpkg__action_data_subarchive = get_header_tar_lzma;
47 return EXIT_SUCCESS; 51 return EXIT_SUCCESS;
48 } 52 }
53 if (ENABLE_FEATURE_SEAMLESS_XZ
54 && strcmp(name_ptr, "xz") == 0
55 ) {
56 archive_handle->dpkg__action_data_subarchive = get_header_tar_xz;
57 return EXIT_SUCCESS;
58 }
49 } 59 }
50 return EXIT_FAILURE; 60 return EXIT_FAILURE;
51} 61}
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)
37 } 37 }
38 archive_handle->offset += 110; 38 archive_handle->offset += 110;
39 39
40 if (strncmp(&cpio_header[0], "07070", 5) != 0 40 if (!is_prefixed_with(&cpio_header[0], "07070")
41 || (cpio_header[5] != '1' && cpio_header[5] != '2') 41 || (cpio_header[5] != '1' && cpio_header[5] != '2')
42 ) { 42 ) {
43 bb_error_msg_and_die("unsupported cpio format, use newc or crc"); 43 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 ba43bb073..2dbcdb50c 100644
--- a/archival/libarchive/get_header_tar.c
+++ b/archival/libarchive/get_header_tar.c
@@ -17,36 +17,6 @@
17typedef uint32_t aliased_uint32_t FIX_ALIASING; 17typedef uint32_t aliased_uint32_t FIX_ALIASING;
18typedef off_t aliased_off_t FIX_ALIASING; 18typedef off_t aliased_off_t FIX_ALIASING;
19 19
20
21const char* FAST_FUNC strip_unsafe_prefix(const char *str)
22{
23 const char *cp = str;
24 while (1) {
25 char *cp2;
26 if (*cp == '/') {
27 cp++;
28 continue;
29 }
30 if (strncmp(cp, "/../"+1, 3) == 0) {
31 cp += 3;
32 continue;
33 }
34 cp2 = strstr(cp, "/../");
35 if (!cp2)
36 break;
37 cp = cp2 + 4;
38 }
39 if (cp != str) {
40 static smallint warned = 0;
41 if (!warned) {
42 warned = 1;
43 bb_error_msg("removing leading '%.*s' from member names",
44 (int)(cp - str), str);
45 }
46 }
47 return cp;
48}
49
50/* NB: _DESTROYS_ str[len] character! */ 20/* NB: _DESTROYS_ str[len] character! */
51static unsigned long long getOctal(char *str, int len) 21static unsigned long long getOctal(char *str, int len)
52{ 22{
@@ -135,7 +105,7 @@ static void process_pax_hdr(archive_handle_t *archive_handle, unsigned sz, int g
135 value = end + 1; 105 value = end + 1;
136 106
137#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS 107#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
138 if (!global && strncmp(value, "path=", sizeof("path=") - 1) == 0) { 108 if (!global && is_prefixed_with(value, "path=")) {
139 value += sizeof("path=") - 1; 109 value += sizeof("path=") - 1;
140 free(archive_handle->tar__longname); 110 free(archive_handle->tar__longname);
141 archive_handle->tar__longname = xstrdup(value); 111 archive_handle->tar__longname = xstrdup(value);
@@ -148,7 +118,7 @@ static void process_pax_hdr(archive_handle_t *archive_handle, unsigned sz, int g
148 * This is what Red Hat's patched version of tar uses. 118 * This is what Red Hat's patched version of tar uses.
149 */ 119 */
150# define SELINUX_CONTEXT_KEYWORD "RHT.security.selinux" 120# define SELINUX_CONTEXT_KEYWORD "RHT.security.selinux"
151 if (strncmp(value, SELINUX_CONTEXT_KEYWORD"=", sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1) == 0) { 121 if (is_prefixed_with(value, SELINUX_CONTEXT_KEYWORD"=")) {
152 value += sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1; 122 value += sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1;
153 free(archive_handle->tar__sctx[global]); 123 free(archive_handle->tar__sctx[global]);
154 archive_handle->tar__sctx[global] = xstrdup(value); 124 archive_handle->tar__sctx[global] = xstrdup(value);
@@ -232,7 +202,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
232 202
233 /* Check header has valid magic, "ustar" is for the proper tar, 203 /* Check header has valid magic, "ustar" is for the proper tar,
234 * five NULs are for the old tar format */ 204 * five NULs are for the old tar format */
235 if (strncmp(tar.magic, "ustar", 5) != 0 205 if (!is_prefixed_with(tar.magic, "ustar")
236 && (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY 206 && (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
237 || memcmp(tar.magic, "\0\0\0\0", 5) != 0) 207 || memcmp(tar.magic, "\0\0\0\0", 5) != 0)
238 ) { 208 ) {
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4 */
5
6#include "libbb.h"
7#include "bb_archive.h"
8
9char FAST_FUNC get_header_tar_xz(archive_handle_t *archive_handle)
10{
11 /* Can't lseek over pipes */
12 archive_handle->seek = seek_by_read;
13
14 fork_transformer_with_sig(archive_handle->src_fd, unpack_xz_stream, "unxz");
15 archive_handle->offset = 0;
16 while (get_header_tar(archive_handle) == EXIT_SUCCESS)
17 continue;
18
19 /* Can only do one file at a time */
20 return EXIT_FAILURE;
21}
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)
12 char magic[7]; 12 char magic[7];
13 13
14 xread(ar_archive->src_fd, magic, AR_MAGIC_LEN); 14 xread(ar_archive->src_fd, magic, AR_MAGIC_LEN);
15 if (strncmp(magic, AR_MAGIC, AR_MAGIC_LEN) != 0) { 15 if (!is_prefixed_with(magic, AR_MAGIC)) {
16 bb_error_msg_and_die("invalid ar magic"); 16 bb_error_msg_and_die("invalid ar magic");
17 } 17 }
18 ar_archive->offset += AR_MAGIC_LEN; 18 ar_archive->offset += AR_MAGIC_LEN;
diff --git a/archival/libarchive/unsafe_prefix.c b/archival/libarchive/unsafe_prefix.c
new file mode 100644
index 000000000..9994f4d94
--- /dev/null
+++ b/archival/libarchive/unsafe_prefix.c
@@ -0,0 +1,36 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4 */
5
6#include "libbb.h"
7#include "bb_archive.h"
8
9const char* FAST_FUNC strip_unsafe_prefix(const char *str)
10{
11 const char *cp = str;
12 while (1) {
13 char *cp2;
14 if (*cp == '/') {
15 cp++;
16 continue;
17 }
18 if (is_prefixed_with(cp, "/../"+1)) {
19 cp += 3;
20 continue;
21 }
22 cp2 = strstr(cp, "/../");
23 if (!cp2)
24 break;
25 cp = cp2 + 4;
26 }
27 if (cp != str) {
28 static smallint warned = 0;
29 if (!warned) {
30 warned = 1;
31 bb_error_msg("removing leading '%.*s' from member names",
32 (int)(cp - str), str);
33 }
34 }
35 return cp;
36}
diff --git a/archival/unzip.c b/archival/unzip.c
index 1ef026a9f..d370203e8 100644
--- a/archival/unzip.c
+++ b/archival/unzip.c
@@ -599,14 +599,18 @@ int unzip_main(int argc, char **argv)
599 /* Skip extra header bytes */ 599 /* Skip extra header bytes */
600 unzip_skip(zip_header.formatted.extra_len); 600 unzip_skip(zip_header.formatted.extra_len);
601 601
602 /* Guard against "/abspath", "/../" and similar attacks */
603 overlapping_strcpy(dst_fn, strip_unsafe_prefix(dst_fn));
604
602 /* Filter zip entries */ 605 /* Filter zip entries */
603 if (find_list_entry(zreject, dst_fn) 606 if (find_list_entry(zreject, dst_fn)
604 || (zaccept && !find_list_entry(zaccept, dst_fn)) 607 || (zaccept && !find_list_entry(zaccept, dst_fn))
605 ) { /* Skip entry */ 608 ) { /* Skip entry */
606 i = 'n'; 609 i = 'n';
607 610
608 } else { /* Extract entry */ 611 } else {
609 if (listing) { /* List entry */ 612 if (listing) {
613 /* List entry */
610 unsigned dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16); 614 unsigned dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16);
611 if (!verbose) { 615 if (!verbose) {
612 // " Length Date Time Name\n" 616 // " Length Date Time Name\n"
@@ -642,9 +646,11 @@ int unzip_main(int argc, char **argv)
642 total_size += zip_header.formatted.cmpsize; 646 total_size += zip_header.formatted.cmpsize;
643 } 647 }
644 i = 'n'; 648 i = 'n';
645 } else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */ 649 } else if (dst_fd == STDOUT_FILENO) {
650 /* Extracting to STDOUT */
646 i = -1; 651 i = -1;
647 } else if (last_char_is(dst_fn, '/')) { /* Extract directory */ 652 } else if (last_char_is(dst_fn, '/')) {
653 /* Extract directory */
648 if (stat(dst_fn, &stat_buf) == -1) { 654 if (stat(dst_fn, &stat_buf) == -1) {
649 if (errno != ENOENT) { 655 if (errno != ENOENT) {
650 bb_perror_msg_and_die("can't stat '%s'", dst_fn); 656 bb_perror_msg_and_die("can't stat '%s'", dst_fn);
@@ -658,22 +664,27 @@ int unzip_main(int argc, char **argv)
658 } 664 }
659 } else { 665 } else {
660 if (!S_ISDIR(stat_buf.st_mode)) { 666 if (!S_ISDIR(stat_buf.st_mode)) {
661 bb_error_msg_and_die("'%s' exists but is not directory", dst_fn); 667 bb_error_msg_and_die("'%s' exists but is not a %s",
668 dst_fn, "directory");
662 } 669 }
663 } 670 }
664 i = 'n'; 671 i = 'n';
665 672
666 } else { /* Extract file */ 673 } else {
674 /* Extract file */
667 check_file: 675 check_file:
668 if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */ 676 if (stat(dst_fn, &stat_buf) == -1) {
677 /* File does not exist */
669 if (errno != ENOENT) { 678 if (errno != ENOENT) {
670 bb_perror_msg_and_die("can't stat '%s'", dst_fn); 679 bb_perror_msg_and_die("can't stat '%s'", dst_fn);
671 } 680 }
672 i = 'y'; 681 i = 'y';
673 } else { /* File already exists */ 682 } else {
683 /* File already exists */
674 if (overwrite == O_NEVER) { 684 if (overwrite == O_NEVER) {
675 i = 'n'; 685 i = 'n';
676 } else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */ 686 } else if (S_ISREG(stat_buf.st_mode)) {
687 /* File is regular file */
677 if (overwrite == O_ALWAYS) { 688 if (overwrite == O_ALWAYS) {
678 i = 'y'; 689 i = 'y';
679 } else { 690 } else {
@@ -681,8 +692,10 @@ int unzip_main(int argc, char **argv)
681 my_fgets80(key_buf); 692 my_fgets80(key_buf);
682 i = key_buf[0]; 693 i = key_buf[0];
683 } 694 }
684 } else { /* File is not regular file */ 695 } else {
685 bb_error_msg_and_die("'%s' exists but is not regular file", dst_fn); 696 /* File is not regular file */
697 bb_error_msg_and_die("'%s' exists but is not a %s",
698 dst_fn, "regular file");
686 } 699 }
687 } 700 }
688 } 701 }
diff --git a/console-tools/loadkmap.c b/console-tools/loadkmap.c
index 66ec3b043..f525ee5d1 100644
--- a/console-tools/loadkmap.c
+++ b/console-tools/loadkmap.c
@@ -10,8 +10,9 @@
10//usage:#define loadkmap_trivial_usage 10//usage:#define loadkmap_trivial_usage
11//usage: "< keymap" 11//usage: "< keymap"
12//usage:#define loadkmap_full_usage "\n\n" 12//usage:#define loadkmap_full_usage "\n\n"
13//usage: "Load a binary keyboard translation table from stdin\n" 13//usage: "Load a binary keyboard translation table from stdin"
14/* //usage: "\n -C TTY Affect TTY instead of /dev/tty" */ 14////usage: "\n"
15////usage: "\n -C TTY Affect TTY instead of /dev/tty"
15//usage: 16//usage:
16//usage:#define loadkmap_example_usage 17//usage:#define loadkmap_example_usage
17//usage: "$ loadkmap < /etc/i18n/lang-keymap\n" 18//usage: "$ loadkmap < /etc/i18n/lang-keymap\n"
@@ -56,7 +57,7 @@ int loadkmap_main(int argc UNUSED_PARAM, char **argv)
56*/ 57*/
57 58
58 xread(STDIN_FILENO, flags, 7); 59 xread(STDIN_FILENO, flags, 7);
59 if (strncmp(flags, BINARY_KEYMAP_MAGIC, 7)) 60 if (!is_prefixed_with(flags, BINARY_KEYMAP_MAGIC))
60 bb_error_msg_and_die("not a valid binary keymap"); 61 bb_error_msg_and_die("not a valid binary keymap");
61 62
62 xread(STDIN_FILENO, flags, MAX_NR_KEYMAPS); 63 xread(STDIN_FILENO, flags, MAX_NR_KEYMAPS);
diff --git a/coreutils/Config.src b/coreutils/Config.src
index 2914fc36a..f15e03990 100644
--- a/coreutils/Config.src
+++ b/coreutils/Config.src
@@ -87,44 +87,6 @@ config CUT
87 cut is used to print selected parts of lines from 87 cut is used to print selected parts of lines from
88 each file to stdout. 88 each file to stdout.
89 89
90config DD
91 bool "dd"
92 default y
93 help
94 dd copies a file (from standard input to standard output,
95 by default) using specific input and output blocksizes,
96 while optionally performing conversions on it.
97
98config FEATURE_DD_SIGNAL_HANDLING
99 bool "Enable DD signal handling for status reporting"
100 default y
101 depends on DD
102 help
103 Sending a SIGUSR1 signal to a running `dd' process makes it
104 print to standard error the number of records read and written
105 so far, then to resume copying.
106
107 $ dd if=/dev/zero of=/dev/null&
108 $ pid=$! kill -USR1 $pid; sleep 1; kill $pid
109 10899206+0 records in
110 10899206+0 records out
111
112config FEATURE_DD_THIRD_STATUS_LINE
113 bool "Enable the third status line upon signal"
114 default y
115 depends on DD && FEATURE_DD_SIGNAL_HANDLING
116 help
117 Displays a coreutils-like third status line with transferred bytes,
118 elapsed time and speed.
119
120config FEATURE_DD_IBS_OBS
121 bool "Enable ibs, obs and conv options"
122 default y
123 depends on DD
124 help
125 Enables support for writing a certain number of bytes in and out,
126 at a time, and performing conversions on the data stream.
127
128config DF 90config DF
129 bool "df" 91 bool "df"
130 default y 92 default y
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 @@
31//usage:#define basename_trivial_usage 31//usage:#define basename_trivial_usage
32//usage: "FILE [SUFFIX]" 32//usage: "FILE [SUFFIX]"
33//usage:#define basename_full_usage "\n\n" 33//usage:#define basename_full_usage "\n\n"
34//usage: "Strip directory path and .SUFFIX from FILE\n" 34//usage: "Strip directory path and .SUFFIX from FILE"
35//usage: 35//usage:
36//usage:#define basename_example_usage 36//usage:#define basename_example_usage
37//usage: "$ basename /usr/local/bin/foo\n" 37//usage: "$ basename /usr/local/bin/foo\n"
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)
373 date_buf[0] = '\0'; 373 date_buf[0] = '\0';
374 } else { 374 } else {
375 /* Handle special conversions */ 375 /* Handle special conversions */
376 if (strncmp(fmt_dt2str, "%f", 2) == 0) { 376 if (is_prefixed_with(fmt_dt2str, "%f")) {
377 fmt_dt2str = (char*)"%Y.%m.%d-%H:%M:%S"; 377 fmt_dt2str = (char*)"%Y.%m.%d-%H:%M:%S";
378 } 378 }
379 /* Generate output string */ 379 /* Generate output string */
diff --git a/coreutils/dd.c b/coreutils/dd.c
index db61f665e..e2d95931c 100644
--- a/coreutils/dd.c
+++ b/coreutils/dd.c
@@ -8,6 +8,51 @@
8 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 8 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
9 */ 9 */
10 10
11//config:config DD
12//config: bool "dd"
13//config: default y
14//config: help
15//config: dd copies a file (from standard input to standard output,
16//config: by default) using specific input and output blocksizes,
17//config: while optionally performing conversions on it.
18//config:
19//config:config FEATURE_DD_SIGNAL_HANDLING
20//config: bool "Enable signal handling for status reporting"
21//config: default y
22//config: depends on DD
23//config: help
24//config: Sending a SIGUSR1 signal to a running `dd' process makes it
25//config: print to standard error the number of records read and written
26//config: so far, then to resume copying.
27//config:
28//config: $ dd if=/dev/zero of=/dev/null &
29//config: $ pid=$!; kill -USR1 $pid; sleep 1; kill $pid
30//config: 10899206+0 records in
31//config: 10899206+0 records out
32//config:
33//config:config FEATURE_DD_THIRD_STATUS_LINE
34//config: bool "Enable the third status line upon signal"
35//config: default y
36//config: depends on DD && FEATURE_DD_SIGNAL_HANDLING
37//config: help
38//config: Displays a coreutils-like third status line with transferred bytes,
39//config: elapsed time and speed.
40//config:
41//config:config FEATURE_DD_IBS_OBS
42//config: bool "Enable ibs, obs and conv options"
43//config: default y
44//config: depends on DD
45//config: help
46//config: Enables support for writing a certain number of bytes in and out,
47//config: at a time, and performing conversions on the data stream.
48//config:
49//config:config FEATURE_DD_STATUS
50//config: bool "Enable status display options"
51//config: default y
52//config: depends on DD
53//config: help
54//config: Enables support for status=noxfer/none option.
55
11//usage:#define dd_trivial_usage 56//usage:#define dd_trivial_usage
12//usage: "[if=FILE] [of=FILE] " IF_FEATURE_DD_IBS_OBS("[ibs=N] [obs=N] ") "[bs=N] [count=N] [skip=N]\n" 57//usage: "[if=FILE] [of=FILE] " IF_FEATURE_DD_IBS_OBS("[ibs=N] [obs=N] ") "[bs=N] [count=N] [skip=N]\n"
13//usage: " [seek=N]" IF_FEATURE_DD_IBS_OBS(" [conv=notrunc|noerror|sync|fsync]") 58//usage: " [seek=N]" IF_FEATURE_DD_IBS_OBS(" [conv=notrunc|noerror|sync|fsync]")
@@ -32,8 +77,12 @@
32//usage: "\n conv=fsync Physically write data out before finishing" 77//usage: "\n conv=fsync Physically write data out before finishing"
33//usage: "\n conv=swab Swap every pair of bytes" 78//usage: "\n conv=swab Swap every pair of bytes"
34//usage: ) 79//usage: )
80//usage: IF_FEATURE_DD_STATUS(
81//usage: "\n status=noxfer Suppress rate output"
82//usage: "\n status=none Suppress all output"
83//usage: )
35//usage: "\n" 84//usage: "\n"
36//usage: "\nN may be suffixed by c (1), w (2), b (512), kD (1000), k (1024), MD, M, GD, G" 85//usage: "\nN may be suffixed by c (1), w (2), b (512), kB (1000), k (1024), MB, M, GB, G"
37//usage: 86//usage:
38//usage:#define dd_example_usage 87//usage:#define dd_example_usage
39//usage: "$ dd if=/dev/zero of=/dev/ram1 bs=1M count=4\n" 88//usage: "$ dd if=/dev/zero of=/dev/ram1 bs=1M count=4\n"
@@ -54,13 +103,18 @@ static const struct suffix_mult dd_suffixes[] = {
54 { "c", 1 }, 103 { "c", 1 },
55 { "w", 2 }, 104 { "w", 2 },
56 { "b", 512 }, 105 { "b", 512 },
106 { "kB", 1000 },
57 { "kD", 1000 }, 107 { "kD", 1000 },
58 { "k", 1024 }, 108 { "k", 1024 },
59 { "K", 1024 }, /* compat with coreutils dd */ 109 { "K", 1024 }, /* compat with coreutils dd (it also accepts KB and KD, TODO?) */
110 { "MB", 1000000 },
60 { "MD", 1000000 }, 111 { "MD", 1000000 },
61 { "M", 1048576 }, 112 { "M", 1024*1024 },
113 { "GB", 1000000000 },
62 { "GD", 1000000000 }, 114 { "GD", 1000000000 },
63 { "G", 1073741824 }, 115 { "G", 1024*1024*1024 },
116 /* "D" suffix for decimal is not in coreutils manpage, looks like it's deprecated */
117 /* coreutils also understands TPEZY suffixes for tera- and so on, with B suffix for decimal */
64 { "", 0 } 118 { "", 0 }
65}; 119};
66 120
@@ -70,6 +124,7 @@ struct globals {
70 unsigned long long total_bytes; 124 unsigned long long total_bytes;
71 unsigned long long begin_time_us; 125 unsigned long long begin_time_us;
72#endif 126#endif
127 int flags;
73} FIX_ALIASING; 128} FIX_ALIASING;
74#define G (*(struct globals*)&bb_common_bufsiz1) 129#define G (*(struct globals*)&bb_common_bufsiz1)
75#define INIT_G() do { \ 130#define INIT_G() do { \
@@ -77,6 +132,21 @@ struct globals {
77 memset(&G, 0, sizeof(G)); \ 132 memset(&G, 0, sizeof(G)); \
78} while (0) 133} while (0)
79 134
135enum {
136 /* Must be in the same order as OP_conv_XXX! */
137 /* (see "flags |= (1 << what)" below) */
138 FLAG_NOTRUNC = (1 << 0) * ENABLE_FEATURE_DD_IBS_OBS,
139 FLAG_SYNC = (1 << 1) * ENABLE_FEATURE_DD_IBS_OBS,
140 FLAG_NOERROR = (1 << 2) * ENABLE_FEATURE_DD_IBS_OBS,
141 FLAG_FSYNC = (1 << 3) * ENABLE_FEATURE_DD_IBS_OBS,
142 FLAG_SWAB = (1 << 4) * ENABLE_FEATURE_DD_IBS_OBS,
143 /* end of conv flags */
144 FLAG_TWOBUFS = (1 << 5) * ENABLE_FEATURE_DD_IBS_OBS,
145 FLAG_COUNT = 1 << 6,
146 FLAG_STATUS = 1 << 7,
147 FLAG_STATUS_NONE = 1 << 7,
148 FLAG_STATUS_NOXFER = 1 << 8,
149};
80 150
81static void dd_output_status(int UNUSED_PARAM cur_signal) 151static void dd_output_status(int UNUSED_PARAM cur_signal)
82{ 152{
@@ -93,6 +163,13 @@ static void dd_output_status(int UNUSED_PARAM cur_signal)
93 G.out_full, G.out_part); 163 G.out_full, G.out_part);
94 164
95#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE 165#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
166# if ENABLE_FEATURE_DD_STATUS
167 if (G.flags & FLAG_STATUS_NOXFER) /* status=noxfer active? */
168 return;
169 //TODO: should status=none make dd stop reacting to USR1 entirely?
170 //So far we react to it (we print the stats),
171 //status=none only suppresses final, non-USR1 generated status message.
172# endif
96 fprintf(stderr, "%llu bytes (%sB) copied, ", 173 fprintf(stderr, "%llu bytes (%sB) copied, ",
97 G.total_bytes, 174 G.total_bytes,
98 /* show fractional digit, use suffixes */ 175 /* show fractional digit, use suffixes */
@@ -148,20 +225,8 @@ static bool write_and_stats(const void *buf, size_t len, size_t obs,
148int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 225int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
149int dd_main(int argc UNUSED_PARAM, char **argv) 226int dd_main(int argc UNUSED_PARAM, char **argv)
150{ 227{
151 enum {
152 /* Must be in the same order as OP_conv_XXX! */
153 /* (see "flags |= (1 << what)" below) */
154 FLAG_NOTRUNC = (1 << 0) * ENABLE_FEATURE_DD_IBS_OBS,
155 FLAG_SYNC = (1 << 1) * ENABLE_FEATURE_DD_IBS_OBS,
156 FLAG_NOERROR = (1 << 2) * ENABLE_FEATURE_DD_IBS_OBS,
157 FLAG_FSYNC = (1 << 3) * ENABLE_FEATURE_DD_IBS_OBS,
158 FLAG_SWAB = (1 << 4) * ENABLE_FEATURE_DD_IBS_OBS,
159 /* end of conv flags */
160 FLAG_TWOBUFS = (1 << 5) * ENABLE_FEATURE_DD_IBS_OBS,
161 FLAG_COUNT = 1 << 6,
162 };
163 static const char keywords[] ALIGN1 = 228 static const char keywords[] ALIGN1 =
164 "bs\0""count\0""seek\0""skip\0""if\0""of\0" 229 "bs\0""count\0""seek\0""skip\0""if\0""of\0"IF_FEATURE_DD_STATUS("status\0")
165#if ENABLE_FEATURE_DD_IBS_OBS 230#if ENABLE_FEATURE_DD_IBS_OBS
166 "ibs\0""obs\0""conv\0" 231 "ibs\0""obs\0""conv\0"
167#endif 232#endif
@@ -170,6 +235,10 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
170 static const char conv_words[] ALIGN1 = 235 static const char conv_words[] ALIGN1 =
171 "notrunc\0""sync\0""noerror\0""fsync\0""swab\0"; 236 "notrunc\0""sync\0""noerror\0""fsync\0""swab\0";
172#endif 237#endif
238#if ENABLE_FEATURE_DD_STATUS
239 static const char status_words[] ALIGN1 =
240 "none\0""noxfer\0";
241#endif
173 enum { 242 enum {
174 OP_bs = 0, 243 OP_bs = 0,
175 OP_count, 244 OP_count,
@@ -177,6 +246,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
177 OP_skip, 246 OP_skip,
178 OP_if, 247 OP_if,
179 OP_of, 248 OP_of,
249 IF_FEATURE_DD_STATUS(OP_status,)
180#if ENABLE_FEATURE_DD_IBS_OBS 250#if ENABLE_FEATURE_DD_IBS_OBS
181 OP_ibs, 251 OP_ibs,
182 OP_obs, 252 OP_obs,
@@ -216,14 +286,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
216#endif 286#endif
217 /* These are all zeroed at once! */ 287 /* These are all zeroed at once! */
218 struct { 288 struct {
219 int flags;
220 size_t oc; 289 size_t oc;
221 ssize_t prev_read_size; /* for detecting swab failure */ 290 ssize_t prev_read_size; /* for detecting swab failure */
222 off_t count; 291 off_t count;
223 off_t seek, skip; 292 off_t seek, skip;
224 const char *infile, *outfile; 293 const char *infile, *outfile;
225 } Z; 294 } Z;
226#define flags (Z.flags )
227#define oc (Z.oc ) 295#define oc (Z.oc )
228#define prev_read_size (Z.prev_read_size) 296#define prev_read_size (Z.prev_read_size)
229#define count (Z.count ) 297#define count (Z.count )
@@ -279,7 +347,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
279 n = index_in_strings(conv_words, val); 347 n = index_in_strings(conv_words, val);
280 if (n < 0) 348 if (n < 0)
281 bb_error_msg_and_die(bb_msg_invalid_arg, val, "conv"); 349 bb_error_msg_and_die(bb_msg_invalid_arg, val, "conv");
282 flags |= (1 << n); 350 G.flags |= (1 << n);
283 if (!arg) /* no ',' left, so this was the last specifier */ 351 if (!arg) /* no ',' left, so this was the last specifier */
284 break; 352 break;
285 /* *arg = ','; - to preserve ps listing? */ 353 /* *arg = ','; - to preserve ps listing? */
@@ -295,7 +363,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
295 } 363 }
296 /* These can be large: */ 364 /* These can be large: */
297 if (what == OP_count) { 365 if (what == OP_count) {
298 flags |= FLAG_COUNT; 366 G.flags |= FLAG_COUNT;
299 count = XATOU_SFX(val, dd_suffixes); 367 count = XATOU_SFX(val, dd_suffixes);
300 /*continue;*/ 368 /*continue;*/
301 } 369 }
@@ -315,6 +383,16 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
315 outfile = val; 383 outfile = val;
316 /*continue;*/ 384 /*continue;*/
317 } 385 }
386#if ENABLE_FEATURE_DD_STATUS
387 if (what == OP_status) {
388 int n;
389 n = index_in_strings(status_words, val);
390 if (n < 0)
391 bb_error_msg_and_die(bb_msg_invalid_arg, val, "status");
392 G.flags |= FLAG_STATUS << n;
393 /*continue;*/
394 }
395#endif
318 } /* end of "for (argv[i])" */ 396 } /* end of "for (argv[i])" */
319 397
320//XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever 398//XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever
@@ -322,7 +400,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
322 obuf = ibuf; 400 obuf = ibuf;
323#if ENABLE_FEATURE_DD_IBS_OBS 401#if ENABLE_FEATURE_DD_IBS_OBS
324 if (ibs != obs) { 402 if (ibs != obs) {
325 flags |= FLAG_TWOBUFS; 403 G.flags |= FLAG_TWOBUFS;
326 obuf = xmalloc(obs); 404 obuf = xmalloc(obs);
327 } 405 }
328#endif 406#endif
@@ -347,12 +425,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
347 if (outfile) { 425 if (outfile) {
348 int oflag = O_WRONLY | O_CREAT; 426 int oflag = O_WRONLY | O_CREAT;
349 427
350 if (!seek && !(flags & FLAG_NOTRUNC)) 428 if (!seek && !(G.flags & FLAG_NOTRUNC))
351 oflag |= O_TRUNC; 429 oflag |= O_TRUNC;
352 430
353 xmove_fd(xopen(outfile, oflag), ofd); 431 xmove_fd(xopen(outfile, oflag), ofd);
354 432
355 if (seek && !(flags & FLAG_NOTRUNC)) { 433 if (seek && !(G.flags & FLAG_NOTRUNC)) {
356 if (ftruncate(ofd, seek * obs) < 0) { 434 if (ftruncate(ofd, seek * obs) < 0) {
357 struct stat st; 435 struct stat st;
358 436
@@ -383,7 +461,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
383 goto die_outfile; 461 goto die_outfile;
384 } 462 }
385 463
386 while (!(flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) { 464 while (!(G.flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) {
387 ssize_t n; 465 ssize_t n;
388 466
389 if (devzero) { 467 if (devzero) {
@@ -396,7 +474,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
396 break; 474 break;
397 if (n < 0) { 475 if (n < 0) {
398 /* "Bad block" */ 476 /* "Bad block" */
399 if (!(flags & FLAG_NOERROR)) 477 if (!(G.flags & FLAG_NOERROR))
400 goto die_infile; 478 goto die_infile;
401 bb_simple_perror_msg(infile); 479 bb_simple_perror_msg(infile);
402 /* GNU dd with conv=noerror skips over bad blocks */ 480 /* GNU dd with conv=noerror skips over bad blocks */
@@ -405,7 +483,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
405 * conv=noerror just ignores input bad blocks */ 483 * conv=noerror just ignores input bad blocks */
406 n = 0; 484 n = 0;
407 } 485 }
408 if (flags & FLAG_SWAB) { 486 if (G.flags & FLAG_SWAB) {
409 uint16_t *p16; 487 uint16_t *p16;
410 ssize_t n2; 488 ssize_t n2;
411 489
@@ -430,12 +508,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
430 G.in_full++; 508 G.in_full++;
431 else { 509 else {
432 G.in_part++; 510 G.in_part++;
433 if (flags & FLAG_SYNC) { 511 if (G.flags & FLAG_SYNC) {
434 memset(ibuf + n, 0, ibs - n); 512 memset(ibuf + n, 0, ibs - n);
435 n = ibs; 513 n = ibs;
436 } 514 }
437 } 515 }
438 if (flags & FLAG_TWOBUFS) { 516 if (G.flags & FLAG_TWOBUFS) {
439 char *tmp = ibuf; 517 char *tmp = ibuf;
440 while (n) { 518 while (n) {
441 size_t d = obs - oc; 519 size_t d = obs - oc;
@@ -457,7 +535,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
457 goto out_status; 535 goto out_status;
458 } 536 }
459 537
460 if (flags & FLAG_FSYNC) { 538 if (G.flags & FLAG_FSYNC) {
461 if (fsync(ofd) < 0) 539 if (fsync(ofd) < 0)
462 goto die_outfile; 540 goto die_outfile;
463 } 541 }
@@ -480,11 +558,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
480 558
481 exitcode = EXIT_SUCCESS; 559 exitcode = EXIT_SUCCESS;
482 out_status: 560 out_status:
483 dd_output_status(0); 561 if (!ENABLE_FEATURE_DD_STATUS || !(G.flags & FLAG_STATUS_NONE))
562 dd_output_status(0);
484 563
485 if (ENABLE_FEATURE_CLEAN_UP) { 564 if (ENABLE_FEATURE_CLEAN_UP) {
486 free(obuf); 565 free(obuf);
487 if (flags & FLAG_TWOBUFS) 566 if (G.flags & FLAG_TWOBUFS)
488 free(ibuf); 567 free(ibuf);
489 } 568 }
490 569
diff --git a/coreutils/ls.c b/coreutils/ls.c
index 1b63be56d..14c8beaff 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -566,12 +566,12 @@ static NOINLINE unsigned display_single(const struct dnode *dn)
566#if ENABLE_FEATURE_LS_TIMESTAMPS 566#if ENABLE_FEATURE_LS_TIMESTAMPS
567 if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) { 567 if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
568 char *filetime; 568 char *filetime;
569 time_t ttime = dn->dn_mtime; 569 const time_t *ttime = &dn->dn_mtime;
570 if (G.all_fmt & TIME_ACCESS) 570 if (G.all_fmt & TIME_ACCESS)
571 ttime = dn->dn_atime; 571 ttime = &dn->dn_atime;
572 if (G.all_fmt & TIME_CHANGE) 572 if (G.all_fmt & TIME_CHANGE)
573 ttime = dn->dn_ctime; 573 ttime = &dn->dn_ctime;
574 filetime = ctime(&ttime); 574 filetime = ctime(ttime);
575 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */ 575 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
576 if (G.all_fmt & LIST_FULLTIME) { /* -e */ 576 if (G.all_fmt & LIST_FULLTIME) { /* -e */
577 /* Note: coreutils 8.4 ls --full-time prints: 577 /* Note: coreutils 8.4 ls --full-time prints:
@@ -580,13 +580,16 @@ static NOINLINE unsigned display_single(const struct dnode *dn)
580 column += printf("%.24s ", filetime); 580 column += printf("%.24s ", filetime);
581 } else { /* LIST_DATE_TIME */ 581 } else { /* LIST_DATE_TIME */
582 /* G.current_time_t ~== time(NULL) */ 582 /* G.current_time_t ~== time(NULL) */
583 time_t age = G.current_time_t - ttime; 583 time_t age = G.current_time_t - *ttime;
584 printf("%.6s ", filetime + 4); /* "Jun 30" */
585 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { 584 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
586 /* hh:mm if less than 6 months old */ 585 /* less than 6 months old */
587 printf("%.5s ", filetime + 11); 586 /* "mmm dd hh:mm " */
588 } else { /* year. buggy if year > 9999 ;) */ 587 printf("%.12s ", filetime + 4);
589 printf(" %.4s ", filetime + 20); 588 } else {
589 /* "mmm dd yyyy " */
590 /* "mmm dd yyyyy " after year 9999 :) */
591 strchr(filetime + 20, '\n')[0] = ' ';
592 printf("%.7s%6s", filetime + 4, filetime + 20);
590 } 593 }
591 column += 13; 594 column += 13;
592 } 595 }
diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c
index 34ceefb1c..1e7c5bfa0 100644
--- a/coreutils/od_bloaty.c
+++ b/coreutils/od_bloaty.c
@@ -395,10 +395,10 @@ print_named_ascii(size_t n_bytes, const char *block,
395 }; 395 };
396 // buf[N] pos: 01234 56789 396 // buf[N] pos: 01234 56789
397 char buf[12] = " x\0 xxx\0"; 397 char buf[12] = " x\0 xxx\0";
398 // share string with print_ascii.
399 // [12] because we take three 32bit stack slots anyway, and 398 // [12] because we take three 32bit stack slots anyway, and
400 // gcc is too dumb to initialize with constant stores, 399 // gcc is too dumb to initialize with constant stores,
401 // it copies initializer from rodata. Oh well. 400 // it copies initializer from rodata. Oh well.
401 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65410
402 402
403 while (n_bytes--) { 403 while (n_bytes--) {
404 unsigned masked_c = *(unsigned char *) block++; 404 unsigned masked_c = *(unsigned char *) block++;
@@ -462,9 +462,6 @@ print_ascii(size_t n_bytes, const char *block,
462 case '\v': 462 case '\v':
463 s = " \\v"; 463 s = " \\v";
464 break; 464 break;
465 case '\x7f':
466 s = " 177";
467 break;
468 default: 465 default:
469 buf[6] = (c >> 6 & 3) + '0'; 466 buf[6] = (c >> 6 & 3) + '0';
470 buf[7] = (c >> 3 & 7) + '0'; 467 buf[7] = (c >> 3 & 7) + '0';
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)
110 FILE *dst_stream; 110 FILE *dst_stream;
111 int mode; 111 int mode;
112 112
113 if (strncmp(line, "begin-base64 ", 13) == 0) { 113 if (is_prefixed_with(line, "begin-base64 ")) {
114 line_ptr = line + 13; 114 line_ptr = line + 13;
115 decode_fn_ptr = read_base64; 115 decode_fn_ptr = read_base64;
116 } else if (strncmp(line, "begin ", 6) == 0) { 116 } else if (is_prefixed_with(line, "begin ")) {
117 line_ptr = line + 6; 117 line_ptr = line + 6;
118 decode_fn_ptr = read_stduu; 118 decode_fn_ptr = read_stduu;
119 } else { 119 } 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)
199 } 199 }
200 200
201 /* Handle DAC 960 devices */ 201 /* Handle DAC 960 devices */
202 if (strncmp(cp, "rd/", 3) == 0) { 202 if (is_prefixed_with(cp, "rd/")) {
203 cp += 3; 203 cp += 3;
204 if (cp[0] != 'c' || !isdigit(cp[1]) 204 if (cp[0] != 'c' || !isdigit(cp[1])
205 || cp[2] != 'd' || !isdigit(cp[3])) 205 || cp[2] != 'd' || !isdigit(cp[3]))
@@ -224,9 +224,9 @@ static char *base_device(const char *device)
224#if ENABLE_FEATURE_DEVFS 224#if ENABLE_FEATURE_DEVFS
225 /* Now let's handle devfs (ugh) names */ 225 /* Now let's handle devfs (ugh) names */
226 len = 0; 226 len = 0;
227 if (strncmp(cp, "ide/", 4) == 0) 227 if (is_prefixed_with(cp, "ide/"))
228 len = 4; 228 len = 4;
229 if (strncmp(cp, "scsi/", 5) == 0) 229 if (is_prefixed_with(cp, "scsi/"))
230 len = 5; 230 len = 5;
231 if (len) { 231 if (len) {
232 cp += len; 232 cp += len;
@@ -237,38 +237,38 @@ static char *base_device(const char *device)
237 * some number of digits at each level, abort. 237 * some number of digits at each level, abort.
238 */ 238 */
239 for (hier = devfs_hier; *hier; hier++) { 239 for (hier = devfs_hier; *hier; hier++) {
240 len = strlen(*hier); 240 cp = is_prefixed_with(cp, *hier);
241 if (strncmp(cp, *hier, len) != 0) 241 if (!cp)
242 goto errout; 242 goto errout;
243 cp += len; 243 while (*cp != '/' && *cp != '\0') {
244 while (*cp != '/' && *cp != 0) {
245 if (!isdigit(*cp)) 244 if (!isdigit(*cp))
246 goto errout; 245 goto errout;
247 cp++; 246 cp++;
248 } 247 }
248//FIXME: what if *cp = '\0' now? cp++ moves past it!!!
249 cp++; 249 cp++;
250 } 250 }
251 cp[-1] = 0; 251 cp[-1] = '\0';
252 return str; 252 return str;
253 } 253 }
254 254
255 /* Now handle devfs /dev/disc or /dev/disk names */ 255 /* Now handle devfs /dev/disc or /dev/disk names */
256 disk = 0; 256 disk = NULL;
257 if (strncmp(cp, "discs/", 6) == 0) 257 if (is_prefixed_with(cp, "discs/"))
258 disk = "disc"; 258 disk = "disc";
259 else if (strncmp(cp, "disks/", 6) == 0) 259 else if (is_prefixed_with(cp, "disks/"))
260 disk = "disk"; 260 disk = "disk";
261 if (disk) { 261 if (disk) {
262 cp += 6; 262 cp += 6;
263 if (strncmp(cp, disk, 4) != 0) 263 cp = is_prefixed_with(cp, disk);
264 if (!cp)
264 goto errout; 265 goto errout;
265 cp += 4; 266 while (*cp != '/' && *cp != '\0') {
266 while (*cp != '/' && *cp != 0) {
267 if (!isdigit(*cp)) 267 if (!isdigit(*cp))
268 goto errout; 268 goto errout;
269 cp++; 269 cp++;
270 } 270 }
271 *cp = 0; 271 *cp = '\0';
272 return str; 272 return str;
273 } 273 }
274#endif 274#endif
@@ -593,8 +593,8 @@ static void fsck_device(struct fs_info *fs /*, int interactive */)
593 type, "from fstab"); 593 type, "from fstab");
594 } else if (fstype 594 } else if (fstype
595 && (fstype[0] != 'n' || fstype[1] != 'o') /* != "no" */ 595 && (fstype[0] != 'n' || fstype[1] != 'o') /* != "no" */
596 && strncmp(fstype, "opts=", 5) != 0 596 && !is_prefixed_with(fstype, "opts=")
597 && strncmp(fstype, "loop", 4) != 0 597 && !is_prefixed_with(fstype, "loop")
598 && !strchr(fstype, ',') 598 && !strchr(fstype, ',')
599 ) { 599 ) {
600 type = fstype; 600 type = fstype;
@@ -627,8 +627,8 @@ static int device_already_active(char *device)
627#ifdef BASE_MD 627#ifdef BASE_MD
628 /* Don't check a soft raid disk with any other disk */ 628 /* Don't check a soft raid disk with any other disk */
629 if (instance_list 629 if (instance_list
630 && (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1) 630 && (is_prefixed_with(instance_list->device, BASE_MD)
631 || !strncmp(device, BASE_MD, sizeof(BASE_MD)-1)) 631 || is_prefixed_with(device, BASE_MD))
632 ) { 632 ) {
633 return 1; 633 return 1;
634 } 634 }
@@ -895,7 +895,7 @@ static void compile_fs_type(char *fs_type)
895 if (strcmp(s, "loop") == 0) 895 if (strcmp(s, "loop") == 0)
896 /* loop is really short-hand for opts=loop */ 896 /* loop is really short-hand for opts=loop */
897 goto loop_special_case; 897 goto loop_special_case;
898 if (strncmp(s, "opts=", 5) == 0) { 898 if (is_prefixed_with(s, "opts=")) {
899 s += 5; 899 s += 5;
900 loop_special_case: 900 loop_special_case:
901 fs_type_flag[num] = negate ? FS_TYPE_FLAG_NEGOPT : FS_TYPE_FLAG_OPT; 901 fs_type_flag[num] = negate ? FS_TYPE_FLAG_NEGOPT : FS_TYPE_FLAG_OPT;
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])
740 unlink(name); 740 unlink(name);
741 if (bb_copyfd_eof(fd, fd_tmp) < 0) 741 if (bb_copyfd_eof(fd, fd_tmp) < 0)
742 xfunc_die(); 742 xfunc_die();
743 if (fd) /* Prevents closing of stdin */ 743 if (fd != STDIN_FILENO)
744 close(fd); 744 close(fd);
745 fd = fd_tmp; 745 fd = fd_tmp;
746 xlseek(fd, 0, SEEK_SET);
746 } 747 }
747 fp[i] = fdopen(fd, "r"); 748 fp[i] = fdopen(fd, "r");
748 } 749 }
diff --git a/editors/patch.c b/editors/patch.c
index 13785ef46..cb25e4140 100644
--- a/editors/patch.c
+++ b/editors/patch.c
@@ -345,6 +345,8 @@ done:
345// state 1: Found +++ file indicator, look for @@ 345// state 1: Found +++ file indicator, look for @@
346// state 2: In hunk: counting initial context lines 346// state 2: In hunk: counting initial context lines
347// state 3: In hunk: getting body 347// state 3: In hunk: getting body
348// Like GNU patch, we don't require a --- line before the +++, and
349// also allow the --- after the +++ line.
348 350
349int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 351int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
350int patch_main(int argc UNUSED_PARAM, char **argv) 352int patch_main(int argc UNUSED_PARAM, char **argv)
@@ -412,7 +414,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
412 } 414 }
413 415
414 // Open a new file? 416 // Open a new file?
415 if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) { 417 if (is_prefixed_with(patchline, "--- ") || is_prefixed_with(patchline, "+++ ")) {
416 char *s, **name = reverse ? &newname : &oldname; 418 char *s, **name = reverse ? &newname : &oldname;
417 int i; 419 int i;
418 420
@@ -444,7 +446,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
444 446
445 // Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@ 447 // Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@
446 // but a missing ,value means the value is 1. 448 // but a missing ,value means the value is 1.
447 } else if (state == 1 && !strncmp("@@ -", patchline, 4)) { 449 } else if (state == 1 && is_prefixed_with(patchline, "@@ -")) {
448 int i; 450 int i;
449 char *s = patchline+4; 451 char *s = patchline+4;
450 452
@@ -462,6 +464,14 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
462 TT.context = 0; 464 TT.context = 0;
463 state = 2; 465 state = 2;
464 466
467 // If the --- line is missing or malformed, either oldname
468 // or (for -R) newname could be NULL -- but not both. Like
469 // GNU patch, proceed based on the +++ line, and avoid SEGVs.
470 if (!oldname)
471 oldname = xstrdup("MISSING_FILENAME");
472 if (!newname)
473 newname = xstrdup("MISSING_FILENAME");
474
465 // If this is the first hunk, open the file. 475 // If this is the first hunk, open the file.
466 if (TT.filein == -1) { 476 if (TT.filein == -1) {
467 int oldsum, newsum, empty = 0; 477 int oldsum, newsum, empty = 0;
diff --git a/editors/vi.c b/editors/vi.c
index 1fa97b568..926aef19b 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'
2017 p--; 2017 p--;
2018 } 2018 }
2019 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS 2019 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
2020 // 123456789 2020 if (p > text) {
2021 if ((p[-1] != '\n') && (dot>text)) {
2022 p--; 2021 p--;
2023 p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED); // shrink buffer 1 char 2022 p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED); // shrink buffer 1 char
2024 } 2023 }
@@ -4073,8 +4072,9 @@ static void do_cmd(int c)
4073 undo_queue_commit(); 4072 undo_queue_commit();
4074 break; 4073 break;
4075 case KEYCODE_DELETE: 4074 case KEYCODE_DELETE:
4076 c = 'x'; 4075 if (dot < end - 1)
4077 // fall through 4076 dot = yank_delete(dot, dot, 1, YANKDEL, ALLOW_UNDO);
4077 break;
4078 case 'X': // X- delete char before dot 4078 case 'X': // X- delete char before dot
4079 case 'x': // x- delete the current char 4079 case 'x': // x- delete the current char
4080 case 's': // s- substitute the current char 4080 case 's': // s- substitute the current char
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 @@
19# support module loading on hotplug 19# support module loading on hotplug
20$MODALIAS=.* root:root 660 @modprobe "$MODALIAS" 20$MODALIAS=.* root:root 660 @modprobe "$MODALIAS"
21 21
22# null may already exist; therefore ownership has to be changed with command 22# null may already exist; therefore mode has to be changed with command
23null root:root 666 @chmod 666 $MDEV 23null root:root 666 @chmod 666 $MDEV
24zero root:root 666 24zero root:root 666
25full root:root 666 25full root:root 666
@@ -31,7 +31,7 @@ grsec root:root 660
31kmem root:root 640 31kmem root:root 640
32mem root:root 640 32mem root:root 640
33port root:root 640 33port root:root 640
34# console may already exist; therefore ownership has to be changed with command 34# console may already exist; therefore mode has to be changed with command
35console root:tty 600 @chmod 600 $MDEV 35console root:tty 600 @chmod 600 $MDEV
36ptmx root:tty 666 36ptmx root:tty 666
37pty.* root:tty 660 37pty.* root:tty 660
@@ -63,6 +63,12 @@ control.* root:audio 660 =snd/
63midi.* root:audio 660 =snd/ 63midi.* root:audio 660 =snd/
64seq root:audio 660 =snd/ 64seq root:audio 660 =snd/
65timer root:audio 660 =snd/ 65timer root:audio 660 =snd/
66# for kernels/mdevs which expose full path of alsa devices:
67snd/pcm.* root:audio 660
68snd/control.* root:audio 660
69snd/midi.* root:audio 660
70snd/seq root:audio 660
71snd/timer root:audio 660
66 72
67adsp root:audio 660 >sound/ 73adsp root:audio 660 >sound/
68audio root:audio 660 >sound/ 74audio root:audio 660 >sound/
@@ -139,6 +145,6 @@ dahdi!(.*) root:dialout 660 =dahdi/%1
139# We are hooking to the last events. To avoid having two scripts running, 145# We are hooking to the last events. To avoid having two scripts running,
140# we check for DISK_MEDIA_CHANGE=1 (prev to last event has it, 146# we check for DISK_MEDIA_CHANGE=1 (prev to last event has it,
141# and it's the _only_ difference in env for these two events as of kernel 3.7.7) 147# and it's the _only_ difference in env for these two events as of kernel 3.7.7)
142# Unfortunately, there is no event for "user pressed [Turn USB storage] btn"! 148# Unfortunately, there is no event for "user pressed [Turn USB storage on] btn"!
143# Script merely backgrounds and tries to rescan partition table for 1 minute: 149# Script merely backgrounds and tries to rescan partition table for 1 minute:
144ACTION=change;SUBSYSTEM=block;DISK_MEDIA_CHANGE=1;.* 0:0 660 */etc/mdev.conf.change_blockdev.sh 150ACTION=change;SUBSYSTEM=block;DISK_MEDIA_CHANGE=1;.* 0:0 660 */etc/mdev.conf.change_blockdev.sh
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;
184char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC; 184char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC;
185char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC; 185char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC;
186char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC; 186char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC;
187char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC;
187 188
188void seek_by_jump(int fd, off_t amount) FAST_FUNC; 189void seek_by_jump(int fd, off_t amount) FAST_FUNC;
189void seek_by_read(int fd, off_t amount) FAST_FUNC; 190void seek_by_read(int fd, off_t amount) FAST_FUNC;
diff --git a/include/grp_.h b/include/grp_.h
index e5075e5a0..db13ce3b4 100644
--- a/include/grp_.h
+++ b/include/grp_.h
@@ -30,89 +30,36 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
30 * so that function calls are directed to bb_internal_XXX replacements 30 * so that function calls are directed to bb_internal_XXX replacements
31 */ 31 */
32#undef endgrent 32#undef endgrent
33#define setgrent bb_internal_setgrent
34#define endgrent bb_internal_endgrent 33#define endgrent bb_internal_endgrent
35#define getgrent bb_internal_getgrent
36#define fgetgrent bb_internal_fgetgrent
37#define putgrent bb_internal_putgrent
38#define getgrgid bb_internal_getgrgid 34#define getgrgid bb_internal_getgrgid
39#define getgrnam bb_internal_getgrnam 35#define getgrnam bb_internal_getgrnam
40#define getgrent_r bb_internal_getgrent_r
41#define getgrgid_r bb_internal_getgrgid_r
42#define getgrnam_r bb_internal_getgrnam_r
43#define fgetgrent_r bb_internal_fgetgrent_r
44#define getgrouplist bb_internal_getgrouplist 36#define getgrouplist bb_internal_getgrouplist
45#define initgroups bb_internal_initgroups 37#define initgroups bb_internal_initgroups
46 38
47
48/* All function names below should be remapped by #defines above 39/* All function names below should be remapped by #defines above
49 * in order to not collide with libc names. */ 40 * in order to not collide with libc names. */
50 41
51
52/* Rewind the group-file stream. */
53extern void setgrent(void);
54
55/* Close the group-file stream. */ 42/* Close the group-file stream. */
56extern void endgrent(void); 43void FAST_FUNC endgrent(void);
57
58#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
59/* Read an entry from the group-file stream, opening it if necessary. */
60extern struct group *getgrent(void);
61
62/* Read a group entry from STREAM. */
63extern struct group *fgetgrent(FILE *__stream);
64
65/* Write the given entry onto the given stream. */
66extern int putgrent(const struct group *__restrict __p,
67 FILE *__restrict __f);
68#endif
69
70/* Search for an entry with a matching group ID. */
71extern struct group *getgrgid(gid_t __gid);
72
73/* Search for an entry with a matching group name. */
74extern struct group *getgrnam(const char *__name);
75
76/* Reentrant versions of some of the functions above.
77
78 PLEASE NOTE: the `getgrent_r' function is not (yet) standardized.
79 The interface may change in later versions of this library. But
80 the interface is designed following the principals used for the
81 other reentrant functions so the chances are good this is what the
82 POSIX people would choose. */
83
84extern int getgrent_r(struct group *__restrict __resultbuf,
85 char *__restrict __buffer, size_t __buflen,
86 struct group **__restrict __result);
87 44
88/* Search for an entry with a matching group ID. */ 45/* Search for an entry with a matching group ID. */
89extern int getgrgid_r(gid_t __gid, struct group *__restrict __resultbuf, 46struct group* FAST_FUNC getgrgid(gid_t __gid);
90 char *__restrict __buffer, size_t __buflen,
91 struct group **__restrict __result);
92 47
93/* Search for an entry with a matching group name. */ 48/* Search for an entry with a matching group name. */
94extern int getgrnam_r(const char *__restrict __name, 49struct group* FAST_FUNC getgrnam(const char *__name);
95 struct group *__restrict __resultbuf,
96 char *__restrict __buffer, size_t __buflen,
97 struct group **__restrict __result);
98 50
99/* Read a group entry from STREAM. This function is not standardized 51/* Reentrant versions of some of the functions above. */
100 an probably never will. */
101extern int fgetgrent_r(FILE *__restrict __stream,
102 struct group *__restrict __resultbuf,
103 char *__restrict __buffer, size_t __buflen,
104 struct group **__restrict __result);
105 52
106/* Store at most *NGROUPS members of the group set for USER into 53/* Store at most *NGROUPS members of the group set for USER into
107 *GROUPS. Also include GROUP. The actual number of groups found is 54 *GROUPS. Also include GROUP. The actual number of groups found is
108 returned in *NGROUPS. Return -1 if the if *NGROUPS is too small. */ 55 returned in *NGROUPS. Return -1 if the if *NGROUPS is too small. */
109extern int getgrouplist(const char *__user, gid_t __group, 56int FAST_FUNC getgrouplist(const char *__user, gid_t __group,
110 gid_t *__groups, int *__ngroups); 57 gid_t *__groups, int *__ngroups);
111 58
112/* Initialize the group set for the current user 59/* Initialize the group set for the current user
113 by reading the group database and using all groups 60 by reading the group database and using all groups
114 of which USER is a member. Also include GROUP. */ 61 of which USER is a member. Also include GROUP. */
115extern int initgroups(const char *__user, gid_t __group); 62int FAST_FUNC initgroups(const char *__user, gid_t __group);
116 63
117POP_SAVED_FUNCTION_VISIBILITY 64POP_SAVED_FUNCTION_VISIBILITY
118 65
diff --git a/include/libbb.h b/include/libbb.h
index 2850b1d5a..1733e9a0f 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -402,6 +402,7 @@ const char *bb_basename(const char *name) FAST_FUNC;
402/* NB: can violate const-ness (similarly to strchr) */ 402/* NB: can violate const-ness (similarly to strchr) */
403char *last_char_is(const char *s, int c) FAST_FUNC; 403char *last_char_is(const char *s, int c) FAST_FUNC;
404const char* endofname(const char *name) FAST_FUNC; 404const char* endofname(const char *name) FAST_FUNC;
405char *is_prefixed_with(const char *string, const char *key) FAST_FUNC;
405 406
406int ndelay_on(int fd) FAST_FUNC; 407int ndelay_on(int fd) FAST_FUNC;
407int ndelay_off(int fd) FAST_FUNC; 408int ndelay_off(int fd) FAST_FUNC;
@@ -937,9 +938,11 @@ void die_if_bad_username(const char* name) FAST_FUNC;
937#if ENABLE_FEATURE_UTMP 938#if ENABLE_FEATURE_UTMP
938void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); 939void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname);
939void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); 940void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname);
941void FAST_FUNC update_utmp_DEAD_PROCESS(pid_t pid);
940#else 942#else
941# define write_new_utmp(pid, new_type, tty_name, username, hostname) ((void)0) 943# define write_new_utmp(pid, new_type, tty_name, username, hostname) ((void)0)
942# define update_utmp(pid, new_type, tty_name, username, hostname) ((void)0) 944# define update_utmp(pid, new_type, tty_name, username, hostname) ((void)0)
945# define update_utmp_DEAD_PROCESS(pid) ((void)0)
943#endif 946#endif
944 947
945 948
diff --git a/include/platform.h b/include/platform.h
index e2b61592e..a550feae6 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -234,6 +234,7 @@ typedef uint64_t bb__aliased_uint64_t FIX_ALIASING;
234 * a lvalue. This makes it more likely to not swap them by mistake 234 * a lvalue. This makes it more likely to not swap them by mistake
235 */ 235 */
236#if defined(i386) || defined(__x86_64__) || defined(__powerpc__) 236#if defined(i386) || defined(__x86_64__) || defined(__powerpc__)
237# define BB_UNALIGNED_MEMACCESS_OK 1
237# define move_from_unaligned_int(v, intp) ((v) = *(bb__aliased_int*)(intp)) 238# define move_from_unaligned_int(v, intp) ((v) = *(bb__aliased_int*)(intp))
238# define move_from_unaligned_long(v, longp) ((v) = *(bb__aliased_long*)(longp)) 239# define move_from_unaligned_long(v, longp) ((v) = *(bb__aliased_long*)(longp))
239# define move_from_unaligned16(v, u16p) ((v) = *(bb__aliased_uint16_t*)(u16p)) 240# define move_from_unaligned16(v, u16p) ((v) = *(bb__aliased_uint16_t*)(u16p))
@@ -242,6 +243,7 @@ typedef uint64_t bb__aliased_uint64_t FIX_ALIASING;
242# define move_to_unaligned32(u32p, v) (*(bb__aliased_uint32_t*)(u32p) = (v)) 243# define move_to_unaligned32(u32p, v) (*(bb__aliased_uint32_t*)(u32p) = (v))
243/* #elif ... - add your favorite arch today! */ 244/* #elif ... - add your favorite arch today! */
244#else 245#else
246# define BB_UNALIGNED_MEMACCESS_OK 0
245/* performs reasonably well (gcc usually inlines memcpy here) */ 247/* performs reasonably well (gcc usually inlines memcpy here) */
246# define move_from_unaligned_int(v, intp) (memcpy(&(v), (intp), sizeof(int))) 248# define move_from_unaligned_int(v, intp) (memcpy(&(v), (intp), sizeof(int)))
247# define move_from_unaligned_long(v, longp) (memcpy(&(v), (longp), sizeof(long))) 249# define move_from_unaligned_long(v, longp) (memcpy(&(v), (longp), sizeof(long)))
@@ -387,6 +389,7 @@ typedef unsigned smalluint;
387#define HAVE_SETBIT 1 389#define HAVE_SETBIT 1
388#define HAVE_SIGHANDLER_T 1 390#define HAVE_SIGHANDLER_T 1
389#define HAVE_STPCPY 1 391#define HAVE_STPCPY 1
392#define HAVE_MEMPCPY 1
390#define HAVE_STRCASESTR 1 393#define HAVE_STRCASESTR 1
391#define HAVE_STRCHRNUL 1 394#define HAVE_STRCHRNUL 1
392#define HAVE_STRSEP 1 395#define HAVE_STRSEP 1
@@ -486,6 +489,8 @@ typedef unsigned smalluint;
486#endif 489#endif
487 490
488#if defined(__FreeBSD__) 491#if defined(__FreeBSD__)
492/* users say mempcpy is not present in FreeBSD 9.x */
493# undef HAVE_MEMPCPY
489# undef HAVE_CLEARENV 494# undef HAVE_CLEARENV
490# undef HAVE_FDATASYNC 495# undef HAVE_FDATASYNC
491# undef HAVE_MNTENT_H 496# undef HAVE_MNTENT_H
@@ -550,6 +555,18 @@ typedef void (*sighandler_t)(int);
550extern char *stpcpy(char *p, const char *to_add) FAST_FUNC; 555extern char *stpcpy(char *p, const char *to_add) FAST_FUNC;
551#endif 556#endif
552 557
558#ifndef HAVE_MEMPCPY
559#include <string.h>
560/* In case we are wrong about !HAVE_MEMPCPY, and toolchain _does_ have
561 * mempcpy(), avoid colliding with it:
562 */
563#define mempcpy bb__mempcpy
564static ALWAYS_INLINE void *mempcpy(void *dest, const void *src, size_t len)
565{
566 return memcpy(dest, src, len) + len;
567}
568#endif
569
553#ifndef HAVE_STRCASESTR 570#ifndef HAVE_STRCASESTR
554extern char *strcasestr(const char *s, const char *pattern) FAST_FUNC; 571extern char *strcasestr(const char *s, const char *pattern) FAST_FUNC;
555#endif 572#endif
diff --git a/include/pwd_.h b/include/pwd_.h
index 625b6f5a2..17348298a 100644
--- a/include/pwd_.h
+++ b/include/pwd_.h
@@ -34,69 +34,30 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
34#define setpwent bb_internal_setpwent 34#define setpwent bb_internal_setpwent
35#define endpwent bb_internal_endpwent 35#define endpwent bb_internal_endpwent
36#define getpwent bb_internal_getpwent 36#define getpwent bb_internal_getpwent
37#define fgetpwent bb_internal_fgetpwent
38#define putpwent bb_internal_putpwent
39#define getpwuid bb_internal_getpwuid 37#define getpwuid bb_internal_getpwuid
40#define getpwnam bb_internal_getpwnam 38#define getpwnam bb_internal_getpwnam
41#define getpwent_r bb_internal_getpwent_r
42#define getpwuid_r bb_internal_getpwuid_r
43#define getpwnam_r bb_internal_getpwnam_r 39#define getpwnam_r bb_internal_getpwnam_r
44#define fgetpwent_r bb_internal_fgetpwent_r
45
46 40
47/* All function names below should be remapped by #defines above 41/* All function names below should be remapped by #defines above
48 * in order to not collide with libc names. */ 42 * in order to not collide with libc names. */
49 43
50
51/* Rewind the password-file stream. */ 44/* Rewind the password-file stream. */
52extern void setpwent(void); 45void FAST_FUNC setpwent(void);
53 46
54/* Close the password-file stream. */ 47/* Close the password-file stream. */
55extern void endpwent(void); 48void FAST_FUNC endpwent(void);
56 49
57#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
58/* Read an entry from the password-file stream, opening it if necessary. */ 50/* Read an entry from the password-file stream, opening it if necessary. */
59extern struct passwd *getpwent(void); 51struct passwd* FAST_FUNC getpwent(void);
60
61/* Read an entry from STREAM. */
62extern struct passwd *fgetpwent(FILE *__stream);
63
64/* Write the given entry onto the given stream. */
65extern int putpwent(const struct passwd *__restrict __p,
66 FILE *__restrict __f);
67#endif
68 52
69/* Search for an entry with a matching user ID. */ 53/* Search for an entry with a matching user ID. */
70extern struct passwd *getpwuid(uid_t __uid); 54struct passwd* FAST_FUNC getpwuid(uid_t __uid);
71 55
72/* Search for an entry with a matching username. */ 56/* Search for an entry with a matching username. */
73extern struct passwd *getpwnam(const char *__name); 57struct passwd* FAST_FUNC getpwnam(const char *__name);
74
75/* Reentrant versions of some of the functions above.
76
77 PLEASE NOTE: the `getpwent_r' function is not (yet) standardized.
78 The interface may change in later versions of this library. But
79 the interface is designed following the principals used for the
80 other reentrant functions so the chances are good this is what the
81 POSIX people would choose. */
82
83extern int getpwent_r(struct passwd *__restrict __resultbuf,
84 char *__restrict __buffer, size_t __buflen,
85 struct passwd **__restrict __result);
86
87extern int getpwuid_r(uid_t __uid,
88 struct passwd *__restrict __resultbuf,
89 char *__restrict __buffer, size_t __buflen,
90 struct passwd **__restrict __result);
91
92extern int getpwnam_r(const char *__restrict __name,
93 struct passwd *__restrict __resultbuf,
94 char *__restrict __buffer, size_t __buflen,
95 struct passwd **__restrict __result);
96 58
97/* Read an entry from STREAM. This function is not standardized and 59/* Reentrant versions of some of the functions above. */
98 probably never will. */ 60int FAST_FUNC getpwnam_r(const char *__restrict __name,
99extern int fgetpwent_r(FILE *__restrict __stream,
100 struct passwd *__restrict __resultbuf, 61 struct passwd *__restrict __resultbuf,
101 char *__restrict __buffer, size_t __buflen, 62 char *__restrict __buffer, size_t __buflen,
102 struct passwd **__restrict __result); 63 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 {
57 57
58#ifdef UNUSED_FOR_NOW 58#ifdef UNUSED_FOR_NOW
59/* Open database for reading */ 59/* Open database for reading */
60extern void setspent(void); 60void FAST_FUNC setspent(void);
61 61
62/* Close database */ 62/* Close database */
63extern void endspent(void); 63void FAST_FUNC endspent(void);
64 64
65/* Get next entry from database, perhaps after opening the file */ 65/* Get next entry from database, perhaps after opening the file */
66extern struct spwd *getspent(void); 66struct spwd* FAST_FUNC getspent(void);
67 67
68/* Get shadow entry matching NAME */ 68/* Get shadow entry matching NAME */
69extern struct spwd *getspnam(const char *__name); 69struct spwd* FAST_FUNC getspnam(const char *__name);
70 70
71/* Read shadow entry from STRING */ 71/* Read shadow entry from STRING */
72extern struct spwd *sgetspent(const char *__string); 72struct spwd* FAST_FUNC sgetspent(const char *__string);
73 73
74/* Read next shadow entry from STREAM */ 74/* Read next shadow entry from STREAM */
75extern struct spwd *fgetspent(FILE *__stream); 75struct spwd* FAST_FUNC fgetspent(FILE *__stream);
76 76
77/* Write line containing shadow password entry to stream */ 77/* Write line containing shadow password entry to stream */
78extern int putspent(const struct spwd *__p, FILE *__stream); 78int FAST_FUNC putspent(const struct spwd *__p, FILE *__stream);
79 79
80/* Reentrant versions of some of the functions above */ 80/* Reentrant versions of some of the functions above */
81extern int getspent_r(struct spwd *__result_buf, char *__buffer, 81int FAST_FUNC getspent_r(struct spwd *__result_buf, char *__buffer,
82 size_t __buflen, struct spwd **__result); 82 size_t __buflen, struct spwd **__result);
83#endif 83#endif
84 84
85extern int getspnam_r(const char *__name, struct spwd *__result_buf, 85int FAST_FUNC getspnam_r(const char *__name, struct spwd *__result_buf,
86 char *__buffer, size_t __buflen, 86 char *__buffer, size_t __buflen,
87 struct spwd **__result); 87 struct spwd **__result);
88 88
89#ifdef UNUSED_FOR_NOW 89#ifdef UNUSED_FOR_NOW
90extern int sgetspent_r(const char *__string, struct spwd *__result_buf, 90int FAST_FUNC sgetspent_r(const char *__string, struct spwd *__result_buf,
91 char *__buffer, size_t __buflen, 91 char *__buffer, size_t __buflen,
92 struct spwd **__result); 92 struct spwd **__result);
93 93
94extern int fgetspent_r(FILE *__stream, struct spwd *__result_buf, 94int FAST_FUNC fgetspent_r(FILE *__stream, struct spwd *__result_buf,
95 char *__buffer, size_t __buflen, 95 char *__buffer, size_t __buflen,
96 struct spwd **__result); 96 struct spwd **__result);
97/* Protect password file against multi writers */ 97/* Protect password file against multi writers */
98extern int lckpwdf(void); 98int FAST_FUNC lckpwdf(void);
99 99
100/* Unlock password file */ 100/* Unlock password file */
101extern int ulckpwdf(void); 101int FAST_FUNC ulckpwdf(void);
102#endif 102#endif
103 103
104POP_SAVED_FUNCTION_VISIBILITY 104POP_SAVED_FUNCTION_VISIBILITY
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)
538 struct init_action *a; 538 struct init_action *a;
539 539
540 if (pid > 0) { 540 if (pid > 0) {
541 update_utmp(pid, DEAD_PROCESS, 541 update_utmp_DEAD_PROCESS(pid);
542 /*tty_name:*/ NULL,
543 /*username:*/ NULL,
544 /*hostname:*/ NULL
545 );
546 for (a = init_action_list; a; a = a->next) { 542 for (a = init_action_list; a; a = a->next) {
547 if (a->pid == pid) { 543 if (a->pid == pid) {
548 a->pid = 0; 544 a->pid = 0;
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index dba66cc93..3f51ecef6 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -197,7 +197,7 @@ void lbb_prepare(const char *applet
197 if (argv[1] 197 if (argv[1]
198 && !argv[2] 198 && !argv[2]
199 && strcmp(argv[1], "--help") == 0 199 && strcmp(argv[1], "--help") == 0
200 && strncmp(applet, "busybox", 7) != 0 200 && !is_prefixed_with(applet, "busybox")
201 ) { 201 ) {
202 /* Special case. POSIX says "test --help" 202 /* Special case. POSIX says "test --help"
203 * should be no different from e.g. "test --foo". */ 203 * should be no different from e.g. "test --foo". */
@@ -686,7 +686,7 @@ static int busybox_main(char **argv)
686 return 0; 686 return 0;
687 } 687 }
688 688
689 if (strncmp(argv[1], "--list", 6) == 0) { 689 if (is_prefixed_with(argv[1], "--list")) {
690 unsigned i = 0; 690 unsigned i = 0;
691 const char *a = applet_names; 691 const char *a = applet_names;
692 dup2(1, 2); 692 dup2(1, 2);
@@ -791,7 +791,7 @@ void FAST_FUNC run_applet_and_exit(const char *name, char **argv)
791 int applet = find_applet_by_name(name); 791 int applet = find_applet_by_name(name);
792 if (applet >= 0) 792 if (applet >= 0)
793 run_applet_no_and_exit(applet, argv); 793 run_applet_no_and_exit(applet, argv);
794 if (strncmp(name, "busybox", 7) == 0) 794 if (is_prefixed_with(name, "busybox"))
795 exit(busybox_main(argv)); 795 exit(busybox_main(argv));
796} 796}
797 797
@@ -842,7 +842,7 @@ int main(int argc UNUSED_PARAM, char **argv)
842 842
843#if defined(SINGLE_APPLET_MAIN) 843#if defined(SINGLE_APPLET_MAIN)
844 /* Only one applet is selected in .config */ 844 /* Only one applet is selected in .config */
845 if (argv[1] && strncmp(argv[0], "busybox", 7) == 0) { 845 if (argv[1] && is_prefixed_with(argv[0], "busybox")) {
846 /* "busybox <applet> <params>" should still work as expected */ 846 /* "busybox <applet> <params>" should still work as expected */
847 argv++; 847 argv++;
848 } 848 }
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,
110 return xname2id(s); 110 return xname2id(s);
111 return r; 111 return r;
112} 112}
113
114/* Experimental "mallocing" API.
115 * The goal is nice: "we want to support a case when "guests" group is very large"
116 * but the code is butt-ugly.
117 */
118#if 0
119static char *find_latest(char last, char *cp)
120{
121 if (!cp)
122 return last;
123 cp += strlen(cp) + 1;
124 if (last < cp)
125 last = cp;
126 return last;
127}
128
129struct group* FAST_FUNC xmalloc_getgrnam(const char *name)
130{
131 struct {
132 struct group gr;
133 // May still be not enough!
134 char buf[64*1024 - sizeof(struct group) - 16];
135 } *s;
136 struct group *grp;
137 int r;
138 char *last;
139 char **gr_mem;
140
141 s = xmalloc(sizeof(*s));
142 r = getgrnam_r(name, &s->gr, s->buf, sizeof(s->buf), &grp);
143 if (!grp) {
144 free(s);
145 return grp;
146 }
147 last = find_latest(s->buf, grp->gr_name);
148 last = find_latest(last, grp->gr_passwd);
149 gr_mem = grp->gr_mem;
150 while (*gr_mem)
151 last = find_latest(last, *gr_mem++);
152 gr_mem++; /* points past NULL */
153 if (last < (char*)gr_mem)
154 last = (char*)gr_mem;
155//FIXME: what if we get not only truncated, but also moved here?
156// grp->gr_name pointer and friends are invalid now!!!
157 s = xrealloc(s, last - (char*)s);
158 return grp;
159}
160#endif
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 @@
5 5
6#include "libbb.h" 6#include "libbb.h"
7 7
8char* FAST_FUNC is_prefixed_with(const char *string, const char *key)
9{
10#if 0 /* Two passes over key - probably slower */
11 int len = strlen(key);
12 if (strncmp(string, key, len) == 0)
13 return string + len;
14 return NULL;
15#else /* Open-coded */
16 while (*key != '\0') {
17 if (*key != *string)
18 return NULL;
19 key++;
20 string++;
21 }
22 return (char*)string;
23#endif
24}
25
8/* returns the array index of the string */ 26/* returns the array index of the string */
9/* (index of first match is returned, or -1) */ 27/* (index of first match is returned, or -1) */
10int FAST_FUNC index_in_str_array(const char *const string_array[], const char *key) 28int 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)
39int FAST_FUNC index_in_substr_array(const char *const string_array[], const char *key) 57int FAST_FUNC index_in_substr_array(const char *const string_array[], const char *key)
40{ 58{
41 int i; 59 int i;
42 int len = strlen(key); 60 if (key[0]) {
43 if (len) {
44 for (i = 0; string_array[i] != 0; i++) { 61 for (i = 0; string_array[i] != 0; i++) {
45 if (strncmp(string_array[i], key, len) == 0) { 62 if (is_prefixed_with(string_array[i], key)) {
46 return i; 63 return i;
47 } 64 }
48 } 65 }
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
32 return 0; 32 return 0;
33 } 33 }
34 /* If we expect this to be a hostname, try hostname database first */ 34 /* If we expect this to be a hostname, try hostname database first */
35#ifdef DEBUG
36 if (hostfirst) { 35 if (hostfirst) {
36#ifdef DEBUG
37 bb_error_msg("gethostbyname(%s)", name); 37 bb_error_msg("gethostbyname(%s)", name);
38 }
39#endif 38#endif
40 if (hostfirst) {
41 hp = gethostbyname(name); 39 hp = gethostbyname(name);
42 if (hp != NULL) { 40 if (hp) {
43 memcpy(&s_in->sin_addr, hp->h_addr_list[0], 41 memcpy(&s_in->sin_addr, hp->h_addr_list[0],
44 sizeof(struct in_addr)); 42 sizeof(struct in_addr));
45 return 0; 43 return 0;
@@ -51,7 +49,7 @@ int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostf
51 bb_error_msg("getnetbyname(%s)", name); 49 bb_error_msg("getnetbyname(%s)", name);
52#endif 50#endif
53 np = getnetbyname(name); 51 np = getnetbyname(name);
54 if (np != NULL) { 52 if (np) {
55 s_in->sin_addr.s_addr = htonl(np->n_net); 53 s_in->sin_addr.s_addr = htonl(np->n_net);
56 return 1; 54 return 1;
57 } 55 }
@@ -66,7 +64,7 @@ int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostf
66 bb_error_msg("gethostbyname(%s)", name); 64 bb_error_msg("gethostbyname(%s)", name);
67#endif 65#endif
68 hp = gethostbyname(name); 66 hp = gethostbyname(name);
69 if (hp == NULL) { 67 if (!hp) {
70 return -1; 68 return -1;
71 } 69 }
72 memcpy(&s_in->sin_addr, hp->h_addr_list[0], sizeof(struct in_addr)); 70 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
74} 72}
75 73
76 74
77/* numeric: & 0x8000: default instead of *, 75/* numeric: & 0x8000: "default" instead of "*",
78 * & 0x4000: host instead of net, 76 * & 0x4000: host instead of net,
79 * & 0x0fff: don't resolve 77 * & 0x0fff: don't resolve
80 */ 78 */
@@ -83,16 +81,16 @@ char* FAST_FUNC INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t ne
83 /* addr-to-name cache */ 81 /* addr-to-name cache */
84 struct addr { 82 struct addr {
85 struct addr *next; 83 struct addr *next;
86 struct sockaddr_in addr; 84 uint32_t nip;
87 int host; 85 smallint is_host;
88 char name[1]; 86 char name[1];
89 }; 87 };
90 static struct addr *cache = NULL; 88 static struct addr *cache = NULL;
91 89
92 struct addr *pn; 90 struct addr *pn;
93 char *name; 91 char *name;
94 uint32_t ad, host_ad; 92 uint32_t nip;
95 int host = 0; 93 smallint is_host;
96 94
97 if (s_in->sin_family != AF_INET) { 95 if (s_in->sin_family != AF_INET) {
98#ifdef DEBUG 96#ifdef DEBUG
@@ -102,61 +100,57 @@ char* FAST_FUNC INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t ne
102 errno = EAFNOSUPPORT; 100 errno = EAFNOSUPPORT;
103 return NULL; 101 return NULL;
104 } 102 }
105 ad = s_in->sin_addr.s_addr; 103 nip = s_in->sin_addr.s_addr;
106#ifdef DEBUG 104#ifdef DEBUG
107 bb_error_msg("rresolve: %08x, mask %08x, num %08x", (unsigned)ad, netmask, numeric); 105 bb_error_msg("rresolve: %08x mask:%08x num:%08x", (unsigned)nip, netmask, numeric);
108#endif 106#endif
109 if (ad == INADDR_ANY) {
110 if ((numeric & 0x0FFF) == 0) {
111 if (numeric & 0x8000)
112 return xstrdup("default");
113 return xstrdup("*");
114 }
115 }
116 if (numeric & 0x0FFF) 107 if (numeric & 0x0FFF)
117 return xstrdup(inet_ntoa(s_in->sin_addr)); 108 return xmalloc_sockaddr2dotted_noport((void*)s_in);
109 if (nip == INADDR_ANY) {
110 if (numeric & 0x8000)
111 return xstrdup("default");
112 return xstrdup("*");
113 }
114
115 is_host = ((nip & (~netmask)) != 0 || (numeric & 0x4000));
118 116
119 if ((ad & (~netmask)) != 0 || (numeric & 0x4000))
120 host = 1;
121 pn = cache; 117 pn = cache;
122 while (pn) { 118 while (pn) {
123 if (pn->addr.sin_addr.s_addr == ad && pn->host == host) { 119 if (pn->nip == nip && pn->is_host == is_host) {
124#ifdef DEBUG 120#ifdef DEBUG
125 bb_error_msg("rresolve: found %s %08x in cache", 121 bb_error_msg("rresolve: found %s %08x in cache",
126 (host ? "host" : "net"), (unsigned)ad); 122 (is_host ? "host" : "net"), (unsigned)nip);
127#endif 123#endif
128 return xstrdup(pn->name); 124 return xstrdup(pn->name);
129 } 125 }
130 pn = pn->next; 126 pn = pn->next;
131 } 127 }
132 128
133 host_ad = ntohl(ad);
134 name = NULL; 129 name = NULL;
135 if (host) { 130 if (is_host) {
136 struct hostent *ent;
137#ifdef DEBUG 131#ifdef DEBUG
138 bb_error_msg("gethostbyaddr (%08x)", (unsigned)ad); 132 bb_error_msg("sockaddr2host_noport(%08x)", (unsigned)nip);
139#endif 133#endif
140 ent = gethostbyaddr((char *) &ad, 4, AF_INET); 134 name = xmalloc_sockaddr2host_noport((void*)s_in);
141 if (ent)
142 name = xstrdup(ent->h_name);
143 } else if (ENABLE_FEATURE_ETC_NETWORKS) { 135 } else if (ENABLE_FEATURE_ETC_NETWORKS) {
144 struct netent *np; 136 struct netent *np;
145#ifdef DEBUG 137#ifdef DEBUG
146 bb_error_msg("getnetbyaddr (%08x)", (unsigned)host_ad); 138 bb_error_msg("getnetbyaddr(%08x)", (unsigned)ntohl(nip));
147#endif 139#endif
148 np = getnetbyaddr(host_ad, AF_INET); 140 np = getnetbyaddr(ntohl(nip), AF_INET);
149 if (np) 141 if (np)
150 name = xstrdup(np->n_name); 142 name = xstrdup(np->n_name);
151 } 143 }
152 if (!name) 144 if (!name)
153 name = xstrdup(inet_ntoa(s_in->sin_addr)); 145 name = xmalloc_sockaddr2dotted_noport((void*)s_in);
146
154 pn = xmalloc(sizeof(*pn) + strlen(name)); /* no '+ 1', it's already accounted for */ 147 pn = xmalloc(sizeof(*pn) + strlen(name)); /* no '+ 1', it's already accounted for */
155 pn->next = cache; 148 pn->next = cache;
156 pn->addr = *s_in; 149 pn->nip = nip;
157 pn->host = host; 150 pn->is_host = is_host;
158 strcpy(pn->name, name); 151 strcpy(pn->name, name);
159 cache = pn; 152 cache = pn;
153
160 return name; 154 return name;
161} 155}
162 156
@@ -188,9 +182,6 @@ int FAST_FUNC INET6_resolve(const char *name, struct sockaddr_in6 *sin6)
188 182
189char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric) 183char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric)
190{ 184{
191 char name[128];
192 int s;
193
194 if (sin6->sin6_family != AF_INET6) { 185 if (sin6->sin6_family != AF_INET6) {
195#ifdef DEBUG 186#ifdef DEBUG
196 bb_error_msg("rresolve: unsupported address family %d!", 187 bb_error_msg("rresolve: unsupported address family %d!",
@@ -200,8 +191,7 @@ char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric)
200 return NULL; 191 return NULL;
201 } 192 }
202 if (numeric & 0x7FFF) { 193 if (numeric & 0x7FFF) {
203 inet_ntop(AF_INET6, &sin6->sin6_addr, name, sizeof(name)); 194 return xmalloc_sockaddr2dotted_noport((void*)sin6);
204 return xstrdup(name);
205 } 195 }
206 if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { 196 if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
207 if (numeric & 0x8000) 197 if (numeric & 0x8000)
@@ -209,15 +199,7 @@ char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric)
209 return xstrdup("*"); 199 return xstrdup("*");
210 } 200 }
211 201
212 s = getnameinfo((struct sockaddr *) sin6, sizeof(*sin6), 202 return xmalloc_sockaddr2host_noport((void*)sin6);
213 name, sizeof(name),
214 /*serv,servlen:*/ NULL, 0,
215 0);
216 if (s != 0) {
217 bb_error_msg("getnameinfo failed");
218 return NULL;
219 }
220 return xstrdup(name);
221} 203}
222 204
223#endif /* CONFIG_FEATURE_IPV6 */ 205#endif /* CONFIG_FEATURE_IPV6 */
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 3d96a8e9f..7982f2997 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -714,23 +714,20 @@ static char *username_path_completion(char *ud)
714 */ 714 */
715static NOINLINE unsigned complete_username(const char *ud) 715static NOINLINE unsigned complete_username(const char *ud)
716{ 716{
717 /* Using _r function to avoid pulling in static buffers */ 717 struct passwd *pw;
718 char line_buff[256];
719 struct passwd pwd;
720 struct passwd *result;
721 unsigned userlen; 718 unsigned userlen;
722 719
723 ud++; /* skip ~ */ 720 ud++; /* skip ~ */
724 userlen = strlen(ud); 721 userlen = strlen(ud);
725 722
726 setpwent(); 723 setpwent();
727 while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) { 724 while ((pw = getpwent()) != NULL) {
728 /* Null usernames should result in all users as possible completions. */ 725 /* Null usernames should result in all users as possible completions. */
729 if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) { 726 if (/* !ud[0] || */ is_prefixed_with(pw->pw_name, ud)) {
730 add_match(xasprintf("~%s/", pwd.pw_name)); 727 add_match(xasprintf("~%s/", pw->pw_name));
731 } 728 }
732 } 729 }
733 endpwent(); 730 endpwent(); /* don't keep password file open */
734 731
735 return 1 + userlen; 732 return 1 + userlen;
736} 733}
@@ -845,7 +842,7 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
845 if (!pfind[0] && DOT_OR_DOTDOT(name_found)) 842 if (!pfind[0] && DOT_OR_DOTDOT(name_found))
846 continue; 843 continue;
847 /* match? */ 844 /* match? */
848 if (strncmp(name_found, pfind, pf_len) != 0) 845 if (!is_prefixed_with(name_found, pfind))
849 continue; /* no */ 846 continue; /* no */
850 847
851 found = concat_path_file(paths[i], name_found); 848 found = concat_path_file(paths[i], name_found);
@@ -1932,19 +1929,25 @@ static void parse_and_put_prompt(const char *prmt_ptr)
1932 cwd_buf = xrealloc_getcwd_or_warn(NULL); 1929 cwd_buf = xrealloc_getcwd_or_warn(NULL);
1933 if (!cwd_buf) 1930 if (!cwd_buf)
1934 cwd_buf = (char *)bb_msg_unknown; 1931 cwd_buf = (char *)bb_msg_unknown;
1935 else { 1932 else if (home_pwd_buf[0]) {
1933 char *after_home_user;
1934
1936 /* /home/user[/something] -> ~[/something] */ 1935 /* /home/user[/something] -> ~[/something] */
1937 l = strlen(home_pwd_buf);
1938 if (l != 0
1939#if !ENABLE_PLATFORM_MINGW32 1936#if !ENABLE_PLATFORM_MINGW32
1940 && strncmp(home_pwd_buf, cwd_buf, l) == 0 1937 after_home_user = is_prefixed_with(cwd_buf, home_pwd_buf);
1941#else 1938#else
1942 && strncasecmp(home_pwd_buf, cwd_buf, l) == 0 1939 after_home_user = NULL;
1940 l = strlen(home_pwd_buf);
1941 if (l != 0
1942 && strncasecmp(home_pwd_buf, cwd_buf, l) == 0) {
1943 after_home_user = cwd_buf + l;
1944 }
1943#endif 1945#endif
1944 && (cwd_buf[l] == '/' || cwd_buf[l] == '\0') 1946 if (after_home_user
1947 && (*after_home_user == '/' || *after_home_user == '\0')
1945 ) { 1948 ) {
1946 cwd_buf[0] = '~'; 1949 cwd_buf[0] = '~';
1947 overlapping_strcpy(cwd_buf + 1, cwd_buf + l); 1950 overlapping_strcpy(cwd_buf + 1, after_home_user);
1948 } 1951 }
1949 } 1952 }
1950 } 1953 }
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
154 else 154 else
155 ioctl(dfd, LOOP_CLR_FD, 0); 155 ioctl(dfd, LOOP_CLR_FD, 0);
156 } 156 }
157 157 } else {
158 /* If this block device already set up right, re-use it.
159 * (Yes this is racy, but associating two loop devices with the same
160 * file isn't pretty either. In general, mounting the same file twice
161 * without using losetup manually is problematic.)
162 */
163 } else
164 if (strcmp(file, (char *)loopinfo.lo_file_name) != 0
165 || offset != loopinfo.lo_offset
166 ) {
167 rc = -1; 158 rc = -1;
168 } 159 }
169 close(dfd); 160 close(dfd);
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 @@
17int FAST_FUNC match_fstype(const struct mntent *mt, const char *t_fstype) 17int FAST_FUNC match_fstype(const struct mntent *mt, const char *t_fstype)
18{ 18{
19 int match = 1; 19 int match = 1;
20 int len;
21 20
22 if (!t_fstype) 21 if (!t_fstype)
23 return match; 22 return match;
@@ -27,10 +26,10 @@ int FAST_FUNC match_fstype(const struct mntent *mt, const char *t_fstype)
27 t_fstype += 2; 26 t_fstype += 2;
28 } 27 }
29 28
30 len = strlen(mt->mnt_type);
31 while (1) { 29 while (1) {
32 if (strncmp(mt->mnt_type, t_fstype, len) == 0 30 char *after_mnt_type = is_prefixed_with(t_fstype, mt->mnt_type);
33 && (t_fstype[len] == '\0' || t_fstype[len] == ',') 31 if (after_mnt_type
32 && (*after_mnt_type == '\0' || *after_mnt_type == ',')
34 ) { 33 ) {
35 return match; 34 return match;
36 } 35 }
diff --git a/libbb/procps.c b/libbb/procps.c
index 3c99ac6e7..fff9f8b0b 100644
--- a/libbb/procps.c
+++ b/libbb/procps.c
@@ -206,11 +206,11 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
206 // Rss: nnn kB 206 // Rss: nnn kB
207 // ..... 207 // .....
208 208
209 char *tp = buf, *p; 209 char *tp, *p;
210 210
211#define SCAN(S, X) \ 211#define SCAN(S, X) \
212 if (strncmp(tp, S, sizeof(S)-1) == 0) { \ 212 if ((tp = is_prefixed_with(buf, S)) != NULL) { \
213 tp = skip_whitespace(tp + sizeof(S)-1); \ 213 tp = skip_whitespace(tp); \
214 total->X += currec.X = fast_strtoul_10(&tp); \ 214 total->X += currec.X = fast_strtoul_10(&tp); \
215 continue; \ 215 continue; \
216 } 216 }
@@ -248,7 +248,7 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
248 // skipping "rw-s FILEOFS M:m INODE " 248 // skipping "rw-s FILEOFS M:m INODE "
249 tp = skip_whitespace(skip_fields(tp, 4)); 249 tp = skip_whitespace(skip_fields(tp, 4));
250 // filter out /dev/something (something != zero) 250 // filter out /dev/something (something != zero)
251 if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) { 251 if (!is_prefixed_with(tp, "/dev/") || strcmp(tp, "/dev/zero\n") == 0) {
252 if (currec.smap_mode[1] == 'w') { 252 if (currec.smap_mode[1] == 'w') {
253 currec.mapped_rw = currec.smap_size; 253 currec.mapped_rw = currec.smap_size;
254 total->mapped_rw += currec.smap_size; 254 total->mapped_rw += currec.smap_size;
@@ -498,8 +498,8 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
498 while (fgets(buf, sizeof(buf), file)) { 498 while (fgets(buf, sizeof(buf), file)) {
499 char *tp; 499 char *tp;
500#define SCAN_TWO(str, name, statement) \ 500#define SCAN_TWO(str, name, statement) \
501 if (strncmp(buf, str, sizeof(str)-1) == 0) { \ 501 if ((tp = is_prefixed_with(buf, str)) != NULL) { \
502 tp = skip_whitespace(buf + sizeof(str)-1); \ 502 tp = skip_whitespace(tp); \
503 sscanf(tp, "%u", &sp->name); \ 503 sscanf(tp, "%u", &sp->name); \
504 statement; \ 504 statement; \
505 } 505 }
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)
22 char buffer[128]; 22 char buffer[128];
23 23
24 while (fgets(buffer, sizeof(buffer), f)) { 24 while (fgets(buffer, sizeof(buffer), f)) {
25 if (strncmp(buffer, "UTC", 3) == 0) { 25 if (is_prefixed_with(buffer, "UTC")) {
26 utc = 1; 26 utc = 1;
27 break; 27 break;
28 } 28 }
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)
33 33
34char* FAST_FUNC skip_dev_pfx(const char *tty_name) 34char* FAST_FUNC skip_dev_pfx(const char *tty_name)
35{ 35{
36 if (strncmp(tty_name, "/dev/", 5) == 0) 36 if (is_prefixed_with(tty_name, "/dev/"))
37 tty_name += 5; 37 tty_name += 5;
38 return (char*)tty_name; 38 return (char*)tty_name;
39} 39}
diff --git a/libbb/update_passwd.c b/libbb/update_passwd.c
index a30af6f72..a2004f480 100644
--- a/libbb/update_passwd.c
+++ b/libbb/update_passwd.c
@@ -62,6 +62,8 @@ static void check_selinux_update_passwd(const char *username)
62 only if CONFIG_PASSWD=y and applet_name[0] == 'p' like in passwd 62 only if CONFIG_PASSWD=y and applet_name[0] == 'p' like in passwd
63 or if CONFIG_CHPASSWD=y and applet_name[0] == 'c' like in chpasswd 63 or if CONFIG_CHPASSWD=y and applet_name[0] == 'c' like in chpasswd
64 64
65 8) delete a user from all groups: update_passwd(FILE, NULL, NULL, MEMBER)
66
65 This function does not validate the arguments fed to it 67 This function does not validate the arguments fed to it
66 so the calling program should take care of that. 68 so the calling program should take care of that.
67 69
@@ -82,7 +84,6 @@ int FAST_FUNC update_passwd(const char *filename,
82 char *fnamesfx; 84 char *fnamesfx;
83 char *sfx_char; 85 char *sfx_char;
84 char *name_colon; 86 char *name_colon;
85 unsigned user_len;
86 int old_fd; 87 int old_fd;
87 int new_fd; 88 int new_fd;
88 int i; 89 int i;
@@ -99,13 +100,13 @@ int FAST_FUNC update_passwd(const char *filename,
99 if (filename == NULL) 100 if (filename == NULL)
100 return ret; 101 return ret;
101 102
102 check_selinux_update_passwd(name); 103 if (name)
104 check_selinux_update_passwd(name);
103 105
104 /* New passwd file, "/etc/passwd+" for now */ 106 /* New passwd file, "/etc/passwd+" for now */
105 fnamesfx = xasprintf("%s+", filename); 107 fnamesfx = xasprintf("%s+", filename);
106 sfx_char = &fnamesfx[strlen(fnamesfx)-1]; 108 sfx_char = &fnamesfx[strlen(fnamesfx)-1];
107 name_colon = xasprintf("%s:", name); 109 name_colon = xasprintf("%s:", name ? name : "");
108 user_len = strlen(name_colon);
109 110
110 if (shadow) 111 if (shadow)
111 old_fp = fopen(filename, "r+"); 112 old_fp = fopen(filename, "r+");
@@ -167,13 +168,45 @@ int FAST_FUNC update_passwd(const char *filename,
167 line = xmalloc_fgetline(old_fp); 168 line = xmalloc_fgetline(old_fp);
168 if (!line) /* EOF/error */ 169 if (!line) /* EOF/error */
169 break; 170 break;
170 if (strncmp(name_colon, line, user_len) != 0) { 171
172 if (!name && member) {
173 /* Delete member from all groups */
174 /* line is "GROUP:PASSWD:[member1[,member2]...]" */
175 unsigned member_len = strlen(member);
176 char *list = strrchr(line, ':');
177 while (list) {
178 list++;
179 next_list_element:
180 if (is_prefixed_with(list, member)) {
181 char c;
182 changed_lines++;
183 c = list[member_len];
184 if (c == '\0') {
185 if (list[-1] == ',')
186 list--;
187 *list = '\0';
188 break;
189 }
190 if (c == ',') {
191 overlapping_strcpy(list, list + member_len + 1);
192 goto next_list_element;
193 }
194 changed_lines--;
195 }
196 list = strchr(list, ',');
197 }
198 fprintf(new_fp, "%s\n", line);
199 goto next;
200 }
201
202 cp = is_prefixed_with(line, name_colon);
203 if (!cp) {
171 fprintf(new_fp, "%s\n", line); 204 fprintf(new_fp, "%s\n", line);
172 goto next; 205 goto next;
173 } 206 }
174 207
175 /* We have a match with "name:"... */ 208 /* We have a match with "name:"... */
176 cp = line + user_len; /* move past name: */ 209 /* cp points past "name:" */
177 210
178#if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP 211#if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP
179 if (member) { 212 if (member) {
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
130 updwtmp(bb_path_wtmp_file, &utent); 130 updwtmp(bb_path_wtmp_file, &utent);
131#endif 131#endif
132} 132}
133
134/* man utmp:
135 * When init(8) finds that a process has exited, it locates its utmp entry
136 * by ut_pid, sets ut_type to DEAD_PROCESS, and clears ut_user, ut_host
137 * and ut_time with null bytes.
138 * [same applies to other processes which maintain utmp entries, like telnetd]
139 *
140 * We do not bother actually clearing fields:
141 * it might be interesting to know who was logged in and from where
142 */
143void FAST_FUNC update_utmp_DEAD_PROCESS(pid_t pid)
144{
145 update_utmp(pid, DEAD_PROCESS, NULL, NULL, NULL);
146}
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;)
171 const char *cp; 171 const char *cp;
172 struct addrinfo hint; 172 struct addrinfo hint;
173 173
174 if (ENABLE_FEATURE_UNIX_LOCAL && strncmp(host, "local:", 6) == 0) { 174 if (ENABLE_FEATURE_UNIX_LOCAL && is_prefixed_with(host, "local:")) {
175 struct sockaddr_un *sun; 175 struct sockaddr_un *sun;
176 176
177 r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_un)); 177 r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_un));
diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c
index 2060d7811..3886facf0 100644
--- a/libpwdgrp/pwd_grp.c
+++ b/libpwdgrp/pwd_grp.c
@@ -1,613 +1,541 @@
1/* vi: set sw=4 ts=4: */ 1/* vi: set sw=4 ts=4: */
2/* Copyright (C) 2003 Manuel Novoa III 2/* Copyright (C) 2014 Tito Ragusa <farmatito@tiscali.it>
3 * 3 *
4 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 4 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
5 */ 5 */
6 6/* This program is distributed in the hope that it will be useful,
7/* Nov 6, 2003 Initial version. 7 * but WITHOUT ANY WARRANTY!!
8 * 8 *
9 * NOTE: This implementation is quite strict about requiring all 9 * Rewrite of some parts. Main differences are:
10 * field seperators. It also does not allow leading whitespace
11 * except when processing the numeric fields. glibc is more
12 * lenient. See the various glibc difference comments below.
13 * 10 *
14 * TODO: 11 * 1) the buffer for getpwuid, getgrgid, getpwnam, getgrnam is dynamically
15 * Move to dynamic allocation of (currently statically allocated) 12 * allocated.
16 * buffers; especially for the group-related functions since 13 * If ENABLE_FEATURE_CLEAN_UP is set the buffers are freed at program
17 * large group member lists will cause error returns. 14 * exit using the atexit function to make valgrind happy.
15 * 2) the passwd/group files:
16 * a) must contain the expected number of fields (as per count of field
17 * delimeters ":") or we will complain with a error message.
18 * b) leading and trailing whitespace in fields is stripped.
19 * c) some fields are not allowed to be empty (e.g. username, uid/gid,
20 * homedir, shell) and in this case NULL is returned and errno is
21 * set to EINVAL. This behaviour could be easily changed by
22 * modifying PW_DEF, GR_DEF, SP_DEF strings (uppercase
23 * makes a field mandatory).
24 * d) the string representing uid/gid must be convertible by strtoXX
25 * functions, or errno is set to EINVAL.
26 * e) leading and trailing whitespace in group member names is stripped.
27 * 3) the internal function for getgrouplist uses dynamically allocated buffer.
28 * 4) at the moment only the functions really used by busybox code are
29 * implemented, if you need a particular missing function it should be
30 * easy to write it by using the internal common code.
18 */ 31 */
19 32
20#include "libbb.h" 33#include "libbb.h"
21#include <assert.h>
22
23/**********************************************************************/
24/* Sizes for statically allocated buffers. */
25
26#define PWD_BUFFER_SIZE 256
27#define GRP_BUFFER_SIZE 256
28 34
29/**********************************************************************/ 35struct const_passdb {
30/* Prototypes for internal functions. */ 36 const char *filename;
31 37 char def[7 + 2*ENABLE_USE_BB_SHADOW];
32static int bb__pgsreader( 38 uint8_t off[7 + 2*ENABLE_USE_BB_SHADOW];
33 int FAST_FUNC (*parserfunc)(void *d, char *line), 39 uint8_t numfields;
34 void *data, 40 uint8_t size_of;
35 char *__restrict line_buff, 41};
36 size_t buflen, 42struct passdb {
37 FILE *f); 43 const char *filename;
44 char def[7 + 2*ENABLE_USE_BB_SHADOW];
45 uint8_t off[7 + 2*ENABLE_USE_BB_SHADOW];
46 uint8_t numfields;
47 uint8_t size_of;
48 FILE *fp;
49 char *malloced;
50};
51/* Note: for shadow db, def[] will not contain terminating NUL,
52 * but convert_to_struct() logic detects def[] end by "less than SP?",
53 * not by "is it NUL?" condition; and off[0] happens to be zero
54 * for every db anyway, so there _is_ in fact a terminating NUL there.
55 */
38 56
39static int FAST_FUNC bb__parsepwent(void *pw, char *line); 57/* S = string not empty, s = string maybe empty,
40static int FAST_FUNC bb__parsegrent(void *gr, char *line); 58 * I = uid,gid, l = long maybe empty, m = members,
59 * r = reserved
60 */
61#define PW_DEF "SsIIsSS"
62#define GR_DEF "SsIm"
63#define SP_DEF "Ssllllllr"
64
65static const struct const_passdb const_pw_db = {
66 _PATH_PASSWD, PW_DEF,
67 {
68 offsetof(struct passwd, pw_name), /* 0 S */
69 offsetof(struct passwd, pw_passwd), /* 1 s */
70 offsetof(struct passwd, pw_uid), /* 2 I */
71 offsetof(struct passwd, pw_gid), /* 3 I */
72 offsetof(struct passwd, pw_gecos), /* 4 s */
73 offsetof(struct passwd, pw_dir), /* 5 S */
74 offsetof(struct passwd, pw_shell) /* 6 S */
75 },
76 sizeof(PW_DEF)-1, sizeof(struct passwd)
77};
78static const struct const_passdb const_gr_db = {
79 _PATH_GROUP, GR_DEF,
80 {
81 offsetof(struct group, gr_name), /* 0 S */
82 offsetof(struct group, gr_passwd), /* 1 s */
83 offsetof(struct group, gr_gid), /* 2 I */
84 offsetof(struct group, gr_mem) /* 3 m (char **) */
85 },
86 sizeof(GR_DEF)-1, sizeof(struct group)
87};
41#if ENABLE_USE_BB_SHADOW 88#if ENABLE_USE_BB_SHADOW
42static int FAST_FUNC bb__parsespent(void *sp, char *line); 89static const struct const_passdb const_sp_db = {
90 _PATH_SHADOW, SP_DEF,
91 {
92 offsetof(struct spwd, sp_namp), /* 0 S Login name */
93 offsetof(struct spwd, sp_pwdp), /* 1 s Encrypted password */
94 offsetof(struct spwd, sp_lstchg), /* 2 l */
95 offsetof(struct spwd, sp_min), /* 3 l */
96 offsetof(struct spwd, sp_max), /* 4 l */
97 offsetof(struct spwd, sp_warn), /* 5 l */
98 offsetof(struct spwd, sp_inact), /* 6 l */
99 offsetof(struct spwd, sp_expire), /* 7 l */
100 offsetof(struct spwd, sp_flag) /* 8 r Reserved */
101 },
102 sizeof(SP_DEF)-1, sizeof(struct spwd)
103};
43#endif 104#endif
44 105
45/**********************************************************************/
46/* We avoid having big global data. */ 106/* We avoid having big global data. */
47
48struct statics { 107struct statics {
49 /* Smaller things first */ 108 /* We use same buffer (db[0].malloced) for getpwuid and getpwnam.
50 /* It's ok to use one buffer for getpwuid and getpwnam. Manpage says: 109 * Manpage says:
51 * "The return value may point to a static area, and may be overwritten 110 * "The return value may point to a static area, and may be overwritten
52 * by subsequent calls to getpwent(), getpwnam(), or getpwuid()." 111 * by subsequent calls to getpwent(), getpwnam(), or getpwuid()."
53 */ 112 */
54 struct passwd getpw_resultbuf; 113 struct passdb db[2 + ENABLE_USE_BB_SHADOW];
55 struct group getgr_resultbuf; 114 char *tokenize_end;
56 115 unsigned string_size;
57 char getpw_buffer[PWD_BUFFER_SIZE];
58 char getgr_buffer[GRP_BUFFER_SIZE];
59#if 0 //ENABLE_USE_BB_SHADOW
60 struct spwd getsp_resultbuf;
61 char getsp_buffer[PWD_BUFFER_SIZE];
62#endif
63// Not converted - too small to bother
64//pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
65//FILE *pwf /*= NULL*/;
66//FILE *grf /*= NULL*/;
67//FILE *spf /*= NULL*/;
68}; 116};
69 117
70static struct statics *ptr_to_statics; 118static struct statics *ptr_to_statics;
119#define S (*ptr_to_statics)
120#define has_S (ptr_to_statics)
121
122#if ENABLE_FEATURE_CLEAN_UP
123static void free_static(void)
124{
125 free(S.db[0].malloced);
126 free(S.db[1].malloced);
127# if ENABLE_USE_BB_SHADOW
128 free(S.db[2].malloced);
129# endif
130 free(ptr_to_statics);
131}
132#endif
71 133
72static struct statics *get_S(void) 134static struct statics *get_S(void)
73{ 135{
74 if (!ptr_to_statics) 136 if (!ptr_to_statics) {
75 ptr_to_statics = xzalloc(sizeof(*ptr_to_statics)); 137 ptr_to_statics = xzalloc(sizeof(S));
138 memcpy(&S.db[0], &const_pw_db, sizeof(const_pw_db));
139 memcpy(&S.db[1], &const_gr_db, sizeof(const_gr_db));
140#if ENABLE_USE_BB_SHADOW
141 memcpy(&S.db[2], &const_sp_db, sizeof(const_sp_db));
142#endif
143#if ENABLE_FEATURE_CLEAN_UP
144 atexit(free_static);
145#endif
146 }
76 return ptr_to_statics; 147 return ptr_to_statics;
77} 148}
78 149
79/* Always use in this order, get_S() must be called first */ 150/* Internal functions */
80#define RESULTBUF(name) &((S = get_S())->name##_resultbuf)
81#define BUFFER(name) (S->name##_buffer)
82 151
83/**********************************************************************/ 152/* Divide the passwd/group/shadow record in fields
84/* For the various fget??ent_r funcs, return 153 * by substituting the given delimeter
85 * 154 * e.g. ':' or ',' with '\0'.
86 * 0: success 155 * Returns the number of fields found.
87 * ENOENT: end-of-file encountered 156 * Strips leading and trailing whitespace in fields.
88 * ERANGE: buflen too small
89 * other error values possible. See bb__pgsreader.
90 *
91 * Also, *result == resultbuf on success and NULL on failure.
92 *
93 * NOTE: glibc difference - For the ENOENT case, glibc also sets errno.
94 * We do not, as it really isn't an error if we reach the end-of-file.
95 * Doing so is analogous to having fgetc() set errno on EOF.
96 */ 157 */
97/**********************************************************************/ 158static int tokenize(char *buffer, int ch)
98
99int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf,
100 char *__restrict buffer, size_t buflen,
101 struct passwd **__restrict result)
102{ 159{
103 int rv; 160 char *p = buffer;
104 161 char *s = p;
105 *result = NULL; 162 int num_fields = 0;
106 163
107 rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, stream); 164 for (;;) {
108 if (!rv) { 165 if (isblank(*s)) {
109 *result = resultbuf; 166 overlapping_strcpy(s, skip_whitespace(s));
167 }
168 if (*p == ch || *p == '\0') {
169 char *end = p;
170 while (p != s && isblank(p[-1]))
171 p--;
172 if (p != end)
173 overlapping_strcpy(p, end);
174 num_fields++;
175 if (*end == '\0') {
176 S.tokenize_end = p + 1;
177 return num_fields;
178 }
179 *p = '\0';
180 s = p + 1;
181 }
182 p++;
110 } 183 }
111
112 return rv;
113} 184}
114 185
115int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf, 186/* Returns !NULL on success and matching line broken up in fields by '\0' in buf.
116 char *__restrict buffer, size_t buflen, 187 * We require the expected number of fields to be found.
117 struct group **__restrict result) 188 */
118{ 189static char *parse_common(FILE *fp, struct passdb *db,
119 int rv; 190 const char *key, int field_pos)
191{
192 char *buf;
193
194 while ((buf = xmalloc_fgetline(fp)) != NULL) {
195 /* Skip empty lines, comment lines */
196 if (buf[0] == '\0' || buf[0] == '#')
197 goto free_and_next;
198 if (tokenize(buf, ':') != db->numfields) {
199 /* number of fields is wrong */
200 bb_error_msg("%s: bad record", db->filename);
201 goto free_and_next;
202 }
120 203
121 *result = NULL; 204 if (field_pos == -1) {
205 /* no key specified: sequential read, return a record */
206 break;
207 }
208 if (strcmp(key, nth_string(buf, field_pos)) == 0) {
209 /* record found */
210 break;
211 }
212 free_and_next:
213 free(buf);
214 }
122 215
123 rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, stream); 216 S.string_size = S.tokenize_end - buf;
124 if (!rv) { 217/*
125 *result = resultbuf; 218 * Ugly hack: group db requires additional buffer space
219 * for members[] array. If there is only one group, we need space
220 * for 3 pointers: alignment padding, group name, NULL.
221 * +1 for every additional group.
222 */
223 if (buf && db->numfields == sizeof(GR_DEF)-1) { /* if we read group file... */
224 int cnt = 3;
225 char *p = buf;
226 while (p < S.tokenize_end)
227 if (*p++ == ',')
228 cnt++;
229 S.string_size += cnt * sizeof(char*);
230//bb_error_msg("+%d words = %u key:%s buf:'%s'", cnt, S.string_size, key, buf);
231 buf = xrealloc(buf, S.string_size);
126 } 232 }
127 233
128 return rv; 234 return buf;
129} 235}
130 236
131#if ENABLE_USE_BB_SHADOW 237static char *parse_file(struct passdb *db,
132#ifdef UNUSED_FOR_NOW 238 const char *key, int field_pos)
133int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf,
134 char *__restrict buffer, size_t buflen,
135 struct spwd **__restrict result)
136{ 239{
137 int rv; 240 char *buf = NULL;
138 241 FILE *fp = fopen_for_read(db->filename);
139 *result = NULL;
140 242
141 rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, stream); 243 if (fp) {
142 if (!rv) { 244 buf = parse_common(fp, db, key, field_pos);
143 *result = resultbuf; 245 fclose(fp);
144 } 246 }
145 247 return buf;
146 return rv;
147} 248}
148#endif
149#endif
150
151/**********************************************************************/
152/* For the various fget??ent funcs, return NULL on failure and a
153 * pointer to the appropriate struct (statically allocated) on success.
154 * TODO: audit & stop using these in bbox, they pull in static buffers */
155/**********************************************************************/
156 249
157#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS 250/* Convert passwd/group/shadow file record in buffer to a struct */
158struct passwd *fgetpwent(FILE *stream) 251static void *convert_to_struct(struct passdb *db,
252 char *buffer, void *result)
159{ 253{
160 struct statics *S; 254 const char *def = db->def;
161 struct passwd *resultbuf = RESULTBUF(getpw); 255 const uint8_t *off = db->off;
162 char *buffer = BUFFER(getpw);
163 struct passwd *result;
164 256
165 fgetpwent_r(stream, resultbuf, buffer, sizeof(BUFFER(getpw)), &result); 257 /* For consistency, zero out all fields */
166 return result; 258 memset(result, 0, db->size_of);
167}
168 259
169struct group *fgetgrent(FILE *stream) 260 for (;;) {
170{ 261 void *member = (char*)result + (*off++);
171 struct statics *S;
172 struct group *resultbuf = RESULTBUF(getgr);
173 char *buffer = BUFFER(getgr);
174 struct group *result;
175
176 fgetgrent_r(stream, resultbuf, buffer, sizeof(BUFFER(getgr)), &result);
177 return result;
178}
179#endif
180 262
263 if ((*def | 0x20) == 's') { /* s or S */
264 *(char **)member = (char*)buffer;
265 if (!buffer[0] && (*def == 'S')) {
266 errno = EINVAL;
267 }
268 }
269 if (*def == 'I') {
270 *(int *)member = bb_strtou(buffer, NULL, 10);
271 }
181#if ENABLE_USE_BB_SHADOW 272#if ENABLE_USE_BB_SHADOW
182#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS 273 if (*def == 'l') {
183struct spwd *fgetspent(FILE *stream) 274 long n = -1;
184{ 275 if (buffer[0])
185 struct statics *S; 276 n = bb_strtol(buffer, NULL, 10);
186 struct spwd *resultbuf = RESULTBUF(getsp); 277 *(long *)member = n;
187 char *buffer = BUFFER(getsp); 278 }
188 struct spwd *result;
189
190 fgetspent_r(stream, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
191 return result;
192}
193#endif 279#endif
194 280 if (*def == 'm') {
195#ifdef UNUSED_FOR_NOW 281 char **members;
196int sgetspent_r(const char *string, struct spwd *result_buf, 282 int i = tokenize(buffer, ',');
197 char *buffer, size_t buflen, struct spwd **result) 283
198{ 284 /* Store members[] after buffer's end.
199 int rv = ERANGE; 285 * This is safe ONLY because there is a hack
200 286 * in parse_common() which allocates additional space
201 *result = NULL; 287 * at the end of malloced buffer!
202 288 */
203 if (buflen < PWD_BUFFER_SIZE) { 289 members = (char **)
204 DO_ERANGE: 290 ( ((intptr_t)S.tokenize_end + sizeof(members[0]))
205 errno = rv; 291 & -(intptr_t)sizeof(members[0])
206 goto DONE; 292 );
207 } 293 ((struct group *)result)->gr_mem = members;
208 294 while (--i >= 0) {
209 if (string != buffer) { 295 if (buffer[0]) {
210 if (strlen(string) >= buflen) { 296 *members++ = buffer;
211 goto DO_ERANGE; 297 // bb_error_msg("member[]='%s'", buffer);
298 }
299 buffer += strlen(buffer) + 1;
300 }
301 *members = NULL;
212 } 302 }
213 strcpy(buffer, string); 303 /* def "r" does nothing */
214 }
215 304
216 rv = bb__parsespent(result_buf, buffer); 305 def++;
217 if (!rv) { 306 if ((unsigned char)*def <= (unsigned char)' ')
218 *result = result_buf; 307 break;
308 buffer += strlen(buffer) + 1;
219 } 309 }
220 310
221 DONE: 311 if (errno)
222 return rv; 312 result = NULL;
223}
224#endif
225#endif /* ENABLE_USE_BB_SHADOW */
226
227/**********************************************************************/
228
229#define GETXXKEY_R_FUNC getpwnam_r
230#define GETXXKEY_R_PARSER bb__parsepwent
231#define GETXXKEY_R_ENTTYPE struct passwd
232#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->pw_name, key))
233#define GETXXKEY_R_KEYTYPE const char *__restrict
234#define GETXXKEY_R_PATHNAME _PATH_PASSWD
235#include "pwd_grp_internal.c"
236
237#define GETXXKEY_R_FUNC getgrnam_r
238#define GETXXKEY_R_PARSER bb__parsegrent
239#define GETXXKEY_R_ENTTYPE struct group
240#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->gr_name, key))
241#define GETXXKEY_R_KEYTYPE const char *__restrict
242#define GETXXKEY_R_PATHNAME _PATH_GROUP
243#include "pwd_grp_internal.c"
244
245#if ENABLE_USE_BB_SHADOW
246#define GETXXKEY_R_FUNC getspnam_r
247#define GETXXKEY_R_PARSER bb__parsespent
248#define GETXXKEY_R_ENTTYPE struct spwd
249#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->sp_namp, key))
250#define GETXXKEY_R_KEYTYPE const char *__restrict
251#define GETXXKEY_R_PATHNAME _PATH_SHADOW
252#include "pwd_grp_internal.c"
253#endif
254
255#define GETXXKEY_R_FUNC getpwuid_r
256#define GETXXKEY_R_PARSER bb__parsepwent
257#define GETXXKEY_R_ENTTYPE struct passwd
258#define GETXXKEY_R_TEST(ENT) ((ENT)->pw_uid == key)
259#define GETXXKEY_R_KEYTYPE uid_t
260#define GETXXKEY_R_PATHNAME _PATH_PASSWD
261#include "pwd_grp_internal.c"
262
263#define GETXXKEY_R_FUNC getgrgid_r
264#define GETXXKEY_R_PARSER bb__parsegrent
265#define GETXXKEY_R_ENTTYPE struct group
266#define GETXXKEY_R_TEST(ENT) ((ENT)->gr_gid == key)
267#define GETXXKEY_R_KEYTYPE gid_t
268#define GETXXKEY_R_PATHNAME _PATH_GROUP
269#include "pwd_grp_internal.c"
270
271/**********************************************************************/
272/* TODO: audit & stop using these in bbox, they pull in static buffers */
273
274/* This one has many users */
275struct passwd *getpwuid(uid_t uid)
276{
277 struct statics *S;
278 struct passwd *resultbuf = RESULTBUF(getpw);
279 char *buffer = BUFFER(getpw);
280 struct passwd *result;
281
282 getpwuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getpw)), &result);
283 return result; 313 return result;
284} 314}
285 315
286/* This one has many users */ 316static int massage_data_for_r_func(struct passdb *db,
287struct group *getgrgid(gid_t gid) 317 char *buffer, size_t buflen,
318 void **result,
319 char *buf)
288{ 320{
289 struct statics *S; 321 void *result_buf = *result;
290 struct group *resultbuf = RESULTBUF(getgr);
291 char *buffer = BUFFER(getgr);
292 struct group *result;
293
294 getgrgid_r(gid, resultbuf, buffer, sizeof(BUFFER(getgr)), &result);
295 return result;
296}
297
298#if 0 //ENABLE_USE_BB_SHADOW
299/* This function is non-standard and is currently not built. It seems
300 * to have been created as a reentrant version of the non-standard
301 * functions getspuid. Why getspuid was added, I do not know. */
302int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf,
303 char *__restrict buffer, size_t buflen,
304 struct spwd **__restrict result)
305{
306 int rv;
307 struct passwd *pp;
308 struct passwd password;
309 char pwd_buff[PWD_BUFFER_SIZE];
310
311 *result = NULL; 322 *result = NULL;
312 rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp); 323 if (buf) {
313 if (!rv) { 324 if (S.string_size > buflen) {
314 rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result); 325 errno = ERANGE;
326 } else {
327 memcpy(buffer, buf, S.string_size);
328 *result = convert_to_struct(db, buffer, result_buf);
329 }
330 free(buf);
315 } 331 }
316 332 /* "The reentrant functions return zero on success.
317 return rv; 333 * In case of error, an error number is returned."
334 * NB: not finding the record is also a "success" here:
335 */
336 return errno;
318} 337}
319 338
320/* This function is non-standard and is currently not built. 339static void* massage_data_for_non_r_func(struct passdb *db, char *buf)
321 * Why it was added, I do not know. */
322struct spwd *getspuid(uid_t uid)
323{ 340{
324 struct statics *S; 341 if (!buf)
325 struct spwd *resultbuf = RESULTBUF(getsp); 342 return NULL;
326 char *buffer = BUFFER(getsp);
327 struct spwd *result;
328 343
329 getspuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getsp)), &result); 344 free(db->malloced);
330 return result; 345 /* We enlarge buf and move string data up, freeing space
346 * for struct passwd/group/spwd at the beginning. This way,
347 * entire result of getXXnam is in a single malloced block.
348 * This enables easy creation of xmalloc_getpwnam() API.
349 */
350 db->malloced = buf = xrealloc(buf, db->size_of + S.string_size);
351 memmove(buf + db->size_of, buf, S.string_size);
352 return convert_to_struct(db, buf + db->size_of, buf);
331} 353}
332#endif
333 354
334/* This one has many users */ 355/****** getXXnam/id_r */
335struct passwd *getpwnam(const char *name) 356
357static int FAST_FUNC getXXnam_r(const char *name, uintptr_t db_and_field_pos,
358 char *buffer, size_t buflen,
359 void *result)
336{ 360{
337 struct statics *S; 361 char *buf;
338 struct passwd *resultbuf = RESULTBUF(getpw); 362 struct passdb *db = &get_S()->db[db_and_field_pos >> 2];
339 char *buffer = BUFFER(getpw);
340 struct passwd *result;
341 363
342 getpwnam_r(name, resultbuf, buffer, sizeof(BUFFER(getpw)), &result); 364 buf = parse_file(db, name, 0 /*db_and_field_pos & 3*/);
343 return result; 365 /* "db_and_field_pos & 3" is commented out since so far we don't implement
366 * getXXXid_r() functions which would use that to pass 2 here */
367
368 return massage_data_for_r_func(db, buffer, buflen, result, buf);
344} 369}
345 370
346/* This one has many users */ 371int FAST_FUNC getpwnam_r(const char *name, struct passwd *struct_buf,
347struct group *getgrnam(const char *name) 372 char *buffer, size_t buflen,
373 struct passwd **result)
348{ 374{
349 struct statics *S; 375 /* Why the "store buffer address in result" trick?
350 struct group *resultbuf = RESULTBUF(getgr); 376 * This way, getXXnam_r has the same ABI signature as getpwnam_r,
351 char *buffer = BUFFER(getgr); 377 * hopefully compiler can optimize tail call better in this case.
352 struct group *result; 378 */
353 379 *result = struct_buf;
354 getgrnam_r(name, resultbuf, buffer, sizeof(BUFFER(getgr)), &result); 380 return getXXnam_r(name, (0 << 2) + 0, buffer, buflen, result);
355 return result;
356} 381}
357 382#if ENABLE_USE_BB_SHADOW
358#if 0 //ENABLE_USE_BB_SHADOW 383int FAST_FUNC getspnam_r(const char *name, struct spwd *struct_buf, char *buffer, size_t buflen,
359struct spwd *getspnam(const char *name) 384 struct spwd **result)
360{ 385{
361 struct statics *S; 386 *result = struct_buf;
362 struct spwd *resultbuf = RESULTBUF(getsp); 387 return getXXnam_r(name, (2 << 2) + 0, buffer, buflen, result);
363 char *buffer = BUFFER(getsp);
364 struct spwd *result;
365
366 getspnam_r(name, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
367 return result;
368} 388}
369#endif 389#endif
370 390
371/**********************************************************************/ 391#ifdef UNUSED
372 392/****** getXXent_r */
373/* FIXME: we don't have such CONFIG_xx - ?! */
374
375#if defined CONFIG_USE_BB_THREADSAFE_SHADOW && defined PTHREAD_MUTEX_INITIALIZER
376static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
377# define LOCK pthread_mutex_lock(&mylock)
378# define UNLOCK pthread_mutex_unlock(&mylock);
379#else
380# define LOCK ((void) 0)
381# define UNLOCK ((void) 0)
382#endif
383 393
384static FILE *pwf /*= NULL*/; 394static int FAST_FUNC getXXent_r(uintptr_t db_idx, char *buffer, size_t buflen,
385void setpwent(void) 395 void *result)
386{ 396{
387 LOCK; 397 char *buf;
388 if (pwf) { 398 struct passdb *db = &get_S()->db[db_idx];
389 rewind(pwf); 399
400 if (!db->fp) {
401 db->fp = fopen_for_read(db->filename);
402 if (!db->fp) {
403 return errno;
404 }
405 close_on_exec_on(fileno(db->fp));
390 } 406 }
391 UNLOCK; 407
408 buf = parse_common(db->fp, db, /*no search key:*/ NULL, -1);
409 if (!buf && !errno)
410 errno = ENOENT;
411 return massage_data_for_r_func(db, buffer, buflen, result, buf);
392} 412}
393 413
394void endpwent(void) 414int FAST_FUNC getpwent_r(struct passwd *struct_buf, char *buffer, size_t buflen,
415 struct passwd **result)
395{ 416{
396 LOCK; 417 *result = struct_buf;
397 if (pwf) { 418 return getXXent_r(0, buffer, buflen, result);
398 fclose(pwf);
399 pwf = NULL;
400 }
401 UNLOCK;
402} 419}
420#endif
403 421
422/****** getXXent */
404 423
405int getpwent_r(struct passwd *__restrict resultbuf, 424static void* FAST_FUNC getXXent(uintptr_t db_idx)
406 char *__restrict buffer, size_t buflen,
407 struct passwd **__restrict result)
408{ 425{
409 int rv; 426 char *buf;
410 427 struct passdb *db = &get_S()->db[db_idx];
411 LOCK;
412 *result = NULL; /* In case of error... */
413 428
414 if (!pwf) { 429 if (!db->fp) {
415 pwf = fopen_for_read(_PATH_PASSWD); 430 db->fp = fopen_for_read(db->filename);
416 if (!pwf) { 431 if (!db->fp) {
417 rv = errno; 432 return NULL;
418 goto ERR;
419 } 433 }
420 close_on_exec_on(fileno(pwf)); 434 close_on_exec_on(fileno(db->fp));
421 } 435 }
422 436
423 rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, pwf); 437 buf = parse_common(db->fp, db, /*no search key:*/ NULL, -1);
424 if (!rv) { 438 return massage_data_for_non_r_func(db, buf);
425 *result = resultbuf;
426 }
427
428 ERR:
429 UNLOCK;
430 return rv;
431} 439}
432 440
433static FILE *grf /*= NULL*/; 441struct passwd* FAST_FUNC getpwent(void)
434void setgrent(void)
435{ 442{
436 LOCK; 443 return getXXent(0);
437 if (grf) {
438 rewind(grf);
439 }
440 UNLOCK;
441} 444}
442 445
443void endgrent(void) 446/****** getXXnam/id */
444{
445 LOCK;
446 if (grf) {
447 fclose(grf);
448 grf = NULL;
449 }
450 UNLOCK;
451}
452 447
453int getgrent_r(struct group *__restrict resultbuf, 448static void* FAST_FUNC getXXnam(const char *name, unsigned db_and_field_pos)
454 char *__restrict buffer, size_t buflen,
455 struct group **__restrict result)
456{ 449{
457 int rv; 450 char *buf;
458 451 struct passdb *db = &get_S()->db[db_and_field_pos >> 2];
459 LOCK;
460 *result = NULL; /* In case of error... */
461 452
462 if (!grf) { 453 buf = parse_file(db, name, db_and_field_pos & 3);
463 grf = fopen_for_read(_PATH_GROUP); 454 return massage_data_for_non_r_func(db, buf);
464 if (!grf) {
465 rv = errno;
466 goto ERR;
467 }
468 close_on_exec_on(fileno(grf));
469 }
470
471 rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, grf);
472 if (!rv) {
473 *result = resultbuf;
474 }
475
476 ERR:
477 UNLOCK;
478 return rv;
479} 455}
480 456
481#ifdef UNUSED_FOR_NOW 457struct passwd* FAST_FUNC getpwnam(const char *name)
482#if ENABLE_USE_BB_SHADOW
483static FILE *spf /*= NULL*/;
484void setspent(void)
485{ 458{
486 LOCK; 459 return getXXnam(name, (0 << 2) + 0);
487 if (spf) {
488 rewind(spf);
489 }
490 UNLOCK;
491} 460}
492 461struct group* FAST_FUNC getgrnam(const char *name)
493void endspent(void)
494{ 462{
495 LOCK; 463 return getXXnam(name, (1 << 2) + 0);
496 if (spf) {
497 fclose(spf);
498 spf = NULL;
499 }
500 UNLOCK;
501} 464}
502 465struct passwd* FAST_FUNC getpwuid(uid_t id)
503int getspent_r(struct spwd *resultbuf, char *buffer,
504 size_t buflen, struct spwd **result)
505{ 466{
506 int rv; 467 return getXXnam(utoa(id), (0 << 2) + 2);
507
508 LOCK;
509 *result = NULL; /* In case of error... */
510
511 if (!spf) {
512 spf = fopen_for_read(_PATH_SHADOW);
513 if (!spf) {
514 rv = errno;
515 goto ERR;
516 }
517 close_on_exec_on(fileno(spf));
518 }
519
520 rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, spf);
521 if (!rv) {
522 *result = resultbuf;
523 }
524
525 ERR:
526 UNLOCK;
527 return rv;
528} 468}
529#endif 469struct group* FAST_FUNC getgrgid(gid_t id)
530#endif /* UNUSED_FOR_NOW */
531
532#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
533struct passwd *getpwent(void)
534{ 470{
535 static char line_buff[PWD_BUFFER_SIZE]; 471 return getXXnam(utoa(id), (1 << 2) + 2);
536 static struct passwd pwd;
537 struct passwd *result;
538
539 getpwent_r(&pwd, line_buff, sizeof(line_buff), &result);
540 return result;
541} 472}
542 473
543struct group *getgrent(void) 474/****** end/setXXend */
544{
545 static char line_buff[GRP_BUFFER_SIZE];
546 static struct group gr;
547 struct group *result;
548 475
549 getgrent_r(&gr, line_buff, sizeof(line_buff), &result); 476void FAST_FUNC endpwent(void)
550 return result; 477{
478 if (has_S && S.db[0].fp) {
479 fclose(S.db[0].fp);
480 S.db[0].fp = NULL;
481 }
551} 482}
552 483void FAST_FUNC setpwent(void)
553#if ENABLE_USE_BB_SHADOW
554struct spwd *getspent(void)
555{ 484{
556 static char line_buff[PWD_BUFFER_SIZE]; 485 if (has_S && S.db[0].fp) {
557 static struct spwd spwd; 486 rewind(S.db[0].fp);
558 struct spwd *result; 487 }
559
560 getspent_r(&spwd, line_buff, sizeof(line_buff), &result);
561 return result;
562} 488}
563 489void FAST_FUNC endgrent(void)
564struct spwd *sgetspent(const char *string)
565{ 490{
566 static char line_buff[PWD_BUFFER_SIZE]; 491 if (has_S && S.db[1].fp) {
567 static struct spwd spwd; 492 fclose(S.db[1].fp);
568 struct spwd *result; 493 S.db[1].fp = NULL;
569 494 }
570 sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result);
571 return result;
572} 495}
573#endif
574#endif /* UNUSED_SINCE_WE_AVOID_STATIC_BUFS */
575 496
576static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gid) 497/****** initgroups and getgrouplist */
498
499static gid_t* FAST_FUNC getgrouplist_internal(int *ngroups_ptr,
500 const char *user, gid_t gid)
577{ 501{
578 FILE *grfile; 502 FILE *fp;
579 gid_t *group_list; 503 gid_t *group_list;
580 int ngroups; 504 int ngroups;
581 struct group group;
582 char buff[PWD_BUFFER_SIZE];
583 505
584 /* We alloc space for 8 gids at a time. */ 506 /* We alloc space for 8 gids at a time. */
585 group_list = xmalloc(8 * sizeof(group_list[0])); 507 group_list = xzalloc(8 * sizeof(group_list[0]));
586 group_list[0] = gid; 508 group_list[0] = gid;
587 ngroups = 1; 509 ngroups = 1;
588 510
589 grfile = fopen_for_read(_PATH_GROUP); 511 fp = fopen_for_read(_PATH_GROUP);
590 if (grfile) { 512 if (fp) {
591 while (!bb__pgsreader(bb__parsegrent, &group, buff, sizeof(buff), grfile)) { 513 struct passdb *db = &get_S()->db[1];
514 char *buf;
515 while ((buf = parse_common(fp, db, NULL, -1)) != NULL) {
592 char **m; 516 char **m;
593 assert(group.gr_mem); /* Must have at least a NULL terminator. */ 517 struct group group;
518 if (!convert_to_struct(db, buf, &group))
519 goto next;
594 if (group.gr_gid == gid) 520 if (group.gr_gid == gid)
595 continue; 521 goto next;
596 for (m = group.gr_mem; *m; m++) { 522 for (m = group.gr_mem; *m; m++) {
597 if (strcmp(*m, user) != 0) 523 if (strcmp(*m, user) != 0)
598 continue; 524 continue;
599 group_list = xrealloc_vector(group_list, /*8=2^3:*/ 3, ngroups); 525 group_list = xrealloc_vector(group_list, /*8=2^3:*/ 3, ngroups);
600 group_list[ngroups++] = group.gr_gid; 526 group_list[ngroups++] = group.gr_gid;
601 break; 527 goto next;
602 } 528 }
529 next:
530 free(buf);
603 } 531 }
604 fclose(grfile); 532 fclose(fp);
605 } 533 }
606 *ngroups_ptr = ngroups; 534 *ngroups_ptr = ngroups;
607 return group_list; 535 return group_list;
608} 536}
609 537
610int initgroups(const char *user, gid_t gid) 538int FAST_FUNC initgroups(const char *user, gid_t gid)
611{ 539{
612 int ngroups; 540 int ngroups;
613 gid_t *group_list = getgrouplist_internal(&ngroups, user, gid); 541 gid_t *group_list = getgrouplist_internal(&ngroups, user, gid);
@@ -617,7 +545,7 @@ int initgroups(const char *user, gid_t gid)
617 return ngroups; 545 return ngroups;
618} 546}
619 547
620int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups) 548int FAST_FUNC getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
621{ 549{
622 int ngroups_old = *ngroups; 550 int ngroups_old = *ngroups;
623 gid_t *group_list = getgrouplist_internal(ngroups, user, gid); 551 gid_t *group_list = getgrouplist_internal(ngroups, user, gid);
@@ -631,409 +559,3 @@ int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
631 free(group_list); 559 free(group_list);
632 return ngroups_old; 560 return ngroups_old;
633} 561}
634
635#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
636int putpwent(const struct passwd *__restrict p, FILE *__restrict f)
637{
638 int rv = -1;
639
640#if 0
641 /* glibc does this check */
642 if (!p || !f) {
643 errno = EINVAL;
644 return rv;
645 }
646#endif
647
648 /* No extra thread locking is needed above what fprintf does. */
649 if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n",
650 p->pw_name, p->pw_passwd,
651 (unsigned long)(p->pw_uid),
652 (unsigned long)(p->pw_gid),
653 p->pw_gecos, p->pw_dir, p->pw_shell) >= 0
654 ) {
655 rv = 0;
656 }
657
658 return rv;
659}
660
661int putgrent(const struct group *__restrict p, FILE *__restrict f)
662{
663 int rv = -1;
664
665#if 0
666 /* glibc does this check */
667 if (!p || !f) {
668 errno = EINVAL;
669 return rv;
670 }
671#endif
672
673 if (fprintf(f, "%s:%s:%lu:",
674 p->gr_name, p->gr_passwd,
675 (unsigned long)(p->gr_gid)) >= 0
676 ) {
677 static const char format[] ALIGN1 = ",%s";
678
679 char **m;
680 const char *fmt;
681
682 fmt = format + 1;
683
684 assert(p->gr_mem);
685 m = p->gr_mem;
686
687 while (1) {
688 if (!*m) {
689 if (fputc('\n', f) >= 0) {
690 rv = 0;
691 }
692 break;
693 }
694 if (fprintf(f, fmt, *m) < 0) {
695 break;
696 }
697 m++;
698 fmt = format;
699 }
700 }
701
702 return rv;
703}
704#endif
705
706#if ENABLE_USE_BB_SHADOW
707#ifdef UNUSED_FOR_NOW
708static const unsigned char put_sp_off[] ALIGN1 = {
709 offsetof(struct spwd, sp_lstchg), /* 2 - not a char ptr */
710 offsetof(struct spwd, sp_min), /* 3 - not a char ptr */
711 offsetof(struct spwd, sp_max), /* 4 - not a char ptr */
712 offsetof(struct spwd, sp_warn), /* 5 - not a char ptr */
713 offsetof(struct spwd, sp_inact), /* 6 - not a char ptr */
714 offsetof(struct spwd, sp_expire) /* 7 - not a char ptr */
715};
716
717int putspent(const struct spwd *p, FILE *stream)
718{
719 const char *fmt;
720 long x;
721 int i;
722 int rv = -1;
723
724 /* Unlike putpwent and putgrent, glibc does not check the args. */
725 if (fprintf(stream, "%s:%s:", p->sp_namp,
726 (p->sp_pwdp ? p->sp_pwdp : "")) < 0
727 ) {
728 goto DO_UNLOCK;
729 }
730
731 for (i = 0; i < sizeof(put_sp_off); i++) {
732 fmt = "%ld:";
733 x = *(long *)((char *)p + put_sp_off[i]);
734 if (x == -1) {
735 fmt += 3;
736 }
737 if (fprintf(stream, fmt, x) < 0) {
738 goto DO_UNLOCK;
739 }
740 }
741
742 if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) {
743 goto DO_UNLOCK;
744 }
745
746 if (fputc('\n', stream) > 0) {
747 rv = 0;
748 }
749
750 DO_UNLOCK:
751 return rv;
752}
753#endif
754#endif /* USE_BB_SHADOW */
755
756/**********************************************************************/
757/* Internal functions */
758/**********************************************************************/
759
760static const unsigned char pw_off[] ALIGN1 = {
761 offsetof(struct passwd, pw_name), /* 0 */
762 offsetof(struct passwd, pw_passwd), /* 1 */
763 offsetof(struct passwd, pw_uid), /* 2 - not a char ptr */
764 offsetof(struct passwd, pw_gid), /* 3 - not a char ptr */
765 offsetof(struct passwd, pw_gecos), /* 4 */
766 offsetof(struct passwd, pw_dir), /* 5 */
767 offsetof(struct passwd, pw_shell) /* 6 */
768};
769
770static int FAST_FUNC bb__parsepwent(void *data, char *line)
771{
772 char *endptr;
773 char *p;
774 int i;
775
776 i = 0;
777 while (1) {
778 p = (char *) data + pw_off[i];
779
780 if (i < 2 || i > 3) {
781 *((char **) p) = line;
782 if (i == 6) {
783 return 0;
784 }
785 /* NOTE: glibc difference - glibc allows omission of
786 * ':' seperators after the gid field if all remaining
787 * entries are empty. We require all separators. */
788 line = strchr(line, ':');
789 if (!line) {
790 break;
791 }
792 } else {
793 unsigned long t = strtoul(line, &endptr, 10);
794 /* Make sure we had at least one digit, and that the
795 * failing char is the next field seperator ':'. See
796 * glibc difference note above. */
797 /* TODO: Also check for leading whitespace? */
798 if ((endptr == line) || (*endptr != ':')) {
799 break;
800 }
801 line = endptr;
802 if (i & 1) { /* i == 3 -- gid */
803 *((gid_t *) p) = t;
804 } else { /* i == 2 -- uid */
805 *((uid_t *) p) = t;
806 }
807 }
808
809 *line++ = '\0';
810 i++;
811 } /* while (1) */
812
813 return -1;
814}
815
816/**********************************************************************/
817
818static const unsigned char gr_off[] ALIGN1 = {
819 offsetof(struct group, gr_name), /* 0 */
820 offsetof(struct group, gr_passwd), /* 1 */
821 offsetof(struct group, gr_gid) /* 2 - not a char ptr */
822};
823
824static int FAST_FUNC bb__parsegrent(void *data, char *line)
825{
826 char *endptr;
827 char *p;
828 int i;
829 char **members;
830 char *end_of_buf;
831
832 end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */
833 i = 0;
834 while (1) {
835 p = (char *) data + gr_off[i];
836
837 if (i < 2) {
838 *((char **) p) = line;
839 line = strchr(line, ':');
840 if (!line) {
841 break;
842 }
843 *line++ = '\0';
844 i++;
845 } else {
846 *((gid_t *) p) = strtoul(line, &endptr, 10);
847
848 /* NOTE: glibc difference - glibc allows omission of the
849 * trailing colon when there is no member list. We treat
850 * this as an error. */
851
852 /* Make sure we had at least one digit, and that the
853 * failing char is the next field seperator ':'. See
854 * glibc difference note above. */
855 if ((endptr == line) || (*endptr != ':')) {
856 break;
857 }
858
859 i = 1; /* Count terminating NULL ptr. */
860 p = endptr;
861
862 if (p[1]) { /* We have a member list to process. */
863 /* Overwrite the last ':' with a ',' before counting.
864 * This allows us to (1) test for initial ','
865 * and (2) adds one ',' so that the number of commas
866 * equals the member count. */
867 *p = ',';
868 do {
869 /* NOTE: glibc difference - glibc allows and trims leading
870 * (but not trailing) space. We treat this as an error. */
871 /* NOTE: glibc difference - glibc allows consecutive and
872 * trailing commas, and ignores "empty string" users. We
873 * treat this as an error. */
874 if (*p == ',') {
875 ++i;
876 *p = 0; /* nul-terminate each member string. */
877 if (!*++p || (*p == ',') || isspace(*p)) {
878 goto ERR;
879 }
880 }
881 } while (*++p);
882 }
883
884 /* Now align (p+1), rounding up. */
885 /* Assumes sizeof(char **) is a power of 2. */
886 members = (char **)( (((intptr_t) p) + sizeof(char **))
887 & ~((intptr_t)(sizeof(char **) - 1)) );
888
889 if (((char *)(members + i)) > end_of_buf) { /* No space. */
890 break;
891 }
892
893 ((struct group *) data)->gr_mem = members;
894
895 if (--i) {
896 p = endptr; /* Pointing to char prior to first member. */
897 while (1) {
898 *members++ = ++p;
899 if (!--i)
900 break;
901 while (*++p)
902 continue;
903 }
904 }
905 *members = NULL;
906
907 return 0;
908 }
909 } /* while (1) */
910
911 ERR:
912 return -1;
913}
914
915/**********************************************************************/
916
917#if ENABLE_USE_BB_SHADOW
918static const unsigned char sp_off[] ALIGN1 = {
919 offsetof(struct spwd, sp_namp), /* 0: char* */
920 offsetof(struct spwd, sp_pwdp), /* 1: char* */
921 offsetof(struct spwd, sp_lstchg), /* 2: long */
922 offsetof(struct spwd, sp_min), /* 3: long */
923 offsetof(struct spwd, sp_max), /* 4: long */
924 offsetof(struct spwd, sp_warn), /* 5: long */
925 offsetof(struct spwd, sp_inact), /* 6: long */
926 offsetof(struct spwd, sp_expire), /* 7: long */
927 offsetof(struct spwd, sp_flag) /* 8: unsigned long */
928};
929
930static int FAST_FUNC bb__parsespent(void *data, char *line)
931{
932 char *endptr;
933 char *p;
934 int i;
935
936 i = 0;
937 while (1) {
938 p = (char *) data + sp_off[i];
939 if (i < 2) {
940 *((char **) p) = line;
941 line = strchr(line, ':');
942 if (!line) {
943 break; /* error */
944 }
945 } else {
946 *((long *) p) = strtoul(line, &endptr, 10);
947 if (endptr == line) {
948 *((long *) p) = -1L;
949 }
950 line = endptr;
951 if (i == 8) {
952 if (*line != '\0') {
953 break; /* error */
954 }
955 return 0; /* all ok */
956 }
957 if (*line != ':') {
958 break; /* error */
959 }
960 }
961 *line++ = '\0';
962 i++;
963 }
964
965 return EINVAL;
966}
967#endif
968
969/**********************************************************************/
970
971/* Reads until EOF, or until it finds a line which fits in the buffer
972 * and for which the parser function succeeds.
973 *
974 * Returns 0 on success and ENOENT for end-of-file (glibc convention).
975 */
976static int bb__pgsreader(
977 int FAST_FUNC (*parserfunc)(void *d, char *line),
978 void *data,
979 char *__restrict line_buff,
980 size_t buflen,
981 FILE *f)
982{
983 int skip;
984 int rv = ERANGE;
985
986 if (buflen < PWD_BUFFER_SIZE) {
987 errno = rv;
988 return rv;
989 }
990
991 skip = 0;
992 while (1) {
993 if (!fgets(line_buff, buflen, f)) {
994 if (feof(f)) {
995 rv = ENOENT;
996 }
997 break;
998 }
999
1000 {
1001 int line_len = strlen(line_buff) - 1;
1002 if (line_len >= 0 && line_buff[line_len] == '\n') {
1003 line_buff[line_len] = '\0';
1004 } else
1005 if (line_len + 2 == buflen) {
1006 /* A start (or continuation) of overlong line */
1007 skip = 1;
1008 continue;
1009 } /* else: a last line in the file, and it has no '\n' */
1010 }
1011
1012 if (skip) {
1013 /* This "line" is a remainder of overlong line, ignore */
1014 skip = 0;
1015 continue;
1016 }
1017
1018 /* NOTE: glibc difference - glibc strips leading whitespace from
1019 * records. We do not allow leading whitespace. */
1020
1021 /* Skip empty lines, comment lines, and lines with leading
1022 * whitespace. */
1023 if (line_buff[0] != '\0' && line_buff[0] != '#' && !isspace(line_buff[0])) {
1024 if (parserfunc == bb__parsegrent) {
1025 /* Do evil group hack:
1026 * The group entry parsing function needs to know where
1027 * the end of the buffer is so that it can construct the
1028 * group member ptr table. */
1029 ((struct group *) data)->gr_name = line_buff + buflen;
1030 }
1031 if (parserfunc(data, line_buff) == 0) {
1032 rv = 0;
1033 break;
1034 }
1035 }
1036 } /* while (1) */
1037
1038 return rv;
1039}
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 @@
1/* vi: set sw=4 ts=4: */
2/* Copyright (C) 2003 Manuel Novoa III
3 *
4 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
5 */
6
7/* Nov 6, 2003 Initial version.
8 *
9 * NOTE: This implementation is quite strict about requiring all
10 * field seperators. It also does not allow leading whitespace
11 * except when processing the numeric fields. glibc is more
12 * lenient. See the various glibc difference comments below.
13 *
14 * TODO:
15 * Move to dynamic allocation of (currently statically allocated)
16 * buffers; especially for the group-related functions since
17 * large group member lists will cause error returns.
18 */
19
20#ifndef GETXXKEY_R_FUNC
21#error GETXXKEY_R_FUNC is not defined!
22#endif
23
24int GETXXKEY_R_FUNC(GETXXKEY_R_KEYTYPE key,
25 GETXXKEY_R_ENTTYPE *__restrict resultbuf,
26 char *__restrict buffer, size_t buflen,
27 GETXXKEY_R_ENTTYPE **__restrict result)
28{
29 FILE *stream;
30 int rv;
31
32 *result = NULL;
33
34 stream = fopen_for_read(GETXXKEY_R_PATHNAME);
35 if (!stream)
36 return errno;
37 while (1) {
38 rv = bb__pgsreader(GETXXKEY_R_PARSER, resultbuf, buffer, buflen, stream);
39 if (!rv) {
40 if (GETXXKEY_R_TEST(resultbuf)) { /* found key? */
41 *result = resultbuf;
42 break;
43 }
44 } else {
45 if (rv == ENOENT) { /* EOF encountered */
46 rv = 0;
47 }
48 break;
49 }
50 }
51 fclose(stream);
52
53 return rv;
54}
55
56#undef GETXXKEY_R_FUNC
57#undef GETXXKEY_R_PARSER
58#undef GETXXKEY_R_ENTTYPE
59#undef GETXXKEY_R_TEST
60#undef GETXXKEY_R_KEYTYPE
61#undef GETXXKEY_R_PATHNAME
diff --git a/loginutils/deluser.c b/loginutils/deluser.c
index e39ac5506..110cd6310 100644
--- a/loginutils/deluser.c
+++ b/loginutils/deluser.c
@@ -11,9 +11,10 @@
11 */ 11 */
12 12
13//usage:#define deluser_trivial_usage 13//usage:#define deluser_trivial_usage
14//usage: "USER" 14//usage: IF_LONG_OPTS("[--remove-home] ") "USER"
15//usage:#define deluser_full_usage "\n\n" 15//usage:#define deluser_full_usage "\n\n"
16//usage: "Delete USER from the system" 16//usage: "Delete USER from the system"
17// --remove-home is self-explanatory enough to put it in --help
17 18
18//usage:#define delgroup_trivial_usage 19//usage:#define delgroup_trivial_usage
19//usage: IF_FEATURE_DEL_USER_FROM_GROUP("[USER] ")"GROUP" 20//usage: IF_FEATURE_DEL_USER_FROM_GROUP("[USER] ")"GROUP"
@@ -37,6 +38,19 @@ int deluser_main(int argc, char **argv)
37 /* Are we deluser or delgroup? */ 38 /* Are we deluser or delgroup? */
38 int do_deluser = (ENABLE_DELUSER && (!ENABLE_DELGROUP || applet_name[3] == 'u')); 39 int do_deluser = (ENABLE_DELUSER && (!ENABLE_DELGROUP || applet_name[3] == 'u'));
39 40
41#if !ENABLE_LONG_OPTS
42 const int opt_delhome = 0;
43#else
44 int opt_delhome = 0;
45 if (do_deluser) {
46 applet_long_options =
47 "remove-home\0" No_argument "\xff";
48 opt_delhome = getopt32(argv, "");
49 argv += opt_delhome;
50 argc -= opt_delhome;
51 }
52#endif
53
40 if (geteuid() != 0) 54 if (geteuid() != 0)
41 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); 55 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
42 56
@@ -55,10 +69,14 @@ int deluser_main(int argc, char **argv)
55 case 2: 69 case 2:
56 if (do_deluser) { 70 if (do_deluser) {
57 /* "deluser USER" */ 71 /* "deluser USER" */
58 xgetpwnam(name); /* bail out if USER is wrong */ 72 struct passwd *pw;
73
74 pw = xgetpwnam(name); /* bail out if USER is wrong */
59 pfile = bb_path_passwd_file; 75 pfile = bb_path_passwd_file;
60 if (ENABLE_FEATURE_SHADOWPASSWDS) 76 if (ENABLE_FEATURE_SHADOWPASSWDS)
61 sfile = bb_path_shadow_file; 77 sfile = bb_path_shadow_file;
78 if (opt_delhome)
79 remove_file(pw->pw_dir, FILEUTILS_RECUR);
62 } else { 80 } else {
63 struct group *gr; 81 struct group *gr;
64 do_delgroup: 82 do_delgroup:
@@ -73,12 +91,11 @@ int deluser_main(int argc, char **argv)
73 if (!member) { 91 if (!member) {
74 /* "delgroup GROUP" */ 92 /* "delgroup GROUP" */
75 struct passwd *pw; 93 struct passwd *pw;
76 struct passwd pwent;
77 /* Check if the group is in use */ 94 /* Check if the group is in use */
78#define passwd_buf bb_common_bufsiz1 95 while ((pw = getpwent()) != NULL) {
79 while (!getpwent_r(&pwent, passwd_buf, sizeof(passwd_buf), &pw)) { 96 if (pw->pw_gid == gr->gr_gid)
80 if (pwent.pw_gid == gr->gr_gid) 97 bb_error_msg_and_die("'%s' still has '%s' as their primary group!",
81 bb_error_msg_and_die("'%s' still has '%s' as their primary group!", pwent.pw_name, name); 98 pw->pw_name, name);
82 } 99 }
83 //endpwent(); 100 //endpwent();
84 } 101 }
@@ -97,16 +114,22 @@ int deluser_main(int argc, char **argv)
97 } 114 }
98 } while (ENABLE_FEATURE_SHADOWPASSWDS && pfile); 115 } while (ENABLE_FEATURE_SHADOWPASSWDS && pfile);
99 116
100 if (ENABLE_DELGROUP && do_deluser > 0) { 117 if (do_deluser > 0) {
101 /* "deluser USER" also should try to delete 118 /* Delete user from all groups */
102 * same-named group. IOW: do "delgroup USER" 119 if (update_passwd(bb_path_group_file, NULL, NULL, name) == -1)
103 */ 120 return EXIT_FAILURE;
121
122 if (ENABLE_DELGROUP) {
123 /* "deluser USER" also should try to delete
124 * same-named group. IOW: do "delgroup USER"
125 */
104// On debian deluser is a perl script that calls userdel. 126// On debian deluser is a perl script that calls userdel.
105// From man userdel: 127// From man userdel:
106// If USERGROUPS_ENAB is defined to yes in /etc/login.defs, userdel will 128// If USERGROUPS_ENAB is defined to yes in /etc/login.defs, userdel will
107// delete the group with the same name as the user. 129// delete the group with the same name as the user.
108 do_deluser = -1; 130 do_deluser = -1;
109 goto do_delgroup; 131 goto do_delgroup;
132 }
110 } 133 }
111 return EXIT_SUCCESS; 134 return EXIT_SUCCESS;
112 } 135 }
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)
454 else { 454 else {
455 if (safe_waitpid(child_pid, NULL, 0) == -1) 455 if (safe_waitpid(child_pid, NULL, 0) == -1)
456 bb_perror_msg("waitpid"); 456 bb_perror_msg("waitpid");
457 update_utmp(child_pid, DEAD_PROCESS, NULL, NULL, NULL); 457 update_utmp_DEAD_PROCESS(child_pid);
458 } 458 }
459 IF_PAM(login_pam_end(pamh);) 459 IF_PAM(login_pam_end(pamh);)
460 return 0; 460 return 0;
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)
438 log5("user:%s entry:%s", fileName, parser->data); 438 log5("user:%s entry:%s", fileName, parser->data);
439 439
440 /* check if line is setting MAILTO= */ 440 /* check if line is setting MAILTO= */
441 if (0 == strncmp(tokens[0], "MAILTO=", 7)) { 441 if (is_prefixed_with(tokens[0], "MAILTO=")) {
442#if ENABLE_FEATURE_CROND_CALL_SENDMAIL 442#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
443 free(mailTo); 443 free(mailTo);
444 mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL; 444 mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL;
445#endif /* otherwise just ignore such lines */ 445#endif /* otherwise just ignore such lines */
446 continue; 446 continue;
447 } 447 }
448 if (0 == strncmp(tokens[0], "SHELL=", 6)) { 448 if (is_prefixed_with(tokens[0], "SHELL=")) {
449 free(shell); 449 free(shell);
450 shell = xstrdup(&tokens[0][6]); 450 shell = xstrdup(&tokens[0][6]);
451 continue; 451 continue;
diff --git a/miscutils/dc.c b/miscutils/dc.c
index 6bcfbe249..f94d6fa6b 100644
--- a/miscutils/dc.c
+++ b/miscutils/dc.c
@@ -196,14 +196,6 @@ struct op {
196}; 196};
197 197
198static const struct op operators[] = { 198static const struct op operators[] = {
199 {"+", add},
200 {"add", add},
201 {"-", sub},
202 {"sub", sub},
203 {"*", mul},
204 {"mul", mul},
205 {"/", divide},
206 {"div", divide},
207#if ENABLE_FEATURE_DC_LIBM 199#if ENABLE_FEATURE_DC_LIBM
208 {"**", power}, 200 {"**", power},
209 {"exp", power}, 201 {"exp", power},
@@ -216,28 +208,47 @@ static const struct op operators[] = {
216 {"not", not}, 208 {"not", not},
217 {"eor", eor}, 209 {"eor", eor},
218 {"xor", eor}, 210 {"xor", eor},
211 {"+", add},
212 {"add", add},
213 {"-", sub},
214 {"sub", sub},
215 {"*", mul},
216 {"mul", mul},
217 {"/", divide},
218 {"div", divide},
219 {"p", print_no_pop}, 219 {"p", print_no_pop},
220 {"f", print_stack_no_pop}, 220 {"f", print_stack_no_pop},
221 {"o", set_output_base}, 221 {"o", set_output_base},
222}; 222};
223 223
224/* Feed the stack machine */
224static void stack_machine(const char *argument) 225static void stack_machine(const char *argument)
225{ 226{
226 char *end; 227 char *end;
227 double d; 228 double number;
228 const struct op *o; 229 const struct op *o;
229 230
230 d = strtod(argument, &end); 231 next:
231 if (end != argument && *end == '\0') { 232 number = strtod(argument, &end);
232 push(d); 233 if (end != argument) {
233 return; 234 argument = end;
235 push(number);
236 goto next;
234 } 237 }
235 238
239 /* We might have matched a digit, eventually advance the argument */
240 argument = skip_whitespace(argument);
241
242 if (*argument == '\0')
243 return;
244
236 o = operators; 245 o = operators;
237 do { 246 do {
238 if (strcmp(o->name, argument) == 0) { 247 char *after_name = is_prefixed_with(argument, o->name);
248 if (after_name) {
249 argument = after_name;
239 o->function(); 250 o->function();
240 return; 251 goto next;
241 } 252 }
242 o++; 253 o++;
243 } while (o != operators + ARRAY_SIZE(operators)); 254 } while (o != operators + ARRAY_SIZE(operators));
@@ -254,25 +265,11 @@ int dc_main(int argc UNUSED_PARAM, char **argv)
254 if (!argv[0]) { 265 if (!argv[0]) {
255 /* take stuff from stdin if no args are given */ 266 /* take stuff from stdin if no args are given */
256 char *line; 267 char *line;
257 char *cursor;
258 char *token;
259 while ((line = xmalloc_fgetline(stdin)) != NULL) { 268 while ((line = xmalloc_fgetline(stdin)) != NULL) {
260 cursor = line; 269 stack_machine(line);
261 while (1) {
262 token = skip_whitespace(cursor);
263 if (*token == '\0')
264 break;
265 cursor = skip_non_whitespace(token);
266 if (*cursor != '\0')
267 *cursor++ = '\0';
268 stack_machine(token);
269 }
270 free(line); 270 free(line);
271 } 271 }
272 } else { 272 } else {
273 // why? it breaks "dc -2 2 + p"
274 //if (argv[0][0] == '-')
275 // bb_show_usage();
276 do { 273 do {
277 stack_machine(*argv); 274 stack_machine(*argv);
278 } while (*++argv); 275 } while (*++argv);
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,
1405 int indexx; 1405 int indexx;
1406 const char *pty1; 1406 const char *pty1;
1407 const char *pty2; 1407 const char *pty2;
1408 size_t len;
1409 /* 1 to 5 "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */ 1408 /* 1 to 5 "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */
1410 static const char *const fmt[] = { 1409 static const char *const fmt[] = {
1411 NULL , 1410 NULL ,
@@ -1425,12 +1424,11 @@ const char *get_old_name(const char *devname, unsigned int namelen,
1425 }; 1424 };
1426 1425
1427 for (trans = translate_table; trans->match != NULL; ++trans) { 1426 for (trans = translate_table; trans->match != NULL; ++trans) {
1428 len = strlen(trans->match); 1427 char *after_match = is_prefixed_with(devname, trans->match);
1429 1428 if (after_match) {
1430 if (strncmp(devname, trans->match, len) == 0) {
1431 if (trans->format == NULL) 1429 if (trans->format == NULL)
1432 return devname + len; 1430 return after_match;
1433 sprintf(buffer, trans->format, devname + len); 1431 sprintf(buffer, trans->format, after_match);
1434 return buffer; 1432 return buffer;
1435 } 1433 }
1436 } 1434 }
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)
516 // handle a case when we have many buffered lines 516 // handle a case when we have many buffered lines
517 // already in the pipe 517 // already in the pipe
518 while ((num_buf = xmalloc_fgetline(fp)) != NULL) { 518 while ((num_buf = xmalloc_fgetline(fp)) != NULL) {
519 if (strncmp(num_buf, "exit", 4) == 0) { 519 if (is_prefixed_with(num_buf, "exit")) {
520 DEBUG_MESSAGE("exit"); 520 DEBUG_MESSAGE("exit");
521 break; 521 break;
522 } 522 }
diff --git a/miscutils/i2c_tools.c b/miscutils/i2c_tools.c
new file mode 100644
index 000000000..03bb03974
--- /dev/null
+++ b/miscutils/i2c_tools.c
@@ -0,0 +1,1396 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Minimal i2c-tools implementation for busybox.
4 * Parts of code ported from i2c-tools:
5 * http://www.lm-sensors.org/wiki/I2CTools.
6 *
7 * Copyright (C) 2014 by Bartosz Golaszewski <bartekgola@gmail.com>
8 *
9 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10 */
11
12//config:config I2CGET
13//config: bool "i2cget"
14//config: default y
15//config: select PLATFORM_LINUX
16//config: help
17//config: Read from I2C/SMBus chip registers.
18//config:
19//config:config I2CSET
20//config: bool "i2cset"
21//config: default y
22//config: select PLATFORM_LINUX
23//config: help
24//config: Set I2C registers.
25//config:
26//config:config I2CDUMP
27//config: bool "i2cdump"
28//config: default y
29//config: select PLATFORM_LINUX
30//config: help
31//config: Examine I2C registers.
32//config:
33//config:config I2CDETECT
34//config: bool "i2cdetect"
35//config: default y
36//config: select PLATFORM_LINUX
37//config: help
38//config: Detect I2C chips.
39//config:
40
41//applet:IF_I2CGET(APPLET(i2cget, BB_DIR_USR_SBIN, BB_SUID_DROP))
42//applet:IF_I2CSET(APPLET(i2cset, BB_DIR_USR_SBIN, BB_SUID_DROP))
43//applet:IF_I2CDUMP(APPLET(i2cdump, BB_DIR_USR_SBIN, BB_SUID_DROP))
44//applet:IF_I2CDETECT(APPLET(i2cdetect, BB_DIR_USR_SBIN, BB_SUID_DROP))
45
46//kbuild:lib-$(CONFIG_I2CGET) += i2c_tools.o
47//kbuild:lib-$(CONFIG_I2CSET) += i2c_tools.o
48//kbuild:lib-$(CONFIG_I2CDUMP) += i2c_tools.o
49//kbuild:lib-$(CONFIG_I2CDETECT) += i2c_tools.o
50
51/*
52 * Unsupported stuff:
53 *
54 * - upstream i2c-tools can also look-up i2c busses by name, we only accept
55 * numbers,
56 * - bank and bankreg parameters for i2cdump are not supported because of
57 * their limited usefulness (see i2cdump manual entry for more info),
58 * - i2cdetect doesn't look for bus info in /proc as it does in upstream, but
59 * it shouldn't be a problem in modern kernels.
60 */
61
62#include "libbb.h"
63
64/*
65 * /dev/i2c-X ioctl commands. The ioctl's parameter is always an unsigned long,
66 * except for:
67 * - I2C_FUNCS, takes pointer to an unsigned long
68 * - I2C_RDWR, takes pointer to struct i2c_rdwr_ioctl_data
69 * - I2C_SMBUS, takes pointer to struct i2c_smbus_ioctl_data
70 */
71
72/*
73 * NOTE: Slave address is 7 or 10 bits, but 10-bit addresses
74 * are not supported due to code brokenness.
75 */
76
77/* Use this slave address. */
78#define I2C_SLAVE 0x0703
79/* Use this slave address, even if it is already in use by a driver. */
80#define I2C_SLAVE_FORCE 0x0706
81/* 0 for 7 bit addrs, != 0 for 10 bit. */
82#define I2C_TENBIT 0x0704
83/* Get the adapter functionality mask. */
84#define I2C_FUNCS 0x0705
85/* Combined R/W transfer (one STOP only). */
86#define I2C_RDWR 0x0707
87/* != 0 to use PEC with SMBus. */
88#define I2C_PEC 0x0708
89/* SMBus transfer. */
90#define I2C_SMBUS 0x0720
91
92/* Structure used in the I2C_SMBUS ioctl call. */
93struct i2c_smbus_ioctl_data {
94 uint8_t read_write;
95 uint8_t command;
96 uint32_t size;
97 union i2c_smbus_data *data;
98};
99
100/* Structure used in the I2C_RDWR ioctl call. */
101struct i2c_rdwr_ioctl_data {
102 struct i2c_msg *msgs; /* Pointers to i2c_msgs. */
103 uint32_t nmsgs; /* Number of i2c_msgs. */
104};
105
106/* As specified in SMBus standard. */
107#define I2C_SMBUS_BLOCK_MAX 32
108/* Not specified but we use same structure. */
109#define I2C_SMBUS_I2C_BLOCK_MAX 32
110
111/* Data for SMBus Messages. */
112union i2c_smbus_data {
113 uint8_t byte;
114 uint16_t word;
115 /* block[0] is used for length and one more for PEC */
116 uint8_t block[I2C_SMBUS_BLOCK_MAX + 2];
117};
118
119#define I2C_RDRW_IOCTL_MAX_MSGS 42
120#define I2C_MAX_REGS 256
121
122/* Smbus_access read or write markers. */
123#define I2C_SMBUS_READ 1
124#define I2C_SMBUS_WRITE 0
125
126/* SMBus transaction types (size parameter in the below functions). */
127#define I2C_SMBUS_QUICK 0
128#define I2C_SMBUS_BYTE 1
129#define I2C_SMBUS_BYTE_DATA 2
130#define I2C_SMBUS_WORD_DATA 3
131#define I2C_SMBUS_PROC_CALL 4
132#define I2C_SMBUS_BLOCK_DATA 5
133#define I2C_SMBUS_I2C_BLOCK_BROKEN 6
134#define I2C_SMBUS_BLOCK_PROC_CALL 7
135#define I2C_SMBUS_I2C_BLOCK_DATA 8
136
137#define DETECT_MODE_AUTO 0
138#define DETECT_MODE_QUICK 1
139#define DETECT_MODE_READ 2
140
141/* Defines to determine what functionality is present. */
142#define I2C_FUNC_I2C 0x00000001
143#define I2C_FUNC_10BIT_ADDR 0x00000002
144#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004
145#define I2C_FUNC_SMBUS_PEC 0x00000008
146#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000
147#define I2C_FUNC_SMBUS_QUICK 0x00010000
148#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000
149#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000
150#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000
151#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000
152#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000
153#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000
154#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000
155#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000
156#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
157#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000
158#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000
159
160#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \
161 I2C_FUNC_SMBUS_WRITE_BYTE)
162#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \
163 I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
164#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \
165 I2C_FUNC_SMBUS_WRITE_WORD_DATA)
166#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
167 I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
168#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
169 I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)
170
171/*
172 * This is needed for ioctl_or_perror_and_die() since it only accepts pointers.
173 */
174static ALWAYS_INLINE void *itoptr(int i)
175{
176 return (void*)(intptr_t)i;
177}
178
179static int32_t i2c_smbus_access(int fd, char read_write, uint8_t cmd,
180 int size, union i2c_smbus_data *data)
181{
182 struct i2c_smbus_ioctl_data args;
183
184 args.read_write = read_write;
185 args.command = cmd;
186 args.size = size;
187 args.data = data;
188
189 return ioctl(fd, I2C_SMBUS, &args);
190}
191
192static int32_t i2c_smbus_read_byte(int fd)
193{
194 union i2c_smbus_data data;
195 int err;
196
197 err = i2c_smbus_access(fd, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data);
198 if (err < 0)
199 return err;
200
201 return data.byte;
202}
203
204#if ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP
205static int32_t i2c_smbus_write_byte(int fd, uint8_t val)
206{
207 return i2c_smbus_access(fd, I2C_SMBUS_WRITE,
208 val, I2C_SMBUS_BYTE, NULL);
209}
210
211static int32_t i2c_smbus_read_byte_data(int fd, uint8_t cmd)
212{
213 union i2c_smbus_data data;
214 int err;
215
216 err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd,
217 I2C_SMBUS_BYTE_DATA, &data);
218 if (err < 0)
219 return err;
220
221 return data.byte;
222}
223
224static int32_t i2c_smbus_read_word_data(int fd, uint8_t cmd)
225{
226 union i2c_smbus_data data;
227 int err;
228
229 err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd,
230 I2C_SMBUS_WORD_DATA, &data);
231 if (err < 0)
232 return err;
233
234 return data.word;
235}
236#endif /* ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP */
237
238#if ENABLE_I2CSET
239static int32_t i2c_smbus_write_byte_data(int file,
240 uint8_t cmd, uint8_t value)
241{
242 union i2c_smbus_data data;
243
244 data.byte = value;
245
246 return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd,
247 I2C_SMBUS_BYTE_DATA, &data);
248}
249
250static int32_t i2c_smbus_write_word_data(int file, uint8_t cmd, uint16_t value)
251{
252 union i2c_smbus_data data;
253
254 data.word = value;
255
256 return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd,
257 I2C_SMBUS_WORD_DATA, &data);
258}
259
260static int32_t i2c_smbus_write_block_data(int file, uint8_t cmd,
261 uint8_t length, const uint8_t *values)
262{
263 union i2c_smbus_data data;
264
265 if (length > I2C_SMBUS_BLOCK_MAX)
266 length = I2C_SMBUS_BLOCK_MAX;
267
268 memcpy(data.block+1, values, length);
269 data.block[0] = length;
270
271 return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd,
272 I2C_SMBUS_BLOCK_DATA, &data);
273}
274
275static int32_t i2c_smbus_write_i2c_block_data(int file, uint8_t cmd,
276 uint8_t length, const uint8_t *values)
277{
278 union i2c_smbus_data data;
279
280 if (length > I2C_SMBUS_BLOCK_MAX)
281 length = I2C_SMBUS_BLOCK_MAX;
282
283 memcpy(data.block+1, values, length);
284 data.block[0] = length;
285
286 return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd,
287 I2C_SMBUS_I2C_BLOCK_BROKEN, &data);
288}
289#endif /* ENABLE_I2CSET */
290
291#if ENABLE_I2CDUMP
292/*
293 * Returns the number of bytes read, vals must hold at
294 * least I2C_SMBUS_BLOCK_MAX bytes.
295 */
296static int32_t i2c_smbus_read_block_data(int fd, uint8_t cmd, uint8_t *vals)
297{
298 union i2c_smbus_data data;
299 int i, err;
300
301 err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd,
302 I2C_SMBUS_BLOCK_DATA, &data);
303 if (err < 0)
304 return err;
305
306 for (i = 1; i <= data.block[0]; i++)
307 *vals++ = data.block[i];
308 return data.block[0];
309}
310
311static int32_t i2c_smbus_read_i2c_block_data(int fd, uint8_t cmd,
312 uint8_t len, uint8_t *vals)
313{
314 union i2c_smbus_data data;
315 int i, err;
316
317 if (len > I2C_SMBUS_BLOCK_MAX)
318 len = I2C_SMBUS_BLOCK_MAX;
319 data.block[0] = len;
320
321 err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd,
322 len == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN :
323 I2C_SMBUS_I2C_BLOCK_DATA, &data);
324 if (err < 0)
325 return err;
326
327 for (i = 1; i <= data.block[0]; i++)
328 *vals++ = data.block[i];
329 return data.block[0];
330}
331#endif /* ENABLE_I2CDUMP */
332
333#if ENABLE_I2CDETECT
334static int32_t i2c_smbus_write_quick(int fd, uint8_t val)
335{
336 return i2c_smbus_access(fd, val, 0, I2C_SMBUS_QUICK, NULL);
337}
338#endif /* ENABLE_I2CDETECT */
339
340static int i2c_bus_lookup(const char *bus_str)
341{
342 return xstrtou_range(bus_str, 10, 0, 0xfffff);
343}
344
345#if ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP
346static int i2c_parse_bus_addr(const char *addr_str)
347{
348 /* Slave address must be in range 0x03 - 0x77. */
349 return xstrtou_range(addr_str, 16, 0x03, 0x77);
350}
351
352static void i2c_set_pec(int fd, int pec)
353{
354 ioctl_or_perror_and_die(fd, I2C_PEC,
355 itoptr(pec ? 1 : 0),
356 "can't set PEC");
357}
358#endif /* ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP */
359
360#if ENABLE_I2CGET || ENABLE_I2CSET
361static int i2c_parse_data_addr(const char *data_addr)
362{
363 /* Data address must be an 8 bit integer. */
364 return xstrtou_range(data_addr, 16, 0, 0xff);
365}
366#endif /* ENABLE_I2CGET || ENABLE_I2CSET */
367
368/*
369 * Opens the device file associated with given i2c bus.
370 *
371 * Upstream i2c-tools also support opening devices by i2c bus name
372 * but we drop it here for size reduction.
373 */
374static int i2c_dev_open(int i2cbus)
375{
376 char filename[sizeof("/dev/i2c-%d") + sizeof(int)*3];
377 int fd;
378
379 sprintf(filename, "/dev/i2c-%d", i2cbus);
380 fd = open(filename, O_RDWR);
381 if (fd < 0) {
382 filename[8] = '/'; /* change to "/dev/i2c/%d" */
383 fd = xopen(filename, O_RDWR);
384 }
385
386 return fd;
387}
388
389static void i2c_set_slave_addr(int fd, int addr, int force)
390{
391 ioctl_or_perror_and_die(fd, force ? I2C_SLAVE_FORCE : I2C_SLAVE,
392 itoptr(addr),
393 "can't set address to 0x%02x", addr);
394}
395
396/* Size reducing helpers for xxx_check_funcs(). */
397static void get_funcs_matrix(int fd, unsigned long *funcs)
398{
399 ioctl_or_perror_and_die(fd, I2C_FUNCS, funcs,
400 "can't get adapter functionality matrix");
401}
402
403#if ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP
404static void check_funcs_test_end(int funcs, int pec, const char *err)
405{
406 if (pec && !(funcs & (I2C_FUNC_SMBUS_PEC | I2C_FUNC_I2C)))
407 bb_error_msg("warning: adapter does not support PEC");
408
409 if (err)
410 bb_error_msg_and_die(
411 "adapter has no %s capability", err);
412}
413#endif /* ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP */
414
415/*
416 * The below functions emit an error message and exit if the adapter doesn't
417 * support desired functionalities.
418 */
419#if ENABLE_I2CGET || ENABLE_I2CDUMP
420static void check_read_funcs(int fd, int mode, int data_addr, int pec)
421{
422 unsigned long funcs;
423 const char *err = NULL;
424
425 get_funcs_matrix(fd, &funcs);
426 switch (mode) {
427 case I2C_SMBUS_BYTE:
428 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE)) {
429 err = "SMBus receive byte";
430 break;
431 }
432 if (data_addr >= 0 && !(funcs & I2C_FUNC_SMBUS_WRITE_BYTE))
433 err = "SMBus send byte";
434 break;
435 case I2C_SMBUS_BYTE_DATA:
436 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
437 err = "SMBus read byte";
438 break;
439 case I2C_SMBUS_WORD_DATA:
440 if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
441 err = "SMBus read word";
442 break;
443#if ENABLE_I2CDUMP
444 case I2C_SMBUS_BLOCK_DATA:
445 if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA))
446 err = "SMBus block read";
447 break;
448
449 case I2C_SMBUS_I2C_BLOCK_DATA:
450 if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK))
451 err = "I2C block read";
452 break;
453#endif /* ENABLE_I2CDUMP */
454 default:
455 bb_error_msg_and_die("Programmer goofed!");
456 }
457 check_funcs_test_end(funcs, pec, err);
458}
459#endif /* ENABLE_I2CGET || ENABLE_I2CDUMP */
460
461#if ENABLE_I2CSET
462static void check_write_funcs(int fd, int mode, int pec)
463{
464 unsigned long funcs;
465 const char *err = NULL;
466
467 get_funcs_matrix(fd, &funcs);
468 switch (mode) {
469 case I2C_SMBUS_BYTE:
470 if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE))
471 err = "SMBus send byte";
472 break;
473
474 case I2C_SMBUS_BYTE_DATA:
475 if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
476 err = "SMBus write byte";
477 break;
478
479 case I2C_SMBUS_WORD_DATA:
480 if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA))
481 err = "SMBus write word";
482 break;
483
484 case I2C_SMBUS_BLOCK_DATA:
485 if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
486 err = "SMBus block write";
487 break;
488 case I2C_SMBUS_I2C_BLOCK_DATA:
489 if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
490 err = "I2C block write";
491 break;
492 }
493 check_funcs_test_end(funcs, pec, err);
494}
495#endif /* ENABLE_I2CSET */
496
497static void confirm_or_abort(void)
498{
499 fprintf(stderr, "Continue? [y/N] ");
500 fflush_all();
501 if (!bb_ask_confirmation())
502 bb_error_msg_and_die("aborting");
503}
504
505/*
506 * Return only if user confirms the action, abort otherwise.
507 *
508 * The messages displayed here are much less elaborate than their i2c-tools
509 * counterparts - this is done for size reduction.
510 */
511static void confirm_action(int bus_addr, int mode, int data_addr, int pec)
512{
513 bb_error_msg("WARNING! This program can confuse your I2C bus");
514
515 /* Don't let the user break his/her EEPROMs */
516 if (bus_addr >= 0x50 && bus_addr <= 0x57 && pec) {
517 bb_error_msg_and_die("this is I2C not smbus - using PEC on I2C "
518 "devices may result in data loss, aborting");
519 }
520
521 if (mode == I2C_SMBUS_BYTE && data_addr >= 0 && pec)
522 bb_error_msg("WARNING! May interpret a write byte command "
523 "with PEC as a write byte data command");
524
525 if (pec)
526 bb_error_msg("PEC checking enabled");
527
528 confirm_or_abort();
529}
530
531#if ENABLE_I2CGET
532//usage:#define i2cget_trivial_usage
533//usage: "[-f] [-y] BUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]"
534//usage:#define i2cget_full_usage "\n\n"
535//usage: "Read from I2C/SMBus chip registers\n"
536//usage: "\n I2CBUS i2c bus number"
537//usage: "\n ADDRESS 0x03 - 0x77"
538//usage: "\nMODE is:"
539//usage: "\n b read byte data (default)"
540//usage: "\n w read word data"
541//usage: "\n c write byte/read byte"
542//usage: "\n Append p for SMBus PEC"
543//usage: "\n"
544//usage: "\n -f force access"
545//usage: "\n -y disable interactive mode"
546int i2cget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
547int i2cget_main(int argc UNUSED_PARAM, char **argv)
548{
549 const unsigned opt_f = (1 << 0), opt_y = (1 << 1);
550 const char *const optstr = "fy";
551
552 int bus_num, bus_addr, data_addr = -1, status;
553 int mode = I2C_SMBUS_BYTE, pec = 0, fd;
554 unsigned opts;
555
556 opt_complementary = "-2:?4"; /* from 2 to 4 args */
557 opts = getopt32(argv, optstr);
558 argv += optind;
559
560 bus_num = i2c_bus_lookup(argv[0]);
561 bus_addr = i2c_parse_bus_addr(argv[1]);
562
563 if (argv[2]) {
564 data_addr = i2c_parse_data_addr(argv[2]);
565 mode = I2C_SMBUS_BYTE_DATA;
566 if (argv[3]) {
567 switch (argv[3][0]) {
568 case 'b': /* Already set */ break;
569 case 'w': mode = I2C_SMBUS_WORD_DATA; break;
570 case 'c': mode = I2C_SMBUS_BYTE; break;
571 default:
572 bb_error_msg("invalid mode");
573 bb_show_usage();
574 }
575 pec = argv[3][1] == 'p';
576 }
577 }
578
579 fd = i2c_dev_open(bus_num);
580 check_read_funcs(fd, mode, data_addr, pec);
581 i2c_set_slave_addr(fd, bus_addr, opts & opt_f);
582
583 if (!(opts & opt_y))
584 confirm_action(bus_addr, mode, data_addr, pec);
585
586 if (pec)
587 i2c_set_pec(fd, 1);
588
589 switch (mode) {
590 case I2C_SMBUS_BYTE:
591 if (data_addr >= 0) {
592 status = i2c_smbus_write_byte(fd, data_addr);
593 if (status < 0)
594 bb_error_msg("warning - write failed");
595 }
596 status = i2c_smbus_read_byte(fd);
597 break;
598 case I2C_SMBUS_WORD_DATA:
599 status = i2c_smbus_read_word_data(fd, data_addr);
600 break;
601 default: /* I2C_SMBUS_BYTE_DATA */
602 status = i2c_smbus_read_byte_data(fd, data_addr);
603 }
604 close(fd);
605
606 if (status < 0)
607 bb_perror_msg_and_die("read failed");
608
609 printf("0x%0*x\n", mode == I2C_SMBUS_WORD_DATA ? 4 : 2, status);
610
611 return 0;
612}
613#endif /* ENABLE_I2CGET */
614
615#if ENABLE_I2CSET
616//usage:#define i2cset_trivial_usage
617//usage: "[-f] [-y] [-m MASK] BUS CHIP-ADDR DATA-ADDR [VALUE] ... [MODE]"
618//usage:#define i2cset_full_usage "\n\n"
619//usage: "Set I2C registers\n"
620//usage: "\n I2CBUS i2c bus number"
621//usage: "\n ADDRESS 0x03 - 0x77"
622//usage: "\nMODE is:"
623//usage: "\n c byte, no value"
624//usage: "\n b byte data (default)"
625//usage: "\n w word data"
626//usage: "\n i I2C block data"
627//usage: "\n s SMBus block data"
628//usage: "\n Append p for SMBus PEC"
629//usage: "\n"
630//usage: "\n -f force access"
631//usage: "\n -y disable interactive mode"
632//usage: "\n -r read back and compare the result"
633//usage: "\n -m MASK mask specifying which bits to write"
634int i2cset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
635int i2cset_main(int argc, char **argv)
636{
637 const unsigned opt_f = (1 << 0), opt_y = (1 << 1),
638 opt_m = (1 << 2), opt_r = (1 << 3);
639 const char *const optstr = "fym:r";
640
641 int bus_num, bus_addr, data_addr, mode = I2C_SMBUS_BYTE, pec = 0;
642 int val, blen = 0, mask = 0, fd, status;
643 unsigned char block[I2C_SMBUS_BLOCK_MAX];
644 char *opt_m_arg = NULL;
645 unsigned opts;
646
647 opt_complementary = "-3"; /* from 3 to ? args */
648 opts = getopt32(argv, optstr, &opt_m_arg);
649 argv += optind;
650 argc -= optind;
651
652 bus_num = i2c_bus_lookup(argv[0]);
653 bus_addr = i2c_parse_bus_addr(argv[1]);
654 data_addr = i2c_parse_data_addr(argv[2]);
655
656 if (argv[3]) {
657 if (!argv[4] && argv[3][0] != 'c') {
658 mode = I2C_SMBUS_BYTE_DATA; /* Implicit b */
659 } else {
660 switch (argv[argc-1][0]) {
661 case 'c': /* Already set */ break;
662 case 'b': mode = I2C_SMBUS_BYTE_DATA; break;
663 case 'w': mode = I2C_SMBUS_WORD_DATA; break;
664 case 's': mode = I2C_SMBUS_BLOCK_DATA; break;
665 case 'i': mode = I2C_SMBUS_I2C_BLOCK_DATA; break;
666 default:
667 bb_error_msg("invalid mode");
668 bb_show_usage();
669 }
670
671 pec = argv[argc-1][1] == 'p';
672 if (mode == I2C_SMBUS_BLOCK_DATA ||
673 mode == I2C_SMBUS_I2C_BLOCK_DATA) {
674 if (pec && mode == I2C_SMBUS_I2C_BLOCK_DATA)
675 bb_error_msg_and_die(
676 "PEC not supported for I2C "
677 "block writes");
678 if (opts & opt_m)
679 bb_error_msg_and_die(
680 "mask not supported for block "
681 "writes");
682 }
683 }
684 }
685
686 /* Prepare the value(s) to be written according to current mode. */
687 switch (mode) {
688 case I2C_SMBUS_BYTE_DATA:
689 val = xstrtou_range(argv[3], 0, 0, 0xff);
690 break;
691 case I2C_SMBUS_WORD_DATA:
692 val = xstrtou_range(argv[3], 0, 0, 0xffff);
693 break;
694 case I2C_SMBUS_BLOCK_DATA:
695 case I2C_SMBUS_I2C_BLOCK_DATA:
696 for (blen = 3; blen < (argc - 1); blen++)
697 block[blen] = xstrtou_range(argv[blen], 0, 0, 0xff);
698 val = -1;
699 break;
700 default:
701 val = -1;
702 break;
703 }
704
705 if (opts & opt_m) {
706 mask = xstrtou_range(opt_m_arg, 0, 0,
707 (mode == I2C_SMBUS_BYTE ||
708 mode == I2C_SMBUS_BYTE_DATA) ? 0xff : 0xffff);
709 }
710
711 fd = i2c_dev_open(bus_num);
712 check_write_funcs(fd, mode, pec);
713 i2c_set_slave_addr(fd, bus_addr, opts & opt_f);
714
715 if (!(opts & opt_y))
716 confirm_action(bus_addr, mode, data_addr, pec);
717
718 /*
719 * If we're using mask - read the current value here and adjust the
720 * value to be written.
721 */
722 if (opts & opt_m) {
723 int tmpval;
724
725 switch (mode) {
726 case I2C_SMBUS_BYTE:
727 tmpval = i2c_smbus_read_byte(fd);
728 break;
729 case I2C_SMBUS_WORD_DATA:
730 tmpval = i2c_smbus_read_word_data(fd, data_addr);
731 break;
732 default:
733 tmpval = i2c_smbus_read_byte_data(fd, data_addr);
734 }
735
736 if (tmpval < 0)
737 bb_perror_msg_and_die("can't read old value");
738
739 val = (val & mask) | (tmpval & ~mask);
740
741 if (!(opts & opt_y)) {
742 bb_error_msg("old value 0x%0*x, write mask "
743 "0x%0*x, will write 0x%0*x to register "
744 "0x%02x",
745 mode == I2C_SMBUS_WORD_DATA ? 4 : 2, tmpval,
746 mode == I2C_SMBUS_WORD_DATA ? 4 : 2, mask,
747 mode == I2C_SMBUS_WORD_DATA ? 4 : 2, val,
748 data_addr);
749 confirm_or_abort();
750 }
751 }
752
753 if (pec)
754 i2c_set_pec(fd, 1);
755
756 switch (mode) {
757 case I2C_SMBUS_BYTE:
758 status = i2c_smbus_write_byte(fd, data_addr);
759 break;
760 case I2C_SMBUS_WORD_DATA:
761 status = i2c_smbus_write_word_data(fd, data_addr, val);
762 break;
763 case I2C_SMBUS_BLOCK_DATA:
764 status = i2c_smbus_write_block_data(fd, data_addr,
765 blen, block);
766 break;
767 case I2C_SMBUS_I2C_BLOCK_DATA:
768 status = i2c_smbus_write_i2c_block_data(fd, data_addr,
769 blen, block);
770 break;
771 default: /* I2C_SMBUS_BYTE_DATA */
772 status = i2c_smbus_write_byte_data(fd, data_addr, val);
773 break;
774 }
775 if (status < 0)
776 bb_perror_msg_and_die("write failed");
777
778 if (pec)
779 i2c_set_pec(fd, 0); /* Clear PEC. */
780
781 /* No readback required - we're done. */
782 if (!(opts & opt_r))
783 return 0;
784
785 switch (mode) {
786 case I2C_SMBUS_BYTE:
787 status = i2c_smbus_read_byte(fd);
788 val = data_addr;
789 break;
790 case I2C_SMBUS_WORD_DATA:
791 status = i2c_smbus_read_word_data(fd, data_addr);
792 break;
793 default: /* I2C_SMBUS_BYTE_DATA */
794 status = i2c_smbus_read_byte_data(fd, data_addr);
795 }
796
797 if (status < 0) {
798 printf("Warning - readback failed\n");
799 } else
800 if (status != val) {
801 printf("Warning - data mismatch - wrote "
802 "0x%0*x, read back 0x%0*x\n",
803 mode == I2C_SMBUS_WORD_DATA ? 4 : 2, val,
804 mode == I2C_SMBUS_WORD_DATA ? 4 : 2, status);
805 } else {
806 printf("Value 0x%0*x written, readback matched\n",
807 mode == I2C_SMBUS_WORD_DATA ? 4 : 2, val);
808 }
809
810 return 0;
811}
812#endif /* ENABLE_I2CSET */
813
814#if ENABLE_I2CDUMP
815//usage:#define i2cdump_trivial_usage
816//usage: "[-f] [-r FIRST-LAST] [-y] BUS ADDR [MODE]"
817//usage:#define i2cdump_full_usage "\n\n"
818//usage: "Examine I2C registers\n"
819//usage: "\n I2CBUS i2c bus number"
820//usage: "\n ADDRESS 0x03 - 0x77"
821//usage: "\nMODE is:"
822//usage: "\n b byte (default)"
823//usage: "\n w word"
824//usage: "\n W word on even register addresses"
825//usage: "\n i I2C block"
826//usage: "\n s SMBus block"
827//usage: "\n c consecutive byte"
828//usage: "\n Append p for SMBus PEC"
829//usage: "\n"
830//usage: "\n -f force access"
831//usage: "\n -y disable interactive mode"
832//usage: "\n -r limit the number of registers being accessed"
833int i2cdump_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
834int i2cdump_main(int argc UNUSED_PARAM, char **argv)
835{
836 const unsigned opt_f = (1 << 0), opt_y = (1 << 1),
837 opt_r = (1 << 2);
838 const char *const optstr = "fyr:";
839
840 int bus_num, bus_addr, mode = I2C_SMBUS_BYTE_DATA, even = 0, pec = 0;
841 unsigned first = 0x00, last = 0xff;
842 int fd, i, j, res, blen = 0, tmp;
843 unsigned char cblock[I2C_SMBUS_BLOCK_MAX + I2C_MAX_REGS];
844 unsigned char block[I2C_SMBUS_BLOCK_MAX];
845 char *opt_r_str, *dash;
846 unsigned opts;
847
848 opt_complementary = "-2:?3"; /* from 2 to 3 args */
849 opts = getopt32(argv, optstr, &opt_r_str);
850 argv += optind;
851
852 bus_num = i2c_bus_lookup(argv[0]);
853 bus_addr = i2c_parse_bus_addr(argv[1]);
854
855 if (argv[2]) {
856 switch (argv[2][0]) {
857 case 'b': /* Already set */ break;
858 case 'c': mode = I2C_SMBUS_BYTE; break;
859 case 'w': mode = I2C_SMBUS_WORD_DATA; break;
860 case 'W':
861 mode = I2C_SMBUS_WORD_DATA;
862 even = 1;
863 break;
864 case 's': mode = I2C_SMBUS_BLOCK_DATA; break;
865 case 'i': mode = I2C_SMBUS_I2C_BLOCK_DATA; break;
866 default:
867 bb_error_msg_and_die("invalid mode");
868 }
869
870 if (argv[2][1] == 'p') {
871 if (argv[2][0] == 'W' || argv[2][0] == 'i') {
872 bb_error_msg_and_die(
873 "pec not supported for -W and -i");
874 } else {
875 pec = 1;
876 }
877 }
878 }
879
880 if (opts & opt_r) {
881 first = strtol(opt_r_str, &dash, 0);
882 if (dash == opt_r_str || *dash != '-' || first > 0xff)
883 bb_error_msg_and_die("invalid range");
884 last = xstrtou_range(++dash, 0, first, 0xff);
885
886 /* Range is not available for every mode */
887 switch (mode) {
888 case I2C_SMBUS_BYTE:
889 case I2C_SMBUS_BYTE_DATA:
890 break;
891 case I2C_SMBUS_WORD_DATA:
892 if (!even || (!(first % 2) && last % 2))
893 break;
894 /* Fall through */
895 default:
896 bb_error_msg_and_die(
897 "range not compatible with selected mode");
898 }
899 }
900
901 fd = i2c_dev_open(bus_num);
902 check_read_funcs(fd, mode, -1 /* data_addr */, pec);
903 i2c_set_slave_addr(fd, bus_addr, opts & opt_f);
904
905 if (pec)
906 i2c_set_pec(fd, 1);
907
908 if (!(opts & opt_y))
909 confirm_action(bus_addr, mode, -1 /* data_addr */, pec);
910
911 /* All but word data */
912 if (mode != I2C_SMBUS_WORD_DATA || even) {
913 /*
914 * FIXME This section has been ported from upstream i2cdump.
915 * It has been reworked a bit but is still pretty spaghetti
916 * and needs splitting into several functions.
917 */
918 if (mode == I2C_SMBUS_BLOCK_DATA ||
919 mode == I2C_SMBUS_I2C_BLOCK_DATA) {
920 res = i2c_smbus_read_block_data(fd, 0, cblock);
921 blen = res;
922 } else {
923 for (res = 0; res < I2C_MAX_REGS; res += tmp) {
924 tmp = i2c_smbus_read_i2c_block_data(
925 fd, res, I2C_SMBUS_BLOCK_MAX,
926 cblock + res);
927 if (tmp < 0) {
928 bb_error_msg_and_die(
929 "block read failed");
930 }
931 }
932 if (res >= I2C_MAX_REGS)
933 res = I2C_MAX_REGS;
934 for (i = 0; i < res; i++)
935 block[i] = cblock[i];
936 if (mode != I2C_SMBUS_BLOCK_DATA)
937 for (i = res; i < I2C_MAX_REGS; i++)
938 cblock[i] = -1;
939 }
940
941 if (mode == I2C_SMBUS_BYTE) {
942 res = i2c_smbus_write_byte(fd, first);
943 if (res < 0)
944 bb_perror_msg_and_die(
945 "write start address failed");
946 }
947
948 printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f"
949 " 0123456789abcdef\n");
950
951 for (i = 0; i < I2C_MAX_REGS; i += 0x10) {
952 if (mode == I2C_SMBUS_BLOCK_DATA && i >= blen)
953 break;
954 if (i/16 < first/16)
955 continue;
956 if (i/16 > last/16)
957 break;
958
959 printf("%02x: ", i);
960 for (j = 0; j < 16; j++) {
961 fflush_all();
962 /* Skip unwanted registers */
963 if (i+j < first || i+j > last) {
964 printf(" ");
965 if (mode == I2C_SMBUS_WORD_DATA) {
966 printf(" ");
967 j++;
968 }
969 continue;
970 }
971
972 switch (mode) {
973 case I2C_SMBUS_BYTE_DATA:
974 res = i2c_smbus_read_byte_data(fd, i+j);
975 block[i+j] = res;
976 break;
977 case I2C_SMBUS_WORD_DATA:
978 res = i2c_smbus_read_word_data(fd, i+j);
979 if (res < 0) {
980 block[i+j] = res;
981 block[i+j+1] = res;
982 } else {
983 block[i+j] = res & 0xff;
984 block[i+j+1] = res >> 8;
985 }
986 break;
987 case I2C_SMBUS_BYTE:
988 res = i2c_smbus_read_byte(fd);
989 block[i+j] = res;
990 break;
991 default:
992 res = block[i+j];
993 }
994
995 if (mode == I2C_SMBUS_BLOCK_DATA &&
996 i+j >= blen) {
997 printf(" ");
998 } else if (res < 0) {
999 printf("XX ");
1000 if (mode == I2C_SMBUS_WORD_DATA)
1001 printf("XX ");
1002 } else {
1003 printf("%02x ", block[i+j]);
1004 if (mode == I2C_SMBUS_WORD_DATA)
1005 printf("%02x ", block[i+j+1]);
1006 }
1007
1008 if (mode == I2C_SMBUS_WORD_DATA)
1009 j++;
1010 }
1011 printf(" ");
1012
1013 for (j = 0; j < 16; j++) {
1014 if (mode == I2C_SMBUS_BLOCK_DATA && i+j >= blen)
1015 break;
1016 /* Skip unwanted registers */
1017 if (i+j < first || i+j > last) {
1018 printf(" ");
1019 continue;
1020 }
1021
1022 res = block[i+j];
1023 if (res < 0) {
1024//FIXME: impossible, block[] is uchar[]
1025 printf("X");
1026 } else if (res == 0x00 || res == 0xff) {
1027 printf(".");
1028 } else if (res < 32 || res >= 127) {
1029 printf("?");
1030 } else {
1031 printf("%c", res);
1032 }
1033 }
1034 printf("\n");
1035 }
1036 } else {
1037 /* Word data. */
1038 printf(" 0,8 1,9 2,a 3,b 4,c 5,d 6,e 7,f\n");
1039 for (i = 0; i < 256; i += 8) {
1040 if (i/8 < first/8)
1041 continue;
1042 if (i/8 > last/8)
1043 break;
1044
1045 printf("%02x: ", i);
1046 for (j = 0; j < 8; j++) {
1047 /* Skip unwanted registers. */
1048 if (i+j < first || i+j > last) {
1049 printf(" ");
1050 continue;
1051 }
1052
1053 res = i2c_smbus_read_word_data(fd, i+j);
1054 if (res < 0)
1055 printf("XXXX ");
1056 else
1057 printf("%04x ", res & 0xffff);
1058 }
1059 printf("\n");
1060 }
1061 }
1062
1063 return 0;
1064}
1065#endif /* ENABLE_I2CDUMP */
1066
1067#if ENABLE_I2CDETECT
1068enum adapter_type {
1069 ADT_DUMMY = 0,
1070 ADT_ISA,
1071 ADT_I2C,
1072 ADT_SMBUS,
1073};
1074
1075struct adap_desc {
1076 const char *funcs;
1077 const char *algo;
1078};
1079
1080static const struct adap_desc adap_descs[] = {
1081 { .funcs = "dummy",
1082 .algo = "Dummy bus", },
1083 { .funcs = "isa",
1084 .algo = "ISA bus", },
1085 { .funcs = "i2c",
1086 .algo = "I2C adapter", },
1087 { .funcs = "smbus",
1088 .algo = "SMBus adapter", },
1089};
1090
1091struct i2c_func
1092{
1093 long value;
1094 const char* name;
1095};
1096
1097static const struct i2c_func i2c_funcs_tab[] = {
1098 { .value = I2C_FUNC_I2C,
1099 .name = "I2C" },
1100 { .value = I2C_FUNC_SMBUS_QUICK,
1101 .name = "SMBus Quick Command" },
1102 { .value = I2C_FUNC_SMBUS_WRITE_BYTE,
1103 .name = "SMBus Send Byte" },
1104 { .value = I2C_FUNC_SMBUS_READ_BYTE,
1105 .name = "SMBus Receive Byte" },
1106 { .value = I2C_FUNC_SMBUS_WRITE_BYTE_DATA,
1107 .name = "SMBus Write Byte" },
1108 { .value = I2C_FUNC_SMBUS_READ_BYTE_DATA,
1109 .name = "SMBus Read Byte" },
1110 { .value = I2C_FUNC_SMBUS_WRITE_WORD_DATA,
1111 .name = "SMBus Write Word" },
1112 { .value = I2C_FUNC_SMBUS_READ_WORD_DATA,
1113 .name = "SMBus Read Word" },
1114 { .value = I2C_FUNC_SMBUS_PROC_CALL,
1115 .name = "SMBus Process Call" },
1116 { .value = I2C_FUNC_SMBUS_WRITE_BLOCK_DATA,
1117 .name = "SMBus Block Write" },
1118 { .value = I2C_FUNC_SMBUS_READ_BLOCK_DATA,
1119 .name = "SMBus Block Read" },
1120 { .value = I2C_FUNC_SMBUS_BLOCK_PROC_CALL,
1121 .name = "SMBus Block Process Call" },
1122 { .value = I2C_FUNC_SMBUS_PEC,
1123 .name = "SMBus PEC" },
1124 { .value = I2C_FUNC_SMBUS_WRITE_I2C_BLOCK,
1125 .name = "I2C Block Write" },
1126 { .value = I2C_FUNC_SMBUS_READ_I2C_BLOCK,
1127 .name = "I2C Block Read" },
1128 { .value = 0, .name = NULL }
1129};
1130
1131static enum adapter_type i2cdetect_get_funcs(int bus)
1132{
1133 enum adapter_type ret;
1134 unsigned long funcs;
1135 int fd;
1136
1137 fd = i2c_dev_open(bus);
1138
1139 get_funcs_matrix(fd, &funcs);
1140 if (funcs & I2C_FUNC_I2C)
1141 ret = ADT_I2C;
1142 else if (funcs & (I2C_FUNC_SMBUS_BYTE |
1143 I2C_FUNC_SMBUS_BYTE_DATA |
1144 I2C_FUNC_SMBUS_WORD_DATA))
1145 ret = ADT_SMBUS;
1146 else
1147 ret = ADT_DUMMY;
1148
1149 close(fd);
1150
1151 return ret;
1152}
1153
1154static void NORETURN list_i2c_busses_and_exit(void)
1155{
1156 const char *const i2cdev_path = "/sys/class/i2c-dev";
1157
1158 char path[NAME_MAX], name[128];
1159 struct dirent *de, *subde;
1160 enum adapter_type adt;
1161 DIR *dir, *subdir;
1162 int rv, bus;
1163 char *pos;
1164 FILE *fp;
1165
1166 /*
1167 * XXX Upstream i2cdetect also looks for i2c bus info in /proc/bus/i2c,
1168 * but we won't bother since it's only useful on older kernels (before
1169 * 2.6.5). We expect sysfs to be present and mounted at /sys/.
1170 */
1171
1172 dir = xopendir(i2cdev_path);
1173 while ((de = readdir(dir))) {
1174 if (de->d_name[0] == '.')
1175 continue;
1176
1177 /* Simple version for ISA chips. */
1178 snprintf(path, NAME_MAX, "%s/%s/name",
1179 i2cdev_path, de->d_name);
1180 fp = fopen(path, "r");
1181 if (fp == NULL) {
1182 snprintf(path, NAME_MAX,
1183 "%s/%s/device/name",
1184 i2cdev_path, de->d_name);
1185 fp = fopen(path, "r");
1186 }
1187
1188 /* Non-ISA chips require the hard-way. */
1189 if (fp == NULL) {
1190 snprintf(path, NAME_MAX,
1191 "%s/%s/device/name",
1192 i2cdev_path, de->d_name);
1193 subdir = opendir(path);
1194 if (subdir == NULL)
1195 continue;
1196
1197 while ((subde = readdir(subdir))) {
1198 if (subde->d_name[0] == '.')
1199 continue;
1200
1201 if (is_prefixed_with(subde->d_name, "i2c-")) {
1202 snprintf(path, NAME_MAX,
1203 "%s/%s/device/%s/name",
1204 i2cdev_path, de->d_name,
1205 subde->d_name);
1206 fp = fopen(path, "r");
1207 goto found;
1208 }
1209 }
1210 }
1211
1212found:
1213 if (fp != NULL) {
1214 /*
1215 * Get the rest of the info and display a line
1216 * for a single bus.
1217 */
1218 memset(name, 0, sizeof(name));
1219 pos = fgets(name, sizeof(name), fp);
1220 fclose(fp);
1221 if (pos == NULL)
1222 continue;
1223
1224 pos = strchr(name, '\n');
1225 if (pos != NULL)
1226 *pos = '\0';
1227
1228 rv = sscanf(de->d_name, "i2c-%d", &bus);
1229 if (rv != 1)
1230 continue;
1231
1232 if (is_prefixed_with(name, "ISA"))
1233 adt = ADT_ISA;
1234 else
1235 adt = i2cdetect_get_funcs(bus);
1236
1237 printf(
1238 "i2c-%d\t%-10s\t%-32s\t%s\n",
1239 bus, adap_descs[adt].funcs,
1240 name, adap_descs[adt].algo);
1241 }
1242 }
1243
1244 exit(EXIT_SUCCESS);
1245}
1246
1247static void NORETURN no_support(const char *cmd)
1248{
1249 bb_error_msg_and_die("bus doesn't support %s", cmd);
1250}
1251
1252static void will_skip(const char *cmd)
1253{
1254 bb_error_msg(
1255 "warning: can't use %s command, "
1256 "will skip some addresses", cmd);
1257}
1258
1259//usage:#define i2cdetect_trivial_usage
1260//usage: "[-F I2CBUS] [-l] [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]"
1261//usage:#define i2cdetect_full_usage "\n\n"
1262//usage: "Detect I2C chips.\n"
1263//usage: "\n I2CBUS i2c bus number"
1264//usage: "\n FIRST and LAST limit the probing range"
1265//usage: "\n"
1266//usage: "\n -l output list of installed busses"
1267//usage: "\n -y disable interactive mode"
1268//usage: "\n -a force scanning of non-regular addresses"
1269//usage: "\n -q use smbus quick write commands for probing (default)"
1270//usage: "\n -r use smbus read byte commands for probing"
1271//usage: "\n -F display list of functionalities"
1272int i2cdetect_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1273int i2cdetect_main(int argc UNUSED_PARAM, char **argv)
1274{
1275 const unsigned opt_y = (1 << 0), opt_a = (1 << 1),
1276 opt_q = (1 << 2), opt_r = (1 << 3),
1277 opt_F = (1 << 4), opt_l = (1 << 5);
1278 const char *const optstr = "yaqrFl";
1279
1280 int fd, bus_num, i, j, mode = DETECT_MODE_AUTO;
1281 int status;
1282 unsigned first = 0x00, last = 0x77;
1283 unsigned long funcs;
1284 unsigned opts;
1285
1286 opt_complementary = "q--r:r--q:" /* mutually exclusive */
1287 "-1:?3"; /* from 1 to 3 args */
1288 opts = getopt32(argv, optstr);
1289 argv += optind;
1290
1291 if (opts & opt_l)
1292 list_i2c_busses_and_exit();
1293
1294 bus_num = i2c_bus_lookup(argv[0]);
1295 fd = i2c_dev_open(bus_num);
1296 get_funcs_matrix(fd, &funcs);
1297
1298 if (opts & opt_F) {
1299 /* Only list the functionalities. */
1300 printf("Functionalities implemented by bus #%d\n", bus_num);
1301 for (i = 0; i2c_funcs_tab[i].value; i++) {
1302 printf("%-32s %s\n", i2c_funcs_tab[i].name,
1303 funcs & i2c_funcs_tab[i].value ? "yes" : "no");
1304 }
1305
1306 return EXIT_SUCCESS;
1307 }
1308
1309 if (opts & opt_r)
1310 mode = DETECT_MODE_READ;
1311 else if (opts & opt_q)
1312 mode = DETECT_MODE_QUICK;
1313
1314 if (opts & opt_a)
1315 last = 0x7f;
1316
1317 /* Read address range. */
1318 if (argv[1]) {
1319 first = xstrtou_range(argv[1], 16, first, last);
1320 if (argv[2])
1321 last = xstrtou_range(argv[2], 16, first, last);
1322 }
1323
1324 if (!(funcs & (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_READ_BYTE))) {
1325 no_support("detection commands");
1326 } else
1327 if (mode == DETECT_MODE_QUICK && !(funcs & I2C_FUNC_SMBUS_QUICK)) {
1328 no_support("SMBus Quick Write command");
1329 } else
1330 if (mode == DETECT_MODE_READ && !(funcs & I2C_FUNC_SMBUS_READ_BYTE)) {
1331 no_support("SMBus Receive Byte command");
1332 } else {
1333 if (!(funcs & I2C_FUNC_SMBUS_QUICK))
1334 will_skip("SMBus Quick Write");
1335 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE))
1336 will_skip("SMBus Receive Byte");
1337 }
1338
1339 if (!(opts & opt_y))
1340 confirm_action(-1, -1, -1, 0);
1341
1342 printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\n");
1343 for (i = 0; i < 128; i += 16) {
1344 printf("%02x: ", i);
1345 for(j = 0; j < 16; j++) {
1346 fflush_all();
1347
1348 if (mode == DETECT_MODE_AUTO) {
1349 if ((i+j >= 0x30 && i+j <= 0x37) ||
1350 (i+j >= 0x50 && i+j <= 0x5F))
1351 mode = DETECT_MODE_READ;
1352 else
1353 mode = DETECT_MODE_QUICK;
1354 }
1355
1356 /* Skip unwanted addresses. */
1357 if (i+j < first
1358 || i+j > last
1359 || (mode == DETECT_MODE_READ && !(funcs & I2C_FUNC_SMBUS_READ_BYTE))
1360 || (mode == DETECT_MODE_QUICK && !(funcs & I2C_FUNC_SMBUS_QUICK)))
1361 {
1362 printf(" ");
1363 continue;
1364 }
1365
1366 i2c_set_slave_addr(fd, i + j, 0);
1367
1368 switch (mode) {
1369 case DETECT_MODE_READ:
1370 /*
1371 * This is known to lock SMBus on various
1372 * write-only chips (mainly clock chips).
1373 */
1374 status = i2c_smbus_read_byte(fd);
1375 break;
1376 default: /* DETECT_MODE_QUICK: */
1377 /*
1378 * This is known to corrupt the Atmel
1379 * AT24RF08 EEPROM.
1380 */
1381 status = i2c_smbus_write_quick(fd,
1382 I2C_SMBUS_WRITE);
1383 break;
1384 }
1385
1386 if (status < 0)
1387 printf("-- ");
1388 else
1389 printf("%02x ", i+j);
1390 }
1391 printf("\n");
1392 }
1393
1394 return 0;
1395}
1396#endif /* ENABLE_I2CDETECT */
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)
87 if (++n > 0) 87 if (++n > 0)
88 ut.ut_type = n != 3 ? n : SHUTDOWN_TIME; 88 ut.ut_type = n != 3 ? n : SHUTDOWN_TIME;
89#else 89#else
90 if (strncmp(ut.ut_user, "shutdown", 8) == 0) 90 if (is_prefixed_with(ut.ut_user, "shutdown"))
91 ut.ut_type = SHUTDOWN_TIME; 91 ut.ut_type = SHUTDOWN_TIME;
92 else if (strncmp(ut.ut_user, "reboot", 6) == 0) 92 else if (is_prefixed_with(ut.ut_user, "reboot"))
93 ut.ut_type = BOOT_TIME; 93 ut.ut_type = BOOT_TIME;
94 else if (strncmp(ut.ut_user, "runlevel", 8) == 0) 94 else if (is_prefixed_with(ut.ut_user, "runlevel"))
95 ut.ut_type = RUN_LVL; 95 ut.ut_type = RUN_LVL;
96#endif 96#endif
97 } else { 97 } else {
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 {
42static void show_entry(struct utmp *ut, int state, time_t dur_secs) 42static void show_entry(struct utmp *ut, int state, time_t dur_secs)
43{ 43{
44 unsigned days, hours, mins; 44 unsigned days, hours, mins;
45 char duration[32]; 45 char duration[sizeof("(%u+02:02)") + sizeof(int)*3];
46 char login_time[17]; 46 char login_time[17];
47 char logout_time[8]; 47 char logout_time[8];
48 const char *logout_str; 48 const char *logout_str;
@@ -53,7 +53,8 @@ static void show_entry(struct utmp *ut, int state, time_t dur_secs)
53 * but some systems have it wrong */ 53 * but some systems have it wrong */
54 tmp = ut->ut_tv.tv_sec; 54 tmp = ut->ut_tv.tv_sec;
55 safe_strncpy(login_time, ctime(&tmp), 17); 55 safe_strncpy(login_time, ctime(&tmp), 17);
56 snprintf(logout_time, 8, "- %s", ctime(&dur_secs) + 11); 56 tmp = dur_secs;
57 snprintf(logout_time, 8, "- %s", ctime(&tmp) + 11);
57 58
58 dur_secs = MAX(dur_secs - (time_t)ut->ut_tv.tv_sec, (time_t)0); 59 dur_secs = MAX(dur_secs - (time_t)ut->ut_tv.tv_sec, (time_t)0);
59 /* unsigned int is easier to divide than time_t (which may be signed long) */ 60 /* unsigned int is easier to divide than time_t (which may be signed long) */
diff --git a/miscutils/man.c b/miscutils/man.c
index 3f389b435..4ce40b247 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)
66 goto ordinary_manpage; 66 goto ordinary_manpage;
67 67
68 line = xmalloc_open_zipped_read_close(man_filename, NULL); 68 line = xmalloc_open_zipped_read_close(man_filename, NULL);
69 if (!line || strncmp(line, ".so ", 4) != 0) { 69 if (!line || !is_prefixed_with(line, ".so ")) {
70 free(line); 70 free(line);
71 goto ordinary_manpage; 71 goto ordinary_manpage;
72 } 72 }
@@ -232,7 +232,7 @@ int man_main(int argc UNUSED_PARAM, char **argv)
232 if (!token[1]) 232 if (!token[1])
233 continue; 233 continue;
234 if (strcmp("DEFINE", token[0]) == 0) { 234 if (strcmp("DEFINE", token[0]) == 0) {
235 if (strncmp("pager", token[1], 5) == 0) { 235 if (is_prefixed_with("pager", token[1])) {
236 pager = xstrdup(skip_whitespace(token[1]) + 5); 236 pager = xstrdup(skip_whitespace(token[1]) + 5);
237 } 237 }
238 } else 238 } else
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)
105 int mtd_num; 105 int mtd_num;
106 int dev_num = UBI_DEV_NUM_AUTO; 106 int dev_num = UBI_DEV_NUM_AUTO;
107 int vol_id = UBI_VOL_NUM_AUTO; 107 int vol_id = UBI_VOL_NUM_AUTO;
108 int vid_hdr_offset = 0;
108 char *vol_name; 109 char *vol_name;
109 unsigned long long size_bytes = size_bytes; /* for compiler */ 110 unsigned long long size_bytes = size_bytes; /* for compiler */
110 char *size_bytes_str; 111 char *size_bytes_str;
@@ -133,10 +134,11 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
133#define OPTION_a (1 << 5) 134#define OPTION_a (1 << 5)
134#define OPTION_t (1 << 6) 135#define OPTION_t (1 << 6)
135 if (do_mkvol) { 136 if (do_mkvol) {
136 opt_complementary = "-1:d+:n+:a+"; 137 opt_complementary = "-1:d+:n+:a+:O+";
137 opts = getopt32(argv, "md:n:N:s:a:t:", 138 opts = getopt32(argv, "md:n:N:s:a:t:O:",
138 &dev_num, &vol_id, 139 &dev_num, &vol_id,
139 &vol_name, &size_bytes_str, &alignment, &type 140 &vol_name, &size_bytes_str, &alignment, &type,
141 &vid_hdr_offset
140 ); 142 );
141 } else 143 } else
142 if (do_update) { 144 if (do_update) {
@@ -162,17 +164,19 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
162 // bb_error_msg_and_die("%s: not a char device", ubi_ctrl); 164 // bb_error_msg_and_die("%s: not a char device", ubi_ctrl);
163 165
164//usage:#define ubiattach_trivial_usage 166//usage:#define ubiattach_trivial_usage
165//usage: "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV" 167//usage: "-m MTD_NUM [-d UBI_NUM] [-O VID_HDR_OFF] UBI_CTRL_DEV"
166//usage:#define ubiattach_full_usage "\n\n" 168//usage:#define ubiattach_full_usage "\n\n"
167//usage: "Attach MTD device to UBI\n" 169//usage: "Attach MTD device to UBI\n"
168//usage: "\n -m MTD_NUM MTD device number to attach" 170//usage: "\n -m MTD_NUM MTD device number to attach"
169//usage: "\n -d UBI_NUM UBI device number to assign" 171//usage: "\n -d UBI_NUM UBI device number to assign"
172//usage: "\n -O VID_HDR_OFF VID header offset"
170 if (do_attach) { 173 if (do_attach) {
171 if (!(opts & OPTION_m)) 174 if (!(opts & OPTION_m))
172 bb_error_msg_and_die("%s device not specified", "MTD"); 175 bb_error_msg_and_die("%s device not specified", "MTD");
173 176
174 attach_req.mtd_num = mtd_num; 177 attach_req.mtd_num = mtd_num;
175 attach_req.ubi_num = dev_num; 178 attach_req.ubi_num = dev_num;
179 attach_req.vid_hdr_offset = vid_hdr_offset;
176 180
177 xioctl(fd, UBI_IOCATT, &attach_req); 181 xioctl(fd, UBI_IOCATT, &attach_req);
178 } else 182 } else
diff --git a/modutils/depmod.c b/modutils/depmod.c
index aa228ec85..9713aef92 100644
--- a/modutils/depmod.c
+++ b/modutils/depmod.c
@@ -33,7 +33,6 @@ typedef struct module_info {
33static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARAM, 33static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARAM,
34 void *data, int depth UNUSED_PARAM) 34 void *data, int depth UNUSED_PARAM)
35{ 35{
36 char modname[MODULE_NAME_LEN];
37 module_info **first = (module_info **) data; 36 module_info **first = (module_info **) data;
38 char *image, *ptr; 37 char *image, *ptr;
39 module_info *info; 38 module_info *info;
@@ -51,9 +50,12 @@ static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARA
51 50
52 info->dnext = info->dprev = info; 51 info->dnext = info->dprev = info;
53 info->name = xstrdup(fname + 2); /* skip "./" */ 52 info->name = xstrdup(fname + 2); /* skip "./" */
54 info->modname = xstrdup(filename2modname(fname, modname)); 53 info->modname = filename2modname(
54 bb_get_last_path_component_nostrip(fname),
55 NULL
56 );
55 for (ptr = image; ptr < image + len - 10; ptr++) { 57 for (ptr = image; ptr < image + len - 10; ptr++) {
56 if (strncmp(ptr, "depends=", 8) == 0) { 58 if (is_prefixed_with(ptr, "depends=")) {
57 char *u; 59 char *u;
58 60
59 ptr += 8; 61 ptr += 8;
@@ -62,15 +64,15 @@ static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARA
62 *u = '_'; 64 *u = '_';
63 ptr += string_to_llist(ptr, &info->dependencies, ","); 65 ptr += string_to_llist(ptr, &info->dependencies, ",");
64 } else if (ENABLE_FEATURE_MODUTILS_ALIAS 66 } else if (ENABLE_FEATURE_MODUTILS_ALIAS
65 && strncmp(ptr, "alias=", 6) == 0 67 && is_prefixed_with(ptr, "alias=")
66 ) { 68 ) {
67 llist_add_to(&info->aliases, xstrdup(ptr + 6)); 69 llist_add_to(&info->aliases, xstrdup(ptr + 6));
68 ptr += strlen(ptr); 70 ptr += strlen(ptr);
69 } else if (ENABLE_FEATURE_MODUTILS_SYMBOLS 71 } else if (ENABLE_FEATURE_MODUTILS_SYMBOLS
70 && strncmp(ptr, "__ksymtab_", 10) == 0 72 && is_prefixed_with(ptr, "__ksymtab_")
71 ) { 73 ) {
72 ptr += 10; 74 ptr += 10;
73 if (strncmp(ptr, "gpl", 3) == 0 75 if (is_prefixed_with(ptr, "gpl")
74 || strcmp(ptr, "strings") == 0 76 || strcmp(ptr, "strings") == 0
75 ) { 77 ) {
76 continue; 78 continue;
@@ -242,17 +244,19 @@ int depmod_main(int argc UNUSED_PARAM, char **argv)
242 if (!(option_mask32 & OPT_n)) 244 if (!(option_mask32 & OPT_n))
243 xfreopen_write("modules.alias", stdout); 245 xfreopen_write("modules.alias", stdout);
244 for (m = modules; m != NULL; m = m->next) { 246 for (m = modules; m != NULL; m = m->next) {
247 char modname[MODULE_NAME_LEN];
245 const char *fname = bb_basename(m->name); 248 const char *fname = bb_basename(m->name);
246 int fnlen = strchrnul(fname, '.') - fname; 249 filename2modname(fname, modname);
247 while (m->aliases) { 250 while (m->aliases) {
248 /* Last word can well be m->modname instead, 251 /*
249 * but depmod from module-init-tools 3.4 252 * Last word used to be a basename
250 * uses module basename, i.e., no s/-/_/g. 253 * (filename with path and .ko.* stripped)
251 * (pathname and .ko.* are still stripped) 254 * at the time of module-init-tools 3.4.
252 * Mimicking that... */ 255 * kmod v.12 uses module name, i.e., s/-/_/g.
253 printf("alias %s %.*s\n", 256 */
257 printf("alias %s %s\n",
254 (char*)llist_pop(&m->aliases), 258 (char*)llist_pop(&m->aliases),
255 fnlen, fname); 259 modname);
256 } 260 }
257 } 261 }
258#endif 262#endif
@@ -260,12 +264,13 @@ int depmod_main(int argc UNUSED_PARAM, char **argv)
260 if (!(option_mask32 & OPT_n)) 264 if (!(option_mask32 & OPT_n))
261 xfreopen_write("modules.symbols", stdout); 265 xfreopen_write("modules.symbols", stdout);
262 for (m = modules; m != NULL; m = m->next) { 266 for (m = modules; m != NULL; m = m->next) {
267 char modname[MODULE_NAME_LEN];
263 const char *fname = bb_basename(m->name); 268 const char *fname = bb_basename(m->name);
264 int fnlen = strchrnul(fname, '.') - fname; 269 filename2modname(fname, modname);
265 while (m->symbols) { 270 while (m->symbols) {
266 printf("alias symbol:%s %.*s\n", 271 printf("alias symbol:%s %s\n",
267 (char*)llist_pop(&m->symbols), 272 (char*)llist_pop(&m->symbols),
268 fnlen, fname); 273 modname);
269 } 274 }
270 } 275 }
271#endif 276#endif
diff --git a/modutils/modinfo.c b/modutils/modinfo.c
index 0ab942890..8e74b6438 100644
--- a/modutils/modinfo.c
+++ b/modutils/modinfo.c
@@ -62,7 +62,7 @@ static void modinfo(const char *path, const char *version,
62 "firmware", 62 "firmware",
63 }; 63 };
64 size_t len; 64 size_t len;
65 int j, length; 65 int j;
66 char *ptr, *the_module; 66 char *ptr, *the_module;
67 const char *field = env->field; 67 const char *field = env->field;
68 int tags = env->tags; 68 int tags = env->tags;
@@ -94,16 +94,18 @@ static void modinfo(const char *path, const char *version,
94 pattern = field; 94 pattern = field;
95 if ((1<<j) & OPT_TAGS) 95 if ((1<<j) & OPT_TAGS)
96 pattern = shortcuts[j]; 96 pattern = shortcuts[j];
97 length = strlen(pattern);
98 ptr = the_module; 97 ptr = the_module;
99 while (1) { 98 while (1) {
99 char *after_pattern;
100
100 ptr = memchr(ptr, *pattern, len - (ptr - (char*)the_module)); 101 ptr = memchr(ptr, *pattern, len - (ptr - (char*)the_module));
101 if (ptr == NULL) /* no occurance left, done */ 102 if (ptr == NULL) /* no occurance left, done */
102 break; 103 break;
103 if (strncmp(ptr, pattern, length) == 0 && ptr[length] == '=') { 104 after_pattern = is_prefixed_with(ptr, pattern);
105 if (after_pattern && *after_pattern == '=') {
104 /* field prefixes are 0x80 or 0x00 */ 106 /* field prefixes are 0x80 or 0x00 */
105 if ((ptr[-1] & 0x7F) == '\0') { 107 if ((ptr[-1] & 0x7F) == 0x00) {
106 ptr += length + 1; 108 ptr = after_pattern + 1;
107 display(ptr, pattern, (1<<j) != tags); 109 display(ptr, pattern, (1<<j) != tags);
108 ptr += strlen(ptr); 110 ptr += strlen(ptr);
109 } 111 }
@@ -154,7 +156,7 @@ int modinfo_main(int argc UNUSED_PARAM, char **argv)
154 if (colon == NULL) 156 if (colon == NULL)
155 continue; 157 continue;
156 *colon = '\0'; 158 *colon = '\0';
157 filename2modname(tokens[0], name); 159 filename2modname(bb_basename(tokens[0]), name);
158 for (i = 0; argv[i]; i++) { 160 for (i = 0; argv[i]; i++) {
159 if (fnmatch(argv[i], name, 0) == 0) { 161 if (fnmatch(argv[i], name, 0) == 0) {
160 modinfo(tokens[0], uts.release, &env); 162 modinfo(tokens[0], uts.release, &env);
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c
index b7990bff1..9c941064b 100644
--- a/modutils/modprobe-small.c
+++ b/modutils/modprobe-small.c
@@ -116,21 +116,21 @@ static char* copy_stringbuf(void)
116 116
117static char* find_keyword(char *ptr, size_t len, const char *word) 117static char* find_keyword(char *ptr, size_t len, const char *word)
118{ 118{
119 int wlen;
120
121 if (!ptr) /* happens if xmalloc_open_zipped_read_close cannot read it */ 119 if (!ptr) /* happens if xmalloc_open_zipped_read_close cannot read it */
122 return NULL; 120 return NULL;
123 121
124 wlen = strlen(word); 122 len -= strlen(word) - 1;
125 len -= wlen - 1;
126 while ((ssize_t)len > 0) { 123 while ((ssize_t)len > 0) {
127 char *old = ptr; 124 char *old = ptr;
125 char *after_word;
126
128 /* search for the first char in word */ 127 /* search for the first char in word */
129 ptr = memchr(ptr, *word, len); 128 ptr = memchr(ptr, word[0], len);
130 if (ptr == NULL) /* no occurance left, done */ 129 if (ptr == NULL) /* no occurance left, done */
131 break; 130 break;
132 if (strncmp(ptr, word, wlen) == 0) 131 after_word = is_prefixed_with(ptr, word);
133 return ptr + wlen; /* found, return ptr past it */ 132 if (after_word)
133 return after_word; /* found, return ptr past it */
134 ++ptr; 134 ++ptr;
135 len -= (ptr - old); 135 len -= (ptr - old);
136 } 136 }
@@ -149,9 +149,13 @@ static void replace(char *s, char what, char with)
149static char *filename2modname(const char *filename, char *modname) 149static char *filename2modname(const char *filename, char *modname)
150{ 150{
151 int i; 151 int i;
152 char *from; 152 const char *from;
153 153
154 from = bb_get_last_path_component_nostrip(filename); 154 // Disabled since otherwise "modprobe dir/name" would work
155 // as if it is "modprobe name". It is unclear why
156 // 'basenamization' was here in the first place.
157 //from = bb_get_last_path_component_nostrip(filename);
158 from = filename;
155 for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++) 159 for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++)
156 modname[i] = (from[i] == '-') ? '_' : from[i]; 160 modname[i] = (from[i] == '-') ? '_' : from[i];
157 modname[i] = '\0'; 161 modname[i] = '\0';
@@ -159,6 +163,15 @@ static char *filename2modname(const char *filename, char *modname)
159 return modname; 163 return modname;
160} 164}
161 165
166static int pathname_matches_modname(const char *pathname, const char *modname)
167{
168 int r;
169 char name[MODULE_NAME_LEN];
170 filename2modname(bb_get_last_path_component_nostrip(pathname), name);
171 r = (strcmp(name, modname) == 0);
172 return r;
173}
174
162/* Take "word word", return malloced "word",NUL,"word",NUL,NUL */ 175/* Take "word word", return malloced "word",NUL,"word",NUL,NUL */
163static char* str_2_list(const char *str) 176static char* str_2_list(const char *str)
164{ 177{
@@ -291,18 +304,6 @@ static void parse_module(module_info *info, const char *pathname)
291 free(module_image); 304 free(module_image);
292} 305}
293 306
294static int pathname_matches_modname(const char *pathname, const char *modname)
295{
296 int r;
297 char name[MODULE_NAME_LEN];
298 const char *fname = bb_get_last_path_component_nostrip(pathname);
299 const char *suffix = strrstr(fname, ".ko");
300 safe_strncpy(name, fname, suffix - fname + 1);
301 replace(name, '-', '_');
302 r = (strcmp(name, modname) == 0);
303 return r;
304}
305
306static FAST_FUNC int fileAction(const char *pathname, 307static FAST_FUNC int fileAction(const char *pathname,
307 struct stat *sb UNUSED_PARAM, 308 struct stat *sb UNUSED_PARAM,
308 void *modname_to_match, 309 void *modname_to_match,
@@ -535,22 +536,69 @@ static module_info** find_alias(const char *alias)
535// TODO: open only once, invent config_rewind() 536// TODO: open only once, invent config_rewind()
536static int already_loaded(const char *name) 537static int already_loaded(const char *name)
537{ 538{
538 int ret = 0; 539 int ret;
539 char *s; 540 char *line;
540 parser_t *parser = config_open2("/proc/modules", xfopen_for_read); 541 FILE *fp;
541 while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) { 542
542 if (strcmp(s, name) == 0) { 543 ret = 5 * 2;
543 ret = 1; 544 again:
544 break; 545 fp = fopen_for_read("/proc/modules");
546 if (!fp)
547 return 0;
548 while ((line = xmalloc_fgetline(fp)) != NULL) {
549 char *live;
550 char *after_name;
551
552 // Examples from kernel 3.14.6:
553 //pcspkr 12718 0 - Live 0xffffffffa017e000
554 //snd_timer 28690 2 snd_seq,snd_pcm, Live 0xffffffffa025e000
555 //i915 801405 2 - Live 0xffffffffa0096000
556 after_name = is_prefixed_with(line, name);
557 if (!after_name || *after_name != ' ') {
558 free(line);
559 continue;
545 } 560 }
561 live = strstr(line, " Live");
562 free(line);
563 if (!live) {
564 /* State can be Unloading, Loading, or Live.
565 * modprobe must not return prematurely if we see "Loading":
566 * it can cause further programs to assume load completed,
567 * but it did not (yet)!
568 * Wait up to 5*20 ms for it to resolve.
569 */
570 ret -= 2;
571 if (ret == 0)
572 break; /* huh? report as "not loaded" */
573 fclose(fp);
574 usleep(20*1000);
575 goto again;
576 }
577 ret = 1;
578 break;
546 } 579 }
547 config_close(parser); 580 fclose(fp);
548 return ret; 581
582 return ret & 1;
549} 583}
550#else 584#else
551#define already_loaded(name) is_rmmod 585#define already_loaded(name) 0
552#endif 586#endif
553 587
588static int rmmod(const char *filename)
589{
590 int r;
591 char modname[MODULE_NAME_LEN];
592
593 filename2modname(filename, modname);
594 r = delete_module(modname, O_NONBLOCK | O_EXCL);
595 dbg1_error_msg("delete_module('%s', O_NONBLOCK | O_EXCL):%d", modname, r);
596 if (r != 0 && !(option_mask32 & OPT_q)) {
597 bb_perror_msg("remove '%s'", modname);
598 }
599 return r;
600}
601
554/* 602/*
555 * Given modules definition and module name (or alias, or symbol) 603 * Given modules definition and module name (or alias, or symbol)
556 * load/remove the module respecting dependencies. 604 * load/remove the module respecting dependencies.
@@ -567,26 +615,36 @@ static void process_module(char *name, const char *cmdline_options)
567 module_info **infovec; 615 module_info **infovec;
568 module_info *info; 616 module_info *info;
569 int infoidx; 617 int infoidx;
570 int is_rmmod = (option_mask32 & OPT_r) != 0; 618 int is_remove = (option_mask32 & OPT_r) != 0;
571 619
572 dbg1_error_msg("process_module('%s','%s')", name, cmdline_options); 620 dbg1_error_msg("process_module('%s','%s')", name, cmdline_options);
573 621
574 replace(name, '-', '_'); 622 replace(name, '-', '_');
575 623
576 dbg1_error_msg("already_loaded:%d is_rmmod:%d", already_loaded(name), is_rmmod); 624 dbg1_error_msg("already_loaded:%d is_remove:%d", already_loaded(name), is_remove);
625
626 if (applet_name[0] == 'r') {
627 /* rmmod.
628 * Does not remove dependencies, no need to scan, just remove.
629 * (compat note: this allows and strips .ko suffix)
630 */
631 rmmod(name);
632 return;
633 }
634
577 /* 635 /*
578 * We used to have "is_rmmod != already_loaded(name)" check here, but 636 * We used to have "is_remove != already_loaded(name)" check here, but
579 * modprobe -r pci:v00008086d00007010sv00000000sd00000000bc01sc01i80 637 * modprobe -r pci:v00008086d00007010sv00000000sd00000000bc01sc01i80
580 * won't unload modules (there are more than one) 638 * won't unload modules (there are more than one)
581 * which have this alias. 639 * which have this alias.
582 */ 640 */
583 if (!is_rmmod && already_loaded(name)) { 641 if (!is_remove && already_loaded(name)) {
584 dbg1_error_msg("nothing to do for '%s'", name); 642 dbg1_error_msg("nothing to do for '%s'", name);
585 return; 643 return;
586 } 644 }
587 645
588 options = NULL; 646 options = NULL;
589 if (!is_rmmod) { 647 if (!is_remove) {
590 char *opt_filename = xasprintf("/etc/modules/%s", name); 648 char *opt_filename = xasprintf("/etc/modules/%s", name);
591 options = xmalloc_open_read_close(opt_filename, NULL); 649 options = xmalloc_open_read_close(opt_filename, NULL);
592 if (options) 650 if (options)
@@ -620,7 +678,7 @@ static void process_module(char *name, const char *cmdline_options)
620 0 /* depth */ 678 0 /* depth */
621 ); 679 );
622 dbg1_error_msg("dirscan complete"); 680 dbg1_error_msg("dirscan complete");
623 /* Module was not found, or load failed, or is_rmmod */ 681 /* Module was not found, or load failed, or is_remove */
624 if (module_found_idx >= 0) { /* module was found */ 682 if (module_found_idx >= 0) { /* module was found */
625 infovec = xzalloc(2 * sizeof(infovec[0])); 683 infovec = xzalloc(2 * sizeof(infovec[0]));
626 infovec[0] = &modinfo[module_found_idx]; 684 infovec[0] = &modinfo[module_found_idx];
@@ -631,6 +689,14 @@ static void process_module(char *name, const char *cmdline_options)
631 infovec = find_alias(name); 689 infovec = find_alias(name);
632 } 690 }
633 691
692 if (!infovec) {
693 /* both dirscan and find_alias found nothing */
694 if (!is_remove && applet_name[0] != 'd') /* it wasn't rmmod or depmod */
695 bb_error_msg("module '%s' not found", name);
696//TODO: _and_die()? or should we continue (un)loading modules listed on cmdline?
697 goto ret;
698 }
699
634 /* There can be more than one module for the given alias. For example, 700 /* There can be more than one module for the given alias. For example,
635 * "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches 701 * "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches
636 * ata_piix because it has alias "pci:v00008086d00007010sv*sd*bc*sc*i*" 702 * ata_piix because it has alias "pci:v00008086d00007010sv*sd*bc*sc*i*"
@@ -639,28 +705,15 @@ static void process_module(char *name, const char *cmdline_options)
639 * a *list* of modinfo pointers from find_alias(). 705 * a *list* of modinfo pointers from find_alias().
640 */ 706 */
641 707
642 /* rmmod or modprobe -r? unload module(s) */ 708 /* modprobe -r? unload module(s) */
643 if (is_rmmod) { 709 if (is_remove) {
644 infoidx = 0; 710 infoidx = 0;
645 while ((info = infovec[infoidx++]) != NULL) { 711 while ((info = infovec[infoidx++]) != NULL) {
646 int r; 712 int r = rmmod(bb_get_last_path_component_nostrip(info->pathname));
647 char modname[MODULE_NAME_LEN];
648
649 filename2modname(info->pathname, modname);
650 r = delete_module(modname, O_NONBLOCK | O_EXCL);
651 dbg1_error_msg("delete_module('%s', O_NONBLOCK | O_EXCL):%d", modname, r);
652 if (r != 0) { 713 if (r != 0) {
653 if (!(option_mask32 & OPT_q)) 714 goto ret; /* error */
654 bb_perror_msg("remove '%s'", modname);
655 goto ret;
656 } 715 }
657 } 716 }
658
659 if (applet_name[0] == 'r') {
660 /* rmmod: do not remove dependencies, exit */
661 goto ret;
662 }
663
664 /* modprobe -r: we do not stop here - 717 /* modprobe -r: we do not stop here -
665 * continue to unload modules on which the module depends: 718 * continue to unload modules on which the module depends:
666 * "-r --remove: option causes modprobe to remove a module. 719 * "-r --remove: option causes modprobe to remove a module.
@@ -669,14 +722,6 @@ static void process_module(char *name, const char *cmdline_options)
669 */ 722 */
670 } 723 }
671 724
672 if (!infovec) {
673 /* both dirscan and find_alias found nothing */
674 if (!is_rmmod && applet_name[0] != 'd') /* it wasn't rmmod or depmod */
675 bb_error_msg("module '%s' not found", name);
676//TODO: _and_die()? or should we continue (un)loading modules listed on cmdline?
677 goto ret;
678 }
679
680 infoidx = 0; 725 infoidx = 0;
681 while ((info = infovec[infoidx++]) != NULL) { 726 while ((info = infovec[infoidx++]) != NULL) {
682 /* Iterate thru dependencies, trying to (un)load them */ 727 /* Iterate thru dependencies, trying to (un)load them */
@@ -689,7 +734,7 @@ static void process_module(char *name, const char *cmdline_options)
689 } 734 }
690 free(deps); 735 free(deps);
691 736
692 if (is_rmmod) 737 if (is_remove)
693 continue; 738 continue;
694 739
695 /* We are modprobe: load it */ 740 /* We are modprobe: load it */
@@ -892,10 +937,10 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
892 } 937 }
893 938
894#if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE 939#if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
895 /* If not rmmod, parse possible module options given on command line. 940 /* If not rmmod/-r, parse possible module options given on command line.
896 * insmod/modprobe takes one module name, the rest are parameters. */ 941 * insmod/modprobe takes one module name, the rest are parameters. */
897 options = NULL; 942 options = NULL;
898 if ('r' != applet0) { 943 if (!(option_mask32 & OPT_r)) {
899 char **arg = argv; 944 char **arg = argv;
900 while (*++arg) { 945 while (*++arg) {
901 /* Enclose options in quotes */ 946 /* Enclose options in quotes */
@@ -906,7 +951,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
906 } 951 }
907 } 952 }
908#else 953#else
909 if ('r' != applet0) 954 if (!(option_mask32 & OPT_r))
910 argv[1] = NULL; 955 argv[1] = NULL;
911#endif 956#endif
912 957
@@ -930,10 +975,11 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
930 } 975 }
931 976
932 /* Try to load modprobe.dep.bb */ 977 /* Try to load modprobe.dep.bb */
933 load_dep_bb(); 978 if ('r' != applet0) /* not rmmod */
979 load_dep_bb();
934 980
935 /* Load/remove modules. 981 /* Load/remove modules.
936 * Only rmmod loops here, modprobe has only argv[0] */ 982 * Only rmmod/modprobe -r loops here, insmod/modprobe has only argv[0] */
937 do { 983 do {
938 process_module(*argv, options); 984 process_module(*argv, options);
939 } while (*++argv); 985 } while (*++argv);
diff --git a/modutils/modprobe.c b/modutils/modprobe.c
index f0904285b..996de4074 100644
--- a/modutils/modprobe.c
+++ b/modutils/modprobe.c
@@ -15,8 +15,11 @@
15#include <sys/utsname.h> 15#include <sys/utsname.h>
16#include <fnmatch.h> 16#include <fnmatch.h>
17 17
18//#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__) 18#if 1
19#define DBG(...) ((void)0) 19#define DBG(...) ((void)0)
20#else
21#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__)
22#endif
20 23
21/* Note that unlike older versions of modules.dep/depmod (busybox and m-i-t), 24/* Note that unlike older versions of modules.dep/depmod (busybox and m-i-t),
22 * we expect the full dependency list to be specified in modules.dep. 25 * we expect the full dependency list to be specified in modules.dep.
@@ -229,26 +232,20 @@ static ALWAYS_INLINE struct module_entry *get_or_add_modentry(const char *module
229{ 232{
230 return helper_get_module(module, 1); 233 return helper_get_module(module, 1);
231} 234}
232static ALWAYS_INLINE struct module_entry *get_modentry(const char *module) 235/* So far this function always gets a module pathname, never an alias name.
236 * The crucial difference is that pathname needs dirname stripping,
237 * while alias name must NOT do it!
238 * Testcase where dirname stripping is likely to go wrong: "modprobe devname:snd/timer"
239 */
240static ALWAYS_INLINE struct module_entry *get_modentry(const char *pathname)
233{ 241{
234 return helper_get_module(module, 0); 242 return helper_get_module(bb_get_last_path_component_nostrip(pathname), 0);
235} 243}
236 244
237static void add_probe(const char *name) 245static void add_probe(const char *name)
238{ 246{
239 struct module_entry *m; 247 struct module_entry *m;
240 248
241 /*
242 * get_or_add_modentry() strips path from name and works
243 * on remaining basename.
244 * This would make "rmmod dir/name" and "modprobe dir/name"
245 * to work like "rmmod name" and "modprobe name",
246 * which is wrong, and can be abused via implicit modprobing:
247 * "ifconfig /usbserial up" tries to modprobe netdev-/usbserial.
248 */
249 if (strchr(name, '/'))
250 bb_error_msg_and_die("malformed module name '%s'", name);
251
252 m = get_or_add_modentry(name); 249 m = get_or_add_modentry(name);
253 if (!(option_mask32 & (OPT_REMOVE | OPT_SHOW_DEPS)) 250 if (!(option_mask32 & (OPT_REMOVE | OPT_SHOW_DEPS))
254 && (m->flags & MODULE_FLAG_LOADED) 251 && (m->flags & MODULE_FLAG_LOADED)
@@ -263,7 +260,7 @@ static void add_probe(const char *name)
263 llist_add_to_end(&G.probes, m); 260 llist_add_to_end(&G.probes, m);
264 G.num_unresolved_deps++; 261 G.num_unresolved_deps++;
265 if (ENABLE_FEATURE_MODUTILS_SYMBOLS 262 if (ENABLE_FEATURE_MODUTILS_SYMBOLS
266 && strncmp(m->modname, "symbol:", 7) == 0 263 && is_prefixed_with(m->modname, "symbol:")
267 ) { 264 ) {
268 G.need_symbols = 1; 265 G.need_symbols = 1;
269 } 266 }
@@ -356,22 +353,18 @@ static char *parse_and_add_kcmdline_module_options(char *options, const char *mo
356 char *kcmdline_buf; 353 char *kcmdline_buf;
357 char *kcmdline; 354 char *kcmdline;
358 char *kptr; 355 char *kptr;
359 int len;
360 356
361 kcmdline_buf = xmalloc_open_read_close("/proc/cmdline", NULL); 357 kcmdline_buf = xmalloc_open_read_close("/proc/cmdline", NULL);
362 if (!kcmdline_buf) 358 if (!kcmdline_buf)
363 return options; 359 return options;
364 360
365 kcmdline = kcmdline_buf; 361 kcmdline = kcmdline_buf;
366 len = strlen(modulename);
367 while ((kptr = strsep(&kcmdline, "\n\t ")) != NULL) { 362 while ((kptr = strsep(&kcmdline, "\n\t ")) != NULL) {
368 if (strncmp(modulename, kptr, len) != 0) 363 char *after_modulename = is_prefixed_with(kptr, modulename);
369 continue; 364 if (!after_modulename || *after_modulename != '.')
370 kptr += len;
371 if (*kptr != '.')
372 continue; 365 continue;
373 /* It is "modulename.xxxx" */ 366 /* It is "modulename.xxxx" */
374 kptr++; 367 kptr = after_modulename + 1;
375 if (strchr(kptr, '=') != NULL) { 368 if (strchr(kptr, '=') != NULL) {
376 /* It is "modulename.opt=[val]" */ 369 /* It is "modulename.opt=[val]" */
377 options = gather_options_str(options, kptr); 370 options = gather_options_str(options, kptr);
@@ -428,7 +421,7 @@ static int do_modprobe(struct module_entry *m)
428 421
429 rc = 0; 422 rc = 0;
430 fn = llist_pop(&m->deps); /* we leak it */ 423 fn = llist_pop(&m->deps); /* we leak it */
431 m2 = get_or_add_modentry(fn); 424 m2 = get_or_add_modentry(bb_get_last_path_component_nostrip(fn));
432 425
433 if (option_mask32 & OPT_REMOVE) { 426 if (option_mask32 & OPT_REMOVE) {
434 /* modprobe -r */ 427 /* modprobe -r */
@@ -510,7 +503,7 @@ static void load_modules_dep(void)
510 colon = last_char_is(tokens[0], ':'); 503 colon = last_char_is(tokens[0], ':');
511 if (colon == NULL) 504 if (colon == NULL)
512 continue; 505 continue;
513 *colon = 0; 506 *colon = '\0';
514 507
515 m = get_modentry(tokens[0]); 508 m = get_modentry(tokens[0]);
516 if (m == NULL) 509 if (m == NULL)
@@ -557,7 +550,6 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
557 550
558 if (opt & OPT_LIST_ONLY) { 551 if (opt & OPT_LIST_ONLY) {
559 int i; 552 int i;
560 char name[MODULE_NAME_LEN];
561 char *colon, *tokens[2]; 553 char *colon, *tokens[2];
562 parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read); 554 parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read);
563 555
@@ -569,10 +561,14 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
569 if (!colon) 561 if (!colon)
570 continue; 562 continue;
571 *colon = '\0'; 563 *colon = '\0';
572 filename2modname(tokens[0], name);
573 if (!argv[0]) 564 if (!argv[0])
574 puts(tokens[0]); 565 puts(tokens[0]);
575 else { 566 else {
567 char name[MODULE_NAME_LEN];
568 filename2modname(
569 bb_get_last_path_component_nostrip(tokens[0]),
570 name
571 );
576 for (i = 0; argv[i]; i++) { 572 for (i = 0; argv[i]; i++) {
577 if (fnmatch(argv[i], name, 0) == 0) { 573 if (fnmatch(argv[i], name, 0) == 0) {
578 puts(tokens[0]); 574 puts(tokens[0]);
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,
2255 * symbols so they cannot fudge it by adding the prefix on 2255 * symbols so they cannot fudge it by adding the prefix on
2256 * their references. 2256 * their references.
2257 */ 2257 */
2258 if (strncmp((char *)s->name, "GPLONLY_", 8) == 0) { 2258 if (is_prefixed_with((char *)s->name, "GPLONLY_")) {
2259#if ENABLE_FEATURE_CHECK_TAINTED_MODULE 2259#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
2260 if (gpl) 2260 if (gpl)
2261 s->name += 8; 2261 s->name += 8;
diff --git a/modutils/modutils.c b/modutils/modutils.c
index 6187ca72f..84300d931 100644
--- a/modutils/modutils.c
+++ b/modutils/modutils.c
@@ -47,18 +47,26 @@ int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim)
47 47
48char* FAST_FUNC filename2modname(const char *filename, char *modname) 48char* FAST_FUNC filename2modname(const char *filename, char *modname)
49{ 49{
50 char local_modname[MODULE_NAME_LEN];
50 int i; 51 int i;
51 char *from; 52 const char *from;
52 53
53 if (filename == NULL) 54 if (filename == NULL)
54 return NULL; 55 return NULL;
55 if (modname == NULL) 56 if (modname == NULL)
56 modname = xmalloc(MODULE_NAME_LEN); 57 modname = local_modname;
57 from = bb_get_last_path_component_nostrip(filename); 58 // Disabled since otherwise "modprobe dir/name" would work
59 // as if it is "modprobe name". It is unclear why
60 // 'basenamization' was here in the first place.
61 //from = bb_get_last_path_component_nostrip(filename);
62 from = filename;
58 for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++) 63 for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++)
59 modname[i] = (from[i] == '-') ? '_' : from[i]; 64 modname[i] = (from[i] == '-') ? '_' : from[i];
60 modname[i] = '\0'; 65 modname[i] = '\0';
61 66
67 if (modname == local_modname)
68 return xstrdup(modname);
69
62 return modname; 70 return modname;
63} 71}
64 72
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
750 750
751 Note that for busybox telnetd to work you need several things: 751 Note that for busybox telnetd to work you need several things:
752 First of all, your kernel needs: 752 First of all, your kernel needs:
753 UNIX98_PTYS=y 753 CONFIG_UNIX98_PTYS=y
754 DEVPTS_FS=y
755 754
756 Next, you need a /dev/pts directory on your root filesystem: 755 Next, you need a /dev/pts directory on your root filesystem:
757 756
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)
284 // Need to remove SUID_NEVER from applets.h for this to work 284 // Need to remove SUID_NEVER from applets.h for this to work
285 //xsetuid(getuid()); 285 //xsetuid(getuid());
286 286
287 err_str = xasprintf("interface %s %%s", device);
288 { 287 {
289 unsigned opt; 288 unsigned opt;
290 char *str_timeout; 289 char *str_timeout;
@@ -302,7 +301,7 @@ int arping_main(int argc UNUSED_PARAM, char **argv)
302 } 301 }
303 302
304 target = argv[optind]; 303 target = argv[optind];
305 304 err_str = xasprintf("interface %s %%s", device);
306 xfunc_error_retval = 2; 305 xfunc_error_retval = 2;
307 306
308 { 307 {
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,
194 if ((len != 1 || d->name[1] != '*') 194 if ((len != 1 || d->name[1] != '*')
195 /* we assume (do not check) that query_string 195 /* we assume (do not check) that query_string
196 * ends in ".in-addr.arpa" */ 196 * ends in ".in-addr.arpa" */
197 && strncmp(d->rip, query_string, strlen(d->rip)) == 0 197 && is_prefixed_with(query_string, d->rip)
198 ) { 198 ) {
199#if DEBUG 199#if DEBUG
200 fprintf(stderr, "Found name:%s\n", d->name); 200 fprintf(stderr, "Found name:%s\n", d->name);
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)
1174 1174
1175 //umask(077); - admin can set umask before starting us 1175 //umask(077); - admin can set umask before starting us
1176 1176
1177 /* Signals. We'll always take -EPIPE rather than a rude signal, thanks */ 1177 /* Signals */
1178 signal(SIGPIPE, SIG_IGN); 1178 bb_signals(0
1179 /* We'll always take EPIPE rather than a rude signal, thanks */
1180 + (1 << SIGPIPE)
1181 /* LIST command spawns chilren. Prevent zombies */
1182 + (1 << SIGCHLD)
1183 , SIG_IGN);
1179 1184
1180 /* Set up options on the command socket (do we need these all? why?) */ 1185 /* Set up options on the command socket (do we need these all? why?) */
1181 setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1)); 1186 setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1));
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)
697 goto config_error; 697 goto config_error;
698 } 698 }
699 *host_port++ = '\0'; 699 *host_port++ = '\0';
700 if (strncmp(host_port, "http://", 7) == 0) 700 if (is_prefixed_with(host_port, "http://"))
701 host_port += 7; 701 host_port += 7;
702 if (*host_port == '\0') { 702 if (*host_port == '\0') {
703 goto config_error; 703 goto config_error;
@@ -1894,7 +1894,7 @@ static Htaccess_Proxy *find_proxy_entry(const char *url)
1894{ 1894{
1895 Htaccess_Proxy *p; 1895 Htaccess_Proxy *p;
1896 for (p = proxy; p; p = p->next) { 1896 for (p = proxy; p; p = p->next) {
1897 if (strncmp(url, p->url_from, strlen(p->url_from)) == 0) 1897 if (is_prefixed_with(url, p->url_from))
1898 return p; 1898 return p;
1899 } 1899 }
1900 return NULL; 1900 return NULL;
@@ -2183,7 +2183,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2183 if (STRNCASECMP(iobuf, "Range:") == 0) { 2183 if (STRNCASECMP(iobuf, "Range:") == 0) {
2184 /* We know only bytes=NNN-[MMM] */ 2184 /* We know only bytes=NNN-[MMM] */
2185 char *s = skip_whitespace(iobuf + sizeof("Range:")-1); 2185 char *s = skip_whitespace(iobuf + sizeof("Range:")-1);
2186 if (strncmp(s, "bytes=", 6) == 0) { 2186 if (is_prefixed_with(s, "bytes=") == 0) {
2187 s += sizeof("bytes=")-1; 2187 s += sizeof("bytes=")-1;
2188 range_start = BB_STRTOOFF(s, &s, 10); 2188 range_start = BB_STRTOOFF(s, &s, 10);
2189 if (s[0] != '-' || range_start < 0) { 2189 if (s[0] != '-' || range_start < 0) {
@@ -2269,7 +2269,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2269 tptr = urlcopy + 1; /* skip first '/' */ 2269 tptr = urlcopy + 1; /* skip first '/' */
2270 2270
2271#if ENABLE_FEATURE_HTTPD_CGI 2271#if ENABLE_FEATURE_HTTPD_CGI
2272 if (strncmp(tptr, "cgi-bin/", 8) == 0) { 2272 if (is_prefixed_with(tptr, "cgi-bin/")) {
2273 if (tptr[8] == '\0') { 2273 if (tptr[8] == '\0') {
2274 /* protect listing "cgi-bin/" */ 2274 /* protect listing "cgi-bin/" */
2275 send_headers_and_exit(HTTP_FORBIDDEN); 2275 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)
289 /* "hwaddress <class> <address>": 289 /* "hwaddress <class> <address>":
290 * unlike ifconfig, ip doesnt want <class> 290 * unlike ifconfig, ip doesnt want <class>
291 * (usually "ether" keyword). Skip it. */ 291 * (usually "ether" keyword). Skip it. */
292 if (strncmp(command, "hwaddress", 9) == 0) { 292 if (is_prefixed_with(command, "hwaddress")) {
293 varvalue = skip_whitespace(skip_non_whitespace(varvalue)); 293 varvalue = skip_whitespace(skip_non_whitespace(varvalue));
294 } 294 }
295# endif 295# endif
@@ -298,7 +298,7 @@ static char *parse(const char *command, struct interface_defn_t *ifd)
298# if ENABLE_FEATURE_IFUPDOWN_IP 298# if ENABLE_FEATURE_IFUPDOWN_IP
299 /* Sigh... Add a special case for 'ip' to convert from 299 /* Sigh... Add a special case for 'ip' to convert from
300 * dotted quad to bit count style netmasks. */ 300 * dotted quad to bit count style netmasks. */
301 if (strncmp(command, "bnmask", 6) == 0) { 301 if (is_prefixed_with(command, "bnmask")) {
302 unsigned res; 302 unsigned res;
303 varvalue = get_var("netmask", 7, ifd); 303 varvalue = get_var("netmask", 7, ifd);
304 if (varvalue) { 304 if (varvalue) {
@@ -1159,12 +1159,12 @@ static char *run_mapping(char *physical, struct mapping_defn_t *map)
1159 1159
1160static llist_t *find_iface_state(llist_t *state_list, const char *iface) 1160static llist_t *find_iface_state(llist_t *state_list, const char *iface)
1161{ 1161{
1162 unsigned iface_len = strlen(iface);
1163 llist_t *search = state_list; 1162 llist_t *search = state_list;
1164 1163
1165 while (search) { 1164 while (search) {
1166 if ((strncmp(search->data, iface, iface_len) == 0) 1165 char *after_iface = is_prefixed_with(search->data, iface);
1167 && (search->data[iface_len] == '=') 1166 if (after_iface
1167 && *after_iface == '='
1168 ) { 1168 ) {
1169 return search; 1169 return search;
1170 } 1170 }
diff --git a/networking/inetd.c b/networking/inetd.c
index 584c5e5e4..dce5a0885 100644
--- a/networking/inetd.c
+++ b/networking/inetd.c
@@ -645,7 +645,7 @@ static servtab_t *dup_servtab(servtab_t *sep)
645} 645}
646 646
647/* gcc generates much more code if this is inlined */ 647/* gcc generates much more code if this is inlined */
648static servtab_t *parse_one_line(void) 648static NOINLINE servtab_t *parse_one_line(void)
649{ 649{
650 int argc; 650 int argc;
651 char *token[6+MAXARGV]; 651 char *token[6+MAXARGV];
@@ -675,6 +675,8 @@ static servtab_t *parse_one_line(void)
675 * default host for the following lines. */ 675 * default host for the following lines. */
676 free(default_local_hostname); 676 free(default_local_hostname);
677 default_local_hostname = sep->se_local_hostname; 677 default_local_hostname = sep->se_local_hostname;
678 /*sep->se_local_hostname = NULL; - redundant */
679 /* (we'll overwrite this field anyway) */
678 goto more; 680 goto more;
679 } 681 }
680 } else 682 } else
@@ -688,10 +690,10 @@ static servtab_t *parse_one_line(void)
688 parse_err: 690 parse_err:
689 bb_error_msg("parse error on line %u, line is ignored", 691 bb_error_msg("parse error on line %u, line is ignored",
690 parser->lineno); 692 parser->lineno);
691 free_servtab_strings(sep);
692 /* Just "goto more" can make sep to carry over e.g. 693 /* Just "goto more" can make sep to carry over e.g.
693 * "rpc"-ness (by having se_rpcver_lo != 0). 694 * "rpc"-ness (by having se_rpcver_lo != 0).
694 * We will be more paranoid: */ 695 * We will be more paranoid: */
696 free_servtab_strings(sep);
695 free(sep); 697 free(sep);
696 goto new; 698 goto new;
697 } 699 }
@@ -725,7 +727,7 @@ static servtab_t *parse_one_line(void)
725 goto parse_err; 727 goto parse_err;
726#endif 728#endif
727 } 729 }
728 if (strncmp(arg, "rpc/", 4) == 0) { 730 if (is_prefixed_with(arg, "rpc/")) {
729#if ENABLE_FEATURE_INETD_RPC 731#if ENABLE_FEATURE_INETD_RPC
730 unsigned n; 732 unsigned n;
731 arg += 4; 733 arg += 4;
@@ -815,7 +817,7 @@ static servtab_t *parse_one_line(void)
815 } 817 }
816#endif 818#endif
817 argc = 0; 819 argc = 0;
818 while ((arg = token[6+argc]) != NULL && argc < MAXARGV) 820 while (argc < MAXARGV && (arg = token[6+argc]) != NULL)
819 sep->se_argv[argc++] = xstrdup(arg); 821 sep->se_argv[argc++] = xstrdup(arg);
820 /* Some inetd.conf files have no argv's, not even argv[0]. 822 /* Some inetd.conf files have no argv's, not even argv[0].
821 * Fix them up. 823 * Fix them up.
@@ -1654,7 +1656,7 @@ static void FAST_FUNC daytime_stream(int s, servtab_t *sep UNUSED_PARAM)
1654{ 1656{
1655 time_t t; 1657 time_t t;
1656 1658
1657 t = time(NULL); 1659 time(&t);
1658 fdprintf(s, "%.24s\r\n", ctime(&t)); 1660 fdprintf(s, "%.24s\r\n", ctime(&t));
1659} 1661}
1660static void FAST_FUNC daytime_dg(int s, servtab_t *sep) 1662static void FAST_FUNC daytime_dg(int s, servtab_t *sep)
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)
91{ 91{
92 static char *buff; /* defaults to NULL */ 92 static char *buff; /* defaults to NULL */
93 93
94 free(buff);
95 if (sap->sa_family == 0xFFFF || sap->sa_family == 0) 94 if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
96 return "[NONE SET]"; 95 return "[NONE SET]";
96 free(buff);
97 buff = INET_rresolve((struct sockaddr_in *) sap, numeric, 0xffffff00); 97 buff = INET_rresolve((struct sockaddr_in *) sap, numeric, 0xffffff00);
98 return buff; 98 return buff;
99} 99}
@@ -173,9 +173,9 @@ static const char* FAST_FUNC INET6_sprint(struct sockaddr *sap, int numeric)
173{ 173{
174 static char *buff; 174 static char *buff;
175 175
176 free(buff);
177 if (sap->sa_family == 0xFFFF || sap->sa_family == 0) 176 if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
178 return "[NONE SET]"; 177 return "[NONE SET]";
178 free(buff);
179 buff = INET6_rresolve((struct sockaddr_in6 *) sap, numeric); 179 buff = INET6_rresolve((struct sockaddr_in6 *) sap, numeric);
180 return buff; 180 return buff;
181} 181}
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)
701 /* There was no "dev IFACE", but we need that */ 701 /* There was no "dev IFACE", but we need that */
702 bb_error_msg_and_die("need \"dev IFACE\""); 702 bb_error_msg_and_die("need \"dev IFACE\"");
703 } 703 }
704 if (l && strncmp(d, l, strlen(d)) != 0) { 704 if (l && !is_prefixed_with(l, d)) {
705 bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s)", d, l); 705 bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s)", d, l);
706 } 706 }
707 707
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)
161 if (*next) 161 if (*next)
162 *next++ = '\0'; 162 *next++ = '\0';
163 /* Check for selectors, mac= is assumed */ 163 /* Check for selectors, mac= is assumed */
164 if (strncmp(selector, "bus=", 4) == 0) { 164 if (is_prefixed_with(selector, "bus=")) {
165 ch->bus_info = xstrdup(selector + 4); 165 ch->bus_info = xstrdup(selector + 4);
166 found_selector++; 166 found_selector++;
167 } else if (strncmp(selector, "driver=", 7) == 0) { 167 } else if (is_prefixed_with(selector, "driver=")) {
168 ch->driver = xstrdup(selector + 7); 168 ch->driver = xstrdup(selector + 7);
169 found_selector++; 169 found_selector++;
170 } else if (strncmp(selector, "phyaddr=", 8) == 0) { 170 } else if (is_prefixed_with(selector, "phyaddr=")) {
171 ch->phy_address = xatoi_positive(selector + 8); 171 ch->phy_address = xatoi_positive(selector + 8);
172 found_selector++; 172 found_selector++;
173 } else { 173 } else {
174#endif 174#endif
175 lmac = xmalloc(ETH_ALEN); 175 lmac = xmalloc(ETH_ALEN);
176 ch->mac = ether_aton_r(selector + (strncmp(selector, "mac=", 4) != 0 ? 0 : 4), lmac); 176 ch->mac = ether_aton_r(selector + (is_prefixed_with(selector, "mac=") ? 4 : 0), lmac);
177 if (ch->mac == NULL) 177 if (ch->mac == NULL)
178 bb_error_msg_and_die("can't parse %s", selector); 178 bb_error_msg_and_die("can't parse %s", selector);
179#if ENABLE_FEATURE_NAMEIF_EXTENDED 179#if ENABLE_FEATURE_NAMEIF_EXTENDED
diff --git a/networking/netstat.c b/networking/netstat.c
index f80b845bc..1303d3f3d 100644
--- a/networking/netstat.c
+++ b/networking/netstat.c
@@ -228,12 +228,12 @@ static long extract_socket_inode(const char *lname)
228{ 228{
229 long inode = -1; 229 long inode = -1;
230 230
231 if (strncmp(lname, "socket:[", sizeof("socket:[")-1) == 0) { 231 if (is_prefixed_with(lname, "socket:[")) {
232 /* "socket:[12345]", extract the "12345" as inode */ 232 /* "socket:[12345]", extract the "12345" as inode */
233 inode = bb_strtoul(lname + sizeof("socket:[")-1, (char**)&lname, 0); 233 inode = bb_strtoul(lname + sizeof("socket:[")-1, (char**)&lname, 0);
234 if (*lname != ']') 234 if (*lname != ']')
235 inode = -1; 235 inode = -1;
236 } else if (strncmp(lname, "[0000]:", sizeof("[0000]:")-1) == 0) { 236 } else if (is_prefixed_with(lname, "[0000]:")) {
237 /* "[0000]:12345", extract the "12345" as inode */ 237 /* "[0000]:12345", extract the "12345" as inode */
238 inode = bb_strtoul(lname + sizeof("[0000]:")-1, NULL, 0); 238 inode = bb_strtoul(lname + sizeof("[0000]:")-1, NULL, 0);
239 if (errno) /* not NUL terminated? */ 239 if (errno) /* not NUL terminated? */
@@ -622,7 +622,7 @@ static int FAST_FUNC unix_do_one(char *line)
622 622
623 /* TODO: currently we stop at first NUL byte. Is it a problem? */ 623 /* TODO: currently we stop at first NUL byte. Is it a problem? */
624 line += path_ofs; 624 line += path_ofs;
625 *strchrnul(line, '\n') = '\0'; 625 chomp(line);
626 while (*line) 626 while (*line)
627 fputc_printable(*line++, stdout); 627 fputc_printable(*line++, stdout);
628 bb_putchar('\n'); 628 bb_putchar('\n');
diff --git a/networking/ntpd.c b/networking/ntpd.c
index 2d4f076d9..0233ed82c 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -1,30 +1,43 @@
1/* 1/*
2 * NTP client/server, based on OpenNTPD 3.9p1 2 * NTP client/server, based on OpenNTPD 3.9p1
3 * 3 *
4 * Author: Adam Tkac <vonsch@gmail.com> 4 * Busybox port author: Adam Tkac (C) 2009 <vonsch@gmail.com>
5 * 5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree. 6 * OpenNTPd 3.9p1 copyright holders:
7 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
8 * Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
9 *
10 * OpenNTPd code is licensed under ISC-style licence:
11 *
12 * Permission to use, copy, modify, and distribute this software for any
13 * purpose with or without fee is hereby granted, provided that the above
14 * copyright notice and this permission notice appear in all copies.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
21 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
22 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 ***********************************************************************
7 * 24 *
8 * Parts of OpenNTPD clock syncronization code is replaced by 25 * Parts of OpenNTPD clock syncronization code is replaced by
9 * code which is based on ntp-4.2.6, whuch carries the following 26 * code which is based on ntp-4.2.6, which carries the following
10 * copyright notice: 27 * copyright notice:
11 * 28 *
12 *********************************************************************** 29 * Copyright (c) University of Delaware 1992-2009
13 * * 30 *
14 * Copyright (c) University of Delaware 1992-2009 * 31 * Permission to use, copy, modify, and distribute this software and
15 * * 32 * its documentation for any purpose with or without fee is hereby
16 * Permission to use, copy, modify, and distribute this software and * 33 * granted, provided that the above copyright notice appears in all
17 * its documentation for any purpose with or without fee is hereby * 34 * copies and that both the copyright notice and this permission
18 * granted, provided that the above copyright notice appears in all * 35 * notice appear in supporting documentation, and that the name
19 * copies and that both the copyright notice and this permission * 36 * University of Delaware not be used in advertising or publicity
20 * notice appear in supporting documentation, and that the name * 37 * pertaining to distribution of the software without specific,
21 * University of Delaware not be used in advertising or publicity * 38 * written prior permission. The University of Delaware makes no
22 * pertaining to distribution of the software without specific, * 39 * representations about the suitability this software for any
23 * written prior permission. The University of Delaware makes no * 40 * purpose. It is provided "as is" without express or implied warranty.
24 * representations about the suitability this software for any *
25 * purpose. It is provided "as is" without express or implied *
26 * warranty. *
27 * *
28 *********************************************************************** 41 ***********************************************************************
29 */ 42 */
30 43
@@ -37,14 +50,15 @@
37//usage: "\n -q Quit after clock is set" 50//usage: "\n -q Quit after clock is set"
38//usage: "\n -N Run at high priority" 51//usage: "\n -N Run at high priority"
39//usage: "\n -w Do not set time (only query peers), implies -n" 52//usage: "\n -w Do not set time (only query peers), implies -n"
40//usage: IF_FEATURE_NTPD_SERVER(
41//usage: "\n -l Run as server on port 123"
42//usage: "\n -I IFACE Bind server to IFACE, implies -l"
43//usage: )
44//usage: "\n -S PROG Run PROG after stepping time, stratum change, and every 11 mins" 53//usage: "\n -S PROG Run PROG after stepping time, stratum change, and every 11 mins"
45//usage: "\n -p PEER Obtain time from PEER (may be repeated)" 54//usage: "\n -p PEER Obtain time from PEER (may be repeated)"
46//usage: IF_FEATURE_NTPD_CONF( 55//usage: IF_FEATURE_NTPD_CONF(
47//usage: "\n If -p is not given, read /etc/ntp.conf" 56//usage: "\n If -p is not given, 'server HOST' lines"
57//usage: "\n from /etc/ntp.conf are used"
58//usage: )
59//usage: IF_FEATURE_NTPD_SERVER(
60//usage: "\n -l Also run as server on port 123"
61//usage: "\n -I IFACE Bind server to IFACE, implies -l"
48//usage: ) 62//usage: )
49 63
50// -l and -p options are not compatible with "standard" ntpd: 64// -l and -p options are not compatible with "standard" ntpd:
@@ -363,8 +377,6 @@ struct globals {
363 */ 377 */
364#define G_precision_sec 0.002 378#define G_precision_sec 0.002
365 uint8_t stratum; 379 uint8_t stratum;
366 /* Bool. After set to 1, never goes back to 0: */
367 smallint initial_poll_complete;
368 380
369#define STATE_NSET 0 /* initial state, "nothing is set" */ 381#define STATE_NSET 0 /* initial state, "nothing is set" */
370//#define STATE_FSET 1 /* frequency set from file */ 382//#define STATE_FSET 1 /* frequency set from file */
@@ -1071,7 +1083,7 @@ select_and_cluster(void)
1071 1083
1072 num_points = 0; 1084 num_points = 0;
1073 item = G.ntp_peers; 1085 item = G.ntp_peers;
1074 if (G.initial_poll_complete) while (item != NULL) { 1086 while (item != NULL) {
1075 double rd, offset; 1087 double rd, offset;
1076 1088
1077 p = (peer_t *) item->data; 1089 p = (peer_t *) item->data;
@@ -1636,7 +1648,7 @@ update_local_clock(peer_t *p)
1636 if (G.ntp_status & LI_MINUSSEC) 1648 if (G.ntp_status & LI_MINUSSEC)
1637 tmx.status |= STA_DEL; 1649 tmx.status |= STA_DEL;
1638 1650
1639 tmx.constant = G.poll_exp - 4; 1651 tmx.constant = (int)G.poll_exp - 4 > 0 ? (int)G.poll_exp - 4 : 0;
1640 /* EXPERIMENTAL. 1652 /* EXPERIMENTAL.
1641 * The below if statement should be unnecessary, but... 1653 * The below if statement should be unnecessary, but...
1642 * It looks like Linux kernel's PLL is far too gentle in changing 1654 * It looks like Linux kernel's PLL is far too gentle in changing
@@ -2272,7 +2284,6 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv)
2272 VERB4 bb_error_msg("disabling burst mode"); 2284 VERB4 bb_error_msg("disabling burst mode");
2273 G.polladj_count = 0; 2285 G.polladj_count = 0;
2274 G.poll_exp = MINPOLL; 2286 G.poll_exp = MINPOLL;
2275 G.initial_poll_complete = 1;
2276 } 2287 }
2277 send_query_to_peer(p); 2288 send_query_to_peer(p);
2278 } else { 2289 } else {
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 @@
1/*
2 * NTP client/server, based on OpenNTPD 3.9p1
3 *
4 * Author: Adam Tkac <vonsch@gmail.com>
5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree.
7 */
8#include "libbb.h"
9#include <netinet/ip.h> /* For IPTOS_LOWDELAY definition */
10#include <sys/resource.h> /* setpriority */
11#ifndef IPTOS_LOWDELAY
12# define IPTOS_LOWDELAY 0x10
13#endif
14
15
16/* Sync to peers every N secs */
17#define INTERVAL_QUERY_NORMAL 30
18#define INTERVAL_QUERY_PATHETIC 60
19#define INTERVAL_QUERY_AGRESSIVE 5
20
21/* Bad if *less than* TRUSTLEVEL_BADPEER */
22#define TRUSTLEVEL_BADPEER 6
23#define TRUSTLEVEL_PATHETIC 2
24#define TRUSTLEVEL_AGRESSIVE 8
25#define TRUSTLEVEL_MAX 10
26
27#define QSCALE_OFF_MIN 0.05
28#define QSCALE_OFF_MAX 0.50
29
30/* Single query might take N secs max */
31#define QUERYTIME_MAX 15
32/* Min offset for settime at start. "man ntpd" says it's 128 ms */
33#define STEPTIME_MIN_OFFSET 0.128
34
35typedef struct {
36 uint32_t int_partl;
37 uint32_t fractionl;
38} l_fixedpt_t;
39
40typedef struct {
41 uint16_t int_parts;
42 uint16_t fractions;
43} s_fixedpt_t;
44
45enum {
46 NTP_DIGESTSIZE = 16,
47 NTP_MSGSIZE_NOAUTH = 48,
48 NTP_MSGSIZE = (NTP_MSGSIZE_NOAUTH + 4 + NTP_DIGESTSIZE),
49};
50
51typedef struct {
52 uint8_t m_status; /* status of local clock and leap info */
53 uint8_t m_stratum; /* stratum level */
54 uint8_t m_ppoll; /* poll value */
55 int8_t m_precision_exp;
56 s_fixedpt_t m_rootdelay;
57 s_fixedpt_t m_dispersion;
58 uint32_t m_refid;
59 l_fixedpt_t m_reftime;
60 l_fixedpt_t m_orgtime;
61 l_fixedpt_t m_rectime;
62 l_fixedpt_t m_xmttime;
63 uint32_t m_keyid;
64 uint8_t m_digest[NTP_DIGESTSIZE];
65} msg_t;
66
67enum {
68 NTP_VERSION = 4,
69 NTP_MAXSTRATUM = 15,
70
71 /* Status Masks */
72 MODE_MASK = (7 << 0),
73 VERSION_MASK = (7 << 3),
74 VERSION_SHIFT = 3,
75 LI_MASK = (3 << 6),
76
77 /* Leap Second Codes (high order two bits of m_status) */
78 LI_NOWARNING = (0 << 6), /* no warning */
79 LI_PLUSSEC = (1 << 6), /* add a second (61 seconds) */
80 LI_MINUSSEC = (2 << 6), /* minus a second (59 seconds) */
81 LI_ALARM = (3 << 6), /* alarm condition */
82
83 /* Mode values */
84 MODE_RES0 = 0, /* reserved */
85 MODE_SYM_ACT = 1, /* symmetric active */
86 MODE_SYM_PAS = 2, /* symmetric passive */
87 MODE_CLIENT = 3, /* client */
88 MODE_SERVER = 4, /* server */
89 MODE_BROADCAST = 5, /* broadcast */
90 MODE_RES1 = 6, /* reserved for NTP control message */
91 MODE_RES2 = 7, /* reserved for private use */
92};
93
94#define OFFSET_1900_1970 2208988800UL /* 1970 - 1900 in seconds */
95
96typedef struct {
97 double d_offset;
98 double d_delay;
99 //UNUSED: double d_error;
100 time_t d_rcv_time;
101 uint32_t d_refid4;
102 uint8_t d_leap;
103 uint8_t d_stratum;
104 uint8_t d_good;
105} datapoint_t;
106
107#define NUM_DATAPOINTS 8
108typedef struct {
109 len_and_sockaddr *p_lsa;
110 char *p_dotted;
111 /* When to send new query (if p_fd == -1)
112 * or when receive times out (if p_fd >= 0): */
113 time_t next_action_time;
114 int p_fd;
115 uint8_t p_datapoint_idx;
116 uint8_t p_trustlevel;
117 double p_xmttime;
118 datapoint_t update;
119 datapoint_t p_datapoint[NUM_DATAPOINTS];
120 msg_t p_xmt_msg;
121} peer_t;
122
123enum {
124 OPT_n = (1 << 0),
125 OPT_q = (1 << 1),
126 OPT_N = (1 << 2),
127 OPT_x = (1 << 3),
128 /* Insert new options above this line. */
129 /* Non-compat options: */
130 OPT_p = (1 << 4),
131 OPT_l = (1 << 5) * ENABLE_FEATURE_NTPD_SERVER,
132};
133
134
135struct globals {
136 /* total round trip delay to currently selected reference clock */
137 double rootdelay;
138 /* reference timestamp: time when the system clock was last set or corrected */
139 double reftime;
140 llist_t *ntp_peers;
141#if ENABLE_FEATURE_NTPD_SERVER
142 int listen_fd;
143#endif
144 unsigned verbose;
145 unsigned peer_cnt;
146 unsigned scale;
147 uint32_t refid;
148 uint32_t refid4;
149 uint8_t synced;
150 uint8_t leap;
151#define G_precision_exp -6
152// int8_t precision_exp;
153 uint8_t stratum;
154 uint8_t time_was_stepped;
155 uint8_t first_adj_done;
156};
157#define G (*ptr_to_globals)
158
159static const int const_IPTOS_LOWDELAY = IPTOS_LOWDELAY;
160
161
162static void
163set_next(peer_t *p, unsigned t)
164{
165 p->next_action_time = time(NULL) + t;
166}
167
168static void
169add_peers(char *s)
170{
171 peer_t *p;
172
173 p = xzalloc(sizeof(*p));
174 p->p_lsa = xhost2sockaddr(s, 123);
175 p->p_dotted = xmalloc_sockaddr2dotted_noport(&p->p_lsa->u.sa);
176 p->p_fd = -1;
177 p->p_xmt_msg.m_status = MODE_CLIENT | (NTP_VERSION << 3);
178 p->p_trustlevel = TRUSTLEVEL_PATHETIC;
179 p->next_action_time = time(NULL); /* = set_next(p, 0); */
180
181 llist_add_to(&G.ntp_peers, p);
182 G.peer_cnt++;
183}
184
185static double
186gettime1900d(void)
187{
188 struct timeval tv;
189 gettimeofday(&tv, NULL); /* never fails */
190 return (tv.tv_sec + 1.0e-6 * tv.tv_usec + OFFSET_1900_1970);
191}
192
193static void
194d_to_tv(double d, struct timeval *tv)
195{
196 tv->tv_sec = (long)d;
197 tv->tv_usec = (d - tv->tv_sec) * 1000000;
198}
199
200static double
201lfp_to_d(l_fixedpt_t lfp)
202{
203 double ret;
204 lfp.int_partl = ntohl(lfp.int_partl);
205 lfp.fractionl = ntohl(lfp.fractionl);
206 ret = (double)lfp.int_partl + ((double)lfp.fractionl / UINT_MAX);
207 return ret;
208}
209
210#if 0 //UNUSED
211static double
212sfp_to_d(s_fixedpt_t sfp)
213{
214 double ret;
215 sfp.int_parts = ntohs(sfp.int_parts);
216 sfp.fractions = ntohs(sfp.fractions);
217 ret = (double)sfp.int_parts + ((double)sfp.fractions / USHRT_MAX);
218 return ret;
219}
220#endif
221
222#if ENABLE_FEATURE_NTPD_SERVER
223static l_fixedpt_t
224d_to_lfp(double d)
225{
226 l_fixedpt_t lfp;
227 lfp.int_partl = (uint32_t)d;
228 lfp.fractionl = (uint32_t)((d - lfp.int_partl) * UINT_MAX);
229 lfp.int_partl = htonl(lfp.int_partl);
230 lfp.fractionl = htonl(lfp.fractionl);
231 return lfp;
232}
233
234static s_fixedpt_t
235d_to_sfp(double d)
236{
237 s_fixedpt_t sfp;
238 sfp.int_parts = (uint16_t)d;
239 sfp.fractions = (uint16_t)((d - sfp.int_parts) * USHRT_MAX);
240 sfp.int_parts = htons(sfp.int_parts);
241 sfp.fractions = htons(sfp.fractions);
242 return sfp;
243}
244#endif
245
246static unsigned
247error_interval(void)
248{
249 unsigned interval, r;
250 interval = INTERVAL_QUERY_PATHETIC * QSCALE_OFF_MAX / QSCALE_OFF_MIN;
251 r = (unsigned)rand() % (unsigned)(interval / 10);
252 return (interval + r);
253}
254
255static int
256do_sendto(int fd,
257 const struct sockaddr *from, const struct sockaddr *to, socklen_t addrlen,
258 msg_t *msg, ssize_t len)
259{
260 ssize_t ret;
261
262 errno = 0;
263 if (!from) {
264 ret = sendto(fd, msg, len, MSG_DONTWAIT, to, addrlen);
265 } else {
266 ret = send_to_from(fd, msg, len, MSG_DONTWAIT, to, from, addrlen);
267 }
268 if (ret != len) {
269 bb_perror_msg("send failed");
270 return -1;
271 }
272 return 0;
273}
274
275static int
276send_query_to_peer(peer_t *p)
277{
278 // Why do we need to bind()?
279 // See what happens when we don't bind:
280 //
281 // socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
282 // setsockopt(3, SOL_IP, IP_TOS, [16], 4) = 0
283 // gettimeofday({1259071266, 327885}, NULL) = 0
284 // sendto(3, "xxx", 48, MSG_DONTWAIT, {sa_family=AF_INET, sin_port=htons(123), sin_addr=inet_addr("10.34.32.125")}, 16) = 48
285 // ^^^ we sent it from some source port picked by kernel.
286 // time(NULL) = 1259071266
287 // write(2, "ntpd: entering poll 15 secs\n", 28) = 28
288 // poll([{fd=3, events=POLLIN}], 1, 15000) = 1 ([{fd=3, revents=POLLIN}])
289 // recv(3, "yyy", 68, MSG_DONTWAIT) = 48
290 // ^^^ this recv will receive packets to any local port!
291 //
292 // Uncomment this and use strace to see it in action:
293#define PROBE_LOCAL_ADDR // { len_and_sockaddr lsa; lsa.len = LSA_SIZEOF_SA; getsockname(p->query.fd, &lsa.u.sa, &lsa.len); }
294
295 if (p->p_fd == -1) {
296 int fd, family;
297 len_and_sockaddr *local_lsa;
298
299 family = p->p_lsa->u.sa.sa_family;
300 p->p_fd = fd = xsocket_type(&local_lsa, family, SOCK_DGRAM);
301 /* local_lsa has "null" address and port 0 now.
302 * bind() ensures we have a *particular port* selected by kernel
303 * and remembered in p->p_fd, thus later recv(p->p_fd)
304 * receives only packets sent to this port.
305 */
306 PROBE_LOCAL_ADDR
307 xbind(fd, &local_lsa->u.sa, local_lsa->len);
308 PROBE_LOCAL_ADDR
309#if ENABLE_FEATURE_IPV6
310 if (family == AF_INET)
311#endif
312 setsockopt(fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY));
313 free(local_lsa);
314 }
315
316 /*
317 * Send out a random 64-bit number as our transmit time. The NTP
318 * server will copy said number into the originate field on the
319 * response that it sends us. This is totally legal per the SNTP spec.
320 *
321 * The impact of this is two fold: we no longer send out the current
322 * system time for the world to see (which may aid an attacker), and
323 * it gives us a (not very secure) way of knowing that we're not
324 * getting spoofed by an attacker that can't capture our traffic
325 * but can spoof packets from the NTP server we're communicating with.
326 *
327 * Save the real transmit timestamp locally.
328 */
329 p->p_xmt_msg.m_xmttime.int_partl = rand();
330 p->p_xmt_msg.m_xmttime.fractionl = rand();
331 p->p_xmttime = gettime1900d();
332
333 if (do_sendto(p->p_fd, /*from:*/ NULL, /*to:*/ &p->p_lsa->u.sa, /*addrlen:*/ p->p_lsa->len,
334 &p->p_xmt_msg, NTP_MSGSIZE_NOAUTH) == -1
335 ) {
336 close(p->p_fd);
337 p->p_fd = -1;
338 set_next(p, INTERVAL_QUERY_PATHETIC);
339 return -1;
340 }
341
342 if (G.verbose)
343 bb_error_msg("sent query to %s", p->p_dotted);
344 set_next(p, QUERYTIME_MAX);
345
346 return 0;
347}
348
349
350/* Time is stepped only once, when the first packet from a peer is received.
351 */
352static void
353step_time_once(double offset)
354{
355 double dtime;
356 llist_t *item;
357 struct timeval tv;
358 char buf[80];
359 time_t tval;
360
361 if (G.time_was_stepped)
362 goto bail;
363 G.time_was_stepped = 1;
364
365 /* if the offset is small, don't step, slew (later) */
366 if (offset < STEPTIME_MIN_OFFSET && offset > -STEPTIME_MIN_OFFSET)
367 goto bail;
368
369 gettimeofday(&tv, NULL); /* never fails */
370 dtime = offset + tv.tv_sec;
371 dtime += 1.0e-6 * tv.tv_usec;
372 d_to_tv(dtime, &tv);
373
374 if (settimeofday(&tv, NULL) == -1)
375 bb_perror_msg_and_die("settimeofday");
376
377 tval = tv.tv_sec;
378 strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &tval);
379
380 bb_error_msg("setting clock to %s (offset %fs)", buf, offset);
381
382 for (item = G.ntp_peers; item != NULL; item = item->link) {
383 peer_t *p = (peer_t *) item->data;
384 p->next_action_time -= (time_t)offset;
385 }
386
387 bail:
388 if (option_mask32 & OPT_q)
389 exit(0);
390}
391
392
393/* Time is periodically slewed when we collect enough
394 * good data points.
395 */
396static int
397compare_offsets(const void *aa, const void *bb)
398{
399 const peer_t *const *a = aa;
400 const peer_t *const *b = bb;
401 if ((*a)->update.d_offset < (*b)->update.d_offset)
402 return -1;
403 return ((*a)->update.d_offset > (*b)->update.d_offset);
404}
405static unsigned
406updated_scale(double offset)
407{
408 if (offset < 0)
409 offset = -offset;
410 if (offset > QSCALE_OFF_MAX)
411 return 1;
412 if (offset < QSCALE_OFF_MIN)
413 return QSCALE_OFF_MAX / QSCALE_OFF_MIN;
414 return QSCALE_OFF_MAX / offset;
415}
416static void
417slew_time(void)
418{
419 llist_t *item;
420 double offset_median;
421 struct timeval tv;
422
423 {
424 peer_t **peers = xzalloc(sizeof(peers[0]) * G.peer_cnt);
425 unsigned goodpeer_cnt = 0;
426 unsigned middle;
427
428 for (item = G.ntp_peers; item != NULL; item = item->link) {
429 peer_t *p = (peer_t *) item->data;
430 if (p->p_trustlevel < TRUSTLEVEL_BADPEER)
431 continue;
432 if (!p->update.d_good) {
433 free(peers);
434 return;
435 }
436 peers[goodpeer_cnt++] = p;
437 }
438
439 if (goodpeer_cnt == 0) {
440 free(peers);
441 goto clear_good;
442 }
443
444 qsort(peers, goodpeer_cnt, sizeof(peers[0]), compare_offsets);
445
446 middle = goodpeer_cnt / 2;
447 if (middle != 0 && (goodpeer_cnt & 1) == 0) {
448 offset_median = (peers[middle-1]->update.d_offset + peers[middle]->update.d_offset) / 2;
449 G.rootdelay = (peers[middle-1]->update.d_delay + peers[middle]->update.d_delay) / 2;
450 G.stratum = 1 + MAX(peers[middle-1]->update.d_stratum, peers[middle]->update.d_stratum);
451 } else {
452 offset_median = peers[middle]->update.d_offset;
453 G.rootdelay = peers[middle]->update.d_delay;
454 G.stratum = 1 + peers[middle]->update.d_stratum;
455 }
456 G.leap = peers[middle]->update.d_leap;
457 G.refid4 = peers[middle]->update.d_refid4;
458 G.refid =
459#if ENABLE_FEATURE_IPV6
460 peers[middle]->p_lsa->u.sa.sa_family != AF_INET ?
461 G.refid4 :
462#endif
463 peers[middle]->p_lsa->u.sin.sin_addr.s_addr;
464 free(peers);
465 }
466//TODO: if (offset_median > BIG) step_time(offset_median)?
467
468 G.scale = updated_scale(offset_median);
469
470 bb_error_msg("adjusting clock by %fs, our stratum is %u, time scale %u",
471 offset_median, G.stratum, G.scale);
472
473 errno = 0;
474 d_to_tv(offset_median, &tv);
475 if (adjtime(&tv, &tv) == -1)
476 bb_perror_msg_and_die("adjtime failed");
477 if (G.verbose >= 2)
478 bb_error_msg("old adjust: %d.%06u", (int)tv.tv_sec, (unsigned)tv.tv_usec);
479
480 if (G.first_adj_done) {
481 uint8_t synced = (tv.tv_sec == 0 && tv.tv_usec == 0);
482 if (synced != G.synced) {
483 G.synced = synced;
484 bb_error_msg("clock is %ssynced", synced ? "" : "un");
485 }
486 }
487 G.first_adj_done = 1;
488
489 G.reftime = gettime1900d();
490
491 clear_good:
492 for (item = G.ntp_peers; item != NULL; item = item->link) {
493 peer_t *p = (peer_t *) item->data;
494 p->update.d_good = 0;
495 }
496}
497
498static void
499update_peer_data(peer_t *p)
500{
501 /* Clock filter.
502 * Find the datapoint with the lowest delay.
503 * Use that as the peer update.
504 * Invalidate it and all older ones.
505 */
506 int i;
507 int best = -1;
508 int good = 0;
509
510 for (i = 0; i < NUM_DATAPOINTS; i++) {
511 if (p->p_datapoint[i].d_good) {
512 good++;
513 if (best < 0 || p->p_datapoint[i].d_delay < p->p_datapoint[best].d_delay)
514 best = i;
515 }
516 }
517
518 if (good < 8) //FIXME: was it meant to be NUM_DATAPOINTS, not 8?
519 return;
520
521 p->update = p->p_datapoint[best]; /* struct copy */
522 slew_time();
523
524 for (i = 0; i < NUM_DATAPOINTS; i++)
525 if (p->p_datapoint[i].d_rcv_time <= p->p_datapoint[best].d_rcv_time)
526 p->p_datapoint[i].d_good = 0;
527}
528
529static unsigned
530scale_interval(unsigned requested)
531{
532 unsigned interval, r;
533 interval = requested * G.scale;
534 r = (unsigned)rand() % (unsigned)(MAX(5, interval / 10));
535 return (interval + r);
536}
537static void
538recv_and_process_peer_pkt(peer_t *p)
539{
540 ssize_t size;
541 msg_t msg;
542 double T1, T2, T3, T4;
543 unsigned interval;
544 datapoint_t *datapoint;
545
546 /* We can recvfrom here and check from.IP, but some multihomed
547 * ntp servers reply from their *other IP*.
548 * TODO: maybe we should check at least what we can: from.port == 123?
549 */
550 size = recv(p->p_fd, &msg, sizeof(msg), MSG_DONTWAIT);
551 if (size == -1) {
552 bb_perror_msg("recv(%s) error", p->p_dotted);
553 if (errno == EHOSTUNREACH || errno == EHOSTDOWN
554 || errno == ENETUNREACH || errno == ENETDOWN
555 || errno == ECONNREFUSED || errno == EADDRNOTAVAIL
556 || errno == EAGAIN
557 ) {
558//TODO: always do this?
559 set_next(p, error_interval());
560 goto close_sock;
561 }
562 xfunc_die();
563 }
564
565 if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
566 bb_error_msg("malformed packet received from %s", p->p_dotted);
567 goto bail;
568 }
569
570 if (msg.m_orgtime.int_partl != p->p_xmt_msg.m_xmttime.int_partl
571 || msg.m_orgtime.fractionl != p->p_xmt_msg.m_xmttime.fractionl
572 ) {
573 goto bail;
574 }
575
576 if ((msg.m_status & LI_ALARM) == LI_ALARM
577 || msg.m_stratum == 0
578 || msg.m_stratum > NTP_MAXSTRATUM
579 ) {
580// TODO: stratum 0 responses may have commands in 32-bit m_refid field:
581// "DENY", "RSTR" - peer does not like us at all
582// "RATE" - peer is overloaded, reduce polling freq
583 interval = error_interval();
584 bb_error_msg("reply from %s: not synced, next query in %us", p->p_dotted, interval);
585 goto close_sock;
586 }
587
588 /*
589 * From RFC 2030 (with a correction to the delay math):
590 *
591 * Timestamp Name ID When Generated
592 * ------------------------------------------------------------
593 * Originate Timestamp T1 time request sent by client
594 * Receive Timestamp T2 time request received by server
595 * Transmit Timestamp T3 time reply sent by server
596 * Destination Timestamp T4 time reply received by client
597 *
598 * The roundtrip delay and local clock offset are defined as
599 *
600 * delay = (T4 - T1) - (T3 - T2); offset = ((T2 - T1) + (T3 - T4)) / 2
601 */
602 T1 = p->p_xmttime;
603 T2 = lfp_to_d(msg.m_rectime);
604 T3 = lfp_to_d(msg.m_xmttime);
605 T4 = gettime1900d();
606
607 datapoint = &p->p_datapoint[p->p_datapoint_idx];
608
609 datapoint->d_offset = ((T2 - T1) + (T3 - T4)) / 2;
610 datapoint->d_delay = (T4 - T1) - (T3 - T2);
611 if (datapoint->d_delay < 0) {
612 bb_error_msg("reply from %s: negative delay %f", p->p_dotted, datapoint->d_delay);
613 interval = error_interval();
614 set_next(p, interval);
615 goto close_sock;
616 }
617 //UNUSED: datapoint->d_error = (T2 - T1) - (T3 - T4);
618 datapoint->d_rcv_time = (time_t)(T4 - OFFSET_1900_1970); /* = time(NULL); */
619 datapoint->d_good = 1;
620
621 datapoint->d_leap = (msg.m_status & LI_MASK);
622 //UNUSED: datapoint->o_precision = msg.m_precision_exp;
623 //UNUSED: datapoint->o_rootdelay = sfp_to_d(msg.m_rootdelay);
624 //UNUSED: datapoint->o_rootdispersion = sfp_to_d(msg.m_dispersion);
625 //UNUSED: datapoint->d_refid = ntohl(msg.m_refid);
626 datapoint->d_refid4 = msg.m_xmttime.fractionl;
627 //UNUSED: datapoint->o_reftime = lfp_to_d(msg.m_reftime);
628 //UNUSED: datapoint->o_poll = msg.m_ppoll;
629 datapoint->d_stratum = msg.m_stratum;
630
631 if (p->p_trustlevel < TRUSTLEVEL_PATHETIC)
632 interval = scale_interval(INTERVAL_QUERY_PATHETIC);
633 else if (p->p_trustlevel < TRUSTLEVEL_AGRESSIVE)
634 interval = scale_interval(INTERVAL_QUERY_AGRESSIVE);
635 else
636 interval = scale_interval(INTERVAL_QUERY_NORMAL);
637
638 set_next(p, interval);
639
640 /* Every received reply which we do not discard increases trust */
641 if (p->p_trustlevel < TRUSTLEVEL_MAX) {
642 p->p_trustlevel++;
643 if (p->p_trustlevel == TRUSTLEVEL_BADPEER)
644 bb_error_msg("peer %s now valid", p->p_dotted);
645 }
646
647 if (G.verbose)
648 bb_error_msg("reply from %s: offset %f delay %f, next query in %us", p->p_dotted,
649 datapoint->d_offset, datapoint->d_delay, interval);
650
651 update_peer_data(p);
652//TODO: do it after all peers had a chance to return at least one reply?
653 step_time_once(datapoint->d_offset);
654
655 p->p_datapoint_idx++;
656 if (p->p_datapoint_idx >= NUM_DATAPOINTS)
657 p->p_datapoint_idx = 0;
658
659 close_sock:
660 /* We do not expect any more packets from this peer for now.
661 * Closing the socket informs kernel about it.
662 * We open a new socket when we send a new query.
663 */
664 close(p->p_fd);
665 p->p_fd = -1;
666 bail:
667 return;
668}
669
670#if ENABLE_FEATURE_NTPD_SERVER
671static void
672recv_and_process_client_pkt(void /*int fd*/)
673{
674 ssize_t size;
675 uint8_t version;
676 double rectime;
677 len_and_sockaddr *to;
678 struct sockaddr *from;
679 msg_t msg;
680 uint8_t query_status;
681 uint8_t query_ppoll;
682 l_fixedpt_t query_xmttime;
683
684 to = get_sock_lsa(G.listen_fd);
685 from = xzalloc(to->len);
686
687 size = recv_from_to(G.listen_fd, &msg, sizeof(msg), MSG_DONTWAIT, from, &to->u.sa, to->len);
688 if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
689 char *addr;
690 if (size < 0) {
691 if (errno == EAGAIN)
692 goto bail;
693 bb_perror_msg_and_die("recv");
694 }
695 addr = xmalloc_sockaddr2dotted_noport(from);
696 bb_error_msg("malformed packet received from %s: size %u", addr, (int)size);
697 free(addr);
698 goto bail;
699 }
700
701 query_status = msg.m_status;
702 query_ppoll = msg.m_ppoll;
703 query_xmttime = msg.m_xmttime;
704
705 /* Build a reply packet */
706 memset(&msg, 0, sizeof(msg));
707 msg.m_status = G.synced ? G.leap : LI_ALARM;
708 msg.m_status |= (query_status & VERSION_MASK);
709 msg.m_status |= ((query_status & MODE_MASK) == MODE_CLIENT) ?
710 MODE_SERVER : MODE_SYM_PAS;
711 msg.m_stratum = G.stratum;
712 msg.m_ppoll = query_ppoll;
713 msg.m_precision_exp = G_precision_exp;
714 rectime = gettime1900d();
715 msg.m_xmttime = msg.m_rectime = d_to_lfp(rectime);
716 msg.m_reftime = d_to_lfp(G.reftime);
717 //msg.m_xmttime = d_to_lfp(gettime1900d()); // = msg.m_rectime
718 msg.m_orgtime = query_xmttime;
719 msg.m_rootdelay = d_to_sfp(G.rootdelay);
720 version = (query_status & VERSION_MASK); /* ... >> VERSION_SHIFT - done below instead */
721 msg.m_refid = (version > (3 << VERSION_SHIFT)) ? G.refid4 : G.refid;
722
723 /* We reply from the local address packet was sent to,
724 * this makes to/from look swapped here: */
725 do_sendto(G.listen_fd,
726 /*from:*/ &to->u.sa, /*to:*/ from, /*addrlen:*/ to->len,
727 &msg, size);
728
729 bail:
730 free(to);
731 free(from);
732}
733#endif
734
735/* Upstream ntpd's options:
736 *
737 * -4 Force DNS resolution of host names to the IPv4 namespace.
738 * -6 Force DNS resolution of host names to the IPv6 namespace.
739 * -a Require cryptographic authentication for broadcast client,
740 * multicast client and symmetric passive associations.
741 * This is the default.
742 * -A Do not require cryptographic authentication for broadcast client,
743 * multicast client and symmetric passive associations.
744 * This is almost never a good idea.
745 * -b Enable the client to synchronize to broadcast servers.
746 * -c conffile
747 * Specify the name and path of the configuration file,
748 * default /etc/ntp.conf
749 * -d Specify debugging mode. This option may occur more than once,
750 * with each occurrence indicating greater detail of display.
751 * -D level
752 * Specify debugging level directly.
753 * -f driftfile
754 * Specify the name and path of the frequency file.
755 * This is the same operation as the "driftfile FILE"
756 * configuration command.
757 * -g Normally, ntpd exits with a message to the system log
758 * if the offset exceeds the panic threshold, which is 1000 s
759 * by default. This option allows the time to be set to any value
760 * without restriction; however, this can happen only once.
761 * If the threshold is exceeded after that, ntpd will exit
762 * with a message to the system log. This option can be used
763 * with the -q and -x options. See the tinker command for other options.
764 * -i jaildir
765 * Chroot the server to the directory jaildir. This option also implies
766 * that the server attempts to drop root privileges at startup
767 * (otherwise, chroot gives very little additional security).
768 * You may need to also specify a -u option.
769 * -k keyfile
770 * Specify the name and path of the symmetric key file,
771 * default /etc/ntp/keys. This is the same operation
772 * as the "keys FILE" configuration command.
773 * -l logfile
774 * Specify the name and path of the log file. The default
775 * is the system log file. This is the same operation as
776 * the "logfile FILE" configuration command.
777 * -L Do not listen to virtual IPs. The default is to listen.
778 * -n Don't fork.
779 * -N To the extent permitted by the operating system,
780 * run the ntpd at the highest priority.
781 * -p pidfile
782 * Specify the name and path of the file used to record the ntpd
783 * process ID. This is the same operation as the "pidfile FILE"
784 * configuration command.
785 * -P priority
786 * To the extent permitted by the operating system,
787 * run the ntpd at the specified priority.
788 * -q Exit the ntpd just after the first time the clock is set.
789 * This behavior mimics that of the ntpdate program, which is
790 * to be retired. The -g and -x options can be used with this option.
791 * Note: The kernel time discipline is disabled with this option.
792 * -r broadcastdelay
793 * Specify the default propagation delay from the broadcast/multicast
794 * server to this client. This is necessary only if the delay
795 * cannot be computed automatically by the protocol.
796 * -s statsdir
797 * Specify the directory path for files created by the statistics
798 * facility. This is the same operation as the "statsdir DIR"
799 * configuration command.
800 * -t key
801 * Add a key number to the trusted key list. This option can occur
802 * more than once.
803 * -u user[:group]
804 * Specify a user, and optionally a group, to switch to.
805 * -v variable
806 * -V variable
807 * Add a system variable listed by default.
808 * -x Normally, the time is slewed if the offset is less than the step
809 * threshold, which is 128 ms by default, and stepped if above
810 * the threshold. This option sets the threshold to 600 s, which is
811 * well within the accuracy window to set the clock manually.
812 * Note: since the slew rate of typical Unix kernels is limited
813 * to 0.5 ms/s, each second of adjustment requires an amortization
814 * interval of 2000 s. Thus, an adjustment as much as 600 s
815 * will take almost 14 days to complete. This option can be used
816 * with the -g and -q options. See the tinker command for other options.
817 * Note: The kernel time discipline is disabled with this option.
818 */
819
820/* By doing init in a separate function we decrease stack usage
821 * in main loop.
822 */
823static NOINLINE void ntp_init(char **argv)
824{
825 unsigned opts;
826 llist_t *peers;
827
828 srand(getpid());
829
830 if (getuid())
831 bb_error_msg_and_die(bb_msg_you_must_be_root);
832
833 peers = NULL;
834 opt_complementary = "dd:p::"; /* d: counter, p: list */
835 opts = getopt32(argv,
836 "nqNx" /* compat */
837 "p:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */
838 "d" /* compat */
839 "46aAbgL", /* compat, ignored */
840 &peers, &G.verbose);
841 if (!(opts & (OPT_p|OPT_l)))
842 bb_show_usage();
843 if (opts & OPT_x) /* disable stepping, only slew is allowed */
844 G.time_was_stepped = 1;
845 while (peers)
846 add_peers(llist_pop(&peers));
847 if (!(opts & OPT_n)) {
848 bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO, argv);
849 logmode = LOGMODE_NONE;
850 }
851#if ENABLE_FEATURE_NTPD_SERVER
852 G.listen_fd = -1;
853 if (opts & OPT_l) {
854 G.listen_fd = create_and_bind_dgram_or_die(NULL, 123);
855 socket_want_pktinfo(G.listen_fd);
856 setsockopt(G.listen_fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY));
857 }
858#endif
859 /* I hesitate to set -20 prio. -15 should be high enough for timekeeping */
860 if (opts & OPT_N)
861 setpriority(PRIO_PROCESS, 0, -15);
862
863 /* Set some globals */
864#if 0
865 /* With constant b = 100, G.precision_exp is also constant -6.
866 * Uncomment this and you'll see */
867 {
868 int prec = 0;
869 int b;
870# if 0
871 struct timespec tp;
872 /* We can use sys_clock_getres but assuming 10ms tick should be fine */
873 clock_getres(CLOCK_REALTIME, &tp);
874 tp.tv_sec = 0;
875 tp.tv_nsec = 10000000;
876 b = 1000000000 / tp.tv_nsec; /* convert to Hz */
877# else
878 b = 100; /* b = 1000000000/10000000 = 100 */
879# endif
880 while (b > 1)
881 prec--, b >>= 1;
882 //G.precision_exp = prec;
883 bb_error_msg("G.precision_exp:%d", prec); /* -6 */
884 }
885#endif
886 G.scale = 1;
887
888 bb_signals((1 << SIGTERM) | (1 << SIGINT), record_signo);
889 bb_signals((1 << SIGPIPE) | (1 << SIGHUP), SIG_IGN);
890}
891
892int ntpd_main(int argc UNUSED_PARAM, char **argv) MAIN_EXTERNALLY_VISIBLE;
893int ntpd_main(int argc UNUSED_PARAM, char **argv)
894{
895 struct globals g;
896 struct pollfd *pfd;
897 peer_t **idx2peer;
898
899 memset(&g, 0, sizeof(g));
900 SET_PTR_TO_GLOBALS(&g);
901
902 ntp_init(argv);
903
904 {
905 /* if ENABLE_FEATURE_NTPD_SERVER, + 1 for listen_fd: */
906 unsigned cnt = g.peer_cnt + ENABLE_FEATURE_NTPD_SERVER;
907 idx2peer = xzalloc(sizeof(idx2peer[0]) * cnt);
908 pfd = xzalloc(sizeof(pfd[0]) * cnt);
909 }
910
911 while (!bb_got_signal) {
912 llist_t *item;
913 unsigned i, j;
914 unsigned sent_cnt, trial_cnt;
915 int nfds, timeout;
916 time_t cur_time, nextaction;
917
918 /* Nothing between here and poll() blocks for any significant time */
919
920 cur_time = time(NULL);
921 nextaction = cur_time + 3600;
922
923 i = 0;
924#if ENABLE_FEATURE_NTPD_SERVER
925 if (g.listen_fd != -1) {
926 pfd[0].fd = g.listen_fd;
927 pfd[0].events = POLLIN;
928 i++;
929 }
930#endif
931 /* Pass over peer list, send requests, time out on receives */
932 sent_cnt = trial_cnt = 0;
933 for (item = g.ntp_peers; item != NULL; item = item->link) {
934 peer_t *p = (peer_t *) item->data;
935
936 /* Overflow-safe "if (p->next_action_time <= cur_time) ..." */
937 if ((int)(cur_time - p->next_action_time) >= 0) {
938 if (p->p_fd == -1) {
939 /* Time to send new req */
940 trial_cnt++;
941 if (send_query_to_peer(p) == 0)
942 sent_cnt++;
943 } else {
944 /* Timed out waiting for reply */
945 close(p->p_fd);
946 p->p_fd = -1;
947 timeout = error_interval();
948 bb_error_msg("timed out waiting for %s, "
949 "next query in %us", p->p_dotted, timeout);
950 if (p->p_trustlevel >= TRUSTLEVEL_BADPEER) {
951 p->p_trustlevel /= 2;
952 if (p->p_trustlevel < TRUSTLEVEL_BADPEER)
953 bb_error_msg("peer %s now invalid", p->p_dotted);
954 }
955 set_next(p, timeout);
956 }
957 }
958
959 if (p->next_action_time < nextaction)
960 nextaction = p->next_action_time;
961
962 if (p->p_fd >= 0) {
963 /* Wait for reply from this peer */
964 pfd[i].fd = p->p_fd;
965 pfd[i].events = POLLIN;
966 idx2peer[i] = p;
967 i++;
968 }
969 }
970
971 if ((trial_cnt > 0 && sent_cnt == 0) || g.peer_cnt == 0)
972 step_time_once(0); /* no good peers, don't wait */
973
974 timeout = nextaction - cur_time;
975 if (timeout < 1)
976 timeout = 1;
977
978 /* Here we may block */
979 if (g.verbose >= 2)
980 bb_error_msg("poll %us, sockets:%u", timeout, i);
981 nfds = poll(pfd, i, timeout * 1000);
982 if (nfds <= 0)
983 continue;
984
985 /* Process any received packets */
986 j = 0;
987#if ENABLE_FEATURE_NTPD_SERVER
988 if (g.listen_fd != -1) {
989 if (pfd[0].revents /* & (POLLIN|POLLERR)*/) {
990 nfds--;
991 recv_and_process_client_pkt(/*g.listen_fd*/);
992 }
993 j = 1;
994 }
995#endif
996 for (; nfds != 0 && j < i; j++) {
997 if (pfd[j].revents /* & (POLLIN|POLLERR)*/) {
998 nfds--;
999 recv_and_process_peer_pkt(idx2peer[j]);
1000 }
1001 }
1002 } /* while (!bb_got_signal) */
1003
1004 kill_myself_with_sig(bb_got_signal);
1005}
diff --git a/networking/route.c b/networking/route.c
index 24cc2eb5a..65c2fb7c8 100644
--- a/networking/route.c
+++ b/networking/route.c
@@ -55,6 +55,7 @@
55#define RTF_WINDOW 0x0080 /* per route window clamping */ 55#define RTF_WINDOW 0x0080 /* per route window clamping */
56#define RTF_IRTT 0x0100 /* Initial round trip time */ 56#define RTF_IRTT 0x0100 /* Initial round trip time */
57#define RTF_REJECT 0x0200 /* Reject route */ 57#define RTF_REJECT 0x0200 /* Reject route */
58#define RTF_NONEXTHOP 0x00200000 /* route with no nexthop */
58#endif 59#endif
59 60
60#if defined(SIOCADDRTOLD) || defined(RTF_IRTT) /* route */ 61#if defined(SIOCADDRTOLD) || defined(RTF_IRTT) /* route */
@@ -128,7 +129,7 @@ static const char tbl_ipvx[] ALIGN1 =
128 "\013\043reinstate" /* Since last, we can save a byte. */ 129 "\013\043reinstate" /* Since last, we can save a byte. */
129; 130;
130 131
131static const int flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */ 132static const uint16_t flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */
132#ifdef RTF_REJECT 133#ifdef RTF_REJECT
133 RTF_REJECT, 134 RTF_REJECT,
134#endif 135#endif
@@ -449,7 +450,11 @@ static NOINLINE void INET6_setroute(int action, char **args)
449} 450}
450#endif 451#endif
451 452
452static const unsigned flagvals[] = { /* Must agree with flagchars[]. */ 453static const
454IF_NOT_FEATURE_IPV6(uint16_t)
455IF_FEATURE_IPV6(unsigned)
456flagvals[] = { /* Must agree with flagchars[]. */
457 RTF_UP,
453 RTF_GATEWAY, 458 RTF_GATEWAY,
454 RTF_HOST, 459 RTF_HOST,
455 RTF_REINSTATE, 460 RTF_REINSTATE,
@@ -458,27 +463,25 @@ static const unsigned flagvals[] = { /* Must agree with flagchars[]. */
458#if ENABLE_FEATURE_IPV6 463#if ENABLE_FEATURE_IPV6
459 RTF_DEFAULT, 464 RTF_DEFAULT,
460 RTF_ADDRCONF, 465 RTF_ADDRCONF,
461 RTF_CACHE 466 RTF_CACHE,
467 RTF_REJECT,
468 RTF_NONEXTHOP, /* this one doesn't fit into 16 bits */
462#endif 469#endif
463}; 470};
464
465#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
466#define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE)
467
468/* Must agree with flagvals[]. */ 471/* Must agree with flagvals[]. */
469static const char flagchars[] ALIGN1 = 472static const char flagchars[] ALIGN1 =
470 "GHRDM" 473 "UGHRDM"
471#if ENABLE_FEATURE_IPV6 474#if ENABLE_FEATURE_IPV6
472 "DAC" 475 "DAC!n"
473#endif 476#endif
474; 477;
478#define IPV4_MASK (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
479#define IPV6_MASK (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE|RTF_REJECT|RTF_NONEXTHOP)
475 480
476static void set_flags(char *flagstr, int flags) 481static void set_flags(char *flagstr, int flags)
477{ 482{
478 int i; 483 int i;
479 484
480 *flagstr++ = 'U';
481
482 for (i = 0; (*flagstr = flagchars[i]) != 0; i++) { 485 for (i = 0; (*flagstr = flagchars[i]) != 0; i++) {
483 if (flags & flagvals[i]) { 486 if (flags & flagvals[i]) {
484 ++flagstr; 487 ++flagstr;
@@ -491,6 +494,7 @@ void FAST_FUNC bb_displayroutes(int noresolve, int netstatfmt)
491{ 494{
492 char devname[64], flags[16], *sdest, *sgw; 495 char devname[64], flags[16], *sdest, *sgw;
493 unsigned long d, g, m; 496 unsigned long d, g, m;
497 int r;
494 int flgs, ref, use, metric, mtu, win, ir; 498 int flgs, ref, use, metric, mtu, win, ir;
495 struct sockaddr_in s_addr; 499 struct sockaddr_in s_addr;
496 struct in_addr mask; 500 struct in_addr mask;
@@ -501,20 +505,24 @@ void FAST_FUNC bb_displayroutes(int noresolve, int netstatfmt)
501 "Destination Gateway Genmask Flags %s Iface\n", 505 "Destination Gateway Genmask Flags %s Iface\n",
502 netstatfmt ? " MSS Window irtt" : "Metric Ref Use"); 506 netstatfmt ? " MSS Window irtt" : "Metric Ref Use");
503 507
504 if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */ 508 /* Skip the first line. */
505 goto ERROR; /* Empty or missing line, or read error. */ 509 r = fscanf(fp, "%*[^\n]\n");
510 if (r < 0) {
511 /* Empty line, read error, or EOF. Yes, if routing table
512 * is completely empty, /proc/net/route has no header.
513 */
514 goto ERROR;
506 } 515 }
507 while (1) { 516 while (1) {
508 int r;
509 r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n", 517 r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
510 devname, &d, &g, &flgs, &ref, &use, &metric, &m, 518 devname, &d, &g, &flgs, &ref, &use, &metric, &m,
511 &mtu, &win, &ir); 519 &mtu, &win, &ir);
512 if (r != 11) { 520 if (r != 11) {
521 ERROR:
513 if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */ 522 if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
514 break; 523 break;
515 } 524 }
516 ERROR: 525 bb_perror_msg_and_die(bb_msg_read_error);
517 bb_error_msg_and_die("fscanf");
518 } 526 }
519 527
520 if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */ 528 if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */
@@ -574,13 +582,13 @@ static void INET6_displayroutes(void)
574 int r; 582 int r;
575 r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n", 583 r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n",
576 addr6x+14, &prefix_len, &slen, addr6x+40+7, 584 addr6x+14, &prefix_len, &slen, addr6x+40+7,
577 &metric, &use, &refcnt, &iflags, iface); 585 &metric, &refcnt, &use, &iflags, iface);
578 if (r != 9) { 586 if (r != 9) {
579 if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */ 587 if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
580 break; 588 break;
581 } 589 }
582 ERROR: 590 ERROR:
583 bb_error_msg_and_die("fscanf"); 591 bb_perror_msg_and_die(bb_msg_read_error);
584 } 592 }
585 593
586 /* Do the addr6x shift-and-insert changes to ':'-delimit addresses. 594 /* Do the addr6x shift-and-insert changes to ':'-delimit addresses.
@@ -606,10 +614,6 @@ static void INET6_displayroutes(void)
606 } while (i < 40+28+7); 614 } while (i < 40+28+7);
607 } 615 }
608 616
609 if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */
610 continue;
611 }
612
613 set_flags(flags, (iflags & IPV6_MASK)); 617 set_flags(flags, (iflags & IPV6_MASK));
614 618
615 r = 0; 619 r = 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)
462 while (ts) { 462 while (ts) {
463 if (ts->shell_pid == pid) { 463 if (ts->shell_pid == pid) {
464 ts->shell_pid = -1; 464 ts->shell_pid = -1;
465// man utmp: 465 update_utmp_DEAD_PROCESS(pid);
466// When init(8) finds that a process has exited, it locates its utmp entry
467// by ut_pid, sets ut_type to DEAD_PROCESS, and clears ut_user, ut_host
468// and ut_time with null bytes.
469// [same applies to other processes which maintain utmp entries, like telnetd]
470//
471// We do not bother actually clearing fields:
472// it might be interesting to know who was logged in and from where
473 update_utmp(pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL);
474 break; 466 break;
475 } 467 }
476 ts = ts->next; 468 ts = ts->next;
@@ -739,7 +731,7 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
739 continue; 731 continue;
740 kill_session: 732 kill_session:
741 if (ts->shell_pid > 0) 733 if (ts->shell_pid > 0)
742 update_utmp(ts->shell_pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL); 734 update_utmp_DEAD_PROCESS(ts->shell_pid);
743 free_session(ts); 735 free_session(ts);
744 ts = next; 736 ts = next;
745 } 737 }
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)
1752 } 1752 }
1753#endif 1753#endif
1754 /* enter bound state */ 1754 /* enter bound state */
1755 timeout = lease_seconds / 2;
1756 temp_addr.s_addr = packet.yiaddr; 1755 temp_addr.s_addr = packet.yiaddr;
1757 bb_info_msg("Lease of %s obtained, lease time %u", 1756 bb_info_msg("Lease of %s obtained, lease time %u",
1758 inet_ntoa(temp_addr), (unsigned)lease_seconds); 1757 inet_ntoa(temp_addr), (unsigned)lease_seconds);
@@ -1761,6 +1760,11 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1761 start = monotonic_sec(); 1760 start = monotonic_sec();
1762 udhcp_run_script(&packet, state == REQUESTING ? "bound" : "renew"); 1761 udhcp_run_script(&packet, state == REQUESTING ? "bound" : "renew");
1763 already_waited_sec = (unsigned)monotonic_sec() - start; 1762 already_waited_sec = (unsigned)monotonic_sec() - start;
1763 timeout = lease_seconds / 2;
1764 if ((unsigned)timeout < already_waited_sec) {
1765 /* Something went wrong. Back to discover state */
1766 timeout = already_waited_sec = 0;
1767 }
1764 1768
1765 state = BOUND; 1769 state = BOUND;
1766 change_listen_mode(LISTEN_NONE); 1770 change_listen_mode(LISTEN_NONE);
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)
413 413
414 max_sock = udhcp_sp_fd_set(&rfds, server_socket); 414 max_sock = udhcp_sp_fd_set(&rfds, server_socket);
415 if (server_config.auto_time) { 415 if (server_config.auto_time) {
416 tv.tv_sec = timeout_end - monotonic_sec(); 416 /* cast to signed is essential if tv_sec is wider than int */
417 tv.tv_sec = (int)(timeout_end - monotonic_sec());
417 tv.tv_usec = 0; 418 tv.tv_usec = 0;
418 } 419 }
419 retval = 0; 420 retval = 0;
diff --git a/networking/wget.c b/networking/wget.c
index 774accf7a..42bfbb206 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -38,8 +38,14 @@
38 38
39#if 0 39#if 0
40# define log_io(...) bb_error_msg(__VA_ARGS__) 40# define log_io(...) bb_error_msg(__VA_ARGS__)
41# define SENDFMT(fp, fmt, ...) \
42 do { \
43 log_io("> " fmt, ##__VA_ARGS__); \
44 fprintf(fp, fmt, ##__VA_ARGS__); \
45 } while (0);
41#else 46#else
42# define log_io(...) ((void)0) 47# define log_io(...) ((void)0)
48# define SENDFMT(fp, fmt, ...) fprintf(fp, fmt, ##__VA_ARGS__)
43#endif 49#endif
44 50
45 51
@@ -55,6 +61,36 @@ static const char P_FTP[] = "ftp";
55static const char P_HTTP[] = "http"; 61static const char P_HTTP[] = "http";
56static const char P_HTTPS[] = "https"; 62static const char P_HTTPS[] = "https";
57 63
64#if ENABLE_FEATURE_WGET_LONG_OPTIONS
65/* User-specified headers prevent using our corresponding built-in headers. */
66enum {
67 HDR_HOST = (1<<0),
68 HDR_USER_AGENT = (1<<1),
69 HDR_RANGE = (1<<2),
70 HDR_AUTH = (1<<3) * ENABLE_FEATURE_WGET_AUTHENTICATION,
71 HDR_PROXY_AUTH = (1<<4) * ENABLE_FEATURE_WGET_AUTHENTICATION,
72};
73static const char wget_user_headers[] ALIGN1 =
74 "Host:\0"
75 "User-Agent:\0"
76 "Range:\0"
77# if ENABLE_FEATURE_WGET_AUTHENTICATION
78 "Authorization:\0"
79 "Proxy-Authorization:\0"
80# endif
81 ;
82# define USR_HEADER_HOST (G.user_headers & HDR_HOST)
83# define USR_HEADER_USER_AGENT (G.user_headers & HDR_USER_AGENT)
84# define USR_HEADER_RANGE (G.user_headers & HDR_RANGE)
85# define USR_HEADER_AUTH (G.user_headers & HDR_AUTH)
86# define USR_HEADER_PROXY_AUTH (G.user_headers & HDR_PROXY_AUTH)
87#else /* No long options, no user-headers :( */
88# define USR_HEADER_HOST 0
89# define USR_HEADER_USER_AGENT 0
90# define USR_HEADER_RANGE 0
91# define USR_HEADER_AUTH 0
92# define USR_HEADER_PROXY_AUTH 0
93#endif
58 94
59/* Globals */ 95/* Globals */
60struct globals { 96struct globals {
@@ -69,6 +105,7 @@ struct globals {
69#if ENABLE_FEATURE_WGET_LONG_OPTIONS 105#if ENABLE_FEATURE_WGET_LONG_OPTIONS
70 char *post_data; 106 char *post_data;
71 char *extra_headers; 107 char *extra_headers;
108 unsigned char user_headers; /* Headers mentioned by the user */
72#endif 109#endif
73 char *fname_out; /* where to direct output (-O) */ 110 char *fname_out; /* where to direct output (-O) */
74 const char *proxy_flag; /* Use proxies if env vars are set */ 111 const char *proxy_flag; /* Use proxies if env vars are set */
@@ -842,43 +879,46 @@ static void download_one_url(const char *url)
842#endif 879#endif
843 /* Send HTTP request */ 880 /* Send HTTP request */
844 if (use_proxy) { 881 if (use_proxy) {
845 fprintf(sfp, "GET %s://%s/%s HTTP/1.1\r\n", 882 SENDFMT(sfp, "GET %s://%s/%s HTTP/1.1\r\n",
846 target.protocol, target.host, 883 target.protocol, target.host,
847 target.path); 884 target.path);
848 } else { 885 } else {
849 fprintf(sfp, "%s /%s HTTP/1.1\r\n", 886 SENDFMT(sfp, "%s /%s HTTP/1.1\r\n",
850 (option_mask32 & WGET_OPT_POST_DATA) ? "POST" : "GET", 887 (option_mask32 & WGET_OPT_POST_DATA) ? "POST" : "GET",
851 target.path); 888 target.path);
852 } 889 }
853 890 if (!USR_HEADER_HOST)
854 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n", 891 SENDFMT(sfp, "Host: %s\r\n", target.host);
855 target.host, G.user_agent); 892 if (!USR_HEADER_USER_AGENT)
893 SENDFMT(sfp, "User-Agent: %s\r\n", G.user_agent);
856 894
857 /* Ask server to close the connection as soon as we are done 895 /* Ask server to close the connection as soon as we are done
858 * (IOW: we do not intend to send more requests) 896 * (IOW: we do not intend to send more requests)
859 */ 897 */
860 fprintf(sfp, "Connection: close\r\n"); 898 SENDFMT(sfp, "Connection: close\r\n");
861 899
862#if ENABLE_FEATURE_WGET_AUTHENTICATION 900#if ENABLE_FEATURE_WGET_AUTHENTICATION
863 if (target.user) { 901 if (target.user && !USR_HEADER_AUTH) {
864 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6, 902 SENDFMT(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
865 base64enc(target.user)); 903 base64enc(target.user));
866 } 904 }
867 if (use_proxy && server.user) { 905 if (use_proxy && server.user && !USR_HEADER_PROXY_AUTH) {
868 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n", 906 SENDFMT(sfp, "Proxy-Authorization: Basic %s\r\n",
869 base64enc(server.user)); 907 base64enc(server.user));
870 } 908 }
871#endif 909#endif
872 910
873 if (G.beg_range != 0) 911 if (G.beg_range != 0 && !USR_HEADER_RANGE)
874 fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range); 912 SENDFMT(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range);
875 913
876#if ENABLE_FEATURE_WGET_LONG_OPTIONS 914#if ENABLE_FEATURE_WGET_LONG_OPTIONS
877 if (G.extra_headers) 915 if (G.extra_headers) {
916 log_io(G.extra_headers);
878 fputs(G.extra_headers, sfp); 917 fputs(G.extra_headers, sfp);
918 }
879 919
880 if (option_mask32 & WGET_OPT_POST_DATA) { 920 if (option_mask32 & WGET_OPT_POST_DATA) {
881 fprintf(sfp, 921 SENDFMT(sfp,
882 "Content-Type: application/x-www-form-urlencoded\r\n" 922 "Content-Type: application/x-www-form-urlencoded\r\n"
883 "Content-Length: %u\r\n" 923 "Content-Length: %u\r\n"
884 "\r\n" 924 "\r\n"
@@ -888,7 +928,7 @@ static void download_one_url(const char *url)
888 } else 928 } else
889#endif 929#endif
890 { 930 {
891 fprintf(sfp, "\r\n"); 931 SENDFMT(sfp, "\r\n");
892 } 932 }
893 933
894 fflush(sfp); 934 fflush(sfp);
@@ -1105,7 +1145,9 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
1105#if ENABLE_FEATURE_WGET_LONG_OPTIONS 1145#if ENABLE_FEATURE_WGET_LONG_OPTIONS
1106 applet_long_options = wget_longopts; 1146 applet_long_options = wget_longopts;
1107#endif 1147#endif
1108 opt_complementary = "-1" IF_FEATURE_WGET_TIMEOUT(":T+") IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::"); 1148 opt_complementary = "-1"
1149 IF_FEATURE_WGET_TIMEOUT(":T+")
1150 IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
1109 getopt32(argv, "csqO:P:Y:U:T:" /*ignored:*/ "t:", 1151 getopt32(argv, "csqO:P:Y:U:T:" /*ignored:*/ "t:",
1110 &G.fname_out, &G.dir_prefix, 1152 &G.fname_out, &G.dir_prefix,
1111 &G.proxy_flag, &G.user_agent, 1153 &G.proxy_flag, &G.user_agent,
@@ -1118,16 +1160,32 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
1118 1160
1119#if ENABLE_FEATURE_WGET_LONG_OPTIONS 1161#if ENABLE_FEATURE_WGET_LONG_OPTIONS
1120 if (headers_llist) { 1162 if (headers_llist) {
1121 int size = 1; 1163 int size = 0;
1122 char *cp; 1164 char *hdr;
1123 llist_t *ll = headers_llist; 1165 llist_t *ll = headers_llist;
1124 while (ll) { 1166 while (ll) {
1125 size += strlen(ll->data) + 2; 1167 size += strlen(ll->data) + 2;
1126 ll = ll->link; 1168 ll = ll->link;
1127 } 1169 }
1128 G.extra_headers = cp = xmalloc(size); 1170 G.extra_headers = hdr = xmalloc(size + 1);
1129 while (headers_llist) { 1171 while (headers_llist) {
1130 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist)); 1172 int bit;
1173 const char *words;
1174
1175 size = sprintf(hdr, "%s\r\n",
1176 (char*)llist_pop(&headers_llist));
1177 /* a bit like index_in_substrings but don't match full key */
1178 bit = 1;
1179 words = wget_user_headers;
1180 while (*words) {
1181 if (strstr(hdr, words) == hdr) {
1182 G.user_headers |= bit;
1183 break;
1184 }
1185 bit <<= 1;
1186 words += strlen(words) + 1;
1187 }
1188 hdr += size;
1131 } 1189 }
1132 } 1190 }
1133#endif 1191#endif
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[])
204 goto err_exit; 204 goto err_exit;
205 } 205 }
206 // get filename 206 // get filename
207 *strchrnul(s, '\n') = '\0'; 207 chomp(s);
208 fname = strchr(s, ' '); 208 fname = strchr(s, ' ');
209 if (!fname) { 209 if (!fname) {
210// bad_fname: 210// bad_fname:
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)
44 return ((unsigned long long)d * G.mem_unit) >> G_unit_steps; 44 return ((unsigned long long)d * G.mem_unit) >> G_unit_steps;
45} 45}
46 46
47static unsigned long parse_cached_kb(void)
48{
49 char buf[60]; /* actual lines we expect are ~30 chars or less */
50 FILE *fp;
51 unsigned long cached = 0;
52
53 fp = xfopen_for_read("/proc/meminfo");
54 while (fgets(buf, sizeof(buf), fp) != NULL) {
55 if (sscanf(buf, "Cached: %lu %*s\n", &cached) == 1)
56 break;
57 }
58 if (ENABLE_FEATURE_CLEAN_UP)
59 fclose(fp);
60
61 return cached;
62}
47 63
48int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 64int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
49int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM)) 65int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
50{ 66{
51 struct sysinfo info; 67 struct sysinfo info;
68 unsigned long long cached;
52 69
53 INIT_G(); 70 INIT_G();
54 71
@@ -73,49 +90,47 @@ int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
73 } 90 }
74 } 91 }
75#endif 92#endif
76 93 printf(" %11s%11s%11s%11s%11s%11s\n"
77 sysinfo(&info); 94 "Mem: ",
78
79 /* Kernels prior to 2.4.x will return info.mem_unit==0, so cope... */
80 G.mem_unit = (info.mem_unit ? info.mem_unit : 1);
81
82 printf(" %13s%13s%13s%13s%13s\n",
83 "total", 95 "total",
84 "used", 96 "used",
85 "free", 97 "free",
86 "shared", "buffers" /* swap and total don't have these columns */ 98 "shared", "buffers", "cached" /* swap and total don't have these columns */
87 /* procps version 3.2.8 also shows "cached" column, but
88 * sysinfo() does not provide this value, need to parse
89 * /proc/meminfo instead and get "Cached: NNN kB" from there.
90 */
91 ); 99 );
92 100
93#define FIELDS_5 "%13llu%13llu%13llu%13llu%13llu\n" 101 sysinfo(&info);
94#define FIELDS_3 (FIELDS_5 + 2*6) 102 /* Kernels prior to 2.4.x will return info.mem_unit==0, so cope... */
95#define FIELDS_2 (FIELDS_5 + 3*6) 103 G.mem_unit = (info.mem_unit ? info.mem_unit : 1);
104 /* Extract cached from /proc/meminfo and convert to mem_units */
105 cached = ((unsigned long long) parse_cached_kb() * 1024) / G.mem_unit;
106
107#define FIELDS_6 "%11llu%11llu%11llu%11llu%11llu%11llu\n"
108#define FIELDS_3 (FIELDS_6 + 3*6)
109#define FIELDS_2 (FIELDS_6 + 4*6)
96 110
97 printf("Mem: "); 111 printf(FIELDS_6,
98 printf(FIELDS_5, 112 scale(info.totalram), //total
99 scale(info.totalram), 113 scale(info.totalram - info.freeram), //used
100 scale(info.totalram - info.freeram), 114 scale(info.freeram), //free
101 scale(info.freeram), 115 scale(info.sharedram), //shared
102 scale(info.sharedram), 116 scale(info.bufferram), //buffers
103 scale(info.bufferram) 117 scale(cached) //cached
104 ); 118 );
105 /* Show alternate, more meaningful busy/free numbers by counting 119 /* Show alternate, more meaningful busy/free numbers by counting
106 * buffer cache as free memory (make it "-/+ buffers/cache" 120 * buffer cache as free memory. */
107 * if/when we add support for "cached" column): */ 121 printf("-/+ buffers/cache:");
108 printf("-/+ buffers: "); 122 cached += info.freeram;
123 cached += info.bufferram;
109 printf(FIELDS_2, 124 printf(FIELDS_2,
110 scale(info.totalram - info.freeram - info.bufferram), 125 scale(info.totalram - cached), //used
111 scale(info.freeram + info.bufferram) 126 scale(cached) //free
112 ); 127 );
113#if BB_MMU 128#if BB_MMU
114 printf("Swap:"); 129 printf("Swap: ");
115 printf(FIELDS_3, 130 printf(FIELDS_3,
116 scale(info.totalswap), 131 scale(info.totalswap), //total
117 scale(info.totalswap - info.freeswap), 132 scale(info.totalswap - info.freeswap), //used
118 scale(info.freeswap) 133 scale(info.freeswap) //free
119 ); 134 );
120#endif 135#endif
121 return EXIT_SUCCESS; 136 return EXIT_SUCCESS;
diff --git a/procps/mpstat.c b/procps/mpstat.c
index c628d6215..6028903fa 100644
--- a/procps/mpstat.c
+++ b/procps/mpstat.c
@@ -522,13 +522,11 @@ static void get_irqs_from_stat(struct stats_irq *irq)
522 FILE *fp; 522 FILE *fp;
523 char buf[1024]; 523 char buf[1024];
524 524
525 fp = fopen_for_read(PROCFS_STAT); 525 fp = xfopen_for_read(PROCFS_STAT);
526 if (!fp)
527 return;
528 526
529 while (fgets(buf, sizeof(buf), fp)) { 527 while (fgets(buf, sizeof(buf), fp)) {
530 //bb_error_msg("/proc/stat:'%s'", buf); 528 //bb_error_msg("/proc/stat:'%s'", buf);
531 if (strncmp(buf, "intr ", 5) == 0) { 529 if (is_prefixed_with(buf, "intr ")) {
532 /* Read total number of IRQs since system boot */ 530 /* Read total number of IRQs since system boot */
533 sscanf(buf + 5, "%"FMT_DATA"u", &irq->irq_nr); 531 sscanf(buf + 5, "%"FMT_DATA"u", &irq->irq_nr);
534 } 532 }
@@ -644,9 +642,7 @@ static void get_uptime(data_t *uptime)
644 char buf[sizeof(long)*3 * 2 + 4]; /* enough for long.long */ 642 char buf[sizeof(long)*3 * 2 + 4]; /* enough for long.long */
645 unsigned long uptime_sec, decimal; 643 unsigned long uptime_sec, decimal;
646 644
647 fp = fopen_for_read(PROCFS_UPTIME); 645 fp = xfopen_for_read(PROCFS_UPTIME);
648 if (!fp)
649 return;
650 if (fgets(buf, sizeof(buf), fp)) { 646 if (fgets(buf, sizeof(buf), fp)) {
651 if (sscanf(buf, "%lu.%lu", &uptime_sec, &decimal) == 2) { 647 if (sscanf(buf, "%lu.%lu", &uptime_sec, &decimal) == 2) {
652 *uptime = (data_t)uptime_sec * G.hz + decimal * G.hz / 100; 648 *uptime = (data_t)uptime_sec * G.hz + decimal * G.hz / 100;
diff --git a/procps/powertop.c b/procps/powertop.c
index e3c29d1c3..1de5d329e 100644
--- a/procps/powertop.c
+++ b/procps/powertop.c
@@ -360,7 +360,7 @@ static void process_irq_counts(void)
360 } 360 }
361 361
362 name = p; 362 name = p;
363 strchrnul(name, '\n')[0] = '\0'; 363 chomp(p);
364 /* Save description of the interrupt */ 364 /* Save description of the interrupt */
365 if (nr >= 20000) 365 if (nr >= 20000)
366 sprintf(irq_desc, " <kernel IPI> : %s", name); 366 sprintf(irq_desc, " <kernel IPI> : %s", name);
@@ -458,9 +458,9 @@ static NOINLINE int process_timer_stats(void)
458 // func = "Load balancing tick"; 458 // func = "Load balancing tick";
459 //} 459 //}
460 460
461 if (strncmp(func, "tick_nohz_", 10) == 0) 461 if (is_prefixed_with(func, "tick_nohz_"))
462 continue; 462 continue;
463 if (strncmp(func, "tick_setup_sched_timer", 20) == 0) 463 if (is_prefixed_with(func, "tick_setup_sched_timer"))
464 continue; 464 continue;
465 //if (strcmp(process, "powertop") == 0) 465 //if (strcmp(process, "powertop") == 0)
466 // continue; 466 // continue;
@@ -470,7 +470,7 @@ static NOINLINE int process_timer_stats(void)
470 process = idx < 2 ? "[kernel module]" : "<kernel core>"; 470 process = idx < 2 ? "[kernel module]" : "<kernel core>";
471 } 471 }
472 472
473 strchrnul(p, '\n')[0] = '\0'; 473 chomp(p);
474 474
475 // 46D\01136\0kondemand/1\0do_dbs_timer (delayed_work_timer_fn) 475 // 46D\01136\0kondemand/1\0do_dbs_timer (delayed_work_timer_fn)
476 // ^ ^ ^ 476 // ^ ^ ^
@@ -674,7 +674,7 @@ static void show_timerstats(void)
674//usage:#define powertop_trivial_usage 674//usage:#define powertop_trivial_usage
675//usage: "" 675//usage: ""
676//usage:#define powertop_full_usage "\n\n" 676//usage:#define powertop_full_usage "\n\n"
677//usage: "Analyze power consumption on Intel-based laptops\n" 677//usage: "Analyze power consumption on Intel-based laptops"
678 678
679int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 679int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
680int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv) 680int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
diff --git a/procps/pwdx.c b/procps/pwdx.c
index 781810488..4e34149ed 100644
--- a/procps/pwdx.c
+++ b/procps/pwdx.c
@@ -21,7 +21,7 @@
21//usage:#define pwdx_trivial_usage 21//usage:#define pwdx_trivial_usage
22//usage: "PID..." 22//usage: "PID..."
23//usage:#define pwdx_full_usage "\n\n" 23//usage:#define pwdx_full_usage "\n\n"
24//usage: "Show current directory for PIDs\n" 24//usage: "Show current directory for PIDs"
25 25
26#include "libbb.h" 26#include "libbb.h"
27 27
@@ -41,7 +41,7 @@ int pwdx_main(int argc UNUSED_PARAM, char **argv)
41 // Allowed on the command line: 41 // Allowed on the command line:
42 // /proc/NUM 42 // /proc/NUM
43 // NUM 43 // NUM
44 if (strncmp(arg, "/proc/", 6) == 0) 44 if (is_prefixed_with(arg, "/proc/"))
45 arg += 6; 45 arg += 6;
46 46
47 pid = bb_strtou(arg, NULL, 10); 47 pid = bb_strtou(arg, NULL, 10);
diff --git a/runit/runsvdir.c b/runit/runsvdir.c
index af7e75ba7..b4c0b2ef0 100644
--- a/runit/runsvdir.c
+++ b/runit/runsvdir.c
@@ -59,7 +59,6 @@ struct globals {
59 int svnum; 59 int svnum;
60#if ENABLE_FEATURE_RUNSVDIR_LOG 60#if ENABLE_FEATURE_RUNSVDIR_LOG
61 char *rplog; 61 char *rplog;
62 int rploglen;
63 struct fd_pair logpipe; 62 struct fd_pair logpipe;
64 struct pollfd pfd[1]; 63 struct pollfd pfd[1];
65 unsigned stamplog; 64 unsigned stamplog;
@@ -70,7 +69,6 @@ struct globals {
70#define svdir (G.svdir ) 69#define svdir (G.svdir )
71#define svnum (G.svnum ) 70#define svnum (G.svnum )
72#define rplog (G.rplog ) 71#define rplog (G.rplog )
73#define rploglen (G.rploglen )
74#define logpipe (G.logpipe ) 72#define logpipe (G.logpipe )
75#define pfd (G.pfd ) 73#define pfd (G.pfd )
76#define stamplog (G.stamplog ) 74#define stamplog (G.stamplog )
@@ -219,15 +217,12 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
219 struct stat s; 217 struct stat s;
220 dev_t last_dev = last_dev; /* for gcc */ 218 dev_t last_dev = last_dev; /* for gcc */
221 ino_t last_ino = last_ino; /* for gcc */ 219 ino_t last_ino = last_ino; /* for gcc */
222 time_t last_mtime = 0; 220 time_t last_mtime;
223 int wstat;
224 int curdir; 221 int curdir;
225 pid_t pid;
226 unsigned deadline;
227 unsigned now;
228 unsigned stampcheck; 222 unsigned stampcheck;
229 int i; 223 int i;
230 int need_rescan = 1; 224 int need_rescan;
225 bool i_am_init;
231 char *opt_s_argv[3]; 226 char *opt_s_argv[3];
232 227
233 INIT_G(); 228 INIT_G();
@@ -238,18 +233,21 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
238 getopt32(argv, "Ps:", &opt_s_argv[0]); 233 getopt32(argv, "Ps:", &opt_s_argv[0]);
239 argv += optind; 234 argv += optind;
240 235
236 i_am_init = (getpid() == 1);
241 bb_signals(0 237 bb_signals(0
242 | (1 << SIGTERM) 238 | (1 << SIGTERM)
243 | (1 << SIGHUP) 239 | (1 << SIGHUP)
244 /* For busybox's init, SIGTERM == reboot, 240 /* For busybox's init, SIGTERM == reboot,
245 * SIGUSR1 == halt 241 * SIGUSR1 == halt,
246 * SIGUSR2 == poweroff 242 * SIGUSR2 == poweroff,
247 * so we need to intercept SIGUSRn too. 243 * Ctlr-ALt-Del sends SIGINT to init,
244 * so we need to intercept SIGUSRn and SIGINT too.
248 * Note that we do not implement actual reboot 245 * Note that we do not implement actual reboot
249 * (killall(TERM) + umount, etc), we just pause 246 * (killall(TERM) + umount, etc), we just pause
250 * respawing and avoid exiting (-> making kernel oops). 247 * respawing and avoid exiting (-> making kernel oops).
251 * The user is responsible for the rest. */ 248 * The user is responsible for the rest.
252 | (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0) 249 */
250 | (i_am_init ? ((1 << SIGUSR1) | (1 << SIGUSR2) | (1 << SIGINT)) : 0)
253 , record_signo); 251 , record_signo);
254 svdir = *argv++; 252 svdir = *argv++;
255 253
@@ -257,8 +255,7 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
257 /* setup log */ 255 /* setup log */
258 if (*argv) { 256 if (*argv) {
259 rplog = *argv; 257 rplog = *argv;
260 rploglen = strlen(rplog); 258 if (strlen(rplog) < 7) {
261 if (rploglen < 7) {
262 warnx("log must have at least seven characters"); 259 warnx("log must have at least seven characters");
263 } else if (piped_pair(logpipe)) { 260 } else if (piped_pair(logpipe)) {
264 warnx("can't create pipe for log"); 261 warnx("can't create pipe for log");
@@ -287,11 +284,16 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
287 close_on_exec_on(curdir); 284 close_on_exec_on(curdir);
288 285
289 stampcheck = monotonic_sec(); 286 stampcheck = monotonic_sec();
287 need_rescan = 1;
288 last_mtime = 0;
290 289
291 for (;;) { 290 for (;;) {
291 unsigned now;
292 unsigned sig;
293
292 /* collect children */ 294 /* collect children */
293 for (;;) { 295 for (;;) {
294 pid = wait_any_nohang(&wstat); 296 pid_t pid = wait_any_nohang(NULL);
295 if (pid <= 0) 297 if (pid <= 0)
296 break; 298 break;
297 for (i = 0; i < svnum; i++) { 299 for (i = 0; i < svnum; i++) {
@@ -345,15 +347,15 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
345 } 347 }
346 pfd[0].revents = 0; 348 pfd[0].revents = 0;
347#endif 349#endif
348 deadline = (need_rescan ? 1 : 5); 350 {
349 sig_block(SIGCHLD); 351 unsigned deadline = (need_rescan ? 1 : 5);
350#if ENABLE_FEATURE_RUNSVDIR_LOG 352#if ENABLE_FEATURE_RUNSVDIR_LOG
351 if (rplog) 353 if (rplog)
352 poll(pfd, 1, deadline*1000); 354 poll(pfd, 1, deadline*1000);
353 else 355 else
354#endif 356#endif
355 sleep(deadline); 357 sleep(deadline);
356 sig_unblock(SIGCHLD); 358 }
357 359
358#if ENABLE_FEATURE_RUNSVDIR_LOG 360#if ENABLE_FEATURE_RUNSVDIR_LOG
359 if (pfd[0].revents & POLLIN) { 361 if (pfd[0].revents & POLLIN) {
@@ -361,21 +363,25 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
361 while (read(logpipe.rd, &ch, 1) > 0) { 363 while (read(logpipe.rd, &ch, 1) > 0) {
362 if (ch < ' ') 364 if (ch < ' ')
363 ch = ' '; 365 ch = ' ';
364 for (i = 6; i < rploglen; i++) 366 for (i = 6; rplog[i] != '\0'; i++)
365 rplog[i-1] = rplog[i]; 367 rplog[i-1] = rplog[i];
366 rplog[rploglen-1] = ch; 368 rplog[i-1] = ch;
367 } 369 }
368 } 370 }
369#endif 371#endif
370 if (!bb_got_signal) 372 sig = bb_got_signal;
373 if (!sig)
371 continue; 374 continue;
375 bb_got_signal = 0;
372 376
373 /* -s SCRIPT: useful if we are init. 377 /* -s SCRIPT: useful if we are init.
374 * In this case typically script never returns, 378 * In this case typically script never returns,
375 * it halts/powers off/reboots the system. */ 379 * it halts/powers off/reboots the system. */
376 if (opt_s_argv[0]) { 380 if (opt_s_argv[0]) {
381 pid_t pid;
382
377 /* Single parameter: signal# */ 383 /* Single parameter: signal# */
378 opt_s_argv[1] = utoa(bb_got_signal); 384 opt_s_argv[1] = utoa(sig);
379 pid = spawn(opt_s_argv); 385 pid = spawn(opt_s_argv);
380 if (pid > 0) { 386 if (pid > 0) {
381 /* Remembering to wait for _any_ children, 387 /* Remembering to wait for _any_ children,
@@ -385,17 +391,16 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
385 } 391 }
386 } 392 }
387 393
388 if (bb_got_signal == SIGHUP) { 394 if (sig == SIGHUP) {
389 for (i = 0; i < svnum; i++) 395 for (i = 0; i < svnum; i++)
390 if (sv[i].pid) 396 if (sv[i].pid)
391 kill(sv[i].pid, SIGTERM); 397 kill(sv[i].pid, SIGTERM);
392 } 398 }
393 /* SIGHUP or SIGTERM (or SIGUSRn if we are init) */ 399 /* SIGHUP or SIGTERM (or SIGUSRn if we are init) */
394 /* Exit unless we are init */ 400 /* Exit unless we are init */
395 if (getpid() != 1) 401 if (!i_am_init)
396 return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS; 402 return (SIGHUP == sig) ? 111 : EXIT_SUCCESS;
397 403
398 /* init continues to monitor services forever */ 404 /* init continues to monitor services forever */
399 bb_got_signal = 0;
400 } /* for (;;) */ 405 } /* for (;;) */
401} 406}
diff --git a/shell/ash.c b/shell/ash.c
index b95356034..df7083370 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -7075,6 +7075,14 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
7075 len = strlen(p); 7075 len = strlen(p);
7076 if (!(subtype == VSPLUS || subtype == VSLENGTH)) 7076 if (!(subtype == VSPLUS || subtype == VSLENGTH))
7077 memtodest(p, len, syntax, quotes); 7077 memtodest(p, len, syntax, quotes);
7078#if ENABLE_UNICODE_SUPPORT
7079 if (subtype == VSLENGTH && len > 0) {
7080 reinit_unicode_for_ash();
7081 if (unicode_status == UNICODE_ON) {
7082 len = unicode_strlen(p);
7083 }
7084 }
7085#endif
7078 return len; 7086 return len;
7079 } 7087 }
7080 7088
@@ -7158,15 +7166,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
7158 varunset(p, var, 0, 0); 7166 varunset(p, var, 0, 0);
7159 7167
7160 if (subtype == VSLENGTH) { 7168 if (subtype == VSLENGTH) {
7161 ssize_t n = varlen; 7169 cvtnum(varlen > 0 ? varlen : 0);
7162 if (n > 0) {
7163 reinit_unicode_for_ash();
7164 if (unicode_status == UNICODE_ON) {
7165 const char *val = lookupvar(var);
7166 n = unicode_strlen(val);
7167 }
7168 }
7169 cvtnum(n > 0 ? n : 0);
7170 goto record; 7170 goto record;
7171 } 7171 }
7172 7172
@@ -10981,7 +10981,7 @@ static union node *andor(void);
10981static union node *pipeline(void); 10981static union node *pipeline(void);
10982static union node *parse_command(void); 10982static union node *parse_command(void);
10983static void parseheredoc(void); 10983static void parseheredoc(void);
10984static char peektoken(void); 10984static char nexttoken_ends_list(void);
10985static int readtoken(void); 10985static int readtoken(void);
10986 10986
10987static union node * 10987static union node *
@@ -10991,7 +10991,7 @@ list(int nlflag)
10991 int tok; 10991 int tok;
10992 10992
10993 checkkwd = CHKNL | CHKKWD | CHKALIAS; 10993 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10994 if (nlflag == 2 && peektoken()) 10994 if (nlflag == 2 && nexttoken_ends_list())
10995 return NULL; 10995 return NULL;
10996 n1 = NULL; 10996 n1 = NULL;
10997 for (;;) { 10997 for (;;) {
@@ -11033,8 +11033,15 @@ list(int nlflag)
11033 tokpushback = 1; 11033 tokpushback = 1;
11034 } 11034 }
11035 checkkwd = CHKNL | CHKKWD | CHKALIAS; 11035 checkkwd = CHKNL | CHKKWD | CHKALIAS;
11036 if (peektoken()) 11036 if (nexttoken_ends_list()) {
11037 /* Testcase: "<<EOF; then <W".
11038 * It used to segfault w/o this check:
11039 */
11040 if (heredoclist) {
11041 raise_error_unexpected_syntax(-1);
11042 }
11037 return n1; 11043 return n1;
11044 }
11038 break; 11045 break;
11039 case TEOF: 11046 case TEOF:
11040 if (heredoclist) 11047 if (heredoclist)
@@ -12471,7 +12478,7 @@ readtoken(void)
12471} 12478}
12472 12479
12473static char 12480static char
12474peektoken(void) 12481nexttoken_ends_list(void)
12475{ 12482{
12476 int t; 12483 int t;
12477 12484
diff --git a/shell/ash_test/ash-heredoc/heredoc1.right b/shell/ash_test/ash-heredoc/heredoc1.right
new file mode 100644
index 000000000..895f5ee80
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc1.right
@@ -0,0 +1 @@
heredoc1.tests: line 3: syntax error: unexpected "then"
diff --git a/shell/ash_test/ash-heredoc/heredoc1.tests b/shell/ash_test/ash-heredoc/heredoc1.tests
new file mode 100755
index 000000000..a912a67c7
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc1.tests
@@ -0,0 +1,3 @@
1# We used to SEGV on this:
2
3<<EOF; then <W
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)
5884 * Our solution: ONLY bare $(trap) or `trap` is special. 5884 * Our solution: ONLY bare $(trap) or `trap` is special.
5885 */ 5885 */
5886 s = skip_whitespace(s); 5886 s = skip_whitespace(s);
5887 if (strncmp(s, "trap", 4) == 0 5887 if (is_prefixed_with(s, "trap")
5888 && skip_whitespace(s + 4)[0] == '\0' 5888 && skip_whitespace(s + 4)[0] == '\0'
5889 ) { 5889 ) {
5890 static const char *const argv[] = { NULL, NULL }; 5890 static const char *const argv[] = { NULL, NULL };
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 @@
1#!/bin/sh
2# Copyright 2015 by Bernhard Reutner-Fischer
3# Licensed under GPLv2 or later, see file LICENSE in this source tree.
4
5. ./testing.sh
6
7# testing "test name" "command" "expected result" "file input" "stdin"
8
9testing "dc basic syntax (stdin, multiple args)" \
10 "dc" \
11 "30\n" \
12 "" "10 20+p"
13
14testing "dc basic syntax (argv, single arg)" \
15 "dc '10 20+p'" \
16 "30\n" \
17 "" ""
18
19testing "dc basic syntax (argv, multiple args)" \
20 "dc 10 20+p" \
21 "30\n" \
22 "" ""
23
24testing "dc complex with spaces (single arg)" \
25 "dc '8 8 * 2 2 + / p'" \
26 "16\n" \
27 "" ""
28
29testing "dc complex without spaces (single arg)" \
30 "dc '8 8*2 2+/p'" \
31 "16\n" \
32 "" ""
33
34testing "dc complex with spaces (multiple args)" \
35 "dc 8 8 \* 2 2 + / p" \
36 "16\n" \
37 "" ""
38
39testing "dc complex without spaces (multiple args)" \
40 "dc 8 8\*2 2+/p" \
41 "16\n" \
42 "" ""
43
44exit $FAILCOUNT
45
46# we do not support arguments
47testing "dc -e <exprs>" \
48 "dc -e '10 2+f'" \
49 "12\n" \
50 "" ""
51
52testing "dc -f <exprs-from-given-file>" \
53 "dc -f input" \
54 "12\n" \
55 "10 2+f" ""
56
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" \
44 "" \ 44 "" \
45 "stdin" 45 "stdin"
46 46
47testing "diff of empty file against stdin" \
48 "diff -u - input | $TRIM_TAB" \
49"\
50--- -
51+++ input
52@@ -1 +0,0 @@
53-a
54" \
55 "" \
56 "a\n"
57
47testing "diff of empty file against nonempty one" \ 58testing "diff of empty file against nonempty one" \
48 "diff -u - input | $TRIM_TAB" \ 59 "diff -u - input | $TRIM_TAB" \
49"\ 60"\
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
434 you will probably find this utility very helpful. If you don't have 434 you will probably find this utility very helpful. If you don't have
435 any need to reading text files, you can leave this disabled. 435 any need to reading text files, you can leave this disabled.
436 436
437config MOUNT
438 bool "mount"
439 default y
440 select PLATFORM_LINUX
441 help
442 All files and filesystems in Unix are arranged into one big directory
443 tree. The 'mount' utility is used to graft a filesystem onto a
444 particular part of the tree. A filesystem can either live on a block
445 device, or it can be accessible over the network, as is the case with
446 NFS filesystems. Most people using BusyBox will also want to enable
447 the 'mount' utility.
448
449config FEATURE_MOUNT_FAKE
450 bool "Support option -f"
451 default y
452 depends on MOUNT
453 help
454 Enable support for faking a file system mount.
455
456config FEATURE_MOUNT_VERBOSE
457 bool "Support option -v"
458 default y
459 depends on MOUNT
460 help
461 Enable multi-level -v[vv...] verbose messages. Useful if you
462 debug mount problems and want to see what is exactly passed
463 to the kernel.
464
465config FEATURE_MOUNT_HELPERS
466 bool "Support mount helpers"
467 default n
468 depends on MOUNT
469 help
470 Enable mounting of virtual file systems via external helpers.
471 E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call
472 "obexfs -b00.11.22.33.44.55 /mnt"
473 Also "mount -t sometype [-o opts] fs /mnt" will try
474 "sometype [-o opts] fs /mnt" if simple mount syscall fails.
475 The idea is to use such virtual filesystems in /etc/fstab.
476
477config FEATURE_MOUNT_LABEL
478 bool "Support specifying devices by label or UUID"
479 default y
480 depends on MOUNT
481 select VOLUMEID
482 help
483 This allows for specifying a device by label or uuid, rather than by
484 name. This feature utilizes the same functionality as blkid/findfs.
485 This also enables label or uuid support for swapon.
486
487config FEATURE_MOUNT_NFS
488 bool "Support mounting NFS file systems on Linux < 2.6.23"
489 default n
490 depends on MOUNT
491 select FEATURE_HAVE_RPC
492 select FEATURE_SYSLOG
493 help
494 Enable mounting of NFS file systems on Linux kernels prior
495 to version 2.6.23. Note that in this case mounting of NFS
496 over IPv6 will not be possible.
497
498 Note that this option links in RPC support from libc,
499 which is rather large (~10 kbytes on uclibc).
500
501config FEATURE_MOUNT_CIFS
502 bool "Support mounting CIFS/SMB file systems"
503 default y
504 depends on MOUNT
505 help
506 Enable support for samba mounts.
507
508config FEATURE_MOUNT_FLAGS
509 depends on MOUNT
510 bool "Support lots of -o flags in mount"
511 default y
512 help
513 Without this, mount only supports ro/rw/remount. With this, it
514 supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
515 noatime, diratime, nodiratime, loud, bind, move, shared, slave,
516 private, unbindable, rshared, rslave, rprivate, and runbindable.
517
518config FEATURE_MOUNT_FSTAB
519 depends on MOUNT
520 bool "Support /etc/fstab and -a"
521 default y
522 help
523 Support mount all and looking for files in /etc/fstab.
524
525config PIVOT_ROOT 437config PIVOT_ROOT
526 bool "pivot_root" 438 bool "pivot_root"
527 default y 439 default y
diff --git a/util-linux/acpid.c b/util-linux/acpid.c
index 38421c2d7..0f2cb6bdc 100644
--- a/util-linux/acpid.c
+++ b/util-linux/acpid.c
@@ -121,10 +121,8 @@ static void process_event(const char *event)
121 char *handler = xasprintf("./%s", event); 121 char *handler = xasprintf("./%s", event);
122 const char *args[] = { "run-parts", handler, NULL }; 122 const char *args[] = { "run-parts", handler, NULL };
123 123
124 // debug info 124 // log the event
125 if (option_mask32 & OPT_d) { 125 bb_error_msg("%s", event);
126 bb_error_msg("%s", event);
127 }
128 126
129 // spawn handler 127 // spawn handler
130 // N.B. run-parts would require scripts to have #!/bin/sh 128 // N.B. run-parts would require scripts to have #!/bin/sh
@@ -153,7 +151,7 @@ static const char *find_action(struct input_event *ev, const char *buf)
153 } 151 }
154 152
155 if (buf) { 153 if (buf) {
156 if (strncmp(buf, evt_tab[i].desc, strlen(buf)) == 0) { 154 if (is_prefixed_with(evt_tab[i].desc, buf)) {
157 action = evt_tab[i].desc; 155 action = evt_tab[i].desc;
158 break; 156 break;
159 } 157 }
@@ -256,7 +254,7 @@ int acpid_main(int argc UNUSED_PARAM, char **argv)
256 /* No -d "Debug", we log to log file. 254 /* No -d "Debug", we log to log file.
257 * This includes any output from children. 255 * This includes any output from children.
258 */ 256 */
259 xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO); 257 xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_APPEND), STDOUT_FILENO);
260 xdup2(STDOUT_FILENO, STDERR_FILENO); 258 xdup2(STDOUT_FILENO, STDERR_FILENO);
261 /* Also, acpid's messages (but not children) will go to syslog too */ 259 /* Also, acpid's messages (but not children) will go to syslog too */
262 openlog(applet_name, LOG_PID, LOG_DAEMON); 260 openlog(applet_name, LOG_PID, LOG_DAEMON);
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)
2781 the process hangs on the attempt to read a music CD. 2781 the process hangs on the attempt to read a music CD.
2782 So try to be careful. This only works since 2.1.73. */ 2782 So try to be careful. This only works since 2.1.73. */
2783 2783
2784 if (strncmp("/dev/hd", device, 7)) 2784 if (!is_prefixed_with(device, "/dev/hd"))
2785 return 0; 2785 return 0;
2786 2786
2787 snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5); 2787 snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5);
2788 procf = fopen_for_read(buf); 2788 procf = fopen_for_read(buf);
2789 if (procf != NULL && fgets(buf, sizeof(buf), procf)) 2789 if (procf != NULL && fgets(buf, sizeof(buf), procf))
2790 is_ide = (!strncmp(buf, "cdrom", 5) || 2790 is_ide = (is_prefixed_with(buf, "cdrom") ||
2791 !strncmp(buf, "tape", 4)); 2791 is_prefixed_with(buf, "tape"));
2792 else 2792 else
2793 /* Now when this proc file does not exist, skip the 2793 /* Now when this proc file does not exist, skip the
2794 device when it is read-only. */ 2794 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)
854 854
855 d->d_magic = BSD_DISKMAGIC; 855 d->d_magic = BSD_DISKMAGIC;
856 856
857 if (strncmp(disk_device, "/dev/sd", 7) == 0) 857 if (is_prefixed_with(disk_device, "/dev/sd"))
858 d->d_type = BSD_DTYPE_SCSI; 858 d->d_type = BSD_DTYPE_SCSI;
859 else 859 else
860 d->d_type = BSD_DTYPE_ST506; 860 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)
440 (unsigned int*)sgilabel, sizeof(*sgilabel)) == 0); 440 (unsigned int*)sgilabel, sizeof(*sgilabel)) == 0);
441 441
442 write_sector(0, sgilabel); 442 write_sector(0, sgilabel);
443 if (!strncmp((char*)sgilabel->directory[0].vol_file_name, "sgilabel", 8)) { 443 if (is_prefixed_with((char*)sgilabel->directory[0].vol_file_name, "sgilabel")) {
444 /* 444 /*
445 * keep this habit of first writing the "sgilabel". 445 * keep this habit of first writing the "sgilabel".
446 * I never tested whether it works without (AN 981002). 446 * 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)
27 if (!dev) 27 if (!dev)
28 bb_show_usage(); 28 bb_show_usage();
29 29
30 if (strncmp(dev, "/dev/", 5) == 0) { 30 if (is_prefixed_with(dev, "/dev/")) {
31 /* Just pass any /dev/xxx name right through. 31 /* Just pass any /dev/xxx name right through.
32 * This might aid in some scripts being able 32 * This might aid in some scripts being able
33 * to call this unconditionally */ 33 * to call this unconditionally */
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 @@
32//usage: " -o OFFSET Offset in bytes to discard from" 32//usage: " -o OFFSET Offset in bytes to discard from"
33//usage: "\n -l LEN Bytes to discard" 33//usage: "\n -l LEN Bytes to discard"
34//usage: "\n -m MIN Minimum extent length" 34//usage: "\n -m MIN Minimum extent length"
35//usage: "\n -v, Print number of discarded bytes" 35//usage: "\n -v Print number of discarded bytes"
36//usage: ) 36//usage: )
37 37
38#include "libbb.h" 38#include "libbb.h"
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)
69 strftime(cp, sizeof(cp), "%c", ptm); 69 strftime(cp, sizeof(cp), "%c", ptm);
70#else 70#else
71 char *cp = ctime(&t); 71 char *cp = ctime(&t);
72 strchrnul(cp, '\n')[0] = '\0'; 72 chomp(cp);
73#endif 73#endif
74 74
75#if !SHOW_HWCLOCK_DIFF 75#if !SHOW_HWCLOCK_DIFF
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)
610 * We use strstr("/block/") to forestall future surprises. 610 * We use strstr("/block/") to forestall future surprises.
611 */ 611 */
612 type = S_IFCHR; 612 type = S_IFCHR;
613 if (strstr(path, "/block/") || (G.subsystem && strncmp(G.subsystem, "block", 5) == 0)) 613 if (strstr(path, "/block/") || (G.subsystem && is_prefixed_with(G.subsystem, "block")))
614 type = S_IFBLK; 614 type = S_IFBLK;
615 615
616#if ENABLE_FEATURE_MDEV_CONF 616#if ENABLE_FEATURE_MDEV_CONF
diff --git a/util-linux/mount.c b/util-linux/mount.c
index 62fd41fd7..cb40c802d 100644
--- a/util-linux/mount.c
+++ b/util-linux/mount.c
@@ -17,8 +17,103 @@
17// mount_it_now() does the actual mount. 17// mount_it_now() does the actual mount.
18// 18//
19 19
20//config:config MOUNT
21//config: bool "mount"
22//config: default y
23//config: select PLATFORM_LINUX
24//config: help
25//config: All files and filesystems in Unix are arranged into one big directory
26//config: tree. The 'mount' utility is used to graft a filesystem onto a
27//config: particular part of the tree. A filesystem can either live on a block
28//config: device, or it can be accessible over the network, as is the case with
29//config: NFS filesystems. Most people using BusyBox will also want to enable
30//config: the 'mount' utility.
31//config:
32//config:config FEATURE_MOUNT_FAKE
33//config: bool "Support option -f"
34//config: default y
35//config: depends on MOUNT
36//config: help
37//config: Enable support for faking a file system mount.
38//config:
39//config:config FEATURE_MOUNT_VERBOSE
40//config: bool "Support option -v"
41//config: default y
42//config: depends on MOUNT
43//config: help
44//config: Enable multi-level -v[vv...] verbose messages. Useful if you
45//config: debug mount problems and want to see what is exactly passed
46//config: to the kernel.
47//config:
48//config:config FEATURE_MOUNT_HELPERS
49//config: bool "Support mount helpers"
50//config: default n
51//config: depends on MOUNT
52//config: help
53//config: Enable mounting of virtual file systems via external helpers.
54//config: E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call
55//config: "obexfs -b00.11.22.33.44.55 /mnt"
56//config: Also "mount -t sometype [-o opts] fs /mnt" will try
57//config: "sometype [-o opts] fs /mnt" if simple mount syscall fails.
58//config: The idea is to use such virtual filesystems in /etc/fstab.
59//config:
60//config:config FEATURE_MOUNT_LABEL
61//config: bool "Support specifying devices by label or UUID"
62//config: default y
63//config: depends on MOUNT
64//config: select VOLUMEID
65//config: help
66//config: This allows for specifying a device by label or uuid, rather than by
67//config: name. This feature utilizes the same functionality as blkid/findfs.
68//config: This also enables label or uuid support for swapon.
69//config:
70//config:config FEATURE_MOUNT_NFS
71//config: bool "Support mounting NFS file systems on Linux < 2.6.23"
72//config: default n
73//config: depends on MOUNT
74//config: select FEATURE_HAVE_RPC
75//config: select FEATURE_SYSLOG
76//config: help
77//config: Enable mounting of NFS file systems on Linux kernels prior
78//config: to version 2.6.23. Note that in this case mounting of NFS
79//config: over IPv6 will not be possible.
80//config:
81//config: Note that this option links in RPC support from libc,
82//config: which is rather large (~10 kbytes on uclibc).
83//config:
84//config:config FEATURE_MOUNT_CIFS
85//config: bool "Support mounting CIFS/SMB file systems"
86//config: default y
87//config: depends on MOUNT
88//config: help
89//config: Enable support for samba mounts.
90//config:
91//config:config FEATURE_MOUNT_FLAGS
92//config: depends on MOUNT
93//config: bool "Support lots of -o flags in mount"
94//config: default y
95//config: help
96//config: Without this, mount only supports ro/rw/remount. With this, it
97//config: supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
98//config: noatime, diratime, nodiratime, loud, bind, move, shared, slave,
99//config: private, unbindable, rshared, rslave, rprivate, and runbindable.
100//config:
101//config:config FEATURE_MOUNT_FSTAB
102//config: depends on MOUNT
103//config: bool "Support /etc/fstab and -a"
104//config: default y
105//config: help
106//config: Support mount all and looking for files in /etc/fstab.
107//config:
108//config:config FEATURE_MOUNT_OTHERTAB
109//config: depends on FEATURE_MOUNT_FSTAB
110//config: bool "Support -T <alt_fstab>"
111//config: default y
112//config: help
113//config: Support mount -T (specifying an alternate fstab)
114
20//usage:#define mount_trivial_usage 115//usage:#define mount_trivial_usage
21//usage: "[OPTIONS] [-o OPTS] DEVICE NODE" 116//usage: "[OPTIONS] [-o OPT] DEVICE NODE"
22//usage:#define mount_full_usage "\n\n" 117//usage:#define mount_full_usage "\n\n"
23//usage: "Mount a filesystem. Filesystem autodetection requires /proc.\n" 118//usage: "Mount a filesystem. Filesystem autodetection requires /proc.\n"
24//usage: "\n -a Mount all filesystems in fstab" 119//usage: "\n -a Mount all filesystems in fstab"
@@ -41,8 +136,11 @@
41//usage: ) 136//usage: )
42////usage: "\n -s Sloppy (ignored)" 137////usage: "\n -s Sloppy (ignored)"
43//usage: "\n -r Read-only mount" 138//usage: "\n -r Read-only mount"
44//usage: "\n -w Read-write mount (default)" 139////usage: "\n -w Read-write mount (default)"
45//usage: "\n -t FSTYPE[,...] Filesystem type(s)" 140//usage: "\n -t FSTYPE[,...] Filesystem type(s)"
141//usage: IF_FEATURE_MOUNT_OTHERTAB(
142//usage: "\n -T FILE Read FILE instead of /etc/fstab"
143//usage: )
46//usage: "\n -O OPT Mount only filesystems with option OPT (-a only)" 144//usage: "\n -O OPT Mount only filesystems with option OPT (-a only)"
47//usage: "\n-o OPT:" 145//usage: "\n-o OPT:"
48//usage: IF_FEATURE_MOUNT_LOOP( 146//usage: IF_FEATURE_MOUNT_LOOP(
@@ -64,7 +162,7 @@
64//usage: "\n move Relocate an existing mount point" 162//usage: "\n move Relocate an existing mount point"
65//usage: ) 163//usage: )
66//usage: "\n remount Remount a mounted filesystem, changing flags" 164//usage: "\n remount Remount a mounted filesystem, changing flags"
67//usage: "\n ro/rw Same as -r/-w" 165//usage: "\n ro Same as -r"
68//usage: "\n" 166//usage: "\n"
69//usage: "\nThere are filesystem-specific -o flags." 167//usage: "\nThere are filesystem-specific -o flags."
70//usage: 168//usage:
@@ -167,7 +265,7 @@ enum {
167}; 265};
168 266
169 267
170#define OPTION_STR "o:t:rwanfvsiO:" 268#define OPTION_STR "o:t:rwanfvsiO:" IF_FEATURE_MOUNT_OTHERTAB("T:")
171enum { 269enum {
172 OPT_o = (1 << 0), 270 OPT_o = (1 << 0),
173 OPT_t = (1 << 1), 271 OPT_t = (1 << 1),
@@ -180,6 +278,7 @@ enum {
180 OPT_s = (1 << 8), 278 OPT_s = (1 << 8),
181 OPT_i = (1 << 9), 279 OPT_i = (1 << 9),
182 OPT_O = (1 << 10), 280 OPT_O = (1 << 10),
281 OPT_T = (1 << 11),
183}; 282};
184 283
185#if ENABLE_FEATURE_MTAB_SUPPORT 284#if ENABLE_FEATURE_MTAB_SUPPORT
@@ -542,7 +641,7 @@ static llist_t *get_block_backed_filesystems(void)
542 if (!f) continue; 641 if (!f) continue;
543 642
544 while ((buf = xmalloc_fgetline(f)) != NULL) { 643 while ((buf = xmalloc_fgetline(f)) != NULL) {
545 if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5])) 644 if (is_prefixed_with(buf, "nodev") && isspace(buf[5]))
546 goto next; 645 goto next;
547 fs = skip_whitespace(buf); 646 fs = skip_whitespace(buf);
548 if (*fs == '#' || *fs == '*' || !*fs) 647 if (*fs == '#' || *fs == '*' || !*fs)
@@ -1265,9 +1364,9 @@ static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *fi
1265 strcspn(opteq, " \t\n\r,")); 1364 strcspn(opteq, " \t\n\r,"));
1266 continue; 1365 continue;
1267 case 18: // "proto" 1366 case 18: // "proto"
1268 if (!strncmp(opteq, "tcp", 3)) 1367 if (is_prefixed_with(opteq, "tcp"))
1269 tcp = 1; 1368 tcp = 1;
1270 else if (!strncmp(opteq, "udp", 3)) 1369 else if (is_prefixed_with(opteq, "udp"))
1271 tcp = 0; 1370 tcp = 0;
1272 else 1371 else
1273 bb_error_msg("warning: unrecognized proto= option"); 1372 bb_error_msg("warning: unrecognized proto= option");
@@ -1360,7 +1459,7 @@ static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *fi
1360 "rdirplus\0" 1459 "rdirplus\0"
1361 "acl\0"; 1460 "acl\0";
1362 int val = 1; 1461 int val = 1;
1363 if (!strncmp(opt, "no", 2)) { 1462 if (is_prefixed_with(opt, "no")) {
1364 val = 0; 1463 val = 0;
1365 opt += 2; 1464 opt += 2;
1366 } 1465 }
@@ -1880,7 +1979,7 @@ static int singlemount(struct mntent *mp, int ignore_busy)
1880 } 1979 }
1881 1980
1882 // Might this be an NFS filesystem? 1981 // Might this be an NFS filesystem?
1883 if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0) 1982 if ((!mp->mnt_type || is_prefixed_with(mp->mnt_type, "nfs"))
1884 && strchr(mp->mnt_fsname, ':') != NULL 1983 && strchr(mp->mnt_fsname, ':') != NULL
1885 ) { 1984 ) {
1886 if (!mp->mnt_type) 1985 if (!mp->mnt_type)
@@ -2034,7 +2133,7 @@ int mount_main(int argc UNUSED_PARAM, char **argv)
2034 char *O_optmatch = NULL; 2133 char *O_optmatch = NULL;
2035 char *storage_path; 2134 char *storage_path;
2036 llist_t *lst_o = NULL; 2135 llist_t *lst_o = NULL;
2037 const char *fstabname; 2136 const char *fstabname = "/etc/fstab";
2038 FILE *fstab; 2137 FILE *fstab;
2039 int i, j; 2138 int i, j;
2040 int rc = EXIT_SUCCESS; 2139 int rc = EXIT_SUCCESS;
@@ -2061,6 +2160,7 @@ int mount_main(int argc UNUSED_PARAM, char **argv)
2061 // Max 2 params; -o is a list, -v is a counter 2160 // Max 2 params; -o is a list, -v is a counter
2062 opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv"); 2161 opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
2063 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch 2162 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
2163 IF_FEATURE_MOUNT_OTHERTAB(, &fstabname)
2064 IF_FEATURE_MOUNT_VERBOSE(, &verbose)); 2164 IF_FEATURE_MOUNT_VERBOSE(, &verbose));
2065 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o 2165 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
2066 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r 2166 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
@@ -2128,8 +2228,10 @@ int mount_main(int argc UNUSED_PARAM, char **argv)
2128 return rc; 2228 return rc;
2129 } 2229 }
2130 2230
2231 // A malicious user could overmount /usr without this.
2232 if (ENABLE_FEATURE_MOUNT_OTHERTAB && nonroot)
2233 fstabname = "/etc/fstab";
2131 // Open either fstab or mtab 2234 // Open either fstab or mtab
2132 fstabname = "/etc/fstab";
2133 if (cmdopt_flags & MS_REMOUNT) { 2235 if (cmdopt_flags & MS_REMOUNT) {
2134 // WARNING. I am not sure this matches util-linux's 2236 // WARNING. I am not sure this matches util-linux's
2135 // behavior. It's possible util-linux does not 2237 // behavior. It's possible util-linux does not
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 @@
11//usage:#define rdate_trivial_usage 11//usage:#define rdate_trivial_usage
12//usage: "[-sp] HOST" 12//usage: "[-sp] HOST"
13//usage:#define rdate_full_usage "\n\n" 13//usage:#define rdate_full_usage "\n\n"
14//usage: "Get and possibly set the system date/time from a remote HOST\n" 14//usage: "Get and possibly set system time from a remote HOST\n"
15//usage: "\n -s Set the system date/time (default)" 15//usage: "\n -s Set system time (default)"
16//usage: "\n -p Print the date/time" 16//usage: "\n -p Print time"
17 17
18#include "libbb.h" 18#include "libbb.h"
19 19
@@ -36,7 +36,7 @@ static time_t askremotedate(const char *host)
36 fd = create_and_connect_stream_or_die(host, bb_lookup_port("time", "tcp", 37)); 36 fd = create_and_connect_stream_or_die(host, bb_lookup_port("time", "tcp", 37));
37 37
38 if (safe_read(fd, &nett, 4) != 4) /* read time from server */ 38 if (safe_read(fd, &nett, 4) != 4) /* read time from server */
39 bb_error_msg_and_die("%s did not send the complete time", host); 39 bb_error_msg_and_die("%s: %s", host, "short read");
40 if (ENABLE_FEATURE_CLEAN_UP) 40 if (ENABLE_FEATURE_CLEAN_UP)
41 close(fd); 41 close(fd);
42 42
diff --git a/util-linux/rtcwake.c b/util-linux/rtcwake.c
index 33cdbfad4..8aee0cfcb 100644
--- a/util-linux/rtcwake.c
+++ b/util-linux/rtcwake.c
@@ -32,18 +32,18 @@
32//usage: "\n -l,--local Clock is set to local time" 32//usage: "\n -l,--local Clock is set to local time"
33//usage: "\n -u,--utc Clock is set to UTC time" 33//usage: "\n -u,--utc Clock is set to UTC time"
34//usage: "\n -d,--device=DEV Specify the RTC device" 34//usage: "\n -d,--device=DEV Specify the RTC device"
35//usage: "\n -m,--mode=MODE Set the sleep state (default: standby)" 35//usage: "\n -m,--mode=MODE Set sleep state (default: standby)"
36//usage: "\n -s,--seconds=SEC Set the timeout in SEC seconds from now" 36//usage: "\n -s,--seconds=SEC Set timeout in SEC seconds from now"
37//usage: "\n -t,--time=TIME Set the timeout to TIME seconds from epoch" 37//usage: "\n -t,--time=TIME Set timeout to TIME seconds from epoch"
38//usage: ) 38//usage: )
39//usage: IF_NOT_LONG_OPTS( 39//usage: IF_NOT_LONG_OPTS(
40//usage: "\n -a Read clock mode from adjtime" 40//usage: "\n -a Read clock mode from adjtime"
41//usage: "\n -l Clock is set to local time" 41//usage: "\n -l Clock is set to local time"
42//usage: "\n -u Clock is set to UTC time" 42//usage: "\n -u Clock is set to UTC time"
43//usage: "\n -d DEV Specify the RTC device" 43//usage: "\n -d DEV Specify the RTC device"
44//usage: "\n -m MODE Set the sleep state (default: standby)" 44//usage: "\n -m MODE Set sleep state (default: standby)"
45//usage: "\n -s SEC Set the timeout in SEC seconds from now" 45//usage: "\n -s SEC Set timeout in SEC seconds from now"
46//usage: "\n -t TIME Set the timeout to TIME seconds from epoch" 46//usage: "\n -t TIME Set timeout to TIME seconds from epoch"
47//usage: ) 47//usage: )
48 48
49#include "libbb.h" 49#include "libbb.h"
@@ -66,7 +66,7 @@ static NOINLINE bool may_wakeup(const char *rtcname)
66 return false; 66 return false;
67 67
68 /* wakeup events could be disabled or not supported */ 68 /* wakeup events could be disabled or not supported */
69 return strncmp(buf, "enabled\n", 8) == 0; 69 return is_prefixed_with(buf, "enabled\n") != NULL;
70} 70}
71 71
72static NOINLINE void setup_alarm(int fd, time_t *wakeup, time_t rtc_time) 72static NOINLINE void setup_alarm(int fd, time_t *wakeup, time_t rtc_time)
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 @@
8 */ 8 */
9 9
10//usage:#define swapon_trivial_usage 10//usage:#define swapon_trivial_usage
11//usage: "[-a]" IF_FEATURE_SWAPON_DISCARD(" [-d[POL]]") IF_FEATURE_SWAPON_PRI(" [-p PRI]") " [DEVICE]" 11//usage: "[-a] [-e]" IF_FEATURE_SWAPON_DISCARD(" [-d[POL]]") IF_FEATURE_SWAPON_PRI(" [-p PRI]") " [DEVICE]"
12//usage:#define swapon_full_usage "\n\n" 12//usage:#define swapon_full_usage "\n\n"
13//usage: "Start swapping on DEVICE\n" 13//usage: "Start swapping on DEVICE\n"
14//usage: "\n -a Start swapping on all swap devices" 14//usage: "\n -a Start swapping on all swap devices"
@@ -16,15 +16,17 @@
16//usage: "\n -d[POL] Discard blocks at swapon (POL=once)," 16//usage: "\n -d[POL] Discard blocks at swapon (POL=once),"
17//usage: "\n as freed (POL=pages), or both (POL omitted)" 17//usage: "\n as freed (POL=pages), or both (POL omitted)"
18//usage: ) 18//usage: )
19//usage: "\n -e Silently skip devices that do not exist"
19//usage: IF_FEATURE_SWAPON_PRI( 20//usage: IF_FEATURE_SWAPON_PRI(
20//usage: "\n -p PRI Set swap device priority" 21//usage: "\n -p PRI Set swap device priority"
21//usage: ) 22//usage: )
22//usage: 23//usage:
23//usage:#define swapoff_trivial_usage 24//usage:#define swapoff_trivial_usage
24//usage: "[-a] [DEVICE]" 25//usage: "[-a] [-e] [DEVICE]"
25//usage:#define swapoff_full_usage "\n\n" 26//usage:#define swapoff_full_usage "\n\n"
26//usage: "Stop swapping on DEVICE\n" 27//usage: "Stop swapping on DEVICE\n"
27//usage: "\n -a Stop swapping on all swap devices" 28//usage: "\n -a Stop swapping on all swap devices"
29//usage: "\n -e Silently skip devices that do not exist"
28 30
29#include "libbb.h" 31#include "libbb.h"
30#include <mntent.h> 32#include <mntent.h>
@@ -77,15 +79,18 @@ struct globals {
77/* Command line options */ 79/* Command line options */
78enum { 80enum {
79 OPTBIT_a, /* -a all */ 81 OPTBIT_a, /* -a all */
82 OPTBIT_e, /* -e ifexists */
80 IF_FEATURE_SWAPON_DISCARD( OPTBIT_d ,) /* -d discard */ 83 IF_FEATURE_SWAPON_DISCARD( OPTBIT_d ,) /* -d discard */
81 IF_FEATURE_SWAPON_PRI ( OPTBIT_p ,) /* -p priority */ 84 IF_FEATURE_SWAPON_PRI ( OPTBIT_p ,) /* -p priority */
82 OPT_a = 1 << OPTBIT_a, 85 OPT_a = 1 << OPTBIT_a,
86 OPT_e = 1 << OPTBIT_e,
83 OPT_d = IF_FEATURE_SWAPON_DISCARD((1 << OPTBIT_d)) + 0, 87 OPT_d = IF_FEATURE_SWAPON_DISCARD((1 << OPTBIT_d)) + 0,
84 OPT_p = IF_FEATURE_SWAPON_PRI ((1 << OPTBIT_p)) + 0, 88 OPT_p = IF_FEATURE_SWAPON_PRI ((1 << OPTBIT_p)) + 0,
85}; 89};
86 90
87#define OPT_ALL (option_mask32 & OPT_a) 91#define OPT_ALL (option_mask32 & OPT_a)
88#define OPT_DISCARD (option_mask32 & OPT_d) 92#define OPT_DISCARD (option_mask32 & OPT_d)
93#define OPT_IFEXISTS (option_mask32 & OPT_e)
89#define OPT_PRIO (option_mask32 & OPT_p) 94#define OPT_PRIO (option_mask32 & OPT_p)
90 95
91static int swap_enable_disable(char *device) 96static int swap_enable_disable(char *device)
@@ -95,6 +100,8 @@ static int swap_enable_disable(char *device)
95 struct stat st; 100 struct stat st;
96 101
97 resolve_mount_spec(&device); 102 resolve_mount_spec(&device);
103 if (!OPT_IFEXISTS)
104 xstat(device, &st);
98 105
99 if (do_swapoff) { 106 if (do_swapoff) {
100 err = swapoff(device); 107 err = swapoff(device);
@@ -112,7 +119,8 @@ static int swap_enable_disable(char *device)
112 } 119 }
113 err = swapon(device, g_flags); 120 err = swapon(device, g_flags);
114 /* Don't complain on swapon -a if device is already in use */ 121 /* Don't complain on swapon -a if device is already in use */
115 quiet = (OPT_ALL && errno == EBUSY); 122 /* Don't complain if file does not exist with -e option */
123 quiet = (OPT_ALL && errno == EBUSY) || (OPT_IFEXISTS && errno == ENOENT);
116 } 124 }
117 } 125 }
118 126
@@ -229,7 +237,7 @@ static int do_all_in_proc_swaps(void)
229 return err; 237 return err;
230} 238}
231 239
232#define OPTSTR_SWAPON "a" \ 240#define OPTSTR_SWAPON "ae" \
233 IF_FEATURE_SWAPON_DISCARD("d::") \ 241 IF_FEATURE_SWAPON_DISCARD("d::") \
234 IF_FEATURE_SWAPON_PRI("p:") 242 IF_FEATURE_SWAPON_PRI("p:")
235 243
@@ -242,7 +250,7 @@ int swap_on_off_main(int argc UNUSED_PARAM, char **argv)
242 250
243 INIT_G(); 251 INIT_G();
244 252
245 getopt32(argv, do_swapoff ? "a" : OPTSTR_SWAPON 253 getopt32(argv, do_swapoff ? "ae" : OPTSTR_SWAPON
246 IF_FEATURE_SWAPON_DISCARD(, &discard) 254 IF_FEATURE_SWAPON_DISCARD(, &discard)
247 IF_FEATURE_SWAPON_PRI(, &prio) 255 IF_FEATURE_SWAPON_PRI(, &prio)
248 ); 256 );
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)
302{ 302{
303 char *tmp = *fsname; 303 char *tmp = *fsname;
304 304
305 if (strncmp(*fsname, "UUID=", 5) == 0) 305 if (is_prefixed_with(*fsname, "UUID="))
306 tmp = get_devname_from_uuid(*fsname + 5); 306 tmp = get_devname_from_uuid(*fsname + 5);
307 else if (strncmp(*fsname, "LABEL=", 6) == 0) 307 else if (is_prefixed_with(*fsname, "LABEL=") == 0)
308 tmp = get_devname_from_label(*fsname + 6); 308 tmp = get_devname_from_label(*fsname + 6);
309 309
310 if (tmp == *fsname) 310 if (tmp == *fsname)