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 '' 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

View File

@ -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
##################################################################### #####################################################################