Skip to content

Commit

Permalink
Cleanup ZEDLETs
Browse files Browse the repository at this point in the history
This commit factors out several common ZEDLET code blocks into
zed-functions.sh.  This shortens the length of the scripts, thereby
(hopefully) making them easier to understand and maintain.

In addition, this commit revamps the coding style used by the
scripts to be more consistent and (again, hopefully) maintainable.
It now mostly follows the Google Shell Style Guide.  I've tried to
assimilate the following resources:

  Google Shell Style Guide
  https://google-styleguide.googlecode.com/svn/trunk/shell.xml

  Dash as /bin/sh
  https://wiki.ubuntu.com/DashAsBinSh

  Filenames and Pathnames in Shell: How to do it Correctly
  http://www.dwheeler.com/essays/filenames-in-shell.html

  Common shell script mistakes
  http://www.pixelbeat.org/programming/shell_script_mistakes.html

Finally, this commit updates the exit codes used by the ZEDLETs to be
more consistent with one another.

All scripts run cleanly through ShellCheck <http://www.shellcheck.net/>.
All scripts have been tested on bash and dash.

Signed-off-by: Chris Dunlap <[email protected]>
  • Loading branch information
dun authored and behlendorf committed Apr 27, 2015
1 parent 0336f3d commit aded9a6
Show file tree
Hide file tree
Showing 11 changed files with 630 additions and 337 deletions.
3 changes: 3 additions & 0 deletions cmd/zed/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ DEFAULT_INCLUDES += \
-I$(top_srcdir)/include \
-I$(top_srcdir)/lib/libspl/include

EXTRA_DIST = $(top_srcdir)/cmd/zed/zed.d/README

sbin_PROGRAMS = zed

zed_SOURCES = \
Expand Down Expand Up @@ -33,6 +35,7 @@ zed_LDADD = \
zedconfdir = $(sysconfdir)/zfs/zed.d

dist_zedconf_DATA = \
$(top_srcdir)/cmd/zed/zed.d/zed-functions.sh \
$(top_srcdir)/cmd/zed/zed.d/zed.rc

zedexecdir = $(libexecdir)/zfs/zed.d
Expand Down
30 changes: 30 additions & 0 deletions cmd/zed/zed.d/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Shell scripts are the recommended choice for ZEDLETs that mostly call
other utilities and do relatively little data manipulation.

Shell scripts MUST work on both bash and dash.

Shell scripts MUST run cleanly through ShellCheck:
http://www.shellcheck.net/

General functions reside in "zed-functions.sh". Use them where applicable.

Additional references that may be of use:

Google Shell Style Guide
https://google-styleguide.googlecode.com/svn/trunk/shell.xml

Dash as /bin/sh
https://wiki.ubuntu.com/DashAsBinSh

Common shell script mistakes
http://www.pixelbeat.org/programming/shell_script_mistakes.html

Filenames and Pathnames in Shell: How to do it Correctly
http://www.dwheeler.com/essays/filenames-in-shell.html

Autoconf: Portable Shell Programming
https://www.gnu.org/software/autoconf/manual/autoconf.html#Portable-Shell

Please BE CONSISTENT with the existing style, check for errors,
minimize dependencies where possible, try to be portable,
and comment anything non-obvious. Festina lente.
19 changes: 13 additions & 6 deletions cmd/zed/zed.d/all-debug.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,23 @@
#
# Log all environment variables to ZED_DEBUG_LOG.
#
test -f "${ZED_ZEDLET_DIR}/zed.rc" && . "${ZED_ZEDLET_DIR}/zed.rc"
# This can be a useful aid when developing/debugging ZEDLETs since it shows the
# environment variables defined for each zevent.

# Override the default umask to restrict access to a newly-created logfile.
umask 077
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
. "${ZED_ZEDLET_DIR}/zed-functions.sh"

: "${ZED_DEBUG_LOG:="${TMPDIR:="/tmp"}/zed.debug.log"}"

