Add periodic exec of one-shot commands

This commit is contained in:
eleksir
2025-03-23 11:34:05 +03:00
parent 5976d0b327
commit 7603aaa104
7 changed files with 200 additions and 90 deletions

View File

@@ -4,8 +4,23 @@ daemon: true
# PID file for daemon.
pidfile: /var/run/reniced.pid
# Delay among iterations.
loop_delay: 5000
# Delay between process list update task consecutive runs.
process_list_update_period: 5000
# Delay between kill consecutive tasks.
process_kill_period: 5000
# Delay between changing niceness consecutive tasks.
process_nice_period: 5000
# Delay between changing io niceness consecutive tasks (linux-only).
process_ionice_period: 5000
# Period of time between consecutive periodic_process list tasks hammering.
periodic_process_invocation_period: 60000
# Period of time between process exec() invocations.
periodic_process_invocation_delay: 500
# Whether to print debug messages to stdout.
debug: true
@@ -15,7 +30,11 @@ max_workers: 3
# Capacity of non-blocking tasks. All tasks that exceeding this capacity will block submission of new tasks until queue
# has vacant place for new task.
nb_capacity: 30
nb_capacity: 3000
# Time to time run these commands.
periodic_process:
- ps xa
kill:
- sig: stop

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env gmake -f
BINARY=reniced
BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l -trimpath
BUILDOPTS=-ldflags="-s -w" -a -gcflags=all="-l -B" -trimpath -buildvcs=false
all: clean build

View File

@@ -77,9 +77,7 @@ func main() {
}
if d, err := cntxt.Reborn(); err != nil {
log.Fatal("Unable to run: ", err)
return
log.Fatalf("Unable to run: %s", err) //nolint: gocritic
} else if d != nil {
return
}

View File

@@ -13,4 +13,4 @@ func (cnf Config) SetIOPriority(which int, who int, prio uint32) (err error) {
return cnf.NotImplementedError
}
func (cnf Config) IORenice(cnf Config, p *proc.Process, processName string) {}
func (cnf Config) IORenice(p *proc.Process, processName string) {}

View File

