github.com/rck/u-root@v0.0.0-20180106144920-7eb602e381bb/cmds/init/init.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  // assumptions
     6  // we've been booted into a ramfs with all this stuff unpacked and ready.
     7  // we don't need a loop device mount because it's all there.
     8  // So we run /go/bin/go build installcommand
     9  // and then exec /buildbin/sh
    10  
    11  package main
    12  
    13  import (
    14  	"flag"
    15  	"fmt"
    16  	"io/ioutil"
    17  	"log"
    18  	"os"
    19  	"os/exec"
    20  	"path/filepath"
    21  	"runtime"
    22  	"strings"
    23  	"syscall"
    24  
    25  	"github.com/u-root/u-root/pkg/uroot/util"
    26  )
    27  
    28  var (
    29  	verbose = flag.Bool("v", false, "print all build commands")
    30  	test    = flag.Bool("test", false, "Test mode: don't try to set control tty")
    31  	debug   = func(string, ...interface{}) {}
    32  )
    33  
    34  func main() {
    35  	a := []string{"build"}
    36  	flag.Parse()
    37  	log.Printf("Welcome to u-root")
    38  	util.Rootfs()
    39  
    40  	if *verbose {
    41  		debug = log.Printf
    42  		a = append(a, "-x")
    43  	}
    44  
    45  	// populate buildbin
    46  
    47  	// In earlier versions we just had src/cmds. Due to the Go rules it seems we need to
    48  	// embed the URL of the repo everywhere. Yuck.
    49  	c, err := filepath.Glob("/src/github.com/u-root/*/cmds/[a-z]*")
    50  	if err != nil || len(c) == 0 {
    51  		log.Printf("In a break with tradition, you seem to have NO u-root commands: %v", err)
    52  	}
    53  	o, err := filepath.Glob("/src/*/*/*")
    54  	if err != nil {
    55  		log.Printf("Your filepath glob for other commands seems busted: %v", err)
    56  	}
    57  	c = append(c, o...)
    58  	for _, v := range c {
    59  		name := filepath.Base(v)
    60  		if name == "installcommand" || name == "init" {
    61  			continue
    62  		} else {
    63  			destPath := filepath.Join("/buildbin", name)
    64  			source := "/buildbin/installcommand"
    65  			if err := os.Symlink(source, destPath); err != nil {
    66  				log.Printf("Symlink %v -> %v failed; %v", source, destPath, err)
    67  			}
    68  		}
    69  	}
    70  
    71  	envs := os.Environ()
    72  	debug("envs %v", envs)
    73  	os.Setenv("GOBIN", "/buildbin")
    74  	a = append(a, "-o", "/buildbin/installcommand", filepath.Join(util.CmdsPath, "installcommand"))
    75  	cmd := exec.Command("go", a...)
    76  	installenvs := envs
    77  	installenvs = append(envs, "GOBIN=/buildbin")
    78  	cmd.Env = installenvs
    79  	cmd.Dir = "/"
    80  
    81  	cmd.Stdin = os.Stdin
    82  	cmd.Stderr = os.Stderr
    83  	cmd.Stdout = os.Stdout
    84  	debug("Run %v", cmd)
    85  	if err := cmd.Run(); err != nil {
    86  		log.Printf("%v\n", err)
    87  	}
    88  
    89  	// Before entering an interactive shell, decrease the loglevel because
    90  	// spamming non-critical logs onto the shell frustrates users. The logs
    91  	// are still accessible through dmesg.
    92  	const sysLogActionConsoleLevel = 8
    93  	const kernNotice = 5 // Only messages more severe than "notice" are printed.
    94  	if _, _, err := syscall.Syscall(syscall.SYS_SYSLOG, sysLogActionConsoleLevel, 0, kernNotice); err != 0 {
    95  		log.Print("Could not set log level")
    96  	}
    97  
    98  	// install /env.
    99  	os.Setenv("GOBIN", "/ubin")
   100  	envs = append(envs, "GOBIN=/ubin")
   101  	for _, e := range envs {
   102  		nv := strings.SplitN(e, "=", 2)
   103  		if len(nv) < 2 {
   104  			nv = append(nv, "")
   105  		}
   106  		n := filepath.Join("/env", nv[0])
   107  		if err := ioutil.WriteFile(n, []byte(nv[1]), 0666); err != nil {
   108  			log.Printf("%v: %v", n, err)
   109  		}
   110  	}
   111  
   112  	var profile string
   113  	// Some systems wipe out all the environment variables we so carefully craft.
   114  	// There is a way out -- we can put them into /etc/profile.d/uroot if we want.
   115  	// The PATH variable has to change, however.
   116  	path := fmt.Sprintf("%v:%v:%v:%v", util.GoBin(), util.PATHHEAD, "$PATH", util.PATHTAIL)
   117  	for k, v := range util.Env {
   118  		// We're doing the hacky way for now. We can clean this up later.
   119  		if k == "PATH" {
   120  			profile += "export PATH=" + path + "\n"
   121  		} else {
   122  			profile += "export " + k + "=" + v + "\n"
   123  		}
   124  	}
   125  
   126  	// The IFS lets us force a rehash every time we type a command, so that when we
   127  	// build uroot commands we don't keep rebuilding them.
   128  	profile += "IFS=`hash -r`\n"
   129  
   130  	// IF the profile is used, THEN when the user logs in they will need a
   131  	// private tmpfs. There's no good way to do this on linux. The closest
   132  	// we can get for now is to mount a tmpfs of /go/pkg/%s_%s :-( Same
   133  	// applies to ubin. Each user should have their own.
   134  	profile += fmt.Sprintf("sudo mount -t tmpfs none /go/pkg/%s_%s\n", runtime.GOOS, runtime.GOARCH)
   135  	profile += fmt.Sprintf("sudo mount -t tmpfs none /ubin\n")
   136  	profile += fmt.Sprintf("sudo mount -t tmpfs none /pkg\n")
   137  
   138  	// Now here's some good fun. We've set environment variables we want to see used.
   139  	// But on some systems the environment variable we create is completely ignored.
   140  	// Oh, is that you again, tinycore? Well.
   141  	// So we can save the day by writing the profile string to /etc/profile.d/uroot.sh
   142  	// mode, usually, 644.
   143  	// Only bother doing this is /etc/profile.d exists and is a directory.
   144  	if fi, err := os.Stat("/etc/profile.d"); err == nil && fi.IsDir() {
   145  		if err := ioutil.WriteFile("/etc/profile.d/uroot.sh", []byte(profile), 0644); err != nil {
   146  			log.Printf("Trying to write uroot profile failed: %v", err)
   147  		}
   148  	}
   149  
   150  	// Start background build.
   151  	if isBgBuildEnabled() {
   152  		go startBgBuild()
   153  	}
   154  
   155  	// There may be an inito if we are building on
   156  	// an existing initramfs. So, first, try to
   157  	// run inito and then run our shell
   158  	// inito is always first and we set default flags for it.
   159  	cloneFlags := uintptr(syscall.CLONE_NEWPID)
   160  	cmdList := []string{"/inito", "/buildbin/uinit", "/buildbin/rush"}
   161  	noCmdFound := true
   162  	for _, v := range cmdList {
   163  		if _, err := os.Stat(v); !os.IsNotExist(err) {
   164  			noCmdFound = false
   165  			cmd = exec.Command(v)
   166  			cmd.Env = envs
   167  			cmd.Stdin = os.Stdin
   168  			cmd.Stderr = os.Stderr
   169  			cmd.Stdout = os.Stdout
   170  			if *test {
   171  				cmd.SysProcAttr = &syscall.SysProcAttr{Cloneflags: cloneFlags}
   172  			} else {
   173  				cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true, Cloneflags: cloneFlags}
   174  			}
   175  			debug("Run %v", cmd)
   176  			if err := cmd.Run(); err != nil {
   177  				log.Print(err)
   178  			}
   179  		}
   180  		// only the first init needs its own PID space.
   181  		cloneFlags = 0
   182  	}
   183  
   184  	if noCmdFound {
   185  		log.Printf("init: No suitable executable found in %+v", cmdList)
   186  	}
   187  
   188  	log.Printf("init: All commands exited")
   189  	log.Printf("init: Syncing filesystems")
   190  	syscall.Sync()
   191  	log.Printf("init: Exiting...")
   192  }