diff --git a/plugins/available/sshagent.plugin.bash b/plugins/available/sshagent.plugin.bash new file mode 100644 index 00000000..14db47d4 --- /dev/null +++ b/plugins/available/sshagent.plugin.bash @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +cite about-plugin +about-plugin 'sshagent helper functions' + +function _get_sshagent_pid_from_env_file() { + local env_file="${1}" + [[ -r "${env_file}" ]] || { + echo ""; + return + } + tail -1 "${env_file}" \ + | cut -d' ' -f4 \ + | cut -d';' -f1 +} + +function _get_process_status_field() { + # uses /proc filesystem + local \ + pid \ + status_file \ + field + pid="${1}" + field="${2}" + status_file="/proc/${pid}/status" + if ! ([[ -d "${status_file%/*}" ]] \ + && [[ -r "${status_file}" ]]); then + echo ""; return; + fi + grep "${field}:" "${status_file}" \ + | cut -d':' -f2 \ + | sed -e 's/[[:space:]]\+//g' \ + | cut -d'(' -f1 +} + +function _is_item_in_list() { + local item + for item in "${@:1}"; do + if [[ "${item}" == "${1}" ]]; then + return 1 + fi + done + return 0 +} + + +function _is_proc_alive_at_pid() { + local \ + pid \ + expected_name \ + actual_name \ + actual_state + pid="${1?}" + expected_name="ssh-agent" + # we want to exclude: X (killed), T (traced), Z (zombie) + actual_name=$(_get_process_status_field "${pid}" "Name") + [[ "${expected_name}" == "${actual_name}" ]] || return 1 + actual_state=$(_get_process_status_field "${pid}" "State") + if _is_item_in_list "${actual_state}" "X" "T" "Z"; then + return 1 + fi + return 0 +} + + +function _ensure_valid_sshagent_env() { + local \ + agent_pid \ + tmp_res + + mkdir -p "${HOME}/.ssh" + type restorecon &> /dev/null + tmp_res="$?" + + if [[ "${tmp_res}" -eq 0 ]]; then + restorecon -rv "${HOME}/.ssh" + fi + + # no env file -> shoot a new agent + if ! [[ -r "${SSH_AGENT_ENV}" ]]; then + ssh-agent > "${SSH_AGENT_ENV}" + return + fi + + ## do not trust pre-existing SSH_AGENT_ENV + agent_pid=$(_get_sshagent_pid_from_env_file "${SSH_AGENT_ENV}") + if [[ -z "${agent_pid}" ]]; then + # no pid detected -> shoot a new agent + ssh-agent > "${SSH_AGENT_ENV}" + return + fi + + ## do not trust SSH_AGENT_PID + if _is_proc_alive_at_pid "${agent_pid}"; then + return + fi + + echo "There's a dead ssh-agent at the landing..." + ssh-agent > "${SSH_AGENT_ENV}" + return +} + + +function _ensure_sshagent_dead() { + [[ -r "${SSH_AGENT_ENV}" ]] \ + || return ## no agent file - no problems + ## ensure the file indeed points to a really running agent: + agent_pid=$( + _get_sshagent_pid_from_env_file \ + "${SSH_AGENT_ENV}" + ) + + [[ -n "${agent_pid}" ]] \ + || return # no pid - no problem + + _is_proc_alive_at_pid "${agent_pid}" \ + || return # process is not alive - no problem + + echo -e -n "Killing ssh-agent (pid:${agent_pid}) ... " + kill -9 "${agent_pid}" && echo "DONE" || echo "FAILED" + rm -f "${SSH_AGENT_ENV}" +} + + +function sshagent() { + about 'ensures ssh-agent is up and running' + param '1: on|off ' + example '$ sshagent on' + group 'ssh' + [[ -v SSH_AGENT_ENV ]] \ + || export SSH_AGENT_ENV="${HOME}/.ssh/agent_env.${HOSTNAME}" + + case "${1}" in + on) _ensure_valid_sshagent_env; + # shellcheck disable=SC1090 + source "${SSH_AGENT_ENV}" > /dev/null; + ;; + off) _ensure_sshagent_dead + ;; + *) + ;; + esac +} + +sshagent on