...

Source file src/github.com/Microsoft/hcsshim/cmd/runhcs/exec.go

Documentation: github.com/Microsoft/hcsshim/cmd/runhcs

     1  //go:build windows
     2  
     3  package main
     4  
     5  import (
     6  	"encoding/json"
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  	"syscall"
    12  
    13  	"github.com/Microsoft/hcsshim/internal/appargs"
    14  	"github.com/opencontainers/runtime-spec/specs-go"
    15  	"github.com/urfave/cli"
    16  )
    17  
    18  var execCommand = cli.Command{
    19  	Name:  "exec",
    20  	Usage: "execute new process inside the container",
    21  	ArgsUsage: `<container-id> <command> [command options]  || -p process.json <container-id>
    22  
    23  Where "<container-id>" is the name for the instance of the container and
    24  "<command>" is the command to be executed in the container.
    25  "<command>" can't be empty unless a "-p" flag provided.
    26  
    27  EXAMPLE:
    28  For example, if the container is configured to run the linux ps command the
    29  following will output a list of processes running in the container:
    30  
    31      	# runhcs exec <container-id> ps`,
    32  	Flags: []cli.Flag{
    33  		cli.StringFlag{
    34  			Name:  "cwd",
    35  			Usage: "current working directory in the container",
    36  		},
    37  		cli.StringSliceFlag{
    38  			Name:  "env, e",
    39  			Usage: "set environment variables",
    40  		},
    41  		cli.BoolFlag{
    42  			Name:  "tty, t",
    43  			Usage: "allocate a pseudo-TTY",
    44  		},
    45  		cli.StringFlag{
    46  			Name: "user, u",
    47  		},
    48  		cli.StringFlag{
    49  			Name:  "process, p",
    50  			Usage: "path to the process.json",
    51  		},
    52  		cli.BoolFlag{
    53  			Name:  "detach,d",
    54  			Usage: "detach from the container's process",
    55  		},
    56  		cli.StringFlag{
    57  			Name:  "pid-file",
    58  			Value: "",
    59  			Usage: "specify the file to write the process id to",
    60  		},
    61  		cli.StringFlag{
    62  			Name:  "shim-log",
    63  			Value: "",
    64  			Usage: `path to the log file or named pipe (e.g. \\.\pipe\ProtectedPrefix\Administrators\runhcs-<container-id>-<exec-id>-log) for the launched shim process`,
    65  		},
    66  	},
    67  	Before: appargs.Validate(argID, appargs.Rest(appargs.String)),
    68  	Action: func(context *cli.Context) error {
    69  		id := context.Args().First()
    70  		pidFile, err := absPathOrEmpty(context.String("pid-file"))
    71  		if err != nil {
    72  			return err
    73  		}
    74  		shimLog, err := absPathOrEmpty(context.String("shim-log"))
    75  		if err != nil {
    76  			return err
    77  		}
    78  		c, err := getContainer(id, false)
    79  		if err != nil {
    80  			return err
    81  		}
    82  		defer c.Close()
    83  		status, err := c.Status()
    84  		if err != nil {
    85  			return err
    86  		}
    87  		if status != containerRunning {
    88  			return errContainerStopped
    89  		}
    90  		spec, err := getProcessSpec(context, c)
    91  		if err != nil {
    92  			return err
    93  		}
    94  		p, err := startProcessShim(id, pidFile, shimLog, spec)
    95  		if err != nil {
    96  			return err
    97  		}
    98  		if !context.Bool("detach") {
    99  			state, err := p.Wait()
   100  			if err != nil {
   101  				return err
   102  			}
   103  			os.Exit(int(state.Sys().(syscall.WaitStatus).ExitCode))
   104  		}
   105  		return nil
   106  	},
   107  	SkipArgReorder: true,
   108  }
   109  
   110  func getProcessSpec(context *cli.Context, c *container) (*specs.Process, error) {
   111  	if path := context.String("process"); path != "" {
   112  		f, err := os.Open(path)
   113  		if err != nil {
   114  			return nil, err
   115  		}
   116  		defer f.Close()
   117  		var p specs.Process
   118  		if err := json.NewDecoder(f).Decode(&p); err != nil {
   119  			return nil, err
   120  		}
   121  		return &p, validateProcessSpec(&p)
   122  	}
   123  
   124  	// process via cli flags
   125  	p := c.Spec.Process
   126  
   127  	if len(context.Args()) == 1 {
   128  		return nil, fmt.Errorf("process args cannot be empty")
   129  	}
   130  	p.Args = context.Args()[1:]
   131  	// override the cwd, if passed
   132  	if context.String("cwd") != "" {
   133  		p.Cwd = context.String("cwd")
   134  	}
   135  	// append the passed env variables
   136  	p.Env = append(p.Env, context.StringSlice("env")...)
   137  
   138  	// set the tty
   139  	if context.IsSet("tty") {
   140  		p.Terminal = context.Bool("tty")
   141  	}
   142  	// override the user, if passed
   143  	if context.String("user") != "" {
   144  		p.User.Username = context.String("user")
   145  	}
   146  	return p, nil
   147  }
   148  
   149  func validateProcessSpec(spec *specs.Process) error {
   150  	if spec.Cwd == "" {
   151  		return fmt.Errorf("cwd property must not be empty")
   152  	}
   153  	// IsAbs doesnt recognize Unix paths on Windows builds so handle that case
   154  	// here.
   155  	if !filepath.IsAbs(spec.Cwd) && !strings.HasPrefix(spec.Cwd, "/") {
   156  		return fmt.Errorf("cwd must be an absolute path")
   157  	}
   158  	if len(spec.Args) == 0 {
   159  		return fmt.Errorf("args must not be empty")
   160  	}
   161  	return nil
   162  }
   163  

View as plain text