/var/lib/sorcery/modules/libresurrect

     1	#!/bin/bash
     2	#---------------------------------------------------------------------
     3	## @Synopsis Functions for dealing with resurrecting a spell
     4	##
     5	## @Copyright Copyright (C) 2005 The Source Mage Team
     6	## <http://www.sourcemage.org>
     7	##
     8	## @Globals TBD
     9	##
    10	#---------------------------------------------------------------------
    11	
    12	
    13	#---------------------------------------------------------------------
    14	## @param spellname
    15	## determines if a particular version of a spell can be resurrected
    16	#---------------------------------------------------------------------
    17	function can_resurrect()  {
    18	  debug "libresurrect" "can_resurrect - $*"
    19	  local SPELL=$1
    20	  local VERSION=$2
    21	
    22	  find_cache "$INSTALL_CACHE/$SPELL-$VERSION-$HOST" || return 1
    23	  debug "libresurrect" "I am able to resurrect."
    24	  return 0
    25	}
    26	
    27	#------------------------------------------------------------------------
    28	## @param spell
    29	## @param version
    30	## @param cache file (optional)
    31	##
    32	## High level overview:
    33	##
    34	## ensure theres a cache tarball
    35	##
    36	## unpack the tarball somewhere
    37	##
    38	## if the spell is installed backup everything
    39	##
    40	## split the files into three categories: state data, config files, regular files
    41	##   this can be roughly viewed as (its slightly more complicated)
    42	##   state data is data owned by sorcery (/var/state)
    43	##   config files are defined in the /var/lib/sorcery/configs file
    44	##   things not in the above categories are regular files
    45	##
    46	## copy regular files onto the system
    47	## copy config files carefully (dont trample on user changes)
    48	##   if the file does not exist, just copy the new one
    49	##   elif the file exists and is identical to the new one, do nothing
    50	##   else the file exists and differs from the new one then
    51	##     if it is owned by the old spell with a valid md5
    52	##       replace the file
    53	##     elif it is owned by the old spell with an invalid md5 (admin changed it)
    54	##       ask if the user wants to change it
    55	##     else the file might be owned by another spell, or might be an alien
    56	##       ask if the user wants to change it
    57	##
    58	## if the spell was installed
    59	##   remove regular files unique to the old spell
    60	##   remove unchanged config files
    61	##   ask about changed config files
    62	##
    63	## update state data
    64	## call it a day
    65	##
    66	## FIXME: some of the informative message calls might work better as debug messages
    67	#------------------------------------------------------------------------
    68	function resurrect_spell()  { (
    69	  debug "libresurrect" "resurrect - $*"
    70	  local SPELL=$1
    71	  local VERSION=$2
    72	  local CACHE_COMP=$3
    73	
    74	  spell_held $SPELL && {
    75	    message "Refusing to resurrect a held spell, please unhold it first"
    76	    return 1
    77	  }
    78	
    79	  if [[ ! $VERSION ]] ; then
    80	    debug libresurrect "No spell version passed in"
    81	    return 1
    82	  fi
    83	
    84	  # 1) if no cache file exists fail
    85	  if [[ -z $CACHE_COMP ]]; then
    86	    CACHE_COMP=$(can_resurrect $SPELL $VERSION) || {
    87	      message "No cache file could be found"
    88	      return 1
    89	    }
    90	  fi
    91	
    92	  if test -z $CACHE_COMP || ! test -f $CACHE_COMP ; then
    93	    message "bug in can_resurrect, failing!"
    94	    return 1
    95	  fi
    96	
    97	  # 2) become king of the hill
    98	  acquire_cast_lock
    99	
   100	  # 3) setup resurrect sandbox
   101	
   102	  local RESURRECT_DIR=$BUILD_DIRECTORY/resurrect-$SPELL-$VERSION
   103	
   104	  mkdir -p $BUILD_DIRECTORY &&
   105	  mk_source_dir $RESURRECT_DIR || {
   106	    debug "libresurrect" "Failed to make $RESURRECT_DIR"
   107	    resurrect_fail
   108	    return 1
   109	  }
   110	
   111	  pushd $RESURRECT_DIR &>/dev/null || {
   112	    message "Failed to change directories to $RESURRECT_DIR"
   113	    resurrect_fail
   114	    return 1
   115	  }
   116	
   117	  # 4) unpack tarball to resurrect dir or fail
   118	  # note: tarballs are cached relative to $INSTALL_ROOT and $STATE_ROOT
   119	  # eg, with no special prefix, however install logs are relative to
   120	  # track_root and state root (isnt this fun?)
   121	  uncompress_unpack_cache $CACHE_COMP || {
   122	    message "Failed to unpack $CACHE_COMP"
   123	    resurrect_fail
   124	    return 1
   125	  }
   126	
   127	  # 5) if the spell is installed, take its install/md5 logs, and normalize them
   128	  # every way possible so I dont have to think about it later
   129	  local installed
   130	  if spell_ok $SPELL ; then
   131	    installed=yes
   132	    # note if installed is not yes, none of this should be assumed to exist
   133	
   134	    # welcome to the first ring of pathname adjustment hell
   135	    local OLD_SPELL_VERSION=$(private_installed_version $SPELL)
   136	    local OLD_INSTALL_LOG=${INSTALL_LOGS}/$SPELL-$OLD_SPELL_VERSION
   137	    local OLD_MD5_LOG=${MD5SUM_LOGS}/$SPELL-$OLD_SPELL_VERSION
   138	
   139	    local OLD_INSTALL_LOG_F=$TMP_DIR/old.install.$SPELL.filterable
   140	    local OLD_INSTALL_LOG_R=$TMP_DIR/old.install.$SPELL.root
   141	
   142	    local OLD_DATA_F=$TMP_DIR/old.data.$SPELL.filterable
   143	    local OLD_DATA_R=$TMP_DIR/old.data.$SPELL.root
   144	    local OLD_DATA_F_C=$TMP_DIR/old.data.$SPELL.filterable.config
   145	    local OLD_DATA_R_C=$TMP_DIR/old.data.$SPELL.root.config
   146	    local OLD_DATA_F_NC=$TMP_DIR/old.data.$SPELL.filterable.nonconfig
   147	    local OLD_DATA_R_NC=$TMP_DIR/old.data.$SPELL.root.nonconfig
   148	
   149	    local OLD_MDATA_F=$TMP_DIR/old.mdata.$SPELL.filterable
   150	    local OLD_MDATA_R=$TMP_DIR/old.mdata.$SPELL.root
   151	
   152	    local OLD_MD5_LOG_F=$TMP_DIR/old.md5.$SPELL.filterable
   153	    local OLD_MD5_LOG_R=$TMP_DIR/old.md5.$SPELL.root
   154	
   155	    if test -e $OLD_INSTALL_LOG ; then
   156	      log_adjuster $OLD_INSTALL_LOG $OLD_INSTALL_LOG_F log filterable
   157	      log_adjuster $OLD_INSTALL_LOG $OLD_INSTALL_LOG_R log root
   158	      # dont include the logs in meta-data
   159	      seperate_state_files $OLD_INSTALL_LOG_F $OLD_DATA_F /dev/stdout |
   160	      grep -v "$LOG_DIRECTORY" > $OLD_MDATA_F
   161	      log_adjuster $OLD_DATA_F $OLD_DATA_R filterable root
   162	      cat $OLD_DATA_F| filter_configs -v > $OLD_DATA_F_C
   163	      log_adjuster $OLD_DATA_F_C $OLD_DATA_R_C filterable root
   164	      cat $OLD_DATA_F| filter_configs > $OLD_DATA_F_NC
   165	      log_adjuster $OLD_DATA_F_NC $OLD_DATA_R_NC filterable root
   166	      log_adjuster $OLD_MDATA_F $OLD_MDATA_R filterable root
   167	    fi
   168	
   169	    if test -e $OLD_MD5_LOG ; then
   170	      log_adjuster $OLD_MD5_LOG $OLD_MD5_LOG_F log filterable md5_log_filter
   171	      log_adjuster $OLD_MD5_LOG $OLD_MD5_LOG_R log root md5_log_filter
   172	    fi
   173	  fi
   174	
   175	  # 5) if theres a tablet in the cache, load it, otherwise load the
   176	  # "regular" spell info
   177	  # load spell or tablet
   178	
   179	  # notice the magic dot
   180	  local TABLET_PATH=.${TABLET_PATH#$STATE_ROOT}
   181	  local OLD_TABLET_DIR
   182	  local SPELL_CONFIG_FILTER
   183	  local SECTION_CONFIG_FILTER
   184	  local GRIMOIRE_CONFIG_FILTER
   185	  tablet_find_resurrect_dir $SPELL OLD_TABLET_DIR
   186	  local TMP_VERSION=$VERSION
   187	  if [[ $OLD_TABLET_DIR ]] && test -d $OLD_TABLET_DIR ; then
   188	    debug "libresurrect" "loading tablet at $OLD_TABLET_DIR"
   189	    tablet_set_spell $SPELL $OLD_TABLET_DIR || {
   190	      message "something is wrong with $OLD_TABLET_DIR"
   191	      returrect_fail
   192	      return 1
   193	    }
   194	    tablet_get_spell_filter "$OLD_TABLET_DIR" configs SPELL_CONFIG_FILTER
   195	    tablet_get_grimoire_filter "$OLD_TABLET_DIR" configs SECTION_CONFIG_FILTER
   196	    tablet_get_section_filter "$OLD_TABLET_DIR" configs GRIMOIRE_CONFIG_FILTER
   197	  else
   198	    unset OLD_TABLET_DIR
   199	    debug "libresurrect" "no tablet"
   200	    codex_set_current_spell_by_name $SPELL
   201	    SPELL_CONFIG_FILTER=$SCRIPT_DIRECTORY/configs
   202	    SECTION_CONFIG_FILTER=$SECTION_DIRECTORY/configs
   203	    GRIMOIRE_CONFIG_FILTER=$GRIMOIRE/configs
   204	  fi
   205	  local CONFIG_FILTERS="$GRIMOIRE_CONFIG_FILTER $SECTION_CONFIG_FILTER $SPELL_CONFIG_FILTER"
   206	  # loading the spell may give us a different version which we dont want
   207	  if [[ $VERSION != $TMP_VERSION ]]; then
   208	    debug "libresurrect" "restoring VERSION from $VERSION to $TMP_VERSION"
   209	  fi
   210	  VERSION=$TMP_VERSION
   211	
   212	  # 6) find the cache's install log and divide it up every way imaginable
   213	  if ! [[ $OLD_TABLET_DIR ]] ; then
   214	    # in this case we know nothing about how the install log was built, so
   215	    # we have to make one up
   216	    local NEW_INSTALL_LOG=$TMP_DIR/new.install.log
   217	    find . | sed 's/^.//'|
   218	    log_adjuster /dev/stdin $NEW_INSTALL_LOG filterable log
   219	  else
   220	    local NEW_INSTALL_LOG=${INSTALL_LOGS#$STATE_ROOT/}/$SPELL-$VERSION
   221	    test -f $NEW_INSTALL_LOG || {
   222	      message "No install log found in unpacked cache, expected $NEW_INSTALL_LOG"
   223	      resurrect_fail
   224	      return 1
   225	    }
   226	  fi
   227	
   228	  # welcome back, did you get cold outside?
   229	  local NEW_INSTALL_LOG_F=$TMP_DIR/new.install.$SPELL.filterable
   230	  local NEW_INSTALL_LOG_R=$TMP_DIR/new.install.$SPELL.root
   231	
   232	  local NEW_DATA_F=$TMP_DIR/new.data.$SPELL.filterable
   233	  local NEW_DATA_R=$TMP_DIR/new.data.$SPELL.root
   234	  local NEW_DATA_F_C=$TMP_DIR/new.data.$SPELL.config.filterable
   235	  local NEW_DATA_R_C=$TMP_DIR/new.data.$SPELL.config.root
   236	  local NEW_DATA_F_NC=$TMP_DIR/new.data.$SPELL.nonconfig.filterable
   237	  local NEW_DATA_R_NC=$TMP_DIR/new.data.$SPELL.nonconfig.root
   238	
   239	  local NEW_MDATA_F=$TMP_DIR/new.mdata.$SPELL.filterable
   240	  local NEW_MDATA_R=$TMP_DIR/new.mdata.$SPELL.root
   241	
   242	  log_adjuster $NEW_INSTALL_LOG $NEW_INSTALL_LOG_F log filterable
   243	  log_adjuster $NEW_INSTALL_LOG $NEW_INSTALL_LOG_R log root
   244	  seperate_state_files $NEW_INSTALL_LOG_F $NEW_DATA_F $NEW_MDATA_F
   245	  log_adjuster $NEW_DATA_F $NEW_DATA_R filterable root
   246	  # cannot use filter_configs here because the tablet is in a
   247	  # non-standard location and may not exist at all...
   248	  cat $NEW_DATA_F| filter_in $CONFIGS $CONFIG_FILTERS > $NEW_DATA_F_C
   249	  log_adjuster $NEW_DATA_F_C $NEW_DATA_R_C filterable root
   250	  cat $NEW_DATA_F| filter $CONFIGS $CONFIG_FILTERS > $NEW_DATA_F_NC
   251	  log_adjuster $NEW_DATA_F_NC $NEW_DATA_R_NC filterable root
   252	  log_adjuster $NEW_MDATA_F $NEW_MDATA_R filterable root
   253	
   254	  # now that we have the install logs from the cache sorted out we can
   255	  # restore these which may have been overridden by tablet
   256	  tablet_unload_roots
   257	
   258	  # 7) take install lock
   259	  lock_resources "libgrimoire" "install"
   260	
   261	  # 8) backup the installed version of the spell
   262	  if [[ $installed == yes ]] && test -e $OLD_INSTALL_LOG ; then
   263	    # TODO evaluate the usefulness of this (see note in resurrect_fail
   264	    #local SAVE_DIR=$RESURRECT_DIR/save.dir.$$
   265	    #backup_spell $SPELL $SAVE_DIR $OLD_INSTALL_LOG_R || {
   266	      #message "Failed to backup old installation"
   267	      #resurrect_fail
   268	      #return 1
   269	    #}
   270	    # its okay to remove this stuff because its presumably been backed up
   271	    remove_files_and_dirs $OLD_MDATA_R $STATE_ROOT
   272	  fi
   273	
   274	  # 9) remove conflicts, but not self-conflicts, they make no sense
   275	  # be extra careful to get the right list, since we may be called from within cast
   276	  local conflicts;
   277	  if [[ -z $CAST_TMPDIR ]]; then
   278	    conflicts="$TMP_DIR/list.of.conflicts"
   279	    > "$conflicts"
   280	  else
   281	    conflicts="$CAST_TMPDIR/conflict_list" # the original $CONFLICT_LIST
   282	  fi
   283	  # we need the CONFLICTS file for this, but it wasn't unpacked yet
   284	  # change the SCRIPT_DIRECTORY out of the tablet (in the making) for this
   285	  # TODO: unpack just this file instead if it turns out there are problems
   286	  local tmp="$SCRIPT_DIRECTORY"
   287	  SCRIPT_DIRECTORY=$(codex_find_spell_by_name $SPELL)
   288	  query_conflicts $SPELL "$conflicts"
   289	  if [[ $? == 0 ]] && dispel_conflicts $SPELL "$conflicts"; then
   290	    rm "$conflicts"
   291	  else
   292	    rm "$conflicts"
   293	    resurrect_fail
   294	    return 1
   295	  fi
   296	  SCRIPT_DIRECTORY="$tmp"
   297	
   298	  #####
   299	  # Start of critical region
   300	  #####
   301	
   302	  resurrect_sub || return $?
   303	
   304	  #####
   305	  # End of critical region
   306	  #####
   307	
   308	  resurrect_success
   309	  unlock_resources "libgrimoire" "install"
   310	  unlock_resources "cast" "$SPELL"
   311	  unlock_resources "solo" "cast"
   312	
   313	  # do a cleanse --fix if requested (FIXME make this turn off-able)
   314	  #cleanse --fix  $SILENT  $SPELL
   315	
   316	  # n) run triggers
   317	  #TABLET_PATH=$STATE_DIRECTORY/tablet # reset to default, was relative
   318	  #SPELL_CONFIG="$DEPENDS_CONFIG/$SPELL"
   319	  #run_triggers # doesn't work since the spell isn't installed yet
   320	
   321	  return 0
   322	) }
   323	
   324	
   325	function resurrect_sub() {
   326	  # 10) if theres a PRE_RESURRECT run it or fail/rollback
   327	  if test -x $SCRIPT_DIRECTORY/PRE_RESURRECT ; then
   328	    . $SCRIPT_DIRECTORY/PRE_RESURRECT || {
   329	      debug "libresurrect" "PRE_RESURRECT failed"
   330	      resurrect_fail 1
   331	      return 1
   332	    }
   333	  fi
   334	
   335	
   336	  # 11) for each non-config file, all old files are already backed up
   337	  # tar is nice enough to preserve permissions, atime, etc. for us, using
   338	  # mkdir and cp it isnt so easy, this also has the advantage of not
   339	  # totally screwing us if we stomp on some critical libraries.
   340	  # ld-linux.so.2 anyone?
   341	  local res_fail=0
   342	  local verbose
   343	  [[ $VOYEUR != off ]] && verbose=-v
   344	  cat $NEW_DATA_F_NC | while read line; do
   345	    # filterable data also has the clever trait that to make it relative
   346	    # in this context we prepend a .
   347	
   348	    # tar doesnt like directories, but symlinks to directories pass
   349	    # test -d so handle them specially
   350	    test -h ".$line" && echo .$line ||
   351	    test -d ".$line" || echo .$line
   352	  done | sort | tar -cT - | tar $verbose -xf - -C ${INSTALL_ROOT:-/} || {
   353	    debug "libresurrect" "Failed to install regular files"
   354	    resurrect_fail 1
   355	    return 1
   356	  }
   357	  ldconfig # can never be too careful
   358	
   359	  # 12) carefully install config files (save old ones too), or fail/rollback
   360	  # this is interactive and so we must use iterator
   361	  local savetime=$(date +'%Y%m%d%H%M')
   362	
   363	  local res_fail=0 BREAK
   364	  function resurrect_install_conf_sub() {
   365	    debug libresurrect "resurrect_install_conf_sub -- $@"
   366	    test -d ".$1" ||
   367	    internal_install_config_file ".$1" "${INSTALL_ROOT}${1}" $savetime \
   368	                                                         "$OLD_MD5_LOG_R" || {
   369	      res_fail=1
   370	      BREAK=1
   371	    }
   372	  }
   373	  iterate resurrect_install_conf_sub $'\n' "$(<$NEW_DATA_F_C)"
   374	
   375	  [[ $res_fail == 0 ]] || {
   376	    debug "libresurrect" "Failed to install config files"
   377	    resurrect_fail 1
   378	    return 1
   379	  }
   380	  ldconfig # paranoia paranoia
   381	
   382	  # 13) remove un-replaced files
   383	  if [[ $installed == yes ]] && test -e $OLD_INSTALL_LOG ; then
   384	    # show what is in arg 1 but isnt in arg 2
   385	    # this handles non-configs which can be removed
   386	    awk -F : 'BEGIN {
   387	      while (getline < ARGV[2] ) { fc[$1]=1; }
   388	      while (getline < ARGV[1] ) { if( fc[$1] != 1 ) { print $1; } }
   389	    }' $OLD_DATA_F_NC $NEW_DATA_F_NC |
   390	    log_adjuster /dev/stdin  $TMP_DIR/leftover.$SPELL.non-config filterable root
   391	
   392	    # clear out old files/symlinks
   393	    remove_files_and_dirs $TMP_DIR/leftover.$SPELL.non-config "$INSTALL_ROOT"
   394	
   395	    # this handles configs which must be backed up if they've changed
   396	    awk -F : 'BEGIN {
   397	      while (getline < ARGV[2] ) { fc[$1]=1; }
   398	      while (getline < ARGV[1] ) { if( fc[$1] != 1 ) { print $1; } }
   399	    }' $OLD_DATA_F_C $NEW_DATA_F_C |
   400	    log_adjuster /dev/stdin  $TMP_DIR/leftover.$SPELL.config filterable root
   401	    touch $TMP_DIR/leftover.$SPELL.config
   402	    while read file; do
   403	      if check_if_modified "$OLD_MD5_LOG"; then
   404	        reap_modified_file $file
   405	      fi
   406	    done < $TMP_DIR/leftover.$SPELL.config
   407	  fi
   408	
   409	  # 14) if theres a POST_RESURRECT run it
   410	  if test -x $SCRIPT_DIRECTORY/POST_RESURRECT ; then
   411	    . $SCRIPT_DIRECTORY/POST_RESURRECT || {
   412	      message "POST_RESURRECT failed"
   413	      resurrect_fail 1
   414	      return 1
   415	    }
   416	  fi
   417	  ldconfig # can never be too careful
   418	}
   419	
   420	
   421	#------------------------------------------------------------------------
   422	## @param from        File we might want to install
   423	## @param to          File to replace
   424	## @param savetime    Backup time if necessary (optional, defaults to $(date +'%Y%m%d%H%M')
   425	#------------------------------------------------------------------------
   426	function real_install_config_file() {
   427	  if [[ ! $1 ]] || [[ ! $2 ]] ; then
   428	    message "${PROBLEM_COLOR}Missing values for arguments \"$1\" or \"$2\"" \
   429	            "$DEFAULT_COLOR"
   430	    return 1
   431	  fi
   432	
   433	  local old_md5_log
   434	  if [[ $OLD_SPELL_VERSION ]] ; then
   435	    old_md5_log=$MD5SUM_LOGS/$SPELL-$OLD_SPELL_VERSION
   436	    # log must be in filterable form
   437	    log_adjuster "$old_md5_log" "$TMP_DIR/$SPELL.md5" filterable root
   438	  else
   439	    old_md5_log=/dev/null
   440	  fi
   441	  local savetime=${3:-$(date +'%Y%m%d%H%M')}
   442	  internal_install_config_file "$1" "$2" $savetime $TMP_DIR/$SPELL.md5
   443	  rc=$?
   444	  [[ $OLD_SPELL_VERSION ]] && rm $TMP_DIR/$SPELL.md5
   445	
   446	  return $rc
   447	}
   448	
   449	#------------------------------------------------------------------------
   450	## @param from          File we might want to install
   451	## @param to            File to replace
   452	## @param savetime      Backup time if necessary
   453	## @param md5_log       MD5 log for previous version of the spell, must be in root form
   454	#------------------------------------------------------------------------
   455	function internal_install_config_file() {
   456	  local from=$1
   457	  local to=$2
   458	  local savetime=$3
   459	  local md5_log=$4
   460	
   461	  debug libresurrect "$FUNCNAME -- $@"
   462	  # if its a symlink, just copy, someday maybe we should figure
   463	  # out where it points to and so something, but thats too annoying
   464	  if test -h "$from" ; then
   465	    mkdir -p "$(smgl_dirname "$to")" &&
   466	    cp -v --no-dereference --preserve=mode,timestamps "$from" "$to"
   467	    return $?
   468	  fi
   469	
   470	  # if the file does not exist, or stage root is invoked
   471	  if ! test -e "$to" || [[ $STAGED_INSTALL == on ]] ; then
   472	    # copy the new one in (plus leading directories)
   473	    mkdir -p "$(smgl_dirname "$to")" &&
   474	    cp -v --preserve=mode,timestamps "$from" "$to"
   475	  # to get here the file must exist
   476	  elif test -d "$to"; then
   477	    message "Trying to install a file ($from) to what was a directory ($to), I dont know what to do!"
   478	    return 1
   479	
   480	  # elif the file exists and is identical to the new one
   481	  elif cmp "$to" "$from" &>/dev/null; then
   482	    echo "$from"
   483	    # preserve modification status
   484	    if check_if_modified "$to" "$md5_log"  ; then
   485	      mark_file_modified "$to"
   486	    fi
   487	  # else the file exists and differs from the new one then
   488	  else
   489	    # if it is owned by the old spell with a valid md5
   490	    # note that the md5_log is a subset of the install log, so if it
   491	    # matches in the md5 log, it is owned by the spell
   492	    if check_if_modified "$to" "$md5_log" ; then
   493	      # it is owned by the old spell with an invalid md5 OR
   494	      # the file might be owned by another spell OR
   495	      # might be an alien, all lead to the same direction
   496	      # note, it might be nice to split the above cases out for more informative
   497	      # user messages
   498	      real_handle_changed_config "$from" "$to" $savetime
   499	    else
   500	      # replace the file
   501	      mkdir -p "$(smgl_dirname "$to")" &&
   502	      cp -v --preserve=mode,timestamps "$from" "$to"
   503	    fi
   504	  fi
   505	}
   506	
   507	#------------------------------------------------------------------------
   508	## @param from          File we might want to install
   509	## @param to            File to replace
   510	## @param savetime      Backup time if necessary
   511	##
   512	## Present the user with the following menu
   513	## (0) trash $to and install over it
   514	## (1) backup $to to $to.$savetime, install the new file in its place
   515	## (2) leave $to in its place, copy the new file to $to.$savetime
   516	## (3) do nothing
   517	## (4) see a diff between $to and the new file
   518	## choice 2 is currently the default, someday there will be a menu to
   519	## choose what the default will be
   520	#------------------------------------------------------------------------
   521	function real_handle_changed_config() {
   522	  local from=$1
   523	  local to=$2
   524	  local savetime=$3
   525	  local number
   526	  local default=$DEFAULT_CHANGED_CONFIG_ACTION
   527	
   528	  local continue=yes
   529	  message "${MESSAGE_COLOR}Installing to $to, please choose an option:"
   530	  while [[ "$continue" == "yes" ]] ; do
   531	    message -n "${QUERY_COLOR}"
   532	    message "(0) trash $to and install over it"
   533	    message "(1) backup $to to $to.$savetime.old, install the new file in its place"
   534	    message "(2) leave $to in its place, copy the new file to $to.$savetime.new"
   535	    message "(3) do nothing - leave $to in its place, discard new file"
   536	    message "(4) see a diff between $to and the new file"
   537	    # TODO: someday add an option to use an external merge tool
   538	
   539	    number=""
   540	    while [[ $number != [0-9]* ]] || (( " $number >= 5 " )); do
   541	      message -n "\n${QUERY_COLOR}Which one do you want? [$default]" \
   542	                 "$DEFAULT_COLOR"
   543	      read -n 1 -t $PROMPT_DELAY number
   544	      if [[ ! -n $number ]]; then
   545	          number=$default
   546	      fi
   547	    done
   548	    echo
   549	    case $number in
   550	      0)  cp -v --preserve=mode,timestamps "$from" "$to"
   551	          break ;;
   552	      1)  cp -v --preserve=mode,timestamps "$to" "$to.$savetime.old"
   553	          cp -v --preserve=mode,timestamps "$from" "$to"
   554	          break ;;
   555	      2)  cp -v --preserve=mode,timestamps "$from" "$to.$savetime.new"
   556	          mark_file_modified "$to"
   557	          # this is deliberatly not track_manual, if this is run from
   558	          # FINAL the user might not have wanted the files tracked
   559	          # so we wont make an assumption on what the user intends
   560	          touch "$to"
   561	          break ;;
   562	      3)  mark_file_modified "$to"
   563	          touch "$to"
   564	          break ;;
   565	      4)  diff -u "$to" "$from" | $PAGER;;
   566	    esac
   567	    message "\n\nPlease select another option"
   568	  done
   569	
   570	}
   571	
   572	function resurrect_success() {
   573	
   574	  local INSTALL_LOG=$INSTALL_LOGS/$SPELL-$VERSION
   575	  local MD5_LOG=$MD5SUM_LOGS/$SPELL-$VERSION
   576	  local TMP_INSTALL_LOG_STATE=$TMP_DIR/$SPELL.tmp.ilog.state
   577	  local TMP_INSTALL_LOG_DATA=$TMP_DIR/$SPELL.tmp.ilog.data
   578	  local TMP_INSTALL_LOG=$TMP_DIR/$SPELL.tmp.ilog.all
   579	
   580	  # 14) copy compile log
   581	  # this file is not regenerated unlike its friends install and md5sum
   582	  local OLD_COMPILE_LOG=$(find .${COMPILE_LOGS#$INSTALL_ROOT}/ -maxdepth 1 -type f|head -n 1)
   583	  local COMPILE_LOG="$COMPILE_LOGS/$(smgl_basename "$OLD_COMPILE_LOG")"
   584	  # also relative to STATE_ROOT
   585	  cp "$OLD_COMPILE_LOG" "$COMPILE_LOG"
   586	
   587	  # 15) commit state data:
   588	  if [[ $OLD_TABLET_DIR ]] ; then
   589	    # depends info if it exists
   590	    # none of the old values are valid, only the new, uncommitted values are
   591	    remove_depends_status $DEPENDS_STATUS $SPELL
   592	    local spell_depends=$OLD_TABLET_DIR/depends
   593	    local t_DEPENDS_STATUS t_SUB_DEPENDS_STATUS
   594	    lock_start_transaction "$DEPENDS_STATUS" t_DEPENDS_STATUS
   595	    if [ -e $spell_depends ] ; then
   596	      cat  $spell_depends >> $t_DEPENDS_STATUS
   597	    fi
   598	    lock_commit_transaction $DEPENDS_STATUS
   599	
   600	    remove_sub_depends $SUB_DEPENDS_STATUS ".*" $SPELL
   601	    local spell_sub_depends=$OLD_TABLET_DIR/sub_depends
   602	    if [[ -e $spell_sub_depends ]]; then
   603	      lock_start_transaction "$SUB_DEPENDS_STATUS" t_SUB_DEPENDS_STATUS
   604	      cat $spell_sub_depends >> $t_SUB_DEPENDS_STATUS
   605	      lock_commit_transaction $SUB_DEPENDS_STATUS
   606	    fi
   607	
   608	    #   if theres a tablet dir, store it with a new timestamp
   609	    if new_tablet=$(tablet_get_path $SPELL) ; then
   610	      cp -Rp $OLD_TABLET_DIR/* $new_tablet
   611	      ln $INSTALL_LOG -sf $new_tablet/logs/install
   612	      ln $MD5_LOG -sf $new_tablet/logs/md5sum
   613	      ln $COMPILE_LOG -sf $new_tablet/logs/compile
   614	      find $new_tablet > $TMP_INSTALL_LOG_STATE # relative to STATE_ROOT
   615	    else
   616	      debug "libresurrect" "WARNING: unable to make tablet for $SPELL, oh well"
   617	    fi
   618	  fi
   619	
   620	
   621	  echo $COMPILE_LOG >> $TMP_INSTALL_LOG_STATE
   622	  echo $INSTALL_LOG >> $TMP_INSTALL_LOG_STATE
   623	  echo $MD5_LOG >> $TMP_INSTALL_LOG_STATE
   624	
   625	  # Use the cache's listing of files in filterable form
   626	  # dont use root form because it may be different
   627	  log_adjuster $NEW_DATA_F $TMP_INSTALL_LOG_DATA filterable root
   628	  cat $TMP_INSTALL_LOG_STATE $TMP_INSTALL_LOG_DATA > $TMP_INSTALL_LOG
   629	  log_adjuster $TMP_INSTALL_LOG $INSTALL_LOG root log
   630	
   631	  create_md5list $TMP_INSTALL_LOG /dev/stdout |
   632	  log_adjuster /dev/stdin $MD5_LOG root log md5_log_filter
   633	
   634	  tablet_check_version_cache "$VERSION_STATUS" &&
   635	  (
   636	    if [[ $new_tablet ]]; then
   637	      tablet_get_patchlevel $new_tablet PATCHLEVEL
   638	      tablet_get_security_patch $new_tablet SECURITY_PATCH
   639	      tablet_get_updated $new_tablet UPDATED
   640	    else
   641	      debug "libresurrect" "bad tablet for version cache; new_tablet: $new_tablet"
   642	      # we don't have a good tablet, so we don't know what we just resurrected
   643	      # insert dummy values
   644	      UPDATED=0
   645	      PATCHLEVEL=0
   646	      SECURITY_PATCH=0
   647	    fi
   648	    add_version_cache "$VERSION_STATUS" "$SPELL" "$VERSION" "$PATCHLEVEL" "$SECURITY_PATCH" "$UPDATED"
   649	  )
   650	
   651	  # add packages line
   652	  add_spell "$SPELL" "installed" "$VERSION"
   653	
   654	  pop_install_queue "$SPELL"
   655	  echo $SPELL >> $SUCCESS_LIST
   656	
   657	  popd &>/dev/null
   658	
   659	  rm_source_dir $RESURRECT_DIR
   660	
   661	  clear_line
   662	  activity_log  "resurrect"  "$SPELL"  "$VERSION"  "success"
   663	  message  "${RESURRECT_COLOR}Resurrected spell: ${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
   664	           "version ${VERSION_COLOR}${VERSION}${DEFAULT_COLOR}"
   665	}
   666	
   667	
   668	function resurrect_fail() {
   669	  message  "${PROBLEM_COLOR}Resurrect failed for spell:" \
   670	           "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR} version" \
   671	           "${VERSION_COLOR}${VERSION}${DEFAULT_COLOR}"
   672	
   673	  #if [[ $1 == 1 ]] ; then
   674	    #TODO evaluate the usefulness of a backup/rollback scheme
   675	    # this would be the location in which to do rollback, however
   676	    # I think it adds needless complexity and can be dangerous
   677	    # also, it shouldnt be needed in a properly functioning resurrect
   678	  #fi
   679	
   680	  unlock_resources "libgrimoire" "install"
   681	  unlock_resources "cast" "$SPELL"
   682	  unlock_resources "solo" "cast"
   683	
   684	  [[  $CLEAN_SOURCE == on ]] && rm_source_dir $RESURRECT_DIR 2>/dev/null
   685	
   686	  echo $SPELL >> $FAILED_LIST
   687	}
   688	
   689	#---------------------------------------------------------------------
   690	## @License
   691	##
   692	## This software is free software; you can redistribute it and/or modify
   693	## it under the terms of the GNU General Public License as published by
   694	## the Free Software Foundation; either version 2 of the License, or
   695	## (at your option) any later version.
   696	##
   697	## This software is distributed in the hope that it will be useful,
   698	## but WITHOUT ANY WARRANTY; without even the implied warranty of
   699	## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   700	## GNU General Public License for more details.
   701	##
   702	## You should have received a copy of the GNU General Public License
   703	## along with this software; if not, write to the Free Software
   704	## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   705	##
   706	#---------------------------------------------------------------------