Out with the old, in with the new Moby

- remove remainder of editions code
- add a new check container to run tests without Docker
- switch over `make test` to use new command to build tests

Signed-off-by: Justin Cormack <justin.cormack@docker.com>
This commit is contained in:
Justin Cormack
2017-03-06 21:25:11 +00:00
parent ab6575c089
commit 159202416c
143 changed files with 280 additions and 10651 deletions

View File

@@ -1 +1 @@
{"variants":["aufs", "lts4.4"]}
{"variants":[]}

1
.gitignore vendored
View File

@@ -11,3 +11,4 @@ disk.img.*
*.tar.gz
*.vhdx
*.efi
*-bzImage

137
Makefile
View File

@@ -1,39 +1,39 @@
.PHONY: test hyperkit-test qemu qemu-iso media ebpf ci ci-pr get get-regextract
.PHONY: default test hyperkit-test qemu qemu-iso media ebpf ci ci-pr
all:
$(MAKE) -C alpine
default: bin/moby
aufs:
$(MAKE) AUFS=true all
all: default
.PHONY: alpine/initrd.img
alpine/initrd.img:
$(MAKE) -C alpine initrd.img
GO_COMPILE=mobylinux/go-compile:236629d9fc0779db9e7573ceb8b0e92f08f553be@sha256:16020c2d90cecb1f1d2d731187e947535c23f38b62319dd386ae642b4b32e1fb
.PHONY: alpine/initrd-test.img
alpine/initrd-test.img:
$(MAKE) -C alpine initrd-test.img
MOBY_DEPS=$(wildcard *.go) pkg vendor
GOOS=$(shell uname -s | tr '[:upper:]' '[:lower:]')
GOARCH=amd64
ifneq ($(GOOS),linux)
CROSS=-e GOOS=$(GOOS) -e GOARCH=$(GOARCH)
endif
.PHONY: kernel/x86_64/vmlinuz64
kernel/x86_64/vmlinuz64:
$(MAKE) -C kernel
bin/moby: $(MOBY_DEPS) | bin
tar cf - $(MOBY_DEPS) | docker run --rm --net=none --log-driver=none -i $(CROSS) $(GO_COMPILE) --package github.com/docker/moby -o $@ | tar xf -
.PHONY: alpine/mobylinux-bios.iso
alpine/mobylinux-bios.iso:
$(MAKE) -C alpine mobylinux-bios.iso
QEMU_IMAGE=mobylinux/qemu:156d2160c2ccf4d5118221bc2708f6c0981d54cc@sha256:e1345ba0400d6c45bf3bdf4f4ed425c3d7596d11e6553b83f17f5893dfc49f7b
.PHONY: alpine/mobylinux-efi.iso
alpine/mobylinux-efi.iso:
$(MAKE) -C alpine mobylinux-efi.iso
moby-initrd.img: bin/moby moby.yaml
$^
QEMU_IMAGE=mobylinux/qemu:0fb8c648e8ed9ef6b1ec449587aeab6c53872744@sha256:606f30d815102e73bc01c07915dc0d5f153b0252c63f5f0ed1e39621ec656eb5
moby-bzImage: moby-initrd.img
test-initrd.img: bin/moby test.yaml
$^
test-bzImage: test-initrd.img
# interactive versions need to use volume mounts
qemu: alpine/initrd.img kernel/x86_64/vmlinuz64
docker run -it --rm -v $(CURDIR)/alpine/initrd.img:/tmp/initrd.img -v $(CURDIR)/kernel/x86_64/vmlinuz64:/tmp/vmlinuz64 $(QEMU_IMAGE)
qemu: moby-initrd.img
docker run -it --rm -v $(CURDIR)/moby-initrd.img:/tmp/initrd.img -v $(CURDIR)/moby-bzImage:/tmp/vmlinuz64 $(QEMU_IMAGE)
qemu-iso: alpine/mobylinux-bios.iso
docker run -it --rm -v $(CURDIR)/alpine/mobylinux-bios.iso:/tmp/mobylinux-bios.iso $(QEMU_IMAGE)
docker run -it --rm -v $(CURDIR)/mobylinux-bios.iso:/tmp/mobylinux-bios.iso $(QEMU_IMAGE)
bin:
mkdir -p $@
@@ -58,10 +58,6 @@ else
touch $@
endif
bin/regextract: | bin
curl -fsSL https://circleci.com/api/v1/project/justincormack/regextract/latest/artifacts/0/\$$CIRCLE_ARTIFACTS/darwin/amd64/regextract > $@
chmod a+x $@
hyperkit: scripts/hyperkit.sh bin/com.docker.hyperkit bin/vpnkit alpine/initrd.img kernel/x86_64/vmlinuz64
./scripts/hyperkit.sh
@@ -69,54 +65,15 @@ define check_test_log
@cat $1 |grep -q 'Moby test suite PASSED'
endef
hyperkit-test: scripts/hyperkit.sh bin/com.docker.hyperkit bin/vpnkit alpine/initrd-test.img kernel/x86_64/vmlinuz64
hyperkit-test: scripts/hyperkit.sh bin/com.docker.hyperkit bin/vpnkit test-initrd.img test-bzImage
rm -f disk.img
INITRD=alpine/initrd-test.img script -q /dev/null ./scripts/hyperkit.sh | tee test.log
script -q /dev/null ./scripts/hyperkit.sh test | tee test.log
$(call check_test_log, test.log)
test: alpine/initrd-test.img kernel/x86_64/vmlinuz64
test: test-initrd.img test-bzImage
tar cf - $^ | docker run --rm -i $(QEMU_IMAGE) 2>&1 | tee test.log
$(call check_test_log, test.log)
ifeq ($(CI_TAG),)
TAG=$(shell git rev-parse HEAD)
else
TAG=$(CI_TAG)
endif
STATUS=$(shell git status -s)
MOBYLINUX_TAG=alpine/mobylinux.tag
ifdef AUFS
AUFS_PREFIX=aufs-
endif
ifdef LTS4.4
AUFS_PREFIX=lts4.4-
endif
MEDIA_IMAGE=mobylinux/media:$(MEDIA_PREFIX)$(AUFS_PREFIX)$(TAG)
INITRD_IMAGE=mobylinux/mobylinux:$(MEDIA_PREFIX)$(AUFS_PREFIX)$(TAG)
KERNEL_IMAGE=mobylinux/kernel:$(MEDIA_PREFIX)$(AUFS_PREFIX)$(TAG)
MEDIA_TOYBOX=mobylinux/toybox-media:0a26fe5f574e444849983f9c4148ef74b3804d55@sha256:5ac38f77b66deb194c9016591b9b096e81fcdc9f7c3e6d01566294a6b4b4ebd2
Dockerfile.media:
printf "FROM $(MEDIA_TOYBOX)\nADD . /\n" > $@
MEDIA_TARBALL=Dockerfile.media -C alpine initrd.img initrd-test.img mobylinux-efi.iso mobylinux.efi -C ../kernel/x86_64 vmlinuz64 vmlinux kernel-headers.tar kernel-dev.tar
media: Dockerfile.media alpine/initrd.img alpine/initrd-test.img kernel/x86_64/vmlinuz64 alpine/mobylinux-efi.iso
ifeq ($(STATUS),)
tar cf - $(MEDIA_TARBALL) | docker build -f Dockerfile.media -t $(MEDIA_IMAGE) -
docker push $(MEDIA_IMAGE)
[ -f $(MOBYLINUX_TAG) ]
docker tag $(shell cat $(MOBYLINUX_TAG)) $(INITRD_IMAGE)
docker push $(INITRD_IMAGE)
tar cf - Dockerfile.media -C kernel/x86_64 bzImage kernel.tar | docker build -f Dockerfile.media -t $(KERNEL_IMAGE) -
docker push $(KERNEL_IMAGE)
else
$(error "git not clean")
endif
EBPF_TAG=ebpf/ebpf.tag
EBPF_IMAGE=mobylinux/ebpf:$(MEDIA_PREFIX)$(AUFS_PREFIX)$(TAG)
ebpf: alpine/initrd.img kernel/x86_64/vmlinuz64
@@ -128,54 +85,22 @@ else
$(error "git not clean")
endif
MEDIA_FILES=kernel/x86_64/vmlinuz64 kernel/x86_64/vmlinux alpine/initrd.img alpine/mobylinux-efi.iso alpine/mobylinux.efi
MEDIA_FILES_OPT=kernel/x86_64/kernel-headers.tar kernel/x86_64/kernel-dev.tar alpine/initrd-test.img
get:
ifeq ($(STATUS),)
IMAGE=$$( docker create mobylinux/media:$(MEDIA_PREFIX)$(AUFS_PREFIX)$(TAG) /dev/null ) && \
mkdir -p kernel/x86_64 && \
for FILE in $(MEDIA_FILES); do docker cp $$IMAGE:$$(basename $$FILE) $$FILE || exit; done; \
for FILE in $(MEDIA_FILES_OPT); do docker cp $$IMAGE:$$(basename $$FILE) $$FILE; done; \
docker rm $$IMAGE
else
$(error "git not clean")
endif
# Get artifacts using regextract for cases where docker is not available
get-regextract: bin/regextract
ifeq ($(STATUS),)
TMP_EXTRACT=$$(mktemp -d) && \
for FILE in $(MEDIA_FILES) $(MEDIA_FILES_OPT); do mkdir -p $$(dirname $$FILE); done; \
bin/regextract mobylinux/media:$(MEDIA_PREFIX)$(AUFS_PREFIX)$(TAG) | tar xf - -C $$TMP_EXTRACT && \
mkdir -p kernel/x86_64 && \
for FILE in $(MEDIA_FILES); do cp $$TMP_EXTRACT/$$(basename $$FILE) $$FILE || exit; done; \
for FILE in $(MEDIA_FILES_OPT); do cp $$TMP_EXTRACT/$$(basename $$FILE) $$FILE; done; \
rm -Rf $$TMP_EXTRACT
else
$(error "git not clean")
endif
ci:
$(MAKE) clean
$(MAKE) all
$(MAKE)
$(MAKE) test
$(MAKE) media
ci-tag:
$(MAKE) clean
$(MAKE) all
$(MAKE)
$(MAKE) test
$(MAKE) media
ci-pr:
$(MAKE) clean
$(MAKE) all
$(MAKE)
$(MAKE) test
.PHONY: clean
clean:
$(MAKE) -C alpine clean
$(MAKE) -C kernel clean
rm -rf bin disk.img test.log Dockerfile.media
rm -rf bin disk.img test.log *-initrd.img *-bzImage *.iso

