... to solve the nvm-sh environment issue.(mostly vscode) NOTE: may also import other envvars, tbc.
211 lines
6.0 KiB
Bash
Executable File
211 lines
6.0 KiB
Bash
Executable File
#!/bin/bash
|
|
#systemd-run --user --scope --slice=YukiLauncher.slice --unit="$1-$$".scope /bin/sh -c '"$@"' _ "$@"
|
|
|
|
# goddamn nvm-sh !!!
|
|
# have to update PATH before launch
|
|
PATH=$(zsh -c -i 'echo $PATH')
|
|
export PATH
|
|
|
|
# start-bg.sh - robustly start a command in the background and redirect stdout+stderr to a log
|
|
# Generated by GitHub Copilot.
|
|
|
|
# Features:
|
|
# - Uses setsid (preferred) to detach from the controlling terminal.
|
|
# - Falls back to nohup if setsid is unavailable.
|
|
# - Runs the command with stdin closed, stdout+stderr redirected to a logfile.
|
|
# - Sets a safe umask and changes to / to avoid blocking filesystems.
|
|
# - Writes a pidfile next to the log for easy management.
|
|
# - Uses positional args ($@), script PID ($$) and reports child PID ($!).
|
|
#
|
|
# Usage:
|
|
# start-bg.sh [ -l LOGFILE | -d LOGDIR ] [ -m METHOD ] -- command [args...]
|
|
#
|
|
# Options:
|
|
# -l, --log LOGFILE Path to log file (if omitted an auto name is used)
|
|
# -d, --dir LOGDIR Directory for autogenerated logs (default: ./logs)
|
|
# -m, --method METHOD Background method: setsid (default) | nohup
|
|
# -h, --help Show this help and exit
|
|
#
|
|
# Examples:
|
|
# ./start-bg.sh -- sleep 60
|
|
# ./start-bg.sh -d /var/log/myapp -- /usr/bin/myapp --config /etc/myapp.conf
|
|
# ./start-bg.sh -l ./my.log -- /usr/bin/myapp arg1 arg2
|
|
|
|
set -euo pipefail
|
|
|
|
progname=$(basename "$0")
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: $progname [ -l LOGFILE | -d LOGDIR ] [ -m METHOD ] -- command [args...]
|
|
|
|
Start "command [args...]" detached from the terminal and redirect stdout+stderr to a log.
|
|
|
|
METHOD:
|
|
setsid - use setsid to start a session and detach (preferred)
|
|
nohup - use nohup (fallback/compat)
|
|
|
|
When no -l is provided, a log file is auto-generated:
|
|
<LOGDIR>/<command>.<YYYYMMDD-HHMMSS>.pid<$$>.log
|
|
|
|
A pidfile is written as <logfile>.pid containing the child PID.
|
|
EOF
|
|
exit 1
|
|
}
|
|
|
|
# Defaults
|
|
logdir="/tmp/$USER/runner"
|
|
logfile=""
|
|
method="setsid"
|
|
|
|
# Parse options until the `--` separator
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-l|--log)
|
|
if [[ $# -lt 2 ]]; then echo "Missing argument for $1"; usage; fi
|
|
logfile="$2"; shift 2 ;;
|
|
-d|--dir)
|
|
if [[ $# -lt 2 ]]; then echo "Missing argument for $1"; usage; fi
|
|
logdir="$2"; shift 2 ;;
|
|
-m|--method)
|
|
if [[ $# -lt 2 ]]; then echo "Missing argument for $1"; usage; fi
|
|
method="$2"; shift 2 ;;
|
|
-h|--help)
|
|
usage ;;
|
|
--)
|
|
shift; break ;;
|
|
-*)
|
|
echo "Unknown option: $1"; usage ;;
|
|
*)
|
|
break ;;
|
|
esac
|
|
done
|
|
|
|
# Remaining args are the command to run
|
|
if [[ $# -eq 0 ]]; then
|
|
echo "Error: no command specified."
|
|
usage
|
|
fi
|
|
|
|
# Preserve the command and args
|
|
cmd=( "$@" )
|
|
cmd_basename=$(basename "${cmd[0]}")
|
|
timestamp=$(date +%Y%m%d-%H%M%S)
|
|
|
|
mkdir -p -- "${logdir}"
|
|
|
|
if [[ -z "${logfile}" ]]; then
|
|
logfile="${logdir}/${cmd_basename}.${timestamp}.pid$$.log"
|
|
fi
|
|
|
|
pidfile="${logfile}.pid"
|
|
|
|
# Validate method
|
|
case "${method}" in
|
|
setsid|nohup) ;;
|
|
*)
|
|
echo "Invalid method: ${method}. Allowed: setsid|nohup"
|
|
exit 2
|
|
;;
|
|
esac
|
|
|
|
# Prepare environment for child
|
|
# We construct a small wrapper for safe daemonization:
|
|
start_with_setsid() {
|
|
# Prefer existing setsid binary (coreutils/util-linux)
|
|
if ! command -v setsid >/dev/null 2>&1; then
|
|
return 1
|
|
fi
|
|
|
|
# Use setsid to start the process in a new session. We:
|
|
# - set a conservative umask
|
|
# - change to / to avoid keeping directories busy
|
|
# - close stdin and redirect stdout/stderr to logfile
|
|
#
|
|
# We run setsid directly with the command array so arguments are preserved.
|
|
#
|
|
# Note: redirecting >"$logfile" 2>&1 < /dev/null outside ensures the setsid child
|
|
# inherits the redirections.
|
|
(
|
|
umask 022
|
|
cd / || true
|
|
# exec will replace the subshell with the command
|
|
exec "${cmd[@]}"
|
|
) >"${logfile}" 2>&1 < /dev/null &
|
|
child_pid=$!
|
|
|
|
# Detach the job from this shell's job table if possible
|
|
disown "$child_pid" 2>/dev/null || true
|
|
|
|
# setsid the already-forked child to start a new session. Some systems allow:
|
|
# setsid -w <pid>
|
|
# but that's nonportable; instead we try to re-exec the command under setsid.
|
|
# If `setsid` can't be applied to the backgrounded PID, we respawn via setsid:
|
|
if setsid true >/dev/null 2>&1; then
|
|
# Respawn under setsid to ensure proper session leader if available.
|
|
# Kill the previous child (it is still running) and restart under setsid.
|
|
# To avoid races: only do this if cmd is still running and the pid we started is a shell wrapper.
|
|
if kill -0 "$child_pid" 2>/dev/null; then
|
|
# Attempt to terminate wrapper and spawn a true setsid child.
|
|
kill "$child_pid" 2>/dev/null || true
|
|
# Short sleep to allow process cleanup (best-effort)
|
|
sleep 0.05
|
|
fi
|
|
|
|
# Now start the real setsid-backed child with same redirections
|
|
setsid "${cmd[@]}" >"${logfile}" 2>&1 < /dev/null &
|
|
child_pid=$!
|
|
disown "$child_pid" 2>/dev/null || true
|
|
fi
|
|
|
|
printf '%s' "$child_pid"
|
|
return 0
|
|
}
|
|
|
|
start_with_nohup() {
|
|
# Use nohup as fallback; nohup will ignore HUP but doesn't start a new session.
|
|
nohup "${cmd[@]}" >"${logfile}" 2>&1 < /dev/null &
|
|
child_pid=$!
|
|
disown "$child_pid" 2>/dev/null || true
|
|
printf '%s' "$child_pid"
|
|
return 0
|
|
}
|
|
|
|
# Main launcher: pick method and start
|
|
echo "Launching command: ${cmd[*]}"
|
|
echo "Log file: ${logfile}"
|
|
echo "Method: ${method}"
|
|
|
|
child_pid=""
|
|
|
|
if [[ "${method}" == "setsid" ]]; then
|
|
# Try to start via setsid; if setsid not present, fallback to nohup
|
|
if child_pid=$(start_with_setsid); then
|
|
: # success
|
|
else
|
|
echo "setsid not available, falling back to nohup"
|
|
child_pid=$(start_with_nohup)
|
|
fi
|
|
else
|
|
child_pid=$(start_with_nohup)
|
|
fi
|
|
|
|
# Write pidfile (best-effort)
|
|
if [[ -n "${child_pid}" ]]; then
|
|
printf '%s\n' "${child_pid}" > "${pidfile}" 2>/dev/null || true
|
|
fi
|
|
|
|
# Reporting
|
|
echo "Script PID: $$"
|
|
if [[ -n "${child_pid}" ]]; then
|
|
echo "Child PID: ${child_pid}"
|
|
else
|
|
echo "Child PID: (unknown)"
|
|
fi
|
|
echo "Pidfile: ${pidfile}"
|
|
echo "Started at: $(date --iso-8601=seconds 2>/dev/null || date +%Y-%m-%dT%H:%M:%S%z)"
|
|
echo
|
|
echo "To follow the log: tail -F ${logfile}"
|
|
|
|
exit 0
|