1 #!/bin/bash
  2 
  3 #   ------------------------------------------------------------------
  4 #
  5 #   Shell program to download a clouds map for xplanet from a random
  6 #   mirror, replacing the previously downloaded clouds map only after
  7 #   checking its correctness.
  8 #
  9 #   Copyright 2004-2007, Thorsten Bonow <toto@withouthat.org>.
 10 #
 11 #   This program is free software; you can redistribute it and/or
 12 #   modify it under the terms of the GNU General Public License as
 13 #   published by the Free Software Foundation; either version 2 of the
 14 #   License, or (at your option) any later version.
 15 #
 16 #   This program is distributed in the hope that it will be useful,
 17 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 19 #   General Public License for more details.
 20 #
 21 #   Description:
 22 #
 23 #   Usage:
 24 #
 25 #       xplanet-update-cloudmap.sh [ -h | --help ] | [ -v | --verbose ]
 26 #
 27 #   Options:
 28 #
 29 #       -h, --help    Display this help message and exit.
 30 #       -v, --verbose Output informative messages about what the script
 31 #                     is doing.
 32 #
 33 #   External programs:
 34 #
 35 #       sed      - stream editor
 36 #       basename - strip directory and suffix from filenames
 37 #       pgrep    - look up or signal processes based on name and other
 38 #                  attributes
 39 #       getopt   - parse command options (enhanced)
 40 #       date     - print or set the system date and time
 41 #       find     - search for files in a directory hierarchy
 42 #       wget     - the non-interactive network downloader.
 43 #       cp       - copy files and directories
 44 #       file     - determine file type
 45 #       grep     - print lines matching a pattern
 46 #       stat     - display file or file system status
 47 #       mktemp   - make temporary filename (unique)
 48 #
 49 #   Revision History:
 50 #
 51 #   2004-03-20      File created by new_script ver. 2.1.0
 52 #                   See ChangeLog for additional changes
 53 #
 54 #   CVS: $Id: xplanet-update-cloudmap.sh,v 1.5 2007-02-24 22:30:15 toto Exp $
 55 #
 56 #   ------------------------------------------------------------------
 57 
 58 ##### Preamble #####
 59 
 60 #   ------------------------------------------------------------------
 61 #   The list of cloud map mirrors. Add new ones here.
 62 #   ------------------------------------------------------------------
 63 
 64     declare -a CLOUDMAP_MIRRORS=(
 65         "ftp://mirror.pacific.net.au/pub2/xplanet/clouds_2048.jpg"
 66         "http://xplanet.fortha.org/clouds_2048.jpg"
 67         "http://www.ruwenzori.net/earth/clouds_2048.jpg"
 68         "http://xplanet.dyndns.org/clouds/clouds_2048.jpg"
 69         "http://userpage.fu-berlin.de/~jml/clouds_2048.jpg"
 70         "http://rcswww.urz.tu-dresden.de/~es179238/clouds_2048.jpg"
 71         "http://home.megapass.co.kr/~jhkim1101/cloud_data/clouds_2048.jpg"
 72         "http://user.chol.com/~winxplanet/cloud_data/clouds_2048.jpg"
 73         "http://home.megapass.co.kr/~gitto88/cloud_data/clouds_2048.jpg"
 74         "http://myhome.hanafos.com/~hyoungkee/cloud_data/clouds_2048.jpg"
 75         "http://giga.forfun.net/clouds_2048.jpg"
 76         "http://php.nctu.edu.tw/~ijliao/clouds_2048.jpg"
 77         "http://www.wizabit.eclipse.co.uk/xplanet/files/mirror/clouds_2048.jpg"
 78         "http://www.wizabit.eclipse.co.uk/xplanet/files/local/clouds_2048.jpg"
 79         "ftp://ftp.iastate.edu/pub/xplanet/clouds_2048.jpg"
 80         "http://enekoalonso.com/projects/xplanet/clouds_2048.php"
 81         "http://xplanet.nerp.net/clouds_2048.php"
 82         )
 83 
 84 #   ------------------------------------------------------------------
 85 #   Constants
 86 #   ------------------------------------------------------------------
 87 
 88     PATH=/sbin:/usr/sbin:/bin:/usr/bin
 89 
 90     declare -r PROGNAME=$(basename $0)
 91     declare -r VERSION="0.9.6"
 92 
 93     declare -r CLOUDMAP_DIR=/usr/local/share/xplanet/images
 94     declare -r CLOUDMAP_FILE=clouds_2048.jpg
 95 
 96     # how often to download the image? Defaults to 3h
 97     declare -r MAX_DOWNLOAD_FREQUENCY_MINUTES=180
 98 
 99     # how often to try to download
