687 lines
21 KiB
Python
Executable File
687 lines
21 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import socket
|
|
import optparse
|
|
import os
|
|
import json
|
|
import traceback
|
|
import time
|
|
import sys
|
|
import io
|
|
import subprocess
|
|
import logging
|
|
|
|
msg = None
|
|
|
|
def player_metadata():
|
|
for k in range(20):
|
|
try:
|
|
time.sleep(1.0)
|
|
return subprocess.check_output(['playerctl', 'metadata']).decode('utf-8').strip()
|
|
except:
|
|
continue
|
|
|
|
def memory_stats():
|
|
with io.BytesIO(subprocess.check_output('free', shell=True)) as f:
|
|
t1 = f.read().decode('utf-8').splitlines()
|
|
mem_total = int(t1[1].strip().split()[1])
|
|
mem_used = int(t1[1].strip().split()[2])
|
|
|
|
return dict(
|
|
mem_total=mem_total,
|
|
mem_used=mem_used,
|
|
)
|
|
|
|
def eternal_oom(memory_limit=None):
|
|
import signal
|
|
import re
|
|
import time
|
|
import pprint
|
|
self_pid = os.getpid()
|
|
if memory_limit is None:
|
|
memory_limit = 3 * 1024 * 1024
|
|
assert isinstance(memory_limit, int) \
|
|
and memory_limit < memory_stats()['mem_total'] * 0.8 \
|
|
and memory_limit > 512 * 1024
|
|
|
|
def pandas_data_frame(lines, groups_regex, header_regex, extra_columns):
|
|
header = re.compile(header_regex).search(lines[0]).groups()
|
|
rows = [
|
|
re.compile(groups_regex).search(row).groups()
|
|
for row in lines[1:]
|
|
]
|
|
columns = {
|
|
column: []
|
|
for column in header
|
|
}
|
|
for row in rows:
|
|
for value, column in zip(row, header):
|
|
columns[column].append(value)
|
|
for column, transformation in extra_columns.items():
|
|
columns[column] = [
|
|
transformation(
|
|
{
|
|
k : v[index]
|
|
for k, v in columns.items()
|
|
}
|
|
)
|
|
for index in range(len(rows))
|
|
]
|
|
|
|
return columns
|
|
|
|
def pandas_merge(left, right, on):
|
|
index = {}
|
|
input_data_frames = [
|
|
('left', left),
|
|
('right', right),
|
|
]
|
|
for index_name, data_frame in input_data_frames:
|
|
current_index = {}
|
|
for row_index, value in enumerate(data_frame[on]):
|
|
if not value in current_index:
|
|
current_index[value] = []
|
|
current_index[value].append(row_index)
|
|
|
|
index[index_name] = current_index
|
|
|
|
merged_data_frame = dict(
|
|
header=[
|
|
column + '_x'
|
|
for column in left
|
|
] + [
|
|
column + '_y'
|
|
for column in right
|
|
],
|
|
columns={},
|
|
)
|
|
|
|
for column in merged_data_frame['header']:
|
|
merged_data_frame['columns'][column] = []
|
|
|
|
common_values = {
|
|
left_value
|
|
for left_value in index['left']
|
|
if left_value in index['right']
|
|
}
|
|
common_rows = sorted(
|
|
[
|
|
dict(
|
|
left_row_index=index['left'][value][0],
|
|
right_row_index=index['right'][value][0],
|
|
)
|
|
for value in common_values
|
|
],
|
|
key=lambda x: x['left_row_index'],
|
|
)
|
|
for common_row in common_rows:
|
|
row = sum([
|
|
[
|
|
values[
|
|
common_row['%s_row_index' % index_name]
|
|
]
|
|
for column, values in data_frame.items()
|
|
]
|
|
for index_name, data_frame in input_data_frames
|
|
], [])
|
|
for column, value in zip(merged_data_frame['header'], row):
|
|
merged_data_frame['columns'][column].append(value)
|
|
|
|
return merged_data_frame['columns']
|
|
|
|
def pandas_sort_values(data_frame, by, ascending):
|
|
assert len(by) == 1
|
|
assert ascending is False
|
|
t1 = [
|
|
o['row_index']
|
|
for o in sorted(
|
|
[
|
|
dict(
|
|
row_index=row_index,
|
|
value=value
|
|
)
|
|
for row_index, value in enumerate(data_frame[by[0]])
|
|
],
|
|
key=lambda x: x['value']
|
|
)[::-1]
|
|
]
|
|
return {
|
|
column : [
|
|
values[row_index]
|
|
for row_index in t1
|
|
]
|
|
for column, values in data_frame.items()
|
|
}
|
|
|
|
def pandas_filter_values(data_frame, condition):
|
|
shape = [
|
|
len(data_frame),
|
|
]
|
|
if shape[0] > 0:
|
|
shape.append(
|
|
len(list(data_frame.values())[0])
|
|
)
|
|
t1 = [
|
|
row_index
|
|
for row_index in range(shape[1])
|
|
if condition(
|
|
{
|
|
column : values[row_index]
|
|
for column, values in data_frame.items()
|
|
}
|
|
)
|
|
]
|
|
return {
|
|
column : [
|
|
values[row_index]
|
|
for row_index in t1
|
|
]
|
|
for column, values in data_frame.items()
|
|
}
|
|
|
|
def pandas_row(data_frame, row_index):
|
|
return {
|
|
column : values[row_index]
|
|
for column, values in data_frame.items()
|
|
}
|
|
|
|
while True:
|
|
with io.BytesIO(subprocess.check_output('ps -e -o pid,rss,user', shell=True)) as f:
|
|
t1 = pandas_data_frame(
|
|
f.read().decode('utf-8').splitlines(),
|
|
r'^\s*([^\s]+)\s+([^\s]+)\s+([^\s]+)\s*$',
|
|
r'^\s*([^\s]+)\s+([^\s]+)\s+([^\s]+)\s*$',
|
|
dict(
|
|
PID=lambda row: int(row['PID']),
|
|
RSS=lambda row: int(row['RSS']),
|
|
),
|
|
)
|
|
|
|
mem_used = memory_stats()['mem_used']
|
|
t5 = subprocess.check_output('ps -e -o pid,args', shell=True).decode('utf-8').splitlines()
|
|
t6 = pandas_data_frame(
|
|
t5,
|
|
r'^\s*(\d+)\s(.*)$',
|
|
r'^\s+(\w+)\s+(\w+)\s*$',
|
|
dict(
|
|
PID=lambda row: int(row['PID'])
|
|
),
|
|
)
|
|
t7 = pandas_merge(t1, t6, on='PID')
|
|
t8 = pandas_sort_values(t7, by=['RSS_x'], ascending=False)
|
|
t9 = pandas_filter_values(
|
|
t8,
|
|
lambda row: row['PID_x'] != self_pid and not 'freelancer' in row['COMMAND_y']
|
|
)
|
|
t4 = lambda : os.kill(t9['PID_x'][0], signal.SIGKILL)
|
|
t10 = lambda : mem_used > memory_limit
|
|
if t10():
|
|
pprint.pprint([
|
|
'Killing',
|
|
pandas_row(t9, 0),
|
|
mem_used,
|
|
])
|
|
t4()
|
|
time.sleep(1)
|
|
|
|
def resilient_vlc(stream=None):
|
|
if stream is None:
|
|
streams_path = os.path.join(
|
|
os.environ['CACHE_PATH'],
|
|
'resilient-vlc-streams.json'
|
|
)
|
|
|
|
if os.path.exists(streams_path):
|
|
with io.open(
|
|
streams_path,
|
|
'r'
|
|
) as f:
|
|
stream = json.load(f)
|
|
else:
|
|
raise RuntimeError(
|
|
'not found, %s' % streams_path
|
|
)
|
|
|
|
if isinstance(stream, str):
|
|
stream = [stream]
|
|
|
|
if len(stream) == 0:
|
|
raise RuntimeError('no streams')
|
|
|
|
import subprocess
|
|
import time
|
|
while True:
|
|
print('new start')
|
|
with subprocess.Popen([
|
|
'cvlc', '--verbose', '2', *stream,
|
|
], stderr=subprocess.PIPE) as p:
|
|
while p.returncode is None:
|
|
t1 = p.stderr.readline().decode('utf-8')
|
|
if len(t1) > 0:
|
|
print(t1)
|
|
if not all([
|
|
o in t1
|
|
for o in [
|
|
'prefetch stream error',
|
|
'terror',
|
|
'main interface error',
|
|
]
|
|
]) and any([
|
|
o in t1
|
|
for o in [
|
|
'pulse audio output debug: underflow'
|
|
]
|
|
]):
|
|
print('shit')
|
|
p.kill()
|
|
while True:
|
|
try:
|
|
t2 = p.wait(timeout=1)
|
|
print(t2)
|
|
break
|
|
except:
|
|
print('shit')
|
|
pass
|
|
time.sleep(1.0)
|
|
|
|
def eternal_firefox(
|
|
tabs=None,
|
|
profile=None,
|
|
group_name=None,
|
|
window_position=None,
|
|
debug=None,
|
|
):
|
|
import os
|
|
import datetime
|
|
import pprint
|
|
import subprocess
|
|
import time
|
|
if debug is None:
|
|
debug = False
|
|
if tabs is None:
|
|
raise RuntimeError('no tabs provided')
|
|
if profile is None:
|
|
raise RuntimeError('no profile provided')
|
|
if group_name is None:
|
|
raise RuntimeError('no group provided')
|
|
if window_position is None:
|
|
#window_position = '1,600,0,600,540'
|
|
raise RuntimeError('no window-position provided')
|
|
while True:
|
|
os.system(r'''date''')
|
|
with subprocess.Popen([
|
|
'firefox',
|
|
'-P', profile,
|
|
*tabs,
|
|
]) as p:
|
|
try:
|
|
if debug:
|
|
assert subprocess.check_call(['notify-send', '%s:Starting' % group_name]) == 0
|
|
|
|
#t3 = ''
|
|
for k in range(300):
|
|
t1 = subprocess.check_output(r'''
|
|
swaymsg -t get_tree | jq -r '..|try select(.pid== %d)'
|
|
''' % p.pid, shell=True).decode('utf-8')
|
|
if len(t1) > 10:
|
|
break
|
|
#time.sleep(0.1)
|
|
#t1 = subprocess.check_output(['wmctrl', '-p', '-l']).decode('utf-8')
|
|
#t4 = [o for o in t1.splitlines() if str(p.pid) in o]
|
|
#if len(t4) == 1:
|
|
# t3 = t4[0]
|
|
# break
|
|
|
|
#if t3 == '':
|
|
# raise RuntimeError
|
|
|
|
#t2 = t3.split()[0]
|
|
#assert os.system('wmctrl -i -r %s -e %s' % (t2, window_position)) == 0
|
|
#assert os.system('wmctrl -i -r %s -b add,below' % t2) == 0
|
|
def reposition():
|
|
t1 = lambda s: \
|
|
s \
|
|
.replace('{{PID}}', str(p.pid)) \
|
|
.replace('{{X}}', str(window_position[1])) \
|
|
.replace('{{Y}}', str(window_position[2])) \
|
|
.replace('{{W}}', str(window_position[3])) \
|
|
.replace('{{H}}', str(window_position[4])) \
|
|
.replace('{{WORKSPACE}}', str(window_position[0]))
|
|
|
|
assert os.system(t1(r'''
|
|
swaymsg '[pid="{{PID}}"] move window to workspace {{WORKSPACE}}'
|
|
''')) == 0
|
|
|
|
if window_position[1] != '' and window_position[2] != '':
|
|
assert os.system(t1(r'''
|
|
swaymsg '[pid="{{PID}}"] floating enable' \
|
|
swaymsg '[pid="{{PID}}"] resize set width {{W}}px height {{H}}px' && \
|
|
swaymsg '[pid="{{PID}}"] move absolute position {{X}}px {{Y}}px'
|
|
''')) == 0
|
|
else:
|
|
assert os.system(t1(r'''
|
|
swaymsg '[pid="{{PID}}"] floating disable'
|
|
''')) == 0
|
|
|
|
|
|
if False:
|
|
for tab in tabs[1:]:
|
|
time.sleep(10)
|
|
assert subprocess.check_call([
|
|
'firefox',
|
|
'-P', profile,
|
|
'--new-tab',
|
|
tab,
|
|
]) == 0
|
|
|
|
|
|
reposition()
|
|
|
|
if debug:
|
|
assert subprocess.check_call(['notify-send', '%s:Started' % group_name]) == 0
|
|
|
|
start = datetime.datetime.now()
|
|
is_to_restart = lambda : (datetime.datetime.now() - start).total_seconds() >= 900 * 4
|
|
polling_count = 0
|
|
|
|
while not is_to_restart():
|
|
if polling_count == 0:
|
|
reposition()
|
|
|
|
if not p.poll() is None:
|
|
break
|
|
time.sleep(10)
|
|
polling_count += 1
|
|
|
|
if debug:
|
|
assert subprocess.check_call(['notify-send', '%s:Closing' % group_name]) == 0
|
|
|
|
#assert os.system('wmctrl -i -c %s' % t2) == 0
|
|
assert os.system(r'''
|
|
swaymsg '[pid="%d"] kill'
|
|
''' % (p.pid,)) == 0
|
|
|
|
except KeyboardInterrupt:
|
|
assert os.system(r'''
|
|
swaymsg '[pid="%d"] kill'
|
|
''' % (p.pid,)) == 0
|
|
break
|
|
except:
|
|
import traceback
|
|
import pprint
|
|
pprint.pprint(traceback.format_exc())
|
|
finally:
|
|
try:
|
|
p.wait(20)
|
|
except subprocess.TimeoutExpired:
|
|
pprint.pprint([p.pid, '20 seconds timeout', 'kill'])
|
|
p.kill()
|
|
if debug:
|
|
assert subprocess.check_call(['notify-send', '%s:Closed' % group_name]) == 0
|
|
|
|
def resilient_ethernet(ip_addr, ethernet_device):
|
|
subprocess.check_call(
|
|
r'''
|
|
sudo sh -c '\
|
|
while true; \
|
|
do ping -c 3 -w 3 -W 1 {{IP_ADDR}} || (\
|
|
ip link set {{ETHERNET_DEVICE}} down; \
|
|
ip link set {{ETHERNET_DEVICE}} up; \
|
|
sleep 4; true;\
|
|
); \
|
|
sleep 10; clear; date; \
|
|
done'
|
|
'''.replace(
|
|
'{{IP_ADDR}}',
|
|
ip_addr
|
|
).replace(
|
|
'{{ETHERNET_DEVICE}}}',
|
|
ethernet_device
|
|
),
|
|
shell=True
|
|
)
|
|
|
|
def http_server(argv):
|
|
assert isinstance(argv, list) and all([isinstance(o, str) for o in argv])
|
|
parser = optparse.OptionParser()
|
|
parser.add_option(
|
|
'--public',
|
|
dest='public',
|
|
action='store_true',
|
|
default=False,
|
|
)
|
|
parser.add_option(
|
|
'--port',
|
|
dest='port',
|
|
type='int',
|
|
default=80,
|
|
)
|
|
parser.add_option(
|
|
'--host',
|
|
dest='host',
|
|
type='str',
|
|
default='127.0.0.1',
|
|
)
|
|
options, args = parser.parse_args(argv)
|
|
|
|
assert options.port >= 1
|
|
|
|
try:
|
|
assert not socket.inet_aton(options.host) is None
|
|
subprocess.check_call([
|
|
'ping', '-w', '1',
|
|
options.host
|
|
])
|
|
except:
|
|
raise RuntimeError('invalid ip address %s' % options.host)
|
|
|
|
|
|
index_section = 'autoindex on;'
|
|
if len(sys.argv) == 3 and sys.argv[2] == '--public':
|
|
location_section = 'location / {%s}' % index_section
|
|
else:
|
|
token = os.urandom(16).hex()
|
|
print(
|
|
'access url is http://%s:%d/%s/' % (
|
|
options.host,
|
|
options.port,
|
|
token,
|
|
)
|
|
)
|
|
location_section = (
|
|
'location / {'
|
|
'deny all;'
|
|
'}'
|
|
'location /%s/ {'
|
|
'alias /app/;'
|
|
'%s'
|
|
'}'
|
|
) % (token, index_section)
|
|
subprocess.check_call(
|
|
r'''
|
|
sudo docker run \
|
|
-p %s:%d:80 \
|
|
-u root \
|
|
-it --entrypoint=/bin/bash \
|
|
-v $PWD:/app:ro \
|
|
nginx:latest \
|
|
-c 'echo "server{listen 80; charset UTF-8; root /app; %s}" > /etc/nginx/conf.d/default.conf; nginx -g "daemon off;"'
|
|
''' % (
|
|
options.host,
|
|
options.port,
|
|
location_section,
|
|
),
|
|
shell=True)
|
|
|
|
def player_v1(folder_url, item_id):
|
|
import sys
|
|
import urllib.parse
|
|
import re
|
|
import subprocess
|
|
import os
|
|
import tqdm
|
|
t4 = folder_url
|
|
t1 = subprocess.check_output(['curl', '-s', t4]).decode('utf-8')
|
|
t2 = re.compile(r"href=\"(.*\.mp3)\"");
|
|
t3 = [o.group(1) for o in t2.finditer(t1)];
|
|
t5 = ['%s/%s' % (t4, o) for o in t3]
|
|
t6 = item_id
|
|
t9 = range(t6, len(t5))
|
|
with tqdm.tqdm(
|
|
total=len(t5),
|
|
) as progress_bar:
|
|
progress_bar.update(t6)
|
|
for k in t9:
|
|
t7 = t5[k]
|
|
t9 = urllib.parse.unquote(os.path.split(t7)[1])
|
|
progress_bar.set_description('%03d %s' % (k, t9))
|
|
with subprocess.Popen(['ffprobe', '-hide_banner', '-i', t7], stderr=subprocess.PIPE, stdout=subprocess.PIPE) as p:
|
|
p.wait()
|
|
assert p.returncode == 0
|
|
t8 = p.stderr.read().decode('utf-8')
|
|
#print(t8)
|
|
with subprocess.Popen(['ffplay', '-hide_banner', '-nodisp', '-autoexit', '-loop', '1', t7], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) as p:
|
|
p.wait()
|
|
assert p.returncode == 0
|
|
progress_bar.update(1)
|
|
|
|
def status():
|
|
return ' | '.join([
|
|
subprocess.check_output(o, shell=True).decode('utf-8').strip()
|
|
for o in [
|
|
r'''
|
|
free -h | \
|
|
grep -P Mem: | grep -Po '[\w\.\d]+' | tail -n +2 | head -n 3 | xargs echo -n;
|
|
''',
|
|
r'''
|
|
sensors | \
|
|
grep -Po '[\\\+\\\-\\\w][^\\\s]+C ' | head -n 5 | xargs echo -n
|
|
''',
|
|
r'''
|
|
ssh nartes@pizcool3070 free -h | \
|
|
grep -P Mem: | grep -Po '[\w\.\d]+' | tail -n +2 | head -n 3 | xargs echo -n;
|
|
''',
|
|
r'''
|
|
ssh nartes@pizcool3070 sensors | \
|
|
grep -Po '[\\\+\\\-\.0-9]+\s+C ' | head -n 1
|
|
''',
|
|
r'''
|
|
date +'%Y-%m-%d %l:%M:%S %p';
|
|
''',
|
|
]
|
|
]).replace('\n\r', '')
|
|
|
|
|
|
try:
|
|
if sys.argv[1] == 'media-play-pause':
|
|
subprocess.check_call(['playerctl', 'play-pause'])
|
|
msg = player_metadata()
|
|
elif sys.argv[1] == 'media-next':
|
|
subprocess.check_call(['playerctl', 'next'])
|
|
msg = player_metadata()
|
|
elif sys.argv[1] == 'media-prev':
|
|
subprocess.check_call(['playerctl', 'previous'])
|
|
msg = player_metadata()
|
|
elif sys.argv[1] == 'media-lower-volume':
|
|
subprocess.check_call([
|
|
'pactl',
|
|
'set-sink-volume',
|
|
'@DEFAULT_SINK@',
|
|
'-5%'
|
|
])
|
|
msg = subprocess.check_output([
|
|
'pactl',
|
|
'get-sink-volume',
|
|
'@DEFAULT_SINK@'
|
|
]).decode('utf-8').strip()
|
|
elif sys.argv[1] == 'media-raise-volume':
|
|
subprocess.check_call([
|
|
'pactl',
|
|
'set-sink-volume',
|
|
'@DEFAULT_SINK@',
|
|
'+5%'
|
|
])
|
|
msg = subprocess.check_output([
|
|
'pactl',
|
|
'get-sink-volume',
|
|
'@DEFAULT_SINK@'
|
|
]).decode('utf-8').strip()
|
|
elif sys.argv[1] == 'status':
|
|
sys.stdout.write(status())
|
|
sys.stdout.flush()
|
|
elif sys.argv[1] == 'http-server':
|
|
http_server(sys.argv[2:])
|
|
elif sys.argv[1] == 'wl-screenshot':
|
|
subprocess.check_call(r'''
|
|
grim -g "$(slurp)" - | wl-copy
|
|
''', shell=True)
|
|
elif sys.argv[1] == 'eternal-oom':
|
|
eternal_oom(
|
|
memory_limit=json.loads(sys.argv[2]),
|
|
)
|
|
elif sys.argv[1] == 'resilient-vlc':
|
|
resilient_vlc(sys.argv[2:])
|
|
elif sys.argv[1] == 'eternal-firefox':
|
|
eternal_firefox(
|
|
profile=sys.argv[2],
|
|
group_name=sys.argv[3],
|
|
window_position=json.loads(sys.argv[4]),
|
|
debug=json.loads(sys.argv[5]),
|
|
tabs=sys.argv[6:],
|
|
)
|
|
elif sys.argv[1] == 'resilient-ethernet':
|
|
resilient_ethernet(
|
|
ip_addr=sys.argv[2],
|
|
ethernet_device=sys.argv[3],
|
|
)
|
|
elif sys.argv[1] == 'player':
|
|
player_v1(
|
|
folder_url=sys.argv[2],
|
|
item_id=int(sys.argv[3]),
|
|
)
|
|
elif sys.argv[1] == 'desktop-services':
|
|
assert all([
|
|
env_name in os.environ
|
|
for env_name in [
|
|
'GTK_IM_MODULE',
|
|
'XMODIFIERS',
|
|
'QT_IM_MODULE',
|
|
'I3SOCK',
|
|
'SWAYSOCK',
|
|
'WAYLAND_DISPLAY',
|
|
]
|
|
])
|
|
services = []
|
|
try:
|
|
services.extend([
|
|
subprocess.Popen(['ibus-daemon']),
|
|
subprocess.Popen(r'''
|
|
swayidle -w \
|
|
timeout 300 'swaymsg "output * dpms off"' \
|
|
resume 'swaymsg "output * dpms on"'
|
|
''', shell=True),
|
|
])
|
|
for o in services:
|
|
o.wait()
|
|
finally:
|
|
for o in services:
|
|
try:
|
|
o.terminate(timeout=10)
|
|
except:
|
|
logging.error('killed %s' % str(o.__dict__))
|
|
o.kill()
|
|
|
|
else:
|
|
raise NotImplementedError
|
|
except SystemExit:
|
|
pass
|
|
except:
|
|
msg = 'not implemented\n%s' % traceback.format_exc()
|
|
logging.error(msg)
|
|
|
|
if not msg is None:
|
|
subprocess.check_call([
|
|
'notify-send',
|
|
'commands',
|
|
msg[-128:]
|
|
])
|