package sys import ( "fmt" "io" "os" "strings" "golang.org/x/sys/unix" "github.com/cyphar/filepath-securejoin/pathrs-lite" "github.com/cyphar/filepath-securejoin/pathrs-lite/procfs" ) func procfsOpenRoot(proc *procfs.Handle, subpath string, flags int) (*os.File, error) { handle, err := proc.OpenRoot(subpath) if err != nil { return nil, err } defer handle.Close() return pathrs.Reopen(handle, flags) } // WriteSysctls sets the given sysctls to the requested values. func WriteSysctls(sysctls map[string]string) error { // We are going to write multiple sysctls, which require writing to an // unmasked procfs which is not going to be cached. To avoid creating a new // procfs instance for each one, just allocate one handle for all of them. proc, err := procfs.OpenUnsafeProcRoot() if err != nil { return err } defer proc.Close() for key, value := range sysctls { keyPath := strings.ReplaceAll(key, ".", "/") sysctlFile, err := procfsOpenRoot(proc, "sys/"+keyPath, unix.O_WRONLY|unix.O_TRUNC|unix.O_CLOEXEC) if err != nil { return fmt.Errorf("open sysctl %s file: %w", key, err) } defer sysctlFile.Close() n, err := io.WriteString(sysctlFile, value) if n != len(value) && err == nil { err = fmt.Errorf("short write to file (%d bytes != %d bytes)", n, len(value)) } if err != nil { return fmt.Errorf("failed to write sysctl %s = %q: %w", key, value, err) } } return nil }