#!/bin/sh

SET_FILE=/etc/sysconfig/ipsets
TMP_FIFO=/var/run/ipset-persistent


info_msg=""

restore_ipsets()
{
	critical_error_occured=0
	
	
	#determine the set names in use and how they change
	mkfifo -m go= "${TMP_FIFO}"
	if [ $? -ne 0 ]; then #error handling
		info_msg="$0 Warning: Could not create FIFO. No sets were modified.\n" >&2
		return 1
	fi
	
	
	#Node: The “-u” in the sort is required because of a bug in ipset (see http://marc.info/?l=netfilter&m=135601993725177&w=2).
	old_sets="$(ipset list -name  |  sort -u)"
	new_sets="$(grep ^create "${SET_FILE}"  |  cut -d " " -f 2   |  sort -u)"
	
	kept_sets="$(  printf "%s\n" "${old_sets}" > "${TMP_FIFO}"  &
	               printf "%s\n" "${new_sets}"  |  comm -12 "${TMP_FIFO}" -
                  )"
	dropped_sets="$(  printf "%s\n" "${old_sets}" > "${TMP_FIFO}"  &
	               printf "%s\n" "${new_sets}"  |  comm -23 "${TMP_FIFO}" -
                  )"
	
	
	rm -f "${TMP_FIFO}"
	
	
	#merge new and temporary sets
	kept_sets_pattern="$( printf "%s\n" "${kept_sets}"  |  sed ':a;N;$!ba;s/\n/\\|/g' )"
	sed "s/^\(create\|add\)\([[:space:]][[:space:]]*\)\(${kept_sets_pattern}\)\([[:space:]][[:space:]]*\)/\1\2_tmp_\3\4/" "${SET_FILE}"  |  ipset restore  2> /dev/null
	if [ $? -ne 0 ]; then #error handling
		info_msg="$0 Error: Could not merge new and temporary ipsets. ipsets may have been mangled.\n" >&2
		return 1
	fi
	
	
	#swap old kept sets with their temporary version and delete the later afterwards
	for set in ${kept_sets}; do
		ipset swap "${set}" "_tmp_${set}"  2> /dev/null
		if [ $? -ne 0 ]; then #error handling
			critical_error_occured=1
		fi
		
		ipset flush "_tmp_${set}"  2> /dev/null
		if [ $? -ne 0 ]; then #error handling
			critical_error_occured=1
		fi
		
		ipset destroy "_tmp_${set}"  2> /dev/null
		if [ $? -ne 0 ]; then #error handling
			critical_error_occured=1
		fi
	done
	
	
	#try to delete dropped sets
	#Note: This must happen after swaping the old kept sets with their temporary versions and the deletion of the later ones.
	#Note: All sets must be flushed, before they are destroyed.
	for set in ${dropped_sets}; do
		ipset flush "${set}"  2> /dev/null
		if [ $? -ne 0 ]; then #error handling
			critical_error_occured=1
		fi
	done
	for set in ${dropped_sets}; do
		ipset destroy "${set}"  2> /dev/null
		if [ $? -ne 0 ]; then #error handling
			critical_error_occured=1
		fi
	done
	
	
	#check for errors
	if [ "${critical_error_occured}" -ne 0 ]; then
		info_msg="$0 Error: An error occured during loading the new ipsets or dropped sets were still in use by iptables. ipsets may have been mangled.\n" >&2
		return 1
	fi
	
	
	return 0
}
















#Copyright © 2012, Christoph Anton Mitterer <mail@christoph.anton.mitterer.name>.
#All rights reserved.
#
#
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program.  If not, see <http://www.gnu.org/licenses/>.
