diff --git a/nohang/nohang b/nohang/nohang index d69d333..892363a 100755 --- a/nohang/nohang +++ b/nohang/nohang @@ -11,6 +11,7 @@ from sre_constants import error as invalid_re from signal import signal, SIGKILL, SIGTERM, SIGINT, SIGQUIT, SIGHUP + ############################################################################### # define functions @@ -19,25 +20,27 @@ from signal import signal, SIGKILL, SIGTERM, SIGINT, SIGQUIT, SIGHUP def exe(cmd): """ execute cmd in subprocess.Popen() """ + cmd_list = shlex.split(cmd) cmd_num_dict['cmd_num'] += 1 cmd_num = cmd_num_dict['cmd_num'] - log('Execute the command({}) in {}: {}'.format( + + log('Execute the command ({}) in {}: {}'.format( cmd_num, threading.current_thread().getName(), - cmd)) + cmd_list)) t3 = monotonic() - with Popen(cmd, shell=True) as proc: + with Popen(cmd_list) as proc: try: proc.wait(timeout=exe_timeout) exit_status = proc.poll() t4 = monotonic() - log('Command({}) execution completed in {} sec; exit status' \ + log('Command ({}) execution completed in {} sec; exit status' \ ': {}'.format(cmd_num, round(t4 - t3, 3), exit_status)) except TimeoutExpired: proc.kill() t4 = monotonic() - log('TimeoutExpired for the command({}) in {} sec'.format( + log('TimeoutExpired for the command ({}) in {} sec'.format( cmd_num, round(t4 - t3, 3))) @@ -61,9 +64,9 @@ def start_thread(func, *a, **k): t2 = monotonic() if debug_threading: - log('{} has started in {} ms, {} threads currently alive'.format( - th_name, round((t2 - t1) * 1000, 1), threading.active_count() - )) + log('{} has started in {} ms, {} threads are ' \ + 'currently alive'.format(th_name, round(( + t2 - t1) * 1000, 1), threading.active_count())) except RuntimeError: @@ -153,6 +156,17 @@ def pop(cmd, username): """ run cmd in subprocess.Popen() """ + + cmd_num_dict['cmd_num'] += 1 + cmd_num = cmd_num_dict['cmd_num'] + + log('Execute the Command-{} {} in {}'.format( + cmd_num, + cmd, + threading.current_thread().getName() + )) + + if swap_total == 0: wait_time = 2 else: @@ -164,18 +178,15 @@ def pop(cmd, username): try: proc.wait(timeout=wait_time) err = proc.poll() + t4 = monotonic() except TimeoutExpired: proc.kill() + t4 = monotonic() + if debug_gui_notifications: log('TimeoutExpired: notify user: {}'.format(username)) - t4 = monotonic() - - err = 0 - if debug_gui_notifications: - pass - #log('Popen time: {} sec; exit status: {}; cmd: {}'.format(round(t4 - t3, 3), err, cmd)) log('Popen time: {} sec; exit status: {}; cmd: {}'.format(round(t4 - t3, 3), err, cmd)) @@ -279,15 +290,18 @@ def send_notify(threshold, name, pid): """ title = 'Freeze prevention' - body = '{} [{}] {}'.format( - notify_sig_dict[threshold], - pid, - name.replace( - # symbol '&' can break notifications in some themes, - # therefore it is replaced by '*' - '&', '*' - ) - ) + + if hide_corrective_action_type: + body = 'Corrective action applied' + else: + body = '{} [{}] {}'.format( + notify_sig_dict[threshold], + pid, + name.replace( + # symbol '&' can break notifications in some themes, + # therefore it is replaced by '*' + '&', '*' + )) start_thread(send_notification, title, body) @@ -301,9 +315,12 @@ def send_notify_etc(pid, name, command): pid: str process pid """ title = 'Freeze prevention' - body = 'Victim is [{}] {}\nExecute the co' \ - 'mmand:\n{}'.format( - pid, name.replace('&', '*'), command.replace('&', '*')) + if hide_corrective_action_type: + body = 'Corrective action applied' + else: + body = 'Victim is [{}] {}\nExecute the command:\n' \ + '{}'.format(pid, name.replace( + '&', '*'), command.replace('&', '*')) start_thread(send_notification, title, body) @@ -3016,9 +3033,24 @@ print_victim_cmdline = conf_parse_bool('print_victim_cmdline') print_config_at_startup = conf_parse_bool('print_config_at_startup') print_mem_check_results = conf_parse_bool('print_mem_check_results') debug_sleep = conf_parse_bool('debug_sleep') + +hide_corrective_action_type = conf_parse_bool('hide_corrective_action_type') + + + + low_memory_warnings_enabled = conf_parse_bool('low_memory_warnings_enabled') + + + + + + + + + post_action_gui_notifications = conf_parse_bool( 'post_action_gui_notifications') @@ -3369,33 +3401,35 @@ if separate_log: import logging log_dir = '/var/log/nohang' - - try: - os.mkdir(log_dir) - except PermissionError: - print('ERROR: can not create log dir') - except FileExistsError: - pass - logfile = log_dir + '/nohang.log' try: - with open(logfile, 'a') as f: - pass - except FileNotFoundError: - print('ERROR: log FileNotFoundError') + os.mkdir(log_dir) + except FileExistsError: + pass except PermissionError: - print('ERROR: log PermissionError') + errprint('ERROR: cannot create {}'.format(log_dir)) + + try: + os.chmod(log_dir, mode=0o750) + except FileNotFoundError: + errprint('ERROR: file not found: {}'.format(log_dir)) + except PermissionError: + errprint('ERROR: permission denied: {}'.format(log_dir)) try: logging.basicConfig( filename=logfile, level=logging.INFO, format="%(asctime)s: %(message)s") - except PermissionError: - errprint('ERROR: Permission denied: {}'.format(logfile)) except FileNotFoundError: - errprint('ERROR: FileNotFoundError: {}'.format(logfile)) + errprint('ERROR: file not found: {}'.format(logfile)) + except PermissionError: + errprint('ERROR: permission denied: {}'.format(logfile)) + + + + if 'min_mem_report_interval' in config_dict: @@ -3511,11 +3545,13 @@ if (low_memory_warnings_enabled or \ post_kill_exe != ''): import threading + import shlex from subprocess import Popen, TimeoutExpired + psi_support = os.path.exists(psi_path) diff --git a/nohang/nohang-desktop.conf b/nohang/nohang-desktop.conf index de31d99..ea08288 100644 --- a/nohang/nohang-desktop.conf +++ b/nohang/nohang-desktop.conf @@ -112,6 +112,8 @@ over_sleep = 0.05 4. Warnings and notifications + 4.1. GUI notifications after corrective actions + Description: Type: boolean Valid values: True and False @@ -122,6 +124,14 @@ post_action_gui_notifications = True Type: boolean Valid values: True and False +hide_corrective_action_type = True + + 4.2. Low memory warnings + + Description: + Type: boolean + Valid values: True and False + low_memory_warnings_enabled = True Description: diff --git a/nohang/nohang.conf b/nohang/nohang.conf index 10ba4a1..141257d 100644 --- a/nohang/nohang.conf +++ b/nohang/nohang.conf @@ -112,6 +112,8 @@ over_sleep = 0.05 4. Warnings and notifications + 4.1. GUI notifications after corrective actions + Description: Type: boolean Valid values: True and False @@ -122,6 +124,14 @@ post_action_gui_notifications = False Type: boolean Valid values: True and False +hide_corrective_action_type = True + + 4.2. Low memory warnings + + Description: + Type: boolean + Valid values: True and False + low_memory_warnings_enabled = False Description: diff --git a/nohang/nohang.service.in b/nohang/nohang.service.in index c7bb616..d7d34d7 100644 --- a/nohang/nohang.service.in +++ b/nohang/nohang.service.in @@ -8,18 +8,20 @@ ExecStart=:TARGET_BIN:/nohang --config :TARGET_CONF:/nohang/nohang.conf Restart=always RestartSec=0 KillMode=mixed +TasksMax=100 Nice=-15 CPUSchedulingResetOnFork=true OOMScoreAdjust=-10 +UMask=0027 +PrivateTmp=true RestrictRealtime=yes MemoryDenyWriteExecute=yes ProtectKernelModules=true SystemCallArchitectures=native ReadOnlyPaths=/ ReadWritePaths=/tmp /var /run /dev/shm -PrivateTmp=true -CapabilityBoundingSet=CAP_KILL CAP_AUDIT_WRITE CAP_DAC_READ_SEARCH CAP_IPC_LOCK CAP_SETGID CAP_SETUID CAP_SYS_PTRACE CAP_CHOWN -AmbientCapabilities=CAP_KILL CAP_AUDIT_WRITE CAP_DAC_READ_SEARCH CAP_IPC_LOCK CAP_SETGID CAP_SETUID CAP_SYS_PTRACE +CapabilityBoundingSet=CAP_KILL CAP_IPC_LOCK CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_AUDIT_WRITE CAP_SETUID CAP_SETGID +AmbientCapabilities=CAP_KILL CAP_IPC_LOCK CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_AUDIT_WRITE CAP_SETUID CAP_SETGID [Install] WantedBy=multi-user.target diff --git a/nohang/test.conf b/nohang/test.conf index 4afe728..d7a7ae2 100644 --- a/nohang/test.conf +++ b/nohang/test.conf @@ -112,6 +112,8 @@ over_sleep = 0.05 4. Warnings and notifications + 4.1. GUI notifications after corrective actions + Description: Type: boolean Valid values: True and False @@ -122,6 +124,14 @@ post_action_gui_notifications = True Type: boolean Valid values: True and False +hide_corrective_action_type = False + + 4.2. Low memory warnings + + Description: + Type: boolean + Valid values: True and False + low_memory_warnings_enabled = True Description: