Files
aaPanel/script/restart_services.py
T
Jack e65107a251 Update to v7.37.0
1. Added: USDT payment method (please contact customer service via email for payment)
2. Added: Russian language support
3. Added: Domains - Provider List added PorkBun, NameSilo
4. Added: Domains - SSL Certificate - Added Business Certificate
5. Added: Domains - SSL Certificate - Apply for SSL added file verification
6. Added: Setting - Panel SSL added file verification
7. Added: WP Toolkit - Create site with auto DNS resolution
8. Added: FTP - Log and Log analytics support for Ubuntu and Debian
9. Added: Website/WP Toolkit option to choose whether to create default HTML file when adding sites
10. Added: Automatic Daemon tasks: Nginx, Apache, FTP, Redis, MySQL, Memcached, etc.
11. Added: Brute force protection compatible with 2 log formats
12. Optimized: Brute force protection history records now display in reverse chronological order
13. Optimized: Brute force protection settings added unblock time notification for IP blocking configuration
14. Optimized: Website/WP Toolkit SSL interface
15. Optimized: Cron automatic backup with retry mechanism for remote uploads
16. Optimized: Pay interface
17. Fixed: Temporary login Fixed blank page issue
18. Fixed: Mail Server Fixed missing MX record auto-configuration issue
19. Fixed: Node projects occasionally failing to retrieve running status
20. Fixed: Issue where Account could not be confirmed after editing in some cases
21. Fixed #225
2025-05-23 19:30:26 +08:00

280 lines
8.7 KiB
Python

