diff --git a/README.md b/README.md index 7dab7a4..98e1e90 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,25 @@ # x730-script -This is the safe shutdown script for x730; -NOTE: +This is the safe shutdown script for x730 -We test this shell script base official Raspbian '2018-11-13-raspbian-stretch.img' version; -We test this shell script base official Raspbian '2019-09-26 Raspbian Buster' version also; +> [!NOTE] +> +> This version targets newer Raspberry Pi OS versions supporting `pinctrl` instead of `/sys/class/gpio` or `raspi-gpio` -How to use? -* step 1: -> wget https://raw.githubusercontent.com/geekworm-com/x730-script/master/x730.sh +## Prequisites -> sudo chmod +x x730.sh +1. OS supporting systemd +2. `bash` shell (v5+ or GNU coreutils) +3. Either sleep supporting fractions of seconds or perl or python +4. `pinctrl` binary on path -> sudo bash x730.sh -* step 2: +## Installation -> printf "%s\\n" "alias x730off='sudo x730shutdown.sh'" >> ~/.bashrc +Run `sudo install.sh` -* step 3: -> sudo reboot -* how to safe shut down, run the following comamdn: -> x730off +## Usage + +Use default GUI or terminal possibilities for poweroff/reboot or press the button of you PI's case (short reboot, long shutdown). diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..6f6ccee --- /dev/null +++ b/install.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +SCRIPT_PATH="${BASH_SOURCE}" +while [ -L "${SCRIPT_PATH}" ]; do + SCRIPT_DIR="$(cd -P "$(dirname "${SCRIPT_PATH}")" >/dev/null 2>&1 && pwd)" + SCRIPT_PATH="$(readlink "${SCRIPT_PATH}")" + [[ ${SCRIPT_PATH} != /* ]] && SCRIPT_PATH="${SCRIPT_DIR}/${SCRIPT_PATH}" +done +SCRIPT_PATH="$(readlink -f "${SCRIPT_PATH}")" +SCRIPT_DIR="$(cd -P "$(dirname -- "${SCRIPT_PATH}")" >/dev/null 2>&1 && pwd)" + +# Test is root +if [ "$EUID" -ne 0 ] +then + echo "Please run as root" + exit +fi + + +# Check prequisites +echo "Checking prequisites ..." + +echo "Test required binaries" +for bin in "systemctl" "pinctrl" "bash" +do + if ! command -v "$bin" &> /dev/null + then + echo "$bin ... missing" + exit 1 + else + echo "$bin ... passed" + fi +done + +echo "Test " + + +# Install shell scripts +chmod 755 "$SCRIPT_DIR"/src/*.sh +cp "$SCRIPT_DIR"/src/*.sh /usr/local/bin/ + + +# Install systemd units +chmod 644 "$SCRIPT_DIR"/src/*.service +cp "$SCRIPT_DIR"/src/*.service /etc/systemd/system/ + +systemctl enable x730poweroff +systemctl enable x730reboot +systemctl enable --now x730button diff --git a/src/x730button.service b/src/x730button.service new file mode 100644 index 0000000..2bf0fa8 --- /dev/null +++ b/src/x730button.service @@ -0,0 +1,11 @@ +[Unit] +Description=X730 Case Button +After=local-fs.target + +[Service] +Type=exec +RemainAfterExit=no +ExecStart=/usr/local/bin/x730button.sh + +[Install] +WantedBy=multi-user.target diff --git a/src/x730button.sh b/src/x730button.sh new file mode 100644 index 0000000..d2a9631 --- /dev/null +++ b/src/x730button.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +SHUTDOWN=4 +REBOOT_PULSE_MINIMUM_MILLISECONDS=200 +REBOOT_PULSE_MAXIMUM_MILLISECONDS=600 +SLEEP_PULSE_SECONDS="0.200" +pinctrl set "$SHUTDOWN" ip pd + +BOOT=17 +pinctrl set "$BOOT" op pn dl +pinctrl set "$BOOT" dh + +python() { + "$(command -v python3 || command -v python || command -v python2)" "$@" +} + +exSleep() { + sleep "$1" \ + || perl -e "select(undef, undef, undef, $1)" \ + || python -c "import time; time.sleep($1)" +} + +getShutdownSignal() { + pinctrl lev "$SHUTDOWN" +} + +getPulseTimestamp() { + if [ -n "$EPOCHREALTIME" ] + then + local us="${EPOCHREALTIME/[^0-9]/}" + echo "${us:0:-3}" + else + python -c "import time; print(int(time.time() * 1000))" \ + || date +%s%3N + fi +} + +sleepPulse() { + exSleep "$SLEEP_PULSE_SECONDS" +} + +echo "X730 Shutting down..." + +while : +do + shutdownSignal=$(getShutdownSignal) + if [ "$shutdownSignal" = 0 ] + then + sleepPulse + else + pulseStart=$(getPulseTimestamp) + while [ "$shutdownSignal" = 1 ] + do + sleepPulse + if [ $(( $(getPulseTimestamp) - $pulseStart )) -gt $REBOOT_PULSE_MAXIMUM_MILLISECONDS ] + then + echo "X730 Shutting down, halting Rpi ..." + sudo poweroff + exit + fi + shutdownSignal=$(getShutdownSignal) + done + if [ $(( $(getPulseTimestamp) - $pulseStart )) -gt $REBOOT_PULSE_MINIMUM_MILLISECONDS ] + then + echo "X730 Rebooting, recycling Rpi ..." + sudo reboot + exit + fi + fi +done diff --git a/src/x730poweroff.service b/src/x730poweroff.service new file mode 100644 index 0000000..49aced8 --- /dev/null +++ b/src/x730poweroff.service @@ -0,0 +1,13 @@ +[Unit] +Description=X730 Case Poweroff +DefaultDependencies=no +Conflicts=reboot.target +Before=poweroff.target halt.target shutdown.target + +[Service] +Type=oneshot +RemainAfterExit=no +ExecStart=/usr/local/bin/x730shutdown.sh "4" + +[Install] +WantedBy=poweroff.target halt.target shutdown.target diff --git a/src/x730reboot.service b/src/x730reboot.service new file mode 100644 index 0000000..cb6696f --- /dev/null +++ b/src/x730reboot.service @@ -0,0 +1,13 @@ +[Unit] +Description=X730 Case Reboot +DefaultDependencies=no +Conflicts=poweroff.target halt.target +Before=reboot.target + +[Service] +Type=oneshot +RemainAfterExit=no +ExecStart=/usr/local/bin/x730shutdown.sh "1" + +[Install] +WantedBy=reboot.target diff --git a/src/x730shutdown.sh b/src/x730shutdown.sh new file mode 100644 index 0000000..b255520 --- /dev/null +++ b/src/x730shutdown.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +BUTTON=18 + +pinctrl set "$BUTTON" op pn dl +pinctrl set "$BUTTON" dh + +exSleep() { + sleep "$1" \ + || perl -e "select(undef, undef, undef, $1)" \ + || python3 -c "import time; time.sleep($1)" +} + +# 1-2 sec for reboot, 3-7 for poweroff (default) 8+ crash (pull the plug) +SLEEP=${1:-4} + +if ! [[ "$SLEEP" =~ ^[0-9\.]+$ ]] ; then + echo "error: sleep time not a number" >&2; exit 1 +fi + +echo "X730 Shutting down..." +exSleep "$SLEEP" + +#restore GPIO 18 +pinctrl set "$BUTTON" dl diff --git a/uninstall.sh b/uninstall.sh new file mode 100644 index 0000000..8eb50c2 --- /dev/null +++ b/uninstall.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +SCRIPT_PATH="${BASH_SOURCE}" +while [ -L "${SCRIPT_PATH}" ]; do + SCRIPT_DIR="$(cd -P "$(dirname "${SCRIPT_PATH}")" >/dev/null 2>&1 && pwd)" + SCRIPT_PATH="$(readlink "${SCRIPT_PATH}")" + [[ ${SCRIPT_PATH} != /* ]] && SCRIPT_PATH="${SCRIPT_DIR}/${SCRIPT_PATH}" +done +SCRIPT_PATH="$(readlink -f "${SCRIPT_PATH}")" +SCRIPT_DIR="$(cd -P "$(dirname -- "${SCRIPT_PATH}")" >/dev/null 2>&1 && pwd)" + +# Test is root +if [ "$EUID" -ne 0 ] +then + echo "Please run as root" + exit +fi + + +# Uninstall systemd units +systemctl disable x730poweroff +systemctl disable x730reboot +systemctl disable --now x730button +rm /etc/systemd/system/x730poweroff.service +rm /etc/systemd/system/x730reboot.service +rm /etc/systemd/system/x730button.service + +# Unnstall shell scripts +rm /usr/local/bin/x730shutdown.sh +rm /usr/local/bin/x730button.sh diff --git a/x730.sh b/x730.sh deleted file mode 100644 index 1fbe25a..0000000 --- a/x730.sh +++ /dev/null @@ -1,72 +0,0 @@ -#x730 Powering on /reboot /shutdown from hardware -#!/bin/bash - - sudo sed -e '/shutdown/ s/^#*/#/' -i /etc/rc.local - - echo '#!/bin/bash - -SHUTDOWN=4 -REBOOTPULSEMINIMUM=200 -REBOOTPULSEMAXIMUM=600 -echo "$SHUTDOWN" > /sys/class/gpio/export -echo "in" > /sys/class/gpio/gpio$SHUTDOWN/direction -BOOT=17 -echo "$BOOT" > /sys/class/gpio/export -echo "out" > /sys/class/gpio/gpio$BOOT/direction -echo "1" > /sys/class/gpio/gpio$BOOT/value - -echo "X730 Shutting down..." - -while [ 1 ]; do - shutdownSignal=$(cat /sys/class/gpio/gpio$SHUTDOWN/value) - if [ $shutdownSignal = 0 ]; then - /bin/sleep 0.2 - else - pulseStart=$(date +%s%N | cut -b1-13) - while [ $shutdownSignal = 1 ]; do - /bin/sleep 0.02 - if [ $(($(date +%s%N | cut -b1-13)-$pulseStart)) -gt $REBOOTPULSEMAXIMUM ]; then - echo "X730 Shutting down", SHUTDOWN, ", halting Rpi ..." - sudo poweroff - exit - fi - shutdownSignal=$(cat /sys/class/gpio/gpio$SHUTDOWN/value) - done - if [ $(($(date +%s%N | cut -b1-13)-$pulseStart)) -gt $REBOOTPULSEMINIMUM ]; then - echo "X730 Rebooting", SHUTDOWN, ", recycling Rpi ..." - sudo reboot - exit - fi - fi -done' > /etc/x730pwr.sh -sudo chmod +x /etc/x730pwr.sh -sudo sed -i '$ i /etc/x730pwr.sh &' /etc/rc.local - - -#X730 full shutdown through Software -#!/bin/bash - - sudo sed -e '/button/ s/^#*/#/' -i /etc/rc.local - - echo '#!/bin/bash - -BUTTON=18 - -echo "$BUTTON" > /sys/class/gpio/export; -echo "out" > /sys/class/gpio/gpio$BUTTON/direction -echo "1" > /sys/class/gpio/gpio$BUTTON/value - -SLEEP=${1:-4} - -re='^[0-9\.]+$' -if ! [[ $SLEEP =~ $re ]] ; then - echo "error: sleep time not a number" >&2; exit 1 -fi - -echo "X730 Shutting down..." -/bin/sleep $SLEEP - -#restore GPIO 18 -echo "0" > /sys/class/gpio/gpio$BUTTON/value -' > /usr/local/bin/x730shutdown.sh -sudo chmod +x /usr/local/bin/x730shutdown.sh