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