mdserver-web/plugins/apache/index.py

594 lines
16 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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: 配置出错<br><a style="color:red;">' + check_data[1].replace("\n", '<br>') + '</a>'
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"<IfModule mpm_%s_module>.*?(%s)\s+(\w+).*?</IfModule>" % (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"(<IfModule mpm_%s_module>.*?)%s(\s+)\d+(.*?</IfModule>)" % (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: 配置出错<br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
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')