]> git.proxmox.com Git - mirror_smartmontools-debian.git/blob - update-smart-drivedb.in
d98f6f7397ed99215860976437daf119af75c141
[mirror_smartmontools-debian.git] / update-smart-drivedb.in
1 #! /bin/sh
2 #
3 # smartmontools drive database update script
4 #
5 # Home page of code is: http://www.smartmontools.org
6 #
7 # Copyright (C) 2010-17 Christian Franke
8 #
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2, or (at your option)
12 # any later version.
13 #
14 # You should have received a copy of the GNU General Public License
15 # (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
16 #
17 # $Id: update-smart-drivedb.in 4584 2017-11-03 22:43:32Z chrfranke $
18 #
19
20 set -e
21
22 # Set by config.status
23 @ENABLE_SCRIPTPATH_TRUE@export PATH="@scriptpath@"
24 PACKAGE="@PACKAGE@"
25 VERSION="@VERSION@"
26 prefix="@prefix@"
27 exec_prefix="@exec_prefix@"
28 sbindir="@sbindir@"
29 datarootdir="@datarootdir@"
30 datadir="@datadir@"
31 drivedbdir="@drivedbdir@"
32
33 # Download tools
34 os_dltools="@os_dltools@"
35
36 # drivedb.h update branch
37 BRANCH="@DRIVEDB_BRANCH@"
38
39 # Default drivedb location
40 DRIVEDB="$drivedbdir/drivedb.h"
41
42 # GnuPG used to verify signature (disabled if empty)
43 GPG="@gnupg@"
44
45 # Smartctl used for syntax check
46 SMARTCTL="$sbindir/smartctl"
47
48 myname=$0
49
50 usage()
51 {
52 cat <<EOF
53 smartmontools $VERSION drive database update script
54
55 Usage: $myname [OPTIONS] [DESTFILE]
56
57 -s SMARTCTL Use SMARTCTL for syntax check ('-s -' to disable)
58 [default: $SMARTCTL]
59 -t TOOL Use TOOL for download: $os_dltools
60 [default: first one found in PATH]
61 -u LOCATION Use URL of LOCATION for download:
62 sf (Sourceforge code browser via HTTPS)
63 svn (SVN repository via HTTPS) [default]
64 svni (SVN repository via HTTP)
65 trac (Trac code browser via HTTPS)
66 --trunk Download from SVN trunk (may require '--no-verify')
67 --cacert FILE Use CA certificates from FILE to verify the peer
68 --capath DIR Use CA certificate files from DIR to verify the peer
69 --insecure Don't abort download if certificate verification fails
70 --no-verify Don't verify signature
71 --export-key Print the OpenPGP/GPG public key block
72 --dryrun Print download commands only
73 -v Verbose output
74
75 Updates $DRIVEDB
76 or DESTFILE from branches/$BRANCH of smartmontools
77 SVN repository.
78 EOF
79 exit 1
80 }
81
82 error()
83 {
84 echo "$myname: $*" >&2
85 exit 1
86 }
87
88 warning()
89 {
90 echo "$myname: (Warning) $*" >&2
91 }
92
93 selecturl()
94 {
95 case $1 in
96 sf) url='https://sourceforge.net/p/smartmontools/code/HEAD/tree/trunk/smartmontools/drivedb.h?format=raw' ;;
97 svn) url='https://svn.code.sf.net/p/smartmontools/code/trunk/smartmontools/drivedb.h' ;;
98 svni) url='http://svn.code.sf.net/p/smartmontools/code/trunk/smartmontools/drivedb.h' ;;
99 trac) url='https://www.smartmontools.org/export/HEAD/trunk/smartmontools/drivedb.h' ;;
100 *) usage ;;
101 esac
102 }
103
104 inpath()
105 {
106 local d rc save
107 rc=1
108 save=$IFS
109 IFS=':'
110 for d in $PATH; do
111 test -f "$d/$1" || continue
112 test -x "$d/$1" || continue
113 rc=0
114 break
115 done
116 IFS=$save
117 return $rc
118 }
119
120 vecho()
121 {
122 test -n "$q" || echo "$*"
123 }
124
125 # vrun COMMAND ARGS...
126 vrun()
127 {
128 if [ -n "$dryrun" ]; then
129 echo "$*"
130 elif [ -n "$q" ]; then
131 "$@" 2>/dev/null
132 else
133 echo "$*"
134 "$@"
135 fi
136 }
137
138 # vrun2 OUTFILE COMMAND ARGS...
139 vrun2()
140 {
141 local f err rc
142 f=$1; shift
143 rc=0
144 if [ -n "$dryrun" ]; then
145 echo "$* > $f"
146 else
147 vecho "$* > $f"
148 err=`"$@" 2>&1 > $f` || rc=$?
149 if [ -n "$err" ]; then
150 vecho "$err" >&2
151 test $rc != 0 || rc=42
152 fi
153 fi
154 return $rc
155 }
156
157 # download URL FILE
158 download()
159 {
160 local f u rc
161 u=$1; f=$2
162 rc=0
163
164 case $tool in
165 curl)
166 vrun curl ${q:+-s} -f --max-redirs 0 \
167 ${cacert:+--cacert "$cacert"} \
168 ${capath:+--capath "$capath"} \
169 ${insecure:+--insecure} \
170 -o "$f" "$u" || rc=$?
171 ;;
172
173 wget)
174 vrun wget $q --max-redirect=0 \
175 ${cacert:+--ca-certificate="$cacert"} \
176 ${capath:+--ca-directory="$capath"} \
177 ${insecure:+--no-check-certificate} \
178 -O "$f" "$u" || rc=$?
179 ;;
180
181 lynx)
182 test -z "$cacert" || vrun export SSL_CERT_FILE="$cacert"
183 test -z "$capath" || vrun export SSL_CERT_DIR="$capath"
184 # Check also stderr as lynx does not return != 0 on HTTP error
185 vrun2 "$f" lynx -stderr -noredir -source "$u" || rc=$?
186 ;;
187
188 svn)
189 vrun svn $q export \
190 --non-interactive --no-auth-cache \
191 ${cacert:+--config-option "servers:global:ssl-trust-default-ca=no"} \
192 ${cacert:+--config-option "servers:global:ssl-authority-files=$cacert"} \
193 ${insecure:+--trust-server-cert} \
194 "$u" "$f" || rc=$?
195 ;;
196
197 fetch) # FreeBSD
198 vrun fetch $q --no-redirect \
199 ${cacert:+--ca-cert "$cacert"} \
200 ${capath:+--ca-path "$capath"} \
201 ${insecure:+--no-verify-hostname} \
202 -o "$f" "$u" || rc=$?
203 ;;
204
205 ftp) # OpenBSD
206 vrun ftp \
207 ${cacert:+-S cafile="$cacert"} \
208 ${capath:+-S capath="$capath"} \
209 ${insecure:+-S dont} \
210 -o "$f" "$u" || rc=$?
211 ;;
212
213 *) error "$tool: unknown (internal error)" ;;
214 esac
215 return $rc
216 }
217
218 # check_file FILE FIRST_CHAR MIN_SIZE MAX_SIZE
219 check_file()
220 {
221 local firstchar f maxsize minsize size
222 test -z "$dryrun" || return 0
223 f=$1; firstchar=$2; minsize=$3; maxsize=$4
224
225 # Check first chars
226 case `dd if="$f" bs=1 count=1 2>/dev/null` in
227 $firstchar) ;;
228 \<) echo "HTML error message"; return 1 ;;
229 *) echo "unknown file contents"; return 1 ;;
230 esac
231
232 # Check file size
233 size=`wc -c < "$f"`
234 if test "$size" -lt $minsize; then
235 echo "too small file size $size bytes"
236 return 1
237 fi
238 if test "$size" -gt $maxsize; then
239 echo "too large file size $size bytes"
240 return 1
241 fi
242 return 0
243 }
244
245 # unexpand_svn_id < INFILE > OUTFILE
246 unexpand_svn_id()
247 {
248 sed 's,\$''Id'': drivedb\.h [0-9][0-9]* 2[-0-9]* [012][:0-9]*Z [a-z][a-z]* \$,$''Id''$,'
249 }
250
251 # Smartmontools Signing Key (through 2018)
252 # <smartmontools-database@listi.jpberlin.de>
253 # Key ID DFD22559
254 public_key="\
255 -----BEGIN PGP PUBLIC KEY BLOCK-----
256
257 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
298 =CNK4
299 -----END PGP PUBLIC KEY BLOCK-----
300 "
301
302 # gpg_verify FILE.asc FILE
303 gpg_verify()
304 {
305 local gnupgtmp opts out rc
306 opts="--quiet ${q:+--no-secmem-warnin} --batch --no-tty"
307
308 # Create temp home dir
309 gnupgtmp="$tmpdir/.gnupg.$$.tmp"
310 rm -f -r "$gnupgtmp"
311 mkdir "$gnupgtmp" || exit 1
312 chmod 0700 "$gnupgtmp"
313
314 # Import public key
315 "$GPG" $opts --homedir="$gnupgtmp" --import <<EOF
316 $public_key
317 EOF
318 test $? = 0 || exit 1
319
320 # Verify
321 rc=0
322 out=`"$GPG" $opts --homedir="$gnupgtmp" --verify "$1" "$2" </dev/null 2>&1` || rc=1
323 vecho "$out"
324
325 rm -f -r "$gnupgtmp"
326 return $rc
327 }
328
329 # mv_all PREFIX OLD NEW
330 mv_all()
331 {
332 mv "${1}${2}" "${1}${3}"
333 mv "${1}${2}.raw" "${1}${3}.raw"
334 mv "${1}${2}.raw.asc" "${1}${3}.raw.asc"
335 }
336
337 # Parse options
338 smtctl=$SMARTCTL
339 tool=
340 url=
341 q="-q"
342 dryrun=
343 trunk=
344 cacert=
345 capath=
346 insecure=
347 no_verify=
348
349 while true; do case $1 in
350 -s)
351 shift; test -n "$1" || usage
352 smtctl=$1 ;;
353
354 -t)
355 shift
356 case $1 in *\ *) usage ;; esac
357 case " $os_dltools " in *\ $1\ *) ;; *) usage ;; esac
358 tool=$1 ;;
359
360 -u)
361 shift; selecturl "$1" ;;
362
363 -v)
364 q= ;;
365
366 --dryrun)
367 dryrun=t ;;
368
369 --trunk)
370 trunk=trunk ;;
371
372 --cacert)
373 shift; test -n "$1" || usage
374 cacert=$1 ;;
375
376 --capath)
377 shift; test -n "$1" || usage
378 capath=$1 ;;
379
380 --insecure)
381 insecure=t ;;
382
383 --no-verify)
384 no_verify=t ;;
385
386 --export-key)
387 cat <<EOF
388 $public_key
389 EOF
390 exit 0 ;;
391
392 -*)
393 usage ;;
394
395 *)
396 break ;;
397 esac; shift; done
398
399 case $# in
400 0) DEST=$DRIVEDB ;;
401 1) DEST=$1 ;;
402 *) usage ;;
403 esac
404
405 if [ -z "$tool" ]; then
406 # Find download tool in PATH
407 for t in $os_dltools; do
408 if inpath "$t"; then
409 tool=$t
410 break
411 fi
412 done
413 test -n "$tool" || error "found none of: $os_dltools"
414 fi
415
416 test -n "$url" || selecturl "svn"
417
418 # Check option compatibility
419 case "$tool:$url" in
420 svn:http*://svn.code.sf.net*) ;;
421 svn:*) error "'-t svn' requires '-u svn' or '-u svni'" ;;
422 esac
423 case "$tool:${capath:+set}" in
424 svn:set) warning "'--capath' is ignored if '-t svn' is used" ;;
425 esac
426 case "${insecure:-f}:$url" in
427 t:http:*) insecure= ;;
428 ?:https:*) ;;
429 *) error "'-u svni' requires '--insecure'" ;;
430 esac
431 case "$tool:$insecure" in
432 lynx:t) warning "'--insecure' is ignored if '-t lynx' is used" ;;
433 esac
434
435 # Check for smartctl
436 if [ "$smtctl" != "-" ]; then
437 "$smtctl" -V >/dev/null 2>&1 \
438 || error "$smtctl: not found ('-s -' to ignore)"
439 fi
440
441 # Check for GnuPG
442 if [ -z "$no_verify" ]; then
443 test -n "$GPG" \
444 || error "GnuPG is not available ('--no-verify' to ignore)"
445 "$GPG" --version >/dev/null 2>&1 \
446 || error "$GPG: not found ('--no-verify' to ignore)"
447 fi
448
449 # Use destination directory as temp directory for gpg
450 tmpdir=`dirname "$DEST"`
451
452 # Adjust URLs
453 src=`echo "$url" | sed "s,/trunk/,/branches/$BRANCH/,"`
454 src_asc=`echo "$src" | sed "s,/drivedb\.h,/drivedb.h.raw.asc,"`
455 test -z "$trunk" || src=$url
456
457 # Download
458 test -n "$dryrun" || rm -f "$DEST.new" "$DEST.new.raw" "$DEST.new.raw.asc"
459
460 vecho "Download ${trunk:-branches/$BRANCH}/drivedb.h with $tool"
461 rc=0
462 download "$src" "$DEST.new" || rc=$?
463 if [ $rc != 0 ]; then
464 rm -f "$DEST.new"
465 error "${trunk:-$BRANCH}/drivedb.h: download failed ($tool: exit $rc)"
466 fi
467 if ! errmsg=`check_file "$DEST.new" '/' 10000 1000000`; then
468 mv "$DEST.new" "$DEST.error"
469 error "$DEST.error: $errmsg"
470 fi
471
472 vecho "Download branches/$BRANCH/drivedb.h.raw.asc with $tool"
473 rc=0
474 download "$src_asc" "$DEST.new.raw.asc" || rc=$?
475 if [ $rc != 0 ]; then
476 rm -f "$DEST.new" "$DEST.new.raw.asc"
477 error "$BRANCH/drivedb.h.raw.asc: download failed ($tool: exit $rc)"
478 fi
479 if ! errmsg=`check_file "$DEST.new.raw.asc" '-' 200 2000`; then
480 rm -f "$DEST.new"
481 mv "$DEST.new.raw.asc" "$DEST.error.raw.asc"
482 error "$DEST.error.raw.asc: $errmsg"
483 fi
484
485 test -z "$dryrun" || exit 0
486
487 # Create raw file with unexpanded SVN Id
488 # (This assumes newlines are LF and not CR/LF)
489 unexpand_svn_id < "$DEST.new" > "$DEST.new.raw"
490
491 # Adjust timestamps and permissions
492 touch "$DEST.new" "$DEST.new.raw" "$DEST.new.raw.asc"
493 chmod 0644 "$DEST.new" "$DEST.new.raw" "$DEST.new.raw.asc"
494
495 if [ -z "$no_verify" ]; then
496 # Verify raw file
497 if ! gpg_verify "$DEST.new.raw.asc" "$DEST.new.raw"; then
498 mv_all "$DEST" ".new" ".error"
499 test -n "$trunk" || error "$DEST.error.raw: *** BAD signature ***"
500 error "$DEST.error.raw: signature from branch no longer valid ('--no-verify' to ignore)"
501 fi
502 fi
503
504 if [ "$smtctl" != "-" ]; then
505 # Check syntax
506 if ! "$smtctl" -B "$DEST.new" -P showall >/dev/null; then
507 mv_all "$DEST" ".new" ".error"
508 error "$DEST.error: rejected by $smtctl, probably no longer compatible"
509 fi
510 vecho "$smtctl: syntax OK"
511 fi
512
513 # Keep old file if identical, ignore missing Id keyword expansion in new file
514 rm -f "$DEST.lastcheck"
515 if [ -f "$DEST" ]; then
516 if [ -f "$DEST.raw" ] && [ -f "$DEST.raw.asc" ]; then
517 if cmp "$DEST.raw" "$DEST.new.raw" >/dev/null 2>&1 \
518 && cmp "$DEST.raw.asc" "$DEST.new.raw.asc" >/dev/null 2>&1 \
519 && { cmp "$DEST" "$DEST.new" >/dev/null 2>&1 \
520 || cmp "$DEST.raw" "$DEST.new" >/dev/null 2>&1; }
521 then
522 rm -f "$DEST.new" "$DEST.new.raw" "$DEST.new.raw.asc"
523 touch "$DEST.lastcheck"
524 echo "$DEST is already up to date"
525 exit 0
526 fi
527 mv_all "$DEST" "" ".old"
528 else
529 mv "$DEST" "$DEST.old"
530 fi
531 fi
532
533 mv_all "$DEST" ".new" ""
534
535 echo "$DEST updated from ${trunk:-branches/$BRANCH}${no_verify:+ (NOT VERIFIED)}"