#!/bin/zsh
# vim: sts=2 sw=2 et
setopt no_unset warn_create_global
zmodload zsh/system || exit $?
typeset -g map=/run/display-container-map
typeset -g scandir=/run/service
typeset -g build=/run/xpra.service
main() {
typeset -g container client_display
container=$1
shift || exit 100
[[ $container == */* ]] && exit 100
client_display=${${DISPLAY%%.*}#:}
do_lock
find_server_display
ensure_services
do_unlock
start_services
print_cookie
}
typeset -f -t main
do_lock() {
mkdir -p $map || exit $?
touch $map/.lock || exit $?
zsystem flock -f lockfd $map/.lock || exit $?
}
find_server_display() {
typeset -g display
local -a found allocated
found=( $map/*=$container(N) )
if ! (($#found)); then
# spawn a new Xpra session this container.
allocated=( $map/*=*(N) )
allocated=( ${${allocated:t}%%=*} )
#for n in {${${DISPLAY%%.*}#:}00..${${DISPLAY%%.*}#:}99}; do
for n in {100..199}; do
if (( $allocated[(I)$n] == 0 )); then
display=$n
touch $map/$display=$container
break
fi
done
if [[ -z $display ]]; then
exit 3 # All 99 displays occupied?!
fi
else
display=${${found[1]:t}%%=*}
fi
}
ensure_services() {
typeset -ga new_services client_services
typeset -g sv_name_xorg=xpra-xorg.$display
typeset -g sv_name_server=xpra-server.$display
typeset -g sv_name_client=xpra-client.$display.for.$client_display
write_sv_definitions
# if (($#new_services)); then
# - s6-svscanctl -a /run/service
# # TODO: wait for s6-supervises to start
# fi
}
### Color and text definition {{{1
typeset -g hl_fatal hl_reset
# die messages
if (( $terminfo[colors] >= 8 )); then
hl_fatal='%F{red}%B'; hl_fatal=${(%)hl_fatal}
hl_warn='%F{yellow}%B'; hl_warn=${(%)hl_warn}
hl_reset='%b%f'; hl_reset=${(%)hl_reset}
fi
### Utility functions {{{1
err_msg() {
local first=$1
shift
printf >&2 '%s%s%s%s\n' "$hl_fatal" "$first" "$hl_reset" "$*"
}
warn_msg() {
local first=$1
shift
printf >&2 '%s%s%s%s\n' "$hl_warn" "$first" "$hl_reset" "$*"
}
# helper that prints out stack, error message and exits
die_ret() {
set +x
local ret n
ret=$1
shift
print -r - >&2 "${hl_fatal}Fatal$hl_reset error occurend in:"
for n in {${#funcfiletrace}..1}; do
printf >&2 '%d> %s (%s)\n' $n "$funcfiletrace[$n]" "$functrace[$n]"
done
printf >&2 '%s\n' "${hl_fatal}*$hl_reset $^@"
exit $ret
}
die() {
set +x
die_ret 1 "$@"
}
die100() { # 100: wrong usage
set +x
die_ret 100 "$@"
}
die111() { # 111: system call failed
set +x
die_ret 111 "$@"
}
-() { # Run command and die on nonzero exitcode
"$@" || die_ret $? "command failed with exitcode $?: ${(j: :)${(q)@}}"
}
# service builder
sv_start() {
(($+sv_name)) && \
die100 "sv_start: previous service definition ${(qqq)sv_name} wasn't ended properly"
(( $# != 1 )) && die100 "sv_start: incorrect arguments"
[[ $1 == */* ]] && die100 "sv_start: incorrect argument"
typeset -g sv_name=$1
typeset -g sv_dir=$build/$1
- mkdir -p $sv_dir
- touch $sv_dir/down
}
sv_end() {
(($+sv_name)) || die100 "$0: no service definition started"
(($+sv_dir)) || die100 "$0: inconsistent state for service ${(qqq)sv_name}"
(( $# != 0 )) && die100 "$0: incorrect arguments"
if ! [[ -L $scandir/$sv_name ]]; then
- s6-svlink -t 3000 $scandir $sv_dir
# - s6-mkfifodir $sv_dir/event
# - ln -s -T $sv_dir $scandir/$sv_name
# new_services+=( $scandir/$sv_name )
fi
unset sv_name sv_dir
}
sv_link_command() {
(($+sv_name)) || die100 "$0: no service definition started"
(($+sv_dir)) || die100 "$0: inconsistent state for service ${(qqq)sv_name}"
(( $# != 2 )) && die100 "$0: incorrect arguments"
local script_path=$sv_dir/$1
- ln -s -f -T ${commands[$2]} $script_path
}
sv_write_lines() {
(($+sv_name)) || die100 "$0: no service definition started"
(($+sv_dir)) || die100 "$0: inconsistent state for service ${(qqq)sv_name}"
(( $# < 2 )) && die100 "$0: incorrect arguments"
local file_path=$sv_dir/$1
shift
printf '%s\n' >$file_path "$@" || \
die111 "Error writing to ${(qqq)file_path}"
}
sv_el_script() {
(($+sv_name)) || die100 "$0: no service definition started"
(($+sv_dir)) || die100 "$0: inconsistent state for service ${(qqq)sv_name}"
- sv_write_lines $1 '#!/bin/execlineb -P' "$@[2,-1]"
- chmod +x $sv_dir/$1
}
sv_notification_fd() {
(($+sv_name)) || die100 "$0: no service definition started"
(($+sv_dir)) || die100 "$0: inconsistent state for service ${(qqq)sv_name}"
(( $# != 1 )) && die100 "$0: incorrect arguments"
- sv_write_lines notification-fd $1
}
sv_env() {
(($+sv_name)) || die100 "$0: no service definition started"
(($+sv_dir)) || die100 "$0: inconsistent state for service ${(qqq)sv_name}"
(( $# < 2 )) && die100 "$0: incorrect arguments"
- mkdir -p $sv_dir/env
local k v
for k v in "$@"; do
[[ $k == */* ]] && die100 "$0: invalid env variable ${(qqq)k}"
- sv_write_lines env/$k $v
done
}
sv_mkdir() {
(($+sv_name)) || die100 "$0: no service definition started"
(($+sv_dir)) || die100 "$0: inconsistent state for service ${(qqq)sv_name}"
(( $# < 1 )) && die100 "$0: incorrect arguments"
- mkdir -p $sv_dir/${^@}
}
# end service builder
sv_common_finish() {
local -a lines=(
'backtick -E up { s6-svstat -o up ../'${(qqq)sv_name_server}' }'
'backtick -E wantedup { s6-svstat -o wantedup ../'${(qqq)sv_name_server}' }'
## down this service if server is neither up nor wanted up
'if -n { if { $up } $wantedup }'
# 'ifelse { eltest true == $up -o true == $wantedup } { }'
's6-svc -d .'
)
- sv_el_script finish $lines
}
write_sv_xorg() {
- sv_start $sv_name_xorg
- sv_notification_fd 3
- sv_link_command run service.xpra-xorg.run
- sv_common_finish
- sv_env display_number $display
- sv_end
}
write_sv_server() {
- sv_start $sv_name_server
- sv_el_script run \
xpra-server $display --displayfd=3
- sv_notification_fd 3
- sv_end
}
write_sv_xclipring() {
local selection=$1
local name=xclipring-$selection.$display
- sv_start $name
- sv_el_script run \
"if { mkdir -p /run/xclipring/$selection/$display }" \
"in-xpra $display /usr/local/bin/xclipring" \
"-s ${selection:u}" \
"-c 32" \
"-d /run/xclipring/$selection/$display"
- sv_end
client_services+=( $name )
}
write_sv_client() {
- sv_start $sv_name_client
- sv_el_script run \
's6-envdir env' \
xpra-client "$display" \
"--title=\"[${container}] @title@ «s=@server-machine@ c=@client-machine@ d=$display w=@xid@\""
- sv_common_finish
- sv_env DISPLAY $DISPLAY
- sv_end
client_services+=( $sv_name_client )
}
write_sv_definitions() {
- write_sv_xorg
- write_sv_server
- write_sv_client
- write_sv_xclipring primary
- write_sv_xclipring clipboard
}
do_unlock() {
zsystem flock -u $lockfd
unset lockfd
}
start_services() {
- s6-svc -wU -T 5000 -o $scandir/$sv_name_server
local s
for s in $client_services; do
- s6-svc -o $scandir/$s
done
}
print_cookie() {
local -a cookies
# Obtain authentication cookie
cookies=( $(xauth -f /run/Xauthority.$display list :$display ) )
(( $#cookies == 3 )) || exit 1
[[ $cookies[2] == MIT-MAGIC-COOKIE-1 ]] || exit 1
printf '%s\t%s\n' $display $cookies[3]
}
main "$@"