pywebexec 1.4.17__py3-none-any.whl → 1.4.19__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 +72 -40
- pywebexec/static/js/popup.js +3 -0
- pywebexec/static/js/script.js +7 -0
- pywebexec/version.py +2 -2
- {pywebexec-1.4.17.dist-info → pywebexec-1.4.19.dist-info}/METADATA +1 -1
- {pywebexec-1.4.17.dist-info → pywebexec-1.4.19.dist-info}/RECORD +10 -10
- {pywebexec-1.4.17.dist-info → pywebexec-1.4.19.dist-info}/LICENSE +0 -0
- {pywebexec-1.4.17.dist-info → pywebexec-1.4.19.dist-info}/WHEEL +0 -0
- {pywebexec-1.4.17.dist-info → pywebexec-1.4.19.dist-info}/entry_points.txt +0 -0
- {pywebexec-1.4.17.dist-info → pywebexec-1.4.19.dist-info}/top_level.txt +0 -0
pywebexec/pywebexec.py
CHANGED
@@ -48,7 +48,7 @@ CONFDIR = os.path.expanduser("~/").rstrip('/')
|
|
48
48
|
if os.path.isdir(f"{CONFDIR}/.config"):
|
49
49
|
CONFDIR += '/.config'
|
50
50
|
CONFDIR += "/.pywebexec"
|
51
|
-
|
51
|
+
COMMAND_ID = str(uuid.uuid4())
|
52
52
|
|
53
53
|
# In-memory cache for command statuses
|
54
54
|
command_status_cache = {}
|
@@ -291,17 +291,27 @@ def daemon_d(action, pidfilepath, silent=False, hostname=None, args=None):
|
|
291
291
|
except Exception as e:
|
292
292
|
print(e)
|
293
293
|
|
294
|
-
def start_term(
|
294
|
+
def start_term():
|
295
295
|
os.environ["PYWEBEXEC"] = " (shared)"
|
296
296
|
os.chdir(CWD)
|
297
297
|
start_time = datetime.now().isoformat()
|
298
298
|
user = pwd.getpwuid(os.getuid())[0]
|
299
|
-
print(f"Starting terminal session for {user} : {
|
300
|
-
update_command_status(
|
301
|
-
|
299
|
+
print(f"Starting terminal session for {user} : {COMMAND_ID}")
|
300
|
+
update_command_status(COMMAND_ID, {
|
301
|
+
'status': 'running',
|
302
|
+
'command': 'term',
|
303
|
+
'params': [user, os.ttyname(sys.stdout.fileno())],
|
304
|
+
'start_time': start_time,
|
305
|
+
'user': user
|
306
|
+
})
|
307
|
+
output_file_path = get_output_file_path(COMMAND_ID)
|
302
308
|
res = script(output_file_path)
|
303
309
|
end_time = datetime.now().isoformat()
|
304
|
-
update_command_status(
|
310
|
+
update_command_status(COMMAND_ID, {
|
311
|
+
'status': 'success',
|
312
|
+
'end_time': end_time,
|
313
|
+
'exit_code': res
|
314
|
+
})
|
305
315
|
print("Terminal session ended")
|
306
316
|
return res
|
307
317
|
|
@@ -371,7 +381,7 @@ def parseargs():
|
|
371
381
|
os.mkdir(COMMAND_STATUS_DIR, mode=0o700)
|
372
382
|
if args.action == "term":
|
373
383
|
COMMAND_STATUS_DIR = f"{os.getcwd()}/{COMMAND_STATUS_DIR}"
|
374
|
-
sys.exit(start_term(
|
384
|
+
sys.exit(start_term())
|
375
385
|
|
376
386
|
(hostname, ip) = resolve(gethostname()) if args.listen == '0.0.0.0' else resolve(args.listen)
|
377
387
|
|
@@ -409,25 +419,11 @@ def get_status_file_path(command_id):
|
|
409
419
|
def get_output_file_path(command_id):
|
410
420
|
return os.path.join(COMMAND_STATUS_DIR, f'{command_id}_output.txt')
|
411
421
|
|
412
|
-
def update_command_status(command_id,
|
422
|
+
def update_command_status(command_id, updates):
|
413
423
|
status_file_path = get_status_file_path(command_id)
|
414
424
|
status_data = read_command_status(command_id) or {}
|
415
|
-
status_data
|
416
|
-
if
|
417
|
-
status_data['command'] = command
|
418
|
-
if params is not None:
|
419
|
-
status_data['params'] = params
|
420
|
-
if start_time is not None:
|
421
|
-
status_data['start_time'] = start_time
|
422
|
-
if end_time is not None:
|
423
|
-
status_data['end_time'] = end_time
|
424
|
-
if exit_code is not None:
|
425
|
-
status_data['exit_code'] = exit_code
|
426
|
-
if pid is not None:
|
427
|
-
status_data['pid'] = pid
|
428
|
-
if user is not None:
|
429
|
-
status_data['user'] = user
|
430
|
-
if status != 'running':
|
425
|
+
status_data.update(updates)
|
426
|
+
if status_data['status'] != 'running':
|
431
427
|
output_file_path = get_output_file_path(command_id)
|
432
428
|
if os.path.exists(output_file_path):
|
433
429
|
status_data['last_output_line'] = get_last_non_empty_line_of_file(output_file_path)
|
@@ -435,7 +431,7 @@ def update_command_status(command_id, status, command=None, params=None, start_t
|
|
435
431
|
json.dump(status_data, f)
|
436
432
|
|
437
433
|
# Update cache if status is not "running"
|
438
|
-
if status != 'running':
|
434
|
+
if status_data['status'] != 'running':
|
439
435
|
command_status_cache[command_id] = status_data
|
440
436
|
elif command_id in command_status_cache:
|
441
437
|
del command_status_cache[command_id]
|
@@ -463,8 +459,12 @@ def read_command_status(command_id):
|
|
463
459
|
def sigwinch_passthrough(sig, data):
|
464
460
|
s = struct.pack("HHHH", 0, 0, 0, 0)
|
465
461
|
a = struct.unpack('hhhh', fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, s))
|
466
|
-
global p
|
462
|
+
global p, COMMAND_ID
|
467
463
|
p.setwinsize(a[0], a[1])
|
464
|
+
update_command_status(COMMAND_ID, {
|
465
|
+
'rows': a[0],
|
466
|
+
'cols': a[1],
|
467
|
+
})
|
468
468
|
|
469
469
|
|
470
470
|
def script(output_file):
|
@@ -481,12 +481,22 @@ def script(output_file):
|
|
481
481
|
|
482
482
|
def run_command(command, params, command_id, user):
|
483
483
|
start_time = datetime.now().isoformat()
|
484
|
-
update_command_status(command_id,
|
484
|
+
update_command_status(command_id, {
|
485
|
+
'status': 'running',
|
486
|
+
'command': command,
|
487
|
+
'params': params,
|
488
|
+
'start_time': start_time,
|
489
|
+
'user': user
|
490
|
+
})
|
485
491
|
output_file_path = get_output_file_path(command_id)
|
486
492
|
try:
|
487
493
|
with open(output_file_path, 'wb') as fd:
|
488
494
|
p = pexpect.spawn(command, params, ignore_sighup=True, timeout=None)
|
489
|
-
update_command_status(command_id,
|
495
|
+
update_command_status(command_id, {
|
496
|
+
'status': 'running',
|
497
|
+
'pid': p.pid,
|
498
|
+
'user': user
|
499
|
+
})
|
490
500
|
p.setwinsize(24, 125)
|
491
501
|
p.logfile = fd
|
492
502
|
p.expect(pexpect.EOF)
|
@@ -496,16 +506,36 @@ def run_command(command, params, command_id, user):
|
|
496
506
|
# Update the status based on the result
|
497
507
|
if status is None:
|
498
508
|
exit_code = -15
|
499
|
-
update_command_status(command_id,
|
509
|
+
update_command_status(command_id, {
|
510
|
+
'status': 'aborted',
|
511
|
+
'end_time': end_time,
|
512
|
+
'exit_code': exit_code,
|
513
|
+
'user': user
|
514
|
+
})
|
500
515
|
else:
|
501
516
|
exit_code = status
|
502
517
|
if exit_code == 0:
|
503
|
-
update_command_status(command_id,
|
518
|
+
update_command_status(command_id, {
|
519
|
+
'status': 'success',
|
520
|
+
'end_time': end_time,
|
521
|
+
'exit_code': exit_code,
|
522
|
+
'user': user
|
523
|
+
})
|
504
524
|
else:
|
505
|
-
update_command_status(command_id,
|
525
|
+
update_command_status(command_id, {
|
526
|
+
'status': 'failed',
|
527
|
+
'end_time': end_time,
|
528
|
+
'exit_code': exit_code,
|
529
|
+
'user': user
|
530
|
+
})
|
506
531
|
except Exception as e:
|
507
532
|
end_time = datetime.now().isoformat()
|
508
|
-
update_command_status(command_id,
|
533
|
+
update_command_status(command_id, {
|
534
|
+
'status': 'failed',
|
535
|
+
'end_time': end_time,
|
536
|
+
'exit_code': 1,
|
537
|
+
'user': user
|
538
|
+
})
|
509
539
|
with open(get_output_file_path(command_id), 'a') as output_file:
|
510
540
|
output_file.write(str(e))
|
511
541
|
|
@@ -618,14 +648,16 @@ def run_command_endpoint():
|
|
618
648
|
except ValueError as e:
|
619
649
|
return jsonify({'error': str(e)}), 400
|
620
650
|
|
621
|
-
# Generate a unique command_id
|
622
|
-
command_id = str(uuid.uuid4())
|
623
|
-
|
624
651
|
# Get the user from the session
|
625
652
|
user = session.get('username', '-')
|
626
|
-
|
653
|
+
command_id = str(uuid.uuid4())
|
627
654
|
# Set the initial status to running and save command details
|
628
|
-
update_command_status(command_id,
|
655
|
+
update_command_status(command_id, {
|
656
|
+
'status': 'running',
|
657
|
+
'command': command,
|
658
|
+
'params': params,
|
659
|
+
'user': user
|
660
|
+
})
|
629
661
|
|
630
662
|
# Run the command in a separate thread
|
631
663
|
thread = threading.Thread(target=run_command, args=(command_path, params, command_id, user))
|
@@ -698,6 +730,7 @@ def get_command_output(command_id):
|
|
698
730
|
response = {
|
699
731
|
'output': output[-maxsize:],
|
700
732
|
'status': status_data.get("status"),
|
733
|
+
'cols': status_data.get("cols"),
|
701
734
|
'links': {
|
702
735
|
'next': f'{request.url_root}command_output/{command_id}?offset={new_offset}&maxsize={maxsize}{token_param}'
|
703
736
|
}
|
@@ -735,9 +768,8 @@ def main():
|
|
735
768
|
sys.argv.remove("shareterm")
|
736
769
|
with open(basef + ".log", "ab+") as log:
|
737
770
|
pywebexec = subprocess.Popen([sys.executable] + sys.argv, stdout=log, stderr=log)
|
738
|
-
|
739
|
-
|
740
|
-
res = start_term(command_id)
|
771
|
+
print_urls(COMMAND_ID)
|
772
|
+
res = start_term()
|
741
773
|
print("Stopping server")
|
742
774
|
time.sleep(1)
|
743
775
|
pywebexec.terminate()
|
pywebexec/static/js/popup.js
CHANGED
@@ -55,6 +55,9 @@ async function fetchOutput(url) {
|
|
55
55
|
terminal.write(data.error);
|
56
56
|
clearInterval(outputInterval);
|
57
57
|
} else {
|
58
|
+
if (data.cols) {
|
59
|
+
terminal.resize(data.cols, terminal.rows);
|
60
|
+
} else fitAddon.fit();
|
58
61
|
percentage = slider.value;
|
59
62
|
fullOutput += data.output;
|
60
63
|
if (fullOutput.length > maxSize)
|
pywebexec/static/js/script.js
CHANGED
@@ -32,6 +32,10 @@ function initTerminal()
|
|
32
32
|
},
|
33
33
|
customGlyphs: false,
|
34
34
|
rescaleOverlappingGlyphs: true,
|
35
|
+
// windowsPty: {
|
36
|
+
// backend: 'conpty',
|
37
|
+
// buildnumber: 21376,
|
38
|
+
// }
|
35
39
|
});
|
36
40
|
}
|
37
41
|
let terminal = initTerminal()
|
@@ -137,6 +141,9 @@ async function fetchOutput(url) {
|
|
137
141
|
terminal.write(data.error);
|
138
142
|
clearInterval(outputInterval);
|
139
143
|
} else {
|
144
|
+
if (data.cols) {
|
145
|
+
terminal.resize(data.cols, terminal.rows);
|
146
|
+
} else fitAddon.fit();
|
140
147
|
fullOutput += data.output;
|
141
148
|
if (fullOutput.length > maxSize)
|
142
149
|
fullOutput = fullOutput.slice(-maxSize);
|
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=hacpB6OnS_ZN1SBmuReTUkvAuenl6czD6aD_yV8wusY,28462
|
3
|
+
pywebexec/version.py,sha256=uoD5RETbNPgiYh3HM3h7SS3IHV8yYSaVY1iOmNCumf8,413
|
4
4
|
pywebexec/static/css/Consolas NF.ttf,sha256=DJEOzF0eqZ-kxu3Gs_VE8X0NJqiobBzmxWDGpdgGRxI,1313900
|
5
5
|
pywebexec/static/css/style.css,sha256=cGJHPFj23SQ_bFpesfUaFA3VFxhXtpRUOL_zzx3x_X8,5726
|
6
6
|
pywebexec/static/css/xterm.css,sha256=gy8_LGA7Q61DUf8ElwFQzHqHMBQnbbEmpgZcbdgeSHI,5383
|
@@ -14,8 +14,8 @@ pywebexec/static/images/popup.svg,sha256=0Bl9A_v5cBsMPn6FnOlVWlAQKgd2zqiWQbhjcL9
|
|
14
14
|
pywebexec/static/images/running.gif,sha256=iYuzQGkMxrakSIwt6gPieKCImGZoSAHmU5MUNZa7cpw,25696
|
15
15
|
pywebexec/static/images/success.svg,sha256=PJDcCSTevJh7rkfSFLtc7P0pbeh8PVQBS8DaOLQemmc,489
|
16
16
|
pywebexec/static/js/commands.js,sha256=8JDb3Q55EJOYf2Q9Uy6qEuqAnn1oGjM0RndgQ4aOjqo,7725
|
17
|
-
pywebexec/static/js/popup.js,sha256=
|
18
|
-
pywebexec/static/js/script.js,sha256=
|
17
|
+
pywebexec/static/js/popup.js,sha256=JdBHm09MBYpbh5It_LYj0DH_MXj5Z407-AXscHOny4Y,4779
|
18
|
+
pywebexec/static/js/script.js,sha256=KSYOWNHtRrGZX4UZ60BzEK-06SgLonDpxU6EEhdeiQo,12276
|
19
19
|
pywebexec/static/js/xterm/LICENSE,sha256=EU1P4eXTull-_T9I80VuwnJXubB-zLzUl3xpEYj2T1M,1083
|
20
20
|
pywebexec/static/js/xterm/ansi_up.min.js,sha256=KNGV0vEr30hNqKQimTAvGVy-icD5A1JqMQTtvYtKR2Y,13203
|
21
21
|
pywebexec/static/js/xterm/xterm-addon-fit.js,sha256=Pprm9pZe4SadVXS5Bc8b9VnC9Ex4QlWwA0pxOH53Gck,1460
|
@@ -23,9 +23,9 @@ pywebexec/static/js/xterm/xterm.js,sha256=Bzka76jZwEhVt_LlS0e0qMw7ryGa1p5qfxFyeo
|
|
23
23
|
pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
24
|
pywebexec/templates/index.html,sha256=DYtT555wSNhnFtzzHhPMWJireynCJNnAuTytpoORQeE,2321
|
25
25
|
pywebexec/templates/popup.html,sha256=T6_tAOUoA58sA1oxB5pb8i42RenoMdCsH8T86Gccb6Q,945
|
26
|
-
pywebexec-1.4.
|
27
|
-
pywebexec-1.4.
|
28
|
-
pywebexec-1.4.
|
29
|
-
pywebexec-1.4.
|
30
|
-
pywebexec-1.4.
|
31
|
-
pywebexec-1.4.
|
26
|
+
pywebexec-1.4.19.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
|
27
|
+
pywebexec-1.4.19.dist-info/METADATA,sha256=bAlOxVzchw1tY4ocEf-FriPyY990xvl3S1dIrb2vEb4,7801
|
28
|
+
pywebexec-1.4.19.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
29
|
+
pywebexec-1.4.19.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
|
30
|
+
pywebexec-1.4.19.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
|
31
|
+
pywebexec-1.4.19.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|