From d99dee944eabab5184c356027b0a7a9dd9e2541a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 2 Sep 2021 13:59:39 +0200 Subject: udhcpd: update --help to include -a MSEC function old new delta packed_usage 33886 33891 +5 Signed-off-by: Denys Vlasenko --- networking/udhcp/dhcpd.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c index 91f70970a..eef8a3b59 100644 --- a/networking/udhcp/dhcpd.c +++ b/networking/udhcp/dhcpd.c @@ -27,7 +27,7 @@ //kbuild:lib-$(CONFIG_FEATURE_UDHCP_RFC3397) += domain_codec.o //usage:#define udhcpd_trivial_usage -//usage: "[-fS] [-I ADDR]" IF_FEATURE_UDHCP_PORT(" [-P PORT]") " [CONFFILE]" +//usage: "[-fS] [-I ADDR] [-a MSEC]" IF_FEATURE_UDHCP_PORT(" [-P PORT]") " [CONFFILE]" //usage:#define udhcpd_full_usage "\n\n" //usage: "DHCP server\n" //usage: "\n -f Run in foreground" @@ -877,6 +877,12 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv) /* Setup the signal pipe on fds 3,4 - must be before openlog() */ udhcp_sp_setup(); +#define OPT_f (1 << 0) +#define OPT_S (1 << 1) +#define OPT_I (1 << 2) +#define OPT_v (1 << 3) +#define OPT_a (1 << 4) +#define OPT_P (1 << 5) opt = getopt32(argv, "^" "fSI:va:"IF_FEATURE_UDHCP_PORT("P:") "\0" @@ -887,24 +893,24 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv) , &str_a IF_FEATURE_UDHCP_PORT(, &str_P) IF_UDHCP_VERBOSE(, &dhcp_verbose) - ); - if (!(opt & 1)) { /* no -f */ + ); + if (!(opt & OPT_f)) { /* no -f */ bb_daemonize_or_rexec(0, argv); logmode = LOGMODE_NONE; } /* update argv after the possible vfork+exec in daemonize */ argv += optind; - if (opt & 2) { /* -S */ + if (opt & OPT_S) { openlog(applet_name, LOG_PID, LOG_DAEMON); logmode |= LOGMODE_SYSLOG; } - if (opt & 4) { /* -I */ + if (opt & OPT_I) { len_and_sockaddr *lsa = xhost_and_af2sockaddr(str_I, 0, AF_INET); server_data.server_nip = lsa->u.sin.sin_addr.s_addr; free(lsa); } #if ENABLE_FEATURE_UDHCP_PORT - if (opt & 32) { /* -P */ + if (opt & OPT_P) { SERVER_PORT = xatou16(str_P); CLIENT_PORT = SERVER_PORT + 1; } -- cgit v1.2.3-55-g6feb From 62d0c8e02872d444ba20b4bdf638ac26c509a3dd Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 2 Sep 2021 14:40:54 +0200 Subject: udhcpd: check config file for bad IP ranges (start > end) function old new delta .rodata 104209 104238 +29 read_config 208 225 +17 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 46/0) Total: 46 bytes Signed-off-by: Denys Vlasenko --- networking/udhcp/dhcpd.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c index eef8a3b59..b67dfc3bc 100644 --- a/networking/udhcp/dhcpd.c +++ b/networking/udhcp/dhcpd.c @@ -451,6 +451,8 @@ static NOINLINE void read_config(const char *file) server_data.start_ip = ntohl(server_data.start_ip); server_data.end_ip = ntohl(server_data.end_ip); + if (server_data.start_ip > server_data.end_ip) + bb_error_msg_and_die("bad start/end IP range in %s", file); } static void write_leases(void) @@ -858,7 +860,6 @@ int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int udhcpd_main(int argc UNUSED_PARAM, char **argv) { int server_socket = -1, retval; - uint8_t *state; unsigned timeout_end; unsigned num_ips; unsigned opt; @@ -966,6 +967,7 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv) struct dhcp_packet packet; int bytes; int tv; + uint8_t *msg_type; uint8_t *server_id_opt; uint8_t *requested_ip_opt; uint32_t requested_nip; @@ -1040,8 +1042,8 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv) bb_info_msg("not a REQUEST%s", ", ignoring packet"); continue; } - state = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE); - if (state == NULL || state[0] < DHCP_MINTYPE || state[0] > DHCP_MAXTYPE) { + msg_type = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE); + if (!msg_type || msg_type[0] < DHCP_MINTYPE || msg_type[0] > DHCP_MAXTYPE) { bb_info_msg("no or bad message type option%s", ", ignoring packet"); continue; } @@ -1077,7 +1079,7 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv) move_from_unaligned32(requested_nip, requested_ip_opt); } - switch (state[0]) { + switch (msg_type[0]) { case DHCPDISCOVER: log1("received %s", "DISCOVER"); -- cgit v1.2.3-55-g6feb From 3f2d969db9023e273a327418b32ebd4ed88893c4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 2 Sep 2021 16:23:24 +0200 Subject: udhcp: clarify aspects of relay operation, add TODOs and FIXMEs, tweak --help function old new delta packed_usage 33891 33920 +29 dhcprelay_main 943 926 -17 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 29/-17) Total: 12 bytes Signed-off-by: Denys Vlasenko --- networking/udhcp/dhcpd.c | 7 +++++ networking/udhcp/dhcprelay.c | 61 ++++++++++++++++++++++++++++---------------- 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c index b67dfc3bc..0f5edb75c 100644 --- a/networking/udhcp/dhcpd.c +++ b/networking/udhcp/dhcpd.c @@ -614,6 +614,10 @@ static void send_packet_to_relay(struct dhcp_packet *dhcp_pkt) udhcp_send_kernel_packet(dhcp_pkt, server_data.server_nip, SERVER_PORT, dhcp_pkt->gateway_nip, SERVER_PORT, + /* Yes, relay agents receive (and send) all their packets on SERVER_PORT, + * even those which are clients' requests and would normally + * (i.e. without relay) use CLIENT_PORT. See RFC 1542. + */ server_data.interface); } @@ -1025,6 +1029,9 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv) * socket read inside this call is restarted on caught signals. */ bytes = udhcp_recv_kernel_packet(&packet, server_socket); +//NB: we do not check source port here. Should we? +//It should be CLIENT_PORT for clients, +//or SERVER_PORT for relay agents (in which case giaddr must be != 0.0.0.0) if (bytes < 0) { /* bytes can also be -2 ("bad packet data") */ if (bytes == -1 && errno != EINTR) { diff --git a/networking/udhcp/dhcprelay.c b/networking/udhcp/dhcprelay.c index ef9447b4b..de3e4b0e1 100644 --- a/networking/udhcp/dhcprelay.c +++ b/networking/udhcp/dhcprelay.c @@ -17,7 +17,8 @@ //usage:#define dhcprelay_trivial_usage //usage: "CLIENT_IFACE[,CLIENT_IFACE2]... SERVER_IFACE [SERVER_IP]" //usage:#define dhcprelay_full_usage "\n\n" -//usage: "Relay DHCP requests between clients and server" +//usage: "Relay DHCP requests between clients and server.\n" +//usage: "Without SERVER_IP, requests are broadcast on SERVER_IFACE." #include "common.h" @@ -31,7 +32,7 @@ /* This list holds information about clients. The xid_* functions manipulate this list. */ struct xid_item { unsigned timestamp; - int client; + unsigned iface_no; uint32_t xid; struct sockaddr_in ip; struct xid_item *next; @@ -40,7 +41,7 @@ struct xid_item { #define dhcprelay_xid_list (*(struct xid_item*)bb_common_bufsiz1) #define INIT_G() do { setup_common_bufsiz(); } while (0) -static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, int client) +static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, unsigned iface_no) { struct xid_item *item; @@ -50,7 +51,7 @@ static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, int client /* add xid entry */ item->ip = *ip; item->xid = xid; - item->client = client; + item->iface_no = iface_no; item->timestamp = monotonic_sec(); item->next = dhcprelay_xid_list.next; dhcprelay_xid_list.next = item; @@ -127,7 +128,7 @@ static int get_dhcp_packet_type(struct dhcp_packet *p) * make_iface_list - parses client/server interface names * returns array */ -static char **make_iface_list(char **client_and_server_ifaces, int *client_number) +static char **make_iface_list(char **client_and_server_ifaces, unsigned *client_number) { char *s, **iface_list; int i, cn; @@ -165,9 +166,9 @@ static char **make_iface_list(char **client_and_server_ifaces, int *client_numbe /* Creates listen sockets (in fds) bound to client and server ifaces, * and returns numerically max fd. */ -static int init_sockets(char **iface_list, int num_clients, int *fds) +static unsigned init_sockets(char **iface_list, unsigned num_clients, int *fds) { - int i, n; + unsigned i, n; n = 0; for (i = 0; i < num_clients; i++) { @@ -195,13 +196,14 @@ static int sendto_ip4(int sock, const void *msg, int msg_len, struct sockaddr_in * p - packet to send * client - number of the client */ -static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, int *fds, +static void pass_to_server(struct dhcp_packet *p, int packet_len, unsigned from_iface_no, int *fds, struct sockaddr_in *client_addr, struct sockaddr_in *server_addr) { int type; /* check packet_type */ type = get_dhcp_packet_type(p); +//FIXME: the above does not consider packet_len! if (type != DHCPDISCOVER && type != DHCPREQUEST && type != DHCPDECLINE && type != DHCPRELEASE && type != DHCPINFORM @@ -210,7 +212,10 @@ static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, in } /* create new xid entry */ - xid_add(p->xid, client_addr, client); + xid_add(p->xid, client_addr, from_iface_no); +//TODO: since we key request/reply pairs on xid values, shouldn't we drop new requests +//with xid accidentally matching a xid of one of requests we currently hold +//waiting for their replies? /* forward request to server */ /* note that we send from fds[0] which is bound to SERVER_PORT (67). @@ -229,25 +234,30 @@ static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds) int type; struct xid_item *item; - /* check xid */ - item = xid_find(p->xid); - if (!item) { - return; - } - /* check packet type */ type = get_dhcp_packet_type(p); +//FIXME: the above does not consider packet_len! if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) { return; } + /* check xid */ + item = xid_find(p->xid); + if (!item) { + return; + } +//NB: RFC 1542 section 4.1 seems to envision the logic that +//relay agents use giaddr (dhcp_msg.gateway_nip in our code) +//to find out on which interface to reply. +//(server is meant to copy giaddr from our request packet to its reply). +//Above, we don't use that logic, instead we use xid as a key. + //TODO: also do it if (p->flags & htons(BROADCAST_FLAG)) is set! if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY)) item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST); - if (sendto_ip4(fds[item->client], p, packet_len, &item->ip) != 0) { - return; /* send error occurred */ - } + sendto_ip4(fds[item->iface_no], p, packet_len, &item->ip); + /* ^^^ if send error occurred, we can't do much, hence no check */ /* remove xid entry */ xid_del(p->xid); @@ -259,7 +269,7 @@ int dhcprelay_main(int argc UNUSED_PARAM, char **argv) struct sockaddr_in server_addr; char **iface_list; int *fds; - int num_sockets, max_socket; + unsigned num_sockets, max_socket; uint32_t our_nip; INIT_G(); @@ -293,7 +303,7 @@ int dhcprelay_main(int argc UNUSED_PARAM, char **argv) // every N minutes? fd_set rfds; struct timeval tv; - int i; + unsigned i; FD_ZERO(&rfds); for (i = 0; i < num_sockets; i++) @@ -304,15 +314,17 @@ int dhcprelay_main(int argc UNUSED_PARAM, char **argv) int packlen; struct dhcp_packet dhcp_msg; - /* server */ + /* from server */ if (FD_ISSET(fds[0], &rfds)) { packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]); +//NB: we do not check source port here. Should we? +//It should be SERVER_PORT. if (packlen > 0) { pass_to_client(&dhcp_msg, packlen, fds); } } - /* clients */ + /* from clients */ for (i = 1; i < num_sockets; i++) { struct sockaddr_in client_addr; socklen_t addr_size; @@ -325,6 +337,11 @@ int dhcprelay_main(int argc UNUSED_PARAM, char **argv) (struct sockaddr *)(&client_addr), &addr_size); if (packlen <= 0) continue; +//NB: we do not check source port here. Should we? +//It should be CLIENT_PORT for clients. +//It can be SERVER_PORT for relay agents (in which case giaddr must be != 0.0.0.0), +//but is it even supported to chain relay agents like this? +//(we still copy client_addr.port and use it to reply to the port we got request from) /* Get our IP on corresponding client_iface */ // RFC 1542 -- cgit v1.2.3-55-g6feb From f02691939eba5c043a90c0f1f428d4124dc83a1c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 2 Sep 2021 17:09:12 +0200 Subject: dhcprelay: change two more variables to unsigned Signed-off-by: Denys Vlasenko --- networking/udhcp/dhcprelay.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/networking/udhcp/dhcprelay.c b/networking/udhcp/dhcprelay.c index de3e4b0e1..2352c34a2 100644 --- a/networking/udhcp/dhcprelay.c +++ b/networking/udhcp/dhcprelay.c @@ -131,7 +131,7 @@ static int get_dhcp_packet_type(struct dhcp_packet *p) static char **make_iface_list(char **client_and_server_ifaces, unsigned *client_number) { char *s, **iface_list; - int i, cn; + unsigned i, cn; /* get number of items */ cn = 2; /* 1 server iface + at least 1 client one */ -- cgit v1.2.3-55-g6feb From 4a36ef11ac4d46ae651cbedc440ca6853415283a Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Mon, 30 Aug 2021 20:31:42 +0100 Subject: ash: regressions in process substitution Stacy Harper reports that this script: test() { . /tmp/bb_test; } echo "export TEST=foo" >/tmp/bb_test test 2>/dev/null echo "$TEST" correctly prints 'foo' in BusyBox 1.33 but hangs in 1.34. Bisection suggested the problem was caused by commit a1b0d3856 (ash: add process substitution in bash-compatibility mode). Removing the call to unwindredir() in cmdloop() introduced in that commit makes the script work again. Additionally, these examples of process substitution: while true; do cat <(echo hi); done f() { while true; do cat <(echo hi); done } f result in running out of file descriptors. This is a regression from v5 of the process substitution patch caused by changes to evalcommand() not being transferred to v6. function old new delta static.pushredir - 99 +99 evalcommand 1729 1750 +21 exitreset 69 86 +17 cmdloop 372 365 -7 unwindredir 28 - -28 pushredir 112 - -112 ------------------------------------------------------------------------------ (add/remove: 1/2 grow/shrink: 2/1 up/down: 137/-147) Total: -10 bytes Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- shell/ash.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index b5947147a..53c140930 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -10278,6 +10278,9 @@ evalcommand(union node *cmd, int flags) /* First expand the arguments. */ TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); +#if BASH_PROCESS_SUBST + redir_stop = redirlist; +#endif file_stop = g_parsefile; back_exitstatus = 0; @@ -10356,7 +10359,11 @@ evalcommand(union node *cmd, int flags) lastarg = nargv[-1]; expredir(cmd->ncmd.redirect); +#if !BASH_PROCESS_SUBST redir_stop = pushredir(cmd->ncmd.redirect); +#else + pushredir(cmd->ncmd.redirect); +#endif preverrout_fd = 2; if (BASH_XTRACEFD && xflag) { /* NB: bash closes fd == $BASH_XTRACEFD when it is changed. @@ -13476,9 +13483,6 @@ cmdloop(int top) #if JOBS if (doing_jobctl) showjobs(SHOW_CHANGED|SHOW_STDERR); -#endif -#if BASH_PROCESS_SUBST - unwindredir(NULL); #endif inter = 0; if (iflag && top) { -- cgit v1.2.3-55-g6feb From d7e39f26d711b5736a3a478b0d8b353c0c02e958 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 2 Sep 2021 23:53:48 +0200 Subject: examples/var_service/fw/run: allow extif's to be more than one iface Signed-off-by: Denys Vlasenko --- examples/var_service/fw/run | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/examples/var_service/fw/run b/examples/var_service/fw/run index 41078d0ab..15c2b2f0a 100755 --- a/examples/var_service/fw/run +++ b/examples/var_service/fw/run @@ -179,7 +179,9 @@ ipt="iptables -t nat -A OUTPUT" # OUTGOING TRAFFIC ipt="iptables -t nat -A POSTROUTING" # Masquerade boxes on my private net -doit $ipt -s 192.168.0.0/24 -o $extif -j MASQUERADE +for e in $extif; do + doit $ipt -s 192.168.0.0/24 -o $e -j MASQUERADE +done # *** mangle *** ### DEBUG @@ -204,7 +206,9 @@ fi doit $ipt -p tcp -j REJECT # Anything else isn't ok. REJECT = irc opens faster # (it probes proxy ports, DROP will incur timeout delays) ipt="iptables -t filter -A INPUT" -doit $ipt -i $extif -j iext +for e in $extif; do + doit $ipt -i $e -j iext +done echo; echo "* Enabling forwarding" @@ -222,12 +226,12 @@ echo; echo "* Routing:" ip r l echo; echo "* Firewall:" { -echo '---FILTER--'; -iptables -v -L -x -n; -echo '---NAT-----'; -iptables -t nat -v -L -x -n; -echo '---MANGLE--'; -iptables -t mangle -v -L -x -n; +echo '---FILTER--' +iptables -v -L -x -n +echo '---NAT-----' +iptables -t nat -v -L -x -n +echo '---MANGLE--' +iptables -t mangle -v -L -x -n } \ | grep -v '^$' | grep -Fv 'bytes target' echo -- cgit v1.2.3-55-g6feb From 8aa626ffffbe7f7dfa6db8a37a0f841ce777085d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 4 Sep 2021 01:50:42 +0200 Subject: udhcp: add comments, no code changes Signed-off-by: Denys Vlasenko --- networking/udhcp/common.h | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h index ca778dab8..e374771cb 100644 --- a/networking/udhcp/common.h +++ b/networking/udhcp/common.h @@ -38,13 +38,27 @@ struct dhcp_packet { #define BROADCAST_FLAG 0x8000 /* "I need broadcast replies" */ uint32_t ciaddr; /* client IP (if client is in BOUND, RENEW or REBINDING state) */ uint32_t yiaddr; /* 'your' (client) IP address */ - /* IP address of next server to use in bootstrap, returned in DHCPOFFER, DHCPACK by server */ + /* IP address of "next server" (usually meant to be an TFTP server) + * to use in bootstrap, returned in DHCPOFFER, DHCPACK by server: */ uint32_t siaddr_nip; - uint32_t gateway_nip; /* aka 'giaddr': relay agent IP address */ + /* RFC 951 (BOOTP): "place my (server) IP address in the 'siaddr' field" + * (IOW: unconditionally, not just if we are also a TFTP server). + * DHCP servers don't have to do this, they add SERVER_ID option + * to their reply packets to let client identify lease-giving server. + */ + uint32_t gateway_nip; /* aka 'giaddr': relay agent IP address, else 0 */ uint8_t chaddr[16]; /* link-layer client hardware address (MAC) */ uint8_t sname[64]; /* server host name (ASCIZ) */ + /* RFC 951 (BOOTP): "If the client wishes to restrict booting + * to a particular server name, it may place [it] in 'sname'" + */ uint8_t file[128]; /* boot file name (ASCIZ) */ - uint32_t cookie; /* fixed first four option bytes (99,130,83,99 dec) */ + /* RFC 951 (BOOTP): in client requests, "...can be a 'generic' name + * such as 'unix' or 'gateway'; this means 'boot the named program + * configured for my machine'" + */ + /* BOOTP fields end here, BOOTP says optional uint8_t vend[64] follows */ + uint32_t cookie; /* DHCP magic bytes: 99,130,83,99 decimal */ uint8_t options[DHCP_OPTIONS_BUFSIZE + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS]; }; #define DHCP_PKT_SNAME_LEN 64 @@ -142,18 +156,21 @@ struct dhcp_scan_state { //#define DHCP_NTP_SERVER 0x2a //#define DHCP_WINS_SERVER 0x2c #define DHCP_REQUESTED_IP 0x32 /* 50: sent by client if specific IP is wanted */ -#define DHCP_LEASE_TIME 0x33 /* 51: */ -#define DHCP_OPTION_OVERLOAD 0x34 /* 52: */ -#define DHCP_MESSAGE_TYPE 0x35 /* 53: */ +#define DHCP_LEASE_TIME 0x33 /* 51: 32bit big-endian */ +#define DHCP_OPTION_OVERLOAD 0x34 /* 52: 1 byte */ +#define DHCP_MESSAGE_TYPE 0x35 /* 53: 1 byte */ #define DHCP_SERVER_ID 0x36 /* 54: server's IP */ #define DHCP_PARAM_REQ 0x37 /* 55: list of options client wants */ //#define DHCP_ERR_MESSAGE 0x38 /* 56: error message when sending NAK etc */ -#define DHCP_MAX_SIZE 0x39 /* 57: */ +#define DHCP_MAX_SIZE 0x39 /* 57: 16bit big-endian */ +// 0x3a /* 58: from server: renew time, 32bit big-endian */ +// 0x3b /* 59: from server: rebind time, 32bit big-endian */ #define DHCP_VENDOR 0x3c /* 60: client's vendor (a string) */ #define DHCP_CLIENT_ID 0x3d /* 61: by default client's MAC addr, but may be arbitrarily long */ //#define DHCP_TFTP_SERVER_NAME 0x42 /* 66: same as 'sname' field */ //#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 */ +// 0x50 /* 80: rapid commit ("I'm ok with getting immediate ACK, not just OFFER"), 0 bytes */ #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 */ -- cgit v1.2.3-55-g6feb From f4ba69d47698c5357c32e21eb9122a5031b9b080 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 4 Sep 2021 17:00:22 +0200 Subject: shuf: make -i 99999999990-100000000000 work even on 32 bits function old new delta shuf_main 443 501 +58 .rodata 104238 104245 +7 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 65/0) Total: 65 bytes Signed-off-by: Denys Vlasenko --- coreutils/shuf.c | 45 +++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/coreutils/shuf.c b/coreutils/shuf.c index 77f8a8ff9..50483a25e 100644 --- a/coreutils/shuf.c +++ b/coreutils/shuf.c @@ -67,9 +67,10 @@ int shuf_main(int argc, char **argv) { unsigned opts; char *opt_i_str, *opt_n_str, *opt_o_str; - unsigned i; char **lines; + unsigned long long lo = lo; unsigned numlines, outlines; + unsigned i; char eol; opts = getopt32(argv, "^" @@ -89,8 +90,8 @@ int shuf_main(int argc, char **argv) } else if (opts & OPT_i) { /* create a range of numbers */ + unsigned long long hi; char *dash; - uintptr_t lo, hi; if (argv[0]) bb_show_usage(); @@ -100,27 +101,24 @@ int shuf_main(int argc, char **argv) bb_error_msg_and_die("bad range '%s'", opt_i_str); } *dash = '\0'; - if (sizeof(lo) == sizeof(int)) { - lo = xatou(opt_i_str); - hi = xatou(dash + 1); - } else - if (sizeof(lo) == sizeof(long)) { - lo = xatoul(opt_i_str); - hi = xatoul(dash + 1); - } else { - lo = xatoull(opt_i_str); - hi = xatoull(dash + 1); - } + lo = xatoull(opt_i_str); + hi = xatoull(dash + 1); *dash = '-'; - if (hi < lo) { + if (hi < lo) bb_error_msg_and_die("bad range '%s'", opt_i_str); + hi -= lo; + if (sizeof(size_t) > sizeof(numlines)) { + if (hi >= UINT_MAX) + bb_error_msg_and_die("bad range '%s'", opt_i_str); + } else { + if (hi >= UINT_MAX / sizeof(lines[0])) + bb_error_msg_and_die("bad range '%s'", opt_i_str); } - numlines = (hi+1) - lo; - lines = xmalloc(numlines * sizeof(lines[0])); + numlines = hi + 1; + lines = xmalloc((size_t)numlines * sizeof(lines[0])); for (i = 0; i < numlines; i++) { - lines[i] = (char*)lo; - lo++; + lines[i] = (char*)(uintptr_t)i; } } else { /* default - read lines from stdin or the input file */ @@ -163,14 +161,9 @@ int shuf_main(int argc, char **argv) eol = '\0'; for (i = numlines - outlines; i < numlines; i++) { - if (opts & OPT_i) { - if (sizeof(lines[0]) == sizeof(int)) - printf("%u%c", (unsigned)(uintptr_t)lines[i], eol); - else if (sizeof(lines[0]) == sizeof(long)) - printf("%lu%c", (unsigned long)(uintptr_t)lines[i], eol); - else - printf("%llu%c", (unsigned long long)(uintptr_t)lines[i], eol); - } else + if (opts & OPT_i) + printf("%llu%c", lo + (uintptr_t)lines[i], eol); + else printf("%s%c", lines[i], eol); } -- cgit v1.2.3-55-g6feb From 7d06d6e18651cb183a3723fa21ef62935ea08647 Mon Sep 17 00:00:00 2001 From: Daniel Thau Date: Thu, 2 Sep 2021 07:41:08 -0400 Subject: awk: fix printf %% A refactor of the awk printf code in e2e3802987266c98df0efdf40ad5da4b07df0113 appears to have broken the printf interpretation of two percent signs, which normally outputs only one percent sign. The patch below brings busybox awk printf behavior back into alignment with the pre-e2e380 behavior, the busybox printf util, and other common (awk and non-awk) printf implementations. function old new delta awk_printf 626 672 +46 Signed-off-by: Daniel Thau Signed-off-by: Denys Vlasenko --- editors/awk.c | 9 ++++++++- testsuite/awk.tests | 6 ++++++ testsuite/printf.tests | 5 +++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/editors/awk.c b/editors/awk.c index 3adbca7aa..f7b8ef0d3 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -2346,8 +2346,15 @@ static char *awk_printf(node *n, size_t *len) size_t slen; s = f; - while (*f && (*f != '%' || *++f == '%')) + while (*f && *f != '%') f++; + c = *++f; + if (c == '%') { /* double % */ + slen = f - s; + s = xstrndup(s, slen); + f++; + goto tail; + } while (*f && !isalpha(*f)) { if (*f == '*') syntax_error("%*x formats are not supported"); diff --git a/testsuite/awk.tests b/testsuite/awk.tests index dc2ae2e11..bcaafe8fd 100755 --- a/testsuite/awk.tests +++ b/testsuite/awk.tests @@ -463,4 +463,10 @@ testing "awk \"cmd\" | getline" \ "HELLO\n" \ '' '' +# printf %% should print one % (had a bug where it didn't) +testing 'awk printf %% prints one %' \ + "awk 'BEGIN { printf \"%%\n\" }'" \ + "%\n" \ + '' '' + exit $FAILCOUNT diff --git a/testsuite/printf.tests b/testsuite/printf.tests index 34a65926e..050edef71 100755 --- a/testsuite/printf.tests +++ b/testsuite/printf.tests @@ -79,6 +79,11 @@ testing "printf understands %Ld" \ "-5\n""0\n" \ "" "" +testing "printf understands %%" \ + "${bb}printf '%%\n' 2>&1; echo \$?" \ + "%\n""0\n" \ + "" "" + testing "printf handles positive numbers for %d" \ "${bb}printf '%d\n' 3 +3 ' 3' ' +3' 2>&1; echo \$?" \ "3\n"\ -- cgit v1.2.3-55-g6feb From 4b032a4d6c4a9e24e66c262d6350e6499d0facb3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 6 Sep 2021 17:38:18 +0200 Subject: chat: hopefully fix infinite spinning on input EOF function old new delta chat_main 1295 1303 +8 Signed-off-by: Denys Vlasenko --- miscutils/chat.c | 64 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/miscutils/chat.c b/miscutils/chat.c index f9e12a4ac..83aac37de 100644 --- a/miscutils/chat.c +++ b/miscutils/chat.c @@ -201,6 +201,9 @@ int chat_main(int argc UNUSED_PARAM, char **argv) DIR_RECORD, }; +#define inbuf bb_common_bufsiz1 + setup_common_bufsiz(); + // make x* functions fail with correct exitcode xfunc_error_retval = ERR_IO; @@ -361,35 +364,36 @@ int chat_main(int argc UNUSED_PARAM, char **argv) // get reply pfd.fd = STDIN_FILENO; pfd.events = POLLIN; - while (!exitcode + while (exitcode == ERR_OK && poll(&pfd, 1, timeout) > 0 - && (pfd.revents & POLLIN) + /* && (pfd.revents & POLLIN) - may be untrue (e.g. only POLLERR set) */ ) { llist_t *l; ssize_t delta; -#define buf bb_common_bufsiz1 - setup_common_bufsiz(); // read next char from device - if (safe_read(STDIN_FILENO, buf+buf_len, 1) > 0) { - // dump device input if RECORD fname - if (record_fd > 0) { - full_write(record_fd, buf+buf_len, 1); - } - // dump device input if ECHO ON - if (echo) { -// if (buf[buf_len] < ' ') { -// full_write2_str("^"); -// buf[buf_len] += '@'; -// } - full_write(STDERR_FILENO, buf+buf_len, 1); - } - buf_len++; - // move input frame if we've reached higher bound - if (buf_len > COMMON_BUFSIZE) { - memmove(buf, buf+buf_len-max_len, max_len); - buf_len = max_len; - } + if (safe_read(STDIN_FILENO, inbuf + buf_len, 1) <= 0) { + exitcode = ERR_IO; + goto expect_done; + } + + // dump device input if RECORD fname + if (record_fd > 0) { + full_write(record_fd, inbuf + buf_len, 1); + } + // dump device input if ECHO ON + if (echo) { +// if (inbuf[buf_len] < ' ') { +// full_write2_str("^"); +// inbuf[buf_len] += '@'; +// } + full_write(STDERR_FILENO, inbuf + buf_len, 1); + } + buf_len++; + // move input frame if we've reached higher bound + if (buf_len > COMMON_BUFSIZE) { + memmove(inbuf, inbuf + buf_len - max_len, max_len); + buf_len = max_len; } // N.B. rule of thumb: values being looked for can // be found only at the end of input buffer @@ -399,20 +403,20 @@ int chat_main(int argc UNUSED_PARAM, char **argv) // abort condition is met? -> bail out for (l = aborts, exitcode = ERR_ABORT; l; l = l->link, ++exitcode) { size_t len = strlen(l->data); - delta = buf_len-len; - if (delta >= 0 && !memcmp(buf+delta, l->data, len)) + delta = buf_len - len; + if (delta >= 0 && !memcmp(inbuf + delta, l->data, len)) goto expect_done; } exitcode = ERR_OK; // expected reply received? -> goto next command delta = buf_len - expect_len; - if (delta >= 0 && !memcmp(buf+delta, expect, expect_len)) + if (delta >= 0 && memcmp(inbuf + delta, expect, expect_len) == 0) goto expect_done; -#undef buf } /* while (have data) */ - // device timed out or unexpected reply received + // device timed out, or unexpected reply received, + // or we got a signal (poll() returned -1 with EINTR). exitcode = ERR_TIMEOUT; expect_done: #if ENABLE_FEATURE_CHAT_NOFAIL @@ -434,7 +438,7 @@ int chat_main(int argc UNUSED_PARAM, char **argv) } #endif // bail out unless we expected successfully - if (exitcode) + if (exitcode != ERR_OK) break; //----------------------- @@ -478,7 +482,7 @@ int chat_main(int argc UNUSED_PARAM, char **argv) continue; } if ('p' == c) { - usleep(10000); + msleep(10); len--; continue; } -- cgit v1.2.3-55-g6feb From e0bf3df0205d5ccef52df67b1760b8b54f15ec6e Mon Sep 17 00:00:00 2001 From: "Roberto A. Foglietta" Date: Tue, 7 Sep 2021 01:19:31 +0200 Subject: ash: add bash-like ERR trap and set -E While at it, stop incrementing LINENO inside traps function old new delta evaltree 567 762 +195 evalfun 268 348 +80 trapcmd 286 333 +47 dotrap 129 157 +28 exitshell 120 139 +19 readtoken1 3096 3110 +14 nlprompt 25 39 +14 nlnoprompt 19 33 +14 .rodata 104245 104255 +10 forkchild 610 617 +7 optletters_optnames 64 68 +4 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 11/0 up/down: 432/0) Total: 432 bytes Signed-off-by: Roberto A. Foglietta Signed-off-by: Denys Vlasenko --- shell/ash.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 53c140930..cfe0433a8 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -348,6 +348,7 @@ static const char *const optletters_optnames[] = { "a" "allexport", "b" "notify", "u" "nounset", + "E" "errtrace", "\0" "vi" #if BASH_PIPEFAIL ,"\0" "pipefail" @@ -442,15 +443,16 @@ struct globals_misc { #define aflag optlist[11] #define bflag optlist[12] #define uflag optlist[13] -#define viflag optlist[14] +#define Eflag optlist[14] +#define viflag optlist[15] #if BASH_PIPEFAIL -# define pipefail optlist[15] +# define pipefail optlist[16] #else # define pipefail 0 #endif #if DEBUG -# define nolog optlist[15 + BASH_PIPEFAIL] -# define debug optlist[16 + BASH_PIPEFAIL] +# define nolog optlist[16 + BASH_PIPEFAIL] +# define debug optlist[17 + BASH_PIPEFAIL] #endif /* trap handler commands */ @@ -468,7 +470,11 @@ struct globals_misc { /* indicates specified signal received */ uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */ - char *trap[NSIG]; + char *trap[NSIG + 1]; +/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */ +#define NTRAP_ERR NSIG +#define NTRAP_LAST NSIG + char **trap_ptr; /* used only by "trap hack" */ /* Rarely referenced stuff */ @@ -2166,6 +2172,8 @@ struct globals_var { struct var varinit[ARRAY_SIZE(varinit_data)]; int lineno; char linenovar[sizeof("LINENO=") + sizeof(int)*3]; + unsigned trap_depth; + bool in_trap_ERR; /* ERR cannot recurse, no need to be a counter */ }; extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var; #define G_var (*ash_ptr_to_globals_var) @@ -2176,6 +2184,8 @@ extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var; #define varinit (G_var.varinit ) #define lineno (G_var.lineno ) #define linenovar (G_var.linenovar ) +#define trap_depth (G_var.trap_depth ) +#define in_trap_ERR (G_var.in_trap_ERR ) #define vifs varinit[0] #if ENABLE_ASH_MAIL # define vmail varinit[1] @@ -5163,13 +5173,13 @@ clear_traps(void) char **tp; INT_OFF; - for (tp = trap; tp < &trap[NSIG]; tp++) { + for (tp = trap; tp <= &trap[NTRAP_LAST]; tp++) { if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */ if (trap_ptr == trap) free(*tp); /* else: it "belongs" to trap_ptr vector, don't free */ *tp = NULL; - if ((tp - trap) != 0) + if ((tp - trap) != 0 && (tp - trap) < NSIG) setsignal(tp - trap); } } @@ -9253,7 +9263,9 @@ dotrap(void) *g = 0; if (!p) continue; + trap_depth++; evalstring(p, 0); + trap_depth--; if (evalskip != SKIPFUNC) exitstatus = status; } @@ -9321,7 +9333,7 @@ evaltree(union node *n, int flags) case NCMD: evalfn = evalcommand; checkexit: - if (eflag && !(flags & EV_TESTED)) + if (!(flags & EV_TESTED)) checkexit = ~0; goto calleval; case NFOR: @@ -9395,8 +9407,32 @@ evaltree(union node *n, int flags) */ dotrap(); - if (checkexit & status) - raise_exception(EXEND); + if (checkexit & status) { + if (trap[NTRAP_ERR] && !in_trap_ERR) { + int err; + struct jmploc *volatile savehandler = exception_handler; + struct jmploc jmploc; + + in_trap_ERR = 1; + trap_depth++; + err = setjmp(jmploc.loc); + if (!err) { + exception_handler = &jmploc; + savestatus = exitstatus; + evalstring(trap[NTRAP_ERR], 0); + } + trap_depth--; + in_trap_ERR = 0; + + exception_handler = savehandler; + if (err && exception_type != EXERROR) + longjmp(exception_handler->loc, 1); + + exitstatus = savestatus; + } + if (eflag) + raise_exception(EXEND); + } if (flags & EV_EXIT) raise_exception(EXEND); @@ -9861,7 +9897,12 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) struct jmploc jmploc; int e; int savefuncline; + char *savetrap = NULL; + if (!Eflag) { + savetrap = trap[NTRAP_ERR]; + trap[NTRAP_ERR] = NULL; + } saveparam = shellparam; savefuncline = funcline; savehandler = exception_handler; @@ -9884,6 +9925,12 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) evaltree(func->n.ndefun.body, flags & EV_TESTED); funcdone: INT_OFF; + if (savetrap) { + if (!trap[NTRAP_ERR]) + trap[NTRAP_ERR] = savetrap; + else + free(savetrap); + } funcline = savefuncline; freefunc(func); freeparam(&shellparam); @@ -10912,13 +10959,15 @@ preadbuffer(void) static void nlprompt(void) { - g_parsefile->linno++; + if (trap_depth == 0) + g_parsefile->linno++; setprompt_if(doprompt, 2); } static void nlnoprompt(void) { - g_parsefile->linno++; + if (trap_depth == 0) + g_parsefile->linno++; needprompt = doprompt; } @@ -12620,7 +12669,8 @@ checkend: { if (c == '\n' || c == PEOF) { c = PEOF; - g_parsefile->linno++; + if (trap_depth == 0) + g_parsefile->linno++; needprompt = doprompt; } else { int len_here; @@ -13869,7 +13919,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) nextopt(nullstr); ap = argptr; if (!*ap) { - for (signo = 0; signo < NSIG; signo++) { + for (signo = 0; signo <= NTRAP_LAST; signo++) { char *tr = trap_ptr[signo]; if (tr) { /* note: bash adds "SIG", but only if invoked @@ -13878,7 +13928,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) * We are printing short names: */ out1fmt("trap -- %s %s\n", single_quote(tr), - get_signame(signo)); + (signo == NTRAP_ERR) ? "ERR" : get_signame(signo)); /* trap_ptr != trap only if we are in special-cased `trap` code. * In this case, we will exit very soon, no need to free(). */ /* if (trap_ptr != trap && tp[0]) */ @@ -13904,7 +13954,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) exitcode = 0; while (*ap) { - signo = get_signum(*ap); + signo = strcmp(*ap, "ERR") == 0 ? NTRAP_ERR : get_signum(*ap); if (signo < 0) { /* Mimic bash message exactly */ ash_msg("%s: invalid signal specification", *ap); @@ -13923,7 +13973,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) } free(trap[signo]); trap[signo] = action; - if (signo != 0) + if (signo != 0 && signo < NSIG) setsignal(signo); INT_ON; next: @@ -14348,7 +14398,9 @@ exitshell(void) if (p) { trap[0] = NULL; evalskip = 0; + trap_depth++; evalstring(p, 0); + trap_depth--; evalskip = SKIPFUNCDEF; /*free(p); - we'll exit soon */ } -- cgit v1.2.3-55-g6feb From 41beb53787ec798a27f336c4758cb5ebd8f0c75a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 7 Sep 2021 01:52:21 +0200 Subject: ash: eval: Check nflag in evaltree instead of cmdloop Upstream commit: Date: Thu, 4 Jun 2020 21:53:55 +1000 eval: Check nflag in evaltree instead of cmdloop This patch moves the nflag check from cmdloop into evaltree. This is so that nflag will be in force even if we enter the shell via a path other than cmdloop, e.g., through sh -c. Signed-off-by: Denys Vlasenko --- shell/ash.c | 5 ++++- shell/ash_test/ash-misc/set-n1.right | 3 +++ shell/ash_test/ash-misc/set-n1.tests | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 shell/ash_test/ash-misc/set-n1.right create mode 100755 shell/ash_test/ash-misc/set-n1.tests diff --git a/shell/ash.c b/shell/ash.c index cfe0433a8..2d2c09ba5 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -9299,6 +9299,9 @@ evaltree(union node *n, int flags) setstackmark(&smark); + if (nflag) + goto out; + if (n == NULL) { TRACE(("evaltree(NULL) called\n")); goto out; @@ -13557,7 +13560,7 @@ cmdloop(int top) out2str("\nUse \"exit\" to leave shell.\n"); } numeof++; - } else if (nflag == 0) { + } else { int i; /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ diff --git a/shell/ash_test/ash-misc/set-n1.right b/shell/ash_test/ash-misc/set-n1.right new file mode 100644 index 000000000..ac01831a7 --- /dev/null +++ b/shell/ash_test/ash-misc/set-n1.right @@ -0,0 +1,3 @@ +set -n stops in -c? +YES +Ok:0 diff --git a/shell/ash_test/ash-misc/set-n1.tests b/shell/ash_test/ash-misc/set-n1.tests new file mode 100755 index 000000000..90d0f9146 --- /dev/null +++ b/shell/ash_test/ash-misc/set-n1.tests @@ -0,0 +1,2 @@ +$THIS_SH -c "echo 'set -n stops in -c?'; set -n; echo NO" && echo YES +echo Ok:$? -- cgit v1.2.3-55-g6feb From f415e21a7dce1d4f4b760fddfaba85c551681e11 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 7 Sep 2021 01:54:23 +0200 Subject: ash: eval: Do not cache value of eflag in evaltree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upsteam commit: Date: Mon, 17 May 2021 15:19:23 +0800 eval: Do not cache value of eflag in evaltree Patrick BrĂ¼nn wrote: > Since we are migrating to Debian bullseye, we discovered a new behavior > with our scripts, which look like this: >>cleanup() { >> set +e >> rmdir "" >>} >>set -eu >>trap 'cleanup' EXIT INT TERM >>echo 'Hello world!' > > With old dash v0.5.10.2 this script would return 0 as we expected it. > But since commit 62cf6955f8abe875752d7163f6f3adbc7e49ebae it returns > the last exit code of our cleanup function. ... Thanks for the report. This is actually a fairly old bug with set -e that's just been exposed by the exit status change. What's really happening is that cleanup itself is triggering a set -e exit incorrectly because evaltree cached the value of eflag prior to the function call. Signed-off-by: Denys Vlasenko --- shell/ash.c | 15 +++++++-------- shell/ash_test/ash-misc/exitcode_trap7.right | 2 ++ shell/ash_test/ash-misc/exitcode_trap7.tests | 7 +++++++ 3 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 shell/ash_test/ash-misc/exitcode_trap7.right create mode 100755 shell/ash_test/ash-misc/exitcode_trap7.tests diff --git a/shell/ash.c b/shell/ash.c index 2d2c09ba5..c65f09782 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -9336,8 +9336,7 @@ evaltree(union node *n, int flags) case NCMD: evalfn = evalcommand; checkexit: - if (!(flags & EV_TESTED)) - checkexit = ~0; + checkexit = ~flags & EV_TESTED; goto calleval; case NFOR: evalfn = evalfor; @@ -9359,7 +9358,6 @@ evaltree(union node *n, int flags) case NAND: case NOR: case NSEMI: { - #if NAND + 1 != NOR #error NAND + 1 != NOR #endif @@ -9387,8 +9385,7 @@ evaltree(union node *n, int flags) if (!status) { n = n->nif.ifpart; goto evaln; - } - if (n->nif.elsepart) { + } else if (n->nif.elsepart) { n = n->nif.elsepart; goto evaln; } @@ -9410,7 +9407,7 @@ evaltree(union node *n, int flags) */ dotrap(); - if (checkexit & status) { + if (checkexit && status) { if (trap[NTRAP_ERR] && !in_trap_ERR) { int err; struct jmploc *volatile savehandler = exception_handler; @@ -9434,10 +9431,12 @@ evaltree(union node *n, int flags) exitstatus = savestatus; } if (eflag) - raise_exception(EXEND); + goto exexit; } - if (flags & EV_EXIT) + if (flags & EV_EXIT) { + exexit: raise_exception(EXEND); + } popstackmark(&smark); TRACE(("leaving evaltree (no interrupts)\n")); diff --git a/shell/ash_test/ash-misc/exitcode_trap7.right b/shell/ash_test/ash-misc/exitcode_trap7.right new file mode 100644 index 000000000..07d66e9d9 --- /dev/null +++ b/shell/ash_test/ash-misc/exitcode_trap7.right @@ -0,0 +1,2 @@ +Start +Ok:0 diff --git a/shell/ash_test/ash-misc/exitcode_trap7.tests b/shell/ash_test/ash-misc/exitcode_trap7.tests new file mode 100755 index 000000000..9772a7b8c --- /dev/null +++ b/shell/ash_test/ash-misc/exitcode_trap7.tests @@ -0,0 +1,7 @@ +$THIS_SH -c ' +cleanup() { set +e; false; } +set -eu +trap cleanup EXIT +echo Start +' +echo Ok:$? -- cgit v1.2.3-55-g6feb From e53c7dbafc78948e5c0d8d8ccb0bdcd9f936c62e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 7 Sep 2021 02:23:51 +0200 Subject: hush: fix set -n to act immediately, not just after run_list() Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-misc/exitcode_trap7.tests | 2 +- shell/hush.c | 8 ++++++-- shell/hush_test/hush-misc/exitcode_trap7.right | 2 ++ shell/hush_test/hush-misc/exitcode_trap7.tests | 7 +++++++ shell/hush_test/hush-misc/set-n1.right | 3 +++ shell/hush_test/hush-misc/set-n1.tests | 2 ++ 6 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 shell/hush_test/hush-misc/exitcode_trap7.right create mode 100755 shell/hush_test/hush-misc/exitcode_trap7.tests create mode 100644 shell/hush_test/hush-misc/set-n1.right create mode 100755 shell/hush_test/hush-misc/set-n1.tests diff --git a/shell/ash_test/ash-misc/exitcode_trap7.tests b/shell/ash_test/ash-misc/exitcode_trap7.tests index 9772a7b8c..f4b0eb544 100755 --- a/shell/ash_test/ash-misc/exitcode_trap7.tests +++ b/shell/ash_test/ash-misc/exitcode_trap7.tests @@ -1,6 +1,6 @@ $THIS_SH -c ' cleanup() { set +e; false; } -set -eu +set -e trap cleanup EXIT echo Start ' diff --git a/shell/hush.c b/shell/hush.c index 27092c12f..5fafa322c 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -9898,7 +9898,8 @@ static int run_list(struct pipe *pi) #if ENABLE_HUSH_LOOPS G.flag_break_continue = 0; #endif - rcode = r = run_pipe(pi); /* NB: rcode is a smalluint, r is int */ + rcode = r = G.o_opt[OPT_O_NOEXEC] ? 0 : run_pipe(pi); + /* NB: rcode is a smalluint, r is int */ if (r != -1) { /* We ran a builtin, function, or group. * rcode is already known @@ -10137,7 +10138,10 @@ static int set_mode(int state, char mode, const char *o_opt) int idx; switch (mode) { case 'n': - G.o_opt[OPT_O_NOEXEC] = state; + /* set -n has no effect in interactive shell */ + /* Try: while set -n; do echo $-; done */ + if (!G_interactive_fd) + G.o_opt[OPT_O_NOEXEC] = state; break; case 'x': IF_HUSH_MODE_X(G_x_mode = state;) diff --git a/shell/hush_test/hush-misc/exitcode_trap7.right b/shell/hush_test/hush-misc/exitcode_trap7.right new file mode 100644 index 000000000..07d66e9d9 --- /dev/null +++ b/shell/hush_test/hush-misc/exitcode_trap7.right @@ -0,0 +1,2 @@ +Start +Ok:0 diff --git a/shell/hush_test/hush-misc/exitcode_trap7.tests b/shell/hush_test/hush-misc/exitcode_trap7.tests new file mode 100755 index 000000000..f4b0eb544 --- /dev/null +++ b/shell/hush_test/hush-misc/exitcode_trap7.tests @@ -0,0 +1,7 @@ +$THIS_SH -c ' +cleanup() { set +e; false; } +set -e +trap cleanup EXIT +echo Start +' +echo Ok:$? diff --git a/shell/hush_test/hush-misc/set-n1.right b/shell/hush_test/hush-misc/set-n1.right new file mode 100644 index 000000000..ac01831a7 --- /dev/null +++ b/shell/hush_test/hush-misc/set-n1.right @@ -0,0 +1,3 @@ +set -n stops in -c? +YES +Ok:0 diff --git a/shell/hush_test/hush-misc/set-n1.tests b/shell/hush_test/hush-misc/set-n1.tests new file mode 100755 index 000000000..90d0f9146 --- /dev/null +++ b/shell/hush_test/hush-misc/set-n1.tests @@ -0,0 +1,2 @@ +$THIS_SH -c "echo 'set -n stops in -c?'; set -n; echo NO" && echo YES +echo Ok:$? -- cgit v1.2.3-55-g6feb From 0d7dfa9012d01159c371bf041bf53afe0780df9f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 7 Sep 2021 17:34:58 +0200 Subject: ash: support testsuite for !FEATURE_SUID_CONFIG_QUIET configs Signed-off-by: Denys Vlasenko --- shell/ash_test/run-all | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/ash_test/run-all b/shell/ash_test/run-all index caf033577..b9f5ee788 100755 --- a/shell/ash_test/run-all +++ b/shell/ash_test/run-all @@ -61,7 +61,8 @@ do_test() # echo Running test: "$x" echo -n "$1/$x:" { - "$THIS_SH" "./$x" >"$name.xx" 2>&1 + "$THIS_SH" "./$x" 2>&1 | \ + grep -va "^ash: using fallback suid method$" >"$name.xx" diff -u "$name.xx" "$name.right" >"$TOPDIR/$noslash-$x.fail" \ && rm -f "$name.xx" "$TOPDIR/$noslash-$x.fail" } && echo " ok" || echo " fail" -- cgit v1.2.3-55-g6feb From d6c9cbc0727cc3875672ae280ec129514a9d8594 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 7 Sep 2021 18:01:49 +0200 Subject: ash: fix LINENO in functions From larger patch by Roberto A. Foglietta function old new delta evalfun 348 369 +21 ash_main 1202 1218 +16 setinputstring 65 73 +8 lookupvar 116 106 -10 evaltree 772 753 -19 evalsubshell 192 173 -19 evalfor 175 156 -19 evalcase 273 254 -19 evalcommand 1560 1536 -24 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/6 up/down: 45/-110) Total: -65 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 18 ++++++------------ shell/ash_test/ash-vars/var_LINENO2.right | 3 +++ shell/ash_test/ash-vars/var_LINENO2.tests | 8 ++++++++ shell/hush_test/hush-vars/var_LINENO2.right | 3 +++ shell/hush_test/hush-vars/var_LINENO2.tests | 8 ++++++++ 5 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 shell/ash_test/ash-vars/var_LINENO2.right create mode 100755 shell/ash_test/ash-vars/var_LINENO2.tests create mode 100644 shell/hush_test/hush-vars/var_LINENO2.right create mode 100755 shell/hush_test/hush-vars/var_LINENO2.tests diff --git a/shell/ash.c b/shell/ash.c index c65f09782..79fa3adba 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -2357,7 +2357,7 @@ lookupvar(const char *name) v->var_func(NULL); #endif if (!(v->flags & VUNSET)) { - if (v == &vlineno && v->var_text == linenovar) { + if (v->var_text == linenovar) { fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno); } return var_end(v->var_text); @@ -9322,8 +9322,6 @@ evaltree(union node *n, int flags) goto setstatus; case NREDIR: errlinno = lineno = n->nredir.linno; - if (funcline) - lineno -= funcline - 1; expredir(n->nredir.redirect); pushredir(n->nredir.redirect); status = redirectsafe(n->nredir.redirect, REDIR_PUSH); @@ -9502,8 +9500,6 @@ evalfor(union node *n, int flags) int status = 0; errlinno = lineno = n->ncase.linno; - if (funcline) - lineno -= funcline - 1; arglist.list = NULL; arglist.lastp = &arglist.list; @@ -9534,8 +9530,6 @@ evalcase(union node *n, int flags) int status = 0; errlinno = lineno = n->ncase.linno; - if (funcline) - lineno -= funcline - 1; arglist.list = NULL; arglist.lastp = &arglist.list; @@ -9569,8 +9563,6 @@ evalsubshell(union node *n, int flags) int status; errlinno = lineno = n->nredir.linno; - if (funcline) - lineno -= funcline - 1; expredir(n->nredir.redirect); if (!backgnd && (flags & EV_EXIT) && !may_have_traps) @@ -9898,6 +9890,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) struct jmploc *volatile savehandler; struct jmploc jmploc; int e; + int savelineno; int savefuncline; char *savetrap = NULL; @@ -9905,6 +9898,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) savetrap = trap[NTRAP_ERR]; trap[NTRAP_ERR] = NULL; } + savelineno = lineno; saveparam = shellparam; savefuncline = funcline; savehandler = exception_handler; @@ -9934,6 +9928,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) free(savetrap); } funcline = savefuncline; + lineno = savelineno; freefunc(func); freeparam(&shellparam); shellparam = saveparam; @@ -10322,8 +10317,6 @@ evalcommand(union node *cmd, int flags) int vlocal; errlinno = lineno = cmd->ncmd.linno; - if (funcline) - lineno -= funcline - 1; /* First expand the arguments. */ TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); @@ -11201,7 +11194,7 @@ setinputstring(char *string) g_parsefile->next_to_pgetc = string; g_parsefile->left_in_line = strlen(string); g_parsefile->buf = NULL; - g_parsefile->linno = 1; + g_parsefile->linno = lineno; INT_ON; } @@ -14705,6 +14698,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) // ^^ not necessary since now we special-case fd 0 // in save_fd_on_redirect() + lineno = 1; // dash: evalstring(minusc, sflag ? 0 : EV_EXIT); // The above makes // ash -sc 'echo $-' diff --git a/shell/ash_test/ash-vars/var_LINENO2.right b/shell/ash_test/ash-vars/var_LINENO2.right new file mode 100644 index 000000000..73656647c --- /dev/null +++ b/shell/ash_test/ash-vars/var_LINENO2.right @@ -0,0 +1,3 @@ +Start LINENO=6, calling function +In function: LINENO=4 +After function: LINENO=8 diff --git a/shell/ash_test/ash-vars/var_LINENO2.tests b/shell/ash_test/ash-vars/var_LINENO2.tests new file mode 100755 index 000000000..7036dbdc8 --- /dev/null +++ b/shell/ash_test/ash-vars/var_LINENO2.tests @@ -0,0 +1,8 @@ +#skip lines: make "line number within function" differ from overall line number +#skip lines +f() { + echo "In function: LINENO=$LINENO" +} +echo "Start LINENO=$LINENO, calling function" +f +echo "After function: LINENO=$LINENO" diff --git a/shell/hush_test/hush-vars/var_LINENO2.right b/shell/hush_test/hush-vars/var_LINENO2.right new file mode 100644 index 000000000..73656647c --- /dev/null +++ b/shell/hush_test/hush-vars/var_LINENO2.right @@ -0,0 +1,3 @@ +Start LINENO=6, calling function +In function: LINENO=4 +After function: LINENO=8 diff --git a/shell/hush_test/hush-vars/var_LINENO2.tests b/shell/hush_test/hush-vars/var_LINENO2.tests new file mode 100755 index 000000000..7036dbdc8 --- /dev/null +++ b/shell/hush_test/hush-vars/var_LINENO2.tests @@ -0,0 +1,8 @@ +#skip lines: make "line number within function" differ from overall line number +#skip lines +f() { + echo "In function: LINENO=$LINENO" +} +echo "Start LINENO=$LINENO, calling function" +f +echo "After function: LINENO=$LINENO" -- cgit v1.2.3-55-g6feb From 64aa86b720641cb50be9636e6c20d4dbbea6fed0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 7 Sep 2021 18:16:45 +0200 Subject: ash: LINENO starts from 0 in -c SCRIPT mode The var_LINENO3.tests fails for hush: it does start from 0, but does not increment. Signed-off-by: Denys Vlasenko --- shell/ash.c | 2 +- shell/ash_test/ash-misc/control_char3.right | 2 +- shell/ash_test/ash-misc/control_char4.right | 2 +- shell/ash_test/ash-misc/shift1.right | 2 +- shell/ash_test/ash-misc/tickquote1.right | 2 +- .../ash-parsing/groups_and_keywords2.right | 2 +- shell/ash_test/ash-psubst/emptytick.right | 4 +-- shell/ash_test/ash-vars/param_expand_alt.right | 4 +-- shell/ash_test/ash-vars/param_expand_assign.right | 14 +++++----- .../ash-vars/param_expand_bash_substring.right | 10 +++---- shell/ash_test/ash-vars/param_expand_default.right | 2 +- .../ash-vars/param_expand_indicate_error.right | 32 +++++++++++----------- shell/ash_test/ash-vars/var6.right | 4 +-- shell/ash_test/ash-vars/var_LINENO3.right | 2 ++ shell/ash_test/ash-vars/var_LINENO3.tests | 2 ++ shell/hush_test/hush-vars/var_LINENO3.right | 2 ++ shell/hush_test/hush-vars/var_LINENO3.tests | 2 ++ 17 files changed, 49 insertions(+), 41 deletions(-) create mode 100644 shell/ash_test/ash-vars/var_LINENO3.right create mode 100755 shell/ash_test/ash-vars/var_LINENO3.tests create mode 100644 shell/hush_test/hush-vars/var_LINENO3.right create mode 100755 shell/hush_test/hush-vars/var_LINENO3.tests diff --git a/shell/ash.c b/shell/ash.c index 79fa3adba..35dbb2f28 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -14698,7 +14698,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) // ^^ not necessary since now we special-case fd 0 // in save_fd_on_redirect() - lineno = 1; + lineno = 0; // bash compat // dash: evalstring(minusc, sflag ? 0 : EV_EXIT); // The above makes // ash -sc 'echo $-' diff --git a/shell/ash_test/ash-misc/control_char3.right b/shell/ash_test/ash-misc/control_char3.right index 283e02cbb..654005d24 100644 --- a/shell/ash_test/ash-misc/control_char3.right +++ b/shell/ash_test/ash-misc/control_char3.right @@ -1 +1 @@ -SHELL: line 1: : not found +SHELL: line 0: : not found diff --git a/shell/ash_test/ash-misc/control_char4.right b/shell/ash_test/ash-misc/control_char4.right index 2bf18e684..ec9d5fc98 100644 --- a/shell/ash_test/ash-misc/control_char4.right +++ b/shell/ash_test/ash-misc/control_char4.right @@ -1 +1 @@ -SHELL: line 1: -: not found +SHELL: line 0: -: not found diff --git a/shell/ash_test/ash-misc/shift1.right b/shell/ash_test/ash-misc/shift1.right index b53453c3a..fdba79fd3 100644 --- a/shell/ash_test/ash-misc/shift1.right +++ b/shell/ash_test/ash-misc/shift1.right @@ -1,5 +1,5 @@ 2 3 4 -0: shift: line 1: Illegal number: -1 +0: shift: line 0: Illegal number: -1 1 2 3 4 2 3 4 3 4 diff --git a/shell/ash_test/ash-misc/tickquote1.right b/shell/ash_test/ash-misc/tickquote1.right index 2e661bfe3..e55a31c2d 100644 --- a/shell/ash_test/ash-misc/tickquote1.right +++ b/shell/ash_test/ash-misc/tickquote1.right @@ -1 +1 @@ -./tickquote1.tests: line 1: syntax error: unterminated quoted string +./tickquote1.tests: line 0: syntax error: unterminated quoted string diff --git a/shell/ash_test/ash-parsing/groups_and_keywords2.right b/shell/ash_test/ash-parsing/groups_and_keywords2.right index 3fcbeb662..2ce38fe6e 100644 --- a/shell/ash_test/ash-parsing/groups_and_keywords2.right +++ b/shell/ash_test/ash-parsing/groups_and_keywords2.right @@ -1,3 +1,3 @@ -./groups_and_keywords2.tests: eval: line 1: syntax error: unexpected ")" +./groups_and_keywords2.tests: eval: line 2: syntax error: unexpected ")" Fail:2 ./groups_and_keywords2.tests: line 8: syntax error: unexpected ")" diff --git a/shell/ash_test/ash-psubst/emptytick.right b/shell/ash_test/ash-psubst/emptytick.right index 459c4f735..8fe9839ca 100644 --- a/shell/ash_test/ash-psubst/emptytick.right +++ b/shell/ash_test/ash-psubst/emptytick.right @@ -1,8 +1,8 @@ 0 0 -./emptytick.tests: line 1: : Permission denied +./emptytick.tests: line 2: : Permission denied 127 -./emptytick.tests: line 1: : Permission denied +./emptytick.tests: line 3: : Permission denied 127 0 0 diff --git a/shell/ash_test/ash-vars/param_expand_alt.right b/shell/ash_test/ash-vars/param_expand_alt.right index 1303f8064..d10f1206f 100644 --- a/shell/ash_test/ash-vars/param_expand_alt.right +++ b/shell/ash_test/ash-vars/param_expand_alt.right @@ -1,5 +1,5 @@ -SHELL: line 1: syntax error: bad substitution -SHELL: line 1: syntax error: bad substitution +SHELL: line 0: syntax error: bad substitution +SHELL: line 0: syntax error: bad substitution __ _z_ _z_ _ _ _ _ _ diff --git a/shell/ash_test/ash-vars/param_expand_assign.right b/shell/ash_test/ash-vars/param_expand_assign.right index 6e9ea1379..52eaef9f8 100644 --- a/shell/ash_test/ash-vars/param_expand_assign.right +++ b/shell/ash_test/ash-vars/param_expand_assign.right @@ -1,11 +1,11 @@ -SHELL: line 1: syntax error: bad substitution -SHELL: line 1: syntax error: bad substitution -SHELL: line 1: syntax error: bad substitution +SHELL: line 0: syntax error: bad substitution +SHELL: line 0: syntax error: bad substitution +SHELL: line 0: syntax error: bad substitution 0 -SHELL: line 1: 1: bad variable name -SHELL: line 1: 1: bad variable name -SHELL: line 1: 1: bad variable name -SHELL: line 1: 1: bad variable name +SHELL: line 0: 1: bad variable name +SHELL: line 0: 1: bad variable name +SHELL: line 0: 1: bad variable name +SHELL: line 0: 1: bad variable name _aa _aa _aa diff --git a/shell/ash_test/ash-vars/param_expand_bash_substring.right b/shell/ash_test/ash-vars/param_expand_bash_substring.right index 687dd9002..22a0b2881 100644 --- a/shell/ash_test/ash-vars/param_expand_bash_substring.right +++ b/shell/ash_test/ash-vars/param_expand_bash_substring.right @@ -1,8 +1,8 @@ -SHELL: line 1: syntax error: bad substitution -SHELL: line 1: syntax error: bad substitution -SHELL: line 1: syntax error: bad substitution -SHELL: line 1: syntax error: bad substitution -SHELL: line 1: syntax error: missing '}' +SHELL: line 0: syntax error: bad substitution +SHELL: line 0: syntax error: bad substitution +SHELL: line 0: syntax error: bad substitution +SHELL: line 0: syntax error: bad substitution +SHELL: line 0: syntax error: missing '}' 0 1 =|| 1:1 =|| diff --git a/shell/ash_test/ash-vars/param_expand_default.right b/shell/ash_test/ash-vars/param_expand_default.right index 7a42f67e8..0852105ca 100644 --- a/shell/ash_test/ash-vars/param_expand_default.right +++ b/shell/ash_test/ash-vars/param_expand_default.right @@ -1,4 +1,4 @@ -SHELL: line 1: syntax error: bad substitution +SHELL: line 0: syntax error: bad substitution _0 _0 _ _ _ _word _word _aaaa _aaaa _aaaa _aaaa _aaaa diff --git a/shell/ash_test/ash-vars/param_expand_indicate_error.right b/shell/ash_test/ash-vars/param_expand_indicate_error.right index 33afacee0..53ea07181 100644 --- a/shell/ash_test/ash-vars/param_expand_indicate_error.right +++ b/shell/ash_test/ash-vars/param_expand_indicate_error.right @@ -1,14 +1,14 @@ -SHELL: line 1: syntax error: bad substitution +SHELL: line 0: syntax error: bad substitution 1 0 ==== _ -SHELL: line 1: 1: parameter not set -SHELL: line 1: 1: parameter not set or null -SHELL: line 1: 1: message1 -SHELL: line 1: 1: message1 -SHELL: line 1: 1: unset! -SHELL: line 1: 1: null or unset! +SHELL: line 0: 1: parameter not set +SHELL: line 0: 1: parameter not set or null +SHELL: line 0: 1: message1 +SHELL: line 0: 1: message1 +SHELL: line 0: 1: unset! +SHELL: line 0: 1: null or unset! ==== _aaaa _aaaa @@ -19,20 +19,20 @@ _aaaa _aaaa ==== _ -SHELL: line 1: f: parameter not set -SHELL: line 1: f: parameter not set or null -SHELL: line 1: f: message3 -SHELL: line 1: f: message3 -SHELL: line 1: f: unset! -SHELL: line 1: f: null or unset! +SHELL: line 0: f: parameter not set +SHELL: line 0: f: parameter not set or null +SHELL: line 0: f: message3 +SHELL: line 0: f: message3 +SHELL: line 0: f: unset! +SHELL: line 0: f: null or unset! ==== _ _ -SHELL: line 1: f: parameter not set or null +SHELL: line 0: f: parameter not set or null _ -SHELL: line 1: f: message4 +SHELL: line 0: f: message4 _ -SHELL: line 1: f: null or unset! +SHELL: line 0: f: null or unset! ==== _fff _fff diff --git a/shell/ash_test/ash-vars/var6.right b/shell/ash_test/ash-vars/var6.right index b37417fa1..e83f7b5eb 100644 --- a/shell/ash_test/ash-vars/var6.right +++ b/shell/ash_test/ash-vars/var6.right @@ -1,2 +1,2 @@ -SHELL: line 1: syntax error: bad substitution -SHELL: line 1: syntax error: bad substitution +SHELL: line 0: syntax error: bad substitution +SHELL: line 0: syntax error: bad substitution diff --git a/shell/ash_test/ash-vars/var_LINENO3.right b/shell/ash_test/ash-vars/var_LINENO3.right new file mode 100644 index 000000000..198c3cc99 --- /dev/null +++ b/shell/ash_test/ash-vars/var_LINENO3.right @@ -0,0 +1,2 @@ +LINENO starts from 0 in -c +and increments on next line: 1 diff --git a/shell/ash_test/ash-vars/var_LINENO3.tests b/shell/ash_test/ash-vars/var_LINENO3.tests new file mode 100755 index 000000000..4502edfe0 --- /dev/null +++ b/shell/ash_test/ash-vars/var_LINENO3.tests @@ -0,0 +1,2 @@ +$THIS_SH -c 'echo "LINENO starts from $LINENO in -c" +echo "and increments on next line: $LINENO"' diff --git a/shell/hush_test/hush-vars/var_LINENO3.right b/shell/hush_test/hush-vars/var_LINENO3.right new file mode 100644 index 000000000..198c3cc99 --- /dev/null +++ b/shell/hush_test/hush-vars/var_LINENO3.right @@ -0,0 +1,2 @@ +LINENO starts from 0 in -c +and increments on next line: 1 diff --git a/shell/hush_test/hush-vars/var_LINENO3.tests b/shell/hush_test/hush-vars/var_LINENO3.tests new file mode 100755 index 000000000..4502edfe0 --- /dev/null +++ b/shell/hush_test/hush-vars/var_LINENO3.tests @@ -0,0 +1,2 @@ +$THIS_SH -c 'echo "LINENO starts from $LINENO in -c" +echo "and increments on next line: $LINENO"' -- cgit v1.2.3-55-g6feb From bcff3a7b5ab3302ea03a471bae195f8454008a20 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 7 Sep 2021 18:24:08 +0200 Subject: shell/ash_test/run-all: unset locale/language variables Signed-off-by: Denys Vlasenko --- shell/ash_test/run-all | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/shell/ash_test/run-all b/shell/ash_test/run-all index b9f5ee788..96703ef12 100755 --- a/shell/ash_test/run-all +++ b/shell/ash_test/run-all @@ -1,5 +1,14 @@ #!/bin/sh +unset LANG LANGUAGE +unset LC_COLLATE +unset LC_CTYPE +unset LC_MONETARY +unset LC_MESSAGES +unset LC_NUMERIC +unset LC_TIME +unset LC_ALL + TOPDIR=`pwd` if test ! -x ash; then @@ -66,7 +75,7 @@ do_test() diff -u "$name.xx" "$name.right" >"$TOPDIR/$noslash-$x.fail" \ && rm -f "$name.xx" "$TOPDIR/$noslash-$x.fail" } && echo " ok" || echo " fail" - done + done ) } -- cgit v1.2.3-55-g6feb From 574b9c446da11baaf89551f09f951d6523eff731 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 7 Sep 2021 21:44:44 +0200 Subject: hush: fix var_LINENO3.tests failure function old new delta parse_and_run_string 40 62 +22 i_getch 105 102 -3 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 22/-3) Total: 19 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 5fafa322c..6d472337f 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2753,6 +2753,12 @@ static int i_getch(struct in_str *i) if (ch != '\0') { i->p++; i->last_char = ch; +#if ENABLE_HUSH_LINENO_VAR + if (ch == '\n') { + G.parse_lineno++; + debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno); + } +#endif return ch; } return EOF; @@ -7540,11 +7546,11 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger) static void parse_and_run_string(const char *s) { struct in_str input; - //IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;) + IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;) setup_string_in_str(&input, s); parse_and_run_stream(&input, '\0'); - //IF_HUSH_LINENO_VAR(G.parse_lineno = sv;) + IF_HUSH_LINENO_VAR(G.parse_lineno = sv;) } static void parse_and_run_file(HFILE *fp) -- cgit v1.2.3-55-g6feb From 6a9b3f7acfaa7365515f1eb70427d5ddd687c162 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 7 Sep 2021 22:51:42 +0200 Subject: shuf: add a TODO, code shrink function old new delta shuf_main 501 500 -1 Signed-off-by: Denys Vlasenko --- coreutils/shuf.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/coreutils/shuf.c b/coreutils/shuf.c index 50483a25e..3def3d80f 100644 --- a/coreutils/shuf.c +++ b/coreutils/shuf.c @@ -44,21 +44,25 @@ */ static void shuffle_lines(char **lines, unsigned numlines, unsigned outlines) { - unsigned i; - unsigned r; - char *tmp; - srand(monotonic_us()); - for (i = numlines - 1; outlines > 0; i--, outlines--) { - r = rand(); + while (outlines != 0) { + char *tmp; + unsigned r = rand(); /* RAND_MAX can be as small as 32767 */ - if (i > RAND_MAX) + if (numlines > RAND_MAX) r ^= rand() << 15; - r %= i + 1; - tmp = lines[i]; - lines[i] = lines[r]; + r %= numlines; +//TODO: the above method is seriously non-uniform when numlines is very large. +//For example, with numlines of 0xf0000000, +//values of (r % numlines) in [0, 0x0fffffff] range +//are more likely: e.g. r=1 and r=0xf0000001 both map to 1, +//whereas only one value, r=0xefffffff, maps to 0xefffffff. + numlines--; + tmp = lines[numlines]; + lines[numlines] = lines[r]; lines[r] = tmp; + outlines--; } } -- cgit v1.2.3-55-g6feb From 30af5938afad076e12b8ece123cab0b8bc92a596 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 8 Sep 2021 00:39:16 +0200 Subject: ash: parser: Fix handling of empty aliases Upstream commit: Date: Tue, 28 Apr 2020 01:15:26 +1000 parser: Fix handling of empty aliases Dash was incorrectly handling empty aliases. When attempting to use an empty alias with nothing else, I'm (incorrectly) prompted for more input: ``` $ alias empty='' $ empty > ``` Other shells (e.g., bash, yash) correctly handle the lone, empty alias as an empty command: ``` $ alias empty='' $ empty $ ``` The problem here is that we incorrectly enter the loop eating TNLs in readtoken(). This patch fixes it by setting checkkwd correctly. function old new delta list 351 355 +4 Signed-off-by: Denys Vlasenko --- shell/ash.c | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 35dbb2f28..5a18ff1a1 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -11755,27 +11755,28 @@ static union node *andor(void); static union node *pipeline(void); static union node *parse_command(void); static void parseheredoc(void); -static int peektoken(void); static int readtoken(void); static union node * list(int nlflag) { + int chknl = nlflag & 1 ? 0 : CHKNL; union node *n1, *n2, *n3; int tok; n1 = NULL; for (;;) { - switch (readtoken()) { + checkkwd = chknl | CHKKWD | CHKALIAS; + tok = readtoken(); + switch (tok) { case TNL: - if (!(nlflag & 1)) - break; parseheredoc(); return n1; case TEOF: - if (!n1 && (nlflag & 1)) + if (!n1 && !chknl) n1 = NODE_EOF; + out_eof: parseheredoc(); tokpushback++; lasttoken = TEOF; @@ -11783,8 +11784,7 @@ list(int nlflag) } tokpushback++; - checkkwd = CHKNL | CHKKWD | CHKALIAS; - if (nlflag == 2 && ((1 << peektoken()) & tokendlist)) + if (nlflag == 2 && ((1 << tok) & tokendlist)) return n1; nlflag |= 2; @@ -11813,15 +11813,16 @@ list(int nlflag) n1 = n3; } switch (tok) { - case TNL: case TEOF: + goto out_eof; + case TNL: tokpushback = 1; /* fall through */ case TBACKGND: case TSEMI: break; default: - if ((nlflag & 1)) + if (!chknl) raise_error_unexpected_syntax(-1); tokpushback = 1; return n1; @@ -11995,8 +11996,9 @@ simplecmd(void) switch (t) { #if BASH_FUNCTION case TFUNCTION: - if (peektoken() != TWORD) + if (readtoken() != TWORD) raise_error_unexpected_syntax(TWORD); + tokpushback = 1; function_flag = 1; break; #endif @@ -12033,7 +12035,9 @@ simplecmd(void) #if BASH_FUNCTION if (function_flag) { checkkwd = CHKNL | CHKKWD; - switch (peektoken()) { + t = readtoken(); + tokpushback = 1; + switch (t) { case TBEGIN: case TIF: case TCASE: @@ -13306,16 +13310,6 @@ readtoken(void) return t; } -static int -peektoken(void) -{ - int t; - - t = readtoken(); - tokpushback = 1; - return t; -} - /* * Read and parse a command. Returns NODE_EOF on end of file. * (NULL is a valid parse tree indicating a blank line.) -- cgit v1.2.3-55-g6feb From 1c06ddd8bbbd6906e5bf00ec93e04d5090718be9 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 8 Sep 2021 00:56:53 +0200 Subject: ash: parser: Save and restore heredoclist in expandstr Upstream commit: Date: Sun, 17 May 2020 23:36:25 +1000 parser: Save and restore heredoclist in expandstr On Sun, May 17, 2020 at 01:19:28PM +0100, Harald van Dijk wrote: > This still does not restore the state completely. It does not clean up any > pending heredocs. I see: > > $ PS1='$(< src/dash: 1: Syntax error: Unterminated quoted string > $(< > > > That is, after entering the ':' command, the shell is still trying to read > the heredoc from the prompt. This patch saves and restores the heredoclist in expandstr. It also removes a bunch of unnecessary volatiles as those variables are only referenced in case of a longjmp other than one started by a signal like SIGINT. function old new delta expandstr 268 255 -13 Signed-off-by: Denys Vlasenko --- shell/ash.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 5a18ff1a1..cf62fdf75 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -13359,23 +13359,26 @@ parseheredoc(void) static const char * expandstr(const char *ps, int syntax_type) { - union node n; + struct parsefile *file_stop; + struct jmploc *volatile savehandler; + struct heredoc *saveheredoclist; + const char *result; int saveprompt; - struct parsefile *file_stop = g_parsefile; - volatile int saveint; - struct jmploc *volatile savehandler = exception_handler; struct jmploc jmploc; - const char *volatile result; + union node n; int err; + file_stop = g_parsefile; + /* XXX Fix (char *) cast. */ setinputstring((char *)ps); + saveheredoclist = heredoclist; + heredoclist = NULL; saveprompt = doprompt; doprompt = 0; result = ps; - - SAVE_INT(saveint); + savehandler = exception_handler; err = setjmp(jmploc.loc); if (err) goto out; @@ -13402,11 +13405,11 @@ out: exception_handler = savehandler; if (err && exception_type != EXERROR) longjmp(exception_handler->loc, 1); - RESTORE_INT(saveint); doprompt = saveprompt; /* Try: PS1='`xxx(`' */ unwindfiles(file_stop); + heredoclist = saveheredoclist; return result; } -- cgit v1.2.3-55-g6feb From c54025612711a6b1997efd99206b9fbcaa5a29cf Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 8 Sep 2021 01:03:57 +0200 Subject: ash: use pgetc_eatbnl() in more places, take 3 Adding previously skipped "readtoken1(pgetc_eatbnl(), syntax_type..." change from upstream commit: Date: Thu Mar 8 08:37:11 2018 +0100 parser: use pgetc_eatbnl() in more places dash has a pgetc_eatbnl function in parser.c which skips any backslash-newline combinations. It's not used everywhere it could be. There is also some duplicated backslash-newline handling elsewhere in parser.c. Replace most of the calls to pgetc() with calls to pgetc_eatbnl() and remove the duplicated backslash-newline handling. Testcase: PS1='\ :::' should result in ::: prompt, not ::: prompt Signed-off-by: Denys Vlasenko --- shell/ash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/ash.c b/shell/ash.c index cf62fdf75..f1c21188e 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -13388,7 +13388,7 @@ expandstr(const char *ps, int syntax_type) * PS1='$(date "+%H:%M:%S) > ' */ exception_handler = &jmploc; - readtoken1(pgetc(), syntax_type, FAKEEOFMARK, 0); + readtoken1(pgetc_eatbnl(), syntax_type, FAKEEOFMARK, 0); n.narg.type = NARG; n.narg.next = NULL; -- cgit v1.2.3-55-g6feb From 8c68ae8416c8b54222eb3cd1d4908a570147e134 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 8 Sep 2021 01:43:12 +0200 Subject: ash: parser: Fix alias expansion after heredoc or newlines Upstream commit: Date: Wed, 29 Apr 2020 00:19:59 +1000 parser: Fix alias expansion after heredoc or newlines This script should print OK: alias a="case x in " b=x a b) echo BAD;; esac alias BEGIN={ END=} BEGIN cat <<- EOF > /dev/null $(:) EOF END : <<- EOF && $(:) EOF BEGIN echo OK END However, because the value of checkkwd is either zeroed when it shouldn't, or isn't zeroed when it should, dash currently gets it wrong in every case. This patch fixes it by saving checkkwd and zeroing it where needed. function old new delta readtoken 157 176 +19 Signed-off-by: Denys Vlasenko --- shell/ash.c | 7 +++++-- shell/ash_test/ash-alias/alias_brace.right | 1 + shell/ash_test/ash-alias/alias_brace.tests | 16 ++++++++++++++++ shell/ash_test/ash-alias/alias_case.right | 1 + shell/ash_test/ash-alias/alias_case.tests | 8 ++++++++ 5 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 shell/ash_test/ash-alias/alias_brace.right create mode 100755 shell/ash_test/ash-alias/alias_brace.tests create mode 100644 shell/ash_test/ash-alias/alias_case.right create mode 100755 shell/ash_test/ash-alias/alias_case.tests diff --git a/shell/ash.c b/shell/ash.c index f1c21188e..5a001b004 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -13265,10 +13265,14 @@ readtoken(void) if (kwd & CHKNL) { while (t == TNL) { parseheredoc(); + checkkwd = 0; t = xxreadtoken(); } } + kwd |= checkkwd; + checkkwd = 0; + if (t != TWORD || quoteflag) { goto out; } @@ -13287,7 +13291,7 @@ readtoken(void) } } - if (checkkwd & CHKALIAS) { + if (kwd & CHKALIAS) { #if ENABLE_ASH_ALIAS struct alias *ap; ap = lookupalias(wordtext, 1); @@ -13300,7 +13304,6 @@ readtoken(void) #endif } out: - checkkwd = 0; #if DEBUG if (!alreadyseen) TRACE(("token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : "")); diff --git a/shell/ash_test/ash-alias/alias_brace.right b/shell/ash_test/ash-alias/alias_brace.right new file mode 100644 index 000000000..7326d9603 --- /dev/null +++ b/shell/ash_test/ash-alias/alias_brace.right @@ -0,0 +1 @@ +Ok diff --git a/shell/ash_test/ash-alias/alias_brace.tests b/shell/ash_test/ash-alias/alias_brace.tests new file mode 100755 index 000000000..7571b64ac --- /dev/null +++ b/shell/ash_test/ash-alias/alias_brace.tests @@ -0,0 +1,16 @@ +# Note: bash would need: +#shopt -s expand_aliases +# to enable aliases in non-interactive mode +alias BEGIN={ END=} +BEGIN + cat <<- EOF > /dev/null + $(:) + EOF +END + +: <<- EOF && + $(:) +EOF +BEGIN + echo Ok +END diff --git a/shell/ash_test/ash-alias/alias_case.right b/shell/ash_test/ash-alias/alias_case.right new file mode 100644 index 000000000..7326d9603 --- /dev/null +++ b/shell/ash_test/ash-alias/alias_case.right @@ -0,0 +1 @@ +Ok diff --git a/shell/ash_test/ash-alias/alias_case.tests b/shell/ash_test/ash-alias/alias_case.tests new file mode 100755 index 000000000..ed8275875 --- /dev/null +++ b/shell/ash_test/ash-alias/alias_case.tests @@ -0,0 +1,8 @@ +# Note: bash would need: +#shopt -s expand_aliases +# to enable aliases in non-interactive mode +alias a="case x in " b=x +a +b) echo BAD;; +*) echo Ok;; +esac -- cgit v1.2.3-55-g6feb From 48cb983b136fb74c61db594a30e18bdc42b7264c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 8 Sep 2021 09:52:04 +0200 Subject: ash: parser: Get rid of PEOA Upstream commit: Date: Wed, 27 May 2020 12:19:13 +1000 parser: Get rid of PEOA PEOA is a special character used to mark an alias as being finished so that we don't enter an infinite loop with nested aliases. It complicates the parser because we have to ensure that it is skipped where necessary and not copied to the resulting token text. This patch removes it and instead delays the marking of aliases until the second pgetc. This has the same effect as the current PEOA code while keeping the complexities within the input code. This adds ~32 bytes of global data: function old new delta __pgetc - 512 +512 freestrings - 95 +95 popfile 86 110 +24 pushstring 141 160 +19 basepf 76 84 +8 syntax_index_table 258 257 -1 S_I_T 30 28 -2 .rodata 104255 104247 -8 pgetc_without_PEOA 13 - -13 xxreadtoken 230 215 -15 popstring 158 120 -38 readtoken1 3110 3045 -65 pgetc 547 22 -525 ------------------------------------------------------------------------------ (add/remove: 2/1 grow/shrink: 3/7 up/down: 658/-667) Total: -9 bytes text data bss dec hex filename 1043102 559 5020 1048681 100069 busybox_old 1043085 559 5052 1048696 100078 busybox_unstripped Signed-off-by: Denys Vlasenko --- shell/ash.c | 239 +++++++++++++++++++++++++++--------------------------------- 1 file changed, 108 insertions(+), 131 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 5a001b004..ba116d83a 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -295,6 +295,10 @@ typedef long arith_t; # define PIPE_BUF 4096 /* amount of buffering in a pipe */ #endif +#ifndef unlikely +# define unlikely(cond) (cond) +#endif + #if !BB_MMU # error "Do not even bother, ash will not run on NOMMU machine" #endif @@ -583,6 +587,9 @@ struct strpush { #endif char *string; /* remember the string since it may change */ + /* Delay freeing so we can stop nested aliases. */ + struct strpush *spfree; + /* Remember last two characters for pungetc. */ int lastc[2]; @@ -605,6 +612,9 @@ struct parsefile { struct strpush *strpush; /* for pushing strings at this level */ struct strpush basestrpush; /* so pushing one is fast */ + /* Delay freeing so we can stop nested aliases. */ + struct strpush *spfree; + /* Remember last two characters for pungetc. */ int lastc[2]; @@ -3013,12 +3023,8 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) #define CENDFILE 11 /* end of file */ #define CCTL 12 /* like CWORD, except it must be escaped */ #define CSPCL 13 /* these terminate a word */ -#define CIGN 14 /* character should be ignored */ #define PEOF 256 -#if ENABLE_ASH_ALIAS -# define PEOA 257 -#endif #define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE @@ -3028,49 +3034,43 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) # define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8)) #endif static const uint16_t S_I_T[] ALIGN2 = { -#if ENABLE_ASH_ALIAS - SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */ -#endif - SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */ - SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */ - SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */ - SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */ - SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */ - SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */ - SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */ - SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */ - SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */ - SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */ - SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */ + SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 0, ' ' */ + SIT_ITEM(CNL , CNL , CNL , CNL ), /* 1, \n */ + SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 2, !*-/:=?[]~ */ + SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 3, '"' */ + SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 4, $ */ + SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 5, "'" */ + SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 6, ( */ + SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 7, ) */ + SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 8, \ */ + SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 9, ` */ + SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 10, } */ #if !USE_SIT_FUNCTION - SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */ - SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */ - SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */ + SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 11, PEOF */ + SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 12, 0-9A-Za-z */ + SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 13, CTLESC ... */ #endif #undef SIT_ITEM }; /* Constants below must match table above */ enum { -#if ENABLE_ASH_ALIAS - CSPCL_CIGN_CIGN_CIGN , /* 0 */ -#endif - CSPCL_CWORD_CWORD_CWORD , /* 1 */ - CNL_CNL_CNL_CNL , /* 2 */ - CWORD_CCTL_CCTL_CWORD , /* 3 */ - CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */ - CVAR_CVAR_CWORD_CVAR , /* 5 */ - CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */ - CSPCL_CWORD_CWORD_CLP , /* 7 */ - CSPCL_CWORD_CWORD_CRP , /* 8 */ - CBACK_CBACK_CCTL_CBACK , /* 9 */ - CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */ - CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */ - CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */ - CWORD_CWORD_CWORD_CWORD , /* 13 */ - CCTL_CCTL_CCTL_CCTL , /* 14 */ + CSPCL_CWORD_CWORD_CWORD , /* 0 */ + CNL_CNL_CNL_CNL , /* 1 */ + CWORD_CCTL_CCTL_CWORD , /* 2 */ + CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 3 */ + CVAR_CVAR_CWORD_CVAR , /* 4 */ + CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 5 */ + CSPCL_CWORD_CWORD_CLP , /* 6 */ + CSPCL_CWORD_CWORD_CRP , /* 7 */ + CBACK_CBACK_CCTL_CBACK , /* 8 */ + CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 9 */ + CENDVAR_CENDVAR_CWORD_CENDVAR , /* 10 */ + CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 11 */ + CWORD_CWORD_CWORD_CWORD , /* 12 */ + CCTL_CCTL_CCTL_CCTL , /* 13 */ }; -/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF, +/* c in SIT(c, syntax) must be an *unsigned char* or PEOF, * caller must ensure proper cast on it if c is *char_ptr! */ #if USE_SIT_FUNCTION @@ -3088,44 +3088,28 @@ SIT(int c, int syntax) * but glibc one isn't. With '/' always treated as CWORD, * both work fine. */ -# if ENABLE_ASH_ALIAS - static const uint8_t syntax_index_table[] ALIGN1 = { - 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */ - 7, 8, 3, 3,/*3,*/3, 1, 1, /* "()*-/:;<" */ - 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */ - 11, 3 /* "}~" */ - }; -# else static const uint8_t syntax_index_table[] ALIGN1 = { 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */ 6, 7, 2, 2,/*2,*/2, 0, 0, /* "()*-/:;<" */ 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */ 10, 2 /* "}~" */ }; -# endif const char *s; int indx; if (c == PEOF) return CENDFILE; -# if ENABLE_ASH_ALIAS - if (c == PEOA) - indx = 0; - else -# endif - { - /* Cast is purely for paranoia here, - * just in case someone passed signed char to us */ - if ((unsigned char)c >= CTL_FIRST - && (unsigned char)c <= CTL_LAST - ) { - return CCTL; - } - s = strchrnul(spec_symbls, c); - if (*s == '\0') - return CWORD; - indx = syntax_index_table[s - spec_symbls]; + /* Cast is purely for paranoia here, + * just in case someone passed signed char to us */ + if ((unsigned char)c >= CTL_FIRST + && (unsigned char)c <= CTL_LAST + ) { + return CCTL; } + s = strchrnul(spec_symbls, c); + if (*s == '\0') + return CWORD; + indx = syntax_index_table[s - spec_symbls]; return (S_I_T[indx] >> (syntax*4)) & 0xf; } @@ -3396,9 +3380,6 @@ static const uint8_t syntax_index_table[] ALIGN1 = { /* 254 */ CWORD_CWORD_CWORD_CWORD, /* 255 */ CWORD_CWORD_CWORD_CWORD, /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE, -# if ENABLE_ASH_ALIAS - /* PEOA */ CSPCL_CIGN_CIGN_CIGN, -# endif }; #if 1 @@ -10712,7 +10693,7 @@ pushstring(char *s, struct alias *ap) len = strlen(s); INT_OFF; - if (g_parsefile->strpush) { + if (g_parsefile->strpush || g_parsefile->spfree) { sp = ckzalloc(sizeof(*sp)); sp->prev = g_parsefile->strpush; } else { @@ -10722,6 +10703,7 @@ pushstring(char *s, struct alias *ap) sp->prev_string = g_parsefile->next_to_pgetc; sp->prev_left_in_line = g_parsefile->left_in_line; sp->unget = g_parsefile->unget; + sp->spfree = g_parsefile->spfree; memcpy(sp->lastc, g_parsefile->lastc, sizeof(sp->lastc)); #if ENABLE_ASH_ALIAS sp->ap = ap; @@ -10733,11 +10715,11 @@ pushstring(char *s, struct alias *ap) g_parsefile->next_to_pgetc = s; g_parsefile->left_in_line = len; g_parsefile->unget = 0; + g_parsefile->spfree = NULL; INT_ON; } -static void -popstring(void) +static void popstring(void) { struct strpush *sp = g_parsefile->strpush; @@ -10752,10 +10734,6 @@ popstring(void) if (sp->string != sp->ap->val) { free(sp->string); } - sp->ap->flag &= ~ALIASINUSE; - if (sp->ap->flag & ALIASDEAD) { - unalias(sp->ap->name); - } } #endif g_parsefile->next_to_pgetc = sp->prev_string; @@ -10763,8 +10741,7 @@ popstring(void) g_parsefile->unget = sp->unget; memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc)); g_parsefile->strpush = sp->prev; - if (sp != &(g_parsefile->basestrpush)) - free(sp); + g_parsefile->spfree = sp; INT_ON; } @@ -10853,26 +10830,16 @@ preadfd(void) */ //#define pgetc_debug(...) bb_error_msg(__VA_ARGS__) #define pgetc_debug(...) ((void)0) -static int pgetc(void); +static int __pgetc(void); static int preadbuffer(void) { char *q; int more; - if (g_parsefile->strpush) { -#if ENABLE_ASH_ALIAS - if (g_parsefile->left_in_line == -1 - && g_parsefile->strpush->ap - && g_parsefile->next_to_pgetc[-1] != ' ' - && g_parsefile->next_to_pgetc[-1] != '\t' - ) { - pgetc_debug("preadbuffer PEOA"); - return PEOA; - } -#endif + if (unlikely(g_parsefile->strpush)) { popstring(); - return pgetc(); + return __pgetc(); } /* on both branches above g_parsefile->left_in_line < 0. * "pgetc" needs refilling. @@ -10966,8 +10933,31 @@ nlnoprompt(void) needprompt = doprompt; } -static int -pgetc(void) +static void freestrings(struct strpush *sp) +{ + INT_OFF; + do { + struct strpush *psp; + + if (sp->ap) { + sp->ap->flag &= ~ALIASINUSE; + if (sp->ap->flag & ALIASDEAD) { + unalias(sp->ap->name); + } + } + + psp = sp; + sp = sp->spfree; + + if (psp != &(g_parsefile->basestrpush)) + free(psp); + } while (sp); + + g_parsefile->spfree = NULL; + INT_ON; +} + +static int __pgetc(void) { int c; @@ -10989,23 +10979,19 @@ pgetc(void) return c; } -#if ENABLE_ASH_ALIAS -static int -pgetc_without_PEOA(void) +/* + * Read a character from the script, returning PEOF on end of file. + * Nul characters in the input are silently discarded. + */ +static int pgetc(void) { - int c; - do { - pgetc_debug("pgetc at %d:%p'%s'", - g_parsefile->left_in_line, - g_parsefile->next_to_pgetc, - g_parsefile->next_to_pgetc); - c = pgetc(); - } while (c == PEOA); - return c; + struct strpush *sp = g_parsefile->spfree; + + if (unlikely(sp)) + freestrings(sp); + + return __pgetc(); } -#else -# define pgetc_without_PEOA() pgetc() -#endif /* * Undo a call to pgetc. Only two characters may be pushed back. @@ -11082,6 +11068,7 @@ pushfile(void) pf->prev = g_parsefile; pf->pf_fd = -1; /*pf->strpush = NULL; - ckzalloc did it */ + /*pf->spfree = NULL;*/ /*pf->basestrpush.prev = NULL;*/ /*pf->unget = 0;*/ g_parsefile = pf; @@ -11099,8 +11086,12 @@ popfile(void) if (pf->pf_fd >= 0) close(pf->pf_fd); free(pf->buf); - while (pf->strpush) + if (g_parsefile->spfree) + freestrings(g_parsefile->spfree); + while (pf->strpush) { popstring(); + freestrings(g_parsefile->spfree); + } g_parsefile = pf->prev; free(pf); INT_ON; @@ -12390,7 +12381,7 @@ static int readtoken1(int c, int syntax, char *eofmark, int striptabs) { /* NB: syntax parameter fits into smallint */ - /* c parameter is an unsigned char or PEOF or PEOA */ + /* c parameter is an unsigned char or PEOF */ char *out; size_t len; struct nodelist *bqlist; @@ -12460,7 +12451,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) USTPUTC(c, out); break; case CBACK: /* backslash */ - c = pgetc_without_PEOA(); + c = pgetc(); if (c == PEOF) { USTPUTC(CTLESC, out); USTPUTC('\\', out); @@ -12567,8 +12558,6 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) break; case CENDFILE: goto endword; /* exit outer loop */ - case CIGN: - break; default: if (synstack->varnest == 0) { #if BASH_REDIR_OUTPUT @@ -12590,8 +12579,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) #endif goto endword; /* exit outer loop */ } - IF_ASH_ALIAS(if (c != PEOA)) - USTPUTC(c, out); + USTPUTC(c, out); } c = pgetc_top(synstack); } /* for (;;) */ @@ -12642,14 +12630,9 @@ checkend: { int markloc; char *p; -#if ENABLE_ASH_ALIAS - if (c == PEOA) - c = pgetc_without_PEOA(); -#endif if (striptabs) { - while (c == '\t') { - c = pgetc_without_PEOA(); - } + while (c == '\t') + c = pgetc(); } markloc = out - (char *)stackblock(); @@ -12663,7 +12646,7 @@ checkend: { * F * (see heredoc_bkslash_newline2.tests) */ - c = pgetc_without_PEOA(); + c = pgetc(); } if (c == '\n' || c == PEOF) { @@ -12788,7 +12771,6 @@ parsesub: { c = pgetc_eatbnl(); if ((checkkwd & CHKEOFMARK) - || c > 255 /* PEOA or PEOF */ || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) ) { #if BASH_DOLLAR_SQUOTE @@ -12811,7 +12793,7 @@ parsesub: { PARSEBACKQNEW(); } } else { - /* $VAR, $, ${...}, or PEOA/PEOF */ + /* $VAR, $, ${...}, or PEOF */ smalluint newsyn = synstack->syntax; USTPUTC(CTLVAR, out); @@ -13006,13 +12988,9 @@ parsebackq: { ) { STPUTC('\\', pout); } - if (pc <= 255 /* not PEOA or PEOF */) { - break; - } - /* fall through */ + break; case PEOF: - IF_ASH_ALIAS(case PEOA:) raise_error_syntax("EOF in backquote substitution"); case '\n': @@ -13147,7 +13125,7 @@ xxreadtoken(void) setprompt_if(needprompt, 2); for (;;) { /* until token or start of word found */ c = pgetc_eatbnl(); - if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) + if (c == ' ' || c == '\t') continue; if (c == '#') { @@ -13205,7 +13183,6 @@ xxreadtoken(void) c = pgetc_eatbnl(); switch (c) { case ' ': case '\t': - IF_ASH_ALIAS(case PEOA:) continue; case '#': while ((c = pgetc()) != '\n' && c != PEOF) -- cgit v1.2.3-55-g6feb From eb607777697f4c5eb2dfd86e5837a8c379f65979 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 9 Sep 2021 16:26:41 +0200 Subject: ash: eval: Prevent recursive PS4 expansion Date: Wed, 27 May 2020 13:19:10 +1000 eval: Prevent recursive PS4 expansion Yaroslav Halchenko wrote: > I like to (ab)use PS4 and set -x for tracing execution of scripts. > Reporting time and PID is very useful in this context. > > I am not 100% certain if bash's behavior (of actually running the command > embedded within PS4 string, probably eval'ing it) is actually POSIX > compliant, posh seems to not do that; but I think it is definitely not > desired for dash to just stall: > > - the script: > #!/bin/sh > set -x > export PS4='+ $(date +%T.%N) [$$] ' > echo "lets go" > sleep 1 > echo "done $var" > > - bash: > /tmp > bash --posix test.sh > +export 'PS4=+ $(date +%T.%N) [$$] ' > +PS4='+ $(date +%T.%N) [$$] ' > + 09:15:48.982296333 [2764323] echo 'lets go' > lets go > + 09:15:48.987829613 [2764323] sleep 1 > + 09:15:49.994485037 [2764323] echo 'done ' > done > ... > - dash: (stalls it set -x) > /tmp > dash test.sh > +export PS4=+ $(date +%T.%N) [$$] > ^C^C This patch fixes the infinite loop caused by repeated expansions of PS4. Signed-off-by: Denys Vlasenko --- shell/ash.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/shell/ash.c b/shell/ash.c index ba116d83a..3524d046e 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -404,6 +404,7 @@ struct globals_misc { uint8_t exitstatus; /* exit status of last command */ uint8_t back_exitstatus;/* exit status of backquoted command */ smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ + smallint inps4; /* Prevent PS4 nesting. */ int savestatus; /* exit status of last command outside traps */ int rootpid; /* pid of main shell */ /* shell level: 0 for the main shell, 1 for its children, and so on */ @@ -492,6 +493,7 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc; #define exitstatus (G_misc.exitstatus ) #define back_exitstatus (G_misc.back_exitstatus ) #define job_warning (G_misc.job_warning) +#define inps4 (G_misc.inps4 ) #define savestatus (G_misc.savestatus ) #define rootpid (G_misc.rootpid ) #define shlvl (G_misc.shlvl ) @@ -10423,10 +10425,12 @@ evalcommand(union node *cmd, int flags) } /* Print the command if xflag is set. */ - if (xflag) { + if (xflag && !inps4) { const char *pfx = ""; + inps4 = 1; fdprintf(preverrout_fd, "%s", expandstr(ps4val(), DQSYNTAX)); + inps4 = 0; sp = varlist.list; while (sp) { @@ -14323,6 +14327,7 @@ exitreset(void) } evalskip = 0; loopnest = 0; + inps4 = 0; /* from expand.c: */ ifsfree(); -- cgit v1.2.3-55-g6feb From 305a30d80b63e06d312c9d98ae73934ae143e564 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Thu, 9 Sep 2021 08:15:31 +0100 Subject: awk: fix read beyond end of buffer Commit 7d06d6e18 (awk: fix printf %%) can cause awk printf to read beyond the end of a strduped buffer: 2349 while (*f && *f != '%') 2350 f++; 2351 c = *++f; If the loop terminates because a NUL character is detected the character after the NUL is read. This can result in failures depending on the value of that character. function old new delta awk_printf 672 665 -7 Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- editors/awk.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index f7b8ef0d3..3594717b1 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -2348,17 +2348,19 @@ static char *awk_printf(node *n, size_t *len) s = f; while (*f && *f != '%') f++; - c = *++f; - if (c == '%') { /* double % */ - slen = f - s; - s = xstrndup(s, slen); - f++; - goto tail; - } - while (*f && !isalpha(*f)) { - if (*f == '*') - syntax_error("%*x formats are not supported"); - f++; + if (*f) { + c = *++f; + if (c == '%') { /* double % */ + slen = f - s; + s = xstrndup(s, slen); + f++; + goto tail; + } + while (*f && !isalpha(*f)) { + if (*f == '*') + syntax_error("%*x formats are not supported"); + f++; + } } c = *f; if (!c) { -- cgit v1.2.3-55-g6feb From 8a0adba9f67a661e0f2428bf43ae8da15f641ec0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 9 Sep 2021 18:57:07 +0200 Subject: awk: code shrink: avoid duplicate NUL checks and strlen() function old new delta awk_printf 665 652 -13 Signed-off-by: Denys Vlasenko --- editors/awk.c | 54 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index 3594717b1..46bda93b2 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -2345,38 +2345,49 @@ static char *awk_printf(node *n, size_t *len) var *arg; size_t slen; + /* Find end of the next format spec, or end of line */ s = f; - while (*f && *f != '%') + while (1) { + c = *f; + if (!c) /* no percent chars found at all */ + goto nul; + if (c == '%') { + c = *++f; + if (!c) /* we are past % in "....%" */ + goto nul; + break; + } + f++; + } + /* we are past % in "....%...", c == char after % */ + if (c == '%') { /* double % */ + slen = f - s; + s = xstrndup(s, slen); f++; - if (*f) { + goto tail; /* print "....%" part verbatim */ + } + while (1) { + if (isalpha(c)) + break; + if (c == '*') + syntax_error("%*x formats are not supported"); c = *++f; - if (c == '%') { /* double % */ + if (!c) { /* "....%...." and no letter found after % */ + /* Example: awk 'BEGIN { printf "^^^%^^^\n"; }' */ + nul: slen = f - s; - s = xstrndup(s, slen); - f++; - goto tail; - } - while (*f && !isalpha(*f)) { - if (*f == '*') - syntax_error("%*x formats are not supported"); - f++; + goto tail; /* print remaining string, exit loop */ } } - c = *f; - if (!c) { - /* Tail of fmt with no percent chars, - * or "....%" (percent seen, but no format specifier char found) - */ - slen = strlen(s); - goto tail; - } - sv = *++f; - *f = '\0'; + /* we are at A in "....%...A..." */ + arg = evaluate(nextarg(&n), TMPVAR); /* Result can be arbitrarily long. Example: * printf "%99999s", "BOOM" */ + sv = *++f; + *f = '\0'; if (c == 'c') { char cc = is_numeric(arg) ? getvar_i(arg) : *getvar_s(arg); char *r = xasprintf(s, cc ? cc : '^' /* else strlen will be wrong */); @@ -2395,6 +2406,7 @@ static char *awk_printf(node *n, size_t *len) } else if (strchr("eEfFgGaA", c)) { s = xasprintf(s, d); } else { +//TODO: GNU Awk 5.0.1: printf "%W" prints "%W", does not error out syntax_error(EMSG_INV_FMT); } } -- cgit v1.2.3-55-g6feb From e60c56932ed95eb1c72b12d7404d42798da61bca Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 9 Sep 2021 19:13:32 +0200 Subject: awk: code shrink function old new delta awk_printf 652 651 -1 Signed-off-by: Denys Vlasenko --- editors/awk.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index 46bda93b2..6644d7d6f 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -2351,16 +2351,15 @@ static char *awk_printf(node *n, size_t *len) c = *f; if (!c) /* no percent chars found at all */ goto nul; - if (c == '%') { - c = *++f; - if (!c) /* we are past % in "....%" */ - goto nul; - break; - } f++; + if (c == '%') + break; } - /* we are past % in "....%...", c == char after % */ - if (c == '%') { /* double % */ + /* we are past % in "....%..." */ + c = *f; + if (!c) /* "....%" */ + goto nul; + if (c == '%') { /* "....%%...." */ slen = f - s; s = xstrndup(s, slen); f++; -- cgit v1.2.3-55-g6feb From 857800c65584d544242c54eb873129c23ba20265 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 9 Sep 2021 19:26:39 +0200 Subject: awk: never return NULL from awk_printf() function old new delta awk_printf 651 628 -23 Signed-off-by: Denys Vlasenko --- editors/awk.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index 6644d7d6f..f6314ac72 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -2338,7 +2338,7 @@ static char *awk_printf(node *n, size_t *len) b = NULL; i = 0; - while (*f) { /* "print one format spec" loop */ + while (1) { /* "print one format spec" loop */ char *s; char c; char sv; @@ -2363,7 +2363,7 @@ static char *awk_printf(node *n, size_t *len) slen = f - s; s = xstrndup(s, slen); f++; - goto tail; /* print "....%" part verbatim */ + goto append; /* print "....%" part verbatim */ } while (1) { if (isalpha(c)) @@ -2412,7 +2412,7 @@ static char *awk_printf(node *n, size_t *len) slen = strlen(s); } *f = sv; - + append: if (i == 0) { b = s; i = slen; @@ -2422,7 +2422,7 @@ static char *awk_printf(node *n, size_t *len) b = xrealloc(b, i + slen + 1); strcpy(b + i, s); i += slen; - if (!c) /* tail? */ + if (!c) /* s is NOT allocated and this is the last part of string? */ break; free(s); } -- cgit v1.2.3-55-g6feb From 82c5eb8e4681ba345500e5c368fb54741bb0c450 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Wed, 25 Aug 2021 23:12:37 +0300 Subject: httpd,telnetd: make default port configurable BusyBox on Termux can't use ports less than 1024 it's patched to change default port for httpd to 8080 and telnetd to 8023. https://github.com/termux/termux-packages/blob/master/packages/busybox/0011-networking-telnetd-default-port.patch https://github.com/termux/termux-packages/blob/master/packages/busybox/0010-networking-httpd-default-port.patch To avoid such patches we can make port configurable. function old new delta packed_usage 33920 33914 -6 Signed-off-by: Sergey Ponomarev Signed-off-by: Denys Vlasenko --- include/usage.src.h | 3 +++ networking/httpd.c | 12 ++++++++++-- networking/telnetd.c | 10 ++++++++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/include/usage.src.h b/include/usage.src.h index 1ac252d1b..5d2038834 100644 --- a/include/usage.src.h +++ b/include/usage.src.h @@ -31,6 +31,9 @@ # define ADJTIME_PATH "/etc/adjtime" #endif +#define STR1(s) #s +#define STR(s) STR1(s) + INSERT #define busybox_notes_usage \ diff --git a/networking/httpd.c b/networking/httpd.c index 56ab85b82..9972a5378 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -102,6 +102,11 @@ //config: help //config: HTTP server. //config: +//config:config FEATURE_HTTPD_PORT_DEFAULT +//config: int "Default port" +//config: default 80 +//config: range 1 65535 +//config: //config:config FEATURE_HTTPD_RANGES //config: bool "Support 'Ranges:' header" //config: default y @@ -270,7 +275,7 @@ //usage: "\n -i Inetd mode" //usage: "\n -f Don't daemonize" //usage: "\n -v[v] Verbose" -//usage: "\n -p [IP:]PORT Bind to IP:PORT (default *:80)" +//usage: "\n -p [IP:]PORT Bind to IP:PORT (default *:"STR(CONFIG_FEATURE_HTTPD_PORT_DEFAULT)")" //usage: IF_FEATURE_HTTPD_SETUID( //usage: "\n -u USER[:GRP] Set uid/gid after binding to port") //usage: IF_FEATURE_HTTPD_BASIC_AUTH( @@ -316,6 +321,9 @@ #define HEADER_READ_TIMEOUT 60 +#define STR1(s) #s +#define STR(s) STR1(s) + static const char DEFAULT_PATH_HTTPD_CONF[] ALIGN1 = "/etc"; static const char HTTPD_CONF[] ALIGN1 = "httpd.conf"; static const char HTTP_200[] ALIGN1 = "HTTP/1.1 200 OK\r\n"; @@ -542,7 +550,7 @@ enum { SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \ IF_FEATURE_HTTPD_RANGES(range_start = -1;) \ - bind_addr_or_port = "80"; \ + bind_addr_or_port = STR(CONFIG_FEATURE_HTTPD_PORT_DEFAULT); \ index_page = index_html; \ file_size = -1; \ } while (0) diff --git a/networking/telnetd.c b/networking/telnetd.c index de4d733f9..581da1924 100644 --- a/networking/telnetd.c +++ b/networking/telnetd.c @@ -68,6 +68,12 @@ //config: help //config: Selecting this will make telnetd able to run standalone. //config: +//config:config FEATURE_TELNETD_PORT_DEFAULT +//config: int "Default port" +//config: default 23 +//config: range 1 65535 +//config: depends on FEATURE_TELNETD_STANDALONE +//config: //config:config FEATURE_TELNETD_INETD_WAIT //config: bool "Support -w SEC option (inetd wait mode)" //config: default y @@ -103,7 +109,7 @@ //usage: "\n -K Close connection as soon as login exits" //usage: "\n (normally wait until all programs close slave pty)" //usage: IF_FEATURE_TELNETD_STANDALONE( -//usage: "\n -p PORT Port to listen on" +//usage: "\n -p PORT Port to listen on. Default "STR(CONFIG_FEATURE_TELNETD_PORT_DEFAULT) //usage: "\n -b ADDR[:PORT] Address to bind to" //usage: "\n -F Run in foreground" //usage: "\n -i Inetd mode" @@ -708,7 +714,7 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv) } else { master_fd = 0; if (!(opt & OPT_WAIT)) { - unsigned portnbr = 23; + unsigned portnbr = CONFIG_FEATURE_TELNETD_PORT_DEFAULT; if (opt & OPT_PORT) portnbr = xatou16(opt_portnbr); master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr); -- cgit v1.2.3-55-g6feb From 7ab9cd23988b48956fcfe171d5828d61285baf40 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 9 Sep 2021 22:00:44 +0200 Subject: libbb: make bb_lookup_port() abort on bad port names Also, no need to preserve errno function old new delta .rodata 104247 104241 -6 bb_lookup_port 97 83 -14 nc_main 1039 1018 -21 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/3 up/down: 0/-41) Total: -41 bytes Signed-off-by: Denys Vlasenko --- libbb/xconnect.c | 20 ++++++-------------- networking/ftpgetput.c | 3 +-- networking/nc.c | 2 +- networking/nc_bloaty.c | 7 ++----- 4 files changed, 10 insertions(+), 22 deletions(-) diff --git a/libbb/xconnect.c b/libbb/xconnect.c index 5dd9cfd28..f1983a68b 100644 --- a/libbb/xconnect.c +++ b/libbb/xconnect.c @@ -115,27 +115,19 @@ void FAST_FUNC xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) /* Return port number for a service. * If "port" is a number use it as the port. - * If "port" is a name it is looked up in /etc/services, - * if it isnt found return default_port + * If "port" is a name it is looked up in /etc/services. + * if NULL, return default_port */ -unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned default_port) +unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned port_nr) { - unsigned port_nr = default_port; if (port) { - int old_errno; - - /* Since this is a lib function, we're not allowed to reset errno to 0. - * Doing so could break an app that is deferring checking of errno. */ - old_errno = errno; port_nr = bb_strtou(port, NULL, 10); if (errno || port_nr > 65535) { struct servent *tserv = getservbyname(port, protocol); - port_nr = default_port; - if (tserv) - port_nr = ntohs(tserv->s_port); -//FIXME: else: port string was garbage, but we don't report that??? + if (!tserv) + bb_error_msg_and_die("bad port '%s'", port); + port_nr = ntohs(tserv->s_port); } - errno = old_errno; } return (uint16_t)port_nr; } diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c index 30b3dabd1..4c92f34a1 100644 --- a/networking/ftpgetput.c +++ b/networking/ftpgetput.c @@ -290,8 +290,7 @@ static const char ftpgetput_longopts[] ALIGN1 = int ftpgetput_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ftpgetput_main(int argc UNUSED_PARAM, char **argv) { - const char *port = "ftp"; - /* socket to ftp server */ + const char *port = NULL; #if ENABLE_FTPPUT && !ENABLE_FTPGET # define ftp_action ftp_send diff --git a/networking/nc.c b/networking/nc.c index 705b7356a..d351bf72a 100644 --- a/networking/nc.c +++ b/networking/nc.c @@ -216,7 +216,7 @@ int nc_main(int argc, char **argv) close(sfd); } else { cfd = create_and_connect_stream_or_die(argv[0], - argv[1] ? bb_lookup_port(argv[1], "tcp", 0) : 0); + bb_lookup_port(argv[1], "tcp", 0)); } } diff --git a/networking/nc_bloaty.c b/networking/nc_bloaty.c index 25b95246f..cfa133eae 100644 --- a/networking/nc_bloaty.c +++ b/networking/nc_bloaty.c @@ -813,8 +813,6 @@ int nc_main(int argc UNUSED_PARAM, char **argv) //if (option_mask32 & OPT_o) /* hexdump log */ if (option_mask32 & OPT_p) { /* local source port */ o_lport = bb_lookup_port(str_p, o_udpmode ? "udp" : "tcp", 0); - if (!o_lport) - bb_error_msg_and_die("bad local port '%s'", str_p); } //if (option_mask32 & OPT_r) /* randomize various things */ //if (option_mask32 & OPT_u) /* use UDP */ @@ -827,9 +825,8 @@ int nc_main(int argc UNUSED_PARAM, char **argv) if (argv[0]) { themaddr = xhost2sockaddr(argv[0], - argv[1] - ? bb_lookup_port(argv[1], o_udpmode ? "udp" : "tcp", 0) - : 0); + bb_lookup_port(argv[1], o_udpmode ? "udp" : "tcp", 0) + ); } /* create & bind network socket */ -- cgit v1.2.3-55-g6feb From 0599e0f87bcaa4b9f91652fa53bc29a3bdacfa13 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 9 Sep 2021 23:45:13 +0200 Subject: basename: implement -a and -s SUFFIX function old new delta basename_main 145 207 +62 packed_usage 33914 33950 +36 .rodata 104241 104250 +9 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/0 up/down: 107/0) Total: 107 bytes Signed-off-by: Denys Vlasenko --- coreutils/basename.c | 62 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/coreutils/basename.c b/coreutils/basename.c index 0dd2c43c7..0b721c03c 100644 --- a/coreutils/basename.c +++ b/coreutils/basename.c @@ -29,9 +29,11 @@ /* http://www.opengroup.org/onlinepubs/007904975/utilities/basename.html */ //usage:#define basename_trivial_usage -//usage: "FILE [SUFFIX]" +//usage: "FILE [SUFFIX] | -a FILE... | -s SUFFIX FILE..." //usage:#define basename_full_usage "\n\n" -//usage: "Strip directory path and .SUFFIX from FILE" +//usage: "Strip directory path and SUFFIX from FILE\n" +//usage: "\n -a All arguments are FILEs" +//usage: "\n -s SUFFIX Remove SUFFIX (implies -a)" //usage: //usage:#define basename_example_usage //usage: "$ basename /usr/local/bin/foo\n" @@ -48,31 +50,43 @@ int basename_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int basename_main(int argc UNUSED_PARAM, char **argv) { - size_t m, n; - char *s; + unsigned opts; + const char *suffix = NULL; - if (argv[1] && strcmp(argv[1], "--") == 0) { - argv++; - } - if (!argv[1]) - bb_show_usage(); + /* '+': stop at first non-option */ + opts = getopt32(argv, "^+" "as:" + "\0" "-1" /* At least one argument */ + , &suffix + ); + argv += optind; - /* It should strip slash: /abc/def/ -> def */ - s = bb_get_last_path_component_strip(*++argv); + do { + char *s; + size_t m; - m = strlen(s); - if (*++argv) { - if (argv[1]) - bb_show_usage(); - n = strlen(*argv); - if ((m > n) && (strcmp(s+m-n, *argv) == 0)) { - m -= n; - /*s[m] = '\0'; - redundant */ + /* It should strip slash: /abc/def/ -> def */ + s = bb_get_last_path_component_strip(*argv++); + m = strlen(s); + if (!opts) { + if (*argv) { + suffix = *argv; + if (argv[1]) + bb_show_usage(); + } } - } + if (suffix) { + size_t n = strlen(suffix); + if ((m > n) && (strcmp(s + m - n, suffix) == 0)) { + m -= n; + /*s[m] = '\0'; - redundant */ + } + } + /* puts(s) will do, but we can do without stdio this way: */ + s[m++] = '\n'; + /* NB: != is correct here: */ + if (full_write(STDOUT_FILENO, s, m) != (ssize_t)m) + return EXIT_FAILURE; + } while (opts && *argv); - /* puts(s) will do, but we can do without stdio this way: */ - s[m++] = '\n'; - /* NB: != is correct here: */ - return full_write(STDOUT_FILENO, s, m) != (ssize_t)m; + return EXIT_SUCCESS; } -- cgit v1.2.3-55-g6feb From c421388dcaa6adba9f2f7cda3be4537daa466355 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 10 Sep 2021 00:20:05 +0200 Subject: blkdiscard: accept -f (force) as no-op function old new delta .rodata 104250 104251 +1 Signed-off-by: Denys Vlasenko --- util-linux/blkdiscard.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/util-linux/blkdiscard.c b/util-linux/blkdiscard.c index ff2101ed0..7ac8045f9 100644 --- a/util-linux/blkdiscard.c +++ b/util-linux/blkdiscard.c @@ -18,10 +18,12 @@ //usage:#define blkdiscard_trivial_usage //usage: "[-o OFS] [-l LEN] [-s] DEVICE" //usage:#define blkdiscard_full_usage "\n\n" -//usage: "Discard sectors on DEVICE\n" -//usage: "\n -o OFS Byte offset into device" -//usage: "\n -l LEN Number of bytes to discard" -//usage: "\n -s Perform a secure discard" +//usage: "Discard sectors on DEVICE\n" +//usage: "\n -o OFS Byte offset into device" +//usage: "\n -l LEN Number of bytes to discard" +//usage: "\n -s Perform a secure discard" +///////: "\n -f Disable check for mounted filesystem" +//////////////// -f: accepted but is a nop (we do no check anyway) //usage: //usage:#define blkdiscard_example_usage //usage: "$ blkdiscard -o 0 -l 1G /dev/sdb" @@ -51,9 +53,10 @@ int blkdiscard_main(int argc UNUSED_PARAM, char **argv) OPT_OFFSET = (1 << 0), OPT_LENGTH = (1 << 1), OPT_SECURE = (1 << 2), + OPT_FORCE = (1 << 3), //nop }; - opts = getopt32(argv, "^" "o:l:s" "\0" "=1", &offset_str, &length_str); + opts = getopt32(argv, "^" "o:l:sf" "\0" "=1", &offset_str, &length_str); argv += optind; fd = xopen(argv[0], O_RDWR|O_EXCL); -- cgit v1.2.3-55-g6feb From c00bcf2d2c41af832ca70c749f1300309bf1cb91 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 10 Sep 2021 09:48:55 +0200 Subject: libbb: reuse "bad port" error message string function old new delta .rodata 104251 104232 -19 Signed-off-by: Denys Vlasenko --- libbb/xconnect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbb/xconnect.c b/libbb/xconnect.c index f1983a68b..0e0b247b8 100644 --- a/libbb/xconnect.c +++ b/libbb/xconnect.c @@ -231,7 +231,7 @@ IF_NOT_FEATURE_IPV6(sa_family_t af = AF_INET;) cp++; /* skip ':' */ port = bb_strtou(cp, NULL, 10); if (errno || (unsigned)port > 0xffff) { - bb_error_msg("bad port spec '%s'", org_host); + bb_error_msg("bad port '%s'", cp); if (ai_flags & DIE_ON_ERROR) xfunc_die(); return NULL; -- cgit v1.2.3-55-g6feb From 40f2dd7dd2e50c9d81dda4d72bf9c85c4c479a89 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 10 Sep 2021 10:07:42 +0200 Subject: httpd: fix config deps Signed-off-by: Denys Vlasenko --- networking/httpd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/networking/httpd.c b/networking/httpd.c index 9972a5378..55ca2ae8b 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -106,6 +106,7 @@ //config: int "Default port" //config: default 80 //config: range 1 65535 +//config: depends on HTTPD //config: //config:config FEATURE_HTTPD_RANGES //config: bool "Support 'Ranges:' header" -- cgit v1.2.3-55-g6feb