1#!/sbin/sh
2#
3# CDDL HEADER START
4#
5# The contents of this file are subject to the terms of the
6# Common Development and Distribution License, Version 1.0 only
7# (the "License").  You may not use this file except in compliance
8# with the License.
9#
10# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11# or http://www.opensolaris.org/os/licensing.
12# See the License for the specific language governing permissions
13# and limitations under the License.
14#
15# When distributing Covered Code, include this CDDL HEADER in each
16# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17# If applicable, add the following below this CDDL HEADER, with the
18# fields enclosed by brackets "[]" replaced with your own identifying
19# information: Portions Copyright [yyyy] [name of copyright owner]
20#
21# CDDL HEADER END
22#
23#
24# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25# Use is subject to license terms.
26#
27#ident	"%Z%%M%	%I%	%E% SMI"
28
29PATH=/sbin:/usr/bin:/usr/sbin
30LC_ALL=C
31export PATH LC_ALL
32
33. /lib/svc/share/smf_include.sh
34. /lib/svc/share/fs_include.sh
35
36usage()
37{
38	echo "usage: $0 [-r rootdir]" >&2
39	echo "
40See http://sun.com/msg/SMF-8000-MY for more information on the use of
41this script."
42	exit 2;
43}
44
45repositorydir=etc/svc
46repository=repository
47
48myroot=/
49while getopts r: opt; do
50	case "$opt" in
51	    r)	myroot=$OPTARG
52		if [ ! -d $myroot ]; then
53			echo "$myroot: not a directory" >&2
54			exit 1
55		fi
56		# validate directory and make sure it ends in '/'.
57		case "$myroot" in
58		    //*) echo "$myroot: must begin with a single /" >&2
59			usage;;
60		    /)	echo "$myroot: alternate root cannot be /" >&2
61			usage;;
62
63		    /*/) ;;			# ends with /
64		    /*) myroot="$myroot/";;	# add final /
65
66		    *)	echo "$myroot: must be a full path" >&2
67			usage;;
68		esac;;
69	    ?)	usage;;
70	esac
71done
72
73if [ $OPTIND -le $# ]; then
74	# getopts(1) didn't slurp up everything.
75	usage
76fi
77
78#
79# Note that the below test is carefully constructed to fail *open*;  if
80# anything goes wrong, it will drive onward.
81#
82if [ -x /usr/bin/id -a -x /usr/bin/grep ] &&
83    /usr/bin/id 2>/dev/null | /usr/bin/grep -v '^[^=]*=0(' >/dev/null 2>&1; then
84	echo "$0: may only be invoked by root" >&2
85	exit 2
86fi
87
88echo >&2 "
89See http://sun.com/msg/SMF-8000-MY for more information on the use of
90this script to restore backup copies of the smf(5) repository.
91
92If there are any problems which need human intervention, this script will
93give instructions and then exit back to your shell."
94
95if [ "$myroot" -eq / ]; then
96	system="system"
97	[ "`/sbin/zonename`" != global ] && system="zone"
98	echo >&2 "
99Note that upon full completion of this script, the $system will be rebooted
100using reboot(1M), which will interrupt any active services.
101"
102fi
103
104# check that the filesystem is as expected
105cd "$myroot" || exit 1
106cd "$myroot$repositorydir" || exit 1
107
108nouser=false
109rootro=false
110
111# check to make sure /usr is mounted
112if [ ! -x /usr/bin/pgrep ]; then
113	nouser=true
114fi
115
116if [ ! -w "$myroot" ]; then
117	rootro=true
118fi
119
120if [ "$nouser" = true -o "$rootro" = true ]; then
121	if [ "$nouser" = true -a "$rootro" = true ]; then
122		echo "The / filesystem is mounted read-only, and the /usr" >&2
123		echo "filesystem has not yet been mounted." >&2
124	elif [ "$nouser" = true ]; then
125		echo "The /usr filesystem has not yet been mounted." >&2
126	else
127		echo "The / filesystem is mounted read-only." >&2
128	fi
129
130	echo >&2 "
131This must be rectified before $0 can continue.
132
133If / or /usr are on SVM (md(7d)) partitions, first run
134    /lib/svc/method/svc-metainit
135
136To properly mount / and /usr, run:
137    /lib/svc/method/fs-root
138then
139    /lib/svc/method/fs-usr
140
141After those have completed successfully, re-run:
142    $0 $*
143
144to continue.
145"
146	exit 1
147fi
148
149# at this point, we know / is mounted read-write, and /usr is mounted.
150oldreps="`
151	/bin/ls -1rt $repository-*-[0-9]*[0-9] | \
152	    /bin/sed -e '/[^A-Za-z0-9_,.-]/d' -e 's/^'$repository'-//'
153`"
154
155if [ -z "$oldreps" ]; then
156	cat >&2 <<EOF
157There are no available backups of $myroot$repositorydir/$repository.db.
158The only available repository is "-seed-".  Note that restoring the seed
159will lose all customizations, including those made by the system during
160the installation and/or upgrade process.
161
162EOF
163	prompt="Enter -seed- to restore from the seed, or -quit- to exit: \c"
164	default=
165else
166	cat >&2 <<EOF
167The following backups of $myroot$repositorydir/$repository.db exist, from
168oldest to newest:
169
170$oldreps
171
172The backups are named based on their type and the time what they were taken.
173Backups beginning with "boot" are made before the first change is made to
174the repository after system boot.  Backups beginning with "manifest_import"
175are made after svc:/system/manifest-import:default finishes its processing.
176The time of backup is given in YYYYMMDD_HHMMSS format.
177
178Please enter either a specific backup repository from the above list to
179restore it, or one of the following choices:
180
181	CHOICE		  ACTION
182	----------------  ----------------------------------------------
183	boot		  restore the most recent post-boot backup
184	manifest_import	  restore the most recent manifest_import backup
185	-seed-		  restore the initial starting repository  (All
186			    customizations will be lost, including those
187			    made by the install/upgrade process.)
188	-quit-		  cancel script and quit
189
190EOF
191	prompt="Enter response [boot]: \c"
192	default="boot"
193fi
194
195cont=false
196while [ $cont = false ]; do
197	echo "$prompt"
198
199	read x || exit 1
200	[ -z "$x" ] && x="$default"
201
202	case "$x" in
203	    -seed-)
204		if [ $myroot != / -o "`/sbin/zonename`" = global ]; then
205			file="$myroot"lib/svc/seed/global.db
206		else
207			file="$myroot"lib/svc/seed/nonglobal.db
208		fi;;
209	    -quit-)
210		echo "Exiting."
211		exit 0;;
212	    /*)
213		file="$x";;
214	    */*)
215		file="$myroot$x";;
216	    ?*)
217		file="$myroot$repositorydir/repository-$x";;
218	    *)	file= ;;
219	esac
220
221	if [ -f $file ]; then
222		if [ -r $file ]; then
223			checkresults="`echo PRAGMA integrity_check\; | \
224			    /lib/svc/bin/sqlite $file >&1 | grep -v '^ok$'`"
225
226			if [ -n "$checkresults" ]; then
227				echo "$file: integrity check failed:" >&2
228				echo "$checkresults" >&2
229				echo
230			else
231				cont=true
232			fi
233		else
234			echo "$file: not readable"
235		fi
236	elif [ -n "$file" ]; then
237		echo "$file: not found"
238	fi
239done
240
241errors="$myroot"etc/svc/volatile/db_errors
242repo="$myroot$repositorydir/$repository.db"
243new="$repo"_old_"`date +%Y''%m''%d'_'%H''%M''%S`"
244
245steps=
246if [ "$myroot" = / ]; then
247	steps="$steps
248svc.startd(1M) and svc.configd(1M) will be quiesced, if running."
249fi
250
251if [ -r $repo ]; then
252	steps="$steps
253$repo
254    -- renamed --> $new"
255fi
256if [ -r $errors ]; then
257	steps="$steps
258$errors
259    -- copied --> ${new}_errors"
260fi
261
262cat >&2 <<EOF
263
264After confirmation, the following steps will be taken:
265$steps
266$file
267    -- copied --> $repo
268EOF
269
270if [ "$myroot" = / ]; then
271	echo "and the system will be rebooted with reboot(1M)."
272fi
273
274echo
275cont=false
276while [ $cont = false ]; do
277	echo "Proceed [yes/no]? \c"
278	read x || x=n
279
280	case "$x" in
281	    [Yy]|[Yy][Ee][Ss])
282		cont=true;;
283	    [Nn]|[Nn][Oo])
284		echo; echo "Exiting..."
285		exit 0;
286	esac;
287done
288
289umask 077		# we want files to be root-readable only.
290
291startd_msg=
292if [ "$myroot" = / ]; then
293	zone="`zonename`"
294	startd="`pgrep -z "$zone" -f svc.startd`"
295
296	echo
297	echo "Quiescing svc.startd(1M) and svc.configd(1M): \c"
298	if [ -n "$startd" ]; then
299		pstop $startd
300		startd_msg=\
301"To start svc.start(1M) running, do: /usr/bin/prun $startd"
302	fi
303	pkill -z "$zone" -f svc.configd
304
305	sleep 1			# yes, this is hack
306
307	echo "done."
308fi
309
310if [ -r "$repo" ]; then
311	echo "$repo"
312	echo "    -- renamed --> $new"
313	if mv $repo $new; then
314		:
315	else
316		echo "Failed.  $startd_msg"
317		exit 1;
318	fi
319fi
320
321if [ -r $errors ]; then
322	echo "$errors"
323	echo "    -- copied --> ${new}_errors"
324	if cp -p $errors ${new}_errors; then
325		:
326	else
327		mv -f $new $repo
328		echo "Failed.  $startd_msg"
329		exit 1;
330	fi
331fi
332
333echo "$file"
334echo "    -- copied --> $repo"
335
336if cp $file $repo.new.$$ && mv $repo.new.$$ $repo; then
337	:
338else
339	rm -f $repo.new.$$ ${new}_errors
340	mv -f $new $repo
341	echo "Failed.  $startd_msg"
342	exit 1;
343fi
344
345echo
346echo "The backup repository has been successfully restored."
347echo
348
349if [ "$myroot" = / ]; then
350	echo "Rebooting in 5 seconds."
351	sleep 5
352	reboot
353fi
354