summaryrefslogtreecommitdiff
path: root/usr/src/cmd/svc/milestone/mdata-fetch
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/svc/milestone/mdata-fetch')
-rwxr-xr-xusr/src/cmd/svc/milestone/mdata-fetch477
1 files changed, 477 insertions, 0 deletions
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}