├── .gitignore ├── .gitmodules ├── .travis.yml ├── Dockerfile ├── Documentation ├── .gitattributes ├── .gitignore ├── Makefile └── libct.txt ├── LICENSE ├── Makefile ├── README.md ├── go ├── Makefile ├── cmd.go ├── libct.go ├── libct_test.go └── process.go ├── py ├── libct │ ├── __init__.py │ └── libctmodule.c └── setup.py ├── scripts ├── Makefile.build ├── Makefile.config ├── Makefile.rules ├── Makefile.version ├── feature-tests.mak └── utilities.mak ├── src ├── Makefile ├── arch │ └── x86 │ │ └── include │ │ └── asm │ │ ├── page.h │ │ └── types.h ├── cgroups.c ├── cmd.c ├── ct.c ├── devnodes.c ├── fs.c ├── include │ ├── asm-generic │ │ └── int.h │ ├── beancounter.h │ ├── bug.h │ ├── cgroups.h │ ├── cmd.h │ ├── compiler.h │ ├── ct.h │ ├── err.h │ ├── fs.h │ ├── libct.h │ ├── linux-kernel.h │ ├── list.h │ ├── log.h │ ├── namespaces.h │ ├── net.h │ ├── net_util.h │ ├── process.h │ ├── rpc.h │ ├── security.h │ ├── session.h │ ├── systemd.h │ ├── types.h │ ├── uapi │ │ ├── libct-errors.h │ │ ├── libct-log-levels.h │ │ └── libct.h │ ├── util.h │ ├── vz │ │ ├── readelf.h │ │ ├── vz.h │ │ ├── vz_net.h │ │ ├── vzcalluser.h │ │ ├── vzctl_veth.h │ │ ├── vziolimit.h │ │ ├── vziptable_defs.h │ │ ├── vzlist.h │ │ ├── vzsyscalls.h │ │ └── vztypes.h │ └── xmalloc.h ├── libct.c ├── linux_kernel.c ├── log.c ├── lsm │ ├── apparmor.c │ ├── lsm.c │ ├── lsm.h │ ├── nop.c │ └── selinux.c ├── namespaces.c ├── net.c ├── net_util.c ├── process.c ├── route.c ├── security.c ├── session.c ├── systemd.c ├── util.c └── vz │ ├── readelf.c │ ├── vz.c │ └── vz_net.c └── test ├── Makefile ├── apparmor.test ├── ct_apparmor.c ├── ct_caps.c ├── ct_cgroup_basic.c ├── ct_cgroup_sub.c ├── ct_create.c ├── ct_create_exec.c ├── ct_enter.c ├── ct_enter_pidns.c ├── ct_ext_mount.c ├── ct_fds.c ├── ct_kill_nons.c ├── ct_net_host.c ├── ct_net_veth.c ├── ct_pause.c ├── ct_pid_enter.c ├── ct_private_subdir.c ├── ct_private_subdir_ns.c ├── ct_proc.c ├── ct_root.c ├── ct_root_enter.c ├── ct_service.c ├── ct_switch.c ├── ct_userns.c ├── ct_userns_self.c ├── file_piggy.c ├── test.h ├── vz_cgroup_blkio.c ├── vz_cgroup_cpu.c ├── vz_cgroup_memory.c ├── vz_create_exec.c ├── vz_enter.c └── vz_net_veth.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.d 3 | *.bin 4 | *.elf 5 | *.out 6 | *.swp 7 | *.swo 8 | .git-ignore 9 | *.so 10 | *.a 11 | cscope* 12 | tags 13 | TAGS 14 | patches 15 | src/include/version.h 16 | src/include/config.h 17 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule ".shipped/libnl"] 2 | path = .shipped/libnl 3 | url = git://github.com/tgraf/libnl.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | compiler: gcc 4 | 5 | before_install: 6 | - sudo apt-get update -qq 7 | 8 | install: 9 | - sudo apt-get install make gcc autoconf autoconf-doc libtool bison flex libselinux1-dev libapparmor-dev libdbus-1-dev libcap-dev 10 | 11 | script: 12 | - git submodule update --init --recursive 13 | - ( cd .shipped/libnl && ./autogen.sh && ./configure && make -j2 ) 14 | - make -j2 15 | - make -j2 test-build 16 | 17 | notifications: 18 | email: 19 | on_success: change 20 | on_failure: always 21 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | RUN apt-get update && apt-get install -y \ 4 | build-essential \ 5 | pkg-config \ 6 | libtool \ 7 | autoconf \ 8 | git-core \ 9 | bison \ 10 | flex \ 11 | libselinux1-dev \ 12 | libapparmor-dev \ 13 | libdbus-1-dev 14 | 15 | COPY . /libct 16 | WORKDIR /libct 17 | 18 | # build libnl 19 | RUN git submodule update --init && \ 20 | cd .shipped/libnl && \ 21 | ./autogen.sh && \ 22 | ./configure && make -j $(nproc) 23 | 24 | RUN make clean && make -j $(nproc) 25 | -------------------------------------------------------------------------------- /Documentation/.gitattributes: -------------------------------------------------------------------------------- 1 | *.txt whitespace 2 | -------------------------------------------------------------------------------- /Documentation/.gitignore: -------------------------------------------------------------------------------- 1 | *.xml 2 | *.html 3 | *.[1-8] 4 | -------------------------------------------------------------------------------- /Documentation/Makefile: -------------------------------------------------------------------------------- 1 | ASCIIDOC := asciidoc 2 | A2X := a2x 3 | XMLTO := xmlto 4 | 5 | SRC += libct.txt 6 | MANS := $(patsubst %.txt,%.1,$(SRC)) 7 | PDFS := $(patsubst %.txt,%.pdf,$(SRC)) 8 | 9 | all: $(MANS) 10 | 11 | %.1: %.txt 12 | $(E) " GEN " $@ 13 | $(Q) $(ASCIIDOC) -b docbook -d manpage -o $(patsubst %.1,%.xml,$@) $< 14 | $(Q) $(XMLTO) man --skip-validation $(patsubst %.1,%.xml,$@) 2>/dev/null 15 | 16 | all-pdfs: $(PDFS) 17 | 18 | %.pdf: %.txt 19 | $(E) " GEN " $@ 20 | $(Q) $(A2X) -fpdf -d manpage $^ 21 | 22 | clean: 23 | $(E) " CLEANUP docs" 24 | $(Q) rm -f ./*.xml 25 | $(Q) rm -f ./*.1 26 | $(Q) rm -f ./*.pdf 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION_MAJOR := 0 2 | VERSION_MINOR := 1 3 | VERSION_SUBLEVEL := 4 | VERSION_EXTRA := 5 | VERSION_NAME := 6 | VERSION_SO_MAJOR := 0 7 | VERSION_SO_MINOR := 1 8 | 9 | MAKEFLAGS := -r -R --no-print-directory 10 | 11 | ifeq ($(strip $(V)),) 12 | E = @echo 13 | Q = @ 14 | else 15 | E = @\# 16 | Q = 17 | endif 18 | 19 | FIND := find 20 | CSCOPE := cscope 21 | TAGS := ctags 22 | RM := rm -f 23 | CP := cp 24 | LD := ld 25 | CC := gcc 26 | CD := cd 27 | ECHO := echo 28 | NM := nm 29 | AWK := awk 30 | SH := bash 31 | MAKE := make 32 | OBJCOPY := objcopy 33 | MKDIR := mkdir 34 | LN := ln 35 | ESED := esed 36 | SED := sed 37 | CAT := cat 38 | 39 | # 40 | # Fetch ARCH from the uname if not yet set 41 | # 42 | ARCH ?= $(shell uname -m | sed \ 43 | -e s/i.86/i386/ \ 44 | -e s/sun4u/sparc64/ \ 45 | -e s/arm.*/arm/ \ 46 | -e s/sa110/arm/ \ 47 | -e s/s390x/s390/ \ 48 | -e s/parisc64/parisc/ \ 49 | -e s/ppc.*/powerpc/ \ 50 | -e s/mips.*/mips/ \ 51 | -e s/sh[234].*/sh/) 52 | 53 | ifeq ($(ARCH),x86_64) 54 | ARCH := x86 55 | DEFINES := -DCONFIG_X86_64 -DARCH="\"$(ARCH)\"" 56 | LDARCH := i386:x86-64 57 | endif 58 | 59 | # Installation paths 60 | PREFIX ?= /usr/local 61 | MANDIR := $(PREFIX)/share/man 62 | LIBDIR := $(PREFIX)/lib 63 | # For recent Debian/Ubuntu with multiarch support 64 | DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture \ 65 | -qDEB_HOST_MULTIARCH 2>/dev/null) 66 | ifneq "$(DEB_HOST_MULTIARCH)" "" 67 | LIBDIR := $(PREFIX)/lib/$(DEB_HOST_MULTIARCH) 68 | # For most other systems 69 | else ifeq "$(shell uname -m)" "x86_64" 70 | LIBDIR := $(PREFIX)/lib64 71 | endif 72 | 73 | INCLUDEDIR := $(PREFIX)/include/libct 74 | 75 | ifneq ($(ARCH),x86) 76 | $(error "The architecture $(ARCH) isn't supported")) 77 | endif 78 | 79 | ifneq ("$(wildcard /proc/vz)","") 80 | VZ := 1 81 | endif 82 | 83 | 84 | cflags-y += -iquote src/include 85 | cflags-y += -iquote src/include/vz 86 | cflags-y += -iquote src/lsm 87 | cflags-y += -iquote src 88 | cflags-y += -fno-strict-aliasing 89 | cflags-y += -I/usr/include 90 | cflags-y += -I/usr/include/libnl3 91 | cflags-y += -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include 92 | cflags-y += -I/usr/lib64/dbus-1.0/include/ 93 | cflags-y += -I/usr/include/dbus-1.0 94 | export cflags-y 95 | 96 | VERSION_MAJOR := 0 97 | VERSION_MINOR := 1 98 | VERSION_SUBLEVEL := 0 99 | VERSION_EXTRA := 100 | VERSION_NAME := 101 | CONFIG_SELINUX := 102 | CONFIG_APPARMOR := 103 | 104 | # FAKE_LIBS is required for go bindings, 105 | # because LDFLAGS can not be customized there 106 | FAKE_LIBS := 107 | 108 | export VERSION_MAJOR VERSION_MINOR VERSION_SUBLEVEL VERSION_EXTRA VERSION_NAME 109 | 110 | include scripts/Makefile.version 111 | include scripts/Makefile.config 112 | 113 | LIBS := -lrt 114 | 115 | DEFINES += -D_FILE_OFFSET_BITS=64 116 | DEFINES += -D_GNU_SOURCE 117 | 118 | WARNINGS := -Wall -Wno-unused-result 119 | 120 | ifneq ($(WERROR),0) 121 | WARNINGS += -Werror 122 | endif 123 | 124 | ifeq ($(DEBUG),1) 125 | DEFINES += -DCR_DEBUG 126 | CFLAGS += -O0 -ggdb3 127 | else 128 | CFLAGS += -O2 129 | endif 130 | 131 | ifeq ($(CONFIG_APPARMOR),y) 132 | DEFINES += -DHAVE_APPARMOR 133 | else 134 | FAKE_LIBS += libapparmor.a 135 | endif 136 | 137 | ifeq ($(CONFIG_SELINUX),y) 138 | DEFINES += -DHAVE_SELINUX 139 | else 140 | FAKE_LIBS += libselinux.a 141 | endif 142 | 143 | CFLAGS += $(WARNINGS) $(DEFINES) 144 | 145 | export E Q CC ECHO MAKE CFLAGS LIBS ARCH DEFINES MAKEFLAGS 146 | export CONFIG_SELINUX CONFIG_APPARMOR 147 | export SH RM OBJCOPY LDARCH LD CP MKDIR CD LN 148 | export ESED SED CAT 149 | 150 | include scripts/Makefile.rules 151 | 152 | build := -r -R --no-print-directory -f scripts/Makefile.build makefile=Makefile obj 153 | run := -r -R --no-print-directory 154 | 155 | LIBCT := libct 156 | LIBCT-INC := src/include/uapi/libct.h src/include/uapi/libct-log-levels.h src/include/uapi/libct-errors.h 157 | 158 | .PHONY: all clean tags docs 159 | 160 | cflags-y += -I.shipped/libnl/include/ 161 | cflags-y += -iquote src/include 162 | cflags-y += -iquote src/arch/$(ARCH)/include 163 | export cflags-y 164 | 165 | # 166 | # First order targets, usually pregenerated 167 | EARLY-GEN := $(VERSION_HEADER) config 168 | 169 | # 170 | # Library itself 171 | src/%: $(EARLY-GEN) 172 | $(Q) $(MAKE) $(build)=src $@ 173 | src: $(EARLY-GEN) 174 | $(Q) $(MAKE) $(build)=src all 175 | 176 | .PHONY: src 177 | 178 | $(LIBCT).a: src/$(LIBCT).a 179 | $(E) " LN " $@ 180 | $(Q) $(LN) -sf $^ $@ 181 | 182 | $(LIBCT).so: src/$(LIBCT).so 183 | $(E) " LN " $@ 184 | $(Q) $(LN) -sf $^ $@ 185 | 186 | libselinux.a: src/$(LIBCT).a 187 | $(Q) $(LN) -sf $^ $@ 188 | 189 | libapparmor.a: src/$(LIBCT).a 190 | $(Q) $(LN) -sf $^ $@ 191 | 192 | src/$(LIBCT).so: src/$(LIBCT).a 193 | 194 | all: $(LIBCT).so $(LIBCT).a $(FAKE_LIBS) 195 | @true 196 | 197 | test-build: 198 | $(Q) $(MAKE) -C test all 199 | 200 | test: test-build 201 | $(Q) $(MAKE) -C test run-local 202 | 203 | docs: 204 | $(Q) $(MAKE) -s -C Documentation all 205 | 206 | docs-pdf: 207 | $(Q) $(MAKE) -s -C Documentation all-pdfs 208 | 209 | tags: 210 | $(E) " GEN " $@ 211 | $(Q) $(RM) tags 212 | $(Q) $(FIND) -L . \( -name '*.[hcS]' -o -name '*.go' \) ! -path './.*' -print | xargs ctags -a 213 | 214 | clean: 215 | $(Q) $(MAKE) $(build)=src clean 216 | $(Q) $(MAKE) -C test clean 217 | $(Q) $(MAKE) -s -C Documentation clean 218 | $(Q) $(RM) $(LIBCT).so $(LIBCT).a 219 | $(Q) $(RM) $(CONFIG) 220 | $(Q) $(RM) $(VERSION_HEADER) 221 | $(Q) $(RM) libapparmor.a libselinux.a 222 | 223 | install: 224 | $(E) " INSTALL " 225 | $(Q) install -D -m 755 $(LIBCT).so \ 226 | $(DESTDIR)$(LIBDIR)/$(LIBCT).so.$(VERSION_SO_MAJOR).$(VERSION_SO_MINOR) 227 | $(Q) ln -fns $(LIBCT).so.$(VERSION_SO_MAJOR).$(VERSION_SO_MINOR) \ 228 | $(DESTDIR)$(LIBDIR)/$(LIBCT).so.$(VERSION_SO_MAJOR) 229 | $(Q) ln -fns $(LIBCT).so.$(VERSION_SO_MAJOR).$(VERSION_SO_MINOR) \ 230 | $(DESTDIR)$(LIBDIR)/$(LIBCT).so 231 | $(Q) mkdir -p $(DESTDIR)$(INCLUDEDIR) 232 | $(Q) install -m 644 $(LIBCT-INC) $(DESTDIR)$(INCLUDEDIR) 233 | 234 | 235 | .DEFAULT_GOAL := all 236 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/xemul/libct.svg)](https://travis-ci.org/xemul/libct) 2 | 3 | LIBCT 4 | ===== 5 | 6 | Libct is a containers management library which provides convenient API for 7 | frontend programs to rule a container during its whole lifetime. 8 | 9 | The library operates on two entities: 10 | 11 | * session -- everyone willing to work with container must first open a 12 | session. Currently there is only one type of session -- local, when all 13 | containers are created as child tasks of the caller using namespaces, 14 | cgroups etc.; 15 | 16 | * container -- a container. By default container is "empty", when started 17 | it is merely a fork()-ed process. Container can be equipped with various 18 | things, e.g. 19 | 20 | - Namespaces. Libct accepts clone mask with which container is started 21 | 22 | - Controllers. One may configure all existing CGroup controllers inside 23 | which container will be started. 24 | 25 | - Root on a filesystem. This is a directory into which container will 26 | be chroot()-ed (or pivot_root()-ed if mount namespace is used). 27 | 28 | - Private area. This is where the files for container are. Currently 29 | only one type is supported -- a directory that will be bind-mounted 30 | into root. 31 | 32 | - Network. Caller may assign host NIC of veth pair's end to container 33 | on start. 34 | 35 | 36 | For more details, see [Documentation/libct.txt](Documentation/libct.txt). 37 | For usage examples, see [test](test/) directory. 38 | All the API calls, types and constants are collected in 39 | [src/include/uapi/libct.h](src/include/uapi/libct.h). 40 | 41 | ## Compiling 42 | 43 | Currently, libct depends on libnl which is configured as a git submodule. 44 | So, before trying to compile libct for the first time you need to prepare 45 | libnl first: 46 | 47 | git submodule update --init --recursive 48 | (cd .shipped/libnl/ && ./autogen.sh && ./configure && make) 49 | 50 | Once it is done, please compile as usual, i.e. 51 | 52 | make 53 | -------------------------------------------------------------------------------- /go/Makefile: -------------------------------------------------------------------------------- 1 | CGO_LDFLAGS := -l:libct.a -l:libnl-route-3.a -l:libnl-3.a -l:libapparmor.a -l:libselinux.a -l:libdbus-1.a -lm 2 | LIBRARY_PATH := ../.shipped/libnl/lib/.libs:.. 3 | export CGO_LDFLAGS LIBRARY_PATH 4 | 5 | test: 6 | go test -v . 7 | -------------------------------------------------------------------------------- /go/cmd.go: -------------------------------------------------------------------------------- 1 | package libct 2 | 3 | // #include 4 | import "C" 5 | 6 | import "unsafe" 7 | 8 | func __allocCmd(c *Command, toFree [][]*C.char) (*C.struct_libct_cmd, [][]*C.char) { 9 | var cmd *C.struct_libct_cmd 10 | 11 | cmd = (*C.struct_libct_cmd)(C.malloc(C.size_t(unsafe.Sizeof(*cmd)))) 12 | 13 | if len(c.Dir) != 0 { 14 | cmd.dir = C.CString(c.Dir) 15 | } else { 16 | cmd.dir = nil 17 | } 18 | cmd.path = C.CString(c.Path) 19 | cmd.next = nil 20 | 21 | cargv := make([]*C.char, len(c.Args)+1) 22 | toFree = append(toFree, cargv) 23 | 24 | for i, arg := range c.Args { 25 | cargv[i] = C.CString(arg) 26 | } 27 | cmd.argv = &cargv[0] 28 | 29 | var penv **C.char 30 | if c.Env == nil { 31 | penv = nil 32 | } else { 33 | cenv := make([]*C.char, len(c.Env)+1) 34 | toFree = append(toFree, cenv) 35 | 36 | for i, e := range c.Env { 37 | cenv[i] = C.CString(e) 38 | } 39 | penv = &cenv[0] 40 | } 41 | cmd.envp = penv 42 | 43 | return cmd, toFree 44 | } 45 | 46 | func allocCmd(cmds []Command) (*C.struct_libct_cmd, [][]*C.char) { 47 | var start, prev *C.struct_libct_cmd 48 | var toFree [][]*C.char 49 | 50 | start = nil 51 | for i := range cmds { 52 | var cmd *C.struct_libct_cmd 53 | c := &cmds[i] 54 | cmd, toFree = __allocCmd(c, toFree) 55 | 56 | if start == nil { 57 | start = cmd 58 | } else { 59 | prev.next = cmd 60 | } 61 | prev = cmd 62 | } 63 | 64 | return start, toFree 65 | } 66 | 67 | func freeCmd(cmd *C.struct_libct_cmd, toFree [][]*C.char) { 68 | freeStrings := func(array []*C.char) { 69 | for _, item := range array { 70 | if item != nil { 71 | C.free(unsafe.Pointer(item)) 72 | } 73 | } 74 | } 75 | 76 | for _, f := range toFree { 77 | freeStrings(f) 78 | } 79 | 80 | for cmd != nil { 81 | next := cmd.next 82 | 83 | defer C.free(unsafe.Pointer(cmd.dir)) 84 | defer C.free(unsafe.Pointer(cmd.path)) 85 | defer C.free(unsafe.Pointer(cmd)) 86 | 87 | cmd = next 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /go/libct_test.go: -------------------------------------------------------------------------------- 1 | package libct 2 | 3 | import "testing" 4 | import "syscall" 5 | import "os" 6 | 7 | func init() { 8 | LogInit(os.Stderr, LOG_MSG) 9 | } 10 | 11 | func TestSpawnExecv(t *testing.T) { 12 | s := &Session{} 13 | 14 | err := s.OpenLocal() 15 | if err != nil { 16 | t.Fatal(err) 17 | } 18 | 19 | p, err := s.ProcessCreateDesc() 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | ct, err := s.ContainerCreate("test") 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | preCmds := []Command{ 30 | {Path: "touch", Args: []string{"touch", "/tmp/hello"}}, 31 | {Path: "touch", Args: []string{"touch", "/tmp/hello2"}}, 32 | } 33 | postCmds := []Command{ 34 | {Path: "touch", Args: []string{"touch", "/tmp/Hello"}}, 35 | {Path: "touch", Args: []string{"touch", "/tmp/Hello2"}}, 36 | } 37 | ct.AddMount("", "/tmp", 0, "tmpfs", "", preCmds, postCmds) 38 | 39 | ct.SetNsMask(syscall.CLONE_NEWNS | syscall.CLONE_NEWPID) 40 | if err = p.SetEnv([]string{"PATH=/bin:/usr/bin"}); err != nil { 41 | t.Fatal(err) 42 | } 43 | 44 | err = ct.SpawnExecve(p, "sh", 45 | []string{"sh", "-c", "env | grep -q TEST_LIBCT=test_libct"}, 46 | []string{"TEST_LIBCT=test_libct"}) 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | status, err := p.Wait() 51 | ct.Wait() 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | if !status.Success() { 56 | t.Fatal(status.String()) 57 | } 58 | } 59 | 60 | func TestSpawnExecvStdout(t *testing.T) { 61 | s := &Session{} 62 | 63 | err := s.OpenLocal() 64 | if err != nil { 65 | t.Fatal(err) 66 | } 67 | 68 | p, err := s.ProcessCreateDesc() 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | 73 | pr, pw, err := os.Pipe() 74 | ir, iw, err := os.Pipe() 75 | er, ew, err := os.Pipe() 76 | tr, tw, err := os.Pipe() 77 | 78 | p.Stdout = pw 79 | p.Stdin = ir 80 | p.Stderr = ew 81 | p.ExtraFiles = append(p.ExtraFiles, tr) 82 | 83 | ct, err := s.ContainerCreate("test") 84 | if err != nil { 85 | t.Fatal(err) 86 | } 87 | 88 | if err = ct.AddController(CTL_CPU); err != nil { 89 | t.Fatal(err) 90 | } 91 | if err = ct.AddController(CTL_MEMORY); err != nil { 92 | t.Fatal(err) 93 | } 94 | if err = p.SetEnv([]string{"TEST_LIBCT=hello", "PATH=/bin:/usr/bin"}); err != nil { 95 | t.Fatal(err) 96 | } 97 | err = ct.SpawnExecve(p, "sh", 98 | []string{"sh", "-c", "echo ok; cat; cat <&3 >&2; env | grep -q TEST_LIBCT"}, 99 | nil) 100 | defer ct.Wait() 101 | 102 | val, err := ct.ReadController(CTL_MEMORY, "memory.usage_in_bytes") 103 | if err != nil { 104 | t.Fatal(err) 105 | } 106 | t.Log(val) 107 | 108 | pw.Close() 109 | ir.Close() 110 | tr.Close() 111 | ew.Close() 112 | defer pr.Close() 113 | defer iw.Close() 114 | defer tw.Close() 115 | defer er.Close() 116 | 117 | if err != nil { 118 | t.Fatal(err) 119 | } 120 | 121 | procs, err := ct.Processes() 122 | if err != nil { 123 | t.Fatal(err) 124 | } 125 | 126 | if len(procs) > 2 { 127 | t.Fatal(procs) 128 | } 129 | 130 | iw.WriteString("iok") 131 | iw.Close() 132 | tw.WriteString("good") 133 | tw.Close() 134 | 135 | status, err := p.Wait() 136 | if err != nil { 137 | t.Fatal(status) 138 | } 139 | ct.Wait() 140 | 141 | data := make([]byte, 1024) 142 | count, err := er.Read(data) 143 | if count != 4 { 144 | t.Fatal(count, string(data), data) 145 | } 146 | count, err = pr.Read(data) 147 | if count != 6 { 148 | t.Fatal(count, string(data), data) 149 | } 150 | if !status.Success() { 151 | t.Fatal(status.String()) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /go/process.go: -------------------------------------------------------------------------------- 1 | /* A part of this code was copied from the golang sources src/os/exec/exec.go */ 2 | 3 | package libct 4 | 5 | // #cgo CFLAGS: -DCONFIG_X86_64 -DARCH="x86" -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE 6 | // #include "../src/include/uapi/libct.h" 7 | // #include "../src/include/uapi/libct-errors.h" 8 | // #include 9 | import "C" 10 | import "os" 11 | import "io" 12 | import "syscall" 13 | import "unsafe" 14 | 15 | type ProcessDesc struct { 16 | desc C.ct_process_desc_t 17 | handle C.ct_process_t 18 | 19 | // Stdin specifies the process's standard input. If Stdin is 20 | // nil, the process reads from the null device (os.DevNull). 21 | Stdin io.Reader 22 | 23 | // Stdout and Stderr specify the process's standard output and error. 24 | // 25 | // If either is nil, Run connects the corresponding file descriptor 26 | // to the null device (os.DevNull). 27 | // 28 | // If Stdout and Stderr are the same writer, at most one 29 | // goroutine at a time will call Write. 30 | Stdout io.Writer 31 | Stderr io.Writer 32 | 33 | // ExtraFiles specifies additional open files to be inherited by the 34 | // new process. It does not include standard input, standard output, or 35 | // standard error. If non-nil, entry i becomes file descriptor 3+i. 36 | ExtraFiles []file 37 | 38 | childFiles []file 39 | closeAfterStart []io.Closer 40 | closeAfterWait []io.Closer 41 | goroutine []func() error 42 | 43 | errch chan error // one send per goroutine 44 | } 45 | 46 | // interfaceEqual protects against panics from doing equality tests on 47 | // two interfaces with non-comparable underlying types. 48 | func interfaceEqual(a, b interface{}) bool { 49 | defer func() { 50 | recover() 51 | }() 52 | return a == b 53 | } 54 | 55 | func (p *ProcessDesc) writerDescriptor(w io.Writer) (f *os.File, err error) { 56 | if w == nil { 57 | f, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0) 58 | if err != nil { 59 | return 60 | } 61 | p.closeAfterStart = append(p.closeAfterStart, f) 62 | return 63 | } 64 | 65 | if f, ok := w.(*os.File); ok { 66 | return f, nil 67 | } 68 | 69 | pr, pw, err := os.Pipe() 70 | if err != nil { 71 | return 72 | } 73 | 74 | p.closeAfterStart = append(p.closeAfterStart, pw) 75 | p.closeAfterWait = append(p.closeAfterWait, pr) 76 | p.goroutine = append(p.goroutine, func() error { 77 | _, err := io.Copy(w, pr) 78 | return err 79 | }) 80 | return pw, nil 81 | } 82 | 83 | func (p *ProcessDesc) stdout() (f file, err error) { 84 | if f, ok := p.Stdout.(console); ok { 85 | return f, nil 86 | } 87 | return p.writerDescriptor(p.Stdout) 88 | } 89 | 90 | func (p *ProcessDesc) stderr() (f file, err error) { 91 | if f, ok := p.Stderr.(console); ok { 92 | return f, nil 93 | } 94 | if p.Stderr != nil && interfaceEqual(p.Stderr, p.Stdout) { 95 | return p.childFiles[1], nil 96 | } 97 | return p.writerDescriptor(p.Stderr) 98 | } 99 | 100 | func (c *ProcessDesc) stdin() (f file, err error) { 101 | if c.Stdin == nil { 102 | f, err = os.Open(os.DevNull) 103 | if err != nil { 104 | return 105 | } 106 | c.closeAfterStart = append(c.closeAfterStart, f) 107 | return 108 | } 109 | 110 | if f, ok := c.Stdin.(console); ok { 111 | return f, nil 112 | } 113 | if f, ok := c.Stdin.(*os.File); ok { 114 | return f, nil 115 | } 116 | 117 | pr, pw, err := os.Pipe() 118 | if err != nil { 119 | return 120 | } 121 | 122 | c.closeAfterStart = append(c.closeAfterStart, pr) 123 | c.closeAfterWait = append(c.closeAfterWait, pw) 124 | c.goroutine = append(c.goroutine, func() error { 125 | _, err := io.Copy(pw, c.Stdin) 126 | if err1 := pw.Close(); err == nil { 127 | err = err1 128 | } 129 | return err 130 | }) 131 | return pr, nil 132 | } 133 | 134 | func (p *ProcessDesc) closeDescriptors(closers []io.Closer) { 135 | for _, fd := range closers { 136 | fd.Close() 137 | } 138 | } 139 | 140 | func (p *ProcessDesc) SetCaps(mask uint64, apply_to int) error { 141 | ret := C.libct_process_desc_set_caps(p.desc, C.ulong(mask), C.uint(apply_to)) 142 | if ret != 0 { 143 | return LibctError{int(ret)} 144 | } 145 | 146 | return nil 147 | } 148 | 149 | func (p *ProcessDesc) SetUid(uid int) error { 150 | ret := C.libct_process_desc_setuid(p.desc, C.uint(uid)) 151 | if ret != 0 { 152 | return LibctError{int(ret)} 153 | } 154 | 155 | return nil 156 | } 157 | 158 | func (p *ProcessDesc) SetGid(gid int) error { 159 | ret := C.libct_process_desc_setgid(p.desc, C.uint(gid)) 160 | if ret != 0 { 161 | return LibctError{int(ret)} 162 | } 163 | 164 | return nil 165 | } 166 | 167 | func (p *ProcessDesc) SetUser(user string) error { 168 | cuser := C.CString(user) 169 | defer C.free(unsafe.Pointer(cuser)) 170 | ret := C.libct_process_desc_set_user(p.desc, cuser) 171 | if ret != 0 { 172 | return LibctError{int(ret)} 173 | } 174 | 175 | return nil 176 | } 177 | 178 | func (p *ProcessDesc) SetParentDeathSignal(sig syscall.Signal) error { 179 | if ret := C.libct_process_desc_set_pdeathsig(p.desc, C.int(sig)); ret != 0 { 180 | return LibctError{int(ret)} 181 | } 182 | 183 | return nil 184 | } 185 | 186 | func (p *ProcessDesc) SetLSMLabel(label string) error { 187 | clabel := C.CString(label) 188 | defer C.free(unsafe.Pointer(clabel)) 189 | 190 | if ret := C.libct_process_desc_set_lsm_label(p.desc, clabel); ret != 0 { 191 | return LibctError{int(ret)} 192 | } 193 | 194 | return nil 195 | } 196 | 197 | func (p *ProcessDesc) Wait() (*os.ProcessState, error) { 198 | 199 | pid, err := p.GetPid() 200 | if err != nil { 201 | return nil, err 202 | } 203 | 204 | process, err := os.FindProcess(pid) 205 | if err != nil { 206 | return nil, err 207 | } 208 | 209 | ps, err := process.Wait() 210 | if err != nil { 211 | return nil, err 212 | } 213 | 214 | var copyError error 215 | for _ = range p.goroutine { 216 | if err := <-p.errch; err != nil && copyError == nil { 217 | copyError = err 218 | } 219 | } 220 | 221 | p.closeDescriptors(p.closeAfterWait) 222 | 223 | return ps, nil 224 | } 225 | 226 | func (p *ProcessDesc) SetEnv(env []string) error { 227 | cenv := make([]*C.char, len(env)) 228 | for i, v := range env { 229 | cenv[i] = C.CString(v) 230 | } 231 | 232 | ret := C.libct_process_desc_set_env(p.desc, &cenv[0], C.int(len(env))) 233 | 234 | for i := range cenv { 235 | C.free(unsafe.Pointer(cenv[i])) 236 | } 237 | if ret < 0 { 238 | return LibctError{int(ret)} 239 | } 240 | return nil 241 | } 242 | 243 | func (p *ProcessDesc) SetRlimit(resource int, soft uint64, hard uint64) error { 244 | ret := C.libct_process_desc_set_rlimit(p.desc, C.int(resource), C.uint64_t(soft), C.uint64_t(hard)) 245 | if ret < 0 { 246 | return LibctError{int(ret)} 247 | } 248 | return nil 249 | } 250 | 251 | func (p *ProcessDesc) GetPid() (int, error) { 252 | 253 | ret := C.libct_process_get_pid(p.handle) 254 | if ret < 0 { 255 | return -1, LibctError{int(ret)} 256 | } 257 | 258 | return int(ret), nil 259 | } 260 | -------------------------------------------------------------------------------- /py/setup.py: -------------------------------------------------------------------------------- 1 | # setup.py: setup script 2 | # 3 | # Copyright (C) 2014 Parallels, Inc. 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library. If not, see 17 | # . 18 | 19 | from distutils.core import setup, Extension 20 | 21 | module1 = Extension('libct.libctcapi', 22 | sources=['libct/libctmodule.c'], 23 | include_dirs=['../src/include/uapi'], 24 | library_dirs=['..', '../.shipped/libnl/lib/.libs/'], 25 | libraries=['ct', 'nl-route-3', 'nl-3', 'dbus-1']) 26 | 27 | setup(name='libct', 28 | version='0.1.0', 29 | description='A containers management library.', 30 | author='Dmitry Guryanov', 31 | author_email='dguryanov@parallels.com', 32 | packages=["libct"], 33 | ext_modules=[module1]) 34 | -------------------------------------------------------------------------------- /scripts/Makefile.build: -------------------------------------------------------------------------------- 1 | ## 2 | ## General helpers for simplified Makefiles. 3 | ## 4 | MAKEFLAGS := -r -R --no-print-directory 5 | 6 | targets := 7 | deps := 8 | deps-after := 9 | all-objs := 10 | incdeps := 11 | _all := 12 | _cleanup-y := 13 | 14 | include scripts/Makefile.rules 15 | include $(obj)/$(makefile) 16 | 17 | ## 18 | ## Append targets to be auto-cleanuped 19 | define add-cleanup-obj-c-by-name 20 | _cleanup-y+= $(1).o 21 | _cleanup-y+= $(1).i 22 | _cleanup-y+= $(1).d 23 | _cleanup-y+= $(1).s 24 | endef 25 | 26 | define add-cleanup-obj-S-by-name 27 | _cleanup-y+= $(1).o 28 | _cleanup-y+= $(1).d 29 | _cleanup-y+= $(1).i 30 | endef 31 | 32 | ## 33 | ## 34 | ## Generate a bundle of rules for C files 35 | define gen-target-c-bundle 36 | $(eval $(call gen-rule-o-from-c-by-name,$(1),$(2),$(3))) 37 | $(eval $(call gen-rule-i-from-c-by-name,$(1),$(2),$(3))) 38 | $(eval $(call gen-rule-d-from-c-by-name,$(1),$(2),$(3))) 39 | $(eval $(call gen-rule-s-from-c-by-name,$(1),$(2),$(3))) 40 | $(eval $(call add-cleanup-obj-c-by-name,$(1))) 41 | endef 42 | 43 | ## 44 | ## 45 | ## Generate a bundle of rules for S files 46 | define gen-target-S-bundle 47 | $(eval $(call gen-rule-o-from-S-by-name,$(1),$(2),$(3))) 48 | $(eval $(call gen-rule-d-from-S-by-name,$(1),$(2),$(3))) 49 | $(eval $(call gen-rule-i-from-S-by-name,$(1),$(2),$(3))) 50 | $(eval $(call add-cleanup-obj-S-by-name,$(1))) 51 | endef 52 | 53 | ## 54 | ## 55 | ## Shared or standalone targets 56 | ifneq ($(obj-y),) 57 | obj-y := $(addprefix $(obj)/, $(obj-y)) 58 | $(foreach file, \ 59 | $(obj-y), \ 60 | $(eval \ 61 | $(call gen-target-c-bundle, \ 62 | $(file:.o=),$(file:.o=)))) 63 | all-objs += $(obj-y) 64 | deps += $(obj-y:.o=.d) 65 | endif 66 | 67 | ifneq ($(obj-e),) 68 | $(foreach file, \ 69 | $(obj-e), \ 70 | $(eval \ 71 | $(call gen-target-c-bundle, \ 72 | $(file:.o=),$(file:.o=)))) 73 | all-objs += $(obj-e) 74 | deps += $(obj-e:.o=.d) 75 | endif 76 | 77 | ifneq ($(asm-y),) 78 | asm-y := $(addprefix $(obj)/, $(asm-y)) 79 | $(foreach file, \ 80 | $(asm-y), \ 81 | $(eval \ 82 | $(call gen-target-S-bundle, \ 83 | $(file:.o=),$(file:.o=)))) 84 | all-objs += $(asm-y) 85 | deps += $(asm-y:.o=.d) 86 | endif 87 | 88 | ifneq ($(asm-e),) 89 | $(foreach file, \ 90 | $(asm-e), \ 91 | $(eval \ 92 | $(call gen-target-S-bundle, \ 93 | $(file:.o=),$(file:.o=)))) 94 | all-objs += $(asm-e) 95 | deps += $(asm-e:.o=.d) 96 | endif 97 | 98 | ## 99 | ## 100 | ## Standalone files where sources are kept in external 101 | ## directories. Usually needed when same source files 102 | ## are compiled with different flags. 103 | ifneq ($(obj-ext-src-y),) 104 | __obj-ext-src-y := $(addprefix $(obj)/, $(notdir $(obj-ext-src-y))) 105 | $(foreach file, \ 106 | $(obj-ext-src-y), \ 107 | $(eval \ 108 | $(call gen-target-c-bundle, \ 109 | $(file:.o=), \ 110 | $(addprefix $(obj)/,$(notdir $(file:.o=)))))) 111 | all-objs += $(__obj-ext-src-y) 112 | deps += $(__obj-ext-src-y:.o=.d) 113 | endif 114 | 115 | ## 116 | ## 117 | ## Generate rules for a target 118 | define gen-target-rules 119 | 120 | $(1)-all-objs := 121 | 122 | ifneq ($($(1)-obj-y),) 123 | $(foreach file, \ 124 | $($(1)-obj-y), \ 125 | $(eval \ 126 | $(call gen-target-c-bundle, \ 127 | $(obj)/$(file:.o=), \ 128 | $(obj)/$(file:.o=), \ 129 | $($(1)-obj-y-cflags)))) 130 | $(1)-all-objs += $$(addprefix $(obj)/, $($(1)-obj-y)) 131 | deps += $$(addprefix $(obj)/, $($(1)-obj-y:.o=.d)) 132 | endif 133 | 134 | ifneq ($($(1)-obj-e),) 135 | $(foreach file, \ 136 | $($(1)-obj-e), \ 137 | $(eval \ 138 | $(call gen-target-c-bundle, \ 139 | $(file:.o=), \ 140 | $(file:.o=), \ 141 | $($(1)-obj-e-cflags)))) 142 | $(1)-all-objs += $$($(1)-obj-e) 143 | deps += $$($(1)-obj-e:.o=.d) 144 | endif 145 | 146 | ifneq ($($(1)-asm-y),) 147 | $(foreach file, \ 148 | $($(1)-asm-y), \ 149 | $(eval \ 150 | $(call gen-target-S-bundle, \ 151 | $(obj)/$(file:.o=), \ 152 | $(obj)/$(file:.o=), \ 153 | $($(1)-asm-y-asmflags)))) 154 | $(1)-all-objs += $$(addprefix $(obj)/, $($(1)-asm-y)) 155 | deps += $$($(1)-asm-y:.o=.d) 156 | endif 157 | 158 | ifneq ($($(1)-asm-e),) 159 | $(foreach file, \ 160 | $($(1)-asm-e), \ 161 | $(eval \ 162 | $(call gen-target-S-bundle, \ 163 | $(file:.o=), \ 164 | $(file:.o=), \ 165 | $($(1)-asm-e-asmflags)))) 166 | $(1)-all-objs += $$($(1)-asm-e) 167 | deps += $$($(1)-asm-e:.o=.d) 168 | endif 169 | 170 | $(1)-all-objs += $(all-objs) 171 | 172 | $$(obj)/$(1).built-in.o: $$($(1)-all-objs) $$($(1)-libs-e) $(libs-e) 173 | $$(E) " LINK " $$@ 174 | $$(Q) $$(LD) $$(LDFLAGS) -r -o $$@ $$^ 175 | 176 | _all += $$(obj)/$(1).built-in.o 177 | cleanup-y += $$(obj)/$(1).built-in.o 178 | endef 179 | 180 | ## 181 | ## 182 | ## Walk over all targets and generate rules they require 183 | $(foreach target, \ 184 | $(targets), \ 185 | $(eval \ 186 | $(call gen-target-rules,$(target)))) 187 | 188 | ## 189 | ## 190 | ## No targets -- just builtin default one 191 | ifeq ($(targets),) 192 | ifneq ($(all-objs),) 193 | $(obj)/built-in.o: $(all-objs) $(libs-e) 194 | $(E) " LINK " $@ 195 | $(Q) $(LD) $(LDFLAGS) -r -o $@ $^ 196 | 197 | _all += $(obj)/built-in.o 198 | cleanup-y += $(obj)/built-in.o 199 | endif 200 | endif 201 | 202 | ## 203 | ## A rule for building library. 204 | ifneq ($(lib-so),) 205 | $(obj)/$(lib-so).so: $(all-objs) $(libs-e) 206 | $(E) " LINK " $@ 207 | $(Q) $(CC) -shared $(cflags-so) -o $@ $^ 208 | 209 | _all += $(obj)/$(lib-so).so 210 | cleanup-y += $(obj)/$(lib-so).so 211 | endif 212 | 213 | ## 214 | ## A rule for building library. 215 | ifneq ($(lib-a),) 216 | $(obj)/$(lib-a).a: $(all-objs) $(libs-e) 217 | $(E) " LINK " $@ 218 | $(Q) ar -cr -o $@ $^ 219 | 220 | _all += $(obj)/$(lib-a).a 221 | cleanup-y += $(obj)/$(lib-a).a 222 | endif 223 | 224 | ## 225 | ## 226 | ## Include deps if requested 227 | ifneq ($(incdeps),) 228 | ifneq ($(deps-after),) 229 | $(deps): | $(deps-after) 230 | endif 231 | -include $(deps) 232 | endif 233 | 234 | ## 235 | ## 236 | ## Autocomplete cleanups 237 | cleanup-y += $(_cleanup-y) 238 | 239 | ## 240 | ## Predefined .PHONY targets 241 | .PHONY: all clean 242 | 243 | all: $(_all) 244 | @echo > /dev/null 245 | 246 | clean: 247 | $(E) " CLEANUP " $(obj) 248 | $(Q) $(RM) $(cleanup-y) 249 | -------------------------------------------------------------------------------- /scripts/Makefile.config: -------------------------------------------------------------------------------- 1 | include scripts/utilities.mak 2 | include scripts/feature-tests.mak 3 | 4 | CONFIG := src/include/config.h 5 | 6 | ifeq ($(call try-cc,$(LIBSELINUX_DEV_TEST),-l:libselinux.a),y) 7 | DEFINES += -DCONFIG_SELINUX 8 | CONFIG_SELINUX := y 9 | endif 10 | 11 | ifeq ($(call try-cc,$(LIBAPPARMOR_DEV_TEST),-l:libselinux.a),y) 12 | DEFINES += -DCONFIG_APPARMOR 13 | CONFIG_APPARMOR := y 14 | endif 15 | 16 | $(CONFIG): scripts/utilities.mak scripts/feature-tests.mak 17 | $(E) " GEN " $@ 18 | $(Q) @echo '#ifndef __LIBCT_CONFIG_H__' > $@ 19 | $(Q) @echo '#define __LIBCT_CONFIG_H__' >> $@ 20 | $(Q) @echo '#endif /* __LIBCT_CONFIG_H__ */' >> $@ 21 | 22 | config: $(CONFIG) 23 | 24 | .PHONY: config 25 | -------------------------------------------------------------------------------- /scripts/Makefile.rules: -------------------------------------------------------------------------------- 1 | ## 2 | ## 3 | ## These are per-file generators. 4 | ## 5 | define gen-rule-o-from-c-by-name 6 | $(2).o: $(1).c 7 | $$(E) " CC " $$@ 8 | $$(Q) $$(CC) -c $$(CFLAGS) $$(cflags-y) $(3) $$< -o $$@ 9 | endef 10 | 11 | define gen-rule-i-from-c-by-name 12 | $(2).i: $(1).c 13 | $$(E) " CC " $$@ 14 | $$(Q) $$(CC) -E $$(CFLAGS) $$(cflags-y) $(3) $$< -o $$@ 15 | endef 16 | 17 | define gen-rule-s-from-c-by-name 18 | $(2).s: $(1).c 19 | $$(E) " CC " $$@ 20 | $$(Q) $$(CC) -S $$(CFLAGS) $$(cflags-y) $(3) -fverbose-asm $$< -o $$@ 21 | endef 22 | 23 | define gen-rule-o-from-S-by-name 24 | $(2).o: $(1).S 25 | $$(E) " CC " $$@ 26 | $$(Q) $$(CC) -c $$(CFLAGS) $$(cflags-y) $(3) $$(ASMFLAGS) $(4) $$< -o $$@ 27 | endef 28 | 29 | define gen-rule-d-from-c-by-name 30 | $(2).d: $(1).c 31 | $$(E) " DEP " $$@ 32 | $$(Q) $$(CC) -M -MT $$@ -MT $$(patsubst %.d,%.o,$$@) $$(CFLAGS) $$(cflags-y) $(3) $$< -o $$@ 33 | endef 34 | 35 | define gen-rule-d-from-S-by-name 36 | $(2).d: $(1).S 37 | $$(E) " DEP " $$@ 38 | $$(Q) $$(CC) -M -MT $$@ -MT $$(patsubst %.d,%.o,$$@) $$(CFLAGS) $$(cflags-y) $(3) $$< -o $$@ 39 | endef 40 | 41 | define gen-rule-i-from-S-by-name 42 | $(2).i: $(1).S 43 | $$(E) " CC " $$@ 44 | $$(Q) $$(CC) -E $$(CFLAGS) $$(cflags-y) $(3) $$< -o $$@ 45 | endef 46 | 47 | ## 48 | ## In case if someone add last resort rule 49 | ## together with .SUFFIXES not cleaned, this 50 | ## will slow down the build procedure 51 | scripts/Makefile.rules:: 52 | @echo > /dev/null 53 | -------------------------------------------------------------------------------- /scripts/Makefile.version: -------------------------------------------------------------------------------- 1 | VERSION := $(VERSION_MAJOR)$(if $(VERSION_MINOR),.$(VERSION_MINOR))$(if $(VERSION_SUBLEVEL),.$(VERSION_SUBLEVEL)) 2 | 3 | VERSION_HEADER := src/include/version.h 4 | GITID := $(shell if [ -d ".git" ]; then git reflog -n 1 | cut -f1 -d' '; fi) 5 | ifeq ($(GITID),) 6 | GITID := 0 7 | endif 8 | 9 | VERSION_CODE := $(shell expr $(VERSION_MAJOR) \* 65536 \+ $(VERSION_MINOR) \* 256 \+ $(VERSION_SUBLEVEL)) 10 | 11 | $(VERSION_HEADER): Makefile scripts/Makefile.version 12 | $(E) " GEN " $@ 13 | $(Q) echo "/* Autogenerated, do not edit */" > $(VERSION_HEADER) 14 | $(Q) echo "#ifndef __LIBCT_VERSION_H__" >> $(VERSION_HEADER) 15 | $(Q) echo "#define __LIBCT_VERSION_H__" >> $(VERSION_HEADER) 16 | $(Q) echo "#define LIBCT_VERSION \"$(VERSION)\"" >> $(VERSION_HEADER) 17 | $(Q) echo "#define LIBCT_VERSION_MAJOR " $(VERSION_MAJOR) >> $(VERSION_HEADER) 18 | $(Q) echo "#define LIBCT_VERSION_MINOR " $(VERSION_MINOR) >> $(VERSION_HEADER) 19 | $(Q) echo "#define LIBCT_VERSION_SUBLEVEL " $(VERSION_SUBLEVEL) >> $(VERSION_HEADER) 20 | $(Q) echo "#define LIBCT_VERSION_CODE " $(VERSION_CODE) >> $(VERSION_HEADER) 21 | $(Q) echo "#define LIBCT_GITID \"$(GITID)\"" >> $(VERSION_HEADER) 22 | $(Q) echo "#define LIBCT_GEN_VERSION(a,b,c) \\" >> $(VERSION_HEADER) 23 | $(Q) echo " (((a) << 16) + ((b) << 8) + (c))" >> $(VERSION_HEADER) 24 | $(Q) echo "#endif /* __LIBCT_VERSION_H__ */" >> $(VERSION_HEADER) 25 | 26 | ## 27 | ## In case if someone add last resort rule 28 | ## together with .SUFFIXES not cleaned, this 29 | ## will slow down the build procedure 30 | scripts/Makefile.version:: 31 | @echo > /dev/null 32 | -------------------------------------------------------------------------------- /scripts/feature-tests.mak: -------------------------------------------------------------------------------- 1 | define LIBAPPARMOR_DEV_TEST 2 | 3 | #include 4 | 5 | int main(void) 6 | { 7 | return 0; 8 | } 9 | endef 10 | 11 | define LIBSELINUX_DEV_TEST 12 | 13 | #include 14 | 15 | int main(void) 16 | { 17 | return 0; 18 | } 19 | endef 20 | -------------------------------------------------------------------------------- /scripts/utilities.mak: -------------------------------------------------------------------------------- 1 | # try-cc 2 | # Usage: option = $(call try-cc, source-to-build, cc-options) 3 | try-cc = $(shell sh -c \ 4 | 'TMP="$(OUTPUT)$(TMPOUT).$$$$"; \ 5 | echo "$(1)" | \ 6 | $(CC) -x c - $(2) -o "$$TMP" > /dev/null 2>&1 && echo y; \ 7 | rm -f "$$TMP"') 8 | 9 | # try-build 10 | # Usage: option = $(call try-build, source-to-build, cc-options, link-options) 11 | try-build = $(shell sh -c \ 12 | 'TMP="$(OUTPUT)$(TMPOUT).$$$$"; \ 13 | echo "$(1)" | \ 14 | $(CC) -x c - $(2) $(3) -o "$$TMP" > /dev/null 2>&1 && echo y; \ 15 | rm -f "$$TMP"') 16 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | lib-so += libct 2 | lib-a += libct 3 | 4 | obj-y += libct.o 5 | obj-y += session.o 6 | obj-y += ct.o 7 | obj-y += namespaces.o 8 | obj-y += cgroups.o 9 | obj-y += log.o 10 | obj-y += linux_kernel.o 11 | obj-y += fs.o 12 | obj-y += net.o 13 | obj-y += security.o 14 | obj-y += util.o 15 | obj-y += devnodes.o 16 | obj-y += route.o 17 | obj-y += process.o 18 | obj-y += net_util.o 19 | obj-y += cmd.o 20 | obj-y += vz/vz.o 21 | obj-y += vz/vz_net.o 22 | obj-y += vz/readelf.o 23 | obj-y += lsm/lsm.o 24 | obj-$(CONFIG_APPARMOR) += lsm/apparmor.o 25 | obj-$(CONFIG_SELINUX) += lsm/selinux.o 26 | obj-y += lsm/nop.o 27 | obj-y += systemd.o 28 | 29 | cflags-y += -fPIC -fno-stack-protector 30 | cflags-so += -rdynamic 31 | 32 | .SECONDARY: 33 | 34 | ifneq ($(MAKECMDGOALS),clean) 35 | incdeps := y 36 | endif 37 | -------------------------------------------------------------------------------- /src/arch/x86/include/asm/page.h: -------------------------------------------------------------------------------- 1 | #ifndef __ASM_LIBCT_PAGE_H__ 2 | #define __ASM_LIBCT_PAGE_H__ 3 | 4 | #define PAGE_SHIFT (12) 5 | #define PAGE_SIZE (1 << PAGE_SHIFT) 6 | #define PAGES(len) ((len) >> PAGE_SHIFT) 7 | 8 | #endif /* __ASM_LIBCT_PAGE_H__ */ 9 | -------------------------------------------------------------------------------- /src/arch/x86/include/asm/types.h: -------------------------------------------------------------------------------- 1 | #ifndef __ASM_LIBCT_TYPES_H__ 2 | #define __ASM_LIBCT_TYPES_H__ 3 | 4 | #include "asm-generic/int.h" 5 | 6 | #include "asm/page.h" 7 | 8 | #define TASK_SIZE ((1UL << 47) - PAGE_SIZE) 9 | 10 | #endif /* __ASM_LIBCT_TYPES_H__ */ 11 | -------------------------------------------------------------------------------- /src/cmd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "cmd.h" 6 | #include "xmalloc.h" 7 | 8 | void free_cmd(struct libct_cmd *cmd) 9 | { 10 | struct libct_cmd *next; 11 | int i; 12 | 13 | while (cmd) { 14 | next = cmd->next; 15 | 16 | if (cmd->envp) { 17 | for (i = 0; cmd->envp[i]; i++) 18 | xfree(cmd->envp[i]); 19 | } 20 | for (i = 0; cmd->argv[i]; i++) 21 | xfree(cmd->argv[i]); 22 | 23 | xfree(cmd->envp); 24 | xfree(cmd->argv); 25 | xfree(cmd->dir); 26 | xfree(cmd->path); 27 | 28 | xfree(cmd); 29 | cmd = next; 30 | } 31 | } 32 | 33 | int __exec_cmd(struct libct_cmd *cmd) 34 | { 35 | int status; 36 | pid_t pid; 37 | 38 | pid = fork(); 39 | if (pid < 0) { 40 | pr_perror("Unable to fork"); 41 | return -1; 42 | } 43 | 44 | if (pid == 0) { 45 | if (cmd->dir && chdir(cmd->dir) < 0) { 46 | pr_perror("Unable to change working directory"); 47 | return 1; 48 | } 49 | if (cmd->envp) 50 | execvpe(cmd->path, cmd->argv, cmd->envp); 51 | else 52 | execvp(cmd->path, cmd->argv); 53 | pr_perror("Unable to exec"); 54 | return 1; 55 | } 56 | 57 | if (waitpid(pid, &status, 0) != pid) { 58 | pr_perror("Unable to wait the %d process", pid); 59 | return -1; 60 | } 61 | 62 | if (status != 0) { 63 | pr_perror("The command returned 0x%x", status); 64 | return -1; 65 | } 66 | 67 | return 0; 68 | } 69 | 70 | int exec_cmd(struct libct_cmd *cmd) 71 | { 72 | while (cmd) { 73 | if (__exec_cmd(cmd)) 74 | return -1; 75 | cmd = cmd->next; 76 | } 77 | 78 | return 0; 79 | } 80 | 81 | struct libct_cmd *__alloc_cmd(struct libct_cmd *src) 82 | { 83 | struct libct_cmd *dst; 84 | int i; 85 | 86 | dst = xzalloc(sizeof(*src)); 87 | if (dst == NULL) 88 | return NULL; 89 | 90 | if (src->dir) { 91 | dst->dir = xstrdup(src->dir); 92 | if (!dst->dir) 93 | goto err; 94 | } 95 | dst->path = xstrdup(src->path); 96 | if (!dst->path) 97 | goto err; 98 | 99 | if (src->envp) { 100 | for (i = 0; ; i++) { 101 | char **env; 102 | 103 | env = xrealloc(dst->envp, sizeof(char *) * (i + 1)); 104 | if (!env) 105 | goto err; 106 | 107 | dst->envp = env; 108 | if (src->envp[i] == NULL) { 109 | dst->envp[i] = NULL; 110 | break; 111 | } else { 112 | dst->envp[i] = xstrdup(src->envp[i]); 113 | if (!dst->envp[i]) 114 | goto err; 115 | } 116 | } 117 | } 118 | 119 | for (i = 0; ; i++) { 120 | char **argv; 121 | 122 | argv = xrealloc(dst->argv, sizeof(char *) * (i + 1)); 123 | if (!argv) 124 | goto err; 125 | 126 | dst->argv = argv; 127 | if (src->argv[i] == NULL) { 128 | dst->argv[i] = NULL; 129 | break; 130 | } else { 131 | dst->argv[i] = xstrdup(src->argv[i]); 132 | if (!dst->argv[i]) 133 | goto err; 134 | } 135 | } 136 | 137 | return dst; 138 | err: 139 | free_cmd(dst); 140 | return NULL; 141 | } 142 | 143 | struct libct_cmd *alloc_cmd(struct libct_cmd *src) 144 | { 145 | struct libct_cmd *head = NULL, *prev, *dst; 146 | 147 | while (src) { 148 | dst = __alloc_cmd(src); 149 | if (dst == NULL) 150 | goto err; 151 | 152 | if (head == NULL) 153 | head = dst; 154 | else 155 | prev->next = dst; 156 | 157 | prev = dst; 158 | src = src->next; 159 | } 160 | 161 | return head; 162 | err: 163 | free_cmd(head); 164 | return NULL; 165 | } 166 | -------------------------------------------------------------------------------- /src/devnodes.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "list.h" 7 | #include "uapi/libct.h" 8 | #include "xmalloc.h" 9 | #include "ct.h" 10 | 11 | struct fs_devnode { 12 | struct list_head l; 13 | 14 | char *path; 15 | mode_t mode; 16 | dev_t dev; 17 | }; 18 | 19 | int local_add_devnode(ct_handler_t h, char *path, int mode, int major, int minor) 20 | { 21 | struct container *ct = cth2ct(h); 22 | struct fs_devnode *dev; 23 | 24 | if (ct->state != CT_STOPPED) 25 | /* FIXME -- implement */ 26 | return -LCTERR_BADCTSTATE; 27 | 28 | dev = xmalloc(sizeof(*dev)); 29 | if (dev == NULL) 30 | return -1; 31 | 32 | dev->path = xstrdup(path); 33 | if (dev->path == NULL) { 34 | xfree(dev); 35 | return -1; 36 | } 37 | dev->mode = mode; 38 | dev->dev = makedev(major, minor); 39 | list_add_tail(&dev->l, &ct->fs_devnodes); 40 | 41 | return 0; 42 | } 43 | 44 | int libct_fs_add_devnode(ct_handler_t ct, char *path, int mode, int major, int minor) 45 | { 46 | 47 | if (!path) 48 | return -LCTERR_INVARG; 49 | 50 | return ct->ops->fs_add_devnode(ct, path, mode, major, minor); 51 | } 52 | 53 | int fs_create_devnodes(struct container *ct) 54 | { 55 | struct fs_devnode *d; 56 | 57 | list_for_each_entry(d, &ct->fs_devnodes, l) { 58 | unlink(d->path); 59 | if (mknod(d->path, d->mode, d->dev) == -1) { 60 | pr_perror("Unable to create device"); 61 | return -1; 62 | } 63 | } 64 | 65 | return 0; 66 | } 67 | 68 | void fs_free_devnodes(struct container *ct) 69 | { 70 | struct fs_devnode *d, *dn; 71 | 72 | list_for_each_entry_safe(d, dn, &ct->fs_devnodes, l) { 73 | list_del(&d->l); 74 | xfree(d->path); 75 | xfree(d); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/include/asm-generic/int.h: -------------------------------------------------------------------------------- 1 | #ifndef __UAPI_ASM_LIBCT_GENERIC_INT_H__ 2 | #define __UAPI_ASM_LIBCT_GENERIC_INT_H__ 3 | 4 | #include 5 | 6 | typedef uint64_t u64; 7 | typedef int64_t s64; 8 | typedef unsigned int u32; 9 | typedef signed int s32; 10 | typedef unsigned short u16; 11 | typedef signed short s16; 12 | typedef unsigned char u8; 13 | typedef signed char s8; 14 | 15 | #endif /* __UAPI_ASM_LIBCT_GENERIC_INT_H__ */ 16 | -------------------------------------------------------------------------------- /src/include/bug.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_BUG_H__ 2 | #define __LIBCT_BUG_H__ 3 | 4 | #include 5 | 6 | #include "compiler.h" 7 | #include "log.h" 8 | 9 | #ifndef BUG_ON_HANDLER 10 | 11 | # define __raise() raise(SIGABRT) 12 | 13 | # define BUG_ON_HANDLER(condition) \ 14 | do { \ 15 | if ((condition)) { \ 16 | pr_err("BUG at %s:%d\n", __FILE__, __LINE__); \ 17 | __raise(); \ 18 | *(volatile unsigned long *)NULL = 0xdead0000 + __LINE__; \ 19 | } \ 20 | } while (0) 21 | 22 | #endif /* BUG_ON_HANDLER */ 23 | 24 | #define BUG_ON(condition) BUG_ON_HANDLER((condition)) 25 | #define BUG() BUG_ON(true) 26 | 27 | #endif /* __LIBCT_BUG_H__ */ 28 | -------------------------------------------------------------------------------- /src/include/cgroups.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_CGROUP_H__ 2 | #define __LIBCT_CGROUP_H__ 3 | 4 | #include 5 | 6 | #include "uapi/libct.h" 7 | 8 | #include "list.h" 9 | 10 | #define cbit(ctype) (1 << ctype) 11 | 12 | struct container; 13 | struct mntent; 14 | 15 | struct controller { 16 | struct list_head ct_l; /* links into container->cgroups */ 17 | enum ct_controller ctype; 18 | }; 19 | 20 | struct cg_desc { 21 | char *name; 22 | char *mounted_at; 23 | struct cg_desc *merged; 24 | }; 25 | 26 | int cgroup_add_mount(struct mntent *); 27 | 28 | /* 29 | * Postponed cgroups configuration 30 | */ 31 | 32 | struct cg_config { 33 | enum ct_controller ctype; 34 | char *param; 35 | char *value; 36 | struct list_head l; 37 | }; 38 | 39 | extern struct cg_desc cg_descs[]; 40 | 41 | extern int cgroup_create_one(struct container *ct, struct controller *ctl); 42 | extern int cgroups_create(struct container *ct); 43 | extern int cgroups_attach(struct container *ct, pid_t pid); 44 | extern void cgroups_destroy(struct container *ct); 45 | extern void cgroups_free(struct container *ct); 46 | 47 | extern int local_add_controller(ct_handler_t h, enum ct_controller ctype); 48 | extern int local_config_controller(ct_handler_t h, enum ct_controller ctype, char *param, char *value); 49 | extern int local_read_controller(ct_handler_t h, enum ct_controller ctype, char *param, void *buf, size_t len); 50 | extern int config_controller(struct container *ct, enum ct_controller ctype, char *param, char *value); 51 | 52 | extern int try_mount_cg(struct container *ct); 53 | 54 | extern int cgroups_create_service(void); 55 | extern int add_service_controller(struct container *ct); 56 | extern int service_ctl_killall(struct container *ct); 57 | 58 | extern struct libct_processes *local_controller_tasks(ct_handler_t h); 59 | 60 | extern int cgroup_freezer_set_state(struct container *ct, bool freeze); 61 | 62 | #define DEFAULT_CGROUPS_PATH "/sys/fs/cgroup" 63 | 64 | #endif /* __LIBCT_CGROUP_H__ */ 65 | -------------------------------------------------------------------------------- /src/include/cmd.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_CMD_H__ 2 | #define __LIBCT_CMD_H__ 3 | 4 | #include "uapi/libct.h" 5 | 6 | extern void free_cmd(struct libct_cmd *cmd); 7 | extern struct libct_cmd * alloc_cmd(struct libct_cmd *cmd); 8 | extern int exec_cmd(struct libct_cmd *cmd); 9 | 10 | #endif /* __LIBCT_CMD_H__ */ 11 | -------------------------------------------------------------------------------- /src/include/compiler.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_COMPILER_H__ 2 | #define __LIBCT_COMPILER_H__ 3 | 4 | /* 5 | * Various definitions for success build, 6 | * picked from various places, mostly from 7 | * the linux kernel. 8 | */ 9 | 10 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 11 | #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) 12 | 13 | #define __stringify_1(x...) #x 14 | #define __stringify(x...) __stringify_1(x) 15 | 16 | #define NORETURN __attribute__((__noreturn__)) 17 | #define __packed __attribute__((__packed__)) 18 | #define __used __attribute__((__used__)) 19 | #define __maybe_unused __attribute__((unused)) 20 | #define __always_unused __attribute__((unused)) 21 | 22 | #define __section(S) __attribute__ ((__section__(#S))) 23 | 24 | #ifndef __always_inline 25 | # define __always_inline inline __attribute__((always_inline)) 26 | #endif 27 | 28 | #define likely(x) __builtin_expect(!!(x), 1) 29 | #define unlikely(x) __builtin_expect(!!(x), 0) 30 | 31 | #ifndef always_inline 32 | # define always_inline __always_inline 33 | #endif 34 | 35 | #ifndef noinline 36 | # define noinline __attribute__((noinline)) 37 | #endif 38 | 39 | #define __aligned(x) __attribute__((aligned(x))) 40 | 41 | #ifndef offsetof 42 | # define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 43 | #endif 44 | 45 | #define barrier() asm volatile("" ::: "memory") 46 | 47 | #define container_of(ptr, type, member) ({ \ 48 | const typeof( ((type *)0)->member ) *__mptr = (ptr); \ 49 | (type *)( (char *)__mptr - offsetof(type,member) );}) 50 | 51 | #define __round_mask(x, y) ((__typeof__(x))((y) - 1)) 52 | #define round_up(x, y) ((((x) - 1) | __round_mask(x, y)) + 1) 53 | #define round_down(x, y) ((x) & ~__round_mask(x, y)) 54 | #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) 55 | #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) 56 | 57 | #define min(x, y) ({ \ 58 | typeof(x) _min1 = (x); \ 59 | typeof(y) _min2 = (y); \ 60 | (void) (&_min1 == &_min2); \ 61 | _min1 < _min2 ? _min1 : _min2; }) 62 | 63 | #define max(x, y) ({ \ 64 | typeof(x) _max1 = (x); \ 65 | typeof(y) _max2 = (y); \ 66 | (void) (&_max1 == &_max2); \ 67 | _max1 > _max2 ? _max1 : _max2; }) 68 | 69 | #define is_log2(v) (((v) & ((v) - 1)) == 0) 70 | 71 | #endif /* __LIBCT_COMPILER_H__ */ 72 | -------------------------------------------------------------------------------- /src/include/ct.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_CT_H__ 2 | #define __LIBCT_CT_H__ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "uapi/libct.h" 10 | 11 | #include "fs.h" 12 | #include "net.h" 13 | #include "process.h" 14 | 15 | struct container_ops { 16 | ct_process_t (*spawn_cb)(ct_handler_t h, ct_process_desc_t p, int (*cb)(void *), void *arg); 17 | ct_process_t (*spawn_execve)(ct_handler_t, ct_process_desc_t p, char *path, char **argv, char **env); 18 | ct_process_t (*enter_cb)(ct_handler_t h, ct_process_desc_t p, int (*cb)(void *), void *arg); 19 | ct_process_t (*enter_execve)(ct_handler_t h, ct_process_desc_t p, char *path, char **argv, char **env); 20 | ct_process_t (*load)(ct_handler_t h, pid_t pid); 21 | int (*switch_ct)(ct_handler_t h); 22 | int (*kill)(ct_handler_t h); 23 | int (*wait)(ct_handler_t h); 24 | enum ct_state (*get_state)(ct_handler_t h); 25 | int (*set_nsmask)(ct_handler_t h, unsigned long nsmask); 26 | int (*set_nspath)(ct_handler_t h, unsigned long ns, char *path); 27 | int (*add_controller)(ct_handler_t h, enum ct_controller ctype); 28 | int (*config_controller)(ct_handler_t h, enum ct_controller ctype, char *param, char *value); 29 | int (*read_controller)(ct_handler_t h, enum ct_controller ctype, char *param, void *buf, size_t len); 30 | int (*fs_set_root)(ct_handler_t h, char *root); 31 | int (*fs_set_private)(ct_handler_t h, enum ct_fs_type, void *priv); 32 | int (*fs_add_mount)(ct_handler_t h, char *src, char *dst, int flags, char *fstype, char *data, 33 | struct libct_cmd *premount, struct libct_cmd *postdump); 34 | int (*fs_add_bind_mount)(ct_handler_t h, char *src, char *dst, int flags); 35 | int (*fs_del_bind_mount)(ct_handler_t h, char *dst); 36 | int (*set_option)(ct_handler_t h, int opt, void *args); 37 | int (*fs_add_devnode)(ct_handler_t h, char *path, int type, int major, int minor); 38 | int (*set_console_fd)(ct_handler_t h, int fd); 39 | void (*destroy)(ct_handler_t h); 40 | void (*detach)(ct_handler_t h); 41 | ct_net_t (*net_add)(ct_handler_t h, enum ct_net_type, void *arg); 42 | int (*net_del)(ct_handler_t h, enum ct_net_type, void *arg); 43 | ct_net_route_t (*net_route_add)(ct_handler_t h); 44 | int (*uname)(ct_handler_t h, char *host, char *domain); 45 | int (*add_uid_map)(ct_handler_t ct, unsigned int first, 46 | unsigned int lower_first, unsigned int count); 47 | int (*add_gid_map)(ct_handler_t ct, unsigned int first, 48 | unsigned int lower_first, unsigned int count); 49 | struct libct_processes *(*get_processes)(ct_handler_t ct); 50 | int (*pause)(ct_handler_t ct); 51 | int (*resume)(ct_handler_t ct); 52 | int (*set_slice)(ct_handler_t ct, char *slice); 53 | int (*set_sysctl)(ct_handler_t ct, char *name, char *val); 54 | }; 55 | 56 | struct ct_handler { 57 | const struct container_ops *ops; 58 | struct list_head s_lh; 59 | }; 60 | 61 | ct_handler_t ct_create(char *name); 62 | 63 | #define CT_AUTO_PROC 0x1 64 | #define CT_KILLABLE 0x2 65 | #define CT_NOSETSID 0x4 66 | #define CT_SYSTEMD 0x8 67 | #define CT_TASKLESS 0x10 68 | 69 | /* 70 | * The main structure describing a container 71 | */ 72 | struct container { 73 | char *name; 74 | char *slice; 75 | struct ct_handler h; 76 | enum ct_state state; 77 | 78 | unsigned int flags; 79 | 80 | /* 81 | * Virtualization-specific fields 82 | */ 83 | 84 | unsigned long nsmask; /* namespaces used by container */ 85 | unsigned long setnsmask; 86 | struct list_head setns_list; 87 | int *ns_fds; 88 | 89 | unsigned long cgroups_mask; 90 | struct list_head cgroups; 91 | struct list_head cg_configs; 92 | char *cgroup_sub; 93 | char *hostname; 94 | char *domainname; 95 | 96 | /* 97 | * FS-specific fields 98 | */ 99 | 100 | char *root_path; /* directory where the CT's root is */ 101 | const struct ct_fs_ops *fs_ops; 102 | void *fs_priv; 103 | struct list_head fs_mnts; /* list of struct fs_mount objects */ 104 | struct list_head fs_devnodes; /* list of struct fs_mount objects */ 105 | 106 | /* 107 | * Network-specific fields 108 | */ 109 | 110 | struct list_head ct_nets; /* list of struct ct_net objects */ 111 | struct list_head ct_net_routes; /* list of struct ct_net objects */ 112 | 113 | /* 114 | * Session-specific fields 115 | */ 116 | int tty_fd; 117 | 118 | struct list_head uid_map; 119 | struct list_head gid_map; 120 | 121 | struct process p; 122 | 123 | struct list_head sysctls; 124 | }; 125 | 126 | struct _uid_gid_map { 127 | struct list_head node; 128 | unsigned int first; 129 | unsigned int lower_first; 130 | unsigned int count; 131 | }; 132 | 133 | static inline struct container *cth2ct(struct ct_handler *h) 134 | { 135 | return container_of(h, struct container, h); 136 | } 137 | 138 | extern char *local_ct_name(ct_handler_t h); 139 | 140 | static inline bool fs_private(struct container *ct) 141 | { 142 | return /* FIXME ct->root_path || */ (ct->nsmask & CLONE_NEWNS); 143 | } 144 | 145 | extern void ct_handler_init(ct_handler_t h); 146 | 147 | struct execv_args { 148 | char *path; 149 | char **argv; 150 | char **env; 151 | }; 152 | 153 | #endif /* __LIBCT_CT_H__ */ 154 | -------------------------------------------------------------------------------- /src/include/err.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Adopted from linux kernel 3 | */ 4 | #ifndef __LIBCT_ERR_H__ 5 | #define __LIBCT_ERR_H__ 6 | 7 | #include "compiler.h" 8 | 9 | /* 10 | * The address of a block returned by malloc or realloc in GNU 11 | * systems is always a multiple of eight (or sixteen on 64-bit systems). 12 | * 13 | * Thus we may encode error number in low bits. 14 | */ 15 | #define MAX_ERRNO 4095 16 | 17 | #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO) 18 | 19 | static inline void *ERR_PTR(long error) 20 | { 21 | return (void *)error; 22 | } 23 | 24 | static inline long PTR_ERR(const void *ptr) 25 | { 26 | return (long)ptr; 27 | } 28 | 29 | static inline long IS_ERR(const void *ptr) 30 | { 31 | return IS_ERR_VALUE((unsigned long)ptr); 32 | } 33 | 34 | static inline long IS_ERR_OR_NULL(const void *ptr) 35 | { 36 | return !ptr || IS_ERR_VALUE((unsigned long)ptr); 37 | } 38 | 39 | static inline void *ERR_CAST(const void *ptr) 40 | { 41 | /* cast away the const */ 42 | return (void *)ptr; 43 | } 44 | 45 | static inline int PTR_RET(const void *ptr) 46 | { 47 | if (IS_ERR(ptr)) 48 | return PTR_ERR(ptr); 49 | else 50 | return 0; 51 | } 52 | 53 | #endif /* __LIBCT_ERR_H__ */ 54 | -------------------------------------------------------------------------------- /src/include/fs.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_FS_H__ 2 | #define __LIBCT_FS_H__ 3 | 4 | #include "uapi/libct.h" 5 | 6 | struct container; 7 | 8 | struct ct_fs_ops { 9 | int (*mount)(char *root, void *fs_priv); 10 | void (*umount)(char *root, void *fs_priv); 11 | void *(*get)(void *fs_priv); 12 | void (*put)(void *fs_priv); 13 | }; 14 | 15 | extern const struct ct_fs_ops *fstype_get_ops(enum ct_fs_type type); 16 | extern int local_fs_set_root(ct_handler_t h, char *root); 17 | extern int local_fs_set_private(ct_handler_t ct, enum ct_fs_type type, void *priv); 18 | extern int local_add_bind_mount(ct_handler_t ct, char *src, char *dst, int flags); 19 | extern int local_del_bind_mount(ct_handler_t ct, char *dst); 20 | extern int local_add_mount(ct_handler_t h, char *src, char *dst, int flags, char *fstype, char *data, 21 | struct libct_cmd *premount, struct libct_cmd *postdump); 22 | 23 | extern int fs_mount(struct container *ct); 24 | extern int fs_mount_ext(struct container *ct); 25 | extern void fs_umount(struct container *ct); 26 | extern void fs_umount_ext(struct container *ct); 27 | extern void fs_free(struct container *ct); 28 | 29 | extern int local_add_devnode(ct_handler_t h, char *path, int mode, int maj, int minor); 30 | 31 | extern int fs_create_devnodes(struct container *ct); 32 | extern void fs_free_devnodes(struct container *ct); 33 | 34 | #endif /* __LIBCT_FS_H__ */ 35 | -------------------------------------------------------------------------------- /src/include/libct.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_H__ 2 | #define __LIBCT_H__ 3 | 4 | extern int libct_init_local(void); 5 | 6 | #endif /* __LIBCT_H__ */ 7 | -------------------------------------------------------------------------------- /src/include/linux-kernel.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_LINUX_KERNEL_H__ 2 | #define __LIBCT_LINUX_KERNEL_H__ 3 | 4 | extern unsigned long kernel_ns_mask; 5 | 6 | extern int linux_get_ns_mask(void); 7 | extern int linux_get_cgroup_mounts(void); 8 | extern int linux_get_last_capability(void); 9 | 10 | #endif /* __LIBCT_LINUX_KERNEL_H__ */ 11 | -------------------------------------------------------------------------------- /src/include/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Warning: this header is used in log plugin as well, 3 | * modify carefully. 4 | */ 5 | 6 | #ifndef __LIBCT_LOG_H__ 7 | #define __LIBCT_LOG_H__ 8 | 9 | #include 10 | #include 11 | 12 | #include "uapi/libct-log-levels.h" 13 | 14 | #ifndef LOG_PREFIX 15 | # define LOG_PREFIX 16 | #endif 17 | 18 | extern void print_on_level(unsigned int loglevel, const char *format, ...); 19 | 20 | #define pr_msg(fmt, ...) \ 21 | print_on_level(LOG_MSG, \ 22 | fmt, ##__VA_ARGS__) 23 | 24 | #define pr_info(fmt, ...) \ 25 | print_on_level(LOG_INFO, \ 26 | LOG_PREFIX fmt, ##__VA_ARGS__) 27 | 28 | #define pr_err(fmt, ...) \ 29 | print_on_level(LOG_ERROR, \ 30 | "Error (%s:%d): " LOG_PREFIX fmt, \ 31 | __FILE__, __LINE__, ##__VA_ARGS__) 32 | 33 | #define pr_err_once(fmt, ...) \ 34 | do { \ 35 | static bool __printed; \ 36 | if (!__printed) { \ 37 | pr_err(fmt, ##__VA_ARGS__); \ 38 | __printed = 1; \ 39 | } \ 40 | } while (0) 41 | 42 | #define pr_warn(fmt, ...) \ 43 | print_on_level(LOG_WARN, \ 44 | "Warn (%s:%d): " LOG_PREFIX fmt, \ 45 | __FILE__, __LINE__, ##__VA_ARGS__) 46 | 47 | #define pr_warn_once(fmt, ...) \ 48 | do { \ 49 | static bool __printed; \ 50 | if (!__printed) { \ 51 | pr_warn(fmt, ##__VA_ARGS__); \ 52 | __printed = 1; \ 53 | } \ 54 | } while (0) 55 | 56 | #define pr_debug(fmt, ...) \ 57 | print_on_level(LOG_DEBUG, \ 58 | LOG_PREFIX fmt, ##__VA_ARGS__) 59 | 60 | #define pr_perror(fmt, ...) \ 61 | pr_err(fmt ": %m\n", ##__VA_ARGS__) 62 | 63 | #endif /* __LIBCT_LOG_H__ */ 64 | -------------------------------------------------------------------------------- /src/include/namespaces.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_NAMESPACES_H__ 2 | #define __LIBCT_NAMESPACES_H__ 3 | 4 | struct ns_desc { 5 | char *name; 6 | unsigned long cflag; 7 | int idx; 8 | }; 9 | 10 | extern struct ns_desc *namespaces[6]; 11 | extern struct ns_desc pid_ns; 12 | extern struct ns_desc net_ns; 13 | 14 | extern int switch_ns(int pid, struct ns_desc *d, int *old_ns); 15 | extern void restore_ns(int ns_fd, struct ns_desc *d); 16 | 17 | #endif /* __LIBCT_NAMESPACES_H__ */ 18 | -------------------------------------------------------------------------------- /src/include/net.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_NET_H__ 2 | #define __LIBCT_NET_H__ 3 | 4 | #include "uapi/libct.h" 5 | 6 | #include "list.h" 7 | 8 | struct _NetaddReq; 9 | struct container; 10 | 11 | typedef const struct ct_net_ops *(*net_get_ops_cb)(enum ct_net_type ntype); 12 | extern ct_net_t __local_net_add(ct_handler_t h, enum ct_net_type, void *arg, net_get_ops_cb cb); 13 | 14 | extern ct_net_t local_net_add(ct_handler_t h, enum ct_net_type, void *arg); 15 | extern int local_net_del(ct_handler_t h, enum ct_net_type, void *arg); 16 | extern void net_release(struct container *ct); 17 | extern int net_start(struct container *ct); 18 | extern void net_stop(struct container *ct); 19 | 20 | extern ct_net_route_t local_net_route_add(ct_handler_t h); 21 | extern void net_route_release(struct container *ct); 22 | extern int net_route_setup(struct container *ct); 23 | 24 | struct ct_net_route_nh { /* next hop */ 25 | char *gateway; 26 | char *dev; 27 | 28 | struct list_head l; 29 | }; 30 | 31 | struct ct_net_route { 32 | char *dst; 33 | char *src; 34 | char *dev; 35 | 36 | struct list_head nhs; 37 | 38 | struct list_head l; 39 | }; 40 | 41 | struct ct_net_ops { 42 | struct ct_net *(*create)(void *arg, struct ct_net_ops const *ops); 43 | int (*start)(struct container *ct, struct ct_net *n); 44 | void (*stop)(struct container *ct, struct ct_net *n); 45 | void (*destroy)(struct ct_net *n); 46 | int (*match)(struct ct_net *n, void *arg); 47 | int (*set_mac_addr)(struct ct_net *n, char *addr); 48 | int (*set_master)(struct ct_net *n, char *master); 49 | int (*add_ip_addr)(ct_net_t n, char *addr); 50 | int (*set_mtu)(ct_net_t n, int mtu); 51 | }; 52 | 53 | struct ct_net_ip_addr { 54 | char *addr; 55 | struct list_head l; 56 | }; 57 | 58 | struct ct_net { 59 | int ifidx; 60 | int mtu; 61 | char *name; 62 | char *addr; 63 | char *master; 64 | 65 | struct list_head ip_addrs; 66 | 67 | struct list_head l; 68 | const struct ct_net_ops *ops; 69 | }; 70 | 71 | extern const struct ct_net_ops *net_get_ops(enum ct_net_type); 72 | 73 | 74 | #endif /* __LIBCT_NET_H__ */ 75 | -------------------------------------------------------------------------------- /src/include/net_util.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_NET_UTILS_H__ 2 | #define __LIBCT_NET_UTILS_H__ 3 | 4 | #include "net.h" 5 | 6 | struct ct_net_host_nic { 7 | struct ct_net n; 8 | char *name; 9 | }; 10 | 11 | struct ct_net_veth { 12 | struct ct_net n; 13 | struct ct_net peer; 14 | }; 15 | 16 | extern void ct_net_init(ct_net_t n, const struct ct_net_ops *ops); 17 | extern void ct_net_clean(ct_net_t n); 18 | 19 | extern int net_dev_set_mtu(ct_net_t n, int mtu); 20 | extern int net_dev_set_mac_addr(ct_net_t n, char *addr); 21 | extern int net_dev_set_master(ct_net_t n, char *master); 22 | extern int net_dev_add_ip_addr(ct_net_t n, char *addr); 23 | extern void veth_stop(struct container *ct, struct ct_net *n); 24 | extern int veth_match(struct ct_net *n, void *arg); 25 | extern void veth_free(struct ct_net_veth *vn); 26 | extern struct ct_net_veth *cn2vn(struct ct_net *n); 27 | 28 | extern struct nl_sock *net_sock_open(); 29 | extern void net_sock_close(struct nl_sock *sk); 30 | extern struct nl_cache *net_get_link_cache(struct nl_sock *sk); 31 | extern int net_link_apply(char *name, ct_net_t n); 32 | extern int net_add_ip_addrs(struct nl_sock *sk, ct_net_t n); 33 | #endif /* __LIBCT_NET_UTILS_H__ */ 34 | -------------------------------------------------------------------------------- /src/include/process.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_PROCESS_H__ 2 | #define __LIBCT_PROCESS_H__ 3 | 4 | #include 5 | #include 6 | 7 | #include "uapi/libct.h" 8 | 9 | #include "compiler.h" 10 | 11 | struct process_ops { 12 | int (*wait)(ct_process_t p, int *status); 13 | void (*destroy)(ct_process_t p); 14 | int (*get_pid)(ct_process_t p); 15 | }; 16 | 17 | struct ct_process { 18 | const struct process_ops *ops; 19 | }; 20 | 21 | struct process { 22 | struct ct_process h; 23 | pid_t pid; 24 | int status; 25 | }; 26 | 27 | struct process_desc_ops { 28 | int (*setuid)(ct_process_desc_t p, unsigned int uid); 29 | int (*setgid)(ct_process_desc_t p, unsigned int gid); 30 | int (*setgroups)(ct_process_desc_t p, unsigned int size, unsigned int *groups); 31 | int (*set_user)(ct_process_desc_t p, char *user); 32 | int (*set_caps)(ct_process_desc_t h, unsigned long mask, unsigned int apply_to); 33 | int (*set_pdeathsig)(ct_process_desc_t h, int sig); 34 | int (*set_lsm_label)(ct_process_desc_t h, char *label); 35 | int (*set_fds)(ct_process_desc_t h, int *fds, int fdn); 36 | int (*set_env)(ct_process_desc_t h, char **env, int envn); 37 | int (*set_rlimit)(ct_process_desc_t h, int resource, uint64_t soft, uint64_t hard); 38 | ct_process_desc_t (*copy)(ct_process_desc_t h); 39 | void (*destroy)(ct_process_desc_t p); 40 | }; 41 | 42 | struct ct_process_desc { 43 | const struct process_desc_ops *ops; 44 | }; 45 | 46 | struct process_desc { 47 | struct ct_process_desc h; 48 | unsigned int uid; 49 | unsigned int gid; 50 | unsigned int ngroups; 51 | unsigned int *groups; 52 | char *user; 53 | 54 | unsigned int cap_mask; 55 | uint64_t cap_bset; 56 | uint64_t cap_caps; 57 | 58 | int pdeathsig; 59 | 60 | int lsm_on_exec; 61 | char *lsm_label; 62 | 63 | int *fds; 64 | int fdn; 65 | char **env; 66 | int envn; 67 | 68 | struct rlimit rlimit[RLIM_NLIMITS]; 69 | }; 70 | 71 | static inline struct process_desc *prh2pr(ct_process_desc_t h) 72 | { 73 | return container_of(h, struct process_desc, h); 74 | } 75 | 76 | static inline struct process *ph2p(ct_process_t h) 77 | { 78 | return container_of(h, struct process, h); 79 | } 80 | 81 | extern void local_process_desc_init(struct process_desc *p); 82 | extern struct process_desc *local_process_copy(struct process_desc *p); 83 | 84 | extern void local_process_init(struct process *p); 85 | 86 | #endif //__LIBCT_PROCESS_H__ 87 | -------------------------------------------------------------------------------- /src/include/rpc.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_RPC_H__ 2 | #define __LIBCT_RPC_H__ 3 | 4 | struct _RpcResponse; 5 | typedef struct _RpcResponse RpcResponse; 6 | struct _RpcRequest; 7 | typedef struct _RpcRequest RpcRequest; 8 | 9 | extern int do_send_resp(int sk, RpcRequest *req, int err, RpcResponse *resp); 10 | extern int send_resp(int sk, RpcRequest *req, int err); 11 | 12 | extern int recv_req(int sk, RpcRequest **req, int **fds, int *nr_fds); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/include/security.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_SECURITY_H__ 2 | #define __LIBCT_SECURITY_H__ 3 | 4 | struct process_desc; 5 | 6 | extern int apply_creds(struct process_desc *p); 7 | 8 | #endif /* __LIBCT_SECURITY_H__ */ 9 | -------------------------------------------------------------------------------- /src/include/session.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_SESSION_H__ 2 | #define __LIBCT_SESSION_H__ 3 | 4 | #include "uapi/libct.h" 5 | 6 | #include "list.h" 7 | #include "ct.h" 8 | 9 | enum { 10 | BACKEND_NONE, 11 | BACKEND_LOCAL, 12 | BACKEND_UNIX, 13 | BACKEND_VZ, 14 | }; 15 | 16 | struct backend_ops { 17 | int type; 18 | ct_handler_t (*create_ct)(libct_session_t s, char *name); 19 | ct_handler_t (*open_ct)(libct_session_t s, char *name); 20 | void (*update_ct_state)(libct_session_t s, pid_t pid); 21 | ct_process_desc_t (*create_process_desc)(libct_session_t s); 22 | void (*close)(libct_session_t s); 23 | }; 24 | 25 | struct libct_session { 26 | const struct backend_ops *ops; 27 | struct list_head s_cts; 28 | }; 29 | 30 | struct local_session { 31 | struct libct_session s; 32 | }; 33 | 34 | static inline struct local_session *s2ls(libct_session_t s) 35 | { 36 | return container_of(s, struct local_session, s); 37 | } 38 | 39 | extern void local_session_add(libct_session_t, struct container *ct); 40 | 41 | #endif /* __LIBCT_SESSION_H__ */ 42 | -------------------------------------------------------------------------------- /src/include/systemd.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_SYSTEMD_H__ 2 | #define __LIBCT_SYSTEMD_H__ 3 | 4 | struct container; 5 | int systemd_start_unit(struct container *ct, int pid); 6 | int systemd_add_pid(struct container *ct, int pid); 7 | 8 | #endif //__LIBCT_SYSTEMD_H__ 9 | -------------------------------------------------------------------------------- /src/include/types.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_TYPES_H__ 2 | #define __LIBCT_TYPES_H__ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "asm-generic/int.h" 10 | #include "asm/page.h" 11 | 12 | #endif /* __LIBCT_TYPES_H__ */ 13 | -------------------------------------------------------------------------------- /src/include/uapi/libct-errors.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_ERRORS_H__ 2 | #define __LIBCT_ERRORS_H__ 3 | /* 4 | * This file contains error codes, that can be returned from various 5 | * library calls in negative form. 6 | */ 7 | 8 | /* Codes bellow 1000 are reserved for generic Linux error codes */ 9 | 10 | /* Generic */ 11 | #define LCTERR_BADCTSTATE 1002 /* Bad container state */ 12 | #define LCTERR_BADTYPE 1003 /* Bad type requested */ 13 | #define LCTERR_BADARG 1004 /* Bad argument for request */ 14 | #define LCTERR_NONS 1005 /* Required namespace is not available */ 15 | #define LCTERR_NOTFOUND 1006 /* Requested object not found */ 16 | #define LCTERR_INVARG 1007 /* Invalid API call argument */ 17 | #define LCTERR_OPNOTSUPP 1008 /* Operation not supported */ 18 | 19 | /* FS-specific */ 20 | #define LCTERR_CANTMOUNT 1010 /* Can't mount something */ 21 | 22 | /* CGroups-specifig */ 23 | #define LCTERR_CGCREATE 1021 /* Can't create cgroup */ 24 | #define LCTERR_CGCONFIG 1022 /* Can't configure cgroup */ 25 | #define LCTERR_CGATTACH 1023 /* Can't attach to cgroup */ 26 | 27 | /* RPC-specific ones */ 28 | #define LCTERR_BADCTRID 1042 /* Bad container remote ID given */ 29 | #define LCTERR_BADCTRNAME 1043 /* Bad name on open */ 30 | #define LCTERR_RPCUNKNOWN 1044 /* Remote problem , but err is not given */ 31 | #define LCTERR_RPCCOMM 1045 /* Error communicating via channel */ 32 | 33 | #endif /* __LIBCT_ERRORS_H__ */ 34 | -------------------------------------------------------------------------------- /src/include/uapi/libct-log-levels.h: -------------------------------------------------------------------------------- 1 | #ifndef __UAPI_LIBCT_LOG_LEVELS_H__ 2 | #define __UAPI_LIBCT_LOG_LEVELS_H__ 3 | 4 | #define LOG_MSG (0) /* Print message regardless of log level */ 5 | #define LOG_ERROR (1) /* Errors only, when we're in trouble */ 6 | #define LOG_WARN (2) /* Warnings, dazen and confused but trying to continue */ 7 | #define LOG_INFO (3) /* Informative, everything is fine */ 8 | #define LOG_DEBUG (4) /* Debug only */ 9 | 10 | #define DEFAULT_LOGLEVEL LOG_WARN 11 | 12 | void libct_log_init(int fd, unsigned int level); 13 | 14 | #endif /* __UAPI_LIBCT_LOG_LEVELS_H__ */ 15 | -------------------------------------------------------------------------------- /src/include/util.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_UTIL_H__ 2 | #define __LIBCT_UTIL_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define xvaopt(parm, type, def) ({ \ 9 | type s; \ 10 | s = va_arg(parm, type); \ 11 | if (!s) \ 12 | s = def; \ 13 | s; }) 14 | 15 | 16 | extern int do_mount(char *src, char *dst, int flags, char *fstype, char *data); 17 | extern int set_string(char **dest, char *src); 18 | extern int parse_int(const char *str, int *val); 19 | extern int parse_uint(const char *str, unsigned int *val); 20 | extern int stat_file(const char *file); 21 | extern int setup_fds_at(int proc_fd, int *fds, int n); 22 | 23 | extern int spawn_sock_wait(int sk); 24 | extern int spawn_sock_wait_and_close(int sk); 25 | extern void spawn_sock_wake(int sk, int ret); 26 | extern void spawn_sock_wake_and_close(int sk, int ret); 27 | #endif /* __LIBCT_UTIL_H__ */ 28 | -------------------------------------------------------------------------------- /src/include/vz/readelf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999-2010, Parallels, Inc. All rights reserved. 3 | * 4 | */ 5 | 6 | #ifndef _READELF_H_ 7 | #define _READELF_H_ 8 | 9 | enum {elf_none = 0, 10 | elf_32 = 1, 11 | elf_64 = 2}; 12 | int get_arch_from_elf(const char *file); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/include/vz/vz.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_VZ_H__ 2 | #define __LIBCT_VZ_H__ 3 | 4 | #include "uapi/libct.h" 5 | 6 | struct container_ops; 7 | 8 | ct_handler_t vz_ct_create(char *name); 9 | const struct container_ops *get_vz_ct_ops(void); 10 | int vzctl_open(void); 11 | void vzctl_close(void); 12 | int get_vzctlfd(void); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/include/vz/vz_net.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_VZ_NET_H__ 2 | #define __LIBCT_VZ_NET_H__ 3 | 4 | #include "net.h" 5 | 6 | extern const struct ct_net_ops *vz_net_get_ops(enum ct_net_type); 7 | extern ct_net_t vz_net_add(ct_handler_t h, enum ct_net_type ntype, void *arg); 8 | 9 | #endif /* __LIBCT_VZ_NET_H__ */ 10 | -------------------------------------------------------------------------------- /src/include/vz/vzcalluser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * include/linux/vzcalluser.h 3 | * 4 | * Copyright (C) 2005 SWsoft 5 | * All rights reserved. 6 | * 7 | * Licensing governed by "linux/COPYING.SWsoft" file. 8 | * 9 | */ 10 | 11 | #ifndef _LINUX_VZCALLUSER_H 12 | #define _LINUX_VZCALLUSER_H 13 | 14 | #include 15 | #include "types.h" 16 | #include "vziptable_defs.h" 17 | 18 | #ifndef __ENVID_T_DEFINED__ 19 | typedef unsigned envid_t; 20 | #define __ENVID_T_DEFINED__ 21 | #endif 22 | 23 | #ifndef __KERNEL__ 24 | #define __user 25 | #endif 26 | 27 | /* 28 | * VE management ioctls 29 | */ 30 | 31 | struct vzctl_old_env_create { 32 | envid_t veid; 33 | unsigned flags; 34 | #define VE_CREATE 1 /* Create VE, VE_ENTER added automatically */ 35 | #define VE_EXCLUSIVE 2 /* Fail if exists */ 36 | #define VE_ENTER 4 /* Enter existing VE */ 37 | #define VE_TEST 8 /* Test if VE exists */ 38 | #define VE_LOCK 16 /* Do not allow entering created VE */ 39 | #define VE_SKIPLOCK 32 /* Allow entering embrion VE */ 40 | __u32 addr; 41 | }; 42 | 43 | struct vzctl_mark_env_to_down { 44 | envid_t veid; 45 | }; 46 | 47 | struct vzctl_setdevperms { 48 | envid_t veid; 49 | unsigned type; 50 | #define VE_USE_MAJOR 010 /* Test MAJOR supplied in rule */ 51 | #define VE_USE_MINOR 030 /* Test MINOR supplied in rule */ 52 | #define VE_USE_MASK 030 /* Testing mask, VE_USE_MAJOR|VE_USE_MINOR */ 53 | unsigned dev; 54 | unsigned mask; 55 | }; 56 | 57 | struct vzctl_ve_netdev { 58 | envid_t veid; 59 | int op; 60 | #define VE_NETDEV_ADD 1 61 | #define VE_NETDEV_DEL 2 62 | char __user *dev_name; 63 | }; 64 | 65 | struct vzctl_ve_configure { 66 | unsigned int veid; 67 | unsigned int key; 68 | #define VE_CONFIGURE_OS_RELEASE 2 69 | #define VE_CONFIGURE_CREATE_PROC_LINK 4 70 | #define VE_CONFIGURE_OPEN_TTY 5 71 | #define VE_CONFIGURE_MOUNT_OPTIONS 7 72 | unsigned int val; 73 | unsigned int size; 74 | char data[0]; 75 | }; 76 | 77 | struct vzctl_ve_meminfo { 78 | envid_t veid; 79 | unsigned long val; 80 | }; 81 | 82 | struct vzctl_env_create_cid { 83 | envid_t veid; 84 | unsigned flags; 85 | __u32 class_id; 86 | }; 87 | 88 | struct vzctl_env_create { 89 | envid_t veid; 90 | unsigned flags; 91 | __u32 class_id; 92 | }; 93 | 94 | struct env_create_param { 95 | __u64 iptables_mask; 96 | }; 97 | 98 | #define VZCTL_ENV_CREATE_DATA_MINLEN sizeof(struct env_create_param) 99 | 100 | struct env_create_param2 { 101 | __u64 iptables_mask; 102 | __u64 feature_mask; 103 | __u32 total_vcpus; /* 0 - don't care, same as in host */ 104 | }; 105 | 106 | struct env_create_param3 { 107 | __u64 iptables_mask; 108 | __u64 feature_mask; 109 | __u32 total_vcpus; 110 | __u32 pad; 111 | __u64 known_features; 112 | }; 113 | 114 | #define VE_FEATURE_SYSFS (1ULL << 0) 115 | #define VE_FEATURE_NFS (1ULL << 1) 116 | #define VE_FEATURE_DEF_PERMS (1ULL << 2) 117 | #define VE_FEATURE_SIT (1ULL << 3) 118 | #define VE_FEATURE_IPIP (1ULL << 4) 119 | #define VE_FEATURE_PPP (1ULL << 5) 120 | #define VE_FEATURE_IPGRE (1ULL << 6) 121 | #define VE_FEATURE_BRIDGE (1ULL << 7) 122 | #define VE_FEATURE_NFSD (1ULL << 8) 123 | 124 | #define VE_FEATURES_OLD (VE_FEATURE_SYSFS) 125 | #define VE_FEATURES_DEF (VE_FEATURE_SYSFS | \ 126 | VE_FEATURE_DEF_PERMS) 127 | 128 | typedef struct env_create_param3 env_create_param_t; 129 | #define VZCTL_ENV_CREATE_DATA_MAXLEN sizeof(env_create_param_t) 130 | 131 | struct vzctl_env_create_data { 132 | envid_t veid; 133 | unsigned flags; 134 | __u32 class_id; 135 | env_create_param_t __user *data; 136 | int datalen; 137 | }; 138 | 139 | struct vz_load_avg { 140 | int val_int; 141 | int val_frac; 142 | }; 143 | 144 | struct vz_cpu_stat { 145 | unsigned long user_jif; 146 | unsigned long nice_jif; 147 | unsigned long system_jif; 148 | unsigned long uptime_jif; 149 | __u64 idle_clk; 150 | __u64 strv_clk; 151 | __u64 uptime_clk; 152 | struct vz_load_avg avenrun[3]; /* loadavg data */ 153 | }; 154 | 155 | struct vzctl_cpustatctl { 156 | envid_t veid; 157 | struct vz_cpu_stat __user *cpustat; 158 | }; 159 | 160 | #define VZCTLTYPE '.' 161 | #define VZCTL_OLD_ENV_CREATE _IOW(VZCTLTYPE, 0, \ 162 | struct vzctl_old_env_create) 163 | #define VZCTL_MARK_ENV_TO_DOWN _IOW(VZCTLTYPE, 1, \ 164 | struct vzctl_mark_env_to_down) 165 | #define VZCTL_SETDEVPERMS _IOW(VZCTLTYPE, 2, \ 166 | struct vzctl_setdevperms) 167 | #define VZCTL_ENV_CREATE_CID _IOW(VZCTLTYPE, 4, \ 168 | struct vzctl_env_create_cid) 169 | #define VZCTL_ENV_CREATE _IOW(VZCTLTYPE, 5, \ 170 | struct vzctl_env_create) 171 | #define VZCTL_GET_CPU_STAT _IOW(VZCTLTYPE, 6, \ 172 | struct vzctl_cpustatctl) 173 | #define VZCTL_ENV_CREATE_DATA _IOW(VZCTLTYPE, 10, \ 174 | struct vzctl_env_create_data) 175 | #define VZCTL_VE_NETDEV _IOW(VZCTLTYPE, 11, \ 176 | struct vzctl_ve_netdev) 177 | #define VZCTL_VE_MEMINFO _IOW(VZCTLTYPE, 13, \ 178 | struct vzctl_ve_meminfo) 179 | #define VZCTL_VE_CONFIGURE _IOW(VZCTLTYPE, 15, \ 180 | struct vzctl_ve_configure) 181 | 182 | #ifdef __KERNEL__ 183 | #ifdef CONFIG_COMPAT 184 | #include 185 | 186 | struct compat_vzctl_ve_netdev { 187 | envid_t veid; 188 | int op; 189 | compat_uptr_t dev_name; 190 | }; 191 | 192 | struct compat_vzctl_ve_meminfo { 193 | envid_t veid; 194 | compat_ulong_t val; 195 | }; 196 | 197 | struct compat_vzctl_env_create_data { 198 | envid_t veid; 199 | unsigned flags; 200 | __u32 class_id; 201 | compat_uptr_t data; 202 | int datalen; 203 | }; 204 | 205 | #define VZCTL_COMPAT_ENV_CREATE_DATA _IOW(VZCTLTYPE, 10, \ 206 | struct compat_vzctl_env_create_data) 207 | #define VZCTL_COMPAT_VE_NETDEV _IOW(VZCTLTYPE, 11, \ 208 | struct compat_vzctl_ve_netdev) 209 | #define VZCTL_COMPAT_VE_MEMINFO _IOW(VZCTLTYPE, 13, \ 210 | struct compat_vzctl_ve_meminfo) 211 | #endif 212 | #endif 213 | 214 | #endif 215 | -------------------------------------------------------------------------------- /src/include/vz/vzctl_veth.h: -------------------------------------------------------------------------------- 1 | /* 2 | * include/linux/vzctl_veth.h 3 | * 4 | * Copyright (C) 2006 SWsoft 5 | * All rights reserved. 6 | * 7 | * Licensing governed by "linux/COPYING.SWsoft" file. 8 | * 9 | */ 10 | 11 | #ifndef _VZCTL_VETH_H 12 | #define _VZCTL_VETH_H 13 | 14 | #include 15 | #include 16 | 17 | #ifndef __ENVID_T_DEFINED__ 18 | typedef unsigned envid_t; 19 | #define __ENVID_T_DEFINED__ 20 | #endif 21 | 22 | struct vzctl_ve_hwaddr { 23 | envid_t veid; 24 | int op; 25 | #define VE_ETH_ADD 1 26 | #define VE_ETH_DEL 2 27 | #define VE_ETH_ALLOW_MAC_CHANGE 3 28 | #define VE_ETH_DENY_MAC_CHANGE 4 29 | unsigned char dev_addr[6]; 30 | int addrlen; 31 | char dev_name[16]; 32 | unsigned char dev_addr_ve[6]; 33 | int addrlen_ve; 34 | char dev_name_ve[16]; 35 | }; 36 | 37 | #define VETHCTLTYPE '[' 38 | 39 | #define VETHCTL_VE_HWADDR _IOW(VETHCTLTYPE, 3, \ 40 | struct vzctl_ve_hwaddr) 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/include/vz/vziolimit.h: -------------------------------------------------------------------------------- 1 | /* 2 | * include/linux/vziolimit.h 3 | * 4 | * Copyright (C) 2010, Parallels inc. 5 | * All rights reserved. 6 | * 7 | */ 8 | 9 | #ifndef _LINUX_VZIOLIMIT_H 10 | #define _LINUX_VZIOLIMIT_H 11 | 12 | #include 13 | #include 14 | 15 | #define VZIOLIMITTYPE 'I' 16 | 17 | struct iolimit_state { 18 | unsigned int id; 19 | unsigned int speed; 20 | unsigned int burst; 21 | unsigned int latency; 22 | }; 23 | 24 | #define VZCTL_SET_IOLIMIT _IOW(VZIOLIMITTYPE, 0, struct iolimit_state) 25 | #define VZCTL_GET_IOLIMIT _IOR(VZIOLIMITTYPE, 1, struct iolimit_state) 26 | #define VZCTL_SET_IOPSLIMIT _IOW(VZIOLIMITTYPE, 2, struct iolimit_state) 27 | #define VZCTL_GET_IOPSLIMIT _IOR(VZIOLIMITTYPE, 3, struct iolimit_state) 28 | 29 | #endif /* _LINUX_VZIOLIMIT_H */ 30 | -------------------------------------------------------------------------------- /src/include/vz/vziptable_defs.h: -------------------------------------------------------------------------------- 1 | #ifndef _LINUX_VZIPTABLE_DEFS_H 2 | #define _LINUX_VZIPTABLE_DEFS_H 3 | 4 | #include 5 | #include 6 | 7 | /* 8 | * This masks represent modules 9 | * 10 | * Strictly speaking we use only a small subset 11 | * of this bits novadays but we MUST RESERVE all 12 | * the bits were ever used in a sake of ABI compatibility 13 | * (ie compatibility with vzctl user-space utility) 14 | * 15 | * DON'T EVER DELETE/MODIFY THIS BITS 16 | */ 17 | #define VE_IPT_GENERATE(name, shift) name = (1U << shift) 18 | 19 | enum ve_ipt_mods { 20 | VE_IPT_GENERATE(VE_IP_IPTABLES_MOD, 0), 21 | VE_IPT_GENERATE(VE_IP_FILTER_MOD, 1), 22 | VE_IPT_GENERATE(VE_IP_MANGLE_MOD, 2), 23 | VE_IPT_GENERATE(VE_IP_MATCH_LIMIT_MOD, 3), 24 | VE_IPT_GENERATE(VE_IP_MATCH_MULTIPORT_MOD, 4), 25 | VE_IPT_GENERATE(VE_IP_MATCH_TOS_MOD, 5), 26 | VE_IPT_GENERATE(VE_IP_TARGET_TOS_MOD, 6), 27 | VE_IPT_GENERATE(VE_IP_TARGET_REJECT_MOD, 7), 28 | VE_IPT_GENERATE(VE_IP_TARGET_TCPMSS_MOD, 8), 29 | VE_IPT_GENERATE(VE_IP_MATCH_TCPMSS_MOD, 9), 30 | VE_IPT_GENERATE(VE_IP_MATCH_TTL_MOD, 10), 31 | VE_IPT_GENERATE(VE_IP_TARGET_LOG_MOD, 11), 32 | VE_IPT_GENERATE(VE_IP_MATCH_LENGTH_MOD, 12), 33 | VE_IPT_GENERATE(VE_IP_CONNTRACK_MOD, 14), 34 | VE_IPT_GENERATE(VE_IP_CONNTRACK_FTP_MOD, 15), 35 | VE_IPT_GENERATE(VE_IP_CONNTRACK_IRC_MOD, 16), 36 | VE_IPT_GENERATE(VE_IP_MATCH_CONNTRACK_MOD, 17), 37 | VE_IPT_GENERATE(VE_IP_MATCH_STATE_MOD, 18), 38 | VE_IPT_GENERATE(VE_IP_MATCH_HELPER_MOD, 19), 39 | VE_IPT_GENERATE(VE_IP_NAT_MOD, 20), 40 | VE_IPT_GENERATE(VE_IP_NAT_FTP_MOD, 21), 41 | VE_IPT_GENERATE(VE_IP_NAT_IRC_MOD, 22), 42 | VE_IPT_GENERATE(VE_IP_TARGET_REDIRECT_MOD, 23), 43 | VE_IPT_GENERATE(VE_IP_MATCH_OWNER_MOD, 24), 44 | VE_IPT_GENERATE(VE_IP_MATCH_MAC_MOD, 25), 45 | VE_IPT_GENERATE(VE_IP_IPTABLES6_MOD, 26), 46 | VE_IPT_GENERATE(VE_IP_FILTER6_MOD, 27), 47 | VE_IPT_GENERATE(VE_IP_MANGLE6_MOD, 28), 48 | VE_IPT_GENERATE(VE_IP_IPTABLE_NAT_MOD, 29), 49 | VE_IPT_GENERATE(VE_NF_CONNTRACK_MOD, 30), 50 | }; 51 | 52 | /* these masks represent modules with their dependences */ 53 | #define VE_IP_IPTABLES (VE_IP_IPTABLES_MOD) 54 | #define VE_IP_FILTER (VE_IP_FILTER_MOD | VE_IP_IPTABLES) 55 | #define VE_IP_MANGLE (VE_IP_MANGLE_MOD | VE_IP_IPTABLES) 56 | #define VE_IP_IPTABLES6 (VE_IP_IPTABLES6_MOD) 57 | #define VE_IP_FILTER6 (VE_IP_FILTER6_MOD | VE_IP_IPTABLES6) 58 | #define VE_IP_MANGLE6 (VE_IP_MANGLE6_MOD | VE_IP_IPTABLES6) 59 | #define VE_NF_CONNTRACK (VE_NF_CONNTRACK_MOD | VE_IP_IPTABLES) 60 | #define VE_IP_CONNTRACK (VE_IP_CONNTRACK_MOD | VE_IP_IPTABLES) 61 | #define VE_IP_CONNTRACK_FTP (VE_IP_CONNTRACK_FTP_MOD | VE_IP_CONNTRACK) 62 | #define VE_IP_CONNTRACK_IRC (VE_IP_CONNTRACK_IRC_MOD | VE_IP_CONNTRACK) 63 | #define VE_IP_NAT (VE_IP_NAT_MOD | VE_IP_CONNTRACK) 64 | #define VE_IP_NAT_FTP (VE_IP_NAT_FTP_MOD | VE_IP_NAT | VE_IP_CONNTRACK_FTP) 65 | #define VE_IP_NAT_IRC (VE_IP_NAT_IRC_MOD | VE_IP_NAT | VE_IP_CONNTRACK_IRC) 66 | #define VE_IP_IPTABLE_NAT (VE_IP_IPTABLE_NAT_MOD | VE_IP_CONNTRACK) 67 | 68 | /* safe iptables mask to be used by default */ 69 | #define VE_IP_DEFAULT (VE_IP_IPTABLES | VE_IP_FILTER | VE_IP_MANGLE | \ 70 | VE_IP_IPTABLES6 | VE_IP_FILTER6 | VE_IP_MANGLE6) 71 | 72 | #define VE_IP_ALL (~0ULL) 73 | #define VE_IP_NONE (0ULL) 74 | 75 | static inline bool mask_ipt_allow(__u64 permitted, __u64 mask) 76 | { 77 | return (permitted & mask) == mask; 78 | } 79 | 80 | #endif /* _LINUX_VZIPTABLE_DEFS_H */ 81 | -------------------------------------------------------------------------------- /src/include/vz/vzlist.h: -------------------------------------------------------------------------------- 1 | #ifndef __LINUX_VZLIST_H__ 2 | #define __LINUX_VZLIST_H__ 3 | 4 | #include 5 | #include 6 | 7 | #ifndef __KERNEL__ 8 | #include 9 | #endif 10 | 11 | #ifndef __ENVID_T_DEFINED__ 12 | typedef unsigned envid_t; 13 | #define __ENVID_T_DEFINED__ 14 | #endif 15 | 16 | struct vzlist_vepidctl { 17 | envid_t veid; 18 | unsigned int num; 19 | pid_t *pid; 20 | }; 21 | 22 | #define VZLISTTYPE 'x' 23 | #define VZCTL_GET_VEPIDS _IOR(VZLISTTYPE, 2, struct vzlist_vepidctl) 24 | 25 | #endif /* __LINUX_VZLIST_H__ */ 26 | -------------------------------------------------------------------------------- /src/include/vz/vzsyscalls.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999-2010, Parallels, Inc. All rights reserved. 3 | * 4 | */ 5 | 6 | #ifndef _VZSYSCALLS_H_ 7 | #define _VZSYSCALLS_H_ 8 | 9 | #include 10 | 11 | #ifdef __ia64__ 12 | #define __NR_setluid 1506 13 | #define __NR_setublimit 1507 14 | #define __NR_ioprio_set 1274 15 | #elif __x86_64__ 16 | #define __NR_setluid 501 17 | #define __NR_setublimit 502 18 | #define __NR_ioprio_set 251 19 | #define __NR_setns 308 20 | #elif __powerpc__ 21 | #define __NR_setluid 411 22 | #define __NR_setublimit 412 23 | #define __NR_ioprio_set 273 24 | #elif defined(__i386__) || defined(__sparc__) 25 | #define __NR_setluid 511 26 | #define __NR_setublimit 512 27 | #define __NR_setns 346 28 | #ifdef __sparc__ 29 | #define __NR_ioprio_set 196 30 | #else 31 | #define __NR_ioprio_set 289 32 | #endif 33 | #else 34 | #error "no syscall for this arch" 35 | #endif 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/include/vz/vztypes.h: -------------------------------------------------------------------------------- 1 | #ifndef _LINUX_TYPES_H 2 | #define _LINUX_TYPES_H 3 | 4 | #include 5 | 6 | #ifndef __ASSEMBLY__ 7 | #ifdef __KERNEL__ 8 | 9 | #define DECLARE_BITMAP(name,bits) \ 10 | unsigned long name[BITS_TO_LONGS(bits)] 11 | 12 | #endif 13 | 14 | #include 15 | 16 | #ifdef __KERNEL__ 17 | 18 | typedef __u32 __kernel_dev_t; 19 | 20 | typedef __kernel_fd_set fd_set; 21 | typedef __kernel_dev_t dev_t; 22 | typedef __kernel_ino_t ino_t; 23 | typedef __kernel_mode_t mode_t; 24 | typedef __kernel_nlink_t nlink_t; 25 | typedef __kernel_off_t off_t; 26 | typedef __kernel_pid_t pid_t; 27 | typedef __kernel_daddr_t daddr_t; 28 | typedef __kernel_key_t key_t; 29 | typedef __kernel_suseconds_t suseconds_t; 30 | typedef __kernel_timer_t timer_t; 31 | typedef __kernel_clockid_t clockid_t; 32 | typedef __kernel_mqd_t mqd_t; 33 | 34 | #ifndef __ENVID_T_DEFINED__ 35 | typedef unsigned envid_t; 36 | #define __ENVID_T_DEFINED__ 37 | #endif 38 | 39 | typedef _Bool bool; 40 | 41 | typedef __kernel_uid32_t uid_t; 42 | typedef __kernel_gid32_t gid_t; 43 | typedef __kernel_uid16_t uid16_t; 44 | typedef __kernel_gid16_t gid16_t; 45 | 46 | typedef unsigned long uintptr_t; 47 | 48 | #ifdef CONFIG_UID16 49 | /* This is defined by include/asm-{arch}/posix_types.h */ 50 | typedef __kernel_old_uid_t old_uid_t; 51 | typedef __kernel_old_gid_t old_gid_t; 52 | #endif /* CONFIG_UID16 */ 53 | 54 | #if defined(__GNUC__) 55 | typedef __kernel_loff_t loff_t; 56 | #endif 57 | 58 | /* 59 | * The following typedefs are also protected by individual ifdefs for 60 | * historical reasons: 61 | */ 62 | #ifndef _SIZE_T 63 | #define _SIZE_T 64 | typedef __kernel_size_t size_t; 65 | #endif 66 | 67 | #ifndef _SSIZE_T 68 | #define _SSIZE_T 69 | typedef __kernel_ssize_t ssize_t; 70 | #endif 71 | 72 | #ifndef _PTRDIFF_T 73 | #define _PTRDIFF_T 74 | typedef __kernel_ptrdiff_t ptrdiff_t; 75 | #endif 76 | 77 | #ifndef _TIME_T 78 | #define _TIME_T 79 | typedef __kernel_time_t time_t; 80 | #endif 81 | 82 | #ifndef _CLOCK_T 83 | #define _CLOCK_T 84 | typedef __kernel_clock_t clock_t; 85 | #endif 86 | 87 | #ifndef _CADDR_T 88 | #define _CADDR_T 89 | typedef __kernel_caddr_t caddr_t; 90 | #endif 91 | 92 | /* bsd */ 93 | typedef unsigned char u_char; 94 | typedef unsigned short u_short; 95 | typedef unsigned int u_int; 96 | typedef unsigned long u_long; 97 | 98 | /* sysv */ 99 | typedef unsigned char unchar; 100 | typedef unsigned short ushort; 101 | typedef unsigned int uint; 102 | typedef unsigned long ulong; 103 | 104 | #ifndef __BIT_TYPES_DEFINED__ 105 | #define __BIT_TYPES_DEFINED__ 106 | 107 | typedef __u8 u_int8_t; 108 | typedef __s8 int8_t; 109 | typedef __u16 u_int16_t; 110 | typedef __s16 int16_t; 111 | typedef __u32 u_int32_t; 112 | typedef __s32 int32_t; 113 | 114 | #endif /* !(__BIT_TYPES_DEFINED__) */ 115 | 116 | typedef __u8 uint8_t; 117 | typedef __u16 uint16_t; 118 | typedef __u32 uint32_t; 119 | 120 | #if defined(__GNUC__) 121 | typedef __u64 uint64_t; 122 | typedef __u64 u_int64_t; 123 | typedef __s64 int64_t; 124 | #endif 125 | 126 | /* 127 | * aligned_u64 should be used in defining kernel<->userspace ABIs to avoid 128 | * common 32/64-bit compat problems. 129 | * 64-bit values align to 4-byte boundaries on x86_32 (and possibly other 130 | * architectures) and to 8-byte boundaries on 64-bit architetures. The new 131 | * aligned_64 type enforces 8-byte alignment so that structs containing 132 | * aligned_64 values have the same alignment on 32-bit and 64-bit architectures. 133 | * No conversions are necessary between 32-bit user-space and a 64-bit kernel. 134 | */ 135 | #define aligned_u64 __u64 __attribute__((aligned(8))) 136 | #define aligned_be64 __be64 __attribute__((aligned(8))) 137 | #define aligned_le64 __le64 __attribute__((aligned(8))) 138 | 139 | /** 140 | * The type used for indexing onto a disc or disc partition. 141 | * 142 | * Linux always considers sectors to be 512 bytes long independently 143 | * of the devices real block size. 144 | * 145 | * blkcnt_t is the type of the inode's block count. 146 | */ 147 | #ifdef CONFIG_LBDAF 148 | typedef u64 sector_t; 149 | typedef u64 blkcnt_t; 150 | #else 151 | typedef unsigned long sector_t; 152 | typedef unsigned long blkcnt_t; 153 | #endif 154 | 155 | /* 156 | * The type of an index into the pagecache. Use a #define so asm/types.h 157 | * can override it. 158 | */ 159 | #ifndef pgoff_t 160 | #define pgoff_t unsigned long 161 | #endif 162 | 163 | #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 164 | typedef u64 dma_addr_t; 165 | #else 166 | typedef u32 dma_addr_t; 167 | #endif /* dma_addr_t */ 168 | 169 | #endif /* __KERNEL__ */ 170 | 171 | /* 172 | * Below are truly Linux-specific types that should never collide with 173 | * any application/library that wants linux/types.h. 174 | */ 175 | 176 | #ifdef __CHECKER__ 177 | #define __bitwise__ __attribute__((bitwise)) 178 | #else 179 | #define __bitwise__ 180 | #endif 181 | #ifdef __CHECK_ENDIAN__ 182 | #define __bitwise __bitwise__ 183 | #else 184 | #define __bitwise 185 | #endif 186 | 187 | typedef __u16 __bitwise __le16; 188 | typedef __u16 __bitwise __be16; 189 | typedef __u32 __bitwise __le32; 190 | typedef __u32 __bitwise __be32; 191 | typedef __u64 __bitwise __le64; 192 | typedef __u64 __bitwise __be64; 193 | 194 | typedef __u16 __bitwise __sum16; 195 | typedef __u32 __bitwise __wsum; 196 | 197 | /* this is a special 64bit data type that is 8-byte aligned */ 198 | #define __aligned_u64 __u64 __attribute__((aligned(8))) 199 | #define __aligned_be64 __be64 __attribute__((aligned(8))) 200 | #define __aligned_le64 __le64 __attribute__((aligned(8))) 201 | 202 | #ifdef __KERNEL__ 203 | typedef unsigned __bitwise__ gfp_t; 204 | typedef unsigned __bitwise__ fmode_t; 205 | 206 | #ifdef CONFIG_PHYS_ADDR_T_64BIT 207 | typedef u64 phys_addr_t; 208 | #else 209 | typedef u32 phys_addr_t; 210 | #endif 211 | 212 | typedef phys_addr_t resource_size_t; 213 | 214 | typedef struct { 215 | volatile int counter; 216 | } atomic_t; 217 | 218 | #ifdef CONFIG_64BIT 219 | typedef struct { 220 | volatile long counter; 221 | } atomic64_t; 222 | #endif 223 | 224 | struct list_head { 225 | struct list_head *next, *prev; 226 | }; 227 | 228 | struct ustat { 229 | __kernel_daddr_t f_tfree; 230 | __kernel_ino_t f_tinode; 231 | char f_fname[6]; 232 | char f_fpack[6]; 233 | }; 234 | 235 | #endif /* __KERNEL__ */ 236 | #endif /* __ASSEMBLY__ */ 237 | #endif /* _LINUX_TYPES_H */ 238 | -------------------------------------------------------------------------------- /src/include/xmalloc.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_XMALLOC_H__ 2 | #define __LIBCT_XMALLOC_H__ 3 | 4 | #include 5 | #include 6 | 7 | #include "log.h" 8 | 9 | #define __xalloc(op, size, ...) \ 10 | ({ \ 11 | void *___p = op( __VA_ARGS__ ); \ 12 | if (!___p) \ 13 | pr_err("%s: Can't allocate %li bytes\n", \ 14 | __func__, (long)(size)); \ 15 | ___p; \ 16 | }) 17 | 18 | #define xstrdup(str) __xalloc(strdup, strlen(str) + 1, str) 19 | #define xmalloc(size) __xalloc(malloc, size, size) 20 | #define xzalloc(size) __xalloc(calloc, size, 1, size) 21 | #define xrealloc(p, size) __xalloc(realloc, size, p, size) 22 | 23 | #define xfree(p) do { if (p) free(p); } while (0) 24 | 25 | #define xrealloc_safe(pptr, size) \ 26 | ({ \ 27 | int __ret = -1; \ 28 | void *new = xrealloc(*pptr, size); \ 29 | if (new) { \ 30 | *pptr = new; \ 31 | __ret = 0; \ 32 | } \ 33 | __ret; \ 34 | }) 35 | 36 | #define memzero_p(p) memset(p, 0, sizeof(*p)) 37 | #define memzero(p, size) memset(p, 0, size) 38 | 39 | #endif /* __LIBCT_XMALLOC_H__ */ 40 | -------------------------------------------------------------------------------- /src/linux_kernel.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "linux-kernel.h" 10 | #include "cgroups.h" 11 | #include "log.h" 12 | 13 | unsigned long kernel_ns_mask; 14 | 15 | int linux_get_ns_mask(void) 16 | { 17 | DIR *d; 18 | 19 | d = opendir("/proc/self/ns"); 20 | if (d) { 21 | struct dirent *de; 22 | 23 | while ((de = readdir(d)) != NULL) { 24 | if (!strcmp(de->d_name, ".")) 25 | continue; 26 | if (!strcmp(de->d_name, "..")) 27 | continue; 28 | 29 | if (!strcmp(de->d_name, "ipc")) 30 | kernel_ns_mask |= CLONE_NEWIPC; 31 | else if (!strcmp(de->d_name, "net")) 32 | kernel_ns_mask |= CLONE_NEWNET; 33 | else if (!strcmp(de->d_name, "mnt")) 34 | kernel_ns_mask |= CLONE_NEWNS; 35 | else if (!strcmp(de->d_name, "pid")) 36 | kernel_ns_mask |= CLONE_NEWPID; 37 | else if (!strcmp(de->d_name, "uts")) 38 | kernel_ns_mask |= CLONE_NEWUTS; 39 | else if (!strcmp(de->d_name, "user")) 40 | kernel_ns_mask |= CLONE_NEWUSER; 41 | } 42 | } 43 | 44 | closedir(d); 45 | return 0; 46 | } 47 | 48 | int linux_get_cgroup_mounts(void) 49 | { 50 | int ret = 0; 51 | FILE *f; 52 | struct mntent *me; 53 | 54 | f = setmntent("/proc/mounts", "r"); 55 | if (!f) 56 | return -1; 57 | 58 | while ((me = getmntent(f)) != NULL) { 59 | if (!strcmp(me->mnt_type, "cgroup")) { 60 | ret = cgroup_add_mount(me); 61 | if (ret) 62 | break; 63 | } 64 | } 65 | 66 | fclose(f); 67 | return ret; 68 | } 69 | 70 | int linux_get_last_capability(void) 71 | { 72 | FILE *f; 73 | static int last_cap = -1; 74 | int ret; 75 | 76 | if (last_cap > 0) 77 | return last_cap; 78 | 79 | f = fopen("/proc/sys/kernel/cap_last_cap", "r"); 80 | if (f == NULL) { 81 | pr_perror("Unable to open /proc/sys/kernel/cap_last_cap"); 82 | return -1; 83 | } 84 | ret = fscanf(f, "%d", &last_cap); 85 | fclose(f); 86 | if (ret != 1) { 87 | pr_err("Unable to parse /proc/sys/kernel/cap_last_cap"); 88 | return -1; 89 | } 90 | 91 | return last_cap; 92 | } 93 | 94 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #include "compiler.h" 16 | #include "types.h" 17 | 18 | #include "log.h" 19 | 20 | static unsigned int current_loglevel = DEFAULT_LOGLEVEL; 21 | static char logbuf[PAGE_SIZE]; 22 | static int logfd = -1; 23 | 24 | static void log_set_fd(int fd) 25 | { 26 | if (logfd != -1) 27 | close(logfd); 28 | logfd = dup(fd); 29 | } 30 | 31 | static void loglevel_set(unsigned int loglevel) 32 | { 33 | current_loglevel = loglevel; 34 | } 35 | 36 | void libct_log_init(int fd, unsigned int level) 37 | { 38 | log_set_fd(fd); 39 | loglevel_set(level); 40 | } 41 | 42 | static inline bool pr_quelled(unsigned int loglevel) 43 | { 44 | return loglevel != LOG_MSG && loglevel > current_loglevel; 45 | } 46 | 47 | static void __print_on_level(unsigned int loglevel, const char *format, va_list params) 48 | { 49 | size_t size; 50 | 51 | if (logfd < 0) 52 | return; 53 | 54 | size = vsnprintf(logbuf, PAGE_SIZE, format, params); 55 | write(logfd, logbuf, size); 56 | } 57 | 58 | void print_on_level(unsigned int loglevel, const char *format, ...) 59 | { 60 | va_list params; 61 | int save_errno = errno; 62 | 63 | if (pr_quelled(loglevel)) 64 | return; 65 | 66 | va_start(params, format); 67 | __print_on_level(loglevel, format, params); 68 | va_end(params); 69 | 70 | errno = save_errno; 71 | } 72 | -------------------------------------------------------------------------------- /src/lsm/apparmor.c: -------------------------------------------------------------------------------- 1 | /* apparmor 2 | * 3 | * Copyright © 2012 Serge Hallyn . 4 | * Copyright © 2012 Canonical Ltd. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "log.h" 33 | #include "lsm/lsm.h" 34 | 35 | /* set by lsm_apparmor_drv_init if true */ 36 | static int aa_enabled = 0; 37 | 38 | static int mount_features_enabled = 0; 39 | 40 | #define AA_DEF_PROFILE "lxc-container-default" 41 | #define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask" 42 | #define AA_ENABLED_FILE "/sys/module/apparmor/parameters/enabled" 43 | 44 | static bool check_mount_feature_enabled(void) 45 | { 46 | return mount_features_enabled == 1; 47 | } 48 | 49 | static void load_mount_features_enabled(void) 50 | { 51 | struct stat statbuf; 52 | int ret; 53 | 54 | ret = stat(AA_MOUNT_RESTR, &statbuf); 55 | if (ret == 0) 56 | mount_features_enabled = 1; 57 | } 58 | 59 | /* aa_getcon is not working right now. Use our hand-rolled version below */ 60 | static int apparmor_enabled(void) 61 | { 62 | FILE *fin; 63 | char e; 64 | int ret; 65 | 66 | fin = fopen(AA_ENABLED_FILE, "r"); 67 | if (!fin) 68 | return 0; 69 | ret = fscanf(fin, "%c", &e); 70 | fclose(fin); 71 | if (ret == 1 && e == 'Y') { 72 | load_mount_features_enabled(); 73 | return 1; 74 | } 75 | 76 | return 0; 77 | } 78 | 79 | static char *apparmor_process_label_get(pid_t pid) 80 | { 81 | char path[100], *space; 82 | int ret; 83 | char *buf = NULL, *newbuf; 84 | int sz = 0; 85 | FILE *f; 86 | 87 | ret = snprintf(path, 100, "/proc/%d/attr/current", pid); 88 | if (ret < 0 || ret >= 100) { 89 | pr_err("path name too long"); 90 | return NULL; 91 | } 92 | again: 93 | f = fopen(path, "r"); 94 | if (!f) { 95 | pr_err("opening %s", path); 96 | if (buf) 97 | free(buf); 98 | return NULL; 99 | } 100 | sz += 1024; 101 | newbuf = realloc(buf, sz); 102 | if (!newbuf) { 103 | free(buf); 104 | pr_err("out of memory"); 105 | fclose(f); 106 | return NULL; 107 | } 108 | buf = newbuf; 109 | memset(buf, 0, sz); 110 | ret = fread(buf, 1, sz - 1, f); 111 | fclose(f); 112 | if (ret < 0) { 113 | pr_err("reading %s", path); 114 | free(buf); 115 | return NULL; 116 | } 117 | if (ret >= sz) 118 | goto again; 119 | space = index(buf, '\n'); 120 | if (space) 121 | *space = '\0'; 122 | space = index(buf, ' '); 123 | if (space) 124 | *space = '\0'; 125 | return buf; 126 | } 127 | 128 | static int apparmor_am_unconfined(void) 129 | { 130 | char *p = apparmor_process_label_get(getpid()); 131 | int ret = 0; 132 | if (!p || strcmp(p, "unconfined") == 0) 133 | ret = 1; 134 | if (p) 135 | free(p); 136 | return ret; 137 | } 138 | 139 | /* 140 | * apparmor_process_label_set: Set AppArmor process profile 141 | * 142 | * @label : the profile to set 143 | * @conf : the container configuration to use @label is NULL 144 | * @default : use the default profile if label is NULL 145 | * @on_exec : this is ignored. Apparmor profile will be changed immediately 146 | * 147 | * Returns 0 on success, < 0 on failure 148 | * 149 | * Notes: This relies on /proc being available. 150 | */ 151 | static int apparmor_process_label_set(const char *inlabel, 152 | int use_default, int on_exec) 153 | { 154 | const char *label = inlabel; 155 | 156 | if (!aa_enabled) 157 | return 0; 158 | 159 | if (!label) { 160 | if (use_default) 161 | label = AA_DEF_PROFILE; 162 | else 163 | label = "unconfined"; 164 | } 165 | 166 | if (!check_mount_feature_enabled() && strcmp(label, "unconfined") != 0) { 167 | pr_warn("Incomplete AppArmor support in your kernel"); 168 | return -1; 169 | } 170 | 171 | 172 | if (strcmp(label, "unconfined") == 0 && apparmor_am_unconfined()) { 173 | pr_info("apparmor profile unchanged"); 174 | return 0; 175 | } 176 | 177 | if (aa_change_profile(label) < 0) { 178 | pr_err("failed to change apparmor profile to %s", label); 179 | return -1; 180 | } 181 | 182 | pr_info("changed apparmor profile to %s", label); 183 | return 0; 184 | } 185 | 186 | static struct lsm_drv apparmor_drv = { 187 | .name = "AppArmor", 188 | .enabled = apparmor_enabled, 189 | .process_label_get = apparmor_process_label_get, 190 | .process_label_set = apparmor_process_label_set, 191 | }; 192 | 193 | struct lsm_drv *lsm_apparmor_drv_init(void) 194 | { 195 | if (!apparmor_enabled()) 196 | return NULL; 197 | aa_enabled = 1; 198 | return &apparmor_drv; 199 | } 200 | -------------------------------------------------------------------------------- /src/lsm/lsm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lxc: linux Container library 3 | * 4 | * Authors: 5 | * Copyright © 2012 Serge Hallyn 6 | * Copyright © 2012 Canonical Ltd. 7 | * Dwight Engen 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 | */ 23 | 24 | #if HAVE_APPARMOR || HAVE_SELINUX 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "log.h" 33 | #include "lsm/lsm.h" 34 | 35 | static struct lsm_drv *drv = NULL; 36 | 37 | extern struct lsm_drv *lsm_apparmor_drv_init(void); 38 | extern struct lsm_drv *lsm_selinux_drv_init(void); 39 | extern struct lsm_drv *lsm_nop_drv_init(void); 40 | 41 | __attribute__((constructor)) 42 | void lsm_init(void) 43 | { 44 | if (drv) { 45 | pr_msg("LSM security driver %s", drv->name); 46 | return; 47 | } 48 | 49 | #if HAVE_APPARMOR 50 | drv = lsm_apparmor_drv_init(); 51 | #endif 52 | #if HAVE_SELINUX 53 | if (!drv) 54 | drv = lsm_selinux_drv_init(); 55 | #endif 56 | 57 | if (!drv) 58 | drv = lsm_nop_drv_init(); 59 | pr_msg("Initialized LSM security driver %s", drv->name); 60 | } 61 | 62 | int lsm_enabled(void) 63 | { 64 | if (drv) 65 | return drv->enabled(); 66 | return 0; 67 | } 68 | 69 | const char *lsm_name(void) 70 | { 71 | if (drv) 72 | return drv->name; 73 | return "none"; 74 | } 75 | 76 | char *lsm_process_label_get(pid_t pid) 77 | { 78 | if (!drv) { 79 | pr_err("LSM driver not inited"); 80 | return NULL; 81 | } 82 | return drv->process_label_get(pid); 83 | } 84 | 85 | int lsm_process_label_set(const char *label, 86 | int use_default, int on_exec) 87 | { 88 | if (!drv) { 89 | pr_err("LSM driver not inited"); 90 | return -1; 91 | } 92 | return drv->process_label_set(label, use_default, on_exec); 93 | } 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /src/lsm/lsm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * lxc: linux Container library 3 | * 4 | * Copyright © 2013 Oracle. 5 | * 6 | * Authors: 7 | * Dwight Engen 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 | */ 23 | 24 | #ifndef __lxc_lsm_h 25 | #define __lxc_lsm_h 26 | 27 | struct lxc_conf; 28 | 29 | #include 30 | 31 | struct lsm_drv { 32 | const char *name; 33 | 34 | int (*enabled)(void); 35 | char *(*process_label_get)(pid_t pid); 36 | int (*process_label_set)(const char *label, 37 | int use_default, int on_exec); 38 | }; 39 | 40 | #if HAVE_APPARMOR || HAVE_SELINUX 41 | void lsm_init(void); 42 | int lsm_enabled(void); 43 | const char *lsm_name(void); 44 | char *lsm_process_label_get(pid_t pid); 45 | int lsm_process_label_set(const char *label, 46 | int use_default, int on_exec); 47 | #else 48 | static inline void lsm_init(void) { } 49 | static inline int lsm_enabled(void) { return 0; } 50 | static inline const char *lsm_name(void) { return "none"; } 51 | static inline char *lsm_process_label_get(pid_t pid) { return NULL; } 52 | static inline int lsm_process_label_set(const char *label, 53 | int use_default, int on_exec) { return 0; } 54 | #endif 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/lsm/nop.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lxc: linux Container library 3 | * 4 | * Copyright © 2013 Oracle. 5 | * 6 | * Authors: 7 | * Dwight Engen 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 | */ 23 | 24 | #include 25 | #include "lsm/lsm.h" 26 | 27 | static char *nop_process_label_get(pid_t pid) 28 | { 29 | return NULL; 30 | } 31 | 32 | static int nop_process_label_set(const char *label, 33 | int use_default, int on_exec) 34 | { 35 | return 0; 36 | } 37 | 38 | static int nop_enabled(void) 39 | { 40 | return 0; 41 | } 42 | 43 | static struct lsm_drv nop_drv = { 44 | .name = "nop", 45 | .enabled = nop_enabled, 46 | .process_label_get = nop_process_label_get, 47 | .process_label_set = nop_process_label_set, 48 | }; 49 | 50 | struct lsm_drv *lsm_nop_drv_init(void) 51 | { 52 | return &nop_drv; 53 | } 54 | -------------------------------------------------------------------------------- /src/lsm/selinux.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lxc: linux Container library 3 | * 4 | * Copyright © 2013 Oracle. 5 | * 6 | * Authors: 7 | * Dwight Engen 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "log.h" 32 | #include "lsm/lsm.h" 33 | 34 | #define DEFAULT_LABEL "unconfined_t" 35 | 36 | /* 37 | * selinux_process_label_get: Get SELinux context of a process 38 | * 39 | * @pid : the pid to get, or 0 for self 40 | * 41 | * Returns the context of the given pid. The caller must free() 42 | * the returned string. 43 | * 44 | * Note that this relies on /proc being available. 45 | */ 46 | static char *selinux_process_label_get(pid_t pid) 47 | { 48 | security_context_t ctx; 49 | char *label; 50 | 51 | if (getpidcon_raw(pid, &ctx) < 0) { 52 | pr_err("failed to get SELinux context for pid %d", pid); 53 | return NULL; 54 | } 55 | label = strdup((char *)ctx); 56 | freecon(ctx); 57 | return label; 58 | } 59 | 60 | /* 61 | * selinux_process_label_set: Set SELinux context of a process 62 | * 63 | * @label : label string 64 | * @default : use the default context if label is NULL 65 | * @on_exec : the new context will take effect on exec(2) not immediately 66 | * 67 | * Returns 0 on success, < 0 on failure 68 | * 69 | * Notes: This relies on /proc being available. 70 | */ 71 | static int selinux_process_label_set(const char *inlabel, 72 | int use_default, int on_exec) 73 | { 74 | const char *label = inlabel; 75 | if (!label) { 76 | if (use_default) 77 | label = DEFAULT_LABEL; 78 | else 79 | return -1; 80 | } 81 | if (!strcmp(label, "unconfined_t")) 82 | return 0; 83 | 84 | if (on_exec) { 85 | if (setexeccon_raw((char *)label) < 0) { 86 | pr_err("failed to set new SELinux exec context %s", label); 87 | return -1; 88 | } 89 | } else { 90 | if (setcon_raw((char *)label) < 0) { 91 | pr_err("failed to set new SELinux context %s", label); 92 | return -1; 93 | } 94 | } 95 | 96 | pr_info("changed SELinux%s context to %s", on_exec ? " exec" : "", label); 97 | return 0; 98 | } 99 | 100 | static struct lsm_drv selinux_drv = { 101 | .name = "SELinux", 102 | .enabled = is_selinux_enabled, 103 | .process_label_get = selinux_process_label_get, 104 | .process_label_set = selinux_process_label_set, 105 | }; 106 | 107 | struct lsm_drv *lsm_selinux_drv_init(void) 108 | { 109 | if (!is_selinux_enabled()) 110 | return NULL; 111 | return &selinux_drv; 112 | } 113 | -------------------------------------------------------------------------------- /src/namespaces.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "namespaces.h" 7 | #include "vzsyscalls.h" 8 | #include "bug.h" 9 | #include "log.h" 10 | 11 | struct ns_desc pid_ns = { 12 | .name = "pid", 13 | .cflag = CLONE_NEWPID, 14 | .idx = 0, 15 | }; 16 | 17 | struct ns_desc net_ns = { 18 | .name = "net", 19 | .cflag = CLONE_NEWNET, 20 | .idx = 1, 21 | }; 22 | 23 | static struct ns_desc ipc_ns = { 24 | .name = "ipc", 25 | .cflag = CLONE_NEWIPC, 26 | .idx = 2, 27 | }; 28 | 29 | static struct ns_desc uts_ns = { 30 | .name = "uts", 31 | .cflag = CLONE_NEWUTS, 32 | .idx = 3, 33 | }; 34 | 35 | static struct ns_desc mnt_ns = { 36 | .name = "mnt", 37 | .cflag = CLONE_NEWNS, 38 | .idx = 4, 39 | }; 40 | 41 | static struct ns_desc user_ns = { 42 | .name = "user", 43 | .cflag = CLONE_NEWUSER, 44 | .idx = 5, 45 | }; 46 | 47 | struct ns_desc *namespaces[6] = { 48 | &pid_ns, 49 | &net_ns, 50 | &ipc_ns, 51 | &uts_ns, 52 | &mnt_ns, 53 | /* 54 | * user_ns must be the last one. After switching in an user namespace, 55 | * we may not have enough permissions to enter in other namespaces. 56 | */ 57 | &user_ns, 58 | }; 59 | 60 | int setns(int fd, int nstype) __attribute__((weak)); 61 | 62 | static int libct_setns(int fd, int nstype) 63 | { 64 | int ret; 65 | 66 | if (setns) 67 | ret = setns(fd, nstype); 68 | else 69 | ret = syscall(__NR_setns, fd, nstype); 70 | 71 | if (ret) 72 | pr_perror("Unable to switch namespace %d", nstype); 73 | 74 | return ret; 75 | } 76 | 77 | int switch_ns(int pid, struct ns_desc *nd, int *rst) 78 | { 79 | char buf[32]; 80 | int nsfd; 81 | int ret = -1; 82 | 83 | snprintf(buf, sizeof(buf), "/proc/%d/ns/%s", pid, nd->name); 84 | nsfd = open(buf, O_RDONLY); 85 | if (nsfd < 0) { 86 | pr_perror("Unable to open %s", buf); 87 | goto err_ns; 88 | } 89 | 90 | if (rst) { 91 | snprintf(buf, sizeof(buf), "/proc/self/ns/%s", nd->name); 92 | *rst = open(buf, O_RDONLY); 93 | if (*rst < 0) { 94 | pr_perror("Unable to open %s", buf); 95 | goto err_rst; 96 | } 97 | } 98 | 99 | ret = libct_setns(nsfd, nd->cflag); 100 | if (ret < 0) { 101 | pr_perror("Unable setns into %s:%d", nd->name, pid); 102 | goto err_set; 103 | } 104 | 105 | close(nsfd); 106 | return 0; 107 | 108 | err_set: 109 | if (rst) 110 | close(*rst); 111 | err_rst: 112 | close(nsfd); 113 | err_ns: 114 | return -1; 115 | } 116 | 117 | void restore_ns(int rst, struct ns_desc *nd) 118 | { 119 | if (libct_setns(rst, nd->cflag)) 120 | BUG(); 121 | close(rst); 122 | } 123 | -------------------------------------------------------------------------------- /src/net_util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "net_util.h" 13 | #include "xmalloc.h" 14 | #include "util.h" 15 | 16 | /* 17 | * Generic Linux networking management 18 | */ 19 | 20 | struct nl_sock *net_sock_open() 21 | { 22 | struct nl_sock *sk; 23 | int err; 24 | 25 | sk = nl_socket_alloc(); 26 | if (sk == NULL) 27 | return NULL; 28 | 29 | if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { 30 | nl_socket_free(sk); 31 | pr_err("Unable to connect socket: %s", nl_geterror(err)); 32 | return NULL; 33 | } 34 | 35 | return sk; 36 | } 37 | 38 | void net_sock_close(struct nl_sock *sk) 39 | { 40 | if (sk == NULL) 41 | return; 42 | 43 | nl_close(sk); 44 | nl_socket_free(sk); 45 | 46 | return; 47 | } 48 | 49 | struct nl_cache *net_get_link_cache(struct nl_sock *sk) 50 | { 51 | struct nl_cache *cache; 52 | int err; 53 | 54 | err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &cache); 55 | if (err) { 56 | pr_err("Unable to alloc link cache: %s", nl_geterror(err)); 57 | return NULL; 58 | } 59 | 60 | return cache; 61 | } 62 | 63 | static int __net_add_ip_addr(struct nl_sock *sk, ct_net_t n, char *saddr) 64 | { 65 | struct rtnl_addr *addr; 66 | struct nl_addr *l; 67 | int err, ret = -1; 68 | 69 | err = nl_addr_parse(saddr, AF_UNSPEC, &l); 70 | if (err) { 71 | pr_err("Unable to parse address: %s\n", nl_geterror(err)); 72 | return -1; 73 | } 74 | 75 | addr = rtnl_addr_alloc(); 76 | if (addr == NULL) 77 | goto err; 78 | 79 | rtnl_addr_set_local(addr, l); 80 | rtnl_addr_set_ifindex(addr, n->ifidx); 81 | err = rtnl_addr_add(sk, addr, 0); 82 | if (err) { 83 | pr_err("Unable to add %s: %s\n", saddr, nl_geterror(err)); 84 | goto err; 85 | } 86 | 87 | ret = 0; 88 | err: 89 | nl_addr_put(l); 90 | rtnl_addr_put(addr); 91 | return ret; 92 | } 93 | 94 | int net_link_apply(char *name, ct_net_t n) 95 | { 96 | struct rtnl_link *link = NULL, *orig = NULL; 97 | struct nl_cache *cache = NULL; 98 | struct nl_sock *sk; 99 | int err = -1; 100 | 101 | sk = net_sock_open(); 102 | if (sk == NULL) 103 | return -1; 104 | 105 | cache = net_get_link_cache(sk); 106 | if (cache == NULL) 107 | goto free; 108 | 109 | orig = rtnl_link_get_by_name(cache, name); 110 | if (orig == NULL) 111 | goto free; 112 | 113 | link = rtnl_link_alloc(); 114 | if (link == NULL) 115 | goto free; 116 | 117 | rtnl_link_set_name(link, n->name); 118 | 119 | if (n->addr) { 120 | struct nl_addr* addr; 121 | 122 | addr = nl_addr_build(AF_LLC, ether_aton(n->addr), ETH_ALEN); 123 | if (addr == NULL) 124 | goto free; 125 | rtnl_link_set_addr(link, addr); 126 | } 127 | 128 | if (n->master) { 129 | int idx; 130 | 131 | idx = rtnl_link_name2i(cache, n->master); 132 | if (idx == 0) 133 | goto free; 134 | 135 | rtnl_link_set_master(link, idx); 136 | } 137 | 138 | rtnl_link_set_flags(link, IFF_UP); 139 | 140 | err = rtnl_link_change(sk, orig, link, 0); 141 | if (err) { 142 | pr_err("Unable to change link %s: %s", n->name, nl_geterror(err)); 143 | goto free; 144 | } 145 | 146 | err = -1; 147 | if (nl_cache_refill(sk, cache)) 148 | goto free; 149 | 150 | n->ifidx = rtnl_link_name2i(cache, n->name); 151 | if ( n->ifidx == 0) 152 | goto free; 153 | 154 | if (net_add_ip_addrs(sk, n)) 155 | goto free; 156 | err = 0; 157 | free: 158 | rtnl_link_put(link); 159 | rtnl_link_put(orig); 160 | nl_cache_put(cache); 161 | net_sock_close(sk); 162 | return err; 163 | } 164 | 165 | 166 | int net_add_ip_addrs(struct nl_sock *sk, ct_net_t n) 167 | { 168 | struct ct_net_ip_addr *addr; 169 | 170 | list_for_each_entry(addr, &n->ip_addrs, l) { 171 | if (__net_add_ip_addr(sk, n, addr->addr)) 172 | goto err; 173 | } 174 | 175 | return 0; 176 | err: 177 | // FIXME rollback 178 | return -1; 179 | } 180 | 181 | 182 | void ct_net_init(ct_net_t n, const struct ct_net_ops *ops) 183 | { 184 | INIT_LIST_HEAD(&n->ip_addrs); 185 | n->name = NULL; 186 | n->ops = ops; 187 | } 188 | 189 | void ct_net_clean(ct_net_t n) 190 | { 191 | struct ct_net_ip_addr *addr, *t; 192 | 193 | xfree(n->name); 194 | xfree(n->addr); 195 | xfree(n->master); 196 | 197 | list_for_each_entry_safe(addr, t, &n->ip_addrs, l) { 198 | xfree(addr->addr); 199 | xfree(addr); 200 | } 201 | } 202 | 203 | int net_dev_set_mtu(ct_net_t n, int mtu) 204 | { 205 | n->mtu = mtu; 206 | 207 | return 0; 208 | } 209 | 210 | int net_dev_set_mac_addr(ct_net_t n, char *addr) 211 | { 212 | return set_string(&n->addr, addr); 213 | } 214 | 215 | int net_dev_set_master(ct_net_t n, char *master) 216 | { 217 | return set_string(&n->master, master); 218 | } 219 | 220 | int net_dev_add_ip_addr(ct_net_t n, char *addr) 221 | { 222 | struct ct_net_ip_addr *a; 223 | 224 | a = xzalloc(sizeof(*a)); 225 | if (a == NULL) 226 | return -1; 227 | 228 | a->addr = xstrdup(addr); 229 | if (a->addr == NULL) { 230 | xfree(a); 231 | return -1; 232 | } 233 | 234 | list_add(&a->l, &n->ip_addrs); 235 | 236 | return 0; 237 | } 238 | 239 | void veth_stop(struct container *ct, struct ct_net *n) 240 | { 241 | /* TODO: Ask Pavel about veth stop algo 242 | * FIXME -- don't destroy veth here, keep it across 243 | * container's restarts. This needs checks in the 244 | * veth_pair_create() for existance. 245 | */ 246 | } 247 | 248 | int veth_match(struct ct_net *n, void *arg) 249 | { 250 | struct ct_net_veth *vn = cn2vn(n); 251 | struct ct_net_veth_arg *va = arg; 252 | 253 | /* Matching hostname should be enough */ 254 | return !strcmp(vn->peer.name, va->host_name); 255 | } 256 | 257 | void veth_free(struct ct_net_veth *vn) 258 | { 259 | ct_net_clean(&vn->n); 260 | ct_net_clean(&vn->peer); 261 | xfree(vn); 262 | } 263 | -------------------------------------------------------------------------------- /src/route.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "uapi/libct.h" 7 | 8 | #include "namespaces.h" 9 | #include "xmalloc.h" 10 | #include "util.h" 11 | #include "list.h" 12 | #include "err.h" 13 | #include "net.h" 14 | #include "ct.h" 15 | #include "net_util.h" 16 | 17 | void net_route_nh_free(ct_net_route_nh_t nh) 18 | { 19 | if (nh == NULL) 20 | return; 21 | 22 | xfree(nh->gateway); 23 | xfree(nh->dev); 24 | xfree(nh); 25 | } 26 | 27 | void net_route_free(struct ct_net_route *r) 28 | { 29 | ct_net_route_nh_t nh, t; 30 | 31 | if (r == NULL) 32 | return; 33 | 34 | list_for_each_entry_safe(nh, t, &r->nhs, l) 35 | net_route_nh_free(nh); 36 | 37 | xfree(r->dst); 38 | xfree(r->src); 39 | xfree(r->dev); 40 | xfree(r); 41 | } 42 | 43 | 44 | int net_route_nh_add(struct rtnl_route *route, struct ct_net_route_nh *n, struct nl_cache *cache) 45 | { 46 | struct rtnl_nexthop *nh; 47 | int err; 48 | 49 | nh = rtnl_route_nh_alloc(); 50 | if (nh == NULL) 51 | return -1; 52 | 53 | if (n->dev) { 54 | int idx; 55 | 56 | idx = rtnl_link_name2i(cache, n->dev); 57 | if (idx == 0) 58 | goto free; 59 | 60 | rtnl_route_nh_set_ifindex(nh, idx); 61 | } 62 | 63 | if (n->gateway) { 64 | struct nl_addr *addr; 65 | 66 | err = nl_addr_parse(n->gateway, AF_UNSPEC, &addr); 67 | if (err) { 68 | pr_err("Unable to parse %s: %s", n->gateway, nl_geterror(err)); 69 | goto free; 70 | } 71 | 72 | rtnl_route_nh_set_gateway(nh, addr); 73 | nl_addr_put(addr); 74 | } 75 | 76 | rtnl_route_add_nexthop(route, nh); 77 | return 0; 78 | free: 79 | rtnl_route_nh_free(nh); 80 | return -1; 81 | } 82 | 83 | int net_route_add(struct nl_sock *sk, struct nl_cache *cache, struct ct_net_route *r) 84 | { 85 | struct ct_net_route_nh *n; 86 | struct rtnl_route *route; 87 | struct nl_addr *addr; 88 | int err, ret = -1; 89 | 90 | route = rtnl_route_alloc(); 91 | if (route == NULL) 92 | return -1; 93 | 94 | if (r->src) { 95 | err = nl_addr_parse(r->src, AF_UNSPEC, &addr); 96 | if (err) { 97 | pr_err("Unable to parse %s: %s", r->src, nl_geterror(err)); 98 | goto out; 99 | } 100 | err = rtnl_route_set_src(route, addr); 101 | nl_addr_put(addr); 102 | if (err) { 103 | pr_err("Unable to set %s: %s", r->src, nl_geterror(err)); 104 | goto out; 105 | } 106 | } 107 | 108 | if (r->dst) { 109 | err = nl_addr_parse(r->dst, AF_UNSPEC, &addr); 110 | if (err) { 111 | pr_err("Unable to parse %s: %s", r->dst, nl_geterror(err)); 112 | goto out; 113 | } 114 | err = rtnl_route_set_dst(route, addr); 115 | nl_addr_put(addr); 116 | if (err) { 117 | pr_err("Unable to set %s: %s", r->dst, nl_geterror(err)); 118 | goto out; 119 | } 120 | } 121 | 122 | list_for_each_entry(n, &r->nhs, l) 123 | if (net_route_nh_add(route, n, cache)) 124 | goto out; 125 | 126 | if (r->dev) { 127 | int idx; 128 | 129 | idx = rtnl_link_name2i(cache, r->dev); 130 | if (idx == 0) 131 | goto out; 132 | 133 | rtnl_route_set_iif(route, idx); 134 | } 135 | 136 | err = rtnl_route_add(sk, route, NLM_F_EXCL); 137 | if (err) { 138 | pr_err("Unable to add route: %s\n", nl_geterror(err)); 139 | goto out; 140 | } 141 | 142 | ret = 0; 143 | out: 144 | rtnl_route_put(route); 145 | 146 | return ret; 147 | } 148 | 149 | int net_route_setup(struct container *ct) 150 | { 151 | struct ct_net_route *r; 152 | int rst, ret = -1; 153 | struct nl_sock *sk; 154 | struct nl_cache *cache; 155 | 156 | if (list_empty(&ct->ct_net_routes)) 157 | return 0; 158 | 159 | if (switch_ns(ct->p.pid, &net_ns, &rst)) 160 | return -1; 161 | 162 | sk = net_sock_open(); 163 | restore_ns(rst, &net_ns); 164 | if (sk == NULL) 165 | return -1; 166 | 167 | cache = net_get_link_cache(sk); 168 | if (cache == NULL) 169 | goto out; 170 | 171 | list_for_each_entry(r, &ct->ct_net_routes, l) { 172 | ret = net_route_add(sk, cache, r); 173 | if (ret) 174 | goto out; 175 | } 176 | 177 | ret = 0; 178 | out: 179 | nl_cache_put(cache); 180 | net_sock_close(sk); 181 | 182 | return ret; 183 | } 184 | 185 | void net_route_release(struct container *ct) 186 | { 187 | struct ct_net_route *r, *t; 188 | list_for_each_entry_safe(r, t, &ct->ct_net_routes, l) 189 | net_route_free(r); 190 | } 191 | 192 | ct_net_route_t local_net_route_add(ct_handler_t h) 193 | { 194 | struct container *ct = cth2ct(h); 195 | struct ct_net_route *r; 196 | 197 | r = xzalloc(sizeof(*r)); 198 | if (r == NULL) 199 | return NULL; 200 | 201 | INIT_LIST_HEAD(&r->nhs); 202 | 203 | list_add(&r->l, &ct->ct_net_routes); 204 | 205 | return r; 206 | } 207 | 208 | ct_net_route_t libct_net_route_add(ct_handler_t ct) 209 | { 210 | return ct->ops->net_route_add(ct); 211 | } 212 | 213 | int libct_net_route_set_src(ct_net_route_t r, char *addr) 214 | { 215 | return set_string(&r->src, addr); 216 | } 217 | 218 | int libct_net_route_set_dst(ct_net_route_t r, char *addr) 219 | { 220 | return set_string(&r->dst, addr); 221 | } 222 | 223 | int libct_net_route_set_dev(ct_net_route_t r, char *dev) 224 | { 225 | return set_string(&r->dev, dev); 226 | } 227 | 228 | ct_net_route_nh_t libct_net_route_add_nh(ct_net_route_t r) 229 | { 230 | ct_net_route_nh_t nh; 231 | 232 | nh = xzalloc(sizeof(*nh)); 233 | if (nh == NULL) 234 | return NULL; 235 | 236 | list_add(&nh->l, &r->nhs); 237 | 238 | return nh; 239 | } 240 | 241 | int libct_net_route_nh_set_gw(ct_net_route_nh_t nh, char *addr) 242 | { 243 | return set_string(&nh->gateway, addr); 244 | } 245 | 246 | int libct_net_route_nh_set_dev(ct_net_route_nh_t nh, char *dev) 247 | { 248 | return set_string(&nh->dev, dev); 249 | } 250 | -------------------------------------------------------------------------------- /src/security.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | 13 | #include "uapi/libct.h" 14 | 15 | #include "linux-kernel.h" 16 | #include "security.h" 17 | #include "xmalloc.h" 18 | #include "list.h" 19 | #include "log.h" 20 | #include "ct.h" 21 | 22 | static int apply_bset(uint64_t mask) 23 | { 24 | int i, last_cap; 25 | 26 | last_cap = linux_get_last_capability(); 27 | if (last_cap < 0) 28 | return -1; 29 | 30 | for (i = 0; i <= last_cap; i++) { 31 | if (mask & (1ULL << i)) 32 | continue; 33 | 34 | if (prctl(PR_CAPBSET_DROP, i, 0, 0, 0) == -1) 35 | return -1; 36 | } 37 | 38 | return 0; 39 | } 40 | 41 | extern int capget(cap_user_header_t header, const cap_user_data_t data); 42 | extern int capset(cap_user_header_t header, const cap_user_data_t data); 43 | 44 | static int apply_all_caps(uint64_t mask) 45 | { 46 | struct __user_cap_header_struct header; 47 | struct __user_cap_data_struct data[2]; /* as of .._VERSION_3 */ 48 | 49 | memset(&header, 0, sizeof(header)); 50 | capget(&header, data); 51 | switch (header.version) { 52 | case _LINUX_CAPABILITY_VERSION_1: 53 | case _LINUX_CAPABILITY_VERSION_2: 54 | case _LINUX_CAPABILITY_VERSION_3: 55 | break; 56 | default: 57 | return -1; 58 | } 59 | 60 | header.pid = 0; 61 | 62 | data[0].effective = mask; 63 | data[0].permitted = mask; 64 | data[0].inheritable = mask; 65 | 66 | mask >>= 32; 67 | data[1].effective = mask; 68 | data[1].permitted = mask; 69 | data[1].inheritable = mask; 70 | 71 | return capset(&header, data); 72 | } 73 | 74 | static int __add_group(gid_t gid, gid_t **groups, int *ngroups) 75 | { 76 | int n; 77 | gid_t *g; 78 | 79 | n = *ngroups + 1; 80 | if (*groups == NULL) 81 | g = xmalloc(n * sizeof(gid_t)); 82 | else 83 | g = xrealloc(*groups, n * sizeof(gid_t)); 84 | if (g == NULL) 85 | return -1; 86 | 87 | g[n - 1] = gid; 88 | *groups = g; 89 | *ngroups = n; 90 | return 0; 91 | } 92 | 93 | static int libct_getgroups(char *user, gid_t **__groups) 94 | { 95 | char *name = NULL, *passwd = NULL, *users = NULL; 96 | char buff[4096], *str; 97 | int ngroups = 0; 98 | gid_t *groups = NULL; 99 | FILE *f; 100 | 101 | f = fopen("/etc/group", "r"); 102 | if (f == NULL) { 103 | return -1; 104 | } 105 | 106 | while ((str = fgets(buff, sizeof(buff), f))) { 107 | gid_t gid; 108 | int off, len, ret; 109 | 110 | name = NULL; 111 | passwd = NULL; 112 | users = NULL; 113 | 114 | ret = sscanf(str, "%m[^:]:%m[^:]:%u:%ms", &name, &passwd, &gid, &users); 115 | if (ret < 3) { 116 | pr_err("Unable to parse: %s -> %d\n", str, ret); 117 | goto err; 118 | } 119 | if (ret < 4) 120 | continue; 121 | 122 | len = strlen(users); 123 | off = 0; 124 | while (off < len) { 125 | char *c; 126 | c = strchr(users + off, ','); 127 | if (c != NULL) 128 | *c = 0; 129 | 130 | if (strcmp(users + off, user) == 0) { 131 | if (__add_group(gid, &groups, &ngroups)) 132 | goto err; 133 | break; 134 | } 135 | 136 | if (c != NULL) { 137 | *c = ','; 138 | off = c - users; 139 | } else 140 | off = len; 141 | } 142 | xfree(name); 143 | xfree(passwd); 144 | xfree(users); 145 | } 146 | 147 | *__groups = groups; 148 | fclose(f); 149 | return ngroups; 150 | err: 151 | xfree(name); 152 | xfree(passwd); 153 | xfree(users); 154 | fclose(f); 155 | return -1; 156 | } 157 | 158 | static int libct_getpwnam(char *user, char *buf, size_t buflen, struct passwd *result) 159 | { 160 | FILE *f; 161 | char buff[4096], *str; 162 | 163 | f = fopen("/etc/passwd", "r"); 164 | if (f == NULL) { 165 | return -1; 166 | } 167 | 168 | while ((str = fgets(buff, sizeof(buff), f))) { 169 | char *name = NULL, *passwd = NULL, *gecos = NULL, *home = NULL, *shell = NULL; 170 | char suid[11]; 171 | unsigned int uid, gid; 172 | int ret; 173 | off_t off; 174 | 175 | /* name:password:UID:GID:GECOS:directory:shell */ 176 | /* root:x:0:0:root:/root:/bin/sh */ 177 | errno = 0; 178 | ret = sscanf(str, "%m[^:]:%m[^:]:%u:%u:%m[^:]:%m[^:]:%m[^:]", &name, &passwd, &uid, &gid, &gecos, &home, &shell); 179 | if (ret != 7) { 180 | pr_perror("Unable to parse: %s -> %d\n", str, ret); 181 | xfree(name); 182 | xfree(passwd); 183 | xfree(gecos); 184 | xfree(home); 185 | xfree(shell); 186 | goto err; 187 | } 188 | snprintf(suid, sizeof(suid), "%d", uid); 189 | 190 | if (strcmp(user, name) && strcmp(suid, user)) 191 | continue; 192 | 193 | off = 0; 194 | result->pw_name = buf + off; 195 | off += strlen(name) + 1; 196 | result->pw_passwd = buf + off; 197 | off += strlen(passwd) + 1; 198 | result->pw_uid = uid; 199 | result->pw_gid = gid; 200 | result->pw_gecos = buf + off; 201 | off += strlen(gecos) + 1; 202 | result->pw_dir = buf + off; 203 | off += strlen(home) + 1; 204 | result->pw_shell = buf + off; 205 | off += strlen(shell) + 1; 206 | 207 | strcpy(result->pw_name, name); 208 | strcpy(result->pw_passwd, passwd); 209 | strcpy(result->pw_gecos, gecos); 210 | strcpy(result->pw_dir, home); 211 | strcpy(result->pw_shell, shell); 212 | xfree(name); 213 | xfree(passwd); 214 | xfree(gecos); 215 | xfree(home); 216 | xfree(shell); 217 | fclose(f); 218 | return 0; 219 | } 220 | 221 | err: 222 | fclose(f); 223 | return -1; 224 | } 225 | 226 | int apply_creds(struct process_desc *p) 227 | { 228 | if (p->user) { 229 | char buf[4096]; 230 | struct passwd e; 231 | gid_t *groups = NULL; 232 | int ngroups; 233 | 234 | if (libct_getpwnam(p->user, buf, sizeof(buf), &e)) 235 | return -1; 236 | 237 | p->uid = e.pw_uid; 238 | p->gid = e.pw_gid; 239 | 240 | ngroups = libct_getgroups(e.pw_name, &groups); 241 | if (ngroups < 0) 242 | return ngroups; 243 | if (p->groups == NULL) { 244 | p->groups = groups; 245 | p->ngroups = ngroups; 246 | } else { 247 | gid_t *_groups; 248 | _groups = xrealloc(p->groups, p->ngroups + ngroups); 249 | if (_groups == NULL) 250 | return -1; 251 | p->groups = _groups; 252 | memcpy(p->groups + p->ngroups, groups, sizeof(gid_t) * ngroups); 253 | p->ngroups += ngroups; 254 | xfree(groups); 255 | } 256 | 257 | setenv("HOME", e.pw_dir, 0); 258 | } 259 | 260 | if (setgroups(p->ngroups, p->groups)) 261 | return -1; 262 | 263 | if (p->cap_mask & CAPS_BSET) 264 | if (apply_bset(p->cap_bset) < 0) 265 | return -1; 266 | 267 | if (prctl(PR_SET_KEEPCAPS, 1)) 268 | pr_perror("Unable to set PR_SET_KEEPCAPS\n"); 269 | if (setgid(p->gid) || setuid(p->uid)) 270 | return -1; 271 | if (prctl(PR_SET_KEEPCAPS, 0)) { 272 | pr_perror("Unable to clear PR_SET_KEEPCAPS\n"); 273 | return -1; 274 | } 275 | 276 | if (p->cap_mask & CAPS_ALLCAPS) 277 | if (apply_all_caps(p->cap_caps) < 0) 278 | return -1; 279 | 280 | return 0; 281 | } 282 | -------------------------------------------------------------------------------- /src/session.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "uapi/libct.h" 4 | 5 | #include "session.h" 6 | #include "process.h" 7 | #include "xmalloc.h" 8 | #include "libct.h" 9 | #include "ct.h" 10 | #include "vz.h" 11 | 12 | static void close_local_session(libct_session_t s) 13 | { 14 | struct local_session *l = s2ls(s); 15 | xfree(l); 16 | } 17 | 18 | static ct_handler_t create_local_ct(libct_session_t s, char *name) 19 | { 20 | ct_handler_t ct; 21 | 22 | ct = ct_create(name); 23 | if (!ct) 24 | return libct_err_to_handle(-1); 25 | 26 | return ct; 27 | } 28 | 29 | static ct_process_desc_t local_process_create_desc(libct_session_t s) 30 | { 31 | struct process_desc *p; 32 | 33 | p = xmalloc(sizeof(*p)); 34 | if (p == NULL) 35 | return libct_err_to_handle(-1); 36 | 37 | local_process_desc_init(p); 38 | 39 | return &p->h; 40 | } 41 | 42 | static void update_local_ct_state(libct_session_t s, pid_t pid) 43 | { 44 | ct_handler_t h; 45 | 46 | list_for_each_entry(h, &s->s_cts, s_lh) { 47 | struct container *ct = cth2ct(h); 48 | if (ct->p.pid != pid) 49 | continue; 50 | 51 | h->ops->wait(h); 52 | } 53 | } 54 | 55 | static const struct backend_ops local_session_ops = { 56 | .type = BACKEND_LOCAL, 57 | .create_ct = create_local_ct, 58 | .create_process_desc = local_process_create_desc, 59 | .close = close_local_session, 60 | .update_ct_state = update_local_ct_state, 61 | }; 62 | 63 | 64 | static void close_vz_session(libct_session_t s) 65 | { 66 | struct local_session *l = s2ls(s); 67 | xfree(l); 68 | vzctl_close(); 69 | } 70 | 71 | static ct_handler_t create_vz_ct(libct_session_t s, char *name) 72 | { 73 | ct_handler_t ct = NULL; 74 | if (vzctl_open() == -1) 75 | return libct_err_to_handle(-1); 76 | 77 | ct = vz_ct_create(name); 78 | if (!ct) 79 | return libct_err_to_handle(-1); 80 | 81 | return ct; 82 | } 83 | 84 | static void update_vz_ct_state(libct_session_t s, pid_t pid) 85 | { 86 | /* TODO: implement afterwards */ 87 | } 88 | 89 | 90 | static const struct backend_ops vz_session_ops = { 91 | .type = BACKEND_VZ, 92 | .create_ct = create_vz_ct, 93 | .create_process_desc = local_process_create_desc, 94 | .close = close_vz_session, 95 | .update_ct_state = update_vz_ct_state, 96 | }; 97 | 98 | libct_session_t libct_session_open_local(void) 99 | { 100 | struct local_session *s; 101 | 102 | if (libct_init_local()) 103 | return libct_err_to_handle(-1); 104 | 105 | s = xmalloc(sizeof(*s)); 106 | if (s) { 107 | INIT_LIST_HEAD(&s->s.s_cts); 108 | if (!access("/proc/vz", F_OK)) 109 | s->s.ops = &vz_session_ops; 110 | else 111 | s->s.ops = &local_session_ops; 112 | return &s->s; 113 | } 114 | 115 | return libct_err_to_handle(-1); 116 | } 117 | 118 | static inline ct_handler_t new_ct(libct_session_t ses, ct_handler_t cth) 119 | { 120 | if (!libct_handle_is_err(cth) && list_empty(&cth->s_lh)) 121 | list_add_tail(&cth->s_lh, &ses->s_cts); 122 | 123 | return cth; 124 | } 125 | 126 | ct_handler_t libct_container_create(libct_session_t ses, char *name) 127 | { 128 | ct_handler_t cth; 129 | 130 | if (!name) 131 | return libct_err_to_handle(-LCTERR_INVARG); 132 | 133 | cth = ses->ops->create_ct(ses, name); 134 | return new_ct(ses, cth); 135 | } 136 | 137 | ct_handler_t libct_container_open(libct_session_t ses, char *name) 138 | { 139 | ct_handler_t cth; 140 | 141 | if (!name) 142 | return libct_err_to_handle(-LCTERR_INVARG); 143 | 144 | if (!ses->ops->open_ct) 145 | return libct_err_to_handle(-1); 146 | 147 | /* 148 | * FIXME -- there can exist multiple handlers, need 149 | * to invalidate them all on container destruction. 150 | */ 151 | 152 | cth = ses->ops->open_ct(ses, name); 153 | return new_ct(ses, cth); 154 | } 155 | 156 | ct_process_desc_t libct_process_desc_create(libct_session_t ses) 157 | { 158 | return ses->ops->create_process_desc(ses); 159 | } 160 | 161 | void libct_session_close(libct_session_t s) 162 | { 163 | ct_handler_t cth, n; 164 | 165 | list_for_each_entry_safe(cth, n, &s->s_cts, s_lh) 166 | libct_container_close(cth); 167 | 168 | s->ops->close(s); 169 | } 170 | -------------------------------------------------------------------------------- /src/systemd.c: -------------------------------------------------------------------------------- 1 | #include "systemd.h" 2 | #include "xmalloc.h" 3 | #include "log.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ct.h" 10 | #include "log.h" 11 | #include "util.h" 12 | 13 | DBusConnection *get_connection(DBusBusType type) 14 | { 15 | DBusError error; 16 | DBusConnection *conn; 17 | 18 | dbus_error_init(&error); 19 | conn = dbus_bus_get(type, &error); 20 | if (dbus_error_is_set (&error)) { 21 | pr_err("dbus error: %s\n", error.message); 22 | dbus_error_free (&error); 23 | return NULL; 24 | } 25 | 26 | return conn; 27 | } 28 | 29 | static DBusMessage *dbus_send_message(DBusConnection *conn, DBusMessage *msg) 30 | { 31 | DBusMessage *reply; 32 | DBusError error; 33 | 34 | dbus_error_init(&error); 35 | reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &error); 36 | if (dbus_error_is_set (&error)) { 37 | pr_err("dbus error: %s\n", error.message); 38 | dbus_error_free (&error); 39 | return NULL; 40 | } 41 | 42 | dbus_connection_flush(conn); 43 | return reply; 44 | } 45 | 46 | static void set_property(DBusMessageIter *props, const char *key, int type, const void *value) 47 | { 48 | DBusMessageIter prop, var; 49 | const char type_str[] = { type, '\0' }; 50 | 51 | dbus_message_iter_open_container(props, 'r', NULL, &prop); 52 | dbus_message_iter_append_basic(&prop, 's', &key); 53 | dbus_message_iter_open_container(&prop, 'v', type_str, &var); 54 | dbus_message_iter_append_basic(&var, type, value); 55 | dbus_message_iter_close_container(&prop, &var); 56 | dbus_message_iter_close_container(props, &prop); 57 | } 58 | 59 | static void set_pid(DBusMessageIter *props, int pid) 60 | { 61 | DBusMessageIter t, a, v; 62 | const char *key = "PIDs"; 63 | const char *type_str = "au"; 64 | const dbus_int32_t pids[] = { pid }; 65 | const dbus_int32_t *p = pids; 66 | 67 | dbus_message_iter_open_container(props, DBUS_TYPE_STRUCT, NULL, &t); 68 | dbus_message_iter_append_basic(&t, DBUS_TYPE_STRING, &key); 69 | 70 | dbus_message_iter_open_container(&t, 'v', type_str, &v); 71 | dbus_message_iter_open_container(&v, 'a', "u", &a); 72 | dbus_message_iter_append_fixed_array(&a, 'u', &p, 1); 73 | dbus_message_iter_close_container(&v, &a); 74 | dbus_message_iter_close_container(&t, &v); 75 | 76 | dbus_message_iter_close_container(props, &t); 77 | } 78 | 79 | int systemd_start_unit(struct container *ct, int pid) 80 | { 81 | static const char *mode = "fail"; 82 | char *slice = "system.slice"; 83 | char unit_name[PATH_MAX], *name = unit_name; 84 | char desc[1024], *pdesc = desc; 85 | dbus_bool_t yes = true; 86 | 87 | DBusConnection *conn; 88 | DBusMessage *msg, *reply; 89 | DBusMessageIter args, props, aux; 90 | 91 | if (ct->slice) 92 | slice = ct->slice; 93 | snprintf(unit_name, sizeof(unit_name), "%s-%s.scope", slice, ct->name); 94 | snprintf(desc, sizeof(desc), "docker container %s", ct->name); 95 | 96 | msg = dbus_message_new_method_call("org.freedesktop.systemd1", 97 | "/org/freedesktop/systemd1", 98 | "org.freedesktop.systemd1.Manager", 99 | "StartTransientUnit"); 100 | if (!msg) { 101 | pr_err("can't allocate new method call"); 102 | return -1; 103 | } 104 | 105 | dbus_message_append_args(msg, 's', &name, 's', &mode, 0); 106 | 107 | dbus_message_iter_init_append(msg, &args); 108 | 109 | dbus_message_iter_open_container(&args, 'a', "(sv)", &props); 110 | set_property(&props, "Description", 's', &pdesc); 111 | set_property(&props, "Slice", 's', &slice); 112 | 113 | set_property(&props, "MemoryAccounting", 'b', &yes); 114 | set_property(&props, "CPUAccounting", 'b', &yes); 115 | set_property(&props, "BlockIOAccounting", 'b', &yes); 116 | 117 | set_pid(&props, pid); 118 | dbus_message_iter_close_container(&args, &props); 119 | 120 | dbus_message_iter_open_container(&args, 'a', "(sa(sv))", &aux); 121 | dbus_message_iter_close_container(&args, &aux); 122 | 123 | conn = get_connection(DBUS_BUS_SYSTEM); 124 | if (conn == NULL) 125 | return -1; 126 | 127 | reply = dbus_send_message(conn, msg); 128 | dbus_message_unref(msg); 129 | if (reply == NULL) 130 | return -1; 131 | 132 | dbus_message_unref(reply); 133 | 134 | return 0; 135 | } 136 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "uapi/libct.h" 15 | #include "xmalloc.h" 16 | #include "util.h" 17 | #include "log.h" 18 | 19 | static int create_dest(char *path, mode_t mode, bool isdir) 20 | { 21 | char *tok; 22 | int ret; 23 | 24 | tok = path; 25 | while (1) { 26 | char c = 0; 27 | 28 | tok = strchr(tok + 1, '/'); 29 | if (tok != NULL) { 30 | c = *tok; 31 | *tok = 0; 32 | } 33 | 34 | if (tok == NULL && !isdir) { 35 | ret = open(path, O_CREAT | O_WRONLY, mode); 36 | if (ret >= 0) 37 | close(ret); 38 | } else 39 | ret = mkdir(path, mode); 40 | 41 | if (ret < 0 && errno != EEXIST) { 42 | pr_perror("couldn't create %s", path); 43 | if (tok != NULL) 44 | *tok = c; 45 | return -1; 46 | } 47 | 48 | if (tok == NULL) 49 | break; 50 | 51 | *tok = c; 52 | } 53 | 54 | return 0; 55 | } 56 | 57 | int do_mount(char *src, char *dst, int flags, char *fstype, char *data) 58 | { 59 | unsigned long mountflags = 0; 60 | struct stat st; 61 | bool isdir = true; 62 | 63 | if (flags & CT_FS_BIND) { 64 | if (fstype || data) 65 | return -1; 66 | 67 | mountflags |= MS_BIND; 68 | 69 | if (stat(src, &st)) { 70 | pr_perror("Unable to stat %s", src); 71 | return -1; 72 | } 73 | isdir = S_ISDIR(st.st_mode); 74 | } 75 | 76 | if (create_dest(dst, 0755, isdir)) 77 | return -1; 78 | 79 | if (flags & CT_FS_RDONLY) 80 | mountflags |= MS_RDONLY; 81 | if (flags & CT_FS_NOEXEC) 82 | mountflags |= MS_NOEXEC; 83 | if (flags & CT_FS_NOSUID) 84 | mountflags |= MS_NOSUID; 85 | if (flags & CT_FS_NODEV) 86 | mountflags |= MS_NODEV; 87 | if (flags & CT_FS_STRICTATIME) 88 | mountflags |= MS_STRICTATIME; 89 | if (flags & CT_FS_REC) 90 | mountflags |= MS_REC; 91 | 92 | if (mount(src, dst, fstype, mountflags, data) == -1) { 93 | pr_perror("Unable to mount %s -> %s\n", src, dst); 94 | return -1; 95 | } 96 | 97 | if (flags & CT_FS_PRIVATE) { 98 | if (mount(NULL, dst, NULL, MS_PRIVATE, NULL) == -1) { 99 | pr_perror("Unable to mark %s as private", dst); 100 | umount(dst); 101 | return -1; 102 | } 103 | } 104 | 105 | return 0; 106 | } 107 | 108 | int set_string(char **dest, char *src) 109 | { 110 | char *t; 111 | 112 | t = xstrdup(src); 113 | if (t == NULL) 114 | return -1; 115 | 116 | xfree(*dest); 117 | *dest = t; 118 | 119 | return 0; 120 | } 121 | 122 | int parse_uint(const char *str, unsigned int *val) 123 | { 124 | char *tail; 125 | long int n; 126 | 127 | if (*str == '\0') 128 | return -1; 129 | 130 | errno = 0; 131 | n = strtoul(str, &tail, 10); 132 | if (*tail != '\0' || n >= UINT_MAX) 133 | return -1; 134 | *val = (unsigned int)n; 135 | 136 | return 0; 137 | } 138 | 139 | int parse_int(const char *str, int *val) 140 | { 141 | char *tail; 142 | long int n; 143 | 144 | if (*str == '\0') 145 | return -1; 146 | 147 | errno = 0; 148 | n = strtol(str, &tail, 10); 149 | if (*tail != '\0' || errno == ERANGE || n > INT_MAX) 150 | return -1; 151 | *val = (int)n; 152 | 153 | return 0; 154 | } 155 | 156 | /* 157 | 1 - exist 158 | 0 - doesn't exist 159 | -1 - error 160 | */ 161 | int stat_file(const char *file) 162 | { 163 | struct stat st; 164 | 165 | if (stat(file, &st)) { 166 | if (errno != ENOENT) { 167 | pr_perror("unable to stat %s", file); 168 | return -1; 169 | } 170 | return 0; 171 | } 172 | return 1; 173 | } 174 | 175 | /* Close all file descriptors, which are not less than n */ 176 | static int close_fds(int proc_fd, int n) 177 | { 178 | struct dirent *de; 179 | DIR *d; 180 | int fd; 181 | 182 | fd = openat(proc_fd, "self/fd", O_DIRECTORY | O_RDONLY); 183 | if (fd < 0) { 184 | pr_perror("Unable to open /proc/self/fd"); 185 | return -1; 186 | } 187 | 188 | d = fdopendir(fd); 189 | if (d == NULL) { 190 | pr_perror("Unable to open /proc/self/fd"); 191 | close(fd); 192 | return -1; 193 | } 194 | 195 | while ((de = readdir(d))) { 196 | int fd; 197 | 198 | if (de->d_name[0] == '.') 199 | continue; 200 | 201 | fd = atoi(de->d_name); 202 | if (dirfd(d) == fd) 203 | continue; 204 | if (fd < n) 205 | continue; 206 | close(fd); 207 | } 208 | 209 | closedir(d); 210 | 211 | return 0; 212 | } 213 | 214 | /* 215 | * Setup file descriptors from the fds array according to the positions in the 216 | * arrays and close other file descriptros. 217 | * 218 | * proc_self_d has to point on /proc/self/fd 219 | */ 220 | int setup_fds_at(int proc_fd, int *fds, int n) 221 | { 222 | int i; 223 | 224 | libct_log_init(-1, 0); /* close */ 225 | 226 | for (i = 0; i < n; i++) { 227 | if (fds[i] == LIBCT_CONSOLE_FD) { 228 | fds[i] = open("/dev/console", O_RDWR); 229 | if (fds[i] == -1) { 230 | pr_perror("Unable to open /dev/console"); 231 | return -1; 232 | } 233 | } 234 | } 235 | 236 | /* skip used file descriptors and fill all unused descriptors */ 237 | for (i = 0; i < n; i++) { 238 | if (fcntl(i, F_GETFD) != -1 || errno != EBADF) 239 | continue; /* inuse */ 240 | 241 | if (dup2(fds[i], i) == -1) { 242 | pr_perror("Unable to dup %d -> %d", fds[i], i); 243 | return -1; 244 | } 245 | 246 | fds[i] = i; 247 | } 248 | 249 | if (proc_fd < n) 250 | proc_fd = dup(proc_fd); 251 | 252 | /* move target descriptros from target places */ 253 | for (i = 0; i < n; i++) { 254 | int ret; 255 | 256 | if (fds[i] == i || fds[i] >= n) 257 | continue; 258 | 259 | ret = dup(fds[i]); 260 | if (ret == -1) { 261 | pr_perror("Unable to dup %d", fds[i]); 262 | return -1; 263 | } 264 | 265 | fds[i] = ret; 266 | } 267 | 268 | for (i = 0; i < n; i++) { 269 | if (fds[i] == i) 270 | continue; 271 | 272 | if (dup2(fds[i], i) == -1) { 273 | pr_perror("Unable to dup %d -> %d", fds[i], i); 274 | return -1; 275 | } 276 | 277 | fds[i] = i; 278 | } 279 | 280 | return close_fds(proc_fd, n); 281 | } 282 | 283 | int spawn_sock_wait(int sk) 284 | { 285 | int ret = INT_MIN; 286 | read(sk, &ret, sizeof(ret)); 287 | return ret; 288 | } 289 | 290 | int spawn_sock_wait_and_close(int sk) 291 | { 292 | int ret = spawn_sock_wait(sk); 293 | shutdown(sk, SHUT_RD); 294 | return ret; 295 | } 296 | 297 | void spawn_sock_wake(int sk, int ret) 298 | { 299 | write(sk, &ret, sizeof(ret)); 300 | } 301 | 302 | void spawn_sock_wake_and_close(int sk, int ret) 303 | { 304 | write(sk, &ret, sizeof(ret)); 305 | shutdown(sk, SHUT_WR); 306 | } 307 | -------------------------------------------------------------------------------- /src/vz/readelf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999-2010, Parallels, Inc. All rights reserved. 3 | * 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "readelf.h" 16 | 17 | #define EI_NIDENT 16 18 | #define ELFMAG "\177ELF" 19 | #define OLFMAG "\177OLF" 20 | 21 | struct elf_hdr_s { 22 | uint8_t ident[EI_NIDENT]; 23 | uint16_t type; 24 | uint16_t machine; 25 | }; 26 | 27 | static inline int check_elf_magic(const uint8_t *buf) 28 | { 29 | if (memcmp(buf, ELFMAG, 4) && 30 | memcmp(buf, OLFMAG, 4)) 31 | return -1; 32 | else 33 | return 0; 34 | } 35 | 36 | int get_arch_from_elf(const char *file) 37 | { 38 | int fd, nbytes, class; 39 | struct stat st; 40 | struct elf_hdr_s elf_hdr; 41 | 42 | if (stat(file, &st)) 43 | return -1; 44 | if (!S_ISREG(st.st_mode)) 45 | return -1; 46 | fd = open(file, O_RDONLY); 47 | if (fd < 0) 48 | return -1; 49 | nbytes = read(fd, (void *) &elf_hdr, sizeof(elf_hdr)); 50 | close(fd); 51 | if (nbytes < sizeof(elf_hdr)) 52 | return -1; 53 | if (check_elf_magic(elf_hdr.ident)) 54 | return -1; 55 | class = elf_hdr.ident[4]; 56 | switch (class) { 57 | case elf_32: 58 | return elf_32; 59 | break; 60 | case elf_64: 61 | return elf_64; 62 | break; 63 | default: 64 | return elf_none; 65 | break; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/vz/vz_net.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "vzctl_veth.h" 17 | #include "vz_net.h" 18 | #include "vz.h" 19 | #include "log.h" 20 | #include "xmalloc.h" 21 | #include "net_util.h" 22 | #include "util.h" 23 | #include "ct.h" 24 | 25 | #define ETH_ALEN 6 26 | 27 | static int gen_hwaddr(unsigned char *buf, int size) 28 | { 29 | int res = -1; 30 | int fd = -1; 31 | 32 | fd = open("/dev/urandom", O_RDONLY); 33 | if (fd < 0) 34 | return errno; 35 | 36 | res = read(fd, buf, size); 37 | if (res < 0) { 38 | int _errno = errno; 39 | close(fd); 40 | return _errno; 41 | } 42 | 43 | close(fd); 44 | if (res != size) 45 | return EINVAL; 46 | 47 | /* use locally administrated address */ 48 | buf[0] = 0xfe; 49 | 50 | return 0; 51 | } 52 | 53 | static int vz_veth_ioctl(int op_type, struct container *ct, struct ct_net *n, const char *pair0, const char *pair1) 54 | { 55 | struct vzctl_ve_hwaddr veth; 56 | int ret = -1; 57 | unsigned int veid = 0; 58 | if (ct) { 59 | ret = parse_uint(ct->name, &veid); 60 | if (ret) { 61 | pr_err("Unable to parse container's ID"); 62 | return -1; 63 | } 64 | } else { 65 | veid = 0; 66 | } 67 | 68 | veth.op = op_type; 69 | veth.veid = veid; 70 | veth.addrlen = ETH_ALEN; 71 | veth.addrlen_ve = ETH_ALEN; 72 | 73 | ret = gen_hwaddr(veth.dev_addr, ETH_ALEN); 74 | if (ret) { 75 | pr_err("Failed to gen_hwaddr: err=%d", ret); 76 | return -1; 77 | } 78 | if (n->addr) { 79 | sscanf(n->addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &veth.dev_addr_ve[0], 80 | &veth.dev_addr_ve[1], &veth.dev_addr_ve[2], &veth.dev_addr_ve[3], 81 | &veth.dev_addr_ve[4], &veth.dev_addr_ve[5]); 82 | 83 | } else { 84 | ret = gen_hwaddr(veth.dev_addr_ve, ETH_ALEN); 85 | if (ret) { 86 | pr_err("Failed to gen_hwaddr: err=%d", ret); 87 | return -1; 88 | } 89 | } 90 | memcpy(veth.dev_name, pair0, sizeof(veth.dev_name)); 91 | memcpy(veth.dev_name_ve, pair1, sizeof(veth.dev_name_ve)); 92 | 93 | ret = ioctl(get_vzctlfd(), VETHCTL_VE_HWADDR, &veth); 94 | if (ret) { 95 | if (errno == ENOTTY) { 96 | pr_err("veth feature is" 97 | " not supported by the kernel"); 98 | } else { 99 | pr_err("Unable to perform operation %d on veth device" 100 | " pair %s %s err=%d", 101 | op_type, pair0, pair1, errno); 102 | pr_perror("Error"); 103 | } 104 | return -1; 105 | } 106 | return 0; 107 | } 108 | 109 | static struct ct_net *vz_veth_create(void *arg, struct ct_net_ops const *ops) 110 | { 111 | struct ct_net_veth_arg *va = arg; 112 | struct ct_net_veth *vn = NULL; 113 | 114 | if (!arg || !va->host_name || !va->ct_name) 115 | return NULL; 116 | 117 | vn = xzalloc(sizeof(*vn)); 118 | if (!vn) 119 | return NULL; 120 | 121 | ct_net_init(&vn->n, ops); 122 | ct_net_init(&vn->peer, ops); 123 | 124 | vn->peer.name = xstrdup(va->host_name); 125 | vn->n.name = xstrdup(va->ct_name); 126 | if (!vn->peer.name || !vn->n.name) { 127 | xfree(vn->peer.name); 128 | xfree(vn->n.name); 129 | veth_free(vn); 130 | return NULL; 131 | } 132 | 133 | return &vn->n; 134 | 135 | return NULL; 136 | } 137 | 138 | static int vz_veth_start(struct container *ct, struct ct_net *n) 139 | { 140 | struct ct_net_veth *vn = NULL; 141 | int ret = -1; 142 | 143 | if (!n) 144 | return -LCTERR_BADARG; 145 | vn = cn2vn(n); 146 | 147 | ret = vz_veth_ioctl(VE_ETH_ADD, ct, n, vn->peer.name, vn->n.name); 148 | if (ret) 149 | return ret; 150 | ret = vz_veth_ioctl(VE_ETH_ALLOW_MAC_CHANGE, ct, n, vn->peer.name, vn->n.name); 151 | return ret; 152 | } 153 | 154 | static void vz_veth_destroy(struct ct_net *n) 155 | { 156 | struct ct_net_veth *vn = NULL; 157 | if (!n) 158 | return; 159 | vn = cn2vn(n); 160 | /* TODO: Ask Pavel: should we explicitly remove veth */ 161 | /*vz_veth_ioctl(VE_ETH_DEL, NULL, vn->n.name, vn->peer.name);*/ 162 | veth_free(vn); 163 | } 164 | 165 | static const struct ct_net_ops vz_veth_nic_ops = { 166 | .create = vz_veth_create, 167 | .destroy = vz_veth_destroy, 168 | .start = vz_veth_start, 169 | .stop = veth_stop, 170 | .match = veth_match, 171 | .set_mac_addr = net_dev_set_mac_addr, 172 | .set_master = net_dev_set_master, 173 | .add_ip_addr = net_dev_add_ip_addr, 174 | .set_mtu = net_dev_set_mtu, 175 | }; 176 | 177 | const struct ct_net_ops *vz_net_get_ops(enum ct_net_type ntype) 178 | { 179 | switch (ntype) { 180 | case CT_NET_VETH: 181 | return &vz_veth_nic_ops; 182 | default: 183 | return NULL; 184 | } 185 | } 186 | 187 | ct_net_t vz_net_add(ct_handler_t h, enum ct_net_type ntype, void *arg) 188 | { 189 | return __local_net_add(h, ntype, arg, vz_net_get_ops); 190 | } 191 | 192 | struct ct_net_veth *cn2vn(struct ct_net *n) 193 | { 194 | return container_of(n, struct ct_net_veth, n); 195 | } 196 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | COMMON_TESTS = vz_create_exec vz_net_veth vz_enter 2 | 3 | LOCAL_TESTS = ct_create ct_enter ct_proc ct_root ct_root_enter \ 4 | ct_create_exec ct_cgroup_basic ct_net_host \ 5 | ct_net_veth ct_private_subdir \ 6 | ct_ext_mount ct_private_subdir_ns \ 7 | ct_cgroup_sub ct_service ct_kill_nons ct_pid_enter \ 8 | ct_userns ct_caps ct_fds ct_pause ct_userns_self \ 9 | ct_userns_self ct_enter_pidns ct_switch 10 | 11 | VZ_TESTS = vz_cgroup_memory vz_cgroup_cpu vz_cgroup_blkio 12 | 13 | TESTS = $(LOCAL_TESTS) $(VZ_TESTS) $(COMMON_TESTS) 14 | 15 | PIGS = file_piggy 16 | 17 | CC = gcc 18 | CFLAGS = -I../src/include/uapi/ -g -Wall -Werror 19 | 20 | CFLAGS += $(DEFINES) 21 | 22 | ifneq ("$(wildcard /proc/vz)","") 23 | CFLAGS += -I/usr/src/kernels/$(shell uname -r)/include/ 24 | CFLAGS += -DVZ 25 | endif 26 | LIBNLDIR = ../.shipped/libnl/lib/.libs 27 | LDFLAGS = -Wl,--no-as-needed \ 28 | -lct -L../src/ -Wl,-rpath,'$$ORIGIN/../src' \ 29 | -lnl-route-3 -lnl-3 -L$(LIBNLDIR) -Wl,-rpath,'$$ORIGIN/$(LIBNLDIR)' \ 30 | -ldbus-1 31 | 32 | COMMON_OUTS = $(COMMON_TESTS:%=%.out) 33 | LOCAL_OUTS = $(LOCAL_TESTS:%=%.out) 34 | VZ_OUTS = $(VZ_TESTS:%=%.out) 35 | 36 | ifdef CONFIG_APPARMOR 37 | LDFLAGS += -lapparmor 38 | LOCAL_TESTS += ct_apparmor 39 | endif 40 | 41 | ifdef CONFIG_SELINUX 42 | LDFLAGS += -lselinux 43 | endif 44 | 45 | OUTS = $(TESTS:%=%.out) 46 | OBJS = $(TESTS:%=%.o) 47 | 48 | all: $(PIGS) $(TESTS) 49 | 50 | local: $(PIGS) $(LOCAL_TESTS) 51 | common: $(PIGS) $(COMMON_TESTS) 52 | 53 | vz: $(VZ_TESTS) 54 | 55 | cleanouts: 56 | rm -f $(OUTS) 57 | 58 | clean: cleanouts 59 | rm -f $(TESTS) 60 | rm -f $(PIGS) 61 | rm -f $(OBJS) 62 | rm -rf root 63 | 64 | %.o: %.c 65 | $(CC) -c $(CFLAGS) $^ -o $@ 66 | 67 | define gen-out 68 | $(1): $(1).o 69 | $$(CC) -o $$@ $$^ $$(LDFLAGS) 70 | 71 | $(1).out: $(1) 72 | ./$$^ > $$@ 73 | @ tail -n1 $$@ 74 | endef 75 | $(foreach t, $(TESTS) $(PIGS), $(eval $(call gen-out,$(t)))) 76 | 77 | run-local: cleanouts root $(PIGS) $(LOCAL_OUTS) $(COMMON_OUTS) 78 | 79 | run-vz: cleanouts root $(VZ_OUTS) $(COMMON_OUTS) 80 | 81 | run: cleanouts $(PIGS) $(OUTS) 82 | 83 | root: 84 | mkdir root 85 | curl -sSL https://github.com/jpetazzo/docker-busybox/raw/buildroot-2014.02/rootfs.tar | tar -xC root 86 | 87 | .PHONY: all local vz clean run run-local run-vz 88 | -------------------------------------------------------------------------------- /test/apparmor.test: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | profile libct-test flags=(attach_disconnected,mediate_deleted) { 4 | #include 5 | 6 | deny /proc/self/stat r, 7 | } 8 | -------------------------------------------------------------------------------- /test/ct_apparmor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test empty "container" creation 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "test.h" 15 | 16 | static int set_ct_alive(void *a) 17 | { 18 | int fd; 19 | 20 | fd = open("/proc/self/stat", O_RDONLY); 21 | if (fd >= 0) 22 | return 1; 23 | 24 | *(int *)a = 1; 25 | return 0; 26 | } 27 | 28 | int main(int argc, char **argv) 29 | { 30 | int *ct_alive; 31 | libct_session_t s; 32 | ct_handler_t ct; 33 | ct_process_desc_t p; 34 | 35 | if (system("apparmor_parser -r apparmor.test")) 36 | return 1; 37 | 38 | ct_alive = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 39 | MAP_SHARED | MAP_ANON, 0, 0); 40 | *ct_alive = 0; 41 | 42 | s = libct_session_open_local(); 43 | ct = libct_container_create(s, "test"); 44 | p = libct_process_desc_create(s); 45 | libct_process_desc_set_lsm_label(p, "libct-test"); 46 | libct_container_spawn_cb(ct, p, set_ct_alive, ct_alive); 47 | libct_container_wait(ct); 48 | libct_container_destroy(ct); 49 | libct_session_close(s); 50 | 51 | if (!*ct_alive) 52 | return fail("Container is not alive"); 53 | else 54 | return pass("Container is alive"); 55 | } 56 | -------------------------------------------------------------------------------- /test/ct_caps.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test empty "container" creation 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "test.h" 10 | 11 | #define TEST_CAPS 0x1234 12 | 13 | extern int capget(cap_user_header_t header, const cap_user_data_t data); 14 | extern int capset(cap_user_header_t header, const cap_user_data_t data); 15 | 16 | static int set_ct_alive(void *a) 17 | { 18 | struct __user_cap_header_struct hdr = {_LINUX_CAPABILITY_VERSION_3, 0}; 19 | struct __user_cap_data_struct data[2]; 20 | 21 | memset(&data, 0, sizeof(data)); 22 | 23 | if (capget(&hdr, data)) 24 | return -1; 25 | 26 | if (data[0].effective != TEST_CAPS) 27 | return 1; 28 | 29 | *(int *)a = 1; 30 | return 0; 31 | } 32 | 33 | int main(int argc, char **argv) 34 | { 35 | int *ct_alive; 36 | libct_session_t s; 37 | ct_handler_t ct; 38 | ct_process_desc_t p; 39 | ct_process_t pr; 40 | 41 | test_init(); 42 | 43 | ct_alive = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 44 | MAP_SHARED | MAP_ANON, 0, 0); 45 | *ct_alive = 0; 46 | 47 | s = libct_session_open_local(); 48 | ct = libct_container_create(s, "test"); 49 | p = libct_process_desc_create(s); 50 | libct_process_desc_set_caps(p, TEST_CAPS, CAPS_ALLCAPS); 51 | pr = libct_container_spawn_cb(ct, p, set_ct_alive, ct_alive); 52 | if (libct_handle_is_err(pr)) 53 | return fail("Unable to execute the init process"); 54 | libct_container_wait(ct); 55 | libct_container_destroy(ct); 56 | libct_session_close(s); 57 | 58 | if (!*ct_alive) 59 | return fail("Container is not alive"); 60 | else 61 | return pass("Container is alive"); 62 | } 63 | -------------------------------------------------------------------------------- /test/ct_cgroup_basic.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test creation of container with cgroup controller 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "test.h" 9 | 10 | static int check_freezer_cg(void *a) 11 | { 12 | int *st = a; 13 | char path[128]; 14 | 15 | sprintf(path, "/sys/fs/cgroup/freezer/test-fr/freezer.state"); 16 | st[0] = 1; 17 | if (access(path, F_OK) == 0) 18 | st[1] = 1; 19 | 20 | return 0; 21 | } 22 | 23 | int main(int argc, char **argv) 24 | { 25 | int *ct_state; 26 | libct_session_t s; 27 | ct_handler_t ct; 28 | ct_process_desc_t p; 29 | 30 | ct_state = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 31 | MAP_SHARED | MAP_ANON, 0, 0); 32 | ct_state[0] = 0; 33 | ct_state[1] = 0; 34 | 35 | s = libct_session_open_local(); 36 | ct = libct_container_create(s, "test-fr"); 37 | p = libct_process_desc_create(s); 38 | libct_controller_add(ct, CTL_FREEZER); 39 | libct_container_spawn_cb(ct, p, check_freezer_cg, ct_state); 40 | libct_container_wait(ct); 41 | libct_container_destroy(ct); 42 | libct_session_close(s); 43 | 44 | if (!ct_state[0]) 45 | return fail("Container is not alive"); 46 | if (!ct_state[1]) 47 | return fail("Freezer cgroup is not there"); 48 | 49 | return pass("Freezed CT is OK"); 50 | } 51 | -------------------------------------------------------------------------------- /test/ct_cgroup_sub.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test how cgroups subgroups work inside containers 3 | * (LIBCT_OPT_CGROUP_SUBMOUNT option). 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "test.h" 16 | 17 | #ifndef CLONE_NEWNS 18 | #define CLONE_NEWNS 0x00020000 19 | #endif 20 | 21 | #define FS_ROOT "libct_test_root_ns" 22 | #define FS_PRIVATE "libct_test_private_ns" 23 | #define FS_CG "cg" 24 | 25 | static int check_cgroup(void *a) 26 | { 27 | int *s = a; 28 | 29 | s[0] = 1; 30 | mkdir("/"FS_CG"/freezer/x", 0600); 31 | if (access("/"FS_CG"/freezer/x/freezer.state", F_OK) == 0) 32 | s[1] = 1; 33 | rmdir("/"FS_CG"/freezer/x"); 34 | 35 | return 0; 36 | } 37 | 38 | int main(int argc, char **argv) 39 | { 40 | int *ct_status; 41 | libct_session_t s; 42 | ct_handler_t ct; 43 | ct_process_desc_t p; 44 | int fs_err = 0; 45 | 46 | test_init(); 47 | 48 | mkdir(FS_ROOT, 0600); 49 | mkdir(FS_PRIVATE, 0600); 50 | mkdir(FS_PRIVATE "/" FS_CG, 0600); 51 | 52 | ct_status = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 53 | MAP_SHARED | MAP_ANON, 0, 0); 54 | ct_status[0] = 0; 55 | ct_status[1] = 0; 56 | 57 | s = libct_session_open_local(); 58 | ct = libct_container_create(s, "test"); 59 | p = libct_process_desc_create(s); 60 | libct_container_set_nsmask(ct, CLONE_NEWNS); 61 | libct_controller_add(ct, CTL_FREEZER); 62 | libct_fs_set_root(ct, FS_ROOT); 63 | libct_fs_set_private(ct, CT_FS_SUBDIR, FS_PRIVATE); 64 | libct_container_set_option(ct, LIBCT_OPT_CGROUP_SUBMOUNT, FS_CG); 65 | libct_container_spawn_cb(ct, p, check_cgroup, ct_status); 66 | libct_container_wait(ct); 67 | libct_container_destroy(ct); 68 | libct_session_close(s); 69 | 70 | if (rmdir(FS_PRIVATE "/" FS_CG) < 0) 71 | fs_err |= 1; 72 | if (rmdir(FS_PRIVATE) < 0) 73 | fs_err |= 2; 74 | if (rmdir(FS_ROOT) < 0) 75 | fs_err |= 3; 76 | 77 | if (fs_err) { 78 | printf("FS remove failed %x\n", fs_err); 79 | return fail("FS broken"); 80 | } 81 | 82 | if (!ct_status[0]) 83 | return fail("CT not running"); 84 | if (!ct_status[1]) 85 | return fail("CG not sub-mounted"); 86 | 87 | return pass("OK"); 88 | } 89 | -------------------------------------------------------------------------------- /test/ct_create.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test empty "container" creation 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "test.h" 10 | 11 | #define UID 31451 12 | #define GID 92653 13 | 14 | static int set_ct_alive(void *a) 15 | { 16 | if (getuid() != UID) 17 | return -1; 18 | if (getgid() != GID) 19 | return -1; 20 | *(int *)a = 1; 21 | return 0; 22 | } 23 | 24 | int main(int argc, char **argv) 25 | { 26 | int *ct_alive; 27 | libct_session_t s; 28 | ct_handler_t ct; 29 | ct_process_desc_t p; 30 | ct_process_t pr; 31 | 32 | ct_alive = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 33 | MAP_SHARED | MAP_ANON, 0, 0); 34 | if (ct_alive == MAP_FAILED) 35 | return tst_perr("Unable to allocate memory"); 36 | *ct_alive = 0; 37 | 38 | s = libct_session_open_local(); 39 | if (libct_handle_is_err(s)) 40 | return fail("Unable to create a new session"); 41 | 42 | ct = libct_container_create(s, "test"); 43 | if (libct_handle_is_err(ct)) 44 | return fail("Unable to create a container object"); 45 | 46 | p = libct_process_desc_create(s); 47 | if (libct_handle_is_err(p)) 48 | return fail("Unable to create a process descriptor"); 49 | 50 | libct_process_desc_setuid(p, UID); 51 | libct_process_desc_setgid(p, GID); 52 | 53 | pr = libct_container_spawn_cb(ct, p, set_ct_alive, ct_alive); 54 | if (libct_handle_is_err(pr)) 55 | return fail("Unable to start CT"); 56 | 57 | libct_container_wait(ct); 58 | libct_container_destroy(ct); 59 | libct_session_close(s); 60 | 61 | if (!*ct_alive) 62 | return fail("Container is not alive"); 63 | else 64 | return pass("Container is alive"); 65 | } 66 | -------------------------------------------------------------------------------- /test/ct_create_exec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test creation of container using executable 3 | */ 4 | #define _XOPEN_SOURCE 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "test.h" 14 | 15 | #define PIGGY_FILE "libct_piggy_file" 16 | #define PIGGY_DATA "libct_piggy_data" 17 | 18 | int main(int argc, char **argv) 19 | { 20 | libct_session_t s; 21 | ct_handler_t ct; 22 | ct_process_desc_t pd; 23 | ct_process_t p; 24 | char *piggy_a[4]; 25 | int fd, master, slave; 26 | char dat[sizeof(PIGGY_DATA)]; 27 | char *slavename; 28 | int fds[3]; 29 | 30 | s = libct_session_open_local(); 31 | ct = libct_container_create(s, "test"); 32 | pd = libct_process_desc_create(s); 33 | 34 | piggy_a[0] = "file_piggy"; 35 | piggy_a[1] = PIGGY_FILE; 36 | piggy_a[2] = PIGGY_DATA; 37 | piggy_a[3] = NULL; 38 | 39 | master = open("/dev/ptmx", O_RDWR); 40 | if (master < 0) 41 | goto err; 42 | 43 | grantpt(master); 44 | unlockpt(master); 45 | 46 | slavename = ptsname(master); 47 | if (slavename == NULL) 48 | goto err; 49 | slave = open(slavename, O_RDWR); 50 | if (slave < 0) 51 | goto err; 52 | 53 | if (libct_container_set_console_fd(ct, slave) < 0) 54 | goto err; 55 | 56 | fds[0] = fds[1] = fds[2] = slave; 57 | if (libct_process_desc_set_fds(pd, fds, 3)) 58 | goto err; 59 | 60 | p = libct_container_spawn_execv(ct, pd, "./file_piggy", piggy_a); 61 | if (libct_handle_is_err(p)) 62 | goto err; 63 | 64 | read(master, dat, 3); 65 | write(master, "\3", 1); /* Ctrl-C */ 66 | if (libct_container_wait(ct) < 0) 67 | goto err; 68 | 69 | libct_container_destroy(ct); 70 | libct_session_close(s); 71 | 72 | fd = open(PIGGY_FILE, O_RDONLY); 73 | if (fd < 0) 74 | return fail("Piggy file not created"); 75 | 76 | memset(dat, 0, sizeof(dat)); 77 | read(fd, dat, sizeof(dat)); 78 | close(fd); 79 | 80 | if (strcmp(dat, PIGGY_DATA)) 81 | return fail("Piggy data differs"); 82 | else 83 | return pass("Piggy file is OK"); 84 | err: 85 | return fail("Something wrong"); 86 | } 87 | -------------------------------------------------------------------------------- /test/ct_enter.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test entering into living container 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "test.h" 11 | 12 | struct ct_arg { 13 | int wait_fd; 14 | int *mark; 15 | }; 16 | 17 | static int set_ct_alive(void *a) 18 | { 19 | struct ct_arg *cta = a; 20 | char c; 21 | 22 | cta->mark[0] = 1; 23 | read(cta->wait_fd, &c, 1); 24 | return 0; 25 | } 26 | 27 | static int set_ct_enter(void *a) 28 | { 29 | struct ct_arg *cta = a; 30 | cta->mark[1] = 1; 31 | return 0; 32 | } 33 | 34 | int main(int argc, char **argv) 35 | { 36 | struct ct_arg cta; 37 | int p[2], status; 38 | libct_session_t s; 39 | ct_handler_t ct; 40 | ct_process_desc_t pd; 41 | ct_process_t pr; 42 | 43 | test_init(); 44 | 45 | if (pipe(p)) 46 | return tst_perr("Unable to create pipe"); 47 | cta.mark = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 48 | MAP_SHARED | MAP_ANON, 0, 0); 49 | cta.mark[0] = 0; 50 | cta.mark[1] = 0; 51 | cta.wait_fd = p[0]; 52 | 53 | s = libct_session_open_local(); 54 | ct = libct_container_create(s, "test"); 55 | libct_container_set_option(ct, LIBCT_OPT_KILLABLE, NULL); 56 | pd = libct_process_desc_create(s); 57 | pr = libct_container_spawn_cb(ct, pd, set_ct_alive, &cta); 58 | if (libct_handle_is_err(pr)) { 59 | return fail("Unable to start CT"); 60 | } 61 | 62 | pr = libct_container_enter_cb(ct, pd, set_ct_enter, &cta); 63 | if (libct_handle_is_err(pr)) 64 | return fail("Unable to enter into CT"); 65 | libct_process_wait(pr, &status); 66 | 67 | write(p[1], "a", 1); 68 | libct_container_wait(ct); 69 | libct_container_destroy(ct); 70 | libct_process_desc_destroy(pd); 71 | libct_process_destroy(pr); 72 | libct_session_close(s); 73 | 74 | if (!cta.mark[0]) 75 | return fail("CT is not alive"); 76 | 77 | if (!cta.mark[1]) 78 | return fail("CT is not enterable"); 79 | 80 | return pass("CT is created and entered"); 81 | } 82 | -------------------------------------------------------------------------------- /test/ct_enter_pidns.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test entering into living container 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "test.h" 12 | 13 | struct ct_arg { 14 | int wait_fd; 15 | int *mark; 16 | }; 17 | 18 | static int set_ct_alive(void *a) 19 | { 20 | struct ct_arg *cta = a; 21 | char c; 22 | 23 | cta->mark[0] = 1; 24 | read(cta->wait_fd, &c, 1); 25 | return 0; 26 | } 27 | 28 | static int set_ct_enter(void *a) 29 | { 30 | struct ct_arg *cta = a; 31 | char c; 32 | 33 | cta->mark[1] = 1; 34 | read(cta->wait_fd, &c, 1); 35 | return 0; 36 | } 37 | 38 | int main(int argc, char **argv) 39 | { 40 | struct ct_arg cta; 41 | int p[2], status; 42 | libct_session_t s; 43 | ct_handler_t ct; 44 | ct_process_desc_t pd; 45 | ct_process_t pr, epr; 46 | 47 | test_init(); 48 | 49 | if (pipe(p)) 50 | return tst_perr("Unable to create pipe"); 51 | cta.mark = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 52 | MAP_SHARED | MAP_ANON, 0, 0); 53 | cta.mark[0] = 0; 54 | cta.mark[1] = 0; 55 | cta.wait_fd = p[0]; 56 | 57 | s = libct_session_open_local(); 58 | ct = libct_container_create(s, "test"); 59 | libct_container_set_nsmask(ct, CLONE_NEWPID); 60 | pd = libct_process_desc_create(s); 61 | pr = libct_container_spawn_cb(ct, pd, set_ct_alive, &cta); 62 | if (libct_handle_is_err(pr)) { 63 | return fail("Unable to start CT"); 64 | } 65 | 66 | epr = libct_container_enter_cb(ct, pd, set_ct_enter, &cta); 67 | if (libct_handle_is_err(epr)) 68 | return fail("Unable to enter into CT"); 69 | 70 | if (kill(libct_process_get_pid(pr), SIGKILL)) 71 | return fail("Unable to send a signal to the init process"); 72 | 73 | if (libct_process_wait(epr, &status)) 74 | return fail("Unable to wait a process"); 75 | if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL) 76 | return fail("Unexpected status %x\n", status); 77 | 78 | if (libct_process_wait(pr, &status)) 79 | return fail("Unable to wait a process"); 80 | if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL) 81 | return fail("Unexpected status %x\n", status); 82 | 83 | 84 | libct_container_wait(ct); 85 | libct_container_destroy(ct); 86 | libct_process_desc_destroy(pd); 87 | libct_process_destroy(epr); 88 | libct_session_close(s); 89 | 90 | return pass("CT is created and entered"); 91 | } 92 | -------------------------------------------------------------------------------- /test/ct_ext_mount.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test external bind mount works 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "test.h" 16 | 17 | #define FS_ROOT "libct_test_root" 18 | #define FS_EXT "libct_test_external" 19 | #define FS_ACT "libct_test_actions" 20 | #define FS_PTS "libct_test_devpts" 21 | #define FS_DIR "dir" 22 | #define FS_FILE "file" 23 | 24 | static int check_fs_data(void *a) 25 | { 26 | int fd; 27 | int *fs_data = a; 28 | struct statfs sfs; 29 | 30 | fd = open("/" FS_DIR "/" FS_FILE, O_RDONLY); 31 | if (fd < 0) 32 | return 0; 33 | 34 | close(fd); 35 | 36 | if (statfs("/" FS_PTS, &sfs)) { 37 | printf("stafs(%s): %m", FS_PTS); 38 | return 1; 39 | } 40 | 41 | if (sfs.f_type != DEVPTS_SUPER_MAGIC) { 42 | printf("Unexpected fs magic %lx instead of %x\n", sfs.f_type, DEVPTS_SUPER_MAGIC); 43 | return 1; 44 | } 45 | 46 | if (access(FS_ACT "/hello", F_OK)) { 47 | tst_err("Unable to access %s", FS_ACT "/hello"); 48 | return 1; 49 | } 50 | 51 | *fs_data = 1; 52 | return 0; 53 | } 54 | 55 | int main(int argc, char **argv) 56 | { 57 | char *fs_data; 58 | libct_session_t s; 59 | ct_handler_t ct; 60 | ct_process_desc_t p; 61 | int fs_err = 0; 62 | char *preargv[] = {"echo", "premount", NULL}; 63 | struct libct_cmd premount = { 64 | .path = "echo", 65 | .argv = preargv, 66 | }; 67 | char *postargv[] = {"touch", FS_ROOT "/" FS_ACT "/hello", NULL}; 68 | struct libct_cmd postmount = { 69 | .path = "touch", 70 | .argv = postargv, 71 | }; 72 | 73 | mkdir(FS_EXT, 0600); 74 | if (creat(FS_EXT "/" FS_FILE, 0600) < 0) 75 | return tst_perr("Can't create file"); 76 | 77 | mkdir(FS_ROOT, 0600); 78 | mkdir(FS_ROOT "/" FS_DIR, 0600); 79 | unlink(FS_ROOT "/" FS_DIR "/" FS_FILE); 80 | 81 | fs_data = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 82 | MAP_SHARED | MAP_ANON, 0, 0); 83 | fs_data[0] = '\0'; 84 | 85 | s = libct_session_open_local(); 86 | ct = libct_container_create(s, "test"); 87 | p = libct_process_desc_create(s); 88 | printf("Set root\n"); 89 | libct_fs_set_root(ct, FS_ROOT); 90 | printf("Set bind\n"); 91 | libct_fs_add_bind_mount(ct, FS_EXT, FS_DIR, 0); 92 | libct_fs_add_mount(ct, "test_devpts", FS_PTS, 0, "devpts", "newinstance"); 93 | libct_fs_add_mount_with_actions(ct, "test_actions", FS_ACT, 0, "tmpfs", "", &premount, &postmount); 94 | printf("Spawn\n"); 95 | libct_container_spawn_cb(ct, p, check_fs_data, fs_data); 96 | printf("Done\n"); 97 | libct_container_wait(ct); 98 | libct_container_destroy(ct); 99 | libct_session_close(s); 100 | 101 | if (rmdir(FS_ROOT "/" FS_DIR) < 0) 102 | fs_err |= 1; 103 | if (rmdir(FS_ROOT "/" FS_PTS) < 0) 104 | fs_err |= 16; 105 | if (rmdir(FS_ROOT "/" FS_ACT) < 0) 106 | fs_err |= 32; 107 | if (rmdir(FS_ROOT) < 0) 108 | fs_err |= 2; 109 | if (unlink(FS_EXT "/" FS_FILE) < 0) 110 | fs_err |= 4; 111 | if (rmdir(FS_EXT) < 0) 112 | fs_err |= 8; 113 | 114 | if (fs_err) { 115 | printf("FS remove failed %x\n", fs_err); 116 | return fail("FS broken"); 117 | } 118 | 119 | if (!fs_data[0]) 120 | return fail("FS private not accessible"); 121 | 122 | return pass("Subdir as private is OK"); 123 | } 124 | -------------------------------------------------------------------------------- /test/ct_fds.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test empty "container" creation 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "test.h" 10 | 11 | 12 | static int set_ct_alive(void *a) 13 | { 14 | int t; 15 | 16 | if (read(0, &t, sizeof(t)) != sizeof(t)) 17 | return -1; 18 | 19 | t = t * 2; 20 | 21 | if (write(1, &t, sizeof(t)) != sizeof(t)) 22 | return -1; 23 | close(1); 24 | 25 | if (read(3, &t, sizeof(t)) != sizeof(t)) 26 | return -1; 27 | 28 | t = t * 3; 29 | 30 | if (write(2, &t, sizeof(t)) != sizeof(t)) 31 | return -1; 32 | close(2); 33 | 34 | if (read(0, &t, sizeof(t)) != 0) 35 | return -1; 36 | 37 | if (read(3, &t, sizeof(t)) != 0) 38 | return -1; 39 | 40 | return 0; 41 | } 42 | 43 | int check(int *inp, int *outp, int *errp, int *extp, int *fds, int v) 44 | { 45 | int i, t; 46 | 47 | for (i = 0; i < 4; i++) 48 | close(fds[i]); 49 | 50 | t = v; 51 | if (write(inp[1], &t, sizeof(t)) != sizeof(t)) { 52 | fail(); 53 | return -1; 54 | } 55 | close(inp[1]); 56 | if (read(outp[0], &t, sizeof(t)) != sizeof(t)) { 57 | fail(); 58 | return -11; 59 | } 60 | if (t != v * 2) { 61 | fail(); 62 | return -11; 63 | } 64 | if (read(outp[0], &t, sizeof(t)) != 0) { 65 | fail(); 66 | return -11; 67 | } 68 | 69 | t = v; 70 | if (write(extp[1], &t, sizeof(t)) != sizeof(t)) { 71 | fail(); 72 | return -11; 73 | } 74 | close(extp[1]); 75 | if (read(errp[0], &t, sizeof(t)) != sizeof(t)) { 76 | fail(); 77 | return -11; 78 | } 79 | if (t != v * 3) { 80 | fail(); 81 | return -11; 82 | } 83 | if (read(errp[0], &t, sizeof(t)) != 0) { 84 | fail(); 85 | return -1; 86 | } 87 | 88 | return 0; 89 | } 90 | 91 | int main(int argc, char **argv) 92 | { 93 | libct_session_t s; 94 | ct_handler_t ct; 95 | ct_process_desc_t p; 96 | int fds[4], efds[4]; 97 | 98 | int inp[2], outp[2], errp[2], extp[2]; 99 | int einp[2], eoutp[2], eerrp[2], eextp[2]; 100 | 101 | if (pipe(inp) || pipe(outp) || pipe(errp) || pipe(extp)) 102 | return -1; 103 | 104 | fds[0] = inp[0]; 105 | fds[1] = outp[1]; 106 | fds[2] = errp[1]; 107 | fds[3] = extp[0]; 108 | 109 | if (pipe(einp) || pipe(eoutp) || pipe(eerrp) || pipe(eextp)) 110 | return -1; 111 | 112 | efds[0] = einp[0]; 113 | efds[1] = eoutp[1]; 114 | efds[2] = eerrp[1]; 115 | efds[3] = eextp[0]; 116 | 117 | s = libct_session_open_local(); 118 | ct = libct_container_create(s, "test"); 119 | p = libct_process_desc_create(s); 120 | libct_process_desc_set_fds(p, fds, 4); 121 | libct_container_spawn_cb(ct, p, set_ct_alive, NULL); 122 | libct_process_desc_set_fds(p, efds, 4); 123 | libct_container_enter_cb(ct, p, set_ct_alive, NULL); 124 | 125 | if (check(einp, eoutp, eerrp, eextp, efds, 5) || 126 | check(inp, outp, errp, extp, fds, 7)) 127 | return 1; 128 | 129 | 130 | libct_container_wait(ct); 131 | libct_container_destroy(ct); 132 | libct_session_close(s); 133 | 134 | pass("Container is alive"); 135 | return 0; 136 | } 137 | -------------------------------------------------------------------------------- /test/ct_kill_nons.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test that service cgroup works 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "test.h" 13 | 14 | struct ct_arg { 15 | int start_fd; 16 | int *mark; 17 | }; 18 | 19 | static int loop_in_ct(void *a) 20 | { 21 | struct ct_arg *cta = a; 22 | char c = 'a'; 23 | int cpid; 24 | 25 | cpid = fork(); 26 | if (cpid == 0) 27 | goto loop; 28 | 29 | cta->mark[0] = getpid(); 30 | cta->mark[1] = cpid; 31 | 32 | write(cta->start_fd, &c, 1); 33 | 34 | loop: 35 | /* 36 | * Don't close the pipe. If killing cgroup 37 | * failed, test would hang forever FIXME 38 | */ 39 | 40 | while (1) 41 | sleep(10); 42 | exit(1); 43 | } 44 | 45 | int main(int argc, char **argv) 46 | { 47 | struct ct_arg cta; 48 | int p[2]; 49 | libct_session_t s; 50 | ct_handler_t ct; 51 | ct_process_desc_t pd; 52 | ct_process_t pr; 53 | char c; 54 | 55 | pipe(p); 56 | cta.mark = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 57 | MAP_SHARED | MAP_ANON, 0, 0); 58 | cta.mark[0] = 0; 59 | cta.mark[1] = 0; 60 | cta.start_fd = p[1]; 61 | 62 | s = libct_session_open_local(); 63 | ct = libct_container_create(s, "test-k"); 64 | pd = libct_process_desc_create(s); 65 | if (libct_container_set_option(ct, LIBCT_OPT_KILLABLE, NULL)) 66 | return tst_err("can't set killable"); 67 | 68 | pr = libct_container_spawn_cb(ct, pd, loop_in_ct, &cta); 69 | if (libct_handle_is_err(pr)) 70 | return tst_err("can't start CT"); 71 | 72 | close(p[1]); 73 | read(p[0], &c, 1); 74 | 75 | libct_container_kill(ct); 76 | libct_container_wait(ct); 77 | libct_container_destroy(ct); 78 | libct_session_close(s); 79 | 80 | if (read(p[0], &c, 1) != 0) /* FIXME -- this may block on error */ 81 | return fail("Pipes are alive?"); 82 | 83 | if (!cta.mark[0]) 84 | return fail("CT is not alive"); 85 | 86 | if (!cta.mark[1]) 87 | return fail("CT hasn't forked"); 88 | 89 | return pass("killed OK"); 90 | } 91 | -------------------------------------------------------------------------------- /test/ct_net_host.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test how host nic assignment works 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "test.h" 12 | 13 | static int check_ct_net(void *a) 14 | { 15 | int *ct_status = a; 16 | 17 | ct_status[0] = 1; 18 | if (!system("ip link l dm0")) 19 | ct_status[1] = 1; 20 | 21 | return 0; 22 | } 23 | 24 | int main(int argc, char **argv) 25 | { 26 | int *ct_status; 27 | libct_session_t s; 28 | ct_handler_t ct; 29 | ct_process_desc_t p; 30 | ct_process_t pr; 31 | ct_net_t nd; 32 | 33 | test_init(argc, argv); 34 | 35 | ct_status = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 36 | MAP_SHARED | MAP_ANON, 0, 0); 37 | if (ct_status == MAP_FAILED) 38 | return tst_perr("Unable to allocate memory"); 39 | 40 | ct_status[0] = 0; 41 | ct_status[1] = 0; 42 | 43 | system("ip link add name dm0 type dummy"); 44 | if (system("ip link l dm0")) 45 | return tst_err("Can't create dummy device"); 46 | 47 | s = libct_session_open_local(); 48 | if (libct_handle_is_err(s)) 49 | return tst_err("Unable to create a session"); 50 | 51 | ct = libct_container_create(s, "test"); 52 | p = libct_process_desc_create(s); 53 | 54 | if (libct_handle_is_err(ct) || 55 | libct_handle_is_err(p)) 56 | return tst_err("Unable tot create handle"); 57 | 58 | if (libct_container_set_nsmask(ct, CLONE_NEWNET)) 59 | return tst_err("Unable to set nsmask"); 60 | 61 | nd = libct_net_add(ct, CT_NET_HOSTNIC, "dm0"); 62 | if (libct_handle_is_err(nd)) { 63 | system("ip link del dm0"); 64 | return tst_err("Can't add hostnic"); 65 | } 66 | pr = libct_container_spawn_cb(ct, p, check_ct_net, ct_status); 67 | if (libct_handle_is_err(pr)) { 68 | system("ip link del dm0"); 69 | return tst_err("Can't spawn CT"); 70 | } 71 | 72 | libct_container_wait(ct); 73 | libct_container_destroy(ct); 74 | libct_session_close(s); 75 | 76 | system("ip link del dm0"); 77 | 78 | if (!ct_status[0]) 79 | return fail("CT is not alive"); 80 | if (!ct_status[1]) 81 | return fail("Netdevice not assigned"); 82 | 83 | return pass("HostNic works OK"); 84 | } 85 | -------------------------------------------------------------------------------- /test/ct_net_veth.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test that veth pair can be created 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "test.h" 13 | 14 | #define VETH_HOST_NAME "hveth0" 15 | #define VETH_CT_NAME "cveth0" 16 | 17 | struct ct_arg { 18 | int wait_pipe; 19 | int *mark; 20 | }; 21 | 22 | static int check_ct_net(void *a) 23 | { 24 | struct ct_arg *ca = a; 25 | char c; 26 | 27 | ca->mark[0] = 1; 28 | if (!system("ip link l " VETH_CT_NAME "")) 29 | ca->mark[2] = 1; 30 | 31 | read(ca->wait_pipe, &c, 1); 32 | return 0; 33 | } 34 | 35 | int main(int argc, char **argv) 36 | { 37 | int p[2]; 38 | struct ct_arg ca; 39 | libct_session_t s; 40 | ct_handler_t ct; 41 | ct_process_desc_t pd; 42 | ct_process_t pr; 43 | struct ct_net_veth_arg va; 44 | ct_net_t nd, nd_peer; 45 | 46 | ca.mark = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 47 | MAP_SHARED | MAP_ANON, 0, 0); 48 | pipe(p); 49 | 50 | ca.mark[0] = 0; 51 | ca.mark[1] = 0; 52 | ca.mark[2] = 0; 53 | ca.wait_pipe = p[0]; 54 | 55 | va.host_name = VETH_HOST_NAME; 56 | va.ct_name = VETH_CT_NAME; 57 | 58 | s = libct_session_open_local(); 59 | ct = libct_container_create(s, "test"); 60 | pd = libct_process_desc_create(s); 61 | libct_container_set_nsmask(ct, CLONE_NEWNET); 62 | 63 | nd = libct_net_add(ct, CT_NET_VETH, &va); 64 | if (libct_handle_is_err(nd)) 65 | return tst_err("Can't add hostnic"); 66 | 67 | nd_peer = libct_net_dev_get_peer(nd); 68 | if (libct_handle_is_err(nd_peer)) 69 | return tst_err("Can't get a veth peer"); 70 | 71 | if (libct_net_dev_set_mac_addr(nd_peer, "00:11:22:33:44:66")) 72 | return tst_err("Can't set mac"); 73 | 74 | if (libct_net_dev_set_mac_addr(nd, "00:11:22:33:44:55")) 75 | return tst_err("Can't set mac"); 76 | 77 | if (libct_net_dev_add_ip_addr(nd, "192.168.123.123/32")) 78 | return tst_err("Can't set addr"); 79 | 80 | pr = libct_container_spawn_cb(ct, pd, check_ct_net, &ca); 81 | if (libct_handle_is_err(pr)) 82 | return tst_err("Can't spawn CT"); 83 | 84 | if (!system("ip link l " VETH_HOST_NAME "")) 85 | ca.mark[1] = 1; 86 | 87 | write(p[1], "a", 1); 88 | 89 | libct_container_wait(ct); 90 | libct_container_destroy(ct); 91 | libct_session_close(s); 92 | 93 | if (!ca.mark[0]) 94 | return fail("CT is not alive"); 95 | if (!ca.mark[1]) 96 | return fail("VETH not created"); 97 | if (!ca.mark[2]) 98 | return fail("VETH not assigned"); 99 | 100 | return pass("VETH works OK"); 101 | } 102 | -------------------------------------------------------------------------------- /test/ct_pause.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test empty "container" creation 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "test.h" 12 | 13 | static int set_ct_alive(void *a) 14 | { 15 | int *pfd = (int *)a; 16 | char buf; 17 | 18 | close(pfd[1]); 19 | if (read(pfd[0], &buf, sizeof(buf)) < 0) 20 | return 1; 21 | 22 | close(pfd[0]); 23 | return 0; 24 | } 25 | 26 | int main(int argc, char **argv) 27 | { 28 | int pfd[2], status; 29 | libct_session_t s; 30 | ct_handler_t ct; 31 | ct_process_desc_t p; 32 | ct_process_t pr; 33 | 34 | test_init(); 35 | 36 | if (pipe(pfd) < 0) 37 | return 1; 38 | 39 | s = libct_session_open_local(); 40 | if (libct_handle_is_err(s)) 41 | return fail("Unable to create a new session"); 42 | 43 | ct = libct_container_create(s, "test"); 44 | if (libct_handle_is_err(ct)) 45 | return fail("Unable to create a container object"); 46 | libct_controller_add(ct, CTL_FREEZER); 47 | 48 | p = libct_process_desc_create(s); 49 | if (libct_handle_is_err(p)) 50 | return fail("Unable to create a process descriptor"); 51 | 52 | pr = libct_container_spawn_cb(ct, p, set_ct_alive, pfd); 53 | close(pfd[0]); 54 | if (libct_handle_is_err(pr)) 55 | return fail("Unable to start CT"); 56 | 57 | if (libct_container_pause(ct)) { 58 | fail("Unable to pause"); 59 | return 1; 60 | } 61 | if (libct_container_resume(ct)) { 62 | fail("Unable to resume"); 63 | return 1; 64 | } 65 | 66 | close(pfd[1]); 67 | if (wait(&status) < 0) 68 | return 1; 69 | libct_container_wait(ct); 70 | libct_container_destroy(ct); 71 | libct_session_close(s); 72 | 73 | if (status == 0) 74 | pass("Container is alive"); 75 | else { 76 | fail("%x", status); 77 | return 1; 78 | } 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /test/ct_pid_enter.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test entering into living container with pidns 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "test.h" 11 | 12 | #ifndef CLONE_NEWPID 13 | #define CLONE_NEWPID 0x20000000 14 | #endif 15 | 16 | struct ct_arg { 17 | int wait_fd; 18 | int *mark; 19 | }; 20 | 21 | static int set_ct_alive(void *a) 22 | { 23 | struct ct_arg *cta = a; 24 | char c; 25 | 26 | cta->mark[0] = getpid(); 27 | cta->mark[1] = getppid(); 28 | read(cta->wait_fd, &c, 1); 29 | return 0; 30 | } 31 | 32 | static int set_ct_enter(void *a) 33 | { 34 | struct ct_arg *cta = a; 35 | cta->mark[2] = getpid(); 36 | cta->mark[3] = getppid(); 37 | return 0; 38 | } 39 | 40 | int main(int argc, char **argv) 41 | { 42 | struct ct_arg cta; 43 | int p[2], status; 44 | libct_session_t s; 45 | ct_handler_t ct; 46 | ct_process_desc_t pd; 47 | ct_process_t pr; 48 | 49 | pipe(p); 50 | cta.mark = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 51 | MAP_SHARED | MAP_ANON, 0, 0); 52 | cta.mark[0] = -1; 53 | cta.mark[1] = -1; 54 | cta.mark[2] = -1; 55 | cta.mark[3] = -1; 56 | cta.wait_fd = p[0]; 57 | 58 | s = libct_session_open_local(); 59 | ct = libct_container_create(s, "test"); 60 | pd = libct_process_desc_create(s); 61 | libct_container_set_nsmask(ct, CLONE_NEWPID); 62 | libct_container_spawn_cb(ct, pd, set_ct_alive, &cta); 63 | pr = libct_container_enter_cb(ct, pd, set_ct_enter, &cta); 64 | libct_process_wait(pr, &status); 65 | 66 | write(p[1], "a", 1); 67 | libct_container_wait(ct); 68 | libct_container_destroy(ct); 69 | libct_session_close(s); 70 | 71 | if (cta.mark[0] == -1) 72 | return fail("CT is not alive"); 73 | 74 | if (cta.mark[1] == -1) 75 | return fail("CT is not enterable"); 76 | 77 | printf("pids (%d:%d) (%d:%d)\n", cta.mark[0], cta.mark[1], cta.mark[2], cta.mark[3]); 78 | 79 | return pass("OK"); 80 | } 81 | -------------------------------------------------------------------------------- /test/ct_private_subdir.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test subdir as private FS (CT_FS_SUBDIR) 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "test.h" 14 | 15 | #define FS_ROOT "libct_test_root" 16 | #define FS_PRIVATE "libct_test_private" 17 | #define FS_FILE "libct_test_file" 18 | 19 | static int check_fs_data(void *a) 20 | { 21 | int fd; 22 | int *fs_data = a; 23 | 24 | fd = open("/" FS_FILE, O_RDONLY); 25 | if (fd < 0) 26 | return 0; 27 | 28 | *fs_data = 1; 29 | close(fd); 30 | return 0; 31 | } 32 | 33 | int main(int argc, char **argv) 34 | { 35 | char *fs_data; 36 | libct_session_t s; 37 | ct_handler_t ct; 38 | ct_process_desc_t p; 39 | int fs_err = 0; 40 | 41 | mkdir(FS_ROOT, 0600); 42 | mkdir(FS_PRIVATE, 0600); 43 | if (creat(FS_PRIVATE "/" FS_FILE, 0600) < 0) 44 | return tst_perr("Can't create file"); 45 | unlink(FS_ROOT "/" FS_FILE); 46 | 47 | fs_data = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 48 | MAP_SHARED | MAP_ANON, 0, 0); 49 | fs_data[0] = '\0'; 50 | 51 | s = libct_session_open_local(); 52 | ct = libct_container_create(s, "test"); 53 | p = libct_process_desc_create(s); 54 | libct_fs_set_root(ct, FS_ROOT); 55 | libct_fs_set_private(ct, CT_FS_SUBDIR, FS_PRIVATE); 56 | libct_container_spawn_cb(ct, p, check_fs_data, fs_data); 57 | libct_container_wait(ct); 58 | libct_container_destroy(ct); 59 | libct_session_close(s); 60 | 61 | if (unlink(FS_PRIVATE "/" FS_FILE) < 0) 62 | fs_err |= 1; 63 | if (rmdir(FS_PRIVATE) < 0) 64 | fs_err |= 2; 65 | if (rmdir(FS_ROOT) < 0) 66 | fs_err |= 3; 67 | 68 | if (fs_err) { 69 | printf("FS remove failed %x\n", fs_err); 70 | return fail("FS broken"); 71 | } 72 | 73 | if (!fs_data[0]) 74 | return fail("FS private not accessible"); 75 | 76 | return pass("Subdir as private is OK"); 77 | } 78 | -------------------------------------------------------------------------------- /test/ct_private_subdir_ns.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test subdir as private FS in new mount namespace 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "test.h" 14 | 15 | #ifndef CLONE_NEWNS 16 | #define CLONE_NEWNS 0x00020000 17 | #endif 18 | 19 | #define FS_ROOT "libct_test_root_ns" 20 | #define FS_PRIVATE "libct_test_private_ns" 21 | #define FS_FILE "libct_test_file_ns" 22 | 23 | static int check_fs_data(void *a) 24 | { 25 | int fd; 26 | int *fs_data = a; 27 | 28 | fd = open("/" FS_FILE, O_RDONLY); 29 | if (fd < 0) 30 | return 0; 31 | 32 | *fs_data = 1; 33 | close(fd); 34 | return 0; 35 | } 36 | 37 | int main(int argc, char **argv) 38 | { 39 | char *fs_data; 40 | libct_session_t s; 41 | ct_handler_t ct; 42 | ct_process_desc_t p; 43 | int fs_err = 0; 44 | 45 | mkdir(FS_ROOT, 0600); 46 | mkdir(FS_PRIVATE, 0600); 47 | if (creat(FS_PRIVATE "/" FS_FILE, 0600) < 0) { 48 | tst_perr("Can't create file"); 49 | return 2; 50 | } 51 | unlink(FS_ROOT "/" FS_FILE); 52 | 53 | fs_data = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 54 | MAP_SHARED | MAP_ANON, 0, 0); 55 | fs_data[0] = '\0'; 56 | 57 | s = libct_session_open_local(); 58 | ct = libct_container_create(s, "test"); 59 | p = libct_process_desc_create(s); 60 | libct_container_set_nsmask(ct, CLONE_NEWNS); 61 | libct_fs_set_root(ct, FS_ROOT); 62 | libct_fs_set_private(ct, CT_FS_SUBDIR, FS_PRIVATE); 63 | libct_container_spawn_cb(ct, p, check_fs_data, fs_data); 64 | libct_container_wait(ct); 65 | libct_container_destroy(ct); 66 | libct_session_close(s); 67 | 68 | if (unlink(FS_PRIVATE "/" FS_FILE) < 0) 69 | fs_err |= 1; 70 | if (rmdir(FS_PRIVATE) < 0) 71 | fs_err |= 2; 72 | if (rmdir(FS_ROOT) < 0) 73 | fs_err |= 3; 74 | 75 | if (fs_err) { 76 | printf("FS remove failed %x\n", fs_err); 77 | return fail("FS broken"); 78 | } 79 | 80 | if (!fs_data[0]) 81 | return fail("FS private not accessible"); 82 | 83 | return pass("Subdir as private is OK"); 84 | } 85 | -------------------------------------------------------------------------------- /test/ct_proc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test how proc automount works (LIBCT_OPT_AUTO_PROC_MOUNT) 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "test.h" 13 | 14 | #ifndef CLONE_NEWNS 15 | #define CLONE_NEWNS 0x00020000 16 | #endif 17 | 18 | #ifndef CLONE_NEWPID 19 | #define CLONE_NEWPID 0x20000000 20 | #endif 21 | 22 | static int set_ct_root_pids(void *a) 23 | { 24 | int *pids = a; 25 | char buf[32]; 26 | 27 | memset(buf, 0, sizeof(buf)); 28 | pids[0] = getpid(); 29 | readlink("/proc/self", buf, sizeof(buf)); 30 | pids[1] = atoi(buf); 31 | 32 | return 0; 33 | } 34 | 35 | int main(int argc, char **argv) 36 | { 37 | int *ct_root_pids; 38 | libct_session_t s; 39 | ct_handler_t ct; 40 | ct_process_desc_t p; 41 | 42 | test_init(); 43 | 44 | ct_root_pids = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 45 | MAP_SHARED | MAP_ANON, 0, 0); 46 | ct_root_pids[0] = 0; 47 | ct_root_pids[1] = 0; 48 | 49 | s = libct_session_open_local(); 50 | ct = libct_container_create(s, "test"); 51 | p = libct_process_desc_create(s); 52 | if (libct_container_set_nsmask(ct, CLONE_NEWPID | CLONE_NEWNS)) { 53 | tst_err("No pid & mount NS"); 54 | return 2; 55 | } 56 | 57 | libct_container_set_option(ct, LIBCT_OPT_AUTO_PROC_MOUNT, NULL); 58 | 59 | libct_container_spawn_cb(ct, p, set_ct_root_pids, ct_root_pids); 60 | libct_container_wait(ct); 61 | libct_container_destroy(ct); 62 | libct_session_close(s); 63 | 64 | /* Should be init */ 65 | if ((ct_root_pids[0] != 1) || (ct_root_pids[1] != 1)) 66 | return fail("Pid mismatch"); 67 | else 68 | return pass("Pids are OK"); 69 | } 70 | -------------------------------------------------------------------------------- /test/ct_root.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test simple chroot()-ed CT 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "test.h" 14 | 15 | #define FS_ROOT "libct_test_root" 16 | #define FS_DATA "libct_test_string" 17 | #define FS_FILE "file" 18 | 19 | static int check_fs_data(void *a) 20 | { 21 | int fd; 22 | 23 | fd = open("/" FS_FILE, O_RDONLY); 24 | if (fd < 0) 25 | return 1; 26 | 27 | read(fd, a, sizeof(FS_DATA)); 28 | close(fd); 29 | return 0; 30 | } 31 | 32 | int main(int argc, char **argv) 33 | { 34 | int fd; 35 | char *fs_data; 36 | libct_session_t s; 37 | ct_handler_t ct; 38 | ct_process_desc_t p; 39 | ct_process_t pr; 40 | 41 | mkdir(FS_ROOT, 0600); 42 | fd = open(FS_ROOT "/" FS_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0600); 43 | if (fd < 0) { 44 | tst_perr("Can't create file"); 45 | return 2; 46 | } 47 | 48 | write(fd, FS_DATA, sizeof(FS_DATA)); 49 | close(fd); 50 | 51 | fs_data = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 52 | MAP_SHARED | MAP_ANON, 0, 0); 53 | fs_data[0] = '\0'; 54 | 55 | s = libct_session_open_local(); 56 | ct = libct_container_create(s, "test"); 57 | p = libct_process_desc_create(s); 58 | if (libct_fs_set_root(ct, FS_ROOT)) 59 | return fail("Unable to set root"); 60 | pr = libct_container_spawn_cb(ct, p, check_fs_data, fs_data); 61 | if (libct_handle_is_err(pr)) 62 | return fail("Unable to start CT"); 63 | if (libct_container_wait(ct)) 64 | return fail("Unable to wait CT"); 65 | libct_container_destroy(ct); 66 | libct_session_close(s); 67 | 68 | unlink(FS_ROOT "/" FS_FILE); 69 | rmdir(FS_ROOT); 70 | 71 | if (strcmp(fs_data, FS_DATA)) 72 | return fail("FS not accessed"); 73 | else 74 | return pass("FS is OK"); 75 | } 76 | -------------------------------------------------------------------------------- /test/ct_root_enter.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test that entering into chroot()-ed CT works 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "test.h" 15 | 16 | #define FS_ROOT "libct_test_root" 17 | #define FS_DATA "libct_test_string" 18 | #define FS_FILE "file" 19 | #define ENTER_DOFF 1024 20 | 21 | struct ct_arg { 22 | int wait_fd; 23 | char *fs_data; 24 | }; 25 | 26 | static int read_fs_data(char *a) 27 | { 28 | int fd; 29 | 30 | fd = open("/" FS_FILE, O_RDONLY); 31 | if (fd < 0) 32 | return 1; 33 | 34 | read(fd, a, sizeof(FS_DATA)); 35 | close(fd); 36 | 37 | return 0; 38 | } 39 | 40 | static int ct_main_fn(void *a) 41 | { 42 | struct ct_arg *cta = a; 43 | char c; 44 | 45 | if (read_fs_data(cta->fs_data)) 46 | return 1; 47 | 48 | read(cta->wait_fd, &c, 1); 49 | return 0; 50 | } 51 | 52 | static int ct_enter_fn(void *a) 53 | { 54 | struct ct_arg *cta = a; 55 | 56 | return read_fs_data(cta->fs_data + ENTER_DOFF); 57 | } 58 | 59 | int main(int argc, char **argv) 60 | { 61 | int fd, p[2], status; 62 | struct ct_arg cta; 63 | libct_session_t s; 64 | ct_handler_t ct; 65 | ct_process_desc_t pd; 66 | ct_process_t pr; 67 | 68 | pipe(p); 69 | 70 | mkdir(FS_ROOT, 0600); 71 | fd = open(FS_ROOT "/" FS_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0600); 72 | if (fd < 0) { 73 | tst_perr("Can't create file"); 74 | return 2; 75 | } 76 | 77 | write(fd, FS_DATA, sizeof(FS_DATA)); 78 | close(fd); 79 | 80 | cta.fs_data = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 81 | MAP_SHARED | MAP_ANON, 0, 0); 82 | cta.fs_data[0] = '\0'; 83 | cta.fs_data[ENTER_DOFF] = '\0'; 84 | cta.wait_fd = p[0]; 85 | 86 | s = libct_session_open_local(); 87 | ct = libct_container_create(s, "test"); 88 | pd = libct_process_desc_create(s); 89 | libct_fs_set_root(ct, FS_ROOT); 90 | libct_container_spawn_cb(ct, pd, ct_main_fn, &cta); 91 | pr = libct_container_enter_cb(ct, pd, ct_enter_fn, &cta); 92 | if (libct_handle_is_err(pr)) 93 | fail("Unable to enter into CT"); 94 | libct_process_wait(pr, &status); 95 | write(p[1], "a", 1); 96 | libct_container_wait(ct); 97 | libct_container_destroy(ct); 98 | libct_session_close(s); 99 | 100 | unlink(FS_ROOT "/" FS_FILE); 101 | rmdir(FS_ROOT); 102 | 103 | if (strcmp(cta.fs_data, FS_DATA)) 104 | return fail("FS not accessed"); 105 | 106 | if (strcmp(cta.fs_data + ENTER_DOFF, FS_DATA)) 107 | return fail("FS not entered"); 108 | 109 | return pass("FS is created and entered"); 110 | } 111 | -------------------------------------------------------------------------------- /test/ct_service.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test that service cgroup works 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "test.h" 13 | 14 | struct ct_arg { 15 | int start_fd; 16 | int wait_fd; 17 | int *mark; 18 | }; 19 | 20 | static int set_ct_alive(void *a) 21 | { 22 | struct ct_arg *cta = a; 23 | char c = 'a'; 24 | 25 | cta->mark[0] = getpid(); 26 | write(cta->start_fd, &c, 1); 27 | read(cta->wait_fd, &c, 1); 28 | return 0; 29 | } 30 | 31 | static int check_service_cg(int pid) 32 | { 33 | FILE *f; 34 | char buf[32]; 35 | 36 | f = fopen("/sys/fs/cgroup/.libct/test-s/tasks", "r"); 37 | if (!f) { 38 | perror("No file\n"); 39 | return 0; 40 | } 41 | 42 | memset(buf, 0, sizeof(buf)); 43 | if (!fgets(buf, sizeof(buf), f)) { 44 | fclose(f); 45 | return 0; 46 | } 47 | 48 | fclose(f); 49 | 50 | return atoi(buf) == pid; 51 | } 52 | 53 | int main(int argc, char **argv) 54 | { 55 | struct ct_arg cta; 56 | int p[2], p2[2]; 57 | libct_session_t s; 58 | ct_handler_t ct; 59 | ct_process_desc_t pd; 60 | ct_process_t pr; 61 | int cg_ok = 0; 62 | char c; 63 | 64 | pipe(p); 65 | pipe(p2); 66 | cta.mark = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 67 | MAP_SHARED | MAP_ANON, 0, 0); 68 | cta.mark[0] = 0; 69 | cta.start_fd = p2[1]; 70 | cta.wait_fd = p[0]; 71 | 72 | s = libct_session_open_local(); 73 | ct = libct_container_create(s, "test-s"); 74 | pd = libct_process_desc_create(s); 75 | if (libct_container_set_option(ct, LIBCT_OPT_KILLABLE, NULL)) { 76 | tst_err("can't set killable"); 77 | return 2; 78 | } 79 | 80 | pr = libct_container_spawn_cb(ct, pd, set_ct_alive, &cta); 81 | if (libct_handle_is_err(pr)) { 82 | tst_err("can't start CT"); 83 | return 2; 84 | } 85 | 86 | read(p2[0], &c, 1); 87 | if (cta.mark[0]) 88 | cg_ok = check_service_cg(cta.mark[0]); 89 | write(p[1], &c, 1); 90 | 91 | libct_container_wait(ct); 92 | libct_container_destroy(ct); 93 | libct_session_close(s); 94 | 95 | if (!cta.mark[0]) 96 | return fail("CT is not alive"); 97 | 98 | if (!cg_ok) 99 | return fail("Service CG is not there"); 100 | 101 | return pass("service CG works OK"); 102 | } 103 | -------------------------------------------------------------------------------- /test/ct_switch.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test entering into living container 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "test.h" 14 | 15 | int main(int argc, char **argv) 16 | { 17 | libct_session_t s; 18 | ct_handler_t ct1, ct2; 19 | ct_process_desc_t pd; 20 | ct_process_t pr; 21 | char root1[] = "libct_root1"; 22 | char root2[] = "libct_root2"; 23 | char buf[1024]; 24 | 25 | test_init(); 26 | 27 | mkdir(root1, 0700); 28 | mkdir(root2, 0700); 29 | snprintf(buf, sizeof(buf), "%s/%s", root1, "test1"); 30 | mkdir(buf, 0700); 31 | snprintf(buf, sizeof(buf), "%s/%s", root2, "test2"); 32 | mkdir(buf, 0700); 33 | 34 | s = libct_session_open_local(); 35 | ct1 = libct_container_create(s, "test1"); 36 | ct2 = libct_container_create(s, "test2"); 37 | 38 | libct_container_set_nsmask(ct1, CLONE_NEWNS); 39 | libct_container_set_nsmask(ct2, CLONE_NEWNS); 40 | libct_fs_set_root(ct1, root1); 41 | libct_fs_set_root(ct2, root2); 42 | 43 | libct_container_set_option(ct1, LIBCT_OPT_TASKLESS, 0); 44 | libct_container_set_option(ct2, LIBCT_OPT_TASKLESS, 0); 45 | 46 | pd = libct_process_desc_create(s); 47 | 48 | pr = libct_container_spawn_cb(ct1, pd, NULL, NULL); 49 | if (libct_handle_is_err(pr)) { 50 | return fail("Unable to start CT"); 51 | } 52 | 53 | pr = libct_container_spawn_cb(ct2, pd, NULL, NULL); 54 | if (libct_handle_is_err(pr)) { 55 | return fail("Unable to start CT"); 56 | } 57 | 58 | if (libct_container_switch(ct1)) 59 | return fail("Unable to switch CT"); 60 | if (access("/test1", F_OK)) 61 | return fail("Unable to access /test1"); 62 | if (libct_container_switch(ct2)) 63 | return fail("Unable to switch CT"); 64 | if (access("/test2", F_OK)) 65 | return fail("Unable to access /test2"); 66 | 67 | libct_container_kill(ct1); 68 | libct_container_kill(ct2); 69 | libct_container_destroy(ct1); 70 | libct_container_destroy(ct2); 71 | libct_session_close(s); 72 | 73 | return pass("CT is created and entered"); 74 | } 75 | -------------------------------------------------------------------------------- /test/ct_userns.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test empty "container" creation 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "test.h" 15 | 16 | #define CT_USERNS_ROOT "ct_userns_root" 17 | 18 | static int set_ct_alive(void *a) 19 | { 20 | struct stat st; 21 | 22 | if (getuid() != 0) 23 | return -1; 24 | if (getgid() != 0) 25 | return -1; 26 | 27 | if (stat("test", &st)) 28 | return -1; 29 | 30 | if (st.st_uid || st.st_gid) 31 | return -1; 32 | 33 | *(int *)a = 1; 34 | return 0; 35 | } 36 | 37 | int main(int argc, char **argv) 38 | { 39 | int *ct_alive; 40 | libct_session_t s; 41 | ct_handler_t ct; 42 | ct_process_desc_t p; 43 | ct_process_t pr; 44 | 45 | ct_alive = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 46 | MAP_SHARED | MAP_ANON, 0, 0); 47 | *ct_alive = 0; 48 | 49 | s = libct_session_open_local(); 50 | ct = libct_container_create(s, "test"); 51 | p = libct_process_desc_create(s); 52 | if (libct_container_set_nsmask(ct, CLONE_NEWPID | CLONE_NEWUSER | CLONE_NEWNS)) 53 | return fail("Unable to set nsmask"); 54 | 55 | unshare(CLONE_NEWNS); 56 | mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL); 57 | 58 | umask(0); 59 | mkdir(CT_USERNS_ROOT, 0777); 60 | 61 | if (mount("ct_user", CT_USERNS_ROOT, "tmpfs", 0, NULL)) 62 | return -1; 63 | libct_fs_set_root(ct, CT_USERNS_ROOT); 64 | 65 | mkdir(CT_USERNS_ROOT "/test", 0777); 66 | chown(CT_USERNS_ROOT "/test", 120000, 140000); 67 | 68 | if (libct_userns_add_uid_map(ct, 0, 120000, 1100) || 69 | libct_userns_add_uid_map(ct, 1100, 130000, 1200) || 70 | libct_userns_add_gid_map(ct, 0, 140000, 1200) || 71 | libct_userns_add_gid_map(ct, 1200, 150000, 1100)) 72 | return fail("Unable to set {u,g}id mappings"); 73 | pr = libct_container_spawn_cb(ct, p, set_ct_alive, ct_alive); 74 | if (libct_handle_is_err(pr)) 75 | return fail("Unable to start CT"); 76 | if (libct_container_wait(ct)) 77 | return fail("Unable to wait CT"); 78 | libct_container_destroy(ct); 79 | libct_session_close(s); 80 | 81 | if (!*ct_alive) 82 | return fail("Container is not alive"); 83 | else 84 | return pass("Container is alive"); 85 | } 86 | -------------------------------------------------------------------------------- /test/ct_userns_self.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test empty "container" creation 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "test.h" 15 | 16 | static int set_ct_alive(void *a) 17 | { 18 | if (getuid() != 0) 19 | return -1; 20 | if (getgid() != 0) 21 | return -1; 22 | 23 | *(int *)a = 1; 24 | return 0; 25 | } 26 | 27 | int main(int argc, char **argv) 28 | { 29 | int *ct_alive, status; 30 | libct_session_t s; 31 | ct_handler_t ct; 32 | ct_process_desc_t p; 33 | ct_process_t pr; 34 | 35 | ct_alive = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 36 | MAP_SHARED | MAP_ANON, 0, 0); 37 | *ct_alive = 0; 38 | 39 | s = libct_session_open_local(); 40 | ct = libct_container_create(s, "test"); 41 | p = libct_process_desc_create(s); 42 | if (libct_container_set_nsmask(ct, CLONE_NEWPID | CLONE_NEWUSER | CLONE_NEWNS)) 43 | return fail("Unable to set nsmask"); 44 | 45 | if (libct_userns_add_uid_map(ct, 0, getuid(), 1) || 46 | libct_userns_add_gid_map(ct, 0, getgid(), 1)) 47 | return fail("Unable to set {u,g}id mappings"); 48 | if (libct_fs_add_mount(ct, "tmpfs", "/tmp", 0, "tmpfs", NULL)) 49 | return fail("Unable to add /tmp"); 50 | pr = libct_container_spawn_cb(ct, p, set_ct_alive, ct_alive); 51 | if (libct_handle_is_err(pr)) 52 | return fail("Unable to start CT"); 53 | if (libct_process_wait(pr, &status)) 54 | return fail("Unable to wait process"); 55 | if (libct_container_wait(ct)) 56 | return fail("Unable to wait CT"); 57 | libct_container_destroy(ct); 58 | libct_session_close(s); 59 | 60 | if (!*ct_alive) 61 | return fail("Container is not alive"); 62 | else 63 | return pass("Container is alive"); 64 | } 65 | -------------------------------------------------------------------------------- /test/file_piggy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char **argv) 8 | { 9 | int fd, len, ret; 10 | sigset_t mask; 11 | int sig; 12 | 13 | if (getsid(0) != getpid()) 14 | return 1; 15 | 16 | sigemptyset(&mask); 17 | sigaddset(&mask, SIGINT); 18 | sigprocmask(SIG_BLOCK, &mask, NULL); 19 | write(1, "ok\n", 3); 20 | 21 | sigwait(&mask, &sig); 22 | 23 | /* usage: piggy file_name data_to_put_there */ 24 | if (argc < 3) 25 | return 1; 26 | 27 | fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0600); 28 | if (fd < 0) 29 | return 1; 30 | 31 | len = strlen(argv[2]); 32 | ret = write(fd, argv[2], len); 33 | close(fd); 34 | 35 | if (ret != len) { 36 | unlink(argv[1]); 37 | return 1; 38 | } 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /test/test.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBCT_TEST_H__ 2 | #define __LIBCT_TEST_H__ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | static inline void test_init() 10 | { 11 | libct_log_init(STDERR_FILENO, LOG_DEBUG); 12 | } 13 | 14 | static inline int __tst_msg(int code, const char *format, ...) 15 | { 16 | va_list vl; 17 | va_start(vl, format); 18 | vprintf(format, vl); 19 | va_end(vl); 20 | return code; 21 | } 22 | 23 | #define tst_err(fmt, ...) \ 24 | __tst_msg(2, "%s:%d: Error: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__) 25 | 26 | #define tst_perr(fmt, ...) \ 27 | __tst_msg(2, "%s:%d: Error: " fmt ": %m\n", __func__, __LINE__, ##__VA_ARGS__) 28 | 29 | #define pass(fmt, ...) \ 30 | __tst_msg(0, "%s:%d: PASS: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__) 31 | 32 | #define fail(fmt, ...) \ 33 | __tst_msg(1, "%s:%d: FAIL: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__) 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /test/vz_cgroup_blkio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "test.h" 10 | 11 | #define FS_ROOT "/" 12 | #define VZCTLDEV "/dev/vzctl" 13 | 14 | /* FIXME duplicates vziolimit.h */ 15 | struct iolimit_state { 16 | unsigned int id; 17 | unsigned int speed; 18 | unsigned int burst; 19 | unsigned int latency; 20 | }; 21 | #define VZCTL_GET_IOPSLIMIT _IOR('I', 3, struct iolimit_state) 22 | 23 | 24 | #define STR_HELPER(x) #x 25 | #define STR(x) STR_HELPER(x) 26 | 27 | #define CT_ID 1339 28 | #define CT_NAME STR(CT_ID) 29 | #define IOPSLIMIT 1 30 | #define IOPSLIMIT_STR STR(IOPSLIMIT) 31 | #define IOPRIOLIMIT 7 32 | #define IOPRIOLIMIT_STR STR(IOPRIOLIMIT) 33 | 34 | int is_iopslimit_correct(unsigned int expected_limit) 35 | { 36 | int fd = -1; 37 | int ret = 0; 38 | struct iolimit_state io; 39 | 40 | fd = open(VZCTLDEV, O_RDWR); 41 | if (fd == -1) { 42 | fprintf(stderr, "Unable to open %s!\n", VZCTLDEV); 43 | goto err; 44 | } 45 | 46 | io.id = CT_ID; 47 | if (ioctl(fd, VZCTL_GET_IOPSLIMIT, &io)) { 48 | perror("ioctl"); 49 | goto err; 50 | } 51 | ret = (io.speed == expected_limit); 52 | 53 | err: 54 | close(fd); 55 | return ret; 56 | } 57 | 58 | int is_iopriolimit_correct(unsigned int expected_limit) 59 | { 60 | int ret = 0; 61 | FILE *f = NULL; 62 | int limit; 63 | char buf[1024]; 64 | 65 | f = fopen("/proc/bc/" CT_NAME "/ioprio", "r"); 66 | if (!f) { 67 | fprintf(stderr, "Unable to open /proc/bc/" CT_NAME "/ioprio!\n"); 68 | goto err; 69 | } 70 | 71 | if (fscanf(f, "%s %d", buf, &limit) != 2) { 72 | fprintf(stderr, "fscanf failed!\n"); 73 | goto err; 74 | } 75 | ret = (limit == expected_limit); 76 | err: 77 | fclose(f); 78 | return ret; 79 | } 80 | 81 | int main(int argc, char *argv[]) 82 | { 83 | libct_session_t s; 84 | ct_handler_t ct; 85 | ct_process_desc_t p; 86 | ct_process_t pr; 87 | char *sleep_a[] = { "sleep", "2", NULL}; 88 | 89 | s = libct_session_open_local(); 90 | ct = libct_container_create(s, CT_NAME); 91 | p = libct_process_desc_create(s); 92 | libct_fs_set_root(ct, FS_ROOT); 93 | 94 | libct_container_set_nsmask(ct, 95 | CLONE_NEWNS | 96 | CLONE_NEWUTS | 97 | CLONE_NEWIPC | 98 | CLONE_NEWNET | 99 | CLONE_NEWPID); 100 | 101 | libct_controller_add(ct, CTL_BLKIO); 102 | libct_controller_configure(ct, CTL_BLKIO, "throttle.write_iops_device", IOPSLIMIT_STR); 103 | libct_controller_configure(ct, CTL_BLKIO, "weight", IOPRIOLIMIT_STR); 104 | pr = libct_container_spawn_execv(ct, p, "/bin/sleep", sleep_a); 105 | if (libct_handle_is_err(pr)) 106 | goto err; 107 | 108 | if (!is_iopslimit_correct(IOPSLIMIT)) 109 | goto err; 110 | if (!is_iopriolimit_correct(IOPRIOLIMIT)) 111 | goto err; 112 | 113 | libct_container_wait(ct); 114 | libct_container_destroy(ct); 115 | 116 | libct_session_close(s); 117 | 118 | return pass("All is ok");; 119 | err: 120 | return fail("Something wrong"); 121 | } 122 | -------------------------------------------------------------------------------- /test/vz_cgroup_cpu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "test.h" 9 | 10 | #define FS_ROOT "/" 11 | 12 | #define STR_HELPER(x) #x 13 | #define STR(x) STR_HELPER(x) 14 | 15 | #define CT_ID 1339 16 | #define CT_NAME STR(CT_ID) 17 | #define CPUS "0-1,3" 18 | 19 | int is_cpu_count_correct(const char *expected_cpus) 20 | { 21 | int ret = 0; 22 | FILE *f = NULL; 23 | char buf[1024] = {'\0'}; 24 | 25 | f = fopen("/proc/vz/fairsched/" CT_NAME "/cpuset.cpus", "r"); 26 | if (!f) { 27 | fprintf(stderr, "Unable to open /proc/vz/fairsched/" CT_NAME "/cpuset.cpus!\n"); 28 | goto err; 29 | } 30 | 31 | if (fscanf(f, "%s", buf) != 1) { 32 | fprintf(stderr, "fscanf failed!\n"); 33 | goto err; 34 | } 35 | ret = !strcmp(buf, expected_cpus); 36 | err: 37 | fclose(f); 38 | return ret; 39 | } 40 | 41 | 42 | int main(int argc, char *argv[]) 43 | { 44 | libct_session_t s; 45 | ct_handler_t ct; 46 | ct_process_desc_t p; 47 | ct_process_t pr; 48 | char *sleep_a[] = { "sleep", "2", NULL}; 49 | 50 | s = libct_session_open_local(); 51 | ct = libct_container_create(s, CT_NAME); 52 | p = libct_process_desc_create(s); 53 | libct_fs_set_root(ct, FS_ROOT); 54 | 55 | libct_container_set_nsmask(ct, 56 | CLONE_NEWNS | 57 | CLONE_NEWUTS | 58 | CLONE_NEWIPC | 59 | CLONE_NEWNET | 60 | CLONE_NEWPID); 61 | 62 | libct_controller_add(ct, CTL_CPUSET); 63 | libct_controller_configure(ct, CTL_CPUSET, "cpuset.cpus", CPUS); 64 | pr = libct_container_spawn_execv(ct, p, "/bin/sleep", sleep_a); 65 | if (libct_handle_is_err(pr)) 66 | goto err; 67 | 68 | if (!is_cpu_count_correct(CPUS)) 69 | goto err; 70 | 71 | libct_container_wait(ct); 72 | libct_container_destroy(ct); 73 | 74 | libct_session_close(s); 75 | 76 | return pass("All is ok");; 77 | err: 78 | return fail("Something wrong"); 79 | } 80 | -------------------------------------------------------------------------------- /test/vz_cgroup_memory.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "test.h" 9 | 10 | #define FS_ROOT "/" 11 | 12 | #define STR_HELPER(x) #x 13 | #define STR(x) STR_HELPER(x) 14 | 15 | #define MEMLIMIT 134217728 16 | #define MEMLIMIT_STR STR(MEMLIMIT) 17 | 18 | #define CT_ID 1339 19 | #define CT_NAME STR(CT_ID) 20 | 21 | int is_memory_correct(unsigned long expected_limit) 22 | { 23 | int ret = 0; 24 | FILE *f = NULL; 25 | char buf[1024] = {'\0'}; 26 | unsigned long flds[5] = {0}; 27 | unsigned long limit = 0; 28 | 29 | f = fopen("/proc/bc/" CT_NAME "/resources", "r"); 30 | if (!f) { 31 | fprintf(stderr, "Unable to open /proc/bc/" CT_NAME "/resources!\n"); 32 | goto err; 33 | } 34 | 35 | while (fscanf(f, "%s %lu %lu %lu %lu %lu", buf, &flds[0], &flds[1], &flds[2], &flds[3], &flds[4]) == 6) { 36 | if (strcmp(buf, "physpages") == 0 && flds[2] == flds[3]) { 37 | limit = flds[2] * getpagesize(); 38 | break; 39 | } 40 | } 41 | if (!limit) { 42 | fprintf(stderr, "unable to read \n"); 43 | goto err; 44 | } 45 | ret = (limit == expected_limit); 46 | err: 47 | fclose(f); 48 | return ret; 49 | } 50 | 51 | 52 | int main(int argc, char *argv[]) 53 | { 54 | libct_session_t s; 55 | ct_handler_t ct; 56 | ct_process_desc_t p; 57 | ct_process_t pr; 58 | char *run_a[3] = { "sleep", "2", NULL}; 59 | 60 | test_init(argc, argv); 61 | 62 | s = libct_session_open_local(); 63 | ct = libct_container_create(s, CT_NAME); 64 | p = libct_process_desc_create(s); 65 | libct_fs_set_root(ct, FS_ROOT); 66 | 67 | libct_container_set_nsmask(ct, 68 | CLONE_NEWNS | 69 | CLONE_NEWUTS | 70 | CLONE_NEWIPC | 71 | CLONE_NEWNET | 72 | CLONE_NEWPID); 73 | 74 | libct_controller_add(ct, CTL_MEMORY); 75 | libct_controller_configure(ct, CTL_MEMORY, "limit_in_bytes", MEMLIMIT_STR); 76 | 77 | pr = libct_container_spawn_execv(ct, p, "/bin/sleep", run_a); 78 | if (libct_handle_is_err(pr)) 79 | goto err; 80 | 81 | if (!is_memory_correct(MEMLIMIT)) 82 | goto err; 83 | 84 | libct_container_wait(ct); 85 | libct_container_destroy(ct); 86 | 87 | libct_session_close(s); 88 | 89 | return pass("All is ok");; 90 | err: 91 | return fail("Something wrong"); 92 | } 93 | -------------------------------------------------------------------------------- /test/vz_create_exec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "test.h" 8 | 9 | #define FS_ROOT "root" 10 | int main(int argc, char *argv[]) 11 | { 12 | libct_session_t s; 13 | ct_handler_t ct; 14 | ct_process_desc_t pd; 15 | ct_process_t p; 16 | char *ls_a[2] = { "ls", NULL}; 17 | 18 | s = libct_session_open_local(); 19 | if (libct_handle_is_err(s)) 20 | return fail("Unable to create a new session"); 21 | 22 | ct = libct_container_create(s, "1337"); 23 | pd = libct_process_desc_create(s); 24 | if (libct_handle_is_err(ct) || 25 | libct_handle_is_err(pd)) 26 | return fail("Unable to create a handle for process or container"); 27 | 28 | if (libct_fs_set_root(ct, FS_ROOT)) 29 | return fail("Unable to set FS_ROOT"); 30 | 31 | if (libct_container_set_nsmask(ct, 32 | CLONE_NEWNS | 33 | CLONE_NEWUTS | 34 | CLONE_NEWIPC | 35 | CLONE_NEWNET | 36 | CLONE_NEWPID)) 37 | return fail("Unable to set nsmask"); 38 | 39 | p = libct_container_spawn_execv(ct, pd, "/bin/ls", ls_a); 40 | if (libct_handle_is_err(p)) 41 | goto err; 42 | 43 | if (libct_container_wait(ct)) 44 | return fail("Unable to wait a container"); 45 | libct_container_destroy(ct); 46 | 47 | libct_session_close(s); 48 | 49 | return pass("All is ok");; 50 | err: 51 | return fail("Something wrong"); 52 | } 53 | -------------------------------------------------------------------------------- /test/vz_enter.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "test.h" 11 | 12 | #define FS_ROOT "root" 13 | int main(int argc, char *argv[]) 14 | { 15 | libct_session_t s; 16 | ct_handler_t ct; 17 | ct_process_desc_t pd; 18 | ct_process_t pr, p; 19 | char *sleep_a[] = { "cat", NULL}; 20 | char *ls_a[] = { "sh", "-c", "cat; echo ok", NULL}; 21 | int fds[] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}; 22 | int pfd[2], tfd[2], ifd[2], status; 23 | char buf[10]; 24 | 25 | test_init(); 26 | 27 | s = libct_session_open_local(); 28 | ct = libct_container_create(s, "1339"); 29 | pd = libct_process_desc_create(s); 30 | libct_fs_set_root(ct, FS_ROOT); 31 | 32 | libct_container_set_nsmask(ct, 33 | CLONE_NEWNS | 34 | CLONE_NEWUTS | 35 | CLONE_NEWIPC | 36 | CLONE_NEWNET | 37 | CLONE_NEWPID); 38 | 39 | if (pipe(pfd)) 40 | goto err; 41 | 42 | fds[0] = pfd[0]; 43 | fcntl(pfd[1], F_SETFD, FD_CLOEXEC); 44 | libct_process_desc_set_fds(pd, fds, 3); 45 | p = libct_container_spawn_execv(ct, pd, "/bin/cat", sleep_a); 46 | if (libct_handle_is_err(p)) 47 | goto err; 48 | close(pfd[0]); 49 | 50 | if (pipe(tfd)) 51 | goto err; 52 | if (pipe(ifd)) 53 | goto err; 54 | 55 | fds[0] = ifd[0]; 56 | fds[1] = tfd[1]; 57 | fcntl(tfd[0], F_SETFD, FD_CLOEXEC); 58 | libct_process_desc_set_fds(pd, fds, 3); 59 | pr = libct_container_enter_execv(ct, pd, "/bin/sh", ls_a); 60 | if (libct_handle_is_err(pr)) 61 | goto err; 62 | close(tfd[1]); 63 | close(ifd[0]); 64 | close(ifd[1]); 65 | 66 | if (read(tfd[0], buf, sizeof(buf)) != 3) 67 | goto err; 68 | 69 | if (libct_process_wait(pr, &status)) 70 | goto err; 71 | 72 | close(pfd[1]); 73 | 74 | libct_container_wait(ct); 75 | libct_container_destroy(ct); 76 | 77 | libct_session_close(s); 78 | 79 | return pass("All is ok");; 80 | err: 81 | return fail("Something wrong"); 82 | } 83 | -------------------------------------------------------------------------------- /test/vz_net_veth.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "test.h" 8 | 9 | #define VETH_HOST_NAME "hveth0" 10 | #define VETH_CT_NAME "cveth0" 11 | 12 | #define FS_ROOT "root" 13 | int main(int argc, char *argv[]) 14 | { 15 | libct_session_t s; 16 | ct_handler_t ct; 17 | ct_net_t nd; 18 | ct_process_desc_t pd; 19 | ct_process_t p; 20 | struct ct_net_veth_arg va = { 21 | .host_name = VETH_HOST_NAME, 22 | .ct_name = VETH_CT_NAME 23 | }; 24 | char *ip_a[] = { "ip", "link", "show", NULL}; 25 | 26 | s = libct_session_open_local(); 27 | 28 | ct = libct_container_create(s, "1337"); 29 | pd = libct_process_desc_create(s); 30 | libct_fs_set_root(ct, FS_ROOT); 31 | libct_container_set_nsmask(ct, 32 | CLONE_NEWNS | 33 | CLONE_NEWUTS | 34 | CLONE_NEWIPC | 35 | CLONE_NEWNET | 36 | CLONE_NEWPID); 37 | 38 | nd = libct_net_add(ct, CT_NET_VETH, &va); 39 | if (libct_handle_is_err(nd)) 40 | return tst_err("Can't add hostnic"); 41 | 42 | if (libct_net_dev_set_mac_addr(nd, "00:11:22:33:44:55")) 43 | return tst_err("Can't set mac"); 44 | 45 | p = libct_container_spawn_execv(ct, pd, "/sbin/ip", ip_a); 46 | if (libct_handle_is_err(p)) 47 | goto err; 48 | 49 | libct_container_wait(ct); 50 | libct_container_destroy(ct); 51 | 52 | libct_session_close(s); 53 | 54 | return pass("All is ok");; 55 | err: 56 | return fail("Something wrong"); 57 | } 58 | --------------------------------------------------------------------------------