***
Changes from v1:
1) everything outlined in my response to eblake's review
2) changed the names of the functions to match those in the netcf API.
***
netcf-transaction is a shell script that can take a snapshot of
network config files, and later either commit the changes made (by
deleting the snapshot) or revert back to the original config. If
uncommitted changes exist at boottime, netcf-transaction will
automatically revert them.
Available commands:
change-begin: Save a copy of the current network interface config
change-commit: Delete the saved snapshot
change-rollback: Archive the current state of config into a rollback
directory, and copy the original snapshotted state
back into place
snapshot-dir: Return the path to the snapshot directory.
start, restart, reload, force-reload, condrestart, and try-restart are
all synomyms for snapshot-config.
---
configure.ac | 17 +++
netcf.spec.in | 1 +
src/Makefile.am | 34 ++++++-
src/netcf-transaction.init.sh | 223 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 274 insertions(+), 1 deletions(-)
create mode 100755 src/netcf-transaction.init.sh
diff --git a/configure.ac b/configure.ac
index 9fd8607..ddcde3a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -57,6 +57,23 @@ if test "$prefix" = "/usr" && test
"$sysconfdir" = '${prefix}/etc' ; then
sysconfdir='/etc'
fi
+dnl
+dnl init script flavor
+dnl
+AC_MSG_CHECKING([for init script flavor])
+AC_ARG_WITH([init-script],
+ [AC_HELP_STRING([--with-init-script=@<:@redhat|auto|none@:>@],
+ [Style of init script to install @<:@default=auto@:>@])])
+if test "x$with_init_script" = "x" || test
"x$with_init_script" = "xauto"; then
+ if test "$prefix" = "$HOME" || test "$cross_compiling"
= yes || test ! -f /etc/redhat-release; then
+ with_init_script=none
+ else
+ with_init_script=redhat
+ fi
+fi
+AM_CONDITIONAL([NETCF_INIT_SCRIPT_RED_HAT], test x$with_init_script = xredhat)
+AC_MSG_RESULT($with_init_script)
+
AC_OUTPUT(Makefile \
gnulib/lib/Makefile \
gnulib/tests/Makefile \
diff --git a/netcf.spec.in b/netcf.spec.in
index 06ddcf0..a41e49d 100644
--- a/netcf.spec.in
+++ b/netcf.spec.in
@@ -64,6 +64,7 @@ rm -rf $RPM_BUILD_ROOT
%defattr(-,root,root,-)
%{_datadir}/netcf
%{_libdir}/*.so.*
+%{_sysconfdir}/rc.d/init.d/netcf-transaction
%doc AUTHORS COPYING NEWS
%files devel
diff --git a/src/Makefile.am b/src/Makefile.am
index 9d8a3d7..5595581 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -14,7 +14,8 @@ bin_PROGRAMS = ncftool
noinst_PROGRAMS = ncftransform
EXTRA_DIST = netcf_public.syms \
- netcf_private.syms
+ netcf_private.syms \
+ netcf-transaction.init.sh
if NETCF_DRIVER_INITSCRIPTS
DRIVER_SOURCES = drv_initscripts.c dutil.h dutil.c \
@@ -57,4 +58,35 @@ internal.h: datadir.h
datadir.h: $(top_builddir)/config.status
echo '#define DATADIR "$(datadir)"' > datadir.h
+install-data-local: install-init
+
+uninstall-local: uninstall-init
+
+if NETCF_INIT_SCRIPT_RED_HAT
+# This is for the initscript that handles network config change
+# transactions.
+install-init: netcf-transaction.init
+ mkdir -p $(DESTDIR)$(sysconfdir)/rc.d/init.d
+ $(INSTALL_SCRIPT) netcf-transaction.init \
+ $(DESTDIR)$(sysconfdir)/rc.d/init.d/netcf-transaction
+
+uninstall-init:
+ rm -f $(DESTDIR)$(sysconfdir)/rc.d/init.d/netcf-transaction \
+ $(DESTDIR)$(sysconfdir)/sysconfig/netcf-transaction
+
+BUILT_SOURCES += netcf-transaction.init
+
+netcf-transaction.init: netcf-transaction.init.sh $(top_builddir)/config.status
+ $(AM_V_GEN)sed \
+ -e 's!\@localstatedir\@!$(localstatedir)!g' \
+ -e 's!\@sysconfdir\@!$(sysconfdir)!g' \
+ < $< > $@-t && \
+ chmod a+x $@-t && \
+ mv $@-t $@
+else
+install-init:
+uninstall-init:
+netcf-transaction.init:
+endif # NETCF_INIT_SCRIPT_RED_HAT
+
DISTCLEANFILES += $(BUILT_SOURCES)
diff --git a/src/netcf-transaction.init.sh b/src/netcf-transaction.init.sh
new file mode 100755
index 0000000..ebe8d50
--- /dev/null
+++ b/src/netcf-transaction.init.sh
@@ -0,0 +1,223 @@
+#!/bin/sh
+#
+# netcf-transaction: save/restore current network interface configuration
+#
+# chkconfig: - 09 91
+# description: This script can save the current state of network config, \
+# and later revert to that config, or commit the new config \
+# (by deleting the snapshot). At boottime, if there are \
+# uncommitted changes to the network config, they are \
+# reverted (and the discarded changes are archived in \
+# /var/lib/netcf/network-rollback-*).
+
+### BEGIN INIT INFO
+# Provides: netcf-transaction
+# Required-Start: $local_fs
+# Short-Description: save/restore network configuration files
+# Description: This script can save the current state of network config,
+# and later revert to that config, or commit the new config
+# (by deleting the snapshot). At boottime, if there are
+# uncommitted changes to the network config, they are
+# reverted (and the discarded changes are archived in
+# /var/lib/netcf/network-rollback-*).
+#
+### END INIT INFO
+
+sysconfdir="@sysconfdir@"
+localstatedir="@localstatedir@"
+
+netconfdir="$sysconfdir"/sysconfig/network-scripts
+snapshotdir="$localstatedir"/lib/netcf/network-snapshot
+rollbackdirbase="$localstatedir"/lib/netcf/network-rollback
+
+# Source function library.
+test ! -r "$sysconfdir"/rc.d/init.d/functions ||
+ . "$sysconfdir"/rc.d/init.d/functions
+
+# take a snapshot of current network configuration scripts
+change_begin ()
+{
+ if test -e "$snapshotdir"
+ then
+ echo "snapshot directory $snapshotdir already exists"
+ return 1
+ fi
+ if ! mkdir -p "$snapshotdir"
+ then
+ echo "failed to create snapshot directory $snapshotdir"
+ return 1
+ fi
+ for f in "$netconfdir"/ifcfg-* "$netconfdir"/route-* \
+ "$netconfdir"/rule-*
+ do
+ test ! -f "$f" && continue
+ if ! cp -p "$f" "$snapshotdir"
+ then
+ echo "failed to copy $f to $snapshotdir"
+ return 1
+ fi
+ done
+}
+
+change_commit ()
+{
+ rm -rf "$snapshotdir"
+}
+
+# rollback network configuration to last snapshot (if one exists)
+change_rollback ()
+{
+ # return immediately if there is no snapshot
+ test ! -d "$snapshotdir" && return 0
+
+ rollback_ret=0
+
+ # eliminate all but the last 20 rollback saves
+ LC_ALL=C ls -d "$rollbackdirbase"-* 2>/dev/null | head -n -20 |\
+ while read r
+ do
+ if ! rm -rf "$r"
+ then
+ # indicate an error, but continue
+ echo "Failed to remove obsolete rollback directory $r"
+ rollback_ret=1
+ fi
+ done
+
+ # save a copy of the current config before rollback "just in case"
+ rollbackdir=$rollbackdirbase-$(date +%Y.%m.%d-%H:%M:%S)
+ if ! mkdir -p "$rollbackdir"
+ then
+ echo "failed to create rollback directory $rollbackdir"
+ return 1
+ fi
+ for f in "$netconfdir"/ifcfg-* "$netconfdir"/route-* \
+ "$netconfdir"/rule-*
+ do
+ test ! -f "$f" && continue
+ if ! cp -p "$f" "$rollbackdir"
+ then
+ echo "failed to copy $f to $rollbackdir"
+ return 1
+ fi
+ done
+
+ # There are 4 classes of files, each handled slightly differently to minimize
+ # disruption in services:
+ # 1) file in both, unmodified - just erase the snapshot copy
+ # 2) file in both, modified - copy the snapshot version over the current &
erase copy
+ # 3) file in current only - remove the file
+ # 4) file in snapshot only - copy the snapshot file to current
+ #
+ # We handle the 1st three cases in one loop going through all current config
+ # files, and what is left over in snapshotdir is, by definition, case 4.
+ #
+ # (NB: we can't mv the files, because then the selinux labels
+ # don't get reset properly.)
+
+ for f in "$netconfdir"/ifcfg-* "$netconfdir"/route-* \
+ "$netconfdir"/rule-*
+ do
+ test ! -f "$f" && continue
+
+ snapshotf=$snapshotdir/${f##*/}
+ if test -f "$snapshotf"
+ then
+ # Case (1) & (2) (only copies if they're different)
+ if ! cmp -s "$snapshotf" "$f"
+ then
+ if ! cp -pf "$snapshotf" "$f"
+ then
+ echo "failed to restore $snapshotf to $f"
+ return 1
+ fi
+ fi
+ if ! rm -f "$snapshotf"
+ then
+ # indicate an error, but continue
+ echo "Failed to remove obsolete snapshot file $snapshotf"
+ rollback_ret=1
+ fi
+ else
+ # Case (3)
+ if ! rm -f "$f"
+ then
+ # indicate an error, but continue
+ echo "Failed to remove unwanted config file $f"
+ rollback_ret=1
+ fi
+ fi
+ done
+
+ # Case (4)
+ for f in "$snapshotdir"/ifcfg-* "$snapshotdir"/route-* \
+ "$snapshotdir"/rule-*
+ do
+ test ! -f "$f" && continue
+ if ! cp -pf "$f" "$netconfdir"
+ then
+ echo "failed to restore $f to $netconfdir"
+ return 1
+ fi
+ done
+
+ if ! rm -rf "$snapshotdir"
+ then
+ # indicate an error, but continue
+ echo "Failed to remove obsolete snapshot directory $snapshotdir"
+ rollback_ret=1
+ fi
+
+ return $rollback_ret
+}
+
+# usage [val]
+# Display usage string, then exit with VAL (defaults to 2).
+usage() {
+ echo "Usage: $0
{change-begin|change-commit|change-rollback|snapshot-dir|start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
+ exit ${1-2}
+}
+
+# See how we were called.
+if test $# != 1; then
+ usage
+fi
+
+retval=0
+case "$1" in
+ # commands required in all Fedora initscripts
+ start|restart|reload|force-reload|condrestart|try-restart)
+ echo -n $"Running $prog $1: "
+ change_rollback
+ retval=$?
+ echo
+ ;;
+ stop|status)
+ ;;
+
+ --help)
+ usage 0
+ ;;
+ # specific to netcf-transaction
+ change-begin)
+ change_begin
+ retval=$?
+ ;;
+ change-commit)
+ change_commit
+ retval=$?
+ ;;
+ change-rollback)
+ change_rollback
+ retval=$?
+ ;;
+ snapshot-dir)
+ echo $snapshotdir
+ ;;
+ *)
+ usage
+ retval=2
+ ;;
+esac
+
+exit $retval
--
1.7.3.4