treewide: introduce osbase package and move things around

All except localregistry moved from metropolis/pkg to osbase,
localregistry moved to metropolis/test as its only used there anyway.

Change-Id: If1a4bf377364bef0ac23169e1b90379c71b06d72
Reviewed-on: https://review.monogon.dev/c/monogon/+/3079
Tested-by: Jenkins CI
Reviewed-by: Serge Bazanski <serge@monogon.tech>
diff --git a/osbase/sysctl/BUILD.bazel b/osbase/sysctl/BUILD.bazel
new file mode 100644
index 0000000..f77c80b
--- /dev/null
+++ b/osbase/sysctl/BUILD.bazel
@@ -0,0 +1,8 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+    name = "sysctl",
+    srcs = ["options.go"],
+    importpath = "source.monogon.dev/osbase/sysctl",
+    visibility = ["//visibility:public"],
+)
diff --git a/osbase/sysctl/options.go b/osbase/sysctl/options.go
new file mode 100644
index 0000000..b5e1e36
--- /dev/null
+++ b/osbase/sysctl/options.go
@@ -0,0 +1,29 @@
+package sysctl
+
+import (
+	"fmt"
+	"os"
+	"path"
+	"strings"
+)
+
+// Options contains sysctl options to apply
+type Options map[string]string
+
+// Apply attempts to apply all options in Options. It aborts on the first
+// one which returns an error when applying.
+func (o Options) Apply() error {
+	for name, value := range o {
+		filePath := path.Join("/proc/sys/", strings.ReplaceAll(name, ".", "/"))
+		optionFile, err := os.OpenFile(filePath, os.O_WRONLY, 0)
+		if err != nil {
+			return fmt.Errorf("failed to set option %v: %w", name, err)
+		}
+		if _, err := optionFile.WriteString(value + "\n"); err != nil {
+			optionFile.Close()
+			return fmt.Errorf("failed to set option %v: %w", name, err)
+		}
+		optionFile.Close() // In a loop, defer'ing could open a lot of FDs
+	}
+	return nil
+}