Changes: * Use sudo in service.sh for Darwin to find user services if not running as root * Fix parsing the output of softwareupdate command on Darwin in update.sh * Better document usage of sudo in docs/Sudo.md
232 lines
7.4 KiB
Bash
Executable file
232 lines
7.4 KiB
Bash
Executable file
#!/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"
|