mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-09-04 16:30:52 +00:00
Merge pull request #1248 from justincormack/remove-alpine
Out with the old, in with the new Moby
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
|
*.tar.gz
|
||||||
*.vhdx
|
*.vhdx
|
||||||
*.efi
|
*.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:
|
default: bin/moby
|
||||||
$(MAKE) -C alpine
|
|
||||||
|
|
||||||
aufs:
|
all: default
|
||||||
$(MAKE) AUFS=true all
|
|
||||||
|
|
||||||
.PHONY: alpine/initrd.img
|
GO_COMPILE=mobylinux/go-compile:236629d9fc0779db9e7573ceb8b0e92f08f553be@sha256:16020c2d90cecb1f1d2d731187e947535c23f38b62319dd386ae642b4b32e1fb
|
||||||
alpine/initrd.img:
|
|
||||||
$(MAKE) -C alpine initrd.img
|
|
||||||
|
|
||||||
.PHONY: alpine/initrd-test.img
|
MOBY_DEPS=$(wildcard *.go) pkg vendor
|
||||||
alpine/initrd-test.img:
|
GOOS=$(shell uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
$(MAKE) -C alpine initrd-test.img
|
GOARCH=amd64
|
||||||
|
ifneq ($(GOOS),linux)
|
||||||
|
CROSS=-e GOOS=$(GOOS) -e GOARCH=$(GOARCH)
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY: kernel/x86_64/vmlinuz64
|
bin/moby: $(MOBY_DEPS) | bin
|
||||||
kernel/x86_64/vmlinuz64:
|
tar cf - $(MOBY_DEPS) | docker run --rm --net=none --log-driver=none -i $(CROSS) $(GO_COMPILE) --package github.com/docker/moby -o $@ | tar xf -
|
||||||
$(MAKE) -C kernel
|
|
||||||
|
|
||||||
.PHONY: alpine/mobylinux-bios.iso
|
QEMU_IMAGE=mobylinux/qemu:156d2160c2ccf4d5118221bc2708f6c0981d54cc@sha256:e1345ba0400d6c45bf3bdf4f4ed425c3d7596d11e6553b83f17f5893dfc49f7b
|
||||||
alpine/mobylinux-bios.iso:
|
|
||||||
$(MAKE) -C alpine mobylinux-bios.iso
|
|
||||||
|
|
||||||
.PHONY: alpine/mobylinux-efi.iso
|
moby-initrd.img: bin/moby moby.yaml
|
||||||
alpine/mobylinux-efi.iso:
|
$^
|
||||||
$(MAKE) -C alpine mobylinux-efi.iso
|
|
||||||
|
|
||||||
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
|
# interactive versions need to use volume mounts
|
||||||
qemu: alpine/initrd.img kernel/x86_64/vmlinuz64
|
qemu: moby-initrd.img
|
||||||
docker run -it --rm -v $(CURDIR)/alpine/initrd.img:/tmp/initrd.img -v $(CURDIR)/kernel/x86_64/vmlinuz64:/tmp/vmlinuz64 $(QEMU_IMAGE)
|
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
|
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:
|
bin:
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
@@ -58,10 +58,6 @@ else
|
|||||||
touch $@
|
touch $@
|
||||||
endif
|
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
|
hyperkit: scripts/hyperkit.sh bin/com.docker.hyperkit bin/vpnkit alpine/initrd.img kernel/x86_64/vmlinuz64
|
||||||
./scripts/hyperkit.sh
|
./scripts/hyperkit.sh
|
||||||
|
|
||||||
@@ -69,54 +65,15 @@ define check_test_log
|
|||||||
@cat $1 |grep -q 'Moby test suite PASSED'
|
@cat $1 |grep -q 'Moby test suite PASSED'
|
||||||
endef
|
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
|
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)
|
$(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
|
tar cf - $^ | docker run --rm -i $(QEMU_IMAGE) 2>&1 | tee test.log
|
||||||
$(call check_test_log, 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_TAG=ebpf/ebpf.tag
|
||||||
EBPF_IMAGE=mobylinux/ebpf:$(MEDIA_PREFIX)$(AUFS_PREFIX)$(TAG)
|
EBPF_IMAGE=mobylinux/ebpf:$(MEDIA_PREFIX)$(AUFS_PREFIX)$(TAG)
|
||||||
ebpf: alpine/initrd.img kernel/x86_64/vmlinuz64
|
ebpf: alpine/initrd.img kernel/x86_64/vmlinuz64
|
||||||
@@ -128,54 +85,22 @@ else
|
|||||||
$(error "git not clean")
|
$(error "git not clean")
|
||||||
endif
|
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:
|
ci:
|
||||||
$(MAKE) clean
|
$(MAKE) clean
|
||||||
$(MAKE) all
|
$(MAKE)
|
||||||
$(MAKE) test
|
$(MAKE) test
|
||||||
$(MAKE) media
|
|
||||||
|
|
||||||
ci-tag:
|
ci-tag:
|
||||||
$(MAKE) clean
|
$(MAKE) clean
|
||||||
$(MAKE) all
|
$(MAKE)
|
||||||
$(MAKE) test
|
$(MAKE) test
|
||||||
$(MAKE) media
|
|
||||||
|
|
||||||
ci-pr:
|
ci-pr:
|
||||||
$(MAKE) clean
|
$(MAKE) clean
|
||||||
$(MAKE) all
|
$(MAKE)
|
||||||
$(MAKE) test
|
$(MAKE) test
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(MAKE) -C alpine clean
|
rm -rf bin disk.img test.log *-initrd.img *-bzImage *.iso
|
||||||
$(MAKE) -C kernel clean
|
|
||||||
rm -rf bin disk.img test.log Dockerfile.media
|
|
||||||
|
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