# Append stdout to the logfile after obtaining an advisory lock.
exec >> "${ZED_DEBUG_LOG:=/tmp/zed.debug.log}"
flock -x 1
lockfile="$(basename -- "${ZED_DEBUG_LOG}").lock"

umask 077
zed_lock "${lockfile}"
exec >> "${ZED_DEBUG_LOG}"

printenv | sort
echo

exec >&-
zed_unlock "${lockfile}"
exit 0
9 changes: 4 additions & 5 deletions cmd/zed/zed.d/all-syslog.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
#!/bin/sh
#
# Log the zevent via syslog.
#
test -f "${ZED_ZEDLET_DIR}/zed.rc" && . "${ZED_ZEDLET_DIR}/zed.rc"

logger -t "${ZED_SYSLOG_TAG:=zed}" -p "${ZED_SYSLOG_PRIORITY:=daemon.notice}" \
eid="${ZEVENT_EID}" class="${ZEVENT_SUBCLASS}" \
"${ZEVENT_POOL:+pool=$ZEVENT_POOL}"
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
. "${ZED_ZEDLET_DIR}/zed-functions.sh"

zed_log_msg "eid=${ZEVENT_EID}" "class=${ZEVENT_SUBCLASS}" \
"${ZEVENT_POOL:+"pool=${ZEVENT_POOL}"}"
exit 0
88 changes: 30 additions & 58 deletions cmd/zed/zed.d/data-email.sh
Original file line number Diff line number Diff line change
@@ -1,81 +1,53 @@
#!/bin/sh
#
# Send email to ZED_EMAIL in response to a DATA zevent.
# Only one message per ZED_EMAIL_INTERVAL_SECS will be sent for a given
# class/pool combination. This protects against spamming the recipient
# should multiple events occur together in time for the same pool.
# Send email to ZED_EMAIL in response to a DATA error.
#
# Only one email per ZED_EMAIL_INTERVAL_SECS will be sent for a given
# class/pool combination. This protects against spamming the recipient
# should multiple events occur together in time for the same pool.
#
# Exit codes:
# 0: email sent
# 1: email failed
# 2: email suppressed
# 3: missing executable
# 4: unsupported event class
# 5: internal error
# State File Format:
# POOL;TIME_OF_LAST_EMAIL
#
test -f "${ZED_ZEDLET_DIR}/zed.rc" && . "${ZED_ZEDLET_DIR}/zed.rc"
# 2: email not configured
# 3: email suppressed
# 9: internal error

test -n "${ZEVENT_POOL}" || exit 5
test -n "${ZEVENT_SUBCLASS}" || exit 5
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
. "${ZED_ZEDLET_DIR}/zed-functions.sh"

if test "${ZEVENT_SUBCLASS}" != "data"; then \
logger -t "${ZED_SYSLOG_TAG:=zed}" \
-p "${ZED_SYSLOG_PRIORITY:=daemon.warning}" \
`basename "$0"`: unsupported event class \"${ZEVENT_SUBCLASS}\"
exit 4
fi
[ -n "${ZED_EMAIL}" ] || exit 2

# Only send email if ZED_EMAIL has been configured.
test -n "${ZED_EMAIL}" || exit 2
[ -n "${ZEVENT_POOL}" ] || exit 9
[ -n "${ZEVENT_SUBCLASS}" ] || exit 9

# Ensure requisite executables are installed.
if ! command -v "${MAIL:=mail}" >/dev/null 2>&1; then
logger -t "${ZED_SYSLOG_TAG:=zed}" \
-p "${ZED_SYSLOG_PRIORITY:=daemon.warning}" \
`basename "$0"`: "${MAIL}" not installed
exit 3
if [ "${ZEVENT_SUBCLASS}" != "data" ]; then \
zed_log_err "unsupported event class \"${ZEVENT_SUBCLASS}\""
exit 9
fi

