aboutsummaryrefslogtreecommitdiff
path: root/src/slack-autoupdate
blob: f31bd993147d098587f574c40329e508ef614f3a (plain)
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
#!/bin/bash
#
# Update your system automatically!
#
# Packages from different sources are downloaded, under sub-shells, into the
# PACKAGE_DIR where they can be installed on reboot.
#
# A failure encountered at any point should abort any pending package updates
# to install.
#
# To enable sbotools updates, make the /etc/sbotools/sbotools.conf file exist
# and configure PKG_DIR to point to the same path as STAGING_DIR.

# Set to "yes" to send notification to stdout, causing cron to send an email.
NOTIFY="${NOTIFY:-no}"

# Reboot after this period for install after a successful update.
#
# Set to empty to disable reboot or 'now' to reboot immediately.
export REBOOT_TIME="${REBOOT_TIME:-""}"

# Packages are copied here on successful download.
export PACKAGE_DIR="${PACKAGE_DIR:-/var/spool/slack-autoupdate}"

# Packages are temperarily stored here until success.
export STAGING_DIR="${STAGING_DIR:-/var/cache/slack-autoupdate/staging}"

# Information of interest to the admin on success.
export UPDATE_INFO="${UPDATE_INFO:-$STAGING_DIR/info.txt}"

# Information of interest to the admin on failure.
export UPDATE_ERROR="${UPDATE_ERROR:-$STAGING_DIR/error.txt}"

# Avoid concurrently running with another instance.
if [ "$(ls /var/lock/autoupdate.* 2>/dev/null)" ]; then
  echo "Another instance of autoupdate is running.  If this is not correct, you can remove /var/lock/autoupdate.* files and run autoupdate again." \
    >>"$PACKAGE_DIR/$(basename "$UPDATE_ERROR")"
  if [ "$NOTIFY" = "yes" ]; then
    cat "$PACKAGE_DIR/$(basename "$UPDATE_ERROR")"
  fi

  exit 1
fi

touch /var/lock/autoupdate.$$
trap "rm -f /var/lock/autoupdate.$$" EXIT

# Keep it simple stupid, install pending updates first.
mkdir --parents "$PACKAGE_DIR"
if [ -n "$(find "$PACKAGE_DIR" -name "*.t*z")" ]; then
  exit 0
fi

# Make the set of package updates available atomically by storing them in a
# temporary location until completion. 
rm -fr "$STAGING_DIR"
mkdir --parents "$STAGING_DIR"

