 aa66aa0c9a
			
		
	
	aa66aa0c9a
	
	
	
		
			
			The following commands output 1,2,1,1
    cmd0='a=("${a[@]}" 'a'); echo "${#a}"'
    cmd1='a+=('a'); echo "${#a}"'
    bash -c "$cmd0"; zsh -c "$cmd0"
    bash -c "$cmd1"; zsh -c "$cmd1"
The following outputs 0,1,0,0
    cmd2='f() { echo "$#"; }; f "${a[@]}"'
    cmd3="a=(); $cmd2"
    bash -c "$cmd2"; zsh -c "$cmd2"
    bash -c "$cmd3"; zsh -c "$cmd3"
Signed-off-by: Yousong Zhou <yszhou4tech@gmail.com>
		
	
		
			
				
	
	
		
			282 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			282 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env bash
 | |
| 
 | |
| SELF="$0"
 | |
| 
 | |
| # Linux bridge for connecting lan and wan network of guest machines
 | |
| BR_LAN="${BR_LAN:-br-lan}"
 | |
| BR_WAN="${BR_WAN:-br-wan}"
 | |
| 
 | |
| # Host network interface providing internet access for guest machines
 | |
| IF_INET="${IF_INET:-eth0}"
 | |
| 
 | |
| # qemu-bridge-helper does two things here
 | |
| #
 | |
| # - create tap interface
 | |
| # - add the tap interface to bridge
 | |
| #
 | |
| # as such it requires CAP_NET_ADMIN to do its job.  It will be convenient to
 | |
| # have it as a root setuid program.  Be aware of the security risks implied
 | |
| #
 | |
| # the helper has an acl list which defaults to deny all bridge.  we need to add
 | |
| # $BR_LAN and $BR_WAN to its allow list
 | |
| #
 | |
| #	# sudo vim /etc/qemu/bridge.conf
 | |
| #	allow br-lan
 | |
| #	allow br-wan
 | |
| #
 | |
| # Other allowed directives can be 'allow all', 'deny all', 'include xxx',  See
 | |
| # qemu-bridge-helper.c of qemu source code for details.
 | |
| #
 | |
| # The helper can be provided by package qemu-system-common on debian, or
 | |
| # qemu-kvm-common on rhel
 | |
| #
 | |
| HELPER="${HELPER:-/usr/libexec/qemu-bridge-helper}"
 | |
| 
 | |
| ### end of global settings
 | |
| 
 | |
| __errmsg() {
 | |
| 	echo "$*" >&2
 | |
| }
 | |
| 
 | |
| do_setup() {
 | |
| 	# setup bridge for LAN network
 | |
| 	sudo ip link add dev "$BR_LAN" type bridge
 | |
| 	sudo ip link set dev "$BR_LAN" up
 | |
| 	sudo ip addr add 192.168.1.3/24 dev "$BR_LAN"
 | |
| 
 | |
| 	# setup bridge for WAN network
 | |
| 	#
 | |
| 	# minimal dnsmasq config for configuring guest wan network with dhcp
 | |
| 	#
 | |
| 	#	# sudo apt-get install dnsmasq
 | |
| 	#	# sudo vi /etc/dnsmasq.conf
 | |
| 	#	interface=br-wan
 | |
| 	#	dhcp-range=192.168.7.50,192.168.7.150,255.255.255.0,30m
 | |
| 	#
 | |
| 	sudo ip link add dev "$BR_WAN" type bridge
 | |
| 	sudo ip link set dev "$BR_WAN" up
 | |
| 	sudo ip addr add 192.168.7.1/24 dev "$BR_WAN"
 | |
| 
 | |
| 	# guest internet access
 | |
| 	sudo sysctl -w "net.ipv4.ip_forward=1"
 | |
| 	sudo sysctl -w "net.ipv4.conf.$BR_WAN.proxy_arp=1"
 | |
| 	while sudo iptables -t nat -D POSTROUTING -o "$IF_INET" -j MASQUERADE 2>/dev/null; do true; done
 | |
| 	      sudo iptables -t nat -A POSTROUTING -o "$IF_INET" -j MASQUERADE
 | |
| }
 | |
