computer-use-ootb-internal 0.0.120__tar.gz → 0.0.121__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.
Files changed (40) hide show
  1. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/PKG-INFO +1 -1
  2. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/pyproject.toml +1 -1
  3. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/guard_service.py +228 -228
  4. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/.gitignore +0 -0
  5. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/deploy.sh +0 -0
  6. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/README.md +0 -0
  7. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/__init__.py +0 -0
  8. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/app_teachmode.py +0 -0
  9. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/app_teachmode_gradio.py +0 -0
  10. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/animation/click_animation.py +0 -0
  11. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/animation/icons8-select-cursor-transparent-96.gif +0 -0
  12. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/animation/test_animation.py +0 -0
  13. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/executor/teachmode_executor.py +0 -0
  14. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/simple_parser/__init__.py +0 -0
  15. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/simple_parser/gui_capture.py +0 -0
  16. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/simple_parser/utils.py +0 -0
  17. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/uia_tools/__init__.py +0 -0
  18. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/uia_tools/screenshot_cli.py +0 -0
  19. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/uia_tools/screenshot_service.py +0 -0
  20. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/llm_utils/llm_utils.py +0 -0
  21. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/llm_utils/oai.py +0 -0
  22. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/llm_utils/run_litellm.py +0 -0
  23. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/llm_utils/run_llm.py +0 -0
  24. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/tools/__init__.py +0 -0
  25. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/tools/aws_request.py +0 -0
  26. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/tools/base.py +0 -0
  27. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/tools/bash.py +0 -0
  28. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/tools/collection.py +0 -0
  29. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/tools/colorful_text.py +0 -0
  30. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/tools/computer.py +0 -0
  31. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/tools/computer_marbot.py +0 -0
  32. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/tools/edit.py +0 -0
  33. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/tools/run.py +0 -0
  34. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/computer_use_demo/tools/screen_capture.py +0 -0
  35. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/dependency_check.py +0 -0
  36. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/preparation/__init__.py +0 -0
  37. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/preparation/star_rail_prepare.py +0 -0
  38. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/requirements-lite.txt +0 -0
  39. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/run_teachmode_ootb_args.py +0 -0
  40. {computer_use_ootb_internal-0.0.120 → computer_use_ootb_internal-0.0.121}/src/computer_use_ootb_internal/service_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: computer-use-ootb-internal
3
- Version: 0.0.120
3
+ Version: 0.0.121
4
4
  Summary: Computer Use OOTB
5
5
  Author-email: Siyuan Hu <siyuan.hu.sg@gmail.com>
6
6
  Requires-Python: >=3.11
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "computer-use-ootb-internal"
7
- version = "0.0.120"
7
+ version = "0.0.121"
8
8
  description = "Computer Use OOTB"
9
9
  authors = [{ name = "Siyuan Hu", email = "siyuan.hu.sg@gmail.com" }]
10
10
  requires-python = ">=3.11"
@@ -87,150 +87,20 @@ def receive_command():
87
87
  return jsonify({"message": f"Command {command_id} received and queued"}), 202 # Accepted
88
88
 
89
89
  # --- Helper Functions --- Only logging helpers needed adjustments
