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
|
#!/usr/bin/env bash
# shellcheck disable=SC1091
# Ad-hoc script for just patching the system.
# Environment variables:
# RKHUNTER - will be set to 0 if rkhunter exists and this variable is not set.
# Setting it to 1 will skip using rkhunter.
# NEEDREST - will be set to 0 if needrestart exists and this variable is not set.
# Setting it to 1 will skip using needrestart or any other helper
# like dnf needs-restarting or zypper needs-rebooting / zypper ps.
# os_patching made by albatrossflavour et al., binary:
OSPBIN='/usr/local/bin/os_patching_fact_generation.sh'
. /etc/os-release || exit 1
# Debian act as if ID_LIKE wasn't necessary if ID == ID_LIKE. Great job, guys.
if [ "$ID" = "debian" ]; then
ID_LIKE="debian"
fi
# 0. Internal helpers
hline() {
[ -n "$COLUMNS" ] && MYCOLS="$COLUMNS"
[ -z "$MYCOLS" ] && MYCOLS="$(/usr/bin/tput cols 2>/dev/null)"
[ -z "$MYCOLS" ] && MYCOLS=16
c=0
printf '\033[1m'
while [ "$c" -lt "$MYCOLS" ]; do
printf '─'
c="$((c+1))"
done
printf '\033[0m\n'
}
dline() {
[ -n "$COLUMNS" ] && MYCOLS="$COLUMNS"
[ -z "$MYCOLS" ] && MYCOLS="$(/usr/bin/tput cols 2>/dev/null)"
[ -z "$MYCOLS" ] && MYCOLS=16
c=0
while [ "$c" -lt "$MYCOLS" ]; do
printf '┄'
c="$((c+1))"
done
printf '\n'
}
header() {
if [ -n "$1" ]; then
hline
printf ' \033[3m\033[1m%b\033[0m\n' "$1"
hline
fi
}
footer() {
if [ -n "$1" ]; then
dline
printf '\033[3m\033[1m%b\033[0m\n' "$1"
fi
}
# 1. Find out about auxiliary helpers like rkhunter
declare NRSBIN RKHBIN
[ -z "$RKHUNTER" ] && RKHUNTER=2
[ -z "$NEEDREST" ] && NEEDREST=2
if [ "$NEEDREST" -gt 1 ] ; then
for bin in /usr/sbin/needrestart /usr/bin/needrestart; do
if [ -x "$bin" ]; then
NRSBIN="$bin"
NEEDREST=0
break
fi
done
fi
if [ "$RKHUNTER" -gt 1 ] ; then
for bin in /usr/bin/rkhunter /usr/sbin/rkhunter; do
if [ -x "$bin" ]; then
RKHBIN="$bin"
RKHUNTER=0
break
fi
done
fi
# 2. Patching.
case "$ID_LIKE" in
"debian")
APTBIN='/usr/bin/apt'
APTOPTS=(
'-o' 'Apt::Cmd::Disable-Script-Warning=true'
'-o' 'Dpkg::Progress-Fancy=False'
'-o' 'Apt::Color=False'
'-o' 'Dpkg::Use-Pty=False'
'-o' 'Quiet::NoUpdate=True'
'-o' 'APT::Get::AutomaticRemove=False'
'-o' 'APT::Get::AutomaticRemove::Kernels=False'
'-o' 'APT::Get::Assume-Yes=True'
)
if [ "$RKHUNTER" -eq 0 ]; then
header 'Starting rkhunter check'
"$RKHBIN" -c --sk || exit 120
fi
# 2.1. Package list refresh
header 'Starting package list update'
"$APTBIN" "${APTOPTS[@]}" update || exit 110
ULIST="$("$APTBIN" "${APTOPTS[@]}" -q list --upgradable | grep -iP '^[0-9a-z_:\-+\.]+/.+' | sed 's/^\([^/]\+\).*/\1/')"
# Only one update will be one update with or without line-break, and NO update will be also with or without line-break.
# Solution: Always add a line-break, and grep away empty lines.
UPDATENUM="$(printf '%b\n' "$ULIST" | grep -vcP '^$')"
printf '\033[3m\033[1m%b update(s) found.\033[0m\n' "$UPDATENUM"
# 2.2. Package update.
# 2.2.1. No updates found?
if [ "$UPDATENUM" -lt 1 ]; then
printf '\033[3m\033[1m\033[2mSkipping updates.\033[0m\n'
else
# 2.2.2. Updates found?
header 'Starting package updates'
"$APTBIN" "${APTOPTS[@]}" full-upgrade || exit 112
header 'Starting package auto-removal'
"$APTBIN" "${APTOPTS[@]}" --purge autoremove || exit 113
# 2.2.3. Package file index update
if [ -x /usr/bin/apt-file ]; then
printf 'Starting apt-file update'
/usr/bin/apt-file "${APTOPTS[@]}" update || true
fi
if [ "$RKHUNTER" -eq 0 ]; then
header 'Starting rkhunter update'
"$RKHBIN" --propupd || exit 121
fi
# 2.2.4. Requirement for reboot
if [ "$NEEDREST" -eq 0 ]; then
header 'Starting needrestart investigation'
"$NRSBIN" -b
# Outdated comment (kind of), see $NEEDREST at the top of the file --
# If we don't have needrestart, this will fail - which is OK, without
# a means of controlling whether reboot is necessary we will reboot in any case.
if ! "$NRSBIN" -p; then
footer 'Outdated libraries or kernel found, rebooting'
/usr/bin/systemctl reboot || reboot
else
if [ "$UPDATENUM" -gt 0 ]; then
if [ -x "$OSPBIN" ]; then
header 'Starting os_patching_fact_generation.sh'
ospstart="$(/usr/bin/date '+%s')"
"$OSPBIN"
ospend="$(/usr/bin/date '+%s')"
footer "...done ($((ospend - ospstart)) seconds)."
fi
fi
fi
elif [ "$NEEDREST" -gt 1 ]; then
footer 'No needrestart found, rebooting'
/usr/bin/systemctl reboot || reboot
fi
fi
;;
"suse"*)
# Caution:
# 1. Broken package dependenciers will not be solved
# 2. Orphaned packages will be kept in-place
header 'Refreshing zypper "services"'
mystart="$(/usr/bin/date '+%s')"
/usr/bin/zypper -q --non-interactive refresh-services && printf 'OK.\n' || exit 110
myend="$(/usr/bin/date '+%s')"
footer "...done ($((myend - mystart)) seconds)."
header 'Refreshing repository cache'
mystart="$(/usr/bin/date '+%s')"
/usr/bin/zypper -q --non-interactive refresh && printf 'OK.\n' || exit 111
myend="$(/usr/bin/date '+%s')"
footer "...done ($((myend - mystart)) seconds)."
# TODO: no amount of "-q" keeps zypper from delivering the verbose list of updates before
# installing them. If only the zypper guys were modern in thinking script or automation approaches...
header 'Running update'
mystart="$(/usr/bin/date '+%s')"
/usr/bin/zypper -q --no-refresh --non-interactive-include-reboot-patches \
up -y --auto-agree-with-licenses --solver-focus 'Update' && printf 'OK.\n' || exit 112
myend="$(/usr/bin/date '+%s')"
footer "...done ($((myend - mystart)) seconds)."
header 'Running dist-upgrade'
mystart="$(/usr/bin/date '+%s')"
/usr/bin/zypper -q --no-refresh --non-interactive-include-reboot-patches \
dup -y --allow-name-change --allow-arch-change --allow-vendor-change --no-allow-downgrade \
--auto-agree-with-licenses --solver-focus 'Update' && printf 'OK.\n' || exit 113
myend="$(/usr/bin/date '+%s')"
footer "...done ($((myend - mystart)) seconds)."
# zypper: why deliver exit codes WHEN WE CAN TOSS EFFIN STRINGS AT THE CONSOLE ONLY
# also... too many people rather tend to localise their systems....:
export LANG=C # use "C" as safe haven, we DO NOT want this to fail
# yes, in the very recent part they invented "needs-rebooting", but that only checks
# core services and libraries... :( so we do both here. We want to be rather aggressive
# on unattended updates in that we reboot if there are lingering "programs". Any of them.
header 'Checking reboot requirement... '
if ! /usr/bin/zypper -q needs-rebooting; then
# TODO: zypper being locked by another "application" delivers RC7 - is this reserved
# exclusively for this case?
footer 'Rebooting (zypper needs-rebooting)'
/usr/bin/systemctl reboot
elif [ "$(/usr/bin/zypper ps -sss 2>&1 | wc -l)" -gt 0 ]; then
footer 'Rebooting (zypper ps)'
/usr/bin/systemctl reboot
else
printf 'no reboot required.\n'
if [ -x "$OSPBIN" ]; then
header 'Starting os_patching_fact_generation.sh'
ospstart="$(/usr/bin/date '+%s')"
"$OSPBIN"
ospend="$(/usr/bin/date '+%s')"
footer "...done ($((ospend - ospstart)) seconds)."
fi
fi
;;
"rhel"*|"centos"*)
# we do not use --skip-broken here - we keep our systems tidy, so any pollution may and should
# cause an error :-)
header 'Starting package list update'
mystart="$(/usr/bin/date '+%s')"
/usr/bin/dnf -d1 makecache && printf 'OK.\n' || exit 110
myend="$(/usr/bin/date '+%s')"
footer "...done ($((myend - mystart)) seconds)."
header 'Starting package upgrade'
mystart="$(/usr/bin/date '+%s')"
/usr/bin/dnf --comment='os_patching_adhoc' -d1 --obsoletes --best -y upgrade &&\
printf 'OK.\n' || exit 111
myend="$(/usr/bin/date '+%s')"
footer "...done ($((myend - mystart)) seconds)."
header 'Checking reboot requirement'
if ! /usr/bin/dnf -d1 needs-restarting -r; then
footer 'Outdated libraries or kernel found, rebooting.'
/usr/bin/systemctl reboot
else
if [ -x "$OSPBIN" ]; then
header 'Starting os_patching_fact_generation.sh'
ospstart="$(/usr/bin/date '+%s')"
"$OSPBIN"
ospend="$(/usr/bin/date '+%s')"
footer "...done ($((ospend - ospstart)) seconds)."
fi
fi
;;
esac
|