The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/distrobuilder/pull/5

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
From f89cebdc882a1d62a0614c02e5505ff5c80af31b Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@canonical.com>
Date: Thu, 8 Feb 2018 15:38:53 +0100
Subject: [PATCH] distrobuilder: fix and restructure chroot code

Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
---
 distrobuilder/chroot.go | 138 +++++++++++++++++++++++++++++++-----------------
 distrobuilder/main.go   |  14 +++--
 2 files changed, 100 insertions(+), 52 deletions(-)

diff --git a/distrobuilder/chroot.go b/distrobuilder/chroot.go
index 02d11ec..f0be5b7 100644
--- a/distrobuilder/chroot.go
+++ b/distrobuilder/chroot.go
@@ -4,80 +4,124 @@ import (
        "fmt"
        "os"
        "path/filepath"
+       "regexp"
+       "strconv"
        "syscall"
 
        "github.com/lxc/distrobuilder/managers"
        "github.com/lxc/distrobuilder/shared"
 )
 
-func prepareChroot(cacheDir string) ([]string, error) {
-       type Mount struct {
-               source string
-               target string
-               fstype string
-               flags  uintptr
-               data   string
+type chrootMount struct {
+       source  string
+       target  string
+       fstype  string
+       flags   uintptr
+       data    string
+       remove  bool
+       mounted bool
+       isDir   bool
+}
+
+func mountFilesystems(rootfs string, mounts []chrootMount) bool {
+       ok := true
+
+       for i, mount := range mounts {
+               if mount.isDir {
+                       os.MkdirAll(filepath.Join(rootfs, mount.target), 0755)
+               } else {
+                       os.Create(filepath.Join(rootfs, mount.target))
+               }
+               err := syscall.Mount(mount.source, filepath.Join(rootfs, 
mount.target),
+                       mount.fstype, mount.flags, mount.data)
+               if err != nil {
+                       fmt.Fprintf(os.Stderr, "Failed to mount '%s': %s\n", 
mount.source, err)
+                       ok = false
+                       break
+               }
+               mounts[i].mounted = true
        }
 
-       var (
-               err      error
-               unmounts []string
-       )
+       return ok
+}
 
-       mounts := []Mount{
-               {filepath.Join(cacheDir, "rootfs"), "", "tmpfs", 
syscall.MS_BIND, ""},
-               {"proc", "proc", "proc", 0, ""},
-               {"sys", "sys", "sysfs", 0, ""},
-               {"udev", "dev", "devtmpfs", 0, ""},
-               {"shm", "/dev/shm", "tmpfs", 0, ""},
-               {"/dev/pts", "/dev/pts", "tmpfs", syscall.MS_BIND, ""},
-               {"run", "/run", "tmpfs", 0, ""},
-               {"tmp", "/tmp", "tmpfs", 0, ""},
+func unmountFilesystems(rootfs string, mounts []chrootMount) {
+       // unmount targets in reversed order
+       for i := len(mounts) - 1; i >= 0; i-- {
+               if mounts[i].mounted {
+                       err := syscall.Unmount(filepath.Join(rootfs, 
mounts[i].target), 0)
+                       if err != nil {
+                               fmt.Fprintf(os.Stderr, "Failed to unmount '%s': 
%s\n", mounts[i].target, err)
+                               continue
+                       }
+                       mounts[i].mounted = false
+
+                       if mounts[i].remove {
+                               os.RemoveAll(filepath.Join(rootfs, 
mounts[i].target))
+                       }
+               }
        }
+}
 
-       os.MkdirAll(filepath.Join(cacheDir, "rootfs", "dev", "pts"), 0755)
+func killChrootProcesses(rootfs string) error {
+       proc, err := os.Open(filepath.Join(rootfs, "proc"))
+       if err != nil {
+               return err
+       }
 
-       for _, mount := range mounts {
-               err = syscall.Mount(mount.source, filepath.Join(cacheDir, 
"rootfs", mount.target),
-                       mount.fstype, mount.flags, mount.data)
-               if err != nil {
-                       return unmounts, err
+       dirs, err := proc.Readdirnames(0)
+       if err != nil {
+               return err
+       }
+
+       for _, dir := range dirs {
+               match, _ := regexp.MatchString(`\d+`, dir)
+               if match {
+                       link, _ := os.Readlink(filepath.Join(rootfs, "proc", 
dir, "root"))
+                       if link == rootfs {
+                               pid, _ := strconv.Atoi(dir)
+                               syscall.Kill(pid, syscall.SIGKILL)
+                       }
                }
-               unmounts = append(unmounts, filepath.Join(cacheDir, "rootfs", 
mount.target))
        }
 
-       return unmounts, nil
+       return nil
 }
 
-func setupChroot(cacheDir string) (func() error, error) {
+func setupChroot(rootfs string) (func() error, error) {
        var err error
 
-       cwd, err := os.Getwd()
-       if err != nil {
-               return nil, err
+       mounts := []chrootMount{
+               {rootfs, "", "tmpfs", syscall.MS_BIND, "", false, false, true},
+               {"proc", "proc", "proc", 0, "", false, false, true},
+               {"sys", "sys", "sysfs", 0, "", false, false, true},
+               {"udev", "dev", "devtmpfs", 0, "", false, false, true},
+               {"shm", "/dev/shm", "tmpfs", 0, "", true, false, true},
+               {"/dev/pts", "/dev/pts", "tmpfs", syscall.MS_BIND, "", true, 
false, true},
+               {"run", "/run", "tmpfs", 0, "", false, false, true},
+               {"tmp", "/tmp", "tmpfs", 0, "", false, false, true},
+               {"/etc/resolv.conf", "/etc/resolv.conf", "", syscall.MS_BIND, 
"", false, false, false},
        }
 
-       err = shared.Copy("/etc/resolv.conf", filepath.Join(cacheDir, "rootfs", 
"etc", "resolv.conf"))
+       cwd, err := os.Getwd()
        if err != nil {
                return nil, err
        }
 
-       unmounts, err := prepareChroot(cacheDir)
-       if err != nil {
-               for i := len(unmounts) - 1; i >= 0; i-- {
-                       syscall.Unmount(unmounts[i], 0)
-               }
-               return nil, err
+       ok := mountFilesystems(rootfs, mounts)
+       if !ok {
+               unmountFilesystems(rootfs, mounts)
+               return nil, fmt.Errorf("Failed to mount filesystems")
        }
 
-       syscall.Mknod(filepath.Join(cacheDir, "rootfs", "dev", "null"), 1, 3)
+       syscall.Mknod(filepath.Join(rootfs, "dev", "null"), 1, 3)
 
        root, err := os.Open("/")
        if err != nil {
                return nil, err
        }
 
-       err = syscall.Chroot(filepath.Join(cacheDir, "rootfs"))
+       err = syscall.Chroot(rootfs)
        if err != nil {
                root.Close()
                return nil, err
@@ -89,11 +133,6 @@ func setupChroot(cacheDir string) (func() error, error) {
        }
 
        return func() error {
-               // unmount targets in reversed order
-               for i := len(unmounts) - 1; i >= 0; i-- {
-                       syscall.Unmount(unmounts[i], 0)
-               }
-
                defer root.Close()
 
                err = root.Chdir()
@@ -111,11 +150,16 @@ func setupChroot(cacheDir string) (func() error, error) {
                        return err
                }
 
+               // This will kill all processes in the chroot and allow to 
cleanly
+               // unmount everything.
+               killChrootProcesses(rootfs)
+               unmountFilesystems(rootfs, mounts)
+
                return nil
        }, nil
 }
 
-func manageChroot(def shared.DefinitionPackages) error {
+func managePackages(def shared.DefinitionPackages) error {
        var err error
 
        manager := managers.Get(def.Manager)
diff --git a/distrobuilder/main.go b/distrobuilder/main.go
index c8187a4..f9fa2f4 100644
--- a/distrobuilder/main.go
+++ b/distrobuilder/main.go
@@ -52,6 +52,7 @@ import (
        "fmt"
        "io"
        "os"
+       "path/filepath"
 
        "github.com/lxc/distrobuilder/shared"
        "github.com/lxc/distrobuilder/sources"
@@ -151,17 +152,20 @@ func run(c *cli.Context) error {
                defer os.RemoveAll(c.GlobalString("cache-dir"))
        }
 
-       exitFunc, err := setupChroot(c.GlobalString("cache-dir"))
+       // enter chroot
+       exitChroot, err := 
setupChroot(filepath.Join(c.GlobalString("cache-dir"), "rootfs"))
        if err != nil {
-               return fmt.Errorf("Failed to chroot: %s", err)
+               return fmt.Errorf("Failed to setup chroot: %s", err)
        }
-       defer exitFunc()
 
-       err = manageChroot(def.Packages)
+       err = managePackages(def.Packages)
        if err != nil {
-               return fmt.Errorf("Failed to run setup: %s", err)
+               exitChroot()
+               return fmt.Errorf("Failed to manage packages: %s", err)
        }
 
+       exitChroot()
+
        return nil
 }
 
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to