1eda14cbcSMatt Macy#!/bin/sh 2*e92ffd9bSMartin Matuska# shellcheck disable=SC2154,SC3043 3eda14cbcSMatt Macy# zed-functions.sh 4eda14cbcSMatt Macy# 5eda14cbcSMatt Macy# ZED helper functions for use in ZEDLETs 6eda14cbcSMatt Macy 7eda14cbcSMatt Macy 8eda14cbcSMatt Macy# Variable Defaults 9eda14cbcSMatt Macy# 10eda14cbcSMatt Macy: "${ZED_LOCKDIR:="/var/lock"}" 11eda14cbcSMatt Macy: "${ZED_NOTIFY_INTERVAL_SECS:=3600}" 12eda14cbcSMatt Macy: "${ZED_NOTIFY_VERBOSE:=0}" 13eda14cbcSMatt Macy: "${ZED_RUNDIR:="/var/run"}" 14eda14cbcSMatt Macy: "${ZED_SYSLOG_PRIORITY:="daemon.notice"}" 15eda14cbcSMatt Macy: "${ZED_SYSLOG_TAG:="zed"}" 16eda14cbcSMatt Macy 17eda14cbcSMatt MacyZED_FLOCK_FD=8 18eda14cbcSMatt Macy 19eda14cbcSMatt Macy 20eda14cbcSMatt Macy# zed_check_cmd (cmd, ...) 21eda14cbcSMatt Macy# 22eda14cbcSMatt Macy# For each argument given, search PATH for the executable command [cmd]. 23eda14cbcSMatt Macy# Log a message if [cmd] is not found. 24eda14cbcSMatt Macy# 25eda14cbcSMatt Macy# Arguments 26eda14cbcSMatt Macy# cmd: name of executable command for which to search 27eda14cbcSMatt Macy# 28eda14cbcSMatt Macy# Return 29eda14cbcSMatt Macy# 0 if all commands are found in PATH and are executable 30eda14cbcSMatt Macy# n for a count of the command executables that are not found 31eda14cbcSMatt Macy# 32eda14cbcSMatt Macyzed_check_cmd() 33eda14cbcSMatt Macy{ 34eda14cbcSMatt Macy local cmd 35eda14cbcSMatt Macy local rv=0 36eda14cbcSMatt Macy 37eda14cbcSMatt Macy for cmd; do 38eda14cbcSMatt Macy if ! command -v "${cmd}" >/dev/null 2>&1; then 39eda14cbcSMatt Macy zed_log_err "\"${cmd}\" not installed" 40eda14cbcSMatt Macy rv=$((rv + 1)) 41eda14cbcSMatt Macy fi 42eda14cbcSMatt Macy done 43eda14cbcSMatt Macy return "${rv}" 44eda14cbcSMatt Macy} 45eda14cbcSMatt Macy 46eda14cbcSMatt Macy 47eda14cbcSMatt Macy# zed_log_msg (msg, ...) 48eda14cbcSMatt Macy# 49eda14cbcSMatt Macy# Write all argument strings to the system log. 50eda14cbcSMatt Macy# 51eda14cbcSMatt Macy# Globals 52eda14cbcSMatt Macy# ZED_SYSLOG_PRIORITY 53eda14cbcSMatt Macy# ZED_SYSLOG_TAG 54eda14cbcSMatt Macy# 55eda14cbcSMatt Macy# Return 56eda14cbcSMatt Macy# nothing 57eda14cbcSMatt Macy# 58eda14cbcSMatt Macyzed_log_msg() 59eda14cbcSMatt Macy{ 60eda14cbcSMatt Macy logger -p "${ZED_SYSLOG_PRIORITY}" -t "${ZED_SYSLOG_TAG}" -- "$@" 61eda14cbcSMatt Macy} 62eda14cbcSMatt Macy 63eda14cbcSMatt Macy 64eda14cbcSMatt Macy# zed_log_err (msg, ...) 65eda14cbcSMatt Macy# 66eda14cbcSMatt Macy# Write an error message to the system log. This message will contain the 67eda14cbcSMatt Macy# script name, EID, and all argument strings. 68eda14cbcSMatt Macy# 69eda14cbcSMatt Macy# Globals 70eda14cbcSMatt Macy# ZED_SYSLOG_PRIORITY 71eda14cbcSMatt Macy# ZED_SYSLOG_TAG 72eda14cbcSMatt Macy# ZEVENT_EID 73eda14cbcSMatt Macy# 74eda14cbcSMatt Macy# Return 75eda14cbcSMatt Macy# nothing 76eda14cbcSMatt Macy# 77eda14cbcSMatt Macyzed_log_err() 78eda14cbcSMatt Macy{ 79eda14cbcSMatt Macy logger -p "${ZED_SYSLOG_PRIORITY}" -t "${ZED_SYSLOG_TAG}" -- "error:" \ 80dae17134SMartin Matuska "${0##*/}:""${ZEVENT_EID:+" eid=${ZEVENT_EID}:"}" "$@" 81eda14cbcSMatt Macy} 82eda14cbcSMatt Macy 83eda14cbcSMatt Macy 84eda14cbcSMatt Macy# zed_lock (lockfile, [fd]) 85eda14cbcSMatt Macy# 86eda14cbcSMatt Macy# Obtain an exclusive (write) lock on [lockfile]. If the lock cannot be 87eda14cbcSMatt Macy# immediately acquired, wait until it becomes available. 88eda14cbcSMatt Macy# 89eda14cbcSMatt Macy# Every zed_lock() must be paired with a corresponding zed_unlock(). 90eda14cbcSMatt Macy# 91eda14cbcSMatt Macy# By default, flock-style locks associate the lockfile with file descriptor 8. 92eda14cbcSMatt Macy# The bash manpage warns that file descriptors >9 should be used with care as 93eda14cbcSMatt Macy# they may conflict with file descriptors used internally by the shell. File 94eda14cbcSMatt Macy# descriptor 9 is reserved for zed_rate_limit(). If concurrent locks are held 95eda14cbcSMatt Macy# within the same process, they must use different file descriptors (preferably 96eda14cbcSMatt Macy# decrementing from 8); otherwise, obtaining a new lock with a given file 97eda14cbcSMatt Macy# descriptor will release the previous lock associated with that descriptor. 98eda14cbcSMatt Macy# 99eda14cbcSMatt Macy# Arguments 100eda14cbcSMatt Macy# lockfile: pathname of the lock file; the lock will be stored in 101eda14cbcSMatt Macy# ZED_LOCKDIR unless the pathname contains a "/". 102eda14cbcSMatt Macy# fd: integer for the file descriptor used by flock (OPTIONAL unless holding 103eda14cbcSMatt Macy# concurrent locks) 104eda14cbcSMatt Macy# 105eda14cbcSMatt Macy# Globals 106eda14cbcSMatt Macy# ZED_FLOCK_FD 107eda14cbcSMatt Macy# ZED_LOCKDIR 108eda14cbcSMatt Macy# 109eda14cbcSMatt Macy# Return 110eda14cbcSMatt Macy# nothing 111eda14cbcSMatt Macy# 112eda14cbcSMatt Macyzed_lock() 113eda14cbcSMatt Macy{ 114eda14cbcSMatt Macy local lockfile="$1" 115eda14cbcSMatt Macy local fd="${2:-${ZED_FLOCK_FD}}" 116eda14cbcSMatt Macy local umask_bak 117eda14cbcSMatt Macy local err 118eda14cbcSMatt Macy 119eda14cbcSMatt Macy [ -n "${lockfile}" ] || return 120eda14cbcSMatt Macy if ! expr "${lockfile}" : '.*/' >/dev/null 2>&1; then 121eda14cbcSMatt Macy lockfile="${ZED_LOCKDIR}/${lockfile}" 122eda14cbcSMatt Macy fi 123eda14cbcSMatt Macy 124eda14cbcSMatt Macy umask_bak="$(umask)" 125eda14cbcSMatt Macy umask 077 126eda14cbcSMatt Macy 127eda14cbcSMatt Macy # Obtain a lock on the file bound to the given file descriptor. 128eda14cbcSMatt Macy # 1293ff01b23SMartin Matuska eval "exec ${fd}>> '${lockfile}'" 13016038816SMartin Matuska if ! err="$(flock --exclusive "${fd}" 2>&1)"; then 131eda14cbcSMatt Macy zed_log_err "failed to lock \"${lockfile}\": ${err}" 132eda14cbcSMatt Macy fi 133eda14cbcSMatt Macy 134eda14cbcSMatt Macy umask "${umask_bak}" 135eda14cbcSMatt Macy} 136eda14cbcSMatt Macy 137eda14cbcSMatt Macy 138eda14cbcSMatt Macy# zed_unlock (lockfile, [fd]) 139eda14cbcSMatt Macy# 140eda14cbcSMatt Macy# Release the lock on [lockfile]. 141eda14cbcSMatt Macy# 142eda14cbcSMatt Macy# Arguments 143eda14cbcSMatt Macy# lockfile: pathname of the lock file 144eda14cbcSMatt Macy# fd: integer for the file descriptor used by flock (must match the file 145eda14cbcSMatt Macy# descriptor passed to the zed_lock function call) 146eda14cbcSMatt Macy# 147eda14cbcSMatt Macy# Globals 148eda14cbcSMatt Macy# ZED_FLOCK_FD 149eda14cbcSMatt Macy# ZED_LOCKDIR 150eda14cbcSMatt Macy# 151eda14cbcSMatt Macy# Return 152eda14cbcSMatt Macy# nothing 153eda14cbcSMatt Macy# 154eda14cbcSMatt Macyzed_unlock() 155eda14cbcSMatt Macy{ 156eda14cbcSMatt Macy local lockfile="$1" 157eda14cbcSMatt Macy local fd="${2:-${ZED_FLOCK_FD}}" 158eda14cbcSMatt Macy local err 159eda14cbcSMatt Macy 160eda14cbcSMatt Macy [ -n "${lockfile}" ] || return 161eda14cbcSMatt Macy if ! expr "${lockfile}" : '.*/' >/dev/null 2>&1; then 162eda14cbcSMatt Macy lockfile="${ZED_LOCKDIR}/${lockfile}" 163eda14cbcSMatt Macy fi 164eda14cbcSMatt Macy 165eda14cbcSMatt Macy # Release the lock and close the file descriptor. 16616038816SMartin Matuska if ! err="$(flock --unlock "${fd}" 2>&1)"; then 167eda14cbcSMatt Macy zed_log_err "failed to unlock \"${lockfile}\": ${err}" 168eda14cbcSMatt Macy fi 169eda14cbcSMatt Macy eval "exec ${fd}>&-" 170eda14cbcSMatt Macy} 171eda14cbcSMatt Macy 172eda14cbcSMatt Macy 173eda14cbcSMatt Macy# zed_notify (subject, pathname) 174eda14cbcSMatt Macy# 175eda14cbcSMatt Macy# Send a notification via all available methods. 176eda14cbcSMatt Macy# 177eda14cbcSMatt Macy# Arguments 178eda14cbcSMatt Macy# subject: notification subject 179eda14cbcSMatt Macy# pathname: pathname containing the notification message (OPTIONAL) 180eda14cbcSMatt Macy# 181eda14cbcSMatt Macy# Return 182eda14cbcSMatt Macy# 0: notification succeeded via at least one method 183eda14cbcSMatt Macy# 1: notification failed 184eda14cbcSMatt Macy# 2: no notification methods configured 185eda14cbcSMatt Macy# 186eda14cbcSMatt Macyzed_notify() 187eda14cbcSMatt Macy{ 188eda14cbcSMatt Macy local subject="$1" 189eda14cbcSMatt Macy local pathname="$2" 190eda14cbcSMatt Macy local num_success=0 191eda14cbcSMatt Macy local num_failure=0 192eda14cbcSMatt Macy 193eda14cbcSMatt Macy zed_notify_email "${subject}" "${pathname}"; rv=$? 194eda14cbcSMatt Macy [ "${rv}" -eq 0 ] && num_success=$((num_success + 1)) 195eda14cbcSMatt Macy [ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1)) 196eda14cbcSMatt Macy 197eda14cbcSMatt Macy zed_notify_pushbullet "${subject}" "${pathname}"; rv=$? 198eda14cbcSMatt Macy [ "${rv}" -eq 0 ] && num_success=$((num_success + 1)) 199eda14cbcSMatt Macy [ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1)) 200eda14cbcSMatt Macy 201eda14cbcSMatt Macy zed_notify_slack_webhook "${subject}" "${pathname}"; rv=$? 202eda14cbcSMatt Macy [ "${rv}" -eq 0 ] && num_success=$((num_success + 1)) 203eda14cbcSMatt Macy [ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1)) 204eda14cbcSMatt Macy 20516038816SMartin Matuska zed_notify_pushover "${subject}" "${pathname}"; rv=$? 20616038816SMartin Matuska [ "${rv}" -eq 0 ] && num_success=$((num_success + 1)) 20716038816SMartin Matuska [ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1)) 20816038816SMartin Matuska 209eda14cbcSMatt Macy [ "${num_success}" -gt 0 ] && return 0 210eda14cbcSMatt Macy [ "${num_failure}" -gt 0 ] && return 1 211eda14cbcSMatt Macy return 2 212eda14cbcSMatt Macy} 213eda14cbcSMatt Macy 214eda14cbcSMatt Macy 215eda14cbcSMatt Macy# zed_notify_email (subject, pathname) 216eda14cbcSMatt Macy# 217eda14cbcSMatt Macy# Send a notification via email to the address specified by ZED_EMAIL_ADDR. 218eda14cbcSMatt Macy# 219eda14cbcSMatt Macy# Requires the mail executable to be installed in the standard PATH, or 220eda14cbcSMatt Macy# ZED_EMAIL_PROG to be defined with the pathname of an executable capable of 221eda14cbcSMatt Macy# reading a message body from stdin. 222eda14cbcSMatt Macy# 223eda14cbcSMatt Macy# Command-line options to the mail executable can be specified in 224eda14cbcSMatt Macy# ZED_EMAIL_OPTS. This undergoes the following keyword substitutions: 225eda14cbcSMatt Macy# - @ADDRESS@ is replaced with the space-delimited recipient email address(es) 226eda14cbcSMatt Macy# - @SUBJECT@ is replaced with the notification subject 227eda14cbcSMatt Macy# 228eda14cbcSMatt Macy# Arguments 229eda14cbcSMatt Macy# subject: notification subject 230eda14cbcSMatt Macy# pathname: pathname containing the notification message (OPTIONAL) 231eda14cbcSMatt Macy# 232eda14cbcSMatt Macy# Globals 233eda14cbcSMatt Macy# ZED_EMAIL_PROG 234eda14cbcSMatt Macy# ZED_EMAIL_OPTS 235eda14cbcSMatt Macy# ZED_EMAIL_ADDR 236eda14cbcSMatt Macy# 237eda14cbcSMatt Macy# Return 238eda14cbcSMatt Macy# 0: notification sent 239eda14cbcSMatt Macy# 1: notification failed 240eda14cbcSMatt Macy# 2: not configured 241eda14cbcSMatt Macy# 242eda14cbcSMatt Macyzed_notify_email() 243eda14cbcSMatt Macy{ 244eda14cbcSMatt Macy local subject="$1" 245eda14cbcSMatt Macy local pathname="${2:-"/dev/null"}" 246eda14cbcSMatt Macy 247eda14cbcSMatt Macy : "${ZED_EMAIL_PROG:="mail"}" 248eda14cbcSMatt Macy : "${ZED_EMAIL_OPTS:="-s '@SUBJECT@' @ADDRESS@"}" 249eda14cbcSMatt Macy 250eda14cbcSMatt Macy # For backward compatibility with ZED_EMAIL. 251eda14cbcSMatt Macy if [ -n "${ZED_EMAIL}" ] && [ -z "${ZED_EMAIL_ADDR}" ]; then 252eda14cbcSMatt Macy ZED_EMAIL_ADDR="${ZED_EMAIL}" 253eda14cbcSMatt Macy fi 254eda14cbcSMatt Macy [ -n "${ZED_EMAIL_ADDR}" ] || return 2 255eda14cbcSMatt Macy 256eda14cbcSMatt Macy zed_check_cmd "${ZED_EMAIL_PROG}" || return 1 257eda14cbcSMatt Macy 258eda14cbcSMatt Macy [ -n "${subject}" ] || return 1 259eda14cbcSMatt Macy if [ ! -r "${pathname}" ]; then 260eda14cbcSMatt Macy zed_log_err \ 261dae17134SMartin Matuska "${ZED_EMAIL_PROG##*/} cannot read \"${pathname}\"" 262eda14cbcSMatt Macy return 1 263eda14cbcSMatt Macy fi 264eda14cbcSMatt Macy 265eda14cbcSMatt Macy ZED_EMAIL_OPTS="$(echo "${ZED_EMAIL_OPTS}" \ 266eda14cbcSMatt Macy | sed -e "s/@ADDRESS@/${ZED_EMAIL_ADDR}/g" \ 267eda14cbcSMatt Macy -e "s/@SUBJECT@/${subject}/g")" 268eda14cbcSMatt Macy 269*e92ffd9bSMartin Matuska # shellcheck disable=SC2086,SC2248 2702617128aSMartin Matuska eval ${ZED_EMAIL_PROG} ${ZED_EMAIL_OPTS} < "${pathname}" >/dev/null 2>&1 271eda14cbcSMatt Macy rv=$? 272eda14cbcSMatt Macy if [ "${rv}" -ne 0 ]; then 273dae17134SMartin Matuska zed_log_err "${ZED_EMAIL_PROG##*/} exit=${rv}" 274eda14cbcSMatt Macy return 1 275eda14cbcSMatt Macy fi 276eda14cbcSMatt Macy return 0 277eda14cbcSMatt Macy} 278eda14cbcSMatt Macy 279eda14cbcSMatt Macy 280eda14cbcSMatt Macy# zed_notify_pushbullet (subject, pathname) 281eda14cbcSMatt Macy# 282eda14cbcSMatt Macy# Send a notification via Pushbullet <https://www.pushbullet.com/>. 283eda14cbcSMatt Macy# The access token (ZED_PUSHBULLET_ACCESS_TOKEN) identifies this client to the 284eda14cbcSMatt Macy# Pushbullet server. The optional channel tag (ZED_PUSHBULLET_CHANNEL_TAG) is 285eda14cbcSMatt Macy# for pushing to notification feeds that can be subscribed to; if a channel is 286eda14cbcSMatt Macy# not defined, push notifications will instead be sent to all devices 287eda14cbcSMatt Macy# associated with the account specified by the access token. 288eda14cbcSMatt Macy# 289eda14cbcSMatt Macy# Requires awk, curl, and sed executables to be installed in the standard PATH. 290eda14cbcSMatt Macy# 291eda14cbcSMatt Macy# References 292eda14cbcSMatt Macy# https://docs.pushbullet.com/ 293eda14cbcSMatt Macy# https://www.pushbullet.com/security 294eda14cbcSMatt Macy# 295eda14cbcSMatt Macy# Arguments 296eda14cbcSMatt Macy# subject: notification subject 297eda14cbcSMatt Macy# pathname: pathname containing the notification message (OPTIONAL) 298eda14cbcSMatt Macy# 299eda14cbcSMatt Macy# Globals 300eda14cbcSMatt Macy# ZED_PUSHBULLET_ACCESS_TOKEN 301eda14cbcSMatt Macy# ZED_PUSHBULLET_CHANNEL_TAG 302eda14cbcSMatt Macy# 303eda14cbcSMatt Macy# Return 304eda14cbcSMatt Macy# 0: notification sent 305eda14cbcSMatt Macy# 1: notification failed 306eda14cbcSMatt Macy# 2: not configured 307eda14cbcSMatt Macy# 308eda14cbcSMatt Macyzed_notify_pushbullet() 309eda14cbcSMatt Macy{ 310eda14cbcSMatt Macy local subject="$1" 311eda14cbcSMatt Macy local pathname="${2:-"/dev/null"}" 312eda14cbcSMatt Macy local msg_body 313eda14cbcSMatt Macy local msg_tag 314eda14cbcSMatt Macy local msg_json 315eda14cbcSMatt Macy local msg_out 316eda14cbcSMatt Macy local msg_err 317eda14cbcSMatt Macy local url="https://api.pushbullet.com/v2/pushes" 318eda14cbcSMatt Macy 319eda14cbcSMatt Macy [ -n "${ZED_PUSHBULLET_ACCESS_TOKEN}" ] || return 2 320eda14cbcSMatt Macy 321eda14cbcSMatt Macy [ -n "${subject}" ] || return 1 322eda14cbcSMatt Macy if [ ! -r "${pathname}" ]; then 323eda14cbcSMatt Macy zed_log_err "pushbullet cannot read \"${pathname}\"" 324eda14cbcSMatt Macy return 1 325eda14cbcSMatt Macy fi 326eda14cbcSMatt Macy 327eda14cbcSMatt Macy zed_check_cmd "awk" "curl" "sed" || return 1 328eda14cbcSMatt Macy 329eda14cbcSMatt Macy # Escape the following characters in the message body for JSON: 330eda14cbcSMatt Macy # newline, backslash, double quote, horizontal tab, vertical tab, 331eda14cbcSMatt Macy # and carriage return. 332eda14cbcSMatt Macy # 333eda14cbcSMatt Macy msg_body="$(awk '{ ORS="\\n" } { gsub(/\\/, "\\\\"); gsub(/"/, "\\\""); 334eda14cbcSMatt Macy gsub(/\t/, "\\t"); gsub(/\f/, "\\f"); gsub(/\r/, "\\r"); print }' \ 335eda14cbcSMatt Macy "${pathname}")" 336eda14cbcSMatt Macy 337eda14cbcSMatt Macy # Push to a channel if one is configured. 338eda14cbcSMatt Macy # 339eda14cbcSMatt Macy [ -n "${ZED_PUSHBULLET_CHANNEL_TAG}" ] && msg_tag="$(printf \ 340eda14cbcSMatt Macy '"channel_tag": "%s", ' "${ZED_PUSHBULLET_CHANNEL_TAG}")" 341eda14cbcSMatt Macy 342eda14cbcSMatt Macy # Construct the JSON message for pushing a note. 343eda14cbcSMatt Macy # 344eda14cbcSMatt Macy msg_json="$(printf '{%s"type": "note", "title": "%s", "body": "%s"}' \ 345eda14cbcSMatt Macy "${msg_tag}" "${subject}" "${msg_body}")" 346eda14cbcSMatt Macy 347eda14cbcSMatt Macy # Send the POST request and check for errors. 348eda14cbcSMatt Macy # 349eda14cbcSMatt Macy msg_out="$(curl -u "${ZED_PUSHBULLET_ACCESS_TOKEN}:" -X POST "${url}" \ 350eda14cbcSMatt Macy --header "Content-Type: application/json" --data-binary "${msg_json}" \ 351eda14cbcSMatt Macy 2>/dev/null)"; rv=$? 352eda14cbcSMatt Macy if [ "${rv}" -ne 0 ]; then 353eda14cbcSMatt Macy zed_log_err "curl exit=${rv}" 354eda14cbcSMatt Macy return 1 355eda14cbcSMatt Macy fi 356eda14cbcSMatt Macy msg_err="$(echo "${msg_out}" \ 357eda14cbcSMatt Macy | sed -n -e 's/.*"error" *:.*"message" *: *"\([^"]*\)".*/\1/p')" 358eda14cbcSMatt Macy if [ -n "${msg_err}" ]; then 359eda14cbcSMatt Macy zed_log_err "pushbullet \"${msg_err}"\" 360eda14cbcSMatt Macy return 1 361eda14cbcSMatt Macy fi 362eda14cbcSMatt Macy return 0 363eda14cbcSMatt Macy} 364eda14cbcSMatt Macy 365eda14cbcSMatt Macy 366eda14cbcSMatt Macy# zed_notify_slack_webhook (subject, pathname) 367eda14cbcSMatt Macy# 368eda14cbcSMatt Macy# Notification via Slack Webhook <https://api.slack.com/incoming-webhooks>. 369eda14cbcSMatt Macy# The Webhook URL (ZED_SLACK_WEBHOOK_URL) identifies this client to the 370eda14cbcSMatt Macy# Slack channel. 371eda14cbcSMatt Macy# 372eda14cbcSMatt Macy# Requires awk, curl, and sed executables to be installed in the standard PATH. 373eda14cbcSMatt Macy# 374eda14cbcSMatt Macy# References 375eda14cbcSMatt Macy# https://api.slack.com/incoming-webhooks 376eda14cbcSMatt Macy# 377eda14cbcSMatt Macy# Arguments 378eda14cbcSMatt Macy# subject: notification subject 379eda14cbcSMatt Macy# pathname: pathname containing the notification message (OPTIONAL) 380eda14cbcSMatt Macy# 381eda14cbcSMatt Macy# Globals 382eda14cbcSMatt Macy# ZED_SLACK_WEBHOOK_URL 383eda14cbcSMatt Macy# 384eda14cbcSMatt Macy# Return 385eda14cbcSMatt Macy# 0: notification sent 386eda14cbcSMatt Macy# 1: notification failed 387eda14cbcSMatt Macy# 2: not configured 388eda14cbcSMatt Macy# 389eda14cbcSMatt Macyzed_notify_slack_webhook() 390eda14cbcSMatt Macy{ 391eda14cbcSMatt Macy [ -n "${ZED_SLACK_WEBHOOK_URL}" ] || return 2 392eda14cbcSMatt Macy 393eda14cbcSMatt Macy local subject="$1" 394eda14cbcSMatt Macy local pathname="${2:-"/dev/null"}" 395eda14cbcSMatt Macy local msg_body 396eda14cbcSMatt Macy local msg_tag 397eda14cbcSMatt Macy local msg_json 398eda14cbcSMatt Macy local msg_out 399eda14cbcSMatt Macy local msg_err 400eda14cbcSMatt Macy local url="${ZED_SLACK_WEBHOOK_URL}" 401eda14cbcSMatt Macy 402eda14cbcSMatt Macy [ -n "${subject}" ] || return 1 403eda14cbcSMatt Macy if [ ! -r "${pathname}" ]; then 404eda14cbcSMatt Macy zed_log_err "slack webhook cannot read \"${pathname}\"" 405eda14cbcSMatt Macy return 1 406eda14cbcSMatt Macy fi 407eda14cbcSMatt Macy 408eda14cbcSMatt Macy zed_check_cmd "awk" "curl" "sed" || return 1 409eda14cbcSMatt Macy 410eda14cbcSMatt Macy # Escape the following characters in the message body for JSON: 411eda14cbcSMatt Macy # newline, backslash, double quote, horizontal tab, vertical tab, 412eda14cbcSMatt Macy # and carriage return. 413eda14cbcSMatt Macy # 414eda14cbcSMatt Macy msg_body="$(awk '{ ORS="\\n" } { gsub(/\\/, "\\\\"); gsub(/"/, "\\\""); 415eda14cbcSMatt Macy gsub(/\t/, "\\t"); gsub(/\f/, "\\f"); gsub(/\r/, "\\r"); print }' \ 416eda14cbcSMatt Macy "${pathname}")" 417eda14cbcSMatt Macy 418eda14cbcSMatt Macy # Construct the JSON message for posting. 419eda14cbcSMatt Macy # 420eda14cbcSMatt Macy msg_json="$(printf '{"text": "*%s*\n%s"}' "${subject}" "${msg_body}" )" 421eda14cbcSMatt Macy 422eda14cbcSMatt Macy # Send the POST request and check for errors. 423eda14cbcSMatt Macy # 424eda14cbcSMatt Macy msg_out="$(curl -X POST "${url}" \ 425eda14cbcSMatt Macy --header "Content-Type: application/json" --data-binary "${msg_json}" \ 426eda14cbcSMatt Macy 2>/dev/null)"; rv=$? 427eda14cbcSMatt Macy if [ "${rv}" -ne 0 ]; then 428eda14cbcSMatt Macy zed_log_err "curl exit=${rv}" 429eda14cbcSMatt Macy return 1 430eda14cbcSMatt Macy fi 431eda14cbcSMatt Macy msg_err="$(echo "${msg_out}" \ 432eda14cbcSMatt Macy | sed -n -e 's/.*"error" *:.*"message" *: *"\([^"]*\)".*/\1/p')" 433eda14cbcSMatt Macy if [ -n "${msg_err}" ]; then 434eda14cbcSMatt Macy zed_log_err "slack webhook \"${msg_err}"\" 435eda14cbcSMatt Macy return 1 436eda14cbcSMatt Macy fi 437eda14cbcSMatt Macy return 0 438eda14cbcSMatt Macy} 439eda14cbcSMatt Macy 44016038816SMartin Matuska# zed_notify_pushover (subject, pathname) 44116038816SMartin Matuska# 44216038816SMartin Matuska# Send a notification via Pushover <https://pushover.net/>. 44316038816SMartin Matuska# The access token (ZED_PUSHOVER_TOKEN) identifies this client to the 44416038816SMartin Matuska# Pushover server. The user token (ZED_PUSHOVER_USER) defines the user or 44516038816SMartin Matuska# group to which the notification will be sent. 44616038816SMartin Matuska# 44716038816SMartin Matuska# Requires curl and sed executables to be installed in the standard PATH. 44816038816SMartin Matuska# 44916038816SMartin Matuska# References 45016038816SMartin Matuska# https://pushover.net/api 45116038816SMartin Matuska# 45216038816SMartin Matuska# Arguments 45316038816SMartin Matuska# subject: notification subject 45416038816SMartin Matuska# pathname: pathname containing the notification message (OPTIONAL) 45516038816SMartin Matuska# 45616038816SMartin Matuska# Globals 45716038816SMartin Matuska# ZED_PUSHOVER_TOKEN 45816038816SMartin Matuska# ZED_PUSHOVER_USER 45916038816SMartin Matuska# 46016038816SMartin Matuska# Return 46116038816SMartin Matuska# 0: notification sent 46216038816SMartin Matuska# 1: notification failed 46316038816SMartin Matuska# 2: not configured 46416038816SMartin Matuska# 46516038816SMartin Matuskazed_notify_pushover() 46616038816SMartin Matuska{ 46716038816SMartin Matuska local subject="$1" 46816038816SMartin Matuska local pathname="${2:-"/dev/null"}" 46916038816SMartin Matuska local msg_body 47016038816SMartin Matuska local msg_out 47116038816SMartin Matuska local msg_err 47216038816SMartin Matuska local url="https://api.pushover.net/1/messages.json" 47316038816SMartin Matuska 47416038816SMartin Matuska [ -n "${ZED_PUSHOVER_TOKEN}" ] && [ -n "${ZED_PUSHOVER_USER}" ] || return 2 47516038816SMartin Matuska 47616038816SMartin Matuska if [ ! -r "${pathname}" ]; then 47716038816SMartin Matuska zed_log_err "pushover cannot read \"${pathname}\"" 47816038816SMartin Matuska return 1 47916038816SMartin Matuska fi 48016038816SMartin Matuska 48116038816SMartin Matuska zed_check_cmd "curl" "sed" || return 1 48216038816SMartin Matuska 48316038816SMartin Matuska # Read the message body in. 48416038816SMartin Matuska # 48516038816SMartin Matuska msg_body="$(cat "${pathname}")" 48616038816SMartin Matuska 48716038816SMartin Matuska if [ -z "${msg_body}" ] 48816038816SMartin Matuska then 48916038816SMartin Matuska msg_body=$subject 49016038816SMartin Matuska subject="" 49116038816SMartin Matuska fi 49216038816SMartin Matuska 49316038816SMartin Matuska # Send the POST request and check for errors. 49416038816SMartin Matuska # 49516038816SMartin Matuska msg_out="$( \ 49616038816SMartin Matuska curl \ 49716038816SMartin Matuska --form-string "token=${ZED_PUSHOVER_TOKEN}" \ 49816038816SMartin Matuska --form-string "user=${ZED_PUSHOVER_USER}" \ 49916038816SMartin Matuska --form-string "message=${msg_body}" \ 50016038816SMartin Matuska --form-string "title=${subject}" \ 50116038816SMartin Matuska "${url}" \ 50216038816SMartin Matuska 2>/dev/null \ 50316038816SMartin Matuska )"; rv=$? 50416038816SMartin Matuska if [ "${rv}" -ne 0 ]; then 50516038816SMartin Matuska zed_log_err "curl exit=${rv}" 50616038816SMartin Matuska return 1 50716038816SMartin Matuska fi 50816038816SMartin Matuska msg_err="$(echo "${msg_out}" \ 50916038816SMartin Matuska | sed -n -e 's/.*"errors" *:.*\[\(.*\)\].*/\1/p')" 51016038816SMartin Matuska if [ -n "${msg_err}" ]; then 51116038816SMartin Matuska zed_log_err "pushover \"${msg_err}"\" 51216038816SMartin Matuska return 1 51316038816SMartin Matuska fi 51416038816SMartin Matuska return 0 51516038816SMartin Matuska} 51616038816SMartin Matuska 51716038816SMartin Matuska 518eda14cbcSMatt Macy# zed_rate_limit (tag, [interval]) 519eda14cbcSMatt Macy# 520eda14cbcSMatt Macy# Check whether an event of a given type [tag] has already occurred within the 521eda14cbcSMatt Macy# last [interval] seconds. 522eda14cbcSMatt Macy# 523eda14cbcSMatt Macy# This function obtains a lock on the statefile using file descriptor 9. 524eda14cbcSMatt Macy# 525eda14cbcSMatt Macy# Arguments 526eda14cbcSMatt Macy# tag: arbitrary string for grouping related events to rate-limit 527eda14cbcSMatt Macy# interval: time interval in seconds (OPTIONAL) 528eda14cbcSMatt Macy# 529eda14cbcSMatt Macy# Globals 530eda14cbcSMatt Macy# ZED_NOTIFY_INTERVAL_SECS 531eda14cbcSMatt Macy# ZED_RUNDIR 532eda14cbcSMatt Macy# 533eda14cbcSMatt Macy# Return 534eda14cbcSMatt Macy# 0 if the event should be processed 535eda14cbcSMatt Macy# 1 if the event should be dropped 536eda14cbcSMatt Macy# 537eda14cbcSMatt Macy# State File Format 538eda14cbcSMatt Macy# time;tag 539eda14cbcSMatt Macy# 540eda14cbcSMatt Macyzed_rate_limit() 541eda14cbcSMatt Macy{ 542eda14cbcSMatt Macy local tag="$1" 543eda14cbcSMatt Macy local interval="${2:-${ZED_NOTIFY_INTERVAL_SECS}}" 544eda14cbcSMatt Macy local lockfile="zed.zedlet.state.lock" 545eda14cbcSMatt Macy local lockfile_fd=9 546eda14cbcSMatt Macy local statefile="${ZED_RUNDIR}/zed.zedlet.state" 547eda14cbcSMatt Macy local time_now 548eda14cbcSMatt Macy local time_prev 549eda14cbcSMatt Macy local umask_bak 550eda14cbcSMatt Macy local rv=0 551eda14cbcSMatt Macy 552eda14cbcSMatt Macy [ -n "${tag}" ] || return 0 553eda14cbcSMatt Macy 554eda14cbcSMatt Macy zed_lock "${lockfile}" "${lockfile_fd}" 555eda14cbcSMatt Macy time_now="$(date +%s)" 556eda14cbcSMatt Macy time_prev="$(grep -E "^[0-9]+;${tag}\$" "${statefile}" 2>/dev/null \ 557eda14cbcSMatt Macy | tail -1 | cut -d\; -f1)" 558eda14cbcSMatt Macy 559eda14cbcSMatt Macy if [ -n "${time_prev}" ] \ 560eda14cbcSMatt Macy && [ "$((time_now - time_prev))" -lt "${interval}" ]; then 561eda14cbcSMatt Macy rv=1 562eda14cbcSMatt Macy else 563eda14cbcSMatt Macy umask_bak="$(umask)" 564eda14cbcSMatt Macy umask 077 565eda14cbcSMatt Macy grep -E -v "^[0-9]+;${tag}\$" "${statefile}" 2>/dev/null \ 566eda14cbcSMatt Macy > "${statefile}.$$" 567eda14cbcSMatt Macy echo "${time_now};${tag}" >> "${statefile}.$$" 568eda14cbcSMatt Macy mv -f "${statefile}.$$" "${statefile}" 569eda14cbcSMatt Macy umask "${umask_bak}" 570eda14cbcSMatt Macy fi 571eda14cbcSMatt Macy 572eda14cbcSMatt Macy zed_unlock "${lockfile}" "${lockfile_fd}" 573eda14cbcSMatt Macy return "${rv}" 574eda14cbcSMatt Macy} 575eda14cbcSMatt Macy 576eda14cbcSMatt Macy 577eda14cbcSMatt Macy# zed_guid_to_pool (guid) 578eda14cbcSMatt Macy# 579eda14cbcSMatt Macy# Convert a pool GUID into its pool name (like "tank") 580eda14cbcSMatt Macy# Arguments 581eda14cbcSMatt Macy# guid: pool GUID (decimal or hex) 582eda14cbcSMatt Macy# 583eda14cbcSMatt Macy# Return 584eda14cbcSMatt Macy# Pool name 585eda14cbcSMatt Macy# 586eda14cbcSMatt Macyzed_guid_to_pool() 587eda14cbcSMatt Macy{ 588eda14cbcSMatt Macy if [ -z "$1" ] ; then 589eda14cbcSMatt Macy return 590eda14cbcSMatt Macy fi 591eda14cbcSMatt Macy 59216038816SMartin Matuska guid="$(printf "%u" "$1")" 59316038816SMartin Matuska $ZPOOL get -H -ovalue,name guid | awk '$1 == '"$guid"' {print $2; exit}' 594eda14cbcSMatt Macy} 595eda14cbcSMatt Macy 596eda14cbcSMatt Macy# zed_exit_if_ignoring_this_event 597eda14cbcSMatt Macy# 598eda14cbcSMatt Macy# Exit the script if we should ignore this event, as determined by 599eda14cbcSMatt Macy# $ZED_SYSLOG_SUBCLASS_INCLUDE and $ZED_SYSLOG_SUBCLASS_EXCLUDE in zed.rc. 600eda14cbcSMatt Macy# This function assumes you've imported the normal zed variables. 601eda14cbcSMatt Macyzed_exit_if_ignoring_this_event() 602eda14cbcSMatt Macy{ 603eda14cbcSMatt Macy if [ -n "${ZED_SYSLOG_SUBCLASS_INCLUDE}" ]; then 604eda14cbcSMatt Macy eval "case ${ZEVENT_SUBCLASS} in 605eda14cbcSMatt Macy ${ZED_SYSLOG_SUBCLASS_INCLUDE});; 606eda14cbcSMatt Macy *) exit 0;; 607eda14cbcSMatt Macy esac" 608eda14cbcSMatt Macy elif [ -n "${ZED_SYSLOG_SUBCLASS_EXCLUDE}" ]; then 609eda14cbcSMatt Macy eval "case ${ZEVENT_SUBCLASS} in 610eda14cbcSMatt Macy ${ZED_SYSLOG_SUBCLASS_EXCLUDE}) exit 0;; 611eda14cbcSMatt Macy *);; 612eda14cbcSMatt Macy esac" 613eda14cbcSMatt Macy fi 614eda14cbcSMatt Macy} 615