implement RE UID matching; vatidation RE patterns at startup
This commit is contained in:
parent
4fcab87fc5
commit
68e0efc6a5
175
nohang
175
nohang
@ -194,7 +194,6 @@ def pid_to_name(pid):
|
|||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def pid_to_cmdline(pid):
|
def pid_to_cmdline(pid):
|
||||||
"""
|
"""
|
||||||
Get process cmdline by pid.
|
Get process cmdline by pid.
|
||||||
@ -209,11 +208,11 @@ def pid_to_cmdline(pid):
|
|||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
def pid_to_uid(pid):
|
||||||
|
with open('/proc/' + pid + '/status') as f:
|
||||||
|
for n, line in enumerate(f):
|
||||||
|
if n is uid_index:
|
||||||
|
return line.split('\t')[1]
|
||||||
|
|
||||||
|
|
||||||
def send_notify_warn():
|
def send_notify_warn():
|
||||||
@ -324,13 +323,6 @@ def sleep_after_send_signal(signal):
|
|||||||
sleep(min_delay_after_sigterm)
|
sleep(min_delay_after_sigterm)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def find_victim_and_send_signal(signal):
|
def find_victim_and_send_signal(signal):
|
||||||
"""
|
"""
|
||||||
Find victim with highest badness and send SIGTERM/SIGKILL
|
Find victim with highest badness and send SIGTERM/SIGKILL
|
||||||
@ -338,10 +330,11 @@ def find_victim_and_send_signal(signal):
|
|||||||
pid_badness_list = []
|
pid_badness_list = []
|
||||||
|
|
||||||
for pid in os.listdir('/proc'):
|
for pid in os.listdir('/proc'):
|
||||||
# только директории, имена которых состоят только из цифр, за исключением /proc/1/
|
# only directories whose names consist only of numbers, except /proc/1/
|
||||||
if pid[0].isdecimal() is False or pid == '1':
|
if pid[0].isdecimal() is False or pid == '1':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# find and modify badness (if it needs)
|
||||||
try:
|
try:
|
||||||
badness = int(rline1('/proc/' + pid + '/oom_score'))
|
badness = int(rline1('/proc/' + pid + '/oom_score'))
|
||||||
|
|
||||||
@ -354,13 +347,13 @@ def find_victim_and_send_signal(signal):
|
|||||||
|
|
||||||
name = pid_to_name(pid)
|
name = pid_to_name(pid)
|
||||||
cmdline = pid_to_cmdline(pid)
|
cmdline = pid_to_cmdline(pid)
|
||||||
#uid = pid_to_uid(pid)
|
uid = pid_to_uid(pid)
|
||||||
|
|
||||||
# skip kthreads
|
# skip kthreads
|
||||||
if cmdline == '':
|
if cmdline == '':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
#print([pid], [name], [cmdline])
|
#print([uid], [name], [cmdline])
|
||||||
|
|
||||||
if search(avoid_regex, name) is not None:
|
if search(avoid_regex, name) is not None:
|
||||||
badness = int(badness / avoid_factor)
|
badness = int(badness / avoid_factor)
|
||||||
@ -382,6 +375,16 @@ def find_victim_and_send_signal(signal):
|
|||||||
print(' Cmdline matches with prefer_re_cmdline \033[33m{}\033[0m: \033[33m{}\033[0m, Name: {}'.format(
|
print(' Cmdline matches with prefer_re_cmdline \033[33m{}\033[0m: \033[33m{}\033[0m, Name: {}'.format(
|
||||||
prefer_re_cmdline, cmdline, name))
|
prefer_re_cmdline, cmdline, name))
|
||||||
|
|
||||||
|
if search(avoid_re_uid, uid) is not None:
|
||||||
|
badness = int(badness / avoid_uid_factor)
|
||||||
|
print(' UID matches with avoid_re_uid \033[33m{}\033[0m: \033[33m{}\033[0m, Name: {}'.format(
|
||||||
|
avoid_re_uid, uid, name))
|
||||||
|
|
||||||
|
if search(prefer_re_uid, uid) is not None:
|
||||||
|
badness = int((badness + 1) * prefer_cmd_factor)
|
||||||
|
print(' UID matches with prefer_re_uid \033[33m{}\033[0m: \033[33m{}\033[0m, Name: {}'.format(
|
||||||
|
prefer_re_uid, uid, name))
|
||||||
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
badness = 0
|
badness = 0
|
||||||
except ProcessLookupError:
|
except ProcessLookupError:
|
||||||
@ -449,11 +452,9 @@ def find_victim_and_send_signal(signal):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
if detailed_rss:
|
if detailed_rss:
|
||||||
print('anon file shmem, MiB:', anon_rss, file_rss, shmem_rss)
|
print('anon file shmem, MiB:', anon_rss, file_rss, shmem_rss)
|
||||||
|
|
||||||
|
|
||||||
if execute_the_command and signal is SIGTERM and name in etc_dict:
|
if execute_the_command and signal is SIGTERM and name in etc_dict:
|
||||||
command = etc_dict[name]
|
command = etc_dict[name]
|
||||||
exit_status = os.system(etc_dict[name])
|
exit_status = os.system(etc_dict[name])
|
||||||
@ -523,7 +524,6 @@ def find_victim_and_send_signal(signal):
|
|||||||
|
|
||||||
print(victim_badness_is_too_small)
|
print(victim_badness_is_too_small)
|
||||||
|
|
||||||
|
|
||||||
sleep_after_send_signal(signal)
|
sleep_after_send_signal(signal)
|
||||||
|
|
||||||
|
|
||||||
@ -664,18 +664,6 @@ def calculate_percent(arg_key):
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# find mem_total
|
# find mem_total
|
||||||
# find positions of SwapFree and SwapTotal in /proc/meminfo
|
# find positions of SwapFree and SwapTotal in /proc/meminfo
|
||||||
|
|
||||||
@ -778,7 +766,6 @@ print('The path to the config:', config)
|
|||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
||||||
# parsing the config with obtaining the parameters dictionary
|
# parsing the config with obtaining the parameters dictionary
|
||||||
|
|
||||||
# conf_parameters_dict
|
# conf_parameters_dict
|
||||||
@ -846,18 +833,94 @@ mlockall = conf_parse_bool('mlockall')
|
|||||||
gui_low_memory_warnings = conf_parse_bool('gui_low_memory_warnings')
|
gui_low_memory_warnings = conf_parse_bool('gui_low_memory_warnings')
|
||||||
gui_notifications = conf_parse_bool('gui_notifications')
|
gui_notifications = conf_parse_bool('gui_notifications')
|
||||||
decrease_oom_score_adj = conf_parse_bool('decrease_oom_score_adj')
|
decrease_oom_score_adj = conf_parse_bool('decrease_oom_score_adj')
|
||||||
|
|
||||||
regex_matching = conf_parse_bool('regex_matching')
|
regex_matching = conf_parse_bool('regex_matching')
|
||||||
|
if regex_matching:
|
||||||
|
from re import search
|
||||||
|
import sre_constants
|
||||||
|
|
||||||
execute_the_command = conf_parse_bool('execute_the_command')
|
execute_the_command = conf_parse_bool('execute_the_command')
|
||||||
|
|
||||||
prefer_regex = conf_parse_string('prefer_regex')
|
prefer_regex = conf_parse_string('prefer_regex')
|
||||||
|
if prefer_regex == '':
|
||||||
|
print('Invalid prefer_regex value, ' \
|
||||||
|
'regex pattern must not be empty')
|
||||||
|
exit()
|
||||||
|
# RE pattern validation
|
||||||
|
try:
|
||||||
|
search(prefer_regex, '')
|
||||||
|
except sre_constants.error:
|
||||||
|
print('Invalid prefer_regex value, ' \
|
||||||
|
'invalid RE pattern: {}'.format(prefer_regex))
|
||||||
|
exit()
|
||||||
|
|
||||||
avoid_regex = conf_parse_string('avoid_regex')
|
avoid_regex = conf_parse_string('avoid_regex')
|
||||||
|
if avoid_regex == '':
|
||||||
|
print('Invalid avoid_regex value, ' \
|
||||||
|
'regex pattern must not be empty')
|
||||||
|
exit()
|
||||||
|
# RE pattern validation
|
||||||
|
try:
|
||||||
|
search(avoid_regex, '')
|
||||||
|
except sre_constants.error:
|
||||||
|
print('Invalid avoid_regex value, ' \
|
||||||
|
'invalid RE pattern: {}'.format(avoid_regex))
|
||||||
|
exit()
|
||||||
|
|
||||||
prefer_re_cmdline = conf_parse_string('prefer_re_cmdline')
|
prefer_re_cmdline = conf_parse_string('prefer_re_cmdline')
|
||||||
|
if prefer_re_cmdline == '':
|
||||||
|
print('Invalid prefer_re_cmdline value, ' \
|
||||||
|
'regex pattern must not be empty')
|
||||||
|
exit()
|
||||||
|
# RE pattern validation
|
||||||
|
try:
|
||||||
|
search(prefer_re_cmdline, '')
|
||||||
|
except sre_constants.error:
|
||||||
|
print('Invalid prefer_re_cmdline value, ' \
|
||||||
|
'invalid RE pattern: {}'.format(prefer_re_cmdline))
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
avoid_re_cmdline = conf_parse_string('avoid_re_cmdline')
|
avoid_re_cmdline = conf_parse_string('avoid_re_cmdline')
|
||||||
|
if avoid_re_cmdline == '':
|
||||||
|
print('Invalid avoid_re_cmdline value, ' \
|
||||||
|
'regex pattern must not be empty')
|
||||||
|
exit()
|
||||||
|
# RE pattern validation
|
||||||
|
try:
|
||||||
|
search(avoid_re_cmdline, '')
|
||||||
|
except sre_constants.error:
|
||||||
|
print('Invalid avoid_re_cmdline value, ' \
|
||||||
|
'invalid RE pattern: {}'.format(avoid_re_cmdline))
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
prefer_re_uid = conf_parse_string('prefer_re_uid')
|
||||||
|
if prefer_re_uid == '':
|
||||||
|
print('Invalid prefer_re_uid value, ' \
|
||||||
|
'regex pattern must not be empty')
|
||||||
|
exit()
|
||||||
|
# RE pattern validation
|
||||||
|
try:
|
||||||
|
search(prefer_re_uid, '')
|
||||||
|
except sre_constants.error:
|
||||||
|
print('Invalid prefer_re_uid value, ' \
|
||||||
|
'invalid RE pattern: {}'.format(prefer_re_uid))
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
avoid_re_uid = conf_parse_string('avoid_re_uid')
|
||||||
|
if avoid_re_uid == '':
|
||||||
|
print('Invalid avoid_re_uid value, ' \
|
||||||
|
'regex pattern must not be empty')
|
||||||
|
exit()
|
||||||
|
# RE pattern validation
|
||||||
|
try:
|
||||||
|
search(avoid_re_uid, '')
|
||||||
|
except sre_constants.error:
|
||||||
|
print('Invalid avoid_re_uid value, ' \
|
||||||
|
'invalid RE pattern: {}'.format(avoid_re_uid))
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
mem_min_sigterm_kb, mem_min_sigterm_mb, mem_min_sigterm_percent = calculate_percent(
|
mem_min_sigterm_kb, mem_min_sigterm_mb, mem_min_sigterm_percent = calculate_percent(
|
||||||
@ -1059,15 +1122,6 @@ else:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if 'prefer_cmd_factor' in config_dict:
|
if 'prefer_cmd_factor' in config_dict:
|
||||||
prefer_cmd_factor = string_to_float_convert_test(config_dict['prefer_cmd_factor'])
|
prefer_cmd_factor = string_to_float_convert_test(config_dict['prefer_cmd_factor'])
|
||||||
if prefer_cmd_factor is None:
|
if prefer_cmd_factor is None:
|
||||||
@ -1094,20 +1148,30 @@ else:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
if 'prefer_uid_factor' in config_dict:
|
||||||
|
prefer_uid_factor = string_to_float_convert_test(config_dict['prefer_uid_factor'])
|
||||||
|
if prefer_uid_factor is None:
|
||||||
|
print('Invalid prefer_uid_factor value, not float\nExit')
|
||||||
|
exit()
|
||||||
|
if prefer_uid_factor < 1 and prefer_uid_factor > 1000:
|
||||||
|
print('prefer_uid_factor value out of range [1; 1000]\nExit')
|
||||||
|
exit()
|
||||||
|
else:
|
||||||
|
print('prefer_uid_factor not in config\nExit')
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
if 'avoid_uid_factor' in config_dict:
|
||||||
|
avoid_uid_factor = string_to_float_convert_test(config_dict['avoid_uid_factor'])
|
||||||
|
if avoid_uid_factor is None:
|
||||||
|
print('Invalid avoid_uid_factor value, not float\nExit')
|
||||||
|
exit()
|
||||||
|
if avoid_uid_factor < 1 and avoid_uid_factor > 1000:
|
||||||
|
print('avoid_uid_factor value out of range [1; 1000]\nExit')
|
||||||
|
exit()
|
||||||
|
else:
|
||||||
|
print('avoid_uid_factor not in config\nExit')
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if 'min_time_between_warnings' in config_dict:
|
if 'min_time_between_warnings' in config_dict:
|
||||||
@ -1329,16 +1393,11 @@ if print_config:
|
|||||||
# for calculating the column width when printing mem and zram
|
# for calculating the column width when printing mem and zram
|
||||||
mem_len = len(str(round(mem_total / 1024.0)))
|
mem_len = len(str(round(mem_total / 1024.0)))
|
||||||
|
|
||||||
|
|
||||||
if gui_notifications or gui_low_memory_warnings:
|
if gui_notifications or gui_low_memory_warnings:
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
notify_sig_dict = {SIGKILL: 'Killing',
|
notify_sig_dict = {SIGKILL: 'Killing',
|
||||||
SIGTERM: 'Terminating'}
|
SIGTERM: 'Terminating'}
|
||||||
|
|
||||||
if regex_matching:
|
|
||||||
from re import search
|
|
||||||
|
|
||||||
|
|
||||||
rate_mem = rate_mem * 1048576
|
rate_mem = rate_mem * 1048576
|
||||||
rate_swap = rate_swap * 1048576
|
rate_swap = rate_swap * 1048576
|
||||||
rate_zram = rate_zram * 1048576
|
rate_zram = rate_zram * 1048576
|
||||||
|
42
nohang.conf
42
nohang.conf
@ -108,10 +108,11 @@ min_delay_after_sigkill = 3
|
|||||||
oom_score_adj_max значение oom_score_adj будет опущено
|
oom_score_adj_max значение oom_score_adj будет опущено
|
||||||
до oom_score_adj_max перед поиском жертвы.
|
до oom_score_adj_max перед поиском жертвы.
|
||||||
|
|
||||||
|
Enabling the option requires root privileges.
|
||||||
Valid values are True and False.
|
Valid values are True and False.
|
||||||
Values are case sensitive.
|
Values are case sensitive.
|
||||||
|
|
||||||
decrease_oom_score_adj = False
|
decrease_oom_score_adj = True
|
||||||
|
|
||||||
Valid values are integers from the range [0; 1000].
|
Valid values are integers from the range [0; 1000].
|
||||||
|
|
||||||
@ -141,13 +142,15 @@ regex_matching = False
|
|||||||
|
|
||||||
RE pattern must not be empty!
|
RE pattern must not be empty!
|
||||||
|
|
||||||
Matching with process names
|
Matching process names with RE patterns
|
||||||
|
|
||||||
|
Прблема в том, что если включил регекс, то нельзя выключить ненужный мэтчинг. Можно только установить фактор в единицу.
|
||||||
|
|
||||||
prefer_regex = foo
|
prefer_regex = foo
|
||||||
|
|
||||||
Valid values are floating-point numbers from the range [1; 1000].
|
Valid values are floating-point numbers from the range [1; 1000].
|
||||||
|
|
||||||
prefer_factor = 3
|
prefer_factor = 2
|
||||||
|
|
||||||
Badness of processes whose names correspond to avoid_regex will
|
Badness of processes whose names correspond to avoid_regex will
|
||||||
be calculated by the following formula:
|
be calculated by the following formula:
|
||||||
@ -155,22 +158,41 @@ prefer_factor = 3
|
|||||||
|
|
||||||
# Need more examples
|
# Need more examples
|
||||||
|
|
||||||
avoid_regex = ^(sshd|Xorg)$
|
avoid_regex = foo
|
||||||
|
|
||||||
Valid values are floating-point numbers from the range [1; 1000].
|
Valid values are floating-point numbers from the range [1; 1000].
|
||||||
|
|
||||||
avoid_factor = 3
|
avoid_factor = 2
|
||||||
|
|
||||||
|
|
||||||
Matching with cmdlines
|
Matching cmlines with RE patterns
|
||||||
|
|
||||||
prefer_re_cmdline = ^/usr/lib/firefox
|
# re_match_cmdline = True
|
||||||
|
|
||||||
avoid_re_cmdline = ^/usr/lib/virtualbox
|
prefer_re_cmdline = voo
|
||||||
|
|
||||||
|
avoid_re_cmdline = foo
|
||||||
|
|
||||||
|
prefer_cmd_factor = 20
|
||||||
|
|
||||||
|
avoid_cmd_factor = 2
|
||||||
|
|
||||||
|
|
||||||
|
Matching UIDs with RE patterns
|
||||||
|
|
||||||
|
Note that UID=0 итак избегается для убийства.
|
||||||
|
|
||||||
|
# re_match_uid = True
|
||||||
|
|
||||||
|
prefer_re_uid = ^(0)$
|
||||||
|
|
||||||
|
avoid_re_uid=foo
|
||||||
|
|
||||||
|
prefer_uid_factor = 200
|
||||||
|
|
||||||
|
avoid_uid_factor = 2
|
||||||
|
|
||||||
prefer_cmd_factor = 3
|
|
||||||
|
|
||||||
avoid_cmd_factor = 3
|
|
||||||
|
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
|
Loading…
Reference in New Issue
Block a user