| 
 | |
| check_setup_() {
 | |
| 	ip link show "$BR_LAN" >/dev/null || return 1
 | |
| 	ip link show "$BR_WAN" >/dev/null || return 1
 | |
| 	[ -x "$HELPER" ] || {
 | |
| 		__errmsg "helper $HELPER is not an executable"
 | |
| 		return 1
 | |
| 	}
 | |
| }
 | |
| 
 | |
| check_setup() {
 | |
| 	check_setup_ || {
 | |
| 		__errmsg "please check the script content to see the environment requirement"
 | |
| 		return 1
 | |
| 	}
 | |
| }
 | |
| #do_setup; check_setup; exit $?
 | |
| 
 | |
| usage() {
 | |
| 	cat >&2 <<EOF
 | |
| Usage: $SELF [-h|--help]
 | |
|        $SELF <target>
 | |
|          [<subtarget> [<extra-qemu-options>]]
 | |
|          [--kernel <kernel>]
 | |
|          [--rootfs <rootfs>]
 | |
| 
 | |
| <subtarget> will default to "generic" and must be specified if
 | |
| <extra-qemu-options> are present
 | |
| 
 | |
| e.g. <subtarget> for malta can be le, be, le64, be64, le-glibc, le64-glibc, etc
 | |
| 
 | |
| <kernel>, <rootfs> can be required or optional arguments to qemu depending on
 | |
| the actual <target> in use.  They will default to files under bin/targets/
 | |
| 
 | |
| Examples
 | |
| 
 | |
|   $SELF x86 64
 | |
|   $SELF x86 64 -enable-kvm -device virtio-balloon-pci
 | |
|   $SELF x86 64 -incoming tcp:0:4444
 | |
|   $SELF x86 64-glibc
 | |
|   $SELF malta be -m 64
 | |
|   $SELF malta le64
 | |
|   $SELF malta be-glibc
 | |
|   $SELF armvirt 32 \\
 | |
|                 --kernel bin/targets/armvirt/32/openwrt-armvirt-32-zImage \\
 | |
|                 --rootfs bin/targets/armvirt/32/openwrt-armvirt-32-root.ext4
 | |
| EOF
 | |
| }
 | |
| 
 | |
| rand_mac() {
 | |
| 	hexdump -n 3 -e '"52:54:00:" 2/1 "%02x:" 1/1 "%02x"' /dev/urandom
 | |
| }
 | |
| 
 | |
| parse_args() {
 | |
| 	o_qemu_extra=()
 | |
| 	while [ "$#" -gt 0 ]; do
 | |
| 		case "$1" in
 | |
| 			--kernel) o_kernel="$2"; shift 2 ;;
 | |
| 			--rootfs) o_rootfs="$2"; shift 2 ;;
 | |
| 			--help|-h)
 | |
| 				usage
 | |
| 				exit 0
 | |
| 				;;
 | |
| 			*)
 | |
| 				if [ -z "$o_target" ]; then
 | |
| 					o_target="$1"
 | |
| 				elif [ -z "$o_subtarget" ]; then
 | |
| 					o_subtarget="$1"
 | |
| 				else
 | |
| 					o_qemu_extra+=("$1")
 | |
| 				fi
 | |
| 				shift
 | |
| 				;;
 | |
| 		esac
 | |
| 	done
 | |
| 
 | |
| 	MAC_LAN="$(rand_mac)"
 | |
| 	MAC_WAN="$(rand_mac)"
 | |
| 	[ -n "$o_target" ] || {
 | |
| 		usage
 | |
| 		return 1
 | |
| 	}
 | |
| 	[ -n "$o_subtarget" ] || o_subtarget="generic"
 | |
| 	o_bindir="bin/targets/$o_target/$o_subtarget"
 | |
| }
 | |
| 
 | |
| start_qemu_armvirt() {
 | |
| 	local kernel="$o_kernel"
 | |
| 	local rootfs="$o_rootfs"
 | |
| 	local cpu
 | |
| 	local qemu_exe
 | |
| 
 | |
| 	case "${o_subtarget%-*}" in
 | |
| 		32)
 | |
| 			qemu_exe="qemu-system-arm"
 | |
| 			cpu="cortex-a15"
 | |
| 			[ -n "$kernel" ] || kernel="$o_bindir/openwrt-$o_target-${o_subtarget%-*}-zImage-initramfs"
 | |
| 			;;
 | |
| 		64)
 | |
| 			qemu_exe="qemu-system-aarch64"
 | |
| 			cpu="cortex-a57"
 | |
| 			[ -n "$kernel" ] || kernel="$o_bindir/openwrt-$o_target-${o_subtarget%-*}-Image-initramfs"
 | |
| 			;;
 | |
| 		*)
 | |
| 			__errmsg "target $o_target: unknown subtarget $o_subtarget"
 | |
| 			return 1
 | |
| 			;;
 | |
| 	esac
 | |
| 	[ -z "$rootfs" ] || {
 | |
| 		if [ ! -f "$rootfs" -a -s "$rootfs.gz" ]; then
 | |
| 			gunzip "$rootfs.gz"
 | |
| 		fi
 | |
| 		o_qemu_extra+=( \
 | |
| 			"-drive" "file=$rootfs,format=raw,if=virtio" \
 | |
| 			"-append" "root=/dev/vda rootwait" \
 | |
| 		)
 | |
| 	}
 | |
| 
 | |
| 	"$qemu_exe" -machine virt -cpu "$cpu" -nographic \
 | |
| 		-netdev bridge,id=lan,br="$BR_LAN,helper=$HELPER" -device virtio-net-pci,id=devlan,netdev=lan,mac="$MAC_LAN" \
 | |
| 		-netdev bridge,id=wan,br="$BR_WAN,helper=$HELPER" -device virtio-net-pci,id=devwan,netdev=wan,mac="$MAC_WAN" \
 | |
| 		-kernel "$kernel" \
 | |
| 		"${o_qemu_extra[@]}"
 | |
| }
 | |
| 
 | |
| start_qemu_malta() {
 | |
| 	local is64
 | |
| 	local isel
 | |
| 	local qemu_exe
 | |
| 	local kernel="$o_kernel"
 | |
| 
 | |
| 	# o_subtarget can be le, be, le64, be64, le-glibc, le64-glibc, etc..
 | |
| 	is64="$(echo $o_subtarget | grep -o 64)"
 | |
| 	[ "$(echo "$o_subtarget" | grep -o '^..')" = "le" ] && isel="el"
 | |
| 	qemu_exe="qemu-system-mips$is64$isel"
 | |
| 
 | |
| 	[ -n "$kernel" ] || kernel="$o_bindir/openwrt-malta-${o_subtarget%-*}-vmlinux-initramfs.elf"
 | |
| 
 | |
| 	# NOTE: order of wan, lan -device arguments matters as it will affect which
 | |
| 	# one will be actually used as the wan, lan network interface inside the
 | |
| 	# guest machine
 | |
| 	"$qemu_exe" -machine malta -nographic \
 | |
| 		-netdev bridge,id=wan,br="$BR_WAN,helper=$HELPER" -device pcnet,netdev=wan,mac="$MAC_WAN" \
 | |
| 		-netdev bridge,id=lan,br="$BR_LAN,helper=$HELPER" -device pcnet,netdev=lan,mac="$MAC_LAN" \
 | |
| 		-kernel "$kernel" \
 | |
| 		"${o_qemu_extra[@]}"
 | |
| }
 | |
