#!/bin/sh set -eu MAGIC='~/rc' progname= rc_fifo= res_fifo= sessions=',' info() { local fmt="${1}" shift 1 printf "%s: ${fmt}\n" "${progname}" "${@}" return 0 } error() { local fmt="${1}" shift 1 printf "%s: Error: ${fmt}\n" "${progname}" "${@}" 1>&2 return 0 } valid_session_type() { local session_type="${1}" shift 1 case "${session_type}" in *[!A-Za-z0-9:]* | '' | ':'* | *':') return 1 esac return 0 } run_services() { local old_sessions="${1}" local new_sessions="${1}" shift 1 local service_script= for service_script in "${XDG_CACHE_HOME:-${HOME}/.cache}/homerc/"*; do "$(dirname ${0})/svc" "${service_script##*/}" \ "${old_sessions};${new_sessions}" done } beg_session() { local session_type="${1}" shift 1 local s_type= local add= local new_sessions= local old_sessions= s_type="$(printf '%s' "${session_type}" | tr ':' '_')" if eval "[ \${sessions_${s_type}:-0} -eq 0 ]"; then add=true else add=false fi # Increment. eval "sessions_${s_type}=\$((\${sessions_${s_type}:-0} + 1))" if ${add}; then new_sessions="${sessions}${session_type}," old_sessions="${sessions}" sessions="${new_sessions}" run_services "${old_sessions}" "${new_sessions}" return ${?} fi return 0 } end_session() { local session_type="${1}" shift 1 local s_type= local new_sessions= local old_sessions= s_type="$(printf '%s' "${session_type}" | tr ':' '_')" if eval "[ \${sessions_${s_type}:-0} -eq 0 ]"; then error 'No running sessions of type %s' "${session_type}" return 1 fi # Decrement. eval "sessions_${s_type}=\$((\${sessions_${s_type}} - 1))" if eval "[ \${sessions_${s_type}} -eq 0 ]"; then eval "unset sessions_${s_type}" new_sessions="${sessions%,${session_type},*}" new_sessions="${new_sessions},${sessions#*,${session_type},}" old_sessions="${sessions}" sessions="${new_sessions}" run_services "${old_sessions}" "${new_sessions}" return ${?} fi return 0 } list_sessions() { local printed= local session= printed=false IFS=',' for session in ${sessions}; do unset IFS if ${printed}; then printf ', ' fi eval "printf '%s (%d)' '${session}' \${sessions_${session}}" printed=true done unset IFS } listen() { local magic= local cmd= local arg= while :; do if read -r magic cmd arg; then case "${magic}" in "${MAGIC}");; *) continue esac case "${cmd}" in 'beg') if valid_session_type "${arg}"; then beg_session "${arg}" fi ;; 'end') if valid_session_type "${arg}"; then end_session "${arg}" fi ;; 'who') { printf '%s ' "${MAGIC}" list_sessions printf '\n' } 1>"${arg}" ;; 'bye') break ;; *) continue ;; esac fi done 0<"${rc_fifo}" } run_daemon() { local session_type="${1}" shift 1 local piddir= exec 0<&- 1>&- 2>&- # TODO: # Disassociate from its process group (usually a shell), to insulate # itself from signals (such as HUP) sent to the process group # Ignore all terminal I/O signals piddir="${XDG_CACHE_HOME:-${HOME}/.cache}/homerc" mkdir -p "${piddir}" rm -f "${piddir}/"* beg_session "${session_type}" listen rm "${rc_fifo}" rm -f "${piddir}/"* return 0 } start() { local session_type="${1}" shift 1 if mkfifo -m 0600 "${rc_fifo}" 2>/dev/null; then run_daemon "${session_type}" & else # Signal the running rc daemon. printf "%s beg %s\n" "${MAGIC}" "${session_type}" \ 1>"${rc_fifo}" fi return 0 } stop() { local session_type="${1}" shift 1 if mkfifo -m 0600 "${rc_fifo}" 2>/dev/null; then # Daemonizing just to end a non-existent session makes no sense. error 'No running daemon found' rm "${rc_fifo}" return 1 else # Signal the running rc daemon. printf "%s end %s\n" "${MAGIC}" "${session_type}" \ 1>"${rc_fifo}" fi return 0 } status() { local magic= local list= if mkfifo -m 0600 "${rc_fifo}" 2>/dev/null; then # Daemonizing just to list non-existent sessions makes no sense. error 'No running daemon found' rm "${rc_fifo}" return 1 else # Establish response FIFO. if ! mkfifo -m 0600 "${res_fifo}" 2>/dev/null; then error 'Failed to communicate with running daemon' return 1 fi # Signal the running rc daemon. printf "%s who %s\n" "${MAGIC}" "${res_fifo}" \ 1>"${rc_fifo}" if read -r magic list; then case "${magic}" in "${MAGIC}");; *) continue esac info 'Sessions: %s' "${list}" else error 'Failed to communicate with running daemon' rm "${res_fifo}" return 1 fi fi return 0 } usage() { printf 'Usage: %s {start|stop} \n' "${0}" printf ' or: %s status\n' "${0}" return 0 } main() { local action= local session_type= if [ ${#} -ne 2 ]; then usage 1>&2 return 1 fi action="${1}" session_type="${2}" if ! valid_session_type; then error 'Invalid session type' return 1 fi progname="${0##*/}" rc_fifo="/tmp/homercrc-$(id -u)" res_fifo="/tmp/homercres-${$}" case "${action}" in 'start') start "${session_type}" ;; 'stop') stop "${session_type}" ;; 'status') status ;; *) usage 1>&2 return 1 ;; esac return 0 } main "${@}"