diff --git a/README.md b/README.md
index 70b82b5..7b42636 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ https://2ch.hk/s/res/2310304.html#2311483, https://archive.li/idixk
### Некоторые особенности
- задача - препятствовать зависанию системы при нехватке доступной памяти, а также корректное завершение процессов с целью увеличения объема доступной памяти
-- демон на Python 3, VmRSS не более 13 MiB
+- демон на Python 3, VmRSS от 10 до 14 MiB в зависимости от настроек
- требуется ядро `Linux 3.14` или новее
- периодически проверяет объем доступной памяти, при дефиците памяти отправляет `SIGKILL` или `SIGTERM` процессу с наибольшим `oom_score`
- поддержка работы со `zram`, возможность реакции на `mem_used_total`
@@ -50,6 +50,7 @@ https://2ch.hk/s/res/2310304.html#2311483, https://archive.li/idixk
- возможность задания `oom_score_min` для предотвращения убийства невиновных
- verbosity: опциональность печати параметров конфига при старте программы, опциональность печати результатов проверки памяти и времени между проверками памяти
- возможность предотвращения избыточного убийства процессов с помощью задания миниального `oom_score` для убиваемых процессов и установка минимальной задержки просле отправки сигналов (параметры конфига `min_delay_after_sigkill` и `min_delay_after_sigterm`)
+- возможность показа десктопных уведомлений
- наличие `man` страницы
- наличие установщика для пользователей `systemd`
- протестировано на `Debian 9 x86_64`, `Debian 8 i386`, `Fedora 28 x86_64`
diff --git a/nohang b/nohang
index b0ecc4a..ca5bad7 100755
--- a/nohang
+++ b/nohang
@@ -1,7 +1,6 @@
#!/usr/bin/env python3
-# Nohang - No Hang Daemon
-
+# Nohang - The No Hang Daemon for Linux
################################################################################
@@ -17,6 +16,10 @@ from argparse import ArgumentParser
### задание констант
+version = 'unknown'
+
+sig_dict = {9:'SIGKILL', 15:'SIGTERM'}
+
# директория, в которой запущен скрипт
cd = os.getcwd()
@@ -174,14 +177,39 @@ def find_victim_and_send_signal(signal):
name = pid_to_name(pid)
print(
- ' Try to send signal {} to {}, Pid {}, oom_score {}'.format(
- signal, name, pid, oom_score
+ ' Try to send the {} signal to {}, Pid {}, oom_score {}'.format(
+ sig_dict[signal], name, pid, oom_score
)
)
try:
os.kill(int(pid), signal)
print(' Success')
+
+ if desktop_notifications:
+
+ info = '"The {} signal has been sent to {},\nPid: {}, oom_score: {}" &'.format(sig_dict[signal], name, pid, oom_score)
+
+ if root:
+ # получаем множество залогиненных юзеров из вывода команды users
+ users_set = set(str(
+ Popen('users', shell=True, stdout=PIPE).communicate()[0][0:-1]
+ )[2:-1].split(' '))
+ # отправляем уведомление всем залогиненным юзерам
+ for i in users_set:
+ root_notify_command = 'DISPLAY=:0.0 sudo -u {} {} -i dialog-warnig "Nohang tried to prevent OOM" -t {} -u critical '.format(
+ i, notify_command, notify_time
+ )
+ os.system(root_notify_command + info)
+ else:
+ user_notify_command =(
+ '{} -i dialog-warnig "Nohang tried to prevent OOM" -t {} -u critical '.format(
+ notify_command, notify_time
+ )
+ )
+ os.system(user_notify_command + info)
+
+
except ProcessLookupError:
print(' No such process')
except PermissionError:
@@ -326,6 +354,7 @@ else:
exit()
+
if 'print_mem_check_results' in config_dict:
print_mem_check_results = config_dict['print_mem_check_results']
if print_mem_check_results == 'True':
@@ -345,6 +374,7 @@ else:
exit()
+
if 'print_sleep_periods' in config_dict:
print_sleep_periods = config_dict['print_sleep_periods']
if print_sleep_periods == 'True':
@@ -400,11 +430,11 @@ else:
if 'self_oom_score_adj' in config_dict:
self_oom_score_adj = config_dict['self_oom_score_adj']
- if string_to_int_convert_test(self_oom_score_adj) is None:
+ self_oom_score_adj = string_to_int_convert_test(self_oom_score_adj)
+ if self_oom_score_adj is None:
print('Invalid self_oom_score_adj value, not integer\nExit')
exit()
else:
- self_oom_score_adj = int(self_oom_score_adj)
if self_oom_score_adj < -1000 or self_oom_score_adj > 1000:
print('Недопустимое значение self_oom_score_adj\nExit')
exit()
@@ -593,6 +623,49 @@ else:
exit()
+
+if 'desktop_notifications' in config_dict:
+ desktop_notifications = config_dict['desktop_notifications']
+ if desktop_notifications == 'True':
+ desktop_notifications = True
+ from subprocess import Popen, PIPE
+ elif desktop_notifications == 'False':
+ desktop_notifications = False
+ else:
+ print(
+ 'Invalid desktop_notifications value {} (should be True or False)\nExit'.format(
+ desktop_notifications
+ )
+ )
+ exit()
+else:
+ print('desktop_notifications not in config\nExit')
+ exit()
+
+
+if 'notify_command' in config_dict:
+ notify_command = config_dict['notify_command'].strip()
+else:
+ print('notify_command not in config\nExit')
+ exit()
+
+
+if 'notify_time' in config_dict:
+ notify_time = config_dict['notify_time']
+ notify_time = string_to_float_convert_test(notify_time)
+ if notify_time is None:
+ print('Invalid notify_time value, not float\nExit')
+ exit()
+ if notify_time <= 0:
+ print('Недопустимое значение notify_time\nExit')
+ exit()
+ else:
+ notify_time = int(notify_time * 1000)
+else:
+ print('notify_time not in config\nExit')
+ exit()
+
+
################################################################################
### получение уровней в кибибайтах
@@ -732,7 +805,7 @@ if print_config:
print('zram_max_sigterm: {}'.format(zram_max_sigterm))
print('zram_max_sigkill: {}'.format(zram_max_sigkill))
- print('\nV. ПРЕДОТВРАЩЕНИЕ УБИЙСТВ НЕВИНОВНЫХ')
+ print('\nV. THE 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))
@@ -745,6 +818,11 @@ if print_config:
print('oom_score_adj_before: {}'.format(oom_score_adj_before))
print('oom_score_adj_after: {}'.format(oom_score_adj_after))
+ print('\nVI. DESKTOP NOTIFICATIONS')
+ print('desktop_notifications: {}'.format(desktop_notifications))
+ if desktop_notifications:
+ print('notify_command: {}'.format(notify_command))
+ print('notify_time: {}'.format(round(notify_time / 1000, 1)))
# для рассчета ширины столбцов при печати mem и zram
@@ -757,7 +835,6 @@ print('\nStart monitoring...')
### цикл проверки уровней доступной памяти
-
while True:
#decrease_oom_score_adj(oom_score_adj_before, oom_score_adj_after)
diff --git a/nohang.conf b/nohang.conf
index 7c7a7d0..f1aa80d 100644
--- a/nohang.conf
+++ b/nohang.conf
@@ -88,11 +88,11 @@ rate_zram = 1
mem_min_sigterm = 0.5 G
swap_min_sigkill = 200 M
-mem_min_sigterm = 8%
-mem_min_sigkill = 4%
+mem_min_sigterm = 8 %
+mem_min_sigkill = 4 %
-swap_min_sigterm = 8%
-swap_min_sigkill = 4%
+swap_min_sigterm = 8 %
+swap_min_sigkill = 4 %
Задание общей доли zram в памяти, при превышении которой
происходит отправка соответствующих сигналов.
@@ -107,7 +107,7 @@ zram_max_sigkill = 60 %
#####################################################################
- V. ПРЕДОТВРАЩЕНИЕ УБИЙСТВ НЕВИНОВНЫХ
+ V. THE PREVENTION OF KILLING INNOCENT VICTIMS
Минимальное значение oom_score, которым должен обладать
процесс для того, чтобы ему был отправлен сигнал.
@@ -122,7 +122,7 @@ oom_score_min = 15
для предотвращения риска убийства сразу множества процессов.
Должно быть неотрицательным числом.
-min_delay_after_sigterm = 0.1
+min_delay_after_sigterm = 0.2
min_delay_after_sigkill = 3
Процессы браузера chromium обычно имеют oom_score_adj
@@ -145,3 +145,24 @@ decrease_oom_score_adj = False
oom_score_adj_before = 10
oom_score_adj_after = 5
+#####################################################################
+
+ VI. DESKTOP NOTIFICATIONS
+
+ Эта возможность требует наличия notify-send в системе.
+ В Debian/Ubuntu это обеспечивается установкой пакета
+ libnotify-bin. Также требуется наличие notification-daemon.
+ Допустимые значения: True и False
+
+desktop_notifications = False
+
+ Может использоваться другая команда, если ее синтаксис совместим
+ с синтаксисом notify-send
+
+notify_command = notify-send
+
+ Время показа уведомлений в секундах.
+ Должно быть положительным числом.
+
+notify_time = 15
+