mirror of
https://github.com/rancher/os.git
synced 2025-08-09 18:48:05 +00:00
reboot --kexec almost works
Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
This commit is contained in:
parent
5e4b5975a9
commit
c5d4cb91c3
@ -152,7 +152,7 @@ func installAction(c *cli.Context) error {
|
|||||||
if cloudConfig == "" {
|
if cloudConfig == "" {
|
||||||
if installType != "upgrade" {
|
if installType != "upgrade" {
|
||||||
// TODO: I wonder if its plausible to merge a new cloud-config into an existing one on upgrade - so for now, i'm only turning off the warning
|
// TODO: I wonder if its plausible to merge a new cloud-config into an existing one on upgrade - so for now, i'm only turning off the warning
|
||||||
log.Warn("Cloud-config not provided: you might need to provide cloud-config on bootDir with ssh_authorized_keys")
|
log.Warn("Cloud-config not provided: you might need to provide cloud-config on boot with ssh_authorized_keys")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
os.MkdirAll("/opt", 0755)
|
os.MkdirAll("/opt", 0755)
|
||||||
@ -410,7 +410,6 @@ func layDownOS(image, installType, cloudConfig, device, partition, statedir, kap
|
|||||||
//cloudConfig := SCRIPTS_DIR + "/conf/empty.yml" //${cloudConfig:-"${SCRIPTS_DIR}/conf/empty.yml"}
|
//cloudConfig := SCRIPTS_DIR + "/conf/empty.yml" //${cloudConfig:-"${SCRIPTS_DIR}/conf/empty.yml"}
|
||||||
CONSOLE := "tty0"
|
CONSOLE := "tty0"
|
||||||
baseName := "/mnt/new_img"
|
baseName := "/mnt/new_img"
|
||||||
bootDir := "boot/"
|
|
||||||
kernelArgs := "printk.devkmsg=on rancher.state.dev=LABEL=RANCHER_STATE rancher.state.wait" // console="+CONSOLE
|
kernelArgs := "printk.devkmsg=on rancher.state.dev=LABEL=RANCHER_STATE rancher.state.wait" // console="+CONSOLE
|
||||||
if statedir != "" {
|
if statedir != "" {
|
||||||
kernelArgs = kernelArgs + " rancher.state.directory=" + statedir
|
kernelArgs = kernelArgs + " rancher.state.directory=" + statedir
|
||||||
@ -432,12 +431,12 @@ func layDownOS(image, installType, cloudConfig, device, partition, statedir, kap
|
|||||||
case "generic":
|
case "generic":
|
||||||
log.Debugf("formatAndMount")
|
log.Debugf("formatAndMount")
|
||||||
var err error
|
var err error
|
||||||
device, partition, err = formatAndMount(baseName, bootDir, device, partition)
|
device, partition, err = formatAndMount(baseName, device, partition)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("formatAndMount %s", err)
|
log.Errorf("formatAndMount %s", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = installSyslinux(device, baseName, bootDir, diskType)
|
err = installSyslinux(device, baseName, diskType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("installSyslinux %s", err)
|
log.Errorf("installSyslinux %s", err)
|
||||||
return err
|
return err
|
||||||
@ -449,7 +448,7 @@ func layDownOS(image, installType, cloudConfig, device, partition, statedir, kap
|
|||||||
}
|
}
|
||||||
case "arm":
|
case "arm":
|
||||||
var err error
|
var err error
|
||||||
device, partition, err = formatAndMount(baseName, bootDir, device, partition)
|
device, partition, err = formatAndMount(baseName, device, partition)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -459,45 +458,45 @@ func layDownOS(image, installType, cloudConfig, device, partition, statedir, kap
|
|||||||
case "amazon-ebs-hvm":
|
case "amazon-ebs-hvm":
|
||||||
CONSOLE = "ttyS0"
|
CONSOLE = "ttyS0"
|
||||||
var err error
|
var err error
|
||||||
device, partition, err = formatAndMount(baseName, bootDir, device, partition)
|
device, partition, err = formatAndMount(baseName, device, partition)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if installType == "amazon-ebs-hvm" {
|
if installType == "amazon-ebs-hvm" {
|
||||||
installSyslinux(device, baseName, bootDir, diskType)
|
installSyslinux(device, baseName, diskType)
|
||||||
}
|
}
|
||||||
//# AWS Networking recommends disabling.
|
//# AWS Networking recommends disabling.
|
||||||
seedData(baseName, cloudConfig, FILES)
|
seedData(baseName, cloudConfig, FILES)
|
||||||
case "googlecompute":
|
case "googlecompute":
|
||||||
CONSOLE = "ttyS0"
|
CONSOLE = "ttyS0"
|
||||||
var err error
|
var err error
|
||||||
device, partition, err = formatAndMount(baseName, bootDir, device, partition)
|
device, partition, err = formatAndMount(baseName, device, partition)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
installSyslinux(device, baseName, bootDir, diskType)
|
installSyslinux(device, baseName, diskType)
|
||||||
seedData(baseName, cloudConfig, FILES)
|
seedData(baseName, cloudConfig, FILES)
|
||||||
case "noformat":
|
case "noformat":
|
||||||
var err error
|
var err error
|
||||||
device, partition, err = mountdevice(baseName, bootDir, device, partition, false)
|
device, partition, err = install.MountDevice(baseName, device, partition, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
installSyslinux(device, baseName, bootDir, diskType)
|
installSyslinux(device, baseName, diskType)
|
||||||
if err := os.MkdirAll(filepath.Join(baseName, statedir), 0755); err != nil {
|
if err := os.MkdirAll(filepath.Join(baseName, statedir), 0755); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "raid":
|
case "raid":
|
||||||
var err error
|
var err error
|
||||||
device, partition, err = mountdevice(baseName, bootDir, device, partition, false)
|
device, partition, err = install.MountDevice(baseName, device, partition, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
installSyslinux(device, baseName, bootDir, diskType)
|
installSyslinux(device, baseName, diskType)
|
||||||
case "bootstrap":
|
case "bootstrap":
|
||||||
CONSOLE = "ttyS0"
|
CONSOLE = "ttyS0"
|
||||||
var err error
|
var err error
|
||||||
device, partition, err = mountdevice(baseName, bootDir, device, partition, true)
|
device, partition, err = install.MountDevice(baseName, device, partition, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -507,39 +506,39 @@ func layDownOS(image, installType, cloudConfig, device, partition, statedir, kap
|
|||||||
fallthrough
|
fallthrough
|
||||||
case "upgrade":
|
case "upgrade":
|
||||||
var err error
|
var err error
|
||||||
device, partition, err = mountdevice(baseName, bootDir, device, partition, false)
|
device, partition, err = install.MountDevice(baseName, device, partition, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Debugf("upgrading - %s, %s, %s, %s", device, baseName, bootDir, diskType)
|
log.Debugf("upgrading - %s, %s, %s, %s", device, baseName, diskType)
|
||||||
// TODO: detect pv-grub, and don't kill it with syslinux
|
// TODO: detect pv-grub, and don't kill it with syslinux
|
||||||
upgradeBootloader(device, baseName, bootDir, diskType)
|
upgradeBootloader(device, baseName, diskType)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unexpected install type %s", installType)
|
return fmt.Errorf("unexpected install type %s", installType)
|
||||||
}
|
}
|
||||||
kernelArgs = kernelArgs + " console=" + CONSOLE
|
kernelArgs = kernelArgs + " console=" + CONSOLE
|
||||||
|
|
||||||
if kappend == "" {
|
if kappend == "" {
|
||||||
preservedAppend, _ := ioutil.ReadFile(filepath.Join(baseName, bootDir+"append"))
|
preservedAppend, _ := ioutil.ReadFile(filepath.Join(baseName, install.BootDir+"append"))
|
||||||
kappend = string(preservedAppend)
|
kappend = string(preservedAppend)
|
||||||
} else {
|
} else {
|
||||||
ioutil.WriteFile(filepath.Join(baseName, bootDir+"append"), []byte(kappend), 0644)
|
ioutil.WriteFile(filepath.Join(baseName, install.BootDir+"append"), []byte(kappend), 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
if installType == "amazon-ebs-pv" {
|
if installType == "amazon-ebs-pv" {
|
||||||
menu := install.BootVars{
|
menu := install.BootVars{
|
||||||
BaseName: baseName,
|
BaseName: baseName,
|
||||||
BootDir: bootDir,
|
BootDir: install.BootDir,
|
||||||
Timeout: 0,
|
Timeout: 0,
|
||||||
Fallback: 0, // need to be conditional on there being a 'rollback'?
|
Fallback: 0, // need to be conditional on there being a 'rollback'?
|
||||||
Entries: []install.MenuEntry{
|
Entries: []install.MenuEntry{
|
||||||
install.MenuEntry{"RancherOS-current", bootDir, VERSION, kernelArgs, kappend},
|
install.MenuEntry{"RancherOS-current", install.BootDir, VERSION, kernelArgs, kappend},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
install.PvGrubConfig(menu)
|
install.PvGrubConfig(menu)
|
||||||
}
|
}
|
||||||
log.Debugf("installRancher")
|
log.Debugf("installRancher")
|
||||||
currentCfg, err := installRancher(baseName, bootDir, VERSION, DIST, kernelArgs+" "+kappend)
|
currentCfg, err := installRancher(baseName, VERSION, DIST, kernelArgs+" "+kappend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("%s", err)
|
log.Errorf("%s", err)
|
||||||
return err
|
return err
|
||||||
@ -548,55 +547,12 @@ func layDownOS(image, installType, cloudConfig, device, partition, statedir, kap
|
|||||||
|
|
||||||
// Used by upgrade
|
// Used by upgrade
|
||||||
if kexec {
|
if kexec {
|
||||||
vmlinuzFile, initrdFile, err := readSyslinuxCfg(currentCfg)
|
power.Kexec(currentCfg, kernelArgs+" "+kappend)
|
||||||
if err != nil {
|
|
||||||
log.Errorf("%s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// kexec -l ${DIST}/vmlinuz --initrd=${DIST}/initrd --append="${kernelArgs} ${APPEND}" -f
|
|
||||||
cmd := exec.Command(
|
|
||||||
"kexec",
|
|
||||||
"-l", DIST+"/"+vmlinuzFile,
|
|
||||||
"--initrd", DIST+"/"+initrdFile,
|
|
||||||
"--append", "'"+kernelArgs+" "+kappend+"'",
|
|
||||||
"-f")
|
|
||||||
log.Debugf("Run(%#v)", cmd)
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
if _, err := cmd.Output(); err != nil {
|
|
||||||
log.Errorf("Failed to kexec: %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Infof("kexec'd to new install")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readSyslinuxCfg(currentCfg string) (string, string, error) {
|
|
||||||
vmlinuzFile := ""
|
|
||||||
initrdFile := ""
|
|
||||||
// Need to parse currentCfg for the lines:
|
|
||||||
// KERNEL ../vmlinuz-4.9.18-rancher^M
|
|
||||||
// INITRD ../initrd-41e02e6-dirty^M
|
|
||||||
buf, err := ioutil.ReadFile(currentCfg)
|
|
||||||
if err != nil {
|
|
||||||
return vmlinuzFile, initrdFile, err
|
|
||||||
}
|
|
||||||
s := bufio.NewScanner(bytes.NewReader(buf))
|
|
||||||
for s.Scan() {
|
|
||||||
line := strings.TrimSpace(s.Text())
|
|
||||||
if strings.HasPrefix(line, "KERNEL") {
|
|
||||||
vmlinuzFile = strings.TrimSpace(strings.TrimPrefix(line, "KERNEL"))
|
|
||||||
vmlinuzFile = filepath.Base(vmlinuzFile)
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(line, "INITRD") {
|
|
||||||
initrdFile = strings.TrimSpace(strings.TrimPrefix(line, "INITRD"))
|
|
||||||
initrdFile = filepath.Base(initrdFile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return vmlinuzFile, initrdFile, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// files is an array of 'sourcefile:destination' - but i've not seen any examples of it being used.
|
// files is an array of 'sourcefile:destination' - but i've not seen any examples of it being used.
|
||||||
func seedData(baseName, cloudData string, files []string) error {
|
func seedData(baseName, cloudData string, files []string) error {
|
||||||
log.Debugf("seedData")
|
log.Debugf("seedData")
|
||||||
@ -772,59 +728,7 @@ func formatdevice(device, partition string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mountdevice(baseName, bootDir, device, partition string, raw bool) (string, string, error) {
|
func formatAndMount(baseName, device, partition string) (string, string, error) {
|
||||||
log.Debugf("mountdevice %s, raw %v", partition, raw)
|
|
||||||
|
|
||||||
if partition == "" {
|
|
||||||
if raw {
|
|
||||||
log.Debugf("util.Mount (raw) %s, %s", partition, baseName)
|
|
||||||
|
|
||||||
cmd := exec.Command("lsblk", "-no", "pkname", partition)
|
|
||||||
log.Debugf("Run(%v)", cmd)
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
device := ""
|
|
||||||
// TODO: out can == "" - this is used to "detect software RAID" which is terrible
|
|
||||||
if out, err := cmd.Output(); err == nil {
|
|
||||||
device = "/dev/" + strings.TrimSpace(string(out))
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("mountdevice return -> d: %s, p: %s", device, partition)
|
|
||||||
return device, partition, util.Mount(partition, baseName, "", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
//rootfs := partition
|
|
||||||
// Don't use ResolveDevice - it can fail, whereas `blkid -L LABEL` works more often
|
|
||||||
|
|
||||||
cfg := config.LoadConfig()
|
|
||||||
if d, _ := util.Blkid("RANCHER_BOOT"); d != "" {
|
|
||||||
partition = d
|
|
||||||
baseName = filepath.Join(baseName, "boot")
|
|
||||||
} else {
|
|
||||||
if dev := util.ResolveDevice(cfg.Rancher.State.Dev); dev != "" {
|
|
||||||
// try the rancher.state.dev setting
|
|
||||||
partition = dev
|
|
||||||
} else {
|
|
||||||
if d, _ := util.Blkid("RANCHER_STATE"); d != "" {
|
|
||||||
partition = d
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmd := exec.Command("lsblk", "-no", "pkname", partition)
|
|
||||||
log.Debugf("Run(%v)", cmd)
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
// TODO: out can == "" - this is used to "detect software RAID" which is terrible
|
|
||||||
if out, err := cmd.Output(); err == nil {
|
|
||||||
device = "/dev/" + strings.TrimSpace(string(out))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
os.MkdirAll(baseName, 0755)
|
|
||||||
cmd := exec.Command("mount", partition, baseName)
|
|
||||||
//cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
|
|
||||||
log.Debugf("mountdevice return2 -> d: %s, p: %s", device, partition)
|
|
||||||
return device, partition, cmd.Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatAndMount(baseName, bootDir, device, partition string) (string, string, error) {
|
|
||||||
log.Debugf("formatAndMount")
|
log.Debugf("formatAndMount")
|
||||||
|
|
||||||
err := formatdevice(device, partition)
|
err := formatdevice(device, partition)
|
||||||
@ -832,31 +736,14 @@ func formatAndMount(baseName, bootDir, device, partition string) (string, string
|
|||||||
log.Errorf("formatdevice %s", err)
|
log.Errorf("formatdevice %s", err)
|
||||||
return device, partition, err
|
return device, partition, err
|
||||||
}
|
}
|
||||||
device, partition, err = mountdevice(baseName, bootDir, device, partition, false)
|
device, partition, err = install.MountDevice(baseName, device, partition, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("mountdevice %s", err)
|
log.Errorf("mountdevice %s", err)
|
||||||
return device, partition, err
|
return device, partition, err
|
||||||
}
|
}
|
||||||
//err = createbootDirs(baseName, bootDir)
|
|
||||||
//if err != nil {
|
|
||||||
// log.Errorf("createbootDirs %s", err)
|
|
||||||
// return bootDir, err
|
|
||||||
//}
|
|
||||||
return device, partition, nil
|
return device, partition, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NOPEcreatebootDir(baseName, bootDir string) error {
|
|
||||||
log.Debugf("createbootDirs")
|
|
||||||
|
|
||||||
if err := os.MkdirAll(filepath.Join(baseName, bootDir+"grub"), 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(filepath.Join(baseName, bootDir+"syslinux"), 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setBootable(device, diskType string) error {
|
func setBootable(device, diskType string) error {
|
||||||
// TODO make conditional - if there is a bootable device already, don't break it
|
// TODO make conditional - if there is a bootable device already, don't break it
|
||||||
// TODO: make RANCHER_BOOT bootable - it might not be device 1
|
// TODO: make RANCHER_BOOT bootable - it might not be device 1
|
||||||
@ -875,10 +762,10 @@ func setBootable(device, diskType string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func upgradeBootloader(device, baseName, bootDir, diskType string) error {
|
func upgradeBootloader(device, baseName, diskType string) error {
|
||||||
log.Debugf("start upgradeBootloader")
|
log.Debugf("start upgradeBootloader")
|
||||||
|
|
||||||
grubDir := filepath.Join(baseName, bootDir+"grub")
|
grubDir := filepath.Join(baseName, install.BootDir+"grub")
|
||||||
if _, err := os.Stat(grubDir); os.IsNotExist(err) {
|
if _, err := os.Stat(grubDir); os.IsNotExist(err) {
|
||||||
log.Debugf("%s does not exist - no need to upgrade bootloader", grubDir)
|
log.Debugf("%s does not exist - no need to upgrade bootloader", grubDir)
|
||||||
// we've already upgraded
|
// we've already upgraded
|
||||||
@ -886,12 +773,12 @@ func upgradeBootloader(device, baseName, bootDir, diskType string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// deal with systems which were previously upgraded, then rolled back, and are now being re-upgraded
|
// deal with systems which were previously upgraded, then rolled back, and are now being re-upgraded
|
||||||
grubBackup := filepath.Join(baseName, bootDir+"grub_backup")
|
grubBackup := filepath.Join(baseName, install.BootDir+"grub_backup")
|
||||||
if err := os.RemoveAll(grubBackup); err != nil {
|
if err := os.RemoveAll(grubBackup); err != nil {
|
||||||
log.Errorf("RemoveAll (%s): %s", grubBackup, err)
|
log.Errorf("RemoveAll (%s): %s", grubBackup, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
backupSyslinuxDir := filepath.Join(baseName, bootDir+"syslinux_backup")
|
backupSyslinuxDir := filepath.Join(baseName, install.BootDir+"syslinux_backup")
|
||||||
if _, err := os.Stat(backupSyslinuxDir); !os.IsNotExist(err) {
|
if _, err := os.Stat(backupSyslinuxDir); !os.IsNotExist(err) {
|
||||||
backupSyslinuxLdlinuxSys := filepath.Join(backupSyslinuxDir, "ldlinux.sys")
|
backupSyslinuxLdlinuxSys := filepath.Join(backupSyslinuxDir, "ldlinux.sys")
|
||||||
if _, err := os.Stat(backupSyslinuxLdlinuxSys); !os.IsNotExist(err) {
|
if _, err := os.Stat(backupSyslinuxLdlinuxSys); !os.IsNotExist(err) {
|
||||||
@ -914,7 +801,7 @@ func upgradeBootloader(device, baseName, bootDir, diskType string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
syslinuxDir := filepath.Join(baseName, bootDir+"syslinux")
|
syslinuxDir := filepath.Join(baseName, install.BootDir+"syslinux")
|
||||||
// it seems that v0.5.0 didn't have a syslinux dir, while 0.7 does
|
// it seems that v0.5.0 didn't have a syslinux dir, while 0.7 does
|
||||||
if _, err := os.Stat(syslinuxDir); !os.IsNotExist(err) {
|
if _, err := os.Stat(syslinuxDir); !os.IsNotExist(err) {
|
||||||
if err := os.Rename(syslinuxDir, backupSyslinuxDir); err != nil {
|
if err := os.Rename(syslinuxDir, backupSyslinuxDir); err != nil {
|
||||||
@ -935,15 +822,15 @@ func upgradeBootloader(device, baseName, bootDir, diskType string) error {
|
|||||||
|
|
||||||
cfg = strings.Replace(cfg, "current", "previous", -1)
|
cfg = strings.Replace(cfg, "current", "previous", -1)
|
||||||
// TODO consider removing the APPEND line - as the global.cfg should have the same result
|
// TODO consider removing the APPEND line - as the global.cfg should have the same result
|
||||||
ioutil.WriteFile(filepath.Join(baseName, bootDir, "linux-current.cfg"), []byte(cfg), 0644)
|
ioutil.WriteFile(filepath.Join(baseName, install.BootDir, "linux-current.cfg"), []byte(cfg), 0644)
|
||||||
|
|
||||||
lines := strings.Split(cfg, "\n")
|
lines := strings.Split(cfg, "\n")
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
line = strings.TrimSpace(line)
|
line = strings.TrimSpace(line)
|
||||||
if strings.HasPrefix(line, "APPEND") {
|
if strings.HasPrefix(line, "APPEND") {
|
||||||
log.Errorf("write new (%s) %s", filepath.Join(baseName, bootDir, "global.cfg"), err)
|
log.Errorf("write new (%s) %s", filepath.Join(baseName, install.BootDir, "global.cfg"), err)
|
||||||
// TODO: need to append any extra's the user specified
|
// TODO: need to append any extra's the user specified
|
||||||
ioutil.WriteFile(filepath.Join(baseName, bootDir, "global.cfg"), []byte(cfg), 0644)
|
ioutil.WriteFile(filepath.Join(baseName, install.BootDir, "global.cfg"), []byte(cfg), 0644)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -951,10 +838,10 @@ func upgradeBootloader(device, baseName, bootDir, diskType string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return installSyslinux(device, baseName, bootDir, diskType)
|
return installSyslinux(device, baseName, diskType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func installSyslinux(device, baseName, bootDir, diskType string) error {
|
func installSyslinux(device, baseName, diskType string) error {
|
||||||
log.Debugf("installSyslinux(%s)", device)
|
log.Debugf("installSyslinux(%s)", device)
|
||||||
|
|
||||||
mbrFile := "mbr.bin"
|
mbrFile := "mbr.bin"
|
||||||
@ -1004,7 +891,7 @@ func installSyslinux(device, baseName, bootDir, diskType string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sysLinuxDir := filepath.Join(baseName, bootDir, "syslinux")
|
sysLinuxDir := filepath.Join(baseName, install.BootDir, "syslinux")
|
||||||
if err := os.MkdirAll(sysLinuxDir, 0755); err != nil {
|
if err := os.MkdirAll(sysLinuxDir, 0755); err != nil {
|
||||||
log.Errorf("MkdirAll(%s)): %s", sysLinuxDir, err)
|
log.Errorf("MkdirAll(%s)): %s", sysLinuxDir, err)
|
||||||
//return err
|
//return err
|
||||||
@ -1037,13 +924,13 @@ func installSyslinux(device, baseName, bootDir, diskType string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func installRancher(baseName, bootDir, VERSION, DIST, kappend string) (string, error) {
|
func installRancher(baseName, VERSION, DIST, kappend string) (string, error) {
|
||||||
log.Debugf("installRancher")
|
log.Debugf("installRancher")
|
||||||
|
|
||||||
// detect if there already is a linux-current.cfg, if so, move it to linux-previous.cfg,
|
// detect if there already is a linux-current.cfg, if so, move it to linux-previous.cfg,
|
||||||
currentCfg := filepath.Join(baseName, bootDir, "linux-current.cfg")
|
currentCfg := filepath.Join(baseName, install.BootDir, "linux-current.cfg")
|
||||||
if _, err := os.Stat(currentCfg); !os.IsNotExist(err) {
|
if _, err := os.Stat(currentCfg); !os.IsNotExist(err) {
|
||||||
previousCfg := filepath.Join(baseName, bootDir, "linux-previous.cfg")
|
previousCfg := filepath.Join(baseName, install.BootDir, "linux-previous.cfg")
|
||||||
if _, err := os.Stat(previousCfg); !os.IsNotExist(err) {
|
if _, err := os.Stat(previousCfg); !os.IsNotExist(err) {
|
||||||
if err := os.Remove(previousCfg); err != nil {
|
if err := os.Remove(previousCfg); err != nil {
|
||||||
return currentCfg, err
|
return currentCfg, err
|
||||||
@ -1059,19 +946,19 @@ func installRancher(baseName, bootDir, VERSION, DIST, kappend string) (string, e
|
|||||||
if file.IsDir() {
|
if file.IsDir() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := dfs.CopyFile(filepath.Join(DIST, file.Name()), filepath.Join(baseName, bootDir), file.Name()); err != nil {
|
if err := dfs.CopyFile(filepath.Join(DIST, file.Name()), filepath.Join(baseName, install.BootDir), file.Name()); err != nil {
|
||||||
log.Errorf("copy %s: %s", file.Name(), err)
|
log.Errorf("copy %s: %s", file.Name(), err)
|
||||||
//return err
|
//return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// the general INCLUDE syslinuxcfg
|
// the general INCLUDE syslinuxcfg
|
||||||
if err := dfs.CopyFile(filepath.Join(DIST, "isolinux", "isolinux.cfg"), filepath.Join(baseName, bootDir, "syslinux"), "syslinux.cfg"); err != nil {
|
if err := dfs.CopyFile(filepath.Join(DIST, "isolinux", "isolinux.cfg"), filepath.Join(baseName, install.BootDir, "syslinux"), "syslinux.cfg"); err != nil {
|
||||||
log.Errorf("copy global syslinux.cfgS%s: %s", "syslinux.cfg", err)
|
log.Errorf("copy global syslinux.cfgS%s: %s", "syslinux.cfg", err)
|
||||||
//return err
|
//return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// The global.cfg INCLUDE - useful for over-riding the APPEND line
|
// The global.cfg INCLUDE - useful for over-riding the APPEND line
|
||||||
globalFile := filepath.Join(filepath.Join(baseName, bootDir), "global.cfg")
|
globalFile := filepath.Join(filepath.Join(baseName, install.BootDir), "global.cfg")
|
||||||
if _, err := os.Stat(globalFile); !os.IsNotExist(err) {
|
if _, err := os.Stat(globalFile); !os.IsNotExist(err) {
|
||||||
err := ioutil.WriteFile(globalFile, []byte("APPEND "+kappend), 0644)
|
err := ioutil.WriteFile(globalFile, []byte("APPEND "+kappend), 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,5 +1,18 @@
|
|||||||
package install
|
package install
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rancher/os/config"
|
||||||
|
"github.com/rancher/os/log"
|
||||||
|
"github.com/rancher/os/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
const BootDir = "boot/"
|
||||||
|
|
||||||
type MenuEntry struct {
|
type MenuEntry struct {
|
||||||
Name, BootDir, Version, KernelArgs, Append string
|
Name, BootDir, Version, KernelArgs, Append string
|
||||||
}
|
}
|
||||||
@ -9,3 +22,55 @@ type BootVars struct {
|
|||||||
Fallback int
|
Fallback int
|
||||||
Entries []MenuEntry
|
Entries []MenuEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MountDevice(baseName, device, partition string, raw bool) (string, string, error) {
|
||||||
|
log.Debugf("mountdevice %s, raw %v", partition, raw)
|
||||||
|
|
||||||
|
if partition == "" {
|
||||||
|
if raw {
|
||||||
|
log.Debugf("util.Mount (raw) %s, %s", partition, baseName)
|
||||||
|
|
||||||
|
cmd := exec.Command("lsblk", "-no", "pkname", partition)
|
||||||
|
log.Debugf("Run(%v)", cmd)
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
device := ""
|
||||||
|
// TODO: out can == "" - this is used to "detect software RAID" which is terrible
|
||||||
|
if out, err := cmd.Output(); err == nil {
|
||||||
|
device = "/dev/" + strings.TrimSpace(string(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("mountdevice return -> d: %s, p: %s", device, partition)
|
||||||
|
return device, partition, util.Mount(partition, baseName, "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
//rootfs := partition
|
||||||
|
// Don't use ResolveDevice - it can fail, whereas `blkid -L LABEL` works more often
|
||||||
|
|
||||||
|
cfg := config.LoadConfig()
|
||||||
|
if d, _ := util.Blkid("RANCHER_BOOT"); d != "" {
|
||||||
|
partition = d
|
||||||
|
baseName = filepath.Join(baseName, BootDir)
|
||||||
|
} else {
|
||||||
|
if dev := util.ResolveDevice(cfg.Rancher.State.Dev); dev != "" {
|
||||||
|
// try the rancher.state.dev setting
|
||||||
|
partition = dev
|
||||||
|
} else {
|
||||||
|
if d, _ := util.Blkid("RANCHER_STATE"); d != "" {
|
||||||
|
partition = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd := exec.Command("lsblk", "-no", "pkname", partition)
|
||||||
|
log.Debugf("Run(%v)", cmd)
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
// TODO: out can == "" - this is used to "detect software RAID" which is terrible
|
||||||
|
if out, err := cmd.Output(); err == nil {
|
||||||
|
device = "/dev/" + strings.TrimSpace(string(out))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.MkdirAll(baseName, 0755)
|
||||||
|
cmd := exec.Command("mount", partition, baseName)
|
||||||
|
//cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
|
||||||
|
log.Debugf("mountdevice return2 -> d: %s, p: %s", device, partition)
|
||||||
|
return device, partition, cmd.Run()
|
||||||
|
}
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
package install
|
package install
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/rancher/os/log"
|
"github.com/rancher/os/log"
|
||||||
)
|
)
|
||||||
@ -43,3 +47,48 @@ DEFAULT RancherOS-current
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ReadGlobalCfg(globalCfg string) (string, error) {
|
||||||
|
append := ""
|
||||||
|
buf, err := ioutil.ReadFile(globalCfg)
|
||||||
|
if err != nil {
|
||||||
|
return append, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s := bufio.NewScanner(bytes.NewReader(buf))
|
||||||
|
for s.Scan() {
|
||||||
|
line := strings.TrimSpace(s.Text())
|
||||||
|
if strings.HasPrefix(line, "APPEND") {
|
||||||
|
append = strings.TrimSpace(strings.TrimPrefix(line, "APPEND"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return append, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadSyslinuxCfg(currentCfg string) (string, string, error) {
|
||||||
|
vmlinuzFile := ""
|
||||||
|
initrdFile := ""
|
||||||
|
// Need to parse currentCfg for the lines:
|
||||||
|
// KERNEL ../vmlinuz-4.9.18-rancher^M
|
||||||
|
// INITRD ../initrd-41e02e6-dirty^M
|
||||||
|
buf, err := ioutil.ReadFile(currentCfg)
|
||||||
|
if err != nil {
|
||||||
|
return vmlinuzFile, initrdFile, err
|
||||||
|
}
|
||||||
|
|
||||||
|
DIST := filepath.Dir(currentCfg)
|
||||||
|
|
||||||
|
s := bufio.NewScanner(bytes.NewReader(buf))
|
||||||
|
for s.Scan() {
|
||||||
|
line := strings.TrimSpace(s.Text())
|
||||||
|
if strings.HasPrefix(line, "KERNEL") {
|
||||||
|
vmlinuzFile = strings.TrimSpace(strings.TrimPrefix(line, "KERNEL"))
|
||||||
|
vmlinuzFile = filepath.Join(DIST, filepath.Base(vmlinuzFile))
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(line, "INITRD") {
|
||||||
|
initrdFile = strings.TrimSpace(strings.TrimPrefix(line, "INITRD"))
|
||||||
|
initrdFile = filepath.Join(DIST, filepath.Base(initrdFile))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vmlinuzFile, initrdFile, err
|
||||||
|
}
|
||||||
|
@ -19,6 +19,9 @@ import (
|
|||||||
"github.com/rancher/os/util"
|
"github.com/rancher/os/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// You can't shutdown the system from a process in console because we want to stop the console container.
|
||||||
|
// If you do that you kill yourself. So we spawn a separate container to do power operations
|
||||||
|
// This can up because on shutdown we want ssh to gracefully die, terminating ssh connections and not just hanging tcp session
|
||||||
func runDocker(name string) error {
|
func runDocker(name string) error {
|
||||||
if os.ExpandEnv("${IN_DOCKER}") == "true" {
|
if os.ExpandEnv("${IN_DOCKER}") == "true" {
|
||||||
return nil
|
return nil
|
||||||
@ -100,40 +103,24 @@ func runDocker(name string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func common(name string) {
|
func reboot(name string, force bool, code uint) {
|
||||||
if os.Geteuid() != 0 {
|
if os.Geteuid() != 0 {
|
||||||
log.Fatalf("%s: Need to be root", os.Args[0])
|
log.Fatalf("%s: Need to be root", os.Args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !force {
|
||||||
if err := runDocker(name); err != nil {
|
if err := runDocker(name); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func Off() {
|
|
||||||
common("poweroff")
|
|
||||||
reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Reboot() {
|
|
||||||
common("reboot")
|
|
||||||
reboot(syscall.LINUX_REBOOT_CMD_RESTART)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Halt() {
|
|
||||||
common("halt")
|
|
||||||
reboot(syscall.LINUX_REBOOT_CMD_HALT)
|
|
||||||
}
|
|
||||||
|
|
||||||
func reboot(code uint) {
|
|
||||||
err := shutDownContainers()
|
err := shutDownContainers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
syscall.Sync()
|
syscall.Sync()
|
||||||
|
|
||||||
err = syscall.Reboot(int(code))
|
err := syscall.Reboot(int(code))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,28 @@
|
|||||||
package power
|
package power
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/rancher/os/cmd/control/install"
|
||||||
"github.com/rancher/os/config"
|
"github.com/rancher/os/config"
|
||||||
"github.com/rancher/os/log"
|
"github.com/rancher/os/log"
|
||||||
|
"github.com/rancher/os/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Main() {
|
var (
|
||||||
|
haltFlag bool
|
||||||
|
poweroffFlag bool
|
||||||
|
rebootFlag bool
|
||||||
|
forceFlag bool
|
||||||
|
kexecFlag bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func Shutdown() {
|
||||||
log.InitLogger()
|
log.InitLogger()
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
|
|
||||||
@ -16,33 +30,181 @@ func Main() {
|
|||||||
app.Usage = "Control and configure RancherOS"
|
app.Usage = "Control and configure RancherOS"
|
||||||
app.Version = config.Version
|
app.Version = config.Version
|
||||||
app.Author = "Rancher Labs, Inc."
|
app.Author = "Rancher Labs, Inc."
|
||||||
app.Email = "sid@rancher.com"
|
|
||||||
app.EnableBashCompletion = true
|
app.EnableBashCompletion = true
|
||||||
app.Action = shutdown
|
app.Action = shutdown
|
||||||
app.Flags = []cli.Flag{
|
app.Flags = []cli.Flag{
|
||||||
cli.StringFlag{
|
// --no-wall
|
||||||
Name: "r, R",
|
// Do not send wall message before halt, power-off,
|
||||||
Usage: "reboot after shutdown",
|
// reboot.
|
||||||
},
|
|
||||||
cli.StringFlag{
|
// halt, poweroff, reboot ONLY
|
||||||
Name: "h",
|
// -f, --force
|
||||||
Usage: "halt the system",
|
// Force immediate halt, power-off, reboot. Do not
|
||||||
|
// contact the init system.
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "f, force",
|
||||||
|
Usage: "Force immediate halt, power-off, reboot. Do not contact the init system.",
|
||||||
|
Destination: &forceFlag,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// -w, --wtmp-only
|
||||||
|
// Only write wtmp shutdown entry, do not actually
|
||||||
|
// halt, power-off, reboot.
|
||||||
|
|
||||||
|
// -d, --no-wtmp
|
||||||
|
// Do not write wtmp shutdown entry.
|
||||||
|
|
||||||
|
// -n, --no-sync
|
||||||
|
// Don't sync hard disks/storage media before halt,
|
||||||
|
// power-off, reboot.
|
||||||
|
|
||||||
|
// shutdown ONLY
|
||||||
|
// -h
|
||||||
|
// Equivalent to --poweroff, unless --halt is
|
||||||
|
// specified.
|
||||||
|
|
||||||
|
// -k
|
||||||
|
// Do not halt, power-off, reboot, just write wall
|
||||||
|
// message.
|
||||||
|
|
||||||
|
// -c
|
||||||
|
// Cancel a pending shutdown. This may be used
|
||||||
|
// cancel the effect of an invocation of shutdown
|
||||||
|
// with a time argument that is not "+0" or "now".
|
||||||
|
|
||||||
}
|
}
|
||||||
app.HideHelp = true
|
// -H, --halt
|
||||||
|
// Halt the machine.
|
||||||
|
if app.Name == "halt" {
|
||||||
|
app.Flags = append(app.Flags, cli.BoolTFlag{
|
||||||
|
Name: "H, halt",
|
||||||
|
Usage: "halt the machine",
|
||||||
|
Destination: &haltFlag,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
app.Flags = append(app.Flags, cli.BoolFlag{
|
||||||
|
Name: "H, halt",
|
||||||
|
Usage: "halt the machine",
|
||||||
|
Destination: &haltFlag,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// -P, --poweroff
|
||||||
|
// Power-off the machine (the default for shutdown cmd).
|
||||||
|
if app.Name == "poweroff" {
|
||||||
|
app.Flags = append(app.Flags, cli.BoolTFlag{
|
||||||
|
Name: "P, poweroff",
|
||||||
|
Usage: "halt the machine",
|
||||||
|
Destination: &poweroffFlag,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
app.Flags = append(app.Flags, cli.BoolFlag{
|
||||||
|
Name: "P, poweroff",
|
||||||
|
Usage: "halt the machine",
|
||||||
|
Destination: &poweroffFlag,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// -r, --reboot
|
||||||
|
// Reboot the machine.
|
||||||
|
if app.Name == "reboot" {
|
||||||
|
app.Flags = append(app.Flags, cli.BoolTFlag{
|
||||||
|
Name: "r, reboot",
|
||||||
|
Usage: "reboot after shutdown",
|
||||||
|
Destination: &rebootFlag,
|
||||||
|
})
|
||||||
|
// OR? maybe implement it as a `kexec` cli tool?
|
||||||
|
app.Flags = append(app.Flags, cli.BoolFlag{
|
||||||
|
Name: "kexec",
|
||||||
|
Usage: "kexec the default kernel",
|
||||||
|
Destination: &kexecFlag,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
app.Flags = append(app.Flags, cli.BoolFlag{
|
||||||
|
Name: "r, reboot",
|
||||||
|
Usage: "reboot after shutdown",
|
||||||
|
Destination: &rebootFlag,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//TODO: add the time and msg flags...
|
||||||
app.Run(os.Args)
|
app.Run(os.Args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func shutdown(c *cli.Context) error {
|
func Kexec(bootDir, append string) error {
|
||||||
common("")
|
cfgFile := filepath.Join(bootDir, "linux-current.cfg")
|
||||||
reboot := c.String("r")
|
vmlinuzFile, initrdFile, err := install.ReadSyslinuxCfg(cfgFile)
|
||||||
poweroff := c.String("h")
|
if err != nil {
|
||||||
|
log.Errorf("%s", err)
|
||||||
if reboot == "now" {
|
return err
|
||||||
Reboot()
|
|
||||||
} else if poweroff == "now" {
|
|
||||||
Off()
|
|
||||||
}
|
}
|
||||||
|
globalCfgFile := filepath.Join(bootDir, "global.cfg")
|
||||||
|
if append == "" {
|
||||||
|
append, err = install.ReadGlobalCfg(globalCfgFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("%s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: read global.cfg if append == ""
|
||||||
|
// kexec -l ${DIST}/vmlinuz --initrd=${DIST}/initrd --append="${kernelArgs} ${APPEND}" -f
|
||||||
|
cmd := exec.Command(
|
||||||
|
"kexec",
|
||||||
|
"-l", vmlinuzFile,
|
||||||
|
"--initrd", initrdFile,
|
||||||
|
"--append", "'"+append+"'",
|
||||||
|
"-f")
|
||||||
|
log.Debugf("Run(%#v)", cmd)
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
if _, err := cmd.Output(); err != nil {
|
||||||
|
log.Errorf("Failed to kexec: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Infof("kexec'd to new install")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reboot is used by installation / upgrade
|
||||||
|
// TODO: add kexec option
|
||||||
|
func Reboot() {
|
||||||
|
reboot("reboot", false, syscall.LINUX_REBOOT_CMD_RESTART)
|
||||||
|
}
|
||||||
|
|
||||||
|
func shutdown(c *cli.Context) error {
|
||||||
|
if kexecFlag {
|
||||||
|
if os.Geteuid() != 0 {
|
||||||
|
log.Fatalf("%s: Need to be root", os.Args[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// need to mount boot dir, or `system-docker run -v /:/host -w /host/boot` ?
|
||||||
|
baseName := "/mnt/new_img"
|
||||||
|
_, _, err := install.MountDevice(baseName, "", "", false)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("ERROR: can't Kexec: %s", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer util.Unmount(baseName)
|
||||||
|
return Kexec(filepath.Join(baseName, install.BootDir), "")
|
||||||
|
}
|
||||||
|
// the shutdown command's default is poweroff
|
||||||
|
var powerCmd uint
|
||||||
|
powerCmd = syscall.LINUX_REBOOT_CMD_POWER_OFF
|
||||||
|
if rebootFlag {
|
||||||
|
powerCmd = syscall.LINUX_REBOOT_CMD_RESTART
|
||||||
|
} else if poweroffFlag {
|
||||||
|
powerCmd = syscall.LINUX_REBOOT_CMD_POWER_OFF
|
||||||
|
} else if haltFlag {
|
||||||
|
powerCmd = syscall.LINUX_REBOOT_CMD_HALT
|
||||||
|
}
|
||||||
|
|
||||||
|
timeArg := c.Args().Get(0)
|
||||||
|
if c.App.Name == "shutdown" && timeArg != "" {
|
||||||
|
if timeArg != "now" {
|
||||||
|
err := fmt.Errorf("Sorry, can't parse '%s' as time value (only 'now' supported)", timeArg)
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO: if there are more params, LOG them
|
||||||
|
}
|
||||||
|
|
||||||
|
reboot(c.App.Name, forceFlag, powerCmd)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
12
main.go
12
main.go
@ -29,19 +29,21 @@ var entrypoints = map[string]func(){
|
|||||||
"console.sh": control.ConsoleInitMain,
|
"console.sh": control.ConsoleInitMain,
|
||||||
"docker": docker.Main,
|
"docker": docker.Main,
|
||||||
"dockerlaunch": dfs.Main,
|
"dockerlaunch": dfs.Main,
|
||||||
"halt": power.Halt,
|
|
||||||
"init": osInit.MainInit,
|
"init": osInit.MainInit,
|
||||||
"netconf": network.Main,
|
"netconf": network.Main,
|
||||||
"poweroff": power.Off,
|
|
||||||
"reboot": power.Reboot,
|
|
||||||
"respawn": respawn.Main,
|
|
||||||
"ros-sysinit": sysinit.Main,
|
"ros-sysinit": sysinit.Main,
|
||||||
"shutdown": power.Main,
|
|
||||||
"system-docker": systemdocker.Main,
|
"system-docker": systemdocker.Main,
|
||||||
"wait-for-docker": wait.Main,
|
"wait-for-docker": wait.Main,
|
||||||
"cni-glue": glue.Main,
|
"cni-glue": glue.Main,
|
||||||
"bridge": bridge.Main,
|
"bridge": bridge.Main,
|
||||||
"host-local": hostlocal.Main,
|
"host-local": hostlocal.Main,
|
||||||
|
"respawn": respawn.Main,
|
||||||
|
|
||||||
|
// Power commands
|
||||||
|
"halt": power.Shutdown,
|
||||||
|
"poweroff": power.Shutdown,
|
||||||
|
"reboot": power.Shutdown,
|
||||||
|
"shutdown": power.Shutdown,
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
Loading…
Reference in New Issue
Block a user