atomicshop 2.11.47__py3-none-any.whl → 3.10.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.
- atomicshop/__init__.py +1 -1
- atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
- atomicshop/a_mains/addons/process_list/compile.cmd +7 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +8 -1
- atomicshop/a_mains/dns_gateway_setting.py +11 -0
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/github_wrapper.py +11 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/{addons/mains → a_mains}/msi_unpacker.py +3 -1
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/a_mains/update_config_toml.py +38 -0
- atomicshop/appointment_management.py +5 -3
- atomicshop/basics/ansi_escape_codes.py +3 -1
- atomicshop/basics/argparse_template.py +2 -0
- atomicshop/basics/booleans.py +27 -30
- atomicshop/basics/bytes_arrays.py +43 -0
- atomicshop/basics/classes.py +149 -1
- atomicshop/basics/dicts.py +12 -0
- atomicshop/basics/enums.py +2 -2
- atomicshop/basics/exceptions.py +5 -1
- atomicshop/basics/list_of_classes.py +29 -0
- atomicshop/basics/list_of_dicts.py +69 -5
- atomicshop/basics/lists.py +14 -0
- atomicshop/basics/multiprocesses.py +374 -50
- atomicshop/basics/package_module.py +10 -0
- atomicshop/basics/strings.py +160 -7
- atomicshop/basics/threads.py +14 -0
- atomicshop/basics/tracebacks.py +13 -4
- atomicshop/certificates.py +153 -52
- atomicshop/config_init.py +12 -7
- atomicshop/console_user_response.py +7 -14
- atomicshop/consoles.py +9 -0
- atomicshop/datetimes.py +98 -0
- atomicshop/diff_check.py +340 -40
- atomicshop/dns.py +128 -12
- atomicshop/etws/_pywintrace_fix.py +17 -0
- atomicshop/etws/const.py +38 -0
- atomicshop/etws/providers.py +21 -0
- atomicshop/etws/sessions.py +43 -0
- atomicshop/etws/trace.py +168 -0
- atomicshop/etws/traces/trace_dns.py +162 -0
- atomicshop/etws/traces/trace_sysmon_process_creation.py +126 -0
- atomicshop/etws/traces/trace_tcp.py +130 -0
- atomicshop/file_io/csvs.py +222 -24
- atomicshop/file_io/docxs.py +35 -18
- atomicshop/file_io/file_io.py +35 -19
- atomicshop/file_io/jsons.py +49 -0
- atomicshop/file_io/tomls.py +139 -0
- atomicshop/filesystem.py +864 -293
- atomicshop/get_process_list.py +133 -0
- atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +52 -19
- atomicshop/http_parse.py +149 -93
- atomicshop/ip_addresses.py +6 -1
- atomicshop/mitm/centered_settings.py +132 -0
- atomicshop/mitm/config_static.py +207 -0
- atomicshop/mitm/config_toml_editor.py +55 -0
- atomicshop/mitm/connection_thread_worker.py +875 -357
- atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
- atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
- atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
- atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
- atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
- atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
- atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
- atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
- atomicshop/mitm/engines/create_module_template.py +58 -14
- atomicshop/mitm/import_config.py +359 -139
- atomicshop/mitm/initialize_engines.py +160 -74
- atomicshop/mitm/message.py +64 -23
- atomicshop/mitm/mitm_main.py +892 -0
- atomicshop/mitm/recs_files.py +183 -0
- atomicshop/mitm/shared_functions.py +4 -10
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +257 -166
- atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py +136 -0
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +525 -0
- atomicshop/monitor/change_monitor.py +96 -120
- atomicshop/monitor/checks/dns.py +139 -70
- atomicshop/monitor/checks/file.py +77 -0
- atomicshop/monitor/checks/network.py +81 -77
- atomicshop/monitor/checks/process_running.py +33 -34
- atomicshop/monitor/checks/url.py +94 -0
- atomicshop/networks.py +671 -0
- atomicshop/on_exit.py +205 -0
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/permissions.py +22 -0
- atomicshop/permissions/ubuntu_permissions.py +239 -0
- atomicshop/permissions/win_permissions.py +33 -0
- atomicshop/print_api.py +24 -41
- atomicshop/process.py +63 -17
- atomicshop/process_poller/__init__.py +0 -0
- atomicshop/process_poller/pollers/__init__.py +0 -0
- atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
- atomicshop/process_poller/process_pool.py +207 -0
- atomicshop/process_poller/simple_process_pool.py +311 -0
- atomicshop/process_poller/tracer_base.py +45 -0
- atomicshop/process_poller/tracers/__init__.py +0 -0
- atomicshop/process_poller/tracers/event_log.py +46 -0
- atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
- atomicshop/python_file_patcher.py +1 -1
- atomicshop/python_functions.py +27 -75
- atomicshop/question_answer_engine.py +2 -2
- atomicshop/scheduling.py +24 -5
- atomicshop/sound.py +4 -2
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +158 -172
- atomicshop/startup/__init__.py +0 -0
- atomicshop/startup/win/__init__.py +0 -0
- atomicshop/startup/win/startup_folder.py +53 -0
- atomicshop/startup/win/task_scheduler.py +119 -0
- atomicshop/system_resource_monitor.py +61 -46
- atomicshop/system_resources.py +8 -8
- atomicshop/tempfiles.py +1 -2
- atomicshop/timer.py +30 -11
- atomicshop/urls.py +41 -0
- atomicshop/venvs.py +28 -0
- atomicshop/versioning.py +27 -0
- atomicshop/web.py +110 -25
- atomicshop/web_apis/__init__.py +0 -0
- atomicshop/web_apis/google_custom_search.py +44 -0
- atomicshop/web_apis/google_llm.py +188 -0
- atomicshop/websocket_parse.py +450 -0
- atomicshop/wrappers/certauthw/certauth.py +1 -0
- atomicshop/wrappers/cryptographyw.py +29 -8
- atomicshop/wrappers/ctyping/etw_winapi/__init__.py +0 -0
- atomicshop/wrappers/ctyping/etw_winapi/const.py +335 -0
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +393 -0
- atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +13 -9
- atomicshop/wrappers/ctyping/msi_windows_installer/tables.py +35 -0
- atomicshop/wrappers/ctyping/setup_device.py +466 -0
- atomicshop/wrappers/ctyping/win_console.py +39 -0
- atomicshop/wrappers/dockerw/dockerw.py +113 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
- atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
- atomicshop/wrappers/factw/get_file_data.py +12 -5
- atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
- atomicshop/wrappers/factw/postgresql/firmware.py +4 -6
- atomicshop/wrappers/githubw.py +583 -51
- atomicshop/wrappers/loggingw/consts.py +49 -0
- atomicshop/wrappers/loggingw/filters.py +102 -0
- atomicshop/wrappers/loggingw/formatters.py +58 -71
- atomicshop/wrappers/loggingw/handlers.py +459 -40
- atomicshop/wrappers/loggingw/loggers.py +19 -0
- atomicshop/wrappers/loggingw/loggingw.py +1010 -178
- atomicshop/wrappers/loggingw/reading.py +344 -19
- atomicshop/wrappers/mongodbw/__init__.py +0 -0
- atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
- atomicshop/wrappers/mongodbw/mongodbw.py +1432 -0
- atomicshop/wrappers/netshw.py +271 -0
- atomicshop/wrappers/playwrightw/engine.py +34 -19
- atomicshop/wrappers/playwrightw/infra.py +5 -0
- atomicshop/wrappers/playwrightw/javascript.py +7 -3
- atomicshop/wrappers/playwrightw/keyboard.py +14 -0
- atomicshop/wrappers/playwrightw/scenarios.py +172 -5
- atomicshop/wrappers/playwrightw/waits.py +9 -7
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/processes.py +81 -0
- atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
- atomicshop/wrappers/psutilw/psutilw.py +9 -0
- atomicshop/wrappers/pyopensslw.py +9 -2
- atomicshop/wrappers/pywin32w/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/cert_store.py +116 -0
- atomicshop/wrappers/pywin32w/console.py +34 -0
- atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +212 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +57 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +49 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py +97 -0
- atomicshop/wrappers/pywin32w/winshell.py +19 -0
- atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
- atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
- atomicshop/wrappers/socketw/accepter.py +21 -7
- atomicshop/wrappers/socketw/certificator.py +216 -150
- atomicshop/wrappers/socketw/creator.py +190 -50
- atomicshop/wrappers/socketw/dns_server.py +500 -173
- atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +144 -102
- atomicshop/wrappers/socketw/sender.py +65 -35
- atomicshop/wrappers/socketw/sni.py +334 -165
- atomicshop/wrappers/socketw/socket_base.py +134 -0
- atomicshop/wrappers/socketw/socket_client.py +137 -95
- atomicshop/wrappers/socketw/socket_server_tester.py +14 -9
- atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
- atomicshop/wrappers/socketw/ssl_base.py +15 -14
- atomicshop/wrappers/socketw/statistics_csv.py +148 -17
- atomicshop/wrappers/sysmonw.py +157 -0
- atomicshop/wrappers/ubuntu_terminal.py +65 -26
- atomicshop/wrappers/win_auditw.py +189 -0
- atomicshop/wrappers/winregw/__init__.py +0 -0
- atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
- atomicshop/wrappers/winregw/winreg_network.py +232 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -49
- atomicshop-3.10.5.dist-info/RECORD +306 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
- atomicshop/_basics_temp.py +0 -101
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
- atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
- atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
- atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
- atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
- atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
- atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
- atomicshop/addons/package_setup/Setup.cmd +0 -7
- atomicshop/addons/process_list/compile.cmd +0 -2
- atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/archiver.py +0 -34
- atomicshop/archiver/search_in_archive.py +0 -250
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -44
- atomicshop/archiver/zips.py +0 -293
- atomicshop/etw/dns_trace.py +0 -118
- atomicshop/etw/etw.py +0 -61
- atomicshop/file_types.py +0 -24
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- atomicshop/mitm/initialize_mitm_server.py +0 -240
- atomicshop/monitor/checks/hash.py +0 -44
- atomicshop/monitor/checks/hash_checks/file.py +0 -55
- atomicshop/monitor/checks/hash_checks/url.py +0 -62
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/permissions.py +0 -110
- atomicshop/process_poller.py +0 -237
- atomicshop/script_as_string_processor.py +0 -38
- atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
- atomicshop/ssh_scripts/process_from_port.py +0 -27
- atomicshop/wrappers/_process_wrapper_curl.py +0 -27
- atomicshop/wrappers/_process_wrapper_tar.py +0 -21
- atomicshop/wrappers/dockerw/install_docker.py +0 -209
- atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/loggingw/checks.py +0 -20
- atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/base.py +0 -59
- atomicshop/wrappers/socketw/get_process.py +0 -107
- atomicshop/wrappers/wslw.py +0 -191
- atomicshop-2.11.47.dist-info/RECORD +0 -251
- /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons/mains → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
- /atomicshop/{addons/mains → a_mains}/search_for_hyperlinks_in_docx.py +0 -0
- /atomicshop/{archiver → etws}/__init__.py +0 -0
- /atomicshop/{etw → etws/traces}/__init__.py +0 -0
- /atomicshop/{monitor/checks/hash_checks → mitm/statistic_analyzer_helper}/__init__.py +0 -0
- /atomicshop/{wrappers/nodejsw → permissions}/__init__.py +0 -0
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
atomicshop/on_exit.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import atexit
|
|
2
|
+
import signal
|
|
3
|
+
import sys
|
|
4
|
+
import platform
|
|
5
|
+
|
|
6
|
+
import win32api
|
|
7
|
+
import win32con
|
|
8
|
+
|
|
9
|
+
from .print_api import print_api
|
|
10
|
+
from .wrappers.pywin32w import console
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
EXIT_HANDLER_INSTANCE = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ExitHandler:
|
|
17
|
+
"""
|
|
18
|
+
This class is used to handle exit events: Closing the console, pressing 'CTRL+C', Killing the process.
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
from atomicshop import on_exit
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def clean_up_function():
|
|
25
|
+
print("Cleaning up.")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
on_exit.ExitHandler(clean_up_function).register_handlers()
|
|
29
|
+
|
|
30
|
+
# OR
|
|
31
|
+
on_exit.register_exit_handler(clean_up_function)
|
|
32
|
+
"""
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
cleanup_action: callable,
|
|
36
|
+
args: tuple = None,
|
|
37
|
+
kwargs: dict = None
|
|
38
|
+
):
|
|
39
|
+
"""
|
|
40
|
+
:param cleanup_action: The action to run when one of exit types is triggered.
|
|
41
|
+
:param args: The arguments to pass to the cleanup action.
|
|
42
|
+
:param kwargs: The keyword arguments to pass to the cleanup action.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
if not callable(cleanup_action):
|
|
46
|
+
raise ValueError("The 'cleanup_action' must be a callable function.")
|
|
47
|
+
|
|
48
|
+
if args is None:
|
|
49
|
+
args = tuple()
|
|
50
|
+
|
|
51
|
+
if kwargs is None:
|
|
52
|
+
kwargs = dict()
|
|
53
|
+
|
|
54
|
+
self.cleanup_action: callable = cleanup_action
|
|
55
|
+
self.args: tuple = args
|
|
56
|
+
self.kwargs: dict = kwargs
|
|
57
|
+
|
|
58
|
+
self._called: bool = False
|
|
59
|
+
self._handler_hit: bool = False
|
|
60
|
+
|
|
61
|
+
def _run_cleanup(self):
|
|
62
|
+
if not self._called:
|
|
63
|
+
self._called = True
|
|
64
|
+
self.cleanup_action(*self.args, **self.kwargs)
|
|
65
|
+
|
|
66
|
+
def console_handler(self, event):
|
|
67
|
+
if event == win32con.CTRL_CLOSE_EVENT:
|
|
68
|
+
|
|
69
|
+
if not self._handler_hit:
|
|
70
|
+
self._handler_hit = True
|
|
71
|
+
|
|
72
|
+
print("Console close event.")
|
|
73
|
+
self._run_cleanup()
|
|
74
|
+
|
|
75
|
+
return True
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
def atexit_handler(self):
|
|
79
|
+
if not self._handler_hit:
|
|
80
|
+
self._handler_hit = True
|
|
81
|
+
|
|
82
|
+
print("atexit_handler")
|
|
83
|
+
self._run_cleanup()
|
|
84
|
+
|
|
85
|
+
def signal_handler(self, signum, frame):
|
|
86
|
+
if not self._handler_hit:
|
|
87
|
+
self._handler_hit = True
|
|
88
|
+
|
|
89
|
+
print_api(f"Signal {signum}")
|
|
90
|
+
self._run_cleanup()
|
|
91
|
+
# Exit the process gracefully
|
|
92
|
+
raise SystemExit(0)
|
|
93
|
+
|
|
94
|
+
def register_handlers(
|
|
95
|
+
self,
|
|
96
|
+
at_exit: bool = True,
|
|
97
|
+
console_close: bool = True,
|
|
98
|
+
kill_signal: bool = True
|
|
99
|
+
):
|
|
100
|
+
"""
|
|
101
|
+
Register the exit handlers.
|
|
102
|
+
|
|
103
|
+
:param at_exit: Register the atexit handler.
|
|
104
|
+
Just remember that the atexit handler will be called right away on [Ctrl+C], meaning if you want to do
|
|
105
|
+
something specifically on KeyboardInterrupt, you should handle it separately and set this parameter to False.
|
|
106
|
+
Same goes for all the exceptions.
|
|
107
|
+
:param console_close: Register the console close handler.
|
|
108
|
+
:param kill_signal: Register the kill signal handler.
|
|
109
|
+
"""
|
|
110
|
+
if at_exit:
|
|
111
|
+
atexit.register(self.atexit_handler)
|
|
112
|
+
if console_close:
|
|
113
|
+
win32api.SetConsoleCtrlHandler(self.console_handler, True)
|
|
114
|
+
if kill_signal:
|
|
115
|
+
signal.signal(signal.SIGINT, self.signal_handler)
|
|
116
|
+
signal.signal(signal.SIGTERM, self.signal_handler)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def register_exit_handler(
|
|
120
|
+
clean_up_function,
|
|
121
|
+
at_exit: bool = True,
|
|
122
|
+
console_close: bool = True,
|
|
123
|
+
kill_signal: bool = True,
|
|
124
|
+
*args, **kwargs):
|
|
125
|
+
"""
|
|
126
|
+
This function will register the exit handler to handle exit events: Closing the console, pressing 'CTRL+C',
|
|
127
|
+
Killing the process.
|
|
128
|
+
|
|
129
|
+
:param clean_up_function: The action to run when one of exit types is triggered.
|
|
130
|
+
:param at_exit: Register the atexit handler.
|
|
131
|
+
Just remember that the atexit handler will be called right away on [Ctrl+C], meaning if you want to do something
|
|
132
|
+
specifically on KeyboardInterrupt, you should handle it separately and set this parameter to False.
|
|
133
|
+
Same goes for all the exceptions.
|
|
134
|
+
:param console_close: Register the console close handler.
|
|
135
|
+
:param kill_signal: Register the kill signal handler.
|
|
136
|
+
Same problem as with atexit handler, it will be called right away on [Ctrl+C].
|
|
137
|
+
:param args: The arguments to pass to the cleanup action.
|
|
138
|
+
:param kwargs: The keyword arguments to pass to the cleanup action.
|
|
139
|
+
"""
|
|
140
|
+
global EXIT_HANDLER_INSTANCE
|
|
141
|
+
EXIT_HANDLER_INSTANCE = ExitHandler(clean_up_function, args, kwargs)
|
|
142
|
+
EXIT_HANDLER_INSTANCE.register_handlers(at_exit=at_exit, console_close=console_close, kill_signal=kill_signal)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def restart_function(callable_function, *args, **kwargs):
|
|
146
|
+
"""
|
|
147
|
+
This function will run the callable function with the given arguments and keyword arguments.
|
|
148
|
+
If the function raises an exception, the function will be restarted.
|
|
149
|
+
|
|
150
|
+
:param callable_function: The function to run.
|
|
151
|
+
:param args: The arguments to pass to the function.
|
|
152
|
+
:param kwargs: The keyword arguments to pass to the function.
|
|
153
|
+
:return: The return value of the function.
|
|
154
|
+
"""
|
|
155
|
+
while True:
|
|
156
|
+
try:
|
|
157
|
+
return callable_function(*args, **kwargs)
|
|
158
|
+
except Exception as e:
|
|
159
|
+
print(f"ERROR: {e}")
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _ref_run_callable_on_exit_and_signals(
|
|
164
|
+
callable_function,
|
|
165
|
+
print_kwargs: dict = None,
|
|
166
|
+
*args,
|
|
167
|
+
**kwargs
|
|
168
|
+
):
|
|
169
|
+
"""
|
|
170
|
+
THIS IS FOR REFERENCE ONLY.
|
|
171
|
+
|
|
172
|
+
This function will run the callable function with the given arguments and keyword arguments.
|
|
173
|
+
If the function raises an exception, the function will be restarted.
|
|
174
|
+
|
|
175
|
+
:param callable_function: The function to run.
|
|
176
|
+
:param print_kwargs: print_api kwargs.
|
|
177
|
+
:param args: The arguments to pass to the function.
|
|
178
|
+
:param kwargs: The keyword arguments to pass to the function.
|
|
179
|
+
:return: The return value of the function.
|
|
180
|
+
"""
|
|
181
|
+
def signal_handler(signum, frame):
|
|
182
|
+
print_api(f"Signal {signum} received, exiting.", **(print_kwargs or {}))
|
|
183
|
+
callable_function(*args, **kwargs)
|
|
184
|
+
sys.exit(0)
|
|
185
|
+
|
|
186
|
+
def exit_handler():
|
|
187
|
+
print_api("Exiting.", **(print_kwargs or {}))
|
|
188
|
+
callable_function(*args, **kwargs)
|
|
189
|
+
sys.exit(0)
|
|
190
|
+
|
|
191
|
+
signals = [signal.SIGINT, signal.SIGTERM]
|
|
192
|
+
if platform.system() != 'Windows':
|
|
193
|
+
signals.append(signal.SIGQUIT)
|
|
194
|
+
signals.append(signal.SIGHUP)
|
|
195
|
+
for sig in signals:
|
|
196
|
+
signal.signal(sig, signal_handler)
|
|
197
|
+
|
|
198
|
+
# signal.signal(signal.SIGINT, signal_handler)
|
|
199
|
+
# signal.signal(signal.SIGTERM, signal_handler)
|
|
200
|
+
atexit.register(exit_handler)
|
|
201
|
+
|
|
202
|
+
# Register console handler for Windows.
|
|
203
|
+
if platform.system() == 'Windows':
|
|
204
|
+
console_handler = console.ConsoleHandler(exit_handler, args, kwargs)
|
|
205
|
+
console_handler.register_handler()
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Loading resources using stdlib importlib.resources APIs (Python 3.7+)
|
|
2
|
+
https://docs.python.org/3/library/importlib.html#module-importlib.resources"""
|
|
3
|
+
import importlib.resources
|
|
4
|
+
from contextlib import redirect_stdout
|
|
5
|
+
import io
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
from typing import Callable
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PackageMainsProcessor:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
script_file_stem: str = None
|
|
15
|
+
):
|
|
16
|
+
self.script_file_stem: str = script_file_stem
|
|
17
|
+
self.resources_directory_name: str = 'a_mains'
|
|
18
|
+
|
|
19
|
+
def get_resource_path(self) -> str:
|
|
20
|
+
return f'{__package__}.{self.resources_directory_name}'
|
|
21
|
+
|
|
22
|
+
def read_script_file_to_string(self) -> str:
|
|
23
|
+
script_string = importlib.resources.read_text(self.get_resource_path(), f'{self.script_file_stem}.py')
|
|
24
|
+
|
|
25
|
+
return script_string
|
|
26
|
+
|
|
27
|
+
def execute_script_file(
|
|
28
|
+
self,
|
|
29
|
+
function_name: str = 'main',
|
|
30
|
+
args: tuple = None,
|
|
31
|
+
kwargs: dict = None,
|
|
32
|
+
get_printed_output: bool = False
|
|
33
|
+
) -> str:
|
|
34
|
+
"""
|
|
35
|
+
Execute a script file from the package resources and get result as string.
|
|
36
|
+
|
|
37
|
+
:param function_name: Name of the function to call within the script.
|
|
38
|
+
:param args: Tuple of positional arguments to pass to the function.
|
|
39
|
+
:param kwargs: Dictionary of keyword arguments to pass to the function.
|
|
40
|
+
:param get_printed_output: If True, captures and returns printed output instead of return value.
|
|
41
|
+
|
|
42
|
+
:return: Output of the script execution as a string.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
if not args:
|
|
46
|
+
args = ()
|
|
47
|
+
if not kwargs:
|
|
48
|
+
kwargs = {}
|
|
49
|
+
|
|
50
|
+
module_name = f"{self.get_resource_path()}.{self.script_file_stem}" # script_file_name WITHOUT ".py"
|
|
51
|
+
|
|
52
|
+
module = importlib.import_module(module_name)
|
|
53
|
+
callable_function: Callable = getattr(module, function_name)
|
|
54
|
+
|
|
55
|
+
if get_printed_output:
|
|
56
|
+
with io.StringIO() as buffer, redirect_stdout(buffer):
|
|
57
|
+
callable_function(*args, **kwargs)
|
|
58
|
+
result = buffer.getvalue()
|
|
59
|
+
else:
|
|
60
|
+
result = callable_function(*args, **kwargs)
|
|
61
|
+
|
|
62
|
+
return result
|
|
63
|
+
|
|
64
|
+
def execute_script_with_subprocess(
|
|
65
|
+
self,
|
|
66
|
+
arguments: list = None
|
|
67
|
+
) -> tuple[str, str, int]:
|
|
68
|
+
"""
|
|
69
|
+
Execute a script file from the package resources using subprocess and get result as string.
|
|
70
|
+
:param arguments: Dictionary of arguments to pass to the script.
|
|
71
|
+
Example: ['--port', '8080', '-v']
|
|
72
|
+
:return: Tuple containing (stdout, stderr, returncode).
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
# script_file_name WITHOUT ".py"
|
|
76
|
+
module_name = f"{self.get_resource_path()}.{self.script_file_stem}"
|
|
77
|
+
|
|
78
|
+
command = [sys.executable, "-m", module_name]
|
|
79
|
+
if arguments:
|
|
80
|
+
command.extend(arguments)
|
|
81
|
+
|
|
82
|
+
result = subprocess.run(command, capture_output=True, text=True)
|
|
83
|
+
|
|
84
|
+
return result.stdout, result.stderr, result.returncode
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import ctypes
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def is_admin() -> bool:
|
|
6
|
+
"""
|
|
7
|
+
Function checks on Windows or POSIX OSes if the script is executed under Administrative Privileges.
|
|
8
|
+
:return: True / False.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
if os.name == 'nt':
|
|
12
|
+
if ctypes.windll.shell32.IsUserAnAdmin() == 0:
|
|
13
|
+
result = False
|
|
14
|
+
else:
|
|
15
|
+
result = True
|
|
16
|
+
else:
|
|
17
|
+
if 'SUDO_USER' in os.environ and os.geteuid() == 0:
|
|
18
|
+
result = True
|
|
19
|
+
else:
|
|
20
|
+
result = False
|
|
21
|
+
|
|
22
|
+
return result
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import stat
|
|
3
|
+
import contextlib
|
|
4
|
+
import subprocess
|
|
5
|
+
import getpass
|
|
6
|
+
|
|
7
|
+
# Import pwd only on linux.
|
|
8
|
+
if os.name == 'posix':
|
|
9
|
+
import pwd
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_sudo_executer_username() -> str:
|
|
13
|
+
"""
|
|
14
|
+
Function gets the username of the user who executed the script with sudo.
|
|
15
|
+
:return: str, username.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
if 'SUDO_USER' in os.environ:
|
|
19
|
+
return os.environ['SUDO_USER']
|
|
20
|
+
else:
|
|
21
|
+
return ''
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def detect_current_user(
|
|
25
|
+
optional_env_user_var: str = 'CUSTOM_SCRIPTED_USER'
|
|
26
|
+
) -> str:
|
|
27
|
+
"""
|
|
28
|
+
Try to robustly determine the 'real' installing user.
|
|
29
|
+
|
|
30
|
+
Priority:
|
|
31
|
+
1. FDB_INSTALL_USER env var (explicit override).
|
|
32
|
+
2. If running as root with sudo: use SUDO_USER.
|
|
33
|
+
3. Otherwise: use effective uid.
|
|
34
|
+
4. Fallbacks: getpass.getuser() / $USER.
|
|
35
|
+
|
|
36
|
+
:param optional_env_user_var: str, name of the environment variable that can override the user detection.
|
|
37
|
+
:return: str, username.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# 1. Explicit override for weird environments (CI, containers, etc.)
|
|
41
|
+
env_user = os.getenv(optional_env_user_var)
|
|
42
|
+
if env_user:
|
|
43
|
+
return env_user
|
|
44
|
+
|
|
45
|
+
# 2. If we are root, prefer the sudo caller if any
|
|
46
|
+
try:
|
|
47
|
+
euid = os.geteuid()
|
|
48
|
+
except AttributeError: # non-POSIX, very unlikely here
|
|
49
|
+
euid = None
|
|
50
|
+
|
|
51
|
+
if euid == 0:
|
|
52
|
+
sudo_user = os.environ.get("SUDO_USER")
|
|
53
|
+
if sudo_user:
|
|
54
|
+
return sudo_user
|
|
55
|
+
|
|
56
|
+
# 3. Normal case: effective uid -> username
|
|
57
|
+
if euid is not None:
|
|
58
|
+
try:
|
|
59
|
+
return pwd.getpwuid(euid).pw_name
|
|
60
|
+
except Exception:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
# 4. Fallbacks that don’t depend on utmp/tty
|
|
64
|
+
try:
|
|
65
|
+
return getpass.getuser()
|
|
66
|
+
except Exception:
|
|
67
|
+
return os.environ.get("USER", "unknown")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def set_executable(file_path: str):
|
|
71
|
+
"""
|
|
72
|
+
Function sets the executable permission on a file.
|
|
73
|
+
Equivalent to: chmod +x <file_path>
|
|
74
|
+
|
|
75
|
+
:param file_path: str, path to the file.
|
|
76
|
+
:return:
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
# os.chmod(file_path, os.stat(file_path).st_mode | 0o111)
|
|
80
|
+
os.chmod(file_path, os.stat(file_path).st_mode | stat.S_IXUSR)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def set_trusted_executable(file_path: str):
|
|
84
|
+
"""
|
|
85
|
+
Function sets the executable permission on a file and marks it as trusted.
|
|
86
|
+
:param file_path: str, path to the file.
|
|
87
|
+
:return:
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
# Check if the file exists
|
|
91
|
+
if not os.path.exists(file_path):
|
|
92
|
+
raise FileNotFoundError(f"The file does not exist: {file_path} ")
|
|
93
|
+
|
|
94
|
+
# Execute the `gio set` command
|
|
95
|
+
subprocess.run(
|
|
96
|
+
["gio", "set", file_path, "metadata::trusted", "true"],
|
|
97
|
+
check=True
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def set_xfce_exe_checksum(desktop_file_path):
|
|
102
|
+
# Expand `~` to the full home directory path
|
|
103
|
+
desktop_file_path = os.path.expanduser(desktop_file_path)
|
|
104
|
+
|
|
105
|
+
# Ensure the file exists
|
|
106
|
+
if not os.path.exists(desktop_file_path):
|
|
107
|
+
raise FileNotFoundError(f"The file does not exist: {desktop_file_path} ")
|
|
108
|
+
|
|
109
|
+
# Calculate the SHA256 checksum of the file
|
|
110
|
+
result = subprocess.run(
|
|
111
|
+
["sha256sum", desktop_file_path],
|
|
112
|
+
stdout=subprocess.PIPE,
|
|
113
|
+
check=True,
|
|
114
|
+
text=True
|
|
115
|
+
)
|
|
116
|
+
checksum = result.stdout.split()[0]
|
|
117
|
+
|
|
118
|
+
# Set the metadata::xfce-exe-checksum attribute using `gio`
|
|
119
|
+
subprocess.run(
|
|
120
|
+
["gio", "set", "-t", "string", desktop_file_path, "metadata::xfce-exe-checksum", checksum],
|
|
121
|
+
check=True
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def change_file_owner(file_path: str, username: str):
|
|
126
|
+
"""
|
|
127
|
+
Function changes the owner of the file to the specified user.
|
|
128
|
+
:param file_path: str, path to the file.
|
|
129
|
+
:param username: str, username of the new owner.
|
|
130
|
+
:return:
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
uid = pwd.getpwnam(username).pw_uid
|
|
134
|
+
os.chown(file_path, uid, -1)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def is_executable(file_path: str) -> bool:
|
|
138
|
+
"""
|
|
139
|
+
Function checks if the file has the executable permission.
|
|
140
|
+
Equivalent to: stat -c "%a %n" <file_path>
|
|
141
|
+
|
|
142
|
+
:param file_path: str, path to the file.
|
|
143
|
+
:return: bool, True / False.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
return bool(os.stat(file_path).st_mode & stat.S_IXUSR)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def run_as_root(command):
|
|
150
|
+
subprocess.check_call(['sudo'] + command)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@contextlib.contextmanager
|
|
154
|
+
def temporary_regular_permissions():
|
|
155
|
+
"""
|
|
156
|
+
This function is used to temporarily change the effective user and group ID to the original user's.
|
|
157
|
+
This is used to run commands with the original user's permissions.
|
|
158
|
+
If you executed a script with 'sudo' and wanted certain action to execute as regular user and not root.
|
|
159
|
+
|
|
160
|
+
Example:
|
|
161
|
+
with temporary_regular_permissions():
|
|
162
|
+
# Do something with regular permissions.
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
:return:
|
|
166
|
+
"""
|
|
167
|
+
# Save the current effective user and group ID
|
|
168
|
+
original_euid, original_egid = os.geteuid(), os.getegid()
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
# Get the original user's UID and GID
|
|
172
|
+
orig_uid = int(os.environ.get('SUDO_UID', os.getuid()))
|
|
173
|
+
orig_gid = int(os.environ.get('SUDO_GID', os.getgid()))
|
|
174
|
+
|
|
175
|
+
# Set the effective user and group ID to the original user's
|
|
176
|
+
os.setegid(orig_gid)
|
|
177
|
+
os.seteuid(orig_uid)
|
|
178
|
+
|
|
179
|
+
# Provide the context to do something with these permissions
|
|
180
|
+
yield
|
|
181
|
+
finally:
|
|
182
|
+
# Revert to the original effective user and group ID
|
|
183
|
+
os.seteuid(original_euid)
|
|
184
|
+
os.setegid(original_egid)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def expand_user_path(user_name, path):
|
|
188
|
+
pwnam = pwd.getpwnam(user_name)
|
|
189
|
+
home_dir = pwnam.pw_dir
|
|
190
|
+
return path.replace("~", home_dir)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def set_folder_permissions(
|
|
194
|
+
folder_path: str,
|
|
195
|
+
username: str = None,
|
|
196
|
+
logged_in_non_sudo_user: bool = False
|
|
197
|
+
):
|
|
198
|
+
"""
|
|
199
|
+
Set ownership and permissions for an existing folder.
|
|
200
|
+
|
|
201
|
+
:param folder_path: Path to the folder (must already exist)
|
|
202
|
+
:param username: Username to assign ownership to (ignored if non_sudo_user=True)
|
|
203
|
+
:param logged_in_non_sudo_user: If True, use the current logged-in user unless running under sudo
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
if not username and not logged_in_non_sudo_user:
|
|
207
|
+
raise ValueError("A username must be provided, or 'non_sudo_user' must be set to True.")
|
|
208
|
+
|
|
209
|
+
# Handle non_sudo_user case
|
|
210
|
+
if logged_in_non_sudo_user:
|
|
211
|
+
# Get the current logged-in user
|
|
212
|
+
username = pwd.getpwuid(os.getuid())[0]
|
|
213
|
+
|
|
214
|
+
# Get the UID and GID of the specified user
|
|
215
|
+
user_info = pwd.getpwnam(username)
|
|
216
|
+
user_uid = user_info.pw_uid
|
|
217
|
+
user_gid = user_info.pw_gid
|
|
218
|
+
|
|
219
|
+
# Change ownership of the folder to the specified user
|
|
220
|
+
# print(f"Changing ownership of {folder_path} to user '{username}'...")
|
|
221
|
+
os.chown(folder_path, user_uid, user_gid)
|
|
222
|
+
|
|
223
|
+
# Set appropriate permissions (read, write, execute for the owner)
|
|
224
|
+
# print(f"Setting permissions for {folder_path}...")
|
|
225
|
+
os.chmod(folder_path, 0o755) # Owner rwx, group r-x, others r-x
|
|
226
|
+
|
|
227
|
+
# print(f"Ownership and permissions updated for folder: '{folder_path}'")
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def is_directory_owner(directory_path: str, username: str) -> bool:
|
|
231
|
+
"""
|
|
232
|
+
Function checks if the directory is owned by the specified user.
|
|
233
|
+
:param directory_path: str, path to the directory.
|
|
234
|
+
:param username: str, username of the user.
|
|
235
|
+
:return: bool, True / False.
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
uid = pwd.getpwnam(username).pw_uid
|
|
239
|
+
return os.stat(directory_path).st_uid == uid
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def unblock_file_windows(file_path):
|
|
5
|
+
"""
|
|
6
|
+
Unblock a file on Windows. This is used to unblock files downloaded from the internet.
|
|
7
|
+
When you Right-click then navigate to Properties, you will see the Unblock checkbox.
|
|
8
|
+
:param file_path:
|
|
9
|
+
:return:
|
|
10
|
+
"""
|
|
11
|
+
try:
|
|
12
|
+
subprocess.run(["powershell", "-Command", f"Unblock-File -Path '{file_path}'"], check=True)
|
|
13
|
+
print(f"Successfully unblocked the file: {file_path}")
|
|
14
|
+
except subprocess.CalledProcessError as e:
|
|
15
|
+
print(f"Failed to unblock the file: {file_path}\nError: {e}")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_command_to_run_as_admin_windows(command: str) -> str:
|
|
19
|
+
"""
|
|
20
|
+
Function returns the command to run a command as administrator on Windows.
|
|
21
|
+
NOTE: When you run something this way, the parent will be the powershell.exe process.
|
|
22
|
+
If you need a status result directly of the executed command, you need to use subprocess.run() instead.
|
|
23
|
+
|
|
24
|
+
:param command: str, command to run.
|
|
25
|
+
:return: str, command to run as administrator.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
executable = command.split()[0]
|
|
29
|
+
command = (
|
|
30
|
+
f"powershell -Command "
|
|
31
|
+
f"\"Start-Process {executable} -ArgumentList '{' '.join(command.split()[1:])}' -Verb RunAs\"")
|
|
32
|
+
|
|
33
|
+
return command
|