func createPidFile(path string, process *libcontainer.Process) error { pid, err := process.Pid() if err != nil { return err } f, err := os.Create(path) if err != nil { return err } defer f.Close() _, err = fmt.Fprintf(f, "%d", pid) return err }
func createPidFile(path string, process *libcontainer.Process) error { pid, err := process.Pid() if err != nil { return err } f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) if err != nil { return err } defer f.Close() _, err = fmt.Fprintf(f, "%d", pid) return err }
// createPidFile creates a file with the processes pid inside it atomically // it creates a temp file with the paths filename + '.' infront of it // then renames the file func createPidFile(path string, process *libcontainer.Process) error { pid, err := process.Pid() if err != nil { return err } var ( tmpDir = filepath.Dir(path) tmpName = filepath.Join(tmpDir, fmt.Sprintf(".%s", filepath.Base(path))) ) f, err := os.OpenFile(tmpName, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666) if err != nil { return err } _, err = fmt.Fprintf(f, "%d", pid) f.Close() if err != nil { return err } return os.Rename(tmpName, path) }
func waitInPIDHost(p *libcontainer.Process, c libcontainer.Container) func() (*os.ProcessState, error) { return func() (*os.ProcessState, error) { pid, err := p.Pid() if err != nil { return nil, err } process, err := os.FindProcess(pid) s, err := process.Wait() if err != nil { execErr, ok := err.(*exec.ExitError) if !ok { return s, err } s = execErr.ProcessState } killCgroupProcs(c) p.Wait() return s, err } }
// forward handles the main signal event loop forwarding, resizing, or reaping depending // on the signal received. func (h *signalHandler) forward(process *libcontainer.Process) (int, error) { // make sure we know the pid of our main process so that we can return // after it dies. pid1, err := process.Pid() if err != nil { return -1, err } // perform the initial tty resize. h.tty.resize() for s := range h.signals { switch s { case syscall.SIGWINCH: h.tty.resize() case syscall.SIGCHLD: exits, err := h.reap() if err != nil { logrus.Error(err) } for _, e := range exits { logrus.WithFields(logrus.Fields{ "pid": e.pid, "status": e.status, }).Debug("process exited") if e.pid == pid1 { // call Wait() on the process even though we already have the exit // status because we must ensure that any of the go specific process // fun such as flushing pipes are complete before we return. process.Wait() return e.status, nil } } default: logrus.Debugf("sending signal to process %s", s) if err := syscall.Kill(pid1, s.(syscall.Signal)); err != nil { logrus.Error(err) } } } return -1, nil }
func TestEnter(t *testing.T) { if testing.Short() { return } root, err := newTestRoot() ok(t, err) defer os.RemoveAll(root) rootfs, err := newRootfs() ok(t, err) defer remove(rootfs) config := newTemplateConfig(rootfs) container, err := factory.Create("test", config) ok(t, err) defer container.Destroy() // Execute a first process in the container stdinR, stdinW, err := os.Pipe() ok(t, err) var stdout, stdout2 bytes.Buffer pconfig := libcontainer.Process{ Args: []string{"sh", "-c", "cat && readlink /proc/self/ns/pid"}, Env: standardEnvironment, Stdin: stdinR, Stdout: &stdout, } err = container.Start(&pconfig) stdinR.Close() defer stdinW.Close() ok(t, err) pid, err := pconfig.Pid() ok(t, err) // Execute another process in the container stdinR2, stdinW2, err := os.Pipe() ok(t, err) pconfig2 := libcontainer.Process{ Env: standardEnvironment, } pconfig2.Args = []string{"sh", "-c", "cat && readlink /proc/self/ns/pid"} pconfig2.Stdin = stdinR2 pconfig2.Stdout = &stdout2 err = container.Start(&pconfig2) stdinR2.Close() defer stdinW2.Close() ok(t, err) pid2, err := pconfig2.Pid() ok(t, err) processes, err := container.Processes() ok(t, err) n := 0 for i := range processes { if processes[i] == pid || processes[i] == pid2 { n++ } } if n != 2 { t.Fatal("unexpected number of processes", processes, pid, pid2) } // Wait processes stdinW2.Close() waitProcess(&pconfig2, t) stdinW.Close() waitProcess(&pconfig, t) // Check that both processes live in the same pidns pidns := string(stdout.Bytes()) ok(t, err) pidns2 := string(stdout2.Bytes()) ok(t, err) if pidns != pidns2 { t.Fatal("The second process isn't in the required pid namespace", pidns, pidns2) } }
func TestCheckpoint(t *testing.T) { if testing.Short() { return } root, err := newTestRoot() if err != nil { t.Fatal(err) } defer os.RemoveAll(root) rootfs, err := newRootfs() if err != nil { t.Fatal(err) } defer remove(rootfs) config := newTemplateConfig(rootfs) config.Mounts = append(config.Mounts, &configs.Mount{ Destination: "/sys/fs/cgroup", Device: "cgroup", Flags: defaultMountFlags | syscall.MS_RDONLY, }) factory, err := libcontainer.New(root, libcontainer.Cgroupfs) if err != nil { t.Fatal(err) } container, err := factory.Create("test", config) if err != nil { t.Fatal(err) } defer container.Destroy() stdinR, stdinW, err := os.Pipe() if err != nil { t.Fatal(err) } var stdout bytes.Buffer pconfig := libcontainer.Process{ Cwd: "/", Args: []string{"cat"}, Env: standardEnvironment, Stdin: stdinR, Stdout: &stdout, } err = container.Start(&pconfig) stdinR.Close() defer stdinW.Close() if err != nil { t.Fatal(err) } pid, err := pconfig.Pid() if err != nil { t.Fatal(err) } process, err := os.FindProcess(pid) if err != nil { t.Fatal(err) } imagesDir, err := ioutil.TempDir("", "criu") if err != nil { t.Fatal(err) } defer os.RemoveAll(imagesDir) checkpointOpts := &libcontainer.CriuOpts{ ImagesDirectory: imagesDir, WorkDirectory: imagesDir, } dumpLog := filepath.Join(checkpointOpts.WorkDirectory, "dump.log") restoreLog := filepath.Join(checkpointOpts.WorkDirectory, "restore.log") if err := container.Checkpoint(checkpointOpts); err != nil { showFile(t, dumpLog) t.Fatal(err) } state, err := container.Status() if err != nil { t.Fatal(err) } if state != libcontainer.Running { t.Fatal("Unexpected state checkpoint: ", state) } stdinW.Close() _, err = process.Wait() if err != nil { t.Fatal(err) } // reload the container container, err = factory.Load("test") if err != nil { t.Fatal(err) } restoreStdinR, restoreStdinW, err := os.Pipe() if err != nil { t.Fatal(err) } restoreProcessConfig := &libcontainer.Process{ Cwd: "/", Stdin: restoreStdinR, Stdout: &stdout, } err = container.Restore(restoreProcessConfig, checkpointOpts) restoreStdinR.Close() defer restoreStdinW.Close() if err != nil { showFile(t, restoreLog) t.Fatal(err) } state, err = container.Status() if err != nil { t.Fatal(err) } if state != libcontainer.Running { t.Fatal("Unexpected restore state: ", state) } pid, err = restoreProcessConfig.Pid() if err != nil { t.Fatal(err) } process, err = os.FindProcess(pid) if err != nil { t.Fatal(err) } _, err = restoreStdinW.WriteString("Hello!") if err != nil { t.Fatal(err) } restoreStdinW.Close() s, err := process.Wait() if err != nil { t.Fatal(err) } if !s.Success() { t.Fatal(s.String(), pid) } output := string(stdout.Bytes()) if !strings.Contains(output, "Hello!") { t.Fatal("Did not restore the pipe correctly:", output) } }
func TestCheckpoint(t *testing.T) { if testing.Short() { return } root, err := newTestRoot() if err != nil { t.Fatal(err) } defer os.RemoveAll(root) rootfs, err := newRootfs() if err != nil { t.Fatal(err) } defer remove(rootfs) config := newTemplateConfig(rootfs) factory, err := libcontainer.New(root, libcontainer.Cgroupfs) if err != nil { t.Fatal(err) } container, err := factory.Create("test", config) if err != nil { t.Fatal(err) } defer container.Destroy() stdinR, stdinW, err := os.Pipe() if err != nil { t.Fatal(err) } var stdout bytes.Buffer pconfig := libcontainer.Process{ Args: []string{"cat"}, Env: standardEnvironment, Stdin: stdinR, Stdout: &stdout, } err = container.Start(&pconfig) stdinR.Close() defer stdinW.Close() if err != nil { t.Fatal(err) } pid, err := pconfig.Pid() if err != nil { t.Fatal(err) } process, err := os.FindProcess(pid) if err != nil { t.Fatal(err) } imagesDir, err := ioutil.TempDir("", "criu") if err != nil { t.Fatal(err) } defer os.RemoveAll(imagesDir) checkpointOpts := &libcontainer.CriuOpts{ ImagesDirectory: imagesDir, WorkDirectory: imagesDir, } if err := container.Checkpoint(checkpointOpts); err != nil { t.Fatal(err) } state, err := container.Status() if err != nil { t.Fatal(err) } if state != libcontainer.Checkpointed { t.Fatal("Unexpected state: ", state) } stdinW.Close() _, err = process.Wait() if err != nil { t.Fatal(err) } // reload the container container, err = factory.Load("test") if err != nil { t.Fatal(err) } restoreStdinR, restoreStdinW, err := os.Pipe() if err != nil { t.Fatal(err) } restoreProcessConfig := &libcontainer.Process{ Stdin: restoreStdinR, Stdout: &stdout, } err = container.Restore(restoreProcessConfig, &libcontainer.CriuOpts{ ImagesDirectory: imagesDir, }) restoreStdinR.Close() defer restoreStdinW.Close() state, err = container.Status() if err != nil { t.Fatal(err) } if state != libcontainer.Running { t.Fatal("Unexpected state: ", state) } pid, err = restoreProcessConfig.Pid() if err != nil { t.Fatal(err) } process, err = os.FindProcess(pid) if err != nil { t.Fatal(err) } _, err = restoreStdinW.WriteString("Hello!") if err != nil { t.Fatal(err) } restoreStdinW.Close() s, err := process.Wait() if err != nil { t.Fatal(err) } if !s.Success() { t.Fatal(s.String(), pid) } output := string(stdout.Bytes()) if !strings.Contains(output, "Hello!") { t.Fatal("Did not restore the pipe correctly:", output) } }