diff --git a/main.go b/main.go index 850800f..3c435f1 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "log" + "math" "os" "os/signal" "os/user" @@ -21,6 +22,9 @@ var pool *pond.WorkerPool // Канал, в который приходят уведомления для хэндлера сигналов от траппера сигналов. var sigChan = make(chan os.Signal, 1) +var kill = make(map[string]string) +var renice = make(map[string]int) + // main основная функция программы. func main() { // Самое время поставить траппер сигналов. @@ -98,11 +102,21 @@ func reniced(cnf Config) { ok := pool.TrySubmit(func() { if processName, err := p.Name(); err == nil { for _, k := range cnf.Kill { - for _, killProcessName := range k.Name { - if processName == killProcessName { - switch k.Sig { - case "kill": - _ = p.SendSignal(syscall.SIGKILL) + if killSignal := kill[processName]; killSignal != "" { + switch killSignal { + case "kill": + _ = p.SendSignal(syscall.SIGKILL) + + if cnf.Debug { + log.Printf( + "Matching processName with killProcessname (%s), SIG%s sent", + processName, + strings.ToUpper(k.Sig), + ) + } + case "stop": + if s, err := p.Status(); err == nil && s[0] != "stop" { + _ = p.SendSignal(syscall.SIGSTOP) if cnf.Debug { log.Printf( @@ -111,125 +125,121 @@ func reniced(cnf Config) { strings.ToUpper(k.Sig), ) } - case "stop": - if s, err := p.Status(); err == nil && s[0] != "stop" { - _ = p.SendSignal(syscall.SIGSTOP) + } else if err == nil && s[0] == "stop" && cnf.Debug { + log.Printf( + "Matching processName with killProcessname (%s) and it is already stopped", + processName, + ) + } + case "term": + _ = p.SendSignal(syscall.SIGTERM) - if cnf.Debug { - log.Printf( - "Matching processName with killProcessname (%s), SIG%s sent", - processName, - strings.ToUpper(k.Sig), - ) - } - } else if err == nil && s[0] == "stop" && cnf.Debug { - log.Printf( - "Matching processName with killProcessname (%s) and it is already stopped", - processName, - ) - } - case "term": - _ = p.SendSignal(syscall.SIGTERM) + if cnf.Debug { + log.Printf( + "Matching processName with killProcessname (%s), SIG%s sent", + processName, + strings.ToUpper(k.Sig), + ) + } + case "int": + _ = p.SendSignal(syscall.SIGINT) - if cnf.Debug { - log.Printf( - "Matching processName with killProcessname (%s), SIG%s sent", - processName, - strings.ToUpper(k.Sig), - ) - } - case "int": - _ = p.SendSignal(syscall.SIGINT) + if cnf.Debug { + log.Printf( + "Matching processName with killProcessname (%s), SIG%s sent", + processName, + strings.ToUpper(k.Sig), + ) + } + case "quit": + _ = p.SendSignal(syscall.SIGQUIT) - if cnf.Debug { - log.Printf( - "Matching processName with killProcessname (%s), SIG%s sent", - processName, - strings.ToUpper(k.Sig), - ) - } - case "quit": - _ = p.SendSignal(syscall.SIGQUIT) + if cnf.Debug { + log.Printf( + "Matching processName with killProcessname (%s), SIG%s sent", + processName, + strings.ToUpper(k.Sig), + ) + } + case "abrt": + _ = p.SendSignal(syscall.SIGABRT) - if cnf.Debug { - log.Printf( - "Matching processName with killProcessname (%s), SIG%s sent", - processName, - strings.ToUpper(k.Sig), - ) - } - case "abrt": - _ = p.SendSignal(syscall.SIGABRT) + if cnf.Debug { + log.Printf( + "Matching processName with killProcessname (%s), SIG%s sent", + processName, + strings.ToUpper(k.Sig), + ) + } + case "hup": + _ = p.SendSignal(syscall.SIGHUP) - if cnf.Debug { - log.Printf( - "Matching processName with killProcessname (%s), SIG%s sent", - processName, - strings.ToUpper(k.Sig), - ) - } - case "hup": - _ = p.SendSignal(syscall.SIGHUP) + if cnf.Debug { + log.Printf( + "Matching processName with killProcessname (%s), SIG%s sent", + processName, + strings.ToUpper(k.Sig), + ) + } + case "usr1": + _ = p.SendSignal(syscall.SIGUSR1) - if cnf.Debug { - log.Printf( - "Matching processName with killProcessname (%s), SIG%s sent", - processName, - strings.ToUpper(k.Sig), - ) - } - case "usr1": - _ = p.SendSignal(syscall.SIGUSR1) + if cnf.Debug { + log.Printf( + "Matching processName with killProcessname (%s), SIG%s sent", + processName, + strings.ToUpper(k.Sig), + ) + } + case "usr2": + _ = p.SendSignal(syscall.SIGUSR2) - if cnf.Debug { - log.Printf( - "Matching processName with killProcessname (%s), SIG%s sent", - processName, - strings.ToUpper(k.Sig), - ) - } - case "usr2": - _ = p.SendSignal(syscall.SIGUSR2) - - if cnf.Debug { - log.Printf( - "Matching processName with killProcessname (%s), SIG%s sent", - processName, - strings.ToUpper(k.Sig), - ) - } + if cnf.Debug { + log.Printf( + "Matching processName with killProcessname (%s), SIG%s sent", + processName, + strings.ToUpper(k.Sig), + ) } } } } - // Для каждого процесса извлекаем его текущий nicelevel. - if currentNiceLevel, err := p.Nice(); err == nil { - // Вынимаем список приоритетов. - for _, n := range cnf.Prio { - // Для каждого приоритета, проходимся по списку имён процессов. - for _, name := range n.Name { - // Достаём имя текущего процесса. - if processName, err = p.Name(); err == nil { - // Если имя процесса совпадает с одним из списка в конфиге, то пробуем менять ему nice. - if processName == name { - if currentNiceLevel != n.Nice { - _ = syscall.Setpriority(syscall.PRIO_PROCESS, int(p.Pid), int(n.Nice)) + // Для каждого процесса извлекаем его текущий priority. + if currentPrioLevel, err := syscall.Getpriority(syscall.PRIO_PROCESS, int(p.Pid)); err == nil { + if niceLevel := renice[processName]; niceLevel != 0 { + prioLevel := niceLevel + currentNiceLevel := currentPrioLevel - if cnf.Debug { - log.Printf( - "Set niceness for %s(%d) to %d (was %d)", - name, - p.Pid, - n.Nice, - currentNiceLevel, - ) - } - } else if cnf.Debug { - log.Printf("Nicelevel for %d already set to %d", p.Pid, n.Nice) - } - } + 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 %d already set to %d", p.Pid, niceLevel) } } } diff --git a/types.go b/types.go index 7019ad5..cfd8d80 100644 --- a/types.go +++ b/types.go @@ -32,7 +32,7 @@ type Config struct { } type Prio []struct { - Nice int32 `yaml:"nice,omitempty"` + Nice int `yaml:"nice,omitempty"` Name []string `yaml:"name,omitempty"` } diff --git a/util.go b/util.go index 79b8dab..6ae8582 100644 --- a/util.go +++ b/util.go @@ -40,7 +40,7 @@ func readConf(path string) (Config, error) { if cfg.CmdDelay == 0 { cfg.CmdDelay = 20 - log.Println("cmd_delay can not be 0, setting to %d milliseconds", cfg.CmdDelay) + log.Printf("cmd_delay can not be 0, setting to %d milliseconds", cfg.CmdDelay) } if cfg.LoopDelay == 0 { @@ -67,10 +67,8 @@ func readConf(path string) (Config, error) { log.Printf("nb_capacity set to same value as max_workers: %d", cfg.MaxWorkers) } - newPrio := make(Prio, 0) - // Обычный юзер не может задрать nice больше 0, только руту такое можно. - var maxNice int32 + var maxNice int if os.Getuid() == 0 { maxNice = -20 @@ -79,10 +77,10 @@ func readConf(path string) (Config, error) { for _, v := range cfg.Prio { switch { case len(v.Name) != 0 && (v.Nice < 20 && v.Nice > maxNice): - newPrio = append(newPrio, v) + for _, procName := range v.Name { + renice[procName] = v.Nice - if cfg.Debug { - for _, procName := range v.Name { + if cfg.Debug { log.Printf("Add %s to list of nicelevel %d processes.", procName, v.Nice) } } @@ -93,18 +91,14 @@ func readConf(path string) (Config, error) { } } - cfg.Prio = newPrio - - newKill := make(Kill, 0) - for _, v := range cfg.Kill { if len(v.Name) != 0 { switch v.Sig { case "kill", "stop", "term", "int", "quit", "abrt", "hup", "usr1", "usr2": - newKill = append(newKill, v) + for _, procName := range v.Name { + kill[procName] = v.Sig - if cfg.Debug { - for _, procName := range v.Name { + if cfg.Debug { log.Printf( "Add %s to list of processes that should be killed with SIG%s signal.", procName, @@ -120,8 +114,6 @@ func readConf(path string) (Config, error) { } } - cfg.Kill = newKill - return cfg, nil }