From 24bac68795c63066b594395ca725b089edc7170e Mon Sep 17 00:00:00 2001 From: macmpi <16296055+macmpi@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:18:57 +0100 Subject: [PATCH] version 1.2 New feature: auto-update apkovl file with latest 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 -> 8778 bytes headless.apkovl.tar.gz.sha512 | 1 + make.sh | 3 +- overlay/usr/local/bin/headless_bootstrap | 280 ++++++++++++++--------- sample_auto-updt | 10 + sample_unattended.sh | 7 +- 7 files changed, 203 insertions(+), 113 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..3a5dfb6 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 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 acordingly, 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..95fc7fa670baad2ad6a00f8705778b20da74bd30 100644 GIT binary patch literal 8778 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 pg6s&+QJ&I;_ew)dH3l~Yhsj5 literal 7547 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 RchCRx^FNd+aH;@!004hQ8tMQ5 diff --git a/headless.apkovl.tar.gz.sha512 b/headless.apkovl.tar.gz.sha512 new file mode 100644 index 0000000..82783e9 --- /dev/null +++ b/headless.apkovl.tar.gz.sha512 @@ -0,0 +1 @@ +9cb4f27ccd957dbb44afda322c71e5142c9cef95da401516f1dba154495e8b8151c0ccb9b4e95c7188b538dc7daac493b881b0a5fa5475d2e0a449f95740f739 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..dc1ae6c 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 [ -n "${gdgt_term}" ]; 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 "${gdgt_term}" /etc/securetty || echo "${gdgt_term}" >>/etc/securetty + /sbin/getty -L 115200 "${gdgt_term}" 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,95 @@ 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" +_logger "Updating overlay file..." + +# wget -q -O /tmp/sha -T 10 "$sha_url" >/dev/null 2>&1 && \ +if wget -q -O /tmp/apkovl -T 10 "$file_url" >/dev/null 2>&1 && \ +echo "36243ca58766232a1ff996de611724cee295109f981f3eb5d4b6df916c856036c002abd0cd58266602f3fcbf22777c1bfd1fb891888e864294b86dba366f6f2e toto" >/tmp/sha && \ + [ "$( 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" +[ "$updt_status" = "successful" ] || return 1 +# 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 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" +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" ver="$( 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 "$ver" ] && ! [ "$ver" = "$HDLSBSTRP_VERSION" ]; then + new_vers="!! Version $ver is available on Github project page !!" + _logger "$new_vers" + printf '%s\n\n' "$new_vers" >>/tmp/.trash/banner + # Optionally update apkovl if key-file allows it. + [ -f "${ovlpath}"/auto-updt ] && _updt_apkovl & + 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 +228,38 @@ 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 connected over USB (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 -L /sys/class/udc/*/device/gadget*/net/"$INTERFACE" -maxdepth 0 -exec \ + sh -c 'cat "${0%/device*}"/current_speed' {} \; )" = "UNKNOWN" ] && \ + 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 +270,64 @@ 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 +} + +_test_serial_gadget() { +## Check if serial port is actually connected: +# circumvoluted solution as busybox ash built-in timeout +# only supports integer seconds (1 sec minimum, overly long here). +printf '%s\0' "" >"$1" & +local pid="$!" +# Short timeout to let backgrounded write complete if port OK. +# shellcheck disable=SC2169 # busybox ash read supports -t with fractional seconds. +read -r -t 0.05 <"$1" 2>&1 +kill -9 "$pid" >/dev/null 2>&1 || echo "${1##*/}" } _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) +# 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 +modprobe -q g_cdc -# 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" +# Look for valid serial port actually connected with USB cable (just take last one). +# (setting console to unconnect serial port would block boot) +gdgt_term="" +while read -r ttyGSx; do + gdgt_term="$( _test_serial_gadget "$ttyGSx" )" +done <<-EOF + $( find /dev/ttyGS* 2>/dev/null ) + EOF +if [ -n "$gdgt_term" ]; then + # Default serial config: xon/xoff flow control. + stty -g -F /dev/"$gdgt_term" >/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 + # 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" -setconsole /dev/ttyGS0 + setconsole /dev/"$gdgt_term" +else + _logger "USB-gadget port not connected !" +fi } @@ -269,31 +338,36 @@ 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_term="" +[ "$( find -L /sys/class/udc/*/is_a_peripheral -print0 2>/dev/null | \ + xargs -0 cat 2>/dev/null | \ + grep -c "0" )" -ge "1" ] && \ + _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 +380,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}" ########################################################