Skip to content

Instantly share code, notes, and snippets.

@PhrozenByte
Last active April 7, 2022 20:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PhrozenByte/2c0e03bbdb750da4c36d54f02c009430 to your computer and use it in GitHub Desktop.
Save PhrozenByte/2c0e03bbdb750da4c36d54f02c009430 to your computer and use it in GitHub Desktop.
Munin statefile support functions for munin shell plugins
# -*- sh -*-
# Munin statefile support functions for munin shell plugins
#
: << =cut
=head1 NAME
plugin-statefile.sh - Munin statefile support functions for munin shell plugins
=head1 AUTHOR
Copyright (C) 2019 Daniel Rudolf
=head1 LICENSE
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License only.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
See <http://www.gnu.org/licenses/> to receive a full-text-copy of
the GNU General Public License.
=cut
MUNIN_STATEFILE_TMP=""
# Checks whether all given arguments are valid munin field names.
#
# `test_fieldname` takes a arbitrary number of parameters and returns with rc 0
# when all given arguments are valid munin field names, otherwise it returns
# with rc 1.
#
# Usage:
# test_fieldname foo bar_42
test_fieldname () {
[ $# -gt 0 ] || return 1
for ARG in "$@"; do
if [ -z "$ARG" ] || ! echo "$ARG" | sed -ne '/^[A-Za-z_][A-Za-z0-9_]*$/!{q1}'; then
return 1
fi
done
}
# Writes data to the temporary munin statefile.
#
# `write_statefile` takes three or any larger odd number of parameters. The
# first argument is the common field name to all attributes to store. All
# following argument tuples represent the names and values of attributes to
# write to the temporary statefile.
#
# Both field names and attribute names must meet munin's field name
# requirements. Use `clean_fieldname` to create compliant names. You can't
# store multiline values.
#
# All data is written to a temporary file until explicitly commited using
# `commit_statefile`. You can also discard all data written by calling
# `discard_statefile`.
#
# Usage:
# write_statefile my_field foo value_of_foo bar value_of_bar
# write_statefile my_other_field answer_to_life_universe_and_everything 42
write_statefile () {
if [ -z "$MUNIN_STATEFILE_TMP" ]; then
MUNIN_STATEFILE_TMP="$(mktemp)"
echo "%MUNIN-STATE1.0" > "$MUNIN_STATEFILE_TMP"
fi
local FIELD="$1"
test_fieldname "$FIELD" || { echo "Invalid field name: $FIELD" >&2; return 1; }
shift
local ATTR=""
local VALUE=""
while [ $# -ge 2 ]; do
ATTR="$1"
test_fieldname "$ATTR" || { echo "Invalid attribute name: $ATTR" >&2; return 1; }
VALUE="$2"
[ "$(echo "$VALUE" | wc -l)" -eq 1 ] || { echo "Invalid multiline attribute value: $VALUE" >&2; return 1; }
shift 2
[ -n "$VALUE" ] || continue
echo "$FIELD.$ATTR $(echo "$VALUE" | head -n 1)" >> "$MUNIN_STATEFILE_TMP"
done
}
# Discards the temporary munin statefile.
#
# `discard_statefile` deletes the temporary munin statefile and discards all
# uncommited data.
#
# Usage:
# discard_statefile
discard_statefile () {
[ -z "$MUNIN_STATEFILE_TMP" ] || rm -f "$MUNIN_STATEFILE_TMP"
MUNIN_STATEFILE_TMP=""
}
# Commits the temporary munin statefile.
#
# `commit_statefile` replaces the plugin's statefile by the temporary munin
# statefile (created by `write_statefile`). If there's no temporary munin
# statefile (i.e. `write_statefile` wasn't called yet), the plugin's statefile
# will be deleted.
#
# Usage:
# commit_statefile
commit_statefile () {
if [ -z "$MUNIN_STATEFILE_TMP" ]; then
rm -f "$MUNIN_STATEFILE"
return 0
fi
mv "$MUNIN_STATEFILE_TMP" "$MUNIN_STATEFILE"
}
# Returns attribute values stored in the plugin's statefile.
#
# `read_statefile` switches behavior depending on the number of arguments
# given. It always reads the plugin's statefile and bypasses the temporary
# munin statefile until it was commited using `commit_statefile`.
#
# If no argument is given, `read_statefile` returns a list of all fields
# present in the plugin's statefile.
#
# If a single argument is given, `read_statefile` takes this argument as field
# name and returns a list of the names of all attributes stored for this field.
#
# If multiple arguments are given, `read_statefile` takes the first argument as
# common field name and all following arguments as attribute names. It returns
# the values of the searched attributes line by line. The number of lines
# returned strictly matches the number of attributes requested, resuting in
# empty lines for non-existing attributes.
#
# Usage:
# read_statefile
# Return:
# my_field
# my_other_field
#
# Usage:
# read_statefile my_field
# Return:
# foo
# bar
#
# Usage:
# read_statefile my_field foo non_existing bar
# Return:
# value_of_foo
#
# value_of_bar
read_statefile () {
if ! [ -f "$MUNIN_STATEFILE" ]; then
[ $# -lt 2 ] || seq 2 $# | xargs printf '\n%.0s'
return 0
fi
[ "$(head -n 1 "$MUNIN_STATEFILE")" == "%MUNIN-STATE1.0" ] || { echo "Invalid statefile header" >&2; return 1; }
if [ $# -eq 0 ]; then
sed -ne '2,$s/^\([A-Za-z0-9_]*\)\.[A-Za-z0-9_]* .*$/\1/p' "$MUNIN_STATEFILE" | uniq
return 0
elif [ $# -eq 1 ]; then
local FIELD="$1"
test_fieldname "$FIELD" || { echo "Invalid field name: $FIELD" >&2; return 1; }
sed -ne "2,\$s/^$FIELD\.\([A-Za-z0-9_]*\) .*$/\1/p" "$MUNIN_STATEFILE" | uniq
return 0
fi
local FIELD="$1"
test_fieldname "$FIELD" || { echo "Invalid field name: $FIELD" >&2; return 1; }
shift
local ATTR=""
while [ $# -gt 0 ]; do
ATTR="$1"
test_fieldname "$ATTR" || { echo "Invalid attribute name: $ATTR" >&2; return 1; }
shift
echo "$(sed -ne "2,\$s/^$FIELD\.$ATTR \(.*\)$/\1/p" "$MUNIN_STATEFILE" | tail -n 1)"
done
}
# Assigns variables for all attributes stored in the plugin's statefile.
#
# `assign_statefile` takes a single parameter as field name and assigns
# variables for all attributes stored for this field.
#
# Example:
# write_statefile my_field foo value_of_foo bar value_of_bar
# commit_statefile
# assign_statefile my_field
#
# The above example will assign the variables `$foo` and `$bar` with the
# values "value_of_foo" resp. "value_of_bar".
#
# Usage:
# assign_statefile my_field
assign_statefile () {
[ -f "$MUNIN_STATEFILE" ] || return 0
[ "$(head -n 1 "$MUNIN_STATEFILE")" == "%MUNIN-STATE1.0" ] || { echo "Invalid statefile header" >&2; return 1; }
local FIELD="$1"
test_fieldname "$FIELD" || { echo "Invalid field name: $FIELD" >&2; return 1; }
shift
local ATTR=""
local VALUE=""
while IFS=" " read -r ATTR VALUE; do
eval "$ATTR=\"\$VALUE\""
done < <(sed -ne "2,\$s/^$FIELD\.\([A-Za-z0-9_]*\) \(.*\)$/\1 \2/p" "$MUNIN_STATEFILE")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment