├── conf ├── nat.conf ├── switch.conf ├── bvmd ├── templates │ ├── linux.conf │ ├── freebsd.conf │ └── windows.conf ├── dhcp.conf └── bvm.conf ├── pkg ├── create ├── MANIFEST ├── gen └── bin │ └── +MANIFEST ├── .gitignore ├── Installation.md ├── LICENSE ├── src ├── Makefile ├── config.h ├── booter.h ├── cdisk.h ├── cnet.h ├── main.h ├── zfs.h ├── create.h ├── vnet.h ├── cdisk.c ├── config.c ├── cnet.c ├── dhcp.h ├── vm.h ├── zfs.c ├── main.c └── booter.c ├── README_zh.md ├── README.md └── bvm.8 /conf/nat.conf: -------------------------------------------------------------------------------- 1 | nat0=172.16.1.1/24 2 | nat1=10.10.30.1/24 3 | nat2=192.168.1.1/24 4 | -------------------------------------------------------------------------------- /pkg/create: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ./gen > ./bin/+MANIFEST 4 | cd ./bin 5 | pkg create -m ./ 6 | cd ../ 7 | -------------------------------------------------------------------------------- /conf/switch.conf: -------------------------------------------------------------------------------- 1 | switch0=10.0.1.0/24 2 | switch1=10.0.2.0/24 3 | switch2=10.0.3.0/24 4 | switch3= 5 | switch4= 6 | switch5= 7 | switch6= 8 | switch7= 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /src/bvm 2 | /src/bvmb 3 | /src/bvmdhcp 4 | /src/*.o 5 | /src/*~ 6 | /src/.DS_Store 7 | /.DS_Store 8 | /*~ 9 | /pkg/bin/*.txz 10 | /port/ 11 | /.vscode/ 12 | -------------------------------------------------------------------------------- /conf/bvmd: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | 4 | # Add the following lines to /etc/rc.conf to enable `bvmd': 5 | # 6 | # bvmd_enable="YES" 7 | # PROVIDE: bvmd 8 | # REQUIRE: sshd 9 | # 10 | 11 | . /etc/rc.subr 12 | 13 | name="bvmd" 14 | rcvar=bvmd_enable 15 | start_flag=" --autoboot" 16 | bvm="/usr/local/bin/bvm" 17 | 18 | start_cmd="start_bvm" 19 | 20 | load_rc_config "$name" 21 | : ${rsyncd_enable="NO"} 22 | 23 | start_bvm() 24 | { 25 | echo "checking vms ...." 26 | $bvm $start_flag 27 | } 28 | 29 | run_rc_command "$1" 30 | -------------------------------------------------------------------------------- /conf/templates/linux.conf: -------------------------------------------------------------------------------- 1 | vm_ostype="Debian" 2 | vm_ram="2g" 3 | vm_cpus="2" 4 | vm_sockets="1" 5 | vm_cores="2" 6 | vm_threads="1" 7 | vm_disks="1" 8 | vm_disk0_size="10g" 9 | vm_network_interface="virtio-net" 10 | vm_bootfrom="cd0" 11 | vm_boot_type="uefi" 12 | vm_vncstatus="on" 13 | vm_vncport="5900" 14 | vm_vncwait="on" 15 | vm_vncwidth="1024" 16 | vm_vncheight="768" 17 | vm_audiostatus="off" 18 | vm_tpmstatus="off" 19 | vm_share_status="off" 20 | vm_zfs="off" 21 | vm_hostbridge="hostbridge" 22 | vm_nics="1" 23 | vm_nic0_netmode="NAT" 24 | vm_nic0_nat="nat0" 25 | vm_nic0_rpstatus="disable" 26 | vm_nic0_ip="dhcp" 27 | -------------------------------------------------------------------------------- /conf/templates/freebsd.conf: -------------------------------------------------------------------------------- 1 | vm_ostype="FreeBSD" 2 | vm_ram="1g" 3 | vm_cpus="1" 4 | vm_sockets="1" 5 | vm_cores="1" 6 | vm_threads="1" 7 | vm_disks="1" 8 | vm_disk0_size="10g" 9 | vm_network_interface="virtio-net" 10 | vm_bootfrom="cd0" 11 | vm_boot_type="uefi" 12 | vm_vncstatus="on" 13 | vm_vncport="5900" 14 | vm_vncwait="on" 15 | vm_vncwidth="800" 16 | vm_vncheight="600" 17 | vm_audiostatus="off" 18 | vm_tpmstatus="off" 19 | vm_share_status="off" 20 | vm_zfs="off" 21 | vm_hostbridge="hostbridge" 22 | vm_nics="1" 23 | vm_nic0_netmode="NAT" 24 | vm_nic0_nat="nat0" 25 | vm_nic0_rpstatus="disable" 26 | vm_nic0_ip="dhcp" 27 | -------------------------------------------------------------------------------- /conf/templates/windows.conf: -------------------------------------------------------------------------------- 1 | vm_ostype="Windows" 2 | vm_ram="4g" 3 | vm_cpus="2" 4 | vm_sockets="1" 5 | vm_cores="2" 6 | vm_threads="1" 7 | vm_disks="1" 8 | vm_disk0_size="80g" 9 | vm_network_interface="virtio-net" 10 | vm_bootfrom="cd0" 11 | vm_boot_type="uefi" 12 | vm_vncstatus="on" 13 | vm_vncport="5900" 14 | vm_vncwait="on" 15 | vm_vncwidth="1280" 16 | vm_vncheight="800" 17 | vm_audiostatus="off" 18 | vm_tpmstatus="off" 19 | vm_share_status="off" 20 | vm_zfs="off" 21 | vm_hostbridge="hostbridge" 22 | vm_nics="1" 23 | vm_nic0_netmode="NAT" 24 | vm_nic0_nat="nat0" 25 | vm_nic0_rpstatus="disable" 26 | vm_nic0_ip="dhcp" 27 | -------------------------------------------------------------------------------- /Installation.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | ``` 3 | # pkg update 4 | # pkg install bvm 5 | ``` 6 | ### -- or -- 7 | ``` 8 | # portsnap fetch update 9 | # cd /usr/ports/sysutils/bvm/ 10 | # make install clean 11 | ``` 12 | ### -- or -- 13 | ``` 14 | // Compile source code 15 | # cd src 16 | # make 17 | # cp bvm bvmb /usr/local/bin/ 18 | # mkdir /usr/local/etc/bvm/ 19 | # cd .. 20 | # cp conf/*.conf /usr/local/etc/bvm/ 21 | # cp conf/bvmd /usr/local/etc/rc.d/ 22 | 23 | // Generate the installation package 24 | # cd pkg 25 | # ./create 26 | 27 | // Installation 28 | // bvm.txz is replaced with the installation package you generated 29 | # pkg add bvm.txz 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /conf/dhcp.conf: -------------------------------------------------------------------------------- 1 | ######################## 2 | # DHCP default parameter 3 | # The following parameters act on all network segments. 4 | ######################## 5 | lease_time=86400 6 | dns="8.8.8.8 114.114.114.114" 7 | domain_name="your.domain.com" 8 | 9 | ######################## 10 | # nat0=172.16.1.1/24 11 | # For the following parameters only net0 valid 12 | ######################## 13 | nat0_lease_time=36000 14 | nat0_dns="114.114.115.115 180.76.76.76" 15 | nat0_domain_name="nat0.doamin.com" 16 | nat0_dynamic_ip="172.16.1.100 172.16.1.254" 17 | #nat0_static_ip_01="172.16.1.2 00:00:00:00:00:00" 18 | #nat0_static_ip_02="172.16.1.3 00:00:00:00:00:00" 19 | 20 | ######################## 21 | # nat1=10.10.30.1/24 22 | # For the following parameters only net1 valid 23 | ######################## 24 | nat1_dynamic_ip="10.10.30.50 10.10.30.150" 25 | 26 | ######################## 27 | # nat2=192.168.1.1/24 28 | # For the following parameters only net2 valid 29 | ######################## 30 | nat2_dynamic_ip="192.168.1.100 192.168.1.200" 31 | -------------------------------------------------------------------------------- /pkg/MANIFEST: -------------------------------------------------------------------------------- 1 | { 2 | "name":"bvm", 3 | "origin":"sysutils/bvm", 4 | "version":"1.2.4", 5 | "comment":"bhyve vm manager", 6 | "maintainer":"guoqiang_cn@126.com", 7 | "www":"https://github.com/bigdragonsoft/bvm", 8 | "abi":"FreeBSD:11:amd64", 9 | "arch":"freebsd:11:x86:64", 10 | "prefix":"/usr/local", 11 | "flatsize":0, 12 | "licenselogic":"single", 13 | "desc":"bhyve vm manager", 14 | # "deps":{ 15 | # "tmux":{ 16 | # "origin":"sysutils/tmux", 17 | # "version":"2.5_1" 18 | # }, 19 | # "grub2-bhyve":{ 20 | # "origin":"sysutils/grub2-bhyve", 21 | # "version":"0.40_3" 22 | # }, 23 | # "bhyve-firmware":{ 24 | # "origin":"sysutils/bhyve-firmware", 25 | # "version":"1.0" 26 | # } 27 | # 28 | # }, 29 | "categories":["sysutils"], 30 | "files":{ 31 | "/usr/local/bin/bvm":"", 32 | "/usr/local/bin/bvmb":"", 33 | "/usr/local/etc/rc.d/bvmd":"", 34 | "/usr/local/etc/bvm/bvm.conf":"", 35 | "/usr/local/etc/bvm/nat.conf":"", 36 | "/usr/local/etc/bvm/switch.conf":"" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | BVM Copyright (c) 2018, Qiang Guo 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | name = bvm 2 | objs = config.o vnet.o vm.o create.o cdisk.o cnet.o zfs.o 3 | objects = main.o $(objs) 4 | booter_name = bvmb 5 | booter_objs = booter.o $(objs) 6 | dhcp_name = bvmdhcp 7 | dhcp_objs = dhcp.o $(objs) 8 | opt = -O 9 | #-Wno-deprecated-declarations 10 | #-Wno-deprecated-non-prototype 11 | #-Wno-gnu-folding-constant 12 | 13 | # Add OpenSSL link libraries 14 | LDFLAGS = -lcrypt -lssl -lcrypto 15 | 16 | all: $(name) $(booter_name) $(dhcp_name) 17 | 18 | $(name): $(objects) 19 | cc $(opt) -o $(name) $(objects) $(LDFLAGS) 20 | 21 | $(booter_name): $(booter_objs) 22 | cc $(opt) -o $(booter_name) $(booter_objs) $(LDFLAGS) 23 | 24 | $(dhcp_name): $(dhcp_objs) 25 | cc $(opt) -o $(dhcp_name) $(dhcp_objs) -lpcap -lpthread $(LDFLAGS) 26 | 27 | main.o: main.c main.h 28 | cc $(opt) -c main.c -o main.o 29 | 30 | config.o: config.c config.h 31 | cc $(opt) -c config.c -o config.o 32 | 33 | vnet.o: vnet.c vnet.h 34 | cc $(opt) -c vnet.c -o vnet.o 35 | 36 | vm.o: vm.c vm.h 37 | cc $(opt) -c vm.c -o vm.o 38 | 39 | create.o: create.c create.h 40 | cc $(opt) -c create.c -o create.o 41 | 42 | cdisk.o: cdisk.c cdisk.h 43 | cc $(opt) -c cdisk.c -o cdisk.o 44 | 45 | cnet.o: cnet.c cnet.h 46 | cc $(opt) -c cnet.c -o cnet.o 47 | 48 | booter.o: booter.c booter.h 49 | cc $(opt) -c booter.c -o booter.o 50 | 51 | zfs.o: zfs.c zfs.h 52 | cc $(opt) -c zfs.c -o zfs.o 53 | 54 | dhcp.o: dhcp.c dhcp.h 55 | cc $(opt) -c dhcp.c -o dhcp.o 56 | 57 | 58 | clean: 59 | rm -f $(objects) $(name) 60 | rm -f booter.o $(booter_name) 61 | rm -f dhcp.o $(dhcp_name) 62 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONFIG_H 2 | #define _CONFIG_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define NAME_LEN 200 9 | #define VALUE_LEN 512 10 | #define true 1 11 | #define false 0 12 | 13 | //链表节点数据结构 14 | struct _key{ 15 | char name[NAME_LEN]; 16 | char value[VALUE_LEN]; 17 | struct _key* prive; 18 | struct _key* next; 19 | }; 20 | 21 | typedef struct _key key_type; 22 | 23 | 24 | //数据链表结构体 25 | struct _list_key{ 26 | key_type *head; 27 | key_type *tail; 28 | }; 29 | 30 | typedef struct _list_key list_t; 31 | 32 | //全局变量,初始化链表地址 33 | extern list_t *list; 34 | extern list_t LIST; 35 | 36 | 37 | void init_config(const char *); //接口函数, 初始化数据结构 *** 38 | char * get_value_by_name(const char *); //接口函数,根据键值名获取键值 *** 39 | void free_config(void); //接口函数,释放动态申请内存 *** 40 | void print_config_list(void); //查看所有 key->value *** 41 | // 42 | // 43 | // 44 | int has_equal(const char *str); 45 | void add_key_to_list(key_type); 46 | void copy_data(key_type *, key_type); 47 | void read_config(const char *); 48 | int check_shot_notes(const char *); 49 | int check_long_start_notes(const char *); 50 | int check_long_end_notes(const char *); 51 | char * check_notes(int *,const char *); 52 | void * malloc_data(size_t, int); 53 | void delte_space(char *); 54 | char * get_str_in_line(char *, const char *); 55 | void make_str_to_key(char *); 56 | int has_double_quotes(const char *); 57 | char * get_str_in_long_end_notes(char *, const char *); 58 | char * get_double_qutes(char *str); 59 | void str_to_key(key_type *, char *); 60 | 61 | #endif // _CONFIG_H 62 | 63 | -------------------------------------------------------------------------------- /pkg/gen: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | sha1=`sha256 /usr/local/bin/bvm | cut -d " " -f4` 4 | sha2=`sha256 /usr/local/bin/bvmb | cut -d " " -f4` 5 | sha3=`sha256 /usr/local/etc/rc.d/bvmd | cut -d " " -f4` 6 | sha4=`sha256 /usr/local/etc/bvm/bvm.conf | cut -d " " -f4` 7 | sha5=`sha256 /usr/local/etc/bvm/nat.conf | cut -d " " -f4` 8 | sha6=`sha256 /usr/local/etc/bvm/switch.conf | cut -d " " -f4` 9 | 10 | size1=`ls -l /usr/local/bin/bvm | awk '{print $5}'` 11 | size2=`ls -l /usr/local/bin/bvmb | awk '{print $5}'` 12 | size3=`ls -l /usr/local/etc/rc.d/bvmd | awk '{print $5}'` 13 | size4=`ls -l /usr/local/etc/bvm/bvm.conf | awk '{print $5}'` 14 | size5=`ls -l /usr/local/etc/bvm/nat.conf | awk '{print $5}'` 15 | size6=`ls -l /usr/local/etc/bvm/switch.conf | awk '{print $5}'` 16 | size=`echo "$size1 + $size2 + $size3 + $size4 + $size5 + $size6" | bc` 17 | #echo flatsize=$size 18 | 19 | awk -F: '{ 20 | if (index($0,"flatsize")>0) { 21 | printf "%s:%s,\n", $1, size 22 | } 23 | else if (index($0,"/usr/local/bin/bvmb")>0) { 24 | printf "%s:\"1$%s\",\n", $1, sha2 25 | } 26 | else if (index($0,"/usr/local/bin/bvm")>0) { 27 | printf "%s:\"1$%s\",\n", $1, sha1 28 | } 29 | else if (index($0,"bvmd")>0) { 30 | printf "%s:\"1$%s\",\n", $1, sha3 31 | } 32 | else if (index($0,"bvm.conf")>0) { 33 | printf "%s:\"1$%s\",\n", $1, sha4 34 | } 35 | else if (index($0,"nat.conf")>0) { 36 | printf "%s:\"1$%s\",\n", $1, sha5 37 | } 38 | else if (index($0,"switch.conf")>0) { 39 | printf "%s:\"1$%s\"\n", $1, sha6 40 | } 41 | else { 42 | print $0 43 | } 44 | }' size="$size" sha1="$sha1" sha2="$sha2" sha3="$sha3" sha4="$sha4" sha5="$sha5" sha6="$sha6" MANIFEST 45 | 46 | -------------------------------------------------------------------------------- /pkg/bin/+MANIFEST: -------------------------------------------------------------------------------- 1 | { 2 | "name":"bvm", 3 | "origin":"sysutils/bvm", 4 | "version":"1.1.2_1", 5 | "comment":"bhyve vm manager", 6 | "maintainer":"bigdragon@chinafreebsd.cn", 7 | "www":"http://bigdragon.chinafreebsd.cn/bvm/", 8 | "abi":"FreeBSD:11:amd64", 9 | "arch":"freebsd:11:x86:64", 10 | "prefix":"/usr/local", 11 | "flatsize":304702, 12 | "licenselogic":"single", 13 | "desc":"bhyve vm manager", 14 | # "deps":{ 15 | # "tmux":{ 16 | # "origin":"sysutils/tmux", 17 | # "version":"2.5_1" 18 | # }, 19 | # "grub2-bhyve":{ 20 | # "origin":"sysutils/grub2-bhyve", 21 | # "version":"0.40_3" 22 | # }, 23 | # "bhyve-firmware":{ 24 | # "origin":"sysutils/bhyve-firmware", 25 | # "version":"1.0" 26 | # } 27 | # 28 | # }, 29 | "categories":["sysutils"], 30 | "files":{ 31 | "/usr/local/bin/bvm":"1$ca91f10d8cb249dbc2eff0ebe775d76b16c235ff149bcdfc0707fb9124b1c130", 32 | "/usr/local/bin/bvmb":"1$d1fa3fb1fec4b9bf7b39c044195d24792db50943f8ecdfad7747d54422440946", 33 | "/usr/local/etc/rc.d/bvmd":"1$6883bcdddaa51a77539b224de9928c84821bbcee45714ec1024b6800258aafe7", 34 | "/usr/local/etc/bvm/bvm.conf":"1$caf1da1699b0d90a8c2660793f4ee32b1f991ef23491d540ab36c5145961e280", 35 | "/usr/local/etc/bvm/nat.conf":"1$705a3097f83dde1aba46f3782ca6143801366b89c287dae40664256964e48911", 36 | "/usr/local/etc/bvm/switch.conf":"1$6c3afc6acae3e24148608f3f00b8c3697771441fc0df9ae59aaf414aa7835ef5" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/booter.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | BVM Copyright (c) 2018-2025, Qiang Guo (bigdragonsoft@gmail.com) 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | *---------------------------------------------------------------------------*/ 26 | 27 | #ifndef BVM_BOOTER_H 28 | #define BVM_BOOTER_H 29 | 30 | #include "main.h" 31 | 32 | #define EM0_VER 11.1 33 | 34 | float host_version(); 35 | void grub_booter(vm_node *p); 36 | void uefi_booter(vm_node *p); 37 | void convert(char *code, vm_node *p); 38 | int run(char *cmd, vm_node *p); 39 | 40 | #endif //BVM_BOOTER_H 41 | -------------------------------------------------------------------------------- /src/cdisk.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | BVM Copyright (c) 2018-2025, Qiang Guo (bigdragonsoft@gmail.com) 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | *---------------------------------------------------------------------------*/ 26 | 27 | #ifndef BVM_CREATE_DISK_H 28 | #define BVM_CREATE_DISK_H 29 | 30 | #include "create.h" 31 | 32 | enum { 33 | DISKETTE = 0, 34 | DISKFUNC = 1, 35 | }; 36 | 37 | void disk_config_init(); 38 | void create_disk_config(); 39 | void edit_disk_config(); 40 | void show_disk_config(); 41 | void add_disk(int); 42 | void delete_disk(int); 43 | int check_disk_enter_valid(); 44 | void set_disk_edit(int type, int edit); 45 | 46 | #endif //BVM_CREATE_DISK_H 47 | -------------------------------------------------------------------------------- /src/cnet.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | BVM Copyright (c) 2018-2025, Qiang Guo (bigdragonsoft@gmail.com) 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | *---------------------------------------------------------------------------*/ 26 | 27 | #ifndef BVM_CREATE_NETWORK_H 28 | #define BVM_CREATE_NETWORK_H 29 | 30 | #include "create.h" 31 | 32 | enum { 33 | BVMNETWORK = 0, 34 | BVMNETWORKFUNC = 1, 35 | }; 36 | 37 | void network_config_init(); 38 | void create_network_config(); 39 | void edit_network_config(); 40 | void show_network_config(); 41 | void add_nic(int); 42 | void delete_nic(int); 43 | void vm_del_nic(); 44 | int select_nic(); 45 | int check_network_enter_valid(); 46 | void set_network_edit(int type, int edit); 47 | 48 | #endif //BVM_CREATE_NETWORK_H 49 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | BVM Copyright (c) 2018-2025, Qiang Guo (bigdragonsoft@gmail.com) 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | *---------------------------------------------------------------------------*/ 26 | 27 | 28 | #ifndef BVM_MAIN_H 29 | #define BVM_MAIN_H 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "config.h" 38 | #include "create.h" 39 | #include "vnet.h" 40 | #include "vm.h" 41 | #include "zfs.h" 42 | 43 | typedef struct { 44 | char name[8]; 45 | char version[16]; 46 | char author[32]; 47 | char email[64]; 48 | char website[256]; 49 | } pro_stru; 50 | 51 | void usage(); 52 | void version(); 53 | 54 | #endif //BVM_MAIN_H 55 | -------------------------------------------------------------------------------- /src/zfs.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | BVM Copyright (c) 2018-2025, Qiang Guo (bigdragonsoft@gmail.com) 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | *---------------------------------------------------------------------------*/ 26 | 27 | 28 | #ifndef BVM_ZFS_H 29 | #define BVM_ZFS_H 30 | 31 | #include "vm.h" 32 | 33 | #define ZPOOL_LISTSIZE 128 34 | #define ZPOOL_BUFFERSIZE 128 35 | #define SNAPSHOT_LISTSIZE 32 36 | #define SNAPSHOT_BUFFERSIZE 128 37 | 38 | enum SNAPSHOT_ENUM { 39 | SS_ALL = 0, 40 | SS_VM, 41 | }; 42 | 43 | extern char zpool_list[][ZPOOL_BUFFERSIZE]; 44 | extern char snapshot_list[][SNAPSHOT_BUFFERSIZE]; 45 | 46 | int is_zfs_supported_by_kldstat(); 47 | int is_zfs_supported_by_config(); 48 | int support_zfs(); 49 | int get_zpool_list(); 50 | int exist_zvol(char *zvol); 51 | int exist_snapshot(char *zvol); 52 | int exist_parent_snapshot(char *zvol, char *ssname); 53 | int exist_clone_vm(char *ssname); 54 | void create_zfs_disk(vm_stru *vm, int disk_ord); 55 | void resize_zfs_disk(vm_stru *vm, char *size, int disk_ord); 56 | void link_to_zvol(vm_stru *vm, int disk_ord, char *zvol); 57 | void remove_zvol(vm_stru *vm, int disk_ord); 58 | void rename_zvol(vm_stru *vm, char *oldname, char *newname); 59 | int clone_zvol(vm_stru *vm, char *src_vm_name, char *dst_vm_name, int link); 60 | void show_snapshot_list(char *vm_name); 61 | void show_snapshot_list_all(); 62 | void vm_snapshot(char *vm_name); 63 | void vm_rollback(char *vm_name); 64 | void enter_snapshot_name(vm_stru *vm, char *ssname); 65 | void select_snapshot_name(vm_stru *vm, char *ssname); 66 | int check_ssname_spell(char *ssname); 67 | 68 | #endif //BVM_ZFS_H 69 | -------------------------------------------------------------------------------- /src/create.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | BVM Copyright (c) 2018-2025, Qiang Guo (bigdragonsoft@gmail.com) 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | *---------------------------------------------------------------------------*/ 26 | 27 | #ifndef BVM_CREATE_H 28 | #define BVM_CREATE_H 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include "vm.h" 41 | 42 | #define MM_MAX 48 //Main Menu Max 43 | #define DM_MAX 40 //Disk Menu Max 44 | #define NM_MAX 64 //Network Menu Max 45 | 46 | #define BVM_ECHO 0 //Echo 47 | #define BVM_NOECHO 1 //No Echo 48 | 49 | struct _create_stru { 50 | char desc[128]; 51 | char *value; 52 | void (*func)(int); 53 | int arg; 54 | int edit; 55 | int submenu; 56 | }; 57 | typedef struct _create_stru create_stru; 58 | 59 | extern vm_stru new_vm; 60 | extern create_stru tbl[]; 61 | extern char *options; 62 | 63 | void create_init(); 64 | void set_const_config(); 65 | void enter_vm(char *vm_name); 66 | void edit_vm(char *vm_name); 67 | 68 | void show_all_enter(); 69 | void enter_vm_name(int); 70 | void enter_vm_ostype(int); 71 | void enter_vm_version(int); 72 | void enter_vm_cpus(int); 73 | void enter_vm_ram(int); 74 | void enter_vm_zfs(int); 75 | void enter_vm_zpool(int); 76 | void enter_vm_disks(int); 77 | void enter_vm_vdisk_size(int disk_ord); 78 | void enter_vm_cdstatus(int); 79 | void enter_vm_cds(int); 80 | void enter_vm_cd_iso(int); 81 | void enter_vm_bootfrom(int); 82 | void enter_vm_boot_type(int); 83 | void enter_vm_vncstatus(int); 84 | void enter_vm_vncport(int); 85 | void enter_vm_vncwidth(int); 86 | void enter_vm_vncheight(int); 87 | void enter_vm_vncpassword(int); 88 | void enter_vm_vncwait(int); 89 | void enter_vm_vncbind(int); 90 | void enter_vm_audiostatus(int); 91 | void enter_vm_hostbridge(int); 92 | void enter_vm_autoboot(int); 93 | void enter_vm_bootindex(int); 94 | void enter_vm_bootdelay(int); 95 | void enter_vm_tpmstatus(int); 96 | void enter_vm_sharestatus(int); 97 | void enter_vm_sharename(int); 98 | void enter_vm_sharepath(int); 99 | void enter_vm_sharero(int); 100 | void enter_vm_nics(int); 101 | void enter_vm_netmode(char *msg, char *value); 102 | void enter_vm_rpstatus(char *netmode, char *value); 103 | void enter_vm_rplist(char *netmode, char *value); 104 | void enter_vm_nat(char *netmode, char *value); 105 | //void enter_vm_bind(char *netmode, char *value); 106 | void enter_vm_bind(char *netmode, char *rpstatus, char *value); 107 | void enter_vm_ip(char *value); 108 | void enter_vm_device(char *value); 109 | void enter_vm_network_interface(int); 110 | void enter_vm_storage_interface(int); 111 | 112 | void enter_vm_disk_config(int); 113 | void enter_vm_network_config(int); 114 | //void enter_vm_driver_config(int); 115 | 116 | void exit_the_menu(int); 117 | 118 | void enter_vm_netmode_proc(int nic_idx); 119 | void enter_vm_rpstatus_proc(int nic_idx); 120 | void enter_vm_rplist_proc(int nic_idx); 121 | void enter_vm_bind_proc(int nic_idx); 122 | void enter_vm_nat_proc(int nic_idx); 123 | void enter_vm_ip_proc(int nic_idx); 124 | void enter_static_ipv4(char *value); 125 | 126 | int check_portlist(char *portlist, int nic_idx); 127 | int split_portlist(int *port, char *portlist, char sep); 128 | int get_proto(char *proto, char *portlist); 129 | int set_portlist(char *ip); 130 | 131 | int get_filelist(char *dir, char **opt, char **opt_desc); 132 | int check_numbers(char *value, char *unit); 133 | int bvm_gets(char *s, int len, char echo); 134 | void enter_numbers(char *msg, char *unit, char *value); 135 | int check_version(char *value); 136 | void enter_version(char *msg, char *value); 137 | int check_options(int min, int max, char *value); 138 | void enter_options(char *msg, char **opt, char **opt_desc, char *value); 139 | void add_item(create_stru *table, char *desc, char *value, void (*func)(), int arg, int edit, int submenu); 140 | int check_enter_valid(); 141 | int strtoint(char *str); 142 | int is_edit_item(create_stru *tbl, create_stru *select[], int item); 143 | int is_non_show_item(int item); 144 | 145 | void welcome(); 146 | void goback_mainmenu(int); 147 | 148 | #endif //BVM_CREATE_H 149 | -------------------------------------------------------------------------------- /src/vnet.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | BVM Copyright (c) 2018-2025, Qiang Guo (bigdragonsoft@gmail.com) 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | *---------------------------------------------------------------------------*/ 26 | 27 | 28 | #ifndef BVM_VNET_H 29 | #define BVM_VNET_H 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include "vm.h" 38 | 39 | #define VNET_LISTSIZE 128 40 | #define VNET_BUFFERSIZE 32 41 | #define VNET_DEFAULT_BRIDGE "default-bridge" 42 | #define VNET_BRIDGED_DESC "bvm-Bridged" 43 | #define VNET_NAT_DESC "bvm-Nat" 44 | #define VNET_SWITCH_DESC "bvm-Switch" 45 | #define VNET_TRUE 1 46 | #define VNET_FALSE 0 47 | #define NAT_CONF_FN "nat.conf" 48 | #define SWITCH_CONF_FN "switch.conf" 49 | 50 | enum VNET_SWITCH_ENUM { 51 | BRIDGE = 0, //网桥 52 | TAP, //虚拟网卡 53 | LO, //回环 54 | NIC, //物理网卡 55 | NAT, 56 | SWITCH, 57 | ALL, 58 | }; 59 | 60 | enum NIC_LIST_TYPE_ENUM { 61 | CABLE_AND_WIRELESS = 0, //有线网卡和无线网卡 62 | CABLE, //有线网卡 63 | WIRELESS, //无线网卡 64 | }; 65 | 66 | enum BRIDGE_ENUM { 67 | CREATE_BRIDGE = 0, 68 | CREATE_TAP, 69 | DESTROY_BRIDGE, 70 | DESTROY_TAP, 71 | SET_NAT_IP, 72 | SET_SWITCH_IP, 73 | SET_NAT_DESC, 74 | SET_SWITCH_DESC, 75 | SET_TAP_DESC, 76 | SET_DESC, 77 | UNSET_DESC, 78 | UP_TAP, 79 | UP_BRIDGE, 80 | ADD_TAP, 81 | ADD_NIC, 82 | ADD_TAP_NIC, 83 | DEL_TAP, 84 | DEL_NIC, 85 | CUSTOM, 86 | }; 87 | 88 | enum TAP_LIST_TYPE { 89 | TL_NO_NIC = 0, 90 | TL_NIC, 91 | }; 92 | 93 | struct _nat_stru { 94 | char name[16]; 95 | char desc[32]; 96 | char ip[20]; 97 | int flag; 98 | }; 99 | typedef struct _nat_stru nat_stru; 100 | 101 | /* ----暂时不用---- 102 | struct _vnet_stru { 103 | char name[16]; 104 | int index; 105 | int switch; 106 | }; 107 | typedef struct _vnet_stru vnet_stru; 108 | 109 | struct _vnet_node { 110 | vnet_stru card; 111 | struct _vnet_node *next; 112 | }; 113 | typedef struct _vnet_node vnet_node; 114 | ------------------*/ 115 | 116 | extern char *bridge_list[]; 117 | extern char *tap_list[]; 118 | extern nat_stru *nat_list[]; 119 | extern nat_stru *switch_list[]; 120 | extern char bridge[]; 121 | extern char tap[]; 122 | extern char nic[]; 123 | extern char nic_list[][VNET_BUFFERSIZE]; 124 | extern nat_stru nat; 125 | extern nat_stru Switch; 126 | extern int cur_nic_idx; 127 | extern vm_stru *cur_vm; 128 | 129 | void redirect_port(); 130 | int search_nat_redirect(int pn, int nat_order); 131 | int run_cmd(char *cmd); 132 | int dup2_run_cmd(char *cmd); 133 | int read_redirect_rule(int *rule); 134 | int write_redirect_rule(int *rule); 135 | 136 | int create_nat(char *nat_name); 137 | int create_bridged(char *bind); 138 | int create_switch(char *switch_name); 139 | void run_bridge_command(int action); 140 | void get_nic_name(int index, char *nic); 141 | void get_new_bridge(char *bridge); 142 | void get_new_tap(char *tap); 143 | void unset_device(char *device); 144 | void add_nat(char *ip); 145 | void del_nat(char *nat_name); 146 | void set_nat(char *nat_name, char *ip); 147 | void unset_nat(char *nat_name); 148 | void nat_info(); 149 | void print_nat_list(); 150 | void add_switch(char *ip); 151 | void del_switch(char *switch_name); 152 | void set_switch(char *switch_name, char *ip); 153 | void unset_switch(char *switch_name); 154 | void switch_info(); 155 | void free_vnet_list(int type); 156 | 157 | void destroy_all_bridge(); 158 | void destroy_all_tap(); 159 | void get_bridge_desc(char *bridge, char *desc); 160 | int check_nic_in_bridge(char *bridge, char *nic); 161 | void find_desc_in_all_bridges(char *mode, char *bridge); 162 | void find_nic_in_all_bridges(char *bridge); 163 | void get_new_vnet_name(int type, char *name); 164 | int get_ip(char *ip); 165 | int check_ip(char *ip); 166 | int get_new_nat(); 167 | int get_new_switch(); 168 | 169 | void load_vnet_list(nat_stru **vnet_list, char *file); 170 | void save_vnet_list(nat_stru **vnet_list, char *file); 171 | void del_ng(char *ng_name, char *file); 172 | void set_ng(char *ng_name, char *ip, char *file); 173 | void unset_ng(char *ng_name, char *file); 174 | void add_ng(char *ip, char *file); 175 | int get_new_ng(char *file); 176 | void ng_info(char *file); 177 | char *get_ng_info(char *ng_name, char *file); 178 | 179 | void load_nat_list(); 180 | void save_nat_list(); 181 | void load_switch_list(); 182 | void save_switch_list(); 183 | void load_ng_list(nat_stru **ng_list, char *file); 184 | void save_ng_list(nat_stru **ng_list, char *file); 185 | int get_nic_list(int type); 186 | void get_lo_name(char *name); 187 | void get_lagg_name(char *name); 188 | void get_wlan_name(char *name); 189 | char *get_nat_info(char *nat_name); 190 | char *get_switch_info(char *switch_name); 191 | void get_members_in_bridge(char *bridge_name); 192 | void get_vnet_list(int type); 193 | void get_buffer(FILE *fp, int type); 194 | void sort_vnet_list(int type); 195 | int get_vnet_name_ord(char *name); 196 | int tap_list_count(int flag_nic); 197 | void print_vnet_list(int type); 198 | void free_vnet_list_proc(char **p); 199 | void free_nat_list_proc(nat_stru **p); 200 | char lastch(char *s); 201 | char *strtolower(char *s); 202 | char *strtoupper(char *s); 203 | char *rightstr(char *dst, char *src, int n); 204 | char *leftstr(char *dst, char *src, int n); 205 | void ltrim(char *s); 206 | void rtrim(char *s); 207 | void trim(char *s); 208 | 209 | #endif //BVM_VNET_H 210 | -------------------------------------------------------------------------------- /src/cdisk.c: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | BVM Copyright (c) 2018-2025, Qiang Guo (bigdragonsoft@gmail.com) 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | *---------------------------------------------------------------------------*/ 26 | 27 | #include "cdisk.h" 28 | #include "zfs.h" 29 | 30 | const int disk_enter_items = DISK_NUM + 4; //输入菜单数量 31 | const int disk_submenu_items = 3; //子菜单数量 32 | const int disk_delete_item_pos = disk_enter_items + 1; //delete菜单的偏移量 33 | 34 | create_stru diskmenu[DM_MAX] = {0}; 35 | create_stru *disk_sel[DM_MAX] = {0}; 36 | //void (*disk_sel[DM_MAX])() = {0}; 37 | 38 | // 磁盘菜单初始化 39 | void disk_config_init() 40 | { 41 | static int flag = 1; 42 | if (flag) { 43 | flag = 0; 44 | /*-------table-----desc------------value------------------------func--------------------arg-edit-submenu*/ 45 | add_item(diskmenu, "disk number", (char*)&new_vm.disks, enter_vm_disks, 0, 1, 0); 46 | for (int n=0; n max) { 113 | printf("\033[1A\033[K"); 114 | continue; 115 | } 116 | 117 | if (disk_sel[n]->func == goback_mainmenu) { 118 | if (check_disk_enter_valid() == -1) { 119 | //printf("\033[1A\033[K"); 120 | continue; 121 | } 122 | break; 123 | } 124 | if (!is_edit_item(diskmenu, disk_sel, n)) { 125 | printf("\033[1A\033[K"); 126 | continue; 127 | } 128 | if (disk_sel[n]) disk_sel[n]->func(disk_sel[n]->arg); 129 | show_disk_config(); 130 | } 131 | } 132 | 133 | // 设置磁盘菜单项的编辑属性 134 | void set_disk_edit(int type, int edit) 135 | { 136 | int n = disk_enter_items; 137 | 138 | if (type == DISKETTE) 139 | for (int i=0; i= max_options) { 186 | error("Too many disk menu items (%d) exceeds available options (%d)\n", 187 | index + 1, max_options); 188 | exit(1); 189 | } 190 | 191 | disk_sel[index] = &diskmenu[n]; 192 | //printf("[%2d]. %-13s", index, diskmenu[n].desc); 193 | printf("[%c]. %-13s", options[index], diskmenu[n].desc); 194 | if (diskmenu[n].value) 195 | printf(": %s", diskmenu[n].value); 196 | printf("\n"); 197 | ++index; 198 | } 199 | } 200 | ++n; 201 | } 202 | } 203 | 204 | // 增加磁盘 205 | void add_disk(int not_use) 206 | { 207 | warn("... add disk ...\n"); 208 | vm_add_disk(new_vm.name); 209 | load_vm_info(new_vm.name, &new_vm); 210 | 211 | //开启delete菜单 212 | diskmenu[disk_delete_item_pos].edit = 1; 213 | } 214 | 215 | // 删除磁盘 216 | void delete_disk(int not_use) 217 | { 218 | warn("... delete disk ...\n"); 219 | vm_del_disk(new_vm.name); 220 | load_vm_info(new_vm.name, &new_vm); 221 | 222 | //只有1块磁盘时关闭delete菜单 223 | if (atoi(new_vm.disks) <= 1) 224 | diskmenu[disk_delete_item_pos].edit = -1; 225 | } 226 | 227 | 228 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | /* ICEAGE 写于 2017年12月8日 2 | * 此模块用于读取配置文件中的键值 3 | * 配置文件的书写方式为: key = value 4 | * 可以过滤掉不含有 "[=]" 的行 5 | * 支持 "[#] [;] [//] [/ * * /]" 等类型注释 6 | * 支持空格过滤,如必须使用空格,支持双引号引用字符串,这样空格可以保留 7 | */ 8 | 9 | #include "config.h" 10 | 11 | 12 | list_t *list, LIST; 13 | 14 | 15 | //释放动态内存 16 | void free_config() 17 | { 18 | 19 | key_type *curr = list->head; 20 | 21 | if(curr != NULL){ 22 | for(;curr != NULL; curr = curr->next){ 23 | free(curr->prive); 24 | } 25 | } 26 | } 27 | 28 | 29 | //根据键名获取键值接口 **** 30 | char * get_value_by_name(const char * name) 31 | { 32 | key_type *curr = list->head; 33 | 34 | while(curr){ 35 | if(strcmp(curr->name, name) == 0) 36 | return curr->value; 37 | curr = curr->next; 38 | } 39 | 40 | return NULL; 41 | } 42 | 43 | 44 | 45 | //判断是否有等于号 46 | int has_equal(const char *str) 47 | { 48 | int i; 49 | 50 | for(i = 0; i < strlen(str); i++){ 51 | if(*(str + i) == 61) return true; 52 | } 53 | 54 | return false; 55 | } 56 | 57 | 58 | 59 | 60 | //读取配置文件 61 | void read_config(const char * config_name) 62 | { 63 | FILE *fp; 64 | char buf[NAME_LEN + VALUE_LEN]; 65 | int long_flag = 0; 66 | char *str; 67 | 68 | if(!(fp = fopen(config_name, "r"))){ 69 | printf("Open %s error\n", config_name); 70 | exit(0); 71 | } 72 | 73 | while(!feof(fp)){ 74 | if(fgets(buf, NAME_LEN + VALUE_LEN, fp)) { 75 | buf[strlen(buf) - 1] = '\0'; 76 | if(has_equal(buf)){ 77 | if((str = check_notes(&long_flag, buf))){ 78 | make_str_to_key(str); 79 | } 80 | } 81 | } 82 | } 83 | /* 84 | while(!feof(fp)){ 85 | fscanf(fp, "%[^\n]\n", buf); 86 | if(has_equal(buf)){ 87 | if((str = check_notes(&long_flag, buf))){ 88 | make_str_to_key(str); 89 | } 90 | } 91 | } 92 | */ 93 | fclose(fp); 94 | } 95 | 96 | //从字符串中获取键名和键值 97 | void make_str_to_key(char * str) 98 | { 99 | key_type key; 100 | 101 | str_to_key(&key, str); 102 | delte_space(key.name); 103 | 104 | has_double_quotes(key.value) ? get_double_qutes(key.value) : delte_space(key.value); 105 | add_key_to_list(key); 106 | 107 | free(str); 108 | } 109 | 110 | /* 111 | //从字符串中获取键名和键值 112 | void make_str_to_key(char * str) 113 | { 114 | key_type key; 115 | 116 | if (has_double_quotes(key.value)) 117 | get_double_qutes(key.value); 118 | else 119 | str_to_key(&key, str); 120 | 121 | delte_space(key.value); 122 | 123 | add_key_to_list(key); 124 | 125 | free(str); 126 | } 127 | */ 128 | 129 | void str_to_key(key_type * key, char * str) 130 | { 131 | int i; 132 | int j = 0; 133 | int flag = false; 134 | 135 | //printf("%s\n", str); 136 | for(i=0; iname + i) = '\0'; 141 | continue; 142 | } 143 | 144 | if(!flag){ 145 | *(key->name + i) = *(str + i); 146 | }else{ 147 | *(key->value + j) = *(str + i); 148 | j++; 149 | } 150 | } 151 | 152 | *(key->value + j) = '\0'; 153 | 154 | } 155 | 156 | 157 | 158 | 159 | 160 | char * get_double_qutes(char *str) 161 | { 162 | char tmp[VALUE_LEN]; 163 | 164 | int i; 165 | int j = 0; 166 | int flag = false; 167 | 168 | strcpy(tmp, str); 169 | memset(str, 0, strlen(str)); 170 | 171 | for(i=0; i 1) ? true : false; 199 | 200 | } 201 | 202 | 203 | 204 | //删除字符串中空格 205 | void delte_space(char *str) 206 | { 207 | char tmp[VALUE_LEN + NAME_LEN]; 208 | int i, j = 0; 209 | 210 | 211 | strcpy(tmp, str); 212 | 213 | for(i = 0; i < strlen(tmp); i++){ 214 | if(*(tmp+i) != 32){ 215 | *(str+j) = *(tmp+i); 216 | j++; 217 | } 218 | } 219 | 220 | *(str+j) = '\0'; 221 | } 222 | 223 | 224 | 225 | 226 | 227 | 228 | //检查 "[#] [;] [//] [/* */]" 等类型注释 229 | char * check_notes(int * flag, const char * str) 230 | { 231 | 232 | char * p; 233 | p = (char *)malloc_data(sizeof(char), VALUE_LEN + NAME_LEN); 234 | 235 | if(!(*flag)){ 236 | if(check_long_start_notes(str)){ 237 | *flag = 1; 238 | return get_str_in_line(p, str); 239 | }else{ 240 | return check_shot_notes(str) ? get_str_in_line(p, str) : strcpy(p,str); 241 | } 242 | }else{ 243 | if(check_long_end_notes(str)){ 244 | *flag = 0; 245 | return get_str_in_long_end_notes(p, str); 246 | } 247 | return NULL; 248 | } 249 | } 250 | 251 | 252 | //获取长注释行 [* /] 结尾后的字符串 253 | char * get_str_in_long_end_notes(char * dst, const char * str) 254 | { 255 | int i = 0; 256 | int j = 0; 257 | 258 | for(i = 0; i < strlen(str); i++) 259 | if(*(str + i) == 42 && *(str + i + 1) == 47) break; 260 | 261 | 262 | for(i += 2; i < strlen(str); i++){ 263 | if(*(str + i) != '\0'){ 264 | *(dst+j) = *(str+i); 265 | j++; 266 | } 267 | } 268 | 269 | *(dst+j) = '\0'; 270 | return dst; 271 | } 272 | 273 | 274 | 275 | // 获取注释行中的字符串 比如: [name = value # notice ....], 则获取 # 之前内容 276 | char * get_str_in_line(char *dst, const char * str) 277 | { 278 | int i = 0; 279 | int j = 0; 280 | 281 | if(*str == 35 || *str == 59 || (*str == 47 && *(str + 1) == 47) || (*(str + i) == 47 && *(str + i - 1) == 42)) 282 | return NULL; 283 | else{ 284 | for(i = 0; i < strlen(str) ; i++){ 285 | if(( *(str+i) != 35 && *(str+i) != 59) && (*(str + i) != 47 || *(str + i + 1) != 47) 286 | && (*(str + i) != 47 || *(str + i + 1) != 42)){ 287 | 288 | *(dst+j) = *(str+i); 289 | j++; 290 | }else break; 291 | } 292 | } 293 | 294 | if(i) *(dst + j) = '\0'; 295 | 296 | return dst; 297 | } 298 | 299 | 300 | 301 | 302 | //强制申请内存 303 | void * malloc_data(size_t size, int len) 304 | { 305 | void * p = NULL; 306 | 307 | while(!p) p = (void *)malloc(size * len); 308 | 309 | return p; 310 | } 311 | 312 | 313 | 314 | 315 | // 检查 "[ /* */]" 类型注释的后半部分 316 | int check_long_end_notes(const char * str) 317 | { 318 | int i; 319 | for(i = 0; i < strlen(str) - 1; i++){ 320 | if(*(str + i) == 42 && *(str + i + 1) == 47) 321 | return true; 322 | } 323 | 324 | return false; 325 | } 326 | 327 | 328 | 329 | 330 | 331 | 332 | // 检查 "[ /* */]" 类型注释的前半部分 333 | int check_long_start_notes(const char * str) 334 | { 335 | int i; 336 | for(i = 0; i < strlen(str) - 1; i++){ 337 | if(*(str + i) == 47 && *(str + i + 1) == 42) 338 | return true; 339 | } 340 | 341 | return false; 342 | } 343 | 344 | 345 | 346 | 347 | // 判断是否为 "[#] [;] [//]" 类型注释 348 | int check_shot_notes(const char * str) 349 | { 350 | int i; 351 | 352 | for(i = 0; i < strlen(str) - 1; i++){ 353 | if(*(str + i) == 35 || (*(str + i) == 47 && *(str + i + 1) == 47) || *(str + i) == 59) 354 | return true; 355 | } 356 | 357 | return false; 358 | } 359 | 360 | 361 | 362 | //初始化数据结构(双向链表) 363 | void init_config(const char * file_name) 364 | { 365 | list = &LIST; 366 | list->head = NULL; 367 | list->tail = NULL; 368 | 369 | read_config(file_name); 370 | } 371 | 372 | 373 | 374 | //链表中添加数据 375 | void add_key_to_list(key_type key) 376 | { 377 | key_type *new; 378 | 379 | new = (key_type *)malloc_data(sizeof(key_type), 1); 380 | 381 | new->prive = NULL; 382 | new->next = NULL; 383 | copy_data(new, key); 384 | 385 | if(!list->head){ 386 | list->tail = list->head = new; 387 | }else{ 388 | new->prive = list->tail; 389 | list->tail->next = new; 390 | list->tail = new; 391 | } 392 | } 393 | 394 | 395 | //复制数据到链表节点中 396 | void copy_data(key_type * dst, key_type src) 397 | { 398 | strcpy(dst->name, src.name); 399 | strcpy(dst->value, src.value); 400 | } 401 | 402 | 403 | 404 | 405 | 406 | //打印整个链表 407 | void print_config_list(void) 408 | { 409 | key_type * curr = list->head; 410 | 411 | while(curr != NULL){ 412 | printf("%s = %s\n", curr->name, curr->value); 413 | curr = curr->next; 414 | } 415 | } 416 | 417 | 418 | 419 | 420 | 421 | -------------------------------------------------------------------------------- /conf/bvm.conf: -------------------------------------------------------------------------------- 1 | # setup your vmdir path 2 | vmdir= 3 | 4 | ##################################### 5 | # Boot Mode Recommendations 6 | ##################################### 7 | # UEFI boot mode is recommended for better stability and compatibility. 8 | # Grub boot may fail with some Linux distributions due to version differences. 9 | # If grub boot fails, you can edit the grub commands below to match your OS version, 10 | # or simply switch to UEFI boot mode in VM configuration. 11 | ##################################### 12 | 13 | ##################################### 14 | # BSD Systems 15 | ##################################### 16 | 17 | ################# 18 | # FreeBSD 19 | ################# 20 | os1=FreeBSD 21 | 22 | FreeBSD_uefi_boot_enable=1 23 | FreeBSD_grub_boot_enable=1 24 | FreeBSD_grub_cmd_enable=1 25 | 26 | FreeBSD_vm_grubcmd_cd="/usr/sbin/bhyveload -m ${vm_ram} -d ${vm_iso} ${vm_name}" 27 | FreeBSD_vm_grubcmd_hdd="/usr/sbin/bhyveload -m ${vm_ram} -d ${vm_disk} ${vm_name}" 28 | 29 | ################# 30 | # OpenBSD 31 | ################# 32 | os2=OpenBSD 33 | 34 | OpenBSD_uefi_boot_enable=1 35 | OpenBSD_grub_boot_enable=1 36 | OpenBSD_grub_cmd_enable=1 37 | 38 | OpenBSD_vm_grubcmd_cd="echo -e 'kopenbsd -h com0 (cd0)/${vm_version}/amd64/bsd.rd\nboot\n' | grub-bhyve -r cd0 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 39 | OpenBSD_vm_grubcmd_hdd="echo -e 'kopenbsd -h com0 (hd0,openbsd1)/bsd -r sd0a\nboot\n' | grub-bhyve -r hd0 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 40 | 41 | ################# 42 | # NetBSD 43 | ################# 44 | os3=NetBSD 45 | 46 | NetBSD_uefi_boot_enable=1 47 | NetBSD_grub_boot_enable=1 48 | NetBSD_grub_cmd_enable=1 49 | 50 | NetBSD_vm_grubcmd_cd="echo -e 'knetbsd -h -r cd0a (cd0)/netbsd\nboot\n' | grub-bhyve -r cd0 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 51 | NetBSD_vm_grubcmd_hdd="echo -e 'knetbsd -h -r wd0a (hd0,netbsd1)/netbsd\nboot\n' | grub-bhyve -r hd0 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 52 | 53 | ################# 54 | # TrueOS 55 | ################# 56 | os4=TrueOS 57 | 58 | TrueOS_uefi_boot_enable=1 59 | TrueOS_grub_boot_enable=1 60 | TrueOS_grub_cmd_enable=1 61 | 62 | TrueOS_vm_grubcmd_cd="/usr/sbin/bhyveload -m ${vm_ram} -d ${vm_iso} ${vm_name}" 63 | TrueOS_vm_grubcmd_hdd="/usr/sbin/bhyveload -m ${vm_ram} -d ${vm_disk} ${vm_name}" 64 | 65 | ################# 66 | # pfSense 67 | # pfSense installation steps: 68 | # 1. Download pfSense-CE-2.3.5-RELEASE-4g-amd64-nanobsd.img 69 | # 2. dd if=/iso_path/pfSense-CE-2.3.5-RELEASE-4g-amd64-nanobsd.img of=/vmdir/routename/disk.img bs=1m 70 | ################# 71 | os5=pfSense 72 | 73 | pfSense_uefi_boot_enable=0 74 | pfSense_grub_boot_enable=1 75 | pfSense_grub_cmd_enable=1 76 | 77 | pfSense_vm_grubcmd_cd="/usr/sbin/bhyveload -m ${vm_ram} -d ${vm_iso} ${vm_name}" 78 | pfSense_vm_grubcmd_hdd="/usr/sbin/bhyveload -m ${vm_ram} -d ${vm_disk} ${vm_name}" 79 | 80 | ################# 81 | # OPNsense 82 | # OPNsense installation steps: 83 | # 1. Download OPNsense-18.1-OpenSSL-nano-amd64.img 84 | # 2. dd if=/iso_path/OPNsense-18.1-OpenSSL-nano-amd64.img of=/vmdir/routername/disk.img bs=1m 85 | ################# 86 | os6=OPNsense 87 | 88 | OPNsense_uefi_boot_enable=0 89 | OPNsense_grub_boot_enable=1 90 | OPNsense_grub_cmd_enable=1 91 | 92 | OPNsense_vm_grubcmd_cd="/usr/sbin/bhyveload -m ${vm_ram} -d ${vm_iso} ${vm_name}" 93 | OPNsense_vm_grubcmd_hdd="/usr/sbin/bhyveload -m ${vm_ram} -d ${vm_disk} ${vm_name}" 94 | 95 | ##################################### 96 | # Linux Systems 97 | ##################################### 98 | 99 | ################# 100 | # Debian 101 | ################# 102 | os7=Debian 103 | 104 | Debian_uefi_boot_enable=1 105 | Debian_grub_boot_enable=1 106 | Debian_grub_cmd_enable=1 107 | 108 | Debian_vm_grubcmd_cd="grub-bhyve -r cd0 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 109 | Debian_vm_grubcmd_hdd="grub-bhyve -r hd0,msdos1 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 110 | 111 | ################# 112 | # Debian_LVM 113 | ################# 114 | os8=Debian_LVM 115 | 116 | Debian_LVM_uefi_boot_enable=1 117 | Debian_LVM_grub_boot_enable=1 118 | Debian_LVM_grub_cmd_enable=1 119 | 120 | Debian_LVM_vm_grubcmd_cd="grub-bhyve -r cd0 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 121 | Debian_LVM_vm_grubcmd_hdd="echo -e 'configfile (hd0,msdos1)/grub/grub.cfg\n' | grub-bhyve -r hd0,msdos1 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 122 | 123 | ################# 124 | # Ubuntu 125 | # Grub does not support LVM partitions 126 | ################# 127 | os9=Ubuntu 128 | 129 | Ubuntu_uefi_boot_enable=1 130 | Ubuntu_grub_boot_enable=1 131 | Ubuntu_grub_cmd_enable=1 132 | 133 | Ubuntu_vm_grubcmd_cd="grub-bhyve -r cd0 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 134 | Ubuntu_vm_grubcmd_hdd="grub-bhyve -r hd0,msdos1 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 135 | 136 | ################ 137 | # Kali 138 | # Grub LVM partition installation is not successful 139 | ################# 140 | os10=Kali 141 | 142 | Kali_uefi_boot_enable=1 143 | Kali_grub_boot_enable=1 144 | Kali_grub_cmd_enable=1 145 | 146 | #Kail_vm_grubcmd_cd="echo -e 'linux /install/vmlinuz text\ninitrd /install/initrd.gz\nboot\n' | grub-bhyve -r cd0 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 147 | Kali_vm_grubcmd_cd="grub-bhyve -r cd0 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 148 | Kali_vm_grubcmd_hdd="grub-bhyve -r hd0,msdos1 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 149 | 150 | ################# 151 | # CentOS 152 | # Grub supports versions 7.0, 7.1, 7.2 153 | ################# 154 | os11=CentOS 155 | 156 | CentOS_uefi_boot_enable=1 157 | CentOS_grub_boot_enable=1 158 | CentOS_grub_cmd_enable=1 159 | 160 | CentOS_vm_grubcmd_cd="echo -e 'linux /isolinux/vmlinuz\ninitrd /isolinux/initrd.img\nboot\n' | grub-bhyve -r cd0 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 161 | CentOS_vm_grubcmd_hdd="echo -e 'configfile (hd0,msdos1)/grub2/grub.cfg\n' | grub-bhyve -r hd0,msdos1 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 162 | 163 | ################# 164 | # AlmaLinux 165 | ################# 166 | os12=AlmaLinux 167 | 168 | AlmaLinux_uefi_boot_enable=1 169 | AlmaLinux_grub_boot_enable=0 170 | AlmaLinux_grub_cmd_enable=0 171 | 172 | ################# 173 | # RockyLinux 174 | ################# 175 | os13=RockyLinux 176 | 177 | RockyLinux_uefi_boot_enable=1 178 | RockyLinux_grub_boot_enable=0 179 | RockyLinux_grub_cmd_enable=0 180 | 181 | ################# 182 | # Fedora 183 | ################# 184 | os14=Fedora 185 | 186 | Fedora_uefi_boot_enable=1 187 | Fedora_grub_boot_enable=1 188 | Fedora_grub_cmd_enable=1 189 | 190 | Fedora_vm_grubcmd_cd="echo -e 'linux /isolinux/vmlinuz\ninitrd /isolinux/initrd.img\nboot\n' | grub-bhyve -r cd0 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 191 | Fedora_vm_grubcmd_hdd="echo -e 'configfile /grub2/grub.cfg\n' | grub-bhyve -r hd0,msdos1 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 192 | 193 | ################# 194 | # openSUSE 195 | ################# 196 | os15=openSUSE 197 | 198 | openSUSE_uefi_boot_enable=1 199 | openSUSE_grub_boot_enable=1 200 | openSUSE_grub_cmd_enable=1 201 | 202 | openSUSE_vm_grubcmd_cd="echo -e 'linux /boot/x86_64/loader/linux text\ninitrd /boot/x86_64/loader/initrd\nboot\n' | grub-bhyve -r cd0 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 203 | openSUSE_vm_grubcmd_hdd="echo -e 'linux /boot/vmlinuz text\ninitrd /boot/initrd\nboot\n' | grub-bhyve -r hd0,gpt2 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 204 | 205 | ################# 206 | # openSUSE_LVM 207 | ################# 208 | os16=openSUSE_LVM 209 | 210 | openSUSE_LVM_uefi_boot_enable=1 211 | openSUSE_LVM_grub_boot_enable=1 212 | openSUSE_LVM_grub_cmd_enable=1 213 | 214 | openSUSE_LVM_vm_grubcmd_cd="echo -e 'linux /boot/x86_64/loader/linux text\ninitrd /boot/x86_64/loader/initrd\nboot\n' | grub-bhyve -r cd0 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 215 | openSUSE_LVM_vm_grubcmd_hdd="echo -e 'linux /boot/vmlinuz text\ninitrd /boot/initrd\nboot\n' | grub-bhyve -r lvm/system-root -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 216 | 217 | ################# 218 | # Gentoo 219 | # Not tested 220 | ################# 221 | os17=Gentoo 222 | 223 | Gentoo_uefi_boot_enable=1 224 | Gentoo_grub_boot_enable=1 225 | Gentoo_grub_cmd_enable=1 226 | 227 | Gentoo_vm_grubcmd_cd="echo -e 'linux /isolinux/gentoo text init=/linuxrc root=/dev/ram0 looptype=squashfs loop=/image.squashfs udev nodevfs\ncdroot=/dev/hda1\ninitrd /isolinux/gentoo.igz\nboot\n' | grub-bhyve -r cd0 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 228 | Gentoo_vm_grubcmd_hdd="echo -e 'linux /isolinux/gentoo text\ninitrd /isolinux/gentoo.igz\nboot\n' | grub-bhyve -r hd0,msdos1 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 229 | 230 | ################# 231 | # OpenEuler 232 | # Supports 25.x 233 | ################# 234 | os18=OpenEuler 235 | 236 | OpenEuler_uefi_boot_enable=1 237 | OpenEuler_grub_boot_enable=1 238 | OpenEuler_grub_cmd_enable=1 239 | 240 | OpenEuler_vm_grubcmd_cd="echo -e 'linux /isolinux/vmlinuz quiet splash console=tty0 console=ttyS0,115200n8\ninitrd /isolinux/initrd.img\nboot\n' | grub-bhyve -r cd0 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 241 | OpenEuler_vm_grubcmd_hdd="echo -e 'configfile (hd0,gpt2)/grub2/grub.cfg\n' | grub-bhyve -r hd0,gpt2 -m ${vm_devicemap} -M ${vm_ram} ${vm_name}" 242 | 243 | ################# 244 | # KylinServer 245 | ################# 246 | os19=KylinServer 247 | 248 | KylinServer_uefi_boot_enable=1 249 | KylinServer_grub_boot_enable=0 250 | KylinServer_grub_cmd_enable=0 251 | 252 | ##################################### 253 | # Solaris Systems 254 | ##################################### 255 | 256 | ################# 257 | # OpenIndiana 258 | ################# 259 | os20=OpenIndiana 260 | 261 | OpenIndiana_uefi_boot_enable=1 262 | OpenIndiana_grub_boot_enable=0 263 | OpenIndiana_grub_cmd_enable=0 264 | 265 | ##################################### 266 | # Windows Systems 267 | ##################################### 268 | 269 | ################# 270 | # Windows 271 | # Support Windows10 Windows11 WindowsServer 272 | # TPM 2.0 is already supported and the installation can proceed normally 273 | # Install steps: 274 | # 1. Download the https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.285-1/virtio-win.iso 275 | # 2. Boot with Windows image and install the system 276 | # 3. Replace the CD image with virtio-win.iso to reboot the system and install networking 277 | ################# 278 | os21=Windows 279 | 280 | Windows_uefi_boot_enable=1 281 | Windows_grub_boot_enable=0 282 | Windows_grub_cmd_enable=0 283 | 284 | ##################################### 285 | # Other 286 | ##################################### 287 | 288 | ################# 289 | # Other 290 | ################# 291 | os22=Other 292 | 293 | Other_uefi_boot_enable=1 294 | Other_grub_boot_enable=0 295 | Other_grub_cmd_enable=0 296 | -------------------------------------------------------------------------------- /src/cnet.c: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | BVM Copyright (c) 2018-2025, Qiang Guo (bigdragonsoft@gmail.com) 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | *---------------------------------------------------------------------------*/ 26 | 27 | #include "cnet.h" 28 | 29 | const int nic_menu_item_num = 6; //每块网卡所需的菜单项数量 30 | const int nic_enter_items = NIC_NUM * nic_menu_item_num + 2; //输入菜单数量 31 | const int nic_submenu_items = 3; //子菜单数量 32 | const int nic_delete_item_pos = nic_enter_items + 1; //delete菜单的偏移量 33 | 34 | create_stru networkmenu[NM_MAX] = {0}; 35 | create_stru *network_sel[NM_MAX] = {0}; 36 | //void (*network_sel[NM_MAX])() = {0}; 37 | 38 | // 网络菜单初始化 39 | void network_config_init() 40 | { 41 | static int flag = 1; 42 | if (flag) { 43 | flag = 0; 44 | add_item(networkmenu, "NIC numbers", (char*)&new_vm.nics, enter_vm_nics, 0, 1, 0); 45 | for (int n=0; n max) { 116 | printf("\033[1A\033[K"); 117 | continue; 118 | } 119 | 120 | if (network_sel[n]->func == goback_mainmenu) { 121 | if (check_network_enter_valid() == -1) { 122 | //printf("\033[1A\033[K"); 123 | continue; 124 | } 125 | break; 126 | } 127 | if (!is_edit_item(networkmenu, network_sel, n)) { 128 | printf("\033[1A\033[K"); 129 | continue; 130 | } 131 | if (network_sel[n]) network_sel[n]->func(network_sel[n]->arg); 132 | show_network_config(); 133 | 134 | } 135 | } 136 | 137 | // 设置网络菜单项的编辑属性 138 | void set_network_edit(int type, int edit) 139 | { 140 | int n = nic_enter_items; 141 | 142 | if (type == BVMNETWORK) 143 | for (int i=0; i= max_options) { 225 | error("Too many network menu items (%d) exceeds available options (%d)\n", 226 | index + 1, max_options); 227 | exit(1); 228 | } 229 | 230 | network_sel[index] = &networkmenu[n]; 231 | 232 | //根据网络模式的不同,变更对于bind的不同描述 233 | if (networkmenu[n-1].value && strcmp(networkmenu[n-1].value, "NAT") == 0) { 234 | str_replace(networkmenu[n].desc, "bind", "wan"); 235 | } 236 | if (networkmenu[n-1].value && strcmp(networkmenu[n-1].value, "Bridged") == 0) { 237 | str_replace(networkmenu[n].desc, "wan", "bind"); 238 | } 239 | 240 | printf("[%c]. %-14s", options[index], networkmenu[n].desc); 241 | if (networkmenu[n].value) 242 | printf(": %s", networkmenu[n].value); 243 | printf("\n"); 244 | ++index; 245 | } 246 | } 247 | ++n; 248 | } 249 | } 250 | 251 | // 增加网卡 252 | void add_nic(int not_use) 253 | { 254 | warn("... add nic ...\n"); 255 | if (atoi(new_vm.nics) == NIC_NUM) { 256 | error("The number of network cards reached the limit\n"); 257 | err_exit(); 258 | } 259 | sprintf(new_vm.nics, "%d", atoi(new_vm.nics) + 1); 260 | } 261 | 262 | // 删除网卡 263 | void delete_nic(int not_use) 264 | { 265 | warn("... delete nic ...\n"); 266 | vm_del_nic(); 267 | } 268 | 269 | // 删除一块网卡 270 | void vm_del_nic() 271 | { 272 | //选择网卡 273 | int n = select_nic(); 274 | 275 | //修改vm配置文件 276 | for (int i=n+1; i 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | #include "vnet.h" 56 | #include "config.h" 57 | 58 | #define DHCP_BROADCAST_FLAG 0x8000 59 | #define DHCP_INVALID -1 60 | #define DHCP_NONE 0 61 | 62 | //地址池输出类型 63 | enum OUTPUT_TYPE_ENUM { 64 | OUTPUT_TO_CONSOLE = 0, 65 | OUTPUT_TO_FILE = 1, 66 | }; 67 | 68 | // 报文类型 69 | enum DHCP_MSSAGE_TYPES_ENUM { 70 | DHCP_DISCOVER = 1, 71 | DHCP_OFFER = 2, 72 | DHCP_REQUEST = 3, 73 | DHCP_DECLINE = 4, 74 | DHCP_ACK = 5, 75 | DHCP_NAK = 6, 76 | DHCP_RELEASE = 7, 77 | DHCP_INFORM = 8, 78 | }; 79 | 80 | 81 | // options 可选项代码 82 | enum DHCP_OPTIONS_ENUM { 83 | 84 | // 用于后续字节字段在单词边界上对齐,填充功能 85 | PAD = 0, //value:0 86 | 87 | //标识报文option字段结束,后面的字节应该由pad填充 88 | END = 255, //value:0xff 89 | 90 | //子网掩码选项 91 | SUBNET_MASK = 1, 92 | 93 | //路由器选项 94 | ROUTER = 3, 95 | 96 | //指定DHCP Client可用的DNS的地址列表 97 | DOMAIN_NAME_SERVER = 6, 98 | 99 | //DHCP Client填充自己的主机名,可以是本地域名限定,也可以不限定,长度最小为1 100 | HOST_NAME = 12, 101 | 102 | //客户端通过域名系统解析主机名 103 | DOMAIN_NAME = 15, 104 | 105 | //客户端请求(DHCPDISCOVER)中,客户端请求分配特定的IP地址 106 | REQUESTED_IP_ADDRESS = 50, 107 | 108 | //客户端请求(DHCPDISCOVER或DHCPREQUEST)中,客户端请求IP 地址的租用时间。 109 | //在服务器应答(DHCPOFFER)中,DHCP服务器使用此选项指定它愿意提供的租用时间 110 | IP_ADDRESS_LEASE_TIME = 51, 111 | 112 | //DHCP 报文类型 113 | DHCP_MESSAGE_TYPE = 53, 114 | 115 | //用于DHCPOFFER和DHCPREQUEST消息中,并且可以选择包含在DHCPACK和DHCPNAK消息中。 116 | //DHCP Server在DHCPOFFER中包含此选项,以便客户端区分哪一个DHCP Server提供的服务。 117 | //DHCP Client通过在DHCPREQUEST消息中包含此选项来接收哪一个Server的服务。 118 | SERVER_IDENTIFIER = 54, 119 | 120 | //表示DHCP Client请求指定的配置参数列表。客户端可以按优先顺序列出选项。 121 | //DHCP Server不需要按请求的顺序返回选项,但必须尝试按客户端请求的顺序插入请求的选项。 122 | PARAMETER_REQUEST_LIST = 55, 123 | 124 | //DHCP服务器使用此选项在发生故障时通过DHCPNAK消息向DHCP客户端提供错误消息。 125 | //客户机可以在DHCPDECLINE消息中使用此选项来指示客户机拒绝所提供参数的原因, 126 | //客户端可以将其显示在可用的输出设备上。 127 | MESSAGE = 56, 128 | 129 | //指定从地址分配到客户端转换到续订状态的时间间隔,一般为租约时间的一半 130 | RENEWAL_T1_TIME_VALUE = 58, 131 | 132 | //指定从地址分配到客户端转换到重新绑定状态的时间间隔,一般为租约时间87.5% 133 | REBINDING_T2_TIME_VALUE = 59, 134 | 135 | //DHCP客户端使用此选项来选择性地标识DHCP客户端的类型和配置 136 | VENDOR_CLASS_IDENTIFIER = 60, 137 | 138 | //DHCP客户端使用此选项指定其唯一标识符。 139 | //DHCP服务器使用这个值来索引它们的地址绑定数据库。 140 | //此值对于管理域中的所有客户端都是唯一的。 141 | CLIENT_IDENTIFIER = 61 142 | 143 | }; 144 | 145 | // 端口 146 | enum PORTS_ENUM { 147 | DHCP_SERVER_PORT = 67, 148 | DHCP_CLIENT_PORT = 68 149 | }; 150 | 151 | // 报文操作类型 152 | enum OP_TYPES_ENUM { 153 | BOOTREQUEST = 1, //请求 154 | BOOTREPLY = 2, //应答 155 | }; 156 | 157 | // MAC 地址类型 158 | enum HARDWARE_ADDR_TYPES_ENUM { 159 | ETHERNET = 0x01, 160 | ETHERNET_LEN = 0x06, 161 | }; 162 | 163 | // DHCP 报文格式 ----------------------------------------------------------------------------- 164 | enum { 165 | DHCP_HEADER_SIZE = 236, //报文头部(options之前)所占字节数 166 | }; 167 | 168 | struct _dhcp_message_header { 169 | uint8_t op; // 报文类型 1=请求,2=应答 170 | uint8_t htype; // 客户端 MAC 地址类型 1=以太网 171 | uint8_t hlen; // 客户端 MAC 地址长度 6=以太网长度 172 | uint8_t hops; // 经过中继服务器的个数 173 | uint32_t xid; // 客户端的随机校验码 174 | uint16_t secs; // 从获取到 IP 地址或者续约开始到现在的消耗时间 175 | uint16_t flags; // 广播应答标志位 0x8000=广播 0x0000=单播 176 | uint32_t ciaddr; // 客户端 IP 地址,仅在服务器发送的 ACK 报文中显示 177 | uint32_t yiaddr; // 服务器分配给客户端的 IP 地址,仅在服务器发送 OFFER 和 ACK 报文中显示 178 | uint32_t siaddr; // 下一个为客户端分配 IP 地址的服务器 IP 179 | uint32_t giaddr; // 客户端发出请求报文后经过的第一个中继服务器的 IP 地址 180 | uint8_t chaddr[16]; // 客户端 MAC 地址 181 | uint8_t sname[64]; // DHCP 服务器名称,在 OFFER 和 ACK 报文中显示 182 | uint8_t file[128]; // 服务器为客户端指定的启动配置,仅在 OFFER 报文中显示 183 | uint8_t magic[4]; // magic cookie 0x63, 0x82, 0x53, 0x63 184 | uint8_t options[312]; // 可选项字段,长度可变 185 | }; 186 | typedef struct _dhcp_message_header dhcp_message_header; 187 | 188 | // DHCP options 189 | // 可选选项格式为 “代码 + 长度 + 数据” 190 | struct _dhcp_option { 191 | uint8_t id; // 代码 192 | uint8_t len; // 长度 193 | uint8_t data[256]; // 数据 194 | 195 | }; 196 | typedef struct _dhcp_option dhcp_option; 197 | 198 | // 由于options为可变长度,所以定义一个链表记录数据 199 | struct _dhcp_option_list { 200 | dhcp_option opts; 201 | struct _dhcp_option_list *next; 202 | }; 203 | typedef struct _dhcp_option_list dhcp_option_list; 204 | 205 | // 完整的报文格式(包含options链表) 206 | struct _dhcp_msg { 207 | dhcp_message_header hdr; 208 | dhcp_option_list *opts_list; 209 | }; 210 | typedef struct _dhcp_msg dhcp_msg; 211 | 212 | 213 | // DHCP 地址池 ----------------------------------------------------------------------------- 214 | // 地址绑定的格式 215 | enum BIND_STATUS { 216 | ERROR_STATUS = -1, // 错误状态 217 | EMPTY = 0, // 空闲 218 | ASSOCIATED, // 已分配 219 | PENDING, // 未完成 220 | EXPIRED, // 已过期 221 | RELEASED, // 已回收 222 | DISABLE, // 禁用 223 | STATIC, // 静态ip 224 | }; 225 | 226 | struct _address_bind { 227 | uint32_t ip; // ip地址 228 | uint8_t mac[16]; // mac地址 229 | uint32_t xid; // 验证码 230 | uint8_t cid[16]; // 客户端标识 231 | uint8_t cid_len; // 客户端标识长度 232 | time_t bind_time; // 绑定开始的时间 233 | int status; // 绑定的状态 234 | }; 235 | typedef struct _address_bind address_bind; 236 | 237 | // 地址绑定链表 238 | struct _address_bind_list { 239 | address_bind addr; 240 | struct _address_bind_list *next; 241 | }; 242 | typedef struct _address_bind_list address_bind_list; 243 | 244 | // DHCP 服务器 245 | struct _dhcp_server_stru { 246 | uint32_t ip; // 服务器ip 247 | uint32_t netmask; // 服务器掩码 248 | uint32_t broadcast; // 服务器广播域 249 | uint32_t gateway; // 网关 250 | char dns[128]; // DNS 251 | char domain_name[128]; // DOMAIN_NAME 252 | char ifname[16]; // 网卡(网桥)名称 253 | char nat[16]; // nat名称 254 | uint32_t lease_time; // 租赁时间长度 255 | uint32_t first_ip; // 动态ip开始 256 | uint32_t last_ip; // 动态ip结束 257 | uint32_t current_ip; // 当前要分配的动态ip 258 | 259 | address_bind bind[256]; 260 | //address_bind_list *bind_list; 261 | }; 262 | typedef struct _dhcp_server_stru dhcp_server_stru; 263 | 264 | // 网桥监控 ----------------------------------------------------------------------------- 265 | enum { 266 | REMOVE_BRIDGE = 0, //删除网桥 267 | REMOVE_MAC, //删除MAC 268 | }; 269 | 270 | enum { 271 | BRIDGE_INIT =0, //初始状态 272 | BRIDGE_WORKING, //网桥工作中 273 | BRIDGE_REMOVE, //删除网桥 274 | BRIDGE_NEW, //新增网桥 275 | }; 276 | 277 | struct _listen_dev_stru { 278 | char name[VNET_BUFFERSIZE]; //网桥名称 279 | int status; //网桥的状态 1:正在监控,2:无需监控,3:新增监控 280 | pthread_t tid; //线程id 281 | }; 282 | typedef struct _listen_dev_stru listen_dev_stru; 283 | 284 | //主机mac地址与网桥的对应结构 285 | struct _host_mac_stru { 286 | char ifname[VNET_BUFFERSIZE]; //网桥名称 287 | uint8_t mac[16]; //经过网桥服务器的MAC地址 288 | }; 289 | typedef struct _host_mac_stru host_mac_stru; 290 | 291 | //对应结构链表 292 | struct _host_mac_list { 293 | host_mac_stru host; 294 | struct _host_mac_list *next; 295 | }; 296 | typedef struct _host_mac_list host_mac_list; 297 | 298 | //给pcap_loop回调函数中传递参数的结构 299 | struct _configuration { 300 | int id; 301 | char title[255]; 302 | }; 303 | typedef struct _configuration configuration; 304 | 305 | /************函数部分**************/ 306 | 307 | //线程 308 | void *dhcp_server(); 309 | void *scan_if(); 310 | void *listen_bridge(void *net_dev); 311 | 312 | 313 | //地址池 ---------- 314 | void init_dhcp_pool(); 315 | int get_server_index(uint8_t *mac); 316 | int find_bridge_in_pool(char *dev); 317 | int find_mac_in_pool(int server_id, uint8_t *mac); 318 | int find_empty_ip_in_pool(int server_id); 319 | int get_ip_status_in_pool(int server_id, uint32_t *ip, int *bind_idx); 320 | int fresh_pool(int server_id); 321 | void init_address_bind(address_bind *bind); 322 | void fill_bind_in_pool(int server_idx, int bind_idx, int status, dhcp_msg *request); 323 | void print_dhcp_pool(int output, int server_idx, int bind_idx); 324 | void print_dhcp_pool_used(int output); 325 | 326 | 327 | //报文处理 ---------- 328 | int get_dhcp_message_type(dhcp_msg *msg); 329 | int set_option(dhcp_option *opt, uint8_t id, char *data); 330 | int load_options(dhcp_option *opt, uint8_t id, dhcp_msg *msg); 331 | int list_to_options(dhcp_msg *msg); 332 | int add_option_list(dhcp_option *opt, dhcp_option_list **list); 333 | 334 | void message_controller(int fd, struct sockaddr_in server_sock); 335 | 336 | int dhcp_discover_proc(dhcp_msg *request, dhcp_msg *reply); 337 | int dhcp_request_proc(dhcp_msg *request, dhcp_msg *reply); 338 | int dhcp_decline_proc(dhcp_msg *request, dhcp_msg *reply); 339 | int dhcp_release_proc(dhcp_msg *request, dhcp_msg *reply); 340 | int dhcp_inform_proc(dhcp_msg *request, dhcp_msg *reply); 341 | 342 | void init_reply(dhcp_msg *request, dhcp_msg *reply); 343 | void set_reply_options(dhcp_msg *reply, int dhcp_type, int server_idx, int bind_idx); 344 | int send_dhcp_reply(int s, struct sockaddr_in *client_sock, dhcp_msg *reply); 345 | 346 | void init_option_list(dhcp_option_list *list); 347 | void destroy_option_list(dhcp_option_list *list); 348 | void print_option_list(dhcp_option_list *list); 349 | void print_options(dhcp_msg *msg); 350 | void print_dhcp_msg(dhcp_msg *msg); 351 | 352 | int parse_byte(char *s, void **p); 353 | int parse_byte_list (char *s, void **p); 354 | int parse_short(char *s, void **p); 355 | int parse_short_list(char *s, void **p); 356 | int parse_long(char *s, void **p); 357 | int parse_string(char *s, void **p); 358 | int parse_ip(char *s, void **p); 359 | int parse_ip_list(char *s, void **p); 360 | int parse_mac(char *s, void **p); 361 | uint8_t parse_option(dhcp_option *opt, char *name, char *value); 362 | 363 | 364 | //监控网桥 ---------- 365 | void get_bridge_name(listen_dev_stru *dev); 366 | int get_bridge_num(listen_dev_stru *dev); 367 | void bridge_cmp(listen_dev_stru *cur_dev, listen_dev_stru *new_dev); 368 | 369 | void create_listen(listen_dev_stru *dev); 370 | void get_packet(u_char *dev, const struct pcap_pkthdr *hdr, const u_char *packet); 371 | 372 | struct in_addr *get_interfaces_ip(const char *ifname); 373 | struct in_addr *get_interfaces_netmask(const char *ifname); 374 | struct in_addr get_interfaces_broadcast(const char *ifname); 375 | int get_interfaces_mac(char *ifname, uint8_t *mac); 376 | 377 | int add_client_list(host_mac_stru *host); 378 | void destroy_client_list(); 379 | host_mac_stru *find_client(host_mac_stru *host); 380 | int remove_client(int type, host_mac_stru *host); 381 | int print_client_list(); 382 | 383 | 384 | //辅助函数 ---------- 385 | char *ip_to_str(uint32_t ip); 386 | char *mac_to_str(uint8_t *mac); 387 | char *status_to_str(int status); 388 | char *time_to_str(time_t time); 389 | 390 | 391 | #endif //BVM_DHCP_H 392 | -------------------------------------------------------------------------------- /src/vm.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | BVM Copyright (c) 2018-2025, Qiang Guo (bigdragonsoft@gmail.com) 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | *---------------------------------------------------------------------------*/ 26 | 27 | #ifndef BVM_VM_H 28 | #define BVM_VM_H 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | #define TABSTOP 8 58 | #define BUFFERSIZE 512 59 | #define FN_MAX_LEN 512 60 | #define CMD_MAX_LEN 256 61 | #define PORT_LIST_LEN 256 62 | #define PROTO_LEN 4 //协议字符最大长度 tcp/udp 63 | #define OS_NUM 32 //最大操作系统类型数量 64 | #define DISK_NUM 8 //最大磁盘数量 65 | #define CD_NUM 4 //最大CD数量 66 | #define NIC_NUM 8 //最大网卡数量 67 | #define PORT_NUM 16 //最大端口转发数量 68 | #define MAX_BOOT_NUM 32 //最大自动启动数量 69 | #define NAT_ORDER 1029 //防火墙规则中nat的序列号 70 | #define CRYPT_BUFFER 1048576 //加密数据缓冲区大小为1M字节 71 | #define CRYPT_LEN 100 //加密数据总长度为100M字节 72 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) 73 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) 74 | 75 | // 加密相关常量 76 | #define MAGIC_MARK "AESMARK!" 77 | #define HEADER_SIZE (8 + 16) // 8字节标记 + 16字节IV 78 | 79 | //#define BVM_DEBUG 80 | 81 | extern char *osdir; 82 | extern char vmdir[]; 83 | extern char *dhcp_pool_file; 84 | 85 | enum DEBUG_COLOR_ENUM { 86 | NOCOLOR = 0, 87 | RED, 88 | GREEN, 89 | YELLOW, 90 | RED_FLASH, 91 | GREEN_FLASH, 92 | YELLOW_FLASH, 93 | }; 94 | 95 | enum VM_LIST_ENUM { 96 | VM_SHORT_LIST = 0, 97 | VM_LONG_LIST = 1, 98 | }; 99 | 100 | enum VM_LIST_SORT_ENUM { 101 | LS_BY_NAME = 0, 102 | LS_BY_IP, 103 | LS_BY_OS, 104 | LS_BY_STATUS, 105 | }; 106 | 107 | enum VM_ENUM { 108 | VM_OFF = 0, 109 | VM_ON, 110 | VM_ALL, 111 | }; 112 | 113 | enum RETURN_ENUM { 114 | RET_FAILURE = -1, 115 | RET_SUCCESS = 0, 116 | }; 117 | 118 | enum SHOW_DEVICE_ENUM { 119 | SD_CLASSICAL = 0, 120 | SD_SIMPLE, 121 | }; 122 | 123 | enum SCAN_PORT_ENUM { 124 | SP_SHOW = 0, 125 | SP_VALID, 126 | }; 127 | 128 | struct _os_stru { 129 | char type[32]; 130 | int uefi_boot; 131 | int grub_boot; 132 | int grub_cmd; 133 | }; 134 | typedef struct _os_stru os_stru; 135 | extern os_stru bvm_os[]; 136 | 137 | struct _redirect_stru { 138 | char proto[PROTO_LEN]; //协议类型 protocol 139 | int vm_port; //虚拟机端口 virtual machine port 140 | int host_port; //宿主机端口 host machine port 141 | }; 142 | typedef struct _redirect_stru redirect_stru; 143 | 144 | struct _scan_redirect_port_stru { //用于扫描端口 145 | char *vm_name; 146 | redirect_stru *port; 147 | int *ret; 148 | }; 149 | typedef struct _scan_redirect_port_stru scan_redirect_port_stru; 150 | 151 | struct _find_vm_stru { 152 | char *vm_name; 153 | int *nic_index; 154 | }; 155 | typedef struct _find_vm_stru find_vm_stru; 156 | 157 | struct _nic_stru { 158 | char name[32]; //网卡名称 159 | char netmode[32]; //网络模式 160 | char nat[32]; //NAT 161 | char rpstatus[32]; //是否端口转发 162 | int rpnum; //端口转发的数量 163 | redirect_stru ports[PORT_NUM]; //端口映射表 164 | char rplist[PORT_LIST_LEN]; //端口映射列表 (80:80,22:2224) 165 | char bridge[32]; //网桥 166 | char tap[32]; //tap 167 | char ip[32]; //ip 168 | char mac[32]; //mac 169 | char bind[32]; //绑定宿主的网卡名称 170 | }; 171 | typedef struct _nic_stru nic_stru; 172 | 173 | struct _disk_stru { 174 | char name[32]; //磁盘名称 175 | char size[32]; //磁盘大小 176 | char path[256]; //磁盘路径 177 | }; 178 | typedef struct _disk_stru disk_stru; 179 | 180 | struct _vm_stru { 181 | char name[32]; //名称 182 | char profile[256]; // 183 | char zfs[8]; //是否使用zfs创建磁盘 (on/off) 184 | char zpool[32]; //zpool存储池 185 | char disks[8]; //磁盘数 186 | disk_stru vdisk[DISK_NUM]; //磁盘信息 187 | char ram[32]; //内存 188 | char cpus[8]; //cpu数量 189 | char sockets[8]; // CPU 插槽数,默认 1 190 | char cores[8]; // 每插槽核心数,默认等于 cpus 191 | char threads[8]; // 每核心线程数,默认 1 192 | 193 | char ostype[32]; //操作系统类型 194 | char version[32]; //操作系统版本 195 | char cdstatus[8]; //是否使用CD (on/off) 196 | char cds[8]; //CD数量 197 | char cd_iso[CD_NUM][256]; //多CD ISO路径 198 | char iso[256]; //iso (向后兼容,使用第一个CD路径) 199 | char bootfrom[32]; //启动介质 (cd0/hd0) 200 | char hostbridge[32]; //hostbridge 201 | char boot_type[32]; //是否uefi启动 (grub/uefi/uefi_csm) 202 | char uefi_vars[256]; //UEFI变量文件路径 (用于持久化UEFI设置) 203 | char disk[256]; //磁盘文件路径 204 | char devicemap[256]; //devicemap路径 205 | char grubcmd[512]; //grub启动命令 206 | char grubcd[512]; //grub-cd启动命令 207 | char grubhd[512]; //grub-hd启动命令 208 | 209 | char nics[8]; //网卡数量 210 | nic_stru nic[NIC_NUM]; //网卡信息 211 | 212 | char vncstatus[8]; //是否开启vnc (on/off) 213 | char vncport[8]; //vnc port 214 | char vncwidth[8]; //vnc 窗口宽度 215 | char vncheight[8]; //vnc 窗口高度 216 | char vncpassword[32]; //vnc 密码 (optional) 217 | char vncwait[8]; //vnc wait选项 (on/off) 218 | char vncbind[32]; //vnc 绑定地址 (default: 0.0.0.0) 219 | 220 | char audiostatus[8]; //是否开启音频 (on/off) 221 | 222 | char autoboot[8]; //是否随宿主机自动启动 (on/off) 223 | char bootindex[8]; //启动序号 224 | char bootdelay[8]; //启动延迟 225 | 226 | char status[16]; //虚拟机状态 (on/off) 227 | char lock[4]; //是否锁定 (0/1) 228 | char crypt[4]; //是否加密 (0/1) 229 | char booter[16]; //启动器 (bvmb) 230 | 231 | char tpmstatus[8]; //TPM状态 (on/off) 232 | char tpmversion[8]; //TPM版本 (2.0) 233 | char tpmpath[256]; //TPM socket路径 234 | 235 | char network_interface[16]; //网络接口驱动 236 | char storage_interface[16]; //存储接口驱动 237 | 238 | char share_status[8]; //VirtIO-9P共享状态 (on/off) 239 | char share_name[64]; //共享名称 (guest中使用的标识) 240 | char share_path[256]; //主机共享目录路径 241 | char share_ro[8]; //只读模式 (on/off) 242 | }; 243 | typedef struct _vm_stru vm_stru; 244 | 245 | struct _vm_node { 246 | vm_stru vm; 247 | struct _vm_node *next; 248 | }; 249 | typedef struct _vm_node vm_node; 250 | extern vm_node *vms; 251 | extern vm_node *boot[]; 252 | 253 | struct _copy_stru { 254 | char *src; 255 | char *dst; 256 | int disks; 257 | }; 258 | typedef struct _copy_stru copy_stru; 259 | 260 | struct _autoboot_stru { 261 | char *vm_name; 262 | int delay; 263 | }; 264 | typedef struct _autoboot_stru autoboot_stru; 265 | typedef void *(*fun)(void*); 266 | 267 | // 命令行配置选项结构体 (用于 --create from 命令) 268 | struct _create_opts_stru { 269 | int use_grub; // -s 启动类型为 grub 270 | char cpus[8]; // -U=N CPU 数量 271 | char ram[32]; // -m=SIZE 内存大小 272 | char disk_size[32]; // -d=SIZE 第一块磁盘大小 273 | char netmode[32]; // -n=bridge/nat 网络模式 274 | char bind_nic[32]; // -i=NIC 绑定网卡 275 | }; 276 | typedef struct _create_opts_stru create_opts_stru; 277 | 278 | char *bvm_strcpy(char *dst, const char *src); 279 | int write_log_time(); 280 | int write_log(char *fmt, ...); 281 | void set_vmdir(); 282 | void set_bvm_os(); 283 | void check_bre(); 284 | void auto_fix_conf(char *conf_file, char *key, char *value); 285 | void vm_init(); 286 | void vm_end(); 287 | 288 | void vm_create(char *vm_name, char *template_vm_name, create_opts_stru *opts); 289 | void vm_config(char *vm_name); 290 | void vm_start(char *vm_name); 291 | void vm_login(char *vm_name); 292 | void vm_stop(char *vm_name); 293 | void vm_restart(char *vm_name); 294 | void vm_killsession(char *vm_name); 295 | void vm_poweroff(char *vm_name, int flag_msg); 296 | int vm_clone(char *src_vm_name, char *dst_vm_name); 297 | int vm_rename(char *old_vm_name, char *new_vm_name); 298 | int vm_remove(char *vm_name, int skip_confirm, int show_list); 299 | void vm_add_disk(char *vm_name); 300 | void vm_del_disk(char *vm_name); 301 | void vm_list(int list_type, char *index_key, int online_only); 302 | void vm_show_device_all(int show_type); 303 | void vm_show_nat(int show_type); 304 | void vm_show_switch(int show_type); 305 | void vm_show_device_name(char *device); 306 | void vm_show_device(char *device, int show_type); 307 | void vm_info(char *vm_name); 308 | void vm_info_all(char *vm_name); 309 | void vm_os_list(); 310 | void vm_lock_all(int flag); 311 | void vm_lock(char *vm_name, int flag); 312 | void vm_crypt(char *vm_name, int flag); 313 | void vm_clean(); 314 | void vm_show_ports(int show_type, scan_redirect_port_stru *check); 315 | void vm_autoboot(); 316 | void vm_autoboot_list(); 317 | int vm_booting(autoboot_stru *boot); 318 | void vm_boot_from_hd(char *vm_name); 319 | 320 | int find_vm_by_ip(char *ip, find_vm_stru *result, vm_stru *self); 321 | int scan_port(int scan_type, vm_stru *vm, scan_redirect_port_stru *check); 322 | void show_port(vm_stru *vm, int nic_index); 323 | int is_valid_port(vm_stru *vm, int nic_index, scan_redirect_port_stru *check); 324 | 325 | void create_vm_disk_all(vm_stru *vm); 326 | void create_vm_disk(vm_stru *vm, int disk_ord); 327 | void adjust_vm_disk_all(vm_stru *vm); 328 | void adjust_vm_disk(vm_stru *vm, char *size, int disk_ord); 329 | long imgsize_cmp(char *size1, char *size2); 330 | long disk_size_to_kb(char *size); 331 | double total_disk_size(vm_stru *vm); 332 | void unit_convert(double n, int flag_int, char *save); 333 | int is_integer(double num); 334 | 335 | int write_vm_device_map(vm_stru *vm); 336 | //int copy_vm_disk(char *src_vm_name, char *dst_vm_name); 337 | int copy_vm_disk(copy_stru *name); 338 | 339 | void show_dhcp_pool(); 340 | 341 | void create_vm_list(); 342 | void destroy_vm_list(); 343 | void add_to_vm_list(char *vm_name); 344 | void sort_vm_list(int type); 345 | void load_vm_config_from_path(char *filename, vm_stru *vm); 346 | void load_vm_info(char *vm_name, vm_stru *vm); 347 | void save_vm_info(char *vm_name, vm_stru *vm); 348 | void show_vm_name(int status); 349 | void select_vm(char *vm_name, int status); 350 | void print_vm_list(int list_type, int online_only); 351 | void print_vm_net_stat(); 352 | vm_node* find_vm_list(const char *vm_name); 353 | int del_from_vm_list(char *vm_name); 354 | void get_vm_name(char *dir); 355 | void update_vm_status(char *vm_name, int status); 356 | int get_vm_status(const char *vm_name); 357 | int get_vm_count(); 358 | int vm_online_count(); 359 | int check_vm_files(char *vm_name); 360 | void gen_uefi_boot_code(char **code, vm_node *p); 361 | void gen_grub_boot_code(char **code, vm_node *p); 362 | int write_boot_code(char **code, vm_node *p); 363 | int gen_vm_start_code(char *vm_name); 364 | int create_networking(char *vm_name); 365 | void set_grub_cmd(vm_node *p); 366 | void str_replace(char *str, char *ostr, const char *nstr); 367 | void get_bvm_os(os_stru *os); 368 | int support_uefi(char *os); 369 | int support_grub(char *os); 370 | int get_mac_from_bridge(char *bridge, char *tap, char *mac); 371 | void generate_mac(char *mac); 372 | int get_ip_from_dhcp_pool(char *mac, char *ip); 373 | void get_display_ip(vm_stru *vm, char *ip_buf); 374 | int disk_offset(vm_stru *vm); 375 | int check_vm_disks(vm_stru *vm); 376 | int select_disk(vm_stru *vm); 377 | int bvm_get_pid(char *name); 378 | int get_vm_pid(vm_stru *vm); 379 | int get_bvmb_pid(vm_stru *vm); 380 | int exist_hw_vmm_vmx_initialized(vm_stru *vm); 381 | int get_vmx(vm_stru *vm); 382 | void file_lock(char *file, int flag); 383 | void crypt_write(char *file, unsigned char *s, int index, int size); 384 | int crypt_read(char *file, unsigned char *s, int index, int size); 385 | void bvm_xor(unsigned char *s, char *passwd); 386 | int bvm_crypt_chunk(unsigned char *data, int len, unsigned char *key, unsigned char *iv, int encrypt); 387 | void bvm_crypt(unsigned char *data, char *passwd); 388 | void clean_tap(char *tap_name); 389 | void clean_bridge(char *bridge_name); 390 | int check_spell(char *vm_name); 391 | int check_shutdown(char *cmd); 392 | int is_nic(char *nic_name); 393 | int max_vm_name_len(); 394 | void gen_autoboot(int *count); 395 | int waitting_boot(char *vm_name); 396 | void err_exit(); 397 | int wait_exec(fun func, void *args); 398 | void waitting(); 399 | void delay(int sec); 400 | int debug(unsigned color, char *fmt, ...); 401 | int error(char *fmt, ...); 402 | int WARN(char *fmt, ...); 403 | int warn(char *fmt, ...); 404 | int success(char *fmt, ...); 405 | int red(char *fmt, ...); 406 | int green(char *fmt, ...); 407 | int title(char *fmt, ...); 408 | 409 | unsigned long long get_cpu_frequency(); 410 | void format_cpu_time(unsigned long long ticks, char *buf, size_t bufsize, unsigned long long cpu_freq); 411 | void format_bytes(unsigned long long bytes, char *buf, size_t bufsize); 412 | void vm_show_stats(const char *vm_name); 413 | unsigned long long parse_size(const char *size_str); 414 | void test(); 415 | #endif //BVM_VM_H 416 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | [English](README.md) | [中文](README_zh.md) 2 | 3 | ## 简介 4 | BVM 是一个基于 FreeBSD 的 Bhyve 虚拟机管理工具。它提供了简单易用的命令行界面,让用户可以方便地创建、配置和管理虚拟机。BVM 支持多种主流操作系统,并提供灵活的网络和存储配置,是一个功能强大的虚拟化管理工具。 5 | 6 | ## 功能特性 7 | 1. 支持多种主流操作系统,包括: 8 | - BSD 系统如 FreeBSD、OpenBSD、NetBSD 9 | - Linux 发行版如 Debian、Ubuntu、OpenSuse、CentOS、Kali 10 | - Windows 系统如 Windows 10、Windows 11 11 | 2. 灵活的存储配置: 12 | - 支持为每个虚拟机添加多个虚拟磁盘 13 | - 支持动态添加和删除磁盘 14 | - 多种存储接口:AHCI、VirtIO-BLK、NVMe 15 | - 支持 ZFS 存储,具有快照和数据保护功能 16 | - 多光驱支持(最多 4 个 CD,用于驱动安装等场景) 17 | 3. 强大的网络功能: 18 | - 支持为每个虚拟机配置多个网卡 19 | - 支持桥接、NAT 和 Switch 网络模式 20 | - NAT 模式下支持端口转发 21 | - 内置 DHCP 服务器,`bvm --ll` 可显示动态 IP 22 | 4. 多种引导方式: 23 | - 支持传统的 GRUB 引导 24 | - 支持现代的 UEFI 引导(包含 UEFI 变量持久化支持) 25 | 5. VNC 和显示: 26 | - 可配置 VNC 绑定地址、端口、分辨率 27 | - VNC 密码保护 28 | - VNC wait 选项用于启动同步 29 | - HDA 音频设备支持 30 | 6. 高级功能: 31 | - 支持 TPM 2.0 (可信平台模块),用于 Windows 11 32 | - VirtIO-9P 共享文件夹(与虚拟机共享宿主机目录) 33 | - CPU 拓扑控制(sockets、cores、threads) 34 | - 虚拟机加密保护 35 | - 自动启动配置(支持启动顺序) 36 | - 快照和回滚支持 37 | - 完整的命令行管理界面 38 | 39 | ## 安装 40 | ``` 41 | # pkg update 42 | # pkg install bvm 43 | ``` 44 | ### -- or -- 45 | ``` 46 | # portsnap fetch update 47 | # cd /usr/ports/sysutils/bvm/ 48 | # make install clean 49 | ``` 50 | 51 | ## 基本配置 52 | 在运行之前,需要编辑 /usr/local/etc/bvm/bvm.conf 文件,设置虚拟机存储目录 53 | ``` 54 | vmdir=/your/vm/dir/ 55 | ``` 56 | 57 | ## 使用方法 58 | ``` 59 | Usage: bvm [args...] 60 | Options: 61 | --abinfo Display information about auto-boot VMs 62 | --addisk Add a new disk to VM 63 | --addnat Add NAT 64 | --addswitch Add Switch 65 | --autoboot Auto-boot VMs 66 | --clone Clone VM 67 | --config Configure VM 68 | --create Create new VM 69 | --deldisk Delete a disk 70 | --delnat Delete NAT 71 | --delswitch Delete Switch 72 | --swinfo Output Switch info 73 | --decrypt Decrypt VM 74 | --encrypt Encrypt VM 75 | --login Log in to VM 76 | --ls List VMs and status 77 | --ll List VMs and status in long format 78 | --netstat Show VM network status 79 | --natinfo Output NAT info 80 | --lock Lock VM 81 | --lockall Lock all VMs 82 | --os Output OS list 83 | --poweroff Force power off 84 | --reload-nat Reload NAT redirect port 85 | --remove Destroy VM(s) 86 | --rename Rename VM 87 | --restart Restart VM 88 | --reboot Restart VM (alias for --restart) 89 | --rollback Roll back to snapshot point 90 | --setnat Set NAT IP address 91 | --setsw Set Switch IP address 92 | --setpr Set port redirection list 93 | --showpr Show port redirection list 94 | --showdev Show device 95 | --showdevall Show all devices in class mode 96 | --showdevuse Show all devices in simple mode 97 | --showdhcp Show all DHCP clients 98 | --showsnap Show snapshot list of VM 99 | --showsnapall Show snapshot list of all VMs 100 | --showstats Show VM stats 101 | --snapshot Generate snapshot for VM 102 | --start Start VM 103 | --stop Stop VM 104 | --unlock Unlock VM 105 | --unlockall Unlock all VMs 106 | --unsetsw Unset Switch IP address 107 | --vminfo Output VM info 108 | ``` 109 | 110 | ## 常见问题 111 | 112 | ### 问题 1: 如何创建虚拟机? 113 | ``` 114 | 答: 您可以通过以下三种方式创建虚拟机: 115 | 116 | 1. 使用命令 'bvm --create vmname' 创建 117 | 118 | 2. 使用现有虚拟机配置作为模板: 119 | 120 | bvm --create newname from template-vmname 121 | 122 | - template-vmname: 现有虚拟机名称(模板) 123 | - newname: 新虚拟机名称 124 | 该命令将复制模板虚拟机的配置到新虚拟机中进行定制。 125 | 126 | 可用标准模板: 127 | - freebsd (FreeBSD 标准配置) 128 | - linux (Linux 标准配置) 129 | - windows (Windows 标准配置) 130 | 131 | 您也可以使用任何现有的虚拟机名称作为模板。 132 | 133 | 模板创建的可选参数: 134 | -s 设置启动类型为 grub 135 | -U=N 设置 CPU 数量(例如 -U=4) 136 | -m=SIZE 设置内存大小(例如 -m=512m、-m=2g) 137 | -d=SIZE 设置第一块磁盘大小(例如 -d=10g、-d=1t) 138 | -n=MODE 设置网络模式(bridge 或 nat) 139 | -i=NIC 设置绑定网卡(例如 -i=em0) 140 | 141 | 示例: 142 | bvm --create myvm from linux -s -U=4 -m=512m -d=10g -n=bridge -i=em0 143 | 144 | 3. 克隆现有虚拟机: 145 | 146 | bvm --clone oldname newname 147 | 148 | - oldname: 现有虚拟机名称 149 | - newname: 新虚拟机名称 150 | 该命令将创建一个虚拟机的完整副本。 151 | ``` 152 | ### 问题 2: 如何修改虚拟机配置? 153 | ``` 154 | 答: 虚拟机必须关闭,然后使用命令 'bvm --config vmname' 修改设置 155 | 以下是一些配置参数解释: 156 | 157 | 参数 描述 158 | --------- ----------- 159 | cpus VM使用的CPU数量(总虚拟核心数) 160 | ram VM分配的内存(例如512M或1G) 161 | CD 启用/禁用光驱 (on/off) 162 | cd numbers 光驱数量 (1-4) 163 | cd(N) iso 第 N 个光驱的 ISO 路径(输入 '-' 可清空 cd1-cd3) 164 | ios path ISO镜像目录(自动列出供选择) 165 | boot from 启动选项 (cd0:CD启动/hd0:硬盘启动) 166 | boot type 启动方式 (grub: 标准引导, uefi: UEFI引导, uefi_csm: UEFI CSM/兼容传统BIOS) 167 | TPM (UEFI) 启用TPM 2.0支持(需要UEFI,Windows 11必需) 168 | shared folder 共享文件夹(与虚拟机共享宿主机目录) 169 | VNC 启用/禁用VNC显示 170 | VNC bind VNC服务器绑定地址(默认0.0.0.0) 171 | VNC port VNC服务器端口号 172 | VNC width/height VNC显示分辨率 173 | VNC password VNC连接密码(可选) 174 | VNC wait 启动前等待VNC连接 175 | audio 启用HDA音频设备 176 | auto boot 自启动配置(见bvm --autoboot) 177 | hostbridge CPU架构 (intel:hostbridge/AMD:amd_hostbridge) 178 | disk config 磁盘配置(可添加/删除磁盘,设置存储接口:ahci-hd/virtio-blk/nvme) 179 | network config 网络配置(Bridge/NAT/Switch模式) 180 | ``` 181 | ### 问题 3: 如何查看虚拟机配置信息? 182 | ``` 183 | 答: 使用 'bvm --vminfo vmname' 查看虚拟机详细信息,包括虚拟机名称、操作系统、IP地址、网络模式、磁盘信息等。 184 | 185 | 示例输出: 186 | --------------------------- 187 | Welcome to Bhyve Vm Manager 188 | --------------------------- 189 | name : debian 190 | os type : Debian 191 | ram : 512m 192 | cpus : 1 193 | disk interface : ahci-hd 194 | disk numbers : 1 195 | |-ZFS support : off 196 | |-disk(0) size : 5g 197 | cd status : on 198 | |-iso path : /root/iso/debian-9.3.0-amd64-netinst.iso 199 | boot from : cd0 200 | hostbridge : hostbridge 201 | boot type : grub 202 | auto boot : yes 203 | |-index : 2 204 | |-time : 15 sec. 205 | nic interface : e1000 206 | nic numbers : 1 207 | nic_0 208 | |-network mode : NAT 209 | |-wan : em0 210 | |-gateway : nat2 [GW 192.168.1.1/24] 211 | |-redirect : disable 212 | |-bridge : bridge1 213 | |-tap : vmnet1 214 | |-ip : 192.168.1.10/24 215 | status : off 216 | lock : no 217 | crypt : no 218 | ``` 219 | ### 问题 4: 如何启动虚拟机? 220 | ``` 221 | 答: 使用命令 'bvm --start vmname' 启动,使用 'bvm --restart vmname' 或 'bvm --reboot vmname' 重启。对于使用GRUB启动模式的虚拟机,使用 'bvm --login vmname' 登录。对于UEFI启动模式,也可以尝试使用 'bvm --login' 登录,但控制台支持取决于客户机操作系统配置;如果失败,请使用VNC或SSH。 222 | ``` 223 | 224 | ### 问题 5: 什么是虚拟机自动启动,如何使用? 225 | ``` 226 | 答: 虚拟机自动启动意味着在系统启动时自动启动虚拟机。例如:有两个虚拟机,vm1和vm2,其中vm1是一个数据库服务器,vm2是一个依赖于vm1数据库服务的Web服务器,因此vm1必须在vm2之前启动。在vm1和vm2的配置中,有如下选项: 227 | 228 | vm1的配置: 229 | [8]. auto boot : yes 230 | [9]. boot index : 1 231 | [a]. boot time : 60 232 | 233 | vm2的配置: 234 | [8]. auto boot : yes 235 | [9]. boot index : 2 236 | [a]. boot time : 15 237 | 238 | 其中[8]表示自动启动,[9]表示启动顺序,[a]表示启动延迟。 239 | 这意味着vm2将在vm1启动60秒后启动,如果有一个vm3,它的启动时间取决于vm2的启动时间值。 240 | 241 | 要正确使用此选项,请将以下内容添加到启动脚本中: 242 | sysrc bvmd_enable=yes 243 | 244 | 当系统启动时,将自动启动所有设置为自动启动的虚拟机,或者可以使用以下命令手动启动: 245 | bvm --autoboot 246 | 247 | 你也可以随时检查自动启动虚拟机的设置情况: 248 | bvm --abinfo 249 | 250 | 示例输出: 251 | --------------------------------- 252 | idx vm time(sec) 253 | --------------------------------- 254 | 1 freebsd 18 255 | 2 debian 15 256 | ``` 257 | ### 问题 6: 如何关闭虚拟机? 258 | ``` 259 | 答: 有两种方法: 260 | 261 | 1. 正常关闭: 262 | bvm --stop vmname 263 | 264 | 2. 强制关闭(当虚拟机无法正常关闭时使用): 265 | bvm --poweroff vmname 266 | ``` 267 | ### 问题 7: 如何查看虚拟机列表? 268 | ``` 269 | 答: 有两种格式:长格式和短格式: 270 | 271 | - 短格式: 272 | bvm --ls 273 | 274 | - 长格式: 275 | bvm --ll 276 | 两者都显示虚拟机名称、状态、操作系统、IP地址等。 277 | 278 | 你也可以使用参数来获取特定信息: 279 | 280 | - 在线列表: 281 | bvm --ls online 282 | 283 | - 按状态输出: 284 | bvm --ls bystatus 285 | 286 | - 按操作系统输出: 287 | bvm --ls byos 288 | 289 | - 按IP地址输出: 290 | bvm --ls byip 291 | ``` 292 | ### 问题 8: 如何锁定虚拟机? 293 | ``` 294 | 答: 有时你需要防止意外删除虚拟机,可以使用锁定功能。锁定后,虚拟机无法删除,直到解锁。 295 | 296 | bvm --lock vmname 297 | bvm --unlock vmname 298 | bvm --lockall 299 | bvm --unlockall 300 | ``` 301 | 302 | ### 问题 9: 虚拟机加密和解密操作 303 | ``` 304 | 答: 出于安全考虑,有些虚拟机在关闭时需要加密以防止未经授权使用虚拟机文件。加密的虚拟机无法启动,直到解密。需要注意的是,加密和解密操作需要使用相同的密码,密码一旦丢失将无法解密。bvm 1.3.4 版本之后,由于修改的加密算法,旧的加密虚拟机将无法解密,需要先用老版本解密,然后使用新版本加密。 305 | 306 | 虚拟机加密和解密操作使用以下命令: 307 | 308 | bvm --encrypt vmname 309 | bvm --decrypt vmname 310 | ``` 311 | ### 问题 10: 如何查看虚拟机网络状态? 312 | ``` 313 | 答: 使用 'bvm --netstat' 命令查看在线虚拟机网络状态,包括网络模式、IP地址、网关、端口转发、桥接、TAP等。 314 | 315 | 示例输出: 316 | NAME NIC MODE IP GATEWAY PORTS BRIDGE TAP 317 | ob 0 NAT dhcp 172.16.1.1/24 (nat0) - bridge0 vmnet0 318 | c 0 NAT dhcp 172.16.1.1/24 (nat0) - bridge0 vmnet1 319 | freebsd-14 0 NAT 172.16.1.10/24 172.16.1.1/24 (nat0) tcp 22:2224 bridge0 vmnet3 320 | ``` 321 | 322 | ### 问题 11: 如何查看网络设备信息? 323 | ``` 324 | 答: 可以使用以下命令查看网络设备信息: 325 | 326 | bvm --showdev 327 | bvm --showdevall 328 | bvm --showdevuse 329 | 330 | 示例输出: 331 | default-bridge 332 | |-em0 333 | |-c (nic1) 334 | |-test (nic1) 335 | nat0 [172.16.1.1/24] 336 | |-ob (nic0) 337 | |-c (nic0) 338 | |-c11 (nic0) 339 | |-c12 (nic0) 340 | |-centos (nic0) 341 | |-test (nic0) 342 | |-freebsd-14 (nic0) 343 | nat1 [10.10.30.1/24] 344 | |-null 345 | nat2 [192.168.1.1/24] 346 | |-c2 (nic0) 347 | |-debian (nic0) 348 | switch0 [10.0.1.0/24] 349 | |-null 350 | switch1 [10.0.2.0/24] 351 | |-null 352 | switch2 [10.0.3.0/24] 353 | |-null 354 | switch3 355 | |-null 356 | switch4 357 | |-null 358 | switch5 359 | |-null 360 | switch6 361 | |-null 362 | switch7 363 | |-null 364 | 365 | default-bridge 是默认的虚拟机-主机桥接;nat0-nat2 是 3 个用于主机通信的保留 NAT 接口;switch0-switch7 是 8 个用于主机通信的保留虚拟机通信接口,它们也是桥接,但与默认桥接不同,主要用于内部网络,通常不包括主机的物理 NIC。 366 | ``` 367 | ### 问题 12: bvm 支持哪些网络模式? 368 | ``` 369 | 答: bvm 提供桥接和 NAT 模式,其中 3 个保留的 NAT 接口用于主机通信,8 个交换接口用于主机通信。管理员可以按需设置 NAT 网络 IP 地址,交换机通常不需要配置,除非在特殊情况下。 370 | 371 | NAT 配置在 /usr/local/etc/bvm/nat.conf 中定义,如下所示: 372 | 373 | nat0=172.16.1.1/24 374 | nat1=10.10.30.1/24 375 | nat2=192.168.1.1/24 376 | 377 | 交换机配置在 /usr/local/etc/bvm/switch.conf 中定义,如下所示: 378 | 379 | switch0=10.0.1.0/24 380 | switch1=10.0.2.0/24 381 | switch2=10.0.3.0/24 382 | switch3= 383 | switch4= 384 | switch5= 385 | switch6= 386 | switch7= 387 | 388 | bvm 提供管理 NAT 和交换接口的命令: 389 | 390 | 查询命令: 391 | 392 | bvm --natinfo 393 | bvm --swinfo 394 | 395 | 设置命令: 396 | 397 | bvm --setnat 398 | bvm --setsw 399 | bvm --unsetsw 400 | 401 | 添加命令: 402 | 403 | bvm --addnat 404 | bvm --addswitch 405 | 406 | 删除命令: 407 | 408 | bvm --delnat 409 | bvm --delswitch 410 | ``` 411 | 412 | ### 问题 13: 什么是端口转发? 413 | ``` 414 | 答: 端口转发将内部虚拟机端口映射到主机端口,以实现虚拟机-主机通信。bvm 允许在虚拟机创建和配置期间设置端口转发。此外,bvm 支持动态端口转发,可以在不停止虚拟机的情况下添加/删除规则,使用以下命令: 415 | 416 | bvm --setpr 417 | bvm --showpr 418 | 419 | 例如,设置 IP 为 192.168.1.10 的虚拟机的端口转发: 420 | 421 | bvm --setpr 192.168.1.10 422 | 423 | 你可以随时查看端口转发规则: 424 | 425 | bvm --showpr 426 | 427 | 示例输出: 428 | PROTO VM IP:PORT HOST PORT VM NAME 429 | tcp 172.16.1.10:22 2224 freebsd-14 430 | tcp 172.16.1.10:80 8080 freebsd-14 431 | ``` 432 | ### 问题 14: 如何查看 DHCP 客户端信息? 433 | ``` 434 | 答: 使用 'bvm --showdhcp' 命令查看在线虚拟机 DHCP 客户端信息,包括虚拟机名称、MAC地址、IP地址、租期等。 435 | 436 | 示例输出: 437 | ip mac bind_time status 438 | -- --- --------- ------ 439 | 172.16.1.100 00:a0:98:ad:89:74 03/26 09:30:37 - 03/26 19:30:37 ASSOCIATED 440 | 192.168.1.100 00:a0:98:dc:ff:5b 03/26 09:34:26 - 03/27 09:34:26 ASSOCIATED 441 | 442 | ``` 443 | ### 问题 15: 如何配置 DHCP? 444 | ``` 445 | 答: DHCP 配置文件位于 /usr/local/etc/bvm/dhcp.conf,可以修改 nat0-nat2 参数,如下所示: 446 | 447 | 静态 IP 地址应写成以下格式: 448 | 449 | nat0_static_ip_01="172.16.1.2 00:00:00:00:00:00" 450 | nat0_static_ip_02="172.16.1.3 00:00:00:00:00:00" 451 | 452 | 动态 IP 范围应写成以下格式: 453 | 454 | nat0_dynamic_ip="172.16.1.100 172.16.1.254" 455 | 456 | 其他部分可以根据默认配置填写。 457 | ``` 458 | ### 问题 16: 如何查看虚拟机CPU、内存、网络流量等信息 ? 459 | ``` 460 | 答: 使用 'bvm --showstats vmname' 命令查看虚拟机CPU、内存、网络流量等信息。 461 | 462 | 示例输出: 463 | [ Infrastructure Metrics ] 464 | CPU Usage : 0.00% (Allocated: 1 Cores) 465 | Memory Usage : 0.97% (Allocated: 2g, Active: 19.89 MB) 466 | Disk I/O & Cap : 467 | - disk0 : Cap: 5g, Used: 1.82 GB(/var/vm/fb/disk.img) 468 | Network Traffic: 469 | - vmnet0 : RX: 726 B / TX: 4.43 KB (Drops: 0) 470 | 471 | [ Advanced VM Stats ] 472 | VM Exits : 1437238 (Rate: ~45784/sec) 473 | - IO Access : 202732 474 | - Emulation : 685290 475 | - Interrupts : 45059 (NMI: 0) 476 | 477 | [ Availability & Services ] 478 | PID : 6044 479 | Host Load Avg : 1.44, 1.43, 1.36 480 | Status : Online 481 | Uptime : 52:34 (Process) 482 | Boot Time : 31s (CPU Runtime) 483 | VNC Service : Disabled 484 | ``` 485 | 486 | ### 问题 17: 如何使用共享文件夹? 487 | ``` 488 | 答: 共享文件夹允许您与虚拟机共享宿主机目录。在虚拟机配置中启用 'shared folder',设置共享名称和宿主机路径。 489 | 490 | 配置选项: 491 | shared folder 启用/禁用共享文件夹 (on/off) 492 | share name 虚拟机中用于识别共享的名称 493 | share path 要共享的宿主机目录路径 494 | share ro 只读模式 (on/off) 495 | 496 | 在 Linux 虚拟机中挂载: 497 | mkdir -p /mnt/hostshare 498 | mount -t 9p -o trans=virtio hostshare /mnt/hostshare 499 | 500 | 注意:VirtIO-9P 在 Linux 虚拟机中支持良好。FreeBSD 14 虚拟机缺少 virtio_p9fs 模块;FreeBSD 15+ 完整支持。 501 | ``` 502 | -------------------------------------------------------------------------------- /src/zfs.c: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | BVM Copyright (c) 2018-2025, Qiang Guo (bigdragonsoft@gmail.com) 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | *---------------------------------------------------------------------------*/ 26 | 27 | 28 | #include "zfs.h" 29 | #include "config.h" 30 | #include "vm.h" 31 | #include "create.h" 32 | #include "vnet.h" 33 | 34 | char zpool_list[ZPOOL_LISTSIZE][ZPOOL_BUFFERSIZE] = {0}; 35 | char snapshot_list[SNAPSHOT_LISTSIZE][SNAPSHOT_BUFFERSIZE] = {0}; 36 | 37 | // 是否支持zfs 38 | int support_zfs() 39 | { 40 | return is_zfs_supported_by_kldstat(); 41 | } 42 | 43 | // 检测宿主机是否支持zfs(通过配置文件) 44 | // 1:支持 45 | // 0:不支持 46 | int is_zfs_supported_by_config() 47 | { 48 | int f1 = 0; 49 | int f2 = 0; 50 | 51 | char *value; 52 | 53 | //zfs_enable="YES" 54 | init_config("/etc/rc.conf"); 55 | if ((value = get_value_by_name("zfs_enable")) != NULL) { 56 | if (strcmp(strtolower(value), "yes") == 0) f1 = 1; 57 | } 58 | free_config(); 59 | 60 | //zfs_load="YES" 61 | init_config("/boot/loader.conf"); 62 | if ((value = get_value_by_name("zfs_load")) != NULL) { 63 | if (strcmp(strtolower(value), "yes") == 0) f2 = 1; 64 | } 65 | free_config(); 66 | 67 | if (f1 && f2) return 1; 68 | else return 0; 69 | } 70 | 71 | // 检测宿主机是否支持zfs 72 | // 1:支持 73 | // 0:不支持 74 | // -1:错误 75 | int is_zfs_supported_by_kldstat() 76 | { 77 | FILE *fp; 78 | char line[BUFFERSIZE]; 79 | int supported = 0; 80 | 81 | // 执行 kldstat 命令并读取输出 82 | fp = popen("kldstat", "r"); 83 | if (fp == NULL) { 84 | error("Failed to execute the kldstat command\n"); 85 | return -1; 86 | } 87 | 88 | // 检查输出中是否包含 zfs.ko 模块 89 | while (fgets(line, sizeof(line), fp) != NULL) { 90 | if (strstr(line, "zfs.ko") != NULL) { 91 | supported = 1; 92 | break; 93 | } 94 | } 95 | 96 | pclose(fp); 97 | return supported; 98 | } 99 | 100 | // 获取所有zpool存储池列表 101 | int get_zpool_list() 102 | { 103 | FILE *fp; 104 | fp = popen("df -Th | awk '{if ($2 == \"zfs\") print $1}'", "r"); 105 | if (fp == NULL) { 106 | error("can't get zpool name\n"); 107 | err_exit(); 108 | } 109 | 110 | int n = 0; 111 | char buf[ZPOOL_BUFFERSIZE]; 112 | while (fgets(buf, ZPOOL_BUFFERSIZE, fp)) { 113 | buf[strlen(buf)-1] = '\0'; 114 | strcpy(zpool_list[n++], buf); 115 | } 116 | 117 | pclose(fp); 118 | return n; 119 | } 120 | 121 | // 是否存在卷 122 | int exist_zvol(char *zvol) 123 | { 124 | FILE *fp; 125 | fp = popen("zfs list | awk '{print $1}'", "r"); 126 | if (fp == NULL) { 127 | error("can't get %s\n", zvol); 128 | err_exit(); 129 | } 130 | 131 | int n = 0; 132 | char buf[ZPOOL_BUFFERSIZE]; 133 | while (fgets(buf, ZPOOL_BUFFERSIZE, fp)) { 134 | buf[strlen(buf)-1] = '\0'; 135 | if (strcmp(buf, zvol) == 0) { 136 | pclose(fp); 137 | return 1; 138 | } 139 | } 140 | pclose(fp); 141 | return 0; 142 | } 143 | 144 | // 卷是否存在子快照 145 | int exist_snapshot(char *zvol) 146 | { 147 | char cmd[BUFFERSIZE]; 148 | sprintf(cmd, "zfs list -t snapshot | grep %s | awk '{print $1}'", zvol); 149 | 150 | FILE *fp; 151 | fp = popen(cmd, "r"); 152 | if (fp == NULL) { 153 | error("can't get snapshot of %s", zvol); 154 | err_exit(); 155 | } 156 | 157 | int n = 0; 158 | char buf[SNAPSHOT_BUFFERSIZE]; 159 | while (fgets(buf, SNAPSHOT_BUFFERSIZE, fp)) { 160 | buf[strlen(buf)-1] = '\0'; 161 | strcpy(snapshot_list[n++], buf); 162 | } 163 | 164 | pclose(fp); 165 | return n; 166 | } 167 | 168 | // 卷是否存在父快照 169 | int exist_parent_snapshot(char *zvol, char *ssname) 170 | { 171 | char cmd[BUFFERSIZE]; 172 | sprintf(cmd, "zfs list -Ho origin %s 2> /dev/null", zvol); 173 | 174 | FILE *fp; 175 | fp = popen(cmd, "r"); 176 | if (fp == NULL) { 177 | error("can't get parent snapshot of %s", zvol); 178 | err_exit(); 179 | } 180 | 181 | int n = 0; 182 | char buf[SNAPSHOT_BUFFERSIZE]; 183 | if (fgets(buf, SNAPSHOT_BUFFERSIZE, fp)) { 184 | buf[strlen(buf)-1] = '\0'; 185 | strcpy(ssname, buf); 186 | } 187 | 188 | pclose(fp); 189 | if (strstr(ssname, "@")) return 1; 190 | else return 0; 191 | } 192 | 193 | // 快照是否存在克隆vm 194 | int exist_clone_vm(char *ssname) 195 | { 196 | vm_node *p = vms; 197 | while (p) { 198 | 199 | char zvol[BUFFERSIZE]; 200 | sprintf(zvol, "%s/bvm_%s_disk", p->vm.zpool, p->vm.name); 201 | 202 | char parent[BUFFERSIZE]; 203 | if (exist_parent_snapshot(zvol, parent) && strcmp(ssname, parent) == 0) return 1; 204 | 205 | p = p->next; 206 | } 207 | 208 | return 0; 209 | } 210 | 211 | // 创建zfs虚拟机磁盘文件 212 | void create_zfs_disk(vm_stru *vm, int disk_ord) 213 | { 214 | char zvol[FN_MAX_LEN]; 215 | char cmd[BUFFERSIZE]; 216 | char disk[32]; 217 | 218 | int n = disk_ord; 219 | if (n == 0) 220 | strcpy(disk, "disk"); 221 | else 222 | sprintf(disk, "disk%d", n); 223 | sprintf(zvol, "%s/bvm_%s_%s", vm->zpool, vm->name, disk); 224 | 225 | if (!exist_zvol(zvol)) { 226 | sprintf(cmd, "zfs create -V %s %s", vm->vdisk[n].size, zvol); 227 | run_cmd(cmd); 228 | link_to_zvol(vm, disk_ord, zvol); 229 | } 230 | } 231 | 232 | // 调整 ZFS 卷大小 233 | // vm:虚拟机 234 | // size:调整后的磁盘空间大小 235 | // disk_ord:盘号 236 | void resize_zfs_disk(vm_stru *vm, char *size, int disk_ord) 237 | { 238 | char zvol[FN_MAX_LEN]; 239 | char cmd[BUFFERSIZE]; 240 | char disk[32]; 241 | 242 | int n = disk_ord; 243 | if (n == 0) 244 | strcpy(disk, "disk"); 245 | else 246 | sprintf(disk, "disk%d", n); 247 | sprintf(zvol, "%s/bvm_%s_%s", vm->zpool, vm->name, disk); 248 | 249 | if (exist_zvol(zvol)) { 250 | // zfs set volsize=SIZE poolname/dataset 251 | sprintf(cmd, "zfs set volsize=%s %s", size, zvol); 252 | run_cmd(cmd); 253 | } 254 | } 255 | 256 | // 磁盘文件链接到卷 257 | void link_to_zvol(vm_stru *vm, int disk_ord, char *zvol) 258 | { 259 | char fn[FN_MAX_LEN]; 260 | char dev[BUFFERSIZE]; 261 | char disk[32]; 262 | int n = disk_ord; 263 | 264 | if (n == 0) 265 | strcpy(disk, "/disk.img"); 266 | else 267 | sprintf(disk, "/disk%d.img", n); 268 | sprintf(fn, "%s%s%s", vmdir, vm->name, disk); 269 | 270 | if (access(fn, 0) == -1) { 271 | sprintf(dev, "/dev/zvol/%s", zvol); 272 | if (symlink(dev, fn) != 0) { 273 | error("create disk failure\n"); 274 | err_exit(); 275 | } 276 | } 277 | } 278 | 279 | // 删除卷 280 | void remove_zvol(vm_stru *vm, int disk_ord) 281 | { 282 | char zvol[ZPOOL_BUFFERSIZE]; 283 | 284 | if (disk_ord == 0) 285 | sprintf(zvol, "%s/bvm_%s_disk", vm->zpool, vm->name); 286 | else 287 | sprintf(zvol, "%s/bvm_%s_disk%d", vm->zpool, vm->name, disk_ord); 288 | 289 | int fdel = 0; 290 | char cmd[BUFFERSIZE]; 291 | 292 | //vm存在快照 293 | if (exist_snapshot(zvol)) { 294 | int n = 0; 295 | int ff = 0; 296 | while (strlen(snapshot_list[n]) > 0) { 297 | //存在快照的克隆 298 | if (exist_clone_vm(snapshot_list[n])) { 299 | fdel = 0; 300 | ff = 1; 301 | break; 302 | } 303 | n++; 304 | } 305 | //不存在快照的克隆 306 | if (ff != 1) { 307 | fdel = 1; 308 | int n = 0; 309 | while (strlen(snapshot_list[n]) > 0) { 310 | sprintf(cmd, "zfs destroy %s", snapshot_list[n]); 311 | run_cmd(cmd); 312 | ++n; 313 | } 314 | } 315 | } 316 | //vm不存在快照 317 | else 318 | fdel = 1; 319 | 320 | if (fdel) { 321 | sprintf(cmd, "zfs destroy %s", zvol); 322 | run_cmd(cmd); 323 | } 324 | else { 325 | error("can't remove \"%s\" with associated snapshots\n", vm->name); 326 | err_exit(); 327 | } 328 | } 329 | 330 | // 卷更名 331 | void rename_zvol(vm_stru *vm, char *oldname, char *newname) 332 | { 333 | char zvol_old[ZPOOL_BUFFERSIZE]; 334 | char zvol_new[ZPOOL_BUFFERSIZE]; 335 | 336 | 337 | for (int n = 0; n < atoi(vm->disks); n++) { 338 | if (n == 0) { 339 | sprintf(zvol_old, "%s/bvm_%s_disk", vm->zpool, oldname); 340 | sprintf(zvol_new, "%s/bvm_%s_disk", vm->zpool, newname); 341 | } 342 | else { 343 | sprintf(zvol_old, "%s/bvm_%s_disk%d", vm->zpool, oldname, n); 344 | sprintf(zvol_new, "%s/bvm_%s_disk%d", vm->zpool, newname, n); 345 | } 346 | 347 | char cmd[BUFFERSIZE]; 348 | sprintf(cmd, "zfs rename %s %s", zvol_old, zvol_new); 349 | run_cmd(cmd); 350 | 351 | if (n == 0) 352 | sprintf(cmd, "%s%s/disk.img", vmdir, newname); 353 | else 354 | sprintf(cmd, "%s%s/disk%d.img", vmdir, newname, n); 355 | 356 | if (remove(cmd) == -1) { 357 | error("%s can't remove\n", cmd); 358 | err_exit(); 359 | } 360 | 361 | link_to_zvol(vm, n, zvol_new); 362 | } 363 | } 364 | 365 | // 克隆卷 366 | int clone_zvol(vm_stru *vm, char *src_vm_name, char *dst_vm_name, int link) 367 | { 368 | char zvol_src[ZPOOL_BUFFERSIZE]; 369 | char zvol_dst[ZPOOL_BUFFERSIZE]; 370 | 371 | 372 | for (int n = 0; n < atoi(vm->disks); n++) { 373 | if (n == 0) { 374 | sprintf(zvol_src, "%s/bvm_%s_disk", vm->zpool, src_vm_name); 375 | sprintf(zvol_dst, "%s/bvm_%s_disk", vm->zpool, dst_vm_name); 376 | } 377 | else { 378 | sprintf(zvol_src, "%s/bvm_%s_disk%d", vm->zpool, src_vm_name, n); 379 | sprintf(zvol_dst, "%s/bvm_%s_disk%d", vm->zpool, dst_vm_name, n); 380 | } 381 | 382 | char snapname[BUFFERSIZE]; 383 | sprintf(snapname, "%ld", time(NULL)); 384 | 385 | char cmd[BUFFERSIZE]; 386 | sprintf(cmd, "zfs snapshot %s@%s", zvol_src, snapname); 387 | run_cmd(cmd); 388 | sprintf(cmd, "zfs clone %s@%s %s", zvol_src, snapname, zvol_dst); 389 | run_cmd(cmd); 390 | 391 | if (link) { 392 | link_to_zvol(vm, n, zvol_dst); 393 | } 394 | } 395 | 396 | return RET_SUCCESS; 397 | 398 | } 399 | 400 | // 显示快照列表 401 | void show_snapshot_list(char *vm_name) 402 | { 403 | vm_node *p; 404 | if ((p = find_vm_list(vm_name)) == NULL) { 405 | error("%s not exist\n", vm_name); 406 | show_vm_name(VM_OFF); 407 | return; 408 | } 409 | 410 | vm_stru *vm = &p->vm; 411 | if (!support_zfs() || strcmp(vm->zfs, "off") == 0) { 412 | warn("%s (non-zfs)\n", vm->name); 413 | return; 414 | } 415 | 416 | char zvol[BUFFERSIZE]; 417 | sprintf(zvol, "%s/bvm_%s_disk@", vm->zpool, vm->name); 418 | 419 | int cnt = exist_snapshot(zvol); 420 | warn("%s (snapshots: %d)\n", vm->name, cnt); 421 | 422 | if (cnt) { 423 | int n = 0; 424 | while (strlen(snapshot_list[n]) > 0) { 425 | printf("|-%s\n", (char*)(strstr(snapshot_list[n], "@") + 1)); 426 | //printf("|-%s\n", snapshot_list[n]); 427 | ++n; 428 | } 429 | } 430 | } 431 | 432 | // 显示全部vm快照列表 433 | void show_snapshot_list_all() 434 | { 435 | vm_node *p = vms; 436 | while (p) { 437 | show_snapshot_list(p->vm.name); 438 | printf("\n"); 439 | p = p->next; 440 | } 441 | } 442 | 443 | // vm快照处理 444 | void vm_snapshot(char *vm_name) 445 | { 446 | vm_node *p; 447 | if ((p = find_vm_list(vm_name)) == NULL) { 448 | error("%s does not exist\n", vm_name); 449 | show_vm_name(VM_OFF); 450 | return; 451 | } 452 | 453 | if (!support_zfs() || strcmp(p->vm.zfs, "on") != 0) { 454 | error("%s is non zfs\n", vm_name); 455 | return; 456 | } 457 | 458 | if (get_vm_status(vm_name) == VM_ON) { 459 | error("can't snapshot, %s is running\n", vm_name); 460 | return; 461 | } 462 | 463 | char ssname[ZPOOL_BUFFERSIZE]; 464 | enter_snapshot_name(&p->vm, (char*)ssname); 465 | 466 | for (int n=0; nvm.disks); n++) { 467 | char zvol[ZPOOL_BUFFERSIZE]; 468 | if (n == 0) 469 | sprintf(zvol, "%s/bvm_%s_disk", p->vm.zpool, p->vm.name); 470 | else 471 | sprintf(zvol, "%s/bvm_%s_disk%d", p->vm.zpool, p->vm.name, n); 472 | 473 | char cmd[BUFFERSIZE]; 474 | sprintf(cmd, "zfs snapshot %s@%s", zvol, ssname); 475 | run_cmd(cmd); 476 | } 477 | 478 | 479 | } 480 | 481 | // vm回滚处理 482 | void vm_rollback(char *vm_name) 483 | { 484 | vm_node *p; 485 | if ((p = find_vm_list(vm_name)) == NULL) { 486 | error("%s does not exist\n", vm_name); 487 | show_vm_name(VM_OFF); 488 | return; 489 | } 490 | 491 | if (!support_zfs() || strcmp(p->vm.zfs, "on") != 0) { 492 | error("%s is non zfs\n", vm_name); 493 | return; 494 | } 495 | 496 | if (get_vm_status(vm_name) == VM_ON) { 497 | error("can't rollback, %s is running\n", vm_name); 498 | return; 499 | } 500 | 501 | char ssname[ZPOOL_BUFFERSIZE]; 502 | select_snapshot_name(&p->vm, ssname); 503 | 504 | for (int n=0; nvm.disks); n++) { 505 | char zvol[ZPOOL_BUFFERSIZE]; 506 | if (n == 0) 507 | sprintf(zvol, "%s/bvm_%s_disk", p->vm.zpool, p->vm.name); 508 | else 509 | sprintf(zvol, "%s/bvm_%s_disk%d", p->vm.zpool, p->vm.name, n); 510 | 511 | char cmd[BUFFERSIZE]; 512 | sprintf(cmd, "zfs rollback %s@%s", zvol, ssname); 513 | run_cmd(cmd); 514 | } 515 | 516 | 517 | } 518 | 519 | 520 | // 检测快照名称的拼写 521 | int check_ssname_spell(char *ssname) 522 | { 523 | if (ssname == NULL) return 0; 524 | if (strlen(ssname) == 0) return 0; 525 | 526 | int n = 0; 527 | while (strlen(ssname) > n) { 528 | if (isalnum(ssname[n])) 529 | ++n; 530 | else 531 | return 0; 532 | } 533 | return 1; 534 | } 535 | 536 | // 输入快照名称 537 | void enter_snapshot_name(vm_stru *vm, char *ssname) 538 | { 539 | char *msg = "Enter snapshot name: "; 540 | 541 | while (1) { 542 | printf("%s", msg); 543 | fgets(ssname, ZPOOL_BUFFERSIZE, stdin); 544 | ssname[strlen(ssname)-1] = '\0'; 545 | if (check_ssname_spell(ssname) == 1) { 546 | //if (check_spell(ssname) == RET_SUCCESS) { 547 | char zvol[BUFFERSIZE]; 548 | sprintf(zvol, "%s/bvm_%s_disk@", vm->zpool, vm->name); 549 | int cnt = exist_snapshot(zvol); 550 | int ff = 0; 551 | while (--cnt >= 0) { 552 | char *p = (char*)(strstr(snapshot_list[cnt], "@") + 1); 553 | if (strcmp(p, ssname) == 0) { 554 | ff = 1; 555 | break; 556 | } 557 | } 558 | 559 | if (ff) 560 | warn("%s already exist\n", ssname); 561 | else 562 | break; 563 | } 564 | else { 565 | warn("snapshot name invalid\n"); 566 | } 567 | } 568 | } 569 | 570 | // 选择快照名称 571 | void select_snapshot_name(vm_stru *vm, char *ssname) 572 | { 573 | char *msg = "Enter snapshot name: "; 574 | char *opts[SNAPSHOT_LISTSIZE] = {0}; 575 | char zvol[BUFFERSIZE]; 576 | sprintf(zvol, "%s/bvm_%s_disk@", vm->zpool, vm->name); 577 | int cnt = exist_snapshot(zvol); 578 | 579 | if (cnt) { 580 | int n = 0; 581 | while (strlen(snapshot_list[n]) > 0) { 582 | opts[n] = (char*)malloc(BUFFERSIZE * sizeof(char)); 583 | memset(opts[n], 0, BUFFERSIZE * sizeof(char)); 584 | char *p = (char*)(strstr(snapshot_list[n], "@") + 1); 585 | strcpy(opts[n], p); 586 | ++n; 587 | } 588 | enter_options(msg, opts, NULL, ssname); 589 | 590 | while (n >= 0) { 591 | if (opts[n]) free(opts[n]); 592 | --n; 593 | } 594 | 595 | } 596 | else 597 | strcpy(ssname, ""); 598 | } 599 | 600 | 601 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | BVM Copyright (c) 2018-2025, Qiang Guo (bigdragonsoft@gmail.com) 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | *---------------------------------------------------------------------------*/ 26 | 27 | 28 | #include "main.h" 29 | 30 | pro_stru program = { 31 | "bvm", 32 | "1.3.5", 33 | "Qiang Guo", 34 | "bigdragonsoft@gmail.com", 35 | "https://github.com/bigdragonsoft/bvm", 36 | }; 37 | 38 | // 版本 39 | void version() 40 | { 41 | printf("%s %s\n", program.name, program.version); 42 | printf("author: %s\n", program.author); 43 | printf("email: %s\n", program.email); 44 | printf("%s\n", program.website); 45 | printf("Copyright (C) 2017~2025 BigDragonSoft.com, ChinaFreeBSD.cn\n"); 46 | } 47 | 48 | // 程序用法 49 | void usage() 50 | { 51 | char *help[] = { 52 | "Usage: ${name} [args...]", 53 | "", 54 | "VM Management Options:", 55 | " --create Create new VM", 56 | "", 57 | " Usage: ${name} --create [from [options]]", 58 | " Standard templates: freebsd, linux, windows", 59 | " Or use an existing VM name as a template", 60 | " Options: -s(grub), -U=cpus, -m=mem, -d=disk, -n=net, -i=bind_nic", 61 | "", 62 | " --start Start VM", 63 | " --stop Stop VM (ACPI shutdown)", 64 | " --poweroff Force power off VM", 65 | " --restart Restart VM", 66 | " --reboot Restart VM (alias for --restart)", 67 | " --set-hd-boot Set VM to boot from hard disk", 68 | " --login Log in to VM console (for grub boot)", 69 | " --config Configure VM settings (cpus, ram, disk, network, etc.)", 70 | " --clone Clone VM to a new one", 71 | " --remove Destroy VM(s) permanently", 72 | " --rename Rename VM", 73 | " --vminfo Display detailed VM configuration info", 74 | " --ls List VMs (short format)", 75 | " --ll List VMs (long format)", 76 | " --showstats Show VM resource usage statistics", 77 | " --os List supported OS types", 78 | "", 79 | "VM Operation & Security:", 80 | " --autoboot Start all auto-boot enabled VMs", 81 | " --abinfo Show auto-boot configuration", 82 | " --lock Lock VM (prevent accidental deletion/modification)", 83 | " --unlock Unlock VM", 84 | " --lockall Lock all VMs", 85 | " --unlockall Unlock all VMs", 86 | " --encrypt Encrypt VM data", 87 | " --decrypt Decrypt VM data", 88 | "", 89 | "Storage & Snapshot Options:", 90 | " --addisk Add a new disk to VM", 91 | " --deldisk Delete a disk from VM", 92 | " --snapshot Create a snapshot of VM", 93 | " --rollback Rollback VM to a snapshot", 94 | " --showsnap Show snapshots of a VM", 95 | " --showsnapall Show snapshots of all VMs", 96 | "", 97 | "Network Management (NAT & Switch):", 98 | " --netstats Show VM network status", 99 | " --natinfo Show NAT configuration info", 100 | " --addnat Add a new NAT interface", 101 | " --delnat Delete a NAT interface", 102 | " --setnat Set NAT IP address", 103 | " --reload-nat Reload NAT port redirection rules", 104 | " --swinfo Show Switch configuration info", 105 | " --addswitch Add a new Switch", 106 | " --delswitch Delete a Switch", 107 | " --setsw Set Switch IP address", 108 | " --unsetsw Unset Switch IP address", 109 | " --setpr Set port redirection (dynamic)", 110 | " --showpr Show active port redirection rules", 111 | " --showdhcp Show DHCP client leases", 112 | " --showdev Show network device mapping", 113 | " --showdevall Show all network devices (class mode)", 114 | " --showdevuse Show all network devices (simple mode)", 115 | "", 116 | "Example:", 117 | " ${name} --create vm1 from linux -U=4 -m=4g -d=20g", 118 | " ${name} --start vm1", 119 | " ${name} --ls", 120 | " ${name} --ll online", 121 | " ${name} --vminfo vm1", 122 | "", 123 | "For more details, please read 'man ${name}'", 124 | NULL}; 125 | 126 | int n = 0; 127 | while (*(help+n)) { 128 | char str[BUFFERSIZE]; 129 | strcpy(str, *(help+n++)); 130 | str_replace(str, "${name}", program.name); 131 | printf("%s\n", str); 132 | } 133 | } 134 | 135 | 136 | 137 | int main(int argc, char *argv[]) 138 | { 139 | if (argc < 2) { 140 | usage(); 141 | return 0; 142 | } 143 | 144 | check_bre(); 145 | set_vmdir(); 146 | set_bvm_os(); 147 | 148 | int opt; 149 | char *short_options = "hvl"; 150 | 151 | //未使用的字母 152 | //q, x, y, z 153 | //F, G, J, M, P, Q, U, X, Y, Z 154 | struct option long_options[] = { 155 | {"help", 0, NULL, 'h'}, 156 | {"version", 0, NULL, 'v'}, 157 | {"ls", 0, NULL, 'l'}, 158 | {"ll", 0, NULL, '('}, 159 | {"netstats", 0, NULL, 'j'}, 160 | {"os", 0, NULL, 'o'}, 161 | {"create", 1, NULL, 'C'}, 162 | {"config", 1, NULL, 'e'}, 163 | {"start", 1, NULL, 's'}, 164 | {"autoboot", 0, NULL, 'B'}, 165 | {"abinfo", 0, NULL, 'b'}, 166 | {"login", 1, NULL, 'L'}, 167 | {"stop", 1, NULL, 'S'}, 168 | {"restart", 1, NULL, 'R'}, 169 | {"reboot", 1, NULL, 'O'}, 170 | {"poweroff", 1, NULL, 'p'}, 171 | {"clone", 1, NULL, 'c'}, 172 | {"rename", 1, NULL, 'r'}, 173 | {"remove", 1, NULL, 'd'}, 174 | {"addisk", 1, NULL, '+'}, 175 | {"deldisk", 1, NULL, '-'}, 176 | {"showstats", 1, NULL, 'k'}, 177 | {"vminfo", 1, NULL, 'i'}, 178 | {"vminfoall", 1, NULL, 'I'}, 179 | {"natinfo", 0, NULL, 'n'}, 180 | {"setnat", 1, NULL, 'N'}, 181 | {"addnat", 1, NULL, 'A'}, 182 | {"delnat", 1, NULL, 'D'}, 183 | {"swinfo", 0, NULL, '1'}, 184 | {"setsw", 1, NULL, '2'}, 185 | {"addswitch", 1, NULL, '3'}, 186 | {"delswitch", 1, NULL, '4'}, 187 | {"unsetsw", 1, NULL, 'u'}, 188 | {"reload-nat", 0, NULL, 'E'}, 189 | {"lock", 1, NULL, '5'}, 190 | {"unlock", 1, NULL, '6'}, 191 | {"lockall", 0, NULL, '7'}, 192 | {"unlockall", 0, NULL, '8'}, 193 | {"setpr", 1, NULL, 'a'}, 194 | {"showpr", 0, NULL, 'f'}, 195 | {"showdev", 0, NULL, '9'}, 196 | {"showdevall", 0, NULL, 'T'}, 197 | {"showdevuse", 0, NULL, 'm'}, 198 | {"showdhcp", 0, NULL, 'g'}, 199 | {"showsnap", 1, NULL, 'w'}, 200 | {"showsnapall", 0, NULL, 'V'}, 201 | {"snapshot", 1, NULL, 'W'}, 202 | {"rollback", 1, NULL, 'K'}, 203 | {"set-hd-boot", 1, NULL, 'H'}, 204 | {"test", 0, NULL, 't'}, 205 | {"destroy-bridge", 0, NULL, 0}, 206 | {"destroy-tap", 0, NULL, 0}, 207 | {"encrypt", 1, NULL, 0}, 208 | {"decrypt", 1, NULL, 0}, 209 | {NULL, 0, NULL, 0} 210 | }; 211 | 212 | int option_index = 0; 213 | while ((opt=getopt_long(argc, argv, short_options, long_options, &option_index))!=-1) { 214 | write_log("bvm --%s %s %s", long_options[option_index].name, optarg?optarg:"", argv[optind]?argv[optind]:""); 215 | vm_init(); 216 | switch (opt) { 217 | case 0: 218 | if (strcmp(long_options[option_index].name, "destroy-bridge") == 0) { 219 | destroy_all_bridge(); 220 | } 221 | if (strcmp(long_options[option_index].name, "destroy-tap") == 0) { 222 | destroy_all_tap(); 223 | } 224 | if (strcmp(long_options[option_index].name, "encrypt") == 0) { 225 | vm_crypt(optarg, 1); 226 | } 227 | if (strcmp(long_options[option_index].name, "decrypt") == 0) { 228 | vm_crypt(optarg, 0); 229 | } 230 | break; 231 | 232 | case 'C': //create 233 | { 234 | create_opts_stru opts = {0}; 235 | char *template_name = NULL; 236 | int has_from = 0; 237 | 238 | // 检查是否有 "from" 关键字 239 | if (argv[optind] && strcmp(strtolower(argv[optind]), "from") == 0) { 240 | has_from = 1; 241 | if (argv[optind + 1]) { 242 | template_name = argv[optind + 1]; 243 | optind += 2; // 跳过 "from" 和模板名称 244 | } else { 245 | error("Invalid parameters.\n"); 246 | err_exit(); 247 | } 248 | } 249 | 250 | // 如果使用模板创建,解析额外的配置参数 251 | if (has_from) { 252 | while (argv[optind] && argv[optind][0] == '-') { 253 | char *arg = argv[optind]; 254 | 255 | // -s 启动类型为 grub 256 | if (strcmp(arg, "-s") == 0) { 257 | opts.use_grub = 1; 258 | } 259 | // -U=N CPU数量 260 | else if (strncmp(arg, "-U=", 3) == 0) { 261 | char *val = arg + 3; 262 | // 验证: 必须是正整数 263 | int cpu_num = atoi(val); 264 | if (cpu_num <= 0 || val[0] == '\0') { 265 | error("Invalid CPU count: %s (must be a positive integer)\n", arg); 266 | err_exit(); 267 | } 268 | strcpy(opts.cpus, val); 269 | } 270 | // -m=SIZE 内存大小 271 | else if (strncmp(arg, "-m=", 3) == 0) { 272 | char *val = arg + 3; 273 | // 验证: 必须是数字+单位(m/g) 274 | int len = strlen(val); 275 | if (len < 2) { 276 | error("Invalid memory size: %s (e.g. 512m, 2g)\n", arg); 277 | err_exit(); 278 | } 279 | char unit = tolower(val[len-1]); 280 | if (unit != 'm' && unit != 'g') { 281 | error("Invalid memory size: %s (unit must be m or g)\n", arg); 282 | err_exit(); 283 | } 284 | // 验证数字部分 285 | for (int i = 0; i < len - 1; i++) { 286 | if (!isdigit(val[i])) { 287 | error("Invalid memory size: %s (must be number+unit)\n", arg); 288 | err_exit(); 289 | } 290 | } 291 | strcpy(opts.ram, val); 292 | } 293 | // -d=SIZE 磁盘大小 294 | else if (strncmp(arg, "-d=", 3) == 0) { 295 | char *val = arg + 3; 296 | // 验证: 必须是数字+单位(m/g/t) 297 | int len = strlen(val); 298 | if (len < 2) { 299 | error("Invalid disk size: %s (e.g. 10g, 1t)\n", arg); 300 | err_exit(); 301 | } 302 | char unit = tolower(val[len-1]); 303 | if (unit != 'm' && unit != 'g' && unit != 't') { 304 | error("Invalid disk size: %s (unit must be m, g or t)\n", arg); 305 | err_exit(); 306 | } 307 | // 验证数字部分 308 | for (int i = 0; i < len - 1; i++) { 309 | if (!isdigit(val[i])) { 310 | error("Invalid disk size: %s (must be number+unit)\n", arg); 311 | err_exit(); 312 | } 313 | } 314 | strcpy(opts.disk_size, val); 315 | } 316 | // -n=MODE 网络模式 317 | else if (strncmp(arg, "-n=", 3) == 0) { 318 | char *val = arg + 3; 319 | // 验证: 必须是 bridge 或 nat 320 | if (strcasecmp(val, "bridge") == 0) { 321 | strcpy(opts.netmode, "Bridged"); 322 | } else if (strcasecmp(val, "nat") == 0) { 323 | strcpy(opts.netmode, "NAT"); 324 | } else { 325 | error("Invalid network mode: %s (must be bridge or nat)\n", arg); 326 | err_exit(); 327 | } 328 | } 329 | // -i=NIC 绑定网卡 330 | else if (strncmp(arg, "-i=", 3) == 0) { 331 | char *val = arg + 3; 332 | // 验证: 非空 333 | if (strlen(val) == 0) { 334 | error("Invalid bind NIC: %s (cannot be empty)\n", arg); 335 | err_exit(); 336 | } 337 | strcpy(opts.bind_nic, val); 338 | } 339 | else { 340 | error("Unknown option: %s\n", arg); 341 | err_exit(); 342 | } 343 | optind++; 344 | } 345 | } 346 | 347 | vm_create(optarg, template_name, has_from ? &opts : NULL); 348 | } 349 | break; 350 | 351 | case 'e': //config 352 | vm_config(optarg); 353 | break; 354 | 355 | case 's': //start 356 | vm_start(optarg); 357 | break; 358 | 359 | case 'B': //autoboot 360 | vm_autoboot(); 361 | break; 362 | 363 | case 'b': //abinfo 364 | vm_autoboot_list(); 365 | break; 366 | 367 | case 'L': //login 368 | vm_login(optarg); 369 | break; 370 | 371 | case 'S': //stop 372 | vm_stop(optarg); 373 | break; 374 | 375 | case 'R': //restart 376 | case 'O': //reboot 377 | vm_restart(optarg); 378 | 379 | //延迟1秒为防止tap被过早清理掉 380 | //执行 bvm --restart 后由于时间差误删除tap 381 | //当时在测试 dhcp 时发现非 freebsd 虚拟机,比如 openbsd 382 | //在使用 bvm --restart 后 tap 丢失 383 | //最后才发现是清理 tap 模块的执行速度太快了 384 | //在虚拟机被复位后还没开始启动这个间隙里,清理 tap 就开始了 385 | //所以需要延迟一下,把这个间隙错开 386 | sleep(1); 387 | break; 388 | 389 | case 'p': //poweroff 390 | vm_poweroff(optarg, 1); 391 | vm_killsession(optarg); 392 | break; 393 | 394 | case 'c': //clone 395 | vm_clone(optarg, argv[optind]); 396 | break; 397 | 398 | case 'd': //remove 399 | { 400 | char *vm_list[256]; 401 | int vm_count = 0; 402 | vm_list[vm_count++] = optarg; 403 | 404 | while (optind < argc && argv[optind] && argv[optind][0] != '-') { 405 | if (vm_count < 256) { 406 | vm_list[vm_count++] = argv[optind]; 407 | } 408 | optind++; 409 | } 410 | 411 | fprintf(stderr, "\033[33mEnter 'YES' To remove the vm ["); 412 | for(int i=0; i [args...] 60 | Options: 61 | --abinfo Display information about auto-boot VMs 62 | --addisk Add a new disk to VM 63 | --addnat Add NAT 64 | --addswitch Add Switch 65 | --autoboot Auto-boot VMs 66 | --clone Clone VM 67 | --config Configure VM 68 | --create Create new VM 69 | --deldisk Delete a disk 70 | --delnat Delete NAT 71 | --delswitch Delete Switch 72 | --swinfo Output Switch info 73 | --decrypt Decrypt VM 74 | --encrypt Encrypt VM 75 | --login Log in to VM 76 | --ls List VMs and status 77 | --ll List VMs and status in long format 78 | --netstat Show VM network status 79 | --natinfo Output NAT info 80 | --lock Lock VM 81 | --lockall Lock all VMs 82 | --os Output OS list 83 | --poweroff Force power off 84 | --reload-nat Reload NAT redirect port 85 | --remove Destroy VM(s) 86 | --rename Rename VM 87 | --restart Restart VM 88 | --reboot Restart VM (alias for --restart) 89 | --rollback Roll back to snapshot point 90 | --setnat Set NAT IP address 91 | --setsw Set Switch IP address 92 | --setpr Set port redirection list 93 | --showpr Show port redirection list 94 | --showdev Show device 95 | --showdevall Show all devices in class mode 96 | --showdevuse Show all devices in simple mode 97 | --showdhcp Show all DHCP clients 98 | --showsnap Show snapshot list of VM 99 | --showsnapall Show snapshot list of all VMs 100 | --showstats Show VM stats 101 | --snapshot Generate snapshot for VM 102 | --start Start VM 103 | --stop Stop VM 104 | --unlock Unlock VM 105 | --unlockall Unlock all VMs 106 | --unsetsw Unset Switch IP address 107 | --vminfo Output VM info 108 | ``` 109 | 110 | ## Q&A 111 | 112 | ### Question 1: How to create a virtual machine? 113 | ``` 114 | Answer: You can create a new virtual machine in three ways: 115 | 116 | 1. Use the command 'bvm --create vmname' to create 117 | 118 | 2. Use an existing VM configuration as template: 119 | 120 | bvm --create newname from template-vmname 121 | 122 | - template-vmname: Name of existing VM (template) 123 | - newname: Name of new VM 124 | This command will copy the template VM's configuration to the new VM for customization. 125 | 126 | Standard templates available: 127 | - freebsd (FreeBSD standard configuration) 128 | - linux (Linux standard configuration) 129 | - windows (Windows standard configuration) 130 | 131 | You can also use any existing VM name as a template. 132 | 133 | Optional parameters for template creation: 134 | -s Set boot type to grub 135 | -U=N Set CPU count (e.g. -U=4) 136 | -m=SIZE Set memory size (e.g. -m=512m, -m=2g) 137 | -d=SIZE Set first disk size (e.g. -d=10g, -d=1t) 138 | -n=MODE Set network mode (bridge or nat) 139 | -i=NIC Set bind NIC (e.g. -i=em0) 140 | 141 | Example: 142 | bvm --create myvm from linux -s -U=4 -m=512m -d=10g -n=bridge -i=em0 143 | 144 | 3. Clone an existing VM: 145 | 146 | bvm --clone oldname newname 147 | 148 | - oldname: Name of existing VM 149 | - newname: Name of new VM 150 | This command will make a complete copy of a VM. 151 | ``` 152 | ### Question 2: How to modify VM configuration? 153 | ``` 154 | Answer: The VM must be powered off, then use command 'bvm --config vmname' to modify settings 155 | Some configuration parameters explained: 156 | 157 | Parameter Description 158 | --------- ----------- 159 | cpus Number of CPUs used by VM (the total vCPU count) 160 | ram Memory allocated to VM (e.g. 512M or 1G) 161 | CD Enable/disable CD-ROM (on/off) 162 | cd numbers Number of CD drives (1-4) 163 | cd(N) iso ISO path for CD N (enter '-' to clear for cd1-cd3) 164 | boot from Boot options (cd0:CD boot/hd0:Hard disk boot) 165 | boot type Boot method (grub: Standard, uefi: UEFI, uefi_csm: UEFI CSM/Legacy BIOS) 166 | TPM (UEFI) Enable TPM 2.0 support (requires UEFI, needed for Windows 11) 167 | shared folder Share host directories with the VM (VirtIO-9P) 168 | VNC Enable/disable VNC display 169 | VNC bind VNC server bind address (default: 0.0.0.0) 170 | VNC port VNC server port number 171 | VNC width/height VNC display resolution 172 | VNC password Optional password for VNC connection 173 | VNC wait Wait for VNC connection before boot 174 | audio Enable HDA audio device 175 | auto boot Auto-start configuration (see bvm --autoboot) 176 | hostbridge CPU architecture (intel:hostbridge/AMD:amd_hostbridge) 177 | disk config Disk configuration (can add/remove disks, set storage interface: ahci-hd/virtio-blk/nvme) 178 | network config Network configuration (Bridge/NAT/Switch modes) 179 | ``` 180 | ### Question 3: How to view VM configuration information? 181 | ``` 182 | Answer: Use 'bvm --vminfo vmname' to view detailed VM information, including VM name, OS, IP address, network mode, disk info, etc. 183 | 184 | Example output: 185 | --------------------------- 186 | Welcome to Bhyve Vm Manager 187 | --------------------------- 188 | name : debian 189 | os type : Debian 190 | ram : 512m 191 | cpus : 1 192 | disk interface : ahci-hd 193 | disk numbers : 1 194 | |-ZFS support : off 195 | |-disk(0) size : 5g 196 | cd status : on 197 | |-iso path : /root/iso/debian-9.3.0-amd64-netinst.iso 198 | boot from : cd0 199 | hostbridge : hostbridge 200 | boot type : grub 201 | auto boot : yes 202 | |-index : 2 203 | |-time : 15 sec. 204 | nic interface : e1000 205 | nic numbers : 1 206 | nic_0 207 | |-network mode : NAT 208 | |-wan : em0 209 | |-gateway : nat2 [GW 192.168.1.1/24] 210 | |-redirect : disable 211 | |-bridge : bridge1 212 | |-tap : vmnet1 213 | |-ip : 192.168.1.10/24 214 | status : off 215 | lock : no 216 | crypt : no 217 | ``` 218 | ### Question 4: How to start a VM? 219 | ``` 220 | Answer: Use command 'bvm --start vmname' to start, use 'bvm --restart vmname' or 'bvm --reboot vmname' to restart. For VMs using GRUB boot mode, use 'bvm --login vmname' to log in. For UEFI boot mode, 'bvm --login' can be attempted, but console login support depends on the guest OS configuration; if it fails, please use VNC or SSH. 221 | ``` 222 | 223 | ### Question 5: What is VM auto-start and how to use it? 224 | ``` 225 | Answer: VM auto-start means automatically starting VMs when the system boots. For example: with two VMs, vm1 and vm2, where vm1 is a database server and vm2 is a web server that depends on vm1's database service, vm1 must start before vm2. In vm1 and vm2's configurations, there are options like: 226 | 227 | vm1's config: 228 | [8]. auto boot : yes 229 | [9]. boot index : 1 230 | [a]. boot time : 60 231 | 232 | vm2's config: 233 | [8]. auto boot : yes 234 | [9]. boot index : 2 235 | [a]. boot time : 15 236 | 237 | Where [8] indicates auto-start, [9] indicates start order, [a] indicates start delay. 238 | This means vm2 will start 60 seconds after vm1, and if there's a vm3, its start time depends on vm2's boot time value. 239 | 240 | To properly use this option, add to startup script: 241 | sysrc bvmd_enable=yes 242 | 243 | When system boots, it will automatically start all VMs set for auto-start in order, or you can manually start with: 244 | bvm --autoboot 245 | 246 | You can also check auto-start VM settings anytime: 247 | bvm --abinfo 248 | 249 | Example output: 250 | --------------------------------- 251 | idx vm time(sec) 252 | --------------------------------- 253 | 1 freebsd 18 254 | 2 debian 15 255 | ``` 256 | ### Question 6: How to shut down a VM? 257 | ``` 258 | Answer: There are two ways: 259 | 260 | 1. Normal shutdown: 261 | bvm --stop vmname 262 | 263 | 2. Force shutdown (use when VM cannot shut down normally): 264 | bvm --poweroff vmname 265 | ``` 266 | ### Question 7: How to view VM list? 267 | ``` 268 | Answer: There are two formats - long and short: 269 | 270 | - Short list: 271 | bvm --ls 272 | 273 | - Long list: 274 | bvm --ll 275 | Both show VM name, status, OS, IP address etc. 276 | 277 | You can also use parameters to get specific information: 278 | 279 | - Online list: 280 | bvm --ls online 281 | 282 | - By status: 283 | bvm --ls bystatus 284 | 285 | - By OS: 286 | bvm --ls byos 287 | 288 | - By IP: 289 | bvm --ls byip 290 | ``` 291 | ### Question 8: How to lock VMs? 292 | ``` 293 | Answer: Sometimes you need to prevent accidental VM deletion by using the lock feature. When locked, VMs cannot be deleted until unlocked. 294 | 295 | bvm --lock vmname 296 | bvm --unlock vmname 297 | bvm --lockall 298 | bvm --unlockall 299 | ``` 300 | 301 | ### Question 9: VM encryption and decryption operations 302 | ``` 303 | Answer: For security, some VMs need encryption when powered off to prevent unauthorized use of VM files. Encrypted VMs cannot start until decrypted. Note that encryption and decryption require the same password; if the password is lost, it cannot be decrypted. Starting from bvm 1.3.4, due to the modified encryption algorithm, old encrypted VMs cannot be decrypted; you need to decrypt them with an old version first, then encrypt them with a new version. 304 | 305 | VM encryption/decryption is done with these commands: 306 | 307 | bvm --encrypt vmname 308 | bvm --decrypt vmname 309 | ``` 310 | ### Question 10: How to check VM network status? 311 | ``` 312 | Answer: Use ‘bvm --netstat’ command to view online VM network status, including network mode, IP address, gateway, port forwarding, bridge, TAP etc. 313 | 314 | Example output: 315 | NAME NIC MODE IP GATEWAY PORTS BRIDGE TAP 316 | ob 0 NAT dhcp 172.16.1.1/24 (nat0) - bridge0 vmnet0 317 | c 0 NAT dhcp 172.16.1.1/24 (nat0) - bridge0 vmnet1 318 | freebsd-14 0 NAT 172.16.1.10/24 172.16.1.1/24 (nat0) tcp 22:2224 bridge0 vmnet3 319 | ``` 320 | 321 | ### Question 11: How to view network device information? 322 | ``` 323 | Answer: You can use these commands to view network device information: 324 | 325 | bvm --showdev 326 | bvm --showdevall 327 | bvm --showdevuse 328 | 329 | Example output: 330 | default-bridge 331 | |-em0 332 | |-c (nic1) 333 | |-test (nic1) 334 | nat0 [172.16.1.1/24] 335 | |-ob (nic0) 336 | |-c (nic0) 337 | |-c11 (nic0) 338 | |-c12 (nic0) 339 | |-centos (nic0) 340 | |-test (nic0) 341 | |-freebsd-14 (nic0) 342 | nat1 [10.10.30.1/24] 343 | |-null 344 | nat2 [192.168.1.1/24] 345 | |-c2 (nic0) 346 | |-debian (nic0) 347 | switch0 [10.0.1.0/24] 348 | |-null 349 | switch1 [10.0.2.0/24] 350 | |-null 351 | switch2 [10.0.3.0/24] 352 | |-null 353 | switch3 354 | |-null 355 | switch4 356 | |-null 357 | switch5 358 | |-null 359 | switch6 360 | |-null 361 | switch7 362 | |-null 363 | 364 | The default-bridge is the default VM-host bridge; nat0-nat2 are 3 reserved NAT interfaces for host communication; switch0-switch7 are 8 reserved VM communication interfaces, which are also bridges but differ from the default bridge as they're mainly for internal networking and usually don't include host physical NICs. 365 | ``` 366 | ### Question 12: What network modes does bvm have? 367 | ``` 368 | Answer: bvm provides bridge and NAT modes, with 3 reserved NAT interfaces for host communication and 8 switch interfaces for host communication. Administrators can set NAT network IP addresses as needed, switches usually don't need configuration except in special cases. 369 | 370 | NAT configuration is in /usr/local/etc/bvm/nat.conf, defined as: 371 | 372 | nat0=172.16.1.1/24 373 | nat1=10.10.30.1/24 374 | nat2=192.168.1.1/24 375 | 376 | Switch configuration is in /usr/local/etc/bvm/switch.conf, defined as: 377 | 378 | switch0=10.0.1.0/24 379 | switch1=10.0.2.0/24 380 | switch2=10.0.3.0/24 381 | switch3= 382 | switch4= 383 | switch5= 384 | switch6= 385 | switch7= 386 | 387 | bvm provides commands to manage NAT and switch interfaces: 388 | 389 | Query commands: 390 | 391 | bvm --natinfo 392 | bvm --swinfo 393 | 394 | Setting commands: 395 | 396 | bvm --setnat 397 | bvm --setsw 398 | bvm --unsetsw 399 | 400 | Add commands: 401 | 402 | bvm --addnat 403 | bvm --addswitch 404 | 405 | Delete commands: 406 | 407 | bvm --delnat 408 | bvm --delswitch 409 | ``` 410 | 411 | ### Question 13: What is port forwarding? 412 | ``` 413 | Answer: Port forwarding maps internal VM ports to host ports, enabling VM-host communication. bvm allows setting port forwarding during VM creation and configuration. Additionally, bvm supports dynamic port forwarding to add/remove rules without stopping VMs, using commands: 414 | 415 | bvm --setpr 416 | bvm --showpr 417 | 418 | For example, to set port forwarding for a VM with IP 192.168.1.10: 419 | 420 | bvm --setpr 192.168.1.10 421 | 422 | You can view port forwarding rules anytime: 423 | 424 | bvm --showpr 425 | 426 | Example output: 427 | PROTO VM IP:PORT HOST PORT VM NAME 428 | tcp 172.16.1.10:22 2224 freebsd-14 429 | tcp 172.16.1.10:80 8080 freebsd-14 430 | ``` 431 | ### Question 14: How to view DHCP client information? 432 | ``` 433 | Answer: Use ’bvm --showdhcp‘ command to view online VM DHCP client information, including VM name, MAC address, IP address, lease time etc. 434 | 435 | Example output: 436 | ip mac bind_time status 437 | -- --- --------- ------ 438 | 172.16.1.100 00:a0:98:ad:89:74 03/26 09:30:37 - 03/26 19:30:37 ASSOCIATED 439 | 192.168.1.100 00:a0:98:dc:ff:5b 03/26 09:34:26 - 03/27 09:34:26 ASSOCIATED 440 | 441 | ``` 442 | ### Question 15: How to configure DHCP? 443 | ``` 444 | Answer: The DHCP configuration file is located at /usr/local/etc/bvm/dhcp.conf, where you can modify the nat0-nat2 parameters as needed. 445 | 446 | Static IP addresses should be written in the following format: 447 | 448 | nat0_static_ip_01="172.16.1.2 00:00:00:00:00:00" 449 | nat0_static_ip_02="172.16.1.3 00:00:00:00:00:00" 450 | 451 | Dynamic IP range should be written in the following format: 452 | 453 | nat0_dynamic_ip="172.16.1.100 172.16.1.254" 454 | 455 | Other sections can be filled in according to the default configuration. 456 | ``` 457 | ### Question 16: How to view VM CPU, memory, network traffic information? 458 | ``` 459 | Answer: Use 'bvm --showstats vmname' command to view VM CPU, memory, network traffic information. 460 | 461 | Example output: 462 | [ Infrastructure Metrics ] 463 | CPU Usage : 0.00% (Allocated: 1 Cores) 464 | Memory Usage : 0.97% (Allocated: 2g, Active: 19.89 MB) 465 | Disk I/O & Cap : 466 | - disk0 : Cap: 5g, Used: 1.82 GB (/var/vm/fb/disk.img) 467 | Network Traffic: 468 | - vmnet0 : RX: 726 B / TX: 4.43 KB (Drops: 0) 469 | 470 | [ Advanced VM Stats ] 471 | VM Exits : 1437238 (Rate: ~45784/sec) 472 | - IO Access : 202732 473 | - Emulation : 685290 474 | - Interrupts : 45059 (NMI: 0) 475 | 476 | [ Availability & Services ] 477 | PID : 6044 478 | Host Load Avg : 1.44, 1.43, 1.36 479 | Status : Online 480 | Uptime : 52:34 (Process) 481 | Boot Time : 31s (CPU Runtime) 482 | VNC Service : Disabled 483 | ``` 484 | 485 | ### Question 17: How to use file sharing (Shared Folder)? 486 | ``` 487 | Answer: Shared folders allow you to share host directories with virtual machines. Enable 'shared folder' in the VM configuration, set the share name and host path. 488 | 489 | Configuration options: 490 | shared folder Enable/disable file sharing (on/off) 491 | share name Name used in guest to identify the share 492 | share path Host directory path to share 493 | share ro Read-only mode (on/off) 494 | 495 | Mount in Linux guest: 496 | mkdir -p /mnt/hostshare 497 | mount -t 9p -o trans=virtio hostshare /mnt/hostshare 498 | 499 | Note: VirtIO-9P is well supported in Linux guests. FreeBSD 14 guests lack the virtio_p9fs module; FreeBSD 15+ has full support. 500 | ``` 501 | -------------------------------------------------------------------------------- /bvm.8: -------------------------------------------------------------------------------- 1 | .Dd December 14, 2025 2 | .Dt BVM 8 3 | .Os 4 | .Sh NAME 5 | .Nm bvm 6 | .Nd "Bhyve Virtual machines Management tool" 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Cm --help 10 | .Nm 11 | .Cm --version 12 | .Pp 13 | .Nm 14 | .Cm --create 15 | .Ar name 16 | .Op Cm from Ar template-name Op Fl s | Fl U Ns = Ns Ar N | Fl m Ns = Ns Ar SIZE | Fl d Ns = Ns Ar SIZE | Fl n Ns = Ns Ar MODE | Fl i Ns = Ns Ar NIC 17 | .Nm 18 | .Cm --config 19 | .Ar name 20 | .Nm 21 | .Cm --vminfo 22 | .Ar name 23 | .Nm 24 | .Cm --showstats 25 | .Ar name 26 | .Nm 27 | .Cm --os 28 | .Pp 29 | .Nm 30 | .Cm --ls 31 | .Op Ar byname | byos | bystatus | online 32 | .Nm 33 | .Cm --ll 34 | .Op Ar byname | byip | byos | bystatus | online 35 | .Nm 36 | .Cm --login 37 | .Ar name 38 | .Nm 39 | .Cm --start 40 | .Ar name 41 | .Nm 42 | .Cm --restart 43 | .Ar name 44 | .Nm 45 | .Cm --reboot 46 | .Ar name 47 | .Nm 48 | .Cm --stop 49 | .Ar name 50 | .Nm 51 | .Cm --poweroff 52 | .Ar name 53 | .Nm 54 | .Cm --set-hd-boot 55 | .Ar name 56 | .Pp 57 | .Nm 58 | .Cm --clone 59 | .Ar name new-name 60 | .Nm 61 | .Cm --remove 62 | .Ar name ... 63 | .Nm 64 | .Cm --rename 65 | .Ar name new-name 66 | .Pp 67 | .Nm 68 | .Cm --encrypt 69 | .Ar name 70 | .Nm 71 | .Cm --decrypt 72 | .Ar name 73 | .Pp 74 | .Nm 75 | .Cm --lock 76 | .Ar name 77 | .Nm 78 | .Cm --unlock 79 | .Ar name 80 | .Nm 81 | .Cm --lockall 82 | .Nm 83 | .Cm --unlockall 84 | .Pp 85 | .Nm 86 | .Cm --addisk 87 | .Ar name 88 | .Nm 89 | .Cm --deldisk 90 | .Ar name 91 | .Pp 92 | .Nm 93 | .Cm --abinfo 94 | .Nm 95 | .Cm --autoboot 96 | .Pp 97 | .Nm 98 | .Cm --reload-nat 99 | .Pp 100 | .Nm 101 | .Cm --setnat 102 | .Ar Nat ip/mask 103 | .Nm 104 | .Cm --setsw 105 | .Ar Switch ip/mask 106 | .Nm 107 | .Cm --unsetsw 108 | .Ar Switch 109 | .Pp 110 | .Nm 111 | .Cm --setpr 112 | .Ar ip 113 | .Nm 114 | .Cm --showpr 115 | .Pp 116 | .Nm 117 | .Cm --showdhcp 118 | .Pp 119 | .Nm 120 | .Cm --showdev 121 | .Nm 122 | .Cm --showdevuse 123 | .Nm 124 | .Cm --showdevall 125 | .Pp 126 | .Nm 127 | .Cm --snapshot 128 | .Ar name 129 | .Nm 130 | .Cm --rollback 131 | .Ar name 132 | .Nm 133 | .Cm --showsnap 134 | .Ar name 135 | .Nm 136 | .Cm --showsnapall 137 | .Pp 138 | .Nm 139 | .Cm --netstat 140 | .Pp 141 | .\" ============ DESCRIPTION ============= 142 | .Sh DESCRIPTION 143 | The 144 | .Nm 145 | is a 146 | .Xr bhyve 8 147 | virtual machine management tool based on FreeBSD that 148 | allows you to easily manage virtual machines. 149 | .Pp 150 | The virtual machine's network is handled by one or more virtual switches. 151 | Each switch has a simple name, which is stored in the corresponding 152 | configuration file and can be modified by the 153 | .Nm 154 | create one or more 155 | .Xr bridge 4 156 | devices for each virtual switch and dynamically assign them to the 157 | virtual machine 158 | .Xr tap 4 159 | interface. When the virtual machine is shut down normally, the 160 | .Nm 161 | will automatically clean up the useless network devices. 162 | .Pp 163 | The 164 | .Nm 165 | supports multiple network cards and multiple hard disks. 166 | Each virtual machine can have multiple network cards or multiple hard disks, 167 | and can easily add, delete, and modify network cards or hard disks. 168 | .\" ============ BASIC SETUP ============ 169 | .Sh BASIC SETUP 170 | Once 171 | .Nm 172 | is installed, the first thing you have to do is set up the virtual 173 | machine's storage directory. 174 | .Pp 175 | Edit the value of 176 | .Pa vmdir 177 | in 178 | .Pa /usr/local/etc/bvm/bvm.conf 179 | .Bd -literal -offset indent 180 | vmdir=/your/vm/dir/ 181 | .Ed 182 | .Pp 183 | If you want to automatically boot the specified list of virtual machines 184 | in order while the host is booting, add the following 185 | lines to 186 | .Pa /etc/rc.conf 187 | to enable: 188 | .Bd -literal -offset indent 189 | bvmd_enable="YES" 190 | .Ed 191 | .\" ============ OPTIONS ============= 192 | .Sh OPTIONS 193 | .Bl -tag -width indent 194 | .It Cm --abinfo 195 | Show information for all auto-bootstrap virtual machines. 196 | .It Cm --addisk Ar name 197 | Add a hard disk to the virtual machine. 198 | .It Cm --autoboot 199 | Start the automatic booting virtual machine in order. 200 | Boot option, which is usually not required in administrator manual 201 | maintenance mode. Sometimes the host is powered on again after shutdown 202 | or unexpected downtime. 203 | At this time, the administrator-set virtual machine can be automatically 204 | started by the rc script. The method is to use 205 | .Nm Cm --config 206 | to modify the default value of the auto boot menu to yes. 207 | 208 | In self-start mode, 209 | .Nm 210 | will refer to the boot level and delay options of vm. The boot level is 211 | the boot priority. When there are multiple self-starting virtual machines, 212 | the lower the startup priority, the more the virtual machine starts. 213 | Back to front. Boot time (boot time) 214 | After starting the current virtual machine, it can also be said that 215 | the number of seconds is tentative before starting the next virtual machine. 216 | The reason for designing these two parameters is because some services may 217 | need to be activated first to ensure normal business. 218 | 219 | For example, the relationship between the database and the web service, 220 | if the data virtual machine library is not prioritized, 221 | it may cause the web service to be abnormal! 222 | 223 | The correct way to use this option is to add the startup script: 224 | .Bd -literal -offset indent 225 | # sysrc bvmd_enable=yes 226 | .Ed 227 | .It Cm --clone Ar name Ar new-name 228 | Clone a new virtual machine from an existing virtual machine. 229 | .It Cm --config Ar name 230 | Edit the configuration of the virtual machine. 231 | This option needs to be used in the case of a virtual machine. Options that 232 | allow users to configure independently are: cpus, ram, iso path, boot from, 233 | hostbridge, uefi, TPM, VNC settings, shared folder, audio, auto boot, disk config, and network config. 234 | .Bl -tag -width 17n 235 | .It cpus 236 | The total number of virtual CPUs used by the current virtual machine. 237 | .It ram 238 | The size of the memory requested by the current virtual machine, 239 | the unit of which can be M/G, etc. 240 | .It iso path 241 | The directory where the iso disc image is located (is the directory, 242 | non-iso absolute address). When the directory is specified, 243 | .Nm 244 | will automatically list the iso files contained in the current directory. 245 | The user can select the menu number. 246 | .It boot from 247 | This is an important option. When the system is first installed, 248 | .Nm 249 | will automatically adjust this option to hd0 for system startup. 250 | When you need to use iso disc for system maintenance, you can change 251 | this option to cd0. 252 | .It CD 253 | Enable or disable CD-ROM support (on/off). 254 | .It "cd numbers" 255 | Number of CD-ROM drives (1-4). Multiple CDs are useful for scenarios 256 | like Windows driver installation. 257 | .It "cd(N) iso" 258 | ISO path for CD N. Enter '-' or 'none' to clear the ISO path for cd1-cd3. 259 | The cd(0) is required and cannot be cleared when CD is enabled. 260 | .It boot type 261 | Select the boot method. Options are: 262 | .Bl -tag -width 10n 263 | .It grub 264 | Standard Bhyve loader (bhyveload/grub-bhyve). Recommended for most BSD and Linux VMs. 265 | .It uefi 266 | UEFI boot mode. Required for Windows and some modern Linux distributions. 267 | Often used with VNC for GUI. 268 | .It uefi_csm 269 | UEFI Compatibility Support Module (Provides Legacy BIOS compatibility). 270 | .El 271 | Note: UEFI modes might have limited console support with 272 | .Cm --login 273 | depending on the guest OS configuration. 274 | .It TPM (UEFI) 275 | Enable TPM 2.0 (Trusted Platform Module) support. This is required for 276 | Windows 11 installation and needs UEFI boot mode. The swtpm package must 277 | be installed on the host. 278 | .It VNC settings 279 | VNC configuration includes bind address (default 0.0.0.0), port number, 280 | display resolution (width/height), optional password protection, and 281 | wait option (wait for VNC connection before boot). 282 | .It audio 283 | Enable HDA audio device support for the virtual machine. Requires /dev/dsp0 284 | on the host. 285 | .It auto boot 286 | See 287 | .Cm --autoboot 288 | for details. 289 | .It hostbridge 290 | This is a schema attribute whose attribute value should be hostbridge when 291 | using the Intel system CPU; its attribute value should be amd_hostbridge when 292 | using the AMD series CPU. 293 | .It disk config 294 | The option can add or delete the hard disk of the current virtual machine. 295 | Supports multiple storage interfaces: ahci-hd (AHCI), virtio-blk (VirtIO Block), 296 | and nvme (NVM Express). If you only want to add the hard disk, it is recommended to use 297 | .Nm 298 | .Cm --addisk 299 | shortcut command. 300 | .It shared folder 301 | VirtIO-9P file sharing. This option allows sharing host directories 302 | with the virtual machine. When enabled, you can set the share name 303 | (used in guest to identify the share), share path (host directory to share), 304 | and read-only mode. Linux guests can mount the share using: 305 | .Bd -literal -offset indent 306 | mount -t 9p -o trans=virtio sharename /mnt/point 307 | .Ed 308 | Note: FreeBSD 14 guests lack virtio_p9fs support; FreeBSD 15+ has full support. 309 | .It network config 310 | This option allows you to configure the networking of virtual machines. 311 | Supports Bridge, NAT, and Switch network modes with built-in DHCP server. 312 | .El 313 | .It Cm --create Ar name Op Cm from Ar template-name Op options 314 | Create a new virtual machine. 315 | .Bd -literal -offset indent 316 | # bvm --create vm1 317 | # bvm --create vm2 from vm1 318 | # bvm --create vm3 from Linux -s -U=4 -m=512m -d=10g -n=bridge -i=em0 319 | .Ed 320 | .Pp 321 | Standard templates available: 322 | .Bl -tag -width 10n 323 | .It freebsd 324 | FreeBSD standard configuration. 325 | .It linux 326 | Linux standard configuration. 327 | .It windows 328 | Windows standard configuration. 329 | .El 330 | .Pp 331 | You can also use any existing VM name as a template. 332 | .Pp 333 | When using 334 | .Cm from 335 | to create from a template, optional parameters can override template settings: 336 | .Bl -tag -width 10n 337 | .It Fl s 338 | Set boot type to grub. 339 | .It Fl U Ns = Ns Ar N 340 | Set CPU count (e.g. -U=4). 341 | .It Fl m Ns = Ns Ar SIZE 342 | Set memory size (e.g. -m=512m, -m=2g). 343 | .It Fl d Ns = Ns Ar SIZE 344 | Set first disk size (e.g. -d=10g, -d=1t). 345 | .It Fl n Ns = Ns Ar MODE 346 | Set network mode (bridge or nat). 347 | .It Fl i Ns = Ns Ar NIC 348 | Set bind NIC (e.g. -i=em0). 349 | .El 350 | .It Cm --deldisk Ar name 351 | Delete the disks in the virtual machine. 352 | .It Cm --decrypt Ar name 353 | Decrypt the virtual machine. 354 | .It Cm --encrypt Ar name 355 | Encrypt the virtual machine. 356 | .It Cm --help 357 | Show all options and descriptions. 358 | .It Cm --login Ar name 359 | Login to a running virtual machine from the console. For UEFI mode, 360 | console support depends on the guest OS configuration; if it fails, please use 361 | the VNC client or SSH. 362 | .It Cm --ls Op Ar byname | byos | bystatus | online 363 | Show a list and status of all virtual machines. 364 | By default, the output is sorted in ascending order by name, 365 | or one of the following options is specified. 366 | .Bl -tag -width 17n 367 | .It Ar byname 368 | Output list in ascending order by name. 369 | .It Ar byos 370 | Output list in ascending order by OS. 371 | .It Ar bystatus 372 | Output list in ascending order by virtual machine status. 373 | .It Ar online 374 | Only show running virtual machines. 375 | .El 376 | .Pp 377 | .Bd -literal -offset indent 378 | # bvm --ls byos 379 | NAME GUEST CPU MEMORY DISK STATE 380 | c Debian 1 512M [2]5.5G off 381 | d Debian 1 512M [1]5G off * 382 | abc Debian 1 512M [1]10G on 383 | b FreeBSD 1 1G [1]10G off 384 | bb FreeBSD 1 1G [1]10G off 385 | 386 | # bvm --ls 387 | NAME GUEST CPU MEMORY DISK STATE 388 | abc Debian 1 512M [1]10G on 389 | b FreeBSD 1 1G [1]10G off 390 | bb FreeBSD 1 1G [1]10G off 391 | c Debian 1 512M [2]5.5G off 392 | d Debian 1 512M [1]5G off * 393 | .Ed 394 | .Pp 395 | .Bl -tag -width 17n 396 | The meaning of the list items is as follows: 397 | .It Ar NAME 398 | The name of the virtual machine. 399 | .It Ar GUEST 400 | Virtual machine operating system. 401 | .It Ar CPU 402 | Number of CPUs. 403 | .It Ar MEMORY 404 | Size of memory. 405 | .It Ar DISK 406 | Disk capacity, the number in square brackets represents the number of disks, 407 | and the capacity is the sum of all disk capacities. 408 | .It Ar STATE 409 | The state of the virtual machine, off or on, if the virtual machine is locked, 410 | a yellow symbol '*' is also displayed, if the virtual machine is encrypted, 411 | a red symbol '*' is also displayed. 412 | .El 413 | .It Cm --ll Op Ar byname | byip | byos | bystatus | online 414 | Show a list and status of all virtual machines in long format. 415 | By default, the output is sorted in ascending order by name, 416 | or one of the following options is specified. 417 | .Bl -tag -width 17n 418 | .It Ar byname 419 | Output list in ascending order by name. 420 | .It Ar byip 421 | Output list in ascending order by IP-addr. 422 | .It Ar byos 423 | Output list in ascending order by OS. 424 | .It Ar bystatus 425 | Output list in ascending order by virtual machine status. 426 | .It Ar online 427 | Only show running virtual machines. 428 | .El 429 | .Pp 430 | The added list items have the following meanings: 431 | .Bl -tag -width 17n 432 | .It Ar IP 433 | The IP address of the virtual machine. 434 | .It Ar LOADER 435 | The boot loader for the virtual machine. 436 | .It Ar AUTOSTART 437 | The state in which the virtual machine is automatically started, "Yes" is 438 | automatically started, and the number next to it is the startup sequence. 439 | .El 440 | .It Cm --lock Ar name 441 | Locks the specified virtual machine, and can't 442 | .Cm --config 443 | , 444 | .Cm --remove 445 | , 446 | .Cm --start 447 | , and so on. 448 | Only simple read operations can be performed, such as 449 | .Cm --vminfo 450 | , 451 | .Cm --ls 452 | , 453 | this option also prevents the risk of virtual machine files being accidentally deleted. 454 | .It Cm --lockall 455 | Lock all virtual machines, behaving the same as 456 | .Cm --lock . 457 | .It Cm --netstat 458 | Show all network configuration information of virtual machines, 459 | including NIC, mode, IP address, gateway, port forwarding, bridge, 460 | and TAP device, etc. 461 | .It Cm --os 462 | Show a list of operating systems supported by 463 | .Nm . 464 | .It Cm --poweroff Ar name 465 | Force the virtual machine to power off. When there are some special reasons 466 | that prevent the virtual machine from shutting down properly, you need to use 467 | this option to force the virtual machine to power off. 468 | .It Cm --reload-nat 469 | Reload the NAT port redirect. In general, you do not need to use this option, 470 | .Nm 471 | will automatically handle port redirection, this option is manual mode. 472 | .It Cm --remove Ar name ... 473 | Destroy one or more virtual machines and they cannot be recovered. 474 | Note that the virtual machines cannot be running when you run this command. 475 | .It Cm --rename Ar name new-name 476 | Rename the virtual machine. 477 | .It Cm --restart Ar name 478 | Restart a virtual machine. 479 | .It Cm --reboot Ar name 480 | Restart a virtual machine (alias for --restart). 481 | .It Cm --rollback Ar name 482 | Roll back to the snapshot point. 483 | .It Cm --set-hd-boot Ar name 484 | Set the virtual machine to boot from the hard disk. 485 | .It Cm --setnat Ar nat ip/mask 486 | Set the IP address and mask of the NAT. 487 | .Bd -literal -offset indent 488 | # bvm --setnat nat0 172.16.1.1/24 489 | .Ed 490 | .It Cm --setpr Ar ip 491 | Port redirection is set dynamically with immediate effect. 492 | In general, the virtual machine must be turned off to set the 493 | virtual machine parameters, but this option can set the port redirection 494 | at any time in the virtual machine on state, and take effect immediately. 495 | .Bd -literal -offset indent 496 | # bvm --setpr 10.10.30.10 497 | .Ed 498 | .It Cm --showpr 499 | Show all list of port redirect. 500 | .Bd -literal -offset indent 501 | # bvm --showpr 502 | PROTO VM IP:PORT HOST PORT VM NAME 503 | tcp 172.16.1.10:22 2224 freebsd-14 504 | tcp 172.16.1.10:80 8080 freebsd-14 505 | udp 192.168.1.254:53 9953 abc 506 | tcp 10.10.30.10:22 3322 ob 507 | .Ed 508 | .It Cm --setsw Ar switch ip/mask 509 | Set the ip address and mask of the virtual switch. 510 | The same method as 511 | .Cm --setnat . 512 | .It Cm --showdev 513 | Select a network device and show its relationship to the virtual machine NIC. 514 | .It Cm --showdevall 515 | Show a relationship table for all network devices. 516 | .It Cm --showdevuse 517 | Show a relational table of all network devices in use. 518 | .It Cm --showdhcp 519 | Show all DHCP clients. 520 | .It Cm --showsnap Ar name 521 | Show snapshots list of the virtual machine. 522 | .It Cm --showsnapall 523 | Show snapshots list of the all virtual machines. 524 | .It Cm --showstats Ar name 525 | Show the statistics of the virtual machine, including CPU/Memory usage, Disk I/O, Network traffic, and Service status. 526 | .It Cm --snapshot Ar name 527 | Generating snapshots for the virtual machine. 528 | .It Cm --start Ar name 529 | Start a virtual machine. 530 | .It Cm --stop Ar name 531 | Shut down a virtual machine. 532 | .It Cm --unlock Ar name 533 | Unlock a virtual machine. 534 | .It Cm --unlockall 535 | Unlock all virtual machines. 536 | .It Cm --unsetsw Ar switch 537 | Delete the IP address of the virtual switch. 538 | .It Cm --version 539 | Show the version number of 540 | .Nm 541 | installed. 542 | .It Cm --vminfo Ar name 543 | Shows the configuration of the virtual machine. 544 | .\" ============ CONFIGURE FILES ============= 545 | .Sh CONFIGURE FILES 546 | .Pa /usr/local/etc/bvm/bvm.conf 547 | .Bd -literal -offset indent 548 | This file records the virtual machine's directory and a list of all supported 549 | operating systems. 550 | .Ed 551 | .Pp 552 | .Pa /usr/local/etc/bvm/nat.conf 553 | .Bd -literal -offset indent 554 | This file records the configuration information for all NAT. 555 | .Pp 556 | nat0=172.16.1.1/24 557 | nat1=10.10.30.1/24 558 | nat2=192.168.1.1/24 559 | .Ed 560 | .Pp 561 | .Pa /usr/local/etc/bvm/switch.conf 562 | .Bd -literal -offset indent 563 | This file records the configuration information of all virtual switches. 564 | The default 0-2 sets the IP address, and 3-7 is not set. 565 | .Pp 566 | switch0=10.0.1.0/24 567 | switch1=10.0.2.0/24 568 | switch2=10.0.3.0/24 569 | switch3= 570 | switch4= 571 | switch5= 572 | switch6= 573 | switch7= 574 | .Ed 575 | .Pp 576 | .Pa /usr/local/etc/bvm/dhcp.conf 577 | .Bd -literal -offset indent 578 | This file records DHCP configuration information, including lease time, 579 | DNS, IP address pool, etc. 580 | .Ed 581 | .\" ============ SEE ALSO ============= 582 | .Sh SEE ALSO 583 | .Xr tmux 1 , 584 | .Xr bridge 4 , 585 | .Xr tap 4 , 586 | .Xr bhyve 8 , 587 | .Xr bhyveload 8 , 588 | .Xr zfs 8 589 | .\" ============ BUGS ============= 590 | .Sh BUGS 591 | Please report all bugs/issues/feature requests to the GitHub project at 592 | .Lk https://github.com/bigdragonsoft/bvm 593 | .\" ============ AUTHOR ============= 594 | .Sh AUTHORS 595 | .An Qiang Guo Aq Mt bigdragonsoft@gmail.com 596 | -------------------------------------------------------------------------------- /src/booter.c: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | BVM Copyright (c) 2018-2025, Qiang Guo (bigdragonsoft@gmail.com) 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | *---------------------------------------------------------------------------*/ 26 | 27 | #include "booter.h" 28 | 29 | int main(int argc, char **argv) 30 | { 31 | char vm_name[32]; 32 | if (argc > 1 ) 33 | strcpy(vm_name, argv[1]); 34 | else { 35 | error("bvmb error\n"); 36 | exit(1); 37 | } 38 | 39 | check_bre(); 40 | set_vmdir(); 41 | set_bvm_os(); 42 | 43 | host_version(); 44 | 45 | vm_init(); 46 | vm_node *p; 47 | if ((p = find_vm_list(vm_name)) == NULL) exit(0); 48 | 49 | if (strcmp(p->vm.boot_type, "grub") == 0) { 50 | set_grub_cmd(p); 51 | grub_booter(p); 52 | } 53 | else { 54 | uefi_booter(p); 55 | } 56 | 57 | vm_end(); 58 | 59 | return 0; 60 | } 61 | 62 | // 宿主机版本号 63 | float host_version() 64 | { 65 | float ver = 0; 66 | struct utsname name; 67 | if (uname(&name) == 0) { //success 68 | char *p1 = name.release; 69 | char *p2 = strstr(p1, "-"); 70 | p1[p2-p1] = '\0'; 71 | ver = atof(p1); 72 | } 73 | return ver; 74 | } 75 | 76 | // 执行代码 77 | int run(char *cmd, vm_node *p) 78 | { 79 | convert(cmd, p); 80 | //warn("%s\n", cmd); 81 | 82 | write_log(cmd); 83 | int ret = system(cmd); 84 | return WEXITSTATUS(ret); 85 | } 86 | 87 | // Helper to append CD args 88 | // 只添加有效的CD(ISO路径不为空的) 89 | static void append_cd_args(char *cmd, vm_node *p, int slot_start) { 90 | if (strcmp(p->vm.cdstatus, "on") == 0) { 91 | char t[BUFFERSIZE]; 92 | int slot_id = 0; // 实际使用的槽位号 93 | for (int n=0; nvm.cds); n++) { 94 | // 跳过 ISO 路径为空的 CD 95 | if (strlen(p->vm.cd_iso[n]) == 0) { 96 | continue; 97 | } 98 | sprintf(t, "-s %d:%d,ahci-cd,${vm_cd_iso_%d} ", slot_start, slot_id, n); 99 | strcat(cmd, t); 100 | slot_id++; 101 | } 102 | } 103 | } 104 | 105 | // Helper to append HD args 106 | static void append_hd_args(char *cmd, vm_node *p, int slot_start) { 107 | char t[BUFFERSIZE]; 108 | int slot = slot_start; 109 | int id = 0; 110 | for (int n=0; nvm.disks); n++) { 111 | if (strlen(p->vm.storage_interface) == 0) 112 | sprintf(t, "-s %d:%d,ahci-hd,${vm_disk%d} ", slot, id, n); 113 | else 114 | sprintf(t, "-s %d:%d,${vm_storage_interface},${vm_disk%d} ", slot, id, n); 115 | strcat(cmd, t); 116 | if (++id == 8) slot++; 117 | } 118 | } 119 | 120 | // Helper to count valid CDs (ISO path not empty) 121 | static int count_valid_cds(vm_node *p) { 122 | int count = 0; 123 | for (int n=0; nvm.cds); n++) { 124 | if (strlen(p->vm.cd_iso[n]) > 0) { 125 | count++; 126 | } 127 | } 128 | return count; 129 | } 130 | 131 | // grub启动器 132 | void grub_booter(vm_node *p) 133 | { 134 | int ret; 135 | int boot; 136 | if (strcmp(p->vm.bootfrom, "cd0") == 0) 137 | boot = 0; 138 | else 139 | boot = 1; 140 | 141 | if (boot == 0 && strcmp(p->vm.cdstatus, "off") == 0) { 142 | error("can't start the vm from CD\n"); 143 | exit(1); 144 | } 145 | 146 | // 检查从 CD 启动时是否有有效的 ISO 文件 147 | if (boot == 0 && count_valid_cds(p) == 0) { 148 | error("can't start the vm from CD: no valid ISO file configured\n"); 149 | exit(1); 150 | } 151 | 152 | char cmd[BUFFERSIZE]; 153 | while (1) { 154 | 155 | //grub 156 | if (boot == 0) 157 | strcpy(cmd, "${vm_grubcd}"); 158 | else 159 | strcpy(cmd, "${vm_grubhd}"); 160 | 161 | run(cmd, p); 162 | 163 | //bhyve 164 | char t[BUFFERSIZE]; 165 | strcpy(cmd, "bhyve -c cpus=${vm_cpus},sockets=${vm_sockets},cores=${vm_cores},threads=${vm_threads} -m ${vm_ram} -HAPuw "); 166 | strcat(cmd, "-s 0:0,${vm_hostbridge} "); 167 | 168 | 169 | 170 | // 固定 slot 分配:HD 始终在 slot 2,CD 始终在 HD 之后 171 | // 这样无论从哪里启动,设备路径保持一致,UEFI vars 中的启动项不会失效 172 | int disk_count = atoi(p->vm.disks); 173 | int hd_slots_needed = (disk_count + 7) / 8; 174 | if (hd_slots_needed == 0) hd_slots_needed = 1; 175 | int hd_slot_start = 2; 176 | int cd_slot = 2 + hd_slots_needed; 177 | 178 | // 按固定顺序追加参数:先 HD 后 CD 179 | append_hd_args(cmd, p, hd_slot_start); 180 | append_cd_args(cmd, p, cd_slot); 181 | 182 | 183 | for (int n=0; nvm.nics); n++) { 184 | if (host_version() >= EM0_VER) 185 | if (strlen(p->vm.network_interface) == 0) 186 | sprintf(t, "-s 10:%d,e1000,${vm_tap%d},mac=${vm_mac%d} ", n, n, n); 187 | else 188 | sprintf(t, "-s 10:%d,${vm_network_interface},${vm_tap%d},mac=${vm_mac%d} ", n, n, n); 189 | else 190 | sprintf(t, "-s 10:%d,virtio-net,${vm_tap%d},mac=${vm_mac%d} ", n, n, n); 191 | strcat(cmd, t); 192 | } 193 | 194 | // VirtIO-9P file sharing (grub mode) 195 | if (strcmp(p->vm.share_status, "on") == 0 && strlen(p->vm.share_path) > 0) { 196 | char share_cmd[512]; 197 | if (strcmp(p->vm.share_ro, "on") == 0) 198 | sprintf(share_cmd, "-s 20,virtio-9p,%s=%s,ro ", 199 | p->vm.share_name, p->vm.share_path); 200 | else 201 | sprintf(share_cmd, "-s 20,virtio-9p,%s=%s ", 202 | p->vm.share_name, p->vm.share_path); 203 | strcat(cmd, share_cmd); 204 | } 205 | 206 | strcat(cmd, "-s 31,lpc -l com1,stdio "); 207 | strcat(cmd, "${vm_name}"); 208 | 209 | ret = run(cmd, p); 210 | 211 | /*********** bhyve EXIT STATUS *********** 212 | 0 rebooted 213 | 1 powered off 214 | 2 halted 215 | 3 triple fault 216 | 4 exited due to an error 217 | *******************************************/ 218 | if (ret > 0 && ret < 4) break; 219 | strcpy(cmd, "/usr/sbin/bhyvectl --destroy --vm=${vm_name}"); 220 | run(cmd, p); 221 | boot = 1; 222 | vm_boot_from_hd(p->vm.name); 223 | } 224 | strcpy(cmd, "/usr/sbin/bhyvectl --destroy --vm=${vm_name}"); 225 | run(cmd, p); 226 | for (int n=0; nvm.nics); n++) { 227 | sprintf(cmd, "/sbin/ifconfig ${vm_tap%d} destroy", n); 228 | run(cmd, p); 229 | } 230 | } 231 | 232 | // uefi启动器 233 | void uefi_booter(vm_node *p) 234 | { 235 | int ret; 236 | int boot; 237 | if (strcmp(p->vm.bootfrom, "cd0") == 0) 238 | boot = 0; 239 | else 240 | boot = 1; 241 | 242 | if (boot == 0 && strcmp(p->vm.cdstatus, "off") == 0) { 243 | error("can't start the vm from CD\n"); 244 | exit(1); 245 | } 246 | 247 | // 检查从 CD 启动时是否有有效的 ISO 文件 248 | if (boot == 0 && count_valid_cds(p) == 0) { 249 | error("can't start the vm from CD: no valid ISO file configured\n"); 250 | exit(1); 251 | } 252 | 253 | // 自动迁移逻辑:如果 VM 使用 UEFI 但没有 vars 文件 254 | if (strcmp(p->vm.boot_type, "grub") != 0 && strlen(p->vm.uefi_vars) == 0) { 255 | // 尝试创建 vars 文件 256 | char template_vars[] = "/usr/local/share/uefi-firmware/BHYVE_UEFI_VARS.fd"; 257 | char vm_vars[256]; 258 | sprintf(vm_vars, "%s%s/efivars.fd", vmdir, p->vm.name); 259 | 260 | // 如果模板存在且 vars 文件不存在,则创建 261 | if (access(template_vars, R_OK) == 0 && access(vm_vars, F_OK) != 0) { 262 | // 复制模板 263 | char copy_cmd[512]; 264 | sprintf(copy_cmd, "cp %s %s", template_vars, vm_vars); 265 | if (system(copy_cmd) == 0) { 266 | strcpy(p->vm.uefi_vars, vm_vars); 267 | save_vm_info(p->vm.name, &p->vm); 268 | write_log("Auto-migrated %s to UEFI vars persistence mode", p->vm.name); 269 | } 270 | } 271 | } 272 | 273 | // 如果设置为从光盘启动(boot=0),强制重置 UEFI 变量以确保光盘启动优先 274 | // 这是一个必要的变通方案,因为无法直接修改 nvram 二进制文件中的启动顺序 275 | if (boot == 0 && strcmp(p->vm.boot_type, "grub") != 0 && strlen(p->vm.uefi_vars) > 0) { 276 | char template_vars[] = "/usr/local/share/uefi-firmware/BHYVE_UEFI_VARS.fd"; 277 | char backup_vars[512]; 278 | sprintf(backup_vars, "%s.orig", p->vm.uefi_vars); 279 | 280 | if (access(template_vars, R_OK) == 0 && access(p->vm.uefi_vars, F_OK) == 0) { 281 | // 只有当 .orig 不存在时才创建备份 282 | // 如果 .orig 已存在,说明之前的有效备份还在,不要覆盖它 283 | if (access(backup_vars, F_OK) != 0) { 284 | char backup_cmd[512]; 285 | sprintf(backup_cmd, "cp %s %s", p->vm.uefi_vars, backup_vars); 286 | system(backup_cmd); 287 | write_log("Backed up UEFI vars for VM %s before CD boot", p->vm.name); 288 | } else { 289 | write_log("UEFI vars backup already exists for VM %s, keeping it", p->vm.name); 290 | } 291 | 292 | // 重置 vars 293 | char reset_cmd[512]; 294 | sprintf(reset_cmd, "cp %s %s", template_vars, p->vm.uefi_vars); 295 | system(reset_cmd); 296 | 297 | write_log("Reset UEFI vars for VM %s to ensure CD boot", p->vm.name); 298 | } 299 | } 300 | 301 | // 如果设置为从硬盘启动(boot=1),恢复之前备份的 .orig 文件 302 | // .orig 保存的是切换到 CD 启动之前的 vars 文件(包含正确的 HD 启动项) 303 | // 因为 CD 启动时用空模板覆盖了 vars,所以现在需要恢复 304 | if (boot == 1 && strcmp(p->vm.boot_type, "grub") != 0 && strlen(p->vm.uefi_vars) > 0) { 305 | char backup_vars[512]; 306 | sprintf(backup_vars, "%s.orig", p->vm.uefi_vars); 307 | 308 | if (access(backup_vars, F_OK) == 0) { 309 | // 恢复备份到 vars 310 | char restore_cmd[512]; 311 | sprintf(restore_cmd, "mv %s %s", backup_vars, p->vm.uefi_vars); 312 | system(restore_cmd); 313 | 314 | write_log("Restored UEFI vars backup for VM %s (contains HD boot entries)", p->vm.name); 315 | } 316 | } 317 | 318 | // TPM 启动逻辑 319 | if (strcmp(p->vm.tpmstatus, "on") == 0) { 320 | char tpm_sock[256]; 321 | if (strlen(p->vm.tpmpath) > 0) 322 | strcpy(tpm_sock, p->vm.tpmpath); 323 | else 324 | sprintf(tpm_sock, "/tmp/%s/swtpm.sock", p->vm.name); 325 | 326 | char tpm_dir[256]; 327 | sprintf(tpm_dir, "%s%s/tpm", vmdir, p->vm.name); 328 | 329 | // 检查 swtpm 是否需要初始化 330 | char tpm_permall[512]; 331 | sprintf(tpm_permall, "%s/tpm2-00.permall", tpm_dir); 332 | 333 | if (access(tpm_permall, F_OK) != 0) { 334 | // 创建 swtpm 包装器以修复 FreeBSD 上的 --print-capabilities 问题 335 | // 使用 VM 目录而不是 /tmp 以避免 noexec 问题 336 | char wrapper_path[512]; 337 | sprintf(wrapper_path, "%s%s/swtpm_wrapper.sh", vmdir, p->vm.name); 338 | 339 | FILE *fp = fopen(wrapper_path, "w"); 340 | if (fp) { 341 | fprintf(fp, "#!/bin/sh\n"); 342 | fprintf(fp, "# Check if first argument is one of the commands requiring socket mode\n"); 343 | fprintf(fp, "case \"$1\" in\n"); 344 | fprintf(fp, " socket|cuse|-v|--version|--help|-h)\n"); 345 | fprintf(fp, " exec /usr/local/bin/swtpm \"$@\"\n"); 346 | fprintf(fp, " ;;\n"); 347 | fprintf(fp, " *)\n"); 348 | fprintf(fp, " # Default to socket mode for everything else\n"); 349 | fprintf(fp, " exec /usr/local/bin/swtpm socket \"$@\"\n"); 350 | fprintf(fp, " ;;\n"); 351 | fprintf(fp, "esac\n"); 352 | fclose(fp); 353 | chmod(wrapper_path, 0755); 354 | } 355 | 356 | write_log("Initializing TPM state for %s...", p->vm.name); 357 | char init_cmd[1024]; 358 | // 使用 --tpmstate dir:///path 格式更安全 359 | sprintf(init_cmd, "mkdir -p %s; /usr/local/bin/swtpm_setup --tpm %s --tpmstate dir://%s --create-ek-cert --create-platform-cert --tpm2", tpm_dir, wrapper_path, tpm_dir); 360 | int ret = system(init_cmd); 361 | 362 | // 清理包装器 363 | unlink(wrapper_path); 364 | 365 | if (ret != 0) { 366 | write_log("Error: swtpm_setup failed with exit code %d", ret); 367 | // 继续进行,可能在没有证书的情况下工作,但 Windows 可能会抱怨 368 | } 369 | } 370 | 371 | char sock_dir[256]; 372 | sprintf(sock_dir, "/tmp/%s", p->vm.name); 373 | 374 | char tpm_ctrl_sock[256]; 375 | sprintf(tpm_ctrl_sock, "/tmp/%s/swtpm-ctrl.sock", p->vm.name); 376 | 377 | char start_swtpm[1024]; 378 | char swtpm_log[256]; 379 | sprintf(swtpm_log, "/tmp/swtpm-%s.log", p->vm.name); 380 | 381 | sprintf(start_swtpm, 382 | "mkdir -p %s; " 383 | "mkdir -p %s; " 384 | "rm -f %s; " // 删除过期的 socket 385 | "rm -f %s; " 386 | "/usr/local/bin/swtpm socket --tpmstate dir=%s --ctrl type=unixio,path=%s --server type=unixio,path=%s --tpm2 --flags not-need-init,startup-clear -d --pid file=/var/run/swtpm-%s.pid > %s 2>&1", 387 | sock_dir, tpm_dir, tpm_sock, tpm_ctrl_sock, tpm_dir, tpm_ctrl_sock, tpm_sock, p->vm.name, swtpm_log); 388 | 389 | write_log("Starting swtpm: %s", start_swtpm); 390 | int ret = system(start_swtpm); 391 | 392 | if (ret != 0) { 393 | write_log("Error: swtpm command failed with exit code %d. Check %s for details.", ret, swtpm_log); 394 | 395 | // 将日志内容打印到 bvm 日志 396 | char log_content[1024] = {0}; 397 | FILE *fp = fopen(swtpm_log, "r"); 398 | if (fp) { 399 | size_t n = fread(log_content, 1, sizeof(log_content)-1, fp); 400 | if (n > 0) write_log("swtpm log output: %s", log_content); 401 | fclose(fp); 402 | } 403 | exit(1); 404 | } 405 | 406 | // 等待 socket 准备就绪(最多 10 秒) 407 | int wait_retries = 100; 408 | while (wait_retries-- > 0) { 409 | if (access(tpm_sock, F_OK) == 0) break; 410 | usleep(100000); // 100ms 411 | } 412 | 413 | if (access(tpm_sock, F_OK) != 0) { 414 | write_log("Error: swtpm socket %s not found after waiting 10s. Aborting boot. Check %s for details.", tpm_sock, swtpm_log); 415 | 416 | // 将日志内容打印到 bvm 日志 417 | char log_content[1024] = {0}; 418 | FILE *fp = fopen(swtpm_log, "r"); 419 | if (fp) { 420 | size_t n = fread(log_content, 1, sizeof(log_content)-1, fp); 421 | if (n > 0) write_log("swtpm log output: %s", log_content); 422 | fclose(fp); 423 | } 424 | 425 | // 清理任何部分进程 426 | char stop_swtpm[512]; 427 | sprintf(stop_swtpm, 428 | "if [ -f /var/run/swtpm-%s.pid ]; then " 429 | "kill $(cat /var/run/swtpm-%s.pid); " 430 | "rm /var/run/swtpm-%s.pid; " 431 | "fi", 432 | p->vm.name, p->vm.name, p->vm.name); 433 | system(stop_swtpm); 434 | exit(1); 435 | } 436 | } 437 | 438 | 439 | char cmd[BUFFERSIZE]; 440 | while (1) { 441 | 442 | //bhyve 443 | char t[BUFFERSIZE]; 444 | strcpy(cmd, "bhyve -c cpus=${vm_cpus},sockets=${vm_sockets},cores=${vm_cores},threads=${vm_threads} -m ${vm_ram} -HAPuw "); 445 | 446 | strcat(cmd, "-s 0:0,${vm_hostbridge} "); 447 | 448 | 449 | // 固定 slot 分配:HD 始终在 slot 2,CD 始终在 HD 之后 450 | // 这样无论从哪里启动,设备路径保持一致,UEFI vars 中的启动项不会失效 451 | int disk_count = atoi(p->vm.disks); 452 | int hd_slots_needed = (disk_count + 7) / 8; 453 | if (hd_slots_needed == 0) hd_slots_needed = 1; 454 | int hd_slot_start = 2; 455 | int cd_slot = 2 + hd_slots_needed; 456 | 457 | 458 | // 按固定顺序追加参数:先 HD 后 CD 459 | append_hd_args(cmd, p, hd_slot_start); 460 | append_cd_args(cmd, p, cd_slot); 461 | 462 | 463 | for (int n=0; nvm.nics); n++) { 464 | //经测试 uefi 下 e1000 无效,只能使用 virtio-net 465 | //if (host_version() >= EM0_VER) 466 | // sprintf(t, "-s 10:%d,e1000,${vm_tap%d} ", n, n); 467 | //else 468 | sprintf(t, "-s 10:%d,virtio-net,${vm_tap%d},mac=${vm_mac%d} ", n, n, n); 469 | strcat(cmd, t); 470 | } 471 | // VNC configuration 472 | if (boot == 0 || strcmp(p->vm.vncstatus, "on") == 0) { 473 | char vnc_cmd[256]; 474 | sprintf(vnc_cmd, "-s 29,fbuf,tcp=${vm_vncbind}:${vm_vncport},w=${vm_vncwidth},h=${vm_vncheight}"); 475 | 476 | // Add password if set 477 | if (strlen(p->vm.vncpassword) > 0) { 478 | strcat(vnc_cmd, ",password=${vm_vncpassword}"); 479 | } 480 | 481 | // Add wait option 482 | if (boot == 0 || strcmp(p->vm.vncwait, "on") == 0) { 483 | strcat(vnc_cmd, ",wait"); 484 | } 485 | 486 | strcat(vnc_cmd, " "); 487 | strcat(cmd, vnc_cmd); 488 | } 489 | // Audio support 490 | if (strcmp(p->vm.audiostatus, "on") == 0) 491 | strcat(cmd, "-s 6,hda,play=/dev/dsp0,rec=/dev/dsp0 "); 492 | 493 | // VirtIO-9P file sharing 494 | if (strcmp(p->vm.share_status, "on") == 0 && strlen(p->vm.share_path) > 0) { 495 | char share_cmd[512]; 496 | if (strcmp(p->vm.share_ro, "on") == 0) 497 | sprintf(share_cmd, "-s 20,virtio-9p,%s=%s,ro ", 498 | p->vm.share_name, p->vm.share_path); 499 | else 500 | sprintf(share_cmd, "-s 20,virtio-9p,%s=%s ", 501 | p->vm.share_name, p->vm.share_path); 502 | strcat(cmd, share_cmd); 503 | } 504 | 505 | strcat(cmd, "-s 30,xhci,tablet "); 506 | strcat(cmd, "${vm_tpm_param}"); 507 | strcat(cmd, "-s 31,lpc -l com1,stdio "); 508 | 509 | // 智能选择 UEFI 固件模式 510 | if (strlen(p->vm.uefi_vars) > 0 && access(p->vm.uefi_vars, R_OK) == 0) { 511 | // 新方式:使用 CODE + VARS(优先) 512 | if (access("/usr/local/share/uefi-firmware/BHYVE_UEFI_CODE.fd", R_OK) == 0) { 513 | strcat(cmd, "-l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI_CODE.fd,${vm_uefi_vars} "); 514 | } else { 515 | // CODE 文件不存在,回退到旧方式 516 | strcat(cmd, "-l bootrom,/usr/local/share/uefi-firmware/${vm_bhyve_uefi_fd} "); 517 | } 518 | } else { 519 | // 兼容旧方式(没有 vars 文件或 vars 文件不可读) 520 | strcat(cmd, "-l bootrom,/usr/local/share/uefi-firmware/${vm_bhyve_uefi_fd} "); 521 | } 522 | strcat(cmd, "${vm_name}"); 523 | 524 | ret = run(cmd, p); 525 | 526 | /*********** bhyve EXIT STATUS *********** 527 | 0 rebooted 528 | 1 powered off 529 | 2 halted 530 | 3 triple fault 531 | 4 exited due to an error 532 | *******************************************/ 533 | write_log("bhyve exited with status: %d", ret); 534 | if (ret > 0 && ret <= 4) break; 535 | strcpy(cmd, "/usr/sbin/bhyvectl --destroy --vm=${vm_name}"); 536 | run(cmd, p); 537 | boot = 1; 538 | vm_boot_from_hd(p->vm.name); 539 | } 540 | strcpy(cmd, "/usr/sbin/bhyvectl --destroy --vm=${vm_name}"); 541 | run(cmd, p); 542 | for (int n=0; nvm.nics); n++) { 543 | sprintf(cmd, "/sbin/ifconfig ${vm_tap%d} destroy", n); 544 | run(cmd, p); 545 | } 546 | 547 | // 清理 .orig 备份文件 548 | // 如果从 HD 启动后正常结束,删除 .orig 备份 549 | // 这样下次从 CD 启动时,会正确备份当前的 efivars(包含 HD 启动项) 550 | if (boot == 1 && strcmp(p->vm.boot_type, "grub") != 0 && strlen(p->vm.uefi_vars) > 0) { 551 | char backup_vars[512]; 552 | sprintf(backup_vars, "%s.orig", p->vm.uefi_vars); 553 | if (access(backup_vars, F_OK) == 0) { 554 | unlink(backup_vars); 555 | write_log("Cleaned up UEFI vars backup for VM %s after HD boot", p->vm.name); 556 | } 557 | } 558 | 559 | // TPM 停止逻辑 560 | 561 | if (strcmp(p->vm.tpmstatus, "on") == 0) { 562 | char stop_swtpm[512]; 563 | sprintf(stop_swtpm, 564 | "if [ -f /var/run/swtpm-%s.pid ]; then " 565 | "kill -9 $(cat /var/run/swtpm-%s.pid) 2>/dev/null; " 566 | "rm -f /var/run/swtpm-%s.pid; " 567 | "fi", 568 | p->vm.name, p->vm.name, p->vm.name); 569 | 570 | write_log("Stopping swtpm: %s", stop_swtpm); 571 | system(stop_swtpm); 572 | } 573 | 574 | } 575 | 576 | // 代码转换 577 | void convert(char *code, vm_node *p) 578 | { 579 | char *str = code; 580 | char vm_disk[256]; 581 | 582 | if (strlen(p->vm.grubcmd) > 0) 583 | str_replace(str, "${vm_grubcmd}", p->vm.grubcmd); 584 | str_replace(str, "${vm_grubcd}", p->vm.grubcd); 585 | str_replace(str, "${vm_grubhd}", p->vm.grubhd); 586 | str_replace(str, "${vm_name}", p->vm.name); 587 | str_replace(str, "${vm_version}", p->vm.version); 588 | str_replace(str, "${vm_bootfrom}", p->vm.bootfrom); 589 | str_replace(str, "${vm_boot_type}", p->vm.boot_type); 590 | str_replace(str, "${vm_devicemap}", p->vm.devicemap); 591 | str_replace(str, "${vm_ram}", p->vm.ram); 592 | str_replace(str, "${vm_cpus}", p->vm.cpus); 593 | str_replace(str, "${vm_sockets}", p->vm.sockets); 594 | str_replace(str, "${vm_cores}", p->vm.cores); 595 | str_replace(str, "${vm_threads}", p->vm.threads); 596 | str_replace(str, "${vm_hostbridge}", p->vm.hostbridge); 597 | str_replace(str, "${vm_disk}", p->vm.disk); 598 | str_replace(str, "${vm_disk0}", p->vm.disk); 599 | str_replace(str, "${vm_iso}", p->vm.iso); 600 | str_replace(str, "${vm_vncport}", p->vm.vncport); 601 | str_replace(str, "${vm_vncwidth}", p->vm.vncwidth); 602 | str_replace(str, "${vm_vncheight}", p->vm.vncheight); 603 | str_replace(str, "${vm_vncpassword}", p->vm.vncpassword); 604 | if (strlen(p->vm.vncbind) > 0) 605 | str_replace(str, "${vm_vncbind}", p->vm.vncbind); 606 | else 607 | str_replace(str, "${vm_vncbind}", "0.0.0.0"); 608 | str_replace(str, "${vm_network_interface}", p->vm.network_interface); 609 | if (strlen(p->vm.storage_interface) > 0) 610 | str_replace(str, "${vm_storage_interface}", p->vm.storage_interface); 611 | else 612 | str_replace(str, "${vm_storage_interface}", "ahci-hd"); 613 | str_replace(str, "${vm_uefi_vars}", p->vm.uefi_vars); 614 | if (strcmp(p->vm.boot_type, "uefi") == 0) 615 | str_replace(str, "${vm_bhyve_uefi_fd}", "BHYVE_UEFI.fd"); 616 | if (strcmp(p->vm.boot_type, "uefi_csm")== 0) 617 | str_replace(str, "${vm_bhyve_uefi_fd}", "BHYVE_UEFI_CSM.fd"); 618 | 619 | // TPM 620 | char tpm_param[512] = ""; 621 | if (strcmp(p->vm.tpmstatus, "on") == 0) { 622 | char tpm_sock[256]; 623 | if (strlen(p->vm.tpmpath) > 0) 624 | strcpy(tpm_sock, p->vm.tpmpath); 625 | else 626 | sprintf(tpm_sock, "/tmp/%s/swtpm.sock", p->vm.name); 627 | 628 | sprintf(tpm_param, "-l tpm,swtpm,%s ", tpm_sock); 629 | } 630 | str_replace(str, "${vm_tpm_param}", tpm_param); 631 | 632 | for (int n=0; nvm.nics); n++) { 633 | char buf[32]; 634 | sprintf(buf, "${vm_tap%d}", n); 635 | str_replace(str, buf, p->vm.nic[n].tap); 636 | 637 | sprintf(buf, "${vm_mac%d}", n); 638 | str_replace(str, buf, p->vm.nic[n].mac); 639 | } 640 | 641 | for (int n=0; nvm.disks); n++) { 642 | char buf[32]; 643 | sprintf(buf, "${vm_disk%d}", n); 644 | str_replace(str, buf, p->vm.vdisk[n].path); 645 | } 646 | 647 | // 多 CD ISO 路径替换 648 | for (int n=0; nvm.cds); n++) { 649 | char buf[32]; 650 | sprintf(buf, "${vm_cd_iso_%d}", n); 651 | str_replace(str, buf, p->vm.cd_iso[n]); 652 | } 653 | 654 | } 655 | --------------------------------------------------------------------------------