1// +build linux 2 3package main 4 5import ( 6 "encoding/json" 7 "fmt" 8 "os" 9 "strconv" 10 "strings" 11 12 "github.com/codegangsta/cli" 13 "github.com/opencontainers/runc/libcontainer/utils" 14 "github.com/opencontainers/runtime-spec/specs-go" 15) 16 17var execCommand = cli.Command{ 18 Name: "exec", 19 Usage: "execute new process inside the container", 20 ArgsUsage: `<container-id> <container command> 21 22Where "<container-id>" is the name for the instance of the container and 23"<container command>" is the command to be executed in the container. 24 25For example, if the container is configured to run the linux ps command the 26following will output a list of processes running in the container: 27 28 # runc exec <container-id> ps`, 29 Flags: []cli.Flag{ 30 cli.StringFlag{ 31 Name: "console", 32 Usage: "specify the pty slave path for use with the container", 33 }, 34 cli.StringFlag{ 35 Name: "cwd", 36 Usage: "current working directory in the container", 37 }, 38 cli.StringSliceFlag{ 39 Name: "env, e", 40 Usage: "set environment variables", 41 }, 42 cli.BoolFlag{ 43 Name: "tty, t", 44 Usage: "allocate a pseudo-TTY", 45 }, 46 cli.StringFlag{ 47 Name: "user, u", 48 Usage: "UID (format: <uid>[:<gid>])", 49 }, 50 cli.StringFlag{ 51 Name: "process, p", 52 Usage: "path to the process.json", 53 }, 54 cli.BoolFlag{ 55 Name: "detach,d", 56 Usage: "detach from the container's process", 57 }, 58 cli.StringFlag{ 59 Name: "pid-file", 60 Value: "", 61 Usage: "specify the file to write the process id to", 62 }, 63 cli.StringFlag{ 64 Name: "process-label", 65 Usage: "set the asm process label for the process commonly used with selinux", 66 }, 67 cli.StringFlag{ 68 Name: "apparmor", 69 Usage: "set the apparmor profile for the process", 70 }, 71 cli.BoolFlag{ 72 Name: "no-new-privs", 73 Usage: "set the no new privileges value for the process", 74 }, 75 cli.StringSliceFlag{ 76 Name: "cap, c", 77 Value: &cli.StringSlice{}, 78 Usage: "add a capability to the bounding set for the process", 79 }, 80 cli.BoolFlag{ 81 Name: "no-subreaper", 82 Usage: "disable the use of the subreaper used to reap reparented processes", 83 }, 84 }, 85 Action: func(context *cli.Context) { 86 if os.Geteuid() != 0 { 87 fatalf("runc should be run as root") 88 } 89 status, err := execProcess(context) 90 if err != nil { 91 fatalf("exec failed: %v", err) 92 } 93 os.Exit(status) 94 }, 95} 96 97func execProcess(context *cli.Context) (int, error) { 98 container, err := getContainer(context) 99 if err != nil { 100 return -1, err 101 } 102 detach := context.Bool("detach") 103 state, err := container.State() 104 if err != nil { 105 return -1, err 106 } 107 bundle := utils.SearchLabels(state.Config.Labels, "bundle") 108 p, err := getProcess(context, bundle) 109 if err != nil { 110 return -1, err 111 } 112 r := &runner{ 113 enableSubreaper: !context.Bool("no-subreaper"), 114 shouldDestroy: false, 115 container: container, 116 console: context.String("console"), 117 detach: detach, 118 pidFile: context.String("pid-file"), 119 } 120 return r.run(p) 121} 122 123func getProcess(context *cli.Context, bundle string) (*specs.Process, error) { 124 if path := context.String("process"); path != "" { 125 f, err := os.Open(path) 126 if err != nil { 127 return nil, err 128 } 129 defer f.Close() 130 var p specs.Process 131 if err := json.NewDecoder(f).Decode(&p); err != nil { 132 return nil, err 133 } 134 return &p, validateProcessSpec(&p) 135 } 136 // process via cli flags 137 if err := os.Chdir(bundle); err != nil { 138 return nil, err 139 } 140 spec, err := loadSpec(specConfig) 141 if err != nil { 142 return nil, err 143 } 144 p := spec.Process 145 p.Args = context.Args()[1:] 146 // override the cwd, if passed 147 if context.String("cwd") != "" { 148 p.Cwd = context.String("cwd") 149 } 150 if ap := context.String("apparmor"); ap != "" { 151 p.ApparmorProfile = ap 152 } 153 if l := context.String("process-label"); l != "" { 154 p.SelinuxLabel = l 155 } 156 if caps := context.StringSlice("cap"); len(caps) > 0 { 157 p.Capabilities = caps 158 } 159 // append the passed env variables 160 for _, e := range context.StringSlice("env") { 161 p.Env = append(p.Env, e) 162 } 163 // set the tty 164 if context.IsSet("tty") { 165 p.Terminal = context.Bool("tty") 166 } 167 if context.IsSet("no-new-privs") { 168 p.NoNewPrivileges = context.Bool("no-new-privs") 169 } 170 // override the user, if passed 171 if context.String("user") != "" { 172 u := strings.SplitN(context.String("user"), ":", 2) 173 if len(u) > 1 { 174 gid, err := strconv.Atoi(u[1]) 175 if err != nil { 176 return nil, fmt.Errorf("parsing %s as int for gid failed: %v", u[1], err) 177 } 178 p.User.GID = uint32(gid) 179 } 180 uid, err := strconv.Atoi(u[0]) 181 if err != nil { 182 return nil, fmt.Errorf("parsing %s as int for uid failed: %v", u[0], err) 183 } 184 p.User.UID = uint32(uid) 185 } 186 return &p, nil 187} 188