- #!/bin/bash
- declare -x uuidgen="/usr/bin/uuidgen"
- declare -x plistbuddy="/usr/libexec/PlistBuddy"
- declare -x awk="/usr/bin/awk"
- declare -x cat="/bin/cat"
- declare -x cp="/bin/cp"
- declare -x dscl="/usr/bin/dscl"
- declare -x defaults="/usr/bin/defaults"
- declare -x du="/usr/bin/du"
- declare -x id="/usr/bin/id"
- declare -x ntpdate="ntpdate"
- declare -x scutil="/usr/sbin/scutil"
- declare -x perl="/usr/bin/perl"
- declare -x basename="/usr/bin/basename"
- declare -x date="/bin/date"
- declare -x defaults="/usr/bin/defaults"
- declare -x dscl="/usr/bin/dscl"
- declare -x find="/usr/bin/find"
- declare -x groups="/usr/bin/groups"
- declare -x ls="/bin/ls"
- declare -x mv="/bin/mv"
- declare -x rm="/bin/rm "
- declare -x rmdir="/bin/rmdir"
- declare -x sudo="/usr/bin/sudo"
- declare -x serversetup="/System/Library/ServerSetup/serversetup"
- declare -x plistbuddy="/usr/libexec/PlistBuddy"
- # -- Runtime varibles
- declare -x REQCMDS="$awk $dscl $deaults $ntpdate $perl $scutil"
- declare -x SCRIPT="${0##*/}" ; SCRIPTNAME="${SCRIPT%%\.*}"
- declare -x SCRIPTPATH="$0" RUNDIRECTORY="${0%/*}"
- declare -x SYSTEMVERSION="/System/Library/CoreServices/SystemVersion.plist"
- declare -x OSVER="$("$defaults" read "${SYSTEMVERSION%.plist}" ProductVersion )"
- #declare -x CONFIGFILE="${RUNDIRECTORY:?}/${SCRIPTNAME}.conf"
- declare -x BUILDVERSION="2009051"
- [ "$EUID" != 0 ] && printf "%s\n" "This script requires root access ($EUID)!" && exit 1
- # -- Start the script log
- # Set to "VERBOSE" for more logging prior to using -v
- declare -x LOGLEVEL="NORMAL" SCRIPTLOG="/Library/Logs/${SCRIPT%%\.*}.log"
- declare -i CURRENT_LOG_SIZE="$("$du" -hm "${SCRIPTLOG:?}" |
- "$awk" '/^[0-9]/{print $1;exit}')"
- if [ ${CURRENT_LOG_SIZE:=0} -gt 50 ] ; then
- "$rm" "$SCRIPTLOG"
- statusMessage "LOGSIZE:$CURRENT_LOG_SIZE, too large removing"
- fi
- exec 2>>"${SCRIPTLOG:?}" # Redirect standard error to log file
- # Strip any extention from scriptname and log stderr to script log
- if [ -n ${SCRIPTLOG:?"The script log has not been specified"} ] ; then
- printf "%s\n" \
- "STARTED:$SCRIPTNAME:EUID:$EUID:$("$date" +%H:%M:%S): Mac OS X $OSVER:BUILD:$BUILDVERSION" >>"${SCRIPTLOG:?}"
- printf "%s\n" "Log file is: ${SCRIPTLOG:?}"
- fi
- statusMessage() { # Status message function with type and now color!
- # Requires SCRIPTLOG STATUS_TYPE=1 STATUS_MESSAGE=2
- declare date="${date:="/bin/date"}"
- declare DATE="$("$date" -u "+%Y-%m-%d")"
- declare STATUS_TYPE="$1" STATUS_MESSAGE="$2"
- if [ "$ENABLECOLOR" = "YES" ] ; then
- # Background Color
- declare REDBG="41" WHITEBG="47" BLACKBG="40"
- declare YELLOWBG="43" BLUEBG="44" GREENBG="42"
- # Foreground Color
- declare BLACKFG="30" WHITEFG="37" YELLOWFG="33"
- declare BLUEFG="36" REDFG="31"
- declare BOLD="1" NOTBOLD="0"
- declare format='\033[%s;%s;%sm%s\033[0m\n'
- # "Bold" "Background" "Forground" "Status message"
- printf '\033[0m' # Clean up any previous color in the prompt
- else
- declare format='%s\n'
- fi
- # Function only seems to work on intel and higher.
- showUIDialog(){
- statusMessage header "FUNCTION: # $FUNCNAME" ; unset EXITVALUE TRY
- "$killall" -HUP "System Events" 2>/dev/null
- declare -x UIMESSAGE="$1"
- "$osascript" <<EOF
- try
- with timeout of 0.1 seconds
- tell application "System Events"
- set UIMESSAGE to (system attribute "UIMESSAGE") as string
- activate
- display dialog UIMESSAGE with icon 2 giving up after "3600" buttons "Dismiss" default button "Dismiss"
- end tell
- end timeout
- end try
- EOF
- return 0
- } # END showUIDialog()
- case "${STATUS_TYPE:?"Error status message with null type"}" in
- progress) \
- [ -n "$LOGLEVEL" ] &&
- printf $format $NOTBOLD $WHITEBG $BLACKFG "PROGRESS:$STATUS_MESSAGE" ;
- printf "%s\n" "$DATE:PROGRESS: $STATUS_MESSAGE" >> "${SCRIPTLOG:?}" ;;
- # Used for general progress messages, always viewable
- notice) \
- printf "%s\n" "$DATE:NOTICE:$STATUS_MESSAGE" >> "${SCRIPTLOG:?}" ;
- [ -n "$LOGLEVEL" ] &&
- printf $format $NOTBOLD $YELLOWBG $BLACKFG "NOTICE :$STATUS_MESSAGE" ;;
- # Notifications of non-fatal errors , always viewable
- error) \
- printf "%s\n\a" "$DATE:ERROR:$STATUS_MESSAGE" >> "${SCRIPTLOG:?}" ;
- [ -n "$LOGLEVEL" ] &&
- printf $format $NOTBOLD $REDBG $YELLOWFG "ERROR :$STATUS_MESSAGE" ;;
- # Errors , always viewable
- verbose) \
- printf "%s\n" "$DATE:VERBOSE: $STATUS_MESSAGE" >> "${SCRIPTLOG:?}" ;
- [ "$LOGLEVEL" = "VERBOSE" ] &&
- printf $format $NOTBOLD $WHITEBG $BLACKFG "VERBOSE :$STATUS_MESSAGE" ;;
- # All verbose output
- header) \
- [ "$LOGLEVEL" = "VERBOSE" ] &&
- printf $format $NOTBOLD $BLUEBG $BLUEFG "VERBOSE :$STATUS_MESSAGE" ;
- printf "%s\n" "$DATE:PROGRESS: $STATUS_MESSAGE" >> "${SCRIPTLOG:?}" ;;
- # Function and section headers for the script
- passed) \
- [ "$LOGLEVEL" = "VERBOSE" ] &&
- printf $format $NOTBOLD $GREENBG $BLACKFG "SANITY :$STATUS_MESSAGE" ;
- printf "%s\n" "$DATE:SANITY: $STATUS_MESSAGE" >> "${SCRIPTLOG:?}" ;;
- # Sanity checks and "good" information
- graphical) \
- [ "$GUI" = "ENABLED" ] &&
- showUIDialog "$STATUS_MESSAGE" ;;
- esac
- return 0
- } # END statusMessage()
- die() { # die Function
- statusMessage header "FUNCTION: # $FUNCNAME" ; unset EXITVALUE
- declare LASTDIETYPE="$1" LAST_MESSAGE="$2" LASTEXIT="$3"
- declare LASTDIETYPE="${LASTDIETYPE:="UNTYPED"}"
- if [ ${LASTEXIT:="192"} -gt 0 ] ; then
- statusMessage error "$LASTDIETYPE :$LAST_MESSAGE:EXIT:$LASTEXIT"
- # Print specific error message in red
- else
- statusMessage verbose "$LASTDIETYPE :$LAST_MESSAGE:EXIT:$LASTEXIT"
- # Print specific error message in white
- fi
- statusMessage verbose "COMPLETED:$SCRIPT IN $SECONDS SECONDS"
- "$killall" "System Events"
- exit "${LASTEXIT}" # Exit with last status or 192 if none.
- return 1 # Should never get here
- } # END die()
- cleanUp() { # -- Clean up of our inportant sessions variables and functions.
- statusMessage header "FUNCTION: # $FUNCNAME" ; unset EXITVALUE
- statusMessage verbose "TIME: $SCRIPT ran in $SECONDS seconds"
- unset -f ${!check*}
- [ "${ENABLECOLOR:-"ENABLECOLOR"}" = "YES" ] && printf '\033[0m' # Clear Color
- if [ "$PPID" == 1 ] ; then # LaunchD is always PID 1 in 10.4+
- : # Future LaunchD code
- fi
- exec 2>&- # Reset the error redirects
- return 0
- } # END cleanUp()
- # Check script options
- statusMessage header "GETOPTS: Processing script $# options:$@"
- # ABOVE: Check to see if we are running as a postflight script,the installer creates $SCRIPT_NAME
- [ $# = 0 ] && statusMessage verbose "No options given"
- # If we are not running postflight and no parameters given, print usage to stderr and exit status 1
- while getopts vCud:f: SWITCH ; do
- case $SWITCH in
- v ) export LOGLEVEL="VERBOSE" ;;
- C ) export ENABLECOLOR="YES" ;;
- u ) export GUI="ENABLED" ;;
- d ) export SAVE_DIRECTORY="$OPTARG" ;;
- f ) export CSV_FILE="$OPTARG" ;;
- esac
- done # END getopts
- checkLineEndings(){
- declare -i FUNCSECONDS="$SECONDS" # Capture start time
- declare FILE_TO_CHECK="$1"
- statusMessage header "FUNCTION: # ${FUNCNAME}" ; unset EXITVALUE
- if [ -f "$FILE_TO_CHECK" ] ; then
- if ! $perl -ne "exit 1 if m/\r\n/;" "$FILE_TO_CHECK" ; then
- statusMessage notice \
- "Incorrect line endings detected (probobly due to Mircosoft edit)"
- statusMessage notice \
- "Backup: $CSV_FILE.bak"
- $cp -f "$FILE_TO_CHECK" "$FILE_TO_CHECK".bak
- statusMessage verbose 'Resetting line endings \r/\n/ to \n'
- $perl -i -pe 's/\r/\n/g' "$FILE_TO_CHECK"
- elif ! $perl -ne "exit 1 if m/\r/;" "$FILE_TO_CHECK" ; then
- statusMessage notice \
- "Incorrect line endings detected (DOS?) fixing backup: $FILE_TO_CHECK.bak"
- $cp -f "$FILE_TO_CHECK" "$FILE_TO_CHECK".bak
- statusMessage verbose 'Resetting line endings \r/\n/'
- $perl -i -pe 's/\r/\n/g' "$FILE_TO_CHECK"
- fi
- else
- statusMessage error "File: $FILE_TO_CHECK does not exist"
- die ERROR "Invalid file specified: $FILE_TO_CHECK"
- fi
- statusMessage verbose "TIME:$FUNCNAME:Took $FUNCTIME seconds to EXIT:$EXITVALUE"
- }
- checkCommands() { # CHECK_CMDS Required Commands installed check using the REQCMDS varible.
- declare -i FUNCSECONDS="$SECONDS" # Capture start time
- statusMessage header "FUNCTION: # ${FUNCNAME}" ; unset EXITVALUE
- declare REQCMDS="$1"
- for RQCMD in ${REQCMDS:?} ; do
- if [ -x "$RQCMD" ] ; then
- statusMessage passed "PASSED: $RQCMD is executable"
- else
- # Export the command Name to the die status message can refernce it"
- export RQCMD ; return 1
- fi
- done
- return 0
- declare -i FUNCTIME=$(( ${SECONDS:?} - ${FUNCSECONDS:?} ))
- [ "${FUNCTIME:?}" != 0 ] &&
- statusMessage verbose "TIME:$FUNCNAME:Took $FUNCTIME seconds to EXIT:$EXITVALUE"
- } # END checkCommands()
- checkSystemVersion() {
- # CHECK_OS Read the /Sys*/Lib*/CoreSer*/S*Version.plist value for OS version
- statusMessage header "FUNCTION: # ${FUNCNAME}" ; unset EXITVALUE
- declare OSVER="$1"
- case "${OSVER:?}" in
- 10.0* | 10.1* | 10.2* | 10.3*) \
- die ERROR "$FUNCNAME: Unsupported OS version: $OSVER." 192 ;;
- 10.4*) \
- statusMessage passed "CHECK_OS: OS check: $OSVER successful!";
- return 0;;
- 10.5*) \
- statusMessage passed "CHECK_OS: OS check: $OSVER successful!";
- return 0;;
- 10.6*) \
- die ERROR "$FUNCNAME:$LINENO Unsupported OS:$OSVER is too new." 192 ;;
- *) \
- die ERROR "CHECK_OS:$LINENO Unsupported OS:$OSVER unknown error" 192 ;;
- esac
- return 1
- checkDNS(){
- declare SERVER_HOSTNAME="$1" SERVER_IP="$2"
- statusMessage header "$SERVER_HOSTNAME ($SERVER_IP)"
- if host "${SERVER_HOSTNAME:-null}" &>/dev/null ; then
- statusMessage passed "FORWARD[A]:$SERVER_HOSTNAME ($SERVER_IP)"
- else
- statusMessage error "FORWARD[A]:$SERVER_HOSTNAME ($SERVER_IP)"
- fi
- if host "${SERVER_IP:-null}" &>/dev/null ; then
- statusMessage passed "REVERSE[PTR]:$SERVER_HOSTNAME ($SERVER_IP)"
- else
- statusMessage error "REVERSE[PTR]:$SERVER_HOSTNAME ($SERVER_IP)"
- fi
- printf "\n"
- }
- } # END checkSystemVersion()
- statusMessage verbose "Using file: $CSV_FILE"
- checkLineEndings "$CSV_FILE"
- declare -x PLIST="${SAVE_DIRECTORY:="."}/${CSV_FILE%%.csv}.plist"
- $plistbuddy -c "Add :listName string ${CSV_FILE%%.csv}" "$PLIST"
- $plistbuddy -c "Add :uuid string $($uuidgen)" "$PLIST"
- OLDIFS="$IFS"
- IFS=$'\n'
- declare -ix N=0
- for LINE in `$cat "${CSV_FILE:?"Must specify a file with -f"}"` ; do
- let LINE_NUM++
- if [ $LINE_NUM -eq 1 ] ; then
- statusMessage progress "Skipping first line:$LINE"
- continue
- fi
- # statusMessage verbose "processing $LINE"
- declare -x SITE_ID="$(printf "$LINE" | $awk -F',' '{print $1;exit}')"
- declare -x SERVER_ETHERNET[0]="$(printf "$LINE" | $awk -F',' '{print $12;exit}')"
- declare -x SERVER_ETHERNET[1]="$(printf "$LINE" | $awk -F',' '{print $13;exit}')"
- declare -x SERVER_HW_SERIAL="$(printf "$LINE" | $awk -F',' '{print substr($15,1,8);exit}')"
- # AdminUser
- declare -x ADMIN_USER_EXISTS="false"
- declare -x ADMIN_USER_NAME="$(printf "$LINE" | $awk -F',' '{print $16;exit}')"
- declare -x ADMIN_USER_PASSWORD="$(printf "$LINE" | $awk -F',' '{print $17;exit}')"
- declare -x ADMIN_USER_REALNAME="$ADMIN_USER_NAME"
- declare -ix ADMIN_USER_UID="501"
- # HostName
- declare -x HOST_NAME="$(printf "$LINE" | $awk -F',' '{print $4;exit}')"
- statusMessage verbose "HOST_NAME: $HOST_NAME"
- # Bonjour
- declare -x BONJOUR_ENABLED="true"
- declare -x BONJOUR_NAME="$(printf $HOST_NAME | $awk -F'.' '{print $1;exit}')"
- # ComputerName
- declare -x COMPUTER_NAME="$(printf $HOST_NAME | $awk -F'.' '{print $1;exit}')"
- # DS
- declare -x DS_TYPE="Standalone"
- # DefaultGroupName
- declare -x DEFAULT_GROUP_LONGNAME="Work Group"
- declare -x DEFAULT_GROUP_SHORTNAME="workgroup"
- # EnableARD
- declare -x ENABLE_ARD="true"
- # InstallLanguage
- declare -x INSTALL_LAUNGUAGE="English"
- # Keyboard
- declare -x KEYBOARD_DEFAULT_FORMAT="0"
- declare -x KEYBOARD_DEFAULT_SCRIPT="0"
- declare -x KEYBOARD_RESNAME="U.S."
- declare -ix KEYBOARD_SCRIPTID="0"
- declare -ix KEYBOARD_KBRESID="0"
- # NetworkInterfaces
- declare -ax INTERFACE_ACTIVEAT[0]="false"
- declare -ax INTERFACE_ACTIVETCPIP[0]="true"
- declare -ax DNS_DOMAIN[0]="$(printf "$LINE" | $awk -F',' '{print $11;exit}')"
- declare -ax DNS_SERVER_PRIMARY[0]="$(printf "$LINE" | $awk -F',' '{print $8;exit}')"
- declare -ax DNS_SERVER_SECONDARY[0]="$(printf "$LINE" | $awk -F',' '{print $9;exit}')"
- declare -ax DNS_SERVER_TERIARY[0]="$(printf "$LINE" | $awk -F',' '{print $10;exit}')"
- declare -ax INTERFACE_DEVICENAME[0]="en0"
- declare -ax ETHERNET_ADDRESS[0]="00:00:00:00:00:00"
- declare -ax IPV6_TYPE[0]="3"
- declare -ax PORT_NAME[0]="Ethernet 1"
- declare -ax IP_ADDRESS[0]="$(printf "$LINE" | $awk -F',' '{print $5;exit}')"
- declare -ax ROUTER[0]="$(printf "$LINE" | $awk -F',' '{print $7;exit}')"
- declare -ax SUBNET_MASK[0]="$(printf "$LINE" | $awk -F',' '{print $6;exit}')"
- declare -ax INTERFACE_TYPE[0]="Manual Configuration"
- declare -ax INTERFACE_ACTIVEAT[1]="false"
- declare -ax INTERFACE_ACTIVETCPIP[1]="false"
- declare -ax INTERFACE_DEVICENAME[1]="en1"
- declare -ax ETHERNET_ADDRESS[1]="00:00:00:00:00:00"
- declare -ax IPV6_TYPE[1]="3"
- declare -ax PORT_NAME[1]="Ethernet 2"
- declare -ax IP_ADDRESS[1]="192.168.50.90"
- declare -ax ROUTER[1]="0.0.0.0"
- declare -ax SUBNET_MASK[1]="255.255.255.0"
- declare -ax INTERFACE_TYPE[1]="Manual Configuration"
- # PrimaryLanguage
- declare -x PRIMARY_LANGUAGE="English"
- # Serial Number
- declare -x SERIAL_NUMBER="$(printf "$LINE" | $awk -F',' '{print $14;exit}')"
- # Service NTP
- declare -x HOST_NTP="false"
- declare -x HOST_NTP_SERVER="$(printf "$LINE" | $awk -F',' '{print $21;exit}')"
- declare -x USE_NTP="true"
- # TimeZone
- declare -x TIME_ZONE="$(printf "$LINE" | $awk -F',' '{print $20;exit}')"
- # Version Number
- declare -ix VERSION_NUMBER="3"
- declare SERVER_MAC="$(printf "%s" "${SERVER_ETHERNET[0]}" | $awk 'BEGIN{FS=""}{print $1$2":"$3$4":"$5$6":"$7$8":"$9$10":"$11$12}')"
- $plistbuddy -c "Add :items array" "$PLIST"
- $plistbuddy -c "Add :items: dict" "$PLIST"
- $plistbuddy -c "Add :items:$N::hardwareAddress string $SERVER_MAC" "$PLIST"
- $plistbuddy -c "Add :items:$N::hostname string $HOST_NAME" "$PLIST"
- $plistbuddy -c "Add :items:$N::name string $HOST_NAME" "$PLIST"
- $plistbuddy -c "Add :items:$N::networkAddress string ${IP_ADDRESS[0]}" "$PLIST"
- $plistbuddy -c "Add :items:$N::preferHostname bool false" "$PLIST"
- let N++
- done
- OLDIFS="$IFS"