1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
|
#!/bin/bash
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright 2020 Joyent, Inc.
#
set -o xtrace
fatal()
{
echo "Error: $1"
exit $SMF_EXIT_ERR_FATAL
}
. /lib/svc/share/smf_include.sh
. /lib/svc/share/fs_include.sh
. /lib/sdc/usb-key.sh
# first of all, if we aren't the global zone this doesn't make any sense to run
smf_is_globalzone || exit $SMF_EXIT_OK
# We need the links to /dev/dsk. Rather than trying to play games with manually
# invoking syseventd ask devfsadm to do some work.
/usr/sbin/devfsadm -c disk
function destroy_zpools
{
for pool in $(zpool list -p -o name | grep -v NAME) ; do
zpool destroy -f ${pool}
done
}
function mount_zfs
{
local dataset=$1
local mountpoint=$2
local output=
#
# Try to mount the ZFS dataset. If the mountpoint is busy, wait five
# seconds and try again. Fail if the mount attempt returns EBUSY three
# consecutive times.
#
for i in {1..3}; do
output=$(mount -F zfs ${dataset} ${mountpoint} 2>&1)
if [[ $? -eq 0 ]]; then
break
fi
if [ "${output}" == "mount failed: Device busy" ]; then
sleep 5
else
echo ${output} 1>&2
return
fi
done
# The mount attempt must have failed
echo ${output} 1>&2
}
function unlock_pool
{
local pool=$1
# If the key is already loaded, don't bother trying again
local keystatus="$(zfs get -Hpo value keystatus $pool)"
if [[ "$keystatus" == "available" ]]; then
return
fi
kbmadm unlock $pool && return
echo "Failed to unlock $pool; recovery may be required" | \
tee -a /dev/console >&2
exit $SMF_EXIT_ERR_FATAL
}
/bin/bootparams | grep "^noimport=true" >/dev/null
if [ $? -ne 0 ]; then
# If the zpool doesn't exist, then there's nothing to mount.
# Assume the system zpool is zones, but if a different system pool
# identifies itself (by virtue of the .system_pool file being present in the
# pool's root dataset), then use that system pool instead.
SYS_ZPOOL=zones
# Import specified zpools, or all zpools available
pools=$(/bin/bootparams | egrep "^zpools?=" | cut -d= -f2 | tr , ' ')
if [ -z ${pools} ]; then
pools=$(zpool import | grep "pool:" | awk '{print $2}')
fi
for pool in $pools; do
zpool import -f $pool || continue
is_encr="$(zfs get -Hpo value encryption $pool)"
[[ "$is_encr" != "off" ]] && unlock_pool $pool
# Due to early, failed attempts to support the filesystem_limits
# feature we now need to ensure the dependent feature is enabled.
zpool set feature@extensible_dataset=enabled $pool
if [[ -f /$pool/.system_pool ]]; then
SYS_ZPOOL=$pool
[[ "$is_encr" != "off" ]] && kbmadm set-syspool $pool
fi
done
svccfg -s svc:/system/smartdc/init setprop \
config/zpool=${SYS_ZPOOL}
svccfg -s svc:/system/smartdc/init:default refresh
# If the destroy_zpools boot parameter is set, destroy all zpools
/bin/bootparams | grep "^destroy_zpools=true" >/dev/null
if [ $? -eq 0 ]; then
destroy_zpools
fi
# A machine is reset to its original unsetup state (i.e. a 'factory reset')
# when the smartdc:factoryreset ZFS user property is set on the var dataset.
reset=$(zfs get -H -o value smartdc:factoryreset ${SYS_ZPOOL}/var)
if [ "${reset}" == "yes" ]; then
destroy_zpools
fi
# Capture the zpool's status output in the method's log file for
# troubleshooting.
#
# Note: It is critical that we do not run 'status -v'. If there are errors
# in the zpool error log and the zpool is large (e.g. > 200TB), then the
# lookup for the error file names can take a very long time (several hours).
# This would block the system boot until it completed.
zpool status ${SYS_ZPOOL}
if [ $? -eq 0 ]; then
# Stash the SUNWdefault.xml file so we can update the
# persistent version after mounting zones/config.
cp /etc/zones/SUNWdefault.xml /tmp/
# Mount and configure all system datasets
mount_zfs ${SYS_ZPOOL}/var /var
mount_zfs ${SYS_ZPOOL}/config /etc/zones
mount_zfs ${SYS_ZPOOL}/opt /opt
# Update the the persistent SUNWdefault.xml file to match the
# contents on ramdisk now that zones/config is mounted.
cp /tmp/SUNWdefault.xml /etc/zones/
rm -f /tmp/SUNWdefault.xml
#
# We include a manifest of all files shipped in the platform image,
# along with an MD5 hash of their contents. This was originally
# shipped as "/var/log/manifest", but once a machine is set up, "/var"
# now comes from the pool. The upshot of this is that every SmartOS
# machine has the manifest from the platform at setup time stored in
# "/var/log/manifest". Now that the manifest has moved to an
# accessible location, we should remove this file and replace it with a
# symbolic link.
#
if [[ -f '/var/log/manifest' && ! -L '/var/log/manifest' &&
! -e '/var/log/manifest.original' ]]; then
mv '/var/log/manifest' '/var/log/manifest.original'
ln -s '../../usr/share/smartos/manifest' '/var/log/manifest'
fi
if [[ -z $(/bin/bootparams | grep '^smartos=true') ]]; then
mkdir -p /opt/smartdc/agents/smf
mount -O -F lofs /var/svc/manifest/site /opt/smartdc/agents/smf
fi
if [[ -n $(/bin/bootparams | grep '^headnode=true') || \
-n $(/bin/bootparams | grep '^smartos=true') ]]; then
mkdir /usbkey
mount_zfs ${SYS_ZPOOL}/usbkey /usbkey
fi
if [[ -n $(/bin/bootparams | grep '^smartos=true') ]]; then
mount -F lofs /usbkey/shadow /etc/shadow
mount -F lofs /usbkey/ssh /etc/ssh
fi
swap -a /dev/zvol/dsk/${SYS_ZPOOL}/swap || \
fatal "failed to configure swap device"
#
# Configure the dump device on top of a ZFS volume. In addition to the
# usual dumpadm(1m) call, there are two prerequisites for using this
# volume as a dump device: (1) that zvol must be using the noparity
# checksum algorithem, and (2) the MULTI_VDEV_CRASH_DUMP ZFS feature
# must be enabled. Prerequisite (1) is necessary since the exact
# on-disk value for ZIO_CHECKSUM_NOPARITY has changed, so to avoid a
# flag day on all systems, this service just sets that property again
# every time.
#
zfs set checksum=noparity ${SYS_ZPOOL}/dump || \
fatal "failed to set checksum=noparity on dump zvol"
zpool set feature@multi_vdev_crash_dump=enabled ${SYS_ZPOOL} || \
fatal "failed to enable multi_vdev_crash_dump ZFS feature"
dumpadm -y -d /dev/zvol/dsk/${SYS_ZPOOL}/dump || \
fatal "failed to configure dump device"
zfs list -H -o name ${SYS_ZPOOL}/cores/global >/dev/null 2>&1
if [ $? -ne 0 ]; then
# Booting for the first time on a CN whose cores dataset is setup
# in the 6.x style. Convert to the new style.
zfs destroy -r ${SYS_ZPOOL}/cores
zfs create -o compression=gzip -o mountpoint=none ${SYS_ZPOOL}/cores
zfs create -o quota=10g -o mountpoint=/${SYS_ZPOOL}/global/cores \
${SYS_ZPOOL}/cores/global
fi
ln -s /${SYS_ZPOOL}/global/cores /cores
[[ -f /${SYS_ZPOOL}/currbooted ]] && \
mv /${SYS_ZPOOL}/currbooted /${SYS_ZPOOL}/lastbooted
uname -v >/${SYS_ZPOOL}/currbooted
fi
fi
# The rest only applies to the headnode
/bin/bootparams | grep "^headnode=true" >/dev/null || exit $SMF_EXIT_OK
# If we rebooted during an upgrade, we're in deep trouble.
if [ -d /var/upgrade_in_progress ]; then
echo "ERROR: An upgrade was in progress when the system rebooted." \
>/dev/console
echo " The system is in an indeterminate state, unable to continue." \
>/dev/console
exit $SMF_EXIT_ERR_FATAL
fi
COPYINPOINT=`svcprop -p "joyentfs/usb_copy_path" ${SMF_FMRI}`
DEBUG=`svcprop -p "joyentfs/debug" ${SMF_FMRI}`
if [[ -d /mnt ]]; then
chown root:root /mnt
chmod 700 /mnt
else
mkdir -m 700 /mnt
fi
function make_usb_copy_if_possible
{
[[ -n "${SYS_ZPOOL}" ]] || fatal "don't know system zpool name"
zpool list -Ho name | grep "^${SYS_ZPOOL}\$"
if [[ $? != 0 ]]; then
echo "skipping USB copy setup: no ${SYS_ZPOOL} zpool" >/dev/console
# Still return OK, because this is the expected case for first headnode
# boot.
return 0
fi
USBDATASET=${SYS_ZPOOL}/usbkey
if ! zfs list -Ho name | grep "^${USBDATASET}\$" >/dev/null; then
echo "skipping USB copy setup: no zones/usbkey dataset" >/dev/console
# Still return OK, because as of HEAD-2343 a CN being converted to a HN
# will not yet have this dataset on its first boot as an HN.
return 0
fi
echo "" > /dev/console
echo "Moving files from USB boot device onto disk storage." > /dev/console
echo "This may take several minutes. Please note the time..." > /dev/console
echo "" > /dev/console
echo "" > /dev/console
mkdir ${COPYINPOINT}
mount_zfs ${USBDATASET} ${COPYINPOINT}
(cd ${USBMOUNTPOINT}; rsync -av --log-file=/dev/console --exclude private --exclude os * ${COPYINPOINT})
if [[ -d ${USBMOUNTPOINT}/os ]]; then
(cd ${USBMOUNTPOINT}/os ; \
for dir in $(ls -d *); do
# source comes from pcfs which we've got lowering the case
# of everything, but we normally use capital T and Z for
# buildstamp, so fix it here.
source_dir=${dir}
target_dir=$(echo ${dir} | tr "[:lower:]" "[:upper:]")
mkdir -p ${COPYINPOINT}/os
echo "Copying: ${source_dir}/ ${COPYINPOINT}/os/${target_dir}" > /dev/console
rsync -a ${source_dir}/ ${COPYINPOINT}/os/${target_dir}
done
)
fi
echo "" > /dev/console
echo "Done copying files from USB device" > /dev/console
return 0
}
USBMOUNTPOINT=$(mount_usb_key "")
if [[ $? -ne 0 ]]; then
fatal "couldn't mount USB key"
fi
make_usb_copy_if_possible
exit $?
|