Skip to content

Commit 9e2ce27

Browse files
committed
checkpoint: add support for containers with terminals
CRIU was extended to report about orphaned master pty-s via RPC. Signed-off-by: Andrei Vagin <[email protected]>
1 parent abbcddb commit 9e2ce27

File tree

2 files changed

+61
-32
lines changed

2 files changed

+61
-32
lines changed

libcontainer/container_linux.go

Lines changed: 56 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"io"
1010
"io/ioutil"
11+
"net"
1112
"os"
1213
"os/exec"
1314
"path/filepath"
@@ -676,20 +677,21 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
676677
defer imageDir.Close()
677678

678679
rpcOpts := criurpc.CriuOpts{
679-
ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
680-
WorkDirFd: proto.Int32(int32(workDir.Fd())),
681-
LogLevel: proto.Int32(4),
682-
LogFile: proto.String("dump.log"),
683-
Root: proto.String(c.config.Rootfs),
684-
ManageCgroups: proto.Bool(true),
685-
NotifyScripts: proto.Bool(true),
686-
Pid: proto.Int32(int32(c.initProcess.pid())),
687-
ShellJob: proto.Bool(criuOpts.ShellJob),
688-
LeaveRunning: proto.Bool(criuOpts.LeaveRunning),
689-
TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
690-
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
691-
FileLocks: proto.Bool(criuOpts.FileLocks),
692-
EmptyNs: proto.Uint32(criuOpts.EmptyNs),
680+
ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
681+
WorkDirFd: proto.Int32(int32(workDir.Fd())),
682+
LogLevel: proto.Int32(4),
683+
LogFile: proto.String("dump.log"),
684+
Root: proto.String(c.config.Rootfs),
685+
ManageCgroups: proto.Bool(true),
686+
NotifyScripts: proto.Bool(true),
687+
Pid: proto.Int32(int32(c.initProcess.pid())),
688+
ShellJob: proto.Bool(criuOpts.ShellJob),
689+
LeaveRunning: proto.Bool(criuOpts.LeaveRunning),
690+
TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
691+
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
692+
FileLocks: proto.Bool(criuOpts.FileLocks),
693+
EmptyNs: proto.Uint32(criuOpts.EmptyNs),
694+
OrphanPtsMaster: proto.Bool(true),
693695
}
694696

695697
// append optional criu opts, e.g., page-server and port
@@ -864,20 +866,21 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
864866
req := &criurpc.CriuReq{
865867
Type: &t,
866868
Opts: &criurpc.CriuOpts{
867-
ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
868-
WorkDirFd: proto.Int32(int32(workDir.Fd())),
869-
EvasiveDevices: proto.Bool(true),
870-
LogLevel: proto.Int32(4),
871-
LogFile: proto.String("restore.log"),
872-
RstSibling: proto.Bool(true),
873-
Root: proto.String(root),
874-
ManageCgroups: proto.Bool(true),
875-
NotifyScripts: proto.Bool(true),
876-
ShellJob: proto.Bool(criuOpts.ShellJob),
877-
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
878-
TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
879-
FileLocks: proto.Bool(criuOpts.FileLocks),
880-
EmptyNs: proto.Uint32(criuOpts.EmptyNs),
869+
ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
870+
WorkDirFd: proto.Int32(int32(workDir.Fd())),
871+
EvasiveDevices: proto.Bool(true),
872+
LogLevel: proto.Int32(4),
873+
LogFile: proto.String("restore.log"),
874+
RstSibling: proto.Bool(true),
875+
Root: proto.String(root),
876+
ManageCgroups: proto.Bool(true),
877+
NotifyScripts: proto.Bool(true),
878+
ShellJob: proto.Bool(criuOpts.ShellJob),
879+
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
880+
TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
881+
FileLocks: proto.Bool(criuOpts.FileLocks),
882+
EmptyNs: proto.Uint32(criuOpts.EmptyNs),
883+
OrphanPtsMaster: proto.Bool(true),
881884
},
882885
}
883886

@@ -974,6 +977,11 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
974977

975978
logPath := filepath.Join(opts.WorkDirectory, req.GetOpts().GetLogFile())
976979
criuClient := os.NewFile(uintptr(fds[0]), "criu-transport-client")
980+
criuClientCon, err := net.FileConn(criuClient)
981+
if err != nil {
982+
return err
983+
}
984+
977985
criuServer := os.NewFile(uintptr(fds[1]), "criu-transport-server")
978986
defer criuClient.Close()
979987
defer criuServer.Close()
@@ -1033,14 +1041,15 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
10331041
if err != nil {
10341042
return err
10351043
}
1036-
_, err = criuClient.Write(data)
1044+
_, err = criuClientCon.Write(data)
10371045
if err != nil {
10381046
return err
10391047
}
10401048

10411049
buf := make([]byte, 10*4096)
1050+
oob := make([]byte, 4096)
10421051
for true {
1043-
n, err := criuClient.Read(buf)
1052+
n, oobn, _, _, err := criuClientCon.(*net.UnixConn).ReadMsgUnix(buf, oob)
10441053
if err != nil {
10451054
return err
10461055
}
@@ -1064,7 +1073,7 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
10641073
t := resp.GetType()
10651074
switch {
10661075
case t == criurpc.CriuReqType_NOTIFY:
1067-
if err := c.criuNotifications(resp, process, opts, extFds); err != nil {
1076+
if err := c.criuNotifications(resp, process, opts, extFds, oob[:oobn]); err != nil {
10681077
return err
10691078
}
10701079
t = criurpc.CriuReqType_NOTIFY
@@ -1148,11 +1157,12 @@ func unlockNetwork(config *configs.Config) error {
11481157
return nil
11491158
}
11501159

1151-
func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Process, opts *CriuOpts, fds []string) error {
1160+
func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Process, opts *CriuOpts, fds []string, oob []byte) error {
11521161
notify := resp.GetNotify()
11531162
if notify == nil {
11541163
return fmt.Errorf("invalid response: %s", resp.String())
11551164
}
1165+
logrus.Debugf("notify: %s\n", notify.GetScript())
11561166
switch {
11571167
case notify.GetScript() == "post-dump":
11581168
f, err := os.Create(filepath.Join(c.root, "checkpoint"))
@@ -1205,6 +1215,20 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc
12051215
logrus.Error(err)
12061216
}
12071217
}
1218+
case notify.GetScript() == "orphan-pts-master":
1219+
scm, err := syscall.ParseSocketControlMessage(oob)
1220+
if err != nil {
1221+
return err
1222+
}
1223+
fds, err := syscall.ParseUnixRights(&scm[0])
1224+
1225+
master := os.NewFile(uintptr(fds[0]), "orphan-pts-master")
1226+
defer master.Close()
1227+
1228+
// While we can access console.master, using the API is a good idea.
1229+
if err := utils.SendFd(process.ConsoleSocket, master); err != nil {
1230+
return err
1231+
}
12081232
}
12091233
return nil
12101234
}

restore.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ restored.`,
2020
Description: `Restores the saved state of the container instance that was previously saved
2121
using the runc checkpoint command.`,
2222
Flags: []cli.Flag{
23+
cli.StringFlag{
24+
Name: "console-socket",
25+
Value: "",
26+
Usage: "path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal",
27+
},
2328
cli.StringFlag{
2429
Name: "image-path",
2530
Value: "",

0 commit comments

Comments
 (0)