diff options
Diffstat (limited to 'usr/src/cmd/svc')
46 files changed, 3882 insertions, 928 deletions
diff --git a/usr/src/cmd/svc/common/notify_params.c b/usr/src/cmd/svc/common/notify_params.c index 16104d899e..73ea6ccc5a 100644 --- a/usr/src/cmd/svc/common/notify_params.c +++ b/usr/src/cmd/svc/common/notify_params.c @@ -287,7 +287,7 @@ listnotify_print(nvlist_t *nvl, const char *event) { char *fmri; nvlist_t **params; - size_t n; + uint_t n; int32_t tset; int i; diff --git a/usr/src/cmd/svc/configd/backend.c b/usr/src/cmd/svc/configd/backend.c index 466d2d54cc..40726dd1d0 100644 --- a/usr/src/cmd/svc/configd/backend.c +++ b/usr/src/cmd/svc/configd/backend.c @@ -22,6 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2017 Joyent, Inc. */ /* @@ -2194,7 +2195,24 @@ backend_tx_begin(backend_type_t t, backend_tx_t **txp) UPDATE_TOTALS((*txp)->bt_be, bt_exec, ts, vts); if (r == SQLITE_FULL) (*txp)->bt_full = 1; - r = backend_error((*txp)->bt_be, r, errmsg); + /* + * We explicitly handle an ENOSPC error here for the beginning of the + * transaction, instead of in backend_error, which calls backend_panic + * for this case, resulting in the death of svc.configd. That may be + * appropriate in other cases, but in this case we would rather fail so + * that configd remains up and the caller gets an approprate error. The + * failure mode is that there is not enough swap space to open the + * non-persistent database, so there won't be enough space to restart + * configd, leaving SMF in a state requiring manual intervention. + */ + if (r == SQLITE_CANTOPEN && errno == ENOSPC && + (*txp)->bt_type == BACKEND_TYPE_NONPERSIST) { + configd_info("Warning: no space to open %s\n", + bes[BACKEND_TYPE_NONPERSIST]->be_path); + r = REP_PROTOCOL_FAIL_NO_RESOURCES; + } else { + r = backend_error((*txp)->bt_be, r, errmsg); + } if (r != REP_PROTOCOL_SUCCESS) { assert(r != REP_PROTOCOL_DONE); diff --git a/usr/src/cmd/svc/configd/rc_node.c b/usr/src/cmd/svc/configd/rc_node.c index a5b968c53c..33cb2be7a2 100644 --- a/usr/src/cmd/svc/configd/rc_node.c +++ b/usr/src/cmd/svc/configd/rc_node.c @@ -24,6 +24,9 @@ * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ /* * rc_node.c - In-memory SCF object management diff --git a/usr/src/cmd/svc/milestone/Makefile b/usr/src/cmd/svc/milestone/Makefile index 901727dc9f..576576ba2e 100644 --- a/usr/src/cmd/svc/milestone/Makefile +++ b/usr/src/cmd/svc/milestone/Makefile @@ -21,6 +21,8 @@ # # Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. # +# Copyright 2019 Joyent, Inc. +# include ../../Makefile.cmd @@ -30,6 +32,7 @@ BUILTXML= \ console-login.xml FSSVCS= \ + joyent-fs.xml \ local-fs.xml \ minimal-fs.xml \ root-fs.xml \ @@ -38,6 +41,7 @@ FSSVCS= \ FSMANIFESTS= $(FSSVCS:%=$(ROOTSVCSYSTEMFILESYSTEM)/%) NETSVCS= \ + network-early-admin.xml \ network-initial.xml \ network-install.xml \ network-iptun.xml \ @@ -75,8 +79,12 @@ SYSTEMSVCS= \ early-manifest-import.xml \ identity.xml \ manifest-import.xml \ + mdata.xml \ process-security.xml \ rmtmpfiles.xml \ + smartdc-config.xml \ + smartdc-init.xml \ + smartdc-ur.xml \ vtdaemon.xml SYSTEMMANIFESTS = $(SYSTEMSVCS:%=$(ROOTSVCSYSTEM)/%) @@ -103,6 +111,7 @@ SVCMETHOD=\ console-login \ devices-audio \ devices-local \ + fs-joyent \ fs-local \ fs-minimal \ fs-root \ @@ -110,6 +119,9 @@ SVCMETHOD=\ identity-domain \ identity-node \ manifest-import \ + mdata-execute \ + mdata-fetch \ + net-early-admin \ net-loc \ net-loopback \ net-init \ @@ -122,6 +134,11 @@ SVCMETHOD=\ net-routing-setup \ net-svc \ rmtmpfiles \ + smartdc-config \ + smartdc-init \ + smartdc-ur \ + sysidtool-net \ + sysidtool-system \ vtdaemon $(ROOTSVCMETHOD) := FILEMODE = 0555 @@ -161,4 +178,4 @@ $(ROOTSVCSYSTEM)/svc/%: % $(ROOT)/lib/svc/share/%: %.share $(INS.rename) -clean lint _msg: +clean _msg: diff --git a/usr/src/cmd/svc/milestone/console-login b/usr/src/cmd/svc/milestone/console-login index 81010231b5..8cc6495b2d 100644 --- a/usr/src/cmd/svc/milestone/console-login +++ b/usr/src/cmd/svc/milestone/console-login @@ -73,6 +73,20 @@ if [ "$val" = "/dev/vt/1" ]; then exit $SMF_EXIT_ERR_CONFIG fi +# In SmartOS we use ttyb for metadata so we set to a non-existent device here +# to disable the service and exit. +if [[ ${val} == "/dev/term/b" && \ + $(sysinfo | json "Product") == "SmartDC HVM" ]]; then + + val=/dev/do_not_use_ttyb_in_a_vm +fi + +if [[ ! -e $val ]]; then + # This device doesn't exist, can't run a tty on it. + /usr/sbin/svcadm disable $SMF_FMRI + exit $SMF_EXIT_OK +fi + args="$args -d $val" args="$args `getproparg -l ttymon/label`" diff --git a/usr/src/cmd/svc/milestone/fs-joyent b/usr/src/cmd/svc/milestone/fs-joyent new file mode 100755 index 0000000000..0a9dc065a3 --- /dev/null +++ b/usr/src/cmd/svc/milestone/fs-joyent @@ -0,0 +1,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 $? diff --git a/usr/src/cmd/svc/milestone/fs-root b/usr/src/cmd/svc/milestone/fs-root index 9652eaaf94..f9de44c831 100644 --- a/usr/src/cmd/svc/milestone/fs-root +++ b/usr/src/cmd/svc/milestone/fs-root @@ -22,6 +22,7 @@ # # Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2015 Nexenta Systems, Inc. All rights reserved. +# Copyright 2016 Joyent, Inc. # # Make sure that the libraries essential to this stage of booting can be found. @@ -64,92 +65,27 @@ if smf_is_nonglobalzone; then exit $SMF_EXIT_OK fi -# -# Root is already mounted (by the kernel), but still needs to be -# checked, possibly remounted and entered into mnttab. First -# mount /usr if it is a separate file system. If the file system -# type is something other than zfs, mount it read-only. This must -# be done first to allow utilities such as fsck and setmnt to -# reside on /usr minimizing the space required by the root file -# system. -# -readvfstab "/usr" < $vfstab -if [ -n "$mountp" ]; then - if [ "$fstype" = zfs ]; then - mountfs - /usr $fstype $mntopts - || exit $SMF_EXIT_ERR_FATAL - else - # - # Must use -o largefiles here to ensure the - # read-only mount does not fail as a result of - # having a large file present on /usr. This gives - # fsck a chance to fix up the largefiles flag - # before we remount /usr read-write. - # - if [ "x$mntopts" = x- ]; then - mntopts='ro,largefiles' - else - checkopt largefiles $mntopts - if [ "x$option" != xlargefiles ]; then - mntopts="largefiles,$mntopts" - fi - - checkopt ro $mntopts - if [ "x$option" != xro ]; then - mntopts="ro,$mntopts" - fi - - # - # Requesting logging on a read-only mount - # causes errors to be displayed, so remove - # "logging" from the list of options for now. - # The read-write mount performed later will - # specify the logging option if appropriate. - # - - checkopt logging $mntopts - if [ "x$option" = xlogging ]; then - mntopts="$otherops" - fi - fi - - mountfs -O /usr $fstype $mntopts - || exit $SMF_EXIT_ERR_FATAL - fi -fi +/sbin/mount -F ufs -o remount,rw,nologging /devices/ramdisk:a / +/usr/sbin/lofiadm -X -a /usr.lgz # -# if we are booted from zfs, the /usr mount probably won't be a -# legacy mount. Use the standard zfs mount command instead. - -readmnttab "/" < /etc/mnttab -if [ "$fstype" = zfs ]; then - mountp=`/sbin/zfs get -H -o value mountpoint $special/usr 2>/dev/null` - # - # if mountp = /usr, there is a non-legacy mount of /usr - # in the boot environment being booted. - # - if [ "x$mountp" = "x/usr" ] ; then - /sbin/zfs mount $special/usr - if [ $? != 0 ] ; then - msg='zfs-mount failed' - echo $msg - echo "$SMF_FMRI:" $msg >/dev/msglog - exit $SMF_EXIT_ERR_FATAL - fi +# Prior to mounting /usr, devfsadm is not yet available. As such, we must +# locate the lofi block device node in /devices rather than in /dev. This +# path has changed over time so we try both the old (pre-partition support) +# and new paths. +# +lofi_devices_path='/devices/pseudo/lofi@1:disk' +if [ ! -b "$lofi_devices_path" ]; then + lofi_devices_path='/devices/pseudo/lofi@0:1' + if [ ! -b "$lofi_devices_path" ]; then + echo 'could not locate lofi block device in /devices' >&2 + exit $SMF_EXIT_ERR_FATAL fi fi -# -# Also mount /boot now so that things like keymap.sh can access -# boot properties through eeprom. Readonly isn't required because -# /boot (and other pcfs filesystems) aren't fsck'ed at boot yet. -# Also, we don't account for caching /boot as it must be on a local -# disk. So what's in vfstab is fine as it stands; just look to see -# if it's there and avoid the mount if not. -# -readvfstab "/boot" < $vfstab - -if [ -n "$mountp" ]; then - mountfs - /boot $fstype $mntopts - || exit $SMF_EXIT_ERR_FATAL +if ! /sbin/mount -F ufs -o ro "$lofi_devices_path" /usr; then + echo "could not mount /usr from $lofi_devices_path" >&2 + exit $SMF_EXIT_ERR_FATAL fi # diff --git a/usr/src/cmd/svc/milestone/fs-usr b/usr/src/cmd/svc/milestone/fs-usr index 715cb1bca3..e4b6a263e1 100644 --- a/usr/src/cmd/svc/milestone/fs-usr +++ b/usr/src/cmd/svc/milestone/fs-usr @@ -19,171 +19,23 @@ # # CDDL HEADER END # - # # Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T. # All rights reserved. # Copyright 2016 Nexenta Systems, Inc. +# Copyright 2012, Joyent, Inc. All rights reserved. # Copyright 2019 OmniOS Community Edition (OmniOSce) Association. # - . /lib/svc/share/smf_include.sh -. /lib/svc/share/fs_include.sh - -UPDATEFILE=/etc/svc/volatile/boot_archive_needs_update - -# -# Once root is read/write we can enable the dedicated dumpdevice if it exists -# locally. This is an optimization as svc-dumpadm will attempt do this later. -# -dump_setup() -{ - [ -r /etc/dumpadm.conf ] && . /etc/dumpadm.conf - - readswapdev $DUMPADM_DEVICE < $vfstab - - # - # Make sure that the dump save area has been configured before - # proceeding. If the variable has not been defined or does not exist - # then bail out early. This will prevent us from configuring a - # dump save area before a hostname has been configured (i.e after - # sys-unconfig has been invoked). - # - [ -z "$DUMPADM_SAVDIR" ] && return - - # - # If we have a dedicated dump device, then go ahead and configure it. - # - if [ "x$special" != "x$DUMPADM_DEVICE" ]; then - if [ -x /usr/sbin/dumpadm -a -b $DUMPADM_DEVICE ]; then - /usr/sbin/dumpadm -u || exit $SMF_EXIT_ERR_CONFIG - fi - fi -} - -# -# Write a unique id into this kernel image; this will be included -# in the dump header and panicbuf of any crashdump of this image. -# -if [ -x /usr/sbin/dumpadm ]; then - /usr/sbin/dumpadm -i -fi - -rootiszfs=0 -# get the fstype of root -readmnttab / </etc/mnttab -if [ "$fstype" = zfs ] ; then - rootiszfs=1 - dump_setup -fi - -# -# Add physical swap. -# -/sbin/swapadd -1 - -# -# Check and remount the / (root) file system. -# For NFS mounts, force the llock option on. -# -if smf_is_globalzone && [ $rootiszfs = 0 ]; then - readvfstab / < $vfstab - checkfs $fsckdev $fstype $mountp || exit $SMF_EXIT_ERR_FATAL - checkopt "llock" $mntopts - mntopts='remount' - - [ -n "$otherops" ] && mntopts="${mntopts},${otherops}" - [ "$fstype" = nfs ] && mntopts="${mntopts},llock" - - mountfs -m $mountp $fstype $mntopts - || exit $SMF_EXIT_ERR_FATAL -fi - -# -# Check and remount the /usr file system (formerly mounted read-only). -# Unless root is zfs, in which case we've already mounted /usr read-write -# -if [ "$rootiszfs" = 0 ] ; then - readvfstab /usr < $vfstab - if [ "$mountp" ]; then - checkopt ro $mntopts - if [ "x$option" != xro ]; then - checkfs $fsckdev $fstype $mountp || - exit $SMF_EXIT_ERR_FATAL - if [ "x$mntopts" != x- ]; then - mntopts="remount,$mntopts" - else - mntopts="remount" - fi - - mountfs - /usr $fstype $mntopts - || - exit $SMF_EXIT_ERR_FATAL - fi - fi -fi - -# -# Check and mount the /usr/platform file system. This should only be -# present when a SunOS 5.5 (Solaris 2.5) or greater client is being -# administered by a SunOS 5.4 or less host. -# -readvfstab /usr/platform < $vfstab -if [ "$mountp" ]; then - checkfs $fsckdev $fstype $mountp || exit $SMF_EXIT_ERR_FATAL - mountfs - $mountp $fstype $mntopts - || exit $SMF_EXIT_ERR_FATAL -fi - -# -# Mount the fd file systems if mount point exists. -# -readvfstab /dev/fd < $vfstab -if [ "$mountp" -a -d /dev/fd ]; then - mountfs - /dev/fd - - - || exit $SMF_EXIT_ERR_FATAL -fi - -if [ -f "${UPDATEFILE}" ]; then - /usr/sbin/bootadm update-archive - if [ $? != 0 ]; then - cecho "" - cecho "WARNING: Automatic update of the boot archive failed." - cecho "Update the archives using 'bootadm update-archive'" - cecho "command and then reboot the system from the same device" - cecho "that was previously booted." - cecho "" - exit $SMF_EXIT_ERR_FATAL - fi - rm -f $UPDATEFILE - cecho "" - cecho "WARNING: Reboot required." - cecho "The system has updated the cache of files (boot archive) that" - cecho "is used during the early boot sequence. To avoid booting and" - cecho "running the system with the previously out-of-sync version of" - cecho "these files, the system will be restarted." - cecho "" +mount /dev/fd - bootcmd=`/usr/sbin/eeprom bootcmd | /usr/bin/sed -e 's#bootcmd=##g'` - if [ `uname -p` = "i386" ]; then - /usr/sbin/reboot -f dryrun - if [ $? = 0 ]; then - /usr/sbin/reboot -f -- "$bootcmd" - exit $SMF_EXIT_OK - fi - boot_prop=`/usr/sbin/svccfg -s svc:/system/boot-config:default \ - listprop config/auto-reboot-safe | \ - /usr/bin/nawk '{ print $3}'` - if [ "$boot_prop" = "true" ]; then - /usr/sbin/reboot -p - exit $SMF_EXIT_OK - fi - cecho "" - cecho "It has not been possible to restart automatically." - cecho "Reboot the system from the same device that was" - cecho "previously booted." - cecho "" - exit $SMF_EXIT_ERR_FATAL - fi - /usr/sbin/reboot -- "$bootcmd" +if smf_is_globalzone; then + # svc.startd makes a backup of the repo on boot. Since this is a + # live-image, the backup takes up an unnecessary 4MB in memory, so remove + # it now. + rm -f /etc/svc/repository-* fi exit $SMF_EXIT_OK diff --git a/usr/src/cmd/svc/milestone/identity-node b/usr/src/cmd/svc/milestone/identity-node index c9b20ba669..6b740f3dd1 100644 --- a/usr/src/cmd/svc/milestone/identity-node +++ b/usr/src/cmd/svc/milestone/identity-node @@ -27,18 +27,30 @@ # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" +# Copyright 2019 Joyent, Inc. +# . /lib/svc/share/smf_include.sh . /lib/svc/share/net_include.sh +set -o xtrace + # Make sure that the libraries essential to this stage of booting can be found. LD_LIBRARY_PATH=/lib; export LD_LIBRARY_PATH # -# If DHCP was used on a primary interface then set the hostname -# that was returned. If no hostname was returned, set the name -# to be "unknown". The hostname must be set to something, because +# For the GZ, use one of the following values for hostname, in order: +# * DHCP hostname (if set on a primary interface) +# * hostname value from config file +# * hostname bootparam +# * if not a headnode: +# * admin MAC address +# * any other MAC address +# +# If none of the above could be found, default to "headnode" for headnodes, and +# "unknown" for non-headnodes. +# +# The hostname must be set to something, because # tooltalk will hang unless the name can be locally resolved. # Sendmail also requires the name to be resolvable locally. # Later, in inetsvc, we create a name "unknown" and create a entry @@ -51,44 +63,124 @@ LD_LIBRARY_PATH=/lib; export LD_LIBRARY_PATH # kernel if /etc/nodename does not exist, as is expected on an initial boot. # +set_gz_hostname() { + hostname=${CONFIG_hostname} + if [ -z "$hostname" ] || [ "$hostname" == "unknown" ]; then + hostname=$SYSINFO_Bootparam_hostname + fi + + if [ -n "$hostname" ] && [ "$hostname" != "unknown" ]; then + return + fi + + # $headnode is set by load_sdc_config() + if [ "$headnode" == "true" ]; then + hostname="headnode" + return + fi + + if [[ -n ${SYSINFO_NIC_admin} ]]; then + eval "admin_mac=\${SYSINFO_Network_Interface_${SYSINFO_NIC_admin}_MAC_Address}" + if [[ -n ${admin_mac} ]]; then + hostname=$(echo "${admin_mac}" | tr ':' '-') + return + fi + fi + + fallback_mac=$(set | grep "^SYSINFO_Network_Interface_.*_MAC_Address" | head -n1 | cut -d'=' -f2) + if [[ -n ${fallback_mac} ]]; then + hostname=$(echo "${fallback_mac}" | tr ':' '-') + return + fi + + hostname="unknown" +} + + smf_netstrategy case "$_INIT_NET_STRATEGY" in - "dhcp") hostname=`/sbin/dhcpinfo Hostname` ;; - "rarp") hostname=`/sbin/hostconfig -h -p bootparams` - trap 'intr=1' 2 3 - while [ -z "$hostname" -a ! -f /etc/.UNCONFIGURED -a \ - -z "$intr" ]; do - echo "re-trying host configuration..." - # Restrict this to IPv4 interfaces. - /sbin/ifconfig -adD4 auto-revarp up - hostname=`/sbin/hostconfig -h -p bootparams` - done - trap 2 3 ;; - "none") hostname="`shcat /etc/nodename 2>/dev/null`" - if [ -z "$hostname" ]; then - if smf_is_globalzone; then - hostname=`/sbin/hostconfig -h -p bootparams` - else - hostname=`/sbin/uname -n` - fi - fi ;; + "dhcp") hostname=`/sbin/dhcpinfo Hostname` ;; + "rarp") hostname=`/sbin/hostconfig -h -p bootparams` + trap 'intr=1' 2 3 + while [ -z "$hostname" -a ! -f /etc/.UNCONFIGURED -a \ + -z "$intr" ]; do + echo "re-trying host configuration..." + # Restrict this to IPv4 interfaces. + /sbin/ifconfig -adD4 auto-revarp up + hostname=`/sbin/hostconfig -h -p bootparams` + done + trap 2 3 ;; + # /etc/nodename defaults to "unknown" on SmartOS + "none") hostname="`shcat /etc/nodename 2>/dev/null`" + if [ -z "$hostname" ]; then + if smf_is_globalzone; then + hostname=`/sbin/hostconfig -h -p bootparams` + else + hostname=`/sbin/uname -n` + fi + fi ;; esac +# Load sysinfo variables with SYSINFO_ prefix and config variables with +# CONFIG_ prefix. +# Note: since we're still starting up, "soft" values like network IP and such could +# not be set yet. + +if smf_is_globalzone; then + . /lib/sdc/config.sh + + load_sdc_sysinfo + + if boot_file_config_enabled; then + load_boot_file_config + else + load_sdc_config + fi +fi + # # If the netstrategy was unsuccessful and we haven't got a locally configured # name, default to "unknown" # -if [ -z "$hostname" ]; then - hostname="`shcat /etc/nodename 2>/dev/null`" - if [ -z "$hostname" ]; then - hostname="unknown" - fi +if [ -z "$hostname" ] || [ "$hostname" == "unknown" ]; then + hostname="`shcat /etc/nodename 2>/dev/null`" + if [ -z "$hostname" ] || [ "$hostname" == "unknown" ]; then + if smf_is_globalzone; then + set_gz_hostname + else + hostname="unknown" + fi + fi +fi + +if smf_is_globalzone; then + echo "$hostname" > /etc/nodename fi /sbin/uname -S $hostname -echo "Hostname: `/sbin/uname -n`" > /dev/msglog +# Reloading sysinfo here serves two purposes: +# - getting the IP info (which should exist now) +# - updating the host info (which we just set) +eval $(/usr/bin/sysinfo -f -p | sed -e "s/^/SYSINFO_/") + +# Try to add the /etc/hosts entry if we can find an IP +if [[ -n ${SYSINFO_NIC_admin} ]]; then + eval "ipaddr=\${SYSINFO_Network_Interface_${SYSINFO_NIC_admin}_IPv4_Address}" +fi +if [[ -z ${ipaddr} ]]; then + ipaddr=$(set | grep "^SYSINFO_Network_Interface_.*_IPv4_Address" | head -n1 | cut -d'=' -f2) +fi +if [[ -n ${ipaddr} ]]; then + fullname="" + + if [ -n "$CONFIG_dns_domain" ]; then + fullname=" ${hostname}.${CONFIG_dns_domain}" + fi + + printf "${ipaddr}\t${hostname}${fullname}\n" >> /etc/hosts +fi # Reset the library path now that we are past the critical stage unset LD_LIBRARY_PATH diff --git a/usr/src/cmd/svc/milestone/joyent-fs.xml b/usr/src/cmd/svc/milestone/joyent-fs.xml new file mode 100644 index 0000000000..f21eae27d8 --- /dev/null +++ b/usr/src/cmd/svc/milestone/joyent-fs.xml @@ -0,0 +1,93 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + Copyright 2005 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License, Version 1.0 only + (the "License"). You may not use this file except in compliance + with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END +--> + +<service_bundle type='manifest' name='SUNWcsr:filesystem-joyent'> + +<service + name='system/filesystem/smartdc' + type='service' + version='1'> + + <create_default_instance enabled='true' /> + + <single_instance/> + + <dependency + name='usr' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/filesystem/usr' /> + </dependency> + + <dependency + name='kbmd' + grouping='optional_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/kbmd:default' /> + </dependency> + + <!-- + Start method timeout is infinite to handle potentially unbounded + fsck times. + --> + <exec_method + type='method' + name='start' + exec='/lib/svc/method/fs-joyent' + timeout_seconds='0' /> + + <exec_method + type='method' + name='stop' + exec=':true' + timeout_seconds='0' /> + + <property_group name='startd' type='framework'> + <propval name='duration' type='astring' value='transient' /> + </property_group> + <property_group name='joyentfs' type='application'> + <stability value='Evolving'/> + <propval name='debug' type='boolean' value='false'/> + <propval name='usb_copy_path' type='astring' value='/usbkey'/> + <propval name='usb_mountpoint' type='astring' value='usbkey'/> + </property_group> + + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> + Joyent file system mounts + </loctext> + </common_name> + </template> +</service> + +</service_bundle> diff --git a/usr/src/cmd/svc/milestone/make-console-login-xml b/usr/src/cmd/svc/milestone/make-console-login-xml index 336ba8fe45..bcb13c1c7c 100644 --- a/usr/src/cmd/svc/milestone/make-console-login-xml +++ b/usr/src/cmd/svc/milestone/make-console-login-xml @@ -24,6 +24,8 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2019 Joyent, Inc. +# cat >console-login.xml <<EOF <?xml version="1.0"?> @@ -31,6 +33,8 @@ cat >console-login.xml <<EOF Copyright 2008 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. + Copyright 2015 Joyent, Inc. + NOTE: This service manifest is not editable; its contents will be overwritten by package or patch operations, including operating system upgrade. Make customizations in a different @@ -125,14 +129,14 @@ cat >console-login.xml <<EOF <property_group name='ttymon' type='application'> <propval name='value_authorization' type='astring' value='solaris.smf.value.vt' /> - <propval name='device' type='astring' value='/dev/console' /> + <propval name='device' type='astring' value='/dev/wscons' /> <propval name='label' type='astring' value='console' /> <propval name='timeout' type='count' value='0' /> <propval name='nohangup' type='boolean' value='true' /> <propval name='modules' type='astring' value='ldterm,ttcompat' /> <propval name='prompt' type='astring' - value='\`uname -n\` console login:' /> + value='\`uname -n\` wscons login:' /> <propval name='terminal_type' type='astring' value='' /> </property_group> @@ -140,8 +144,39 @@ cat >console-login.xml <<EOF <instance name='default' enabled='true'> </instance> +EOF + +for tty in a b c d; do + cat >>console-login.xml <<EOF +<instance name='tty$tty' enabled='true'> + + <dependency + name='system-console' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/console-login:default' /> + </dependency> + + <!-- these are passed to ttymon in the method script --> + <property_group name='ttymon' type='application'> + <propval name='value_authorization' type='astring' + value='solaris.smf.value.vt' /> + <propval name='device' type='astring' value='/dev/term/$tty' /> + <propval name='label' type='astring' value='115200' /> + <propval name='timeout' type='count' value='0' /> + <propval name='nohangup' type='boolean' value='true' /> + <propval name='modules' type='astring' + value='ldterm,ttcompat' /> + <propval name='prompt' type='astring' + value='\`uname -n\` tty$tty login:' /> + <propval name='terminal_type' type='astring' + value='xterm' /> + </property_group> +</instance> EOF +done # Note that this script file is normally parsed during build by sh(1). # When the parser encounters an EOF token (like the one above), it diff --git a/usr/src/cmd/svc/milestone/manifest-import b/usr/src/cmd/svc/milestone/manifest-import index 30f23c26b4..b67361183f 100644 --- a/usr/src/cmd/svc/milestone/manifest-import +++ b/usr/src/cmd/svc/milestone/manifest-import @@ -74,13 +74,17 @@ function svccfg_apply { } # -# If the smf repository has file entries that are missing +# If the smf/manifest table has file entries that are missing # then there is work to be done by the cleanup process. # function cleanup_needwork { - smfmfiles=`svcprop -p manifestfiles '*' 2>/dev/null | - nawk -v early="$early" '$2 == "astring" && - (early != "true" || $3 ~ "^/lib/") { print $3 }'` + if [ "$early" == true ]; then + smfmfiles=`/usr/bin/svcprop smf/manifest | \ + awk '(/^lib_/ && /\/manifestfile /) {print $3}'` + else + smfmfiles=`/usr/bin/svcprop smf/manifest | \ + awk '/\/manifestfile / {print $3}'` + fi nw=`/lib/svc/bin/mfstscan $smfmfiles 2>&1 1>/dev/null` [ "$nw" ] && return 1 @@ -201,8 +205,13 @@ function import_manifests { rm -f $logf - nonsite_dirs=`/usr/bin/find $basedir/* -name site \ - -prune -o -type d -print -prune` + if [ "${basedir}" == "/opt/custom/smf" ]; then + # Special case where we will just import from the root, not subdirs + nonsite_dirs=${basedir} + else + nonsite_dirs=`/usr/bin/find $basedir/* -name site \ + -prune -o -type d -print -prune` + fi if [ -n "$_MFST_DEBUG" ]; then nonsite_manifests=`/lib/svc/bin/mfstscan $nonsite_dirs` @@ -454,6 +463,10 @@ else import_manifests "/lib/svc/manifest" true import_manifests "/var/svc/manifest" true + if [ -d "/opt/custom/smf" ]; then + import_manifests "/opt/custom/smf" true + fi + # # Apply profiles # @@ -472,9 +485,9 @@ fi # 6. Final actions. # -if $activity; then - /usr/sbin/svcadm _smf_backup "manifest_import" || true -fi +#if $activity; then +# /usr/sbin/svcadm _smf_backup "manifest_import" || true +#fi # # If the filesystem is NOT read only then move the repo back to perm diff --git a/usr/src/cmd/svc/milestone/mdata-execute b/usr/src/cmd/svc/milestone/mdata-execute new file mode 100755 index 0000000000..fca08ffbc7 --- /dev/null +++ b/usr/src/cmd/svc/milestone/mdata-execute @@ -0,0 +1,53 @@ +#!/usr/bin/bash +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at http://smartos.org/CDDL +# +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file. +# +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# + +set -o xtrace + +. /lib/svc/share/smf_include.sh +smf_is_globalzone && exit ${SMF_EXIT_OK} + +# If we got as far as running the user-script the 'provision' was a success +# from here out a failure will leave the zone running. +if [ -f /var/svc/provisioning ]; then + mv /var/svc/provision{ing,_success} +fi + +if [[ -x /var/svc/mdata-operator-script ]]; then + /var/svc/mdata-operator-script + operator_script_exit=$? + if [[ ${operator_script_exit} -gt 0 ]]; then + echo "WARNING: operator-script failed: exited ${operator_script_exit}" \ + >&2 + fi +fi + +user_script_exit=${SMF_EXIT_OK} +if [ -x /var/svc/mdata-user-script ]; then + /var/svc/mdata-user-script + [ $? -gt 0 ] && user_script_exit=${SMF_EXIT_ERR_FATAL} +fi + +exit ${user_script_exit} diff --git a/usr/src/cmd/svc/milestone/mdata-fetch b/usr/src/cmd/svc/milestone/mdata-fetch new file mode 100755 index 0000000000..10574ca7e0 --- /dev/null +++ b/usr/src/cmd/svc/milestone/mdata-fetch @@ -0,0 +1,477 @@ +#!/usr/bin/bash +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at http://smartos.org/CDDL +# +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file. +# +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2022 Joyent, Inc. +# + +export PS4='[\D{%FT%TZ}] ${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' +export PATH=/usr/bin:/usr/sbin:$PATH + +set -o xtrace + +. /lib/svc/share/smf_include.sh +smf_is_globalzone && exit ${SMF_EXIT_OK} + +if [ ! -x /usr/sbin/mdata-get ]; then + echo "Metadata mdata-get tool not found." + exit ${SMF_EXIT_ERR_FATAL} +fi + +function fatal() { + if [[ -n $1 ]]; then + echo "FATAL: $*" >&2 + fi + exit ${SMF_EXIT_ERR_FATAL} +} + +# Test if an address looks like an IPv4 address. +function isIPv4() { + [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] +} + +# Test if an address looks like an IPv4 address + CIDR. +function isIPv4AndCIDR() { + [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$ ]] +} + +# Test if an address looks like an IPv6 address. +function isIPv6() { + [[ "${1,,}" =~ ^[0-9a-f]*:[0-9a-f]*:[:0-9a-f]*$ ]] +} + +# Test if an address looks like an IPv6 address + CIDR. +function isIPv6AndCIDR() { + [[ "${1,,}" =~ ^[0-9a-f]*:[0-9a-f]*:[:0-9a-f]*/[0-9]{1,3}$ ]] +} + +# For old zones that were created prior to OS-2253 and bumping the mdata:fetch +# start timeout, we need to fix this otherwise we could timeout waiting for the +# socket. +cur_timeout=$(svcprop -p start/timeout_seconds svc:/smartdc/mdata:fetch) +if [[ -z ${cur_timeout} || ${cur_timeout} -lt 1800 ]]; then + # The current manifest has an old timeout value, fix in case we timeout + # here. XXX we can still hit OS-2296 here where smf will forget that we + # set this. + svccfg -s svc:/smartdc/mdata:fetch 'setprop start/timeout_seconds = 1800' + svcadm refresh svc:/smartdc/mdata:fetch +fi + +# This waits until /.zonecontrol/metadata.sock exists then exits 0 +/usr/vm/sbin/filewait /.zonecontrol/metadata.sock + +if [[ ! -e /.zonecontrol/metadata.sock ]]; then + # this is a bug since filewait should not have returned until file existed. + fatal "missing /.zonecontrol/metadata.sock, Unable to start mdata:fetch" +fi + +# Update sysinfo to ensure values that come from metadata are populated. +/usr/bin/sysinfo -fu + +echo "Retrieving metadata user-data" +/usr/sbin/mdata-get user-data >/var/db/mdata-user-data.new +case $? in + 0) + echo "Metadata user-data successfuly retrieved." + mv /var/db/mdata-user-data{.new,} + ;; + 1) + echo "Metadata user-data not defined." + rm -f /var/db/mdata-user-data{,.new} + ;; + *) + echo "Metadata couldn't be retrieved." + exit ${SMF_EXIT_ERR_FATAL} + ;; +esac + +echo "Retrieving metadata user-script..." +/usr/sbin/mdata-get user-script >/var/svc/mdata-user-script.new +case $? in + 0) + echo "Metadata user-script successfuly retrieved." + mv /var/svc/mdata-user-script{.new,} + chmod +x /var/svc/mdata-user-script + ;; + 1) + echo "Metadata user-script not defined." + rm -f /var/svc/mdata-user-script{,.new} + ;; + *) + echo "Metadata couldn't be retrieved." + exit ${SMF_EXIT_ERR_FATAL} + ;; +esac + +echo "Retrieving metadata operator-script..." +/usr/sbin/mdata-get sdc:operator-script >/var/svc/mdata-operator-script.new +case $? in + 0) + echo "Metadata operator-script successfuly retrieved." + mv /var/svc/mdata-operator-script{.new,} + chmod +x /var/svc/mdata-operator-script + ;; + 1) + echo "Metadata operator-script not defined." + rm -f /var/svc/mdata-operator-script{,.new} + ;; + *) + echo "Metadata couldn't be retrieved." + exit ${SMF_EXIT_ERR_FATAL} + ;; +esac + +echo "Retrieving tmpfs value..." +tmpfs=$(/usr/sbin/mdata-get sdc:tmpfs) +if [[ $? == 0 && -n ${tmpfs} && -f /etc/vfstab ]]; then + check="swap - /tmp tmpfs"; + + if [[ ${tmpfs} == "0" ]]; then + # When tmpfs is set 0, we remove any entry from /etc/vfstab but cannot + # adjust the "live" value as current /tmp will be in-use. On reboot the + # new value will take effect. + + grep -v "^${check}" /etc/vfstab > /etc/vfstab.new \ + && mv /etc/vfstab.new /etc/vfstab + else + new="swap - /tmp tmpfs - yes size=${tmpfs}m"; + if ! /usr/bin/grep "^${new}" /etc/vfstab; then + if ! /usr/bin/grep "^${check}" /etc/vfstab; then + # no tmpfs line. add it. + echo "${new}" >> /etc/vfstab + else + # existing tmpfs line, but wrong value. fix it. + /usr/bin/sed -i "" -e "s|^swap.*/tmp.*tmpfs.*$|${new}|" /etc/vfstab + echo $? + fi + + if mount | grep "^/tmp"; then + # Also fix current size, since /etc/vfstab didn't have our correct line + # but only if we have /tmp mounted at all. If not, we'll have to wait + # until the next reboot since /tmp will be in-use. + /usr/sbin/mount -F tmpfs -o remount,size=${tmpfs}m /tmp + fi + + fi + fi +fi + +# +# If we have NFS volumes, we'll add them to vfstab, and enable the nfs/client +# service so that will mount the volumes for us. +# +echo "Retrieving volume metadata..." + +volumes_added=0 +while IFS="|" read -r nfsvolume mountpoint name mode type; do + + cat >&2 <<EOF +*** VOLUME *** +NFSVOLUME: ${nfsvolume} +MOUNTPOINT: ${mountpoint} +MODE: ${mode} +NAME: ${name} +TYPE: ${type} +EOF + + if [[ -n ${type} && ${type} != "tritonnfs" ]]; then + fatal "unsupported volume type: ${type}" + fi + + # if we don't have volume and mountpoint, we can't add vfstab lines + if [[ -z ${nfsvolume} || -z ${mountpoint} ]]; then + fatal "invalid volume specification (need both volume & mountpoint)" + fi + + if ! grep "^${nfsvolume}[ ]" /etc/vfstab; then + if [[ -z ${mode} ]]; then + mode="rw" + fi + + if [[ ${mode} != "rw" && ${mode} != "ro" ]]; then + fatal "invalid volume mode: '${mode}'" + fi + + line=$(printf "%s - %s nfs - yes %s\n" "${nfsvolume}" "${mountpoint}" "${mode}") + volumes_added=$((${volumes_added}+1)) + + mkdir -p ${mountpoint} + echo "${line}" >> /etc/vfstab + fi +done < <(/usr/sbin/mdata-get sdc:volumes \ + | /usr/bin/json \ + -d '|' \ + -a nfsvolume mountpoint name mode type) + +if [[ ${volumes_added} -gt 0 ]]; then + for svc in \ + svc:/network/nfs/nlockmgr:default \ + svc:/network/nfs/status:default \ + svc:/network/rpc/bind:default \ + svc:/network/nfs/client:default \ + ; do + + svcadm enable ${svc} || fatal "Unable to enable ${svc}" + done +fi + +# We use the special sdc:nics value here though this is not an interface for +# use elsewhere. If this is changed please also update agent.js in the metadata +# agent. +# +# We run this every startup in case nics have changed since last boot. As +# network/physical has an optional_all dependency on this service, we'll have +# had our chance to write these files out before networking comes up. This +# might eventually be replaced by network/physical grabbing data directly. +echo "Retrieving nics data..." +while IFS="|" read -r iface ip ips netmask primary gateway gateways; do + + # if we don't have interface name, we don't know what files to write out. + if [[ -z ${iface} ]]; then + continue; + fi + + # A VM created on an older platform may not have the "ips" or "gateways" + # properties, so we will need to grab the older "ip" and "gateway" versions + # instead. + [[ -z $ips ]] && ips=$ip + [[ -z $gateways ]] && gateways=$gateway + + # We remove the hostname.netX file first, so we can append to a clean file + rm -f /etc/hostname.${iface} + + # remove any existing DHCP or addrconf configuration, since we'll create one + # if it belongs later + rm -f /etc/dhcp.${iface} + rm -f /etc/addrconf.${iface} + + OLDIFS=$IFS + IFS=$',' + for ip in $ips; do + # so it shows up in the logs + echo "iface[${iface}] ip[${ip}] netmask[${netmask}]" \ + "primary[${primary}] gateway[${gateway}]" + + [[ -z ${ip} ]] && continue; + + if [[ ${ip} == "dhcp" ]]; then + touch /etc/dhcp.${iface} + if hostname=`mdata-get sdc:hostname`; then + echo "inet ${hostname}" >> /etc/hostname.${iface} + fi + elif [[ ${ip} == "addrconf" ]]; then + touch /etc/addrconf.${iface} + elif isIPv4 ${ip} && [[ -n ${netmask} ]]; then + # We're using an older configuration where the routing prefix is + # specified using a mask instead of CIDR notation. We'll need to + # invoke ifconfig differently. + echo "${ip} netmask ${netmask} up" >> /etc/hostname.${iface} + elif isIPv4AndCIDR ${ip} \ + || isIPv6AndCIDR ${ip} \ + || isIPv6 ${ip}; then + # Either a routing prefix was specified, or, in the case of IPv6, we + # won't specify one and fall back on NDP to find it when we configure + # our interfaces. + echo "${ip} up" >> /etc/hostname.${iface} + fi + done + + if [[ ${primary} == "true" ]]; then + rm -f /etc/defaultrouter + for gateway in $gateways; do + if [[ -n ${gateway} ]] && isIPv4 ${gateway}; then + echo "${gateway}" >> /etc/defaultrouter + fi + done + fi + IFS=$OLDIFS + + # XXX we leave old hostname.netX files around and just replace when we have + # one next. +done < <(/usr/sbin/mdata-get sdc:nics \ + | /usr/bin/json \ + -d '|' \ + -e 'this.ips = this.ips ? this.ips.join(",") : ""' \ + -e 'this.gateways = this.gateways ? this.gateways.join(",") : ""' \ + -a interface ip ips netmask primary gateway gateways) + +# rebuild resolv.conf +resolvers=$(mdata-get sdc:resolvers) +resolvers_result=$? + +# Determine if resolv.conf is managed for us +maintain_resolvers=$(mdata-get sdc:maintain_resolvers) +maintain_result=$? +if [[ ${maintain_result} != 0 ]]; then + echo "Error getting maintain_resolvers, code: ${maintain_result}" + maintain_resolvers="false" +fi + +# If this is our first boot, write an initial set of resolvers +if [[ -f /var/svc/provisioning ]]; then + echo "First boot: writing resolvers" + maintain_resolvers="true" +fi + +if [[ ${resolvers_result} == 0 && ${maintain_resolvers} == "true" ]]; then + + # if dns_domain is missing or broken, we still write out, just w/o search + search="search $(mdata-get sdc:dns_domain)" + if [[ $? != 0 ]]; then + search= + fi + + if [[ ${resolvers} == "[]" ]]; then + nameservers= + else + nameservers=$(echo ${resolvers} | json -a | sed -e "s/^/nameserver /") + fi + + rm -f /etc/.resolv.conf.tmp + if [[ -n ${search} ]]; then + echo "${search}" > /etc/.resolv.conf.tmp + fi + if [[ -n ${nameservers} ]]; then + echo "${nameservers}" >> /etc/.resolv.conf.tmp + fi + + cp /etc/.resolv.conf.tmp /etc/resolv.conf \ + && cat /etc/.resolv.conf.tmp >&2 \ + && rm -f /etc/.resolv.conf.tmp +else + if [[ ${resolvers_result} == 0 ]]; then + echo "Error getting resolvers, code: ${resolvers_result}" + fi + + if [[ ${maintain_resolvers} == "true" ]]; then + echo "Not setting resolvers, maintain_resolvers=${maintain_resolvers}" + fi +fi + + +# Fetch routes + +# It is possible to specify the same route in several different ways using +# route(1m). We therefore use route(1m) itself to manage adding, deleting +# and determining duplicates. +zone_routes_file=/etc/inet/static_routes +vmadm_routes_file=/etc/inet/static_routes.vmadm +tmpdir=$(mktemp -d /tmp/mdata.XXXXXX) + +if [ -z $tmpdir ]; then + echo "Error creating temporary directory." + exit ${SMF_EXIT_ERR_FATAL} +fi + +# directory structure for the new copy of static_routes.vmadm (the one that +# will replace the current static_routes.vmadm once all of the adds and +# deletes have been applied): +new_root=${tmpdir}/new-routes +new_inet=${new_root}/etc/inet +# directory structure for the previous copy of static_routes.vmadm (used to +# determine routes that have been removed since the last time mdata-fetch +# was run): +old_root=${tmpdir}/old-routes +old_inet=${old_root}/etc/inet +# directory structure for the zone's persistent routes - those not created +# by vmadm (used to determine if vmadm routes are duplicates): +zone_root=${tmpdir}/zone-routes +zone_inet=${zone_root}/etc/inet + +mkdir -p $new_inet +mkdir -p ${old_root}/etc/inet +mkdir -p ${zone_root}/etc/inet + +if [[ -f ${vmadm_routes_file} ]]; then + cp $vmadm_routes_file ${old_inet}/static_routes +fi + +function route_in_per_zone_file() +{ + cp $zone_routes_file $zone_inet + output=$(route -pR $zone_root add $* 2>&1) + [[ $output =~ "entry exists" ]] +} + +# If re-running this script after initial boot, network/physical and +# network/routing-setup are already enabled, so apply routing adds and +# deletes manually +if [[ $(/usr/bin/svcs -H -o state network/routing-setup) == "online" ]]; then + routing_up="true" +fi + +while IFS="|" read -r gateway dst linklocal; do + echo "route: gateway[${gateway}] dst[${dst}] linklocal[${linklocal}]" + route_type="" + if [[ ${linklocal} == "true" ]]; then + route_type="-interface " + fi + route_str="${route_type}${dst} ${gateway}" + + if route_in_per_zone_file $route_str; then + echo "not adding duplicate route: ${route_str}" + # the zone has also defined this route; do nothing + else + echo "adding route to file: ${route_str}" + route -pR $new_root add $route_str + if [[ -n "${routing_up}" ]]; then + route add $route_str + fi + fi + + route -pR $old_root delete $route_str +done < <(/usr/sbin/mdata-get sdc:routes \ + | /usr/bin/json -d '|' -a gateway dst linklocal) + + +# Anything left in the old static_routes file is a delete. Don't delete the +# route from the routing tables if there's a duplicate route in the zone's +# static_routes file +if [[ -f ${old_inet}/static_routes ]]; then + egrep -v "^(#|$)" ${old_inet}/static_routes | while read -r route_str; do + if [[ "${route_str}" == "" ]]; then + continue + fi + + if route_in_per_zone_file "$route_str"; then + echo "not deleting duplicate route: ${route_str}" + else + if [[ -n "${routing_up}" ]]; then + route delete $route_str + fi + fi + done +fi + +if [[ -f ${new_inet}/static_routes ]]; then + cp ${new_inet}/static_routes ${vmadm_routes_file} +else + rm ${vmadm_routes_file} +fi +rm -rf $tmpdir + + +# Unconditionally enable mdata:execute, so that the last provisioning step +# is always taken (regardless of whether user-script exists or not) +svcadm enable smartdc/mdata:execute + +exit ${SMF_EXIT_OK} diff --git a/usr/src/cmd/svc/milestone/mdata.xml b/usr/src/cmd/svc/milestone/mdata.xml new file mode 100644 index 0000000000..152a7cb485 --- /dev/null +++ b/usr/src/cmd/svc/milestone/mdata.xml @@ -0,0 +1,39 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<service_bundle type="manifest" name="mdata"> + <service name="smartdc/mdata" type="service" version="1"> + <dependency name="filesystem" grouping="require_all" restart_on="error" type="service"> + <service_fmri value="svc:/system/filesystem/minimal" /> + </dependency> + <property_group name="startd" type="framework"> + <propval name="duration" type="astring" value="transient" /> + <propval name="ignore_error" type="astring" value="core,signal" /> + </property_group> + <instance name="fetch" enabled="true"> + <dependency name="boot-file" grouping="exclude_all" restart_on="refresh" type="path"> + <service_fmri value="file://localhost/tmp/.FIRST_REBOOT_NOT_YET_COMPLETE"/> + </dependency> + <dependency name="rmtmpfiles" grouping="optional_all" restart_on="error" type="service"> + <service_fmri value="svc:/system/rmtmpfiles" /> + </dependency> + <exec_method type="method" name="start" exec="/lib/svc/method/mdata-fetch" timeout_seconds="1800" /> + <exec_method type="method" name="stop" exec=":true" timeout_seconds="60" /> + </instance> + <instance name="execute" enabled="false"> + <dependency name="network" grouping="require_all" restart_on="error" type="service"> + <service_fmri value="svc:/milestone/multi-user:default" /> + </dependency> + <dependency name="mdata" grouping="require_all" restart_on="error" type="service"> + <service_fmri value="svc:/smartdc/mdata:fetch" /> + </dependency> + <exec_method type="method" name="start" exec="/lib/svc/method/mdata-execute" timeout_seconds="300" /> + <exec_method type="method" name="stop" exec=":true" timeout_seconds="60" /> + </instance> + <stability value="Evolving" /> + <template> + <common_name> + <loctext xml:lang="C">Joyent SDC metadata handler</loctext> + </common_name> + </template> + </service> +</service_bundle> diff --git a/usr/src/cmd/svc/milestone/minimal-fs.xml b/usr/src/cmd/svc/milestone/minimal-fs.xml index b7af22bfcd..844760baa1 100644 --- a/usr/src/cmd/svc/milestone/minimal-fs.xml +++ b/usr/src/cmd/svc/milestone/minimal-fs.xml @@ -44,11 +44,11 @@ <single_instance/> <dependency - name='usr' + name='joyent' grouping='require_all' restart_on='none' type='service'> - <service_fmri value='svc:/system/filesystem/usr' /> + <service_fmri value='svc:/system/filesystem/smartdc' /> </dependency> <dependency diff --git a/usr/src/cmd/svc/milestone/net-early-admin b/usr/src/cmd/svc/milestone/net-early-admin new file mode 100644 index 0000000000..a01881c904 --- /dev/null +++ b/usr/src/cmd/svc/milestone/net-early-admin @@ -0,0 +1,260 @@ +#!/bin/ksh93 +# +# +# 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. +# + +# Traditionally, when a Triton compute node boots, the network is not +# configured until after the local filesystems are mounted (in fact +# network/physical:default depends upon /system/filesystem/smartdc). Most +# obviously, in the case of a head node or a standalone SmartOS install, the +# network configuration is stored on the local zpool, so the network cannot +# be configured until this happens. +# +# For Triton compute nodes with encrypted zpools, we must enable the admin +# network before the local zpool filesytems are online -- we have to be able +# to communicate to the head node services to obtain the pin to unlock the +# local zpool. For PXE booted Triton compute nodes, we therefore configure +# the admin network sooner via the network/early-admin:default service. When +# network/physical:default runs, it will skip the configuration of the admin +# network and configuring the remaining interfaces. +# +# If we are not a Triton compute node, we exit successfully almost immediately +# without configuring the admin network. When the network/physical:default +# service runs, it will configure all the network interfaces as it traditionally +# has + +PATH=/bin:/usr/bin:/sbin:/usr/sbin + +. /lib/svc/share/smf_include.sh +. /lib/sdc/config.sh +. /lib/sdc/network.sh + +PS4='+ [$LINENO] ' + +set -o xtrace + +if ! smf_is_globalzone; then + echo "Non-global zone; no action required; exiting" + exit $SMT_EXIT_OK +fi + +# XXX Until OS-8367 is sorted out, immediately enable the varpd service. +# SMF will properly cope with things if it fails (and blocks both +# network/physical and zones). +svcadm enable varpd + +if ! boot_file_config_enabled; then + echo "Boot-time networking files not present; exiting" + exit $SMF_EXIT_OK +fi + +function fatal +{ + # XXX: For SMF methods, does it matter/better to redirect to stderr? + echo "Error: $*" >&2 + exit $SMF_EXIT_ERR_FATAL +} + +if ! boot_file_config_valid; then + echo "ERROR: boot-time network config file incorrect" >&2 + exit $SMF_EXIT_ERR_CONFIG +fi + +unset aggrs +unset tags +typeset -A tagv +typeset -A aggr_links aggr_mode +typeset -A ip ip6 netmask gateway gateway6 mtu mac +/usr/lib/sdc/net-boot-config | while IFS="=" read var value; do + if [[ "$var" =~ _nic$ ]]; then + name=${var%_nic} + tags+=("${name}") + tagv[$name]="$value" + elif [[ "$var" =~ _aggr$ ]]; then + name=${var%_aggr} + aggrs+=(${name}) + aggr_links[$name]="${value//,/ }" + elif [[ "$var" =~ _lacp_mode$ ]]; then + name=${var%_lacp_mode} + aggr_mode[$name]="$value" + elif [[ "$var" =~ _mtu$ ]]; then + (( value < 1500 || value > 65535 )) && + fatal "ERROR: $var MTU value \'$value\' is not in" \ + "range [1500, 65535]" + + name=${var%_mtu} + mtu[$name]="$value" + elif [[ "$var" =~ _ip$ ]]; then + name=${var%_ip} + ip[$name]="$value" + elif [[ "$var" =~ _ip6$ ]]; then + name=${var%_ip6} + ip6[$name]="$value" + elif [[ "$var" =~ _netmask$ ]]; then + name=${var%_netmask} + netmask[$name]="$value" + elif [[ "$var" =~ _gateway$ ]]; then + name=${var%_gateway} + gateway[$name]="$value" + elif [[ "$var" =~ _gateway6$ ]]; then + name=${var%_gateway6} + gateway6[$name]="$value" + elif [[ "$var" =~ _mac$ ]]; then + name=${var%_mac} + mac[$name]="$value" + elif [[ "$var" == "dns_resolvers" ]]; then + dns_resolvers=(${value//,/ }) + fi + + eval "CONFIG_$var"="$value" +done + +# This must happen befor any other dladm commands (which includes log_if_state) +# or else dladm commands can fail +dladm init-phys + +log_if_state before + +typeset -A mac_to_link +out=$(dladm show-phys -mpo link,address) +(( $? == 0 )) || fatal "dladm show-phys failed" +while IFS=: read link addr; do + macaddr=$(normalize_mac $addr) + mac_to_link["$macaddr"]="$link" +done <<< "$out" + +ADMIN_NIC_TAG=${CONFIG_admin_tag:-"admin"} +[[ -n "${tagv[$ADMIN_NIC_TAG]}" ]] || \ + fatal "ERROR: admin nic tag '$ADMIN_NIC_TAG' not present" + +# Set $nic to the link that has the admin nic tag. For unfortunate +# historic reasons, for aggrs, the MAC address and link name of an aggr +# are the same value by net-boot-config (and the source of many errors). +# If ${aggr_links[$nic]} is non-empty, it means $nic isn't a MAC address +# but an aggr name (and we're done). Otherwise we must map the MAC address +# from $tagv back to the link. +nic="${tagv[$ADMIN_NIC_TAG]}" +if [[ -z "${aggr_links[$nic]}" ]]; then + if !valid_mac "$nic"; then + fatal "ERROR: admin mac address $nic not found on system" + fi + + _nic=${mac_to_link[$nic]} + if [[ -z "$_nic" ]]; then + fatal "ERROR: Invalid value of ${ADMIN_NIC_TAG}_nic ($nic)" + fi + nic=$_nic +fi + +# If there are other nic tags configured on the same link as +# the admin tag, find the largest MTU to use to set the +# datalink MTU +dlmtu="${mtu[$ADMIN_NIC_TAG]}" + +for tag in ${tags[@]}; do + tagmac="${tagv[$tag]}" + + # If the 'mac' for the nic tag is actually an aggr, the + # tag will appear in $aggr_links (and we want to use that as + # the link name). If it is not an aggr, we need to map the + # MAC address to the link name so we can check if $tag + # resides on the same link as the admin interface + if [[ -n "${aggr_links[$tagmac]}" ]]; then + link="$tagmac" + else + link="${mac_to_link[$tagmac]}" + fi + + if [[ "$link" == "$nic" && $dlmtu -lt ${mtu[$tag]} ]]; then + dlmtu=${mtu[$tag]} + fi +done + + +if [[ -n "${aggr_links[$nic]}" ]]; then + mode=${aggr_mode[$nic]:-"off"} + + for l in ${aggr_links[$nic]}; do + [[ -n "${mac_to_link[$l]}" ]] || fatal "MAC '$l' not present" + link="${mac_to_link[$l]}" + if [[ -n "$dlmtu" ]]; then + dladm set-linkprop -p mtu=$dlmtu $link || \ + fatal "ERROR: Failed to set mtu on $link to $dlmtu" + fi + links+="${link} " + done + links="${links% }" + + echo "Creating aggr: $nic (mode=$mode, links=${links})" + dladm create-aggr -l ${links// / -l } -L $mode $nic +fi + +if [[ -n "$dlmtu" ]]; then + dladm set-linkprop -p mtu=$dlmtu $nic || \ + fatal "ERROR: Failed to set mtu on aggr $nic to $dlmtu" +fi + +driver=${nic%%+([0-9])} +get_link_state $nic +if [[ "$link_state" == "down" ]]; then + echo "admin nic '${nic}' is down: unplumbing" + /sbin/ifconfig $nic down unplumb + wait_for_nic_state $nic "unknown" +fi + +# There's some sort of race condition in the bnx driver: if the plumb +# command comes too soon after the unplumb, the interface can come up +# in a state where it never fires interrupts for link state changes. +if [[ "$driver" == "bnx" ]]; then + sleep 5 +fi + +/sbin/ifconfig $nic plumb mtu ${mtu[$ADMIN_NIC_TAG]} +wait_for_nic_state $nic "up" + +if [[ -n "${ip[$ADMIN_NIC_TAG]}" ]]; then + /sbin/ifconfig $nic inet ${ip[$ADMIN_NIC_TAG]} \ + netmask ${netmask[$ADMIN_NIC_TAG]:-"+"} up + [[ -n "${gateway[$ADMIN_NIC_TAG]}" ]] && \ + /usr/sbin/route add default ${gateway[$ADMIN_NIC_TAG]} +fi + +if [[ -n "${ip6[$ADMIN_NIC_TAG]}" ]]; then + /sbin/ifconfig $nic inet6 plumb mtu ${mtu[$ADMIN_NIC_TAG]} + [[ "${ip6[$ADMIN_NIC_TAG]}" != "addrconf" ]] && \ + /sbin/ifconfig $nic inet6 addif ${ip6[$ADMIN_NIC_TAG]} preferred up + [[ -n "${gateway6[$ADMIN_NIC_TAG]}" ]] && \ + /usr/sbin/route add -inet6 default ${gateway6[$ADMIN_NIC_TAG]} +fi + +# Add just the routes reachable through the admin network -- usually these are +# only present with rack aware networking (RAN) +/usr/lib/sdc/net-boot-config --routes | while read dst gw; do + if ! ip_in_net $gw ${ip[$ADMIN_NIC_TAG]} ${netmask[$ADMIN_NIC_TAG]}; then + continue + fi + route add "$dst" "$gw" +done + +if [[ -n "${CONFIG_dns_domain}" && -n ${dns_resolvers[0]} ]]; then + echo "search ${CONFIG_dns_domain}" > /etc/resolv.conf + for serv in ${dns_resolvers[@]}; do + echo "nameserver $serv" >> /etc/resolv.conf + done +fi + +touch /etc/svc/volatile/.early_admin_setup + +log_if_state after diff --git a/usr/src/cmd/svc/milestone/net-physical b/usr/src/cmd/svc/milestone/net-physical index 15929cb28a..5c459a3622 100644 --- a/usr/src/cmd/svc/milestone/net-physical +++ b/usr/src/cmd/svc/milestone/net-physical @@ -1,4 +1,4 @@ -#!/sbin/sh +#!/usr/bin/ksh93 # # CDDL HEADER START # @@ -25,12 +25,22 @@ # All rights reserved. # Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2012 Milan Jurik. All rights reserved. +# Copyright 2021 Joyent, Inc. # Copyright 2021 Tintri by DDN, Inc. All rights reserved. # -# Copyright 2020 OmniOS Community Edition (OmniOSce) Association. + +# +# NOTE: Upstream illumos has IPMP configuration and upgrading of ipadm.conf in +# here. SmartOS does not. Recent improvements to IPMP specifically for +# upgrading ipadm.conf do not appear here, therefore. +# . /lib/svc/share/smf_include.sh -. /lib/svc/share/net_include.sh +. /lib/sdc/config.sh +. /lib/sdc/network.sh + +set -o errexit +set -o xtrace # # In a shared-IP zone we need this service to be up, but all of the work @@ -38,557 +48,832 @@ # failing if we try to do it), so just bail out. # In the global zone and exclusive-IP zones we proceed. # -smf_configure_ip || exit $SMF_EXIT_OK +smf_configure_ip || exit ${SMF_EXIT_OK} # Make sure that the libraries essential to this stage of booting can be found. LD_LIBRARY_PATH=/lib; export LD_LIBRARY_PATH +# Time (in seconds) to wait for admin NIC to get DHCP address before continuing. +ADMIN_DHCP_TIMEOUT=300 +ActiveAggrLinks= +typeset -A ActiveAggrLinks + smf_netstrategy -if smf_is_globalzone; then - net_reconfigure || exit $SMF_EXIT_ERR_CONFIG - - # Update PVID on interfaces configured with VLAN 1 - update_pvid - - # - # Upgrade handling. The upgrade file consists of a series of dladm(8) - # commands. Note that after we are done, we cannot rename the upgrade - # script file as the file system is still read-only at this point. - # Defer this to the manifest-import service. - # - upgrade_script=/var/svc/profile/upgrade_datalink - if [ -f "${upgrade_script}" ]; then - . "${upgrade_script}" - fi - - # - # Upgrade handling for ibd: - # After we are done with the upgrade handling, we can not set the - # ibd/ibd_upgraded property to "true" as the file system is - # read-only at this point. It will be done later by ibd-post-upgrade - # service. - # - if [ -x /sbin/ibd_upgrade ]; then - ibd_upgraded=`/bin/svcprop -c -p ibd/ibd_upgraded \ - $SMF_FMRI 2> /dev/null` - if [ "$ibd_upgraded" != "true" ]; then - /sbin/ibd_upgrade -v - fi - fi - - # - # Bring up simnets, link aggregations and initialize security objects. - # Note that link property initialization is deferred until after - # IP interfaces are plumbed to ensure that the links will not - # be unloaded (and the property settings lost). We should bring - # up simnets prior to VLANs/Aggrs to enable creation of VLANs/Aggrs - # over simnets. - # - /sbin/dladm up-simnet - /sbin/dladm up-aggr - /sbin/dladm up-vlan - /sbin/dladm up-part - /sbin/dladm init-secobj - # - # Bring up VNICs - # - /sbin/dladm up-vnic - # - # Create flows via flowadm. - # - /sbin/flowadm init-flow - # - # Bring up overlays - # Note that there may be VNICs configured over these overlays but - # these cannot be brought up until the network interface on which - # varpd will listen is enabled, and that doesn't happen until - # near the end of this script. Therefore VNIC initialisation is - # repeated below if overlays are present. - # - /sbin/dladm up-overlay -fi +function add_active_aggr_links +{ + set -o xtrace + typeset alink -# -# If the system was net booted by DHCP, hand DHCP management off to the -# DHCP agent (ifconfig communicates to the DHCP agent through the -# loopback interface). -# -if [ -n "$_INIT_NET_IF" -a "$_INIT_NET_STRATEGY" = "dhcp" ]; then - /sbin/dhcpagent -a -fi + for alink in ${2//,/ }; do + ActiveAggrLinks[$alink]=$1 + done +} -# -# The network initialization is done early to support diskless and -# dataless configurations. For IPv4 interfaces that were configured by -# the kernel (e.g. those on diskless machines) and not configured by -# DHCP, reset the netmask using the local "/etc/netmasks" file if one -# exists, and then reset the broadcast address based on the netmask. -# -/sbin/ifconfig -auD4 netmask + broadcast + +# Waits for up to 10 seconds for the link state to change to the given value +function wait_for_admin_nic_state +{ + wait_for_nic_state "${SYSINFO_NIC_admin}" "$1" +} -is_iptun () +# Plumbs the admin interface, and attempts to work around poorly-behaved +# drivers that can't handle plumb commands too quickly after one another +function plumb_admin { - intf=$1 - # Is this a persistent IP tunnel link? - /sbin/dladm show-iptun -P $intf > /dev/null 2>&1 - if [ $? -eq 0 ]; then - return 0 - fi - # Is this an implicit IP tunnel (i.e., ip.tun0) - ORIGIFS="$IFS" - IFS="$IFS." - set -- $intf - IFS="$ORIGIFS" - if [ $# -eq 2 -a \( "$1" = "ip" -o "$1" = "ip6" \) ]; then - # - # It looks like one, but another type of link might be - # using a name that looks like an implicit IP tunnel. - # If dladm show-link -P finds it, then it's not an IP - # tunnel. - # - /sbin/dladm show-link -Pp $intf > /dev/null 2>&1 - if [ $? -eq 0 ]; then - return 1 - else - return 0 - fi - fi - return 1 + set -o xtrace + driver=${SYSINFO_NIC_admin%%[0-9]*} + get_link_state ${SYSINFO_NIC_admin} + if [[ "$link_state" == "down" ]]; then + echo "admin nic '${SYSINFO_NIC_admin}' is down: unplumbing" + /sbin/ifconfig ${SYSINFO_NIC_admin} down unplumb + wait_for_admin_nic_state "unknown" + fi + + # There's some sort of race condition in the bnx driver: if the plumb + # command comes too soon after the unplumb, the interface can come up + # in a state where it never fires interrupts for link state changes. + if [[ "$driver" == "bnx" ]]; then + sleep 5 + fi + /sbin/ifconfig ${SYSINFO_NIC_admin} plumb mtu ${CONFIG_admin_mtu:-1500} + wait_for_admin_nic_state "up" } -bringup_ipif() +# Creates, plumbs and brings up a vnic with the specified inet parameters +function vnic_up { - for showif_output in `\ - /sbin/ipadm show-if -p -o ifname,state,current`; do - intf=`echo $showif_output | /usr/bin/cut -f1 -d:` - state=`echo $showif_output | /usr/bin/cut -f2 -d:` - current=`echo $showif_output | /usr/bin/cut -f3 -d:` - if [[ "$state" != "disabled" && $current != *Z* ]]; then - # - # skip if not a persistent interface, or if it should - # get IP configuration from the global zone ('Z' flag - # is set) - # - continue; - elif is_iptun $intf; then - # skip IP tunnel interfaces plumbed by net-iptun - continue; - elif [ -f /etc/hostname.$intf ] || \ - [ -f /etc/hostname6.$intf ]; then - if [[ $current != *Z* ]]; then - echo "found /etc/hostname.$intf "\ - "or /etc/hostname6.$intf, "\ - "ignoring ipadm configuration" > /dev/msglog - continue; - else - echo "Ignoring /etc/hostname*.$intf" \ - > /dev/msglog - { - /sbin/ifconfig $intf unplumb - /sbin/ifconfig $intf inet6 unplumb - } > /dev/null 2>&1 - fi - fi - - # Enable the interface managed by ipadm - /sbin/ipadm enable-if -t $intf - done + set -o xtrace + + typeset link="$1" + typeset iface="$2" + typeset ip="$3" + typeset netmask="$4" + typeset vlan_id="$5" + typeset mac_addr="$6" + typeset dhcp_primary="$7" + typeset mtu="$8" + typeset details= + typeset vlan_opt= + typeset mac_addr_opt= + typeset prop_opt= + + details="link='${link}', iface='${iface}', ip='${ip}'" + details="${details}, netmask='${netmask}, vlan_id='${vlan_id}'" + + if [[ -z ${link} ]] || [[ -z ${iface} ]] || \ + [[ -z ${ip} ]] || ([[ ${ip} != "dhcp" ]] && [[ -z ${netmask} ]]); then + echo "WARNING: not bringing up nic (insufficient configuration): " \ + "$details" + return + fi + + eval "vnic_already_up=\${vnic_${iface}_up}" + if [[ -n "${vnic_already_up}" ]]; then + echo "vnic already up: $details" + return + fi + + if [[ -n ${mac_addr} ]] && [[ -n ${ActiveAggrLinks[${mac_addr}]} ]]; then + echo "WARNING: trying to assign MAC address \"${mac_addr}\" to vnic," \ + " but it already belongs to link aggr " \ + "\"${ActiveAggrLinks[${mac_addr}]}\"" + return + fi + + echo "Bringing up nic: $details" + + if [[ -n ${vlan_id} ]] && [[ ${vlan_id} != 0 ]]; then + vlan_opt="-v ${vlan_id}" + fi + + if [[ -n ${mac_addr} ]]; then + mac_addr_opt="-m ${mac_addr}" + fi + + if [[ -n ${mtu} ]]; then + valid_mtu ${iface} ${mtu} + prop_opt="-p mtu=${mtu}" + fi + + /usr/sbin/dladm create-vnic -t -l ${link} ${prop_opt} ${vlan_opt} \ + ${mac_addr_opt} ${iface} + if [[ $? -ne 0 ]]; then + echo "Failed to create VNIC ${iface}" + exit $SMF_EXIT_ERR_FATAL + fi + + /sbin/ifconfig ${iface} plumb + if [[ $? -ne 0 ]]; then + echo "Failed to plumb ${iface}" + exit $SMF_EXIT_ERR_FATAL + fi + + if [[ ${ip} == "dhcp" ]]; then + # We ignore errors here because the most common one is that DHCP + # is already running. + + if [[ -n ${dhcp_primary} ]]; then + /sbin/ifconfig ${iface} dhcp primary || /bin/true + else + /sbin/ifconfig ${iface} dhcp || /bin/true + fi + else + /sbin/ifconfig ${iface} inet ${ip} netmask ${netmask} up + fi + eval "vnic_${iface}_up=true" } -# -# All the IPv4 and IPv6 interfaces are plumbed before doing any -# interface configuration. This prevents errors from plumb failures -# getting mixed in with the configured interface lists that the script -# outputs. -# +# Creates, plumbs and brings up a vnic with the specified inet6 parameters +function vnic_up6 +{ + set -o xtrace + + typeset link="$1" + typeset iface="$2" + typeset ip="$3" + typeset vlan_id="$4" + typeset mac_addr="$5" + typeset mtu="$6" + typeset details= + typeset vlan_opt= + typeset mac_addr_opt= + typeset prop_opt= + + details="link='${link}', iface='${iface}', ip6='${ip}'" + details="${details}, vlan_id='${vlan_id}'" + + if [[ -z ${link} ]] || [[ -z ${iface} ]] || [[ -z ${ip} ]]; then + echo "WARNING: not bringing up nic (insufficient configuration): " \ + "$details" + return + fi + + if [[ -n ${mac_addr} ]] && [[ -n ${ActiveAggrLinks[${mac_addr}]} ]]; then + echo "WARNING: trying to assign MAC address \"${mac_addr}\" to vnic," \ + " but it already belongs to link aggr " \ + "\"${ActiveAggrLinks[${mac_addr}]}\"" + return + fi + + # only bring up nic if not already up + eval "vnic_already_up=\${vnic_${iface}_up}" + if [[ -z "${vnic_already_up}" ]]; then + echo "Bringing up nic: $details" + + if [[ -n ${vlan_id} ]] && [[ ${vlan_id} != 0 ]]; then + vlan_opt="-v ${vlan_id}" + fi + + if [[ -n ${mac_addr} ]]; then + mac_addr_opt="-m ${mac_addr}" + fi + + if [[ -n ${mtu} ]]; then + valid_mtu ${iface} ${mtu} + prop_opt="-p mtu=${mtu}" + fi + + /usr/sbin/dladm create-vnic -t -l ${link} ${prop_opt} ${vlan_opt} \ + ${mac_addr_opt} ${iface} + if [[ $? -ne 0 ]]; then + echo "Failed to create VNIC ${iface}" + exit $SMF_EXIT_ERR_FATAL + fi + fi + + /sbin/ifconfig ${iface} inet6 plumb + if [[ $? -ne 0 ]]; then + echo "Failed to plumb ${iface}" + exit $SMF_EXIT_ERR_FATAL + fi + + if [[ -n ${ip} ]]; then + /sbin/ifconfig ${iface} inet6 up + fi + if [[ ${ip} != "addrconf" ]]; then + /sbin/ifconfig ${iface} inet6 \ + addif ${ip} preferred up + fi + eval "vnic_${iface}_up=true" +} -# -# First deal with /etc/hostname -# -# Get the list of IPv4 interfaces to configure by breaking -# /etc/hostname.* into separate args by using "." as a shell separator -# character. -# -interface_names="`echo /etc/hostname.*[0-9] 2>/dev/null`" -if [ "$interface_names" != "/etc/hostname.*[0-9]" ]; then - ORIGIFS="$IFS" - IFS="$IFS." - set -- $interface_names - IFS="$ORIGIFS" - while [ $# -ge 2 ]; do - shift - intf_name=$1 - while [ $# -gt 1 -a "$2" != "/etc/hostname" ]; do - intf_name="$intf_name.$2" - shift - done - shift - - # skip IP tunnel interfaces plumbed by net-iptun. - if is_iptun $intf_name; then - continue - fi - - read one rest < /etc/hostname.$intf_name - if [ "$one" = ipmp ]; then - ipmp_list="$ipmp_list $intf_name" - else - inet_list="$inet_list $intf_name" - fi - done -fi +# If there are aggregations in sysinfo, set them up. +function create_aggrs +{ + set -o xtrace + typeset links macs mode mtu + if [[ -z "${SYSINFO_Aggregations}" ]]; then + return 0 + fi + + aggrs=(${SYSINFO_Aggregations//,/ }) + for aggr in "${aggrs[@]}"; do + eval "links=\${SYSINFO_Aggregation_${aggr}_Interfaces}" + eval "macs=\${SYSINFO_Aggregation_${aggr}_MACs}" + eval "mode=\${SYSINFO_Aggregation_${aggr}_LACP_mode}" + eval "mtu=\${CONFIG_${aggr}_mtu}" + [[ -z "$mode" ]] && mode="off" + + echo "Creating aggr: ${aggr} (mode=${mode}, links=${links})" + dladm create-aggr -l ${links//,/ -l } -L ${mode} ${aggr} + if [[ $? -eq 0 ]]; then + add_active_aggr_links ${aggr} ${macs} + fi + + if [[ -n "$mtu" ]]; then + dladm set-linkprop -p mtu=${mtu} ${aggr} + if [[ $? -ne 0 ]]; then + echo "Failed to set mtu on aggr ${aggr} to ${mtu}" + exit $SMF_EXIT_ERR_FATAL + fi + fi + done + + # Creating the aggregations may affect the nic tags in sysinfo, so update: + /usr/bin/sysinfo -u + load_sdc_sysinfo +} # -# Get the list of IPv6 interfaces to configure by breaking -# /etc/hostname6.* into separate args by using "." as a shell separator -# character. +# Try various config parameters to set the default route # -interface_names="`echo /etc/hostname6.*[0-9] 2>/dev/null`" -if [ "$interface_names" != "/etc/hostname6.*[0-9]" ]; then - ORIGIFS="$IFS" - IFS="$IFS." - set -- $interface_names - IFS="$ORIGIFS" - while [ $# -ge 2 ]; do - shift - intf_name=$1 - while [ $# -gt 1 -a "$2" != "/etc/hostname6" ]; do - intf_name="$intf_name.$2" - shift - done - shift - - # skip IP tunnel interfaces plumbed by net-iptun. - if is_iptun $intf_name; then - continue - fi - - read one rest < /etc/hostname6.$intf_name - if [ "$one" = ipmp ]; then - ipmp6_list="$ipmp6_list $intf_name" - else - inet6_list="$inet6_list $intf_name" - fi - done -fi +function set_default_route +{ + set -o xtrace + typeset default_gw -# -# Create all of the IPv4 IPMP interfaces. -# -if [ -n "$ipmp_list" ]; then - set -- $ipmp_list - while [ $# -gt 0 ]; do - if /sbin/ifconfig $1 ipmp; then - ipmp_created="$ipmp_created $1" - else - ipmp_failed="$ipmp_failed $1" - fi - shift - done - [ -n "$ipmp_failed" ] && warn_failed_ifs "create IPv4 IPMP" \ - "$ipmp_failed" -fi + if [[ -n "${CONFIG_headnode_default_gateway}" ]]; then + default_gw="${CONFIG_headnode_default_gateway}" -# -# Step through the IPv4 interface list and try to plumb every interface. -# Generate list of plumbed and failed IPv4 interfaces. -# -if [ -n "$inet_list" ]; then - set -- $inet_list - while [ $# -gt 0 ]; do - /sbin/ifconfig $1 plumb - if /sbin/ifconfig $1 inet >/dev/null 2>&1; then - inet_plumbed="$inet_plumbed $1" - else - inet_failed="$inet_failed $1" - fi - shift - done - [ -n "$inet_failed" ] && warn_failed_ifs "plumb IPv4" "$inet_failed" -fi + elif [[ -n ${CONFIG_admin_gateway} ]]; then + default_gw="${CONFIG_admin_gateway}" -# Run autoconf to connect to a WLAN if the interface is a wireless one -if [ -x /sbin/wificonfig -a -n "$inet_plumbed" ]; then - set -- $inet_plumbed - while [ $# -gt 0 ]; do - if [ -r /dev/wifi/$1 ]; then - /sbin/wificonfig -i $1 startconf >/dev/null - fi - shift - done -fi + elif [[ -n ${BOOT_admin_gateway} ]]; then + default_gw=${BOOT_admin_gateway} -# -# Step through the IPv6 interface list and plumb every interface. -# Generate list of plumbed and failed IPv6 interfaces. Each plumbed -# interface will be brought up later, after processing any contents of -# the /etc/hostname6.* file. -# -if [ -n "$inet6_list" ]; then - set -- $inet6_list - while [ $# -gt 0 ]; do - /sbin/ifconfig $1 inet6 plumb - if /sbin/ifconfig $1 inet6 >/dev/null 2>&1; then - inet6_plumbed="$inet6_plumbed $1" - else - inet6_failed="$inet6_failed $1" - fi - shift - done - [ -n "$inet6_failed" ] && warn_failed_ifs "plumb IPv6" "$inet6_failed" -fi + elif [[ -n ${CONFIG_external_gateway} ]]; then + default_gw=${CONFIG_external_gateway} + fi -# -# Create all of the IPv6 IPMP interfaces. -# -if [ -n "$ipmp6_list" ]; then - set -- $ipmp6_list - while [ $# -gt 0 ]; do - if /sbin/ifconfig $1 inet6 ipmp; then - ipmp6_created="$ipmp6_created $1" - else - ipmp6_failed="$ipmp6_failed $1" - fi - shift - done - [ -n "$ipmp6_failed" ] && warn_failed_ifs "create IPv6 IPMP" \ - "$ipmp6_failed" -fi + if [[ -n ${default_gw} ]]; then + echo "${default_gw}" > /etc/defaultrouter + fi -# -# Upgrade ipadm.conf. -# -if /usr/bin/grep -q _family /etc/ipadm/ipadm.conf; then - oldifs=$(/usr/bin/sed -En \ - 's/^_ifname=([a-z0-9_]+);_family=[0-9]+;$/\1/p' \ - /etc/ipadm/ipadm.conf | /usr/bin/sort -u) - /usr/bin/sed -i '/_family/d' /etc/ipadm/ipadm.conf - for oldif in $oldifs; do - /usr/bin/printf \ - "_ifname=%s;_ifclass=0;_families=2,26;\n" \ - $oldif >> /etc/ipadm/ipadm.conf - done -fi + if [[ -n ${CONFIG_admin_gateway6} ]]; then + default_gw6="${CONFIG_admin_gateway6}" -# -# Finally configure interfaces set up with ipadm. Any /etc/hostname*.intf -# files take precedence over ipadm defined configurations except when -# we are in a non-global zone and Layer-3 protection of IP addresses is -# enforced on the interface by the global zone. -# -bringup_ipif + elif [[ -n ${BOOT_admin_gateway6} ]]; then + default_gw6=${BOOT_admin_gateway6} -# -# Process the /etc/hostname[6].* files for IPMP interfaces. Processing these -# before non-IPMP interfaces avoids accidental implicit IPMP group creation. -# -[ -n "$ipmp_created" ] && if_configure inet "IPMP" $ipmp_created -[ -n "$ipmp6_created" ] && if_configure inet6 "IPMP" $ipmp6_created + elif [[ -n ${CONFIG_external_gateway6} ]]; then + default_gw6=${CONFIG_external_gateway6} + fi + + if [[ -n ${default_gw6} ]]; then + # add static route + /usr/sbin/route add -inet6 default ${default_gw6} + fi +} # -# Process the /etc/hostname[6].* files for non-IPMP interfaces. +# Go through and set up the MTU for all of the various nic tags # -[ -n "$inet_plumbed" ] && if_configure inet "" $inet_plumbed -[ -n "$inet6_plumbed" ] && if_configure inet6 "" $inet6_plumbed +function setup_mtu +{ + set -o xtrace + typeset tag oldifs val mac link curmtu + typeset -A mtus + typeset -A tagmap + + set -o xtrace + oldifs=$IFS + IFS=, + for tag in ${SYSINFO_Nic_Tags}; do + eval "val=\${CONFIG_${tag}_mtu}" + eval "mac=\${CONFIG_${tag}_nic}" + [[ -z "$val" ]] && continue + + valid_mtu ${tag} $val + + # + # Note, it doesn't matter what tag we use for a given mac + # address, because we'll always get the same link name later on. + # + if [[ -z "${tagmap[$mac]}" ]]; then + tagmap[$mac]=$tag + fi + + if [[ -z "${mtus[$mac]}" ]]; then + mtus[$mac]=$val + elif [[ "${mtus[$mac]}" -lt $val ]]; then + mtus[$mac]=$val + fi + done + IFS=$oldifs + + for mac in ${!mtus[@]}; do + tag=${tagmap[$mac]} + eval "link=\${SYSINFO_NIC_${tag}}" + if [[ -z "${link}" ]]; then + echo "/usbkey/config error: Missing link name for ${tag}" + exit $SMF_EXIT_ERR_FATAL + fi + + # + # Check the current MTU of the device. To help out devices which + # don't support the setting of the MTU (here's looking at you + # bnx), if the MTU is identical to its default, don't do + # anything and save the poor folks stuck with bnx some grief. + # + curmtu=$(/usr/sbin/dladm show-linkprop -c -o value -p mtu ${link}) + [[ $? -eq 0 ]] && [[ "$curmtu" -eq "${mtus[$mac]}" ]] && continue + + if ! /usr/sbin/dladm set-linkprop -p mtu=${mtus[$mac]} ${link}; then + echo "Failed to set mtu to ${mtus[$mac]} for link ${link}" + exit $SMF_EXIT_ERR_FATAL + fi + done +} # -# For the IPv4 and IPv6 interfaces that failed to plumb, find (or create) -# IPMP meta-interfaces to host their data addresses. +# Set up resolvers, if we can # -[ -n "$inet_failed" ] && move_addresses inet -[ -n "$inet6_failed" ] && move_addresses inet6 - -# Run DHCP if requested. Skip boot-configured interface. -interface_names="`echo /etc/dhcp.*[0-9] 2>/dev/null`" -if [ "$interface_names" != '/etc/dhcp.*[0-9]' ]; then - # - # First find the primary interface. Default to the first - # interface if not specified. First primary interface found - # "wins". Use care not to "reconfigure" a net-booted interface - # configured using DHCP. Run through the list of interfaces - # again, this time trying DHCP. - # - i4d_fail= - firstif= - primary= - ORIGIFS="$IFS" - IFS="${IFS}." - set -- $interface_names - - while [ $# -ge 2 ]; do - shift - [ -z "$firstif" ] && firstif=$1 - - for i in `shcat /etc/dhcp\.$1`; do - if [ "$i" = primary ]; then - primary=$1 - break - fi - done - - [ -n "$primary" ] && break - shift - done - - [ -z "$primary" ] && primary="$firstif" - cmdline=`shcat /etc/dhcp\.${primary}` - - if [ "$_INIT_NET_IF" != "$primary" ]; then - echo "starting DHCP on primary interface $primary" - /sbin/ifconfig $primary auto-dhcp primary $cmdline - # Exit code 4 means ifconfig timed out waiting for dhcpagent - [ $? != 0 ] && [ $? != 4 ] && i4d_fail="$i4d_fail $primary" - fi - - set -- $interface_names - - while [ $# -ge 2 ]; do - shift - cmdline=`shcat /etc/dhcp\.$1` - if [ "$1" != "$primary" -a \ - "$1" != "$_INIT_NET_IF" ]; then - echo "starting DHCP on interface $1" - /sbin/ifconfig $1 dhcp start wait 0 $cmdline - # Exit code can't be timeout when wait is 0 - [ $? != 0 ] && i4d_fail="$i4d_fail $1" - fi - shift - done - IFS="$ORIGIFS" - unset ORIGIFS - [ -n "$i4d_fail" ] && warn_failed_ifs "configure IPv4 DHCP" "$i4d_fail" -fi +function configure_resolv_conf +{ + if [[ ${RESOLV_CONF_DONE} == true ]]; then + return + elif [[ -n ${CONFIG_dns_domain} ]] && [[ -n ${CONFIG_dns_resolvers} ]]; then + echo "search ${CONFIG_dns_domain}" > /etc/resolv.conf + if [[ -n ${CONFIG_binder_admin_ips} ]]; then + for serv in $(echo "${CONFIG_binder_admin_ips}" | sed -e "s/,/ /g"); do + echo "nameserver ${serv}" >> /etc/resolv.conf + done + fi + for serv in $(echo "${CONFIG_dns_resolvers}" | sed -e "s/,/ /g"); do + echo "nameserver ${serv}" >> /etc/resolv.conf + done + RESOLV_CONF_DONE=true + fi +} -# There is a chicken-and-egg problem with bringing up overlay VNICs at boot -# time. When the first VNIC is added to an overlay, it creates a kernel socket -# to listen for incoming encapsulated frames. Therefore, VNICs cannot be added -# until after IP interfaces have been brought up. Overlay VNICs may themselves -# have IP interfaces over them and so it is necessary to attempt to bring up -# any remaining IP interfaces once the overlay VNICs are in place. -if smf_is_globalzone && dladm show-link -p -o class | egrep -s 'overlay'; then - echo "Bringing up any remaining VNICs on overlays" - /sbin/dladm up-vnic - echo "Bringing up any remaining IP interfaces on overlay VNICs" - bringup_ipif -fi +# Helper function for plumbing an interface for an address family once +typeset -A plumbedifs +function plumbif +{ + iface=$1 + inet=$2 + addrtype=$3 + if [[ -z ${plumbedifs[${iface},${inet}]} ]]; then + plumbedifs[${iface},${inet}]="true" + /sbin/ifconfig ${iface} ${inet} plumb + if [[ ! ${addrtype} =~ "^vrrp" ]]; then + /sbin/ifconfig ${iface} ${inet} up + fi + fi +} -# In order to avoid bringing up the interfaces that have -# intentionally been left down, perform RARP only if the system -# has no configured hostname in /etc/nodename -hostname="`shcat /etc/nodename 2>/dev/null`" -if [ "$_INIT_NET_STRATEGY" = "rarp" -o -z "$hostname" ]; then - /sbin/ifconfig -adD4 auto-revarp netmask + broadcast + up +if smf_is_globalzone; then + EARLY_ADMIN= + [[ -f /etc/svc/volatile/.early_admin_setup ]] && EARLY_ADMIN=1 + [[ -n "$EARLY_ADMIN" ]] || /usr/sbin/dladm init-phys + + # The next command is for logging purposes only + log_if_state before + + # Load sysinfo variables with SYSINFO_ prefix: we primarily care about + # the NIC_variables, which contain the actual interface name for a nic + # tag (it has mapped the foo_nic=<MAC address> variables to interface + # names for us). + load_sdc_sysinfo + + if boot_file_config_enabled; then + # We have a boot-time networking file present - use its values rather + # than ones from the config file or bootparams + if ! boot_file_config_valid; then + echo "ERROR: boot-time network config file incorrect" + exit ${SMF_EXIT_ERR_CONFIG} + fi + + load_boot_file_config + + # NOTE: some of the routes boot_file_config_init tries to add may + # fail if they are admin network routes added by the + # network/early-admin service. This is expected and not a problem. + boot_file_config_init + else + # Load config variables with CONFIG_ prefix, + # and sets the headnode variable + load_sdc_config + # Load boot params with BOOT_ prefix + load_sdc_bootparams + fi + + # Set up etherstubs + for stub in $(echo "${CONFIG_etherstub}" | sed -e "s/,/ /g"); do + /usr/sbin/dladm create-etherstub -t $stub || echo "ERROR: could not create etherstub ${stub}." + done + + # Create aggregations + create_aggrs + + # Make any mtu adjustments that may be necessary + setup_mtu + + # Setup admin NIC + ADMIN_NIC_TAG=${CONFIG_admin_tag:-"admin"} + + # If there is no NIC with the admin tag, and the config has + # admin_nic_autoselect=true, designate the first NIC reported + # by dladm for admin use. This is useful in environments where + # the NICs are known to change beneath us. + if [[ "${BOOT_smartos}" == "true" ]] && \ + [[ "${CONFIG_admin_nic_autoselect}" == "true" ]] && \ + ! nictagadm exists $ADMIN_NIC_TAG ; then + autoselected_admin_nic=$(dladm show-phys -m -p -o address | head -n1) + if [[ -z ${autoselected_admin_nic} ]] ; then + echo "ERROR: no NICs found, unable to autoselect admin NIC." + exit ${SMF_EXIT_ERR_CONFIG} + fi + + nictagadm add $ADMIN_NIC_TAG "${autoselected_admin_nic}" + if [[ $? -ne 0 ]] ; then + echo "ERROR: unable to add admin tag to NIC ${autoselected_admin_nic}" + exit ${SMF_EXIT_ERR_FATAL} + fi + + SYSINFO_NIC_admin=$(dladm show-phys -m -p -o link | head -n1) + if [[ -n ${SYSINFO_NIC_admin} ]] ; then + echo "Autoselected ${SYSINFO_NIC_admin} for use as admin NIC." + fi + + nictagadm list + elif [[ -v CONFIG_admin_tag ]]; then + # + # This handles the case when the 'admin_tag' property is set to + # override the default admin nic tag. + # + eval SYSINFO_NIC_admin='$'SYSINFO_NIC_${CONFIG_admin_tag} + + eval CONFIG_admin_ip='$'CONFIG_${CONFIG_admin_tag}_ip + eval CONFIG_admin_ip6='$'CONFIG_${CONFIG_admin_tag}_ip6 + eval CONFIG_admin_netmask='$'CONFIG_${CONFIG_admin_tag}_netmask + eval CONFIG_admin_mtu='$'CONFIG_${CONFIG_admin_tag}_mtu + eval CONFIG_admin_gateway='$'CONFIG_${CONFIG_admin_tag}_gateway + eval CONFIG_admin_gateway6='$'CONFIG_${CONFIG_admin_tag}_gateway6 + fi + + if [[ -z "${SYSINFO_NIC_admin}" ]]; then + echo "ERROR: admin NIC not found, unable to bring up admin network." + exit ${SMF_EXIT_ERR_CONFIG} + fi + + [[ -n "$EARLY_ADMIN" ]] || plumb_admin + + # If we performed early configuration of the admin network and + # the admin interface is on a link aggregation, the aggregation + # has already been configured. Add it to the list of active aggrs + # so the checks in vnic_up[6] are aware of it. + if [[ -n "$EARLY_ADMIN" && "${SYSINFO_NIC_admin}" =~ aggr[0-9]+$ ]]; then + eval "macs=\${SYSINFO_Aggregation_${SYSINFO_NIC_admin}_MACs}" + + add_active_aggr_links ${SYSINFO_NIC_admin} $macs + fi + + # Prefer the config file for admin nic values, but use + # bootparams if present + admin_ip=${CONFIG_admin_ip} + if [[ -z "$admin_ip" ]]; then + admin_ip=${BOOT_admin_ip} + fi + + admin_netmask=${CONFIG_admin_netmask} + if [[ -z "$admin_netmask" ]]; then + admin_netmask=${BOOT_admin_netmask} + fi + + admin_ip6=${CONFIG_admin_ip6} + if [[ -z "$admin_ip6" ]]; then + admin_ip6=${BOOT_admin_ip6} + fi + + if [[ $admin_ip == 'none' ]]; then + echo 'INFO: not configuring IP on admin interface (admin_ip=none)' + elif [[ -n "$EARLY_ADMIN" ]]; then + echo 'INFO: admin interface already configured (early setup)' + ADMIN_NIC_UP=true + elif [[ -n $admin_ip ]] && [[ -n $admin_netmask ]]; then + /sbin/ifconfig ${SYSINFO_NIC_admin} inet ${admin_ip} \ + netmask ${admin_netmask} up + ADMIN_NIC_UP=true + + # also setup resolv.conf if we can + configure_resolv_conf + else + if [[ ${headnode} == "true" ]]; then + echo "ERROR: headnode but no admin_{ip,netmask} in config, not bringing up admin network." + # Set a flag, but try to plumb the other interfaces anyway + ADMIN_NIC_MISCONFIGURED=true + else + # We ignore errors here because the most common one is that DHCP is + # already running. + /sbin/ifconfig ${SYSINFO_NIC_admin} dhcp || /bin/true + + # Wait for DHCP + timeout=${ADMIN_DHCP_TIMEOUT} + dhcp_admin_ip=$(/sbin/ifconfig ${SYSINFO_NIC_admin} | grep inet | awk '{ print $2 }') + while [[ (-z ${dhcp_admin_ip} || ${dhcp_admin_ip} == "0.0.0.0") && ${timeout} -gt 0 ]]; do + dhcp_admin_ip=$(/sbin/ifconfig ${SYSINFO_NIC_admin} | grep inet | awk '{ print $2 }') + timeout=$((${timeout} - 1)) + sleep 1 + done + + ADMIN_NIC_UP=true + fi + fi + + if [[ -n ${admin_ip6} && -z "$EARLY_ADMIN" ]]; then + # Plumb interface for inet6 + ifconfig ${SYSINFO_NIC_admin} inet6 \ + plumb mtu ${CONFIG_admin_mtu:-1500} up + + # Autodiscovery IPv6 using SLAAC + # NOTE: in.ndpd will be started later, due to plumbing the interface. + # this means autodiscovery also happens when configuring a + # static address. + # + # in.ndpd also sets a default route and this can't be disabled. + + # Configure static IPv6 + if [[ ${admin_ip6} != "addrconf" ]]; then + /sbin/ifconfig ${SYSINFO_NIC_admin} inet6 \ + addif ${admin_ip6} preferred up + fi + + ADMIN_NIC_UP=true + + # don't setup resolv.conf, IPv6 addresses already work + fi + + # If on Parallels or VirtualBox, create a bridge which + # allows traffic to flow correctly to the host-only network + if [[ "${ADMIN_NIC_UP}" == "true" ]] \ + && [[ ${SYSINFO_Product} == "Parallels Virtual Platform" \ + || ${SYSINFO_Product} == "VirtualBox" ]] \ + && [[ -z $(/usr/sbin/dladm show-bridge -p vmwarebr) ]]; then + /usr/sbin/dladm create-bridge -l ${SYSINFO_NIC_admin} vmwarebr + fi + + # Setup the external NIC. The installer may have already set up external0, + # so, if it exists, we're not going to try and set up the vnic again. + if [[ -n ${SYSINFO_NIC_external} ]] \ + && ! dladm show-vnic external0 > /dev/null; then + + if [[ -n "${CONFIG_external_ip}" ]]; then + vnic_up "${SYSINFO_NIC_external}" "external0" \ + "${CONFIG_external_ip}" "${CONFIG_external_netmask}" \ + "${CONFIG_external_vlan_id}" "${CONFIG_external_mac}" \ + "primary" "${CONFIG_external_mtu}" + fi + + if [[ -n "${CONFIG_external_ip6}" ]]; then + vnic_up6 "${SYSINFO_NIC_external}" "external0" \ + "${CONFIG_external_ip6}" \ + "${CONFIG_external_vlan_id}" "${CONFIG_external_mac}" \ + "${CONFIG_external_mtu}" + fi + fi + + set_default_route + + # Setup extra nics, if specified in the config file + nic_tags="${SYSINFO_Nic_Tags}" + if [[ -n "${nic_tags}" ]]; then + tags=(${nic_tags//,/ }) + + if boot_file_config_enabled; then + bootparam_ip_keys="" + bootparam_ip6_keys="" + config_ip_keys=${CONFIG_bootfile_ip_keys//,/ } + config_ip6_keys=${CONFIG_bootfile_ip6_keys//,/ } + else + bootparam_ip_keys=$(sdc_bootparams_keys | grep -- "-ip$" || true) + bootparam_ip6_keys=$(sdc_bootparams_keys | grep -- "-ip6$" || true) + config_ip_keys=$(sdc_config_keys | grep "_ip$" || true) + config_ip6_keys=$(sdc_config_keys | grep "_ip6$" || true) + fi + + for tag in "${tags[@]}"; do + + eval "link=\${SYSINFO_NIC_${tag}}" + if [[ -z "${link}" ]]; then + echo "WARNING: No link found with tag '${tag}'" + continue + fi + + for key in ${config_ip_keys}; do + if [[ ${key} == ${tag}[0-9]_ip ]] || [[ ${key} == ${tag}[0-9][0-9]_ip ]]; then + iface=${key//_ip/} + #echo " iface=$iface" + eval "ip=\${CONFIG_${iface}_ip}" + eval "netmask=\${CONFIG_${iface}_netmask}" + eval "vlan=\${CONFIG_${iface}_vlan_id}" + eval "macaddr=\${CONFIG_${iface}_mac}" + eval "mtu=\${CONFIG_${iface}_mtu}" + + echo vnic_up "${link}" "${iface}" "${ip}" "${netmask}" "${vlan}" "${macaddr}" "${mtu}" + vnic_up "${link}" "${iface}" "${ip}" "${netmask}" \ + "${vlan}" "${macaddr}" "" "${mtu}" + fi + done + + for key in ${bootparam_ip_keys}; do + if [[ ${key} == ${tag}[0-9]-ip ]] || [[ ${key} == ${tag}[0-9][0-9]-ip ]]; then + iface=${key//-ip/} + eval "ip=\${BOOT_${iface}_ip}" + eval "netmask=\${BOOT_${iface}_netmask}" + eval "vlan=\${BOOT_${iface}_vlan_id}" + eval "macaddr=\${BOOT_${iface}_mac}" + eval "mtu=\${CONFIG_${iface}_mtu}" + echo vnic_up "${link}" "${iface}" "${ip}" "${netmask}" "${vlan}" "${macaddr}" "${mtu}" + vnic_up "${link}" "${iface}" "${ip}" "${netmask}" \ + "${vlan}" "${macaddr}" "" "${mtu}" + fi + done + + for key in ${config_ip6_keys}; do + if [[ ${key} == ${tag}[0-9]_ip6 ]] || [[ ${key} == ${tag}[0-9][0-9]_ip6 ]]; then + iface=${key//_ip6/} + #echo " iface=$iface" + eval "ip=\${CONFIG_${iface}_ip6}" + eval "vlan=\${CONFIG_${iface}_vlan_id}" + eval "macaddr=\${CONFIG_${iface}_mac}" + eval "mtu=\${CONFIG_${iface}_mtu}" + + echo vnic_up6 "${link}" "${iface}" "${ip}" "${vlan}" "${macaddr}" "${mtu}" + vnic_up6 "${link}" "${iface}" "${ip}" \ + "${vlan}" "${macaddr}" "${mtu}" + fi + done + + for key in ${bootparam_ip6_keys}; do + if [[ ${key} == ${tag}[0-9]-ip6 ]] || [[ ${key} == ${tag}[0-9][0-9]-ip6 ]]; then + iface=${key//-ip6/} + eval "ip=\${BOOT_${iface}_ip6}" + eval "vlan=\${BOOT_${iface}_vlan_id}" + eval "macaddr=\${BOOT_${iface}_mac}" + eval "mtu=\${CONFIG_${iface}_mtu}" + echo vnic_up6 "${link}" "${iface}" "${ip}" "${vlan}" "${macaddr}" "${mtu}" + vnic_up6 "${link}" "${iface}" "${ip}" \ + "${vlan}" "${macaddr}" "${mtu}" + fi + done + + done + + # All vnics are done. If the config has admin_ip=none, then we won't + # have resolvers set up yet, so try again here. + configure_resolv_conf + fi +else + # Non-global zones + + # Bring up statically assigned interfaces, and find the primary DHCP + # interface, if it exists + while IFS=: read -r iface addrtype; do + # Keep track of whether or not we've configured our first IPv4 + # address on this interface + first_ipv4_configured="" + iface_configured="" + + if [[ -f /etc/dhcp.${iface} ]]; then + plumbif ${iface} inet ${addrtype} + if [[ -z "${primary}" ]]; then + /sbin/ifconfig ${iface} auto-dhcp primary + primary=${iface} + else + /sbin/ifconfig ${iface} auto-dhcp + fi + first_ipv4_configured="true" + iface_configured="true" + fi + + if [[ -f /etc/hostname.${iface} ]]; then + while read ifparams; do + # For IPv4, we need to set the address on the first logical + # interface. For IPv6, the address on the first interface is + # the link-local address, and can't be changed. + # + # Lines starting with inet indicate the hostname for that + # interface, and are used by dhcpagent. Skip over them. + if [[ -f /etc/dhcp.${iface} && "${ifparams}" == inet* ]]; then + continue + elif [[ "${ifparams}" == {3}({1,3}(\d).){1,3}(\d)* ]]; then + plumbif ${iface} inet ${addrtype} + if [[ -z "${first_ipv4_configured}" ]]; then + first_ipv4_configured="true" + ifcommand="inet" + else + ifcommand="inet addif" + fi + else + plumbif ${iface} inet6 ${addrtype} + ifcommand="inet6 addif" + fi + + # vrrp interfaces can't be brought up with ifconfig: vrrpadm + # handles that instead + if [[ "${addrtype}" =~ "^vrrp" ]]; then + /sbin/ifconfig ${iface} ${ifcommand} \ + `printf "%s" "${ifparams}" | sed -e 's/ up//'` + else + /sbin/ifconfig ${iface} ${ifcommand} ${ifparams} up + fi + + iface_configured="true" + done < /etc/hostname.${iface} + fi + + if [[ -f /etc/addrconf.${iface} ]]; then + # Enable the NDP daemon, so that once this script finishes, we'll + # be able to pick up router advertisments and finish configuring + # the network interface. We then just need to plumb the interface, + # and let in.ndpd take care of configuring addresses. + svcadm enable svc:/network/routing/ndp:default + plumbif ${iface} inet6 ${addrtype} + iface_configured="true" + fi + + # If we didn't configure the device at all, mark it for DHCP if we + # don't do DHCP on anyone else. + if [[ -z ${iface_configured} && -z "${first_iface}" ]]; then + first_iface=${iface} + fi + done < <(/usr/sbin/dladm show-vnic -p -o link,macaddrtype 2>/dev/null) + + if [[ -z "${primary}" && -n "${first_iface}" ]]; then + first_iface_type=$(dladm show-vnic ${first_iface} -p -o macaddrtype) + plumbif ${first_iface} inet ${first_iface_type} + /sbin/ifconfig ${first_iface} auto-dhcp + primary=${first_iface} + fi + + while IFS=: read -r iface addrtype; do + if [[ "${iface}" == "${primary}" + || -f /etc/hostname.${iface} + || -f /etc/dhcp.${iface} + || -f /etc/addrconf.${iface} + || "${addrtype}" =~ "^vrrp" ]]; then + continue + fi + + plumbif ${iface} inet ${addrtype} + /sbin/ifconfig ${iface} auto-dhcp start wait 0 + done < <(/usr/sbin/dladm show-vnic -p -o link,macaddrtype 2>/dev/null) fi -# -# If the /etc/defaultrouter file exists, process it now so that the next -# stage of booting will have access to NFS. -# -if [ -f /etc/defaultrouter ]; then - while read router rubbish; do - case "$router" in - '#'* | '') ;; # Ignore comments, empty lines - *) /sbin/route -n add default -gateway $router ;; - esac - done </etc/defaultrouter +log_if_state after + +# Since we hopefully made networking changes here, update the sysinfo cache +if smf_is_globalzone; then + /usr/bin/sysinfo -u fi -# -# If we get here and were not asked to plumb any IPv4 interfaces, look -# for boot properties that direct us. -# -# - The "network-interface" property is required and indicates the -# interface name. -# - The "xpv-hcp" property, if present, is used by the hypervisor -# tools to indicate how the specified interface should be configured. -# Permitted values are "dhcp" and "off", where "off" indicates static -# IP configuration. -# -# In the case where "xpv-hcp" is set to "dhcp", no further properties -# are required or examined. -# -# In the case where "xpv-hcp" is not present or set to "off", the -# "host-ip" and "subnet-mask" properties are used to configure -# the specified interface. The "router-ip" property, if present, -# is used to add a default route. -# -nic="`/sbin/devprop network-interface`" -if smf_is_globalzone && [ -z "$inet_list" ] && [ -n "$nic" ]; then - hcp="`/sbin/devprop xpv-hcp`" - case "$hcp" in - "dhcp") - /sbin/ifconfig $nic plumb 2>/dev/null - [ -n "`/sbin/ifconfig $nic 2>/dev/null`" ] && ( - # The interface is successfully plumbed, so - # modify "inet_list" to force the exit code - # checks to work. - inet_list=$nic; - # Given that this is the only IPv4 interface, - # we assert that it is primary. - echo "starting DHCP on primary interface $primary"; - /sbin/ifconfig $nic auto-dhcp primary; - # Exit code 4 means ifconfig timed out waiting - # for dhcpagent - [ $? != 0 ] && [ $? != 4 ] && \ - i4d_fail="$i4d_fail $nic"; - ) - ;; - - "off"|"") - /sbin/devprop host-ip subnet-mask router-ip | ( - read ip; - read mask; - read router; - [ -n "$ip" ] && [ -n "$mask" ] && \ - /sbin/ifconfig $nic plumb 2>/dev/null - [ -n "`/sbin/ifconfig $nic 2>/dev/null`" ] && ( - # The interface is successfully - # plumbed, so modify "inet_list" to - # force the exit code checks to work. - inet_list=$nic; - /sbin/ifconfig $nic inet $ip \ - netmask $mask broadcast + up 2>/dev/null; - [ -n "$router" ] && route add \ - default $router 2>/dev/null; - ) - ) - ;; - esac +# Enable symmetric routing: when there are multiple nics configured, always +# take into account the interface a packet is being sent over when +# selecting a route. This prevents packets being sent with another nic's +# source IP. +/usr/sbin/ndd -set /dev/ip ip_strict_src_multihoming 1 + +# If the admin nic was missing config options, exit with a config error +if [[ -n "${ADMIN_NIC_MISCONFIGURED}" ]]; then + exit ${SMF_EXIT_ERR_CONFIG} fi -# -# We tell smf this service is online if any of the following is true: -# - no interfaces were configured for plumbing and no DHCP failures -# - any non-loopback IPv4 interfaces are up and have a non-zero address -# - there are any DHCP interfaces started -# - any non-loopback IPv6 interfaces are up -# -# If we weren't asked to configure any interfaces, exit -if [ -z "$inet_list" ] && [ -z "$inet6_list" ]; then - # Config error if DHCP was attempted without plumbed interfaces - [ -n "$i4d_fail" ] && exit $SMF_EXIT_ERR_CONFIG - exit $SMF_EXIT_OK +if [[ $admin_ip == 'none' ]]; then + # + # We're done, even if there are not any usable IP addresses. + # + exit $SMF_EXIT_OK fi # Any non-loopback IPv4 interfaces with usable addresses up? -if [ -n "`/sbin/ifconfig -a4u`" ]; then - /sbin/ifconfig -a4u | while read intf addr rest; do - [ $intf = inet ] && [ $addr != 127.0.0.1 ] && - [ $addr != 0.0.0.0 ] && exit $SMF_EXIT_OK - done && exit $SMF_EXIT_OK +if [[ -n "`/sbin/ifconfig -a4u`" ]]; then + /sbin/ifconfig -a4u | while read intf addr rest; do + [[ ${intf} == "inet" ]] && [[ ${addr} != "127.0.0.1" ]] && + [[ ${addr} != "0.0.0.0" ]] && exit ${SMF_EXIT_OK} + done && exit ${SMF_EXIT_OK} fi # Any DHCP interfaces started? -[ -n "`/sbin/ifconfig -a4 dhcp status 2>/dev/null`" ] && exit $SMF_EXIT_OK +[[ -n "`/sbin/ifconfig -a4 dhcp status 2>/dev/null`" ]] && exit ${SMF_EXIT_OK} # Any non-loopback IPv6 interfaces up? -if [ -n "`/sbin/ifconfig -au6`" ]; then - /sbin/ifconfig -au6 | while read intf addr rest; do - [ $intf = inet6 ] && [ $addr != ::1/128 ] && exit $SMF_EXIT_OK - done && exit $SMF_EXIT_OK +if [[ -n "`/sbin/ifconfig -au6`" ]]; then + /sbin/ifconfig -au6 | while read intf addr rest; do + [[ ${intf} = "inet6" ]] && [[ ${addr} != "::1/128" ]] && exit ${SMF_EXIT_OK} + done && exit ${SMF_EXIT_OK} fi # This service was supposed to configure something yet didn't. Exit # with config error. -exit $SMF_EXIT_ERR_CONFIG +exit ${SMF_EXIT_ERR_CONFIG} diff --git a/usr/src/cmd/svc/milestone/net-routing-setup b/usr/src/cmd/svc/milestone/net-routing-setup index 5b65f90d91..3ac6a3f7aa 100644 --- a/usr/src/cmd/svc/milestone/net-routing-setup +++ b/usr/src/cmd/svc/milestone/net-routing-setup @@ -21,12 +21,16 @@ # # # Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. +# +# Copyright (c) 2012 Joyent, Inc. All rights reserved. # Copyright (c) 2021 H. William Welliver # This script configures IP routing. . /lib/svc/share/smf_include.sh +set -o xtrace + # # In a shared-IP zone we need this service to be up, but all of the work # it tries to do is irrelevant (and will actually lead to the service @@ -82,6 +86,17 @@ fi smf_netstrategy # +# Read /etc/inet/static_routes.vmadm and add each link-local route. +# +if [ -f /etc/inet/static_routes.vmadm ]; then + echo "Adding vmadm persistent link-local routes:" + /usr/bin/egrep -v "^(#|$)" /etc/inet/static_routes.vmadm | + /usr/bin/grep -- "-interface " | while read line; do + /usr/sbin/route add $line + done +fi + +# # See if static routes were created by install. If so, they were created # under /etc/svc/volatile. Copy them into their proper place. # @@ -185,7 +200,8 @@ fi # however, as persistent daemon state is now controlled by SMF. # ipv4_routing_set=`/usr/bin/svcprop -p routeadm/ipv4-routing-set $SMF_FMRI` -if [ -z "$defrouters" ]; then +smartos_param=`/usr/bin/bootparams | grep "^smartos"` +if [ -z "$defrouters" ] && [ "$smartos_param" != "" ]; then # # Set default value for ipv4-routing to enabled. If routeadm -e/-d # has not yet been run by the administrator, we apply this default. @@ -223,5 +239,22 @@ if [ -f /etc/inet/static_routes ]; then done fi +# +# Read /etc/inet/static_routes.vmadm and add each non-link-local route. +# +if [ -f /etc/inet/static_routes.vmadm ]; then + echo "Adding vmadm persistent routes:" + /usr/bin/egrep -v "^(#|$)" /etc/inet/static_routes.vmadm | + /usr/bin/grep -v -- "-interface " | while read line; do + /usr/sbin/route add $line + done +fi + +# +# Log the result +# +echo "Routing setup complete:" +/usr/bin/netstat -rn + # Clear exit status. exit $SMF_EXIT_OK diff --git a/usr/src/cmd/svc/milestone/network-early-admin.xml b/usr/src/cmd/svc/milestone/network-early-admin.xml new file mode 100644 index 0000000000..6566f31bff --- /dev/null +++ b/usr/src/cmd/svc/milestone/network-early-admin.xml @@ -0,0 +1,73 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + Copyright 2019, Joyent, Inc. + + 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. + +--> + +<service_bundle type='manifest' name='network-early-admin'> + +<service + name='network/early-admin' + type='service' + version='1'> + + <!-- ifconfig needs loopback for IPC with dhcpagent --> + <dependency + name='network' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/network/loopback' /> + <service_fmri value='svc:/network/datalink-management' /> + <service_fmri value='svc:/network/ip-interface-management' /> + </dependency> + + <instance name='default' enabled='true'> + + <exec_method + type='method' + name='start' + exec='/lib/svc/method/net-early-admin' + timeout_seconds='600' /> + + <exec_method + type='method' + name='stop' + exec=':true' + timeout_seconds='3' /> + + <property_group name='startd' type='framework'> + <propval name='duration' type='astring' value='transient' /> + </property_group> + + <template> + <common_name> + <loctext xml:lang='C'> + Triton admin network on compute nodes + </loctext> + </common_name> + <documentation> + <manpage title='ifconfig' section='1M' + manpath='/usr/share/man' /> + <manpage title='dladm' section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> + + </instance> + + <stability value='Unstable' /> + +</service> + +</service_bundle> diff --git a/usr/src/cmd/svc/milestone/network-location.xml b/usr/src/cmd/svc/milestone/network-location.xml index a53dfce90a..f669d38d4c 100644 --- a/usr/src/cmd/svc/milestone/network-location.xml +++ b/usr/src/cmd/svc/milestone/network-location.xml @@ -81,14 +81,6 @@ </dependency> <dependency - name='location_netcfg' - grouping='require_all' - restart_on='none' - type='service'> - <service_fmri value='svc:/network/netcfg:default' /> - </dependency> - - <dependency name='filesystem' grouping='require_all' restart_on='none' diff --git a/usr/src/cmd/svc/milestone/network-physical.xml b/usr/src/cmd/svc/milestone/network-physical.xml index 288eced560..d0eb7db1b9 100644 --- a/usr/src/cmd/svc/milestone/network-physical.xml +++ b/usr/src/cmd/svc/milestone/network-physical.xml @@ -47,7 +47,13 @@ <service_fmri value='svc:/network/loopback' /> </dependency> - <instance name='default' enabled='true'> + <dependency + name='joyent' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/filesystem/smartdc' /> + </dependency> <!-- physical:default and physical:nwam are mutually exclusive. @@ -55,13 +61,25 @@ does not work. --> <dependency - name='physical_nwam' - grouping='exclude_all' + name='mdata-fetch' + grouping='optional_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/smartdc/mdata:fetch' /> + </dependency> + + <!-- Prevent this and network/early-admin from trying to configure the + admin interface at the same time --> + <dependency + name='early-admin' + grouping='optional_all' restart_on='none' type='service'> - <service_fmri value='svc:/network/physical:nwam' /> + <service_fmri value='svc:/network/early-admin:default' /> </dependency> + <instance name='default' enabled='true'> + <exec_method type='method' name='start' @@ -92,82 +110,6 @@ </instance> - <instance name='nwam' enabled='false'> - - <exec_method - type='method' - name='start' - exec='/lib/svc/method/net-nwam start' - timeout_seconds='120' > - <method_context> - <method_credential user='root' group='root' - supp_groups='netadm' privileges='zone' /> - </method_context> - </exec_method> - - <exec_method - type='method' - name='stop' - exec='/lib/svc/method/net-nwam stop' - timeout_seconds='60' > - <method_context> - <method_credential user='root' group='root' - supp_groups='netadm' privileges='zone' /> - </method_context> - </exec_method> - - <exec_method - type='method' - name='refresh' - exec='/lib/svc/method/net-nwam refresh' - timeout_seconds='60' > - <method_context> - <method_credential user='root' group='root' - supp_groups='netadm' privileges='zone' /> - </method_context> - </exec_method> - - <property_group name='general' type='framework'> - <!-- to start/stop NWAM services --> - <propval name='action_authorization' type='astring' - value='solaris.smf.manage.nwam' /> - <propval name='value_authorization' type='astring' - value='solaris.smf.manage.nwam' /> - </property_group> - - <property_group name='nwamd' type='application'> - <stability value='Unstable' /> - <propval name='debug' type='boolean' value='false' /> - <propval name='autoconf' type='boolean' value='false' /> - <propval name='ncu_wait_time' type='count' value='60' /> - <propval name='condition_check_interval' type='count' - value='120' /> - <propval name='scan_interval' type='count' value='120' /> - <propval name='scan_level' type='astring' value='weak' /> - <propval name='strict_bssid' type='boolean' value='false' /> - <propval name='active_ncp' type='astring' value='Automatic' /> - <propval name='value_authorization' type='astring' - value='solaris.smf.value.nwam' /> - </property_group> - - <template> - <common_name> - <loctext xml:lang='C'> - physical network interface autoconfiguration - </loctext> - </common_name> - <documentation> - <manpage title='nwamd' section='8' - manpath='/usr/share/man' /> - <doc_link - name='Network Auto-Magic OpenSolaris Project Page' - uri='http://hub.opensolaris.org/bin/view/Project+nwam/' - /> - </documentation> - </template> - - </instance> - <stability value='Unstable' /> </service> diff --git a/usr/src/cmd/svc/milestone/network-routing-setup.xml b/usr/src/cmd/svc/milestone/network-routing-setup.xml index bc7560fa98..c84a88577e 100644 --- a/usr/src/cmd/svc/milestone/network-routing-setup.xml +++ b/usr/src/cmd/svc/milestone/network-routing-setup.xml @@ -38,11 +38,19 @@ <!-- loopback/physical network configuration is required --> <dependency - name='network' - grouping='optional_all' + name='loopback' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/network/loopback' /> + </dependency> + + <dependency + name='physical' + grouping='require_all' restart_on='none' type='service'> - <service_fmri value='svc:/milestone/network' /> + <service_fmri value='svc:/network/physical' /> </dependency> <!-- usr filesystem required to run routing-related commands --> diff --git a/usr/src/cmd/svc/milestone/network.xml b/usr/src/cmd/svc/milestone/network.xml index 75b5578f44..48386ebf73 100644 --- a/usr/src/cmd/svc/milestone/network.xml +++ b/usr/src/cmd/svc/milestone/network.xml @@ -54,6 +54,14 @@ <service_fmri value='svc:/network/physical' /> </dependency> + <dependency + name='routing-setup' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/network/routing-setup' /> + </dependency> + <exec_method type='method' name='start' diff --git a/usr/src/cmd/svc/milestone/single-user.xml b/usr/src/cmd/svc/milestone/single-user.xml index ffa150af57..88b846a2ce 100644 --- a/usr/src/cmd/svc/milestone/single-user.xml +++ b/usr/src/cmd/svc/milestone/single-user.xml @@ -68,7 +68,7 @@ <dependency name='manifests' - grouping='require_all' + grouping='optional_all' restart_on='none' type='service'> <service_fmri value='svc:/system/manifest-import' /> diff --git a/usr/src/cmd/svc/milestone/smartdc-config b/usr/src/cmd/svc/milestone/smartdc-config new file mode 100755 index 0000000000..510c93add6 --- /dev/null +++ b/usr/src/cmd/svc/milestone/smartdc-config @@ -0,0 +1,211 @@ +#!/bin/bash +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2019 Joyent, Inc. +# + +# +# Despite its "smartdc/config" name, this service is used both for SmartOS and +# Triton. It has two jobs: +# +# During an initial setup, this runs through the initial (possibly interactive) +# configuration. +# +# During normal operation, it does some miscellaneous configuration based on +# /usbkey/config (which, under Triton, will have already been updated from the +# USB key by svc:/system/filesystem/smartdc:default). +# + +set -o errexit +set -o xtrace + +. /lib/svc/share/smf_include.sh +. /lib/sdc/config.sh + +export PATH="/usr/sbin:/sbin:/usr/bin" + +set_root_password() { + enc_password=$1 + + sed -e "s|^root:[^\:]*:|root:${enc_password}:|" /etc/shadow > /etc/shadow.new \ + && chmod 400 /etc/shadow.new \ + && mv /etc/shadow.new /etc/shadow +} + +case "$1" in +'start') + + # If we're a headnode, see if we have to do interactive configuration. + if /bin/bootparams | grep "^headnode=true" > /dev/null 2>&1; then + USB_PATH=/mnt/`svcprop -p "joyentfs/usb_mountpoint" svc:/system/filesystem/smartdc:default` + + # Check for config and run interactive if it doesn't exist. + if [[ ! -f ${USB_PATH}/config ]]; then + if /bin/bootparams | grep "^noimport=true" >/dev/null 2>&1; then + # Skipping interactive config, bypass rest of script. + exit $SMF_EXIT_OK + fi + + /smartdc/lib/sdc-on-tty -d /dev/console \ + ${USB_PATH}/scripts/prompt-config.sh "${USB_PATH}" + + # If user quit from interactive configuration then we're done. + [[ ! -f ${USB_PATH}/config ]] && exit $SMF_EXIT_OK + fi + elif /bin/bootparams | grep "^smartos=true" > /dev/null 2>&1; then + USB_PATH=/`svcprop -p "joyentfs/usb_copy_path" svc:/system/filesystem/smartdc:default` + + # Check for config and run interactive if it doesn't exist. + if [[ ! -f ${USB_PATH}/config ]]; then + if /bin/bootparams | grep "^noimport=true" >/dev/null 2>&1; then + # Skipping interactive config, bypass rest of script. + exit $SMF_EXIT_OK + fi + + /smartdc/lib/sdc-on-tty -d /dev/console \ + /smartdc/lib/smartos_prompt_config.sh "${USB_PATH}" + + # If user quit from interactive configuration then we're done. + [[ ! -f ${USB_PATH}/config ]] && exit $SMF_EXIT_OK + fi + fi + + # This puts config vars in CONFIG_ + load_sdc_config + load_sdc_sysinfo + + # Write the info about this datacenter to /.dcinfo so we can use in the GZ + echo "SDC_DATACENTER_NAME='${CONFIG_datacenter_name}'" > /.dcinfo + + if [[ -n "${SYSINFO_Bootparam_smartos}" && -f /usbkey/shadow ]]; then + echo "setting root password from /usbkey/shadow" + # Boot parameter takes precidence over config + elif [[ -n "${SYSINFO_Bootparam_root_shadow}" ]]; then + set_root_password "${SYSINFO_Bootparam_root_shadow}" + echo "Set root password boot parameters." + elif [[ -n "${CONFIG_root_shadow}" ]]; then + set_root_password "${CONFIG_root_shadow}" + echo "Set root password from config." + else + echo "No root shadow entry in the config, cannot set." + fi + + # Set authorized_keys for root + if [[ -n "${CONFIG_root_authorized_keys_file}" ]] \ + && [[ -n "${CONFIG_config_inc_dir}" ]] \ + && [[ -d "${CONFIG_config_inc_dir}" ]] \ + && [[ -f "${CONFIG_config_inc_dir}/${CONFIG_root_authorized_keys_file}" ]]; then + + mkdir -p /root/.ssh + cp "${CONFIG_config_inc_dir}/${CONFIG_root_authorized_keys_file}" /root/.ssh/authorized_keys + chmod 0600 /root/.ssh/authorized_keys + chmod 0700 /root/.ssh + fi + + if [[ -n "${CONFIG_ntp_conf_file}" ]] \ + && [[ -n "${CONFIG_config_inc_dir}" ]] \ + && [[ -d "${CONFIG_config_inc_dir}" ]] \ + && [[ -f "${CONFIG_config_inc_dir}/${CONFIG_ntp_conf_file}" ]]; then + # + # We were given a valid NTP configuration file, so use it without + # modification: + # + cp "${CONFIG_config_inc_dir}/${CONFIG_ntp_conf_file}" \ + /etc/inet/ntp.conf + echo "Copied NTP configuration." + else + # + # If we have an admin network defined, allow time service from this + # network: + # + ntp_aflag= + if [[ -n ${CONFIG_admin_network} && -n ${CONFIG_admin_netmask} ]]; then + if [[ "${CONFIG_admin_network}" != "..." ]]; then + ntp_aflag="-a ${CONFIG_admin_network}/${CONFIG_admin_netmask}" + fi + fi + + # + # If we were given a list of servers, use it: + # + ntp_hosts="${CONFIG_ntp_hosts}" + if [[ -z "${ntp_hosts}" ]]; then + # + # Otherwise, use the default SmartOS vendor pool from the NTP + # Pool Project: + # + ntp_hosts='0.smartos.pool.ntp.org' + fi + + # + # Generate NTP configuration: + # + if ! /smartdc/lib/ntp_config -f /etc/inet/ntp.conf \ + -s "${ntp_hosts}" ${ntp_aflag}; then + echo "FATAL: could not configure NTP" >&2 + exit ${SMF_EXIT_ERR_CONFIG} + fi + echo "Generated NTP configuration." + fi + + # set the keymap. For dvorak for instance + if [[ -n ${CONFIG_default_keymap} ]]; then + /usr/bin/loadkeys ${CONFIG_default_keymap} + fi + + # + # In SmartOS, disabling SMT via the boot option is a pain, so we support a + # config option in /usbkey/config, the official mechanism for permanent + # configuration. We'd like to do this earlier but we have to wait for + # /usbkey to be mounted first. This does imply we've potentially handed out + # "too many" interrupts for the set of CPUs remaining online. + # + if [[ -n $(/bin/bootparams | grep '^smartos=true') ]]; then + if [[ "$CONFIG_smt_enabled" = "false" ]]; then + psradm -aS || exit $SMF_EXIT_ERR_FATAL + fi + fi + + # Enable virtual terminals to support interactive installation + vtdaemon="svc:/system/vtdaemon" + svccfg -s ${vtdaemon} setprop options/hotkeys=true + svcadm refresh ${vtdaemon} + svcadm enable ${vtdaemon} + svcadm enable svc:/system/console-login:vt2 + svcadm enable svc:/system/console-login:vt3 + svcadm enable svc:/system/console-login:vt4 + svcadm enable svc:/system/console-login:vt5 + svcadm enable svc:/system/console-login:vt6 + + # force update of sysinfo (and dump to stdout so we have in the log) + sysinfo -f + + ;; + +'stop') + ;; + +*) + echo "Usage: $0 { start | stop }" + exit $SMF_EXIT_ERR_FATAL + ;; +esac +exit $SMF_EXIT_OK diff --git a/usr/src/cmd/svc/milestone/smartdc-config.xml b/usr/src/cmd/svc/milestone/smartdc-config.xml new file mode 100644 index 0000000000..bf84d1991b --- /dev/null +++ b/usr/src/cmd/svc/milestone/smartdc-config.xml @@ -0,0 +1,145 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + Copyright 2011 Joyent, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. +--> + +<service_bundle type='manifest' name='smartdc:config'> + +<!-- + This service applies inital configuration for the GZ in smartdc. +--> +<service + name='system/smartdc/config' + type='service' + version='1'> + + <create_default_instance enabled='true' /> + + <single_instance /> + + <!-- + dependency/dependent info + We need to wait for fs-joyent to be done so we can access the USB key + We need to run before sysconfig so interactive configuration can talk + on the console + (sysconfig) + We don't want anything else running which spews on console + (system/identy) + We don't want manifest import scribbling on the console while running + (manifest-import) + network/physical can't run without a config file + (network/physical) + --> + <dependency + name='filesystem-joyent' + type='service' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/system/filesystem/smartdc' /> + </dependency> + + <!-- + We don't want ssh starting if we haven't updated the passwords/keys + --> + <dependent + name='ssh_multi-user-server' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/network/ssh' /> + </dependent> + + <!-- Make ntp wait so we can setup the config. --> + <dependent + name='ntp' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/network/ntp' /> + </dependent> + + <dependent + name='headnode_config' + grouping='optional_all' + restart_on='none'> + <service_fmri value='svc:/milestone/sysconfig' /> + </dependent> + + <dependent + name='identity' + grouping='optional_all' + restart_on='none'> + <service_fmri value='svc:/system/identity:node' /> + </dependent> + + <dependent + name='headnode_no_output' + grouping='optional_all' + restart_on='none'> + <service_fmri value='svc:/system/manifest-import' /> + </dependent> + + <dependent + name='network-physical' + grouping='optional_all' + restart_on='none'> + <service_fmri value='svc:/network/physical:default' /> + </dependent> + + <exec_method + type='method' + name='start' + exec='/lib/svc/method/smartdc-config %m' + timeout_seconds='0'> + </exec_method> + + <exec_method + type='method' + name='stop' + exec='/lib/svc/method/smartdc-config %m' + timeout_seconds='60'> + </exec_method> + + <property_group name='startd' type='framework'> + <propval name='duration' type='astring' value='transient' /> + </property_group> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> + SmartDC live-image config management service + </loctext> + </common_name> + <documentation> + <manpage title='smartdc' section='5' manpath='/usr/share/man' /> + </documentation> + </template> +</service> + +</service_bundle> diff --git a/usr/src/cmd/svc/milestone/smartdc-init b/usr/src/cmd/svc/milestone/smartdc-init new file mode 100755 index 0000000000..1b2a550095 --- /dev/null +++ b/usr/src/cmd/svc/milestone/smartdc-init @@ -0,0 +1,246 @@ +#!/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 (c) 2018, Joyent, Inc. +# + +export PS4='[\D{%FT%TZ}] ${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' +set -o xtrace + +. /lib/svc/share/smf_include.sh +. /lib/sdc/config.sh + +# Make sure working directory is / to prevent unmounting problems. +cd / +PATH=/usr/sbin:/usr/bin; export PATH + +wait_and_clear() +{ + while [ true ]; do + # It seems like jobs -p can miscount if we don't run jobs first + jobs >/dev/null + local cnt=`jobs -p | wc -l` + [ $cnt -eq 0 ] && break + for s in `svcs -x | nawk '{ + if ($1 ~ /^svc:/) nm=$1 + if ($1 == "State:" && $2 == "maintenance") print nm + }'` + do + svcadm clear $s + done + sleep 1 + done +} + +# Sets the default firewall rules for a node (unless they're already set) +set_default_fw_rules() { + local fw_default_v + if [[ -f /var/fw/.default_rules_setup ]]; then + read fw_default_v < /var/fw/.default_rules_setup + else + fw_default_v=0 + fi + + # Handle empty files from before we started versioning default rules + if [[ -z $fw_default_v ]]; then + fw_default_v=1 + fi + + if [[ $fw_default_v -lt 1 ]]; then + /usr/sbin/fwadm add -f - <<RULES +{ + "rules": [ + { + "description": "allow all ICMPv4 types", + "rule": "FROM any TO all vms ALLOW icmp type all", + "enabled": true, + "global": true + } + ] +} +RULES + [[ $? -ne 0 ]] && return 1 + echo 1 > /var/fw/.default_rules_setup + fi + + if [[ $fw_default_v -lt 2 ]]; then + /usr/sbin/fwadm add -f - <<RULES +{ + "rules": [ + { + "description": "allow all ICMPv6 types", + "rule": "FROM any TO all vms ALLOW icmp6 type all", + "enabled": true, + "global": true + } + ] +} +RULES + [[ $? -ne 0 ]] && return 1 + echo 2 > /var/fw/.default_rules_setup + fi +} + +configure_fwadm() +{ + if [[ ! -d /var/log/fw/logs ]]; then + mkdir -p /var/log/fw/logs + mv /var/log/fw/*-*.log /var/log/fw/logs + fi + + # See also OS-2635 + if [[ -f /var/log/fw/fw.log.0 ]]; then + for file in /var/log/fw/fw.log.[0-9]*; do + mv ${file} "/var/log/fw/fwadm_$(uname -n)_$(stat -c "%y" ${file} | cut -d'.' -f1 | tr ' ' 'T').log" + done + fi + if [[ -f /var/log/fw/fw.log ]]; then + mv /var/log/fw/fw.log /var/log/fw/fwadm.log + fi + + if [[ ! -e /var/log/fw/fwadm.log ]]; then + touch /var/log/fw/fwadm.log + fi +} + +configure_vmadm() +{ + # ensure /var/log/vm exists for VM.log logs + mkdir -p /var/log/vm/logs + + # See also OS-2635 + if [[ -f /var/log/vm/vm.log.0 ]]; then + for file in /var/log/vm/vm.log.[0-9]*; do + mv ${file} "/var/log/vm/vmadm_$(uname -n)_$(stat -c "%y" ${file} | cut -d'.' -f1 | tr ' ' 'T').log" + done + fi + if [[ -f /var/log/vm/vm.log ]]; then + mv /var/log/vm/vm.log /var/log/vm/vmadm.log + fi + + # need to create this file so rotation works + if [[ ! -e /var/log/vm/vmadm.log ]]; then + touch /var/log/vm/vmadm.log + fi +} + +update_root_password() +{ + + enc_password=`nawk -F= '{ + if ($1 == "root_shadow") + print substr($2, 2, length($2) - 2) + }' /opt/smartdc/config/node.config` + + [[ -z "$enc_password" ]] && return 0 + + sed -e "s|^root:[^\:]*:|root:${enc_password}:|" /etc/shadow \ + >/etc/shadow.new \ + && chmod 400 /etc/shadow.new \ + && mv /etc/shadow.new /etc/shadow +} + +# Loads config file for the node. These are the config values from the headnode +# plus authorized keys and anything else we want. +# This function is only invoked on a compute node. +install_config() +{ + # On standalone machines we don't do this update + [[ -n $(/usr/bin/bootparams | grep "^standalone=true") ]] && return 0 + + load_sdc_config + + curl -k -o /tmp/node.config --silent \ + "http://${CONFIG_assets_admin_ip}/extra/joysetup/node.config" + + [[ ! -f /tmp/node.config ]] && return 0 + grep datacenter_name /tmp/node.config >/dev/null 2>&1 + if [ $? != 0 ]; then + # There is no valid config file served by the assets zone + rm -f /tmp/node.config + return 0 + fi + + # Install the file if the local copy is different + diff /tmp/node.config /opt/smartdc/config/node.config >/dev/null 2>&1 + if [ $? != 0 ]; then + printf "Updating config file\n" >/dev/console + mkdir -p /opt/smartdc/config + mv /tmp/node.config /opt/smartdc/config + update_root_password + else + rm -f /tmp/node.config + fi +} + +case "$1" in +'start') + + # Always setup socket filter no matter what happens next. + /sbin/soconfig -F datafilt datafilt prog '2:2:0,2:2:6,26:2:0,26:2:6' + + USBMOUNT= + + # If we're not importing the pools, we shouldn't try to setup as a headnode + # (since there'll be no zpool) + if /bin/bootparams | grep "^noimport=true" > /dev/null 2>&1; then + exit $SMF_EXIT_OK + fi + + # If we're a headnode, we'll not have AMQP args on the cmdline, and we want + # to run an initial_script first anyway. + if /bin/bootparams | grep "^headnode=true" > /dev/null 2>&1; then + USBMOUNT=/mnt/`svcprop -p joyentfs/usb_mountpoint svc:/system/filesystem/smartdc:default` + + # No config file (e.g. user quit during interactive configuration), so + # treat as if "noimport=true". + [[ ! -f $USBMOUNT/config ]] && exit $SMF_EXIT_OK + + initial_script=${USBMOUNT}/$(grep "^initial_script=" $USBMOUNT/config.inc/generic 2>/dev/null | cut -d'=' -f2-) + if [ -n ${initial_script} ] && [ -e ${initial_script} ]; then + # Execute the script + ${initial_script} + result=$? + if [ ${result} -eq 2 ]; then + # we're rebooting, no need to start ur + echo "REBOOTING!" >> /dev/console + enable_ur="false" + elif [ ${result} -ne 0 ]; then + echo "WARNING: initial_script failed with exit code [${result}]." + exit $SMF_EXIT_ERR_FATAL + fi + fi + elif /bin/bootparams | grep "^smartos=true" > /dev/null 2>&1; then + set_default_fw_rules + else + install_config + fi + + configure_fwadm + configure_vmadm + + if /bin/bootparams | grep "^headnode=true" > /dev/null 2>&1; then + /usr/sbin/umount $USBMOUNT + fi + + ;; + +'stop') + ;; + +*) + echo "Usage: $0 { start | stop }" + exit $SMF_EXIT_ERR_FATAL + ;; +esac +exit $SMF_EXIT_OK diff --git a/usr/src/cmd/svc/milestone/smartdc-init.xml b/usr/src/cmd/svc/milestone/smartdc-init.xml new file mode 100644 index 0000000000..266aac53df --- /dev/null +++ b/usr/src/cmd/svc/milestone/smartdc-init.xml @@ -0,0 +1,124 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + Copyright 2012 Joyent, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. +--> + +<service_bundle type='manifest' name='Joyent:joyent'> + +<!-- + This service imports the zone's zpool on bootup, before zones try to + start. It also sets up configuration data on that zpool, if necessary. +--> +<service + name='system/smartdc/init' + type='service' + version='1'> + + <create_default_instance enabled='true' /> + + <single_instance /> + + <!-- + dependency info + This depends on the system/zones svc which seems weird since that + svc runs late in boot because it depends on milestone/multi-user-server. + Our svc does the initial setup of the infrastructure zones on the + headnode so we need the system/zones svc to run first so that we can + install and boot the infrastructure zones. The reason this seems + weird is that this svc also sets up the zpool when we first boot, + before we reboot to install the infrastructure zones, so it might seem + like it should run early in boot, but it can't because of the zone + issue described above. + + The dependency on network/physical is needed since all of the earlier + svcs which have a dependency on network/physical are using optional_all. + That makes sense for those svcs since they can come up without a + network, but for SmartOS, by this point we require the network to be + there. + --> + <dependency + name='zones' + type='service' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/system/zones' /> + </dependency> + + <dependency + name='net-physical' + type='service' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/network/physical' /> + </dependency> + + <exec_method + type='method' + name='start' + exec='/lib/svc/method/smartdc-init %m' + timeout_seconds='0'> + </exec_method> + + <exec_method + type='method' + name='stop' + exec='/lib/svc/method/smartdc-init %m' + timeout_seconds='60'> + </exec_method> + + <property_group name='startd' type='framework'> + <propval name='duration' type='astring' value='transient' /> + </property_group> + + <!-- + The zpool holding the zones (this must be imported). + --> + <property_group name='config' type='application'> + <propval name='zpool' type='astring' value='zones' /> + </property_group> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> + Joyent live-image management service + </loctext> + </common_name> + <documentation> + <manpage title='zones' section='5' manpath='/usr/share/man' /> + <manpage + title='zpool' + section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> +</service> + +</service_bundle> diff --git a/usr/src/cmd/svc/milestone/smartdc-ur b/usr/src/cmd/svc/milestone/smartdc-ur new file mode 100755 index 0000000000..2c32482368 --- /dev/null +++ b/usr/src/cmd/svc/milestone/smartdc-ur @@ -0,0 +1,79 @@ +#!/bin/bash +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2010-2011 Joyent, Inc. All rights reserved. +# Use is subject to license terms. + +set -o xtrace + +. /lib/svc/share/smf_include.sh +. /lib/sdc/config.sh + +PATH=/usr/sbin:/usr/bin; export PATH + +if [[ -n "$(/bin/bootparams | grep "^standalone=true")" || + -n "$(/bin/bootparams | grep "^smartos=true")" ]]; then + # Standalone and SmartOS systems do not need ur + svcadm disable "${SMF_FMRI}" + exit $SMF_EXIT_OK +fi + +case "$1" in +'start') + # + # Grab AMQP parameters from the kernel command line or headnode config + # + + load_sdc_config + load_sdc_sysinfo + + if [[ -n ${SYSINFO_Bootparam_rabbitmq} ]]; then + rabbit=${SYSINFO_Bootparam_rabbitmq} + fi + if [[ -z ${rabbit} ]] && [[ -n ${CONFIG_rabbitmq} ]]; then + rabbit=${CONFIG_rabbitmq} + fi + + if [[ -z $rabbit ]]; then + echo "unable to find AMQP parameters!" + exit $SMF_EXIT_ERR_FATAL + fi + + export AMQP_LOGIN=$(echo $rabbit | cut -d: -f1) + export AMQP_PASSWORD=$(echo $rabbit | cut -d: -f2) + export AMQP_HOST=$(echo $rabbit | cut -d: -f3) + export AMQP_PORT=$(echo $rabbit | cut -d: -f4) + export NODE_PATH=/usr/node/node_modules + + /usr/bin/ctrun -l child -o noorphan /smartdc/ur-agent/ur-agent 2>&1 & + + ;; + +'stop') + ;; + +*) + echo "Usage: $0 { start | stop }" + exit $SMF_EXIT_ERR_FATAL + ;; +esac +exit $SMF_EXIT_OK diff --git a/usr/src/cmd/svc/milestone/smartdc-ur.xml b/usr/src/cmd/svc/milestone/smartdc-ur.xml new file mode 100644 index 0000000000..7ee78dcb02 --- /dev/null +++ b/usr/src/cmd/svc/milestone/smartdc-ur.xml @@ -0,0 +1,62 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +--> + +<!-- + Copyright (c) 2014, Joyent, Inc. +--> + +<service_bundle type="manifest" name="ur"> + <service name="smartdc/agent/ur" type="service" version="0.0.1"> + + <create_default_instance enabled="true"/> + <single_instance/> + + <dependency name="smartdc-init" grouping="require_all" restart_on="error" type="service"> + <service_fmri value="svc:/milestone/single-user"/> + </dependency> + + <exec_method + type="method" + name="start" + exec="/lib/svc/method/smartdc-ur %m" + timeout_seconds="60"> + <method_context> + <method_credential user="root" group="staff"/> + </method_context> + </exec_method> + + <exec_method type="method" name="restart" exec=":kill" timeout_seconds="60"> + <method_context> + <method_credential user="root" group="staff"/> + </method_context> + </exec_method> + + <exec_method type="method" name="stop" exec=":kill" timeout_seconds="60"> + <method_context> + <method_credential user="root" group="staff"/> + </method_context> + </exec_method> + + <property_group name="startd" type="framework"> + <propval name="ignore_error" type="astring" value="core,signal"/> + </property_group> + + <property_group name="application" type="application"> + </property_group> + + <stability value="Evolving"/> + + <template> + <common_name> + <loctext xml:lang="C">Ur Agent (node)</loctext> + </common_name> + </template> + + </service> + +</service_bundle> diff --git a/usr/src/cmd/svc/milestone/sysidtool-net b/usr/src/cmd/svc/milestone/sysidtool-net new file mode 100755 index 0000000000..538d17bba1 --- /dev/null +++ b/usr/src/cmd/svc/milestone/sysidtool-net @@ -0,0 +1,5 @@ +#!/bin/sh + +# This exists solely to support the service on older SmartOS zone images. + +exit 0 diff --git a/usr/src/cmd/svc/milestone/sysidtool-system b/usr/src/cmd/svc/milestone/sysidtool-system new file mode 100755 index 0000000000..538d17bba1 --- /dev/null +++ b/usr/src/cmd/svc/milestone/sysidtool-system @@ -0,0 +1,5 @@ +#!/bin/sh + +# This exists solely to support the service on older SmartOS zone images. + +exit 0 diff --git a/usr/src/cmd/svc/profile/Makefile b/usr/src/cmd/svc/profile/Makefile index 2b00542413..8c44470471 100644 --- a/usr/src/cmd/svc/profile/Makefile +++ b/usr/src/cmd/svc/profile/Makefile @@ -23,6 +23,7 @@ # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # Copyright 2019 Peter Tribble. +# Copyright 2019 Joyent, Inc. # include ../../Makefile.cmd @@ -32,6 +33,7 @@ FILEMODE = 0444 ROOTPROFILE = $(ROOT)/etc/svc/profile PROFILESRCS = \ + generic.xml \ generic_open.xml \ generic_limited_net.xml \ inetd_generic.xml \ @@ -60,7 +62,7 @@ TEST = /usr/bin/test LISTSVCS = listsvcs.pl install: all $(PROFILES) - $(RM) $(ROOTPROFILE)/platform.xml + $(CP) platform_none.xml $(ROOTPROFILE)/platform.xml # SUNW,Sun-Fire-V890 $(RM) $(ROOTPROFILE)/platform_SUNW,Sun-Fire-V890.xml $(LN) $(ROOTPROFILE)/platform_SUNW,Sun-Fire-880.xml \ @@ -87,7 +89,7 @@ $(CHECK_OPEN) $(CHECK_LMTD): \ @$(COMM) -23 $@.enabled $@.all | $(TEE) $@.notcovered @$(TEST) ! -s $@.notcovered && $(TOUCH) $@ -lint _msg: +_msg: clobber clean: $(RM) $(CHECK_OPEN)* $(CHECK_LMTD)* diff --git a/usr/src/cmd/svc/profile/generic.xml b/usr/src/cmd/svc/profile/generic.xml new file mode 100644 index 0000000000..61232545dd --- /dev/null +++ b/usr/src/cmd/svc/profile/generic.xml @@ -0,0 +1,397 @@ +<?xml version='1.0'?> +<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'> +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + Copyright 2010 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + Copyright 2019 Joyent, Inc. + + The purpose of the limited_net profile is to provide a set of + active services that allow one to connect to the machine via ssh + (requires sshd). The services which are deactivated here are those + that are at odds with this goal. Those which are activated are + explicit requirements for the goal's satisfaction. + + NOTE: Service profiles delivered by this package are not editable, + and their contents will be overwritten by package or patch + operations, including operating system upgrade. Make customizations + in a distinct file. The paths, /etc/svc/profile/site.xml and + /var/svc/profile/site.xml, are distinguished locations for site-specific + service profile, treated otherwise equivalently to this file. +--> +<service_bundle type='profile' name='generic_limited_net' + xmlns:xi='http://www.w3.org/2003/XInclude' > + + <!-- + svc.startd(1M) services + --> + <service name='system/cryptosvc' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/coreadm' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/metainit' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='system/cron' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/dbus' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/extended-accounting' version='1' type='service'> + <instance name='flow' enabled='false'/> + <instance name='process' enabled='false'/> + <instance name='task' enabled='false'/> + <instance name='net' enabled='false'/> + </service> + <service name='system/identity' version='1' type='service'> + <instance name='domain' enabled='true'/> + </service> + <service name='system/intrd' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/keymap' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/picl' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/sac' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='system/scheduler' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/system-log' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/utmp' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/zones' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/rcap' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/hotplug' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='network/rpc/bind' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/name-service-cache' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='network/netmask' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/nfs/status' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/nfs/nlockmgr' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/nfs/client' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/nfs/server' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/nfs/rquota' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/nfs/cbd' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/nfs/mapid' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/smb/client' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='network/ssh' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='network/smtp' version='1' type='service'> + <instance name='sendmail' enabled='false'/> + </service> + <service name='network/sendmail-client' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/inetd' version='1' type='restarter'> + <instance name='default' enabled='true'/> + </service> + <service name='system/filesystem/autofs' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='system/filesystem/rmvolmgr' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='system/power' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + + <service name='network/dns/multicast' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='network/dhcp-server' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='network/ntp' version='1' type='service'> + <instance name='default' enabled='true' /> + </service> + <service name='network/rarp' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='network/slp' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='network/security/kadmin' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='network/security/krb5_prop' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='network/security/krb5kdc' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + + <service name='application/management/net-snmp' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/management/seaport' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/management/snmpdx' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/management/wbem' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/print/ipp-listener' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/print/ppd-cache-update' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/print/rfc1179' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/cups/in-lpd' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/stosreg' version='1' type='service'> + <instance name='default' enabled='true' /> + </service> + + <!-- + default inetd(1M) services + --> + <service name='network/finger' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/ftp' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/login' version='1' type='service'> + <instance name='rlogin' enabled='false'/> + <instance name='klogin' enabled='false'/> + <instance name='eklogin' enabled='false'/> + </service> + <service name='network/shell' version='1' type='service'> + <instance name='default' enabled='false'/> + <instance name='kshell' enabled='false'/> + </service> + <service name='network/telnet' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + non-default inetd(1M) services + --> + <service name='network/uucp' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/chargen' version='1' type='service'> + <instance name='stream' enabled='false'/> + <instance name='dgram' enabled='false'/> + </service> + <service name='network/daytime' version='1' type='service'> + <instance name='stream' enabled='false'/> + <instance name='dgram' enabled='false'/> + </service> + <service name='network/discard' version='1' type='service'> + <instance name='stream' enabled='false'/> + <instance name='dgram' enabled='false'/> + </service> + <service name='network/echo' version='1' type='service'> + <instance name='stream' enabled='false'/> + <instance name='dgram' enabled='false'/> + </service> + <service name='network/time' version='1' type='service'> + <instance name='stream' enabled='false'/> + <instance name='dgram' enabled='false'/> + </service> + <service name='network/comsat' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rexec' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/talk' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/stdiscover' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/stlisten' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + default inetd(1M) RPC services enabled + --> + <service name='network/rpc/gss' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/mdcomm' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/smserver' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/security/ktkt_warn' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + default inetd(1M) RPC services disabled + --> + <service name='network/rpc/rstat' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/rusers' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/meta' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/metamed' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/metamh' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + non-default inetd(1M) RPC services disabled + --> + <service name='network/rpc/rex' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/spray' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/wall' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + Disable Avahi mDNS bridge service + --> + <service name='system/avahi-bridge-dsd' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + Enable CDE/ToolTalk/GDM services. + --> + <service name='network/rpc/cde-ttdbserver' version='1' type='service'> + <instance name='tcp' enabled='false' /> + </service> + <service name='application/graphical-login/gdm' version='1' + type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='network/rpc/cde-calendar-manager' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + Disable X11 services. + --> + <service name='application/x11/xfs' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + pkg.depotd service + --> + <service name='system/pkgserv' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + Enable VNC config service for xVM + --> + <service name='system/xvm/vnc-config' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='system/xvm/ipagent' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='application/print/service-selector' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='system/dumpadm' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + + <service name='system/metasync' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='application/font/fc-cache' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='system/consolekit' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='application/opengl/ogl-select' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='system/hal' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + +</service_bundle> diff --git a/usr/src/cmd/svc/shell/mfsthistory b/usr/src/cmd/svc/shell/mfsthistory index 7a719d1c49..2a1bac3b74 100644 --- a/usr/src/cmd/svc/shell/mfsthistory +++ b/usr/src/cmd/svc/shell/mfsthistory @@ -311,7 +311,6 @@ svc:/network/routing/legacy-routing:ipv4 var/svc/manifest/network/routing/legacy svc:/network/routing/legacy-routing var/svc/manifest/network/routing/legacy-routing.xml svc:/network/shares/group:default var/svc/manifest/network/shares/group.xml svc:/network/shares/group var/svc/manifest/network/shares/group.xml -svc:/network/ssl/proxy var/svc/manifest/network/ssl/kssl-proxy.xml svc:/system/auditd:default var/svc/manifest/system/auditd.xml svc:/system/auditd var/svc/manifest/system/auditd.xml svc:/system/boot-archive-update:default var/svc/manifest/system/boot-archive-update.xml diff --git a/usr/src/cmd/svc/shell/smf_include.sh b/usr/src/cmd/svc/shell/smf_include.sh index e7285c42e7..7aef5c73a9 100644 --- a/usr/src/cmd/svc/shell/smf_include.sh +++ b/usr/src/cmd/svc/shell/smf_include.sh @@ -22,6 +22,7 @@ # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2012 Joyent, Inc. All rights reserved. # Copyright 2015 Nexenta Systems, Inc. All rights reserved. # Copyright 2012 Joyent, Inc. All rights reserved. # Copyright 2021 Oxide Computer Company diff --git a/usr/src/cmd/svc/startd/graph.c b/usr/src/cmd/svc/startd/graph.c index f8d5f2b0ce..685c1c262c 100644 --- a/usr/src/cmd/svc/startd/graph.c +++ b/usr/src/cmd/svc/startd/graph.c @@ -145,6 +145,8 @@ #include <assert.h> #include <errno.h> #include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> #include <fm/libfmevent.h> #include <libscf.h> #include <libscf_priv.h> @@ -4846,6 +4848,20 @@ vertex_subgraph_dependencies_shutdown(scf_handle_t *h, graph_vertex_t *v, was_up = up_state(old_state); now_up = up_state(v->gv_state); + if (halting != -1 && old_state == RESTARTER_STATE_DISABLED && + v->gv_state != RESTARTER_STATE_DISABLED) { + /* + * We're halting and we have a svc which is transitioning to + * offline in parallel. This leads to a race condition where + * gt_enter_offline might re-enable the svc after we disabled + * it. Since we're halting, we want to ensure no svc ever + * transitions out of the disabled state. In this case, modify + * the flags to keep us on the halting path. + */ + was_up = 0; + now_up = 0; + } + if (!was_up && now_up) { ++non_subgraph_svcs; } else if (was_up && !now_up) { @@ -6785,6 +6801,9 @@ repository_event_thread(void *unused) char *fmri = startd_alloc(max_scf_fmri_size); char *pg_name = startd_alloc(max_scf_value_size); int r; + int fd; + + (void) pthread_setname_np(pthread_self(), "repository_event"); (void) pthread_setname_np(pthread_self(), "repository_event"); @@ -6809,6 +6828,14 @@ retry: goto retry; } + if ((fd = open("/etc/svc/volatile/startd.ready", O_RDONLY | O_CREAT, + S_IRUSR)) < 0) { + log_error(LOG_WARNING, "Couldn't create startd.ready file\n", + SCF_GROUP_FRAMEWORK, scf_strerror(scf_error())); + } else { + (void) close(fd); + } + /*CONSTCOND*/ while (1) { ssize_t res; diff --git a/usr/src/cmd/svc/startd/method.c b/usr/src/cmd/svc/startd/method.c index 1c5549179c..2629b74210 100644 --- a/usr/src/cmd/svc/startd/method.c +++ b/usr/src/cmd/svc/startd/method.c @@ -100,34 +100,18 @@ static uint_t method_events[] = { * method_record_start(restarter_inst_t *) * Record a service start for rate limiting. Place the current time * in the circular array of instance starts. + * + * Save the critical_failure_period and critical_failure_allowed with either + * the defaults or the svc properties startd/critical_failure_count and + * startd/critical_failure_period. + * ri_crit_fail_allowed is capped at RINST_START_TIMES. */ static void method_record_start(restarter_inst_t *inst) { - int index = inst->ri_start_index++ % RINST_START_TIMES; - - inst->ri_start_time[index] = gethrtime(); -} - -/* - * method_rate_critical(restarter_inst_t *) - * Return true if the average start interval is less than the permitted - * interval. The implicit interval defaults to RINST_FAILURE_RATE_NS and - * RINST_START_TIMES but may be overridden with the svc properties - * startd/critical_failure_count and startd/critical_failure_period - * which represent the number of failures to consider and the amount of - * time in seconds in which that number may occur, respectively. Note that - * this time is measured as of the transition to 'enabled' rather than wall - * clock time. - * Implicit success if insufficient measurements for an average exist. - */ -int -method_rate_critical(restarter_inst_t *inst) -{ + int index; + uint_t critical_failure_allowed = RINST_START_TIMES; hrtime_t critical_failure_period; - uint_t critical_failure_count = RINST_START_TIMES; - uint_t n = inst->ri_start_index; - hrtime_t avg_ns = 0; uint64_t scf_fr, scf_st; scf_propvec_t *prop = NULL; scf_propvec_t restart_critical[] = { @@ -151,17 +135,48 @@ method_rate_critical(restarter_inst_t *inst) * in seconds but tracked in ns */ critical_failure_period = (hrtime_t)scf_fr * NANOSEC; - critical_failure_count = (uint_t)scf_st; + critical_failure_allowed = (uint_t)scf_st; + + if (critical_failure_allowed > RINST_START_TIMES) + critical_failure_allowed = RINST_START_TIMES; + if (critical_failure_allowed < 1) + critical_failure_allowed = 1; + } - if (inst->ri_start_index < critical_failure_count) + + inst->ri_crit_fail_allowed = critical_failure_allowed; + inst->ri_crit_fail_period = critical_failure_period; + + index = inst->ri_start_index++ % critical_failure_allowed; + inst->ri_start_time[index] = gethrtime(); +} + +/* + * method_rate_critical(restarter_inst_t *) + * Return true if the number of failures within the interval + * ri_crit_fail_period exceeds ri_crit_fail_allowed. The allowed failure + * count defaults to RINST_START_TIMES and the implicit interval defaults + * to RINST_FAILURE_RATE_NS but may be overridden with the svc properties + * startd/critical_failure_count and startd/critical_failure_period which + * represent the acceptable number of failures and the amount of time in + * seconds in which that number may occur, respectively. Note that this time + * is measured as of the transition to 'enabled' rather than wall clock + * time. Implicitly not critical if insufficient failures have occured. + */ +int +method_rate_critical(restarter_inst_t *inst) +{ + uint_t n = inst->ri_start_index; + uint_t fail_allowed = inst->ri_crit_fail_allowed; + hrtime_t diff_ns; + + if (n < fail_allowed) return (0); - avg_ns = - (inst->ri_start_time[(n - 1) % critical_failure_count] - - inst->ri_start_time[n % critical_failure_count]) / - (critical_failure_count - 1); + diff_ns = inst->ri_start_time[(n - 1) % fail_allowed] - + inst->ri_start_time[n % fail_allowed]; - return (avg_ns < critical_failure_period); + return (diff_ns < inst->ri_crit_fail_period); } /* diff --git a/usr/src/cmd/svc/startd/startd.h b/usr/src/cmd/svc/startd/startd.h index a952144c93..cf6e7e5cc5 100644 --- a/usr/src/cmd/svc/startd/startd.h +++ b/usr/src/cmd/svc/startd/startd.h @@ -398,7 +398,7 @@ typedef enum { #define RINST_RETAKE_MASK 0x0f000000 -#define RINST_START_TIMES 5 /* failures to consider */ +#define RINST_START_TIMES 10 /* up to 10 fails to consider */ #define RINST_FAILURE_RATE_NS 600000000000LL /* 1 failure/10 minutes */ #define RINST_WT_SVC_FAILURE_RATE_NS NANOSEC /* 1 failure/second */ @@ -420,6 +420,8 @@ typedef struct restarter_inst { hrtime_t ri_start_time[RINST_START_TIMES]; uint_t ri_start_index; /* times started */ + uint_t ri_crit_fail_allowed; + hrtime_t ri_crit_fail_period; uu_list_node_t ri_link; pthread_mutex_t ri_lock; diff --git a/usr/src/cmd/svc/svcadm/Makefile b/usr/src/cmd/svc/svcadm/Makefile index 07d22a2226..fae2142dac 100644 --- a/usr/src/cmd/svc/svcadm/Makefile +++ b/usr/src/cmd/svc/svcadm/Makefile @@ -21,6 +21,7 @@ # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2012, Joyent, Inc. All rights reserved. # PROG = svcadm @@ -49,7 +50,11 @@ $(PROG): $(OBJS) $(POFILE): $(POFILES) cat $(POFILES) > $(POFILE) -install: all $(ROOTUSRSBINPROG) +install: all $(ROOTSBINPROG) $(ROOTUSRSBINPROG) + +$(ROOTUSRSBINPROG): + -$(RM) $@ + -$(SYMLINK) ../../sbin/$(PROG) $@ clean: $(RM) $(OBJS) diff --git a/usr/src/cmd/svc/svccfg/Makefile b/usr/src/cmd/svc/svccfg/Makefile index d5721fd12e..d000d419cd 100644 --- a/usr/src/cmd/svc/svccfg/Makefile +++ b/usr/src/cmd/svc/svccfg/Makefile @@ -93,7 +93,8 @@ $(NOT_NATIVE)SVCCFG_EXTRA_LIBS += -ltecla # # If no adjunct, then we'll already find /usr/lib via LDLIBS below. # -NATIVE_LIBS += libxml2.so libl.so libumem.so libmd.so libnvpair.so libc.so +NATIVE_LIBS += libxml2.so libl.so libumem.so libmd.so libnvpair.so libc.so \ + libscf.so libuutil.so LIBSCF = $(SRC)/lib/libscf LIBTECLA = $(SRC)/lib/libtecla # just for the header @@ -101,9 +102,10 @@ LIBUUTIL = $(SRC)/lib/libuutil LDLIBS += $(SVCCFG_EXTRA_LIBS) -$(NATIVE_BUILD)CC = $(NATIVECC) -$(NATIVE_BUILD)LD = $(NATIVELD) -$(NATIVE_BUILD)CFLAGS = $(NATIVE_CFLAGS) +$(NATIVE_BUILD)CC = $(NATIVECC64) +$(NATIVE_BUILD)LD = $(NATIVELD64) +# Add the -g debug flag in spite of being native so ctfconvert won't whine. +$(NATIVE_BUILD)CFLAGS = $(NATIVE_CFLAGS64) -g $(NATIVE_BUILD)CPPFLAGS = \ -DNATIVE_BUILD \ $(MYCPPFLAGS) \ diff --git a/usr/src/cmd/svc/svccfg/svccfg_libscf.c b/usr/src/cmd/svc/svccfg/svccfg_libscf.c index a1a3a44ad2..491040eca7 100644 --- a/usr/src/cmd/svc/svccfg/svccfg_libscf.c +++ b/usr/src/cmd/svc/svccfg/svccfg_libscf.c @@ -12604,7 +12604,7 @@ lscf_service_delete(scf_service_t *svc, int force) static int delete_callback(void *data, scf_walkinfo_t *wip) { - int force = (int)data; + int force = (int)(intptr_t)data; if (wip->inst != NULL) (void) lscf_instance_delete(wip->inst, force); @@ -12684,7 +12684,7 @@ lscf_delete(const char *fmri, int force) * Match FMRI to entity. */ if ((ret = scf_walk_fmri(g_hndl, 1, (char **)&fmri, SCF_WALK_SERVICE, - delete_callback, (void *)force, NULL, semerr)) != 0) { + delete_callback, (void *)(intptr_t)force, NULL, semerr)) != 0) { semerr(gettext("Failed to walk instances: %s\n"), scf_strerror(ret)); } diff --git a/usr/src/cmd/svc/svcs/Makefile b/usr/src/cmd/svc/svcs/Makefile index aac8f82e0a..10cbbe2127 100644 --- a/usr/src/cmd/svc/svcs/Makefile +++ b/usr/src/cmd/svc/svcs/Makefile @@ -34,7 +34,7 @@ include ../../Makefile.cmd include ../../Makefile.ctf POFILE = $(PROG)_all.po -LDLIBS += -lcontract -lscf -luutil -lumem -lnvpair -lzonecfg -lproc +LDLIBS += -lcontract -lscf -luutil -lumem -lnvpair -lzonecfg -lsasl -lproc CPPFLAGS += -I ../common lint := LINTFLAGS = -mux diff --git a/usr/src/cmd/svc/svcs/explain.c b/usr/src/cmd/svc/svcs/explain.c index 331a5375fd..8a8dfa043b 100644 --- a/usr/src/cmd/svc/svcs/explain.c +++ b/usr/src/cmd/svc/svcs/explain.c @@ -196,6 +196,7 @@ static char *emsg_invalid_dep; extern scf_handle_t *h; extern char *g_zonename; +extern char *g_zonealias; /* ARGSUSED */ static int @@ -2017,6 +2018,9 @@ print_service(inst_t *svcp, int verbose) if (g_zonename != NULL) (void) printf(gettext(" Zone: %s\n"), g_zonename); + if (g_zonealias != NULL) + (void) printf(gettext(" Alias: %s\n"), g_zonealias); + stime = svcp->stime.tv_sec; tmp = localtime(&stime); diff --git a/usr/src/cmd/svc/svcs/svcs.c b/usr/src/cmd/svc/svcs/svcs.c index 87735a69c7..9beb62047c 100644 --- a/usr/src/cmd/svc/svcs/svcs.c +++ b/usr/src/cmd/svc/svcs/svcs.c @@ -60,6 +60,7 @@ #include <sys/ctfs.h> #include <sys/stat.h> +#include <sasl/saslutil.h> #include <assert.h> #include <errno.h> #include <fcntl.h> @@ -138,6 +139,9 @@ static int first_paragraph = 1; /* For -l mode. */ static char *common_name_buf; /* Sized for maximal length value. */ char *locale; /* Current locale. */ char *g_zonename; /* zone being operated upon */ +char *g_zonealias; /* alias for zone, if any */ +static char g_aliasdec[MAXPATHLEN / 4 * 3]; /* decoded zone alias buffer */ +static char g_aliasbuf[MAXPATHLEN]; /* base64 encoded zone alias buffer */ /* * Pathname storage for path generated from the fmri. @@ -244,7 +248,25 @@ ht_free(void) static void ht_init(void) { - assert(ht_buckets == NULL); + if (ht_buckets != NULL) { + /* + * If we already have a hash table (e.g., because we are + * processing multiple zones), destroy it before creating + * a new one. + */ + struct ht_elem *elem, *next; + int i; + + for (i = 0; i < ht_buckets_num; i++) { + for (elem = ht_buckets[i]; elem != NULL; elem = next) { + next = elem->next; + free((char *)elem->fmri); + free(elem); + } + } + + free(ht_buckets); + } ht_buckets_num = 8; ht_buckets = safe_malloc(sizeof (*ht_buckets) * ht_buckets_num); @@ -3684,6 +3706,24 @@ again: assert(opt_zone == NULL || zids == NULL); if (opt_zone == NULL) { + zone_status_t status; + + if (zone_getattr(zids[zent], ZONE_ATTR_STATUS, + &status, sizeof (status)) < 0 || + status != ZONE_IS_RUNNING) { + /* + * If this zone is not running or we cannot + * get its status, we do not want to attempt + * to bind an SCF handle to it, lest we + * accidentally interfere with a zone that + * is not yet running by looking up a door + * to its svc.configd (which could potentially + * block a mount with an EBUSY). + */ + zent++; + goto nextzone; + } + if (getzonenamebyid(zids[zent++], zonename, sizeof (zonename)) < 0) { uu_warn(gettext("could not get name for " @@ -3706,18 +3746,46 @@ again: uu_die(gettext("invalid zone '%s'\n"), g_zonename); scf_value_destroy(zone); + + /* + * On SmartOS, there may be a base64-encoded string attribute + * named 'alias' associated with this zone. This alias is + * useful, so we attempt to make it available when we are + * displaying -xZ output. If it's not available or not + * decodable, we just ignore it. + */ + if (g_zonename != NULL) { + unsigned len; + struct zone_attrtab zattrs; + zone_dochandle_t zhdl = zonecfg_init_handle(); + + bzero(&zattrs, sizeof (zattrs)); + (void) strcpy(zattrs.zone_attr_name, "alias"); + + if (zhdl != NULL && + zonecfg_get_handle(g_zonename, zhdl) == Z_OK && + zonecfg_lookup_attr(zhdl, &zattrs) == Z_OK && + zonecfg_get_attr_string(&zattrs, g_aliasbuf, + sizeof (g_aliasbuf)) == Z_OK && + sasl_decode64(g_aliasbuf, strlen(g_aliasbuf), + g_aliasdec, sizeof (g_aliasdec), &len) == SASL_OK) { + g_aliasdec[len] = '\0'; + g_zonealias = g_aliasdec; + } else { + g_zonealias = NULL; + } + zonecfg_fini_handle(zhdl); + } } if (scf_handle_bind(h) == -1) { if (g_zonename != NULL) { - uu_warn(gettext("Could not bind to repository " + if (show_zones) + goto nextzone; + + uu_die(gettext("Could not bind to repository " "server for zone %s: %s\n"), g_zonename, scf_strerror(scf_error())); - - if (!show_zones) - return (UU_EXIT_FATAL); - - goto nextzone; } uu_die(gettext("Could not bind to repository server: %s. " @@ -3776,7 +3844,7 @@ again: if (opt_mode == 'L') { if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE, - print_log, NULL, &exit_status, uu_warn)) != 0) { + print_log, NULL, errarg, errfunc)) != 0) { uu_warn(gettext("failed to iterate over " "instances: %s\n"), scf_strerror(err)); exit_status = UU_EXIT_FATAL; |