github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/cmd/ctr/commands/tasks/exec.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 tasks 18 19 import ( 20 "errors" 21 "io" 22 "net/url" 23 "os" 24 25 "github.com/containerd/console" 26 "github.com/containerd/containerd" 27 "github.com/containerd/containerd/cio" 28 "github.com/containerd/containerd/cmd/ctr/commands" 29 "github.com/sirupsen/logrus" 30 "github.com/urfave/cli" 31 ) 32 33 //TODO:(jessvalarezo) exec-id is optional here, update to required arg 34 var execCommand = cli.Command{ 35 Name: "exec", 36 Usage: "execute additional processes in an existing container", 37 ArgsUsage: "[flags] CONTAINER CMD [ARG...]", 38 SkipArgReorder: true, 39 Flags: []cli.Flag{ 40 cli.StringFlag{ 41 Name: "cwd", 42 Usage: "working directory of the new process", 43 }, 44 cli.BoolFlag{ 45 Name: "tty,t", 46 Usage: "allocate a TTY for the container", 47 }, 48 cli.BoolFlag{ 49 Name: "detach,d", 50 Usage: "detach from the task after it has started execution", 51 }, 52 cli.StringFlag{ 53 Name: "exec-id", 54 Usage: "exec specific id for the process", 55 }, 56 cli.StringFlag{ 57 Name: "fifo-dir", 58 Usage: "directory used for storing IO FIFOs", 59 }, 60 cli.StringFlag{ 61 Name: "log-uri", 62 Usage: "log uri for custom shim logging", 63 }, 64 }, 65 Action: func(context *cli.Context) error { 66 var ( 67 id = context.Args().First() 68 args = context.Args().Tail() 69 tty = context.Bool("tty") 70 detach = context.Bool("detach") 71 ) 72 if id == "" { 73 return errors.New("container id must be provided") 74 } 75 client, ctx, cancel, err := commands.NewClient(context) 76 if err != nil { 77 return err 78 } 79 defer cancel() 80 container, err := client.LoadContainer(ctx, id) 81 if err != nil { 82 return err 83 } 84 spec, err := container.Spec(ctx) 85 if err != nil { 86 return err 87 } 88 task, err := container.Task(ctx, nil) 89 if err != nil { 90 return err 91 } 92 93 pspec := spec.Process 94 pspec.Terminal = tty 95 pspec.Args = args 96 97 var ( 98 ioCreator cio.Creator 99 stdinC = &stdinCloser{ 100 stdin: os.Stdin, 101 } 102 ) 103 104 if logURI := context.String("log-uri"); logURI != "" { 105 uri, err := url.Parse(logURI) 106 if err != nil { 107 return err 108 } 109 110 if dir := context.String("fifo-dir"); dir != "" { 111 return errors.New("can't use log-uri with fifo-dir") 112 } 113 114 if tty { 115 return errors.New("can't use log-uri with tty") 116 } 117 118 ioCreator = cio.LogURI(uri) 119 } else { 120 cioOpts := []cio.Opt{cio.WithStreams(stdinC, os.Stdout, os.Stderr), cio.WithFIFODir(context.String("fifo-dir"))} 121 if tty { 122 cioOpts = append(cioOpts, cio.WithTerminal) 123 } 124 ioCreator = cio.NewCreator(cioOpts...) 125 } 126 127 process, err := task.Exec(ctx, context.String("exec-id"), pspec, ioCreator) 128 if err != nil { 129 return err 130 } 131 stdinC.closer = func() { 132 process.CloseIO(ctx, containerd.WithStdinCloser) 133 } 134 // if detach, we should not call this defer 135 if !detach { 136 defer process.Delete(ctx) 137 } 138 139 statusC, err := process.Wait(ctx) 140 if err != nil { 141 return err 142 } 143 144 var con console.Console 145 if tty { 146 con = console.Current() 147 defer con.Reset() 148 if err := con.SetRaw(); err != nil { 149 return err 150 } 151 } 152 if !detach { 153 if tty { 154 if err := HandleConsoleResize(ctx, process, con); err != nil { 155 logrus.WithError(err).Error("console resize") 156 } 157 } else { 158 sigc := commands.ForwardAllSignals(ctx, process) 159 defer commands.StopCatch(sigc) 160 } 161 } 162 163 if err := process.Start(ctx); err != nil { 164 return err 165 } 166 if detach { 167 return nil 168 } 169 status := <-statusC 170 code, _, err := status.Result() 171 if err != nil { 172 return err 173 } 174 if code != 0 { 175 return cli.NewExitError("", int(code)) 176 } 177 return nil 178 }, 179 } 180 181 type stdinCloser struct { 182 stdin *os.File 183 closer func() 184 } 185 186 func (s *stdinCloser) Read(p []byte) (int, error) { 187 n, err := s.stdin.Read(p) 188 if err == io.EOF { 189 if s.closer != nil { 190 s.closer() 191 } 192 } 193 return n, err 194 }