diff --git a/CHANGELOG.md b/CHANGELOG.md index 2315a83d..21af5427 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ bugs in actually-released versions. - Show the configured Flamenco Manager name in the webapp's browser window title. - Workers can be marked as 'restartable' by using the `-restart-exit-code N` commandline option. More info in the [Worker Actions documentation](https://flamenco.blender.org/usage/worker-actions/). - The `{timestamp}` placeholder in the render output path is now replaced with a local timestamp (rather than UTC). +- Log more information about the operating system at startup. On Windows this includes the version & edition (like "Core" or "Professional"), and on Linux this includes the distribution, version, and kernel version. - Security updates of some dependencies: - https://pkg.go.dev/vuln/GO-2023-1989 - https://pkg.go.dev/vuln/GO-2023-1990 diff --git a/cmd/flamenco-manager/main.go b/cmd/flamenco-manager/main.go index 7ca961ac..9b3c5409 100644 --- a/cmd/flamenco-manager/main.go +++ b/cmd/flamenco-manager/main.go @@ -39,6 +39,7 @@ import ( "projects.blender.org/studio/flamenco/internal/own_url" "projects.blender.org/studio/flamenco/internal/upnp_ssdp" "projects.blender.org/studio/flamenco/pkg/shaman" + "projects.blender.org/studio/flamenco/pkg/sysinfo" ) var cliArgs struct { @@ -58,11 +59,17 @@ const ( func main() { output := zerolog.ConsoleWriter{Out: colorable.NewColorableStdout(), TimeFormat: time.RFC3339} log.Logger = log.Output(output) + + osDetail, err := sysinfo.Description() + if err != nil { + osDetail = err.Error() + } log.Info(). Str("version", appinfo.ApplicationVersion). Str("git", appinfo.ApplicationGitHash). Str("releaseCycle", appinfo.ReleaseCycle). Str("os", runtime.GOOS). + Str("osDetail", osDetail). Str("arch", runtime.GOARCH). Msgf("starting %v", appinfo.ApplicationName) diff --git a/cmd/flamenco-worker/main.go b/cmd/flamenco-worker/main.go index e6ce3e7c..d42be507 100644 --- a/cmd/flamenco-worker/main.go +++ b/cmd/flamenco-worker/main.go @@ -23,6 +23,7 @@ import ( "projects.blender.org/studio/flamenco/internal/appinfo" "projects.blender.org/studio/flamenco/internal/worker" "projects.blender.org/studio/flamenco/internal/worker/cli_runner" + "projects.blender.org/studio/flamenco/pkg/sysinfo" ) var ( @@ -61,12 +62,18 @@ func main() { output := zerolog.ConsoleWriter{Out: colorable.NewColorableStdout(), TimeFormat: time.RFC3339} log.Logger = log.Output(output) + osDetail, err := sysinfo.Description() + if err != nil { + osDetail = err.Error() + } + log.Info(). Str("version", appinfo.ApplicationVersion). Str("git", appinfo.ApplicationGitHash). Str("releaseCycle", appinfo.ReleaseCycle). - Str("OS", runtime.GOOS). - Str("ARCH", runtime.GOARCH). + Str("os", runtime.GOOS). + Str("osDetail", osDetail). + Str("arch", runtime.GOARCH). Int("pid", os.Getpid()). Msgf("starting %v Worker", appinfo.ApplicationName) configLogLevel() diff --git a/go.mod b/go.mod index c56fe36d..3d636714 100644 --- a/go.mod +++ b/go.mod @@ -54,6 +54,7 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.1 // indirect + github.com/zcalusic/sysinfo v1.0.1 // indirect golang.org/x/mod v0.8.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect diff --git a/go.sum b/go.sum index 64fab2be..1e8e6188 100644 --- a/go.sum +++ b/go.sum @@ -172,6 +172,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zcalusic/sysinfo v1.0.1 h1:cVh8q3codjh43AGRTa54dJ2Zq+qPejv8n2VWpxKViwc= +github.com/zcalusic/sysinfo v1.0.1/go.mod h1:LxwKwtQdbTIQc65drhjQzYzt0o7jfB80LrrZm7SWn8o= github.com/ziflex/lecho/v3 v3.1.0 h1:65bSzSc0yw7EEhi44lMnkOI877ZzbE7tGDWfYCQXZwI= github.com/ziflex/lecho/v3 v3.1.0/go.mod h1:dwQ6xCAKmSBHhwZ6XmiAiDptD7iklVkW7xQYGUncX0Q= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/pkg/sysinfo/sysinfo.go b/pkg/sysinfo/sysinfo.go new file mode 100644 index 00000000..25fdd19d --- /dev/null +++ b/pkg/sysinfo/sysinfo.go @@ -0,0 +1,15 @@ +package sysinfo + +// SPDX-License-Identifier: GPL-3.0-or-later + +// CanSymlink tries to determine whether the running system can use symbolic +// links. +func CanSymlink() (bool, error) { + return canSymlink() +} + +// Description returns a string that describes the current platform in more +// detail than runtime.GOOS does. +func Description() (string, error) { + return description() +} diff --git a/pkg/sysinfo/sysinfo_darwin.go b/pkg/sysinfo/sysinfo_darwin.go new file mode 100644 index 00000000..b89cd031 --- /dev/null +++ b/pkg/sysinfo/sysinfo_darwin.go @@ -0,0 +1,14 @@ +package sysinfo + +// SPDX-License-Identifier: GPL-3.0-or-later + +// canSymlink always returns true, as symlinking on non-Windows platforms is not +// hard. +func canSymlink() (bool, error) { + return true, nil +} + +func description() (string, error) { + // TODO: figure out how to get more info on macOS. + return "macOS", nil +} diff --git a/pkg/sysinfo/sysinfo_linux.go b/pkg/sysinfo/sysinfo_linux.go new file mode 100644 index 00000000..753eb145 --- /dev/null +++ b/pkg/sysinfo/sysinfo_linux.go @@ -0,0 +1,22 @@ +package sysinfo + +// SPDX-License-Identifier: GPL-3.0-or-later + +import ( + "fmt" + + the_other_sysinfo "github.com/zcalusic/sysinfo" +) + +// canSymlink always returns true, as symlinking on non-Windows platforms is not +// hard. +func canSymlink() (bool, error) { + return true, nil +} + +func description() (string, error) { + var si the_other_sysinfo.SysInfo + si.GetSysInfo() + description := fmt.Sprintf("%s (%s)", si.OS.Name, si.Kernel.Release) + return description, nil +} diff --git a/pkg/sysinfo/sysinfo_windows.go b/pkg/sysinfo/sysinfo_windows.go new file mode 100644 index 00000000..85b5e2f6 --- /dev/null +++ b/pkg/sysinfo/sysinfo_windows.go @@ -0,0 +1,80 @@ +package sysinfo + +// SPDX-License-Identifier: GPL-3.0-or-later + +import ( + "fmt" + + "github.com/rs/zerolog/log" + "golang.org/x/sys/windows/registry" +) + +// editionCanSymlink maps the EditionID key in the registry to a boolean indicating whether +// symlinks are available. +var editionCanSymlink = map[string]bool{ + "Core": false, // Windows Home misses the tool to allow symlinking to a user. + "Professional": true, // Still requires explicit right to be assigned to the user. +} + +// canSymlink tries to determine whether the running system can use symbolic +// links, based on information from the Windows registry. +func canSymlink() (bool, error) { + editionID, err := windowsEditionID() + if err != nil { + return false, fmt.Errorf("determining edition of Windows: %w", err) + } + + canSymlink, found := editionCanSymlink[editionID] + if !found { + log.Warn().Str("editionID", editionID).Msg("unknown Windows edition, assuming it can use symlinks") + return true, nil + } + + return canSymlink, nil +} + +func description() (string, error) { + productName, err := registryReadString( + `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, + "ProductName", + ) + if err != nil { + return "", err + } + + editionID, err := windowsEditionID() + if err != nil { + return "", err + } + + description := fmt.Sprintf("%s (%s)", productName, editionID) + return description, nil +} + +func windowsEditionID() (string, error) { + // Values seen so far: + // - "Professional" + // - "Core" + return registryReadString( + `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, + "EditionID", + ) +} + +func registryReadString(keyPath, valueName string) (string, error) { + regkey, err := registry.OpenKey( + registry.LOCAL_MACHINE, + keyPath, + registry.QUERY_VALUE) + if err != nil { + return "", fmt.Errorf("opening registry key %q: %w", keyPath, err) + } + defer regkey.Close() + + value, _, err := regkey.GetStringValue(valueName) + if err != nil { + return "", fmt.Errorf("reading registry key %q: %w", valueName, err) + } + + return value, nil +}