From e186d033c1b6325a22da3843e9f9eb3145973f6e Mon Sep 17 00:00:00 2001 From: John-Michael Mulesa Date: Fri, 1 Nov 2024 06:30:51 -0700 Subject: [PATCH] Add built-in Windows Update Active Hours maintenance window to Aukera on Windows. PiperOrigin-RevId: 692155843 --- auklib/auklib_darwin.go | 13 ++++++++++++ auklib/auklib_linux.go | 14 ++++++++++++ auklib/auklib_windows.go | 46 ++++++++++++++++++++++++++++++++++++++++ go.mod | 14 ++++++------ go.sum | 8 +++---- main_windows.go | 2 +- schedule/schedule.go | 8 +++++++ window/window.go | 28 ++++++++++++++++++++++++ 8 files changed, 122 insertions(+), 11 deletions(-) diff --git a/auklib/auklib_darwin.go b/auklib/auklib_darwin.go index bce26db..8ddee81 100644 --- a/auklib/auklib_darwin.go +++ b/auklib/auklib_darwin.go @@ -17,6 +17,12 @@ package auklib +import ( + "fmt" + "runtime" + "time" +) + var ( // DataDir defines app data filesystem location. DataDir = "/var/lib/aukera" @@ -30,3 +36,10 @@ var ( // MetricSvc sets platform source for metrics. MetricSvc = "darwin" ) + +// ActiveHours retrieves the user/auto-set active hours times. +// Stubbed out on darwin. +func ActiveHours() (time.Time, time.Time, error) { + var t time.Time + return t, t, fmt.Errorf("ActiveHours: unsupported operating system: %s", runtime.GOOS) +} diff --git a/auklib/auklib_linux.go b/auklib/auklib_linux.go index f192ec6..bb3b9e6 100644 --- a/auklib/auklib_linux.go +++ b/auklib/auklib_linux.go @@ -12,10 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build linux // +build linux package auklib +import ( + "fmt" + "runtime" + "time" +) + var ( // DataDir defines app data filesystem location. DataDir = "/var/lib/aukera" @@ -29,3 +36,10 @@ var ( // MetricRoot sets metric path for all aukera metrics MetricRoot = `/aukera/metrics` ) + +// ActiveHours retrieves the user/auto-set active hours times. +// Stubbed out on linux. +func ActiveHours() (time.Time, time.Time, error) { + var t time.Time + return t, t, fmt.Errorf("ActiveHours: unsupported operating system: %s", runtime.GOOS) +} diff --git a/auklib/auklib_windows.go b/auklib/auklib_windows.go index 82faa2b..4e945c9 100644 --- a/auklib/auklib_windows.go +++ b/auklib/auklib_windows.go @@ -12,13 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build windows // +build windows package auklib import ( + "fmt" "os" "path/filepath" + "time" + + "golang.org/x/sys/windows/registry" ) var ( @@ -33,4 +38,45 @@ var ( MetricRoot = `/aukera/metrics` // MetricSvc sets platform source for metrics. MetricSvc = "aukera" + + activeHoursStart, activeHoursEnd uint64 + activeStartTime, activeEndTime time.Time +) + +const ( + activeHoursPath = `SOFTWARE\Microsoft\WindowsUpdate\UX\Settings\` ) + +// ActiveHours retrieves the user/auto-set active hours times from the registry. +// Returns the start and end times of the active hours window, respectively. +func ActiveHours() (time.Time, time.Time, error) { + + k, err := registry.OpenKey(registry.LOCAL_MACHINE, activeHoursPath, registry.READ) + if err != nil { + return activeStartTime, activeEndTime, err + } + defer k.Close() + + activeHoursStart, _, err = k.GetIntegerValue("ActiveHoursStart") + if err != nil { + return activeStartTime, activeEndTime, fmt.Errorf("unable to get active hours start time: %v", err) + } + + now := time.Now() + activeStartTime = time.Date(now.Year(), now.Month(), now.Day(), int(activeHoursStart), 0, 0, 0, now.Location()) + + activeHoursEnd, _, err = k.GetIntegerValue("ActiveHoursEnd") + if err != nil { + return activeStartTime, activeEndTime, fmt.Errorf("unable to get active hours end time: %v", err) + } + + var day int + if int(activeHoursEnd) < activeStartTime.Hour() { + day = now.Day() + 1 + } else { + day = now.Day() + } + activeEndTime = time.Date(now.Year(), now.Month(), day, int(activeHoursEnd), 0, 0, 0, now.Location()) + + return activeStartTime, activeEndTime, nil +} diff --git a/go.mod b/go.mod index 8db279f..95942ac 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,12 @@ module github.com/google/aukera go 1.18 require ( - github.com/go-chi/chi/v5 v5.0.8 - github.com/google/cabbie v1.0.3-0.20210720165919-9cf1b44a02bb - github.com/google/deck v1.1.0 - github.com/google/go-cmp v0.5.9 - github.com/robfig/cron/v3 v3.0.1 - golang.org/x/sys v0.2.0 + github.com/go-chi/chi/v5 v5.0.8 + github.com/google/cabbie v1.0.3-0.20210720165919-9cf1b44a02bb + github.com/google/deck v0.0.0-20221206151953-9363e9de5515 + github.com/google/go-cmp v0.5.4 + github.com/robfig/cron/v3 v3.0.1 + golang.org/x/sys v0.2.0 ) + +require golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect diff --git a/go.sum b/go.sum index aabb933..c3ab5f1 100644 --- a/go.sum +++ b/go.sum @@ -6,12 +6,11 @@ github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU github.com/google/aukera v0.0.0-20201117230544-d145c8357fea/go.mod h1:oXqTZORBzdwQ6L32YjJmaPajqIV/hoGEouwpFMf4cJE= github.com/google/cabbie v1.0.3-0.20210720165919-9cf1b44a02bb h1:yaCjMrP26G5bOvAkje2w1H79d1/wkWvtEShLR/xL3Ek= github.com/google/cabbie v1.0.3-0.20210720165919-9cf1b44a02bb/go.mod h1:6MmHaUrgfabehCHAIaxdrbmvHSxUVXj3Abs08FMABSo= -github.com/google/deck v1.1.0 h1:kePIz/wtlpsFSx3+sEmYnlBPudF0x3u0QqB91EC8l38= -github.com/google/deck v1.1.0/go.mod h1:VyLix33qBTXGsn4vbF85lWLv/9ushseSAoqS27uX6Zg= +github.com/google/deck v0.0.0-20221206151953-9363e9de5515 h1:cvU37922F9lwM81KV2uYxJA8MsRSDz/Rzi62zsJon88= +github.com/google/deck v0.0.0-20221206151953-9363e9de5515/go.mod h1:DoDv8G58DuLNZF0KysYn0bA/6ZWhmRW3fZE2VnGEH0w= github.com/google/glazier v0.0.0-20210617205946-bf91b619f5d4/go.mod h1:g7oyIhindbeebnBh0hbFua5rv6XUt/nweDwIWdvxirg= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/logger v1.1.0/go.mod h1:w7O8nrRr0xufejBlQMI83MXqRusvREoJdaAxV+CoAB4= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/iamacarpet/go-win64api v0.0.0-20210311141720-fe38760bed28/go.mod h1:oGJx9dz0Ny7HC7U55RZ0Smd6N9p3hXP/+hOFtuYrAxM= @@ -30,5 +29,6 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= diff --git a/main_windows.go b/main_windows.go index 86bf009..f133fc4 100644 --- a/main_windows.go +++ b/main_windows.go @@ -32,7 +32,7 @@ import ( type winSvc struct{} func setup() error { - evt, err := eventlog.Init("aukera") + evt, err := eventlog.InitWithDefaultInstall("aukera") if err != nil { return err } diff --git a/schedule/schedule.go b/schedule/schedule.go index fb77733..2ab1285 100644 --- a/schedule/schedule.go +++ b/schedule/schedule.go @@ -17,6 +17,7 @@ package schedule import ( "fmt" + "runtime" "strings" "time" @@ -66,6 +67,13 @@ func Schedule(names ...string) ([]window.Schedule, error) { if err != nil { return nil, err } + switch runtime.GOOS { + case "windows": + m, err = window.ActiveHoursWindow(m) + if err != nil { + return nil, err + } + } if len(names) == 0 { names = m.Keys() } diff --git a/window/window.go b/window/window.go index b939de1..0584810 100644 --- a/window/window.go +++ b/window/window.go @@ -577,3 +577,31 @@ func reportConfFileMetric(path, result string) { m.Data.AddStringField("file_path", path) m.Set(result) } + +// ActiveHoursWindow retrieves the built-in Active Hours maintenance windows if available. +func ActiveHoursWindow(m Map) (Map, error) { + activeStartTime, activeEndTime, err := auklib.ActiveHours() + if err != nil { + return nil, err + } + activeWindow := Window{ + Name: "active_hours", + Labels: []string{"active_hours"}, + Starts: activeStartTime, + Expires: activeEndTime, + Duration: activeEndTime.Sub(activeStartTime), + Schedule: Schedule{ + Name: "active_hours", + Opens: activeStartTime, + Closes: activeEndTime, + Duration: activeEndTime.Sub(activeStartTime), + }, + } + if activeWindow.Schedule.IsOpen() { + activeWindow.Schedule.State = "open" + } else { + activeWindow.Schedule.State = "closed" + } + m.Add(activeWindow) + return m, nil +}