Release new version 0.20.2
[grml-autoconfig.git] / bin / save-config
1 #!/bin/zsh
2 # Filename:      save-config
3 # Purpose:       generate grml configuration archive and store it anywhere
4 # Authors:       grml-team (grml.org), (c) Michael Prokop <mika@grml.org>
5 # Bug-Reports:   see http://grml.org/bugs/
6 # License:       This file is licensed under the GPL v2.
7 ################################################################################
8
9 # some zsh-stuff {{{
10   autoload colors ; colors
11   setopt nonomatch
12   . /etc/grml/sh-lib
13   . /etc/grml/script-functions
14 # }}}
15
16 # set variables  {{{
17   LANG=C
18   LC_ALL=C
19   [[ $UID != 0 ]] && runas='sudo' # important for /etc
20
21   if [ -d /run/live/overlay/rw ] ; then # since Dec 2018
22     CHANGE_DIR='/run/live/overlay/rw'
23   elif [ -d /lib/live/mount/overlay/rw ] ; then # 2012 until Dec 2018, backwards compatibility
24     CHANGE_DIR='/lib/live/mount/overlay/rw'
25   else
26     echo "Error: no overlay directories found (like /lib/live/mount/overlay or /live/overlay)." >&2
27     bailout; exit 1
28   fi
29
30   if [ -d /run/live/rootfs ] ; then
31     ORIG_DIR="$(find /run/live/rootfs/ -maxdepth 1 -name \*.squashfs | head -1)"
32   elif [ -d /lib/live/mount/rootfs ] ; then
33     ORIG_DIR="$(find /lib/live/mount/rootfs/ -maxdepth 1 -name \*.squashfs | head -1)"
34   else
35     echo "Error: no rootfs directories found in '/run/live/rootfs' or '/lib/live/mount/rootfs'." >&2
36     bailout; exit 1
37   fi
38
39   check4progs mutt &>/dev/null || echo "Warning, mutt not available for mail handling.">&2
40   check4progs tar || { echo "Sorry, can't continue. Exiting.">&2 ; bailout ; exit 1 }
41
42   CONFIG=/etc/grml/saveconfig
43   [ -r "$CONFIG" ] && . $CONFIG
44
45   PROGRAMNAME=${0##*/}
46   HOSTNAME=$(hostname)
47   DATE=$(date)
48   GRML_VERSION=$(awk '{print $1}' /etc/grml_version 2>/dev/null || print "not a grml system")
49   KERNEL=$(uname -a)
50
51   TMPDIR='/tmp'
52   MAILFILE="${TMPDIR}/mail.txt"
53
54   [ -n "$FILELIST" ] || FILELIST=$(mktemp $TMPDIR/filelist.XXXXXX)
55 # }}}
56
57 # functions {{{
58 debug(){
59   if [[ $DEBUG -gt 0 ]] ; then
60     echo "debug: $*"
61   fi
62 # setopt xtrace
63 # set -x
64 }
65
66 bailout(){
67   rm -f "$FILELIST"
68   rm -f "$MAILFILE"
69 }
70
71 trap bailout 1 2 3 15
72
73 findchanged() {
74   if [ -d "$1" ]; then
75     for i in `(cd "$1"; find . -type f 2>/dev/null | sed 's,^\./,,g' | grep -v ' ' )`; do
76       cmp -s "$1/$i" "$2/$i" || echo "$1/$i"
77     done
78   elif [ -e "$1" ]; then
79     cmp -s "$1" "$2" || echo "$1"
80   fi
81 }
82 # }}}
83
84 # usage information {{{
85 usage()
86 {
87   print 1>&2 "
88 $bg[black]$fg[green]${boldcolor}${PROGRAMNAME} - save configuration of grml system${reset_color}
89
90 $bg[black]$fg[blue]${boldcolor}Usage:${reset_color}
91   $PROGRAMNAME [-target_options] -{all,home,etc,configdir}
92
93 $bg[black]$fg[blue]${boldcolor}Target options:${reset_color}
94   -ssh user@host:/path/to/file  copy configuration via ssh/scp to remote host
95   -mail <recipient>             send configuration via mail
96   -file foo_bar_config.tbz      save configuration in specified file
97
98   Notice: if no option is specified the default is assumed:
99           create file config.tbz in current directory
100
101 $bg[black]$fg[blue]${boldcolor}Files-to-store options:${reset_color}
102   -home                         store hidden files from \$HOME (\$HOME/.*)
103   -grmlhome                     store hidden files from \$HOME (\$HOME/.*) of user grml [use as user root]
104   -etc                          store modified files from /etc
105   -configdir                    store \$HOME/config
106   -all                          store all configuration files (:= -home, -configdir and -etc)
107
108   Notice: it is also possible to use environment variables:
109           \$SAVE_HOME, \$SAVE_GRMLHOME, \$SAVE_ETC, \$SAVE_CONFIGDIR and \$SAVE_ALL
110
111 $bg[black]$fg[blue]${boldcolor}Usage examples:${reset_color}
112   $PROGRAMNAME -all                                  => store all configuration files in config.tbz in current dir
113   $PROGRAMNAME -home -mail  devnull@grml.org         => store \$HOME/.* in config.tbz and send it via mail
114   $PROGRAMNAME -etc  -ssh   devnull@grml.org:/path/  => store /etc in config.tbz and scp it to specified host
115   $PROGRAMNAME -all  -file  foo.tbz                  => store all configuration files in foo.tbz
116   SAVE_ALL=yes $PROGRAMNAME -file /path/foo.tbz      => store all configuration files in /path/foo.tbz
117
118 More information on save-config can be found in the manual page: man save-config
119
120 See also: restore-config(1), bootoptions: myconfig=/dev/ice, extract=PATH,
121           netconfig=server.tld/path/to/config.tbz
122
123 Report bugs, send wishes and feedback to the grml team:
124 http://grml.org/bugs/ - contact (at) grml.org
125 "
126 }
127 # }}}
128
129 # what do we want to store? {{{
130 save_home(){
131   debug "save home"
132   for i in $HOME/.* ; do findchanged "$i" /etc/skel/$(basename "$i"); done >> $FILELIST
133   debug "debug: $FILELIST"
134 }
135
136 save_grmlhome(){
137   debug "save grmlhome"
138   if [ -d /home/grml/ ] ; then
139      for i in /home/grml/.* ; do findchanged "$i" /etc/skel/$(basename "$i"); done >> $FILELIST
140   fi
141   debug "debug: $FILELIST"
142 }
143
144 save_etc(){
145   debug "save etc"
146   if [ -n "$NEWLAYOUT" ] ; then
147      $runas find "${CHANGE_DIR}/etc" | sed -e "s#${CHANGE_DIR}## ; /etc$/d" >> $FILELIST
148   else
149      $runas findchanged /etc "${ORIG_DIR}/etc" >> $FILELIST
150   fi
151 }
152
153 save_configdir(){
154   debug "save configdir"
155   if [ -d $HOME/config ] ; then
156      ls $HOME/config/*  >> $FILELIST 2>/dev/null
157      ls $HOME/config/.* >> $FILELIST 2>/dev/null
158   fi
159 }
160 # }}}
161
162 # create configuration file {{{
163 create_config(){
164   if ! [ -r "$FILELIST" ]; then
165     echo "Filelist $FILELIST could not be read." >&2
166     echo "Error when generating $FILENAME." >&2
167     bailout ; exit 1
168   else
169     # GNU tar sucks so much, really. Avoid the "file changed as we read it":
170     tar cf /dev/null /etc
171     # now really execute the according tar command:
172     if BZIP2=-9 $runas tar -T - -cpPjf "$FILENAME" <"$FILELIST" ; then
173       echo "Successfully stored configuration in file $FILENAME"
174     else
175       echo "Error when generating $FILENAME." >&2
176       bailout ; exit 1
177     fi
178   fi
179 }
180 # }}}
181
182 # commandline parsing {{{
183 parse_options()
184 {
185    zparseopts -K -- help=o_help mail:=o_mail \
186                     file:=o_file home=o_home grmlhome=o_grmlhome etc=o_etc \
187                     configdir=o_configdir all=o_all ssh:=o_ssh
188
189    if [[ "$#" == 0 || "$o_help" != "" || "$1" == '-h' || "$1" == '--help' ]]; then
190       usage ; exit
191    fi
192
193    if [[ "$o_file" != "" ]]; then
194      FILENAME="$o_file[2]"
195    else
196      FILENAME="config.tbz"
197    fi
198
199    if [[ "$o_home" != "" ]]; then
200       debug "home is set"
201       SAVE_HOME="yes"
202    fi
203
204    if [[ "$o_grmlhome" != "" ]]; then
205       debug "grmlhome is set"
206       SAVE_GRMLHOME="yes"
207    fi
208
209    if [[ "$o_etc" != "" ]]; then
210       debug "etc is set"
211       SAVE_ETC="yes"
212    fi
213
214    if [[ "$o_configdir" != "" ]]; then
215       debug "configdir is set"
216       SAVE_CONFIGDIR="yes"
217    fi
218
219    if [[ "$o_all" != "" ]]; then
220       debug "home, grmlhome, etc and configdir are set"
221       SAVE_HOME="yes"
222       SAVE_GRMLHOME="yes"
223       SAVE_ETC="yes"
224       SAVE_CONFIGDIR="yes"
225    fi
226
227    if [[ "$o_ssh" != "" ]]; then
228       debug "scp $FILENAME $o_ssh[2]"
229       scp $FILENAME $o_ssh[2]
230    fi
231
232    if [[ "$o_mail" != "" ]]; then
233       check4progs mutt || { echo "Sorry, mutt not available for sending mail. Exiting.">&2 ; exit 1 }
234       recipient=$o_mail[2]
235       debug "send mail to $recipient"
236       echo "Created on $DATE on host $HOSTNAME running grml $GRML_VERSION" > $MAILFILE
237       mutt -s "configuration of $HOSTNAME ($DATE)" -a $FILENAME $recipient < $MAILFILE
238    fi
239 }
240 parse_options $*
241 # }}}
242
243 # execution wrapper {{{
244 runit(){
245    if [[ $SAVE_HOME == "yes" ]]; then
246      debug "running save_home"
247      save_home
248      SETSAVE=1
249    fi
250    if [[ $SAVE_GRMLHOME == "yes" ]]; then
251      debug "running save_grmlhome"
252      save_grmlhome
253      SETSAVE=1
254    fi
255    if [[ $SAVE_ETC == "yes" ]] ; then
256      debug "running save_etc"
257      save_etc
258      SETSAVE=1
259    fi
260    if [[ $SAVE_CONFIGDIR == "yes" ]] ; then
261      debug "running save_configdir"
262      save_configdir
263      SETSAVE=1
264    fi
265    if [ -z $SETSAVE ] ; then
266      echo "Sorry, you did not select any configuration which should be saved. Exiting."
267      bailout ; exit 1
268    fi
269 }
270 # }}}
271
272 # now run it
273   runit
274   create_config
275   bailout
276
277 ## END OF FILE #################################################################
278 # vim:foldmethod=marker