#!/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="${2}" shift 2 local ret= local service_script= ret=0 for service_script in "${XDG_CONFIG_HOME:-${HOME}/.config}/homerc/"*; do "$(dirname "${0}")/svc" "${service_script##*/}" \ "${old_sessions};${new_sessions}" || ret=1 done return ${ret} } 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 1 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 # No running sessions of specified 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 1 fi return 0 } list_sessions() { local printed= local session= local s_type= printed=false IFS=',' for session in ${sessions}; do unset IFS case "${session}" in '') continue;; esac s_type="$(printf '%s' "${session}" | tr ':' '_')" if ${printed}; then printf ', ' fi eval "printf '%s (%d)' \"\${session}\" \${sessions_${s_type}}" printed=true done unset IFS return 0 } listen() { local magic= local cmd= local arg= while :; do if read -r magic cmd arg 0<"${rc_fifo}"; then case "${magic}" in "${MAGIC}");; *) continue;; esac case "${cmd}" in 'beg' | 'end') if valid_session_type "${arg}"; then "${cmd}_session" "${arg}" || : fi ;; 'who') { printf '%s ' "${MAGIC}" list_sessions printf '\n' } 1>"${arg}" || : ;; 'bye') break ;; *) continue ;; esac fi done return 0 } run_daemon() { local session_type="${1}" shift 1 local cachedir= cachedir="${XDG_CACHE_HOME:-${HOME}/.cache}/homerc" mkdir -p "${cachedir}/run" exec 0<&- exec 1>>"${cachedir}/rc.log" 2>&1 trap '' HUP beg_session "${session_type}" listen rm "${rc_fifo}" 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 0<"${res_fifo}" && \ [ "x${magic}" = "x${MAGIC}" ]; then info 'Sessions: %s' "${list}" rm "${res_fifo}" else error 'Failed to communicate with running daemon' rm "${res_fifo}" return 1 fi fi return 0 } quit() { if mkfifo -m 0600 "${rc_fifo}" 2>/dev/null; then # Daemonizing just to quit makes no sense. error 'No running daemon found' rm "${rc_fifo}" return 1 else # Signal the running rc daemon. printf '%s bye\n' "${MAGIC}" 1>"${rc_fifo}" fi return 0 } usage() { printf 'Usage: %s {start|stop} \n' "${0}" printf ' or: %s {status|quit}\n\n' "${0}" printf ' is one or more strings of alphanumeric ' printf 'characters\nseparated by colons.\n' return 0 } main() { local action="${1-}" local session_type="${2-}" progname="${0##*/}" rc_fifo="/tmp/homercrc-$(id -u)" res_fifo="/tmp/homercres-${$}" case "${action}" in 'start' | 'stop') if [ ${#} -ne 2 ] || ! valid_session_type \ "${session_type}"; then usage 1>&2 return 1 fi "${action}" "${session_type}" ;; 'status') if [ ${#} -ne 1 ]; then usage 1>&2 return 1 fi status ;; 'quit') if [ ${#} -ne 1 ]; then usage 1>&2 return 1 fi quit ;; *) usage 1>&2 return 1 ;; esac return 0 } main "${@}"