Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

locksmithd will not reboot outside of reboot windows, if no semaphore was acquired before #10

Merged
merged 2 commits into from
Aug 3, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 60 additions & 43 deletions locksmithctl/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package main
import "C"

import (
"errors"
"fmt"
"os"
"os/signal"
Expand Down Expand Up @@ -116,6 +117,53 @@ func expBackoff(interval time.Duration) time.Duration {
return interval
}

// waitForRebootWindow get the REBOOT_WINDOW_START, REBOOT_WINDOW_LENGTH
// and check if everything is ok and the get then Periodic. Check if
// it is outside of the reboot window and If yes then sleep and return true.
func waitForRebootWindow() (bool, error) {
var period *timeutil.Periodic

startw := os.Getenv("LOCKSMITHD_REBOOT_WINDOW_START")
if startw == "" {
startw = os.Getenv("REBOOT_WINDOW_START")
}

lengthw := os.Getenv("LOCKSMITHD_REBOOT_WINDOW_LENGTH")
if lengthw == "" {
lengthw = os.Getenv("REBOOT_WINDOW_LENGTH")
}

if (startw == "") != (lengthw == "") {
return false, errors.New("Either both or neither $REBOOT_WINDOW_START and $REBOOT_WINDOW_LENGTH must be set")
}

if startw != "" && lengthw != "" {
p, err := timeutil.ParsePeriodic(startw, lengthw)
if err != nil {
return false, fmt.Errorf("unable to parse periodic: %w", err)
}

period = p
}

if period != nil {
now := time.Now()
sleeptime := period.DurationToStart(now)
if sleeptime > 0 {
dlog.Infof("Reboot window start is %q and length is %q", startw, lengthw)
next := period.Next(time.Now())
dlog.Infof("Next window begins at %s and ends at %s", next.Start, next.End)
dlog.Infof("Waiting for %s to reboot.", sleeptime)
time.Sleep(sleeptime)
return true, nil
}
} else {
dlog.Info("No configured reboot window")
}

return false, nil
}

func (r rebooter) rebootAndSleep() {
// Broadcast a notice, if broadcast found lines to notify, delay the reboot.
delaymins := loginsRebootDelay / time.Minute
Expand All @@ -135,7 +183,8 @@ func (r rebooter) rebootAndSleep() {
}

// lockAndReboot attempts to acquire the lock and reboot the machine in an
// infinite loop. Returns if the reboot failed.
// infinite loop if it is in the reboot window.
// Returns if the reboot failed.
func (r rebooter) lockAndReboot(lck *lock.Lock) {
interval := initialInterval
for {
Expand All @@ -145,6 +194,14 @@ func (r rebooter) lockAndReboot(lck *lock.Lock) {
dlog.Warningf("Failed to acquire lock: %v. Retrying in %v.", err, interval)
time.Sleep(interval)

hasSleep, err := waitForRebootWindow()
if err != nil {
dlog.Fatalf("unable to check reboot window period: %v", err)
}
if hasSleep {
interval = initialInterval
}

continue
}

Expand Down Expand Up @@ -261,8 +318,6 @@ func unlockHeldLocks(strategy string, stop chan struct{}, wg *sync.WaitGroup) {
// attempts to acquire the reboot lock. If the reboot lock is acquired then the
// machine will reboot.
func runDaemon() int {
var period *timeutil.Periodic

strategy := os.Getenv("REBOOT_STRATEGY")

if strategy == "" {
Expand All @@ -274,39 +329,6 @@ func runDaemon() int {
return 0
}

// XXX: REBOOT_WINDOW_* are deprecated in favor of variables with LOCKSMITHD_ prefix,
// but the old ones are read for compatibility.
startw := os.Getenv("LOCKSMITHD_REBOOT_WINDOW_START")
if startw == "" {
startw = os.Getenv("REBOOT_WINDOW_START")
}

lengthw := os.Getenv("LOCKSMITHD_REBOOT_WINDOW_LENGTH")
if lengthw == "" {
lengthw = os.Getenv("REBOOT_WINDOW_LENGTH")
}

if (startw == "") != (lengthw == "") {
dlog.Fatal("Either both or neither $REBOOT_WINDOW_START and $REBOOT_WINDOW_LENGTH must be set")
}

if startw != "" && lengthw != "" {
p, err := timeutil.ParsePeriodic(startw, lengthw)
if err != nil {
dlog.Fatalf("Error parsing reboot window: %s", err)
}

period = p
}

if period != nil {
dlog.Infof("Reboot window start is %q and length is %q", startw, lengthw)
next := period.Next(time.Now())
dlog.Infof("Next window begins at %s and ends at %s", next.Start, next.End)
} else {
dlog.Info("No configured reboot window")
}

coordinatorConf, err := coordinatorconf.New(coordinatorName, strategy)
if err != nil {
dlog.Fatalf("unable to become 'update coordinator': %v", err)
Expand Down Expand Up @@ -364,13 +386,8 @@ func runDaemon() int {
close(stop)
wg.Wait()

if period != nil {
now := time.Now()
sleeptime := period.DurationToStart(now)
if sleeptime > 0 {
dlog.Infof("Waiting for %s to reboot.", sleeptime)
time.Sleep(sleeptime)
}
if _, err := waitForRebootWindow(); err != nil {
dlog.Fatalf("unable to check reboot window period: %v", err)
}

return r.reboot()
Expand Down