-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathinstall_bigfix.sh
More file actions
executable file
·677 lines (626 loc) · 30.3 KB
/
Copy pathinstall_bigfix.sh
File metadata and controls
executable file
·677 lines (626 loc) · 30.3 KB
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
#!/usr/bin/env bash
# Short link: https://bit.ly/installbigfix
#
# kickstart bigfix install
# tested as working with the following: Mac OS X, Debian, Ubuntu, RHEL, CentOS, Fedora, OracleEL, SUSE
#
# Supported architectures:
# - x86_64 / amd64 (BigFix 11)
# - i386 / i686 (falls back to BigFix 9.5, the last release with 32-bit x86 builds)
# - armhf (Raspberry Pi OS / Raspbian, .deb only)
# - ppc64le (Ubuntu .deb only in this script; RPM ppc64le/s390x not yet wired up)
#
# Reference: https://support.bigfix.com/bes/install/besclients-nonwindows.html
# Related: https://github.com/bigfix/bfdocker/tree/master/besclient
#
# Usage:
# curl -O https://raw.githubusercontent.com/jgstew/tools/master/bash/install_bigfix.sh
# bash install_bigfix.sh __ROOT_OR_RELAY_FQDN__
#
# Single Line:
# curl -O https://raw.githubusercontent.com/jgstew/tools/master/bash/install_bigfix.sh ; bash install_bigfix.sh __ROOT_OR_RELAY_FQDN__
# TODO: use the masthead file in current directory if present
# Ensure we are actually running under bash, not sh/dash/ash.
# $BASH_VERSION is unset in any non-bash shell.
# This guard must stay pure POSIX (no [[ ]], no &>) so sh can parse it,
# then it transparently re-executes the script under bash if available.
# see the issue this solves: https://github.com/jgstew/tools/issues/20
# https://unix.stackexchange.com/questions/71121/determine-shell-in-script-during-runtime
if [ -z "$BASH_VERSION" ]; then
if command -v bash >/dev/null 2>&1; then
exec bash "$0" "$@"
fi
echo "ERROR: this script requires bash (it uses [[ ]], &>, and OSTYPE)." >&2
echo " Re-run as: bash $0 $*" >&2
exit 1
fi
# https://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script
# https://www.tldp.org/LDP/abs/html/functions.html
# FUNCTION: check if command exists
command_exists () {
type "$1" &> /dev/null ;
}
# FUNCTION: is the BESClient daemon currently running?
# Uses pgrep when available, falls back to ps. The [B] in the ps pattern
# keeps grep from matching its own command line. Returns 1 (not running)
# when neither tool is present, so callers treat "unknown" as "not started".
besclient_running () {
if command_exists pgrep ; then
pgrep -x BESClient > /dev/null 2>&1
elif command_exists ps ; then
ps -e 2>/dev/null | grep -q '[B]ESClient'
else
return 1
fi
}
# resolve the directory this script lives in, regardless of CWD
# (degrades to CWD when piped, e.g. `curl ... | bash`, where $0 is not a file)
SCRIPTDIR="$(cd "$(dirname "$0")" && pwd)"
# All downloads and generated files are staged in STAGINGDIR.
# Defaults to the script's own directory; falls back to /tmp when that
# is not writable (e.g. script mounted read-only in a container).
# This is separate from INSTALLDIR, which is where the BES installer
# expects to read the masthead from (OS specific, set further below).
# see the issue this solves: https://github.com/jgstew/tools/issues/16
STAGINGDIR="$SCRIPTDIR"
if [ ! -w "$STAGINGDIR" ]; then
echo "NOTE: $STAGINGDIR is not writable, staging in /tmp instead"
STAGINGDIR="/tmp"
fi
STAGED_CFG="$STAGINGDIR/clientsettings.cfg"
# if $1 exists, then set MASTHEADURL
# https://www.tldp.org/LDP/abs/html/comparison-ops.html
if [ -n "$1" ]; then
MASTHEADURL="https://$1:52311/masthead/masthead.afxm"
RELAYFQDN="$1:52311"
# if parameter contains colon:
if [[ "$1" == *":"* ]]; then
MASTHEADURL="https://$1/masthead/masthead.afxm"
RELAYFQDN=$1
fi
else
# TODO: allow a masthead to be provided in the CWD instead.
echo Must provide FQDN of Root or Relay
exit 1
fi
if [ -n "$2" ]; then
RELAYPASS="$2"
fi
# URLVERSION determines which version of the BigFix agent is downloaded and is
# typically set to the latest release (URLMAJORMINOR is derived from it below).
# most recent version# found here under `Agent`: https://support.bigfix.com/bes/release/
URLVERSION=11.0.6.137
# Pick a modern awk. Solaris /usr/bin/awk is the ancient oawk: it has no
# gsub() and misparses the /=/ regex as the /= operator (breaks the
# besclient.config generator below). nawk and /usr/xpg4/bin/awk are POSIX.
# On Linux awk is already gawk/mawk and nawk is normally absent, so this
# resolves back to plain awk there - a no-op.
AWK=awk
if command_exists nawk ; then
AWK=nawk
elif [ -x /usr/xpg4/bin/awk ] ; then
AWK=/usr/xpg4/bin/awk
fi
# check for x32bit or x64bit OS
MACHINETYPE=`uname -m`
# set OSBIT based on MACHINETYPE. Only x86-family CPUs get OSBIT=x32 (name lacks "64" but contains "86").
# Anything else (x86_64, aarch64, ppc64le, s390x, armv7l, ...) gets OSBIT=x64 and is disambiguated below.
# Solaris x86 reports "i86pc" from `uname -m` for 64-bit hardware too (it is a
# platform token, not a bitness), so it is excluded here - BigFix 11 ships a
# 64-bit Solaris pkg and Solaris 11 is 64-bit only.
if [[ $MACHINETYPE != *"64"* ]] && [[ $MACHINETYPE == *"86"* ]] && [[ $MACHINETYPE != "i86pc" ]]; then
OSBIT=x32
URLVERSION=9.5.25.11
else
OSBIT=x64
# NOTE: non-x86 64-bit CPUs (ppc64le, aarch64, s390x) also land here; the deb
# path corrects URLBITS for ppc64le/arm below, but the rpm path does not yet
# (see the TODO in the rpm branch), so it would wrongly download x86_64 there.
fi
URLMAJORMINOR=`echo $URLVERSION | $AWK -F. '{print $1 $2}'`
############################################################
# TODO: add more linux cases, not all are handled
# if clientsettings.cfg exists in CWD (or next to this script) copy it to
# staging. CWD wins if both exist. Guard against copying a file onto itself
# when the cfg already sits in the staging directory.
if [ -f clientsettings.cfg ] && [ ! -f "$STAGED_CFG" ] && [ "$(pwd)/clientsettings.cfg" != "$STAGED_CFG" ] ; then
cp clientsettings.cfg "$STAGED_CFG"
fi
if [ -f "$SCRIPTDIR/clientsettings.cfg" ] && [ ! -f "$STAGED_CFG" ] && [ "$SCRIPTDIR/clientsettings.cfg" != "$STAGED_CFG" ] ; then
cp "$SCRIPTDIR/clientsettings.cfg" "$STAGED_CFG"
fi
if [ ! -f "$STAGED_CFG" ] ; then
# create clientsettings.cfg file
echo -n > "$STAGED_CFG"
>> "$STAGED_CFG" echo _BESClient_RelaySelect_FailoverRelay=https://$RELAYFQDN/bfmirror/downloads/
>> "$STAGED_CFG" echo __RelaySelect_Automatic=1
>> "$STAGED_CFG" echo _BESClient_Resource_StartupNormalSpeed=1
>> "$STAGED_CFG" echo _BESClient_Download_RetryMinutes=1
>> "$STAGED_CFG" echo _BESClient_Download_CheckAvailabilitySeconds=120
>> "$STAGED_CFG" echo _BESClient_Resource_WorkIdle=20
>> "$STAGED_CFG" echo _BESClient_Resource_SleepIdle=500
>> "$STAGED_CFG" echo _BESClient_Resource_PowerSaveEnable=1
>> "$STAGED_CFG" echo _BESClient_Query_SleepTime=500
>> "$STAGED_CFG" echo _BESClient_Query_WorkTime=250
>> "$STAGED_CFG" echo _BESClient_Query_NMOMaxQueryTime=30
>> "$STAGED_CFG" echo _BESClient_Resource_AccelerateForPendingMessage=1
>> "$STAGED_CFG" echo _BESClient_Comm_CommandPollEnable=1
>> "$STAGED_CFG" echo _BESClient_Comm_CommandPollIntervalSeconds=1800
>> "$STAGED_CFG" echo _BESClient_Log_Days=30
>> "$STAGED_CFG" echo _BESClient_Log_MaxSize=1536000
>> "$STAGED_CFG" echo _BESClient_Download_UtilitiesCacheLimitMB=500
>> "$STAGED_CFG" echo _BESClient_Download_DownloadsCacheLimitMB=5000
>> "$STAGED_CFG" echo _BESClient_Download_MinimumDiskFreeMB=2000
>> "$STAGED_CFG" echo _BESClient_ActionManager_HistoryKeepDays=1825
>> "$STAGED_CFG" echo _BESClient_ActionManager_HistoryDisplayDaysTech=90
>> "$STAGED_CFG" echo _BESClient_ActionManager_CompletionDialogTimeoutSeconds=30
>> "$STAGED_CFG" echo _BESClient_PersistentConnection_Enabled=1
>> "$STAGED_CFG" echo _BESClient_ActionManager_OverrideTimeoutSeconds=21600
# Best-effort record of who initiated the install.
# $SUDO_USER only exists when launched through sudo, so fall back through
# progressively less specific sources. who/logname need a tty/utmp entry,
# so in docker containers / non-interactive root this resolves to "root"
# via id -un, which is the truthful answer there.
# see the issue this solves: https://github.com/jgstew/tools/issues/17
INSTALL_USER="$SUDO_USER"
[ -z "$INSTALL_USER" ] && INSTALL_USER=`who am i 2>/dev/null | awk '{print $1}'`
[ -z "$INSTALL_USER" ] && INSTALL_USER=`logname 2>/dev/null`
[ -z "$INSTALL_USER" ] && INSTALL_USER="$USER"
[ -z "$INSTALL_USER" ] && INSTALL_USER=`id -un 2>/dev/null`
>> "$STAGED_CFG" echo _BESClient_InstallTime_SudoUser=$INSTALL_USER
if [ -n "$RELAYPASS" ]; then
>> "$STAGED_CFG" echo _BESClient_SecureRegistration=$RELAYPASS
fi
fi
if [[ $OSTYPE == darwin* ]]; then
# Mac OS X
# the macOS installer reads the masthead from the staging folder
INSTALLDIR="$STAGINGDIR"
# example: https://software.bigfix.com/download/bes/110/BESAgent-11.0.6.137-BigFix_MacOS11.0.pkg
INSTALLERURL="https://software.bigfix.com/download/bes/$URLMAJORMINOR/BESAgent-$URLVERSION-BigFix_MacOS11.0.pkg"
INSTALLER="$STAGINGDIR/BESAgent.pkg"
else
# For most Linux:
INSTALLDIR="/etc/opt/BESClient"
# if dpkg exists (Debian)
if command_exists dpkg ; then
# Debian based
INSTALLER="$STAGINGDIR/BESAgent.deb"
# check distribution
# cat /etc/os-release /etc/lsb-release | grep --ignore-case --max-count=1 --count ubuntu
# cat /etc/os-release | grep '^ID' | awk -F= '{ print $2 }'
DEBDIST=`cat /etc/lsb-release | grep '^DISTRIB_ID' | awk -F= '{ print $2 }'`
if [[ $DEBDIST == "" ]]; then
DEBDIST=`cat /etc/os-release | grep '^ID' | awk -F= '{ print $2 }'`
fi
if [[ $OSBIT == x64 ]]; then
URLBITS=amd64
UBUNTUDIST=ubuntu18
DEBIANDIST=debian10
else
URLBITS=i386
# BigFix 9.5 (the last release with 32-bit x86 builds) uses older distro tags.
UBUNTUDIST=ubuntu10
DEBIANDIST=debian6
fi
if [[ $DEBDIST == *buntu* ]]; then
# Ubuntu
INSTALLERURL="https://software.bigfix.com/download/bes/$URLMAJORMINOR/BESAgent-$URLVERSION-$UBUNTUDIST.$URLBITS.deb"
else
# Debian
INSTALLERURL="https://software.bigfix.com/download/bes/$URLMAJORMINOR/BESAgent-$URLVERSION-$DEBIANDIST.$URLBITS.deb"
# get rasbian installer if on arm architecture:
if uname -m | grep --quiet --ignore-case -E "aarch64|arm" ; then
# Raspberry Pi OS / Raspbian is 32-bit ARM (armhf)
URLBITS=armhf
INSTALLERURL="https://software.bigfix.com/download/bes/$URLMAJORMINOR/BESAgent-$URLVERSION-raspbian10.armhf.deb"
dpkg --add-architecture armhf
apt-get update
fi
fi
# Check for CPU architecture (ppc64el)
# https://software.bigfix.com/download/bes/110/BESAgent-11.0.6.137-ubuntu18.ppc64el.deb
if uname -m | grep --quiet --ignore-case -E "ppc64le" ; then
# PPC64LE architecture
URLBITS=ppc64el
INSTALLERURL="https://software.bigfix.com/download/bes/$URLMAJORMINOR/BESAgent-$URLVERSION-ubuntu18.$URLBITS.deb"
fi
# if rpm exists (and dpkg does not - a Debian family system with the rpm
# command installed, e.g. via `alien`, must still use the .deb installer,
# so rpm is only considered when dpkg is absent)
elif command_exists rpm ; then
# rpm - Currently assuming RedHat based
INSTALLER="$STAGINGDIR/BESAgent.rpm"
if [[ $OSBIT == x64 ]]; then
URLBITS=x86_64
RHELDIST=rhe7
SUSEDIST=sle12
else
URLBITS=i686
# BigFix 9.5 (the last release with 32-bit x86 builds) uses older distro tags.
RHELDIST=rhe6
SUSEDIST=sle11
fi
INSTALLERURL="https://software.bigfix.com/download/bes/$URLMAJORMINOR/BESAgent-$URLVERSION-$RHELDIST.$URLBITS.rpm"
# TODO check for other CPU architectures (arm64, ppc64le, s390x, etc) and set URLBITS accordingly
# https://software.bigfix.com/download/bes/110/BESAgent-11.0.6.137-al2.aarch64.rpm
# https://software.bigfix.com/download/bes/110/BESAgent-11.0.6.137-rhe7.ppc64le.rpm
# https://software.bigfix.com/download/bes/110/BESAgent-11.0.6.137-rhe7.s390x.rpm
# if not RHEL family, fall through to SUSE (the only other RPM-based dist BigFix ships)
# Amazon Linux has /etc/system-release but NOT /etc/redhat-release, and
# should use the RHEL build, so only assume SUSE when neither exists.
if [ ! -f /etc/redhat-release ] && [ ! -f /etc/system-release ] ; then
# Assume SUSE
# SUSE is the only other RPM based linux supported by BigFix that is not based upon the RHEL family
INSTALLERURL=https://software.bigfix.com/download/bes/$URLMAJORMINOR/BESAgent-$URLVERSION-$SUSEDIST.$URLBITS.rpm
# NOTE: BigFix 11 dropped sle11 builds; sle12 is the oldest SUSE build published for 11.0.x.
# 32-bit x86 (i686) falls back to BigFix 9.5 which does publish sle11.i686.rpm.
# TODO: Check for CPU architecture (ppc64le, s390x) and set URLBITS accordingly
# https://software.bigfix.com/download/bes/110/BESAgent-11.0.6.137-sle12.ppc64le.rpm
# https://software.bigfix.com/download/bes/110/BESAgent-11.0.6.137-sle12.s390x.rpm
fi # END_IF not-RHEL-family
fi # END_IF dpkg (Debian) / rpm
if command_exists pkgadd ; then
# TODO: test case for Solaris
echo "Solaris Detected"
# BigFix on Solaris uses /etc/opt/BESClient for the masthead/clientsettings staging
# (same layout as Linux). Set it explicitly so any future non-Linux paths above
# don't accidentally leak into the Solaris branch.
INSTALLDIR="/etc/opt/BESClient"
INSTALLER="$STAGINGDIR/BESAgent.pkg"
# example: https://software.bigfix.com/download/bes/110/BESAgent-11.0.6.137.x86_sol11.pkg
INSTALLERURL=https://software.bigfix.com/download/bes/$URLMAJORMINOR/BESAgent-$URLVERSION.x86_sol11.pkg
echo $INSTALLERURL
# TODO check for CPU architecture (sparc)
# https://software.bigfix.com/download/bes/110/BESAgent-11.0.6.137.sparc_sol11.pkg
fi # END_IF pkgadd
fi # END_IF darwin
############################################################
# MUST HAVE ROOT PRIV
if [ "$(id -u)" != "0" ]; then
# dump out data for debugging
echo
echo OSTYPE=$OSTYPE
echo MACHINETYPE=$MACHINETYPE
echo OSBIT=$OSBIT
echo INSTALLDIR=$INSTALLDIR
echo INSTALLER=$INSTALLER
echo INSTALLERURL=$INSTALLERURL
echo URLBITS=$URLBITS
echo URLVERSION=$URLVERSION
echo URLMAJORMINOR=$URLMAJORMINOR
echo MASTHEADURL=$MASTHEADURL
echo DEBDIST=$DEBDIST
echo
echo "Sorry, you are not root. Exiting."
echo
exit 1
fi
############################################################
### Start execution: #################################
############################################################
# Create $INSTALLDIR folder if missing
if [ ! -d "$INSTALLDIR" ]; then
# Control will enter here if $INSTALLDIR doesn't exist.
mkdir -p "$INSTALLDIR"
fi
#### Downloads #############################################
# Fail fast if OS detection above did not produce a download URL (unsupported OS/arch).
if [ -z "$INSTALLERURL" ] || [ -z "$INSTALLER" ]; then
(>&2 echo "ERROR: could not determine BigFix agent download URL for this OS/arch.")
(>&2 echo " OSTYPE=$OSTYPE MACHINETYPE=$MACHINETYPE OSBIT=$OSBIT")
exit 3
fi
DLEXITCODE=0
if command_exists curl ; then
# Download the BigFix agent (using cURL because it is on most Linux & OS X by default)
curl -S -f -o "$INSTALLER" $INSTALLERURL
# https://stackoverflow.com/questions/6348902/how-can-i-add-numbers-in-a-bash-script
DLEXITCODE=$(( DLEXITCODE + $? ))
# Download the masthead, renamed, into the correct location
# TODO: get masthead from CWD instead if present
# https://unix.stackexchange.com/questions/60750/does-curl-have-a-no-check-certificate-option-like-wget
# the url for the masthead will not use a valid SSL certificate, instead it will use one tied to the masthead itself
curl -S -f --insecure -o "$INSTALLDIR/actionsite.afxm" $MASTHEADURL
DLEXITCODE=$(( DLEXITCODE + $? ))
else
if command_exists wget ; then
# this is run if curl doesn't exist, but wget does
# download using wget
# https://stackoverflow.com/questions/16678487/wget-command-to-download-a-file-and-save-as-a-different-filename
# https://www.gnu.org/software/wget/manual/html_node/HTTPS-_0028SSL_002fTLS_0029-Options.html
wget $MASTHEADURL -O "$INSTALLDIR/actionsite.afxm" --no-check-certificate
DLEXITCODE=$(( DLEXITCODE + $? ))
wget $INSTALLERURL -O "$INSTALLER"
DLEXITCODE=$(( DLEXITCODE + $? ))
else
echo neither wget nor curl is installed.
echo not able to download required files.
echo exiting...
exit 2
fi
fi
# Exit if download failed
if [ $DLEXITCODE -ne 0 ]; then
# https://stackoverflow.com/questions/2990414/echo-that-outputs-to-stderr
(>&2 echo Download Failed. ExitCode=$DLEXITCODE)
exit $DLEXITCODE
fi
# See here: https://github.com/jgstew/tools/blob/master/bash/enable_incoming_port.sh
# open up linux firewall to accept UDP 52311 - iptables
if command_exists iptables ; then
# -C checks if the rule already exists so re-runs don't append duplicates
iptables -C INPUT -p udp --dport 52311 -j ACCEPT 2>/dev/null || iptables -A INPUT -p udp --dport 52311 -j ACCEPT
fi
# open up linux firewall to accept UDP 52311 - firewall-cmd
if command_exists firewall-cmd ; then
firewall-cmd --zone=public --add-port=52311/udp --permanent
firewall-cmd --reload
fi
# open up linux firewall to accept UDP 52311 - firewall-offline-cmd
if command_exists firewall-offline-cmd ; then
# this applies in anaconda at install time in particular
firewall-offline-cmd --add-port=52311/udp
firewall-offline-cmd --reload
fi
# open Debian/Ubuntu firewall:
if command_exists ufw ; then
ufw allow 52311/udp
fi
# install BigFix client
if [[ $INSTALLER == *.deb ]]; then
# debian (DEB)
# Prefer apt-get so runtime dependencies get resolved automatically on
# minimal or container images. Fall back to dpkg (no dependency
# resolution) if apt-get is missing or fails (e.g. empty package lists).
# The absolute path (contains a slash) makes apt-get treat it as a local
# file, not a repo package.
# see the issue this solves: https://github.com/jgstew/tools/issues/19
if command_exists apt-get ; then
apt-get install -y "$INSTALLER" || dpkg -i "$INSTALLER"
else
dpkg -i "$INSTALLER"
fi
# Same shared-library gap as the rpm path below: the BESAgent .deb does not
# declare its library dependencies, so apt cannot pull them in. This is most
# visible for foreign-architecture installs (e.g. the raspbian armhf client
# on an arm64 host), where libstdc++.so.6:armhf is never installed and the
# binary fails at runtime with "cannot open shared object file".
#
# ldd is the cheap check that works on native hosts: if it reports no missing
# libraries the common case pays nothing (no extra tooling installed). But
# ldd runs the binary through its loader, so it cannot trace a foreign-arch
# binary - it prints "not a dynamic executable" instead. In that case fall
# back to a static readelf scan of the ELF NEEDED entries, which is
# architecture independent. Either way, map each soname to its package with
# apt-file (apt, unlike rpm, has no soname virtual-provides) and let apt-get
# install it - apt-get is idempotent, so already-present libraries are no-ops
# and only the genuinely missing ones get pulled in. Nothing is hardcoded so
# this adapts if the missing library changes.
if command_exists ldd && command_exists apt-get && [ -f /opt/BESClient/bin/BESClient ]; then
LDDOUT=`ldd /opt/BESClient/bin/BESClient 2>/dev/null`
MISSINGLIBS=`echo "$LDDOUT" | awk '/not found/ {print $1}' | sort -u`
# ldd could not trace the binary (foreign architecture) if it said so or
# produced nothing at all.
UNTRACEABLE=false
if echo "$LDDOUT" | grep -q "not a dynamic executable" || [ -z "$LDDOUT" ]; then
UNTRACEABLE=true
fi
if [ -n "$MISSINGLIBS" ] || [ "$UNTRACEABLE" = true ]; then
# architecture of the client package (armhf, ppc64el, amd64, i386) and its
# multiarch triplet, read from the already-installed libc for that arch.
# The triplet scopes the apt-file lookup to the right architecture's copy
# of the library (the soname itself is an ldconfig symlink, not a
# packaged file, so the lookup matches the versioned file under the
# triplet directory, e.g. .../arm-linux-gnueabihf/libstdc++.so.6.0.30).
PKGARCH=`dpkg-deb -f "$INSTALLER" Architecture 2>/dev/null`
TRIPLET=`dpkg -L libc6${PKGARCH:+:$PKGARCH} 2>/dev/null | grep -oE '/lib/[a-z0-9_]+-linux-gnu[a-z]*' | head -n1 | sed 's#/lib/##'`
# apt-file maps a soname to its package; install and index it on demand.
if ! command_exists apt-file ; then
apt-get install -y apt-file && apt-file update
fi
# Candidate sonames: exactly the missing ones when ldd could trace,
# otherwise every NEEDED entry from a static readelf scan.
CANDIDATELIBS="$MISSINGLIBS"
if [ "$UNTRACEABLE" = true ]; then
command_exists readelf || apt-get install -y binutils
if command_exists readelf ; then
CANDIDATELIBS=`readelf -d /opt/BESClient/bin/BESClient 2>/dev/null | awk -F'[][]' '/NEEDED/ {print $2}'`
fi
fi
for MISSINGLIB in $CANDIDATELIBS; do
# skip the dynamic loader itself: it lives outside the triplet dir and
# is always provided by an already-installed libc.
case "$MISSINGLIB" in ld-*|ld.so*) continue ;; esac
PROVIDER=""
if command_exists apt-file && [ -n "$TRIPLET" ]; then
# -l: package names only; -a: restrict to the client's arch. Scope the
# match to the arch triplet dir so it resolves to the real library
# package and not an unrelated cross-compiler package.
if [ -n "$PKGARCH" ]; then
PROVIDER=`apt-file -l -a "$PKGARCH" search "$TRIPLET/$MISSINGLIB" 2>/dev/null | head -n1`
else
PROVIDER=`apt-file -l search "$TRIPLET/$MISSINGLIB" 2>/dev/null | head -n1`
fi
fi
if [ -n "$PROVIDER" ]; then
echo "ensuring BESClient library $MISSINGLIB via package $PROVIDER${PKGARCH:+:$PKGARCH}"
if [ -n "$PKGARCH" ]; then
apt-get install -y "$PROVIDER:$PKGARCH" || apt-get install -y "$PROVIDER"
else
apt-get install -y "$PROVIDER"
fi
elif [ -n "$MISSINGLIBS" ]; then
# only warn about libraries ldd actually flagged as missing; in the
# untraceable case an unresolved NEEDED entry is usually already
# satisfied and not worth a warning.
case " $MISSINGLIBS " in
*" $MISSINGLIB "*) (>&2 echo "WARNING: could not find a package providing $MISSINGLIB - BESClient may not run") ;;
esac
fi
done
fi
fi
fi
if [[ $INSTALLER == *.pkg ]]; then
# PKG type
# Could be Mac OS X, Solaris, or AIX
if command_exists installer ; then
# Mac OS X
installer -pkg "$INSTALLER" -target /
else
if command_exists pkgadd ; then
# TODO: test case for Solaris
echo y | pkgadd -d "$INSTALLER" BESagent
fi # pkgadd
fi # installer
fi # *.pkg install file
if [[ $INSTALLER == *.rpm ]]; then
# linux (RPM)
# if file `/etc/init.d/besclient` exists then stop before upgrade
if [ -f /etc/init.d/besclient ]; then
/etc/init.d/besclient stop
fi
# Prefer a dependency resolving package manager (dnf, then yum) so runtime
# dependencies like libstdc++ get pulled in automatically on minimal or
# container images. Fall back to plain rpm, which does not resolve
# dependencies, since some environments only have the rpm command.
# The absolute path (contains a slash) makes dnf/yum treat it as a local
# file, not a repo package.
# see the issue this solves: https://github.com/jgstew/tools/issues/19
if command_exists dnf ; then
dnf install -y "$INSTALLER"
elif command_exists yum ; then
yum install -y "$INSTALLER"
elif command_exists zypper ; then
# SUSE: --no-gpg-checks because the BESAgent rpm key is not imported
zypper --non-interactive --no-gpg-checks install "$INSTALLER"
else
# if file `/etc/init.d/besclient` exists then do upgrade
if [ -f /etc/init.d/besclient ]; then
rpm -U "$INSTALLER"
else
rpm -ivh "$INSTALLER"
fi
fi
# The BESAgent rpm does not declare its shared library dependencies
# (verified against 11.0.6.137: `rpm -qp --requires` lists no sonames),
# so even a dependency resolving package manager cannot pull them in.
# Instead ask ldd which libraries are actually missing and install
# whatever package provides that soname (e.g. minimal container images
# are missing libdbus-1.so.3 and sometimes libstdc++.so.6).
# Nothing is hardcoded here so this adapts if the missing library changes.
# see the issue this solves: https://github.com/jgstew/tools/issues/19
if command_exists ldd && [ -f /opt/BESClient/bin/BESClient ]; then
for MISSINGLIB in `ldd /opt/BESClient/bin/BESClient 2>/dev/null | grep "not found" | awk '{print $1}' | sort -u`; do
echo "BESClient requires missing library: $MISSINGLIB - attempting to install it"
if command_exists dnf ; then
dnf install -y "$MISSINGLIB()(64bit)" || dnf install -y "$MISSINGLIB"
elif command_exists yum ; then
yum install -y "$MISSINGLIB()(64bit)" || yum install -y "$MISSINGLIB"
elif command_exists zypper ; then
zypper --non-interactive install "$MISSINGLIB()(64bit)" || zypper --non-interactive install "$MISSINGLIB"
else
(>&2 echo "WARNING: no package manager available to install $MISSINGLIB - BESClient may not run")
fi
done
fi
fi
# Linux only: /var/opt/BESClient and init.d/systemd do not apply to macOS,
# where the pkg installer handles config placement and service startup itself.
if [[ $OSTYPE != darwin* ]]; then
# if missing, create besclient.config file based upon the staged clientsettings.cfg
if [ ! -f /var/opt/BESClient/besclient.config ]; then
# NOTE: no /=/ regex literals here. Solaris nawk (and oawk) misparse a `/`
# after `~` or a `{` as the start of the /= operator, so the separator is
# matched with index()/gsub("=") using a plain string instead.
cat "$STAGED_CFG" | $AWK 'BEGIN { print "[Software\\BigFix\\EnterpriseClient]"; print "EnterpriseClientFolder = /opt/BESClient"; print; print "[Software\\BigFix\\EnterpriseClient\\GlobalOptions]"; print "StoragePath = /var/opt/BESClient"; print "LibPath = /opt/BESClient/BESLib"; } index($0, "=") { gsub("=", " "); print "\n[Software\\BigFix\\EnterpriseClient\\Settings\\Client\\" $1 "]\nvalue = " $2;}' > /var/opt/BESClient/besclient.config
chmod 600 /var/opt/BESClient/besclient.config
fi
### start the BigFix client (required for most linux dist)
# Do not start bigfix if: StartBigFix=false
if [[ "$StartBigFix" != "false" ]]; then
# Prefer the platform's service manager on real hosts: the SysV init.d
# script (shipped by the .deb) or systemd (used by the .rpm packages).
# Ignore failures here - many environments cannot run either, and the
# direct-launch fallback below covers them.
if [ -f /etc/init.d/besclient ]; then
/etc/init.d/besclient start || true
elif command_exists systemctl ; then
systemctl start besclient || true
fi
# Fallback for containers with no working init system: no systemd as PID 1
# ("System has not been booted with systemd"), no D-Bus, no systemctl at
# all, or a SysV stub that can't source /lib/lsb/init-functions. In those
# cases the block above is a no-op, so launch the client binary directly -
# the same approach as bigfix/bfdocker (see the reference in the header).
# BESClient daemonizes and holds its own single-instance lock, so this is
# harmless when the service manager already started it (and the guard
# skips it when the process is already detected).
if ! besclient_running && [ -x /opt/BESClient/bin/BESClient ]; then
echo "ERROR: starting /opt/BESClient/bin/BESClient directly (no running client detected)"
/opt/BESClient/bin/BESClient &
fi
fi
fi # END_IF not darwin
# nothing to wait for or tail if the client was intentionally not started
if [[ "$StartBigFix" == "false" ]]; then
exit 0
fi
# output the contents of the log file to see if things are working: https://github.com/jgstew/tools/blob/master/bash/bigfixlogs.sh
# Resolve the BigFix log folder: most unix/linux systems use the first path,
# but macOS stores the logs under /Library/Application Support. The client
# creates this folder shortly after it starts, so poll for up to ~30s rather
# than a single fixed sleep - a freshly (and sometimes directly) launched
# client can take a few seconds to initialize. If neither ever appears, exit
# nonzero so the failure is visible.
BESLOGDIR_UNIX="/var/opt/BESClient/__BESData/__Global/Logs"
BESLOGDIR_MACOS="/Library/Application Support/Bigfix/BES Agent/__BESData/__Global/Logs"
BESLOGDIR=""
echo "waiting up to 30 seconds for the BigFix log folder to appear"
i=0
while [ "$i" -lt 30 ]; do
if [ -d "$BESLOGDIR_UNIX" ]; then
BESLOGDIR="$BESLOGDIR_UNIX"
break
elif [ -d "$BESLOGDIR_MACOS" ]; then
BESLOGDIR="$BESLOGDIR_MACOS"
break
fi
i=$((i + 1))
sleep 1
done
if [ -z "$BESLOGDIR" ]; then
echo "ERROR: could not find BigFix log folder in either:" >&2
echo " $BESLOGDIR_UNIX" >&2
echo " $BESLOGDIR_MACOS" >&2
exit 1
fi
BESLOGFILE="$BESLOGDIR/`date +%Y%m%d`.log"
if [ -f "$BESLOGFILE" ]; then
# Use the obsolescent `tail -25` count form and `-f`: both are understood by
# Solaris SVR4 /usr/bin/tail AND GNU tail. The GNU long options
# (--lines/--verbose) and even POSIX `-n 25` are rejected by Solaris tail
# (it exited nonzero and failed the whole script). Print the filename
# ourselves in place of GNU --verbose.
echo "==> $BESLOGFILE <=="
if [ -n "$NOEXIT" ]; then
# tail log forever if NOEXIT set to anything
tail -f "$BESLOGFILE"
else
tail -25 "$BESLOGFILE"
fi
# Related:
# - https://bigfix.me/fixlet/details/24646
# - https://bigfix.me/relevance/details/3020387
else
echo "ERROR: BigFix log file not found: $BESLOGFILE" >&2
fi
### References:
# - https://stackoverflow.com/questions/733824/how-to-run-a-sh-script-in-an-unix-console-mac-terminal
# - https://stackoverflow.com/questions/394230/detect-the-os-from-a-bash-script
# - https://flokoe.github.io/bash-hackers-wiki/scripting/posparams/
# - https://stackoverflow.com/questions/6482377/check-existence-of-input-argument-in-a-bash-shell-script
# - https://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script
# - https://forum.bigfix.com/t/script-to-kickstart-the-installation-of-bigfix-on-os-x-debian-family-rhel-family/17023
# - https://stackoverflow.com/questions/30557508/bash-checking-if-string-does-not-contain-other-string