diff --git a/README.md b/README.md index 2d6a987..4cb57e6 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Packages: - forkexec: fork-exec provides mount, unshare, ptrace, seccomp, capset before exec - memfd: read regular file and creates a seaed memfd for its contents - unixsocket: send / recv oob msg from a unix socket -- cgroup: creates cgroup directories and collects resource usage / limits +- cgroup: creates cgroup directories and collects resource usage / limits - tracer: ptrace tracer and provides syscall trap filter context - runprogram: wrapper to call forkexec and trecer - rununshared: wrapper to call forkexec and unshared namespaces @@ -71,3 +71,4 @@ It seems unshare net or ipc takes time, maybe limits action by seccomp instead. TODO: 1. Add ability to pre-fork container deamons +2. Add fork-exec with execve memfd (execveat), sethostname, setdomainname, prepare devices (mknod) diff --git a/deamon/deamon.go b/deamon/deamon.go new file mode 100644 index 0000000..64e65a0 --- /dev/null +++ b/deamon/deamon.go @@ -0,0 +1,52 @@ +// Package deamon provides pre-forked container to reduce the container +// create / destroy costs (about 160ms). It creates deamon within unshared +// container and communicate with original process using unix socket with +// oob for fd / pid and structs are encoded in gob format. +package deamon + +/* +Protocol between client and deamon (not thread safe): +- copyin (copy file into container): + send: path, perm, + reply: "finished" (after copy finished) / "error" +- open (open file inside container): + send: path, flags, perm + reply: "success", / "error" +- delete (unlik file / rmdir dir inside container): + send: path + reply: "finished" / "error" +- reset (clean up container for later use (clear workdir / tmp)): + send: + reply: "success" +- execve: (execute file inside container): + send: argv, envv, fd map, rlimits, + reply: + - success: "success", + - failed: "failed" + send (success): "init_finished" (as cmd) + - reply: "finished" / send: "kill" (as cmd) + - send: "kill" (as cmd) / reply: "finished" + +Any socket related error will cause the deamon exit (with all process inside container) +*/ + +import ( + "github.com/criyle/go-judger/types/rlimit" +) + +// Cmd is the control message send into deamon +type Cmd struct { + Cmd string // type of the cmd + Path string // path + Perm int // write perm / open perm + Flags int // open flags + Argv []string // execve argv + Envv []string // execve envv + FdMap []int // execvc fd map + RLmits []rlimit.RLimit // execve posix rlimit +} + +// Reply is the reply message send back to controller +type Reply struct { + Error string // empty if no error +} diff --git a/unixsocket/socket.go b/unixsocket/socket.go index ac8796a..8138dde 100644 --- a/unixsocket/socket.go +++ b/unixsocket/socket.go @@ -30,7 +30,7 @@ type Msg struct { } // NewSocket creates Socket conn struct using existing unix socket fd -// creates by socketpair or net.DialUnix +// creates by socketpair or net.DialUnix and mark it as close_on_exec (avoid fd leak) // it need SOCK_SEQPACKET socket for reliable transfer // it will need SO_PASSCRED to pass unix credential, Notice: in the documentation, // if cred is not specified, self information will be sent @@ -40,6 +40,7 @@ func NewSocket(fd int) (*Socket, error) { return nil, fmt.Errorf("NewSocket: fd(%d) is not a valid fd", fd) } defer file.Close() + syscall.CloseOnExec(int(file.Fd())) conn, err := net.FileConn(file) if err != nil { return nil, err