90
- def get_python_executable():
91
- python_exe = sys.executable
92
- if " " in python_exe and not python_exe.startswith('"'):
93
- python_exe = f'"{python_exe}"'
94
- return python_exe
95
-
96
- def get_pip_executable():
97
- """Tries to locate the pip executable in the same environment."""
98
- try:
99
- current_python = sys.executable
100
- log_info(f"get_pip_executable: sys.executable = {current_python}")
101
- python_path = pathlib.Path(current_python)
102
- # Common location is ../Scripts/pip.exe relative to python.exe
103
- pip_path = python_path.parent / "Scripts" / "pip.exe"
104
- log_info(f"get_pip_executable: Checking for pip at {pip_path}")
105
-
106
- if pip_path.exists():
107
- log_info(f"get_pip_executable: pip.exe found at {pip_path}")
108
- # Quote if necessary
109
- pip_exe = str(pip_path)
110
- if " " in pip_exe and not pip_exe.startswith('"'):
111
- pip_exe = f'"{pip_exe}"'
112
- return pip_exe
113
- else:
114
- log_error(f"get_pip_executable: pip.exe NOT found at {pip_path}. Falling back to 'python -m pip'.")
115
- # Fallback is intended here
116
- pass # Explicitly pass to reach the fallback return outside the else
117
-
118
- except Exception as e:
119
- log_error(f"get_pip_executable: Error determining pip path: {e}", exc_info=True)
120
- log_error("get_pip_executable: Falling back to 'python -m pip' due to error.")
121
-
122
- # Fallback return statement if 'exists' is false or an exception occurred
123
- return f"{get_python_executable()} -m pip"
124
-
125
- def log_info(msg):
126
- thread_name = threading.current_thread().name
127
- full_msg = f"[{thread_name}] {msg}"
128
- logging.info(full_msg)
129
- try:
130
- # Only log to event log from main service thread or known non-daemon threads if possible
131
- # Trying from waitress/flask threads might cause issues.
132
- # For simplicity, maybe remove event log integration or make it conditional.
133
- if threading.current_thread().name in ["MainThread", "CommandProcessor"]: # Example condition
134
- servicemanager.LogInfoMsg(str(full_msg))
135
- except Exception as e:
136
- logging.warning(f"Could not write info to Windows Event Log: {e}")
137
-
138
- def log_error(msg, exc_info=False):
139
- thread_name = threading.current_thread().name
140
- full_msg = f"[{thread_name}] {msg}"
141
- logging.error(full_msg, exc_info=exc_info)
142
- try:
143
- if threading.current_thread().name in ["MainThread", "CommandProcessor"]:
144
- servicemanager.LogErrorMsg(str(full_msg))
145
- except Exception as e:
146
- logging.warning(f"Could not write error to Windows Event Log: {e}")
147
-
148
- # --- PowerShell Task Scheduler Helpers ---
149
-
150
- _TASK_NAME_PREFIX = "OOTB_UserLogon_"
151
-
152
- def run_powershell_command(command, log_output=True):
153
- """Executes a PowerShell command and handles output/errors. Returns True on success."""
154
- # Use log_info from the service instance if available, otherwise use root logger
155
- logger = _service_instance.log_info if _service_instance else logging.info
156
- error_logger = _service_instance.log_error if _service_instance else logging.error
157
- logger(f"Executing PowerShell: {command}")
158
- try:
159
- # Using encoding important for non-ASCII usernames/paths
160
- result = subprocess.run(
161
- ["powershell.exe", "-NoProfile", "-NonInteractive", "-Command", command],
162
- capture_output=True, text=True, check=True, encoding='utf-8', errors='ignore'
163
- )
164
- if log_output and result.stdout:
165
- logger(f"PowerShell STDOUT:\n{result.stdout.strip()}")
166
- if log_output and result.stderr:
167
- logger(f"PowerShell STDERR:\n{result.stderr.strip()}") # Log stderr as info
168
- return True
169
- except FileNotFoundError:
170
- error_logger("'powershell.exe' not found. Cannot manage scheduled tasks.")
171
- return False
172
- except subprocess.CalledProcessError as e:
173
- error_logger(f"PowerShell command failed (Exit Code {e.returncode}):")
174
- error_logger(f" Command: {e.cmd}")
175
- if e.stdout: error_logger(f" STDOUT: {e.stdout.strip()}")
176
- if e.stderr: error_logger(f" STDERR: {e.stderr.strip()}")
177
- return False
178
- except Exception as e:
179
- error_logger(f"Unexpected error running PowerShell: {e}", exc_info=True)
180
- return False
181
-
182
- def create_or_update_logon_task(username, task_command, python_executable):
183
- """Creates or updates a scheduled task to run a command at user logon."""
184
- logger = _service_instance.log_info if _service_instance else logging.info
185
- error_logger = _service_instance.log_error if _service_instance else logging.error
186
- task_name = f"{_TASK_NAME_PREFIX}{username}"
187
- # Escape single quotes in paths and commands for PowerShell
188
- safe_python_exe = python_executable.replace("'", "''")
189
- # Ensure task_command is just the arguments, not the python exe itself
190
- command_parts = task_command.split(' ', 1)
191
- if len(command_parts) > 1 and command_parts[0] == python_executable:
192
- safe_task_command_args = command_parts[1].replace("'", "''")
193
- else: # Fallback if task_command doesn't start with python_exe
194
- safe_task_command_args = task_command.replace(python_executable, "").strip().replace("'", "''")
195
-
196
- safe_task_name = task_name.replace("'", "''")
197
- safe_username = username.replace("'", "''") # Handle usernames with quotes?
198
-
199
- action = f"$Action = New-ScheduledTaskAction -Execute '{safe_python_exe}' -Argument '{safe_task_command_args}'"
200
- trigger = f"$Trigger = New-ScheduledTaskTrigger -AtLogOn -User '{safe_username}'"
201
- principal = f"$Principal = New-ScheduledTaskPrincipal -UserId '{safe_username}' -LogonType Interactive"
202
- settings = "$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -DontStopOnIdleEnd -ExecutionTimeLimit ([System.TimeSpan]::Zero) -RunOnlyIfNetworkAvailable:$false"
203
-
204
- command = f"""
205
- try {{
206
- {action}
207
- {trigger}
208
- {principal}
209
- {settings}
210
- Register-ScheduledTask -TaskName '{safe_task_name}' -Action $Action -Trigger $Trigger -Principal $Principal -Settings $Settings -Force -ErrorAction Stop
211
- Write-Host "Scheduled task '{safe_task_name}' registered/updated successfully."
212
- }} catch {{
213
- Write-Error "Failed to register/update scheduled task '{safe_task_name}': $_"
214
- exit 1 # Indicate failure
215
- }}
216
- """
217
- success = run_powershell_command(command)
218
- if success:
219
- logger(f"Successfully created/updated scheduled task '{task_name}' for user '{username}'.")
220
- else:
221
- error_logger(f"Failed to create/update scheduled task '{task_name}' for user '{username}'.")
222
- return success
90
+ # Move these inside the class later
91
+ # def get_python_executable(): ...
92
+ # def get_pip_executable(): ...
223
93
 
94
+ # Define loggers at module level for use before instance exists?
95
+ # Or handle carefully within instance methods.
224
96
 
225
- def remove_logon_task(username):
226
- """Removes the logon scheduled task for a user."""
227
- logger = _service_instance.log_info if _service_instance else logging.info
228
- task_name = f"{_TASK_NAME_PREFIX}{username}"
229
- safe_task_name = task_name.replace("'", "''")
230
- command = f"Unregister-ScheduledTask -TaskName '{safe_task_name}' -Confirm:$false -ErrorAction SilentlyContinue"
231
- run_powershell_command(command, log_output=False)
232
- logger(f"Attempted removal of scheduled task '{task_name}' for user '{username}'.")
233
- return True
97
+ # --- PowerShell Task Scheduler Helpers --- (These will become methods) ---
98
+
99
+ # _TASK_NAME_PREFIX = "OOTB_UserLogon_" # Move to class
100
+
101
+ # def run_powershell_command(command, log_output=True): ...
102
+ # def create_or_update_logon_task(username, task_command, python_executable): ...
103
+ # def remove_logon_task(username): ...
234
104
 
235
105
  # --- End PowerShell Task Scheduler Helpers ---
236
106
 
@@ -238,6 +108,137 @@ class GuardService(win32serviceutil.ServiceFramework):
238
108
  _svc_name_ = _SERVICE_NAME
239
109
  _svc_display_name_ = _SERVICE_DISPLAY_NAME
240
110
  _svc_description_ = _SERVICE_DESCRIPTION
