Saturday
This is an ever growing / changing library of common functions that I have created over the years. Fairly easy to use, just grab the common functions you need when creating your own scripts. Thought I would share with the community. Any feedback is welcome.
#!/bin/zsh
######################################################################################################
#
# Gobal "Common" variables (do not change these!)
#
######################################################################################################
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
LOGGED_IN_USER=$( scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
USER_DIR=$( dscl . -read /Users/${LOGGED_IN_USER} NFSHomeDirectory | awk '{ print $2 }' )
OS_PLATFORM=$(/usr/bin/uname -p)
[[ "$OS_PLATFORM" == 'i386' ]] && HWtype="SPHardwareDataType.0.cpu_type" || HWtype="SPHardwareDataType.0.chip_type"
SYSTEM_PROFILER_BLOB=$( /usr/sbin/system_profiler -json 'SPHardwareDataType')
MAC_SERIAL_NUMBER=$( echo $SYSTEM_PROFILER_BLOB | /usr/bin/plutil -extract 'SPHardwareDataType.0.serial_number' 'raw' -)
MAC_CPU=$( echo $SYSTEM_PROFILER_BLOB | /usr/bin/plutil -extract "${HWtype}" 'raw' -)
MAC_HADWARE_CLASS=$( echo $SYSTEM_PROFILER_BLOB | /usr/bin/plutil -extract 'SPHardwareDataType.0.machine_name' 'raw' -)
MAC_RAM=$( echo $SYSTEM_PROFILER_BLOB | /usr/bin/plutil -extract 'SPHardwareDataType.0.physical_memory' 'raw' -)
FREE_DISK_SPACE=$(($( /usr/sbin/diskutil info / | /usr/bin/grep "Free Space" | /usr/bin/awk '{print $6}' | /usr/bin/cut -c 2- ) / 1024 / 1024 / 1024 ))
MACOS_VERSION=$( sw_vers -productVersion | xargs)
SW_DIALOG="/usr/local/bin/dialog"
###################################################
#
# App Specfic variables (Feel free to change these)
#
###################################################
SUPPORT_DIR="/Library/Application Support/GiantEagle"
OVERLAY_ICON="${SUPPORT_DIR}/SupportFiles/DiskSpace.png"
SD_BANNER_IMAGE="${SUPPORT_DIR}/SupportFiles/GE_SD_BannerImage.png"
LOG_DIR="${SUPPORT_DIR}/logs"
LOG_FILE="${LOG_DIR}/AppDelete.log"
LOG_STAMP=$(echo $(/bin/date +%Y%m%d))
ICON_FILES="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/"
JSONOptions=$(mktemp /var/tmp/ClearBrowserCache.XXXXX)
BANNER_TEXT_PADDING=" " #Five space to accommodate our logo on the left side
SD_INFO_BOX_MSG=""
SD_WINDOW_TITLE="${BANNER_TEXT_PADDING}Disk Space Notification"
# Swift Dialog version requirements
[[ -e "/usr/local/bin/dialog" ]] && SD_VERSION=$( ${SW_DIALOG} --version) || SD_VERSION="0.0.0"
MIN_SD_REQUIRED_VERSION="2.3.3"
DIALOG_INSTALL_POLICY="install_SwiftDialog"
SHOW_DISK_USAGE="ShowDiskUsage"
####################################################################################################
#
# Functions
#
####################################################################################################
function create_log_directory ()
{
# Ensure that the log directory and the log files exist. If they
# do not then create them and set the permissions.
#
# RETURN: None
# If the log directory doesnt exist - create it and set the permissions
if [[ ! -d "${LOG_DIR}" ]]; then
/bin/mkdir -p "${LOG_DIR}"
/bin/chmod 755 "${LOG_DIR}"
fi
# If the log file does not exist - create it and set the permissions
if [[ ! -f "${LOG_FILE}" ]]; then
/usr/bin/touch "${LOG_FILE}"
/bin/chmod 644 "${LOG_FILE}"
fi
}
function logMe ()
{
# Basic two pronged logging function that will log like this:
#
# 20231204 12:00:00: Some message here
#
# This function logs both to STDOUT/STDERR and a file
# The log file is set by the $LOG_FILE variable.
#
# RETURN: None
echo "${1}" 1>&2
echo "$(/bin/date '+%Y%m%d %H:%M:%S'): ${1}\n" >> "${LOG_FILE}"
}
function check_swift_dialog_install ()
{
# Check to make sure that Swift Dialog is installed and functioning correctly
# Will install process if missing or corrupted
#
# RETURN: None
logMe "Ensuring that swiftDialog version is installed..."
if [[ ! -x "${SW_DIALOG}" ]]; then
logMe "Swift Dialog is missing or corrupted - Installing from JAMF"
install_swift_dialog
SD_VERSION=$( ${SW_DIALOG} --version)
fi
if ! is-at-least "${MIN_SD_REQUIRED_VERSION}" "${SD_VERSION}"; then
logMe "Swift Dialog is outdated - Installing version '${MIN_SD_REQUIRED_VERSION}' from JAMF..."
install_swift_dialog
else
logMe "Swift Dialog is currently running: ${SD_VERSION}"
fi
}
function install_swift_dialog ()
{
# Install Swift dialog From JAMF
# PARMS Expected: DIALOG_INSTALL_POLICY - policy trigger from JAMF
#
# RETURN: None
/usr/local/bin/jamf policy -trigger ${DIALOG_INSTALL_POLICY}
}
function welcomemsg ()
{
}
autoload 'is-at-least'
check_swift_dialog_install
create_infobox_message
welcomemsg
exit 0
function update_display_list ()
{
# setopt -s nocasematch
# This function updates the Swift Dialog list display with easy to implement parameter passing...
# The Swift Dialog native structure is very strict with the command structure...this routine makes
# it easier to implement
#
# Param list
#
# $1 - Action to be done ("Create", "Add", "Change", "Clear", "Info", "Show", "Done", "Update")
# ${2} - Affected item (2nd field in JSON Blob listitem entry)
# ${3} - Icon status "wait, success, fail, error, pending or progress"
# ${4} - Status Text
# $5 - Progress Text (shown below progress bar)
# $6 - Progress amount
# increment - increments the progress by one
# reset - resets the progress bar to 0
# complete - maxes out the progress bar
# If an integer value is sent, this will move the progress bar to that value of steps
# the GLOB :l converts any inconing parameter into lowercase
case "${1:l}" in
"create" | "show" )
# Display the Dialog prompt
eval "${DYNAMIC_DIALOG_BASE_STRING}"
;;
"add" )
# Add an item to the list
#
# $2 name of item
# $3 Icon status "wait, success, fail, error, pending or progress"
# $4 Optional status text
/bin/echo "listitem: add, title: ${2}, status: ${3}, statustext: ${4}" >> "${DIALOG_COMMAND_FILE}"
;;
"buttonaction" )
# Change button 1 action
/bin/echo 'button1action: "'${2}'"' >> "${DIALOG_COMMAND_FILE}"
;;
"buttonchange" )
# change text of button 1
/bin/echo "button1text: ${2}" >> "${DIALOG_COMMAND_FILE}"
;;
"buttondisable" )
# disable button 1
/bin/echo "button1: disable" >> "${DIALOG_COMMAND_FILE}"
;;
"buttonenable" )
# Enable button 1
/bin/echo "button1: enable" >> "${DIALOG_COMMAND_FILE}"
;;
"change" )
# Change the listitem Status
# Increment the progress bar by static amount ($6)
# Display the progress bar text ($5)
/bin/echo "listitem: title: ${2}, status: ${3}, statustext: ${4}" >> "${DIALOG_COMMAND_FILE}"
if [[ ! -z $5 ]]; then
/bin/echo "progresstext: $5" >> "${DIALOG_COMMAND_FILE}"
/bin/echo "progress: $6" >> "${DIALOG_COMMAND_FILE}"
fi
;;
"clear" )
# Clear the list and show an optional message
/bin/echo "list: clear" >> "${DIALOG_COMMAND_FILE}"
/bin/echo "message: ${2}" >> "${DIALOG_COMMAND_FILE}"
;;
"delete" )
# Delete item from list
/bin/echo "listitem: delete, title: ${2}" >> "${DIALOG_COMMAND_FILE}"
;;
"destroy" )
# Kill the progress bar and clean up
/bin/echo "quit:" >> "${DIALOG_COMMAND_FILE}"
;;
"done" )
# Complete the progress bar and clean up
/bin/echo "progress: complete" >> "${DIALOG_COMMAND_FILE}"
/bin/echo "progresstext: $5" >> "${DIALOG_COMMAND_FILE}"
;;
"icon" )
# set / clear the icon, pass <nil> if you want to clear the icon
[[ -z ${2} ]] && /bin/echo "icon: none" >> "${DIALOG_COMMAND_FILE}" || /bin/echo "icon: ${2}" >> $"${DIALOG_COMMAND_FILE}"
;;
"image" )
# Display an image and show an optional message
/bin/echo "image: ${2}" >> "${DIALOG_COMMAND_FILE}"
[[ ! -z ${3} ]] && /bin/echo "progresstext: $5" >> "${DIALOG_COMMAND_FILE}"
;;
"infobox" )
# Show text message
/bin/echo "infobox: ${2}" >> "${DIALOG_COMMAND_FILE}"
;;
"infotext" )
# Show text message
/bin/echo "infotext: ${2}" >> "${DIALOG_COMMAND_FILE}"
;;
"show" )
# Activate the dialog box
/bin/echo "activate:" >> $"${DIALOG_COMMAND_FILE}"
;;
"title" )
# Set / Clear the title, pass <nil> to clear the title
[[ -z ${2} ]] && /bin/echo "title: none:" >> "${DIALOG_COMMAND_FILE}" || /bin/echo "title: ${2}" >> "${DIALOG_COMMAND_FILE}"
;;
"progress" )
# Increment the progress bar by static amount ($6)
# Display the progress bar text ($5)
/bin/echo "progress: ${6}" >> "${DIALOG_COMMAND_FILE}"
/bin/echo "progresstext: ${5}" >> "${DIALOG_COMMAND_FILE}"
;;
esac
}
function erase_support_files ()
{
# Erase the support files and generally clean up after ourselves
#
# RETURNS: None
/bin/rm -f "${LOCKFILE_PATH}"
/bin/rm -f "${CURRENT_DEFERAL_EXPIRATION_TIMESTAMP_FILE}"
/bin/rm -f "${DIALOG_COMMAND_FILE}"
/bin/rm -f "${OVERLAY_ICON}"
/bin/rm -f "${HARDWARE_ICON}"
/bin/rm -f "${JSON_OPTIONS_FILE_PAT}"
/bin/rm -f "${ANYCONNECT_PACKAGE_PATH}"
}
function unload_and_delete_daemon ()
{
# Unloads the launch daemon from launchctl
#
# RETURNS: None
/bin/launchctl unload -wF "${LAUNCH_DAEMON_PATH:r}"
/bin/rm -f "${LAUNCH_DAEMON_PATH}"
}
function runAsUser ()
{
if [ "$LoggedInUser" != "loginwindow" ]; then
launchctl asuser "$uid" sudo -iu "$LoggedInUser" open "$@"
else
echo "no user logged in"
# uncomment the exit command
# to make the function exit with an error when no user is logged in
# exit 1
fi
}
function Contains ()
{
# Purpose: Scan for item inside an arry
# Paramters: #1 - Pass entire array
# #2 - Item to look for in array
# Returns: Return 0 if element exists in array
local list=$1[@]
local elem=$2
for i in "${!list}"
do
[[ "$i" == "${elem}" ]] && return 0
done
return 1
}
function alltrim ()
{
echo "${1}" | /usr/bin/xargs
}
function create_infobox_message()
{
################################
#
# Swift Dialog InfoBox message construct
#
################################
SD_INFO_BOX_MSG="## System Info ##\n"
SD_INFO_BOX_MSG+="${MAC_CPU}<br>"
SD_INFO_BOX_MSG+="${MAC_SERIAL_NUMBER}<br>"
SD_INFO_BOX_MSG+="${MAC_RAM} RAM<br>"
SD_INFO_BOX_MSG+="${FREE_DISK_SPACE}GB Available<br>"
SD_INFO_BOX_MSG+="macOS ${MACOS_VERSION}<br>"
}
function get_hardware_platform_icon ()
{
case $(alltrim "${MAC_HARDWARE_CLASS}") in
"MacBook Air" )
HARDWARE_ICON="${ICON_DIRECTORY}/com.apple.macbookair-2018-gold.icns"
;;
"MacBook Pro" )
HARDWARE_ICON="${ICON_DIRECTORY}/com.apple.macbookpro-16-silver.icns"
;;
"Mac Mini" )
HARDWARE_ICON="${ICON_DIRECTORY}/com.apple.macmini-2020.icns"
;;
"iMac" )
HARDWARE_ICON="${ICON_DIRECTORY}/com.apple.imac-2021-blue.icns"
;;
"Mac Pro" )
HARDWARE_ICON="${ICON_DIRECTORY}/com.apple.macpro-2019.icns"
;;
"Mac Studio" )
HARDWARE_ICON="${ICON_DIRECTORY}/com.apple.macstudio.icns"
;;
esac
}
function cleanup_and_exit ()
{
[[ -f ${JSON_OPTIONS} ]] && /bin/rm -rf ${JSON_OPTIONS}
[[ -f ${TMP_FILE_STORAGE} ]] && /bin/rm -rf ${TMP_FILE_STORAGE}
[[ -f ${DIALOG_COMMAND_FILE} ]] && /bin/rm -rf ${DIALOG_COMMAND_FILE}
exit 0
}
function unload_and_delete_daemon ()
{
# Unloads the launch daemon from launchctl
#
# RETURNS: None
/bin/launchctl unload -wF "${LAUNCH_DAEMON_PATH:r}"
/bin/rm -f "${LAUNCH_DAEMON_PATH}"
}
function read_lockfile_pid ()
{
# Reads a file which contains a PID.
#
# RETURNS: A string representation of a PID or nothing if
# the file is empty or doesnt exist
if [[ ! -e ${LOCKFILE_PATH} ]]; then
return
fi
pid=$(cat "${LOCKFILE_PATH}")
echo $pid
}
function clear_lock_file ()
{
# Remove the lock file - we use this if we exit for a deferral and d
# not want to clean up other files
#
# RETURNS: None
/bin/rm -f "${LOCKFILE_PATH}"
}
function set_lock_file_with_pid ()
{
printf $$ > "${LOCKFILE_PATH}"
}
typeset -a adapter
typeset -a ip_address
function get_nic_info
{
# Creates two arrays showing the Adapter name & IP address of all found IP address running on a system.
# Detects for the presence of VPN adapter as well
#
# RETURNS: Two arrays (must be declared global outside of function)
typeset -a nic_interfaces && nic_interfaces=( ${(f)"$( networksetup -listnetworkserviceorder | grep "Device:" | awk '{print $3, $NF}' )"} )
# Get ISP Info
isp=$(curl -s https://ipecho.net/plain)
adapter+="ISP"
ip_address+=$isp
# Go thru all of the adapters and get the name & IP address of each on
for i ($nic_interfaces); do
if [[ ${i} != *"bridge"* ]]; then
adapter+=$( echo $i | awk '{print $1}' | tr -d ',' )
interface=$( echo $i | awk '{print $2}')
ip=$(ifconfig ${interface::-1} | grep "inet " | awk '{print $2}')
ip_address+=$ip
fi
done
# If VPN is activate, get that info as well
if [[ "$( echo 'state' | /opt/cisco/anyconnect/bin/vpn -s | grep -m 1 ">> state:" )" == *'Connected' ]]; then
ip_address+=$(/opt/cisco/anyconnect/bin/vpn -s stats | grep 'Client Address (IPv4)' | awk -F ': ' '{ print $2 }' | xargs)
adapter+="VPN"
fi
}
function GetJAMFServer ()
{
jamfpro_url=$(/usr/bin/defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url)
}
function GetJamfProAPIToken ()
{
# This function uses Basic Authentication to get a new bearer token for API authentication.
# Use user account's username and password credentials with Basic Authorization to request a bearer token.
#
# Parms expected: jampro_user, jamfpro_password,
api_token=$(/usr/bin/curl -X POST --silent -u "${jamfpro_user}:${jamfpro_password}" "${jamfpro_url}/api/v1/auth/token" | plutil -extract token raw -)
}
function APITokenValidCheck ()
{
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
api_authentication_check=$(/usr/bin/curl --write-out %{http_code} --silent --output /dev/null "${jamfpro_url}/api/v1/auth" --request GET --header "Authorization: Bearer ${api_token}")
}
function CheckAndRenewAPIToken ()
{
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
APITokenValidCheck
# If the api_authentication_check has a value of 200, that means that the current
# bearer token is valid and can be used to authenticate an API call.
if [[ ${api_authentication_check} == 200 ]]; then
# If the current bearer token is valid, it is used to connect to the keep-alive endpoint. This will
# trigger the issuing of a new bearer token and the invalidation of the previous one.
api_token=$(/usr/bin/curl "${jamfpro_url}/api/v1/auth/keep-alive" --silent --request POST --header "Authorization: Bearer ${api_token}" | plutil -extract token raw -)
else
# If the current bearer token is not valid, this will trigger the issuing of a new bearer token
# using Basic Authentication.
GetJamfProAPIToken
fi
}
function FileVaultRecoveryKeyValidCheck ()
{
# Verify that a FileVault recovery key is available by running an API command
# which checks if there is a FileVault recovery key present.
#
# The API call will only return the HTTP status code.
filevault_recovery_key_check=$(/usr/bin/curl --write-out %{http_code} --silent --output /dev/null "${jamfpro_url}/api/v1/computers-inventory/$ID/filevault" --request GET --header "Authorization: Bearer ${api_token}")
}
function FileVaultRecoveryKeyRetrieval ()
{
# Retrieves a FileVault recovery key from the computer inventory record.
filevault_recovery_key_retrieved=$(/usr/bin/curl -sf --header "Authorization: Bearer ${api_token}" "${jamfpro_url}/api/v1/computers-inventory/$ID/filevault" -H "Accept: application/json" | plutil -extract personalRecoveryKey raw -)
}