Hey friendos,
Here's a little doozy we've been using for a while.
You have problem.You environment uses local accounts, or just cached mobile directory accounts directed to the local HD. You want to re-image but have to manually backup the user's home. That sucks.
Welcome BackupRestoreUsers.sh (worst title ever).
https://interpublic.app.box.com/s/wu5zud1j2yyd0mqpcq8y
It can be copied to a sharepoint or external hard drive and run interactively via the .command wrapper scripts without modification to backup all homes on a Mac.
OR
You can make two copies of the script, one for backup and one for restore. Then hard code the mode in the script (caspermode=true, mode="restore"/"backup")
Configure cdpmode=true, and set your options and credentials. (It will search mount points when netbooted for your CasperShare, then use that server to backup user homes to). Make a sharepoint on your CDPs for backing up user homes (maybe called UserBackup)... the script will mount it. (we just use inexpensive USB drives connected to the MacMinis at each location as we don't do bulk imaging often).
Upload the scripts via Casper Admin, and set them to...
BackupRestoreUsers-backup.sh - run before
BackupRestoreUsers-restore.sh - run after
Add them to a imaging configuration that erases and reimages. The workflow has timeout which defaults to backup all users (apart from the excluded ones hardcoded in the script).
Netboot your CEO's Macbook Air *, run an erase imaging workflow, follow the prompts and backup and restore their user data without out leaving CasperImaging!
* not a great idea
-- DISCLAIMER - use at your own risk! No warranty! Code is ugly disclaimer!
I do plan on opensourcing this, along with my patch management system very soon so it can be refined.
1#!/bin/bash2version="0.98"3#4# BackupRestoreUsers5#6# INTERACTIVE MODE7# ----------------8# usage:9# 10# configure your backuprepo as below (default: backuproot="$(dirname $0)/_UserBackups")11#12# copy the script folder to a share, then launch script via the xxx.command wrappers.13#14# CASPER IMAGING MODE15#--------------------16# usage:17# as casper imaging is a little difficult with it's scripting workflows it goes like this.18#19# configure your backuprepo as below (either backuproot=/Volume/LocalHD, afpurl, smburl)20# or21# use the network segment section so you can find the nearest backupserver automatically.22#23# set 'caspermode=true'24# uncomment 'mode="backup"'25# save as 'BackupRestoreUsers-backup.sh'26# do the same for 'mode="restore"'27# save as 'BackupRestoreUsers-restore.sh'28#29# you should now have 2 scripts, a backup and restore mode script.30#31# upload both Casper Admin repo.32#33# allocate it to a configuration34#35# get info set the -backup.sh to run BEFORE, and -restore.sh to run AFTER36#37#38# bugs. GUI can get stuck on osascript. sometimes crashes? more testing.39#40# more info lachlan.stewart@interpublic.com41##############################################################42#43# 0.1 casperimaging spin off - BackupRestoreUsers.sh was getting to big for it's boots44# 0.2 added initial gui and user exclusion45# 0.3 merged functionality from old script -- can now run interactively 46# 0.9 testing release47# 0.91 added functionality to deal with alternate user paritions that are mounted with fstab, and flags for /Users/.nobackup (automated wipe)48# 0.92 changed to ditto due to some bugs in tar - enabled compression by default49# 0.93 compression off, faster without50# 0.94 another day, another method.. now will create a spareimage and rsync. this should be most robust51# 0.95 fixed flush users bug, discovers caspermount server to do away with network segment code. assumes you are using cdp for userbackup location5253###############################54#55# configure from here56#57###############################5859caspermode=false # enable casper imaging mode (auto - nooptions to restore a different backup, logs log and doesn't recognise command switches)60#mode="backup" # set mode for use with casper - set priority to run before61#mode="restore" # set mode for use with casper - set priority to run after6263userfolder="/Users"64altuserpart="/Volumes/Data" # watch for this user partition/path and use if seen (good if using fstab method to mount a userpartion eg. "/Volumes/UserHD/Users")65excludedusers="^.|Shared|Deleted Users|admin|Library" # first line excludes all files starting with "." add anymore here...66restoreto="$userfolder"6768showlog=true # open the log during6970rsyncexcludes=( ".Spotlight-V100" ".fseventsd" ".TemporaryItems" "**/Library/Application Support/Google/Chrome/Safe Browsing Download/**" ) # exclude any know troublesome files from copy7172debug=false # dump to debuglog - very verbose - just for me.7374###############################75#76# backup repo config77#78###############################7980#81# cdp mode82#8384cdpmode=false85caspersharename="CasperShare" # share name to grep mount for, find your cdp mount86userbackupcredentials="userbackup:userbackup" # username:password to mount userbackup share from mounted cdp87userbackupshare="UserBackup"8889#90# manual backuproot91#9293backuproot="$(dirname $0)/_UserBackups" # backup to ./_UserBackups/ in this script folder9495# single network destinations96# ----------------------------97# afp://[username:password]@rhost[:port]/volume98#afpurl="afp://username:password@servername/backup"99# //[domain;][user[:password]@]server[/share] path100#smburl="//username:password@servername/backup"101102# network segment specific destinations103# --------------------------------------104# edit the xxx.xxx.xxx and associated xxxurl=105# 106# or (comment out networksegment to disable)107108#networksegment=$(ipconfig getifaddr en0 | cut -d. -f1-3) # ip and cut last quad for network segment109110if [ "$networksegment" != "" ]111then112 case $networksegment in113 10.132.11 )114 # site: BNE SMT115 afpurl="afp://userbackup:userbackup@10.132.11.18/UserBackup"116 ;;117 10.128.15 )118 # MEB 520BST 119 afpurl="afp://userbackup:userbackup@10.128.15.19/UserBackup"120 ;;121 10.132.9 )122 # MEB McCann 123 afpurl="afp://userbackup:userbackup@10.132.9.21/UserBackup"124 ;;125 10.132.108 | 10.128.20 | 10.132.3 | 10.132.12 )126 # SYD McCann/CMG127 afpurl="afp://userbackup:userbackup@10.128.20.120/UserBackup"128 ;;129 *)130 # default fallback131 echo "Unhandled network segment -- $networksegment - using default"132 afpurl="afp://userbackup:userbackup@mainserver/UserBackup"133 ;;134 esac135fi136137scriptname="BackupRestoreUsers"138139140#####################################141#142# End of configurable settings143#144#####################################145146#147# some common fuctions148#149150secho()151{152 # superecho - writes to log and will display a dialog to gui with timeout (a kludge for UI feedback in CI)153 message="$1"154 dialogtimeout="$2"155 echo "$message"156 echo "$message" >> "$logto/$log"157 [ "$dialogtimeout" != "" ] && osascript -e "tell app "System Events"" -e "activate" -e "display dialog "$message" buttons {"..."} giving up after $dialogtimeout" -e "end tell" 158}159160errorHander()161{162 # Error function163 message="$1"164 secho "ERROR: $message"165 osascript -e "tell app "System Events"" -e "activate" -e "display dialog "$message" buttons {"OK"} default button {"OK"} with title "Error"" -e "end tell"166 cp "$logto/$log" "$backuppath/$log.ERROR.log"167 rm "$logto/$log"168 killall "Casper Imaging"169 exit 1170}171172askQuestion()173{174 message=$1175 button1=$2 #default176 button2=$3177 timeout=$4 #will return default after178 # we'll follow apple convention... default button should be right bottommost (button1)179 if [ "$timeout" != "" ]180 then181 buttonreturn=$(osascript -e "tell app "System Events"" -e "activate" -e "display dialog "$message182183184(Default: $button1 in $timeout secs)" buttons {"$button2","$button1"} default button {"$button1"} giving up after $timeout" -e "end tell")185 else186 # no timeout.. important questions187 buttonreturn=$(osascript -e "tell app "System Events"" -e "activate" -e "display dialog "$message" buttons {"$button2","$button1"} default button {"$button1"}" -e "end tell")188 fi 189 [ "$(echo $buttonreturn | grep "gave up:true")" != "" ] && result=1 # timeout, return default 1190 [ "$(echo $buttonreturn | grep -w "$button1")" != "" ] && result=1191 [ "$(echo $buttonreturn | grep -w "$button2")" != "" ] && result=2192 echo $result193}194195#196# core brains197#198199findServer()200{201 server=$(mount | grep $caspersharename | cut -d'@' -f2- | cut -d':' -f-1)202 # if there was no mount with caspershare, don't set afp url203 [ "$server" != "" ] && afpurl="afp://$userbackupcredentials@$server/$userbackupshare"204}205206207mountShare()208{209 # this code is for automounting network shares, if running the script via ARD / Casper Imaging / DeployStudio210 # lets try to mount network volume, no error checking here... expecting properly formed urls to be passed to mount_afp/smb211 secho "Mounting the network share..." 1212 share=$(echo "$networkbackuppath" | cut -d'/' -f 4-)213 mountpath="/Volumes/$share"214 mkdir "$mountpath"215 # check if afp or smb216 if [ "$afpurl" != "" ]217 then218 mount_afp "$afpurl" "$mountpath"219 mounterror="$?"220 fi221 if [ "$smburl" != "" ]222 then223 mount_smb "$smburl" "$mountpath"224 mounterror="$?"225 fi226 if [ "$mounterror" != "0" ]227 then228 errorHander "There was a problem mounting the network share $share - mount_afp/smb returned $mounterror"229 exit 1230 fi231 backuproot="$mountpath/_UserBackups"232 [ ! -d "$backuproot" ] && mkdir "$backuproot"233 # end network mount234}235236unmountShare()237{238 #if we are using a network share, unmount it239 cd /240 secho "Unmounting $mountpath" 1241 sleep 1242 umount "$mountpath"243244 if [ "$?" != "0" ]245 then246 echo "Forcing unmount"247 diskutil unmount force "$mountpath"248 if [ "$?" != "0" ]249 then250 errorHander "There was a problem unmounting $backuproot"251 fi252 fi253}254255excludeUsers()256{257 while true258 do259 # generate a dialog message for exlusions260 message=$(261 echo -ne "Would you like to exclude any users?262"263 echo -ne "264"265 echo -ne " Users to backup266"267 echo -ne "-------------------------268"269 echo -ne "270"271 for user in $(ls $userfolder | grep -vE "$excludedusers")272 do273 echo -ne "$user "274 if [ -f "$userfolder/.$user-excluded" ]275 then276 echo -ne " - EXCLUDED277"278 else279 echo -ne "280"281 fi282 done283 )284 answer=$(askQuestion "$message" "Start Backup" "Exclude Users" 15)285 if [ "$answer" == "2" ]286 then287 for user in $(ls $userfolder | grep -vE "$excludedusers")288 do289 answer=$(askQuestion "User:290291$user292" "Include" "Exclude")293 if [ "$answer" == "1" ]294 then295 # remove exclusion if it exists296 [ -f "$userfolder/.$user-excluded" ] && rm "$userfolder/.$user-excluded"297 else298 # flag them as excluded299 touch "$userfolder/.$user-excluded"300 fi301 done302 else303 # don't exclude anymore304 break305 fi306 done307}308309chooseUserfolder()310{311 # in case you User partition isn't found, GUI to select a difference backup source312 answer=$(askQuestion "Do you want to backup from:313314315$userfolder" "Yes" "Choose different..." 20)316 if [ "$answer" == "2" ]317 then318 newuserfolder=$(/usr/bin/osascript << EOT319 tell application "Finder"320 activate321 set theFolder to (choose folder with prompt "Choose another /Users folder to backup:")322 return (POSIX path of theFolder)323 end tell324 EOT)325 fi326 # if a new location is chosen, use it327 [ ! -z "$newuserfolder" ] && userfolder="$newuserfolder"328}329330chooseBackup()331{332 while true333 do334 # generate a dialog message for backuplist335 message=$(336 echo -ne " Available Backups337"338 echo -ne "-------------------------339"340 echo -ne "341"342 [ -f "$backuproot/.DS_Store" ] && rm "$backuproot/.DS_Store" # just in case someone has been poking around in Users)343 for backupid in $(ls "$backuproot")344 do345 echo -ne "$backupid346"347 done348 )349 answer=$(askQuestion "$message" "Select a Backup..." "Quit")350 if [ "$answer" == "1" ]351 then352 for backupid in $(ls "$backuproot")353 do354 answer=$(askQuestion "BackupID: $backupid" "Next" "Select")355 if [ "$answer" == "2" ]356 then357 # set new backup358 uniqueid="$backupid"359 backuppath="$backuproot/$uniqueid"360 backupsettings="$backuppath/BackupSettings"361 return 0 362 fi363 done364 else365 # jump out 366 return 2367 fi368 done369}370371backupUsers()372{373 [ -d "$altuserpart" ] && userfolder="$altuserpart" # if we find alternate user partion, point at it374375 if $caspermode376 then377 # if we wish to run an automated image, skipping backup... touch /Users/.nobackup before executing script378 # but double check before skpping the backup.379 if [ -f "$userfolder/.nobackup" ]380 then381 answer=$(askQuestion "### This Mac has been flagged NOT to Backup ###382383($userfolder/.nobackup exists)" "Don't Backup - WIPE IT ALL" "Remove flag and continue..." 30)384 if [ "$answer" == "1" ]385 then386 # flag to save us checking for backups to restore post imaging387 touch "/tmp/.nobackup-$uniqueid"388 return 0389 else 390 # remove the flag and run as normal391 rm "$userfolder/.nobackup"392 fi393 fi394395 answer=$(askQuestion "Do you want to backup user data on this Mac?" "Backup" "Don't Backup" 20)396 if [ "$answer" == "2" ]397 then398 answer=$(askQuestion "Are you really sure?399400All user data will be LOST!" "Backup" "Wipe it ALL!")401 if [ "$answer" == "2" ] 402 then403 # flag to save us checking for backups after restore404 touch "/tmp/.nobackup-$uniqueid"405 return 0406 fi407 fi408 fi409410 # Check to see if there is an existing backup for this Mac411 if [ -d "$backuppath" ]412 then413 # check to see if it's complete414 if [ "$(defaults read $backupsettings BackupComplete)" != "1" ]415 then416 # if there is an incomplete backup, prompt... default to overwrite.. or give option to abort and sort it out.417 answer=$(askQuestion "There is an INCOMPLETE backup, would you like to overwrite it?" "Backup and Overwrite" "Abort" 20)418 [ "$answer" == "2" ] && errorHander "There is already a backup @ $backuppath ... Aborting..."419 else420 # if there is an COMPLETE backup, prompt... default to abort.. or give option to overwrite...421 if [ "$(defaults read $backupsettings HasBeenRestored)" == "1" ]422 then423 answer=$(askQuestion "There is an existing COMPLETE backup which HAS been restored, would you like to overwrite it?" "Backup and Overwrite" "Abort" 20)424 [ "$answer" == "2" ] && errorHander "There is already a backup @ $backuppath ... Aborting..."425 else426 answer=$(askQuestion "WARNING! There is an existing COMPLETE backup which HAS NOT been restored, would you like to overwrite it?" "Abort" "Backup and Overwrite" 60)427 [ "$answer" == "1" ] && errorHander "There is already a backup @ $backuppath ... Aborting..."428 fi429 fi430 # remove the old / existing backup431 rm -R "$backuppath"432 fi433434 chooseUserfolder435436 excludeUsers437438 answer=$(askQuestion "Would you like to flush user Trashes?" "Flush" "Don't Flush" 5)439 if [ "$answer" == "1" ]440 then441 flushtrash=true442 else443 flushtrash=false444 fi445446 #answer=$(askQuestion "Would you like to flush user Caches?" "Flush" "Don't Flush" 5)447 #if [ "$answer" == "1" ]448 #then449 # flushcache=true450 #else451 # flushcache=false452 #fi453454 # make paths and copy the logs to the server so we don't run out of space455 mkdir "$backuppath"456 mkdir "$backuppath/logs"457 cp "$logto/$log" "$backuppath/logs/$log"458 rm "$logto/$log"459 logto="$backuppath/logs"460461 secho462 secho "Backing up $uniqueid"463 secho464 secho "Scanning and sizing home folders ..." 2465 secho "--------------------------------------"466 secho467468 # open the log in console for more feedback469 [ $showlog ] && open "$logto/$log"470471 usercount=0472 totalsize=0473474 # get all the user folders in /User, process them and get size in bytes475 for user in $(ls $userfolder | grep -vE "$excludedusers")476 do477 if [ ! -f "$userfolder/.$user-excluded" ] # check if they are excluded478 then479480 if $flushtrash481 then482 secho "Flushing Trash for $user..."483 rm -R "$userfolder/$user/.Trash"484 fi485486 #if $flushcache487 #then488 # secho "Flushing Trash for $user..."489 # rm -R "$userfolder/$user/Library/Caches/"490 #fi491492 secho "Sizing home for $user..."493 size=$(( $(du -sk "$userfolder/$user" | awk '{print $1}') * 1024 ))494 secho "$user - $(($size / 1024 / 1024 )) MB" 2 495 userarray[$usercount]="$user"496 sizearray[$usercount]="$size"497 totalsize=$(( $totalsize + $size ))498 (( usercount ++ ))499 else500 secho "$user - EXCLUDED"501 fi502 done503504 # get free space on back up disk505 backupfree=$(( $(df -k "$backuproot" | tail -n1 | awk '{ print $4 }') * 1024 ))506507 secho "----------------------------------------------------------------------------------"508 secho "Total for $usercount user(s) is $(( $totalsize / 1024 / 1024 )) MBs - Free space on backup volume is $(( backupfree / 1024 / 1024 )) MBs" 2509 secho "----------------------------------------------------------------------------------"510 if [ $totalsize -gt $backupfree ]511 then512 answer=$(askQuestion "There may not be enough free space on Backup volume" "Continue" "Stop" 30)513 if [ "$answer" == "2" ] 514 then515 # error out ...516 errorHander "There is not enough space on the backup volume."517 else518 secho "WARNING: Ignoring free space error... I sure hope you know what you are doing!"519 fi520 fi521522 before=$(date +%s)523 backuptogo=$totalsize524 for (( i = 0 ; i < $usercount ; i++ ))525 do526 user="${userarray[$i]}"527 usersize="${sizearray[$i]}"528 usersettings="$backuppath/Users/$user/UserSettings"529 # write some details about the user530 defaults write $usersettings UserSize -string $usersize531 # this reads the local ds via localonly (reads ds on target disk - not the system we are booted from)532 networkusertest=$(dscl -f "$dslocal" localonly -read "/Local/Default/Users/$user" AuthenticationAuthority | grep LocalCachedUser) 533 if [ "$networkusertest" != "" ]534 then535 defaults write $usersettings NetworkUser -bool true536 usertype="Network User"537 else538 defaults write $usersettings NetworkUser -bool false539 usertype="Local User"540 fi541 secho542 secho "Backing up ($(( $i + 1 ))/$usercount): $user - $(( $usersize / 1024 / 1024 ))/$(( $backuptogo / 1024 / 1024 )) MBs - $usertype" 2543 secho "-------------------------------------------------------------------"544 mkdir -p "$backuppath/Users/$user"545 imagesize=$(( $usersize + (( $usersize / 4 )) )) # add 25% of padding to vol size546 # create a sparse image on the backup store547 secho "Creating image for $user of size $(( $imagesize / 1024 / 1024 )) MBs..." 1548 hdiutil create -size ${imagesize} -type SPARSE -fs HFS+J -volname _backup_$user "$backuppath/Users/$user/$user.sparseimage" 2>> "$logto/$log"549 error="$?"550 if [ "$error" != "0" ]551 then552 errorHander "Error creating backup image for $user - hdiutil returned error: $error"553 fi554 # attach our new image555 hdiutil attach -owners on "$backuppath/Users/$user/$user.sparseimage" 2>> "$logto/$log"556 if [ "$error" != "0" ]557 then558 errorHander "Error attaching backup image for $user - hdiutil returned error: $error"559 fi560 # write our rsync exclude file561 for rsyncexclude in ${rsyncexcludes[@]}562 do563 echo "$rsyncexclude" >> /tmp/rsync-excludes.txt 564 done565 # rsync all data566 rsync $rsyncopts -aE --log-file="$logto/$log" --exclude-from="/tmp/rsync-excludes.txt" "$userfolder/$user/" "/Volumes/_backup_$user/$user/"567 error="$?"568 rm /tmp/rsync-excludes.txt 569570 # rsync error handler - we can ignore a couple of common errors571 if [ "$error" != "0" ]572 then573 case $error in574 23)575 secho "rsync error 23 - some files were inaccessible (probably in use)"576 ;;577 24)578 secho "rsync error 24 - some files disappeared during rsync"579 ;;580 12)581 errorHander "Problem backing $user, out of disk space"582 ;;583 *)584 errorHander "Problem backing up $user, rsync returned error: $error"585 ;;586 esac587 fi588 secho "Ejecting /Volumes/_backup_$user ..."589 diskutil eject "/Volumes/_backup_$user"590 [ "$?" != "0" ] && errorHander "There was a problem ejecting /Volumes/_backup_$user"591 # decrement our backupdata to go...592 backuptogo=$(( $backuptogo - $usersize )) 593 done594 after=$(date +%s)595 totalsecs=$(( $after - $before ))596 # echo "Completed in $(($diff / 60)):$(($diff % 60)) min:secs"597 totaltime="$(date -r $totalsecs +%M:%S) secs"598599 [ $showlog ] && killall Console600601 defaults write $backupsettings BackupDate -string "$(date)"602 defaults write $backupsettings TotalTime -string "$totaltime"603 defaults write $backupsettings TotalSize -string "$totalsize"604 defaults write $backupsettings HasBeenRestored -bool false605 defaults write $backupsettings BackupComplete -bool true606607 secho608 secho "=========================================="609 secho " Completed $(( $totalsize / 1024 / 1024 )) MBs in $totaltime" 3610 secho "=========================================="611 secho "finished: $(date)"612 secho613 secho "NOTE: this script only backs up User folders in $userfolder"614 secho " any files outside of this have NOT been backup up."615 secho616}617618619restoreUsers()620{621 # check for no backup flag622 if [ -f "/tmp/.nobackup-$uniqueid" ]623 then624 secho "No backup performed for $uniqueid..." 2625 rm "/tmp/.nobackup-$uniqueid"626 return 0627 fi628629 while true630 do631 if [ -d "$backuppath" ] # if there is a backup for this mac restore632 then633 #if its already been restored, error out634 if [ "$(defaults read $backupsettings HasBeenRestored)" == "1" ]635 then636 answer=$(askQuestion "Backup $uniqueid has already been restored" "Continue with Restore" "Choose another backup..." )637 if [ "$answer" == "2" ]638 then639 # to choose another... reset backup path and reloop640 backuppath=""641 continue642 fi643 fi644 backupdate=$(defaults read $backupsettings BackupDate)645 totaltime=$(defaults read $backupsettings TotalTime)646 totalsize=$(defaults read $backupsettings TotalSize)647 if ! $caspermode648 then649 # if we are in interactive mode.. present options650 # read in backup details and make a dialog message651 message=$(652 echo -ne "Backup Details653--------------------654$uniqueid655Totalsize: $(( $totalsize / 1024 / 1024 )) MBs656Completed in: $totaltime"657 echo -ne "658659"660 echo -ne "Users661"662 echo -ne "------663"664 #665 # read in all the backups on the store666 #667 [ -f "$backuppath/Users/.DS_Store" ] && rm "$backuppath/Users/.DS_Store" # just in case someone has been poking around in Users668 for backupuserfolder in $(ls "$backuppath/Users/")669 do670 user=$(basename $backupuserfolder)671 usersize=$(defaults read "$backuppath/Users/$backupuserfolder/UserSettings" UserSize)672 echo -ne "$user - $(( $usersize / 1024 / 1024 )) MBs673"674 done675 echo -ne "676677"678 )679 answer=$(askQuestion "$message" "Restore" "Choose another backup..." )680 if [ "$answer" == "2" ]681 then682 # to choose another... reset backup path and reloop683 backuppath=""684 continue685 fi686 fi687688 # ask to remove backup, do it before we start the restore so it can be unattended689 answer=$(askQuestion "Would you like to delete the backup once it has been restore successfully?" "Delete Backup" "Keep" 20)690 if [ "$answer" == "1" ]691 then692 removebackup=true693 else694 removebackup=false695 fi696697 # do the restore698 cp "$logto/$log" "$backuppath/logs/$log"699 rm "$logto/$log"700 logto="$backuppath/logs"701702 # open the log in console for more feedback703 [ $showlog ] && open "$logto/$log"704705 secho "$uniqueid - Restore size: $(( $totalsize / 1024 / 1024 )) MBs" 2706 secho 707 before=$(date +%s)708 backuptogo=$totalsize709 [ -f "$backuppath/Users/.DS_Store" ] && rm "$backuppath/Users/.DS_Store" # just in case someone has been poking around in Users710 for userroot in "$backuppath/Users/"*711 do712 user=$(basename $userroot)713 usersettings="$backuppath/Users/$user/UserSettings"714 usersize=$(defaults read $usersettings UserSize)715 if [ "$(defaults read $usersettings NetworkUser)" == "1" ]716 then717 usertype="Network"718 else719 usertype="Local"720 fi721 secho722 secho "Restoring: $user $(( $usersize / 1024 / 1024 ))/$(( $backuptogo / 1024 / 1024 )) MBs - $usertype account" 2723 secho "-------------------------------------------------------------------"724725 hdiutil attach -owners on "$userroot/$user.sparseimage" 2>> "$logto/$log"726 error="$?"727 if [ "$error" != "0" ]728 then729 errorHander "Error attaching backup image for $user - hdiutil returned error: $error"730 fi731 rsync $rsyncopts -aE --log-file="$logto/$log" "/Volumes/_backup_$user/$user/" "$restoreto/$user/" 732 error="$?" 733 # rsync error handler734 if [ "$error" != "0" ]735 then736 case $error in737 23)738 secho "rsync error 23 - some files were inaccessible (probably in use)"739 ;;740 24)741 secho "rsync error 24 - some files disappeared during rsync"742 ;;743 12)744 errorHander "Problem backing $user, out of disk space"745 ;;746 *)747 errorHander "Problem backing up $user, rsync returned error: $error"748 ;;749 esac750 fi751 secho "Ejecting /Volumes/_backup_$user ..."752 diskutil eject "/Volumes/_backup_$user"753 [ "$?" != "0" ] && errorHander "There was a problem ejecting /Volumes/_backup_$user"754 # decrement data755 backuptogo=$(( $backuptogo - $usersize ))756 done757 after=$(date +%s)758 totalsecs=$(( $after - $before ))759 totaltime="$(date -r $totalsecs +%M:%S) secs"760 defaults write $backupsettings HasBeenRestored -bool true761 secho762 secho "=========================================="763 secho " Completed $(( $totalsize / 1024 / 1024 )) MBs in $totaltime" 3764 secho "=========================================="765 secho "finished: $(date)"766 cp "$logto"/*.log "$target/Library/Logs/" # copy logs to target when done767768 [ $showlog ] && killall Console769770 # remove them when done771 if $removebackup772 then773 # delete the backup 774 secho "Deleting $backuppath" 2775 while [ -d "$backuppath" ]776 do777 # network shares have some locks / trashes that hold up proceedings, just keep hitting it until it dies778 rm -R "$backuppath"779 sleep 1780 done781 else782 defaults write $backupsettings HasBeenRestored -bool true783 fi784 break785 else786 # no backups found for restore787 if $caspermode788 then789 secho "There are no backups for $uniqueid" 4790 break791 else792 if [ ! "$(ls $backuproot)" ] # check if the backup folder is empty793 then794 secho "There are no backups on $backuproot"795 break796 fi797 # interactive mode choose another backup and loop back798 chooseBackup799 # we want to quit?800 [ "$?" == "2" ] && break 801 fi802 fi803 done804}805806807################################808# Begin!809################################810811812datestamp=$(date "+%F_%H-%M-%S")813814logto="/tmp"815log="$scriptname-$datestamp.log"816817if $debug818then819 # dump debug logs to /820 set -xv; exec 1>/BackupRestoreUsers-DEBUG-$datestamp.log 2>&1821fi822823dscl="/usr/bin/dscl" ### test - we should run dscl from current booted os. assumed that netboot system will be > system if upgrading.824dslocal="/var/db/dslocal/nodes/Default" # path to local directory data825826target="$1" # casper passes .. $1 - target, $2 - computername, $3 username --- we only need target path.827[ "$target" == "/" ] && target="" # if we are targetting / - set target to "" so we don't end up with //Users828userfolder="$target$userfolder" # eg. /Volumes/MacHD/Users829restoreto="$target$restoreto" #830dslocal="$target$dslocal" # etc..831832# if there we are in cdpmode, find the cdp server, and set url833[ $cdpmode ] && findServer834835# just drop afp or smb url into this variable836networkbackuppath="$afpurl$smburl"837838# uniqueid - serial or MAC address of en0839uniqueid=$(ioreg -c "IOPlatformExpertDevice" | awk -F '"' '/IOPlatformSerialNumber/ {print $4}')840[ "$uniqueid" == "" ] && uniqueid=$(ifconfig en0 | awk ' /ether/ {print $2}') # if no serial, fallback to MAC address841842# set the IFS to cr843OLDIFS=$IFS844IFS=$'845' 846847#848# Lets Go!849# 850secho851secho "$scriptname $mode $version - $uniqueid" 1852secho "================================================="853secho "started: $(date)"854secho855856# if we have a network path, mount it857[ "$networkbackuppath" != "" ] && mountShare858859[ ! -d "$backuproot" ] && errorHander "I can't find $backuproot"860861backuppath="$backuproot/$uniqueid"862backupsettings="$backuppath/BackupSettings"863864#865# Core handler .. parse mode line etc866#867868if $caspermode869then870 # mode is hard coded871 case $mode in872 backup )873 backupUsers874 ;;875 restore )876 restoreUsers877 ;;878 esac879else880 # interactive mode called by wrappers881 case $2 in882 --backup )883 backupUsers884 ;;885 --restore )886 restoreUsers887 ;;888 * )889 usage890 ;;891 esac892fi893894# unmount network share895[ "$networkbackuppath" != "" ] && unmountShare896897IFS=$OLDIFS898exit