mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-09-02 23:47:11 +00:00
Out with the old, in with the new Moby
- remove remainder of editions code - add a new check container to run tests without Docker - switch over `make test` to use new command to build tests Signed-off-by: Justin Cormack <justin.cormack@docker.com>
This commit is contained in:
@@ -1 +1 @@
|
||||
{"variants":["aufs", "lts4.4"]}
|
||||
{"variants":[]}
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,3 +11,4 @@ disk.img.*
|
||||
*.tar.gz
|
||||
*.vhdx
|
||||
*.efi
|
||||
*-bzImage
|
||||
|
137
Makefile
137
Makefile
@@ -1,39 +1,39 @@
|
||||
.PHONY: test hyperkit-test qemu qemu-iso media ebpf ci ci-pr get get-regextract
|
||||
.PHONY: default test hyperkit-test qemu qemu-iso media ebpf ci ci-pr
|
||||
|
||||
all:
|
||||
$(MAKE) -C alpine
|
||||
default: bin/moby
|
||||
|
||||
aufs:
|
||||
$(MAKE) AUFS=true all
|
||||
all: default
|
||||
|
||||
.PHONY: alpine/initrd.img
|
||||
alpine/initrd.img:
|
||||
$(MAKE) -C alpine initrd.img
|
||||
GO_COMPILE=mobylinux/go-compile:236629d9fc0779db9e7573ceb8b0e92f08f553be@sha256:16020c2d90cecb1f1d2d731187e947535c23f38b62319dd386ae642b4b32e1fb
|
||||
|
||||
.PHONY: alpine/initrd-test.img
|
||||
alpine/initrd-test.img:
|
||||
$(MAKE) -C alpine initrd-test.img
|
||||
MOBY_DEPS=$(wildcard *.go) pkg vendor
|
||||
GOOS=$(shell uname -s | tr '[:upper:]' '[:lower:]')
|
||||
GOARCH=amd64
|
||||
ifneq ($(GOOS),linux)
|
||||
CROSS=-e GOOS=$(GOOS) -e GOARCH=$(GOARCH)
|
||||
endif
|
||||
|
||||
.PHONY: kernel/x86_64/vmlinuz64
|
||||
kernel/x86_64/vmlinuz64:
|
||||
$(MAKE) -C kernel
|
||||
bin/moby: $(MOBY_DEPS) | bin
|
||||
tar cf - $(MOBY_DEPS) | docker run --rm --net=none --log-driver=none -i $(CROSS) $(GO_COMPILE) --package github.com/docker/moby -o $@ | tar xf -
|
||||
|
||||
.PHONY: alpine/mobylinux-bios.iso
|
||||
alpine/mobylinux-bios.iso:
|
||||
$(MAKE) -C alpine mobylinux-bios.iso
|
||||
QEMU_IMAGE=mobylinux/qemu:156d2160c2ccf4d5118221bc2708f6c0981d54cc@sha256:e1345ba0400d6c45bf3bdf4f4ed425c3d7596d11e6553b83f17f5893dfc49f7b
|
||||
|
||||
.PHONY: alpine/mobylinux-efi.iso
|
||||
alpine/mobylinux-efi.iso:
|
||||
$(MAKE) -C alpine mobylinux-efi.iso
|
||||
moby-initrd.img: bin/moby moby.yaml
|
||||
$^
|
||||
|
||||
QEMU_IMAGE=mobylinux/qemu:0fb8c648e8ed9ef6b1ec449587aeab6c53872744@sha256:606f30d815102e73bc01c07915dc0d5f153b0252c63f5f0ed1e39621ec656eb5
|
||||
moby-bzImage: moby-initrd.img
|
||||
|
||||
test-initrd.img: bin/moby test.yaml
|
||||
$^
|
||||
|
||||
test-bzImage: test-initrd.img
|
||||
|
||||
# interactive versions need to use volume mounts
|
||||
qemu: alpine/initrd.img kernel/x86_64/vmlinuz64
|
||||
docker run -it --rm -v $(CURDIR)/alpine/initrd.img:/tmp/initrd.img -v $(CURDIR)/kernel/x86_64/vmlinuz64:/tmp/vmlinuz64 $(QEMU_IMAGE)
|
||||
qemu: moby-initrd.img
|
||||
docker run -it --rm -v $(CURDIR)/moby-initrd.img:/tmp/initrd.img -v $(CURDIR)/moby-bzImage:/tmp/vmlinuz64 $(QEMU_IMAGE)
|
||||
|
||||
qemu-iso: alpine/mobylinux-bios.iso
|
||||
docker run -it --rm -v $(CURDIR)/alpine/mobylinux-bios.iso:/tmp/mobylinux-bios.iso $(QEMU_IMAGE)
|
||||
docker run -it --rm -v $(CURDIR)/mobylinux-bios.iso:/tmp/mobylinux-bios.iso $(QEMU_IMAGE)
|
||||
|
||||
bin:
|
||||
mkdir -p $@
|
||||
@@ -58,10 +58,6 @@ else
|
||||
touch $@
|
||||
endif
|
||||
|
||||
bin/regextract: | bin
|
||||
curl -fsSL https://circleci.com/api/v1/project/justincormack/regextract/latest/artifacts/0/\$$CIRCLE_ARTIFACTS/darwin/amd64/regextract > $@
|
||||
chmod a+x $@
|
||||
|
||||
hyperkit: scripts/hyperkit.sh bin/com.docker.hyperkit bin/vpnkit alpine/initrd.img kernel/x86_64/vmlinuz64
|
||||
./scripts/hyperkit.sh
|
||||
|
||||
@@ -69,54 +65,15 @@ define check_test_log
|
||||
@cat $1 |grep -q 'Moby test suite PASSED'
|
||||
endef
|
||||
|
||||
hyperkit-test: scripts/hyperkit.sh bin/com.docker.hyperkit bin/vpnkit alpine/initrd-test.img kernel/x86_64/vmlinuz64
|
||||
hyperkit-test: scripts/hyperkit.sh bin/com.docker.hyperkit bin/vpnkit test-initrd.img test-bzImage
|
||||
rm -f disk.img
|
||||
INITRD=alpine/initrd-test.img script -q /dev/null ./scripts/hyperkit.sh | tee test.log
|
||||
script -q /dev/null ./scripts/hyperkit.sh test | tee test.log
|
||||
$(call check_test_log, test.log)
|
||||
|
||||
test: alpine/initrd-test.img kernel/x86_64/vmlinuz64
|
||||
test: test-initrd.img test-bzImage
|
||||
tar cf - $^ | docker run --rm -i $(QEMU_IMAGE) 2>&1 | tee test.log
|
||||
$(call check_test_log, test.log)
|
||||
|
||||
ifeq ($(CI_TAG),)
|
||||
TAG=$(shell git rev-parse HEAD)
|
||||
else
|
||||
TAG=$(CI_TAG)
|
||||
endif
|
||||
|
||||
STATUS=$(shell git status -s)
|
||||
MOBYLINUX_TAG=alpine/mobylinux.tag
|
||||
ifdef AUFS
|
||||
AUFS_PREFIX=aufs-
|
||||
endif
|
||||
ifdef LTS4.4
|
||||
AUFS_PREFIX=lts4.4-
|
||||
endif
|
||||
|
||||
MEDIA_IMAGE=mobylinux/media:$(MEDIA_PREFIX)$(AUFS_PREFIX)$(TAG)
|
||||
INITRD_IMAGE=mobylinux/mobylinux:$(MEDIA_PREFIX)$(AUFS_PREFIX)$(TAG)
|
||||
KERNEL_IMAGE=mobylinux/kernel:$(MEDIA_PREFIX)$(AUFS_PREFIX)$(TAG)
|
||||
|
||||
MEDIA_TOYBOX=mobylinux/toybox-media:0a26fe5f574e444849983f9c4148ef74b3804d55@sha256:5ac38f77b66deb194c9016591b9b096e81fcdc9f7c3e6d01566294a6b4b4ebd2
|
||||
|
||||
Dockerfile.media:
|
||||
printf "FROM $(MEDIA_TOYBOX)\nADD . /\n" > $@
|
||||
|
||||
MEDIA_TARBALL=Dockerfile.media -C alpine initrd.img initrd-test.img mobylinux-efi.iso mobylinux.efi -C ../kernel/x86_64 vmlinuz64 vmlinux kernel-headers.tar kernel-dev.tar
|
||||
|
||||
media: Dockerfile.media alpine/initrd.img alpine/initrd-test.img kernel/x86_64/vmlinuz64 alpine/mobylinux-efi.iso
|
||||
ifeq ($(STATUS),)
|
||||
tar cf - $(MEDIA_TARBALL) | docker build -f Dockerfile.media -t $(MEDIA_IMAGE) -
|
||||
docker push $(MEDIA_IMAGE)
|
||||
[ -f $(MOBYLINUX_TAG) ]
|
||||
docker tag $(shell cat $(MOBYLINUX_TAG)) $(INITRD_IMAGE)
|
||||
docker push $(INITRD_IMAGE)
|
||||
tar cf - Dockerfile.media -C kernel/x86_64 bzImage kernel.tar | docker build -f Dockerfile.media -t $(KERNEL_IMAGE) -
|
||||
docker push $(KERNEL_IMAGE)
|
||||
else
|
||||
$(error "git not clean")
|
||||
endif
|
||||
|
||||
EBPF_TAG=ebpf/ebpf.tag
|
||||
EBPF_IMAGE=mobylinux/ebpf:$(MEDIA_PREFIX)$(AUFS_PREFIX)$(TAG)
|
||||
ebpf: alpine/initrd.img kernel/x86_64/vmlinuz64
|
||||
@@ -128,54 +85,22 @@ else
|
||||
$(error "git not clean")
|
||||
endif
|
||||
|
||||
MEDIA_FILES=kernel/x86_64/vmlinuz64 kernel/x86_64/vmlinux alpine/initrd.img alpine/mobylinux-efi.iso alpine/mobylinux.efi
|
||||
MEDIA_FILES_OPT=kernel/x86_64/kernel-headers.tar kernel/x86_64/kernel-dev.tar alpine/initrd-test.img
|
||||
|
||||
get:
|
||||
ifeq ($(STATUS),)
|
||||
IMAGE=$$( docker create mobylinux/media:$(MEDIA_PREFIX)$(AUFS_PREFIX)$(TAG) /dev/null ) && \
|
||||
mkdir -p kernel/x86_64 && \
|
||||
for FILE in $(MEDIA_FILES); do docker cp $$IMAGE:$$(basename $$FILE) $$FILE || exit; done; \
|
||||
for FILE in $(MEDIA_FILES_OPT); do docker cp $$IMAGE:$$(basename $$FILE) $$FILE; done; \
|
||||
docker rm $$IMAGE
|
||||
else
|
||||
$(error "git not clean")
|
||||
endif
|
||||
|
||||
# Get artifacts using regextract for cases where docker is not available
|
||||
get-regextract: bin/regextract
|
||||
ifeq ($(STATUS),)
|
||||
TMP_EXTRACT=$$(mktemp -d) && \
|
||||
for FILE in $(MEDIA_FILES) $(MEDIA_FILES_OPT); do mkdir -p $$(dirname $$FILE); done; \
|
||||
bin/regextract mobylinux/media:$(MEDIA_PREFIX)$(AUFS_PREFIX)$(TAG) | tar xf - -C $$TMP_EXTRACT && \
|
||||
mkdir -p kernel/x86_64 && \
|
||||
for FILE in $(MEDIA_FILES); do cp $$TMP_EXTRACT/$$(basename $$FILE) $$FILE || exit; done; \
|
||||
for FILE in $(MEDIA_FILES_OPT); do cp $$TMP_EXTRACT/$$(basename $$FILE) $$FILE; done; \
|
||||
rm -Rf $$TMP_EXTRACT
|
||||
else
|
||||
$(error "git not clean")
|
||||
endif
|
||||
|
||||
ci:
|
||||
$(MAKE) clean
|
||||
$(MAKE) all
|
||||
$(MAKE)
|
||||
$(MAKE) test
|
||||
$(MAKE) media
|
||||
|
||||
ci-tag:
|
||||
$(MAKE) clean
|
||||
$(MAKE) all
|
||||
$(MAKE)
|
||||
$(MAKE) test
|
||||
$(MAKE) media
|
||||
|
||||
ci-pr:
|
||||
$(MAKE) clean
|
||||
$(MAKE) all
|
||||
$(MAKE)
|
||||
$(MAKE) test
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
$(MAKE) -C alpine clean
|
||||
$(MAKE) -C kernel clean
|
||||
rm -rf bin disk.img test.log Dockerfile.media
|
||||
rm -rf bin disk.img test.log *-initrd.img *-bzImage *.iso
|
||||
|
1
alpine/.gitignore
vendored
1
alpine/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
etc/moby-commit
|
@@ -1,59 +0,0 @@
|
||||
FROM mobylinux/alpine-base:5837a236153f00bb215642e3e0639252eb49cdf9@sha256:f6f12aebe2af07c9250014ff283485dbdf082bd9cfbd74aad27a3d2dcf13e0b1
|
||||
|
||||
RUN \
|
||||
addgroup -g 50 docker && \
|
||||
adduser -G docker -u 1001 -s /bin/sh -D -g "Docker" docker && \
|
||||
passwd -d root && \
|
||||
adduser -D -H -s /sbin/nologin dockremap
|
||||
|
||||
COPY . .
|
||||
RUN cd /usr/bin && \
|
||||
ln -s docker-runc runc && \
|
||||
ln -s docker-containerd-shim containerd-shim && \
|
||||
ln -s docker-containerd-ctr containerd-ctr && \
|
||||
ln -s docker-containerd containerd
|
||||
|
||||
RUN \
|
||||
rc-update add sysctl boot && \
|
||||
rc-update add bootmisc boot && \
|
||||
rc-update add urandom boot && \
|
||||
rc-update add hostname boot && \
|
||||
rc-update add vsudd boot && \
|
||||
rc-update add sysklogd boot && \
|
||||
rc-update add hwclock boot && \
|
||||
rc-update add tap-vsockd boot && \
|
||||
rc-update add networking boot && \
|
||||
rc-update add dhcpcd boot && \
|
||||
rc-update add acpid default && \
|
||||
rc-update add chronyd default && \
|
||||
rc-update add savecache shutdown && \
|
||||
rc-update add killprocs shutdown && \
|
||||
rc-update add mount-ro shutdown && \
|
||||
rc-update add dmesg sysinit && \
|
||||
rc-update add devfs sysinit && \
|
||||
rc-update add hwdrivers sysinit && \
|
||||
rc-update add sysfs && \
|
||||
rc-update add procfs && \
|
||||
rc-update add sysfsconf && \
|
||||
rc-update add fsck && \
|
||||
rc-update add crond && \
|
||||
rc-update add local && \
|
||||
rc-update add localmount && \
|
||||
rc-update add docker default && \
|
||||
rc-update add proxy default && \
|
||||
rc-update add transfused default && \
|
||||
rc-update add automount sysinit && \
|
||||
rc-update add diagnostics default && \
|
||||
rc-update add diagnostics-server boot && \
|
||||
rc-update add database-mac boot && \
|
||||
rc-update add database-windows boot && \
|
||||
rc-update add database-aws default && \
|
||||
rc-update add database-gcp default && \
|
||||
rc-update add hostsettings default && \
|
||||
rc-update add windowsnet boot && \
|
||||
rc-update add hv_kvp_daemon default && \
|
||||
rc-update add hv_vss_daemon default && \
|
||||
rc-update add oom default && \
|
||||
rc-update add test default && \
|
||||
rc-update add containerd default && \
|
||||
true
|
@@ -1,85 +0,0 @@
|
||||
all: initrd.img initrd-test.img mobylinux-efi.iso mobylinux-bios.iso
|
||||
|
||||
TAG=$(shell git rev-parse HEAD)
|
||||
STATUS=$(shell git status -s)
|
||||
ifeq ($(STATUS),)
|
||||
DIRTY=
|
||||
else
|
||||
DIRTY=-dirty
|
||||
endif
|
||||
|
||||
# By default we want to always auth to GCE to upload moby images
|
||||
FORCE_GSUTIL_AUTH ?= 1
|
||||
|
||||
BIOS_IMAGE=mobylinux/mkimage-iso-bios:2a860edda12a44c8e141a924f29ea931dbc01110@sha256:974304cfff80524a37bf96d299de9b3163ba07df5feeb620c6bdcd132b46f16d
|
||||
|
||||
EFI_IMAGE=mobylinux/mkimage-iso-efi:f81034d118744a42e8e93cfe0777dc3490c1f6a0@sha256:4bb10d794fd0fe58366c36ab0fabf7b6d22e5bd760a994900da3b910230519cc
|
||||
|
||||
PAD4_IMAGE=mobylinux/pad4:1edffcbfa13d4795f006d38e871a778ffba03d8a@sha256:1ad26970698670373ee0bf374a06900f712a61b8038255e78271b840a1267b25
|
||||
|
||||
TAR2INITRD_IMAGE=mobylinux/tar2initrd:d5711601eb5b89de0f052d87365e18388ff3f1b5@sha256:58d377e65845f91400e173ce9fca93462f2f237947eef2b0d2c17bb4f2da5ee8
|
||||
|
||||
TARTAR2INITRD_IMAGE=mobylinux/tartar2initrd:d56cde1558e3080e59a32e3cd7c7141baa601811@sha256:e1ad4522ff906d339da5f250b9ef6bffa5a70b4dec7d2cf7f7dbd0447b79352f
|
||||
|
||||
MKIMAGE_BASE=mobylinux/mkimage-base:870f7512498f2ce5feccebe15fb0d03c5c3ebac2@sha256:47d1ed872b6a44f13b61ea80b3eeab4519dc151c7d684a89a53aa26233b4e087
|
||||
|
||||
moby.img: Dockerfile etc usr init
|
||||
$(MAKE) -j -C packages
|
||||
printf $(TAG)$(DIRTY) > etc/moby-commit
|
||||
BUILD=$$( tar cf - $^ \
|
||||
-C packages/proxy usr sbin etc -C ../.. \
|
||||
-C packages/transfused sbin etc -C ../.. \
|
||||
-C packages/tap-vsockd sbin etc -C ../.. \
|
||||
-C packages/docker usr etc -C ../.. \
|
||||
-C packages/diagnostics usr etc -C ../.. \
|
||||
-C packages/automount etc -C ../.. \
|
||||
-C packages/windowsnet etc -C ../.. \
|
||||
-C packages/hostsettings etc -C ../.. \
|
||||
-C packages/chronyd etc -C ../.. \
|
||||
-C packages/userns etc -C ../.. \
|
||||
-C packages/nc-vsock usr -C ../.. \
|
||||
-C packages/vsudd sbin etc -C ../.. \
|
||||
-C packages/mobyconfig etc usr -C ../.. \
|
||||
-C packages/mobyplatform usr -C ../.. \
|
||||
-C packages/oom etc -C ../.. \
|
||||
-C packages/9pmount-vsock sbin -C ../.. \
|
||||
-C packages/test etc -C ../.. \
|
||||
-C packages/iptables usr -C ../.. \
|
||||
-C packages/containerd etc -C ../.. \
|
||||
| \
|
||||
docker build -q - ) && [ -n "$$BUILD" ] && echo "Built $$BUILD" && \
|
||||
echo $$BUILD > mobylinux.tag && \
|
||||
docker run --rm --read-only --net=none --log-driver=none --tmpfs /tmp -v /var/run/docker.sock:/var/run/docker.sock $(MKIMAGE_BASE) $$BUILD | \
|
||||
docker run --rm --read-only --net=none --log-driver=none --tmpfs /tmp -i $(TAR2INITRD_IMAGE) > $@
|
||||
|
||||
container.img:
|
||||
$(MAKE) -C containers
|
||||
tar cf - containers/*/container.tar | \
|
||||
docker run --rm --read-only --net=none --log-driver=none --tmpfs /tmp -i $(TARTAR2INITRD_IMAGE) > $@
|
||||
|
||||
test/test.img:
|
||||
$(MAKE) -C test
|
||||
|
||||
../kernel/x86_64/kernel.img:
|
||||
$(MAKE) -C ../kernel
|
||||
|
||||
initrd.img: moby.img ../kernel/x86_64/kernel.img container.img
|
||||
cat $^ > $@
|
||||
|
||||
initrd-test.img: initrd.img test/test.img
|
||||
cat $^ > $@
|
||||
|
||||
# outputs tarball of mobylinux-efi.iso mobylinux.efi
|
||||
mobylinux-efi.iso: initrd.img ../kernel/x86_64/vmlinuz64
|
||||
tar cf - initrd.img -C ../kernel/x86_64 vmlinuz64 | docker run --rm --net=none --log-driver=none -i $(EFI_IMAGE) | tar xf -
|
||||
|
||||
mobylinux-bios.iso: initrd.img ../kernel/x86_64/vmlinuz64
|
||||
tar cf - initrd.img -C ../kernel/x86_64 vmlinuz64 | docker run --rm --net=none --log-driver=none -i $(BIOS_IMAGE) >$@
|
||||
|
||||
clean:
|
||||
rm -f *.img *.vhd *.iso *.tag mobylinux.efi etc/moby-commit
|
||||
$(MAKE) -C packages clean
|
||||
$(MAKE) -C containers clean
|
||||
$(MAKE) -C test clean
|
||||
|
||||
.DELETE_ON_ERROR:
|
@@ -1,10 +0,0 @@
|
||||
DIRS=$(wildcard */)
|
||||
.PHONY: clean $(DIRS)
|
||||
|
||||
default: $(DIRS)
|
||||
|
||||
$(DIRS):
|
||||
$(MAKE) -C $@
|
||||
|
||||
clean:
|
||||
for f in $(DIRS); do $(MAKE) -C $$f clean; done
|
@@ -1,14 +0,0 @@
|
||||
RIDDLER=mobylinux/riddler:7d4545d8b8ac2700971a83f12a3446a76db28c14@sha256:11b7310df6482fc38aa52b419c2ef1065d7b9207c633d47554e13aa99f6c0b72
|
||||
|
||||
BINFMT_IMAGE=mobylinux/binfmt:a94e0587b702edaa95cc6f303464959d0eb2311c@sha256:432732b90cbe0498f5ca148d75b90bb1eabd8fbfe8c872df8b23906c225091b1
|
||||
|
||||
default: container.tar
|
||||
|
||||
container.tar:
|
||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock $(RIDDLER) \
|
||||
$(BINFMT_IMAGE) /containers/binfmt --cap-drop all --read-only -v /proc/sys/fs/binfmt_misc:/binfmt_misc $(BINFMT_IMAGE) /usr/bin/binfmt -dir /etc/binfmt.d/ -mount /binfmt_misc >$@
|
||||
|
||||
clean:
|
||||
rm -f container.tar
|
||||
|
||||
.DELETE_ON_ERROR:
|
@@ -1,14 +0,0 @@
|
||||
RIDDLER=mobylinux/riddler:7d4545d8b8ac2700971a83f12a3446a76db28c14@sha256:11b7310df6482fc38aa52b419c2ef1065d7b9207c633d47554e13aa99f6c0b72
|
||||
|
||||
RNGD_IMAGE=mobylinux/rngd:3dad6dd43270fa632ac031e99d1947f20b22eec9@sha256:1c93c1db7196f6f71f8e300bc1d15f0376dd18e8891c8789d77c8ff19f3a9a92
|
||||
|
||||
default: container.tar
|
||||
|
||||
container.tar:
|
||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock $(RIDDLER) \
|
||||
$(RNGD_IMAGE) /containers/rngd --cap-drop all --cap-add SYS_ADMIN --read-only --oom-score-adj -800 $(RNGD_IMAGE) /bin/tini /usr/sbin/rngd -f >$@
|
||||
|
||||
clean:
|
||||
rm -f container.tar
|
||||
|
||||
.DELETE_ON_ERROR:
|
@@ -1,46 +0,0 @@
|
||||
# Moby dhcpcd config
|
||||
|
||||
# Only configure standard external ethernet
|
||||
allowinterfaces eth*
|
||||
|
||||
# Inform the DHCP server of our hostname for DDNS.
|
||||
hostname
|
||||
|
||||
# Use the hardware address of the interface for the Client ID.
|
||||
clientid
|
||||
# or
|
||||
# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361.
|
||||
# Some non-RFC compliant DHCP servers do not reply with this set.
|
||||
# In this case, comment out duid and enable clientid above.
|
||||
#duid
|
||||
|
||||
# Persist interface configuration when dhcpcd exits.
|
||||
persistent
|
||||
|
||||
# Rapid commit support.
|
||||
# Safe to enable by default because it requires the equivalent option set
|
||||
# on the server to actually work.
|
||||
option rapid_commit
|
||||
|
||||
# A list of options to request from the DHCP server.
|
||||
option domain_name_servers, domain_name, domain_search, host_name
|
||||
option classless_static_routes
|
||||
# Most distributions have NTP support.
|
||||
# option ntp_servers
|
||||
# Respect the network MTU. This is applied to DHCP routes.
|
||||
option interface_mtu
|
||||
|
||||
# A ServerID is required by RFC2131.
|
||||
require dhcp_server_identifier
|
||||
|
||||
# Generate Stable Private IPv6 Addresses instead of hardware based ones
|
||||
slaac private
|
||||
|
||||
# Do not wait
|
||||
nodelay
|
||||
|
||||
# Do not arp to check IP
|
||||
noarp
|
||||
|
||||
# Only fork when we have ipv4
|
||||
waitip 4
|
@@ -1,2 +0,0 @@
|
||||
tmpfs /run tmpfs defaults,nodev,nosuid,noexec,relatime,size=10%,mode=755 0 0
|
||||
tmpfs /tmp tmpfs defaults,nodev,nosuid,noexec,relatime,size=10%,mode=1777 0 0
|
@@ -1,12 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
# do nothing as we do this in automount script
|
||||
|
||||
start()
|
||||
{
|
||||
return 0
|
||||
}
|
||||
|
||||
stop()
|
||||
{
|
||||
return 0
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
# Copyright (c) 2007-2015 The OpenRC Authors.
|
||||
# See the Authors file at the top-level directory of this distribution and
|
||||
# https://github.com/OpenRC/openrc/blob/master/AUTHORS
|
||||
#
|
||||
# This file is part of OpenRC. It is subject to the license terms in
|
||||
# the LICENSE file found in the top-level directory of this
|
||||
# distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
|
||||
# This file may not be copied, modified, propagated, or distributed
|
||||
# except according to the terms contained in the LICENSE file.
|
||||
|
||||
depend()
|
||||
{
|
||||
before bootmisc logger
|
||||
keyword -prefix -systemd-nspawn -vserver
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
local quiet rc=0
|
||||
yesno $rc_verbose || quiet=-q
|
||||
|
||||
ebegin "Configuring kernel parameters"
|
||||
set --
|
||||
# Do additional sysctl configuration for cloud editions
|
||||
[ "$(mobyplatform)" != "mac" ] && [ "$(mobyplatform)" != "windows" ] && CLOUD=/etc/sysctl.d/cloud/*.conf
|
||||
for i in /run/sysctl.d/*.conf \
|
||||
/etc/sysctl.d/*.conf \
|
||||
/usr/local/lib/sysctl.d/*.conf \
|
||||
/usr/lib/sysctl.d/*.conf \
|
||||
/lib/sysctl.d/*.conf \
|
||||
/etc/sysctl.conf \
|
||||
$CLOUD; do
|
||||
if [ -e "$i" ]; then
|
||||
sysctl ${quiet} -p "$i"
|
||||
rc=$(( $rc + $? ))
|
||||
fi
|
||||
done
|
||||
|
||||
eend $rc "Unable to configure some kernel parameters"
|
||||
}
|
@@ -1,76 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
# Copyright 1999-2011 Gentoo Foundation
|
||||
# Distributed under the terms of the GNU General Public License, v2 or later
|
||||
# $Header: /var/cvsroot/gentoo-x86/app-admin/sysklogd/files/sysklogd.rc7,v 1.1 2011/09/14 22:22:57 polynomial-c Exp $
|
||||
|
||||
extra_started_commands="reload"
|
||||
|
||||
depend() {
|
||||
need clock hostname localmount vsudd bootmisc
|
||||
before net
|
||||
provide logger
|
||||
}
|
||||
|
||||
start_daemon() {
|
||||
local retval=0
|
||||
local daemon="$1"
|
||||
local options="$2"
|
||||
|
||||
[ -z "${daemon}" ] && return 1
|
||||
|
||||
ebegin "sysklogd -> start: ${daemon}"
|
||||
start-stop-daemon --start --exec /usr/sbin/"${daemon}" \
|
||||
--pidfile /var/run/"${daemon}".pid -- ${options}
|
||||
retval=$?
|
||||
eend ${retval} "Failed to start ${daemon}"
|
||||
|
||||
return ${retval}
|
||||
}
|
||||
|
||||
stop_daemon() {
|
||||
local retval=0
|
||||
local daemon="$1"
|
||||
|
||||
[ -z "${daemon}" ] && return 1
|
||||
|
||||
ebegin "sysklogd -> stop: ${daemon}"
|
||||
# syslogd can be stubborn some times (--retry 15)...
|
||||
start-stop-daemon --stop --retry 15 --quiet --pidfile /var/run/"${daemon}".pid
|
||||
retval=$?
|
||||
eend ${retval} "Failed to stop ${daemon}"
|
||||
|
||||
return ${retval}
|
||||
}
|
||||
|
||||
start() {
|
||||
start_daemon "syslogd" "${SYSLOGD}" || return 1
|
||||
|
||||
# klogd do not always start proper if started too early
|
||||
sleep 1
|
||||
|
||||
if ! start_daemon "klogd" "${KLOGD}" ; then
|
||||
stop_daemon "syslogd"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
stop() {
|
||||
stop_daemon "klogd" || return 1
|
||||
stop_daemon "syslogd" || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
reload() {
|
||||
local ret=0
|
||||
|
||||
ebegin "Reloading configuration"
|
||||
|
||||
start-stop-daemon --signal HUP --pidfile /var/run/syslogd.pid
|
||||
ret=$((${ret} + $?))
|
||||
start-stop-daemon --signal USR1 --pidfile /var/run/klogd.pid
|
||||
ret=$((${ret} + $?))
|
||||
|
||||
eend ${ret}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
# /etc/inittab
|
||||
|
||||
::sysinit:/sbin/openrc sysinit
|
||||
::sysinit:/sbin/openrc boot
|
||||
::wait:/sbin/openrc default
|
||||
|
||||
# Stuff to do for the 3-finger salute
|
||||
::ctrlaltdel:/sbin/reboot
|
||||
|
||||
# Stuff to do before rebooting
|
||||
::shutdown:/sbin/openrc shutdown
|
@@ -1,12 +0,0 @@
|
||||
|
||||
Welcome to Moby
|
||||
|
||||
## .
|
||||
## ## ## ==
|
||||
## ## ## ## ## ===
|
||||
/"""""""""""""""""\___/ ===
|
||||
~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ / ===- ~~~
|
||||
\______ o __/
|
||||
\ \ __/
|
||||
\____\_______/
|
||||
|
@@ -1 +0,0 @@
|
||||
Welcome to Moby
|
@@ -1,2 +0,0 @@
|
||||
auto lo
|
||||
iface lo inet loopback
|
@@ -1,11 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Windows and Mac have TRIM support, clean out unused frequently
|
||||
case "$(mobyplatform)" in
|
||||
"windows")
|
||||
/sbin/fstrim /var
|
||||
;;
|
||||
"mac")
|
||||
mobyconfig exists disk/trim && [ "$(mobyconfig get disk/trim)" = "true" ] && /sbin/fstrim /var
|
||||
;;
|
||||
esac
|
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# TODO check with hdparm if supports TRIM
|
||||
/sbin/fstrim /var || true
|
@@ -1,22 +0,0 @@
|
||||
# general limits
|
||||
vm.max_map_count = 262144
|
||||
vm.overcommit_memory = 1
|
||||
net.core.somaxconn = 1024
|
||||
net.ipv4.neigh.default.gc_thresh1 = 30000
|
||||
net.ipv4.neigh.default.gc_thresh2 = 32000
|
||||
net.ipv4.neigh.default.gc_thresh3 = 32768
|
||||
fs.aio-max-nr = 1048576
|
||||
fs.inotify.max_user_watches = 524288
|
||||
fs.file-max = 524288
|
||||
# for rngd
|
||||
kernel.random.write_wakeup_threshold = 3072
|
||||
# security restrictions
|
||||
kernel.kptr_restrict = 2
|
||||
net.ipv4.conf.all.send_redirects = 0
|
||||
net.ipv4.conf.default.accept_redirects = 0
|
||||
net.ipv4.conf.default.accept_source_route = 0
|
||||
net.ipv6.conf.all.accept_redirects = 0
|
||||
net.ipv6.conf.default.accept_redirects = 0
|
||||
kernel.perf_event_paranoid = 3
|
||||
fs.protected_hardlinks = 1
|
||||
fs.protected_symlinks = 1
|
@@ -1,2 +0,0 @@
|
||||
kernel.modules_disabled=1
|
||||
kernel.sysrq = 0
|
@@ -1,3 +0,0 @@
|
||||
fs/cgroup/memory/memory.use_hierarchy = 1
|
||||
kernel/mm/transparent_hugepage/enabled = madvise
|
||||
kernel/mm/transparent_hugepage/defrag = madvise
|
@@ -1,29 +0,0 @@
|
||||
# /etc/syslog.conf Configuration file for syslogd.
|
||||
#
|
||||
# For more information see syslog.conf(5)
|
||||
# manpage.
|
||||
|
||||
# First some standard logfiles. Log by facility.
|
||||
#
|
||||
|
||||
auth,authpriv.* /var/log/auth.log
|
||||
*.*;auth,authpriv.none -/var/log/syslog
|
||||
cron.* /var/log/cron.log
|
||||
daemon.* -/var/log/daemon.log
|
||||
kern.* -/var/log/kern.log
|
||||
lpr.* -/var/log/lpr.log
|
||||
mail.* -/var/log/mail.log
|
||||
user.* -/var/log/user.log
|
||||
|
||||
# Local facilities for our programs
|
||||
local0.* -/var/log/docker.log
|
||||
|
||||
# Some `catch-all' logfiles.
|
||||
#
|
||||
*.=debug;\
|
||||
auth,authpriv.none;\
|
||||
news.none;mail.none -/var/log/debug
|
||||
*.=info;*.=notice;*.=warning;\
|
||||
auth,authpriv.none;\
|
||||
cron,daemon.none;\
|
||||
mail,news.none -/var/log/messages
|
44
alpine/init
44
alpine/init
@@ -1,44 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
setup_console() {
|
||||
tty=${1%,*}
|
||||
speed=${1#*,}
|
||||
inittab="$2"
|
||||
securetty="$3"
|
||||
line=
|
||||
term="linux"
|
||||
[ "$speed" = "$1" ] && speed=115200
|
||||
|
||||
case "$tty" in
|
||||
ttyS*|ttyAMA*|ttyUSB*|ttyMFD*)
|
||||
line="-L"
|
||||
term="vt100"
|
||||
;;
|
||||
tty0)
|
||||
# skip current console
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
# skip consoles already in inittab
|
||||
grep -q "^$tty:" "$inittab" && return
|
||||
|
||||
echo "$tty::once:cat /etc/issue" >> "$inittab"
|
||||
echo "$tty::respawn:/sbin/getty -n -l /bin/sh $line $speed $tty $term" >> "$inittab"
|
||||
if ! grep -q -w "$tty" "$securetty"; then
|
||||
echo "$tty" >> "$securetty"
|
||||
fi
|
||||
}
|
||||
|
||||
/bin/mount -t tmpfs tmpfs /mnt
|
||||
|
||||
/bin/cp -a / /mnt 2>/dev/null
|
||||
|
||||
/bin/mount -t proc -o noexec,nosuid,nodev proc /proc
|
||||
for opt in $(cat /proc/cmdline); do
|
||||
case "$opt" in
|
||||
console=*)
|
||||
setup_console ${opt#console=} /mnt/etc/inittab /mnt/etc/securetty;;
|
||||
esac
|
||||
done
|
||||
|
||||
exec /bin/busybox switch_root /mnt /sbin/init
|
1
alpine/packages/9pmount-vsock/.gitignore
vendored
1
alpine/packages/9pmount-vsock/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
sbin/
|
@@ -1,248 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <syslog.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <err.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "hvsock.h"
|
||||
|
||||
#define NONE 0
|
||||
#define LISTEN 1
|
||||
#define CONNECT 2
|
||||
|
||||
int mode = NONE;
|
||||
|
||||
char *default_sid = "C378280D-DA14-42C8-A24E-0DE92A1028E2";
|
||||
char *mount = "/bin/mount";
|
||||
|
||||
void fatal(const char *msg)
|
||||
{
|
||||
syslog(LOG_CRIT, "%s Error: %d. %s", msg, errno, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int handle(int fd, char *tag, char *path)
|
||||
{
|
||||
char *options = NULL;
|
||||
int status;
|
||||
pid_t pid;
|
||||
int res;
|
||||
|
||||
res = asprintf(&options,
|
||||
"trans=fd,dfltuid=1001,dfltgid=50,version=9p2000,msize=4096,rfdno=%d,wfdno=%d",
|
||||
fd, fd);
|
||||
if (res < 0)
|
||||
fatal("asprintf()");
|
||||
|
||||
char *argv[] = {
|
||||
mount,
|
||||
"-t", "9p", "-o", options,
|
||||
tag, path,
|
||||
NULL
|
||||
};
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
execv(mount, argv);
|
||||
fatal("execv()");
|
||||
}
|
||||
|
||||
res = waitpid(pid, &status, 0);
|
||||
if (res == -1) {
|
||||
syslog(LOG_CRIT,
|
||||
"waitpid failed: %d. %s", errno, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
static int create_listening_socket(GUID serviceid)
|
||||
{
|
||||
SOCKADDR_HV sa;
|
||||
int lsock;
|
||||
int res;
|
||||
|
||||
lsock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW);
|
||||
if (lsock == -1)
|
||||
fatal("socket()");
|
||||
|
||||
sa.Family = AF_HYPERV;
|
||||
sa.Reserved = 0;
|
||||
sa.VmId = HV_GUID_WILDCARD;
|
||||
sa.ServiceId = serviceid;
|
||||
|
||||
res = bind(lsock, (const struct sockaddr *)&sa, sizeof(sa));
|
||||
if (res == -1)
|
||||
fatal("bind()");
|
||||
|
||||
res = listen(lsock, 1);
|
||||
if (res == -1)
|
||||
fatal("listen()");
|
||||
|
||||
return lsock;
|
||||
}
|
||||
|
||||
static int connect_socket(GUID serviceid)
|
||||
{
|
||||
SOCKADDR_HV sa;
|
||||
int sock;
|
||||
int res;
|
||||
|
||||
sock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW);
|
||||
if (sock == -1)
|
||||
fatal("socket()");
|
||||
|
||||
sa.Family = AF_HYPERV;
|
||||
sa.Reserved = 0;
|
||||
sa.VmId = HV_GUID_PARENT;
|
||||
sa.ServiceId = serviceid;
|
||||
|
||||
res = connect(sock, (const struct sockaddr *)&sa, sizeof(sa));
|
||||
if (res == -1)
|
||||
fatal("connect()");
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
static int accept_socket(int lsock)
|
||||
{
|
||||
SOCKADDR_HV sac;
|
||||
socklen_t socklen = sizeof(sac);
|
||||
int csock;
|
||||
|
||||
csock = accept(lsock, (struct sockaddr *)&sac, &socklen);
|
||||
if (csock == -1)
|
||||
fatal("accept()");
|
||||
|
||||
syslog(LOG_INFO, "Connect from: " GUID_FMT ":" GUID_FMT "\n",
|
||||
GUID_ARGS(sac.VmId), GUID_ARGS(sac.ServiceId));
|
||||
|
||||
return csock;
|
||||
}
|
||||
|
||||
void usage(char *name)
|
||||
{
|
||||
printf("%s: mount a 9P filesystem from an hvsock connection\n", name);
|
||||
printf("usage:\n");
|
||||
printf("\t[--serviceid <guid>] <listen | connect> <tag> <path>\n");
|
||||
printf("where\n");
|
||||
printf("\t--serviceid <guid>: use <guid> as the well-known service GUID\n");
|
||||
printf("\t (defaults to %s)\n", default_sid);
|
||||
printf("\t--listen: listen forever for incoming AF_HVSOCK connections\n");
|
||||
printf("\t--connect: connect to the parent partition\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int res = 0;
|
||||
GUID sid;
|
||||
int c;
|
||||
/* Defaults to a testing GUID */
|
||||
char *serviceid = default_sid;
|
||||
char *tag = NULL;
|
||||
char *path = NULL;
|
||||
|
||||
opterr = 0;
|
||||
while (1) {
|
||||
static struct option long_options[] = {
|
||||
/* These options set a flag. */
|
||||
{"serviceid", required_argument, NULL, 's'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
int option_index = 0;
|
||||
|
||||
c = getopt_long(argc, argv, "s:", long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 's':
|
||||
serviceid = optarg;
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
if (strcmp(argv[optind], "listen") == 0)
|
||||
mode = LISTEN;
|
||||
else if (strcmp(argv[optind], "connect") == 0)
|
||||
mode = CONNECT;
|
||||
optind++;
|
||||
}
|
||||
if (mode == NONE) {
|
||||
fprintf(stderr, "Please supply either listen or connect\n");
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
tag = argv[optind++];
|
||||
|
||||
if (optind < argc)
|
||||
path = argv[optind++];
|
||||
|
||||
if (!tag) {
|
||||
fprintf(stderr, "Please supply a tag name\n");
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
fprintf(stderr, "Please supply a path\n");
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
res = parseguid(serviceid, &sid);
|
||||
if (res) {
|
||||
fprintf(stderr,
|
||||
"Failed to parse serviceid as GUID: %s\n", serviceid);
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
openlog(argv[0], LOG_CONS | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
|
||||
for (;;) {
|
||||
int lsocket;
|
||||
int sock;
|
||||
int r;
|
||||
|
||||
if (mode == LISTEN) {
|
||||
syslog(LOG_INFO, "starting in listening mode with serviceid=%s, tag=%s, path=%s", serviceid, tag, path);
|
||||
lsocket = create_listening_socket(sid);
|
||||
sock = accept_socket(lsocket);
|
||||
close(lsocket);
|
||||
} else {
|
||||
syslog(LOG_INFO, "starting in connect mode with serviceid=%s, tag=%s, path=%s", serviceid, tag, path);
|
||||
sock = connect_socket(sid);
|
||||
}
|
||||
|
||||
r = handle(sock, tag, path);
|
||||
close(sock);
|
||||
|
||||
if (r == 0) {
|
||||
syslog(LOG_INFO, "mount successful for serviceid=%s tag=%s path=%s", serviceid, tag, path);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This can happen if the client times out the connection
|
||||
* after we accept it
|
||||
*/
|
||||
syslog(LOG_CRIT, "mount failed with %d for serviceid=%s tag=%s path=%s", r, serviceid, tag, path);
|
||||
sleep(1); /* retry */
|
||||
}
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
C_COMPILE=mobylinux/c-compile:ac075fed7c87e4af30d8490ae0504166cceb0df3@sha256:0e82d441ce112d638f904a08199c76b022c065a2dbf8908bb366755267d4417f
|
||||
|
||||
default: sbin/9pmount-vsock
|
||||
|
||||
DEPS=$(wildcard *.c *.h)
|
||||
|
||||
sbin/9pmount-vsock: $(DEPS)
|
||||
mkdir -p $(dir $@)
|
||||
tar cf - $(DEPS) | docker run --rm --net=none --log-driver=none -i $(C_COMPILE) -o $@ -lpthread | tar xf -
|
||||
|
||||
clean:
|
||||
rm -rf sbin
|
@@ -1,37 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "hvsock.h"
|
||||
|
||||
int parseguid(const char *s, GUID *g)
|
||||
{
|
||||
int res;
|
||||
int p0, p1, p2, p3, p4, p5, p6, p7;
|
||||
|
||||
res = sscanf(s, GUID_FMT,
|
||||
&g->Data1, &g->Data2, &g->Data3,
|
||||
&p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7);
|
||||
if (res != 11)
|
||||
return 1;
|
||||
g->Data4[0] = p0;
|
||||
g->Data4[1] = p1;
|
||||
g->Data4[2] = p2;
|
||||
g->Data4[3] = p3;
|
||||
g->Data4[4] = p4;
|
||||
g->Data4[5] = p5;
|
||||
g->Data4[6] = p6;
|
||||
g->Data4[7] = p7;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_GUID(HV_GUID_ZERO,
|
||||
0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||
DEFINE_GUID(HV_GUID_BROADCAST,
|
||||
0xFFFFFFFF, 0xFFFF, 0xFFFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
|
||||
DEFINE_GUID(HV_GUID_WILDCARD,
|
||||
0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||
DEFINE_GUID(HV_GUID_CHILDREN,
|
||||
0x90db8b89, 0x0d35, 0x4f79, 0x8c, 0xe9, 0x49, 0xea, 0x0a, 0xc8, 0xb7, 0xcd);
|
||||
DEFINE_GUID(HV_GUID_LOOPBACK,
|
||||
0xe0e16197, 0xdd56, 0x4a10, 0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38);
|
||||
DEFINE_GUID(HV_GUID_PARENT,
|
||||
0xa42e7cda, 0xd03f, 0x480c, 0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78);
|
@@ -1,48 +0,0 @@
|
||||
/* AF_HYPERV definitions and utilities */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
/* GUID handling */
|
||||
typedef struct _GUID {
|
||||
uint32_t Data1;
|
||||
uint16_t Data2;
|
||||
uint16_t Data3;
|
||||
uint8_t Data4[8];
|
||||
} GUID;
|
||||
|
||||
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
const GUID name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}}
|
||||
|
||||
/* Helper macros for parsing/printing GUIDs */
|
||||
#define GUID_FMT "%08x-%04hx-%04hx-%02x%02x-%02x%02x%02x%02x%02x%02x"
|
||||
#define GUID_ARGS(_g) \
|
||||
(_g).Data1, (_g).Data2, (_g).Data3, \
|
||||
(_g).Data4[0], (_g).Data4[1], (_g).Data4[2], (_g).Data4[3], \
|
||||
(_g).Data4[4], (_g).Data4[5], (_g).Data4[6], (_g).Data4[7]
|
||||
#define GUID_SARGS(_g) \
|
||||
&(_g).Data1, &(_g).Data2, &(_g).Data3, \
|
||||
&(_g).Data4[0], &(_g).Data4[1], &(_g).Data4[2], &(_g).Data4[3], \
|
||||
&(_g).Data4[4], &(_g).Data4[5], &(_g).Data4[6], &(_g).Data4[7]
|
||||
|
||||
extern int parseguid(const char *s, GUID *g);
|
||||
|
||||
/* HV Socket definitions */
|
||||
#define AF_HYPERV 43
|
||||
#define HV_PROTOCOL_RAW 1
|
||||
|
||||
typedef struct _SOCKADDR_HV {
|
||||
unsigned short Family;
|
||||
unsigned short Reserved;
|
||||
GUID VmId;
|
||||
GUID ServiceId;
|
||||
} SOCKADDR_HV;
|
||||
|
||||
extern const GUID HV_GUID_ZERO;
|
||||
extern const GUID HV_GUID_BROADCAST;
|
||||
extern const GUID HV_GUID_WILDCARD;
|
||||
extern const GUID HV_GUID_CHILDREN;
|
||||
extern const GUID HV_GUID_LOOPBACK;
|
||||
extern const GUID HV_GUID_PARENT;
|
@@ -1,10 +0,0 @@
|
||||
DEPS=proxy diagnostics transfused tap-vsockd docker nc-vsock vsudd 9pmount-vsock iptables
|
||||
.PHONY: clean $(DEPS)
|
||||
|
||||
default: $(DEPS)
|
||||
|
||||
$(DEPS):
|
||||
$(MAKE) -C $@
|
||||
|
||||
clean:
|
||||
for f in $(DEPS); do $(MAKE) -C $$f clean; done
|
@@ -1,185 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
depend()
|
||||
{
|
||||
need dev
|
||||
}
|
||||
|
||||
do_fsck()
|
||||
{
|
||||
# preen
|
||||
/sbin/e2fsck -p $*
|
||||
EXIT_CODE=$?
|
||||
# exit code 1 is errors corrected
|
||||
[ "${EXIT_CODE}" -eq 1 ] && EXIT_CODE=0
|
||||
# exit code 2 or 3 means need to reboot
|
||||
[ "${EXIT_CODE}" -eq 2 -o "${EXIT_CODE}" -eq 3 ] && /sbin/reboot
|
||||
# exit code 4 or over is fatal
|
||||
[ "${EXIT_CODE}" -lt 4 ] && return "${EXIT_CODE}"
|
||||
|
||||
# try harder
|
||||
/sbin/e2fsck -y $*
|
||||
# exit code 1 is errors corrected
|
||||
[ "${EXIT_CODE}" -eq 1 ] && EXIT_CODE=0
|
||||
# exit code 2 or 3 means need to reboot
|
||||
[ "${EXIT_CODE}" -eq 2 -o "${EXIT_CODE}" -eq 3 ] && /sbin/reboot
|
||||
# exit code 4 or over is fatal
|
||||
[ "${EXIT_CODE}" -ge 4 ] && printf "Filesystem unrecoverably corrupted, will reformat\n"
|
||||
|
||||
return "${EXIT_CODE}"
|
||||
}
|
||||
|
||||
do_fsck_extend_mount()
|
||||
{
|
||||
DRIVE="$1"
|
||||
DATA="$2"
|
||||
|
||||
do_fsck "$DATA" || return 1
|
||||
|
||||
# only try to extend if there is a single partition and free space
|
||||
PARTITIONS=$(sfdisk -J "$DRIVE" | jq '.partitiontable.partitions | length')
|
||||
|
||||
if [ "$PARTITIONS" -eq 1 ] && \
|
||||
sfdisk -F "$DRIVE" | grep -q 'Unpartitioned space' &&
|
||||
! sfdisk -F "$DRIVE" | grep -q '0 B, 0 bytes, 0 sectors'
|
||||
then
|
||||
SPACE=$(sfdisk -F "$DRIVE" | grep 'Unpartitioned space')
|
||||
printf "Resizing disk partition: $SPACE\n"
|
||||
|
||||
START=$(sfdisk -J "$DRIVE" | jq -e '.partitiontable.partitions | map(select(.type=="83")) | .[0].start')
|
||||
|
||||
sfdisk -q --delete "$DRIVE" 2> /dev/null
|
||||
echo "${START},,83;" | sfdisk -q "$DRIVE"
|
||||
|
||||
# set bootable flag
|
||||
sfdisk -A "$DRIVE" 1
|
||||
|
||||
# update status
|
||||
blockdev --rereadpt $diskdev 2> /dev/null
|
||||
mdev -s
|
||||
|
||||
# wait for device
|
||||
for i in $(seq 1 50); do test -b "$DATA" && break || sleep .1; mdev -s; done
|
||||
|
||||
# resize2fs fails unless we use -f here
|
||||
do_fsck -f "$DATA" || return 1
|
||||
resize2fs "$DATA"
|
||||
|
||||
do_fsck "$DATA" || return 1
|
||||
fi
|
||||
|
||||
mount "$DATA" /var
|
||||
}
|
||||
|
||||
do_mkfs()
|
||||
{
|
||||
diskdev="$1"
|
||||
|
||||
# new disks does not have an DOS signature in sector 0
|
||||
# this makes sfdisk complain. We can workaround this by letting
|
||||
# fdisk create that DOS signature, by just do a "w", a write.
|
||||
# http://bugs.alpinelinux.org/issues/145
|
||||
echo "w" | fdisk $diskdev >/dev/null
|
||||
|
||||
# format one large partition
|
||||
echo ";" | sfdisk --quiet $diskdev
|
||||
|
||||
# update status
|
||||
blockdev --rereadpt $diskdev 2> /dev/null
|
||||
mdev -s
|
||||
|
||||
FSOPTS="-O resize_inode,has_journal,extent,huge_file,flex_bg,uninit_bg,64bit,dir_nlink,extra_isize"
|
||||
|
||||
mkfs.ext4 -q -F $FSOPTS ${diskdev}1
|
||||
|
||||
mount ${diskdev}1 /var
|
||||
}
|
||||
|
||||
do_swapfile()
|
||||
{
|
||||
DRIVE=$1
|
||||
SIZE=$(sfdisk -J "$DRIVE" | jq -e -r '.partitiontable.partitions | map(select(.type=="83")) | .[0].size')
|
||||
SWAP=/var/spool/swap
|
||||
MOUNT=$(mount | grep 'on /var type')
|
||||
# Create swap on desktop platforms if disk size larger than 16GB
|
||||
if [ "$(mobyplatform)" = "mac" -o "$(mobyplatform)" = "windows" ] && \
|
||||
[ ! -f $SWAP ] && [ "$SIZE" -gt 33554432 ] && \
|
||||
[ -n "$MOUNT" ]
|
||||
then
|
||||
mkdir -p "$(dirname $SWAP)"
|
||||
dd if=/dev/zero of=$SWAP bs=1k count=1048576
|
||||
chmod 600 $SWAP
|
||||
mkswap $SWAP
|
||||
fi
|
||||
[ -f "$SWAP" ] && swapon $SWAP
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
ebegin "Configuring host block device"
|
||||
|
||||
# We are being specific with Azure for now. Otherwise the subshell
|
||||
# below will reference /dev/sdb, which is Azure's "local resource disk"
|
||||
# (see
|
||||
# https://blogs.msdn.microsoft.com/mast/2013/12/06/understanding-the-temporary-drive-on-windows-azure-virtual-machines/).
|
||||
#
|
||||
# Since attempting to format swap on that disk will cause Azure VHD
|
||||
# validation to fail, we default to using /dev/sda. requirements
|
||||
# including not allowing swap partitions.
|
||||
#
|
||||
# IMPORTANT: If this, or the root device (/dev/sda1) changes in the
|
||||
# syslinux / bootloader config for Azure, they will need to be updated
|
||||
# in parallel.
|
||||
if [ "$(mobyplatform)" = "azure" ]
|
||||
then
|
||||
DEV="sda"
|
||||
else
|
||||
DEV="$(find /dev -maxdepth 1 -type b ! -name 'loop*' | grep -v '[0-9]$' | sed 's@.*/dev/@@' | sort | head -1 )"
|
||||
fi
|
||||
|
||||
[ -z "${DEV}" ] && exit 1
|
||||
|
||||
DRIVE="/dev/${DEV}"
|
||||
|
||||
# see if it has a partition table already
|
||||
if sfdisk -d "${DRIVE}" >/dev/null 2>/dev/null
|
||||
then
|
||||
DATA=$(sfdisk -J "$DRIVE" | jq -e -r '.partitiontable.partitions | map(select(.type=="83")) | .[0].node')
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
do_fsck_extend_mount "$DRIVE" "$DATA" || do_mkfs "$DRIVE"
|
||||
else
|
||||
do_mkfs "$DRIVE"
|
||||
fi
|
||||
else
|
||||
do_mkfs "$DRIVE"
|
||||
fi
|
||||
|
||||
# Use existing swap partition of present; we do not create one now
|
||||
SWAP_PART=$(fdisk -l "$DRIVE" | grep 'Linux swap' | head -1 | awk '{print $1}')
|
||||
if [ -z "$SWAP_PART" ]
|
||||
then
|
||||
do_swapfile "$DRIVE"
|
||||
else
|
||||
swapon "$SWAP_PART"
|
||||
fi
|
||||
|
||||
# boot2docker compat, has /var and /tmp on partition
|
||||
[ -d /var/var/lib/boot2docker/ ] && (mv /var/var/* /var && rm -rf /var/var)
|
||||
|
||||
# add in directories under /var that are lost in formatting
|
||||
[ -L /var/run ] || ln -s /run /var/run
|
||||
[ -L /var/lock ] || ln -s /run/lock /var/lock
|
||||
[ -d /var/log ] || mkdir -m 755 /var/log
|
||||
[ -d /var/empty ] || mkdir -m 755 /var/empty
|
||||
[ -d /var/spool ] || mkdir -m 755 /var/spool
|
||||
[ -d /var/cache ] || mkdir -m 755 /var/cache
|
||||
[ -d /var/cache/apk ] || mkdir -m 755 /var/cache/apk
|
||||
[ -d /var/cache/misc ] || mkdir -m 755 /var/cache/misc
|
||||
[ -d /var/local ] || mkdir -m 755 /var/local
|
||||
[ -d /var/tmp ] || mkdir -m 1777 /var/tmp
|
||||
|
||||
mount | grep -q ' on /var type '
|
||||
|
||||
eend $? "Failed to mount block device"
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
pool pool.ntp.org iburst minpoll 2
|
||||
makestep 0.5 -1
|
||||
keyfile /etc/chrony/chrony.keys
|
||||
driftfile /var/lib/chrony/chrony.drift
|
||||
cmdport 0
|
@@ -1,91 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
# Copyright 1999-2007 Gentoo Foundation
|
||||
# Distributed under the terms of the GNU General Public License v2
|
||||
# $Header: /var/cvsroot/gentoo-x86/net-misc/chrony/files/chronyd.rc,v 1.8 2007/03/22 14:32:09 tove Exp $
|
||||
|
||||
description="NTP daemon"
|
||||
|
||||
depend() {
|
||||
need net
|
||||
after firewall
|
||||
provide ntp-client ntp-server
|
||||
use dns
|
||||
}
|
||||
|
||||
checkconfig() {
|
||||
# Note that /etc/chrony/chrony.keys is *NOT* checked. This
|
||||
# is because the user may have specified another key
|
||||
# file, and we don't want to force the user to use that
|
||||
# exact name for the key file.
|
||||
if [ ! -f "${CFGFILE}" ] ; then
|
||||
eerror "Please create ${CFGFILE} and the"
|
||||
eerror "chrony key file (usually /etc/chrony/chrony.keys)"
|
||||
eerror "by using the"
|
||||
eerror ""
|
||||
eerror " chrony.conf.example"
|
||||
eerror " chrony.keys.example"
|
||||
eerror ""
|
||||
eerror "files (from the documentation directory)"
|
||||
eerror "as templates."
|
||||
return 1
|
||||
else
|
||||
# Actually, I tried it, and chrony seems to ignore the pidfile
|
||||
# option. I'm going to leave it here anyway, since you never
|
||||
# know if it might be handy
|
||||
PIDFILE=`awk '/^ *pidfile/{print $2}' "${CFGFILE}"`
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
setxtrarg() {
|
||||
if [ -c /dev/rtc ]; then
|
||||
grep -q '^rtcfile' "${CFGFILE}" && ARGS="${ARGS} -s"
|
||||
fi
|
||||
grep -q '^dumponexit$' "${CFGFILE}" && ARGS="${ARGS} -r"
|
||||
return 0
|
||||
}
|
||||
|
||||
start() {
|
||||
case "$(mobyplatform)" in
|
||||
"windows")
|
||||
exit 0
|
||||
;;
|
||||
"mac")
|
||||
sed -i -e "s/^pool [^ ]\+/server 192.168.65.1 trust/g" /etc/chrony/chrony.conf
|
||||
;;
|
||||
"aws")
|
||||
sed -i -e "s/^pool [^ ]\+/pool 0.amazon.pool.ntp.org/g" /etc/chrony/chrony.conf
|
||||
;;
|
||||
"gcp")
|
||||
sed -i -e "s/^pool [^ ]\+/server metadata.google.internal trust/g" /etc/chrony/chrony.conf
|
||||
;;
|
||||
"azure")
|
||||
# TODO needs an ntp solution
|
||||
;;
|
||||
esac
|
||||
|
||||
checkconfig || return $?
|
||||
setxtrarg
|
||||
|
||||
[ -n "${PIDFILE}" ] || PIDFILE=/var/run/chronyd.pid
|
||||
|
||||
ebegin "Starting chronyd"
|
||||
start-stop-daemon --start --quiet \
|
||||
--exec /usr/sbin/chronyd \
|
||||
--pidfile "${PIDFILE}" \
|
||||
-- -f "${CFGFILE}" ${ARGS}
|
||||
eend $? "Failed to start chronyd"
|
||||
}
|
||||
|
||||
stop() {
|
||||
[ "$(mobyplatform)" = "windows" ] && exit 0
|
||||
|
||||
checkconfig || return $?
|
||||
|
||||
[ -n "${PIDFILE}" ] || PIDFILE=/var/run/chronyd.pid
|
||||
|
||||
ebegin "Stopping chronyd"
|
||||
start-stop-daemon --stop --quiet \
|
||||
--pidfile "${PIDFILE}"
|
||||
eend $? "Failed to stop chronyd"
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
depend()
|
||||
{
|
||||
before docker
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
ebegin "Running system containerd"
|
||||
|
||||
# set ulimits as high as possible
|
||||
ulimit -n 1048576
|
||||
ulimit -p unlimited
|
||||
|
||||
/usr/bin/containerd 1>&2 2>/var/log/containerd.log &
|
||||
|
||||
ewaitfile 5 /var/run/containerd/containerd.sock
|
||||
|
||||
eend $? "Failed to start system containerd"
|
||||
|
||||
ebegin "Running system containers"
|
||||
|
||||
LOG=/var/log/system-containers.log
|
||||
touch $LOG
|
||||
|
||||
for f in /containers/*
|
||||
do
|
||||
containerd-ctr containers start --no-pivot --attach "$(basename $f)" "$f" 2>$LOG >$LOG &
|
||||
printf " $(basename $f)"
|
||||
done
|
||||
|
||||
eend $? "Failed to start system containers"
|
||||
}
|
1
alpine/packages/diagnostics/.gitignore
vendored
1
alpine/packages/diagnostics/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
usr/bin/diagnostics-server
|
@@ -1,14 +0,0 @@
|
||||
GO_COMPILE=mobylinux/go-compile:d2d25ac665b5148ad356d0eab3ff3762a68c633d@sha256:aab55d0c317460850e66a07dd94139cc11ea9e1c0bee88716a6a8c768740885f
|
||||
|
||||
default: usr/bin/diagnostics-server
|
||||
|
||||
DEPS=$(wildcard *.go)
|
||||
|
||||
usr/bin/diagnostics-server: $(DEPS) ../vendor/manifest
|
||||
mkdir -p $(dir $@)
|
||||
tar cf - $(DEPS) -C .. vendor | docker run --rm --net=none --log-driver=none -i $(GO_COMPILE) -o $@ | tar xf -
|
||||
|
||||
clean:
|
||||
rm -f usr/bin/diagnostics-server
|
||||
|
||||
.DELETE_ON_ERROR:
|
@@ -1,214 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultCaptureTimeout = 5 * time.Second
|
||||
defaultCommandTimeout = defaultCaptureTimeout
|
||||
|
||||
// Might eventually have some pretty long (~30s) traces in here, so 35
|
||||
// seconds seems reasonable.
|
||||
allCaptureTimeout = 35 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
commonCmdCaptures = []CommandCapturer{
|
||||
{"/bin/date", nil, defaultCommandTimeout},
|
||||
{"/bin/uname", []string{"-a"}, defaultCommandTimeout},
|
||||
{"/bin/ps", []string{"uax"}, defaultCommandTimeout},
|
||||
{"/bin/netstat", []string{"-tulpn"}, defaultCommandTimeout},
|
||||
{"/sbin/apk", []string{"audit", "--system"}, defaultCommandTimeout}, // check if system binaries were modified
|
||||
{"/sbin/iptables-save", nil, defaultCommandTimeout},
|
||||
{"/sbin/ifconfig", nil, defaultCommandTimeout},
|
||||
{"/sbin/route", nil, defaultCommandTimeout},
|
||||
{"/usr/sbin/brctl", []string{"show"}, defaultCommandTimeout},
|
||||
{"/bin/dmesg", nil, defaultCommandTimeout},
|
||||
{"/usr/bin/docker", []string{"ps"}, defaultCommandTimeout},
|
||||
{"/usr/bin/docker", []string{"version"}, defaultCommandTimeout},
|
||||
{"/usr/bin/docker", []string{"info"}, defaultCommandTimeout},
|
||||
{"/usr/bin/docker", []string{"network", "ls"}, defaultCommandTimeout},
|
||||
{"/usr/bin/docker", []string{"node", "ls"}, defaultCommandTimeout},
|
||||
{"/usr/bin/docker", []string{"service", "ls"}, defaultCommandTimeout},
|
||||
{"/usr/bin/tail", []string{"-20000", "/var/log/docker.log"}, defaultCommandTimeout},
|
||||
{"/usr/bin/tail", []string{"-20000", "/var/log/messages"}, defaultCommandTimeout},
|
||||
{"/bin/mount", nil, defaultCommandTimeout},
|
||||
{"/bin/df", []string{"-h"}, defaultCommandTimeout},
|
||||
{"/bin/ls", []string{"-l", "/var"}, defaultCommandTimeout},
|
||||
{"/bin/ls", []string{"-l", "/var/lib"}, defaultCommandTimeout},
|
||||
{"/bin/ls", []string{"-l", "/var/lib/docker"}, defaultCommandTimeout},
|
||||
{"/usr/bin/diagnostics", nil, defaultCommandTimeout},
|
||||
{"/bin/ping", []string{"-w", "5", "8.8.8.8"}, 6 * time.Second},
|
||||
{"/bin/cat", []string{"/etc/docker/daemon.json"}, defaultCommandTimeout},
|
||||
{"/bin/cat", []string{"/etc/network/interfaces"}, defaultCommandTimeout},
|
||||
{"/bin/cat", []string{"/etc/resolv.conf"}, defaultCommandTimeout},
|
||||
{"/bin/cat", []string{"/etc/sysctl.conf"}, defaultCommandTimeout},
|
||||
{"/bin/cat", []string{"/proc/cpuinfo"}, defaultCommandTimeout},
|
||||
{"/usr/bin/nslookup", []string{"docker.com"}, defaultCommandTimeout},
|
||||
{"/usr/bin/nslookup", []string{"docker.com", "8.8.8.8"}, defaultCommandTimeout},
|
||||
{"/usr/bin/curl", []string{"http://www.docker.com/"}, defaultCommandTimeout},
|
||||
{"/usr/bin/curl", []string{"http://104.239.220.248/"}, defaultCommandTimeout}, // a www.docker.com address
|
||||
{"/usr/bin/curl", []string{"http://216.58.213.68/"}, defaultCommandTimeout}, // a www.google.com address
|
||||
{"/usr/bin/curl", []string{"http://91.198.174.192/"}, defaultCommandTimeout}, // a www.wikipedia.com address
|
||||
{"/bin/cat", []string{"/var/lib/docker/volumes/metadata.db"}, defaultCommandTimeout}, // [docker/docker#29636]
|
||||
{"/bin/cat", []string{"/var/lib/docker/network/files/local-kv.db"}, defaultCommandTimeout}, // [docker/docker#29636]
|
||||
}
|
||||
localCmdCaptures = []CommandCapturer{
|
||||
{"/usr/bin/tail", []string{"-100", "/var/log/proxy-vsockd.log"}, defaultCommandTimeout},
|
||||
{"/usr/bin/tail", []string{"-100", "/var/log/vsudd.log"}, defaultCommandTimeout},
|
||||
}
|
||||
localCaptures = []Capturer{NewDatabaseCapturer()}
|
||||
)
|
||||
|
||||
func init() {
|
||||
for _, c := range commonCmdCaptures {
|
||||
localCaptures = append(localCaptures, c)
|
||||
}
|
||||
for _, c := range localCmdCaptures {
|
||||
localCaptures = append(localCaptures, c)
|
||||
}
|
||||
}
|
||||
|
||||
// Capturer defines behavior for structs which will capture arbitrary
|
||||
// diagnostic information and write it to a tar archive with a timeout.
|
||||
type Capturer interface {
|
||||
Capture(context.Context, *tar.Writer)
|
||||
}
|
||||
|
||||
// CommandCapturer describes commands with its arguments and timeout to
|
||||
// capture diagnostic information from
|
||||
type CommandCapturer struct {
|
||||
command string
|
||||
args []string
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// Capture writes output from a CommandCapturer to a tar archive
|
||||
func (cc CommandCapturer) Capture(parentCtx context.Context, w *tar.Writer) {
|
||||
stdout := &bytes.Buffer{}
|
||||
stderr := &bytes.Buffer{}
|
||||
done := make(chan struct{})
|
||||
|
||||
name := strings.Join(append([]string{path.Base(cc.command)}, cc.args...), " ")
|
||||
ctx, cancel := context.WithTimeout(parentCtx, cc.timeout)
|
||||
cmd := exec.CommandContext(ctx, cc.command, cc.args...)
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
|
||||
go runCmd(cmd, done)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Println("ERROR:", ctx.Err())
|
||||
case <-done:
|
||||
tarWrite(w, stdout, name+".stdout")
|
||||
tarWrite(w, stderr, name+".stderr")
|
||||
}
|
||||
|
||||
cancel()
|
||||
}
|
||||
|
||||
// TODO(nathanleclaire): Is the user of log.Fatalln in this function really the
|
||||
// right choice? i.e., should the program really exit on failure here?
|
||||
func tarWrite(w *tar.Writer, buf *bytes.Buffer, headerName string) {
|
||||
contents, err := ioutil.ReadAll(buf)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
log.Println("HEADER:", headerName)
|
||||
log.Println("{")
|
||||
contentLines := strings.Split(string(contents), "\n")
|
||||
for _, line := range contentLines {
|
||||
log.Println(line)
|
||||
}
|
||||
log.Println("}")
|
||||
|
||||
hdr := &tar.Header{
|
||||
Name: headerName,
|
||||
Mode: 0644,
|
||||
Size: int64(len(contents)),
|
||||
}
|
||||
if err := w.WriteHeader(hdr); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if _, err := w.Write(contents); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
func runCmd(cmd *exec.Cmd, done chan<- struct{}) {
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Println("ERROR:", err)
|
||||
}
|
||||
done <- struct{}{}
|
||||
}
|
||||
|
||||
// DatabaseCapturer defines behavior for capturing diagnostic
|
||||
// information from dumping a database
|
||||
type DatabaseCapturer struct {
|
||||
*CommandCapturer
|
||||
}
|
||||
|
||||
// NewDatabaseCapturer creates a new DatabaseCapturer that cats
|
||||
// the database's contents to a CommandCapturer
|
||||
func NewDatabaseCapturer() DatabaseCapturer {
|
||||
return DatabaseCapturer{
|
||||
&CommandCapturer{
|
||||
command: "/bin/cat",
|
||||
timeout: defaultCommandTimeout,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Capture writes output from a DatabaseCapturer to a tar archive
|
||||
func (dc DatabaseCapturer) Capture(parentCtx context.Context, w *tar.Writer) {
|
||||
// Dump the database
|
||||
dbBase := "/Database"
|
||||
filepath.Walk(dbBase, func(path string, f os.FileInfo, err error) error {
|
||||
if f.Mode().IsRegular() {
|
||||
dc.CommandCapturer.args = []string{path}
|
||||
dc.CommandCapturer.Capture(parentCtx, w)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Capture is the outer level wrapper function to trigger the capturing of
|
||||
// information. Clients are expected to call it with a slice of Capturers
|
||||
// which define the information to be captured. By using an interface we can
|
||||
// flexibly define various capture actions for the various listeners.
|
||||
//
|
||||
// It is a passed a tar.Writer which the results of the capture will be written
|
||||
// to.
|
||||
func Capture(w *tar.Writer, captures []Capturer) {
|
||||
allCaptureCtx, cancel := context.WithTimeout(context.Background(), allCaptureTimeout)
|
||||
done := make(chan struct{})
|
||||
|
||||
go func(captures []Capturer, ctx context.Context, done chan<- struct{}) {
|
||||
for _, c := range captures {
|
||||
c.Capture(ctx, w)
|
||||
}
|
||||
done <- struct{}{}
|
||||
}(captures, allCaptureCtx, done)
|
||||
|
||||
select {
|
||||
case <-allCaptureCtx.Done():
|
||||
log.Println("Global command context", allCaptureCtx.Err())
|
||||
case <-done:
|
||||
log.Println("Captures all finished")
|
||||
}
|
||||
|
||||
cancel()
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
depend()
|
||||
{
|
||||
after docker containerd
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
/usr/bin/diagnostics
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
start()
|
||||
{
|
||||
ebegin "Starting diagnostics server"
|
||||
|
||||
DIAGNOSTICS_SERVER_FLAGS=""
|
||||
|
||||
case "$(mobyplatform)" in
|
||||
"aws")
|
||||
DIAGNOSTICS_SERVER_FLAGS="-http"
|
||||
;;
|
||||
"azure")
|
||||
DIAGNOSTICS_SERVER_FLAGS="-http"
|
||||
;;
|
||||
"gcp")
|
||||
DIAGNOSTICS_SERVER_FLAGS="-http"
|
||||
;;
|
||||
"windows")
|
||||
DIAGNOSTICS_SERVER_FLAGS="-hvsock"
|
||||
;;
|
||||
"mac")
|
||||
DIAGNOSTICS_SERVER_FLAGS="-vsock"
|
||||
;;
|
||||
esac
|
||||
|
||||
/usr/bin/diagnostics-server ${DIAGNOSTICS_SERVER_FLAGS} &
|
||||
|
||||
eend 0
|
||||
}
|
@@ -1,225 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
errDockerRespNotOK = errors.New("Docker API call did not return OK")
|
||||
)
|
||||
|
||||
const (
|
||||
healthcheckTimeout = 5 * time.Second
|
||||
dockerSock = "/var/run/docker.sock"
|
||||
dockerPingOK = "LGTM"
|
||||
httpMagicPort = ":44554" // chosen arbitrarily due to IANA availability -- might change
|
||||
bucket = "editionsdiagnostics"
|
||||
sessionIDField = "session"
|
||||
)
|
||||
|
||||
var (
|
||||
cloudCaptures = []Capturer{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
for _, c := range commonCmdCaptures {
|
||||
cloudCaptures = append(cloudCaptures, c)
|
||||
}
|
||||
|
||||
cloudCaptures = append(cloudCaptures, SystemContainerCapturer{})
|
||||
}
|
||||
|
||||
// HTTPDiagnosticListener sets a health check and optional diagnostic endpoint
|
||||
// for cloud editions.
|
||||
type HTTPDiagnosticListener struct{}
|
||||
|
||||
func dockerHTTPGet(ctx context.Context, url string) (*http.Response, error) {
|
||||
client := &http.Client{
|
||||
Transport: &UnixSocketRoundTripper{},
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return resp, errDockerRespNotOK
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// UnixSocketRoundTripper provides a way to make HTTP request to Docker socket
|
||||
// directly.
|
||||
type UnixSocketRoundTripper struct{}
|
||||
|
||||
// RoundTrip dials the Docker UNIX socket to make a HTTP request.
|
||||
func (u UnixSocketRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
dial, err := net.Dial("unix", dockerSock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn := httputil.NewClientConn(dial, nil)
|
||||
|
||||
return conn.Do(req)
|
||||
}
|
||||
|
||||
// Listen starts the HTTPDiagnosticListener and sets up handlers for its endpoints
|
||||
func (h HTTPDiagnosticListener) Listen() {
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), healthcheckTimeout)
|
||||
defer cancel()
|
||||
|
||||
errCh := make(chan error)
|
||||
|
||||
go func() {
|
||||
_, err := dockerHTTPGet(ctx, "/_ping")
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
if err != nil {
|
||||
http.Error(w, "Docker daemon ping error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
case <-ctx.Done():
|
||||
http.Error(w, "Docker daemon ping timed out", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := w.Write([]byte(dockerPingOK)); err != nil {
|
||||
log.Println("Error writing HTTP success response:", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
http.HandleFunc("/diagnose", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Invalid request type. Should be POST with form value 'session' set", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
diagnosticsSessionID := r.FormValue(sessionIDField)
|
||||
|
||||
if diagnosticsSessionID == "" {
|
||||
http.Error(w, "No 'session' field specified for diagnostics run", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
http.Error(w, "Error getting hostname:"+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// To keep URL cleaner
|
||||
hostname = strings.Replace(hostname, ".", "-", -1)
|
||||
|
||||
if _, err := w.Write([]byte("OK hostname=" + hostname + " session=" + diagnosticsSessionID + "\n")); err != nil {
|
||||
http.Error(w, "Error writing: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Do the actual capture and uplaod to S3 in the background.
|
||||
// No need to make caller sit and wait for the result. They
|
||||
// probably have a lot of other things going on, like other
|
||||
// servers to request diagnostics for.
|
||||
//
|
||||
// TODO(nathanleclaire): Potentially, endpoint to check the
|
||||
// result of this capture and upload process as well.
|
||||
go func() {
|
||||
dir, err := ioutil.TempDir("", "diagnostics")
|
||||
if err != nil {
|
||||
log.Println("Error creating temp dir on diagnostic request:: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := ioutil.TempFile(dir, "diagnostics")
|
||||
if err != nil {
|
||||
log.Println("Error creating temp file on diagnostic request:", err)
|
||||
return
|
||||
}
|
||||
|
||||
tarWriter := tar.NewWriter(file)
|
||||
|
||||
Capture(tarWriter, cloudCaptures)
|
||||
|
||||
if err := tarWriter.Close(); err != nil {
|
||||
log.Println("Error closing archive writer: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := file.Close(); err != nil {
|
||||
log.Println("Error closing file: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
readFile, err := os.Open(file.Name())
|
||||
if err != nil {
|
||||
log.Println("Error opening report file to upload: ", err)
|
||||
return
|
||||
}
|
||||
defer readFile.Close()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
contentLength, err := io.Copy(buf, readFile)
|
||||
if err != nil {
|
||||
log.Println("Error copying to buffer: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
reportURI := fmt.Sprintf("https://%s.s3.amazonaws.com/%s-%s.tar", bucket, diagnosticsSessionID, hostname)
|
||||
|
||||
uploadReq, err := http.NewRequest("PUT", reportURI, buf)
|
||||
if err != nil {
|
||||
log.Println("Error getting bucket request: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
uploadReq.Header.Set("x-amz-acl", "bucket-owner-full-control")
|
||||
uploadReq.Header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
|
||||
uploadReq.Header.Set("Content-Length", strconv.Itoa(int(contentLength)))
|
||||
|
||||
client := &http.Client{}
|
||||
|
||||
if uploadResp, err := client.Do(uploadReq); err != nil {
|
||||
log.Println("Error writing: ", err)
|
||||
body, err := ioutil.ReadAll(uploadResp.Body)
|
||||
if err != nil {
|
||||
log.Println("Error reading response body: ", err)
|
||||
}
|
||||
log.Println(string(body))
|
||||
return
|
||||
}
|
||||
log.Println("No error sending S3 request")
|
||||
log.Println("Diagnostics request finished")
|
||||
}()
|
||||
})
|
||||
|
||||
// Start HTTP server to indicate general Docker health.
|
||||
// TODO: no magic port?
|
||||
http.ListenAndServe(httpMagicPort, nil)
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"syscall"
|
||||
|
||||
"github.com/rneugeba/virtsock/go/hvsock"
|
||||
)
|
||||
|
||||
// HVSockDiagnosticListener is a diagnostic server using HVSock (Windows)
|
||||
type HVSockDiagnosticListener struct{}
|
||||
|
||||
// Listen sets up the diagnostic server to listen on an HVSock
|
||||
func (l HVSockDiagnosticListener) Listen() {
|
||||
svcid, _ := hvsock.GuidFromString("445BA2CB-E69B-4912-8B42-D7F494D007EA")
|
||||
hvsock, err := hvsock.Listen(hvsock.HypervAddr{VmId: hvsock.GUID_WILDCARD, ServiceId: svcid})
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); !ok || errno != syscall.EAFNOSUPPORT {
|
||||
log.Printf("Failed to bind to hvsock port: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
TarRespond(hvsock)
|
||||
}
|
||||
}
|
@@ -1,63 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"log/syslog"
|
||||
)
|
||||
|
||||
func init() {
|
||||
syslog, err := syslog.New(syslog.LOG_INFO|syslog.LOG_DAEMON, "diagnostics")
|
||||
if err != nil {
|
||||
log.Fatalln("Failed to open syslog", err)
|
||||
}
|
||||
|
||||
log.SetOutput(syslog)
|
||||
log.SetFlags(0)
|
||||
}
|
||||
|
||||
// DiagnosticListener listens for starting diagnostics capture requests
|
||||
type DiagnosticListener interface {
|
||||
// Listen(), a blocking method intended to be invoked in its own
|
||||
// goroutine, will listen for a diagnostic information request and
|
||||
// capture the desired information if one is received.
|
||||
Listen()
|
||||
}
|
||||
|
||||
// DiagnosticUploader uploads the collected information to the mothership.
|
||||
type DiagnosticUploader interface {
|
||||
Upload() error
|
||||
}
|
||||
|
||||
func main() {
|
||||
flHTTP := flag.Bool("http", false, "Enable diagnostic HTTP listener")
|
||||
flVSock := flag.Bool("vsock", false, "Enable vsock listener")
|
||||
flHVSock := flag.Bool("hvsock", false, "Enable hvsock listener")
|
||||
flRawTCP := flag.Bool("rawtcp", false, "Enable raw TCP listener")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
listeners := make([]DiagnosticListener, 0)
|
||||
|
||||
if *flHTTP {
|
||||
listeners = append(listeners, HTTPDiagnosticListener{})
|
||||
}
|
||||
|
||||
if *flVSock {
|
||||
listeners = append(listeners, VSockDiagnosticListener{})
|
||||
}
|
||||
|
||||
if *flHVSock {
|
||||
listeners = append(listeners, HVSockDiagnosticListener{})
|
||||
}
|
||||
|
||||
if *flRawTCP {
|
||||
listeners = append(listeners, RawTCPDiagnosticListener{})
|
||||
}
|
||||
|
||||
for _, l := range listeners {
|
||||
go l.Listen()
|
||||
}
|
||||
forever := make(chan int)
|
||||
<-forever
|
||||
}
|
@@ -1,21 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
// RawTCPDiagnosticListener is a diagnostic server listening on a TCP port
|
||||
type RawTCPDiagnosticListener struct{}
|
||||
|
||||
// Listen for RawTCPDiagnosticListener listens on port 62374
|
||||
func (l RawTCPDiagnosticListener) Listen() {
|
||||
ip, err := net.Listen("tcp", ":62374")
|
||||
if err != nil {
|
||||
log.Printf("Failed to bind to TCP port 62374: %s", err)
|
||||
}
|
||||
|
||||
for {
|
||||
TarRespond(ip)
|
||||
}
|
||||
}
|
@@ -1,90 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
)
|
||||
|
||||
const (
|
||||
systemLogDir = "editionslogs"
|
||||
systemContainerLabel = "com.docker.editions.system"
|
||||
)
|
||||
|
||||
// SystemContainerCapturer gets the logs from containers which are run
|
||||
// specifically for Docker Editions.
|
||||
type SystemContainerCapturer struct{}
|
||||
|
||||
// Capture writes output from a CommandCapturer to a tar archive
|
||||
func (s SystemContainerCapturer) Capture(parentCtx context.Context, w *tar.Writer) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultCaptureTimeout)
|
||||
defer cancel()
|
||||
|
||||
errCh := make(chan error)
|
||||
|
||||
go func() {
|
||||
// URL encoded.
|
||||
// Reads more like this:
|
||||
// /v1.25/containers/json?all=1&filters={"label":{"com.docker.editions.system":true}}
|
||||
resp, err := dockerHTTPGet(ctx, "/containers/json?all=1&filters=%7B%22label%22%3A%7B%22"+systemContainerLabel+"%22%3Atrue%7D%7D")
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
names := []struct {
|
||||
ID string `json:"id"`
|
||||
Names []string
|
||||
}{}
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(&names); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
for _, c := range names {
|
||||
resp, err := dockerHTTPGet(ctx, "/containers/"+c.ID+"/logs?stderr=1&stdout=1×tamps=1&tail=all")
|
||||
if err != nil {
|
||||
log.Println("ERROR (get request):", err)
|
||||
continue
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
logLines, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Println("ERROR (reading response):", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Docker API returns a Names array where the names are
|
||||
// like this: ["/foobar", "/quux"]
|
||||
//
|
||||
// Use the first one from it.
|
||||
//
|
||||
// Additionally, the slash from it helps delimit a path
|
||||
// separator in the tar archive.
|
||||
//
|
||||
// TODO(nathanleclaire): This seems fragile, but I'm
|
||||
// not sure what approach would be much better.
|
||||
tarWrite(w, bytes.NewBuffer(logLines), systemLogDir+c.Names[0])
|
||||
}
|
||||
|
||||
errCh <- nil
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Println("System container log capture context error", ctx.Err())
|
||||
case err := <-errCh:
|
||||
if err != nil {
|
||||
log.Println("System container log capture error", err)
|
||||
}
|
||||
log.Println("System container log capture finished successfully")
|
||||
}
|
||||
}
|
@@ -1,40 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
// TarRespond is used to write back a tar archive over a connection for the
|
||||
// lower-level listener types.
|
||||
//
|
||||
// In local virtualization (which this is for) we write back a tar file
|
||||
// directly and the client takes care of shipping the result to the mothership.
|
||||
//
|
||||
// By contrast, in cloud editions we expect each node to ship the captured
|
||||
// information on its own, so this function is not used.
|
||||
//
|
||||
// This is a deliberate design to choice to ensure that it is possible in the
|
||||
// future for diagnostic information to be reported from nodes which have have
|
||||
// been separated via network partition from the node which initiates
|
||||
// diagnostic collection, and/or if we decide to automatically collect
|
||||
// diagnostic information from nodes which deem *themselves* unhealthy at a
|
||||
// future time.
|
||||
func TarRespond(l net.Listener) {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
log.Printf("Error accepting connection: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
w := tar.NewWriter(conn)
|
||||
|
||||
Capture(w, localCaptures)
|
||||
|
||||
if err := w.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
EXIT_STATUS=0
|
||||
|
||||
fail()
|
||||
{
|
||||
printf "✗ $1"
|
||||
EXIT_STATUS=1
|
||||
}
|
||||
|
||||
ok()
|
||||
{
|
||||
printf "✓ $1"
|
||||
}
|
||||
|
||||
DEV="$(find /dev -maxdepth 1 -type b ! -name 'loop*' | grep -v '[0-9]$' | sed 's@.*/dev/@@' | head -1 )"
|
||||
[ $? -eq 0 ] && ok "Drive found: $DEV\n" || fail "No drive found\n"
|
||||
DEV=$(mount | grep '/dev/.* on /var type')
|
||||
[ $? -eq 0 ] && ok "Drive mounted: $DEV\n" || fail "No drive mounted\n"
|
||||
INET=$(ifconfig eth0 2> /dev/null | grep 'inet addr')
|
||||
[ $? -eq 0 ] && ok "Network connected: $INET\n" || fail "No network connection\n"
|
||||
if [ "$(mobyplatform)" = "mac" ]
|
||||
then
|
||||
FUSE=$(ps -eo args | grep '^/sbin/transfused')
|
||||
[ $? -eq 0 ] && ok "Process transfused running\n" || fail "No transfused process\n"
|
||||
fi
|
||||
if [ "$(mobyplatform)" = "windows" ]
|
||||
then
|
||||
TAPVS=$(ps -eo args | grep '^/sbin/tap-vsockd')
|
||||
[ $? -eq 0 ] && ok "Process tap-vsockd running\n" || fail "No tap-vsockd process\n"
|
||||
fi
|
||||
DOCKER=$(ps -eo args | grep '^dockerd')
|
||||
[ $? -eq 0 ] && ok "Process dockerd running: $DOCKER\n" || fail "No dockerd process\n"
|
||||
CONTAINERD=$(ps -eo args | grep '^docker-containerd')
|
||||
[ $? -eq 0 ] && ok "Process containerd running: $CONTAINERD\n" || fail "No containerd process\n"
|
||||
DOCKERPS=$(docker ps 2>&1)
|
||||
DOCKERDV=$(docker version --format '{{.Server.Version}}')
|
||||
[ $? -eq 0 ] && ok "Docker daemon working: ${DOCKERDV}\n" || fail "Docker ps failed: $DOCKERPS\n"
|
||||
DIAGNOSTICS=$(ps -eo args | grep '^/usr/bin/diagnostics-server')
|
||||
[ $? -eq 0 ] && ok "Diagnostics server running: $DIAGNOSTICS\n" || fail "No diagnostics server\n"
|
||||
CONTAINERD=$(ps -eo args | grep '^/usr/bin/containerd')
|
||||
[ $? -eq 0 ] && ok "System containerd server running: $CONTAINERD\n" || fail "No containerd server\n"
|
||||
CONTAINERPS=$(containerd-ctr containers 2>&1)
|
||||
[ $? -eq 0 ] && ok "System containerd working\n" || fail "containerd failed: $CONTAINERPS\n"
|
||||
|
||||
exit $EXIT_STATUS
|
@@ -1,25 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"syscall"
|
||||
|
||||
"github.com/rneugeba/virtsock/go/vsock"
|
||||
)
|
||||
|
||||
// VSockDiagnosticListener is a diagnostic server listening on VSock
|
||||
type VSockDiagnosticListener struct{}
|
||||
|
||||
// Listen for VSockDiagnosticListener listens on a VSock's port 62374
|
||||
func (l VSockDiagnosticListener) Listen() {
|
||||
vsock, err := vsock.Listen(uint(62374))
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); !ok || errno != syscall.EAFNOSUPPORT {
|
||||
log.Printf("Failed to bind to vsock port 62374: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
TarRespond(vsock)
|
||||
}
|
||||
}
|
1
alpine/packages/docker/.gitignore
vendored
1
alpine/packages/docker/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
usr/
|
@@ -1,69 +0,0 @@
|
||||
DOCKER_BIN_URL?=https://s3.amazonaws.com/editions-stage-us-east-1-150610-005505/linux/static/stable/x86_64/docker-17.03.0-ce.tgz
|
||||
DOCKER_VERSION?=17.03.0-ce
|
||||
ARCH?=x86_64
|
||||
OS?=Linux
|
||||
|
||||
AWS_CLI_IMAGE=mobylinux/aws-cli:9432b650794d38a70cf00be4da971367c52d1d5b@sha256:679f6f45fb8598cab90888733a07ddeeb26a27a7889114f89aaf712eaa3abe06
|
||||
|
||||
all: usr/bin/docker
|
||||
|
||||
RELEASE_CANDIDATE=$(findstring -rc,$(DOCKER_VERSION))
|
||||
ifeq ($(RELEASE_CANDIDATE),-rc)
|
||||
DOCKER_HOST=test.docker.com
|
||||
else
|
||||
DOCKER_HOST=get.docker.com
|
||||
endif
|
||||
|
||||
DOCKER_IMAGE?=docker:$(DOCKER_VERSION)
|
||||
|
||||
ifeq ($(DOCKER_VERSION),master)
|
||||
ifeq ($(OS),Linux)
|
||||
OS=linux
|
||||
endif
|
||||
ifeq ($(ARCH),x86_64)
|
||||
ARCH=amd64
|
||||
endif
|
||||
MASTER_VERSION:=$(shell curl -fsSL https://master.dockerproject.org/version)
|
||||
DOCKER_BIN_URL=https://master.dockerproject.org/$(OS)/$(ARCH)/docker-$(MASTER_VERSION).tgz
|
||||
endif
|
||||
|
||||
S3_PREFIX=s3://
|
||||
|
||||
usr/bin/docker: Makefile
|
||||
mkdir -p usr/bin
|
||||
rm -f usr/bin/* ok
|
||||
ifdef DOCKER_BIN_URL
|
||||
ifeq ($(filter $(S3_PREFIX)%, $(DOCKER_BIN_URL)),)
|
||||
(curl -fsSL "${DOCKER_BIN_URL}" && touch ok) | tar xzf -
|
||||
else
|
||||
(docker run --rm -e AWS_SECRET_ACCESS_KEY -e AWS_ACCESS_KEY_ID $(AWS_CLI_IMAGE) s3 cp '$(DOCKER_BIN_URL)' -) | tar xzf -
|
||||
endif
|
||||
else
|
||||
(curl -fsSL https://${DOCKER_HOST}/builds/${OS}/${ARCH}/docker-${DOCKER_VERSION}.tgz && touch ok) | tar xzf -
|
||||
endif
|
||||
rm ok
|
||||
if [ -d "./bundles" ]; then \
|
||||
mv bundles/${DOCKER_VERSION}/binary-daemon/docker-containerd-ctr \
|
||||
bundles/${DOCKER_VERSION}/binary-client/docker \
|
||||
bundles/${DOCKER_VERSION}/binary-daemon/docker-containerd \
|
||||
bundles/${DOCKER_VERSION}/binary-daemon/dockerd \
|
||||
bundles/${DOCKER_VERSION}/binary-daemon/docker-proxy \
|
||||
bundles/${DOCKER_VERSION}/binary-daemon/docker-runc \
|
||||
bundles/${DOCKER_VERSION}/binary-daemon/docker-containerd-shim \
|
||||
usr/bin/ ;\
|
||||
else \
|
||||
mv docker/docker-containerd-ctr \
|
||||
docker/docker \
|
||||
docker/docker-containerd \
|
||||
docker/dockerd \
|
||||
docker/docker-proxy \
|
||||
docker/docker-runc \
|
||||
docker/docker-containerd-shim \
|
||||
usr/bin/ ; fi
|
||||
([ -e docker/docker-init ] && mv docker/docker-init usr/bin/) || true
|
||||
([ -d docker/completion ] && rm -rf docker/completion) || true
|
||||
|
||||
clean:
|
||||
rm -rf usr/ docker/ ok
|
||||
|
||||
.DELETE_ON_ERROR:
|
@@ -1,143 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
depend()
|
||||
{
|
||||
after transfused
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
ebegin "Starting Docker"
|
||||
|
||||
# check if overriding Docker with a dev bundle
|
||||
if mobyconfig exists bundle
|
||||
then
|
||||
bundle=$(mobyconfig get bundle)
|
||||
[ -n "$bundle" -a -d "$bundle" -a -h "$bundle/binary-client/docker" ] && \
|
||||
export PATH=$bundle/binary-client:$bundle/binary-daemon:$PATH && \
|
||||
printf "Overriding Docker with provided bundle: $bundle\n"
|
||||
fi
|
||||
|
||||
command="${DOCKER_BINARY:-dockerd}"
|
||||
|
||||
pidfile="/run/docker.pid"
|
||||
|
||||
# mount /run shared eg for volume drivers
|
||||
mount --make-shared /run
|
||||
|
||||
# create cgroups mount for systemd containers
|
||||
if [ ! -d /sys/fs/cgroup/systemd ]
|
||||
then
|
||||
mkdir -p /sys/fs/cgroup/systemd
|
||||
mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd
|
||||
fi
|
||||
|
||||
# Only start with networking on cloud editions
|
||||
DOCKER_OPTS="${DOCKER_OPTS} -H unix:///var/run/docker.sock"
|
||||
|
||||
# On desktop editions, force swarm advertise address to be on eth0
|
||||
# Currently we do not support multi node swarm on these editions
|
||||
# for cloud plartforms the init scripts set this up
|
||||
case "$(mobyplatform)" in
|
||||
windows|mac)
|
||||
DOCKER_OPTS="${DOCKER_OPTS} --swarm-default-advertise-addr=eth0"
|
||||
;;
|
||||
esac
|
||||
|
||||
# some config is always in /etc/docker cannot specify alternative eg certs
|
||||
mkdir -p /etc/docker
|
||||
if mobyconfig exists etc/docker
|
||||
then
|
||||
for f in $(mobyconfig find etc/docker)
|
||||
do
|
||||
mkdir -p $(dirname $f)
|
||||
mobyconfig get $f > $f
|
||||
done
|
||||
fi
|
||||
|
||||
[ -f /etc/docker/daemon.json ] || printf '{}' > /etc/docker/daemon.json
|
||||
|
||||
if mobyconfig exists network
|
||||
then
|
||||
NETWORK_MODE="$(mobyconfig get network | tr -d '[[:space:]]')"
|
||||
NATIVE_PORT_FORWARDING="$(mobyconfig get native/port-forwarding | tr -d '[[:space:]]')"
|
||||
if [ "${NETWORK_MODE}" = "slirp" -o "${NATIVE_PORT_FORWARDING}" = "true" ]; then
|
||||
if dockerd --help | grep -q -- --userland-proxy-path; then
|
||||
DOCKER_OPTS="${DOCKER_OPTS} --userland-proxy-path /usr/bin/slirp-proxy"
|
||||
else
|
||||
cp /usr/bin/slirp-proxy /usr/bin/docker-proxy
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if mobyconfig exists proxy
|
||||
then
|
||||
export http_proxy="$(mobyconfig get proxy/http)"
|
||||
export https_proxy="$(mobyconfig get proxy/https)"
|
||||
export no_proxy="$(mobyconfig get proxy/exclude)"
|
||||
fi
|
||||
|
||||
# Set Docker to debug debug if not specified in daemon.json
|
||||
cat /etc/docker/daemon.json | jq -e 'has("debug")' > /dev/null || DOCKER_OPTS="${DOCKER_OPTS} --debug"
|
||||
|
||||
# On GCP, send logs to Google Cloud Logging
|
||||
# Disabled because of https://github.com/docker/docker/issues/29344
|
||||
# [ $(mobyplatform) = "gcp" ] && DOCKER_OPTS="${DOCKER_OPTS} --log-driver=gcplogs"
|
||||
|
||||
# choose storage driver
|
||||
if ! $(cat /etc/docker/daemon.json | jq -e '."storage-driver"' > /dev/null)
|
||||
then
|
||||
STORAGE_DRIVER="overlay2"
|
||||
[ -d /var/lib/docker/aufs ] && grep -q aufs /proc/filesystems && STORAGE_DRIVER="aufs"
|
||||
DOCKER_OPTS="${DOCKER_OPTS} --storage-driver ${STORAGE_DRIVER}"
|
||||
fi
|
||||
|
||||
# set ulimits as high as possible
|
||||
ulimit -n 1048576
|
||||
ulimit -p unlimited
|
||||
# set locked memory higher for varnish
|
||||
ulimit -l 82000
|
||||
|
||||
DOCKER_LOGFILE="/var/log/docker.log"
|
||||
|
||||
case "$(mobyplatform)" in
|
||||
windows|mac)
|
||||
# Allow iptables to be overriden
|
||||
export PATH=/usr/local/sbin:$PATH
|
||||
;;
|
||||
esac
|
||||
|
||||
start-stop-daemon --start --quiet \
|
||||
--background \
|
||||
--startas /bin/sh \
|
||||
--pidfile ${pidfile} \
|
||||
-- -c "exec ${command} --pidfile=${pidfile} ${DOCKER_OPTS} 2>&1 | logger -p local0.info"
|
||||
|
||||
ewaitfile 20 ${pidfile} /var/run/docker.sock /var/run/docker/libcontainerd/docker-containerd.sock
|
||||
|
||||
# On desktop forward metrics to host if enabled in daemon.json
|
||||
case "$(mobyplatform)" in
|
||||
windows|mac)
|
||||
METRICS_ADDR=$(cat /etc/docker/daemon.json | jq -e -r '."metrics-addr"')
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
METRICS_IP="$(echo "$METRICS_ADDR" | cut -d: -f1)"
|
||||
METRICS_PORT="$(echo "$METRICS_ADDR" | cut -d: -f2)"
|
||||
/usr/bin/slirp-proxy -proto tcp -host-ip 0.0.0.0 -host-port "$METRICS_PORT" -container-ip "$METRICS_IP" -container-port "$METRICS_PORT" -i -no-local-ip &
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
eend $? "Failed to start docker"
|
||||
}
|
||||
|
||||
stop()
|
||||
{
|
||||
# stop docker
|
||||
einfo "Stopping docker"
|
||||
pidfile="/run/docker.pid"
|
||||
start-stop-daemon --stop --quiet --pidfile ${pidfile} --retry 15
|
||||
|
||||
return 0
|
||||
}
|
@@ -1,22 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="Configuring settings from database."
|
||||
|
||||
depend() {
|
||||
# AWS gets settings via network
|
||||
need net
|
||||
before docker
|
||||
}
|
||||
|
||||
start() {
|
||||
ebegin "Configuring host settings from database"
|
||||
|
||||
mobyconfig exists random-seed && mobyconfig get random-seed > /dev/urandom
|
||||
|
||||
mobyconfig exists etc/resolv.conf && mobyconfig get etc/resolv.conf > /etc/resolv.conf
|
||||
mobyconfig exists etc/hosts && mobyconfig get etc/hosts >> /etc/hosts
|
||||
|
||||
mobyconfig exists etc/ssl/certs/ca-certificates.crt && mobyconfig get etc/ssl/certs/ca-certificates.crt >> /etc/ssl/certs/ca-certificates.crt
|
||||
|
||||
eend 0
|
||||
}
|
1
alpine/packages/iptables/.gitignore
vendored
1
alpine/packages/iptables/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
usr
|
@@ -1,13 +0,0 @@
|
||||
# This image is currently just tagged latest as non reproducible
|
||||
IPTABLES_IMAGE=mobylinux/pinata-iptables@sha256:c94aa6902e858712058d0550320564ecb03d1f8f6370f91cdbc3f18b87662411
|
||||
|
||||
all: usr/local/sbin/iptables
|
||||
|
||||
usr/local/sbin/iptables:
|
||||
mkdir -p $(dir $@)
|
||||
docker run --rm --net=none --log-driver=none $(IPTABLES_IMAGE) | tar xf - -C $(dir $@)
|
||||
|
||||
clean:
|
||||
rm -rf usr
|
||||
|
||||
.DELETE_ON_ERROR:
|
@@ -1,29 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="Set up database (AWS)."
|
||||
|
||||
depend() {
|
||||
before docker hostsettings windowsnet
|
||||
}
|
||||
|
||||
start() {
|
||||
[ "$(mobyplatform)" = "aws" ] || exit 0
|
||||
|
||||
ebegin "Setting up database (AWS)"
|
||||
|
||||
mkdir -p /Database
|
||||
mount -t tmpfs tmpfs /Database
|
||||
|
||||
ENDPOINT="http://169.254.169.254/latest/meta-data"
|
||||
curl "${ENDPOINT}/local-hostname" 2> /dev/null | mobyconfig set etc/hostname
|
||||
# as this is a bit late let us set it anyway
|
||||
mobyconfig get etc/hostname > /etc/hostname
|
||||
hostname -F /etc/hostname
|
||||
# TODO should collect all keys
|
||||
curl -f -I "${ENDPOINT}/public-keys/0/openssh-key" 2> /dev/null > /dev/null && \
|
||||
curl "${ENDPOINT}/public-keys/0/openssh-key" 2> /dev/null | mobyconfig set etc/ssh/authorized_keys
|
||||
USERDATA="http://169.254.169.254/latest/user-data/"
|
||||
curl "${USERDATA}" 2> /dev/null | mobyconfig set userdata
|
||||
|
||||
eend 0
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="Set up database (GCP)."
|
||||
|
||||
depend() {
|
||||
before docker hostsettings windowsnet
|
||||
}
|
||||
|
||||
start() {
|
||||
[ "$(mobyplatform)" = "gcp" ] || exit 0
|
||||
|
||||
ebegin "Setting up database (GCP)"
|
||||
|
||||
mkdir -p /Database
|
||||
mount -t tmpfs tmpfs /Database
|
||||
|
||||
PROJECT=http://metadata.google.internal/computeMetadata/v1/project
|
||||
INSTANCE=http://metadata.google.internal/computeMetadata/v1/instance
|
||||
curl "${INSTANCE}/hostname" 2> /dev/null | mobyconfig set etc/hostname
|
||||
# as this is a bit late let us set it anyway
|
||||
mobyconfig get etc/hostname > /etc/hostname
|
||||
hostname -F /etc/hostname
|
||||
curl -f -I "${PROJECT}/attributes/sshKeys" 2> /dev/null > /dev/null && \
|
||||
curl "${PROJECT}/attributes/sshKeys" 2> /dev/null | mobyconfig set etc/ssh/authorized_keys
|
||||
|
||||
eend 0
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="Set up database (OSX)."
|
||||
|
||||
depend() {
|
||||
before docker hostsettings windowsnet
|
||||
}
|
||||
|
||||
start() {
|
||||
[ "$(mobyplatform)" = "mac" ] || exit 0
|
||||
|
||||
ebegin "Setting up database (OSX)"
|
||||
|
||||
mkdir -p /Database
|
||||
mount -t tmpfs tmpfs /Database
|
||||
mkdir -p /mnt/db
|
||||
mount -t 9p -o trans=virtio,dfltuid=1001,dfltgid=50,version=9p2000 db /mnt/db
|
||||
[ $? -ne 0 ] && rm -rf /Database && printf "Could not mount configuration database\n" 1>&2 && eend 1
|
||||
cp -a /mnt/db/branch/master/ro/com.docker.driver.amd64-linux/* /Database
|
||||
umount /mnt/db
|
||||
rmdir /mnt/db
|
||||
|
||||
eend 0
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="Set up database (Windows)."
|
||||
|
||||
depend() {
|
||||
before docker hostsettings windowsnet
|
||||
}
|
||||
|
||||
start() {
|
||||
[ "$(mobyplatform)" = "windows" ] || exit 0
|
||||
|
||||
ebegin "Setting up database (Windows)"
|
||||
|
||||
mkdir -p /Database
|
||||
mount -t tmpfs tmpfs /Database
|
||||
mkdir -p /tmp/db
|
||||
/sbin/9pmount-vsock listen db /tmp/db
|
||||
[ $? -ne 0 ] && rm -rf /Database && printf "Could not mount configuration database\n" 1>&2 && eend 1
|
||||
cp -a /tmp/db/branch/master/ro/com.docker.driver.amd64-linux/* /Database
|
||||
umount /tmp/db
|
||||
|
||||
eend 0
|
||||
}
|
@@ -1,80 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ $# -ne 2 ]
|
||||
then
|
||||
printf "usage: $0 [get|set|wait|exists|path|dir|find] key\n"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
BASE=/Database
|
||||
|
||||
KEY=$(echo $2 | sed 's@^/@@')
|
||||
|
||||
if [ $1 == "exists" ]
|
||||
then
|
||||
[ -d ${BASE}/${KEY} ] && exit 0
|
||||
[ -f ${BASE}/${KEY} ] || exit 1
|
||||
VALUE="$(cat ${BASE}/${KEY} | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
||||
[ -z "${VALUE}" ] && exit 1
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ $1 == "dir" ]
|
||||
then
|
||||
[ -d ${BASE}/${KEY} ] && exit 0 || exit 1
|
||||
fi
|
||||
|
||||
if [ $1 == "get" ]
|
||||
then
|
||||
if [ -f ${BASE}/${KEY} ]
|
||||
then
|
||||
cat ${BASE}/${KEY}
|
||||
exit 0
|
||||
else
|
||||
printf "No such key: ${KEY}\n" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $1 == "set" ]
|
||||
then
|
||||
mkdir -p $(dirname "${BASE}/${KEY}")
|
||||
cat - > ${BASE}/${KEY}
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ $1 == "wait" ]
|
||||
then
|
||||
while [ ! -s ${BASE}/${KEY} ]
|
||||
do
|
||||
sleep 1
|
||||
done
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ $1 == "path" ]
|
||||
then
|
||||
if [ -e ${BASE}/${KEY} ]
|
||||
then
|
||||
printf ${BASE}/${KEY}
|
||||
exit 0
|
||||
else
|
||||
printf "No such key: ${KEY}\n" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $1 == "find" ]
|
||||
then
|
||||
if [ -e ${BASE}/${KEY} ]
|
||||
then
|
||||
find ${BASE}/${KEY} -type f | sed "s@^${BASE}@@g"
|
||||
exit 0
|
||||
else
|
||||
printf "No such key: ${KEY}\n" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
printf "usage: $0 [get|set|wait|exists|path|dir|find] key\n"
|
||||
exit 1
|
@@ -1,21 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Returns the platform we are running on
|
||||
|
||||
# return the value matching the kvpair in the command line
|
||||
kvpair()
|
||||
{
|
||||
awk -v flag=$1 '{
|
||||
for(i = 1; i <= NF; i++) {
|
||||
split($i, kvpair, "=");
|
||||
if(kvpair[1] == flag) {
|
||||
printf kvpair[2];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}' /proc/cmdline
|
||||
}
|
||||
|
||||
PLATFORM=$(kvpair mobyplatform)
|
||||
|
||||
[ -z ${PLATFORM} ] && printf unknown || printf ${PLATFORM}
|
1
alpine/packages/nc-vsock/.gitignore
vendored
1
alpine/packages/nc-vsock/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
usr/
|
@@ -1,20 +0,0 @@
|
||||
From https://github.com/stefanha/linux vsock-extras
|
||||
f442d29db40a1999d139dccf110f3b590a0ed5b8:nc-vsock.c
|
||||
|
||||
including include/uapi/linux/vm_sockets.h, patched with:
|
||||
|
||||
--- /home/ijc/x 2016-03-17 09:31:23.288944691 +0000
|
||||
+++ alpine/packages/nc-vsock/include/uapi/linux/vm_sockets.h 2016-03-17 09:30:07.916069307 +0000
|
||||
@@ -16,7 +16,12 @@
|
||||
#ifndef _UAPI_VM_SOCKETS_H
|
||||
#define _UAPI_VM_SOCKETS_H
|
||||
|
||||
+#ifdef __KERNEL__
|
||||
#include <linux/socket.h>
|
||||
+#else
|
||||
+#define __kernel_sa_family_t sa_family_t
|
||||
+#include <sys/socket.h>
|
||||
+#endif
|
||||
|
||||
/* Option name for STREAM socket buffer size. Use as the option name in
|
||||
* setsockopt(3) or getsockopt(3) to set or get an unsigned long long that
|
@@ -1,12 +0,0 @@
|
||||
C_COMPILE=mobylinux/c-compile:ac075fed7c87e4af30d8490ae0504166cceb0df3@sha256:0e82d441ce112d638f904a08199c76b022c065a2dbf8908bb366755267d4417f
|
||||
|
||||
default: usr/bin/nc-vsock
|
||||
|
||||
DEPS=$(wildcard *.c *.h)
|
||||
|
||||
usr/bin/nc-vsock: $(DEPS)
|
||||
mkdir -p $(dir $@)
|
||||
tar cf - $(DEPS) | docker run --rm --net=none --log-driver=none -i $(C_COMPILE) -o $@ -luuid | tar xf -
|
||||
|
||||
clean:
|
||||
rm -rf usr
|
@@ -1,491 +0,0 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <netdb.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include "vm_sockets.h"
|
||||
|
||||
#define MODE_READ 1 /* From the vsock */
|
||||
#define MODE_WRITE 2 /* To the vsock */
|
||||
#define MODE_RDWR (MODE_READ|MODE_WRITE)
|
||||
|
||||
/*
|
||||
* Hyper-V Sockets headerfile pull in too much other stuff. Replicate
|
||||
* the bits we need here.
|
||||
*/
|
||||
#ifndef AF_HYPERV
|
||||
#define AF_HYPERV 42
|
||||
#endif
|
||||
|
||||
struct sockaddr_hv {
|
||||
unsigned short shv_family; /* Address family */
|
||||
unsigned short reserved; /* Must be Zero */
|
||||
uuid_t shv_vm_id; /* Not used. Must be Zero. */
|
||||
uuid_t shv_service_id; /* Service ID */
|
||||
};
|
||||
UUID_DEFINE(SHV_VMID_GUEST,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
||||
#define SHV_PROTO_RAW 1
|
||||
|
||||
/*
|
||||
* MSFT's GUIDs are a bonkers mix of native and big endian byte
|
||||
* order. The uuid library uses RFC 4122, which is always big endian.
|
||||
* The Linux kernel uuid.h actually looks more like it should be
|
||||
* called guid.h. We use the uuid library for ease of parsing/printing
|
||||
* and then this function to convert between UUID and GUID.
|
||||
* https://en.wikipedia.org/wiki/Globally_unique_identifier
|
||||
*/
|
||||
static void uuid2guid(uuid_t u)
|
||||
{
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
char t;
|
||||
t = u[0]; u[0] = u[3]; u[3] = t;
|
||||
t = u[1]; u[1] = u[2]; u[2] = t;
|
||||
|
||||
t = u[4]; u[4] = u[5]; u[5] = t;
|
||||
|
||||
t = u[6]; u[6] = u[7]; u[7] = t;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int parse_cid(const char *cid_str)
|
||||
{
|
||||
char *end = NULL;
|
||||
long cid = strtol(cid_str, &end, 10);
|
||||
|
||||
if (cid_str != end && *end == '\0') {
|
||||
return cid;
|
||||
} else {
|
||||
fprintf(stderr, "invalid cid: %s\n", cid_str);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_port(const char *port_str)
|
||||
{
|
||||
char *end = NULL;
|
||||
long port = strtol(port_str, &end, 10);
|
||||
|
||||
if (port_str != end && *end == '\0') {
|
||||
return port;
|
||||
} else {
|
||||
fprintf(stderr, "invalid port number: %s\n", port_str);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int vsock_listen(const char *port_str)
|
||||
{
|
||||
int listen_fd;
|
||||
int client_fd;
|
||||
struct sockaddr_vm sa_listen = {
|
||||
.svm_family = AF_VSOCK,
|
||||
.svm_cid = VMADDR_CID_ANY,
|
||||
};
|
||||
struct sockaddr_vm sa_client;
|
||||
socklen_t socklen_client = sizeof(sa_client);
|
||||
int port;
|
||||
int ret;
|
||||
|
||||
port = parse_port(port_str);
|
||||
if (port < 0)
|
||||
return -1;
|
||||
|
||||
sa_listen.svm_port = port;
|
||||
|
||||
listen_fd = socket(AF_VSOCK, SOCK_STREAM, 0);
|
||||
if (listen_fd < 0) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = bind(listen_fd, (struct sockaddr*)&sa_listen, sizeof(sa_listen));
|
||||
if (ret != 0) {
|
||||
perror("bind");
|
||||
close(listen_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = listen(listen_fd, 1);
|
||||
if (ret != 0) {
|
||||
perror("listen");
|
||||
close(listen_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
client_fd = accept(listen_fd,
|
||||
(struct sockaddr*)&sa_client, &socklen_client);
|
||||
if (client_fd < 0) {
|
||||
perror("accept");
|
||||
close(listen_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Connection from cid %u port %u...\n",
|
||||
sa_client.svm_cid, sa_client.svm_port);
|
||||
|
||||
close(listen_fd);
|
||||
return client_fd;
|
||||
}
|
||||
|
||||
static int hvsock_listen(const char *port_str)
|
||||
{
|
||||
int listen_fd;
|
||||
int client_fd;
|
||||
struct sockaddr_hv sa_listen = {
|
||||
.shv_family = AF_HYPERV,
|
||||
.reserved = 0,
|
||||
};
|
||||
struct sockaddr_hv sa_client;
|
||||
socklen_t socklen_client = sizeof(sa_client);
|
||||
char vm_str[128], svc_str[128];
|
||||
int ret;
|
||||
|
||||
uuid_copy(sa_listen.shv_vm_id, SHV_VMID_GUEST);
|
||||
|
||||
ret = uuid_parse(port_str, sa_listen.shv_service_id);
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
|
||||
uuid2guid(sa_listen.shv_service_id);
|
||||
|
||||
listen_fd = socket(AF_HYPERV, SOCK_STREAM, SHV_PROTO_RAW);
|
||||
if (listen_fd < 0) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = bind(listen_fd, (struct sockaddr*)&sa_listen, sizeof(sa_listen));
|
||||
if (ret != 0) {
|
||||
perror("bind");
|
||||
close(listen_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = listen(listen_fd, 1);
|
||||
if (ret != 0) {
|
||||
perror("listen");
|
||||
close(listen_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
client_fd = accept(listen_fd,
|
||||
(struct sockaddr*)&sa_client, &socklen_client);
|
||||
if (client_fd < 0) {
|
||||
perror("accept");
|
||||
close(listen_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uuid_unparse(sa_client.shv_vm_id, vm_str);
|
||||
uuid_unparse(sa_client.shv_service_id, svc_str);
|
||||
fprintf(stderr, "Connection from %s port %s...\n", vm_str, svc_str);
|
||||
|
||||
close(listen_fd);
|
||||
return client_fd;
|
||||
}
|
||||
|
||||
static int tcp_connect(const char *node, const char *service)
|
||||
{
|
||||
int fd = -1;
|
||||
int ret;
|
||||
const struct addrinfo hints = {
|
||||
.ai_family = AF_INET,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
};
|
||||
struct addrinfo *res = NULL;
|
||||
struct addrinfo *addrinfo;
|
||||
|
||||
ret = getaddrinfo(node, service, &hints, &res);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (addrinfo = res; addrinfo; addrinfo = addrinfo->ai_next) {
|
||||
fd = socket(addrinfo->ai_family,
|
||||
addrinfo->ai_socktype, addrinfo->ai_protocol);
|
||||
if (fd < 0) {
|
||||
perror("socket");
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen);
|
||||
if (ret != 0) {
|
||||
perror("connect");
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int vsock_connect(const char *cid_str, const char *port_str)
|
||||
{
|
||||
int fd;
|
||||
int cid;
|
||||
int port;
|
||||
struct sockaddr_vm sa = {
|
||||
.svm_family = AF_VSOCK,
|
||||
};
|
||||
int ret;
|
||||
|
||||
cid = parse_cid(cid_str);
|
||||
if (cid < 0)
|
||||
return -1;
|
||||
|
||||
sa.svm_cid = cid;
|
||||
|
||||
port = parse_port(port_str);
|
||||
if (port < 0)
|
||||
return -1;
|
||||
|
||||
sa.svm_port = port;
|
||||
|
||||
fd = socket(AF_VSOCK, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = connect(fd, (struct sockaddr*)&sa, sizeof(sa));
|
||||
if (ret != 0) {
|
||||
perror("connect");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int hvsock_connect(const char *vm_str, const char *svc_str)
|
||||
{
|
||||
int fd;
|
||||
int ret;
|
||||
struct sockaddr_hv sa = {
|
||||
.shv_family = AF_HYPERV,
|
||||
.reserved = 0,
|
||||
};
|
||||
|
||||
ret = uuid_parse(vm_str, sa.shv_vm_id);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "VM GUID parse error: %s\n", vm_str);
|
||||
return -1;
|
||||
}
|
||||
uuid2guid(sa.shv_vm_id);
|
||||
|
||||
ret = uuid_parse(svc_str, sa.shv_service_id);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Service GUID parse error: %s\n", svc_str);
|
||||
return -1;
|
||||
}
|
||||
uuid2guid(sa.shv_service_id);
|
||||
|
||||
fd = socket(AF_HYPERV, SOCK_STREAM, SHV_PROTO_RAW);
|
||||
if (fd < 0) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = connect(fd, (struct sockaddr*)&sa, sizeof(sa));
|
||||
if (ret != 0) {
|
||||
perror("connect");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int get_fds(int argc, char **argv, int fds[2])
|
||||
{
|
||||
fds[0] = STDIN_FILENO;
|
||||
fds[1] = -1;
|
||||
|
||||
if (argc >= 3 && strcmp(argv[1], "-l") == 0) {
|
||||
if (strstr(argv[2], "-"))
|
||||
fds[1] = hvsock_listen(argv[2]);
|
||||
else
|
||||
fds[1] = vsock_listen(argv[2]);
|
||||
if (fds[1] < 0)
|
||||
return -1;
|
||||
|
||||
if (argc == 6 && strcmp(argv[3], "-t") == 0) {
|
||||
fds[0] = tcp_connect(argv[4], argv[5]);
|
||||
if (fds[0] < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
} else if (argc == 3) {
|
||||
if (strstr(argv[1], "-") || strstr(argv[2], "-"))
|
||||
fds[1] = hvsock_connect(argv[1], argv[2]);
|
||||
else
|
||||
fds[1] = vsock_connect(argv[1], argv[2]);
|
||||
if (fds[1] < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "usage: %s [-r|-w] [-l <port> [-t <dst> <dstport>] | <cid> <port>]\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_nonblock(int fd, bool enable)
|
||||
{
|
||||
int ret;
|
||||
int flags;
|
||||
|
||||
ret = fcntl(fd, F_GETFL);
|
||||
if (ret < 0) {
|
||||
perror("fcntl");
|
||||
return;
|
||||
}
|
||||
|
||||
flags = ret & ~O_NONBLOCK;
|
||||
if (enable)
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
fcntl(fd, F_SETFL, flags);
|
||||
}
|
||||
|
||||
static int xfer_data(int in_fd, int out_fd)
|
||||
{
|
||||
char buf[256*1024];
|
||||
char *send_ptr = buf;
|
||||
ssize_t nbytes;
|
||||
ssize_t remaining;
|
||||
int ret;
|
||||
|
||||
if (out_fd == STDIN_FILENO) out_fd = STDOUT_FILENO;
|
||||
|
||||
nbytes = read(in_fd, buf, sizeof(buf));
|
||||
if (nbytes < 0)
|
||||
return -1;
|
||||
|
||||
if (nbytes == 0) {
|
||||
if (out_fd == STDOUT_FILENO)
|
||||
return 0;
|
||||
ret = shutdown(out_fd, SHUT_WR);
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
perror("shutdown");
|
||||
return -1;
|
||||
}
|
||||
|
||||
remaining = nbytes;
|
||||
while (remaining > 0) {
|
||||
nbytes = write(out_fd, send_ptr, remaining);
|
||||
if (nbytes < 0 && errno == EAGAIN)
|
||||
nbytes = 0;
|
||||
else if (nbytes <= 0)
|
||||
return -1;
|
||||
|
||||
if (remaining > nbytes) {
|
||||
/* Wait for fd to become writeable again */
|
||||
for (;;) {
|
||||
fd_set wfds;
|
||||
FD_ZERO(&wfds);
|
||||
FD_SET(out_fd, &wfds);
|
||||
ret = select(out_fd + 1, NULL,
|
||||
&wfds, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
perror("select");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(out_fd, &wfds))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
send_ptr += nbytes;
|
||||
remaining -= nbytes;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void main_loop(int fds[2], int mode)
|
||||
{
|
||||
fd_set rfds;
|
||||
int nfds = fds[fds[0] > fds[1] ? 0 : 1] + 1;
|
||||
/* Which fd's are readable */
|
||||
bool rfd0 = !!(mode&MODE_WRITE), rfd1 = !!(mode&MODE_READ);
|
||||
int ret;
|
||||
|
||||
set_nonblock(fds[0], true);
|
||||
set_nonblock(fds[1], true);
|
||||
|
||||
for (;;) {
|
||||
if (!rfd0 && !rfd1)
|
||||
return;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
if (rfd0)
|
||||
FD_SET(fds[0], &rfds);
|
||||
if (rfd1)
|
||||
FD_SET(fds[1], &rfds);
|
||||
|
||||
ret = select(nfds, &rfds, NULL, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
perror("select");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (rfd0 && FD_ISSET(fds[0], &rfds)) {
|
||||
switch (xfer_data(fds[0], fds[1])) {
|
||||
case -1: return;
|
||||
case 0: rfd0 = false; break;
|
||||
case 1: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rfd1 && FD_ISSET(fds[1], &rfds)) {
|
||||
switch (xfer_data(fds[1], fds[0])) {
|
||||
case -1: return;
|
||||
case 0: rfd1 = false; break;
|
||||
case 1: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int mode = MODE_RDWR;
|
||||
int fds[2];
|
||||
|
||||
if (argc >= 2) {
|
||||
if (!strcmp(argv[1], "-r")) {
|
||||
mode = MODE_READ;
|
||||
argv++; argc--;
|
||||
} else if (!strcmp(argv[1], "-w")) {
|
||||
mode = MODE_WRITE;
|
||||
argv++; argc--;
|
||||
}
|
||||
}
|
||||
|
||||
if (get_fds(argc, argv, fds) < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
main_loop(fds, mode);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* VMware vSockets Driver
|
||||
*
|
||||
* Copyright (C) 2007-2013 VMware, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation version 2 and no later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_VM_SOCKETS_H
|
||||
#define _UAPI_VM_SOCKETS_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/socket.h>
|
||||
#else
|
||||
#define __kernel_sa_family_t sa_family_t
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
/* Option name for STREAM socket buffer size. Use as the option name in
|
||||
* setsockopt(3) or getsockopt(3) to set or get an unsigned long long that
|
||||
* specifies the size of the buffer underlying a vSockets STREAM socket.
|
||||
* Value is clamped to the MIN and MAX.
|
||||
*/
|
||||
|
||||
#define SO_VM_SOCKETS_BUFFER_SIZE 0
|
||||
|
||||
/* Option name for STREAM socket minimum buffer size. Use as the option name
|
||||
* in setsockopt(3) or getsockopt(3) to set or get an unsigned long long that
|
||||
* specifies the minimum size allowed for the buffer underlying a vSockets
|
||||
* STREAM socket.
|
||||
*/
|
||||
|
||||
#define SO_VM_SOCKETS_BUFFER_MIN_SIZE 1
|
||||
|
||||
/* Option name for STREAM socket maximum buffer size. Use as the option name
|
||||
* in setsockopt(3) or getsockopt(3) to set or get an unsigned long long
|
||||
* that specifies the maximum size allowed for the buffer underlying a
|
||||
* vSockets STREAM socket.
|
||||
*/
|
||||
|
||||
#define SO_VM_SOCKETS_BUFFER_MAX_SIZE 2
|
||||
|
||||
/* Option name for socket peer's host-specific VM ID. Use as the option name
|
||||
* in getsockopt(3) to get a host-specific identifier for the peer endpoint's
|
||||
* VM. The identifier is a signed integer.
|
||||
* Only available for hypervisor endpoints.
|
||||
*/
|
||||
|
||||
#define SO_VM_SOCKETS_PEER_HOST_VM_ID 3
|
||||
|
||||
/* Option name for determining if a socket is trusted. Use as the option name
|
||||
* in getsockopt(3) to determine if a socket is trusted. The value is a
|
||||
* signed integer.
|
||||
*/
|
||||
|
||||
#define SO_VM_SOCKETS_TRUSTED 5
|
||||
|
||||
/* Option name for STREAM socket connection timeout. Use as the option name
|
||||
* in setsockopt(3) or getsockopt(3) to set or get the connection
|
||||
* timeout for a STREAM socket.
|
||||
*/
|
||||
|
||||
#define SO_VM_SOCKETS_CONNECT_TIMEOUT 6
|
||||
|
||||
/* Option name for using non-blocking send/receive. Use as the option name
|
||||
* for setsockopt(3) or getsockopt(3) to set or get the non-blocking
|
||||
* transmit/receive flag for a STREAM socket. This flag determines whether
|
||||
* send() and recv() can be called in non-blocking contexts for the given
|
||||
* socket. The value is a signed integer.
|
||||
*
|
||||
* This option is only relevant to kernel endpoints, where descheduling the
|
||||
* thread of execution is not allowed, for example, while holding a spinlock.
|
||||
* It is not to be confused with conventional non-blocking socket operations.
|
||||
*
|
||||
* Only available for hypervisor endpoints.
|
||||
*/
|
||||
|
||||
#define SO_VM_SOCKETS_NONBLOCK_TXRX 7
|
||||
|
||||
/* The vSocket equivalent of INADDR_ANY. This works for the svm_cid field of
|
||||
* sockaddr_vm and indicates the context ID of the current endpoint.
|
||||
*/
|
||||
|
||||
#define VMADDR_CID_ANY -1U
|
||||
|
||||
/* Bind to any available port. Works for the svm_port field of
|
||||
* sockaddr_vm.
|
||||
*/
|
||||
|
||||
#define VMADDR_PORT_ANY -1U
|
||||
|
||||
/* Use this as the destination CID in an address when referring to the
|
||||
* hypervisor. VMCI relies on it being 0, but this would be useful for other
|
||||
* transports too.
|
||||
*/
|
||||
|
||||
#define VMADDR_CID_HYPERVISOR 0
|
||||
|
||||
/* This CID is specific to VMCI and can be considered reserved (even VMCI
|
||||
* doesn't use it anymore, it's a legacy value from an older release).
|
||||
*/
|
||||
|
||||
#define VMADDR_CID_RESERVED 1
|
||||
|
||||
/* Use this as the destination CID in an address when referring to the host
|
||||
* (any process other than the hypervisor). VMCI relies on it being 2, but
|
||||
* this would be useful for other transports too.
|
||||
*/
|
||||
|
||||
#define VMADDR_CID_HOST 2
|
||||
|
||||
/* Invalid vSockets version. */
|
||||
|
||||
#define VM_SOCKETS_INVALID_VERSION -1U
|
||||
|
||||
/* The epoch (first) component of the vSockets version. A single byte
|
||||
* representing the epoch component of the vSockets version.
|
||||
*/
|
||||
|
||||
#define VM_SOCKETS_VERSION_EPOCH(_v) (((_v) & 0xFF000000) >> 24)
|
||||
|
||||
/* The major (second) component of the vSockets version. A single byte
|
||||
* representing the major component of the vSockets version. Typically
|
||||
* changes for every major release of a product.
|
||||
*/
|
||||
|
||||
#define VM_SOCKETS_VERSION_MAJOR(_v) (((_v) & 0x00FF0000) >> 16)
|
||||
|
||||
/* The minor (third) component of the vSockets version. Two bytes representing
|
||||
* the minor component of the vSockets version.
|
||||
*/
|
||||
|
||||
#define VM_SOCKETS_VERSION_MINOR(_v) (((_v) & 0x0000FFFF))
|
||||
|
||||
/* Address structure for vSockets. The address family should be set to
|
||||
* AF_VSOCK. The structure members should all align on their natural
|
||||
* boundaries without resorting to compiler packing directives. The total size
|
||||
* of this structure should be exactly the same as that of struct sockaddr.
|
||||
*/
|
||||
|
||||
struct sockaddr_vm {
|
||||
__kernel_sa_family_t svm_family;
|
||||
unsigned short svm_reserved1;
|
||||
unsigned int svm_port;
|
||||
unsigned int svm_cid;
|
||||
unsigned char svm_zero[sizeof(struct sockaddr) -
|
||||
sizeof(sa_family_t) -
|
||||
sizeof(unsigned short) -
|
||||
sizeof(unsigned int) - sizeof(unsigned int)];
|
||||
};
|
||||
|
||||
#define IOCTL_VM_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9)
|
||||
|
||||
#endif /* _UAPI_VM_SOCKETS_H */
|
@@ -1,21 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="oom killer settings"
|
||||
|
||||
depend()
|
||||
{
|
||||
after docker
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
ebegin "Adjusting oom killer settings"
|
||||
|
||||
for f in $(find /run -name '*.pid')
|
||||
do
|
||||
PID=$(cat $f)
|
||||
[ -f /proc/$PID/oom_score_adj ] && echo "-800" > /proc/$PID/oom_score_adj
|
||||
done
|
||||
|
||||
eend $? "Failed to adjust oom settings"
|
||||
}
|
3
alpine/packages/proxy/.gitignore
vendored
3
alpine/packages/proxy/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
proxy
|
||||
usr/
|
||||
sbin/
|
@@ -1,202 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
@@ -1,21 +0,0 @@
|
||||
GO_COMPILE=mobylinux/go-compile:d2d25ac665b5148ad356d0eab3ff3762a68c633d@sha256:aab55d0c317460850e66a07dd94139cc11ea9e1c0bee88716a6a8c768740885f
|
||||
|
||||
all: usr/bin/slirp-proxy sbin/proxy-vsockd
|
||||
|
||||
DEPS=$(wildcard *.go libproxy/*.go)
|
||||
|
||||
proxy: $(DEPS) ../vendor/manifest
|
||||
tar cf - $(DEPS) -C .. vendor | docker run --rm --net=none --log-driver=none -i $(GO_COMPILE) -o $@ | tar xf -
|
||||
|
||||
usr/bin/slirp-proxy: proxy
|
||||
mkdir -p usr/bin
|
||||
cp proxy $@
|
||||
|
||||
sbin/proxy-vsockd: proxy
|
||||
mkdir -p sbin
|
||||
cp proxy $@
|
||||
|
||||
clean:
|
||||
rm -rf proxy sbin usr
|
||||
|
||||
.DELETE_ON_ERROR:
|
@@ -1,11 +0,0 @@
|
||||
docker-proxy which can set up tunnels into the VM
|
||||
=================================================
|
||||
|
||||
This is a replacement for the built-in `docker-proxy` command, which
|
||||
proxies data from external ports to internal container ports.
|
||||
|
||||
This program uses the 9P filesystem under /port to extend the port
|
||||
forward from the host running the Moby VM all the way to the container.
|
||||
|
||||
docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 8080 -container-ip 172.17.0.2 -container-port 8080
|
||||
|
@@ -1,46 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
depend()
|
||||
{
|
||||
before docker
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
[ "$(mobyplatform)" != "mac" ] && [ "$(mobyplatform)" != "windows" ] && exit 0
|
||||
|
||||
ebegin "Setting up proxy port service"
|
||||
|
||||
mkdir -p /port
|
||||
if [ "$(mobyplatform)" = "windows" ]; then
|
||||
# Running on a Hyper-V hypervisor
|
||||
/sbin/9pmount-vsock --serviceid 0B95756A-9985-48AD-9470-78E060895BE7 listen port /port
|
||||
else
|
||||
mount -t 9p -o trans=virtio,dfltuid=1001,dfltgid=50,version=9p2000 port /port
|
||||
fi
|
||||
[ -n "${PIDFILE}" ] || PIDFILE=/var/run/proxy-vsockd.pid
|
||||
[ -n "${LOGFILE}" ] || LOGFILE=/var/log/proxy-vsockd.log
|
||||
|
||||
ulimit -n 1048576
|
||||
start-stop-daemon --start --quiet \
|
||||
--background \
|
||||
--exec /sbin/proxy-vsockd \
|
||||
--make-pidfile --pidfile ${PIDFILE} \
|
||||
--stderr "${LOGFILE}" --stdout "${LOGFILE}" \
|
||||
-- -vsockPort 62373 -hvGuid 0B95756A-9985-48AD-9470-78E060895BE7
|
||||
|
||||
eend $? "Failed to start proxy port service"
|
||||
}
|
||||
|
||||
stop()
|
||||
{
|
||||
[ "$(mobyplatform)" != "mac" ] && [ "$(mobyplatform)" != "windows" ] && exit 0
|
||||
|
||||
ebegin "Stopping proxy port service"
|
||||
|
||||
[ -n "${PIDFILE}" ] || PIDFILE=/var/run/proxy-vsockd.pid
|
||||
|
||||
start-stop-daemon --stop --quiet --pidfile ${PIDFILE}
|
||||
|
||||
eend $? "Failed to stop proxy-vsockd"
|
||||
}
|
@@ -1,216 +0,0 @@
|
||||
package libproxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var testBuf = []byte("Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo")
|
||||
var testBufSize = len(testBuf)
|
||||
|
||||
type EchoServer interface {
|
||||
Run()
|
||||
Close()
|
||||
LocalAddr() net.Addr
|
||||
}
|
||||
|
||||
type TCPEchoServer struct {
|
||||
listener net.Listener
|
||||
testCtx *testing.T
|
||||
}
|
||||
|
||||
type UDPEchoServer struct {
|
||||
conn net.PacketConn
|
||||
testCtx *testing.T
|
||||
}
|
||||
|
||||
func NewEchoServer(t *testing.T, proto, address string) EchoServer {
|
||||
var server EchoServer
|
||||
if strings.HasPrefix(proto, "tcp") {
|
||||
listener, err := net.Listen(proto, address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
server = &TCPEchoServer{listener: listener, testCtx: t}
|
||||
} else {
|
||||
socket, err := net.ListenPacket(proto, address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
server = &UDPEchoServer{conn: socket, testCtx: t}
|
||||
}
|
||||
return server
|
||||
}
|
||||
|
||||
func (server *TCPEchoServer) Run() {
|
||||
go func() {
|
||||
for {
|
||||
client, err := server.listener.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go func(client net.Conn) {
|
||||
if _, err := io.Copy(client, client); err != nil {
|
||||
server.testCtx.Logf("can't echo to the client: %v\n", err.Error())
|
||||
}
|
||||
client.Close()
|
||||
}(client)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (server *TCPEchoServer) LocalAddr() net.Addr { return server.listener.Addr() }
|
||||
func (server *TCPEchoServer) Close() { server.listener.Addr() }
|
||||
|
||||
func (server *UDPEchoServer) Run() {
|
||||
go func() {
|
||||
readBuf := make([]byte, 1024)
|
||||
for {
|
||||
read, from, err := server.conn.ReadFrom(readBuf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for i := 0; i != read; {
|
||||
written, err := server.conn.WriteTo(readBuf[i:read], from)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
i += written
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (server *UDPEchoServer) LocalAddr() net.Addr { return server.conn.LocalAddr() }
|
||||
func (server *UDPEchoServer) Close() { server.conn.Close() }
|
||||
|
||||
func testProxyAt(t *testing.T, proto string, proxy Proxy, addr string) {
|
||||
defer proxy.Close()
|
||||
go proxy.Run()
|
||||
client, err := net.Dial(proto, addr)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't connect to the proxy: %v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
client.SetDeadline(time.Now().Add(10 * time.Second))
|
||||
if _, err = client.Write(testBuf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
recvBuf := make([]byte, testBufSize)
|
||||
if _, err = client.Read(recvBuf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(testBuf, recvBuf) {
|
||||
t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf))
|
||||
}
|
||||
}
|
||||
|
||||
func testProxy(t *testing.T, proto string, proxy Proxy) {
|
||||
testProxyAt(t, proto, proxy, proxy.FrontendAddr().String())
|
||||
}
|
||||
|
||||
func TestTCP4Proxy(t *testing.T) {
|
||||
backend := NewEchoServer(t, "tcp", "127.0.0.1:0")
|
||||
defer backend.Close()
|
||||
backend.Run()
|
||||
frontendAddr := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
|
||||
proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testProxy(t, "tcp", proxy)
|
||||
}
|
||||
|
||||
func TestTCP6Proxy(t *testing.T) {
|
||||
backend := NewEchoServer(t, "tcp", "[::1]:0")
|
||||
defer backend.Close()
|
||||
backend.Run()
|
||||
frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0}
|
||||
proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testProxy(t, "tcp", proxy)
|
||||
}
|
||||
|
||||
func TestTCPDualStackProxy(t *testing.T) {
|
||||
// If I understand `godoc -src net favoriteAddrFamily` (used by the
|
||||
// net.Listen* functions) correctly this should work, but it doesn't.
|
||||
t.Skip("No support for dual stack yet")
|
||||
backend := NewEchoServer(t, "tcp", "[::1]:0")
|
||||
defer backend.Close()
|
||||
backend.Run()
|
||||
frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0}
|
||||
proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ipv4ProxyAddr := &net.TCPAddr{
|
||||
IP: net.IPv4(127, 0, 0, 1),
|
||||
Port: proxy.FrontendAddr().(*net.TCPAddr).Port,
|
||||
}
|
||||
testProxyAt(t, "tcp", proxy, ipv4ProxyAddr.String())
|
||||
}
|
||||
|
||||
func TestUDP4Proxy(t *testing.T) {
|
||||
backend := NewEchoServer(t, "udp", "127.0.0.1:0")
|
||||
defer backend.Close()
|
||||
backend.Run()
|
||||
frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
|
||||
proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testProxy(t, "udp", proxy)
|
||||
}
|
||||
|
||||
func TestUDP6Proxy(t *testing.T) {
|
||||
backend := NewEchoServer(t, "udp", "[::1]:0")
|
||||
defer backend.Close()
|
||||
backend.Run()
|
||||
frontendAddr := &net.UDPAddr{IP: net.IPv6loopback, Port: 0}
|
||||
proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testProxy(t, "udp", proxy)
|
||||
}
|
||||
|
||||
func TestUDPWriteError(t *testing.T) {
|
||||
frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
|
||||
// Hopefully, this port will be free: */
|
||||
backendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 25587}
|
||||
proxy, err := NewProxy(frontendAddr, backendAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer proxy.Close()
|
||||
go proxy.Run()
|
||||
client, err := net.Dial("udp", "127.0.0.1:25587")
|
||||
if err != nil {
|
||||
t.Fatalf("Can't connect to the proxy: %v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
// Make sure the proxy doesn't stop when there is no actual backend:
|
||||
client.Write(testBuf)
|
||||
client.Write(testBuf)
|
||||
backend := NewEchoServer(t, "udp", "127.0.0.1:25587")
|
||||
defer backend.Close()
|
||||
backend.Run()
|
||||
client.SetDeadline(time.Now().Add(10 * time.Second))
|
||||
if _, err = client.Write(testBuf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
recvBuf := make([]byte, testBufSize)
|
||||
if _, err = client.Read(recvBuf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(testBuf, recvBuf) {
|
||||
t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf))
|
||||
}
|
||||
}
|
@@ -1,73 +0,0 @@
|
||||
// Package libproxy provides a network Proxy interface and implementations for TCP
|
||||
// and UDP.
|
||||
package libproxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/rneugeba/virtsock/go/vsock"
|
||||
)
|
||||
|
||||
// Proxy defines the behavior of a proxy. It forwards traffic back and forth
|
||||
// between two endpoints : the frontend and the backend.
|
||||
// It can be used to do software port-mapping between two addresses.
|
||||
// e.g. forward all traffic between the frontend (host) 127.0.0.1:3000
|
||||
// to the backend (container) at 172.17.42.108:4000.
|
||||
type Proxy interface {
|
||||
// Run starts forwarding traffic back and forth between the front
|
||||
// and back-end addresses.
|
||||
Run()
|
||||
// Close stops forwarding traffic and close both ends of the Proxy.
|
||||
Close()
|
||||
// FrontendAddr returns the address on which the proxy is listening.
|
||||
FrontendAddr() net.Addr
|
||||
// BackendAddr returns the proxied address.
|
||||
BackendAddr() net.Addr
|
||||
}
|
||||
|
||||
// NewVsockProxy creates a Proxy listening on Vsock
|
||||
func NewVsockProxy(frontendAddr *vsock.VsockAddr, backendAddr net.Addr) (Proxy, error) {
|
||||
switch backendAddr.(type) {
|
||||
case *net.UDPAddr:
|
||||
listener, err := vsock.Listen(frontendAddr.Port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewUDPProxy(frontendAddr, NewUDPListener(listener), backendAddr.(*net.UDPAddr))
|
||||
case *net.TCPAddr:
|
||||
listener, err := vsock.Listen(frontendAddr.Port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewTCPProxy(listener, backendAddr.(*net.TCPAddr))
|
||||
default:
|
||||
panic(fmt.Errorf("Unsupported protocol"))
|
||||
}
|
||||
}
|
||||
|
||||
// NewIPProxy creates a Proxy according to the specified frontendAddr and backendAddr.
|
||||
func NewIPProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) {
|
||||
switch frontendAddr.(type) {
|
||||
case *net.UDPAddr:
|
||||
listener, err := net.ListenUDP("udp", frontendAddr.(*net.UDPAddr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewUDPProxy(frontendAddr, listener, backendAddr.(*net.UDPAddr))
|
||||
case *net.TCPAddr:
|
||||
listener, err := net.Listen("tcp", frontendAddr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewTCPProxy(listener, backendAddr.(*net.TCPAddr))
|
||||
case *vsock.VsockAddr:
|
||||
listener, err := vsock.Listen(frontendAddr.(*vsock.VsockAddr).Port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewTCPProxy(listener, backendAddr.(*net.TCPAddr))
|
||||
default:
|
||||
panic(fmt.Errorf("Unsupported protocol"))
|
||||
}
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
package libproxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// StubProxy is a proxy that is a stub (does nothing).
|
||||
type StubProxy struct {
|
||||
frontendAddr net.Addr
|
||||
backendAddr net.Addr
|
||||
}
|
||||
|
||||
// Run does nothing.
|
||||
func (p *StubProxy) Run() {}
|
||||
|
||||
// Close does nothing.
|
||||
func (p *StubProxy) Close() {}
|
||||
|
||||
// FrontendAddr returns the frontend address.
|
||||
func (p *StubProxy) FrontendAddr() net.Addr { return p.frontendAddr }
|
||||
|
||||
// BackendAddr returns the backend address.
|
||||
func (p *StubProxy) BackendAddr() net.Addr { return p.backendAddr }
|
||||
|
||||
// NewStubProxy creates a new StubProxy
|
||||
func NewStubProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) {
|
||||
return &StubProxy{
|
||||
frontendAddr: frontendAddr,
|
||||
backendAddr: backendAddr,
|
||||
}, nil
|
||||
}
|
@@ -1,106 +0,0 @@
|
||||
package libproxy
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Conn defines a network connection
|
||||
type Conn interface {
|
||||
io.Reader
|
||||
io.Writer
|
||||
io.Closer
|
||||
CloseRead() error
|
||||
CloseWrite() error
|
||||
}
|
||||
|
||||
// TCPProxy is a proxy for TCP connections. It implements the Proxy interface to
|
||||
// handle TCP traffic forwarding between the frontend and backend addresses.
|
||||
type TCPProxy struct {
|
||||
listener net.Listener
|
||||
frontendAddr net.Addr
|
||||
backendAddr *net.TCPAddr
|
||||
}
|
||||
|
||||
// NewTCPProxy creates a new TCPProxy.
|
||||
func NewTCPProxy(listener net.Listener, backendAddr *net.TCPAddr) (*TCPProxy, error) {
|
||||
// If the port in frontendAddr was 0 then ListenTCP will have a picked
|
||||
// a port to listen on, hence the call to Addr to get that actual port:
|
||||
return &TCPProxy{
|
||||
listener: listener,
|
||||
frontendAddr: listener.Addr(),
|
||||
backendAddr: backendAddr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// HandleTCPConnection forwards the TCP traffic to a specified backend address
|
||||
func HandleTCPConnection(client Conn, backendAddr *net.TCPAddr, quit chan bool) {
|
||||
backend, err := net.DialTCP("tcp", nil, backendAddr)
|
||||
if err != nil {
|
||||
log.Printf("Can't forward traffic to backend tcp/%v: %s\n", backendAddr, err)
|
||||
client.Close()
|
||||
return
|
||||
}
|
||||
|
||||
event := make(chan int64)
|
||||
var broker = func(to, from Conn) {
|
||||
written, err := io.Copy(to, from)
|
||||
if err != nil {
|
||||
log.Println("error copying:", err)
|
||||
}
|
||||
err = from.CloseRead()
|
||||
if err != nil {
|
||||
log.Println("error CloseRead from:", err)
|
||||
}
|
||||
err = to.CloseWrite()
|
||||
if err != nil {
|
||||
log.Println("error CloseWrite to:", err)
|
||||
}
|
||||
event <- written
|
||||
}
|
||||
|
||||
go broker(client, backend)
|
||||
go broker(backend, client)
|
||||
|
||||
var transferred int64
|
||||
for i := 0; i < 2; i++ {
|
||||
select {
|
||||
case written := <-event:
|
||||
transferred += written
|
||||
case <-quit:
|
||||
// Interrupt the two brokers and "join" them.
|
||||
client.Close()
|
||||
backend.Close()
|
||||
for ; i < 2; i++ {
|
||||
transferred += <-event
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
client.Close()
|
||||
backend.Close()
|
||||
}
|
||||
|
||||
// Run starts forwarding the traffic using TCP.
|
||||
func (proxy *TCPProxy) Run() {
|
||||
quit := make(chan bool)
|
||||
defer close(quit)
|
||||
for {
|
||||
client, err := proxy.listener.Accept()
|
||||
if err != nil {
|
||||
log.Printf("Stopping proxy on tcp/%v for tcp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
|
||||
return
|
||||
}
|
||||
go HandleTCPConnection(client.(Conn), proxy.backendAddr, quit)
|
||||
}
|
||||
}
|
||||
|
||||
// Close stops forwarding the traffic.
|
||||
func (proxy *TCPProxy) Close() { proxy.listener.Close() }
|
||||
|
||||
// FrontendAddr returns the TCP address on which the proxy is listening.
|
||||
func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
|
||||
|
||||
// BackendAddr returns the TCP proxied address.
|
||||
func (proxy *TCPProxy) BackendAddr() net.Addr { return proxy.backendAddr }
|
@@ -1,202 +0,0 @@
|
||||
package libproxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// UDPListener defines a listener interface to read, write and close a UDP connection
|
||||
type UDPListener interface {
|
||||
ReadFromUDP(b []byte) (int, *net.UDPAddr, error)
|
||||
WriteToUDP(b []byte, addr *net.UDPAddr) (int, error)
|
||||
Close() error
|
||||
}
|
||||
|
||||
// udpEncapsulator encapsulates a UDP connection and listener
|
||||
type udpEncapsulator struct {
|
||||
conn *net.Conn
|
||||
listener net.Listener
|
||||
m *sync.Mutex
|
||||
r *sync.Mutex
|
||||
w *sync.Mutex
|
||||
}
|
||||
|
||||
func (u *udpEncapsulator) getConn() (net.Conn, error) {
|
||||
u.m.Lock()
|
||||
defer u.m.Unlock()
|
||||
if u.conn != nil {
|
||||
return *u.conn, nil
|
||||
}
|
||||
conn, err := u.listener.Accept()
|
||||
if err != nil {
|
||||
log.Printf("Failed to accept connection: %#v", err)
|
||||
return nil, err
|
||||
}
|
||||
u.conn = &conn
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// ReadFromUDP reads the bytestream from a udpEncapsulator, returning the
|
||||
// number of bytes read and the unpacked UDPAddr struct
|
||||
func (u *udpEncapsulator) ReadFromUDP(b []byte) (int, *net.UDPAddr, error) {
|
||||
conn, err := u.getConn()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
u.r.Lock()
|
||||
defer u.r.Unlock()
|
||||
datagram := &udpDatagram{payload: b}
|
||||
length, err := datagram.Unmarshal(conn)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
udpAddr := net.UDPAddr{IP: *datagram.IP, Port: int(datagram.Port), Zone: datagram.Zone}
|
||||
return length, &udpAddr, nil
|
||||
}
|
||||
|
||||
// WriteToUDP writes a bytestream to a specified UDPAddr, returning the number
|
||||
// of bytes successfully written
|
||||
func (u *udpEncapsulator) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) {
|
||||
conn, err := u.getConn()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
u.w.Lock()
|
||||
defer u.w.Unlock()
|
||||
datagram := &udpDatagram{payload: b, IP: &addr.IP, Port: uint16(addr.Port), Zone: addr.Zone}
|
||||
return len(b), datagram.Marshal(conn)
|
||||
}
|
||||
|
||||
// Close closes the connection in the udpEncapsulator
|
||||
func (u *udpEncapsulator) Close() error {
|
||||
if u.conn != nil {
|
||||
conn := *u.conn
|
||||
conn.Close()
|
||||
}
|
||||
u.listener.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewUDPConn initializes a new UDP connection
|
||||
func NewUDPConn(conn net.Conn) UDPListener {
|
||||
var m sync.Mutex
|
||||
var r sync.Mutex
|
||||
var w sync.Mutex
|
||||
return &udpEncapsulator{
|
||||
conn: &conn,
|
||||
listener: nil,
|
||||
m: &m,
|
||||
r: &r,
|
||||
w: &w,
|
||||
}
|
||||
}
|
||||
|
||||
// NewUDPListener initializes a new UDP listener
|
||||
func NewUDPListener(listener net.Listener) UDPListener {
|
||||
var m sync.Mutex
|
||||
var r sync.Mutex
|
||||
var w sync.Mutex
|
||||
return &udpEncapsulator{
|
||||
conn: nil,
|
||||
listener: listener,
|
||||
m: &m,
|
||||
r: &r,
|
||||
w: &w,
|
||||
}
|
||||
}
|
||||
|
||||
type udpDatagram struct {
|
||||
payload []byte
|
||||
IP *net.IP
|
||||
Port uint16
|
||||
Zone string
|
||||
}
|
||||
|
||||
// Marshal marshals data from the udpDatagram to the provided connection
|
||||
func (u *udpDatagram) Marshal(conn net.Conn) error {
|
||||
// marshal the variable length header to a temporary buffer
|
||||
var header bytes.Buffer
|
||||
var length uint16
|
||||
length = uint16(len(*u.IP))
|
||||
if err := binary.Write(&header, binary.LittleEndian, &length); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := binary.Write(&header, binary.LittleEndian, u.IP); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := binary.Write(&header, binary.LittleEndian, &u.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
length = uint16(len(u.Zone))
|
||||
if err := binary.Write(&header, binary.LittleEndian, &length); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := binary.Write(&header, binary.LittleEndian, []byte(u.Zone)); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
length = uint16(len(u.payload))
|
||||
if err := binary.Write(&header, binary.LittleEndian, &length); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
length = uint16(2 + header.Len() + len(u.payload))
|
||||
if err := binary.Write(conn, binary.LittleEndian, &length); err != nil {
|
||||
return nil
|
||||
}
|
||||
_, err := io.Copy(conn, &header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
payload := bytes.NewBuffer(u.payload)
|
||||
_, err = io.Copy(conn, payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unmarshal unmarshals data from the connection to the udpDatagram
|
||||
func (u *udpDatagram) Unmarshal(conn net.Conn) (int, error) {
|
||||
var length uint16
|
||||
// frame length
|
||||
if err := binary.Read(conn, binary.LittleEndian, &length); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := binary.Read(conn, binary.LittleEndian, &length); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var IP net.IP
|
||||
IP = make([]byte, length)
|
||||
if err := binary.Read(conn, binary.LittleEndian, &IP); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
u.IP = &IP
|
||||
if err := binary.Read(conn, binary.LittleEndian, &u.Port); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := binary.Read(conn, binary.LittleEndian, &length); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
Zone := make([]byte, length)
|
||||
if err := binary.Read(conn, binary.LittleEndian, &Zone); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
u.Zone = string(Zone)
|
||||
if err := binary.Read(conn, binary.LittleEndian, &length); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
_, err := io.ReadFull(conn, u.payload[0:length])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(length), nil
|
||||
}
|
@@ -1,165 +0,0 @@
|
||||
package libproxy
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// UDPConnTrackTimeout is the timeout used for UDP connection tracking
|
||||
UDPConnTrackTimeout = 90 * time.Second
|
||||
// UDPBufSize is the buffer size for the UDP proxy
|
||||
UDPBufSize = 65507
|
||||
)
|
||||
|
||||
// A net.Addr where the IP is split into two fields so you can use it as a key
|
||||
// in a map:
|
||||
type connTrackKey struct {
|
||||
IPHigh uint64
|
||||
IPLow uint64
|
||||
Port int
|
||||
}
|
||||
|
||||
func newConnTrackKey(addr *net.UDPAddr) *connTrackKey {
|
||||
if len(addr.IP) == net.IPv4len {
|
||||
return &connTrackKey{
|
||||
IPHigh: 0,
|
||||
IPLow: uint64(binary.BigEndian.Uint32(addr.IP)),
|
||||
Port: addr.Port,
|
||||
}
|
||||
}
|
||||
return &connTrackKey{
|
||||
IPHigh: binary.BigEndian.Uint64(addr.IP[:8]),
|
||||
IPLow: binary.BigEndian.Uint64(addr.IP[8:]),
|
||||
Port: addr.Port,
|
||||
}
|
||||
}
|
||||
|
||||
type connTrackMap map[connTrackKey]*net.UDPConn
|
||||
|
||||
// UDPProxy is proxy for which handles UDP datagrams. It implements the Proxy
|
||||
// interface to handle UDP traffic forwarding between the frontend and backend
|
||||
// addresses.
|
||||
type UDPProxy struct {
|
||||
listener UDPListener
|
||||
frontendAddr net.Addr
|
||||
backendAddr *net.UDPAddr
|
||||
connTrackTable connTrackMap
|
||||
connTrackLock sync.Mutex
|
||||
}
|
||||
|
||||
// NewUDPProxy creates a new UDPProxy.
|
||||
func NewUDPProxy(frontendAddr net.Addr, listener UDPListener, backendAddr *net.UDPAddr) (*UDPProxy, error) {
|
||||
|
||||
return &UDPProxy{
|
||||
listener: listener,
|
||||
frontendAddr: frontendAddr,
|
||||
backendAddr: backendAddr,
|
||||
connTrackTable: make(connTrackMap),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (proxy *UDPProxy) replyLoop(proxyConn *net.UDPConn, clientAddr *net.UDPAddr, clientKey *connTrackKey) {
|
||||
defer func() {
|
||||
proxy.connTrackLock.Lock()
|
||||
delete(proxy.connTrackTable, *clientKey)
|
||||
proxy.connTrackLock.Unlock()
|
||||
proxyConn.Close()
|
||||
}()
|
||||
|
||||
readBuf := make([]byte, UDPBufSize)
|
||||
for {
|
||||
proxyConn.SetReadDeadline(time.Now().Add(UDPConnTrackTimeout))
|
||||
again:
|
||||
read, err := proxyConn.Read(readBuf)
|
||||
if err != nil {
|
||||
if err, ok := err.(*net.OpError); ok && err.Err == syscall.ECONNREFUSED {
|
||||
// This will happen if the last write failed
|
||||
// (e.g: nothing is actually listening on the
|
||||
// proxied port on the container), ignore it
|
||||
// and continue until UDPConnTrackTimeout
|
||||
// expires:
|
||||
goto again
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := 0; i != read; {
|
||||
written, err := proxy.listener.WriteToUDP(readBuf[i:read], clientAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
i += written
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run starts forwarding the traffic using UDP.
|
||||
func (proxy *UDPProxy) Run() {
|
||||
readBuf := make([]byte, UDPBufSize)
|
||||
for {
|
||||
read, from, err := proxy.listener.ReadFromUDP(readBuf)
|
||||
if err != nil {
|
||||
// NOTE: Apparently ReadFrom doesn't return
|
||||
// ECONNREFUSED like Read do (see comment in
|
||||
// UDPProxy.replyLoop)
|
||||
if !isClosedError(err) {
|
||||
log.Printf("Stopping proxy on %v for udp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
fromKey := newConnTrackKey(from)
|
||||
proxy.connTrackLock.Lock()
|
||||
proxyConn, hit := proxy.connTrackTable[*fromKey]
|
||||
if !hit {
|
||||
proxyConn, err = net.DialUDP("udp", nil, proxy.backendAddr)
|
||||
if err != nil {
|
||||
log.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err)
|
||||
proxy.connTrackLock.Unlock()
|
||||
continue
|
||||
}
|
||||
proxy.connTrackTable[*fromKey] = proxyConn
|
||||
go proxy.replyLoop(proxyConn, from, fromKey)
|
||||
}
|
||||
proxy.connTrackLock.Unlock()
|
||||
for i := 0; i != read; {
|
||||
written, err := proxyConn.Write(readBuf[i:read])
|
||||
if err != nil {
|
||||
log.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err)
|
||||
break
|
||||
}
|
||||
i += written
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close stops forwarding the traffic.
|
||||
func (proxy *UDPProxy) Close() {
|
||||
proxy.listener.Close()
|
||||
proxy.connTrackLock.Lock()
|
||||
defer proxy.connTrackLock.Unlock()
|
||||
for _, conn := range proxy.connTrackTable {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// FrontendAddr returns the UDP address on which the proxy is listening.
|
||||
func (proxy *UDPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
|
||||
|
||||
// BackendAddr returns the proxied UDP address.
|
||||
func (proxy *UDPProxy) BackendAddr() net.Addr { return proxy.backendAddr }
|
||||
|
||||
func isClosedError(err error) bool {
|
||||
/* This comparison is ugly, but unfortunately, net.go doesn't export errClosing.
|
||||
* See:
|
||||
* http://golang.org/src/pkg/net/net.go
|
||||
* https://code.google.com/p/go/issues/detail?id=4337
|
||||
* https://groups.google.com/forum/#!msg/golang-nuts/0_aaCvBmOcM/SptmDyX1XJMJ
|
||||
*/
|
||||
return strings.HasSuffix(err.Error(), "use of closed network connection")
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if path.Base(os.Args[0]) == "proxy-vsockd" {
|
||||
manyPorts()
|
||||
return
|
||||
}
|
||||
onePort()
|
||||
}
|
@@ -1,117 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
"proxy/libproxy"
|
||||
|
||||
"github.com/rneugeba/virtsock/go/hvsock"
|
||||
"github.com/rneugeba/virtsock/go/vsock"
|
||||
)
|
||||
|
||||
// Listen on virtio-vsock and AF_HYPERV for multiplexed connections
|
||||
func manyPorts() {
|
||||
var (
|
||||
vsockPort = flag.Int("vsockPort", 62373, "virtio-vsock port")
|
||||
hvGUID = flag.String("hvGuid", "0B95756A-9985-48AD-9470-78E060895BE7", "Hyper-V service GUID")
|
||||
)
|
||||
flag.Parse()
|
||||
|
||||
listeners := make([]net.Listener, 0)
|
||||
|
||||
vsock, err := vsock.Listen(uint(*vsockPort))
|
||||
if err != nil {
|
||||
log.Printf("Failed to bind to vsock port %d: %#v", vsockPort, err)
|
||||
} else {
|
||||
listeners = append(listeners, vsock)
|
||||
}
|
||||
svcid, _ := hvsock.GuidFromString(*hvGUID)
|
||||
hvsock, err := hvsock.Listen(hvsock.HypervAddr{VmId: hvsock.GUID_WILDCARD, ServiceId: svcid})
|
||||
if err != nil {
|
||||
log.Printf("Failed to bind hvsock guid: %s: %#v", *hvGUID, err)
|
||||
} else {
|
||||
listeners = append(listeners, hvsock)
|
||||
}
|
||||
|
||||
quit := make(chan bool)
|
||||
defer close(quit)
|
||||
|
||||
for _, l := range listeners {
|
||||
go func(l net.Listener) {
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
log.Printf("Error accepting connection: %#v", err)
|
||||
return // no more listening
|
||||
}
|
||||
go func(conn net.Conn) {
|
||||
// Read header which describes TCP/UDP and destination IP:port
|
||||
d, err := unmarshalDestination(conn)
|
||||
if err != nil {
|
||||
log.Printf("Failed to unmarshal header: %#v", err)
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
switch d.Proto {
|
||||
case TCP:
|
||||
backendAddr := net.TCPAddr{IP: d.IP, Port: int(d.Port), Zone: ""}
|
||||
libproxy.HandleTCPConnection(conn.(libproxy.Conn), &backendAddr, quit)
|
||||
break
|
||||
case UDP:
|
||||
backendAddr := &net.UDPAddr{IP: d.IP, Port: int(d.Port), Zone: ""}
|
||||
|
||||
proxy, err := libproxy.NewUDPProxy(backendAddr, libproxy.NewUDPConn(conn), backendAddr)
|
||||
if err != nil {
|
||||
log.Printf("Failed to setup UDP proxy for %s: %#v", backendAddr, err)
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
proxy.Run()
|
||||
break
|
||||
default:
|
||||
log.Printf("Unknown protocol: %d", d.Proto)
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
}(conn)
|
||||
}
|
||||
}(l)
|
||||
}
|
||||
forever := make(chan int)
|
||||
<-forever
|
||||
}
|
||||
|
||||
const (
|
||||
// TCP protocol const
|
||||
TCP = 1
|
||||
// UDP protocol const
|
||||
UDP = 2
|
||||
)
|
||||
|
||||
type destination struct {
|
||||
Proto uint8
|
||||
IP net.IP
|
||||
Port uint16
|
||||
}
|
||||
|
||||
func unmarshalDestination(conn net.Conn) (destination, error) {
|
||||
d := destination{}
|
||||
if err := binary.Read(conn, binary.LittleEndian, &d.Proto); err != nil {
|
||||
return d, err
|
||||
}
|
||||
var length uint16
|
||||
// IP length
|
||||
if err := binary.Read(conn, binary.LittleEndian, &length); err != nil {
|
||||
return d, err
|
||||
}
|
||||
d.IP = make([]byte, length)
|
||||
if err := binary.Read(conn, binary.LittleEndian, &d.IP); err != nil {
|
||||
return d, err
|
||||
}
|
||||
if err := binary.Read(conn, binary.LittleEndian, &d.Port); err != nil {
|
||||
return d, err
|
||||
}
|
||||
return d, nil
|
||||
}
|
@@ -1,105 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"proxy/libproxy"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func onePort() {
|
||||
host, _, container, localIP := parseHostContainerAddrs()
|
||||
|
||||
var ipP libproxy.Proxy
|
||||
var err error
|
||||
|
||||
if localIP {
|
||||
ipP, err = listenInVM(host, container)
|
||||
if err != nil {
|
||||
sendError(err)
|
||||
}
|
||||
}
|
||||
|
||||
ctl, err := exposePort(host, container)
|
||||
if err != nil {
|
||||
sendError(err)
|
||||
}
|
||||
|
||||
go handleStopSignals()
|
||||
// TODO: avoid this line if we are running in a TTY
|
||||
sendOK()
|
||||
if ipP != nil {
|
||||
ipP.Run()
|
||||
} else {
|
||||
select {} // sleep forever
|
||||
}
|
||||
ctl.Close() // ensure ctl remains alive and un-GCed until here
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Best-effort attempt to listen on the address in the VM. This is for
|
||||
// backwards compatibility with software that expects to be able to listen on
|
||||
// 0.0.0.0 and then connect from within a container to the external port.
|
||||
// If the address doesn't exist in the VM (i.e. it exists only on the host)
|
||||
// then this is not a hard failure.
|
||||
func listenInVM(host net.Addr, container net.Addr) (libproxy.Proxy, error) {
|
||||
ipP, err := libproxy.NewIPProxy(host, container)
|
||||
if err == nil {
|
||||
return ipP, nil
|
||||
}
|
||||
if opError, ok := err.(*net.OpError); ok {
|
||||
if syscallError, ok := opError.Err.(*os.SyscallError); ok {
|
||||
if syscallError.Err == syscall.EADDRNOTAVAIL {
|
||||
log.Printf("Address %s doesn't exist in the VM: only binding on the host", host)
|
||||
return nil, nil // Non-fatal error
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func exposePort(host net.Addr, container net.Addr) (*os.File, error) {
|
||||
name := host.Network() + ":" + host.String() + ":" + container.Network() + ":" + container.String()
|
||||
log.Printf("exposePort %s\n", name)
|
||||
err := os.Mkdir("/port/"+name, 0)
|
||||
if err != nil {
|
||||
log.Printf("Failed to mkdir /port/%s: %#v\n", name, err)
|
||||
return nil, err
|
||||
}
|
||||
ctl, err := os.OpenFile("/port/"+name+"/ctl", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
log.Printf("Failed to open /port/%s/ctl: %#v\n", name, err)
|
||||
return nil, err
|
||||
}
|
||||
_, err = ctl.WriteString(fmt.Sprintf("%s", name))
|
||||
if err != nil {
|
||||
log.Printf("Failed to open /port/%s/ctl: %#v\n", name, err)
|
||||
return nil, err
|
||||
}
|
||||
_, err = ctl.Seek(0, 0)
|
||||
if err != nil {
|
||||
log.Printf("Failed to seek on /port/%s/ctl: %#v\n", name, err)
|
||||
return nil, err
|
||||
}
|
||||
results := make([]byte, 100)
|
||||
count, err := ctl.Read(results)
|
||||
if err != nil {
|
||||
log.Printf("Failed to read from /port/%s/ctl: %#v\n", name, err)
|
||||
return nil, err
|
||||
}
|
||||
// We deliberately keep the control file open since 9P clunk
|
||||
// will trigger a shutdown on the host side.
|
||||
|
||||
response := string(results[0:count])
|
||||
if strings.HasPrefix(response, "ERROR ") {
|
||||
os.Remove("/port/" + name + "/ctl")
|
||||
response = strings.Trim(response[6:], " \t\r\n")
|
||||
return nil, errors.New(response)
|
||||
}
|
||||
// Hold on to a reference to prevent premature GC and close
|
||||
return ctl, nil
|
||||
}
|
@@ -1,85 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var interactiveMode bool
|
||||
|
||||
// sendError signals the error to the parent and quits the process.
|
||||
func sendError(err error) {
|
||||
if interactiveMode {
|
||||
log.Fatal("Failed to set up proxy", err)
|
||||
}
|
||||
f := os.NewFile(3, "signal-parent")
|
||||
|
||||
fmt.Fprintf(f, "1\n%s", err)
|
||||
f.Close()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// sendOK signals the parent that the forward is running.
|
||||
func sendOK() {
|
||||
if interactiveMode {
|
||||
log.Println("Proxy running")
|
||||
return
|
||||
}
|
||||
f := os.NewFile(3, "signal-parent")
|
||||
fmt.Fprint(f, "0\n")
|
||||
f.Close()
|
||||
}
|
||||
|
||||
// Map dynamic TCP ports onto vsock ports over this offset
|
||||
var vSockTCPPortOffset = 0x10000
|
||||
|
||||
// Map dynamic UDP ports onto vsock ports over this offset
|
||||
var vSockUDPPortOffset = 0x20000
|
||||
|
||||
// From docker/libnetwork/portmapper/proxy.go:
|
||||
|
||||
// parseHostContainerAddrs parses the flags passed on reexec to create the TCP or UDP
|
||||
// net.Addrs to map the host and container ports
|
||||
func parseHostContainerAddrs() (host net.Addr, port int, container net.Addr, localIP bool) {
|
||||
var (
|
||||
proto = flag.String("proto", "tcp", "proxy protocol")
|
||||
hostIP = flag.String("host-ip", "", "host ip")
|
||||
hostPort = flag.Int("host-port", -1, "host port")
|
||||
containerIP = flag.String("container-ip", "", "container ip")
|
||||
containerPort = flag.Int("container-port", -1, "container port")
|
||||
interactive = flag.Bool("i", false, "print success/failure to stdout/stderr")
|
||||
noLocalIP = flag.Bool("no-local-ip", false, "bind only on the Host, not in the VM")
|
||||
)
|
||||
|
||||
flag.Parse()
|
||||
interactiveMode = *interactive
|
||||
|
||||
switch *proto {
|
||||
case "tcp":
|
||||
host = &net.TCPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
|
||||
port = vSockTCPPortOffset + *hostPort
|
||||
container = &net.TCPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
|
||||
case "udp":
|
||||
host = &net.UDPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
|
||||
port = vSockUDPPortOffset + *hostPort
|
||||
container = &net.UDPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
|
||||
default:
|
||||
log.Fatalf("unsupported protocol %s", *proto)
|
||||
}
|
||||
localIP = !*noLocalIP
|
||||
return host, port, container, localIP
|
||||
}
|
||||
|
||||
func handleStopSignals() {
|
||||
s := make(chan os.Signal, 10)
|
||||
signal.Notify(s, os.Interrupt, syscall.SIGTERM, syscall.SIGSTOP)
|
||||
|
||||
for range s {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
1
alpine/packages/tap-vsockd/.gitignore
vendored
1
alpine/packages/tap-vsockd/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
sbin
|
@@ -1,12 +0,0 @@
|
||||
C_COMPILE=mobylinux/c-compile:ac075fed7c87e4af30d8490ae0504166cceb0df3@sha256:0e82d441ce112d638f904a08199c76b022c065a2dbf8908bb366755267d4417f
|
||||
|
||||
default: sbin/tap-vsockd
|
||||
|
||||
DEPS=$(wildcard *.c *.h)
|
||||
|
||||
sbin/tap-vsockd: $(DEPS)
|
||||
mkdir -p $(dir $@)
|
||||
tar cf - $(DEPS) | docker run --rm --net=none --log-driver=none -i $(C_COMPILE) -o $@ -lpthread | tar xf -
|
||||
|
||||
clean:
|
||||
rm -rf sbin
|
@@ -1,48 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="VPN proxy"
|
||||
|
||||
depend()
|
||||
{
|
||||
need database-windows
|
||||
before net
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
[ "$(mobyplatform)" != "windows" ] && exit 0
|
||||
|
||||
mobyconfig exists network || exit 0
|
||||
NETWORK_MODE="$(mobyconfig get network | tr -d '[[:space:]]')"
|
||||
[ "${NETWORK_MODE}" = "hybrid" ] || exit 0
|
||||
|
||||
ebegin "Starting VPN proxy"
|
||||
|
||||
PIDFILE=/var/run/tap-vsockd.pid
|
||||
start-stop-daemon --start \
|
||||
--exec /sbin/tap-vsockd \
|
||||
--background \
|
||||
--pidfile ${PIDFILE} \
|
||||
-- \
|
||||
--tap eth0 \
|
||||
--message-size 8192 \
|
||||
--buffer-size 262144 \
|
||||
--pidfile "${PIDFILE}" \
|
||||
--listen
|
||||
|
||||
eend $? "Failed to start VPN proxy"
|
||||
}
|
||||
|
||||
stop()
|
||||
{
|
||||
[ "$(mobyplatform)" != "windows" ] && exit 0
|
||||
|
||||
ebegin "Stopping VPN proxy"
|
||||
|
||||
PIDFILE=/var/run/tap-vsockd.pid
|
||||
|
||||
start-stop-daemon --stop --quiet \
|
||||
--pidfile "${PIDFILE}"
|
||||
|
||||
eend $? "Failed to stop VPN proxy"
|
||||
}
|
@@ -1,40 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "hvsock.h"
|
||||
|
||||
int parseguid(const char *s, GUID *g)
|
||||
{
|
||||
int res;
|
||||
int p0, p1, p2, p3, p4, p5, p6, p7;
|
||||
|
||||
res = sscanf(s, GUID_FMT,
|
||||
&g->Data1, &g->Data2, &g->Data3,
|
||||
&p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7);
|
||||
if (res != 11)
|
||||
return 1;
|
||||
|
||||
g->Data4[0] = p0;
|
||||
g->Data4[1] = p1;
|
||||
g->Data4[2] = p2;
|
||||
g->Data4[3] = p3;
|
||||
g->Data4[4] = p4;
|
||||
g->Data4[5] = p5;
|
||||
g->Data4[6] = p6;
|
||||
g->Data4[7] = p7;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_GUID(HV_GUID_ZERO,
|
||||
0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||
DEFINE_GUID(HV_GUID_BROADCAST,
|
||||
0xFFFFFFFF, 0xFFFF, 0xFFFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
|
||||
DEFINE_GUID(HV_GUID_WILDCARD,
|
||||
0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||
|
||||
DEFINE_GUID(HV_GUID_CHILDREN,
|
||||
0x90db8b89, 0x0d35, 0x4f79, 0x8c, 0xe9, 0x49, 0xea, 0x0a, 0xc8, 0xb7, 0xcd);
|
||||
DEFINE_GUID(HV_GUID_LOOPBACK,
|
||||
0xe0e16197, 0xdd56, 0x4a10, 0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38);
|
||||
DEFINE_GUID(HV_GUID_PARENT,
|
||||
0xa42e7cda, 0xd03f, 0x480c, 0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78);
|
@@ -1,48 +0,0 @@
|
||||
/* AF_HYPERV definitions and utilities */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
/* GUID handling */
|
||||
typedef struct _GUID {
|
||||
uint32_t Data1;
|
||||
uint16_t Data2;
|
||||
uint16_t Data3;
|
||||
uint8_t Data4[8];
|
||||
} GUID;
|
||||
|
||||
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
const GUID name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}}
|
||||
|
||||
/* Helper macros for parsing/printing GUIDs */
|
||||
#define GUID_FMT "%08x-%04hx-%04hx-%02x%02x-%02x%02x%02x%02x%02x%02x"
|
||||
#define GUID_ARGS(_g) \
|
||||
(_g).Data1, (_g).Data2, (_g).Data3, \
|
||||
(_g).Data4[0], (_g).Data4[1], (_g).Data4[2], (_g).Data4[3], \
|
||||
(_g).Data4[4], (_g).Data4[5], (_g).Data4[6], (_g).Data4[7]
|
||||
#define GUID_SARGS(_g) \
|
||||
&(_g).Data1, &(_g).Data2, &(_g).Data3, \
|
||||
&(_g).Data4[0], &(_g).Data4[1], &(_g).Data4[2], &(_g).Data4[3], \
|
||||
&(_g).Data4[4], &(_g).Data4[5], &(_g).Data4[6], &(_g).Data4[7]
|
||||
|
||||
extern int parseguid(const char *s, GUID *g);
|
||||
|
||||
/* HV Socket definitions */
|
||||
#define AF_HYPERV 43
|
||||
#define HV_PROTOCOL_RAW 1
|
||||
|
||||
typedef struct _SOCKADDR_HV {
|
||||
unsigned short Family;
|
||||
unsigned short Reserved;
|
||||
GUID VmId;
|
||||
GUID ServiceId;
|
||||
} SOCKADDR_HV;
|
||||
|
||||
extern const GUID HV_GUID_ZERO;
|
||||
extern const GUID HV_GUID_BROADCAST;
|
||||
extern const GUID HV_GUID_WILDCARD;
|
||||
extern const GUID HV_GUID_CHILDREN;
|
||||
extern const GUID HV_GUID_LOOPBACK;
|
||||
extern const GUID HV_GUID_PARENT;
|
@@ -1,231 +0,0 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "protocol.h"
|
||||
|
||||
/* Version 0 of the protocol used this */
|
||||
char expected_hello_old[5] = {'V', 'M', 'N', 'E', 'T'};
|
||||
|
||||
/* Version 1 and later of the protocol used this */
|
||||
char expected_hello[5] = {'V', 'M', 'N', '3', 'T'};
|
||||
|
||||
int really_read(int fd, uint8_t *buffer, size_t total)
|
||||
{
|
||||
size_t remaining = total;
|
||||
ssize_t n;
|
||||
|
||||
while (remaining > 0) {
|
||||
n = read(fd, buffer, remaining);
|
||||
if (n == 0) {
|
||||
syslog(LOG_CRIT, "EOF reading from socket: closing\n");
|
||||
goto err;
|
||||
}
|
||||
if (n < 0) {
|
||||
syslog(LOG_CRIT,
|
||||
"Failure reading from socket: closing: %s",
|
||||
strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
remaining -= (size_t) n;
|
||||
buffer = buffer + n;
|
||||
}
|
||||
return 0;
|
||||
err:
|
||||
/*
|
||||
* On error: stop reading from the socket and trigger a clean
|
||||
* shutdown
|
||||
*/
|
||||
shutdown(fd, SHUT_RD);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int really_write(int fd, uint8_t *buffer, size_t total)
|
||||
{
|
||||
size_t remaining = total;
|
||||
ssize_t n;
|
||||
|
||||
while (remaining > 0) {
|
||||
n = write(fd, buffer, remaining);
|
||||
if (n == 0) {
|
||||
syslog(LOG_CRIT, "EOF writing to socket: closing");
|
||||
goto err;
|
||||
}
|
||||
if (n < 0) {
|
||||
syslog(LOG_CRIT,
|
||||
"Failure writing to socket: closing: %s",
|
||||
strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
remaining -= (size_t) n;
|
||||
buffer = buffer + n;
|
||||
}
|
||||
return 0;
|
||||
err:
|
||||
/* On error: stop listening to the socket */
|
||||
shutdown(fd, SHUT_WR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct init_message *create_init_message()
|
||||
{
|
||||
struct init_message *m;
|
||||
|
||||
m = malloc(sizeof(struct init_message));
|
||||
if (!m)
|
||||
return NULL;
|
||||
|
||||
bzero(m, sizeof(struct init_message));
|
||||
memcpy(&m->hello[0], &expected_hello[0], sizeof(m->hello));
|
||||
m->version = CURRENT_VERSION;
|
||||
memset(&m->commit[0], 0, sizeof(m->commit));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
char *print_init_message(struct init_message *m)
|
||||
{
|
||||
char tmp[41];
|
||||
|
||||
memcpy(&tmp[0], &m->commit[0], 40);
|
||||
tmp[40] = '\000';
|
||||
char *buffer;
|
||||
int n;
|
||||
|
||||
buffer = malloc(80);
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
|
||||
n = snprintf(buffer, 80, "version %d, commit %s", m->version, tmp);
|
||||
if (n < 0) {
|
||||
perror("Failed to format init_message");
|
||||
exit(1);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int read_init_message(int fd, struct init_message *ci)
|
||||
{
|
||||
int res;
|
||||
|
||||
bzero(ci, sizeof(struct init_message));
|
||||
|
||||
res = really_read(fd, (uint8_t *)&ci->hello[0], sizeof(ci->hello));
|
||||
if (res == -1) {
|
||||
syslog(LOG_CRIT, "Failed to read hello from client");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = memcmp(&ci->hello[0],
|
||||
&expected_hello_old[0], sizeof(expected_hello_old));
|
||||
if (res == 0) {
|
||||
ci->version = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
res = memcmp(&ci->hello[0],
|
||||
&expected_hello[0], sizeof(expected_hello));
|
||||
if (res != 0) {
|
||||
syslog(LOG_CRIT, "Failed to read header magic from client");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = really_read(fd, (uint8_t *)&ci->version, sizeof(ci->version));
|
||||
if (res == -1) {
|
||||
syslog(LOG_CRIT, "Failed to read header version from client");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = really_read(fd, (uint8_t *)&ci->commit[0], sizeof(ci->commit));
|
||||
if (res == -1) {
|
||||
syslog(LOG_CRIT, "Failed to read header hash from client");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_init_message(int fd, struct init_message *ci)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = really_write(fd, (uint8_t *)&ci->hello[0], sizeof(ci->hello));
|
||||
if (res == -1) {
|
||||
syslog(LOG_CRIT, "Failed to write hello to client");
|
||||
return -1;
|
||||
}
|
||||
if (ci->version > 0) {
|
||||
res = really_write(fd, (uint8_t *)&ci->version,
|
||||
sizeof(ci->version));
|
||||
if (res == -1) {
|
||||
syslog(LOG_CRIT, "Failed to write version to client");
|
||||
return -1;
|
||||
}
|
||||
res = really_write(fd, (uint8_t *)&ci->commit[0],
|
||||
sizeof(ci->commit));
|
||||
if (res == -1) {
|
||||
syslog(LOG_CRIT,
|
||||
"Failed to write header hash to client");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read_vif_info(int fd, struct vif_info *vif)
|
||||
{
|
||||
uint8_t buffer[10];
|
||||
|
||||
if (really_read(fd, &buffer[0], sizeof(buffer)) == -1) {
|
||||
syslog(LOG_CRIT, "Failed to read vif info from client");
|
||||
return -1;
|
||||
}
|
||||
|
||||
vif->mtu = (size_t)(buffer[0] | (buffer[1] << 8));
|
||||
vif->max_packet_size = (size_t)(buffer[2] | (buffer[3] << 8));
|
||||
memcpy(vif->mac, &buffer[4], 6);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int write_vif_info(int fd, struct vif_info *vif)
|
||||
{
|
||||
uint8_t buffer[10];
|
||||
|
||||
buffer[0] = (uint8_t) ((vif->mtu >> 0) & 0xff);
|
||||
buffer[1] = (uint8_t) ((vif->mtu >> 8) & 0xff);
|
||||
buffer[2] = (uint8_t) ((vif->max_packet_size >> 0) & 0xff);
|
||||
buffer[3] = (uint8_t) ((vif->max_packet_size >> 8) & 0xff);
|
||||
memcpy(&buffer[0] + 4, &(vif->mac)[0], 6);
|
||||
|
||||
if (really_write(fd, &buffer[0], sizeof(buffer)) == -1) {
|
||||
syslog(LOG_CRIT, "Failed to write vif into to client");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_command(int fd, enum command *c)
|
||||
{
|
||||
uint8_t command = *c;
|
||||
|
||||
if (really_write(fd, (uint8_t *)&command, sizeof(command)) == -1) {
|
||||
syslog(LOG_CRIT, "Failed to write command to client");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_ethernet_args(int fd, struct ethernet_args *args)
|
||||
{
|
||||
if (really_write(fd, (uint8_t *)&args->uuid_string[0], 36) == -1) {
|
||||
syslog(LOG_CRIT, "Failed to write ethernet args to client");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
@@ -1,59 +0,0 @@
|
||||
#ifndef _VMNET_PROTOCOL_H_
|
||||
#define _VMNET_PROTOCOL_H_
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Client -> Server init_message */
|
||||
/* Server -> Client init_message */
|
||||
struct init_message {
|
||||
char hello[5];
|
||||
uint8_t _padding[3];
|
||||
uint32_t version;
|
||||
char commit[40]; /* git sha of the compiled commit */
|
||||
};
|
||||
|
||||
/*
|
||||
* This should be bumped whenever we add something (like a feature or a
|
||||
* bugfix) and we wish the UI to be able to detect when to trigger a
|
||||
* reinstall.
|
||||
*/
|
||||
#define CURRENT_VERSION 13
|
||||
|
||||
extern struct init_message *create_init_message(void);
|
||||
extern int read_init_message(int fd, struct init_message *ci);
|
||||
extern int write_init_message(int fd, struct init_message *ci);
|
||||
extern char *print_init_message(struct init_message *m);
|
||||
|
||||
/* Client -> Server command */
|
||||
enum command {
|
||||
ethernet = 1,
|
||||
};
|
||||
|
||||
extern int write_command(int fd, enum command *c);
|
||||
|
||||
/* Client -> Server command arguments */
|
||||
struct ethernet_args {
|
||||
char uuid_string[36];
|
||||
};
|
||||
|
||||
extern int write_ethernet_args(int fd, struct ethernet_args *args);
|
||||
|
||||
/* Server -> Client: details of a vif */
|
||||
struct vif_info {
|
||||
uint8_t mac[6];
|
||||
short _padding;
|
||||
size_t max_packet_size;
|
||||
size_t mtu;
|
||||
};
|
||||
|
||||
extern int read_vif_info(int fd, struct vif_info *vif);
|
||||
extern int write_vif_info(int fd, struct vif_info *vif);
|
||||
|
||||
extern char expected_hello[5];
|
||||
extern char expected_hello_old[5];
|
||||
|
||||
extern int really_read(int fd, uint8_t *buffer, size_t total);
|
||||
extern int really_write(int fd, uint8_t *buffer, size_t total);
|
||||
|
||||
#endif /* _VMNET_PROTOCOL_H_ */
|
@@ -1,269 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include "ring.h"
|
||||
|
||||
extern void fatal(const char *msg);
|
||||
|
||||
|
||||
/* A fixed-size circular buffer.
|
||||
|
||||
The producer and consumer are positive integers from 0 to 2 * size-1.
|
||||
Adds are modulo 2 * size. This effectively uses one bit to distinguish
|
||||
the case where the buffer is empty (consumer == producer) from the case
|
||||
where the buffer is full (consumer + size == producer). */
|
||||
struct ring {
|
||||
int producer; /* Next sequence number to be written */
|
||||
int consumer; /* Next sequence number to be read */
|
||||
int last; /* Sequence number of end of stream or -1 */
|
||||
int size; /* Maximum number of buffered bytes */
|
||||
pthread_cond_t c;
|
||||
pthread_mutex_t m;
|
||||
char *data;
|
||||
};
|
||||
|
||||
struct ring *ring_allocate(int size)
|
||||
{
|
||||
struct ring *ring = (struct ring*)malloc(sizeof(struct ring));
|
||||
if (!ring) {
|
||||
fatal("Failed to allocate ring buffer metadata");
|
||||
}
|
||||
ring->data = (char*)malloc(size);
|
||||
if (!ring->data) {
|
||||
fatal("Failed to allocate ring buffer data");
|
||||
}
|
||||
int err = 0;
|
||||
if ((err = pthread_cond_init(&ring->c, NULL)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to create condition variable");
|
||||
}
|
||||
if ((err = pthread_mutex_init(&ring->m, NULL)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to create mutex");
|
||||
}
|
||||
ring->size = size;
|
||||
ring->producer = ring->consumer = 0;
|
||||
ring->last = -1;
|
||||
return ring;
|
||||
}
|
||||
|
||||
#define RING_DATA_AVAILABLE(r) \
|
||||
((r->producer >= r->consumer) ? \
|
||||
(r->producer - r->consumer) : \
|
||||
(2 * r->size + r->producer - r->consumer))
|
||||
#define RING_FREE_REQUESTS(r) (r->size - RING_DATA_AVAILABLE(r))
|
||||
|
||||
#define RING_GET(r, seq) (&(r->data[seq % r->size]))
|
||||
|
||||
/* Signal that new data is been produced */
|
||||
void ring_producer_advance(struct ring *ring, int n)
|
||||
{
|
||||
int err = 0;
|
||||
assert(n >= 0);
|
||||
if ((err = pthread_mutex_lock(&ring->m)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to lock mutex");
|
||||
}
|
||||
ring->producer = (ring->producer + n) % (2 * ring->size);
|
||||
if ((err = pthread_cond_broadcast(&ring->c)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to signal condition variable");
|
||||
}
|
||||
if ((err = pthread_mutex_unlock(&ring->m)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to unlock mutex");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Signal that data has been consumed */
|
||||
void ring_consumer_advance(struct ring *ring, int n)
|
||||
{
|
||||
int err = 0;
|
||||
assert(n >= 0);
|
||||
if ((err = pthread_mutex_lock(&ring->m)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to lock mutex");
|
||||
}
|
||||
ring->consumer = (ring->consumer + n) % (2 * ring->size);
|
||||
|
||||
if ((err = pthread_cond_broadcast(&ring->c)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to signal condition variable");
|
||||
}
|
||||
if ((err = pthread_mutex_unlock(&ring->m)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to unlock mutex");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* The producer sends Eof */
|
||||
void ring_producer_eof(struct ring *ring)
|
||||
{
|
||||
int err = 0;
|
||||
if ((err = pthread_mutex_lock(&ring->m)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to lock mutex");
|
||||
}
|
||||
ring->last = ring->producer - 1;
|
||||
if ((err = pthread_cond_broadcast(&ring->c)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to signal condition variable");
|
||||
}
|
||||
if ((err = pthread_mutex_unlock(&ring->m)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to unlock mutex");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Wait for n bytes to become available. If the ring has shutdown, return
|
||||
non-zero. If data is available then return zero and fill in the first
|
||||
iovec_len entries of the iovec. */
|
||||
int ring_producer_wait_available(
|
||||
struct ring *ring, size_t n, struct iovec *iovec, int *iovec_len
|
||||
) {
|
||||
int ret = 1;
|
||||
int err = 0;
|
||||
if ((err = pthread_mutex_lock(&ring->m)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to lock mutex");
|
||||
}
|
||||
while ((RING_FREE_REQUESTS(ring) < n) && (ring->last == -1)) {
|
||||
if ((err = pthread_cond_wait(&ring->c, &ring->m)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to wait on condition variable");
|
||||
}
|
||||
}
|
||||
if (ring->last != -1) {
|
||||
goto out;
|
||||
}
|
||||
char *producer = RING_GET(ring, ring->producer);
|
||||
char *consumer = RING_GET(ring, ring->consumer);
|
||||
assert (producer >= RING_GET(ring, 0));
|
||||
assert (producer <= RING_GET(ring, ring->size-1));
|
||||
assert (consumer >= RING_GET(ring, 0));
|
||||
assert (consumer <= RING_GET(ring, ring->size-1));
|
||||
if (*iovec_len <= 0) {
|
||||
ret = 0;
|
||||
fprintf(stderr, "no iovecs\n");
|
||||
goto out;
|
||||
}
|
||||
if (consumer > producer) {
|
||||
/* producer has not wrapped around the buffer yet */
|
||||
iovec[0].iov_base = producer;
|
||||
iovec[0].iov_len = consumer - producer;
|
||||
assert(iovec[0].iov_len > 0);
|
||||
*iovec_len = 1;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
/* consumer has wrapped around, so the first chunk is from the producer to
|
||||
the end of the buffer */
|
||||
iovec[0].iov_base = producer;
|
||||
iovec[0].iov_len = ring->size - (int) (producer - RING_GET(ring, 0));
|
||||
assert(iovec[0].iov_len > 0);
|
||||
if (*iovec_len == 1) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
*iovec_len = 1;
|
||||
/* also include the chunk from the beginning of the buffer to the consumer */
|
||||
iovec[1].iov_base = RING_GET(ring, 0);
|
||||
iovec[1].iov_len = consumer - RING_GET(ring, 0);
|
||||
if (iovec[1].iov_len > 0) {
|
||||
/* ... but don't bother if it's zero */
|
||||
*iovec_len = 2;
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
if ((err = pthread_mutex_unlock(&ring->m)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to unlock mutex");
|
||||
}
|
||||
if (ret == 0) {
|
||||
for (int i = 0; i < *iovec_len; i++) {
|
||||
assert(iovec[i].iov_base >= (void*)RING_GET(ring, 0));
|
||||
assert(iovec[i].iov_base + iovec[i].iov_len - 1 <= (void*)RING_GET(ring, ring->size - 1));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wait for n bytes to become available. If the ring has shutdown, return
|
||||
non-zero. If data is available then return zero and fill in the first
|
||||
iovec_len entries of the iovec. */
|
||||
int ring_consumer_wait_available(
|
||||
struct ring *ring, size_t n, struct iovec *iovec, int *iovec_len
|
||||
) {
|
||||
|
||||
int ret = 1;
|
||||
int err = 0;
|
||||
if ((err = pthread_mutex_lock(&ring->m)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to lock mutex");
|
||||
}
|
||||
while ((RING_DATA_AVAILABLE(ring) < n) && (ring->last == -1)) {
|
||||
if ((err = pthread_cond_wait(&ring->c, &ring->m)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to wait on condition variable");
|
||||
}
|
||||
}
|
||||
if (ring->last != -1) {
|
||||
goto out;
|
||||
}
|
||||
char *producer = RING_GET(ring, ring->producer);
|
||||
char *consumer = RING_GET(ring, ring->consumer);
|
||||
assert (producer >= RING_GET(ring, 0));
|
||||
assert (producer <= RING_GET(ring, ring->size-1));
|
||||
assert (consumer >= RING_GET(ring, 0));
|
||||
assert (consumer <= RING_GET(ring, ring->size-1));
|
||||
if (*iovec_len <= 0) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
if (producer > consumer) {
|
||||
/* producer has not wrapped around the buffer yet */
|
||||
iovec[0].iov_base = consumer;
|
||||
iovec[0].iov_len = producer - consumer;
|
||||
assert(iovec[0].iov_len > 0);
|
||||
*iovec_len = 1;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
/* producer has wrapped around, so the first chunk is from the consumer to
|
||||
the end of the buffer */
|
||||
iovec[0].iov_base = consumer;
|
||||
iovec[0].iov_len = ring->size - (int) (consumer - RING_GET(ring, 0));
|
||||
assert(iovec[0].iov_len > 0);
|
||||
if (*iovec_len == 1) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
*iovec_len = 1;
|
||||
/* also include the chunk from the beginning of the buffer to the producer */
|
||||
iovec[1].iov_base = RING_GET(ring, 0);
|
||||
iovec[1].iov_len = producer - RING_GET(ring, 0);
|
||||
if (iovec[1].iov_len > 0) {
|
||||
/* ... but don't bother if its zero */
|
||||
*iovec_len = 2;
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
if ((err = pthread_mutex_unlock(&ring->m)) != 0) {
|
||||
errno = err;
|
||||
fatal("Failed to unlock mutex");
|
||||
}
|
||||
if (ret == 0) {
|
||||
for (int i = 0; i < *iovec_len; i++) {
|
||||
assert(iovec[i].iov_base >= (void*)RING_GET(ring, 0));
|
||||
assert(iovec[i].iov_base + iovec[i].iov_len - 1 <= (void*)RING_GET(ring, ring->size - 1));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
/* A fixed-size circular buffer */
|
||||
struct ring;
|
||||
|
||||
/* Allocate a circular buffer with the given payload size.
|
||||
Size must be < INT_MAX / 2. */
|
||||
extern struct ring *ring_allocate(int size);
|
||||
|
||||
/* Signal that new data is been produced */
|
||||
extern void ring_producer_advance(struct ring *ring, int n);
|
||||
|
||||
/* Signal that data has been consumed */
|
||||
extern void ring_consumer_advance(struct ring *ring, int n);
|
||||
|
||||
/* The producer sends Eof. This will cause ring_consumer_wait_available
|
||||
and ring_producer_wait_available to return an error. */
|
||||
extern void ring_producer_eof(struct ring *ring);
|
||||
|
||||
/* Wait for n bytes of space for new data to become available. If
|
||||
ring_producer_eof has been called, return non-zero. If space is available
|
||||
then fill the first *iovec_len entries of the iovec and set *iovec_len to
|
||||
the number of iovecs used. */
|
||||
extern int ring_producer_wait_available(
|
||||
struct ring *ring, size_t n, struct iovec *iovec, int *iovec_len
|
||||
);
|
||||
|
||||
/* Wait for n bytes to become available for reading. If ring_producer_eof has
|
||||
been called, return non-zero. If data is available then fill the first
|
||||
*iovec_len entries of the iovec and set *iovec_len to the number of iovecs
|
||||
used. */
|
||||
extern int ring_consumer_wait_available(
|
||||
struct ring *ring, size_t n, struct iovec *iovec, int *iovec_len
|
||||
);
|
@@ -1,727 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <syslog.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <err.h>
|
||||
#include <sys/uio.h>
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <ifaddrs.h>
|
||||
|
||||
#include "hvsock.h"
|
||||
#include "protocol.h"
|
||||
#include "ring.h"
|
||||
|
||||
int daemon_flag;
|
||||
int nofork_flag;
|
||||
int listen_flag;
|
||||
int connect_flag;
|
||||
|
||||
char *default_sid = "30D48B34-7D27-4B0B-AAAF-BBBED334DD59";
|
||||
|
||||
/* Support big frames if the server requests it */
|
||||
const int max_packet_size = 16384;
|
||||
|
||||
static int verbose;
|
||||
#define INFO(...) \
|
||||
do { \
|
||||
if (verbose) { \
|
||||
printf(__VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
} \
|
||||
} while (0)
|
||||
#define DBG(...) \
|
||||
do { \
|
||||
if (verbose > 1) { \
|
||||
printf(__VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
} \
|
||||
} while (0)
|
||||
#define TRC(...) \
|
||||
do { \
|
||||
if (verbose > 2) { \
|
||||
printf(__VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void fatal(const char *msg)
|
||||
{
|
||||
syslog(LOG_CRIT, "%s Error: %d. %s", msg, errno, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int alloc_tap(const char *dev)
|
||||
{
|
||||
const char *clonedev = "/dev/net/tun";
|
||||
struct ifreq ifr;
|
||||
int persist = 1;
|
||||
int fd;
|
||||
|
||||
fd = open(clonedev, O_RDWR);
|
||||
if (fd == -1)
|
||||
fatal("Failed to open /dev/net/tun");
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
||||
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
|
||||
if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0)
|
||||
fatal("TUNSETIFF failed");
|
||||
|
||||
if (ioctl(fd, TUNSETPERSIST, persist) < 0)
|
||||
fatal("TUNSETPERSIST failed");
|
||||
|
||||
syslog(LOG_INFO, "successfully created TAP device %s", dev);
|
||||
return fd;
|
||||
}
|
||||
|
||||
void set_macaddr(const char *dev, uint8_t *mac)
|
||||
{
|
||||
struct ifreq ifq;
|
||||
int fd;
|
||||
|
||||
fd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (fd == -1)
|
||||
fatal("Could not get socket to set MAC address");
|
||||
strcpy(ifq.ifr_name, dev);
|
||||
memcpy(&ifq.ifr_hwaddr.sa_data[0], mac, 6);
|
||||
ifq.ifr_hwaddr.sa_family = ARPHRD_ETHER;
|
||||
|
||||
if (ioctl(fd, SIOCSIFHWADDR, &ifq) == -1)
|
||||
fatal("SIOCSIFHWADDR failed");
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void set_mtu(const char *dev, int mtu)
|
||||
{
|
||||
struct ifreq ifq;
|
||||
int fd;
|
||||
|
||||
fd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (fd == -1)
|
||||
fatal("Could not get socket to set MTU");
|
||||
strcpy(ifq.ifr_name, dev);
|
||||
ifq.ifr_mtu = mtu;
|
||||
|
||||
if (ioctl(fd, SIOCSIFMTU, &ifq) == -1)
|
||||
fatal("SIOCSIFMTU failed");
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/* Negotiate a vmnet connection, returns 0 on success and 1 on error. */
|
||||
int negotiate(int fd, struct vif_info *vif)
|
||||
{
|
||||
enum command command = ethernet;
|
||||
struct init_message *me;
|
||||
struct ethernet_args args;
|
||||
struct init_message you;
|
||||
char *txt;
|
||||
|
||||
me = create_init_message();
|
||||
if (!me)
|
||||
goto err;
|
||||
|
||||
if (write_init_message(fd, me) == -1)
|
||||
goto err;
|
||||
|
||||
if (read_init_message(fd, &you) == -1)
|
||||
goto err;
|
||||
|
||||
txt = print_init_message(&you);
|
||||
if (!txt)
|
||||
goto err;
|
||||
|
||||
syslog(LOG_INFO, "Server reports %s", txt);
|
||||
free(txt);
|
||||
|
||||
if (write_command(fd, &command) == -1)
|
||||
goto err;
|
||||
|
||||
/* We don't need a uuid */
|
||||
memset(&args.uuid_string[0], 0, sizeof(args.uuid_string));
|
||||
if (write_ethernet_args(fd, &args) == -1)
|
||||
goto err;
|
||||
|
||||
if (read_vif_info(fd, vif) == -1)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
syslog(LOG_CRIT, "Failed to negotiate vmnet connection");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Argument passed to proxy threads */
|
||||
struct connection {
|
||||
int fd; /* Hyper-V socket with vmnet protocol */
|
||||
int tapfd; /* TAP device with ethernet frames */
|
||||
struct vif_info vif; /* Contains MAC, MTU etc, received from server */
|
||||
struct ring* from_vmnet_ring;
|
||||
struct ring* to_vmnet_ring;
|
||||
int message_size; /* Maximum size of a Hyper-V read or write */
|
||||
};
|
||||
|
||||
/* Trim the iovec so that it contains at most len bytes. */
|
||||
void trim_iovec(struct iovec *iovec, int *iovec_len, size_t len)
|
||||
{
|
||||
for (int i = 0; i < *iovec_len; i++) {
|
||||
if (iovec[i].iov_len > len) {
|
||||
iovec[i].iov_len = len;
|
||||
*iovec_len = i + 1;
|
||||
return;
|
||||
}
|
||||
len -= iovec[i].iov_len;
|
||||
}
|
||||
}
|
||||
|
||||
size_t len_iovec(struct iovec *iovec, int iovec_len)
|
||||
{
|
||||
size_t len = 0;
|
||||
for (int i = 0; i < iovec_len; i++) {
|
||||
len += iovec[i].iov_len;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Read bytes from vmnet into the from_vmnet_ring */
|
||||
static void* vmnet_to_ring(void *arg)
|
||||
{
|
||||
struct connection *c = (struct connection *)arg;
|
||||
struct ring *ring = c->from_vmnet_ring;
|
||||
struct iovec iovec[2]; /* We won't need more than 2 for the ring */
|
||||
int iovec_len;
|
||||
while (1) {
|
||||
iovec_len = sizeof(iovec) / sizeof(struct iovec);
|
||||
TRC("vmnet_to_ring: ring_producer_wait_available n=%d iovec_len=%d\n", 1, iovec_len);
|
||||
if (ring_producer_wait_available(ring, 1, &iovec[0], &iovec_len) != 0) {
|
||||
fatal("Failed to read a data from vmnet");
|
||||
}
|
||||
trim_iovec(iovec, &iovec_len, c->message_size);
|
||||
{
|
||||
int length = 0;
|
||||
for (int i = 0; i < iovec_len; i ++) {
|
||||
length += iovec[i].iov_len;
|
||||
}
|
||||
TRC("vmnet_to_ring readv len %d\n", length);
|
||||
}
|
||||
ssize_t n = readv(c->fd, &iovec[0], iovec_len);
|
||||
TRC("vmnet_to_ring: read %zd\n", n);
|
||||
if (n == 0) {
|
||||
syslog(LOG_CRIT, "EOF reading from socket: closing\n");
|
||||
ring_producer_eof(ring);
|
||||
goto err;
|
||||
}
|
||||
if (n < 0) {
|
||||
syslog(LOG_CRIT,
|
||||
"Failure reading from socket: closing: %s (%d)",
|
||||
strerror(errno), errno);
|
||||
ring_producer_eof(ring);
|
||||
goto err;
|
||||
}
|
||||
TRC("vmnet_to_ring: advance producer %zd\n", n);
|
||||
ring_producer_advance(ring, (size_t) n);
|
||||
}
|
||||
err:
|
||||
/*
|
||||
* On error: stop reading from the socket and trigger a clean
|
||||
* shutdown
|
||||
*/
|
||||
TRC("vmnet_to_ring: shutdown\n");
|
||||
shutdown(c->fd, SHUT_RD);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Decode packets on the from_vmnet_ring and write to the tap device */
|
||||
static void* ring_to_tap(void *arg)
|
||||
{
|
||||
struct connection *c = (struct connection *)arg;
|
||||
struct iovec iovec[2]; /* We won't need more than 2 for the ring */
|
||||
int iovec_len;
|
||||
int length;
|
||||
struct ring *ring = c->from_vmnet_ring;
|
||||
while (1) {
|
||||
/* Read the packet length: this requires 2 bytes */
|
||||
iovec_len = sizeof(iovec) / sizeof(struct iovec);
|
||||
TRC("ring_to_tap: ring_consumer_wait_available n=%d iovec_len=%d\n", 2, iovec_len);
|
||||
if (ring_consumer_wait_available(ring, 2, &iovec[0], &iovec_len) != 0) {
|
||||
fatal("Failed to read a packet header from host");
|
||||
}
|
||||
length = *((uint8_t*)iovec[0].iov_base) & 0xff;
|
||||
/* The second byte might be in the second iovec array */
|
||||
if (iovec[0].iov_len >= 2) {
|
||||
length |= (*((uint8_t*)iovec[0].iov_base + 1) & 0xff) << 8;
|
||||
} else {
|
||||
length |= (*((uint8_t*)iovec[1].iov_base) & 0xff) << 8;
|
||||
}
|
||||
assert(length > 0);
|
||||
TRC("ring_to_tap: packet of length %d\n", length);
|
||||
if (length > max_packet_size) {
|
||||
syslog(LOG_CRIT,
|
||||
"Received an over-large packet: %d > %ld",
|
||||
length, max_packet_size);
|
||||
exit(1);
|
||||
}
|
||||
ring_consumer_advance(ring, 2);
|
||||
|
||||
/* Read the variable length packet */
|
||||
iovec_len = sizeof(iovec) / sizeof(struct iovec);
|
||||
TRC("ring_to_tap: ring_consumer_wait_available n=%d iovec_len=%d\n", length, iovec_len);
|
||||
if (ring_consumer_wait_available(ring, length, &iovec[0], &iovec_len) != 0) {
|
||||
fatal("Failed to read a packet body from host");
|
||||
}
|
||||
assert(len_iovec(&iovec[0], iovec_len) >= length);
|
||||
trim_iovec(iovec, &iovec_len, length);
|
||||
ssize_t n = writev(c->tapfd, &iovec[0], iovec_len);
|
||||
if (n != length) {
|
||||
syslog(LOG_CRIT,
|
||||
"Failed to write %d bytes to tap device (wrote %d)", length, n);
|
||||
//exit(1);
|
||||
}
|
||||
TRC("ring_to_tap: ring_consumer_advance n=%zd\n", n);
|
||||
ring_consumer_advance(ring, (size_t) length);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Write packets with header from the tap device onto the to_vmnet_ring */
|
||||
static void *tap_to_ring(void *arg)
|
||||
{
|
||||
struct connection *connection = (struct connection *)arg;
|
||||
struct ring *ring = connection->to_vmnet_ring;
|
||||
struct iovec iovec[2]; /* We won't need more than 2 for the ring */
|
||||
int iovec_len;
|
||||
struct iovec payload[2]; /* The packet body after the 2 byte header */
|
||||
int payload_len;
|
||||
size_t length;
|
||||
while (1) {
|
||||
/* Wait for space for a 2 byte header + max_packet_size */
|
||||
length = 2 + connection->vif.max_packet_size;
|
||||
iovec_len = sizeof(iovec) / sizeof(struct iovec);
|
||||
TRC("tap_to_ring: ring_producer_wait_available n=%zd iovec_len=%d\n", length, iovec_len);
|
||||
if (ring_producer_wait_available(ring, length, &iovec[0], &iovec_len) != 0) {
|
||||
fatal("Failed to find enough free space for a packet");
|
||||
}
|
||||
assert(iovec_len > 0);
|
||||
assert(iovec[0].iov_len > 0);
|
||||
memcpy(&payload[0], &iovec[0], sizeof(struct iovec) * iovec_len);
|
||||
payload_len = iovec_len;
|
||||
|
||||
/* take the first 2 bytes of the free space which will contain the header */
|
||||
char *header1 = payload[0].iov_base;
|
||||
payload[0].iov_base++;
|
||||
payload[0].iov_len--;
|
||||
if (payload[0].iov_len == 0) {
|
||||
assert(payload_len == 2); /* because `length` > 1 */
|
||||
payload[0].iov_base = payload[1].iov_base;
|
||||
payload[0].iov_len = payload[1].iov_len;
|
||||
payload_len --;
|
||||
}
|
||||
char *header2 = payload[0].iov_base;
|
||||
payload[0].iov_base++;
|
||||
payload[0].iov_len--;
|
||||
/* payload is now where the packet should go */
|
||||
|
||||
/* limit the message size */
|
||||
trim_iovec(payload, &payload_len, connection->message_size);
|
||||
|
||||
length = readv(connection->tapfd, payload, payload_len);
|
||||
|
||||
if (length == -1) {
|
||||
if (errno == ENXIO)
|
||||
fatal("tap device has gone down");
|
||||
|
||||
syslog(LOG_WARNING, "ignoring error %d", errno);
|
||||
/*
|
||||
* This is what mirage-net-unix does. Is it a good
|
||||
* idea really?
|
||||
*/
|
||||
exit(1);
|
||||
}
|
||||
*header1 = (length >> 0) & 0xff;
|
||||
*header2 = (length >> 8) & 0xff;
|
||||
TRC("tap_to_ring: ring_producer_advance n=%zd\n", length + 2);
|
||||
|
||||
ring_producer_advance(ring, (size_t) (length + 2));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Write bytes from the to_vmnet_ring to the vmnet fd */
|
||||
static void *ring_to_vmnet(void *arg)
|
||||
{
|
||||
struct connection *c = (struct connection *)arg;
|
||||
struct iovec iovec[2]; /* We won't need more than 2 for the ring */
|
||||
int iovec_len;
|
||||
int length;
|
||||
struct ring *ring = c->to_vmnet_ring;
|
||||
while (1) {
|
||||
/* Read the packet length: this requires 2 bytes */
|
||||
iovec_len = sizeof(iovec) / sizeof(struct iovec);
|
||||
TRC("ring_to_vmnet: ring_producer_wait_available n=%d iovec_len=%d\n", 1, iovec_len);
|
||||
if (ring_consumer_wait_available(ring, 1, &iovec[0], &iovec_len) != 0) {
|
||||
fatal("Failed to read data from ring");
|
||||
}
|
||||
trim_iovec(iovec, &iovec_len, c->message_size);
|
||||
length = 0;
|
||||
for (int i = 0; i < iovec_len; i++ ) {
|
||||
length += iovec[i].iov_len;
|
||||
}
|
||||
TRC("ring_to_vmnet: read %d bytes\n", length);
|
||||
ssize_t n = writev(c->fd, &iovec[0], iovec_len);
|
||||
|
||||
TRC("ring_to_vmnet: advance consumer %zd\n", n);
|
||||
ring_consumer_advance(ring, (size_t) n);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a connection by exchanging ethernet frames forever.
|
||||
*/
|
||||
static void handle(struct connection *connection)
|
||||
{
|
||||
pthread_t v2r, r2t, t2r, r2v;
|
||||
|
||||
if (pthread_create(&t2r, NULL, tap_to_ring, connection) != 0)
|
||||
fatal("Failed to create the tap_to_ring thread");
|
||||
|
||||
if (pthread_create(&v2r, NULL, vmnet_to_ring, connection) != 0)
|
||||
fatal("Failed to create the vmnet_to_tap thread");
|
||||
|
||||
if (pthread_create(&r2t, NULL, ring_to_tap, connection) != 0)
|
||||
fatal("Failed to create the ring_to_tap thread");
|
||||
|
||||
if (pthread_create(&r2v, NULL, ring_to_vmnet, connection) != 0)
|
||||
fatal("Failed to create the ring_to_vmnet thread");
|
||||
|
||||
if (pthread_join(t2r, NULL) != 0)
|
||||
fatal("Failed to join the tap_to_ring thread");
|
||||
|
||||
if (pthread_join(v2r, NULL) != 0)
|
||||
fatal("Failed to join the vmnet_to_ring thread");
|
||||
|
||||
if (pthread_join(t2r, NULL) != 0)
|
||||
fatal("Failed to join the tap_to_ring thread");
|
||||
|
||||
if (pthread_join(r2v, NULL) != 0)
|
||||
fatal("Failed to join the ring_to_vmnet thread");
|
||||
}
|
||||
|
||||
static int create_listening_socket(GUID serviceid)
|
||||
{
|
||||
SOCKADDR_HV sa;
|
||||
int lsock;
|
||||
int res;
|
||||
|
||||
lsock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW);
|
||||
if (lsock == -1)
|
||||
fatal("socket()");
|
||||
|
||||
sa.Family = AF_HYPERV;
|
||||
sa.Reserved = 0;
|
||||
sa.VmId = HV_GUID_WILDCARD;
|
||||
sa.ServiceId = serviceid;
|
||||
|
||||
res = bind(lsock, (const struct sockaddr *)&sa, sizeof(sa));
|
||||
if (res == -1)
|
||||
fatal("bind()");
|
||||
|
||||
res = listen(lsock, SOMAXCONN);
|
||||
if (res == -1)
|
||||
fatal("listen()");
|
||||
|
||||
return lsock;
|
||||
}
|
||||
|
||||
static int connect_socket(GUID serviceid)
|
||||
{
|
||||
SOCKADDR_HV sa;
|
||||
int sock;
|
||||
int res;
|
||||
|
||||
sock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW);
|
||||
if (sock == -1)
|
||||
fatal("socket()");
|
||||
|
||||
sa.Family = AF_HYPERV;
|
||||
sa.Reserved = 0;
|
||||
sa.VmId = HV_GUID_PARENT;
|
||||
sa.ServiceId = serviceid;
|
||||
|
||||
res = connect(sock, (const struct sockaddr *)&sa, sizeof(sa));
|
||||
if (res == -1)
|
||||
fatal("connect()");
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
static int accept_socket(int lsock)
|
||||
{
|
||||
SOCKADDR_HV sac;
|
||||
socklen_t socklen = sizeof(sac);
|
||||
int csock;
|
||||
|
||||
csock = accept(lsock, (struct sockaddr *)&sac, &socklen);
|
||||
if (csock == -1)
|
||||
fatal("accept()");
|
||||
|
||||
syslog(LOG_INFO, "Connect from: " GUID_FMT ":" GUID_FMT "\n",
|
||||
GUID_ARGS(sac.VmId), GUID_ARGS(sac.ServiceId));
|
||||
|
||||
return csock;
|
||||
}
|
||||
|
||||
void write_pidfile(const char *pidfile)
|
||||
{
|
||||
pid_t pid = getpid();
|
||||
char *pid_s;
|
||||
FILE *file;
|
||||
int len;
|
||||
|
||||
if (asprintf(&pid_s, "%lld", (long long)pid) == -1)
|
||||
fatal("Failed to allocate pidfile string");
|
||||
|
||||
len = strlen(pid_s);
|
||||
file = fopen(pidfile, "w");
|
||||
if (file == NULL) {
|
||||
syslog(LOG_CRIT, "Failed to open pidfile %s", pidfile);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (fwrite(pid_s, 1, len, file) != len)
|
||||
fatal("Failed to write pid to pidfile");
|
||||
|
||||
fclose(file);
|
||||
free(pid_s);
|
||||
}
|
||||
|
||||
void daemonize(const char *pidfile)
|
||||
{
|
||||
pid_t pid;
|
||||
int null;
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1)
|
||||
fatal("Failed to fork()");
|
||||
else if (pid != 0)
|
||||
exit(0);
|
||||
|
||||
if (setsid() == -1)
|
||||
fatal("Failed to setsid()");
|
||||
|
||||
if (chdir("/") == -1)
|
||||
fatal("Failed to chdir()");
|
||||
|
||||
null = open("/dev/null", O_RDWR);
|
||||
if (null == -1)
|
||||
fatal("Failed to open /dev/null");
|
||||
dup2(null, STDIN_FILENO);
|
||||
dup2(null, STDOUT_FILENO);
|
||||
dup2(null, STDERR_FILENO);
|
||||
close(null);
|
||||
|
||||
if (pidfile)
|
||||
write_pidfile(pidfile);
|
||||
}
|
||||
|
||||
void usage(char *name)
|
||||
{
|
||||
printf("%s usage:\n", name);
|
||||
printf("\t[--daemon] [--tap <name>] [--serviceid <guid>] [--pid <file>]\n");
|
||||
printf("\t[--message-size <bytes>] [--buffer-size <bytes>]\n");
|
||||
printf("\t[--listen | --connect]\n\n");
|
||||
printf("where\n");
|
||||
printf("\t--daemonize: run as a background daemon\n");
|
||||
printf("\t--nofork: don't run handlers in subprocesses\n");
|
||||
printf("\t--tap <name>: create a tap device with the given name\n");
|
||||
printf("\t (defaults to eth1)\n");
|
||||
printf("\t--serviceid <guid>: use <guid> as the well-known service GUID\n");
|
||||
printf("\t (defaults to %s)\n", default_sid);
|
||||
printf("\t--pid <file>: write a pid to the given file\n");
|
||||
printf("\t--message-size <bytes>: dictates the maximum transfer size for AF_HVSOCK\n");
|
||||
printf("\t--buffer-size <bytes>: dictates the buffer size for AF_HVSOCK\n");
|
||||
printf("\t--listen: listen forever for incoming AF_HVSOCK connections\n");
|
||||
printf("\t--connect: connect to the parent partition\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *serviceid = default_sid;
|
||||
struct connection connection;
|
||||
char *tap = "eth1";
|
||||
char *pidfile = NULL;
|
||||
int lsocket = -1;
|
||||
int sock = -1;
|
||||
int res = 0;
|
||||
int status;
|
||||
pid_t child;
|
||||
int tapfd;
|
||||
int ring_size = 1048576;
|
||||
int message_size = 8192; /* Well known to work across Hyper-V versions */
|
||||
GUID sid;
|
||||
int c;
|
||||
|
||||
int option_index;
|
||||
int log_flags = LOG_CONS | LOG_NDELAY;
|
||||
static struct option long_options[] = {
|
||||
/* These options set a flag. */
|
||||
{"daemon", no_argument, &daemon_flag, 1},
|
||||
{"nofork", no_argument, &nofork_flag, 1},
|
||||
{"serviceid", required_argument, NULL, 's'},
|
||||
{"tap", required_argument, NULL, 't'},
|
||||
{"pidfile", required_argument, NULL, 'p'},
|
||||
{"listen", no_argument, &listen_flag, 1},
|
||||
{"connect", no_argument, &connect_flag, 1},
|
||||
{"buffer-size", required_argument, NULL, 'b'},
|
||||
{"message-size", required_argument, NULL, 'm'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
opterr = 0;
|
||||
while (1) {
|
||||
option_index = 0;
|
||||
|
||||
c = getopt_long(argc, argv, "ds:t:p:r:m:v",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'd':
|
||||
daemon_flag = 1;
|
||||
break;
|
||||
case 'n':
|
||||
nofork_flag = 1;
|
||||
break;
|
||||
case 's':
|
||||
serviceid = optarg;
|
||||
break;
|
||||
case 't':
|
||||
tap = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
pidfile = optarg;
|
||||
break;
|
||||
case 'b':
|
||||
ring_size = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
message_size = atoi(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
verbose ++;
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ((listen_flag && connect_flag) || !(listen_flag || connect_flag)) {
|
||||
fprintf(stderr, "Please supply either the --listen or --connect flag, but not both.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (daemon_flag && !pidfile) {
|
||||
fprintf(stderr, "For daemon mode, please supply a --pidfile argument.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
res = parseguid(serviceid, &sid);
|
||||
if (res) {
|
||||
fprintf(stderr, "Failed to parse serviceid as GUID: %s\n", serviceid);
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!daemon_flag)
|
||||
log_flags |= LOG_PERROR;
|
||||
|
||||
openlog(argv[0], log_flags, LOG_DAEMON);
|
||||
|
||||
tapfd = alloc_tap(tap);
|
||||
connection.tapfd = tapfd;
|
||||
connection.to_vmnet_ring = ring_allocate(ring_size);
|
||||
connection.from_vmnet_ring = ring_allocate(ring_size);
|
||||
connection.message_size = message_size;
|
||||
if (listen_flag) {
|
||||
syslog(LOG_INFO, "starting in listening mode with serviceid=%s and tap=%s", serviceid, tap);
|
||||
lsocket = create_listening_socket(sid);
|
||||
} else {
|
||||
syslog(LOG_INFO, "starting in connect mode with serviceid=%s and tap=%s", serviceid, tap);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (sock != -1) {
|
||||
close(sock);
|
||||
sock = -1;
|
||||
}
|
||||
|
||||
if (listen_flag)
|
||||
sock = accept_socket(lsocket);
|
||||
else
|
||||
sock = connect_socket(sid);
|
||||
|
||||
connection.fd = sock;
|
||||
if (negotiate(sock, &connection.vif) != 0) {
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "VMNET VIF has MAC %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
connection.vif.mac[0], connection.vif.mac[1], connection.vif.mac[2],
|
||||
connection.vif.mac[3], connection.vif.mac[4], connection.vif.mac[5]
|
||||
);
|
||||
set_macaddr(tap, &connection.vif.mac[0]);
|
||||
set_mtu(tap, connection.vif.mtu);
|
||||
|
||||
/* Daemonize after we've made our first reliable connection */
|
||||
if (daemon_flag) {
|
||||
daemon_flag = 0;
|
||||
daemonize(pidfile);
|
||||
}
|
||||
if (nofork_flag) {
|
||||
handle(&connection);
|
||||
exit(1);
|
||||
}
|
||||
/*
|
||||
* Run the multithreaded part in a subprocess. On error the
|
||||
* process will exit() which tears down all the threads
|
||||
*/
|
||||
child = fork();
|
||||
if (child == 0) {
|
||||
handle(&connection);
|
||||
/*
|
||||
* should never happen but just in case of a logic
|
||||
* bug in handle
|
||||
*/
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (waitpid(child, &status, 0) != -1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
depend()
|
||||
{
|
||||
need docker containerd
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
[ -d /test ] || exit 0
|
||||
|
||||
ebegin "Running tests"
|
||||
|
||||
if containerd-ctr containers start --no-pivot --attach test /test
|
||||
then
|
||||
printf "Moby test suite PASSED\n"
|
||||
else
|
||||
printf "Moby test suite FAILED\n"
|
||||
fi
|
||||
|
||||
eend "Tests completed"
|
||||
|
||||
# now terminate with extreme prejudice
|
||||
poweroff -f
|
||||
}
|
1
alpine/packages/transfused/.gitignore
vendored
1
alpine/packages/transfused/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
sbin
|
@@ -1,12 +0,0 @@
|
||||
C_COMPILE=mobylinux/c-compile:ac075fed7c87e4af30d8490ae0504166cceb0df3@sha256:0e82d441ce112d638f904a08199c76b022c065a2dbf8908bb366755267d4417f
|
||||
|
||||
default: sbin/transfused
|
||||
|
||||
DEPS=$(wildcard *.c *.h)
|
||||
|
||||
sbin/transfused: $(DEPS)
|
||||
mkdir -p $(dir $@)
|
||||
tar cf - $(DEPS) | docker run --rm --net=none --log-driver=none -i $(C_COMPILE) -o $@ | tar xf -
|
||||
|
||||
clean:
|
||||
rm -rf sbin
|
@@ -1,40 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="fuse proxy server"
|
||||
|
||||
start()
|
||||
{
|
||||
[ "$(mobyplatform)" != "mac" ] && exit 0
|
||||
ebegin "Starting FUSE socket passthrough"
|
||||
|
||||
mkdir -p /host_docker_app
|
||||
find /tmp -mindepth 1 -delete
|
||||
|
||||
PIDFILE=/var/run/transfused.pid
|
||||
STARTUP_LOGFILE=/var/transfused_start.log
|
||||
|
||||
start-stop-daemon --start --quiet \
|
||||
--background \
|
||||
--exec /sbin/transfused \
|
||||
--pidfile ${PIDFILE} \
|
||||
-- \
|
||||
-p "${PIDFILE}" \
|
||||
-l "${STARTUP_LOGFILE}"
|
||||
|
||||
ewaitfile 2 ${PIDFILE}
|
||||
|
||||
eend $? "Failed to start transfused"
|
||||
}
|
||||
|
||||
stop()
|
||||
{
|
||||
[ "$(mobyplatform)" != "mac" ] && exit 0
|
||||
ebegin "Stopping FUSE socket passthrough"
|
||||
|
||||
PIDFILE=/var/run/transfused.pid
|
||||
|
||||
start-stop-daemon --stop --quiet \
|
||||
--pidfile "${PIDFILE}"
|
||||
|
||||
eend $? "Failed to stop transfused"
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,86 +0,0 @@
|
||||
#ifndef _TRANSFUSED_H_
|
||||
#define _TRANSFUSED_H_
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/socket.h>
|
||||
#include "transfused_perfstat.h"
|
||||
|
||||
#define IN_BUFSZ ((1 << 20) + 16)
|
||||
#define OUT_BUFSZ ((1 << 20) + 64)
|
||||
#define EVENT_BUFSZ 4096
|
||||
#define CTL_BUFSZ 65536
|
||||
#define PERFSTATS_PER_SEGMENT 2730 /* (64k - 16) / 24 */
|
||||
#define MAX_PERFSTAT_CHECK 64
|
||||
|
||||
#define DEFAULT_FUSERMOUNT "/bin/fusermount"
|
||||
#define DEFAULT_SOCKET "v:_:1525"
|
||||
#define DEFAULT_SERVER "v:2:1524"
|
||||
|
||||
#define PING 128
|
||||
#define RMDIR_SYSCALL 0
|
||||
#define UNLINK_SYSCALL 1
|
||||
#define MKDIR_SYSCALL 2
|
||||
#define SYMLINK_SYSCALL 3
|
||||
#define TRUNCATE_SYSCALL 4
|
||||
#define CHMOD_SYSCALL 5
|
||||
#define MKNOD_REG_SYSCALL 6
|
||||
/* these could be turned into an enum probably but...C standard nausea */
|
||||
|
||||
#define MOUNT_SUITABILITY_REQUEST 1
|
||||
#define EXPORT_SUITABILITY_REQUEST 2
|
||||
#define START_PERFSTAT_REQUEST 3
|
||||
#define STOP_PERFSTAT_REQUEST 4
|
||||
|
||||
#define TRANSFUSE_LOG_ERROR 1
|
||||
#define TRANSFUSE_LOG_NOTICE 2
|
||||
#define PONG_REPLY 3
|
||||
#define MOUNT_SUITABILITY_REPLY 4
|
||||
#define TRANSFUSE_NOTIFY_CHANNEL 5
|
||||
#define PERFSTAT_REPLY 6
|
||||
#define ERROR_REPLY 7
|
||||
|
||||
struct parameters;
|
||||
struct connection;
|
||||
|
||||
struct parameters {
|
||||
char *server;
|
||||
char *socket;
|
||||
char *fusermount;
|
||||
char *pidfile;
|
||||
char *logfile;
|
||||
int logfile_fd;
|
||||
int ctl_sock;
|
||||
int data_sock;
|
||||
pthread_mutex_t ctl_lock;
|
||||
struct connection *connections;
|
||||
};
|
||||
|
||||
typedef struct parameters parameters_t;
|
||||
|
||||
struct connection {
|
||||
struct connection *next;
|
||||
parameters_t *params;
|
||||
char *type_descr;
|
||||
char *mount_point;
|
||||
struct sockaddr sa_client;
|
||||
socklen_t socklen_client;
|
||||
int sock;
|
||||
int perfstat;
|
||||
perfstats_t *perfstats;
|
||||
pthread_mutex_t perfstat_lock;
|
||||
};
|
||||
|
||||
typedef struct connection connection_t;
|
||||
|
||||
pthread_attr_t detached;
|
||||
|
||||
void *must_malloc(char *const descr, size_t size);
|
||||
void lock(char *const descr, pthread_mutex_t *mutex);
|
||||
void unlock(char *const descr, pthread_mutex_t *mutex);
|
||||
void write_exactly(char *descr, int fd, void *buf, size_t nbyte);
|
||||
|
||||
void *error_reply(uint16_t id, const char *fmt, ...);
|
||||
|
||||
connection_t *find_connection(connection_t *conn, char *name, size_t len);
|
||||
|
||||
#endif /* _TRANSFUSED_H_ */
|
@@ -1,257 +0,0 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "transfused.h"
|
||||
#include "transfused_log.h"
|
||||
|
||||
void log_timestamp(int fd)
|
||||
{
|
||||
char timestamp[26];
|
||||
int msec;
|
||||
struct tm *tm_info;
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
msec = lrint(tv.tv_usec / 1000.0);
|
||||
if (msec >= 1000) {
|
||||
msec -= 1000;
|
||||
tv.tv_sec++;
|
||||
}
|
||||
tm_info = localtime(&tv.tv_sec);
|
||||
|
||||
strftime(timestamp, 26, "%Y-%m-%d %H:%M:%S", tm_info);
|
||||
dprintf(fd, "%s.%03d ", timestamp, msec);
|
||||
}
|
||||
|
||||
void vlog_sock_locked(int fd, uint16_t msg_type, const char *fmt, va_list args)
|
||||
{
|
||||
int rc, len;
|
||||
va_list targs;
|
||||
char *fill;
|
||||
|
||||
va_copy(targs, args);
|
||||
len = vsnprintf(NULL, 0, fmt, targs);
|
||||
if (len < 0)
|
||||
die(1, NULL, NULL, "Couldn't log due to vsnprintf failure");
|
||||
va_end(targs);
|
||||
|
||||
/* 4 for length itself and 2 for message type */
|
||||
rc = len + 4 + 2;
|
||||
write_exactly("vlog_sock_locked", fd,
|
||||
(uint32_t *)&rc, sizeof(uint32_t));
|
||||
write_exactly("vlog_sock_locked", fd, &msg_type, sizeof(uint16_t));
|
||||
|
||||
va_copy(targs, args);
|
||||
rc = vdprintf(fd, fmt, targs);
|
||||
if (rc < 0)
|
||||
die(1, NULL, "Couldn't send log message with vdprintf", "");
|
||||
va_end(targs);
|
||||
|
||||
if (rc < len) {
|
||||
/* we didn't write the whole message :-( */
|
||||
rc = len - rc;
|
||||
fill = (char *)calloc(rc, 1);
|
||||
if (fill == NULL)
|
||||
die(1, NULL, "vlog_sock_locked fill", "");
|
||||
write_exactly("vlog_sock_locked fill", fd, fill, rc);
|
||||
}
|
||||
}
|
||||
|
||||
void log_sock_locked(int fd, uint16_t msg_type, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vlog_sock_locked(fd, msg_type, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void die(int exit_code, parameters_t *params, const char *parg,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list argp, targs;
|
||||
int in_errno = errno;
|
||||
int fd = 0;
|
||||
|
||||
if (params != NULL) {
|
||||
fd = params->ctl_sock;
|
||||
lock("die ctl_lock", ¶ms->ctl_lock);
|
||||
}
|
||||
|
||||
va_start(argp, fmt);
|
||||
va_copy(targs, argp);
|
||||
vsyslog(LOG_CRIT, fmt, targs);
|
||||
va_end(targs);
|
||||
|
||||
if (fd != 0)
|
||||
vlog_sock_locked(fd, TRANSFUSE_LOG_ERROR, fmt, argp);
|
||||
va_end(argp);
|
||||
|
||||
if (parg != NULL) {
|
||||
if (*parg != 0) {
|
||||
syslog(LOG_CRIT, "%s: %s", parg, strerror(in_errno));
|
||||
if (fd != 0)
|
||||
log_sock_locked(fd, TRANSFUSE_LOG_ERROR,
|
||||
"%s: %s", parg, strerror(in_errno));
|
||||
} else {
|
||||
syslog(LOG_CRIT, "%s", strerror(in_errno));
|
||||
if (fd != 0)
|
||||
log_sock_locked(fd, TRANSFUSE_LOG_ERROR,
|
||||
"%s", strerror(in_errno));
|
||||
}
|
||||
}
|
||||
if (fd != 0)
|
||||
close(fd); /* flush */
|
||||
exit(exit_code);
|
||||
/* Nobody else should die before we terminate everything */
|
||||
unlock("die ctl_lock", ¶ms->ctl_lock);
|
||||
}
|
||||
|
||||
void vlog_locked(parameters_t *params, uint16_t msg_type,
|
||||
const char *fmt, va_list args)
|
||||
{
|
||||
int rc;
|
||||
int fd = params->ctl_sock;
|
||||
va_list targs;
|
||||
|
||||
if (fd != 0) {
|
||||
vlog_sock_locked(fd, msg_type, fmt, args);
|
||||
} else {
|
||||
va_copy(targs, args);
|
||||
/* TODO: translate msg_type to syslog message type */
|
||||
vsyslog(LOG_INFO, fmt, targs);
|
||||
va_end(targs);
|
||||
|
||||
fd = params->logfile_fd;
|
||||
if (fd != 0) {
|
||||
va_copy(targs, args);
|
||||
/* TODO: include message type? */
|
||||
rc = vdprintf(fd, fmt, targs);
|
||||
if (rc < 0)
|
||||
die(1, NULL,
|
||||
"Couldn't write log message with vdprintf", "");
|
||||
va_end(targs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vlog_time_locked(parameters_t *params, uint16_t msg_type,
|
||||
const char *fmt, va_list args)
|
||||
{
|
||||
int fd = params->logfile_fd;
|
||||
|
||||
if (fd != 0 && params->ctl_sock == 0)
|
||||
log_timestamp(fd);
|
||||
vlog_locked(params, msg_type, fmt, args);
|
||||
}
|
||||
|
||||
void log_time_locked(parameters_t *params, uint16_t msg_type,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vlog_time_locked(params, msg_type, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void log_time(parameters_t *params, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
lock("log_time ctl_lock", ¶ms->ctl_lock);
|
||||
vlog_time_locked(params, TRANSFUSE_LOG_ERROR, fmt, args);
|
||||
unlock("log_time ctl_lock", ¶ms->ctl_lock);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void log_notice_time(parameters_t *params, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
lock("log_time ctl_lock", ¶ms->ctl_lock);
|
||||
vlog_time_locked(params, TRANSFUSE_LOG_NOTICE, fmt, args);
|
||||
unlock("log_time ctl_lock", ¶ms->ctl_lock);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
parameters_t *params;
|
||||
char *msg;
|
||||
} log_thread_state;
|
||||
|
||||
void *log_time_thread(void *log_state_ptr)
|
||||
{
|
||||
log_thread_state *log_state = log_state_ptr;
|
||||
|
||||
log_time(log_state->params, log_state->msg);
|
||||
|
||||
free(log_state->msg);
|
||||
free(log_state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void thread_log_time(connection_t *conn, const char *fmt, ...)
|
||||
{
|
||||
log_thread_state *log_state;
|
||||
pthread_t logger;
|
||||
va_list args;
|
||||
|
||||
log_state = must_malloc("thread_log_time log_state",
|
||||
sizeof(log_thread_state));
|
||||
log_state->params = conn->params;
|
||||
|
||||
va_start(args, fmt);
|
||||
if (vasprintf(&log_state->msg, fmt, args) == -1)
|
||||
die(1, conn->params,
|
||||
"Couldn't allocate thread_log_time message", "");
|
||||
va_end(args);
|
||||
|
||||
/* TODO: We currently spawn a new thread for every
|
||||
* message. This is far from ideal but fine for now as we
|
||||
* anticipate thread-sensitive log demand to be low. */
|
||||
errno = pthread_create(&logger, &detached, log_time_thread, log_state);
|
||||
if (errno)
|
||||
die(1, conn->params, "",
|
||||
"Couldn't create log thread for %s connection %s: ",
|
||||
conn->type_descr, conn->mount_point);
|
||||
}
|
||||
|
||||
void log_continue_locked(parameters_t *params, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vlog_locked(params, TRANSFUSE_LOG_ERROR, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void log_continue(parameters_t *params, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
lock("log_continue ctl_lock", ¶ms->ctl_lock);
|
||||
vlog_locked(params, TRANSFUSE_LOG_ERROR, fmt, args);
|
||||
unlock("log_continue ctl_lock", ¶ms->ctl_lock);
|
||||
|
||||
va_end(args);
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
#ifndef _TRANSFUSED_LOG_H_
|
||||
#define _TRANSFUSED_LOG_H_
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "transfused.h"
|
||||
|
||||
void die(int exit_code, parameters_t *params, const char *perror_arg,
|
||||
const char *fmt, ...);
|
||||
|
||||
void vlog_locked(parameters_t *params, uint16_t msg_type,
|
||||
const char *fmt, va_list args);
|
||||
void vlog_time_locked(parameters_t *params, uint16_t msg_type,
|
||||
const char *fmt, va_list args);
|
||||
|
||||
void log_time_locked(parameters_t *params, uint16_t msg_type,
|
||||
const char *fmt, ...);
|
||||
|
||||
void log_time(parameters_t *params, const char *fmt, ...);
|
||||
void log_notice_time(parameters_t *params, const char *fmt, ...);
|
||||
void thread_log_time(connection_t *conn, const char *fmt, ...);
|
||||
void log_continue_locked(parameters_t *params, const char *fmt, ...);
|
||||
void log_continue(parameters_t *params, const char *fmt, ...);
|
||||
|
||||
#endif /* _TRANSFUSED_LOG_H_ */
|
@@ -1,173 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "transfused.h"
|
||||
#include "transfused_log.h"
|
||||
|
||||
uint64_t now(parameters_t *params)
|
||||
{
|
||||
uint64_t ns_in_s = 1000000000;
|
||||
struct timespec now;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &now))
|
||||
die(1, params, "now", "");
|
||||
return (uint64_t)now.tv_sec * ns_in_s + (uint64_t)now.tv_nsec;
|
||||
}
|
||||
|
||||
size_t size_of_perfstats(perfstats_t *p)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
while (p) {
|
||||
len += sizeof(perfstat_t) * p->len + sizeof(perfstats_t);
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int perfstat_open(uint64_t unique, connection_t *conn)
|
||||
{
|
||||
size_t sz;
|
||||
perfstats_t *old_perfstats = conn->perfstats;
|
||||
perfstats_t *stats = conn->perfstats;
|
||||
perfstat_t stat;
|
||||
|
||||
if (!conn->perfstat)
|
||||
return 0;
|
||||
|
||||
lock("perfstat lock: perfstat_open", &conn->perfstat_lock);
|
||||
if (conn->perfstat) {
|
||||
if (!stats || stats->len >= PERFSTATS_PER_SEGMENT) {
|
||||
sz = sizeof(perfstats_t);
|
||||
sz += PERFSTATS_PER_SEGMENT * sizeof(perfstat_t);
|
||||
stats = must_malloc("perfstats",sz);
|
||||
stats->next = old_perfstats;
|
||||
stats->len = 0;
|
||||
conn->perfstats = stats;
|
||||
}
|
||||
|
||||
stat = (perfstat_t) {
|
||||
.id = unique,
|
||||
.start = now(conn->params),
|
||||
.stop = 0
|
||||
};
|
||||
stats->perfstat[stats->len] = stat;
|
||||
stats->len++;
|
||||
}
|
||||
unlock("perfstat unlock: perfstat_close", &conn->perfstat_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perfstat_close_locked(uint64_t unique, parameters_t *params,
|
||||
perfstats_t *perfstats, int to_check)
|
||||
{
|
||||
int i;
|
||||
perfstat_t *stat;
|
||||
|
||||
if (!perfstats)
|
||||
return 1;
|
||||
|
||||
i = perfstats->len - 1;
|
||||
while (i >= 0 && to_check > 0) {
|
||||
stat = &perfstats->perfstat[i];
|
||||
if (stat->id == unique) {
|
||||
stat->stop = now(params);
|
||||
return 0;
|
||||
} else {
|
||||
i--;
|
||||
to_check--;
|
||||
}
|
||||
}
|
||||
|
||||
if (to_check && !perfstat_close_locked(unique, params, perfstats->next,
|
||||
to_check))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int perfstat_close(uint64_t unique, connection_t *conn)
|
||||
{
|
||||
int rc = 0;
|
||||
pthread_mutex_t *perfstat_lock = &conn->perfstat_lock;
|
||||
|
||||
if (!conn->perfstat)
|
||||
return 0;
|
||||
|
||||
lock("perfstat lock: perfstat_close", perfstat_lock);
|
||||
if (conn->perfstat)
|
||||
rc = perfstat_close_locked(unique, conn->params,
|
||||
conn->perfstats,
|
||||
MAX_PERFSTAT_CHECK);
|
||||
unlock("perfstat unlock: perfstat_close", perfstat_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void *start_perfstat(parameters_t *params, char *req, size_t len)
|
||||
{
|
||||
char *reply;
|
||||
uint16_t id = *((uint16_t *) req);
|
||||
char *mount = (char *) req + 2;
|
||||
connection_t *conn = find_connection(params->connections, mount,
|
||||
len - 2);
|
||||
if (conn == NULL)
|
||||
return (void *)error_reply(id, "Mount %s unknown", mount);
|
||||
|
||||
lock("perfstat lock: start_perfstat", &conn->perfstat_lock);
|
||||
conn->perfstat = 1;
|
||||
unlock("perfstat lock: start_perfstat", &conn->perfstat_lock);
|
||||
|
||||
reply = (char *)must_malloc("start_perfstat", 8);
|
||||
*((uint32_t *)reply) = 16;
|
||||
*((uint16_t *) (reply + 4)) = PERFSTAT_REPLY;
|
||||
*((uint16_t *) (reply + 6)) = id;
|
||||
*((uint64_t *) (reply + 8)) = now(params);
|
||||
|
||||
return (void *)reply;
|
||||
}
|
||||
|
||||
void copy_and_free_perfstats(perfstats_t *p, char *buf)
|
||||
{
|
||||
size_t len;
|
||||
perfstats_t *p_next;
|
||||
|
||||
while (p) {
|
||||
p_next = p->next;
|
||||
len = p->len * sizeof(perfstat_t);
|
||||
memcpy(buf, p->perfstat, len);
|
||||
buf += len;
|
||||
free(p);
|
||||
p = p_next;
|
||||
}
|
||||
}
|
||||
|
||||
void *stop_perfstat(parameters_t *params, char *req, size_t len)
|
||||
{
|
||||
char *reply;
|
||||
uint16_t id = *((uint16_t *) req);
|
||||
char *mount = (char *) req + 2;
|
||||
connection_t *conn = find_connection(params->connections, mount,
|
||||
len - 2);
|
||||
if (conn == NULL)
|
||||
return (void *)error_reply(id, "Mount %s unknown", mount);
|
||||
|
||||
size_t out_len = 16;
|
||||
|
||||
lock("perfstat lock: stop_perfstat", &conn->perfstat_lock);
|
||||
conn->perfstat = 0;
|
||||
|
||||
out_len += size_of_perfstats(conn->perfstats);
|
||||
|
||||
reply = (char *)must_malloc("stop_perfstat", out_len);
|
||||
*((uint32_t *)reply) = out_len;
|
||||
*((uint16_t *) (reply + 4)) = PERFSTAT_REPLY;
|
||||
*((uint16_t *) (reply + 6)) = id;
|
||||
*((uint64_t *) (reply + 8)) = now(params);
|
||||
|
||||
copy_and_free_perfstats(conn->perfstats, reply + 16);
|
||||
conn->perfstats = NULL;
|
||||
|
||||
unlock("perfstat lock: stop_perfstat", &conn->perfstat_lock);
|
||||
|
||||
return (void *)reply;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user