/var/lib/sorcery/modules/libdispel
1 #!/bin/bash
2 #---------------------------------------------------------------------
3 ##
4 ## @Synopsis Functions for dispelling a spell
5 ##
6 ##
7 ## @Copyright Original version Copyright 2001 by Kyle Sallee
8 ## @Copyright Additions/Corrections Copyright 2002-4 by the SourceMage Team
9 ##
10 ## Functions for dispelling spells.
11 #---------------------------------------------------------------------
12
13
14 #---------------------------------------------------------------------
15 ## @Stdin list of files
16 ##
17 ## Given a list of files from standard input, deletes each file.
18 ## Performs a "rm -f" on each file given in standard input, so be
19 ## careful using this function!
20 ##
21 #---------------------------------------------------------------------
22 function reap_regular_files() {
23 debug "libsorcery" "reap_regular_files()"
24 local FILE
25 while read FILE; do
26 rm -f "$FILE"
27 done
28 }
29
30
31 #---------------------------------------------------------------------
32 ## @Stdin list of files
33 ##
34 ## Reads a list of files from standard input. If the file has been
35 ## modified (md5sum doesn't match the stored md5sum), then function
36 ## C<reap_modified_file> is called. Otherwise, the file is deleted.
37 ##
38 #---------------------------------------------------------------------
39 function reap_config_files() {
40 debug "libsorcery" "reap_config_files()"
41 local FILE md5
42 while read FILE; do
43 [[ $FILE ]] || continue
44 if [[ $TOTAL_DISPEL ]] ; then
45 rm -f "$FILE"
46 continue
47 fi
48
49 md5=$(md5sum "$FILE")
50 orig="$SCRIPT_DIRECTORY/init.d/${FILE##*/}.conf"
51 if grep -qx "$md5" "$MD5S"; then
52 debug libdispel "$FILE is a config file that hasnt changed, removing..."
53 rm -f "$FILE"
54 # this check only handles sysconfig files, since they're the only ones
55 # nicely available at this point
56 elif [[ -f $orig ]] &&
57 [[ $(cut -d" " -f1 <<< "$md5") == $(md5sum "$orig" | cut -d" " -f1) ]]; then
58 debug libdispel "$FILE is a config file that was reverted to the default, removing..."
59 rm -f "$FILE"
60 else
61 reap_modified_file "$FILE"
62 fi
63 done
64 }
65
66 #---------------------------------------------------------------------
67 ## @param file
68 ##
69 ## If C<PRESERVE> is off, will move the file to filename.YYYYMMDD. If
70 ## C<PRESERVE> is on, the file will not be moved.
71 ##
72 #---------------------------------------------------------------------
73 function reap_modified_file() {
74 local SAVE
75 message "${FILE_COLOR}${1}${DEFAULT_COLOR}"
76 message "${MESSAGE_COLOR}was previously modified by SA?"
77 case $PRESERVE in
78 on) message "Therefore, it was not reaped." ;;
79 off) SAVE="$1.`date -u +%Y%m%d`"
80 mv $1 $SAVE
81 message "Therefore, it was moved to"
82 message "${FILE_COLOR}${SAVE}" ;;
83 esac
84 message "${DEFAULT_COLOR}"
85
86 }
87
88 #---------------------------------------------------------------------
89 ## Removes spell config stages if there are any
90 #---------------------------------------------------------------------
91 function reap_spell_config_stage() {
92 lock_file "$CONFIG_STAGE_DIRECTORY/$SPELL"
93 # it's locked - we also don't have to care about the unlikely "current" stage
94 rm -fr "$CONFIG_STAGE_DIRECTORY/$SPELL"
95 unlock_file "$CONFIG_STAGE_DIRECTORY/$SPELL"
96 }
97
98 #---------------------------------------------------------------------
99 ## Removes depends entries for what the spell depends on
100 #---------------------------------------------------------------------
101 function reap_depends() {
102 # save the old depends data as abandoned stuff so its seen later on
103 # recasts as the default
104 mkdir -p $ABANDONED_DEPENDS
105 local t_file
106 lock_start_transaction "$ABANDONED_DEPENDS/$SPELL" t_file
107 search_depends_status $DEPENDS_STATUS $SPELL > $t_file
108 lock_commit_transaction "$ABANDONED_DEPENDS/$SPELL"
109
110 # This conditional is here because the iso team wants to be able to
111 # save dependencies after dispel (bug 8109), average users are expected
112 # to always run this code to remove old depends.
113 if [[ $NO_REAP_DEPENDS != "on" ]] ; then
114 remove_depends_status $DEPENDS_STATUS $SPELL
115 remove_sub_depends "$SUB_DEPENDS_STATUS" "$SPELL" ".*"
116 fi
117 }
118
119
120 #---------------------------------------------------------------------
121 ## @param file
122 ## @param file
123 ##
124 ## First argument is a file containing a list of files to reap.
125 ## Second argument is a file containing md5 sums of those files, used
126 ## to detect if a config file has been modified. Config files are any
127 ## files in /etc or any of its sub-directories.
128 ##
129 #---------------------------------------------------------------------
130 function reaper() {
131 # Example: reaper "$INSTALL_LOG" "$MD5_LOG"
132
133 debug "libdispel" "Running reaper() on $1"
134
135 if ! [ "$REAP" == "on" ] ||
136 ! [ -f $1 ]; then return
137 fi
138
139 local MD5S=$2
140
141 local UNIQUE="$$.$RANDOM"
142 local REAPER_FILES="$TMP_DIR/reaper.$UNIQUE.files"
143 local REAPER_DIRS="$TMP_DIR/reaper.$UNIQUE.dirs"
144 local REAPER_SYMS="$TMP_DIR/reaper.$UNIQUE.syms"
145 local REAPER_SPECIAL="$TMP_DIR/reaper.$UNIQUE.special"
146
147 rm -f $REAPER_FILES $REAPER_DIRS $REAPER_SYMS
148
149 # convert from TRACK_ROOT to / for protected filtering,
150 # then to INSTALL_ROOT.
151 # INSTALL_ROOT is relative to /, TRACK_ROOT is arbitrary.
152 seperate_state_files $1 /dev/stdout /dev/null |
153 log_adjuster /dev/stdin /dev/stdout log filterable |
154 filter_protected |
155 log_adjuster /dev/stdin /dev/stdout filterable root |
156 while read ITEM; do
157 if test -h "$ITEM"; then echo "$ITEM" >> $REAPER_SYMS
158 elif test -f "$ITEM"; then echo "$ITEM" >> $REAPER_FILES
159 elif test -d "$ITEM"; then echo "$ITEM" >> $REAPER_DIRS
160 # if it isn't a symlink, regular file or directory, assume it is a
161 # special file (character, block, or fifo)
162 elif test -e "$ITEM"; then echo "$ITEM" >> $REAPER_SPECIAL
163 fi
164 done
165
166 if test -f $REAPER_FILES ; then
167 sed "s:^$INSTALL_ROOT::" $REAPER_FILES |
168 grep -v /var/log/sorcery |
169 filter_configs -v |
170 sed "s:^:$INSTALL_ROOT:" |
171 reap_config_files
172
173 sed "s:^$INSTALL_ROOT::" $REAPER_FILES |
174 grep -v /var/log/sorcery |
175 filter_configs |
176 sed "s:^:$INSTALL_ROOT:" |
177 reap_regular_files
178 fi
179
180 [ -f $REAPER_SYMS ] && rm -f `cat $REAPER_SYMS` 2>/dev/null
181 [ -f $REAPER_SPECIAL ] && rm -f `cat $REAPER_SPECIAL` 2>/dev/null
182 [ -f $REAPER_DIRS ] && rmdir `sort -r $REAPER_DIRS` 2>/dev/null
183 [ -f $REAPER_FILES ] && rmdir `get_dirnames < $REAPER_FILES |uniq|sort -r` 2>/dev/null
184
185 rm -f $REAPER_FILES $REAPER_DIRS $REAPER_SYMS $REAPER_SPECIAL
186 }
187
188 #---------------------------------------------------------------------
189 ## @param file
190 ##
191 ## First argument is a file containing install log, removes state files
192 ##
193 #---------------------------------------------------------------------
194 function reap_state_files() {
195 # Example: reaper "$INSTALL_LOG" "$MD5_LOG"
196
197 debug "libdispel" "Running reap_state_files() on $1"
198
199 local UNIQUE="$$.$RANDOM"
200 local REAPER_FILES="$TMP_DIR/reaper.$UNIQUE.files"
201 local REAPER_DIRS="$TMP_DIR/reaper.$UNIQUE.dirs"
202 local REAPER_SYMS="$TMP_DIR/reaper.$UNIQUE.syms"
203
204 rm -f $REAPER_FILES $REAPER_DIRS $REAPER_SYMS
205
206 # convert from TRACK_ROOT to / for protected filtering,
207 # then to INSTALL_ROOT.
208 # INSTALL_ROOT is relative to /, TRACK_ROOT is arbitrary.
209 seperate_state_files $1 /dev/null /dev/stdout |
210 log_adjuster /dev/stdin /dev/stdout log root |
211 grep -v ${LOG_DIRECTORY#$STATE_ROOT} |
212 while read ITEM; do
213 if test -h "$ITEM"; then echo "$ITEM" >> $REAPER_SYMS
214 elif test -f "$ITEM"; then echo "$ITEM" >> $REAPER_FILES
215 elif test -d "$ITEM"; then echo "$ITEM" >> $REAPER_DIRS
216 fi
217 done
218
219 if test -f $REAPER_FILES ; then
220 cat $REAPER_FILES | xargs rm
221 fi
222
223 [ -f $REAPER_SYMS ] && rm -f `cat $REAPER_SYMS` 2>/dev/null
224 [ -f $REAPER_DIRS ] && rmdir `sort -r $REAPER_DIRS` 2>/dev/null
225 [ -f $REAPER_FILES ] && rmdir `get_dirnames < $REAPER_FILES |uniq|sort -r` 2>/dev/null
226
227 rm -f $REAPER_FILES $REAPER_DIRS $REAPER_SYMS
228
229 # bug 8826, if we dont explicitly remove the directory it can form
230 # a corrupt zombified tablet page
231 [[ "$TABLET_PAGE" ]] && test -d "$TABLET_PAGE" && rm -rf "$TABLET_PAGE"
232 return 0
233 }
234
235 #-----
236 ## Checks that a spell is indeed installed.
237 ## @return true Can be dispelled
238 ## @return false Cannot be dispelled
239 #-----
240 function dispel_not_possible() {
241
242 if ! spell_ok $SPELL ; then
243 message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
244 "${PROBLEM_COLOR}is not installed${DEFAULT_COLOR}."
245 else
246 false
247 fi
248
249 }
250
251 #-----
252 ## Does the sustained checks for spells
253 #-----
254 function dispel_sustained() {
255
256 if [ "$SUSTAIN" == "on" ] &&
257 grep -q "^$SPELL$" $SUSTAINED
258 then
259 message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
260 "${PROBLEM_COLOR}is sustained${DEFAULT_COLOR}."
261 else
262 false
263 fi
264
265 }
266
267 #-----
268 ## Find out where a spell is located
269 ## @Globals SPELL
270 #-----
271 function load_spell() {
272 local SPELL=$1
273 if tablet_set_spell $SPELL ; then
274 load_functions_file
275 else
276 SCRIPT_DIRECTORY="`codex_find_spell_by_name $SPELL`"
277
278 VERSION=`private_installed_version $SPELL`
279 INST_LOG=$INSTALL_LOGS/$SPELL-$VERSION
280 MD5_LOG=$MD5SUM_LOGS/$SPELL-$VERSION
281 if [[ $SCRIPT_DIRECTORY ]] ; then
282 codex_set_current_spell $SCRIPT_DIRECTORY
283 load_functions_file
284 else
285 message "Spell is missing from grimoires"
286 message "unable to run any PRE or POST_REMOVE scripts it once had."
287 if ! query "Continue anyway?" y; then
288 return 1
289 fi
290 fi
291 fi
292 }
293
294 #-----
295 ## Run the PRE_REMOVE script if it exists
296 #-----
297 function pre_remove() {
298 debug "libdispel" "pre_remove()"
299 if [[ $SCRIPT_DIRECTORY ]] ; then
300 run_spell_file PRE_REMOVE pre_remove
301 fi
302 return 0
303 }
304
305 #-----
306 ## Run the POST_REMOVE script if it exists.
307 #-----
308 function post_remove() {
309 debug "libdispel" "post_remove()"
310 if [[ $SCRIPT_DIRECTORY ]] ; then
311 run_spell_file POST_REMOVE post_remove
312 fi
313 return 0
314 }
315
316 #---------------------------------------------------------------------
317 ## Dispel a spell
318 #---------------------------------------------------------------------
319 function dispel_spell() {
320
321 local SPELL=$1
322 # fake dispel, only remove sorcery metadata
323 if [[ $DEBUG_DISPEL ]] ; then
324 echo "pretending to dispel $SPELL"
325 load_spell $SPELL &&
326 remove_spell $SPELL &&
327 reap_depends &&
328 message "${DISPEL_COLOR}Partly dispelled spell:" \
329 "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}"
330 return
331 fi
332
333 if dispel_sustained || dispel_not_possible ; then
334 DISPEL_EXIT_STATUS=1
335 return 1
336 fi
337
338 [[ $TRIGGER == off ]] || trigger "pre_dispel"
339
340 (
341 # this function messes with the environment a lot leave it in a subshell
342 load_spell $SPELL &&
343 pre_remove &&
344 reaper $INST_LOG $MD5_LOG &&
345 post_remove &&
346 reap_state_files $INST_LOG &&
347 reap_spell_config_stage &&
348 remove_spell $SPELL &&
349 remove_version_cache "$VERSION_STATUS" $SPELL &&
350 { [[ $TRIGGER == off ]] || trigger "dispel";} &&
351 remove_triggers $SPELL &&
352 { [[ $DEQUEUE == off ]] || pop_install_queue $SPELL; true; } &&
353 message "${DISPEL_COLOR}Dispelled spell:" \
354 "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" &&
355 activity_log "dispel" "$SPELL" "$VERSION" "success"
356 ) &&
357 reap_depends
358 local rc=$?
359 if [[ "$rc" != 0 ]] ; then
360 message "${PROBLEM_COLOR}Dispel of${DEFAULT_COLOR}" \
361 "${SPELL_COLOR}$SPELL${DEFAULT_COLOR}" \
362 "${PROBLEM_COLOR}failed!${DEFAULT_COLOR}"
363 return $rc
364 fi
365 return 0
366 }
367
368 #---------------------------------------------------------------------
369 ## @param default value
370 ## Set one of always, ask_yes, ask_no, and ignore to on based
371 ## on the value of default, leave the remaining as off.
372 #---------------------------------------------------------------------
373 function dispel_depends_value_to_items() {
374 always=off
375 ask_yes=off
376 ask_no=off
377 ignore=off
378 case $1 in
379 always) always=on ;;
380 ask-yes) ask_yes=on ;;
381 ask-no) ask_no=on ;;
382 ignore) ignore=on ;;
383 *) ignore=on ;;
384 esac
385 }
386
387
388 #---------------------------------------------------------------------
389 ## @param Title of the menu
390 ## @param Name of the parameter being set
391 ## @param Default value of the parameter being set
392 ##
393 ## Present a radiolist menu with the quad-options for one of various
394 ## dependency following options.
395 #---------------------------------------------------------------------
396 function dispel_depends_menu_template() {
397 local TITLE=$1
398 local DEFAULT_NAME=$2
399 local DEFAULT=$3
400 local always ask_yes ask_no ignore rc
401 dispel_depends_value_to_items $DEFAULT
402 RESULT=`eval $DIALOG ' --title "$TITLE" \
403 --radiolist \
404 "" \
405 0 0 0 \
406 always "" $always \
407 ask-yes "" $ask_yes \
408 ask-no "" $ask_no \
409 ignore "" $ignore'`
410 rc=$?
411 if [[ $rc == 0 ]] ; then
412 # remove spurious ""
413 RESULT=`echo "${RESULT}" | sed -e 's/^"//' -e 's/"$//'`
414 modify_local_config $DEFAULT_NAME "$RESULT"
415 eval "$DEFAULT_NAME=\"$RESULT\""
416 fi
417 }
418
419 #---------------------------------------------------------------------
420 ## Present menus for each of the four dependency following options.
421 #---------------------------------------------------------------------
422 function dispel_depends_defaults_menu() {
423 dispel_depends_menu_template \
424 "Default action for orphaned spells" \
425 "ORPHAN_MENU_DEFAULT" "$ORPHAN_MENU_DEFAULT"
426 dispel_depends_menu_template \
427 "Default action for non-orphaned spells" \
428 "NONORPHAN_MENU_DEFAULT" "$NONORPHAN_MENU_DEFAULT"
429 dispel_depends_menu_template \
430 "Default action for recasting repairable parents spells" \
431 "RECAST_PARENT_MENU_DEFAULT" "$RECAST_PARENT_MENU_DEFAULT"
432 dispel_depends_menu_template \
433 "Default action for broken parent spells" \
434 "DISPEL_PARENT_MENU_DEFAULT" "$DISPEL_PARENT_MENU_DEFAULT"
435
436 }
437
438 #---------------------------------------------------------------------
439 ##
440 ## This software is free software; you can redistribute it and/or modify
441 ## it under the terms of the GNU General Public License as published by
442 ## the Free Software Foundation; either version 2 of the License, or
443 ## (at your option) any later version.
444 ##
445 ## This software is distributed in the hope that it will be useful,
446 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
447 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
448 ## GNU General Public License for more details.
449 ##
450 ## You should have received a copy of the GNU General Public License
451 ## along with this software; if not, write to the Free Software
452 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
453 ##
454 #---------------------------------------------------------------------