├── LICENSE ├── README.md ├── bmt └── bmt-rc /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, ATeamSystems 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BMT - Bhyve Management Tool 2 | 3 | This is a super lightweight yet very functional tool to manage Bhyve VMs on FreeBSD. It needs: 4 | 5 | * `/bin/sh` 6 | * A ZFS volume to place VMs into (UFS is not supported) 7 | * GNU screen (`pkg install screen`) 8 | * Grub2 Bhyve loader (`pkg install grub2-bhyve`), if you'll be running Linux VMs 9 | * BHyve UEFI Firmware, if you'll be running Windows VMs (`pkg install -y bhyve-firmware`) 10 | 11 | It supports most UNIX OSes and Windows, handles auto-booting VMs at system start and shutting them down at system shutdown/reboot. 12 | 13 | ## Initial Setup 14 | 15 | ### ZVol Location 16 | 17 | By default BMT will create a ZFS volume `zroot/vms` to house the VM configs and virtual disks, and mount the base in `/usr/local/vms`. 18 | 19 | If you wish to have this somewhere else (ie; if you wish to place it on a different zroot) create a file called `/usr/local/etc/bmt.conf`: 20 | 21 | ``` 22 | # Base ZFS Root 23 | BASE_ZPATH="zssd/vms" 24 | ``` 25 | 26 | Where `zssd` is your preferred ZPOOL, and `vms` is a standard dataset. 27 | 28 | ### Init 29 | 30 | The first time setting up BMT on a system (assumes bmt is installed into `/usr/local/bmt/`): 31 | 32 | ``` 33 | ln -s /usr/local/bmt/bmt.rc.sh /usr/local/etc/rc.d/bmt && 34 | sysrc bmt_enable="YES" && 35 | bmt rcstart && 36 | bmt setup 37 | ``` 38 | 39 | It is best to reboot after this just to be sure everything is applied. 40 | 41 | ## Usage 42 | 43 | You can run just `bmt` to get a list of options. This section needs to be expanded more but the following should get you started: 44 | 45 | ### List All VMs 46 | 47 | `bmt list` will show all VMs, their states, used memory, CPU and if they're set to come up on system boot. 48 | 49 | ### Network Map 50 | 51 | The `bmt netmap` command will attempt to map out VM's TAP devices an label them in an easy to understand way against each bridge. 52 | 53 | You can specify user-friendly labels for the bridges by adding the following format lines to `/usr/local/etc/bmt.conf`: 54 | 55 | ``` 56 | BRIDGE_bridge0_NAME="Private LAN" 57 | BRIDGE_bridge1_NAME="Public WAN" 58 | ``` 59 | 60 | ### New VM 61 | 62 | To create a new VM with a 16 GiB virtual disk: 63 | 64 | ``` 65 | bmt create newvmname -V 16G 66 | ``` 67 | 68 | ### Edit VM 69 | 70 | `bmt edit ` launches opens the appropriate vm.conf file in your preferred editor. 71 | 72 | ### Start/Stop 73 | 74 | To stop a VM: 75 | 76 | `bmt stop vmname` 77 | 78 | To start a VM: 79 | 80 | `bmt start vmname` 81 | 82 | (Where "vmname" is the name you gave it). 83 | 84 | ### Attach To Console 85 | 86 | To attach to the text console: 87 | 88 | `bmt attach vmname` 89 | 90 | ### Execute a console command 91 | 92 | `bmt exec ''` 93 | 94 | example: 95 | `bmt exec freebsd 'root'` 96 | `bmt exec freebsd 'pass'` 97 | `bmt exec freebsd 'uname'` 98 | 99 | ### Copy 100 | 101 | A copy creates a new free standing "thick" VM with no links to the source's volumes. 102 | 103 | To copy a vm: 104 | 105 | `bmt copy vmname new-vmname` 106 | 107 | Add `-d` to the end to destroy snapshots on the new VM to have an entirely clean copy with no snapshots. Otherwise any snapshots on the source VM will be copied (but not linked in any way to the source VM). 108 | 109 | ### Clone 110 | 111 | A clone uses ZFS snapshots to make a new "thin" VM linked to the source. 112 | 113 | To clone a vm: 114 | 115 | `bmt clone vmname new-vmname` 116 | 117 | ### Destroy 118 | 119 | To destroy a vm, it must be off: 120 | 121 | `bmt destroy vmname` 122 | 123 | ### Status 124 | 125 | To see the status of a vm: 126 | 127 | `bmt status vmname` 128 | 129 | ### Block until vm stops 130 | 131 | This command will block until a vm powers off: 132 | 133 | `bmt wait_for_poweroff vmname` 134 | 135 | ### Send/Receive 136 | 137 | The vm send/receive functionality works very similar to zfs. 138 | 139 | This example sends the vm to a compressed file: 140 | 141 | `bmt send vmname | xz > vmname.bmt.xz` 142 | 143 | This example receives a vm from a compressed file: 144 | 145 | `xzcat vmname.bmt.xz | bmt receive vmname` 146 | 147 | This example sends a vm between hosts: 148 | 149 | `bmt send vmname | ssh user@host bmt receive vmname` 150 | 151 | ### Get/Set 152 | 153 | You can get and set parts of the vm configuration with these commands: 154 | 155 | `bmt get vmname VM_CPUS` 156 | 157 | `bmt set vmname VM_CPUS=2` 158 | 159 | ## Networking 160 | 161 | By default with `AUTO_NETWORKING="YES"` set for a VM, a TAP device will automatically be created and assigned to bridge0 (for the example below). 162 | 163 | *NOTE:* It will not automatically create bridge0, you still need to set that up in `/etc/rc.conf` and `ifconfig` 164 | 165 | This auto-provisioning can be overridden via these config blocks: 166 | 167 | ``` 168 | # -- Networking 169 | # Up to 12 nics are possible following the same naming convention ) 170 | # 171 | AUTO_NETWORKING="NO" 172 | VM_N1_BRIDGE_NUM="0" 173 | VM_N1_TAP_NUM="21" 174 | 175 | VM_N2_BRIDGE_NUM="" 176 | VM_N2_TAP_NUM="" 177 | ``` 178 | 179 | *NOTE*: If you have PF enabled make sure to 'skip' any VM taps and bridges `/etc/pf.conf`: 180 | 181 | ``` 182 | set skip on bridge0 183 | set skip on tap21 184 | ``` 185 | 186 | Otherwise nothing will work. Obviously you can apply more granular control over this but this is a common issue when VM networking doesn't work. 187 | 188 | If you need to, you can use the new E1000 network driver instead of the virtio-net driver by setting: 189 | 190 | ``` 191 | VM_NET_DRIVER="e1000" 192 | ``` 193 | 194 | This has proven necessary under Debian 8.10 as there is a bug somewhere that causes packets larger than 230 bytes to be truncated using virtio-net. 195 | 196 | ## PCI Passthru 197 | 198 | PCI passthru requires the variable `VM_PCI_PT` to be set to the PCI address of the PCI device that you would like to passthru to the VM. For example: 199 | ``` 200 | root@donchor--> pciconf -l -v 201 | re1@pci0:5:0:0: class=0x020000 card=0x80011297 chip=0x816810ec rev=0x06 hdr=0x00 202 | vendor = 'Realtek Semiconductor Co., Ltd.' 203 | device = 'RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller' 204 | class = network 205 | subclass = ethernet 206 | ``` 207 | 208 | If I wanted to pass this network card through to a VM, I would use the PCI address 5/0/0 (derived from the string `re1@pci0:5:0:0` [last three digits, replace colons with slashes]). 209 | 210 | Now that we have the PCI address, things get easier. On the VM in question's configuration, add a line like this: 211 | ``` 212 | VM_PCI_PT="5/0/0" 213 | ``` 214 | 215 | Finally, you need to keep an aggregate list of devices that bhyve is using in `/boot/loader.conf`. In our example above, we use `5/0/0`, and another VM is using `6/0/0`. My aggregate entry in `/boot/loader.conf` would look like this: 216 | ``` 217 | pptdevs="5/0/0 6/0/0" 218 | ``` 219 | 220 | At boot time, bhyve will attach to these devices early before the kernel has a chance to decide on a driver for them. In the `pciconf` example above the string `re1@pci0:5:0:0` tells us that the kernel has already attached a driver (`re`) for the pci device. A reboot will be necessary so that bhvye can get between the kernel and driver, and shunt it into the VM once it starts. 221 | 222 | Thats it. After a reboot, your VM should have access to the PCI device! 223 | -------------------------------------------------------------------------------- /bmt: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # BMT - Bhyve Management Tool 5 | # 6 | # Created by Steven Douglas /w improvements by Adam Strohl 7 | # 8 | 9 | # Script Version 10 | VERSION="0.1.44" 11 | 12 | # What directory do the vms live in? 13 | VM_LIVES="/usr/local/vms" 14 | 15 | # Set to '-S' to wire memory (experimental) for PCI Pass through 16 | WIRE_SWITCH=""; 17 | 18 | # Where to start generating tap devices when AUTO_NETWORKING is used and no VM tap is specified manually 19 | TAP_START="20"; 20 | 21 | # How long to wait (in seconds) between VM starts on boot. I found there are clock issues with the guests if too many start at once so increasing this to 10-20 seconds fixes it 22 | RC_START_VM_DELAY="1"; 23 | 24 | # Sanity limit to auto-tap generation 25 | TAP_MAX="200"; 26 | 27 | # Base zpath hack 28 | BASE_ZPATH="zroot/vms" 29 | 30 | # Runtime stuff 31 | RUN_DIR="/var/run/bmt" 32 | 33 | # Load config file if present 34 | if [ -r "/usr/local/etc/bmt.conf" ]; then 35 | . /usr/local/etc/bmt.conf; 36 | fi 37 | 38 | # Setup a space to log what tap/bridges are allocated for netmap 39 | VM_STATE_DIR="${RUN_DIR}/vmstate" 40 | mkdir -p ${VM_STATE_DIR} > /dev/null; 41 | 42 | vml_start () { 43 | VM_STATE_FILE="${VM_STATE_DIR}/${VM_NAME}.state"; 44 | if [ "${ARG_BONUS}" == "-d" ]; then 45 | BMT_DEBUG="YES"; 46 | fi 47 | 48 | SCREEN_LOGGING_FLAGS=""; 49 | if [ -n "${VM_CONSOLE_LOG}" ]; then 50 | SCREEN_LOGGING_FLAGS="-L -Logfile ${VM_CONSOLE_LOG}"; 51 | fi 52 | 53 | # Screen 54 | if [ "$IN_SCREEN" != "YES" -a -z "${BMT_DEBUG}" ]; then 55 | if [ -z "${BMT_DEBUG}" ]; then 56 | `/usr/local/bin/screen ${SCREEN_LOGGING_FLAGS} -dmS bhyve."$VM_NAME" "$SCRIPT" start $VM_NAME is &` 57 | exit; 58 | else 59 | echo "Debugging mode enabled, not launching in screen"; 60 | `"$SCRIPT" start $VM_NAME is &`; 61 | exit; 62 | fi 63 | fi 64 | sys_log "${VM_NAME}: start command running"; 65 | 66 | # Reboot loop 67 | while [ 1 ]; do 68 | sys_log "${VM_NAME}: initializing"; 69 | mutex_lock BMTVMStart; 70 | echo -n > ${VM_STATE_FILE}; 71 | AUTO_NETWORKING_GEN_INTS="" # List of interfaces auto-created : 72 | 73 | # Execute the pre command 74 | if [ "${VM_PRE_CMD}" != "" ]; then 75 | `${VM_PRE_CMD}` 76 | fi 77 | 78 | # Destroy any existing VM's with my name 79 | bhyvectl --vm=$VM_NAME --destroy 2> /dev/null 80 | 81 | # Figure out the PCI devices 82 | FINAL_PCI="" 83 | I=2 84 | 85 | # Default to virtio-net 86 | if [ -z "${VM_NET_DRIVER}" ]; then 87 | VM_NET_DRIVER="virtio-net"; 88 | fi 89 | 90 | # Default to MBR 91 | if [ -z "${VM_BOOT_PART_TYPE}" ]; then 92 | VM_BOOT_PART_TYPE="msdos"; 93 | fi 94 | # Networking Devices 95 | for j in 1 2 3 4 5 6 7 8 9 10 11 12; do 96 | PREFIX_BRIDGE="VM_N${j}_BRIDGE_NUM" 97 | BRIDGE=`eval echo "\$""$PREFIX_BRIDGE"` 98 | PREFIX_TAP="VM_N${j}_TAP_NUM" 99 | TAP=`eval echo "\$""$PREFIX_TAP"` 100 | CUR_NIC_NO="${j}"; 101 | 102 | if [ "$AUTO_NETWORKING" == "YES" ]; then 103 | if [ -n "${BRIDGE}" ]; then 104 | if [ -z "${TAP}" ]; then 105 | # No tap specified, allocate a new one 106 | TAP=`get_next_free_tap` 107 | if [ -z "${TAP}" ]; then 108 | fatal_error "Couldn't allocate free tap (${TAP})" 109 | else 110 | echo "VM:${VM_NAME} NIC #${CUR_NIC_NO} (${VM_NET_DRIVER}) assigned tap${TAP} to bridge${BRIDGE}"; 111 | fi 112 | fi 113 | 114 | if [ -n "$TAP" ]; then 115 | # -- Create specified tap 116 | ifconfig tap${TAP} create; 117 | AUTO_NETWORKING_GEN_INTS="${AUTO_NETWORKING_GEN_INTS} ${TAP}:${BRIDGE}"; 118 | 119 | # -- Record what was allocated into the state file so it can be sourced later 120 | # for stuff like 'bmt netmap' 121 | # 122 | # This could probably be safely moved out of the 123 | # AUTO_NETWORKING==YES if and down to the FINAL_PCI area 124 | echo "VM_N${CUR_NIC_NO}_BRIDGE_NUM=${BRIDGE}" >> ${VM_STATE_FILE}; 125 | echo "VM_N${CUR_NIC_NO}_TAP_NUM=${TAP}" >> ${VM_STATE_FILE}; 126 | 127 | sys_log "${VM_NAME}: auto-creating tap${TAP} to bridge${BRIDGE} for NIC ${CUR_NIC_NO}"; 128 | 129 | CUR_NIC_NO=$((CUR_NIC_NO+1)) 130 | fi 131 | fi 132 | fi 133 | 134 | # -- Add to bridge 135 | if [ -n "$TAP" ]; then 136 | if [ -z "${BRIDGE}" ]; then 137 | fatal_error "tap${TAP} has no associated bridge, check VM config for VM_N${CUR_NIC_NO}_BRIDGE_NUM"; 138 | fi 139 | ifconfig bridge$BRIDGE addm tap$TAP 140 | FINAL_PCI=$FINAL_PCI" -s $I,${VM_NET_DRIVER},tap$TAP " 141 | I=$((I+1)) 142 | fi 143 | 144 | done 145 | 146 | # Scrape any zvols from our current dataset as disks 147 | echo "(hd0) /dev/zvol/$ZPATH/$VM_BOOT_ZVOL" > $SCRIPTPATH/device.map 148 | if [ "$VM_CDROM" == "1" ] ; then 149 | echo "(cd0) $VM_CDROM_MEDIA" >> $SCRIPTPATH/device.map 150 | FINAL_PCI=$FINAL_PCI" -s $I,ahci-cd,$VM_CDROM_MEDIA " 151 | I=$((I+1)) 152 | BOOT_DEVICE="cd0" 153 | else 154 | BOOT_DEVICE="hd0,$VM_BOOT_PART_TYPE"$VM_BOOT_PART 155 | fi 156 | for ds in `zfs list -r -H -t volume -o name $ZPATH` 157 | do 158 | FINAL_PCI=$FINAL_PCI" -s $I,ahci-hd,/dev/zvol/$ds " 159 | echo "Adding disk (zvol) $ds" 160 | I=$((I+1)) 161 | done 162 | 163 | # Get pci passthru devices 164 | for pci_dev in $VM_PCI_PT; do 165 | FINAL_PCI=$FINAL_PCI" -s $I,passthru,${pci_dev} " 166 | echo "Adding PCI PT ${pci_dev}" 167 | I=$((I+1)) 168 | done 169 | 170 | # Add file-based disks 171 | for ds in ${VM_DISK_FILES} 172 | do 173 | FINAL_PCI=$FINAL_PCI" -s $I,ahci-hd,$ds " 174 | echo "Adding disk (file) $ds" 175 | I=$((I+1)) 176 | done 177 | 178 | # Add Framebuffer 179 | if [ "$VM_FRAMEBUFFER" == "1" ] ; then 180 | FINAL_PCI=$FINAL_PCI" -s $I,fbuf,$VM_FRAMEBUFFER_OPTS " 181 | I=$((I+1)) 182 | fi 183 | 184 | # Add USB 3.0 185 | if [ "$VM_USB3" == "1" ] ; then 186 | FINAL_PCI=$FINAL_PCI" -s $I,xhci,tablet " 187 | I=$((I+1)) 188 | fi 189 | 190 | # Make sure that FreeBSD can see the boot vol's partitions 191 | gpart list zvol/${ZPATH}/${VM_BOOT_ZVOL} >> /tmp/caker 192 | 193 | # -- This is so auto-TAPs and other things don't run concurrently 194 | mutex_unlock BMTVMStart; 195 | sys_log "${VM_NAME}: starting"; 196 | 197 | # FreeBSD 198 | if [ "$VM_QUIRK" == "0" ]; then 199 | if [ "$VM_CDROM" == "1" ]; then 200 | bhyveload ${WIRE_SWITCH} -d $VM_CDROM_MEDIA -m $VM_MEMORY"M" $VM_NAME 201 | else 202 | bhyveload ${WIRE_SWITCH} -d "/dev/zvol/$ZPATH/$VM_BOOT_ZVOL" -m $VM_MEMORY"M" $VM_NAME 203 | fi 204 | # Quirk CentOS 6.x 205 | elif [ "$VM_QUIRK" == "1" ]; then 206 | if [ "$VM_CDROM" == "1" ]; then 207 | rm $SCRIPTPATH/grub.cfg 208 | echo "set root=(cd0)" >> $SCRIPTPATH/grub.cfg 209 | echo "linux (cd0)/isolinux/vmlinuz" >> $SCRIPTPATH/grub.cfg 210 | echo "initrd (cd0)/isolinux/initrd.img" >> $SCRIPTPATH/grub.cfg 211 | echo "boot" >> $SCRIPTPATH/grub.cfg 212 | /usr/local/sbin/grub-bhyve ${WIRE_SWITCH} --directory=$SCRIPTPATH -m $SCRIPTPATH/device.map -M $VM_MEMORY $VM_NAME 213 | 214 | else 215 | # Generate grub.cfg 216 | MOUNTPOINT="/tmp/$VM_NAME" 217 | if ! mkdir $MOUNTPOINT; then 218 | fatal_error "Couldn't create temporary mountpoint for grub.cfg generation (${MOUNTPOINT})"; 219 | fi 220 | 221 | if ! fuse-ext2 -o rw+ /dev/zvol/$ZPATH/$VM_BOOT_ZVOL"s"$VM_BOOT_PART $MOUNTPOINT; then 222 | rmdir "$MOUNTPOINT"; 223 | fatal_error "Couldn't mount boot partition with fuse"; 224 | fi 225 | 226 | if ! VMLINUZ_PATH=`find "$MOUNTPOINT/boot/" \( ! -name '.*' \) -type f -name "vmlinuz*" | sed -e "s,$MOUNTPOINT,,g" | sort | tail -n 1`; then 227 | umount "$MOUNTPOINT"; rmdir "$MOUNTPOINT"; 228 | fatal_error "Couldn't run find operation for vmlinuz file"; 229 | fi 230 | 231 | if [ -z "${VMLINUZ_PATH}" ]; then 232 | umount "$MOUNTPOINT"; rmdir "$MOUNTPOINT"; 233 | fatal_error "Couldn't find vmlinuz file in guest disk /boot"; 234 | fi 235 | 236 | if ! INITRAMFS_PATH=`find "$MOUNTPOINT/boot/" \( ! -name '.*' \) -type f -name "initramfs*" | sed -e "s,$MOUNTPOINT,,g " | sort | tail -n 1`; then 237 | umount "$MOUNTPOINT"; rmdir "$MOUNTPOINT"; 238 | fatal_error "Couldn't run find operation for initramfs file"; 239 | fi 240 | 241 | if [ -z "${INITRAMFS_PATH}" ]; then 242 | umount "$MOUNTPOINT"; rmdir "$MOUNTPOINT"; 243 | fatal_error "Couldn't find initramfs file in guest disk /boot"; 244 | fi 245 | 246 | umount "$MOUNTPOINT"; rmdir "$MOUNTPOINT"; 247 | 248 | rm $SCRIPTPATH/grub.cfg 249 | echo "set root=($BOOT_DEVICE)" >> $SCRIPTPATH/grub.cfg 250 | echo "linux (hd0,$VM_BOOT_PART_TYPE$VM_BOOT_PART)$VMLINUZ_PATH root=/dev/sda""$VM_BOOT_PART" >> $SCRIPTPATH/grub.cfg 251 | echo "initrd (hd0,$VM_BOOT_PART_TYPE$VM_BOOT_PART)$INITRAMFS_PATH" >> $SCRIPTPATH/grub.cfg 252 | echo "boot" >> $SCRIPTPATH/grub.cfg 253 | 254 | 255 | # Run Grub 256 | /usr/local/sbin/grub-bhyve ${WIRE_SWITCH} --directory=$SCRIPTPATH -m $SCRIPTPATH/device.map -M $VM_MEMORY $VM_NAME 257 | fi 258 | # Quirk CentOS 7.x 259 | elif [ "$VM_QUIRK" == "2" ]; then 260 | if [ "$VM_CDROM" == "1" ]; then 261 | rm $SCRIPTPATH/grub.cfg 262 | echo "set root=(cd0)" >> $SCRIPTPATH/grub.cfg 263 | echo "linux (cd0)/isolinux/vmlinuz" >> $SCRIPTPATH/grub.cfg 264 | echo "initrd (cd0)/isolinux/initrd.img" >> $SCRIPTPATH/grub.cfg 265 | echo "boot" >> $SCRIPTPATH/grub.cfg 266 | /usr/local/sbin/grub-bhyve ${WIRE_SWITCH} --directory=$SCRIPTPATH -m $SCRIPTPATH/device.map -M $VM_MEMORY $VM_NAME 267 | 268 | else 269 | if [ -z "${VM_GRUB_DIR}" ]; then 270 | VM_GRUB_DIR="/boot/grub2"; 271 | fi 272 | /usr/local/sbin/grub-bhyve ${WIRE_SWITCH} -d ${VM_GRUB_DIR} -r $BOOT_DEVICE -m $SCRIPTPATH/device.map -M $VM_MEMORY $VM_NAME 273 | fi 274 | 275 | # Quirk Ubuntu 276 | elif [ "$VM_QUIRK" == "3" ]; then 277 | if [ -z "${VM_GRUB_DIR}" ]; then 278 | VM_GRUB_DIR="/boot/grub"; 279 | fi 280 | /usr/local/sbin/grub-bhyve ${WIRE_SWITCH} -d ${VM_GRUB_DIR} -r $BOOT_DEVICE -m $SCRIPTPATH/device.map -M $VM_MEMORY $VM_NAME 281 | 282 | # Quirk Fedora 283 | elif [ "$VM_QUIRK" == "4" ]; then 284 | if [ "$VM_CDROM" == "1" ]; then 285 | /usr/local/sbin/grub-bhyve ${WIRE_SWITCH} -d /isolinux/ -r $BOOT_DEVICE -m $SCRIPTPATH/device.map -M $VM_MEMORY $VM_NAME 286 | else 287 | if [ -z "${VM_GRUB_DIR}" ]; then 288 | VM_GRUB_DIR="/grub2"; 289 | fi 290 | /usr/local/sbin/grub-bhyve ${WIRE_SWITCH} -d ${VM_GRUB_DIR} -r $BOOT_DEVICE -m $SCRIPTPATH/device.map -M $VM_MEMORY $VM_NAME 291 | fi 292 | 293 | # Quirk arch 294 | elif [ "$VM_QUIRK" == "5" ]; then 295 | if [ "$VM_CDROM" == "1" ]; then 296 | rm $SCRIPTPATH/grub.cfg 297 | echo "set root=(cd0)" >> $SCRIPTPATH/grub.cfg 298 | echo "linux (cd0)/arch/boot/x86_64/vmlinuz archisobasedir=arch archisolabel=ARCH_201601" >> $SCRIPTPATH/grub.cfg 299 | echo "initrd (cd0)/arch/boot/x86_64/archiso.img" >> $SCRIPTPATH/grub.cfg 300 | echo "boot" >> $SCRIPTPATH/grub.cfg 301 | /usr/local/sbin/grub-bhyve ${WIRE_SWITCH} --directory=$SCRIPTPATH -m $SCRIPTPATH/device.map -M $VM_MEMORY $VM_NAME 302 | 303 | else 304 | /usr/local/sbin/grub-bhyve ${WIRE_SWITCH} --directory=$SCRIPTPATH -m $SCRIPTPATH/device.map -M $VM_MEMORY $VM_NAME 305 | fi 306 | 307 | # Quirk NetBSD 308 | elif [ "$VM_QUIRK" == "6" ]; then 309 | if [ "$VM_CDROM" == "1" ]; then 310 | rm $SCRIPTPATH/grub.cfg 311 | # echo "set root=(cd0)" >> $SCRIPTPATH/grub.cfg 312 | echo "knetbsd -h -r cd0a (cd0)/netbsd" >> $SCRIPTPATH/grub.cfg 313 | echo "boot" >> $SCRIPTPATH/grub.cfg 314 | /usr/local/sbin/grub-bhyve ${WIRE_SWITCH} --directory=$SCRIPTPATH -m $SCRIPTPATH/device.map -M $VM_MEMORY $VM_NAME 315 | 316 | else 317 | rm $SCRIPTPATH/grub.cfg 318 | echo "knetbsd -h -r wd0a (${BOOT_DEVICE})/netbsd" >> $SCRIPTPATH/grub.cfg 319 | echo "boot" >> $SCRIPTPATH/grub.cfg 320 | /usr/local/sbin/grub-bhyve ${WIRE_SWITCH} --directory=$SCRIPTPATH -m $SCRIPTPATH/device.map -M $VM_MEMORY $VM_NAME 321 | fi 322 | 323 | # Quirk OpenBSD 324 | elif [ "$VM_QUIRK" == "7" ]; then 325 | if [ "$VM_CDROM" == "1" ]; then 326 | rm $SCRIPTPATH/grub.cfg 327 | echo "kopenbsd -h com0 (cd0)/5.8/amd64/bsd.rd" >> $SCRIPTPATH/grub.cfg 328 | echo "boot" >> $SCRIPTPATH/grub.cfg 329 | /usr/local/sbin/grub-bhyve ${WIRE_SWITCH} --directory=$SCRIPTPATH -m $SCRIPTPATH/device.map -M $VM_MEMORY $VM_NAME 330 | 331 | else 332 | rm $SCRIPTPATH/grub.cfg 333 | echo "kopenbsd -h com0 -r sd0a (hd0,openbsd${BOOT_PART})/bsd" >> $SCRIPTPATH/grub.cfg 334 | echo "boot" >> $SCRIPTPATH/grub.cfg 335 | /usr/local/sbin/grub-bhyve ${WIRE_SWITCH} --directory=$SCRIPTPATH -m $SCRIPTPATH/device.map -M $VM_MEMORY $VM_NAME 336 | fi 337 | 338 | # Quirk Windows10 339 | elif [ "$VM_QUIRK" == "8" ]; then 340 | #echo "Windows 10 ... nothing to do :P" 341 | USE_BOOTROM="-l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd"; 342 | else 343 | echo "VM_QUIRK NOT SET" 344 | fi 345 | 346 | sys_log "${VM_NAME}: started" 347 | /usr/sbin/bhyve ${WIRE_SWITCH} -c $VM_CPUS -m $VM_MEMORY"M" -A -H -w -s 0,hostbridge -s 31,lpc $FINAL_PCI -l com1,stdio $USE_BOOTROM $VM_NAME 348 | bhyve_exit=$? 349 | sys_log "${VM_NAME}: stopped" 350 | 351 | # Clean up the vm, its time to destroy it. 352 | bhyvectl --vm=$VM_NAME --destroy 2> /dev/null 353 | 354 | # Tidy up any auto networking that was done 355 | for PAIR in ${AUTO_NETWORKING_GEN_INTS}; do 356 | TAP=`echo "${PAIR}" | awk -F : '{print $1}'`; 357 | BRIDGE=`echo "${PAIR}" | awk -F : '{print $2}'`; 358 | sys_log "${VM_NAME}: releasing tap${TAP}" 359 | 360 | echo "VM:${VM_NAME} disconnecting tap${TAP} from bridge${BRIDGE}" 361 | ifconfig bridge${BRIDGE} deletem tap${TAP} #2> /dev/null 362 | 363 | echo "VM:${VM_NAME} removing tap${TAP}" 364 | ifconfig tap${TAP} destroy #2> /dev/null 365 | done 366 | 367 | # Only loop if the exit code was 0 (reboot) 368 | if [ $bhyve_exit -ne 0 ]; then 369 | sys_log "${VM_NAME}: guest shutdown detected"; 370 | echo "VM Shutdown detected, not looping." 371 | break 372 | else 373 | sys_log "${VM_NAME}: guest reboot detected"; 374 | echo "VM Reboot detected, looping" 375 | # -- Re-read the config file in case it's changed 376 | . "${VM_ROOT}/vm.conf"; 377 | fi 378 | done 379 | 380 | rm "${VM_STATE_FILE}"; 381 | 382 | if [ "${VM_POST_CMD}" != "" ]; then 383 | `${VM_POST_CMD}` 384 | fi 385 | } 386 | 387 | vml_stat() { 388 | # bhyvectl --vm=$VM_NAME --get-all | wc -l | tr -d " " | tr -d "\n" 389 | if [ "`bhyvectl --vm=$VM_NAME --get-all 2>&1 | wc -l | tr -d " " | tr -d "\n"`" == "1" ]; then 390 | return 0 391 | else 392 | return 1 393 | fi 394 | } 395 | 396 | vml_attach(){ 397 | vml_stat 398 | if [ "$?" == "1" ]; then 399 | screen -r bhyve."$VM_NAME" 400 | else 401 | echo "VM is not running" 402 | fi 403 | 404 | } 405 | 406 | vml_status(){ 407 | vml_stat 408 | if [ "$?" == "1" ]; then 409 | echo "VM is running" 410 | else 411 | echo "VM is not running" 412 | fi 413 | 414 | } 415 | 416 | fatal_error() { 417 | MESSAGE="${1}"; 418 | sys_log "FATAL ERROR: ${MESSAGE}"; 419 | echo " >>> ERROR: ${MESSAGE}"; 420 | echo " >>> Exiting."; 421 | exit 1; 422 | } 423 | 424 | interface_exists() { 425 | INT="$1"; 426 | if [ -z ${INT} ]; then 427 | return 1 428 | fi 429 | 430 | if ifconfig ${INT} > /dev/null 2> /dev/null; then 431 | return 0 432 | else 433 | return 1 434 | fi 435 | } 436 | 437 | get_next_free_tap() { 438 | CUR_TRY=${TAP_START}; 439 | while [ ${CUR_TRY} -le ${TAP_MAX} ]; do 440 | if ! interface_exists "tap${CUR_TRY}"; then 441 | echo "${CUR_TRY}"; 442 | return 443 | fi 444 | CUR_TRY=$((CUR_TRY+1)); 445 | done 446 | # Return nothing if error 447 | } 448 | 449 | find_all_configs () { 450 | LIST=`find $VM_LIVES | grep "/vm.conf$" | sort -V -t . -k 2 -k 1`; 451 | echo "${LIST}"; 452 | } 453 | 454 | get_config_file_name () { 455 | VM_NAME="$1"; 456 | 457 | CONF_FILE="${VM_LIVES}/${VM_NAME}/vm.conf"; 458 | 459 | if [ -e "${CONF_FILE}" ]; then 460 | echo "${CONF_FILE}"; 461 | fi 462 | } 463 | 464 | find_all_bridges () { 465 | LIST=`ifconfig -a | egrep "^bridge[0-9]+:" | awk -F : '{print $1}'`; 466 | echo "${LIST}"; 467 | } 468 | 469 | # 470 | # Writes entry to syslog 471 | # 472 | sys_log () { 473 | MESSAGE="$1"; 474 | LOG_LEVEL="$2"; 475 | 476 | if [ -z "${SYSLOG_APPNAME}" ]; then 477 | # -- Default to script's filename 478 | SYSLOG_APPNAME="${0##*/}[$$]"; 479 | fi 480 | if [ -z "${LOG_LEVEL}" ]; then 481 | # - Default to notice 482 | LOG_LEVEL="notice"; 483 | fi 484 | 485 | logger -p local0.${LOG_LEVEL} -t "${SYSLOG_APPNAME}" "${MESSAGE}"; 486 | } 487 | 488 | # 489 | # Generic Mutex Lock 490 | # 491 | mutex_lock () { 492 | MUTEXNAME="$1"; 493 | RUNMUTEXDIR="/tmp"; 494 | 495 | LOCKDIR="${RUNMUTEXDIR}/${MUTEXNAME}"; 496 | PIDFILE="${LOCKDIR}/pid"; 497 | #sys_log "obtaining lock ${MUTEXNAME} ..."; 498 | 499 | # 500 | # Based on awesome inspiration from http://wiki.bash-hackers.org/howto/mutex 501 | # 502 | 503 | NO_PID_FILE_COUNT="0"; 504 | LOCK_WAIT="0"; 505 | while ! mkdir "${LOCKDIR}" 2> /dev/null; 506 | do 507 | # ---- Lock failed 508 | # 509 | if ! [ -e "${PIDFILE}" ]; then 510 | ls -l ${PIDFILE}; 511 | # ---- No PID file yet 512 | # This can actually happen with high concurrency, on alert on the 12th failure 513 | # which gives the process that got the lock 3 seconds to write it's PID 514 | # before logging this as a warning 515 | 516 | NO_PID_FILE_COUNT=$(( NO_PID_FILE_COUNT + 1 )); 517 | if [ ${NO_PID_FILE_COUNT} -ge 12 ]; then 518 | #log_write "couldn't get PID of process that has lock on ${LOCKDIR} even after waiting 3 seconds, exiting"; 519 | sys_log "WARNING: couldn't get PID of process that has lock on ${LOCKDIR} even after waiting ${NO_PID_FILE_COUNT} seconds, exiting"; 520 | echo "BMT WARNING: couldn't get PID of process that has lock on ${LOCKDIR} even after waiting ${NO_PID_FILE_COUNT} seconds, exiting"; 521 | exit 1; 522 | fi 523 | else 524 | # ---- There is a PID file 525 | # 526 | NO_PID_FILE_COUNT="0"; 527 | 528 | LOCK_WAIT=$(( LOCK_WAIT + 1 )); 529 | if [ ${LOCK_WAIT} -ge 60 ]; then 530 | sys_log "WARNING: couldn't get lock for ${MUTEXNAME} after waiting ${LOCK_WAIT} seconds, exiting"; 531 | echo "BMT WARNING: couldn't get lock for ${MUTEXNAME} after waiting ${LOCK_WAIT} seconds, exiting"; 532 | exit 1; 533 | fi 534 | 535 | OTHERPID=`cat "${PIDFILE}" 2> /dev/null`; 536 | if [ -n "${OTHERPID}" ]; then 537 | # --- Check to see if the PID is running 538 | if ! kill -0 ${OTHERPID} 2> /dev/null; 539 | then 540 | # --- Stale lockfile 541 | sys_log "removing stale PID lock file for ${MUTEXNAME}"; 542 | rm -rf "${LOCKDIR}"; 543 | # --- Next loop should catch this 544 | else 545 | # --- Process is still running, wait a second before checking again 546 | echo "BMT: waiting for ${MUTEXNAME} lock"; 547 | sys_log "waiting for ${MUTEXNAME} lock"; 548 | sleep 1; 549 | fi 550 | else 551 | # -- OTHERPID is blank 552 | # -- Make sure the file is still there 553 | if [ -e "${PIDFILE}" ]; then 554 | # -- It is, something is messed 555 | #log_write "PID lock file exists ${PIDFILE} but is empty!"; 556 | sys_log "WARNING: PID lock file exists for ${MUTEXNAME} but is empty"; 557 | echo "BMT WARNING: PID lock file exists ${MUTEXNAME} but is empty!"; 558 | fi 559 | fi 560 | fi 561 | done 562 | 563 | # --- Output PID 564 | echo "$$" >"${PIDFILE}"; 565 | 566 | # --- Lock succeeded 567 | #log_write "lock ${MUTEX_NAME} obtained"; 568 | } 569 | 570 | mutex_unlock () { 571 | MUTEXNAME="$1"; 572 | RUNMUTEXDIR="/tmp"; 573 | 574 | if [ -z "${RUNMUTEXDIR}" ]; then 575 | fatal_error "mutex_unlock RUNMUTEXDIR empty"; 576 | fi 577 | 578 | if [ -z "${MUTEXNAME}" ]; then 579 | fatal_error "mutex_unlock MUTEXNAME empty"; 580 | fi 581 | 582 | LOCKDIR="${RUNMUTEXDIR}/${MUTEXNAME}"; 583 | PIDFILE="${LOCKDIR}/pid"; 584 | 585 | if [ -z "${LOCKDIR}" -o "${LOCKDIR}" == "/" ]; then 586 | fatal_error "mutex_unlock LOCKDIR empty or invalid"; 587 | fi 588 | 589 | rm -rf "${LOCKDIR}"; 590 | 591 | #sys_log "lock ${MUTEXNAME} released"; 592 | } 593 | 594 | clear_vm_vars () { 595 | # -- Clears variables that vm.conf uses (ie; to avoid looping isses) 596 | VM_NAME="" 597 | VM_QUIRK="" 598 | VM_CPUS="" 599 | VM_MEMORY="" 600 | VM_ONBOOT="" 601 | VM_PRE_CMD="" 602 | VM_POST_CMD="" 603 | VM_BOOT_ZVOL="" 604 | VM_BOOT_PART="1" 605 | VM_BOOT_PART_TYPE="" 606 | VM_DISK_FILES="" 607 | VM_GRUB_DIR="" 608 | VM_CDROM="" 609 | VM_CDROM_MEDIA="" 610 | AUTO_NETWORKING="" 611 | VM_FRAMEBUFFER="" 612 | VM_FRAMEBUFFER_OPTS="" 613 | VM_USB3="" 614 | 615 | for j in 1 2 3 4 5 6 7 8 9 10 11 12; do 616 | PREFIX_BRIDGE="VM_N${j}_BRIDGE_NUM" 617 | eval $PREFIX_BRIDGE=''; 618 | 619 | PREFIX_TAP="VM_N${j}_TAP_NUM" 620 | eval $PREFIX_TAP='' 621 | done 622 | } 623 | 624 | vml_check () { 625 | # Check to make sure there are no sourcing errors and no duplicate TAP devices 626 | out=`find_all_configs`; 627 | USED_TAPS=""; 628 | RET="0"; 629 | 630 | for vm in $out; do 631 | if ! sh $vm; then 632 | echo "${VM_NAME}: Syntax error in '${vm}'!"; 633 | RET="1"; 634 | else 635 | # -- Clear vars 636 | clear_vm_vars; 637 | 638 | # -- Now load VM config 639 | . $vm; 640 | for j in 1 2 3 4 5 6 7 8 9 10 11 12; do 641 | PREFIX_BRIDGE="VM_N${j}_BRIDGE_NUM" 642 | BRIDGE=`eval echo "\$""$PREFIX_BRIDGE"` 643 | PREFIX_TAP="VM_N${j}_TAP_NUM" 644 | TAP=`eval echo "\$""$PREFIX_TAP"` 645 | 646 | if [ "$TAP" != "" ]; then 647 | #echo "$vm - $TAP"; 648 | USE_CHECK=`echo "${USED_TAPS}" | tr " " "\n" | grep "^${TAP}:"`; 649 | if [ -n "${USE_CHECK}" ]; then 650 | DUPE_TAP_HOST=`echo ${USE_CHECK} | awk -F : '{print $2}'`; 651 | echo ">> ERROR: TAP ${TAP} used by ${VM_NAME} and ${DUPE_TAP_HOST}"; 652 | RET="1"; 653 | else 654 | USED_TAPS="${USED_TAPS} ${TAP}:${VM_NAME}"; 655 | fi 656 | fi 657 | done 658 | fi 659 | done 660 | 661 | #echo "Used taps: ${USED_TAPS}"; 662 | return ${RET}; 663 | } 664 | 665 | vml_check_cond_show () { 666 | if ! vml_check > /dev/null 2> /dev/null; then 667 | echo ""; 668 | echo ">> ERROR: Configuration errors found:"; 669 | vml_check; 670 | echo ""; 671 | fi 672 | } 673 | 674 | NiceSize () 675 | { 676 | SIZE="$1"; # Value (in bytes) 677 | SCALE="$2"; # Decimal places (default is 1) 678 | UNIT="$3"; # Unit (default is auto-detect) 679 | 680 | # ---- Set default decimal places 681 | # 682 | if [ -z "${SCALE}" ]; then 683 | SCALE=1; 684 | fi 685 | 686 | # ---- Set default unit 687 | # 688 | if [ -z "${UNIT}" ]; then 689 | if [ "${SIZE}" -gt 1125899906842624 ]; then 690 | UNIT="PiB"; 691 | elif [ "${SIZE}" -gt 1099511627776 ]; then 692 | UNIT="TiB"; 693 | elif [ "${SIZE}" -gt 1073740800 ]; then 694 | UNIT="GiB"; 695 | elif [ "${SIZE}" -gt 1048576 ]; then 696 | UNIT="MiB"; 697 | elif [ "${SIZE}" -gt 1024 ]; then 698 | UNIT="KiB"; 699 | else 700 | UNIT="byte"; 701 | fi 702 | fi 703 | 704 | # ---- Convert to unit 705 | # 706 | if [ "${UNIT}" == "PiB" ]; then 707 | NICE_SIZE=`echo "scale=${SCALE}; ${SIZE}/1125899906842624" | bc`; 708 | elif [ "${UNIT}" == "TiB" ]; then 709 | NICE_SIZE=`echo "scale=${SCALE}; ${SIZE}/1099511627776" | bc`; 710 | elif [ "${UNIT}" == "GiB" ]; then 711 | NICE_SIZE=`echo "scale=${SCALE}; ${SIZE}/1073741824" | bc`; 712 | elif [ "${UNIT}" == "MiB" ]; then 713 | NICE_SIZE=`echo "scale=${SCALE}; ${SIZE}/1048576" | bc`; 714 | elif [ "${UNIT}" == "KiB" ]; then 715 | NICE_SIZE=`echo "scale=${SCALE}; ${SIZE}/1024" | bc`; 716 | elif [ "${UNIT}" == "byte" ]; then 717 | NICE_SIZE="${SIZE}"; 718 | else 719 | NICE_SIZE="NiceSize: unknown unit \"${UNIT}\""; 720 | fi 721 | 722 | NICE_SIZE=`echo ${NICE_SIZE} | rev | sed 's/\([0-9][0-9][0-9]\)\([0-9]\)/\1,\2/g' | rev`; 723 | echo "${NICE_SIZE} ${UNIT}"; 724 | } 725 | 726 | vml_get_tap_to_vm_map () { 727 | out=`find_all_configs`; 728 | # -- Loop through each VM, returning a sorted list of tapN:vmnames values 729 | RET="" 730 | for vm in $out; do 731 | clear_vm_vars; 732 | 733 | . $vm; 734 | 735 | # -- Include state if there is one 736 | CUR_VM_STATE_FILE="${VM_STATE_DIR}/${VM_NAME}.state"; 737 | if [ -e "${CUR_VM_STATE_FILE}" ]; then 738 | . ${CUR_VM_STATE_FILE}; 739 | fi 740 | 741 | for j in 1 2 3 4 5 6 7 8 9 10 11 12; do 742 | PREFIX_TAP="VM_N${j}_TAP_NUM" 743 | TAP=`eval echo "\$""$PREFIX_TAP"` 744 | 745 | if [ -n "$TAP" ]; then 746 | USE_CHECK=`echo ${members} | tr " " "\n" | grep "tap${TAP}"`; 747 | if [ -n "${USE_CHECK}" ]; then 748 | RET="${RET} tap${TAP}:${VM_NAME}" 749 | fi 750 | fi 751 | done 752 | done 753 | 754 | echo "${RET}" | tr " " "\n" | sort -u 755 | } 756 | 757 | vml_get_tap_vm () { 758 | TAP_TO_FIND="$1"; 759 | TAP_MAP="$2"; 760 | 761 | if [ -z ${TAP_MAP} ]; then 762 | TAP_MAP=`vml_get_tap_to_vm_map`; 763 | fi 764 | 765 | RET=`echo "${TAP_MAP}" | tr " " "\n" | grep "^${TAP_TO_FIND}:" | awk -F : '{print $2}'`; 766 | 767 | echo "${RET}"; 768 | } 769 | 770 | vml_get_tap_to_bridge_map() { 771 | # -- Loop through each bridge and then VM, returning a sorted list of dev:bridgeN values 772 | out=`find_all_configs`; 773 | bridges=`find_all_bridges`; 774 | 775 | RET=""; 776 | 777 | # -- Loop through each bridge 778 | for bridge in $bridges; do 779 | members=`ifconfig $bridge | grep "member: " | awk '{print $2}' | sort -u`; 780 | 781 | if [ -n "${members}" ]; then 782 | # -- loop through taps 783 | for CUR_DEV in $members; do 784 | RET="${RET} ${CUR_DEV}:${bridge}" 785 | done 786 | fi 787 | done 788 | 789 | echo "${RET}"; 790 | } 791 | 792 | vml_get_tap_bridge () { 793 | TAP_TO_FIND="$1"; 794 | TAP_MAP="$2"; 795 | 796 | if [ -z ${TAP_MAP} ]; then 797 | TAP_MAP=`vml_get_tap_to_bridge_map`; 798 | fi 799 | 800 | RET=`echo "${TAP_MAP}" | tr " " "\n" | grep "^${TAP_TO_FIND}:" | awk -F : '{print $2}'`; 801 | 802 | echo "${RET}"; 803 | } 804 | 805 | vml_get_bridge_name () { 806 | bridge="$1"; 807 | BRIDGE_NAME_VAR="BRIDGE_${bridge}_NAME"; 808 | BRIDGE_NAME=`eval echo "\$""$BRIDGE_NAME_VAR"`; 809 | 810 | echo "${BRIDGE_NAME}" 811 | } 812 | 813 | get_auto_tap_bridge_to_off_vm_map () { 814 | out=`find_all_configs`; 815 | # -- Loop through each VM, returning a sorted list of bridgeN:vmnames 816 | # values for auto-configured taps that are not up since they 817 | # won't show up otherwise 818 | # 819 | # ie; bridge3:testvm 820 | # 821 | RET="" 822 | for vm in $out; do 823 | clear_vm_vars; 824 | 825 | . $vm; 826 | 827 | vml_stat 828 | 829 | if [ "$?" == "1" ]; then 830 | STATE="On" 831 | else 832 | STATE="Off" 833 | fi 834 | 835 | # -- Only bother if auto networking is enabled 836 | if [ "$AUTO_NETWORKING" == "YES" -a "${STATE}" == "Off" ]; then 837 | # -- Include state if there is one 838 | CUR_VM_STATE_FILE="${VM_STATE_DIR}/${VM_NAME}.state"; 839 | if [ -e "${CUR_VM_STATE_FILE}" ]; then 840 | . ${CUR_VM_STATE_FILE}; 841 | fi 842 | 843 | for j in 1 2 3 4 5 6 7 8 9 10 11 12; do 844 | PREFIX_BRIDGE="VM_N${j}_BRIDGE_NUM" 845 | BRIDGE=`eval echo "\$""$PREFIX_BRIDGE"` 846 | PREFIX_TAP="VM_N${j}_TAP_NUM" 847 | TAP=`eval echo "\$""$PREFIX_TAP"` 848 | 849 | if [ -n "${BRIDGE}" -a -z "${TAP}" ]; then 850 | RET="${RET} bridge${BRIDGE}:${VM_NAME}" 851 | fi 852 | done 853 | fi 854 | done 855 | 856 | echo "${RET}" | tr " " "\n" | sort -u 857 | } 858 | 859 | vml_netmap () { 860 | vml_check_cond_show; 861 | 862 | out=`find_all_configs`; 863 | bridges=`find_all_bridges`; 864 | 865 | echo "Bridge To VM NIC Mappings"; 866 | echo ""; 867 | 868 | TAP_MAP=`vml_get_tap_to_vm_map`; 869 | AUTO_TAP_MAP=`get_auto_tap_bridge_to_off_vm_map`; 870 | 871 | # -- Loop through each bridge 872 | for bridge in $bridges; do 873 | members=`ifconfig $bridge | grep "member: " | awk '{print $2}' | sort -u`; 874 | 875 | echo -n "${bridge}"; 876 | 877 | BRIDGE_NAME=`vml_get_bridge_name ${bridge}`; 878 | if [ -n "${BRIDGE_NAME}" ]; then 879 | echo " - ${BRIDGE_NAME}"; 880 | else 881 | echo ""; 882 | fi 883 | 884 | echo " |"; 885 | 886 | # -- loop through taps that exist for VMs 887 | for CUR_DEV in $members; do 888 | CUR_VM_NAME=`vml_get_tap_vm "${CUR_DEV}" "${TAP_MAP}"`; 889 | 890 | # -- See if dmesg has dev info 891 | if [ -z "${CUR_VM_NAME}" ]; then 892 | CUR_VM_NAME=`grep "^${CUR_DEV}: " /var/run/dmesg.boot | awk -F '<' '{print $2}' | awk -F '>' '{print $1}' | grep -v "^$" | head -n 1`; 893 | fi 894 | 895 | CUR_TAP_VM_COUNT=`echo "${CUR_VM_NAME}" | wc -l`; 896 | if [ ${CUR_TAP_VM_COUNT} -gt 1 ]; then 897 | printf "\n"; 898 | fi 899 | 900 | # -- Display 901 | if [ -n "${CUR_VM_NAME}" ]; then 902 | printf " +-- %5s -> %s\n" "${CUR_DEV}" "${CUR_VM_NAME}"; 903 | #else 904 | # printf " +-- %5s\n" "${CUR_DEV}"; 905 | fi 906 | 907 | if [ ${CUR_TAP_VM_COUNT} -gt 1 ]; then 908 | printf " WARNING: MULTIPLE VMs ON ${CUR_DEV}!\n\n"; 909 | fi 910 | 911 | 912 | done 913 | 914 | # -- loop through any auto-taps that on VMs not turned on 915 | CUR_BRIDGE_AUTO_VMS=`echo "${AUTO_TAP_MAP}" | egrep "^${bridge}:"`; 916 | for CUR_PAIR in ${CUR_BRIDGE_AUTO_VMS}; do 917 | CUR_VM_NAME=`echo "${CUR_PAIR}" | awk -F : '{print $2}'`; 918 | printf " +-- %5s -> %s\n" "(off)" "${CUR_VM_NAME}"; 919 | done 920 | 921 | # -- loop through for devices with no VM 922 | # same as first loop (above the 'off vm' loop) but will grab physical 923 | # devices so they're at the bottom 924 | FIRST_DEV=""; 925 | for CUR_DEV in $members; do 926 | CUR_VM_NAME=`vml_get_tap_vm "${CUR_DEV}" "${TAP_MAP}"`; 927 | 928 | # -- See if dmesg has dev info 929 | if [ -z "${CUR_VM_NAME}" ]; then 930 | CUR_VM_NAME=`grep "^${CUR_DEV}: " /var/run/dmesg.boot | awk -F '<' '{print $2}' | awk -F '>' '{print $1}' | grep -v "^$" | head -n 1`; 931 | fi 932 | 933 | # -- Display 934 | if [ -z "${CUR_VM_NAME}" ]; then 935 | if [ -z "${FIRST_DEV}" ]; then 936 | printf " |\n"; 937 | FIRST_DEV="No"; 938 | fi 939 | printf " +-- %5s\n" "${CUR_DEV}"; 940 | fi 941 | 942 | done 943 | 944 | echo ""; 945 | done 946 | 947 | #echo "Used taps: ${USED_TAPS}"; 948 | return ${RET}; 949 | } 950 | 951 | get_zpath () { 952 | ZPATH=`zfs list -o name,mountpoint | sed -e s:"//":"/":g | grep $SCRIPTPATH | cut -d " " -f1 | head -n 1`; 953 | if [ -z "${ZPATH}" ]; then 954 | fatal_error "Couldn't get ZPATH"; 955 | else 956 | echo "${ZPATH}"; 957 | fi 958 | } 959 | 960 | vml_info () { 961 | VM_CONF=`get_config_file_name "${VM_NAME}"`; 962 | 963 | if [ -z "${VM_CONF}" ]; then 964 | fatal_error "Couldn't find VM config file"; 965 | fi 966 | 967 | . ${VM_CONF}; 968 | 969 | # -- Include state if there is one 970 | CUR_VM_STATE_FILE="${VM_STATE_DIR}/${VM_NAME}.state"; 971 | if [ -e "${CUR_VM_STATE_FILE}" ]; then 972 | . ${CUR_VM_STATE_FILE}; 973 | fi 974 | 975 | NICE_VM_MEMORY=`NiceSize $(( ${VM_MEMORY} * 1024 * 1024 ))`; 976 | NICE_OS=`vml_quirk_to_text ${VM_QUIRK}`; 977 | 978 | echo "VM Name: ${VM_NAME}"; 979 | echo "OS: ${NICE_OS}"; 980 | echo "CPU Cores: ${VM_CPUS}"; 981 | echo "RAM: ${NICE_VM_MEMORY}"; 982 | echo "Auto-start: ${VM_ONBOOT}"; 983 | echo ""; 984 | 985 | if [ -n "${VM_BOOT_ZVOL}" ]; then 986 | ZPATH=`get_zpath` 987 | VM_ZPOOL="${ZPATH}/${VM_BOOT_ZVOL}"; 988 | SIZE=`zfs get -Hp volsize ${VM_ZPOOL} | awk '{print $3}'` 989 | USED=`zfs get -Hp logicalused ${VM_ZPOOL} | awk '{print $3}'` 990 | NICE_SIZE=`NiceSize ${SIZE}`; 991 | NICE_USED=`NiceSize ${USED}`; 992 | 993 | printf "%-14s %9s %s\n" "Disk (ZVOL):" "${NICE_SIZE}" "${VM_ZPOOL} (${NICE_USED} on disk)"; 994 | 995 | VM_SNAPSHOTS=`zfs list -t snap | egrep "^${VM_ZPOOL}@" | awk '{print $1 ":" $2}'`; 996 | for VM_SNAPSHOT in ${VM_SNAPSHOTS}; do 997 | CUR_SNAP_NAME=`echo "${VM_SNAPSHOT}" | awk -F : '{print $1}'`; 998 | CUR_SNAP_SIZE=`echo "${VM_SNAPSHOT}" | awk -F : '{print $2}'`; 999 | printf " %9s %s\n" "${CUR_SNAP_NAME} (${CUR_SNAP_SIZE} used by snapshot)"; 1000 | done 1001 | fi 1002 | 1003 | if [ -n "${VM_DISK_FILES}" ]; then 1004 | for CUR_DISK_FILE_RAW in ${VM_DISK_FILES}; do 1005 | # -- Expand relative if needed 1006 | if echo "${CUR_DISK_FILE_RAW}" | grep "^/" > /dev/null; then 1007 | CUR_DISK_FILE="${CUR_DISK_FILE_RAW}" 1008 | else 1009 | CUR_DISK_FILE="${SCRIPTPATH}/${CUR_DISK_FILE_RAW}"; 1010 | fi 1011 | if [ -e "${CUR_DISK_FILE}" ]; then 1012 | SIZE=`stat -f %z "${CUR_DISK_FILE}"`; 1013 | NICE_SIZE=`NiceSize ${SIZE}`; 1014 | else 1015 | NICE_SIZE="--"; 1016 | fi 1017 | 1018 | printf "%-14s %9s %s\n" "Disk (File):" "${NICE_SIZE}" "${CUR_DISK_FILE}"; 1019 | done 1020 | fi 1021 | 1022 | echo ""; 1023 | for j in 1 2 3 4 5 6 7 8 9 10 11 12; do 1024 | NIC_NO="$j"; 1025 | PREFIX_BRIDGE="VM_N${j}_BRIDGE_NUM" 1026 | CUR_BRIDGE_NO=`eval echo "\$""$PREFIX_BRIDGE"` 1027 | CUR_BRIDGE="bridge${CUR_BRIDGE_NO}"; 1028 | PREFIX_TAP="VM_N${NIC_NO}_TAP_NUM" 1029 | TAP_NO=`eval echo "\$""$PREFIX_TAP"` 1030 | 1031 | if [ -z "${TAP_NO}" ]; then 1032 | if [ "${AUTO_NETWORKING}" == "YES" ]; then 1033 | TAP_DEV="auto"; 1034 | fi 1035 | else 1036 | TAP_DEV="tap${TAP_NO}"; 1037 | fi 1038 | 1039 | if [ -n "${CUR_BRIDGE_NO}" ]; then 1040 | #echo "x"; 1041 | #REAL_BRIDGE=`vml_get_tap_bridge "${TAP_DEV}"`; 1042 | 1043 | CUR_BRIDGE_NAME=`vml_get_bridge_name ${CUR_BRIDGE}`; 1044 | if [ -z "${CUR_BRIDGE_NAME}" ]; then 1045 | printf "NIC %-2s -> %-6s -> %s\n" "${NIC_NO}" "${TAP_DEV}" "${CUR_BRIDGE}"; 1046 | else 1047 | printf "NIC %-2s -> %-6s -> %s (%s)\n" "${NIC_NO}" "${TAP_DEV}" "${CUR_BRIDGE_NAME}" "${CUR_BRIDGE}"; 1048 | #echo "NIC ${NIC_NO}: (${TAP_DEV}) -> ${CUR_BRIDGE_NAME} (${CUR_BRIDGE})"; 1049 | fi 1050 | fi 1051 | done 1052 | } 1053 | 1054 | vml_list () { 1055 | vml_check_cond_show 1056 | 1057 | # -- Make sure we're root, and if not warn since 'on' etc won't display right 1058 | if [ `id -u`x != "0"x ]; then 1059 | echo "" >&2; 1060 | echo "WARNING: Running as non-root, VM state and other info will not be reported correctly!" >&2; 1061 | echo "" >&2; 1062 | IS_ROOT="No"; 1063 | else 1064 | IS_ROOT="Yes"; 1065 | fi 1066 | 1067 | out=`find_all_configs`; 1068 | COL_FORMAT="%-20s %-13s %-7s %-6s %4s %7s %7s %11s\n"; 1069 | 1070 | printf "${COL_FORMAT}" "Guest Name" "OS" "State" "Boot" "CPUs" "RAM" "Use" "Res"; 1071 | printf -- "---------------------------------------------------------------------------------- -\n"; 1072 | 1073 | TOTAL_USED_CPU="0"; 1074 | TOTAL_USED_MEM="0"; 1075 | TOTAL_VM_MEMORY="0"; 1076 | 1077 | for vm in $out; do 1078 | . $vm 1079 | TYPE=$( vml_quirk_to_text ) 1080 | vml_stat 1081 | if [ "$?" == "1" ]; then 1082 | STATE="On" 1083 | else 1084 | STATE="Off" 1085 | fi 1086 | 1087 | if [ "${IS_ROOT}" == "Yes" ]; then 1088 | if [ "${STATE}" == "On" ]; then 1089 | USED_CPU=`ps aux -ww | grep "bhyve: $VM_NAME (bhyve)" | grep -v grep | awk '{print $3}' | awk -F . '{print $1}'`; 1090 | if [ `id -u` -eq "0" ]; then 1091 | USED_MEM_RAW=`bhyvectl --get-stats --vm=$VM_NAME | grep "^Resident memory" | awk '{print $3}'`; 1092 | if [ -z "${USED_MEM_RAW}" ]; then 1093 | USED_MEM_RAW="0"; 1094 | fi 1095 | USED_MEM=`NiceSize ${USED_MEM_RAW} 1`; 1096 | TOTAL_USED_MEM="$(( ${TOTAL_USED_MEM} + ${USED_MEM_RAW} ))"; 1097 | else 1098 | USED_MEM="--"; 1099 | fi 1100 | else 1101 | USED_CPU="0"; 1102 | USED_MEM="--"; 1103 | fi 1104 | 1105 | # -- Convert MiB into bytes 1106 | VM_MEMORY=$(( ${VM_MEMORY} * 1024 * 1024 )); 1107 | 1108 | if [ -n "${VM_MEMORY}" ]; then 1109 | TOTAL_VM_MEMORY="$(( ${TOTAL_VM_MEMORY} + ${VM_MEMORY} ))"; 1110 | else 1111 | NICE_TOTAL_VM_MEMORY="n/a"; 1112 | fi 1113 | 1114 | if [ -n "${USED_CPU}" ]; then 1115 | TOTAL_USED_CPU="$(( ${TOTAL_USED_CPU} + ${USED_CPU}))"; 1116 | fi 1117 | 1118 | VM_MEMORY=`NiceSize ${VM_MEMORY} 0`; 1119 | else 1120 | STATE="?"; 1121 | USED_CPU="?"; 1122 | USED_MEM="?"; 1123 | VM_MEMORY="?"; 1124 | TOTAL_VM_MEMORY="?"; 1125 | NICE_TOTAL_VM_MEMORY="?"; 1126 | fi 1127 | 1128 | if [ "${VM_ONBOOT}" == "YES" ]; then 1129 | NICE_VM_ONBOOT="Auto"; 1130 | else 1131 | NICE_VM_ONBOOT="Manual"; 1132 | fi 1133 | 1134 | printf "${COL_FORMAT}" "$VM_NAME" "$TYPE" "$STATE" "${NICE_VM_ONBOOT}" "$VM_CPUS" "$VM_MEMORY" "${USED_CPU}%" "$USED_MEM"; 1135 | 1136 | # -- Check for mismatched 1137 | if ! echo "${vm}" | egrep "/${VM_NAME}/vm.conf$" > /dev/null; then 1138 | echo " ^ --- WARNING: VM name defined in vm.conf mismatches mount-point!" >&2; 1139 | echo "" >&2; 1140 | fi 1141 | done 1142 | 1143 | if [ "${IS_ROOT}" == "Yes" ]; then 1144 | NICE_TOTAL_VM_MEMORY=`NiceSize ${TOTAL_VM_MEMORY} 0`; 1145 | NICE_TOTAL_USED_MEM=`NiceSize ${TOTAL_USED_MEM}`; 1146 | else 1147 | TOTAL_USED_CPU="??"; 1148 | NICE_TOTAL_VM_MEMORY="??"; 1149 | NICE_TOTAL_USED_MEM="??"; 1150 | fi 1151 | 1152 | printf -- "---------------------------------------------------------------------------------- -\n"; 1153 | printf "${COL_FORMAT}" "" "" "" "" "" "${NICE_TOTAL_VM_MEMORY}" "${TOTAL_USED_CPU}%" "${NICE_TOTAL_USED_MEM}"; 1154 | } 1155 | 1156 | quick_padding () { 1157 | LENGTH=`echo ${1} | wc -c | tr -d "\n"` 1158 | PAD_CHAR=`expr ${2} - ${LENGTH}` 1159 | for a in `seq 1 $PAD_CHAR`; do 1160 | printf " " 1161 | done 1162 | } 1163 | 1164 | vml_rcstart () { 1165 | 1166 | sys_log "starting auto-boot VMs ..."; 1167 | echo "bhyve management tool" 1168 | 1169 | # Kernel modules 1170 | if [ "${bmt_kern_modules}" == "" ]; then 1171 | bmt_kern_modules="vmm if_tap if_bridge" 1172 | printf " * loading default kernel modules:\n" 1173 | else 1174 | printf " * loading user kernel modules: \n" 1175 | fi 1176 | 1177 | 1178 | for MOD in ${bmt_kern_modules}; do 1179 | printf " * ${MOD}" 1180 | kldload $MOD > /dev/null 2>&1 1181 | printf "$( quick_padding $MOD 20 )" 1182 | echo "done" 1183 | done 1184 | 1185 | # Sysctls 1186 | if [ "${bmt_sysctls}" == "" ]; then 1187 | bmt_sysctls="net.link.tap.up_on_open=1" 1188 | printf " * loading default sysctls:\n" 1189 | else 1190 | printf " * loading user sysctls: \n" 1191 | fi 1192 | 1193 | 1194 | for MOD in ${bmt_sysctls}; do 1195 | echo " * ${MOD}" 1196 | sysctl $MOD > /dev/null 2>&1 1197 | done 1198 | 1199 | 1200 | # sysctl net.link.tap.up_on_open=1 1201 | 1202 | # Virtual Machines 1203 | out=`find_all_configs`; 1204 | printf " * starting vms: \n" 1205 | for vm in $out; do 1206 | . $vm 1207 | 1208 | if [ "$VM_ONBOOT" == "YES" ]; then 1209 | printf " * ${VM_NAME}" 1210 | $SCRIPT start $VM_NAME 1211 | printf "$( quick_padding $VM_NAME 20 )" 1212 | echo "done" 1213 | if [ -n "${RC_START_VM_DELAY}" ]; then 1214 | sleep ${RC_START_VM_DELAY}; 1215 | fi 1216 | fi 1217 | done 1218 | echo "bmt init finished" 1219 | sys_log "auto-boot VMs started"; 1220 | } 1221 | 1222 | vml_wait_for_poweroff(){ 1223 | while true; do 1224 | vml_stat 1225 | if [ "$?" == "0" ]; then 1226 | break 1227 | fi 1228 | done 1229 | } 1230 | 1231 | vml_rcstop() { 1232 | sys_log "shutting down vms ..."; 1233 | printf "bmt stopping all running vms: \n" 1234 | out=`find_all_configs`; 1235 | 1236 | # Quick stop loop 1237 | for vm in $out; do 1238 | . $vm 1239 | vml_stop -q 1240 | done 1241 | 1242 | # Wait for things to actually stop 1243 | for vm in $out; do 1244 | . $vm 1245 | printf " * ${VM_NAME}" 1246 | while true; do 1247 | vml_stat 1248 | if [ "$?" == "0" ]; then 1249 | break 1250 | fi 1251 | done 1252 | printf "$( quick_padding $VM_NAME 20 )" 1253 | echo "done" 1254 | done 1255 | 1256 | # Kernel modules 1257 | if [ "${bmt_kern_modules}" == "" ]; then 1258 | bmt_kern_modules="vmm if_tap if_bridge" 1259 | printf " * unloading default kernel modules:\n" 1260 | else 1261 | printf " * unloading user kernel modules: \n" 1262 | fi 1263 | 1264 | 1265 | for MOD in ${bmt_kern_modules}; do 1266 | printf " * ${MOD}" 1267 | kldunload $MOD > /dev/null 2>&1 1268 | printf "$( quick_padding $MOD 20 )" 1269 | echo "done" 1270 | done 1271 | 1272 | 1273 | sys_log "vm shutdown complete"; 1274 | printf "bmt shutdown finished\n" 1275 | } 1276 | 1277 | vml_stop () { 1278 | sys_log "${VM_NAME}: stopping"; 1279 | if [ "$ARG_FORCE" == "-f" ]; then 1280 | bhyvectl --vm=$VM_NAME --force-poweroff 1281 | bhyvectl --vm=$VM_NAME --destroy 1282 | else 1283 | vml_stat 1284 | if [ "$?" == "1" ]; then 1285 | tpid=`ps aux -ww | grep "bhyve: $VM_NAME (bhyve)" | grep -v grep | awk '{print $2}'` 1286 | kill -TERM $tpid 1287 | else 1288 | if [ "$1" != "-q" ]; then 1289 | echo "VM is not running" 1290 | fi 1291 | fi 1292 | fi 1293 | } 1294 | 1295 | vml_destroy () { 1296 | sys_log "${VM_NAME}: destroying"; 1297 | ARG_FORCE="YES" 1298 | vml_stop 1299 | zfs destroy -r $ZPATH 1300 | echo "$VM_NAME destroyed" 1301 | } 1302 | 1303 | vml_edit () { 1304 | $EDITOR $VM_ROOT"/vm.conf" 1305 | } 1306 | 1307 | vml_vm_exists () { 1308 | if [ -f "${VM_LIVES}/${VM_NAME}/vm.conf" ]; then 1309 | return 1 1310 | else 1311 | return 0 1312 | fi 1313 | } 1314 | 1315 | vml_clone () { 1316 | # If the VM is running, error and exit 1317 | if [ "$FORCE" == "YES" ]; then 1318 | vml_stop 1319 | echo "Waiting for vm to poweroff..." 1320 | vml_wait_for_poweroff 1321 | echo "VM Powered off" 1322 | else 1323 | vml_stat 1324 | 1325 | if [ "$?" == "1" ]; then 1326 | echo "VM is running. Use -f flag to shutdown before cloning" 1327 | exit 1328 | fi 1329 | fi 1330 | 1331 | # Actually do the clone 1332 | SNAPSHOT_NAME="`date +'%s'`_clone_${ARG_BONUS}" 1333 | 1334 | zfs snapshot -r ${ZPATH}@${SNAPSHOT_NAME} 1335 | zfs clone ${ZPATH}@${SNAPSHOT_NAME} ${BASE_ZPATH}/$ARG_BONUS 1336 | # Clone all of the zvols into their destination 1337 | for ds in `zfs list -r -H -t volume -o name $ZPATH` 1338 | do 1339 | DS_NAME=${ds##*/} 1340 | zfs clone ${ds}@${SNAPSHOT_NAME} ${BASE_ZPATH}/${ARG_BONUS}/${DS_NAME} 1341 | done 1342 | 1343 | # Rename the VM in the configuration file 1344 | sed -i -- "s/VM_NAME=\"${VM_NAME}\"/VM_NAME=\"${ARG_BONUS}\"/g" ${VM_LIVES}/${ARG_BONUS}/vm.conf 1345 | rm ${VM_LIVES}/${ARG_BONUS}/vm.conf-- 1346 | } 1347 | 1348 | vml_copy () { 1349 | NEW_VM="${ARG_BONUS}"; 1350 | 1351 | if [ -z "${NEW_VM}" ]; then 1352 | fatal_error "Missing new VM name (sanity check)"; 1353 | fi 1354 | 1355 | echo -n "Copying '${VM_NAME}' to '${NEW_VM}' ... " 1356 | vml_send | bmt receive ${NEW_VM}; 1357 | 1358 | echo "Done"; 1359 | 1360 | if [ "${ARG_BONUS2}" == "-d" ]; then 1361 | echo -n "Removing snapshots on '${NEW_VM}' ... "; 1362 | SNAPSHOTS=`zfs list -t snap | egrep "^${BASE_ZPATH}/${NEW_VM}[/|@]" | awk '{print $1}'`; 1363 | 1364 | for SNAP in ${SNAPSHOTS}; do 1365 | zfs destroy ${SNAP}; 1366 | done 1367 | echo "Done"; 1368 | fi 1369 | } 1370 | 1371 | vml_rename () { 1372 | vml_stat 1373 | 1374 | if [ "$?" == "1" ]; then 1375 | echo "The VM cannot be renamed while running" 1376 | exit 1377 | fi 1378 | 1379 | VM_NAME_TEMP="${VM_NAME}" 1380 | VM_NAME="${ARG_BONUS}" 1381 | vml_require_no_exist 1382 | VM_NAME="${VM_NAME_TEMP}" 1383 | 1384 | zfs rename ${ZPATH} ${BASE_ZPATH}/${ARG_BONUS} 1385 | sed -i -- "s/VM_NAME=\"${VM_NAME}\"/VM_NAME=\"${ARG_BONUS}\"/g" ${VM_LIVES}/${ARG_BONUS}/vm.conf 1386 | rm ${VM_LIVES}/${ARG_BONUS}/vm.conf-- 1387 | } 1388 | 1389 | zvol_clone_check () { 1390 | # -- Make sure this VM has no zvols that are clones (and thus won't copy) 1391 | VM_ZVOL_BASE="$1"; 1392 | 1393 | if [ -z "${VM_ZVOL_BASE}" ]; then 1394 | return 1; 1395 | fi 1396 | 1397 | VM_ZVOL_LIST=`zfs list -o name | egrep "^${VM_ZVOL_BASE}(/|$)"`; 1398 | for CUR_ZVOL in ${VM_ZVOL_LIST}; do 1399 | CUR_ORIGIN=`zfs get -H -o value origin ${CUR_ZVOL}`; 1400 | if [ "${CUR_ORIGIN}" != "-" ]; then 1401 | return 1 1402 | fi 1403 | done 1404 | return 0 1405 | } 1406 | 1407 | vml_send () { 1408 | # -- Make sure this VM has no zvols that are clones (and thus won't copy) 1409 | if ! zvol_clone_check "${ZPATH}"; then 1410 | fatal_error "${CUR_ZVOL} is a clone of another zvol or is the source of another clone. Sending it somewhere isn't going to work because its reference will be missing. Use 'zfs promote ${CUR_ZVOL}' to detach it from it's base and send again. You may need to run that command multiple times if it's a nested clone."; 1411 | fi 1412 | 1413 | if [ "$FORCE" == "YES" ]; then 1414 | vml_stop 1415 | echo "Waiting for vm to poweroff..." 1416 | vml_wait_for_poweroff 1417 | echo "VM Powered off" 1418 | else 1419 | vml_stat 1420 | 1421 | if [ "$?" == "1" ]; then 1422 | echo "VM is running. Use -f flag to shutdown before migration" 1423 | exit 1424 | fi 1425 | fi 1426 | 1427 | # Actually do the export 1428 | SNAPSHOT_NAME="`date +'%s'`_send" 1429 | 1430 | zfs snapshot -r ${ZPATH}@${SNAPSHOT_NAME} 1431 | zfs send -R ${ZPATH}@${SNAPSHOT_NAME} 1432 | # zfs send -vR ${ZPATH}@${SNAPSHOT_NAME} 1433 | } 1434 | 1435 | vml_require_no_exist () { 1436 | vml_vm_exists 1437 | if [ "$?" == "1" ]; then 1438 | echo "VM ${VM_NAME} already exists." 1439 | exit 1440 | fi 1441 | } 1442 | 1443 | vml_require_exist () { 1444 | vml_vm_exists 1445 | if [ "$?" == "0" ]; then 1446 | echo "VM ${VM_NAME} doesn't exist" 1447 | exit 1448 | fi 1449 | 1450 | } 1451 | 1452 | vml_require_off () { 1453 | vml_stat 1454 | if [ "$?" == "1" ]; then 1455 | echo "VM ${VM_NAME} cannot be running" 1456 | exit 1457 | fi 1458 | } 1459 | 1460 | vml_receive () { 1461 | # Special hook for 'bmt scp' pre-send test 1462 | if [ "${VM_NAME}"x == "_scp_test_nop_ivpk5t_"x ]; then 1463 | #echo "Remote command test success."; 1464 | exit 0; 1465 | fi 1466 | 1467 | vml_require_no_exist 1468 | zfs receive ${BASE_ZPATH}/${VM_NAME} 1469 | OLD_VM_NAME=`cat ${VM_LIVES}/${VM_NAME}/vm.conf | grep VM_NAME` 1470 | sysrc -f ${VM_LIVES}/${VM_NAME}/vm.conf VM_NAME="${VM_NAME}" > /dev/null; 1471 | 1472 | } 1473 | 1474 | vml_help () { 1475 | vml_usage 1476 | } 1477 | 1478 | vml_usage (){ 1479 | echo " 1480 | USAGE: 1481 | bmt attach 1482 | bmt check 1483 | bmt create [ -V VOLSIZE[K,M,G,T etc ] 1484 | bmt copy [ -d ] 1485 | bmt clone 1486 | bmt destroy 1487 | bmt edit 1488 | bmt exec '' 1489 | bmt info 1490 | bmt list 1491 | bmt rcstart 1492 | bmt rcstop 1493 | bmt get 1494 | bmt set = 1495 | bmt netmap 1496 | bmt receive [new_vm_name] 1497 | bmt rename 1498 | bmt send 1499 | bmt start [ -d ] 1500 | bmt status 1501 | bmt stop [ -f ] 1502 | bmt scp " 1503 | } 1504 | 1505 | # Send a command to the console of a VM # 1506 | vml_exec () { 1507 | screen -S bhyve.${VM_NAME} -X log 1508 | screen -S bhyve.${VM_NAME} -X stuff "${ARG_BONUS}" 1509 | screen -S bhyve.${VM_NAME} -p0 -X eval "stuff \015" 1510 | screen -S bhyve.${VM_NAME} -X log 1511 | LINES=`cat screenlog.0 | wc -l | tr -d ' '` 1512 | HEAD_LINES=`echo "${LINES} - 1" | bc` 1513 | if [ "${HEAD_LINES}" -eq 0 ]; then 1514 | exit 0 1515 | else 1516 | printf '' 1517 | cat screenlog.0 | tail -n ${LINES} | head -n ${HEAD_LINES} 1518 | rm screenlog.0 1519 | fi 1520 | } 1521 | 1522 | vml_get () { 1523 | echo $ARG_BONUS | grep -q \= 1524 | IS_SET=$? 1525 | 1526 | if [ "${IS_SET}" == "0" ]; then 1527 | echo "Use set instead of get" 1528 | exit 1529 | fi 1530 | sysrc -nf "${VM_LIVES}/${VM_NAME}/vm.conf" ${ARG_BONUS} 1531 | } 1532 | 1533 | vml_set () { 1534 | sysrc -f "${VM_LIVES}/${VM_NAME}/vm.conf" ${ARG_BONUS} 1535 | } 1536 | 1537 | vml_create () { 1538 | # Syntax 1539 | # bmt create [ vm ] [ bonus ( volume size) 1540 | ZVOL_SIZE="10G"; 1541 | 1542 | if [ "${ARG_BONUS}"x == "-V"x ]; then 1543 | if [ -n "${ARG_BONUS2}" ]; then 1544 | ZVOL_SIZE="${ARG_BONUS2}"; 1545 | else 1546 | fatal_error "Missing flag param: 'create -V '"; 1547 | fi 1548 | fi 1549 | 1550 | if ! zfs create "${BASE_ZPATH}/$VM_NAME"; then 1551 | fatal_error "Couldn't create zvol '$BASE_ZPATH/$VM_NAME'"; 1552 | fi 1553 | if ! zfs create -V "$ZVOL_SIZE" -o refreservation=none "$BASE_ZPATH/$VM_NAME/disk"; then 1554 | fatal_error "Couldn't create zvol '$BASE_ZPATH/$VM_NAME/disk'"; 1555 | fi 1556 | 1557 | echo "#!/bin/sh 1558 | 1559 | # -- VM Basics 1560 | # 1561 | VM_NAME=\"$VM_NAME\""' 1562 | VM_QUIRK="0" # - See OS Type list below 1563 | VM_CPUS="1" # - Number of virtual CPUs 1564 | VM_MEMORY="1024" # - Guest RAM allocation 1565 | VM_ONBOOT="YES" # - Start this VM when the host boots 1566 | VM_PRE_CMD="" # - Command to run before starting 1567 | VM_POST_CMD="" # - Command to run after exiting 1568 | 1569 | # -- Booting / Disks 1570 | # 1571 | VM_BOOT_ZVOL="disk" # - The zvol sub-name to boot from 1572 | VM_BOOT_PART_TYPE="" # - The partition scheme: msdos (default) or gpt 1573 | VM_BOOT_PART="1" # - The partition to boot from 1574 | VM_DISK_FILES="" # - Disk-file alternative to BOOT_ZVOLs 1575 | VM_GRUB_DIR="" # - Override the grub dir (ie; /grub2 for LVM) 1576 | # (normally dictated by VM_QUIRK logic) 1577 | 1578 | # -- CD/DVD Media 1579 | # 1580 | VM_CDROM="0" 1581 | VM_CDROM_MEDIA="" 1582 | 1583 | # -- File-based Disk(s) 1584 | # 1585 | 1586 | # -- Networking 1587 | # ( Up to 12 NIC are possible following the same naming convention ) 1588 | # 1589 | AUTO_NETWORKING="YES" 1590 | VM_N1_BRIDGE_NUM="" 1591 | VM_N1_TAP_NUM="" 1592 | 1593 | VM_N2_BRIDGE_NUM="" 1594 | VM_N2_TAP_NUM="" 1595 | 1596 | # -- Framebuffer 1597 | # 1598 | VM_FRAMEBUFFER="0" 1599 | VM_FRAMEBUFFER_OPTS="tcp=0.0.0.0:5900,wait" 1600 | 1601 | # -- USB 3.0 1602 | # 1603 | VM_USB3="0" 1604 | 1605 | # -- PCI Passthru (see README.md for usage) 1606 | # 1607 | VM_PCI_PT="" 1608 | 1609 | # -- OS Type 1610 | # 0 FreeBSD 1611 | # 1 CentOS 6.x 1612 | # 2 CentOS 7.x 1613 | # 3 Ubuntu / Debian 1614 | # 4 Fedora 23 1615 | # 5 Arch 1616 | # 6 NetBSD 1617 | # 7 OpenBSD 5.8 1618 | # 8 Windows 10 1619 | #' > "$VM_LIVES/$VM_NAME/vm.conf" 1620 | echo "VM '${VM_NAME}' created"; 1621 | } 1622 | 1623 | vml_setup () { 1624 | mkdir -p ${VM_LIVES} && 1625 | zfs create -o compression=lz4 -o mountpoint=${VM_LIVES} ${BASE_ZPATH} 1626 | } 1627 | 1628 | vml_quirk_to_text () { 1629 | 1630 | case $VM_QUIRK in 1631 | 0) VM="FreeBSD";; 1632 | 1) VM="CentOS 6";; 1633 | 2) VM="CentOS 7";; 1634 | 3) VM="Ubuntu";; 1635 | 5) VM="Arch";; 1636 | 6) VM="NetBSD";; 1637 | 7) VM="OpenBSD";; 1638 | 8) VM="Windows 10";; 1639 | *) VM="UNKNOWN";; 1640 | esac 1641 | echo "${VM}" 1642 | 1643 | } 1644 | 1645 | vml_sanity () { 1646 | # These are the required binaries 1647 | REQUIRED_BINARIES="screen" 1648 | 1649 | for bin in $REQUIRED_BINARIES; do 1650 | if [ ! -f "`whereis -qb ${bin}`" ]; then 1651 | echo "${bin} not installed, and it is required" 1652 | FATAL="YES" 1653 | fi 1654 | done 1655 | 1656 | # These are the optional binaries 1657 | OPTIONAL_BINARIES="grub-bhyve fusefs-ext2" 1658 | 1659 | for bin in $OPTIONAL_BINARIES; do 1660 | if [ ! -f "`whereis -qb ${bin}`" ]; then 1661 | echo "${bin} not installed, but it is optional" 1662 | fi 1663 | done 1664 | 1665 | if [ "${FATAL}" == "YES" ]; then 1666 | echo "There were fatal errors" 1667 | exit 1668 | fi 1669 | 1670 | # Is the ZPATH set up yet? 1671 | BASE_ZPATH=`zfs get bvm: | grep -v inherited | grep yes | head -n 1 | cut -d " " -f 1` 1672 | if [ "${BASE_ZPATH}" == "" ]; then 1673 | echo "BMT does not appear to be set up yet. Please run bmt setup." 1674 | exit 1675 | fi 1676 | } 1677 | 1678 | vml_scp () { 1679 | # -- Make sure this VM has no zvols that are clones (and thus won't copy) 1680 | if ! zvol_clone_check "${ZPATH}"; then 1681 | fatal_error "${CUR_ZVOL} is a clone of another zvol or is the source of another clone. Sending it somewhere isn't going to work because its reference will be missing. Use 'zfs promote ${CUR_ZVOL}' to detach it from it's base and send again. You may need to run that command multiple times if it's a nested clone."; 1682 | fi 1683 | 1684 | if [ "$FORCE" == "YES" ]; then 1685 | vml_stop 1686 | echo "Waiting for vm to poweroff..." 1687 | vml_wait_for_poweroff 1688 | echo "VM Powered off" 1689 | else 1690 | vml_stat 1691 | 1692 | if [ "$?" == "1" ]; then 1693 | echo "VM is running. Use -f flag to shutdown before migration" 1694 | exit 1695 | fi 1696 | fi 1697 | 1698 | # Work out remote server and VM name 1699 | REMOTE_SERVER=`echo "${ARG_BONUS}" | awk -F : '{print $1}'`; 1700 | REMOTE_VM_NAME=`echo "${ARG_BONUS}" | awk -F : '{print $2}'`; 1701 | 1702 | if [ -z "${REMOTE_SERVER}" ]; then 1703 | fatal_error "missing remote server (check arg format)" 1704 | fi 1705 | 1706 | if [ -z "${REMOTE_VM_NAME}" ]; then 1707 | fatal_error "missing remote vm name (check arg format)" 1708 | fi 1709 | 1710 | # Actually do the export 1711 | SNAPSHOT_NAME="`date +'%s'`_scp" 1712 | 1713 | # Work out the remote user to use 1714 | REMOTE_USER="${SUDO_USER}"; 1715 | SUDO_COMMAND="sudo"; 1716 | if [ -z "${REMOTE_USER}" ]; then 1717 | REMOTE_USER="${USER}"; 1718 | SUDO_COMMAND=""; 1719 | fi 1720 | 1721 | # Test sudo 1722 | if ! ssh ${REMOTE_USER}@${REMOTE_SERVER} ${SUDO_COMMAND} -n /usr/local/ateam/sbin/bmt receive _scp_test_nop_ivpk5t_; then 1723 | echo ""; 1724 | fatal_error "Testing sudo failed"; 1725 | fi 1726 | 1727 | echo -n "Sending local VM '${VM_NAME}' to server '${REMOTE_SERVER}' as remote VM '${REMOTE_VM_NAME}' ... "; 1728 | zfs snapshot -r ${ZPATH}@${SNAPSHOT_NAME} 1729 | zfs send -R ${ZPATH}@${SNAPSHOT_NAME} | ssh ${REMOTE_USER}@${REMOTE_SERVER} ${SUDO_COMMAND} -n /usr/local/ateam/sbin/bmt receive ${REMOTE_VM_NAME}; 1730 | zfs destroy ${ZPATH}@${SNAPSHOT_NAME} 1731 | echo "Done!"; 1732 | } 1733 | 1734 | 1735 | 1736 | ############################################## MAIN 1737 | 1738 | # check setup 1739 | #vml_sanity 1740 | 1741 | 1742 | 1743 | # Argument Handling 1744 | # bmt [ action ] [ vm ] [ bonus ] 1745 | # bmt create [ vm ] 1746 | 1747 | # Flags 1748 | for arg in $* 1749 | do 1750 | case $arg in 1751 | 1752 | 1753 | f) 1754 | ARG_FORCE="YES" 1755 | ;; 1756 | 1757 | is) 1758 | # This hack allows the start command to launch screens without looping 1759 | IN_SCREEN="YES" 1760 | echo "IN SCREEN DETECTED" 1761 | ;; 1762 | 1763 | q) 1764 | ARG_QUIET="YES" 1765 | ;; 1766 | 1767 | c) 1768 | echo "Clear" 1769 | ;; 1770 | 1771 | 1772 | esac 1773 | 1774 | done 1775 | 1776 | 1777 | # Where are we executing from? 1778 | SCRIPT=$(readlink -f "$0") 1779 | 1780 | # Argument $1, action 1781 | 1782 | # Check if the first argument is set 1783 | ARG_ACTION="$1" 1784 | if [ "$ARG_ACTION" == "" ]; then 1785 | echo "Argument action not provided" 1786 | vml_usage 1787 | exit 1788 | fi 1789 | 1790 | # Make sure a valid action is provided 1791 | case $ARG_ACTION in 1792 | help) 1793 | BONUS_REQUIRED="NO" 1794 | VM_REQUIRED="NO" 1795 | ROOT_REQUIRED="NO" 1796 | ;; 1797 | 1798 | attach) 1799 | BONUS_REQUIRED="NO" 1800 | VM_REQUIRED="YES" 1801 | ROOT_REQUIRED="YES" 1802 | ;; 1803 | 1804 | scp) 1805 | BONUS_REQUIRED="YES" 1806 | VM_REQUIRED="YES" 1807 | ROOT_REQUIRED="YES" 1808 | ;; 1809 | clone) 1810 | BONUS_REQUIRED="YES" 1811 | VM_REQUIRED="YES" 1812 | ROOT_REQUIRED="NO" 1813 | ;; 1814 | copy) 1815 | BONUS_REQUIRED="YES" 1816 | VM_REQUIRED="YES" 1817 | ROOT_REQUIRED="NO" 1818 | ;; 1819 | 1820 | create) 1821 | BONUS_REQUIRED="NO" 1822 | VM_REQUIRED="NO" 1823 | if [ "$2" == "" ]; then 1824 | echo "No vm name provided to create" 1825 | exit 1826 | else 1827 | VM_NAME=$2 1828 | fi 1829 | ROOT_REQUIRED="YES" 1830 | ;; 1831 | 1832 | destroy) 1833 | BONUS_REQUIRED="NO" 1834 | VM_REQUIRED="YES" 1835 | ROOT_REQUIRED="YES" 1836 | ;; 1837 | 1838 | 1839 | edit) 1840 | BONUS_REQUIRED="NO" 1841 | VM_REQUIRED="YES" 1842 | ROOT_REQUIRED="YES" 1843 | ;; 1844 | 1845 | exec) 1846 | BONUS_REQUIRED="YES" 1847 | VM_REQUIRED="YES" 1848 | ROOT_REQUIRED="YES" 1849 | ;; 1850 | 1851 | get) 1852 | BONUS_REQUIRED="YES" 1853 | VM_REQUIRED="YES" 1854 | ROOT_REQUIRED="YES" 1855 | ;; 1856 | 1857 | check) 1858 | BONUS_REQUIRED="NO" 1859 | VM_REQUIRED="NO" 1860 | ROOT_REQUIRED="NO" 1861 | ;; 1862 | 1863 | netmap) 1864 | BONUS_REQUIRED="NO" 1865 | VM_REQUIRED="NO" 1866 | ROOT_REQUIRED="NO" 1867 | ;; 1868 | 1869 | info) 1870 | BONUS_REQUIRED="NO" 1871 | VM_REQUIRED="YES" 1872 | ROOT_REQUIRED="NO" 1873 | ;; 1874 | 1875 | list) 1876 | BONUS_REQUIRED="NO" 1877 | VM_REQUIRED="NO" 1878 | ROOT_REQUIRED="NO" 1879 | ;; 1880 | 1881 | rcstart) 1882 | BONUS_REQUIRED="NO" 1883 | VM_REQUIRED="NO" 1884 | ROOT_REQUIRED="YES" 1885 | ;; 1886 | 1887 | rcstop) 1888 | BONUS_REQUIRED="NO" 1889 | VM_REQUIRED="NO" 1890 | ROOT_REQUIRED="YES" 1891 | ;; 1892 | 1893 | receive) 1894 | BONUS_REQUIRED="NO" 1895 | VM_REQUIRED="NO" 1896 | VM_NAME="${2}" 1897 | ROOT_REQUIRED="NO" 1898 | ;; 1899 | 1900 | rename) 1901 | BONUS_REQUIRED="YES" 1902 | VM_REQUIRED="YES" 1903 | ROOT_REQUIRED="YES" 1904 | ;; 1905 | 1906 | sanity) 1907 | BONUS_REQUIRED="NO" 1908 | VM_REQUIRED="NO" 1909 | ROOT_REQUIRED="NO" 1910 | ;; 1911 | 1912 | send) 1913 | BONUS_REQUIRED="NO" 1914 | VM_REQUIRED="YES" 1915 | ROOT_REQUIRED="NO" 1916 | ;; 1917 | 1918 | set) 1919 | BONUS_REQUIRED="YES" 1920 | VM_REQUIRED="YES" 1921 | ROOT_REQUIRED="YES" 1922 | ;; 1923 | 1924 | setup) 1925 | BONUS_REQUIRED="NO" 1926 | VM_REQUIRED="NO" 1927 | SETUP_POOL="zroot" 1928 | ROOT_REQUIRED="YES" 1929 | ;; 1930 | 1931 | start) 1932 | BONUS_REQUIRED="NO" 1933 | VM_REQUIRED="YES" 1934 | ROOT_REQUIRED="YES" 1935 | ;; 1936 | 1937 | status) 1938 | BONUS_REQUIRED="NO" 1939 | VM_REQUIRED="YES" 1940 | ROOT_REQUIRED="NO" 1941 | ;; 1942 | 1943 | stop) 1944 | BONUS_REQUIRED="NO" 1945 | VM_REQUIRED="YES" 1946 | ROOT_REQUIRED="YES" 1947 | if [ "$3"=="-f" ]; then 1948 | ARG_FORCE="-f" 1949 | fi 1950 | ;; 1951 | 1952 | vm_exists) 1953 | BONUS_REQUIRED="NO" 1954 | VM_REQUIRED="NO" 1955 | VM_NAME="${2}" 1956 | ROOT_REQUIRED="NO" 1957 | ;; 1958 | 1959 | wait_for_poweroff) 1960 | BONUS_REQUIRED="NO" 1961 | VM_REQUIRED="YES" 1962 | ROOT_REQUIRED="NO" 1963 | ;; 1964 | 1965 | *) 1966 | echo "$ARG_ACTION is an invalid action" 1967 | vml_usage 1968 | exit 1; 1969 | ;; 1970 | esac 1971 | 1972 | # Are we root? 1973 | if [ "${ROOT_REQUIRED}" == "YES" ]; then 1974 | if [ "`whoami | tr -d \"\\n\"`" != "root" ]; then 1975 | echo "The $ARG_ACTION action requires root" 1976 | exit 1; 1977 | fi 1978 | fi 1979 | 1980 | # Deal with the VM_NAME, if necessary 1981 | if [ "$VM_REQUIRED" == "YES" ] ; then 1982 | VM_NAME=$2 1983 | 1984 | if [ "$VM_NAME" == "" ]; then 1985 | echo "No vm name provided" 1986 | exit 1; 1987 | fi 1988 | 1989 | vml_require_exist 1990 | 1991 | # Source the configuration 1992 | VM_ROOT="${VM_LIVES}/${VM_NAME}" 1993 | 1994 | if ! [ -d "${VM_ROOT}" ]; then 1995 | fatal_error "Invalid VM_ROOT \"${VM_ROOT}\""; 1996 | fi 1997 | 1998 | if ! [ -r "${VM_ROOT}/vm.conf" ]; then 1999 | fatal_error "vm.conf missing from ${VM_ROOT}"; 2000 | fi 2001 | 2002 | . "${VM_ROOT}/vm.conf" 2003 | 2004 | SCRIPTPATH="$VM_ROOT" 2005 | ZPATH=`get_zpath` 2006 | 2007 | if [ -z "${ZPATH}" ]; then 2008 | echo " >>> ERROR: Could not establish ZPATH for $VM_ROOT"; 2009 | exit 1; 2010 | fi 2011 | 2012 | fi 2013 | 2014 | # Deal with bonus $3 2015 | if [ -n "$3" ]; then 2016 | ARG_BONUS="$3" 2017 | fi 2018 | if [ -n "$4" ]; then 2019 | ARG_BONUS2="$4" 2020 | fi 2021 | 2022 | if [ "$BONUS_REQUIRED" == "YES" -a -z "${ARG_BONUS}" ]; then 2023 | echo "Not enough arguments" 2024 | exit 1; 2025 | fi 2026 | 2027 | # Early escape if we are listing 2028 | #if [ "$1" == "list" ] || [ "$1" == "rcstart" ] || [ "$1" == "rcstop" ]; then 2029 | # vml_$1 $@ 2030 | # exit 2031 | #fi 2032 | 2033 | # Early escape if we are creating 2034 | #if [ "$2" == "create" ] ; then 2035 | # vml_$2 $@ 2036 | # exit 2037 | #fi 2038 | 2039 | 2040 | vml_$ARG_ACTION 2041 | RET_VAL=$? 2042 | 2043 | exit ${RET_VAL} 2044 | -------------------------------------------------------------------------------- /bmt-rc: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # PROVIDE: bmt 4 | # REQUIRE: sshd cron 5 | # KEYWORD: shutdown 6 | 7 | . /etc/rc.subr 8 | 9 | name="bmt" 10 | start_cmd="${name}_start" 11 | stop_cmd="${name}_stop" 12 | status_cmd="${name}_status" 13 | rcvar=${name}_enable 14 | 15 | PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/root/bin:/usr/local/ateam/bin" 16 | 17 | bmt_start() 18 | { 19 | ${name} rcstart 20 | } 21 | 22 | bmt_stop() 23 | { 24 | ${name} rcstop 25 | } 26 | 27 | bmt_status() 28 | { 29 | ${name} list 30 | } 31 | 32 | load_rc_config $name 33 | run_rc_command "$@" 34 | 35 | --------------------------------------------------------------------------------