From 91210cbb4960dc66605ee5bb0d7939c3d85c149d Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Wed, 17 Jan 2018 12:32:11 +0000 Subject: [PATCH 1/3] lib/osutil: Check PGID before trying to set it (fixes #4679) Fixes "permission denied" return when are already process group / session leader. GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4678 --- lib/osutil/lowprio_linux.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/osutil/lowprio_linux.go b/lib/osutil/lowprio_linux.go index c3b6ab48..f0e83542 100644 --- a/lib/osutil/lowprio_linux.go +++ b/lib/osutil/lowprio_linux.go @@ -7,6 +7,7 @@ package osutil import ( + "os" "syscall" "github.com/pkg/errors" @@ -44,8 +45,15 @@ func SetLowPriority() error { // Move ourselves to a new process group so that we can use the process // group variants of Setpriority etc to affect all of our threads in one // go. If this fails, bail, so that we don't affect things we shouldn't. - if err := syscall.Setpgid(0, 0); err != nil { - return errors.Wrap(err, "set process group") + // If we are already the leader of our own process group, do nothing. + if pgid, err := syscall.Getpgid(0); err != nil { + // This error really shouldn't happen + return errors.Wrap(err, "get process group") + } else if pgid != os.Getpid() { + // We are not process group leader. Elevate! + if err := syscall.Setpgid(0, 0); err != nil { + return errors.Wrap(err, "set process group") + } } // Process zero is "self", niceness value 9 is something between 0 From c2119c9e62ef93cdeb33dd02d25aff17fd84c765 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Thu, 18 Jan 2018 08:34:56 +0000 Subject: [PATCH 2/3] lib/osutil: Don't attempt to reduce our niceness level (fixes #4681) GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4682 --- lib/osutil/lowprio_linux.go | 39 ++++++++++++++++++++++++++++++++----- lib/osutil/lowprio_unix.go | 12 +++++++++++- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/lib/osutil/lowprio_linux.go b/lib/osutil/lowprio_linux.go index f0e83542..8b07449c 100644 --- a/lib/osutil/lowprio_linux.go +++ b/lib/osutil/lowprio_linux.go @@ -42,23 +42,52 @@ func ioprioSet(class ioprioClass, value int) error { // SetLowPriority lowers the process CPU scheduling priority, and possibly // I/O priority depending on the platform and OS. func SetLowPriority() error { + // Process zero is "self", niceness value 9 is something between 0 + // (default) and 19 (worst priority). But then, this is Linux, so of + // course we get this to take care of as well: + // + // "C library/kernel differences + // + // Within the kernel, nice values are actually represented using the + // range 40..1 (since negative numbers are error codes) and these are + // the values employed by the setpriority() and getpriority() system + // calls. The glibc wrapper functions for these system calls handle the + // translations between the user-land and kernel representations of the + // nice value according to the formula unice = 20 - knice. (Thus, the + // kernel's 40..1 range corresponds to the range -20..19 as seen by user + // space.)" + + const ( + pidSelf = 0 + wantNiceLevel = 20 - 9 + ) + + // Remember Linux kernel nice levels are upside down. + if cur, err := syscall.Getpriority(syscall.PRIO_PROCESS, 0); err == nil && cur <= wantNiceLevel { + // We're done here. + return nil + } + // Move ourselves to a new process group so that we can use the process // group variants of Setpriority etc to affect all of our threads in one // go. If this fails, bail, so that we don't affect things we shouldn't. // If we are already the leader of our own process group, do nothing. - if pgid, err := syscall.Getpgid(0); err != nil { + // + // Oh and this is because Linux doesn't follow the POSIX threading model + // where setting the niceness of the process would actually set the + // niceness of the process, instead it just affects the current thread + // so we need this workaround... + if pgid, err := syscall.Getpgid(pidSelf); err != nil { // This error really shouldn't happen return errors.Wrap(err, "get process group") } else if pgid != os.Getpid() { // We are not process group leader. Elevate! - if err := syscall.Setpgid(0, 0); err != nil { + if err := syscall.Setpgid(pidSelf, 0); err != nil { return errors.Wrap(err, "set process group") } } - // Process zero is "self", niceness value 9 is something between 0 - // (default) and 19 (worst priority). - if err := syscall.Setpriority(syscall.PRIO_PGRP, 0, 9); err != nil { + if err := syscall.Setpriority(syscall.PRIO_PGRP, pidSelf, wantNiceLevel); err != nil { return errors.Wrap(err, "set niceness") } diff --git a/lib/osutil/lowprio_unix.go b/lib/osutil/lowprio_unix.go index 1e4a8b6d..dda4f8bd 100644 --- a/lib/osutil/lowprio_unix.go +++ b/lib/osutil/lowprio_unix.go @@ -19,6 +19,16 @@ import ( func SetLowPriority() error { // Process zero is "self", niceness value 9 is something between 0 // (default) and 19 (worst priority). - err := syscall.Setpriority(syscall.PRIO_PROCESS, 0, 9) + const ( + pidSelf = 0 + wantNiceLevel = 9 + ) + + if cur, err := syscall.Getpriority(syscall.PRIO_PROCESS, pidSelf); err == nil && cur >= wantNiceLevel { + // We're done here. + return nil + } + + err := syscall.Setpriority(syscall.PRIO_PROCESS, pidSelf, wantNiceLevel) return errors.Wrap(err, "set niceness") // wraps nil as nil } From 27d5b17096847d16d01421c30151969adda36628 Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Thu, 18 Jan 2018 17:03:24 +0000 Subject: [PATCH 3/3] lib/osutil: Fix priority lowering on Windows GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4686 --- lib/osutil/lowprio_windows.go | 38 ++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/lib/osutil/lowprio_windows.go b/lib/osutil/lowprio_windows.go index 45793208..75609ec1 100644 --- a/lib/osutil/lowprio_windows.go +++ b/lib/osutil/lowprio_windows.go @@ -6,8 +6,44 @@ package osutil +import ( + "syscall" + + "github.com/pkg/errors" +) + +const ( + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686219(v=vs.85).aspx + aboveNormalPriorityClass = 0x00008000 + belowNormalPriorityClass = 0x00004000 + highPriorityClass = 0x00000080 + idlePriorityClass = 0x00000040 + normalPriorityClass = 0x00000020 + processModeBackgroundBegin = 0x00100000 + processModeBackgroundEnd = 0x00200000 + realtimePriorityClass = 0x00000100 +) + // SetLowPriority lowers the process CPU scheduling priority, and possibly // I/O priority depending on the platform and OS. func SetLowPriority() error { - return nil + modkernel32 := syscall.NewLazyDLL("kernel32.dll") + setPriorityClass := modkernel32.NewProc("SetPriorityClass") + + if err := setPriorityClass.Find(); err != nil { + return errors.Wrap(err, "find proc") + } + + handle, err := syscall.GetCurrentProcess() + if err != nil { + return errors.Wrap(err, "get process handler") + } + defer syscall.CloseHandle(handle) + + res, _, err := setPriorityClass.Call(uintptr(handle), belowNormalPriorityClass) + if res != 0 { + // "If the function succeeds, the return value is nonzero." + return nil + } + return errors.Wrap(err, "set priority class") // wraps nil as nil }