Skip to content

Commit 2303cc9

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 9763082 commit 2303cc9

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"
@@ -727,20 +728,21 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
727728
defer imageDir.Close()
728729

729730
rpcOpts := criurpc.CriuOpts{
730-
ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
731-
WorkDirFd: proto.Int32(int32(workDir.Fd())),
732-
LogLevel: proto.Int32(4),
733-
LogFile: proto.String("dump.log"),
734-
Root: proto.String(c.config.Rootfs),
735-
ManageCgroups: proto.Bool(true),
736-
NotifyScripts: proto.Bool(true),
737-
Pid: proto.Int32(int32(c.initProcess.pid())),
738-
ShellJob: proto.Bool(criuOpts.ShellJob),
739-
LeaveRunning: proto.Bool(criuOpts.LeaveRunning),
740-
TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
741-
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
742-
FileLocks: proto.Bool(criuOpts.FileLocks),
743-
EmptyNs: proto.Uint32(criuOpts.EmptyNs),
731+
ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
732+
WorkDirFd: proto.Int32(int32(workDir.Fd())),
733+
LogLevel: proto.Int32(4),
734+
LogFile: proto.String("dump.log"),
735+
Root: proto.String(c.config.Rootfs),
736+
ManageCgroups: proto.Bool(true),
737+
NotifyScripts: proto.Bool(true),
738+
Pid: proto.Int32(int32(c.initProcess.pid())),
739+
ShellJob: proto.Bool(criuOpts.ShellJob),
740+
LeaveRunning: proto.Bool(criuOpts.LeaveRunning),
741+
TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
742+
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
743+
FileLocks: proto.Bool(criuOpts.FileLocks),
744+
EmptyNs: proto.Uint32(criuOpts.EmptyNs),
745+
OrphanPtsMaster: proto.Bool(true),
744746
}
745747

746748
// append optional criu opts, e.g., page-server and port
@@ -923,20 +925,21 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
923925
req := &criurpc.CriuReq{
924926
Type: &t,
925927
Opts: &criurpc.CriuOpts{
926-
ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
927-
WorkDirFd: proto.Int32(int32(workDir.Fd())),
928-
EvasiveDevices: proto.Bool(true),
929-
LogLevel: proto.Int32(4),
930-
LogFile: proto.String("restore.log"),
931-
RstSibling: proto.Bool(true),
932-
Root: proto.String(root),
933-
ManageCgroups: proto.Bool(true),
934-
NotifyScripts: proto.Bool(true),
935-
ShellJob: proto.Bool(criuOpts.ShellJob),
936-
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
937-
TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
938-
FileLocks: proto.Bool(criuOpts.FileLocks),
939-
EmptyNs: proto.Uint32(criuOpts.EmptyNs),
928+
ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
929+
WorkDirFd: proto.Int32(int32(workDir.Fd())),
930+
EvasiveDevices: proto.Bool(true),
931+
LogLevel: proto.Int32(4),
932+
LogFile: proto.String("restore.log"),
933+
RstSibling: proto.Bool(true),
934+
Root: proto.String(root),
935+
ManageCgroups: proto.Bool(true),
936+
NotifyScripts: proto.Bool(true),
937+
ShellJob: proto.Bool(criuOpts.ShellJob),
938+
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
939+
TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
940+
FileLocks: proto.Bool(criuOpts.FileLocks),
941+
EmptyNs: proto.Uint32(criuOpts.EmptyNs),
942+
OrphanPtsMaster: proto.Bool(true),
940943
},
941944
}
942945

@@ -1037,6 +1040,11 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
10371040

10381041
logPath := filepath.Join(opts.WorkDirectory, req.GetOpts().GetLogFile())
10391042
criuClient := os.NewFile(uintptr(fds[0]), "criu-transport-client")
1043+
criuClientCon, err := net.FileConn(criuClient)
1044+
if err != nil {
1045+
return err
1046+
}
1047+
10401048
criuServer := os.NewFile(uintptr(fds[1]), "criu-transport-server")
10411049
defer criuClient.Close()
10421050
defer criuServer.Close()
@@ -1101,14 +1109,15 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
11011109
if err != nil {
11021110
return err
11031111
}
1104-
_, err = criuClient.Write(data)
1112+
_, err = criuClientCon.Write(data)
11051113
if err != nil {
11061114
return err
11071115
}
11081116

11091117
buf := make([]byte, 10*4096)
1118+
oob := make([]byte, 4096)
11101119
for true {
1111-
n, err := criuClient.Read(buf)
1120+
n, oobn, _, _, err := criuClientCon.(*net.UnixConn).ReadMsgUnix(buf, oob)
11121121
if err != nil {
11131122
return err
11141123
}
@@ -1136,7 +1145,7 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
11361145
criuFeatures = resp.GetFeatures()
11371146
break
11381147
case t == criurpc.CriuReqType_NOTIFY:
1139-
if err := c.criuNotifications(resp, process, opts, extFds); err != nil {
1148+
if err := c.criuNotifications(resp, process, opts, extFds, oob[:oobn]); err != nil {
11401149
return err
11411150
}
11421151
t = criurpc.CriuReqType_NOTIFY
@@ -1220,11 +1229,12 @@ func unlockNetwork(config *configs.Config) error {
12201229
return nil
12211230
}
12221231

1223-
func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Process, opts *CriuOpts, fds []string) error {
1232+
func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Process, opts *CriuOpts, fds []string, oob []byte) error {
12241233
notify := resp.GetNotify()
12251234
if notify == nil {
12261235
return fmt.Errorf("invalid response: %s", resp.String())
12271236
}
1237+
logrus.Debugf("notify: %s\n", notify.GetScript())
12281238
switch {
12291239
case notify.GetScript() == "post-dump":
12301240
f, err := os.Create(filepath.Join(c.root, "checkpoint"))
@@ -1277,6 +1287,20 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc
12771287
logrus.Error(err)
12781288
}
12791289
}
1290+
case notify.GetScript() == "orphan-pts-master":
1291+
scm, err := syscall.ParseSocketControlMessage(oob)
1292+
if err != nil {
1293+
return err
1294+
}
1295+
fds, err := syscall.ParseUnixRights(&scm[0])
1296+
1297+
master := os.NewFile(uintptr(fds[0]), "orphan-pts-master")
1298+
defer master.Close()
1299+
1300+
// While we can access console.master, using the API is a good idea.
1301+
if err := utils.SendFd(process.ConsoleSocket, master); err != nil {
1302+
return err
1303+
}
12801304
}
12811305
return nil
12821306
}

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)