123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673 |
- package dockerlaunch
- import (
- "bufio"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "os/exec"
- "path"
- "strconv"
- "strings"
- "syscall"
- log "github.com/Sirupsen/logrus"
- "github.com/docker/libnetwork/resolvconf"
- "github.com/rancher/docker-from-scratch/selinux"
- "github.com/rancher/docker-from-scratch/util"
- "github.com/rancher/netconf"
- )
- const (
- defaultPrefix = "/usr"
- iptables = "/sbin/iptables"
- modprobe = "/sbin/modprobe"
- systemdRoot = "/sys/fs/cgroup/systemd/user.slice"
- distSuffix = ".dist"
- )
- var (
- mounts = [][]string{
- {"devtmpfs", "/dev", "devtmpfs", ""},
- {"none", "/dev/pts", "devpts", ""},
- {"shm", "/dev/shm", "tmpfs", "rw,nosuid,nodev,noexec,relatime,size=65536k"},
- {"mqueue", "/dev/mqueue", "mqueue", "rw,nosuid,nodev,noexec,relatime"},
- {"none", "/proc", "proc", ""},
- {"none", "/run", "tmpfs", ""},
- {"none", "/sys", "sysfs", ""},
- {"none", "/sys/fs/cgroup", "tmpfs", ""},
- }
- optionalMounts = [][]string{
- {"none", "/sys/fs/selinux", "selinuxfs", ""},
- }
- systemdMounts = [][]string{
- {"systemd", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd"},
- }
- )
- type Config struct {
- Fork bool
- PidOne bool
- CommandName string
- DnsConfig netconf.DnsConfig
- BridgeName string
- BridgeAddress string
- BridgeMtu int
- CgroupHierarchy map[string]string
- LogFile string
- NoLog bool
- EmulateSystemd bool
- NoFiles uint64
- Environment []string
- GraphDirectory string
- }
- func createMounts(mounts ...[]string) error {
- for _, mount := range mounts {
- log.Debugf("Mounting %s %s %s %s", mount[0], mount[1], mount[2], mount[3])
- err := util.Mount(mount[0], mount[1], mount[2], mount[3])
- if err != nil {
- return err
- }
- }
- return nil
- }
- func createOptionalMounts(mounts ...[]string) {
- for _, mount := range mounts {
- log.Debugf("Mounting %s %s %s %s", mount[0], mount[1], mount[2], mount[3])
- err := util.Mount(mount[0], mount[1], mount[2], mount[3])
- if err != nil {
- log.Debugf("Unable to mount %s %s %s %s: %s", mount[0], mount[1], mount[2], mount[3], err)
- }
- }
- }
- func createDirs(dirs ...string) error {
- for _, dir := range dirs {
- if _, err := os.Stat(dir); os.IsNotExist(err) {
- log.Debugf("Creating %s", dir)
- err = os.MkdirAll(dir, 0755)
- if err != nil {
- return err
- }
- }
- }
- return nil
- }
- func mountCgroups(hierarchyConfig map[string]string) error {
- f, err := os.Open("/proc/cgroups")
- if err != nil {
- return err
- }
- defer f.Close()
- scanner := bufio.NewScanner(f)
- hierarchies := make(map[string][]string)
- for scanner.Scan() {
- text := scanner.Text()
- log.Debugf("/proc/cgroups: %s", text)
- fields := strings.SplitN(text, "\t", 3)
- cgroup := fields[0]
- if cgroup == "" || cgroup[0] == '#' || len(fields) < 3 {
- continue
- }
- hierarchy := hierarchyConfig[cgroup]
- if hierarchy == "" {
- hierarchy = fields[1]
- }
- if hierarchy == "0" {
- hierarchy = cgroup
- }
- hierarchies[hierarchy] = append(hierarchies[hierarchy], cgroup)
- }
- for _, hierarchy := range hierarchies {
- if err := mountCgroup(strings.Join(hierarchy, ",")); err != nil {
- return err
- }
- }
- if err = scanner.Err(); err != nil {
- return err
- }
- log.Debug("Done mouting cgroupfs")
- return nil
- }
- func CreateSymlinks(pathSets [][]string) error {
- for _, paths := range pathSets {
- if err := CreateSymlink(paths[0], paths[1]); err != nil {
- return err
- }
- }
- return nil
- }
- func CreateSymlink(src, dest string) error {
- if _, err := os.Lstat(dest); os.IsNotExist(err) {
- log.Debugf("Symlinking %s => %s", dest, src)
- if err = os.Symlink(src, dest); err != nil {
- return err
- }
- }
- return nil
- }
- func mountCgroup(cgroup string) error {
- if err := createDirs("/sys/fs/cgroup/" + cgroup); err != nil {
- return err
- }
- if err := createMounts([][]string{{"none", "/sys/fs/cgroup/" + cgroup, "cgroup", cgroup}}...); err != nil {
- return err
- }
- parts := strings.Split(cgroup, ",")
- if len(parts) > 1 {
- for _, part := range parts {
- if err := CreateSymlink("/sys/fs/cgroup/"+cgroup, "/sys/fs/cgroup/"+part); err != nil {
- return err
- }
- }
- }
- return nil
- }
- func execDocker(config *Config, docker, cmd string, args []string) (*exec.Cmd, error) {
- if len(args) > 0 && args[0] == "docker" {
- args = args[1:]
- }
- log.Debugf("Launching Docker %s %s %v", docker, cmd, args)
- env := os.Environ()
- if len(config.Environment) != 0 {
- env = append(env, config.Environment...)
- }
- if config.Fork {
- cmd := exec.Command(docker, args...)
- if !config.NoLog {
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- }
- cmd.Env = env
- err := cmd.Start()
- if err != nil {
- return cmd, err
- }
- if config.PidOne {
- PidOne()
- }
- return cmd, err
- } else {
- err := syscall.Exec(expand(docker), append([]string{cmd}, args...), env)
- return nil, err
- }
- }
- func copyDefault(folder, name string) error {
- defaultFile := path.Join(defaultPrefix, folder, name)
- if err := CopyFile(defaultFile, folder, name); err != nil {
- return err
- }
- return nil
- }
- func copyDefaultFolder(folder string) error {
- defaultFolder := path.Join(defaultPrefix, folder)
- files, _ := ioutil.ReadDir(defaultFolder)
- for _, file := range files {
- if file.IsDir() {
- continue
- }
- if err := copyDefault(folder, file.Name()); err != nil {
- return err
- }
- }
- return nil
- }
- func defaultFiles(files ...string) error {
- for _, file := range files {
- dir := path.Dir(file)
- name := path.Base(file)
- if err := copyDefault(dir, name); err != nil {
- return err
- }
- }
- return nil
- }
- func defaultFolders(folders ...string) error {
- for _, folder := range folders {
- copyDefaultFolder(folder)
- }
- return nil
- }
- func CopyFile(src, folder, name string) error {
- if _, err := os.Stat(src); os.IsNotExist(err) {
- return nil
- }
- dst := path.Join(folder, name)
- if _, err := os.Stat(dst); err == nil {
- return nil
- }
- if err := createDirs(folder); err != nil {
- return err
- }
- srcFile, err := os.Open(src)
- if err != nil {
- return err
- }
- defer srcFile.Close()
- dstFile, err := os.Create(dst)
- if err != nil {
- return err
- }
- defer dstFile.Close()
- _, err = io.Copy(dstFile, srcFile)
- return err
- }
- func tryCreateFile(name, content string) error {
- if _, err := os.Stat(name); err == nil {
- return nil
- }
- if err := createDirs(path.Dir(name)); err != nil {
- return err
- }
- return ioutil.WriteFile(name, []byte(content), 0644)
- }
- func createPasswd() error {
- return tryCreateFile("/etc/passwd", "root:x:0:0:root:/root:/bin/sh\n")
- }
- func createGroup() error {
- return tryCreateFile("/etc/group", "root:x:0:\n")
- }
- func setupNetworking(config *Config) error {
- if config == nil {
- return nil
- }
- hostname, err := os.Hostname()
- if err != nil {
- return err
- }
- tryCreateFile("/etc/hosts", `127.0.0.1 localhost
- ::1 localhost ip6-localhost ip6-loopback
- fe00::0 ip6-localnet
- ff00::0 ip6-mcastprefix
- ff02::1 ip6-allnodes
- ff02::2 ip6-allrouters
- 127.0.1.1 `+hostname)
- if len(config.DnsConfig.Nameservers) != 0 {
- if _, err := resolvconf.Build("/etc/resolv.conf", config.DnsConfig.Nameservers, config.DnsConfig.Search, nil); err != nil {
- return err
- }
- }
- if config.BridgeName != "" {
- log.Debugf("Creating bridge %s (%s)", config.BridgeName, config.BridgeAddress)
- if err := netconf.ApplyNetworkConfigs(&netconf.NetworkConfig{
- Interfaces: map[string]netconf.InterfaceConfig{
- config.BridgeName: {
- Address: config.BridgeAddress,
- MTU: config.BridgeMtu,
- Bridge: "true",
- },
- },
- }); err != nil {
- return err
- }
- }
- return nil
- }
- func ParseConfig(config *Config, args ...string) []string {
- for i, arg := range args {
- if strings.HasPrefix(arg, "--bip") {
- config.BridgeAddress = util.GetValue(i, args)
- } else if strings.HasPrefix(arg, "--fixed-cidr") {
- config.BridgeAddress = util.GetValue(i, args)
- } else if strings.HasPrefix(arg, "-b") || strings.HasPrefix(arg, "--bridge") {
- config.BridgeName = util.GetValue(i, args)
- } else if strings.HasPrefix(arg, "--mtu") {
- mtu, err := strconv.Atoi(util.GetValue(i, args))
- if err != nil {
- config.BridgeMtu = mtu
- }
- } else if strings.HasPrefix(arg, "-g") || strings.HasPrefix(arg, "--graph") {
- config.GraphDirectory = util.GetValue(i, args)
- }
- }
- if config.BridgeName != "" && config.BridgeAddress != "" {
- newArgs := []string{}
- skip := false
- for _, arg := range args {
- if skip {
- skip = false
- continue
- }
- if arg == "--bip" {
- skip = true
- continue
- } else if strings.HasPrefix(arg, "--bip=") {
- continue
- }
- newArgs = append(newArgs, arg)
- }
- args = newArgs
- }
- return args
- }
- func PrepareFs(config *Config) error {
- if err := createMounts(mounts...); err != nil {
- return err
- }
- createOptionalMounts(optionalMounts...)
- if err := mountCgroups(config.CgroupHierarchy); err != nil {
- return err
- }
- if err := createLayout(config); err != nil {
- return err
- }
- if err := firstPrepare(); err != nil {
- return err
- }
- return nil
- }
- func touchSocket(path string) error {
- if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) {
- return err
- }
- return ioutil.WriteFile(path, []byte{}, 0700)
- }
- func touchSockets(args ...string) error {
- touched := false
- for i, arg := range args {
- if strings.HasPrefix(arg, "-H") {
- val := util.GetValue(i, args)
- if strings.HasPrefix(val, "unix://") {
- val = val[len("unix://"):]
- log.Debugf("Creating temp file at %s", val)
- if err := touchSocket(val); err != nil {
- return err
- }
- touched = true
- }
- }
- }
- if !touched {
- return touchSocket("/var/run/docker.sock")
- }
- return nil
- }
- func createLayout(config *Config) error {
- if err := createDirs("/tmp", "/root/.ssh", "/var", "/usr/lib"); err != nil {
- return err
- }
- graphDirectory := config.GraphDirectory
- if config.GraphDirectory == "" {
- graphDirectory = "/var/lib/docker"
- }
- if err := createDirs(graphDirectory); err != nil {
- return err
- }
- selinux.SetFileContext(graphDirectory, "system_u:object_r:var_lib_t:s0")
- return CreateSymlinks([][]string{
- {"usr/lib", "/lib"},
- {"usr/sbin", "/sbin"},
- {"../run", "/var/run"},
- })
- }
- func firstPrepare() error {
- os.Setenv("PATH", "/sbin:/usr/sbin:/usr/bin")
- if err := defaultFiles(
- "/etc/ssl/certs/ca-certificates.crt",
- "/etc/passwd",
- "/etc/group",
- ); err != nil {
- return err
- }
- if err := defaultFolders(
- "/etc/selinux",
- "/etc/selinux/ros",
- "/etc/selinux/ros/policy",
- "/etc/selinux/ros/contexts",
- ); err != nil {
- return err
- }
- if err := createPasswd(); err != nil {
- return err
- }
- if err := createGroup(); err != nil {
- return err
- }
- return nil
- }
- func secondPrepare(config *Config, docker string, args ...string) error {
- if err := setupNetworking(config); err != nil {
- return err
- }
- if err := touchSockets(args...); err != nil {
- return err
- }
- if err := setupLogging(config); err != nil {
- return err
- }
- for _, i := range []string{docker, iptables, modprobe} {
- if err := setupBin(config, i); err != nil {
- return err
- }
- }
- if err := setUlimit(config); err != nil {
- return err
- }
- if err := setupSystemd(config); err != nil {
- return err
- }
- return nil
- }
- func setupSystemd(config *Config) error {
- if !config.EmulateSystemd {
- return nil
- }
- cgroups, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cgroup", os.Getpid()))
- if err != nil {
- return err
- }
- if strings.Contains(string(cgroups), "name=systemd") {
- return nil
- }
- if err := createMounts(systemdMounts...); err != nil {
- return err
- }
- if err := os.Mkdir(systemdRoot, 0755); err != nil {
- return err
- }
- return ioutil.WriteFile(path.Join(systemdRoot, "cgroup.procs"), []byte(strconv.Itoa(os.Getpid())+"\n"), 0644)
- }
- func expand(bin string) string {
- expanded, err := exec.LookPath(bin)
- if err == nil {
- return expanded
- }
- return bin
- }
- func setupBin(config *Config, bin string) error {
- expanded, err := exec.LookPath(bin)
- if err == nil {
- return nil
- }
- expanded, err = exec.LookPath(bin + distSuffix)
- if err != nil {
- // Purposely not returning error
- return nil
- }
- return CreateSymlink(expanded, expanded[:len(expanded)-len(distSuffix)])
- }
- func setupLogging(config *Config) error {
- if config.LogFile == "" {
- return nil
- }
- if err := createDirs(path.Dir(config.LogFile)); err != nil {
- return err
- }
- output, err := os.OpenFile(config.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
- if err != nil {
- return err
- }
- syscall.Dup3(int(output.Fd()), int(os.Stdout.Fd()), 0)
- syscall.Dup3(int(output.Fd()), int(os.Stderr.Fd()), 0)
- return nil
- }
- func setUlimit(cfg *Config) error {
- var rLimit syscall.Rlimit
- if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
- return err
- }
- if cfg.NoFiles == 0 {
- rLimit.Max = 1000000
- } else {
- rLimit.Max = cfg.NoFiles
- }
- rLimit.Cur = rLimit.Max
- return syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
- }
- func runOrExec(config *Config, docker string, args ...string) (*exec.Cmd, error) {
- if err := secondPrepare(config, docker, args...); err != nil {
- return nil, err
- }
- cmd := "docker"
- if config != nil && config.CommandName != "" {
- cmd = config.CommandName
- }
- return execDocker(config, docker, cmd, args)
- }
- func LaunchDocker(config *Config, docker string, args ...string) (*exec.Cmd, error) {
- if err := PrepareFs(config); err != nil {
- return nil, err
- }
- return runOrExec(config, docker, args...)
- }
- func Main() {
- if os.Getenv("DOCKER_LAUNCH_DEBUG") == "true" {
- log.SetLevel(log.DebugLevel)
- }
- if len(os.Args) < 2 {
- log.Fatalf("Usage Example: %s /usr/bin/docker -d -D", os.Args[0])
- }
- args := []string{}
- if len(os.Args) > 1 {
- args = os.Args[2:]
- }
- var config Config
- args = ParseConfig(&config, args...)
- if os.Getenv("DOCKER_LAUNCH_REAP") == "true" {
- config.Fork = true
- config.PidOne = true
- }
- log.Debugf("Launch config %#v", config)
- _, err := LaunchDocker(&config, os.Args[1], args...)
- if err != nil {
- log.Fatal(err)
- }
- }
|