run: replace only owned launcher instance

This commit is contained in:
2026-08-14 03:30:29 +09:00
parent c4b0141413
commit 24e380651c
2 changed files with 199 additions and 4 deletions

View File

@@ -129,9 +129,12 @@ font-file arguments replace these defaults:
that it starts. It waits for that daemon's IPC socket and IBus address file 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 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 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. The XIM. A signal or XIM exit stops and reaps the launcher's daemon. On repeated
launcher never replaces an existing daemon: an occupied per-user socket is a invocation, it stops a previous live `run.sh` only after confirming that the
visible startup failure. 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.
The IPC socket is placed at `$XDG_RUNTIME_DIR/strans.sock` when 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 `XDG_RUNTIME_DIR` is an absolute path. Otherwise strans uses the per-user

194
run.sh
View File

@@ -1,6 +1,8 @@
#!/bin/sh #!/bin/sh
cd "$(dirname "$0")" || exit 1 cd "$(dirname "$0")" || exit 1
root=$(pwd -P) || exit 1
uid=$(id -u) || exit 1
if test "$#" -eq 0; then if test "$#" -eq 0; then
set -- set --
@@ -46,6 +48,10 @@ fi
strans_pid= strans_pid=
xim_pid= xim_pid=
old_daemon_pid=
old_daemon_start=
old_launcher_pid=
old_launcher_start=
cleanup() cleanup()
{ {
@@ -83,12 +89,198 @@ ibusready()
return 1 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 </proc/net/unix
} 2>/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 cleanup 0
trap 'exit 129' 1 trap 'exit 129' 1
trap 'exit 130' 2 trap 'exit 130' 2
trap 'exit 143' 15 trap 'exit 143' 15
./strans map "$@" & 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=$! strans_pid=$!
attempt=0 attempt=0