pywebexec 0.0.6__tar.gz → 0.0.8__tar.gz
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-0.0.6/pywebexec.egg-info → pywebexec-0.0.8}/PKG-INFO +3 -2
- {pywebexec-0.0.6 → pywebexec-0.0.8}/README.md +1 -1
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pyproject.toml +2 -1
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pywebexec/pywebexec.py +79 -5
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pywebexec/version.py +2 -2
- {pywebexec-0.0.6 → pywebexec-0.0.8/pywebexec.egg-info}/PKG-INFO +3 -2
- pywebexec-0.0.8/pywebexec.egg-info/entry_points.txt +2 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pywebexec.egg-info/requires.txt +1 -0
- pywebexec-0.0.6/pywebexec.egg-info/entry_points.txt +0 -2
- {pywebexec-0.0.6 → pywebexec-0.0.8}/.github/workflows/python-publish.yml +0 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/.gitignore +0 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/LICENSE +0 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pywebexec/__init__.py +0 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pywebexec/static/images/aborted.svg +0 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pywebexec/static/images/copy.svg +0 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pywebexec/static/images/copy_ok.svg +0 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pywebexec/static/images/failed.svg +0 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pywebexec/static/images/running.svg +0 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pywebexec/static/images/success.svg +0 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pywebexec/templates/__init__.py +0 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pywebexec/templates/index.html +0 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pywebexec.egg-info/SOURCES.txt +0 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pywebexec.egg-info/dependency_links.txt +0 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/pywebexec.egg-info/top_level.txt +0 -0
- {pywebexec-0.0.6 → pywebexec-0.0.8}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: pywebexec
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.8
|
|
4
4
|
Summary: Simple Python HTTP Exec Server
|
|
5
5
|
Home-page: https://github.com/joknarf/pywebexec
|
|
6
6
|
Author: Franck Jouvanceau
|
|
@@ -53,6 +53,7 @@ Classifier: Topic :: System :: Systems Administration
|
|
|
53
53
|
Requires-Python: >=3.6
|
|
54
54
|
Description-Content-Type: text/markdown
|
|
55
55
|
License-File: LICENSE
|
|
56
|
+
Requires-Dist: python-daemon>=2.3.2
|
|
56
57
|
Requires-Dist: cryptography>=40.0.2
|
|
57
58
|
Requires-Dist: Flask>=3.0.3
|
|
58
59
|
Requires-Dist: Flask-HTTPAuth>=4.8.0
|
|
@@ -127,5 +128,5 @@ $ pywebexec start
|
|
|
127
128
|
$ pywebexec status
|
|
128
129
|
$ pywebexec stop
|
|
129
130
|
```
|
|
130
|
-
* log of server are stored in
|
|
131
|
+
* log of server are stored in directory `[.config/].pywebexec/pywebexec_<listen>:<port>.log`
|
|
131
132
|
|
|
@@ -9,6 +9,7 @@ maintainers = [
|
|
|
9
9
|
|
|
10
10
|
description = "Simple Python HTTP Exec Server"
|
|
11
11
|
dependencies = [
|
|
12
|
+
"python-daemon>=2.3.2",
|
|
12
13
|
"cryptography>=40.0.2",
|
|
13
14
|
"Flask>=3.0.3",
|
|
14
15
|
"Flask-HTTPAuth>=4.8.0",
|
|
@@ -53,4 +54,4 @@ build-backend = "setuptools.build_meta"
|
|
|
53
54
|
version_file = "pywebexec/version.py"
|
|
54
55
|
|
|
55
56
|
[project.scripts]
|
|
56
|
-
pywebexec = "pywebexec.pywebexec:
|
|
57
|
+
pywebexec = "pywebexec.pywebexec:main"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import sys
|
|
1
2
|
from flask import Flask, request, jsonify, render_template
|
|
2
3
|
from flask_httpauth import HTTPBasicAuth
|
|
3
4
|
import subprocess
|
|
@@ -10,13 +11,17 @@ import random
|
|
|
10
11
|
import string
|
|
11
12
|
from datetime import datetime
|
|
12
13
|
import shlex
|
|
13
|
-
from gunicorn.app.base import BaseApplication
|
|
14
|
+
from gunicorn.app.base import BaseApplication, Application
|
|
14
15
|
|
|
15
16
|
app = Flask(__name__)
|
|
16
17
|
auth = HTTPBasicAuth()
|
|
17
18
|
|
|
18
19
|
# Directory to store the script status and output
|
|
19
20
|
SCRIPT_STATUS_DIR = '.web_status'
|
|
21
|
+
CONFDIR = os.path.expanduser("~/")
|
|
22
|
+
if os.path.isdir(f"{CONFDIR}/.config"):
|
|
23
|
+
CONFDIR += '/.config'
|
|
24
|
+
CONFDIR += "/.pywebexec"
|
|
20
25
|
|
|
21
26
|
if not os.path.exists(SCRIPT_STATUS_DIR):
|
|
22
27
|
os.makedirs(SCRIPT_STATUS_DIR)
|
|
@@ -25,7 +30,7 @@ def generate_random_password(length=12):
|
|
|
25
30
|
characters = string.ascii_letters + string.digits + string.punctuation
|
|
26
31
|
return ''.join(random.choice(characters) for i in range(length))
|
|
27
32
|
|
|
28
|
-
class StandaloneApplication(
|
|
33
|
+
class StandaloneApplication(Application):
|
|
29
34
|
|
|
30
35
|
def __init__(self, app, options=None):
|
|
31
36
|
self.options = options or {}
|
|
@@ -44,16 +49,65 @@ class StandaloneApplication(BaseApplication):
|
|
|
44
49
|
return self.application
|
|
45
50
|
|
|
46
51
|
|
|
47
|
-
def start_gunicorn():
|
|
52
|
+
def start_gunicorn(daemon=False, baselog=None):
|
|
53
|
+
if daemon:
|
|
54
|
+
errorlog = f"{baselog}.log"
|
|
55
|
+
accesslog = None # f"{baselog}.access.log"
|
|
56
|
+
pidfile = f"{baselog}.pid"
|
|
57
|
+
else:
|
|
58
|
+
errorlog = "-"
|
|
59
|
+
accesslog = "-"
|
|
60
|
+
pidfile = None
|
|
48
61
|
options = {
|
|
49
62
|
'bind': '%s:%s' % (args.listen, args.port),
|
|
50
63
|
'workers': 4,
|
|
51
64
|
'timeout': 600,
|
|
52
65
|
'certfile': args.cert,
|
|
53
66
|
'keyfile': args.key,
|
|
67
|
+
'daemon': daemon,
|
|
68
|
+
'errorlog': errorlog,
|
|
69
|
+
'accesslog': accesslog,
|
|
70
|
+
'pidfile': pidfile,
|
|
54
71
|
}
|
|
55
72
|
StandaloneApplication(app, options=options).run()
|
|
56
73
|
|
|
74
|
+
def daemon_d(action, pidfilepath, hostname=None, args=None):
|
|
75
|
+
"""start/stop daemon"""
|
|
76
|
+
import signal
|
|
77
|
+
import daemon, daemon.pidfile
|
|
78
|
+
|
|
79
|
+
pidfile = daemon.pidfile.TimeoutPIDLockFile(pidfilepath+".pid", acquire_timeout=30)
|
|
80
|
+
if action == "stop":
|
|
81
|
+
if pidfile.is_locked():
|
|
82
|
+
pid = pidfile.read_pid()
|
|
83
|
+
print(f"Stopping server pid {pid}")
|
|
84
|
+
try:
|
|
85
|
+
os.kill(pid, signal.SIGINT)
|
|
86
|
+
except:
|
|
87
|
+
return False
|
|
88
|
+
return True
|
|
89
|
+
elif action == "status":
|
|
90
|
+
status = pidfile.is_locked()
|
|
91
|
+
if status:
|
|
92
|
+
print(f"pywebexec running pid {pidfile.read_pid()}")
|
|
93
|
+
return True
|
|
94
|
+
print("pywebexec not running")
|
|
95
|
+
return False
|
|
96
|
+
elif action == "start":
|
|
97
|
+
print(f"Starting server")
|
|
98
|
+
log = open(pidfilepath + ".log", "ab+")
|
|
99
|
+
daemon_context = daemon.DaemonContext(
|
|
100
|
+
stderr=log,
|
|
101
|
+
pidfile=pidfile,
|
|
102
|
+
umask=0o077,
|
|
103
|
+
working_directory=os.getcwd(),
|
|
104
|
+
)
|
|
105
|
+
with daemon_context:
|
|
106
|
+
try:
|
|
107
|
+
start_gunicorn()
|
|
108
|
+
except Exception as e:
|
|
109
|
+
print(e)
|
|
110
|
+
|
|
57
111
|
def parseargs():
|
|
58
112
|
global app, args
|
|
59
113
|
parser = argparse.ArgumentParser(description='Run the script execution server.')
|
|
@@ -77,8 +131,18 @@ def parseargs():
|
|
|
77
131
|
)
|
|
78
132
|
parser.add_argument("-c", "--cert", type=str, help="Path to https certificate")
|
|
79
133
|
parser.add_argument("-k", "--key", type=str, help="Path to https certificate key")
|
|
134
|
+
parser.add_argument("action", nargs="?", help="daemon action start/stop/restart/status", choices=["start","stop","restart","status"])
|
|
80
135
|
|
|
81
136
|
args = parser.parse_args()
|
|
137
|
+
if os.path.isdir(args.dir):
|
|
138
|
+
try:
|
|
139
|
+
os.chdir(args.dir)
|
|
140
|
+
except OSError:
|
|
141
|
+
print(f"Error: cannot chdir {args.dir}", file=sys.stderr)
|
|
142
|
+
sys.exit(1)
|
|
143
|
+
else:
|
|
144
|
+
print(f"Error: {args.dir} not found", file=sys.stderr)
|
|
145
|
+
sys.exit(1)
|
|
82
146
|
|
|
83
147
|
if args.user:
|
|
84
148
|
app.config['USER'] = args.user
|
|
@@ -281,6 +345,16 @@ def list_executables():
|
|
|
281
345
|
def verify_password(username, password):
|
|
282
346
|
return username == app.config['USER'] and password == app.config['PASSWORD']
|
|
283
347
|
|
|
348
|
+
def main():
|
|
349
|
+
basef = f"{CONFDIR}/pywebexec_{args.listen}:{args.port}"
|
|
350
|
+
if not os.path.exists(CONFDIR):
|
|
351
|
+
os.mkdir(CONFDIR, mode=0o700)
|
|
352
|
+
if args.action == "start":
|
|
353
|
+
return start_gunicorn(daemon=True, baselog=basef)
|
|
354
|
+
if args.action:
|
|
355
|
+
return daemon_d(args.action, pidfilepath=basef)
|
|
356
|
+
return start_gunicorn()
|
|
357
|
+
|
|
284
358
|
if __name__ == '__main__':
|
|
285
|
-
|
|
286
|
-
#app.run(host='0.0.0.0', port=5000)
|
|
359
|
+
main()
|
|
360
|
+
# app.run(host='0.0.0.0', port=5000)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: pywebexec
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.8
|
|
4
4
|
Summary: Simple Python HTTP Exec Server
|
|
5
5
|
Home-page: https://github.com/joknarf/pywebexec
|
|
6
6
|
Author: Franck Jouvanceau
|
|
@@ -53,6 +53,7 @@ Classifier: Topic :: System :: Systems Administration
|
|
|
53
53
|
Requires-Python: >=3.6
|
|
54
54
|
Description-Content-Type: text/markdown
|
|
55
55
|
License-File: LICENSE
|
|
56
|
+
Requires-Dist: python-daemon>=2.3.2
|
|
56
57
|
Requires-Dist: cryptography>=40.0.2
|
|
57
58
|
Requires-Dist: Flask>=3.0.3
|
|
58
59
|
Requires-Dist: Flask-HTTPAuth>=4.8.0
|
|
@@ -127,5 +128,5 @@ $ pywebexec start
|
|
|
127
128
|
$ pywebexec status
|
|
128
129
|
$ pywebexec stop
|
|
129
130
|
```
|
|
130
|
-
* log of server are stored in
|
|
131
|
+
* log of server are stored in directory `[.config/].pywebexec/pywebexec_<listen>:<port>.log`
|
|
131
132
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|