| 
 | |
| start_qemu_x86() {
 | |
| 	local rootfs="$o_rootfs"
 | |
| 	local qemu_exe
 | |
| 
 | |
| 	[ -n "$rootfs" ] || {
 | |
| 		rootfs="$o_bindir/openwrt-$o_target-${o_subtarget%-*}-combined-ext4.img"
 | |
| 		if [ ! -f "$rootfs" -a -s "$rootfs.gz" ]; then
 | |
| 			gunzip "$rootfs.gz"
 | |
| 		fi
 | |
| 	}
 | |
| 	#
 | |
| 	# generic: 32-bit, pentium4 (CONFIG_MPENTIUM4), kvm guest, virtio
 | |
| 	# legacy: 32-bit, i486 (CONFIG_M486)
 | |
| 	# 64: 64-bit, kvm guest, virtio
 | |
| 	#
 | |
| 	case "${o_subtarget%-*}" in
 | |
| 		legacy)			qemu_exe="qemu-system-i386"		;;
 | |
| 		generic|64)		qemu_exe="qemu-system-x86_64"	;;
 | |
| 		*)
 | |
| 			__errmsg "target $o_target: unknown subtarget $o_subtarget"
 | |
| 			return 1
 | |
| 			;;
 | |
| 	esac
 | |
| 
 | |
| 	case "${o_subtarget%-*}" in
 | |
| 		legacy)
 | |
| 			# use IDE (PATA) disk instead of AHCI (SATA).  Refer to link
 | |
| 			# [1] for related discussions
 | |
| 			#
 | |
| 			# To use AHCI interface
 | |
| 			#
 | |
| 			#	-device ich9-ahci,id=ahci \
 | |
| 			#	-device ide-drive,drive=drv0,bus=ahci.0 \
 | |
| 			#	-drive "file=$rootfs,format=raw,id=drv0,if=none" \
 | |
| 			#
 | |
| 			# [1] https://dev.openwrt.org/ticket/17947
 | |
| 			"$qemu_exe" -nographic \
 | |
| 				-netdev bridge,id=lan,br="$BR_LAN,helper=$HELPER" -device e1000,id=devlan,netdev=lan,mac="$MAC_LAN" \
 | |
| 				-netdev bridge,id=wan,br="$BR_WAN,helper=$HELPER" -device e1000,id=devwan,netdev=wan,mac="$MAC_WAN" \
 | |
| 				-device ide-drive,drive=drv0 \
 | |
| 				-drive "file=$rootfs,format=raw,id=drv0,if=none" \
 | |
| 				"${o_qemu_extra[@]}"
 | |
| 			;;
 | |
| 		generic|64)
 | |
| 			"$qemu_exe" -nographic \
 | |
| 				-netdev bridge,id=lan,br="$BR_LAN,helper=$HELPER" -device virtio-net-pci,id=devlan,netdev=lan,mac="$MAC_LAN" \
 | |
| 				-netdev bridge,id=wan,br="$BR_WAN,helper=$HELPER" -device virtio-net-pci,id=devwan,netdev=wan,mac="$MAC_WAN" \
 | |
| 				-drive "file=$rootfs,format=raw,if=virtio" \
 | |
| 				"${o_qemu_extra[@]}"
 | |
| 			;;
 | |
| 	esac
 | |
| }
 | |
| 
 | |
| start_qemu() {
 | |
| 	case "$o_target" in
 | |
| 		armvirt)	start_qemu_armvirt	;;
 | |
| 		malta)		start_qemu_malta	;;
 | |
| 		x86)		start_qemu_x86		;;
 | |
| 		*)
 | |
| 			__errmsg "target $o_target is not supported yet"
 | |
| 			return 1
 | |
| 			;;
 | |
| 	esac
 | |
| }
 | |
| 
 | |
| check_setup \
 | |
| 	&& parse_args "$@" \
 | |
| 	&& start_qemu
 |