111
+ _task_name_prefix = "OOTB_UserLogon_" # Class attribute for task prefix
112
+
113
+ # --- Instance Logging Methods ---
114
+ def log_info(self, msg):
115
+ thread_name = threading.current_thread().name
116
+ full_msg = f"[{thread_name}] {msg}"
117
+ logging.info(full_msg)
118
+ try:
119
+ if threading.current_thread().name in ["MainThread", "CommandProcessor"]:
120
+ servicemanager.LogInfoMsg(str(full_msg))
121
+ except Exception as e:
122
+ # Log only to file if event log fails
123
+ logging.warning(f"(Instance) Could not write info to Windows Event Log: {e}")
124
+
125
+ def log_error(self, msg, exc_info=False):
126
+ thread_name = threading.current_thread().name
127
+ full_msg = f"[{thread_name}] {msg}"
128
+ logging.error(full_msg, exc_info=exc_info)
129
+ try:
130
+ if threading.current_thread().name in ["MainThread", "CommandProcessor"]:
131
+ servicemanager.LogErrorMsg(str(full_msg))
132
+ except Exception as e:
133
+ logging.warning(f"(Instance) Could not write error to Windows Event Log: {e}")
134
+ # --- End Instance Logging ---
135
+
136
+ # --- Instance Helper Methods (Moved from module level) ---
137
+ def get_python_executable(self):
138
+ python_exe = sys.executable
139
+ if " " in python_exe and not python_exe.startswith('"'):
140
+ python_exe = f'"{python_exe}"'
141
+ return python_exe
142
+
143
+ def get_pip_executable(self):
144
+ """Tries to locate the pip executable in the same environment."""
145
+ try:
146
+ current_python = sys.executable
147
+ self.log_info(f"get_pip_executable: sys.executable = {current_python}")
148
+ python_path = pathlib.Path(current_python)
149
+ pip_path = python_path.parent / "Scripts" / "pip.exe"
150
+ self.log_info(f"get_pip_executable: Checking for pip at {pip_path}")
151
+
152
+ if pip_path.exists():
153
+ self.log_info(f"get_pip_executable: pip.exe found at {pip_path}")
154
+ pip_exe = str(pip_path)
155
+ if " " in pip_exe and not pip_exe.startswith('"'):
156
+ pip_exe = f'"{pip_exe}"'
157
+ return pip_exe
158
+ else:
159
+ self.log_error(f"get_pip_executable: pip.exe NOT found at {pip_path}. Falling back to 'python -m pip'.")
160
+ pass
161
+
162
+ except Exception as e:
163
+ self.log_error(f"get_pip_executable: Error determining pip path: {e}", exc_info=True)
164
+ self.log_error("get_pip_executable: Falling back to 'python -m pip' due to error.")
165
+
166
+ return f"{self.get_python_executable()} -m pip"
167
+
168
+ # --- PowerShell Methods (Moved from module level) ---
169
+ def run_powershell_command(self, command, log_output=True):
170
+ """Executes a PowerShell command and handles output/errors. Returns True on success."""
171
+ self.log_info(f"Executing PowerShell: {command}")
172
+ try:
173
+ result = subprocess.run(
174
+ ["powershell.exe", "-NoProfile", "-NonInteractive", "-Command", command],
175
+ capture_output=True, text=True, check=True, encoding='utf-8', errors='ignore'
176
+ )
177
+ if log_output and result.stdout:
178
+ self.log_info(f"PowerShell STDOUT:\n{result.stdout.strip()}")
179
+ if log_output and result.stderr:
180
+ self.log_info(f"PowerShell STDERR:\n{result.stderr.strip()}")
181
+ return True
182
+ except FileNotFoundError:
183
+ self.log_error("'powershell.exe' not found. Cannot manage scheduled tasks.")
184
+ return False
185
+ except subprocess.CalledProcessError as e:
186
+ self.log_error(f"PowerShell command failed (Exit Code {e.returncode}):")
187
+ self.log_error(f" Command: {e.cmd}")
188
+ if e.stdout: self.log_error(f" STDOUT: {e.stdout.strip()}")
189
+ if e.stderr: self.log_error(f" STDERR: {e.stderr.strip()}")
190
+ return False
191
+ except Exception as e:
192
+ self.log_error(f"Unexpected error running PowerShell: {e}", exc_info=True)
193
+ return False
194
+
195
+ def create_or_update_logon_task(self, username, task_command, python_executable):
196
+ """Creates or updates a scheduled task to run a command at user logon."""
197
+ task_name = f"{self._task_name_prefix}{username}"
198
+ safe_python_exe = python_executable.replace("'", "''")
199
+ command_parts = task_command.split(' ', 1)
200
+ if len(command_parts) > 1 and command_parts[0] == python_executable:
201
+ safe_task_command_args = command_parts[1].replace("'", "''")
202
+ else:
203
+ safe_task_command_args = task_command.replace(python_executable, "").strip().replace("'", "''")
204
+
205
+ safe_task_name = task_name.replace("'", "''")
206
+ safe_username = username.replace("'", "''")
207
+
208
+ action = f"$Action = New-ScheduledTaskAction -Execute '{safe_python_exe}' -Argument '{safe_task_command_args}'"
209
+ trigger = f"$Trigger = New-ScheduledTaskTrigger -AtLogOn -User '{safe_username}'"
210
+ principal = f"$Principal = New-ScheduledTaskPrincipal -UserId '{safe_username}' -LogonType Interactive"
211
+ settings = "$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -DontStopOnIdleEnd -ExecutionTimeLimit ([System.TimeSpan]::Zero) -RunOnlyIfNetworkAvailable:$false"
212
+
213
+ command = f"""
214
+ try {{
215
+ {action}
216
+ {trigger}
217
+ {principal}
218
+ {settings}
219
+ Register-ScheduledTask -TaskName '{safe_task_name}' -Action $Action -Trigger $Trigger -Principal $Principal -Settings $Settings -Force -ErrorAction Stop
220
+ Write-Host "Scheduled task '{safe_task_name}' registered/updated successfully."
221
+ }} catch {{
222
+ Write-Error "Failed to register/update scheduled task '{safe_task_name}': $_"
223
+ exit 1
224
+ }}
225
+ """
226
+ success = self.run_powershell_command(command)
227
+ if success:
228
+ self.log_info(f"Successfully created/updated scheduled task '{task_name}' for user '{username}'.")
229
+ else:
230
+ self.log_error(f"Failed to create/update scheduled task '{task_name}' for user '{username}'.")
231
+ return success
232
+
233
+ def remove_logon_task(self, username):
234
+ """Removes the logon scheduled task for a user."""
235
+ task_name = f"{self._task_name_prefix}{username}"
236
+ safe_task_name = task_name.replace("'", "''")
237
+ command = f"Unregister-ScheduledTask -TaskName '{safe_task_name}' -Confirm:$false -ErrorAction SilentlyContinue"
238
+ self.run_powershell_command(command, log_output=False)
239
+ self.log_info(f"Attempted removal of scheduled task '{task_name}' for user '{username}'.")
240
+ return True
241
+ # --- End Instance Helper Methods ---
241
242
 
