diff --git a/LaunchDaemon/de.mathias-kettner.check_mk.plist b/LaunchDaemon/de.mathias-kettner.check_mk.plist new file mode 100755 index 00000000000..a0aaac8e294 --- /dev/null +++ b/LaunchDaemon/de.mathias-kettner.check_mk.plist @@ -0,0 +1,36 @@ + + + + + EnvironmentVariables + + HOME + /var/root + PATH + /sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin + + Label + de.mathias-kettner.check_mk + ProgramArguments + + /usr/local/bin/check_mk_agent + + Sockets + + Listeners + + SockServiceName + 6556 + + + inetdCompatibility + + Wait + + + AbandonProcessGroup + + StandardErrorPath + /var/log/check_mk.err + + diff --git a/agents/check_mk_agent.macosx b/agents/check_mk_agent.macosx index a2b7d1aa65d..62af5db5b4b 100755 --- a/agents/check_mk_agent.macosx +++ b/agents/check_mk_agent.macosx @@ -1,44 +1,150 @@ -#!/bin/sh +#!/bin/bash +# Check_MK Agent for Mac OS/X # Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2 # This file is part of Checkmk (https://checkmk.com). It is subject to the terms and # conditions defined in the file COPYING, which is part of this source code package. -# NOTE: This agent has beed adapted from the Check_MK linux agent. +# NOTE: lnx_if requires iproute2mac to be installed (i.e. `brew install iproute2mac`) -# Remove locale settings to eliminate localized outputs where possible # Author: Christian Zigotzky +# Modified by Thomas Kaiser + +# Remove locale settings to eliminate localized outputs where possible export LC_ALL=C unset LANG -export MK_LIBDIR="/to/be/changed" -export MK_CONFDIR="/to/be/changed" +export MK_LIBDIR='/usr/local/lib/check_mk_agent' +export MK_CONFDIR='/etc/check_mk' +export MK_VARDIR='/var/lib/check_mk_agent' +export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + +# All executables in PLUGINSDIR will simply be executed and their +# ouput appended to the output of the agent. Plugins define their own +# sections and must output headers with '<<<' and '>>>' +PLUGINSDIR=$MK_LIBDIR/plugins + +# All executables in LOCALDIR will by executabled and their +# output inserted into the section <<>>. Please +# refer to online documentation for details about local checks. +LOCALDIR=$MK_LIBDIR/local -# Optionally set a tempdir for all subsequent calls -#export TMPDIR= +# All files in SPOOLDIR will simply appended to the agent +# output if they are not outdated (see below) +SPOOLDIR=$MK_VARDIR/spool -# close standard input (for security reasons) and stderr +# close standard input (for security reasons) and stderr when not +# explicitly in debug mode. if [ "$1" = -d ] then set -xv else - exec /dev/null + : + # exec <&- 2>/dev/null fi +function run_mrpe() { + local descr=$1 + shift + local cmdline=$* + + echo '<<>>' + + PLUGIN=${cmdline%% *} + OUTPUT=$(eval "$cmdline") + + echo -n "(${PLUGIN##*/}) $descr $? $OUTPUT" | tr \\n \\1 + echo +} -echo "<<>>" -echo "Version: 1.7.0i1" -echo "AgentOS: macosx" -echo "Hostname: $(hostname)" -echo "AgentDirectory: $MK_CONFDIR" -echo "DataDirectory: $MK_VARDIR" -echo "SpoolDirectory: $SPOOLDIR" -echo "PluginsDirectory: $PLUGINSDIR" -echo "LocalDirectory: $LOCALDIR" +export -f run_mrpe -osver="$(uname -r)" +# Runs a command asynchronous by use of a cache file. Usage: +# run_cached [-s] NAME MAXAGE +# -s creates the section header <<<$NAME>>> +# -m mrpe-mode: stores exit code with the cache +# -ma mrpe-mode with age: stores exit code with the cache and adds the cache age +# NAME is the name of the section (also used as cache file name) +# MAXAGE is the maximum cache livetime in seconds +function run_cached () { + local NOW + NOW=$(date +%s) + local section= + local mrpe=0 + local append_age=0 + # TODO: this function is unable to handle mulitple args at once + # for example: -s -m won't work, it is read as single token "-s -m" + if [ "$1" = -s ] ; then local section="echo '<<<$2:cached($NOW,$3)>>>' ; " ; shift ; fi + if [ "$1" = -m ] ; then local mrpe=1 ; shift ; fi + if [ "$1" = "-ma" ] ; then local mrpe=1 ; local append_age=1 ; shift ; fi + local NAME=$1 + local MAXAGE=$2 + shift 2 + local CMDLINE=$section$* + + if [ ! -d "$MK_VARDIR/cache" ]; then mkdir -p "$MK_VARDIR/cache" ; fi + if [ "$mrpe" = 1 ] ; then + CACHEFILE="$MK_VARDIR/cache/mrpe_$NAME.cache" + else + CACHEFILE="$MK_VARDIR/cache/$NAME.cache" + fi + + # Check if the creation of the cache takes suspiciously long and kill the + # process if the age (access time) of $CACHEFILE.new is twice the MAXAGE. + # Output the eventually already cached section anyways and start the cache + # update again. + if [ -e "$CACHEFILE.new" ] ; then + local CF_ATIME + CF_ATIME=$(stat -f %Sa -t %s "$CACHEFILE.new") + if [ $((NOW - CF_ATIME)) -ge $((MAXAGE * 2)) ] ; then + # Kill the process still accessing that file in case + # it is still running. This avoids overlapping processes! + fuser -k -9 "$CACHEFILE.new" >/dev/null 2>&1 + rm -f "$CACHEFILE.new" + fi + fi + + # Check if cache file exists and is recent enough + if [ -s "$CACHEFILE" ] ; then + local MTIME + MTIME=$(stat -f %Sm -t %s "$CACHEFILE") + local AGE + AGE=$((NOW - MTIME)) + if [ "$AGE" -le "$MAXAGE" ] ; then local USE_CACHEFILE=1 ; fi + # Output the file in any case, even if it is + # outdated. The new file will not yet be available + if [ $append_age -eq 1 ] ; then + # insert the cached-string before the pipe (first -e) + # or, if no pipe found (-e t) append it (third -e), + # but only once and on the second line (2!b) (first line is section header, + # all further lines are long output) + sed -e "2s/|/ (Cached: ${AGE}\/${MAXAGE}s)|/" -e t -e "2s/$/ (Cached: ${AGE}\/${MAXAGE}s)/" < "$CACHEFILE" + else + cat "$CACHEFILE" + fi + fi + + # Cache file outdated and new job not yet running? Start it + if [ -z "$USE_CACHEFILE" ] && [ ! -e "$CACHEFILE.new" ] ; then + # When the command fails, the output is throws away ignored + if [ $mrpe -eq 1 ] ; then + echo "set -o noclobber ; exec > \"$CACHEFILE.new\" || exit 1 ; run_mrpe $NAME \"$CMDLINE\" && mv \"$CACHEFILE.new\" \"$CACHEFILE\" || rm -f \"$CACHEFILE\" \"$CACHEFILE.new\"" | nohup /bin/bash >/dev/null 2>&1 & + else + echo "set -o noclobber ; exec > \"$CACHEFILE.new\" || exit 1 ; $CMDLINE && mv \"$CACHEFILE.new\" \"$CACHEFILE\" || rm -f \"$CACHEFILE\" \"$CACHEFILE.new\"" | nohup /bin/bash >/dev/null 2>&1 & + fi + fi +} + +# Make run_cached available for subshells (plugins, local checks, etc.) +export -f run_cached + +osver="$(sw_vers | sed 1d | tr "\n" " " | awk -F" " '{print $2" ("$4")"}')" + +echo '<<>>' +echo Version: 1.5.0p20 +echo AgentOS: macosx $osver echo '<<>>' -df -kPT hfs,apfs | sed 1d | \ +df -kPT hfs,apfs | egrep -v "Time Machine|com.apple.TimeMachine.localsnapshots|/Volumes/|/private/var/vm" | sed 1d | \ while read DEV REST; do TYPE=$(diskutil info "$DEV" | grep '^\s*Type' | cut -d: -f2 | tr -d '[:space:]') echo "$DEV $TYPE $REST" @@ -49,12 +155,14 @@ echo `sysctl -n vm.loadavg | tr -d '{}'` `top -l 1 -n 1 | egrep ^Processes: | awk '{print $4"/"$2;}'` `echo 'echo $$' | bash` `sysctl -n hw.ncpu` echo '<<>>' +pagesize=$(vm_stat | grep Mach | awk '{print $8}') +compressedpages=$(vm_stat | grep "stored in compressor:" | awk '{print $5}') echo "MemTotal: $(echo $(sysctl -n hw.memsize)/1024 | bc) kB" -echo "MemFree: $(echo "( $(vm_stat | grep speculative: | awk '{print $3}') + $(vm_stat | grep inactive: | awk '{print $3}') + $(vm_stat | grep free: | awk '{print $3}') ) * $(vm_stat | grep Mach | awk '{print $8}') / 1024" | bc) kB" -echo "SwapTotal: 0 kB" -echo "SwapFree: 0 kB" +echo "MemFree: $(echo "( $(vm_stat | grep speculative: | awk '{print $3}') + $(vm_stat | grep inactive: | awk '{print $3}') + $(vm_stat | grep free: | awk '{print $3}') ) * $pagesize / 1024" | bc) kB" +echo "SwapTotal: $(echo "$compressedpages * $pagesize / 1024" | bc) kB" +echo "SwapFree: $(echo "( $compressedpages - $(vm_stat | grep "occupied by compressor:" | awk '{print $5}') ) * $pagesize / 1024" | bc) kB" # FIXME: Just call vm_stat here, write a check plugin that uses that -# navite output of vm_stat +# native output of vm_stat echo '<<>>'; echo `date +%s` - `sysctl -n kern.boottime | cut -d' ' -f 4,7 | tr ',' '.' | @@ -63,17 +171,52 @@ tr -d ' '` | bc # checks plugins with subchecks for parsing that output. Maybe reduce # the output size by grepping away totally useless parts -echo '<<>>'; -date +'%s'; netstat -inb | egrep -v '(^Name|lo|plip)' | grep Link | awk '{ -print $1,$7,$5,$6,"0","0","0","0","0",$10,$8,$9,"0","0",$11,"0","0"; }' -# FIXME: send netstat -inb plain, write proper check plugins for -# clean parsing of the output +# Mac version of lnx_if. Needs `iproute2mac` to have been installed. +if hash ip 2>/dev/null; then + echo '<<>>' + + interfaces=$(ip a | grep '^\S' | grep -E -v '^(awdl0|utun)' | awk -F ':' '{ print $1 }') + counter=0 + echo "[start_iplink]" + while read -r eth; do + counter=$((counter+1)) + echo -n "${counter}: " + ip a show "$eth" + done <<< "$interfaces" + echo "[end_iplink]" + + echo '<<>>' + netstat=$(netstat -inbd | grep -E "$interfaces" | grep Link) + # Format: Name, IBytes, IPckts, IErr, Drop, 0 (fifo), 0 (frame), 0 (compressed), 0 (multicast), Coll, Obyts, OPckts, OErrs, 0 (drop), 0 (fifo), 0 (coll), 0 (drop) + # (Collisions & drops are not broken out into in/out, so I'm assuming receive for now) + echo "$netstat" | awk '{print "\t"$1":\t"$7"\t"$5"\t"$6"\t"$12"\t0\t0\t0\t0\t"$11"\t"$10"\t"$8"\t"$9"\t0\t0\t0\t0"}' + # Convert osx ifconfig to lnx_if format: + # Note: Wifi interfaces may have variable speeds each call + for eth in $interfaces; do + cur_ifconfig=$(ifconfig -v "$eth") + speed=$(echo "$cur_ifconfig" | grep -E "^\s*(down)?link rate:\s*" | cut -d " " -f3,4 | sed -E 's, (.)bps$,\1b/s,') + addr=$(echo "$cur_ifconfig" | grep -E '^\s*ether' | cut -d " " -f2) + link_detected=no + if echo "$cur_ifconfig" | grep -E 'status:\s*active' > /dev/null || [ "$eth" = "lo0" ]; then + link_detected=yes + fi + + echo "[$eth]" + echo -e "\tSpeed: ${speed:-Unknown}" + echo -e "\tLink detected: $link_detected" + echo -e "\tAddress: ${addr:-00:00:00:00:00:00}" + done +fi echo '<<>>' ps ax -o user,vsz,rss,pcpu,command | sed -e 1d -e 's/ *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) */(\1,\2,\3,\4) /' -# NTP seems to be enabled as a default -if which ntpq >/dev/null; then +# macOS 10.13 and above use timed, older variants ntpd +if [ -f /var/db/timed/com.apple.timed.plist ]; then + : + # TODO: parse /var/db/timed/com.apple.timed.plist and provide compatible output + # defaults read /var/db/timed/com.apple.timed TMSystemSource | grep TMTimeError | head -n1 | awk -F'"' '{print $2}' +elif which ntpq >/dev/null; then echo '<<>>' ntpq -np | sed -e 1,2d -e 's/^\(.\)/\1 /' -e 's/^ /%/' fi @@ -84,43 +227,112 @@ netstat -ntfinet | awk ' /^tcp/ { c[$6]++; } END { for (x in c) { print x, c[x]; # Fileinfo-Check: put patterns for files into /etc/check_mk/fileinfo.cfg -perl -e ' -use File::Glob "bsd_glob"; -my @patterns = (); -foreach (bsd_glob("$ARGV[0]/fileinfo.cfg"), bsd_glob("$ARGV[0]/fileinfo.d/*")) { - open my $handle, "<", $_ or next; - while (<$handle>) { - chomp; - next if /^\s*(#|$)/; - my $pattern = $_; - $pattern =~ s/\$DATE:(.*?)\$/substr(`date +"$1"`, 0, -1)/eg; - push @patterns, $pattern; - } - warn "error while reading $_: $!\n" if $!; - close $handle; -} -exit if ! @patterns; - -print "<<>>\n", time, "\n[[[header]]]\nname|status|size|time\n[[[content]]]\n"; - -foreach (@patterns) { - foreach (bsd_glob("$_")) { - if (! -f) { - print "$_|missing\n" if ! -d; - } elsif (my @infos = stat) { - print "$_|ok|$infos[7]|$infos[9]\n"; - } else { - print "$_|stat failed: $!\n"; - } - } -} -' -- "$MK_CONFDIR" +if [ -r "$MK_CONFDIR/fileinfo.cfg" ] ; then + echo '<<>>' + date +%s + + OLD_IFS=$IFS + IFS=' +' + while read -r pattern; do + case $pattern in + /*) for file in $pattern; do + stat -f "%N|%z|%m" "$file" 2>/dev/null || echo "$file|missing|`date +%s`" + done ;; + esac + done < "$MK_CONFDIR/fileinfo.cfg" + IFS=$OLD_IFS +fi -if type tmutil >/dev/null +# Get statistics about monitored jobs. Below the job directory there +# is a sub directory per user that ran a job. That directory must be +# owned by the user so that a symlink or hardlink attack for reading +# arbitrary files can be avoided. +JOB_DIR="$MK_VARDIR/job" +if [[ -d "$JOB_DIR" && -x "$JOB_DIR" ]]; then + echo '<<>>' + for cron_output in $JOB_DIR/**/*; do + echo "==> $(basename "$cron_output") <==" + cat "$cron_output" + done +fi + +# temperatures and sensors, requires HardwareMonitor.app or osx-cpu-temp +if [ -x /Applications/HardwareMonitor.app/Contents/MacOS/hwmonitor ]; then + echo '<<>>' + /Applications/HardwareMonitor.app/Contents/MacOS/hwmonitor -c | grep " C$" | while read ; do + Temp=$(awk -F": " '{print $2}' <<<$REPLY | tr -d -c '[:digit:]') + case $REPLY in + SMART*) + Sensor="$(sed 's/^SMART Disk //' <<<"$REPLY" | cut -d'(' -f1)" + echo "${Sensor}|enabled|disk-thermal|${Temp}000|50000|passive|60000|critical" + ;; + "SMC DRIVE BAY"*) + Sensor="$(sed 's/^SM. //' <<<"$REPLY" | awk -F": " '{print $1}')" + echo "${Sensor}|enabled|disk-thermal|${Temp}000|50000|passive|60000|critical" + ;; + SMC*|SMB*) + Sensor="$(sed 's/^SM. //' <<<"$REPLY" | awk -F": " '{print $1}')" + echo "${Sensor}|enabled|thermal|${Temp}000|70000|passive|90000|critical" + ;; + esac + done +elif type osx-cpu-temp >/dev/null +then + echo '<<>>' + echo "CPU A PROXIMITY|enabled|thermal|$(osx-cpu-temp | tr -d -c '[:digit:]')00|70000|passive|90000|critical" +fi + +# Check SMART if smartmontools are installed, for NVME Check_MK 1.6 or above is needed +if type smartctl >/dev/null then - echo '<<>>' - tmutil latestbackup 2>&1 + echo '<<>>' + diskutil list | grep -v 'virtual)' | awk -F" " '/(internal)/ {print $1}' | while read ; do + DNAME="$(smartctl -d ata -i -f brief $REPLY | grep -v 'Family' | grep -E 'Model|^Serial Number' | sed 's/^.*\( .*\).*$/\1/' | tr '\n' '_' | sed -e 's/ //g' -e 's/ /_/g' -e 's/_$//' -e 's/^APPLE_//')" + if [ -n "${DNAME}" ]; then + # SATA + MODEL="$(sed 's/\_[^.]\{0,15\}$//g' <<<${DNAME})" + smartctl -v 9,raw48 -A $REPLY | grep -E "Offline|Always" | sed "s|^|$DNAME ATA $MODEL |" + else + DNAME="$(smartctl -d nvme -i -f brief $REPLY | grep -v 'Family' | grep -E 'Model|^Serial Number' | sed 's/^.*\( .*\).*$/\1/' | tr '\n' '_' | sed -e 's/ //g' -e 's/ /_/g' -e 's/_$//' -e 's/^APPLE_//')" + if [ -n "${DNAME}" ]; then + # NVME + echo "$REPLY NVME $(sed 's/\_[^.]\{0,15\}$//g' <<<${DNAME})" + smartctl -d nvme -A $REPLY | sed -e '1,5d; /^$/d' + fi + fi + done +fi + +# query security updates in Apt compatible way, check every 24 hours +RestartNeeded=0 +echo '<<>>' +IsOld=$(find /var/run/de.arts-others.softwareupdatecheck -mtime 1 2>/dev/null) +if [ $? -ne 0 -o "X${IsOld}" = "X/var/run/de.arts-others.softwareupdatecheck" ]; then + # file doesn't exist or is older than 24 hours: let's (re)create it + (softwareupdate -l 2>/dev/null | grep recommended >/var/run/de.arts-others.softwareupdatecheck) & +fi +if [ -s /var/run/de.arts-others.softwareupdatecheck ]; then + awk -F',' '{print $1}' >>' +if [ ${RestartNeeded} -gt 1 ]; then + echo "(check_mk_agent.macosx) Reboot%20needed 1 WARN - ${RestartNeeded} pending security updates require a restart" +elif [ ${RestartNeeded} -gt 0 ]; then + echo "(check_mk_agent.macosx) Reboot%20needed 1 WARN - 1 pending security update requires a restart" +else + echo "(check_mk_agent.macosx) Reboot%20needed 0 OK - no reboot required" fi ############################### @@ -128,22 +340,197 @@ fi ############################### # *OSX SW Raid status # *launchctl daemon status -# *hw sensors, how to query them? # *OSX Server specific stuff, LDAP, etc... # *Rewrite cpu / ps check to be faster - takes >1s on my laptop # ioreg -l zeigt etliche interessante Inventurdaten # MK's Remote Plugin Executor -if [ -e "/etc/mrpe.cfg" ] +if [ -e "$MK_CONFDIR/mrpe.cfg" ] then - echo '<<>>' - grep -Ev '^[[:space:]]*($|#)' "/etc/mrpe.cfg" | \ + grep -Ev '^[[:space:]]*($|#)' "$MK_CONFDIR/mrpe.cfg" | \ while read descr cmdline do - PLUGIN=${cmdline%% *} - OUTPUT=$(eval "$cmdline") - echo "(${PLUGIN##*/}) $descr $? $OUTPUT" | tr \\n \\1 - echo + interval= + args="-m" + # NOTE: Due to an escaping-related bug in some old bash versions + # (3.2.x), we have to use an intermediate variable for the pattern. + pattern='\(([^\)]*)\)[[:space:]](.*)' + if [[ $cmdline =~ $pattern ]] + then + parameters=${BASH_REMATCH[1]} + cmdline=${BASH_REMATCH[2]} + + # split multiple parameter assignments + for par in $(echo "$parameters" | tr ":" "\n") + do + # split each assignment + key=$(echo "$par" | cut -d= -f1) + value=$(echo "$par" | cut -d= -f2) + + if [ "$key" = "interval" ] ; then + interval=$value + elif [ "$key" = "appendage" ] ; then + args="-ma" + fi + done + fi + + if [ -z "$interval" ] + then + run_mrpe "$descr" "$cmdline" + else + run_cached "$args" "$descr" "$interval" "$cmdline" + fi + done +fi + +# MK's runas Executor +if [ -e "$MK_CONFDIR/runas.cfg" ] +then + grep -Ev '^[[:space:]]*($|#)' "$MK_CONFDIR/runas.cfg" | \ + while read type user include + do + if [ -d "$include" -o \( "$type" == "mrpe" -a -f "$include" \) ] ; then + PREFIX="" + if [ "$user" != "-" ] ; then + PREFIX="su $user -c " + fi + + # mrpe includes + if [ "$type" == "mrpe" ] ; then + grep -Ev '^[[:space:]]*($|#)' "$include" | \ + while read descr cmdline + do + interval= + # NOTE: Due to an escaping-related bug in some old bash + # versions (3.2.x), we have to use an intermediate variable + # for the pattern. + pattern='\(([^\)]*)\)[[:space:]](.*)' + if [[ $cmdline =~ $pattern ]] + then + parameters=${BASH_REMATCH[1]} + cmdline=${BASH_REMATCH[2]} + + # split multiple parameter assignments + for par in $(echo "$parameters" | tr ":" "\n") + do + # split each assignment + IFS='=' read key value <<< $par + if [ "$key" = "interval" ] + then + interval=$value + # no other parameters supported currently + fi + done + fi + + if [ -n "$PREFIX" ] ; then + cmdline="$PREFIX\'$cmdline\'" + fi + if [ -z "$interval" ] + then + run_mrpe "$descr" "$cmdline" + else + run_cached -m "$descr" "$interval" "$cmdline" + fi + done + + # local and plugin includes + elif [ "$type" == "local" -o "$type" == "plugin" ] ; then + if [ "$type" == "local" ] ; then + echo "<<>>" + fi + + find "$include" -executable -type f | \ + while read filename + do + if [ -n "$PREFIX" ] ; then + cmdline="$PREFIX\"$filename\"" + else + cmdline=$filename + fi + + $cmdline + done + fi + fi + done +fi + +function is_valid_plugin () { + # NOTE: Due to an escaping-related bug in some old bash versions + # (3.2.x), we have to use an intermediate variable for the pattern. + pattern='\.dpkg-(new|old|temp)$' + #TODO Maybe we should change this mechanism + # shellcheck disable=SC2015 + [[ -f "$1" && -x "$1" && ! "$1" =~ $pattern ]] && true || false +} + +# Local checks +if cd "${LOCALDIR}" ; then + echo '<<>>' + for skript in ./*; do + if is_valid_plugin "$skript"; then + ./"$skript" + fi + done + # Call some plugins only every X'th second + for skript in [1-9]*/* ; do + if is_valid_plugin "$skript"; then + run_cached "local_${skript//\//\\}" "${skript%/*}" "$skript" + fi + done +fi + +# Plugins +if cd "${PLUGINSDIR}"; then + for skript in ./*; do + if is_valid_plugin "$skript"; then + ./"$skript" + fi + done + # Call some plugins only every Xth second + for skript in [1-9]*/* ; do + if is_valid_plugin "$skript"; then + run_cached "plugins_${skript//\//\\}" "${skript%/*}" "$skript" + fi + done +fi + +# Agent output snippets created by cronjobs, etc. +if [ -d "${SPOOLDIR}" ] +then + pushd "${SPOOLDIR}" || exit > /dev/null + now=$(date +%s) + + for file in * + do + test "$file" = "*" && break + # output every file in this directory. If the file is prefixed + # with a number, then that number is the maximum age of the + # file in seconds. If the file is older than that, it is ignored. + maxage="" + part="$file" + + # Each away all digits from the front of the filename and + # collect them in the variable maxage. + while [ "${part/#[0-9]/}" != "$part" ] + do + maxage=$maxage${part:0:1} + part=${part:1} + done + + # If there is at least one digit, than we honor that. + if [ "$maxage" ] ; then + mtime=$(stat -f %Sm -t %s "$file") + if [ $((now - mtime)) -gt "$maxage" ] ; then + continue + fi + fi + + # Output the file + cat "$file" done + popd || exit > /dev/null fi diff --git a/agents/mk-job b/agents/mk-job index 94b0f461c89..1f9bc19a664 100755 --- a/agents/mk-job +++ b/agents/mk-job @@ -51,8 +51,13 @@ if [ ! -w "$RUNNING_FILE" ] ; then exec "$@" fi - -/usr/bin/time -o "$RUNNING_FILE" --append \ +if [ "$(uname)" == "Darwin" ]; then + # For OSX: requires GNU-time inzstalled (`brew install gnu-time`) + time=/usr/local/bin/gtime +else + time=/usr/bin/time +fi +"$time" -o "$RUNNING_FILE" --append \ -f "exit_code %x\nreal_time %E\nuser_time %U\nsystem_time %S\nreads %I\nwrites %O\nmax_res_kbytes %M\navg_mem_kbytes %K\ninvol_context_switches %c\nvol_context_switches %w" "$@" RC=$? mv "$RUNNING_FILE" "$OUTPUT_PATH/$IDENT" diff --git a/scripts/install_agent_osx.sh b/scripts/install_agent_osx.sh new file mode 100755 index 00000000000..2bedc0fc050 --- /dev/null +++ b/scripts/install_agent_osx.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# +# Script to install agent and mk-job on Mac OSX +# + +# Init +USER=$(whoami) +DEST_AGENT_PATH=/usr/local/lib/check_mk_agent +MK_JOB_OUTPUT_PATH=/var/lib/check_mk_agent/job +DEST_BIN_PATH=/usr/local/bin +SRC_PATH=../agents + +# Install dependencies +brew install smartmontools osx-cpu-temp gnu-time + +# Modify the plist file (remove cwd which was causing a launch error of "can't change to working dir") +sed -i '' '/WorkingDirectory/{N;d;}' LaunchDaemon/de.mathias-kettner.check_mk.plist + +# Create directories needed +mkdir -p "$DEST_AGENT_PATH" +mkdir -p "$DEST_AGENT_PATH/local" +mkdir -p "$DEST_AGENT_PATH/plugins" +sudo mkdir /etc/check_mk +sudo mkdir -p "${MK_JOB_OUTPUT_PATH}/${USER}" + +# Copy files to required location +cp "{$SRC_PATH}/check_mk_agent.macosx" "${DEST_AGENT_PATH}/" +cp "{$SRC_PATH}/mk-job" "${DEST_AGENT_PATH}/" +sudo cp ../LaunchDaemon/de.mathias-kettner.check_mk.plist /Library/LaunchDaemons/ +ln -s "${DEST_AGENT_PATH}/check_mk_agent.macosx" "${DEST_BIN_PATH}/check_mk_agent" +ln -s "${DEST_AGENT_PATH}/mk-job" "${DEST_BIN_PATH}/mk-job" +sudo touch /var/run/de.arts-others.softwareupdatecheck +sudo touch /var/log/check_mk.err + +# Permissions: agent +chmod +x "${DEST_AGENT_PATH}/check_mk_agent.macosx" +sudo chmod +rw /var/run/de.arts-others.softwareupdatecheck +sudo chmod +rw /var/log/check_mk.err +sudo chown -R root:admin "$DEST_AGENT_PATH" + +# Permissions: launch daemon +sudo chmod 644 /Library/LaunchDaemons/de.mathias-kettner.check_mk.plist + +# Permissions: mk-job +sudo chown "$USER" "${MK_JOB_OUTPUT_PATH}/${USER}" +sudo chmod +rx "$(dirname $MK_JOB_OUTPUT_PATH)" +sudo chmod +rx "$MK_JOB_OUTPUT_PATH" + +# Install LaunchDaemon +sudo launchctl load -w /Library/LaunchDaemons/de.mathias-kettner.check_mk.plist