]> git.proxmox.com Git - mirror_zfs.git/blob - cmd/zed/zed.d/zed-functions.sh
FreeBSD: Add zfs_link_create() error handling
[mirror_zfs.git] / cmd / zed / zed.d / zed-functions.sh
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
17 ZED_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 #
32 zed_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 #
58 zed_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 #
77 zed_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 #
111 zed_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 #
153 zed_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 #
185 zed_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 #
251 zed_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 fi
287 # output message
288 cat "${pathname}"
289 } |
290 eval ${ZED_EMAIL_PROG} ${ZED_EMAIL_OPTS_PARSED} >/dev/null 2>&1
291 rv=$?
292 if [ "${rv}" -ne 0 ]; then
293 zed_log_err "${ZED_EMAIL_PROG##*/} exit=${rv}"
294 return 1
295 fi
296 return 0
297 }
298
299
300 # zed_notify_pushbullet (subject, pathname)
301 #
302 # Send a notification via Pushbullet <https://www.pushbullet.com/>.
303 # The access token (ZED_PUSHBULLET_ACCESS_TOKEN) identifies this client to the
304 # Pushbullet server. The optional channel tag (ZED_PUSHBULLET_CHANNEL_TAG) is
305 # for pushing to notification feeds that can be subscribed to; if a channel is
306 # not defined, push notifications will instead be sent to all devices
307 # associated with the account specified by the access token.
308 #
309 # Requires awk, curl, and sed executables to be installed in the standard PATH.
310 #
311 # References
312 # https://docs.pushbullet.com/
313 # https://www.pushbullet.com/security
314 #
315 # Arguments
316 # subject: notification subject
317 # pathname: pathname containing the notification message (OPTIONAL)
318 #
319 # Globals
320 # ZED_PUSHBULLET_ACCESS_TOKEN
321 # ZED_PUSHBULLET_CHANNEL_TAG
322 #
323 # Return
324 # 0: notification sent
325 # 1: notification failed
326 # 2: not configured
327 #
328 zed_notify_pushbullet()
329 {
330 local subject="$1"
331 local pathname="${2:-"/dev/null"}"
332 local msg_body
333 local msg_tag
334 local msg_json
335 local msg_out
336 local msg_err
337 local url="https://api.pushbullet.com/v2/pushes"
338
339 [ -n "${ZED_PUSHBULLET_ACCESS_TOKEN}" ] || return 2
340
341 [ -n "${subject}" ] || return 1
342 if [ ! -r "${pathname}" ]; then
343 zed_log_err "pushbullet cannot read \"${pathname}\""
344 return 1
345 fi
346
347 zed_check_cmd "awk" "curl" "sed" || return 1
348
349 # Escape the following characters in the message body for JSON:
350 # newline, backslash, double quote, horizontal tab, vertical tab,
351 # and carriage return.
352 #
353 msg_body="$(awk '{ ORS="\\n" } { gsub(/\\/, "\\\\"); gsub(/"/, "\\\"");
354 gsub(/\t/, "\\t"); gsub(/\f/, "\\f"); gsub(/\r/, "\\r"); print }' \
355 "${pathname}")"
356
357 # Push to a channel if one is configured.
358 #
359 [ -n "${ZED_PUSHBULLET_CHANNEL_TAG}" ] && msg_tag="$(printf \
360 '"channel_tag": "%s", ' "${ZED_PUSHBULLET_CHANNEL_TAG}")"
361
362 # Construct the JSON message for pushing a note.
363 #
364 msg_json="$(printf '{%s"type": "note", "title": "%s", "body": "%s"}' \
365 "${msg_tag}" "${subject}" "${msg_body}")"
366
367 # Send the POST request and check for errors.
368 #
369 msg_out="$(curl -u "${ZED_PUSHBULLET_ACCESS_TOKEN}:" -X POST "${url}" \
370 --header "Content-Type: application/json" --data-binary "${msg_json}" \
371 2>/dev/null)"; rv=$?
372 if [ "${rv}" -ne 0 ]; then
373 zed_log_err "curl exit=${rv}"
374 return 1
375 fi
376 msg_err="$(echo "${msg_out}" \
377 | sed -n -e 's/.*"error" *:.*"message" *: *"\([^"]*\)".*/\1/p')"
378 if [ -n "${msg_err}" ]; then
379 zed_log_err "pushbullet \"${msg_err}"\"
380 return 1
381 fi
382 return 0
383 }
384
385
386 # zed_notify_slack_webhook (subject, pathname)
387 #
388 # Notification via Slack Webhook <https://api.slack.com/incoming-webhooks>.
389 # The Webhook URL (ZED_SLACK_WEBHOOK_URL) identifies this client to the
390 # Slack channel.
391 #
392 # Requires awk, curl, and sed executables to be installed in the standard PATH.
393 #
394 # References
395 # https://api.slack.com/incoming-webhooks
396 #
397 # Arguments
398 # subject: notification subject
399 # pathname: pathname containing the notification message (OPTIONAL)
400 #
401 # Globals
402 # ZED_SLACK_WEBHOOK_URL
403 #
404 # Return
405 # 0: notification sent
406 # 1: notification failed
407 # 2: not configured
408 #
409 zed_notify_slack_webhook()
410 {
411 [ -n "${ZED_SLACK_WEBHOOK_URL}" ] || return 2
412
413 local subject="$1"
414 local pathname="${2:-"/dev/null"}"
415 local msg_body
416 local msg_tag
417 local msg_json
418 local msg_out
419 local msg_err
420 local url="${ZED_SLACK_WEBHOOK_URL}"
421
422 [ -n "${subject}" ] || return 1
423 if [ ! -r "${pathname}" ]; then
424 zed_log_err "slack webhook cannot read \"${pathname}\""
425 return 1
426 fi
427
428 zed_check_cmd "awk" "curl" "sed" || return 1
429
430 # Escape the following characters in the message body for JSON:
431 # newline, backslash, double quote, horizontal tab, vertical tab,
432 # and carriage return.
433 #
434 msg_body="$(awk '{ ORS="\\n" } { gsub(/\\/, "\\\\"); gsub(/"/, "\\\"");
435 gsub(/\t/, "\\t"); gsub(/\f/, "\\f"); gsub(/\r/, "\\r"); print }' \
436 "${pathname}")"
437
438 # Construct the JSON message for posting.
439 #
440 msg_json="$(printf '{"text": "*%s*\\n%s"}' "${subject}" "${msg_body}" )"
441
442 # Send the POST request and check for errors.
443 #
444 msg_out="$(curl -X POST "${url}" \
445 --header "Content-Type: application/json" --data-binary "${msg_json}" \
446 2>/dev/null)"; rv=$?
447 if [ "${rv}" -ne 0 ]; then
448 zed_log_err "curl exit=${rv}"
449 return 1
450 fi
451 msg_err="$(echo "${msg_out}" \
452 | sed -n -e 's/.*"error" *:.*"message" *: *"\([^"]*\)".*/\1/p')"
453 if [ -n "${msg_err}" ]; then
454 zed_log_err "slack webhook \"${msg_err}"\"
455 return 1
456 fi
457 return 0
458 }
459
460 # zed_notify_pushover (subject, pathname)
461 #
462 # Send a notification via Pushover <https://pushover.net/>.
463 # The access token (ZED_PUSHOVER_TOKEN) identifies this client to the
464 # Pushover server. The user token (ZED_PUSHOVER_USER) defines the user or
465 # group to which the notification will be sent.
466 #
467 # Requires curl and sed executables to be installed in the standard PATH.
468 #
469 # References
470 # https://pushover.net/api
471 #
472 # Arguments
473 # subject: notification subject
474 # pathname: pathname containing the notification message (OPTIONAL)
475 #
476 # Globals
477 # ZED_PUSHOVER_TOKEN
478 # ZED_PUSHOVER_USER
479 #
480 # Return
481 # 0: notification sent
482 # 1: notification failed
483 # 2: not configured
484 #
485 zed_notify_pushover()
486 {
487 local subject="$1"
488 local pathname="${2:-"/dev/null"}"
489 local msg_body
490 local msg_out
491 local msg_err
492 local url="https://api.pushover.net/1/messages.json"
493
494 [ -n "${ZED_PUSHOVER_TOKEN}" ] && [ -n "${ZED_PUSHOVER_USER}" ] || return 2
495
496 if [ ! -r "${pathname}" ]; then
497 zed_log_err "pushover cannot read \"${pathname}\""
498 return 1
499 fi
500
501 zed_check_cmd "curl" "sed" || return 1
502
503 # Read the message body in.
504 #
505 msg_body="$(cat "${pathname}")"
506
507 if [ -z "${msg_body}" ]
508 then
509 msg_body=$subject
510 subject=""
511 fi
512
513 # Send the POST request and check for errors.
514 #
515 msg_out="$( \
516 curl \
517 --form-string "token=${ZED_PUSHOVER_TOKEN}" \
518 --form-string "user=${ZED_PUSHOVER_USER}" \
519 --form-string "message=${msg_body}" \
520 --form-string "title=${subject}" \
521 "${url}" \
522 2>/dev/null \
523 )"; rv=$?
524 if [ "${rv}" -ne 0 ]; then
525 zed_log_err "curl exit=${rv}"
526 return 1
527 fi
528 msg_err="$(echo "${msg_out}" \
529 | sed -n -e 's/.*"errors" *:.*\[\(.*\)\].*/\1/p')"
530 if [ -n "${msg_err}" ]; then
531 zed_log_err "pushover \"${msg_err}"\"
532 return 1
533 fi
534 return 0
535 }
536
537
538 # zed_notify_ntfy (subject, pathname)
539 #
540 # Send a notification via Ntfy.sh <https://ntfy.sh/>.
541 # The ntfy topic (ZED_NTFY_TOPIC) identifies the topic that the notification
542 # will be sent to Ntfy.sh server. The ntfy url (ZED_NTFY_URL) defines the
543 # self-hosted or provided hosted ntfy service location. The ntfy access token
544 # <https://docs.ntfy.sh/publish/#access-tokens> (ZED_NTFY_ACCESS_TOKEN) reprsents an
545 # access token that could be used if a topic is read/write protected. If a
546 # topic can be written to publicaly, a ZED_NTFY_ACCESS_TOKEN is not required.
547 #
548 # Requires curl and sed executables to be installed in the standard PATH.
549 #
550 # References
551 # https://docs.ntfy.sh
552 #
553 # Arguments
554 # subject: notification subject
555 # pathname: pathname containing the notification message (OPTIONAL)
556 #
557 # Globals
558 # ZED_NTFY_TOPIC
559 # ZED_NTFY_ACCESS_TOKEN (OPTIONAL)
560 # ZED_NTFY_URL
561 #
562 # Return
563 # 0: notification sent
564 # 1: notification failed
565 # 2: not configured
566 #
567 zed_notify_ntfy()
568 {
569 local subject="$1"
570 local pathname="${2:-"/dev/null"}"
571 local msg_body
572 local msg_out
573 local msg_err
574
575 [ -n "${ZED_NTFY_TOPIC}" ] || return 2
576 local url="${ZED_NTFY_URL:-"https://ntfy.sh"}/${ZED_NTFY_TOPIC}"
577
578 if [ ! -r "${pathname}" ]; then
579 zed_log_err "ntfy cannot read \"${pathname}\""
580 return 1
581 fi
582
583 zed_check_cmd "curl" "sed" || return 1
584
585 # Read the message body in.
586 #
587 msg_body="$(cat "${pathname}")"
588
589 if [ -z "${msg_body}" ]
590 then
591 msg_body=$subject
592 subject=""
593 fi
594
595 # Send the POST request and check for errors.
596 #
597 if [ -n "${ZED_NTFY_ACCESS_TOKEN}" ]; then
598 msg_out="$( \
599 curl \
600 -u ":${ZED_NTFY_ACCESS_TOKEN}" \
601 -H "Title: ${subject}" \
602 -d "${msg_body}" \
603 -H "Priority: high" \
604 "${url}" \
605 2>/dev/null \
606 )"; rv=$?
607 else
608 msg_out="$( \
609 curl \
610 -H "Title: ${subject}" \
611 -d "${msg_body}" \
612 -H "Priority: high" \
613 "${url}" \
614 2>/dev/null \
615 )"; rv=$?
616 fi
617 if [ "${rv}" -ne 0 ]; then
618 zed_log_err "curl exit=${rv}"
619 return 1
620 fi
621 msg_err="$(echo "${msg_out}" \
622 | sed -n -e 's/.*"errors" *:.*\[\(.*\)\].*/\1/p')"
623 if [ -n "${msg_err}" ]; then
624 zed_log_err "ntfy \"${msg_err}"\"
625 return 1
626 fi
627 return 0
628 }
629
630
631 # zed_notify_gotify (subject, pathname)
632 #
633 # Send a notification via Gotify <https://gotify.net/>.
634 # The Gotify URL (ZED_GOTIFY_URL) defines a self-hosted Gotify location.
635 # The Gotify application token (ZED_GOTIFY_APPTOKEN) defines a
636 # Gotify application token which is associated with a message.
637 # The optional Gotify priority value (ZED_GOTIFY_PRIORITY) overrides the
638 # default or configured priority at the Gotify server for the application.
639 #
640 # Requires curl and sed executables to be installed in the standard PATH.
641 #
642 # References
643 # https://gotify.net/docs/index
644 #
645 # Arguments
646 # subject: notification subject
647 # pathname: pathname containing the notification message (OPTIONAL)
648 #
649 # Globals
650 # ZED_GOTIFY_URL
651 # ZED_GOTIFY_APPTOKEN
652 # ZED_GOTIFY_PRIORITY
653 #
654 # Return
655 # 0: notification sent
656 # 1: notification failed
657 # 2: not configured
658 #
659 zed_notify_gotify()
660 {
661 local subject="$1"
662 local pathname="${2:-"/dev/null"}"
663 local msg_body
664 local msg_out
665 local msg_err
666
667 [ -n "${ZED_GOTIFY_URL}" ] && [ -n "${ZED_GOTIFY_APPTOKEN}" ] || return 2
668 local url="${ZED_GOTIFY_URL}/message?token=${ZED_GOTIFY_APPTOKEN}"
669
670 if [ ! -r "${pathname}" ]; then
671 zed_log_err "gotify cannot read \"${pathname}\""
672 return 1
673 fi
674
675 zed_check_cmd "curl" "sed" || return 1
676
677 # Read the message body in.
678 #
679 msg_body="$(cat "${pathname}")"
680
681 if [ -z "${msg_body}" ]
682 then
683 msg_body=$subject
684 subject=""
685 fi
686
687 # Send the POST request and check for errors.
688 #
689 if [ -n "${ZED_GOTIFY_PRIORITY}" ]; then
690 msg_out="$( \
691 curl \
692 --form-string "title=${subject}" \
693 --form-string "message=${msg_body}" \
694 --form-string "priority=${ZED_GOTIFY_PRIORITY}" \
695 "${url}" \
696 2>/dev/null \
697 )"; rv=$?
698 else
699 msg_out="$( \
700 curl \
701 --form-string "title=${subject}" \
702 --form-string "message=${msg_body}" \
703 "${url}" \
704 2>/dev/null \
705 )"; rv=$?
706 fi
707
708 if [ "${rv}" -ne 0 ]; then
709 zed_log_err "curl exit=${rv}"
710 return 1
711 fi
712 msg_err="$(echo "${msg_out}" \
713 | sed -n -e 's/.*"errors" *:.*\[\(.*\)\].*/\1/p')"
714 if [ -n "${msg_err}" ]; then
715 zed_log_err "gotify \"${msg_err}"\"
716 return 1
717 fi
718 return 0
719 }
720
721
722
723 # zed_rate_limit (tag, [interval])
724 #
725 # Check whether an event of a given type [tag] has already occurred within the
726 # last [interval] seconds.
727 #
728 # This function obtains a lock on the statefile using file descriptor 9.
729 #
730 # Arguments
731 # tag: arbitrary string for grouping related events to rate-limit
732 # interval: time interval in seconds (OPTIONAL)
733 #
734 # Globals
735 # ZED_NOTIFY_INTERVAL_SECS
736 # ZED_RUNDIR
737 #
738 # Return
739 # 0 if the event should be processed
740 # 1 if the event should be dropped
741 #
742 # State File Format
743 # time;tag
744 #
745 zed_rate_limit()
746 {
747 local tag="$1"
748 local interval="${2:-${ZED_NOTIFY_INTERVAL_SECS}}"
749 local lockfile="zed.zedlet.state.lock"
750 local lockfile_fd=9
751 local statefile="${ZED_RUNDIR}/zed.zedlet.state"
752 local time_now
753 local time_prev
754 local umask_bak
755 local rv=0
756
757 [ -n "${tag}" ] || return 0
758
759 zed_lock "${lockfile}" "${lockfile_fd}"
760 time_now="$(date +%s)"
761 time_prev="$(grep -E "^[0-9]+;${tag}\$" "${statefile}" 2>/dev/null \
762 | tail -1 | cut -d\; -f1)"
763
764 if [ -n "${time_prev}" ] \
765 && [ "$((time_now - time_prev))" -lt "${interval}" ]; then
766 rv=1
767 else
768 umask_bak="$(umask)"
769 umask 077
770 grep -E -v "^[0-9]+;${tag}\$" "${statefile}" 2>/dev/null \
771 > "${statefile}.$$"
772 echo "${time_now};${tag}" >> "${statefile}.$$"
773 mv -f "${statefile}.$$" "${statefile}"
774 umask "${umask_bak}"
775 fi
776
777 zed_unlock "${lockfile}" "${lockfile_fd}"
778 return "${rv}"
779 }
780
781
782 # zed_guid_to_pool (guid)
783 #
784 # Convert a pool GUID into its pool name (like "tank")
785 # Arguments
786 # guid: pool GUID (decimal or hex)
787 #
788 # Return
789 # Pool name
790 #
791 zed_guid_to_pool()
792 {
793 if [ -z "$1" ] ; then
794 return
795 fi
796
797 guid="$(printf "%u" "$1")"
798 $ZPOOL get -H -ovalue,name guid | awk '$1 == '"$guid"' {print $2; exit}'
799 }
800
801 # zed_exit_if_ignoring_this_event
802 #
803 # Exit the script if we should ignore this event, as determined by
804 # $ZED_SYSLOG_SUBCLASS_INCLUDE and $ZED_SYSLOG_SUBCLASS_EXCLUDE in zed.rc.
805 # This function assumes you've imported the normal zed variables.
806 zed_exit_if_ignoring_this_event()
807 {
808 if [ -n "${ZED_SYSLOG_SUBCLASS_INCLUDE}" ]; then
809 eval "case ${ZEVENT_SUBCLASS} in
810 ${ZED_SYSLOG_SUBCLASS_INCLUDE});;
811 *) exit 0;;
812 esac"
813 elif [ -n "${ZED_SYSLOG_SUBCLASS_EXCLUDE}" ]; then
814 eval "case ${ZEVENT_SUBCLASS} in
815 ${ZED_SYSLOG_SUBCLASS_EXCLUDE}) exit 0;;
816 *);;
817 esac"
818 fi
819 }