242
243
  def __init__(self, args):
243
244
  global _service_instance
@@ -247,15 +248,16 @@ class GuardService(win32serviceutil.ServiceFramework):
247
248
  self.server_thread = None
248
249
  self.command_queue = queue.Queue()
249
250
  self.command_processor_thread = None
250
- self.session = requests.Session() # For status reporting
251
+ self.session = requests.Session()
251
252
 
252
- self.python_exe = get_python_executable()
253
- self.pip_command_base = get_pip_executable()
253
+ # Initialize paths using instance methods
254
+ self.python_exe = self.get_python_executable()
255
+ self.pip_command_base = self.get_pip_executable()
254
256
  self.ootb_command = f"{self.python_exe} -m {_OOTB_MODULE}"
255
- _service_instance = self # Set global reference
257
+ _service_instance = self
256
258
 
257
259
  def SvcStop(self):
258
- log_info(f"Service stop requested.")
260
+ self.log_info(f"Service stop requested.")
259
261
  self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
260
262
  self.is_running = False
261
263
  # Signal the command processor thread to stop
@@ -264,7 +266,7 @@ class GuardService(win32serviceutil.ServiceFramework):
264
266
  win32event.SetEvent(self.hWaitStop)
265
267
  # Stopping waitress gracefully from another thread is non-trivial.
266
268
  # We rely on the SCM timeout / process termination for now.
267
- log_info(f"{_SERVICE_NAME} SvcStop: Stop signaled. Server thread will be terminated by SCM.")
269
+ self.log_info(f"{_SERVICE_NAME} SvcStop: Stop signaled. Server thread will be terminated by SCM.")
268
270
 
269
271
 
270
272
  def SvcDoRun(self):
@@ -272,51 +274,51 @@ class GuardService(win32serviceutil.ServiceFramework):
272
274
  servicemanager.PYS_SERVICE_STARTED,
273
275
  (self._svc_name_, ''))
274
276
  try:
275
- log_info(f"{_SERVICE_NAME} starting.")
277
+ self.log_info(f"{_SERVICE_NAME} starting.")
276
278
  # Start the command processor thread
277
279
  self.command_processor_thread = threading.Thread(
278
280
  target=self.process_commands, name="CommandProcessor", daemon=True)
279
281
  self.command_processor_thread.start()
280
- log_info("Command processor thread started.")
282
+ self.log_info("Command processor thread started.")
281
283
 
282
284
  # Start the Flask server (via Waitress) in a separate thread
283
285
  self.server_thread = threading.Thread(
284
286
  target=self.run_server, name="WebServerThread", daemon=True)
285
287
  self.server_thread.start()
286
- log_info(f"Web server thread started, listening on {_LISTEN_HOST}:{_LISTEN_PORT}.")
288
+ self.log_info(f"Web server thread started, listening on {_LISTEN_HOST}:{_LISTEN_PORT}.")
287
289
 
288
- log_info(f"{_SERVICE_NAME} running. Waiting for stop signal.")
290
+ self.log_info(f"{_SERVICE_NAME} running. Waiting for stop signal.")
289
291
  # Keep the main service thread alive waiting for stop signal
290
292
  win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
291
- log_info(f"{_SERVICE_NAME} received stop signal in main thread.")
293
+ self.log_info(f"{_SERVICE_NAME} received stop signal in main thread.")
292
294
 
293
295
  except Exception as e:
294
- log_error(f"Fatal error in SvcDoRun: {e}", exc_info=True)
296
+ self.log_error(f"Fatal error in SvcDoRun: {e}", exc_info=True)
295
297
  self.SvcStop() # Signal stop if possible
296
298
  finally:
297
- log_info(f"{_SERVICE_NAME} SvcDoRun finished.")
299
+ self.log_info(f"{_SERVICE_NAME} SvcDoRun finished.")
298
300
 
299
301
 
300
302
  def run_server(self):
301
303
  """Runs the Flask app using Waitress."""
302
- log_info(f"Waitress server starting on {_LISTEN_HOST}:{_LISTEN_PORT}")
304
+ self.log_info(f"Waitress server starting on {_LISTEN_HOST}:{_LISTEN_PORT}")
303
305
  try:
304
306
  serve(flask_app, host=_LISTEN_HOST, port=_LISTEN_PORT, threads=4)
305
- log_info("Waitress server has stopped.") # Should only happen on shutdown
307
+ self.log_info("Waitress server has stopped.") # Should only happen on shutdown
306
308
  except Exception as e:
307
- log_error(f"Web server thread encountered an error: {e}", exc_info=True)
309
+ self.log_error(f"Web server thread encountered an error: {e}", exc_info=True)
308
310
  # Consider signaling the main thread to stop if the web server fails critically
309
311
  # For now, just log the error.
310
312
 
311
313
 
312
314
  def process_commands(self):
313
315
  """Worker thread to process commands from the queue."""
314
- log_info("Command processor thread starting.")
316
+ self.log_info("Command processor thread starting.")
315
317
  while self.is_running:
