xref: /illumos-gate/usr/src/lib/brand/solaris10/zone/p2v.ksh (revision e9af4bc0b1cc30cea75d6ad4aa2fde97d985e9be)
1#!/bin/ksh -p
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 (the "License").
7# You may not use this file except in compliance with the License.
8#
9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10# or http://www.opensolaris.org/os/licensing.
11# See the License for the specific language governing permissions
12# and limitations under the License.
13#
14# When distributing Covered Code, include this CDDL HEADER in each
15# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16# If applicable, add the following below this CDDL HEADER, with the
17# fields enclosed by brackets "[]" replaced with your own identifying
18# information: Portions Copyright [yyyy] [name of copyright owner]
19#
20# CDDL HEADER END
21#
22# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23# Use is subject to license terms.
24#
25
26# NOTE: this script runs in the global zone and touches the non-global
27# zone, so care should be taken to validate any modifications so that they
28# are safe.
29
30. /usr/lib/brand/solaris10/common.ksh
31
32LOGFILE=
33MSG_PREFIX="p2v: "
34EXIT_CODE=1
35
36usage()
37{
38	echo "$0 [-s] [-m msgprefix] [-u] [-v] [-b patchid]* zonename" >&2
39	exit $EXIT_CODE
40}
41
42# Clean up on interrupt
43trap_cleanup()
44{
45	msg=$(gettext "Postprocessing cancelled due to interrupt.")
46	error "$msg"
47
48	if (( $zone_is_running != 0 )); then
49		error "$e_shutdown" "$ZONENAME"
50		/usr/sbin/zoneadm -z $ZONENAME halt
51	fi
52
53	exit $EXIT_CODE
54}
55
56#
57# Disable any existing live-upgrade configuration.
58# We have already called safe_dir to validate the etc/lu directory.
59#
60fix_lu()
61{
62	ludir=$ZONEROOT/etc/lu
63
64	[[ ! -d $ludir ]] && return
65
66	safe_rm etc/lutab
67	safe_rm etc/lu/.BE_CONFIG
68	safe_rm etc/lu/.CURR_VARS
69	safe_rm etc/lu/ludb.local.xml
70	for i in $ludir/ICF* $ludir/vtoc* $ludir/GRUB*
71	do
72		nm=`basename $i`
73		safe_rm etc/lu/$nm
74	done
75}
76
77#
78# For an exclusive stack zone, fix up the network configuration files.
79# We need to do this even if unconfiguring the zone so sys-unconfig works
80# correctly.
81#
82fix_net()
83{
84	[[ "$STACK_TYPE" == "shared" ]] && return
85
86	NETIF_CNT=$(/usr/bin/ls $ZONEROOT/etc/hostname.* 2>/dev/null | \
87	    /usr/bin/wc -l)
88	if (( $NETIF_CNT != 1 )); then
89		vlog "$v_nonetfix"
90		return
91	fi
92
93	NET=$(LC_ALL=C /usr/sbin/zonecfg -z $ZONENAME info net)
94	if (( $? != 0 )); then
95		error "$e_badinfo" "net"
96		return
97	fi
98
99	NETIF=$(echo $NET | /usr/bin/nawk '{
100		for (i = 1; i < NF; i++) {
101			if ($i == "physical:") {
102				if (length(net) == 0) {
103					i++
104					net = $i
105				} else {
106					multiple=1
107				}
108			}
109		}
110	}
111	END {	if (!multiple)
112			print net
113	}')
114
115	if [[ -z "$NETIF" ]]; then
116		vlog "$v_nonetfix"
117		return
118	fi
119
120	OLD_HOSTNET=$(/usr/bin/ls $ZONEROOT/etc/hostname.*)
121	if [[ "$OLD_HOSTNET" != "$ZONEROOT/etc/hostname.$NETIF" ]]; then
122		safe_move $OLD_HOSTNET $ZONEROOT/etc/hostname.$NETIF
123	fi
124}
125
126#
127# Disable all of the shares since the zone cannot be an NFS server.
128# Note that we disable the various instances of the svc:/network/shares/group
129# SMF service in the fix_smf function.
130#
131fix_nfs()
132{
133	zonedfs=$ZONEROOT/etc/dfs
134
135	[[ ! -d $zonedfs ]] && return
136
137	if [[ -h $zonedfs/dfstab || ! -f $zonedfs/dfstab ]]; then
138		error "$e_badfile" "/etc/dfs/dfstab"
139		return
140	fi
141
142	tmpfile=$(mktemp -t)
143	if [[ $? == 1 || -z "$tmpfile" ]]; then
144		error "$e_tmpfile"
145		return
146	fi
147
148	/usr/bin/nawk '{
149		if (substr($1, 0, 1) == "#") {
150			print $0
151		} else {
152			print "#", $0
153			modified=1
154		}
155	}
156	END {
157		if (modified == 1) {
158			printf("# Modified by p2v ")
159			system("/usr/bin/date")
160			exit 0
161		}
162		exit 1
163	}' $zonedfs/dfstab >>$tmpfile
164
165	if (( $? == 0 )); then
166		if [[ ! -f $zonedfs/dfstab.pre_p2v ]]; then
167			safe_copy $zonedfs/dfstab $zonedfs/dfstab.pre_p2v
168		fi
169		safe_copy $tmpfile $zonedfs/dfstab
170		chown root:sys $zonedfs/dfstab || \
171		    fail_fatal "$f_chown" "$zonedfs/dfstab"
172		chmod 644 $zonedfs/dfstab || \
173		    fail_fatal "$f_chmod" "$zonedfs/dfstab"
174	fi
175	/usr/bin/rm -f $tmpfile
176}
177
178#
179# Comment out most of the old mounts since they are either unneeded or
180# likely incorrect within a zone.  Specific mounts can be manually
181# reenabled if the corresponding device is added to the zone.
182#
183fix_vfstab()
184{
185	if [[ -h $ZONEROOT/etc/vfstab || ! -f $ZONEROOT/etc/vfstab ]]; then
186		error "$e_badfile" "/etc/vfstab"
187		return
188	fi
189
190	tmpfile=$(mktemp -t)
191	if [[ $? == 1 || -z "$tmpfile" ]]; then
192		error "$e_tmpfile"
193		return
194	fi
195
196	/usr/bin/nawk '{
197		if (substr($1, 0, 1) == "#") {
198			print $0
199		} else if ($1 == "fd" || $1 == "/proc" || $1 == "swap" ||
200		    $1 == "ctfs" || $1 == "objfs" || $1 == "sharefs" ||
201		    $4 == "nfs" || $4 == "lofs") {
202			print $0
203		} else {
204			print "#", $0
205			modified=1
206		}
207	}
208	END {
209		if (modified == 1) {
210			printf("# Modified by p2v ")
211			system("/usr/bin/date")
212			exit 0
213		}
214		exit 1
215	}' $ZONEROOT/etc/vfstab >>$tmpfile
216
217	if (( $? == 0 )); then
218		if [[ ! -f $ZONEROOT/etc/vfstab.pre_p2v ]]; then
219			safe_copy $ZONEROOT/etc/vfstab \
220			    $ZONEROOT/etc/vfstab.pre_p2v
221		fi
222		safe_copy $tmpfile $ZONEROOT/etc/vfstab
223		chown root:sys $ZONEROOT/etc/vfstab || \
224		    fail_fatal "$f_chown" "$ZONEROOT/etc/vfstab"
225		chmod 644 $ZONEROOT/etc/vfstab || \
226		    fail_fatal "$f_chmod" "$ZONEROOT/etc/vfstab"
227	fi
228	/usr/bin/rm -f $tmpfile
229}
230
231#
232# Collect the data needed to delete SMF services.  Since we're p2v-ing a
233# physical image there are SMF services which must be deleted.
234#
235fix_smf_pre_uoa()
236{
237	#
238	# Start by getting the svc manifests that are delivered by hollow
239	# pkgs then use 'svccfg inventory' to get the names of the svcs
240	# delivered by those manifests.  The svc names are saved into a
241	# temporary file.
242	#
243
244	SMFTMPFILE=$(mktemp -t smf.XXXXXX)
245	if [[ $? == 1 || -z "$SMFTMPFILE" ]]; then
246		error "$e_tmpfile"
247		return
248	fi
249
250	for i in $ZONEROOT/var/sadm/pkg/*
251	do
252		pkg=$(/usr/bin/basename $i)
253		[[ ! -f $ZONEROOT/var/sadm/pkg/$pkg/save/pspool/$pkg/pkgmap ]] \
254		    && continue
255
256		/usr/bin/egrep -s "SUNW_PKG_HOLLOW=true" \
257		    $ZONEROOT/var/sadm/pkg/$pkg/pkginfo || continue
258
259		for j in $(/usr/bin/nawk '{if ($2 == "f" &&
260		    substr($4, 1, 17) == "var/svc/manifest/") print $4}' \
261		    $ZONEROOT/var/sadm/pkg/$pkg/save/pspool/$pkg/pkgmap)
262		do
263			svcs=$(SVCCFG_NOVALIDATE=1 \
264			    SVCCFG_REPOSITORY=$ZONEROOT/etc/svc/repository.db \
265			    /usr/sbin/svccfg inventory $ZONEROOT/$j)
266			for k in $svcs
267			do
268				echo $k /$j >> $SMFTMPFILE
269			done
270		done
271	done
272}
273
274#
275# Delete or disable SMF services.
276# Zone is booted to milestone=none when this function is called.
277# Use the SMF data collected by fix_smf_pre_uoa() to delete the services.
278#
279fix_smf()
280{
281	#
282	# Zone was already booted to milestone=none, wait until SMF door exists.
283	#
284	for i in 0 1 2 3 4 5 6 7 8 9
285	do
286		[[ -r $ZONEROOT/etc/svc/volatile/repository_door ]] && break
287		sleep 5
288	done
289
290	if [[ $i -eq 9 && ! -r $ZONEROOT/etc/svc/volatile/repository_door ]];
291	then
292		error "$e_nosmf"
293		/usr/bin/rm -f $SMFTMPFILE
294		return
295	fi
296
297	insttmpfile=$(mktemp -t instsmf.XXXXXX)
298	if [[ $? == 1 || -z "$insttmpfile" ]]; then
299		error "$e_tmpfile"
300		/usr/bin/rm -f $SMFTMPFILE
301		return
302	fi
303
304	vlog "$v_rmhollowsvcs"
305        while read fmri mfst
306	do
307		# Delete the svc.
308		vlog "$v_delsvc" "$fmri"
309		echo "/usr/sbin/svccfg delete -f $fmri"
310		echo "/usr/sbin/svccfg delhash -d $mfst"
311		echo "rm -f $mfst"
312	done < $SMFTMPFILE > $ZONEROOT/tmp/smf_rm
313
314	/usr/sbin/zlogin -S $ZONENAME /bin/sh /tmp/smf_rm >/dev/null 2>&1
315
316	/usr/bin/rm -f $SMFTMPFILE
317
318	# Get a list of the svcs that now exist in the zone.
319	LANG=C /usr/sbin/zlogin -S $ZONENAME /usr/bin/svcs -aH | \
320	    /usr/bin/nawk '{print $3}' >>$insttmpfile
321
322	[[ -n $LOGFILE ]] && \
323	    printf "[$(date)] ${MSG_PREFIX}${v_svcsinzone}\n" >&2
324	[[ -n $LOGFILE ]] && cat $insttmpfile >&2
325
326	#
327	# Fix network services if shared stack.
328	#
329	if [[ "$STACK_TYPE" == "shared" ]]; then
330		vlog "$v_fixnetsvcs"
331
332		NETPHYSDEF="svc:/network/physical:default"
333		NETPHYSNWAM="svc:/network/physical:nwam"
334
335		/usr/bin/egrep -s "$NETPHYSDEF" $insttmpfile
336		if (( $? == 0 )); then
337			vlog "$v_enblsvc" "$NETPHYSDEF"
338			/usr/sbin/zlogin -S $ZONENAME \
339			    /usr/sbin/svcadm enable $NETPHYSDEF || \
340			    error "$e_dissvc" "$NETPHYSDEF"
341		fi
342
343		/usr/bin/egrep -s "$NETPHYSNWAM" $insttmpfile
344		if (( $? == 0 )); then
345			vlog "$v_dissvc" "$NETPHYSNWAM"
346			/usr/sbin/zlogin -S $ZONENAME \
347			    /usr/sbin/svcadm disable $NETPHYSNWAM || \
348			    error "$e_enblsvc" "$NETPHYSNWAM"
349		fi
350
351		for i in $(/usr/bin/egrep network/routing $insttmpfile)
352		do
353			# Disable the svc.
354			vlog "$v_dissvc" "$i"
355			/usr/sbin/zlogin -S $ZONENAME \
356			    /usr/sbin/svcadm disable $i || \
357			    error "$e_dissvc" $i
358		done
359	fi
360
361	#
362	# Disable well-known services that don't run in a zone.
363	#
364	vlog "$v_rminvalidsvcs"
365	for i in $(/usr/bin/egrep -hv "^#" \
366	    /usr/lib/brand/solaris10/smf_disable.lst \
367	    /etc/brand/solaris10/smf_disable.conf)
368	do
369		# Skip svcs not installed in the zone.
370		/usr/bin/egrep -s "$i:" $insttmpfile || continue
371
372		# Disable the svc.
373		vlog "$v_dissvc" "$i"
374		/usr/sbin/zlogin -S $ZONENAME /usr/sbin/svcadm disable $i || \
375		    error "$e_dissvc" $i
376	done
377
378	#
379	# Since zones can't be NFS servers, disable all of the instances of
380	# the shares svc.
381	#
382	for i in $(/usr/bin/egrep network/shares/group $insttmpfile)
383	do
384		vlog "$v_dissvc" "$i"
385		/usr/sbin/zlogin -S $ZONENAME /usr/sbin/svcadm disable $i || \
386		    error "$e_dissvc" $i
387	done
388
389	/usr/bin/rm -f $insttmpfile
390}
391
392#
393# Remove well-known pkgs that do not work inside a zone.
394#
395rm_pkgs()
396{
397	/usr/bin/cat <<-EOF > $ZONEROOT/tmp/admin || fatal "$e_adminf"
398	mail=
399	instance=overwrite
400	partial=nocheck
401	runlevel=nocheck
402	idepend=nocheck
403	rdepend=nocheck
404	space=nocheck
405	setuid=nocheck
406	conflict=nocheck
407	action=nocheck
408	basedir=default
409	EOF
410
411	for i in $(/usr/bin/egrep -hv "^#" /usr/lib/brand/solaris10/pkgrm.lst \
412	    /etc/brand/solaris10/pkgrm.conf)
413	do
414		[[ ! -d $ZONEROOT/var/sadm/pkg/$i ]] && continue
415
416		vlog "$v_rmpkg" "$i"
417		/usr/sbin/zlogin -S $ZONENAME \
418		    /usr/sbin/pkgrm -na /tmp/admin $i >&2 || error "$e_rmpkg" $i
419	done
420}
421
422#
423# Zoneadmd writes a one-line index file into the zone when the zone boots,
424# so any information about installed zones from the original system will
425# be lost at that time.  Here we'll warn the sysadmin about any pre-existing
426# zones that they might want to clean up by hand, but we'll leave the zonepaths
427# in place in case they're on shared storage and will be migrated to
428# a new host.
429#
430warn_zones()
431{
432	zoneconfig=$ZONEROOT/etc/zones
433
434	[[ ! -d $zoneconfig ]] && return
435
436	if [[ -h $zoneconfig/index || ! -f $zoneconfig/index ]]; then
437		error "$e_badfile" "/etc/zones/index"
438		return
439	fi
440
441	NGZ=$(/usr/bin/nawk -F: '{
442		if (substr($1, 0, 1) == "#" || $1 == "global")
443			continue
444
445		if ($2 == "installed")
446			printf("%s ", $1)
447	}' $zoneconfig/index)
448
449	# Return if there are no installed zones to warn about.
450	[[ -z "$NGZ" ]] && return
451
452	log "$v_rmzones" "$NGZ"
453
454	NGZP=$(/usr/bin/nawk -F: '{
455		if (substr($1, 0, 1) == "#" || $1 == "global")
456			continue
457
458		if ($2 == "installed")
459			printf("%s ", $3)
460	}' $zoneconfig/index)
461
462	log "$v_rmzonepaths"
463
464	for i in $NGZP
465	do
466		log "    %s" "$i"
467	done
468}
469
470#
471# ^C Should cleanup; if the zone is running, it should try to halt it.
472#
473zone_is_running=0
474trap trap_cleanup INT
475
476#
477# Parse the command line options.
478#
479OPT_U=
480OPT_V=
481OPT_M=
482OPT_L=
483while getopts "uvm:l:" opt
484do
485	case "$opt" in
486		u)	OPT_U="-u";;
487		v)	OPT_V="-v";;
488		m)	MSG_PREFIX="$OPTARG"; OPT_M="-m \"$OPTARG\"";;
489		l)	LOGFILE="$OPTARG"; OPT_L="-l \"$OPTARG\"";;
490		*)	usage;;
491	esac
492done
493shift OPTIND-1
494
495(( $# < 1 )) && usage
496
497(( $# > 2 )) && usage
498
499[[ -n $LOGFILE ]] && exec 2>>$LOGFILE
500
501ZONENAME=$1
502ZONEPATH=$2
503# XXX shared/common script currently uses lower case zonename & zonepath
504zonename="$ZONENAME"
505zonepath="$ZONEPATH"
506ZONEROOT=$ZONEPATH/root
507
508e_badinfo=$(gettext "Failed to get '%s' zone resource")
509e_badfile=$(gettext "Invalid '%s' file within the zone")
510v_mkdirs=$(gettext "Creating mount points")
511v_nonetfix=$(gettext "Cannot update /etc/hostname.{net} file")
512v_adjust=$(gettext "Updating the image to run within a zone")
513v_stacktype=$(gettext "Stack type '%s'")
514v_booting=$(gettext "Booting zone to single user mode")
515e_nosmf=$(gettext "SMF repository unavailable.")
516v_svcsinzone=$(gettext "The following SMF services are installed:")
517v_rmhollowsvcs=$(gettext "Deleting SMF services from hollow packages")
518v_fixnetsvcs=$(gettext "Adjusting network SMF services")
519v_rminvalidsvcs=$(gettext "Disabling invalid SMF services")
520v_delsvc=$(gettext "Delete SMF svc '%s'")
521e_delsvc=$(gettext "deleting SMF svc '%s'")
522v_enblsvc=$(gettext "Enable SMF svc '%s'")
523e_enblsvc=$(gettext "enabling SMF svc '%s'")
524v_dissvc=$(gettext "Disable SMF svc '%s'")
525e_dissvc=$(gettext "disabling SMF svc '%s'")
526e_adminf=$(gettext "Unable to create admin file")
527v_rmpkg=$(gettext "Remove package '%s'")
528e_rmpkg=$(gettext "removing package '%s'")
529v_rmzones=$(gettext "The following zones in this image will be unusable: %s")
530v_rmzonepaths=$(gettext "These zonepaths could be removed from this image:")
531v_halting=$(gettext "Halting zone")
532e_shutdown=$(gettext "Shutting down zone %s...")
533e_badhalt=$(gettext "Zone halt failed")
534v_exitgood=$(gettext "Postprocessing successful.")
535e_exitfail=$(gettext "Postprocessing failed.")
536
537#
538# Do some validation on the paths we'll be accessing
539#
540safe_dir /etc
541safe_dir /var
542safe_opt_dir /etc/dfs
543safe_opt_dir /etc/lu
544safe_opt_dir /etc/zones
545
546mk_zone_dirs
547
548# Now do the work to update the zone.
549
550# Check for zones inside of image.
551warn_zones
552fix_smf_pre_uoa
553
554log "$v_adjust"
555
556#
557# Any errors in these functions are not considered fatal.  The zone can be
558# be fixed up manually afterwards and it may need some additional manual
559# cleanup in any case.
560#
561
562STACK_TYPE=$(/usr/sbin/zoneadm -z $ZONENAME list -p | \
563    /usr/bin/nawk -F: '{print $7}')
564if (( $? != 0 )); then
565	error "$e_badinfo" "stacktype"
566fi
567vlog "$v_stacktype" "$STACK_TYPE"
568
569fix_lu
570fix_net
571fix_nfs
572fix_vfstab
573
574vlog "$v_booting"
575
576#
577# Boot the zone so that we can do all of the SMF updates needed on the zone's
578# repository.
579#
580
581zone_is_running=1
582
583/usr/sbin/zoneadm -z $ZONENAME boot -f -- -m milestone=none
584if (( $? != 0 )); then
585	error "$e_badboot"
586	/usr/bin/rm -f $SMFTMPFILE
587	fatal "$e_exitfail"
588fi
589
590# cleanup SMF services
591fix_smf
592
593# remove invalid pkgs
594rm_pkgs
595
596if [[ -z $failed && -n $OPT_U ]]; then
597	vlog "$v_unconfig"
598
599	sysunconfig_zone
600	if (( $? != 0 )); then
601		failed=1
602	fi
603fi
604
605vlog "$v_halting"
606/usr/sbin/zoneadm -z $ZONENAME halt
607if (( $? != 0 )); then
608	error "$e_badhalt"
609	failed=1
610fi
611zone_is_running=0
612
613if [[ -n $failed ]]; then
614	fatal "$e_exitfail"
615fi
616
617vlog "$v_exitgood"
618exit 0
619