# coding: utf-8 # +------------------------------------------------------------------- # | YakPanel # +------------------------------------------------------------------- # | Copyright (c) 2015-2017 YakPanel(https://www.yakpanel.com) All rights reserved. # +------------------------------------------------------------------- # | Author: hwl@yakpanel.com # +------------------------------------------------------------------- # +-------------------------------------------------------------------- # | 前置web服务器控制器 # +-------------------------------------------------------------------- import os from sys import path as sys_path from platform import machine # 设置运行目录 os.chdir('/www/server/panel') # 添加自定义公共模块路径 if not 'class/' in sys_path: sys_path.insert(0,'class/') # 引入自定义公共模块 import public class webserver: def __init__(self): ''' @name 初始化 ''' if not hasattr(public,'get_panel_path'): self.__panel_path = '/www/server/panel' else: self.__panel_path = public.get_panel_path() self.__webserver_bin = os.path.join(self.__panel_path,'webserver/sbin/webserver') # webserver二进制文件 self.__webserver_conf = os.path.join(self.__panel_path,'webserver/conf/webserver.conf') # webserver配置文件 self.__webserver_pid = os.path.join(self.__panel_path,'webserver/logs/webserver.pid') # webserver进程PID self.__webserver_ctl = os.path.join(self.__panel_path,'script/webserver-ctl.sh') # webserver控制脚本 self.__panel_port_file = os.path.join(self.__panel_path, 'data/port.pl') # 面板端口文件 self.__ssl_key_file = os.path.join(self.__panel_path, 'ssl/privateKey.pem') # SSL私钥文件 self.__ssl_crt_file = os.path.join(self.__panel_path,'ssl/certificate.pem') # SSL证书文件 self.__is_ssl_file = os.path.join(self.__panel_path,'data/ssl.pl') # 是否开启SSL文件 self.__examples_path = os.path.join(self.__panel_path, 'webserver/tpls') # 配置文件模板目录 self.__webserver_conf_example = os.path.join(self.__examples_path ,'webserver.conf') # webserver配置文件模板 self.__webserver_ssl_conf_example = os.path.join(self.__examples_path ,'webserver_ssl.conf') # webserver SSL配置文件模板 self.__webserver_listen_conf_example = os.path.join(self.__examples_path ,'webserver_listen.conf') # webserver监听配置文件模板 self.__webserver_listen_ssl_conf_example = os.path.join(self.__examples_path ,'webserver_listen_ssl.conf') # webserver SSL监听配置文件模板 self.__default_port = 8888 # 默认端口 self.__log_error = 'ERROR' self.__log_debug = 'DEBUG' self.__log_info = 'INFO' self.__log_warning = 'WARNING' def print_log(self,msg,level='INFO'): ''' @name 打印日志 @param msg str 日志内容 @param level str 日志级别 @return void ''' if not hasattr(public,'print_log'): print("[{}]".format(level),msg) else: public.print_log(msg,level) def exec_shell(self,cmd): ''' @name 执行shell命令 @param cmd str shell命令 @return tuple ''' res = public.ExecShell(cmd) if isinstance(res,tuple): return res else: return ('',res) # 获取系统架构是X86还是ARM def get_machine(self): arch = machine() return arch def bin_exists(self): ''' @name 判断webserver二进制文件是否存在 @return bool ''' return os.path.exists(self.__webserver_bin) def conf_exists(self): ''' @name 判断webserver配置文件是否存在 @return bool ''' return os.path.exists(self.__webserver_conf) def conf_example_exists(self): ''' @name 判断webserver配置文件模板是否存在 @return bool ''' return os.path.exists(self.__webserver_conf_example) def pid_exists(self): ''' @name 判断webserver进程PID是否存在 @return bool ''' return os.path.exists(self.__webserver_pid) def ctl_exists(self): ''' @name 判断webserver-ctl.sh是否存在 @return bool ''' return os.path.exists(self.__webserver_ctl) def hasattr_public(self,defname): ''' @name 判断public模块是否存在 @return bool ''' if hasattr(public,defname): return True self.print_log('public.{} not found.'.format(defname),self.__log_error) return False def process_status(self): ''' @name 判断webserver进程是否存在 @return bool ''' if not self.pid_exists(): return False res = self.exec_shell("bash {} status".format(self.__webserver_ctl)) if res[0].find('not running') != -1: return False return True def start(self): ''' @name 启动webserver @return bool ''' if self.process_status(): return False res = self.exec_shell("bash {} start".format(self.__webserver_ctl)) if res[0].find('Failed') != -1: self.print_log(res,self.__log_warning) return False return True def stop(self): ''' @name 停止webserver @return bool ''' if not self.process_status(): return False res = self.exec_shell("bash {} stop".format(self.__webserver_ctl)) if res[0].find('Failed') != -1: self.print_log(res,self.__log_warning) return False return True def restart(self): ''' @name 重启webserver @return bool ''' res = self.exec_shell("bash {} restart".format(self.__webserver_ctl)) if res[0].find('Failed') != -1: self.print_log(res,self.__log_warning) return False return True def reload(self): ''' @name 重载webserver @return bool ''' res = self.exec_shell("bash {} reload".format(self.__webserver_ctl)) if res[0].find('Failed') != -1: self.print_log(res,self.__log_warning) return False return True def get_status(self): ''' @name 获取webserver状态 @return bool ''' return self.process_status() def configtest(self): ''' @name 测试配置文件是否正确 @return bool ''' res = self.exec_shell("bash {} configtest".format(self.__webserver_ctl)) result = "\n".join(res) if result.find('successful') != -1: return True self.print_log(result,self.__log_warning) return False def get_panel_port(self): ''' @name 获取面板端口 @return int ''' # 如果面板端口文件不存在,则使用默认端口 if not os.path.exists(self.__panel_port_file): return self.__default_port # 读取面板端口文件 port = public.ReadFile(self.__panel_port_file) if not port: return self.__default_port try: # 判断端口是否在1-65535之间 port = int(port) if port < 1 or port > 65535: return self.__default_port except: return self.__default_port return port def is_ssl(self): ''' @name 是否开启了SSL @return bool ''' if os.path.exists(self.__is_ssl_file): # 如果开启了SSL,则判断SSL证书和私钥文件是否存在 if os.path.exists(self.__ssl_key_file) and os.path.exists(self.__ssl_crt_file): return True return False def get_ssl_config(self): ''' @name 获取SSL配置 @return dict ''' # 如果没有开启SSL,则返回空字符串 if not self.is_ssl(): return '' ssl_conf = public.ReadFile(self.__webserver_ssl_conf_example) if not ssl_conf: self.print_log('Ssl configuration file not found.',self.__log_warning) return '' return ssl_conf def get_http3_header(self): ''' @name 获取HTTP3头部 @return dict ''' http3_header = '' # 如果开启了SSL,则添加HTTP3头部 if self.is_ssl(): '''h3=":443"; ma=2592000,h3-29=":443"; ma=2592000''' http3_header = "add_header Alt-Svc 'h3=\":{PORT}\"; ma=86400,h3-29=\":{PORT}\"; ma=86400';".format(PORT=self.get_panel_port()) return http3_header def get_listen_config(self): ''' @name 获取监听配置 @return dict ''' default_listen = ''' listen {PORT}; listen [::]:{PORT}; ''' # 是否开启了SSL if self.is_ssl(): listen_conf = public.ReadFile(self.__webserver_listen_ssl_conf_example) else: listen_conf = public.ReadFile(self.__webserver_listen_conf_example) # 如果没有找到监听配置文件,则使用默认监听配置 if not listen_conf: listen_conf = default_listen # 替换面板端口 port = self.get_panel_port() listen_conf = listen_conf.format(PORT=port) return listen_conf def create_conf(self): ''' @name 创建配置文件 @return bool ''' if not self.conf_example_exists(): self.print_log('Web server configuration file template not found.',self.__log_error) return False # 读取配置文件模板 config_example = public.ReadFile(self.__webserver_conf_example) if not config_example: self.print_log('Web server configuration file template read failed.',self.__log_error) return False # 获取监听配置 listen_conf = self.get_listen_config() # 获取SSL配置 ssl_conf = self.get_ssl_config() # 获取HTTP3头部 http3_header = self.get_http3_header() config = config_example.format(LISTEN=listen_conf, SSL_CONFIG=ssl_conf, HTTP3_HEADER=http3_header) res = public.WriteFile(self.__webserver_conf, config) if not res: self.print_log("Write Web server configuration file failed",self.__log_error) return False # 测试配置文件是否正确 if not self.configtest(): self.print_log('Web server configuration file test failed.',self.__log_error) return False return True def chmod_static_files(self): ''' @name 设置静态文件权限 @return bool ''' # 设置静态目录链权限 res = self.exec_shell('chmod 755 /www /www/server /www/server/panel /www/server/panel/YakPanel') if res[1] != '': self.print_log('\n'.join(res),self.__log_warning) return False # 递归设置静态文件权限 res = self.exec_shell('chmod -R 755 /www/server/panel/YakPanel/static') if res[1] != '': self.print_log('\n'.join(res),self.__log_warning) return False return True def run_webserver(self): ''' @name 启动web服务器 @return bool 是否成功 ''' try: # 判断public模块是否符合要求 public_function = ['ExecShell','ReadFile','WriteFile','get_error_info','get_panel_path','get_panel_port','print_log'] for func in public_function: if not self.hasattr_public(func): return False # 判断webserver二进制文件是否存在,如果不存在则尝试调用下载脚本下载 if not self.bin_exists(): if os.path.exists(self.__webserver_ctl): self.exec_shell("bash {} download".format(self.__webserver_ctl)) self.print_log('Web server binary file not found.',self.__log_error) return False # 判断cli工具脚本是否存在 if not self.ctl_exists(): self.print_log('Web server command line tool not found.',self.__log_error) return False # 设置静态文件权限 if not self.chmod_static_files(): self.print_log('Web server static file permission setting failed.',self.__log_error) return False # 创建配置文件 if not self.create_conf(): self.print_log('Web server configuration file creation failed.',self.__log_error) return False # 检查webserver进程是否存在,如果存在则重载,否则启动 if self.process_status(): res = self.reload() if not res: self.print_log('Web server reload failed.',self.__log_warning) else: res = self.start() # 检查进程是否正常运行 if not res: self.print_log('Web server start failed.',self.__log_error) return False except: # 将错误信息写入到日志 self.print_log(public.get_error_info(),self.__log_error) return False return True if __name__ == '__main__': web = webserver() print(web.run_webserver())