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 }