github.com/containerd/Containerd@v1.4.13/diff/stream_windows.go (about) 1 // +build windows 2 3 /* 4 Copyright The containerd Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package diff 20 21 import ( 22 "bytes" 23 "context" 24 "fmt" 25 "io" 26 "io/ioutil" 27 "os" 28 "os/exec" 29 "path/filepath" 30 "sync" 31 32 winio "github.com/Microsoft/go-winio" 33 "github.com/gogo/protobuf/proto" 34 "github.com/gogo/protobuf/types" 35 "github.com/pkg/errors" 36 "github.com/sirupsen/logrus" 37 ) 38 39 const processorPipe = "STREAM_PROCESSOR_PIPE" 40 41 // NewBinaryProcessor returns a binary processor for use with processing content streams 42 func NewBinaryProcessor(ctx context.Context, imt, rmt string, stream StreamProcessor, name string, args []string, payload *types.Any) (StreamProcessor, error) { 43 cmd := exec.CommandContext(ctx, name, args...) 44 cmd.Env = os.Environ() 45 46 if payload != nil { 47 data, err := proto.Marshal(payload) 48 if err != nil { 49 return nil, err 50 } 51 up, err := getUiqPath() 52 if err != nil { 53 return nil, err 54 } 55 path := fmt.Sprintf("\\\\.\\pipe\\containerd-processor-%s-pipe", up) 56 l, err := winio.ListenPipe(path, nil) 57 if err != nil { 58 return nil, err 59 } 60 go func() { 61 defer l.Close() 62 conn, err := l.Accept() 63 if err != nil { 64 logrus.WithError(err).Error("accept npipe connection") 65 return 66 } 67 io.Copy(conn, bytes.NewReader(data)) 68 conn.Close() 69 }() 70 cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", processorPipe, path)) 71 } 72 cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", mediaTypeEnvVar, imt)) 73 var ( 74 stdin io.Reader 75 closer func() error 76 err error 77 ) 78 if f, ok := stream.(RawProcessor); ok { 79 stdin = f.File() 80 closer = f.File().Close 81 } else { 82 stdin = stream 83 } 84 cmd.Stdin = stdin 85 r, w, err := os.Pipe() 86 if err != nil { 87 return nil, err 88 } 89 cmd.Stdout = w 90 stderr := bytes.NewBuffer(nil) 91 cmd.Stderr = stderr 92 93 if err := cmd.Start(); err != nil { 94 return nil, err 95 } 96 p := &binaryProcessor{ 97 cmd: cmd, 98 r: r, 99 mt: rmt, 100 stderr: stderr, 101 } 102 go p.wait() 103 104 // close after start and dup 105 w.Close() 106 if closer != nil { 107 closer() 108 } 109 return p, nil 110 } 111 112 type binaryProcessor struct { 113 cmd *exec.Cmd 114 r *os.File 115 mt string 116 stderr *bytes.Buffer 117 118 mu sync.Mutex 119 err error 120 } 121 122 func (c *binaryProcessor) Err() error { 123 c.mu.Lock() 124 defer c.mu.Unlock() 125 return c.err 126 } 127 128 func (c *binaryProcessor) wait() { 129 if err := c.cmd.Wait(); err != nil { 130 if _, ok := err.(*exec.ExitError); ok { 131 c.mu.Lock() 132 c.err = errors.New(c.stderr.String()) 133 c.mu.Unlock() 134 } 135 } 136 } 137 138 func (c *binaryProcessor) File() *os.File { 139 return c.r 140 } 141 142 func (c *binaryProcessor) MediaType() string { 143 return c.mt 144 } 145 146 func (c *binaryProcessor) Read(p []byte) (int, error) { 147 return c.r.Read(p) 148 } 149 150 func (c *binaryProcessor) Close() error { 151 err := c.r.Close() 152 if kerr := c.cmd.Process.Kill(); err == nil { 153 err = kerr 154 } 155 return err 156 } 157 158 func getUiqPath() (string, error) { 159 dir, err := ioutil.TempDir("", "") 160 if err != nil { 161 return "", err 162 } 163 os.Remove(dir) 164 return filepath.Base(dir), nil 165 }