316
318
  try:
317
319
  item = self.command_queue.get(block=True, timeout=1) # Add timeout to check is_running periodically
318
320
  if item is None:
319
- log_info("Command processor received stop signal.")
321
+ self.log_info("Command processor received stop signal.")
320
322
  break # Exit loop
321
323
 
322
324
  command_id, command = item
@@ -324,7 +326,7 @@ class GuardService(win32serviceutil.ServiceFramework):
324
326
  target = command.get("target_user", "all_active")
325
327
  status = "failed_unknown" # Default
326
328
 
327
- log_info(f"Dequeued Command ID {command_id}: action='{action}', target='{target}'")
329
+ self.log_info(f"Dequeued Command ID {command_id}: action='{action}', target='{target}'")
328
330
 
329
331
  try:
330
332
  if action == "update":
@@ -334,10 +336,10 @@ class GuardService(win32serviceutil.ServiceFramework):
334
336
  elif action == "start_ootb":
335
337
  status = self.handle_start(target)
336
338
  else:
337
- log_error(f"Unknown action in queue: {action}")
339
+ self.log_error(f"Unknown action in queue: {action}")
338
340
  status = "failed_unknown_action"
339
341
  except Exception as handler_ex:
340
- log_error(f"Exception processing Command ID {command_id} ({action}): {handler_ex}", exc_info=True)
342
+ self.log_error(f"Exception processing Command ID {command_id} ({action}): {handler_ex}", exc_info=True)
341
343
  status = "failed_exception"
342
344
  finally:
343
345
  self.report_command_status(command_id, status)
@@ -347,17 +349,17 @@ class GuardService(win32serviceutil.ServiceFramework):
347
349
  # Timeout occurred, just loop again and check self.is_running
348
350
  continue
349
351
  except Exception as e:
350
- log_error(f"Error in command processing loop: {e}", exc_info=True)
352
+ self.log_error(f"Error in command processing loop: {e}", exc_info=True)
351
353
  if self.is_running:
352
354
  time.sleep(5)
353
355
 
354
- log_info("Command processor thread finished.")
356
+ self.log_info("Command processor thread finished.")
355
357
 
356
358
 
357
359
  def report_command_status(self, command_id, status, details=""):
358
360
  """Sends command status back to the server."""
359
361
  if not _SERVER_STATUS_REPORT_URL:
360
- log_error("No server status report URL configured. Skipping report.")
362
+ self.log_error("No server status report URL configured. Skipping report.")
361
363
  return
362
364
 
363
365
  payload = {
@@ -366,41 +368,40 @@ class GuardService(win32serviceutil.ServiceFramework):
366
368
  "details": details,
367
369
  "machine_id": os.getenv('COMPUTERNAME', 'unknown_guard')
368
370
  }
369
- log_info(f"Reporting status for command {command_id}: {status}")
371
+ self.log_info(f"Reporting status for command {command_id}: {status}")
370
372
  try:
371
373
  response = self.session.post(_SERVER_STATUS_REPORT_URL, json=payload, timeout=15)
372
374
  response.raise_for_status()
373
- log_info(f"Status report for command {command_id} accepted by server.")
375
+ self.log_info(f"Status report for command {command_id} accepted by server.")
374
376
  except requests.exceptions.RequestException as e:
375
- log_error(f"Failed to report status for command {command_id}: {e}")
377
+ self.log_error(f"Failed to report status for command {command_id}: {e}")
376
378
  except Exception as e:
377
- log_error(f"Unexpected error reporting status for command {command_id}: {e}", exc_info=True)
379
+ self.log_error(f"Unexpected error reporting status for command {command_id}: {e}", exc_info=True)
378
380
 
379
- # --- Command Handlers --- Copying full implementation from previous version
381
+ # --- Command Handlers --- Now call self. for helpers
380
382
 
381
383
  def handle_update(self):
382
- log_info("Executing OOTB update...")
384
+ self.log_info("Executing OOTB update...")
383
385
  if not self.pip_command_base:
384
- log_error("Cannot update: pip command not found.")
386
+ self.log_error("Cannot update: pip command not found.")
385
387
  return "failed_pip_not_found"
386
388
 
387
389
  update_command = f"{self.pip_command_base} install --upgrade --no-cache-dir {_PACKAGE_NAME}"
388
- log_info(f"Running update command: {update_command}")
390
+ self.log_info(f"Running update command: {update_command}")
389
391
  try:
390
392
  result = subprocess.run(update_command, shell=True, capture_output=True, text=True, check=True, timeout=300, encoding='utf-8')
391
- log_info(f"Update successful: \nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}")
393
+ self.log_info(f"Update successful: \nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}")
392
394
  return "success"
393
395
  except subprocess.CalledProcessError as e:
394
- log_error(f"Update failed (Exit Code {e.returncode}):\nSTDOUT:\n{e.stdout}\nSTDERR:\n{e.stderr}")
396
+ self.log_error(f"Update failed (Exit Code {e.returncode}):\nSTDOUT:\n{e.stdout}\nSTDERR:\n{e.stderr}")
395
397
  return f"failed_exit_{e.returncode}"
396
398
  except subprocess.TimeoutExpired:
397
- log_error(f"Update command timed out.")
399
+ self.log_error(f"Update command timed out.")
398
400
  return "failed_timeout"
399
401
  except Exception as e:
400
- log_error(f"Unexpected error during update: {e}", exc_info=True)
402
+ self.log_error(f"Unexpected error during update: {e}", exc_info=True)
401
403
  return "failed_exception"
402
404
 
403
-
404
405
  def _get_ootb_processes(self, target_user="all_active"):
405
406
  ootb_procs = []
406
407
  target_pid_list = []
@@ -413,7 +414,7 @@ class GuardService(win32serviceutil.ServiceFramework):
413
414
  else:
414
415
  target_users.add(target_user.lower())
415
416
 
416
- log_info(f"Searching for OOTB processes for users: {target_users}")
417
+ self.log_info(f"Searching for OOTB processes for users: {target_users}")
417
418
 
