#!/bin/sh cd "$(dirname "$0")" || exit 1 root=$(pwd -P) || exit 1 uid=$(id -u) || exit 1 if test "$#" -eq 0; then set -- for font in \ /usr/share/fonts/TTF/DejaVuSans.ttf \ /usr/share/fonts/TTF/Jigmo.ttf \ /usr/share/fonts/TTF/Jigmo2.ttf \ /usr/share/fonts/noto/NotoSansCJK-Regular.ttc do if test -f "$font"; then set -- "$@" "$font" fi done if test "$#" -eq 0; then echo "run.sh: no default font found; pass a font file" >&2 exit 1 fi fi if ! test -x ./strans; then echo "run.sh: ./strans is not executable; run make first" >&2 exit 1 fi 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 exit 1 fi strans_pid= xim_pid= old_daemon_pid= old_daemon_start= old_launcher_pid= old_launcher_start= 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_pid=$! attempt=0 while test "$attempt" -lt 10; do if test -S "$ipc" && ibusready; then break fi if ! kill -0 "$strans_pid" 2>/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 fi echo "run.sh: daemon $strans_pid is ready" if test -z "${DISPLAY-}"; then echo "run.sh: DISPLAY is unset; XIM will not be started" wait "$strans_pid" status=$? strans_pid= exit "$status" fi xim/strans-xim & xim_pid=$! echo "run.sh: started XIM $xim_pid; press Ctrl-C to stop both processes" wait "$xim_pid" status=$? xim_pid= exit "$status"