feat: Windows 服务 安装,启动,停止 功能
This commit is contained in:
612
scientific_surfing/service_manager.py
Normal file
612
scientific_surfing/service_manager.py
Normal file
@ -0,0 +1,612 @@
|
||||
"""
|
||||
Cross-platform service manager for installing, uninstalling, and restarting services.
|
||||
|
||||
This module provides a unified interface for managing system services across
|
||||
Windows, Linux, and macOS operating systems.
|
||||
"""
|
||||
|
||||
from dataclasses import asdict
|
||||
import os
|
||||
import platform
|
||||
import subprocess
|
||||
import ctypes
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Optional, Protocol
|
||||
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
from scientific_surfing.storage import StorageManager
|
||||
|
||||
|
||||
class ServiceConfig(BaseModel):
|
||||
"""Configuration model for service installation."""
|
||||
name: str = Field(..., description="Name of the service")
|
||||
executable_path: Path = Field(..., description="Path to the service executable")
|
||||
description: Optional[str] = Field(None, description="Service description")
|
||||
args: Optional[str] = Field(None, description="Command line arguments for the service")
|
||||
|
||||
@field_validator('name')
|
||||
@classmethod
|
||||
def validate_name(cls, v: str) -> str:
|
||||
"""Validate service name format."""
|
||||
if not v or not v.strip():
|
||||
raise ValueError("Service name cannot be empty")
|
||||
if ' ' in v.strip():
|
||||
raise ValueError("Service name cannot contain spaces")
|
||||
return v.strip()
|
||||
|
||||
@field_validator('executable_path')
|
||||
@classmethod
|
||||
def validate_executable_path(cls, v: Path) -> Path:
|
||||
"""Validate executable path exists and is executable."""
|
||||
if not v.exists():
|
||||
raise ValueError(f"Executable path does not exist: {v}")
|
||||
if not v.is_file():
|
||||
raise ValueError(f"Path is not a file: {v}")
|
||||
if not os.access(v, os.X_OK):
|
||||
raise ValueError(f"File is not executable: {v}")
|
||||
return v.resolve()
|
||||
|
||||
|
||||
class ServiceManagerProtocol(Protocol):
|
||||
"""Protocol defining the interface for service managers."""
|
||||
config_dir: str
|
||||
|
||||
def __init__(self, config_dir: str):
|
||||
self.config_dir = config_dir
|
||||
|
||||
def install(self, config: ServiceConfig) -> None:
|
||||
"""Install a service with the given configuration."""
|
||||
...
|
||||
|
||||
def uninstall(self, name: str) -> None:
|
||||
"""Uninstall a service by name."""
|
||||
...
|
||||
|
||||
def start(self, name: str) -> None:
|
||||
"""Start a service by name."""
|
||||
...
|
||||
|
||||
def stop(self, name: str) -> None:
|
||||
"""Stop a service by name."""
|
||||
...
|
||||
|
||||
def restart(self, name: str) -> None:
|
||||
"""Restart a service by name."""
|
||||
...
|
||||
|
||||
|
||||
class WindowsServiceManager(ServiceManagerProtocol):
|
||||
"""Windows-specific service manager using sc.exe."""
|
||||
|
||||
@staticmethod
|
||||
def _is_admin() -> bool:
|
||||
"""Check if the current process has administrator privileges."""
|
||||
try:
|
||||
return ctypes.windll.shell32.IsUserAnAdmin()
|
||||
except:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _format_error_message(operation: str, service_name: str, error: str) -> str:
|
||||
"""Format a user-friendly error message for service operations."""
|
||||
if "access is denied" in error.lower() or "5" in error:
|
||||
return (
|
||||
f"Failed to {operation} service '{service_name}': Access denied.\n\n"
|
||||
f"Administrator privileges are required to {operation} Windows services.\n\n"
|
||||
f"Solutions:\n"
|
||||
f"• Run this script as administrator (right-click → 'Run as administrator')\n"
|
||||
f"• Open an elevated Command Prompt and run the command manually\n"
|
||||
f"• Ensure User Account Control (UAC) is enabled and accept the prompt"
|
||||
)
|
||||
elif "1060" in error:
|
||||
return (
|
||||
f"Failed to {operation} service '{service_name}': Service not found.\n\n"
|
||||
f"The specified service does not exist. Check the service name and try again."
|
||||
)
|
||||
elif "1062" in error and operation in ["stop", "restart"]:
|
||||
return (
|
||||
f"Service '{service_name}' is not currently running.\n\n"
|
||||
f"This is not an error - the service was already stopped."
|
||||
)
|
||||
else:
|
||||
return f"Failed to {operation} service '{service_name}': {error}"
|
||||
|
||||
@staticmethod
|
||||
def _run_as_admin(cmd: list[str], description: str = "Service Management") -> None:
|
||||
"""Run a command with administrator privileges using UAC elevation."""
|
||||
if WindowsServiceManager._is_admin():
|
||||
# Already running as admin, execute directly
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f"Failed to {description.lower()}: {result.stderr}")
|
||||
else:
|
||||
# Provide clear instructions for manual elevation
|
||||
command_str = " ".join(cmd)
|
||||
raise RuntimeError(
|
||||
f"Administrator privileges required to {description.lower()}.\n\n"
|
||||
f"Command: {command_str}\n\n"
|
||||
f"Please do one of the following:\n"
|
||||
f"1. Run this script as administrator (right-click → 'Run as administrator')\n"
|
||||
f"2. Open an elevated Command Prompt and run: {command_str}\n"
|
||||
f"3. Accept the UAC prompt when it appears"
|
||||
)
|
||||
|
||||
def install(self, config: ServiceConfig) -> None:
|
||||
"""Install a Windows service using Python service wrapper."""
|
||||
import tempfile
|
||||
import json
|
||||
from pathlib import Path
|
||||
from .windows_service_wrapper import WindowServiceConfig
|
||||
|
||||
# Check for pywin32 dependency
|
||||
try:
|
||||
import win32serviceutil
|
||||
import win32service
|
||||
import win32event
|
||||
import servicemanager
|
||||
except ImportError:
|
||||
raise RuntimeError(
|
||||
"pywin32 package is required for Windows service support. "
|
||||
"Install it with: pip install pywin32"
|
||||
)
|
||||
|
||||
# Create service configuration for the wrapper
|
||||
|
||||
windows_service_config = WindowServiceConfig(
|
||||
name = "mihomo",
|
||||
display_name = "Scentific Surfing Service",
|
||||
description = "Surfing the Internal scientifically",
|
||||
working_dir = str(config.executable_path.parent),
|
||||
bin_path = str(config.executable_path),
|
||||
args = config.args or '',
|
||||
)
|
||||
|
||||
# Create permanent config file in a stable location
|
||||
config_dir = self.config_dir
|
||||
config_dir.mkdir(parents=True, exist_ok=True)
|
||||
config_file = config_dir / f"{config.name}_config.json"
|
||||
|
||||
with open(config_file, 'w') as f:
|
||||
json.dump(asdict(windows_service_config), f, indent=2)
|
||||
|
||||
# Path to the wrapper script
|
||||
wrapper_script = Path(__file__).parent / "windows_service_wrapper.py"
|
||||
if not wrapper_script.exists():
|
||||
raise RuntimeError(f"Windows service wrapper not found: {wrapper_script}")
|
||||
|
||||
# Build the command to run the Python service wrapper using the proper service class
|
||||
python_exe = sys.executable
|
||||
service_cmd = [
|
||||
python_exe, str(wrapper_script)
|
||||
]
|
||||
|
||||
# Quote the path to handle spaces properly
|
||||
escaped_cmd = " ".join(service_cmd)
|
||||
if ' ' in escaped_cmd:
|
||||
escaped_cmd = f'"{escaped_cmd}"'
|
||||
|
||||
# Create service using sc.exe with the Python wrapper - using proper command format
|
||||
python_exe = sys.executable
|
||||
wrapper_script = Path(__file__).parent / "windows_service_wrapper.py"
|
||||
|
||||
# Build the command with proper quoting for Windows
|
||||
service_cmd = f'"{python_exe}" "{wrapper_script}" "{config_file}"'
|
||||
|
||||
# Create service using sc.exe
|
||||
cmd = [
|
||||
"sc", "create", config.name,
|
||||
"binPath=", service_cmd,
|
||||
"start=", "auto"
|
||||
]
|
||||
|
||||
if config.description:
|
||||
cmd.extend(["DisplayName=", config.description])
|
||||
|
||||
try:
|
||||
self._run_as_admin(cmd, f"install service '{config.name}'")
|
||||
except RuntimeError as e:
|
||||
# Clean up config file on failure
|
||||
try:
|
||||
config_file.unlink(missing_ok=True)
|
||||
except:
|
||||
pass
|
||||
raise RuntimeError(self._format_error_message("install", config.name, str(e)))
|
||||
|
||||
def uninstall(self, name: str) -> None:
|
||||
"""Uninstall a Windows service."""
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
# Stop the service first
|
||||
try:
|
||||
self._run_as_admin(["sc", "stop", name], f"stop service '{name}'")
|
||||
except:
|
||||
# Ignore if service is not running
|
||||
pass
|
||||
|
||||
# Delete the service
|
||||
self._run_as_admin(["sc", "delete", name], f"uninstall service '{name}'")
|
||||
|
||||
# Clean up configuration file
|
||||
config_dir = Path.home() / ".scientific_surfing" / "service_configs"
|
||||
config_file = config_dir / f"{name}_config.json"
|
||||
try:
|
||||
config_file.unlink(missing_ok=True)
|
||||
|
||||
# Remove directory if empty
|
||||
try:
|
||||
config_dir.rmdir()
|
||||
except OSError:
|
||||
pass # Directory not empty
|
||||
except:
|
||||
pass # Ignore cleanup errors
|
||||
|
||||
except RuntimeError as e:
|
||||
raise RuntimeError(self._format_error_message("uninstall", name, str(e)))
|
||||
|
||||
def start(self, name: str) -> None:
|
||||
"""Start a Windows service."""
|
||||
try:
|
||||
self._run_as_admin(["sc", "start", name], f"start service '{name}'")
|
||||
except RuntimeError as e:
|
||||
raise RuntimeError(self._format_error_message("start", name, str(e)))
|
||||
|
||||
def stop(self, name: str) -> None:
|
||||
"""Stop a Windows service."""
|
||||
try:
|
||||
self._run_as_admin(["sc", "stop", name], f"stop service '{name}'")
|
||||
except RuntimeError as e:
|
||||
raise RuntimeError(self._format_error_message("stop", name, str(e)))
|
||||
|
||||
def restart(self, name: str) -> None:
|
||||
"""Restart a Windows service."""
|
||||
try:
|
||||
try:
|
||||
self._run_as_admin(["sc", "stop", name], f"stop service '{name}'")
|
||||
except RuntimeError as e:
|
||||
# Ignore if service is not running (error 1062)
|
||||
if "1062" not in str(e).lower():
|
||||
raise
|
||||
|
||||
self._run_as_admin(["sc", "start", name], f"start service '{name}'")
|
||||
except RuntimeError as e:
|
||||
raise RuntimeError(self._format_error_message("restart", name, str(e)))
|
||||
|
||||
|
||||
class LinuxServiceManager(ServiceManagerProtocol):
|
||||
"""Linux-specific service manager using systemd."""
|
||||
|
||||
def _get_service_file_path(self, name: str) -> Path:
|
||||
"""Get the path to the systemd service file."""
|
||||
return Path("/etc/systemd/system") / f"{name}.service"
|
||||
|
||||
def _create_service_file(self, config: ServiceConfig) -> None:
|
||||
"""Create a systemd service file."""
|
||||
exec_start = f"{config.executable_path}"
|
||||
if config.args:
|
||||
exec_start += f" {config.args}"
|
||||
|
||||
service_content = f"""[Unit]
|
||||
Description={config.description or config.name}
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart={exec_start}
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
User=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
"""
|
||||
|
||||
service_path = self._get_service_file_path(config.name)
|
||||
try:
|
||||
with open(service_path, 'w') as f:
|
||||
f.write(service_content)
|
||||
|
||||
# Set appropriate permissions
|
||||
os.chmod(service_path, 0o644)
|
||||
except OSError as e:
|
||||
raise RuntimeError(f"Failed to create service file: {e}")
|
||||
|
||||
def install(self, config: ServiceConfig) -> None:
|
||||
"""Install a Linux service using systemd."""
|
||||
self._create_service_file(config)
|
||||
|
||||
try:
|
||||
# Reload systemd
|
||||
subprocess.run(["systemctl", "daemon-reload"], capture_output=True, text=True, check=True)
|
||||
|
||||
# Enable and start the service
|
||||
subprocess.run(["systemctl", "enable", config.name], capture_output=True, text=True, check=True)
|
||||
subprocess.run(["systemctl", "start", config.name], capture_output=True, text=True, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to install service: {e.stderr}")
|
||||
|
||||
def uninstall(self, name: str) -> None:
|
||||
"""Uninstall a Linux service."""
|
||||
try:
|
||||
# Stop and disable the service
|
||||
subprocess.run(["systemctl", "stop", name], capture_output=True)
|
||||
subprocess.run(["systemctl", "disable", name], capture_output=True, text=True, check=True)
|
||||
|
||||
# Remove service file
|
||||
service_path = self._get_service_file_path(name)
|
||||
if service_path.exists():
|
||||
service_path.unlink()
|
||||
|
||||
# Reload systemd
|
||||
subprocess.run(["systemctl", "daemon-reload"], capture_output=True, text=True, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to uninstall service: {e.stderr}")
|
||||
|
||||
def start(self, name: str) -> None:
|
||||
"""Start a Linux service."""
|
||||
try:
|
||||
result = subprocess.run(["systemctl", "start", name], capture_output=True, text=True, check=True)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f"Failed to start service: {result.stderr}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to start service: {e.stderr}")
|
||||
|
||||
def stop(self, name: str) -> None:
|
||||
"""Stop a Linux service."""
|
||||
try:
|
||||
result = subprocess.run(["systemctl", "stop", name], capture_output=True, text=True, check=True)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f"Failed to stop service: {result.stderr}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to stop service: {e.stderr}")
|
||||
|
||||
def restart(self, name: str) -> None:
|
||||
"""Restart a Linux service."""
|
||||
try:
|
||||
result = subprocess.run(["systemctl", "restart", name], capture_output=True, text=True, check=True)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f"Failed to restart service: {result.stderr}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to restart service: {e.stderr}")
|
||||
|
||||
|
||||
class MacOSServiceManager(ServiceManagerProtocol):
|
||||
"""macOS-specific service manager using launchd."""
|
||||
|
||||
def _get_launchd_path(self, name: str) -> Path:
|
||||
"""Get the path to the launchd plist file."""
|
||||
return Path("/Library/LaunchDaemons") / f"com.{name}.plist"
|
||||
|
||||
def _create_launchd_plist(self, config: ServiceConfig) -> None:
|
||||
"""Create a launchd plist file."""
|
||||
program_args = [str(config.executable_path)]
|
||||
if config.args:
|
||||
program_args.extend(config.args.split())
|
||||
|
||||
program_args_xml = "\n".join([f" <string>{arg}</string>" for arg in program_args])
|
||||
|
||||
plist_content = f"""<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.{config.name}</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
{program_args_xml}
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/var/log/{config.name}.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/var/log/{config.name}.error.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
"""
|
||||
|
||||
plist_path = self._get_launchd_path(config.name)
|
||||
try:
|
||||
with open(plist_path, 'w') as f:
|
||||
f.write(plist_content)
|
||||
|
||||
# Set appropriate permissions
|
||||
os.chmod(plist_path, 0o644)
|
||||
except OSError as e:
|
||||
raise RuntimeError(f"Failed to create launchd plist: {e}")
|
||||
|
||||
def install(self, config: ServiceConfig) -> None:
|
||||
"""Install a macOS service using launchd."""
|
||||
self._create_launchd_plist(config)
|
||||
|
||||
try:
|
||||
# Load the service
|
||||
plist_path = self._get_launchd_path(config.name)
|
||||
subprocess.run(["launchctl", "load", str(plist_path)], capture_output=True, text=True, check=True)
|
||||
|
||||
# Start the service
|
||||
subprocess.run(["launchctl", "start", f"com.{config.name}"], capture_output=True, text=True, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to install service: {e.stderr}")
|
||||
|
||||
def uninstall(self, name: str) -> None:
|
||||
"""Uninstall a macOS service."""
|
||||
try:
|
||||
# Stop and unload the service
|
||||
subprocess.run(["launchctl", "stop", f"com.{name}"], capture_output=True)
|
||||
|
||||
plist_path = self._get_launchd_path(name)
|
||||
if plist_path.exists():
|
||||
subprocess.run(["launchctl", "unload", str(plist_path)], capture_output=True, text=True, check=True)
|
||||
plist_path.unlink()
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to uninstall service: {e.stderr}")
|
||||
|
||||
def start(self, name: str) -> None:
|
||||
"""Start a macOS service."""
|
||||
try:
|
||||
subprocess.run(["launchctl", "start", f"com.{name}"], capture_output=True, text=True, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to start service: {e.stderr}")
|
||||
|
||||
def stop(self, name: str) -> None:
|
||||
"""Stop a macOS service."""
|
||||
try:
|
||||
subprocess.run(["launchctl", "stop", f"com.{name}"], capture_output=True, text=True, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to stop service: {e.stderr}")
|
||||
|
||||
def restart(self, name: str) -> None:
|
||||
"""Restart a macOS service."""
|
||||
try:
|
||||
subprocess.run(["launchctl", "stop", f"com.{name}"], capture_output=True)
|
||||
subprocess.run(["launchctl", "start", f"com.{name}"], capture_output=True, text=True, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to restart service: {e.stderr}")
|
||||
|
||||
|
||||
class ServiceManager:
|
||||
"""Main service manager that delegates to platform-specific implementations."""
|
||||
|
||||
def __init__(self, config_dir: str) -> None:
|
||||
"""Initialize the service manager with the appropriate platform implementation."""
|
||||
system = platform.system().lower()
|
||||
|
||||
if system == "windows":
|
||||
self._manager: ServiceManagerProtocol = WindowsServiceManager(config_dir)
|
||||
elif system == "linux":
|
||||
self._manager = LinuxServiceManager(config_dir)
|
||||
elif system == "darwin":
|
||||
self._manager = MacOSServiceManager(config_dir)
|
||||
else:
|
||||
raise RuntimeError(f"Unsupported operating system: {system}")
|
||||
|
||||
def install(self, name: str, executable_path: str, description: Optional[str] = None, args: Optional[str] = None) -> None:
|
||||
"""
|
||||
Install a service with the given name and executable path.
|
||||
|
||||
Args:
|
||||
name: Name of the service to install
|
||||
executable_path: Path to the service executable
|
||||
description: Optional description for the service
|
||||
args: Optional command line arguments for the service
|
||||
|
||||
Raises:
|
||||
ValueError: If parameters are invalid
|
||||
RuntimeError: If installation fails
|
||||
"""
|
||||
config = ServiceConfig(
|
||||
name=name,
|
||||
executable_path=Path(executable_path),
|
||||
description=description,
|
||||
args=args
|
||||
)
|
||||
self._manager.install(config)
|
||||
|
||||
def uninstall(self, name: str) -> None:
|
||||
"""
|
||||
Uninstall a service by name.
|
||||
|
||||
Args:
|
||||
name: Name of the service to uninstall
|
||||
|
||||
Raises:
|
||||
RuntimeError: If uninstallation fails
|
||||
"""
|
||||
if not name or not name.strip():
|
||||
raise ValueError("Service name cannot be empty")
|
||||
self._manager.uninstall(name.strip())
|
||||
|
||||
def start(self, name: str) -> None:
|
||||
"""
|
||||
Start a service by name.
|
||||
|
||||
Args:
|
||||
name: Name of the service to start
|
||||
|
||||
Raises:
|
||||
ValueError: If service name is invalid
|
||||
RuntimeError: If start fails
|
||||
"""
|
||||
if not name or not name.strip():
|
||||
raise ValueError("Service name cannot be empty")
|
||||
self._manager.start(name.strip())
|
||||
|
||||
def stop(self, name: str) -> None:
|
||||
"""
|
||||
Stop a service by name.
|
||||
|
||||
Args:
|
||||
name: Name of the service to stop
|
||||
|
||||
Raises:
|
||||
ValueError: If service name is invalid
|
||||
RuntimeError: If stop fails
|
||||
"""
|
||||
if not name or not name.strip():
|
||||
raise ValueError("Service name cannot be empty")
|
||||
self._manager.stop(name.strip())
|
||||
|
||||
def restart(self, name: str) -> None:
|
||||
"""
|
||||
Restart a service by name.
|
||||
|
||||
Args:
|
||||
name: Name of the service to restart
|
||||
|
||||
Raises:
|
||||
ValueError: If service name is invalid
|
||||
RuntimeError: If restart fails
|
||||
"""
|
||||
if not name or not name.strip():
|
||||
raise ValueError("Service name cannot be empty")
|
||||
self._manager.restart(name.strip())
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: python service_manager.py <install|uninstall|start|stop|restart> <service_name> [executable_path] [description] [args]")
|
||||
sys.exit(1)
|
||||
|
||||
action = sys.argv[1]
|
||||
service_name = sys.argv[2]
|
||||
storage = StorageManager()
|
||||
service_manager = ServiceManager(storage.config_dir)
|
||||
|
||||
try:
|
||||
if action == "install":
|
||||
if len(sys.argv) < 4:
|
||||
print("Error: install requires executable_path")
|
||||
sys.exit(1)
|
||||
executable_path = sys.argv[3]
|
||||
description = sys.argv[4] if len(sys.argv) > 4 else None
|
||||
args = sys.argv[5] if len(sys.argv) > 5 else None
|
||||
service_manager.install(service_name, executable_path, description, args)
|
||||
print(f"Service '{service_name}' installed successfully")
|
||||
elif action == "uninstall":
|
||||
service_manager.uninstall(service_name)
|
||||
print(f"Service '{service_name}' uninstalled successfully")
|
||||
elif action == "start":
|
||||
service_manager.start(service_name)
|
||||
print(f"Service '{service_name}' started successfully")
|
||||
elif action == "stop":
|
||||
service_manager.stop(service_name)
|
||||
print(f"Service '{service_name}' stopped successfully")
|
||||
elif action == "restart":
|
||||
service_manager.restart(service_name)
|
||||
print(f"Service '{service_name}' restarted successfully")
|
||||
else:
|
||||
print(f"Error: Unknown action '{action}'")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user