exec.go 4.5 KB

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