github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/tools/testramfs/testramfs.go (about)

     1  // Copyright 2012-2017 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // testramfs tests things, badly
     6  package main
     7  
     8  import (
     9  	"io"
    10  	"log"
    11  	"os"
    12  	"syscall"
    13  
    14  	flag "github.com/spf13/pflag"
    15  	"golang.org/x/sys/unix"
    16  
    17  	"github.com/mvdan/u-root-coreutils/pkg/cpio"
    18  	"github.com/mvdan/u-root-coreutils/pkg/pty"
    19  	"github.com/mvdan/u-root-coreutils/pkg/termios"
    20  )
    21  
    22  const (
    23  	unshareFlags = syscall.CLONE_NEWNS
    24  	cloneFlags   = syscall.CLONE_NEWIPC |
    25  		syscall.CLONE_NEWNET |
    26  		// making newpid work will be more tricky,
    27  		// since none of my CLs to fix go runtime for
    28  		// it ever got in.
    29  		// syscall.CLONE_NEWPID |
    30  		syscall.CLONE_NEWUTS |
    31  		0
    32  )
    33  
    34  var (
    35  	noremove    = flag.BoolP("noremove", "n", false, "remove tempdir when done")
    36  	interactive = flag.BoolP("interactive", "i", false, "interactive mode")
    37  )
    38  
    39  func main() {
    40  	flag.Parse()
    41  
    42  	if flag.NArg() != 1 {
    43  		log.Fatalf("usage: %s <cpio-path>", os.Args[0])
    44  	}
    45  
    46  	c := flag.Args()[0]
    47  
    48  	f, err := os.Open(c)
    49  	if err != nil {
    50  		log.Fatal(err)
    51  	}
    52  
    53  	// So, what's the plan here?
    54  	//
    55  	// - new mount namespace
    56  	//   - root mount is a tmpfs mount filled with the archive.
    57  	//
    58  	// - new PID namespace
    59  	//   - archive/init actually runs as PID 1.
    60  
    61  	// Note this is basically a chroot and umask is inherited.
    62  	// The umask has to be zero else some creation will end
    63  	// up with incorrect permissions, a particular problem
    64  	// in device creation.
    65  	u := unix.Umask(0)
    66  	defer unix.Umask(u)
    67  
    68  	tempDir, err := os.MkdirTemp("", "u-root")
    69  	if err != nil {
    70  		log.Fatal(err)
    71  	}
    72  	// Don't do a RemoveAll. This should be empty and
    73  	// an error can tell us we got something wrong.
    74  	if !*noremove {
    75  		defer func(n string) {
    76  			log.Printf("Removing %v", n)
    77  			if err := os.Remove(n); err != nil {
    78  				log.Fatal(err)
    79  			}
    80  		}(tempDir)
    81  	}
    82  	if err := syscall.Mount("testramfs.tmpfs", tempDir, "tmpfs", 0, ""); err != nil {
    83  		log.Fatal(err)
    84  	}
    85  	if !*noremove {
    86  		defer func(n string) {
    87  			log.Printf("Unmounting %v", n)
    88  			if err := syscall.Unmount(n, syscall.MNT_DETACH); err != nil {
    89  				log.Fatal(err)
    90  			}
    91  		}(tempDir)
    92  	}
    93  
    94  	archiver, err := cpio.Format("newc")
    95  	if err != nil {
    96  		log.Fatal(err)
    97  	}
    98  
    99  	r := archiver.Reader(f)
   100  	for {
   101  		rec, err := r.ReadRecord()
   102  		if err == io.EOF {
   103  			break
   104  		}
   105  		if err != nil {
   106  			log.Fatal(err)
   107  		}
   108  		cpio.CreateFileInRoot(rec, tempDir, false)
   109  	}
   110  
   111  	cmd, err := pty.New()
   112  	if err != nil {
   113  		log.Fatal(err)
   114  	}
   115  	cmd.Command("/init")
   116  	cmd.C.SysProcAttr.Chroot = tempDir
   117  	cmd.C.SysProcAttr.Cloneflags = cloneFlags
   118  	cmd.C.SysProcAttr.Unshareflags = unshareFlags
   119  	if *interactive {
   120  		t, err := termios.GetTermios(0)
   121  		if err != nil {
   122  			log.Fatal("Getting Termios")
   123  		}
   124  		defer func(t *termios.Termios) {
   125  			if err := termios.SetTermios(0, t); err != nil {
   126  				log.Print(err)
   127  			}
   128  		}(t)
   129  		if err := cmd.Run(); err != nil {
   130  			log.Fatal(err)
   131  		}
   132  		return
   133  	}
   134  	if err := cmd.Start(); err != nil {
   135  		log.Fatal(err)
   136  	}
   137  	go io.Copy(cmd.TTY, cmd.Ptm)
   138  
   139  	// At this point you could use an array of commands/output templates to
   140  	// drive the test, and end with the exit command shown nere.
   141  	for _, c := range []string{"date\n", "exit\n", "exit\n", "exit\n"} {
   142  		if _, err := cmd.Ptm.Write([]byte(c)); err != nil {
   143  			log.Printf("Writing %s: %v", c, err)
   144  		}
   145  	}
   146  	if err := cmd.Wait(); err != nil {
   147  		log.Fatal(err)
   148  	}
   149  }