github.com/shaardie/u-root@v4.0.1-0.20190127173353-f24a1c26aa2e+incompatible/cmds/installcommand/installcommand.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  package main
     6  
     7  // Install command from a go source file.
     8  //
     9  // Synopsis:
    10  //     SYMLINK [ARGS...]
    11  //     installcommand [INSTALLCOMMAND_ARGS...] COMMAND [ARGS...]
    12  //
    13  // Description:
    14  //     u-root commands are lazily compiled. Uncompiled commands in the /bin
    15  //     directory are symbolic links to installcommand. When executed through
    16  //     the symbolic link, installcommand will build the command from source and
    17  //     exec it.
    18  //
    19  //     The second form allows commands to be installed and exec'ed without a
    20  //     symbolic link. In this form additional arguments such as `-v` and
    21  //     `-ludicrous` can be passed into installcommand.
    22  //
    23  // Options:
    24  //     -lowpri:    the scheduler priority to lowered before starting
    25  //     -exec:      build and exec the command
    26  //     -force:     do not build if a file already exists at the destination
    27  //     -v:         print all build commands
    28  import (
    29  	"flag"
    30  	"fmt"
    31  	"log"
    32  	"os"
    33  	"os/exec"
    34  	"path/filepath"
    35  	"strings"
    36  	"syscall"
    37  
    38  	"github.com/u-root/u-root/pkg/golang"
    39  	"github.com/u-root/u-root/pkg/uroot/util"
    40  )
    41  
    42  var (
    43  	lowpri = flag.Bool("lowpri", false, "the scheduler priority is lowered before starting")
    44  	exe    = flag.Bool("exec", true, "build AND execute the command")
    45  	force  = flag.Bool("force", false, "build even if a file already exists at the destination")
    46  
    47  	verbose = flag.Bool("v", false, "print all build commands")
    48  	debug   = func(string, ...interface{}) {}
    49  	r = util.UrootPath
    50  )
    51  
    52  type form struct {
    53  	// Name of the command, ex: "ls"
    54  	cmdName string
    55  	// Args passed to the command, ex: {"-l", "-R"}
    56  	cmdArgs []string
    57  
    58  	// Args intended for installcommand
    59  	lowPri  bool
    60  	exec    bool
    61  	force   bool
    62  	verbose bool
    63  }
    64  
    65  func usage() {
    66  	fmt.Fprintf(os.Stderr, "Usage: installcommand [INSTALLCOMMAND_ARGS...] COMMAND [ARGS...]\n")
    67  	os.Exit(2)
    68  }
    69  
    70  // Parse the command line to determine the form.
    71  func parseCommandLine() form {
    72  	// First form:
    73  	//     SYMLINK [ARGS...]
    74  	if !strings.HasSuffix(os.Args[0], "installcommand") {
    75  		return form{
    76  			cmdName: filepath.Base(os.Args[0]),
    77  			cmdArgs: os.Args[1:],
    78  			lowPri:  *lowpri,
    79  			exec:    *exe,
    80  			force:   *force,
    81  			verbose: *verbose,
    82  		}
    83  	}
    84  
    85  	// Second form:
    86  	//     installcommand [INSTALLCOMMAND_ARGS...] COMMAND [ARGS...]
    87  	flag.Parse()
    88  	if flag.NArg() < 1 {
    89  		log.Println("Second form requires a COMMAND argument")
    90  		usage()
    91  	}
    92  	return form{
    93  		cmdName: flag.Arg(0),
    94  		cmdArgs: flag.Args()[1:],
    95  		lowPri:  *lowpri,
    96  		exec:    *exe,
    97  		force:   *force,
    98  		verbose: *verbose,
    99  	}
   100  }
   101  
   102  // run runs the command with the information from form.
   103  // Since run can potentially never return, since it can use Exec,
   104  // it should never return in any other case. Hence, if all goes well
   105  // at the end, we os.Exit(0)
   106  func run(n string, form form) {
   107  	cmd := exec.Command(n, form.cmdArgs...)
   108  	cmd.Stdin = os.Stdin
   109  	cmd.Stderr = os.Stderr
   110  	cmd.Stdout = os.Stdout
   111  	if err := cmd.Run(); err != nil {
   112  		exitErr, ok := err.(*exec.ExitError)
   113  		if !ok {
   114  			log.Fatal(err)
   115  		}
   116  		exitWithStatus(exitErr)
   117  	}
   118  	os.Exit(0)
   119  }
   120  
   121  func main() {
   122  	form := parseCommandLine()
   123  
   124  	if form.lowPri {
   125  		if err := syscall.Setpriority(syscall.PRIO_PROCESS, 0, 20); err != nil {
   126  			log.Printf("Cannot set low priority: %v", err)
   127  		}
   128  	}
   129  
   130  	if form.verbose {
   131  		debug = log.Printf
   132  	}
   133  
   134  	debug("Command name: %v\n", form.cmdName)
   135  	destFile := filepath.Join(r("/ubin"), form.cmdName)
   136  
   137  	// Is the command there? This covers a race condition
   138  	// in that some other process may have caused it to be
   139  	// built.
   140  	if _, err := os.Stat(destFile); err == nil {
   141  		if !form.exec {
   142  			os.Exit(0)
   143  		}
   144  		run(destFile, form)
   145  	}
   146  
   147  	env := golang.Default()
   148  	env.Context.GOROOT = r("/go")
   149  	env.Context.GOPATH = r("/")
   150  
   151  	var srcDir string
   152  	err := filepath.Walk(r("/src"), func(p string, fi os.FileInfo, err error) error {
   153  		if err != nil {
   154  			return nil
   155  		}
   156  
   157  		if fi.IsDir() && filepath.Base(p) == form.cmdName {
   158  			// Make sure it's an actual Go command.
   159  			pkg, err := env.PackageByPath(p)
   160  			if err == nil && pkg.IsCommand() {
   161  				srcDir = p
   162  			}
   163  		}
   164  		return nil
   165  	})
   166  	if err != nil {
   167  		log.Fatal(err)
   168  	}
   169  	if len(srcDir) == 0 {
   170  		log.Fatalf("Can not find source code for %q", form.cmdName)
   171  	}
   172  
   173  	if err := env.BuildDir(srcDir, destFile, golang.BuildOpts{}); err != nil {
   174  		log.Fatalf("Couldn't compile %q: %v", form.cmdName, err)
   175  	}
   176  
   177  	if form.exec {
   178  		run(destFile, form)
   179  	}
   180  }