-
Notifications
You must be signed in to change notification settings - Fork 0
/
combinedscript2.sh
3704 lines (3528 loc) · 150 KB
/
combinedscript2.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/zsh
label="" # if no label is sent to the script, this will be used
# Installomator
#
# Downloads and installs an Applications
# 2020 Armin Briegel - Scripting OS X
#
# inspired by the download scripts from William Smith and Sander Schram
# with additional ideas and contribution from Isaac Ordonez, Mann consulting
# and help from Søren Theilgaard (theilgaard.dk)
VERSION='0.5.0'
VERSIONDATE='2021-04-13'
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
# NOTE: adjust these variables:
# set to 0 for production, 1 for debugging
# while debugging, items will be downloaded to the parent directory of this script
# also no actual installation will be performed
DEBUG=0
# notify behavior
NOTIFY=success
# options:
# - success notify the user on success
# - silent no notifications
# - all all notifications (great for Self Service installation)
# behavior when blocking processes are found
BLOCKING_PROCESS_ACTION=prompt_user_then_kill
# options:
# - ignore continue even when blocking processes are found
# - silent_fail exit script without prompt or installation
# - prompt_user show a user dialog for each blocking process found
# abort after three attempts to quit
# (only if user accepts to quit the apps, otherwise
# the update is cancelled).
# - prompt_user_then_kill
# show a user dialog for each blocking process found,
# attempt to quit two times, kill the process finally
# - prompt_user_loop
# Like prompt-user, but clicking "Not Now", will just wait an hour,
# and then it will ask again.
# - tell_user User will be showed a notification about the important update,
# but user is only allowed to quit and continue, and then we
# ask the app to quit.
# - tell_user_then_kill
# Show dialog 2 times, and if the quitting fails, the
# blocking processes will be killed.
# - kill kill process without prompting or giving the user a chance to save
# logo-icon used in dialog boxes if app is blocking
if [ ! -z "${8}" ]; then
LOGO="${8}"
else
LOGO=appstore
fi
# options:
# - appstore Icon is Apple App Store (default)
# - jamf JAMF Pro
# - mosyleb Mosyle Business
# - mosylem Mosyle Manager (Education)
# path can also be set in the command call, and if file exists, it will be used, like 'LOGO="/System/Applications/App\ Store.app/Contents/Resources/AppIcon.icns"' (spaces are escaped).
# install behavior
INSTALL=""
#INSTALL=force
# options:
# - When not set, software will only be installed
# if it is newer/different in version
# - force Install even if it’s the same version
# Re-opening of closed app
REOPEN="yes"
# options:
# - yes App wil be reopened if it was closed
# - no App not reopened
# NOTE: How labels work
# Each workflow label needs to be listed in the case statement below.
# for each label these variables can be set:
#
# - name: (required)
# Name of the installed app.
# This is used to derive many of the other variables.
#
# - type: (required)
# The type of the installation. Possible values:
# - dmg
# - pkg
# - zip
# - tbz
# - pkgInDmg
# - pkgInZip
# - appInDmgInZip
# - updateronly This last one is for labels that should only run an updateTool (see below)
#
# - packageID: (optional)
# The package ID of a pkg
# If given, will be used to find version of installed software, instead of searching for an app.
# Usefull if a pkg does not install an app.
# See label installomator_st
#
# - downloadURL: (required)
# URL to download the dmg.
# Can be generated with a series of commands (see BBEdit for an example).
#
# - appNewVersion: (optional)
# Version of the downloaded software.
# If given, it will be compared to installed version, to see if download is different.
# It does not check for newer or not, only different.
#
# - expectedTeamID: (required)
# 10-digit developer team ID.
# Obtain the team ID by running:
#
# - Applications (in dmgs or zips)
# spctl -a -vv /Applications/BBEdit.app
#
# - Pkgs
# spctl -a -vv -t install ~/Downloads/desktoppr-0.2.pkg
#
# The team ID is the ten-digit ID at the end of the line starting with 'origin='
#
# - archiveName: (optional)
# The name of the downloaded file.
# When not given the archiveName is derived from the $name.
#
# - appName: (optional)
# File name of the app bundle in the dmg to verify and copy (include .app).
# When not given, the appName is derived from the $name.
#
# - targetDir: (optional)
# dmg or zip:
# Applications will be copied to this directory.
# Default value is '/Applications' for dmg and zip installations.
# pkg:
# targetDir is used as the install-location. Default is '/'.
#
# - blockingProcesses: (optional)
# Array of process names that will block the installation or update.
# If no blockingProcesses array is given the default will be:
# blockingProcesses=( $name )
# When a package contains multiple applications, _all_ should be listed, e.g:
# blockingProcesses=( "Keynote" "Pages" "Numbers" )
# When a workflow has no blocking processes, use
# blockingProcesses=( NONE )
#
# - pkgName: (optional, only used for pkgInDmg, dmgInZip, and appInDmgInZip)
# File name of the pkg/dmg file _inside_ the dmg or zip
# When not given the pkgName is derived from the $name
#
# - updateTool:
# - updateToolArguments:
# When Installomator detects an existing installation of the application,
# and the updateTool variable is set
# $updateTool $updateArguments
# Will be run instead of of downloading and installing a complete new version.
# Use this when the updateTool does differential and optimized downloads.
# e.g. msupdate
#
# - updateToolRunAsCurrentUser:
# When this variable is set (any value), $updateTool will be run as the current user.
#
# MARK: Functions
##appversionfallback(){
## echo "Inside app version fallback"
# if [[ ! -n $appNewVersion ]]; then
##echo "Varcheck = $varcheck"
## if [ "$varcheck" -eq 1 ]; then
## echo "Storing app version"
## appversionplist="/Library/Application Support/JAMF/appversions/${name}/com.g2.appversion.plist"
## defaults write "$appversionplist" AppVersionNumberStored -string $appversion
## fi
##}
cleanupAndExit() { # $1 = exit code, $2 message
if [[ -n $2 && $1 -ne 0 ]]; then
printlog "ERROR: $2"
fi
if [ "$DEBUG" -eq 0 ]; then
# remove the temporary working directory when done
printlog "Deleting $tmpDir"
rm -Rf "$tmpDir"
fi
if [ -n "$dmgmount" ]; then
# unmount disk image
printlog "Unmounting $dmgmount"
hdiutil detach "$dmgmount"
fi
if [ -e "/Library/Application Support/G2/${name}" ]; then
/bin/rm -rf "/Library/Application Support/G2/${name}" &> /dev/null
fi
# appversionfallback
# If we closed any processes, reopen the app again
reopenClosedProcess
printlog "################## End Installomator, exit code $1 \n\n"
exit "$1"
}
deferraltimecalcfunc(){
# Look if app is open via process name
#appOpen="$(pgrep -ix "$appID" | wc -l)"
#path to counter file
counterpathplist="/Library/Application Support/G2/${name}/com.g2.osupdatedeferral.plist"
#path to lastDeferralTimeplist
lastDeferralTimeplist="/Library/Application Support/G2/${name}/lastDeferralTime.plist"
#for x in ${blockingProcesses}; do
# pgrep -xq "$x"
#appOpen="$(pgrep -xq "$x")"
#echo "appOpen: $appOpen"
#if [[ $appOpen -gt 0 ]]; then
#check if counter file exists. If it does, increment the count and store it
if [ -f "$counterpathplist" ] && [ -f "$lastDeferralTimeplist" ]; then
echo "Counter file found."
#Read from the clounter plist file
count=`defaults read "$counterpathplist" DeferralCount`
echo "Current/Old count is $count"
echo "A deferral file/time stamp is present."
#Read the last deferral from the lastDeferralTimeplist file
lastDeferralTimeStamp=`defaults read "$lastDeferralTimeplist" DeferralTimeStamp`
echo "Deferral Time Stamp is $lastDeferralTimeStamp"
else
echo "Counter file does not exist. Creating one now."
#Set the deferral count in the plist to 0 and set the counter also to 0
defaults write "$counterpathplist" DeferralCount -int 0
count=0
echo "Count is $count"
echo "A deferral file/time stamp does not exist. Creating one now."
defaults write "$lastDeferralTimeplist" DeferralTimeStamp -int 0
lastDeferralTimeStamp=0
fi
## This can be replaced with a script parameter to make it more flexible
hoursToDefer="4"
# Read the previous DeferralTimeStamp from the stored value, the first time it will be 0 and subsequent times it will be obtained from previous calculations
echo "Last Deferral is $lastDeferralTimeStamp"
lastDeferralTimeStamp=`defaults read "$lastDeferralTimeplist" DeferralTimeStamp`
# echo "Old Time Stamp is $lastDeferralTimeStamp"
#Calculate the hours to defer in seconds or epoch time
hoursToDeferSecs=$((60*60*hoursToDefer))
#Obtain the current time in epoch
currentTime=$(date +"%s")
#Get the timeSinceDeferral, first instance it will be the time since epoch and subsequently it will be the time between the runs
timeSinceDeferral="$((currentTime-lastDeferralTimeStamp))"
# Check if on the previous run the deferral time stamp is not equal to zero then we will calculate the time for "Troubleshooting purposes"
if [[ "$lastDeferralTimeStamp" -ne "0" ]]; then
#Calculating the number of hours,minutes and seconds to be used only for troubleshooting
numberofhours=$(( timeSinceDeferral/3600 ))
remainder=$(( timeSinceDeferral%3600 ))
numberofminutes=$(( remainder/60 ))
numberofseconds=$((timeSinceDeferral%60))
echo "Max Hour Limit in epoch seconds $hoursToDeferSecs
Current Time in epoch seconds $currentTime
Last Deferred Time in epoch seconds $lastDeferralTimeStamp
The difference in HRS: $numberofhours in MINS $numberofminutes and SECS $numberofseconds"
fi
#fi
}
runAsUser() {
if [[ $currentUser != "loginwindow" ]]; then
uid=$(id -u "$currentUser")
launchctl asuser $uid sudo -u $currentUser "$@"
fi
}
reloadAsUser() {
if [[ $currentUser != "loginwindow" ]]; then
uid=$(id -u "$currentUser")
su - $currentUser -c "${@}"
fi
}
displaydialog() { # $1: message $2: title
message=${1:-"Message"}
title=${2:-"Installomator"}
runAsUser osascript -e "button returned of (display dialog \"$message\" with title \"$title\" buttons {\"Not Now\", \"Quit and Update\"} default button \"Quit and Update\" with icon POSIX file \"$LOGO\")"
}
displaydialogContinue() { # $1: message $2: title
message=${1:-"Message"}
title=${2:-"Installomator"}
runAsUser osascript -e "button returned of (display dialog \"$message\" with title \"$title\" buttons {\"Quit and Update\"} default button \"Quit and Update\" with icon POSIX file \"$LOGO\")"
}
displaynotification() { # $1: message $2: title
message=${1:-"Message"}
title=${2:-"Notification"}
manageaction="/Library/Application Support/JAMF/bin/Management Action.app/Contents/MacOS/Management Action"
if [[ -x "$manageaction" ]]; then
"$manageaction" -message "$message" -title "$title"
else
runAsUser osascript -e "display notification \"$message\" with title \"$title\""
fi
}
# MARK: Logging
log_location="/private/var/log/Installomator.log"
printlog(){
timestamp=$(date +%F\ %T)
if [[ "$(whoami)" == "root" ]]; then
echo "$timestamp" "$label" "$1" | tee -a $log_location
else
echo "$timestamp" "$label" "$1"
fi
}
# will get the latest release download from a github repo
downloadURLFromGit() { # $1 git user name, $2 git repo name
gitusername=${1?:"no git user name"}
gitreponame=${2?:"no git repo name"}
if [[ $type == "pkgInDmg" ]]; then
filetype="dmg"
elif [[ $type == "pkgInZip" ]]; then
filetype="zip"
else
filetype=$type
fi
if [ -n "$archiveName" ]; then
downloadURL=$(curl --silent --fail "https://api.github.com/repos/$gitusername/$gitreponame/releases/latest" \
| awk -F '"' "/browser_download_url/ && /$archiveName\"/ { print \$4; exit }")
else
downloadURL=$(curl --silent --fail "https://api.github.com/repos/$gitusername/$gitreponame/releases/latest" \
| awk -F '"' "/browser_download_url/ && /$filetype\"/ { print \$4; exit }")
fi
if [ -z "$downloadURL" ]; then
cleanupAndExit 9 "could not retrieve download URL for $gitusername/$gitreponame"
#exit 9
else
echo "$downloadURL"
return 0
fi
}
versionFromGit() {
# credit: Søren Theilgaard (@theilgaard)
# $1 git user name, $2 git repo name
gitusername=${1?:"no git user name"}
gitreponame=${2?:"no git repo name"}
appNewVersion=$(curl --silent --fail "https://api.github.com/repos/$gitusername/$gitreponame/releases/latest" | grep tag_name | cut -d '"' -f 4 | sed 's/[^0-9\.]//g')
if [ -z "$appNewVersion" ]; then
printlog "could not retrieve version number for $gitusername/$gitreponame"
appNewVersion=""
else
echo "$appNewVersion"
return 0
fi
}
# Handling of differences in xpath between Catalina and Big Sur
xpath() {
# the xpath tool changes in Big Sur and now requires the `-e` option
if [[ $(sw_vers -buildVersion) > "20A" ]]; then
/usr/bin/xpath -e $@
# alternative: switch to xmllint (which is not perl)
#xmllint --xpath $@ -
else
/usr/bin/xpath $@
fi
}
getAppVersion() {
# modified by: Søren Theilgaard (@theilgaard)
# pkgs contains a version number, then we don't have to search for an app
if [[ $packageID != "" ]]; then
appversion="$(pkgutil --pkg-info-plist ${packageID} 2>/dev/null | grep -A 1 pkg-version | tail -1 | sed -E 's/.*>([0-9.]*)<.*/\1/g')"
if [[ $appversion != "" ]]; then
printlog "found packageID $packageID installed, version $appversion"
return
else
printlog "No version found using packageID $packageID"
fi
fi
# get all apps matching name
applist=$(mdfind "kind:application $appName" -0 )
if [[ $applist = "" ]]; then
printlog "Spotlight not returning any app, trying manually in /Applications."
if [[ -d "/Applications/$appName" ]]; then
applist="/Applications/$appName"
fi
fi
appPathArray=( ${(0)applist} )
if [[ ${#appPathArray} -gt 0 ]]; then
filteredAppPaths=( ${(M)appPathArray:#${targetDir}*} )
if [[ ${#filteredAppPaths} -eq 1 ]]; then
installedAppPath=$filteredAppPaths[1]
#appversion=$(mdls -name kMDItemVersion -raw $installedAppPath )
appversion=$(defaults read $installedAppPath/Contents/Info.plist CFBundleShortVersionString) #Not dependant on Spotlight indexing
printlog "found app at $installedAppPath, version $appversion"
else
printlog "could not determine location of $appName"
fi
else
printlog "could not find $appName"
fi
}
checkRunningProcesses() {
# don't check in DEBUG mode
if [[ $DEBUG -ne 0 ]]; then
printlog "DEBUG mode, not checking for blocking processes"
return
fi
#Total Allowed deferrals
totdefer=2
deferraltimecalcfunc
#saveQuitMSG="The Application must be quit in order to update.
#Please wait until the application reopens"
saveQuitMSG="$name has a required update available and must be quit to update. You have two four-hour deferrals before being forced to proceed with the update. If you choose to Quit please wait for $name to reopen."
qd="Quit & Update"
jamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
if [[ "$timeSinceDeferral" -ge "$hoursToDeferSecs" ]]; then
echo "Deferral prompting: Time limit reached"
#if ([ "$count" -le "$totdefer" ] && [ "$timeSinceDeferral" -ge "$hoursToDeferSecs" ]); then
#echo "Deferral prompting: Count less than total Defer and time limit reached"
elif ([ "$BLOCKING_PROCESS_ACTION" = "prompt_user_loop" ] && [ "$timeSinceDeferral" -ge "$hoursToDeferSecs" ]); then
echo "Deferral Prompting: Prompt user Loop"
else
echo "exiting"
exit 0
fi
echo "Blocking Processes : ${blockingProcesses}"
# try at most 3 times
for i in {1..4}; do
countedProcesses=0
for x in ${blockingProcesses}; do
if pgrep -xq "$x"; then
printlog "found blocking process $x"
appClosed=1
case $BLOCKING_PROCESS_ACTION in
kill)
printlog "killing process $x"
pkill $x
if [[ $? != 0 ]]; then kill -9 $(pgrep "$x"); fi
sleep 5
;;
prompt_user|prompt_user_then_kill)
# if ([ "$count" -le "$totdefer" ] && [ "$timeSinceDeferral" -ge "$hoursToDeferSecs" ]); then
if [[ "$timeSinceDeferral" -ge "$hoursToDeferSecs" ]]; then
if [[ "$count" -ge "totdefer" ]]; then
#displaydialog "No more deferrals left Quit “$x” to continue updating" "The application “$x” needs to be updated."
if [[ $jamfvar == "/" ]]; then
printlog "Using Jamf Pro MDM"
userdefercount="$(($totdefer-$count))"
button="$( "$jamfHelper" -windowType hud -lockHUD -title "$name" -heading "" -description "$saveQuitMSG" -button1 "Quit & Update" -button2 "No Deferrals Left" -alignDescription center -alignHeading center -icon "$LOGO" -iconSize "20x20" -windowPosition lr -timeout 3600)"
else
osascript -e "display dialog \"No more deferrals left Quit "$x" to continue updating\" with title \"The application “$x” needs to be updated.\" buttons {\"Quit and Update\"} default button \"Quit and Update\" with icon POSIX file \"$LOGO\""
fi
echo "Time since last deferral is equal or greater than ${hoursToDefer} hours"
echo "Deferral count is at or above max deferrals allowed...."
printlog "telling app $x to quit"
runAsUser osascript -e "tell app \"$x\" to quit"
if [[ $? != 0 ]]; then kill -9 $(pgrep "$x"); fi
if [ $x = "Microsoft Teams Helper" ] || [ $x = "Teams" ]; then
pkill $x
if [[ $? != 0 ]]; then kill -9 $(pgrep "$x"); fi
fi
#if the app is open then loop again
# appOpen= pgrep -xq "$blockingProcesses"
# if [[ $appOpen -gt 0 ]]; then
# give the user a bit of time to quit apps
printlog "waiting 10 seconds for processes to quit"
sleep 10
# else
count=0
#if the app is closed
# fi
else
echo "Not equal to max deferrals"
echo "jamfvar : $jamfvar"
if [[ $jamfvar == "/" ]]; then
printlog "Using Jamf Pro MDM"
userdefercount="$(($totdefer-$count))"
button="$( "$jamfHelper" -windowType hud -lockHUD -title "$name" -heading "" -description "$saveQuitMSG" -button1 "Defer ($userdefercount)" -button2 "Quit & Update ($qd)" -alignDescription center -alignHeading center -icon "$LOGO" -iconSize "20x20" -windowPosition lr -timeout 3600)"
else
button="$(displaydialog "Quit “$x” to continue updating? (Leave this dialogue if you want to activate this update later)." "The application “$x” needs to be updated.")"
fi
echo "button value =$button"
if [ $button = "Not Now" ] || [ $button = "0" ]; then
defaults write "$lastDeferralTimeplist" DeferralTimeStamp -int $timeSinceDeferral
echo "User chose to defer"
count=`defaults read "$counterpathplist" DeferralCount`
echo "Old count is $count"
((count++))
echo "New count is $count"
defaults write "$counterpathplist" DeferralCount -int $count
echo "don't upgrade"
if [[ "$count" -gt "1" ]]; then
lastDeferralTimeStamp=0
defaults write "$lastDeferralTimeplist" DeferralTimeStamp -int 0
currentTime=$(date +"%s")
timeSinceDeferral="$((currentTime-lastDeferralTimeStamp))"
defaults write "$lastDeferralTimeplist" DeferralTimeStamp -int $timeSinceDeferral
fi
exit 0
# cleanupAndExit 10 "user aborted update"
else
if [[ $i > 2 && $BLOCKING_PROCESS_ACTION = "prompt_user_then_kill" ]]; then
printlog "Changing BLOCKING_PROCESS_ACTION to kill"
BLOCKING_PROCESS_ACTION=kill
count=0
else
printlog "telling app $x to quit"
runAsUser osascript -e "tell app \"$x\" to quit"
if [[ $? != 0 ]]; then kill -9 $(pgrep "$x"); fi
if [ $x = "Microsoft Teams Helper" ] || [ $x = "Teams" ]; then
pkill $x
if [[ $? != 0 ]]; then kill -9 $(pgrep "$x"); fi
fi
#if the app is open then loop again
# appOpen= pgrep -xq "$blockingProcesses"
# echo "appOpen value: $appOpen"
# if [[ $appOpen -gt 0 ]]; then
# give the user a bit of time to quit apps
printlog "waiting 10 seconds for processes to quit"
sleep 10
# else
count=0
#if the app is closed
# fi
fi
fi
fi
fi
;;
prompt_user_loop)
if ([ "$timeSinceDeferral" -ge "$hoursToDeferSecs" ]); then
if [[ $jamfvar == "/" ]]; then
printlog "Using Jamf Pro MDM"
userdefercount="$(($totdefer-$count))"
button="$( "$jamfHelper" -windowType hud -lockHUD -title "$name" -heading "" -description "$saveQuitMSG" -button1 "Defer ($userdefercount)" -button2 "Quit & Update ($qd)" -alignDescription center -alignHeading center -icon "$LOGO" -iconSize "20x20" -windowPosition lr -timeout 3600)"
else
button=$(displaydialog "Quit “$x” to continue updating? (Click “Not Now” to be asked in 1 hour, or leave this open until you are ready)." "The application “$x” needs to be updated.")
fi
if [ $button = "Not Now" ] || [ $button = "0" ]; then
defaults write "$lastDeferralTimeplist" DeferralTimeStamp -int $timeSinceDeferral
echo "User chose to defer"
count=`defaults read "$counterpathplist" DeferralCount`
((count++))
defaults write "$counterpathplist" DeferralCount -int $count
echo "don't upgrade"
if [[ $i < 2 ]]; then
printlog "user wants to wait"
lastDeferralTimeStamp=0
defaults write "$lastDeferralTimeplist" DeferralTimeStamp -int 0
currentTime=$(date +"%s")
timeSinceDeferral="$((currentTime-lastDeferralTimeStamp))"
defaults write "$lastDeferralTimeplist" DeferralTimeStamp -int $timeSinceDeferral
exit 0
# sleep 3600 # 3600 seconds is an hour
else
printlog "change of BLOCKING_PROCESS_ACTION to tell_user"
BLOCKING_PROCESS_ACTION=tell_user
fi
else
printlog "telling app $x to quit"
runAsUser osascript -e "tell app \"$x\" to quit"
if [[ $? != 0 ]]; then kill -9 $(pgrep "$x"); fi
# give the user a bit of time to quit apps
printlog "waiting 10 seconds for processes to quit"
sleep 10
fi
fi
;;
tell_user|tell_user_then_kill)
button=$(displaydialogContinue "Quit “$x” to continue updating? (This is an important update). Wait for notification of update before launching app again." "The application “$x” needs to be updated.")
printlog "telling app $x to quit"
runAsUser osascript -e "tell app \"$x\" to quit"
if [[ $? != 0 ]]; then kill -9 $(pgrep "$x"); fi
# give the user a bit of time to quit apps
printlog "waiting 10 seconds for processes to quit"
sleep 10
if [[ $i > 1 && $BLOCKING_PROCESS_ACTION = tell_user_then_kill ]]; then
printlog "Changing BLOCKING_PROCESS_ACTION to kill"
BLOCKING_PROCESS_ACTION=kill
fi
;;
silent_fail)
cleanupAndExit 12 "blocking process '$x' found, aborting"
;;
esac
countedProcesses=$((countedProcesses + 1))
fi
done
done
if [[ $countedProcesses -ne 0 ]]; then
cleanupAndExit 11 "could not quit all processes, aborting..."
fi
printlog "no more blocking processes, continue with update"
}
reopenClosedProcess() {
# If Installomator closed any processes, let's get the app opened again
# credit: Søren Theilgaard (@theilgaard)
# don't reopen if REOPEN is not "yes"
if [[ $REOPEN != yes ]]; then
printlog "REOPEN=no, not reopening anything"
return
fi
# don't reopen in DEBUG mode
if [[ $DEBUG -ne 0 ]]; then
printlog "DEBUG mode, not reopening anything"
return
fi
if [[ $appClosed == 1 ]]; then
printlog "Telling app $appName to open"
#runAsUser osascript -e "tell app \"$appName\" to open"
#runAsUser open -a "${appName}"
reloadAsUser "open -a \"${appName}\""
#reloadAsUser "open \"${(0)applist}\""
processuser=$(ps aux | grep -i "${appName}" | grep -vi "grep" | awk '{print $1}')
printlog "Reopened ${appName} as $processuser"
else
printlog "App not closed, so no reopen."
fi
}
installAppWithPath() { # $1: path to app to install in $targetDir
# modified by: Søren Theilgaard (@theilgaard)
appPath=${1?:"no path to app"}
# check if app exists
if [ ! -e "$appPath" ]; then
cleanupAndExit 8 "could not find: $appPath"
fi
# verify with spctl
printlog "Verifying: $appPath"
if ! teamID=$(spctl -a -vv "$appPath" 2>&1 | awk '/origin=/ {print $NF }' | tr -d '()' ); then
cleanupAndExit 4 "Error verifying $appPath"
fi
printlog "Team ID matching: $teamID (expected: $expectedTeamID )"
if [ "$expectedTeamID" != "$teamID" ]; then
cleanupAndExit 5 "Team IDs do not match"
fi
## if [[ ! -n $appNewVersion ]]; then
## echo "App New Version not specified"
#To check if AppNewVersion variable exists and store it before it is changed. (Important for cases where app version is the same)
## varcheck=1
## fi
# versioncheck
# credit: Søren Theilgaard (@theilgaard)
appNewVersion=$(defaults read $appPath/Contents/Info.plist CFBundleShortVersionString)
if [[ $appversion == $appNewVersion ]]; then
printlog "Downloaded version of $name is $appNewVersion, same as installed."
##if [ "$varcheck" -eq 1 ]; then
##echo "App New version does not exist so storing the same version as installed"
##appversionplist="/Library/Application Support/JAMF/appversions/${name}/com.g2.appversion.plist"
##defaults write "$appversionplist" AppVersionNumberStored -string $appversion
##fi
# cleanupAndExit
if [[ $INSTALL != "force" ]]; then
message="$name, version $appNewVersion, is the latest version."
if [[ $currentUser != "loginwindow" && $NOTIFY == "all" ]]; then
printlog "notifying"
displaynotification "$message" "No update for $name!"
fi
cleanupAndExit 0 "No new version to install"
else
printlog "Using force to install anyway."
fi
else
printlog "Downloaded version of $name is $appNewVersion (replacing version $appversion)."
fi
# skip install for DEBUG
if [ "$DEBUG" -ne 0 ]; then
printlog "DEBUG enabled, skipping remove, copy and chown steps"
return 0
fi
# check for root
if [ "$(whoami)" != "root" ]; then
# not running as root
cleanupAndExit 6 "not running as root, exiting"
fi
# remove existing application
if [ -e "$targetDir/$appName" ]; then
printlog "Removing existing $targetDir/$appName"
rm -Rf "$targetDir/$appName"
fi
# copy app to /Applications
printlog "Copy $appPath to $targetDir"
if ! ditto "$appPath" "$targetDir/$appName"; then
cleanupAndExit 7 "Error while copying"
fi
# set ownership to current user
if [ "$currentUser" != "loginwindow" ]; then
printlog "Changing owner to $currentUser"
chown -R "$currentUser" "$targetDir/$appName"
else
printlog "No user logged in, not changing user"
fi
}
mountDMG() {
# mount the dmg
printlog "Mounting $tmpDir/$archiveName"
# tempfol=`basename "dmg"`
# appdir=`basename "${name}"`
# mnt=`mktemp -d "/private/tmp/$appdir/${tempfol}.XXXXXX"`
#Put the above variables in a better place and put in cleanup
# always pipe 'Y\n' in case the dmg requires an agreement
if ! dmgmount=$(echo 'Y'$'\n' | hdiutil attach "$tmpDir/$archiveName" -nobrowse -readonly | tail -n 1 | cut -c 54- ); then
## if ! dmgmount=$(echo 'Y'$'\n' | hdiutil attach "$tmpDir/$archiveName" -nobrowse -readonly -mountpoint ${mnt}| tail -n 1 | cut -c 54- ); then
# /usr/bin/hdiutil attach "${dmg}" -quiet -nobrowse -mountpoint ${mnt} &> /dev/null || error 'failed to mount dmg.'
cleanupAndExit 3 "Error mounting $tmpDir/$archiveName"
fi
if [[ ! -e $dmgmount ]]; then
printlog "Error mounting $tmpDir/$archiveName"
cleanupAndExit 3
fi
printlog "Mounted: $dmgmount"
}
installFromDMG() {
mountDMG
installAppWithPath "$dmgmount/$appName"
}
installFromPKG() {
# verify with spctl
printlog "Verifying: $archiveName"
if ! spctlout=$(spctl -a -vv -t install "$archiveName" 2>&1 ); then
printlog "Error verifying $archiveName"
cleanupAndExit 4
fi
teamID=$(echo $spctlout | awk -F '(' '/origin=/ {print $2 }' | tr -d '()' )
# Apple signed software has no teamID, grab entire origin instead
if [[ -z $teamID ]]; then
teamID=$(echo $spctlout | awk -F '=' '/origin=/ {print $NF }')
fi
printlog "Team ID: $teamID (expected: $expectedTeamID )"
if [ "$expectedTeamID" != "$teamID" ]; then
printlog "Team IDs do not match!"
cleanupAndExit 5
fi
##if [[ ! -n $appNewVersion ]]; then
##echo "App New Version not specified"
#To check if AppNewVersion variable exists and store it before it is changed. (Important for cases where app version is the same)
##varcheck=1
##fi
# Check version of pkg to be installed if packageID is set
if [[ $packageID != "" && $appversion != "" ]]; then
printlog "Checking package version."
pkgutil --expand "$archiveName" "$archiveName"_pkg
#printlog "$(cat "$archiveName"_pkg/Distribution | xpath '//installer-gui-script/pkg-ref[@id][@version]' 2>/dev/null)"
appNewVersion=$(cat "${archiveName}"_pkg/Distribution | xpath '//installer-gui-script/pkg-ref[@id][@version]' 2>/dev/null | grep -i "$packageID" | tr ' ' '\n' | grep -i version | cut -d \" -f 2) #sed -E 's/.*\"([0-9.]*)\".*/\1/g'
rm -r "${archiveName}"_pkg
printlog "Downloaded package $packageID version $appNewVersion"
if [[ $appversion == $appNewVersion ]]; then
printlog "Downloaded version of $name is the same as installed."
##if [ "$varcheck" -eq 1 ]; then
##echo "App New version does not exist so storing the same version as installed"
##appversionplist="/Library/Application Support/JAMF/appversions/${name}/com.g2.appversion.plist"
##defaults write "$appversionplist" AppVersionNumberStored -string $appversion
##fi
if [[ $INSTALL != "force" ]]; then
message="$name, version $appNewVersion, is the latest version."
if [[ $currentUser != "loginwindow" && $NOTIFY == "all" ]]; then
printlog "notifying"
displaynotification "$message" "No update for $name!"
fi
cleanupAndExit 0 "No new version to install"
else
printlog "Using force to install anyway."
fi
fi
fi
# skip install for DEBUG
if [ "$DEBUG" -ne 0 ]; then
printlog "DEBUG enabled, skipping installation"
return 0
fi
# check for root
if [ "$(whoami)" != "root" ]; then
# not running as root
cleanupAndExit 6 "not running as root, exiting"
fi
# install pkg
printlog "Installing $archiveName to $targetDir"
if ! installer -pkg "$archiveName" -tgt "$targetDir" ; then
printlog "error installing $archiveName"
cleanupAndExit 9
fi
}
installFromZIP() {
# unzip the archive
printlog "Unzipping $archiveName"
# tar -xf "$archiveName"
# note: when you expand a zip using tar in Mojave the expanded
# app will never pass the spctl check
# unzip -o -qq "$archiveName"
# note: githubdesktop fails spctl verification when expanded
# with unzip
ditto -x -k "$archiveName" "$tmpDir"
installAppWithPath "$tmpDir/$appName"
}
installFromTBZ() {
# unzip the archive
printlog "Unzipping $archiveName"
tar -xf "$archiveName"
installAppWithPath "$tmpDir/$appName"
}
installPkgInDmg() {
mountDMG
# locate pkg in dmg
if [[ -z $pkgName ]]; then
# find first file ending with 'pkg'
findfiles=$(find "$dmgmount" -iname "*.pkg" -maxdepth 1 )
filearray=( ${(f)findfiles} )
if [[ ${#filearray} -eq 0 ]]; then
cleanupAndExit 20 "couldn't find pkg in dmg $archiveName"
fi
archiveName="${filearray[1]}"
printlog "found pkg: $archiveName"
else
# it is now safe to overwrite archiveName for installFromPKG
archiveName="$dmgmount/$pkgName"
fi
# installFromPkgs
installFromPKG
}
installPkgInZip() {
# unzip the archive
printlog "Unzipping $archiveName"
tar -xf "$archiveName"
# locate pkg in zip
if [[ -z $pkgName ]]; then
# find first file ending with 'pkg'
findfiles=$(find "$tmpDir" -iname "*.pkg" -maxdepth 2 )
filearray=( ${(f)findfiles} )
if [[ ${#filearray} -eq 0 ]]; then
cleanupAndExit 20 "couldn't find pkg in zip $archiveName"
fi
archiveName="${filearray[1]}"
# it is now safe to overwrite archiveName for installFromPKG
printlog "found pkg: $archiveName"
else
# it is now safe to overwrite archiveName for installFromPKG
archiveName="$tmpDir/$pkgName"
fi
# installFromPkgs
installFromPKG
}
installAppInDmgInZip() {
# unzip the archive
printlog "Unzipping $archiveName"
tar -xf "$archiveName"
# locate dmg in zip
if [[ -z $pkgName ]]; then
# find first file ending with 'dmg'
findfiles=$(find "$tmpDir" -iname "*.dmg" -maxdepth 2 )
filearray=( ${(f)findfiles} )
if [[ ${#filearray} -eq 0 ]]; then
cleanupAndExit 20 "couldn't find dmg in zip $archiveName"
fi
archiveName="$(basename ${filearray[1]})"
# it is now safe to overwrite archiveName for installFromDMG
printlog "found dmg: $tmpDir/$archiveName"
else
# it is now safe to overwrite archiveName for installFromDMG
archiveName="$pkgName"
fi
# installFromDMG, DMG expected to include an app (will not work with pkg)
installFromDMG
}
runUpdateTool() {
printlog "Function called: runUpdateTool"
if [[ -x $updateTool ]]; then
printlog "running $updateTool $updateToolArguments"
if [[ -n $updateToolRunAsCurrentUser ]]; then
runAsUser $updateTool ${updateToolArguments}
else
$updateTool ${updateToolArguments}
fi
if [[ $? -ne 0 ]]; then
cleanupAndExit 15 "Error running $updateTool"
fi
else
printlog "couldn't find $updateTool, continuing normally"
return 1
fi
return 0
}
finishing() {
printlog "Finishing…"
sleep 10 # wait a moment to let spotlight catch up
getAppVersion
updatedMSG="$name has been updated. Thank you."
if [[ -z $appversion ]]; then
message="Installed $name"
else
message="Installed $name, version $appversion"
fi
printlog "$message"
if [[ $currentUser != "loginwindow" && ( $NOTIFY == "success" || $NOTIFY == "all" ) ]]; then
printlog "notifying"
if [[ $appClosed == 1 ]]; then
if [[ $jamfvar == "/" ]]; then
printlog "Using Jamf Pro MDM"
button="$( "$jamfHelper" -windowType hud -lockHUD -title "$name" -heading "" -description "$updatedMSG" -button2 "Ok" -alignDescription center -alignHeading center -icon "$LOGO" -iconSize "20x20" -windowPosition lr -timeout 3600)"
else
displaynotification "$message" "$name update/installation complete!"
fi
else
if [[ ( $NOTIFY == "success" || $NOTIFY == "all" ) ]]; then