diff options
author | Rod Whitby <rod@whitby.id.au> | 2007-03-25 11:38:45 +0000 |
---|---|---|
committer | Rod Whitby <rod@whitby.id.au> | 2007-03-25 11:38:45 +0000 |
commit | fa2a25a2700d2211fa302a27cb180ce8475f9cd8 (patch) | |
tree | ec0b69f6bea7008f533e0f3b75f287f171fc3a01 /packages/sysconf | |
parent | 93a28f8e6f88b974630f36d59c7f273e12f6a3be (diff) |
sysconf: A general utility for storing network configuration and CONFFILES in a flash partition
Diffstat (limited to 'packages/sysconf')
-rw-r--r-- | packages/sysconf/.mtn2git_empty | 0 | ||||
-rw-r--r-- | packages/sysconf/files/.mtn2git_empty | 0 | ||||
-rw-r--r-- | packages/sysconf/files/conffiles | 56 | ||||
-rw-r--r-- | packages/sysconf/files/sysconf | 1041 | ||||
-rw-r--r-- | packages/sysconf/files/sysconfsetup | 59 | ||||
-rw-r--r-- | packages/sysconf/sysconf_0.1.bb | 52 |
6 files changed, 1208 insertions, 0 deletions
diff --git a/packages/sysconf/.mtn2git_empty b/packages/sysconf/.mtn2git_empty new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/packages/sysconf/.mtn2git_empty diff --git a/packages/sysconf/files/.mtn2git_empty b/packages/sysconf/files/.mtn2git_empty new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/packages/sysconf/files/.mtn2git_empty diff --git a/packages/sysconf/files/conffiles b/packages/sysconf/files/conffiles new file mode 100644 index 0000000000..d99047ce24 --- /dev/null +++ b/packages/sysconf/files/conffiles @@ -0,0 +1,56 @@ +# conffiles +# Known configuration files. These files are preserved on +# a flash upgrade. Other configuration files, found from: +# +# /usr/lib/ipkg/*.conffiles +# /etc/*.conf +# +# are preserved too with an operation of 'diff' if they have been +# changed since /etc/.configured was created. +# +# Lines starting with # are comments, other lines have +# two fields: +# +# operation file +# +# The file must *NOT* have a leading / +# +# operation may be: +# ignore Do not preserve this file +# preserve Preserve this file unconditionally +# diff Compare file with the new version, ask if different +# +preserve linuxrc +preserve etc/.configured +preserve etc/TZ +diff etc/default/conffiles +diff etc/default/devpts +preserve etc/default/rcS +preserve etc/default/sysconf +diff etc/default/usbd +preserve etc/defaultdomain +preserve etc/dropbear/dropbear_dss_host_key +preserve etc/dropbear/dropbear_rsa_host_key +preserve etc/ssh/ssh_host_dsa_key +preserve etc/ssh/ssh_host_dsa_key.pub +preserve etc/ssh/ssh_host_rsa_key +preserve etc/ssh/ssh_host_rsa_key.pub +preserve etc/fstab +preserve etc/group +preserve etc/gshadow +preserve etc/hostname +preserve etc/hosts +preserve etc/localtime +ignore etc/modules +ignore etc/modules.conf +preserve etc/motd +preserve etc/network/interfaces +preserve etc/ntp.drift +preserve etc/passwd +preserve etc/profile +preserve etc/resolv.conf +preserve etc/shadow +preserve etc/syslog.conf +preserve etc/timezone +preserve root/.ssh/authorized_keys +preserve home/root/.ssh/authorized_keys diff --git a/packages/sysconf/files/sysconf b/packages/sysconf/files/sysconf new file mode 100644 index 0000000000..9499b4daa9 --- /dev/null +++ b/packages/sysconf/files/sysconf @@ -0,0 +1,1041 @@ +#!/bin/sh +# sysconf +# +# Utility to manipulate system configuration information held +# in a System Configuration partition +# + +# hardware +# the 'Hardware' string from cpuinfo +hardware(){ + sed -n 's!^Hardware *: !!p' /proc/cpuinfo +} +# +# machine +# outputs an identifier of the current machine - i.e. the board +# slugos is running on. +machine(){ + case "$(hardware)" in + *Coyote*) echo coyote;; + *IXDPG425*) echo ixdpg425;; + *WRV54G*) echo wrv54g;; + *IXDP425*) echo ixdp425;; + *IXDP465*) echo ixdp465;; + *IXCDP1100*) echo ixcdp1100*;; + *Avila*) echo avila;; + *Loft*) echo loft;; + *NAS?100d*) echo nas100d;; + *DSM?G600*) echo dsmg600;; + *NSLU2*) echo nslu2;; + *FSG?3*) echo fsg3;; + *) echo unknown;; + esac +} + +# NSLU2 flash layout is non-standard. +case "$(machine)" in +nslu2) + kpart="Kernel" + syspart="SysConf" + ffspart="Flashdisk";; +*) + kpart="kernel" + syspart="sysconfig" + ffspart="filesystem";; +esac + +# +# mtblockdev "name" +# return (output) the block device name for flash parition "name" +# /proc/mtd has the general form: +# dev: size erasesize name +# mtd5: 00020000 00020000 "FIS directory" +# use this rather than hard-wiring the device because the partition +# table can change - looking in /proc/mtd is more reliable. +mtblockdev(){ + sed -n 's!^mtd\([0-9][0-9]*\):[^"]*"'"$1"'"$!/dev/mtdblock\1!p' /proc/mtd +} + +# +# sysconf_valid +# return true if the SysConf partition exists and seems to be +# potentially valid (it starts with a reasonable length). +sysconf_valid(){ + local sysdev + sysdev="$(mtblockdev $syspart)" + test -n "$sysdev" -a -b "$sysdev" && + devio "<<$sysdev" '!! b.10>s32768<&!' +} + +# +# sysvalmatch "section" "name" 'pattern' "configuration file" +# sysvalof "section" "name" "configuration file" +# sysval "section" "name" +# outputs the value of the SysConf variable 'name' from section 'section', +# if there are multiple definitions only the last is output +# NOTE: these functions should only be used internally, add entries to 'config' +# below if necessary. This is because 'config' does the defaulting. +sysvalmatch(){ + sed -n '/^\['"$1"'\]$/,/^\[.*\]$/s/^'"$2"'=\('"$3"'\)$/\1/p' "$4" | sed -n '$p' +} +sysvalof(){ + sysvalmatch "$1" "$2" '.*' "$3" +} +sysval(){ + test -r "$config_root/etc/default/sysconf" && + sysvalof "$1" "$2" "$config_root/etc/default/sysconf" +} +# +# syssection "section" +# outputs all the values from the given section changed to the format "name value" +# (i.e. the '=' is dropped). +syssection(){ + test -r "$config_root/etc/default/sysconf" && + sed -n '/^\['"$1"'\]$/,/^\[.*\]$/s/^\([^=]*\)=\(.*\)$/\1 \2/p' "$config_root/etc/default/sysconf" +} +# +# config "value" +# convenience callers for specific values to avoid mis-typing in scripts +# NOTE: this function does the defaulting, 'sysval' does not! +# config_root: if set this will override the root where config/sysval +# looks for /etc/default/sysconf +config(){ + case "$1" in + host) if test -n "$(sysval network disk_server_name)" + then + sysval network disk_server_name + elif test -n "$(sysval network default_server_name)" + then + sysval network default_server_name + else + # because we want the name to remain constant: + echo "brokenslug" + fi;; + domain) sysval network w_d_name;; + iface) if test -n "$(sysval network lan_interface)" + then + sysval network lan_interface + else + echo eth0 + fi;; + ip) if test -n "$(sysval network ip_addr)" + then + sysval network ip_addr + else + echo 192.168.1.77 + fi;; + netmask)sysval network netmask;; + gateway)sysval network gateway;; + dns) sysval network dns_server1;; + dns2) sysval network dns_server2;; + dns3) sysval network dns_server3;; + boot) if test -n "$(sysval network bootproto)" + then + sysval network bootproto + else + echo dhcp + fi;; + valid) test -r "$config_root/etc/default/sysconf";; + *) return 1;; + esac +} + +# +# sysconf_read [prefix] +# read the $syspart partition (if present) writing the result into +# /etc/default/sysconf, if the result is empty it will be removed. +sysconf_read(){ + local sysdev sedcmd config_root + config_root="$1" + rm -f /tmp/sysconf.new + sysdev="$(mtblockdev $syspart)" + if sysconf_valid + then + # Read the defined part of $syspart into /etc/default/sysconf. + # $syspart has lines of two forms: + # + # [section] + # name=value + # + # In practice $syspart also contains other stuff, use the command: + # + # devio '<</dev/mtd1;cpb' + # + # to examine the current settings. The badly formatted stuff + # is removed (to be exact, the sed script selects only lines + # which match one of the two above). The lan interface, which + # on NSLU2 defaults to ixp0, is changed to the correct value for + # slugos, eth0. The bootproto, which LinkSys sets to static in + # manufacturing, is reset to dhcp if the IP is still the + # original (192.168.1.77) + sedcmd='/^\[[^][]*\]$/p;' + # only do the ip_addr and lan_interface fixups on NSLU2 + if test "$(machine)" = nslu2 + then + sedcmd="$sedcmd"' + s/^lan_interface=ixp0$/lan_interface=eth0/; + /^ip_addr=192\.168\.1\.77$/,/^bootproto/s/^bootproto=static$/bootproto=dhcp/;' + fi + # and only print lines of the correct form + sedcmd="$sedcmd"' + /^[-a-zA-Z0-9_][-a-zA-Z0-9_]*=/p' + + devio "<<$sysdev" cpb fb1,10 | sed -n "$sedcmd" >/tmp/sysconf.new + fi + # + # test the result - sysconf must be non-empty + if test -s /tmp/sysconf.new + then + mv /tmp/sysconf.new "$config_root/etc/default/sysconf" + else + rm -f /tmp/sysconf.new + return 1 + fi +} + +# +# sysconf_default [prefix] +# Provde a default /etc/default/sysconf when there is no $syspart partition, +# or when it is invalid, this function will read from an existing sysconf, +# copying the values into the new one. +# sysconf_line tag config-tag +# write an appropriate line if the config value is non-empty +sysconf_line(){ + config "$2" | { + local value + read value + test -n "$value" && echo "$1"="$value" + } +} +# +sysconf_default(){ + local config_root + config_root="$1" + { echo '[network]' + sysconf_line disk_server_name host + sysconf_line w_d_name domain + sysconf_line lan_interface iface + sysconf_line ip_addr ip + sysconf_line netmask netmask + sysconf_line gateway gateway + sysconf_line dns_server1 dns + sysconf_line dns_server2 dns2 + sysconf_line dns_server3 dns3 + sysconf_line bootproto boot + } >/tmp/sysconf.new + mv /tmp/sysconf.new "$config_root/etc/default/sysconf" +} + +# +# sysconf_reload [prefix] +# read the values from /etc/default/sysconf and use these values to set +# up the following system files: +# +# /etc/hostname +# /etc/defaultdomain +# /etc/resolv.conf +# /etc/network/interfaces +# /etc/motd +# +sysconf_reload(){ + local config_root host domain iface boot ip netmask gateway ifname iftype + config_root="$1" + host="$(config host)" + test -n "$host" && echo "$host" >"$config_root/etc/hostname" + domain="$(config domain)" + test -n "$domain" && echo "$domain" >"$config_root/etc/defaultdomain" + # + # The DNS server information gives up to three nameservers, + # but this currently only binds in the first. + { + test -n "$domain" && echo "search $domain" + test -n "$(config dns)" && echo "nameserver $(config dns)" + test -n "$(config dns2)" && echo "nameserver $(config dns2)" + test -n "$(config dns3)" && echo "nameserver $(config dns3)" + } >"$config_root/etc/resolv.conf" + # + # Ethernet information. This goes into /etc/network/interfaces, + # however this is only used for static setup (and this is not + # the default). With dhcp the slugos udhcp script, + # /etc/udhcpc.d/50default, loads the values from sysconf. + iface="$(config iface)" + boot="$(config boot)" + # Only dhcp and static are supported at present - bootp + # support requires installation of appropriate packages + # dhcp is the fail-safe + case "$boot" in + dhcp|static) ;; + *) boot=dhcp;; + esac + # + ip="$(config ip)" + netmask="$(config netmask)" + gateway="$(config gateway)" + { + echo "# /etc/network/interfaces" + echo "# configuration file for ifup(8), ifdown(8)" + echo "#" + echo "# The loopback interface" + echo "auto lo" + echo "iface lo inet loopback" + echo "#" + echo "# The interface used by default during boot" + echo "auto $iface" + echo "# Automatically generated from /etc/default/sysconf" + echo "# address, netmask and gateway are ignored for 'dhcp'" + echo "# but required for 'static'" + echo "iface $iface inet $boot" + # The following are ignored for DHCP but are harmless + test -n "$ip" && echo " address $ip" + test -n "$netmask" && echo " netmask $netmask" + test -n "$gateway" && echo " gateway $gateway" + # + # Now read all the other ARPHRD_ETHER (type=1) interfaces + # and add an entry for each. + for ifname in $(test -d /sys/class/net && ls /sys/class/net) + do + if test -r "/sys/class/net/$ifname/type" -a "$ifname" != "$iface" + then + read iftype <"/sys/class/net/$ifname/type" + case "$iftype" in + 1) echo "#" + echo "# /sys/class/net/$ifname:" + echo "auto $ifname" + echo "iface $ifname inet dhcp";; + esac + fi + done + } >"$config_root/etc/network/interfaces" + # + # Finally rewrite /etc/motd + { echo "Host name: $host" + echo "Domain name: $domain" + echo "Network boot method: $boot" + case "$boot" in + static) echo "Host IP address: $ip";; + esac + echo "Use 'sysconf init' to reset the configuration" + echo "Use 'sysconf save' to save the configuration permanently" + echo "Use 'sysconf restore' to restore a previously saved configuration" + echo "Use 'sysconf help' for more information" + } >"$config_root/etc/motd" +} + +# +# sysconf_save_conffiles <flash-directory> <dest> <list> +# preserve the configuration files in a directory or in a CPIO archive +# (which is *not* compressed). If <dest> is a directory the files are +# copied, otherwise a CPIO archive is made with that name. <list> is +# the listing file giving the preserved files and the processing option. +sysconf_save_conffiles(){ + local ffsdir dest list file + ffsdir="$1" + saved="$2" + list="$3" + test -n "$ffsdir" -a -r "$ffsdir/etc/default/conffiles" -a -n "$saved" -a -n "$list" || { + echo "sysconf_save_conffiles: invalid arguments: '$*'" >&2 + echo " usage sysconf_save_conffiles <flash-directory> <dest> <list>" >&2 + return 1 + } + # + ( cd "$ffsdir" + find etc/*.conf $(sed 's!^/!!' usr/lib/ipkg/info/*.conffiles) ! -type d -newer etc/.configured -print | + sed 's/^/diff /' + exec sed 's/#.*$//;/^[ ]*$/d' etc/default/conffiles + ) | sed 's!^/*!!' | + awk '{ op=$1; $1=""; file[$0]=op } + END{ for (f in file) if (file[f] != "ignore") print file[f] f }' | + while read op file + do + if test -e "$ffsdir/$file" + then + echo "$op $file" >&3 + echo "$file" + fi + done 3>"$list" | ( + cd "$ffsdir" + if test -d "$saved" + then + exec cpio -p -d -m -u "$saved" + else + exec cpio -o -H crc >"$saved" + fi + ) +} + +# +# sysconf_verify file +# this is called with the name of a 'diff' file which is, indeed, +# different and with all the std streams connected to the tty. It +# returns a status code to say whether (0) or not (1) to copy the +# file over. +# +# globals: the following must be defined in the calling context! +# saved: the directory containing the unpacked saved files +# ffsdir: the flash directory to which the files are being restored (/) +# +sysconf_verify_help() { + echo "Please specify how to handle this file or link, the options are as follows," + echo "two character abbreviations may be used:" + echo + echo " keep: retain the old file, overwrite the new flash image file" + echo " upgrade: retain the new file, the old (saved) file is not used" + echo " diff: display the differences between the old and the new using diff -u" + echo " shell: temporarily start an interactive shell (sh -i), exit to continue" + echo " skip: ignore this file for the moment. The file is left in the directory" + echo " $saved and many be handled after this script has completed" +} +# +sysconf_verify() { + local command file + + # return 1 here causes the file not to be overwritten, + # control should never get here! + test -n "$sysconf_noninteractive" && { + echo "$0: $*: changed file cannot be handled non-interactively" >&2 + return 1 + } + + file="$1" + echo "$0: $file: configuration file changed." + sysconf_verify_help "$file" + while : + do + echo -n "option: " + read command + case "$command" in + ke*) return 0;; + up*) rm "$saved/$file" + return 1;; + di*) echo "DIFF OLD($saved) NEW($ffsdir)" + diff -u "$saved/$file" "$ffsdir/$file";; + sh*) PS1="$file: " sh -i;; + sk*) return 1;; + *) sysconf_verify_help "$file";; + esac + done +} +# the same, but for a link +sysconf_verify_link() { + local command link + + # return 1 here causes the file not to be overwritten, + # control should never get here! + test -n "$sysconf_noninteractive" && { + echo "$0: $*: changed link cannot be handled non-interactively" >&2 + return 1 + } + + link="$1" + echo "reflash: $link: configuration link changed." + sysconf_verify_help "$link" + while : + do + echo -n "option: " + read command + case "$command" in + ke*) return 0;; + up*) rm "$saved/$link" + return 1;; + di*) echo "DIFF:" + echo "OLD($saved): $link -> $(readlink "$saved/$link")" + echo "NEW($ffsdir): $link -> $(readlink "$ffsdir/$link")";; + sh*) PS1="$link: " sh -i;; + sk*) return 1;; + *) sysconf_verify_help "$link";; + esac + done +} + +# +# sysconf_restore_conffiles <flash-directory> <source-dir> <restore> +# restore the configuration files from a directory. 'source-dir' +# If <source> is a directory of files from sysconf_save_conffiles. The +# list of files restored is written to the third argument (restore), +# but is not required (/dev/null would be ok). +# +# the list of files to restore is read from stdin, along with the +# processing option for each file (the format is as produced by +# sysconf_save_conffiles in the 'list' output). +sysconf_restore_conffiles(){ + local ffsdir saved restore + # these are the globals used by the above function + ffsdir="$1" + saved="$2" + restore="$3" + test -n "$ffsdir" -a -r "$ffsdir/etc/default/conffiles" -a -d "$saved" -a -n "$restore" || { + echo "restore_conffiles: invalid arguments: '$*'" >&2 + echo " usage sysconf_restore_conffiles <flash-directory> <source-dir> <list>" >&2 + return 1 + } + # + # read the list and process each given file + while read op file + do + # handle .configured specially (to preserve the original datestamp) + if test "$file" = "etc/.configured" + then + # this should definately not fail because of the test above! + if cp -a "$saved/$file" "$ffsdir/$file" + then + echo "$file" >&3 + else + echo "sysconf_restore_conffiles: $file: timestamp copy failed (ignored)" >&2 + fi + elif test -h "$saved/file" -o -h "$ffsdir/$file" + then + # new or old symbolic link + if test -h "$saved/$file" -a -h "$ffsdir/$file" && + test "$(readlink "$saved/$file")" = "$(readlink "$ffsdir/$file")" + then + # no change + echo "$file" >&3 + else + # assume a change regardless + case "$op" in + preserve) + echo "$file" + echo "$file" >&3;; + diff) # need user input + if sysconf_verify_link "$file" <>/dev/tty >&0 2>&0 + then + echo "$file" + echo "$file" >&3 + fi;; + esac + fi + else + # only overwrite if necessary + if test -e "$ffsdir/$file" && cmp -s "$saved/$file" "$ffsdir/$file" + then + # do not overwrite + echo "$file" >&3 + elif test ! -e "$ffsdir/$file" + then + # always preserve + echo "$file" + echo "$file" >&3 + else + case "$op" in + preserve) + echo "$file" + echo "$file" >&3;; + diff) # the files are different, get user input + if sysconf_verify "$file" <>/dev/tty >&0 2>&0 + then + echo "$file" + echo "$file" >&3 + fi;; + esac + fi + fi + done 3>"$restore" | (cd "$saved"; exec cpio -p -d -u "$ffsdir") +} + +# +# sysconf_test_restore <flash-directory> <source-dir> +# return true only if the restore does not need to do an interactive +# compare +sysconf_test_restore(){ + local ffsdir saved + # these are the globals used by the above function + ffsdir="$1" + saved="$2" + # this is an error case, but return 0 so that the error is + # detected later + test -n "$ffsdir" -a -r "$ffsdir/etc/default/conffiles" -a -d "$saved" || + return 0 + # + # read the list and check each diff file (this is just a copy of the + # logic above with all the work removed!) + while read op file + do + # handle .configured specially (to preserve the original datestamp) + if test "$op" != diff + then + : # no diff required + elif test "$file" = "etc/.configured" + then + : # special handling + elif test -h "$saved/file" -o -h "$ffsdir/$file" + then + # new or old symbolic link + if test -h "$saved/$file" -a -h "$ffsdir/$file" && + test "$(readlink "$saved/$file")" = "$(readlink "$ffsdir/$file")" + then + : # no change + else + # assume a change regardless + return 1 + fi + else + # only overwrite if necessary + if test -e "$ffsdir/$file" && cmp -s "$saved/$file" "$ffsdir/$file" + then + : # do not overwrite + elif test ! -e "$ffsdir/$file" + then + : # always preserve + else + # a change + return 1 + fi + fi + done + + return 0 +} + +# +# mountflash "flash device" "flash root directory" {mount options} +# Finds and mounts the flash file system on the given directory +mountflash(){ + local ffsdev ffsdir + + ffsdev="$1" + test -n "$ffsdev" -a -b "$ffsdev" || { + echo "$0: unable to find flash file system to copy ($ffsdev)" >&2 + return 1 + } + shift + + ffsdir="$1" + test -n "$ffsdir" -a -d "$ffsdir" || { + echo "$0: mountflash $ffsdir: not a directory (internal error)" >&2 + return 1 + } + shift + + mount -t jffs2 "$@" "$ffsdev" "$ffsdir" || { + echo "$0: $ffsdev: unable to mount flash file system on $ffsdir" >&2 + return 1 + } + return 0 +} + +# +# umountflash [-r] "flash device" +# unmount any instance of the given flash device, if -r is specified a mount on +# root is an error, otherwise a mount on root is ignored (and remains). +umountflash(){ + local rootok ffsno ffsdev + rootok=1 + case "$1" in + -r) rootok= + shift;; + esac + # + # The argument is ffsdev + ffsdev="$1" + ffsno="$(devio "<<$ffsdev" prd)" + test -n "$ffsno" -a "$ffsno" -ge 0 || { + echo "$0: $ffsdev: device number $ffsno is not valid, cannot continue." >&2 + return 1 + } + # + # Make sure that Flashdisk isn't mounted on / + if test -z "$rootok" -a "$(devio "<</etc/init.d/sysconfsetup" prd)" -eq "$ffsno" + then + echo "$0: $ffsdev is mounted on /, unable to continue" >&2 + return 1 + fi + # + # The function is currently always used interactively, so output + echo "$0: umounting any existing mount of $ffsdev" >&2 + # + # check each mount point, do this last first because otherwise nested + # mounts of ffsdev cannot be umounted. + ffs_umount() { + local device mp type options stuff + + read device mp type options stuff + test -z "$device" && return 0 + + # handle following entries first + ffs_umount || return 1 + + # handle this entry, since this is currently only used for unmounting + # the flash root partition we know a file which must exist... + case "$mp/$type" in + //jffs2);; # skip / + */jffs2)test "$(devio "<<$mp/etc/init.d/sysconfsetup" prd 2>/dev/null)" -ne "$ffsno" || + umount "$mp" || { + echo "$0: $mp: unable to umount $ffsdev" >&2 + return 1 + };; + esac + + return 0 + } + # + ffs_umount </proc/mounts || { + echo "$0: umount $ffsdev from all mount points then re-run $0" >&2 + return 1 + } + + return 0 +} + +# +# sysconf_save +# save the system configuration to $syspart - $syspart must exist and +# there must be a writeable device for it. +sysconf_save(){ + local sysdev ffsdev ffsdir saved list size status + ffsdev="$(mtblockdev $ffspart)" + sysdev="$(mtblockdev $syspart)" + status=1 + if test -n "$sysdev" -a -b "$sysdev" -a -n "$ffsdev" -a -b "$ffsdev" + then + # this will succeed silently if the flash device is on / + umountflash "$ffsdev" || exit 1 + # + # Everything is umounted, now remount on a temporary directory. + ffsdir="/tmp/flashdisk.$$" + mkdir "$ffsdir" || { + echo "$0: $ffsdir: failed to create temporary directory" >&2 + exit 1 + } + # + mountflash "$ffsdev" "$ffsdir" -o ro || { + rmdir "$ffsdir" + exit 1 + } + # need temporary files for the cpio output and the listing + saved=/tmp/cpio.$$ + list=/tmp/preserve.$$ + rm -rf "$saved" "$list" + sysconf_save_conffiles "$ffsdir" "$saved" "$list" || { + echo "$0: $saved: archive of saved configuration files failed" >&2 + rm -rf "$saved" + rm "$list" + umount "$ffsdir" && rmdir "$ffsdir" || + echo "$0: $ffsdir: temporary directory cleanup failed" >&2 + return 1 + } + # ignore the error in this case: + umount "$ffsdir" && rmdir "$ffsdir" || + echo "$0: $ffsdir: temporary directory cleanup failed" >&2 + # + # we now have: + # /etc/default/sysconf the basic config + # /tmp/preserve.$$ the list of saved files + # /tmp/cpio.$$ the CPIO archive of those files + # + # make one big file with the sysconf data followed by the + # compressed archive in /tmp/sysconf.$$ + { { cat /etc/default/sysconf + echo '[preserve]' + } | sed -n '1,/^\[preserve\]^/p' + while read op file + do + echo "$op"="$file" + done <"$list" + } >/tmp/sysconf.$$ + size="$(devio "<</tmp/sysconf.$$" 'pr$')" + gzip -9 <"$saved" >>/tmp/sysconf.$$ + # + # more cleanup, then try to write the new sysconf to $syspart + # the format is a 4 byte big-endian length then the text data + # if the data won't fit exit with error code 7 + rm "$saved" "$list" + devio -p "<</tmp/sysconf.$$" ">>$sysdev" ' + $( $4+ # > + !! 7 + $) 0 + wb '"$size"',4 + cp $' + case $? in + 0) echo " done" >&2 + status=0;; + 1) echo " failed" >&2 + echo " $syspart could not be written (no changes made)" >&2;; + 3) echo " failed" >&2 + echo " $syspart partially written, you may want to reset it" >&2;; + 7) echo " failed" >&2 + echo " $syspart is too small: $size bytes required" >&2 + echo " No change made" >&2;; + *) echo " failed" >&2 + echo " Internal error writing $syspart" >&2;; + esac + # + rm -f /tmp/sysconf.$$ + else + echo "sysconf save: $syspart or $ffspart partition not found" >&2 + echo " A RedBoot partition named '$syspart' must exist in the system" >&2 + echo " flash memory for this command to work, and there must be a" >&2 + echo " block device to access this partition (udev will normally" >&2 + echo " create this automatically. The flash partition contents must" >&2 + echo " also be accessible in a partition called '$ffspart'" >&2 + echo + echo " To create the $syspart partition use the 'fis create' command" >&2 + echo " in the RedBoot boot loader, it is sufficient to make the" >&2 + echo " partition one erase block in size unless you have substantially" >&2 + echo " increased the size of the files listed in /etc/default/conffiles" >&2 + fi + + return $status +} + +# +# sysconf_restore [auto] +# restore previously saved configuration information from $syspart +sysconf_restore_error(){ + local root + root="$1" + shift + # ------------------------------------------------------------------------------- + { echo " WARNING: saved configuration files not restored" + test -n "$1" && echo "$*" + echo + echo "The configuration of this machine has been reinitialised using the values" + echo "from /etc/default/sysconf, however configuration files saved in the $syspart" + echo "partition have not been restored." + echo + echo "You can restore these files by correcting any reported errors then running" + echo + echo " sysconf restore" + echo + echo "from the command line. This will completely reinitialise the configuration" + echo "using the information in the $syspart partition." + } >"$root/etc/motd" + cat "$root/etc/motd" >&2 +} +# +sysconf_restore(){ + local sysdev ffsdev ffsdir saved restore size status sysconf_noninteractive config_root + + # if set this means 'do no diff' - this avoids the code above which + # would open /dev/tty and therefore allows this stuff to be done from + # an init script + sysconf_noninteractive= + test "$1" = auto && sysconf_noninteractive=1 + + ffsdev="$(mtblockdev $ffspart)" + sysdev="$(mtblockdev $syspart)" + status=1 + if test -n "$sysdev" -a -b "$sysdev" -a -n "$ffsdev" -a -b "$ffsdev" && + sysconf_valid + then + # this will succeed silently if the flash device is on / + umountflash "$ffsdev" || exit 1 + # + # Everything is umounted, now remount on a temporary directory. + ffsdir="/tmp/flashdisk.$$" + config_root="$ffsdir" + mkdir "$ffsdir" || { + echo "$0: $ffsdir: failed to create temporary directory" >&2 + exit 1 + } + # + mountflash "$ffsdev" "$ffsdir" || { + rmdir "$ffsdir" + exit 1 + } + # + # first restore the $syspart section + sysconf_read "$ffsdir" || sysconf_default "$ffsdir" + # + # now use this to regenerate the system files + sysconf_reload "$ffsdir" + # + # now examine the [preserve] section, if it is there restore + # it if possible. + if test -n "$(syssection preserve)" + then + # 'saved' is a directory, 'restore' is a file (which is + # used to detect unrestored files). The directory needs + # to be populated with files. + saved=/tmp/cpio.$$ + restore=/tmp/restore.$$ + rm -rf "$saved" "$restore" + # + mkdir "$saved" || { + sysconf_restore_error "$ffsdir" "$saved: failed to create temporary directory" + return 1 + } + # + # the CPIO archive is gzip compressed after the text part + # of sysconf, gzip will handle the LZ stream termination + # correctly (and break the pipe) so we don't need to know + # the real length of the data + devio "<<$sysdev" '<=b4+.' 'cp $s-' | gunzip | ( + cd "$saved" + exec cpio -i -d -m -u + ) || { + rm -rf "$saved" + sysconf_restore_error "$ffsdir" "$saved: cpio -i failed" + return 1 + } + # either there must be no 'diff' files or it must + # be possible to interact with a real user. + if test -z "$sysconf_noninteractive" || + syssection preserve | sysconf_test_restore "$ffsdir" "$saved" + then + # + # remove the 'init' motd from sysconf_reload + rm "$ffsdir/etc/motd" + # + # now restore from the directory, using the information in + # the preserve section, if this fails in a non-interactive + # setting the system might not reboot + syssection preserve | + sysconf_restore_conffiles "$ffsdir" "$saved" "$restore" || { + # there is a chance of the user cleaning this up +#------------------------------------------------------------------------------ + sysconf_restore_error "$ffsdir" \ +"$0: $saved: restore of saved configuration files failed. + The flash file system is mounted on $ffsdir. + The saved files are in $saved and the list of files selected for + restore is in $restore. + You should restore any required configuration from $saved, then umount + $ffsdir and reboot." + # this prevents cleanup/umount + return 1 + } + # + # remove the copied files (i.e. the ones which were preserved) + ( cd "$saved" + exec rm $(cat "$restore") + ) + rm "$restore" + # + # clean up, files left in $saved need to be handled by the user + files="$(find "$saved" ! -type d -print)" + if test -n "$files" + then +#------------------------------------------------------------------------------ + sysconf_restore_error "$ffsdir" \ +"$0: some saved configuration files have not been handled: + +$files + +These files can be examined in $saved and restored to +$ffsdir if required. The saved files are in a temporary +directory and will not be retained across a reboot - copy then elsewhere if +you are unsure whether they are needed." + return 1 + fi + # + # so this is safe now (no files, links etc) + rm -rf "$saved" + else + rm -rf "$saved" + # non-interactive and some changed diff files + sysconf_restore_error "$ffsdir" \ +"$0: some of the saved configuration files must be +examined before restoration" + # but continue to the umount + fi + fi + # + # ignore the error in this case: + umount "$ffsdir" && rmdir "$ffsdir" || + echo "$0: $ffsdir: temporary directory cleanup failed" >&2 + status=0 + else + echo "sysconf restore: $syspart or $ffspart partition not found" >&2 + echo " You must have used 'sysconf save' to save configuration data" >&2 + echo " into the $syspart partition before using this command. The command" >&2 + echo " will restore the configuration data to the flash root partition" >&2 + echo " named '$ffspart' - this must also be accessible." >&2 + fi + + return $status +} + +# +# read_one 'prompt' 'group' 'name' +# read a single value +read_one() { + local n o + o="$(sysval "$2" "$3")" + echo -n "$1 [$o]: " >/dev/tty + read n </dev/tty + test -z "$n" && n="$o" + eval "$3='$n'" +} + +# +# sysconf_init +# Change the network initialisation +sysconf_init() { + read_one "Host name" network disk_server_name + read_one "Domain name" network w_d_name + read_one "Boot protocol (dhcp|static)" network bootproto + case "$bootproto" in + static) read_one "IP address" network ip_addr + read_one "IP netmask" network netmask + read_one "IP gateway" network gateway + read_one "First DNS server" network dns_server1 + read_one "Second DNS server" network dns_server2 + read_one "Third DNS server" network dns_server3 + echo "$ip_addr $disk_server_name" >> /etc/hosts + ;; + dhcp) sed -i -e "s/localhost\$/localhost $disk_server_name/" /etc/hosts + ;; + *) bootproto=dhcp;; + esac + # + # The other stuff which cannot be changed + lan_interface="$(config iface)" + # + # Write this out to a new sysconf + { echo "[network]" + echo "lan_interface=$lan_interface" + test -n "$disk_server_name" && echo "disk_server_name=$disk_server_name" + test -n "$w_d_name" && echo "w_d_name=$w_d_name" + echo "bootproto=$bootproto" + case "$bootproto" in + static) echo "ip_addr=$ip_addr" + test -n "$netmask" && echo "netmask=$netmask" + test -n "$gateway" && echo "gateway=$gateway" + test -n "$dns_server1" && echo "dns_server1=$dns_server1" + test -n "$dns_server2" && echo "dns_server2=$dns_server2" + test -n "$dns_server3" && echo "dns_server3=$dns_server3" + ;; + esac + } >/etc/default/sysconf + # + # And reload the result + sysconf_reload + # + # The remove the spurious 'init' motd + rm /etc/motd +} + +# +# sysconf_help +# help text +sysconf_help(){ + # ------------------------------------------------------------------------------- + echo "sysconf: usage: sysconf init|read|default|reload|save|restore" >&2 + echo " init: initialise network information when DHCP is not available" >&2 + echo " read: the current $syspart partition is read into /etc/default/sysconf" >&2 + echo " default: a default /etc/default/sysconf is created" >&2 + echo " reload: system configuration files are recreated from /etc/default/sysconf" >&2 + echo " save: /etc/default/sysconf and the files listed in /etc/default/conffiles" >&2 + echo " are written to the $syspart partition" >&2 + echo " restore: the configuration information in the $syspart partition saved by" >&2 + echo " 'sysconf save' is restored" >&2 +} + +# +# the real commands +sysconf_command="$1" +test $# -gt 0 && shift +case "$sysconf_command" in +init) sysconf_init "$@";; +read) sysconf_read "$@";; +default)sysconf_default "$@";; +reload) sysconf_reload "$@";; +save) sysconf_save "$@";; +restore)sysconf_restore "$@";; +valid) sysconf_valid "$@";; + +sysconf)# just load the functions + ;; + +*) # help text + sysconf_help "$@";; +esac diff --git a/packages/sysconf/files/sysconfsetup b/packages/sysconf/files/sysconfsetup new file mode 100644 index 0000000000..7b91a0e331 --- /dev/null +++ b/packages/sysconf/files/sysconfsetup @@ -0,0 +1,59 @@ +#!/bin/sh +# This script is run once when the system first boots. Its sole +# purpose is to create /etc/default/sysconf (the overall system +# configuration file) and other files derived from this. +# +# The script runs immediately after S10checkroot.sh - this is the +# point at which the rootfs will be mounted rw even if the kernel +# booted with it ro. +# +# rm or mv the file (/etc/default/sysconf) to recreate it, run this +# script with the reload option to overwrite the system files. The +# configuration files described in sysconf_reload (in +# /sbin/sysconf) will be overwritten on reload. +# +# start: standard startup, do a complete (auto) restore if necessary +# reinit: always do a complete auto restore +# reload: just reload sysconf (no config files!) +# +# /etc/default/functions contains useful utility functions - it's +# in a separate file so that it can be loaded by any script +# load_functions "source" +# load the functions in '/sbin/source' - relies on /sbin/source being +# a shell script and having support for this function. + +# load_functions "source" +# load the functions in '/sbin/source' - relies on /sbin/source being +# a shell script and having support for this function. +load_functions(){ + test -n "$1" -a -x "/sbin/$1" && . "/sbin/$1" || { + echo "$0: /sbin/$1: script not found" >&2 + return 1 + } +} + +load_functions sysconf || exit 1 + +case "$1" in +start) test -s /etc/default/sysconf || { + if sysconf_read + then + if sysconf_valid + then + sysconf_restore auto + else + sysconf_reload + fi + else + sysconf_default + sysconf_reload + fi + };; + +reload) test -s /etc/default/sysconf || sysconf_read || sysconf_default + sysconf_reload;; + +reinit) sysconf_restore auto;; + +*) ;; +esac diff --git a/packages/sysconf/sysconf_0.1.bb b/packages/sysconf/sysconf_0.1.bb new file mode 100644 index 0000000000..fd37cd01f7 --- /dev/null +++ b/packages/sysconf/sysconf_0.1.bb @@ -0,0 +1,52 @@ +DESCRIPTION = "System Configuration save/restore functionality" +SECTION = "base" +PRIORITY = "optional" +LICENSE = "GPL" +RDEPENDS = "devio cpio findutils diffutils" +PR = "r8" + +# Currently, the scripts only support ixp4xx machines. +# Feel free to add to the scripts ... +COMPATIBLE_MACHINE = '(ixp4xx|nslu2)' + +SRC_URI = "file://sysconfsetup \ + file://conffiles \ + file://sysconf \ + " + +SCRIPTS = "sysconf" +INITSCRIPTS = "sysconfsetup" + +# This just makes things easier... +S="${WORKDIR}" + +do_install() { + # Directories + install -d ${D}${sysconfdir} \ + ${D}${sysconfdir}/default \ + ${D}${sysconfdir}/init.d \ + ${D}${base_sbindir} + + # Init scripts + install -m 0755 sysconfsetup ${D}${sysconfdir}/init.d/ + + # Configuration files + install -m 0644 conffiles ${D}${sysconfdir}/default + + # Shell scripts + install -m 0755 sysconf ${D}${base_sbindir}/sysconf +} + +pkg_postinst() { + opt= + test -n "$D" && opt="-r $D" + update-rc.d $opt sysconfsetup start 12 S . +} + +pkg_postrm() { + opt= + test -n "$D" && opt="-r $D" + update-rc.d $opt sysconfsetup remove +} + +CONFFILES = "${sysconfdir}/default/conffiles" |