├── warden ├── tmp │ └── .gitkeep ├── root │ ├── .gitignore │ ├── insecure │ │ ├── skeleton │ │ │ ├── bin │ │ │ │ └── .gitkeep │ │ │ ├── jobs │ │ │ │ └── .gitkeep │ │ │ ├── setup.sh │ │ │ ├── run.sh │ │ │ └── stop.sh │ │ ├── README.md │ │ ├── destroy.sh │ │ ├── create.sh │ │ └── clear.sh │ └── linux │ │ ├── skeleton │ │ ├── bin │ │ │ └── .gitkeep │ │ ├── etc │ │ │ └── .gitkeep │ │ ├── jobs │ │ │ └── .gitkeep │ │ ├── run │ │ │ └── .gitkeep │ │ ├── tmp │ │ │ └── .gitkeep │ │ ├── lib │ │ │ ├── hook-child-before-pivot.sh │ │ │ ├── hook-parent-before-clone.sh │ │ │ ├── hook-child-after-pivot.sh │ │ │ ├── common.sh │ │ │ └── hook-parent-after-clone.sh │ │ ├── start.sh │ │ ├── destroy.sh │ │ ├── stop.sh │ │ ├── net_rate.sh │ │ ├── setup.sh │ │ └── net.sh │ │ ├── README.md │ │ ├── .gitignore │ │ ├── destroy.sh │ │ ├── clear.sh │ │ ├── create.sh │ │ ├── setup.sh │ │ └── base │ │ └── setup.sh ├── src │ ├── oom │ │ ├── .gitignore │ │ ├── Makefile │ │ └── oom.c │ ├── closefds │ │ ├── .gitignore │ │ ├── Makefile │ │ └── closefds.c │ ├── repquota │ │ ├── .gitignore │ │ └── Makefile │ ├── wsh │ │ ├── .gitignore │ │ ├── mount.h │ │ ├── Makefile.dep │ │ ├── un.h │ │ ├── util.h │ │ ├── Makefile │ │ ├── pump.h │ │ ├── barrier.h │ │ ├── msg.h │ │ ├── barrier.c │ │ ├── util.c │ │ ├── pump.c │ │ └── un.c │ ├── iomux │ │ ├── .gitignore │ │ ├── test │ │ │ ├── test_muxer.h │ │ │ ├── test_ring_buffer.h │ │ │ ├── test_status_writer.h │ │ │ ├── test.c │ │ │ ├── Makefile │ │ │ ├── test_util.h │ │ │ ├── test_ring_buffer.c │ │ │ └── test_status_writer.c │ │ ├── barrier.h │ │ ├── dlog.h │ │ ├── status_reader.h │ │ ├── pump.h │ │ ├── Makefile │ │ ├── muxer.h │ │ ├── dlog.c │ │ ├── child.h │ │ ├── status_reader.c │ │ ├── status_writer.h │ │ ├── util.h │ │ ├── ring_buffer.h │ │ ├── barrier.c │ │ ├── pump.c │ │ ├── child.c │ │ └── ring_buffer.c │ └── Makefile ├── .rspec ├── lib │ └── warden │ │ ├── errors.rb │ │ ├── util.rb │ │ ├── container.rb │ │ ├── event_emitter.rb │ │ ├── pool │ │ ├── network.rb │ │ ├── uid.rb │ │ ├── base.rb │ │ └── port.rb │ │ ├── container │ │ ├── features │ │ │ ├── cgroup.rb │ │ │ └── mem_limit.rb │ │ └── insecure.rb │ │ ├── network.rb │ │ └── repl_v2_runner.rb ├── .gitignore ├── bin │ ├── warden │ └── warden-repl ├── spec │ ├── support │ │ └── examples │ │ │ ├── info.rb │ │ │ ├── snapshotting.rb │ │ │ ├── running_commands.rb │ │ │ └── streaming_commands.rb │ ├── pool │ │ ├── network_spec.rb │ │ ├── uid_spec.rb │ │ ├── port_spec.rb │ │ └── base_spec.rb │ ├── network_spec.rb │ └── health_check_spec.rb ├── Gemfile ├── TODO.md ├── config │ └── linux.yml ├── Gemfile.lock └── Rakefile ├── warden-client ├── tmp │ └── .gitkeep ├── .gitignore ├── lib │ └── warden │ │ ├── client │ │ └── version.rb │ │ └── client.rb ├── spec │ ├── spec_helper.rb │ └── support │ │ └── mock_warden_server.rb ├── Gemfile ├── Rakefile └── warden-client.gemspec ├── warden-protocol ├── .rspec ├── Gemfile ├── lib │ └── warden │ │ ├── protocol │ │ ├── version.rb │ │ ├── error.rb │ │ ├── ping.rb │ │ ├── list.rb │ │ ├── destroy.rb │ │ ├── echo.rb │ │ ├── stop.rb │ │ ├── copy_in.rb │ │ ├── net_out.rb │ │ ├── limit_memory.rb │ │ ├── copy_out.rb │ │ ├── link.rb │ │ ├── stream.rb │ │ ├── net_in.rb │ │ ├── limit_bandwidth.rb │ │ ├── spawn.rb │ │ ├── run.rb │ │ ├── resource_limits.rb │ │ ├── create.rb │ │ ├── buffer.rb │ │ ├── limit_disk.rb │ │ └── info.rb │ │ └── protocol.rb ├── spec │ ├── support │ │ ├── examples │ │ │ ├── documented_request.rb │ │ │ ├── wrappable_request.rb │ │ │ └── wrappable_reply.rb │ │ ├── matchers.rb │ │ └── helper.rb │ ├── spec_helper.rb │ ├── error_spec.rb │ ├── ping_spec.rb │ ├── destroy_spec.rb │ ├── list_spec.rb │ ├── stop_spec.rb │ ├── echo_spec.rb │ ├── net_out_spec.rb │ ├── limit_memory_spec.rb │ ├── copy_in_spec.rb │ ├── copy_out_spec.rb │ ├── link_spec.rb │ ├── stream_spec.rb │ ├── spawn_spec.rb │ ├── net_in_spec.rb │ ├── limit_bandwidth_spec.rb │ ├── run_spec.rb │ ├── create_spec.rb │ ├── resource_limits_spec.rb │ ├── buffer_spec.rb │ ├── limit_disk_spec.rb │ └── info_spec.rb ├── .gitignore ├── CHANGELOG.md ├── Rakefile └── warden-protocol.gemspec ├── em-warden-client ├── lib │ └── em │ │ └── warden │ │ ├── client │ │ ├── version.rb │ │ ├── error.rb │ │ ├── event_emitter.rb │ │ └── connection.rb │ │ └── client.rb ├── Gemfile ├── .gitignore ├── Rakefile ├── spec │ ├── spec_helper.rb │ └── support │ │ └── mock_warden_server.rb ├── README.md ├── em-warden-client.gemspec └── LICENSE ├── .gitignore ├── README.md └── NOTICE /warden/tmp/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /warden-client/tmp/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /warden/root/.gitignore: -------------------------------------------------------------------------------- 1 | /*/instances 2 | -------------------------------------------------------------------------------- /warden/root/insecure/skeleton/bin/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /warden/root/insecure/skeleton/jobs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/bin/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/etc/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/jobs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/run/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/tmp/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /warden/src/oom/.gitignore: -------------------------------------------------------------------------------- 1 | oom 2 | *.o 3 | -------------------------------------------------------------------------------- /warden/root/insecure/README.md: -------------------------------------------------------------------------------- 1 | # Placeholder 2 | -------------------------------------------------------------------------------- /warden/root/linux/README.md: -------------------------------------------------------------------------------- 1 | # Placeholder 2 | -------------------------------------------------------------------------------- /warden/root/linux/.gitignore: -------------------------------------------------------------------------------- 1 | /*/rootfs 2 | /tmp 3 | -------------------------------------------------------------------------------- /warden/src/closefds/.gitignore: -------------------------------------------------------------------------------- 1 | closefds 2 | *.o 3 | -------------------------------------------------------------------------------- /warden/src/repquota/.gitignore: -------------------------------------------------------------------------------- 1 | repquota 2 | *.o 3 | -------------------------------------------------------------------------------- /warden/src/wsh/.gitignore: -------------------------------------------------------------------------------- 1 | wshd 2 | wsh 3 | *.o 4 | -------------------------------------------------------------------------------- /warden-protocol/.rspec: -------------------------------------------------------------------------------- 1 | --fail-fast --backtrace --color 2 | -------------------------------------------------------------------------------- /warden/src/iomux/.gitignore: -------------------------------------------------------------------------------- 1 | iomux-spawn 2 | iomux-link 3 | *.o 4 | -------------------------------------------------------------------------------- /warden/.rspec: -------------------------------------------------------------------------------- 1 | --fail-fast --format documentation --backtrace --color 2 | -------------------------------------------------------------------------------- /warden-client/.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .bundle 3 | Gemfile.lock 4 | pkg/* 5 | /tmp 6 | -------------------------------------------------------------------------------- /warden-client/lib/warden/client/version.rb: -------------------------------------------------------------------------------- 1 | module Warden 2 | class Client 3 | VERSION = "0.0.7" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /warden/src/iomux/test/test_muxer.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_MUXER 2 | #define TEST_MUXER 1 3 | 4 | void test_muxer(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /warden/src/wsh/mount.h: -------------------------------------------------------------------------------- 1 | #ifndef MOUNT_H 2 | #define MOUNT_H 1 3 | 4 | int mount_umount_pivoted_root(const char *path); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /warden-client/spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "rspec" 2 | require "warden/client" 3 | 4 | RSpec.configure do |config| 5 | # not much to do here... 6 | end 7 | -------------------------------------------------------------------------------- /warden-protocol/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in warden-protocol.gemspec 4 | gemspec 5 | 6 | gem 'rake' -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/version.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | module Warden 4 | module Protocol 5 | VERSION = "0.0.12" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /warden/src/iomux/test/test_ring_buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_RING_BUFFER_H 2 | #define TEST_RING_BUFFER_H 1 3 | 4 | void test_ring_buffer(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /warden/src/iomux/test/test_status_writer.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_STATUS_WRITER 2 | #define TEST_STATUS_WRITER 1 3 | 4 | void test_status_writer(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /em-warden-client/lib/em/warden/client/version.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | module Warden 3 | module Client 4 | VERSION = "0.1.1" 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .idea 3 | \#*\# 4 | .\#* 5 | .bundle 6 | bundler 7 | spec_reports 8 | spec_coverage 9 | ci-artifacts-dir 10 | ci-working-dir 11 | *.rbc 12 | *.swp 13 | .rvmrc 14 | *.pid 15 | -------------------------------------------------------------------------------- /warden-client/Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec 4 | 5 | gem "rake" 6 | gem "warden-protocol", :path => "../warden-protocol" 7 | 8 | group :spec do 9 | gem "rspec" 10 | end 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## File a Bug 2 | 3 | To file a bug against Cloud Foundry Open Source and its components, sign up and use our bug tracking system: [http://cloudfoundry.atlassian.net](http://cloudfoundry.atlassian.net) 4 | -------------------------------------------------------------------------------- /warden/lib/warden/errors.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | module Warden 4 | 5 | class WardenError < StandardError 6 | 7 | def message 8 | super || "unknown error" 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /warden/lib/warden/util.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | module Warden 4 | module Util 5 | def self.path(*args) 6 | File.expand_path(File.join("..", "..", "..", *args), __FILE__) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /warden-protocol/spec/support/examples/documented_request.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | shared_examples "documented request" do 4 | it "should have a description" do 5 | described_class.description.size.should be > 0 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /warden/root/insecure/skeleton/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname "$0") 9 | 10 | # Setup structure 11 | mkdir -p root pids 12 | -------------------------------------------------------------------------------- /em-warden-client/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in em-warden-client.gemspec 4 | gemspec 5 | 6 | gem "warden-client", :path => "../warden-client" 7 | gem "warden-protocol", :path => "../warden-protocol" 8 | -------------------------------------------------------------------------------- /warden-client/Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | require "rspec/core/version" 4 | 5 | desc "Run all examples" 6 | RSpec::Core::RakeTask.new(:spec) do |t| 7 | t.rspec_opts = %w[--color --format documentation] 8 | end 9 | -------------------------------------------------------------------------------- /em-warden-client/.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | -------------------------------------------------------------------------------- /em-warden-client/lib/em/warden/client/error.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | module Warden 3 | module Client 4 | class Error < StandardError 5 | end 6 | 7 | class ConnectionError < Error 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /warden-protocol/.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/lib/hook-child-before-pivot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname $0)/../ 9 | 10 | source ./lib/common.sh 11 | 12 | # Placeholder 13 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/lib/hook-parent-before-clone.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname $0)/../ 9 | 10 | source ./lib/common.sh 11 | 12 | setup_fs 13 | -------------------------------------------------------------------------------- /warden-protocol/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.3 2 | 3 | * Ignore string encoding when a request or response is encoded 4 | 5 | ## 0.0.2 6 | 7 | ### `StopRequest` 8 | 9 | * Added `background` flag 10 | * Added `kill` flag 11 | 12 | ## 0.0.1 13 | 14 | * Initial version. 15 | -------------------------------------------------------------------------------- /warden/src/iomux/test/test.c: -------------------------------------------------------------------------------- 1 | #include "test_muxer.h" 2 | #include "test_ring_buffer.h" 3 | #include "test_status_writer.h" 4 | 5 | int main(int argc, char **argv) { 6 | test_ring_buffer(); 7 | test_muxer(); 8 | test_status_writer(); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /warden-protocol/spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "rspec" 4 | require "rspec/autorun" 5 | require "warden/protocol" 6 | 7 | Dir["./spec/support/**/*.rb"].each { |f| require f } 8 | 9 | RSpec.configure do |config| 10 | config.include(Helper) 11 | end 12 | -------------------------------------------------------------------------------- /warden/.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .bundle 3 | pkg/* 4 | /tmp 5 | root/linux/skeleton/bin/wsh 6 | root/linux/skeleton/bin/wshd 7 | root/linux/skeleton/bin/iomux-link 8 | root/linux/skeleton/bin/iomux-spawn 9 | root/insecure/skeleton/bin/iomux-link 10 | root/insecure/skeleton/bin/iomux-spawn 11 | -------------------------------------------------------------------------------- /warden/src/Makefile: -------------------------------------------------------------------------------- 1 | default: all 2 | 3 | # Proxy any target to the Makefiles in the per-tool directories 4 | %: 5 | cd wsh && $(MAKE) $@ 6 | cd oom && $(MAKE) $@ 7 | cd repquota && $(MAKE) $@ 8 | cd iomux && $(MAKE) $@ 9 | cd closefds && $(MAKE) $@ 10 | 11 | .PHONY: default 12 | -------------------------------------------------------------------------------- /warden/src/wsh/Makefile.dep: -------------------------------------------------------------------------------- 1 | barrier.o: barrier.c barrier.h util.h 2 | mount.o: mount.c mount.h 3 | msg.o: msg.c msg.h 4 | pump.o: pump.c pump.h util.h 5 | un.o: un.c un.h util.h 6 | util.o: util.c util.h 7 | wsh.o: wsh.c msg.h pump.h un.h 8 | wshd.o: wshd.c barrier.h msg.h mount.h un.h util.h 9 | -------------------------------------------------------------------------------- /em-warden-client/Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | require "bundler/gem_tasks" 3 | require "rspec/core/rake_task" 4 | require "rspec/core/version" 5 | 6 | desc "Run all examples" 7 | RSpec::Core::RakeTask.new(:spec) do |t| 8 | t.rspec_opts = %w[--color --format documentation] 9 | end 10 | -------------------------------------------------------------------------------- /warden/src/oom/Makefile: -------------------------------------------------------------------------------- 1 | OPTIMIZATION?=-O0 2 | DEBUG?=-g -ggdb -rdynamic 3 | 4 | all: oom 5 | 6 | clean: 7 | rm -f *.o oom 8 | 9 | .PHONY: all clean 10 | 11 | oom: oom.o 12 | $(CC) -o $@ $^ -lutil 13 | 14 | %.o: %.c 15 | $(CC) -c -Wall -D_GNU_SOURCE $(OPTIMIZATION) $(DEBUG) $(CFLAGS) $< 16 | -------------------------------------------------------------------------------- /warden/src/wsh/un.h: -------------------------------------------------------------------------------- 1 | #ifndef UN_H 2 | #define UN_H 1 3 | 4 | int un_listen(const char *path); 5 | int un_connect(const char *path); 6 | int un_send_fds(int fd, char *data, int datalen, int *fds, int fdslen); 7 | int un_recv_fds(int fd, char *data, int datalen, int *fds, int fdslen); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /warden/bin/warden: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", __FILE__) 4 | $LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__)) 5 | 6 | require "bundler" 7 | Bundler.setup 8 | 9 | require "warden/repl_v2_runner" 10 | 11 | Warden::ReplRunner.run(ARGV) 12 | -------------------------------------------------------------------------------- /warden/src/iomux/barrier.h: -------------------------------------------------------------------------------- 1 | #ifndef BARRIER_H 2 | #define BARRIER_H 1 3 | 4 | typedef struct barrier_s barrier_t; 5 | 6 | barrier_t *barrier_alloc(void); 7 | 8 | void barrier_lift(barrier_t *barrier); 9 | 10 | void barrier_wait(barrier_t *barrier); 11 | 12 | void barrier_free(barrier_t *barrier); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /warden/src/repquota/Makefile: -------------------------------------------------------------------------------- 1 | OPTIMIZATION?=-O0 2 | DEBUG?=-g -ggdb -rdynamic 3 | 4 | all: repquota 5 | 6 | clean: 7 | rm -f *.o repquota 8 | 9 | .PHONY: all clean 10 | 11 | repquota: repquota.o 12 | $(CC) -o $@ $^ -lutil 13 | 14 | %.o: %.c 15 | $(CC) -c -Wall -D_GNU_SOURCE $(OPTIMIZATION) $(DEBUG) $(CFLAGS) $< 16 | -------------------------------------------------------------------------------- /warden/root/insecure/skeleton/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname "$0") 9 | 10 | touch pids/$$ 11 | 12 | # Run script with PWD=root 13 | cd root 14 | 15 | # Replace process with bash interpreting stdin 16 | exec env -i bash 17 | -------------------------------------------------------------------------------- /warden/src/closefds/Makefile: -------------------------------------------------------------------------------- 1 | OPTIMIZATION?=-O0 2 | DEBUG?=-g -ggdb -rdynamic 3 | 4 | all: closefds 5 | 6 | clean: 7 | rm -f *.o closefds 8 | 9 | install: all 10 | # noop 11 | 12 | .PHONY: all clean 13 | 14 | closefds: closefds.o 15 | $(CC) -o $@ $^ 16 | 17 | %.o: %.c 18 | $(CC) -c -Wall $(OPTIMIZATION) $(DEBUG) $(CFLAGS) $< 19 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/error.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class ErrorResponse < BaseResponse 8 | optional :message, :string, 2 9 | optional :data, :string, 4 10 | repeated :backtrace, :string, 3 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/ping.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class PingRequest < BaseRequest 8 | def self.description 9 | "Ping warden." 10 | end 11 | end 12 | 13 | class PingResponse < BaseResponse 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /warden/lib/warden/container.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | # Insecure container should be available on all platforms 4 | require "warden/container/insecure" 5 | 6 | # Require Linux container only when running on Linux 7 | if RUBY_PLATFORM =~ /linux/i 8 | require "warden/container/linux" 9 | else 10 | # Define stub 11 | class Warden::Container::Linux; end 12 | end 13 | -------------------------------------------------------------------------------- /warden/src/iomux/dlog.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG_H 2 | #define LOG_H 1 3 | 4 | #ifdef DEBUG 5 | 6 | void _dlog(const char *file, const char *func, int line, 7 | const char *format, ...); 8 | 9 | #define DLOG(format, ...) _dlog(__FILE__, __FUNCTION__, __LINE__, (format), ##__VA_ARGS__) 10 | 11 | #else 12 | 13 | #define DLOG(format, ...) 14 | 15 | #endif 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/list.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class ListRequest < BaseRequest 8 | def self.description 9 | "List containers." 10 | end 11 | end 12 | 13 | class ListResponse < BaseResponse 14 | repeated :handles, :string, 1 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /warden/src/wsh/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #define fcntl_mix_cloexec(fd) fcntl_set_cloexec((fd), 1) 5 | #define fcntl_mix_nonblock(fd) fcntl_set_nonblock((fd), 1) 6 | 7 | void fcntl_set_cloexec(int fd, int on); 8 | void fcntl_set_nonblock(int fd, int on); 9 | int run(const char *p1, const char *p2); 10 | void setproctitle(char **argv, const char *title); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /warden/spec/support/examples/info.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | shared_examples "info" do 4 | attr_reader :handle 5 | 6 | before do 7 | @handle = client.create.handle 8 | end 9 | 10 | it "should respond to an info request" do 11 | response = client.info(:handle => handle) 12 | response.state.should == "active" 13 | response.container_path.should_not be_nil 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/destroy.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class DestroyRequest < BaseRequest 8 | required :handle, :string, 1 9 | 10 | def self.description 11 | "Shutdown a container." 12 | end 13 | end 14 | 15 | class DestroyResponse < BaseResponse 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname $0) 9 | 10 | source ./etc/config 11 | 12 | if [ -f ./run/wshd.pid ] 13 | then 14 | echo "wshd is already running..." 15 | exit 1 16 | fi 17 | 18 | ./net.sh setup 19 | 20 | ./bin/wshd --run ./run --lib ./lib --root ./mnt --title "wshd: $id" 21 | -------------------------------------------------------------------------------- /warden/root/linux/destroy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | if [ $# -ne 1 ]; then 9 | echo "Usage: ${0} " 10 | exit 1 11 | fi 12 | 13 | target=${1} 14 | 15 | if [ -d "${target}" ]; then 16 | if [ -f "${target}/destroy.sh" ]; then 17 | ${target}/destroy.sh 18 | fi 19 | 20 | rm -rf ${target} 21 | fi 22 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/echo.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class EchoRequest < BaseRequest 8 | required :message, :string, 1 9 | 10 | def self.description 11 | "Echo a message." 12 | end 13 | end 14 | 15 | class EchoResponse < BaseResponse 16 | required :message, :string, 1 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /warden/root/linux/clear.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | if [ $# -ne 1 ]; then 9 | echo "Usage: ${0} " 10 | exit 1 11 | fi 12 | 13 | instances_path=${1} 14 | 15 | cd $(dirname "${0}") 16 | 17 | for instance in ${instances_path}/*; do 18 | echo "Destroying ${instance}" 19 | ./destroy.sh ${instance} & 20 | done 21 | 22 | wait 23 | -------------------------------------------------------------------------------- /warden/src/iomux/status_reader.h: -------------------------------------------------------------------------------- 1 | #ifndef STATUS_READER_H 2 | #define STATUS_READER_H 1 3 | 4 | #include 5 | 6 | typedef struct status_reader_s status_reader_t; 7 | 8 | struct status_reader_s { 9 | int off; 10 | uint8_t buf[4]; 11 | int status; 12 | int fd; 13 | }; 14 | 15 | void status_reader_init(status_reader_t *reader, int fd); 16 | 17 | int status_reader_run(status_reader_t *reader, uint8_t *hup); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /warden/root/insecure/destroy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname "$0") 9 | 10 | if [ -z "${1}" ]; then 11 | echo "Usage: ${0} " 12 | exit 1 13 | fi 14 | 15 | target=${1} 16 | 17 | if [ -d "${target}" ]; then 18 | if [ -f "${target}/stop.sh" ]; then 19 | ${target}/stop.sh 20 | fi 21 | 22 | rm -rf ${target} 23 | fi 24 | -------------------------------------------------------------------------------- /warden/src/iomux/pump.h: -------------------------------------------------------------------------------- 1 | #ifndef PUMP_H 2 | #define PUMP_H 1 3 | 4 | #include 5 | 6 | typedef struct { 7 | int state; 8 | 9 | uint8_t pos_bytes[4]; 10 | uint8_t pos_off; 11 | 12 | uint32_t old_pos; 13 | uint32_t pos; 14 | 15 | int src_fd; 16 | int dst_fd; 17 | } pump_t; 18 | 19 | void pump_setup(pump_t *pump, int src_fd, int dst_fd, uint32_t old_pos); 20 | 21 | int pump_run(pump_t *pump); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /warden/src/iomux/test/Makefile: -------------------------------------------------------------------------------- 1 | OPTIMIZATION?=-O0 2 | DEBUG?=-g -ggdb -rdynamic 3 | VPATH=../ 4 | all: test 5 | 6 | clean: 7 | rm -f *.o test 8 | 9 | .PHONY: all clean 10 | 11 | test: test.o ring_buffer.o util.o test_ring_buffer.o test_muxer.o muxer.o barrier.o status_writer.o status_reader.o test_status_writer.o dlog.o 12 | $(CC) -o $@ $^ -lpthread 13 | 14 | %.o: %.c 15 | $(CC) -c -Wall -D_GNU_SOURCE -I../ $(OPTIMIZATION) $(DEBUG) $(CFLAGS) $< 16 | -------------------------------------------------------------------------------- /em-warden-client/spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rspec' 2 | require 'em/warden/client' 3 | 4 | def em(options = {}) 5 | raise "no block given" unless block_given? 6 | timeout = options[:timeout] ||= 1.0 7 | 8 | ::EM.run do 9 | quantum = 0.005 10 | ::EM.set_quantum(quantum * 1000) # Lowest possible timer resolution 11 | ::EM.set_heartbeat_interval(quantum) # Timeout connections asap 12 | ::EM.add_timer(timeout) { raise "timeout" } 13 | yield 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /warden/root/insecure/create.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname "$0") 9 | 10 | if [ -z "${1}" ]; then 11 | echo "Usage: ${0} " 12 | exit 1 13 | fi 14 | 15 | target=${1} 16 | 17 | if [ -d ${target} ]; then 18 | echo "\"${target}\" already exists, aborting..." 19 | exit 1 20 | fi 21 | 22 | cp -r skeleton "${target}" 23 | "${target}"/setup.sh 24 | echo ${target} 25 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/stop.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class StopRequest < BaseRequest 8 | required :handle, :string, 1 9 | optional :background, :bool, 10 10 | optional :kill, :bool, 20 11 | 12 | def self.description 13 | "Stop all processes inside a container." 14 | end 15 | end 16 | 17 | class StopResponse < BaseResponse 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /warden/root/linux/create.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname "${0}") 9 | 10 | if [ $# -ne 1 ]; then 11 | echo "Usage: ${0} " 12 | exit 1 13 | fi 14 | 15 | target=${1} 16 | 17 | if [ -d ${target} ]; then 18 | echo "\"${target}\" already exists, aborting..." 19 | exit 1 20 | fi 21 | 22 | cp -r skeleton "${target}" 23 | unshare -m "${target}"/setup.sh 24 | echo ${target} 25 | -------------------------------------------------------------------------------- /warden/src/iomux/test/test_util.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_H 2 | #define TEST_H 1 3 | 4 | #include 5 | 6 | #define TEST_CHECK(p) do { \ 7 | printf("%s - %s %s:%d\n", \ 8 | (p) ? "PASS" : "FAIL", \ 9 | #p, \ 10 | __FILE__, __LINE__); \ 11 | } while (0) 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/copy_in.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class CopyInRequest < BaseRequest 8 | required :handle, :string, 1 9 | required :src_path, :string, 2 10 | required :dst_path, :string, 3 11 | 12 | def self.description 13 | "Copy files/directories into the container." 14 | end 15 | end 16 | 17 | class CopyInResponse < BaseResponse 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/net_out.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class NetOutRequest < BaseRequest 8 | required :handle, :string, 1 9 | optional :network, :string, 2 10 | optional :port, :uint32, 3 11 | 12 | def self.description 13 | "Allow traffic from the container to address." 14 | end 15 | end 16 | 17 | class NetOutResponse < BaseResponse 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /warden/root/insecure/clear.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname "$0") 9 | 10 | if [ $# -ne 1 ]; then 11 | echo "Usage: ${0} " 12 | exit 1 13 | fi 14 | 15 | instances_path=${1} 16 | 17 | for instance in instances_path/*; do 18 | [ -f ${instance}/stop.sh ] && ${instance}/stop.sh || true 19 | done 20 | 21 | sleep 0.1 22 | 23 | for instance in instances_path/*; do 24 | rm -rf ${instance} 25 | done 26 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Cloud Foundry Warden, The Cloud Jailer 2 | Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved. 3 | 4 | This product is licensed to you under the Apache License, Version 2.0 (the "License"). 5 | You may not use this product except in compliance with the License. 6 | 7 | This product includes a number of subcomponents with 8 | separate copyright notices and license terms. Your use of these 9 | subcomponents is subject to the terms and conditions of the 10 | subcomponent's license, as noted in the LICENSE file. 11 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/limit_memory.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class LimitMemoryRequest < BaseRequest 8 | required :handle, :string, 1 9 | optional :limit_in_bytes, :uint64, 2 10 | 11 | def self.description 12 | "Set or get the memory limit for the container." 13 | end 14 | end 15 | 16 | class LimitMemoryResponse < BaseResponse 17 | optional :limit_in_bytes, :uint64, 1 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/copy_out.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class CopyOutRequest < BaseRequest 8 | required :handle, :string, 1 9 | required :src_path, :string, 2 10 | required :dst_path, :string, 3 11 | optional :owner, :string, 4 12 | 13 | def self.description 14 | "Copy files/directories out of the container." 15 | end 16 | end 17 | 18 | class CopyOutResponse < BaseResponse 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /warden-protocol/spec/support/matchers.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | RSpec::Matchers.define :be_valid do 4 | error = nil 5 | 6 | match do |actual| 7 | begin 8 | actual.class.decode(actual.encode) 9 | rescue => error 10 | end 11 | 12 | error == nil 13 | end 14 | 15 | failure_message_for_should do |actual| 16 | "expected #{actual} to be valid, but caught error: #{error}" 17 | end 18 | 19 | failure_message_for_should_not do |actual| 20 | "expected #{actual} not to be valid, but didn't catch an error" 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /warden/src/wsh/Makefile: -------------------------------------------------------------------------------- 1 | OPTIMIZATION?=-O0 2 | DEBUG?=-g -ggdb -rdynamic 3 | 4 | all: wshd wsh 5 | 6 | clean: 7 | rm -f *.o clone wshd wsh 8 | 9 | install: all 10 | cp wshd wsh ../../root/linux/skeleton/bin/ 11 | 12 | .PHONY: all clean 13 | 14 | wshd: wshd.o barrier.o mount.o un.o util.o msg.o 15 | $(CC) -o $@ $^ -lutil 16 | 17 | wsh: wsh.o pump.o un.o util.o msg.o 18 | $(CC) -o $@ $^ -lutil 19 | 20 | %.o: %.c 21 | $(CC) -c -Wall $(OPTIMIZATION) $(DEBUG) $(CFLAGS) $< 22 | 23 | -include Makefile.dep 24 | 25 | dep: 26 | $(CC) -MM *.c > Makefile.dep 27 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/link.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class LinkRequest < BaseRequest 8 | required :handle, :string, 1 9 | required :job_id, :uint32, 2 10 | 11 | def self.description 12 | "Do blocking read on results from a job." 13 | end 14 | end 15 | 16 | class LinkResponse < BaseResponse 17 | optional :exit_status, :uint32, 1 18 | optional :stdout, :string, 2 19 | optional :stderr, :string, 3 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/stream.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class StreamRequest < BaseRequest 8 | required :handle, :string, 1 9 | required :job_id, :uint32, 2 10 | 11 | def self.description 12 | "Do blocking stream on results from a job." 13 | end 14 | end 15 | 16 | class StreamResponse < BaseResponse 17 | optional :name, :string, 1 18 | optional :data, :string, 2 19 | optional :exit_status, :uint32, 3 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /warden/src/iomux/Makefile: -------------------------------------------------------------------------------- 1 | OPTIMIZATION?=-O0 2 | DEBUG?=-g -ggdb -rdynamic 3 | 4 | all: iomux-spawn iomux-link 5 | 6 | clean: 7 | rm -f *.o iomux-spawn iomux-link 8 | cd test && $(MAKE) $@ 9 | 10 | .PHONY: all clean 11 | 12 | test: 13 | cd test && $(MAKE) $@ 14 | 15 | iomux-spawn: iomux-spawn.o ring_buffer.o muxer.o status_writer.o child.o util.o barrier.o dlog.o 16 | $(CC) -o $@ $^ -lpthread 17 | 18 | iomux-link: iomux-link.o pump.o status_reader.o util.o 19 | $(CC) -o $@ $^ -lpthread 20 | 21 | %.o: %.c 22 | $(CC) -c -Wall -D_GNU_SOURCE $(OPTIMIZATION) $(DEBUG) $(CFLAGS) $< 23 | -------------------------------------------------------------------------------- /warden/Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gem "rake" 4 | gem "steno", "~> 0.0.15", :git => 'https://github.com/cloudfoundry/steno.git' 5 | gem "membrane", :git => 'https://github.com/cloudfoundry/membrane.git' 6 | gem "eventmachine", "~> 1.0.0" 7 | gem "em-posix-spawn", :git => 'https://github.com/cloudfoundry/common.git' 8 | gem "sys-filesystem" 9 | 10 | gem "warden-protocol", :git => "https://github.com/cloudfoundry/warden.git", :ref => '968745ac' 11 | gem "warden-client", :git => "https://github.com/cloudfoundry/warden.git", :ref => '968745ac' 12 | 13 | group :spec do 14 | gem "rspec" 15 | end 16 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/net_in.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class NetInRequest < BaseRequest 8 | required :handle, :string, 1 9 | optional :container_port, :uint32, 2 10 | optional :host_port, :uint32, 3 11 | 12 | def self.description 13 | "Forward port on external interface to container." 14 | end 15 | end 16 | 17 | class NetInResponse < BaseResponse 18 | required :host_port, :uint32, 1 19 | required :container_port, :uint32, 2 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/lib/hook-child-after-pivot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname $0)/../ 9 | 10 | source ./lib/common.sh 11 | 12 | mkdir -p /dev/pts 13 | mount -t devpts -o newinstance,ptmxmode=0666 devpts /dev/pts 14 | ln -sf pts/ptmx /dev/ptmx 15 | 16 | mkdir -p /proc 17 | mount -t proc none /proc 18 | 19 | hostname $id 20 | 21 | ifconfig lo 127.0.0.1 22 | ifconfig $network_container_iface $network_container_ip netmask $network_netmask 23 | route add default gw $network_host_ip $network_container_iface 24 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/limit_bandwidth.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class LimitBandwidthRequest < BaseRequest 8 | required :handle, :string, 1 9 | optional :rate, :uint64, 2 # Bandwidth rate in byte(s)/sec 10 | optional :burst, :uint64, 3 # Allow burst size in byte(s) 11 | end 12 | 13 | class LimitBandwidthResponse < BaseResponse 14 | optional :rate, :uint64, 1 # Bandwidth rate in byte(s)/sec 15 | optional :burst, :uint64, 2 # Allow burst size in byte(s) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/spawn.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | require "warden/protocol/resource_limits" 5 | 6 | module Warden 7 | module Protocol 8 | class SpawnRequest < BaseRequest 9 | required :handle, :string, 1 10 | required :script, :string, 2 11 | optional :privileged, :bool, 3, :default => false 12 | optional :rlimits, ResourceLimits, 4 13 | 14 | def self.description 15 | "Spawns a command inside a container and returns the job id." 16 | end 17 | end 18 | 19 | class SpawnResponse < BaseResponse 20 | required :job_id, :uint32, 1 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /em-warden-client/README.md: -------------------------------------------------------------------------------- 1 | # Em::Warden::Client 2 | 3 | TODO: Write a gem description 4 | 5 | ## Installation 6 | 7 | Add this line to your application's Gemfile: 8 | 9 | gem 'em-warden-client' 10 | 11 | And then execute: 12 | 13 | $ bundle 14 | 15 | Or install it yourself as: 16 | 17 | $ gem install em-warden-client 18 | 19 | ## Usage 20 | 21 | TODO: Write usage instructions here 22 | 23 | ## Contributing 24 | 25 | 1. Fork it 26 | 2. Create your feature branch (`git checkout -b my-new-feature`) 27 | 3. Commit your changes (`git commit -am 'Added some feature'`) 28 | 4. Push to the branch (`git push origin my-new-feature`) 29 | 5. Create new Pull Request 30 | -------------------------------------------------------------------------------- /warden/src/iomux/muxer.h: -------------------------------------------------------------------------------- 1 | #ifndef MUXER_H 2 | #define MUXER_H 1 3 | 4 | #include 5 | 6 | typedef struct muxer_s muxer_t; 7 | 8 | /** 9 | * Allocates a new muxer 10 | * 11 | * @param accept_fd FD to listen on for new connections 12 | * @param source_fd The input fd. 13 | * @param ring_buf_size How much data should be buffered. 14 | * 15 | * @return A pointer to the new muxer 16 | */ 17 | muxer_t *muxer_alloc(int accept_fd, int source_fd, size_t ring_buf_size); 18 | 19 | void muxer_run(muxer_t *muxer); 20 | 21 | void muxer_wait_for_client(muxer_t *muxer); 22 | 23 | void muxer_stop(muxer_t *muxer); 24 | 25 | void muxer_free(muxer_t *muxer); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /warden/src/iomux/dlog.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "dlog.h" 8 | #include "util.h" 9 | 10 | static pthread_mutex_t stdout_lock = PTHREAD_MUTEX_INITIALIZER; 11 | 12 | void _dlog(const char *file, const char *func, int line, 13 | const char *format, ...) { 14 | va_list ap; 15 | 16 | va_start(ap, format); 17 | 18 | checked_lock(&stdout_lock); 19 | 20 | printf("thread=%ld %s:%s:%d -- ", syscall(SYS_gettid), file, func, line); 21 | vprintf(format, ap); 22 | printf("\n"); 23 | fflush(stdout); 24 | 25 | checked_unlock(&stdout_lock); 26 | 27 | va_end(ap); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /warden/TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | ## Near future 4 | 5 | * Use a pool of pre-created containers to avoid start-up latency. This pool can 6 | have a maximum size, and be lazily re-populated when containers are acquired 7 | from it. 8 | 9 | * Allocate a throw-away user for every container, and synchronize its UID on 10 | the host with its UID inside the container. This allows code on the host to 11 | modify files inside the container directly, instead of having to go through 12 | some kind of proxy. 13 | 14 | ## Far future 15 | 16 | * Implement a shim for the `warden` interface that only uses a chroot jail 17 | under the hood, so other platforms can run the same code without using actual 18 | containers. 19 | -------------------------------------------------------------------------------- /warden-protocol/Rakefile: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "rspec/core/rake_task" 4 | require "rspec/core/version" 5 | 6 | task :default => :spec 7 | 8 | desc "Run all examples" 9 | RSpec::Core::RakeTask.new(:spec) do |t| 10 | # See .rspec 11 | end 12 | 13 | task :ensure_coding do 14 | patterns = [ 15 | /Rakefile$/, 16 | /\.rb$/, 17 | ] 18 | 19 | files = `git ls-files`.split.select do |file| 20 | patterns.any? { |e| e.match(file) } 21 | end 22 | 23 | header = "# coding: UTF-8\n\n" 24 | 25 | files.each do |file| 26 | content = File.read(file) 27 | 28 | unless content.start_with?(header) 29 | File.open(file, "w") do |f| 30 | f.write(header) 31 | f.write(content) 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/run.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | require "warden/protocol/resource_limits" 5 | 6 | module Warden 7 | module Protocol 8 | class RunRequest < BaseRequest 9 | required :handle, :string, 1 10 | required :script, :string, 2 11 | optional :privileged, :bool, 3, :default => false 12 | optional :rlimits, ResourceLimits, 4 13 | 14 | def self.description 15 | "Short hand for spawn(link(cmd)) i.e. spawns a command, links to the result." 16 | end 17 | end 18 | 19 | class RunResponse < BaseResponse 20 | optional :exit_status, :uint32, 1 21 | optional :stdout, :string, 2 22 | optional :stderr, :string, 3 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /warden-protocol/spec/error_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/error" 5 | 6 | describe Warden::Protocol::ErrorResponse do 7 | subject(:response) do 8 | described_class.new 9 | end 10 | 11 | it_should_behave_like "wrappable response" 12 | 13 | it { should_not be_ok } 14 | it { should be_error } 15 | 16 | field :message do 17 | it_should_be_optional 18 | it_should_be_typed_as_string 19 | end 20 | 21 | field :data do 22 | it_should_be_optional 23 | it_should_be_typed_as_string 24 | end 25 | 26 | field :backtrace do 27 | it_should_be_optional 28 | 29 | it "should allow one or more entries" do 30 | subject.backtrace = ["a", "b"] 31 | subject.should be_valid 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /warden/src/iomux/child.h: -------------------------------------------------------------------------------- 1 | #ifndef CHILD_H 2 | #define CHILD_H 1 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef struct child_s child_t; 9 | 10 | struct child_s { 11 | char **argv; 12 | size_t argv_size; 13 | 14 | pid_t pid; 15 | 16 | int stdout[2]; 17 | int stderr[2]; 18 | 19 | int barrier[2]; 20 | }; 21 | 22 | /** 23 | * Forks off a child process that will execute the command specified by _argv_. 24 | * 25 | * NB: stdout/stderr of the child will be redirected to the pipes _stdout_ and 26 | * _stderr_. 27 | */ 28 | child_t *child_create(char **argv, size_t argv_size); 29 | 30 | /** 31 | * Unblocks the child process. 32 | */ 33 | void child_continue(child_t *child); 34 | 35 | void child_free(child_t *child); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/resource_limits.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class ResourceLimits < BaseMessage 8 | optional :as, :uint64, 1 9 | optional :core, :uint64, 2 10 | optional :cpu, :uint64, 3 11 | optional :data, :uint64, 4 12 | optional :fsize, :uint64, 5 13 | optional :locks, :uint64, 6 14 | optional :memlock, :uint64, 7 15 | optional :msgqueue, :uint64, 8 16 | optional :nice, :uint64, 9 17 | optional :nofile, :uint64, 10 18 | optional :nproc, :uint64, 11 19 | optional :rss, :uint64, 12 20 | optional :rtprio, :uint64, 13 21 | optional :sigpending, :uint64, 14 22 | optional :stack, :uint64, 15 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /warden-client/warden-client.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "warden/client/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "warden-client" 7 | s.version = Warden::Client::VERSION 8 | s.authors = ["Pieter Noordhuis", "Matt Page"] 9 | s.email = ["pcnoordhuis@gmail.com", "mpage@vmware.com"] 10 | s.homepage = "http://www.cloudfoundry.org/" 11 | s.summary = %q{Client driver for warden, the ephemeral container manager.} 12 | s.description = %q{Provides a blocking client for interacting with the Warden.} 13 | 14 | s.files = Dir.glob("**/*") 15 | s.test_files = Dir.glob("spec/**/*") 16 | s.executables = [] 17 | s.require_paths = ["lib"] 18 | 19 | s.add_runtime_dependency "warden-protocol" 20 | end 21 | -------------------------------------------------------------------------------- /warden/lib/warden/event_emitter.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | # Imported from the MIT licensed em-hiredis gem by Martyn Loughran. 4 | # https://github.com/mloughran/em-hiredis 5 | 6 | module Warden 7 | module EventEmitter 8 | def on(event, &listener) 9 | _listeners[event] << listener 10 | end 11 | 12 | def emit(event, *args) 13 | _listeners[event].each { |l| l.call(*args) } 14 | end 15 | 16 | def remove_listener(event, &listener) 17 | _listeners[event].delete(listener) 18 | end 19 | 20 | def remove_all_listeners(event) 21 | _listeners.delete(event) 22 | end 23 | 24 | def listeners(event) 25 | _listeners[event] 26 | end 27 | 28 | private 29 | 30 | def _listeners 31 | @_listeners ||= Hash.new { |h,k| h[k] = [] } 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /warden/src/wsh/pump.h: -------------------------------------------------------------------------------- 1 | #ifndef PUMP_H 2 | #define PUMP_H 1 3 | 4 | #define PUMP_READ 1 5 | #define PUMP_WRITE 2 6 | #define PUMP_EXCEPT 4 7 | 8 | typedef struct pump_s pump_t; 9 | 10 | struct pump_s { 11 | int nfd; 12 | 13 | fd_set rfds; 14 | fd_set wfds; 15 | fd_set efds; 16 | }; 17 | 18 | typedef struct pump_pair_s pump_pair_t; 19 | 20 | struct pump_pair_s { 21 | pump_t *p; 22 | 23 | int rfd; 24 | int wfd; 25 | }; 26 | 27 | void pump_init(pump_t *p); 28 | int pump_add_fd(pump_t *p, int fd, int mode); 29 | int pump_add_pair(pump_t *p, pump_pair_t *pp); 30 | int pump_ready(pump_t *p, int fd, int mode); 31 | int pump_select(pump_t *p); 32 | 33 | void pump_pair_init(pump_pair_t *pp, pump_t *p, int rfd, int wfd); 34 | int pump_pair_splice(pump_pair_t *pp); 35 | int pump_pair_copy(pump_pair_t *pp); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /warden/src/wsh/barrier.h: -------------------------------------------------------------------------------- 1 | #ifndef BARRIER_H 2 | #define BARRIER_H 1 3 | 4 | /* 5 | * This type implements a barrier-like primitive on top of a pipe. 6 | * 7 | * It is intended to be used by processes in need of ridiculously simple 8 | * synchronization. Either process can only wait for the other process, or let 9 | * the other process know it can continue. Because a pipe is used, either 10 | * process will be notified by the OS when the other process dies. 11 | */ 12 | 13 | typedef struct barrier_s barrier_t; 14 | 15 | struct barrier_s { 16 | int fd[2]; 17 | }; 18 | 19 | int barrier_open(barrier_t *bar); 20 | void barrier_close(barrier_t *bar); 21 | 22 | void barrier_close_wait(barrier_t *bar); 23 | void barrier_close_signal(barrier_t *bar); 24 | 25 | int barrier_wait(barrier_t *bar); 26 | int barrier_signal(barrier_t *bar); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /warden-protocol/warden-protocol.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path('../lib/warden/protocol/version', __FILE__) 3 | 4 | Gem::Specification.new do |gem| 5 | gem.authors = ["Pieter Noordhuis"] 6 | gem.email = ["pcnoordhuis@gmail.com"] 7 | gem.description = %q{Protocol specification for Warden} 8 | gem.summary = %q{Protocol specification for Warden} 9 | gem.homepage = "" 10 | 11 | gem.files = `git ls-files`.split($\) 12 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 13 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) 14 | gem.name = "warden-protocol" 15 | gem.require_paths = ["lib"] 16 | gem.version = Warden::Protocol::VERSION 17 | 18 | gem.add_dependency "beefcake" 19 | 20 | gem.add_development_dependency "rspec", "~> 2.11" 21 | end 22 | -------------------------------------------------------------------------------- /em-warden-client/lib/em/warden/client/event_emitter.rb: -------------------------------------------------------------------------------- 1 | # Imported from the MIT licensed em-hiredis gem by Martyn Loughran. 2 | # https://github.com/mloughran/em-hiredis 3 | 4 | module EventMachine 5 | module Warden 6 | module Client 7 | end 8 | end 9 | end 10 | 11 | 12 | module EventMachine::Warden::Client::EventEmitter 13 | def on(event, &listener) 14 | _listeners[event] << listener 15 | end 16 | 17 | def emit(event, *args) 18 | _listeners[event].each { |l| l.call(*args) } 19 | end 20 | 21 | def remove_listener(event, &listener) 22 | _listeners[event].delete(listener) 23 | end 24 | 25 | def remove_all_listeners(event) 26 | _listeners.delete(event) 27 | end 28 | 29 | def listeners(event) 30 | _listeners[event] 31 | end 32 | 33 | private 34 | 35 | def _listeners 36 | @_listeners ||= Hash.new { |h,k| h[k] = [] } 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/version" 4 | 5 | require "warden/protocol/error" 6 | 7 | require "warden/protocol/create" 8 | require "warden/protocol/stop" 9 | require "warden/protocol/destroy" 10 | require "warden/protocol/info" 11 | 12 | require "warden/protocol/spawn" 13 | require "warden/protocol/link" 14 | require "warden/protocol/run" 15 | require "warden/protocol/stream" 16 | 17 | require "warden/protocol/net_in" 18 | require "warden/protocol/net_out" 19 | 20 | require "warden/protocol/copy_in" 21 | require "warden/protocol/copy_out" 22 | 23 | require "warden/protocol/limit_memory" 24 | require "warden/protocol/limit_disk" 25 | require "warden/protocol/limit_bandwidth" 26 | 27 | require "warden/protocol/ping" 28 | require "warden/protocol/list" 29 | require "warden/protocol/echo" 30 | 31 | module Warden 32 | module Protocol 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/create.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class CreateRequest < BaseRequest 8 | class BindMount < BaseMessage 9 | module Mode 10 | RO = 0 11 | RW = 1 12 | end 13 | 14 | required :src_path, :string, 1 15 | required :dst_path, :string, 2 16 | required :mode, BindMount::Mode, 3 17 | end 18 | 19 | repeated :bind_mounts, BindMount, 1 20 | optional :grace_time, :uint32, 2 21 | optional :handle, :string, 3 22 | optional :network, :string, 4 23 | optional :rootfs, :string, 5 24 | 25 | def self.description 26 | "Create a container, optionally pass options." 27 | end 28 | end 29 | 30 | class CreateResponse < BaseResponse 31 | required :handle, :string, 1 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /warden/spec/pool/network_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/pool/network" 5 | 6 | describe Warden::Pool::Network do 7 | 8 | context "create" do 9 | 10 | it "should iterate over IPs" do 11 | pool = Warden::Pool::Network.new("127.0.0.0", 2) 12 | pool.acquire.should == "127.0.0.0" 13 | pool.acquire.should == "127.0.0.4" 14 | pool.acquire.should be_nil 15 | end 16 | 17 | it "should default to a proper release delay" do 18 | pool = Warden::Pool::Network.new("127.0.0.0", 2) 19 | pool.release_delay.should >= 5 20 | end 21 | end 22 | 23 | context "release" do 24 | 25 | it "should ignore networks that don't belong to the pool" do 26 | pool = Warden::Pool::Network.new("127.0.0.0", 2) 27 | pool.release(Warden::Network::Address.new("10.10.10.10")) 28 | 29 | pool.size.should == 2 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/lib/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -f etc/config ] && source etc/config 4 | 5 | function get_distrib_codename() { 6 | if [ -r /etc/lsb-release ] 7 | then 8 | source /etc/lsb-release 9 | 10 | if [ -n "$DISTRIB_CODENAME" ] 11 | then 12 | echo $DISTRIB_CODENAME 13 | return 0 14 | fi 15 | else 16 | lsb_release -cs 17 | fi 18 | } 19 | 20 | function setup_fs() { 21 | mkdir -p tmp/rootfs mnt 22 | 23 | distrib_codename=$(get_distrib_codename) 24 | 25 | case "$distrib_codename" in 26 | lucid|natty|oneiric) 27 | mount -n -t aufs -o br:tmp/rootfs=rw:$rootfs_path=ro+wh none mnt 28 | ;; 29 | precise) 30 | mount -n -t overlayfs -o rw,upperdir=tmp/rootfs,lowerdir=$rootfs_path none mnt 31 | ;; 32 | *) 33 | echo "Unsupported: $distrib_codename" 34 | exit 1 35 | ;; 36 | esac 37 | } 38 | 39 | function teardown_fs() { 40 | umount mnt 41 | } 42 | -------------------------------------------------------------------------------- /warden/src/closefds/closefds.c: -------------------------------------------------------------------------------- 1 | #define _BSD_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* 10 | * The code in this file is not portable. 11 | * 12 | * Search for implementations of closefrom(2) for a portable version. 13 | */ 14 | 15 | int main(int argc, char **argv) { 16 | DIR *dirp; 17 | struct dirent *entry; 18 | int fd; 19 | char *eptr; 20 | 21 | dirp = opendir("/proc/self/fd"); 22 | if (dirp == NULL) { 23 | perror("opendir"); 24 | exit(255); 25 | } 26 | 27 | while ((entry = readdir(dirp)) != NULL) { 28 | fd = strtol(entry->d_name, &eptr, 10); 29 | if (eptr[0] == '\0') { 30 | if (fd > 2 && fd != !dirfd(dirp)) { 31 | while (close(fd) == -1 && errno == EINTR); 32 | } 33 | } 34 | } 35 | 36 | closedir(dirp); 37 | 38 | execvp(argv[1], &argv[1]); 39 | perror("execvp"); 40 | exit(255); 41 | } 42 | -------------------------------------------------------------------------------- /warden/src/iomux/status_reader.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "status_reader.h" 6 | #include "util.h" 7 | 8 | void status_reader_init(status_reader_t *reader, int fd) { 9 | assert(NULL != reader); 10 | assert(fd >= 0); 11 | 12 | memset(reader->buf, 0, sizeof(reader->buf)); 13 | reader->off = 0; 14 | reader->status = -1; 15 | reader->fd = fd; 16 | } 17 | 18 | int status_reader_run(status_reader_t *reader, uint8_t *hup) { 19 | uint32_t in_status; 20 | int done = 0; 21 | 22 | reader->off += atomic_read(reader->fd, reader->buf + reader->off, 23 | sizeof(reader->buf) - reader->off, hup); 24 | 25 | if (reader->off >= sizeof(reader->buf)) { 26 | memcpy(&in_status, reader->buf, sizeof(reader->buf)); 27 | reader->status = ntohl(in_status); 28 | done = 1; 29 | } 30 | 31 | if (*hup) { 32 | done = 1; 33 | } 34 | 35 | return done; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /warden-protocol/spec/ping_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/ping" 5 | 6 | describe Warden::Protocol::PingRequest do 7 | subject(:request) do 8 | described_class.new 9 | end 10 | 11 | it_should_behave_like "wrappable request" 12 | 13 | its("class.type_camelized") { should == "Ping" } 14 | its("class.type_underscored") { should == "ping" } 15 | 16 | it "should respond to #create_response" do 17 | request.create_response.should be_a(Warden::Protocol::PingResponse) 18 | end 19 | 20 | it_should_behave_like "documented request" 21 | end 22 | 23 | describe Warden::Protocol::PingResponse do 24 | subject(:response) do 25 | described_class.new 26 | end 27 | 28 | it_should_behave_like "wrappable response" 29 | 30 | its("class.type_camelized") { should == "Ping" } 31 | its("class.type_underscored") { should == "ping" } 32 | 33 | it { should be_ok } 34 | it { should_not be_error } 35 | end 36 | -------------------------------------------------------------------------------- /em-warden-client/em-warden-client.gemspec: -------------------------------------------------------------------------------- 1 | require File.expand_path('../lib/em/warden/client/version', __FILE__) 2 | 3 | Gem::Specification.new do |gem| 4 | gem.authors = ["mpage"] 5 | gem.email = ["mpage@vmware.com"] 6 | gem.description = "EM/Fiber compatible client for Warden" 7 | gem.summary = "Provides EventMachine compatible code for talking with Warden" 8 | gem.homepage = "http://www.cloudfoundry.com" 9 | 10 | gem.files = Dir.glob("**/*") 11 | gem.test_files = Dir.glob("spec/**/*") 12 | gem.name = "em-warden-client" 13 | gem.require_paths = ["lib"] 14 | gem.version = EventMachine::Warden::Client::VERSION 15 | 16 | gem.add_dependency('eventmachine') 17 | gem.add_dependency('warden-protocol', '>= 0.0.9') 18 | 19 | # Only needed for backwards API compatibility. 20 | gem.add_dependency('warden-client') 21 | 22 | gem.add_development_dependency('rake') 23 | gem.add_development_dependency('rspec') 24 | end 25 | -------------------------------------------------------------------------------- /warden-protocol/spec/support/examples/wrappable_request.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | shared_examples "wrappable request" do 4 | let(:wrapped) { subject.wrap } 5 | 6 | it "should respond to #wrap" do 7 | wrapped.should be_a(Warden::Protocol::WrappedRequest) 8 | 9 | type_const = described_class.name.split("::").last.gsub(/Request$/, "") 10 | wrapped.type.should == Warden::Protocol::Type.const_get(type_const) 11 | wrapped.payload.to_s.should == subject.encode.to_s 12 | end 13 | 14 | it "should retain class when unwrapped" do 15 | wrapped.request.should be_a(described_class) 16 | end 17 | 18 | it "should retain properties when unwrapped" do 19 | compare_without_encoding(wrapped.request.to_hash, subject.to_hash) 20 | end 21 | 22 | it "should retain properties when encoded and decoded" do 23 | freshly_wrapped = Warden::Protocol::WrappedRequest.decode(wrapped.encode.to_s) 24 | compare_without_encoding(freshly_wrapped.request.to_hash, subject.to_hash) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /warden-protocol/spec/support/examples/wrappable_reply.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | shared_examples "wrappable response" do 4 | let(:wrapped) { subject.wrap } 5 | 6 | it "should respond to #wrap" do 7 | wrapped.should be_a(Warden::Protocol::WrappedResponse) 8 | 9 | type_const = described_class.name.split("::").last.gsub(/Response$/, "") 10 | wrapped.type.should == Warden::Protocol::Type.const_get(type_const) 11 | wrapped.payload.to_s.should == subject.encode.to_s 12 | end 13 | 14 | it "should retain class when unwrapped" do 15 | wrapped.response.should be_a(described_class) 16 | end 17 | 18 | it "should retain properties when unwrapped" do 19 | compare_without_encoding(wrapped.response.to_hash, subject.to_hash) 20 | end 21 | 22 | it "should retain properties when encoded and decoded" do 23 | freshly_wrapped = Warden::Protocol::WrappedResponse.decode(wrapped.encode.to_s) 24 | compare_without_encoding(freshly_wrapped.response.to_hash, subject.to_hash) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/destroy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname $0) 9 | 10 | source ./etc/config 11 | 12 | ./net.sh teardown 13 | 14 | if [ -f ./run/wshd.pid ] 15 | then 16 | pid=$(cat ./run/wshd.pid) 17 | path=/sys/fs/cgroup/cpu/instance-$id 18 | tasks=$path/tasks 19 | 20 | if [ -d $path ] 21 | then 22 | while true 23 | do 24 | kill -9 $pid 2> /dev/null || true 25 | 26 | # Wait while there are tasks in one of the instance's cgroups 27 | if [ -f $tasks ] && [ -n "$(cat $tasks)" ] 28 | then 29 | sleep 0.1 30 | else 31 | break 32 | fi 33 | done 34 | fi 35 | 36 | # Done, remove pid 37 | rm -f ./run/wshd.pid 38 | 39 | # Remove cgroups 40 | for system_path in /sys/fs/cgroup/* 41 | do 42 | path=$system_path/instance-$id 43 | 44 | if [ -d $path ] 45 | then 46 | rmdir $path 47 | fi 48 | done 49 | 50 | exit 0 51 | fi 52 | -------------------------------------------------------------------------------- /warden/src/iomux/status_writer.h: -------------------------------------------------------------------------------- 1 | #ifndef STATUS_WRITER_H 2 | #define STATUS_WRITER_H 1 3 | 4 | #include 5 | 6 | #include "barrier.h" 7 | 8 | typedef struct status_writer_s status_writer_t; 9 | 10 | /** 11 | * Allocates a new status writer. 12 | * 13 | * @param accept_fd 14 | * @param barrier If supplied, this barrier will be lifted once a client has 15 | * connected. 16 | */ 17 | status_writer_t *status_writer_alloc(int accept_fd, barrier_t *barrier); 18 | 19 | /** 20 | * Starts the status writer. This blocks until someone calls 21 | * status_writer_finish(). 22 | */ 23 | void status_writer_run(status_writer_t *sw); 24 | 25 | /** 26 | * Tells the status writer that the child has completed. The status 27 | * writer in turn writes out the supplied child status to any connected clients. 28 | * 29 | * @param sw 30 | * @param status Exit status of the child. 31 | */ 32 | void status_writer_finish(status_writer_t *sw, int status); 33 | 34 | void status_writer_free(status_writer_t *sw); 35 | #endif 36 | -------------------------------------------------------------------------------- /warden-protocol/spec/destroy_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/destroy" 5 | 6 | describe Warden::Protocol::DestroyRequest do 7 | subject(:request) do 8 | described_class.new(:handle => "handle") 9 | end 10 | 11 | it_should_behave_like "wrappable request" 12 | 13 | its("class.type_camelized") { should == "Destroy" } 14 | its("class.type_underscored") { should == "destroy" } 15 | 16 | field :handle do 17 | it_should_be_required 18 | end 19 | 20 | it "should respond to #create_response" do 21 | request.create_response.should be_a(Warden::Protocol::DestroyResponse) 22 | end 23 | 24 | it_should_behave_like "documented request" 25 | end 26 | 27 | describe Warden::Protocol::DestroyResponse do 28 | subject(:response) do 29 | described_class.new 30 | end 31 | 32 | it_should_behave_like "wrappable response" 33 | 34 | its("class.type_camelized") { should == "Destroy" } 35 | its("class.type_underscored") { should == "destroy" } 36 | 37 | it { should be_ok } 38 | it { should_not be_error } 39 | end 40 | -------------------------------------------------------------------------------- /warden/lib/warden/pool/network.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/network" 4 | require "warden/pool/base" 5 | 6 | module Warden 7 | 8 | module Pool 9 | 10 | class Network < Base 11 | 12 | attr_reader :netmask 13 | 14 | # The release delay can be used to postpone address being acquired again 15 | # after being released. This can be used to make sure the kernel has time 16 | # to clean up things such as lingering connections. 17 | 18 | def initialize(start_address, count, options = {}) 19 | @start_address = Warden::Network::Address.new(start_address) 20 | @netmask = Warden::Network::Netmask.new(255, 255, 255, 252) 21 | @end_address = @start_address + (@netmask.size * (count - 1)) 22 | 23 | options[:release_delay] ||= 5.0 24 | 25 | super(count, options) do |i| 26 | @start_address + @netmask.size * i 27 | end 28 | end 29 | 30 | private 31 | 32 | def belongs?(addr) 33 | (addr >= @start_address) && (addr <= @end_address) 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /warden-protocol/spec/list_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/list" 5 | 6 | describe Warden::Protocol::ListRequest do 7 | subject(:request) do 8 | described_class.new 9 | end 10 | 11 | it_should_behave_like "wrappable request" 12 | 13 | its("class.type_camelized") { should == "List" } 14 | its("class.type_underscored") { should == "list" } 15 | 16 | it "should respond to #create_response" do 17 | request.create_response.should be_a(Warden::Protocol::ListResponse) 18 | end 19 | end 20 | 21 | describe Warden::Protocol::ListResponse do 22 | subject(:response) do 23 | described_class.new 24 | end 25 | 26 | it_should_behave_like "wrappable response" 27 | 28 | its("class.type_camelized") { should == "List" } 29 | its("class.type_underscored") { should == "list" } 30 | 31 | it { should be_ok } 32 | it { should_not be_error } 33 | 34 | field :handles do 35 | it_should_be_optional 36 | 37 | it "should allow one or more handles" do 38 | subject.handles = ["a", "b"] 39 | subject.should be_valid 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /warden/src/iomux/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 1 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define MIN(a, b) (((a) < (b)) ? (a) : (b)) 11 | #define MAX(a, b) (((a) > (b)) ? (a) : (b)) 12 | 13 | #define MUXER_READABLE 1 14 | #define MUXER_STOP 2 15 | 16 | /** 17 | * Reads until _count_ bytes have been read or _fd_ would block. 18 | */ 19 | ssize_t atomic_read(int fd, void *buf, size_t count, uint8_t *hup); 20 | 21 | /** 22 | * Writes until _count_ bytes have been written or _fd_ would block. 23 | */ 24 | ssize_t atomic_write(int fd, const void *buf, size_t count, uint8_t *hup); 25 | 26 | void set_nonblocking(int fd); 27 | 28 | void set_cloexec(int fd); 29 | 30 | void checked_lock(pthread_mutex_t *lock); 31 | 32 | void checked_unlock(pthread_mutex_t *lock); 33 | 34 | int create_unix_domain_listener(const char *path, int backlog); 35 | 36 | int unix_domain_connect(const char *path); 37 | 38 | uint8_t wait_readable_or_stop(int read_fd, int stop_fd); 39 | 40 | void perrorf(const char *fmt, ...); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /em-warden-client/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 mpage 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /warden/lib/warden/pool/uid.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/errors" 4 | require "warden/pool/base" 5 | 6 | module Warden 7 | 8 | module Pool 9 | 10 | class Uid < Base 11 | 12 | class NoUidAvailable < WardenError 13 | 14 | def message 15 | super || "no uid available" 16 | end 17 | end 18 | 19 | def self.local_uids 20 | File.readlines("/etc/passwd").map { |e| e.split(":")[2].to_i }.sort 21 | end 22 | 23 | def initialize(start, count, options = {}) 24 | local_uids = self.class.local_uids 25 | 26 | @start_uid = start 27 | @end_uid = start + (count - 1) 28 | 29 | super(count) do |i| 30 | uid = start + i 31 | 32 | if local_uids.include?(uid) 33 | fail "UID in user pool overlaps with user in /etc/passwd" 34 | end 35 | 36 | uid 37 | end 38 | end 39 | 40 | def acquire 41 | super.tap do |uid| 42 | raise NoUidAvailable unless uid 43 | end 44 | end 45 | 46 | private 47 | 48 | def belongs?(uid) 49 | (uid >= @start_uid) && (uid <= @end_uid) 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /warden/src/iomux/ring_buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef RING_BUFFER_H 2 | #define RING_BUFFER_H 1 3 | 4 | #include 5 | #include 6 | 7 | typedef struct ring_buffer_s ring_buffer_t; 8 | 9 | ring_buffer_t *ring_buffer_alloc(size_t capacity); 10 | 11 | /** 12 | * Writes data to the supplied ring buffer. 13 | * 14 | */ 15 | void ring_buffer_append(ring_buffer_t *buf, const uint8_t *data, size_t size); 16 | 17 | /** 18 | * Reads _size_ bytes from the buffer into _dst_, starting at offset _start_. 19 | * 20 | * @param buf The buffer being read from. 21 | * @param start Offset into the buffer that specifies where to start reading. 22 | * @param dst Where to store the bytes 23 | * @param size How many bytes to read 24 | * 25 | * @return Number of bytes read 26 | */ 27 | size_t ring_buffer_read(const ring_buffer_t *buf, size_t start, uint8_t *dst, 28 | size_t size); 29 | 30 | /** 31 | * Returns a copy of the ring buffer. 32 | * 33 | * NB: Caller is responsible for freeing the returned buffer. 34 | */ 35 | uint8_t *ring_buffer_dup(const ring_buffer_t *buf); 36 | 37 | size_t ring_buffer_size(const ring_buffer_t *buf); 38 | 39 | void ring_buffer_free(ring_buffer_t *buf); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname $0) 9 | 10 | if [ ! -f ./run/wshd.pid ] 11 | then 12 | echo "wshd is not running..." 13 | exit 1 14 | fi 15 | 16 | WAIT=10 17 | 18 | function usage() { 19 | echo "Usage $0 [OPTION]..." >&2 20 | echo " -w N seconds to wait before sending SIGKILL;" >&2 21 | echo " N=0 skips SIGTERM and sends SIGKILL immediately" >&2 22 | exit 1 23 | } 24 | 25 | while getopts ":w:h" opt 26 | do 27 | case $opt in 28 | "w") 29 | WAIT=$OPTARG 30 | ;; 31 | "h") 32 | usage 33 | ;; 34 | "?") 35 | # Ignore invalid options 36 | ;; 37 | esac 38 | done 39 | 40 | function ms() { 41 | echo $(($(date +%s%N)/1000000)) 42 | } 43 | 44 | ms_start=$(ms) 45 | ms_end=$(($ms_start + ($WAIT * 1000))) 46 | 47 | # Send SIGTERM 48 | if [[ $(ms) -lt $ms_end ]] 49 | then 50 | bin/wsh pkill -TERM -v -u 0 || true 51 | fi 52 | 53 | # Wait for processes to quit 54 | while [[ $(ms) -lt $ms_end ]] 55 | do 56 | if ! bin/wsh pgrep -c -v -u 0 > /dev/null 57 | then 58 | exit 0 59 | fi 60 | 61 | sleep 1 62 | done 63 | 64 | # Send SIGKILL 65 | bin/wsh pkill -KILL -v -u 0 || true 66 | -------------------------------------------------------------------------------- /warden-protocol/spec/stop_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/stop" 5 | 6 | describe Warden::Protocol::StopRequest do 7 | subject(:request) do 8 | described_class.new(:handle => "handle") 9 | end 10 | 11 | it_should_behave_like "wrappable request" 12 | 13 | its("class.type_camelized") { should == "Stop" } 14 | its("class.type_underscored") { should == "stop" } 15 | 16 | field :handle do 17 | it_should_be_required 18 | end 19 | 20 | field :background do 21 | it_should_be_optional 22 | it_should_be_typed_as_boolean 23 | end 24 | 25 | field :kill do 26 | it_should_be_optional 27 | it_should_be_typed_as_boolean 28 | end 29 | 30 | it "should respond to #create_response" do 31 | request.create_response.should be_a(Warden::Protocol::StopResponse) 32 | end 33 | 34 | it_should_behave_like "documented request" 35 | end 36 | 37 | describe Warden::Protocol::StopResponse do 38 | subject(:response) do 39 | described_class.new 40 | end 41 | 42 | it_should_behave_like "wrappable response" 43 | 44 | its("class.type_camelized") { should == "Stop" } 45 | its("class.type_underscored") { should == "stop" } 46 | 47 | it { should be_ok } 48 | it { should_not be_error } 49 | end 50 | -------------------------------------------------------------------------------- /warden-protocol/spec/echo_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # coding: UTF-8 3 | 4 | require "spec_helper" 5 | require "warden/protocol/echo" 6 | 7 | describe Warden::Protocol::EchoRequest do 8 | subject(:request) do 9 | described_class.new(:message => "here's a snowman: ☃") 10 | end 11 | 12 | it_should_behave_like "wrappable request" 13 | 14 | its("class.type_camelized") { should == "Echo" } 15 | its("class.type_underscored") { should == "echo" } 16 | 17 | field :message do 18 | it_should_be_required 19 | it_should_be_typed_as_string 20 | end 21 | 22 | it "should respond to #create_response" do 23 | request.create_response.should be_a(Warden::Protocol::EchoResponse) 24 | end 25 | 26 | it_should_behave_like "documented request" 27 | end 28 | 29 | describe Warden::Protocol::EchoResponse do 30 | subject(:response) do 31 | described_class.new(:message => "here's a snowman: ☃") 32 | end 33 | 34 | it_should_behave_like "wrappable response" 35 | 36 | its("class.type_camelized") { should == "Echo" } 37 | its("class.type_underscored") { should == "echo" } 38 | 39 | it { should be_ok } 40 | it { should_not be_error } 41 | 42 | field :message do 43 | it_should_be_required 44 | it_should_be_typed_as_string 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /warden/root/insecure/skeleton/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | WAIT=10 9 | 10 | function usage() { 11 | echo "Usage $0 [OPTION]..." >&2 12 | echo " -w N seconds to wait before sending SIGKILL;" >&2 13 | echo " N=0 skips SIGTERM and sends SIGKILL immediately" >&2 14 | exit 1 15 | } 16 | 17 | while getopts ":w:h" opt 18 | do 19 | case $opt in 20 | "w") 21 | WAIT=$OPTARG 22 | ;; 23 | "h") 24 | usage 25 | ;; 26 | "?") 27 | # Ignore invalid options 28 | ;; 29 | esac 30 | done 31 | 32 | cd $(dirname "$0") 33 | 34 | function pids() { 35 | echo pids/* | xargs -r -n1 basename | xargs -r echo 36 | } 37 | 38 | # Wait for processes to exit 39 | for i in $(seq $WAIT); do 40 | p=$(pids) 41 | 42 | # Break when there are no pids 43 | if [ -z "$p" ] 44 | then 45 | break 46 | fi 47 | 48 | # Send SIGTERM 49 | kill -TERM $p || true 50 | 51 | # When none of the pids is a process `ps` exits with non-zero status 52 | if ! ps -o pid= -p $p > /dev/null 53 | then 54 | break 55 | fi 56 | 57 | sleep 1 58 | done 59 | 60 | p=$(pids) 61 | 62 | # Send SIGKILL 63 | if [ -n "$p" ] 64 | then 65 | kill -KILL $p || true 66 | fi 67 | -------------------------------------------------------------------------------- /warden-protocol/spec/net_out_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/net_out" 5 | 6 | describe Warden::Protocol::NetOutRequest do 7 | subject(:request) do 8 | described_class.new(:handle => "handle") 9 | end 10 | 11 | it_should_behave_like "wrappable request" 12 | 13 | its("class.type_camelized") { should == "NetOut" } 14 | its("class.type_underscored") { should == "net_out" } 15 | 16 | field :handle do 17 | it_should_be_required 18 | it_should_be_typed_as_string 19 | end 20 | 21 | field :network do 22 | it_should_be_optional 23 | it_should_be_typed_as_string 24 | end 25 | 26 | field :port do 27 | it_should_be_optional 28 | it_should_be_typed_as_uint 29 | end 30 | 31 | it "should respond to #create_response" do 32 | request.create_response.should be_a(Warden::Protocol::NetOutResponse) 33 | end 34 | 35 | it_should_behave_like "documented request" 36 | end 37 | 38 | describe Warden::Protocol::NetOutResponse do 39 | subject(:response) do 40 | described_class.new 41 | end 42 | 43 | it_should_behave_like "wrappable response" 44 | 45 | its("class.type_camelized") { should == "NetOut" } 46 | its("class.type_underscored") { should == "net_out" } 47 | 48 | it { should be_ok } 49 | it { should_not be_error } 50 | end 51 | -------------------------------------------------------------------------------- /em-warden-client/lib/em/warden/client.rb: -------------------------------------------------------------------------------- 1 | require 'eventmachine' 2 | require 'fiber' 3 | 4 | require 'em/warden/client/connection' 5 | require 'em/warden/client/error' 6 | 7 | module EventMachine 8 | module Warden 9 | end 10 | end 11 | 12 | class EventMachine::Warden::FiberAwareClient 13 | 14 | attr_reader :socket_path 15 | 16 | def initialize(socket_path) 17 | @socket_path = socket_path 18 | @connection = nil 19 | end 20 | 21 | def connect 22 | return if @connection 23 | @connection = EM.connect_unix_domain(@socket_path, EM::Warden::Client::Connection) 24 | f = Fiber.current 25 | @connection.on(:connected) { f.resume } 26 | Fiber.yield 27 | end 28 | 29 | def connected? 30 | @connection.connected? 31 | end 32 | 33 | def call(*args, &blk) 34 | raise EventMachine::Warden::Client::Error.new("Not connected") unless @connection.connected? 35 | 36 | f = Fiber.current 37 | @connection.call(*args) {|res| f.resume(res) } 38 | result = Fiber.yield 39 | 40 | result.get 41 | end 42 | 43 | def method_missing(method, *args, &blk) 44 | call(method, *args, &blk) 45 | end 46 | 47 | def disconnect(close_after_writing=true) 48 | @connection.close_connection(close_after_writing) 49 | f = Fiber.current 50 | @connection.on(:disconnected) { f.resume } 51 | Fiber.yield 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /warden/src/wsh/msg.h: -------------------------------------------------------------------------------- 1 | #ifndef MSG_H 2 | #define MSG_H 1 3 | 4 | #define MSG_VERSION 1 5 | #define MSG_MAX_SIZE (16 * 1024) 6 | 7 | #include 8 | #include 9 | 10 | typedef struct msg__array_s msg__array_t; 11 | typedef struct msg__rlimit_s msg__rlimit_t; 12 | typedef struct msg__user_s msg__user_t; 13 | typedef struct msg_request_s msg_request_t; 14 | typedef struct msg_response_s msg_response_t; 15 | 16 | struct msg__array_s { 17 | int count; 18 | char buf[8 * 1024]; 19 | }; 20 | 21 | struct msg__rlimit_s { 22 | int count; 23 | struct { 24 | int id; 25 | struct rlimit rlim; 26 | } rlim[RLIMIT_NLIMITS]; 27 | }; 28 | 29 | struct msg__user_s { 30 | char name[32]; 31 | }; 32 | 33 | struct msg_request_s { 34 | int version; 35 | int tty; 36 | msg__array_t arg; 37 | msg__rlimit_t rlim; 38 | msg__user_t user; 39 | }; 40 | 41 | struct msg_response_s { 42 | int version; 43 | }; 44 | 45 | int msg_array_import(msg__array_t * a, int count, const char ** ptr); 46 | const char ** msg_array_export(msg__array_t * a); 47 | 48 | int msg_rlimit_import(msg__rlimit_t *); 49 | int msg_rlimit_export(msg__rlimit_t *); 50 | 51 | int msg_user_import(msg__user_t *u, const char *name); 52 | int msg_user_export(msg__user_t *u); 53 | 54 | void msg_request_init(msg_request_t *req); 55 | void msg_response_init(msg_response_t *res); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/net_rate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname "${0}") 9 | 10 | source ./etc/config 11 | 12 | if [ -z "${RATE:-}" ]; then 13 | echo "Please specify RATE..." 1>&2 14 | exit 1 15 | fi 16 | 17 | if [ -z "${BURST:-}" ]; then 18 | echo "Please specify BURST..." 1>&2 19 | exit 1 20 | fi 21 | 22 | # clear rule if exist 23 | # delete root egress tc qdisc 24 | tc qdisc del dev ${network_host_iface} root 2> /dev/null || true 25 | 26 | # delete root ingress tc qdisc 27 | tc qdisc del dev ${network_host_iface} ingress 2> /dev/null || true 28 | 29 | # set inbound(outside -> eth0 -> w--0 -> w--1) rule with tc's tbf(token bucket filter) qdisc 30 | # rate is the bandwidth 31 | # burst is the burst size 32 | # latency is the maxium time the packet wait to enqueue while no token left 33 | tc qdisc add dev ${network_host_iface} root tbf rate ${RATE}bit burst ${BURST} latency 25ms 34 | 35 | # set outbound(w--1 -> w--0 -> eth0 -> outside) rule 36 | tc qdisc add dev ${network_host_iface} ingress handle ffff: 37 | 38 | # use u32 filter with target(0.0.0.0) mask (0) to filter all the ingress packets 39 | tc filter add dev ${network_host_iface} parent ffff: protocol ip prio 1 u32 match ip src 0.0.0.0/0 police rate ${RATE}bit burst ${BURST} drop flowid :1 40 | -------------------------------------------------------------------------------- /warden-protocol/spec/limit_memory_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/limit_memory" 5 | 6 | describe Warden::Protocol::LimitMemoryRequest do 7 | subject(:request) do 8 | described_class.new(:handle => "handle") 9 | end 10 | 11 | it_should_behave_like "wrappable request" 12 | 13 | its("class.type_camelized") { should == "LimitMemory" } 14 | its("class.type_underscored") { should == "limit_memory" } 15 | 16 | field :handle do 17 | it_should_be_required 18 | it_should_be_typed_as_string 19 | end 20 | 21 | field :limit_in_bytes do 22 | it_should_be_optional 23 | it_should_be_typed_as_uint64 24 | end 25 | 26 | it "should respond to #create_response" do 27 | request.create_response.should be_a(Warden::Protocol::LimitMemoryResponse) 28 | end 29 | 30 | it_should_behave_like "documented request" 31 | end 32 | 33 | describe Warden::Protocol::LimitMemoryResponse do 34 | subject(:response) do 35 | described_class.new 36 | end 37 | 38 | it_should_behave_like "wrappable response" 39 | 40 | its("class.type_camelized") { should == "LimitMemory" } 41 | its("class.type_underscored") { should == "limit_memory" } 42 | 43 | it { should be_ok } 44 | it { should_not be_error } 45 | 46 | field :limit_in_bytes do 47 | it_should_be_optional 48 | it_should_be_typed_as_uint64 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /warden/spec/pool/uid_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/pool/uid" 5 | 6 | describe Warden::Pool::Uid do 7 | 8 | before(:each) do 9 | Warden::Pool::Uid.should_receive(:local_uids).and_return([1000, 1001, 1002]) 10 | end 11 | 12 | context "create" do 13 | 14 | it "should fail when range overlaps with local UIDs" do 15 | pool = nil 16 | expect do 17 | pool = Warden::Pool::Uid.new(1000, 5) 18 | end.to raise_error 19 | end 20 | 21 | it "should work when range doesn't overlap" do 22 | pool = nil 23 | expect do 24 | pool = Warden::Pool::Uid.new(2000, 5) 25 | end.to_not raise_error 26 | 27 | # Check size 28 | pool.size.should == 5 29 | 30 | # Check first entry 31 | pool.acquire.should == 2000 32 | end 33 | end 34 | 35 | context "acquire" do 36 | 37 | it "should raise when no uid is available" do 38 | pool = Warden::Pool::Uid.new(2000, 5) 39 | 40 | expect do 41 | (pool.size + 1).times { pool.acquire } 42 | end.to raise_error Warden::Pool::Uid::NoUidAvailable 43 | end 44 | end 45 | 46 | context "release" do 47 | 48 | it "should ignore uids that don't belong to the pool" do 49 | pool = Warden::Pool::Uid.new(2000, 5) 50 | pool.release(3000) 51 | 52 | pool.size.should == 5 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /warden-protocol/spec/copy_in_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/copy_in" 5 | 6 | describe Warden::Protocol::CopyInRequest do 7 | subject(:request) do 8 | described_class.new( 9 | :handle => "handle", 10 | :src_path => "/src", 11 | :dst_path => "/dst", 12 | ) 13 | end 14 | 15 | it_should_behave_like "wrappable request" 16 | 17 | its("class.type_camelized") { should == "CopyIn" } 18 | its("class.type_underscored") { should == "copy_in" } 19 | 20 | field :handle do 21 | it_should_be_required 22 | it_should_be_typed_as_string 23 | end 24 | 25 | field :src_path do 26 | it_should_be_required 27 | it_should_be_typed_as_string 28 | end 29 | 30 | field :dst_path do 31 | it_should_be_required 32 | it_should_be_typed_as_string 33 | end 34 | 35 | it "should respond to #create_response" do 36 | request.create_response.should be_a(Warden::Protocol::CopyInResponse) 37 | end 38 | 39 | it_should_behave_like "documented request" 40 | end 41 | 42 | describe Warden::Protocol::CopyInResponse do 43 | subject(:response) do 44 | described_class.new 45 | end 46 | 47 | it_should_behave_like "wrappable response" 48 | 49 | its("class.type_camelized") { should == "CopyIn" } 50 | its("class.type_underscored") { should == "copy_in" } 51 | 52 | it { should be_ok } 53 | it { should_not be_error } 54 | end 55 | -------------------------------------------------------------------------------- /warden/src/iomux/barrier.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "barrier.h" 7 | #include "util.h" 8 | 9 | struct barrier_s { 10 | pthread_mutex_t lock; 11 | pthread_cond_t cv; 12 | uint8_t lifted; 13 | }; 14 | 15 | barrier_t *barrier_alloc(void) { 16 | barrier_t *barrier = NULL; 17 | int err = 0; 18 | 19 | barrier = malloc(sizeof(*barrier)); 20 | assert(NULL != barrier); 21 | 22 | barrier->lifted = 0; 23 | 24 | err = pthread_mutex_init(&(barrier->lock), NULL); 25 | assert(!err); 26 | 27 | err = pthread_cond_init(&(barrier->cv), NULL); 28 | assert(!err); 29 | 30 | return barrier; 31 | } 32 | 33 | void barrier_lift(barrier_t *barrier) { 34 | assert(NULL != barrier); 35 | 36 | checked_lock(&(barrier->lock)); 37 | 38 | barrier->lifted = 1; 39 | pthread_cond_broadcast(&(barrier->cv)); 40 | 41 | checked_unlock(&(barrier->lock)); 42 | } 43 | 44 | void barrier_wait(barrier_t *barrier) { 45 | assert(NULL != barrier); 46 | 47 | checked_lock(&(barrier->lock)); 48 | 49 | if (!barrier->lifted) { 50 | pthread_cond_wait(&(barrier->cv), &(barrier->lock)); 51 | } 52 | 53 | checked_unlock(&(barrier->lock)); 54 | } 55 | 56 | void barrier_free(barrier_t *barrier) { 57 | assert(NULL != barrier); 58 | 59 | pthread_mutex_destroy(&(barrier->lock)); 60 | pthread_cond_destroy(&(barrier->cv)); 61 | 62 | free(barrier); 63 | } 64 | -------------------------------------------------------------------------------- /warden/config/linux.yml: -------------------------------------------------------------------------------- 1 | --- 2 | server: 3 | container_klass: Warden::Container::Linux 4 | 5 | # Wait this long before destroying a container, after the last client 6 | # referencing it disconnected. The timer is cancelled when during this 7 | # period, another client references the container. 8 | # 9 | # Clients can be forced to specify this setting by setting the 10 | # server-wide variable to an invalid value: 11 | # container_grace_time: invalid 12 | # 13 | # The grace time can be disabled by setting it to nil: 14 | # container_grace_time: ~ 15 | # 16 | container_grace_time: 300 17 | 18 | unix_domain_permissions: 0777 19 | 20 | # Specifies the path to the base chroot used as the read-only root 21 | # filesystem 22 | container_rootfs_path: /tmp/warden/rootfs 23 | 24 | # Specifies the path to the parent directory under which all containers 25 | # will live. 26 | container_depot_path: /tmp/warden/containers 27 | 28 | # See getrlimit(2) for details. Integer values are passed verbatim. 29 | container_rlimits: 30 | as: 4294967296 31 | nofile: 8192 32 | nproc: 512 33 | 34 | quota: 35 | disk_quota_enabled: true 36 | 37 | health_check_server: 38 | port: 2345 39 | 40 | logging: 41 | level: debug2 42 | 43 | network: 44 | # Use this /30 network as offset for the network pool. 45 | pool_start_address: 10.254.0.0 46 | 47 | # Pool this many /30 networks. 48 | pool_size: 256 49 | 50 | user: 51 | pool_start_uid: 10000 52 | pool_size: 256 53 | -------------------------------------------------------------------------------- /warden-protocol/spec/copy_out_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/copy_out" 5 | 6 | describe Warden::Protocol::CopyOutRequest do 7 | subject(:request) do 8 | described_class.new( 9 | :handle => "handle", 10 | :src_path => "/src", 11 | :dst_path => "/dst", 12 | ) 13 | end 14 | 15 | it_should_behave_like "wrappable request" 16 | 17 | its("class.type_camelized") { should == "CopyOut" } 18 | its("class.type_underscored") { should == "copy_out" } 19 | 20 | field :handle do 21 | it_should_be_required 22 | it_should_be_typed_as_string 23 | end 24 | 25 | field :src_path do 26 | it_should_be_required 27 | it_should_be_typed_as_string 28 | end 29 | 30 | field :dst_path do 31 | it_should_be_required 32 | it_should_be_typed_as_string 33 | end 34 | 35 | field :owner do 36 | it_should_be_optional 37 | it_should_be_typed_as_string 38 | end 39 | 40 | it "should respond to #create_response" do 41 | request.create_response.should be_a(Warden::Protocol::CopyOutResponse) 42 | end 43 | 44 | it_should_behave_like "documented request" 45 | end 46 | 47 | describe Warden::Protocol::CopyOutResponse do 48 | subject(:response) do 49 | described_class.new 50 | end 51 | 52 | it_should_behave_like "wrappable response" 53 | 54 | its("class.type_camelized") { should == "CopyOut" } 55 | its("class.type_underscored") { should == "copy_out" } 56 | 57 | it { should be_ok } 58 | it { should_not be_error } 59 | end 60 | -------------------------------------------------------------------------------- /warden-protocol/spec/link_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/link" 5 | 6 | describe Warden::Protocol::LinkRequest do 7 | subject(:request) do 8 | described_class.new(:handle => "handle", :job_id => 1) 9 | end 10 | 11 | it_should_behave_like "wrappable request" 12 | 13 | its("class.type_camelized") { should == "Link" } 14 | its("class.type_underscored") { should == "link" } 15 | 16 | field :handle do 17 | it_should_be_required 18 | it_should_be_typed_as_string 19 | end 20 | 21 | field :job_id do 22 | it_should_be_required 23 | it_should_be_typed_as_uint 24 | end 25 | 26 | it "should respond to #create_response" do 27 | request.create_response.should be_a(Warden::Protocol::LinkResponse) 28 | end 29 | 30 | it_should_behave_like "documented request" 31 | end 32 | 33 | describe Warden::Protocol::LinkResponse do 34 | subject(:response) do 35 | described_class.new 36 | end 37 | 38 | it_should_behave_like "wrappable response" 39 | 40 | its("class.type_camelized") { should == "Link" } 41 | its("class.type_underscored") { should == "link" } 42 | 43 | it { should be_ok } 44 | it { should_not be_error } 45 | 46 | field :exit_status do 47 | it_should_be_optional 48 | it_should_be_typed_as_uint 49 | end 50 | 51 | field :stdout do 52 | it_should_be_optional 53 | it_should_be_typed_as_string 54 | end 55 | 56 | field :stderr do 57 | it_should_be_optional 58 | it_should_be_typed_as_string 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /warden-protocol/spec/stream_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/stream" 5 | 6 | describe Warden::Protocol::StreamRequest do 7 | subject(:request) do 8 | described_class.new(:handle => "handle", :job_id => 1) 9 | end 10 | 11 | it_should_behave_like "wrappable request" 12 | 13 | its("class.type_camelized") { should == "Stream" } 14 | its("class.type_underscored") { should == "stream" } 15 | 16 | field :handle do 17 | it_should_be_required 18 | it_should_be_typed_as_string 19 | end 20 | 21 | field :job_id do 22 | it_should_be_required 23 | it_should_be_typed_as_uint 24 | end 25 | 26 | it "should respond to #create_response" do 27 | request.create_response.should be_a(Warden::Protocol::StreamResponse) 28 | end 29 | 30 | it_should_behave_like "documented request" 31 | end 32 | 33 | describe Warden::Protocol::StreamResponse do 34 | subject(:response) do 35 | described_class.new 36 | end 37 | 38 | it_should_behave_like "wrappable response" 39 | 40 | its("class.type_camelized") { should == "Stream" } 41 | its("class.type_underscored") { should == "stream" } 42 | 43 | it { should be_ok } 44 | it { should_not be_error } 45 | 46 | field :name do 47 | it_should_be_optional 48 | it_should_be_typed_as_string 49 | end 50 | 51 | field :data do 52 | it_should_be_optional 53 | it_should_be_typed_as_string 54 | end 55 | 56 | field :exit_status do 57 | it_should_be_optional 58 | it_should_be_typed_as_uint 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /warden-protocol/spec/spawn_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/spawn" 5 | 6 | describe Warden::Protocol::SpawnRequest do 7 | subject(:request) do 8 | described_class.new(:handle => "handle", :script => "echo foo") 9 | end 10 | 11 | it_should_behave_like "wrappable request" 12 | 13 | its("class.type_camelized") { should == "Spawn" } 14 | its("class.type_underscored") { should == "spawn" } 15 | 16 | field :handle do 17 | it_should_be_required 18 | end 19 | 20 | field :script do 21 | it_should_be_required 22 | end 23 | 24 | field :privileged do 25 | it_should_be_optional 26 | it_should_default_to false 27 | end 28 | 29 | it "should be populated with ResourceLimits object" do 30 | request.rlimits = Warden::Protocol::ResourceLimits.new 31 | request.should be_valid 32 | end 33 | 34 | it "should respond to #create_response" do 35 | request.create_response.should be_a(Warden::Protocol::SpawnResponse) 36 | end 37 | 38 | it_should_behave_like "documented request" 39 | end 40 | 41 | describe Warden::Protocol::SpawnResponse do 42 | subject(:response) do 43 | described_class.new(:job_id => 1) 44 | end 45 | 46 | it_should_behave_like "wrappable response" 47 | 48 | its("class.type_camelized") { should == "Spawn" } 49 | its("class.type_underscored") { should == "spawn" } 50 | 51 | it { should be_ok } 52 | it { should_not be_error } 53 | 54 | field :job_id do 55 | it_should_be_required 56 | it_should_be_typed_as_uint 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /warden-protocol/spec/net_in_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/net_in" 5 | 6 | describe Warden::Protocol::NetInRequest do 7 | subject(:request) do 8 | described_class.new(:handle => "handle") 9 | end 10 | 11 | it_should_behave_like "wrappable request" 12 | 13 | its("class.type_camelized") { should == "NetIn" } 14 | its("class.type_underscored") { should == "net_in" } 15 | 16 | field :handle do 17 | it_should_be_required 18 | it_should_be_typed_as_string 19 | end 20 | 21 | field :container_port do 22 | it_should_be_optional 23 | it_should_be_typed_as_uint 24 | end 25 | 26 | field :host_port do 27 | it_should_be_optional 28 | it_should_be_typed_as_uint 29 | end 30 | 31 | it "should respond to #create_response" do 32 | request.create_response.should be_a(Warden::Protocol::NetInResponse) 33 | end 34 | 35 | it_should_behave_like "documented request" 36 | end 37 | 38 | describe Warden::Protocol::NetInResponse do 39 | subject(:response) do 40 | described_class.new(:host_port => 1234, :container_port => 1234) 41 | end 42 | 43 | it_should_behave_like "wrappable response" 44 | 45 | its("class.type_camelized") { should == "NetIn" } 46 | its("class.type_underscored") { should == "net_in" } 47 | 48 | it { should be_ok } 49 | it { should_not be_error } 50 | 51 | field :host_port do 52 | it_should_be_required 53 | it_should_be_typed_as_uint 54 | end 55 | 56 | field :container_port do 57 | it_should_be_required 58 | it_should_be_typed_as_uint 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /warden/bin/warden-repl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", __FILE__) 3 | $LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__)) 4 | 5 | require "bundler" 6 | Bundler.setup 7 | 8 | require "optparse" 9 | 10 | require "warden/repl" 11 | 12 | options = {} 13 | OptionParser.new do |op| 14 | op.banner = <<-EOT 15 | Usage: warden-repl [options] 16 | Runs an interactive REPL by default. 17 | 18 | EOT 19 | op.on("-c COMMAND", "Run COMMAND non-interactively") do |command| 20 | options[:command] = command 21 | end 22 | 23 | op.on("-x", "Writes each command preceded by a '+' to stdout before executing") do 24 | options[:trace] = true 25 | end 26 | 27 | op.on("-e", 28 | "Only applicable to -c. If a multi-command string is supplied, exit" \ 29 | + " after the first unsuccessful command.") do 30 | options[:errexit] = true 31 | end 32 | end.parse! 33 | 34 | repl = Warden::Repl.new(options) 35 | 36 | if options[:command] 37 | commands = options[:command].split("\n") 38 | exit_status = nil 39 | commands.each do |command| 40 | command_info = repl.process_line(command) 41 | 42 | exit_status = 43 | if !command_info 44 | 0 45 | elsif command_info[:error] 46 | 1 47 | elsif command_info[:name] == "run" 48 | Integer(command_info[:result][0] || 1) 49 | else 50 | 0 51 | end 52 | 53 | if (exit_status != 0) && options[:errexit] 54 | break 55 | end 56 | end 57 | exit(exit_status) 58 | else 59 | trap('INT') { exit } 60 | repl.start 61 | end 62 | -------------------------------------------------------------------------------- /warden-protocol/spec/limit_bandwidth_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/limit_bandwidth" 5 | 6 | describe Warden::Protocol::LimitBandwidthRequest do 7 | subject(:request) do 8 | described_class.new(:handle => "handle") 9 | end 10 | 11 | it_should_behave_like "wrappable request" 12 | 13 | its("class.type_camelized") { should == "LimitBandwidth" } 14 | its("class.type_underscored") { should == "limit_bandwidth" } 15 | 16 | field :handle do 17 | it_should_be_required 18 | it_should_be_typed_as_string 19 | end 20 | 21 | field :rate do 22 | it_should_be_optional 23 | it_should_be_typed_as_uint64 24 | end 25 | 26 | field :burst do 27 | it_should_be_optional 28 | it_should_be_typed_as_uint64 29 | end 30 | 31 | it "should respond to #create_response" do 32 | request.create_response.should be_a(Warden::Protocol::LimitBandwidthResponse) 33 | end 34 | 35 | it_should_behave_like "documented request" 36 | end 37 | 38 | describe Warden::Protocol::LimitBandwidthResponse do 39 | subject(:response) do 40 | described_class.new 41 | end 42 | 43 | it_should_behave_like "wrappable response" 44 | 45 | its("class.type_camelized") { should == "LimitBandwidth" } 46 | its("class.type_underscored") { should == "limit_bandwidth" } 47 | 48 | it { should be_ok } 49 | it { should_not be_error } 50 | 51 | field :rate do 52 | it_should_be_optional 53 | it_should_be_typed_as_uint64 54 | end 55 | 56 | field :burst do 57 | it_should_be_optional 58 | it_should_be_typed_as_uint64 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /warden/lib/warden/pool/base.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "set" 4 | 5 | module Warden 6 | 7 | module Pool 8 | 9 | class Base 10 | 11 | # The release delay can be used to postpone the entry being 12 | # acquired again after being release. 13 | attr_reader :release_delay 14 | 15 | def initialize(count, options = {}) 16 | @pool = [] 17 | @release_delay = options.delete(:release_delay) || 0.0 18 | 19 | if block_given? 20 | @pool = count.times.map { |i| [nil, yield(i)] } 21 | end 22 | end 23 | 24 | def size 25 | @pool.size 26 | end 27 | 28 | def delete(*entries) 29 | entry_set = Set.new(entries) 30 | @pool.delete_if { |e| entry_set.include?(e[1]) } 31 | nil 32 | end 33 | 34 | def acquire 35 | time, entry = @pool.first 36 | 37 | if time == nil || time < Time.now 38 | @pool.shift 39 | return entry 40 | end 41 | 42 | return nil 43 | end 44 | 45 | def fetch(entry) 46 | pair = @pool.find do |e| 47 | e[1] == entry && (e[0] == nil || e[0] < Time.now) 48 | end 49 | 50 | if pair 51 | @pool.delete(pair) 52 | return entry 53 | end 54 | 55 | return nil 56 | end 57 | 58 | def release(entry) 59 | return unless belongs?(entry) 60 | 61 | @pool.push [Time.now + @release_delay, entry] 62 | end 63 | 64 | private 65 | 66 | def belongs?(entry) 67 | true 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /warden/src/wsh/barrier.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "barrier.h" 9 | #include "util.h" 10 | 11 | int barrier_open(barrier_t *bar) { 12 | int rv; 13 | int aux[2] = { -1, -1 }; 14 | 15 | rv = pipe(aux); 16 | if (rv == -1) { 17 | perror("pipe"); 18 | goto err; 19 | } 20 | 21 | fcntl_mix_cloexec(aux[0]); 22 | fcntl_mix_cloexec(aux[1]); 23 | 24 | bar->fd[0] = aux[0]; 25 | bar->fd[1] = aux[1]; 26 | return 0; 27 | 28 | err: 29 | if (aux[0] >= 0) close(aux[0]); 30 | if (aux[1] >= 0) close(aux[1]); 31 | return -1; 32 | } 33 | 34 | void barrier_close(barrier_t *bar) { 35 | close(bar->fd[0]); 36 | close(bar->fd[1]); 37 | } 38 | 39 | void barrier_close_wait(barrier_t *bar) { 40 | close(bar->fd[0]); 41 | } 42 | 43 | void barrier_close_signal(barrier_t *bar) { 44 | close(bar->fd[1]); 45 | } 46 | 47 | int barrier_wait(barrier_t *bar) { 48 | char buf[1]; 49 | int nread; 50 | 51 | /* Close signal side of pipe on wait */ 52 | barrier_close_signal(bar); 53 | 54 | nread = read(bar->fd[0], buf, sizeof(buf)); 55 | if (nread == -1) { 56 | perror("read"); 57 | return -1; 58 | } else if (nread == 0) { 59 | return -1; 60 | } 61 | 62 | return 0; 63 | } 64 | 65 | int barrier_signal(barrier_t *bar) { 66 | int rv; 67 | char byte = '\0'; 68 | 69 | /* Close wait side of pipe on signal */ 70 | barrier_close_wait(bar); 71 | 72 | rv = write(bar->fd[1], &byte, 1); 73 | if (rv == -1) { 74 | perror("write"); 75 | return -1; 76 | } 77 | 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /warden/spec/pool/port_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/pool/port" 5 | 6 | describe Warden::Pool::Port do 7 | 8 | context "create" do 9 | 10 | it "should fail when the result contains less than 1000 ports" do 11 | Warden::Pool::Port.should_receive(:ip_local_port_range).and_return([32768, 64002]) 12 | 13 | expect do 14 | pool = Warden::Pool::Port.new 15 | end.to raise_error Warden::WardenError 16 | end 17 | 18 | it "should succeed when the result contains more than 1000 ports" do 19 | Warden::Pool::Port.should_receive(:ip_local_port_range).and_return([32768, 61000]) 20 | pool = Warden::Pool::Port.new 21 | 22 | # Check size 23 | start, stop = 61001, 65001 24 | pool.size.should == stop - start 25 | 26 | # Check first entry 27 | pool.acquire.should == start 28 | end 29 | end 30 | 31 | context "acquire" do 32 | 33 | it "should raise when no port is available" do 34 | Warden::Pool::Port.should_receive(:ip_local_port_range).and_return([32768, 61000]) 35 | pool = Warden::Pool::Port.new 36 | 37 | expect do 38 | (pool.size + 1).times { pool.acquire } 39 | end.to raise_error Warden::Pool::Port::NoPortAvailable 40 | end 41 | end 42 | 43 | context "release" do 44 | 45 | it "should ignore ports that don't belong to the pool" do 46 | Warden::Pool::Port.should_receive(:ip_local_port_range).and_return([32768, 61000]) 47 | pool = Warden::Pool::Port.new 48 | old_size = pool.size 49 | pool.release(32000) 50 | 51 | pool.size.should == old_size 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /warden/lib/warden/pool/port.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/errors" 4 | require "warden/pool/base" 5 | 6 | module Warden 7 | 8 | module Pool 9 | 10 | class Port < Base 11 | 12 | class NoPortAvailable < WardenError 13 | 14 | def message 15 | super || "no port available" 16 | end 17 | end 18 | 19 | def self.ip_local_port_range 20 | File.read("/proc/sys/net/ipv4/ip_local_port_range").split.map(&:to_i) 21 | end 22 | 23 | def initialize(options = {}) 24 | ephemeral_start, ephemeral_stop = self.class.ip_local_port_range 25 | start = ephemeral_stop + 1 26 | stop = 65000 + 1 27 | count = stop - start 28 | 29 | # Hardcode the minimum number of ports to 1000 30 | if count < 1000 31 | raise WardenError.new \ 32 | "Insufficient non-ephemeral ports available" + 33 | " (expected >= %d, got: %d)" % [1000, count] 34 | end 35 | 36 | @start_port = start 37 | @end_port = start + (count - 1) 38 | 39 | # The port range spanned by [start, stop) does not overlap with the 40 | # ephemeral port range and will therefore not conflict with ports 41 | # used by locally originated connection. It is safe to map these 42 | # ports to containers. 43 | super(count) do |i| 44 | start + i 45 | end 46 | end 47 | 48 | def acquire 49 | super.tap do |port| 50 | raise NoPortAvailable unless port 51 | end 52 | end 53 | 54 | private 55 | 56 | def belongs?(port) 57 | (port >= @start_port) && (port <= @end_port) 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/lib/hook-parent-after-clone.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname $0)/../ 9 | 10 | source ./lib/common.sh 11 | 12 | # Add new group for every subsystem 13 | for system_path in /sys/fs/cgroup/* 14 | do 15 | instance_path=$system_path/instance-$id 16 | 17 | mkdir -p $instance_path 18 | 19 | if [ $(basename $system_path) == "cpuset" ] 20 | then 21 | cat $system_path/cpuset.cpus > $instance_path/cpuset.cpus 22 | cat $system_path/cpuset.mems > $instance_path/cpuset.mems 23 | fi 24 | 25 | if [ $(basename $system_path) == "devices" ] 26 | then 27 | # disallow everything, allow explicitly 28 | echo a > $instance_path/devices.deny 29 | # /dev/null 30 | echo "c 1:3 rw" > $instance_path/devices.allow 31 | # /dev/zero 32 | echo "c 1:5 rw" > $instance_path/devices.allow 33 | # /dev/random 34 | echo "c 1:8 rw" > $instance_path/devices.allow 35 | # /dev/urandom 36 | echo "c 1:9 rw" > $instance_path/devices.allow 37 | # /dev/tty 38 | echo "c 5:0 rw" > $instance_path/devices.allow 39 | # /dev/ptmx 40 | echo "c 5:2 rw" > $instance_path/devices.allow 41 | # /dev/pts/* 42 | echo "c 136:* rw" > $instance_path/devices.allow 43 | fi 44 | 45 | echo 1 > $instance_path/cgroup.clone_children 46 | echo $PID > $instance_path/tasks 47 | done 48 | 49 | echo $PID > ./run/wshd.pid 50 | 51 | ip link add name $network_host_iface type veth peer name $network_container_iface 52 | ip link set $network_host_iface netns 1 53 | ip link set $network_container_iface netns $PID 54 | 55 | ifconfig $network_host_iface $network_host_ip netmask $network_netmask 56 | 57 | exit 0 58 | -------------------------------------------------------------------------------- /warden-protocol/spec/run_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/run" 5 | 6 | describe Warden::Protocol::RunRequest do 7 | subject(:request) do 8 | described_class.new(:handle => "handle", :script => "echo foo") 9 | end 10 | 11 | it_should_behave_like "wrappable request" 12 | 13 | its("class.type_camelized") { should == "Run" } 14 | its("class.type_underscored") { should == "run" } 15 | 16 | field :handle do 17 | it_should_be_required 18 | end 19 | 20 | field :script do 21 | it_should_be_required 22 | end 23 | 24 | field :privileged do 25 | it_should_be_optional 26 | it_should_default_to false 27 | end 28 | 29 | field :rlimits do 30 | it_should_be_optional 31 | 32 | it "should be populated with ResourceLimits object" do 33 | request.rlimits = Warden::Protocol::ResourceLimits.new 34 | request.should be_valid 35 | end 36 | end 37 | 38 | it "should respond to #create_response" do 39 | request.create_response.should be_a(Warden::Protocol::RunResponse) 40 | end 41 | 42 | it_should_behave_like "documented request" 43 | end 44 | 45 | describe Warden::Protocol::RunResponse do 46 | subject(:response) do 47 | described_class.new 48 | end 49 | 50 | it_should_behave_like "wrappable response" 51 | 52 | its("class.type_camelized") { should == "Run" } 53 | its("class.type_underscored") { should == "run" } 54 | 55 | it { should be_ok } 56 | it { should_not be_error } 57 | 58 | field :exit_status do 59 | it_should_be_optional 60 | it_should_be_typed_as_uint 61 | end 62 | 63 | field :stdout do 64 | it_should_be_optional 65 | it_should_be_typed_as_string 66 | end 67 | 68 | field :stderr do 69 | it_should_be_optional 70 | it_should_be_typed_as_string 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /warden/root/linux/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname "${0}") 9 | 10 | # Check if the old mount point exists, and if so clean it up 11 | if [ -d /dev/cgroup ] 12 | then 13 | if grep -q /dev/cgroup /proc/mounts 14 | then 15 | umount /dev/cgroup 16 | fi 17 | 18 | rmdir /dev/cgroup 19 | fi 20 | 21 | cgroup_path=/sys/fs/cgroup 22 | 23 | if [ ! -d $cgroup_path ] 24 | then 25 | echo "$cgroup_path does not exist..." 26 | exit 1 27 | fi 28 | 29 | # Check if /sys/fs/cgroup is mounted with a cgroup mount, and umount if so 30 | if grep "${cgroup_path} " /proc/mounts | cut -d' ' -f3 | grep -q cgroup 31 | then 32 | find $cgroup_path -mindepth 1 -type d | sort | tac | xargs rmdir 33 | umount $cgroup_path 34 | fi 35 | 36 | # Mount tmpfs 37 | if ! grep "${cgroup_path} " /proc/mounts | cut -d' ' -f3 | grep -q tmpfs 38 | then 39 | mount -t tmpfs none $cgroup_path 40 | fi 41 | 42 | # Mount cgroup subsystems individually 43 | for subsystem in cpu cpuacct devices memory 44 | do 45 | mkdir -p $cgroup_path/$subsystem 46 | 47 | if ! grep -q "${cgroup_path}/$subsystem " /proc/mounts 48 | then 49 | mount -t cgroup -o $subsystem none $cgroup_path/$subsystem 50 | fi 51 | done 52 | 53 | ./net.sh setup 54 | 55 | # Disable AppArmor if possible 56 | if [ -x /etc/init.d/apparmor ]; then 57 | /etc/init.d/apparmor teardown 58 | fi 59 | 60 | # quotaon(8) exits with non-zero status when quotas are ENABLED 61 | if [ "$DISK_QUOTA_ENABLED" = "true" ] && quotaon -p $CONTAINER_DEPOT_MOUNT_POINT_PATH > /dev/null 62 | then 63 | mount -o remount,usrjquota=aquota.user,grpjquota=aquota.group,jqfmt=vfsv0 $CONTAINER_DEPOT_MOUNT_POINT_PATH 64 | quotacheck -ugmb -F vfsv0 $CONTAINER_DEPOT_MOUNT_POINT_PATH 65 | quotaon $CONTAINER_DEPOT_MOUNT_POINT_PATH 66 | fi 67 | -------------------------------------------------------------------------------- /warden-client/spec/support/mock_warden_server.rb: -------------------------------------------------------------------------------- 1 | require "socket" 2 | require "tempfile" 3 | require "warden/protocol" 4 | 5 | class Session 6 | 7 | def initialize(sock, handler = nil) 8 | @sock = sock 9 | @handler = handler 10 | 11 | # Post-initialization 12 | handle(nil) 13 | end 14 | 15 | def handle(request) 16 | @handler.call(self, request) if @handler 17 | end 18 | 19 | def close 20 | @sock.close 21 | ensure 22 | @sock = nil 23 | end 24 | 25 | def respond(*responses) 26 | responses.each do |response| 27 | data = response.wrap.encode.to_s 28 | @sock.write data.length.to_s + "\r\n" 29 | @sock.write data + "\r\n" 30 | end 31 | end 32 | 33 | def run! 34 | while @sock && length = @sock.gets 35 | data = @sock.read(length.to_i) 36 | 37 | # Discard \r\n 38 | @sock.read(2) 39 | 40 | wrapped_request = Warden::Protocol::WrappedRequest.decode(data) 41 | handle(wrapped_request.request) 42 | end 43 | end 44 | end 45 | 46 | shared_context :mock_warden_server do 47 | 48 | SERVER_PATH = File.expand_path("../../../tmp/mock_server.sock", __FILE__) 49 | 50 | def new_client 51 | Warden::Client.new(SERVER_PATH) 52 | end 53 | 54 | def start_server(&blk) 55 | # Make sure the path to the unix socket is not used 56 | FileUtils.rm_rf(SERVER_PATH) 57 | 58 | # Create unix socket server 59 | server = UNIXServer.new(SERVER_PATH) 60 | 61 | # Accept new connections from a thread 62 | @server = Thread.new do 63 | begin 64 | loop do 65 | session = Session.new(server.accept, blk) 66 | session.run! 67 | end 68 | rescue => ex 69 | STDERR.puts ex.message 70 | STDERR.puts ex.backtrace 71 | raise 72 | end 73 | end 74 | end 75 | 76 | after(:each) do 77 | @server.kill if @server 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/buffer.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class Buffer 8 | CRLF = "\r\n" 9 | 10 | def self.request_to_wire(request) 11 | unless request.kind_of?(BaseRequest) 12 | raise ArgumentError, "Expected #kind_of? ::%s" % BaseRequest.name 13 | end 14 | payload_to_wire request.wrap.encode.to_s 15 | end 16 | 17 | def self.response_to_wire(response) 18 | unless response.kind_of?(BaseResponse) 19 | raise ArgumentError, "Expected #kind_of? ::%s" % BaseResponse.name 20 | end 21 | payload_to_wire response.wrap.encode.to_s 22 | end 23 | 24 | def initialize 25 | @buffer = "" 26 | end 27 | 28 | def <<(data) 29 | @buffer += data 30 | end 31 | 32 | def each_request(&blk) 33 | each do |payload| 34 | yield(Warden::Protocol::WrappedRequest.decode(payload).request) 35 | end 36 | end 37 | 38 | def each_response(&blk) 39 | each do |payload| 40 | yield(Warden::Protocol::WrappedResponse.decode(payload).response) 41 | end 42 | end 43 | 44 | protected 45 | 46 | def self.payload_to_wire(payload) 47 | payload.to_s.length.to_s + CRLF + payload.to_s + CRLF 48 | end 49 | 50 | def each 51 | loop do 52 | crlf = @buffer.index(CRLF) 53 | break unless crlf 54 | 55 | length = Integer(@buffer[0...crlf]) 56 | protocol_length = crlf + 2 + length + 2 57 | break unless @buffer.length >= protocol_length 58 | 59 | payload = @buffer[crlf + 2, length] 60 | 61 | # Trim buffer 62 | @buffer = @buffer[protocol_length..-1] 63 | 64 | yield(payload) 65 | end 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/limit_disk.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | 8 | # The byte limits are used to compute block limits on the server side. 9 | 10 | class LimitDiskRequest < BaseRequest 11 | required :handle, :string, 1 12 | 13 | optional :block_limit, :uint32, 10 # Alias for `block_hard` 14 | optional :block, :uint64, 11 # Alias for `block_hard` 15 | optional :block_soft, :uint64, 12 16 | optional :block_hard, :uint64, 13 17 | 18 | optional :inode_limit, :uint32, 20 # Alias for `inode_hard` 19 | optional :inode, :uint64, 21 # Alias for `inode_hard` 20 | optional :inode_soft, :uint64, 22 21 | optional :inode_hard, :uint64, 23 22 | 23 | optional :byte_limit, :uint32, 30 # Alias for `byte_hard` 24 | optional :byte, :uint64, 31 # Alias for `byte_hard` 25 | optional :byte_soft, :uint64, 32 26 | optional :byte_hard, :uint64, 33 27 | 28 | def self.description 29 | "set or get the disk limit for the container." 30 | end 31 | end 32 | 33 | class LimitDiskResponse < BaseResponse 34 | optional :block_limit, :uint32, 10 # Alias for `block_hard` 35 | optional :block, :uint64, 11 # Alias for `block_hard` 36 | optional :block_soft, :uint64, 12 37 | optional :block_hard, :uint64, 13 38 | 39 | optional :inode_limit, :uint32, 20 # Alias for `inode_hard` 40 | optional :inode, :uint64, 21 # Alias for `inode_hard` 41 | optional :inode_soft, :uint64, 22 42 | optional :inode_hard, :uint64, 23 43 | 44 | optional :byte_limit, :uint32, 30 # Alias for `byte_hard` 45 | optional :byte, :uint64, 31 # Alias for `byte_hard` 46 | optional :byte_soft, :uint64, 32 47 | optional :byte_hard, :uint64, 33 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /warden/spec/network_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/network" 5 | 6 | describe Warden::Network do 7 | describe Warden::Network::Netmask do 8 | def instance(*octets) 9 | Warden::Network::Netmask.new(*octets) 10 | end 11 | 12 | it "should raise on invalid masks" do 13 | test = lambda { |octets| 14 | lambda { 15 | instance(*octets) 16 | }.should raise_error 17 | } 18 | 19 | [ 20 | [255, 255, 255, 253], 21 | [255, 255, 255, 251], 22 | [255, 255, 255, 250], 23 | [255, 255, 255, 249], 24 | [255, 255, 255, 247], 25 | [255, 255, 254, 1], 26 | [255, 255, 253, 0], 27 | ].each(&test) 28 | end 29 | 30 | it "should accept valid masks" do 31 | test = lambda { |octets| 32 | lambda { 33 | instance(*octets) 34 | }.should_not raise_error 35 | } 36 | 37 | [ 38 | [255, 255, 255, 255], 39 | [255, 255, 255, 254], 40 | [255, 255, 255, 252], 41 | [255, 255, 255, 248], 42 | [255, 255, 255, 240], 43 | [255, 255, 254, 0], 44 | [255, 255, 252, 0], 45 | ].each(&test) 46 | end 47 | 48 | it "should know its size" do 49 | instance(255, 255, 255, 255).size.should == 1 50 | instance(255, 255, 255, 254).size.should == 2 51 | instance(255, 255, 255, 252).size.should == 4 52 | instance(255, 255, 254, 0).size.should == 512 53 | instance(255, 255, 252, 0).size.should == 1024 54 | end 55 | end 56 | 57 | describe Warden::Network::Address do 58 | it "should know its network given a mask" do 59 | mask = Warden::Network::Netmask.new(255, 255, 255, 0) 60 | address = Warden::Network::Address.new(10, 0, 128, 54) 61 | address.network(mask).should eql Warden::Network::Address.new(10, 0, 128, 0) 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /warden/spec/pool/base_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/pool/base" 5 | 6 | describe Warden::Pool::Base do 7 | 8 | context "create" do 9 | 10 | it "should use a block to populate" do 11 | pool = Warden::Pool::Base.new(5) { |i| i } 12 | pool.size.should == 5 13 | end 14 | 15 | it "should take a release delay" do 16 | pool = Warden::Pool::Base.new(1, :release_delay => 0.01) { |i| i } 17 | 18 | # Acquire and release 19 | entry = pool.acquire 20 | pool.release(entry) 21 | 22 | # It should not be possible to immediately acquire the entry again 23 | pool.acquire.should be_nil 24 | sleep 0.02 25 | pool.acquire.should_not be_nil 26 | end 27 | end 28 | 29 | context "acquire" do 30 | 31 | it "should return nil when empty" do 32 | pool = Warden::Pool::Base.new(0) { |i| i } 33 | pool.acquire.should == nil 34 | end 35 | 36 | it "should return entry when not empty" do 37 | pool = Warden::Pool::Base.new(1) { |i| i } 38 | pool.acquire.should == 0 39 | end 40 | end 41 | 42 | context "fetch" do 43 | 44 | it "should return nil when empty" do 45 | pool = Warden::Pool::Base.new(0) { |i| i } 46 | pool.fetch(0).should == nil 47 | end 48 | 49 | it "should return entry when it exists" do 50 | pool = Warden::Pool::Base.new(5) { |i| i } 51 | pool.fetch(1).should == 1 52 | pool.fetch(1).should == nil 53 | end 54 | 55 | it "should return nil when not available" do 56 | pool = Warden::Pool::Base.new(5) { |i| i } 57 | pool.fetch(10).should == nil 58 | end 59 | end 60 | 61 | context "release" do 62 | 63 | it "should make entry size again" do 64 | pool = Warden::Pool::Base.new(0) { |i| i } 65 | pool.size.should == 0 66 | pool.release(0) 67 | pool.size.should == 1 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /warden-protocol/spec/create_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/create" 5 | 6 | describe Warden::Protocol::CreateRequest do 7 | subject(:request) do 8 | described_class.new 9 | end 10 | 11 | it_should_behave_like "wrappable request" 12 | 13 | its("class.type_camelized") { should == "Create" } 14 | its("class.type_underscored") { should == "create" } 15 | 16 | field :bind_mounts do 17 | it_should_be_optional 18 | 19 | it "should be populated with BindMount objects" do 20 | m = Warden::Protocol::CreateRequest::BindMount.new 21 | m.src_path = "/src" 22 | m.dst_path = "/dst" 23 | m.mode = Warden::Protocol::CreateRequest::BindMount::Mode::RO 24 | 25 | subject.bind_mounts = [m] 26 | subject.should be_valid 27 | end 28 | end 29 | 30 | field :grace_time do 31 | it_should_be_optional 32 | it_should_be_typed_as_uint 33 | end 34 | 35 | field :handle do 36 | it_should_be_optional 37 | it_should_be_typed_as_string 38 | end 39 | 40 | field :network do 41 | it_should_be_optional 42 | it_should_be_typed_as_string 43 | end 44 | 45 | field :rootfs do 46 | it_should_be_optional 47 | it_should_be_typed_as_string 48 | end 49 | 50 | it "should respond to #create_response" do 51 | request.create_response.should be_a(Warden::Protocol::CreateResponse) 52 | end 53 | 54 | it_should_behave_like "documented request" 55 | end 56 | 57 | describe Warden::Protocol::CreateResponse do 58 | subject(:response) do 59 | described_class.new(:handle => "handle") 60 | end 61 | 62 | it_should_behave_like "wrappable response" 63 | 64 | its("class.type_camelized") { should == "Create" } 65 | its("class.type_underscored") { should == "create" } 66 | 67 | it { should be_ok } 68 | it { should_not be_error } 69 | 70 | field :handle do 71 | it_should_be_required 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /warden/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: https://github.com/cloudfoundry/common.git 3 | revision: 2f45a7ff9d82896704d12e3d37a7379fe2d80375 4 | specs: 5 | em-posix-spawn (0.1.6) 6 | eventmachine 7 | posix-spawn 8 | 9 | GIT 10 | remote: https://github.com/cloudfoundry/membrane.git 11 | revision: b33b45c7b8a89af2b40aae1e1cb16e394877681f 12 | specs: 13 | membrane (0.0.3) 14 | 15 | GIT 16 | remote: https://github.com/cloudfoundry/steno.git 17 | revision: e71a658f05b7cbf54006c3cc29db752cdfb192df 18 | specs: 19 | steno (0.0.15) 20 | grape 21 | yajl-ruby 22 | 23 | GIT 24 | remote: https://github.com/cloudfoundry/warden.git 25 | revision: 968745ac412ba9ce1d8e490979b8c81e784770e0 26 | ref: 968745ac 27 | specs: 28 | warden-client (0.0.7) 29 | warden-protocol 30 | warden-protocol (0.0.11) 31 | beefcake 32 | 33 | GEM 34 | remote: http://rubygems.org/ 35 | specs: 36 | beefcake (0.3.7) 37 | diff-lcs (1.1.3) 38 | eventmachine (1.0.0) 39 | ffi (1.0.11) 40 | grape (0.2.1) 41 | hashie (~> 1.2) 42 | multi_json 43 | multi_xml 44 | rack 45 | rack-mount 46 | hashie (1.2.0) 47 | multi_json (1.3.6) 48 | multi_xml (0.5.1) 49 | posix-spawn (0.3.6) 50 | rack (1.4.1) 51 | rack-mount (0.8.3) 52 | rack (>= 1.0.0) 53 | rake (0.9.2.2) 54 | rspec (2.11.0) 55 | rspec-core (~> 2.11.0) 56 | rspec-expectations (~> 2.11.0) 57 | rspec-mocks (~> 2.11.0) 58 | rspec-core (2.11.0) 59 | rspec-expectations (2.11.1) 60 | diff-lcs (~> 1.1.3) 61 | rspec-mocks (2.11.1) 62 | sys-filesystem (1.0.0) 63 | ffi (>= 1.0.0) 64 | yajl-ruby (1.1.0) 65 | 66 | PLATFORMS 67 | ruby 68 | 69 | DEPENDENCIES 70 | em-posix-spawn! 71 | eventmachine (~> 1.0.0) 72 | membrane! 73 | rake 74 | rspec 75 | steno (~> 0.0.15)! 76 | sys-filesystem 77 | warden-client! 78 | warden-protocol! 79 | -------------------------------------------------------------------------------- /warden-protocol/spec/resource_limits_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/run" 5 | 6 | describe Warden::Protocol::ResourceLimits do 7 | subject do 8 | described_class.new 9 | end 10 | 11 | its("class.type_camelized") { should == "ResourceLimits" } 12 | its("class.type_underscored") { should == "resource_limits" } 13 | 14 | field :as do 15 | it_should_be_optional 16 | it_should_be_typed_as_uint64 17 | end 18 | 19 | field :core do 20 | it_should_be_optional 21 | it_should_be_typed_as_uint64 22 | end 23 | 24 | field :cpu do 25 | it_should_be_optional 26 | it_should_be_typed_as_uint64 27 | end 28 | 29 | field :data do 30 | it_should_be_optional 31 | it_should_be_typed_as_uint64 32 | end 33 | 34 | field :fsize do 35 | it_should_be_optional 36 | it_should_be_typed_as_uint64 37 | end 38 | 39 | field :locks do 40 | it_should_be_optional 41 | it_should_be_typed_as_uint64 42 | end 43 | 44 | field :memlock do 45 | it_should_be_optional 46 | it_should_be_typed_as_uint64 47 | end 48 | 49 | field :msgqueue do 50 | it_should_be_optional 51 | it_should_be_typed_as_uint64 52 | end 53 | 54 | field :nice do 55 | it_should_be_optional 56 | it_should_be_typed_as_uint64 57 | end 58 | 59 | field :nofile do 60 | it_should_be_optional 61 | it_should_be_typed_as_uint64 62 | end 63 | 64 | field :nproc do 65 | it_should_be_optional 66 | it_should_be_typed_as_uint64 67 | end 68 | 69 | field :rss do 70 | it_should_be_optional 71 | it_should_be_typed_as_uint64 72 | end 73 | 74 | field :rtprio do 75 | it_should_be_optional 76 | it_should_be_typed_as_uint64 77 | end 78 | 79 | field :sigpending do 80 | it_should_be_optional 81 | it_should_be_typed_as_uint64 82 | end 83 | 84 | field :stack do 85 | it_should_be_optional 86 | it_should_be_typed_as_uint64 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /warden/lib/warden/container/features/cgroup.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/container/spawn" 4 | 5 | module Warden 6 | 7 | module Container 8 | 9 | module Features 10 | 11 | module Cgroup 12 | 13 | def cgroup_path(subsystem) 14 | File.join("/sys/fs/cgroup", subsystem.to_s, "instance-#{self.handle}") 15 | end 16 | 17 | def do_info(request, response) 18 | super(request, response) 19 | 20 | begin 21 | fields = read_memory_stats 22 | response.memory_stat = Protocol::InfoResponse::MemoryStat.new(fields) 23 | rescue => e 24 | raise WardenError.new("Failed getting memory usage: #{e}") 25 | end 26 | 27 | begin 28 | fields = read_cpu_stats 29 | response.cpu_stat = Protocol::InfoResponse::CpuStat.new(fields) 30 | rescue => e 31 | raise WardenError.new("Failed getting cpu stats: #{e}") 32 | end 33 | 34 | nil 35 | end 36 | 37 | def read_memory_stats 38 | lines = File.read(File.join(cgroup_path(:memory), "memory.stat")).split(/\r?\n/) 39 | 40 | mem_stats = {} 41 | 42 | lines.map do |line| 43 | field, value = line.split(" ", 2) 44 | mem_stats[field.to_sym] = value.to_i 45 | end 46 | 47 | mem_stats 48 | end 49 | 50 | def read_cpu_stats 51 | cpu_stats = {} 52 | 53 | lines = File.read(File.join(cgroup_path(:cpuacct), "cpuacct.usage")).split(/\r?\n/) 54 | cpu_stats[:usage] = Integer(lines.first.strip) 55 | 56 | lines = File.read(File.join(cgroup_path(:cpuacct), "cpuacct.stat")).split(/\r?\n/) 57 | lines.map do |line| 58 | field, value = line.split(" ", 2) 59 | cpu_stats[field.to_sym] = Integer(value) 60 | end 61 | 62 | cpu_stats 63 | end 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /warden-protocol/spec/buffer_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/buffer" 5 | require "warden/protocol/echo" 6 | 7 | describe Warden::Protocol::Buffer do 8 | let(:request) { Warden::Protocol::EchoRequest.new(:message => "request") } 9 | let(:response) { Warden::Protocol::EchoResponse.new(:message => "response") } 10 | 11 | it "should support iterating over requests" do 12 | subject << Warden::Protocol::Buffer.request_to_wire(request) 13 | subject.each_request do |request| 14 | request.class.should == Warden::Protocol::EchoRequest 15 | request.message.should == "request" 16 | end 17 | end 18 | 19 | it "should support iterating over responses" do 20 | subject << Warden::Protocol::Buffer.response_to_wire(response) 21 | subject.each_response do |response| 22 | response.class.should == Warden::Protocol::EchoResponse 23 | response.message.should == "response" 24 | end 25 | end 26 | 27 | describe "fuzzing" do 28 | it "should not break request iteration" do 29 | data = Warden::Protocol::Buffer.request_to_wire(request) 30 | 31 | loop do 32 | chunk = data.slice!(0) 33 | subject << chunk 34 | break if data.empty? 35 | 36 | subject.each_request do 37 | fail 38 | end 39 | end 40 | 41 | subject.each_request do |request| 42 | request.class.should == Warden::Protocol::EchoRequest 43 | request.message.should == "request" 44 | end 45 | end 46 | 47 | it "should not break response iteration" do 48 | data = Warden::Protocol::Buffer.response_to_wire(response) 49 | 50 | loop do 51 | chunk = data.slice!(0) 52 | subject << chunk 53 | break if data.empty? 54 | 55 | subject.each_response do 56 | fail 57 | end 58 | end 59 | 60 | subject.each_response do |response| 61 | response.class.should == Warden::Protocol::EchoResponse 62 | response.message.should == "response" 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /warden/src/iomux/test/test_ring_buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "ring_buffer.h" 7 | #include "test_util.h" 8 | #include "util.h" 9 | 10 | static void test_ring_buffer_read_write(void) { 11 | ring_buffer_t *rb = NULL; 12 | uint8_t *str = (uint8_t *) "AAAABBBBCCCC"; 13 | size_t str_size = 12; 14 | size_t buf_capacity = 8; 15 | size_t bytes_read = 0; 16 | uint8_t read_buf[buf_capacity]; 17 | 18 | rb = ring_buffer_alloc(buf_capacity); 19 | 20 | /* Write less than capacity */ 21 | ring_buffer_append(rb, str, 4); 22 | 23 | /* Verify we can read it back */ 24 | bytes_read = ring_buffer_read(rb, 0, read_buf, buf_capacity); 25 | TEST_CHECK(bytes_read == 4); 26 | TEST_CHECK(!memcmp(read_buf, "AAAA", bytes_read)); 27 | 28 | /* Write a string that causes the buffer to wrap */ 29 | ring_buffer_append(rb, str + 4, str_size - 4); 30 | 31 | /* Verify we can read it back */ 32 | bytes_read = ring_buffer_read(rb, 0, read_buf, str_size - 4); 33 | TEST_CHECK(bytes_read == str_size - 4); 34 | TEST_CHECK(!memcmp(read_buf, "BBBBCCCC", bytes_read)); 35 | 36 | /* Verify we can read something from the middle of the buffer */ 37 | bytes_read = ring_buffer_read(rb, 2, read_buf, 4); 38 | TEST_CHECK(bytes_read == 4); 39 | TEST_CHECK(!memcmp(read_buf, "BBCC", bytes_read)); 40 | 41 | /* Verify we only read what is available */ 42 | bytes_read = ring_buffer_read(rb, 4, read_buf, 8); 43 | TEST_CHECK(bytes_read == 4); 44 | TEST_CHECK(!memcmp(read_buf, "CCCC", bytes_read)); 45 | 46 | /* Write something larger than the capacity */ 47 | ring_buffer_append(rb, str, str_size); 48 | 49 | /* Verify the correct substring is written */ 50 | bytes_read = ring_buffer_read(rb, 0, read_buf, buf_capacity); 51 | TEST_CHECK(bytes_read == buf_capacity); 52 | TEST_CHECK(!memcmp(read_buf, "BBBBCCCC", bytes_read)); 53 | 54 | ring_buffer_free(rb); 55 | } 56 | 57 | void test_ring_buffer(void) { 58 | test_ring_buffer_read_write(); 59 | } 60 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname $0) 9 | 10 | source ./lib/common.sh 11 | 12 | # Defaults for debugging the setup script 13 | id=${id:-test} 14 | network_netmask=${network_netmask:-255.255.255.252} 15 | network_host_ip=${network_host_ip:-10.0.0.1} 16 | network_host_iface="w-${id}-0" 17 | network_container_ip=${network_container_ip:-10.0.0.2} 18 | network_container_iface="w-${id}-1" 19 | user_uid=${user_uid:-10000} 20 | rootfs_path=$(readlink -f $rootfs_path) 21 | 22 | # Write configuration 23 | cat > etc/config <<-EOS 24 | id=$id 25 | network_netmask=$network_netmask 26 | network_host_ip=$network_host_ip 27 | network_host_iface=$network_host_iface 28 | network_container_ip=$network_container_ip 29 | network_container_iface=$network_container_iface 30 | user_uid=$user_uid 31 | rootfs_path=$rootfs_path 32 | EOS 33 | 34 | setup_fs 35 | 36 | # Remove files we don't need or want 37 | rm -f mnt/var/cache/apt/archives/*.deb 38 | rm -f mnt/var/cache/apt/*cache.bin 39 | rm -f mnt/var/lib/apt/lists/*_Packages 40 | rm -f mnt/etc/ssh/ssh_host_* 41 | 42 | # Strip /dev down to the bare minimum 43 | rm -rf mnt/dev 44 | mkdir -p mnt/dev 45 | 46 | # /dev/tty 47 | file=mnt/dev/tty 48 | mknod -m 666 $file c 5 0 49 | chown root:tty $file 50 | 51 | # /dev/random, /dev/urandom 52 | file=mnt/dev/random 53 | mknod -m 666 $file c 1 8 54 | chown root:root $file 55 | file=mnt/dev/urandom 56 | mknod -m 666 $file c 1 9 57 | chown root:root $file 58 | 59 | # /dev/null, /dev/zero 60 | file=mnt/dev/null 61 | mknod -m 666 $file c 1 3 62 | chown root:root $file 63 | file=mnt/dev/zero 64 | mknod -m 666 $file c 1 5 65 | chown root:root $file 66 | 67 | # /dev/fd, /dev/std{in,out,err} 68 | pushd mnt/dev > /dev/null 69 | ln -s /proc/self/fd 70 | ln -s fd/0 stdin 71 | ln -s fd/1 stdout 72 | ln -s fd/2 stderr 73 | popd > /dev/null 74 | 75 | cat > mnt/etc/hostname <<-EOS 76 | $id 77 | EOS 78 | 79 | cat > mnt/etc/hosts <<-EOS 80 | 127.0.0.1 localhost 81 | $network_container_ip $id 82 | EOS 83 | 84 | # Inherit nameserver(s) 85 | cp /etc/resolv.conf mnt/etc/ 86 | 87 | # Add vcap user if not already present 88 | $(which chroot) mnt env -i /bin/bash <<-EOS 89 | if ! id vcap > /dev/null 2>&1 90 | then 91 | useradd -mU -u $user_uid -s /bin/bash vcap 92 | fi 93 | EOS 94 | -------------------------------------------------------------------------------- /warden/src/iomux/pump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "pump.h" 9 | #include "util.h" 10 | 11 | #define PUMP_SIZE 4096 12 | 13 | typedef enum { 14 | STATE_OFFSET, 15 | STATE_DISCARD, 16 | STATE_PUMP 17 | } pump_state_t; 18 | 19 | void pump_setup(pump_t *pump, int src_fd, int dst_fd, uint32_t old_pos) { 20 | assert(NULL != pump); 21 | assert(src_fd >= 0); 22 | assert(dst_fd >= 0); 23 | 24 | memset(pump, 0, sizeof(*pump)); 25 | 26 | pump->state = STATE_OFFSET; 27 | 28 | pump->pos = 0; 29 | pump->old_pos = old_pos; 30 | 31 | pump->src_fd = src_fd; 32 | pump->dst_fd = dst_fd; 33 | } 34 | 35 | int pump_run(pump_t *pump) { 36 | uint8_t buf[PUMP_SIZE]; 37 | uint8_t w_hup = 0, r_hup = 0; 38 | uint8_t *bufp = NULL, *buf_end = NULL; 39 | ssize_t ncopy = 0, nread = 0, nwritten = 0; 40 | size_t ndiscard = 0; 41 | ptrdiff_t nremain = 0; 42 | 43 | assert(NULL != pump); 44 | 45 | nread = atomic_read(pump->src_fd, buf, sizeof(buf), &r_hup); 46 | 47 | bufp = buf; 48 | buf_end = buf + nread; 49 | 50 | while ((bufp != buf_end) && (!w_hup)) { 51 | nremain = buf_end - bufp; 52 | 53 | switch (pump->state) { 54 | case STATE_OFFSET: 55 | ncopy = MIN(nremain, sizeof(pump->pos) - pump->pos_off); 56 | 57 | memcpy(pump->pos_bytes + pump->pos_off, bufp, ncopy); 58 | 59 | pump->pos_off += ncopy; 60 | bufp += ncopy; 61 | 62 | if (pump->pos_off == sizeof(pump->pos)) { 63 | /* Have the offset, can proceed to data */ 64 | memcpy(&pump->pos, pump->pos_bytes, sizeof(pump->pos_bytes)); 65 | pump->pos = ntohl(pump->pos); 66 | pump->state = STATE_DISCARD; 67 | } 68 | break; 69 | 70 | case STATE_DISCARD: 71 | if (pump->pos >= pump->old_pos) { 72 | pump->state = STATE_PUMP; 73 | } else { 74 | ndiscard = MIN(pump->old_pos - pump->pos, nremain); 75 | bufp += ndiscard; 76 | pump->pos += ndiscard; 77 | } 78 | break; 79 | 80 | case STATE_PUMP: 81 | nwritten = atomic_write(pump->dst_fd, bufp, nremain, &w_hup); 82 | pump->pos += nwritten; 83 | bufp += nremain; 84 | break; 85 | 86 | default: 87 | /* NOTREACHED */ 88 | assert(0); 89 | break; 90 | } 91 | } 92 | 93 | return (w_hup || r_hup); 94 | } 95 | 96 | 97 | -------------------------------------------------------------------------------- /warden/Rakefile: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "rspec/core/rake_task" 4 | require "rspec/core/version" 5 | require "yaml" 6 | 7 | desc "Run all examples" 8 | RSpec::Core::RakeTask.new(:spec) do |t| 9 | t.rspec_opts = %w[--color --format documentation] 10 | end 11 | 12 | desc "Run (or re-run) steps to setup warden" 13 | task :setup, :config_path do |t, args| 14 | Rake::Task["setup:bin"].invoke 15 | Rake::Task["setup:rootfs"].invoke(args[:config_path]) 16 | end 17 | 18 | namespace :setup do 19 | desc "Compile and install binaries" 20 | task :bin do 21 | Dir.chdir("src") do 22 | sh "make clean all" 23 | end 24 | 25 | sh "cp src/wsh/wshd root/linux/skeleton/bin" 26 | sh "cp src/wsh/wsh root/linux/skeleton/bin" 27 | 28 | ["linux", "insecure"].each do |ct| 29 | sh "cp src/iomux/iomux-spawn root/#{ct}/skeleton/bin" 30 | sh "cp src/iomux/iomux-link root/#{ct}/skeleton/bin" 31 | end 32 | end 33 | 34 | desc "Setup root filesystem for Ubuntu containers" 35 | task :rootfs, :config_path do |t, args| 36 | rootfs_path = ENV["CONTAINER_ROOTFS_PATH"] 37 | 38 | if args[:config_path] 39 | config = YAML.load_file(args[:config_path]) 40 | 41 | if config["server"] && config["server"]["container_rootfs_path"] 42 | rootfs_path = config["server"]["container_rootfs_path"] 43 | end 44 | end 45 | 46 | unless rootfs_path 47 | STDERR.puts "Please specify path to config file, or CONTAINER_ROOTFS_PATH" 48 | Process.exit(1) 49 | end 50 | 51 | sh "mkdir -p #{File.dirname(rootfs_path)}" 52 | sh "sudo -E unshare -m root/linux/base/setup.sh #{rootfs_path}" 53 | end 54 | end 55 | 56 | namespace :warden do 57 | desc "Run Warden server" 58 | task :start, :config_path do |t, args| 59 | require "warden/server" 60 | 61 | if args[:config_path] 62 | config = YAML.load_file(args[:config_path]) 63 | end 64 | 65 | Warden::Server.setup(config || {}) 66 | Warden::Server.run! 67 | end 68 | end 69 | 70 | task :ensure_coding do 71 | patterns = [ 72 | /Rakefile$/, 73 | /\.rb$/, 74 | ] 75 | 76 | files = `git ls-files`.split.select do |file| 77 | patterns.any? { |e| e.match(file) } 78 | end 79 | 80 | header = "# coding: UTF-8\n\n" 81 | 82 | files.each do |file| 83 | content = File.read(file) 84 | 85 | unless content.start_with?(header) 86 | File.open(file, "w") do |f| 87 | f.write(header) 88 | f.write(content) 89 | end 90 | end 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /warden/lib/warden/network.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | module Warden 4 | 5 | module Network 6 | 7 | class Octets 8 | 9 | attr_reader :v 10 | 11 | include Comparable 12 | 13 | def <=>(other) 14 | v <=> Octets.new(other).v 15 | end 16 | 17 | def eql?(other) 18 | v == Octets.new(other).v 19 | end 20 | 21 | def hash 22 | v.hash 23 | end 24 | 25 | def initialize(v, *args) 26 | if args.empty? 27 | if v.kind_of?(Fixnum) 28 | @v = v 29 | elsif v.kind_of?(Octets) 30 | @v = v.v 31 | elsif v.kind_of?(String) 32 | if m = v.match(/[0-9a-f]{8}/) 33 | @v = v.to_i(16) 34 | else 35 | @v = to_integer(v.split(".")) 36 | end 37 | else 38 | raise "invalid value" 39 | end 40 | else 41 | @v = to_integer([v] + args) 42 | end 43 | 44 | raise "invalid address" if @v != (@v & (2 ** 32 - 1)) 45 | end 46 | 47 | def to_octets 48 | [(v >> 24) & 255, (v >> 16) & 255, (v >> 8) & 255, (v >> 0) & 255] 49 | end 50 | 51 | def to_hex 52 | to_octets.map { |e| "%02x" % e }.join 53 | end 54 | 55 | def to_human 56 | to_octets.join(".") 57 | end 58 | 59 | def to_json(*args) 60 | to_human.to_json(*args) 61 | end 62 | 63 | protected 64 | 65 | def to_integer(octets) 66 | octets.inject(0) { |a,e| a * 256 + e.to_i } 67 | end 68 | end 69 | 70 | class Netmask < Octets 71 | 72 | def initialize(*args) 73 | super 74 | raise "invalid netmask" unless valid? 75 | end 76 | 77 | def size 78 | (~v & (2 ** 32 - 1)) + 1 79 | end 80 | 81 | protected 82 | 83 | # Netmask size should be a power of 2 84 | def valid? 85 | ((size - 1) & size) == 0 86 | end 87 | end 88 | 89 | class Address < Octets 90 | 91 | def network(netmask) 92 | Address.new(v & netmask.v) 93 | end 94 | 95 | def +(other) 96 | case other 97 | when Fixnum 98 | Address.new(v + other) 99 | when Netmask 100 | Address.new(v + other.size) 101 | else 102 | raise "cannot add #{other.class}" 103 | end 104 | end 105 | end 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /em-warden-client/spec/support/mock_warden_server.rb: -------------------------------------------------------------------------------- 1 | require "eventmachine" 2 | require "tmpdir" 3 | require "warden/protocol/buffer" 4 | require "em/warden/client" 5 | 6 | class MockWardenServer 7 | class Error < StandardError 8 | end 9 | 10 | class ClientConnection < ::EM::Connection 11 | attr_reader :mock 12 | 13 | def initialize(mock = nil) 14 | super() 15 | 16 | @mock = mock 17 | @buffer = ::Warden::Protocol::Buffer.new 18 | end 19 | 20 | def post_init 21 | mock.connections << self 22 | end 23 | 24 | def handler 25 | mock.handler 26 | end 27 | 28 | def send_response(response) 29 | send_data ::Warden::Protocol::Buffer.response_to_wire(response) 30 | end 31 | 32 | def send_error(err) 33 | send_response ::Warden::Protocol::ErrorResponse.new(:message => err.message) 34 | end 35 | 36 | def receive_data(data = nil) 37 | @buffer << data if data 38 | @buffer.each_request do |request| 39 | begin 40 | response = handler.send(request.class.type_underscored, request) 41 | send_response(response) if response 42 | rescue MockWardenServer::Error => err 43 | send_error(err) 44 | end 45 | end 46 | end 47 | end 48 | 49 | attr_reader :handler 50 | attr_reader :connections 51 | attr_reader :socket_path 52 | 53 | def initialize(handler = nil) 54 | @handler = handler 55 | @connections = [] 56 | @server_sig = nil 57 | @tmpdir = Dir.mktmpdir 58 | @socket_path = File.join(@tmpdir, "warden.sock") 59 | end 60 | 61 | def start 62 | @server_sig = ::EM.start_unix_domain_server(@socket_path, ClientConnection, self) 63 | end 64 | 65 | def create_connection 66 | ::EM.connect_unix_domain(@socket_path, EM::Warden::Client::Connection) 67 | end 68 | 69 | def create_fiber_aware_client 70 | ::EM::Warden::FiberAwareClient.new(@socket_path) 71 | end 72 | 73 | def stop 74 | ::EM.stop_server(@server_sig) 75 | @server_sig = nil 76 | end 77 | end 78 | 79 | def create_mock_handler(request, response = nil) 80 | handler = mock() 81 | mock_cont = handler.should_receive(request.class.type_underscored) 82 | mock_cont = mock_cont.with(request) 83 | 84 | if response 85 | if response.kind_of?(StandardError) 86 | mock_cont.and_raise(response) 87 | else 88 | mock_cont.and_return(response) 89 | end 90 | else 91 | mock_cont.and_return(request.create_response) 92 | end 93 | 94 | handler 95 | end 96 | -------------------------------------------------------------------------------- /warden/src/iomux/child.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "child.h" 10 | #include "util.h" 11 | 12 | child_t *child_create(char **argv, size_t argv_size) { 13 | child_t *child = NULL; 14 | pid_t cpid; 15 | int ii = 0; 16 | char buf; 17 | uint8_t hup = 0; 18 | 19 | signal(SIGPIPE, SIG_IGN); 20 | 21 | assert(NULL != argv); 22 | assert(argv_size > 0); 23 | 24 | child = calloc(1, sizeof(*child)); 25 | assert(NULL != child); 26 | 27 | child->argv = calloc(argv_size + 1, sizeof(char *)); 28 | assert(NULL != child->argv); 29 | 30 | child->argv_size = argv_size; 31 | 32 | for (ii = 0; ii < argv_size; ++ii) { 33 | child->argv[ii] = strdup(argv[ii]); 34 | assert(NULL != child->argv[ii]); 35 | } 36 | 37 | if (-1 == pipe(child->stdout)) { 38 | perror("pipe()"); 39 | assert(0); 40 | } 41 | 42 | if (-1 == pipe(child->stderr)) { 43 | perror("pipe()"); 44 | assert(0); 45 | } 46 | 47 | if (-1 == pipe(child->barrier)) { 48 | perror("pipe()"); 49 | assert(0); 50 | } 51 | for (ii = 0; ii < 2; ++ii) { 52 | set_cloexec(child->barrier[ii]); 53 | } 54 | 55 | cpid = fork(); 56 | if (-1 == cpid) { 57 | perror("fork()"); 58 | assert(0); 59 | } 60 | 61 | if (0 == cpid) { 62 | dup2(child->stdout[1], STDOUT_FILENO); 63 | dup2(child->stderr[1], STDERR_FILENO); 64 | 65 | for (ii = 0; ii < 2; ++ii) { 66 | close(child->stdout[ii]); 67 | close(child->stderr[ii]); 68 | } 69 | 70 | /* In child, wait to be unblocked */ 71 | atomic_read(child->barrier[0], &buf, 1, &hup); 72 | 73 | if (hup) { 74 | exit(1); 75 | } 76 | 77 | execvp(argv[0], argv); 78 | 79 | /* NOTREACHED */ 80 | exit(0); 81 | } 82 | 83 | child->pid = cpid; 84 | 85 | close(child->stdout[1]); 86 | close(child->stderr[1]); 87 | 88 | return child; 89 | } 90 | 91 | void child_continue(child_t *child) { 92 | assert(NULL != child); 93 | 94 | atomic_write(child->barrier[1], "X", 1, NULL); 95 | } 96 | 97 | void child_free(child_t *child) { 98 | int ii = 0; 99 | 100 | assert(NULL != child); 101 | 102 | for (ii = 0; ii < 2; ++ii) { 103 | close(child->stdout[ii]); 104 | close(child->stderr[ii]); 105 | close(child->barrier[ii]); 106 | } 107 | 108 | for (ii = 0; ii < child->argv_size; ++ii) { 109 | free(child->argv[ii]); 110 | } 111 | free(child->argv); 112 | 113 | free(child); 114 | } 115 | -------------------------------------------------------------------------------- /warden/spec/health_check_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "net/http" 5 | require "uri" 6 | 7 | require "warden/server" 8 | require "warden/client" 9 | require "warden/network" 10 | require "warden/util" 11 | 12 | require "warden/container/linux" 13 | 14 | def next_class_c 15 | $class_c ||= Warden::Network::Address.new("172.16.0.0") 16 | 17 | rv = $class_c 18 | $class_c = $class_c + 256 19 | rv 20 | end 21 | 22 | describe "health check" do 23 | let(:work_path) { File.join(Dir.tmpdir, "warden", "spec") } 24 | let(:unix_domain_path) { File.join(work_path, "warden.sock") } 25 | let(:container_klass) { "Warden::Container::Linux" } 26 | let(:container_rootfs_path) { File.join(work_path, "..", "rootfs") } 27 | let(:container_depot_path) { File.join(work_path, "containers") } 28 | 29 | before do 30 | FileUtils.mkdir_p(work_path) 31 | 32 | unless File.directory?(container_rootfs_path) 33 | raise "%s does not exist" % container_rootfs_path 34 | end 35 | 36 | FileUtils.mkdir_p(container_depot_path) 37 | end 38 | 39 | before do 40 | start_warden 41 | end 42 | 43 | def start_warden 44 | FileUtils.rm_f(unix_domain_path) 45 | 46 | # Grab new network for every test to avoid resource contention 47 | @start_address = next_class_c.to_human 48 | 49 | @pid = fork do 50 | Process.setsid 51 | Signal.trap("TERM") { exit } 52 | 53 | config = { 54 | "server" => { 55 | "unix_domain_path" => unix_domain_path, 56 | "container_rootfs_path" => container_rootfs_path, 57 | "container_depot_path" => container_depot_path, 58 | "container_grace_time" => 5 59 | }, 60 | "health_server" => { 61 | "port" => 2345 62 | }, 63 | "logging" => { 64 | "level" => "debug", 65 | "file" => File.join(work_path, "warden.log") 66 | } 67 | } 68 | Warden::Server.setup(config) 69 | Warden::Server.run! 70 | end 71 | 72 | # Wait for the socket to come up 73 | until File.exist?(unix_domain_path) 74 | if Process.waitpid(@pid, Process::WNOHANG) 75 | STDERR.puts "Warden process exited before socket was up; aborting spec suite." 76 | exit 1 77 | end 78 | 79 | sleep 0.01 80 | end 81 | end 82 | 83 | after do 84 | `kill -9 -#{@pid}` 85 | Process.waitpid(@pid) 86 | end 87 | 88 | it "should respond with HTTP 200" do 89 | uri = URI.parse("http://127.0.0.1:2345/") 90 | response = Net::HTTP.get_response(uri) 91 | response.code.should == "200" 92 | response.body.should be_empty 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /warden-protocol/spec/limit_disk_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/limit_disk" 5 | 6 | shared_examples "disk limiting" do 7 | field :block_limit do 8 | it_should_be_optional 9 | it_should_be_typed_as_uint32 10 | end 11 | 12 | field :block do 13 | it_should_be_optional 14 | it_should_be_typed_as_uint64 15 | end 16 | 17 | field :block_soft do 18 | it_should_be_optional 19 | it_should_be_typed_as_uint64 20 | end 21 | 22 | field :block_hard do 23 | it_should_be_optional 24 | it_should_be_typed_as_uint64 25 | end 26 | 27 | field :inode_limit do 28 | it_should_be_optional 29 | it_should_be_typed_as_uint32 30 | end 31 | 32 | field :inode do 33 | it_should_be_optional 34 | it_should_be_typed_as_uint64 35 | end 36 | 37 | field :inode_soft do 38 | it_should_be_optional 39 | it_should_be_typed_as_uint64 40 | end 41 | 42 | field :inode_hard do 43 | it_should_be_optional 44 | it_should_be_typed_as_uint64 45 | end 46 | 47 | field :byte_limit do 48 | it_should_be_optional 49 | it_should_be_typed_as_uint32 50 | end 51 | 52 | field :byte do 53 | it_should_be_optional 54 | it_should_be_typed_as_uint64 55 | end 56 | 57 | field :byte_soft do 58 | it_should_be_optional 59 | it_should_be_typed_as_uint64 60 | end 61 | 62 | field :byte_hard do 63 | it_should_be_optional 64 | it_should_be_typed_as_uint64 65 | end 66 | end 67 | 68 | describe Warden::Protocol::LimitDiskRequest do 69 | subject(:request) do 70 | described_class.new(:handle => "handle") 71 | end 72 | 73 | it_should_behave_like "wrappable request" 74 | 75 | its("class.type_camelized") { should == "LimitDisk" } 76 | its("class.type_underscored") { should == "limit_disk" } 77 | 78 | field :handle do 79 | it_should_be_required 80 | it_should_be_typed_as_string 81 | end 82 | 83 | it_should_behave_like "disk limiting" 84 | 85 | it "should respond to #create_response" do 86 | request.create_response.should be_a(Warden::Protocol::LimitDiskResponse) 87 | end 88 | 89 | it_should_behave_like "documented request" 90 | end 91 | 92 | describe Warden::Protocol::LimitDiskResponse do 93 | subject(:response) do 94 | described_class.new 95 | end 96 | 97 | it_should_behave_like "wrappable response" 98 | 99 | its("class.type_camelized") { should == "LimitDisk" } 100 | its("class.type_underscored") { should == "limit_disk" } 101 | 102 | it { should be_ok } 103 | it { should_not be_error } 104 | 105 | it_should_behave_like "disk limiting" 106 | end 107 | -------------------------------------------------------------------------------- /warden/src/wsh/util.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "util.h" 16 | 17 | void fcntl_set_cloexec(int fd, int on) { 18 | int rv; 19 | int fl; 20 | 21 | rv = fcntl(fd, F_GETFD); 22 | if (rv == -1) { 23 | perror("fcntl"); 24 | abort(); 25 | } 26 | 27 | fl = rv; 28 | if (on) { 29 | fl |= FD_CLOEXEC; 30 | } else { 31 | fl &= ~FD_CLOEXEC; 32 | } 33 | 34 | rv = fcntl(fd, F_SETFD, rv | FD_CLOEXEC); 35 | if (rv == -1) { 36 | perror("fcntl"); 37 | abort(); 38 | } 39 | } 40 | 41 | void fcntl_set_nonblock(int fd, int on) { 42 | int rv; 43 | int fl; 44 | 45 | rv = fcntl(fd, F_GETFL); 46 | if (rv == -1) { 47 | perror("fcntl"); 48 | abort(); 49 | } 50 | 51 | fl = rv; 52 | if (on) { 53 | fl |= O_NONBLOCK; 54 | } else { 55 | fl &= ~O_NONBLOCK; 56 | } 57 | 58 | rv = fcntl(fd, F_SETFL, fl); 59 | if (rv == -1) { 60 | perror("fcntl"); 61 | abort(); 62 | } 63 | } 64 | 65 | int run(const char *p1, const char *p2) { 66 | char path[MAXPATHLEN]; 67 | int rv; 68 | char *argv[2]; 69 | int status; 70 | 71 | rv = snprintf(path, sizeof(path), "%s/%s", p1, p2); 72 | assert(rv < sizeof(path)); 73 | 74 | argv[0] = path; 75 | argv[1] = NULL; 76 | 77 | rv = fork(); 78 | if (rv == -1) { 79 | perror("fork"); 80 | abort(); 81 | } 82 | 83 | if (rv == 0) { 84 | execvp(argv[0], argv); 85 | perror("execvp"); 86 | abort(); 87 | } else { 88 | rv = waitpid(rv, &status, 0); 89 | if (rv == -1) { 90 | perror("waitpid"); 91 | abort(); 92 | } 93 | 94 | if (WEXITSTATUS(status) != 0) { 95 | fprintf(stderr, "Process for \"%s\" exited with %d\n", path, WEXITSTATUS(status)); 96 | return -1; 97 | } 98 | } 99 | 100 | return 0; 101 | } 102 | 103 | void setproctitle(char **argv, const char *title) { 104 | char *last; 105 | int i; 106 | size_t len; 107 | char *p; 108 | size_t n; 109 | 110 | last = argv[0]; 111 | i = 0; 112 | 113 | while (last == argv[i]) { 114 | last += strlen(argv[i++]) + 1; 115 | } 116 | 117 | len = last - argv[0]; 118 | p = argv[0]; 119 | n = strlen(title); 120 | assert(len > n); 121 | 122 | /* Assign argv termination sentinel */ 123 | argv[1] = NULL; 124 | 125 | /* Copy title */ 126 | strncpy(p, title, n); 127 | len -= n; 128 | p += n; 129 | 130 | /* Set remaining bytes to \0 */ 131 | memset(p, '\0', len); 132 | } 133 | -------------------------------------------------------------------------------- /warden-protocol/lib/warden/protocol/info.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/protocol/base" 4 | 5 | module Warden 6 | module Protocol 7 | class InfoRequest < BaseRequest 8 | required :handle, :string, 1 9 | 10 | def self.description 11 | "Show metadata for a container." 12 | end 13 | end 14 | 15 | class InfoResponse < BaseResponse 16 | class MemoryStat < BaseMessage 17 | optional :cache, :uint64, 1 18 | optional :rss, :uint64, 2 19 | optional :mapped_file, :uint64, 3 20 | optional :pgpgin, :uint64, 4 21 | optional :pgpgout, :uint64, 5 22 | optional :swap, :uint64, 6 23 | optional :pgfault, :uint64, 7 24 | optional :pgmajfault, :uint64, 8 25 | optional :inactive_anon, :uint64, 9 26 | optional :active_anon, :uint64, 10 27 | optional :inactive_file, :uint64, 11 28 | optional :active_file, :uint64, 12 29 | optional :unevictable, :uint64, 13 30 | optional :hierarchical_memory_limit, :uint64, 14 31 | optional :hierarchical_memsw_limit, :uint64, 15 32 | optional :total_cache, :uint64, 16 33 | optional :total_rss, :uint64, 17 34 | optional :total_mapped_file, :uint64, 18 35 | optional :total_pgpgin, :uint64, 19 36 | optional :total_pgpgout, :uint64, 20 37 | optional :total_swap, :uint64, 21 38 | optional :total_pgfault, :uint64, 22 39 | optional :total_pgmajfault, :uint64, 23 40 | optional :total_inactive_anon, :uint64, 24 41 | optional :total_active_anon, :uint64, 25 42 | optional :total_inactive_file, :uint64, 26 43 | optional :total_active_file, :uint64, 27 44 | optional :total_unevictable, :uint64, 28 45 | end 46 | 47 | class CpuStat < BaseMessage 48 | optional :usage, :uint64, 1 # Nanoseconds 49 | optional :user, :uint64, 2 # Hz (USER_HZ specifically) 50 | optional :system, :uint64, 3 # Hz 51 | end 52 | 53 | class DiskStat < BaseMessage 54 | optional :bytes_used, :uint64, 1 55 | optional :inodes_used, :uint64, 2 56 | end 57 | 58 | class BandwidthStat < BaseMessage 59 | optional :in_rate, :uint64, 1 60 | optional :in_burst, :uint64, 2 61 | optional :out_rate, :uint64, 3 62 | optional :out_burst, :uint64, 4 63 | end 64 | 65 | optional :state, :string, 10 66 | 67 | repeated :events, :string, 20 68 | 69 | optional :host_ip, :string, 30 70 | optional :container_ip, :string, 31 71 | optional :container_path, :string, 32 72 | 73 | optional :memory_stat, MemoryStat, 40 74 | optional :cpu_stat, CpuStat, 41 75 | optional :disk_stat, DiskStat, 42 76 | optional :bandwidth_stat, BandwidthStat, 43 77 | 78 | repeated :job_ids, :uint64, 44 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /warden/src/oom/oom.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | int main(int argc, char **argv) { 14 | int event_fd = -1; 15 | char oom_control_path[PATH_MAX]; 16 | size_t oom_control_path_len; 17 | int oom_control_fd = -1; 18 | char event_control_path[PATH_MAX]; 19 | size_t event_control_path_len; 20 | int event_control_fd = -1; 21 | char line[LINE_MAX]; 22 | size_t line_len; 23 | int rv; 24 | uint64_t result; 25 | 26 | if (argc != 2) { 27 | fprintf(stderr, "Usage: %s \n", argv[0]); 28 | exit(1); 29 | } 30 | 31 | /* Open event fd */ 32 | event_fd = eventfd(0, 0); 33 | if (event_fd == -1) { 34 | perror("eventfd"); 35 | goto err; 36 | } 37 | 38 | /* Open oom control file */ 39 | oom_control_path_len = snprintf(oom_control_path, sizeof(oom_control_path), "%s/memory.oom_control", argv[1]); 40 | assert(oom_control_path_len < sizeof(oom_control_path)); 41 | 42 | oom_control_fd = open(oom_control_path, O_RDONLY); 43 | if (oom_control_fd == -1) { 44 | perror("open"); 45 | goto err; 46 | } 47 | 48 | /* Open event control file */ 49 | event_control_path_len = snprintf(event_control_path, sizeof(event_control_path), "%s/cgroup.event_control", argv[1]); 50 | assert(event_control_path_len < sizeof(event_control_path)); 51 | 52 | event_control_fd = open(event_control_path, O_WRONLY); 53 | if (event_control_fd == -1) { 54 | perror("open"); 55 | goto err; 56 | } 57 | 58 | /* Write event fd and oom control fd to event control fd */ 59 | line_len = snprintf(line, sizeof(line), "%d %d\n", event_fd, oom_control_fd); 60 | assert(line_len < sizeof(line)); 61 | 62 | rv = write(event_control_fd, line, line_len); 63 | if (rv == -1) { 64 | perror("write"); 65 | goto err; 66 | } 67 | 68 | /* Read oom */ 69 | do { 70 | rv = read(event_fd, &result, sizeof(result)); 71 | } while (rv == -1 && errno == EINTR); 72 | 73 | if (rv == -1) { 74 | perror("read"); 75 | goto err; 76 | } 77 | 78 | assert(rv == sizeof(result)); 79 | 80 | rv = access(event_control_path, W_OK); 81 | if (rv == -1 && errno == ENOENT) { 82 | /* The cgroup appears to be removed */ 83 | perror("access"); 84 | goto err; 85 | } 86 | 87 | if (rv == -1) { 88 | perror("access"); 89 | goto err; 90 | } 91 | 92 | fprintf(stdout, "oom"); 93 | 94 | rv = 0; 95 | goto out; 96 | 97 | err: 98 | rv = 1; 99 | goto out; 100 | 101 | out: 102 | if (event_fd >= 0) { 103 | close(event_fd); 104 | } 105 | 106 | if (oom_control_fd >= 0) { 107 | close(oom_control_fd); 108 | } 109 | 110 | if (event_control_fd >= 0) { 111 | close(event_control_fd); 112 | } 113 | 114 | return rv; 115 | } 116 | -------------------------------------------------------------------------------- /warden-client/lib/warden/client.rb: -------------------------------------------------------------------------------- 1 | require "socket" 2 | require "warden/protocol" 3 | require "warden/client/v1" 4 | 5 | module Warden 6 | 7 | class Client 8 | 9 | class Error < StandardError; end 10 | class ServerError < Error; end 11 | 12 | attr_reader :path 13 | 14 | def initialize(path) 15 | @path = path 16 | @v1mode = false 17 | end 18 | 19 | def connected? 20 | !@sock.nil? 21 | end 22 | 23 | def connect 24 | raise "already connected" if connected? 25 | @sock = ::UNIXSocket.new(path) 26 | end 27 | 28 | def disconnect 29 | raise "not connected" unless connected? 30 | @sock.close rescue nil 31 | @sock = nil 32 | end 33 | 34 | def reconnect 35 | disconnect if connected? 36 | connect 37 | end 38 | 39 | def io 40 | rv = yield 41 | if rv.nil? 42 | disconnect 43 | raise ::EOFError 44 | end 45 | 46 | rv 47 | end 48 | 49 | def read 50 | length = io { @sock.gets } 51 | data = io { @sock.read(length.to_i) } 52 | 53 | # Discard \r\n 54 | io { @sock.read(2) } 55 | 56 | wrapped_response = Warden::Protocol::WrappedResponse.decode(data) 57 | response = wrapped_response.response 58 | 59 | # Raise error replies 60 | if response.is_a?(Warden::Protocol::ErrorResponse) 61 | raise Warden::Client::ServerError.new(response.message) 62 | end 63 | 64 | if @v1mode 65 | response = V1.response_to_v1(response) 66 | end 67 | 68 | response 69 | end 70 | 71 | def write(request) 72 | if request.kind_of?(Array) 73 | @v1mode = true 74 | request = V1.request_from_v1(request.dup) 75 | end 76 | 77 | unless request.kind_of?(Warden::Protocol::BaseRequest) 78 | raise "Expected #kind_of? Warden::Protocol::BaseRequest" 79 | end 80 | 81 | data = request.wrap.encode.to_s 82 | @sock.write data.length.to_s + "\r\n" 83 | @sock.write data + "\r\n" 84 | end 85 | 86 | def stream(request, &blk) 87 | unless request.is_a?(Warden::Protocol::StreamRequest) 88 | msg = "Expected argument to be of type:" 89 | msg << "'#{Warden::Protocol::StreamRequest}'" 90 | msg << ", but received: '#{request.class}'." 91 | raise ArgumentError, msg 92 | end 93 | 94 | response = call(request) 95 | while response.exit_status.nil? 96 | blk.call(response) 97 | response = read 98 | end 99 | 100 | response 101 | end 102 | 103 | def call(request) 104 | write(request) 105 | read 106 | end 107 | 108 | def method_missing(sym, *args, &blk) 109 | klass_name = sym.to_s.gsub(/(^|_)([a-z])/) { $2.upcase } 110 | klass_name += "Request" 111 | klass = Warden::Protocol.const_get(klass_name) 112 | 113 | call(klass.new(*args)) 114 | end 115 | end 116 | end 117 | -------------------------------------------------------------------------------- /warden/lib/warden/repl_v2_runner.rb: -------------------------------------------------------------------------------- 1 | require "optparse" 2 | require "warden/repl_v2" 3 | require "warden/commands_manager" 4 | 5 | module Warden 6 | class ReplRunner 7 | 8 | # Parses command-line arguments, runs Repl and handles the output and exit 9 | # status of commands. 10 | # 11 | # Parameters: 12 | # - args [Array of Strings]: 13 | # Command-line arguments to be parsed. 14 | def self.run(args = []) 15 | options = {} 16 | 17 | opt_parser = OptionParser.new do |op| 18 | op.banner = <<-EOT 19 | Usage: warden [options] -- [commands] 20 | Runs an interactive REPL by default. 21 | 22 | [options] can be one of the following: 23 | 24 | EOT 25 | op.on("--socket socket", "Warden socket path.") do |socket_path| 26 | options[:socket_path] = socket_path 27 | end 28 | 29 | op.on("--trace", "Writes each command preceded by a '+' to stdout before" \ 30 | + " executing.") do |trace| 31 | options[:trace] = true 32 | end 33 | 34 | op.on("--exit_on_error", 35 | "Exit after the first unsuccessful command.") do 36 | options[:exit_on_error] = true 37 | end 38 | 39 | op.on_tail("--help", "Show help.") do 40 | puts op 41 | puts 42 | puts "[commands] can be one of the following separated by a newline." 43 | puts Warden::Repl.new.describe_commands(op.summary_width - 3) 44 | puts 45 | exit 46 | end 47 | end 48 | 49 | global_args = [] 50 | command = [] 51 | delimiter_found = false 52 | args.each do |element| 53 | element = element.dup 54 | if element == "--" 55 | raise "Delimiter: '--' specified multiple times." if delimiter_found 56 | delimiter_found = true 57 | else 58 | if delimiter_found 59 | command << element 60 | else 61 | global_args << element 62 | end 63 | end 64 | end 65 | 66 | opt_parser.parse(global_args) 67 | repl = Warden::Repl.new(options) 68 | 69 | if command.empty? 70 | run_interactively(repl) 71 | else 72 | run_non_interactively(repl, command) 73 | end 74 | end 75 | 76 | private 77 | 78 | def self.run_interactively(repl) 79 | trap('INT') do 80 | STDERR.write("\n\nExiting...\n") 81 | exit 82 | end 83 | 84 | exit(repl.start) 85 | end 86 | 87 | def self.run_non_interactively(repl, command) 88 | command_info = nil 89 | 90 | begin 91 | command_info = repl.process_command(command) 92 | rescue Warden::CommandsManager::CommandError => ce 93 | STDERR.write("#{ce}\n") 94 | ce.backtrace.each { |err| STDERR.write("#{err}\n") } 95 | end 96 | 97 | exit_status = 0 98 | if command_info 99 | STDOUT.write(command_info[:result]) 100 | exit_status = command_info[:exit_status] if command_info[:exit_status] 101 | end 102 | 103 | exit(exit_status) 104 | end 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /warden/root/linux/base/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | packages="openssh-server,rsync" 9 | suite="lucid" 10 | mirror=$(grep "^deb" /etc/apt/sources.list | head -n1 | cut -d" " -f2) 11 | 12 | # Fallback to default Ubuntu mirror when mirror could not be determined 13 | if [ -z "$mirror" ] 14 | then 15 | mirror="http://archive.ubuntu.com/ubuntu/" 16 | fi 17 | 18 | function debootstrap() { 19 | if [ -d $target ] 20 | then 21 | read -p "Target directory already exists. Erase it? " 22 | if [[ $REPLY =~ ^[Yy].*$ ]] 23 | then 24 | rm -rf $target 25 | else 26 | echo "Aborting..." 27 | exit 1 28 | fi 29 | fi 30 | 31 | # -v is too new, revert to old trick 32 | # ${VAR+X} will be 33 | # X when VAR is unset 34 | # $VAR otherwise 35 | # Only do this heuristic when http_proxy is unset 36 | # You can opt out of this by setting http_proxy to nil 37 | if [ -z ${http_proxy+X} ] 38 | then 39 | eval $(apt-config shell http_proxy Acquire::http::Proxy) 40 | export http_proxy 41 | fi 42 | 43 | $(which debootstrap) --verbose --include $packages $suite $target $mirror 44 | } 45 | 46 | function write() { 47 | [ -z "$1" ] && return 1 48 | 49 | mkdir -p $target/$(dirname $1) 50 | cat > $target/$1 51 | } 52 | 53 | function chroot() { 54 | $(which chroot) $target env -i $(cat $target/etc/environment) /bin/bash 55 | } 56 | 57 | if [ $EUID -ne 0 ] 58 | then 59 | echo "Sorry, you need to be root." 60 | exit 1 61 | fi 62 | 63 | if [ "$#" -ne 1 ] 64 | then 65 | echo "Usage: setup.sh [base_dir]" 66 | exit 1 67 | fi 68 | 69 | dirname=$(readlink -nf $(dirname $1)) 70 | if [ ! -d $dirname ] 71 | then 72 | echo "Looks like $dirname doesn't exist or isn't a directory" 73 | exit 1 74 | fi 75 | 76 | target=$1 77 | 78 | debootstrap 79 | 80 | write "etc/apt/sources.list" <<-EOS 81 | deb $mirror $suite main universe 82 | deb $mirror $suite-updates main universe 83 | EOS 84 | 85 | # Disable interactive dpkg 86 | chroot <<-EOS 87 | echo debconf debconf/frontend select noninteractive | 88 | debconf-set-selections 89 | EOS 90 | 91 | # Generate and setup default locale (en_US.UTF-8) 92 | chroot <<-EOS 93 | locale-gen en_US.UTF-8 94 | update-locale LANG="en_US.UTF-8" 95 | EOS 96 | 97 | # Update packages 98 | chroot <<-EOS 99 | apt-get update 100 | EOS 101 | 102 | # Disable initctl so that apt cannot start any daemons 103 | mv $target/sbin/initctl $target/sbin/initctl.real 104 | ln -s /bin/true $target/sbin/initctl 105 | trap "mv $target/sbin/initctl.real $target/sbin/initctl" EXIT 106 | 107 | # Upgrade upstart 108 | chroot <<-EOS 109 | apt-get install -y upstart 110 | EOS 111 | 112 | # If upstart was upgraded, make sure to disable it again 113 | if [ ! -h $target/sbin/initctl ] 114 | then 115 | mv $target/sbin/initctl $target/sbin/initctl.real 116 | ln -s /bin/true $target/sbin/initctl 117 | fi 118 | 119 | # Upgrade everything 120 | chroot <<-EOS 121 | apt-get upgrade -y 122 | EOS 123 | 124 | # Install packages 125 | chroot <<-EOS 126 | apt-get install -y build-essential 127 | EOS 128 | -------------------------------------------------------------------------------- /warden/spec/support/examples/snapshotting.rb: -------------------------------------------------------------------------------- 1 | require "thread" 2 | 3 | shared_examples "snapshotting_common" do 4 | it "should snapshot a container after creation" do 5 | handle = client.create.handle 6 | snapshot_path = File.join(container_depot_path, handle, "snapshot.json") 7 | File.exist?(snapshot_path).should be_true 8 | snapshot = JSON.parse(File.read(snapshot_path)) 9 | snapshot["state"].should == "active" 10 | end 11 | 12 | it "should snapshot a container after it is stopped" do 13 | handle = client.create.handle 14 | snapshot_path = File.join(container_depot_path, handle, "snapshot.json") 15 | File.exist?(snapshot_path).should be_true 16 | snapshot = JSON.parse(File.read(snapshot_path)) 17 | snapshot["state"].should == "active" 18 | 19 | client.stop(:handle => handle) 20 | File.exist?(snapshot_path).should be_true 21 | snapshot = JSON.parse(File.read(snapshot_path)) 22 | snapshot["state"].should == "stopped" 23 | end 24 | 25 | it "should snapshot a container when a spawned process exits" do 26 | handle = client.create.handle 27 | 28 | client.spawn(:handle => handle, :script => "echo abc") 29 | sleep 0.1 30 | 31 | snapshot_path = File.join(container_depot_path, handle, "snapshot.json") 32 | File.exist?(snapshot_path).should be_true 33 | snapshot = JSON.parse(File.read(snapshot_path)) 34 | snapshot["jobs"].keys.size.should == 1 35 | end 36 | 37 | it "should create empty snapshots for alive processes" do 38 | handle = client.create.handle 39 | 40 | client.spawn(:handle => handle, :script => "sleep 2; echo abc") 41 | sleep 0.1 42 | 43 | snapshot_path = File.join(container_depot_path, handle, "snapshot.json") 44 | File.exist?(snapshot_path).should be_true 45 | snapshot = JSON.parse(File.read(snapshot_path)) 46 | snapshot["jobs"].keys.size.should == 1 47 | 48 | job_snapshot = snapshot["jobs"].values.first 49 | job_snapshot.should be_an_instance_of Hash 50 | job_snapshot["stdout"].should == "" 51 | job_snapshot["stderr"].should == "" 52 | end 53 | end 54 | 55 | shared_examples "snapshotting_net_in" do 56 | it "should snapshot a container after net_in request" do 57 | handle = client.create.handle 58 | client.net_in(:handle => handle) 59 | 60 | snapshot_path = File.join(container_depot_path, handle, "snapshot.json") 61 | File.exist?(snapshot_path).should be_true 62 | snapshot = JSON.parse(File.read(snapshot_path)) 63 | snapshot["resources"]["ports"].size.should == 1 64 | end 65 | 66 | it "should create empty snapshot for alive processes after net_in request" do 67 | handle = client.create.handle 68 | client.spawn(:handle => handle, :script => "sleep 2; echo abc") 69 | client.net_in(:handle => handle) 70 | 71 | snapshot_path = File.join(container_depot_path, handle, "snapshot.json") 72 | File.exist?(snapshot_path).should be_true 73 | snapshot = JSON.parse(File.read(snapshot_path)) 74 | snapshot["jobs"].keys.size.should == 1 75 | 76 | job_snapshot = snapshot["jobs"].values.first 77 | job_snapshot.should be_an_instance_of Hash 78 | job_snapshot["stdout"].should == "" 79 | job_snapshot["stderr"].should == "" 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /warden-protocol/spec/info_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "warden/protocol/info" 5 | 6 | describe Warden::Protocol::InfoRequest do 7 | subject(:request) do 8 | described_class.new(:handle => "handle") 9 | end 10 | 11 | it_should_behave_like "wrappable request" 12 | 13 | its("class.type_camelized") { should == "Info" } 14 | its("class.type_underscored") { should == "info" } 15 | 16 | field :handle do 17 | it_should_be_required 18 | it_should_be_typed_as_string 19 | end 20 | 21 | it "should respond to #create_response" do 22 | request.create_response.should be_a(Warden::Protocol::InfoResponse) 23 | end 24 | 25 | it_should_behave_like "documented request" 26 | end 27 | 28 | describe Warden::Protocol::InfoResponse::CpuStat do 29 | field :usage do 30 | it_should_be_optional 31 | it_should_be_typed_as_uint64 32 | end 33 | 34 | field :user do 35 | it_should_be_optional 36 | it_should_be_typed_as_uint64 37 | end 38 | 39 | field :system do 40 | it_should_be_optional 41 | it_should_be_typed_as_uint64 42 | end 43 | end 44 | 45 | describe Warden::Protocol::InfoResponse::DiskStat do 46 | field :bytes_used do 47 | it_should_be_optional 48 | it_should_be_typed_as_uint64 49 | end 50 | 51 | field :inodes_used do 52 | it_should_be_optional 53 | it_should_be_typed_as_uint64 54 | end 55 | end 56 | 57 | describe Warden::Protocol::InfoResponse do 58 | subject(:response) do 59 | described_class.new 60 | end 61 | 62 | it_should_behave_like "wrappable response" 63 | 64 | its("class.type_camelized") { should == "Info" } 65 | its("class.type_underscored") { should == "info" } 66 | 67 | it { should be_ok } 68 | it { should_not be_error } 69 | 70 | field :state do 71 | it_should_be_optional 72 | it_should_be_typed_as_string 73 | end 74 | 75 | field :events do 76 | it_should_be_optional 77 | 78 | it "should allow one or more events" do 79 | subject.events = ["a", "b"] 80 | subject.should be_valid 81 | end 82 | end 83 | 84 | field :host_ip do 85 | it_should_be_optional 86 | it_should_be_typed_as_string 87 | end 88 | 89 | field :container_ip do 90 | it_should_be_optional 91 | it_should_be_typed_as_string 92 | end 93 | 94 | field :container_path do 95 | it_should_be_optional 96 | it_should_be_typed_as_string 97 | end 98 | 99 | field :cpu_stat do 100 | it_should_be_optional 101 | 102 | it "should allow instances of CpuStat" do 103 | subject.cpu_stat = Warden::Protocol::InfoResponse::CpuStat.new 104 | subject.should be_valid 105 | end 106 | end 107 | 108 | field :disk_stat do 109 | it_should_be_optional 110 | 111 | it "should allow instances of DiskStat" do 112 | subject.disk_stat = Warden::Protocol::InfoResponse::DiskStat.new 113 | subject.should be_valid 114 | end 115 | end 116 | 117 | field :job_ids do 118 | it_should_be_optional 119 | 120 | it "should allow one or more job ids" do 121 | subject.job_ids = [1, 2] 122 | subject.should be_valid 123 | end 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /warden/lib/warden/container/features/mem_limit.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/container/spawn" 4 | require "warden/errors" 5 | require "warden/util" 6 | 7 | module Warden 8 | 9 | module Container 10 | 11 | module Features 12 | 13 | module MemLimit 14 | 15 | class OomNotifier 16 | 17 | include Spawn 18 | 19 | attr_reader :container 20 | 21 | def initialize(container) 22 | @container = container 23 | 24 | oom_notifier_path = Warden::Util.path("src/oom/oom") 25 | @child = DeferredChild.new(oom_notifier_path, container.cgroup_path(:memory)) 26 | @child.logger = logger 27 | @child.run 28 | @child_exited = false 29 | 30 | # Zero exit status means a process OOMed, non-zero means an error occurred 31 | @child.callback do 32 | @child_exited = true 33 | 34 | if @child.success? 35 | Fiber.new do 36 | container.oomed 37 | end.resume 38 | end 39 | end 40 | 41 | # Don't care about errback, nothing we can do 42 | end 43 | 44 | def kill 45 | return if @child_exited 46 | 47 | @child.callback do 48 | @child_exited = true 49 | end 50 | 51 | logger.debug("Killing oom-notifier process") 52 | 53 | # em-posix-spawn reaps the exit status for us, so no waitpid needed 54 | # here. 55 | @child.kill 56 | end 57 | end 58 | 59 | def oomed 60 | logger.warn("OOM happened for #{handle}") 61 | 62 | events << 'oom' 63 | if state == State::Active 64 | dispatch(Protocol::StopRequest.new) 65 | end 66 | end 67 | 68 | def do_limit_memory(request, response) 69 | if request.limit_in_bytes 70 | begin 71 | 72 | # Need to set up the oom notifier before we set the memory limit 73 | # to avoid a race between when the limit is set and when the oom 74 | # notifier is registered. 75 | unless @oom_notifier 76 | @oom_notifier = OomNotifier.new(self) 77 | on(:after_stop) do 78 | if @oom_notifier 79 | @oom_notifier.kill 80 | @oom_notifier = nil 81 | end 82 | end 83 | end 84 | 85 | ["memory.limit_in_bytes", "memory.memsw.limit_in_bytes"].each do |path| 86 | File.open(File.join(cgroup_path(:memory), path), 'w') do |f| 87 | f.write(request.limit_in_bytes.to_s) 88 | end 89 | end 90 | 91 | rescue => e 92 | raise WardenError.new("Failed setting memory limit: #{e}") 93 | end 94 | end 95 | 96 | limit_in_bytes = File.read(File.join(cgroup_path(:memory), "memory.limit_in_bytes")) 97 | response.limit_in_bytes = limit_in_bytes.to_i 98 | 99 | nil 100 | end 101 | end 102 | end 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /warden/src/wsh/pump.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "pump.h" 12 | #include "util.h" 13 | 14 | void pump_init(pump_t *p) { 15 | p->nfd = 0; 16 | 17 | FD_ZERO(&p->rfds); 18 | FD_ZERO(&p->wfds); 19 | FD_ZERO(&p->efds); 20 | } 21 | 22 | void pump_pair_init(pump_pair_t *pp, pump_t *p, int rfd, int wfd) { 23 | pp->p = p; 24 | pp->rfd = rfd; 25 | pp->wfd = wfd; 26 | 27 | /* The read side must be non-blocking */ 28 | fcntl_set_nonblock(rfd, 1); 29 | 30 | /* The write side must be blocking */ 31 | fcntl_set_nonblock(wfd, 0); 32 | } 33 | 34 | void pump_pair_close(pump_pair_t *pp) { 35 | close(pp->rfd); 36 | pp->rfd = -1; 37 | close(pp->wfd); 38 | pp->wfd = -1; 39 | } 40 | 41 | int pump_add_fd(pump_t *p, int fd, int mode) { 42 | if (fd < 0) { 43 | return 0; 44 | } 45 | 46 | if (fd > p->nfd) { 47 | p->nfd = fd; 48 | } 49 | 50 | if (mode & PUMP_READ) { 51 | FD_SET(fd, &p->rfds); 52 | } 53 | 54 | if (mode & PUMP_WRITE) { 55 | FD_SET(fd, &p->wfds); 56 | } 57 | 58 | if (mode & PUMP_EXCEPT) { 59 | FD_SET(fd, &p->efds); 60 | } 61 | 62 | return 1; 63 | } 64 | 65 | int pump_add_pair(pump_t *p, pump_pair_t *pp) { 66 | int j = 0; 67 | 68 | j += pump_add_fd(p, pp->rfd, PUMP_READ | PUMP_EXCEPT); 69 | j += pump_add_fd(p, pp->wfd, PUMP_WRITE | PUMP_EXCEPT); 70 | 71 | return j; 72 | } 73 | 74 | int pump_ready(pump_t *p, int fd, int mode) { 75 | int rv = 0; 76 | 77 | if (mode & PUMP_READ) { 78 | rv |= FD_ISSET(fd, &p->rfds); 79 | } 80 | 81 | if (mode & PUMP_WRITE) { 82 | rv |= FD_ISSET(fd, &p->wfds); 83 | } 84 | 85 | if (mode & PUMP_EXCEPT) { 86 | rv |= FD_ISSET(fd, &p->efds); 87 | } 88 | 89 | return rv; 90 | } 91 | 92 | int pump_select(pump_t *p) { 93 | return select(FD_SETSIZE, &p->rfds, NULL, &p->efds, NULL); 94 | } 95 | 96 | int pump_pair_splice(pump_pair_t *pp) { 97 | int rv; 98 | 99 | if (!FD_ISSET(pp->rfd, &pp->p->rfds)) { 100 | return 0; 101 | } 102 | 103 | if (!FD_ISSET(pp->wfd, &pp->p->wfds)) { 104 | return 0; 105 | } 106 | 107 | rv = splice(pp->rfd, NULL, pp->wfd, NULL, 64 * 1024, SPLICE_F_NONBLOCK); 108 | if (rv == -1) { 109 | perror("splice"); 110 | exit(1); 111 | } else if (rv == 0) { 112 | pump_pair_close(pp); 113 | } 114 | 115 | return rv; 116 | } 117 | 118 | int pump_pair_copy(pump_pair_t *pp) { 119 | char buf[64 * 1024]; 120 | char *ptr = buf; 121 | int nr, nw; 122 | 123 | if (!FD_ISSET(pp->rfd, &pp->p->rfds)) { 124 | return 0; 125 | } 126 | 127 | do { 128 | nr = read(pp->rfd, buf, sizeof(buf)); 129 | } while (nr == -1 && errno == EINTR); 130 | 131 | if (nr <= 0) { 132 | pump_pair_close(pp); 133 | return nr; 134 | } 135 | 136 | while (nr) { 137 | do { 138 | nw = write(pp->wfd, ptr, nr); 139 | } while (nw == -1 && errno == EINTR); 140 | 141 | if (nw <= 0) { 142 | pump_pair_close(pp); 143 | return nw; 144 | } 145 | 146 | ptr += nw; 147 | nr -= nw; 148 | } 149 | 150 | return 0; 151 | } 152 | -------------------------------------------------------------------------------- /warden/src/iomux/ring_buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "ring_buffer.h" 9 | #include "util.h" 10 | 11 | struct ring_buffer_s { 12 | size_t capacity; /* Total size of data */ 13 | size_t size; /* How many bytes are currently stored in data */ 14 | size_t start; /* Offset in data of the first logical byte */ 15 | uint8_t data[]; 16 | }; 17 | 18 | /** 19 | * Translates a logical index in the ringbuffer (assuming 0 is the oldest byte 20 | * and size - 1 is the newest byte) into a raw offset in the underlying array. 21 | */ 22 | static inline size_t log_to_raw(const ring_buffer_t *buf, size_t off) { 23 | if (buf->size < buf->capacity) { 24 | return off; 25 | } else { 26 | return (buf->start + off) % buf->capacity; 27 | } 28 | } 29 | 30 | ring_buffer_t *ring_buffer_alloc(size_t capacity) { 31 | ring_buffer_t *buf = NULL; 32 | 33 | assert(capacity > 0); 34 | 35 | buf = calloc(sizeof(*buf) + capacity, sizeof(uint8_t)); 36 | assert(NULL != buf); 37 | 38 | buf->capacity = capacity; 39 | 40 | return buf; 41 | } 42 | 43 | void ring_buffer_append(ring_buffer_t *buf, const uint8_t *data, size_t size) { 44 | size_t off = 0; 45 | size_t nremain = size; 46 | size_t nto_copy = 0; 47 | 48 | assert(NULL != buf); 49 | assert(NULL != data); 50 | 51 | if (0 == size) { 52 | return; 53 | } 54 | 55 | /* Don't waste time copying bytes that would be overwritten */ 56 | if (size > buf->capacity) { 57 | off = size - buf->capacity; 58 | nremain = buf->capacity; 59 | } 60 | 61 | while (nremain > 0) { 62 | nto_copy = MIN(nremain, buf->capacity - buf->start); 63 | 64 | memcpy(buf->data + buf->start, data + off, nto_copy); 65 | 66 | buf->size = MIN(buf->size + nto_copy, buf->capacity); 67 | buf->start = (buf->start + nto_copy) % buf->capacity; 68 | 69 | off += nto_copy; 70 | nremain -= nto_copy; 71 | } 72 | } 73 | 74 | size_t ring_buffer_read(const ring_buffer_t *buf, size_t start, uint8_t *dst, 75 | size_t size) { 76 | size_t ncopied = 0; 77 | size_t nremain = 0; 78 | size_t nto_copy = 0; 79 | 80 | assert(NULL != buf); 81 | assert(NULL != dst); 82 | assert(start <= buf->size); 83 | 84 | nremain = MIN(buf->size - start, size); 85 | 86 | while (nremain > 0) { 87 | nto_copy = MIN(nremain, buf->capacity - log_to_raw(buf, start + ncopied)); 88 | 89 | memcpy(dst + ncopied, 90 | buf->data + log_to_raw(buf, start + ncopied), 91 | nto_copy); 92 | 93 | nremain -= nto_copy; 94 | ncopied += nto_copy; 95 | } 96 | 97 | return ncopied; 98 | } 99 | 100 | uint8_t *ring_buffer_dup(const ring_buffer_t *buf) { 101 | uint8_t *ret = NULL; 102 | 103 | if (buf->size == 0) { 104 | return NULL; 105 | } 106 | 107 | ret = malloc(buf->size); 108 | assert(NULL != buf); 109 | 110 | ring_buffer_read(buf, 0, ret, buf->size); 111 | 112 | return ret; 113 | } 114 | 115 | size_t ring_buffer_size(const ring_buffer_t *buf) { 116 | assert(NULL != buf); 117 | 118 | return buf->size; 119 | } 120 | 121 | void ring_buffer_free(ring_buffer_t *buf) { 122 | assert(NULL != buf); 123 | 124 | free(buf); 125 | } 126 | -------------------------------------------------------------------------------- /warden/lib/warden/container/insecure.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "warden/container/base" 4 | require "warden/errors" 5 | 6 | require "socket" 7 | require "tempfile" 8 | 9 | module Warden 10 | 11 | module Container 12 | 13 | class Insecure < Base 14 | 15 | def self.setup(config, drained = false) 16 | super 17 | 18 | # noop 19 | end 20 | 21 | def do_create(request, response) 22 | sh File.join(root_path, "create.sh"), container_path 23 | logger.debug("Container created") 24 | end 25 | 26 | def do_stop(request, response) 27 | args = [File.join(container_path, "stop.sh")] 28 | args += ["-w", "0"] if request.kill 29 | 30 | # Add option hash 31 | args << { :timeout => nil } 32 | 33 | sh *args 34 | 35 | nil 36 | end 37 | 38 | def do_destroy(request, response) 39 | sh File.join(root_path, "destroy.sh"), container_path 40 | logger.debug("Container destroyed") 41 | end 42 | 43 | def create_job(request) 44 | spawn_job(File.join(container_path, "run.sh"), 45 | :input => request.script, 46 | :env => resource_limits(request)) 47 | end 48 | 49 | def do_net_in(request, response) 50 | host_port = self.class.port_pool.acquire 51 | 52 | # Ignore the container port, since there is nothing we can do 53 | container_port = host_port 54 | 55 | # Port may be re-used after this container has been destroyed 56 | @resources["ports"] << host_port 57 | @acquired["ports"] << host_port 58 | 59 | response.host_port = host_port 60 | response.container_port = container_port 61 | 62 | nil 63 | end 64 | 65 | def acquire(opts = {}) 66 | if !@resources.has_key?("ports") 67 | @resources["ports"] = [] 68 | @acquired["ports"] = [] 69 | else 70 | @acquired["ports"] = @resources["ports"].dup 71 | end 72 | 73 | super 74 | end 75 | 76 | def release 77 | if ports = @acquired.delete("ports") 78 | ports.each { |port| self.class.port_pool.release(port) } 79 | end 80 | 81 | super 82 | end 83 | 84 | def do_copy_in(request, response) 85 | src_path = request.src_path 86 | dst_path = request.dst_path 87 | 88 | perform_rsync(src_path, container_relative_path(dst_path)) 89 | 90 | nil 91 | end 92 | 93 | def do_copy_out(request, response) 94 | src_path = request.src_path 95 | dst_path = request.dst_path 96 | 97 | perform_rsync(container_relative_path(src_path), dst_path) 98 | 99 | if request.owner 100 | sh "chown", "-R", request.owner, dst_path 101 | end 102 | 103 | nil 104 | end 105 | 106 | private 107 | 108 | def perform_rsync(src_path, dst_path) 109 | # Build arguments 110 | args = ["rsync"] 111 | args += ["-r"] # Recursive copy 112 | args += ["-p"] # Preserve permissions 113 | args += ["--links"] # Preserve symlinks 114 | args += [src_path, dst_path] 115 | 116 | # Add option hash 117 | args << { :timeout => nil } 118 | 119 | sh *args 120 | end 121 | 122 | def container_relative_path(path) 123 | File.join(container_path, "root", path[1..-1]) 124 | end 125 | end 126 | end 127 | end 128 | -------------------------------------------------------------------------------- /em-warden-client/lib/em/warden/client/connection.rb: -------------------------------------------------------------------------------- 1 | require "eventmachine" 2 | require "warden/client" 3 | require "warden/client/v1" 4 | require "warden/protocol/buffer" 5 | require "em/warden/client/error" 6 | require "em/warden/client/event_emitter" 7 | 8 | module EventMachine 9 | module Warden 10 | module Client 11 | end 12 | end 13 | end 14 | 15 | class EventMachine::Warden::Client::Connection < ::EM::Connection 16 | include EM::Warden::Client::EventEmitter 17 | 18 | IDLE_TIMEOUT = 30 19 | 20 | class CommandResult 21 | def initialize(value) 22 | @value = value 23 | end 24 | 25 | def get 26 | if @value.kind_of?(StandardError) 27 | raise @value 28 | else 29 | @value 30 | end 31 | end 32 | end 33 | 34 | def cancel_idle_timer 35 | @idle_timer.cancel if @idle_timer 36 | end 37 | 38 | def setup_idle_timer 39 | cancel_idle_timer 40 | 41 | @idle_timer = EM::Timer.new(@idle_timeout) { idle_timeout! } 42 | end 43 | 44 | def idle_timeout=(idle_timeout) 45 | @idle_timeout = idle_timeout 46 | 47 | setup_idle_timer 48 | end 49 | 50 | def idle_timeout! 51 | close_connection 52 | end 53 | 54 | def post_init 55 | @requests = [] 56 | @connected = false 57 | @buffer = ::Warden::Protocol::Buffer.new 58 | 59 | @idle_timer = nil 60 | @idle_timeout = IDLE_TIMEOUT 61 | 62 | on(:connected) do 63 | @connected = true 64 | end 65 | 66 | on(:disconnected) do 67 | @connected = false 68 | end 69 | 70 | on(:disconnected) do 71 | # Execute callback for pending requests 72 | response = EventMachine::Warden::Client::ConnectionError.new("Disconnected") 73 | while !@requests.empty? 74 | _, blk = @requests.shift 75 | if blk 76 | blk.call(CommandResult.new(response)) 77 | end 78 | end 79 | end 80 | end 81 | 82 | def connected? 83 | @connected 84 | end 85 | 86 | def connection_completed 87 | emit(:connected) 88 | end 89 | 90 | def unbind 91 | emit(:disconnected) 92 | end 93 | 94 | def call(*args, &blk) 95 | if args.first.kind_of?(::Warden::Protocol::BaseRequest) 96 | request = args.first 97 | else 98 | # Use array when single array is passed 99 | if args.length == 1 && args.first.is_a?(::Array) 100 | args = args.first 101 | end 102 | 103 | # Create request from array 104 | request = ::Warden::Client::V1.request_from_v1(args.dup) 105 | @v1mode = true 106 | end 107 | 108 | @requests << [request, blk] 109 | 110 | cancel_idle_timer 111 | 112 | send_data(::Warden::Protocol::Buffer.request_to_wire(request)) 113 | end 114 | 115 | def method_missing(method, *args, &blk) 116 | call(*([method] + args), &blk) 117 | end 118 | 119 | def receive_data(data = nil) 120 | @buffer << data if data 121 | @buffer.each_response do |response| 122 | # Transform response if needed 123 | if response.is_a?(Warden::Protocol::ErrorResponse) 124 | response = EventMachine::Warden::Client::Error.new(response.message) 125 | else 126 | if @v1mode 127 | response = Warden::Client::V1.response_to_v1(response) 128 | end 129 | end 130 | 131 | request, blk = @requests.shift 132 | if blk 133 | blk.call(CommandResult.new(response)) 134 | end 135 | 136 | setup_idle_timer if @requests.empty? 137 | end 138 | end 139 | end 140 | -------------------------------------------------------------------------------- /warden-protocol/spec/support/helper.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | module Helper 4 | def self.included(base) 5 | base.extend(ClassMethods) 6 | end 7 | 8 | # Compare strings in hashes regardless of their encoding 9 | def compare_without_encoding(a, b) 10 | (a.keys + b.keys).uniq.each do |key| 11 | if a[key].respond_to?(:encoding) 12 | a[key].should == b[key].force_encoding(a[key].encoding) 13 | else 14 | a[key].should == b[key] 15 | end 16 | end 17 | end 18 | 19 | module ClassMethods 20 | def field(field, &blk) 21 | describe field do 22 | let(:field) do 23 | subject.fields.values.detect { |f| f.name == field } 24 | end 25 | 26 | instance_eval(&blk) 27 | end 28 | end 29 | 30 | def it_should_be_required 31 | it "should be required" do 32 | subject.should be_valid 33 | subject.send("#{field.name}=", nil) 34 | subject.should_not be_valid 35 | end 36 | end 37 | 38 | def it_should_be_optional 39 | it "should be optional" do 40 | subject.should be_valid 41 | subject.send("#{field.name}=", nil) 42 | subject.should be_valid 43 | end 44 | end 45 | 46 | def it_should_default_to(default) 47 | it "should default to #{default}" do 48 | instance = subject.reload 49 | instance.send(field.name).should == default 50 | end 51 | end 52 | 53 | def it_should_be_typed_as_uint 54 | it "should not allow a signed integer" do 55 | subject.send("#{field.name}=", -1) 56 | subject.should_not be_valid 57 | end 58 | 59 | it "should allow zero" do 60 | subject.send("#{field.name}=", 0) 61 | subject.should be_valid 62 | end 63 | 64 | it "should allow integers larger than zero" do 65 | subject.send("#{field.name}=", 37) 66 | subject.should be_valid 67 | end 68 | end 69 | 70 | def it_should_be_typed_as_uint32 71 | it_should_be_typed_as_uint 72 | 73 | it "should allow integer 2^32-1" do 74 | subject.send("#{field.name}=", 2**32-1) 75 | subject.should be_valid 76 | end 77 | 78 | it "should not allow integer 2^32" do 79 | subject.send("#{field.name}=", 2**32) 80 | subject.should_not be_valid 81 | end 82 | end 83 | 84 | def it_should_be_typed_as_uint64 85 | it_should_be_typed_as_uint 86 | 87 | it "should allow integer 2^64-1" do 88 | subject.send("#{field.name}=", 2**64-1) 89 | subject.should be_valid 90 | end 91 | 92 | it "should not allow integer 2^64" do 93 | subject.send("#{field.name}=", 2**64) 94 | subject.should_not be_valid 95 | end 96 | end 97 | 98 | def it_should_be_typed_as_string 99 | it "should allow an empty string" do 100 | subject.send("#{field.name}=", "") 101 | subject.should be_valid 102 | end 103 | 104 | it "should allow a non-empty string" do 105 | subject.send("#{field.name}=", "non-empty") 106 | subject.should be_valid 107 | end 108 | end 109 | 110 | def it_should_be_typed_as_boolean 111 | it "should allow false" do 112 | subject.send("#{field.name}=", false) 113 | subject.should be_valid 114 | end 115 | 116 | it "should allow true" do 117 | subject.send("#{field.name}=", true) 118 | subject.should be_valid 119 | end 120 | end 121 | end 122 | end 123 | -------------------------------------------------------------------------------- /warden/src/iomux/test/test_status_writer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "barrier.h" 10 | #include "status_reader.h" 11 | #include "status_writer.h" 12 | #include "test_util.h" 13 | #include "util.h" 14 | 15 | typedef struct { 16 | uint8_t got_status; 17 | int status; 18 | barrier_t *barrier; 19 | char domain_path[256]; 20 | pthread_t thread; 21 | status_reader_t reader; 22 | } sink_t; 23 | 24 | static sink_t *sink_alloc(const char *domain_path) { 25 | sink_t *s = NULL; 26 | 27 | s = calloc(1, sizeof(*s)); 28 | assert(NULL != s); 29 | 30 | strcpy(s->domain_path, domain_path); 31 | s->barrier = barrier_alloc(); 32 | 33 | return s; 34 | } 35 | 36 | static void sink_free(sink_t *s) { 37 | assert(NULL != s); 38 | 39 | barrier_free(s->barrier); 40 | free(s); 41 | } 42 | 43 | static void *run_status_writer(void *data) { 44 | assert(NULL != data); 45 | 46 | status_writer_run((status_writer_t *) data); 47 | 48 | return NULL; 49 | } 50 | 51 | static void *run_sink(void *data) { 52 | sink_t *s = NULL; 53 | int fd = -1; 54 | uint8_t hup = 0; 55 | int ii = 0; 56 | 57 | assert(NULL != data); 58 | 59 | s = (sink_t *) data; 60 | 61 | for (ii = 0; ii < 5 && fd == -1; ii++) { 62 | fd = unix_domain_connect(s->domain_path); 63 | usleep(100000); 64 | } 65 | barrier_lift(s->barrier); 66 | 67 | if (-1 == fd) { 68 | perror("connect()"); 69 | return NULL; 70 | } 71 | 72 | status_reader_init(&(s->reader), fd); 73 | status_reader_run(&(s->reader), &hup); 74 | if (!hup) { 75 | s->got_status = 1; 76 | s->status = s->reader.status; 77 | } 78 | 79 | close(fd); 80 | 81 | return NULL; 82 | } 83 | 84 | void test_status_writer(void) { 85 | char domain_path[256]; 86 | int listen_sock = 0; 87 | int fd = 0; 88 | status_writer_t *sw = NULL; 89 | pthread_t sw_thread; 90 | sink_t *sinks[3]; 91 | int ii = 0; 92 | int status = 10; 93 | 94 | signal(SIGPIPE, SIG_IGN); 95 | 96 | strcpy(domain_path, "/tmp/muxer_test_sock_XXXXXX"); 97 | fd = mkstemp(domain_path); 98 | assert(-1 != fd); 99 | close(fd); 100 | 101 | for (ii = 0; ii < 3; ++ii) { 102 | sinks[ii] = sink_alloc(domain_path); 103 | } 104 | 105 | listen_sock = create_unix_domain_listener(domain_path, 10); 106 | assert(-1 != listen_sock); 107 | 108 | sw = status_writer_alloc(listen_sock, NULL); 109 | 110 | if (pthread_create(&sw_thread, NULL, run_status_writer, sw)) { 111 | perror("pthread_create"); 112 | assert(0); 113 | } 114 | 115 | /* Create sinks and wait for them to connect */ 116 | for (ii = 0; ii < 3; ++ii) { 117 | if (pthread_create(&(sinks[ii]->thread), NULL, run_sink, sinks[ii])) { 118 | perror("pthread_create"); 119 | assert(0); 120 | } 121 | barrier_wait(sinks[ii]->barrier); 122 | } 123 | 124 | status_writer_finish(sw, status); 125 | 126 | pthread_join(sw_thread, NULL); 127 | for (ii = 0; ii < 3; ++ii) { 128 | pthread_join(sinks[ii]->thread, NULL); 129 | TEST_CHECK(sinks[ii]->got_status == 1); 130 | TEST_CHECK(sinks[ii]->status == status); 131 | } 132 | 133 | /* Cleanup */ 134 | status_writer_free(sw); 135 | for (ii = 0; ii < 3; ++ii) { 136 | sink_free(sinks[ii]); 137 | } 138 | unlink(domain_path); 139 | } 140 | -------------------------------------------------------------------------------- /warden/src/wsh/un.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "un.h" 13 | #include "util.h" 14 | 15 | int un__socket() { 16 | int fd; 17 | 18 | if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { 19 | perror("socket"); 20 | exit(1); 21 | } 22 | 23 | return fd; 24 | } 25 | 26 | int un_listen(const char *path) { 27 | int fd; 28 | struct sockaddr_un sa; 29 | 30 | fd = un__socket(); 31 | 32 | sa.sun_family = AF_UNIX; 33 | assert(strlen(path) < sizeof(sa.sun_path)); 34 | strcpy(sa.sun_path, path); 35 | unlink(sa.sun_path); 36 | 37 | fcntl_mix_cloexec(fd); 38 | 39 | if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { 40 | perror("bind"); 41 | exit(1); 42 | } 43 | 44 | if (listen(fd, 5) == -1) { 45 | perror("listen"); 46 | exit(1); 47 | } 48 | 49 | return fd; 50 | } 51 | 52 | int un_connect(const char *path) { 53 | int fd; 54 | struct sockaddr_un sa; 55 | int rv; 56 | 57 | fd = un__socket(); 58 | 59 | sa.sun_family = AF_UNIX; 60 | assert(strlen(path) < sizeof(sa.sun_path)); 61 | strcpy(sa.sun_path, path); 62 | 63 | rv = connect(fd, (struct sockaddr *)&sa, sizeof(sa)); 64 | if (rv == -1) { 65 | close(fd); 66 | return rv; 67 | } 68 | 69 | return fd; 70 | } 71 | 72 | int un_send_fds(int fd, char *data, int datalen, int *fds, int fdslen) { 73 | struct msghdr mh; 74 | struct cmsghdr *cmh = NULL; 75 | char buf[2048]; 76 | size_t buflen = CMSG_SPACE(sizeof(int) * fdslen); 77 | struct iovec iov[1]; 78 | 79 | assert(sizeof(buf) >= buflen); 80 | 81 | memset(&mh, 0, sizeof(mh)); 82 | 83 | mh.msg_control = buf; 84 | mh.msg_controllen = buflen; 85 | mh.msg_iov = iov; 86 | mh.msg_iovlen = 1; 87 | iov[0].iov_base = data; 88 | iov[0].iov_len = datalen; 89 | 90 | cmh = CMSG_FIRSTHDR(&mh); 91 | cmh->cmsg_level = SOL_SOCKET; 92 | cmh->cmsg_type = SCM_RIGHTS; 93 | cmh->cmsg_len = CMSG_LEN(sizeof(int) * fdslen); 94 | memcpy(CMSG_DATA(cmh), fds, sizeof(int) * fdslen); 95 | mh.msg_controllen = cmh->cmsg_len; 96 | 97 | int rv; 98 | 99 | do { 100 | rv = sendmsg(fd, &mh, 0); 101 | } while (rv == -1 && errno == EINTR); 102 | 103 | return rv; 104 | } 105 | 106 | int un_recv_fds(int fd, char *data, int datalen, int *fds, int fdslen) { 107 | struct msghdr mh; 108 | struct cmsghdr *cmh = NULL; 109 | char buf[2048]; 110 | size_t buflen = CMSG_SPACE(sizeof(int) * fdslen); 111 | struct iovec iov[1]; 112 | 113 | assert(sizeof(buf) >= buflen); 114 | 115 | memset(&mh, 0, sizeof(mh)); 116 | 117 | mh.msg_control = buf; 118 | mh.msg_controllen = buflen; 119 | mh.msg_iov = iov; 120 | mh.msg_iovlen = 1; 121 | iov[0].iov_base = data; 122 | iov[0].iov_len = datalen; 123 | 124 | int rv; 125 | 126 | do { 127 | rv = recvmsg(fd, &mh, 0); 128 | } while (rv == -1 && (errno == EINTR || errno == EAGAIN)); 129 | 130 | if (rv <= 0) { 131 | goto done; 132 | } 133 | 134 | if (fds != NULL) { 135 | cmh = CMSG_FIRSTHDR(&mh); 136 | assert(cmh != NULL); 137 | assert(cmh->cmsg_level == SOL_SOCKET); 138 | assert(cmh->cmsg_type == SCM_RIGHTS); 139 | assert(cmh->cmsg_len == CMSG_LEN(sizeof(int) * fdslen)); 140 | 141 | int *fds_ = (int *)CMSG_DATA(cmh); 142 | int i; 143 | 144 | for (i = 0; i < fdslen; i++) { 145 | fds[i] = fds_[i]; 146 | } 147 | } 148 | 149 | done: 150 | return rv; 151 | } 152 | -------------------------------------------------------------------------------- /warden/spec/support/examples/running_commands.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | shared_examples "running commands" do 4 | attr_reader :handle 5 | 6 | before do 7 | @handle = client.create.handle 8 | end 9 | 10 | it "should redirect stdout output" do 11 | response = client.run(:handle => handle, :script => "echo hi") 12 | response.exit_status.should == 0 13 | response.stdout.should == "hi\n" 14 | response.stderr.should == "" 15 | end 16 | 17 | it "should redirect stderr output" do 18 | response = client.run(:handle => handle, :script => "echo hi 1>&2") 19 | response.exit_status.should == 0 20 | response.stdout.should == "" 21 | response.stderr.should == "hi\n" 22 | end 23 | 24 | it "should propagate exit status" do 25 | response = client.run(:handle => handle, :script => "exit 123") 26 | response.exit_status.should == 123 27 | end 28 | 29 | it "should terminate when the container is destroyed" do 30 | client.write(Warden::Protocol::RunRequest.new(:handle => handle, :script => "sleep 5")) 31 | 32 | # Wait for the command to run 33 | sleep 0.1 34 | 35 | other_client = create_client 36 | other_client.destroy(:handle => handle) 37 | 38 | # The command should not have exited cleanly 39 | response = client.read 40 | response.exit_status.should_not == 0 41 | end 42 | 43 | it "should return an error when the handle is unknown" do 44 | expect do 45 | client.run(:handle => handle.next, :script => "echo") 46 | end.to raise_error(/unknown handle/i) 47 | end 48 | 49 | describe "via spawn/link" do 50 | it "works when linking an unfinished job" do 51 | response = client.spawn(:handle => handle, :script => "sleep 0.1") 52 | job_id = response.job_id 53 | 54 | sleep 0.0 55 | response = client.link(:handle => handle, :job_id => job_id) 56 | response.exit_status.should == 0 57 | end 58 | 59 | it "works when linking a finished job" do 60 | response = client.spawn(:handle => handle, :script => "sleep 0.0") 61 | job_id = response.job_id 62 | 63 | sleep 0.1 64 | response = client.link(:handle => handle, :job_id => job_id) 65 | response.exit_status.should == 0 66 | end 67 | 68 | describe "on different connections" do 69 | let(:c1) { create_client } 70 | let(:c2) { create_client } 71 | 72 | it "works when both link an unfinished job" do 73 | response = c1.spawn(:handle => handle, :script => "sleep 0.1") 74 | job_id = response.job_id 75 | 76 | sleep 0.0 77 | c1.write(Warden::Protocol::LinkRequest.new(:handle => handle, :job_id => job_id)) 78 | c2.write(Warden::Protocol::LinkRequest.new(:handle => handle, :job_id => job_id)) 79 | 80 | r1 = c1.read 81 | r2 = c2.read 82 | r1.should == r2 83 | end 84 | 85 | it "works when both link a finished job" do 86 | response = c1.spawn(:handle => handle, :script => "sleep 0.0") 87 | job_id = response.job_id 88 | 89 | sleep 0.1 90 | c1.write(Warden::Protocol::LinkRequest.new(:handle => handle, :job_id => job_id)) 91 | c2.write(Warden::Protocol::LinkRequest.new(:handle => handle, :job_id => job_id)) 92 | 93 | r1 = c1.read 94 | r2 = c2.read 95 | r1.should == r2 96 | end 97 | 98 | it "works when the one spawning disconnects" do 99 | response = c1.spawn(:handle => handle, :script => "sleep 0.1") 100 | job_id = response.job_id 101 | c1.disconnect 102 | 103 | response = c2.link(:handle => handle, :job_id => job_id) 104 | response.exit_status.should == 0 105 | end 106 | end 107 | end 108 | end 109 | -------------------------------------------------------------------------------- /warden/spec/support/examples/streaming_commands.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | shared_examples "streaming commands" do 4 | attr_reader :handle 5 | 6 | before do 7 | @handle = client.create.handle 8 | end 9 | 10 | it "works when streaming an unfinished job" do 11 | response = client.spawn(:handle => handle, :script => "printf A; sleep 0.05; printf B") 12 | job_id = response.job_id 13 | 14 | sleep 0.0 15 | 16 | client.write(Warden::Protocol::StreamRequest.new(:handle => handle, :job_id => job_id)) 17 | 18 | stdout = "" 19 | loop do 20 | response = client.read 21 | break if response.name.nil? 22 | 23 | response.name.should == "stdout" 24 | stdout << response.data 25 | end 26 | 27 | stdout.should == "AB" 28 | response.exit_status.should == 0 29 | end 30 | 31 | it "works streaming a finished job" do 32 | response = client.spawn(:handle => handle, :script => "printf A; sleep 0.05; printf B") 33 | job_id = response.job_id 34 | 35 | sleep 0.1 36 | 37 | client.write(Warden::Protocol::StreamRequest.new(:handle => handle, :job_id => job_id)) 38 | 39 | stdout = "" 40 | loop do 41 | response = client.read 42 | break if response.name.nil? 43 | 44 | response.name.should == "stdout" 45 | stdout << response.data 46 | end 47 | 48 | stdout.should == "AB" 49 | response.exit_status.should == 0 50 | end 51 | 52 | context "on different connections" do 53 | let(:c1) { create_client } 54 | let(:c2) { create_client } 55 | 56 | it "works when both stream an unfinished job" do 57 | response = c1.spawn(:handle => handle, :script => "printf A; sleep 0.05; printf B") 58 | job_id = response.job_id 59 | 60 | sleep 0.0 61 | c1.write(Warden::Protocol::StreamRequest.new(:handle => handle, :job_id => job_id)) 62 | c2.write(Warden::Protocol::StreamRequest.new(:handle => handle, :job_id => job_id)) 63 | 64 | [c1, c2].each do |client| 65 | stdout = "" 66 | loop do 67 | response = client.read 68 | break if response.name.nil? 69 | 70 | response.name.should == "stdout" 71 | stdout << response.data 72 | end 73 | 74 | stdout.should == "AB" 75 | response.exit_status.should == 0 76 | end 77 | end 78 | 79 | it "works when both stream a finished job" do 80 | response = c1.spawn(:handle => handle, :script => "printf A; sleep 0.05; printf B") 81 | job_id = response.job_id 82 | 83 | sleep 0.1 84 | c1.write(Warden::Protocol::StreamRequest.new(:handle => handle, :job_id => job_id)) 85 | c2.write(Warden::Protocol::StreamRequest.new(:handle => handle, :job_id => job_id)) 86 | 87 | [c1, c2].each do |client| 88 | stdout = "" 89 | loop do 90 | response = client.read 91 | break if response.name.nil? 92 | 93 | response.name.should == "stdout" 94 | stdout << response.data 95 | end 96 | 97 | stdout.should == "AB" 98 | response.exit_status.should == 0 99 | end 100 | end 101 | 102 | it "works when the one spawning disconnects" do 103 | response = c1.spawn(:handle => handle, :script => "printf A; sleep 0.05; printf B") 104 | job_id = response.job_id 105 | c1.disconnect 106 | 107 | c2.write(Warden::Protocol::StreamRequest.new(:handle => handle, :job_id => job_id)) 108 | 109 | [c2].each do |client| 110 | stdout = "" 111 | loop do 112 | response = client.read 113 | break if response.name.nil? 114 | 115 | response.name.should == "stdout" 116 | stdout << response.data 117 | end 118 | 119 | stdout.should == "AB" 120 | response.exit_status.should == 0 121 | end 122 | end 123 | end 124 | end 125 | -------------------------------------------------------------------------------- /warden/root/linux/skeleton/net.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -o xtrace 4 | set -o nounset 5 | set -o errexit 6 | shopt -s nullglob 7 | 8 | cd $(dirname "${0}") 9 | 10 | source ./etc/config 11 | 12 | filter_dispatch_chain="warden-dispatch" 13 | filter_default_chain="warden-default" 14 | filter_instance_prefix="warden-instance-" 15 | filter_instance_chain="${filter_instance_prefix}${id}" 16 | nat_prerouting_chain="warden-prerouting" 17 | nat_instance_prefix="warden-instance-" 18 | nat_instance_chain="${filter_instance_prefix}${id}" 19 | 20 | function teardown_filter() { 21 | # Prune dispatch chain 22 | iptables -S ${filter_dispatch_chain} 2> /dev/null | 23 | grep "\-g ${filter_instance_chain}\b" | 24 | sed -e "s/-A/-D/" | 25 | xargs --no-run-if-empty --max-lines=1 iptables 26 | 27 | # Flush and delete instance chain 28 | iptables -F ${filter_instance_chain} 2> /dev/null || true 29 | iptables -X ${filter_instance_chain} 2> /dev/null || true 30 | } 31 | 32 | function setup_filter() { 33 | teardown_filter 34 | 35 | # Create instance chain 36 | iptables -N ${filter_instance_chain} 37 | iptables -A ${filter_instance_chain} \ 38 | --goto ${filter_default_chain} 39 | 40 | # Bind instance chain to dispatch chain 41 | iptables -I ${filter_dispatch_chain} 2 \ 42 | --in-interface ${network_host_iface} \ 43 | --goto ${filter_instance_chain} 44 | } 45 | 46 | function teardown_nat() { 47 | # Prune prerouting chain 48 | iptables -t nat -S ${nat_prerouting_chain} 2> /dev/null | 49 | grep "\-j ${nat_instance_chain}\b" | 50 | sed -e "s/-A/-D/" | 51 | xargs --no-run-if-empty --max-lines=1 iptables -t nat 52 | 53 | # Flush and delete instance chain 54 | iptables -t nat -F ${nat_instance_chain} 2> /dev/null || true 55 | iptables -t nat -X ${nat_instance_chain} 2> /dev/null || true 56 | } 57 | 58 | function setup_nat() { 59 | teardown_nat 60 | 61 | # Create instance chain 62 | iptables -t nat -N ${nat_instance_chain} 63 | 64 | # Bind instance chain to prerouting chain 65 | iptables -t nat -A ${nat_prerouting_chain} \ 66 | --jump ${nat_instance_chain} 67 | } 68 | 69 | # Lock execution 70 | mkdir -p ../tmp 71 | exec 3> ../tmp/$(basename $0).lock 72 | flock -x -w 10 3 73 | 74 | case "${1}" in 75 | "setup") 76 | setup_filter 77 | setup_nat 78 | 79 | ;; 80 | 81 | "teardown") 82 | teardown_filter 83 | teardown_nat 84 | 85 | ;; 86 | 87 | "in") 88 | if [ -z "${HOST_PORT:-}" ]; then 89 | echo "Please specify HOST_PORT..." 1>&2 90 | exit 1 91 | fi 92 | 93 | if [ -z "${CONTAINER_PORT:-}" ]; then 94 | echo "Please specify CONTAINER_PORT..." 1>&2 95 | exit 1 96 | fi 97 | 98 | iptables -t nat -A ${nat_instance_chain} \ 99 | --protocol tcp \ 100 | --destination-port "${HOST_PORT}" \ 101 | --jump DNAT \ 102 | --to-destination "${network_container_ip}:${CONTAINER_PORT}" 103 | 104 | ;; 105 | 106 | "out") 107 | if [ -z "${NETWORK:-}" ] && [ -z "${PORT:-}" ]; then 108 | echo "Please specify NETWORK and/or PORT..." 1>&2 109 | exit 1 110 | fi 111 | 112 | opts="" 113 | 114 | if [ -n "${NETWORK:-}" ]; then 115 | opts="${opts} --destination ${NETWORK}" 116 | fi 117 | 118 | # Restrict protocol to tcp when port is specified 119 | if [ -n "${PORT:-}" ]; then 120 | opts="${opts} --protocol tcp" 121 | opts="${opts} --destination-port ${PORT}" 122 | fi 123 | 124 | iptables -I ${filter_instance_chain} 1 ${opts} --jump RETURN 125 | 126 | ;; 127 | "get_ingress_info") 128 | if [ -z "${ID:-}" ]; then 129 | echo "Please specify container ID..." 1>&2 130 | exit 1 131 | fi 132 | tc filter show dev w-${ID}-0 parent ffff: 133 | 134 | ;; 135 | "get_egress_info") 136 | if [ -z "${ID:-}" ]; then 137 | echo "Please specify container ID..." 1>&2 138 | exit 1 139 | fi 140 | tc qdisc show dev w-${ID}-0 141 | 142 | ;; 143 | *) 144 | echo "Unknown command: ${1}" 1>&2 145 | exit 1 146 | 147 | ;; 148 | esac 149 | --------------------------------------------------------------------------------