1#!/bin/sh 2# shellcheck disable=SC2154,SC3043 3# zed-functions.sh 4# 5# ZED helper functions for use in ZEDLETs 6 7 8# Variable Defaults 9# 10: "${ZED_LOCKDIR:="/var/lock"}" 11: "${ZED_NOTIFY_INTERVAL_SECS:=3600}" 12: "${ZED_NOTIFY_VERBOSE:=0}" 13: "${ZED_RUNDIR:="/var/run"}" 14: "${ZED_SYSLOG_PRIORITY:="daemon.notice"}" 15: "${ZED_SYSLOG_TAG:="zed"}" 16 17ZED_FLOCK_FD=8 18 19 20# zed_check_cmd (cmd, ...) 21# 22# For each argument given, search PATH for the executable command [cmd]. 23# Log a message if [cmd] is not found. 24# 25# Arguments 26# cmd: name of executable command for which to search 27# 28# Return 29# 0 if all commands are found in PATH and are executable 30# n for a count of the command executables that are not found 31# 32zed_check_cmd() 33{ 34 local cmd 35 local rv=0 36 37 for cmd; do 38 if ! command -v "${cmd}" >/dev/null 2>&1; then 39 zed_log_err "\"${cmd}\" not installed" 40 rv=$((rv + 1)) 41 fi 42 done 43 return "${rv}" 44} 45 46 47# zed_log_msg (msg, ...) 48# 49# Write all argument strings to the system log. 50# 51# Globals 52# ZED_SYSLOG_PRIORITY 53# ZED_SYSLOG_TAG 54# 55# Return 56# nothing 57# 58zed_log_msg() 59{ 60 logger -p "${ZED_SYSLOG_PRIORITY}" -t "${ZED_SYSLOG_TAG}" -- "$@" 61} 62 63 64# zed_log_err (msg, ...) 65# 66# Write an error message to the system log. This message will contain the 67# script name, EID, and all argument strings. 68# 69# Globals 70# ZED_SYSLOG_PRIORITY 71# ZED_SYSLOG_TAG 72# ZEVENT_EID 73# 74# Return 75# nothing 76# 77zed_log_err() 78{ 79 zed_log_msg "error: ${0##*/}:""${ZEVENT_EID:+" eid=${ZEVENT_EID}:"}" "$@" 80} 81 82 83# zed_lock (lockfile, [fd]) 84# 85# Obtain an exclusive (write) lock on [lockfile]. If the lock cannot be 86# immediately acquired, wait until it becomes available. 87# 88# Every zed_lock() must be paired with a corresponding zed_unlock(). 89# 90# By default, flock-style locks associate the lockfile with file descriptor 8. 91# The bash manpage warns that file descriptors >9 should be used with care as 92# they may conflict with file descriptors used internally by the shell. File 93# descriptor 9 is reserved for zed_rate_limit(). If concurrent locks are held 94# within the same process, they must use different file descriptors (preferably 95# decrementing from 8); otherwise, obtaining a new lock with a given file 96# descriptor will release the previous lock associated with that descriptor. 97# 98# Arguments 99# lockfile: pathname of the lock file; the lock will be stored in 100# ZED_LOCKDIR unless the pathname contains a "/". 101# fd: integer for the file descriptor used by flock (OPTIONAL unless holding 102# concurrent locks) 103# 104# Globals 105# ZED_FLOCK_FD 106# ZED_LOCKDIR 107# 108# Return 109# nothing 110# 111zed_lock() 112{ 113 local lockfile="$1" 114 local fd="${2:-${ZED_FLOCK_FD}}" 115 local umask_bak 116 local err 117 118 [ -n "${lockfile}" ] || return 119 if ! expr "${lockfile}" : '.*/' >/dev/null 2>&1; then 120 lockfile="${ZED_LOCKDIR}/${lockfile}" 121 fi 122 123 umask_bak="$(umask)" 124 umask 077 125 126 # Obtain a lock on the file bound to the given file descriptor. 127 # 128 eval "exec ${fd}>> '${lockfile}'" 129 if ! err="$(flock --exclusive "${fd}" 2>&1)"; then 130 zed_log_err "failed to lock \"${lockfile}\": ${err}" 131 fi 132 133 umask "${umask_bak}" 134} 135 136 137# zed_unlock (lockfile, [fd]) 138# 139# Release the lock on [lockfile]. 140# 141# Arguments 142# lockfile: pathname of the lock file 143# fd: integer for the file descriptor used by flock (must match the file 144# descriptor passed to the zed_lock function call) 145# 146# Globals 147# ZED_FLOCK_FD 148# ZED_LOCKDIR 149# 150# Return 151# nothing 152# 153zed_unlock() 154{ 155 local lockfile="$1" 156 local fd="${2:-${ZED_FLOCK_FD}}" 157 local err 158 159 [ -n "${lockfile}" ] || return 160 if ! expr "${lockfile}" : '.*/' >/dev/null 2>&1; then 161 lockfile="${ZED_LOCKDIR}/${lockfile}" 162 fi 163 164 # Release the lock and close the file descriptor. 165 if ! err="$(flock --unlock "${fd}" 2>&1)"; then 166 zed_log_err "failed to unlock \"${lockfile}\": ${err}" 167 fi 168 eval "exec ${fd}>&-" 169} 170 171 172# zed_notify (subject, pathname) 173# 174# Send a notification via all available methods. 175# 176# Arguments 177# subject: notification subject 178# pathname: pathname containing the notification message (OPTIONAL) 179# 180# Return 181# 0: notification succeeded via at least one method 182# 1: notification failed 183# 2: no notification methods configured 184# 185zed_notify() 186{ 187 local subject="$1" 188 local pathname="$2" 189 local num_success=0 190 local num_failure=0 191 192 zed_notify_email "${subject}" "${pathname}"; rv=$? 193 [ "${rv}" -eq 0 ] && num_success=$((num_success + 1)) 194 [ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1)) 195 196 zed_notify_pushbullet "${subject}" "${pathname}"; rv=$? 197 [ "${rv}" -eq 0 ] && num_success=$((num_success + 1)) 198 [ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1)) 199 200 zed_notify_slack_webhook "${subject}" "${pathname}"; rv=$? 201 [ "${rv}" -eq 0 ] && num_success=$((num_success + 1)) 202 [ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1)) 203 204 zed_notify_pushover "${subject}" "${pathname}"; rv=$? 205 [ "${rv}" -eq 0 ] && num_success=$((num_success + 1)) 206 [ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1)) 207 208 zed_notify_ntfy "${subject}" "${pathname}"; rv=$? 209 [ "${rv}" -eq 0 ] && num_success=$((num_success + 1)) 210 [ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1)) 211 212 zed_notify_gotify "${subject}" "${pathname}"; rv=$? 213 [ "${rv}" -eq 0 ] && num_success=$((num_success + 1)) 214 [ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1)) 215 216 [ "${num_success}" -gt 0 ] && return 0 217 [ "${num_failure}" -gt 0 ] && return 1 218 return 2 219} 220 221 222# zed_notify_email (subject, pathname) 223# 224# Send a notification via email to the address specified by ZED_EMAIL_ADDR. 225# 226# Requires the mail executable to be installed in the standard PATH, or 227# ZED_EMAIL_PROG to be defined with the pathname of an executable capable of 228# reading a message body from stdin. 229# 230# Command-line options to the mail executable can be specified in 231# ZED_EMAIL_OPTS. This undergoes the following keyword substitutions: 232# - @ADDRESS@ is replaced with the space-delimited recipient email address(es) 233# - @SUBJECT@ is replaced with the notification subject 234# If @SUBJECT@ was omited here, a "Subject: ..." header will be added to notification 235# 236# 237# Arguments 238# subject: notification subject 239# pathname: pathname containing the notification message (OPTIONAL) 240# 241# Globals 242# ZED_EMAIL_PROG 243# ZED_EMAIL_OPTS 244# ZED_EMAIL_ADDR 245# 246# Return 247# 0: notification sent 248# 1: notification failed 249# 2: not configured 250# 251zed_notify_email() 252{ 253 local subject="${1:-"ZED notification"}" 254 local pathname="${2:-"/dev/null"}" 255 256 : "${ZED_EMAIL_PROG:="mail"}" 257 : "${ZED_EMAIL_OPTS:="-s '@SUBJECT@' @ADDRESS@"}" 258 259 # For backward compatibility with ZED_EMAIL. 260 if [ -n "${ZED_EMAIL}" ] && [ -z "${ZED_EMAIL_ADDR}" ]; then 261 ZED_EMAIL_ADDR="${ZED_EMAIL}" 262 fi 263 [ -n "${ZED_EMAIL_ADDR}" ] || return 2 264 265 zed_check_cmd "${ZED_EMAIL_PROG}" || return 1 266 267 [ -n "${subject}" ] || return 1 268 if [ ! -r "${pathname}" ]; then 269 zed_log_err \ 270 "${ZED_EMAIL_PROG##*/} cannot read \"${pathname}\"" 271 return 1 272 fi 273 274 # construct cmdline options 275 ZED_EMAIL_OPTS_PARSED="$(echo "${ZED_EMAIL_OPTS}" \ 276 | sed -e "s/@ADDRESS@/${ZED_EMAIL_ADDR}/g" \ 277 -e "s/@SUBJECT@/${subject}/g")" 278 279 # pipe message to email prog 280 # shellcheck disable=SC2086,SC2248 281 { 282 # no subject passed as option? 283 if [ "${ZED_EMAIL_OPTS%@SUBJECT@*}" = "${ZED_EMAIL_OPTS}" ] ; then 284 # inject subject header 285 printf "Subject: %s\n" "${subject}" 286 # The following empty line is needed to separate the header from the 287 # body of the message. Otherwise programs like sendmail will skip 288 # everything up to the first empty line (or wont send an email at 289 # all) and will still exit with exit code 0 290 printf "\n" 291 fi 292 # output message 293 cat "${pathname}" 294 } | 295 eval ${ZED_EMAIL_PROG} ${ZED_EMAIL_OPTS_PARSED} >/dev/null 2>&1 296 rv=$? 297 if [ "${rv}" -ne 0 ]; then 298 zed_log_err "${ZED_EMAIL_PROG##*/} exit=${rv}" 299 return 1 300 fi 301 return 0 302} 303 304 305# zed_notify_pushbullet (subject, pathname) 306# 307# Send a notification via Pushbullet <https://www.pushbullet.com/>. 308# The access token (ZED_PUSHBULLET_ACCESS_TOKEN) identifies this client to the 309# Pushbullet server. The optional channel tag (ZED_PUSHBULLET_CHANNEL_TAG) is 310# for pushing to notification feeds that can be subscribed to; if a channel is 311# not defined, push notifications will instead be sent to all devices 312# associated with the account specified by the access token. 313# 314# Requires awk, curl, and sed executables to be installed in the standard PATH. 315# 316# References 317# https://docs.pushbullet.com/ 318# https://www.pushbullet.com/security 319# 320# Arguments 321# subject: notification subject 322# pathname: pathname containing the notification message (OPTIONAL) 323# 324# Globals 325# ZED_PUSHBULLET_ACCESS_TOKEN 326# ZED_PUSHBULLET_CHANNEL_TAG 327# 328# Return 329# 0: notification sent 330# 1: notification failed 331# 2: not configured 332# 333zed_notify_pushbullet() 334{ 335 local subject="$1" 336 local pathname="${2:-"/dev/null"}" 337 local msg_body 338 local msg_tag 339 local msg_json 340 local msg_out 341 local msg_err 342 local url="https://api.pushbullet.com/v2/pushes" 343 344 [ -n "${ZED_PUSHBULLET_ACCESS_TOKEN}" ] || return 2 345 346 [ -n "${subject}" ] || return 1 347 if [ ! -r "${pathname}" ]; then 348 zed_log_err "pushbullet cannot read \"${pathname}\"" 349 return 1 350 fi 351 352 zed_check_cmd "awk" "curl" "sed" || return 1 353 354 # Escape the following characters in the message body for JSON: 355 # newline, backslash, double quote, horizontal tab, vertical tab, 356 # and carriage return. 357 # 358 msg_body="$(awk '{ ORS="\\n" } { gsub(/\\/, "\\\\"); gsub(/"/, "\\\""); 359 gsub(/\t/, "\\t"); gsub(/\f/, "\\f"); gsub(/\r/, "\\r"); print }' \ 360 "${pathname}")" 361 362 # Push to a channel if one is configured. 363 # 364 [ -n "${ZED_PUSHBULLET_CHANNEL_TAG}" ] && msg_tag="$(printf \ 365 '"channel_tag": "%s", ' "${ZED_PUSHBULLET_CHANNEL_TAG}")" 366 367 # Construct the JSON message for pushing a note. 368 # 369 msg_json="$(printf '{%s"type": "note", "title": "%s", "body": "%s"}' \ 370 "${msg_tag}" "${subject}" "${msg_body}")" 371 372 # Send the POST request and check for errors. 373 # 374 msg_out="$(curl -u "${ZED_PUSHBULLET_ACCESS_TOKEN}:" -X POST "${url}" \ 375 --header "Content-Type: application/json" --data-binary "${msg_json}" \ 376 2>/dev/null)"; rv=$? 377 if [ "${rv}" -ne 0 ]; then 378 zed_log_err "curl exit=${rv}" 379 return 1 380 fi 381 msg_err="$(echo "${msg_out}" \ 382 | sed -n -e 's/.*"error" *:.*"message" *: *"\([^"]*\)".*/\1/p')" 383 if [ -n "${msg_err}" ]; then 384 zed_log_err "pushbullet \"${msg_err}"\" 385 return 1 386 fi 387 return 0 388} 389 390 391# zed_notify_slack_webhook (subject, pathname) 392# 393# Notification via Slack Webhook <https://api.slack.com/incoming-webhooks>. 394# The Webhook URL (ZED_SLACK_WEBHOOK_URL) identifies this client to the 395# Slack channel. 396# 397# Requires awk, curl, and sed executables to be installed in the standard PATH. 398# 399# References 400# https://api.slack.com/incoming-webhooks 401# 402# Arguments 403# subject: notification subject 404# pathname: pathname containing the notification message (OPTIONAL) 405# 406# Globals 407# ZED_SLACK_WEBHOOK_URL 408# 409# Return 410# 0: notification sent 411# 1: notification failed 412# 2: not configured 413# 414zed_notify_slack_webhook() 415{ 416 [ -n "${ZED_SLACK_WEBHOOK_URL}" ] || return 2 417 418 local subject="$1" 419 local pathname="${2:-"/dev/null"}" 420 local msg_body 421 local msg_tag 422 local msg_json 423 local msg_out 424 local msg_err 425 local url="${ZED_SLACK_WEBHOOK_URL}" 426 427 [ -n "${subject}" ] || return 1 428 if [ ! -r "${pathname}" ]; then 429 zed_log_err "slack webhook cannot read \"${pathname}\"" 430 return 1 431 fi 432 433 zed_check_cmd "awk" "curl" "sed" || return 1 434 435 # Escape the following characters in the message body for JSON: 436 # newline, backslash, double quote, horizontal tab, vertical tab, 437 # and carriage return. 438 # 439 msg_body="$(awk '{ ORS="\\n" } { gsub(/\\/, "\\\\"); gsub(/"/, "\\\""); 440 gsub(/\t/, "\\t"); gsub(/\f/, "\\f"); gsub(/\r/, "\\r"); print }' \ 441 "${pathname}")" 442 443 # Construct the JSON message for posting. 444 # shellcheck disable=SC2016 445 # 446 msg_json="$(printf '{"text": "*%s*\\n```%s```"}' "${subject}" "${msg_body}" )" 447 448 # Send the POST request and check for errors. 449 # 450 msg_out="$(curl -X POST "${url}" \ 451 --header "Content-Type: application/json" --data-binary "${msg_json}" \ 452 2>/dev/null)"; rv=$? 453 if [ "${rv}" -ne 0 ]; then 454 zed_log_err "curl exit=${rv}" 455 return 1 456 fi 457 msg_err="$(echo "${msg_out}" \ 458 | sed -n -e 's/.*"error" *:.*"message" *: *"\([^"]*\)".*/\1/p')" 459 if [ -n "${msg_err}" ]; then 460 zed_log_err "slack webhook \"${msg_err}"\" 461 return 1 462 fi 463 return 0 464} 465 466# zed_notify_pushover (subject, pathname) 467# 468# Send a notification via Pushover <https://pushover.net/>. 469# The access token (ZED_PUSHOVER_TOKEN) identifies this client to the 470# Pushover server. The user token (ZED_PUSHOVER_USER) defines the user or 471# group to which the notification will be sent. 472# 473# Requires curl and sed executables to be installed in the standard PATH. 474# 475# References 476# https://pushover.net/api 477# 478# Arguments 479# subject: notification subject 480# pathname: pathname containing the notification message (OPTIONAL) 481# 482# Globals 483# ZED_PUSHOVER_TOKEN 484# ZED_PUSHOVER_USER 485# 486# Return 487# 0: notification sent 488# 1: notification failed 489# 2: not configured 490# 491zed_notify_pushover() 492{ 493 local subject="$1" 494 local pathname="${2:-"/dev/null"}" 495 local msg_body 496 local msg_out 497 local msg_err 498 local url="https://api.pushover.net/1/messages.json" 499 500 [ -n "${ZED_PUSHOVER_TOKEN}" ] && [ -n "${ZED_PUSHOVER_USER}" ] || return 2 501 502 if [ ! -r "${pathname}" ]; then 503 zed_log_err "pushover cannot read \"${pathname}\"" 504 return 1 505 fi 506 507 zed_check_cmd "curl" "sed" || return 1 508 509 # Read the message body in. 510 # 511 msg_body="$(cat "${pathname}")" 512 513 if [ -z "${msg_body}" ] 514 then 515 msg_body=$subject 516 subject="" 517 fi 518 519 # Send the POST request and check for errors. 520 # 521 msg_out="$( \ 522 curl \ 523 --form-string "token=${ZED_PUSHOVER_TOKEN}" \ 524 --form-string "user=${ZED_PUSHOVER_USER}" \ 525 --form-string "message=${msg_body}" \ 526 --form-string "title=${subject}" \ 527 "${url}" \ 528 2>/dev/null \ 529 )"; rv=$? 530 if [ "${rv}" -ne 0 ]; then 531 zed_log_err "curl exit=${rv}" 532 return 1 533 fi 534 msg_err="$(echo "${msg_out}" \ 535 | sed -n -e 's/.*"errors" *:.*\[\(.*\)\].*/\1/p')" 536 if [ -n "${msg_err}" ]; then 537 zed_log_err "pushover \"${msg_err}"\" 538 return 1 539 fi 540 return 0 541} 542 543 544# zed_notify_ntfy (subject, pathname) 545# 546# Send a notification via Ntfy.sh <https://ntfy.sh/>. 547# The ntfy topic (ZED_NTFY_TOPIC) identifies the topic that the notification 548# will be sent to Ntfy.sh server. The ntfy url (ZED_NTFY_URL) defines the 549# self-hosted or provided hosted ntfy service location. The ntfy access token 550# <https://docs.ntfy.sh/publish/#access-tokens> (ZED_NTFY_ACCESS_TOKEN) reprsents an 551# access token that could be used if a topic is read/write protected. If a 552# topic can be written to publicaly, a ZED_NTFY_ACCESS_TOKEN is not required. 553# 554# Requires curl and sed executables to be installed in the standard PATH. 555# 556# References 557# https://docs.ntfy.sh 558# 559# Arguments 560# subject: notification subject 561# pathname: pathname containing the notification message (OPTIONAL) 562# 563# Globals 564# ZED_NTFY_TOPIC 565# ZED_NTFY_ACCESS_TOKEN (OPTIONAL) 566# ZED_NTFY_URL 567# 568# Return 569# 0: notification sent 570# 1: notification failed 571# 2: not configured 572# 573zed_notify_ntfy() 574{ 575 local subject="$1" 576 local pathname="${2:-"/dev/null"}" 577 local msg_body 578 local msg_out 579 local msg_err 580 581 [ -n "${ZED_NTFY_TOPIC}" ] || return 2 582 local url="${ZED_NTFY_URL:-"https://ntfy.sh"}/${ZED_NTFY_TOPIC}" 583 584 if [ ! -r "${pathname}" ]; then 585 zed_log_err "ntfy cannot read \"${pathname}\"" 586 return 1 587 fi 588 589 zed_check_cmd "curl" "sed" || return 1 590 591 # Read the message body in. 592 # 593 msg_body="$(cat "${pathname}")" 594 595 if [ -z "${msg_body}" ] 596 then 597 msg_body=$subject 598 subject="" 599 fi 600 601 # Send the POST request and check for errors. 602 # 603 if [ -n "${ZED_NTFY_ACCESS_TOKEN}" ]; then 604 msg_out="$( \ 605 curl \ 606 -u ":${ZED_NTFY_ACCESS_TOKEN}" \ 607 -H "Title: ${subject}" \ 608 -d "${msg_body}" \ 609 -H "Priority: high" \ 610 "${url}" \ 611 2>/dev/null \ 612 )"; rv=$? 613 else 614 msg_out="$( \ 615 curl \ 616 -H "Title: ${subject}" \ 617 -d "${msg_body}" \ 618 -H "Priority: high" \ 619 "${url}" \ 620 2>/dev/null \ 621 )"; rv=$? 622 fi 623 if [ "${rv}" -ne 0 ]; then 624 zed_log_err "curl exit=${rv}" 625 return 1 626 fi 627 msg_err="$(echo "${msg_out}" \ 628 | sed -n -e 's/.*"errors" *:.*\[\(.*\)\].*/\1/p')" 629 if [ -n "${msg_err}" ]; then 630 zed_log_err "ntfy \"${msg_err}"\" 631 return 1 632 fi 633 return 0 634} 635 636 637# zed_notify_gotify (subject, pathname) 638# 639# Send a notification via Gotify <https://gotify.net/>. 640# The Gotify URL (ZED_GOTIFY_URL) defines a self-hosted Gotify location. 641# The Gotify application token (ZED_GOTIFY_APPTOKEN) defines a 642# Gotify application token which is associated with a message. 643# The optional Gotify priority value (ZED_GOTIFY_PRIORITY) overrides the 644# default or configured priority at the Gotify server for the application. 645# 646# Requires curl and sed executables to be installed in the standard PATH. 647# 648# References 649# https://gotify.net/docs/index 650# 651# Arguments 652# subject: notification subject 653# pathname: pathname containing the notification message (OPTIONAL) 654# 655# Globals 656# ZED_GOTIFY_URL 657# ZED_GOTIFY_APPTOKEN 658# ZED_GOTIFY_PRIORITY 659# 660# Return 661# 0: notification sent 662# 1: notification failed 663# 2: not configured 664# 665zed_notify_gotify() 666{ 667 local subject="$1" 668 local pathname="${2:-"/dev/null"}" 669 local msg_body 670 local msg_out 671 local msg_err 672 673 [ -n "${ZED_GOTIFY_URL}" ] && [ -n "${ZED_GOTIFY_APPTOKEN}" ] || return 2 674 local url="${ZED_GOTIFY_URL}/message?token=${ZED_GOTIFY_APPTOKEN}" 675 676 if [ ! -r "${pathname}" ]; then 677 zed_log_err "gotify cannot read \"${pathname}\"" 678 return 1 679 fi 680 681 zed_check_cmd "curl" "sed" || return 1 682 683 # Read the message body in. 684 # 685 msg_body="$(cat "${pathname}")" 686 687 if [ -z "${msg_body}" ] 688 then 689 msg_body=$subject 690 subject="" 691 fi 692 693 # Send the POST request and check for errors. 694 # 695 if [ -n "${ZED_GOTIFY_PRIORITY}" ]; then 696 msg_out="$( \ 697 curl \ 698 --form-string "title=${subject}" \ 699 --form-string "message=${msg_body}" \ 700 --form-string "priority=${ZED_GOTIFY_PRIORITY}" \ 701 "${url}" \ 702 2>/dev/null \ 703 )"; rv=$? 704 else 705 msg_out="$( \ 706 curl \ 707 --form-string "title=${subject}" \ 708 --form-string "message=${msg_body}" \ 709 "${url}" \ 710 2>/dev/null \ 711 )"; rv=$? 712 fi 713 714 if [ "${rv}" -ne 0 ]; then 715 zed_log_err "curl exit=${rv}" 716 return 1 717 fi 718 msg_err="$(echo "${msg_out}" \ 719 | sed -n -e 's/.*"errors" *:.*\[\(.*\)\].*/\1/p')" 720 if [ -n "${msg_err}" ]; then 721 zed_log_err "gotify \"${msg_err}"\" 722 return 1 723 fi 724 return 0 725} 726 727 728 729# zed_rate_limit (tag, [interval]) 730# 731# Check whether an event of a given type [tag] has already occurred within the 732# last [interval] seconds. 733# 734# This function obtains a lock on the statefile using file descriptor 9. 735# 736# Arguments 737# tag: arbitrary string for grouping related events to rate-limit 738# interval: time interval in seconds (OPTIONAL) 739# 740# Globals 741# ZED_NOTIFY_INTERVAL_SECS 742# ZED_RUNDIR 743# 744# Return 745# 0 if the event should be processed 746# 1 if the event should be dropped 747# 748# State File Format 749# time;tag 750# 751zed_rate_limit() 752{ 753 local tag="$1" 754 local interval="${2:-${ZED_NOTIFY_INTERVAL_SECS}}" 755 local lockfile="zed.zedlet.state.lock" 756 local lockfile_fd=9 757 local statefile="${ZED_RUNDIR}/zed.zedlet.state" 758 local time_now 759 local time_prev 760 local umask_bak 761 local rv=0 762 763 [ -n "${tag}" ] || return 0 764 765 zed_lock "${lockfile}" "${lockfile_fd}" 766 time_now="$(date +%s)" 767 time_prev="$(grep -E "^[0-9]+;${tag}\$" "${statefile}" 2>/dev/null \ 768 | tail -1 | cut -d\; -f1)" 769 770 if [ -n "${time_prev}" ] \ 771 && [ "$((time_now - time_prev))" -lt "${interval}" ]; then 772 rv=1 773 else 774 umask_bak="$(umask)" 775 umask 077 776 grep -E -v "^[0-9]+;${tag}\$" "${statefile}" 2>/dev/null \ 777 > "${statefile}.$$" 778 echo "${time_now};${tag}" >> "${statefile}.$$" 779 mv -f "${statefile}.$$" "${statefile}" 780 umask "${umask_bak}" 781 fi 782 783 zed_unlock "${lockfile}" "${lockfile_fd}" 784 return "${rv}" 785} 786 787 788# zed_guid_to_pool (guid) 789# 790# Convert a pool GUID into its pool name (like "tank") 791# Arguments 792# guid: pool GUID (decimal or hex) 793# 794# Return 795# Pool name 796# 797zed_guid_to_pool() 798{ 799 if [ -z "$1" ] ; then 800 return 801 fi 802 803 guid="$(printf "%u" "$1")" 804 $ZPOOL get -H -ovalue,name guid | awk '$1 == '"$guid"' {print $2; exit}' 805} 806 807# zed_exit_if_ignoring_this_event 808# 809# Exit the script if we should ignore this event, as determined by 810# $ZED_SYSLOG_SUBCLASS_INCLUDE and $ZED_SYSLOG_SUBCLASS_EXCLUDE in zed.rc. 811# This function assumes you've imported the normal zed variables. 812zed_exit_if_ignoring_this_event() 813{ 814 if [ -n "${ZED_SYSLOG_SUBCLASS_INCLUDE}" ]; then 815 eval "case ${ZEVENT_SUBCLASS} in 816 ${ZED_SYSLOG_SUBCLASS_INCLUDE});; 817 *) exit 0;; 818 esac" 819 elif [ -n "${ZED_SYSLOG_SUBCLASS_EXCLUDE}" ]; then 820 eval "case ${ZEVENT_SUBCLASS} in 821 ${ZED_SYSLOG_SUBCLASS_EXCLUDE}) exit 0;; 822 *);; 823 esac" 824 fi 825} 826