sing-box-cli 0.0.3__tar.gz → 0.0.5__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.
- sing_box_cli-0.0.5/CHANGELOG.md +38 -0
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/PKG-INFO +21 -5
- sing_box_cli-0.0.5/README.md +46 -0
- sing_box_cli-0.0.5/assets/image.png +0 -0
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/pyproject.toml +3 -3
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/src/sing_box_service/__init__.py +1 -1
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/src/sing_box_service/config.py +3 -2
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/src/sing_box_service/main.py +6 -8
- sing_box_cli-0.0.5/src/sing_box_service/service.py +210 -0
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/uv.lock +117 -58
- sing_box_cli-0.0.3/CHANGELOG.md +0 -15
- sing_box_cli-0.0.3/README.md +0 -30
- sing_box_cli-0.0.3/assets/image.png +0 -0
- sing_box_cli-0.0.3/mkdocs.yml +0 -183
- sing_box_cli-0.0.3/src/sing_box_service/service.py +0 -172
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/.github/dependabot.yml +0 -0
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/.github/workflows/main.yml +0 -0
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/.gitignore +0 -0
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/.pre-commit-config.yaml +0 -0
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/.vscode/PythonImportHelper-v2-Completion.json +0 -0
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/.vscode/settings.json +0 -0
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/LICENSE +0 -0
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/scripts/bump.sh +0 -0
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/scripts/format.sh +0 -0
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/scripts/lint.sh +0 -0
- {sing_box_cli-0.0.3 → sing_box_cli-0.0.5}/src/sing_box_service/py.typed +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## 0.0.5 - 2025-01-07
|
|
6
|
+
|
|
7
|
+
### 🚀 Features
|
|
8
|
+
|
|
9
|
+
- Enable windows logs
|
|
10
|
+
|
|
11
|
+
### 🐛 Bug Fixes
|
|
12
|
+
|
|
13
|
+
- Show and update config encode error
|
|
14
|
+
- Windows sys_platform
|
|
15
|
+
- Remove init_service
|
|
16
|
+
- Ignore mypy warn_unused_ignores
|
|
17
|
+
- Replace schtask with raw pwsh
|
|
18
|
+
|
|
19
|
+
### 📚 Documentation
|
|
20
|
+
|
|
21
|
+
- Update README.md
|
|
22
|
+
|
|
23
|
+
### ⚙️ Miscellaneous Tasks
|
|
24
|
+
|
|
25
|
+
- Type ignore
|
|
26
|
+
- Debug bin path
|
|
27
|
+
|
|
28
|
+
## 0.0.3 - 2025-01-07
|
|
29
|
+
|
|
30
|
+
### 🚀 Features
|
|
31
|
+
|
|
32
|
+
- Sing-box service,config,logs
|
|
33
|
+
|
|
34
|
+
### 🐛 Bug Fixes
|
|
35
|
+
|
|
36
|
+
- Cli name
|
|
37
|
+
|
|
38
|
+
<!-- generated by git-cliff -->
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sing-box-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: cross-platform sing-box-service manager.
|
|
5
5
|
Project-URL: Homepage, https://github.com/aittcuszz/sing-box-service
|
|
6
6
|
Project-URL: Issues, https://github.com/atticuszz/sing-box-service/issues
|
|
@@ -51,7 +51,15 @@ cross-platform sing-box service manager
|
|
|
51
51
|
|
|
52
52
|
## Install
|
|
53
53
|
|
|
54
|
-
###
|
|
54
|
+
### uv
|
|
55
|
+
|
|
56
|
+
windows
|
|
57
|
+
|
|
58
|
+
```powershell
|
|
59
|
+
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
linux
|
|
55
63
|
|
|
56
64
|
```bash
|
|
57
65
|
# root
|
|
@@ -62,14 +70,22 @@ curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
|
62
70
|
ln -s $(which uv) /usr/local/bin/uv
|
|
63
71
|
```
|
|
64
72
|
|
|
65
|
-
###
|
|
73
|
+
### sing-box
|
|
66
74
|
|
|
67
75
|
```bash
|
|
68
|
-
|
|
76
|
+
uv tool install sing-box-cli
|
|
69
77
|
```
|
|
70
78
|
|
|
71
79
|
## Run
|
|
72
80
|
|
|
81
|
+
Windows in Admin powershell
|
|
82
|
+
|
|
83
|
+
```powershell
|
|
84
|
+
sing-box-cli --help
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Linux
|
|
88
|
+
|
|
73
89
|
```bash
|
|
74
|
-
sudo
|
|
90
|
+
sudo uv tool run sing-box-cli --help
|
|
75
91
|
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Sing-Box CLI
|
|
2
|
+
|
|
3
|
+
cross-platform sing-box service manager
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
### uv
|
|
10
|
+
|
|
11
|
+
windows
|
|
12
|
+
|
|
13
|
+
```powershell
|
|
14
|
+
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
linux
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# root
|
|
21
|
+
sudo su
|
|
22
|
+
# install as root
|
|
23
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
24
|
+
# create link
|
|
25
|
+
ln -s $(which uv) /usr/local/bin/uv
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### sing-box
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uv tool install sing-box-cli
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Run
|
|
35
|
+
|
|
36
|
+
Windows in Admin powershell
|
|
37
|
+
|
|
38
|
+
```powershell
|
|
39
|
+
sing-box-cli --help
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Linux
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
sudo uv tool run sing-box-cli --help
|
|
46
|
+
```
|
|
Binary file
|
|
@@ -41,7 +41,7 @@ sing-box-cli = "sing_box_service:main" # equal to from python import main; then
|
|
|
41
41
|
|
|
42
42
|
[tool.uv]
|
|
43
43
|
environments = [
|
|
44
|
-
"sys_platform == '
|
|
44
|
+
"sys_platform == 'win32'",
|
|
45
45
|
"sys_platform == 'linux'",
|
|
46
46
|
]
|
|
47
47
|
|
|
@@ -73,7 +73,7 @@ packages = ["src/sing_box_service"]
|
|
|
73
73
|
[tool.mypy]
|
|
74
74
|
strict = true
|
|
75
75
|
exclude = ["venv", ".venv"]
|
|
76
|
-
|
|
76
|
+
warn_unused_ignores=false
|
|
77
77
|
|
|
78
78
|
## Linter and formatter
|
|
79
79
|
[tool.ruff]
|
|
@@ -209,7 +209,7 @@ sort_commits = "oldest"
|
|
|
209
209
|
|
|
210
210
|
|
|
211
211
|
[tool.bumpversion]
|
|
212
|
-
current_version = "0.0.
|
|
212
|
+
current_version = "0.0.5"
|
|
213
213
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
|
214
214
|
serialize = ["{major}.{minor}.{patch}"]
|
|
215
215
|
search = "{current_version}"
|
|
@@ -25,6 +25,7 @@ class Config:
|
|
|
25
25
|
raise FileNotFoundError(f"❌ {bin_filename} not found in PATH")
|
|
26
26
|
|
|
27
27
|
self.bin_path = Path(bin_path)
|
|
28
|
+
print(f"🔧 Using binary: {self.bin_path}")
|
|
28
29
|
|
|
29
30
|
if self.is_windows:
|
|
30
31
|
self.install_dir = Path(os.environ["ProgramFiles"]) / "sing-box"
|
|
@@ -65,7 +66,7 @@ class Config:
|
|
|
65
66
|
try:
|
|
66
67
|
response = requests.get(self.sub_url)
|
|
67
68
|
response.raise_for_status()
|
|
68
|
-
self.config_file.write_text(response.text)
|
|
69
|
+
self.config_file.write_text(response.text, encoding="utf-8")
|
|
69
70
|
if not self.is_windows:
|
|
70
71
|
shutil.chown(self.config_file, user=self.user, group=self.user)
|
|
71
72
|
print("✅ Configuration updated successfully.")
|
|
@@ -84,7 +85,7 @@ class Config:
|
|
|
84
85
|
|
|
85
86
|
def show_config(self) -> None:
|
|
86
87
|
print(f"📄 Configuration file: {self.config_file}")
|
|
87
|
-
print(self.config_file.read_text())
|
|
88
|
+
print(self.config_file.read_text(encoding="utf-8"))
|
|
88
89
|
|
|
89
90
|
def show_subscription(self) -> None:
|
|
90
91
|
if self.sub_url:
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import subprocess
|
|
3
2
|
import sys
|
|
4
3
|
|
|
5
4
|
import typer
|
|
@@ -29,7 +28,7 @@ class SingBoxCLI:
|
|
|
29
28
|
def ensure_root(self) -> None:
|
|
30
29
|
"""https://gist.github.com/RDCH106/fdd419ef7dd803932b16056aab1d2300"""
|
|
31
30
|
try:
|
|
32
|
-
if os.geteuid() != 0:
|
|
31
|
+
if os.geteuid() != 0: # type: ignore
|
|
33
32
|
print("⚠️ This script must be run as root.")
|
|
34
33
|
sys.exit(1)
|
|
35
34
|
except AttributeError:
|
|
@@ -46,7 +45,7 @@ def service_start() -> None:
|
|
|
46
45
|
cli = SingBoxCLI()
|
|
47
46
|
cli.ensure_root()
|
|
48
47
|
cli.config.init_directories()
|
|
49
|
-
cli.service.
|
|
48
|
+
cli.service.create_service()
|
|
50
49
|
cli.service.start()
|
|
51
50
|
print("✅ Service started.")
|
|
52
51
|
print("🔗 Dashboard URL: https://metacubexd.pages.dev/")
|
|
@@ -67,7 +66,9 @@ def service_restart() -> None:
|
|
|
67
66
|
"""Restart sing-box service"""
|
|
68
67
|
cli = SingBoxCLI()
|
|
69
68
|
cli.ensure_root()
|
|
70
|
-
cli.service.
|
|
69
|
+
if not cli.service.check_service():
|
|
70
|
+
cli.service.create_service()
|
|
71
|
+
print("⌛ Service created successfully.")
|
|
71
72
|
cli.service.restart()
|
|
72
73
|
print("✅ Service restarted.")
|
|
73
74
|
print("🔗 Dashboard URL: https://metacubexd.pages.dev/")
|
|
@@ -130,10 +131,7 @@ def config_show() -> None:
|
|
|
130
131
|
def logs() -> None:
|
|
131
132
|
"""Show sing-box service logs"""
|
|
132
133
|
cli = SingBoxCLI()
|
|
133
|
-
|
|
134
|
-
print("⚠️ Log viewing is not supported on Windows")
|
|
135
|
-
else:
|
|
136
|
-
subprocess.run(["journalctl", "-u", "sing-box", "-o", "cat", "-f"])
|
|
134
|
+
cli.service.logs()
|
|
137
135
|
|
|
138
136
|
|
|
139
137
|
def main() -> None:
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from .config import Config
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ServiceManager:
|
|
8
|
+
def __init__(self, config: Config):
|
|
9
|
+
self.config = config
|
|
10
|
+
|
|
11
|
+
def create_service(self) -> None:
|
|
12
|
+
raise NotImplementedError()
|
|
13
|
+
|
|
14
|
+
def check_service(self) -> bool:
|
|
15
|
+
raise NotImplementedError()
|
|
16
|
+
|
|
17
|
+
def start(self) -> None:
|
|
18
|
+
raise NotImplementedError()
|
|
19
|
+
|
|
20
|
+
def stop(self) -> None:
|
|
21
|
+
raise NotImplementedError()
|
|
22
|
+
|
|
23
|
+
def restart(self) -> None:
|
|
24
|
+
raise NotImplementedError()
|
|
25
|
+
|
|
26
|
+
def status(self) -> str:
|
|
27
|
+
raise NotImplementedError()
|
|
28
|
+
|
|
29
|
+
def logs(self) -> None:
|
|
30
|
+
raise NotImplementedError()
|
|
31
|
+
|
|
32
|
+
def disable(self) -> None:
|
|
33
|
+
raise NotImplementedError()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class WindowsServiceManager(ServiceManager):
|
|
37
|
+
def __init__(self, config: Config):
|
|
38
|
+
super().__init__(config)
|
|
39
|
+
self.task_name = "sing-box"
|
|
40
|
+
self.log_file = self.config.install_dir / "sing-box.log"
|
|
41
|
+
|
|
42
|
+
def create_service(self) -> None:
|
|
43
|
+
start_script = self.config.install_dir / "start-singbox.ps1"
|
|
44
|
+
script_content = f"""
|
|
45
|
+
Add-Type -Name Window -Namespace Console -MemberDefinition '
|
|
46
|
+
[DllImport("Kernel32.dll")]
|
|
47
|
+
public static extern IntPtr GetConsoleWindow();
|
|
48
|
+
[DllImport("user32.dll")]
|
|
49
|
+
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
|
|
50
|
+
'
|
|
51
|
+
$console = [Console.Window]::GetConsoleWindow()
|
|
52
|
+
[Console.Window]::ShowWindow($console, 0)
|
|
53
|
+
|
|
54
|
+
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
55
|
+
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
56
|
+
chcp 65001 | Out-Null
|
|
57
|
+
|
|
58
|
+
Set-Location "{self.config.install_dir}"
|
|
59
|
+
|
|
60
|
+
if (Test-Path "{self.log_file}") {{
|
|
61
|
+
Clear-Content "{self.log_file}"
|
|
62
|
+
}}
|
|
63
|
+
|
|
64
|
+
try {{
|
|
65
|
+
& "{self.config.bin_path}" tools synctime -w -C "{self.config.install_dir}" *>&1 | Tee-Object -FilePath "{self.log_file}" -Append
|
|
66
|
+
& "{self.config.bin_path}" run -C "{self.config.install_dir}" *>&1 | Tee-Object -FilePath "{self.log_file}" -Append
|
|
67
|
+
}} catch {{
|
|
68
|
+
$_ | Out-File -FilePath "{self.log_file}" -Append
|
|
69
|
+
}}
|
|
70
|
+
"""
|
|
71
|
+
start_script.write_text(script_content, encoding="utf-8")
|
|
72
|
+
|
|
73
|
+
ps_command = f"""
|
|
74
|
+
$action = New-ScheduledTaskAction `
|
|
75
|
+
-Execute "pwsh.exe" `
|
|
76
|
+
-Argument "-ExecutionPolicy Bypass -NoLogo -WindowStyle Hidden -File `"{start_script}`""
|
|
77
|
+
|
|
78
|
+
$trigger = New-ScheduledTaskTrigger -AtLogon
|
|
79
|
+
$principal = New-ScheduledTaskPrincipal -UserId "{self.config.user}" -RunLevel Highest
|
|
80
|
+
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable
|
|
81
|
+
|
|
82
|
+
Register-ScheduledTask -TaskName "{self.task_name}" `
|
|
83
|
+
-Action $action `
|
|
84
|
+
-Trigger $trigger `
|
|
85
|
+
-Principal $principal `
|
|
86
|
+
-Settings $settings `
|
|
87
|
+
-Force
|
|
88
|
+
"""
|
|
89
|
+
subprocess.run(["pwsh", "-Command", ps_command], check=True)
|
|
90
|
+
|
|
91
|
+
def check_service(self) -> bool:
|
|
92
|
+
ps_command = f"Get-ScheduledTask -TaskName '{self.task_name}' -ErrorAction SilentlyContinue"
|
|
93
|
+
result = subprocess.run(["pwsh", "-Command", ps_command], capture_output=True)
|
|
94
|
+
return result.returncode == 0
|
|
95
|
+
|
|
96
|
+
def start(self) -> None:
|
|
97
|
+
subprocess.run(
|
|
98
|
+
["pwsh", "-Command", f"Start-ScheduledTask -TaskName '{self.task_name}'"]
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def stop(self) -> None:
|
|
102
|
+
subprocess.run(
|
|
103
|
+
["pwsh", "-Command", f"Stop-ScheduledTask -TaskName '{self.task_name}'"]
|
|
104
|
+
)
|
|
105
|
+
ps_command = "Get-Process | Where-Object { $_.ProcessName -eq 'sing-box' } | Stop-Process -Force"
|
|
106
|
+
subprocess.run(["pwsh", "-Command", ps_command])
|
|
107
|
+
|
|
108
|
+
def restart(self) -> None:
|
|
109
|
+
self.stop()
|
|
110
|
+
self.start()
|
|
111
|
+
|
|
112
|
+
def status(self) -> str:
|
|
113
|
+
ps_command = f"""
|
|
114
|
+
$task = Get-ScheduledTask -TaskName '{self.task_name}' -ErrorAction SilentlyContinue
|
|
115
|
+
if ($task) {{
|
|
116
|
+
$process = Get-Process | Where-Object {{ $_.Path -eq '{self.config.bin_path}' }}
|
|
117
|
+
if ($process) {{
|
|
118
|
+
"Running (PID: $($process.Id))"
|
|
119
|
+
}} else {{
|
|
120
|
+
"Stopped"
|
|
121
|
+
}}
|
|
122
|
+
}} else {{
|
|
123
|
+
"Not installed"
|
|
124
|
+
}}
|
|
125
|
+
"""
|
|
126
|
+
result = subprocess.run(
|
|
127
|
+
["pwsh", "-Command", ps_command], capture_output=True, text=True
|
|
128
|
+
)
|
|
129
|
+
return result.stdout.strip()
|
|
130
|
+
|
|
131
|
+
def logs(self) -> None:
|
|
132
|
+
if not self.log_file.exists():
|
|
133
|
+
print("⚠️ Log file not found")
|
|
134
|
+
return
|
|
135
|
+
try:
|
|
136
|
+
print("⌛ Showing real-time logs (Press Ctrl+C to exit)")
|
|
137
|
+
ps_command = (
|
|
138
|
+
f"Get-Content -Path '{self.log_file}' -Wait -Tail 50 -Encoding UTF8"
|
|
139
|
+
)
|
|
140
|
+
subprocess.run(["pwsh", "-Command", ps_command])
|
|
141
|
+
except KeyboardInterrupt:
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
def disable(self) -> None:
|
|
145
|
+
self.stop()
|
|
146
|
+
subprocess.run(
|
|
147
|
+
[
|
|
148
|
+
"pwsh",
|
|
149
|
+
"-Command",
|
|
150
|
+
f"Unregister-ScheduledTask -TaskName '{self.task_name}' -Confirm:$false",
|
|
151
|
+
]
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class LinuxServiceManager(ServiceManager):
|
|
156
|
+
def __init__(self, config: Config):
|
|
157
|
+
super().__init__(config)
|
|
158
|
+
self.service_name = "sing-box"
|
|
159
|
+
self.service_file = Path("/etc/systemd/system/sing-box.service")
|
|
160
|
+
|
|
161
|
+
def create_service(self) -> None:
|
|
162
|
+
service_content = f"""
|
|
163
|
+
[Unit]
|
|
164
|
+
Description=sing-box service
|
|
165
|
+
Documentation=https://sing-box.sagernet.org
|
|
166
|
+
After=network.target nss-lookup.target
|
|
167
|
+
|
|
168
|
+
[Service]
|
|
169
|
+
Type=simple
|
|
170
|
+
LimitNPROC=500
|
|
171
|
+
LimitNOFILE=1000000
|
|
172
|
+
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
|
|
173
|
+
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
|
|
174
|
+
Restart=always
|
|
175
|
+
ExecStartPre={self.config.bin_path} tools synctime -w -C {self.config.install_dir}
|
|
176
|
+
ExecStart={self.config.bin_path} run -C {self.config.install_dir}
|
|
177
|
+
ExecReload=/bin/kill -HUP $MAINPID
|
|
178
|
+
|
|
179
|
+
[Install]
|
|
180
|
+
WantedBy=multi-user.target
|
|
181
|
+
"""
|
|
182
|
+
self.service_file.write_text(service_content)
|
|
183
|
+
subprocess.run(["systemctl", "daemon-reload"])
|
|
184
|
+
subprocess.run(["systemctl", "enable", self.service_name])
|
|
185
|
+
|
|
186
|
+
def check_service(self) -> bool:
|
|
187
|
+
return self.service_file.exists()
|
|
188
|
+
|
|
189
|
+
def start(self) -> None:
|
|
190
|
+
subprocess.run(["systemctl", "start", self.service_name])
|
|
191
|
+
|
|
192
|
+
def stop(self) -> None:
|
|
193
|
+
subprocess.run(["systemctl", "stop", self.service_name])
|
|
194
|
+
|
|
195
|
+
def restart(self) -> None:
|
|
196
|
+
subprocess.run(["systemctl", "restart", self.service_name])
|
|
197
|
+
|
|
198
|
+
def status(self) -> str:
|
|
199
|
+
try:
|
|
200
|
+
subprocess.check_call(["systemctl", "is-active", self.service_name])
|
|
201
|
+
return "Running"
|
|
202
|
+
except Exception:
|
|
203
|
+
return "Stopped"
|
|
204
|
+
|
|
205
|
+
def logs(self) -> None:
|
|
206
|
+
subprocess.run(["journalctl", "-u", "sing-box", "-o", "cat", "-f"])
|
|
207
|
+
|
|
208
|
+
def disable(self) -> None:
|
|
209
|
+
self.stop()
|
|
210
|
+
subprocess.run(["systemctl", "disable", self.service_name])
|