418
419
  python_exe_path = self.python_exe.strip('"') # Get unquoted path for comparison
419
420
 
@@ -428,20 +429,19 @@ class GuardService(win32serviceutil.ServiceFramework):
428
429
  cmdline = ' '.join(pinfo['cmdline']) if pinfo['cmdline'] else ''
429
430
  # Check if the process executable matches our python path AND module is in cmdline
430
431
  if pinfo['exe'] and pinfo['exe'] == python_exe_path and _OOTB_MODULE in cmdline:
431
- log_info(f"Found matching OOTB process: PID={pinfo['pid']}, User={pinfo['username']}, Cmd={cmdline}")
432
+ self.log_info(f"Found matching OOTB process: PID={pinfo['pid']}, User={pinfo['username']}, Cmd={cmdline}")
432
433
  ootb_procs.append(proc)
433
434
  target_pid_list.append(pinfo['pid'])
434
435
 
435
436
  except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
436
437
  continue
437
- log_info(f"Found {len(ootb_procs)} OOTB process(es) matching criteria: {target_pid_list}")
438
+ self.log_info(f"Found {len(ootb_procs)} OOTB process(es) matching criteria: {target_pid_list}")
438
439
  except Exception as e:
439
- log_error(f"Error enumerating processes: {e}", exc_info=True)
440
+ self.log_error(f"Error enumerating processes: {e}", exc_info=True)
440
441
  return ootb_procs
441
442
 
442
-
443
443
  def handle_stop(self, target_user="all_active"):
444
- log_info(f"Executing stop OOTB for target '{target_user}'...")
444
+ self.log_info(f"Executing stop OOTB for target '{target_user}'...")
445
445
  stop_results = {} # Track results per user {username: (task_status, immediate_status)}
446
446
  failed_users = set()
447
447
 
@@ -458,26 +458,26 @@ class GuardService(win32serviceutil.ServiceFramework):
458
458
  if user:
459
459
  active_sessions[user.lower()] = session['SessionId']
460
460
  except Exception as query_err:
461
- log_error(f"Could not query session {session['SessionId']} during stop: {query_err}")
461
+ self.log_error(f"Could not query session {session['SessionId']} during stop: {query_err}")
462
462
  except Exception as user_enum_err:
463
- log_error(f"Error enumerating users/sessions during stop: {user_enum_err}", exc_info=True)
463
+ self.log_error(f"Error enumerating users/sessions during stop: {user_enum_err}", exc_info=True)
464
464
  return "failed_user_enumeration"
465
465
 
466
- log_info(f"Stop target: '{target_user}'. Active sessions: {active_sessions}")
466
+ self.log_info(f"Stop target: '{target_user}'. Active sessions: {active_sessions}")
467
467
 
468
468
  target_users_normalized = set()
469
469
  if target_user == "all_active":
470
470
  # Target only currently active users for stop all
471
471
  target_users_normalized = set(active_sessions.keys())
472
- log_info(f"Stop targeting all active users: {target_users_normalized}")
472
+ self.log_info(f"Stop targeting all active users: {target_users_normalized}")
473
473
  else:
474
474
  # Target the specific user, regardless of active status (for task removal)
475
475
  normalized_target = target_user.lower()
476
476
  target_users_normalized.add(normalized_target)
477
- log_info(f"Stop targeting specific user: {normalized_target}")
477
+ self.log_info(f"Stop targeting specific user: {normalized_target}")
478
478
 
479
479
  if not target_users_normalized:
480
- log_info("No target users identified for stop.")
480
+ self.log_info("No target users identified for stop.")
481
481
  return "failed_no_target_users" # Or success if none were targeted?
482
482
 
483
483
  # --- Process each target user ---
@@ -486,15 +486,15 @@ class GuardService(win32serviceutil.ServiceFramework):
486
486
  immediate_stop_status = "stop_not_attempted"
487
487
  stopped_count = 0
488
488
 
489
- log_info(f"Processing stop for user '{user}'...")
489
+ self.log_info(f"Processing stop for user '{user}'...")
490
490
 
491
491
  # 1. Always try to remove the scheduled task
492
492
  try:
493
493
  # remove_logon_task always returns True for now, just logs attempt
494
- remove_logon_task(user)
494
+ self.remove_logon_task(user)
495
495
  task_removed_status = "task_removed_attempted"
496
496
  except Exception as task_err:
497
- log_error(f"Exception removing scheduled task for {user}: {task_err}", exc_info=True)
497
+ self.log_error(f"Exception removing scheduled task for {user}: {task_err}", exc_info=True)
498
498
  task_removed_status = "task_exception"
499
499
  failed_users.add(user)
500
500
  # Continue to try and stop process if active
@@ -504,37 +504,37 @@ class GuardService(win32serviceutil.ServiceFramework):
504
504
 
505
505
  if is_active:
506
506
  immediate_stop_status = "stop_attempted"
507
- log_info(f"User '{user}' is active. Attempting to terminate OOTB process(es)...")
507
+ self.log_info(f"User '{user}' is active. Attempting to terminate OOTB process(es)...")
508
508
  # Pass the specific username to _get_ootb_processes
509
509
  procs_to_stop = self._get_ootb_processes(user)
510
510
 
511
511
  if not procs_to_stop:
512
- log_info(f"No running OOTB processes found for active user '{user}'.")
512
+ self.log_info(f"No running OOTB processes found for active user '{user}'.")
513
513
  immediate_stop_status = "stop_skipped_not_running"
514
514
  else:
515
- log_info(f"Found {len(procs_to_stop)} process(es) for user '{user}' to stop.")
515
+ self.log_info(f"Found {len(procs_to_stop)} process(es) for user '{user}' to stop.")
516
516
  for proc in procs_to_stop:
517
517
  try:
518
518
  pid = proc.pid # Get pid before potential termination
519
519
  username = proc.info.get('username', 'unknown_user')
520
- log_info(f"Terminating process PID={pid}, User={username}")
520
+ self.log_info(f"Terminating process PID={pid}, User={username}")
521
521
  proc.terminate()
