1// +build linux 2 3package main 4 5import ( 6 "fmt" 7 "io" 8 "os" 9 "sync" 10 11 "github.com/docker/docker/pkg/term" 12 "github.com/opencontainers/runc/libcontainer" 13) 14 15// setup standard pipes so that the TTY of the calling runc process 16// is not inherited by the container. 17func createStdioPipes(p *libcontainer.Process, rootuid int) (*tty, error) { 18 i, err := p.InitializeIO(rootuid) 19 if err != nil { 20 return nil, err 21 } 22 t := &tty{ 23 closers: []io.Closer{ 24 i.Stdin, 25 i.Stdout, 26 i.Stderr, 27 }, 28 } 29 // add the process's io to the post start closers if they support close 30 for _, cc := range []interface{}{ 31 p.Stdin, 32 p.Stdout, 33 p.Stderr, 34 } { 35 if c, ok := cc.(io.Closer); ok { 36 t.postStart = append(t.postStart, c) 37 } 38 } 39 go func() { 40 io.Copy(i.Stdin, os.Stdin) 41 i.Stdin.Close() 42 }() 43 t.wg.Add(2) 44 go t.copyIO(os.Stdout, i.Stdout) 45 go t.copyIO(os.Stderr, i.Stderr) 46 return t, nil 47} 48 49func (t *tty) copyIO(w io.Writer, r io.ReadCloser) { 50 defer t.wg.Done() 51 io.Copy(w, r) 52 r.Close() 53} 54 55func createTty(p *libcontainer.Process, rootuid int, consolePath string) (*tty, error) { 56 if consolePath != "" { 57 if err := p.ConsoleFromPath(consolePath); err != nil { 58 return nil, err 59 } 60 return &tty{}, nil 61 } 62 console, err := p.NewConsole(rootuid) 63 if err != nil { 64 return nil, err 65 } 66 go io.Copy(console, os.Stdin) 67 go io.Copy(os.Stdout, console) 68 69 state, err := term.SetRawTerminal(os.Stdin.Fd()) 70 if err != nil { 71 return nil, fmt.Errorf("failed to set the terminal from the stdin: %v", err) 72 } 73 return &tty{ 74 console: console, 75 state: state, 76 closers: []io.Closer{ 77 console, 78 }, 79 }, nil 80} 81 82type tty struct { 83 console libcontainer.Console 84 state *term.State 85 closers []io.Closer 86 postStart []io.Closer 87 wg sync.WaitGroup 88} 89 90// ClosePostStart closes any fds that are provided to the container and dup2'd 91// so that we no longer have copy in our process. 92func (t *tty) ClosePostStart() error { 93 for _, c := range t.postStart { 94 c.Close() 95 } 96 return nil 97} 98 99// Close closes all open fds for the tty and/or restores the orignal 100// stdin state to what it was prior to the container execution 101func (t *tty) Close() error { 102 // ensure that our side of the fds are always closed 103 for _, c := range t.postStart { 104 c.Close() 105 } 106 // wait for the copy routines to finish before closing the fds 107 t.wg.Wait() 108 for _, c := range t.closers { 109 c.Close() 110 } 111 if t.state != nil { 112 term.RestoreTerminal(os.Stdin.Fd(), t.state) 113 } 114 return nil 115} 116 117func (t *tty) resize() error { 118 if t.console == nil { 119 return nil 120 } 121 ws, err := term.GetWinsize(os.Stdin.Fd()) 122 if err != nil { 123 return err 124 } 125 return term.SetWinsize(t.console.Fd(), ws) 126} 127