init.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. // +build linux
  2. package init
  3. import (
  4. "bufio"
  5. "fmt"
  6. "io/ioutil"
  7. "os"
  8. "os/exec"
  9. "path/filepath"
  10. "strings"
  11. "syscall"
  12. "github.com/docker/docker/pkg/mount"
  13. "github.com/rancher/os/config"
  14. "github.com/rancher/os/dfs"
  15. "github.com/rancher/os/log"
  16. "github.com/rancher/os/util"
  17. "github.com/rancher/os/util/network"
  18. )
  19. const (
  20. state string = "/state"
  21. boot2DockerMagic string = "boot2docker, please format-me"
  22. tmpfsMagic int64 = 0x01021994
  23. ramfsMagic int64 = 0x858458f6
  24. )
  25. var (
  26. mountConfig = dfs.Config{
  27. CgroupHierarchy: map[string]string{
  28. "cpu": "cpu",
  29. "cpuacct": "cpu",
  30. "net_cls": "net_cls",
  31. "net_prio": "net_cls",
  32. },
  33. }
  34. )
  35. func loadModules(cfg *config.CloudConfig) (*config.CloudConfig, error) {
  36. mounted := map[string]bool{}
  37. f, err := os.Open("/proc/modules")
  38. if err != nil {
  39. return cfg, err
  40. }
  41. defer f.Close()
  42. reader := bufio.NewScanner(f)
  43. for reader.Scan() {
  44. mounted[strings.SplitN(reader.Text(), " ", 2)[0]] = true
  45. }
  46. for _, module := range cfg.Rancher.Modules {
  47. if mounted[module] {
  48. continue
  49. }
  50. log.Debugf("Loading module %s", module)
  51. // split module and module parameters
  52. cmdParam := strings.SplitN(module, " ", -1)
  53. cmd := exec.Command("modprobe", cmdParam...)
  54. cmd.Stdout = os.Stdout
  55. cmd.Stderr = os.Stderr
  56. if err := cmd.Run(); err != nil {
  57. log.Errorf("Could not load module %s, err %v", module, err)
  58. }
  59. }
  60. return cfg, nil
  61. }
  62. func sysInit(c *config.CloudConfig) (*config.CloudConfig, error) {
  63. args := append([]string{config.SysInitBin}, os.Args[1:]...)
  64. cmd := &exec.Cmd{
  65. Path: config.RosBin,
  66. Args: args,
  67. }
  68. cmd.Stdin = os.Stdin
  69. cmd.Stderr = os.Stderr
  70. cmd.Stdout = os.Stdout
  71. if err := cmd.Start(); err != nil {
  72. return c, err
  73. }
  74. return c, os.Stdin.Close()
  75. }
  76. func MainInit() {
  77. log.InitLogger()
  78. // TODO: this breaks and does nothing if the cfg is invalid (or is it due to threading?)
  79. defer func() {
  80. if r := recover(); r != nil {
  81. fmt.Printf("Starting Recovery console: %v\n", r)
  82. recovery(nil)
  83. }
  84. }()
  85. if err := RunInit(); err != nil {
  86. log.Fatal(err)
  87. }
  88. }
  89. func mountConfigured(display, dev, fsType, target string) error {
  90. var err error
  91. if dev == "" {
  92. return nil
  93. }
  94. dev = util.ResolveDevice(dev)
  95. if dev == "" {
  96. return fmt.Errorf("Could not resolve device %q", dev)
  97. }
  98. if fsType == "auto" {
  99. fsType, err = util.GetFsType(dev)
  100. }
  101. if err != nil {
  102. return err
  103. }
  104. log.Debugf("FsType has been set to %s", fsType)
  105. log.Infof("Mounting %s device %s to %s", display, dev, target)
  106. return util.Mount(dev, target, fsType, "")
  107. }
  108. func mountState(cfg *config.CloudConfig) error {
  109. return mountConfigured("state", cfg.Rancher.State.Dev, cfg.Rancher.State.FsType, state)
  110. }
  111. func mountOem(cfg *config.CloudConfig) (*config.CloudConfig, error) {
  112. if cfg == nil {
  113. cfg = config.LoadConfig()
  114. }
  115. if err := mountConfigured("oem", cfg.Rancher.State.OemDev, cfg.Rancher.State.OemFsType, config.OEM); err != nil {
  116. log.Debugf("Not mounting OEM: %v", err)
  117. } else {
  118. log.Infof("Mounted OEM: %s", cfg.Rancher.State.OemDev)
  119. }
  120. return cfg, nil
  121. }
  122. func tryMountState(cfg *config.CloudConfig) error {
  123. if mountState(cfg) == nil {
  124. return nil
  125. }
  126. // If we failed to mount lets run bootstrap and try again
  127. if err := bootstrap(cfg); err != nil {
  128. return err
  129. }
  130. return mountState(cfg)
  131. }
  132. func tryMountAndBootstrap(cfg *config.CloudConfig) (*config.CloudConfig, bool, error) {
  133. if !isInitrd() || cfg.Rancher.State.Dev == "" {
  134. return cfg, false, nil
  135. }
  136. if err := tryMountState(cfg); !cfg.Rancher.State.Required && err != nil {
  137. return cfg, false, nil
  138. } else if err != nil {
  139. return cfg, false, err
  140. }
  141. return cfg, true, nil
  142. }
  143. func getLaunchConfig(cfg *config.CloudConfig, dockerCfg *config.DockerConfig) (*dfs.Config, []string) {
  144. var launchConfig dfs.Config
  145. args := dfs.ParseConfig(&launchConfig, dockerCfg.FullArgs()...)
  146. launchConfig.DNSConfig.Nameservers = cfg.Rancher.Defaults.Network.DNS.Nameservers
  147. launchConfig.DNSConfig.Search = cfg.Rancher.Defaults.Network.DNS.Search
  148. launchConfig.Environment = dockerCfg.Environment
  149. if !cfg.Rancher.Debug {
  150. launchConfig.LogFile = cfg.Rancher.Defaults.SystemDockerLogs
  151. }
  152. return &launchConfig, args
  153. }
  154. func isInitrd() bool {
  155. var stat syscall.Statfs_t
  156. syscall.Statfs("/", &stat)
  157. return int64(stat.Type) == tmpfsMagic || int64(stat.Type) == ramfsMagic
  158. }
  159. func setupSharedRoot(c *config.CloudConfig) (*config.CloudConfig, error) {
  160. if c.Rancher.NoSharedRoot {
  161. return c, nil
  162. }
  163. if isInitrd() {
  164. for _, i := range []string{"/mnt", "/media", "/var/lib/system-docker"} {
  165. if err := os.MkdirAll(i, 0755); err != nil {
  166. return c, err
  167. }
  168. if err := mount.Mount("tmpfs", i, "tmpfs", "rw"); err != nil {
  169. return c, err
  170. }
  171. if err := mount.MakeShared(i); err != nil {
  172. return c, err
  173. }
  174. }
  175. return c, nil
  176. }
  177. return c, mount.MakeShared("/")
  178. }
  179. func PrintConfig() {
  180. cfgString, err := config.Export(false, true)
  181. if err != nil {
  182. log.WithFields(log.Fields{"err": err}).Error("Error serializing config")
  183. } else {
  184. log.Debugf("Config: %s", cfgString)
  185. }
  186. }
  187. func RunInit() error {
  188. os.Setenv("PATH", "/sbin:/usr/sbin:/usr/bin")
  189. if isInitrd() {
  190. log.Debug("Booting off an in-memory filesystem")
  191. // Magic setting to tell Docker to do switch_root and not pivot_root
  192. os.Setenv("DOCKER_RAMDISK", "true")
  193. } else {
  194. log.Debug("Booting off a persistent filesystem")
  195. }
  196. boot2DockerEnvironment := false
  197. var shouldSwitchRoot bool
  198. hypervisor := ""
  199. configFiles := make(map[string][]byte)
  200. initFuncs := []config.CfgFuncData{
  201. config.CfgFuncData{"preparefs", func(c *config.CloudConfig) (*config.CloudConfig, error) {
  202. return c, dfs.PrepareFs(&mountConfig)
  203. }},
  204. config.CfgFuncData{"save init cmdline", func(c *config.CloudConfig) (*config.CloudConfig, error) {
  205. // the Kernel Patch added for RancherOS passes `--` (only) elided kernel boot params to the init process
  206. cmdLineArgs := strings.Join(os.Args, " ")
  207. config.SaveInitCmdline(cmdLineArgs)
  208. cfg := config.LoadConfig()
  209. log.Debugf("Cmdline debug = %t", cfg.Rancher.Debug)
  210. if cfg.Rancher.Debug {
  211. log.SetLevel(log.DebugLevel)
  212. } else {
  213. log.SetLevel(log.InfoLevel)
  214. }
  215. return cfg, nil
  216. }},
  217. config.CfgFuncData{"mount OEM", mountOem},
  218. config.CfgFuncData{"debug save cfg", func(_ *config.CloudConfig) (*config.CloudConfig, error) {
  219. PrintConfig()
  220. cfg := config.LoadConfig()
  221. return cfg, nil
  222. }},
  223. config.CfgFuncData{"load modules", loadModules},
  224. config.CfgFuncData{"recovery console", func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
  225. if cfg.Rancher.Recovery {
  226. recovery(nil)
  227. }
  228. return cfg, nil
  229. }},
  230. config.CfgFuncData{"b2d env", func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
  231. if _, err := os.Stat("/var/lib/boot2docker"); os.IsNotExist(err) {
  232. err := os.Mkdir("/var/lib/boot2docker", 0755)
  233. if err != nil {
  234. log.Errorf("Failed to create boot2docker directory: %v", err)
  235. }
  236. }
  237. if dev := util.ResolveDevice("LABEL=B2D_STATE"); dev != "" {
  238. boot2DockerEnvironment = true
  239. cfg.Rancher.State.Dev = "LABEL=B2D_STATE"
  240. log.Infof("boot2DockerEnvironment %s: %s", cfg.Rancher.State.Dev, dev)
  241. return cfg, nil
  242. }
  243. devices := []string{"/dev/sda", "/dev/vda"}
  244. data := make([]byte, len(boot2DockerMagic))
  245. for _, device := range devices {
  246. f, err := os.Open(device)
  247. if err == nil {
  248. defer f.Close()
  249. _, err = f.Read(data)
  250. if err == nil && string(data) == boot2DockerMagic {
  251. boot2DockerEnvironment = true
  252. cfg.Rancher.State.Dev = "LABEL=B2D_STATE"
  253. cfg.Rancher.State.Autoformat = []string{device}
  254. log.Infof("boot2DockerEnvironment %s: Autoformat %s", cfg.Rancher.State.Dev, cfg.Rancher.State.Autoformat[0])
  255. break
  256. }
  257. }
  258. }
  259. // save here so the bootstrap service can see it (when booting from iso, its very early)
  260. if boot2DockerEnvironment {
  261. if err := config.Set("rancher.state.dev", cfg.Rancher.State.Dev); err != nil {
  262. log.Errorf("Failed to update rancher.state.dev: %v", err)
  263. }
  264. if err := config.Set("rancher.state.autoformat", cfg.Rancher.State.Autoformat); err != nil {
  265. log.Errorf("Failed to update rancher.state.autoformat: %v", err)
  266. }
  267. }
  268. return config.LoadConfig(), nil
  269. }},
  270. config.CfgFuncData{"mount and bootstrap", func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
  271. var err error
  272. cfg, shouldSwitchRoot, err = tryMountAndBootstrap(cfg)
  273. if err != nil {
  274. return nil, err
  275. }
  276. return cfg, nil
  277. }},
  278. config.CfgFuncData{"cloud-init", func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
  279. cfg.Rancher.CloudInit.Datasources = config.LoadConfigWithPrefix(state).Rancher.CloudInit.Datasources
  280. hypervisor = util.GetHypervisor()
  281. if hypervisor == "" {
  282. log.Infof("ros init: No Detected Hypervisor")
  283. } else {
  284. log.Infof("ros init: Detected Hypervisor: %s", hypervisor)
  285. }
  286. if hypervisor == "vmware" {
  287. // add vmware to the end - we don't want to over-ride an choices the user has made
  288. cfg.Rancher.CloudInit.Datasources = append(cfg.Rancher.CloudInit.Datasources, hypervisor)
  289. }
  290. if err := config.Set("rancher.cloud_init.datasources", cfg.Rancher.CloudInit.Datasources); err != nil {
  291. log.Error(err)
  292. }
  293. log.Infof("init, runCloudInitServices(%v)", cfg.Rancher.CloudInit.Datasources)
  294. if err := runCloudInitServices(cfg); err != nil {
  295. log.Error(err)
  296. }
  297. // It'd be nice to push to rsyslog before this, but we don't have network
  298. log.AddRSyslogHook()
  299. return config.LoadConfig(), nil
  300. }},
  301. config.CfgFuncData{"read cfg and log files", func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
  302. filesToCopy := []string{
  303. config.CloudConfigInitFile,
  304. config.CloudConfigScriptFile,
  305. config.CloudConfigBootFile,
  306. config.CloudConfigNetworkFile,
  307. config.MetaDataFile,
  308. config.EtcResolvConfFile,
  309. }
  310. // And all the files in /var/log/boot/
  311. // TODO: I wonder if we can put this code into the log module, and have things write to the buffer until we FsReady()
  312. bootLog := "/var/log/"
  313. if files, err := ioutil.ReadDir(bootLog); err == nil {
  314. for _, file := range files {
  315. if !file.IsDir() {
  316. filePath := filepath.Join(bootLog, file.Name())
  317. filesToCopy = append(filesToCopy, filePath)
  318. log.Debugf("Swizzle: Found %s to save", filePath)
  319. }
  320. }
  321. }
  322. bootLog = "/var/log/boot/"
  323. if files, err := ioutil.ReadDir(bootLog); err == nil {
  324. for _, file := range files {
  325. filePath := filepath.Join(bootLog, file.Name())
  326. filesToCopy = append(filesToCopy, filePath)
  327. log.Debugf("Swizzle: Found %s to save", filePath)
  328. }
  329. }
  330. for _, name := range filesToCopy {
  331. if _, err := os.Lstat(name); !os.IsNotExist(err) {
  332. content, err := ioutil.ReadFile(name)
  333. if err != nil {
  334. log.Errorf("read cfg file (%s) %s", name, err)
  335. continue
  336. }
  337. log.Debugf("Swizzle: Saved %s to memory", name)
  338. configFiles[name] = content
  339. }
  340. }
  341. return cfg, nil
  342. }},
  343. config.CfgFuncData{"switchroot", func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
  344. if !shouldSwitchRoot {
  345. return cfg, nil
  346. }
  347. log.Debugf("Switching to new root at %s %s", state, cfg.Rancher.State.Directory)
  348. if err := switchRoot(state, cfg.Rancher.State.Directory, cfg.Rancher.RmUsr); err != nil {
  349. return cfg, err
  350. }
  351. return cfg, nil
  352. }},
  353. config.CfgFuncData{"mount OEM2", mountOem},
  354. config.CfgFuncData{"write cfg and log files", func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
  355. for name, content := range configFiles {
  356. dirMode := os.ModeDir | 0755
  357. fileMode := os.FileMode(0444)
  358. if strings.HasPrefix(name, "/var/lib/rancher/conf/") {
  359. // only make the conf files harder to get to
  360. dirMode = os.ModeDir | 0700
  361. if name == config.CloudConfigScriptFile {
  362. fileMode = os.FileMode(0755)
  363. } else {
  364. fileMode = os.FileMode(0400)
  365. }
  366. }
  367. if err := os.MkdirAll(filepath.Dir(name), dirMode); err != nil {
  368. log.Error(err)
  369. }
  370. if err := util.WriteFileAtomic(name, content, fileMode); err != nil {
  371. log.Error(err)
  372. }
  373. log.Infof("Swizzle: Wrote file to %s", name)
  374. }
  375. if err := os.MkdirAll(config.VarRancherDir, os.ModeDir|0755); err != nil {
  376. log.Error(err)
  377. }
  378. if err := os.Chmod(config.VarRancherDir, os.ModeDir|0755); err != nil {
  379. log.Error(err)
  380. }
  381. log.FsReady()
  382. log.Debugf("WARNING: switchroot and mount OEM2 phases not written to log file")
  383. return cfg, nil
  384. }},
  385. config.CfgFuncData{"b2d Env", func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
  386. log.Debugf("memory Resolve.conf == [%s]", configFiles["/etc/resolv.conf"])
  387. // this code make sure the open-vm-tools service can be started correct when there is no network
  388. if hypervisor == "vmware" {
  389. // make sure the cache directory exist
  390. if err := os.MkdirAll("/var/lib/rancher/cache/", os.ModeDir|0755); err != nil {
  391. log.Errorf("Create service cache diretory error: %v", err)
  392. }
  393. // move os-services cache file
  394. if _, err := os.Stat("/usr/share/ros/services-cache"); err == nil {
  395. files, err := ioutil.ReadDir("/usr/share/ros/services-cache/")
  396. if err != nil {
  397. log.Errorf("Read file error: %v", err)
  398. }
  399. for _, f := range files {
  400. err := os.Rename("/usr/share/ros/services-cache/"+f.Name(), "/var/lib/rancher/cache/"+f.Name())
  401. if err != nil {
  402. log.Errorf("Rename file error: %v", err)
  403. }
  404. }
  405. if err := os.Remove("/usr/share/ros/services-cache"); err != nil {
  406. log.Errorf("Remove file error: %v", err)
  407. }
  408. }
  409. }
  410. if boot2DockerEnvironment {
  411. if err := config.Set("rancher.state.dev", cfg.Rancher.State.Dev); err != nil {
  412. log.Errorf("Failed to update rancher.state.dev: %v", err)
  413. }
  414. if err := config.Set("rancher.state.autoformat", cfg.Rancher.State.Autoformat); err != nil {
  415. log.Errorf("Failed to update rancher.state.autoformat: %v", err)
  416. }
  417. }
  418. return config.LoadConfig(), nil
  419. }},
  420. config.CfgFuncData{"hypervisor tools", func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
  421. enableHypervisorService(cfg, hypervisor)
  422. return config.LoadConfig(), nil
  423. }},
  424. config.CfgFuncData{"preparefs2", func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
  425. return cfg, dfs.PrepareFs(&mountConfig)
  426. }},
  427. config.CfgFuncData{"load modules2", loadModules},
  428. config.CfgFuncData{"set proxy env", func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
  429. network.SetProxyEnvironmentVariables()
  430. return cfg, nil
  431. }},
  432. config.CfgFuncData{"init SELinux", initializeSelinux},
  433. config.CfgFuncData{"setupSharedRoot", setupSharedRoot},
  434. config.CfgFuncData{"sysinit", sysInit},
  435. }
  436. cfg, err := config.ChainCfgFuncs(nil, initFuncs)
  437. if err != nil {
  438. recovery(err)
  439. }
  440. launchConfig, args := getLaunchConfig(cfg, &cfg.Rancher.SystemDocker)
  441. launchConfig.Fork = !cfg.Rancher.SystemDocker.Exec
  442. //launchConfig.NoLog = true
  443. log.Info("Launching System Docker")
  444. _, err = dfs.LaunchDocker(launchConfig, config.SystemDockerBin, args...)
  445. if err != nil {
  446. log.Errorf("Error Launching System Docker: %s", err)
  447. recovery(err)
  448. return err
  449. }
  450. // Code never gets here - rancher.system_docker.exec=true
  451. return pidOne()
  452. }
  453. func enableHypervisorService(cfg *config.CloudConfig, hypervisorName string) {
  454. if hypervisorName == "" {
  455. return
  456. }
  457. // only enable open-vm-tools for vmware
  458. // these services(xenhvm-vm-tools, kvm-vm-tools, hyperv-vm-tools and bhyve-vm-tools) don't exist yet
  459. if hypervisorName == "vmware" {
  460. hypervisorName = "open"
  461. serviceName := hypervisorName + "-vm-tools"
  462. if !cfg.Rancher.HypervisorService {
  463. log.Infof("Skipping %s as `rancher.hypervisor_service` is set to false", serviceName)
  464. return
  465. }
  466. // Check removed - there's an x509 cert failure on first boot of an installed system
  467. // check quickly to see if there is a yml file available
  468. // if service.ValidService(serviceName, cfg) {
  469. log.Infof("Setting rancher.services_include. %s=true", serviceName)
  470. if err := config.Set("rancher.services_include."+serviceName, "true"); err != nil {
  471. log.Error(err)
  472. }
  473. // } else {
  474. // log.Infof("Skipping %s, can't get %s.yml file", serviceName, serviceName)
  475. // }
  476. }
  477. }