522
522
  try:
523
523
  proc.wait(timeout=3)
524
- log_info(f"Process PID={pid} terminated successfully.")
524
+ self.log_info(f"Process PID={pid} terminated successfully.")
525
525
  stopped_count += 1
526
526
  except psutil.TimeoutExpired:
527
- log_error(f"Process PID={pid} did not terminate gracefully, killing.")
527
+ self.log_error(f"Process PID={pid} did not terminate gracefully, killing.")
528
528
  proc.kill()
529
529
  stopped_count += 1
530
530
  except psutil.NoSuchProcess:
531
- log_info(f"Process PID={pid} already terminated.")
531
+ self.log_info(f"Process PID={pid} already terminated.")
532
532
  # Don't increment stopped_count here as we didn't stop it now
533
533
  except psutil.AccessDenied:
534
- log_error(f"Access denied trying to terminate process PID={pid}.")
534
+ self.log_error(f"Access denied trying to terminate process PID={pid}.")
535
535
  failed_users.add(user) # Mark user as failed if stop fails
536
536
  except Exception as e:
537
- log_error(f"Error stopping process PID={pid}: {e}", exc_info=True)
537
+ self.log_error(f"Error stopping process PID={pid}: {e}", exc_info=True)
538
538
  failed_users.add(user) # Mark user as failed
539
539
 
540
540
  # Determine status based on how many were found vs stopped
@@ -546,7 +546,7 @@ class GuardService(win32serviceutil.ServiceFramework):
546
546
  immediate_stop_status = f"stop_partial_terminated_{stopped_count}_of_{len(procs_to_stop)}"
547
547
 
548
548
  else: # User not active
549
- log_info(f"User '{user}' is not active. Skipping immediate process stop (task removal attempted).")
549
+ self.log_info(f"User '{user}' is not active. Skipping immediate process stop (task removal attempted).")
550
550
  immediate_stop_status = "stop_skipped_inactive"
551
551
 
552
552
  # Record final results for this user
@@ -559,7 +559,7 @@ class GuardService(win32serviceutil.ServiceFramework):
559
559
  if not stop_results: final_status = "no_targets_processed"
560
560
  if len(failed_users) == total_processed and total_processed > 0 : final_status = "failed"
561
561
 
562
- log_info(f"Finished stopping OOTB. Overall Status: {final_status}. Results: {stop_results}")
562
+ self.log_info(f"Finished stopping OOTB. Overall Status: {final_status}. Results: {stop_results}")
563
563
  try:
564
564
  details = json.dumps(stop_results)
565
565
  except Exception:
@@ -567,12 +567,12 @@ class GuardService(win32serviceutil.ServiceFramework):
567
567
  return f"{final_status}::{details}" # Use :: as separator
568
568
 
569
569
  except Exception as e:
570
- log_error(f"Error during combined stop OOTB process: {e}", exc_info=True)
570
+ self.log_error(f"Error during combined stop OOTB process: {e}", exc_info=True)
571
571
  return "failed_exception"
572
572
 
573
573
 
574
574
  def handle_start(self, target_user="all_active"):
575
- log_info(f"Executing start OOTB for target '{target_user}'...")
575
+ self.log_info(f"Executing start OOTB for target '{target_user}'...")
576
576
  start_results = {} # Track results per user {username: (task_status, immediate_status)}
577
577
  failed_users = set()
578
578
 
@@ -594,18 +594,18 @@ class GuardService(win32serviceutil.ServiceFramework):
594
594
  if user:
595
595
  active_sessions[user.lower()] = session['SessionId']
596
596
  except Exception as query_err:
597
- log_error(f"Could not query session {session['SessionId']}: {query_err}")
597
+ self.log_error(f"Could not query session {session['SessionId']}: {query_err}")
598
598
  except Exception as user_enum_err:
599
- log_error(f"Error enumerating users/sessions: {user_enum_err}", exc_info=True)
599
+ self.log_error(f"Error enumerating users/sessions: {user_enum_err}", exc_info=True)
600
600
  return "failed_user_enumeration"
601
601
 
602
- log_info(f"Found active user sessions: {active_sessions}")
602
+ self.log_info(f"Found active user sessions: {active_sessions}")
603
603
 
604
604
  target_users_normalized = set()
605
605
  if target_user == "all_active":
606
606
  # If targeting all_active, only target those CURRENTLY active
607
607
  target_users_normalized = set(active_sessions.keys())
608
- log_info(f"Targeting all active users: {target_users_normalized}")
608
+ self.log_info(f"Targeting all active users: {target_users_normalized}")
609
609
  else:
610
610
  normalized_target = target_user.lower()
611
611
  # Check if the target user actually exists on the system, even if inactive
@@ -613,13 +613,13 @@ class GuardService(win32serviceutil.ServiceFramework):
613
613
  # Let's assume admin provides a valid username for specific targeting.
614
614
  # if normalized_target in all_system_users: # Removing this check, assume valid user input
615
615
  target_users_normalized.add(normalized_target)
616
- log_info(f"Targeting specific user: {normalized_target}")
616
+ self.log_info(f"Targeting specific user: {normalized_target}")
617
617
  # else:
618
618
  # log_error(f"Target user '{target_user}' does not appear to exist on this system based on psutil.")
619
619
  # return "failed_user_does_not_exist"
620
620
 
621
621
  if not target_users_normalized:
622
- log_info("No target users identified (or none active for 'all_active').")
622
+ self.log_info("No target users identified (or none active for 'all_active').")
623
623
  # If target was specific user but they weren't found active, still try task?
624
624
  # Let's proceed to task creation anyway for specific user case.
625
625
  if target_user != "all_active": target_users_normalized.add(target_user.lower())
@@ -639,8 +639,8 @@ class GuardService(win32serviceutil.ServiceFramework):
639
639
  running_procs_by_user[user_lower] = running_procs_by_user.get(user_lower, 0) + 1
640
640
  except Exception: pass
