217 lines
5.3 KiB
Python
Executable File
217 lines
5.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# print('Starting nohang_notify_helper')
|
|
|
|
|
|
def write(path, string):
|
|
"""
|
|
"""
|
|
with open(path, 'w') as f:
|
|
f.write(string)
|
|
|
|
|
|
def rline1(path):
|
|
"""read 1st line from path."""
|
|
try:
|
|
with open(path) as f:
|
|
for line in f:
|
|
return line
|
|
except OSError:
|
|
exit(1)
|
|
|
|
|
|
def rfile(path):
|
|
"""read file."""
|
|
with open(path) as f:
|
|
return f.read()
|
|
|
|
|
|
def re_pid_environ(pid):
|
|
"""
|
|
read environ of 1 process
|
|
returns tuple with USER, DBUS, DISPLAY like follow:
|
|
('user', 'DISPLAY=:0',
|
|
'DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus')
|
|
returns None if these vars is not in /proc/[pid]/environ
|
|
"""
|
|
try:
|
|
env = str(rline1('/proc/' + pid + '/environ'))
|
|
if display_env in env and dbus_env in env and user_env in env:
|
|
env_list = env.split('\x00')
|
|
|
|
# iterating over a list of process environment variables
|
|
for i in env_list:
|
|
if i.startswith(user_env):
|
|
user = i
|
|
if user == 'root':
|
|
return None
|
|
continue
|
|
|
|
if i.startswith(display_env):
|
|
display = i[:10]
|
|
continue
|
|
|
|
if i.startswith(dbus_env):
|
|
dbus = i
|
|
continue
|
|
|
|
if i.startswith('HOME='):
|
|
# exclude Display Manager's user
|
|
if i.startswith('HOME=/var'):
|
|
return None
|
|
|
|
try:
|
|
env = user.partition('USER=')[2], display, dbus
|
|
except UnboundLocalError:
|
|
# print('notify helper: UnboundLocalError')
|
|
return None
|
|
|
|
return env
|
|
|
|
except FileNotFoundError:
|
|
# print('notify helper: FileNotFoundError')
|
|
return None
|
|
except ProcessLookupError:
|
|
# print('notify helper: ProcessLookupError')
|
|
return None
|
|
|
|
|
|
def root_notify_env():
|
|
"""return set(user, display, dbus)"""
|
|
unsorted_envs_list = []
|
|
# iterates over processes, find processes with suitable env
|
|
for pid in listdir('/proc'):
|
|
|
|
if path.exists('/proc/' + pid + '/exe') is True:
|
|
one_env = re_pid_environ(pid)
|
|
unsorted_envs_list.append(one_env)
|
|
|
|
env = set(unsorted_envs_list)
|
|
env.discard(None)
|
|
|
|
# deduplicate dbus
|
|
new_env = []
|
|
end = []
|
|
for i in env:
|
|
key = i[0] + i[1]
|
|
if key not in end:
|
|
end.append(key)
|
|
new_env.append(i)
|
|
else:
|
|
continue
|
|
|
|
return new_env
|
|
|
|
|
|
try:
|
|
write('/proc/self/oom_score_adj', '0')
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
try:
|
|
from os import listdir, path, remove
|
|
from subprocess import Popen, TimeoutExpired
|
|
from sys import argv
|
|
except OSError:
|
|
exit(1)
|
|
|
|
|
|
if len(argv) != 5:
|
|
print('nohang_notify_helper: invalid input')
|
|
exit()
|
|
|
|
|
|
with open('/proc/meminfo') as f:
|
|
for line in f:
|
|
if line.startswith('SwapTotal'):
|
|
swap_total = int(line.split(':')[1][:-4])
|
|
if swap_total > 0:
|
|
wait_time = 10
|
|
else:
|
|
wait_time = 2
|
|
|
|
print('nohang_notify_helper: wait_time:', wait_time)
|
|
|
|
split_by = '#' * 16
|
|
|
|
|
|
uid = argv[2]
|
|
|
|
timestamp = argv[4]
|
|
|
|
|
|
path_to_cache = '/dev/shm/nohang_notify_cache_uid{}_time{}'.format(
|
|
uid, timestamp
|
|
)
|
|
|
|
|
|
try:
|
|
title, body = rfile(path_to_cache).split(split_by)
|
|
except FileNotFoundError:
|
|
print('nohang_notify_helper: FileNotFoundError')
|
|
exit(1)
|
|
|
|
remove(path_to_cache)
|
|
|
|
if uid != '0':
|
|
cmd = ['notify-send', '--icon=dialog-warning', title, body]
|
|
print('nohang_notify_helper: run cmd:', cmd)
|
|
with Popen(cmd) as proc:
|
|
try:
|
|
proc.wait(timeout=wait_time)
|
|
except TimeoutExpired:
|
|
proc.kill()
|
|
print('nohang_notify_helper: TimeoutExpired')
|
|
exit()
|
|
|
|
display_env = 'DISPLAY='
|
|
dbus_env = 'DBUS_SESSION_BUS_ADDRESS='
|
|
user_env = 'USER='
|
|
|
|
list_with_envs = root_notify_env()
|
|
list_len = len(list_with_envs)
|
|
|
|
# if somebody logged in with GUI
|
|
if list_len > 0:
|
|
|
|
for i in list_with_envs:
|
|
print('Send GUI notification:', title, body, i)
|
|
|
|
# iterating over logged-in users
|
|
for i in list_with_envs:
|
|
username, display_env, dbus_env = i[0], i[1], i[2]
|
|
display_tuple = display_env.partition('=')
|
|
dbus_tuple = dbus_env.partition('=')
|
|
display_value = display_tuple[2]
|
|
dbus_value = dbus_tuple[2]
|
|
|
|
try:
|
|
with Popen([
|
|
'sudo', '-u', username,
|
|
'env',
|
|
'DISPLAY=' + display_value,
|
|
'DBUS_SESSION_BUS_ADDRESS=' + dbus_value,
|
|
'notify-send',
|
|
'--icon=dialog-warning',
|
|
title,
|
|
body
|
|
]) as proc:
|
|
try:
|
|
proc.wait(timeout=wait_time)
|
|
except TimeoutExpired:
|
|
proc.kill()
|
|
print('TimeoutExpired: notify user: ' + username)
|
|
except BlockingIOError:
|
|
print('nohang_notify_helper: BlockingIOError')
|
|
except OSError:
|
|
print('nohang_notify_helper: OSError')
|
|
except Exception:
|
|
print('nohang_notify_helper: CANNOT SPAWN NOTIFY-SEND PROCESS')
|
|
else:
|
|
print(
|
|
'Not send GUI notification: [',
|
|
title,
|
|
body,
|
|
']. Nobody logged-in with GUI. Nothing to do.')
|