From 3a710a7cd105c186ad2c63f2fec69c1373175817 Mon Sep 17 00:00:00 2001 From: Kevin Vranken <29517264+Helaas@users.noreply.github.com> Date: Mon, 23 Mar 2026 19:59:58 +0100 Subject: [PATCH 1/2] feat: implement hook system for pre and post command execution --- .../SYSTEM/desktop/paks/MinUI.pak/launch.sh | 33 +++++++++++++++++++ .../SYSTEM/tg5040/paks/MinUI.pak/launch.sh | 33 +++++++++++++++++++ .../SYSTEM/tg5050/paks/MinUI.pak/launch.sh | 33 +++++++++++++++++++ 3 files changed, 99 insertions(+) diff --git a/skeleton/SYSTEM/desktop/paks/MinUI.pak/launch.sh b/skeleton/SYSTEM/desktop/paks/MinUI.pak/launch.sh index 5c1f99ae0..7887b9549 100755 --- a/skeleton/SYSTEM/desktop/paks/MinUI.pak/launch.sh +++ b/skeleton/SYSTEM/desktop/paks/MinUI.pak/launch.sh @@ -47,6 +47,36 @@ fi cd $(dirname "$0") +####################################### +# Hook system + +HOOKS_DIR="$USERDATA_PATH/.hooks" + +run_hooks() { + _hook_phase="$1" + _hook_dir="$HOOKS_DIR/${_hook_phase}-launch.d" + [ -d "$_hook_dir" ] || return 0 + for _hook_script in "$_hook_dir"/*.sh; do + [ -f "$_hook_script" ] || continue + ( export HOOK_PHASE="$_hook_phase"; "$_hook_script" ) > /dev/null 2>&1 || true + done +} + +parse_hook_cmd() { + HOOK_CMD="$1" + HOOK_EMU_PATH=$(echo "$HOOK_CMD" | sed "s/^'\\([^']*\\)'.*/\\1/") + _remainder=$(echo "$HOOK_CMD" | sed "s/^'[^']*'//") + if echo "$_remainder" | grep -q "'"; then + HOOK_TYPE="rom" + HOOK_ROM_PATH=$(echo "$_remainder" | sed "s/.*'\\([^']*\\)'.*/\\1/") + else + HOOK_TYPE="pak" + HOOK_ROM_PATH="" + fi + [ -f /tmp/last.txt ] && HOOK_LAST=$(cat /tmp/last.txt) || HOOK_LAST="" + export HOOK_CMD HOOK_EMU_PATH HOOK_TYPE HOOK_ROM_PATH HOOK_LAST +} + ####################################### EXEC_PATH="/tmp/nextui_exec" @@ -57,7 +87,10 @@ touch "$EXEC_PATH" && sync if [ -f $NEXT_PATH ]; then CMD=`cat $NEXT_PATH` + parse_hook_cmd "$CMD" + run_hooks pre eval $CMD + run_hooks post rm -f $NEXT_PATH fi #done diff --git a/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh b/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh index 09528cf3a..69c597971 100755 --- a/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh +++ b/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh @@ -153,6 +153,36 @@ fi cd $(dirname "$0") +####################################### +# Hook system + +HOOKS_DIR="$USERDATA_PATH/.hooks" + +run_hooks() { + _hook_phase="$1" + _hook_dir="$HOOKS_DIR/${_hook_phase}-launch.d" + [ -d "$_hook_dir" ] || return 0 + for _hook_script in "$_hook_dir"/*.sh; do + [ -f "$_hook_script" ] || continue + ( export HOOK_PHASE="$_hook_phase"; "$_hook_script" ) > /dev/null 2>&1 || true + done +} + +parse_hook_cmd() { + HOOK_CMD="$1" + HOOK_EMU_PATH=$(echo "$HOOK_CMD" | sed "s/^'\\([^']*\\)'.*/\\1/") + _remainder=$(echo "$HOOK_CMD" | sed "s/^'[^']*'//") + if echo "$_remainder" | grep -q "'"; then + HOOK_TYPE="rom" + HOOK_ROM_PATH=$(echo "$_remainder" | sed "s/.*'\\([^']*\\)'.*/\\1/") + else + HOOK_TYPE="pak" + HOOK_ROM_PATH="" + fi + [ -f /tmp/last.txt ] && HOOK_LAST=$(cat /tmp/last.txt) || HOOK_LAST="" + export HOOK_CMD HOOK_EMU_PATH HOOK_TYPE HOOK_ROM_PATH HOOK_LAST +} + ####################################### # kill show2.elf if running @@ -167,7 +197,10 @@ while [ -f $EXEC_PATH ]; do if [ -f $NEXT_PATH ]; then CMD=`cat $NEXT_PATH` + parse_hook_cmd "$CMD" + run_hooks pre eval $CMD + run_hooks post rm -f $NEXT_PATH echo $CPU_SPEED_PERF > $CPU_PATH fi diff --git a/skeleton/SYSTEM/tg5050/paks/MinUI.pak/launch.sh b/skeleton/SYSTEM/tg5050/paks/MinUI.pak/launch.sh index d73b92a2a..1eb393b45 100755 --- a/skeleton/SYSTEM/tg5050/paks/MinUI.pak/launch.sh +++ b/skeleton/SYSTEM/tg5050/paks/MinUI.pak/launch.sh @@ -166,6 +166,36 @@ fi cd $(dirname "$0") +####################################### +# Hook system + +HOOKS_DIR="$USERDATA_PATH/.hooks" + +run_hooks() { + _hook_phase="$1" + _hook_dir="$HOOKS_DIR/${_hook_phase}-launch.d" + [ -d "$_hook_dir" ] || return 0 + for _hook_script in "$_hook_dir"/*.sh; do + [ -f "$_hook_script" ] || continue + ( export HOOK_PHASE="$_hook_phase"; "$_hook_script" ) > /dev/null 2>&1 || true + done +} + +parse_hook_cmd() { + HOOK_CMD="$1" + HOOK_EMU_PATH=$(echo "$HOOK_CMD" | sed "s/^'\\([^']*\\)'.*/\\1/") + _remainder=$(echo "$HOOK_CMD" | sed "s/^'[^']*'//") + if echo "$_remainder" | grep -q "'"; then + HOOK_TYPE="rom" + HOOK_ROM_PATH=$(echo "$_remainder" | sed "s/.*'\\([^']*\\)'.*/\\1/") + else + HOOK_TYPE="pak" + HOOK_ROM_PATH="" + fi + [ -f /tmp/last.txt ] && HOOK_LAST=$(cat /tmp/last.txt) || HOOK_LAST="" + export HOOK_CMD HOOK_EMU_PATH HOOK_TYPE HOOK_ROM_PATH HOOK_LAST +} + ####################################### # kill show2.elf if running @@ -180,7 +210,10 @@ while [ -f $EXEC_PATH ]; do if [ -f $NEXT_PATH ]; then CMD=`cat $NEXT_PATH` + parse_hook_cmd "$CMD" + run_hooks pre eval $CMD + run_hooks post rm -f $NEXT_PATH echo $CPU_SPEED_PERF > $BIG_PATH fi From 4a3fde6f197555859d70d1be9c24407765bdb13b Mon Sep 17 00:00:00 2001 From: Kevin Vranken <29517264+Helaas@users.noreply.github.com> Date: Tue, 24 Mar 2026 01:10:53 +0100 Subject: [PATCH 2/2] feat: implement shared hook runner and integrate into launch and suspend scripts --- skeleton/SYSTEM/desktop/bin/run_hooks.sh | 36 +++++++++++++++++++ .../SYSTEM/desktop/paks/MinUI.pak/launch.sh | 19 +++------- skeleton/SYSTEM/tg5040/bin/run_hooks.sh | 36 +++++++++++++++++++ skeleton/SYSTEM/tg5040/bin/suspend | 7 ++++ .../SYSTEM/tg5040/paks/MinUI.pak/launch.sh | 19 +++------- skeleton/SYSTEM/tg5050/bin/run_hooks.sh | 36 +++++++++++++++++++ skeleton/SYSTEM/tg5050/bin/suspend | 7 ++++ .../SYSTEM/tg5050/paks/MinUI.pak/launch.sh | 19 +++------- 8 files changed, 137 insertions(+), 42 deletions(-) create mode 100755 skeleton/SYSTEM/desktop/bin/run_hooks.sh create mode 100755 skeleton/SYSTEM/tg5040/bin/run_hooks.sh create mode 100755 skeleton/SYSTEM/tg5050/bin/run_hooks.sh diff --git a/skeleton/SYSTEM/desktop/bin/run_hooks.sh b/skeleton/SYSTEM/desktop/bin/run_hooks.sh new file mode 100755 index 000000000..30b86ca60 --- /dev/null +++ b/skeleton/SYSTEM/desktop/bin/run_hooks.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# run_hooks.sh - shared hook runner for NextUI +# Usage: run_hooks.sh [--sync-only] +# +# dir-name: directory name under $USERDATA_PATH/.hooks/ (e.g. pre-launch.d, boot.d) +# --sync-only: force all scripts to run synchronously +# +# By default, scripts run in the background. Scripts ending in .sync.sh +# always run synchronously. All background scripts are waited on before exit. + +DIR_NAME="$1" +SYNC_ONLY="${2:-}" + +: "${SDCARD_PATH:=/var/tmp/nextui/sdcard}" +: "${PLATFORM:=desktop}" +: "${USERDATA_PATH:=$SDCARD_PATH/.userdata/$PLATFORM}" + +HOOK_DIR="$USERDATA_PATH/.hooks/$DIR_NAME" +[ -d "$HOOK_DIR" ] || exit 0 + +case "$DIR_NAME" in + pre-*) export HOOK_PHASE="pre" ;; + post-*) export HOOK_PHASE="post" ;; + boot*) export HOOK_PHASE="boot" ;; +esac +export HOOK_CATEGORY="$DIR_NAME" + +for script in "$HOOK_DIR"/*.sh; do + [ -f "$script" ] || continue + if [ "$SYNC_ONLY" = "--sync-only" ] || echo "$script" | grep -q '\.sync\.sh$'; then + ( "$script" ) > /dev/null 2>&1 || true + else + ( "$script" ) > /dev/null 2>&1 & + fi +done +wait diff --git a/skeleton/SYSTEM/desktop/paks/MinUI.pak/launch.sh b/skeleton/SYSTEM/desktop/paks/MinUI.pak/launch.sh index 7887b9549..5b6922bcc 100755 --- a/skeleton/SYSTEM/desktop/paks/MinUI.pak/launch.sh +++ b/skeleton/SYSTEM/desktop/paks/MinUI.pak/launch.sh @@ -45,23 +45,14 @@ if [ -f "$AUTO_PATH" ]; then "$AUTO_PATH" fi +# Composable boot hooks (run after auto.sh for backward compatibility) +"$SYSTEM_PATH/bin/run_hooks.sh" boot.d + cd $(dirname "$0") ####################################### # Hook system -HOOKS_DIR="$USERDATA_PATH/.hooks" - -run_hooks() { - _hook_phase="$1" - _hook_dir="$HOOKS_DIR/${_hook_phase}-launch.d" - [ -d "$_hook_dir" ] || return 0 - for _hook_script in "$_hook_dir"/*.sh; do - [ -f "$_hook_script" ] || continue - ( export HOOK_PHASE="$_hook_phase"; "$_hook_script" ) > /dev/null 2>&1 || true - done -} - parse_hook_cmd() { HOOK_CMD="$1" HOOK_EMU_PATH=$(echo "$HOOK_CMD" | sed "s/^'\\([^']*\\)'.*/\\1/") @@ -88,9 +79,9 @@ touch "$EXEC_PATH" && sync if [ -f $NEXT_PATH ]; then CMD=`cat $NEXT_PATH` parse_hook_cmd "$CMD" - run_hooks pre + "$SYSTEM_PATH/bin/run_hooks.sh" pre-launch.d eval $CMD - run_hooks post + "$SYSTEM_PATH/bin/run_hooks.sh" post-launch.d rm -f $NEXT_PATH fi #done diff --git a/skeleton/SYSTEM/tg5040/bin/run_hooks.sh b/skeleton/SYSTEM/tg5040/bin/run_hooks.sh new file mode 100755 index 000000000..96fbe4833 --- /dev/null +++ b/skeleton/SYSTEM/tg5040/bin/run_hooks.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# run_hooks.sh - shared hook runner for NextUI +# Usage: run_hooks.sh [--sync-only] +# +# dir-name: directory name under $USERDATA_PATH/.hooks/ (e.g. pre-launch.d, boot.d) +# --sync-only: force all scripts to run synchronously +# +# By default, scripts run in the background. Scripts ending in .sync.sh +# always run synchronously. All background scripts are waited on before exit. + +DIR_NAME="$1" +SYNC_ONLY="${2:-}" + +: "${SDCARD_PATH:=/mnt/SDCARD}" +: "${PLATFORM:=tg5040}" +: "${USERDATA_PATH:=$SDCARD_PATH/.userdata/$PLATFORM}" + +HOOK_DIR="$USERDATA_PATH/.hooks/$DIR_NAME" +[ -d "$HOOK_DIR" ] || exit 0 + +case "$DIR_NAME" in + pre-*) export HOOK_PHASE="pre" ;; + post-*) export HOOK_PHASE="post" ;; + boot*) export HOOK_PHASE="boot" ;; +esac +export HOOK_CATEGORY="$DIR_NAME" + +for script in "$HOOK_DIR"/*.sh; do + [ -f "$script" ] || continue + if [ "$SYNC_ONLY" = "--sync-only" ] || echo "$script" | grep -q '\.sync\.sh$'; then + ( "$script" ) > /dev/null 2>&1 || true + else + ( "$script" ) > /dev/null 2>&1 & + fi +done +wait diff --git a/skeleton/SYSTEM/tg5040/bin/suspend b/skeleton/SYSTEM/tg5040/bin/suspend index 4a384d4c7..593159423 100644 --- a/skeleton/SYSTEM/tg5040/bin/suspend +++ b/skeleton/SYSTEM/tg5040/bin/suspend @@ -16,6 +16,10 @@ asound_state_dir=/tmp/asound-suspend before() { >&2 echo "Preparing for suspend..." + # Run pre-sleep hooks synchronously before services are stopped. + # Subshell ensures a crashing hook cannot block suspend. + ( "$SYSTEM_PATH/bin/run_hooks.sh" pre-sleep.d --sync-only ) >/dev/null 2>&1 || true + >&2 echo "Saving mixer state..." mkdir -p "$asound_state_dir" alsactl --file "$asound_state_dir/asound.state.pre" store || true @@ -36,6 +40,9 @@ before() { after() { >&2 echo "Resumed from suspend." + # Run post-resume hooks in background immediately after wake. + ( "$SYSTEM_PATH/bin/run_hooks.sh" post-resume.d ) >/dev/null 2>&1 & + if [ -n "$wifid_running" ]; then >&2 echo "Starting wpa_supplicant..." /etc/wifi/wifi_init.sh start || true diff --git a/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh b/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh index 69c597971..a3e141076 100755 --- a/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh +++ b/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh @@ -151,23 +151,14 @@ if [ -f "$AUTO_PATH" ]; then "$AUTO_PATH" fi +# Composable boot hooks (run after auto.sh for backward compatibility) +"$SYSTEM_PATH/bin/run_hooks.sh" boot.d + cd $(dirname "$0") ####################################### # Hook system -HOOKS_DIR="$USERDATA_PATH/.hooks" - -run_hooks() { - _hook_phase="$1" - _hook_dir="$HOOKS_DIR/${_hook_phase}-launch.d" - [ -d "$_hook_dir" ] || return 0 - for _hook_script in "$_hook_dir"/*.sh; do - [ -f "$_hook_script" ] || continue - ( export HOOK_PHASE="$_hook_phase"; "$_hook_script" ) > /dev/null 2>&1 || true - done -} - parse_hook_cmd() { HOOK_CMD="$1" HOOK_EMU_PATH=$(echo "$HOOK_CMD" | sed "s/^'\\([^']*\\)'.*/\\1/") @@ -198,9 +189,9 @@ while [ -f $EXEC_PATH ]; do if [ -f $NEXT_PATH ]; then CMD=`cat $NEXT_PATH` parse_hook_cmd "$CMD" - run_hooks pre + "$SYSTEM_PATH/bin/run_hooks.sh" pre-launch.d eval $CMD - run_hooks post + "$SYSTEM_PATH/bin/run_hooks.sh" post-launch.d rm -f $NEXT_PATH echo $CPU_SPEED_PERF > $CPU_PATH fi diff --git a/skeleton/SYSTEM/tg5050/bin/run_hooks.sh b/skeleton/SYSTEM/tg5050/bin/run_hooks.sh new file mode 100755 index 000000000..7e6886546 --- /dev/null +++ b/skeleton/SYSTEM/tg5050/bin/run_hooks.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# run_hooks.sh - shared hook runner for NextUI +# Usage: run_hooks.sh [--sync-only] +# +# dir-name: directory name under $USERDATA_PATH/.hooks/ (e.g. pre-launch.d, boot.d) +# --sync-only: force all scripts to run synchronously +# +# By default, scripts run in the background. Scripts ending in .sync.sh +# always run synchronously. All background scripts are waited on before exit. + +DIR_NAME="$1" +SYNC_ONLY="${2:-}" + +: "${SDCARD_PATH:=/mnt/SDCARD}" +: "${PLATFORM:=tg5050}" +: "${USERDATA_PATH:=$SDCARD_PATH/.userdata/$PLATFORM}" + +HOOK_DIR="$USERDATA_PATH/.hooks/$DIR_NAME" +[ -d "$HOOK_DIR" ] || exit 0 + +case "$DIR_NAME" in + pre-*) export HOOK_PHASE="pre" ;; + post-*) export HOOK_PHASE="post" ;; + boot*) export HOOK_PHASE="boot" ;; +esac +export HOOK_CATEGORY="$DIR_NAME" + +for script in "$HOOK_DIR"/*.sh; do + [ -f "$script" ] || continue + if [ "$SYNC_ONLY" = "--sync-only" ] || echo "$script" | grep -q '\.sync\.sh$'; then + ( "$script" ) > /dev/null 2>&1 || true + else + ( "$script" ) > /dev/null 2>&1 & + fi +done +wait diff --git a/skeleton/SYSTEM/tg5050/bin/suspend b/skeleton/SYSTEM/tg5050/bin/suspend index 9062b84ef..090cfbad5 100644 --- a/skeleton/SYSTEM/tg5050/bin/suspend +++ b/skeleton/SYSTEM/tg5050/bin/suspend @@ -16,6 +16,10 @@ asound_state_dir=/tmp/asound-suspend before() { >&2 echo "Preparing for suspend..." + # Run pre-sleep hooks synchronously before services are stopped. + # Subshell ensures a crashing hook cannot block suspend. + ( "$SYSTEM_PATH/bin/run_hooks.sh" pre-sleep.d --sync-only ) >/dev/null 2>&1 || true + >&2 echo "Saving mixer state..." mkdir -p "$asound_state_dir" alsactl --file "$asound_state_dir/asound.state.pre" store || true @@ -36,6 +40,9 @@ before() { after() { >&2 echo "Resumed from suspend." + # Run post-resume hooks in background immediately after wake. + ( "$SYSTEM_PATH/bin/run_hooks.sh" post-resume.d ) >/dev/null 2>&1 & + if [ -n "$wifid_running" ]; then >&2 echo "Starting wpa_supplicant..." $SYSTEM_PATH/etc/wifi/wifi_init.sh start || true diff --git a/skeleton/SYSTEM/tg5050/paks/MinUI.pak/launch.sh b/skeleton/SYSTEM/tg5050/paks/MinUI.pak/launch.sh index 1eb393b45..db8fb90ab 100755 --- a/skeleton/SYSTEM/tg5050/paks/MinUI.pak/launch.sh +++ b/skeleton/SYSTEM/tg5050/paks/MinUI.pak/launch.sh @@ -164,23 +164,14 @@ if [ -f "$AUTO_PATH" ]; then echo after auto.sh `cat /proc/uptime` >> /tmp/nextui_boottime fi +# Composable boot hooks (run after auto.sh for backward compatibility) +"$SYSTEM_PATH/bin/run_hooks.sh" boot.d + cd $(dirname "$0") ####################################### # Hook system -HOOKS_DIR="$USERDATA_PATH/.hooks" - -run_hooks() { - _hook_phase="$1" - _hook_dir="$HOOKS_DIR/${_hook_phase}-launch.d" - [ -d "$_hook_dir" ] || return 0 - for _hook_script in "$_hook_dir"/*.sh; do - [ -f "$_hook_script" ] || continue - ( export HOOK_PHASE="$_hook_phase"; "$_hook_script" ) > /dev/null 2>&1 || true - done -} - parse_hook_cmd() { HOOK_CMD="$1" HOOK_EMU_PATH=$(echo "$HOOK_CMD" | sed "s/^'\\([^']*\\)'.*/\\1/") @@ -211,9 +202,9 @@ while [ -f $EXEC_PATH ]; do if [ -f $NEXT_PATH ]; then CMD=`cat $NEXT_PATH` parse_hook_cmd "$CMD" - run_hooks pre + "$SYSTEM_PATH/bin/run_hooks.sh" pre-launch.d eval $CMD - run_hooks post + "$SYSTEM_PATH/bin/run_hooks.sh" post-launch.d rm -f $NEXT_PATH echo $CPU_SPEED_PERF > $BIG_PATH fi