github.com/insomniacslk/u-root@v0.0.0-20200717035308-96b791510d76/u-root.go (about) 1 // Copyright 2015-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 package main 6 7 import ( 8 "flag" 9 "fmt" 10 "io/ioutil" 11 "log" 12 "os" 13 "runtime" 14 "strings" 15 16 "github.com/u-root/u-root/pkg/golang" 17 "github.com/u-root/u-root/pkg/shlex" 18 "github.com/u-root/u-root/pkg/uroot" 19 "github.com/u-root/u-root/pkg/uroot/builder" 20 "github.com/u-root/u-root/pkg/uroot/initramfs" 21 ) 22 23 // multiFlag is used for flags that support multiple invocations, e.g. -files 24 type multiFlag []string 25 26 func (m *multiFlag) String() string { 27 return fmt.Sprint(*m) 28 } 29 30 func (m *multiFlag) Set(value string) error { 31 *m = append(*m, value) 32 return nil 33 } 34 35 // Flags for u-root builder. 36 var ( 37 build, format, tmpDir, base, outputPath *string 38 uinitCmd, initCmd *string 39 defaultShell *string 40 useExistingInit *bool 41 fourbins *bool 42 noCommands *bool 43 extraFiles multiFlag 44 ) 45 46 func init() { 47 fourbins = flag.Bool("fourbins", false, "build installcommand on boot, no ahead of time, so we have only four binares") 48 build = flag.String("build", "bb", "u-root build format (e.g. bb or source).") 49 format = flag.String("format", "cpio", "Archival format.") 50 51 tmpDir = flag.String("tmpdir", "", "Temporary directory to put binaries in.") 52 53 base = flag.String("base", "", "Base archive to add files to. By default, this is a couple of directories like /bin, /etc, etc. u-root has a default internally supplied set of files; use base=/dev/null if you don't want any base files.") 54 useExistingInit = flag.Bool("useinit", false, "Use existing init from base archive (only if --base was specified).") 55 outputPath = flag.String("o", "", "Path to output initramfs file.") 56 57 initCmd = flag.String("initcmd", "init", "Symlink target for /init. Can be an absolute path or a u-root command name. Use initcmd=\"\" if you don't want the symlink.") 58 uinitCmd = flag.String("uinitcmd", "", "Symlink target and arguments for /bin/uinit. Can be an absolute path or a u-root command name. Use uinitcmd=\"\" if you don't want the symlink. E.g. -uinitcmd=\"echo foobar\"") 59 defaultShell = flag.String("defaultsh", "elvish", "Default shell. Can be an absolute path or a u-root command name. Use defaultsh=\"\" if you don't want the symlink.") 60 61 noCommands = flag.Bool("nocmd", false, "Build no Go commands; initramfs only") 62 63 flag.Var(&extraFiles, "files", "Additional files, directories, and binaries (with their ldd dependencies) to add to archive. Can be speficified multiple times.") 64 } 65 66 func main() { 67 flag.Parse() 68 69 // Main is in a separate functions so defers run on return. 70 if err := Main(); err != nil { 71 log.Fatal(err) 72 } 73 log.Printf("Successfully wrote initramfs.") 74 } 75 76 var recommendedVersions = []string{ 77 "go1.13", 78 "go1.14", 79 } 80 81 func isRecommendedVersion(v string) bool { 82 for _, r := range recommendedVersions { 83 if strings.HasPrefix(v, r) { 84 return true 85 } 86 } 87 return false 88 } 89 90 // Main is a separate function so defers are run on return, which they wouldn't 91 // on exit. 92 func Main() error { 93 env := golang.Default() 94 if *fourbins && env.GOROOT == "" { 95 log.Fatalf("You have to set GOROOT for fourbins to work") 96 } 97 if env.CgoEnabled { 98 log.Printf("Disabling CGO for u-root...") 99 env.CgoEnabled = false 100 } 101 log.Printf("Build environment: %s", env) 102 if env.GOOS != "linux" { 103 log.Printf("GOOS is not linux. Did you mean to set GOOS=linux?") 104 } 105 106 v, err := env.Version() 107 if err != nil { 108 log.Printf("Could not get environment's Go version, using runtime's version: %v", err) 109 v = runtime.Version() 110 } 111 if !isRecommendedVersion(v) { 112 log.Printf(`WARNING: You are not using one of the recommended Go versions (have = %s, recommended = %v). 113 Some packages may not compile. 114 Go to https://golang.org/doc/install to find out how to install a newer version of Go, 115 or use https://godoc.org/golang.org/dl/%s to install an additional version of Go.`, 116 v, recommendedVersions, recommendedVersions[0]) 117 } 118 119 archiver, err := initramfs.GetArchiver(*format) 120 if err != nil { 121 return err 122 } 123 124 logger := log.New(os.Stderr, "", log.LstdFlags) 125 // Open the target initramfs file. 126 w, err := archiver.OpenWriter(logger, *outputPath, env.GOOS, env.GOARCH) 127 if err != nil { 128 return err 129 } 130 131 var baseFile initramfs.Reader 132 if *base != "" { 133 bf, err := os.Open(*base) 134 if err != nil { 135 return err 136 } 137 defer bf.Close() 138 baseFile = archiver.Reader(bf) 139 } else { 140 baseFile = uroot.DefaultRamfs.Reader() 141 } 142 143 tempDir := *tmpDir 144 if tempDir == "" { 145 var err error 146 tempDir, err = ioutil.TempDir("", "u-root") 147 if err != nil { 148 return err 149 } 150 defer os.RemoveAll(tempDir) 151 } else if _, err := os.Stat(tempDir); os.IsNotExist(err) { 152 if err := os.MkdirAll(tempDir, 0755); err != nil { 153 return fmt.Errorf("temporary directory %q did not exist; tried to mkdir but failed: %v", tempDir, err) 154 } 155 } 156 157 var ( 158 c []uroot.Commands 159 initCommand = *initCmd 160 ) 161 if !*noCommands { 162 var b builder.Builder 163 switch *build { 164 case "bb": 165 b = builder.BBBuilder{} 166 case "binary": 167 b = builder.BinaryBuilder{} 168 case "source": 169 b = builder.SourceBuilder{ 170 FourBins: *fourbins, 171 } 172 default: 173 return fmt.Errorf("could not find builder %q", *build) 174 } 175 176 // Resolve globs into package imports. 177 // 178 // Currently allowed formats: 179 // Go package imports; e.g. github.com/u-root/u-root/cmds/ls (must be in $GOPATH) 180 // Paths to Go package directories; e.g. $GOPATH/src/github.com/u-root/u-root/cmds/* 181 var pkgs []string 182 for _, a := range flag.Args() { 183 p, ok := templates[a] 184 if !ok { 185 pkgs = append(pkgs, a) 186 continue 187 } 188 pkgs = append(pkgs, p...) 189 } 190 if len(pkgs) == 0 { 191 pkgs = []string{"github.com/u-root/u-root/cmds/core/*"} 192 } 193 194 if *fourbins && *build == "source" { 195 initCommand = "/go/bin/go" 196 } 197 198 // The command-line tool only allows specifying one build mode 199 // right now. 200 c = append(c, uroot.Commands{ 201 Builder: b, 202 Packages: pkgs, 203 }) 204 } 205 206 opts := uroot.Opts{ 207 Env: env, 208 Commands: c, 209 TempDir: tempDir, 210 ExtraFiles: extraFiles, 211 OutputFile: w, 212 BaseArchive: baseFile, 213 UseExistingInit: *useExistingInit, 214 InitCmd: initCommand, 215 DefaultShell: *defaultShell, 216 } 217 uinitArgs := shlex.Argv(*uinitCmd) 218 if len(uinitArgs) > 0 { 219 opts.UinitCmd = uinitArgs[0] 220 } 221 if len(uinitArgs) > 1 { 222 opts.UinitArgs = uinitArgs[1:] 223 } 224 return uroot.CreateInitramfs(logger, opts) 225 }