diff --git a/README.md b/README.md index 57c1889..004e384 100644 --- a/README.md +++ b/README.md @@ -125,16 +125,18 @@ font-file arguments replace these defaults: ./run.sh /path/to/primary.ttf /path/to/fallback.ttc ``` -`run.sh` remains in the foreground and owns only the daemon and XIM process -that it starts. It waits for that daemon's IPC socket and IBus address file -before reporting success. With `DISPLAY` set it then starts XIM; without a -display it keeps the headless daemon in the foreground and does not start -XIM. A signal or XIM exit stops and reaps the launcher's daemon. On repeated -invocation, it stops a previous live `run.sh` only after confirming that the -old launcher directly owns the daemon serving the same IPC and IBus -endpoints and that the daemon carries that launcher's PID and process-start -identity. A manually started or orphaned daemon is left untouched and -reported as a visible startup failure. +`run.sh` uses `pkill` to stop this user's existing `strans` and `strans-xim` +processes, starts fresh ones in the background, and returns. Run the same +command again to restart them. Without `DISPLAY`, it starts only the daemon. + +An X startup hook can invoke it directly before starting the desktop session: + +```sh +/path/to/strans/run.sh +exec /bin/sh /etc/xdg/xfce4/xinitrc "$@" +``` + +The launcher targets Linux and requires `pkill` from procps-ng. The IPC socket is placed at `$XDG_RUNTIME_DIR/strans.sock` when `XDG_RUNTIME_DIR` is an absolute path. Otherwise strans uses the per-user diff --git a/run.sh b/run.sh index 602b51c..6df65bd 100755 --- a/run.sh +++ b/run.sh @@ -1,8 +1,6 @@ #!/bin/sh cd "$(dirname "$0")" || exit 1 -root=$(pwd -P) || exit 1 -uid=$(id -u) || exit 1 if test "$#" -eq 0; then set -- @@ -12,9 +10,7 @@ if test "$#" -eq 0; then /usr/share/fonts/TTF/Jigmo2.ttf \ /usr/share/fonts/noto/NotoSansCJK-Regular.ttc do - if test -f "$font"; then - set -- "$@" "$font" - fi + test -f "$font" && set -- "$@" "$font" done if test "$#" -eq 0; then echo "run.sh: no default font found; pass a font file" >&2 @@ -30,297 +26,24 @@ if test -n "${DISPLAY-}" && ! test -x xim/strans-xim; then echo "run.sh: xim/strans-xim is not executable; run make first" >&2 exit 1 fi - -case ${XDG_RUNTIME_DIR-} in -/) ipc=/strans.sock ;; -/*/) ipc=${XDG_RUNTIME_DIR}strans.sock ;; -/*) ipc=${XDG_RUNTIME_DIR}/strans.sock ;; -*) ipc=/tmp/strans.$(id -u) ;; -esac -if test -n "${XDG_CONFIG_HOME-}"; then - busdir=$XDG_CONFIG_HOME/ibus/bus -elif test "${HOME+x}" = x; then - busdir=$HOME/.config/ibus/bus -else - echo "run.sh: HOME or XDG_CONFIG_HOME is required for IBus discovery" >&2 +if ! command -v pkill >/dev/null 2>&1; then + echo "run.sh: pkill is required" >&2 exit 1 fi -strans_pid= -xim_pid= -old_daemon_pid= -old_daemon_start= -old_launcher_pid= -old_launcher_start= +uid=$(id -u) || exit 1 +pkill -TERM -u "$uid" -x strans-xim 2>/dev/null || : +pkill -TERM -u "$uid" -x strans 2>/dev/null || : +sleep 0.1 +pkill -KILL -u "$uid" -x strans-xim 2>/dev/null || : +pkill -KILL -u "$uid" -x strans 2>/dev/null || : -cleanup() -{ - status=$? - trap - 0 1 2 15 - if test -n "$xim_pid"; then - kill "$xim_pid" 2>/dev/null || : - wait "$xim_pid" 2>/dev/null || : - xim_pid= - fi - if test -n "$strans_pid"; then - kill "$strans_pid" 2>/dev/null || : - wait "$strans_pid" 2>/dev/null || : - strans_pid= - fi - exit "$status" -} - -ibusready() -{ - for address in "$busdir"/*; do - test -f "$address" || continue - foundpid= - foundaddress= - while IFS= read -r line; do - case $line in - IBUS_DAEMON_PID="$strans_pid") foundpid=1 ;; - IBUS_ADDRESS=unix:abstract=strans-"$strans_pid",*) foundaddress=1 ;; - esac - done <"$address" - if test -n "$foundpid" && test -n "$foundaddress"; then - return 0 - fi - done - return 1 -} - -procuid() -{ - { - while IFS=' ' read -r key real effective saved filesystem rest; do - case $key in - Uid:) - test "$real" = "$effective" && - test "$real" = "$saved" && - test "$real" = "$filesystem" || return 1 - printf '%s\n' "$real" - return 0 - ;; - esac - done <"/proc/$1/status" - } 2>/dev/null - return 1 -} - -procparent() -{ - { - while IFS=' ' read -r key value rest; do - case $key in - PPid:) printf '%s\n' "$value"; return 0 ;; - esac - done <"/proc/$1/status" - } 2>/dev/null - return 1 -} - -procstart() -{ - proc_stat= - { IFS= read -r proc_stat <"/proc/$1/stat"; } 2>/dev/null || return 1 - proc_stat=${proc_stat##*) } - set -- $proc_stat - test "$#" -ge 20 || return 1 - test "$1" != Z || return 1 - printf '%s\n' "${20}" -} - -sameproc() -{ - test -n "$2" || return 1 - proc_now=$(procstart "$1") || return 1 - test "$proc_now" = "$2" -} - -listeninginode() -{ - listener_inode= - listener_count=0 - { - while IFS=' ' read -r num ref proto flags type state inode path; do - if test "$flags" = 00010000 && test "$type" = 0001 && - test "$state" = 01 && test "$path" = "$1"; then - listener_inode=$inode - listener_count=$((listener_count + 1)) - fi - done /dev/null - test "$listener_count" -eq 1 || return 1 - printf '%s\n' "$listener_inode" -} - -socketowned() -{ - socket_inode=$(listeninginode "$2") || return 1 - for socket_fd in "/proc/$1/fd"/*; do - test "$(readlink "$socket_fd" 2>/dev/null)" = \ - "socket:[$socket_inode]" && return 0 - done - return 1 -} - -validdaemon() -{ - test "$(procuid "$1")" = "$uid" || return 1 - test "$(readlink "/proc/$1/cwd" 2>/dev/null)" = "$root" || return 1 - daemon_exe=$(readlink "/proc/$1/exe" 2>/dev/null) || return 1 - case $daemon_exe in - "$root/strans"|"$root/strans (deleted)") ;; - *) return 1 ;; - esac - test -S "$ipc" || return 1 - socketowned "$1" "$ipc" || return 1 - socketowned "$1" "@strans-$1" -} - -finddaemon() -{ - old_daemon_pid= - old_daemon_start= - for address in "$busdir"/*; do - test -f "$address" || continue - address_pid= - address_ok= - while IFS= read -r line; do - case $line in - IBUS_DAEMON_PID=*) address_pid=${line#IBUS_DAEMON_PID=} ;; - esac - done <"$address" - case $address_pid in - ''|*[!0-9]*) continue ;; - esac - while IFS= read -r line; do - case $line in - IBUS_ADDRESS=unix:abstract=strans-"$address_pid",*) - address_ok=1 ;; - esac - done <"$address" - test -n "$address_ok" || continue - validdaemon "$address_pid" || continue - address_start=$(procstart "$address_pid") || continue - validdaemon "$address_pid" || continue - old_daemon_pid=$address_pid - old_daemon_start=$address_start - return 0 - done - return 1 -} - -findlauncher() -{ - launcher_env=$({ tr '\000' '\n' <"/proc/$old_daemon_pid/environ"; } \ - 2>/dev/null) || return 1 - old_launcher_pid=$(printf '%s\n' "$launcher_env" | - sed -n 's/^STRANS_RUN_OWNER_PID=//p') - old_launcher_start=$(printf '%s\n' "$launcher_env" | - sed -n 's/^STRANS_RUN_OWNER_START=//p') - case $old_launcher_pid in - ''|*[!0-9]*|0|1|"$$") return 1 ;; - esac - case $old_launcher_start in - ''|*[!0-9]*) return 1 ;; - esac - test "$(procuid "$old_launcher_pid")" = "$uid" || return 1 - test "$(readlink "/proc/$old_launcher_pid/cwd" 2>/dev/null)" = "$root" || - return 1 - sameproc "$old_launcher_pid" "$old_launcher_start" || return 1 - sameproc "$old_daemon_pid" "$old_daemon_start" || return 1 - test "$(procparent "$old_daemon_pid")" = "$old_launcher_pid" || return 1 - return 0 -} - -stoplauncher() -{ - sameproc "$old_daemon_pid" "$old_daemon_start" || return 1 - test "$(procparent "$old_daemon_pid")" = "$old_launcher_pid" || return 1 - sameproc "$old_launcher_pid" "$old_launcher_start" || return 1 - kill "$old_launcher_pid" 2>/dev/null || return 1 - echo "run.sh: stopping previous launcher $old_launcher_pid (daemon $old_daemon_pid)" - - stop_attempt=0 - while test "$stop_attempt" -lt 10; do - if ! sameproc "$old_launcher_pid" "$old_launcher_start" && - ! sameproc "$old_daemon_pid" "$old_daemon_start"; then - return 0 - fi - stop_attempt=$((stop_attempt + 1)) - if test "$stop_attempt" -lt 10; then - sleep 1 - fi - done - echo "run.sh: previous launcher did not stop within 10 seconds" >&2 - return 1 -} - -trap cleanup 0 -trap 'exit 129' 1 -trap 'exit 130' 2 -trap 'exit 143' 15 - -launcher_start=$(procstart "$$") || { - echo "run.sh: cannot read launcher process identity from /proc" >&2 - exit 1 -} - -if finddaemon; then - if ! findlauncher; then - echo "run.sh: daemon $old_daemon_pid owns the current endpoints but has no" >&2 - echo "run.sh: live run.sh owner; leaving it untouched (stop it once, then retry)" >&2 - exit 1 - fi - if ! stoplauncher; then - echo "run.sh: previous launcher did not stop cleanly; refusing to start another daemon" >&2 - exit 1 - fi -fi - -STRANS_RUN_OWNER_PID=$$ STRANS_RUN_OWNER_START=$launcher_start \ - ./strans map "$@" & +./strans map "$@" /dev/null; then - wait "$strans_pid" - status=$? - strans_pid= - if test "$status" -eq 0; then - status=1 - fi - echo "run.sh: daemon exited before publishing IPC and IBus endpoints" >&2 - exit "$status" - fi - attempt=$((attempt + 1)) - if test "$attempt" -lt 10; then - sleep 1 - fi -done -if test "$attempt" -eq 10; then - echo "run.sh: daemon did not publish IPC and IBus endpoints within 10 seconds" >&2 - exit 1 +if test -n "${DISPLAY-}"; then + xim/strans-xim