17879db76SDevin Teske#!/bin/sh 27879db76SDevin Teske#- 37879db76SDevin Teske# Copyright (c) 2016 Devin Teske 47879db76SDevin Teske# All rights reserved. 57879db76SDevin Teske# 67879db76SDevin Teske# Redistribution and use in source and binary forms, with or without 77879db76SDevin Teske# modification, are permitted provided that the following conditions 87879db76SDevin Teske# are met: 97879db76SDevin Teske# 1. Redistributions of source code must retain the above copyright 107879db76SDevin Teske# notice, this list of conditions and the following disclaimer. 117879db76SDevin Teske# 2. Redistributions in binary form must reproduce the above copyright 127879db76SDevin Teske# notice, this list of conditions and the following disclaimer in the 137879db76SDevin Teske# documentation and/or other materials provided with the distribution. 147879db76SDevin Teske# 157879db76SDevin Teske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 167879db76SDevin Teske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 177879db76SDevin Teske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 187879db76SDevin Teske# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 197879db76SDevin Teske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 207879db76SDevin Teske# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 217879db76SDevin Teske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 227879db76SDevin Teske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 237879db76SDevin Teske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 247879db76SDevin Teske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 257879db76SDevin Teske# SUCH DAMAGE. 267879db76SDevin Teske# 277879db76SDevin Teske# $FreeBSD$ 287879db76SDevin Teske# 297879db76SDevin Teske############################################################ IDENT(1) 307879db76SDevin Teske# 317879db76SDevin Teske# $Title: netgraph(4) management script for vnet jails $ 327879db76SDevin Teske# 337879db76SDevin Teske############################################################ INFORMATION 347879db76SDevin Teske# 357879db76SDevin Teske# Use this tool with jail.conf(5) (or rc.conf(5) ``legacy'' configuration) to 36626ddc51SDevin Teske# manage `vnet' interfaces for jails. Designed to automate the creation of vnet 37626ddc51SDevin Teske# interface(s) during jail `prestart' and destroy said interface(s) during jail 38626ddc51SDevin Teske# `poststop'. 39626ddc51SDevin Teske# 40626ddc51SDevin Teske# In jail.conf(5) format: 417879db76SDevin Teske# 427879db76SDevin Teske# ### BEGIN EXCERPT ### 437879db76SDevin Teske# 447879db76SDevin Teske# xxx { 457879db76SDevin Teske# host.hostname = "xxx.yyy"; 467879db76SDevin Teske# path = "/vm/xxx"; 477879db76SDevin Teske# 487879db76SDevin Teske# # 497879db76SDevin Teske# # NB: Below 2-lines required 507879db76SDevin Teske# # NB: The number of ngN_xxx interfaces should match the number of 517879db76SDevin Teske# # arguments given to `jng bridge xxx' in exec.prestart value. 527879db76SDevin Teske# # 537879db76SDevin Teske# vnet; 547879db76SDevin Teske# vnet.interface = "ng0_xxx ng1_xxx ..."; 557879db76SDevin Teske# 567879db76SDevin Teske# exec.clean; 577879db76SDevin Teske# exec.system_user = "root"; 587879db76SDevin Teske# exec.jail_user = "root"; 597879db76SDevin Teske# 607879db76SDevin Teske# # 617879db76SDevin Teske# # NB: Below 2-lines required 627879db76SDevin Teske# # NB: The number of arguments after `jng bridge xxx' should match 637879db76SDevin Teske# # the number of ngN_xxx arguments in vnet.interface value. 647879db76SDevin Teske# # 657879db76SDevin Teske# exec.prestart += "jng bridge xxx em0 em1 ..."; 667879db76SDevin Teske# exec.poststop += "jng shutdown xxx"; 677879db76SDevin Teske# 687879db76SDevin Teske# # Standard recipe 697879db76SDevin Teske# exec.start += "/bin/sh /etc/rc"; 707879db76SDevin Teske# exec.stop = "/bin/sh /etc/rc.shutdown"; 717879db76SDevin Teske# exec.consolelog = "/var/log/jail_xxx_console.log"; 727879db76SDevin Teske# mount.devfs; 737879db76SDevin Teske# 747879db76SDevin Teske# # Optional (default off) 757879db76SDevin Teske# #allow.mount; 767879db76SDevin Teske# #allow.set_hostname = 1; 777879db76SDevin Teske# #allow.sysvipc = 1; 787879db76SDevin Teske# #devfs_ruleset = "11"; # rule to unhide bpf for DHCP 797879db76SDevin Teske# } 807879db76SDevin Teske# 817879db76SDevin Teske# ### END EXCERPT ### 827879db76SDevin Teske# 837879db76SDevin Teske# In rc.conf(5) ``legacy'' format (used when /etc/jail.conf does not exist): 847879db76SDevin Teske# 857879db76SDevin Teske# ### BEGIN EXCERPT ### 867879db76SDevin Teske# 877879db76SDevin Teske# jail_enable="YES" 887879db76SDevin Teske# jail_list="xxx" 897879db76SDevin Teske# 907879db76SDevin Teske# # 917879db76SDevin Teske# # Global presets for all jails 927879db76SDevin Teske# # 937879db76SDevin Teske# jail_devfs_enable="YES" # mount devfs 947879db76SDevin Teske# 957879db76SDevin Teske# # 967879db76SDevin Teske# # Global options (default off) 977879db76SDevin Teske# # 987879db76SDevin Teske# #jail_mount_enable="YES" # mount /etc/fstab.{name} 997879db76SDevin Teske# #jail_set_hostname_allow="YES" # Allow hostname to change 1007879db76SDevin Teske# #jail_sysvipc_allow="YES" # Allow SysV Interprocess Comm. 1017879db76SDevin Teske# 1027879db76SDevin Teske# # xxx 1037879db76SDevin Teske# jail_xxx_hostname="xxx.shxd.cx" # hostname 1047879db76SDevin Teske# jail_xxx_rootdir="/vm/xxx" # root directory 1057879db76SDevin Teske# jail_xxx_vnet_interfaces="ng0_xxx ng1xxx ..." # vnet interface(s) 1067879db76SDevin Teske# jail_xxx_exec_prestart0="jng bridge xxx em0 em1 ..." # bridge interface(s) 1077879db76SDevin Teske# jail_xxx_exec_poststop0="jng shutdown xxx" # destroy interface(s) 1087879db76SDevin Teske# #jail_xxx_mount_enable="YES" # mount /etc/fstab.xxx 1097879db76SDevin Teske# #jail_xxx_devfs_ruleset="11" # rule to unhide bpf for DHCP 1107879db76SDevin Teske# 1117879db76SDevin Teske# ### END EXCERPT ### 1127879db76SDevin Teske# 1137879db76SDevin Teske# Note that the legacy rc.conf(5) format is converted to 1147879db76SDevin Teske# /var/run/jail.{name}.conf by /etc/rc.d/jail if jail.conf(5) is missing. 1157879db76SDevin Teske# 1167879db76SDevin Teske# ASIDE: dhclient(8) inside a vnet jail... 1177879db76SDevin Teske# 1187879db76SDevin Teske# To allow dhclient(8) to work inside a vnet jail, make sure the following 1197879db76SDevin Teske# appears in /etc/devfs.rules (which should be created if it doesn't exist): 1207879db76SDevin Teske# 1217879db76SDevin Teske# [devfsrules_jail=11] 1227879db76SDevin Teske# add include $devfsrules_hide_all 1237879db76SDevin Teske# add include $devfsrules_unhide_basic 1247879db76SDevin Teske# add include $devfsrules_unhide_login 1257879db76SDevin Teske# add include $devfsrules_unhide_bpf 1267879db76SDevin Teske# 1277879db76SDevin Teske# And set ether devfs.ruleset="11" (jail.conf(5)) or 1287879db76SDevin Teske# jail_{name}_devfs_ruleset="11" (rc.conf(5)). 1297879db76SDevin Teske# 1307879db76SDevin Teske# NB: While this tool can't create every type of desirable topology, it should 1317879db76SDevin Teske# handle most setups, minus some which considered exotic or purpose-built. 1327879db76SDevin Teske# 1337879db76SDevin Teske############################################################ GLOBALS 1347879db76SDevin Teske 1357879db76SDevin Teskepgm="${0##*/}" # Program basename 1367879db76SDevin Teske 1377879db76SDevin Teske# 1387879db76SDevin Teske# Global exit status 1397879db76SDevin Teske# 1407879db76SDevin TeskeSUCCESS=0 1417879db76SDevin TeskeFAILURE=1 1427879db76SDevin Teske 1437879db76SDevin Teske############################################################ FUNCTIONS 1447879db76SDevin Teske 1457879db76SDevin Teskeusage() 1467879db76SDevin Teske{ 1477879db76SDevin Teske local action usage descr 1487879db76SDevin Teske exec >&2 1497879db76SDevin Teske echo "Usage: $pgm action [arguments]" 1507879db76SDevin Teske echo "Actions:" 1517879db76SDevin Teske for action in \ 1527879db76SDevin Teske bridge \ 1537879db76SDevin Teske graph \ 1547879db76SDevin Teske show \ 1557879db76SDevin Teske show1 \ 1567879db76SDevin Teske shutdown \ 1577879db76SDevin Teske ; do 1587879db76SDevin Teske eval usage=\"\$jng_${action}_usage\" 1597879db76SDevin Teske [ "$usage" ] || continue 1607879db76SDevin Teske eval descr=\"\$jng_${action}_descr\" 1617879db76SDevin Teske printf "\t%s\n\t\t%s\n" "$usage" "$descr" 1627879db76SDevin Teske done 1637879db76SDevin Teske exit $FAILURE 1647879db76SDevin Teske} 1657879db76SDevin Teske 1667879db76SDevin Teskeaction_usage() 1677879db76SDevin Teske{ 1687879db76SDevin Teske local usage action="$1" 1697879db76SDevin Teske eval usage=\"\$jng_${action}_usage\" 1707879db76SDevin Teske echo "Usage: $pgm $usage" >&2 1717879db76SDevin Teske exit $FAILURE 1727879db76SDevin Teske} 1737879db76SDevin Teske 1747879db76SDevin Teskemustberoot_to_continue() 1757879db76SDevin Teske{ 1767879db76SDevin Teske if [ "$( id -u )" -ne 0 ]; then 1777879db76SDevin Teske echo "Must run as root!" >&2 1787879db76SDevin Teske exit $FAILURE 1797879db76SDevin Teske fi 1807879db76SDevin Teske} 1817879db76SDevin Teske 1827879db76SDevin Teskejng_bridge_usage="bridge [-b BRIDGE_NAME] NAME interface0 [interface1 ...]" 1837879db76SDevin Teskejng_bridge_descr="Create ng0_NAME [ng1_NAME ...]" 1847879db76SDevin Teskejng_bridge() 1857879db76SDevin Teske{ 1867879db76SDevin Teske local OPTIND=1 OPTARG flag bridge=bridge 1877879db76SDevin Teske while getopts b: flag; do 1887879db76SDevin Teske case "$flag" in 1897879db76SDevin Teske b) bridge="$OPTARG" 1907879db76SDevin Teske [ "$bridge" ] || action_usage bridge ;; # NOTREACHED 1917879db76SDevin Teske *) action_usage bridge # NOTREACHED 1927879db76SDevin Teske esac 1937879db76SDevin Teske done 1947879db76SDevin Teske shift $(( $OPTIND - 1 )) 1957879db76SDevin Teske 1967879db76SDevin Teske local name="$1" 1977879db76SDevin Teske [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -gt 1 ] || 1987879db76SDevin Teske action_usage bridge # NOTREACHED 1997879db76SDevin Teske shift 1 # name 2007879db76SDevin Teske 2017879db76SDevin Teske mustberoot_to_continue 2027879db76SDevin Teske 2037879db76SDevin Teske local iface iface_devid eiface eiface_devid 2047879db76SDevin Teske local new num quad i=0 2057879db76SDevin Teske for iface in $*; do 2067879db76SDevin Teske 2077879db76SDevin Teske # 0. Make sure the interface doesn't exist already 2087879db76SDevin Teske eiface=ng${i}_$name 2097879db76SDevin Teske ngctl msg "$eiface:" getifname > /dev/null 2>&1 && continue 2107879db76SDevin Teske 2117879db76SDevin Teske # 1. Bring the interface up 2127879db76SDevin Teske ifconfig $iface up || return 2137879db76SDevin Teske 2147879db76SDevin Teske # 2. Set promiscuous mode and don't overwrite src addr 2157879db76SDevin Teske ngctl msg $iface: setpromisc 1 || return 2167879db76SDevin Teske ngctl msg $iface: setautosrc 0 || return 2177879db76SDevin Teske 2187879db76SDevin Teske # 3. Make sure the interface has been bridged 2197879db76SDevin Teske if ! ngctl info ${iface}bridge: > /dev/null 2>&1; then 2207879db76SDevin Teske ngctl mkpeer $iface: bridge lower link0 || return 2217879db76SDevin Teske ngctl connect $iface: $iface:lower upper link1 || 2227879db76SDevin Teske return 2237879db76SDevin Teske ngctl name $iface:lower ${iface}bridge || return 2247879db76SDevin Teske fi 2257879db76SDevin Teske 2267879db76SDevin Teske # 3.5. Optionally create a secondary bridge 2277879db76SDevin Teske if [ "$bridge" != "bridge" ] && 2287879db76SDevin Teske ! ngctl info "$iface$bridge:" > /dev/null 2>&1 2297879db76SDevin Teske then 2307879db76SDevin Teske num=2 2317879db76SDevin Teske while ngctl msg ${iface}bridge: getstats $num \ 2327879db76SDevin Teske > /dev/null 2>&1 2337879db76SDevin Teske do 2347879db76SDevin Teske num=$(( $num + 1 )) 2357879db76SDevin Teske done 2367879db76SDevin Teske ngctl mkpeer $iface:lower bridge link$num link1 || 2377879db76SDevin Teske return 2387879db76SDevin Teske ngctl name ${iface}bridge:link$num "$iface$bridge" || 2397879db76SDevin Teske return 2407879db76SDevin Teske fi 2417879db76SDevin Teske 2427879db76SDevin Teske # 4. Create a new interface to the bridge 2437879db76SDevin Teske num=2 2447879db76SDevin Teske while ngctl msg "$iface$bridge:" getstats $num > /dev/null 2>&1 2457879db76SDevin Teske do 2467879db76SDevin Teske num=$(( $num + 1 )) 2477879db76SDevin Teske done 2487879db76SDevin Teske ngctl mkpeer "$iface$bridge:" eiface link$num ether || return 2497879db76SDevin Teske 2507879db76SDevin Teske # 5. Rename the new interface 2517879db76SDevin Teske while [ ${#eiface} -gt 15 ]; do # OS limitation 2527879db76SDevin Teske eiface=${eiface%?} 2537879db76SDevin Teske done 2547879db76SDevin Teske new=$( set -- `ngctl show -n "$iface$bridge:link$num"` && 2557879db76SDevin Teske echo $2 ) || return 2567879db76SDevin Teske ngctl name "$iface$bridge:link$num" $eiface || return 2577879db76SDevin Teske ifconfig $new name $eiface || return 2587879db76SDevin Teske 2597879db76SDevin Teske # 2607879db76SDevin Teske # 6. Set the MAC address of the new interface using a sensible 2617879db76SDevin Teske # algorithm to prevent conflicts on the network. 2627879db76SDevin Teske # 2637879db76SDevin Teske # The formula I'm using is ``SP:SS:SI:II:II:II'' where: 2647879db76SDevin Teske # + S denotes 16 bits of sum(1) data, split because P (below). 2657879db76SDevin Teske # + P denotes the special nibble whose value, if one of 2667879db76SDevin Teske # 2, 6, A, or E (but usually 2) denotes a privately 2677879db76SDevin Teske # administered MAC address (while remaining routable). 2687879db76SDevin Teske # + I denotes bits that are inherited from parent interface. 2697879db76SDevin Teske # 2707879db76SDevin Teske # The S bits are a CRC-16 checksum of NAME, allowing the jail 2717879db76SDevin Teske # to change link numbers in ng_bridge(4) without affecting the 2727879db76SDevin Teske # MAC address. Meanwhile, if the jail NAME changes (e.g., it 2737879db76SDevin Teske # was duplicated and given a new name with no other changes), 2747879db76SDevin Teske # the underlying network interface changes, or the jail is 2757879db76SDevin Teske # moved to another host, the MAC address will be recalculated 2767879db76SDevin Teske # to a new, similarly unique value preventing conflict. 2777879db76SDevin Teske # 2787879db76SDevin Teske iface_devid=$( ifconfig $iface ether | awk '/ether/,$0=$2' ) 2797879db76SDevin Teske eiface_devid=${iface_devid#??:??:?} 2807879db76SDevin Teske num=$( set -- `echo -n $name | sum` && echo $1 ) 2817879db76SDevin Teske quad=$(( $num & 15 )) 2827879db76SDevin Teske case "$quad" in 2837879db76SDevin Teske 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 2847879db76SDevin Teske 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; 2857879db76SDevin Teske esac 2867879db76SDevin Teske eiface_devid=:$quad$eiface_devid 2877879db76SDevin Teske num=$(( $num >> 4 )) 2887879db76SDevin Teske quad=$(( $num & 15 )) 2897879db76SDevin Teske case "$quad" in 2907879db76SDevin Teske 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 2917879db76SDevin Teske 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; 2927879db76SDevin Teske esac 2937879db76SDevin Teske eiface_devid=$quad$eiface_devid 2947879db76SDevin Teske num=$(( $num >> 4 )) 2957879db76SDevin Teske quad=$(( $num & 15 )) 2967879db76SDevin Teske case "$quad" in 2977879db76SDevin Teske 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 2987879db76SDevin Teske 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; 2997879db76SDevin Teske esac 300*43a45064SDevin Teske case "$iface_devid" in 301*43a45064SDevin Teske ?2:*) eiface_devid=a:$quad$eiface_devid ;; 302*43a45064SDevin Teske *) eiface_devid=2:$quad$eiface_devid 303*43a45064SDevin Teske esac 3047879db76SDevin Teske num=$(( $num >> 4 )) 3057879db76SDevin Teske quad=$(( $num & 15 )) 3067879db76SDevin Teske case "$quad" in 3077879db76SDevin Teske 10) quad=a ;; 11) quad=b ;; 12) quad=c ;; 3087879db76SDevin Teske 13) quad=d ;; 14) quad=e ;; 15) quad=f ;; 3097879db76SDevin Teske esac 3107879db76SDevin Teske eiface_devid=$quad$eiface_devid 3117879db76SDevin Teske ifconfig $eiface ether $eiface_devid > /dev/null 2>&1 3127879db76SDevin Teske 3137879db76SDevin Teske i=$(( $i + 1 )) # on to next ng{i}_name 3147879db76SDevin Teske done # for iface 3157879db76SDevin Teske} 3167879db76SDevin Teske 3177879db76SDevin Teskejng_graph_usage="graph [-f] [-T type] [-o output]" 3187879db76SDevin Teskejng_graph_descr="Generate network graph (default output is \`jng.svg')" 3197879db76SDevin Teskejng_graph() 3207879db76SDevin Teske{ 3217879db76SDevin Teske local OPTIND=1 OPTARG flag 3227879db76SDevin Teske local output=jng.svg output_type= force= 3237879db76SDevin Teske while getopts fo:T: flag; do 3247879db76SDevin Teske case "$flag" in 3257879db76SDevin Teske f) force=1 ;; 3267879db76SDevin Teske o) output="$OPTARG" ;; 3277879db76SDevin Teske T) output_type="$OPTARG" ;; 3287879db76SDevin Teske *) action_usage graph # NOTREACHED 3297879db76SDevin Teske esac 3307879db76SDevin Teske done 3317879db76SDevin Teske shift $(( $OPTIND - 1 )) 3327879db76SDevin Teske [ $# -eq 0 -a "$output" ] || action_usage graph # NOTREACHED 3337879db76SDevin Teske mustberoot_to_continue 3347879db76SDevin Teske if [ -e "$output" -a ! "$force" ]; then 3357879db76SDevin Teske echo "$output: Already exists (use \`-f' to overwrite)" >&2 3367879db76SDevin Teske return $FAILURE 3377879db76SDevin Teske fi 3387879db76SDevin Teske if [ ! "$output_type" ]; then 3397879db76SDevin Teske local valid suffix 3407879db76SDevin Teske valid=$( dot -Txxx 2>&1 ) 3417879db76SDevin Teske for suffix in ${valid##*:}; do 3427879db76SDevin Teske [ "$output" != "${output%.$suffix}" ] || continue 3437879db76SDevin Teske output_type=$suffix 3447879db76SDevin Teske break 3457879db76SDevin Teske done 3467879db76SDevin Teske fi 3477879db76SDevin Teske ngctl dot | dot ${output_type:+-T "$output_type"} -o "$output" 3487879db76SDevin Teske} 3497879db76SDevin Teske 3507879db76SDevin Teskejng_show_usage="show" 3517879db76SDevin Teskejng_show_descr="List possible NAME values for \`show NAME'" 3527879db76SDevin Teskejng_show1_usage="show NAME" 3537879db76SDevin Teskejng_show1_descr="Lists ng0_NAME [ng1_NAME ...]" 3547879db76SDevin Teskejng_show2_usage="show [NAME]" 3557879db76SDevin Teskejng_show() 3567879db76SDevin Teske{ 3577879db76SDevin Teske local OPTIND=1 OPTARG flag 3587879db76SDevin Teske while getopts "" flag; do 3597879db76SDevin Teske case "$flag" in 3607879db76SDevin Teske *) action_usage show2 # NOTREACHED 3617879db76SDevin Teske esac 3627879db76SDevin Teske done 3637879db76SDevin Teske shift $(( $OPTIND - 1 )) 3647879db76SDevin Teske mustberoot_to_continue 3657879db76SDevin Teske if [ $# -eq 0 ]; then 3667879db76SDevin Teske ngctl ls | awk '$4=="bridge",$0=$2' | 3677879db76SDevin Teske xargs -rn1 -Ibridge ngctl show bridge: | 3687879db76SDevin Teske awk 'sub(/^ng[[:digit:]]+_/, "", $2), $0 = $2' | 3697879db76SDevin Teske sort -u 3707879db76SDevin Teske return 3717879db76SDevin Teske fi 3727879db76SDevin Teske ngctl ls | awk -v name="$1" ' 3737879db76SDevin Teske match($2, /^ng[[:digit:]]+_/) && 3747879db76SDevin Teske substr($2, RSTART + RLENGTH) == name && 3757879db76SDevin Teske $4 == "eiface", $0 = $2 3767879db76SDevin Teske ' | sort 3777879db76SDevin Teske} 3787879db76SDevin Teske 3797879db76SDevin Teskejng_shutdown_usage="shutdown NAME" 3807879db76SDevin Teskejng_shutdown_descr="Shutdown ng0_NAME [ng1_NAME ...]" 3817879db76SDevin Teskejng_shutdown() 3827879db76SDevin Teske{ 3837879db76SDevin Teske local OPTIND=1 OPTARG flag 3847879db76SDevin Teske while getopts "" flag; do 3857879db76SDevin Teske case "$flag" in 3867879db76SDevin Teske *) action_usage shutdown # NOTREACHED 3877879db76SDevin Teske esac 3887879db76SDevin Teske done 3897879db76SDevin Teske shift $(( $OPTIND -1 )) 3907879db76SDevin Teske local name="$1" 3917879db76SDevin Teske [ "${name:-x}" = "${name#*[!0-9a-zA-Z_]}" -a $# -eq 1 ] || 3927879db76SDevin Teske action_usage shutdown # NOTREACHED 3937879db76SDevin Teske mustberoot_to_continue 3947879db76SDevin Teske jng_show "$name" | xargs -rn1 -I eiface ngctl shutdown eiface: 3957879db76SDevin Teske} 3967879db76SDevin Teske 3977879db76SDevin Teske############################################################ MAIN 3987879db76SDevin Teske 3997879db76SDevin Teske# 4007879db76SDevin Teske# Command-line arguments 4017879db76SDevin Teske# 4027879db76SDevin Teskeaction="$1" 4037879db76SDevin Teske[ "$action" ] || usage # NOTREACHED 4047879db76SDevin Teske 4057879db76SDevin Teske# 4067879db76SDevin Teske# Validate action argument 4077879db76SDevin Teske# 4087879db76SDevin Teskeif [ "$BASH_VERSION" ]; then 4097879db76SDevin Teske type="$( type -t "jng_$action" )" || usage # NOTREACHED 4107879db76SDevin Teskeelse 4117879db76SDevin Teske type="$( type "jng_$action" 2> /dev/null )" || usage # NOTREACHED 4127879db76SDevin Teskefi 4137879db76SDevin Teskecase "$type" in 4147879db76SDevin Teske*function) 4157879db76SDevin Teske shift 1 # action 4167879db76SDevin Teske eval "jng_$action" \"\$@\" 4177879db76SDevin Teske ;; 4187879db76SDevin Teske*) usage # NOTREACHED 4197879db76SDevin Teskeesac 4207879db76SDevin Teske 4217879db76SDevin Teske################################################################################ 4227879db76SDevin Teske# END 4237879db76SDevin Teske################################################################################ 424