implement RE UID matching; vatidation RE patterns at startup

This commit is contained in:
Alexey Avramov 2018-12-11 18:53:42 +09:00
parent 4fcab87fc5
commit 68e0efc6a5
2 changed files with 149 additions and 68 deletions

175
nohang
View File

@ -194,7 +194,6 @@ def pid_to_name(pid):
return ''
def pid_to_cmdline(pid):
"""
Get process cmdline by pid.
@ -209,11 +208,11 @@ def pid_to_cmdline(pid):
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():
@ -324,13 +323,6 @@ def sleep_after_send_signal(signal):
sleep(min_delay_after_sigterm)
def find_victim_and_send_signal(signal):
"""
Find victim with highest badness and send SIGTERM/SIGKILL
@ -338,10 +330,11 @@ def find_victim_and_send_signal(signal):
pid_badness_list = []
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':
continue
# find and modify badness (if it needs)
try:
badness = int(rline1('/proc/' + pid + '/oom_score'))
@ -354,13 +347,13 @@ def find_victim_and_send_signal(signal):
name = pid_to_name(pid)
cmdline = pid_to_cmdline(pid)
#uid = pid_to_uid(pid)
uid = pid_to_uid(pid)
# skip kthreads
if cmdline == '':
continue
#print([pid], [name], [cmdline])
#print([uid], [name], [cmdline])
if search(avoid_regex, name) is not None:
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(
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:
badness = 0
except ProcessLookupError:
@ -449,11 +452,9 @@ def find_victim_and_send_signal(signal):
except ValueError:
pass
if detailed_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:
command = 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)
sleep_after_send_signal(signal)
@ -664,18 +664,6 @@ def calculate_percent(arg_key):
##########################################################################
# find mem_total
# 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
# conf_parameters_dict
@ -846,18 +833,94 @@ mlockall = conf_parse_bool('mlockall')
gui_low_memory_warnings = conf_parse_bool('gui_low_memory_warnings')
gui_notifications = conf_parse_bool('gui_notifications')
decrease_oom_score_adj = conf_parse_bool('decrease_oom_score_adj')
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')
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')
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')
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')
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(
@ -1059,15 +1122,6 @@ else:
exit()
if 'prefer_cmd_factor' in config_dict:
prefer_cmd_factor = string_to_float_convert_test(config_dict['prefer_cmd_factor'])
if prefer_cmd_factor is None:
@ -1094,20 +1148,30 @@ else:
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:
@ -1329,16 +1393,11 @@ if print_config:
# for calculating the column width when printing mem and zram
mem_len = len(str(round(mem_total / 1024.0)))
if gui_notifications or gui_low_memory_warnings:
from subprocess import Popen, PIPE
notify_sig_dict = {SIGKILL: 'Killing',
SIGTERM: 'Terminating'}
if regex_matching:
from re import search
rate_mem = rate_mem * 1048576
rate_swap = rate_swap * 1048576
rate_zram = rate_zram * 1048576

View File

@ -108,10 +108,11 @@ min_delay_after_sigkill = 3
oom_score_adj_max значение oom_score_adj будет опущено
до oom_score_adj_max перед поиском жертвы.
Enabling the option requires root privileges.
Valid values are True and False.
Values are case sensitive.
decrease_oom_score_adj = False
decrease_oom_score_adj = True
Valid values are integers from the range [0; 1000].
@ -141,13 +142,15 @@ regex_matching = False
RE pattern must not be empty!
Matching with process names
Matching process names with RE patterns
Прблема в том, что если включил регекс, то нельзя выключить ненужный мэтчинг. Можно только установить фактор в единицу.
prefer_regex = foo
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
be calculated by the following formula:
@ -155,22 +158,41 @@ prefer_factor = 3
# Need more examples
avoid_regex = ^(sshd|Xorg)$
avoid_regex = foo
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
#####################################################################