if ! OUTPUT="$(
  # Capture this subshell's error output to standard output.
  exec 2>&1

  echo "Checking updates for slackpkg..."

  slackpkg -batch=on -default_answer=n update || exit "$?"

  CHANGELOG_TXT="$(mktemp /tmp/slack-autoupdate.XXXXXX)"
  PACKAGE_UPDATES="$(mktemp /tmp/slack-autoupdate.XXXXXX)"
  trap "rm -f ${CHANGELOG_TXT} ${PACKAGE_UPDATES}" EXIT

  slackpkg -batch=on -default_answer=n upgrade-all \
    | grep "\.t.z$" \
    | tee "$PACKAGE_UPDATES"  \
    || exit "$?"

  if [ ! -s "$PACKAGE_UPDATES" ]; then
    # No updates
    exit 0
  fi

  #
  # We need slackpkg to support downloading an exact package file to a provided
  # location.  The following is a workaround.
  #

  SOURCE=$(sed -n '
          # Remove leading and trailing blanks
          s/^[[:blank:]]*//
          s/[[:blank:]]*$//
          # Only one token is allowed per line
          /[[:blank:]]/d
          # A single solidus should end the URI
          s,[/]*$,/,
          # Print the lines beginning with one of the URI schemes we look for
          \@^file://@p
          \@^cdrom://@p
          \@^local://@p
          \@^https\{0,1\}://@p
          \@^ftps\{0,1\}://@p' /etc/slackpkg/mirrors)

  while read PKG; do
    PKG_URL="$(
      grep "${PKG}$" /var/lib/slackpkg/CHECKSUMS.md5 \
        | tr -s ' ' \
        | cut -f 2 -d ' '
      )" || exit "$?"

    wget \
      --quiet \
      --directory-prefix="$STAGING_DIR" \
      "${SOURCE}${PKG_URL}" \
      "${SOURCE}${PKG_URL}.asc" \
      || exit "$?"
  done <"$PACKAGE_UPDATES"

  gpg2 --verify-files "$STAGING_DIR"/*.asc || exit "$?"

  (
    # Provide update information to the user.

    # Redirect this subshell's standard output to info file.
    exec 1>>"$UPDATE_INFO"

    LAST_INSTALLED_PACKAGE="$(
      grep \
        "$(ls /var/log/packages | sed "s/$/\\\|/g" | tr --delete '\n')XXXXX" \
        /var/lib/slackpkg/ChangeLog.txt \
        | head -n 1
    )"

    echo "Slackware updates"
    echo
    grep --before-context 10000 "$LAST_INSTALLED_PACKAGE" /var/lib/slackpkg/ChangeLog.txt
    echo
  )
)"; then
  >>"$UPDATE_ERROR" echo -e "slackpkg:\n\n$OUTPUT"
fi

if ! OUTPUT="$(
  exec 2>&1

  echo "Checking updates for sbotools..."

  if [ ! -f /etc/sbotools/sbotools.conf ] \
    || [ "$(sed -n 's/PKG_DIR=//p' /etc/sbotools/sbotools.conf)" != "$STAGING_DIR" ]; then
    # Assume sbotools is disabled.
    exit 0
  fi

  source /etc/profile.d/*.sh

  PACKAGE_UPDATES=$(mktemp /tmp/slack-autoupdate.XXXXXX)
  trap "rm -f ${PACKAGE_UPDATES}" EXIT

  (sbocheck | tee $PACKAGE_UPDATES) || exit 1

  # Avoid checking for 'no updates available'.  It will not
  # work if you host 'overrides' purposely different than what is
  # on SBO.
  if ! grep "needs updating" "$PACKAGE_UPDATES"; then
    exit 0
  fi

  sboupgrade \
    --all \
    --noinstall \
    --nointeractive \
    || exit "$?"

  (
    exec 1>>"$UPDATE_INFO"

    echo "Slackbuild updates"
    echo
    cat "$PACKAGE_UPDATES"
    echo
  )
)"; then
  if [ -f "$UPDATE_ERROR" ]; then
    >>"$UPDATE_ERROR" echo ""
    >>"$UPDATE_ERROR" echo ""
  fi

  >>"$UPDATE_ERROR" echo -e "sbotools:\n\n$OUTPUT"
fi

# Make updates atomic.
if [ -f "$UPDATE_ERROR" ]; then
  mv "$UPDATE_ERROR" "$PACKAGE_DIR"
  mv "$UPDATE_INFO" "$PACKAGE_DIR"

  exit 1
fi


mv "$STAGING_DIR"/* "$PACKAGE_DIR/"
rm -fr "$STAGING_DIR"

if [ -z "$(find "$PACKAGE_DIR" -name "*.t*z" -or -name "info.txt" -or -name "error.txt")" ]; then
  # No updates
  exit 0
fi

if [ "$NOTIFY" = "yes" ]; then
  if [ -f "$UPDATE_ERROR" ]; then
    echo "Failures were encountered while trying to download updates"
    echo ""
    cat "$UPDATE_ERROR"
  elif [ -f "$UPDATE_INFO" ]; then
    echo "Updates pending installation"
    echo ""
    cat "$UPDATE_INFO"
  fi
fi

if [ -n "$REBOOT_TIME" ]; then
  nohup shutdown -r "$REBOOT_TIME" >/dev/null 2>&1 &
fi