home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / boot / i386 / rescue / sbin / modify_resolvconf < prev    next >
Text File  |  2006-11-29  |  26KB  |  816 lines

  1. #!/bin/bash
  2. #
  3. # Copyright (c) 2001-2005 SuSE Linux Products GmbH, Nuernberg, Germany.
  4. # This program is free software; you can redistribute it and/or modify it under
  5. # the terms of the GNU General Public License as published by the Free Software
  6. # Foundation; either version 2 of the License, or (at your option) any later
  7. # version.
  8. #
  9. # This program is distributed in the hope that it will be useful, but WITHOUT
  10. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
  12. # details.
  13. #
  14. # You should have received a copy of the GNU General Public License along with
  15. # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  16. # Place, Suite 330, Boston, MA 02111-1307 USA
  17. #
  18. # Author: Christian Zoz <zoz@suse.de>
  19. #
  20. # $Id: modify_resolvconf 1405 2006-02-13 15:08:56Z zoz $
  21. #
  22.  
  23. if ! touch /tmp &>/dev/null; then
  24.   echo "Filesystem read only: Cannot modify anything" >&2
  25.   exit 1
  26. fi
  27.  
  28. RESOLVCONF=/etc/resolv.conf
  29. NAMEDCONF=/etc/named.d/forwarders.conf
  30. PROGNAME=${0##*/}
  31. RETVAL=0
  32. if [ -x /usr/bin/fmt ] ; then
  33.   FMT="/usr/bin/fmt -u -w 64"
  34. else
  35.   FMT="cat"
  36. fi
  37.  
  38. usage () {
  39.   cat << EOT >&2
  40.   usage:   $PROGNAME <action> <options>
  41.   action:  modify, restore, cleanup or check
  42.   options:                               mandatory for:
  43.     -s|--service <service>               modify, restore
  44.     -e|--extension <string>
  45.     -p|--process <process>               modify
  46.     -i|--pid <pid>
  47.     -f|--script <pathname of script>     modify
  48.     -t|--text <text>                     modify
  49.     -l|--searchlist <list of domains>
  50.     -d|--domain <domain>
  51.     -n|--nameservers <addresses>
  52.     -o|--save_now <pathname of file>
  53.     -a|--save_later <pathname of file>
  54.     -k|--keep
  55.        --resolv
  56.        --named
  57.        --no_restart
  58.     -q|--quiet
  59.     -v|--verbose
  60.     -h|--help                            (does not need an action)
  61.   cleanup and check ignore all options except -q and -v
  62. EOT
  63.   if [ -n "$1" ] ; then
  64.      echo >&2
  65.      ERROR="  ERROR:   "
  66.      echo -e "$*\n" | while read line; do
  67.        echo "${ERROR}${line}" >&2
  68.        ERROR="           "
  69.      done
  70.   fi
  71.   exit 1
  72. }
  73.  
  74. debug () {
  75.   test "$VERBOSE" = "yes" || return
  76.   echo -e "debug: $*" >&2
  77. }
  78.  
  79. warn () {
  80.   test "$QUIET" = "yes" && return
  81.   echo -e "$*" >&2
  82. }
  83.  
  84. log () {
  85.   logger -t "$PROGNAME" "$*"
  86.   debug "$*"
  87.   if [ "$ACTION" = "cleanup" -a "$QUIET" != "yes" ] ; then
  88.     echo -e "$*" >&2
  89.   fi
  90. }
  91.  
  92. write_meta () {
  93.   # At first we prepend a default text to TEXT
  94.   TEXT="This is a temporary ${FILENAME##*/} created by service $SERVICE.\
  95.         The previous file has been saved and will be restored later.\n\n\
  96.         If you don't like your ${FILENAME##*/} to be changed, you\
  97.         can set MODIFY_{RESOLV,NAMED}_CONF_DYNAMICALLY=no. This variables\
  98.         are placed in /etc/sysconfig/network/config.\n\n\
  99.         You can also configure service $SERVICE not to modify it.\n\n\
  100.         $TEXT"
  101.   cat << EOT > $FILENAME
  102. ### BEGIN INFO
  103. #
  104. # Modified_by:  $SERVICE
  105. # Backup:       $BACKUP
  106. # Process:      $PROC
  107. # Process_id:   $PID
  108. # Script:       $SCRIPT
  109. # Saveto:       $SAVELATER
  110. EOT
  111. infoline="# Info:         "
  112. echo -e "$TEXT" |
  113.   $FMT |
  114.   while read line ; do
  115.     echo "$infoline$line";
  116.     infoline="#               "
  117.   done >> $FILENAME
  118.   cat << EOT >> $FILENAME
  119. #
  120. ### END INFO
  121. EOT
  122. }
  123.  
  124. read_resolvconf () {
  125.   OLDDNS="`sed -n '/^[[:space:]]*nameserver[[:space:]]*/s///p' $TMP_DATA`"
  126. }
  127.  
  128. write_resolvconf () {
  129.   # Only one of $DOMAIN and $SEARCH does contain something
  130.   if [ -n "$DOMAIN" ] ; then
  131.     mv $TMP_DATA $TMP_FILE
  132.     sed -e "/^[[:space:]]*\(domain\|search\)[[:space:]]/d" \
  133.       $TMP_FILE > $TMP_DATA
  134.     echo "domain $DOMAIN" >> $TMP_DATA
  135.   fi
  136.   if [ -n "$SEARCH" ] ; then
  137.     mv $TMP_DATA $TMP_FILE
  138.     sed -e "/^[[:space:]]*\(domain\|search\)[[:space:]]/d" \
  139.       $TMP_FILE > $TMP_DATA
  140.     echo "search $SEARCH" >> $TMP_DATA
  141.   fi
  142.   if [ -n "$DNS" ] ; then
  143.     mv $TMP_DATA $TMP_FILE
  144.     sed "/^[[:space:]]*nameserver[[:space:]]/d" $TMP_FILE > $TMP_DATA
  145.     for a in $MODIFY_RESOLV_CONF_STATIC_DNS $DNS; do
  146.       echo "nameserver $a" >> $TMP_DATA
  147.     done
  148.   fi
  149. }
  150.  
  151. read_namedconf () {
  152.   # At first normalize the forwarders entry, i.e. remove all comments and
  153.   # linebreaks from it.
  154.   mv $TMP_DATA $TMP_FILE
  155.   sed -e '/^[[:space:]]*forwarders/{
  156.       h
  157.       :a
  158.       s-/\*.*\*/--g
  159.       s-#.*$--
  160.       s-//.*$--
  161.       s-[[:space:]\n]\+- -g
  162.       /} *; *$/bb
  163.       N
  164.       ba
  165.       :b
  166.       x
  167.       s-for.*$--
  168.       G
  169.       s-\n --
  170.       s- ;-;-g
  171.     } ' $TMP_FILE > $TMP_DATA
  172.   # Then get the old Nameservers
  173.   OLDDNS=`sed -n -e '/^[[:space:]]*forwarders/{
  174.       s-[^0-9. ]*--g
  175.       p
  176.     } ' $TMP_DATA | tr -d '\n'`
  177. }
  178.  
  179. write_namedconf () {
  180.   # Put together a list of nameservers from the new servers and then the
  181.   # old one, but stop after the third
  182.   FORWDS=""
  183.   declare -i n=0
  184.   local a i
  185.   for a in $DNS; do # $OLDDNS
  186.     # named will fail if there are duplicate forwarders (bug 28459)
  187.     for i in $FORWDS ; do
  188.       if [ "$a;" == "$i" ] ; then
  189.         continue 2
  190.       fi 
  191.     done
  192.     FORWDS="$FORWDS $a;"
  193.     n=$((n+1))
  194.     test $n = 3 && break
  195.   done
  196.   # replace the nameservers in the normalized file
  197.   mv $TMP_DATA $TMP_FILE
  198.  
  199.   # Check whether there is an active "forwarders" directive
  200.   grep "^[[:space:]]*forwarders" $TMP_FILE >/dev/null
  201.   if [ $? == 0 ] ; then
  202.     # Yes, replace that directive's argument
  203.     sed -e "/^[[:space:]]*forwarders/{
  204.         s-\(^.*forwarders\).*\$-\1 {$FORWDS };-
  205.       } " $TMP_FILE > $TMP_DATA
  206.   else
  207.     # Check whether the default comment for the "forwarders" directive is present
  208.     grep "^[[:space:]]*# your provider's name server." $TMP_FILE >/dev/null
  209.     if [ $? == 0 ] ; then
  210.       # Yes, put the comment right after that comment
  211.       sed -e "/^[[:space:]]*# your provider's name server./a\        forwarders {$FORWDS };" $TMP_FILE > $TMP_DATA
  212.     else
  213.       # No, so just put the option right after the "options {" block opening
  214.       sed -e "/^options {/a\        forwarders {$FORWDS };" $TMP_FILE > $TMP_DATA
  215.     fi
  216.   fi
  217. }
  218.  
  219. write_file () {
  220.   read_file
  221.   write_meta
  222.   case "$FILENAME" in
  223.     *resolv*) write_resolvconf; ;;
  224.     *named*)  write_namedconf; ;;
  225.   esac
  226.   cat $TMP_DATA >> $FILENAME
  227.   chmod 644 $FILENAME
  228. }
  229.  
  230. read_file () {
  231.   sed "/### BEGIN INFO/,/### END INFO/d" $FILENAME > $TMP_DATA
  232.   case "$FILENAME" in
  233.     *resolv*) read_resolvconf; ;;
  234.     *named*)  read_namedconf; ;;
  235.   esac
  236. }
  237.  
  238. notify_services () {
  239.   test "$NO_RESTART" = "yes" && return
  240.   case "$FILENAME" in
  241.     *resolv*)
  242.       if [ -d /var/spool/postfix/etc/ ] ; then
  243.         cp $RESOLVCONF /var/spool/postfix/etc/
  244.       fi
  245.       if [ -x /etc/init.d/lwresd ] ; then
  246.         warn "`/etc/init.d/lwresd reload`"
  247.       fi
  248.       ;;
  249.     *named*)
  250.       warn "`/etc/init.d/named reload`"
  251.       ;;
  252.   esac
  253. }
  254.  
  255. remove_tmp_files () {
  256.   rm $TMP_DATA $TMP_FILE 2>/dev/null
  257. }
  258.  
  259. trap remove_tmp_files EXIT SIGINT SIGSEGV SIGQUIT SIGTERM
  260.  
  261. TMP_DATA=`mktemp /tmp/${PROGNAME}.XXXXXX`
  262. TMP_FILE=`mktemp /tmp/${PROGNAME}.XXXXXX`
  263.  
  264. ###########################################################
  265. # Parse commandline
  266. ###########################################################
  267.  
  268. # Now set the parsed args but save the original commandline for recursive calls
  269. COMMANDLINE="$@"
  270. ACTION=""
  271. VARIABLE=""
  272. while true ; do
  273.   case "$1" in
  274.     -s|--service) VARIABLE=SERVICE;;
  275.     -e|--extension) VARIABLE=EXT;;
  276.     -p|--process) VARIABLE=PROC;;
  277.     -i|--pid) VARIABLE=PID;;
  278.     -f|--script) VARIABLE=SCRIPT;;
  279.     -t|--text) VARIABLE=TEXT;;
  280.     -l|--searchlist) VARIABLE=SEARCH;;
  281.     -d|--domain) VARIABLE=DOMAIN;;
  282.     -n|--nameservers) VARIABLE=DNS;;
  283.     -q|--quiet) QUIET=yes;;
  284.     -v|--verbose) VERBOSE=yes;;
  285.     -h|--help) usage;;
  286.     -o|--save_now) VARIABLE=SAVENOW;;
  287.     -a|--save_later) VARIABLE=SAVELATER;;
  288.     -k|--keep) KEEP=yes;;
  289.        --resolv) FILENAME=$RESOLVCONF;;
  290.        --named) FILENAME=$NAMEDCONF;;
  291.        --no_restart) NO_RESTART=yes;;
  292.     "") break ;;
  293.     --)
  294.       shift
  295.       test -n "$ACTION" -o $# -gt 1 \
  296.         && usage "Exactly one action may be given.\n"\
  297.                  "Currently given actions: $ACTION $*"
  298.       ACTION="$1"
  299.       shift
  300.       break
  301.       ;;
  302.     -*) usage Unknown option $1;;
  303.     *)
  304.       test -n "$ACTION" && usage "Exactly one action may be given.\n"\
  305.                                  "Currently given actions: $ACTION $1"
  306.       ACTION="$1"
  307.       ;;
  308.   esac
  309.   if [ -n "$VARIABLE" ] ; then
  310.     test -z "$2" && usage Option $1 needs an argument
  311.     eval $VARIABLE=\$2
  312.     shift
  313.     VARIABLE=""
  314.   fi
  315.   shift
  316. done
  317. test -z "$ACTION" && usage No action was given
  318.  
  319. ###########################################################
  320. # Check if the arguments to the options are usefull
  321. ###########################################################
  322.  
  323. case $ACTION in
  324.   modify)
  325. #z#    case "$SERVICE" in
  326. #z#      dhclient|dhcpcd|pppd|ipppd|pcmcia|hotplug) ;;
  327. #z#      *) usage "service must be one of dhclient dhcpcd" \
  328. #z#               " pppd ipppd pcmcia hotplug";;
  329. #z#    esac
  330.     test -z "$SERVICE" && usage "a vaild service must be set with --service"
  331.  
  332.     if [ "$SERVICE" != "hotplug" ] ; then
  333.       if [ -z "$PROC" ] ; then
  334.         usage "a running process/daemon must be set with --process"
  335.       else
  336.         PROCPID=`/sbin/pidofproc $PROC 2>/dev/null`
  337.         if [ -z "$PROCPID" ] ; then
  338.           usage "$PROC is not running currently, this may not happen"
  339.         else
  340.           debug "pid of prodess $PROC is $PROCPID"
  341.         fi
  342.       fi
  343.     fi
  344.  
  345.     if [ -z "$PID" ] ; then
  346.       PID="$PROCPID"
  347.       # usage "you must set the pid of $PROC with --pid"
  348.     else
  349.       PIDPROC=`ps h $PID 2>/dev/null |(read a b c d e f; echo $e)`
  350.       if [ -z "$PIDPROC" ] ; then
  351.         warn "there is no process with id $PID"
  352.       else
  353.         debug "process with pid $PID is $PIDPROC"
  354.       fi
  355.     fi
  356.  
  357. #    if [ -z "$SCRIPT" ] ; then
  358. #      usage "you must set the script that modifies $FILENAME with --script"
  359. #    else
  360.     if [ -n "$SCRIPT" ] ; then
  361.       if [ -e "$SCRIPT" ] ; then
  362.         debug "script $SCRIPT found"
  363.       else
  364.         usage "there is no script named $SCRIPT"
  365.       fi
  366.     fi
  367.  
  368.     if [ -z "$TEXT" ] ; then
  369.       usage "You have to provide an descriptive text with --text\n" \
  370.             "With --text=- you can use a here document"
  371.     else
  372.       # If TEXT="-" read it as here document
  373.       if [ "$TEXT" = "-" ] ; then
  374.         TEXT=""
  375.         while read a; do
  376.           test -z "$a" && a="\n\n"
  377.           TEXT="$TEXT $a"
  378.         done
  379.       fi
  380.     fi
  381.  
  382.     if [ -n "$DOMAIN" -a -n "$SEARCH" ] ; then
  383.       usage "only one of --domain and --searchlist may be used\n" \
  384.             "in resolv.conf search and domain are mutually exclusiv"
  385.     fi
  386.  
  387.     FILENAME=""
  388.     ;;
  389.   restore)
  390. #z#    case "$SERVICE" in
  391. #z#      dhclient|dhcpcd|pppd|ipppd|pcmcia|hotplug) ;;
  392. #z#      *) usage "service must be one of dhclient dhcpcd" \
  393. #z#               " pppd ipppd pcmcia hotplug";;
  394. #z#    esac
  395.     test -z "$SERVICE" && usage "a vaild service must be set with --service"
  396.     ;;
  397.   cleanup)
  398.     if [ -z "$FILENAME" ] ; then
  399.       $0 $COMMANDLINE --resolv
  400.       $0 $COMMANDLINE --named
  401.       exit
  402.     fi
  403.     ;;
  404.   check)
  405.     # ignore all options
  406.     if [ -z "$FILENAME" ] ; then
  407.       FILENAME=$RESOLVCONF
  408.     fi
  409.     KEEP=""
  410.     SAVENOW=""
  411.     ;;
  412.   *) usage "$ACTION is not a valid action" ;;
  413. esac
  414.  
  415.  
  416. ###########################################################
  417. # respect variables in config and choose right filenames
  418. ###########################################################
  419.  
  420. debug "pre-config: FILENAME=$FILENAME"
  421.  
  422. eval `grep "^[[:space:]]*\(\
  423. MODIFY_RESOLV_CONF_DYNAMICALLY\|\
  424. MODIFY_NAMED_CONF_DYNAMICALLY\|\
  425. MODIFY_RESOLV_CONF_STATIC_DNS\
  426. \)=" /etc/sysconfig/network/config 2>/dev/null`
  427.  
  428. if [ "$MODIFY_NAMED_CONF_DYNAMICALLY" = "yes" ] ; then
  429.   test -z "$FILENAME" && FILENAME=$NAMEDCONF
  430. fi
  431. if [ "$MODIFY_RESOLV_CONF_DYNAMICALLY" = "yes" ] ; then
  432.   test -z "$FILENAME" && FILENAME=$RESOLVCONF
  433. fi
  434.  
  435. debug "post-config: FILENAME=$FILENAME"
  436.  
  437. case "$FILENAME" in
  438.   "")
  439.     log "Service $SERVICE tried to modify resolver configuration, but it"
  440.     log "was not modified due to MODIFY_RESOLV\NAMED_CONF_DYNAMICALLY=no"
  441.     exit
  442.     ;;
  443.   $NAMEDCONF)
  444.     if ! [ -r "$FILENAME" -a -x /etc/init.d/named ] ; then
  445.       if [ "$ACTION" = "cleanup" ] ; then
  446.         exit 0
  447.       else
  448.         log "Service $SERVICE tried to modify $FILENAME, but named seems" \
  449.             "not to be installed"
  450.         log "Check your settings of MODIFY_RESOLV\NAMED_CONF_DYNAMICALLY"
  451.         exit 1
  452.       fi
  453.     fi
  454.     ;;
  455.   $RESOLVCONF)
  456.     if ! [ -r "$FILENAME" ] ; then
  457.       touch $FILENAME
  458.       chmod 644 $FILENAME
  459.     fi
  460.     ;;
  461. esac
  462. BACKUP="${FILENAME}.saved.by.$SERVICE"
  463. test -n "$EXT" && BACKUP="${BACKUP}.${EXT}"
  464.  
  465. ###########################################################
  466. # Read current resolv.conf
  467. ###########################################################
  468.  
  469. read_meta () {
  470.   if [ -e $FILENAME ] ; then
  471.     RC_BLOCK=`sed -n "/### BEGIN INFO/,/### END INFO/s/^[[:space:]#]*//p" \
  472.                                                                  $FILENAME`
  473.     if [ -n "$RC_BLOCK" ] ; then
  474.       while read NAME TOKEN; do
  475.         # Note the quotes around RC_BLOCK. Without it we get all in one line.
  476.         eval $NAME='`echo "$RC_BLOCK" | sed -n "/^$TOKEN:[[:space:]]*/s///p"`'
  477.         debug "$NAME=${!NAME}"
  478.       done << "        EOL"
  479.         RC_SERVICE   Modified_by
  480.         RC_BACKUP    Backup
  481.         RC_PROC      Process
  482.         RC_PID       Process_id
  483.         RC_SCRIPT    Script
  484.         RC_SAVELATER Saveto
  485.         EOL
  486.       # Here we want all in one line so we do not quote RC_BLOCK, but we lose
  487.       # all formatting of the text (to be enhanced).
  488.       RC_TEXT=`echo $RC_BLOCK | sed -e "s/^.*Info:[[:space:]]*//" \
  489.                                     -e "s/ END INFO.*$//"`
  490.       debug "RC_TEXT=${RC_TEXT:0:20} ..."
  491.     fi
  492.   fi
  493. }
  494.  
  495. read_meta
  496.  
  497. ###########################################################
  498. # Divide all available backups
  499. ###########################################################
  500.  
  501. # . /sbin/modify_cf.lib
  502.  
  503. # Check all existing backups and divide them into valid direct, valid indirect
  504. # and invalid backups.
  505. # "all existing backups" = all files /etc/resolv.conf.saved.by.* and all
  506. #                          files found in the backup field of another
  507. #                          valid backup
  508. # "valid direct" = file which the backup entry in resolv.conf points to
  509. # "valid indirect" = file which the backup entry of another valid backup
  510. #                    points to
  511. # "invalid" = files /etc/resolv.conf.saved.by.* which are not valid
  512. # All valid backups build a stack of files. These files are stored in three
  513. # variables:
  514. # DIRECT_BACKUP: Contains the only valid direct backup.
  515. # INDIRECT_BACKUPS: Contains all indirect backups except the last of the stack.
  516. #                  Files are ordered like the stack. The first is the backup
  517. #                  of the valid direct backup.
  518. # LAST_BACKUP: Contains the last file of the stack of valid indirect backups.
  519. #              This file does not contain an info block or has an invalid
  520. #              entry in its backup field.
  521. # INVALID_BACKUPS: Contains all invalid backups sorted in chronological
  522. # order, the newest first. This files are only used if there is no valid
  523. # backup, but resolv.conf was modified dynamically.
  524.  
  525. read_field_backup () {
  526.   ls "$*" &>/dev/null || return
  527.   BAFI=`\
  528.   sed -n \
  529.     "/### BEGIN INFO/,/### END INFO/s/^[[:space:]#]*Backup:[[:space:]]*//p" \
  530.     "$*"`
  531.   ls "$BAFI" 2>/dev/null
  532. }
  533.  
  534. # get all backups in chronoligical order, the newest first
  535. for bp in `ls -t ${FILENAME}.saved.by* \
  536.                  $(read_field_backup ${FILENAME}*) 2>/dev/null`; do
  537.   for abp in $ALL_BACKUPS; do
  538.     test "$bp" = "$abp" && continue 2
  539.   done
  540.   ALL_BACKUPS="$ALL_BACKUPS $bp"
  541. done
  542. test -r "$RC_BACKUP" && DIRECT_BACKUP="$RC_BACKUP"
  543. debug "(0) DIRECT_BACKUP: $DIRECT_BACKUP"
  544. debug "(0) ALL_BACKUPS: $ALL_BACKUPS"
  545.  
  546. if [ -n "$ALL_BACKUPS" ] ; then
  547.   # At first put all except DIRECT_BACKUP from ALL_BACKUPS to INVALID_BACKUPS
  548.   INVALID_BACKUPS=`\
  549.   for a in $ALL_BACKUPS; do
  550.     test "$a" != "$DIRECT_BACKUP" && echo "$a"
  551.   done`
  552.   # Then we are going to build a chain of backups and delete the chain elements
  553.   # from INVALID_BACKUPS.
  554.   NEXT_BACKUP=`read_field_backup "$DIRECT_BACKUP"`
  555.   while [ -r "$NEXT_BACKUP" ] ; do
  556.     debug "(searching stack) NEXT_BACKUP=$NEXT_BACKUP"
  557.     # At first delete NEXT_BACKUP from INVALID_BACKUPS
  558.     INVALID_BACKUPS=`\
  559.     for a in $INVALID_BACKUPS; do
  560.       test "$a" != "$NEXT_BACKUP" && echo "$a"
  561.     done`
  562.     # INDIRECT_BACKUPS contains all elements except the last ...
  563.     INDIRECT_BACKUPS="$INDIRECT_BACKUPS $LAST_BACKUP"
  564.     # ... which is always in LAST_BACKUP
  565.     LAST_BACKUP="$NEXT_BACKUP"
  566.     NEXT_BACKUP=`read_field_backup "$NEXT_BACKUP"`
  567.     # Detect a loop
  568.     for a in $DIRECT_BACKUP $INDIRECT_BACKUPS $LAST_BACKUP; do
  569.       test "$NEXT_BACKUP" = "$a" && NEXT_BACKUP=""
  570.     done
  571.   done
  572.   debug "(1) DIRECT_BACKUP: $DIRECT_BACKUP"
  573.   debug "(1) INDIRECT_BACKUPS: $INDIRECT_BACKUPS"
  574.   debug "(1) LAST_BACKUP: $LAST_BACKUP"
  575.   debug "(1) INVALID_BACKUPS: $INVALID_BACKUPS"
  576. fi
  577.  
  578. # The remaining invalid backups have to be reordered. First all backups
  579. # without an infoblock then those with info block, both groups
  580. # still chronologically ordered.
  581. unset IB_1 IB_2 IB_3
  582. for a in $INVALID_BACKUPS; do
  583.   b=`sed -n "/### BEGIN INFO/,/### END INFO/p" $a`
  584.   if [ -z "$b" ] ; then
  585.     IB_1="$IB_1 $a"
  586.   else
  587.     IB_2="$IB_2 $a"
  588.   fi
  589. done
  590. INVALID_BACKUPS="$IB_1 $IB_2"
  591. debug "(2) INVALID_BACKUPS: $INVALID_BACKUPS"
  592. # Then we select all backups with the correct service in the filename. We now
  593. # have two groups, both ordered as before.
  594. # Then we split the first group in those which additionally contain the
  595. # given extension in the filename still sustainig the old order.
  596. unset IB_1 IB_2 IB_3
  597. for a in $INVALID_BACKUPS; do
  598.   if echo $a | grep "$SERVICE" &>/dev/null; then
  599.     if echo $a | grep "$SERVICE.$EXT" &>/dev/null; then
  600.       IB_1="$IB_1 $a"
  601.     else
  602.       IB_2="$IB_2 $a"
  603.     fi
  604.   else
  605.     IB_3="$IB_3 $a"
  606.   fi
  607. done
  608. INVALID_BACKUPS="$IB_1 $IB_2 $IB_3"
  609. debug "(3) INVALID_BACKUPS: $INVALID_BACKUPS"
  610. # Now the list starts with backups that fit most regarding the  name of the
  611. # backup and then at first these which seem to be backups of unmodified files.
  612. # Now we choose the first one. If it has an info block we try to find an
  613. # according stack as before and then take the last element of it.
  614. CHOOSEN_BACKUP=`echo "$INVALID_BACKUPS" | (read a b; echo "$a")`
  615. SUCC_BACKUP=`read_field_backup "$CHOOSEN_BACKUP"`
  616. unset LOOP_STOP
  617. while [ -r "$SUCC_BACKUP" ] ; do
  618.   LOOP_STOP="$LOOP_STOP $SUCC_BACKUP"
  619.   CHOOSEN_BACKUP="$SUCC_BACKUP"
  620.   SUCC_BACKUP=`read_field_backup "$CHOOSEN_BACKUP"`
  621.   # detect a loop
  622.   for a in $LOOP_STOP; do
  623.     test "$a" = "$SUCC_BACKUP" && SUCC_BACKUP=""
  624.   done
  625. done
  626. # IMHO this should be the at least dynamically modified file of all invalid
  627. # backups.
  628. INVALID_BACKUPS="$CHOOSEN_BACKUP $INVALID_BACKUPS"
  629. debug "(4) INVALID_BACKUPS: $INVALID_BACKUPS"
  630.  
  631. #  x=wird auch au▀erhalb verwendet
  632. #
  633. #    ALL_BACKUPS
  634. #    BAFI
  635. #    CHOOSEN_BACKUP
  636. #  x DIRECT_BACKUP
  637. #  x EXT                     ro
  638. #  x FILENAME                ro
  639. #    IB_1
  640. #    IB_2
  641. #    IB_3
  642. #  x INDIRECT_BACKUPS
  643. #  x INVALID_BACKUPS
  644. #  x LAST_BACKUP
  645. #    LOOP_STOP
  646. #    NEXT_BACKUP
  647. #  x RC_BACKUP               ro
  648. #  x SERVICE                 ro
  649. #    SUCC_BACKUP
  650.  
  651. ###########################################################
  652. # Finally execute the requested action
  653. ###########################################################
  654.  
  655. # If there is a resolv.conf and --save_now was set then lets do it:
  656. test -n "$SAVENOW" -a -e "$FILENAME" && cp -p --backup=t $FILENAME $SAVENOW
  657.  
  658. debug "BACKUP=$BACKUP"
  659. case $ACTION in
  660.   modify)
  661.     # maybe there is no resolv.conf or current resolv.conf points to
  662.     # the same backup file that should be used now
  663.     # We don't copy resolv.conf if there is an valid backup of the same
  664.     # service with the same private extension or ...
  665.     for a in $DIRECT_BACKUP $INDIRECT_BACKUPS $LAST_BACKUP; do
  666.       test "$BACKUP" = "$a" && SAVE="no"
  667.     done
  668.     # ... there isn't any
  669.     if [ -e $FILENAME -a "$SAVE" != "no" ] ; then
  670.        cp -p $FILENAME $BACKUP
  671.        debug "$FILENAME saved to $BACKUP"
  672.     fi
  673.     write_file
  674.     log "Service $SERVICE modified $FILENAME. See info block in this file"
  675.     notify_services
  676.    ;;
  677.   restore)
  678.     # If __save_later was set when modifying then we save the temporary
  679.     # resolv.conf
  680.     test -n "$RC_SAVELATER" -a -e "$FILENAME" &&
  681.       cp -p --backup=t $FILENAME $RC_SAVELATER
  682.     # We only restore if a backup with the correct name (already in $BACKUP)
  683.     # exists. If this backup is valid we have to save the integrity of a
  684.     # possibly existing backup stack. If this backup is invalid (= not part
  685.     # of a stack) then we just restore it, even if we invalidate an existing
  686.     # stack.
  687.     if [ -e "$BACKUP" ] ; then
  688.       # Now we search the possibly existing stack and store the two
  689.       # predecessors and one successor of it.
  690.       # Don't remove the empty entry ("") from the list.
  691.       for a in $FILENAME $DIRECT_BACKUP $INDIRECT_BACKUPS \
  692.                $LAST_BACKUP ""; do
  693.         P="$C"
  694.         C="$S"
  695.         S="$a"
  696.         test "$C" = "$BACKUP" && break
  697.       done
  698.       # If we did not found the Backup in the stack of valid backups, we
  699.       # write BACKUP to C and set FILENAME as its predecessor (P). Because
  700.       # the Backup can have a successor even if it is called invalid, we have
  701.       # to look for it.
  702.       if [ "$C" != "$BACKUP" ] ; then
  703.         P="$FILENAME"
  704.         C="$BACKUP"
  705.         S=`read_field_backup "$BACKUP"`
  706.       fi
  707.       debug "(restore) P=$P C=$C S=$S"
  708.       # If we were called with --keep, we don't restore the backup but
  709.       # keep the temporary settings.
  710.       if [ "$KEEP" = "yes" ] ; then
  711.         # We have to keep the resolver settings, but not the meta information
  712.         # in the info block. There are two cases:
  713.     # 1) The Backup (C) was the last in the stack (S="") then we remove the
  714.     #    info block from its predecessor (P) and remove C.
  715.         # 2) The Backup has itself a backup (S!="") then we have to replace the
  716.         #    info block in the predecessor (P) with that from the Backup (C),
  717.         #    but keep everything else from P. And remove C.
  718.         cp -p $P $TMP_FILE
  719.         if [ "$S" = "" ] ; then
  720.           sed "/### BEGIN INFO/,/### END INFO/d" $TMP_FILE > $P
  721.           log "kept $P and removed info block"
  722.         else
  723.           sed -n "/### BEGIN INFO/,/### END INFO/p" $C > $P
  724.           sed "/### BEGIN INFO/,/### END INFO/d" $TMP_FILE >> $P
  725.           log "kept $P and changed info block"
  726.         fi
  727.         rm $C
  728.         log "removed $C"
  729.       else
  730.         # Don't keep the temporary settings, restore.
  731.         # All we have to do is to move the Backup (C) to its predecessor (P),
  732.         # because the backup field of the previous predecessor already points
  733.         # to the filename of P and the backup field of C what then will be in
  734.         # P points to S (if existing).
  735.         mv $C $P
  736.         log "restored $C to $P"
  737.       fi
  738.     else
  739.       # If no extension was given we search for backup files of the calling
  740.       # service with private extensions and call us recursivle for every
  741.       # found extension.
  742.       if [ "$EXT" = "" ] ; then
  743.         for a in `ls -t "$BACKUP"* 2>/dev/null`; do
  744.           EXT=${a##$BACKUP.}
  745.           $0 $COMMANDLINE -e "$EXT" --no_restart
  746.         done
  747.         if [ "$EXT" = "" ] ; then
  748.           log "no matching backup found, left everything alone"
  749.         fi
  750.       fi
  751.     fi
  752.     # If there is no backup left, make sure that $FILENAME does not contain
  753.     # an info block
  754.     if [ -z "`ls ${FILENAME}.saved.by.* $LAST_BACKUP $INDIRECT_BACKUPS \
  755.                  $DIRECT_BACKUP $INVALID_BACKUPS 2>/dev/null`" ] ; then
  756.       if grep -qs "### BEGIN INFO" $FILENAME 2>/dev/null; then
  757.         log "removing info block from $FILENAME, because there is" \
  758.             "no backup left"
  759.         cp -p $FILENAME $TMP_FILE
  760.         sed "/### BEGIN INFO/,/### END INFO/d" $TMP_FILE >  $FILENAME
  761.         chmod 644 $FILENAME
  762.       fi
  763.     fi
  764.     notify_services
  765.     read_file
  766.     ;;
  767.   cleanup)
  768.     # Peter wants to know the old nameservers before cleaning up
  769.     if [ "$DNS" = "show" ] ; then
  770.       read_file
  771.       test -n "$OLDDNS" && echo $OLDDNS
  772.     fi
  773.     # If $FILENAME was modified (= RC_BLOCK nonempty) we have to restore it
  774.     if [ -n "$RC_BLOCK" -a "$KEEP" != "yes" ] ; then
  775.       # If --save_later was set when modifying then we save the temporary
  776.       # resolv.conf
  777.       test -n "$RC_SAVELATER" -a -e "$FILENAME" &&
  778.         cp -p --backup=t $FILENAME $RC_SAVELATER
  779.       # Search the most reasonable backup. That is (if existing) the LAST_BACKUP
  780.       # then the DIRECT_BACKUP and last the first one of INVALID_BACKUPS
  781.       BACKUP=`echo "$LAST_BACKUP $DIRECT_BACKUP $INVALID_BACKUPS" \
  782.               | (read a b; echo $a)`
  783.       if [ -e "$BACKUP" ] ; then
  784.         mv $BACKUP $FILENAME
  785.         log "restored $FILENAME from $BACKUP"
  786.       fi
  787.     fi
  788.     # Now we remove all backups
  789.     MSG=`rm -v $LAST_BACKUP $INDIRECT_BACKUPS $DIRECT_BACKUP \
  790.                $INVALID_BACKUPS 2>/dev/null`
  791.     test -n "$MSG" && log "Deleted the following stale backups: $MSG"
  792.     # After cleaning up resolc.conf should not contain an INFO block that
  793.     # marks it as modified. If it somehow happens that it has it, we remove it.
  794.     if grep -qs "### BEGIN INFO" $FILENAME 2>/dev/null; then
  795.       cp -p $FILENAME $TMP_FILE
  796.       sed "/### BEGIN INFO/,/### END INFO/d" $TMP_FILE >  $FILENAME
  797.       chmod 644 $FILENAME
  798.     fi
  799.     notify_services
  800.     read_file
  801.     ;;
  802.   check)
  803.     if [ -z "$RC_BLOCK" ] ; then
  804.       warn "$FILENAME not modified"
  805.     else
  806.       warn $RC_TEXT
  807.       RETVAL=1
  808.     fi
  809.     ;;
  810. esac
  811.  
  812. # Don't delete empty file. See bug 40728
  813. # test "`cat $FILENAME`" = "" && rm $FILENAME
  814. debug "finished"
  815. exit $RETVAL
  816.