#!/bin/sh # SPDX-FileCopyrightText: 2024 Splunk, Inc. # SPDX-License-Identifier: Apache-2.0 # shellcheck disable=SC1091 . "$(dirname "$0")"/common.sh # In AWK scripts in this file, the following are true: # FULLTEXT is used to capture the output for SHA256 checksum generation. # SPLUNKD is used to determine Splunk service status. if [ "$KERNEL" = "Linux" ] ; then if ! queryHaveCommand systemctl; then assertHaveCommand date assertHaveCommand chkconfig CMD='eval date ; /sbin/chkconfig --list' # shellcheck disable=SC2016 PARSE_0='NR==1 {DATE=$0 ; FULLTEXT=""}' # shellcheck disable=SC2016 PARSE_1='NR>1 { FULLTEXT = FULLTEXT $0 "\n" split($0, ARR) EVT="app=" ARR[1] for (i=0 ; i<7 ; i++) { split(ARR[i+2], STATE, ":") EVT = EVT " runlevel" i "=" STATE[2] } if (ARR[1] ~ /[Ss][Pp][Ll][Uu][Nn][Kk]/) { SPLUNKD=1 } printf "%s type=chkconfig %s\n", DATE, EVT }' MASSAGE="$PARSE_0 $PARSE_1" # Send the collected full text to openssl; this avoids any timing discrepancies # between when the information is collected and when we process it. # shellcheck disable=SC2016 POSTPROCESS='END { if (SPLUNKD==0) { printf "%s app=\"Splunk\" StartMode=Disabled\n", DATE } printf "%s %s", DATE, "file_hash=" printf "%s", FULLTEXT | "LD_LIBRARY_PATH=$SPLUNK_HOME/lib $SPLUNK_HOME/bin/openssl sha256" }' else assertHaveCommand systemctl assertHaveCommand date # Run the systemctl command to get all units and their state CMD='eval date; systemctl list-units --type=service --all' # shellcheck disable=SC2016 PARSE_0='NR==1 {DATE=$0}' # shellcheck disable=SC2016 PARSE_1=' # On header row, get lengths to the fields NR==2 { match($0, /^ */); leading=RLENGTH; match($0, /^.*DESC/); desclen=RLENGTH-4; FULLTEXT=""; next; }' # shellcheck disable=SC2016 PARSE_2='(NR > 2){ # Stop at the empty line if ( !NF ) { exit; } # Skip the leading spaces $0 = substr( $0, leading ); # the description spans fields so catch it seperately desc=substr( $0, desclen ); FULLTEXT = FULLTEXT $0 "\n" if ($1 ~ /[Ss][Pp][Ll][Uu][Nn][Kk]/) { SPLUNKD=1 } printf "%s type=systemctl UNIT=%s, LOADED=%s, ACTIVE=%s, SUB=%s, DESCRIPTION=\"%s\" \n",DATE, $1, $2, $3, $4, desc }' MASSAGE="$PARSE_0 $PARSE_1 $PARSE_2" # shellcheck disable=SC2016 POSTPROCESS='END { if (SPLUNKD==0) { printf "%s app=\"Splunk\" StartMode=Disabled\n", DATE } printf "%s %s", DATE, "file_hash=" printf "%s", FULLTEXT | "LD_LIBRARY_PATH=$SPLUNK_HOME/lib $SPLUNK_HOME/bin/openssl sha256" }' fi elif [ "$KERNEL" = "SunOS" ] ; then assertHaveCommand date assertHaveCommand svcs CMD='eval date ; svcs -H -a -o STATE,FMRI' # shellcheck disable=SC2016 PARSE_0='NR==1 {DATE=$0 ; FULLTEXT=""}' # shellcheck disable=SC2016 PARSE_1='NR>1 { STATE="State=\""$1"\"" idx=index($2,":") STARTNAME="StartName=\""substr($2,0,idx-1)"\"" APP="app=\""substr($2,idx+1)"\"" FULLTEXT=FULLTEXT $0 "\n" }' PARSE_2='/^legacy_run/ { STARTMODE="StartMode=\"Auto\"" }' PARSE_3='/^online/ { STARTMODE="StartMode=\"Auto\"" STATE="State=\"Running\"" }' PARSE_4='/^disabled/ { STARTMODE="StartMode=\"Disabled\"" STATE="State=\"Stopped\"" }' INLINE_PRINT='NR>1 && APP!=0 {printf "%s %s %s %s %s\n", DATE, APP, STARTMODE, STARTNAME, STATE}' MASSAGE="$PARSE_0 $PARSE_1 $PARSE_2 $PARSE_3 $PARSE_4 $INLINE_PRINT" # Send the collected full text to openssl; this avoids any timing discrepancies # between when the information is collected and when we process it. # shellcheck disable=SC2016 POSTPROCESS='END { if (SPLUNKD==0) { printf "%s app=\"Splunk\" StartMode=Disabled\n", DATE } printf "%s %s", DATE, "file_hash=" printf "%s", FULLTEXT | "LD_LIBRARY_PATH=$SPLUNK_HOME/lib $SPLUNK_HOME/bin/openssl sha256" }' elif [ "$KERNEL" = "Darwin" ] ; then assertHaveCommand date assertHaveCommand defaults assertHaveCommand dscl assertHaveCommand find assertHaveCommand ls # Get startup items CMD='eval date ; ls -1 /System/Library/StartupItems/ /Library/StartupItems/' # Get per-user startup items # shellcheck disable=SC2044 # For this to work properly when run as non-root, add a line to # an /etc/sudoers.d file (eg - /etc/sudoers.d/splunk) like this: # splunk ALL=(root) NOPASSWD: /usr/bin/find /Users -name loginwindow.plist if [ $(id -u) != 0 ]; then for PLIST_FILE in $(sudo -n /usr/bin/find /Users -name loginwindow.plist) ; do CMD=$CMD' ; echo '$PLIST_FILE': ; defaults read '$PLIST_FILE done else for PLIST_FILE in $(/usr/bin/find /Users -name loginwindow.plist) ; do CMD=$CMD' ; echo '$PLIST_FILE': ; defaults read '$PLIST_FILE done fi # shellcheck disable=SC2016 PARSE_0='NR==1 {DATE=$0}' # Retrieve path for system startup items # shellcheck disable=SC2016 PARSE_1='/^\/(System|Library)/ { split($0, tmparr, ":") PATH="file_path=\""tmparr[1] USER=0 START_MODE="StartMode=Auto" START_TYPE="StartType=startup" }' # Retrieve user information for user startup items. # shellcheck disable=SC2016 PARSE_2='/^\/Users/ { split($0, tmparr, "/") USER="user=" tmparr[3] START_MODE="StartMode=Auto" START_TYPE="StartType=login" }' # Retrieve the path for user startup items. # shellcheck disable=SC2016 PARSE_3='/[[:blank:]]*Path/ { split($0, path_arr, "=") num=split(path_arr[2], app_arr, "/") split(app_arr[num], app_final, ".") split(path_arr[2], path_final, "\"") APP="app=\"" app_final[1] "\"" FILE_PATH="file_path=\"" path_final[2] "\"" # Only print if we find a path. printf "%s %s %s %s %s %s\n", DATE, APP, START_MODE, START_TYPE, FILE_PATH, USER # Note that we found splunkd if app matches if (APP ~ /[Ss][Pp][Ll][Uu][Nn][Kk]/) { SPLUNKD=1 } }' # Retrieve the system startup item name from the output of "ls -1" # shellcheck disable=SC2016 PARSE_4='/^[^\/]/ { if (NR>1 && USER==0 && NF > 0) { APP="app=\""$0"\"" PATH=PATH$0"\"" printf "%s %s %s %s %s\n", DATE, APP, START_MODE, START_TYPE, PATH } # Note that we found splunkd if app matches if (APP ~ /[Ss][Pp][Ll][Uu][Nn][Kk]/) { SPLUNKD=1 } }' MASSAGE="$PARSE_0 $PARSE_1 $PARSE_2 $PARSE_3 $PARSE_4" POSTPROCESS='END { if (SPLUNKD==0) { printf "%s app=\"Splunk\" StartMode=Disabled\n", DATE } }' elif [ "$KERNEL" = "OpenBSD" ] ; then # For this to work when running as a non-root user, add the following # to /etc/doas.conf (replacing USERNAME with the user running the script): # permit nopass USERNAME cmd /usr/sbin/rcctl args ls started # permit nopass USERNAME cmd /usr/sbin/rcctl args ls failed # permit nopass USERNAME cmd /usr/sbin/rcctl args ls rogue if [ $(id -u) != 0 ]; then failed=" $(doas -n /usr/sbin/rcctl ls failed) " rogue=" $(doas -n /usr/sbin/rcctl ls rogue) " running=" $(doas -n /usr/sbin/rcctl ls started) " else failed=" $(/usr/sbin/rcctl ls failed) " rogue=" $(/usr/sbin/rcctl ls rogue) " running=" $(/usr/sbin/rcctl ls started) " fi enabled=" $(/usr/sbin/rcctl ls on) " for svc in $(/usr/sbin/rcctl ls all); do enabled=false echo $enabled | grep " $svc " && enabled=true failed=false echo $enabled | grep " $svc " && failed=true rogue=false echo $enabled | grep " $svc " && rogue=true state=stopped echo $enabled | grep " $svc " && state=running date "+%a %b %e %H:%M:%S %Z %Y type=rcctl app=$svc, enabled=$enabled, failed=$failed, rogue=$rogue, running=$running" done else # Exits failUnsupportedScript fi $CMD | tee "$TEE_DEST" | $AWK "$MASSAGE $POSTPROCESS" echo "Cmd = [$CMD]; | $AWK '$MASSAGE $POSTPROCESS'" >> "$TEE_DEST"