From 2b6282117f026cfa2a3e7efee7caa054b6265264 Mon Sep 17 00:00:00 2001 From: Thomas De Schampheleire Date: Tue, 26 Mar 2019 13:10:21 +0100 Subject: top: provide cmdline argument '-H' to enable thread scanning by default In particular useful when you want to evaluate the threads in batch mode: top -Hbn1 function old new delta top_main 928 941 +13 packed_usage 33317 33319 +2 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 15/0) Total: 15 bytes Signed-off-by: Philippe Belet Signed-off-by: Thomas De Schampheleire Signed-off-by: Denys Vlasenko --- procps/top.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/procps/top.c b/procps/top.c index 625409755..89f9d23f4 100644 --- a/procps/top.c +++ b/procps/top.c @@ -222,8 +222,9 @@ enum { OPT_d = (1 << 0), OPT_n = (1 << 1), OPT_b = (1 << 2), - OPT_m = (1 << 3), - OPT_EOF = (1 << 4), /* pseudo: "we saw EOF in stdin" */ + OPT_H = (1 << 3), + OPT_m = (1 << 4), + OPT_EOF = (1 << 5), /* pseudo: "we saw EOF in stdin" */ }; #define OPT_BATCH_MODE (option_mask32 & OPT_b) @@ -1043,7 +1044,8 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval) //usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...) //usage:#endif //usage:#define top_trivial_usage -//usage: "[-b"IF_FEATURE_TOPMEM("m")"] [-n COUNT] [-d SECONDS]" +//usage: "[-b"IF_FEATURE_TOPMEM("m")IF_FEATURE_SHOW_THREADS("H")"]" +//usage: " [-n COUNT] [-d SECONDS]" //usage:#define top_full_usage "\n\n" //usage: "Provide a view of process activity in real time." //usage: "\n""Read the status of all processes from /proc each SECONDS" @@ -1076,6 +1078,9 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval) //usage: IF_FEATURE_TOPMEM( //usage: "\n"" -m Same as 's' key" //usage: ) +//usage: IF_FEATURE_SHOW_THREADS( +//usage: "\n"" -H Show threads" +//usage: ) /* Interactive testing: * echo sss | ./busybox top @@ -1110,7 +1115,8 @@ int top_main(int argc UNUSED_PARAM, char **argv) /* all args are options; -n NUM */ make_all_argv_opts(argv); /* options can be specified w/o dash */ - col = getopt32(argv, "d:n:b"IF_FEATURE_TOPMEM("m"), &str_interval, &str_iterations); + col = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations); + /* NB: -m and -H are accepted even if not configured */ #if ENABLE_FEATURE_TOPMEM if (col & OPT_m) /* -m (busybox specific) */ scan_mask = TOPMEM_MASK; @@ -1129,6 +1135,11 @@ int top_main(int argc UNUSED_PARAM, char **argv) str_iterations++; iterations = xatou(str_iterations); } +#if ENABLE_FEATURE_SHOW_THREADS + if (col & OPT_H) { + scan_mask |= PSSCAN_TASKS; + } +#endif /* change to /proc */ xchdir("/proc"); -- cgit v1.2.3-55-g6feb From 93594b1197cf3ae1835eedebbebb2b40ea2a81f7 Mon Sep 17 00:00:00 2001 From: Martin Lewis Date: Thu, 4 Apr 2019 13:29:32 +0200 Subject: telnetd: Added support for AYT IAC command. Fixed a TODO in AYT IAC handling by replying back with a NOP. Signed-off-by: Martin Lewis Signed-off-by: Denys Vlasenko --- networking/telnetd.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/networking/telnetd.c b/networking/telnetd.c index caef15181..bd60c8681 100644 --- a/networking/telnetd.c +++ b/networking/telnetd.c @@ -249,7 +249,7 @@ safe_write_to_pty_decode_iac(struct tsession *ts) * IAC SE (240) End of subnegotiation. Treated as NOP. * IAC NOP (241) NOP. Supported. * IAC BRK (243) Break. Like serial line break. TODO via tcsendbreak()? - * IAC AYT (246) Are you there. Send back evidence that AYT was seen. TODO (send NOP back)? + * IAC AYT (246) Are you there. * These don't look useful: * IAC DM (242) Data mark. What is this? * IAC IP (244) Suspend, interrupt or abort the process. (Ancient cousin of ^C). @@ -277,6 +277,13 @@ safe_write_to_pty_decode_iac(struct tsession *ts) rc = 2; goto update_and_return; } + if (buf[1] == AYT) { + /* Send back evidence that AYT was seen. */ + buf[1] = NOP; + /*rc =*/ safe_write(ts->sockfd_write, buf, 2); + rc = 2; + goto update_and_return; + } if (buf[1] >= 240 && buf[1] <= 249) { /* NOP (241). Ignore (putty keepalive, etc) */ /* All other 2-byte commands also treated as NOPs here */ -- cgit v1.2.3-55-g6feb From c6a8965297f96f63403a1f17606838a0cb4444c5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 4 Apr 2019 16:00:23 +0200 Subject: telnetd: better AYT handling function old new delta telnetd_main 1792 1837 +45 Signed-off-by: Denys Vlasenko --- networking/telnetd.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/networking/telnetd.c b/networking/telnetd.c index bd60c8681..e94d3bd3d 100644 --- a/networking/telnetd.c +++ b/networking/telnetd.c @@ -278,9 +278,14 @@ safe_write_to_pty_decode_iac(struct tsession *ts) goto update_and_return; } if (buf[1] == AYT) { - /* Send back evidence that AYT was seen. */ - buf[1] = NOP; - /*rc =*/ safe_write(ts->sockfd_write, buf, 2); + if (ts->size2 == 0) { /* if nothing buffered yet... */ + /* Send back evidence that AYT was seen */ + unsigned char *buf2 = TS_BUF2(ts); + buf2[0] = IAC; + buf2[1] = NOP; + ts->wridx2 = 0; + ts->rdidx2 = ts->size2 = 2; + } rc = 2; goto update_and_return; } -- cgit v1.2.3-55-g6feb From 29c2dcfe1c934f75c87f5a0bc3bf42b2e73f294c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 4 Apr 2019 16:54:14 +0200 Subject: telnetd: whitespace fix Signed-off-by: Denys Vlasenko --- networking/telnetd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/networking/telnetd.c b/networking/telnetd.c index e94d3bd3d..6abecbde2 100644 --- a/networking/telnetd.c +++ b/networking/telnetd.c @@ -285,7 +285,7 @@ safe_write_to_pty_decode_iac(struct tsession *ts) buf2[1] = NOP; ts->wridx2 = 0; ts->rdidx2 = ts->size2 = 2; - } + } rc = 2; goto update_and_return; } -- cgit v1.2.3-55-g6feb From ae5ca6fc417161eb514103d3c2b38add18012760 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 5 Apr 2019 12:03:48 +0200 Subject: chrt: do not segfault if policy number is unknown While at it, improve help text function old new delta packed_usage 33319 33344 +25 policy_name - 22 +22 show_min_max 59 64 +5 chrt_main 432 429 -3 policies 72 - -72 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 2/1 up/down: 52/-75) Total: -23 bytes text data bss dec hex filename 982150 485 7296 989931 f1aeb busybox_old 982183 485 7296 989964 f1b0c busybox_unstripped Signed-off-by: Denys Vlasenko --- util-linux/chrt.c | 62 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/util-linux/chrt.c b/util-linux/chrt.c index 28e65457c..ede92310f 100644 --- a/util-linux/chrt.c +++ b/util-linux/chrt.c @@ -17,16 +17,16 @@ //kbuild:lib-$(CONFIG_CHRT) += chrt.o //usage:#define chrt_trivial_usage -//usage: "[-prfombi] [PRIO] [PID | PROG ARGS]" +//usage: "-m | -p [PRIO] PID | [-rfobi] PRIO PROG [ARGS]" //usage:#define chrt_full_usage "\n\n" //usage: "Change scheduling priority and class for a process\n" +//usage: "\n -m Show min/max priorities" //usage: "\n -p Operate on PID" //usage: "\n -r Set SCHED_RR class" //usage: "\n -f Set SCHED_FIFO class" //usage: "\n -o Set SCHED_OTHER class" //usage: "\n -b Set SCHED_BATCH class" //usage: "\n -i Set SCHED_IDLE class" -//usage: "\n -m Show min/max priorities" //usage: //usage:#define chrt_example_usage //usage: "$ chrt -r 4 sleep 900; x=$!\n" @@ -39,28 +39,32 @@ # define SCHED_IDLE 5 #endif -static const struct { - char name[sizeof("SCHED_OTHER")]; -} policies[] = { - { "SCHED_OTHER" }, /* 0:SCHED_OTHER */ - { "SCHED_FIFO" }, /* 1:SCHED_FIFO */ - { "SCHED_RR" }, /* 2:SCHED_RR */ - { "SCHED_BATCH" }, /* 3:SCHED_BATCH */ - { "" }, /* 4:SCHED_ISO */ - { "SCHED_IDLE" }, /* 5:SCHED_IDLE */ - /* 6:SCHED_DEADLINE */ -}; +static const char *policy_name(int pol) +{ + if (pol > 6) + return utoa(pol); + return nth_string( + "OTHER" "\0" /* 0:SCHED_OTHER */ + "FIFO" "\0" /* 1:SCHED_FIFO */ + "RR" "\0" /* 2:SCHED_RR */ + "BATCH" "\0" /* 3:SCHED_BATCH */ + "ISO" "\0" /* 4:SCHED_ISO */ + "IDLE" "\0" /* 5:SCHED_IDLE */ + "DEADLINE", /* 6:SCHED_DEADLINE */ + pol + ); +} static void show_min_max(int pol) { - const char *fmt = "%s min/max priority\t: %u/%u\n"; + const char *fmt = "SCHED_%s min/max priority\t: %u/%u\n"; int max, min; max = sched_get_priority_max(pol); min = sched_get_priority_min(pol); if ((max|min) < 0) - fmt = "%s not supported\n"; - printf(fmt, policies[pol].name, min, max); + fmt = "SCHED_%s not supported\n"; + printf(fmt, policy_name(pol), min, max); } #define OPT_m (1<<0) @@ -112,11 +116,11 @@ int chrt_main(int argc UNUSED_PARAM, char **argv) bb_show_usage(); if (opt & OPT_p) { pid_str = *argv++; - if (*argv) { /* "-p [...]" */ + if (*argv) { /* "-p PRIO PID [...]" */ priority = pid_str; pid_str = *argv; } - /* else "-p ", and *argv == NULL */ + /* else "-p PID", and *argv == NULL */ pid = xatoul_range(pid_str, 1, ((unsigned)(pid_t)ULONG_MAX) >> 1); } else { priority = *argv++; @@ -130,16 +134,18 @@ int chrt_main(int argc UNUSED_PARAM, char **argv) print_rt_info: pol = sched_getscheduler(pid); if (pol < 0) - bb_perror_msg_and_die("can't %cet pid %d's policy", 'g', (int)pid); - printf("pid %d's %s scheduling policy: %s\n", - pid, current_new, policies[pol].name); + bb_perror_msg_and_die("can't %cet pid %u's policy", 'g', (int)pid); + printf("pid %u's %s scheduling policy: SCHED_%s\n", + pid, current_new, policy_name(pol) + ); if (sched_getparam(pid, &sp)) - bb_perror_msg_and_die("can't get pid %d's attributes", (int)pid); - printf("pid %d's %s scheduling priority: %d\n", - (int)pid, current_new, sp.sched_priority); + bb_perror_msg_and_die("can't get pid %u's attributes", (int)pid); + printf("pid %u's %s scheduling priority: %d\n", + (int)pid, current_new, sp.sched_priority + ); if (!*argv) { - /* Either it was just "-p ", - * or it was "-p " and we came here + /* Either it was just "-p PID", + * or it was "-p PRIO PID" and we came here * for the second time (see goto below) */ return EXIT_SUCCESS; } @@ -152,9 +158,9 @@ int chrt_main(int argc UNUSED_PARAM, char **argv) ); if (sched_setscheduler(pid, policy, &sp) < 0) - bb_perror_msg_and_die("can't %cet pid %d's policy", 's', (int)pid); + bb_perror_msg_and_die("can't %cet pid %u's policy", 's', (int)pid); - if (!argv[0]) /* "-p [...]" */ + if (!argv[0]) /* "-p PRIO PID [...]" */ goto print_rt_info; BB_EXECVP_or_die(argv); -- cgit v1.2.3-55-g6feb From 43d09e79db91110e7ca09610efd0b62fce453b47 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 5 Apr 2019 16:59:07 +0200 Subject: chrt: fix for SCHED_RESET_ON_FORK bit Signed-off-by: Denys Vlasenko --- util-linux/chrt.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/util-linux/chrt.c b/util-linux/chrt.c index ede92310f..4dd78dabf 100644 --- a/util-linux/chrt.c +++ b/util-linux/chrt.c @@ -135,6 +135,17 @@ int chrt_main(int argc UNUSED_PARAM, char **argv) pol = sched_getscheduler(pid); if (pol < 0) bb_perror_msg_and_die("can't %cet pid %u's policy", 'g', (int)pid); +#ifdef SCHED_RESET_ON_FORK + /* "Since Linux 2.6.32, the SCHED_RESET_ON_FORK flag + * can be ORed in policy when calling sched_setscheduler(). + * As a result of including this flag, children created by + * fork(2) do not inherit privileged scheduling policies" + * + * This bit is also returned by sched_getscheduler()! + * (TODO: do we want to show it?) + */ + pol &= ~SCHED_RESET_ON_FORK; +#endif printf("pid %u's %s scheduling policy: SCHED_%s\n", pid, current_new, policy_name(pol) ); -- cgit v1.2.3-55-g6feb From 1e474d3d933bfe56a7f4eab17cf58cc6dd6a881d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 5 Apr 2019 18:38:12 +0200 Subject: service examples: if iface do not exist, retry upping it I've had a case of a machine where eth0 was appearing a bit later after the boot, and appearing _downed_. ifplugd then fails to detect "link up". Thus, depending on how service startup ("ip link set dev eth0 up") races with driver initialization, ethernet randomly fails to initialize on boot. Signed-off-by: Denys Vlasenko --- examples/var_service/dhcp_if/run | 4 ++-- examples/var_service/dhcpd_if/run | 4 ++-- examples/var_service/ifplugd_if/run | 2 +- examples/var_service/supplicant_if/run | 2 +- examples/var_service/zcip_if/run | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/var_service/dhcp_if/run b/examples/var_service/dhcp_if/run index aec79e027..d8f343586 100755 --- a/examples/var_service/dhcp_if/run +++ b/examples/var_service/dhcp_if/run @@ -8,9 +8,9 @@ pwd="$PWD" if="${PWD##*/dhcp_}" echo "* Upping iface $if" -ip link set dev "$if" up +ip link set dev "$if" up || { sleep 5; exit; } -echo "* Starting udhcpc" +echo "* Starting udhcpc on $if [$$]" exec \ env - PATH="$PATH" \ softlimit \ diff --git a/examples/var_service/dhcpd_if/run b/examples/var_service/dhcpd_if/run index a603bdc71..e3d1b00f4 100755 --- a/examples/var_service/dhcpd_if/run +++ b/examples/var_service/dhcpd_if/run @@ -8,12 +8,12 @@ pwd="$PWD" if="${PWD##*/dhcpd_}" echo "* Upping iface $if" -ip link set dev $if up +ip link set dev $if up || { sleep 5; exit; } >>udhcpd.leases sed 's/^interface.*$/interface '"$if/" -i udhcpd.conf -echo "* Starting udhcpd" +echo "* Starting udhcpd on $if [$$]" exec \ env - PATH="$PATH" \ softlimit \ diff --git a/examples/var_service/ifplugd_if/run b/examples/var_service/ifplugd_if/run index 5c662f298..e7816619d 100755 --- a/examples/var_service/ifplugd_if/run +++ b/examples/var_service/ifplugd_if/run @@ -9,7 +9,7 @@ pwd="$PWD" if="${PWD##*/ifplugd_}" echo "* Upping iface $if" -ip link set dev "$if" up +ip link set dev "$if" up || { sleep 5; exit; } echo "* Starting ifplugd on $if [$$]" exec \ diff --git a/examples/var_service/supplicant_if/run b/examples/var_service/supplicant_if/run index 279d18af5..bc16fb606 100755 --- a/examples/var_service/supplicant_if/run +++ b/examples/var_service/supplicant_if/run @@ -14,7 +14,7 @@ ip link set dev "$if" up || { sleep 5; exit; } ##echo "* Powersave disable on $if" ##iw dev "$if" set power_save off -echo "* Starting wpa_supplicant" +echo "* Starting wpa_supplicant on $if [$$]" exec \ env - PATH="$PATH" \ softlimit \ diff --git a/examples/var_service/zcip_if/run b/examples/var_service/zcip_if/run index 94a875465..699823246 100755 --- a/examples/var_service/zcip_if/run +++ b/examples/var_service/zcip_if/run @@ -8,9 +8,9 @@ pwd="$PWD" if="${PWD##*/zcip_}" echo "* Upping iface $if" -ip link set dev "$if" up +ip link set dev "$if" up || { sleep 5; exit; } -echo "* Starting zcip" +echo "* Starting zcip on $if [$$]" exec \ env - PATH="$PATH" \ softlimit \ -- cgit v1.2.3-55-g6feb From c6c19c31c127d7d45d5e3f664eb85e7f621eb817 Mon Sep 17 00:00:00 2001 From: Bernhard Reutner-Fischer Date: Sun, 7 Apr 2019 18:09:37 +0200 Subject: ipaddress: remove unused variable no_link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ipaddress.c: In function ‘ipaddr_list_or_flush’: ipaddress.c:427:6: warning: variable ‘no_link’ set but not used [-Wunused-but-set-variable] Signed-off-by: Bernhard Reutner-Fischer --- networking/libiproute/ipaddress.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/networking/libiproute/ipaddress.c b/networking/libiproute/ipaddress.c index 0a1b5d798..8364f6a3e 100644 --- a/networking/libiproute/ipaddress.c +++ b/networking/libiproute/ipaddress.c @@ -424,7 +424,6 @@ int FAST_FUNC ipaddr_list_or_flush(char **argv, int flush) struct nlmsg_list *l; struct rtnl_handle rth; char *filter_dev = NULL; - int no_link = 0; ipaddr_reset_filter(oneline); G_filter.showqueue = 1; @@ -516,9 +515,6 @@ int FAST_FUNC ipaddr_list_or_flush(char **argv, int flush) struct nlmsg_list **lp; lp = &linfo; - if (G_filter.oneline) - no_link = 1; - while ((l = *lp) != NULL) { int ok = 0; struct ifinfomsg *ifi = NLMSG_DATA(&l->h); -- cgit v1.2.3-55-g6feb From e2026381bed88e79b6f7657eef8319e60ff83041 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Mon, 18 Mar 2019 11:14:52 +0000 Subject: stat: reduce storage for human-readable filesystem names function old new delta static.humanname - 236 +236 static.fstype - 140 +140 print_statfs 339 341 +2 static.humantypes 288 - -288 ------------------------------------------------------------------------------ (add/remove: 2/1 grow/shrink: 1/0 up/down: 378/-288) Total: 90 bytes text data bss dec hex filename 982183 485 7296 989964 f1b0c busybox_old 982152 485 7296 989933 f1aed busybox_unstripped Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- coreutils/stat.c | 92 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/coreutils/stat.c b/coreutils/stat.c index 122029bda..b6ab5205b 100644 --- a/coreutils/stat.c +++ b/coreutils/stat.c @@ -169,6 +169,42 @@ static const char *human_time(time_t t) } #if ENABLE_FEATURE_STAT_FILESYSTEM +#define FS_TYPE_LIST \ +FS_TYPE(0xADFF, "affs") \ +FS_TYPE(0x1CD1, "devpts") \ +FS_TYPE(0x137D, "ext") \ +FS_TYPE(0xEF51, "ext2") \ +FS_TYPE(0xEF53, "ext2/ext3") \ +FS_TYPE(0x3153464a, "jfs") \ +FS_TYPE(0x58465342, "xfs") \ +FS_TYPE(0xF995E849, "hpfs") \ +FS_TYPE(0x9660, "isofs") \ +FS_TYPE(0x4000, "isofs") \ +FS_TYPE(0x4004, "isofs") \ +FS_TYPE(0x137F, "minix") \ +FS_TYPE(0x138F, "minix (30 char.)") \ +FS_TYPE(0x2468, "minix v2") \ +FS_TYPE(0x2478, "minix v2 (30 char.)") \ +FS_TYPE(0x4d44, "msdos") \ +FS_TYPE(0x4006, "fat") \ +FS_TYPE(0x564c, "novell") \ +FS_TYPE(0x6969, "nfs") \ +FS_TYPE(0x9fa0, "proc") \ +FS_TYPE(0x517B, "smb") \ +FS_TYPE(0x012FF7B4, "xenix") \ +FS_TYPE(0x012FF7B5, "sysv4") \ +FS_TYPE(0x012FF7B6, "sysv2") \ +FS_TYPE(0x012FF7B7, "coh") \ +FS_TYPE(0x00011954, "ufs") \ +FS_TYPE(0x012FD16D, "xia") \ +FS_TYPE(0x5346544e, "ntfs") \ +FS_TYPE(0x1021994, "tmpfs") \ +FS_TYPE(0x52654973, "reiserfs") \ +FS_TYPE(0x28cd3d45, "cramfs") \ +FS_TYPE(0x7275, "romfs") \ +FS_TYPE(0x858458f6, "ramfs") \ +FS_TYPE(0x73717368, "squashfs") \ +FS_TYPE(0x62656572, "sysfs") /* Return the type of the specified file system. * Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris) * Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2) @@ -176,54 +212,22 @@ static const char *human_time(time_t t) */ static const char *human_fstype(uint32_t f_type) { - static const struct types { - uint32_t type; - const char *const fs; - } humantypes[] = { - { 0xADFF, "affs" }, - { 0x1Cd1, "devpts" }, - { 0x137D, "ext" }, - { 0xEF51, "ext2" }, - { 0xEF53, "ext2/ext3" }, - { 0x3153464a, "jfs" }, - { 0x58465342, "xfs" }, - { 0xF995E849, "hpfs" }, - { 0x9660, "isofs" }, - { 0x4000, "isofs" }, - { 0x4004, "isofs" }, - { 0x137F, "minix" }, - { 0x138F, "minix (30 char.)" }, - { 0x2468, "minix v2" }, - { 0x2478, "minix v2 (30 char.)" }, - { 0x4d44, "msdos" }, - { 0x4006, "fat" }, - { 0x564c, "novell" }, - { 0x6969, "nfs" }, - { 0x9fa0, "proc" }, - { 0x517B, "smb" }, - { 0x012FF7B4, "xenix" }, - { 0x012FF7B5, "sysv4" }, - { 0x012FF7B6, "sysv2" }, - { 0x012FF7B7, "coh" }, - { 0x00011954, "ufs" }, - { 0x012FD16D, "xia" }, - { 0x5346544e, "ntfs" }, - { 0x1021994, "tmpfs" }, - { 0x52654973, "reiserfs" }, - { 0x28cd3d45, "cramfs" }, - { 0x7275, "romfs" }, - { 0x858458f6, "ramfs" }, - { 0x73717368, "squashfs" }, - { 0x62656572, "sysfs" }, - { 0, "UNKNOWN" } +# define FS_TYPE(type, name) type, + static const uint32_t fstype[] = { + FS_TYPE_LIST }; - +# undef FS_TYPE +# define FS_TYPE(type, name) name"\0" + static const char humanname[] ALIGN1 = + FS_TYPE_LIST + "UNKNOWN"; +# undef FS_TYPE int i; - for (i = 0; humantypes[i].type; ++i) - if (humantypes[i].type == f_type) + for (i = 0; i < ARRAY_SIZE(fstype); ++i) + if (fstype[i] == f_type) break; - return humantypes[i].fs; + return nth_string(humanname, i); } /* "man statfs" says that statfsbuf->f_fsid is a mess */ -- cgit v1.2.3-55-g6feb From c5150e9ce7359db136ae9337b2f007c6f7ec3113 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 12 Apr 2019 18:52:31 +0200 Subject: brctl: make "show" command retrieve data from /sys ioctl interface is obsolete and has no 32/64 compat shim, making "brctl show" fail for 32-bit userspace and 64-bit kernel. function old new delta show_bridge - 310 +310 read_file - 64 +64 if_indextoname 117 - -117 brctl_main 1183 885 -298 ------------------------------------------------------------------------------ (add/remove: 2/1 grow/shrink: 0/1 up/down: 374/-415) Total: -41 bytes Signed-off-by: Denys Vlasenko --- networking/brctl.c | 163 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 114 insertions(+), 49 deletions(-) diff --git a/networking/brctl.c b/networking/brctl.c index ba4a714f8..706ecfc07 100644 --- a/networking/brctl.c +++ b/networking/brctl.c @@ -67,6 +67,7 @@ //usage: ) #include "libbb.h" +#include "common_bufsiz.h" #include #include @@ -198,6 +199,69 @@ static void arm_ioctl(unsigned long *args, } #endif +#define filedata bb_common_bufsiz1 +static int read_file(const char *name) +{ + int n = open_read_close(name, filedata, COMMON_BUFSIZE - 1); + if (n < 0) { + filedata[0] = '\0'; + } else { + filedata[n] = '\0'; + if (n != 0 && filedata[n - 1] == '\n') + filedata[--n] = '\0'; + } + return n; +} + +/* NB: we are in /sys/class/net + */ +static int show_bridge(const char *name, int need_hdr) +{ +// Output: +//bridge name bridge id STP enabled interfaces +//br0 8000.000000000000 no eth0 + char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32]; + int tabs; + DIR *ifaces; + struct dirent *ent; + char *sfx; + + sfx = pathbuf + sprintf(pathbuf, "%s/bridge/", name); + strcpy(sfx, "bridge_id"); + if (read_file(pathbuf) < 0) + return -1; /* this iface is not a bridge */ + + if (need_hdr) + puts("bridge name\tbridge id\t\tSTP enabled\tinterfaces"); + printf("%s\t\t", name); + printf("%s\t", filedata); + + strcpy(sfx, "stp_state"); + read_file(pathbuf); + if (LONE_CHAR(filedata, '0')) + strcpy(filedata, "no"); + else + if (LONE_CHAR(filedata, '1')) + strcpy(filedata, "yes"); + printf(filedata); + + strcpy(sfx, "brif"); + tabs = 0; + ifaces = opendir(pathbuf); + if (ifaces) { + while ((ent = readdir(ifaces)) != NULL) { + if (tabs) + printf("\t\t\t\t\t"); + else + tabs = 1; + printf("\t\t%s\n", ent->d_name); + } + closedir(ifaces); + } + if (!tabs) /* bridge has no interfaces */ + bb_putchar('\n'); + return 0; +} int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int brctl_main(int argc UNUSED_PARAM, char **argv) @@ -226,6 +290,13 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) char *br, *brif; argv++; + if (!*argv) { + /* bare "brctl" shows --help */ + bb_show_usage(); + } + + xchdir("/sys/class/net"); + while (*argv) { #if ENABLE_FEATURE_BRCTL_FANCY int ifidx[MAX_PORTS]; @@ -237,68 +308,50 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) if (key == -1) /* no match found in keywords array, bail out. */ bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name); argv++; - fd = xsocket(AF_INET, SOCK_STREAM, 0); #if ENABLE_FEATURE_BRCTL_SHOW if (key == ARG_show) { /* show */ - char buf[IFNAMSIZ]; - int bridx[MAX_PORTS]; - int i, num; - arm_ioctl(args, BRCTL_GET_BRIDGES, - (unsigned long) bridx, MAX_PORTS); - num = xioctl(fd, SIOCGIFBR, args); - puts("bridge name\tbridge id\t\tSTP enabled\tinterfaces"); - for (i = 0; i < num; i++) { - int j, tabs; - struct __bridge_info bi; - unsigned char *x; - - if (!if_indextoname(bridx[i], buf)) - bb_perror_msg_and_die("can't get bridge name for index %d", i); - strncpy_IFNAMSIZ(ifr.ifr_name, buf); - - arm_ioctl(args, BRCTL_GET_BRIDGE_INFO, - (unsigned long) &bi, 0); - xioctl(fd, SIOCDEVPRIVATE, &ifr); - printf("%s\t\t", buf); - - /* print bridge id */ - x = (unsigned char *) &bi.bridge_id; - for (j = 0; j < 8; j++) { - printf("%02x", x[j]); - if (j == 1) - bb_putchar('.'); - } - printf(bi.stp_enabled ? "\tyes" : "\tno"); + DIR *net; + struct dirent *ent; + int need_hdr = 1; + int exitcode = EXIT_SUCCESS; + + if (*argv) { + /* "brctl show BR1 BR2 BR3" */ + do { + if (show_bridge(*argv, need_hdr) >= 0) { + need_hdr = 0; + } else { + bb_error_msg("bridge %s does not exist", *argv); +//TODO: if device exists, but is not a BR, brctl from bridge-utils 1.6 says this instead: +// "device eth0 is not a bridge" + exitcode = EXIT_FAILURE; + } + } while (*++argv != NULL); + return exitcode; + } - /* print interface list */ - arm_ioctl(args, BRCTL_GET_PORT_LIST, - (unsigned long) ifidx, MAX_PORTS); - xioctl(fd, SIOCDEVPRIVATE, &ifr); - tabs = 0; - for (j = 0; j < MAX_PORTS; j++) { - if (!ifidx[j]) - continue; - if (!if_indextoname(ifidx[j], buf)) - bb_perror_msg_and_die("can't get interface name for index %d", j); - if (tabs) - printf("\t\t\t\t\t"); - else - tabs = 1; - printf("\t\t%s\n", buf); - } - if (!tabs) /* bridge has no interfaces */ - bb_putchar('\n'); + /* "brctl show" (if no ifaces, shows nothing, not even header) */ + net = xopendir("."); + while ((ent = readdir(net)) != NULL) { + if (DOT_OR_DOTDOT(ent->d_name)) + continue; /* . or .. */ + if (show_bridge(ent->d_name, need_hdr) >= 0) + need_hdr = 0; } - goto done; + closedir(net); + return exitcode; } #endif if (!*argv) /* all but 'show' need at least one argument */ bb_show_usage(); + fd = xsocket(AF_INET, SOCK_STREAM, 0); br = *argv++; +//brctl from bridge-utils 1.6 also still uses ioctl +//for SIOCBRADDBR / SIOCBRDELBR, not /sys accesses if (key == ARG_addbr || key == ARG_delbr) { /* addbr or delbr */ ioctl_or_perror_and_die(fd, key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR, @@ -329,6 +382,8 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) int onoff = index_in_strings(no_yes, *argv); if (onoff < 0) bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name); +//TODO: replace with: +//write "0\n" or "1\n" to /sys/class/net/BR/bridge/stp_state onoff = (unsigned)onoff / 4; arm_ioctl(args, BRCTL_SET_BRIDGE_STP_STATE, onoff, 0); goto fire; @@ -340,6 +395,11 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) BRCTL_SET_BRIDGE_HELLO_TIME, /* ARG_sethello */ BRCTL_SET_BRIDGE_MAX_AGE /* ARG_setmaxage */ }; +//TODO: replace with: +//setageing BR N: write "N*100\n" to /sys/class/net/BR/bridge/ageing_time +//setfd BR N: write "N*100\n" to /sys/class/net/BR/bridge/forward_delay +//sethello BR N: write "N*100\n" to /sys/class/net/BR/bridge/hello_time +//setmaxage BR N: write "N*100\n" to /sys/class/net/BR/bridge/max_age arm_ioctl(args, ops[key - ARG_setageing], str_to_jiffies(*argv), 0); goto fire; } @@ -355,6 +415,11 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) int port = -1; unsigned arg1, arg2; +//TODO: replace with: +//setbridgeprio BR N: write "N\n" to /sys/class/net/BR/bridge/priority +//setpathcost BR PORT N: ?? +//setportprio BR PORT N: ?? + if (key != ARG_setbridgeprio) { /* get portnum */ unsigned i; -- cgit v1.2.3-55-g6feb From dc1b2d43565dee709229dd76d856736bf543792d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 13 Apr 2019 13:58:06 +0200 Subject: brctl: convert remaining commands to work via /sys function old new delta write_ulong - 96 +96 show_bridge 310 338 +28 static.ops 3 - -3 arm_ioctl 20 - -20 packed_usage 33344 33315 -29 brctl_main 885 721 -164 ------------------------------------------------------------------------------ (add/remove: 1/2 grow/shrink: 1/2 up/down: 124/-216) Total: -92 bytes text data bss dec hex filename 982112 485 7296 989893 f1ac5 busybox_old 982157 485 7296 989938 f1af2 busybox_unstripped Signed-off-by: Denys Vlasenko --- networking/brctl.c | 331 ++++++++++++++++++++++++----------------------------- 1 file changed, 147 insertions(+), 184 deletions(-) diff --git a/networking/brctl.c b/networking/brctl.c index 706ecfc07..804728e3c 100644 --- a/networking/brctl.c +++ b/networking/brctl.c @@ -44,27 +44,31 @@ //kbuild:lib-$(CONFIG_BRCTL) += brctl.o //usage:#define brctl_trivial_usage -//usage: "COMMAND [BRIDGE [INTERFACE]]" +//usage: "COMMAND [BRIDGE [ARGS]]" //usage:#define brctl_full_usage "\n\n" //usage: "Manage ethernet bridges\n" //usage: "\nCommands:" //usage: IF_FEATURE_BRCTL_SHOW( -//usage: "\n show Show a list of bridges" +//usage: "\n show [BRIDGE]... Show bridges" //usage: ) //usage: "\n addbr BRIDGE Create BRIDGE" //usage: "\n delbr BRIDGE Delete BRIDGE" //usage: "\n addif BRIDGE IFACE Add IFACE to BRIDGE" //usage: "\n delif BRIDGE IFACE Delete IFACE from BRIDGE" //usage: IF_FEATURE_BRCTL_FANCY( -//usage: "\n setageing BRIDGE TIME Set ageing time" -//usage: "\n setfd BRIDGE TIME Set bridge forward delay" -//usage: "\n sethello BRIDGE TIME Set hello time" -//usage: "\n setmaxage BRIDGE TIME Set max message age" -//usage: "\n setpathcost BRIDGE COST Set path cost" -//usage: "\n setportprio BRIDGE PRIO Set port priority" +//usage: "\n stp BRIDGE 1/yes/on|0/no/off STP on/off" +//usage: "\n setageing BRIDGE SECONDS Set ageing time" +//usage: "\n setfd BRIDGE SECONDS Set bridge forward delay" +//usage: "\n sethello BRIDGE SECONDS Set hello time" +//usage: "\n setmaxage BRIDGE SECONDS Set max message age" //usage: "\n setbridgeprio BRIDGE PRIO Set bridge priority" -//usage: "\n stp BRIDGE [1/yes/on|0/no/off] STP on/off" +//usage: "\n setportprio BRIDGE IFACE PRIO Set port priority" +//usage: "\n setpathcost BRIDGE IFACE COST Set path cost" //usage: ) +// Not yet implemented: +// hairpin BRIDGE IFACE on|off Hairpin on/off +// showmacs BRIDGE List mac addrs +// showstp BRIDGE Show stp info #include "libbb.h" #include "common_bufsiz.h" @@ -84,69 +88,11 @@ # define SIOCBRDELIF BRCTL_DEL_IF #endif - -/* Maximum number of ports supported per bridge interface. */ -#ifndef MAX_PORTS -# define MAX_PORTS 32 -#endif - /* Use internal number parsing and not the "exact" conversion. */ /* #define BRCTL_USE_INTERNAL 0 */ /* use exact conversion */ #define BRCTL_USE_INTERNAL 1 #if ENABLE_FEATURE_BRCTL_FANCY -/* #include - * breaks on musl: we already included netinet/in.h in libbb.h, - * if we include here, we get this: - * In file included from /usr/include/linux/if_bridge.h:18, - * from networking/brctl.c:67: - * /usr/include/linux/in6.h:32: error: redefinition of 'struct in6_addr' - * /usr/include/linux/in6.h:49: error: redefinition of 'struct sockaddr_in6' - * /usr/include/linux/in6.h:59: error: redefinition of 'struct ipv6_mreq' - */ -/* From */ -#define BRCTL_GET_VERSION 0 -#define BRCTL_GET_BRIDGES 1 -#define BRCTL_ADD_BRIDGE 2 -#define BRCTL_DEL_BRIDGE 3 -#define BRCTL_ADD_IF 4 -#define BRCTL_DEL_IF 5 -#define BRCTL_GET_BRIDGE_INFO 6 -#define BRCTL_GET_PORT_LIST 7 -#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8 -#define BRCTL_SET_BRIDGE_HELLO_TIME 9 -#define BRCTL_SET_BRIDGE_MAX_AGE 10 -#define BRCTL_SET_AGEING_TIME 11 -#define BRCTL_SET_GC_INTERVAL 12 -#define BRCTL_GET_PORT_INFO 13 -#define BRCTL_SET_BRIDGE_STP_STATE 14 -#define BRCTL_SET_BRIDGE_PRIORITY 15 -#define BRCTL_SET_PORT_PRIORITY 16 -#define BRCTL_SET_PATH_COST 17 -#define BRCTL_GET_FDB_ENTRIES 18 -struct __bridge_info { - uint64_t designated_root; - uint64_t bridge_id; - uint32_t root_path_cost; - uint32_t max_age; - uint32_t hello_time; - uint32_t forward_delay; - uint32_t bridge_max_age; - uint32_t bridge_hello_time; - uint32_t bridge_forward_delay; - uint8_t topology_change; - uint8_t topology_change_detected; - uint8_t root_port; - uint8_t stp_enabled; - uint32_t ageing_time; - uint32_t gc_interval; - uint32_t hello_timer_value; - uint32_t tcn_timer_value; - uint32_t topology_change_timer_value; - uint32_t gc_timer_value; -}; -/* end */ - /* FIXME: These 4 funcs are not really clean and could be improved */ static ALWAYS_INLINE void bb_strtotimeval(struct timeval *tv, const char *time_str) @@ -188,18 +134,10 @@ static unsigned long str_to_jiffies(const char *time_str) bb_strtotimeval(&tv, time_str); return tv_to_jiffies(&tv); } - -static void arm_ioctl(unsigned long *args, - unsigned long arg0, unsigned long arg1, unsigned long arg2) -{ - args[0] = arg0; - args[1] = arg1; - args[2] = arg2; - args[3] = 0; -} #endif #define filedata bb_common_bufsiz1 + static int read_file(const char *name) { int n = open_read_close(name, filedata, COMMON_BUFSIZE - 1); @@ -217,16 +155,21 @@ static int read_file(const char *name) */ static int show_bridge(const char *name, int need_hdr) { -// Output: -//bridge name bridge id STP enabled interfaces -//br0 8000.000000000000 no eth0 +/* Output: + *bridge name bridge id STP enabled interfaces + *br0 8000.000000000000 no eth0 + */ char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32]; int tabs; DIR *ifaces; struct dirent *ent; char *sfx; - sfx = pathbuf + sprintf(pathbuf, "%s/bridge/", name); +#if IFNAMSIZ == 16 + sfx = pathbuf + sprintf(pathbuf, "%.16s/bridge/", name); +#else + sfx = pathbuf + sprintf(pathbuf, "%.*s/bridge/", (int)IFNAMSIZ, name); +#endif strcpy(sfx, "bridge_id"); if (read_file(pathbuf) < 0) return -1; /* this iface is not a bridge */ @@ -243,13 +186,15 @@ static int show_bridge(const char *name, int need_hdr) else if (LONE_CHAR(filedata, '1')) strcpy(filedata, "yes"); - printf(filedata); + fputs(filedata, stdout); - strcpy(sfx, "brif"); + strcpy(sfx - (sizeof("bridge/")-1), "brif"); tabs = 0; ifaces = opendir(pathbuf); if (ifaces) { while ((ent = readdir(ifaces)) != NULL) { + if (DOT_OR_DOTDOT(ent->d_name)) + continue; /* . or .. */ if (tabs) printf("\t\t\t\t\t"); else @@ -263,6 +208,23 @@ static int show_bridge(const char *name, int need_hdr) return 0; } +static void write_ulong(const char *name, const char *leaf, unsigned long val) +{ + char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32]; + int fd, n; + +#if IFNAMSIZ == 16 + sprintf(pathbuf, "%.16s/%s", name, leaf); +#else + sprintf(pathbuf, "%.*s/%s", (int)IFNAMSIZ, name, leaf); +#endif + fd = xopen(pathbuf, O_WRONLY); + n = sprintf(filedata, "%lu\n", val); + if (write(fd, filedata, n) < 0) + bb_simple_perror_msg_and_die(name); + close(fd); +} + int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int brctl_main(int argc UNUSED_PARAM, char **argv) { @@ -271,24 +233,20 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) IF_FEATURE_BRCTL_FANCY( "stp\0" "setageing\0" "setfd\0" "sethello\0" "setmaxage\0" - "setpathcost\0" "setportprio\0" "setbridgeprio\0" + "setpathcost\0" "setportprio\0" + "setbridgeprio\0" ) IF_FEATURE_BRCTL_SHOW("show\0"); - enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif IF_FEATURE_BRCTL_FANCY(, ARG_stp, ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage, - ARG_setpathcost, ARG_setportprio, ARG_setbridgeprio + ARG_setpathcost, ARG_setportprio, + ARG_setbridgeprio ) IF_FEATURE_BRCTL_SHOW(, ARG_show) }; - int fd; - smallint key; - struct ifreq ifr; - char *br, *brif; - argv++; if (!*argv) { /* bare "brctl" shows --help */ @@ -297,12 +255,10 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) xchdir("/sys/class/net"); - while (*argv) { -#if ENABLE_FEATURE_BRCTL_FANCY - int ifidx[MAX_PORTS]; - unsigned long args[4]; - ifr.ifr_data = (char *) &args; -#endif +// while (*argv) + { + smallint key; + char *br; key = index_in_strings(keywords, *argv); if (key == -1) /* no match found in keywords array, bail out. */ @@ -310,28 +266,28 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) argv++; #if ENABLE_FEATURE_BRCTL_SHOW - if (key == ARG_show) { /* show */ + if (key == ARG_show) { /* show [BR]... */ DIR *net; struct dirent *ent; int need_hdr = 1; int exitcode = EXIT_SUCCESS; if (*argv) { - /* "brctl show BR1 BR2 BR3" */ + /* "show BR1 BR2 BR3" */ do { if (show_bridge(*argv, need_hdr) >= 0) { need_hdr = 0; } else { bb_error_msg("bridge %s does not exist", *argv); -//TODO: if device exists, but is not a BR, brctl from bridge-utils 1.6 says this instead: -// "device eth0 is not a bridge" +//TODO: if device exists, but is not a BR, brctl from bridge-utils 1.6 +//says this instead: "device eth0 is not a bridge" exitcode = EXIT_FAILURE; } } while (*++argv != NULL); return exitcode; } - /* "brctl show" (if no ifaces, shows nothing, not even header) */ + /* "show" (if no ifaces, shows nothing, not even header) */ net = xopendir("."); while ((ent = readdir(net)) != NULL) { if (DOT_OR_DOTDOT(ent->d_name)) @@ -339,7 +295,8 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) if (show_bridge(ent->d_name, need_hdr) >= 0) need_hdr = 0; } - closedir(net); + if (ENABLE_FEATURE_CLEAN_UP) + closedir(net); return exitcode; } #endif @@ -347,33 +304,31 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) if (!*argv) /* all but 'show' need at least one argument */ bb_show_usage(); - fd = xsocket(AF_INET, SOCK_STREAM, 0); br = *argv++; -//brctl from bridge-utils 1.6 also still uses ioctl -//for SIOCBRADDBR / SIOCBRDELBR, not /sys accesses - if (key == ARG_addbr || key == ARG_delbr) { /* addbr or delbr */ + if (key == ARG_addbr || key == ARG_delbr) { + /* addbr or delbr */ + /* brctl from bridge-utils 1.6 still uses ioctl + * for SIOCBRADDBR / SIOCBRDELBR, not /sys accesses + */ + int fd = xsocket(AF_INET, SOCK_STREAM, 0); ioctl_or_perror_and_die(fd, - key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR, - br, "bridge %s", br); - goto done; + key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR, + br, "bridge %s", br + ); + //close(fd); + //goto done; + /* bridge-utils 1.6 simply ignores trailing args: + * "brctl addbr BR1 ARGS" ignores ARGS + */ + if (ENABLE_FEATURE_CLEAN_UP) + close(fd); + return EXIT_SUCCESS; } if (!*argv) /* all but 'addbr/delbr' need at least two arguments */ bb_show_usage(); - strncpy_IFNAMSIZ(ifr.ifr_name, br); - if (key == ARG_addif || key == ARG_delif) { /* addif or delif */ - brif = *argv; - ifr.ifr_ifindex = if_nametoindex(brif); - if (!ifr.ifr_ifindex) { - bb_perror_msg_and_die("iface %s", brif); - } - ioctl_or_perror_and_die(fd, - key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF, - &ifr, "bridge %s", br); - goto done_next_argv; - } #if ENABLE_FEATURE_BRCTL_FANCY if (key == ARG_stp) { /* stp */ static const char no_yes[] ALIGN1 = @@ -382,78 +337,86 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) int onoff = index_in_strings(no_yes, *argv); if (onoff < 0) bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name); -//TODO: replace with: -//write "0\n" or "1\n" to /sys/class/net/BR/bridge/stp_state onoff = (unsigned)onoff / 4; - arm_ioctl(args, BRCTL_SET_BRIDGE_STP_STATE, onoff, 0); - goto fire; + write_ulong(br, "bridge/stp_state", onoff); + //goto done_next_argv; + return EXIT_SUCCESS; } + if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */ - static const uint8_t ops[] ALIGN1 = { - BRCTL_SET_AGEING_TIME, /* ARG_setageing */ - BRCTL_SET_BRIDGE_FORWARD_DELAY, /* ARG_setfd */ - BRCTL_SET_BRIDGE_HELLO_TIME, /* ARG_sethello */ - BRCTL_SET_BRIDGE_MAX_AGE /* ARG_setmaxage */ - }; -//TODO: replace with: -//setageing BR N: write "N*100\n" to /sys/class/net/BR/bridge/ageing_time -//setfd BR N: write "N*100\n" to /sys/class/net/BR/bridge/forward_delay -//sethello BR N: write "N*100\n" to /sys/class/net/BR/bridge/hello_time -//setmaxage BR N: write "N*100\n" to /sys/class/net/BR/bridge/max_age - arm_ioctl(args, ops[key - ARG_setageing], str_to_jiffies(*argv), 0); - goto fire; + /* setageing BR N: "N*100\n" to /sys/class/net/BR/bridge/ageing_time + * setfd BR N: "N*100\n" to /sys/class/net/BR/bridge/forward_delay + * sethello BR N: "N*100\n" to /sys/class/net/BR/bridge/hello_time + * setmaxage BR N: "N*100\n" to /sys/class/net/BR/bridge/max_age + */ + write_ulong(br, + nth_string( + "bridge/ageing_time" "\0" /* ARG_setageing */ + "bridge/forward_delay""\0" /* ARG_setfd */ + "bridge/hello_time" "\0" /* ARG_sethello */ + "bridge/max_age", /* ARG_setmaxage */ + key - ARG_setageing + ), + str_to_jiffies(*argv) + ); + //goto done_next_argv; + return EXIT_SUCCESS; + } + + if (key == ARG_setbridgeprio) { + write_ulong(br, "bridge/priority", xatoi_positive(*argv)); + //goto done_next_argv; + return EXIT_SUCCESS; } + if (key == ARG_setpathcost || key == ARG_setportprio - || key == ARG_setbridgeprio ) { - static const uint8_t ops[] ALIGN1 = { - BRCTL_SET_PATH_COST, /* ARG_setpathcost */ - BRCTL_SET_PORT_PRIORITY, /* ARG_setportprio */ - BRCTL_SET_BRIDGE_PRIORITY /* ARG_setbridgeprio */ - }; - int port = -1; - unsigned arg1, arg2; - -//TODO: replace with: -//setbridgeprio BR N: write "N\n" to /sys/class/net/BR/bridge/priority -//setpathcost BR PORT N: ?? -//setportprio BR PORT N: ?? - - if (key != ARG_setbridgeprio) { - /* get portnum */ - unsigned i; - - port = if_nametoindex(*argv++); - if (!port) - bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, "port"); - memset(ifidx, 0, sizeof ifidx); - arm_ioctl(args, BRCTL_GET_PORT_LIST, (unsigned long)ifidx, - MAX_PORTS); - xioctl(fd, SIOCDEVPRIVATE, &ifr); - for (i = 0; i < MAX_PORTS; i++) { - if (ifidx[i] == port) { - port = i; - break; - } - } - } - arg1 = port; - arg2 = xatoi_positive(*argv); - if (key == ARG_setbridgeprio) { - arg1 = arg2; - arg2 = 0; - } - arm_ioctl(args, ops[key - ARG_setpathcost], arg1, arg2); + if (!argv[1]) + bb_show_usage(); + /* BR is not used (and ignored!) for these commands: + * "setpathcost BR PORT N" writes "N\n" to + * /sys/class/net/PORT/brport/path_cost + * "setportprio BR PORT N" writes "N\n" to + * /sys/class/net/PORT/brport/priority + */ + write_ulong(argv[0], + nth_string( + "brport/path_cost" "\0" /* ARG_setpathcost */ + "brport/priority", /* ARG_setportprio */ + key - ARG_setpathcost + ), + xatoi_positive(argv[1]) + ); + //argv++; + //goto done_next_argv; + return EXIT_SUCCESS; } - fire: - /* Execute the previously set command */ - xioctl(fd, SIOCDEVPRIVATE, &ifr); #endif - done_next_argv: - argv++; - done: - close(fd); + /* always true: if (key == ARG_addif || key == ARG_delif) */ { + /* addif or delif */ + struct ifreq ifr; + int fd = xsocket(AF_INET, SOCK_STREAM, 0); + + strncpy_IFNAMSIZ(ifr.ifr_name, br); + ifr.ifr_ifindex = if_nametoindex(*argv); + if (ifr.ifr_ifindex == 0) { + bb_perror_msg_and_die("iface %s", *argv); + } + ioctl_or_perror_and_die(fd, + key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF, + &ifr, "bridge %s", br + ); + //close(fd); + //goto done_next_argv; + if (ENABLE_FEATURE_CLEAN_UP) + close(fd); + return EXIT_SUCCESS; + } + +// done_next_argv: +// argv++; +// done: } return EXIT_SUCCESS; -- cgit v1.2.3-55-g6feb From 94356088049a29c81dc29e448e621d81ccd43bfc Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 13 Apr 2019 14:17:55 +0200 Subject: brctl: simplify str_to_jiffies() function old new delta write_uint - 96 +96 brctl_main 721 678 -43 write_ulong 96 - -96 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 0/1 up/down: 96/-139) Total: -43 bytes Signed-off-by: Denys Vlasenko --- networking/brctl.c | 68 ++++++++++++++++-------------------------------------- 1 file changed, 20 insertions(+), 48 deletions(-) diff --git a/networking/brctl.c b/networking/brctl.c index 804728e3c..0f2c10f22 100644 --- a/networking/brctl.c +++ b/networking/brctl.c @@ -66,9 +66,9 @@ //usage: "\n setpathcost BRIDGE IFACE COST Set path cost" //usage: ) // Not yet implemented: -// hairpin BRIDGE IFACE on|off Hairpin on/off -// showmacs BRIDGE List mac addrs -// showstp BRIDGE Show stp info +// hairpin BRIDGE IFACE on|off Hairpin on/off +// showmacs BRIDGE List mac addrs +// showstp BRIDGE Show stp info #include "libbb.h" #include "common_bufsiz.h" @@ -88,51 +88,23 @@ # define SIOCBRDELIF BRCTL_DEL_IF #endif -/* Use internal number parsing and not the "exact" conversion. */ -/* #define BRCTL_USE_INTERNAL 0 */ /* use exact conversion */ -#define BRCTL_USE_INTERNAL 1 - #if ENABLE_FEATURE_BRCTL_FANCY -/* FIXME: These 4 funcs are not really clean and could be improved */ -static ALWAYS_INLINE void bb_strtotimeval(struct timeval *tv, - const char *time_str) +static unsigned str_to_jiffies(const char *time_str) { - double secs; -# if BRCTL_USE_INTERNAL + double dd; char *endptr; - secs = /*bb_*/strtod(time_str, &endptr); - if (endptr == time_str) -# else - if (sscanf(time_str, "%lf", &secs) != 1) -# endif + dd = /*bb_*/strtod(time_str, &endptr); + if (endptr == time_str || dd < 0) bb_error_msg_and_die(bb_msg_invalid_arg_to, time_str, "timespec"); - tv->tv_sec = secs; - tv->tv_usec = 1000000 * (secs - tv->tv_sec); -} -static ALWAYS_INLINE unsigned long tv_to_jiffies(const struct timeval *tv) -{ - unsigned long long jif; + dd *= 100; + /* For purposes of brctl, + * capping SECONDS by ~20 million seconds is quite enough: + */ + if (dd > INT_MAX) + dd = INT_MAX; - jif = 1000000ULL * tv->tv_sec + tv->tv_usec; - - return jif/10000; -} -# if 0 -static void jiffies_to_tv(struct timeval *tv, unsigned long jiffies) -{ - unsigned long long tvusec; - - tvusec = 10000ULL*jiffies; - tv->tv_sec = tvusec/1000000; - tv->tv_usec = tvusec - 1000000 * tv->tv_sec; -} -# endif -static unsigned long str_to_jiffies(const char *time_str) -{ - struct timeval tv; - bb_strtotimeval(&tv, time_str); - return tv_to_jiffies(&tv); + return dd; } #endif @@ -208,7 +180,7 @@ static int show_bridge(const char *name, int need_hdr) return 0; } -static void write_ulong(const char *name, const char *leaf, unsigned long val) +static void write_uint(const char *name, const char *leaf, unsigned val) { char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32]; int fd, n; @@ -219,7 +191,7 @@ static void write_ulong(const char *name, const char *leaf, unsigned long val) sprintf(pathbuf, "%.*s/%s", (int)IFNAMSIZ, name, leaf); #endif fd = xopen(pathbuf, O_WRONLY); - n = sprintf(filedata, "%lu\n", val); + n = sprintf(filedata, "%u\n", val); if (write(fd, filedata, n) < 0) bb_simple_perror_msg_and_die(name); close(fd); @@ -338,7 +310,7 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) if (onoff < 0) bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name); onoff = (unsigned)onoff / 4; - write_ulong(br, "bridge/stp_state", onoff); + write_uint(br, "bridge/stp_state", onoff); //goto done_next_argv; return EXIT_SUCCESS; } @@ -349,7 +321,7 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) * sethello BR N: "N*100\n" to /sys/class/net/BR/bridge/hello_time * setmaxage BR N: "N*100\n" to /sys/class/net/BR/bridge/max_age */ - write_ulong(br, + write_uint(br, nth_string( "bridge/ageing_time" "\0" /* ARG_setageing */ "bridge/forward_delay""\0" /* ARG_setfd */ @@ -364,7 +336,7 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) } if (key == ARG_setbridgeprio) { - write_ulong(br, "bridge/priority", xatoi_positive(*argv)); + write_uint(br, "bridge/priority", xatoi_positive(*argv)); //goto done_next_argv; return EXIT_SUCCESS; } @@ -380,7 +352,7 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) * "setportprio BR PORT N" writes "N\n" to * /sys/class/net/PORT/brport/priority */ - write_ulong(argv[0], + write_uint(argv[0], nth_string( "brport/path_cost" "\0" /* ARG_setpathcost */ "brport/priority", /* ARG_setportprio */ -- cgit v1.2.3-55-g6feb From 2945822f8680c10bd6930c752c10849a725b5ef7 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 13 Apr 2019 15:48:31 +0200 Subject: brctl: add TODO: "showmacs BR" Signed-off-by: Denys Vlasenko --- networking/brctl.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/networking/brctl.c b/networking/brctl.c index 0f2c10f22..f44ad9c8d 100644 --- a/networking/brctl.c +++ b/networking/brctl.c @@ -9,9 +9,6 @@ * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ -/* This applet currently uses only the ioctl interface and no sysfs at all. - * At the time of this writing this was considered a feature. - */ //config:config BRCTL //config: bool "brctl (4.7 kb)" //config: default y @@ -46,7 +43,7 @@ //usage:#define brctl_trivial_usage //usage: "COMMAND [BRIDGE [ARGS]]" //usage:#define brctl_full_usage "\n\n" -//usage: "Manage ethernet bridges\n" +//usage: "Manage ethernet bridges" //usage: "\nCommands:" //usage: IF_FEATURE_BRCTL_SHOW( //usage: "\n show [BRIDGE]... Show bridges" @@ -364,6 +361,23 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) //goto done_next_argv; return EXIT_SUCCESS; } + +/* TODO: "showmacs BR" + * port no\tmac addr\t\tis local?\tageing timer + * 1\txx:xx:xx:xx:xx:xx\tno\t\t1.31 + * port no mac addr is local? ageing timer + * 1 xx:xx:xx:xx:xx:xx no 1.31 + * Read fixed-sized records from /sys/class/net/BR/brforward: + * struct __fdb_entry { + * uint8_t mac_addr[ETH_ALEN]; + * uint8_t port_no; //lsb + * uint8_t is_local; + * uint32_t ageing_timer_value; + * uint8_t port_hi; + * uint8_t pad0; + * uint16_t unused; + * }; + */ #endif /* always true: if (key == ARG_addif || key == ARG_delif) */ { /* addif or delif */ -- cgit v1.2.3-55-g6feb From 0e7bd69bb58e0914f25e3543e6beb142a203738c Mon Sep 17 00:00:00 2001 From: Antoine Girard-Vallée Date: Fri, 8 Mar 2019 09:24:42 -0500 Subject: udhcp: add 100 and 101 dhcp options for ipv4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support for the IEEE timezone string and timezone database strings (100 and 101 options respectively) is added for ipv4, conforming to RFC-4833. The two options are passed to hook scripts in the variables tzstr and tzdbstr. function old new delta dhcp_option_strings 280 294 +14 dhcp_optflags 76 80 +4 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 18/0) Total: 18 bytes Signed-off-by: Antoine Girard-Vallée Signed-off-by: Denys Vlasenko --- networking/udhcp/common.c | 4 ++++ networking/udhcp/common.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c index fc4de5716..59cf723ee 100644 --- a/networking/udhcp/common.c +++ b/networking/udhcp/common.c @@ -54,6 +54,8 @@ const struct dhcp_optflag dhcp_optflags[] = { { OPTION_STRING , 0x43 }, /* DHCP_BOOT_FILE */ //TODO: not a string, but a set of LASCII strings: // { OPTION_STRING , 0x4D }, /* DHCP_USER_CLASS */ + { OPTION_STRING , 0x64 }, /* DHCP_PCODE */ + { OPTION_STRING , 0x65 }, /* DHCP_TCODE */ #if ENABLE_FEATURE_UDHCP_RFC3397 { OPTION_DNS_STRING | OPTION_LIST , 0x77 }, /* DHCP_DOMAIN_SEARCH */ { OPTION_SIP_SERVERS , 0x78 }, /* DHCP_SIP_SERVERS */ @@ -121,6 +123,8 @@ const char dhcp_option_strings[] ALIGN1 = "tftp" "\0" /* DHCP_TFTP_SERVER_NAME*/ "bootfile" "\0" /* DHCP_BOOT_FILE */ // "userclass" "\0" /* DHCP_USER_CLASS */ + "tzstr" "\0" /* DHCP_PCODE */ + "tzdbstr" "\0" /* DHCP_TCODE */ #if ENABLE_FEATURE_UDHCP_RFC3397 "search" "\0" /* DHCP_DOMAIN_SEARCH */ // doesn't work in udhcpd.conf since OPTION_SIP_SERVERS diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h index b68f9394e..9d1f71aae 100644 --- a/networking/udhcp/common.h +++ b/networking/udhcp/common.h @@ -149,6 +149,8 @@ enum { //#define DHCP_BOOT_FILE 0x43 /* 67: same as 'file' field */ //#define DHCP_USER_CLASS 0x4d /* 77: RFC 3004. set of LASCII strings. "I am a printer" etc */ #define DHCP_FQDN 0x51 /* 81: client asks to update DNS to map its FQDN to its new IP */ +//#define DHCP_PCODE 0x64 /* 100: RFC 4833. IEEE 1003.1 TZ string */ +//#define DHCP_TCODE 0x65 /* 101: RFC 4833. Reference to the TZ database string */ //#define DHCP_DOMAIN_SEARCH 0x77 /* 119: RFC 3397. set of ASCIZ string, DNS-style compressed */ //#define DHCP_SIP_SERVERS 0x78 /* 120: RFC 3361. flag byte, then: 0: domain names, 1: IP addrs */ //#define DHCP_STATIC_ROUTES 0x79 /* 121: RFC 3442. (mask,ip,router) tuples */ -- cgit v1.2.3-55-g6feb From 7180d9ed863e6ea350938b13384548f4f2617528 Mon Sep 17 00:00:00 2001 From: Rolf Eike Beer Date: Thu, 28 Mar 2019 15:29:29 +0100 Subject: examples/udhcp/simple.script: fix resolv.conf update if it is a dangling symlink If /etc/resolv.conf is a symlink to a tmpfs and the actual file does not already exist, "readlink -f" will not detect it as symlink. Explicitely check for that condition before and touch the file, making the other code work as intended. Signed-off-by: Rolf Eike Beer Signed-off-by: Denys Vlasenko --- examples/udhcp/simple.script | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/udhcp/simple.script b/examples/udhcp/simple.script index 44aa46ece..53974e6d6 100755 --- a/examples/udhcp/simple.script +++ b/examples/udhcp/simple.script @@ -55,6 +55,10 @@ case "$1" in echo "Recreating $RESOLV_CONF" # If the file is a symlink somewhere (like /etc/resolv.conf # pointing to /run/resolv.conf), make sure things work. + if test -L "$RESOLV_CONF"; then + # If it's a dangling symlink, try to create the target. + test -e "$RESOLV_CONF" || touch "$RESOLV_CONF" + fi realconf=$(readlink -f "$RESOLV_CONF" 2>/dev/null || echo "$RESOLV_CONF") tmpfile="$realconf-$$" > "$tmpfile" -- cgit v1.2.3-55-g6feb From cc45cbcca41de47590f4aa839eb0f396f18f9896 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 13 Apr 2019 17:32:40 +0200 Subject: udhcpc6: make it enabled in defconfig Signed-off-by: Denys Vlasenko --- networking/udhcp/d6_dhcpc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index 8a4a4b7a5..97985642b 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -2,15 +2,13 @@ /* * DHCPv6 client. * - * WARNING: THIS CODE IS INCOMPLETE. - * * Copyright (C) 2011-2017 Denys Vlasenko. * * Licensed under GPLv2, see file LICENSE in this source tree. */ //config:config UDHCPC6 //config: bool "udhcpc6 (21 kb)" -//config: default n # not yet ready +//config: default y //config: depends on FEATURE_IPV6 //config: help //config: udhcpc6 is a DHCPv6 client -- cgit v1.2.3-55-g6feb From 0d75e8b7973353f1d034b97bebfd4d2c13a9f5d6 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 13 Apr 2019 19:43:15 +0200 Subject: udhcpc6: add a few comments, no code changes. Signed-off-by: Denys Vlasenko --- networking/udhcp/d6_dhcpc.c | 1 + networking/udhcp/d6_socket.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index 97985642b..112f12df2 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -531,6 +531,7 @@ static uint8_t *add_d6_client_options(uint8_t *ptr) static int d6_mcast_from_client_config_ifindex(struct d6_packet *packet, uint8_t *end) { + /* FF02::1:2 is "All_DHCP_Relay_Agents_and_Servers" address */ static const uint8_t FF02__1_2[16] = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, diff --git a/networking/udhcp/d6_socket.c b/networking/udhcp/d6_socket.c index 6ad53a9c2..fe46e5f1d 100644 --- a/networking/udhcp/d6_socket.c +++ b/networking/udhcp/d6_socket.c @@ -38,6 +38,15 @@ int FAST_FUNC d6_read_interface(const char *interface, int *ifindex, struct in6_ log1("IP %s", inet_ntoa(((struct sockaddr_in *)ifa->ifa_addr)->sin_addr)); } #endif +/* RFC 3315 + * 16. Client Source Address and Interface Selection + * + * "When a client sends a DHCP message to the + * All_DHCP_Relay_Agents_and_Servers address, ... ... The client + * MUST use a link-local address assigned to the interface for which it + * is requesting configuration information as the source address in the + * header of the IP datagram." + */ if (ifa->ifa_addr->sa_family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&sip6->sin6_addr) ) { -- cgit v1.2.3-55-g6feb From 60bf77f7e7c4513a781e9acc1b9bca64c4051140 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 14 Apr 2019 17:01:10 +0200 Subject: udhcpc6: code shrink function old new delta d6_read_interface 593 582 -11 Signed-off-by: Denys Vlasenko --- networking/udhcp/d6_common.h | 7 ++++++- networking/udhcp/d6_dhcpc.c | 3 --- networking/udhcp/d6_socket.c | 18 ++++++++++++------ 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/networking/udhcp/d6_common.h b/networking/udhcp/d6_common.h index d0506e2bb..2178cb9d6 100644 --- a/networking/udhcp/d6_common.h +++ b/networking/udhcp/d6_common.h @@ -145,7 +145,12 @@ struct client6_data_t { #define client6_data (*(struct client6_data_t*)(&bb_common_bufsiz1[COMMON_BUFSIZE - sizeof(struct client6_data_t)])) -int FAST_FUNC d6_read_interface(const char *interface, int *ifindex, struct in6_addr *nip6, uint8_t *mac); +int FAST_FUNC d6_read_interface( + const char *interface, + int *ifindex, + struct in6_addr *nip6, + uint8_t *mac +); int FAST_FUNC d6_listen_socket(int port, const char *inf); diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index 112f12df2..3562988fd 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -204,7 +204,6 @@ static void *d6_copy_option(uint8_t *option, uint8_t *option_end, unsigned code) return xmemdup(opt, opt[3] + 4); } - /*** Script execution code ***/ static char** new_env(void) @@ -902,7 +901,6 @@ static NOINLINE int d6_recv_raw_packet(struct in6_addr *peer_ipv6, struct d6_pac return bytes; } - /*** Main ***/ static int sockfd = -1; @@ -1146,7 +1144,6 @@ static void client_background(void) //usage: "\n USR1 Renew lease" //usage: "\n USR2 Release lease" - int udhcpc6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int udhcpc6_main(int argc UNUSED_PARAM, char **argv) { diff --git a/networking/udhcp/d6_socket.c b/networking/udhcp/d6_socket.c index fe46e5f1d..25e622d6f 100644 --- a/networking/udhcp/d6_socket.c +++ b/networking/udhcp/d6_socket.c @@ -10,7 +10,11 @@ #include #include -int FAST_FUNC d6_read_interface(const char *interface, int *ifindex, struct in6_addr *nip6, uint8_t *mac) +int FAST_FUNC d6_read_interface( + const char *interface, + int *ifindex, + struct in6_addr *nip6, + uint8_t *mac) { int retval = 3; struct ifaddrs *ifap, *ifa; @@ -22,12 +26,12 @@ int FAST_FUNC d6_read_interface(const char *interface, int *ifindex, struct in6_ if (!ifa->ifa_addr || (strcmp(ifa->ifa_name, interface) != 0)) continue; - sip6 = (struct sockaddr_in6*)(ifa->ifa_addr); - if (ifa->ifa_addr->sa_family == AF_PACKET) { - struct sockaddr_ll *sll = (struct sockaddr_ll*)(ifa->ifa_addr); + struct sockaddr_ll *sll = (void*)(ifa->ifa_addr); memcpy(mac, sll->sll_addr, 6); - log2("MAC %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + log2("MAC %02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] + ); *ifindex = sll->sll_ifindex; log2("ifindex %d", *ifindex); retval &= (3 - (1<<0)); @@ -47,6 +51,8 @@ int FAST_FUNC d6_read_interface(const char *interface, int *ifindex, struct in6_ * is requesting configuration information as the source address in the * header of the IP datagram." */ + sip6 = (void*)(ifa->ifa_addr); + if (ifa->ifa_addr->sa_family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&sip6->sin6_addr) ) { @@ -96,7 +102,7 @@ int FAST_FUNC d6_read_interface(const char *interface, int *ifindex, struct in6_ bb_error_msg("can't get %s", "MAC"); if (retval & (1<<1)) bb_error_msg("can't get %s", "link-local IPv6 address"); - return -1; + return retval; } int FAST_FUNC d6_listen_socket(int port, const char *inf) -- cgit v1.2.3-55-g6feb From 51792e126bddaabf572132f1e0d4ed9bfd324c58 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 14 Apr 2019 19:57:13 +0200 Subject: httpd: if remote IP is denied, send FORBIDDEN reply earlier While at it, fix sighup_handler to not clobber errno. function old new delta send_HTTP_FORBIDDEN_and_exit_if_denied_ip - 47 +47 sighup_handler 15 30 +15 handle_incoming_and_exit 2791 2763 -28 checkPermIP 48 - -48 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 1/1 up/down: 62/-76) Total: -14 bytes Signed-off-by: Denys Vlasenko --- networking/httpd.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/networking/httpd.c b/networking/httpd.c index b52526a78..f713f6929 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -95,9 +95,7 @@ * If -c is not set, an attempt will be made to open the default * root configuration file. If -c is set and the file is not found, the * server exits with an error. - * */ - /* TODO: use TCP_CORK, parse_config() */ //config:config HTTPD //config: bool "httpd (32 kb)" //config: default y @@ -246,6 +244,8 @@ //usage: "\n -e STRING HTML encode STRING" //usage: "\n -d STRING URL decode STRING" +/* TODO: use TCP_CORK, parse_config() */ + #include "libbb.h" #include "common_bufsiz.h" #if ENABLE_PAM @@ -1817,7 +1817,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what) log_and_exit(); } -static int checkPermIP(void) +static void send_HTTP_FORBIDDEN_and_exit_if_denied_ip(void) { Htaccess_IP *cur; @@ -1837,10 +1837,13 @@ static int checkPermIP(void) ); #endif if ((rmt_ip & cur->mask) == cur->ip) - return (cur->allow_deny == 'A'); /* A -> 1 */ + if (cur->allow_deny == 'A') + return; + send_headers_and_exit(HTTP_FORBIDDEN); } - return !flg_deny_all; /* depends on whether we saw "D:*" */ + if (flg_deny_all) /* depends on whether we saw "D:*" */ + send_headers_and_exit(HTTP_FORBIDDEN); } #if ENABLE_FEATURE_HTTPD_BASIC_AUTH @@ -2090,7 +2093,6 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) #if ENABLE_FEATURE_HTTPD_BASIC_AUTH smallint authorized = -1; #endif - smallint ip_allowed; char http_major_version; #if ENABLE_FEATURE_HTTPD_PROXY char http_minor_version; @@ -2240,14 +2242,14 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) bb_error_msg("url:%s", urlcopy); tptr = urlcopy; - ip_allowed = checkPermIP(); - while (ip_allowed && (tptr = strchr(tptr + 1, '/')) != NULL) { + send_HTTP_FORBIDDEN_and_exit_if_denied_ip(); + while ((tptr = strchr(tptr + 1, '/')) != NULL) { /* have path1/path2 */ *tptr = '\0'; if (is_directory(urlcopy + 1, /*followlinks:*/ 1)) { /* may have subdir config */ parse_conf(urlcopy + 1, SUBDIR_PARSE); - ip_allowed = checkPermIP(); + send_HTTP_FORBIDDEN_and_exit_if_denied_ip(); } *tptr = '/'; } @@ -2380,7 +2382,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) /* We are done reading headers, disable peer timeout */ alarm(0); - if (strcmp(bb_basename(urlcopy), HTTPD_CONF) == 0 || !ip_allowed) { + if (strcmp(bb_basename(urlcopy), HTTPD_CONF) == 0) { /* protect listing [/path]/httpd.conf or IP deny */ send_headers_and_exit(HTTP_FORBIDDEN); } @@ -2593,7 +2595,9 @@ static void mini_httpd_inetd(void) static void sighup_handler(int sig UNUSED_PARAM) { + int sv = errno; parse_conf(DEFAULT_PATH_HTTPD_CONF, SIGNALED_PARSE); + errno = sv; } enum { -- cgit v1.2.3-55-g6feb From 02d650e15919e48fe031308c77c041159c0e3631 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 14 Apr 2019 20:46:57 +0200 Subject: httpd: fix proxy headers passing - full_write() instead of write() function old new delta handle_incoming_and_exit 2763 2752 -11 Signed-off-by: Denys Vlasenko --- networking/httpd.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/networking/httpd.c b/networking/httpd.c index f713f6929..c486197b8 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -2271,15 +2271,20 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) bb_error_msg("header: '%s'", iobuf); #if ENABLE_FEATURE_HTTPD_PROXY - /* We need 2 more bytes for yet another "\r\n" - - * see near fdprintf(proxy_fd...) further below */ - if (proxy_entry && (header_ptr - header_buf) < IOBUF_SIZE - 4) { - int len = strnlen(iobuf, IOBUF_SIZE - (header_ptr - header_buf) - 4); - memcpy(header_ptr, iobuf, len); - header_ptr += len; - header_ptr[0] = '\r'; - header_ptr[1] = '\n'; - header_ptr += 2; + if (proxy_entry) { + /* Why 4, not 2? + * We need 2 more bytes for yet another "\r\n" - + * see near fdprintf(proxy_fd...) further below. + */ + int maxlen = (IOBUF_SIZE-4) - (int)(header_ptr - header_buf); + if (maxlen > 0) { + int len = strnlen(iobuf, maxlen); + memcpy(header_ptr, iobuf, len); + header_ptr += len; + header_ptr[0] = '\r'; + header_ptr[1] = '\n'; + header_ptr += 2; + } } #endif @@ -2401,7 +2406,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) } #if ENABLE_FEATURE_HTTPD_PROXY - if (proxy_entry != NULL) { + if (proxy_entry) { int proxy_fd; len_and_sockaddr *lsa; @@ -2423,7 +2428,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) header_ptr[0] = '\r'; header_ptr[1] = '\n'; header_ptr += 2; - write(proxy_fd, header_buf, header_ptr - header_buf); + full_write(proxy_fd, header_buf, header_ptr - header_buf); free(header_buf); /* on the order of 8k, free it */ cgi_io_loop_and_exit(proxy_fd, proxy_fd, length); } -- cgit v1.2.3-55-g6feb From bae8f7eaf2997938615ed4282d6d93d3aa1f3fc1 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 16 Apr 2019 10:00:06 +0200 Subject: httpd: do not percent-decode URI if proxying The proxying is documented as follows: P:/url:[http://]hostname[:port]/new/path Howeverm urlcopy is not a true copy anymore when it is fdprint'ed to proxy_fd, this is because percent_decode_in_place() is called after the copy is created. This breaks reverse proxying all URIs containing percent encoded spaces, e.g. - because a decoded URI will be printed out to proxy_fd instead of the original. The fix keeps the logic in place to canonicalize the uri first, before reverse proxying (one could argue that the uri should be proxied completely unaltered, except for the prefix rewrite). function old new delta handle_incoming_and_exit 2752 2792 +40 Signed-off-by: Denys Vlasenko --- networking/httpd.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/networking/httpd.c b/networking/httpd.c index c486197b8..bc0e386ea 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -1817,7 +1817,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what) log_and_exit(); } -static void send_HTTP_FORBIDDEN_and_exit_if_denied_ip(void) +static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(void) { Htaccess_IP *cur; @@ -2195,6 +2195,24 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) send_headers_and_exit(HTTP_NOT_FOUND); } +#if ENABLE_FEATURE_HTTPD_PROXY + proxy_entry = find_proxy_entry(urlcopy); + if (proxy_entry) + header_buf = header_ptr = xmalloc(IOBUF_SIZE); + else +#endif + { + /* (If not proxying,) decode URL escape sequences */ + tptr = percent_decode_in_place(urlcopy, /*strict:*/ 1); + if (tptr == NULL) + send_headers_and_exit(HTTP_BAD_REQUEST); + if (tptr == urlcopy + 1) { + /* '/' or NUL is encoded */ + send_headers_and_exit(HTTP_NOT_FOUND); + } +//should path canonicalization also be conditional on not proxying? + } + /* Canonicalize path */ /* Algorithm stolen from libbb bb_simplify_path(), * but don't strdup, retain trailing slash, protect root */ @@ -2224,7 +2242,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) } } *++urlp = *tptr; - if (*urlp == '\0') + if (*tptr == '\0') break; next_char: tptr++; @@ -2242,24 +2260,18 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) bb_error_msg("url:%s", urlcopy); tptr = urlcopy; - send_HTTP_FORBIDDEN_and_exit_if_denied_ip(); + if_ip_denied_send_HTTP_FORBIDDEN_and_exit(); while ((tptr = strchr(tptr + 1, '/')) != NULL) { /* have path1/path2 */ *tptr = '\0'; if (is_directory(urlcopy + 1, /*followlinks:*/ 1)) { /* may have subdir config */ parse_conf(urlcopy + 1, SUBDIR_PARSE); - send_HTTP_FORBIDDEN_and_exit_if_denied_ip(); + if_ip_denied_send_HTTP_FORBIDDEN_and_exit(); } *tptr = '/'; } -#if ENABLE_FEATURE_HTTPD_PROXY - proxy_entry = find_proxy_entry(urlcopy); - if (proxy_entry) - header_buf = header_ptr = xmalloc(IOBUF_SIZE); -#endif - if (http_major_version >= '0') { /* Request was with "... HTTP/nXXX", and n >= 0 */ -- cgit v1.2.3-55-g6feb From ff36bec49b2a1e398a5d7731a7049662f5c1b4ec Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 16 Apr 2019 10:14:50 +0200 Subject: httpd: add missing {} Signed-off-by: Denys Vlasenko --- networking/httpd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/networking/httpd.c b/networking/httpd.c index bc0e386ea..50e832c1f 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -1836,10 +1836,11 @@ static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(void) (unsigned char)(cur->mask) ); #endif - if ((rmt_ip & cur->mask) == cur->ip) + if ((rmt_ip & cur->mask) == cur->ip) { if (cur->allow_deny == 'A') return; send_headers_and_exit(HTTP_FORBIDDEN); + } } if (flg_deny_all) /* depends on whether we saw "D:*" */ -- cgit v1.2.3-55-g6feb From d0ae4103ddca21b7b765347611a9cf33f0cccd74 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 16 Apr 2019 11:07:37 +0200 Subject: httpd: fix handling of EOF in get_line() Signed-off-by: Denys Vlasenko --- networking/httpd.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/networking/httpd.c b/networking/httpd.c index 50e832c1f..53be500d3 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -1194,7 +1194,8 @@ static void send_headers_and_exit(int responseNum) } /* - * Read from the socket until '\n' or EOF. '\r' chars are removed. + * Read from the socket until '\n' or EOF. + * '\r' chars are removed. * '\n' is replaced with NUL. * Return number of characters read or 0 if nothing is read * ('\r' and '\n' are not counted). @@ -1202,29 +1203,30 @@ static void send_headers_and_exit(int responseNum) */ static int get_line(void) { - int count = 0; + int count; char c; alarm(HEADER_READ_TIMEOUT); + count = 0; while (1) { if (hdr_cnt <= 0) { hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof_hdr_buf); if (hdr_cnt <= 0) - break; + goto ret; hdr_ptr = hdr_buf; } - iobuf[count] = c = *hdr_ptr++; hdr_cnt--; - + c = *hdr_ptr++; if (c == '\r') continue; - if (c == '\n') { - iobuf[count] = '\0'; + if (c == '\n') break; - } + iobuf[count] = c; if (count < (IOBUF_SIZE - 1)) /* check overflow */ count++; } + ret: + iobuf[count] = '\0'; return count; } -- cgit v1.2.3-55-g6feb From fba665a8889ad7ec20f926bf2719be5c688ed829 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 16 Apr 2019 11:37:02 +0200 Subject: httpd: put all headers into environment as HTTP_UPPERCASED_HEADER=val Set up environment variables before running the CGI script. The variables will be named HTTP_ where the is the header name capitalized and all characters not matching [a-z] | [A-Z] | [0-9] replaced with '_'. function old new delta http_response 80 88 +8 http_response_type 20 22 +2 send_headers 718 715 -3 parse_conf 1481 1478 -3 get_line 128 110 -18 cgi_io_loop_and_exit 599 569 -30 send_cgi_and_exit 882 738 -144 handle_incoming_and_exit 2793 2592 -201 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/6 up/down: 10/-399) Total: -389 bytes text data bss dec hex filename 982178 485 7296 989959 f1b07 busybox_old 981675 485 7296 989456 f1910 busybox_unstripped Signed-off-by: Alexander Vickberg Signed-off-by: Denys Vlasenko --- networking/httpd.c | 225 ++++++++++++++++++++++++++++------------------------- 1 file changed, 120 insertions(+), 105 deletions(-) diff --git a/networking/httpd.c b/networking/httpd.c index 53be500d3..11c26a891 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -267,6 +267,7 @@ #define DEBUG 0 #define IOBUF_SIZE 8192 +#define MAX_HTTP_HEADERS_SIZE ((8*1024) - 16) #if PIPE_BUF >= IOBUF_SIZE # error "PIPE_BUF >= IOBUF_SIZE" #endif @@ -305,6 +306,13 @@ typedef struct Htaccess_Proxy { char *url_to; } Htaccess_Proxy; +typedef enum CGI_type { + CGI_NONE = 0, + CGI_NORMAL, + CGI_INDEX, + CGI_INTERPRETER, +} CGI_type; + enum { HTTP_OK = 200, HTTP_PARTIAL_CONTENT = 206, @@ -316,6 +324,7 @@ enum { HTTP_REQUEST_TIMEOUT = 408, HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */ HTTP_INTERNAL_SERVER_ERROR = 500, + HTTP_ENTITY_TOO_LARGE = 413, HTTP_CONTINUE = 100, #if 0 /* future use */ HTTP_SWITCHING_PROTOCOLS = 101, @@ -347,6 +356,7 @@ static const uint16_t http_response_type[] ALIGN2 = { HTTP_BAD_REQUEST, HTTP_FORBIDDEN, HTTP_INTERNAL_SERVER_ERROR, + HTTP_ENTITY_TOO_LARGE, #if 0 /* not implemented */ HTTP_CREATED, HTTP_ACCEPTED, @@ -377,6 +387,7 @@ static const struct { { "Bad Request", "Unsupported method" }, { "Forbidden", "" }, { "Internal Server Error", "Internal Server Error" }, + { "Entity Too Large", "Entity Too Large" }, #if 0 /* not implemented */ { "Created" }, { "Accepted" }, @@ -412,11 +423,6 @@ struct globals { IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;) IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;) - IF_FEATURE_HTTPD_CGI(char *referer;) - IF_FEATURE_HTTPD_CGI(char *user_agent;) - IF_FEATURE_HTTPD_CGI(char *host;) - IF_FEATURE_HTTPD_CGI(char *http_accept;) - IF_FEATURE_HTTPD_CGI(char *http_accept_language;) off_t file_size; /* -1 - unknown */ #if ENABLE_FEATURE_HTTPD_RANGES @@ -1439,23 +1445,17 @@ static void setenv1(const char *name, const char *value) * const char *url The requested URL (with leading /). * const char *orig_uri The original URI before rewriting (if any) * int post_len Length of the POST body. - * const char *cookie For set HTTP_COOKIE. - * const char *content_type For set CONTENT_TYPE. */ static void send_cgi_and_exit( const char *url, const char *orig_uri, const char *request, - int post_len, - const char *cookie, - const char *content_type) NORETURN; + int post_len) NORETURN; static void send_cgi_and_exit( const char *url, const char *orig_uri, const char *request, - int post_len, - const char *cookie, - const char *content_type) + int post_len) { struct fd_pair fromCgi; /* CGI -> httpd pipe */ struct fd_pair toCgi; /* httpd -> CGI pipe */ @@ -1533,26 +1533,14 @@ static void send_cgi_and_exit( #endif } } - setenv1("HTTP_USER_AGENT", G.user_agent); - if (G.http_accept) - setenv1("HTTP_ACCEPT", G.http_accept); - if (G.http_accept_language) - setenv1("HTTP_ACCEPT_LANGUAGE", G.http_accept_language); if (post_len) putenv(xasprintf("CONTENT_LENGTH=%u", post_len)); - if (cookie) - setenv1("HTTP_COOKIE", cookie); - if (content_type) - setenv1("CONTENT_TYPE", content_type); #if ENABLE_FEATURE_HTTPD_BASIC_AUTH if (remoteuser) { setenv1("REMOTE_USER", remoteuser); putenv((char*)"AUTH_TYPE=Basic"); } #endif - if (G.referer) - setenv1("HTTP_REFERER", G.referer); - setenv1("HTTP_HOST", G.host); /* set to "" if NULL */ /* setenv1("SERVER_NAME", safe_gethostname()); - don't do this, * just run "env SERVER_NAME=xyz httpd ..." instead */ @@ -2083,12 +2071,12 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) char *urlcopy; char *urlp; char *tptr; + unsigned header_len; #if ENABLE_FEATURE_HTTPD_CGI static const char request_HEAD[] ALIGN1 = "HEAD"; const char *prequest; - char *cookie = NULL; - char *content_type = NULL; unsigned long length = 0; + enum CGI_type cgi_type = CGI_NONE; #elif ENABLE_FEATURE_HTTPD_PROXY #define prequest request_GET unsigned long length = 0; @@ -2201,7 +2189,9 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) #if ENABLE_FEATURE_HTTPD_PROXY proxy_entry = find_proxy_entry(urlcopy); if (proxy_entry) - header_buf = header_ptr = xmalloc(IOBUF_SIZE); + header_buf = header_ptr = xmalloc(MAX_HTTP_HEADERS_SIZE + 2); +//TODO: why we don't just start pumping data to proxy here, +//without decoding URL, saving headers and such??? else #endif { @@ -2275,31 +2265,79 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) *tptr = '/'; } + tptr = urlcopy + 1; /* skip first '/' */ + +#if ENABLE_FEATURE_HTTPD_CGI + if (is_prefixed_with(tptr, "cgi-bin/")) { + if (tptr[8] == '\0') { + /* protect listing "cgi-bin/" */ + send_headers_and_exit(HTTP_FORBIDDEN); + } + cgi_type = CGI_NORMAL; + } +#endif + + if (urlp[-1] == '/') { + /* When index_page string is appended to / URL, it overwrites + * the query string. If we fall back to call /cgi-bin/index.cgi, + * query string would be lost and not available to the CGI. + * Work around it by making a deep copy. + */ + if (ENABLE_FEATURE_HTTPD_CGI) + g_query = xstrdup(g_query); /* ok for NULL too */ + strcpy(urlp, index_page); + } + if (stat(tptr, &sb) == 0) { +#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR + char *suffix = strrchr(tptr, '.'); + if (suffix) { + Htaccess *cur; + for (cur = script_i; cur; cur = cur->next) { + if (strcmp(cur->before_colon + 1, suffix) == 0) { + cgi_type = CGI_INTERPRETER; + break; + } + } + } +#endif + if (!found_moved_temporarily) { + file_size = sb.st_size; + last_mod = sb.st_mtime; + } + } +#if ENABLE_FEATURE_HTTPD_CGI + else if (urlp[-1] == '/') { + /* It's a dir URL and there is no index.html + * Try cgi-bin/index.cgi */ + if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) { + cgi_type = CGI_INDEX; + } + } +#endif + urlp[0] = '\0'; + + header_len = 0; if (http_major_version >= '0') { /* Request was with "... HTTP/nXXX", and n >= 0 */ /* Read until blank line */ while (1) { - if (!get_line()) + int iobuf_len = get_line(); + if (!iobuf_len) break; /* EOF or error or empty line */ + header_len += iobuf_len + 2; + if (header_len >= MAX_HTTP_HEADERS_SIZE) + send_headers_and_exit(HTTP_ENTITY_TOO_LARGE); if (DEBUG) bb_error_msg("header: '%s'", iobuf); - #if ENABLE_FEATURE_HTTPD_PROXY if (proxy_entry) { - /* Why 4, not 2? - * We need 2 more bytes for yet another "\r\n" - - * see near fdprintf(proxy_fd...) further below. - */ - int maxlen = (IOBUF_SIZE-4) - (int)(header_ptr - header_buf); - if (maxlen > 0) { - int len = strnlen(iobuf, maxlen); - memcpy(header_ptr, iobuf, len); - header_ptr += len; - header_ptr[0] = '\r'; - header_ptr[1] = '\n'; - header_ptr += 2; - } + /* protected from overflow by header_len check above */ + memcpy(header_ptr, iobuf, iobuf_len); + header_ptr += iobuf_len; + header_ptr[0] = '\r'; + header_ptr[1] = '\n'; + header_ptr += 2; } #endif @@ -2321,30 +2359,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) if (errno || length > INT_MAX) send_headers_and_exit(HTTP_BAD_REQUEST); } - } -#endif -#if ENABLE_FEATURE_HTTPD_CGI - else if (STRNCASECMP(iobuf, "Cookie:") == 0) { - if (!cookie) /* in case they send millions of these, do not OOM */ - cookie = xstrdup(skip_whitespace(iobuf + sizeof("Cookie:")-1)); - } else if (STRNCASECMP(iobuf, "Content-Type:") == 0) { - if (!content_type) - content_type = xstrdup(skip_whitespace(iobuf + sizeof("Content-Type:")-1)); - } else if (STRNCASECMP(iobuf, "Referer:") == 0) { - if (!G.referer) - G.referer = xstrdup(skip_whitespace(iobuf + sizeof("Referer:")-1)); - } else if (STRNCASECMP(iobuf, "User-Agent:") == 0) { - if (!G.user_agent) - G.user_agent = xstrdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1)); - } else if (STRNCASECMP(iobuf, "Host:") == 0) { - if (!G.host) - G.host = xstrdup(skip_whitespace(iobuf + sizeof("Host:")-1)); - } else if (STRNCASECMP(iobuf, "Accept:") == 0) { - if (!G.http_accept) - G.http_accept = xstrdup(skip_whitespace(iobuf + sizeof("Accept:")-1)); - } else if (STRNCASECMP(iobuf, "Accept-Language:") == 0) { - if (!G.http_accept_language) - G.http_accept_language = xstrdup(skip_whitespace(iobuf + sizeof("Accept-Language:")-1)); + continue; } #endif #if ENABLE_FEATURE_HTTPD_BASIC_AUTH @@ -2360,6 +2375,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) /* decodeBase64() skips whitespace itself */ decodeBase64(tptr); authorized = check_user_passwd(urlcopy, tptr); + continue; } #endif #if ENABLE_FEATURE_HTTPD_RANGES @@ -2377,6 +2393,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) range_start = -1; } } + continue; } #endif #if ENABLE_FEATURE_HTTPD_GZIP @@ -2394,6 +2411,35 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) content_gzip = 1; //} } + continue; + } +#endif +#if ENABLE_FEATURE_HTTPD_CGI + if (cgi_type != CGI_NONE) { + bool ct = (STRNCASECMP(iobuf, "Content-Type:") == 0); + char *cp; + char *colon = strchr(iobuf, ':'); + + if (!colon) + continue; + cp = iobuf; + while (cp < colon) { + /* a-z => A-Z, not-alnum => _ */ + char c = (*cp & ~0x20); /* toupper for A-Za-z, undef for others */ + if ((unsigned)(c - 'A') <= ('Z' - 'A')) { + *cp++ = c; + continue; + } + if (!isdigit(*cp)) + *cp = '_'; + cp++; + } + /* "Content-Type:" gets no HTTP_ prefix, all others do */ + cp = xasprintf(ct ? "HTTP_%.*s=%s" + 5 : "HTTP_%.*s=%s", + (int)(colon - iobuf), iobuf, + skip_whitespace(colon + 1) + ); + putenv(cp); } #endif } /* while extra header reading */ @@ -2452,51 +2498,20 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) tptr = urlcopy + 1; /* skip first '/' */ #if ENABLE_FEATURE_HTTPD_CGI - if (is_prefixed_with(tptr, "cgi-bin/")) { - if (tptr[8] == '\0') { - /* protect listing "cgi-bin/" */ - send_headers_and_exit(HTTP_FORBIDDEN); - } - send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type); + if (cgi_type != CGI_NONE) { + send_cgi_and_exit( + (cgi_type == CGI_INDEX) ? "/cgi-bin/index.cgi" + /*CGI_NORMAL or CGI_INTERPRETER*/ : urlcopy, + urlcopy, prequest, length + ); } #endif if (urlp[-1] == '/') { - /* When index_page string is appended to / URL, it overwrites - * the query string. If we fall back to call /cgi-bin/index.cgi, - * query string would be lost and not available to the CGI. - * Work around it by making a deep copy. - */ - if (ENABLE_FEATURE_HTTPD_CGI) - g_query = xstrdup(g_query); /* ok for NULL too */ strcpy(urlp, index_page); } - if (stat(tptr, &sb) == 0) { -#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR - char *suffix = strrchr(tptr, '.'); - if (suffix) { - Htaccess *cur; - for (cur = script_i; cur; cur = cur->next) { - if (strcmp(cur->before_colon + 1, suffix) == 0) { - send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type); - } - } - } -#endif - file_size = sb.st_size; - last_mod = sb.st_mtime; - } -#if ENABLE_FEATURE_HTTPD_CGI - else if (urlp[-1] == '/') { - /* It's a dir URL and there is no index.html - * Try cgi-bin/index.cgi */ - if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) { - urlp[0] = '\0'; /* remove index_page */ - send_cgi_and_exit("/cgi-bin/index.cgi", urlcopy, prequest, length, cookie, content_type); - } - } - /* else fall through to send_file, it errors out if open fails: */ +#if ENABLE_FEATURE_HTTPD_CGI if (prequest != request_GET && prequest != request_HEAD) { /* POST for files does not make sense */ send_headers_and_exit(HTTP_NOT_IMPLEMENTED); -- cgit v1.2.3-55-g6feb From cf695976c7cf169495e749c8a907e47c269e0068 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 16 Apr 2019 11:58:28 +0200 Subject: httpd: remove duplicate "decode URL escape sequences" code Signed-off-by: Denys Vlasenko --- networking/httpd.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/networking/httpd.c b/networking/httpd.c index 11c26a891..8921c02a3 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -2177,15 +2177,6 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) g_query = tptr; } - /* Decode URL escape sequences */ - tptr = percent_decode_in_place(urlcopy, /*strict:*/ 1); - if (tptr == NULL) - send_headers_and_exit(HTTP_BAD_REQUEST); - if (tptr == urlcopy + 1) { - /* '/' or NUL is encoded */ - send_headers_and_exit(HTTP_NOT_FOUND); - } - #if ENABLE_FEATURE_HTTPD_PROXY proxy_entry = find_proxy_entry(urlcopy); if (proxy_entry) -- cgit v1.2.3-55-g6feb From c69f648457a552518f92c5a70689bd106f03ec13 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 16 Apr 2019 12:45:26 +0200 Subject: httpd: do not decode URL and headers if proxying - send all verbatim function old new delta handle_incoming_and_exit 2566 2385 -181 Signed-off-by: Denys Vlasenko --- networking/httpd.c | 114 +++++++++++++++++++++-------------------------------- 1 file changed, 46 insertions(+), 68 deletions(-) diff --git a/networking/httpd.c b/networking/httpd.c index 8921c02a3..205c434bf 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -2085,12 +2085,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) smallint authorized = -1; #endif char http_major_version; -#if ENABLE_FEATURE_HTTPD_PROXY - char http_minor_version; - char *header_buf = header_buf; /* for gcc */ - char *header_ptr = header_ptr; - Htaccess_Proxy *proxy_entry; -#endif + char *HTTP_slash; /* Allocation of iobuf is postponed until now * (IOW, server process doesn't need to waste 8k) */ @@ -2152,18 +2147,18 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) send_headers_and_exit(HTTP_BAD_REQUEST); /* Find end of URL and parse HTTP version, if any */ - http_major_version = '0'; - IF_FEATURE_HTTPD_PROXY(http_minor_version = '0';) - tptr = strchrnul(urlp, ' '); +//TODO: mayybe just reject all queries which have no " HTTP/xyz" suffix? +//Then 'http_major_version' can be deleted + http_major_version = ('0' - 1); /* "less than 0th" version */ + HTTP_slash = strchrnul(urlp, ' '); /* Is it " HTTP/"? */ - if (tptr[0] && strncmp(tptr + 1, HTTP_200, 5) == 0) { - http_major_version = tptr[6]; - IF_FEATURE_HTTPD_PROXY(http_minor_version = tptr[8];) + if (HTTP_slash[0] && strncmp(HTTP_slash + 1, HTTP_200, 5) == 0) { + http_major_version = HTTP_slash[6]; + *HTTP_slash++ = '\0'; } - *tptr = '\0'; /* Copy URL from after "GET "/"POST " to stack-allocated char[] */ - urlcopy = alloca((tptr - urlp) + 2 + strlen(index_page)); + urlcopy = alloca((HTTP_slash - urlp) + 2 + strlen(index_page)); /*if (urlcopy == NULL) * send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);*/ strcpy(urlcopy, urlp); @@ -2178,23 +2173,45 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) } #if ENABLE_FEATURE_HTTPD_PROXY - proxy_entry = find_proxy_entry(urlcopy); - if (proxy_entry) - header_buf = header_ptr = xmalloc(MAX_HTTP_HEADERS_SIZE + 2); -//TODO: why we don't just start pumping data to proxy here, -//without decoding URL, saving headers and such??? - else -#endif { - /* (If not proxying,) decode URL escape sequences */ - tptr = percent_decode_in_place(urlcopy, /*strict:*/ 1); - if (tptr == NULL) - send_headers_and_exit(HTTP_BAD_REQUEST); - if (tptr == urlcopy + 1) { - /* '/' or NUL is encoded */ - send_headers_and_exit(HTTP_NOT_FOUND); + int proxy_fd; + len_and_sockaddr *lsa; + Htaccess_Proxy *proxy_entry = find_proxy_entry(urlcopy); + + if (proxy_entry) { + lsa = host2sockaddr(proxy_entry->host_port, 80); + if (!lsa) + send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); + proxy_fd = socket(lsa->u.sa.sa_family, SOCK_STREAM, 0); + if (proxy_fd < 0) + send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); + if (connect(proxy_fd, &lsa->u.sa, lsa->len) < 0) + send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); + /* Config directive was of the form: + * P:/url:[http://]hostname[:port]/new/path + * When /urlSFX is requested, reverse proxy it + * to http://hostname[:port]/new/pathSFX + */ + fdprintf(proxy_fd, "%s %s%s%s%s %s\r\n", + prequest, /* "GET" or "POST" */ + proxy_entry->url_to, /* "/new/path" */ + urlcopy + strlen(proxy_entry->url_from), /* "SFX" */ + (g_query ? "?" : ""), /* "?" (maybe) */ + (g_query ? g_query : ""), /* query string (maybe) */ + HTTP_slash /* HTTP/xyz" or "" */ + ); + cgi_io_loop_and_exit(proxy_fd, proxy_fd, /*max POST length:*/ INT_MAX); } -//should path canonicalization also be conditional on not proxying? + } +#endif + + /* Decode URL escape sequences */ + tptr = percent_decode_in_place(urlcopy, /*strict:*/ 1); + if (tptr == NULL) + send_headers_and_exit(HTTP_BAD_REQUEST); + if (tptr == urlcopy + 1) { + /* '/' or NUL is encoded */ + send_headers_and_exit(HTTP_NOT_FOUND); } /* Canonicalize path */ @@ -2321,16 +2338,6 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) send_headers_and_exit(HTTP_ENTITY_TOO_LARGE); if (DEBUG) bb_error_msg("header: '%s'", iobuf); -#if ENABLE_FEATURE_HTTPD_PROXY - if (proxy_entry) { - /* protected from overflow by header_len check above */ - memcpy(header_ptr, iobuf, iobuf_len); - header_ptr += iobuf_len; - header_ptr[0] = '\r'; - header_ptr[1] = '\n'; - header_ptr += 2; - } -#endif #if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY /* Try and do our best to parse more lines */ @@ -2457,35 +2464,6 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) send_headers_and_exit(HTTP_MOVED_TEMPORARILY); } -#if ENABLE_FEATURE_HTTPD_PROXY - if (proxy_entry) { - int proxy_fd; - len_and_sockaddr *lsa; - - lsa = host2sockaddr(proxy_entry->host_port, 80); - if (lsa == NULL) - send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); - proxy_fd = socket(lsa->u.sa.sa_family, SOCK_STREAM, 0); - if (proxy_fd < 0) - send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); - if (connect(proxy_fd, &lsa->u.sa, lsa->len) < 0) - send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); - fdprintf(proxy_fd, "%s %s%s%s%s HTTP/%c.%c\r\n", - prequest, /* GET or POST */ - proxy_entry->url_to, /* url part 1 */ - urlcopy + strlen(proxy_entry->url_from), /* url part 2 */ - (g_query ? "?" : ""), /* "?" (maybe) */ - (g_query ? g_query : ""), /* query string (maybe) */ - http_major_version, http_minor_version); - header_ptr[0] = '\r'; - header_ptr[1] = '\n'; - header_ptr += 2; - full_write(proxy_fd, header_buf, header_ptr - header_buf); - free(header_buf); /* on the order of 8k, free it */ - cgi_io_loop_and_exit(proxy_fd, proxy_fd, length); - } -#endif - tptr = urlcopy + 1; /* skip first '/' */ #if ENABLE_FEATURE_HTTPD_CGI -- cgit v1.2.3-55-g6feb From 44f5b6a1cb66ee0a6d253de306b167baf33d02c9 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 16 Apr 2019 12:59:20 +0200 Subject: httpd: check denied IPs even before reading 1st query line Signed-off-by: Denys Vlasenko --- networking/httpd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/networking/httpd.c b/networking/httpd.c index 205c434bf..d29335c3c 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -2113,6 +2113,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) if (verbose > 2) bb_error_msg("connected"); } + if_ip_denied_send_HTTP_FORBIDDEN_and_exit(); /* Install timeout handler. get_line() needs it. */ signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit); @@ -2147,7 +2148,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) send_headers_and_exit(HTTP_BAD_REQUEST); /* Find end of URL and parse HTTP version, if any */ -//TODO: mayybe just reject all queries which have no " HTTP/xyz" suffix? +//TODO: maybe just reject all queries which have no " HTTP/xyz" suffix? //Then 'http_major_version' can be deleted http_major_version = ('0' - 1); /* "less than 0th" version */ HTTP_slash = strchrnul(urlp, ' '); @@ -2261,7 +2262,6 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) bb_error_msg("url:%s", urlcopy); tptr = urlcopy; - if_ip_denied_send_HTTP_FORBIDDEN_and_exit(); while ((tptr = strchr(tptr + 1, '/')) != NULL) { /* have path1/path2 */ *tptr = '\0'; -- cgit v1.2.3-55-g6feb From 62ba9e5ac3f158eb36b365af43f8d7d680710963 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 16 Apr 2019 13:18:12 +0200 Subject: httpd: make rmt_ip variable local function old new delta handle_incoming_and_exit 2385 2398 +13 if_ip_denied_send_HTTP_FORBIDDEN_and_exit 51 54 +3 get_line 110 106 -4 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/1 up/down: 16/-4) Total: 12 bytes Signed-off-by: Denys Vlasenko --- networking/httpd.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/networking/httpd.c b/networking/httpd.c index d29335c3c..7cbf9afef 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -407,7 +407,6 @@ struct globals { /* client can handle gzip / we are going to send gzip */ smallint content_gzip; #endif - unsigned rmt_ip; /* used for IP-based allow/deny rules */ time_t last_mod; char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */ const char *bind_addr_or_port; @@ -458,7 +457,6 @@ struct globals { #else # define content_gzip 0 #endif -#define rmt_ip (G.rmt_ip ) #define bind_addr_or_port (G.bind_addr_or_port) #define g_query (G.g_query ) #define opt_c_configFile (G.opt_c_configFile ) @@ -1207,9 +1205,9 @@ static void send_headers_and_exit(int responseNum) * ('\r' and '\n' are not counted). * Data is returned in iobuf. */ -static int get_line(void) +static unsigned get_line(void) { - int count; + unsigned count; char c; alarm(HEADER_READ_TIMEOUT); @@ -1807,7 +1805,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what) log_and_exit(); } -static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(void) +static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip) { Htaccess_IP *cur; @@ -1826,7 +1824,7 @@ static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(void) (unsigned char)(cur->mask) ); #endif - if ((rmt_ip & cur->mask) == cur->ip) { + if ((remote_ip & cur->mask) == cur->ip) { if (cur->allow_deny == 'A') return; send_headers_and_exit(HTTP_FORBIDDEN); @@ -2071,7 +2069,10 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) char *urlcopy; char *urlp; char *tptr; - unsigned header_len; + unsigned remote_ip; +#if ENABLE_FEATURE_HTTPD_CGI + unsigned total_headers_len; +#endif #if ENABLE_FEATURE_HTTPD_CGI static const char request_HEAD[] ALIGN1 = "HEAD"; const char *prequest; @@ -2091,16 +2092,16 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) * (IOW, server process doesn't need to waste 8k) */ iobuf = xmalloc(IOBUF_SIZE); - rmt_ip = 0; + remote_ip = 0; if (fromAddr->u.sa.sa_family == AF_INET) { - rmt_ip = ntohl(fromAddr->u.sin.sin_addr.s_addr); + remote_ip = ntohl(fromAddr->u.sin.sin_addr.s_addr); } #if ENABLE_FEATURE_IPV6 if (fromAddr->u.sa.sa_family == AF_INET6 && fromAddr->u.sin6.sin6_addr.s6_addr32[0] == 0 && fromAddr->u.sin6.sin6_addr.s6_addr32[1] == 0 && ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[2]) == 0xffff) - rmt_ip = ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[3]); + remote_ip = ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[3]); #endif if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) { /* NB: can be NULL (user runs httpd -i by hand?) */ @@ -2113,7 +2114,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) if (verbose > 2) bb_error_msg("connected"); } - if_ip_denied_send_HTTP_FORBIDDEN_and_exit(); + if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip); /* Install timeout handler. get_line() needs it. */ signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit); @@ -2268,7 +2269,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) if (is_directory(urlcopy + 1, /*followlinks:*/ 1)) { /* may have subdir config */ parse_conf(urlcopy + 1, SUBDIR_PARSE); - if_ip_denied_send_HTTP_FORBIDDEN_and_exit(); + if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip); } *tptr = '/'; } @@ -2324,21 +2325,25 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) #endif urlp[0] = '\0'; - header_len = 0; +#if ENABLE_FEATURE_HTTPD_CGI + total_headers_len = 0; +#endif if (http_major_version >= '0') { /* Request was with "... HTTP/nXXX", and n >= 0 */ /* Read until blank line */ while (1) { - int iobuf_len = get_line(); + unsigned iobuf_len = get_line(); if (!iobuf_len) break; /* EOF or error or empty line */ - header_len += iobuf_len + 2; - if (header_len >= MAX_HTTP_HEADERS_SIZE) +#if ENABLE_FEATURE_HTTPD_CGI + /* Prevent unlimited growth of HTTP_xyz envvars */ + total_headers_len += iobuf_len; + if (total_headers_len >= MAX_HTTP_HEADERS_SIZE) send_headers_and_exit(HTTP_ENTITY_TOO_LARGE); +#endif if (DEBUG) bb_error_msg("header: '%s'", iobuf); - #if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY /* Try and do our best to parse more lines */ if ((STRNCASECMP(iobuf, "Content-Length:") == 0)) { -- cgit v1.2.3-55-g6feb From 2efa726b22e9ebb7ee6c192a0fea0c478a857219 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 16 Apr 2019 13:35:56 +0200 Subject: httpd: extract query string only after proxying check function old new delta handle_incoming_and_exit 2398 2370 -28 Signed-off-by: Denys Vlasenko --- networking/httpd.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/networking/httpd.c b/networking/httpd.c index 7cbf9afef..2b0acd7dc 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -411,7 +411,7 @@ struct globals { char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */ const char *bind_addr_or_port; - const char *g_query; + char *g_query; const char *opt_c_configFile; const char *home_httpd; const char *index_page; @@ -2166,14 +2166,6 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) strcpy(urlcopy, urlp); /* NB: urlcopy ptr is never changed after this */ - /* Extract url args if present */ - /* g_query = NULL; - already is */ - tptr = strchr(urlcopy, '?'); - if (tptr) { - *tptr++ = '\0'; - g_query = tptr; - } - #if ENABLE_FEATURE_HTTPD_PROXY { int proxy_fd; @@ -2194,12 +2186,10 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) * When /urlSFX is requested, reverse proxy it * to http://hostname[:port]/new/pathSFX */ - fdprintf(proxy_fd, "%s %s%s%s%s %s\r\n", + fdprintf(proxy_fd, "%s %s%s %s\r\n", prequest, /* "GET" or "POST" */ proxy_entry->url_to, /* "/new/path" */ urlcopy + strlen(proxy_entry->url_from), /* "SFX" */ - (g_query ? "?" : ""), /* "?" (maybe) */ - (g_query ? g_query : ""), /* query string (maybe) */ HTTP_slash /* HTTP/xyz" or "" */ ); cgi_io_loop_and_exit(proxy_fd, proxy_fd, /*max POST length:*/ INT_MAX); @@ -2207,6 +2197,11 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) } #endif + /* Extract url args if present */ + g_query = strchr(urlcopy, '?'); + if (g_query) + *g_query++ = '\0'; + /* Decode URL escape sequences */ tptr = percent_decode_in_place(urlcopy, /*strict:*/ 1); if (tptr == NULL) -- cgit v1.2.3-55-g6feb From a81700bc0810b2cad59d450a380ece20a8eb96a4 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Mon, 15 Apr 2019 10:48:29 +0100 Subject: hush: add bash-compatible EPOCH variables Bash 5.0 added the dynamic variable EPOCHSECONDS and EPOCHREALTIME which return the number of seconds since the Unix Epoch as an integer or float. These are useful for logging or tracing. function old new delta get_local_var_value 207 302 +95 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/0 up/down: 95/0) Total: 95 bytes text data bss dec hex filename 938702 4203 1888 944793 e6a99 busybox_old 938797 4203 1888 944888 e6af8 busybox_unstripped Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- shell/hush.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/shell/hush.c b/shell/hush.c index fa9afa38e..d745148f4 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -379,6 +379,7 @@ #define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT #define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT #define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT +#define BASH_EPOCH_VARS ENABLE_HUSH_BASH_COMPAT #define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST) #define BASH_READ_D ENABLE_HUSH_BASH_COMPAT @@ -1011,6 +1012,9 @@ struct globals { int debug_indent; #endif struct sigaction sa; +#if BASH_EPOCH_VARS + char epoch_buf[sizeof("%lu.nnnnnn") + sizeof(long)*3]; +#endif #if ENABLE_FEATURE_EDITING char user_input_buf[CONFIG_FEATURE_EDITING_MAX_LEN]; #endif @@ -2227,6 +2231,22 @@ static const char* FAST_FUNC get_local_var_value(const char *name) #if ENABLE_HUSH_RANDOM_SUPPORT if (strcmp(name, "RANDOM") == 0) return utoa(next_random(&G.random_gen)); +#endif +#if BASH_EPOCH_VARS + { + const char *fmt = NULL; + if (strcmp(name, "EPOCHSECONDS") == 0) + fmt = "%lu"; + else if (strcmp(name, "EPOCHREALTIME") == 0) + fmt = "%lu.%06u"; + if (fmt) { + struct timeval tv; + gettimeofday(&tv, NULL); + sprintf(G.epoch_buf, fmt, (unsigned long)tv.tv_sec, + (unsigned)tv.tv_usec); + return G.epoch_buf; + } + } #endif return NULL; } -- cgit v1.2.3-55-g6feb From d96c69d87619e62a88cad1eeda795ac36b417c1c Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Mon, 15 Apr 2019 10:49:35 +0100 Subject: ash: an unset dynamic variable should not be dynamic Commit b28d4c346 (ash: [VAR] Move unsetvar functionality into setvareq) dropped the code that caused dynamic variables to lose their special properties when unset. Add it back again. function old new delta setvareq 346 360 +14 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/0 up/down: 14/0) Total: 14 bytes Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- shell/ash.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/shell/ash.c b/shell/ash.c index 34d5d6d68..255d57e62 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -2364,6 +2364,10 @@ setvareq(char *s, int flags) } flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); +#if ENABLE_ASH_RANDOM_SUPPORT + if (flags & VUNSET) + flags &= ~VDYNAMIC; +#endif } else { /* variable s is not found */ if (flags & VNOSET) -- cgit v1.2.3-55-g6feb From 1d37186fe2361f80c821e334cc61f41e2f4eeb72 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Mon, 15 Apr 2019 10:52:05 +0100 Subject: ash: add bash-compatible EPOCH variables Bash 5.0 added the dynamic variable EPOCHSECONDS and EPOCHREALTIME which return the number of seconds since the Unix Epoch as an integer or float. These are useful for logging or tracing. function old new delta change_epoch - 78 +78 .rodata 175167 175235 +68 varinit_data 264 312 +48 change_seconds - 24 +24 change_realtime - 24 +24 ------------------------------------------------------------------------------ (add/remove: 3/0 grow/shrink: 2/0 up/down: 242/0) Total: 242 bytes text data bss dec hex filename 938508 4203 1888 944599 e69d7 busybox_old 938702 4203 1888 944793 e6a99 busybox_unstripped v2: Cast tv_sec and tv_usec to unsigned quantities. Add brackets to macros. Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- shell/ash.c | 77 ++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 255d57e62..b707d00d0 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -220,6 +220,7 @@ #define BASH_SOURCE ENABLE_ASH_BASH_COMPAT #define BASH_PIPEFAIL ENABLE_ASH_BASH_COMPAT #define BASH_HOSTNAME_VAR ENABLE_ASH_BASH_COMPAT +#define BASH_EPOCH_VARS ENABLE_ASH_BASH_COMPAT #define BASH_SHLVL_VAR ENABLE_ASH_BASH_COMPAT #define BASH_XTRACEFD ENABLE_ASH_BASH_COMPAT #define BASH_READ_D ENABLE_ASH_BASH_COMPAT @@ -2053,6 +2054,10 @@ static void changepath(const char *) FAST_FUNC; #if ENABLE_ASH_RANDOM_SUPPORT static void change_random(const char *) FAST_FUNC; #endif +#if BASH_EPOCH_VARS +static void change_seconds(const char *) FAST_FUNC; +static void change_realtime(const char *) FAST_FUNC; +#endif static const struct { int flags; @@ -2079,6 +2084,10 @@ static const struct { #if ENABLE_ASH_RANDOM_SUPPORT { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random }, #endif +#if BASH_EPOCH_VARS + { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHSECONDS", change_seconds }, + { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHREALTIME", change_realtime }, +#endif #if ENABLE_LOCALE_SUPPORT { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all }, { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype }, @@ -2110,26 +2119,26 @@ extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var; #define linenovar (G_var.linenovar ) #define vifs varinit[0] #if ENABLE_ASH_MAIL -# define vmail (&vifs)[1] -# define vmpath (&vmail)[1] -# define vpath (&vmpath)[1] -#else -# define vpath (&vifs)[1] -#endif -#define vps1 (&vpath)[1] -#define vps2 (&vps1)[1] -#define vps4 (&vps2)[1] +# define vmail varinit[1] +# define vmpath varinit[2] +#endif +#define VAR_OFFSET1 (ENABLE_ASH_MAIL*2) +#define vpath varinit[VAR_OFFSET1 + 1] +#define vps1 varinit[VAR_OFFSET1 + 2] +#define vps2 varinit[VAR_OFFSET1 + 3] +#define vps4 varinit[VAR_OFFSET1 + 4] #if ENABLE_ASH_GETOPTS -# define voptind (&vps4)[1] -# define vlineno (&voptind)[1] -# if ENABLE_ASH_RANDOM_SUPPORT -# define vrandom (&vlineno)[1] -# endif -#else -# define vlineno (&vps4)[1] -# if ENABLE_ASH_RANDOM_SUPPORT -# define vrandom (&vlineno)[1] -# endif +# define voptind varinit[VAR_OFFSET1 + 5] +#endif +#define VAR_OFFSET2 (VAR_OFFSET1 + ENABLE_ASH_GETOPTS) +#define vlineno varinit[VAR_OFFSET2 + 5] +#if ENABLE_ASH_RANDOM_SUPPORT +# define vrandom varinit[VAR_OFFSET2 + 6] +#endif +#define VAR_OFFSET3 (VAR_OFFSET2 + ENABLE_ASH_RANDOM_SUPPORT) +#if BASH_EPOCH_VARS +# define vepochs varinit[VAR_OFFSET3 + 6] +# define vepochr varinit[VAR_OFFSET3 + 7] #endif #define INIT_G_var() do { \ unsigned i; \ @@ -2268,7 +2277,7 @@ lookupvar(const char *name) v = *findvar(hashvar(name), name); if (v) { -#if ENABLE_ASH_RANDOM_SUPPORT +#if ENABLE_ASH_RANDOM_SUPPORT || BASH_EPOCH_VARS /* * Dynamic variables are implemented roughly the same way they are * in bash. Namely, they're "special" so long as they aren't unset. @@ -2364,7 +2373,7 @@ setvareq(char *s, int flags) } flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); -#if ENABLE_ASH_RANDOM_SUPPORT +#if ENABLE_ASH_RANDOM_SUPPORT || BASH_EPOCH_VARS if (flags & VUNSET) flags &= ~VDYNAMIC; #endif @@ -11249,6 +11258,32 @@ change_random(const char *value) } #endif +#if BASH_EPOCH_VARS +static void FAST_FUNC +change_epoch(struct var *vepoch, const char *fmt) +{ + struct timeval tv; + char buffer[sizeof("%lu.nnnnnn") + sizeof(long)*3]; + + gettimeofday(&tv, NULL); + sprintf(buffer, fmt, (unsigned long)tv.tv_sec, (unsigned)tv.tv_usec); + setvar(vepoch->var_text, buffer, VNOFUNC); + vepoch->flags &= ~VNOFUNC; +} + +static void FAST_FUNC +change_seconds(const char *value UNUSED_PARAM) +{ + change_epoch(&vepochs, "%lu"); +} + +static void FAST_FUNC +change_realtime(const char *value UNUSED_PARAM) +{ + change_epoch(&vepochr, "%lu.%06u"); +} +#endif + #if ENABLE_ASH_GETOPTS static int getopts(char *optstr, char *optvar, char **optfirst) -- cgit v1.2.3-55-g6feb