100     declare -r MAX_TRY=10
101 
102     # output of "file" when checking a jpeg image
103     declare -r JPEG_STRING="JPEG image data"
104 
105     # minimum size in bytes for a valid cloud map
106     declare -i CLOUDMAP_MIN_SIZE=210000
107 
108     # wget timeout
109     declare -i WGET_TIMEOUT=60
110 
111 
112 ##### Functions #####
113 
114 #   ------------------------------------------------------------------
115 #   Functions
116 #   ------------------------------------------------------------------
117 
118 
119 function clean_up() {
120 
121 #   ------------------------------------------------------------------
122 #   Function to remove temporary files and other housekeeping
123 #       No arguments
124 #   ------------------------------------------------------------------
125 
126     rm -f ${TEMP_FILE1}
127     return
128 
129 }   # end of clean_up
130 
131 
132 function message() {
133 
134 #   ------------------------------------------------------------------
135 #   Function to echo a message.
136 #       Arguments:
137 #           1 (required) MESSAGE_TEXT
138 #   ------------------------------------------------------------------
139 
140     # Fatal error if required argument is missing
141 
142     if [ "$1" = "" ]; then
143         error_exit "message: missing argument 1"
144     fi
145 
146     if [ "$verbose" = "true" ]; then
147         printf "%s\n" "$1"
148     fi
149 
150     return
151 
152 }   # end of message
153 
154 
155 function error_exit() {
156 
157 #   ------------------------------------------------------------------
158 #   Function for exit due to fatal program error
159 #       Arguments:
160 #           1 (optional) string containing descriptive error message
161 #   ------------------------------------------------------------------
162 
163 
164     printf "%s\n" "${PROGNAME}: ${1:-"Unknown Error"}" >&2
165     clean_up
166     exit 1
167 
168 }   # end of error_exit
169 
170 
171 function graceful_exit() {
172 
173 #   ------------------------------------------------------------------
174 #   Function called for a graceful exit
175 #       No arguments
176 #   ------------------------------------------------------------------
177 
178     clean_up
179     exit
180 
181 }   # end of graceful_exit
182 
183 
184 function signal_exit() {
185 
186 #   ------------------------------------------------------------------
187 #   Function to handle termination signals
188 #       Arguments:
189 #           1 (optional) signal_spec
190 #   ------------------------------------------------------------------
191 
192     case $1 in
193         INT)
194             message "$PROGNAME: Program aborted by user" >&2
195             clean_up
196             exit
197             ;;
198         TERM)
199             message "$PROGNAME: Program terminated" >&2
200             clean_up
201             exit
202             ;;
203         *)
204             error_exit "$PROGNAME: Terminating on unknown signal"
205             ;;
206 
207     esac
208 
209 }   # end of signal_exit
210 
211 
212 function make_temp_files() {
213 
214 #   ------------------------------------------------------------------
215 #   Function to create temporary files
216 #       No arguments
217 #   ------------------------------------------------------------------
218 
219     # Temp file for this script, using paranoid method of creation to
220     # insure that file name is not predictable.  This is for security
221     # to avoid "tmp race" attacks.  If more files are needed, create
222     # using the same form.
223 
224     TEMP_FILE1=$(mktemp -q "${CLOUDMAP_DIR}/${PROGNAME}.$$.XXXXXX")
225     if [ "$TEMP_FILE1" = "" ]; then
226         error_exit "cannot create temp file!"
227     fi
228 
229 }   # end of make_temp_files
230 
231 
232 function usage() {
233 
234 #   ------------------------------------------------------------------
235 #   Function to display usage message (does not exit)
236 #       No arguments
237 #   ------------------------------------------------------------------
238 
239     printf "%s\n" "Usage: ${PROGNAME} [-h | --help] | [ -v | --verbose ]"
240 
241 }   # end of usage
242 
243 
244 function helptext() {
245 
246 #   ------------------------------------------------------------------
247 #   Function to display help message for program
248 #       No arguments
249 #   ------------------------------------------------------------------
250 
251     cat <<EOF
252 
253     ${PROGNAME} ${VERSION}
254 
255     This is a program to download a clouds map for xplanet from a
256     random mirror, replacing the previously downloaded clouds map only
257     after checking its correctness.
258 
259     $(usage)
260 
261     Options:
262 
263         -h, --help    Display this help message and exit.
264         -v, --verbose Output informative messages about what the script
265                       is doing.
266 EOF
267 
268 }   # end of helptext
269 
270 
271 function check_for_other_instances() {
272 
273 #   ------------------------------------------------------------------
274 #   Function to terminate this script in case of other running
275 #   instances
276 #       No arguments
277 #   ------------------------------------------------------------------
278 
279     if [ $(pgrep -f -c $PROGNAME) -gt 1 ]; then
280         message "Another instance of this script is running, exiting."
281         graceful_exit
282     fi
283 
284     return
285 
286 }   # end of check_for_other_instances
287 
288 
289 
290 function initialisation() {
291 
292 #   ------------------------------------------------------------------
293 #   Function to initialise the script
294 #       No arguments
295 #   ------------------------------------------------------------------
296 
297     while [ -n "${CLOUDMAP_MIRRORS[${NO_OF_CLOUDMAP_MIRRORS}]}" ]; do
298         let "NO_OF_CLOUDMAP_MIRRORS += 1"
299     done
300 
301     # initialisation of random number generator
302     RANDOM=$(date +$s)
303 
304     return
305 
306 }   # end of initialisation
307 
308 
309 function shut_down() {
310 
311 #   ------------------------------------------------------------------
312 #   Function to shut down the script before exiting
313 #       No arguments
314 #   ------------------------------------------------------------------
315 
316     return
317 
318 }   # end of shutdown
319 
320 
321 function check_permissions() {
322 
323 #   ------------------------------------------------------------------
324 #   Function to check for necessary file permissions
325 #       No arguments
326 #   ------------------------------------------------------------------
327 
328     if [ ! -w "$CLOUDMAP_DIR" ]; then
329         error_exit "Cloud map directory '$CLOUDMAP_DIR' is not writable."
330     elif [ -e "$CLOUDMAP_DIR/$CLOUDMAP_FILE" ]; then
331         if [ ! -r "$CLOUDMAP_DIR/$CLOUDMAP_FILE" ]; then
332             error_exit "Cloud map file '$CLOUDMAP_FILE' is not a regular file."
333         elif [ -d "$CLOUDMAP_DIR/$CLOUDMAP_FILE" ]; then
334             error_exit "Cloud map file '$CLOUDMAP_FILE' is a directory."
335         elif [ ! -w "$CLOUDMAP_DIR/$CLOUDMAP_FILE" ]; then
336             error_exit "Cloud map file '$CLOUDMAP_FILE' is not writable."
337         else
338             OLD_CLOUDMAP_FILE_EXISTS="true"
339         fi
340     else
341         OLD_CLOUDMAP_FILE_EXISTS="false"
342     fi
343 
344     return
345 
346 }   # end of check_permissions
347 
348 
349 function random_number() {
350 
351 #   ------------------------------------------------------------------
352 #   Function to calculate a random number between 0 and the
353 #   NO_OF_CLOUDMAP_MIRRORS-1
354 #       No arguments
355 #   ------------------------------------------------------------------
356 
357     # random number generator has been initialised in initialisation
358     # function
359     rnd_number=$RANDOM
360     let "rnd_number %= $NO_OF_CLOUDMAP_MIRRORS"
361 
362     return
363 
364 }   # end of random_number
365 
366 
367 function check_update() {
368 
369 #   ------------------------------------------------------------------
370 #   Function to check if cloud map is old enough to be updated
371 #       No arguments
372 #   ------------------------------------------------------------------
373 
374     if [ "$(find $CLOUDMAP_DIR -mmin -$MAX_DOWNLOAD_FREQUENCY_MINUTES -name $CLOUDMAP_FILE)" == "$CLOUDMAP_DIR/$CLOUDMAP_FILE" ]; then
375         message "Cloud map is already up to date."
376         graceful_exit
377     fi
378 
379     return
380 
381 }   # end of check_update
382 
383 
384 function update_cloudmap() {
385 
386 #   ------------------------------------------------------------------
387 #   Function to update the cloudmap
388 #       No arguments
389 #   ------------------------------------------------------------------
390 
391     local -i i
392 
393     message "Trying mirrors..."
394     for ((i=0; i <= $MAX_TRY ; i++)); do
395         random_number;
396         wget -q --timeout=$WGET_TIMEOUT ${CLOUDMAP_MIRRORS[$rnd_number]} \
397             --output-document="${TEMP_FILE1}";
398         if [ $? == 0 ] ; then
399             message "Download successful, validating..."
400             validate_cloudmap && break
401         fi
402     done
403 
404     return
405 
406 }   # end of update_cloudmap
407 
408 
409 function validate_cloudmap() {
410 
411 #   ------------------------------------------------------------------
412 #   Function to validate the correctness of the cloudmap
413 #       No arguments
414 #   ------------------------------------------------------------------
415 
416     local -i return_value=1
417 
418     # "Missing file" HTML document is downloaded if there is no cloud
419     # map
420     file "$TEMP_FILE1" | grep -q "$JPEG_STRING"
421     if [ $? == 0 ] ; then
422         if [ $(stat -c%s "$TEMP_FILE1") -gt $CLOUDMAP_MIN_SIZE ]; then
423             return_value=0
424         else
425             message "Cloud map to small."
426         fi
427     else
428         message "Downloaded cloud map is not a valid JPEG."
429     fi
430 
431     if [ $return_value == 0 ]; then
432         cp "$TEMP_FILE1" "$CLOUDMAP_DIR/$CLOUDMAP_FILE" && \
433             message "Cloud map updated."
434     else
435         message "Cloud map not updated."
436     fi
437 
438     return $return_value
439 
440 }   # end of validate_cloudmap
441 
442 
443 #   ------------------------------------------------------------------
444 #   Program starts here
445 #   ------------------------------------------------------------------
446 
447     # verbosity; false by default since this script is normally run by
448     # cron
449     declare verbose="false"
450 
451 
452 ##### Command Line Processing #####
453 
454 # Note that we use `"$@"' to let each command-line parameter expand to
455 # a separate word. The quotes around `$@' are essential! We need
456 # GETOPT_TEMP as the `eval set --' would nuke the return value of
457 # getopt.
458 GETOPT_TEMP=$(getopt -o +hv --long help,verbose -n "$PROGNAME" -- "$@")
459 
460 if [ $? != 0 ] ; then
461     error_exit "Error parsing command line. Terminating..."
462 fi
463 
464 # Note the quotes around `$GETOPT_TEMP': they are essential!
465 eval set -- "$GETOPT_TEMP"
466 
467 # no error checking necessary; sanity of command line and required
468 # arguments has been checked by getopt program
469 while true ; do
470     case $1 in
471         -h|--help)
472             helptext ;
473             graceful_exit
474             ;;
475         -v|--verbose)
476             verbose="true" ;
477             shift
478             ;;
479         --)
480             shift ;
481             break
482             ;;
483         *)
484             # should be impossible to reach: getopt should have caught
485             # an error
486             error_exit "This should not have happened; unknown option '$1'. Terminating..."
487             ;;
488     esac
489 done
490 unset GETOPT_TEMP
491 
492 # processing remaining arguments for the client
493 if [ $# -ne 0  ]; then
494     usage
495     clean_up
496     exit 1
497 fi
498 
499 
500 ##### Initialization And Setup #####
501 
502 # only one instance should run at a time
503 check_for_other_instances
504 
505 # check if update is necessary
506 check_update
507 
508 # check file and directory permissions
509 check_permissions
510 
511 # Trap TERM, HUP, and INT signals and properly exit
512 
513 trap "signal_exit TERM" TERM HUP
514 trap "signal_exit INT"  INT
515 
516 ## Create temporary file(s)
517 #
518 make_temp_files
519 
520 
521 ##### Main Logic #####
522 
523 # is there a valid cloud map to be updated?
524 declare OLD_CLOUDMAP_FILE_EXISTS="false"
525 # No of cloud map mirrors
526 declare -i NO_OF_CLOUDMAP_MIRRORS=0
527 
528 # the random number specifying the mirror to use
529 declare -i rnd_number=0
530 
531 initialisation
532 
533 update_cloudmap
534 
535 shut_down
536 
537 graceful_exit
538 
539 # end of xplanet-update-cloudmap.sh
540 
541 # Local Variables:
542 # mode: outline-minor
543 # outline-regexp: "\\(function\\)\\|\\(##### \\)"
544 # outline-heading-end-regexp: "\\(() {\n\\)\\|\\( #####\n\\)"
545 # fill-column: 70
546 # End: