# coding:utf-8 import sys import io import os import time import threading import subprocess import re web_dir = os.getcwd() + "/web" if os.path.exists(web_dir): sys.path.append(web_dir) os.chdir(web_dir) import core.mw as mw app_debug = False if mw.isAppleSystem(): app_debug = True def getPluginName(): return 'apache' def getPluginDir(): return mw.getPluginDir() + '/' + getPluginName() def getServerDir(): return mw.getServerDir() + '/' + getPluginName() def getInitDFile(): current_os = mw.getOs() if current_os == 'darwin': return '/tmp/' + getPluginName() if current_os.startswith('freebsd'): return '/etc/rc.d/' + getPluginName() return '/etc/init.d/' + getPluginName() def getArgs(): args = sys.argv[2:] # print(args) tmp = {} args_len = len(args) if args_len == 1: t = args[0].strip('{').strip('}') t = t.split(':',2) tmp[t[0]] = t[1] elif args_len > 1: for i in range(len(args)): t = args[i].split(':',2) tmp[t[0]] = t[1] # print(tmp) return tmp def checkArgs(data, ck=[]): for i in range(len(ck)): if not ck[i] in data: return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!')) return (True, mw.returnJson(True, 'ok')) def clearTemp(): path_bin = getServerDir() + "/httpd" def getConf(): path = getServerDir() + "/httpd/conf/httpd.conf" return path def getConfMpm(): path = getServerDir() + "/httpd/conf/extra/httpd-mpm.conf" return path def getConfTpl(): path = getPluginDir() + '/conf/httpd.conf' return path def getOs(): data = {} data['os'] = mw.getOs() data['auth'] = True return mw.getJson(data) def getInitDTpl(): path = getPluginDir() + "/init.d/httpd.tpl" return path def getPidFile(): file = getConf() content = mw.readFile(file) rep = r'pid\s*(.*);' tmp = re.search(rep, content) return tmp.groups()[0].strip() def getFileOwner(filename): import pwd stat = os.lstat(filename) uid = stat.st_uid pw = pwd.getpwuid(uid) return pw.pw_name def checkAuthEq(file, owner='root'): fowner = getFileOwner(file) if (fowner == owner): return True return False def confReplace(): service_path = mw.getServerDir() content = mw.readFile(getConfTpl()) content = content.replace('{$SERVER_PATH}', service_path) # 主配置文件 nconf = getServerDir() + '/httpd/conf/httpd.conf' mw.writeFile(nconf, content) def initDreplace(): file_tpl = getInitDTpl() service_path = mw.getServerDir() initD_path = getServerDir() + '/init.d' # OpenResty is not installed if not os.path.exists(getServerDir()): print("ok") exit(0) # init.d file_bin = initD_path + '/' + getPluginName() if not os.path.exists(initD_path): os.mkdir(initD_path) # initd replace content = mw.readFile(file_tpl) content = content.replace('{$SERVER_PATH}', service_path) mw.writeFile(file_bin, content) mw.execShell('chmod +x ' + file_bin) # config replace confReplace() # systemd # /usr/lib/systemd/system systemDir = mw.systemdCfgDir() systemService = systemDir + '/httpd.service' if os.path.exists(systemDir) and not os.path.exists(systemService): systemServiceTpl = getPluginDir() + '/init.d/httpd.service.tpl' se_content = mw.readFile(systemServiceTpl) se_content = se_content.replace('{$SERVER_PATH}', service_path) mw.writeFile(systemService, se_content) mw.execShell('systemctl daemon-reload') return file_bin def status(): cmd = "ps -ef|grep 'httpd' |grep -v grep | grep -v python | awk '{print $2}'" data = mw.execShell(cmd) if data[0] == '': return 'stop' return 'start' def restyOp(method): file = initDreplace() # 启动时,先检查一下配置文件 check = getServerDir() + "/httpd/bin/httpd -t" check_data = mw.execShell(check) if not check_data[1].find('Syntax OK') > -1: return check_data[1] current_os = mw.getOs() if current_os == "darwin": data = mw.execShell(file + ' ' + method) if data[1] == '': return 'ok' return data[1] if current_os.startswith("freebsd"): mw.execShell('service httpd '+method) if data[1] == '': return 'ok' return data[1] data = mw.execShell('systemctl ' + method + ' httpd') if data[1] == '': return 'ok' return data[1] def op_submit_systemctl_restart(): current_os = mw.getOs() if current_os.startswith("freebsd"): mw.execShell('service httpd restart') return True mw.execShell('systemctl restart httpd') return True def op_submit_init_restart(file): mw.execShell(file + ' restart') def restyOp_restart(): file = initDreplace() # 启动时,先检查一下配置文件 check = getServerDir() + "/httpd/bin/httpd -t" check_data = mw.execShell(check) if not check_data[1].find('Syntax OK') > -1: return 'ERROR: 配置出错
' + check_data[1].replace("\n", '
') + '
' if not mw.isAppleSystem(): threading.Timer(2, op_submit_systemctl_restart).start() return 'ok' threading.Timer(2, op_submit_init_restart, args=(file,)).start() return 'ok' def start(): return restyOp('start') def stop(): r = restyOp('stop') mw.execShell("ps -ef|grep httpd | grep -v grep | awk '{print $2}'|xargs -r kill") return r def restart(): return restyOp_restart() def reload(): confReplace() return restyOp('reload') def initdStatus(): current_os = mw.getOs() if current_os == 'darwin': return "Apple Computer does not support" if current_os.startswith('freebsd'): initd_bin = getInitDFile() if os.path.exists(initd_bin): return 'ok' shell_cmd = 'systemctl status httpd | grep loaded | grep "enabled;"' data = mw.execShell(shell_cmd) if data[0] == '': return 'fail' return 'ok' def initdInstall(): current_os = mw.getOs() if current_os == 'darwin': return "Apple Computer does not support" # freebsd initd install if current_os.startswith('freebsd'): import shutil source_bin = initDreplace() initd_bin = getInitDFile() shutil.copyfile(source_bin, initd_bin) mw.execShell('chmod +x ' + initd_bin) mw.execShell('sysrc httpd_enable="YES"') return 'ok' mw.execShell('systemctl enable httpd') return 'ok' def initdUinstall(): current_os = mw.getOs() if current_os == 'darwin': return "Apple Computer does not support" if current_os.startswith('freebsd'): initd_bin = getInitDFile() os.remove(initd_bin) mw.execShell('sysrc httpd_enable="NO"') return 'ok' mw.execShell('systemctl disable httpd') return 'ok' def getHttpdStatusPort(): conf = mw.getServerDir() + '/apache/httpd/conf/httpd.conf' content = mw.readFile(conf) if not content: return None rep = r'^\s*Listen\s*(?:\d+\.\d+\.\d+\.\d+:)?(\d+)' # 匹配非注释行的 Listen 指令,忽略大小写 tmp = re.search(rep, content, re.IGNORECASE | re.MULTILINE) if tmp: port = tmp.groups()[0].strip() return port return None def runInfoDone(data): result = {} if not data: return result # 解析服务器状态数据 lines = data.strip().split('\n') for line in lines: if ':' in line: key, value = line.split(':', 1) key = key.strip() value = value.strip() result[key] = value return result def runInfo(): op_status = status() if op_status == 'stop': return mw.returnJson(False, "未启动!") port = getHttpdStatusPort() if not port: return mw.returnJson(False, "无法获取端口信息!") # 取Openresty负载状态 try: url = 'http://127.0.0.1:%s/server-status?auto' % port result = mw.httpGet(url, timeout=3) data = runInfoDone(result) return mw.getJson(data) except Exception as e: try: url = 'http://' + mw.getHostAddr() + ':%s/server-status?auto' % port result = mw.httpGet(url) data = runInfoDone(result) return mw.getJson(data) except Exception as e: return mw.returnJson(False, "apache异常!") except Exception as e: return mw.returnJson(False, "apache not started!") def errorLogPath(): return getServerDir() + '/httpd/logs/error.log' def getCfg(): cfg = getConfMpm() content = mw.readFile(cfg) unitrep = "[kmgKMG]" # 获取当前 MPM 模块 mpm_module = "" mpm_match = re.search(r"mpm_(\w+)_module", content) if mpm_match: mpm_module = mpm_match.group(1) # MPM 配置参数 mpm_cfg_args = { "prefork": [ {"name": "StartServers", "ps": "服务器进程启动数量", 'type': 2}, {"name": "MinSpareServers", "ps": "保持空闲的最小服务器进程数", 'type': 2}, {"name": "MaxSpareServers", "ps": "保持空闲的最大服务器进程数", 'type': 2}, {"name": "MaxRequestWorkers", "ps": "允许启动的最大服务器进程数", 'type': 2}, {"name": "MaxConnectionsPerChild", "ps": "服务器进程服务的最大连接数", 'type': 2}, ], "worker": [ {"name": "StartServers", "ps": "初始服务器进程数", 'type': 2}, {"name": "MinSpareThreads", "ps": "保持空闲的最小工作线程数", 'type': 2}, {"name": "MaxSpareThreads", "ps": "保持空闲的最大工作线程数", 'type': 2}, {"name": "ThreadsPerChild", "ps": "每个服务器进程的工作线程数", 'type': 2}, {"name": "MaxRequestWorkers", "ps": "最大工作线程数", 'type': 2}, {"name": "MaxConnectionsPerChild", "ps": "服务器进程服务的最大连接数", 'type': 2}, ], "event": [ {"name": "StartServers", "ps": "初始服务器进程数", 'type': 2}, {"name": "MinSpareThreads", "ps": "保持空闲的最小工作线程数", 'type': 2}, {"name": "MaxSpareThreads", "ps": "保持空闲的最大工作线程数", 'type': 2}, {"name": "ThreadsPerChild", "ps": "每个服务器进程的工作线程数", 'type': 2}, {"name": "MaxRequestWorkers", "ps": "最大工作线程数", 'type': 2}, {"name": "MaxConnectionsPerChild", "ps": "服务器进程服务的最大连接数", 'type': 2}, ], "netware": [ {"name": "ThreadStackSize", "ps": "每个工作线程分配的堆栈大小", 'type': 2}, {"name": "StartThreads", "ps": "服务器启动时启动的工作线程数", 'type': 2}, {"name": "MinSpareThreads", "ps": "保持空闲的最小线程数", 'type': 2}, {"name": "MaxSpareThreads", "ps": "保持空闲的最大线程数", 'type': 2}, {"name": "MaxThreads", "ps": "同时活跃的最大工作线程数", 'type': 2}, {"name": "MaxConnectionsPerChild", "ps": "线程服务的最大连接数", 'type': 2}, ], "mpmt_os2": [ {"name": "StartServers", "ps": "维护的服务器进程数", 'type': 2}, {"name": "MinSpareThreads", "ps": "每个进程的最小空闲线程数", 'type': 2}, {"name": "MaxSpareThreads", "ps": "每个进程的最大空闲线程数", 'type': 2}, {"name": "MaxConnectionsPerChild", "ps": "每个服务器进程的最大连接数", 'type': 2}, ], "winnt": [ {"name": "ThreadsPerChild", "ps": "服务器进程中的工作线程数", 'type': 2}, {"name": "MaxConnectionsPerChild", "ps": "服务器进程服务的最大连接数", 'type': 2}, ] } # 通用配置参数 common_cfg_args = [ {"name": "MaxMemFree", "ps": "每个分配器允许持有的最大空闲KB数", 'type': 2}, ] # 合并配置参数 cfg_args = [] if mpm_module in mpm_cfg_args: cfg_args.extend(mpm_cfg_args[mpm_module]) cfg_args.extend(common_cfg_args) rdata = [] for i in cfg_args: # 匹配 MPM 特定配置 rep = r".*?(%s)\s+(\w+).*?" % (mpm_module, i["name"]) k = re.search(rep, content, re.DOTALL) # 如果没有找到 MPM 特定配置,尝试匹配通用配置 if not k: rep = r"(%s)\s+(\w+)" % i["name"] k = re.search(rep, content) if not k: continue key = k.group(1) v = k.group(2) if len(k.groups()) > 1 else "" if re.search(unitrep, v): u = str.upper(v[-1]) v = v[:-1] if len(u) == 1: psstr = u + "B," + i["ps"] else: psstr = u + "," + i["ps"] else: u = "" kv = {"name": key, "value": v, "unit": u, "ps": i["ps"], "type": i["type"]} rdata.append(kv) return mw.returnJson(True, "ok", rdata) def replaceChar(value, index, new_char): return value[:index] + new_char + value[index+1:] def setCfg(): args = getArgs() # 检查参数,允许动态参数 cfg = getConfMpm() mw.backFile(cfg) content = mw.readFile(cfg) # 获取当前 MPM 模块 mpm_module = "" mpm_match = re.search(r"mpm_(\w+)_module", content) if mpm_match: mpm_module = mpm_match.group(1) # 验证参数值 for k, v in args.items(): # 检查是否为数字参数 if not re.search(r"\d+", v): return mw.returnJson(False, '参数值错误,请输入数字整数') # 替换 MPM 特定配置 if mpm_module: def replace_mpm_config(match): return match.group(1) + k + match.group(2) + v + match.group(3) rep = r"(.*?)%s(\s+)\d+(.*?)" % (mpm_module, k) if re.search(rep, content, re.DOTALL): content = re.sub(rep, replace_mpm_config, content, flags=re.DOTALL) # 替换通用配置 def replace_common_config(match): return k + match.group(1) + v rep = r"%s(\s+)\d+" % k if re.search(rep, content): content = re.sub(rep, replace_common_config, content) mw.writeFile(cfg, content) isError = mw.checkHttpdConfig() if (isError != True): mw.restoreFile(cfg) return mw.returnJson(False, 'ERROR: 配置出错
' + isError.replace("\n", '
') + '
') mw.restartWeb() return mw.returnJson(True, '设置成功') def cronAddCheck(): try: import tool_task tool_task.createBgTask() return mw.returnJson(True, '添加检查任务成功') except Exception as e: return mw.returnJson(False, '添加检查任务失败:'+str(e)) def cronDelCheck(): try: import tool_task tool_task.removeBgTask() return mw.returnJson(True, '删除检查任务成功') except Exception as e: return mw.returnJson(False, '删除检查任务失败:'+str(e)) def cronCheck(): return 'ok' def installPreInspection(): return 'ok' if __name__ == "__main__": version = '2.4' version_pl = getServerDir() + "/version.pl" if os.path.exists(version_pl): version = mw.readFile(version_pl) func = sys.argv[1] if func == 'status': print(status()) elif func == 'start': print(start()) elif func == 'stop': print(stop()) elif func == 'restart': print(restart()) elif func == 'reload': print(reload()) elif func == 'initd_status': print(initdStatus()) elif func == 'initd_install': print(initdInstall()) elif func == 'initd_uninstall': print(initdUinstall()) elif func == 'install_pre_inspection': print(installPreInspection()) elif func == 'conf': print(getConf()) elif func == 'get_os': print(getOs()) elif func == 'run_info': print(runInfo()) elif func == 'error_log': print(errorLogPath()) elif func == 'get_cfg': print(getCfg()) elif func == 'set_cfg': print(setCfg()) elif func == 'check': print(cronCheck()) elif func == 'cron_add_check': print(cronAddCheck()) elif func == 'cron_del_check': print(cronDelCheck()) else: print('error')