package rosa import ( "fmt" "io" "net/http" "os" "slices" "strings" "time" "hakurei.app/container/fhs" "hakurei.app/internal/pkg" ) // busyboxBin is a busybox binary distribution installed under bin/busybox. type busyboxBin struct { // Underlying busybox binary. bin pkg.File } // Kind returns the hardcoded [pkg.Kind] value. func (a busyboxBin) Kind() pkg.Kind { return kindBusyboxBin } // Params is a noop. func (a busyboxBin) Params(*pkg.IContext) {} // Dependencies returns the underlying busybox [pkg.File]. func (a busyboxBin) Dependencies() []pkg.Artifact { return []pkg.Artifact{a.bin} } // String returns the reporting name of the underlying file prefixed with expand. func (a busyboxBin) String() string { return "expand-" + a.bin.(fmt.Stringer).String() } // Cure installs the underlying busybox [pkg.File] to bin/busybox. func (a busyboxBin) Cure(t *pkg.TContext) (err error) { var r io.ReadCloser if r, err = t.Open(a.bin); err != nil { return } defer func() { closeErr := r.Close() if err == nil { err = closeErr } }() binDir := t.GetWorkDir().Append("bin") if err = os.MkdirAll(binDir.String(), 0700); err != nil { return } var w *os.File if w, err = os.OpenFile( binDir.Append("busybox").String(), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0500, ); err != nil { return } defer func() { closeErr := w.Close() if err == nil { err = closeErr } }() _, err = io.Copy(w, r) return } // newBusyboxBin returns a [pkg.Artifact] containing a busybox installation from // the https://busybox.net/downloads/binaries/ binary release. func newBusyboxBin() pkg.Artifact { const ( version = "1.35.0" checksum = "L7OBIsPu9enNHn7FqpBT1kOg_mCLNmetSeNMA3i4Y60Z5jTgnlX3qX3zcQtLx5AB" ) return pkg.NewExec( "busybox-bin-"+version, nil, pkg.ExecTimeoutMax, fhs.AbsRoot, []string{ "PATH=/system/bin", }, AbsSystem.Append("bin", "busybox"), []string{"hush", "-c", "" + "busybox mkdir -p /work/system/bin/ && " + "busybox cp /system/bin/busybox /work/system/bin/ && " + "busybox --install -s /work/system/bin/"}, pkg.Path(AbsSystem, true, busyboxBin{pkg.NewHTTPGet( &http.Client{Transport: &http.Transport{ // busybox website is really slow to respond TLSHandshakeTimeout: 2 * time.Minute, }}, "https://busybox.net/downloads/binaries/"+ version+"-"+linuxArch()+"-linux-musl/busybox", mustDecode(checksum), )}), ) } // NewBusybox returns a [pkg.Artifact] containing a dynamically linked busybox // installation usable within the [Toolchain] it is compiled against. func (t Toolchain) NewBusybox() pkg.Artifact { const ( version = "1.37.0" checksum = "Ial94Tnt7esJ_YEeb0AxunVL6MGYFyOw7Rtu2o87CXCi1TLrc6rlznVsN1rZk7it" ) extra := []pkg.Artifact{ t.NewMake(), t.NewKernelHeaders(), } var env []string if t == toolchainStage3 { extra = nil env = append(env, "EXTRA_LDFLAGS=-static") } return t.New("busybox-"+version, extra, nil, slices.Concat([]string{ "ROSA_BUSYBOX_ENABLE=" + strings.Join([]string{ "STATIC", "PIE", }, " "), "ROSA_BUSYBOX_DISABLE=" + strings.Join([]string{ "FEATURE_IPV6", "FEATURE_PREFER_IPV4_ADDRESS", "FEATURE_HWIB", "ARP", "ARPING", "BRCTL", "FEATURE_BRCTL_FANCY", "FEATURE_BRCTL_SHOW", "DNSD", "ETHER_WAKE", "FTPD", "FEATURE_FTPD_WRITE", "FEATURE_FTPD_ACCEPT_BROKEN_LIST", "FEATURE_FTPD_AUTHENTICATION", "FTPGET", "FTPPUT", "FEATURE_FTPGETPUT_LONG_OPTIONS", "HOSTNAME", "DNSDOMAINNAME", "HTTPD", "FEATURE_HTTPD_PORT_DEFAULT", "FEATURE_HTTPD_RANGES", "FEATURE_HTTPD_SETUID", "FEATURE_HTTPD_BASIC_AUTH", "FEATURE_HTTPD_AUTH_MD5", "FEATURE_HTTPD_CGI", "FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR", "FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV", "FEATURE_HTTPD_ENCODE_URL_STR", "FEATURE_HTTPD_ERROR_PAGES", "FEATURE_HTTPD_PROXY", "FEATURE_HTTPD_GZIP", "FEATURE_HTTPD_ETAG", "FEATURE_HTTPD_LAST_MODIFIED", "FEATURE_HTTPD_DATE", "FEATURE_HTTPD_ACL_IP", "IFCONFIG", "FEATURE_IFCONFIG_STATUS", "FEATURE_IFCONFIG_SLIP", "FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ", "FEATURE_IFCONFIG_HW", "FEATURE_IFCONFIG_BROADCAST_PLUS", "IFENSLAVE", "IFPLUGD", "IFUP", "IFDOWN", "IFUPDOWN_IFSTATE_PATH", "FEATURE_IFUPDOWN_IP", "FEATURE_IFUPDOWN_IPV4", "FEATURE_IFUPDOWN_IPV6", "FEATURE_IFUPDOWN_MAPPING", "INETD", "FEATURE_INETD_SUPPORT_BUILTIN_ECHO", "FEATURE_INETD_SUPPORT_BUILTIN_DISCARD", "FEATURE_INETD_SUPPORT_BUILTIN_TIME", "FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME", "FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN", "IP", "IPADDR", "IPLINK", "IPROUTE", "IPTUNNEL", "IPRULE", "IPNEIGH", "FEATURE_IP_ADDRESS", "FEATURE_IP_LINK", "FEATURE_IP_LINK_CAN", "FEATURE_IP_ROUTE", "FEATURE_IP_ROUTE_DIR", "FEATURE_IP_TUNNEL", "FEATURE_IP_RULE", "FEATURE_IP_NEIGH", "IPCALC", "FEATURE_IPCALC_LONG_OPTIONS", "FEATURE_IPCALC_FANCY", "FAKEIDENTD", "NAMEIF", "FEATURE_NAMEIF_EXTENDED", "NBDCLIENT", "NC", "NC_SERVER", "NC_EXTRA", "NC_110_COMPAT", "NETSTAT", "FEATURE_NETSTAT_WIDE", "FEATURE_NETSTAT_PRG", "NSLOOKUP", "FEATURE_NSLOOKUP_BIG", "FEATURE_NSLOOKUP_LONG_OPTIONS", "NTPD", "FEATURE_NTPD_SERVER", "FEATURE_NTPD_CONF", "FEATURE_NTP_AUTH", "PING", "PING6", "FEATURE_FANCY_PING", "PSCAN", "ROUTE", "SLATTACH", "SSL_CLIENT", "TC", "FEATURE_TC_INGRESS", "TCPSVD", "UDPSVD", "TELNET", "FEATURE_TELNET_TTYPE", "FEATURE_TELNET_AUTOLOGIN", "FEATURE_TELNET_WIDTH", "TELNETD", "FEATURE_TELNETD_STANDALONE", "FEATURE_TELNETD_PORT_DEFAULT", "FEATURE_TELNETD_INETD_WAIT", "TFTP", "FEATURE_TFTP_PROGRESS_BAR", "FEATURE_TFTP_HPA_COMPAT", "TFTPD", "FEATURE_TFTP_GET", "FEATURE_TFTP_PUT", "FEATURE_TFTP_BLOCKSIZE", "TLS", "TRACEROUTE", "TRACEROUTE6", "FEATURE_TRACEROUTE_VERBOSE", "FEATURE_TRACEROUTE_USE_ICMP", "TUNCTL", "FEATURE_TUNCTL_UG", "VCONFIG", "WGET", "FEATURE_WGET_LONG_OPTIONS", "FEATURE_WGET_STATUSBAR", "FEATURE_WGET_FTP", "FEATURE_WGET_AUTHENTICATION", "FEATURE_WGET_TIMEOUT", "FEATURE_WGET_HTTPS", "FEATURE_WGET_OPENSSL", "WHOIS", "ZCIP", "UDHCPD", "FEATURE_UDHCPD_BOOTP", "FEATURE_UDHCPD_WRITE_LEASES_EARLY", "DHCPD_LEASES_FILE", "DUMPLEASES", "DHCPRELAY", "UDHCPC", "FEATURE_UDHCPC_ARPING", "FEATURE_UDHCPC_SANITIZEOPT", "UDHCPC_DEFAULT_SCRIPT", "UDHCPC6_DEFAULT_SCRIPT", "UDHCPC6", "FEATURE_UDHCPC6_RFC3646", "FEATURE_UDHCPC6_RFC4704", "FEATURE_UDHCPC6_RFC4833", "FEATURE_UDHCPC6_RFC5970", }, " "), }, env), ` config_enable() { for ent in "$@"; do sed "s/^# CONFIG_${ent}.*/CONFIG_${ent}=y/" -i .config shift done } config_disable() { for ent in "$@"; do sed "s/^CONFIG_${ent}=y/# CONFIG_${ent} is not set/" -i .config shift done } cat > /bin/gcc << EOF exec clang \ -Wno-ignored-optimization-argument \ ${ROSA_CFLAGS} \ ${LDFLAGS} \ \$@ EOF chmod +x /bin/gcc cd /usr/src/busybox chmod +w editors editors/awk.c patch -p 1 < /usr/src/patches/awk-fix-literal-backslash.patch cd "$(mktemp -d)" make \ KBUILD_SRC=/usr/src/busybox \ -f /usr/src/busybox/Makefile \ defconfig config_enable $ROSA_BUSYBOX_ENABLE config_disable $ROSA_BUSYBOX_DISABLE ln -s ../system/bin/pwd /bin/pwd || true make CFLAGS_busybox="${LDFLAGS} ${EXTRA_LDFLAGS}" "-j$(nproc)" mkdir -p /system/bin/ /work/bin/ cp busybox /system/bin/ mkdir -pv /work/system/bin/ busybox --install -s /work/system/bin/ cp -v busybox /work/system/bin/ ln -vs ../system/bin/hush /work/bin/sh `, pkg.Path(AbsUsrSrc.Append("busybox"), true, pkg.NewHTTPGetTar( &http.Client{Transport: &http.Transport{ // busybox website is really slow to respond TLSHandshakeTimeout: 2 * time.Minute, }}, "https://busybox.net/downloads/busybox-"+version+".tar.bz2", mustDecode(checksum), pkg.TarBzip2, )), pkg.Path( AbsUsrSrc.Append("patches", "awk-fix-literal-backslash.patch"), false, pkg.NewFile("awk-fix-literal-backslash.patch", []byte(`diff --git a/editors/awk.c b/editors/awk.c index 64e752f4b..40f5ba7f7 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -2636,8 +2636,13 @@ static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest /*,in resbuf = qrealloc(resbuf, residx + replen + n, &resbufsize); memcpy(resbuf + residx, sp + pmatch[j].rm_so - start_ofs, n); residx += n; - } else + } else { +/* '\\' and '&' following a backslash keep its original meaning, any other + * occurrence of a '\\' should be treated as literal */ + if (bslash && c != '\\' && c != '&') + resbuf[residx++] = '\\'; resbuf[residx++] = c; + } bslash = 0; } }`)), )) }