#!/usr/bin/env bash # exit if required variables are not set set -o nounset # interpret all arguments as the command to be run command_to_run="$*" # set timezone for error time stamps timezone="America/Chicago" # set log file path log_directory="${HOME}/logs/errwrapper/$(TZ="${timezone}" date +%Y-%m-%d)" log_name="$(TZ="${timezone}" date +%H%M%S.%N)_$(basename "${1}" ".sh").log" log_file="${log_directory}/${log_name}" # configure mailer mail_from="" mail_auth="" mail_to="" mail_server="" mail_port="" mail_password="$(cat "${HOME}/.mailauth")" # configure pid file location pid_directory="${HOME}/pids" pid_file="${pid_directory}/$(basename "${1}" ".sh").pid" # commands required for script execution required_commands="swaks twilio.sh" # configure sms sms_to="" function check_dependencies() { local missing_counter missing_counter=0 for command in ${required_commands}; do if ! hash "${command}" >/dev/null 2>&1; then printf "Command not found in PATH: %s\n" "${command}" >&2 ((missing_counter++)) fi done if ((missing_counter > 0)); then printf "%d or more commands are missing from PATH. Exiting.\n" "${missing_counter}" >&2 exit 1 fi } function check_pid() { if [ ! -d "${pid_directory}" ]; then mkdir -p "${pid_directory}" fi if [ -f "${pid_file}" ]; then echo "Another instance seems to already be running. Please kill it before running again." exit 1 else if ! { echo $$ > "${pid_file}"; } 2>/dev/null; then echo "Specified pid file location ${pid_file} is not writable. Exiting." exit 1 fi fi } function cleanup() { if ! rm "${pid_file}"; then echo "Cleanup failed. You may need to manually remove ${pid_file}." fi } function log_command() { if [ ! -d "${log_directory}" ]; then if ! mkdir -p "${log_directory}"; then echo "Failed to create logging directory ${log_directory}. Exiting." exit 1 fi fi { # log the command invocation echo -e "### Command: ${command_to_run} ###\n" # log the start time echo -e "### Started at $(TZ="${timezone}" date +%r) on $(TZ="${timezone}" date +%F) ###\n" # log the command output ( ${command_to_run} ) # store the exit code return_code="$?" # log the stop time echo "### Stopped at $(TZ="${timezone}" date +%r) on $(TZ="${timezone}" date +%F) ###" } >> "${log_file}" # return the exit code of the subcommand return "${return_code}" } function send_mail() { swaks \ --body - \ --to "${mail_to}" \ --header "Subject: ${subject}" \ --from "${mail_from}" \ --server "${mail_server}:${mail_port}" \ --auth LOGIN \ --auth-user "${mail_auth}" \ --auth-password "${mail_password}" \ -tlsc \ >/dev/null 2>&1 \ < "${log_file}" } function send_sms() { echo "${subject}" | twilio.sh "${sms_to}" >/dev/null 2>&1 } function set_path() { if [ -d "${HOME}/bin" ] ; then PATH="$HOME/bin:$PATH" else echo "Private bin directory not found." exit 1 fi } function main() { # set path for running in cron set_path # check for required script dependencies check_dependencies # check if another instance of the script is running, or didn't exit cleanly check_pid # clean up pid file on exit trap cleanup EXIT # run the command passed as an argument, logging output log_command # store the exit code return_code="$?" # do nothing if the previous command succeeded if [ "${return_code}" -eq 0 ]; then exit 0 fi # set the subject for the email and the contents of the sms subject="\`${command_to_run}\` failed on $(hostname) at $(TZ="${timezone}" date +%Y/%m/%d-%H:%M:%S)" # send an email containing the command name, time, and output send_mail # send an sms containing the command name and time send_sms # exit with the return code of the above command exit "${return_code}" } # run the script main