forked from patchoo/patchoo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
0patchoo.sh
executable file
·1897 lines (1659 loc) · 62.7 KB
/
0patchoo.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/bash
#
# patchoo!
# ========
# Casper patching done right!
#
# https://github.com/patchoo/patchoo
#
# patchoo somewhat emulates munki workflows and user experience for JAMF's Casper.
#
###################################
#
# start configurable settings
#
###################################
name="patchoo"
version="0.9958"
# read only api user please!
apiuser="apiuser"
apipass="apipass"
datafolder="/Library/Application Support/patchoo"
pkgdatafolder="$datafolder/pkgdata"
prefs="$datafolder/com.github.patchoo"
cdialog="/Applications/Utilities/cocoaDialog.app" #please specify the appbundle rather than the actual binary
jamfhelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
# Where's the jamf binary stored? This is for SIP compatibility.
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
# a custom trigger to install cocoadialog if it's not present
installcdttrigger="installcd"
# if you are using a self signed cert for you jss, tell curl to allow it.
selfsignedjsscert=true
# users can defer x update prompts
defermode=true
defaultdeferthresold="10"
# REALLY forces a logout when defers run out
nastymode=true
# users running blocking apps will have x number of prompts delayed, ie will not run on prompt/remindto install until threshold is reached
blockingappmode=true
defaultblockappthreshold="2" # if missed at lunch, then 2x2 hours later... should prompt in afternoon?
# if these apps are in the foreground notifications will not be displayed, presentation apps ? (check with names- sleep 5; osascript -e 'tell application "System Events"' -e 'set frontApp to name of first application process whose frontmost is true' -e 'end tell')
blockingapps=( "Microsoft PowerPoint" "Keynote" )
# this order will correspond to the updatetriggers and asurelease catalogs
# eg. jssgroup[2]="patchooBeta"
# updatetrigger[2]="update-beta"
# asureleasecatalog[2]="beta"
#
# index 0 is the production group and is assumed unless the client is a member of any other groups
jssgroup[0]="----PRODUCTION----"
jssgroup[1]="patchooDev"
jssgroup[2]="patchooBeta"
# these triggers are run based on group membership, index 0 is run after extra group.
patchooswreleasemode=true
updatetrigger[0]="update"
updatetrigger[1]="update-dev"
updatetrigger[2]="update-beta"
# if using patchoo asu release mode these will be appended to computer's SoftwareUpdate server catalogs as per reposado forks -- if not using asu release mode the computer's SwUpdate server will remain untouched.
# eg. http://swupdate.your.domain:8088/content/catalogs/others/index-leopard.merged-1${asureleasecatalogs[i]}.sucatalog
patchooasureleasemode=true
asureleasecatalog[0]="prod"
asureleasecatalog[1]="dev"
asureleasecatalog[2]="beta"
#
# patchooDeploy settings
#
pdusebuildea=true
pdusesites=true
pdusedepts=true
pdusebuildings=true
pdsetcomputername=true # prompt to set computername
# the name of your ext attribute to use as the patchooDeploy build identfier - a populated dropdown EA.
pdbuildea="patchoo Build"
# do you want to prompt the console user to set attributes post enrollment? (not possible post casper imaging)
pdpromptprovisioninfo=true
# this api user requires update/write access to computer records (somewhat risky putting in here - see docs)
# leaving blank will prompt console user for a jss admin account during attribute set (as above)
pdapiadminname=""
pdapiadminpass=""
pddeployreceipt="/Library/Application Support/JAMF/Receipts/patchooDeploy" # this fake receipt internally, and to communicate back to the jss about different patchoo deploy states.
#########################################
#
# configure user prompts and feedback.
#
#########################################
msgtitlenewsoft="New Software Available"
msgnewsoftware="The following new software is available"
msginstalllater="(You can perform the installation later via Self Service)"
msgnewsoftforced="The following software must be installed now!"
msgbootstrap="Mac is provisioning. Do not interrupt or power off."
msgbootstapdeployholdingpattern="Awaiting provisioning information. Your admin has been notified."
msgpatchoodeploywelcome="Welcome to patchoo deploy.
We are gathering provisioning information"
msgshortfwwarn="
IMPORTANT: A firmware update will be installed.
Ensure you connect AC power before starting the update process."
msgshortoswarn="
IMPORTANT: A major OS X upgrade will be performed.
Ensure you connect AC power before starting the update process.
It could take up to 90 minutes to complete."
msgfirmwarewarning="
Firmware updates will be installed after your computer restarts.
Please ensure you are connected to AC Power! Do NOT touch any keys or the power button! A long tone will sound and your screen may be blank for up to 5 minutes.
IT IS VERY IMPORTANT YOU DO NOT INTERRUPT THIS PROCESS AS IT MAY LEAVE YOUR MAC INOPERABLE"
msgosupgradewarning="
Your computer is peforming a major OS X upgrade.
Please ensure you are connected to AC Power! Your computer will restart and the OS upgrade process will continue. It will take up to 90 minutes to complete.
IT IS VERY IMPORTANT YOU DO NOT INTERRUPT THIS PROCESS AS IT MAY LEAVE YOUR MAC INOPERABLE"
iconsize="72"
dialogtimeout="210"
lockscreenlogo="/System/Library/CoreServices/Installer.app/Contents/Resources/Installer.icns" # used for fauxLogout (ARD LockScreen will display this) and bootstrap
# log to the jamf log.
logto="/var/log/"
log="jamf.log"
##################################
##################################
##
## end of configurable settings
##
##################################
##################################
osxversion=$(sw_vers -productVersion | cut -f-2 -d.) # we don't need patch version
udid=$( ioreg -rd1 -c IOPlatformExpertDevice | awk '/IOPlatformUUID/ { split($0, line, "\""); printf("%s\n", line[4]); }' )
OLDIFS="$IFS"
IFS=$'\n'
if [ ! -d "$cdialog" ]
then
echo "INFO: CocoaDialog is missing. Triggering JSS install policy."
$jamf_binary policy -trigger $installcdttrigger
fi
# command line paramaters
mode="$4"
prereqreceipt="$5"
prereqpolicy="$(echo "$6" | sed -e 's/ /\+/g')" # change out " " for +
option="$7"
spawned="$1" # used internally
if $selfsignedjsscert
then
curlopts="-k"
else
curlopts=""
fi
cdialogbin="${cdialog}/Contents/MacOS/cocoaDialog"
bootstrapagent="/Library/LaunchAgents/com.github.patchoo-bootstrap.plist"
jssgroupfile="$datafolder/$name-jssgroups.tmp"
# set and read preferences
computername=$(scutil --get ComputerName)
jssurl=$(defaults read /Library/Preferences/com.jamfsoftware.jamf "jss_url" 2> /dev/null)
daystamp=$(( $(date +%s) / 86400 )) # days since 1-1-70
# due to issue with cocoaDialog outside of user session, this check as been added
#case $osxversion in
# 10.5 | 10.6 | 10.7 | 10.8 )
# displayatlogout=true
# ;;
# *)
# displayatlogout=false
# ;;
#esac
# cocoaDialog issue fixed - using https://github.com/loceee/cocoadialog. we need to undo fauxLogout and change workflow back at some stage.
displayatlogout=true
# create the data folder if it doesn't exist
[ ! -d "$datafolder" ] && mkdir -p "$datafolder"
[ ! -d "$pkgdatafolder" ] && mkdir -p "$pkgdatafolder"
chmod 700 "$datafolder"
# if there is no receipt dir, best make one... derp.
[ ! -d "/Library/Application Support/JAMF/Receipts" ] && mkdir -p "/Library/Application Support/JAMF/Receipts"
# DEBUG STUFF
if [ -f "$datafolder/.patchoo-debug" ]
then
set -vx # DEBUG.
debugpath="$datafolder/debuglogs"
mkdir -p "$debugpath"
debuglogfile="$debugpath/patchoo${mode}-$(date "+%F_%H-%M-%S").log"
exec > "$debuglogfile" 2>&1
fi
# check and write installs avail
installsavail=$(defaults read "$prefs" InstallsAvail 2> /dev/null)
if [ "$?" != "0" ]
then
defaults write "$prefs" InstallsAvail -string "No"
installsavail="No"
fi
# set defaults for defer and blockingapp counts
# defer is the # of times a user can defer updates
deferthreshold=$(defaults read "$prefs" DeferThreshold 2> /dev/null)
if [ "$?" != "0" ]
then
defaults write "$prefs" DeferThreshold -int $defaultdeferthresold
deferthreshold=$defaultdeferthresold
fi
defercount=$(defaults read "$prefs" DeferCount 2> /dev/null)
if [ "$?" != "0" ]
then
defaults write "$prefs" DeferCount -int 0
defercount=0
fi
# blockingapp is the # of times a blocking app can block a prompt
blockappthreshold=$(defaults read "$prefs" BlockingAppThreshold 2> /dev/null)
if [ "$?" != "0" ]
then
defaults write "$prefs" BlockingAppThreshold -int $defaultblockappthreshold
blockappthreshold=$defaultblockappthreshold
fi
blockappcount=$(defaults read "$prefs" BlockingAppCount 2> /dev/null)
if [ "$?" != "0" ]
then
defaults write "$prefs" BlockingAppCount -int 0
blockappcount=0
fi
# if the bootstrap agent exists, set bootstrapmode
if [ -f "$bootstrapagent" ]
then
bootstrapmode=true
else
bootstrapmode=false
fi
# make tmp folder
patchootmp="$(mktemp -d -t patchoo)"
#
# common functions
#
secho()
{
# superecho - writes to log and will display a dialog to gui with timeout
message="$1"
timeout="$2"
title="$3"
icon="$4"
if [ "$timeout" != "" ]
then
echo "$name: USERNOTIFY: $title, $message"
echo "$(date "+%a %b %d %H:%M:%S") $computername $name-$version $mode: USERNOTIFY: $title, $message" >> "$logto/$log"
if [ "$(checkConsoleStatus)" == "userloggedin" ]
then
[ "$title" == "" ] && title="Message"
[ "$icon" == "" ] && icon="notice"
"$cdialogbin" bubble --title "$title" --text "$message" --icon "$icon" --timeout "$timeout" &
fi
else
echo "$name: $message"
echo "$(date "+%a %b %d %H:%M:%S") $computername $name-$version $mode: $message" >> "$logto/$log"
fi
}
displayDialog()
{
text="$1" # core message
title="$2" # menubar title
title2="$3" # bold title
icon="$4" # http://mstratman.github.io/cocoadialog/#documentation3.0/icons
button1="$5"
button2="$6"
button3="$7"
# show the dialog...
"$cdialogbin" msgbox --title "$title" --icon "$icon" --text "$title2" --informative-text "$text" --timeout "$dialogtimeout" --button1 "$button1" --button2 "$button2" --button3 "$button3" --icon-height "$iconsize" --icon-width "$iconsize" --width "500" --string-output
}
makeMessage()
{
message="$message
$1"
}
checkConsoleStatus()
{
userloggedin="$(who | grep console | awk '{print $1}')"
consoleuser="$(ls -l /dev/console | awk '{print $3}')"
screensaver="$(pgrep ScreenSaverEngine)"
if [ "$screensaver" != "" ]
then
# screensaver is running
echo "screensaver"
return
fi
if [ "$userloggedin" == "" ]
then
# no users logged in (at loginwindow)
echo "nologin"
return
fi
if [ "$userloggedin" != "$consoleuser" ]
then
# a user is loggedin, but we are at loginwindow or we have multiple users logged in with switching (too hard for now)
echo "loginwindow"
return
fi
if $blockingappmode
then
# get foreground app
fgapp=$(sudo -u "$userloggedin" osascript -e "tell application \"System Events\"" -e "return name of first application process whose frontmost is true" -e "end tell" 2> /dev/null) # avoid errors in log
# check for blocking apps
for app in ${blockingapps[@]}
do
if [ "$app" == "$fgapp" ]
then
echo "BlockingApp: $app"
return
fi
done
fi
# if we passed all checks, user is logged in and we are safe to prompt or display bubbles
echo "userloggedin"
}
checkProcess()
{
if [ "$(pgrep "$1")" != "" ]
then
return 0
else
return 1
fi
}
spawnScript()
{
# we use this so we can execute from self service.app and call a logout with out breaking policy execution.
# the script copies, then spawns itself
if [ "$spawned" != "--spawned" ]
then
tmpscript="$datafolder/$name-$RANDOM.sh"
cp "$0" "$tmpscript"
# spawn the script in the background
secho "spawned script $tmpscript"
"$tmpscript" --spawned '' '' $mode &
cleanUp
exit 0
fi
}
#
# the mains brains.
#
cachePkg()
{
# run after a pkg is cached in a policy
# - checks for prereqs and calls policies if receipts not found
# - gets pkg data from jss api and gives pkg friendly name in the gui
#
# find the latest addition to the Waiting Room
pkgname=$(ls -t "/Library/Application Support/JAMF/Waiting Room/" | head -n 1 | grep -v .cache.xml)
if [ ! -f "$pkgdatafolder/$pkgname.caspinfo" ] && [ "$pkgname" != "" ]
then
pkgext=${pkgname##*.} # handle zipped bundle pkgs
[ "$pkgext" == "zip" ] && pkgnamelesszip=$(echo "$pkgname" | sed 's/\(.*\)\..*/\1/')
# get pkgdata from the jss api
curl $curlopts -H "Accept: application/xml" -s -u "$apiuser:$apipass" "${jssurl}JSSResource/packages/name/$(echo "$pkgname" | sed -e 's/ /\+/g')" -X GET > "$pkgdatafolder/$pkgname.caspinfo.xml"
# (error checking)
pkgdescription=$(cat "$pkgdatafolder/$pkgname.caspinfo.xml" | xpath //package/info 2> /dev/null | sed 's/<info>//;s/<\/info>//')
if [ "$pkgdescription" == "<info />" ] || [ "$pkgdescription" == "" ] # if it's no pkginfo in jss, set pkgdescription to pkgname (less ext)
then
if [ "$pkgext" == "zip" ]
then
pkgdescription=$(echo "$pkgname" | sed 's/\(.*\)\..*/\1/')
else
pkgdescription=$(echo "$pkgnamelesszip" | sed 's/\(.*\)\..*/\1/')
fi
fi
echo "$pkgdescription" > "$pkgdatafolder/$pkgname.caspinfo"
# if it's flagged as an OS Upgrade (using createOSXInstallPkg), add osupgrade flag
[ "$option" == "--osupgrade" ] && touch "$pkgdatafolder/.os-upgrade"
secho "jamf has cached $pkgname"
secho "$pkgdescription" 2 "Downloaded" "globe"
# flag that we need a recon
touch "$datafolder/.patchoo-recon-required"
if [ "$prereqreceipt" != "" ]
then
# we need to check for a prereq casper receipt
if [ ! -f "/Library/Application Support/JAMF/Receipts/$prereqreceipt" ]
then
# the receipt wasn't found
# query the JSS for the prereqpolicy
secho "$prereqreceipt is required and NOT found"
secho "querying jss for policy $prereqpolicy to install $prereqreceipt"
prereqpolicyid=$(curl $curlopts -H "Accept: application/xml" -s -u "$apiuser:$apipass" "${jssurl}JSSResource/policies/name/$(echo "$prereqpolicy" | sed -e 's/ /\+/g')" -X GET | xpath //policy/general/id 2> /dev/null | sed -e 's/<id>//;s/<\/id>//')
# (error checking)
# let's run the preq policy via id
# this is how we chain incremental updates
$jamf_binary policy -id "$prereqpolicyid"
fi
fi
else
secho "i couldn't find a new pkg in the waiting room. :("
fi
}
checkASU()
{
if [ -f "$pkgdatafolder/.os-upgrade" ]
then
secho "os upgrade is cached, skipping apple software updates.."
return
fi
if $patchooasureleasemode
then
getGroupMembership
setASUCatalogURL
fi
swupdateout="$patchootmp/swupdateout-$RANDOM.tmp"
secho "checking for apple software updates ..."
softwareupdate -la > "$swupdateout"
# check if there are any updates
if [ "$(cat "$swupdateout" | grep "\*")" != "" ]
then
# let's parse the updates
asupkgarray=( $(cat "$swupdateout" | grep "\*" | cut -c6- ) )
asudescriptarray=( $(cat "$swupdateout" | grep -A2 "\*" | grep -v "\*" | cut -f1 -d, | cut -c2- | sed 's/[()]//g' ) )
# first clean up any packages that were installed from the appstore - thanks galenrichards
find "$pkgdatafolder" -iname "*.asuinfo" | while read f
do
basefile=$(basename "$f" ".asuinfo")
if [[ ! " ${asupkgarray[@]//.} " =~ " ${basefile//.} " ]]; then
secho "$basefile not available or already installed. Removing..."
rm $f
fi
done
i=0
for asupkg in ${asupkgarray[@]}
do
if [ ! -f "$pkgdatafolder/$asupkg.asuinfo" ] # it hasn't been downloaded
then
secho "softwareupdate is downloading $asupkg"
softwareupdate -d "$asupkg"
# (insert error checking)
echo "${asudescriptarray[$i]}" > "$pkgdatafolder/$asupkg.asuinfo"
secho "${asudescriptarray[$i]}" 2 "Downloaded" "globe"
# flag that we need a recon
touch "$datafolder/.patchoo-recon-required"
else
secho "$asupkg already downloaded."
fi
(( i ++ ))
done
# check for restart required
if [ "$(cat "$swupdateout" | grep "\[restart\]")" != "" ]
then
touch "$pkgdatafolder/.restart-required"
fi
else
secho "no updates found."
fi
rm "$swupdateout"
}
setASUCatalogURL()
{
# in patchooasureleasemode mode patchoo takes care of re-writing client catalog urls so you can have dev/beta/prod catalogs based on jss group membership
# you set your catalogURL / swupdate server as you usually do in Casper, and it will re-write OS and branch specific URLs based on the local CatalogURL.
# it assumes that you have turned off updates via other mechanisms
currentswupdurl=$(defaults read /Library/Preferences/com.apple.SoftwareUpdate CatalogURL 2> /dev/null)
if [ "$currentswupdurl" != "" ]
then
asuserver="$(echo "$currentswupdurl" | cut -f-3 -d/)"
case $osxversion in
10.5)
swupdateurl="$asuserver/content/catalogs/others/index-leopard.merged-1_${asureleasecatalog[$groupid]}.sucatalog"
;;
10.6)
swupdateurl="$asuserver/content/catalogs/others/index-leopard-snowleopard.merged-1_${asureleasecatalog[$groupid]}.sucatalog"
;;
10.7)
swupdateurl="$asuserver/content/catalogs/others/index-lion-snowleopard-leopard.merged-1_${asureleasecatalog[$groupid]}.sucatalog"
;;
10.8)
swupdateurl="$asuserver/content/catalogs/others/index-mountainlion-lion-snowleopard-leopard.merged-1_${asureleasecatalog[$groupid]}.sucatalog"
;;
10.9)
swupdateurl="$asuserver/content/catalogs/others/index-10.9-mountainlion-lion-snowleopard-leopard.merged-1_${asureleasecatalog[$groupid]}.sucatalog"
;;
10.10)
swupdateurl="$asuserver/content/catalogs/others/index-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1_${asureleasecatalog[$groupid]}.sucatalog"
;;
*)
secho "I can't do this OS X version.. sadface."
return
;;
esac
secho "setting asu CatalogURL to $swupdateurl"
defaults write /Library/Preferences/com.apple.SoftwareUpdate CatalogURL "$swupdateurl"
else
secho "no asu server set, using apple's ..."
fi
}
buildUpdateLists()
{
# make software install list tmp files for processing later
#
# don't expand a nullglob (if there are no matching *.xxx)
shopt -s nullglob
# casper pkgs
casppkginfo="$patchootmp/casppkginfo-$RANDOM.tmp"
for infofile in "$pkgdatafolder/"*.caspinfo
do
# parse the priority from casper xml
casppriority=$(cat "${infofile}.xml" | xpath //package/priority 2> /dev/null | sed 's/<priority>//;s/<\/priority>//')
casppkg=$(basename "$infofile") #get rid of path
casppkg="${casppkg%\.*}" #remove ext.
casppkgdescrip=$(cat "$infofile")
echo -e "${casppriority}\t${casppkg}\t${casppkgdescrip}" >> "$casppkginfo"
done
# if there is an OS Upgrade packge cached in the casper installs, skip the apple updates
if [ -f "$pkgdatafolder/.os-upgrade" ]
then
secho "osupgrade is in casper, skipping apple software updates"
else
# asu pkgs
asupkginfo="$patchootmp/asupkginfo-$RANDOM.tmp"
for infofile in "$pkgdatafolder/"*.asuinfo
do
# check for SMC, EFI and Firmware updates, flag if so
[ "$(echo "$infofile" | grep EFIUpdate)" != "" ] && touch "$pkgdatafolder/.fw-update"
[ "$(echo "$infofile" | grep SMCUpdate)" != "" ] && touch "$pkgdatafolder/.fw-update"
[ "$(echo "$infofile" | grep Firmware)" != "" ] && touch "$pkgdatafolder/.fw-update"
# set priorities for system and sec updates
asupriority=1
# OSX supplemental for 10.8.5 broke the rules, not OSXUpd... this could catch other things too... hmm.
[ "$(echo "$infofile" | grep OSX)" != "" ] && asupriority="98"
# if it's a security or OSX update make it 99
[ "$(echo "$infofile" | grep SecUpd)" != "" ] && asupriority="99"
[ "$(echo "$infofile" | grep OSXUpd)" != "" ] && asupriority="99"
asupkg=$(basename "$infofile") #get rid of path
asupkg="${asupkg%\.*}" #remove ext.
asupkgdescrip=$(cat "$infofile")
echo -e "${asupriority}\t${asupkg}\t${asupkgdescrip}" >> "$asupkginfo"
done
fi
[ -f "$casppkginfo" ] && sort "$casppkginfo" -o "$casppkginfo" # sort the file for priority
[ -f "$asupkginfo" ] && sort "$asupkginfo" -o "$asupkginfo" # sort the file for priority
if [ -f "$casppkginfo" ] || [ -f "$asupkginfo" ]
then
# installs are available, write pref, it also will be picked up by ext attribute to make a smart group.
defaults write "$prefs" InstallsAvail -string "Yes"
installsavail="Yes"
fi
# some output to the log file if not --quiet
if [ -f "$casppkginfo" ] && [ "$1" != "--quiet" ]
then
secho "Casper pkgs waiting to be installed"
secho "--------------------------------------"
while read line
do
secho "$(echo "$line" | cut -f2)"
done < "$casppkginfo"
secho "--------------------------------------"
fi
if [ -f "$asupkginfo" ] && [ "$1" != "--quiet" ]
then
secho "swupdate pkgs waiting to be installed"
secho "-------------------------------------"
while read line
do
secho "$(echo "$line" | cut -f2)"
done < "$asupkginfo"
secho "-------------------------------------"
fi
}
installCasperPkg()
{
caspline="$1"
casppkg=$(echo "$caspline" | cut -f2)
infofile="$pkgdatafolder/${casppkg}.caspinfo"
jamfinstallopts=""
# check if a reboot is required by casper package, flag if it is.
[ "$(cat "${infofile}.xml" | grep "<reboot_required>true</reboot_required>")" != "" ] && touch "$pkgdatafolder/.restart-required"
# check for fut and feu
[ "$(cat "/Library/Application Support/JAMF/Waiting Room/$casppkg.cache.xml" | grep "<fut>true</fut>")" != "" ] && jamfinstallopts="$jamfinstallopts -fut"
[ "$(cat "/Library/Application Support/JAMF/Waiting Room/$casppkg.cache.xml" | grep "<feu>true</feu>")" != "" ] && jamfinstallopts="$jamfinstallopts -feu"
secho "jamf is installing $casppkg"
$jamf_binary install "$jamfinstallopts" -package "$casppkg" -path "/Library/Application Support/JAMF/Waiting Room" -target /
# (insert error checking)
# remove from the waiting room
if [ -d "/Library/Application Support/JAMF/Waiting Room/$casppkg" ]
then
# non-flat pkg
rm -R "/Library/Application Support/JAMF/Waiting Room/$casppkg"
else
# flat pkg
rm "/Library/Application Support/JAMF/Waiting Room/$casppkg"
fi
rm "/Library/Application Support/JAMF/Waiting Room/$casppkg.cache.xml"
}
installSoftware()
{
secho "starting installation ..."
# generate the update list tmp files
buildUpdateLists --quiet
# install all software
if [ -s "$casppkginfo" ] # there are casper updates waiting
then
if $bootstrapmode
then
# bootstrap mode doesn't need cocoadialog progress
while read line
do
installCasperPkg "$line"
done < "$casppkginfo"
else
(
# use cocoadialog for gui
currentpercent=0
casptotal=$(cat "$casppkginfo" | wc -l)
total=$(( casptotal * 100 ))
while read line
do
casppkgdescrip=$(echo "$line" | cut -f3)
installCasperPkg "$line" & # background the jamf install, we'll fudge a progressbar
caspinstallpid=$!
# we are fudging a progress bar, count up to 100, increase bar, until done, then
for (( perfectcount=1; perfectcount<=100; perfectcount++ ))
do
percent=$(( ( (perfectcount + currentpercent) * 100 ) / total ))
(( percent == 100 )) && percent=99 # we don't want out progressbar to finish prematurely
echo "$percent Installing $casppkgdescrip ..."
kill -0 "$caspinstallpid" 2> /dev/null
[ "$?" != "0" ] && break # if it's done, break
sleep 1
done
wait "$caspinstallpid" # if we have run out progress bar, wait for pid to complete.
currentpercent=$(( currentpercent + 100 )) # add another 100 for each completed install
done < "$casppkginfo"
echo "100 Installation complete"
sleep 1
[ -f "$pkgdatafolder/.restart-required" ] && echo "100 Restart is required"
sleep 1
) | "$cdialogbin" progressbar --icon installer --float --title "Installing Software" --text "Starting Installation..." --icon-height "$iconsize" --icon-width "$iconsize" --width "500" --height "114"
fi
fi
if [ -s "$asupkginfo" ] # there are apple updates waiting
then
asucount=0
# bootstrap mode, no progress bars
if $bootstrapmode
then
while read line
do
asupkg=$(echo "$line" | cut -f2)
asupkgdescrip=$(echo "$line" | cut -f3)
secho "softwareupdate is installing $asupkg ..."
softwareupdate -v -i "$asupkg"
done < "$asupkginfo"
else
(
currentpercent=0
asutotal=$(cat "$asupkginfo" | wc -l)
total=$(( asutotal * 100 ))
while read line
do
asupkg=$(echo "$line" | cut -f2)
asupkgdescrip=$(echo "$line" | cut -f3)
secho "softwareupdate is installing $asupkg ..."
# spawn the update process, and direct output to tmpfile for parsing (we probably should use a named pipe here... future...)
swupdcmd="softwareupdate -v -i $asupkg"
swupdateout="$patchootmp/swupdateout-$RANDOM.tmp"
softwareupdate -v -i "$asupkg" > "$swupdateout" &
softwareupdatepid=$!
# wait for the software update to finish, parse output of softwareupdate
while kill -0 "$softwareupdatepid" > /dev/null 2>&1
do
sleep 1
# get percent to update progressbar
percentout=$(cat "$swupdateout" | grep "Progress:" | tail -n1 | awk '{print $2}' | sed 's/\%//g')
percent=$(( ( (percentout + currentpercent) * 100 ) / total ))
echo "$percent Installing $asupkgdescrip ..."
done
currentpercent=$(( currentpercent + 100 )) # add another 100 for each completed install
rm "$swupdateout"
done < "$asupkginfo"
echo "100 Installation complete"
sleep 1
[ -f "$pkgdatafolder/.restart-required" ] && echo "100 Restart is required"
sleep 1
) | "$cdialogbin" progressbar --icon installer --float --title "Installing Apple Software Updates" --text "Starting Installation..." --icon-height "$iconsize" --icon-width "$iconsize" --width "500" --height "114"
fi
fi
# flag for a recon since we've installed
touch "$datafolder/.patchoo-recon-required"
# check for restart
if [ -f "$pkgdatafolder/.restart-required" ]
then
secho "restart is required by pkg"
touch /tmp/.patchoo-restart
fi
# if there was an OS upgrade installed, flush out apple updates from system.
if [ -f "$pkgdatafolder/.os-upgrade" ]
then
secho "flushing apple software updates..."
rm -R /Library/Updates/*
touch /tmp/.patchoo-restart
fi
# reset defer counters and flush pkgdata
defaults write "$prefs" DeferCount -int 0
defaults write "$prefs" InstallsAvail -string "No"
installsavail="No"
rm -R "$pkgdatafolder"
rm /tmp/.patchoo-install
}
promptInstall()
{
promptmode="$1"
# build the lists of updates avail
if $bootstrapmode
then
# if boostrapping, build updatelists (also sets installsavail)
buildUpdateLists --quiet
return
else
buildUpdateLists
fi
# if there are no updates
if [ "$installsavail" != "Yes" ]
then
secho "nothing to install"
return
fi
# prompt in self service mode (no defer, and remove flag)
if [ -f "$datafolder/.patchoo-selfservice-check" ]
then
promptmode="--selfservice"
rm "$datafolder/.patchoo-selfservice-check"
fi
# there are waiting updates ... make a message for the user prompt
secho "prompting user..."
message=""
if [ -f "$casppkginfo" ]
then
while read line
do
makeMessage "$(echo "$line" | cut -f3)" # 3rd column is pkg descript
done < "$casppkginfo"
fi
if [ -f "$asupkginfo" ]
then
while read line
do
makeMessage "$(echo "$line" | cut -f3)"
done < "$asupkginfo"
fi
# add warnings if there are firmware/os upgrade pkgs
addWarnings
case $promptmode in
"--logoutinstallsavail" )
#
# logout reminder prompt flags and 'returns' as we are already at a logout, so we can install directly within this session.
#
makeMessage ""
makeMessage "$msginstalllater"
answer=$(displayDialog "$message" "$msgtitlenewsoft" "$msgnewsoftware" "package" "Install and Restart..." "Install and Shutdown..." "Later")
case $answer in
"Install and Restart..." )
secho "user selected install and restart"
touch /tmp/.patchoo-install
touch /tmp/.patchoo-restart
preInstallWarnings
return
;;
"Install and Shutdown..." )
secho "user selected install and shutdown"
touch /tmp/.patchoo-install
touch /tmp/.patchoo-shutdown
preInstallWarnings
return
;;
"Later" )
secho "user selected install later"
return
;;
"timeout" )
secho "timeout... will install and shutdown, the user is probably going home"
touch /tmp/.patchoo-install
touch /tmp/.patchoo-shutdown
preInstallWarnings
return
;;
esac
;;
"--selfservice" )
#
# self service we don't tell people to use self service, we don't update the defer counter
#
answer=$(displayDialog "$message" "$msgtitlenewsoft" "$msgnewsoftware" "package" "Logout and Install..." "Cancel" )
;;
*)
#
# this is the general prompt for the end of the update trigger run
#
# check to see if we can display a prompt
consolestatus="$(checkConsoleStatus)"
# some users just have blocking apps running constantly (Powerpoint / Keynote)
# we need to prompt them at some stage.
if $blockingappmode
then
if [ "$(echo "$consolestatus" | grep "BlockingApp:")" != "" ]
then
blockremain=$(( blockappthreshold - blockappcount ))
if [ $blockremain -eq 0 ]
then
# blockingapp threshold exceeded, we will prompt user ...
consolestatus="userloggedin"
defaults write "$prefs" BlockingAppCount -int 0
else
(( blockappcount ++ ))
secho "$consolestatus - preventing prompt to install."
secho "blockingapp counter: $blockappcount, blockappthreshold: $blockappthreshold"
defaults write "$prefs" BlockingAppCount -int $blockappcount
fi
fi
fi
# is userloggedin, we should display a prompt
if [ "$consolestatus" == "userloggedin" ]
then
if $defermode
then
# check to see if they are allowed to defer anymore
deferremain=$(( deferthreshold - defercount ))
if [ $deferremain -eq 0 ] || [ $deferremain -lt 0 ]
then
# if the defercounter has run out, FORCED INSTALLATION! set timeout to 30 minutes
dialogtimeout="1830"
answer=$(displayDialog "$message" "$msgtitlenewsoft" "$msgnewsoftforced" "package" "Logout and Install...")
# if it's nastymode (tm) we Logout and Install no matter what
[ $nastymode ] && answer="Logout and Install..."
secho "FORCING INSTALL!"
else
# prompt user with defer option
makeMessage ""
makeMessage "$msginstalllater"
answer=$(displayDialog "$message" "$msgtitlenewsoft" "$msgnewsoftware" "package" "Later ($deferremain remaining)" "Logout and Install...")
secho "deferral counter: $defercount, defer thresold: $deferthreshold"
fi
else
# if we don't have deferals enabled
makeMessage ""
makeMessage "$msginstalllater"
answer=$(displayDialog "$message" "$msgtitlenewsoft" "$msgnewsoftware" "package" "Later" "Logout and Install...")
fi
else
# there something preventing a dialog, don't display anything, return the consolestatus
answer="$consolestatus"
fi
;;
esac
# process the answer.
case $answer in
"Logout and Install..." )
# this flags for install, and logs out the user, logout policy picks up the install flag and does installations.
secho "user selected install and logout..."
# we need to logout the user
touch /tmp/.patchoo-install
echo $$ > /tmp/.patchoo-install-pid
preInstallWarnings
fauxLogout & # we spawn it, so if anything goes awry during install, the fauxLogout is a killswitch to put the mac back into line.
# wait for the fauxlogout to do it's thing
while [ ! -f /tmp/.patchoo-logoutdone ]
do
sleep 1
done
installSoftware
rm /tmp/.patchoo-logoutdone
return # once this finishes fauxLogut will handle logout
;;
"Later ($deferremain remaining)" )
# this decreases counter and displays a notification bubble.
secho "user selected install later, incrementing deferal counter.."
(( defercount ++ ))
defaults write "$prefs" DeferCount -int $defercount
deferremain=$(( deferthreshold - defercount ))
if [ $deferremain -eq 0 ]
then
secho "You cannot defer the installation any further. It will be forced on next notice" 8 "Installion Deferred" "caution"
else
secho "You can defer the installation $deferremain more times" 8 "Installion Deferred" "notice"
fi
;;
"Later" )
secho "user chose later" # no deferals
;;
"Cancel" )
secho "user cancelled installation" # only available from self service