github.com/Cloud-Foundations/Dominator@v0.3.4/cmd/installer/main.go (about) 1 // +build linux 2 3 package main 4 5 import ( 6 "flag" 7 "fmt" 8 stdlog "log" 9 "os" 10 "path/filepath" 11 "sync" 12 "syscall" 13 "time" 14 15 "github.com/Cloud-Foundations/Dominator/lib/constants" 16 "github.com/Cloud-Foundations/Dominator/lib/flags/loadflags" 17 "github.com/Cloud-Foundations/Dominator/lib/fsutil" 18 "github.com/Cloud-Foundations/Dominator/lib/log" 19 "github.com/Cloud-Foundations/Dominator/lib/log/debuglogger" 20 "github.com/Cloud-Foundations/Dominator/lib/logbuf" 21 "github.com/Cloud-Foundations/Dominator/lib/srpc" 22 "github.com/Cloud-Foundations/Dominator/lib/srpc/setupserver" 23 "github.com/Cloud-Foundations/tricorder/go/tricorder" 24 ) 25 26 const logfile = "/var/log/installer/latest" 27 28 type flusher interface { 29 Flush() error 30 } 31 32 type Rebooter interface { 33 Reboot() error 34 String() string 35 } 36 37 var ( 38 dryRun = flag.Bool("dryRun", ifUnprivileged(), 39 "If true, do not make changes") 40 mountPoint = flag.String("mountPoint", "/mnt", 41 "Mount point for new root file-system") 42 objectsDirectory = flag.String("objectsDirectory", "/objects", 43 "Directory where cached objects will be written") 44 logDebugLevel = flag.Int("logDebugLevel", -1, "Debug log level") 45 portNum = flag.Uint("portNum", constants.InstallerPortNumber, 46 "Port number to allocate and listen on for HTTP/RPC") 47 procDirectory = flag.String("procDirectory", "/proc", 48 "Directory where procfs is mounted") 49 skipNetwork = flag.Bool("skipNetwork", false, 50 "If true, do not update target network configuration") 51 skipStorage = flag.Bool("skipStorage", false, 52 "If true, do not update storage") 53 sysfsDirectory = flag.String("sysfsDirectory", "/sys", 54 "Directory where sysfs is mounted") 55 tftpDirectory = flag.String("tftpDirectory", "/tftpdata", 56 "Directory containing (possibly injected) TFTP data") 57 tmpRoot = flag.String("tmpRoot", "/tmproot", 58 "Mount point for temporary (tmpfs) root file-system") 59 ) 60 61 func copyLogs(logFlusher flusher) error { 62 logFlusher.Flush() 63 logdir := filepath.Join(*mountPoint, "var", "log", "installer") 64 return fsutil.CopyFile(filepath.Join(logdir, "log"), logfile, 65 fsutil.PublicFilePerms) 66 } 67 68 func createLogger() (*logbuf.LogBuffer, log.DebugLogger) { 69 os.MkdirAll("/var/log/installer", fsutil.DirPerms) 70 options := logbuf.GetStandardOptions() 71 options.AlsoLogToStderr = true 72 logBuffer := logbuf.NewWithOptions(options) 73 logger := debuglogger.New(stdlog.New(logBuffer, "", 0)) 74 logger.SetLevel(int16(*logDebugLevel)) 75 srpc.SetDefaultLogger(logger) 76 return logBuffer, logger 77 } 78 79 func ifUnprivileged() bool { 80 if os.Geteuid() != 0 { 81 return true 82 } 83 return false 84 } 85 86 func install(updateHwClock bool, logFlusher flusher, 87 logger log.DebugLogger) (Rebooter, error) { 88 var rebooter Rebooter 89 machineInfo, interfaces, err := configureLocalNetwork(logger) 90 if err != nil { 91 return nil, err 92 } 93 if !*skipStorage { 94 rebooter, err = configureStorage(*machineInfo, logger) 95 if err != nil { 96 return nil, err 97 } 98 if !*dryRun && updateHwClock { 99 if err := run("hwclock", *tmpRoot, logger, "-w"); err != nil { 100 logger.Printf("Error updating hardware clock: %s\n", err) 101 } else { 102 logger.Println("Updated hardware clock from system clock") 103 } 104 } 105 } 106 if !*skipNetwork { 107 err := configureNetwork(*machineInfo, interfaces, logger) 108 if err != nil { 109 return nil, err 110 } 111 } 112 if err := copyLogs(logFlusher); err != nil { 113 return nil, fmt.Errorf("error copying logs: %s", err) 114 } 115 if err := unmountStorage(logger); err != nil { 116 return nil, fmt.Errorf("error unmounting: %s", err) 117 } 118 return rebooter, nil 119 } 120 121 func printAndWait(initialTimeoutString, waitTimeoutString string, 122 waitGroup *sync.WaitGroup, rebooterName string, logger log.Logger) { 123 initialTimeout, _ := time.ParseDuration(initialTimeoutString) 124 if initialTimeout < time.Second { 125 initialTimeout = time.Second 126 initialTimeoutString = "1s" 127 } 128 logger.Printf("waiting %s before rebooting with %s rebooter\n", 129 initialTimeoutString, rebooterName) 130 time.Sleep(initialTimeout - time.Second) 131 waitChannel := make(chan struct{}) 132 go func() { 133 waitGroup.Wait() 134 waitChannel <- struct{}{} 135 }() 136 timer := time.NewTimer(time.Second) 137 select { 138 case <-timer.C: 139 case <-waitChannel: 140 return 141 } 142 logger.Printf( 143 "waiting %s for remote shells to terminate before rebooting\n", 144 waitTimeoutString) 145 waitTimeout, _ := time.ParseDuration(waitTimeoutString) 146 timer.Reset(waitTimeout) 147 select { 148 case <-timer.C: 149 case <-waitChannel: 150 } 151 } 152 153 func doMain() error { 154 var timeLogMessage string 155 if fi, err := os.Stat("/build-timestamp"); err != nil { 156 return err 157 } else { 158 now := time.Now() 159 if fi.ModTime().After(now) { 160 timeval := syscall.Timeval{Sec: fi.ModTime().Unix()} 161 if err := syscall.Settimeofday(&timeval); err != nil { 162 return err 163 } 164 timeLogMessage = fmt.Sprintf("System time: %s is earlier than build time: %s.\nAdvancing to build time", 165 now, fi.ModTime()) 166 } 167 } 168 if err := loadflags.LoadForDaemon("installer"); err != nil { 169 return err 170 } 171 flag.Parse() 172 tricorder.RegisterFlags() 173 logBuffer, logger := createLogger() 174 defer logBuffer.Flush() 175 if timeLogMessage != "" { 176 logger.Println(timeLogMessage) 177 } 178 go runShellOnConsole(logger) 179 AddHtmlWriter(logBuffer) 180 params := setupserver.Params{Logger: logger} 181 if err := setupserver.SetupTlsWithParams(params); err != nil { 182 logger.Println(err) 183 } 184 waitGroup := &sync.WaitGroup{} 185 if newLogger, err := startServer(*portNum, waitGroup, logger); err != nil { 186 logger.Printf("cannot start server: %s\n", err) 187 } else { 188 logger = newLogger 189 } 190 rebooter, err := install(timeLogMessage != "", logBuffer, logger) 191 rebooterName := "default" 192 if rebooter != nil { 193 rebooterName = rebooter.String() 194 } 195 if err != nil { 196 logger.Println(err) 197 printAndWait("5m", "1h", waitGroup, rebooterName, logger) 198 } else { 199 printAndWait("5s", "5m", waitGroup, rebooterName, logger) 200 } 201 syscall.Sync() 202 if rebooter != nil { 203 if err := rebooter.Reboot(); err != nil { 204 logger.Printf("error calling %s rebooter: %s\n", rebooterName, err) 205 logger.Println("falling back to default rebooter after 5 minutes") 206 time.Sleep(time.Minute * 5) 207 } 208 } 209 if err := syscall.Reboot(syscall.LINUX_REBOOT_CMD_RESTART); err != nil { 210 logger.Fatalf("error rebooting: %s\n", err) 211 } 212 return nil 213 } 214 215 func main() { 216 if err := doMain(); err != nil { 217 fmt.Fprintln(os.Stderr, err) 218 os.Exit(1) 219 } 220 }