github.com/containerd/Containerd@v1.4.13/runtime/v1/shim/service_linux.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package shim 18 19 import ( 20 "context" 21 "io" 22 "sync" 23 "syscall" 24 25 "github.com/containerd/console" 26 "github.com/containerd/fifo" 27 "github.com/pkg/errors" 28 ) 29 30 type linuxPlatform struct { 31 epoller *console.Epoller 32 } 33 34 func (p *linuxPlatform) CopyConsole(ctx context.Context, console console.Console, stdin, stdout, stderr string, wg *sync.WaitGroup) (console.Console, error) { 35 if p.epoller == nil { 36 return nil, errors.New("uninitialized epoller") 37 } 38 39 epollConsole, err := p.epoller.Add(console) 40 if err != nil { 41 return nil, err 42 } 43 var cwg sync.WaitGroup 44 45 if stdin != "" { 46 in, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY, 0) 47 if err != nil { 48 return nil, err 49 } 50 cwg.Add(1) 51 go func() { 52 cwg.Done() 53 bp := bufPool.Get().(*[]byte) 54 defer bufPool.Put(bp) 55 io.CopyBuffer(epollConsole, in, *bp) 56 // we need to shutdown epollConsole when pipe broken 57 epollConsole.Shutdown(p.epoller.CloseConsole) 58 epollConsole.Close() 59 }() 60 } 61 62 outw, err := fifo.OpenFifo(ctx, stdout, syscall.O_WRONLY, 0) 63 if err != nil { 64 return nil, err 65 } 66 outr, err := fifo.OpenFifo(ctx, stdout, syscall.O_RDONLY, 0) 67 if err != nil { 68 return nil, err 69 } 70 wg.Add(1) 71 cwg.Add(1) 72 go func() { 73 cwg.Done() 74 p := bufPool.Get().(*[]byte) 75 defer bufPool.Put(p) 76 io.CopyBuffer(outw, epollConsole, *p) 77 outw.Close() 78 outr.Close() 79 wg.Done() 80 }() 81 cwg.Wait() 82 return epollConsole, nil 83 } 84 85 func (p *linuxPlatform) ShutdownConsole(ctx context.Context, cons console.Console) error { 86 if p.epoller == nil { 87 return errors.New("uninitialized epoller") 88 } 89 epollConsole, ok := cons.(*console.EpollConsole) 90 if !ok { 91 return errors.Errorf("expected EpollConsole, got %#v", cons) 92 } 93 return epollConsole.Shutdown(p.epoller.CloseConsole) 94 } 95 96 func (p *linuxPlatform) Close() error { 97 return p.epoller.Close() 98 } 99 100 // initialize a single epoll fd to manage our consoles. `initPlatform` should 101 // only be called once. 102 func (s *Service) initPlatform() error { 103 if s.platform != nil { 104 return nil 105 } 106 epoller, err := console.NewEpoller() 107 if err != nil { 108 return errors.Wrap(err, "failed to initialize epoller") 109 } 110 s.platform = &linuxPlatform{ 111 epoller: epoller, 112 } 113 go epoller.Wait() 114 return nil 115 }