# coding: utf-8
import json
import os
import subprocess
import sys
import time
import fcntl
os.chdir("/www/server/panel")
sys.path.insert(0, "class/")
sys.path.insert(0, "/www/server/panel/")
import public
if not "class_v2" in sys.path:
sys.path.insert(0, "/www/server/panel/class_v2")
from panel_site_v2 import panelSite
SETUP_PATH = public.get_setup_path()
DATA_PATH = os.path.join(SETUP_PATH, "panel/data")
DAEMON_SERVICE = os.path.join(DATA_PATH, "daemon_service.pl")
if not os.path.exists(DAEMON_SERVICE):
public.writeFile(DAEMON_SERVICE, json.dumps([]))
MANUAL_FLAG = os.path.join(public.get_panel_path(), "data/mod_push_data", "manual_flag.pl")
if not os.path.exists(MANUAL_FLAG):
public.writeFile(MANUAL_FLAG, json.dumps({}))
SERVICES_MAP = {
"apache": (
"httpd", f"{SETUP_PATH}/apache/logs/httpd.pid", "/etc/init.d/httpd"
),
"nginx": (
"nginx", f"{SETUP_PATH}/nginx/logs/nginx.pid", "/etc/init.d/nginx"
),
"redis": (
"redis", f"{SETUP_PATH}/redis/redis.pid", "/etc/init.d/redis"
),
"mysql": (
"mysqld", "/tmp/mysql.sock", "/etc/init.d/mysqld"
),
"mongodb": (
"mongod", f"{SETUP_PATH}/mongodb/log/configsvr.pid", "/etc/init.d/mongodb"
),
"pure-ftpd": (
"pure-ftpd", "/var/run/pure-ftpd.pid", "/etc/init.d/pure-ftpd"
),
"memcached": (
"memcached", "/var/run/memcached.pid", "/etc/init.d/memcached"
),
"openlitespeed": (
"litespeed", "/tmp/lshttpd/lsphp.sock", "/usr/local/lsws/bin/lswsctrl"
)
}
def add_daemon(service_name: str) -> bool:
if not service_name or service_name not in SERVICES_MAP.keys():
return False
daemon_list = json.loads(public.readFile(DAEMON_SERVICE))
if service_name in daemon_list:
return True
daemon_list.append(service_name)
public.writeFile(DAEMON_SERVICE, json.dumps(list(set(daemon_list))))
return True
def del_daemon(service_name: str) -> bool:
if not service_name:
return False
try:
public.writeFile(DAEMON_SERVICE, json.dumps([
x for x in json.loads(public.readFile(DAEMON_SERVICE)) if x != service_name
]))
except:
return False
return True
def save_file(path: str, body: str) -> None:
try:
with open(path, "w+") as f:
fcntl.flock(f, fcntl.LOCK_EX)
f.write(body)
fcntl.flock(f, fcntl.LOCK_UN)
except (IOError, OSError) as _:
pass
def read_file(path: str) -> dict:
try:
with open(path, "r") as f:
fcntl.flock(f, fcntl.LOCK_SH)
content = f.read()
fcntl.flock(f, fcntl.LOCK_UN)
return json.loads(content) if content else {}
except json.JSONDecodeError as _:
return {}
except (IOError, OSError) as _:
return {}
def manual_flag(server_name: str = None, open_: str = None) -> dict:
"""人为关闭标记"""
manual = read_file(MANUAL_FLAG)
if server_name and open_ == "stop":
manual[server_name] = 1
save_file(MANUAL_FLAG, json.dumps(manual))
return manual
elif server_name and open_ in ["start", "restart"] and manual.get(server_name) == 1:
manual[server_name] = 0
save_file(MANUAL_FLAG, json.dumps(manual))
return manual
else:
return manual
class RestartServices:
def __init__(self):
self.nick_name = None
self.serviced = None
self.pid_file = None
self.bash = None
def is_support(self) -> bool:
try:
map_info = SERVICES_MAP.get(self.nick_name)
if not map_info:
return False
self.serviced, self.pid_file, self.bash = map_info
if not all([self.serviced, self.pid_file, self.bash]):
return False
return True
except:
return False
def is_install_service(self) -> bool:
if self.serviced == "mysqld": # mysql系列卸载根本不干净, 判断bin
if any([
os.path.exists(f"{SETUP_PATH}/mysql/bin/mariadbd"),
os.path.exists(f"{SETUP_PATH}/mysql/bin/mysqld"),
]):
return True
else:
return False
# other
if any([
os.path.exists(f"/etc/init.d/{self.serviced}"),
os.path.exists(f"/etc/init.d/{self.nick_name}"),
os.path.exists(self.bash),
]):
return True
else:
return False
def is_process_running(self) -> bool:
if not os.path.exists(self.pid_file):
return False
# sock file
if self.pid_file.endswith(".sock"):
try:
check_list = [self.serviced] if self.serviced != "mysqld" else ["mysqld", "mariadbd"]
cmd = f"lsof {self.pid_file} 2>/dev/null | grep -E '{'|'.join(check_list)}'"
result = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE)
return result.returncode == 0
except:
return False
else: # pid file
try:
pid = public.readFile(self.pid_file)
if not pid:
return False
pid = int(pid.strip())
if not os.path.exists(f"/proc/{pid}"):
return False
try:
with open(f"/proc/{pid}/stat", "r") as f:
stat = f.read().split()
return stat[2] != "Z"
except:
return False
except:
return False
def _script(self, act: str) -> None:
try:
if act not in ["start", "stop", "restart", "status"]:
return
# "try to {act} [{self.nick_name}]..."
bash_path = self.bash if self.bash else f"/etc/init.d/{self.serviced}"
if self.pid_file.endswith(".sock"):
try:
subprocess.run([f"rm -f {self.pid_file}"])
except FileNotFoundError:
pass
except:
pass
subprocess.run(
[bash_path, act],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
except Exception as e:
print(str(e))
def main(self):
with open(MANUAL_FLAG, 'rb') as fp:
fcntl.flock(fp, fcntl.LOCK_SH)
check_list = json.loads(public.readFile(DAEMON_SERVICE))
manual_info = manual_flag()
for service in check_list:
self.nick_name = service
if not self.is_support() or not self.is_install_service():
continue
if not self.is_process_running():
if manual_info.get(self.nick_name) == 1:
# service closed maually, skip
continue
public.WriteLog(
"Service Daemon", f"Service [ {self.nick_name} ] is Not Running, Try to start it..."
)
self._script("start")
time.sleep(5)
if not self.is_process_running():
self._script("restart")
if manual_info.get(self.nick_name) == 1:
# service is running, remove manual flag
manual_info[self.nick_name] = 0
save_file(MANUAL_FLAG, json.dumps(manual_info))
return
return
def first_time_installed(data: dict) -> None:
"""
首次安装服务启动守护进程服务
"""
if not data:
return
try:
for service in SERVICES_MAP.keys(): # support service
pl_name = f"{DATA_PATH}/first_installed_flag_{service}.pl"
if data.get(service): # panel installed
setup = data[service].get("setup", False)
if setup is False and os.path.exists(pl_name):
os.remove(pl_name)
elif setup is True and not os.path.exists(pl_name):
get = public.dict_obj()
get.name = service
get.status = 1
panelSite().set_restart_task(get)
public.writeFile(pl_name, "1", mode="w")
else:
pass
except:
pass
if __name__ == "__main__":
pass
# import tracemalloc
# tracemalloc.start()
# snapshot1 = tracemalloc.take_snapshot()
# RestartServices().main()
# snapshot2 = tracemalloc.take_snapshot()
# top_stats = snapshot2.compare_to(snapshot1, 'lineno')
# print("[Top memory differences]")
# for stat in top_stats[:3]:
# print("stat", stat)