diff --git a/CHANGELOG.md b/CHANGELOG.md index cdd806a0..aa3afd28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,12 +26,14 @@ and this project adheres to ### Changed -- Make the mobileconfig HTTP API more robust and predictable, add parameters and +- Post-updating relaunch possibility is now determined OS-dependently ([#2231]). +- Made the mobileconfig HTTP API more robust and predictable, add parameters and improve error response ([#2358]). - Improved HTTP requests handling and timeouts. ([#2343]). - Our snap package now uses the `core20` image as its base [#2306]. - Various internal improvements ([#2271], [#2297]). +[#2231]: https://github.com/AdguardTeam/AdGuardHome/issues/2231 [#2271]: https://github.com/AdguardTeam/AdGuardHome/issues/2271 [#2297]: https://github.com/AdguardTeam/AdGuardHome/issues/2297 [#2306]: https://github.com/AdguardTeam/AdGuardHome/issues/2306 diff --git a/internal/home/control_update.go b/internal/home/control_update.go index 2ab5d6a2..dcf428b9 100644 --- a/internal/home/control_update.go +++ b/internal/home/control_update.go @@ -10,8 +10,8 @@ import ( "strings" "syscall" + "github.com/AdguardTeam/AdGuardHome/internal/sysutil" "github.com/AdguardTeam/AdGuardHome/internal/update" - "github.com/AdguardTeam/AdGuardHome/internal/util" "github.com/AdguardTeam/golibs/log" ) @@ -104,12 +104,7 @@ func getVersionResp(info update.VersionInfo) []byte { tlsConf.PortDNSOverQUIC < 1024)) || config.BindPort < 1024 || config.DNS.Port < 1024) { - // On UNIX, if we're running under a regular user, - // but with CAP_NET_BIND_SERVICE set on a binary file, - // and we're listening on ports <1024, - // we won't be able to restart after we replace the binary file, - // because we'll lose CAP_NET_BIND_SERVICE capability. - canUpdate, _ = util.HaveAdminRights() + canUpdate, _ = sysutil.CanBindPrivilegedPorts() } ret["can_autoupdate"] = canUpdate } diff --git a/internal/home/home.go b/internal/home/home.go index 82f5ccf4..a4be016f 100644 --- a/internal/home/home.go +++ b/internal/home/home.go @@ -26,6 +26,7 @@ import ( "github.com/AdguardTeam/AdGuardHome/internal/dnsforward" "github.com/AdguardTeam/AdGuardHome/internal/querylog" "github.com/AdguardTeam/AdGuardHome/internal/stats" + "github.com/AdguardTeam/AdGuardHome/internal/sysutil" "github.com/AdguardTeam/AdGuardHome/internal/update" "github.com/AdguardTeam/AdGuardHome/internal/util" "github.com/AdguardTeam/golibs/log" @@ -222,7 +223,7 @@ func setupConfig(args options) { if (runtime.GOOS == "linux" || runtime.GOOS == "darwin") && config.RlimitNoFile != 0 { - util.SetRlimit(config.RlimitNoFile) + sysutil.SetRlimit(config.RlimitNoFile) } // override bind host/port from the console @@ -376,7 +377,7 @@ func checkPermissions() { if runtime.GOOS == "windows" { // On Windows we need to have admin rights to run properly - admin, _ := util.HaveAdminRights() + admin, _ := sysutil.HaveAdminRights() if admin { return } @@ -493,7 +494,7 @@ func configureLogger(args options) { if ls.LogFile == configSyslog { // Use syslog where it is possible and eventlog on Windows - err := util.ConfigureSyslog(serviceName) + err := sysutil.ConfigureSyslog(serviceName) if err != nil { log.Fatalf("cannot initialize syslog: %s", err) } diff --git a/internal/home/service.go b/internal/home/service.go index fa86f943..aa243634 100644 --- a/internal/home/service.go +++ b/internal/home/service.go @@ -9,6 +9,7 @@ import ( "strings" "syscall" + "github.com/AdguardTeam/AdGuardHome/internal/sysutil" "github.com/AdguardTeam/AdGuardHome/internal/util" "github.com/AdguardTeam/golibs/log" "github.com/kardianos/service" @@ -109,7 +110,7 @@ func sendSigReload() { log.Error("Can't read PID file %s: %s", pidfile, err) return } - err = util.SendProcessSignal(pid, syscall.SIGHUP) + err = sysutil.SendProcessSignal(pid, syscall.SIGHUP) if err != nil { log.Error("Can't send signal to PID %d: %s", pid, err) return diff --git a/internal/sysutil/os_freebsd.go b/internal/sysutil/os_freebsd.go new file mode 100644 index 00000000..48033f49 --- /dev/null +++ b/internal/sysutil/os_freebsd.go @@ -0,0 +1,32 @@ +//+build freebsd + +package sysutil + +import ( + "os" + "syscall" + + "github.com/AdguardTeam/golibs/log" +) + +func canBindPrivilegedPorts() (can bool, err error) { + return HaveAdminRights() +} + +func setRlimit(val uint) { + var rlim syscall.Rlimit + rlim.Max = int64(val) + rlim.Cur = int64(val) + err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlim) + if err != nil { + log.Error("Setrlimit() failed: %v", err) + } +} + +func haveAdminRights() (bool, error) { + return os.Getuid() == 0, nil +} + +func sendProcessSignal(pid int, sig syscall.Signal) error { + return syscall.Kill(pid, sig) +} diff --git a/internal/sysutil/os_linux.go b/internal/sysutil/os_linux.go new file mode 100644 index 00000000..cfe8cf85 --- /dev/null +++ b/internal/sysutil/os_linux.go @@ -0,0 +1,34 @@ +//+build linux + +package sysutil + +import ( + "os" + "syscall" + + "github.com/AdguardTeam/golibs/log" + "golang.org/x/sys/unix" +) + +func canBindPrivilegedPorts() (can bool, err error) { + cnbs, err := unix.PrctlRetInt(unix.PR_CAP_AMBIENT, unix.PR_CAP_AMBIENT_IS_SET, unix.CAP_NET_BIND_SERVICE, 0, 0) + return cnbs == 1, err +} + +func setRlimit(val uint) { + var rlim syscall.Rlimit + rlim.Max = uint64(val) + rlim.Cur = uint64(val) + err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlim) + if err != nil { + log.Error("Setrlimit() failed: %v", err) + } +} + +func haveAdminRights() (bool, error) { + return os.Getuid() == 0, nil +} + +func sendProcessSignal(pid int, sig syscall.Signal) error { + return syscall.Kill(pid, sig) +} diff --git a/internal/sysutil/os_unix.go b/internal/sysutil/os_unix.go new file mode 100644 index 00000000..0a182cdc --- /dev/null +++ b/internal/sysutil/os_unix.go @@ -0,0 +1,32 @@ +//+build aix darwin dragonfly netbsd openbsd solaris + +package sysutil + +import ( + "os" + "syscall" + + "github.com/AdguardTeam/golibs/log" +) + +func canBindPrivilegedPorts() (can bool, err error) { + return HaveAdminRights() +} + +func setRlimit(val uint) { + var rlim syscall.Rlimit + rlim.Max = uint64(val) + rlim.Cur = uint64(val) + err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlim) + if err != nil { + log.Error("Setrlimit() failed: %v", err) + } +} + +func haveAdminRights() (bool, error) { + return os.Getuid() == 0, nil +} + +func sendProcessSignal(pid int, sig syscall.Signal) error { + return syscall.Kill(pid, sig) +} diff --git a/internal/util/os_windows.go b/internal/sysutil/os_windows.go similarity index 69% rename from internal/util/os_windows.go rename to internal/sysutil/os_windows.go index 2a3742fa..58b331b4 100644 --- a/internal/util/os_windows.go +++ b/internal/sysutil/os_windows.go @@ -1,4 +1,6 @@ -package util +//+build windows + +package sysutil import ( "fmt" @@ -7,11 +9,14 @@ import ( "golang.org/x/sys/windows" ) -// Set user-specified limit of how many fd's we can use -func SetRlimit(val uint) { +func canBindPrivilegedPorts() (can bool, err error) { + return HaveAdminRights() } -func HaveAdminRights() (bool, error) { +func setRlimit(val uint) { +} + +func haveAdminRights() (bool, error) { var token windows.Token h := windows.CurrentProcess() err := windows.OpenProcessToken(h, windows.TOKEN_QUERY, &token) @@ -32,6 +37,6 @@ func HaveAdminRights() (bool, error) { return true, nil } -func SendProcessSignal(pid int, sig syscall.Signal) error { +func sendProcessSignal(pid int, sig syscall.Signal) error { return fmt.Errorf("not supported on Windows") } diff --git a/internal/util/syslog_others.go b/internal/sysutil/syslog_others.go similarity index 52% rename from internal/util/syslog_others.go rename to internal/sysutil/syslog_others.go index f4ad9119..f0e15967 100644 --- a/internal/util/syslog_others.go +++ b/internal/sysutil/syslog_others.go @@ -1,14 +1,13 @@ -// +build !windows,!nacl,!plan9 +//+build !windows,!nacl,!plan9 -package util +package sysutil import ( "log" "log/syslog" ) -// ConfigureSyslog reroutes standard logger output to syslog -func ConfigureSyslog(serviceName string) error { +func configureSyslog(serviceName string) error { w, err := syslog.New(syslog.LOG_NOTICE|syslog.LOG_USER, serviceName) if err != nil { return err diff --git a/internal/util/syslog_windows.go b/internal/sysutil/syslog_windows.go similarity index 86% rename from internal/util/syslog_windows.go rename to internal/sysutil/syslog_windows.go index 30ee7815..fbacf44e 100644 --- a/internal/util/syslog_windows.go +++ b/internal/sysutil/syslog_windows.go @@ -1,4 +1,6 @@ -package util +//+build windows nacl plan9 + +package sysutil import ( "log" @@ -12,12 +14,12 @@ type eventLogWriter struct { el *eventlog.Log } -// Write sends a log message to the Event Log. +// Write implements io.Writer interface for eventLogWriter. func (w *eventLogWriter) Write(b []byte) (int, error) { return len(b), w.el.Info(1, string(b)) } -func ConfigureSyslog(serviceName string) error { +func configureSyslog(serviceName string) error { // Note that the eventlog src is the same as the service name // Otherwise, we will get "the description for event id cannot be found" warning in every log record diff --git a/internal/sysutil/sysutil.go b/internal/sysutil/sysutil.go new file mode 100644 index 00000000..47f40600 --- /dev/null +++ b/internal/sysutil/sysutil.go @@ -0,0 +1,31 @@ +// Package sysutil contains utilities for functions requiring system calls. +package sysutil + +import "syscall" + +// CanBindPrivilegedPorts checks if current process can bind to privileged +// ports. +func CanBindPrivilegedPorts() (can bool, err error) { + return canBindPrivilegedPorts() +} + +// SetRlimit sets user-specified limit of how many fd's we can use +// https://github.com/AdguardTeam/AdGuardHome/internal/issues/659. +func SetRlimit(val uint) { + setRlimit(val) +} + +// HaveAdminRights checks if the current user has root (administrator) rights. +func HaveAdminRights() (bool, error) { + return haveAdminRights() +} + +// SendProcessSignal sends signal to a process. +func SendProcessSignal(pid int, sig syscall.Signal) error { + return sendProcessSignal(pid, sig) +} + +// ConfigureSyslog reroutes standard logger output to syslog. +func ConfigureSyslog(serviceName string) error { + return configureSyslog(serviceName) +} diff --git a/internal/util/os_freebsd.go b/internal/util/os_freebsd.go deleted file mode 100644 index 042e83fe..00000000 --- a/internal/util/os_freebsd.go +++ /dev/null @@ -1,32 +0,0 @@ -// +build freebsd - -package util - -import ( - "os" - "syscall" - - "github.com/AdguardTeam/golibs/log" -) - -// Set user-specified limit of how many fd's we can use -// https://github.com/AdguardTeam/AdGuardHome/internal/issues/659 -func SetRlimit(val uint) { - var rlim syscall.Rlimit - rlim.Max = int64(val) - rlim.Cur = int64(val) - err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlim) - if err != nil { - log.Error("Setrlimit() failed: %v", err) - } -} - -// Check if the current user has root (administrator) rights -func HaveAdminRights() (bool, error) { - return os.Getuid() == 0, nil -} - -// SendProcessSignal - send signal to a process -func SendProcessSignal(pid int, sig syscall.Signal) error { - return syscall.Kill(pid, sig) -} diff --git a/internal/util/os_unix.go b/internal/util/os_unix.go deleted file mode 100644 index 53557566..00000000 --- a/internal/util/os_unix.go +++ /dev/null @@ -1,32 +0,0 @@ -// +build aix darwin dragonfly linux netbsd openbsd solaris - -package util - -import ( - "os" - "syscall" - - "github.com/AdguardTeam/golibs/log" -) - -// Set user-specified limit of how many fd's we can use -// https://github.com/AdguardTeam/AdGuardHome/internal/issues/659 -func SetRlimit(val uint) { - var rlim syscall.Rlimit - rlim.Max = uint64(val) - rlim.Cur = uint64(val) - err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlim) - if err != nil { - log.Error("Setrlimit() failed: %v", err) - } -} - -// Check if the current user has root (administrator) rights -func HaveAdminRights() (bool, error) { - return os.Getuid() == 0, nil -} - -// SendProcessSignal - send signal to a process -func SendProcessSignal(pid int, sig syscall.Signal) error { - return syscall.Kill(pid, sig) -}