# The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License") # (see http://www.opensource.org/licenses/CDDL-1.0). # # Copyright (c) 2011, 2012 Jens Elkner. All rights reserved. # Use is subject to license terms. # requires . ${LIBNAME%/*}/man.kshlib ############################################################################ # Base utils ############################################################################ Man.addVar OLDENV 'Contains all environment variables and their values at the time when this script was called. See also \bLASTENV\b and \blistVars()\b. Used to track environment variable changes.' typeset -r OLDENV=${ typeset -p | egrep '^(typeset |[a-zA-Z0-9_]+=)' | \ egrep -v '(^(SDIR|option)| (OPTIND|OLDENV|RANDOM|LINENO|LS_COLORS|_))='; } Man.addVar LASTENV 'A snapshot of environment variables. Gets updated with each \blistVars()\b call. See also \bOLDENV\b.' typeset -t LASTENV="$OLDENV" Man.addFunc listVars '' '[+NAME?listVars - list environment variable changes.] [+DESCRIPTION?List all environment variable changes since the last call of this function, if a non-empty argument is given. With no argument it lists all changes since sourcing this script. Basically useful for troubleshooting and development.]\n\n[arg]\n\n[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage OLDENV LASTENV; }" '}' function listVars { typeset K L LAST CHANGED VAR LINE K=${ typeset -p | egrep '^(typeset |[a-zA-Z0-9_]+=)' | \ egrep -v '(^(option|X)| (K|OLDENV|OPTIND|LASTENV|RANDOM|LINENO|LS_COLORS|_))='; } if [[ -z $1 ]]; then L="$OLDENV" else L="$LASTENV" fi CHANGED="" LAST="" print "$L\n$K\n" | sort | uniq -u | while read LINE ; do VAR=${LINE%%=*} VAR=${VAR##* } if [[ $LAST != $VAR && -n $VAR ]]; then CHANGED+="|$VAR" LAST=$VAR fi done [[ -z $CHANGED ]] && return print '===================================================================' print "= VARS $1" print '===================================================================' print "$K\n" | egrep "(^| )(${CHANGED:1})=" print '===================================================================' LASTENV="$K" } Man.addFunc getprop '' '[+NAME?getprop - convenience function to retrieve a SMF property from the current zone.] [+DESCRIPTION?Convinience function for "svcprop -p PROPERTY SMF_FMRI", but returns instead an empty string if the property does not exist or its value is "".] \n\n\aproperty\a \aSMF_FMRI\a\n\n [+ENVIRONMENT VARIABLES?]{ [+PROPVAL?Gets set to the value returned by this function.] }' function getprop { [[ -z $1 || -z $2 ]] && Log.warn "getprop(): invalid arguments" && return svcprop -q -p $1 $2 if (( $? == 0 )) ; then PROPVAL=${ svcprop -p $1 $2; } [[ $PROPVAL == '""' ]] && return fi print -- "$PROPVAL" } Man.addFunc getNetmaskBits '' '[+NAME?getNetmaskBits - get the number of significant netmask bits from a netmask.] [+DESCRIPTION?Determines the number of significant netmask bits from a netmask given in the traditional format N.N.N.N or CIDR notation /N. On success the number of bits gets printed, otherwise nothing.] \n\n{ \aN.N.N.N\a | \a/N\a }\n\n' function getNetmaskBits { typeset X NM [[ -z $1 ]] && return NM="$1" if [[ ${NM#/} != $NM ]]; then # CIDR X=${NM##/+([[:digit:]])} [[ -n $X ]] && return typeset -ui B=${.sh.match[1]} (( B > 32 )) && return print $B return fi X=${NM/+([[:digit:]]).+([[:digit:]]).+([[:digit:]]).+([[:digit:]])} [[ -n $X ]] && return typeset -ui A=${.sh.match[1]} B=${.sh.match[2]} C=${.sh.match[3]} \ D=${.sh.match[4]} (( A > 255 || B > 255 || C > 255 || D > 255 )) && return typeset -ui2 N=$(( A<<24 | B<<16 | C<<8 |D )) X=${N#*#} B=${X##+(1)} print ${#.sh.match} } Man.addFunc getNetmaskBitsByIP '' '[+NAME?getNetmaskBitsByIP - deduce netmask bits from an IP address.] [+DESCRIPTION?Deduces the default netmask from the given IP address (arg1). Prints out nothing if the argument is not an IP address, otherwise 8 for class A, 16 for class B and 24 for all other IP adresses.] \n\n\aN.N.N.N\a\n\n' function getNetmaskBitsByIP { [[ -z $1 ]] && return NM="$1" X=${NM##+([[:digit:]]).+([[:digit:]]).+([[:digit:]]).+([[:digit:]])} [[ -n $X ]] && return typeset -ui A=${.sh.match[1]} (( A < 127 )) && print 8 && return (( A < 192 )) && print 16 && return print 24 } Man.addFunc matchIP '' '[+NAME?matchIP - check, whether an IP belongs to a given network] [+DESCRIPTION?Checks, whether the given IP address (arg2) belongs to the given network (arg2). The network can be denoted as N.N.N.N/MASK whereby MASK might be a single number (CIDR notation) or classic netmask of the form M.M.M.M. If the given IP address belongs to the given network, this function prints out the IP address. Otherwise nothing gets printed.] \n\n\anetwork\a \aip\a\n\n [+SEE ALSO?\bnetmasks\b(4)]' function matchIP { typeset XBITS XIP X X1 [[ -z $1 || -z $2 ]] && return XBITS=$(( 32 - ${1#*/} )) XIP=${1%/*} X=${XIP/+([[:digit:]]).+([[:digit:]]).+([[:digit:]]).+([[:digit:]])} [[ -n $X ]] && return typeset -ui A=${.sh.match[1]} B=${.sh.match[2]} C=${.sh.match[3]} \ D=${.sh.match[4]} (( A > 255 || B > 255 || C > 255 || D > 255 )) && return typeset -ui16 XNET=$(( A<<24 | B<<16 | C<<8 | D )) X1=${2/+([[:digit:]]).+([[:digit:]]).+([[:digit:]]).+([[:digit:]])} [[ -n $X1 ]] && return A=${.sh.match[1]} B=${.sh.match[2]} C=${.sh.match[3]} D=${.sh.match[4]} (( A > 255 || B > 255 || C > 255 || D > 255 )) && return typeset -ui16 XDIP=$(( A<<24 | B<<16 | C<<8 | D )) (( XNET>>=XBITS )) (( XDIP>>=XBITS )) (( XNET == XDIP )) && print $2 } Man.addVar DEFAULTIF 'Default NIC of the global zone. Only set, if \bgetDefaultIfAndMac\b() gets called.' Man.addVar MAC 'Default Mac address to use for primary VNIC of the zone. Only set, if \bgetDefaultIfAndMac\b() gets called.' Man.addVar MAC_PREFIX 'The first 3 bytes to use, when a new MAC address needs to be generated. Default: "00:01:01" - PRIVATE according to http://standards.ieee.org/regauth/oui/oui.txt . See \bgetDefaultIfAndMac()\b.' [[ -z $MAC_PREFIX ]] && typeset MAC_PREFIX='00:01:01' Man.addFunc getDefaultIfAndMac '' '[+NAME?getDefaultIfAndMac - determine the default network interface and MAC address.] [+DESCRIPTION?Tries to find out the default network interface to use for new VNICs, which is the one containing the default IP route for this zone. On success \bDEFAULTIF\b gets set to this value and the default MAC address to use (\bMAC\b) gets set. Otherwise \bMAC\b stays unset and \bDEFAULTIF\b gets set to the interface {\auname -n\a}0 (which might be wrong).] [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage DEFAULTIF MAC_PREFIX MAC; }" '} [+SEE ALSO?\broute\b(1M), \bipadm\b(1M), \bdladm\b(1M), \bhostname\b(1)]' function getDefaultIfAndMac { typeset X IP MAC="" DEFAULTIF=$(route get default | awk '/interface:/ { print $2 }') if [[ -n $DEFAULTIF ]]; then IP=${ ipadm show-addr -p -o addr $DEFAULTIF/ 2>/dev/null; } IP=${IP%/*} if [[ -n $IP ]]; then X=${IP%.*} typeset -i16 H3=${X##*.} typeset -i16 H4=${IP##*.} typeset -i16 H5=${ zoneadm list -ip |wc -l; } H5=$((H5<<4)) MAC="${MAC_PREFIX}:${H3#*#}:${H4#*#}:${H5#*#}" fi else DEFAULTIF=${ hostname; }0 fi X=${ dladm show-link -p -o over $DEFAULTIF ; } [[ -n $X ]] && DEFAULTIF=$X } Man.addFunc getIP '' '[+NAME?getIP - get the IP address for the given hostname.] [+DESCRIPTION?Tries to determine the IP address from the given hostname (arg1). If it matches N.N.N.N, where N is a digit, it gets print out as is. Otherwise the hosts database will be used to lookup the IP address. On success the IP adress found gets printed, otherwise nothing.] \n\n\ahostname\a\n\n [+SEE ALSO?\bgetent\b(1M)]' function getIP { [[ -z $1 ]] && return typeset X=$1 T T=${X##+([[:digit:]]).+([[:digit:]]).+([[:digit:]]).+([[:digit:]])} [[ -z $T ]] && print $X && return X=${ getent hosts $X; } [[ -n $X ]] && print ${X%%+(\s)*} } Man.addFunc normalizeMac '' '[+NAME?normalizeMac - normalize a MAC address.] [+DESCRIPTION?Normalize the hex based MAC address \aaddr\a formatted as A:B:C:D:E:F to all lower case and padded with "0" (e.g. 0a:0b:0c:0e:0f). Missing hex digets are substituted by "0", however all colon separators need to be there. On success the normalized MAC address gets printed out, otherwise nothing.] [+RETURN VALUES?\b0\b on success (valid MAC address), \b1\b otherwise.] \n\n\aaddr\a ' function normalizeMac { typeset -l M="$1" [[ -z $M ]] && return 1 [[ ! $M == {0,2}([0-9a-f]):{0,2}([0-9a-f]):{0,2}([0-9a-f]):{0,2}([0-9a-f]):{0,2}([0-9a-f]):{0,2}([0-9a-f]) ]] && return 1 printf "%02x:%02x:%02x:%02x:%02x:%02x" \ 16#${.sh.match[1]:-0} 16#${.sh.match[2]:-0} 16#${.sh.match[3]:-0} \ 16#${.sh.match[4]:-0} 16#${.sh.match[5]:-0} 16#${.sh.match[6]:-0} } Man.addFunc capture '' '[+NAME?capture - capture lines from stdin via coprocess.] [+DESCRIPTION?Reads all lines from filedescriptor \b6\b until it gets closed by appending them to an internal variable to avoid blocking, and finally prints its content to fildescriptor \b5\b before exiting. One should always call \bprintCaptured()\b before calling \bcapture()\b again to avoid fd leaking and unexpected results!] [+?If the name of a function \afname\a is given, the \afn\a "$LINE" will be used to format each line read via stdin before appending it to the internal variable.] [+EXAMPLES]{ [+capture "Log.warn" ] [+run_command arg ... 2>&6 | while read X ; do] [+...] [+done] [+printCaptured] } [+SEE ALSO] [+?\bprintCaptured()\b] \n\n[\afname\a] ' function capture { ( integer FN=0 if [[ -n $1 ]]; then typeset FUNC="$1" FN=1 fi typeset ERR='' X while read X ; do (( FN )) && ERR+="${ ${FUNC} "$X" 2>&1; }\n" || ERR+="$X\n" done [[ -n ${ERR} ]] && print -n "$ERR" ) |& exec 5<&p # move stdout of coprocess to fd 5 exec 6>&p # move stdin of coprocess to fd 6 } Man.addFunc printCaptured '' '[+NAME?printCaptured - print out captured input.] [+DESCRIPTION?The counter part to \bcapture()\b. It closes filedescriptor \b6\b so that capturing ends and the content gets printed out to fildescriptor \b5\b. This function in turn reads them in and prints them out to the filedescriptor \afd\a if given, otherwise to stderr. Last but not least fildescriptor \b5\b will be closed.] [+SEE ALSO] [+?\bcapture()\b] \n\n[\afd\a] ' function printCaptured { typeset X FD=${1:-2} exec 6>&- # close stdin of coprocess to let it finish/print warnings while read X; do # read warnings and print them out print -u${FD} "$X" done <&5 exec 5>&- # close stdout of coprocess } Man.addFunc getIndex '' '[+NAME?getIndex - get the index of a string in an array] [+DESCRIPTION?Lookup the given \astring\a in the array with the name \avarray\a. The return value is \b-1\b if not found, the index of the string in the array otherwise.] \n\n\avarray\a \astring\a ' function getIndex { [[ -z $1 || -z $2 ]] && return -1 typeset -n ARRAY=$1 integer I for (( I=${#ARRAY[@]} - 1; I >= 0; I-- )) ; do [[ ${ARRAY[I]} == "$2" ]] && return $I done return -1 } # vim:ts=4 filetype=sh