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: