openshell-shared 0.1.2__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.
- api/__init__.py +1 -0
- api/manager/__init__.py +1 -0
- api/manager/v1/__init__.py +78 -0
- api/manager/v1/authentication.py +278 -0
- api/manager/v1/client.py +111 -0
- api/manager/v1/domains.py +64 -0
- api/manager/v1/entities.py +97 -0
- api/manager/v1/exceptions.py +98 -0
- api/manager/v1/identity.py +44 -0
- api/manager/v1/models.py +342 -0
- api/manager/v1/passports.py +131 -0
- api/manager/v1/sessions.py +83 -0
- api/manager/v1/transport.py +253 -0
- api/manager/v1/tunnels.py +120 -0
- cryptography/__init__.py +0 -0
- cryptography/certificate.py +390 -0
- cryptography/encoding.py +0 -0
- cryptography/identity.py +124 -0
- cryptography/keys.py +463 -0
- cryptography/signatures.py +63 -0
- cryptography/utils.py +0 -0
- domain/__init__.py +0 -0
- domain/domain.py +80 -0
- domain/membership.py +21 -0
- domain/permissions.py +14 -0
- domain/policies.py +2 -0
- identity/__init__.py +0 -0
- identity/identification.py +64 -0
- identity/store.py +150 -0
- modules/__init__.py +0 -0
- modules/shell/__init__.py +0 -0
- modules/shell/client.py +361 -0
- modules/shell/models.py +61 -0
- modules/shell/protocol.py +249 -0
- modules/shell/server.py +511 -0
- modules/shell/session.py +339 -0
- modules/utils.py +212 -0
- openshell_shared-0.1.2.dist-info/METADATA +59 -0
- openshell_shared-0.1.2.dist-info/RECORD +62 -0
- openshell_shared-0.1.2.dist-info/WHEEL +5 -0
- openshell_shared-0.1.2.dist-info/top_level.txt +7 -0
- protocols/__init__.py +0 -0
- protocols/negotiation/challenge.py +127 -0
- protocols/negotiation/models.py +28 -0
- standards/__init__.py +0 -0
- standards/certificates/__init__.py +0 -0
- standards/certificates/status.py +12 -0
- standards/certificates/types.py +11 -0
- standards/entities/__init__.py +0 -0
- standards/entities/types.py +14 -0
- standards/events/__init__.py +0 -0
- standards/events/schemas/__init__.py +0 -0
- standards/events/schemas/entity_registered.py +13 -0
- standards/events/types.py +18 -0
- standards/passports/__init__.py +0 -0
- standards/passports/types.py +5 -0
- standards/permissions/__init__.py +0 -0
- standards/permissions/types.py +14 -0
- standards/roles/__init__.py +0 -0
- standards/roles/types.py +8 -0
- standards/transports/__init__.py +0 -0
- standards/transports/types.py +24 -0
identity/store.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# shared/identity/store.py
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class IdentityStoreError(Exception):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class IdentityStore:
|
|
14
|
+
"""
|
|
15
|
+
Generic filesystem-based identity vault.
|
|
16
|
+
|
|
17
|
+
Responsibilities:
|
|
18
|
+
- persist identity blobs (public/private)
|
|
19
|
+
- load identity blobs
|
|
20
|
+
- export safe/public view
|
|
21
|
+
- ensure structural consistency
|
|
22
|
+
|
|
23
|
+
It does NOT:
|
|
24
|
+
- generate identities
|
|
25
|
+
- perform cryptographic operations
|
|
26
|
+
- enforce business rules
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, base_path: Path, namespace: str):
|
|
30
|
+
"""
|
|
31
|
+
Args:
|
|
32
|
+
base_path: root storage path (e.g. storage/)
|
|
33
|
+
namespace: entity namespace (ra, osam, agent, proxy, etc.)
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
self._base = Path(base_path)
|
|
37
|
+
self._namespace = namespace
|
|
38
|
+
|
|
39
|
+
self._root = self._base / namespace / "identity"
|
|
40
|
+
|
|
41
|
+
self._public_path = self._root / "profile.public.json"
|
|
42
|
+
self._private_path = self._root / "profile.private.json"
|
|
43
|
+
self._meta_path = self._root / "metadata.json"
|
|
44
|
+
|
|
45
|
+
self._ensure_structure()
|
|
46
|
+
|
|
47
|
+
# ---------------------------------------------------------
|
|
48
|
+
# INTERNAL
|
|
49
|
+
# ---------------------------------------------------------
|
|
50
|
+
def _ensure_structure(self):
|
|
51
|
+
self._root.mkdir(parents=True, exist_ok=True)
|
|
52
|
+
|
|
53
|
+
def _write_json(self, path: Path, data: Dict[str, Any]):
|
|
54
|
+
tmp_path = path.with_suffix(".tmp")
|
|
55
|
+
|
|
56
|
+
with open(tmp_path, "w") as f:
|
|
57
|
+
json.dump(data, f, indent=4)
|
|
58
|
+
|
|
59
|
+
tmp_path.replace(path)
|
|
60
|
+
|
|
61
|
+
def _read_json(self, path: Path) -> Dict[str, Any]:
|
|
62
|
+
if not path.exists():
|
|
63
|
+
raise IdentityStoreError(f"Missing identity file: {path}")
|
|
64
|
+
|
|
65
|
+
with open(path, "r") as f:
|
|
66
|
+
return json.load(f)
|
|
67
|
+
|
|
68
|
+
# ---------------------------------------------------------
|
|
69
|
+
# LIFECYCLE
|
|
70
|
+
# ---------------------------------------------------------
|
|
71
|
+
def exists(self) -> bool:
|
|
72
|
+
print("CWD:", os.getcwd())
|
|
73
|
+
print("PUBLIC:", self._public_path)
|
|
74
|
+
print("PRIVATE:", self._private_path)
|
|
75
|
+
|
|
76
|
+
print("PUBLIC EXISTS:", self._public_path.exists())
|
|
77
|
+
print("PRIVATE EXISTS:", self._private_path.exists())
|
|
78
|
+
|
|
79
|
+
return self._public_path.exists() and self._private_path.exists()
|
|
80
|
+
|
|
81
|
+
def save(
|
|
82
|
+
self,
|
|
83
|
+
public_profile: Dict[str, Any],
|
|
84
|
+
private_profile: Dict[str, Any],
|
|
85
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
86
|
+
) -> None:
|
|
87
|
+
"""
|
|
88
|
+
Persist identity bundle atomically.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
self._write_json(self._public_path, public_profile)
|
|
92
|
+
self._write_json(self._private_path, private_profile)
|
|
93
|
+
|
|
94
|
+
if metadata is not None:
|
|
95
|
+
self._write_json(self._meta_path, metadata)
|
|
96
|
+
|
|
97
|
+
def load(self) -> Dict[str, Any]:
|
|
98
|
+
"""
|
|
99
|
+
Load full identity bundle.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
"public": self._read_json(self._public_path),
|
|
104
|
+
"private": self._read_json(self._private_path),
|
|
105
|
+
"metadata": (
|
|
106
|
+
self._read_json(self._meta_path)
|
|
107
|
+
if self._meta_path.exists()
|
|
108
|
+
else None
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
def load_public(self) -> Dict[str, Any]:
|
|
113
|
+
return self._read_json(self._public_path)
|
|
114
|
+
|
|
115
|
+
def load_private(self) -> Dict[str, Any]:
|
|
116
|
+
return self._read_json(self._private_path)
|
|
117
|
+
|
|
118
|
+
# ---------------------------------------------------------
|
|
119
|
+
# EXPORT (SAFE)
|
|
120
|
+
# ---------------------------------------------------------
|
|
121
|
+
def export_public(self, export_path: Path) -> Path:
|
|
122
|
+
"""
|
|
123
|
+
Export only public identity (safe for sharing).
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
data = self.load_public()
|
|
127
|
+
|
|
128
|
+
export_path = Path(export_path)
|
|
129
|
+
export_path.mkdir(parents=True, exist_ok=True)
|
|
130
|
+
|
|
131
|
+
file_path = export_path / f"{self._namespace}.identity.public.json"
|
|
132
|
+
|
|
133
|
+
with open(file_path, "w") as f:
|
|
134
|
+
json.dump(data, f, indent=4)
|
|
135
|
+
|
|
136
|
+
return file_path
|
|
137
|
+
|
|
138
|
+
# ---------------------------------------------------------
|
|
139
|
+
# DELETE (optional, destructive)
|
|
140
|
+
# ---------------------------------------------------------
|
|
141
|
+
def delete(self) -> None:
|
|
142
|
+
"""
|
|
143
|
+
Remove identity completely.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
if self._root.exists():
|
|
147
|
+
for file in self._root.glob("*"):
|
|
148
|
+
file.unlink()
|
|
149
|
+
|
|
150
|
+
self._root.rmdir()
|
modules/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
modules/shell/client.py
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
# shell/client.py
|
|
2
|
+
|
|
3
|
+
# =====================================================
|
|
4
|
+
# LIBRARY IMPORTS
|
|
5
|
+
# =====================================================
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import tty
|
|
9
|
+
import termios
|
|
10
|
+
import asyncio
|
|
11
|
+
import traceback
|
|
12
|
+
|
|
13
|
+
from .protocol import ShellProtocol
|
|
14
|
+
from .models import (
|
|
15
|
+
ProtocolType,
|
|
16
|
+
ProtocolStandard,
|
|
17
|
+
ShellSignal
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# =====================================================
|
|
22
|
+
# SHELL CLIENT
|
|
23
|
+
# =====================================================
|
|
24
|
+
|
|
25
|
+
class ShellClient:
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
communication_handler,
|
|
30
|
+
auth_token: str,
|
|
31
|
+
tunnel_token: str,
|
|
32
|
+
session_token: str
|
|
33
|
+
):
|
|
34
|
+
|
|
35
|
+
self._communication_handler = (
|
|
36
|
+
communication_handler
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
self._auth_token = auth_token
|
|
40
|
+
self._tunnel_token = tunnel_token
|
|
41
|
+
self._session_token = session_token
|
|
42
|
+
|
|
43
|
+
self._is_running = False
|
|
44
|
+
|
|
45
|
+
self._shell_protocol = ShellProtocol(
|
|
46
|
+
auth_token=self._auth_token,
|
|
47
|
+
tunnel_token=self._tunnel_token,
|
|
48
|
+
session_token=self._session_token
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
self._stdin_fd = (
|
|
52
|
+
os.sys.stdin.fileno()
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
self._old_term = None
|
|
56
|
+
|
|
57
|
+
# =================================================
|
|
58
|
+
# START
|
|
59
|
+
# =================================================
|
|
60
|
+
|
|
61
|
+
async def start(self):
|
|
62
|
+
|
|
63
|
+
self._is_running = True
|
|
64
|
+
|
|
65
|
+
print(
|
|
66
|
+
"[SHELL] Opening PTY..."
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
await (
|
|
70
|
+
self._communication_handler
|
|
71
|
+
.send_datapackage(
|
|
72
|
+
self._shell_protocol.open()
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
self._enable_raw_mode()
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
|
|
80
|
+
receiver_task = (
|
|
81
|
+
asyncio.create_task(
|
|
82
|
+
self._receiver_loop()
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
sender_task = (
|
|
87
|
+
asyncio.create_task(
|
|
88
|
+
self._sender_loop()
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
done, pending = (
|
|
93
|
+
await asyncio.wait(
|
|
94
|
+
[
|
|
95
|
+
receiver_task,
|
|
96
|
+
sender_task
|
|
97
|
+
],
|
|
98
|
+
return_when=
|
|
99
|
+
asyncio.FIRST_COMPLETED
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
for task in pending:
|
|
104
|
+
|
|
105
|
+
task.cancel()
|
|
106
|
+
|
|
107
|
+
finally:
|
|
108
|
+
|
|
109
|
+
self._disable_raw_mode()
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
|
|
113
|
+
await (
|
|
114
|
+
self._communication_handler
|
|
115
|
+
.send_datapackage(
|
|
116
|
+
self._shell_protocol.close()
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
except Exception:
|
|
121
|
+
|
|
122
|
+
pass
|
|
123
|
+
|
|
124
|
+
self._is_running = False
|
|
125
|
+
|
|
126
|
+
print(
|
|
127
|
+
"\n[SHELL] Closed"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# =================================================
|
|
131
|
+
# RAW MODE
|
|
132
|
+
# =================================================
|
|
133
|
+
|
|
134
|
+
def _enable_raw_mode(self):
|
|
135
|
+
|
|
136
|
+
self._old_term = (
|
|
137
|
+
termios.tcgetattr(
|
|
138
|
+
self._stdin_fd
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
tty.setraw(
|
|
143
|
+
self._stdin_fd
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
def _disable_raw_mode(self):
|
|
147
|
+
|
|
148
|
+
if self._old_term:
|
|
149
|
+
|
|
150
|
+
termios.tcsetattr(
|
|
151
|
+
self._stdin_fd,
|
|
152
|
+
termios.TCSADRAIN,
|
|
153
|
+
self._old_term
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# =================================================
|
|
157
|
+
# RECEIVER
|
|
158
|
+
# =================================================
|
|
159
|
+
|
|
160
|
+
async def _receiver_loop(self):
|
|
161
|
+
|
|
162
|
+
while self._is_running:
|
|
163
|
+
|
|
164
|
+
try:
|
|
165
|
+
|
|
166
|
+
packet = await (
|
|
167
|
+
self._communication_handler
|
|
168
|
+
.receive_datapackage()
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
if not isinstance(
|
|
172
|
+
packet,
|
|
173
|
+
dict
|
|
174
|
+
):
|
|
175
|
+
continue
|
|
176
|
+
|
|
177
|
+
payload = packet.get(
|
|
178
|
+
"payload",
|
|
179
|
+
{}
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
if (
|
|
183
|
+
payload.get(
|
|
184
|
+
"protocol"
|
|
185
|
+
)
|
|
186
|
+
!=
|
|
187
|
+
ProtocolStandard
|
|
188
|
+
.SHELL
|
|
189
|
+
.value
|
|
190
|
+
):
|
|
191
|
+
continue
|
|
192
|
+
|
|
193
|
+
if (
|
|
194
|
+
payload.get(
|
|
195
|
+
"protocol_type"
|
|
196
|
+
)
|
|
197
|
+
!=
|
|
198
|
+
ProtocolType
|
|
199
|
+
.SERVER
|
|
200
|
+
.value
|
|
201
|
+
):
|
|
202
|
+
continue
|
|
203
|
+
|
|
204
|
+
event = payload.get(
|
|
205
|
+
"event"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# -------------------------
|
|
209
|
+
# OUTPUT
|
|
210
|
+
# -------------------------
|
|
211
|
+
|
|
212
|
+
if event == "OUTPUT":
|
|
213
|
+
|
|
214
|
+
data = payload.get(
|
|
215
|
+
"data",
|
|
216
|
+
""
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
if data:
|
|
220
|
+
|
|
221
|
+
print(
|
|
222
|
+
data,
|
|
223
|
+
end="",
|
|
224
|
+
flush=True
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
continue
|
|
228
|
+
|
|
229
|
+
# -------------------------
|
|
230
|
+
# EXIT
|
|
231
|
+
# -------------------------
|
|
232
|
+
|
|
233
|
+
if event == "EXIT":
|
|
234
|
+
|
|
235
|
+
code = payload.get(
|
|
236
|
+
"return_code",
|
|
237
|
+
0
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
print(
|
|
241
|
+
f"\n\r"
|
|
242
|
+
f"[REMOTE EXIT {code}]"
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
self.stop()
|
|
246
|
+
|
|
247
|
+
break
|
|
248
|
+
|
|
249
|
+
except asyncio.CancelledError:
|
|
250
|
+
|
|
251
|
+
break
|
|
252
|
+
|
|
253
|
+
except Exception:
|
|
254
|
+
|
|
255
|
+
print(
|
|
256
|
+
"\n[SHELL-CLIENT] "
|
|
257
|
+
"Receiver exception:"
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
traceback.print_exc()
|
|
261
|
+
|
|
262
|
+
self.stop()
|
|
263
|
+
|
|
264
|
+
break
|
|
265
|
+
|
|
266
|
+
# =================================================
|
|
267
|
+
# SENDER
|
|
268
|
+
# =================================================
|
|
269
|
+
|
|
270
|
+
async def _sender_loop(self):
|
|
271
|
+
|
|
272
|
+
loop = (
|
|
273
|
+
asyncio.get_running_loop()
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
while self._is_running:
|
|
277
|
+
|
|
278
|
+
try:
|
|
279
|
+
|
|
280
|
+
data = await (
|
|
281
|
+
loop.run_in_executor(
|
|
282
|
+
None,
|
|
283
|
+
os.read,
|
|
284
|
+
self._stdin_fd,
|
|
285
|
+
1024
|
|
286
|
+
)
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
if not data:
|
|
290
|
+
continue
|
|
291
|
+
|
|
292
|
+
# -------------------------
|
|
293
|
+
# CTRL+C
|
|
294
|
+
# -------------------------
|
|
295
|
+
|
|
296
|
+
if data == b"\x03":
|
|
297
|
+
|
|
298
|
+
await (
|
|
299
|
+
self._communication_handler
|
|
300
|
+
.send_datapackage(
|
|
301
|
+
self._shell_protocol.signal(
|
|
302
|
+
ShellSignal
|
|
303
|
+
.SIGINT
|
|
304
|
+
.value
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
continue
|
|
310
|
+
|
|
311
|
+
# -------------------------
|
|
312
|
+
# CTRL+D
|
|
313
|
+
# -------------------------
|
|
314
|
+
|
|
315
|
+
if data == b"\x04":
|
|
316
|
+
|
|
317
|
+
self.stop()
|
|
318
|
+
|
|
319
|
+
break
|
|
320
|
+
|
|
321
|
+
# -------------------------
|
|
322
|
+
# INPUT
|
|
323
|
+
# -------------------------
|
|
324
|
+
|
|
325
|
+
await (
|
|
326
|
+
self._communication_handler
|
|
327
|
+
.send_datapackage(
|
|
328
|
+
self._shell_protocol.input(
|
|
329
|
+
data.decode(
|
|
330
|
+
errors="ignore"
|
|
331
|
+
)
|
|
332
|
+
)
|
|
333
|
+
)
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
except asyncio.CancelledError:
|
|
337
|
+
|
|
338
|
+
break
|
|
339
|
+
|
|
340
|
+
except Exception:
|
|
341
|
+
|
|
342
|
+
print(
|
|
343
|
+
"\n[SHELL-CLIENT] "
|
|
344
|
+
"Sender exception:"
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
traceback.print_exc()
|
|
348
|
+
|
|
349
|
+
self.stop()
|
|
350
|
+
|
|
351
|
+
break
|
|
352
|
+
|
|
353
|
+
# =================================================
|
|
354
|
+
# STOP
|
|
355
|
+
# =================================================
|
|
356
|
+
|
|
357
|
+
def stop(self):
|
|
358
|
+
|
|
359
|
+
self._is_running = False
|
|
360
|
+
|
|
361
|
+
return True
|
modules/shell/models.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# shell/models.py
|
|
2
|
+
|
|
3
|
+
# =====================================================
|
|
4
|
+
# LIBRARY IMPORTS
|
|
5
|
+
# =====================================================
|
|
6
|
+
|
|
7
|
+
from enum import Enum
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# =====================================================
|
|
11
|
+
# PROTOCOL TYPE
|
|
12
|
+
# =====================================================
|
|
13
|
+
|
|
14
|
+
class ProtocolType(str, Enum):
|
|
15
|
+
|
|
16
|
+
CLIENT = "CLIENT"
|
|
17
|
+
SERVER = "SERVER"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# =====================================================
|
|
21
|
+
# PROTOCOL STANDARD
|
|
22
|
+
# =====================================================
|
|
23
|
+
|
|
24
|
+
class ProtocolStandard(str, Enum):
|
|
25
|
+
|
|
26
|
+
SHELL = "SHELL"
|
|
27
|
+
FTP = "FTP"
|
|
28
|
+
SPECIAL = "SPECIAL"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# =====================================================
|
|
32
|
+
# SHELL EVENTS
|
|
33
|
+
# =====================================================
|
|
34
|
+
|
|
35
|
+
class ShellEvent(str, Enum):
|
|
36
|
+
|
|
37
|
+
OPEN = "OPEN"
|
|
38
|
+
|
|
39
|
+
INPUT = "INPUT"
|
|
40
|
+
|
|
41
|
+
OUTPUT = "OUTPUT"
|
|
42
|
+
|
|
43
|
+
RESIZE = "RESIZE"
|
|
44
|
+
|
|
45
|
+
SIGNAL = "SIGNAL"
|
|
46
|
+
|
|
47
|
+
CLOSE = "CLOSE"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# =====================================================
|
|
51
|
+
# SHELL SIGNALS
|
|
52
|
+
# =====================================================
|
|
53
|
+
|
|
54
|
+
class ShellSignal(str, Enum):
|
|
55
|
+
|
|
56
|
+
SIGINT = "SIGINT"
|
|
57
|
+
SIGTERM = "SIGTERM"
|
|
58
|
+
SIGKILL = "SIGKILL"
|
|
59
|
+
SIGHUP = "SIGHUP"
|
|
60
|
+
SIGQUIT = "SIGQUIT"
|
|
61
|
+
SIGTSTP = "SIGTSTP"
|