Added run as service
This commit is contained in:
parent
fb012de76b
commit
fe3b5e72d1
91
README.md
91
README.md
|
|
@ -71,7 +71,97 @@ Combos are configurable in `config.json → hotkeys` using pynput syntax (`<ctrl
|
|||
|
||||
---
|
||||
|
||||
## Run as a Windows Service (no interactive session required)
|
||||
|
||||
ICEYOU can run as a **Windows service** so it starts at boot, has no console
|
||||
window, and keeps running across logoff/logon — i.e. *without* you having to
|
||||
launch it from an interactive session.
|
||||
|
||||
### How it works (important)
|
||||
|
||||
ICEYOU draws a full-screen lock-out, a tray icon, and installs low-level
|
||||
keyboard hooks. Windows runs services in **Session 0**, which is *isolated from
|
||||
the interactive desktop* — anything a Session-0 process draws is invisible to
|
||||
the logged-in user. So a plain "run the app as a service" approach would lock
|
||||
nothing the user could see.
|
||||
|
||||
`iceyou_service.py` installs a **supervisor service** that runs as `LocalSystem`
|
||||
and **launches the ICEYOU GUI agent into the active interactive session** (via
|
||||
`CreateProcessAsUser`). The supervisor:
|
||||
|
||||
- starts the agent the moment a user logs in,
|
||||
- relaunches it if the active session changes (fast user switching) or the
|
||||
agent exits,
|
||||
- terminates the agent and idles when no one is logged in (nothing to protect),
|
||||
- writes diagnostics to `iceyou_service.log`.
|
||||
|
||||
The service binary is registered as the venv's `python.exe` running
|
||||
`iceyou_service.py` directly (no `pythonservice.exe` host is used).
|
||||
|
||||
### Install
|
||||
|
||||
Run these commands in an **elevated (Administrator) PowerShell**:
|
||||
|
||||
```powershell
|
||||
cd F:\ICEYOU
|
||||
|
||||
# Install the service (starts automatically at boot)
|
||||
python iceyou_service.py install
|
||||
|
||||
# Start it now
|
||||
python iceyou_service.py start
|
||||
```
|
||||
|
||||
### Manage
|
||||
|
||||
```powershell
|
||||
python iceyou_service.py start # start the service
|
||||
python iceyou_service.py stop # stop the service (and its agent)
|
||||
python iceyou_service.py restart # restart
|
||||
python iceyou_service.py remove # uninstall the service
|
||||
```
|
||||
|
||||
You can also control it from `services.msc` (look for **ICEYOU Anti-Intrusion
|
||||
Monitor**) or with `sc.exe`.
|
||||
|
||||
### Verification
|
||||
|
||||
After installation, check the registration and status:
|
||||
|
||||
```powershell
|
||||
sc.exe qc ICEYOUMonitor
|
||||
Get-Service ICEYOUMonitor
|
||||
Get-Content F:\ICEYOU\iceyou_service.log -Tail 30
|
||||
```
|
||||
|
||||
The `BINARY_PATH_NAME` should show the venv `python.exe` running
|
||||
`iceyou_service.py`.
|
||||
|
||||
### Notes & limitations
|
||||
|
||||
- Must be installed from an **elevated** prompt (Administrator).
|
||||
- With **no user logged in** there is no desktop, so the white screen cannot
|
||||
appear — the service waits and starts the agent at the next logon.
|
||||
- The GUI agent runs as the **logged-in user** and uses that user's
|
||||
`config.json` and camera.
|
||||
- Antivirus may flag the service (keyboard hooks + session switching). Whitelist
|
||||
the project folder if needed.
|
||||
- This replaces the lighter **Add to Windows Startup** (`startup.py`) option.
|
||||
|
||||
---
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
- **Error 1053** ("did not respond in a timely fashion"): Usually means the
|
||||
registered binary path is wrong or the module name is missing. Re-run
|
||||
`python iceyou_service.py remove` then `install`.
|
||||
- No log file appears: Check `iceyou_service_startup_error.log` for import-time
|
||||
crashes.
|
||||
- Service starts then immediately stops: The most common cause is an incorrect
|
||||
`binPath`. Use `sc.exe qc ICEYOUMonitor` to verify it points to the venv
|
||||
`python.exe`.
|
||||
|
||||
---
|
||||
|
||||
## Tray Menu
|
||||
|
||||
|
|
@ -311,6 +401,7 @@ To harden against CTRL+ALT+DEL → Task Manager, run as Administrator and set th
|
|||
```
|
||||
ICEYOU/
|
||||
├── main.py Tray app entrypoint
|
||||
├── iceyou_service.py Windows service supervisor (launches agent into active session)
|
||||
├── face_enrollment.py Standalone face capture + LBPH training
|
||||
├── test_email.py CLI SMTP sanity check
|
||||
├── config.json Runtime settings (gitignored)
|
||||
|
|
|
|||
481
iceyou_service.py
Normal file
481
iceyou_service.py
Normal file
|
|
@ -0,0 +1,481 @@
|
|||
"""ICEYOU Windows Service (supervisor / session launcher).
|
||||
|
||||
Why a *supervisor* service instead of running the app directly as a service?
|
||||
--------------------------------------------------------------------------
|
||||
ICEYOU is a GUI lock-down application (full-screen white-out, password/face
|
||||
prompt, tray icon, low-level keyboard hooks). A normal Windows service runs in
|
||||
**Session 0**, which—since Windows Vista—is isolated from the interactive
|
||||
desktop. Any window a Session-0 process creates is invisible to the logged-in
|
||||
user, so the white-screen lockout would never appear.
|
||||
|
||||
This service therefore does NOT draw any UI itself. Instead it:
|
||||
|
||||
1. Runs as LocalSystem, auto-starts at boot, has no console window, and keeps
|
||||
running across logoff/logon (i.e. "without an interactive session").
|
||||
2. Detects the currently active interactive console session.
|
||||
3. Launches the ICEYOU GUI agent (``pythonw main.py``) *inside that user's
|
||||
session* using CreateProcessAsUser, so the white screen / tray render
|
||||
where the user can actually see them.
|
||||
4. Supervises the child: if the user logs off and another logs on, or the
|
||||
agent dies, it relaunches the agent in the new active session.
|
||||
|
||||
When no user is logged in there is nothing to protect and no desktop to draw
|
||||
on, so the service simply waits and starts the agent the moment someone logs
|
||||
in.
|
||||
|
||||
Usage (run an *elevated / Administrator* PowerShell):
|
||||
|
||||
# one-time: register the pywin32 service host (if not already done)
|
||||
python .venv\\Scripts\\pywin32_postinstall.py -install
|
||||
|
||||
# install + set to start automatically at boot
|
||||
python iceyou_service.py --startup auto install
|
||||
|
||||
# control
|
||||
python iceyou_service.py start
|
||||
python iceyou_service.py stop
|
||||
python iceyou_service.py restart
|
||||
python iceyou_service.py remove
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Paths (resolved relative to this file so the service is install-location
|
||||
# independent). This file is expected to live at the project root next to
|
||||
# main.py.
|
||||
# ---------------------------------------------------------------------------
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def _bootstrap_pywin32_path() -> None:
|
||||
"""Ensure the pywin32 sub-packages are importable.
|
||||
|
||||
When Windows starts the service it runs ``pythonservice.exe`` with a
|
||||
minimal ``sys.path`` that may NOT include the pywin32 directories
|
||||
(``site-packages\\win32``, ``win32\\lib``, ``Pythonwin``) where
|
||||
``servicemanager``/``win32event``/... actually live. Importing them would
|
||||
then fail with ``ModuleNotFoundError: No module named 'servicemanager'``
|
||||
and the service times out (error 1053). We add those directories (and the
|
||||
DLL directory) up front so the host can import them regardless of how it
|
||||
was launched.
|
||||
"""
|
||||
# Always put the project root first so "import iceyou_service" and any
|
||||
# "from iceyou.xxx" imports succeed even if the service host's cwd or
|
||||
# initial sys.path does not contain the directory where this file lives.
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
site_dirs = []
|
||||
# The project's virtualenv is the primary, reliable location.
|
||||
site_dirs.append(PROJECT_ROOT / ".venv" / "Lib" / "site-packages")
|
||||
try:
|
||||
import site
|
||||
for p in site.getsitepackages():
|
||||
site_dirs.append(Path(p))
|
||||
except Exception:
|
||||
pass
|
||||
# Fallback: site-packages relative to the running interpreter.
|
||||
site_dirs.append(Path(sys.prefix) / "Lib" / "site-packages")
|
||||
|
||||
seen = set()
|
||||
for sp in site_dirs:
|
||||
if not sp or sp in seen or not sp.is_dir():
|
||||
continue
|
||||
seen.add(sp)
|
||||
for sub in ("win32", os.path.join("win32", "lib"), "Pythonwin"):
|
||||
d = sp / sub
|
||||
if d.is_dir() and str(d) not in sys.path:
|
||||
sys.path.append(str(d))
|
||||
dll_dir = sp / "pywin32_system32"
|
||||
if dll_dir.is_dir():
|
||||
os.environ["PATH"] = str(dll_dir) + os.pathsep + os.environ.get("PATH", "")
|
||||
try:
|
||||
os.add_dll_directory(str(dll_dir))
|
||||
except (AttributeError, OSError):
|
||||
pass
|
||||
|
||||
|
||||
def _early_crash_dump(exc: BaseException) -> None:
|
||||
"""Write any import-time exception to a file so we can see it even if
|
||||
the service host never reaches our normal logger."""
|
||||
try:
|
||||
with open(PROJECT_ROOT / "iceyou_service_startup_error.log", "a", encoding="utf-8") as f:
|
||||
import traceback
|
||||
f.write("\n" + "=" * 80 + "\n")
|
||||
f.write("ICEYOU service startup crash at " + __import__("datetime").datetime.now().isoformat() + "\n")
|
||||
f.write("".join(traceback.format_exception(type(exc), exc, exc.__traceback__)))
|
||||
f.write("=" * 80 + "\n")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
_bootstrap_pywin32_path()
|
||||
|
||||
import servicemanager
|
||||
import win32event
|
||||
import win32service
|
||||
import win32serviceutil
|
||||
import win32ts
|
||||
import win32con
|
||||
import win32process
|
||||
import win32profile
|
||||
import win32api
|
||||
except Exception as _bootstrap_exc: # pragma: no cover - only triggered on host failure
|
||||
_early_crash_dump(_bootstrap_exc)
|
||||
raise
|
||||
|
||||
MAIN_SCRIPT = PROJECT_ROOT / "main.py"
|
||||
LOG_FILE = PROJECT_ROOT / "iceyou_service.log"
|
||||
|
||||
# How often (seconds) the supervisor re-checks the active session / child.
|
||||
POLL_INTERVAL = 3.0
|
||||
|
||||
# Sentinel returned by WTSGetActiveConsoleSessionId when no one is at the
|
||||
# physical console (e.g. nobody logged in, or a fast-user-switch transition).
|
||||
NO_ACTIVE_SESSION = 0xFFFFFFFF
|
||||
|
||||
|
||||
def _find_pythonw() -> str:
|
||||
"""Locate the windowed Python interpreter used to run the GUI agent.
|
||||
|
||||
Prefer the project's virtualenv (so the agent gets the right packages),
|
||||
then a pythonw next to the current interpreter, then plain python.
|
||||
"""
|
||||
candidates = [
|
||||
PROJECT_ROOT / ".venv" / "Scripts" / "pythonw.exe",
|
||||
Path(sys.executable).with_name("pythonw.exe"),
|
||||
PROJECT_ROOT / ".venv" / "Scripts" / "python.exe",
|
||||
Path(sys.executable),
|
||||
]
|
||||
for c in candidates:
|
||||
if c.exists():
|
||||
return str(c)
|
||||
# Last resort: hope pythonw is on PATH.
|
||||
return "pythonw.exe"
|
||||
|
||||
|
||||
def _setup_logging() -> logging.Logger:
|
||||
logger = logging.getLogger("iceyou.service")
|
||||
logger.setLevel(logging.INFO)
|
||||
if not logger.handlers:
|
||||
try:
|
||||
handler = logging.FileHandler(LOG_FILE, encoding="utf-8")
|
||||
except Exception:
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(
|
||||
logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
|
||||
)
|
||||
logger.addHandler(handler)
|
||||
return logger
|
||||
|
||||
|
||||
class ICEYOUService(win32serviceutil.ServiceFramework):
|
||||
_svc_name_ = "ICEYOUMonitor"
|
||||
_svc_display_name_ = "ICEYOU Anti-Intrusion Monitor"
|
||||
_svc_description_ = (
|
||||
"Supervises the ICEYOU anti-intrusion GUI agent, launching it into the "
|
||||
"active interactive desktop session so the screen lock-out and tray "
|
||||
"icon are visible to the logged-in user. Runs without an interactive "
|
||||
"session and survives logoff/logon."
|
||||
)
|
||||
|
||||
# Tell the SCM we want to be notified of console session changes so we can
|
||||
# react immediately to logon/unlock rather than only via polling.
|
||||
def __init__(self, args):
|
||||
super().__init__(args)
|
||||
# Both auto-reset, initially non-signalled.
|
||||
self._stop_event = win32event.CreateEvent(None, 0, 0, None)
|
||||
self._wake_event = win32event.CreateEvent(None, 0, 0, None)
|
||||
self._stopping = False
|
||||
self._child_handle = None # PyHANDLE of the running agent process
|
||||
self._child_session = None # session id the agent was launched in
|
||||
self.log = _setup_logging()
|
||||
|
||||
# ---- SCM control handlers ------------------------------------------
|
||||
def SvcStop(self):
|
||||
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
|
||||
self.log.info("Stop requested; terminating agent and shutting down.")
|
||||
self._stopping = True
|
||||
self._terminate_child()
|
||||
win32event.SetEvent(self._stop_event)
|
||||
|
||||
def SvcDoRun(self):
|
||||
servicemanager.LogMsg(
|
||||
servicemanager.EVENTLOG_INFORMATION_TYPE,
|
||||
servicemanager.PYS_SERVICE_STARTED,
|
||||
(self._svc_name_, ""),
|
||||
)
|
||||
# MUST report RUNNING promptly (< ~30 s) or SCM times out with error 1053.
|
||||
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
|
||||
self.log.info("ICEYOU service starting. project_root=%s", PROJECT_ROOT)
|
||||
try:
|
||||
self._run_supervisor()
|
||||
except Exception: # never let the service crash silently
|
||||
self.log.exception("Supervisor loop crashed.")
|
||||
self.log.info("ICEYOU service stopped.")
|
||||
|
||||
def SvcOtherEx(self, control, event_type, data):
|
||||
# React promptly to logon / unlock by nudging the supervisor loop.
|
||||
if control == win32service.SERVICE_CONTROL_SESSIONCHANGE:
|
||||
self.log.info("Session change event: type=%s", event_type)
|
||||
win32event.SetEvent(self._wake_event)
|
||||
|
||||
# ---- Supervisor loop ------------------------------------------------
|
||||
def _run_supervisor(self):
|
||||
pythonw = _find_pythonw()
|
||||
self.log.info("GUI agent interpreter: %s", pythonw)
|
||||
self.log.info("GUI agent script: %s", MAIN_SCRIPT)
|
||||
|
||||
while True:
|
||||
# Wake on: stop request, session change, or poll timeout.
|
||||
rc = win32event.WaitForMultipleObjects(
|
||||
(self._stop_event, self._wake_event),
|
||||
False,
|
||||
int(POLL_INTERVAL * 1000),
|
||||
)
|
||||
if rc == win32event.WAIT_OBJECT_0 or self._stopping:
|
||||
break # stop event signalled
|
||||
|
||||
try:
|
||||
self._ensure_agent(pythonw)
|
||||
except Exception:
|
||||
self.log.exception("Error while ensuring agent is running.")
|
||||
|
||||
def _ensure_agent(self, pythonw: str):
|
||||
"""Make sure exactly one agent is running in the active session."""
|
||||
session_id = win32ts.WTSGetActiveConsoleSessionId()
|
||||
|
||||
if session_id == NO_ACTIVE_SESSION:
|
||||
# No one at the console: nothing to protect. Make sure no stale
|
||||
# agent lingers and wait for a logon.
|
||||
if self._child_handle is not None:
|
||||
self.log.info("No active session; terminating stale agent.")
|
||||
self._terminate_child()
|
||||
return
|
||||
|
||||
child_alive = self._child_alive()
|
||||
|
||||
if child_alive and self._child_session == session_id:
|
||||
return # already running in the right session
|
||||
|
||||
if child_alive and self._child_session != session_id:
|
||||
# Active session changed (e.g. fast user switch). Move the agent.
|
||||
self.log.info(
|
||||
"Active session changed %s -> %s; relaunching agent.",
|
||||
self._child_session, session_id,
|
||||
)
|
||||
self._terminate_child()
|
||||
|
||||
self._launch_agent(pythonw, session_id)
|
||||
|
||||
def _child_alive(self) -> bool:
|
||||
if self._child_handle is None:
|
||||
return False
|
||||
rc = win32event.WaitForSingleObject(self._child_handle, 0)
|
||||
return rc == win32event.WAIT_TIMEOUT # still running
|
||||
|
||||
def _terminate_child(self):
|
||||
if self._child_handle is None:
|
||||
return
|
||||
try:
|
||||
if self._child_alive():
|
||||
win32process.TerminateProcess(self._child_handle, 0)
|
||||
except Exception:
|
||||
self.log.exception("Failed to terminate agent process.")
|
||||
finally:
|
||||
try:
|
||||
win32api.CloseHandle(self._child_handle)
|
||||
except Exception:
|
||||
pass
|
||||
self._child_handle = None
|
||||
self._child_session = None
|
||||
|
||||
def _launch_agent(self, pythonw: str, session_id: int):
|
||||
"""Spawn the GUI agent inside the given interactive session."""
|
||||
user_token = None
|
||||
env = None
|
||||
try:
|
||||
# Requires SE_TCB_NAME, which LocalSystem holds.
|
||||
user_token = win32ts.WTSQueryUserToken(session_id)
|
||||
|
||||
try:
|
||||
env = win32profile.CreateEnvironmentBlock(user_token, False)
|
||||
creation_flags = (
|
||||
win32con.CREATE_NO_WINDOW
|
||||
| win32con.CREATE_UNICODE_ENVIRONMENT
|
||||
)
|
||||
except Exception:
|
||||
self.log.warning(
|
||||
"CreateEnvironmentBlock failed; launching without env block."
|
||||
)
|
||||
env = None
|
||||
creation_flags = win32con.CREATE_NO_WINDOW
|
||||
|
||||
startup = win32process.STARTUPINFO()
|
||||
# Target the interactive default desktop so windows are visible.
|
||||
startup.lpDesktop = "winsta0\\default"
|
||||
|
||||
cmdline = f'"{pythonw}" "{MAIN_SCRIPT}"'
|
||||
|
||||
proc_info = win32process.CreateProcessAsUser(
|
||||
user_token, # hToken
|
||||
None, # appName (use cmdline)
|
||||
cmdline, # commandLine
|
||||
None, # processAttributes
|
||||
None, # threadAttributes
|
||||
False, # inheritHandles
|
||||
creation_flags,
|
||||
env, # environment
|
||||
str(PROJECT_ROOT), # currentDirectory
|
||||
startup,
|
||||
)
|
||||
h_process, h_thread, pid, tid = proc_info
|
||||
try:
|
||||
win32api.CloseHandle(h_thread)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self._child_handle = h_process
|
||||
self._child_session = session_id
|
||||
self.log.info(
|
||||
"Launched ICEYOU agent in session %s (pid=%s).", session_id, pid
|
||||
)
|
||||
except Exception:
|
||||
self.log.exception(
|
||||
"Failed to launch agent in session %s.", session_id
|
||||
)
|
||||
finally:
|
||||
if user_token is not None:
|
||||
try:
|
||||
win32api.CloseHandle(user_token)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def _service_command_line() -> str:
|
||||
"""Full command the SCM should launch to run this service.
|
||||
|
||||
We bypass pywin32's ``pythonservice.exe`` host entirely and register the
|
||||
venv's ``python.exe`` running THIS script directly. When the SCM starts it
|
||||
with no extra arguments, ``main()`` calls ``StartServiceCtrlDispatcher`` and
|
||||
connects to the SCM. This avoids all the host-exe path/module problems that
|
||||
caused the silent error 1053.
|
||||
"""
|
||||
py = PROJECT_ROOT / ".venv" / "Scripts" / "python.exe"
|
||||
if not py.exists():
|
||||
py = Path(sys.executable)
|
||||
script = Path(__file__).resolve()
|
||||
return f'"{py}" "{script}"'
|
||||
|
||||
|
||||
def _install_service(startup_type: int = win32service.SERVICE_AUTO_START) -> None:
|
||||
cmdline = _service_command_line()
|
||||
hscm = win32service.OpenSCManager(None, None, win32service.SC_MANAGER_ALL_ACCESS)
|
||||
try:
|
||||
# Remove a stale registration first so we always get a clean binPath.
|
||||
try:
|
||||
old = win32service.OpenService(
|
||||
hscm, ICEYOUService._svc_name_, win32service.SERVICE_ALL_ACCESS
|
||||
)
|
||||
win32service.DeleteService(old)
|
||||
win32service.CloseServiceHandle(old)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
hs = win32service.CreateService(
|
||||
hscm,
|
||||
ICEYOUService._svc_name_,
|
||||
ICEYOUService._svc_display_name_,
|
||||
win32service.SERVICE_ALL_ACCESS,
|
||||
win32service.SERVICE_WIN32_OWN_PROCESS,
|
||||
startup_type,
|
||||
win32service.SERVICE_ERROR_NORMAL,
|
||||
cmdline,
|
||||
None, # load order group
|
||||
0, # tag id
|
||||
None, # dependencies
|
||||
None, # account (LocalSystem)
|
||||
None, # password
|
||||
)
|
||||
try:
|
||||
win32service.ChangeServiceConfig2(
|
||||
hs,
|
||||
win32service.SERVICE_CONFIG_DESCRIPTION,
|
||||
ICEYOUService._svc_description_,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
win32service.CloseServiceHandle(hs)
|
||||
print(f"Service {ICEYOUService._svc_name_} installed (auto-start).")
|
||||
print(f" binPath = {cmdline}")
|
||||
finally:
|
||||
win32service.CloseServiceHandle(hscm)
|
||||
|
||||
|
||||
def _remove_service() -> None:
|
||||
try:
|
||||
win32serviceutil.StopService(ICEYOUService._svc_name_)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
win32serviceutil.RemoveService(ICEYOUService._svc_name_)
|
||||
print(f"Service {ICEYOUService._svc_name_} removed.")
|
||||
except Exception as e:
|
||||
print(f"Remove failed: {e}")
|
||||
|
||||
|
||||
def _run_as_service() -> None:
|
||||
"""Entry point used when the SCM launches us as a service process."""
|
||||
try:
|
||||
servicemanager.Initialize()
|
||||
servicemanager.PrepareToHostSingle(ICEYOUService)
|
||||
servicemanager.StartServiceCtrlDispatcher()
|
||||
except Exception as exc:
|
||||
_early_crash_dump(exc)
|
||||
raise
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) == 1:
|
||||
# No args => launched by the Service Control Manager.
|
||||
_run_as_service()
|
||||
return
|
||||
|
||||
args_lower = [a.lower() for a in sys.argv[1:]]
|
||||
|
||||
if "install" in args_lower:
|
||||
startup = (
|
||||
win32service.SERVICE_DISABLED
|
||||
if "disabled" in args_lower
|
||||
else win32service.SERVICE_AUTO_START
|
||||
)
|
||||
_install_service(startup)
|
||||
elif "remove" in args_lower or "uninstall" in args_lower:
|
||||
_remove_service()
|
||||
elif "start" in args_lower:
|
||||
win32serviceutil.StartService(ICEYOUService._svc_name_)
|
||||
print("Service start requested.")
|
||||
elif "stop" in args_lower:
|
||||
win32serviceutil.StopService(ICEYOUService._svc_name_)
|
||||
print("Service stop requested.")
|
||||
elif "restart" in args_lower:
|
||||
try:
|
||||
win32serviceutil.StopService(ICEYOUService._svc_name_)
|
||||
except Exception:
|
||||
pass
|
||||
win32serviceutil.StartService(ICEYOUService._svc_name_)
|
||||
print("Service restarted.")
|
||||
else:
|
||||
# Anything else: defer to pywin32's standard handler.
|
||||
win32serviceutil.HandleCommandLine(ICEYOUService)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user