NAME="zed.${ZEVENT_SUBCLASS}.email"
LOCKFILE="${ZED_LOCKDIR:=/var/lock}/${NAME}.lock"
STATEFILE="${ZED_RUNDIR:=/var/run}/${NAME}.state"
zed_check_cmd "mail" || exit 9

# Obtain lock to ensure mutual exclusion for accessing state.
exec 8> "${LOCKFILE}"
flock -x 8
zed_rate_limit "${ZEVENT_POOL};${ZEVENT_SUBCLASS};email" || exit 3

# Query state for last time email was sent for this pool.
TIME_NOW=`date +%s`
TIME_LAST=`egrep "^${ZEVENT_POOL};" "${STATEFILE}" 2>/dev/null | cut -d ";" -f2`
if test -n "${TIME_LAST}"; then
TIME_DELTA=`expr "${TIME_NOW}" - "${TIME_LAST}"`
if test "${TIME_DELTA}" -lt "${ZED_EMAIL_INTERVAL_SECS:=3600}"; then
exit 2
fi
fi

"${MAIL}" -s "ZFS ${ZEVENT_SUBCLASS} error for ${ZEVENT_POOL} on `hostname`" \
"${ZED_EMAIL}" <<EOF
umask 077
email_subject="ZFS ${ZEVENT_SUBCLASS} error for ${ZEVENT_POOL} on $(hostname)"
email_pathname="${TMPDIR:="/tmp"}/$(basename -- "$0").${ZEVENT_EID}.$$"
cat > "${email_pathname}" <<EOF
A ZFS ${ZEVENT_SUBCLASS} error has been detected:
eid: ${ZEVENT_EID}
host: `hostname`
host: $(hostname)
time: ${ZEVENT_TIME_STRING}
pool: ${ZEVENT_POOL}
EOF
MAIL_STATUS=$?

# Update state.
egrep -v "^${ZEVENT_POOL};" "${STATEFILE}" 2>/dev/null > "${STATEFILE}.$$"
echo "${ZEVENT_POOL};${TIME_NOW}" >> "${STATEFILE}.$$"
mv -f "${STATEFILE}.$$" "${STATEFILE}"
mail -s "${email_subject}" "${ZED_EMAIL}" < "${email_pathname}"
mail_status=$?

if test "${MAIL_STATUS}" -ne 0; then
logger -t "${ZED_SYSLOG_TAG:=zed}" \
-p "${ZED_SYSLOG_PRIORITY:=daemon.warning}" \
`basename "$0"`: "${MAIL}" exit="${MAIL_STATUS}"
exit 1
if [ "${mail_status}" -ne 0 ]; then
zed_log_msg "mail exit=${mail_status}"
exit 1
fi

rm -f "${email_pathname}"
exit 0
90 changes: 45 additions & 45 deletions cmd/zed/zed.d/generic-email.sh
Original file line number Diff line number Diff line change
@@ -1,59 +1,59 @@
#!/bin/sh
#
# Send email to ZED_EMAIL in response to a given zevent.
# This is a generic script than can be symlinked to a file in the zed
# enabled-scripts directory in order to have email sent when a particular
# class of zevents occurs. The symlink filename must begin with the zevent
# (sub)class string (eg, "probe_failure-email.sh" for the "probe_failure"
# subclass). Refer to the zed(8) manpage for details.
#
# This is a generic script than can be symlinked to a file in the
# enabled-zedlets directory to have an email sent when a particular class of
# zevents occurs. The symlink filename must begin with the zevent (sub)class
# string (e.g., "probe_failure-email.sh" for the "probe_failure" subclass).
# Refer to the zed(8) manpage for details.
#
# Exit codes:
# 0: email sent
# 1: email failed
# 2: email suppressed
# 3: missing executable
#
test -f "${ZED_ZEDLET_DIR}/zed.rc" && . "${ZED_ZEDLET_DIR}/zed.rc"
# 2: email not configured
# 3: email suppressed

