1// +build linux 2 3package main 4 5import ( 6 "encoding/json" 7 "fmt" 8 "io/ioutil" 9 "os" 10 "runtime" 11 12 "github.com/codegangsta/cli" 13 "github.com/opencontainers/runc/libcontainer/configs" 14 "github.com/opencontainers/runtime-spec/specs-go" 15) 16 17var specCommand = cli.Command{ 18 Name: "spec", 19 Usage: "create a new specification file", 20 ArgsUsage: "", 21 Description: `The spec command creates the new specification file named "` + specConfig + `" for 22the bundle. 23 24The spec generated is just a starter file. Editing of the spec is required to 25achieve desired results. For example, the newly generated spec includes an args 26parameter that is initially set to call the "sh" command when the container is 27started. Calling "sh" may work for an ubuntu container or busybox, but will not 28work for containers that do not include the "sh" program. 29 30EXAMPLE: 31 To run docker's hello-world container one needs to set the args parameter 32in the spec to call hello. This can be done using the sed command or a text 33editor. The following commands create a bundle for hello-world, change the 34default args parameter in the spec from "sh" to "/hello", then run the hello 35command in a new hello-world container named container1: 36 37 mkdir hello 38 cd hello 39 docker pull hello-world 40 docker export $(docker create hello-world) > hello-world.tar 41 mkdir rootfs 42 tar -C rootfs -xf hello-world.tar 43 runc spec 44 sed -i 's;"sh";"/hello";' ` + specConfig + ` 45 runc start container1 46 47In the start command above, "container1" is the name for the instance of the 48container that you are starting. The name you provide for the container instance 49must be unique on your host. 50 51When starting a container through runc, runc needs root privilege. If not 52already running as root, you can use sudo to give runc root privilege. For 53example: "sudo runc start container1" will give runc root privilege to start the 54container on your host.`, 55 Flags: []cli.Flag{ 56 cli.StringFlag{ 57 Name: "bundle, b", 58 Value: "", 59 Usage: "path to the root of the bundle directory", 60 }, 61 }, 62 Action: func(context *cli.Context) { 63 spec := specs.Spec{ 64 Version: specs.Version, 65 Platform: specs.Platform{ 66 OS: runtime.GOOS, 67 Arch: runtime.GOARCH, 68 }, 69 Root: specs.Root{ 70 Path: "rootfs", 71 Readonly: true, 72 }, 73 Process: specs.Process{ 74 Terminal: true, 75 User: specs.User{}, 76 Args: []string{ 77 "sh", 78 }, 79 Env: []string{ 80 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 81 "TERM=xterm", 82 }, 83 Cwd: "/", 84 NoNewPrivileges: true, 85 Capabilities: []string{ 86 "CAP_AUDIT_WRITE", 87 "CAP_KILL", 88 "CAP_NET_BIND_SERVICE", 89 }, 90 Rlimits: []specs.Rlimit{ 91 { 92 Type: "RLIMIT_NOFILE", 93 Hard: uint64(1024), 94 Soft: uint64(1024), 95 }, 96 }, 97 }, 98 Hostname: "runc", 99 Mounts: []specs.Mount{ 100 { 101 Destination: "/proc", 102 Type: "proc", 103 Source: "proc", 104 Options: nil, 105 }, 106 { 107 Destination: "/dev", 108 Type: "tmpfs", 109 Source: "tmpfs", 110 Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, 111 }, 112 { 113 Destination: "/dev/pts", 114 Type: "devpts", 115 Source: "devpts", 116 Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}, 117 }, 118 { 119 Destination: "/dev/shm", 120 Type: "tmpfs", 121 Source: "shm", 122 Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"}, 123 }, 124 { 125 Destination: "/dev/mqueue", 126 Type: "mqueue", 127 Source: "mqueue", 128 Options: []string{"nosuid", "noexec", "nodev"}, 129 }, 130 { 131 Destination: "/sys", 132 Type: "sysfs", 133 Source: "sysfs", 134 Options: []string{"nosuid", "noexec", "nodev", "ro"}, 135 }, 136 { 137 Destination: "/sys/fs/cgroup", 138 Type: "cgroup", 139 Source: "cgroup", 140 Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"}, 141 }, 142 }, 143 Linux: specs.Linux{ 144 MaskedPaths: []string{ 145 "/proc/kcore", 146 "/proc/latency_stats", 147 "/proc/timer_stats", 148 "/proc/sched_debug", 149 }, 150 ReadonlyPaths: []string{ 151 "/proc/asound", 152 "/proc/bus", 153 "/proc/fs", 154 "/proc/irq", 155 "/proc/sys", 156 "/proc/sysrq-trigger", 157 }, 158 Resources: &specs.Resources{ 159 Devices: []specs.DeviceCgroup{ 160 { 161 Allow: false, 162 Access: sPtr("rwm"), 163 }, 164 }, 165 }, 166 Namespaces: []specs.Namespace{ 167 { 168 Type: "pid", 169 }, 170 { 171 Type: "network", 172 }, 173 { 174 Type: "ipc", 175 }, 176 { 177 Type: "uts", 178 }, 179 { 180 Type: "mount", 181 }, 182 }, 183 }, 184 } 185 186 checkNoFile := func(name string) error { 187 _, err := os.Stat(name) 188 if err == nil { 189 return fmt.Errorf("File %s exists. Remove it first", name) 190 } 191 if !os.IsNotExist(err) { 192 return err 193 } 194 return nil 195 } 196 bundle := context.String("bundle") 197 if bundle != "" { 198 if err := os.Chdir(bundle); err != nil { 199 fatal(err) 200 } 201 } 202 if err := checkNoFile(specConfig); err != nil { 203 fatal(err) 204 } 205 data, err := json.MarshalIndent(&spec, "", "\t") 206 if err != nil { 207 fatal(err) 208 } 209 if err := ioutil.WriteFile(specConfig, data, 0666); err != nil { 210 fatal(err) 211 } 212 }, 213} 214 215func sPtr(s string) *string { return &s } 216func rPtr(r rune) *rune { return &r } 217func iPtr(i int64) *int64 { return &i } 218func u32Ptr(i int64) *uint32 { u := uint32(i); return &u } 219func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm } 220 221// loadSpec loads the specification from the provided path. 222// If the path is empty then the default path will be "config.json" 223func loadSpec(cPath string) (spec *specs.Spec, err error) { 224 cf, err := os.Open(cPath) 225 if err != nil { 226 if os.IsNotExist(err) { 227 return nil, fmt.Errorf("JSON specification file %s not found", cPath) 228 } 229 return nil, err 230 } 231 defer cf.Close() 232 233 if err = json.NewDecoder(cf).Decode(&spec); err != nil { 234 return nil, err 235 } 236 return spec, validateProcessSpec(&spec.Process) 237} 238 239func createLibContainerRlimit(rlimit specs.Rlimit) (configs.Rlimit, error) { 240 rl, err := strToRlimit(rlimit.Type) 241 if err != nil { 242 return configs.Rlimit{}, err 243 } 244 return configs.Rlimit{ 245 Type: rl, 246 Hard: uint64(rlimit.Hard), 247 Soft: uint64(rlimit.Soft), 248 }, nil 249} 250