#!/bin/zsh
# vim: sts=2 sw=2 et
setopt no_unset warn_create_global
zmodload zsh/system || exit $?
typeset -g scandir=/run/service
typeset -g build=/run/urxvt.named.service
main() {
local container name arg cmd sock s
local -a quoted
container=$1
[[ $container == */* ]] && exit 100
name=$2
[[ $name == */* ]] && exit 100
shift 2 || exit 100
sock=/run/inbox/$container/run/exec/exec
s=urxvt.for-$container.named-$name
- sv_start $s
- sv_env EXEC_PATH $sock
- sv_env TERM_NAME ${container//./:}
for arg in "$@"; do
quoted+=( "$(s6-quote -- "$arg")" )
done
local -a el_cmd=(
's6-envdir env'
'multisubstitute {'
' importas -i -u EXEC_PATH EXEC_PATH'
' importas -i -u TERM_NAME TERM_NAME'
'}'
'/mnt/ns/bin/socketpair 3 1'
'background -d {'
' fdclose 9'
' fdclose 3'
' s6-sudo $EXEC_PATH'
' /mnt/ns/bin/spawn-pty rxvt-unicode-256color {'
" ${(j: :)quoted}"
' } /mnt/ns/bin/fdsend 1 0 true'
'}'
'fdmove -c 1 2'
'/mnt/ns/bin/fdrecvto 3 4'
'fdclose 3'
'backtick -E PTS_NAME { /mnt/ns/bin/ptsname 4 }'
'if { fdmove 1 9 printf %s\n ${TERM_NAME}:${PTS_NAME} }'
'fdclose 9'
'urxvt -name ${TERM_NAME}:${PTS_NAME} -pty-fd 4'
)
- sv_el_script run $el_cmd
- sv_notification_fd 9
- sv_end
s6-sudo /run/cmd.s link $container xsession.${DISPLAY#:}.$USER run/exec/exec ||
die "Failed to link ${(qqq)container} exec socket."
- s6-svc -wU -T 5000 -o $scandir/$s
}
typeset -f -t main
### 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
main "$@"