github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/cmds/exp/pox/pox.go (about)

     1  // Copyright 2012-2018 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  // pox builds a portable executable as a squashfs image.
     6  // It is intended to create files compatible with tinycore
     7  // tcz files. One of more of the files can be programs
     8  // but that is not required.
     9  // This could have been a simple program but mksquashfs does not
    10  // preserve path information.
    11  // Yeah.
    12  //
    13  // Synopsis:
    14  //     pox [-[-debug]|d] -[-run|r file] [-[-create]|c]  [-[-file]|f tcz-file] file [...file]
    15  //
    16  // Description:
    17  //     pox makes portable executables in squashfs format compatible with
    18  //     tcz format. We don't build in the execution code, rather, we set it
    19  //     up so we can use the command itself. You can create, create and run a command,
    20  //     mount a pox, or mount a pox and run a command in it.
    21  //
    22  // Options:
    23  //     debug|d: verbose
    24  //     file|f file: file name (default /tmp/pox.tcz)
    25  //     run|r: run a file by loopback mounting the squashfs and using the first arg as a command to run in a chroot
    26  //     create|c: create the file.
    27  //     both -c and -r can be used on the same command.
    28  //
    29  // Example:
    30  //	pox -d -r /bin/bash /bin/cat /bin/ls /etc/hosts
    31  //	Will build a squashfs, mount it, and drop you into it running bash.
    32  //	You can use ls and cat on /etc/hosts.
    33  //	Simpler example:
    34  //	pox -d -r /bin/ls /etc/hosts
    35  //	will run ls and exit.
    36  package main
    37  
    38  import (
    39  	"fmt"
    40  	"io"
    41  	"io/ioutil"
    42  	"log"
    43  	"os"
    44  	"os/exec"
    45  	"path/filepath"
    46  	"syscall"
    47  
    48  	flag "github.com/spf13/pflag"
    49  	"github.com/u-root/u-root/pkg/ldd"
    50  	"github.com/u-root/u-root/pkg/loop"
    51  )
    52  
    53  const usage = "pox [-[-debug]|d] -[-run|r file] [-[-create]|c]  [-[-file]|f tcz-file] file [...file]"
    54  
    55  var (
    56  	debug  = flag.BoolP("debug", "d", false, "enable debug prints")
    57  	run    = flag.BoolP("run", "r", false, "run a test with the first argument")
    58  	create = flag.BoolP("create", "c", true, "create it")
    59  	file   = flag.StringP("output", "f", "/tmp/pox.tcz", "Output file")
    60  	v      = func(string, ...interface{}) {}
    61  )
    62  
    63  func pox() error {
    64  	flag.Parse()
    65  	if *debug {
    66  		v = log.Printf
    67  	}
    68  	names := flag.Args()
    69  	if len(names) == 0 {
    70  		return fmt.Errorf(usage)
    71  	}
    72  
    73  	if *create {
    74  		l, err := ldd.Ldd(names)
    75  		if err != nil {
    76  			return fmt.Errorf("Running ldd on %v: %v", names, err)
    77  		}
    78  
    79  		for _, dep := range l {
    80  			v("%s", dep.FullName)
    81  			names = append(names, dep.FullName)
    82  		}
    83  		// Now we need to make a template file hierarchy and put
    84  		// the stuff we want in there.
    85  		dir, err := ioutil.TempDir("", "pox")
    86  		if err != nil {
    87  			return err
    88  		}
    89  		if !*debug {
    90  			defer os.RemoveAll(dir)
    91  		}
    92  		// We don't use defer() here to close files as
    93  		// that can cause open failures with a large enough number.
    94  		for _, f := range names {
    95  			v("Process %v", f)
    96  			fi, err := os.Stat(f)
    97  			if err != nil {
    98  				return err
    99  			}
   100  			in, err := os.Open(f)
   101  			if err != nil {
   102  				return err
   103  			}
   104  			dfile := filepath.Join(dir, f)
   105  			d := filepath.Dir(dfile)
   106  			if err := os.MkdirAll(d, 0755); err != nil {
   107  				in.Close()
   108  				return err
   109  			}
   110  			out, err := os.OpenFile(dfile, os.O_WRONLY|os.O_CREATE, fi.Mode().Perm())
   111  			if err != nil {
   112  				in.Close()
   113  				return err
   114  			}
   115  			_, err = io.Copy(out, in)
   116  			in.Close()
   117  			out.Close()
   118  			if err != nil {
   119  				return err
   120  			}
   121  
   122  		}
   123  		c := exec.Command("mksquashfs", dir, *file, "-noappend")
   124  		o, err := c.CombinedOutput()
   125  		v("%v", string(o))
   126  		if err != nil {
   127  			return fmt.Errorf("%v: %v: %v", c.Args, string(o), err)
   128  		}
   129  	}
   130  
   131  	if !*run {
   132  		return nil
   133  	}
   134  	dir, err := ioutil.TempDir("", "pox")
   135  	if err != nil {
   136  		return err
   137  	}
   138  	if !*debug {
   139  		defer os.RemoveAll(dir)
   140  	}
   141  	m, err := loop.New(*file, "squashfs", "")
   142  	if err != nil {
   143  		return err
   144  	}
   145  	if err := m.Mount(dir, 0); err != nil {
   146  		return err
   147  	}
   148  	c := exec.Command(names[0])
   149  	c.Stdin, c.Stdout, c.Stderr = os.Stdin, os.Stdout, os.Stderr
   150  	c.SysProcAttr = &syscall.SysProcAttr{
   151  		Chroot: dir,
   152  	}
   153  
   154  	err = c.Run()
   155  	if err != nil {
   156  		log.Printf("Running test: %v", err)
   157  	}
   158  	if err := m.Unmount(0); err != nil {
   159  		v("Unmounting and freeing %v: %v", m, err)
   160  	}
   161  
   162  	v("Done, your pox is in %v", *file)
   163  	return err
   164  }
   165  
   166  func main() {
   167  	if err := pox(); err != nil {
   168  		log.Fatal(err)
   169  	}
   170  }