Skip to content

Commit 996903a

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 1a577d4 commit 996903a

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"
@@ -657,20 +658,21 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
657658
defer imageDir.Close()
658659

659660
rpcOpts := criurpc.CriuOpts{
660-
ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
661-
WorkDirFd: proto.Int32(int32(workDir.Fd())),
662-
LogLevel: proto.Int32(4),
663-
LogFile: proto.String("dump.log"),
664-
Root: proto.String(c.config.Rootfs),
665-
ManageCgroups: proto.Bool(true),
666-
NotifyScripts: proto.Bool(true),
667-
Pid: proto.Int32(int32(c.initProcess.pid())),
668-
ShellJob: proto.Bool(criuOpts.ShellJob),
669-
LeaveRunning: proto.Bool(criuOpts.LeaveRunning),
670-
TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
671-
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
672-
FileLocks: proto.Bool(criuOpts.FileLocks),
673-
EmptyNs: proto.Uint32(criuOpts.EmptyNs),
661+
ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
662+
WorkDirFd: proto.Int32(int32(workDir.Fd())),
663+
LogLevel: proto.Int32(4),
664+
LogFile: proto.String("dump.log"),
665+
Root: proto.String(c.config.Rootfs),
666+
ManageCgroups: proto.Bool(true),
667+
NotifyScripts: proto.Bool(true),
668+
Pid: proto.Int32(int32(c.initProcess.pid())),
669+
ShellJob: proto.Bool(criuOpts.ShellJob),
670+
LeaveRunning: proto.Bool(criuOpts.LeaveRunning),
671+
TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
672+
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
673+
FileLocks: proto.Bool(criuOpts.FileLocks),
674+
EmptyNs: proto.Uint32(criuOpts.EmptyNs),
675+
OrphanPtsMaster: proto.Bool(true),
674676
}
675677

676678
// append optional criu opts, e.g., page-server and port
@@ -838,20 +840,21 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
838840
req := &criurpc.CriuReq{
839841
Type: &t,
840842
Opts: &criurpc.CriuOpts{
841-
ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
842-
WorkDirFd: proto.Int32(int32(workDir.Fd())),
843-
EvasiveDevices: proto.Bool(true),
844-
LogLevel: proto.Int32(4),
845-
LogFile: proto.String("restore.log"),
846-
RstSibling: proto.Bool(true),
847-
Root: proto.String(root),
848-
ManageCgroups: proto.Bool(true),
849-
NotifyScripts: proto.Bool(true),
850-
ShellJob: proto.Bool(criuOpts.ShellJob),
851-
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
852-
TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
853-
FileLocks: proto.Bool(criuOpts.FileLocks),
854-
EmptyNs: proto.Uint32(criuOpts.EmptyNs),
843+
ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
844+
WorkDirFd: proto.Int32(int32(workDir.Fd())),
845+
EvasiveDevices: proto.Bool(true),
846+
LogLevel: proto.Int32(4),
847+
LogFile: proto.String("restore.log"),
848+
RstSibling: proto.Bool(true),
849+
Root: proto.String(root),
850+
ManageCgroups: proto.Bool(true),
851+
NotifyScripts: proto.Bool(true),
852+
ShellJob: proto.Bool(criuOpts.ShellJob),
853+
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
854+
TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
855+
FileLocks: proto.Bool(criuOpts.FileLocks),
856+
EmptyNs: proto.Uint32(criuOpts.EmptyNs),
857+
OrphanPtsMaster: proto.Bool(true),
855858
},
856859
}
857860

@@ -947,6 +950,11 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
947950

948951
logPath := filepath.Join(opts.WorkDirectory, req.GetOpts().GetLogFile())
949952
criuClient := os.NewFile(uintptr(fds[0]), "criu-transport-client")
953+
criuClientCon, err := net.FileConn(criuClient)
954+
if err != nil {
955+
return err
956+
}
957+
950958
criuServer := os.NewFile(uintptr(fds[1]), "criu-transport-server")
951959
defer criuClient.Close()
952960
defer criuServer.Close()
@@ -1006,14 +1014,15 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
10061014
if err != nil {
10071015
return err
10081016
}
1009-
_, err = criuClient.Write(data)
1017+
_, err = criuClientCon.Write(data)
10101018
if err != nil {
10111019
return err
10121020
}
10131021

10141022
buf := make([]byte, 10*4096)
1023+
oob := make([]byte, 4096)
10151024
for true {
1016-
n, err := criuClient.Read(buf)
1025+
n, oobn, _, _, err := criuClientCon.(*net.UnixConn).ReadMsgUnix(buf, oob)
10171026
if err != nil {
10181027
return err
10191028
}
@@ -1037,7 +1046,7 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
10371046
t := resp.GetType()
10381047
switch {
10391048
case t == criurpc.CriuReqType_NOTIFY:
1040-
if err := c.criuNotifications(resp, process, opts, extFds); err != nil {
1049+
if err := c.criuNotifications(resp, process, opts, extFds, oob[:oobn]); err != nil {
10411050
return err
10421051
}
10431052
t = criurpc.CriuReqType_NOTIFY
@@ -1121,11 +1130,12 @@ func unlockNetwork(config *configs.Config) error {
11211130
return nil
11221131
}
11231132

1124-
func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Process, opts *CriuOpts, fds []string) error {
1133+
func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Process, opts *CriuOpts, fds []string, oob []byte) error {
11251134
notify := resp.GetNotify()
11261135
if notify == nil {
11271136
return fmt.Errorf("invalid response: %s", resp.String())
11281137
}
1138+
logrus.Debugf("notify: %s\n", notify.GetScript())
11291139
switch {
11301140
case notify.GetScript() == "post-dump":
11311141
f, err := os.Create(filepath.Join(c.root, "checkpoint"))
@@ -1178,6 +1188,20 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc
11781188
logrus.Error(err)
11791189
}
11801190
}
1191+
case notify.GetScript() == "orphan-pts-master":
1192+
scm, err := syscall.ParseSocketControlMessage(oob)
1193+
if err != nil {
1194+
return err
1195+
}
1196+
fds, err := syscall.ParseUnixRights(&scm[0])
1197+
1198+
master := os.NewFile(uintptr(fds[0]), "orphan-pts-master")
1199+
defer master.Close()
1200+
1201+
// While we can access console.master, using the API is a good idea.
1202+
if err := utils.SendFd(process.ConsoleSocket, master); err != nil {
1203+
return err
1204+
}
11811205
}
11821206
return nil
11831207
}

restore.go

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

0 commit comments

Comments
 (0)