Skip to content

Commit d542ece

Browse files
committed
Add tty support from client
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
1 parent b2e649b commit d542ece

File tree

9 files changed

+80
-20
lines changed

9 files changed

+80
-20
lines changed

api/grpc/server/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func (s *apiServer) CreateContainer(ctx context.Context, c *types.CreateContaine
3636
e.Stdout = c.Stdout
3737
e.Stderr = c.Stderr
3838
e.Stdin = c.Stdin
39+
e.Console = c.Console
3940
if c.Checkpoint != "" {
4041
e.Checkpoint = &runtime.Checkpoint{
4142
Name: c.Checkpoint,

ctr/container.go

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111

1212
"github.com/codegangsta/cli"
1313
"github.com/docker/containerd/api/grpc/types"
14+
"github.com/docker/docker/pkg/term"
15+
"github.com/opencontainers/runc/libcontainer"
1416
netcontext "golang.org/x/net/context"
1517
"google.golang.org/grpc"
1618
)
@@ -72,6 +74,10 @@ var StartCommand = cli.Command{
7274
Name: "interactive,i",
7375
Usage: "connect to the stdio of the container",
7476
},
77+
cli.BoolFlag{
78+
Name: "tty,t",
79+
Usage: "allocate a tty for use with the container",
80+
},
7581
},
7682
Action: func(context *cli.Context) {
7783
var (
@@ -84,6 +90,11 @@ var StartCommand = cli.Command{
8490
if id == "" {
8591
fatal("container id cannot be empty", 1)
8692
}
93+
c := getClient()
94+
events, err := c.Events(netcontext.Background(), &types.EventsRequest{})
95+
if err != nil {
96+
fatal(err.Error(), 1)
97+
}
8798
r := &types.CreateContainerRequest{
8899
Id: id,
89100
BundlePath: path,
@@ -94,17 +105,57 @@ var StartCommand = cli.Command{
94105
fatal(err.Error(), 1)
95106
}
96107
}
97-
c := getClient()
108+
if context.Bool("tty") {
109+
if err := attachTty(r); err != nil {
110+
fatal(err.Error(), 1)
111+
}
112+
}
98113
if _, err := c.CreateContainer(netcontext.Background(), r); err != nil {
99114
fatal(err.Error(), 1)
100115
}
101116
if stdin != nil {
102-
io.Copy(stdin, os.Stdin)
117+
go func() {
118+
io.Copy(stdin, os.Stdin)
119+
if state != nil {
120+
term.RestoreTerminal(os.Stdin.Fd(), state)
121+
}
122+
}()
123+
for {
124+
e, err := events.Recv()
125+
if err != nil {
126+
fatal(err.Error(), 1)
127+
}
128+
if e.Id == id && e.Type == "exit" {
129+
os.Exit(int(e.Status))
130+
}
131+
}
103132
}
104133
},
105134
}
106135

107-
var stdin io.WriteCloser
136+
var (
137+
stdin io.WriteCloser
138+
state *term.State
139+
)
140+
141+
func attachTty(r *types.CreateContainerRequest) error {
142+
console, err := libcontainer.NewConsole(os.Getuid(), os.Getgid())
143+
if err != nil {
144+
return err
145+
}
146+
r.Console = console.Path()
147+
stdin = console
148+
go func() {
149+
io.Copy(os.Stdout, console)
150+
console.Close()
151+
}()
152+
s, err := term.SetRawTerminal(os.Stdin.Fd())
153+
if err != nil {
154+
return err
155+
}
156+
state = s
157+
return nil
158+
}
108159

109160
func attachStdio(r *types.CreateContainerRequest) error {
110161
dir, err := ioutil.TempDir("", "ctr-")

event.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ type Event struct {
4141
Stdout string
4242
Stderr string
4343
Stdin string
44+
Console string
4445
Pid int
4546
Status int
4647
Signal os.Signal

io.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,10 @@ type copier struct {
5353

5454
func (l *copier) Close() (err error) {
5555
for _, c := range append(l.closers, l.config.Stdin, l.config.Stdout, l.config.Stderr) {
56-
if cerr := c.Close(); err == nil {
57-
err = cerr
56+
if c != nil {
57+
if cerr := c.Close(); err == nil {
58+
err = cerr
59+
}
5860
}
5961
}
6062
return err

linux/linux.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,18 @@ func (p *libcontainerProcess) Signal(s os.Signal) error {
188188
func (p *libcontainerProcess) Close() error {
189189
// in close we always need to call wait to close/flush any pipes
190190
_, err := p.process.Wait()
191-
p.process.Stdin.(io.Closer).Close()
192-
p.process.Stdout.(io.Closer).Close()
193-
p.process.Stderr.(io.Closer).Close()
191+
// explicitly close any open fd on the process
192+
for _, cl := range []interface{}{
193+
p.process.Stderr,
194+
p.process.Stdout,
195+
p.process.Stdin,
196+
} {
197+
if cl != nil {
198+
if c, ok := cl.(io.Closer); ok {
199+
c.Close()
200+
}
201+
}
202+
}
194203
return err
195204
}
196205

@@ -375,7 +384,7 @@ func (r *libcontainerRuntime) Type() string {
375384
return "libcontainer"
376385
}
377386

378-
func (r *libcontainerRuntime) Create(id, bundlePath string) (runtime.Container, *runtime.IO, error) {
387+
func (r *libcontainerRuntime) Create(id, bundlePath, consolePath string) (runtime.Container, *runtime.IO, error) {
379388
spec, rspec, err := r.loadSpec(
380389
filepath.Join(bundlePath, "config.json"),
381390
filepath.Join(bundlePath, "runtime.json"),
@@ -397,11 +406,9 @@ func (r *libcontainerRuntime) Create(id, bundlePath string) (runtime.Container,
397406
}
398407
var rio runtime.IO
399408
if spec.Process.Terminal {
400-
console, err := process.NewConsole(int(spec.Process.User.UID))
401-
if err != nil {
409+
if err := process.ConsoleFromPath(consolePath); err != nil {
402410
return nil, nil, err
403411
}
404-
rio.Console = console
405412
} else {
406413
i, err := process.InitializeIO(int(spec.Process.User.UID))
407414
if err != nil {

runc/runc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ func (r *runcRuntime) Type() string {
147147
return "runc"
148148
}
149149

150-
func (r *runcRuntime) Create(id, bundlePath string) (runtime.Container, *runtime.IO, error) {
150+
func (r *runcRuntime) Create(id, bundlePath, consolePath string) (runtime.Container, *runtime.IO, error) {
151151
var s specs.Spec
152152
f, err := os.Open(filepath.Join(bundlePath, "config.json"))
153153
if err != nil {

runtime/container.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,9 @@ type Console interface {
3232
}
3333

3434
type IO struct {
35-
Stdin io.WriteCloser
36-
Stdout io.ReadCloser
37-
Stderr io.ReadCloser
38-
Console Console
35+
Stdin io.WriteCloser
36+
Stdout io.ReadCloser
37+
Stderr io.ReadCloser
3938
}
4039

4140
func (i *IO) Close() error {
@@ -44,7 +43,6 @@ func (i *IO) Close() error {
4443
i.Stdin,
4544
i.Stdout,
4645
i.Stderr,
47-
i.Console,
4846
} {
4947
if c != nil {
5048
if err := c.Close(); oerr == nil {

runtime/runtime.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type Runtime interface {
2020
// Type of the runtime
2121
Type() string
2222
// Create creates a new container initialized but without it starting it
23-
Create(id, bundlePath string) (Container, *IO, error)
23+
Create(id, bundlePath, consolePath string) (Container, *IO, error)
2424
// StartProcess adds a new process to the container
2525
StartProcess(Container, specs.Process) (Process, *IO, error)
2626
}

start.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ type StartEvent struct {
55
}
66

77
func (h *StartEvent) Handle(e *Event) error {
8-
container, io, err := h.s.runtime.Create(e.ID, e.BundlePath)
8+
container, io, err := h.s.runtime.Create(e.ID, e.BundlePath, e.Console)
99
if err != nil {
1010
return err
1111
}

0 commit comments

Comments
 (0)