Posted on 08-16-2017 11:33 AM
I'm writing a script to remove our old casper check files and replace them with the new and updated versions (switching JSS's).
Can Cat be used in one script twice to create two different files? When running my first instance of Cat it tries running the contents of the script. Maybe I missed a space somewhere? The second instance of Cat runs fine.
echo "Creating new Script"
# Create Casper Check Script
/bin/cat <<EOF >/Library/Scripts/caspercheck.sh
#!/bin/bash
#
# User-editable variables
#
# For the fileURL variable, put the complete address
# of the zipped Casper QuickAdd installer package
fileURL="myfileserver"
# For the jss_server_address variable, put the complete
# fully qualified domain name address of your Casper server
jss_server_address="myjss"
# For the jss_server_address variable, put the port number
# of your Casper server. This is usually 8443; change as
# appropriate.
jss_server_port="8443"
# For the log_location variable, put the preferred
# location of the log file for this script. If you
# don't have a preference, using the default setting
# should be fine.
log_location="/var/log/caspercheck.log"
#
# The variables below this line should not need to be edited.
# Use caution if doing so.
#
quickadd_dir="/var/root/quickadd"
quickadd_zip="$quickadd_dir/quickadd.zip"
quickadd_installer="$quickadd_dir/casper.pkg"
quickadd_timestamp="$quickadd_dir/quickadd_timestamp"
#
# Begin function section
# =======================
#
# Function to provide custom curl options
myCurl () { /usr/bin/curl -k -L --retry 3 --silent --show-error "$@"; }
# Function to provide logging of the script's actions to
# the log file defined by the log_location variable
ScriptLogging(){
DATE=`date +%Y-%m-%d %H:%M:%S`
LOG="$log_location"
echo "$DATE" " $1" >> $LOG
}
CheckForNetwork(){
# Determine if the network is up by looking for any non-loopback network interfaces.
local test
if [[ -z "${NETWORKUP:=}" ]]; then
test=$(ifconfig -a inet 2>/dev/null | sed -n -e '/127.0.0.1/d' -e '/0.0.0.0/d' -e '/inet/p' | wc -l)
if [[ "${test}" -gt 0 ]]; then
NETWORKUP="-YES-"
else
NETWORKUP="-NO-"
fi
fi
}
CheckSiteNetwork (){
# CheckSiteNetwork function adapted from Facebook's check_corp function script.
# check_corp script available on Facebook's IT-CPE Github repo:
#
# check_corp:
# This script verifies a system is on the corporate network.
# Input: CORP_URL= set this to a hostname on your corp network
# Optional ($1) contains a parameter that is used for testing.
# Output: Returns a check_corp variable that will return "True" if on
# corp network, "False" otherwise.
# If a parameter is passed ($1), the check_corp variable will return it
# This is useful for testing scripts where you want to force check_corp
# to be either "True" or "False"
# USAGE:
# check_corp # No parameter passed
# check_corp "True" # Parameter of "True" is passed and returned
site_network="False"
ping=`host -W .5 $jss_server_address`
# If the ping fails - site_network="False"
[[ $? -eq 0 ]] && site_network="True"
# Check if we are using a test
[[ -n "$1" ]] && site_network="$1"
}
#
# The update_quickadd function checks the timestamp of the fileURL variable and compares it against a locally
# cached timestamp. If the hosted file's timestamp is newer, then the Casper
# QuickAdd installer gets downloaded and extracted into the target directory.
#
# This function uses the myCurl function defined at the top of the script.
#
update_quickadd () {
# Create the destination directory if needed
if [[ ! -d "$quickadd_dir" ]]; then
mkdir "$quickadd_dir"
fi
# If needed, remove existing files from the destination directory
if [[ -d "$quickadd_dir" ]]; then
/bin/rm -rf "$quickadd_dir"/*
fi
# Get modification date of fileURL
modDate=$(myCurl --head $fileURL 2>/dev/null | awk -F': ' '/Last-Modified/{print $2}')
# Downloading Casper agent installer
ScriptLogging "Downloading Casper agent installer from server."
myCurl --output "$quickadd_zip" $fileURL
# Check to make sure download occurred
if [[ ! -f "$quickadd_zip" ]]; then
ScriptLogging "$quickadd_zip not found. Exiting CasperCheck."
ScriptLogging "======== CasperCheck Finished ========"
exit 0
fi
# Verify that the downloaded zip file is a valid zip archive.
zipfile_chk=`/usr/bin/unzip -tq $quickadd_zip > /dev/null; echo $?`
if [ "$zipfile_chk" -eq 0 ]; then
ScriptLogging "Downloaded zip file appears to be a valid zip archive. Proceeding."
else
ScriptLogging "Downloaded zip file appears to be corrupted. Exiting CasperCheck."
ScriptLogging "======== CasperCheck Finished ========"
rm "$quickadd_zip"
exit 0
fi
# Unzip the Casper agent install into the destination directory
# and remove the __MACOSX directory, which is created as part of
# the uncompression process from the destination directory.
/usr/bin/unzip "$quickadd_zip" -d "$quickadd_dir";/bin/rm -rf "$quickadd_dir"/__MACOSX
# Rename newly-downloaded installer to be casper.pkg
mv "$(/usr/bin/find $quickadd_dir -maxdepth 1 ( -iname *.pkg -o -iname *.mpkg ))" "$quickadd_installer"
# Remove downloaded zip file
if [[ -f "$quickadd_zip" ]]; then
/bin/rm -rf "$quickadd_zip"
fi
# Add the quickadd_timestamp file to the destination directory.
# This file is used to help verify if the current Casper agent
# installer is already cached on the machine.
if [[ ! -f "$quickadd_timestamp" ]]; then
echo $modDate > "$quickadd_timestamp"
fi
}
CheckTomcat (){
# Verifies that the JSS's Tomcat service is responding via its assigned port.
tomcat_chk=`nc -z -w 5 $jss_server_address $jss_server_port > /dev/null; echo $?`
if [ "$tomcat_chk" -eq 0 ]; then
ScriptLogging "Machine can connect to $jss_server_address over port $jss_server_port. Proceeding."
else
ScriptLogging "Machine cannot connect to $jss_server_address over port $jss_server_port. Exiting CasperCheck."
ScriptLogging "======== CasperCheck Finished ========"
exit 0
fi
}
CheckInstaller (){
# Compare timestamps and update the Casper agent
# installer if needed.
modDate=$(myCurl --head $fileURL 2>/dev/null | awk -F': ' '/Last-Modified/{print $2}')
if [[ -f "$quickadd_timestamp" ]]; then
cachedDate=$(cat "$quickadd_timestamp")
if [[ "$cachedDate" == "$modDate" ]]; then
ScriptLogging "Current Casper installer already cached."
else
update_quickadd
fi
else
update_quickadd
fi
}
CheckBinary (){
# Identify location of jamf binary.
#
# If the jamf binary is not found, this check will return a
# null value. This null value is used by the CheckCasper
# function, in the "Checking for the jamf binary" section
# of the function.
jamf_binary=`/usr/bin/which jamf`
if [[ "$jamf_binary" == "" ]] && [[ -e "/usr/sbin/jamf" ]] && [[ ! -e "/usr/local/bin/jamf" ]]; then
jamf_binary="/usr/sbin/jamf"
elif [[ "$jamf_binary" == "" ]] && [[ ! -e "/usr/sbin/jamf" ]] && [[ -e "/usr/local/bin/jamf" ]]; then
jamf_binary="/usr/local/bin/jamf"
elif [[ "$jamf_binary" == "" ]] && [[ -e "/usr/sbin/jamf" ]] && [[ -e "/usr/local/bin/jamf" ]]; then
jamf_binary="/usr/local/bin/jamf"
fi
}
InstallCasper () {
# Check for the cached Casper QuickAdd installer and run it
# to fix problems with Casper being able to communicate with
# the Casper server
if [[ ! -e "$quickadd_installer" ]] ; then
ScriptLogging "Casper installer is missing. Downloading."
/bin/rm -rf "$quickadd_timestamp"
update_quickadd
fi
if [[ -e "$quickadd_installer" ]] ; then
ScriptLogging "Casper installer is present. Installing."
/usr/sbin/installer -dumplog -verbose -pkg "$quickadd_installer" -target /
ScriptLogging "Casper agent has been installed."
fi
}
CheckCasper () {
# CheckCasper function adapted from Facebook's jamf_verify.sh script.
# jamf_verify script available on Facebook's IT-CPE Github repo:
# Link: https://github.com/facebook/IT-CPE
# Checking for the jamf binary
CheckBinary
if [[ "$jamf_binary" == "" ]]; then
ScriptLogging "Casper's jamf binary is missing. It needs to be reinstalled."
InstallCasper
CheckBinary
fi
# Verifying Permissions
/usr/bin/chflags noschg $jamf_binary
/usr/bin/chflags nouchg $jamf_binary
/usr/sbin/chown root:wheel $jamf_binary
/bin/chmod 755 $jamf_binary
# Verifies that the JSS is responding to a communication query
# by the Casper agent. If the communication check returns a result
# of anything greater than zero, the communication check has failed.
# If the communication check fails, reinstall the Casper agent using
# the cached installer.
jss_comm_chk=`$jamf_binary checkJSSConnection > /dev/null; echo $?`
if [[ "$jss_comm_chk" -eq 0 ]]; then
ScriptLogging "Machine can connect to the JSS on $jss_server_address."
elif [[ "$jss_comm_chk" -gt 0 ]]; then
ScriptLogging "Machine cannot connect to the JSS on $jss_server_address."
ScriptLogging "Reinstalling Casper agent to fix problem of Casper not being able to communicate with the JSS."
InstallCasper
CheckBinary
fi
# Checking if machine can run a manual trigger
# This section will need to be edited if the policy
# being triggered has different options than the policy
# described below:
#
# Trigger: iscasperup
# Plan: Run Script iscasperonline.sh
#
# The iscasperonline.sh script contains the following:
#
# | #!/bin/sh
# |
# | echo "up"
# |
# | exit 0
#
jamf_policy_chk=`$jamf_binary policy -trigger iscasperup | grep "Script result: up"`
# If the machine can run the specified policy, exit the script.
if [[ -n "$jamf_policy_chk" ]]; then
ScriptLogging "Casper enabled and able to run policies"
# If the machine cannot run the specified policy,
# reinstall the Casper agent using the cached installer.
elif [[ ! -n "$jamf_policy_chk" ]]; then
ScriptLogging "Reinstalling Casper agent to fix problem of Casper not being able to run policies"
InstallCasper
CheckBinary
fi
}
#
# End function section
# ====================
#
# The functions and variables defined above are used
# by the section below to check if the network connection
# is live, if the machine is on a network where
# the Casper JSS is accessible, and if the Casper agent on the
# machine can contact the JSS and run a policy.
#
# If the Casper agent on the machine cannot run a policy, the appropriate
# functions run and repair the Casper agent on the machine.
#
ScriptLogging "======== Starting CasperCheck ========"
# Wait up to 60 minutes for a network connection to become
# available which doesn't use a loopback address. This
# condition which may occur if this script is run by a
# LaunchDaemon at boot time.
#
# The network connection check will occur every 5 seconds
# until the 60 minute limit is reached.
ScriptLogging "Checking for active network connection."
CheckForNetwork
i=1
while [[ "${NETWORKUP}" != "-YES-" ]] && [[ $i -ne 720 ]]
do
sleep 5
NETWORKUP=
CheckForNetwork
echo $i
i=$(( $i + 1 ))
done
# If no network connection is found within 60 minutes,
# the script will exit.
if [[ "${NETWORKUP}" != "-YES-" ]]; then
ScriptLogging "Network connection appears to be offline. Exiting CasperCheck."
fi
if [[ "${NETWORKUP}" == "-YES-" ]]; then
ScriptLogging "Network connection appears to be live."
# Sleeping for 120 seconds to give WiFi time to come online.
ScriptLogging "Pausing for two minutes to give WiFi and DNS time to come online."
sleep 120
CheckSiteNetwork
if [[ "$site_network" == "False" ]]; then
ScriptLogging "Unable to verify access to site network. Exiting CasperCheck."
fi
if [[ "$site_network" == "True" ]]; then
ScriptLogging "Access to site network verified"
CheckTomcat
CheckInstaller
CheckCasper
fi
fi
ScriptLogging "======== CasperCheck Finished ========"
exit 0
EOF
The output I receive in terminal at this section is"
Password:
Unloading Old Daemon
Removing Launch Daemon
Removing Old Script
Creating new Script
Usage: host [-aCdlriTwv] [-c class] [-N ndots] [-t type] [-W time]
[-R number] [-m flag] hostname [server]
-a is equivalent to -v -t ANY
-c specifies query class for non-IN data
-C compares SOA records on authoritative nameservers
-d is equivalent to -v
-l lists all hosts in a domain, using AXFR
-i IP6.INT reverse lookups
-N changes the number of dots allowed before root lookup is done
-r disables recursive processing
-R specifies number of retries for UDP packets
-s a SERVFAIL response should stop query
-t specifies the query type
-T enables TCP/IP mode
-v enables verbose output
-w specifies to wait forever for a reply
-W specifies how long to wait for a reply
-4 use IPv4 query transport only
-6 use IPv6 query transport only
-m set memory debugging flag (trace|record|usage)
UnZip 6.00 of 20 April 2009, by Info-ZIP. Maintained by C. Spieler. Send
bug reports using http://www.info-zip.org/zip-bug.html; see README for details.
Usage: unzip [-Z] [-opts[modifiers]] file[.zip] [list] [-x xlist] [-d exdir]
Default action is to extract files in list, except those in xlist, to exdir;
file[.zip] may be a wildcard. -Z => ZipInfo mode ("unzip -Z" for usage).
-p extract files to pipe, no messages -l list files (short format)
-f freshen existing files, create none -t test compressed archive data
-u update files, create if necessary -z display archive comment only
-v list verbosely/show version info -T timestamp archive to latest
-x exclude files that follow (in xlist) -d extract files into exdir
modifiers:
-n never overwrite existing files -q quiet mode (-qq => quieter)
-o overwrite files WITHOUT prompting -a auto-convert any text files
-j junk paths (do not make directories) -aa treat ALL files as text
-C match filenames case-insensitively -L make (some) names lowercase
-X restore UID/GID info -V retain VMS version numbers
-K keep setuid/setgid/tacky permissions -M pipe through "more" pager
See "unzip -hh" or unzip.txt for more help. Examples:
unzip data1 -x joe => extract all files except joe from zipfile data1.zip
unzip -p foo | more => send contents of foo.zip via pipe into program more
unzip -fo foo ReadMe => quietly replace existing ReadMe if archive file newer
/usr/bin/find: illegal option -- m
usage: find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]
find [-H | -L | -P] [-EXdsx] -f path [path ...] [expression]
usage: nc [-46AacCDdEFhklMnOortUuvz] [-K tc] [-b boundif] [-i interval] [-p source_port] [--apple-delegate-pid pid] [--apple-delegate-uuid uuid]
[-s source_ip_address] [-w timeout] [-X proxy_version]
[-x proxy_address[:port]] [hostname] [port[s]]
Other than this the script itself seems to work. My second instance of cat is as follows and works fine.
# Step #1: Write the plist file and place it in the correct location
# (If you need to adjust the plist, do it right here within lines
# 11-27.)
if [ -e "$daemon" ]
then
echo "$daemon already exists"
else
echo "Writing job plist to /Library/LaunchDaemons/com.company.caspercheck.plist..."
/bin/cat <<EOM >/Library/LaunchDaemons/com.company.caspercheck.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.company.caspercheck</string>
<key>ProgramArguments</key>
<array>
<string>sh</string>
<string>/Library/Scripts/caspercheck.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StartInterval</key>
<integer>590400</integer>
</dict>
</plist>
EOM
fi
# Step #2: Change file's owner, group, and permissions
# (The plist needs to be owned by root and not world/other writable for
# launchd to even run it as a system daemon.)
echo "Changing permissions on job definition..."
/usr/sbin/chown root:wheel /Library/LaunchDaemons/com.company.caspercheck.plist
/bin/chmod 755 /Library/LaunchDaemons/com.company.caspercheck.plist
# Step #3: Load the job into system's launchd
# (Remember: to unload this script you need to use launchctl as root,
# otherwise you'll be unloading (unsuccessfully) your regular user's
# jobs.)
echo "Loading job into launch controller and starting it..."
/bin/launchctl load /Library/LaunchDaemons/com.company.caspercheck.plist
/bin/launchctl start /Library/LaunchDaemons/com.company.caspercheck.plist