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