github.com/Cloud-Foundations/Dominator@v0.3.4/imagebuilder/builder/target.go (about)

     1  package builder
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"sort"
    10  	"strings"
    11  	"syscall"
    12  
    13  	"github.com/Cloud-Foundations/Dominator/lib/goroutine"
    14  	"github.com/Cloud-Foundations/Dominator/lib/wsyscall"
    15  )
    16  
    17  // newNamespaceTarget will create a goroutine which is locked to an OS
    18  // thread with a separate mount namespace.
    19  func newNamespaceTarget() (*goroutine.Goroutine, error) {
    20  	g := goroutine.New()
    21  	var err error
    22  	g.Run(func() { err = wsyscall.UnshareMountNamespace() })
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  	return g, nil
    27  }
    28  
    29  // newNamespaceTargetWithMounts will create a goroutine which is locked to an OS
    30  // thread with a separate mount namespace. The directories specified by
    31  // bindMounts will be mounted into the new namespace.
    32  func newNamespaceTargetWithMounts(rootDir string, bindMounts []string) (
    33  	*goroutine.Goroutine, error) {
    34  	g, err := newNamespaceTarget()
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	g.Run(func() { err = setupMounts(rootDir, bindMounts) })
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  	return g, nil
    43  }
    44  
    45  // Run a command in the target root directory in the specified namespace and
    46  // with a new PID namespace.
    47  func runInTarget(g *goroutine.Goroutine, input io.Reader, output io.Writer,
    48  	rootDir string, envGetter environmentGetter,
    49  	prog string, args ...string) error {
    50  	var environmentToInject map[string]string
    51  	if envGetter != nil {
    52  		environmentToInject = envGetter.getenv()
    53  	}
    54  	cmd := exec.Command(prog, args...)
    55  	cmd.Env = stripVariables(os.Environ(), environmentToCopy, environmentToSet,
    56  		environmentToInject)
    57  	cmd.Dir = "/"
    58  	cmd.Stdin = input
    59  	cmd.Stdout = output
    60  	cmd.Stderr = output
    61  	cmd.SysProcAttr = &syscall.SysProcAttr{
    62  		Chroot:     rootDir,
    63  		Setsid:     true,
    64  		Cloneflags: syscall.CLONE_NEWPID,
    65  	}
    66  	var err error
    67  	g.Run(func() { err = cmd.Start() })
    68  	if err != nil {
    69  		return err
    70  	}
    71  	return cmd.Wait()
    72  }
    73  
    74  // setupMounts will mutate the current namespace.
    75  func setupMounts(rootDir string, bindMounts []string) error {
    76  	err := wsyscall.Mount("none", filepath.Join(rootDir, "proc"), "proc", 0, "")
    77  	if err != nil {
    78  		return err
    79  	}
    80  	err = wsyscall.Mount("none", filepath.Join(rootDir, "sys"), "sysfs", 0, "")
    81  	if err != nil {
    82  		return err
    83  	}
    84  	for _, bindMount := range bindMounts {
    85  		err := wsyscall.Mount(bindMount,
    86  			filepath.Join(rootDir, bindMount), "",
    87  			wsyscall.MS_BIND|wsyscall.MS_RDONLY, "")
    88  		if err != nil {
    89  			return fmt.Errorf("error bind mounting: %s: %s", bindMount, err)
    90  		}
    91  	}
    92  	return nil
    93  }
    94  
    95  func stripVariables(input []string, varsToCopy map[string]struct{},
    96  	varsToSet ...map[string]string) []string {
    97  	output := make([]string, 0)
    98  	for _, nameValue := range input {
    99  		split := strings.SplitN(nameValue, "=", 2)
   100  		if len(split) == 2 {
   101  			if _, ok := varsToCopy[split[0]]; ok {
   102  				output = append(output, nameValue)
   103  			}
   104  		}
   105  	}
   106  	for _, varTable := range varsToSet {
   107  		for name, value := range varTable {
   108  			output = append(output, name+"="+value)
   109  		}
   110  	}
   111  	sort.Strings(output)
   112  	return output
   113  }