1
alpine/.gitignore vendored
View File

@@ -1 +0,0 @@
etc/moby-commit

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -1,12 +0,0 @@
#!/sbin/openrc-run
# do nothing as we do this in automount script
start()
{
return 0
}
stop()
{
return 0
}

View File

@@ -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"
}

View File

@@ -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}
}

View File

@@ -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

View File

@@ -1,12 +0,0 @@
Welcome to Moby
## .
## ## ## ==
## ## ## ## ## ===
/"""""""""""""""""\___/ ===
~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ / ===- ~~~
\______ o __/
\ \ __/
\____\_______/

View File

@@ -1 +0,0 @@
Welcome to Moby

View File

@@ -1,2 +0,0 @@
auto lo
iface lo inet loopback

View File

@@ -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

View File

@@ -1,4 +0,0 @@
#!/bin/sh
# TODO check with hdparm if supports TRIM
/sbin/fstrim /var || true

View File

@@ -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

View File

@@ -1,2 +0,0 @@
kernel.modules_disabled=1
kernel.sysrq = 0

View File

@@ -1,3 +0,0 @@
fs/cgroup/memory/memory.use_hierarchy = 1
kernel/mm/transparent_hugepage/enabled = madvise
kernel/mm/transparent_hugepage/defrag = madvise

View File

@@ -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

View File

@@ -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

View File

@@ -1 +0,0 @@
sbin/

View File

@@ -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 */
}
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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"
}

View File

@@ -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

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -1 +0,0 @@
usr/bin/diagnostics-server

View File

@@ -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:

View File

@@ -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()
}

View File

@@ -1,11 +0,0 @@
#!/sbin/openrc-run
depend()
{
after docker containerd
}
start()
{
/usr/bin/diagnostics
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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&timestamps=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")
}
}

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -1 +0,0 @@
usr/

View File

@@ -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:

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -1 +0,0 @@
usr

View File

@@ -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:

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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}

View File

@@ -1 +0,0 @@
usr/

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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"
}

View File

@@ -1,3 +0,0 @@
proxy
usr/
sbin/

View File

@@ -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.

View File

@@ -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:

View File

@@ -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

View File

@@ -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"
}

View File

@@ -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))
}
}

View File

@@ -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"))
}
}

View File

@@ -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
}

View File

@@ -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 }

View File

@@ -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
}

View File

@@ -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")
}

View File

@@ -1,14 +0,0 @@
package main
import (
"os"
"path"
)
func main() {
if path.Base(os.Args[0]) == "proxy-vsockd" {
manyPorts()
return
}
onePort()
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -1 +0,0 @@
sbin

View File

@@ -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

View File

@@ -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"
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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_ */

View File

@@ -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;
}

View File

@@ -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
);

View File

@@ -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;
}
}
}

View File

@@ -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
}

View File

@@ -1 +0,0 @@
sbin

View File

@@ -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

View File

@@ -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

View File

@@ -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_ */

View File

@@ -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", &params->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", &params->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", &params->ctl_lock);
vlog_time_locked(params, TRANSFUSE_LOG_ERROR, fmt, args);
unlock("log_time ctl_lock", &params->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", &params->ctl_lock);
vlog_time_locked(params, TRANSFUSE_LOG_NOTICE, fmt, args);
unlock("log_time ctl_lock", &params->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", &params->ctl_lock);
vlog_locked(params, TRANSFUSE_LOG_ERROR, fmt, args);
unlock("log_continue ctl_lock", &params->ctl_lock);
va_end(args);
}

View File

@@ -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_ */

View File

@@ -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