1501 lines
53 KiB
Python
Executable File
1501 lines
53 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
|
||
# Nohang - The No Hang Daemon for Linux
|
||
|
||
##########################################################################
|
||
|
||
# импорты
|
||
|
||
import os
|
||
from operator import itemgetter
|
||
from time import sleep
|
||
import datetime
|
||
from argparse import ArgumentParser
|
||
import time
|
||
##########################################################################
|
||
|
||
# задание констант
|
||
|
||
version = 'upstream'
|
||
|
||
sig_dict = {9: 'SIGKILL', 15: 'SIGTERM'}
|
||
|
||
# директория, в которой запущен скрипт
|
||
cd = os.getcwd()
|
||
|
||
# где искать конфиг, если не указан через опцию -c/--config
|
||
default_configs = (
|
||
cd + '/nohang.conf',
|
||
'/etc/nohang/nohang.conf'
|
||
)
|
||
|
||
# универсальное сообщение при инвалидном конфиге
|
||
conf_err_mess = '\nSet up the path to the valid config file with -c/--confi' \
|
||
'g option!\nExit'
|
||
|
||
# означает, что при задани zram disksize = 10000M доступная память
|
||
# уменьшится на 42M
|
||
# найден экспериментально, требует уточнения с разными ядрами и архитектурами
|
||
# на небольших дисксайзах (до гигабайта) может быть больше, до 0.0045
|
||
# создатель модуля zram утверждает, что zram_disksize_factor доожен быть 0.001
|
||
# ("zram uses about 0.1% of the size of the disk"
|
||
# - https://www.kernel.org/doc/Documentation/blockdev/zram.txt),
|
||
# но это утверждение противоречит опытным данным
|
||
zram_disksize_factor = 0.0042
|
||
|
||
##########################################################################
|
||
|
||
# задание функций
|
||
|
||
|
||
def string_to_float_convert_test(string):
|
||
try:
|
||
return float(string)
|
||
except ValueError:
|
||
return None
|
||
|
||
|
||
def string_to_int_convert_test(string):
|
||
try:
|
||
return int(string)
|
||
except ValueError:
|
||
return None
|
||
|
||
|
||
# извлечение праметра из словаря конфига, возврат str
|
||
def conf_parse_string(param):
|
||
if param in config_dict:
|
||
return config_dict[param].strip()
|
||
else:
|
||
print('{} not in config\nExit'.format(param))
|
||
exit()
|
||
|
||
|
||
# извлечение праметра из словаря конфига, возврат bool
|
||
def conf_parse_bool(param):
|
||
if param in config_dict:
|
||
param_str = config_dict[param]
|
||
if param_str == 'True':
|
||
return True
|
||
elif param_str == 'False':
|
||
return False
|
||
else:
|
||
print('Invalid {} value {} (shou' \
|
||
'ld be True or False)\nExit'.format(param, param_str))
|
||
exit()
|
||
else:
|
||
print('{} not in config\nExit'.format(param))
|
||
exit()
|
||
|
||
|
||
def func_decrease_oom_score_adj(oom_score_adj_max):
|
||
# цикл для наполнения oom_list
|
||
for i in os.listdir('/proc'):
|
||
|
||
# пропускаем элементы, не состоящие только из цифр
|
||
if i.isdigit() is not True:
|
||
continue
|
||
|
||
try:
|
||
oom_score_adj = int(rline1('/proc/' + i + '/oom_score_adj'))
|
||
if oom_score_adj > oom_score_adj_max:
|
||
write('/proc/' + i + '/oom_score_adj',
|
||
str(oom_score_adj_max) + '\n')
|
||
except FileNotFoundError:
|
||
pass
|
||
except ProcessLookupError:
|
||
pass
|
||
|
||
|
||
# чтение первой строки файла
|
||
def rline1(path):
|
||
with open(path) as f:
|
||
for line in f:
|
||
return line[:-1]
|
||
|
||
|
||
# запись в файл
|
||
def write(path, string):
|
||
with open(path, 'w') as f:
|
||
f.write(string)
|
||
|
||
|
||
def append_log(string):
|
||
try:
|
||
with open(logfile, 'a') as f:
|
||
f.write(string + '\n')
|
||
except PermissionError:
|
||
print(
|
||
' Cannot append log {}: PermissionError'.format(
|
||
logfile))
|
||
except IsADirectoryError:
|
||
print(
|
||
' Cannot append log {}: IsADirectoryError'.format(
|
||
logfile))
|
||
|
||
|
||
def kib_to_mib(num):
|
||
return round(num / 1024.0)
|
||
|
||
|
||
def percent(num):
|
||
return round(num * 100, 1)
|
||
|
||
|
||
def just_percent_mem(num):
|
||
return str(round(num * 100, 1)).rjust(4, ' ')
|
||
|
||
|
||
def just_percent_swap(num):
|
||
return str(round(num * 100, 1)).rjust(5, ' ')
|
||
|
||
|
||
# K -> M, выравнивание по правому краю
|
||
def human(num, lenth):
|
||
return str(round(num / 1024)).rjust(lenth, ' ')
|
||
|
||
|
||
# возвращает disksize и mem_used_total по zram id
|
||
def zram_stat(zram_id):
|
||
try:
|
||
disksize = rline1('/sys/block/' + zram_id + '/disksize')
|
||
except FileNotFoundError:
|
||
return '0', '0'
|
||
if disksize == ['0\n']:
|
||
return '0', '0'
|
||
try:
|
||
mm_stat = rline1('/sys/block/' + zram_id + '/mm_stat').split(' ')
|
||
mm_stat_list = []
|
||
for i in mm_stat:
|
||
if i != '':
|
||
mm_stat_list.append(i)
|
||
mem_used_total = mm_stat_list[2]
|
||
except FileNotFoundError:
|
||
mem_used_total = rline1('/sys/block/' + zram_id + '/mem_used_total')
|
||
return disksize, mem_used_total # BYTES, str
|
||
|
||
|
||
# имя через пид
|
||
def pid_to_name(pid):
|
||
try:
|
||
with open('/proc/' + pid + '/status') as f:
|
||
for line in f:
|
||
return line[:-1].split('\t')[1]
|
||
except FileNotFoundError:
|
||
return '<unknown>'
|
||
except ProcessLookupError:
|
||
return '<unknown>'
|
||
|
||
|
||
def send_notify_warn():
|
||
# текст отправляемого уведомления
|
||
if mem_used_zram > 0:
|
||
info = '"<i>MemAvailable:</i> <b>{} MiB</b>\n<i>SwapFree:</i> <b>{} MiB</b>\n<i>MemUsedZram:</i> <b>{} MiB</b>" &'.format(
|
||
kib_to_mib(mem_available),
|
||
kib_to_mib(swap_free),
|
||
kib_to_mib(mem_used_zram))
|
||
elif swap_free > 0:
|
||
info = '"<i>MemAvailable:</i> <b>{} MiB</b>\n<i>SwapFree:</i>' \
|
||
' <b>{} MiB</b>" &'.format(
|
||
kib_to_mib(mem_available),
|
||
kib_to_mib(swap_free))
|
||
else:
|
||
info = '"<i>MemAvailable:</i> <b>{} MiB</b>, <b>{} %</b>" &'.format(
|
||
kib_to_mib(mem_available),
|
||
round(mem_available / mem_total * 100, 1))
|
||
|
||
if root:
|
||
# отправляем уведомления всем залогиненным
|
||
for uid in os.listdir('/run/user'):
|
||
root_notify_command = 'sudo -u {} DISPLAY={} notify-s' \
|
||
'end {} "Warning: Low Memory" '.format(
|
||
users_dict[uid], root_display, notify_options)
|
||
os.system(root_notify_command + info)
|
||
else:
|
||
# отправляем уведомление пользователю, который запустил nohang
|
||
user_notify_command = 'notify-send {} "Warning: Low Memory" '.format(
|
||
notify_options)
|
||
os.system(user_notify_command + info)
|
||
|
||
|
||
def send_notify(signal, name, pid, oom_score, vm_rss, vm_swap):
|
||
# текст отправляемого уведомления
|
||
info = '"<u>Nohang</u> sent <u>{}</u> \nto the process <b>{}</b> \n<i>P' \
|
||
'id:</i> <b>{}</b> \n<i>Badness:</i> <b>{}</b> \n<i>VmRSS:</i> <b' \
|
||
'>{} MiB</b> \n<i>VmSwap:</i> <b>{} MiB</b>" &'.format(
|
||
sig_dict[signal], name, pid, oom_score, vm_rss, vm_swap)
|
||
if root:
|
||
# отправляем уведомление всем залогиненным пользователям
|
||
for uid in os.listdir('/run/user'):
|
||
root_notify_command = 'sudo -u {} DISPLAY={} notify-send {} "Pr' \
|
||
'eventing OOM" '.format(
|
||
users_dict[uid], root_display, notify_options)
|
||
os.system(root_notify_command + info)
|
||
else:
|
||
# отправляем уведомление пользователю, который запустил nohang
|
||
user_notify_command = 'notify-send {} "Preventing OOM" '.format(
|
||
notify_options)
|
||
os.system(user_notify_command + info)
|
||
|
||
|
||
def send_notify_black(signal, name, pid):
|
||
# текст отправляемого уведомления
|
||
info = '"<u>Nohang</u> sent <u>{}</u>\nto blacklisted proce' \
|
||
'ss <b>{}</b>, <i>Pid</i> <b>{}</b>" &'.format(
|
||
sig_dict[signal], name, pid)
|
||
if root:
|
||
# отправляем уведомление всем залогиненным пользователям
|
||
for uid in os.listdir('/run/user'):
|
||
root_notify_command = 'sudo -u {} DISPLAY={} notify-send {} "Pr' \
|
||
'eventing OOM" '.format(
|
||
users_dict[uid], root_display, notify_options)
|
||
os.system(root_notify_command + info)
|
||
else:
|
||
# отправляем уведомление пользователю, который запустил nohang
|
||
user_notify_command = 'notify-send {} "Preventing OOM" '.format(
|
||
notify_options)
|
||
os.system(user_notify_command + info)
|
||
|
||
|
||
def sleep_after_send_signal(signal):
|
||
if signal is 9:
|
||
if print_sleep_periods:
|
||
print(' sleep', min_delay_after_sigkill)
|
||
sleep(min_delay_after_sigterm)
|
||
else:
|
||
if print_sleep_periods:
|
||
print(' sleep', min_delay_after_sigterm)
|
||
sleep(min_delay_after_sigterm)
|
||
|
||
|
||
def find_victim_and_send_signal(signal):
|
||
|
||
time_now_and_mem_info = datetime.datetime.today().strftime(
|
||
'\n%Y-%m-%d %a %H:%M:%S\n{}'.format(mem_info))
|
||
|
||
print(time_now_and_mem_info)
|
||
|
||
if logging:
|
||
append_log(time_now_and_mem_info)
|
||
|
||
# выставляем потолок для oom_score_adj всех процессов
|
||
if decrease_oom_score_adj and root:
|
||
func_decrease_oom_score_adj(oom_score_adj_max)
|
||
|
||
# получаем список процессов ((pid, badness))
|
||
oom_list = []
|
||
|
||
if use_regex_lists:
|
||
|
||
# pid list, rename var! blacklisted_pids
|
||
oom_blacklist_regex = []
|
||
|
||
for pid in os.listdir('/proc'):
|
||
if pid.isdigit() is not True:
|
||
continue
|
||
try:
|
||
# пошла итерация для каждого существующего процесса
|
||
oom_score = int(rline1('/proc/' + pid + '/oom_score'))
|
||
name = pid_to_name(pid)
|
||
|
||
# если имя в белом списке,то пропускаем
|
||
res = fullmatch(whitelist_regex, name)
|
||
if res is not None:
|
||
print(' {} (Pid: {}) matches with whitelist_regex'.format(name, pid)),
|
||
continue
|
||
|
||
# если имя в черном списке - добавляем Pid в список для убийства
|
||
# ДОБАВЛЯТЬ И ИМЯ СРАЗУ
|
||
# ВАРИАНТ - СРАЗУ УБИВАТЬ
|
||
res = fullmatch(blacklist_regex, name)
|
||
if res is not None:
|
||
oom_blacklist_regex.append(pid)
|
||
print(' {} (Pid: {}) matches with blacklist_regex'.format(name, pid)),
|
||
|
||
res = fullmatch(avoidlist_regex, name)
|
||
if res is not None:
|
||
# тут уже получаем badness
|
||
oom_score = int(oom_score / avoidlist_factor)
|
||
print(' {} (Pid: {}, Badness {}) matches with avoidlist_regex'.format(name, pid, oom_score)),
|
||
|
||
res = fullmatch(preferlist_regex, name)
|
||
if res is not None:
|
||
oom_score = int((oom_score + 1) * preferlist_factor)
|
||
print(' {} (Pid: {}, Badness {}) matches with preferlist_regex'.format(name, pid, oom_score)),
|
||
|
||
except FileNotFoundError:
|
||
oom_score = 0
|
||
except ProcessLookupError:
|
||
oom_score = 0
|
||
oom_list.append((pid, oom_score))
|
||
|
||
|
||
|
||
# если найден хоть один в черном списке - нет смысла сравнивать с остальными
|
||
|
||
if oom_blacklist_regex != []:
|
||
|
||
for pid in oom_blacklist_regex:
|
||
|
||
name = pid_to_name(pid)
|
||
print(' Preventing OOM: trying to send the {} signal to blacklisted process {}, Pid: {}'.format(
|
||
sig_dict[signal], name, pid))
|
||
|
||
try:
|
||
os.kill(int(pid), signal)
|
||
print(' Success')
|
||
|
||
if desktop_notifications:
|
||
send_notify_black(signal, name, pid)
|
||
|
||
except FileNotFoundError:
|
||
print(' No such process')
|
||
except ProcessLookupError:
|
||
print(' No such process')
|
||
except PermissionError:
|
||
print(' Operation not permitted')
|
||
|
||
# после отправки сигнала процессам из черного списка поспать и выйти из функции
|
||
sleep_after_send_signal(signal)
|
||
return 0
|
||
|
||
else:
|
||
|
||
# not use regex
|
||
for i in os.listdir('/proc'):
|
||
if i[0].isdecimal() is not True:
|
||
continue
|
||
try:
|
||
oom_score = int(rline1('/proc/' + i + '/oom_score'))
|
||
except FileNotFoundError:
|
||
oom_score = 0
|
||
except ProcessLookupError:
|
||
oom_score = 0
|
||
oom_list.append((i, oom_score))
|
||
|
||
|
||
# получаем отсортированный по oom_score (по badness!) список пар (pid, oom_score)
|
||
pid_tuple_list = sorted(oom_list, key=itemgetter(1), reverse=True)[0]
|
||
|
||
# получаем максимальный oom_score
|
||
oom_score = pid_tuple_list[1]
|
||
|
||
|
||
if oom_score >= oom_score_min:
|
||
|
||
# пытаемся отправить сигнал найденной жертве
|
||
|
||
pid = pid_tuple_list[0]
|
||
|
||
name = pid_to_name(pid)
|
||
|
||
# находим VmRSS и VmSwap процесса, которому попытаемся послать сигнал
|
||
try:
|
||
with open('/proc/' + pid + '/status') as f:
|
||
for n, line in enumerate(f):
|
||
if n is vm_rss_index:
|
||
vm_rss = kib_to_mib(int(
|
||
line.split('\t')[1][:-4]))
|
||
continue
|
||
if n is vm_swap_index:
|
||
vm_swap = kib_to_mib(int(
|
||
line.split('\t')[1][:-4]))
|
||
break
|
||
except FileNotFoundError:
|
||
vm_rss = 0
|
||
vm_swap = 0
|
||
except ProcessLookupError:
|
||
vm_rss = 0
|
||
vm_swap = 0
|
||
except IndexError:
|
||
vm_rss = 0
|
||
vm_swap = 0
|
||
|
||
|
||
if name in etc_dict and signal is 15:
|
||
command = etc_dict[name]
|
||
etc_info = ' Process {} is a victim,\n Pid: {}, Badness: {}, VmRSS: {} MiB, VmSwap: {} MiB\n Execute command: {}'.format(name, pid, oom_score, vm_rss, vm_swap, command)
|
||
print(etc_info)
|
||
os.system(etc_dict[name])
|
||
append_log(etc_info)
|
||
|
||
else:
|
||
try_to_send = ' Preventing OOM: trying to send the {} signal to {},\n Pid: {}, Badness: {}, VmRSS: {} MiB, VmSwap: {} MiB'.format(sig_dict[signal], name, pid, oom_score, vm_rss, vm_swap)
|
||
|
||
print(try_to_send)
|
||
|
||
try:
|
||
os.kill(int(pid), signal)
|
||
send_result = ' Success'
|
||
|
||
if desktop_notifications:
|
||
send_notify(signal, name, pid, oom_score, vm_rss, vm_swap)
|
||
|
||
except FileNotFoundError:
|
||
send_result = ' No such process'
|
||
except ProcessLookupError:
|
||
send_result = ' No such process'
|
||
|
||
print(send_result)
|
||
|
||
if logging:
|
||
append_log(try_to_send + '\n' + send_result)
|
||
|
||
else:
|
||
|
||
badness_is_too_small = ' oom_score {} < oom_score_min {}'.format(
|
||
oom_score, oom_score_min)
|
||
|
||
print(badness_is_too_small)
|
||
|
||
if logging:
|
||
append_log(badness_is_too_small)
|
||
|
||
sleep_after_send_signal(signal)
|
||
|
||
|
||
def sleep_after_check_mem():
|
||
# задание периода сна в зависимости от рейтов и уровней доступной памяти
|
||
t_mem = mem_available / rate_mem
|
||
t_swap = swap_free / rate_swap
|
||
t_zram = (mem_total - mem_used_zram) / rate_zram
|
||
|
||
t_mem_swap = t_mem + t_swap
|
||
t_mem_zram = t_mem + t_zram
|
||
|
||
if t_mem_swap <= t_mem_zram:
|
||
t = t_mem_swap
|
||
else:
|
||
t = t_mem_zram
|
||
|
||
try:
|
||
if print_sleep_periods:
|
||
print('sleep', round(t, 2), ' (t_mem={}, t_swap={}, t_zram={})'.format(
|
||
round(t_mem, 2), round(t_swap, 2), round(t_zram, 2)))
|
||
sleep(t)
|
||
except KeyboardInterrupt:
|
||
exit()
|
||
|
||
|
||
##########################################################################
|
||
|
||
# поиск позиций и mem_total
|
||
|
||
|
||
with open('/proc/meminfo') as file:
|
||
mem_list = file.readlines()
|
||
|
||
mem_list_names = []
|
||
for s in mem_list:
|
||
mem_list_names.append(s.split(':')[0])
|
||
|
||
if mem_list_names[2] != 'MemAvailable':
|
||
print('Your Linux kernel is too old, 3.14+ requie\nExit')
|
||
exit()
|
||
|
||
swap_total_index = mem_list_names.index('SwapTotal')
|
||
swap_free_index = swap_total_index + 1
|
||
|
||
mem_total = int(mem_list[0].split(':')[1].strip(' kB\n'))
|
||
|
||
with open('/proc/self/status') as file:
|
||
status_list = file.readlines()
|
||
|
||
# список имен из /proc/*/status для дальнейшего поиска позиций VmRSS and VmSwap
|
||
status_names = []
|
||
for s in status_list:
|
||
status_names.append(s.split(':')[0])
|
||
|
||
vm_rss_index = status_names.index('VmRSS')
|
||
vm_swap_index = status_names.index('VmSwap')
|
||
|
||
##########################################################################
|
||
|
||
# получение пути к конфигу
|
||
|
||
# парсинг аргументов командной строки
|
||
parser = ArgumentParser()
|
||
parser.add_argument(
|
||
'-c',
|
||
'--config',
|
||
help="""path to the config file, default values:
|
||
./nohang.conf, /etc/nohang/nohang.conf""",
|
||
default=None,
|
||
type=str
|
||
)
|
||
|
||
args = parser.parse_args()
|
||
|
||
arg_config = args.config
|
||
|
||
|
||
if arg_config is None:
|
||
|
||
config = None
|
||
for i in default_configs:
|
||
if os.path.exists(i):
|
||
config = i
|
||
break
|
||
if config is None:
|
||
print('По дефолтным путям конфиг не найден\n', conf_err_mess)
|
||
exit()
|
||
|
||
else:
|
||
|
||
if os.path.exists(arg_config):
|
||
config = arg_config
|
||
else:
|
||
print("File {} doesn't exists{}".format(arg_config, conf_err_mess))
|
||
exit()
|
||
|
||
|
||
print('The path to the config file to be used:')
|
||
print(config)
|
||
|
||
##########################################################################
|
||
|
||
# парсинг конфига с получением словаря параметров
|
||
|
||
# conf_parameters_dict
|
||
# conf_restart_dict
|
||
|
||
try:
|
||
with open(config) as f:
|
||
|
||
# словарь с параметрами конфига
|
||
config_dict = dict()
|
||
|
||
# словарь с именами и командами для параметра execute_the_command
|
||
etc_dict = dict()
|
||
|
||
for line in f:
|
||
|
||
a = line.startswith('#')
|
||
b = line.startswith('\n')
|
||
c = line.startswith('\t')
|
||
d = line.startswith(' ')
|
||
|
||
etc = line.startswith('**')
|
||
|
||
if not a and not b and not c and not d and not etc:
|
||
a = line.split('=')
|
||
config_dict[a[0].strip()] = a[1].strip()
|
||
|
||
if etc:
|
||
a = line[2:].split('::')
|
||
etc_name = a[0].strip()
|
||
etc_command = a[1].strip()
|
||
if len(etc_name) > 15:
|
||
print('инвалид конфиг, длина имени процесса не должна превышать 15 символов\nExit')
|
||
exit()
|
||
etc_dict[etc_name] = etc_command
|
||
|
||
except PermissionError:
|
||
print('PermissionError', conf_err_mess)
|
||
exit()
|
||
except UnicodeDecodeError:
|
||
print('UnicodeDecodeError', conf_err_mess)
|
||
exit()
|
||
except IsADirectoryError:
|
||
print('IsADirectoryError', conf_err_mess)
|
||
exit()
|
||
except IndexError:
|
||
print('IndexError', conf_err_mess)
|
||
exit()
|
||
|
||
##########################################################################
|
||
|
||
# извлечение параметров из словаря
|
||
# проверка наличия всех необходимых параметров
|
||
# валидация всех параметров
|
||
|
||
|
||
print_config = conf_parse_bool('print_config')
|
||
|
||
|
||
print_mem_check_results = conf_parse_bool('print_mem_check_results')
|
||
|
||
|
||
print_sleep_periods = conf_parse_bool('print_sleep_periods')
|
||
|
||
|
||
mlockall = conf_parse_bool('mlockall')
|
||
|
||
|
||
if 'self_nice' in config_dict:
|
||
self_nice = string_to_int_convert_test(config_dict['self_nice'])
|
||
if self_nice is None:
|
||
print('Invalid self_nice value, not integer\nExit')
|
||
exit()
|
||
if self_nice < -20 or self_nice > 19:
|
||
print('Недопустимое значение self_nice\nExit')
|
||
exit()
|
||
else:
|
||
print('self_nice not in config\nExit')
|
||
exit()
|
||
|
||
|
||
if 'self_oom_score_adj' in config_dict:
|
||
self_oom_score_adj = string_to_int_convert_test(
|
||
config_dict['self_oom_score_adj'])
|
||
if self_oom_score_adj is None:
|
||
print('Invalid self_oom_score_adj value, not integer\nExit')
|
||
exit()
|
||
if self_oom_score_adj < -1000 or self_oom_score_adj > 1000:
|
||
print('Недопустимое значение self_oom_score_adj\nExit')
|
||
exit()
|
||
else:
|
||
print('self_oom_score_adj not in config\nExit')
|
||
exit()
|
||
|
||
|
||
if 'rate_mem' in config_dict:
|
||
rate_mem = string_to_float_convert_test(config_dict['rate_mem'])
|
||
if rate_mem is None:
|
||
print('Invalid rate_mem value, not float\nExit')
|
||
exit()
|
||
if rate_mem <= 0:
|
||
print('rate_mem должен быть положительным\nExit')
|
||
exit()
|
||
else:
|
||
print('rate_mem not in config\nExit')
|
||
exit()
|
||
|
||
|
||
if 'rate_swap' in config_dict:
|
||
rate_swap = string_to_float_convert_test(config_dict['rate_swap'])
|
||
if rate_swap is None:
|
||
print('Invalid rate_swap value, not float\nExit')
|
||
exit()
|
||
if rate_swap <= 0:
|
||
print('rate_swap должен быть положительным\nExit')
|
||
exit()
|
||
else:
|
||
print('rate_swap not in config\nExit')
|
||
exit()
|
||
|
||
|
||
if 'rate_zram' in config_dict:
|
||
rate_zram = string_to_float_convert_test(config_dict['rate_zram'])
|
||
if rate_zram is None:
|
||
print('Invalid rate_zram value, not float\nExit')
|
||
exit()
|
||
if rate_zram <= 0:
|
||
print('rate_zram должен быть положительным\nExit')
|
||
exit()
|
||
else:
|
||
print('rate_zram not in config\nExit')
|
||
exit()
|
||
|
||
|
||
if 'mem_min_sigterm' in config_dict:
|
||
mem_min_sigterm = config_dict['mem_min_sigterm']
|
||
|
||
if mem_min_sigterm.endswith('%'):
|
||
# отбрасываем процент, получаем число
|
||
mem_min_sigterm_percent = mem_min_sigterm[:-1].strip()
|
||
# далее флоат тест
|
||
mem_min_sigterm_percent = string_to_float_convert_test(mem_min_sigterm_percent)
|
||
if mem_min_sigterm_percent is None:
|
||
print('Invalid mem_min_sigterm value, not float\nExit')
|
||
exit()
|
||
# окончательная валидация
|
||
if mem_min_sigterm_percent < 0 or mem_min_sigterm_percent > 100:
|
||
print('mem_min_sigterm, выраженный в процентах, должен быть быть в диапазоне [0; 100]\nExit')
|
||
exit()
|
||
|
||
# mem_min_sigterm_percent это теперь чистое валидное флоат число процентов, можно переводить в кб
|
||
mem_min_sigterm_kb = mem_min_sigterm_percent / 100 * mem_total
|
||
mem_min_sigterm_mb = round(mem_min_sigterm_kb / 1024)
|
||
|
||
elif mem_min_sigterm.endswith('M'):
|
||
mem_min_sigterm_mb = string_to_float_convert_test(mem_min_sigterm[:-1].strip())
|
||
if mem_min_sigterm_mb is None:
|
||
print('Invalid mem_min_sigterm value, not float\nExit')
|
||
exit()
|
||
mem_min_sigterm_kb = mem_min_sigterm_mb * 1024
|
||
if mem_min_sigterm_kb > mem_total:
|
||
print('mem_min_sigterm value не должен быть больше MemTotal ({} MiB)\nExit'.format(round(mem_total / 1024)))
|
||
exit()
|
||
mem_min_sigterm_percent = mem_min_sigterm_kb / mem_total * 100
|
||
|
||
else:
|
||
print('Конфиг инвалид, для mem_min_sigterm неверно указаны единицы измерения\nExit')
|
||
exit()
|
||
|
||
else:
|
||
print('mem_min_sigterm not in config\nExit')
|
||
exit()
|
||
|
||
|
||
if 'mem_min_sigkill' in config_dict:
|
||
mem_min_sigkill = config_dict['mem_min_sigkill']
|
||
|
||
if mem_min_sigkill.endswith('%'):
|
||
# отбрасываем процент, получаем число
|
||
mem_min_sigkill_percent = mem_min_sigkill[:-1].strip()
|
||
# далее флоат тест
|
||
mem_min_sigkill_percent = string_to_float_convert_test(mem_min_sigkill_percent)
|
||
if mem_min_sigkill_percent is None:
|
||
print('Invalid mem_min_sigkill value, not float\nExit')
|
||
exit()
|
||
# окончательная валидация
|
||
if mem_min_sigkill_percent < 0 or mem_min_sigkill_percent > 100:
|
||
print('mem_min_sigkill, выраженный в процентах, должен быть быть в диапазоне [0; 100]\nExit')
|
||
exit()
|
||
|
||
# mem_min_sigterm_percent это теперь чистое валидное флоат число процентов, можно переводить в кб
|
||
mem_min_sigkill_kb = mem_min_sigkill_percent / 100 * mem_total
|
||
mem_min_sigkill_mb = round(mem_min_sigkill_kb / 1024)
|
||
|
||
elif mem_min_sigkill.endswith('M'):
|
||
mem_min_sigkill_mb = string_to_float_convert_test(mem_min_sigkill[:-1].strip())
|
||
if mem_min_sigkill_mb is None:
|
||
print('Invalid mem_min_sigkill value, not float\nExit')
|
||
exit()
|
||
mem_min_sigkill_kb = mem_min_sigkill_mb * 1024
|
||
if mem_min_sigkill_kb > mem_total:
|
||
print('mem_min_sigkill value не должен быть больше MemTotal ({} MiB)\nExit'.format(round(mem_total / 1024)))
|
||
exit()
|
||
mem_min_sigkill_percent = mem_min_sigkill_kb / mem_total * 100
|
||
|
||
else:
|
||
print('Конфиг инвалид, для mem_min_sigkill неверно указаны единицы измерения\nExit')
|
||
exit()
|
||
|
||
else:
|
||
print('mem_min_sigkill not in config\nExit')
|
||
exit()
|
||
|
||
|
||
|
||
|
||
|
||
# НУЖНА ВАЛИДАЦИЯ НА МЕСТЕ!
|
||
if 'swap_min_sigterm' in config_dict:
|
||
swap_min_sigterm = config_dict['swap_min_sigterm']
|
||
else:
|
||
print('swap_min_sigterm not in config\nExit')
|
||
exit()
|
||
|
||
|
||
# НУЖНА ВАЛИДАЦИЯ НА МЕСТЕ!
|
||
if 'swap_min_sigkill' in config_dict:
|
||
swap_min_sigkill = config_dict['swap_min_sigkill']
|
||
else:
|
||
print('swap_min_sigkill not in config\nExit')
|
||
exit()
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
if 'zram_max_sigterm' in config_dict:
|
||
zram_max_sigterm = config_dict['zram_max_sigterm']
|
||
|
||
if zram_max_sigterm.endswith('%'):
|
||
# отбрасываем процент, получаем число
|
||
zram_max_sigterm_percent = zram_max_sigterm[:-1].strip()
|
||
# далее флоат тест
|
||
zram_max_sigterm_percent = string_to_float_convert_test(zram_max_sigterm_percent)
|
||
if zram_max_sigterm_percent is None:
|
||
print('Invalid zram_max_sigterm value, not float\nExit')
|
||
exit()
|
||
# окончательная валидация
|
||
if zram_max_sigterm_percent < 0 or zram_max_sigterm_percent > 100:
|
||
print('zram_max_sigterm, выраженный в процентах, должен быть быть в диапазоне [0; 100]\nExit')
|
||
exit()
|
||
|
||
# zram_max_sigterm_percent это теперь чистое валидное флоат число процентов, можно переводить в кб
|
||
zram_max_sigterm_kb = zram_max_sigterm_percent / 100 * mem_total
|
||
zram_max_sigterm_mb = round(zram_max_sigterm_kb / 1024)
|
||
|
||
elif zram_max_sigterm.endswith('M'):
|
||
zram_max_sigterm_mb = string_to_float_convert_test(zram_max_sigterm[:-1].strip())
|
||
if zram_max_sigterm_mb is None:
|
||
print('Invalid zram_max_sigterm value, not float\nExit')
|
||
exit()
|
||
zram_max_sigterm_kb = zram_max_sigterm_mb * 1024
|
||
if zram_max_sigterm_kb > mem_total:
|
||
print('zram_max_sigterm value не должен быть больше MemTotal ({} MiB)\nExit'.format(round(mem_total / 1024)))
|
||
exit()
|
||
zram_max_sigterm_percent = zram_max_sigterm_kb / mem_total * 100
|
||
|
||
else:
|
||
print('Конфиг инвалид, для zram_max_sigterm неверно указаны единицы измерения\nExit')
|
||
exit()
|
||
|
||
else:
|
||
print('zram_max_sigterm not in config\nExit')
|
||
exit()
|
||
|
||
|
||
if 'zram_max_sigkill' in config_dict:
|
||
zram_max_sigkill = config_dict['zram_max_sigkill']
|
||
|
||
if zram_max_sigkill.endswith('%'):
|
||
# отбрасываем процент, получаем число
|
||
zram_max_sigkill_percent = zram_max_sigkill[:-1].strip()
|
||
# далее флоат тест
|
||
zram_max_sigkill_percent = string_to_float_convert_test(zram_max_sigkill_percent)
|
||
if zram_max_sigkill_percent is None:
|
||
print('Invalid zram_max_sigkill value, not float\nExit')
|
||
exit()
|
||
# окончательная валидация
|
||
if zram_max_sigkill_percent < 0 or zram_max_sigkill_percent > 100:
|
||
print('zram_max_sigkill, выраженный в процентах, должен быть быть в диапазоне [0; 100]\nExit')
|
||
exit()
|
||
|
||
# zram_max_sigkill_percent это теперь чистое валидное флоат число процентов, можно переводить в кб
|
||
zram_max_sigkill_kb = zram_max_sigkill_percent / 100 * mem_total
|
||
zram_max_sigkill_mb = round(zram_max_sigkill_kb / 1024)
|
||
|
||
elif zram_max_sigkill.endswith('M'):
|
||
zram_max_sigkill_mb = string_to_float_convert_test(zram_max_sigkill[:-1].strip())
|
||
if zram_max_sigkill_mb is None:
|
||
print('Invalid zram_max_sigkill value, not float\nExit')
|
||
exit()
|
||
zram_max_sigkill_kb = zram_max_sigkill_mb * 1024
|
||
if zram_max_sigkill_kb > mem_total:
|
||
print('zram_max_sigkill value не должен быть больше MemTotal ({} MiB)\nExit'.format(round(mem_total / 1024)))
|
||
exit()
|
||
zram_max_sigkill_percent = zram_max_sigkill_kb / mem_total * 100
|
||
|
||
else:
|
||
print('Конфиг инвалид, для zram_max_sigkill неверно указаны единицы измерения\nExit')
|
||
exit()
|
||
|
||
else:
|
||
print('zram_max_sigkill not in config\nExit')
|
||
exit()
|
||
|
||
|
||
if 'min_delay_after_sigterm' in config_dict:
|
||
min_delay_after_sigterm = string_to_float_convert_test(
|
||
config_dict['min_delay_after_sigterm'])
|
||
if min_delay_after_sigterm is None:
|
||
print('Invalid min_delay_after_sigterm value, not float\nExit')
|
||
exit()
|
||
if min_delay_after_sigterm < 0:
|
||
print('min_delay_after_sigterm должен быть неотрицательным\nExit')
|
||
exit()
|
||
else:
|
||
print('min_delay_after_sigterm not in config\nExit')
|
||
exit()
|
||
|
||
|
||
if 'min_delay_after_sigkill' in config_dict:
|
||
min_delay_after_sigkill = string_to_float_convert_test(
|
||
config_dict['min_delay_after_sigkill'])
|
||
if min_delay_after_sigkill is None:
|
||
print('Invalid min_delay_after_sigkill value, not float\nExit')
|
||
exit()
|
||
if min_delay_after_sigkill < 0:
|
||
print('min_delay_after_sigkill должен быть неотрицательным\nExit')
|
||
exit()
|
||
else:
|
||
print('min_delay_after_sigkill not in config\nExit')
|
||
exit()
|
||
|
||
|
||
if 'oom_score_min' in config_dict:
|
||
oom_score_min = string_to_int_convert_test(
|
||
config_dict['oom_score_min'])
|
||
if oom_score_min is None:
|
||
print('Invalid oom_score_min value, not integer\nExit')
|
||
exit()
|
||
if oom_score_min < 0 or oom_score_min > 1000:
|
||
print('Недопустимое значение oom_score_min\nExit')
|
||
exit()
|
||
else:
|
||
print('oom_score_min not in config\nExit')
|
||
exit()
|
||
|
||
|
||
decrease_oom_score_adj = conf_parse_bool('decrease_oom_score_adj')
|
||
|
||
|
||
if 'oom_score_adj_max' in config_dict:
|
||
oom_score_adj_max = string_to_int_convert_test(
|
||
config_dict['oom_score_adj_max'])
|
||
if oom_score_adj_max is None:
|
||
print('Invalid oom_score_adj_max value, not integer\nExit')
|
||
exit()
|
||
if oom_score_adj_max < 0 or oom_score_adj_max > 1000:
|
||
print('Недопустимое значение oom_score_adj_max\nExit')
|
||
exit()
|
||
else:
|
||
print('oom_score_adj_max not in config\nExit')
|
||
exit()
|
||
|
||
|
||
if 'desktop_notifications' in config_dict:
|
||
desktop_notifications = config_dict['desktop_notifications']
|
||
if desktop_notifications == 'True':
|
||
desktop_notifications = True
|
||
users_dict = dict()
|
||
with open('/etc/passwd') as f:
|
||
for line in f:
|
||
line_list = line.split(':')
|
||
username = line_list[0]
|
||
uid = line_list[2]
|
||
users_dict[uid] = username
|
||
elif desktop_notifications == 'False':
|
||
desktop_notifications = False
|
||
else:
|
||
print('Invalid desktop_notifications value {} (shoul' \
|
||
'd be True or False)\nExit'.format(
|
||
desktop_notifications))
|
||
exit()
|
||
else:
|
||
print('desktop_notifications not in config\nExit')
|
||
exit()
|
||
|
||
|
||
notify_options = conf_parse_string('notify_options')
|
||
|
||
|
||
root_display = conf_parse_string('root_display')
|
||
|
||
|
||
use_regex_lists = conf_parse_bool('use_regex_lists')
|
||
if use_regex_lists:
|
||
from re import fullmatch
|
||
|
||
|
||
whitelist_regex = conf_parse_string('whitelist_regex')
|
||
|
||
|
||
blacklist_regex = conf_parse_string('blacklist_regex')
|
||
|
||
|
||
preferlist_regex = conf_parse_string('preferlist_regex')
|
||
|
||
|
||
if 'preferlist_factor' in config_dict:
|
||
preferlist_factor = string_to_float_convert_test(config_dict['preferlist_factor'])
|
||
if preferlist_factor is None:
|
||
print('Invalid preferlist_factor value, not float\nExit')
|
||
exit()
|
||
if preferlist_factor < 1 and preferlist_factor > 1000:
|
||
print('preferlist_factor должен быть в диапазоне [1; 1000]\nExit')
|
||
exit()
|
||
else:
|
||
print('preferlist_factor not in config\nExit')
|
||
exit()
|
||
|
||
|
||
avoidlist_regex = conf_parse_string('avoidlist_regex')
|
||
|
||
|
||
if 'avoidlist_factor' in config_dict:
|
||
avoidlist_factor = string_to_float_convert_test(config_dict['avoidlist_factor'])
|
||
if avoidlist_factor is None:
|
||
print('Invalid avoidlist_factor value, not float\nExit')
|
||
exit()
|
||
if avoidlist_factor < 1 and avoidlist_factor > 1000:
|
||
print('avoidlist_factor должен быть в диапазоне [1; 1000]\nExit')
|
||
exit()
|
||
else:
|
||
print('avoidlist_factor not in config\nExit')
|
||
exit()
|
||
|
||
|
||
logging = conf_parse_bool('logging')
|
||
|
||
|
||
logfile = conf_parse_string('logfile')
|
||
|
||
|
||
low_memory_warnings = conf_parse_bool('low_memory_warnings')
|
||
|
||
|
||
if 'min_time_between_warnings' in config_dict:
|
||
min_time_between_warnings = string_to_float_convert_test(
|
||
config_dict['min_time_between_warnings'])
|
||
if min_time_between_warnings is None:
|
||
print('Invalid min_time_between_warnings value, not float\nExit')
|
||
exit()
|
||
if min_time_between_warnings < 1 or min_time_between_warnings > 300:
|
||
print('Недопустимое значение min_time_between_warnings, должно быть в диапазоне [1; 300]\nExit')
|
||
exit()
|
||
else:
|
||
print('min_time_between_warnings not in config\nExit')
|
||
exit()
|
||
|
||
|
||
if 'mem_min_warnings' in config_dict:
|
||
mem_min_warnings = config_dict['mem_min_warnings']
|
||
|
||
if mem_min_warnings.endswith('%'):
|
||
# отбрасываем процент, получаем число
|
||
mem_min_warnings_percent = mem_min_warnings[:-1].strip()
|
||
# далее флоат тест
|
||
mem_min_warnings_percent = string_to_float_convert_test(mem_min_warnings_percent)
|
||
if mem_min_warnings_percent is None:
|
||
print('Invalid mem_min_warnings value, not float\nExit')
|
||
exit()
|
||
# окончательная валидация
|
||
if mem_min_warnings_percent < 0 or mem_min_warnings_percent > 100:
|
||
print('mem_min_warnings, выраженный в процентах, должен быть быть в диапазоне [0; 100]\nExit')
|
||
exit()
|
||
|
||
# mem_min_warnings_percent это теперь чистое валидное флоат число процентов, можно переводить в кб
|
||
mem_min_warnings_kb = mem_min_warnings_percent / 100 * mem_total
|
||
mem_min_warnings_mb = round(mem_min_warnings_kb / 1024)
|
||
|
||
elif mem_min_warnings.endswith('M'):
|
||
mem_min_warnings_mb = string_to_float_convert_test(mem_min_warnings[:-1].strip())
|
||
if mem_min_warnings_mb is None:
|
||
print('Invalid mem_min_warnings value, not float\nExit')
|
||
exit()
|
||
mem_min_warnings_kb = mem_min_warnings_mb * 1024
|
||
if mem_min_warnings_kb > mem_total:
|
||
print('mem_min_warnings value не должен быть больше MemTotal ({} MiB)\nExit'.format(round(mem_total / 1024)))
|
||
exit()
|
||
mem_min_warnings_percent = mem_min_warnings_kb / mem_total * 100
|
||
|
||
else:
|
||
print('Конфиг инвалид, для mem_min_warnings неверно указаны единицы измерения\nExit')
|
||
exit()
|
||
|
||
else:
|
||
print('mem_min_warnings not in config\nExit')
|
||
exit()
|
||
|
||
|
||
|
||
# НА МЕСТЕ!!!
|
||
if 'swap_min_warnings' in config_dict:
|
||
swap_min_warnings = config_dict['swap_min_warnings']
|
||
else:
|
||
print('swap_min_warnings not in config\nExit')
|
||
exit()
|
||
|
||
|
||
|
||
|
||
|
||
if 'zram_max_warnings' in config_dict:
|
||
zram_max_warnings = config_dict['zram_max_warnings']
|
||
|
||
if zram_max_warnings.endswith('%'):
|
||
# отбрасываем процент, получаем число
|
||
zram_max_warnings_percent = zram_max_warnings[:-1].strip()
|
||
# далее флоат тест
|
||
zram_max_warnings_percent = string_to_float_convert_test(zram_max_warnings_percent)
|
||
if zram_max_warnings_percent is None:
|
||
print('Invalid zram_max_warnings value, not float\nExit')
|
||
exit()
|
||
# окончательная валидация
|
||
if zram_max_warnings_percent < 0 or zram_max_warnings_percent > 100:
|
||
print('zram_max_warnings, выраженный в процентах, должен быть быть в диапазоне [0; 100]\nExit')
|
||
exit()
|
||
|
||
# zram_max_warnings_percent это теперь чистое валидное флоат число процентов, можно переводить в кб
|
||
zram_max_warnings_kb = zram_max_warnings_percent / 100 * mem_total
|
||
zram_max_warnings_mb = round(zram_max_warnings_kb / 1024)
|
||
|
||
elif zram_max_warnings.endswith('M'):
|
||
zram_max_warnings_mb = string_to_float_convert_test(zram_max_warnings[:-1].strip())
|
||
if zram_max_warnings_mb is None:
|
||
print('Invalid zram_max_warnings value, not float\nExit')
|
||
exit()
|
||
zram_max_warnings_kb = zram_max_warnings_mb * 1024
|
||
if zram_max_warnings_kb > mem_total:
|
||
print('zram_max_warnings value не должен быть больше MemTotal ({} MiB)\nExit'.format(round(mem_total / 1024)))
|
||
exit()
|
||
zram_max_warnings_percent = zram_max_warnings_kb / mem_total * 100
|
||
|
||
else:
|
||
print('Конфиг инвалид, для zram_max_warnings неверно указаны единицы измерения\nExit')
|
||
exit()
|
||
|
||
else:
|
||
print('zram_max_warnings not in config\nExit')
|
||
exit()
|
||
|
||
|
||
execute_the_command = conf_parse_bool('execute_the_command')
|
||
|
||
|
||
##########################################################################
|
||
|
||
# получение уровней в кибибайтах
|
||
|
||
|
||
# возвращает число килобайт при задании в конфиге абсолютного значения,
|
||
# или кортеж с числом процентов
|
||
def sig_level_to_kb_swap(string):
|
||
|
||
if string.endswith('%'):
|
||
return float(string[:-1].strip()), True
|
||
elif string.endswith('M'):
|
||
return float(string[:-1].strip()) * 1024
|
||
else:
|
||
print('Конфиг инвалид, где-то неверно указаны единицы измерения\nExit')
|
||
exit()
|
||
|
||
|
||
# получаем число килобайт или кортеж с процентами
|
||
swap_min_sigterm_swap = sig_level_to_kb_swap(swap_min_sigterm)
|
||
swap_min_sigkill_swap = sig_level_to_kb_swap(swap_min_sigkill)
|
||
|
||
swap_min_warnings_swap = sig_level_to_kb_swap(swap_min_warnings)
|
||
|
||
|
||
if isinstance(swap_min_sigterm_swap, tuple):
|
||
swap_term_is_percent = True
|
||
swap_min_sigterm_percent = swap_min_sigterm_swap[0]
|
||
else:
|
||
swap_term_is_percent = False
|
||
swap_min_sigterm_kb = swap_min_sigterm_swap
|
||
|
||
if isinstance(swap_min_sigkill_swap, tuple):
|
||
swap_kill_is_percent = True
|
||
swap_min_sigkill_percent = swap_min_sigkill_swap[0]
|
||
else:
|
||
swap_kill_is_percent = False
|
||
swap_min_sigkill_kb = swap_min_sigkill_swap
|
||
|
||
|
||
if isinstance(swap_min_warnings_swap, tuple):
|
||
swap_warn_is_percent = True
|
||
swap_min_warnings_percent = swap_min_warnings_swap[0]
|
||
else:
|
||
swap_warn_is_percent = False
|
||
swap_min_warnings_kb = swap_min_warnings_swap
|
||
|
||
|
||
##########################################################################
|
||
|
||
# самозащита
|
||
|
||
# повышаем приоритет
|
||
try:
|
||
os.nice(self_nice)
|
||
self_nice_result = 'OK'
|
||
except PermissionError:
|
||
self_nice_result = 'Fail'
|
||
pass
|
||
|
||
# возможность запрета самоубийства
|
||
try:
|
||
with open('/proc/self/oom_score_adj', 'w') as file:
|
||
file.write('{}\n'.format(self_oom_score_adj))
|
||
self_oom_score_adj_result = 'OK'
|
||
except PermissionError:
|
||
pass
|
||
self_oom_score_adj_result = 'Fail'
|
||
except OSError:
|
||
self_oom_score_adj_result = 'Fail'
|
||
pass
|
||
|
||
# запрет своппинга процесса
|
||
if mlockall:
|
||
from ctypes import CDLL
|
||
result = CDLL('libc.so.6', use_errno=True).mlockall(3)
|
||
if result is 0:
|
||
mla_res = 'OK'
|
||
else:
|
||
mla_res = 'Fail'
|
||
else:
|
||
mla_res = ''
|
||
|
||
|
||
if os.geteuid() == 0:
|
||
root = True
|
||
decrease_res = 'OK'
|
||
else:
|
||
root = False
|
||
decrease_res = 'Impossible'
|
||
|
||
|
||
##########################################################################
|
||
|
||
# печать конфига
|
||
|
||
if print_config:
|
||
|
||
print('\nI. STANDARD OUTPUT VERBOSITY')
|
||
print('print_config: {}'.format(print_config))
|
||
print('print_mem_check_results: {}'.format(print_mem_check_results))
|
||
print('print_sleep_periods: {}'.format(print_sleep_periods))
|
||
|
||
print('\nII. SELF-DEFENSE')
|
||
print('mlockall: {} ({})'.format(mlockall, mla_res))
|
||
print('self_nice: {} ({})'.format(
|
||
self_nice, self_nice_result
|
||
))
|
||
print('self_oom_score_adj: {} ({})'.format(
|
||
self_oom_score_adj, self_oom_score_adj_result
|
||
))
|
||
|
||
print('\nIII. MONITORING INTENSITY')
|
||
print('rate_mem: {}'.format(rate_mem))
|
||
print('rate_swap: {}'.format(rate_swap))
|
||
print('rate_zram: {}'.format(rate_zram))
|
||
|
||
print('\nIV. THRESHOLDS FOR SENDING SIGNALS')
|
||
|
||
print('mem_min_sigterm: {} MiB, {} %'.format(
|
||
round(mem_min_sigterm_mb), round(mem_min_sigterm_percent, 1)))
|
||
print('mem_min_sigkill: {} MiB, {} %'.format(
|
||
round(mem_min_sigkill_mb), round(mem_min_sigkill_percent, 1)))
|
||
|
||
print('swap_min_sigterm: {}'.format(swap_min_sigterm))
|
||
print('swap_min_sigkill: {}'.format(swap_min_sigkill))
|
||
|
||
print('zram_max_sigterm: {} MiB, {} %'.format(
|
||
round(zram_max_sigterm_mb), round(zram_max_sigterm_percent, 1)))
|
||
print('zram_max_sigkill: {} MiB, {} %'.format(
|
||
round(zram_max_sigkill_mb), round(zram_max_sigkill_percent, 1)))
|
||
|
||
print('\nV. PREVENTION OF KILLING INNOCENT VICTIMS')
|
||
print('min_delay_after_sigterm: {}'.format(min_delay_after_sigterm))
|
||
print('min_delay_after_sigkill: {}'.format(min_delay_after_sigkill))
|
||
print('oom_score_min: {}'.format(oom_score_min))
|
||
|
||
# False (OK) - OK не нужен когда фолс
|
||
print('decrease_oom_score_adj: {} ({})'.format(
|
||
decrease_oom_score_adj, decrease_res
|
||
))
|
||
if decrease_oom_score_adj:
|
||
print('oom_score_adj_max: {}'.format(oom_score_adj_max))
|
||
|
||
print('\nVI. DESKTOP NOTIFICATIONS')
|
||
print('desktop_notifications: {}'.format(desktop_notifications))
|
||
if desktop_notifications:
|
||
print('notify_options: {}'.format(notify_options))
|
||
print('root_display: {}'.format(root_display))
|
||
|
||
print('\nVII. BLACK, WHITE, AVOID AND PREFER LISTS')
|
||
print('use_regex_lists: {}'.format(use_regex_lists))
|
||
if use_regex_lists:
|
||
print('whitelist_regex: {}'.format(whitelist_regex))
|
||
print('blacklist_regex: {}'.format(blacklist_regex))
|
||
print('preferlist_regex: {}'.format(preferlist_regex))
|
||
print('preferlist_factor: {}'.format(preferlist_factor))
|
||
print('avoidlist_regex: {}'.format(avoidlist_regex))
|
||
print('avoidlist_factor: {}'.format(avoidlist_factor))
|
||
|
||
print('\nVIII. LOGGING')
|
||
print('logging: {}'.format(logging))
|
||
if logging:
|
||
print('logfile: {}'.format(logfile))
|
||
|
||
print('\nIX. LOW MEMORY WARNINGS')
|
||
print('low_memory_warnings: {}'.format(low_memory_warnings))
|
||
if low_memory_warnings:
|
||
print('min_time_between_warnings: {}'.format(min_time_between_warnings))
|
||
|
||
print('mem_min_warnings: {} MiB, {} %'.format(
|
||
round(mem_min_warnings_mb), round(mem_min_warnings_percent, 1)))
|
||
|
||
print('swap_min_warnings: {}'.format(swap_min_warnings))
|
||
|
||
print('zram_max_warnings: {} MiB, {} %'.format(
|
||
round(zram_max_warnings_mb), round(zram_max_warnings_percent, 1)))
|
||
|
||
|
||
print('\nX. EXECUTE THE COMMAND INSTEAD OF SENDING THE SIGTERM SIGNAL')
|
||
print('execute_the_command: {}'.format(execute_the_command))
|
||
if execute_the_command:
|
||
print('\nPROCESS NAME COMMAND TO EXECUTE')
|
||
for key in etc_dict:
|
||
print('{} {}'.format(key.ljust(15), etc_dict[key]))
|
||
|
||
|
||
##########################################################################
|
||
|
||
|
||
# для рассчета ширины столбцов при печати mem и zram
|
||
mem_len = len(str(round(mem_total / 1024.0)))
|
||
|
||
rate_mem = rate_mem * 1048576
|
||
rate_swap = rate_swap * 1048576
|
||
rate_zram = rate_zram * 1048576
|
||
|
||
warn_time_now = 0
|
||
warn_time_delta = 1000
|
||
warn_timer = 0
|
||
|
||
print('\nStart monitoring...')
|
||
|
||
##########################################################################
|
||
|
||
# цикл проверки уровней доступной памяти
|
||
|
||
while True:
|
||
|
||
# находим mem_available, swap_total, swap_free
|
||
with open('/proc/meminfo') as f:
|
||
for n, line in enumerate(f):
|
||
if n is 2:
|
||
mem_available = int(line.split(':')[1].strip(' kB\n'))
|
||
continue
|
||
if n is swap_total_index:
|
||
swap_total = int(line.split(':')[1].strip(' kB\n'))
|
||
continue
|
||
if n is swap_free_index:
|
||
swap_free = int(line.split(':')[1].strip(' kB\n'))
|
||
break
|
||
|
||
|
||
# если swap_min_sigkill задан в процентах
|
||
if swap_kill_is_percent:
|
||
swap_min_sigkill_kb = swap_total * swap_min_sigkill_percent / 100.0
|
||
|
||
if swap_term_is_percent:
|
||
swap_min_sigterm_kb = swap_total * swap_min_sigterm_percent / 100.0
|
||
|
||
if swap_warn_is_percent:
|
||
swap_min_warnings_kb = swap_total * swap_min_warnings_percent / 100.0
|
||
|
||
|
||
# находим MemUsedZram
|
||
disksize_sum = 0
|
||
mem_used_total_sum = 0
|
||
for dev in os.listdir('/sys/block'):
|
||
if dev.startswith('zram'):
|
||
stat = zram_stat(dev)
|
||
disksize_sum += int(stat[0])
|
||
mem_used_total_sum += int(stat[1])
|
||
mem_used_zram = (
|
||
mem_used_total_sum + disksize_sum * zram_disksize_factor
|
||
) / 1024.0
|
||
|
||
|
||
if print_mem_check_results:
|
||
|
||
# для рассчета ширины столбца свопа
|
||
swap_len = len(str(round(swap_total / 1024.0)))
|
||
|
||
# печать размеров доступной памяти
|
||
if swap_total == 0 and mem_used_zram == 0:
|
||
print('MemAvail: {} M, {} %'.format(
|
||
human(mem_available, mem_len),
|
||
just_percent_mem(mem_available / mem_total)))
|
||
|
||
elif swap_total > 0 and mem_used_zram == 0:
|
||
print('MemAvail: {} M, {} % | SwapFree: {} M, {} %'.format(
|
||
human(mem_available, mem_len),
|
||
just_percent_mem(mem_available / mem_total),
|
||
human(swap_free, swap_len),
|
||
just_percent_swap(swap_free / (swap_total + 0.1))))
|
||
|
||
else:
|
||
print('MemAvail: {} M, {} % | SwapFree: {} M, {} % | Mem' \
|
||
'UsedZram: {} M, {} %'.format(
|
||
human(mem_available, mem_len),
|
||
just_percent_mem(mem_available / mem_total),
|
||
human(swap_free, swap_len),
|
||
just_percent_swap(swap_free / (swap_total + 0.1)),
|
||
human(mem_used_zram, mem_len),
|
||
just_percent_mem(mem_used_zram / mem_total)))
|
||
|
||
|
||
# если swap_min_sigkill задан в абсолютной величине и Swap_total = 0
|
||
if swap_total > swap_min_sigkill_kb:
|
||
swap_sigkill_pc = percent(swap_min_sigkill_kb / (swap_total + 0.1))
|
||
else:
|
||
swap_sigkill_pc = '-'
|
||
|
||
if swap_total > swap_min_sigterm_kb:
|
||
swap_sigterm_pc = percent(swap_min_sigterm_kb / (swap_total + 0.1))
|
||
else:
|
||
|
||
# СТОИТ ПЕЧАТАТЬ СВОП ТОЛЬКО ПРИ SwapTotal > 0
|
||
swap_sigterm_pc = '-'
|
||
|
||
|
||
# проверка превышения порогов
|
||
# порог превышен - пытаемся предотвратить OOM
|
||
# пороги не превышены - спим
|
||
|
||
# MEM SWAP KILL
|
||
if mem_available <= mem_min_sigkill_kb and swap_free <= swap_min_sigkill_kb:
|
||
|
||
mem_info = ' MemAvailable ({} MiB, {} %) < mem_min_sigkill ({} MiB, {} %)\n Swa' \
|
||
'pFree ({} MiB, {} %) < swap_min_sigkill ({} MiB, {} %)'.format(
|
||
kib_to_mib(mem_available),
|
||
percent(mem_available / mem_total),
|
||
|
||
kib_to_mib(mem_min_sigkill_kb),
|
||
percent(mem_min_sigkill_kb / mem_total),
|
||
|
||
kib_to_mib(swap_free),
|
||
percent(swap_free / (swap_total + 0.1)),
|
||
|
||
kib_to_mib(swap_min_sigkill_kb),
|
||
swap_sigkill_pc)
|
||
|
||
find_victim_and_send_signal(9)
|
||
|
||
# ZRAM KILL
|
||
elif mem_used_zram >= zram_max_sigkill_kb:
|
||
|
||
mem_info = ' MemUsedZram ({} MiB, {} %) > zram_max_sigkill ({} MiB, {} %)'.format(
|
||
kib_to_mib(mem_used_zram),
|
||
percent(mem_used_zram / mem_total),
|
||
kib_to_mib(zram_max_sigkill_kb),
|
||
percent(zram_max_sigkill_kb / mem_total))
|
||
|
||
find_victim_and_send_signal(9)
|
||
|
||
# MEM SWAP TERM
|
||
elif mem_available <= mem_min_sigterm_kb and swap_free <= swap_min_sigterm_kb:
|
||
|
||
mem_info = ' MemAvailable ({} MiB, {} %) < mem_min_sigterm ({} MiB, {} %)\n Sw' \
|
||
'apFree ({} MiB, {} %) < swap_min_sigterm ({} MiB, {} %)'.format(
|
||
kib_to_mib(mem_available),
|
||
percent(mem_available / mem_total),
|
||
|
||
|
||
kib_to_mib(mem_min_sigterm_kb),
|
||
#percent(mem_min_sigterm_kb / mem_total),
|
||
|
||
# ОКРУГЛЯТЬ НА МЕСТЕ ВЫШЕ
|
||
round(mem_min_sigterm_percent, 1),
|
||
|
||
kib_to_mib(swap_free),
|
||
percent(swap_free / (swap_total + 0.1)),
|
||
|
||
|
||
kib_to_mib(swap_min_sigterm_kb),
|
||
swap_sigterm_pc)
|
||
|
||
find_victim_and_send_signal(15)
|
||
|
||
# ZRAM TERM
|
||
elif mem_used_zram >= zram_max_sigterm_kb:
|
||
|
||
mem_info = ' MemUsedZram ({} MiB, {} %) > zram_max_sigter' \
|
||
'm ({} M, {} %)'.format(
|
||
kib_to_mib(mem_used_zram),
|
||
percent(mem_used_zram / mem_total),
|
||
kib_to_mib(zram_max_sigterm_kb),
|
||
percent(zram_max_sigterm_kb / mem_total))
|
||
|
||
find_victim_and_send_signal(15)
|
||
|
||
# LOW MEMORY WARNINGS
|
||
elif low_memory_warnings and desktop_notifications:
|
||
|
||
if mem_available < mem_min_warnings_kb and swap_free < swap_min_warnings_kb + 0.1 or mem_used_zram > zram_max_warnings_kb:
|
||
warn_time_delta = time.time() - warn_time_now
|
||
warn_time_now = time.time()
|
||
warn_timer += warn_time_delta
|
||
if warn_timer > min_time_between_warnings:
|
||
send_notify_warn()
|
||
warn_timer = 0
|
||
sleep_after_check_mem()
|
||
|
||
# SLEEP BETWEEN MEM CHECKS
|
||
else:
|
||
sleep_after_check_mem()
|
||
|