gopkg.in/hugelgupf/u-root.v9@v9.0.0-20180831063832-3f6f1057f09b/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" 21 "path/filepath" 22 "runtime" 23 "strings" 24 "syscall" 25 26 "github.com/u-root/u-root/pkg/uroot/util" 27 ) 28 29 var ( 30 verbose = flag.Bool("v", false, "print all build commands") 31 test = flag.Bool("test", false, "Test mode: don't try to set control tty") 32 debug = func(string, ...interface{}) {} 33 osInitGo = func() {} 34 cmdList = []string{ 35 "/inito", 36 37 "/bbin/uinit", 38 "/bin/uinit", 39 "/buildbin/uinit", 40 41 "/bin/defaultsh", 42 } 43 cmdCount int 44 envs []string 45 ) 46 47 func main() { 48 flag.Parse() 49 log.Printf("Welcome to u-root") 50 util.Rootfs() 51 52 if *verbose { 53 debug = log.Printf 54 } 55 56 // Before entering an interactive shell, decrease the loglevel because 57 // spamming non-critical logs onto the shell frustrates users. The logs 58 // are still accessible through dmesg. 59 const sysLogActionConsoleLevel = 8 60 const kernNotice = 5 // Only messages more severe than "notice" are printed. 61 if _, _, err := syscall.Syscall(syscall.SYS_SYSLOG, sysLogActionConsoleLevel, 0, kernNotice); err != 0 { 62 log.Print("Could not set log level") 63 } 64 65 envs = os.Environ() 66 debug("envs %v", envs) 67 68 // install /env. 69 for _, e := range envs { 70 nv := strings.SplitN(e, "=", 2) 71 if len(nv) < 2 { 72 nv = append(nv, "") 73 } 74 n := filepath.Join("/env", nv[0]) 75 if err := ioutil.WriteFile(n, []byte(nv[1]), 0666); err != nil { 76 log.Printf("%v: %v", n, err) 77 } 78 } 79 80 var profile string 81 // Some systems wipe out all the environment variables we so carefully craft. 82 // There is a way out -- we can put them into /etc/profile.d/uroot if we want. 83 // The PATH variable has to change, however. 84 epath := fmt.Sprintf("%v:%v:%v:%v", util.GoBin(), util.PATHHEAD, "$PATH", util.PATHTAIL) 85 for k, v := range util.Env { 86 // We're doing the hacky way for now. We can clean this up later. 87 if k == "PATH" { 88 profile += "export PATH=" + epath + "\n" 89 } else { 90 profile += "export " + k + "=" + v + "\n" 91 } 92 } 93 94 // The IFS lets us force a rehash every time we type a command, so that when we 95 // build uroot commands we don't keep rebuilding them. 96 profile += "IFS=`hash -r`\n" 97 98 // IF the profile is used, THEN when the user logs in they will need a 99 // private tmpfs. There's no good way to do this on linux. The closest 100 // we can get for now is to mount a tmpfs of /go/pkg/%s_%s :-( Same 101 // applies to ubin. Each user should have their own. 102 profile += fmt.Sprintf("sudo mount -t tmpfs none /go/pkg/%s_%s\n", runtime.GOOS, runtime.GOARCH) 103 profile += fmt.Sprintf("sudo mount -t tmpfs none /ubin\n") 104 profile += fmt.Sprintf("sudo mount -t tmpfs none /pkg\n") 105 106 // Now here's some good fun. We've set environment variables we want to see used. 107 // But on some systems the environment variable we create is completely ignored. 108 // Oh, is that you again, tinycore? Well. 109 // So we can save the day by writing the profile string to /etc/profile.d/uroot.sh 110 // mode, usually, 644. 111 // Only bother doing this is /etc/profile.d exists and is a directory. 112 if fi, err := os.Stat("/etc/profile.d"); err == nil && fi.IsDir() { 113 if err := ioutil.WriteFile("/etc/profile.d/uroot.sh", []byte(profile), 0644); err != nil { 114 log.Printf("Trying to write uroot profile failed: %v", err) 115 } 116 } 117 118 // Start background build. 119 if isBgBuildEnabled() { 120 go startBgBuild() 121 } 122 123 osInitGo() 124 125 for _, v := range cmdList { 126 if _, err := os.Stat(v); os.IsNotExist(err) { 127 continue 128 } 129 130 // I *love* special cases. Evaluate just the top-most symlink. 131 // 132 // In source mode, this would be a symlink like 133 // /buildbin/defaultsh -> /buildbin/elvish -> 134 // /buildbin/installcommand. 135 // 136 // To actually get the command to build, argv[0] has to end 137 // with /elvish, so we resolve one level of symlink. 138 if path.Base(v) == "defaultsh" { 139 s, err := os.Readlink(v) 140 if err == nil { 141 v = s 142 } 143 } 144 145 // inito is (optionally) created by the u-root command when the 146 // u-root initramfs is merged with an existing initramfs that 147 // has a /init. The name inito means "original /init" There may 148 // be an inito if we are building on an existing initramfs. All 149 // initos need their own pid space. 150 var cloneFlags uintptr 151 if v == "/inito" { 152 cloneFlags = uintptr(syscall.CLONE_NEWPID) 153 } 154 155 cmdCount++ 156 cmd := exec.Command(v) 157 cmd.Env = envs 158 cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr 159 if *test { 160 cmd.SysProcAttr = &syscall.SysProcAttr{Cloneflags: cloneFlags} 161 } else { 162 cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true, Cloneflags: cloneFlags} 163 } 164 debug("Run %v", cmd) 165 if err := cmd.Start(); err != nil { 166 log.Printf("Error starting %v: %v", v, err) 167 continue 168 } 169 for { 170 var s syscall.WaitStatus 171 var r syscall.Rusage 172 if p, err := syscall.Wait4(-1, &s, 0, &r); p == cmd.Process.Pid { 173 debug("Shell exited, exit status %d", s.ExitStatus()) 174 break 175 } else if p != -1 { 176 debug("Reaped PID %d, exit status %d", p, s.ExitStatus()) 177 } else { 178 debug("Error from Wait4 for orphaned child: %v", err) 179 break 180 } 181 } 182 if err := cmd.Process.Release(); err != nil { 183 log.Printf("Error releasing %v:%v", v, err) 184 } 185 } 186 if cmdCount == 0 { 187 log.Printf("init: No suitable executable found in %+v", cmdList) 188 } 189 190 // We need to reap all children before exiting. 191 log.Printf("init: Waiting for orphaned children") 192 for { 193 var s syscall.WaitStatus 194 var r syscall.Rusage 195 p, err := syscall.Wait4(-1, &s, 0, &r) 196 if p == -1 { 197 break 198 } 199 log.Printf("%v: exited with %v, status %v, rusage %v", p, err, s, r) 200 } 201 log.Printf("init: All commands exited") 202 log.Printf("init: Syncing filesystems") 203 syscall.Sync() 204 log.Printf("init: Exiting...") 205 }