From 2046d877eba6db1820c753a287d585e557b3e92c Mon Sep 17 00:00:00 2001 From: macmpi <16296055+macmpi@users.noreply.github.com> Date: Fri, 24 Nov 2023 10:43:56 +0100 Subject: [PATCH] version 1.2 New feature: auto-update apkovl file with latest release on master gadget: improved setup if several ports available and/or some unconnected wlan: only install & start wpa_supplicant if needed updated doc --- README.md | 15 +- headless.apkovl.tar.gz | Bin 7547 -> 8574 bytes headless.apkovl.tar.gz.sha512 | 1 + make.sh | 3 +- overlay/usr/local/bin/headless_bootstrap | 281 ++++++++++++++--------- sample_auto-updt | 10 + sample_unattended.sh | 7 +- 7 files changed, 201 insertions(+), 116 deletions(-) create mode 100644 headless.apkovl.tar.gz.sha512 create mode 100644 sample_auto-updt diff --git a/README.md b/README.md index 8427b02..f2c2f74 100644 --- a/README.md +++ b/README.md @@ -25,15 +25,18 @@ Extra files may be added next to `headless.apkovl.tar.gz` to customise boostrapp - `interfaces`[^3] (*optional*): define network interfaces at will, if defaults DCHP-based are not suitable. - `authorized_keys` (*optional*): provide client's public SSH key to secure `root` ssh login. - `ssh_host_*_key*` (*optional*): provide server's custom ssh keys to be injected (may be stored), instead of using bundled ones[^2] (not stored). Providing an empty key file will trigger new keys generation (ssh server may take longer to start). - - -**Goody:** seamless USB-serial & USB-ethernet gadget mode (PiZero for instance):\ -On supporting Pi devices, just add `dtoverlay=dwc2,dr_mode=peripheral` in `usercfg.txt` (or `config.txt`), and plug USB cable into host Computer port.\ -Serial terminal can then be connected-to from host Computer (e.g. `cu -l ttyACM0` on Linux. xon/xoff flow control).\ -Alternatively, with host Computer set-up to share networking with USB interface as 10.42.0.1 gateway, one can log into device from host with: `ssh root@10.42.0.2`. +- `auto-updt` (*optional*): allow apkovl file automatic update with latest from master branch. If it contains *reboot* keyword all in one line, system will reboot after succesful update (unless ssh session is active or `unattended.sh` script is available). Main execution steps are logged: `cat /var/log/messages | grep headless`. +## Goody: +Seamless USB-serial & USB-ethernet gadget mode (*e.g. PiZero*): +- Make sure dwc2/dwc3 driver is loaded accordingly, and device configuration is set to `peripheral` mode: this may be hardware (including cable) and/or software driven.\ +(on supporting Pi devices, just add `dtoverlay=dwc2,dr_mode=peripheral` in `usercfg.txt` (or `config.txt`) to force by software) +- Plug USB cable into host Computer port before boot.\ +Serial terminal can then be connected-to from host Computer (e.g. `cu -l ttyACM0` on Linux. xon/xoff flow control).\ +Alternatively, with host Computer set-up to share networking with USB interface as 10.42.0.1 gateway, one can log into device from host with: `ssh root@10.42.0.2`. + [^1]: Initial boot fully preserves system's original state (config files & installed packages): a fresh system will therefore come-up as unconfigured. [^2]: About bundled ssh keys: this overlay is meant to **quickly bootstrap** system in order to then proceed with proper install; therefore it purposely embeds [some ssh keys](https://github.com/macmpi/alpine-linux-headless-bootstrap/tree/main/overlay/tmp/.trash) so that bootstrapping is as fast as possible. Those temporary keys are moved in RAM /tmp: they will **not be stored/reused** once actual system install is performed (whether or not ssh server is installed in final setup). diff --git a/headless.apkovl.tar.gz b/headless.apkovl.tar.gz index 8a57b83d8c4d9a00aef5eeab6b579d268fb77a0a..efc2e896dcda08dd21454ea221e6525d2d6c4343 100644 GIT binary patch delta 8240 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 delta 7232 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 O^YcHHDR8O)cmMz^tzh;5 diff --git a/headless.apkovl.tar.gz.sha512 b/headless.apkovl.tar.gz.sha512 new file mode 100644 index 0000000..71f95bc --- /dev/null +++ b/headless.apkovl.tar.gz.sha512 @@ -0,0 +1 @@ +36892e9aa76807941602160ff62948c8953eefa36f9b386a0c7302fc46710b473a3e14b7eaf223a1ae6ac6454a839c78049c311fdbd50a204b2a011cb8faa474 headless.apkovl.tar.gz diff --git a/make.sh b/make.sh index ca10909..0297fb7 100755 --- a/make.sh +++ b/make.sh @@ -30,7 +30,8 @@ if [ -n "$build_path" ]; then doas tar cv -C "$build_path"/overlay --no-recursion \ $(doas find "$build_path"/overlay/ | sed "s|$build_path/overlay/||" | sort | xargs ) | \ gzip -c9n > headless.apkovl.tar.gz - TZ=UTC touch -cm -t "$t_stamp" headless.apkovl.tar.gz + sha512sum headless.apkovl.tar.gz > headless.apkovl.tar.gz.sha512 + TZ=UTC touch -cm -t "$t_stamp" headless.apkovl.tar.gz* doas rm -rf "$build_path" fi diff --git a/overlay/usr/local/bin/headless_bootstrap b/overlay/usr/local/bin/headless_bootstrap index 6ba5a96..2b6ac2a 100755 --- a/overlay/usr/local/bin/headless_bootstrap +++ b/overlay/usr/local/bin/headless_bootstrap @@ -3,7 +3,7 @@ # SPDX-FileCopyrightText: Copyright 2022-2023, macmpi # SPDX-License-Identifier: MIT -HDLSBSTRP_VERSION="1.1" +HDLSBSTRP_VERSION="1.2" _apk() { local cmd="$1" @@ -12,11 +12,11 @@ _apk() { case $cmd in add) # install only if not already present if ! apk info | grep -wq "${pkg}"; then - apk add "$pkg" && printf '%s ' "${pkg}" >> /tmp/.trash/installed + apk add "$pkg" && printf '%s ' "${pkg}" >>/tmp/.trash/installed fi ;; del) # delete only if previously installed - if grep -wq "$pkg" /tmp/.trash/installed > /dev/null 2>&1; then + if grep -wq "$pkg" /tmp/.trash/installed >/dev/null 2>&1; then apk del "$pkg" && sed -i 's/\b'"${pkg}"'\b//' /tmp/.trash/installed fi ;; @@ -27,14 +27,14 @@ _apk() { } _preserve() { -# create a back-up of element (file, folder, symlink) +# Create a back-up of element (file, folder, symlink). [ -z "${1}" ] && return 1 [ -e "${1}" ] && cp -a "${1}" "${1}".orig } _restore() { -# remove element (file, folder, symlink) and replace by -# previous back-up if available +# Remove element (file, folder, symlink) and replace by +# previous back-up if available. [ -z "${1}" ] && return 1 rm -rf "${1}" [ -e "${1}".orig ] && mv -f "${1}".orig "${1}" @@ -46,11 +46,11 @@ alias _logger='logger -st "${0##*/}"' ##### End of part to be duplicated into headless_cleanup (do not alter!) _prep_cleanup() { -## Prep for final headless_cleanup -# clears any installed packages and settings -# copy begininng of this file to keep functions -sed -n '/^#* End .*alter!)$/q;p' /usr/local/bin/headless_bootstrap > /tmp/.trash/headless_cleanup -cat <<-EOF >> /tmp/.trash/headless_cleanup +## Prep for final headless_cleanup: +# clears any installed packages and settings. +# Copy begininng of this file to keep functions. +sed -n '/^#* End .*alter!)$/q;p' /usr/local/bin/headless_bootstrap >/tmp/.trash/headless_cleanup +cat <<-EOF >>/tmp/.trash/headless_cleanup # Redirect stdout and errors to console as service won't show messages exec 1>/dev/console 2>&1 @@ -64,44 +64,45 @@ cat <<-EOF >> /tmp/.trash/headless_cleanup _restore "/etc/hostname" rm -f /etc/modprobe.d/headless_gadget.conf - # remove from boot service to avoid spurious openrc recalls from unattended script + # Remove from boot service to avoid spurious openrc recalls from unattended script. rm -f /etc/runlevels/default/headless_bootstrap rm -f /usr/local/bin/headless_bootstrap - # Run unattended script if available - install -m755 "${ovlpath}"/unattended.sh /tmp/headless_unattended > /dev/null 2>&1 && \ + # Run unattended script if available. + install -m755 ${ovlpath}/unattended.sh /tmp/headless_unattended >/dev/null 2>&1 && \ _logger "Starting headless_unattended service" && \ rc-service headless_unattended start rm -f /etc/init.d/headless_* _logger "Clean-up done, enjoy !" - cat /tmp/.trash/banner > /dev/console - if [ -c /dev/ttyGS0 ]; then - # Enabling terminal login into ttyGS0 serial for 60 sec - # no choice than making permanent change to pre 3.19 versions of /etc/securetty - grep -q "ttyGS0" /etc/securetty || echo "ttyGS0" >> /etc/securetty - /sbin/getty -L 115200 ttyGS0 vt100 & + cat /tmp/.trash/banner >/dev/console + if [ -c /dev/ttyGS${gdgt_id} ]; then + # Enabling terminal login into valid serial port: + # no choice than making permanent change to /etc/securetty (Alpine 3.19 already has ttyGS0). + grep -q "ttyGS${gdgt_id}" /etc/securetty || echo "ttyGS${gdgt_id}" >>/etc/securetty + /sbin/getty -L 115200 /dev/ttyGS${gdgt_id} vt100 & fi + exit 0 EOF chmod +x /tmp/.trash/headless_cleanup } _setup_sshd() { -## Setup temporary SSH server (root login, no password) -# We use some bundled (or optionaly provided) keys to avoid generation at startup and save time +## Setup temporary SSH server (root login, no password): +# we use some bundled (or optionaly provided) keys to avoid generation at startup and save time. _apk add openssh-server _preserve "/etc/ssh/sshd_config" _preserve "/etc/conf.d/sshd" -cat <<-EOF > /etc/ssh/sshd_config +cat <<-EOF >/etc/ssh/sshd_config PermitRootLogin yes Banner /tmp/.trash/banner EOF -# Client authorized_keys or no authentication -if install -m600 "${ovlpath}"/authorized_keys /tmp/.trash/authorized_keys > /dev/null 2>&1; then +# Client authorized_keys or no authentication. +if install -m600 "${ovlpath}"/authorized_keys /tmp/.trash/authorized_keys >/dev/null 2>&1; then _logger "Enabling public key SSH authentication..." - cat <<-EOF >> /etc/ssh/sshd_config + cat <<-EOF >>/etc/ssh/sshd_config AuthenticationMethods publickey AuthorizedKeysFile /tmp/.trash/authorized_keys # relax strict mode as authorized_keys are inside /tmp @@ -109,7 +110,7 @@ if install -m600 "${ovlpath}"/authorized_keys /tmp/.trash/authorized_keys > /dev EOF else _logger "No SSH authentication." - cat <<-EOF >> /etc/ssh/sshd_config + cat <<-EOF >>/etc/ssh/sshd_config AuthenticationMethods none PermitEmptyPasswords yes EOF @@ -118,8 +119,8 @@ fi # Server keys: inject optional custom keys, or generate new (might be stored), # or use bundeled ones (not stored) local keygen_stance="sshd_disable_keygen=yes" -if install -m600 "${ovlpath}"/ssh_host_*_key* /etc/ssh/ > /dev/null 2>&1; then - # check for empty key within injected ones: if found, generate new keys +if install -m600 "${ovlpath}"/ssh_host_*_key* /etc/ssh/ >/dev/null 2>&1; then + # Check for empty key within injected ones: if found, generate new keys. if find /etc/ssh/ -maxdepth 1 -type f -name 'ssh_host_*_key*' -empty | grep -q .; then rm /etc/ssh/ssh_host_*_key* keygen_stance="" @@ -130,63 +131,103 @@ if install -m600 "${ovlpath}"/ssh_host_*_key* /etc/ssh/ > /dev/null 2>&1; then fi else _logger "Using bundled ssh keys from RAM..." - cat <<-EOF >> /etc/ssh/sshd_config + cat <<-EOF >>/etc/ssh/sshd_config HostKey /tmp/.trash/ssh_host_ed25519_key HostKey /tmp/.trash/ssh_host_rsa_key EOF fi -echo "$keygen_stance" >> /etc/conf.d/sshd +echo "$keygen_stance" >>/etc/conf.d/sshd rc-service sshd restart } +_updt_apkovl() { +## Update apkovl overlay file & eventually reboot +# URL redirects to apkovl file on github master: is.gd shortener provides basic analytics. +# Analytics are public and can be checked at https://is.gd/stats.php?url=apkovl_master +# Privacy policy: https://is.gd/privacy.php +local file_url="https://is.gd/apkovl_master" +local sha_url="https://github.com/macmpi/alpine-linux-headless-bootstrap/raw/main/headless.apkovl.tar.gz.sha512" +local updt_status="failed, keeping original version" + +if wget -q -O /tmp/apkovl -T 10 "$file_url" >/dev/null 2>&1 && \ + wget -q -O /tmp/sha -T 10 "$sha_url" >/dev/null 2>&1 && \ + [ "$( sha512sum /tmp/apkovl | awk '{print $1}' )" = "$( awk '{print $1}' /tmp/sha )" ]; then + _is_ro && mount -o remount,rw "${ovlpath}" + cp /tmp/apkovl "${ovl}" + _is_ro && mount -o remount,ro "${ovlpath}" + ! [ "$( sha512sum "${ovl}" | awk '{print $1}' )" = "$( awk '{print $1}' /tmp/sha )" ] && \ + _logger "Bad update: original apkovl file may be altered, please check!..." && return 1 + updt_status="successful" +fi +rm -f /tmp/apkovl /tmp/sha +_logger "Update $updt_status" +if [ "$updt_status" = "successful" ]; then + printf '%s\n\n' "Updated" >>/tmp/.trash/banner +else + printf '\n' >>/tmp/.trash/banner + return 1 +fi +# Reboot if specified in auto-updt file (and no ssh session ongoing nor unattended.sh script available). +! pgrep -a -P "$( cat /run/sshd.pid 2>/dev/null )" 2>/dev/null | grep -q "sshd: root@pts" && \ + ! [ -f "${ovlpath}"/unattended.sh ] && \ + grep -q "^reboot$" "${ovlpath}"/auto-updt && \ + _logger "Will reboot in 3sec..." && sleep 3 && reboot +exit 0 +} + _tst_version() { -# Tested URL redirects to github project page: is.gd shortener provides basic analytics. +## Compare current version with latest online, notify & eventally calls for update +# URL redirects to github project page: is.gd shortener provides basic analytics. # Analytics are public and can be checked at https://is.gd/stats.php?url=apkovl_run # Privacy policy: https://is.gd/privacy.php -local new_vers="" -local status="failed" +local vers="" local ref="/macmpi/alpine-linux-headless-bootstrap/releases/tag/v" -if wget -q -O /tmp/homepg -T 10 https://is.gd/apkovl_run > /dev/null 2>&1; then - status="success" - ver="$( grep -o "$ref.*\"" /tmp/homepg | grep -Eo '[0-9]+[\.[0-9]+]*' )" +local url="https://is.gd/apkovl_run" + +if wget -q -O /tmp/homepg -T 10 "$url" >/dev/null 2>&1; then + _logger "Internet access: success" + vers="$( grep -o "$ref.*\"" /tmp/homepg | grep -Eo '[0-9]+[\.[0-9]+]*' )" rm -f /tmp/homepg - [ -n "$ver" ] && ! [ "$ver" = "$HDLSBSTRP_VERSION" ] && \ - new_vers="!! Version $ver is available on Github project page !!" && \ - _logger "$new_vers" && \ - printf '%s\n\n' "$new_vers" >> /tmp/.trash/banner + if [ -n "$vers" ] && ! [ "$vers" = "$HDLSBSTRP_VERSION" ]; then + vers="!! Version $vers is available on Github project page !!" + _logger "$vers" + printf '%s\n' "$vers" >>/tmp/.trash/banner + # Optionally update apkovl if key-file allows it. + if [ -f "${ovlpath}"/auto-updt ]; then + _logger "Updating overlay file..." + _updt_apkovl & + else + _logger "(check doc to enable auto-update)" + printf '%s\n\n' "(check doc to enable auto-update)" >>/tmp/.trash/banner + fi + fi +else + _logger "Internet access: failed" fi -_logger "Internet access: $status" } _setup_networking() { -## Setup Network interfaces -local has_wifi +## Setup network interfaces. +local has_wifi wlan_lst _has_wifi() { return "$has_wifi"; } -find /sys/class/ieee80211/*/device/net/* -maxdepth 0 -type d -exec basename {} \; > /tmp/.wlan_list 2>/dev/null -[ -s /tmp/.wlan_list ] && [ -f "${ovlpath}"/wpa_supplicant.conf ] +wlan_lst="$( find /sys/class/net/*/phy80211 -exec \ + sh -c 'printf %s\| "$( basename "$( dirname "$0" )" )"' {} \; 2>/dev/null )" +wlan_lst="${wlan_lst%\|}" +[ -n "$wlan_lst" ] && [ -f "${ovlpath}"/wpa_supplicant.conf ] has_wifi=$? -if _has_wifi; then - _logger "Configuring wifi..." - _apk add wpa_supplicant - _preserve "/etc/wpa_supplicant/wpa_supplicant.conf" - install -m600 "${ovlpath}"/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf - rc-service wpa_supplicant restart -else - _logger "No wifi interface or SSID/pass file supplied" -fi _preserve "/etc/network/interfaces" -if ! install -m644 "${ovlpath}"/interfaces /etc/network/interfaces > /dev/null 2>&1; then +if ! install -m644 "${ovlpath}"/interfaces /etc/network/interfaces >/dev/null 2>&1; then _logger "No interfaces file supplied, building defaults..." - cat <<-EOF > /etc/network/interfaces + cat <<-EOF >/etc/network/interfaces auto lo iface lo inet loopback EOF for dev in /sys/class/net/*; do - # shellcheck disable=SC2034 # Unused IFINDEX while still sourced from uevent + # shellcheck disable=SC2034 # Unused IFINDEX while still sourced from uevent. local DEVTYPE INTERFACE IFINDEX DEVTYPE="" # shellcheck source=/dev/null @@ -195,31 +236,37 @@ if ! install -m644 "${ovlpath}"/interfaces /etc/network/interfaces > /dev/null 2 lo) ;; eth) - cat <<-EOF >> /etc/network/interfaces + cat <<-EOF >>/etc/network/interfaces auto $INTERFACE iface $INTERFACE inet dhcp EOF ;; *) - _has_wifi && grep -q "$INTERFACE" /tmp/.wlan_list && \ - cat <<-EOF >> /etc/network/interfaces + # According to below we could rely on DEVTYPE for wlan devices + # https://lists.freedesktop.org/archives/systemd-devel/2014-January/015999.html + # but...some wlan might still be ill-behaved: use wlan_lst + # shellcheck disable=SC2169 # ash does support string replacement. + _has_wifi && ! [ "${wlan_lst/$INTERFACE/}" = "$wlan_lst" ] && \ + cat <<-EOF >>/etc/network/interfaces auto $INTERFACE iface $INTERFACE inet dhcp EOF + # Ensure considered gadget interface is actually the connected one (may have several). [ "$DEVTYPE" = "gadget" ] && \ - cat <<-EOF >> /etc/network/interfaces && cat <<-EOF > /etc/resolv.conf - auto $INTERFACE - iface $INTERFACE inet static - address 10.42.0.2/24 - gateway 10.42.0.1 + find /sys/class/udc/*/device/gadget."${gdgt_id}"/net/"$INTERFACE" -maxdepth 0 >/dev/null 2>&1 && \ + cat <<-EOF >>/etc/network/interfaces && cat <<-EOF >/etc/resolv.conf + auto $INTERFACE + iface $INTERFACE inet static + address 10.42.0.2/24 + gateway 10.42.0.1 - EOF - nameserver 208.67.222.222 - nameserver 208.67.220.220 + EOF + nameserver 208.67.222.222 + nameserver 208.67.220.220 - EOF + EOF ;; esac done @@ -230,35 +277,53 @@ echo "Using following network interfaces:" cat /etc/network/interfaces echo "###################################" +if _has_wifi && grep -qE "$wlan_lst" /etc/network/interfaces; then + _logger "Configuring wifi..." + _apk add wpa_supplicant + _preserve "/etc/wpa_supplicant/wpa_supplicant.conf" + install -m600 "${ovlpath}"/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf + rc-service wpa_supplicant restart +else + _logger "No wifi interface or SSID/pass file supplied" +fi + _preserve "/etc/hostname" -echo "alpine-headless" > /etc/hostname +echo "alpine-headless" >/etc/hostname hostname -F /etc/hostname rc-service networking restart -rm -f /tmp/.wlan_list } _setup_gadget() { -## load composite USB Serial/USB Ethernel driver & setup terminal +## Load composite USB Serial/USB Ethernel driver & setup terminal. _logger "Enabling USB-gadget Serial and Ethernet ports" -lsmod | grep -q "dwc2" || modprobe -qs dwc2 -# remove conflicting modules in case they were initially loaded (cmdline.txt) -modprobe -rq g_serial g_ether g_cdc -modprobe -q g_cdc && sleep 1 -# once driver has settled check if cable is connected: unload if not -[ "$( cat "$udc_gadget"/current_speed )" = "UNKNOWN" ] && \ - _logger "USB cable not connected !!" && modprobe -rq g_cdc && return 1 +# Remove conflicting modules in case they were initially loaded (cmdline.txt). +modprobe -r g_serial g_ether g_cdc +modprobe g_cdc +# Wait for g_cdc to settle and serial ports become available +timeout 1 sh <<-EOF + while ! grep -q "ttyGS" /proc/devices; do sleep 0.2; done + EOF -# default serial config: xon/xoff flow control -stty -g -F /dev/ttyGS0 >/dev/null 2>&1 -# notes to users willing to connect from Linux Ubuntu-based host terminal: -# - user on host needs to be part of dialout group (reboot required), and -# - disable spurious AT commands from ModemManager on host-side Gadget serial port -# you may create a /etc/udev/rules.d/99-ttyacms-gadget.rules as per: -# https://linux-tips.com/t/prevent-modem-manager-to-capture-usb-serial-devices/284/2 -# ATTRS{idVendor}=="0525" ATTRS{idProduct}=="a4aa", ENV{ID_MM_DEVICE_IGNORE}="1" - -setconsole /dev/ttyGS0 +# Determine which gadget ID is connected with USB cable (assume just one max). +# (setting console to unconnected serial port would block boot) +gdgt_id="$( find /sys/class/udc/*/current_speed -exec \ + sh -c 'grep -vq "UNKNOWN" "$0" && find ${0/current_speed/}device/gadget.* -maxdepth 0' {} \; \ + | sed 's/\/.*gadget\.//' )" +if [ -c /dev/ttyGS"${gdgt_id}" ]; then + # Default serial config: xon/xoff flow control. + stty -F /dev/ttyGS"${gdgt_id}" + setconsole /dev/ttyGS"${gdgt_id}" + # Notes to users willing to connect from Linux Ubuntu-based host terminal: + # - user on host needs to be part of dialout group (reboot required), and + # - disable spurious AT commands from ModemManager on host-side Gadget serial port + # one may create a /etc/udev/rules.d/99-ttyacms-gadget.rules as per: + # https://linux-tips.com/t/prevent-modem-manager-to-capture-usb-serial-devices/284/2 + # ATTRS{idVendor}=="0525" ATTRS{idProduct}=="a4aa", ENV{ID_MM_DEVICE_IGNORE}="1" +else + _logger "USB-gadget port not connected !" + modprobe -r g_cdc +fi } @@ -269,31 +334,35 @@ setconsole /dev/ttyGS0 exec 1>/dev/console 2>&1 _logger "Alpine Linux headless bootstrap v$HDLSBSTRP_VERSION by macmpi" -# help randomness for wpa_supplicant and sshd (urandom until 3.16) +# Help randomness for wpa_supplicant and sshd (urandom until 3.16). rc-service seedrng restart || rc-service urandom restart -# setup USB gadget mode if such device mode is enabled -udc_gadget="$( dirname "$( find -L /sys/class/udc/* -maxdepth 2 -type f -name "is_a_peripheral" 2>/dev/null)" )" -[ "$( cat "$udc_gadget"/is_a_peripheral 2>/dev/null )" = "0" ] && \ - _setup_gadget +# Setup USB gadget ports if some ports are enabled in peripheral mode. +# Note: we assume dwc2/dwc3 is pre-loaded, we just check mode. +gdgt_id="" +find /sys/class/udc/*/is_a_peripheral -print0 2>/dev/null | \ + xargs -0 cat 2>/dev/null | grep -q "0" && \ + _setup_gadget -# Determine ovl file location -# grab used ovl filename from dmesg +# Determine ovl file location. +# Grab used ovl filename from dmesg. ovl="$( dmesg | grep -o 'Loading user settings from .*:' | awk '{print $5}' | sed 's/:.*$//' )" if [ -f "${ovl}" ]; then ovlpath="$( dirname "$ovl" )" else - # search path again as mountpoint have been changed later in the boot process... + # Search path again as mountpoint have been changed later in the boot process... ovl="$( basename "${ovl}" )" ovlpath=$( find /media -maxdepth 2 -type d -path '*/.*' -prune -o -type f -name "${ovl}" -exec dirname {} \; | head -1 ) ovl="${ovlpath}/${ovl}" fi -# Create banner file +# Create banner file. warn="" -grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; RO=$? -[ "$RO" -eq "0" ] && warn="(remount partition rw!)" -cat <<-EOF > /tmp/.trash/banner +grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; is_ro=$? +_is_ro() { return "$is_ro"; } + +_is_ro && warn="(remount partition rw!)" +cat <<-EOF >/tmp/.trash/banner Alpine Linux headless bootstrap v$HDLSBSTRP_VERSION by macmpi @@ -306,12 +375,12 @@ cat <<-EOF > /tmp/.trash/banner _setup_networking -# Test latest available version online -# Can be skipped by creating a 'opt-out'-named dummy file aside apkovl file +# Test latest available version online. +# Can be skipped by creating a 'opt-out'-named dummy file aside apkovl file. [ -f "${ovlpath}"/opt-out ] || _tst_version & -# setup sshd unless unattended.sh script prevents it -grep -q "^#NO_SSH$" "${ovlpath}"/unattended.sh > /dev/null 2>&1 \ +# Setup sshd unless unattended.sh script prevents it. +grep -q "^#NO_SSH$" "${ovlpath}"/unattended.sh >/dev/null 2>&1 \ || _setup_sshd _prep_cleanup diff --git a/sample_auto-updt b/sample_auto-updt new file mode 100644 index 0000000..5fbe9ba --- /dev/null +++ b/sample_auto-updt @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: Copyright 2022-2023, macmpi +# SPDX-License-Identifier: MIT + +# Automated reboot after update is cancelled if: +# - there is an opened ssh session +# - unattended.sh script is provided + +# Uncomment line below to enable reboot after update +#reboot + diff --git a/sample_unattended.sh b/sample_unattended.sh index 110e74f..c03ba8f 100644 --- a/sample_unattended.sh +++ b/sample_unattended.sh @@ -37,10 +37,11 @@ else fi # also works in case volume is mounted read-only -grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; RO=$? -[ "$RO" -eq "0" ] && mount -o remount,rw "${ovlpath}" +grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; is_ro=$? +_is_ro() { return "$is_ro"; } +_is_ro && mount -o remount,rw "${ovlpath}" rm -f "${ovl}" -[ "$RO" -eq "0" ] && mount -o remount,ro "${ovlpath}" +_is_ro && mount -o remount,ro "${ovlpath}" ########################################################