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 # Restrict executables to /usr/bin and /usr/sbin
31 PATH=/usr/bin:/usr/sbin
32 export PATH
33 unset LD_LIBRARY_PATH
34
35 . /usr/lib/brand/ipkg/common.ksh
36
37 LOGFILE=
38 EXIT_CODE=1
39
40 # Clean up on failure
41 trap_exit()
42 {
43 if (( $ZONE_IS_MOUNTED != 0 )); then
44 error "$v_unmount"
45 zoneadm -z $ZONENAME unmount
46 fi
47
48 exit $EXIT_CODE
49 }
50
51 #
52 # For an exclusive stack zone, fix up the network configuration files.
53 # We need to do this even if unconfiguring the zone so sys-unconfig works
54 # correctly.
55 #
56 fix_net()
57 {
58 [[ "$STACK_TYPE" == "shared" ]] && return
59
60 NETIF_CNT=0
61 for i in $ZONEROOT/etc/hostname.* $ZONEROOT/etc/dhcp.*
62 do
63 if [[ -f "$i" ]]; then
64 NETIF_CNT=$(expr $NETIF_CNT + 1)
65 OLD_HOSTNET="$i"
66 fi
67 done
68 if (( $NETIF_CNT != 1 )); then
69 vlog "$v_nonetfix"
70 return
71 fi
72
73 NET=$(LC_ALL=C zonecfg -z $ZONENAME info net)
74 if (( $? != 0 )); then
75 error "$e_badinfo" "net"
76 return
77 fi
78
79 NETIF=$(echo $NET | nawk '{
80 for (i = 1; i < NF; i++) {
81 if ($i == "physical:") {
82 if (length(net) == 0) {
83 i++
84 net = $i
85 } else {
86 multiple=1
87 }
88 }
89 }
90 }
91 END { if (!multiple)
92 print net
93 }')
94
95 if [[ -z "$NETIF" ]]; then
96 vlog "$v_nonetfix"
97 return
98 fi
99
100 NEWHOSTNET=${OLD_HOSTNET%*.*}
101 if [[ "$OLD_HOSTNET" != "$NEWHOSTNET.$NETIF" ]]; then
102 safe_move $OLD_HOSTNET $NEWHOSTNET.$NETIF
103 fi
104 }
105
106 #
107 # Disable all of the shares since the zone cannot be an NFS server.
108 # Note that we disable the various instances of the svc:/network/shares/group
109 # SMF service in the fix_smf function.
110 #
111 fix_nfs()
112 {
113 zonedfs=$ZONEROOT/etc/dfs
114
115 if [[ -h $zonedfs/dfstab || ! -f $zonedfs/dfstab ]]; then
116 error "$e_badfile" "/etc/dfs/dfstab"
117 return
118 fi
119
120 tmpfile=$(mktemp -t)
121 if [[ -z "$tmpfile" ]]; then
122 error "$e_tmpfile"
123 return
124 fi
125
126 nawk '{
127 if (substr($1, 0, 1) == "#") {
128 print $0
129 } else {
130 print "#", $0
131 modified=1
132 }
133 }
134 END {
135 if (modified == 1) {
136 printf("# Modified by p2v ")
137 system("/usr/bin/date")
138 exit 0
139 }
140 exit 1
141 }' $zonedfs/dfstab >>$tmpfile
142
143 if (( $? == 0 )); then
144 if [[ ! -f $zonedfs/dfstab.pre_p2v ]]; then
145 safe_copy $zonedfs/dfstab $zonedfs/dfstab.pre_p2v
146 fi
147 safe_copy $tmpfile $zonedfs/dfstab
148 fi
149 rm -f $tmpfile
150 }
151
152 #
153 # Comment out most of the old mounts since they are either unneeded or
154 # likely incorrect within a zone. Specific mounts can be manually
155 # reenabled if the corresponding device is added to the zone.
156 #
157 fix_vfstab()
158 {
159 if [[ -h $ZONEROOT/etc/vfstab || ! -f $ZONEROOT/etc/vfstab ]]; then
160 error "$e_badfile" "/etc/vfstab"
161 return
162 fi
163
164 tmpfile=$(mktemp -t)
165 if [[ -z "$tmpfile" ]]; then
166 error "$e_tmpfile"
167 return
168 fi
169
170 nawk '{
171 if (substr($1, 0, 1) == "#") {
172 print $0
173 } else if ($1 == "fd" || $1 == "/proc" || $1 == "swap" ||
174 $1 == "ctfs" || $1 == "objfs" || $1 == "sharefs" ||
175 $4 == "nfs" || $4 == "lofs") {
176 print $0
177 } else {
178 print "#", $0
179 modified=1
180 }
181 }
182 END {
183 if (modified == 1) {
184 printf("# Modified by p2v ")
185 system("/usr/bin/date")
186 exit 0
187 }
188 exit 1
189 }' $ZONEROOT/etc/vfstab >>$tmpfile
190
191 if (( $? == 0 )); then
192 if [[ ! -f $ZONEROOT/etc/vfstab.pre_p2v ]]; then
193 safe_copy $ZONEROOT/etc/vfstab \
194 $ZONEROOT/etc/vfstab.pre_p2v
195 fi
196 safe_copy $tmpfile $ZONEROOT/etc/vfstab
197 fi
198 rm -f $tmpfile
199 }
200
201 #
202 # Delete or disable SMF services.
203 #
204 fix_smf()
205 {
206 SMF_UPGRADE=/a/var/svc/profile/upgrade
207
208 #
209 # Fix network services if shared stack.
210 #
211 if [[ "$STACK_TYPE" == "shared" ]]; then
212 vlog "$v_fixnetsvcs"
213
214 NETPHYSDEF="svc:/network/physical:default"
215 NETPHYSNWAM="svc:/network/physical:nwam"
216
217 vlog "$v_enblsvc" "$NETPHYSDEF"
218 zlogin -S $ZONENAME "echo /usr/sbin/svcadm enable $NETPHYSDEF \
219 >>$SMF_UPGRADE" </dev/null
220
221 vlog "$v_dissvc" "$NETPHYSNWAM"
222 zlogin -S $ZONENAME \
223 "echo /usr/sbin/svcadm disable $NETPHYSNWAM \
224 >>$SMF_UPGRADE" </dev/null
225
226 # Disable routing svcs.
227 vlog "$v_dissvc" 'svc:/network/routing/*'
228 zlogin -S $ZONENAME \
229 "echo /usr/sbin/svcadm disable 'svc:/network/routing/*' \
230 >>$SMF_UPGRADE" </dev/null
231 fi
232
233 #
234 # Disable well-known services that don't run in a zone.
235 #
236 vlog "$v_rminvalidsvcs"
237 for i in $(egrep -hv "^#" \
238 /usr/lib/brand/ipkg/smf_disable.lst \
239 /etc/brand/ipkg/smf_disable.conf)
240 do
241 # Disable the svc.
242 vlog "$v_dissvc" "$i"
243 zlogin -S $ZONENAME \
244 "echo /usr/sbin/svcadm disable $i >>$SMF_UPGRADE" </dev/null
245 done
246
247 #
248 # Since zones can't be NFS servers, disable all of the instances of
249 # the shares svc.
250 #
251 vlog "$v_dissvc" 'svc:/network/shares/*'
252 zlogin -S $ZONENAME \
253 "echo /usr/sbin/svcadm disable 'svc:/network/shares/*' \
254 >>$SMF_UPGRADE" </dev/null
255 }
256
257 #
258 # Remove well-known pkgs that do not work inside a zone.
259 #
260 rm_pkgs()
261 {
262 for i in $(egrep -hv "^#" /usr/lib/brand/ipkg/pkgrm.lst \
263 /etc/brand/ipkg/pkgrm.conf)
264 do
265 pkg info $i >/dev/null 2>&1
266 if (( $? != 0 )); then
267 continue
268 fi
269
270 vlog "$v_rmpkg" "$i"
271 zlogin -S $ZONENAME LC_ALL=C \
272 /usr/bin/pkg -R /a uninstall -r $i </dev/null >&2 || \
273 error "$e_rmpkg" $i
274 done
275 }
276
277 #
278 # Zoneadmd writes a one-line index file into the zone when the zone boots,
279 # so any information about installed zones from the original system will
280 # be lost at that time. Here we'll warn the sysadmin about any pre-existing
281 # zones that they might want to clean up by hand, but we'll leave the zonepaths
282 # in place in case they're on shared storage and will be migrated to
283 # a new host.
284 #
285 warn_zones()
286 {
287 zoneconfig=$ZONEROOT/etc/zones
288
289 if [[ -h $zoneconfig/index || ! -f $zoneconfig/index ]]; then
290 error "$e_badfile" "/etc/zones/index"
291 return
292 fi
293
294 NGZ=$(nawk -F: '{
295 if (substr($1, 0, 1) == "#" || $1 == "global")
296 continue
297
298 if ($2 == "installed")
299 printf("%s ", $1)
300 }' $zoneconfig/index)
301
302 # Return if there are no installed zones to warn about.
303 [[ -z "$NGZ" ]] && return
304
305 log "$v_rmzones" "$NGZ"
306
307 NGZP=$(nawk -F: '{
308 if (substr($1, 0, 1) == "#" || $1 == "global")
309 continue
310
311 if ($2 == "installed")
312 printf("%s ", $3)
313 }' $zoneconfig/index)
314
315 log "$v_rmzonepaths"
316
317 for i in $NGZP
318 do
319 log " %s" "$i"
320 done
321 }
322
323 #
324 # failure should unmount the zone if necessary;
325 #
326 ZONE_IS_MOUNTED=0
327 trap trap_exit EXIT
328
329 #
330 # Parse the command line options.
331 #
332 OPT_U=
333 OPT_V=
334 OPT_L=
335 while getopts "b:uvl:" opt
336 do
337 case "$opt" in
338 u) OPT_U="-u";;
339 v) OPT_V="-v";;
340 l) LOGFILE="$OPTARG"; OPT_L="-l \"$OPTARG\"";;
341 *) exit 1;;
342 esac
343 done
344 shift OPTIND-1
345
346 (( $# != 2 )) && exit 1
347
348 [[ -n $LOGFILE ]] && exec 2>>$LOGFILE
349
350 ZONENAME=$1
351 ZONEPATH=$2
352 ZONEROOT=$ZONEPATH/root
353
354 e_badinfo=$(gettext "Failed to get '%s' zone resource")
355 e_badfile=$(gettext "Invalid '%s' file within the zone")
356 e_tmpfile=$(gettext "Unable to create temporary file")
357 v_mkdirs=$(gettext "Creating mount points")
358 v_nonetfix=$(gettext "Cannot update /etc/hostname.{net} file")
359 v_change_var=$(gettext "Changing the pkg variant to nonglobal...")
360 e_change_var=$(gettext "Changing the pkg variant to nonglobal failed")
361 v_update=$(gettext "Updating the zone software to match the global zone...")
362 v_updatedone=$(gettext "Zone software update complete")
363 e_badupdate=$(gettext "Updating the Zone software failed")
364 v_adjust=$(gettext "Updating the image to run within a zone")
365 v_stacktype=$(gettext "Stack type '%s'")
366 v_rmhollowsvcs=$(gettext "Deleting global zone-only SMF services")
367 v_fixnetsvcs=$(gettext "Adjusting network SMF services")
368 v_rminvalidsvcs=$(gettext "Disabling invalid SMF services")
369 v_collectingsmf=$(gettext "Collecting SMF svc data")
370 v_delsvc=$(gettext "Delete SMF svc '%s'")
371 e_delsvc=$(gettext "deleting SMF svc '%s'")
372 v_enblsvc=$(gettext "Enable SMF svc '%s'")
373 e_enblsvc=$(gettext "enabling SMF svc '%s'")
374 v_dissvc=$(gettext "Disable SMF svc '%s'")
375 e_adminf=$(gettext "Unable to create admin file")
376 v_rmpkg=$(gettext "Remove package '%s'")
377 e_rmpkg=$(gettext "removing package '%s'")
378 v_rmzones=$(gettext "The following zones in this image will be unusable: %s")
379 v_rmzonepaths=$(gettext "These zonepaths could be removed from this image:")
380 v_exitgood=$(gettext "Postprocessing successful.")
381
382 #
383 # Do some validation on the paths we'll be accessing
384 #
385 safe_dir etc
386 safe_dir etc/dfs
387 safe_dir etc/zones
388 safe_dir var
389 safe_dir var/log
390 safe_dir var/pkg
391
392 # Now do the work to update the zone.
393
394 # Before booting the zone we may need to create a few mnt points, just in
395 # case they don't exist for some reason.
396 #
397 # Whenever we reach into the zone while running in the global zone we
398 # need to validate that none of the interim directories are symlinks
399 # that could cause us to inadvertently modify the global zone.
400 vlog "$v_mkdirs"
401 if [[ ! -f $ZONEROOT/tmp && ! -d $ZONEROOT/tmp ]]; then
402 mkdir -m 1777 -p $ZONEROOT/tmp || exit $EXIT_CODE
403 fi
404 if [[ ! -f $ZONEROOT/var/run && ! -d $ZONEROOT/var/run ]]; then
405 mkdir -m 1755 -p $ZONEROOT/var/run || exit $EXIT_CODE
406 fi
407 if [[ ! -h $ZONEROOT/etc && ! -f $ZONEROOT/etc/mnttab ]]; then
408 touch $ZONEROOT/etc/mnttab || exit $EXIT_CODE
409 chmod 444 $ZONEROOT/etc/mnttab || exit $EXIT_CODE
410 fi
411 if [[ ! -f $ZONEROOT/proc && ! -d $ZONEROOT/proc ]]; then
412 mkdir -m 755 -p $ZONEROOT/proc || exit $EXIT_CODE
413 fi
414 if [[ ! -f $ZONEROOT/dev && ! -d $ZONEROOT/dev ]]; then
415 mkdir -m 755 -p $ZONEROOT/dev || exit $EXIT_CODE
416 fi
417 if [[ ! -h $ZONEROOT/etc && ! -h $ZONEROOT/etc/svc && ! -d $ZONEROOT/etc/svc ]]
418 then
419 mkdir -m 755 -p $ZONEROOT/etc/svc/volatile || exit $EXIT_CODE
420 fi
421
422 # Check for zones inside of image.
423 warn_zones
424
425 STACK_TYPE=$(zoneadm -z $ZONENAME list -p | nawk -F: '{print $7}')
426 if (( $? != 0 )); then
427 error "$e_badinfo" "stacktype"
428 fi
429 vlog "$v_stacktype" "$STACK_TYPE"
430
431 # Note that we're doing this before update-on-attach has run.
432 fix_net
433 fix_nfs
434 fix_vfstab
435
436 #
437 # Mount the zone so that we can do all of the updates needed on the zone.
438 #
439 vlog "$v_mounting"
440 ZONE_IS_MOUNTED=1
441 zoneadm -z $ZONENAME mount -f || fatal "$e_badmount"
442
443 #
444 # Any errors in these functions are not considered fatal. The zone can be
445 # be fixed up manually afterwards and it may need some additional manual
446 # cleanup in any case.
447 #
448
449 log "$v_adjust"
450 # cleanup SMF services
451 fix_smf
452 # remove invalid pkgs
453 rm_pkgs
454
455 vlog "$v_unmount"
456 zoneadm -z $ZONENAME unmount || fatal "$e_badunmount"
457 ZONE_IS_MOUNTED=0
458
459 is_brand_labeled
460 brand_labeled=$?
461 if (( $brand_labeled == 1 )); then
462 # The labeled brand needs to mount the zone's root dataset back onto
463 # ZONEROOT so we can finish processing.
464 mount_active_ds
465 fi
466
467 # Change the pkging variant from global zone to non-global zone.
468 log "$v_change_var"
469 pkg -R $ZONEROOT change-variant variant.opensolaris.zone=nonglobal || \
470 fatal "$e_change_var"
471
472 #
473 # Run update on attach. State is currently 'incomplete' so use the private
474 # force-update option.
475 # This also leaves the zone in the 'installed' state. This is a known bug
476 # in 'zoneadm attach'. We change the zone state back to 'incomplete' for
477 # now but this can be removed once 'zoneadm attach' is fixed.
478 #
479 log "$v_update"
480 zoneadm -z $ZONENAME attach -U >&2 || fatal "$e_badupdate"
481 zoneadm -z $ZONENAME mark incomplete || fatal "$e_badupdate"
482 log "$v_updatedone"
483
484 [[ -n $OPT_U ]] && unconfigure_zone
485
486 (( $brand_labeled == 1 )) && mount_active_ds
487
488 trap - EXIT
489 vlog "$v_exitgood"
490 exit 0