github.com/system-transparency/u-root@v6.0.1-0.20190919065413-ed07a650de4c+incompatible/cmds/core/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 // This is almost certain to be a symlink, and it's no harm 76 // to check it. 77 f := util.ResolveUntilLastSymlink(os.Args[0]) 78 return form{ 79 cmdName: filepath.Base(f), 80 cmdArgs: os.Args[1:], 81 lowPri: *lowpri, 82 exec: *exe, 83 force: *force, 84 verbose: *verbose, 85 } 86 } 87 88 // Second form: 89 // installcommand [INSTALLCOMMAND_ARGS...] COMMAND [ARGS...] 90 flag.Parse() 91 if flag.NArg() < 1 { 92 log.Println("Second form requires a COMMAND argument") 93 usage() 94 } 95 return form{ 96 cmdName: flag.Arg(0), 97 cmdArgs: flag.Args()[1:], 98 lowPri: *lowpri, 99 exec: *exe, 100 force: *force, 101 verbose: *verbose, 102 } 103 } 104 105 // run runs the command with the information from form. 106 // Since run can potentially never return, since it can use Exec, 107 // it should never return in any other case. Hence, if all goes well 108 // at the end, we os.Exit(0) 109 func run(n string, form form) { 110 cmd := exec.Command(n, form.cmdArgs...) 111 cmd.Stdin = os.Stdin 112 cmd.Stderr = os.Stderr 113 cmd.Stdout = os.Stdout 114 if err := cmd.Run(); err != nil { 115 exitErr, ok := err.(*exec.ExitError) 116 if !ok { 117 log.Fatal(err) 118 } 119 exitWithStatus(exitErr) 120 } 121 os.Exit(0) 122 } 123 124 func main() { 125 form := parseCommandLine() 126 127 if form.lowPri { 128 if err := syscall.Setpriority(syscall.PRIO_PROCESS, 0, 20); err != nil { 129 log.Printf("Cannot set low priority: %v", err) 130 } 131 } 132 133 if form.verbose { 134 debug = log.Printf 135 } 136 137 destFile := filepath.Join(r("/ubin"), form.cmdName) 138 139 // Is the command there? This covers a race condition 140 // in that some other process may have caused it to be 141 // built. 142 if _, err := os.Stat(destFile); err == nil { 143 if !form.exec { 144 os.Exit(0) 145 } 146 run(destFile, form) 147 } 148 149 env := golang.Default() 150 env.Context.GOROOT = r("/go") 151 env.Context.GOPATH = r("/") 152 env.Context.CgoEnabled = false 153 154 var srcDir string 155 err := filepath.Walk(r("/src"), func(p string, fi os.FileInfo, err error) error { 156 if err != nil { 157 return nil 158 } 159 160 if fi.IsDir() && filepath.Base(p) == form.cmdName { 161 // Make sure it's an actual Go command. 162 pkg, err := env.PackageByPath(p) 163 if err == nil && pkg.IsCommand() { 164 srcDir = p 165 } 166 } 167 return nil 168 }) 169 if err != nil { 170 log.Fatal(err) 171 } 172 if len(srcDir) == 0 { 173 log.Fatalf("Can not find source code for %q", form.cmdName) 174 } 175 176 if err := env.BuildDir(srcDir, destFile, golang.BuildOpts{}); err != nil { 177 log.Fatalf("Couldn't compile %q: %v", form.cmdName, err) 178 } 179 180 if form.exec { 181 run(destFile, form) 182 } 183 }