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