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 #
23 # Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 # Use is subject to license terms.
25 #
26
27 # Restrict executables to /bin, /usr/bin, /usr/sbin and /usr/sfw/bin
28 PATH=/bin:/usr/bin:/usr/sbin:/usr/sfw/bin
29
30 export PATH
31 umask 022
32
33 # Setup i18n output
34 TEXTDOMAIN="SUNW_OST_OSCMD"
35 export TEXTDOMAIN
36
37 # Log passed arguments to file descriptor 2
38 log()
39 {
40 [[ -n $logfile ]] && echo "$@" >&2
41 }
42
43 #
44 # Send the provided printf()-style arguments to the screen and to the
45 # logfile.
46 #
47 screenlog()
48 {
49 typeset fmt="$1"
50 shift
51
52 printf "$fmt\n" "$@"
53 [[ -n $logfile ]] && printf "$fmt\n" "$@" >&2
54 }
55
56 # Print and log provided text if the shell variable "verbose_mode" is set
57 verbose()
58 {
59 [[ -n $verbose_mode ]] && echo "$@"
60 [[ -n $logfile ]] && [[ -n $verbose_mode ]] && echo "$@" >&2
61 }
62
63 unsupported_cpu=\
64 $(gettext "ERROR: Cannot install branded zone: processor must be %s-compatible")
65
66 cmd_not_found=$(gettext "Required command '%s' cannot be found!")
67 cmd_not_exec=$(gettext "Required command '%s' not executable!")
68 zone_initfail=$(gettext "Attempt to initialize zone '%s' FAILED.")
69 path_abs=$(gettext "Pathname specified to -d '%s' must be absolute.")
70
71 cmd_h=$(gettext "%s -z <zone name> %s -h")
72 cmd_full=\
73 $(gettext "%s -z <zone name> %s [-v | -s] [-d <dir>|<device>] [<cluster> ... ]")
74
75 both_modes=$(gettext "%s: error: cannot select both silent and verbose modes")
76
77 not_found=$(gettext "%s: error: file or directory not found.")
78
79 wrong_type=\
80 $(gettext "%s: error: must be a gzip, bzip2, .Z or uncompressed tar archive.")
81
82 not_readable=$(gettext "Cannot read file '%s'")
83
84 no_install=$(gettext "Could not create install directory '%s'")
85 no_log=$(gettext "Could not create log directory '%s'")
86 no_logfile=$(gettext "Could not create log file '%s'")
87
88 install_zone=$(gettext "Installing zone '%s' at root directory '%s'")
89 install_from=$(gettext "from archive '%s'")
90
91 install_fail=$(gettext "Installation of zone '%s' FAILED.")
92 see_log=$(gettext "See the log file:\n '%s'\nfor details.")
93
94 install_abort=$(gettext "Installation of zone '%s' aborted.")
95 install_good=$(gettext "Installation of zone '%s' completed successfully.")
96
97 # Check if commands passed in exist and are executable.
98 check_cmd()
99 {
100 for cmd in "$@"; do
101 if [[ ! -f $cmd ]]; then
102 screenlog "$cmd_not_found" "$cmd"
103 screenlog "$install_abort" "$zonename"
104 exit $ZONE_SUBPROC_NOTCOMPLETE
105 fi
106
107 if [[ ! -x $cmd ]]; then
108 screenlog "$cmd_not_exec" "$cmd"
109 screenlog "$install_abort" "$zonename"
110 exit $ZONE_SUBPROC_NOTCOMPLETE
111 fi
112 done
113 }
114
115 # Post process as tarball-installed zone for use by BrandZ.
116 init_tarzone()
117 {
118 typeset rootdir="$1"
119
120 if ! $branddir/lx_init_zone "$rootdir"; then
121 screenlog "$zone_initfail" "$zonename"
122 return 1
123 fi
124 }
125
126 # Clean up on interrupt
127 trap_cleanup()
128 {
129 msg=$(gettext "Installation cancelled due to interrupt.")
130
131 screenlog "$msg"
132 exit $int_code
133 }
134
135 #
136 # Output the usage message.
137 #
138 # This is done this way due to limitations in the way gettext strings are
139 # extracted from shell scripts and processed. Use of this somewhat awkward
140 # syntax allows us to produce longer lines of text than otherwise would be
141 # possible without wrapping lines across more than one line of code.
142 #
143 usage()
144 {
145 int_code=$ZONE_SUBPROC_USAGE
146
147 echo $(gettext "Usage:")
148 printf " $cmd_h\n" "zoneadm" "install"
149 printf " $cmd_full\n" "zoneadm" "install"
150
151 echo
152
153 echo $(gettext "The installer will attempt to use the default system") \
154 $(gettext "removable disc device if <archive dir> is not") \
155 $(gettext "specified.") | fmt -80
156
157 echo
158
159 echo $(gettext "<cluster> specifies which package cluster you wish") \
160 $(gettext "to install.") | fmt -80
161
162 echo
163 echo $(gettext "The 'desktop' cluster will be installed by default.")
164 echo
165 echo $(gettext "The available clusters are:")
166 echo " + core"
167 echo " + server"
168 echo " + desktop"
169 echo " + development"
170 echo " + all"
171 echo
172
173 echo $(gettext "Each cluster includes all of the clusters preceding") \
174 $(gettext "it, so the 'server' cluster includes the 'core'") \
175 $(gettext "cluster, the 'desktop' cluster includes the 'core'") \
176 $(gettext "and 'server' clusters, and so on.") | fmt -80
177
178 echo
179 echo $(gettext "Examples")
180 echo "========"
181
182 echo $(gettext "Example 1: Install a base Linux system from CDs or a") \
183 $(gettext "DVD using the system default removable disc device:") |
184 fmt -80
185
186 echo
187 echo " # zoneadm -z myzone install"
188 echo
189
190 echo $(gettext "Example 2: Install the 'server' cluster from CDs or") \
191 $(gettext "a DVD via an alternative removable disc device:") |
192 fmt -80
193
194 echo
195 echo " # zoneadm -z myzone install -d /cdrom/cdrom1 server"
196 echo
197
198 echo $(gettext "Example 3: Install the desktop Linux environment") \
199 $(gettext "from an ISO image made available as '/dev/lofi/1' by") \
200 $(gettext "use of lofiadm(1M):") | fmt -80
201
202 echo
203 echo " # zoneadm -z myzone install -d /dev/lofi/1 desktop"
204 echo
205
206 echo $(gettext "Example 4: Install the entire Linux environment from") \
207 $(gettext "ISO images located in the directory") \
208 "'/export/centos_3.8/isos':" | fmt -80
209
210 echo
211 echo " # zoneadm -z myzone install -d /export/centos_3.8/isos all"
212 echo
213
214 echo $(gettext "Example 5: Install from a compressed tar archive of") \
215 $(gettext "an existing Linux installation (a tar ball) with") \
216 $(gettext "verbose output regarding the progress of the") \
217 $(gettext "installation:") | fmt -80
218
219 echo
220 echo " # zoneadm -z myzone install -v -d /tmp/linux_full.tar.gz"
221 echo
222
223 echo $(gettext "Example 6: Install from a compressed tar archive of") \
224 $(gettext "an existing Linux installation (a tar ball) with NO") \
225 $(gettext "output regarding the progress of the installation") \
226 $(gettext "(silent mode.)") | fmt -80
227
228 echo
229
230 echo $(gettext "NOTE: Silent mode is only recommended for use by") \
231 $(gettext "shell scripts and other non-interactive programs:") |
232 fmt -80
233
234 echo
235 echo " # zoneadm -z myzone install -d /tmp/linux_full.tar.gz -s"
236 echo
237
238 exit $int_code
239 }
240
241 #
242 # The main body of the script starts here.
243 #
244 # This script should never be called directly by a user but rather should
245 # only be called by zoneadm to install a BrandZ Linux zone.
246 #
247
248 #
249 # Exit values used by the script, as #defined in <sys/zone.h>
250 #
251 # ZONE_SUBPROC_OK
252 # ===============
253 # Installation was successful
254 #
255 # ZONE_SUBPROC_USAGE
256 # ==================
257 # Improper arguments were passed, so print a usage message before exiting
258 #
259 # ZONE_SUBPROC_NOTCOMPLETE
260 # ========================
261 # Installation did not complete, but another installation attempt can be
262 # made without an uninstall
263 #
264 # ZONE_SUBPROC_FATAL
265 # ==================
266 # Installation failed and an uninstall will be required before another
267 # install can be attempted
268 #
269 ZONE_SUBPROC_OK=0
270 ZONE_SUBPROC_USAGE=253
271 ZONE_SUBPROC_NOTCOMPLETE=254
272 ZONE_SUBPROC_FATAL=255
273
274 #
275 # An unspecified exit or interrupt should exit with ZONE_SUBPROC_NOTCOMPLETE,
276 # meaning a user will not need to do an uninstall before attempting another
277 # install.
278 #
279 int_code=$ZONE_SUBPROC_NOTCOMPLETE
280
281 trap trap_cleanup INT
282
283 # If we weren't passed at least two arguments, exit now.
284 [[ $# -lt 2 ]] && usage
285
286 #
287 # This script is always started with a full path so we can extract the
288 # brand directory name here.
289 #
290 branddir=$(dirname "$0")
291 zonename="$1"
292 zoneroot="$2"
293
294 install_root="$zoneroot/root"
295 logdir="$install_root/var/log"
296
297 shift; shift # remove zonename and zoneroot from arguments array
298
299 unset gtaropts
300 unset install_opts
301 unset install_src
302 unset msg
303 unset silent_mode
304 unset verbose_mode
305
306 while getopts "d:hsvX" opt
307 do
308 case "$opt" in
309 h) usage;;
310 s) silent_mode=1;;
311 v) verbose_mode=1;;
312 d) install_src="$OPTARG" ;;
313 X) install_opts="$install_opts -x" ;;
314 *) usage;;
315 esac
316 done
317 shift OPTIND-1
318
319 # Providing more than one passed argument generates a usage message
320 if [[ $# -gt 1 ]]; then
321 msg=$(gettext "ERROR: Too many arguments provided:")
322
323 screenlog "$msg"
324 screenlog " \"%s\"" "$@"
325 screenlog ""
326 usage
327 fi
328
329 # Validate any free-form arguments
330 if [[ $# -eq 1 && "$1" != "core" && "$1" != "server" && "$1" != "desktop" &&
331 "$1" != "development" && "$1" != "all" ]]; then
332 msg=$(gettext "ERROR: Unknown cluster name specified: %s")
333
334 screenlog "$msg" "\"$1\""
335 screenlog ""
336 usage
337 fi
338
339 # The install can't be both verbose AND silent...
340 if [[ -n $silent_mode && -n $verbose_mode ]]; then
341 screenlog "$both_modes" "zoneadm install"
342 screenlog ""
343 usage
344 fi
345
346 #
347 # Validate that we're running on a i686-compatible CPU; abort the zone
348 # installation now if we're not.
349 #
350 procinfo=$(LC_ALL=C psrinfo -vp | grep family)
351
352 #
353 # All x86 processors in CPUID families 6 or 15 should be i686-compatible,
354 # assuming third party processor vendors follow AMD and Intel's lead.
355 #
356 if [[ "$procinfo" != *" x86 "* ]] ||
357 [[ "$procinfo" != *" family 6 "* && "$procinfo" != *" family 15 "* ]] ; then
358 screenlog "$unsupported_cpu" "i686"
359 exit $int_code
360 fi
361
362 if [[ -n $install_src ]]; then
363 #
364 # Validate $install_src.
365 #
366 # If install_src is a directory, assume it contains ISO images to
367 # install from, otherwise treat the argument as if it points to a tar
368 # ball file.
369 #
370 if [[ "`echo $install_src | cut -c 1`" != "/" ]]; then
371 screenlog "$path_abs" "$install_src"
372 exit $int_code
373 fi
374
375 if [[ ! -a "$install_src" ]]; then
376 screenlog "$not_found" "$install_src"
377 screenlog "$install_abort" "$zonename"
378 exit $int_code
379 fi
380
381 if [[ ! -r "$install_src" ]]; then
382 screenlog "$not_readable" "$install_src"
383 screenlog "$install_abort" "$zonename"
384 exit $int_code
385 fi
386
387 #
388 # If install_src is a block device, a directory, a possible device
389 # created via lofiadm(1M), or the directory used by a standard volume
390 # management daemon, pass it on to the secondary install script.
391 #
392 # Otherwise, validate the passed filename to prepare for a tar ball
393 # install.
394 #
395 if [[ ! -b "$install_src" && ! -d "$install_src" &&
396 "$install_src" != /dev/lofi/* && "$install_src" != /cdrom/* &&
397 "$install_src" != /media/* ]]; then
398 if [[ ! -f "$install_src" ]]; then
399 screenlog "$wrong_type" "$install_src"
400 screenlog "$install_abort" "$zonename"
401 exit $int_code
402 fi
403
404 filetype=`{ LC_ALL=C file $install_src |
405 awk '{print $2}' ; } 2>/dev/null`
406
407 if [[ "$filetype" = "gzip" ]]; then
408 verbose "\"$install_src\": \"gzip\" archive"
409 gtaropts="-xz"
410 elif [[ "$filetype" = "bzip2" ]]; then
411 verbose "\"$install_src\": \"bzip2\" archive"
412 gtaropts="-xj"
413 elif [[ "$filetype" = "compressed" ]]; then
414 verbose "\"$install_src\": Lempel-Ziv" \
415 "compressed (\".Z\") archive."
416 gtaropts="-xZ"
417 elif [[ "$filetype" = "USTAR" ]]; then
418 verbose "\"$install_src\":" \
419 "uncompressed (\"tar\") archive."
420 gtaropts="-x"
421 else
422 screenlog "$wrong_type" "$install_src"
423 screenlog "$install_abort" "$zonename"
424 exit $int_code
425 fi
426 fi
427 fi
428
429 #
430 # Start silent operation and pass the flag to prepare pass the flag to
431 # the ISO installer, if needed.
432 #
433 if [[ -n $silent_mode ]]
434 then
435 exec 1>/dev/null
436 install_opts="$install_opts -s"
437 fi
438
439 #
440 # If verbose mode was specified, pass the verbose flag to lx_distro_install
441 # for ISO or disc installations and to gtar for tarball-based installs.
442 #
443 if [[ -n $verbose_mode ]]
444 then
445 echo $(gettext "Verbose output mode enabled.")
446 install_opts="$install_opts -v"
447 [[ -n $gtaropts ]] && gtaropts="${gtaropts}v"
448 fi
449
450 [[ -n $gtaropts ]] && gtaropts="${gtaropts}f"
451
452 if [[ ! -d "$install_root" ]]
453 then
454 if ! mkdir -p "$install_root" 2>/dev/null; then
455 screenlog "$no_install" "$install_root"
456 exit $int_code
457 fi
458 fi
459
460 if [[ ! -d "$logdir" ]]
461 then
462 if ! mkdir -p "$logdir" 2>/dev/null; then
463 screenlog "$no_log" "$logdir"
464 exit $int_code
465 fi
466 fi
467
468 logfile="${logdir}/$zonename.install.$$.log"
469
470 if ! > $logfile; then
471 screenlog "$no_logfile" "$logfile"
472 exit $int_code
473 fi
474
475 # Redirect stderr to the log file to automatically log any error messages
476 exec 2>>"$logfile"
477
478 #
479 # From here on out, an unspecified exit or interrupt should exit with
480 # ZONE_SUBPROC_FATAL, meaning a user will need to do an uninstall before
481 # attempting another install, as we've modified the directories we were going
482 # to install to in some way.
483 #
484 int_code=$ZONE_SUBPROC_FATAL
485
486 log "Installation started for zone \"$zonename\" `/usr/bin/date`"
487
488 if [[ -n $gtaropts ]]; then
489 check_cmd /usr/sfw/bin/gtar $branddir/lx_init_zone
490
491 screenlog "$install_zone" "$zonename" "$zoneroot"
492 screenlog "$install_from" "$install_src"
493 echo
494 echo $(gettext "This process may take several minutes.")
495 echo
496
497 if ! ( cd "$install_root" && gtar "$gtaropts" "$install_src" ) ; then
498 log "Error: extraction from tar archive failed."
499 else
500 if ! [[ -d "${install_root}/bin" &&
501 -d "${install_root}/sbin" ]]; then
502 log "Error: improper or incomplete tar archive."
503 else
504 $branddir/lx_init_zone "$install_root" &&
505 init_tarzone "$install_root"
506
507 #
508 # Emit the same code from here whether we're
509 # interrupted or exiting normally.
510 #
511 int_code=$?
512 fi
513 fi
514
515 if [[ $int_code -eq ZONE_SUBPROC_OK ]]; then
516 log "Tar install completed for zone '$zonename' `date`."
517 else
518 log "Tar install failed for zone \"$zonename\" `date`."
519
520 fi
521 else
522 check_cmd $branddir/lx_distro_install
523
524 $branddir/lx_distro_install -z "$zonename" -r "$zoneroot" \
525 -d "$install_src" -l "$logfile" $install_opts "$@"
526
527 #
528 # Emit the same code from here whether we're interrupted or exiting
529 # normally.
530 #
531 int_code=$?
532
533 [[ $int_code -eq $ZONE_SUBPROC_USAGE ]] && usage
534 fi
535
536 if [[ $int_code -ne $ZONE_SUBPROC_OK ]]; then
537 screenlog ""
538 screenlog "$install_fail" "$zonename"
539 screenlog ""
540
541 #
542 # Only make a reference to the log file if one will exist after
543 # zoneadm exits.
544 #
545 [[ $int_code -ne $ZONE_SUBPROC_NOTCOMPLETE ]] &&
546 screenlog "$see_log" "$logfile"
547
548 exit $int_code
549 fi
550
551 #
552 # After the install completes, we've likely moved a new copy of the logfile into
553 # place atop the logfile we WERE writing to, so if we don't reopen the logfile
554 # here the shell will continue writing to the old logfile's inode, meaning we
555 # would lose all log information from this point on.
556 #
557 exec 2>>"$logfile"
558
559 screenlog ""
560 screenlog "$install_good" "$zonename"
561 screenlog ""
562
563 echo $(gettext "Details saved to log file:")
564 echo " \"$logfile\""
565 echo
566
567 exit $ZONE_SUBPROC_OK