]> git.somenet.org - root/pub/somesible.git/blob - roles/base/apt/files/default/ucf.bin
roles/service/nextcloud/defaults
[root/pub/somesible.git] / roles / base / apt / files / default / ucf.bin
1 #!/bin/sh
2 ################################################
3 ### Managed by someone's ansible provisioner ###
4 ################################################
5 # Part of: https://git.somenet.org/root/pub/somesible.git
6 # 2017-2025 by someone <someone@somenet.org>
7 #
8
9 # make sure we exit on error
10 set -e
11
12 # Sanitise environment
13 while read -r env ; do
14     case "$env" in
15         '') continue ;;
16         OPTIND) ;; # Dash croaks when unsetting OPTIND. See #985478
17         PATH|PWD|TERM) ;;
18         LANGUAGE|LANG|LC_*) ;;
19         DEB_*|DEBIAN_*|DEBCONF_*) ;;
20         DPKG_FORCE) ;;
21         UCF_*) ;;
22         *) unset "$env" ;;
23     esac
24 done<<EOF
25 $(awk 'BEGIN{for(v in ENVIRON) print v}')
26 EOF
27
28 # set the version and revision
29 progname="$(basename "$0")"
30 pversion='3.0052'
31
32 ######################################################################
33 ########                                                     #########
34 ########              Utility functions                      #########
35 ########                                                     #########
36 ######################################################################
37 for libdir in "$(dirname "$0")" /usr/share/ucf ; do
38     [ -f "${libdir}/ucf_library.sh" ] && \
39         . "${libdir}/ucf_library.sh" && \
40         break
41 done
42
43 # Usage: get_file_metadate file_name
44 get_file_metadata()
45 {
46     if [ -e "$1" ]; then
47         # get file modification date without the nanoseconds and timezone info
48         moddate="$(date +"%F %T" --date "$(stat --format '@%Y' "$1")")"
49         # print file_name user.group permissions above_date
50         stat --format "%n %U.%G 0%a $moddate" "$1"
51     else
52         echo "/dev/null"
53     fi
54 }
55
56 # Runs the diff command with approrpiate arguments
57 # Usage run_diff diff|sdiff diff_opts old_file new_file
58 run_diff()
59 {
60     local diff_cmd diff_opt old_file new_file old_file_label new_file_label out
61     diff_cmd="$1"
62     diff_opt="$2"
63     old_file="$3"
64     new_file="$4"
65
66     old_file_label="$(get_file_metadata "$old_file")"
67     new_file_label="$(get_file_metadata "$new_file")"
68
69     [ -e "$old_file" ] || old_file=/dev/null
70     [ -e "$new_file" ] || new_file=/dev/null
71
72     if [ "$diff_cmd" = "diff" ] ; then
73       diff "$diff_opt" --label "$old_file_label" "$old_file" \
74             --label "$new_file_label" "$new_file" || true
75     elif [ "$diff_cmd" = "sdiff" ] ; then
76       # unfortunatelly the sdiff command does not support --label option
77       out="$(sdiff "$diff_opt" "$old_file" "$new_file")" || true
78       [ -z "$out" ] || printf "Old file: %s\nNew file: %s\n\n%s" \
79                                "$old_file_label" "$new_file_label" "$out"
80     else
81       echo "Unknown diff command: $diff_cmd" >&2
82       exit 1
83     fi
84 }
85
86 # Use debconf to show the differences
87 # Usage: show_diff actual_file_differences file_stat_differences
88 show_diff() {
89     if [ -z "$1" ]; then
90         DIFF="There are no non-white space differences in the files."
91     else
92         if  [ 99999 -lt "$(echo "$1" | wc -c | awk '{print $1; }')" ]; then
93             DIFF="The differences between the files are too large to display."
94         else
95             # Try to convert non-breaking space to current locale
96             nbsp=$(printf '\302\240' | iconv -c --from-code=UTF-8 --to-code=//TRANSLIT)
97             DIFF="$(printf "%s" "$1" | sed "s/ /${nbsp:- }/g" | debconf-escape -e )"
98         fi
99     fi
100     if [ "$DEBCONF_OK" = "YES" ] && [ "$DEBIAN_HAS_FRONTEND" ]; then
101         templ=ucf/show_diff
102         db_capb escape
103         db_subst $templ DIFF "$DIFF"
104         db_fset $templ seen false
105         db_input critical $templ || true
106         db_go || true
107         db_get $templ
108         # may contain sensitive information, so clear
109         # immediatly after use so it is never written
110         # to disk
111         db_subst $templ DIFF ""
112         db_reset $templ
113         db_capb
114     else
115         if [ -z "$my_pager" ]; then
116             echo "$DIFF" | sensible-pager
117         else
118             echo "$DIFF" | $my_pager
119         fi
120     fi
121 }
122
123 usageversion () {
124         cat >&2 <<END
125 Debian GNU/Linux $progname $pversion.
126            Copyright (C) 2002-2020 Manoj Srivastava.
127            Copyright (C) 2024- Mark Hindley.
128 This is free software; see the GNU General Public Licence for copying
129 conditions.  There is NO warranty.
130
131 Usage: $progname  [options] new_file  destination
132 Options:
133      -h,     --help          print this message
134      -s foo, --src-dir  foo  Set the src dir (historical md5sums live here)
135              --sum-file bar  Force the historical md5sums to be read from
136                              this file.  Overrides any setting of --src-dir.
137      -d[n], --debug=[n]      Set the Debug level to N. Please note there must
138                              be no spaces before the debug level
139      -n,     --no-action     Dry run. No action is actually taken.
140      -P foo, --package foo   Don't follow dpkg-divert diversions by package foo.
141      -v,     --verbose       Make the script verbose
142              --three-way     Register this file in the cache, and turn on the
143                              diff3 option allowing the merging of maintainer
144                              changes into a (potentially modified) local
145                              configuration file. )
146              --state-dir bar Set the state directory to bar instead of the
147                              default '/var/lib/ucf'. Used mostly for testing.
148              --debconf-ok    Indicate that it is ok for ucf to use an already
149                              running debconf instance for prompting.
150              --debconf-template bar
151                              Specify an alternate, caller-provided debconf
152                              template to use for prompting.
153 Usage: $progname  -p  destination
154      -p,     --purge         Remove any reference to destination from records
155
156 By default, the directory the new_file lives in is assumed to be the src-dir,
157 which is where we look for any historical md5sums.
158
159 END
160
161 }
162
163 ######################################################################
164 ########                                                     #########
165 ########        file and hash save/restore functions         #########
166 ########                                                     #########
167 ######################################################################
168 purge_md5sum () {
169     for i in $(/usr/bin/seq 6 -1 0); do
170         if [ -e "${statedir}/hashfile.${i}" ]; then
171             if [ "$docmd" = "YES" ]; then
172                 cp -pf "${statedir}/hashfile.${i}" \
173                     "${statedir}/hashfile.$(($i+1))"
174             else
175                 echo cp -pf "${statedir}/hashfile.${i}" \
176                           "${statedir}/hashfile.$(($i+1))"
177             fi
178         fi
179     done
180     if [ -e "$statedir/hashfile" ]; then
181         if [ "$docmd" = "YES" ]; then
182             cp -pf "$statedir/hashfile"  "$statedir/hashfile.0"
183         else
184             echo cp -pf "$statedir/hashfile"  "$statedir/hashfile.0"
185         fi
186         if [ "$docmd" = "YES" ]; then
187             if [ "$VERBOSE" ]; then
188                 echo >&2 "grep -v ${dest_file_bre} $statedir/hashfile"
189                 grep -v "${dest_file_bre}"  "$statedir/hashfile" >&2 \
190                     || true
191             fi
192             grep -v "${dest_file_bre}" "$statedir/hashfile" > \
193                 "$statedir/hashfile.tmp" || true
194             mv -f "$statedir/hashfile.tmp"  "$statedir/hashfile"
195         fi
196     fi
197     [ "$VERBOSE" ] && echo >&2 "The cache file is $cached_file"
198     if [ "$cached_file" ] && [ -f "$statedir/cache/$cached_file" ]; then
199         $action rm -f "$statedir/cache/$cached_file"
200     fi
201 }
202
203 replace_md5sum () {
204     for i in $(/usr/bin/seq 6 -1 0); do
205         if [ -e "${statedir}/hashfile.${i}" ]; then
206             if [ "$docmd" = "YES" ]; then
207                 cp -pf "${statedir}/hashfile.${i}" \
208                     "${statedir}/hashfile.$(($i+1))"
209             else
210                 echo cp -pf "${statedir}/hashfile.${i}" \
211                     "${statedir}/hashfile.$(($i+1))"
212             fi
213         fi
214     done
215     if [ -e "$statedir/hashfile" ]; then
216         if [ "$docmd" = "YES" ]; then
217             cp -pf "$statedir/hashfile"  "$statedir/hashfile.0"
218         else
219             echo cp -pf "$statedir/hashfile"  "$statedir/hashfile.0"
220         fi
221         if [ "$docmd" = "YES" ]; then
222             if [ "$VERBOSE" ]; then
223                 echo >&2 "grep -v \"${dest_file_bre}\" \"$statedir/hashfile\""
224                 grep -v "${dest_file_bre}" "$statedir/hashfile" >&2 || true
225                 md5sum "$orig_new_file" | sed "s|$orig_new_file|$dest_file|" >&2
226             fi
227             grep -v "${dest_file_bre}" "$statedir/hashfile" > \
228                 "$statedir/hashfile.tmp" || true
229             md5sum "$orig_new_file" | sed "s|$orig_new_file|$dest_file|" >> \
230                 "$statedir/hashfile.tmp"
231             mv -f "$statedir/hashfile.tmp"  "$statedir/hashfile"
232         else
233             echo "(grep -v \"${dest_file_bre}\" \"$statedir/hashfile\""
234             echo " md5sum \"$orig_new_file\" | sed \"s|$orig_new_file|$dest_file|\"; "
235             echo ") | sort > \"$statedir/hashfile\""
236         fi
237     else
238         if [ "$docmd" = "YES" ]; then
239             md5sum "$orig_new_file" | sed "s|$orig_new_file|$dest_file|"  > \
240                 "$statedir/hashfile"
241         else
242             echo " md5sum \"$orig_new_file\" | sed \"s|$orig_new_file|$dest_file|\" >" \
243                 "\"$statedir/hashfile\""
244         fi
245     fi
246     file_size=$(stat -c '%s' "$orig_new_file")
247     if [ "$THREEWAY" ] || [ "$file_size" -lt 25600 ]; then
248         $action cp -pf "$orig_new_file" "$statedir/cache/$cached_file"
249     fi
250     # cp -pf "$orig_new_file" "$dest_file.${DIST_SUFFIX}"
251 }
252
253 replace_conf_file () {
254     # do not mangle $dest_file since it's the one registered in the hashfile
255     # or we have been ask to register
256     real_file="$dest_file"
257     if [ -L "$dest_file" ]; then
258         real_file="$(readlink -nf "$dest_file" || :)"
259         if [ ! "$real_file" ]; then
260             echo >&2 "$dest_file is a broken symlink!"
261             $action rm -f "$dest_file"
262             real_file="$dest_file"
263         fi
264     fi
265     if [ -e "$real_file" ]; then
266         if [ -z "$RETAIN_OLD" ]; then
267             #echo "Saving  ${real_file}.${OLD_SUFFIX},  in case."
268             [ "$VERBOSE" ] &&
269                 echo >&2 "Not saving ${real_file}, since it was unmodified"
270         else
271             $action cp -pf $selinux "${real_file}" "${real_file}.${OLD_SUFFIX}"
272         fi
273     fi
274     if [ -e "${real_file}" ]; then
275         # Do not change the permissions and attributes of the destination
276         $action cp -f $selinux "$new_file" "${real_file}"
277     else
278         # No destination file exists
279         $action cp -pf $selinux "$new_file" "${real_file}"
280     fi
281     replace_md5sum
282 }
283
284 ######################################################################
285 ########                                                     #########
286 ########              Command line args                      #########
287 ########                                                     #########
288 ######################################################################
289 #
290 # Long term variables
291 #
292 docmd='YES'
293 # action='withecho'
294 action=
295 selinux=''
296 DEBUG=0
297 VERBOSE=''
298 statedir='/var/lib/ucf'
299 THREEWAY=
300 DIST_SUFFIX="ucf-dist"
301 NEW_SUFFIX="ucf-new"
302 OLD_SUFFIX="ucf-old"
303 ERR_SUFFIX="merge-error"
304
305 handle_file_args() {
306     if [ "$PURGE" = "YES" ]; then
307         if [ $# -ne 1 ]; then
308             echo >&2 "*** ERROR: Need exactly one file argument when purging, got $#"
309             echo >&2 ""
310             usageversion
311             exit 2
312         fi
313         temp_dest_file="$1"
314         if [ -e "$temp_dest_file" ]; then
315             dest_file=$(vset "$(readlink -q -m "$temp_dest_file")" "The Destination file")
316         else
317             dest_file=$(vset "$temp_dest_file" "The Destination file")
318         fi
319     else
320         if [ $# -ne 2 ]; then
321             echo >&2 "*** ERROR: Need exactly two file arguments, got $#"
322             echo >&2 ""
323             usageversion
324             exit 2
325         fi
326         temp_new_file="$1"
327         temp_dest_file="$2"
328
329         if [ ! -e "${temp_new_file}" ]; then
330             echo >&2 "Error: The new file ${temp_new_file} does not exist!"
331             exit 1
332         fi
333         new_file=$(vset "$(readlink -q -m "$temp_new_file")"  "The new file")
334         if [ -e "$temp_dest_file" ]; then
335             dest_file=$(vset "$(readlink -q -m "$temp_dest_file")" "The Destination file")
336         else
337             dest_file=$(vset "$temp_dest_file" "The Destination file")
338         fi
339     fi
340 }
341
342 handle_opts() {
343     # Arguments are from getopt(1) in quoted mode.
344     eval set --  "$*"
345     while [ $# -gt 0 ] ; do
346         case "$1" in
347             -h|--help) usageversion;                        exit 0 ;;
348             -n|--no-action) action='echo'; docmd='NO';      shift  ;;
349             -v|--verbose) VERBOSE=1;                        shift  ;;
350             -P|--package)
351                 opt_package="$2";                              shift 2 ;;
352             -s|--src-dir)
353                 opt_source_dir="$2";                       shift 2 ;;
354             --sum-file)
355                 opt_old_mdsum_file="$2";                  shift 2 ;;
356             --state-dir)
357                 opt_state_dir="$2";                        shift 2 ;;
358             --debconf-template)
359                 override_template="$2";                    shift 2 ;;
360             -D|-d|--debug|--DEBUG)
361                 # d has an optional argument. As we are in quoted mode,
362                 # an empty parameter will be generated if its optional
363                 # argument is not found.
364                 case "$2" in
365                     "") DEBUG=$(vset 1 "The Debug value");      shift 2 ;;
366                     *)  DEBUG=$(vset "$2" "The Debug value"); shift 2 ;;
367                 esac ;;
368             -p|--purge) PURGE=YES;                         shift   ;;
369             --three-way) THREEWAY=YES;                       shift   ;;
370             --debconf-ok) DEBCONF_OK=YES;                    shift   ;;
371             -Z) selinux='-Z';                                shift   ;;
372             --)  shift ; handle_file_args "$@";            return   ;;
373             *) echo >&2 "Internal error!" ; exit 1 ;;
374         esac
375     done
376 }
377
378 # A separate assignment is essential to ensure getopt(1) error status isn't
379 # lost.
380 UCF_OPTS="$(getopt -a -o hs:d::D::npP:Zv -n "$progname" \
381                      --long help,src-dir:,sum-file:,dest-dir:,debug::,DEBUG::,no-action,package:,purge,verbose,three-way,debconf-ok,debconf-template:,state-dir: \
382                      -- "$@")"
383
384 handle_opts "$UCF_OPTS"
385
386 ######################################################################
387 ########                                                     #########
388 ########              Sanity checking                        #########
389 ########                                                     #########
390 ######################################################################
391 # Need to run as root, or else the
392 if test "$(id -u)" != 0; then
393     if [ "$docmd" = "YES" ]; then
394         echo "$progname: Need to be run as root." >&2
395         echo "$progname: Setting up no action mode." >&2
396         action='echo'; docmd='NO'
397     fi
398 fi
399
400 # Follow dpkg-divert as though we are installed as part of $opt_package
401 divert_line=$(dpkg-divert --listpackage "$dest_file")
402 if [ -n "$divert_line" ]; then
403    # name of the package or 'LOCAL' for a local diversion
404    divert_package="$divert_line"
405
406    if [ "$divert_package" != "$opt_package" ]; then
407        dest_file=$(dpkg-divert --truename "$dest_file")
408    fi
409 fi
410 dest_file_bre="[[:space:]]$(escape_bre "$dest_file")"'$'
411
412 ######################################################################
413 ########                                                     #########
414 ########              Set Default Values                     #########
415 ########                                                     #########
416 ######################################################################
417 # Load site defaults and overrides.
418 if [ -f /etc/ucf.conf ]; then
419     . /etc/ucf.conf
420 fi
421
422 # Command line, env variable, config file, or default
423 if [ "$opt_source_dir" ]; then
424     source_dir=$(vset "$opt_source_dir" "The Source directory")
425 elif [ "$UCF_SOURCE_DIR" ]; then
426     source_dir=$(vset "$UCF_SOURCE_DIR" "The Source directory")
427 elif [ "$conf_source_dir" ]; then
428     source_dir=$(vset "$conf_source_dir" "The Source directory")
429 else
430     if [ "$new_file" ]; then
431         source_dir=$(vset "$(dirname "$new_file")" "The Source directory")
432     else
433         source_dir=$(vset "/tmp" "The Source directory")
434     fi
435
436 fi
437
438 if [ "$PAGER" ] && which "$PAGER" >/dev/null 2>&1 ; then
439     my_pager="$(which "$PAGER")"
440 elif [ -s /usr/bin/pager ] &&
441      [ "$(readlink -e /usr/bin/pager || :)" ]; then
442     my_pager=/usr/bin/pager
443 elif [ -x /usr/bin/sensible-pager ]; then
444     my_pager=/usr/bin/sensible-pager
445 elif [ -x /bin/more ]; then
446     my_pager=/bin/more
447 else
448     my_pager=
449 fi
450
451 # Command line, env variable, config file, or default
452 if [ "$opt_state_dir" ]; then
453     statedir=$(vset "$opt_state_dir" "The State directory")
454 elif [ "$UCF_STATE_DIR" ]; then
455     statedir=$(vset "$UCF_STATE_DIR" "The State directory")
456 elif [ "$conf_state_dir" ]; then
457     statedir=$(vset "$conf_state_dir" "The State directory")
458 else
459     statedir=$(vset '/var/lib/ucf'  "The State directory")
460 fi
461
462 # Handle conffold and conffnew together. See #980996
463 # Default
464 force_conff=
465
466 # Config file
467 if [ "$conf_force_conffold" ]; then
468     if [ "$conf_force_conffnew" ]; then
469         echo >&2 "Error: Only one of conf_force_conffold and conf_force_conffnew should"
470         echo >&2 "       be set in the config file"
471         exit 1
472     fi
473     force_conff=$(vset "old" "Force keep file")
474 elif [ "$conf_force_conffnew" ]; then
475     force_conff=$(vset "new" "Force keep file")
476 fi
477
478 # Environment
479 if [ "$UCF_FORCE_CONFFOLD" ]; then
480     if [ "$UCF_FORCE_CONFFNEW" ]; then
481         echo >&2 "Error: Only one of UCF_FORCE_CONFFOLD and UCF_FORCE_CONFFNEW should"
482         echo >&2 "       be set in the environment"
483         exit 1
484     fi
485     force_conff=$(vset "old" "Force keep file")
486 elif [ "$UCF_FORCE_CONFFNEW" ]; then
487     force_conff=$(vset "new" "Force keep file")
488 fi
489
490 # Env variable, config file, or default
491 if [ "$UCF_FORCE_CONFFMISS" ]; then
492     force_conffmiss=$(vset "$UCF_FORCE_CONFFMISS" "Replace any missing files")
493 elif [ "$conf_force_conffmiss" ]; then
494     force_conffmiss=$(vset "$conf_force_conffmiss" "Replace any missing files")
495 else
496     force_conffmiss=''
497 fi
498
499 # DPKG_FORCE overrides all others: see #925375.
500 IFS=,
501 for f in $DPKG_FORCE ; do
502     case $f in
503         confmiss) force_conffmiss=$(vset 1 "DPKG_FORCE replace missing files") ;;
504         confold) force_conff=$(vset "old" "DPKG_FORCE keep file") ;;
505         confnew) force_conff=$(vset "new" "DPKG_FORCE keep file") ;;
506         # The following override other settings, so break out of loop.
507         confdef) unset force_conff
508                  [ "$VERBOSE" ] && echo >&2 "DPKG_FORCE keep file default"
509                  break ;;
510         confask) force_conff=$(vset "ask" "DPKG_FORCE keep file"); break ;;
511     esac
512 done
513 unset IFS
514
515 if [ -n "$opt_old_mdsum_file" ]; then
516     old_mdsum_file=$(vset "$opt_old_mdsum_file" "The md5sum is found here")
517 elif [ "$UCF_OLD_MDSUM_FILE" ]; then
518     old_mdsum_file=$(vset "$UCF_OLD_MDSUM_FILE" "The md5sum is found here")
519 elif [ "$conf_old_mdsum_file" ]; then
520     old_mdsum_file=$(vset "$conf_old_mdsum_file" "Replace the old file")
521 elif [ "${new_file}" ]; then
522     old_mdsum_file="$source_dir/$(basename "${new_file}").md5sum"
523 else
524     old_mdsum_file=""
525 fi
526
527 ######################################################################
528 ########                                                     #########
529 ########               More Sanity checking                  #########
530 ########                                                     #########
531 ######################################################################
532
533 # VERBOSE of 0 is supposed to be the same as not setting VERBOSE
534 if [ "$VERBOSE" = "0" ]; then
535     VERBOSE=''
536 fi
537
538 if [ -e "$statedir/hashfile" ] && [ ! -w "$statedir/hashfile" ]; then
539     echo >&2 "ucf: do not have write privilege to the state data"
540     if [ "$docmd" = "YES" ]; then
541         exit 1
542     fi
543 fi
544
545 if [ ! -d "$statedir/cache" ]; then
546     $action mkdir -p "$statedir/cache"
547 fi
548
549 # test and see if this file exists in the database
550 if [ -e "$statedir/hashfile" ]; then
551     if [ "$VERBOSE" ]; then
552         echo >&2 "The hash file exists"
553         echo >&2 "grep ${dest_file_bre} $statedir/hashfile"
554         grep "${dest_file_bre}" "$statedir/hashfile" >&2 || true
555     fi
556     lastsum=$(grep "${dest_file_bre}" "$statedir/hashfile" | \
557                    awk '{print $1;}' )
558 fi
559
560 if [ "${new_file}" ]; then
561     old_mdsum_dir="$source_dir/$(basename "${new_file}").md5sum.d"
562 else
563     old_mdsum_dir=""
564 fi
565
566 cached_file="$(echo "$dest_file" | tr / :)"
567 ######################################################################
568 ########                                                     #########
569 ########                  Debugging dump                     #########
570 ########                                                     #########
571 ######################################################################
572
573 if [ "$DEBUG" -gt 0 ]; then
574     cat >&2 <<EOF
575 The new start file is      \`$new_file\'
576 The destination is         \`$dest_file\'  (\`$dest_file_bre\')
577 The history is kept under  \'$source_dir\'
578 The file may be cached at \'$statedir/cache/$cached_file\'
579 EOF
580     if [ -s "$dest_file" ]; then
581         echo "The destination file exists, and has md5sum:"
582         md5sum "$dest_file"
583     else
584         echo "The destination file does not exist."
585     fi
586     if [ "$lastsum" ]; then
587         echo "The old md5sum exists, and is:"
588         echo "$lastsum"
589     else
590         echo "The old md5sum does not exist."
591         if [ -d "$old_mdsum_dir" ] || [ -f "$old_mdsum_file" ]; then
592             echo "However, there are historical md5sums around."
593         fi
594     fi
595     if [ -e "$new_file" ]; then
596         echo "The new file exists, and has md5sum:"
597         md5sum "$new_file"
598     else
599         echo "The new file does not exist."
600     fi
601     if [ -d "$old_mdsum_dir" ]; then
602         echo "The historical md5sum dir $old_mdsum_dir exists"
603     elif [ -f "$old_mdsum_file" ]; then
604         echo "The historical md5sum file $old_mdsum_file exists"
605     else
606         echo "Historical md5sums are not available"
607     fi
608 fi
609
610 ######################################################################
611 ########                                                     #########
612 ########        Short circuit if we are purging              #########
613 ########                                                     #########
614 ######################################################################
615
616 if [ "$PURGE" = "YES" ]; then
617     [ "$VERBOSE" ] &&
618         echo >&2 "Preparing to purge ${dest_file}"
619     purge_md5sum
620     exit 0
621 fi
622
623 ######################################################################
624 ########                                                     #########
625 ########                  DebConf stuff                      #########
626 ########                                                     #########
627 ######################################################################
628
629 # Is debconf already running? Kinda tricky, because it will be after the
630 # confmodule is sourced, so only test before that.
631 if [ -z "$DEBCONF_ALREADY_RUNNING" ]; then
632     if [ "$DEBIAN_HAS_FRONTEND" ]; then
633         DEBCONF_ALREADY_RUNNING='YES'
634     else
635         DEBCONF_ALREADY_RUNNING='NO'
636     fi
637 fi
638
639 export DEBCONF_ALREADY_RUNNING
640
641 if [ -z "$DEBCONF_OK" ]; then
642     if [ "$DEBCONF_ALREADY_RUNNING" = 'YES' ]; then
643         DEBCONF_OK='NO'
644     else
645         DEBCONF_OK='YES'
646     fi
647 fi
648
649 # Time to start nagging the users who call ucf without debconf-ok
650 if [ "$DEBCONF_ALREADY_RUNNING"  = 'YES' ] && [ "$DEBCONF_OK" = NO ]; then
651         # Commented out for now, uncomment after a while to begin nagging
652         # maintainers to fix their scripts.
653         cat \
654 <<END
655 *** WARNING: ucf was run from a maintainer script that uses debconf, but
656              the script did not pass --debconf-ok to ucf. The maintainer
657              script should be fixed to not stop debconf before calling ucf,
658              and pass it this parameter. For now, ucf will revert to using
659              old-style, non-debconf prompting. Ugh!
660
661              Please inform the package maintainer about this problem.
662 END
663 fi
664
665 # Start up debconf or at least get the db_* commands available
666 # Don't check debconf with shellcheck.
667 # shellcheck source=/dev/null
668 if [ -e /usr/share/debconf/confmodule ]; then
669     if test "$(id -u)" = 0; then
670         . /usr/share/debconf/confmodule
671
672         # Load our templates, just in case our template has
673         # not been loaded or the Debconf DB lost or corrupted
674         # since then, but only if it is OK to use debconf.
675         if [ "$DEBCONF_OK" = 'YES' ]; then
676             db_x_loadtemplatefile "$(dpkg-query --control-path ucf templates)" ucf
677         fi
678     else
679         echo >&2 "$progname: Not loading confmodule, since we are not running as root."
680     fi
681     # Only set the title if debconf was not already running.
682     # If it was running, then we do not want to clobber the
683     # title used for configuring the whole package with debconf.
684     if [ "$DEBCONF_ALREADY_RUNNING" = 'NO' ]; then
685         if ! db_settitle ucf/title 2>/dev/null; then
686             # Older debconf that does not support that command.
687             if test "$(id -u)" = 0; then
688                 db_title "Modified configuration file"
689             else
690                 echo >&2 "$progname: Not changing title, since we are not running as root."
691             fi
692         fi
693     fi
694 fi
695
696 ######################################################################
697 ########                                                     #########
698 ########                Start Processing                     #########
699 ########                                                     #########
700 ######################################################################
701
702 orig_new_file="$new_file"       # Since sometimes we replace the newfile below
703 newsum=$(md5sum "$new_file" | awk '{print $1}')
704
705 ######################################################################
706 ########                                                     #########
707 ########               Do the replacement                    #########
708 ########                                                     #########
709 ######################################################################
710 # Step 1: If we have no record of this file, and dest file
711 #         does, We need to determine how to initialize the
712 #         ${old_mdsum_prefix}.old file..
713 if [ -e "$dest_file" ]; then
714     destsum=$(md5sum "$dest_file"  | awk '{print $1}')
715     if [ ! "$lastsum" ]; then
716 #      a: If we have a directory containing historical md5sums of this
717 #         file in question, we should look and see if the currently
718 #         installed file matches any of the old md5sums; in which case
719 #         it can be silently replaced.
720         if [ -d "$old_mdsum_dir" ] || [ -f "$old_mdsum_file" ]; then
721             if [ -d "$old_mdsum_dir"  ]; then
722                 for file in "${old_mdsum_dir}/"*; do
723                     oldsum="$(awk '{print $1}' "$file")"
724                     if [ "$oldsum" = "$destsum"  ]; then
725                         if [ "$force_conff" != "old" ]; then
726 #                           Bingo! replace, set the md5sum, and we are done
727                             [ "$VERBOSE" ] &&
728                                 echo >&2 \
729                                     "Replacing config file $dest_file with new version"
730                             replace_conf_file
731                             exit 0
732                         else
733                             replace_md5sum
734                             cp -pf "$orig_new_file" "$dest_file.${DIST_SUFFIX}"
735                             exit 0
736                         fi
737                     fi
738                 done
739             elif [ -f "$old_mdsum_file" ]; then
740                 oldsum=$(grep -E "^${destsum}" "$old_mdsum_file" || true)
741                 if [ "$oldsum" ]; then
742 #                    Bingo
743                     if [ "$force_conff" != old ]; then
744                         [ "$VERBOSE" ] &&
745                             echo >&2 \
746                                 "Replacing config file $dest_file with new version"
747                         replace_conf_file
748                         exit 0
749                     else
750                         replace_md5sum
751                         cp -pf "$orig_new_file" "$dest_file.${DIST_SUFFIX}"
752                         exit 0
753                     fi
754                 fi
755             fi
756 #          Well, nothing matched. We now check to see if the
757 #          maintainer has an opinion on how to set the ``md5sum of the
758 #          previously installed version'', since we have no way of
759 #          determining that automatically. Please note that unless
760 #          there are limited number of previously released packages
761 #          (like just one), the maintainer is also making a guess at
762 #          this point by supplying a historical md5sum default file.
763             [ "$VERBOSE" ] &&
764                 echo >&2 "Historical md5sums did not match."
765             if [ -d "$old_mdsum_dir"  ]; then
766                 if [ -e "${old_mdsum_dir}/default" ]; then
767                     [ "$VERBOSE" ] &&
768                         echo >&2 "However, a default entry exists, using it."
769                     lastsum="$(awk '{print $1;}' "${old_mdsum_dir}"/default)"
770                     do_replace_md5sum=1
771                 fi
772             elif [ -f "$old_mdsum_file" ]; then
773                 oldsum=$(grep -E "[[:space:]]default$" "$old_mdsum_file" | \
774                     awk '{print $1;}')
775                 if [ "$oldsum" ]; then
776 #                   Bingo
777                     lastsum=$oldsum
778                     do_replace_md5sum=1
779                 fi
780             fi
781         fi
782
783 #       At this point, we are almost certain that either the
784 #       historical record of md5sums is not complete, or the user has
785 #       changed the configuration file. Rather than guessing and
786 #       chosing one of the historical md5sums, we fall through to the
787 #       solution used if there had been no historical md5sums
788 #       directory/file.
789         if [ ! "$lastsum" ]; then
790 #       b: We do not have a historical list of md5sums, or none
791 #          matched, and we still need to initialize the
792 #          ${old_mdsum_prefix}.old file. We can't determine whther or
793 #          not they made any changes, so we err on the side of caution
794 #          and ask.
795             [ "$VERBOSE" ] &&
796                 echo >&2 "No match found, we shall ask."
797             lastsum='AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
798         fi # the old md5sum file does not exist, and the historical
799            # record failed
800     fi # the old md5sum file does not exist (bug))
801 else  # "$dest_file" does not exist
802 # Step 2: If destfile does not exist, create it, set the file
803 #         "${old_mdsum_prefix}.old" to the md5sum of the new file, and we
804 #         are done
805     if [ ! "$lastsum" ]; then
806         # Ok, so there is no indication that the package was ever
807         # installed on this machine.
808         echo >&2 "Creating config file $dest_file with new version"
809         replace_conf_file
810         exit 0
811     elif [ "$lastsum" = "$newsum" ]; then
812         # OK, new version of the file is the same as the last version
813         # we saw. Since the user apparently has deleted the file,
814         # nothing needs be done, unless we have been told differently
815         if [ "$force_conffmiss" ]; then
816             echo >&2 "Recreating deleted config file $dest_file with new version, as asked"
817             replace_conf_file
818             exit 0
819         else
820             echo >&2 "Not replacing deleted config file $dest_file"
821         fi
822
823     else
824         # OK. New upstream version.
825         if [ "$force_conffmiss" ]; then
826             # User has said to replace missing files, so we do so, no
827             # questions asked.
828             echo >&2 "Recreating deleted config file $dest_file with new version, as asked"
829             replace_conf_file
830             exit 0
831         else
832             # Even though the user has deleted this file, they should
833             # be asked now, unless specified otherwise.
834             if [ "$force_conff" != "old" ]; then
835                 destsum='AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
836             else
837                 exit 0
838             fi
839         fi
840     fi
841 fi
842
843 # Here, the destfile exists.
844
845 # step 3: If the old md5sum and the md5sum of the new file
846 #         do not match, we need to take action.
847 if [ "$lastsum" = "$newsum" ]; then
848     if [ "$force_conff" = 'ask' ]; then
849         [ "$VERBOSE" ] &&
850             echo >&2 "Forced to ask even though md5sums match."
851         destsum='AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
852     else
853         [ "$VERBOSE" ] &&
854             echo >&2 "md5sums match, nothing needs be done."
855         if [ "$do_replace_md5sum" ]; then
856             replace_md5sum
857         fi
858         exit 0;                 # Hah. Match. We are done.
859     fi
860 fi
861 #      a: If the md5sum of the dest file is the same as lastsum, replace the
862 #         destfile, saying we are replacing old config files
863 if [ "$destsum" = "$lastsum" ]; then
864     if [ "$force_conff" != old ]; then
865         echo >&2 "Replacing config file $dest_file with new version"
866         replace_conf_file
867         exit 0
868     else
869         replace_md5sum
870         cp -pf "$orig_new_file" "$dest_file.${DIST_SUFFIX}"
871         exit 0
872     fi
873 else
874 #      b: If the md5sum of the dest file differs from lastsum, we need to ask
875 #         the user what action to take.
876     if [ "$force_conff" = "new" ]; then
877         echo >&2 "Replacing config file $dest_file with new version"
878         echo >&2 "since you asked for it."
879         if [ "$destsum" = "$newsum" ]; then
880             echo >&2 "The new and the old files are identical, AFAICS"
881         else
882             echo >&2 "The new and the old files are different"
883         fi
884         replace_conf_file
885         exit 0
886     fi
887     if [ "$force_conff" = "old" ]; then
888         replace_md5sum
889         cp -pf "$orig_new_file" "$dest_file.${DIST_SUFFIX}"
890         exit 0
891     fi
892 #      c: If the destination file is the same as the new maintianer provided one,
893 #         we need do nothing.
894     if [ "$newsum" = "$destsum" ]; then
895         [ "$VERBOSE" ] &&
896             echo >&2 "md5sums of the file in place matches, nothing needs be done."
897         replace_md5sum
898         exit 0;                 # Hah. Match. We are done.
899     fi
900
901     done='NO'
902     while [ "$done" = "NO" ]; do
903         if [ "$DEBCONF_OK" = "YES" ] && [ "$DEBIAN_HAS_FRONTEND" ]; then
904                 # Use debconf to prompt.
905                 if [ -e "$statedir/cache/$cached_file" ] && [ "$THREEWAY" ]; then
906                         templ=ucf/changeprompt_threeway
907                 else
908                         templ=ucf/changeprompt
909                 fi
910                 if [ "$override_template" ]; then
911                         choices="$(db_metaget $templ Choices-C)"
912                         choices2="$(db_metaget "$override_template" Choices-C)"
913                         if [ "$choices" = "$choices2" ]; then
914                                 templ=$override_template
915                         fi
916                 fi
917                 db_fset "$templ" seen false
918                 db_reset "$templ"
919                 db_subst "$templ" FILE "$dest_file"
920                 db_subst "$templ" NEW  "$new_file"
921                 db_subst "$templ" BASENAME "$(basename "$dest_file")"
922                 db_input critical "$templ" || true
923                 if ! db_go; then
924                         # The current ucf interface does not provide a way for it
925                         # to tell its caller that the user chose to back up.
926                         # However, we could get here, if the caller turned on
927                         # debconf's backup capb. The best thing to do seems to be
928                         # to ignore requests to back up.
929                         continue
930                 fi
931                 db_get "$templ"
932                 ANSWER="$RET"
933         else
934             echo >&2 "Need debconf to interact"
935             exit 2
936         fi
937
938         case "$ANSWER" in
939             install_new|y|Y|I|i)
940                 echo >&2 "Replacing config file $dest_file with new version"
941                 RETAIN_OLD=YES
942                 replace_conf_file;
943                 exit 0;
944                 ;;
945             diff|D|d)
946                 DIFF="$(run_diff diff -uBbwt "$dest_file" "$new_file")"
947                 show_diff "$DIFF"
948                 ;;
949             sdiff|S|s)
950                 DIFF="$(run_diff sdiff -BbWt "$dest_file" "$new_file")"
951                 show_diff "$DIFF"
952                 ;;
953             diff_threeway|3|t|T)
954                 if [ -e "$statedir/cache/$cached_file" ] && \
955                     [ "$THREEWAY" ]; then
956                     if [ -e "$dest_file" ]; then
957                         DIFF="$(diff3 -L Current -L Older -L New -A \
958                             "$dest_file" "$statedir/cache/$cached_file" \
959                             "$new_file")"  || true
960                     else
961                         DIFF="$(diff3 -L Current -L Older -L New -A \
962                             /dev/null "$statedir/cache/$cached_file" \
963                             "$new_file")"  || true
964                     fi
965                     show_diff "$DIFF"
966                 else
967                     DIFF="$(run_diff diff -uBbwt "$dest_file" "$new_file")"
968                     show_diff "$DIFF"
969                 fi
970                 ;;
971             merge_threeway|M|m)
972                 echo >&2 "Merging changes into the new version"
973                 if [ -e "$statedir/cache/$cached_file" ] && \
974                        [ "$THREEWAY" ]; then
975                     ret=0
976                     diff3 -L Current -L Older -L New -m \
977                         "$dest_file" "$statedir/cache/$cached_file" \
978                         "$new_file" > "$dest_file.${NEW_SUFFIX}" || ret=$?
979                     case "$ret" in
980                         0)
981                             new_file="$dest_file.${NEW_SUFFIX}"
982                             RETAIN_OLD=YES
983                             replace_conf_file
984                             rm -f "$dest_file.${NEW_SUFFIX}" # don't need this around no mo'
985                             exit 0
986                             ;;
987                         *)
988                             mv "$dest_file.${NEW_SUFFIX}" "$dest_file.${ERR_SUFFIX}"
989                             db_subst ucf/conflicts_found dest_file "$dest_file"
990                             db_subst ucf/conflicts_found ERR_SUFFIX "${ERR_SUFFIX}"
991                             db_input critical ucf/conflicts_found || true
992                             db_go || true
993                             ;;
994                     esac
995                 else
996                     replace_conf_file
997                     rm -f "$dest_file.${NEW_SUFFIX}" # don't need this around no mo'
998                     exit 0
999                 fi
1000                 ;;
1001             shell|Z|z)
1002                 # We explicitly connect STDIN and STDOUT to the
1003                 # script's controlling terminal, so even if STDIN is
1004                 # fed by a pipe, as is the case when run from
1005                 # /usr/bin/debconf, the shell should be fully
1006                 # functional. However, the test for a controlling
1007                 # terminal uses /usr/bin/tty, which consults only
1008                 # STDIN. As far as I can tell, when run from debconf,
1009                 # ucf will _never_ use the current terminal. If the
1010                 # goal is to check for access to a terminal, the test
1011                 # should be for foreground process group membership,
1012                 # not a terminal connected to STDIN (tty -s), and not
1013                 # a terminal it doesn't necessarily own (tty -s
1014                 # </dev/tty). The easiest way do this from a shell is
1015                 # probably with /bin/ps.
1016                 if [ "$(ps -o tpgid= $$)" -eq "$(ps -o pgid= $$)" ]; then
1017                     export UCF_CONFFILE_OLD="$dest_file"
1018                     export UCF_CONFFILE_NEW="$new_file"
1019                     bash >/dev/tty </dev/tty || true
1020                 elif [ -n "$DISPLAY" ]; then
1021                     x-terminal-emulator || true
1022                 else
1023                     # Don't know what to do
1024                     echo >&2 "No terminal, and no DISPLAY set, can't fork shell."
1025                     sleep 3
1026                 fi
1027                 ;;
1028             keep_current|n|N|o|O|'')
1029                 replace_md5sum
1030
1031                 cp -pf "$orig_new_file" "$dest_file.${DIST_SUFFIX}"
1032                 exit 0
1033                 ;;
1034             *)
1035                 if [ "$DEBCONF_OK" = "YES" ]; then
1036                         echo "Error: unknown response from debconf:'$RET'" >&2
1037                         exit 1
1038                 else
1039                         echo
1040                         echo "Please answer with one of the single letters listed." >&2
1041                         echo
1042                 fi
1043         esac
1044     done
1045 fi
1046
1047 db_stop
1048
1049 exit 0