641
641
  except Exception as e:
642
- log_error(f"Error checking existing processes: {e}")
643
- log_info(f"Users currently running OOTB: {running_procs_by_user}")
642
+ self.log_error(f"Error checking existing processes: {e}")
643
+ self.log_info(f"Users currently running OOTB: {running_procs_by_user}")
644
644
 
645
645
  # --- Process each target user ---
646
646
  for user in target_users_normalized:
@@ -648,14 +648,14 @@ class GuardService(win32serviceutil.ServiceFramework):
648
648
  immediate_start_status = "start_not_attempted"
649
649
  token = None # Ensure token is reset/defined
650
650
 
651
- log_info(f"Processing start for user '{user}'...")
651
+ self.log_info(f"Processing start for user '{user}'...")
652
652
 
653
653
  # 1. Always try to create/update the scheduled task
654
654
  try:
655
- task_created = create_or_update_logon_task(user, self.ootb_command, self.python_exe)
655
+ task_created = self.create_or_update_logon_task(user, self.ootb_command, self.python_exe)
656
656
  task_created_status = "task_success" if task_created else "task_failed"
657
657
  except Exception as task_err:
658
- log_error(f"Exception creating/updating scheduled task for {user}: {task_err}", exc_info=True)
658
+ self.log_error(f"Exception creating/updating scheduled task for {user}: {task_err}", exc_info=True)
659
659
  task_created_status = "task_exception"
660
660
  failed_users.add(user)
661
661
  # Continue to potentially try immediate start IF user is active?
@@ -671,7 +671,7 @@ class GuardService(win32serviceutil.ServiceFramework):
671
671
  if is_active:
672
672
  if not is_running:
673
673
  immediate_start_status = "start_attempted"
674
- log_info(f"User '{user}' is active and not running OOTB. Attempting immediate start...")
674
+ self.log_info(f"User '{user}' is active and not running OOTB. Attempting immediate start...")
675
675
  try:
676
676
  session_id = active_sessions[user]
677
677
  token = win32ts.WTSQueryUserToken(session_id)
@@ -686,21 +686,21 @@ class GuardService(win32serviceutil.ServiceFramework):
686
686
  token, self.python_exe, self.ootb_command,
687
687
  None, None, False, creation_flags, env, user_profile_dir, startup
688
688
  )
689
- log_info(f"CreateProcessAsUser call succeeded for user '{user}' (PID: {dwPid}). Checking existence...")
689
+ self.log_info(f"CreateProcessAsUser call succeeded for user '{user}' (PID: {dwPid}). Checking existence...")
690
690
  win32api.CloseHandle(hProcess)
691
691
  win32api.CloseHandle(hThread)
692
692
 
693
693
  time.sleep(1)
694
694
  if psutil.pid_exists(dwPid):
695
- log_info(f"Immediate start succeeded for user '{user}' (PID {dwPid}).")
695
+ self.log_info(f"Immediate start succeeded for user '{user}' (PID {dwPid}).")
696
696
  immediate_start_status = "start_success"
697
697
  else:
698
- log_error(f"Immediate start failed for user '{user}': Process {dwPid} exited immediately.")
698
+ self.log_error(f"Immediate start failed for user '{user}': Process {dwPid} exited immediately.")
699
699
  immediate_start_status = "start_failed_exited"
700
700
  failed_users.add(user)
701
701
 
702
702
  except Exception as proc_err:
703
- log_error(f"Exception during immediate start for user '{user}': {proc_err}", exc_info=True)
703
+ self.log_error(f"Exception during immediate start for user '{user}': {proc_err}", exc_info=True)
704
704
  immediate_start_status = "start_failed_exception"
705
705
  failed_users.add(user)
706
706
  finally:
@@ -708,10 +708,10 @@ class GuardService(win32serviceutil.ServiceFramework):
708
708
  try: win32api.CloseHandle(token)
709
709
  except: pass
710
710
  else: # User is active but already running
711
- log_info(f"User '{user}' is active but OOTB is already running. Skipping immediate start.")
711
+ self.log_info(f"User '{user}' is active but OOTB is already running. Skipping immediate start.")
712
712
  immediate_start_status = "start_skipped_already_running"
713
713
  else: # User is not active
714
- log_info(f"User '{user}' is not active. Skipping immediate start (task created/updated).")
714
+ self.log_info(f"User '{user}' is not active. Skipping immediate start (task created/updated).")
715
715
  immediate_start_status = "start_skipped_inactive"
716
716
 
717
717
  # Record final results for this user
@@ -732,7 +732,7 @@ class GuardService(win32serviceutil.ServiceFramework):
732
732
  else:
733
733
  final_status = "failed_task_user_inactive"
734
734
 
735
- log_info(f"Finished starting OOTB. Overall Status: {final_status}. Results: {start_results}")
735
+ self.log_info(f"Finished starting OOTB. Overall Status: {final_status}. Results: {start_results}")
736
736
  # Return detailed results as a JSON string for easier parsing/logging server-side
737
737
  try:
738
738
  details = json.dumps(start_results)
@@ -741,13 +741,13 @@ class GuardService(win32serviceutil.ServiceFramework):
741
741
  return f"{final_status}::{details}"
742
742
 
743
743
  except Exception as e:
744
- log_error(f"Error during combined start OOTB process: {e}", exc_info=True)
744
+ self.log_error(f"Error during combined start OOTB process: {e}", exc_info=True)
745
745
  return "failed_exception"
746
746
 
747
747
  # --- Main Execution Block ---
748
748
  if __name__ == '__main__':
749
749
  if len(sys.argv) > 1 and sys.argv[1] == 'debug':
750
- log_info("Starting service in debug mode...")
750
+ self.log_info("Starting service in debug mode...")
751
751
  print(f"Running Flask server via Waitress on {_LISTEN_HOST}:{_LISTEN_PORT} for debugging...")
752
752
  print("Service logic (command processing) will NOT run in this mode.")
753
753
  print("Use this primarily to test the '/command' endpoint receiving POSTs.")