pywebexec 1.3.3__py3-none-any.whl → 1.3.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pywebexec/pywebexec.py +71 -46
- pywebexec/version.py +2 -2
- {pywebexec-1.3.3.dist-info → pywebexec-1.3.5.dist-info}/METADATA +1 -1
- {pywebexec-1.3.3.dist-info → pywebexec-1.3.5.dist-info}/RECORD +8 -8
- {pywebexec-1.3.3.dist-info → pywebexec-1.3.5.dist-info}/LICENSE +0 -0
- {pywebexec-1.3.3.dist-info → pywebexec-1.3.5.dist-info}/WHEEL +0 -0
- {pywebexec-1.3.3.dist-info → pywebexec-1.3.5.dist-info}/entry_points.txt +0 -0
- {pywebexec-1.3.3.dist-info → pywebexec-1.3.5.dist-info}/top_level.txt +0 -0
pywebexec/pywebexec.py
CHANGED
@@ -17,8 +17,9 @@ from socket import gethostname, gethostbyname_ex, gethostbyaddr, inet_aton, inet
|
|
17
17
|
import ssl
|
18
18
|
import re
|
19
19
|
import pwd
|
20
|
-
import platform
|
21
20
|
from secrets import token_urlsafe
|
21
|
+
import pty
|
22
|
+
import select
|
22
23
|
|
23
24
|
if os.environ.get('PYWEBEXEC_LDAP_SERVER'):
|
24
25
|
from ldap3 import Server, Connection, ALL, SIMPLE, SUBTREE, Tls
|
@@ -328,12 +329,7 @@ def parseargs():
|
|
328
329
|
user = pwd.getpwuid(os.getuid())[0]
|
329
330
|
update_command_status(command_id, 'running', command="term", params=[user,os.ttyname(sys.stdout.fileno())], start_time=start_time, user=user)
|
330
331
|
output_file_path = get_output_file_path(command_id)
|
331
|
-
|
332
|
-
script_opt = '-F'
|
333
|
-
else:
|
334
|
-
script_opt = '-f'
|
335
|
-
res = os.system(f"script {script_opt} {output_file_path}")
|
336
|
-
|
332
|
+
res = script(output_file_path)
|
337
333
|
end_time = datetime.now().isoformat()
|
338
334
|
update_command_status(command_id, status="success", end_time=end_time, exit_code=res)
|
339
335
|
sys.exit(res)
|
@@ -431,36 +427,88 @@ def read_command_status(command_id):
|
|
431
427
|
|
432
428
|
return status_data
|
433
429
|
|
434
|
-
|
435
|
-
|
430
|
+
|
431
|
+
def script(filename):
|
432
|
+
shell = os.environ.get('SHELL', 'sh')
|
433
|
+
with open(filename, 'wb') as s:
|
434
|
+
def read(fd):
|
435
|
+
data = os.read(fd, 1024)
|
436
|
+
s.write(data)
|
437
|
+
s.flush()
|
438
|
+
return data
|
439
|
+
return pty.spawn(shell, read)
|
440
|
+
|
436
441
|
|
437
442
|
def run_command(command, params, command_id, user):
|
438
443
|
start_time = datetime.now().isoformat()
|
439
444
|
update_command_status(command_id, 'running', command=command, params=params, start_time=start_time, user=user)
|
440
445
|
try:
|
441
446
|
output_file_path = get_output_file_path(command_id)
|
442
|
-
with open(output_file_path, '
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
447
|
+
with open(output_file_path, 'wb') as output_file:
|
448
|
+
def read(fd):
|
449
|
+
data = os.read(fd, 1024)
|
450
|
+
output_file.write(data)
|
451
|
+
output_file.flush()
|
452
|
+
return data
|
453
|
+
|
454
|
+
def spawn_pty():
|
455
|
+
pid, fd = pty.fork()
|
456
|
+
if pid == 0: # children
|
457
|
+
try:
|
458
|
+
os.setsid()
|
459
|
+
except:
|
460
|
+
pass
|
461
|
+
os.execvp(command, [command] + params)
|
462
|
+
else:
|
463
|
+
update_command_status(command_id, 'running', pid=pid, user=user)
|
464
|
+
while True:
|
465
|
+
try:
|
466
|
+
read(fd)
|
467
|
+
except OSError:
|
468
|
+
break
|
469
|
+
(pid, status) = os.waitpid(pid, 0)
|
470
|
+
print(status)
|
471
|
+
return status
|
472
|
+
status = spawn_pty()
|
450
473
|
end_time = datetime.now().isoformat()
|
451
474
|
# Update the status based on the result
|
452
|
-
if
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
475
|
+
if os.WIFEXITED(status):
|
476
|
+
exit_code = os.WEXITSTATUS(status)
|
477
|
+
if exit_code == 0:
|
478
|
+
update_command_status(command_id, 'success', end_time=end_time, exit_code=exit_code, user=user)
|
479
|
+
elif exit_code == -15:
|
480
|
+
update_command_status(command_id, 'aborted', end_time=end_time, exit_code=exit_code, user=user)
|
481
|
+
else:
|
482
|
+
update_command_status(command_id, 'failed', end_time=end_time, exit_code=exit_code, user=user)
|
458
483
|
except Exception as e:
|
459
484
|
end_time = datetime.now().isoformat()
|
460
485
|
update_command_status(command_id, 'failed', end_time=end_time, exit_code=1, user=user)
|
461
486
|
with open(get_output_file_path(command_id), 'a') as output_file:
|
462
487
|
output_file.write(str(e))
|
463
488
|
|
489
|
+
@app.route('/stop_command/<command_id>', methods=['POST'])
|
490
|
+
def stop_command(command_id):
|
491
|
+
status = read_command_status(command_id)
|
492
|
+
if not status or 'pid' not in status:
|
493
|
+
return jsonify({'error': 'Invalid command_id or command not running'}), 400
|
494
|
+
|
495
|
+
pid = status['pid']
|
496
|
+
end_time = datetime.now().isoformat()
|
497
|
+
try:
|
498
|
+
os.killpg(os.getpgid(pid), 15) # Send SIGTERM to the process group
|
499
|
+
#os.waitpid(pid, 0) # Wait for the process to terminate
|
500
|
+
#update_command_status(command_id, 'aborted', end_time=end_time, exit_code=-15)
|
501
|
+
return jsonify({'message': 'Command aborted'})
|
502
|
+
except Exception as e:
|
503
|
+
status_data = read_command_status(command_id) or {}
|
504
|
+
status_data['status'] = 'failed'
|
505
|
+
status_data['end_time'] = end_time
|
506
|
+
status_data['exit_code'] = 1
|
507
|
+
with open(get_status_file_path(command_id), 'w') as f:
|
508
|
+
json.dump(status_data, f)
|
509
|
+
with open(get_output_file_path(command_id), 'a') as output_file:
|
510
|
+
output_file.write(str(e))
|
511
|
+
return jsonify({'error': 'Failed to terminate command'}), 500
|
464
512
|
|
465
513
|
parseargs()
|
466
514
|
|
@@ -563,29 +611,6 @@ def run_command_endpoint():
|
|
563
611
|
|
564
612
|
return jsonify({'message': 'Command is running', 'command_id': command_id})
|
565
613
|
|
566
|
-
@app.route('/stop_command/<command_id>', methods=['POST'])
|
567
|
-
def stop_command(command_id):
|
568
|
-
status = read_command_status(command_id)
|
569
|
-
if not status or 'pid' not in status:
|
570
|
-
return jsonify({'error': 'Invalid command_id or command not running'}), 400
|
571
|
-
|
572
|
-
pid = status['pid']
|
573
|
-
end_time = datetime.now().isoformat()
|
574
|
-
try:
|
575
|
-
os.kill(pid, 15) # Send SIGTERM
|
576
|
-
#update_command_status(command_id, 'aborted', end_time=end_time, exit_code=-15)
|
577
|
-
return jsonify({'message': 'Command aborted'})
|
578
|
-
except Exception as e:
|
579
|
-
status_data = read_command_status(command_id) or {}
|
580
|
-
status_data['status'] = 'failed'
|
581
|
-
status_data['end_time'] = end_time
|
582
|
-
status_data['exit_code'] = 1
|
583
|
-
with open(get_status_file_path(command_id), 'w') as f:
|
584
|
-
json.dump(status_data, f)
|
585
|
-
with open(get_output_file_path(command_id), 'a') as output_file:
|
586
|
-
output_file.write(str(e))
|
587
|
-
return jsonify({'error': 'Failed to terminate command'}), 500
|
588
|
-
|
589
614
|
@app.route('/command_status/<command_id>', methods=['GET'])
|
590
615
|
def get_command_status(command_id):
|
591
616
|
status = read_command_status(command_id)
|
pywebexec/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
pywebexec/__init__.py,sha256=4spIsVaF8RJt8S58AG_wWoORRNkws9Iwqprj27C3ljM,99
|
2
|
-
pywebexec/pywebexec.py,sha256=
|
3
|
-
pywebexec/version.py,sha256=
|
2
|
+
pywebexec/pywebexec.py,sha256=zZRaHVUoWqX6IHCesfrboCjcjKGYnOq3jZpos7h9JEI,26333
|
3
|
+
pywebexec/version.py,sha256=FcYWgM5oYgqvidiF8nHnDFf0uLGibSe_Bdaq7MY6S4U,411
|
4
4
|
pywebexec/static/css/Consolas NF.ttf,sha256=DJEOzF0eqZ-kxu3Gs_VE8X0NJqiobBzmxWDGpdgGRxI,1313900
|
5
5
|
pywebexec/static/css/style.css,sha256=iLX6k1hoWLinZWyqtbH50U-0hND2M-5_Zr1U1UC_gos,5578
|
6
6
|
pywebexec/static/css/xterm.css,sha256=gy8_LGA7Q61DUf8ElwFQzHqHMBQnbbEmpgZcbdgeSHI,5383
|
@@ -20,9 +20,9 @@ pywebexec/static/js/xterm/xterm-addon-fit.js,sha256=Pprm9pZe4SadVXS5Bc8b9VnC9Ex4
|
|
20
20
|
pywebexec/static/js/xterm/xterm.js,sha256=Bzka76jZwEhVt_LlS0e0qMw7ryGa1p5qfxFyeohphBo,283371
|
21
21
|
pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
22
22
|
pywebexec/templates/index.html,sha256=DYtT555wSNhnFtzzHhPMWJireynCJNnAuTytpoORQeE,2321
|
23
|
-
pywebexec-1.3.
|
24
|
-
pywebexec-1.3.
|
25
|
-
pywebexec-1.3.
|
26
|
-
pywebexec-1.3.
|
27
|
-
pywebexec-1.3.
|
28
|
-
pywebexec-1.3.
|
23
|
+
pywebexec-1.3.5.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
|
24
|
+
pywebexec-1.3.5.dist-info/METADATA,sha256=rNB2fBP4AMqwoDaR9tE2yq3EDr1mKs6zW6neFJ87aRM,7397
|
25
|
+
pywebexec-1.3.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
26
|
+
pywebexec-1.3.5.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
|
27
|
+
pywebexec-1.3.5.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
|
28
|
+
pywebexec-1.3.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|