@@ -3,6 +3,7 @@ package lib
import (
"log"
"math"
"os/exec"
"runtime"
"strings"
"syscall"
@@ -13,100 +14,149 @@ import (
// reniced основная логика.
func (cnf Config) Reniced() {
var (
processListUpdateTimer *time.Ticker
processKillTimer *time.Ticker
processReniceTimer *time.Ticker
processIONiceTimer *time.Ticker
processListTimer *time.Ticker
processList []*proc.Process
err error
)
// Прежде всего настроим таймеры
processListUpdateTimer = time.NewTicker(time.Duration(cnf.ProcessListUpdatePeriod) * time.Millisecond)
processReniceTimer = time.NewTicker(time.Duration(cnf.ProcessNicePeriod) * time.Millisecond)
processKillTimer = time.NewTicker(time.Duration(cnf.ProcessKillPeriod) * time.Millisecond)
processIONiceTimer = time.NewTicker(time.Duration(cnf.ProcessIONicePeriod) * time.Millisecond)
processListTimer = time.NewTicker(time.Duration(cnf.PeriodicProcessInvocationPeriod) * time.Millisecond)
for {
processList, err := proc.Processes()
select {
// Обновляем список процессов.
case <-processListUpdateTimer.C:
processList, err = proc.Processes()
if err != nil {
log.Fatalf("Unable to get process list: %s", err)
}
// Не самый эффективный, зато работающий способ - простой перебор массивов.
for _, p := range processList {
ok := cnf.Pool.TrySubmit(func() {
if processName, err := p.Name(); err == nil {
// Для каждого процесса извлекаем его текущий priority.
if currentPrioLevel, err := syscall.Getpriority(syscall.PRIO_PROCESS, int(p.Pid)); err == nil {
if niceLevel := cnf.Renice[processName]; niceLevel != 0 {
prioLevel := niceLevel
currentNiceLevel := currentPrioLevel
if runtime.GOOS == "linux" {
// Value of PR = 20 + (-20 to +19) is 0 to 39
switch {
case niceLevel > 0:
prioLevel = 20 - niceLevel
case niceLevel < 0:
prioLevel = 20 + int(math.Abs(float64(niceLevel)))
default:
prioLevel = 20
}
currentNiceLevel = 20 - currentPrioLevel
}
if currentPrioLevel != prioLevel {
// on Linux Setpriority actually operates niceness value.
_ = syscall.Setpriority(syscall.PRIO_PROCESS, int(p.Pid), niceLevel)
if err != nil {
log.Fatalf("Unable to get process list: %s", err)
}
// Проходим по списку процессов и посылаем сигналы процессам.
case <-processKillTimer.C:
for _, p := range processList {
ok := cnf.Pool.TrySubmit(func() {
if processName, err := p.Name(); err == nil {
// Посылаем процессу сигналы, если таковые есть в конфиге.
if killSignal := cnf.KillSignal[processName]; killSignal != 0 {
switch killSignal { //nolint:exhaustive
case syscall.SIGSTOP:
_ = p.SendSignal(killSignal)
if cnf.Debug {
log.Printf(
"Set niceness for %s(%d) to %d (was %d)",
"Matching processName with killProcessname %s(%d), SIG%s sent",
processName,
p.Pid,
strings.ToUpper(killSignal.String()),
)
}
default:
_ = p.SendSignal(killSignal)
if cnf.Debug {
log.Printf(
"Matching processName with killProcessname %s(%d), SIG%s sent",
processName,
p.Pid,
strings.ToUpper(killSignal.String()),
)
}
}
}
}
})
if !ok {
log.Printf("Unable to add task to pool. Increase nb_capacity?")
}
}
// Проходим по списку процессов и меняем ionice процессам.
case <-processIONiceTimer.C:
for _, p := range processList {
ok := cnf.Pool.TrySubmit(func() {
if processName, err := p.Name(); err == nil {
cnf.IORenice(p, processName)
}
})
if !ok {
log.Printf("Unable to add task to pool. Increase nb_capacity?")
}
}
// Проходим по списку того, что надо запустить и запускаем.
case <-processListTimer.C:
for _, v := range cnf.PeriodicProcesses {
log.Printf("Invoking command: %s", v)
exec.Command(v)
time.Sleep(time.Duration(cnf.PeriodicProcessInvocationDelay) * time.Millisecond)
}
// Проходим по списку процессов и меняем ionice процессам.
case <-processReniceTimer.C:
// Не самый эффективный, зато работающий способ - простой перебор массивов.
for _, p := range processList {
ok := cnf.Pool.TrySubmit(func() {
if processName, err := p.Name(); err == nil {
// Для каждого процесса извлекаем его текущий priority.
if currentPrioLevel, err := syscall.Getpriority(syscall.PRIO_PROCESS, int(p.Pid)); err == nil {
if niceLevel := cnf.Renice[processName]; niceLevel != 0 {
prioLevel := niceLevel
currentNiceLevel := currentPrioLevel
if runtime.GOOS == "linux" {
// Value of PR = 20 + (-20 to +19) is 0 to 39
switch {
case niceLevel > 0:
prioLevel = 20 - niceLevel
case niceLevel < 0:
prioLevel = 20 + int(math.Abs(float64(niceLevel)))
default:
prioLevel = 20
}
currentNiceLevel = 20 - currentPrioLevel
}
if currentPrioLevel != prioLevel {
// on Linux Setpriority actually operates niceness value.
_ = syscall.Setpriority(syscall.PRIO_PROCESS, int(p.Pid), niceLevel)
if cnf.Debug {
log.Printf(
"Set niceness for %s(%d) to %d (was %d)",
processName,
p.Pid,
niceLevel,
currentNiceLevel,
)
}
} else if cnf.Debug {
log.Printf(
"Niceness for %s(%d) already set to %d",
processName,
p.Pid,
niceLevel,
currentNiceLevel,
)
}
} else if cnf.Debug {
log.Printf(
"Niceness for %s(%d) already set to %d",
processName,
p.Pid,
niceLevel,
)
}
}
}
})
cnf.IORenice(p, processName)
// Посылаем процессу сигналы, если таковые есть в конфиге.
if killSignal := cnf.KillSignal[processName]; killSignal != 0 {
switch killSignal { //nolint:exhaustive
case syscall.SIGSTOP:
_ = p.SendSignal(killSignal)
if cnf.Debug {
log.Printf(
"Matching processName with killProcessname %s(%d), SIG%s sent",
processName,
p.Pid,
strings.ToUpper(killSignal.String()),
)
}
default:
_ = p.SendSignal(killSignal)
if cnf.Debug {
log.Printf(
"Matching processName with killProcessname %s(%d), SIG%s sent",
processName,
p.Pid,
strings.ToUpper(killSignal.String()),
)
}
}
}
if !ok {
log.Printf("Unable to add task to pool. Increase nb_capacity?")
}
})
if !ok {
log.Printf("Unable to add task to pool. Increase nb_capacity?")
}
}
time.Sleep(time.Millisecond * time.Duration(cnf.LoopDelay))
}
// Мы сюда никогда не попадём.

View File

@@ -29,8 +29,26 @@ type Config struct {
// Where to put pid-file.
Pidfile string `yaml:"pidfile,omitempty"`
// LoopDelay in milliseconds - whole set of action delay period.
LoopDelay int `yaml:"loop_delay,omitempty"`
// ProcessListUpdatePeriod - delay between process list update task consecutive runs.
ProcessListUpdatePeriod int `yaml:"process_list_update_period,omitempty"`
// ProcessKillPeriod in milliseconds - delay between kill consecutive tasks.
ProcessKillPeriod int `yaml:"process_kill_period,omitempty"`
// ProcessNicePeriod in milliseconds - delay between changing niceness consecutive tasks.
ProcessNicePeriod int `yaml:"process_nice_period,omitempty"`
// ProcessNicePeriod in milliseconds - delay between changing io niceness consecutive tasks (linux-only).
ProcessIONicePeriod int `yaml:"process_ionice_period,omitempty"`
// PeriodicProcessInvocationPeriod in milliseconds - delay between PeriodicProcesses invocations.
PeriodicProcessInvocationPeriod int `yaml:"periodic_process_invocation_period,omitempty"`
// PriodicProcessInvocationDelay in milliseconds - delay between each process calling in PeriodicProcesses list.
PeriodicProcessInvocationDelay int `yaml:"periodic_process_invocation_delay,omitempty"`
// PeriodicProcesses list of processes that should be executed priodically.
PeriodicProcesses []string `yaml:"periodic_process,omitempty"`
// Whether to print debug info on stdout.
Debug bool `yaml:"debug,omitempty"`

View File

@@ -70,9 +70,34 @@ func (cnf *Config) readConfFile(path string) error {
return err
}
if cnf.LoopDelay == 0 {
cnf.LoopDelay = 2000
log.Printf("loop_delay set to %d milliseconds", cnf.LoopDelay)
if cnf.ProcessListUpdatePeriod == 0 {
cnf.ProcessListUpdatePeriod = 5000
log.Printf("process_list_update_priod set to %d milliseconds", cnf.ProcessListUpdatePeriod)
}
if cnf.ProcessNicePeriod == 0 {
cnf.ProcessNicePeriod = 5000
log.Printf("process_nice_priod set to %d milliseconds", cnf.ProcessNicePeriod)
}
if cnf.ProcessIONicePeriod == 0 {
cnf.ProcessIONicePeriod = 5000
log.Printf("process_ionice_period set to %d milliseconds", cnf.ProcessIONicePeriod)
}
if cnf.ProcessKillPeriod == 0 {
cnf.ProcessKillPeriod = 5000
log.Printf("process_kill_period set to %d milliseconds", cnf.ProcessIONicePeriod)
}
if cnf.PeriodicProcessInvocationPeriod == 0 {
cnf.PeriodicProcessInvocationPeriod = 60000
log.Printf("periodic_process_invocation_period set to %d milliseconds", cnf.PeriodicProcessInvocationPeriod)
}
if cnf.PeriodicProcessInvocationDelay == 0 {
cnf.PeriodicProcessInvocationDelay = 500
log.Printf("periodic_process_invocation_delay set to %d milliseconds", cnf.PeriodicProcessInvocationDelay)
}
if cnf.Debug {