diff options
author | Ron Yorston <rmy@pobox.com> | 2015-03-14 20:33:00 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2015-03-14 20:33:00 +0000 |
commit | a4f58436b78fe59e57620c6e0301f213ee25f273 (patch) | |
tree | 8355f724926e605280af2d6f2b1ccc6b1bd02dee | |
parent | ba0c36cfcf84efbac6f89e27238e04bb57e9cd45 (diff) | |
parent | 49acc1a7618a28d34381cbb7661d7c981fcb238f (diff) | |
download | busybox-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
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 | # | ||
50 | cscope.files | ||
51 | cscope.in.out | ||
52 | cscope.out | ||
53 | cscope.po.out | ||
@@ -1,7 +1,7 @@ | |||
1 | VERSION = 1 | 1 | VERSION = 1 |
2 | PATCHLEVEL = 23 | 2 | PATCHLEVEL = 24 |
3 | SUBLEVEL = 0 | 3 | SUBLEVEL = 0 |
4 | EXTRAVERSION = | 4 | EXTRAVERSION = .git |
5 | NAME = Unnamed | 5 | NAME = Unnamed |
6 | 6 | ||
7 | # *DOCUMENTATION* | 7 | # *DOCUMENTATION* |
@@ -1167,24 +1167,7 @@ endif | |||
1167 | ALLSOURCE_ARCHS := $(ARCH) | 1167 | ALLSOURCE_ARCHS := $(ARCH) |
1168 | 1168 | ||
1169 | define all-sources | 1169 | define 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 ) | ||
1188 | endef | 1171 | endef |
1189 | 1172 | ||
1190 | quiet_cmd_cscope-file = FILELST cscope.files | 1173 | quiet_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) \ |
418 | do { \ | 418 | do { \ |
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 */ |
424 | static void put_16bit(ush w) | 425 | static 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 | ||
435 | static void put_32bit(ulg n) | 462 | static 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 | ||
2010 | static void zip(ulg time_stamp) | 2037 | static 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) | |||
2044 | static | 2071 | static |
2045 | IF_DESKTOP(long long) int FAST_FUNC pack_gzip(transformer_state_t *xstate UNUSED_PARAM) | 2072 | IF_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:= \ | |||
30 | DPKG_FILES:= \ | 30 | DPKG_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 | ||
39 | INSERT | 41 | INSERT |
40 | 42 | ||
@@ -43,7 +45,7 @@ lib-$(CONFIG_DPKG_DEB) += $(DPKG_FILES) | |||
43 | 45 | ||
44 | lib-$(CONFIG_AR) += get_header_ar.o unpack_ar_archive.o | 46 | lib-$(CONFIG_AR) += get_header_ar.o unpack_ar_archive.o |
45 | lib-$(CONFIG_CPIO) += get_header_cpio.o | 47 | lib-$(CONFIG_CPIO) += get_header_cpio.o |
46 | lib-$(CONFIG_TAR) += get_header_tar.o | 48 | lib-$(CONFIG_TAR) += get_header_tar.o unsafe_prefix.o |
47 | lib-$(CONFIG_FEATURE_TAR_TO_COMMAND) += data_extract_to_command.o | 49 | lib-$(CONFIG_FEATURE_TAR_TO_COMMAND) += data_extract_to_command.o |
48 | lib-$(CONFIG_LZOP) += lzo1x_1.o lzo1x_1o.o lzo1x_d.o | 50 | lib-$(CONFIG_LZOP) += lzo1x_1.o lzo1x_1o.o lzo1x_d.o |
49 | lib-$(CONFIG_LZOP_COMPR_HIGH) += lzo1x_9x.o | 51 | lib-$(CONFIG_LZOP_COMPR_HIGH) += lzo1x_9x.o |
@@ -52,7 +54,7 @@ lib-$(CONFIG_UNLZMA) += open_transformer.o decompress_unlzma. | |||
52 | lib-$(CONFIG_UNXZ) += open_transformer.o decompress_unxz.o | 54 | lib-$(CONFIG_UNXZ) += open_transformer.o decompress_unxz.o |
53 | lib-$(CONFIG_GUNZIP) += open_transformer.o decompress_gunzip.o | 55 | lib-$(CONFIG_GUNZIP) += open_transformer.o decompress_gunzip.o |
54 | lib-$(CONFIG_UNCOMPRESS) += open_transformer.o decompress_uncompress.o | 56 | lib-$(CONFIG_UNCOMPRESS) += open_transformer.o decompress_uncompress.o |
55 | lib-$(CONFIG_UNZIP) += open_transformer.o decompress_gunzip.o | 57 | lib-$(CONFIG_UNZIP) += open_transformer.o decompress_gunzip.o unsafe_prefix.o |
56 | lib-$(CONFIG_RPM2CPIO) += open_transformer.o decompress_gunzip.o get_header_cpio.o | 58 | lib-$(CONFIG_RPM2CPIO) += open_transformer.o decompress_gunzip.o get_header_cpio.o |
57 | lib-$(CONFIG_RPM) += open_transformer.o decompress_gunzip.o get_header_cpio.o | 59 | lib-$(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 @@ | |||
17 | typedef uint32_t aliased_uint32_t FIX_ALIASING; | 17 | typedef uint32_t aliased_uint32_t FIX_ALIASING; |
18 | typedef off_t aliased_off_t FIX_ALIASING; | 18 | typedef off_t aliased_off_t FIX_ALIASING; |
19 | 19 | ||
20 | |||
21 | const 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! */ |
51 | static unsigned long long getOctal(char *str, int len) | 21 | static 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 | |||
9 | char 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 | |||
9 | const 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 | ||
90 | config 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 | |||
98 | config 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 | |||
112 | config 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 | |||
120 | config 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 | |||
128 | config DF | 90 | config 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 | ||
135 | enum { | ||
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 | ||
81 | static void dd_output_status(int UNUSED_PARAM cur_signal) | 151 | static 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, | |||
148 | int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 225 | int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
149 | int dd_main(int argc UNUSED_PARAM, char **argv) | 226 | int 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 | ||
349 | int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 351 | int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
350 | int patch_main(int argc UNUSED_PARAM, char **argv) | 352 | int 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 |
23 | null root:root 666 @chmod 666 $MDEV | 23 | null root:root 666 @chmod 666 $MDEV |
24 | zero root:root 666 | 24 | zero root:root 666 |
25 | full root:root 666 | 25 | full root:root 666 |
@@ -31,7 +31,7 @@ grsec root:root 660 | |||
31 | kmem root:root 640 | 31 | kmem root:root 640 |
32 | mem root:root 640 | 32 | mem root:root 640 |
33 | port root:root 640 | 33 | port 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 |
35 | console root:tty 600 @chmod 600 $MDEV | 35 | console root:tty 600 @chmod 600 $MDEV |
36 | ptmx root:tty 666 | 36 | ptmx root:tty 666 |
37 | pty.* root:tty 660 | 37 | pty.* root:tty 660 |
@@ -63,6 +63,12 @@ control.* root:audio 660 =snd/ | |||
63 | midi.* root:audio 660 =snd/ | 63 | midi.* root:audio 660 =snd/ |
64 | seq root:audio 660 =snd/ | 64 | seq root:audio 660 =snd/ |
65 | timer root:audio 660 =snd/ | 65 | timer root:audio 660 =snd/ |
66 | # for kernels/mdevs which expose full path of alsa devices: | ||
67 | snd/pcm.* root:audio 660 | ||
68 | snd/control.* root:audio 660 | ||
69 | snd/midi.* root:audio 660 | ||
70 | snd/seq root:audio 660 | ||
71 | snd/timer root:audio 660 | ||
66 | 72 | ||
67 | adsp root:audio 660 >sound/ | 73 | adsp root:audio 660 >sound/ |
68 | audio root:audio 660 >sound/ | 74 | audio 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: |
144 | ACTION=change;SUBSYSTEM=block;DISK_MEDIA_CHANGE=1;.* 0:0 660 */etc/mdev.conf.change_blockdev.sh | 150 | ACTION=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; | |||
184 | char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC; | 184 | char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC; |
185 | char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC; | 185 | char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC; |
186 | char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC; | 186 | char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC; |
187 | char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC; | ||
187 | 188 | ||
188 | void seek_by_jump(int fd, off_t amount) FAST_FUNC; | 189 | void seek_by_jump(int fd, off_t amount) FAST_FUNC; |
189 | void seek_by_read(int fd, off_t amount) FAST_FUNC; | 190 | void 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. */ | ||
53 | extern void setgrent(void); | ||
54 | |||
55 | /* Close the group-file stream. */ | 42 | /* Close the group-file stream. */ |
56 | extern void endgrent(void); | 43 | void 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. */ | ||
60 | extern struct group *getgrent(void); | ||
61 | |||
62 | /* Read a group entry from STREAM. */ | ||
63 | extern struct group *fgetgrent(FILE *__stream); | ||
64 | |||
65 | /* Write the given entry onto the given stream. */ | ||
66 | extern int putgrent(const struct group *__restrict __p, | ||
67 | FILE *__restrict __f); | ||
68 | #endif | ||
69 | |||
70 | /* Search for an entry with a matching group ID. */ | ||
71 | extern struct group *getgrgid(gid_t __gid); | ||
72 | |||
73 | /* Search for an entry with a matching group name. */ | ||
74 | extern 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 | |||
84 | extern 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. */ |
89 | extern int getgrgid_r(gid_t __gid, struct group *__restrict __resultbuf, | 46 | struct 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. */ |
94 | extern int getgrnam_r(const char *__restrict __name, | 49 | struct 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. */ | ||
101 | extern 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. */ |
109 | extern int getgrouplist(const char *__user, gid_t __group, | 56 | int 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. */ |
115 | extern int initgroups(const char *__user, gid_t __group); | 62 | int FAST_FUNC initgroups(const char *__user, gid_t __group); |
116 | 63 | ||
117 | POP_SAVED_FUNCTION_VISIBILITY | 64 | POP_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) */ |
403 | char *last_char_is(const char *s, int c) FAST_FUNC; | 403 | char *last_char_is(const char *s, int c) FAST_FUNC; |
404 | const char* endofname(const char *name) FAST_FUNC; | 404 | const char* endofname(const char *name) FAST_FUNC; |
405 | char *is_prefixed_with(const char *string, const char *key) FAST_FUNC; | ||
405 | 406 | ||
406 | int ndelay_on(int fd) FAST_FUNC; | 407 | int ndelay_on(int fd) FAST_FUNC; |
407 | int ndelay_off(int fd) FAST_FUNC; | 408 | int 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 |
938 | void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); | 939 | void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); |
939 | void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); | 940 | void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); |
941 | void 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); | |||
550 | extern char *stpcpy(char *p, const char *to_add) FAST_FUNC; | 555 | extern 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 | ||
564 | static 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 |
554 | extern char *strcasestr(const char *s, const char *pattern) FAST_FUNC; | 571 | extern 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. */ |
52 | extern void setpwent(void); | 45 | void FAST_FUNC setpwent(void); |
53 | 46 | ||
54 | /* Close the password-file stream. */ | 47 | /* Close the password-file stream. */ |
55 | extern void endpwent(void); | 48 | void 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. */ |
59 | extern struct passwd *getpwent(void); | 51 | struct passwd* FAST_FUNC getpwent(void); |
60 | |||
61 | /* Read an entry from STREAM. */ | ||
62 | extern struct passwd *fgetpwent(FILE *__stream); | ||
63 | |||
64 | /* Write the given entry onto the given stream. */ | ||
65 | extern 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. */ |
70 | extern struct passwd *getpwuid(uid_t __uid); | 54 | struct 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. */ |
73 | extern struct passwd *getpwnam(const char *__name); | 57 | struct 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 | |||
83 | extern int getpwent_r(struct passwd *__restrict __resultbuf, | ||
84 | char *__restrict __buffer, size_t __buflen, | ||
85 | struct passwd **__restrict __result); | ||
86 | |||
87 | extern int getpwuid_r(uid_t __uid, | ||
88 | struct passwd *__restrict __resultbuf, | ||
89 | char *__restrict __buffer, size_t __buflen, | ||
90 | struct passwd **__restrict __result); | ||
91 | |||
92 | extern 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. */ | 60 | int FAST_FUNC getpwnam_r(const char *__restrict __name, |
99 | extern 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 */ |
60 | extern void setspent(void); | 60 | void FAST_FUNC setspent(void); |
61 | 61 | ||
62 | /* Close database */ | 62 | /* Close database */ |
63 | extern void endspent(void); | 63 | void 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 */ |
66 | extern struct spwd *getspent(void); | 66 | struct spwd* FAST_FUNC getspent(void); |
67 | 67 | ||
68 | /* Get shadow entry matching NAME */ | 68 | /* Get shadow entry matching NAME */ |
69 | extern struct spwd *getspnam(const char *__name); | 69 | struct spwd* FAST_FUNC getspnam(const char *__name); |
70 | 70 | ||
71 | /* Read shadow entry from STRING */ | 71 | /* Read shadow entry from STRING */ |
72 | extern struct spwd *sgetspent(const char *__string); | 72 | struct spwd* FAST_FUNC sgetspent(const char *__string); |
73 | 73 | ||
74 | /* Read next shadow entry from STREAM */ | 74 | /* Read next shadow entry from STREAM */ |
75 | extern struct spwd *fgetspent(FILE *__stream); | 75 | struct 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 */ |
78 | extern int putspent(const struct spwd *__p, FILE *__stream); | 78 | int 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 */ |
81 | extern int getspent_r(struct spwd *__result_buf, char *__buffer, | 81 | int 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 | ||
85 | extern int getspnam_r(const char *__name, struct spwd *__result_buf, | 85 | int 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 |
90 | extern int sgetspent_r(const char *__string, struct spwd *__result_buf, | 90 | int 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 | ||
94 | extern int fgetspent_r(FILE *__stream, struct spwd *__result_buf, | 94 | int 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 */ |
98 | extern int lckpwdf(void); | 98 | int FAST_FUNC lckpwdf(void); |
99 | 99 | ||
100 | /* Unlock password file */ | 100 | /* Unlock password file */ |
101 | extern int ulckpwdf(void); | 101 | int FAST_FUNC ulckpwdf(void); |
102 | #endif | 102 | #endif |
103 | 103 | ||
104 | POP_SAVED_FUNCTION_VISIBILITY | 104 | POP_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 | ||
119 | static 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 | |||
129 | struct 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 | ||
8 | char* 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) */ |
10 | int FAST_FUNC index_in_str_array(const char *const string_array[], const char *key) | 28 | int FAST_FUNC index_in_str_array(const char *const string_array[], const char *key) |
@@ -39,10 +57,9 @@ int FAST_FUNC index_in_strings(const char *strings, const char *key) | |||
39 | int FAST_FUNC index_in_substr_array(const char *const string_array[], const char *key) | 57 | int 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 | ||
189 | char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric) | 183 | char* 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 | */ |
715 | static NOINLINE unsigned complete_username(const char *ud) | 715 | static 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 @@ | |||
17 | int FAST_FUNC match_fstype(const struct mntent *mt, const char *t_fstype) | 17 | int 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 | ||
34 | char* FAST_FUNC skip_dev_pfx(const char *tty_name) | 34 | char* 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 | */ | ||
143 | void 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 | /**********************************************************************/ | 35 | struct const_passdb { |
30 | /* Prototypes for internal functions. */ | 36 | const char *filename; |
31 | 37 | char def[7 + 2*ENABLE_USE_BB_SHADOW]; | |
32 | static 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, | 42 | struct 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 | ||
39 | static int FAST_FUNC bb__parsepwent(void *pw, char *line); | 57 | /* S = string not empty, s = string maybe empty, |
40 | static 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 | |||
65 | static 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 | }; | ||
78 | static 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 |
42 | static int FAST_FUNC bb__parsespent(void *sp, char *line); | 89 | static 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 | |||
48 | struct statics { | 107 | struct 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 | ||
70 | static struct statics *ptr_to_statics; | 118 | static 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 | ||
123 | static 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 | ||
72 | static struct statics *get_S(void) | 134 | static 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 | /**********************************************************************/ | 158 | static int tokenize(char *buffer, int ch) |
98 | |||
99 | int 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 | ||
115 | int 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 | { | 189 | static 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 | 237 | static char *parse_file(struct passdb *db, |
132 | #ifdef UNUSED_FOR_NOW | 238 | const char *key, int field_pos) |
133 | int 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 */ |
158 | struct passwd *fgetpwent(FILE *stream) | 251 | static 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 | ||
169 | struct 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') { |
183 | struct 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; |
196 | int 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 */ | ||
275 | struct 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 */ | 316 | static int massage_data_for_r_func(struct passdb *db, |
287 | struct 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. */ | ||
302 | int 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. | 339 | static void* massage_data_for_non_r_func(struct passdb *db, char *buf) |
321 | * Why it was added, I do not know. */ | ||
322 | struct 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 */ |
335 | struct passwd *getpwnam(const char *name) | 356 | |
357 | static 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 */ | 371 | int FAST_FUNC getpwnam_r(const char *name, struct passwd *struct_buf, |
347 | struct 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 | 383 | int FAST_FUNC getspnam_r(const char *name, struct spwd *struct_buf, char *buffer, size_t buflen, |
359 | struct 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 | ||
376 | static 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 | ||
384 | static FILE *pwf /*= NULL*/; | 394 | static int FAST_FUNC getXXent_r(uintptr_t db_idx, char *buffer, size_t buflen, |
385 | void 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 | ||
394 | void endpwent(void) | 414 | int 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 | ||
405 | int getpwent_r(struct passwd *__restrict resultbuf, | 424 | static 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 | ||
433 | static FILE *grf /*= NULL*/; | 441 | struct passwd* FAST_FUNC getpwent(void) |
434 | void setgrent(void) | ||
435 | { | 442 | { |
436 | LOCK; | 443 | return getXXent(0); |
437 | if (grf) { | ||
438 | rewind(grf); | ||
439 | } | ||
440 | UNLOCK; | ||
441 | } | 444 | } |
442 | 445 | ||
443 | void endgrent(void) | 446 | /****** getXXnam/id */ |
444 | { | ||
445 | LOCK; | ||
446 | if (grf) { | ||
447 | fclose(grf); | ||
448 | grf = NULL; | ||
449 | } | ||
450 | UNLOCK; | ||
451 | } | ||
452 | 447 | ||
453 | int getgrent_r(struct group *__restrict resultbuf, | 448 | static 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 | 457 | struct passwd* FAST_FUNC getpwnam(const char *name) |
482 | #if ENABLE_USE_BB_SHADOW | ||
483 | static FILE *spf /*= NULL*/; | ||
484 | void 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 | 461 | struct group* FAST_FUNC getgrnam(const char *name) | |
493 | void 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 | 465 | struct passwd* FAST_FUNC getpwuid(uid_t id) | |
503 | int 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 | 469 | struct group* FAST_FUNC getgrgid(gid_t id) |
530 | #endif /* UNUSED_FOR_NOW */ | ||
531 | |||
532 | #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS | ||
533 | struct 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 | ||
543 | struct 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); | 476 | void 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 | 483 | void FAST_FUNC setpwent(void) | |
553 | #if ENABLE_USE_BB_SHADOW | ||
554 | struct 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 | 489 | void FAST_FUNC endgrent(void) | |
564 | struct 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 | ||
576 | static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gid) | 497 | /****** initgroups and getgrouplist */ |
498 | |||
499 | static 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 | ||
610 | int initgroups(const char *user, gid_t gid) | 538 | int 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 | ||
620 | int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups) | 548 | int 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 | ||
636 | int 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 | |||
661 | int 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 | ||
708 | static 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 | |||
717 | int 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 | |||
760 | static 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 | |||
770 | static 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 | |||
818 | static 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 | |||
824 | static 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 | ||
918 | static 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 | |||
930 | static 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 | */ | ||
976 | static 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 | |||
24 | int 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 | ||
198 | static const struct op operators[] = { | 198 | static 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 */ | ||
224 | static void stack_machine(const char *argument) | 225 | static 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. */ | ||
93 | struct 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. */ | ||
101 | struct 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. */ | ||
112 | union 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 | */ | ||
174 | static ALWAYS_INLINE void *itoptr(int i) | ||
175 | { | ||
176 | return (void*)(intptr_t)i; | ||
177 | } | ||
178 | |||
179 | static 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 | |||
192 | static 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 | ||
205 | static 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 | |||
211 | static 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 | |||
224 | static 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 | ||
239 | static 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 | |||
250 | static 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 | |||
260 | static 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 | |||
275 | static 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 | */ | ||
296 | static 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 | |||
311 | static 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 | ||
334 | static 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 | |||
340 | static 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 | ||
346 | static 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 | |||
352 | static 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 | ||
361 | static 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 | */ | ||
374 | static 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 | |||
389 | static 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(). */ | ||
397 | static 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 | ||
404 | static 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 | ||
420 | static 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 | ||
462 | static 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 | |||
497 | static 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 | */ | ||
511 | static 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" | ||
546 | int i2cget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
547 | int 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" | ||
634 | int i2cset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
635 | int 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" | ||
833 | int i2cdump_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
834 | int 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 | ||
1068 | enum adapter_type { | ||
1069 | ADT_DUMMY = 0, | ||
1070 | ADT_ISA, | ||
1071 | ADT_I2C, | ||
1072 | ADT_SMBUS, | ||
1073 | }; | ||
1074 | |||
1075 | struct adap_desc { | ||
1076 | const char *funcs; | ||
1077 | const char *algo; | ||
1078 | }; | ||
1079 | |||
1080 | static 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 | |||
1091 | struct i2c_func | ||
1092 | { | ||
1093 | long value; | ||
1094 | const char* name; | ||
1095 | }; | ||
1096 | |||
1097 | static 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 | |||
1131 | static 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 | |||
1154 | static 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 | |||
1212 | found: | ||
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 | |||
1247 | static void NORETURN no_support(const char *cmd) | ||
1248 | { | ||
1249 | bb_error_msg_and_die("bus doesn't support %s", cmd); | ||
1250 | } | ||
1251 | |||
1252 | static 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" | ||
1272 | int i2cdetect_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
1273 | int 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 { | |||
42 | static void show_entry(struct utmp *ut, int state, time_t dur_secs) | 42 | static 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 { | |||
33 | static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARAM, | 33 | static 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 | ||
117 | static char* find_keyword(char *ptr, size_t len, const char *word) | 117 | static 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) | |||
149 | static char *filename2modname(const char *filename, char *modname) | 149 | static 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 | ||
166 | static 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 */ |
163 | static char* str_2_list(const char *str) | 176 | static 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 | ||
294 | static 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 | |||
306 | static FAST_FUNC int fileAction(const char *pathname, | 307 | static 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() |
536 | static int already_loaded(const char *name) | 537 | static 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 | ||
588 | static 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 | } |
232 | static 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 | */ | ||
240 | static 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 | ||
237 | static void add_probe(const char *name) | 245 | static 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 | ||
48 | char* FAST_FUNC filename2modname(const char *filename, char *modname) | 48 | char* 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 | ||
1160 | static llist_t *find_iface_state(llist_t *state_list, const char *iface) | 1160 | static 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 */ |
648 | static servtab_t *parse_one_line(void) | 648 | static 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 | } |
1660 | static void FAST_FUNC daytime_dg(int s, servtab_t *sep) | 1662 | static 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 | |||
35 | typedef struct { | ||
36 | uint32_t int_partl; | ||
37 | uint32_t fractionl; | ||
38 | } l_fixedpt_t; | ||
39 | |||
40 | typedef struct { | ||
41 | uint16_t int_parts; | ||
42 | uint16_t fractions; | ||
43 | } s_fixedpt_t; | ||
44 | |||
45 | enum { | ||
46 | NTP_DIGESTSIZE = 16, | ||
47 | NTP_MSGSIZE_NOAUTH = 48, | ||
48 | NTP_MSGSIZE = (NTP_MSGSIZE_NOAUTH + 4 + NTP_DIGESTSIZE), | ||
49 | }; | ||
50 | |||
51 | typedef 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 | |||
67 | enum { | ||
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 | |||
96 | typedef 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 | ||
108 | typedef 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 | |||
123 | enum { | ||
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 | |||
135 | struct 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 | |||
159 | static const int const_IPTOS_LOWDELAY = IPTOS_LOWDELAY; | ||
160 | |||
161 | |||
162 | static void | ||
163 | set_next(peer_t *p, unsigned t) | ||
164 | { | ||
165 | p->next_action_time = time(NULL) + t; | ||
166 | } | ||
167 | |||
168 | static void | ||
169 | add_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 | |||
185 | static double | ||
186 | gettime1900d(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 | |||
193 | static void | ||
194 | d_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 | |||
200 | static double | ||
201 | lfp_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 | ||
211 | static double | ||
212 | sfp_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 | ||
223 | static l_fixedpt_t | ||
224 | d_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 | |||
234 | static s_fixedpt_t | ||
235 | d_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 | |||
246 | static unsigned | ||
247 | error_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 | |||
255 | static int | ||
256 | do_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 | |||
275 | static int | ||
276 | send_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 | */ | ||
352 | static void | ||
353 | step_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 | */ | ||
396 | static int | ||
397 | compare_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 | } | ||
405 | static unsigned | ||
406 | updated_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 | } | ||
416 | static void | ||
417 | slew_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 | |||
498 | static void | ||
499 | update_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 | |||
529 | static unsigned | ||
530 | scale_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 | } | ||
537 | static void | ||
538 | recv_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 | ||
671 | static void | ||
672 | recv_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 | */ | ||
823 | static 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 | |||
892 | int ntpd_main(int argc UNUSED_PARAM, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
893 | int 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 | ||
131 | static const int flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */ | 132 | static 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 | ||
452 | static const unsigned flagvals[] = { /* Must agree with flagchars[]. */ | 453 | static const |
454 | IF_NOT_FEATURE_IPV6(uint16_t) | ||
455 | IF_FEATURE_IPV6(unsigned) | ||
456 | flagvals[] = { /* 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[]. */ |
469 | static const char flagchars[] ALIGN1 = | 472 | static 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 | ||
476 | static void set_flags(char *flagstr, int flags) | 481 | static 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"; | |||
55 | static const char P_HTTP[] = "http"; | 61 | static const char P_HTTP[] = "http"; |
56 | static const char P_HTTPS[] = "https"; | 62 | static 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. */ | ||
66 | enum { | ||
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 | }; | ||
73 | static 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 */ |
60 | struct globals { | 96 | struct 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 | ||
47 | static 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 | ||
48 | int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 64 | int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
49 | int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM)) | 65 | int 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 | ||
679 | int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 679 | int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
680 | int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv) | 680 | int 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); | |||
10981 | static union node *pipeline(void); | 10981 | static union node *pipeline(void); |
10982 | static union node *parse_command(void); | 10982 | static union node *parse_command(void); |
10983 | static void parseheredoc(void); | 10983 | static void parseheredoc(void); |
10984 | static char peektoken(void); | 10984 | static char nexttoken_ends_list(void); |
10985 | static int readtoken(void); | 10985 | static int readtoken(void); |
10986 | 10986 | ||
10987 | static union node * | 10987 | static 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 | ||
12473 | static char | 12480 | static char |
12474 | peektoken(void) | 12481 | nexttoken_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 | |||
9 | testing "dc basic syntax (stdin, multiple args)" \ | ||
10 | "dc" \ | ||
11 | "30\n" \ | ||
12 | "" "10 20+p" | ||
13 | |||
14 | testing "dc basic syntax (argv, single arg)" \ | ||
15 | "dc '10 20+p'" \ | ||
16 | "30\n" \ | ||
17 | "" "" | ||
18 | |||
19 | testing "dc basic syntax (argv, multiple args)" \ | ||
20 | "dc 10 20+p" \ | ||
21 | "30\n" \ | ||
22 | "" "" | ||
23 | |||
24 | testing "dc complex with spaces (single arg)" \ | ||
25 | "dc '8 8 * 2 2 + / p'" \ | ||
26 | "16\n" \ | ||
27 | "" "" | ||
28 | |||
29 | testing "dc complex without spaces (single arg)" \ | ||
30 | "dc '8 8*2 2+/p'" \ | ||
31 | "16\n" \ | ||
32 | "" "" | ||
33 | |||
34 | testing "dc complex with spaces (multiple args)" \ | ||
35 | "dc 8 8 \* 2 2 + / p" \ | ||
36 | "16\n" \ | ||
37 | "" "" | ||
38 | |||
39 | testing "dc complex without spaces (multiple args)" \ | ||
40 | "dc 8 8\*2 2+/p" \ | ||
41 | "16\n" \ | ||
42 | "" "" | ||
43 | |||
44 | exit $FAILCOUNT | ||
45 | |||
46 | # we do not support arguments | ||
47 | testing "dc -e <exprs>" \ | ||
48 | "dc -e '10 2+f'" \ | ||
49 | "12\n" \ | ||
50 | "" "" | ||
51 | |||
52 | testing "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 | ||
47 | testing "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 | |||
47 | testing "diff of empty file against nonempty one" \ | 58 | testing "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 | ||
437 | config 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 | |||
449 | config 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 | |||
456 | config 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 | |||
465 | config 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 | |||
477 | config 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 | |||
487 | config 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 | |||
501 | config 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 | |||
508 | config 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 | |||
518 | config 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 | |||
525 | config PIVOT_ROOT | 437 | config 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:") |
171 | enum { | 269 | enum { |
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 | ||
72 | static NOINLINE void setup_alarm(int fd, time_t *wakeup, time_t rtc_time) | 72 | static 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 */ |
78 | enum { | 80 | enum { |
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 | ||
91 | static int swap_enable_disable(char *device) | 96 | static 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) |