diff --git a/.github/workflows/code-test.yml b/.github/workflows/code-test.yml index 585cd59b8e..ae98ee1c01 100644 --- a/.github/workflows/code-test.yml +++ b/.github/workflows/code-test.yml @@ -5,6 +5,7 @@ on: jobs: build: runs-on: ubuntu-latest + name: Build test steps: - uses: actions/checkout@v4 @@ -27,13 +28,13 @@ jobs: meson setup build \ -Dprefix=/data/share \ - -Dinit-script=monitd \ - -Druntime-path=/data/local/tmp \ + -Dinit-script=module \ + -Druntime-path=/data/share/tmp \ -Dstrip=true \ -Dd_lto=true \ -Db_pie=false \ -Dlog-path=log/lxc \ - -Ddata-path=/data/share/lib/lxc \ + -Ddata-path=lib/lxc \ --localstatedir=/data/share/var \ -Dmemfd-rexec=true \ --buildtype debug \ @@ -54,3 +55,9 @@ jobs: with: name: android-aarch64-lxc-shared-api30 path: /data/share/* + + - name: Upload artifacts lxc-module + uses: actions/upload-artifact@v4.3.1 + with: + name: android-lxc-module + path: lxc-module.zip diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fb4947c5f5..607b13c997 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -57,12 +57,12 @@ jobs: meson setup build \ -Dprefix=/data/share \ - -Dinit-script=monitd \ - -Druntime-path=/data/local/tmp \ + -Dinit-script=module \ + -Druntime-path=/data/share/tmp \ -Dstrip=true \ -Dd_lto=true \ -Dlog-path=log/lxc \ - -Ddata-path=/data/share/lib/lxc \ + -Ddata-path=lib/lxc \ --localstatedir=/data/share/var \ -Db_pie=false \ -Dmemfd-rexec=false \ @@ -92,6 +92,12 @@ jobs: name: android-${{ github.event.inputs.target_arch || 'aarch64' }}-lxc path: /data/share/* + - name: Upload artifacts lxc-module + uses: actions/upload-artifact@v4.3.1 + with: + name: android-lxc-module + path: lxc-module.zip + - name: Create a TAR file for artifact run: | tar -czvf android-${{ github.event.inputs.target_arch || 'aarch64' }}-api30-lxc.tar.gz -C /data/share . @@ -108,3 +114,4 @@ jobs: files: | android-${{ github.event.inputs.target_arch || 'aarch64' }}-api30-lxc.tar.gz android-${{ github.event.inputs.target_arch || 'aarch64' }}-api30-sysroot.tar.gz + lxc-module.zip diff --git a/config/init/common/lxc-net-builtin.in b/config/init/common/lxc-net-builtin.in new file mode 100755 index 0000000000..520628d893 --- /dev/null +++ b/config/init/common/lxc-net-builtin.in @@ -0,0 +1,234 @@ +#!/bin/sh - + +distrosysconfdir="@LXC_DISTRO_SYSCONF@" +varrun="@RUNTIME_PATH@/lxc" +varlib="@LOCALSTATEDIR@/lib" + +# These can be overridden in @LXC_DISTRO_SYSCONF@/lxc +# or in @LXC_DISTRO_SYSCONF@/lxc-net-builtin + +# This source code has been copied and modified +# from "lxc-net.in" and is protected by the same +# license as the original file. + +# For Android built-in dnsmasq, IPv6 support is incomplete and conflicts +# with Android hotspots, as they attempt to bind to all addresses simultaneously. +# So we recommend using a more complete and higher version of dnsmasq, such +# as Termux's dnsmasq in root-repo or others. + +LXC_BRIDGE="lxcbr0" +LXC_BRIDGE_MAC="10:66:6a:00:00:00" +LXC_ADDR="10.0.3.1" +LXC_NETMASK="255.255.255.0" +LXC_NETWORK="10.0.3.0/24" +LXC_DHCP_RANGE="10.0.3.2,10.0.3.254,24" +LXC_DHCP_MAX="253" +LXC_DHCP_CONFILE="" +LXC_DHCP_PING="true" +LXC_DOMAIN="" +LXC_USE_NFT="false" +DNSMASQ_USER="nobody" +LXC_IPV4_DNS1="8.8.8.8" +LXC_IPV4_DNS2="8.8.4.4" + +[ ! -f $distrosysconfdir/lxc ] || . $distrosysconfdir/lxc + +use_nft() { + [ -n "$NFT" ] && nft list ruleset > /dev/null 2>&1 && [ "$LXC_USE_NFT" = "true" ] +} + +NFT="$(command -v nft)" +if ! use_nft; then + use_iptables_lock="-w" + iptables -w -L -n > /dev/null 2>&1 || use_iptables_lock="" +fi + +_netmask2cidr () +{ + # Assumes there's no "255." after a non-255 byte in the mask + local x=${1##*255.} + set -- 0^^^128^192^224^240^248^252^254^ $(( (${#1} - ${#x})*2 )) ${x%%.*} + x=${1%%$3*} + echo $(( $2 + (${#x}/4) )) +} + +_ifdown() { + ip addr flush dev ${LXC_BRIDGE} + ip link set dev ${LXC_BRIDGE} down +} + +_ifup() { + MASK=$(_netmask2cidr ${LXC_NETMASK}) + CIDR_ADDR="${LXC_ADDR}/${MASK}" + ip addr add ${CIDR_ADDR} broadcast + dev ${LXC_BRIDGE} + ip link set dev ${LXC_BRIDGE} address $LXC_BRIDGE_MAC + ip link set dev ${LXC_BRIDGE} up +} + +start_iptables() { + iptables -P FORWARD ACCEPT + iptables $use_iptables_lock -I INPUT -i ${LXC_BRIDGE} -p udp --dport 67 -j ACCEPT + iptables $use_iptables_lock -I INPUT -i ${LXC_BRIDGE} -p tcp --dport 67 -j ACCEPT + iptables $use_iptables_lock -I INPUT -i ${LXC_BRIDGE} -p udp --dport 53 -j ACCEPT + iptables $use_iptables_lock -I INPUT -i ${LXC_BRIDGE} -p tcp --dport 53 -j ACCEPT + iptables $use_iptables_lock -I FORWARD -i ${LXC_BRIDGE} -j ACCEPT + iptables $use_iptables_lock -I FORWARD -o ${LXC_BRIDGE} -j ACCEPT + iptables $use_iptables_lock -t nat -A POSTROUTING -s ${LXC_NETWORK} ! -d ${LXC_NETWORK} -j MASQUERADE + iptables $use_iptables_lock -t mangle -A POSTROUTING -o ${LXC_BRIDGE} -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill +} + +start_nftables() { + NFT_RULESET="" + NFT_RULESET="${NFT_RULESET}; +add table inet lxc; +flush table inet lxc; +add chain inet lxc input { type filter hook input priority 0; }; +add rule inet lxc input iifname ${LXC_BRIDGE} udp dport { 53, 67 } accept; +add rule inet lxc input iifname ${LXC_BRIDGE} tcp dport { 53, 67 } accept; +add chain inet lxc forward { type filter hook forward priority 0; }; +add rule inet lxc forward iifname ${LXC_BRIDGE} accept; +add rule inet lxc forward oifname ${LXC_BRIDGE} accept; +add table ip lxc; +flush table ip lxc; +add chain ip lxc postrouting { type nat hook postrouting priority 100; }; +add rule ip lxc postrouting ip saddr ${LXC_NETWORK} ip daddr != ${LXC_NETWORK} counter masquerade" + nft "${NFT_RULESET}" +} + +start() { + [ ! -f "${varrun}/network_up" ] || { echo "lxc-net-builtin is already running"; exit 1; } + + if [ -d /sys/class/net/${LXC_BRIDGE} ]; then + stop force || true + fi + + FAILED=1 + + cleanup() { + set +e + if [ "$FAILED" = "1" ]; then + echo "Failed to setup lxc-net-builtin." >&2 + stop force + exit 1 + fi + } + + trap cleanup EXIT HUP INT TERM + set -e + + # set up the lxc network + [ ! -d /sys/class/net/${LXC_BRIDGE} ] && ip link add dev ${LXC_BRIDGE} type bridge + echo 1 > /proc/sys/net/ipv4/ip_forward + + # if we are run from systemd on a system with selinux enabled, + # the mkdir will create /run/lxc as init_var_run_t which dnsmasq + # can't write its pid into, so we restorecon it (to var_run_t) + if [ ! -d "${varrun}" ]; then + mkdir -p "${varrun}" + if command -v restorecon >/dev/null 2>&1; then + restorecon "${varrun}" + fi + fi + + _ifup + + if use_nft; then + start_nftables + else + start_iptables + fi + + LXC_DOMAIN_ARG="" + if [ -n "$LXC_DOMAIN" ]; then + LXC_DOMAIN_ARG="-s $LXC_DOMAIN -S /$LXC_DOMAIN/" + fi + + # lxc's dnsmasq should be hermetic and not read `/etc/dnsmasq.conf` (which + # it does by default if `--conf-file` is not present + LXC_DHCP_CONFILE_ARG="--conf-file=${LXC_DHCP_CONFILE:-/dev/null}" + + LXC_DHCP_PING_ARG="" + if [ "x$LXC_DHCP_PING" = "xfalse" ]; then + LXC_DHCP_PING_ARG="--no-ping" + fi + + DNSMASQ_MISC_DIR="$varlib/misc" + if [ ! -d "$DNSMASQ_MISC_DIR" ]; then + mkdir -p "$DNSMASQ_MISC_DIR" + fi + + /system/bin/dnsmasq $LXC_DHCP_CONFILE_ARG $LXC_DOMAIN_ARG $LXC_DHCP_PING_ARG -u ${DNSMASQ_USER} \ + --strict-order --bind-interfaces --pid-file="${varrun}"/dnsmasq.pid \ + --listen-address ${LXC_ADDR} --dhcp-range ${LXC_DHCP_RANGE} \ + --dhcp-lease-max=${LXC_DHCP_MAX} --dhcp-no-override \ + --except-interface=lo --interface=${LXC_BRIDGE} \ + --dhcp-leasefile="${DNSMASQ_MISC_DIR}"/dnsmasq.${LXC_BRIDGE}.leases \ + --dhcp-authoritative --server=${LXC_IPV4_DNS1} --server=${LXC_IPV4_DNS2} || cleanup + + touch "${varrun}"/network_up + FAILED=0 +} + +stop_iptables() { + iptables $use_iptables_lock -D INPUT -i ${LXC_BRIDGE} -p udp --dport 67 -j ACCEPT + iptables $use_iptables_lock -D INPUT -i ${LXC_BRIDGE} -p tcp --dport 67 -j ACCEPT + iptables $use_iptables_lock -D INPUT -i ${LXC_BRIDGE} -p udp --dport 53 -j ACCEPT + iptables $use_iptables_lock -D INPUT -i ${LXC_BRIDGE} -p tcp --dport 53 -j ACCEPT + iptables $use_iptables_lock -D FORWARD -i ${LXC_BRIDGE} -j ACCEPT + iptables $use_iptables_lock -D FORWARD -o ${LXC_BRIDGE} -j ACCEPT + iptables $use_iptables_lock -t nat -D POSTROUTING -s ${LXC_NETWORK} ! -d ${LXC_NETWORK} -j MASQUERADE + iptables $use_iptables_lock -t mangle -D POSTROUTING -o ${LXC_BRIDGE} -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill +} + +stop_nftables() { + # Adding table before removing them is just to avoid + # delete error for non-existent table + NFT_RULESET="add table inet lxc; +delete table inet lxc; +add table ip lxc; +delete table ip lxc; +" + nft "${NFT_RULESET}" +} + +stop() { + [ -f "${varrun}/network_up" ] || [ "$1" = "force" ] || { echo "lxc-net-builtin isn't running"; exit 1; } + + if [ -d /sys/class/net/${LXC_BRIDGE} ]; then + _ifdown + if use_nft; then + stop_nftables + else + stop_iptables + fi + + pid=$(cat "${varrun}"/dnsmasq.pid 2>/dev/null) && kill -9 $pid + rm -f "${varrun}"/dnsmasq.pid + # if $LXC_BRIDGE has attached interfaces, don't destroy the bridge + ls /sys/class/net/${LXC_BRIDGE}/brif/* > /dev/null 2>&1 || ip link delete ${LXC_BRIDGE} + fi + + rm -f "${varrun}"/network_up +} + +# See how we were called. +case "$1" in + start) + start + ;; + + stop) + stop + ;; + + restart|reload|force-reload) + $0 stop + $0 start + ;; + + *) + echo "Usage: $0 {start|stop|restart|reload|force-reload}" + exit 2 +esac + +exit $? diff --git a/config/init/common/meson.build b/config/init/common/meson.build index c322c85e01..5e1ed8b0e5 100644 --- a/config/init/common/meson.build +++ b/config/init/common/meson.build @@ -13,3 +13,10 @@ lxc_net = configure_file( output: 'lxc-net', install: true, install_dir: lxclibexec) + +lxc_net_builtin = configure_file( + configuration: conf, + input: 'lxc-net-builtin.in', + output: 'lxc-net-builtin', + install: true, + install_dir: lxclibexec) diff --git a/config/init/module/boot-completed.sh.in b/config/init/module/boot-completed.sh.in new file mode 100644 index 0000000000..7b9c0b2e04 --- /dev/null +++ b/config/init/module/boot-completed.sh.in @@ -0,0 +1,12 @@ +#!/bin/sh + +export LD_LIBRARY_PATH=/system/lib64:/system/lib:@LIBDIR@:/data/sysroot/lib64:/data/sysroot/lib + +# lxc-autostart +mkdir -p @LOCALSTATEDIR@/lock +@LIBEXECDIR@/lxc/lxc-containers start + +while true do + @LXCHOOKDIR@/android-network + sleep 5 +done \ No newline at end of file diff --git a/config/init/module/customize.sh.in b/config/init/module/customize.sh.in new file mode 100644 index 0000000000..3c8de46e2d --- /dev/null +++ b/config/init/module/customize.sh.in @@ -0,0 +1,65 @@ +#!/bin/sh + +ui_print "- [I] Checking env..." + +if [ -n $KSU ]; then + ui_print "- [D] Su manager is KernelSU." +fi + +if [ $ARCH = "arm64" ]; then + continue +else + abort "- [E] Unsupported arch: $ARCH." +fi + +if [ $API -ge 30 ]; then + continue +else + ui_print "- [W] Func memfd_create() required API 30(R)." +fi + +ui_print "- [D] Fetching download link..." + +DOWNLOAD_URLS=$(curl -s https://api.github.com/repos/Container-On-Android/lxc/releases/latest | grep browser_download_url | cut -d'"' -f4 | grep -E 'gz$') + +URL_LXC=$(echo "$DOWNLOAD_URLS" | sed -n '1p') +URL_SYSROOT=$(echo "$DOWNLOAD_URLS" | sed -n '2p') + +if [ -z $URL_LXC ] && [ -z $URL_SHARE ]; then + abort "- [E] Error: URL_LXC: $URL_LXC URL_SHARE: $URL_SHARE." +else + ui_print "- [D] Fetch ok..." +fi + +ui_print "- [I] Downloading lxc..." + +wget -q $URL_LXC -O /data/local/tmp/lxc.tar.gz + +if [ $? -eq 0 ]; then + ui_print "- [I] Download lxc ok..." +else + rm /data/local/tmp/lxc.tar.gz + abort "- [E] Failed to download lxc. ($URL_LXC)" +fi + +ui_print "- [I] Downloading lxc-sysroot..." + +wget -q $URL_SYSROOT -O /data/local/tmp/lxc-sysroot.tar.gz + +if [ $? -eq 0 ]; then + ui_print "- [D] Download lxc-sysroot ok..." +else + rm /data/local/tmp/lxc-sysroot.tar.gz + abort "- [E] Failed to download lxc-sysroot. ($URL_SYSROOT)" +fi + +ui_print "- [I] Installing lxc..." +mkdir -p @PREFIXDIR@ +mkdir -p /data/sysroot +tar zxf /data/local/tmp/lxc.tar.gz -C @PREFIXDIR@ +tar zxf /data/local/tmp/lxc-sysroot.tar.gz -C /data/sysroot +ui_print "- [I] OK!" + +ui_print "- [I] Clean!" +rm /data/local/tmp/lxc.tar.gz +rm /data/local/tmp/lxc-sysroot.tar.gz \ No newline at end of file diff --git a/config/init/module/meson.build b/config/init/module/meson.build new file mode 100644 index 0000000000..477f1e98f1 --- /dev/null +++ b/config/init/module/meson.build @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: MIT + +if 'module' in init_script + configured_files = [] + + foreach module_files: [ + 'boot-completed.sh.in', + 'customize.sh.in', + 'module.prop.in', + 'service.sh.in', + 'uninstall.sh.in', + ] + configured_file = configure_file( + input: module_files, + output: '@BASENAME@', + configuration: conf, + ) + configured_files += configured_file + endforeach + + configure_file( + command: [zip, '-rj', '@OUTPUT@', '@INPUT@'], + input: configured_files, + output: 'lxc-module.zip', + install: true, + install_dir: project_source_root) + +endif diff --git a/config/init/module/module.prop.in b/config/init/module/module.prop.in new file mode 100644 index 0000000000..4b0572905a --- /dev/null +++ b/config/init/module/module.prop.in @@ -0,0 +1,6 @@ +id=lxc +name=LXC-Daemon +version=1.0.0 +versionCode=10001 +author=@Container-On-Android +description=description \ No newline at end of file diff --git a/config/init/module/service.sh.in b/config/init/module/service.sh.in new file mode 100644 index 0000000000..6901e78904 --- /dev/null +++ b/config/init/module/service.sh.in @@ -0,0 +1,107 @@ +#!/bin/sh + +MODDIR=${0%/*} +PKG_TERMUX=/data/data/com.termux + +NET_STATE="" +NET_FUNC="" +GPU_STATE="" +AUD_STATE="" +DAEMON_STATE="" + +LXC_DIR="@PREFIXDIR@" +LXC_TMPDIR=$LXC_DIR/tmp +LXC_NET=$LXC_DIR/libexec/lxc/lxc-net +LXC_NET_BUILTIN=$LXC_DIR/libexec/lxc/lxc-net-builtin +LXC_DNSMASQ=$LXC_DIR/bin/dnsmasq + +NET_FUNC="" +GPU_FUNC="" +GPU_FUNC_DIR=/storage/emulated/0/lxc + +# Init desc +sed -i "s|description=.*|description={}|g" $MODDIR/module.prop + +# Setup lxc-tmp +if ! mountpoint -q $LXC_TMPDIR; then + mkdir -p $LXC_TMPDIR + mount -t tmpfs tmpfs $LXC_TMPDIR +else + continue +fi + +# Setup Cgroup +if mountpoint -q /sys/fs/cgroup; then + if mountpoint -q /sys/fs/cgroup/unified > /dev/null 2>&1; then + umount /sys/fs/cgroup/unified + fi + umount /sys/fs/cgroup + mount -t tmpfs tmpfs /sys/fs/cgroup + mkdir -p /sys/fs/cgroup/unified + mount -t cgroup2 cgroup2 -o rw,nosuid,nodev,noexec,relatime /sys/fs/cgroup/unified +else + mount -t tmpfs tmpfs /sys/fs/cgroup + mkdir -p /sys/fs/cgroup/unified + mount -t cgroup2 cgroup2 -o rw,nosuid,nodev,noexec,relatime /sys/fs/cgroup/unified +fi + +for subsys in cpu cpuacct memory freezer devices blkio pids cpuset systemd; do + if ! mountpoint -q /sys/fs/cgroup/$subsys > /dev/null 2>&1; then + mkdir -p /sys/fs/cgroup/$subsys + + if [ $subsys == "systemd" ]; then + mount -t cgroup -o none,name=$subsys cgroup /sys/fs/cgroup/$subsys + else + mount -t cgroup -o rw,nosuid,nodev,noexec,relatime,$subsys cgroup /sys/fs/cgroup/$subsys + fi + + if [ $? -eq 0 ]; then + echo "$subsys ok" + else + echo "$subsys failed" + fi + else + echo "$subsys mounted" + fi +done + +# Start lxc-net +if [ -e $LXC_DNSMASQ ]; then + $LXC_NET start + NET_FUNC="full" +else + $LXC_NET_BUILTIN start + NET_FUNC="IPv6 disabled, Hotspot conflict." +fi + +if [ -e $LXC_TMPDIR/lxc/network_up ]; then + NET_STATE="lxc-net ✅ ($NET_FUNC)" +else + NET_STATE="lxc-net ❎" +fi + +# Start lxc-gpu +if [ -e $GPU_FUNC_DIR/gpu ]; then + GPU_FUNC=$(cat $GPU_FUNC_DIR/gpu) + if [ "$GPU_FUNC" = "termux virgl" ]; then + GPU_STATE="lxc-gpu ✅ ($GPU_FUNC)" + cat > $GPU_FUNC_DIR/gpu.conf </config. +# need to run "systemctl mask tmp.mount" in container. +lxc.mount.entry = $PKG_TERMUX/files/usr/tmp tmp tmpfs bind,rw 0 0 +EOF + else + GPU_STATE="lxc-gpu ✅ (Unsupported $GPU_FUNC)" + fi +else + GPU_STATE="lxc-gpu ❎ (not loaded)" +fi + +# Start lxc-audio +AUD_STATE="lxc-audio ❎" + +DAEMON_STATE="$NET_STATE, $GPU_STATE, $AUD_STATE" + +sed -i "s|description={}|description=$DAEMON_STATE|g" $MODDIR/module.prop \ No newline at end of file diff --git a/config/init/module/uninstall.sh.in b/config/init/module/uninstall.sh.in new file mode 100644 index 0000000000..4e43f70782 --- /dev/null +++ b/config/init/module/uninstall.sh.in @@ -0,0 +1,10 @@ +#!/bin/sh + +export LD_LIBRARY_PATH=/system/lib64:/system/lib:@LIBDIR@:/data/sysroot/lib64:/data/sysroot/lib + +for container in $(lxc-ls); do + echo "stopping: $container" + lxc-stop -n "$container" 2>/dev/null || lxc-stop -n "$container" -k +done + +rm -rf @PREFIXDIR@ \ No newline at end of file diff --git a/meson.build b/meson.build index 95af7e7519..088eddc8f6 100644 --- a/meson.build +++ b/meson.build @@ -110,6 +110,8 @@ srcconf.set_quoted('RUNTIME_PATH', runtimepath) srcconf.set_quoted('SBINDIR', sbindir) conf = configuration_data() +conf.set('PREFIXDIR', prefixdir) +conf.set('LIBDIR', libdir) conf.set('BINDIR', bindir) conf.set('LIBEXECDIR', libexecdir) conf.set('LOCALSTATEDIR', localstatedir) @@ -270,10 +272,28 @@ endif have = cc.get_id().contains('clang') and cc.has_header('android/log.h') srcconf.set10('IS_BIONIC', have) +## Android Native Build +srcconf.set10('IS_BIONIC_NATIVE', false) +if meson.is_cross_build() == false and srcconf.get('IS_BIONIC') == 1 + sdk_version = run_command('getprop', 'ro.build.version.sdk', check: true).stdout().strip() + cpu_arch = build_machine.cpu_family() + if cpu_arch == 'aarch64' + add_project_arguments('--target=aarch64-linux-android' + sdk_version, language: 'c') + elif cpu_arch == 'arm' + add_project_arguments('--target=armv7a-linux-androideabi' + sdk_version, language: 'c') + elif cpu_arch == 'X86_64' + add_project_arguments('--target=x86_64-linux-android' + sdk_version, language: 'c') + else + error('Unsupported arch:', cpu_arch) + endif + srcconf.set10('IS_BIONIC_NATIVE', true) +endif + # Feature detection ## Android Log System. +srcconf.set10('USE_ANDROID_LOG', false) if want_android_log - if srcconf.get('IS_BIONIC') == true + if srcconf.get('IS_BIONIC') == 1 liblog = cc.find_library('log', required: true) pkgconfig_libs += liblog liblxc_dependencies += liblog @@ -310,6 +330,7 @@ endif sh = find_program('sh') date = find_program('date') git = find_program('git', required: false) +zip = find_program('zip', required: false) time_epoch = run_command(sh, '-c', 'echo "$SOURCE_DATE_EPOCH"', check: true).stdout().strip() if time_epoch == '' and git.found() and run_command('test', '-e', '.git', check: false).returncode() == 0 # If we're in a git repository, use the creation time of the latest git tag. @@ -921,6 +942,7 @@ if want_install_init subdir('config/init/systemd') subdir('config/init/sysvinit') subdir('config/init/monitd') + subdir('config/init/module') subdir('config/sysconfig') endif if want_selinux diff --git a/meson_options.txt b/meson_options.txt index dd12d98f33..d7dc4390b1 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -15,7 +15,7 @@ option('examples', type: 'boolean', value: true, # was --init-script in autotools option('init-script', type: 'array', - choices: ['systemd', 'sysvinit', 'monitd'], value: ['monitd'], + choices: ['systemd', 'sysvinit', 'monitd', 'module'], value: ['module'], description: 'init script') # was --systemd-unidir in autotools diff --git a/templates/lxc-busybox.in b/templates/lxc-busybox.in index 04fa96333b..fdddfe8721 100755 --- a/templates/lxc-busybox.in +++ b/templates/lxc-busybox.in @@ -260,7 +260,12 @@ EOF return 0 } -if ! options=$(getopt -o hp:n: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid:,busybox-path: -- "$@"); then +GETOPT="getopt" +if toybox getopt --help >/dev/null 2>&1; then + GETOPT="toybox getopt" +fi + +if ! options=$($GETOPT -o hp:n: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid:,busybox-path: -- "$@"); then usage exit 1 fi diff --git a/templates/lxc-download.in b/templates/lxc-download.in index e10e821d6e..ea324b3192 100755 --- a/templates/lxc-download.in +++ b/templates/lxc-download.in @@ -55,11 +55,30 @@ wget_wrapper() { return 1 } +curl_wrapper() { + for _ in $(seq 3); do + if curl "$@"; then + return 0 + fi + done + + return 1 +} + download_file() { if [ "${DOWNLOAD_VERBOSE}" = "true" ]; then echo "Download file: https://${DOWNLOAD_SERVER}$1" fi - if ! wget_wrapper --user-agent="lxc/@PACKAGE_VERSION@ compat:${DOWNLOAD_COMPAT_LEVEL}" -T 30 -q "https://${DOWNLOAD_SERVER}/$1" -O "$2" >/dev/null 2>&1; then + + if [ "${IS_TOYBOX_UTILS}" = "true" ]; then + tool="curl_wrapper" + extra_args="-m 30 -f -L" + else + tool="wget_wrapper" + extra_args="-T 30 -q" + fi + + if ! ${tool} --user-agent "lxc/@PACKAGE_VERSION@ compat:${DOWNLOAD_COMPAT_LEVEL}" ${extra_args} "https://${DOWNLOAD_SERVER}/$1" -o "$2" >/dev/null 2>&1; then if [ "$3" = "noexit" ]; then return 1 else @@ -137,7 +156,47 @@ EOF return 0 } -if ! options=$(getopt -o d:r:a:hl -l dist:,release:,arch:,help,list,variant:,\ +# toybox tar required xz but not found in Android, so we use su manager busybox. +# In most cases, if you need to use this project, we default to having Magisk, +# KernelSU, or APatch installed. +IS_ANDROID_TAR="false" +if tar --version | grep -sq "android"; then + IS_ANDROID_TAR="true" +fi + +BUSYBOX_PATHS=( + "/data/adb/magisk/busybox" + "/data/adb/ksu/bin/busybox" + "/data/adb/ap/bin/busybox" +) + +if [ ! -f "@BINDIR/xz" ]; then + for xz in "${BUSYBOX_PATHS[@]}"; do + if [ -f "${xz}" ]; then + ln -s "${xz}" "@BINDIR/xz" + break + fi + done +fi + +GETOPT="getopt" +IS_TOYBOX_UTILS="false" +if toybox | grep -sqE "tar|getopt"; then + IS_TOYBOX_UTILS="true" + GETOPT="toybox getopt" +fi + +# Check for required binaries +for bin in tar xz wget getopt; do + if ! command -V "${bin}" >/dev/null 2>&1; then + if [ "${IS_TOYBOX_UTILS}" = "false" ]; then + echo "ERROR: Missing required tool: ${bin}" 1>&2 + exit 1 + fi + fi +done + +if ! options=$($GETOPT -o d:r:a:hl -l dist:,release:,arch:,help,list,variant:,\ server:,flush-cache,force-cache,verbose,name:,path:,\ rootfs:,mapped-uid:,mapped-gid: -- "$@"); then usage @@ -166,14 +225,6 @@ while :; do esac done -# Check for required binaries -for bin in tar xz wget; do - if ! command -V "${bin}" >/dev/null 2>&1; then - echo "ERROR: Missing required tool: ${bin}" 1>&2 - exit 1 - fi -done - # Check that we have all variables we need if [ -z "${LXC_NAME}" ] || [ -z "${LXC_PATH}" ] || [ -z "${LXC_ROOTFS}" ]; then if [ "${DOWNLOAD_LIST_IMAGES}" != "true" ]; then @@ -206,7 +257,7 @@ fi trap cleanup EXIT HUP INT TERM # /tmp may be mounted in tmpfs or noexec -if mountpoint -q /data/local/tmp; then +if mountpoint -q /data/share/tmp; then DOWNLOAD_TEMP="${LXC_PATH}" fi @@ -396,7 +447,7 @@ fi # is to use a function wrapper, but the latter can't be used here as the args # are dynamic. We thus need to ignore the warning brought by shellcheck. # shellcheck disable=SC2086 -if [ "${IS_BSD_TAR}" = "true" ]; then +if [ "${IS_BSD_TAR}" = "true" ] || [ "${IS_ANDROID_TAR}" = "true" ]; then tar ${EXCLUDES} --numeric-owner -xpJf "${LXC_CACHE_PATH}/rootfs.tar.xz" -C "${LXC_ROOTFS}" else tar --anchored ${EXCLUDES} --numeric-owner --xattrs-include='*' -xpJf "${LXC_CACHE_PATH}/rootfs.tar.xz" -C "${LXC_ROOTFS}"