#!/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 "$@"