# Only send email if ZED_EMAIL has been configured.
test -n "${ZED_EMAIL}" || exit 2
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
. "${ZED_ZEDLET_DIR}/zed-functions.sh"

# Ensure requisite executables are installed.
if ! command -v "${MAIL:=mail}" >/dev/null 2>&1; then
logger -t "${ZED_SYSLOG_TAG:=zed}" \
-p "${ZED_SYSLOG_PRIORITY:=daemon.warning}" \
`basename "$0"`: "${MAIL}" not installed
exit 3
fi
[ -n "${ZED_EMAIL}" ] || exit 2

# Rate-limit the message based in part on the filename.
#
rate_limit_tag="${ZEVENT_POOL};${ZEVENT_SUBCLASS};$(basename -- "$0")"
rate_limit_interval="${ZED_EMAIL_INTERVAL_SECS}"
zed_rate_limit "${rate_limit_tag}" "${rate_limit_interval}" || exit 3

# Override the default umask to restrict access to the msgbody tmpfile.
umask 077
pool_str="${ZEVENT_POOL:+" for ${ZEVENT_POOL}"}"
host_str=" on $(hostname)"
email_subject="ZFS ${ZEVENT_SUBCLASS} event${pool_str}${host_str}"
email_pathname="${TMPDIR:="/tmp"}/$(basename -- "$0").${ZEVENT_EID}.$$"
{
echo "ZFS has posted the following event:"
echo
echo " eid: ${ZEVENT_EID}"
echo " class: ${ZEVENT_SUBCLASS}"
echo " host: $(hostname)"
echo " time: ${ZEVENT_TIME_STRING}"

SUBJECT="ZFS ${ZEVENT_SUBCLASS} event"
test -n "${ZEVENT_POOL}" && SUBJECT="${SUBJECT} for ${ZEVENT_POOL}"
SUBJECT="${SUBJECT} on `hostname`"
if [ -n "${ZEVENT_VDEV_PATH}" ]; then
echo " vpath: ${ZEVENT_VDEV_PATH}"
[ -n "${ZEVENT_VDEV_TYPE}" ] && echo " vtype: ${ZEVENT_VDEV_TYPE}"
fi

MSGBODY="${TMPDIR:=/tmp}/`basename \"$0\"`.$$"
{
echo "A ZFS ${ZEVENT_SUBCLASS} event has been posted:"
echo
echo " eid: ${ZEVENT_EID}"
echo " host: `hostname`"
echo " time: ${ZEVENT_TIME_STRING}"
test -n "${ZEVENT_VDEV_TYPE}" -a -n "${ZEVENT_VDEV_PATH}" && \
echo " vdev: ${ZEVENT_VDEV_TYPE}:${ZEVENT_VDEV_PATH}"
test -n "${ZEVENT_POOL}" -a -x "${ZPOOL}" && \
"${ZPOOL}" status "${ZEVENT_POOL}"
} > "${MSGBODY}"

test -f "${MSGBODY}" && "${MAIL}" -s "${SUBJECT}" "${ZED_EMAIL}" < "${MSGBODY}"
MAIL_STATUS=$?
rm -f "${MSGBODY}"

if test "${MAIL_STATUS}" -ne 0; then
logger -t "${ZED_SYSLOG_TAG:=zed}" \
-p "${ZED_SYSLOG_PRIORITY:=daemon.warning}" \
`basename "$0"`: "${MAIL}" exit="${MAIL_STATUS}"
exit 1
fi
[ -n "${ZEVENT_POOL}" ] && [ -x "${ZPOOL}" ] \
&& "${ZPOOL}" status "${ZEVENT_POOL}"

} > "${email_pathname}"

mail -s "${email_subject}" "${ZED_EMAIL}" < "${email_pathname}"
mail_status=$?

if [ "${mail_status}" -ne 0 ]; then
zed_log_msg "mail exit=${mail_status}"
exit 1
fi
rm -f "${email_pathname}"
exit 0
Loading

0 comments on commit aded9a6

Please sign in to comment.