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 2012 Nexenta Sysytems, Inc.  All rights reserved.
25# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
26# Use is subject to license terms.
27#
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://illumos.org/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://illumos.org/msg/SMF-8000-MY for more information on the use of
90this script to restore backup copies of the smf(7) 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" = "/" ]; 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(8), 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
133To properly mount / and /usr, run:
134    /lib/svc/method/fs-root
135then
136    /lib/svc/method/fs-usr
137
138After those have completed successfully, re-run:
139    $0 $*
140
141to continue.
142"
143	exit 1
144fi
145
146# at this point, we know / is mounted read-write, and /usr is mounted.
147oldreps="`
148	/bin/ls -1rt $repository-*-[0-9]*[0-9] | \
149	    /bin/sed -e '/[^A-Za-z0-9_,.-]/d' -e 's/^'$repository'-//'
150`"
151
152if [ -z "$oldreps" ]; then
153	cat >&2 <<EOF
154There are no available backups of $myroot$repositorydir/$repository.db.
155The only available repository is "-seed-".  Note that restoring the seed
156will lose all customizations, including those made by the system during
157the installation and/or upgrade process.
158
159EOF
160	prompt="Enter -seed- to restore from the seed, or -quit- to exit: \c"
161	default=
162else
163	cat >&2 <<EOF
164The following backups of $myroot$repositorydir/$repository.db exist, from
165oldest to newest:
166
167$oldreps
168
169The backups are named based on their type and the time what they were taken.
170Backups beginning with "boot" are made before the first change is made to
171the repository after system boot.  Backups beginning with "manifest_import"
172are made after svc:/system/manifest-import:default finishes its processing.
173The time of backup is given in YYYYMMDD_HHMMSS format.
174
175Please enter either a specific backup repository from the above list to
176restore it, or one of the following choices:
177
178	CHOICE		  ACTION
179	----------------  ----------------------------------------------
180	boot		  restore the most recent post-boot backup
181	manifest_import	  restore the most recent manifest_import backup
182	-seed-		  restore the initial starting repository  (All
183			    customizations will be lost, including those
184			    made by the install/upgrade process.)
185	-quit-		  cancel script and quit
186
187EOF
188	prompt="Enter response [boot]: \c"
189	default="boot"
190fi
191
192cont=false
193while [ $cont = false ]; do
194	echo "$prompt"
195
196	read x || exit 1
197	[ -z "$x" ] && x="$default"
198
199	case "$x" in
200	    -seed-)
201		if [ $myroot != / -o "`/sbin/zonename`" = global ]; then
202			file="$myroot"lib/svc/seed/global.db
203		else
204			file="$myroot"lib/svc/seed/nonglobal.db
205		fi;;
206	    -quit-)
207		echo "Exiting."
208		exit 0;;
209	    /*)
210		file="$x";;
211	    */*)
212		file="$myroot$x";;
213	    ?*)
214		file="$myroot$repositorydir/repository-$x";;
215	    *)	file= ;;
216	esac
217
218	if [ -f $file ]; then
219		if [ -r $file ]; then
220			checkresults="`echo PRAGMA integrity_check\; | \
221			    /lib/svc/bin/sqlite $file >&1 | grep -v '^ok$'`"
222
223			if [ -n "$checkresults" ]; then
224				echo "$file: integrity check failed:" >&2
225				echo "$checkresults" >&2
226				echo
227			else
228				cont=true
229			fi
230		else
231			echo "$file: not readable"
232		fi
233	elif [ -n "$file" ]; then
234		echo "$file: not found"
235	fi
236done
237
238errors="$myroot"etc/svc/volatile/db_errors
239repo="$myroot$repositorydir/$repository.db"
240new="$repo"_old_"`date +%Y''%m''%d'_'%H''%M''%S`"
241
242steps=
243if [ "$myroot" = / ]; then
244	steps="$steps
245svc.startd(8) and svc.configd(8) will be quiesced, if running."
246fi
247
248if [ -r $repo ]; then
249	steps="$steps
250$repo
251    -- renamed --> $new"
252fi
253if [ -r $errors ]; then
254	steps="$steps
255$errors
256    -- copied --> ${new}_errors"
257fi
258
259cat >&2 <<EOF
260
261After confirmation, the following steps will be taken:
262$steps
263$file
264    -- copied --> $repo
265EOF
266
267if [ "$myroot" = / ]; then
268	echo "and the system will be rebooted with reboot(8)."
269fi
270
271echo
272cont=false
273while [ $cont = false ]; do
274	echo "Proceed [yes/no]? \c"
275	read x || x=n
276
277	case "$x" in
278	    [Yy]|[Yy][Ee][Ss])
279		cont=true;;
280	    [Nn]|[Nn][Oo])
281		echo; echo "Exiting..."
282		exit 0;
283	esac;
284done
285
286umask 077		# we want files to be root-readable only.
287
288startd_msg=
289if [ "$myroot" = / ]; then
290	zone="`zonename`"
291	startd="`pgrep -z "$zone" -f svc.startd`"
292
293	echo
294	echo "Quiescing svc.startd(8) and svc.configd(8): \c"
295	if [ -n "$startd" ]; then
296		pstop $startd
297		startd_msg=\
298"To start svc.start(8) running, do: /usr/bin/prun $startd"
299	fi
300	pkill -z "$zone" -f svc.configd
301
302	sleep 1			# yes, this is hack
303
304	echo "done."
305fi
306
307if [ -r "$repo" ]; then
308	echo "$repo"
309	echo "    -- renamed --> $new"
310	if mv $repo $new; then
311		:
312	else
313		echo "Failed.  $startd_msg"
314		exit 1;
315	fi
316fi
317
318if [ -r $errors ]; then
319	echo "$errors"
320	echo "    -- copied --> ${new}_errors"
321	if cp -p $errors ${new}_errors; then
322		:
323	else
324		mv -f $new $repo
325		echo "Failed.  $startd_msg"
326		exit 1;
327	fi
328fi
329
330echo "$file"
331echo "    -- copied --> $repo"
332
333if cp $file $repo.new.$$ && mv $repo.new.$$ $repo; then
334	:
335else
336	rm -f $repo.new.$$ ${new}_errors
337	mv -f $new $repo
338	echo "Failed.  $startd_msg"
339	exit 1;
340fi
341
342echo
343echo "The backup repository has been successfully restored."
344echo
345
346if [ "$myroot" = / ]; then
347	echo "Rebooting in 5 seconds."
348	sleep 5
349	reboot
350fi
351