| OLD | NEW |
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package main | 5 package main |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "encoding/json" | 8 "encoding/json" |
| 9 "io" | 9 "io" |
| 10 "os" | 10 "os" |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 cmd.Flags.StringVar(&cmd.chdir, "chdir", "", | 40 cmd.Flags.StringVar(&cmd.chdir, "chdir", "", |
| 41 "If specified, switch to this directory prior to running
the command.") | 41 "If specified, switch to this directory prior to running
the command.") |
| 42 cmd.Flags.Var(&cmd.streamServerURI, "streamserver-uri", | 42 cmd.Flags.Var(&cmd.streamServerURI, "streamserver-uri", |
| 43 "The stream server URI to bind to (e.g., "+string(exampl
eStreamServerURI)+").") | 43 "The stream server URI to bind to (e.g., "+string(exampl
eStreamServerURI)+").") |
| 44 cmd.Flags.BoolVar(&cmd.attach, "attach", true, | 44 cmd.Flags.BoolVar(&cmd.attach, "attach", true, |
| 45 "If true, attaches the bootstrapped process' STDOUT and
STDERR streams.") | 45 "If true, attaches the bootstrapped process' STDOUT and
STDERR streams.") |
| 46 cmd.Flags.BoolVar(&cmd.stdin, "forward-stdin", false, | 46 cmd.Flags.BoolVar(&cmd.stdin, "forward-stdin", false, |
| 47 "If true, forward STDIN to the bootstrapped process.") | 47 "If true, forward STDIN to the bootstrapped process.") |
| 48 | 48 |
| 49 // "stdout" command-line option. | 49 // "stdout" command-line option. |
| 50 cmd.stdout.Name = "stdout" | |
| 51 stdoutFlag := new(nestedflagset.FlagSet) | 50 stdoutFlag := new(nestedflagset.FlagSet) |
| 52 cmd.stdout.addFlags(&stdoutFlag.F) | 51 cmd.stdout.addFlags(&stdoutFlag.F) |
| 53 cmd.Flags.Var(stdoutFlag, "stdout", "STDOUT stream parameters.") | 52 cmd.Flags.Var(stdoutFlag, "stdout", "STDOUT stream parameters.") |
| 54 | 53 |
| 55 // "stderr" command-line option. | 54 // "stderr" command-line option. |
| 56 cmd.stderr.Name = "stderr" | |
| 57 stderrFlag := new(nestedflagset.FlagSet) | 55 stderrFlag := new(nestedflagset.FlagSet) |
| 58 cmd.stderr.addFlags(&stderrFlag.F) | 56 cmd.stderr.addFlags(&stderrFlag.F) |
| 59 cmd.Flags.Var(stderrFlag, "stderr", "STDERR stream parameters.") | 57 cmd.Flags.Var(stderrFlag, "stderr", "STDERR stream parameters.") |
| 60 | 58 |
| 61 return cmd | 59 return cmd |
| 62 }, | 60 }, |
| 63 } | 61 } |
| 64 | 62 |
| 65 type runCommandRun struct { | 63 type runCommandRun struct { |
| 66 subcommands.CommandRunBase | 64 subcommands.CommandRunBase |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 201 Cmd: exec.Command(commandPath, args...), | 199 Cmd: exec.Command(commandPath, args...), |
| 202 } | 200 } |
| 203 proc.Dir = cmd.chdir | 201 proc.Dir = cmd.chdir |
| 204 proc.Env = env.Sorted() | 202 proc.Env = env.Sorted() |
| 205 if cmd.stdin { | 203 if cmd.stdin { |
| 206 proc.Stdin = os.Stdin | 204 proc.Stdin = os.Stdin |
| 207 } | 205 } |
| 208 | 206 |
| 209 // Attach STDOUT / STDERR pipes if configured to do so. | 207 // Attach STDOUT / STDERR pipes if configured to do so. |
| 210 // | 208 // |
| 211 » // We track them with a sync.WaitGroup because we have to wait for them
to | 209 » // This will happen in one of two ways: |
| 212 » // close before reaping the process (see exec.Cmd's StdoutPipe method). | 210 » // - If no stream name is provided, we will not create a Butler stream f
or |
| 211 » // this output; instead, we will connect them directly to the bootstra
pped |
| 212 » // process. |
| 213 » // - If a stream name is provided, we will create a Butler stream for th
is |
| 214 » // output, and tee as necessary. |
| 215 » // |
| 216 » // When connecting the stream to the Butler, we track them with a |
| 217 » // sync.WaitGroup because we have to wait for them to close before reapi
ng the |
| 218 » // process (see exec.Cmd's StdoutPipe method). |
| 213 // | 219 // |
| 214 // In this case, we will hand them to the Butler, which will Close then
when | 220 // In this case, we will hand them to the Butler, which will Close then
when |
| 215 // it counters io.EOF. | 221 // it counters io.EOF. |
| 216 var ( | 222 var ( |
| 217 stdout, stderr io.ReadCloser | 223 stdout, stderr io.ReadCloser |
| 218 streamWG sync.WaitGroup | 224 streamWG sync.WaitGroup |
| 219 ) | 225 ) |
| 220 if cmd.attach { | 226 if cmd.attach { |
| 221 » » stdout, err = proc.StdoutPipe() | 227 » » // STDOUT |
| 222 » » if err != nil { | 228 » » if cmd.stdout.Name == "" { |
| 223 » » » log.WithError(err).Errorf(a, "Failed to get STDOUT pipe.
") | 229 » » » // Forward directly. |
| 224 » » » return runtimeErrorReturnCode | 230 » » » proc.Stdout = os.Stdout |
| 231 » » } else { |
| 232 » » » // Connect to Butler. |
| 233 » » » stdout, err = proc.StdoutPipe() |
| 234 » » » if err != nil { |
| 235 » » » » log.WithError(err).Errorf(a, "Failed to get STDO
UT pipe.") |
| 236 » » » » return runtimeErrorReturnCode |
| 237 » » » } |
| 238 » » » stdout = &callbackReadCloser{stdout, streamWG.Done} |
| 239 » » » streamWG.Add(1) |
| 225 } | 240 } |
| 226 stdout = &callbackReadCloser{stdout, streamWG.Done} | |
| 227 | 241 |
| 228 » » // Get our STDERR pipe | 242 » » // STDERR |
| 229 » » stderr, err = proc.StderrPipe() | 243 » » if cmd.stderr.Name == "" { |
| 230 » » if err != nil { | 244 » » » // Forward directly. |
| 231 » » » log.WithError(err).Errorf(a, "Failed to get STDERR pipe.
") | 245 » » » proc.Stderr = os.Stderr |
| 232 » » » return runtimeErrorReturnCode | 246 » » } else { |
| 247 » » » // Connect to Butler. |
| 248 » » » stderr, err = proc.StderrPipe() |
| 249 » » » if err != nil { |
| 250 » » » » log.WithError(err).Errorf(a, "Failed to get STDE
RR pipe.") |
| 251 » » » » return runtimeErrorReturnCode |
| 252 » » » } |
| 253 » » » stderr = &callbackReadCloser{stderr, streamWG.Done} |
| 254 » » » streamWG.Add(1) |
| 233 } | 255 } |
| 234 stderr = &callbackReadCloser{stderr, streamWG.Done} | |
| 235 streamWG.Add(2) | |
| 236 } | 256 } |
| 237 | 257 |
| 238 if log.IsLogging(a, log.Debug) { | 258 if log.IsLogging(a, log.Debug) { |
| 239 log.Fields{ | 259 log.Fields{ |
| 240 "commandPath": commandPath, | 260 "commandPath": commandPath, |
| 241 "cwd": cwd, | 261 "cwd": cwd, |
| 242 "args": args, | 262 "args": args, |
| 243 }.Debugf(a, "Executing application.") | 263 }.Debugf(a, "Executing application.") |
| 244 for _, entry := range proc.Env { | 264 for _, entry := range proc.Env { |
| 245 log.Debugf(a, "Environment variable: %s", entry) | 265 log.Debugf(a, "Environment variable: %s", entry) |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 372 // callbackReadCloser invokes a callback method when closed. | 392 // callbackReadCloser invokes a callback method when closed. |
| 373 type callbackReadCloser struct { | 393 type callbackReadCloser struct { |
| 374 io.ReadCloser | 394 io.ReadCloser |
| 375 callback func() | 395 callback func() |
| 376 } | 396 } |
| 377 | 397 |
| 378 func (c *callbackReadCloser) Close() error { | 398 func (c *callbackReadCloser) Close() error { |
| 379 defer c.callback() | 399 defer c.callback() |
| 380 return c.ReadCloser.Close() | 400 return c.ReadCloser.Close() |
| 381 } | 401 } |
| OLD | NEW |