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 }