\
+ \
+ \
+
节点调度 \
+
\
+ \
+ 轮询[默认] \
+ ip_hash \
+ fair \
+ url_hash \
+ least_conn \
+ \
+
\
+
\
+ \
+
节点健康检查 \
+
\
+ \
+
\
+
\
+ \
+
节点 \
+
\
+
\
+
\
+ \
+ \
+ IP地址 \
+ 端口 \
+ 验证路径 \
+ 状态 \
+ 权重 \
+ 阀值 \
+ 恢复时间 \
+ 操作 \
+ \
+ \
+ \
+ \
+ 当前节点为空,请至少添加一个普通节点 \
+ \
+ \
+
\
+
\
+
添加节点 \
+
\
+
\
+ ",
+ success:function(){
+ $('input[name="upstream_name"]').val(data['upstream_name']);
+ $('select[name="node_algo"]').val(data['node_algo']);
+
+ $('input[name="node_health_check"]').prop('checked',false);
+ if (data['node_health_check'] == 'ok'){
+ $('input[name="node_health_check"]').prop('checked',true);
+ }
+
+ var node_list = data['node_list'];
+ if (node_list.length>0){
+ $('#nodecon .nulltr').hide();
+ }
+
+ var state_option_list = {
+ '1':'参与者',
+ '2':'备份',
+ '0':'停用',
+ }
+
+ for (var n in node_list) {
+
+ var tbody = '';
+ tbody +=''+node_list[n]['ip']+' ';
+ tbody +=''+node_list[n]['port']+' ';
+ tbody +=''+node_list[n]['path']+' ';
+
+ tbody +="";
+
+ for (i in state_option_list) {
+ if (i == node_list[n]['state']){
+ tbody +=""+state_option_list[i]+" ";
+ } else{
+ tbody +=""+state_option_list[i]+" ";
+ }
+ }
+ tbody +=" ";
+
+ tbody +=' ';
+ tbody +=' ';
+ tbody +=' ';
+ tbody +='删除 ';
+ tbody += ' ';
+ $('#nodecon').append(tbody);
+ }
+
+ $('#nodecon .delete').click(function(){
+ $(this).parent().parent().remove();
+ if ($('#nodecon tr').length == 1 ){
+ $('#nodecon .nulltr').show();
+ }
+ });
+
+ $('.add_node').click(function(){
+ addNode();
+ });
+ },
+ yes:function(index) {
+ var data = {};
+
+ data['node_algo'] = $('select[name="node_algo"]').val();
+ data['node_health_check'] = 'fail';
+ if ($('input[name="node_health_check"]').prop('checked')){
+ data['node_health_check'] = 'ok';
+ }
+
+ var node_list = [];
+ $('#nodecon tr').each(function(){
+
+ var ip = $(this).find('td').eq(0).text();
+ var port = $(this).find('td').eq(1).text();
+
+ if (port == ''){return;}
+
+ var path = $(this).find('td').eq(2).text();
+ var state = $(this).find('select[name="state"]').val();
+ var weight = $(this).find('input[name="weight"]').val();
+ var max_fails = $(this).find('input[name="max_fails"]').val();
+ var fail_timeout = $(this).find('input[name="fail_timeout"]').val();
+
+ var tmp = {
+ ip:ip,
+ port:port,
+ path:path,
+ state:state,
+ weight:weight,
+ max_fails:max_fails,
+ fail_timeout:fail_timeout,
+ }
+ node_list.push(tmp);
+ });
+ data['node_list'] = node_list;
+ data['row'] = row;
+ ooPostCallbak('edit_load_balance', data, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ showMsg(rdata.msg, function(){
+ layer.close(index);
+ loadBalanceListRender();
+ },{ icon: rdata.status ? 1 : 2 }, 2000);
+ });
+ }
+ });
+}
+
+function loadBalanceListRender(){
+ ooPost('load_balance_list', {}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var alist = rdata.data;
+
+ var tbody = '';
+ for (var i = 0; i < alist.length; i++) {
+ tbody += '';
+ tbody += ''+alist[i]['domain']+' ';
+ tbody += ''+alist[i]['upstream_name']+' ';
+ tbody += ''+alist[i]['node_list'].length+' ';
+ tbody += '查看 ';
+ tbody += '查看 ';
+ tbody += '修改 | 删除 ';
+ tbody += ' ';
+ }
+
+ $('#nodeTable').html(tbody);
+ $('.nodeTablePage .Pcount').text('共'+alist.length+'条');
+ $('#nodeTable .edit').click(function(){
+ var row = $(this).data('row');
+ editBalance(alist[row],row);
+ });
+
+ $('#nodeTable .log_look').click(function(){
+ var row = $(this).data('row');
+ var args = {'domain':alist[row]['domain']};
+ pluginRollingLogs('op_load_balance','','get_logs',JSON.stringify(args),20);
+ });
+
+ $('#nodeTable .health_status').click(function(){
+ var row = $(this).data('row');
+ ooPost('get_health_status', {row:row}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+
+ var tval = '';
+ for (var i = 0; i < rdata.data.length; i++) {
+ tval += '';
+ tval += ''+rdata.data[i]['name']+' ';
+
+ if (typeof(rdata.data[i]['down']) != 'undefined' && rdata.data[i]['down']){
+ tval += '不正常 ';
+ } else{
+ tval += '正常 ';
+ }
+ tval += ' ';
+ }
+
+ var tbody = "";
+
+ layer.open({
+ type: 1,
+ area: ['500px','300px'],
+ title: '节点状态',
+ closeBtn: 1,
+ shift: 5,
+ shadeClose: true,
+ btn:['提交','关闭'],
+ content:tbody,
+ });
+ });
+ });
+
+ $('#nodeTable .delete').click(function(){
+ var row = $(this).data('row');
+ ooPost('load_balance_delete', {row:row}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ showMsg(rdata.msg, function(){
+ loadBalanceListRender();
+ },{ icon: rdata.status ? 1 : 2 }, 2000);
+ });
+ });
+
+ });
+}
+
+function loadBalanceList() {
+ var body = '\
+
\
+
添加负载 \
+
\
+
\
+ \
+ \
+ 网站 \
+ 负载名称 \
+ 节点 \
+ 日志 \
+ 状态 \
+ 操作 \
+ \
+ \
+ \
+
\
+
\
+
\
+
\
+
\
+ ';
+ $(".soft-man-con").html(body);
+ loadBalanceListRender();
+}
diff --git a/plugins/op_load_balance/lua/health_check.lua.tpl b/plugins/op_load_balance/lua/health_check.lua.tpl
new file mode 100644
index 000000000..94411c942
--- /dev/null
+++ b/plugins/op_load_balance/lua/health_check.lua.tpl
@@ -0,0 +1,18 @@
+local hc = require "resty.upstream.healthcheck"
+local ok, err = hc.spawn_checker {
+ shm = "healthcheck",
+ type = "http",
+ upstream = "{$UPSTREAM_NAME}",
+ http_req = "GET / HTTP/1.0\r\nHost: {$UPSTREAM_NAME}\r\n\r\n",
+ interval = 2000,
+ timeout = 6000,
+ fall = 3,
+ rise = 2,
+ valid_statuses = {200, 302},
+ concurrency = 20,
+}
+
+if not ok then
+ ngx.log(ngx.ERR, "=======> load balance health checker error: ", err)
+ return
+end
\ No newline at end of file
diff --git a/plugins/op_waf/class/luamaker.py b/plugins/op_waf/class/luamaker.py
new file mode 100644
index 000000000..fa0735aa4
--- /dev/null
+++ b/plugins/op_waf/class/luamaker.py
@@ -0,0 +1,61 @@
+import sys
+import os
+
+
+class luamaker:
+ """
+ lua 处理器
+ """
+ @staticmethod
+ def makeLuaTable(table):
+ """
+ table 转换为 lua table 字符串
+ """
+ _tableMask = {}
+ _keyMask = {}
+
+ def analysisTable(_table, _indent, _parent):
+ if isinstance(_table, tuple):
+ _table = list(_table)
+ if isinstance(_table, list):
+ _table = dict(zip(range(1, len(_table) + 1), _table))
+ if isinstance(_table, dict):
+ _tableMask[id(_table)] = _parent
+ cell = []
+ thisIndent = _indent + " "
+ for k in _table:
+ if sys.version_info[0] == 2:
+ if type(k) not in [int, float, bool, list, dict, tuple]:
+ k = k.encode()
+
+ if not (isinstance(k, str) or isinstance(k, int) or isinstance(k, float)):
+ return
+ key = isinstance(
+ k, int) and "[" + str(k) + "]" or "[\"" + str(k) + "\"]"
+ if _parent + key in _keyMask.keys():
+ return
+ _keyMask[_parent + key] = True
+ var = None
+ v = _table[k]
+ if sys.version_info[0] == 2:
+ if type(v) not in [int, float, bool, list, dict, tuple]:
+ v = v.encode()
+ if isinstance(v, str):
+ # print("lua", var)
+ v = v.replace("\\", "\\\\")
+ v = v.replace("\"", "\\\"")
+ var = "\"" + v + "\""
+
+ elif isinstance(v, bool):
+ var = v and "true" or "false"
+ elif isinstance(v, int) or isinstance(v, float):
+ var = str(v)
+ else:
+ var = analysisTable(v, thisIndent, _parent + key)
+
+ cell.append(thisIndent + key + " = " + str(var))
+ lineJoin = ",\n"
+ return "{\n" + lineJoin.join(cell) + "\n" + _indent + "}"
+ else:
+ pass
+ return analysisTable(table, "", "root")
diff --git a/plugins/op_waf/conf/init.sql b/plugins/op_waf/conf/init.sql
new file mode 100644
index 000000000..210a5d2b0
--- /dev/null
+++ b/plugins/op_waf/conf/init.sql
@@ -0,0 +1,27 @@
+PRAGMA synchronous = 0;
+PRAGMA page_size = 4096;
+PRAGMA journal_mode = wal;
+PRAGMA journal_size_limit = 1073741824;
+
+CREATE TABLE IF NOT EXISTS `logs` (
+ `time` INTEGER,
+ `ip` TEXT,
+ `domain` TEXT,
+ `server_name` TEXT,
+ `method` TEXT,
+ `status_code` INTEGER,
+ `user_agent` TEXT,
+ `uri` TEXT,
+ `rule_name` TEXT,
+ `reason` TEXT
+);
+
+
+CREATE INDEX time_idx ON logs(`time`);
+CREATE INDEX ip_idx ON logs (`ip`);
+CREATE INDEX uri_idx ON logs (`uri`);
+CREATE INDEX method_idx ON logs (`method`);
+CREATE INDEX server_name_idx ON logs (`server_name`);
+CREATE INDEX status_code_idx ON logs (`status_code`);
+CREATE INDEX all_union_idx ON logs (`status_code`, `time`, `ip`, `domain`, `server_name`, `method`, `uri`);
+
diff --git a/plugins/op_waf/conf/luawaf.conf b/plugins/op_waf/conf/luawaf.conf
new file mode 100755
index 000000000..3c93a082d
--- /dev/null
+++ b/plugins/op_waf/conf/luawaf.conf
@@ -0,0 +1,3 @@
+lua_shared_dict waf_limit 30m;
+lua_shared_dict waf_drop_ip 10m;
+lua_shared_dict waf_drop_sum 10m;
\ No newline at end of file
diff --git a/plugins/op_waf/ico.png b/plugins/op_waf/ico.png
new file mode 100644
index 000000000..55dabc43d
Binary files /dev/null and b/plugins/op_waf/ico.png differ
diff --git a/plugins/op_waf/index.html b/plugins/op_waf/index.html
new file mode 100755
index 000000000..73ee75db1
--- /dev/null
+++ b/plugins/op_waf/index.html
@@ -0,0 +1,268 @@
+
+
+
+
\ No newline at end of file
diff --git a/plugins/op_waf/index.py b/plugins/op_waf/index.py
new file mode 100755
index 000000000..92698b3e9
--- /dev/null
+++ b/plugins/op_waf/index.py
@@ -0,0 +1,1593 @@
+# coding:utf-8
+
+'''
+cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/op_waf && bash install.sh install 0.3.2
+python3 /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/op_waf/index.py reload
+'''
+import sys
+import io
+import os
+import time
+import subprocess
+import json
+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 'op_waf'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':', 1)
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':', 1)
+ tmp[t[0]] = t[1]
+
+ 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'))
+
+
+sys.path.append(getPluginDir() + "/class")
+from luamaker import luamaker
+
+
+def listToLuaFile(path, lists):
+ content = luamaker.makeLuaTable(lists)
+ content = "return " + content
+ mw.writeFile(path, content)
+
+
+def htmlToLuaFile(path, content):
+ content = "return [[" + content + "]]"
+ mw.writeFile(path, content)
+
+
+def getConf():
+ path = mw.getServerDir() + "/openresty/nginx/conf/nginx.conf"
+ return path
+
+
+def dstWafConfPath():
+ return mw.getServerDir() + "/web_conf/nginx/vhost/opwaf.conf"
+
+
+def pSqliteDb(dbname='logs'):
+ name = "waf"
+ db_dir = getServerDir() + '/logs/'
+
+ if not os.path.exists(db_dir):
+ mw.execShell('mkdir -p ' + db_dir)
+
+ file = db_dir + name + '.db'
+ if not os.path.exists(file):
+ conn = mw.M(dbname).dbPos(db_dir, name)
+ sql = mw.readFile(getPluginDir() + '/conf/init.sql')
+ sql_list = sql.split(';')
+ for index in range(len(sql_list)):
+ conn.execute(sql_list[index])
+ else:
+ conn = mw.M(dbname).dbPos(db_dir, name)
+
+ conn.execute("PRAGMA synchronous = 0")
+ conn.execute("PRAGMA page_size = 4096")
+ conn.execute("PRAGMA journal_mode = wal")
+ conn.execute("PRAGMA journal_size_limit = 1073741824")
+ return conn
+
+
+def initDomainInfo(conf_reload=False):
+ data = []
+ path_domains = getJsonPath('domains')
+ _list = mw.M('sites').field('id,name,path').where(
+ 'status=?', ('1',)).order('id desc').select()
+
+ for i in range(len(_list)):
+ tmp = {}
+ tmp['name'] = _list[i]['name']
+ tmp['path'] = _list[i]['path']
+
+ _list_domain = mw.M('domain').field('name').where(
+ 'pid=?', (_list[i]['id'],)).order('id desc').select()
+
+ tmp_j = []
+ for j in range(len(_list_domain)):
+ tmp_j.append(_list_domain[j]['name'])
+
+ tmp['domains'] = tmp_j
+ data.append(tmp)
+ cjson = mw.getJson(data)
+ mw.writeFile(path_domains, cjson)
+
+
+def initSiteInfo(conf_reload=False):
+ data = []
+
+ path_site = getJsonPath('site')
+ path_domains = getJsonPath('domains')
+ path_config = getJsonPath('config')
+
+ config_contents = mw.readFile(path_config)
+ config_contents = json.loads(config_contents)
+
+ domain_contents = mw.readFile(path_domains)
+ domain_contents = json.loads(domain_contents)
+
+ try:
+ site_contents = mw.readFile(path_site)
+ if not site_contents:
+ site_contents = "{}"
+ except Exception as e:
+ site_contents = "{}"
+
+ site_contents = json.loads(site_contents)
+ site_contents_new = {}
+ for x in range(len(domain_contents)):
+ name = domain_contents[x]['name']
+ if name in site_contents:
+ site_contents_new[name] = site_contents[name]
+ else:
+ tmp = {}
+ tmp['cdn'] = True
+ tmp['log'] = True
+ tmp['get'] = True
+ tmp['post'] = True
+ tmp['open'] = True
+
+ tmp['cc'] = config_contents['cc']
+ tmp['retry'] = config_contents['retry']
+ tmp['get'] = config_contents['get']
+ tmp['post'] = config_contents['post']
+ tmp['user-agent'] = config_contents['user-agent']
+ tmp['cookie'] = config_contents['cookie']
+ tmp['scan'] = config_contents['scan']
+ tmp['safe_verify'] = config_contents['safe_verify']
+
+ cdn_header = ['x-forwarded-for',
+ 'x-real-ip',
+ 'x-forwarded',
+ 'forwarded-for',
+ 'forwarded',
+ 'true-client-ip',
+ 'client-ip',
+ 'ali-cdn-real-ip',
+ 'cdn-src-ip',
+ 'cdn-real-ip',
+ 'cf-connecting-ip',
+ 'x-cluster-client-ip',
+ 'wl-proxy-client-ip',
+ 'proxy-client-ip',
+ 'true-client-ip',
+ 'HTTP_CF_CONNECTING_IP']
+ tmp['cdn_header'] = cdn_header
+
+ disable_upload_ext = ["php", "jsp"]
+ tmp['disable_upload_ext'] = disable_upload_ext
+
+ disable_path = ['sql']
+ tmp['disable_ext'] = disable_path
+
+ site_contents_new[name] = tmp
+
+ cjson = mw.getJson(site_contents_new)
+ mw.writeFile(path_site, cjson)
+
+
+def initTotalInfo(conf_reload=False):
+ data = []
+
+ path_total = getJsonPath('total')
+ path_domains = getJsonPath('domains')
+
+ domain_contents = mw.readFile(path_domains)
+ domain_contents = json.loads(domain_contents)
+
+ try:
+ total_contents = mw.readFile(path_total)
+ except Exception as e:
+ total_contents = "{}"
+
+ total_contents = json.loads(total_contents)
+ total_contents_new = {}
+ for x in range(len(domain_contents)):
+ name = domain_contents[x]['name']
+ if 'sites' in total_contents and name in total_contents['sites']:
+ pass
+ else:
+ tmp = {}
+ tmp['cdn'] = 0
+ tmp['log'] = 0
+ tmp['get'] = 0
+ tmp['post'] = 0
+ tmp['total'] = 0
+ tmp['path'] = 0
+ tmp['php_path'] = 0
+ tmp['upload_ext'] = 0
+ _name = {}
+ _name[name] = tmp
+ total_contents['sites'] = _name
+
+ total_contents['start_time'] = str(time.time())
+ cjson = mw.getJson(total_contents)
+ mw.writeFile(path_total, cjson)
+
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ waf_root = getServerDir()
+ waf_path = waf_root + "/waf"
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$WAF_PATH}', waf_path)
+ content = content.replace('{$WAF_ROOT}', waf_root)
+
+ if mw.isAppleSystem():
+ content = content.replace('{$MMDB_FILE_SUFFIX}', 'dylib')
+ else:
+ content = content.replace('{$MMDB_FILE_SUFFIX}', 'so')
+
+ return content
+
+
+def autoMakeLuaConfSingle(file, conf_reload=False):
+ path = getServerDir() + "/waf/rule/" + file + ".json"
+ dst_path = getServerDir() + "/waf/conf/rule_" + file + ".lua"
+ if not os.path.exists(dst_path) or conf_reload:
+ content = mw.readFile(path)
+ # print(content)
+ content = json.loads(content)
+ listToLuaFile(dst_path, content)
+
+
+def autoCpImport(file):
+ path = getPluginDir() + "/waf/" + file + ".json"
+ dst_path = getServerDir() + "/waf/" + file + ".json"
+ content = mw.readFile(path)
+ mw.writeFile(dst_path, content)
+
+
+def autoMakeLuaImportSingle(file, conf_reload=False):
+ path = getServerDir() + "/waf/" + file + ".json"
+ dst_path = getServerDir() + "/waf/conf/waf_" + file + ".lua"
+ if not os.path.exists(dst_path) or conf_reload:
+ content = mw.readFile(path)
+ # print(content)
+ content = json.loads(content)
+ listToLuaFile(dst_path, content)
+
+
+def autoMakeLuaHtmlSingle(file, conf_reload=False):
+ path = getServerDir() + "/waf/html/" + file + ".html"
+ dst_path = getServerDir() + "/waf/html/html_" + file + ".lua"
+ if not os.path.exists(dst_path) or conf_reload:
+ content = mw.readFile(path)
+ htmlToLuaFile(dst_path, content)
+
+
+def autoCpHtml(file):
+ path = getPluginDir() + "/waf/html/" + file + ".html"
+ dst_path = getServerDir() + "/waf/html/" + file + ".html"
+ content = mw.readFile(path)
+ mw.writeFile(dst_path, content)
+
+
+def autoMakeLuaConf(conf_reload=False, cp_reload=False):
+ conf_list = ['args', 'cookie', 'ip_black', 'ip_white',
+ 'ipv6_black', 'post', 'scan_black', 'url',
+ 'url_white', 'user_agent']
+ for x in conf_list:
+ autoMakeLuaConfSingle(x, conf_reload)
+
+ import_list = ['config', 'site', 'domains', 'area_limit']
+ for x in import_list:
+ autoMakeLuaImportSingle(x, conf_reload)
+
+ html_list = ['get', 'post', 'safe_js', 'user_agent', 'cookie', 'other']
+ for x in html_list:
+ if cp_reload:
+ autoCpHtml(x)
+ autoMakeLuaHtmlSingle(x, conf_reload)
+
+
+def initDefaultInfo(conf_reload=False):
+ path = getServerDir()
+ dst_path = path + "/waf/default.pl"
+ default_site = ''
+ if os.path.exists(dst_path):
+ return True
+ source_path = path + "/waf/domains.json"
+ content = mw.readFile(source_path)
+ content = json.loads(content)
+
+ ddata = {}
+ dlist = []
+ for i in content:
+ dlist.append(i["name"])
+
+ dlist.append('unset')
+ ddata["list"] = dlist
+ if len(ddata["list"]) < 1:
+ default_site = "unset"
+ else:
+ default_site = dlist[0]
+
+ mw.writeFile(dst_path, default_site)
+
+
+def getSiteListData():
+ path = getServerDir()
+ source_path = path + "/waf/domains.json"
+ dst_path = path + "/waf/default.pl"
+
+ content = mw.readFile(source_path)
+ content = json.loads(content)
+ dlist = []
+ for i in content:
+ dlist.append(i["name"])
+ dlist.append('unset')
+
+ default_site = mw.readFile(dst_path)
+
+ data = {}
+ data['list'] = dlist
+ data['default'] = default_site
+ return data
+
+
+def setDefaultSite(name):
+ path = getServerDir()
+ dst_path = path + "/waf/default.pl"
+ mw.writeFile(dst_path, name)
+ return mw.returnJson(True, 'OK')
+
+
+def getDefaultSite():
+ data = getSiteListData()
+ return mw.returnJson(True, 'OK', data)
+
+
+def getCountry():
+ data = ['中国大陆以外的地区(包括[中国特别行政区:港,澳,台])', '中国大陆(不包括[中国特别行政区:港,澳,台])', '中国香港', '中国澳门', '中国台湾',
+ '美国', '日本', '英国', '德国', '韩国', '法国', '巴西', '加拿大', '意大利', '澳大利亚', '荷兰', '俄罗斯', '印度', '瑞典', '西班牙', '墨西哥',
+ '比利时', '南非', '波兰', '瑞士', '阿根廷', '印度尼西亚', '埃及', '哥伦比亚', '土耳其', '越南', '挪威', '芬兰', '丹麦', '乌克兰', '奥地利',
+ '伊朗', '智利', '罗马尼亚', '捷克', '泰国', '沙特阿拉伯', '以色列', '新西兰', '委内瑞拉', '摩洛哥', '马来西亚', '葡萄牙', '爱尔兰', '新加坡',
+ '欧洲联盟', '匈牙利', '希腊', '菲律宾', '巴基斯坦', '保加利亚', '肯尼亚', '阿拉伯联合酋长国', '阿尔及利亚', '塞舌尔', '突尼斯', '秘鲁', '哈萨克斯坦',
+ '斯洛伐克', '斯洛文尼亚', '厄瓜多尔', '哥斯达黎加', '乌拉圭', '立陶宛', '塞尔维亚', '尼日利亚', '克罗地亚', '科威特', '巴拿马', '毛里求斯', '白俄罗斯',
+ '拉脱维亚', '多米尼加', '卢森堡', '爱沙尼亚', '苏丹', '格鲁吉亚', '安哥拉', '玻利维亚', '赞比亚', '孟加拉国', '巴拉圭', '波多黎各', '坦桑尼亚',
+ '塞浦路斯', '摩尔多瓦', '阿曼', '冰岛', '叙利亚', '卡塔尔', '波黑', '加纳', '阿塞拜疆', '马其顿', '约旦', '萨尔瓦多', '伊拉克', '亚美尼亚', '马耳他',
+ '危地马拉', '巴勒斯坦', '斯里兰卡', '特立尼达和多巴哥', '黎巴嫩', '尼泊尔', '纳米比亚', '巴林', '洪都拉斯', '莫桑比克', '尼加拉瓜', '卢旺达', '加蓬',
+ '阿尔巴尼亚', '利比亚', '吉尔吉斯坦', '柬埔寨', '古巴', '喀麦隆', '乌干达', '塞内加尔', '乌兹别克斯坦', '黑山', '关岛', '牙买加', '蒙古', '文莱',
+ '英属维尔京群岛', '留尼旺', '库拉索岛', '科特迪瓦', '开曼群岛', '巴巴多斯', '马达加斯加', '伯利兹', '新喀里多尼亚', '海地', '马拉维', '斐济', '巴哈马',
+ '博茨瓦纳', '扎伊尔', '阿富汗', '莱索托', '百慕大', '埃塞俄比亚', '美属维尔京群岛', '列支敦士登', '津巴布韦', '直布罗陀', '苏里南', '马里', '也门',
+ '老挝', '塔吉克斯坦', '安提瓜和巴布达', '贝宁', '法属玻利尼西亚', '圣基茨和尼维斯', '圭亚那', '布基纳法索', '马尔代夫', '泽西岛', '摩纳哥', '巴布亚新几内亚',
+ '刚果', '塞拉利昂', '吉布提', '斯威士兰', '缅甸', '毛里塔尼亚', '法罗群岛', '尼日尔', '安道尔', '阿鲁巴', '布隆迪', '圣马力诺', '利比里亚',
+ '冈比亚', '不丹', '几内亚', '圣文森特岛', '荷兰加勒比区', '圣马丁', '多哥', '格陵兰', '佛得角', '马恩岛', '索马里', '法属圭亚那', '西萨摩亚',
+ '土库曼斯坦', '瓜德罗普', '马里亚那群岛', '瓦努阿图', '马提尼克', '赤道几内亚', '南苏丹', '梵蒂冈', '格林纳达', '所罗门群岛', '特克斯和凯科斯群岛', '多米尼克',
+ '乍得', '汤加', '瑙鲁', '圣多美和普林西比', '安圭拉岛', '法属圣马丁', '图瓦卢', '库克群岛', '密克罗尼西亚联邦', '根西岛', '东帝汶', '中非',
+ '几内亚比绍', '帕劳', '美属萨摩亚', '厄立特里亚', '科摩罗', '圣皮埃尔和密克隆', '瓦利斯和富图纳', '英属印度洋领地', '托克劳', '马绍尔群岛', '基里巴斯',
+ '纽埃', '诺福克岛', '蒙特塞拉特岛', '朝鲜', '马约特', '圣卢西亚', '圣巴泰勒米岛']
+ return mw.returnJson(True, 'ok', data)
+
+
+def autoMakeConfig(conf_reload=False, cp_reload=False):
+ initDomainInfo(conf_reload)
+ initSiteInfo(conf_reload)
+ initTotalInfo(conf_reload)
+ autoMakeLuaConf(conf_reload, cp_reload)
+ initDefaultInfo(conf_reload)
+
+
+def setConfRestartWeb():
+ autoMakeConfig(True, False)
+ mw.opWeb('stop')
+ mw.opWeb('start')
+
+
+def restartWeb():
+ mw.opWeb('stop')
+ mw.opWeb('start')
+
+
+def makeOpDstRunLua(conf_reload=False):
+ root_init_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_by_lua_file'
+ root_worker_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_worker_by_lua_file'
+ root_access_dir = mw.getServerDir() + '/web_conf/nginx/lua/access_by_lua_file'
+ path = getServerDir()
+ path_tpl = getPluginDir()
+
+ waf_common_dst = path + "/waf/lua/waf_common.lua"
+ if not os.path.exists(waf_common_dst) or conf_reload:
+ waf_common_tpl = path_tpl + "/waf/lua/waf_common.lua"
+ content = mw.readFile(waf_common_tpl)
+ content = contentReplace(content)
+ mw.writeFile(waf_common_dst, content)
+
+ waf_init_dst = root_init_dir + "/waf_init_preload.lua"
+ if not os.path.exists(waf_init_dst) or conf_reload:
+ waf_init_tpl = path_tpl + "/waf/lua/init_preload.lua"
+ content = mw.readFile(waf_init_tpl)
+ content = contentReplace(content)
+ mw.writeFile(waf_init_dst, content)
+
+ init_worker_dst = root_worker_dir + '/opwaf_init_worker.lua'
+ if not os.path.exists(init_worker_dst) or conf_reload:
+ init_worker_tpl = path_tpl + "/waf/lua/init_worker.lua"
+ content = mw.readFile(init_worker_tpl)
+ content = contentReplace(content)
+ mw.writeFile(init_worker_dst, content)
+
+ access_file_dst = root_access_dir + '/opwaf_init.lua'
+ if not os.path.exists(access_file_dst) or conf_reload:
+ access_file_tpl = path_tpl + "/waf/lua/init.lua"
+ access_file_dst_s = path + "/waf/lua/init.lua"
+ content = mw.readFile(access_file_tpl)
+ content = contentReplace(content)
+ mw.writeFile(access_file_dst, content)
+ mw.writeFile(access_file_dst_s, content)
+
+ waf_mmdb_dst = path + "/waf/lua/waf_maxminddb.lua"
+ if not os.path.exists(waf_mmdb_dst) or conf_reload:
+ waf_mmdb_tpl = path_tpl + "/waf/lua/waf_maxminddb.lua"
+ content = mw.readFile(waf_mmdb_tpl)
+ content = contentReplace(content)
+ mw.writeFile(waf_mmdb_dst, content)
+
+ mw.opLuaMakeAll()
+ return True
+
+
+def makeOpDstStopLua():
+ root_init_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_by_lua_file'
+ root_worker_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_worker_by_lua_file'
+ root_access_dir = mw.getServerDir() + '/web_conf/nginx/lua/access_by_lua_file'
+
+ waf_init_dst = root_init_dir + "/waf_init_preload.lua"
+ if os.path.exists(waf_init_dst):
+ os.remove(waf_init_dst)
+
+ init_worker_dst = root_worker_dir + '/opwaf_init_worker.lua'
+ if os.path.exists(init_worker_dst):
+ os.remove(init_worker_dst)
+
+ access_file_dst = root_access_dir + '/opwaf_init.lua'
+ if os.path.exists(access_file_dst):
+ os.remove(access_file_dst)
+
+ wafconf = dstWafConfPath()
+ if os.path.exists(wafconf):
+ os.remove(wafconf)
+
+ mw.opLuaMakeAll()
+ return True
+
+
+def initDreplace():
+ path = getServerDir()
+ if not os.path.exists(path + '/waf/lua'):
+ sdir = getPluginDir() + '/waf'
+ cmd = 'cp -rf ' + sdir + ' ' + path
+ mw.execShell(cmd)
+
+ logs_path = path + '/logs'
+ if not os.path.exists(logs_path):
+ mw.execShell('mkdir -p ' + logs_path)
+
+ debug_log = path + '/debug.log'
+ if not os.path.exists(debug_log):
+ mw.execShell('echo "" > ' + debug_log)
+
+ config = path + '/waf/config.json'
+ content = mw.readFile(config)
+ content = json.loads(content)
+ content['reqfile_path'] = path + "/waf/html"
+ mw.writeFile(config, mw.getJson(content))
+
+ makeOpDstRunLua()
+
+ waf_conf = dstWafConfPath()
+ if not os.path.exists(waf_conf):
+ waf_tpl = getPluginDir() + "/conf/luawaf.conf"
+ content = mw.readFile(waf_tpl)
+ content = contentReplace(content)
+ mw.writeFile(waf_conf, content)
+
+ autoMakeConfig(True, False)
+
+ pSqliteDb()
+
+ if not mw.isAppleSystem():
+ mw.execShell("chown -R www:www " + path)
+ return path
+
+
+def status():
+ path = getConf()
+ if not os.path.exists(path):
+ return 'stop'
+
+ waf_conf = dstWafConfPath()
+ if not os.path.exists(waf_conf):
+ return 'stop'
+ return 'start'
+
+
+def start():
+ initDreplace()
+
+ import tool_task
+ tool_task.createBgTask()
+
+ restartWeb()
+ return 'ok'
+
+
+def stop():
+
+ makeOpDstStopLua()
+
+ import tool_task
+ tool_task.removeBgTask()
+
+ restartWeb()
+ return 'ok'
+
+
+def restart():
+ restartWeb()
+ return 'ok'
+
+
+def reload():
+ mw.opWeb('stop')
+
+ makeOpDstRunLua(True)
+ autoMakeConfig(True, False)
+
+ elog = mw.getServerDir() + "/openresty/nginx/logs/error.log"
+ if os.path.exists(elog):
+ mw.execShell('rm -rf ' + elog)
+
+ mw.opWeb('start')
+ return 'ok'
+
+def reload_hook():
+ s = status()
+ if s == 'start':
+ return reload()
+ return 'ok'
+
+
+def getJsonPath(name):
+ path = getServerDir() + "/waf/" + name + ".json"
+ return path
+
+
+def getRuleJsonPath(name):
+ path = getServerDir() + "/waf/rule/" + name + ".json"
+ return path
+
+
+def getRule():
+ args = getArgs()
+ data = checkArgs(args, ['rule_name'])
+ if not data[0]:
+ return data[1]
+
+ rule_name = args['rule_name']
+ fpath = getRuleJsonPath(rule_name)
+ content = mw.readFile(fpath)
+ return mw.returnJson(True, 'ok', content)
+
+
+def addRule():
+ args = getArgs()
+ data = checkArgs(args, ['ruleName', 'ruleValue', 'ps'])
+ if not data[0]:
+ return data[1]
+
+ ruleValue = args['ruleValue']
+ ruleName = args['ruleName']
+ ps = args['ps']
+
+ fpath = getRuleJsonPath(ruleName)
+ content = mw.readFile(fpath)
+ content = json.loads(content)
+
+ tmp_k = []
+ tmp_k.append(1)
+ tmp_k.append(ruleValue)
+ tmp_k.append(ps)
+ tmp_k.append(1)
+
+ content.append(tmp_k)
+
+ cjson = mw.getJson(content)
+ mw.writeFile(fpath, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!', content)
+
+
+def removeRule():
+ args = getArgs()
+ data = checkArgs(args, ['ruleName', 'index'])
+ if not data[0]:
+ return data[1]
+
+ index = int(args['index'])
+ ruleName = args['ruleName']
+
+ fpath = getRuleJsonPath(ruleName)
+ content = mw.readFile(fpath)
+ content = json.loads(content)
+
+ k = content[index]
+ content.remove(k)
+
+ cjson = mw.getJson(content)
+ mw.writeFile(fpath, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!', content)
+
+
+def setRuleState():
+ args = getArgs()
+ data = checkArgs(args, ['ruleName', 'index'])
+ if not data[0]:
+ return data[1]
+
+ index = int(args['index'])
+ ruleName = args['ruleName']
+
+ fpath = getRuleJsonPath(ruleName)
+ content = mw.readFile(fpath)
+ content = json.loads(content)
+
+ b = content[index][0]
+ if b == 1:
+ content[index][0] = 0
+ else:
+ content[index][0] = 1
+
+ cjson = mw.getJson(content)
+ mw.writeFile(fpath, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!', content)
+
+
+def modifyRule():
+ args = getArgs()
+ data = checkArgs(args, ['index', 'ruleName', 'ruleBody', 'rulePs'])
+ if not data[0]:
+ return data[1]
+
+ index = int(args['index'])
+ ruleName = args['ruleName']
+ ruleBody = args['ruleBody']
+ rulePs = args['rulePs']
+
+ fpath = getRuleJsonPath(ruleName)
+ content = mw.readFile(fpath)
+ content = json.loads(content)
+
+ tmp = content[index]
+
+ tmp_k = []
+ tmp_k.append(tmp[0])
+ tmp_k.append(ruleBody)
+ tmp_k.append(rulePs)
+ tmp_k.append(tmp[3])
+
+ content[index] = tmp_k
+
+ cjson = mw.getJson(content)
+ mw.writeFile(fpath, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!', content)
+
+
+def getSiteRule():
+ args = getArgs()
+ data = checkArgs(args, ['siteName', 'ruleName'])
+ if not data[0]:
+ return data[1]
+
+ siteName = args['siteName']
+ siteRule = args['ruleName']
+
+ path = getJsonPath('site')
+ content = mw.readFile(path)
+ content = json.loads(content)
+
+ r = content[siteName][siteRule]
+
+ cjson = mw.getJson(r)
+ return mw.returnJson(True, 'ok!', cjson)
+
+
+def addSiteRule():
+ args = getArgs()
+ data = checkArgs(args, ['siteName', 'ruleName', 'ruleValue'])
+ if not data[0]:
+ return data[1]
+
+ siteName = args['siteName']
+ siteRule = args['ruleName']
+ ruleValue = args['ruleValue']
+
+ path = getJsonPath('site')
+ content = mw.readFile(path)
+ content = json.loads(content)
+
+ content[siteName][siteRule].append(ruleValue)
+
+ cjson = mw.getJson(content)
+ mw.writeFile(path, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!')
+
+
+def addIpWhite():
+ args = getArgs()
+ data = checkArgs(args, ['start_ip', 'end_ip'])
+ if not data[0]:
+ return data[1]
+
+ start_ip = args['start_ip']
+ end_ip = args['end_ip']
+
+ path = getRuleJsonPath('ip_white')
+ content = mw.readFile(path)
+ content = json.loads(content)
+
+ data = []
+
+ start_ip_list = start_ip.split('.')
+ tmp = []
+ for x in range(len(start_ip_list)):
+ tmp.append(int(start_ip_list[x]))
+
+ end_ip_list = end_ip.split('.')
+ tmp2 = []
+ for x in range(len(end_ip_list)):
+ tmp2.append(int(end_ip_list[x]))
+
+ data.append(tmp)
+ data.append(tmp2)
+
+ content.append(data)
+
+ cjson = mw.getJson(content)
+ mw.writeFile(path, cjson)
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!')
+
+
+def removeIpWhite():
+ args = getArgs()
+ data = checkArgs(args, ['index'])
+ if not data[0]:
+ return data[1]
+
+ index = args['index']
+
+ path = getRuleJsonPath('ip_white')
+ content = mw.readFile(path)
+ content = json.loads(content)
+
+ k = content[int(index)]
+ content.remove(k)
+
+ cjson = mw.getJson(content)
+ mw.writeFile(path, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!')
+
+
+def addIpBlack():
+ args = getArgs()
+ data = checkArgs(args, ['start_ip', 'end_ip'])
+ if not data[0]:
+ return data[1]
+
+ start_ip = args['start_ip']
+ end_ip = args['end_ip']
+
+ path = getRuleJsonPath('ip_black')
+ content = mw.readFile(path)
+ content = json.loads(content)
+
+ data = []
+
+ start_ip_list = start_ip.split('.')
+ tmp = []
+ for x in range(len(start_ip_list)):
+ tmp.append(int(start_ip_list[x]))
+
+ end_ip_list = end_ip.split('.')
+ tmp2 = []
+ for x in range(len(end_ip_list)):
+ tmp2.append(int(end_ip_list[x]))
+
+ data.append(tmp)
+ data.append(tmp2)
+
+ content.append(data)
+
+ cjson = mw.getJson(content)
+ mw.writeFile(path, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!')
+
+
+def removeIpBlack():
+ args = getArgs()
+ data = checkArgs(args, ['index'])
+ if not data[0]:
+ return data[1]
+
+ index = args['index']
+
+ path = getRuleJsonPath('ip_black')
+ content = mw.readFile(path)
+ content = json.loads(content)
+
+ k = content[int(index)]
+ content.remove(k)
+
+ cjson = mw.getJson(content)
+ mw.writeFile(path, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!')
+
+
+def setIpv6Black():
+ args = getArgs()
+ data = checkArgs(args, ['addr'])
+ if not data[0]:
+ return data[1]
+
+ addr = args['addr'].replace('_', ':')
+ path = getRuleJsonPath('ipv6_black')
+
+ content = mw.readFile(path)
+ content = json.loads(content)
+ content.append(addr)
+
+ cjson = mw.getJson(content)
+ mw.writeFile(path, cjson)
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!')
+
+
+def delIpv6Black():
+ args = getArgs()
+ data = checkArgs(args, ['addr'])
+ if not data[0]:
+ return data[1]
+
+ addr = args['addr'].replace('_', ':')
+ path = getRuleJsonPath('ipv6_black')
+
+ content = mw.readFile(path)
+ content = json.loads(content)
+
+ content.remove(addr)
+ cjson = mw.getJson(content)
+ mw.writeFile(path, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!')
+
+
+def removeSiteRule():
+ args = getArgs()
+ data = checkArgs(args, ['siteName', 'ruleName', 'index'])
+ if not data[0]:
+ return data[1]
+
+ siteName = args['siteName']
+ siteRule = args['ruleName']
+ index = args['index']
+
+ path = getJsonPath('site')
+ content = mw.readFile(path)
+ content = json.loads(content)
+
+ ruleValue = content[siteName][siteRule][int(index)]
+ content[siteName][siteRule].remove(ruleValue)
+
+ cjson = mw.getJson(content)
+ mw.writeFile(path, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!')
+
+
+def setObjStatus():
+ args = getArgs()
+ data = checkArgs(args, ['obj', 'statusCode'])
+ if not data[0]:
+ return data[1]
+
+ conf = getJsonPath('config')
+ content = mw.readFile(conf)
+ cobj = json.loads(content)
+
+ o = args['obj']
+ status = int(args['statusCode'])
+ cobj[o]['status'] = status
+
+ cjson = mw.getJson(cobj)
+ mw.writeFile(conf, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!')
+
+
+def setRetry():
+ args = getArgs()
+ data = checkArgs(args, ['retry', 'retry_time',
+ 'retry_cycle', 'is_open_global'])
+ if not data[0]:
+ return data[1]
+
+ conf = getJsonPath('config')
+ content = mw.readFile(conf)
+ cobj = json.loads(content)
+
+ ## 修复数据类型错误
+ tmp = args
+ tmp['retry'] = int(tmp['retry'])
+ tmp['retry_time'] = int(tmp['retry_time'])
+ tmp['retry_cycle'] = int(tmp['retry_cycle'])
+
+ cobj['retry'] = tmp
+ cjson = mw.getJson(cobj)
+ mw.writeFile(conf, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!', [])
+
+
+def setSafeVerify():
+ args = getArgs()
+ data = checkArgs(args, ['auto', 'time', 'cpu', 'mode'])
+ if not data[0]:
+ return data[1]
+
+ conf = getJsonPath('config')
+ content = mw.readFile(conf)
+ cobj = json.loads(content)
+
+ cobj['safe_verify']['time'] = args['time']
+ cobj['safe_verify']['cpu'] = int(args['cpu'])
+ cobj['safe_verify']['mode'] = args['mode']
+
+ if args['auto'] == '0':
+ cobj['safe_verify']['auto'] = False
+ else:
+ cobj['safe_verify']['auto'] = True
+
+ cjson = mw.getJson(cobj)
+ mw.writeFile(conf, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!', [])
+
+
+def setSiteRetry():
+ return mw.returnJson(True, '设置成功-?!', [])
+
+
+def setCcConf():
+ args = getArgs()
+ data = checkArgs(args, ['siteName', 'cycle', 'limit',
+ 'endtime', 'is_open_global'])
+ if not data[0]:
+ return data[1]
+
+ conf = getJsonPath('config')
+ content = mw.readFile(conf)
+ cobj = json.loads(content)
+
+ tmp = cobj['cc']
+
+ tmp['cycle'] = int(args['cycle'])
+ tmp['limit'] = int(args['limit'])
+ tmp['endtime'] = int(args['endtime'])
+ tmp['is_open_global'] = args['is_open_global']
+ tmp['increase'] = args['increase']
+ cobj['cc'] = tmp
+
+ cjson = mw.getJson(cobj)
+ mw.writeFile(conf, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!', [])
+
+
+def setSiteCcConf():
+ return mw.returnJson(False, '暂未开发!', [])
+
+
+def saveScanRule():
+ args = getArgs()
+ data = checkArgs(args, ['header', 'cookie', 'args'])
+ if not data[0]:
+ return data[1]
+
+ path = getRuleJsonPath('scan_black')
+ cjson = mw.getJson(args)
+ mw.writeFile(path, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!', [])
+
+
+def getSiteConfig():
+ path = getJsonPath('site')
+ content = mw.readFile(path)
+
+ content = json.loads(content)
+
+ total = getJsonPath('total')
+ total_content = mw.readFile(total)
+ total_content = json.loads(total_content)
+
+ # print total_content
+
+ for x in content:
+ tmp = []
+ tmp_v = {}
+ if 'sites' in total_content and x in total_content['sites']:
+ tmp_v = total_content['sites'][x]
+
+ key_list = ['get', 'post', 'user-agent', 'cookie', 'cdn', 'cc']
+ for kx in range(len(key_list)):
+ ktmp = {}
+
+ if kx in tmp_v:
+ ktmp['value'] = tmp_v[key_list[kx]]
+ else:
+ ktmp['value'] = ''
+ ktmp['key'] = key_list[kx]
+ tmp.append(ktmp)
+
+ # print tmp
+ content[x]['total'] = tmp
+
+ content = mw.getJson(content)
+ return mw.returnJson(True, 'ok!', content)
+
+
+def getSiteConfigByName():
+ args = getArgs()
+ data = checkArgs(args, ['siteName'])
+ if not data[0]:
+ return data[1]
+ path = getJsonPath('site')
+ content = mw.readFile(path)
+ content = json.loads(content)
+
+ siteName = args['siteName']
+ retData = {}
+ if siteName in content:
+ retData = content[siteName]
+
+ return mw.returnJson(True, 'ok!', retData)
+
+
+def addSiteCdnHeader():
+ args = getArgs()
+ data = checkArgs(args, ['siteName', 'cdn_header'])
+ if not data[0]:
+ return data[1]
+ path = getJsonPath('site')
+ content = mw.readFile(path)
+ content = json.loads(content)
+
+ siteName = args['siteName']
+ retData = {}
+ if siteName in content:
+ content[siteName]['cdn_header'].append(args['cdn_header'])
+
+ cjson = mw.getJson(content)
+ mw.writeFile(path, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '添加成功!')
+
+
+def removeSiteCdnHeader():
+ args = getArgs()
+ data = checkArgs(args, ['siteName', 'cdn_header'])
+ if not data[0]:
+ return data[1]
+ path = getJsonPath('site')
+ content = mw.readFile(path)
+ content = json.loads(content)
+
+ siteName = args['siteName']
+ retData = {}
+ if siteName in content:
+ content[siteName]['cdn_header'].remove(args['cdn_header'])
+
+ cjson = mw.getJson(content)
+ mw.writeFile(path, cjson)
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '删除成功!')
+
+
+def outputData():
+ args = getArgs()
+ data = checkArgs(args, ['sname'])
+ if not data[0]:
+ return data[1]
+
+ path = getRuleJsonPath(args['sname'])
+ content = mw.readFile(path)
+ return mw.returnJson(True, 'ok', content)
+
+
+def importData():
+ args = getArgs()
+ data = checkArgs(args, ['sname', 'pdata'])
+ if not data[0]:
+ return data[1]
+
+ path = getRuleJsonPath(args['sname'])
+
+ source_data = mw.readFile(path)
+ source_data = json.loads(source_data)
+
+ save_data = []
+ save_data.append(source_data[0])
+ pdata = args['pdata'].strip()
+ try:
+ pdata = json.loads(pdata)
+ mw.writeFile(path, json.dumps(pdata))
+ except Exception as e:
+ pdata = pdata.split("\\n")
+ for x in pdata:
+ pval = x.strip()
+ if pval != "":
+ vv = json.loads(pval)
+ save_data.append(vv[0])
+ mw.writeFile(path, json.dumps(save_data))
+ # restartWeb()
+ return mw.returnJson(True, '设置成功!')
+
+
+def getLogsList():
+ args = getArgs()
+ data = checkArgs(args, ['site', 'page', 'page_size', 'tojs'])
+ if not data[0]:
+ return data[1]
+
+ page = int(args['page'])
+ page_size = int(args['page_size'])
+ domain = args['site']
+ tojs = args['tojs']
+
+ setDefaultSite(domain)
+
+ conn = pSqliteDb('logs')
+
+ field = 'time,ip,domain,server_name,method,uri,user_agent,rule_name,reason'
+ limit = str(page_size) + ' offset ' + str(page_size * (page - 1))
+
+ condition = ''
+ conn = conn.field(field)
+ conn = conn.where("1=1", ()).where("domain=?", (domain,))
+
+ clist = conn.limit(limit).order('time desc').inquiry()
+ count_key = "count(*) as num"
+ count = conn.field(count_key).limit('').order('').inquiry()
+ # print(count)
+ count = count[0][count_key]
+
+ data = {}
+ _page = {}
+ _page['count'] = count
+ _page['p'] = page
+ _page['row'] = page_size
+ _page['tojs'] = tojs
+ data['page'] = mw.getPage(_page)
+ data['data'] = clist
+
+ return mw.returnJson(True, 'ok!', data)
+
+
+def getSafeLogs():
+ args = getArgs()
+ data = checkArgs(args, ['siteName', 'toDate', 'p'])
+ if not data[0]:
+ return data[1]
+
+ path = getServerDir() + '/logs'
+ file = path + '/' + args['siteName'] + '_' + args['toDate'] + '.log'
+ if not os.path.exists(file):
+ return mw.returnJson(False, "文件不存在!")
+
+ retData = []
+ file = open(file)
+ while 1:
+ lines = file.readlines(100000)
+ if not lines:
+ break
+ for line in lines:
+
+ retData.append(json.loads(line))
+
+ return mw.returnJson(True, '设置成功!', retData)
+
+
+def setObjOpen():
+ args = getArgs()
+ data = checkArgs(args, ['obj'])
+ if not data[0]:
+ return data[1]
+
+ conf = getJsonPath('config')
+ content = mw.readFile(conf)
+ cobj = json.loads(content)
+
+ o = args['obj']
+ if cobj[o]["open"]:
+ cobj[o]["open"] = False
+ else:
+ cobj[o]["open"] = True
+
+ cjson = mw.getJson(cobj)
+ mw.writeFile(conf, cjson)
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!')
+
+
+def setSiteObjOpen():
+ args = getArgs()
+ data = checkArgs(args, ['siteName', 'obj'])
+ if not data[0]:
+ return data[1]
+
+ siteName = args['siteName']
+ obj = args['obj']
+
+ path = getJsonPath('site')
+ content = mw.readFile(path)
+ content = json.loads(content)
+
+ if type(content[siteName][obj]) == bool:
+ if content[siteName][obj]:
+ content[siteName][obj] = False
+ else:
+ content[siteName][obj] = True
+ else:
+ if content[siteName][obj]['open']:
+ content[siteName][obj]['open'] = False
+ else:
+ content[siteName][obj]['open'] = True
+
+ cjson = mw.getJson(content)
+ mw.writeFile(path, cjson)
+ setConfRestartWeb()
+ return mw.returnJson(True, '设置成功!')
+
+
+def getWafSrceen():
+ conf = getJsonPath('total')
+ return mw.readFile(conf)
+
+
+def getWafConf():
+ conf = getJsonPath('config')
+ return mw.readFile(conf)
+
+
+def areaLimitSwitch():
+ args = getArgs()
+ data = checkArgs(args, ['area_limit'])
+ if not data[0]:
+ return data[1]
+
+ path_config = getJsonPath('config')
+
+ config_contents = mw.readFile(path_config)
+ config_contents = json.loads(config_contents)
+
+ msg = '关闭成功!'
+ if args['area_limit'] == 'on':
+ msg = '开启成功!'
+ config_contents['area_limit'] = True
+ else:
+ config_contents['area_limit'] = False
+
+ mw.writeFile(path_config, json.dumps(config_contents))
+
+ autoMakeConfig(True, True)
+ restart()
+ return mw.returnJson(True, msg)
+
+
+def getAreaLimit():
+ conf = getJsonPath('area_limit')
+ if not os.path.exists(conf):
+ mw.writeFile(conf, '[]')
+
+ d = mw.readFile(conf)
+ data = json.loads(d)
+ return mw.returnJson(True, 'ok!', data)
+
+
+def delAreaLimit():
+ args = getArgs()
+ data = checkArgs(args, ['site', 'types', 'region'])
+ if not data[0]:
+ return data[1]
+
+ type_list = ["refuse", "accept"]
+ if not args['types'] in type_list:
+ return mw.returnJson(False, '输入的类型错误!')
+
+ region_l = args['region'].split(",")
+ site_l = args['site'].split(",")
+
+ paramMode = {}
+ for i in region_l:
+ if not i:
+ continue
+ i = i.strip()
+ if not i in paramMode:
+ paramMode[i] = "1"
+
+ sitesMode = {}
+ for i in site_l:
+ i = i.strip()
+ if not i:
+ continue
+
+ if not i in sitesMode:
+ sitesMode[i] = "1"
+
+ if len(paramMode) == 0:
+ return mw.returnJson(False, '输入的请求类型错误!')
+ if len(sitesMode) == 0:
+ return mw.returnJson(False, '输入的站点错误!')
+
+ conf = getJsonPath('area_limit')
+ t_data = json.loads(mw.readFile(conf))
+
+ data = {"site": sitesMode, "types": args['types'], "region": paramMode}
+ if not data in t_data:
+ return mw.returnJson(False, '不存在!')
+
+ t_data.remove(data)
+ mw.writeFile(conf, json.dumps(t_data))
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '删除成功!')
+
+
+def addAreaLimit():
+ args = getArgs()
+ data = checkArgs(args, ['site', 'types', 'region'])
+ if not data[0]:
+ return data[1]
+
+ type_list = ["refuse", "accept"]
+ if not args['types'] in type_list:
+ return mw.returnJson(False, '输入的类型错误!')
+
+ region_l = args['region'].split(",")
+ site_l = args['site'].split(",")
+
+ paramMode = {}
+ for i in region_l:
+ if not i:
+ continue
+ i = i.strip()
+ if not i in paramMode:
+ paramMode[i] = "1"
+
+ if '海外' in paramMode and '中国' in paramMode:
+ return mw.returnJson(False, '不允许设置【中国大陆】和【中国大陆以外地区】一同开启地区限制!')
+
+ sitesMode = {}
+ for i in site_l:
+ i = i.strip()
+ if not i:
+ continue
+
+ if not i in sitesMode:
+ sitesMode[i] = "1"
+
+ if len(paramMode) == 0:
+ return mw.returnJson(False, '输入的请求类型错误!')
+ if len(sitesMode) == 0:
+ return mw.returnJson(False, '输入的站点错误!')
+
+ conf = getJsonPath('area_limit')
+ t_data = json.loads(mw.readFile(conf))
+
+ data = {"site": sitesMode, "types": args['types'], "region": paramMode}
+ if data in t_data:
+ return mw.returnJson(False, '已存在!')
+
+ t_data.insert(0, data)
+ mw.writeFile(conf, json.dumps(t_data))
+
+ setConfRestartWeb()
+ return mw.returnJson(True, '添加成功!')
+
+
+def cleanDropIp():
+ url = "http://127.0.0.1/clean_waf_drop_ip"
+ data = mw.httpGet(url)
+ return mw.returnJson(True, 'ok!', data)
+
+
+def testRun():
+ # args = getArgs()
+ # data = checkArgs(args, ['siteName'])
+ # if not data[0]:
+ # return data[1]
+
+ default_path = getServerDir() + "/waf/default.pl"
+ default_site = mw.readFile(default_path)
+ url = "http://" + default_site + '/?t=../etc/passwd'
+ returnData = mw.httpGet(url, 10)
+
+ # url = "https://" + default_site + '/?t=../etc/passwd'
+ # returnData = mw.httpGet(url, 3)
+ return mw.returnJson(True, '测试运行成功!', returnData)
+
+
+def installPreInspection():
+ check_op = mw.getServerDir() + "/openresty"
+ if not os.path.exists(check_op):
+ return "请先安装OpenResty"
+ return 'ok'
+
+
+if __name__ == "__main__":
+ 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 == 'install_pre_inspection':
+ print(installPreInspection())
+ elif func == 'conf':
+ print(getConf())
+ elif func == 'get_rule':
+ print(getRule())
+ elif func == 'add_rule':
+ print(addRule())
+ elif func == 'remove_rule':
+ print(removeRule())
+ elif func == 'set_rule_state':
+ print(setRuleState())
+ elif func == 'modify_rule':
+ print(modifyRule())
+ elif func == 'get_site_rule':
+ print(getSiteRule())
+ elif func == 'add_site_rule':
+ print(addSiteRule())
+ elif func == 'add_ip_white':
+ print(addIpWhite())
+ elif func == 'remove_ip_white':
+ print(removeIpWhite())
+ elif func == 'add_ip_black':
+ print(addIpBlack())
+ elif func == 'remove_ip_black':
+ print(removeIpBlack())
+ elif func == 'set_ipv6_black':
+ print(setIpv6Black())
+ elif func == 'del_ipv6_black':
+ print(delIpv6Black())
+ elif func == 'remove_site_rule':
+ print(removeSiteRule())
+ elif func == 'set_obj_status':
+ print(setObjStatus())
+ elif func == 'set_obj_open':
+ print(setObjOpen())
+ elif func == 'set_site_obj_open':
+ print(setSiteObjOpen())
+ elif func == 'set_cc_conf':
+ print(setCcConf())
+ elif func == 'set_site_cc_conf':
+ print(setSiteCcConf())
+ elif func == 'set_retry':
+ print(setRetry())
+ elif func == 'set_safe_verify':
+ print(setSafeVerify())
+ elif func == 'set_site_retry':
+ print(setSiteRetry())
+ elif func == 'save_scan_rule':
+ print(saveScanRule())
+ elif func == 'get_site_config':
+ print(getSiteConfig())
+ elif func == 'get_default_site':
+ print(getDefaultSite())
+ elif func == 'get_country':
+ print(getCountry())
+ elif func == 'get_site_config_byname':
+ print(getSiteConfigByName())
+ elif func == 'add_site_cdn_header':
+ print(addSiteCdnHeader())
+ elif func == 'remove_site_cdn_header':
+ print(removeSiteCdnHeader())
+ elif func == 'get_logs_list':
+ print(getLogsList())
+ elif func == 'get_safe_logs':
+ print(getSafeLogs())
+ elif func == 'output_data':
+ print(outputData())
+ elif func == 'import_data':
+ print(importData())
+ elif func == 'waf_srceen':
+ print(getWafSrceen())
+ elif func == 'waf_conf':
+ print(getWafConf())
+ elif func == 'area_limit_switch':
+ print(areaLimitSwitch())
+ elif func == 'get_area_limit':
+ print(getAreaLimit())
+ elif func == 'add_area_limit':
+ print(addAreaLimit())
+ elif func == 'del_area_limit':
+ print(delAreaLimit())
+ elif func == 'clean_drop_ip':
+ print(cleanDropIp())
+ elif func == 'test_run':
+ print(testRun())
+ else:
+ print('error')
diff --git a/plugins/op_waf/info.json b/plugins/op_waf/info.json
new file mode 100755
index 000000000..fe18198c7
--- /dev/null
+++ b/plugins/op_waf/info.json
@@ -0,0 +1,29 @@
+{
+ "hook":[
+ {
+ "tag":"site_cb",
+ "site_cb": {
+ "title":"网站统计",
+ "name":"op_waf",
+ "add":{"func":"reload_hook"},
+ "update":{"func":"reload_hook"},
+ "delete":{"func":"reload_hook"}
+ }
+ }
+ ],
+ "sort":2,
+ "title":"OP防火墙",
+ "tip":"soft",
+ "name":"op_waf",
+ "type":"其他插件",
+ "ps":"有效防止sql注入/xss/一句话木马等常见渗透攻击",
+ "install_pre_inspection":true,
+ "shell":"install.sh",
+ "checks":"server/op_waf",
+ "path":"server/op_waf",
+ "author":"loveshell",
+ "home":"https://github.com/loveshell/ngx_lua_waf",
+ "date":"2019-04-21",
+ "pid": "1",
+ "versions": ["0.4.1"]
+}
\ No newline at end of file
diff --git a/plugins/op_waf/install.old.sh b/plugins/op_waf/install.old.sh
new file mode 100755
index 000000000..51ba267e4
--- /dev/null
+++ b/plugins/op_waf/install.old.sh
@@ -0,0 +1,141 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# cd /www/server/mdserver-web/plugins/op_waf && bash install.sh install 0.4.1
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+action=$1
+version=$2
+sys_os=`uname`
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+if [ "$sys_os" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+# /www/server/openresty/luajit/bin/luajit /www/server/op_waf/waf/lua/waf_common.lua
+# /www/server/openresty/luajit/bin/luajit -bl /www/server/op_waf/waf/lua/waf_common.lua
+# /www/server/openresty/luajit/bin/luajit /www/server/web_conf/nginx/lua/access_by_lua_file.lua
+
+
+Install_App(){
+
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/source/op_waf
+ mkdir -p $serverPath/op_waf
+
+ # luarocks
+ if [ ! -f $serverPath/source/op_waf/luarocks-3.5.0.tar.gz ];then
+ wget --no-check-certificate -O $serverPath/source/op_waf/luarocks-3.5.0.tar.gz http://luarocks.org/releases/luarocks-3.5.0.tar.gz
+ fi
+
+ # which luarocks
+ if [ ! -d $serverPath/op_waf/luarocks ];then
+ cd $serverPath/source/op_waf && tar xvf luarocks-3.5.0.tar.gz
+ # cd luarocks-3.9.1 && ./configure && make bootstrap
+
+ cd luarocks-3.5.0 && ./configure --prefix=$serverPath/op_waf/luarocks \
+ --with-lua-include=$serverPath/openresty/luajit/include/luajit-2.1 \
+ --with-lua-bin=$serverPath/openresty/luajit/bin
+ make -I${serverPath}/openresty/luajit/bin
+ make install
+ fi
+
+
+ if [ ! -f $serverPath/source/op_waf/lsqlite3_fsl09y.zip ];then
+ wget --no-check-certificate -O $serverPath/source/op_waf/lsqlite3_fsl09y.zip http://lua.sqlite.org/index.cgi/zip/lsqlite3_fsl09y.zip?uuid=fsl_9y
+ cd $serverPath/source/op_waf && unzip lsqlite3_fsl09y.zip
+ fi
+
+ if [ ! -d $serverPath/source/op_waf/lsqlite3_fsl09y ];then
+ cd $serverPath/source/op_waf && unzip lsqlite3_fsl09y.zip
+ fi
+
+ PATH=${serverPath}/openresty/luajit:${serverPath}/openresty/luajit/include/luajit-2.1:$PATH
+ export PATH=$PATH:$serverPath/op_waf/luarocks/bin
+
+ if [ ! -f $serverPath/op_waf/waf/conf/lsqlite3.so ];then
+ if [ "${sys_os}" == "Darwin" ];then
+ cd $serverPath/source/op_waf/lsqlite3_fsl09y
+ find_cfg=`cat Makefile | grep 'SQLITE_DIR'`
+ if [ "$find_cfg" == "" ];then
+ LIB_SQLITE_DIR=`brew info sqlite | grep /usr/local/Cellar/sqlite | cut -d \ -f 1 | awk 'END {print}'`
+ echo $LIB_SQLITE_DIR
+ sed -i $BAK "s#\$(ROCKSPEC)#\$(ROCKSPEC) SQLITE_DIR=${LIB_SQLITE_DIR}#g" Makefile
+ fi
+ make
+ else
+ cd $serverPath/source/op_waf/lsqlite3_fsl09y && make
+ fi
+ fi
+
+ # copy to code path
+ DEFAULT_DIR=$serverPath/op_waf/luarocks/lib/lua/5.1
+ if [ -f ${DEFAULT_DIR}/lsqlite3.so ];then
+ mkdir -p $serverPath/op_waf/waf/conf
+ cp -rf ${DEFAULT_DIR}/lsqlite3.so $serverPath/op_waf/waf/conf/lsqlite3.so
+ fi
+
+ cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ HTTP_PREFIX="https://"
+ if [ ! -z "$cn" ];then
+ HTTP_PREFIX="https://mirror.ghproxy.com/"
+ fi
+
+ # download GeoLite Data
+ GeoLite2_TAG=`curl -sL "https://api.github.com/repos/P3TERX/GeoLite.mmdb/releases/latest" | grep '"tag_name":' | cut -d'"' -f4`
+ #if [ ! -f $serverPath/op_waf/GeoLite2-City.mmdb ];then
+ wget --no-check-certificate -O $serverPath/op_waf/GeoLite2-City.mmdb ${HTTP_PREFIX}github.com/P3TERX/GeoLite.mmdb/releases/download/${GeoLite2_TAG}/GeoLite2-City.mmdb
+ #fi
+
+ #if [ ! -f $serverPath/op_waf/GeoLite2-Country.mmdb ];then
+ wget --no-check-certificate -O $serverPath/op_waf/GeoLite2-Country.mmdb ${HTTP_PREFIX}github.com/P3TERX/GeoLite.mmdb/releases/download/${GeoLite2_TAG}/GeoLite2-Country.mmdb
+ #fi
+
+ libmaxminddb_ver='1.7.1'
+ if [ ! -f $serverPath/op_waf/waf/mmdb/lib/libmaxminddb.a ] && [ ! -f $serverPath/op_waf/waf/mmdb/lib/libmaxminddb.so ];then
+ libmaxminddb_local_path=$serverPath/source/op_waf/libmaxminddb-${libmaxminddb_ver}.tar.gz
+ libmaxminddb_url_path=${HTTP_PREFIX}github.com/maxmind/libmaxminddb/releases/download/${libmaxminddb_ver}/libmaxminddb-${libmaxminddb_ver}.tar.gz
+ if [ ! -f ${libmaxminddb_local_path} ]; then
+ wget --no-check-certificate -O ${libmaxminddb_local_path} ${libmaxminddb_url_path}
+ fi
+
+ cd $serverPath/source/op_waf && tar -zxvf ${libmaxminddb_local_path} && \
+ cd $serverPath/source/op_waf/libmaxminddb-${libmaxminddb_ver} && \
+ ./configure --prefix=$serverPath/op_waf/waf/mmdb && make && make install
+ fi
+
+ echo "${version}" > $serverPath/op_waf/version.pl
+ echo '安装OP防火墙成功!'
+
+ cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py start
+ echo "cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py start"
+ sleep 2
+ cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py reload
+}
+
+Uninstall_App(){
+
+ cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py stop
+ if [ "$?" == "0" ];then
+ rm -rf $serverPath/op_waf
+ fi
+}
+
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/op_waf/install.sh b/plugins/op_waf/install.sh
new file mode 100755
index 000000000..58d8578fb
--- /dev/null
+++ b/plugins/op_waf/install.sh
@@ -0,0 +1,155 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# cd /www/server/mdserver-web/plugins/op_waf && bash install.sh install 0.4.1
+# cd /www/server/mdserver-web && python3 plugins/op_waf/index.py start
+# cd /www/server/mdserver-web && python3 plugins/op_waf/index.py stop
+# cd /www/server/mdserver-web && python3 plugins/op_waf/tool_task.py run
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+action=$1
+version=$2
+sys_os=`uname`
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+if [ "$sys_os" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+# /www/server/openresty/luajit/bin/luajit /www/server/op_waf/waf/lua/waf_common.lua
+# /www/server/openresty/luajit/bin/luajit -bl /www/server/op_waf/waf/lua/waf_common.lua
+# /www/server/openresty/luajit/bin/luajit /www/server/web_conf/nginx/lua/access_by_lua_file.lua
+
+
+Install_App(){
+
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/source/op_waf
+ mkdir -p $serverPath/op_waf
+
+ # luarocks
+ if [ ! -f $serverPath/source/op_waf/luarocks-3.5.0.tar.gz ];then
+ wget --no-check-certificate -O $serverPath/source/op_waf/luarocks-3.5.0.tar.gz http://luarocks.org/releases/luarocks-3.5.0.tar.gz
+ fi
+
+ # which luarocks
+ if [ ! -d $serverPath/op_waf/luarocks ];then
+ cd $serverPath/source/op_waf && tar xvf luarocks-3.5.0.tar.gz
+ # cd luarocks-3.9.1 && ./configure && make bootstrap
+
+ cd luarocks-3.5.0 && ./configure --prefix=$serverPath/op_waf/luarocks \
+ --with-lua-include=$serverPath/openresty/luajit/include/luajit-2.1 \
+ --with-lua-bin=$serverPath/openresty/luajit/bin
+ make -I${serverPath}/openresty/luajit/bin
+ make install
+ fi
+
+
+ # if [ ! -f $serverPath/source/op_waf/lsqlite3_v096.zip ];then
+ # wget --no-check-certificate -O $serverPath/source/op_waf/lsqlite3_v096.zip http://lua.sqlite.org/home/zip/lsqlite3_v096.zip?uuid=v0.9.6
+ # fi
+
+ if [ ! -f $serverPath/source/op_waf/lsqlite3_v096.zip ];then
+ wget --no-check-certificate -O $serverPath/source/op_waf/lsqlite3_v096.zip https://github.com/midoks/mdserver-web/releases/download/0.18.4/lsqlite3_v096.zip
+ fi
+
+ if [ ! -d $serverPath/source/op_waf/lsqlite3_v096 ];then
+ cd $serverPath/source/op_waf && unzip lsqlite3_v096.zip
+ fi
+
+ PATH=${serverPath}/openresty/luajit:${serverPath}/openresty/luajit/include/luajit-2.1:$PATH
+ export PATH=$PATH:$serverPath/op_waf/luarocks/bin
+
+ if [ ! -f $serverPath/op_waf/waf/conf/lsqlite3.so ];then
+ if [ "${sys_os}" == "Darwin" ];then
+ cd $serverPath/source/op_waf/lsqlite3_v096
+ find_cfg=`cat Makefile | grep 'SQLITE_DIR'`
+ if [ "$find_cfg" == "" ];then
+ LIB_SQLITE_DIR=`brew info sqlite | grep /opt/homebrew/Cellar/sqlite | cut -d \ -f 1 | awk 'END {print}'`
+ echo $LIB_SQLITE_DIR
+ sed -i $BAK "s#\$(ROCKSPEC)#\$(ROCKSPEC) SQLITE_DIR=${LIB_SQLITE_DIR}#g" Makefile
+ fi
+ make
+ else
+ cd $serverPath/source/op_waf/lsqlite3_v096 && make
+ fi
+ fi
+
+ # copy to code path
+ DEFAULT_DIR=$serverPath/op_waf/luarocks/lib/lua/5.1
+ if [ -f ${DEFAULT_DIR}/lsqlite3.so ];then
+ mkdir -p $serverPath/op_waf/waf/conf
+ cp -rf ${DEFAULT_DIR}/lsqlite3.so $serverPath/op_waf/waf/conf/lsqlite3.so
+ fi
+
+ cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ HTTP_PREFIX="https://"
+ if [ ! -z "$cn" ];then
+ HTTP_PREFIX="https://gh-proxy.com/"
+ fi
+
+ # download GeoLite Data
+ GeoLite2_TAG=`curl -sL "https://api.github.com/repos/P3TERX/GeoLite.mmdb/releases/latest" | grep '"tag_name":' | cut -d'"' -f4`
+ if [ ! -f $serverPath/source/op_waf/GeoLite2-City.mmdb ];then
+ wget --no-check-certificate -O $serverPath/source/op_waf/GeoLite2-City.mmdb ${HTTP_PREFIX}github.com/P3TERX/GeoLite.mmdb/releases/download/${GeoLite2_TAG}/GeoLite2-City.mmdb
+ fi
+
+ if [ ! -f $serverPath/op_waf/GeoLite2-City.mmdb ];then
+ cp -rf $serverPath/source/op_waf/GeoLite2-City.mmdb $serverPath/op_waf/GeoLite2-City.mmdb
+ fi
+
+ if [ ! -f $serverPath/source/op_waf/GeoLite2-Country.mmdb ];then
+ wget --no-check-certificate -O $serverPath/source/op_waf/GeoLite2-Country.mmdb ${HTTP_PREFIX}github.com/P3TERX/GeoLite.mmdb/releases/download/${GeoLite2_TAG}/GeoLite2-Country.mmdb
+ fi
+
+ if [ ! -f $serverPath/op_waf/GeoLite2-Country.mmdb ];then
+ cp -rf $serverPath/source/op_waf/GeoLite2-Country.mmdb $serverPath/op_waf/GeoLite2-Country.mmdb
+ fi
+
+
+ libmaxminddb_ver='1.12.2'
+ if [ ! -f $serverPath/op_waf/waf/mmdb/lib/libmaxminddb.a ] && [ ! -f $serverPath/op_waf/waf/mmdb/lib/libmaxminddb.so ];then
+ libmaxminddb_local_path=$serverPath/source/op_waf/libmaxminddb-${libmaxminddb_ver}.tar.gz
+ libmaxminddb_url_path=${HTTP_PREFIX}github.com/maxmind/libmaxminddb/releases/download/${libmaxminddb_ver}/libmaxminddb-${libmaxminddb_ver}.tar.gz
+ if [ ! -f ${libmaxminddb_local_path} ]; then
+ wget --no-check-certificate -O ${libmaxminddb_local_path} ${libmaxminddb_url_path}
+ fi
+
+ cd $serverPath/source/op_waf && tar -zxvf ${libmaxminddb_local_path} && \
+ cd $serverPath/source/op_waf/libmaxminddb-${libmaxminddb_ver} && \
+ ./configure --prefix=$serverPath/op_waf/waf/mmdb && make && make install
+ fi
+
+ echo "${version}" > $serverPath/op_waf/version.pl
+ echo '安装OP防火墙成功!'
+
+ cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py start
+ echo "cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py start"
+ sleep 2
+ cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py reload
+}
+
+Uninstall_App(){
+ cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py stop
+ if [ "$?" == "0" ];then
+ rm -rf $serverPath/op_waf
+ fi
+}
+
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/op_waf/js/op_waf.js b/plugins/op_waf/js/op_waf.js
new file mode 100755
index 000000000..5440c5a69
--- /dev/null
+++ b/plugins/op_waf/js/op_waf.js
@@ -0,0 +1,2105 @@
+
+function owPost(method, args, callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+ $.post('/plugins/run', {name:'op_waf', func:method, args:JSON.stringify(args)}, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function owPostN(method, args, callback){
+ $.post('/plugins/run', {name:'op_waf', func:method, args:JSON.stringify(args)}, function(data) {
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+function getRuleByName(rule_name, callback){
+ owPost('get_rule', {rule_name:rule_name}, function(data){
+ callback(data);
+ });
+}
+
+
+function setRequestCode(ruleName, statusCode){
+ layer.open({
+ type: 1,
+ title: "设置响应代码【" + ruleName + "】",
+ area: '300px',
+ shift: 5,
+ closeBtn: 1,
+ shadeClose: true,
+ content: ''
+ });
+}
+
+function setState(ruleName){
+ var statusCode = $('#statusCode').val();
+ owPost('set_obj_status', {obj:ruleName,statusCode:statusCode},function(data){
+ var rdata = $.parseJSON(data.data);
+ if (rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ wafGloabl();
+ } else {
+ layer.msg('设置失败!',{icon:0,time:2000,shade: [0.3, '#000']});
+ }
+ });
+}
+
+function setObjOpen(ruleName){
+ owPost('set_obj_open', {obj:ruleName},function(data){
+ var rdata = $.parseJSON(data.data);
+ if (rdata.status){
+
+ showMsg(rdata.msg, function(){
+ wafGloabl();
+ },{icon:1,time:2000,shade: [0.3, '#000']},2000);
+ } else {
+ layer.msg('设置失败!',{icon:0,time:2000,shade: [0.3, '#000']});
+ }
+ });
+}
+
+
+//保存CC规则
+function saveCcRule(siteName,is_open_global, type) {
+ var increase = "0";
+ if(type == 2){
+ // set_aicc_open('start');
+ increase = "0";
+ } else {
+ // set_aicc_open('stop');
+ increase = type;
+ }
+ increase = "0";
+ var pdata = {
+ siteName:siteName,
+ cycle: $("input[name='cc_cycle']").val(),
+ limit: $("input[name='cc_limit']").val(),
+ endtime: $("input[name='cc_endtime']").val(),
+ is_open_global:is_open_global,
+ increase:increase
+ }
+ console.log(pdata);
+ var act = 'set_cc_conf';
+ if (siteName != 'undefined') act = 'set_site_cc_conf';
+
+ owPost(act, pdata, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ setTimeout(function(){
+ if (siteName != 'undefined') {
+ siteWafConfig(siteName, 1);
+ } else {
+ wafGloabl();
+ }
+ },1000);
+ });
+}
+
+
+function setCcRule(cycle, limit, endtime, siteName, increase){
+ var incstr = '此处设置仅对当前站点有效。 ';
+ if (siteName == 'undefined') {
+ incstr = '此处设置的是初始值,新添加站点时将继承,对现有站点无效。 ';
+ }
+ // \
+ //
增强模式 \
+ //
\
+ // \
+ // 关闭 \
+ // 开启 \
+ // \
+ //
\
+ //
\
+ // \
+ //
四层防御 \
+ //
\
+ // \
+ // 关闭 \
+ // 开启 \
+ // \
+ //
\
+ //
\
+ //增强模式:CC防御加强版,开启后可能会影响用户体验,建议在用户受到CC攻击时开启。 \
+
+ create_l = layer.open({
+ type: 1,
+ title: "设置CC规则",
+ area: '540px',
+ closeBtn: 1,
+ shadeClose: false,
+ content: '',
+ success:function(layero,index){
+ $('.btn_cc_all').click(function(){
+ saveCcRule(siteName,1,$('[name="enhance_mode"]').val());
+ });
+ $('.btn_cc_present').click(function(){
+ saveCcRule(siteName,0,$('[name="enhance_mode"]').val());
+ });
+ }
+ });
+}
+
+
+//设置retry规则
+function setRetry(retry_cycle, retry, retry_time, siteName) {
+ create_layer = layer.open({
+ type: 1,
+ title: "设置恶意容忍规则",
+ area: '500px',
+ closeBtn: 1,
+ shadeClose: false,
+ content: '',
+ success:function(){
+ $('.btn_retry_all').click(function(){
+ saveRetry(siteName,1);
+ });
+ $('.btn_retry_present').click(function(){
+ saveRetry(siteName,0);
+ });
+ }
+ });
+}
+
+
+
+//设置safe_verify规则
+function setSafeVerify(auto, cpu, time, mode,siteName) {
+ var svlayer = layer.open({
+ type: 1,
+ title: "设置强制安全验证",
+ area: '500px',
+ closeBtn: 1,
+ shadeClose: false,
+ content: '',
+ success:function(index){
+ $('.btn_sv_present').click(function(){
+ var pdata = {
+ siteName: siteName,
+ cpu: $("input[name='cpu']").val(),
+ auto: $("select[name='auto']").val(),
+ mode: $("select[name='mode']").val(),
+ time: $("input[name='time']").val(),
+ }
+ var act = 'set_safe_verify';
+ owPost(act, pdata, function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg, function() {
+ layer.close(svlayer);
+ wafGloabl();
+ },{ icon: rdata.status ? 1 : 2 },1000);
+ });
+ });
+
+
+ },
+ });
+}
+
+
+//保存retry规则
+function saveRetry(siteName,type) {
+ var pdata = {
+ siteName: siteName,
+ retry: $("input[name='retry']").val(),
+ retry_time: $("input[name='retry_time']").val(),
+ retry_cycle: $("input[name='retry_cycle']").val(),
+ is_open_global:type
+ }
+
+ var act = 'set_retry';
+ if (siteName != undefined) act = 'set_site_retry';
+ owPost(act, pdata, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ layer.close(create_layer);
+ wafGloablRefresh(1000);
+ });
+}
+
+function addRule(ruleName) {
+ var pdata = {
+ 'ruleValue': $("input[name='ruleValue']").val(),
+ 'ps': $("input[name='rulePs']").val(),
+ 'ruleName': ruleName
+ }
+
+ owPost('add_rule', pdata, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ if (rdata.status) {
+ setTimeout(function(){
+ setObjConf(ruleName, 1);
+ },1000);
+ }
+ });
+}
+
+function modifyRule(index, ruleName) {
+ var ruleValue = $('.rule_body_' + index).text();
+ $('.rule_body_' + index).html('');
+ var rulePs = $('.rule_ps_' + index).text();
+ $('.rule_ps_' + index).html(' ');
+ $('.rule_modify_' + index).html('保存 | 取消 ');
+ $(".modr_cancel_" + index).click(function () {
+ $('.rule_body_' + index).html(ruleValue);
+ $('.rule_ps_' + index).html(rulePs);
+ $('.rule_modify_' + index).html('编辑 ');
+ })
+}
+
+function modifyRuleSave(index, ruleName) {
+ var pdata = {
+ index: index,
+ ruleName: ruleName,
+ ruleBody: $("textarea[name='rule_body_" + index + "']").val(),
+ rulePs: $("input[name='rule_ps_" + index + "']").val()
+ }
+
+ owPost('modify_rule', pdata, function(data){
+ var rdata = $.parseJSON(data.data);
+
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ if (rdata.status) {
+ setTimeout(function(){
+ setObjConf(ruleName, 1);
+ },1000);
+ }
+ });
+}
+
+function removeRule(ruleName, index) {
+ var pdata = {
+ 'index': index,
+ 'ruleName': ruleName
+ }
+ safeMessage('删除规则', '您真的要删除这条过滤规则吗?', function () {
+ owPost('remove_rule', pdata, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ if (rdata.status) {
+ setTimeout(function(){
+ setObjConf(ruleName, 1);
+ },1000);
+ }
+ });
+ });
+}
+
+function setRuleState(ruleName, index) {
+ var pdata = {
+ 'index': index,
+ 'ruleName': ruleName
+ }
+
+ owPost('set_rule_state', pdata, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ if (rdata.status) {
+ setTimeout(function(){
+ setObjConf(ruleName, 1);
+ },1000);
+ }
+ });
+}
+
+//设置规则
+function setObjConf(ruleName, type) {
+ if (type == undefined) {
+ create_l = layer.open({
+ type: 1,
+ title: "编辑规则【" + ruleName + "】",
+ area: ['700px', '530px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: '\
+
\
+ \
+ \
+ 添加 \
\
+
\
+
\
+
\
+ \
+ \
+ 规则 \
+ 说明 \
+ 操作 \
+ 状态 \
+ \
+ \
+ \
+
\
+
\
+
\
+
\
+ 注意:如果您不了解正则表达式,请不要随意修改规则内容 \
+ 您可以添加或修改规则内容,但请使用正则表达式 \
+ 内置规则允许修改,但不可以直接删除,您可以设置规则状态来定义防火墙是否使用此规则 \
+ '
+ });
+ tableFixed("jc-file-table");
+ }
+
+ getRuleByName(ruleName, function(data){
+ var tmp = $.parseJSON(data.data);
+ var rdata = $.parseJSON(tmp.data);
+ var tbody = ''
+ for (var i = 0; i < rdata.length; i++) {
+ var removeRule = ''
+ if (rdata[i][3] != 0) removeRule = ' | 删除 ';
+ tbody += '\
+ ' + rdata[i][1] + ' \
+ ' + rdata[i][2] + ' \
+ 编辑 ' + removeRule + ' \
+ \
+ \
+ \
+ \
+
\
+ \
+ '
+ }
+ $("#set_obj_conf_con").html(tbody);
+ });
+}
+
+
+//常用扫描器
+function scanRule() {
+
+ getRuleByName('scan_black', function(data){
+ var tmp = $.parseJSON(data.data);
+ var rdata = $.parseJSON(tmp.data);
+
+ create_l = layer.open({
+ type: 1,
+ title: "常用扫描器过滤规则",
+ area: '650px',
+ closeBtn: 1,
+ shadeClose: false,
+ content: ''
+ });
+ });
+}
+
+//保存扫描器规则
+function saveScanRule() {
+ pdata = {
+ header: $("textarea[name='scan_header']").val(),
+ cookie: $("textarea[name='scan_cookie']").val(),
+ args: $("textarea[name='scan_args']").val()
+ }
+ owPost('save_scan_rule', pdata,function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ layer.close(create_l);
+ wafGloablRefresh(1000);
+ });
+}
+
+//添加IP段到IP白名单
+function addIpWhite() {
+ var pdata = {
+ start_ip: $("input[name='start_ip']").val(),
+ end_ip: $("input[name='end_ip']").val()
+ }
+
+ if (pdata['start_ip'].split('.').length < 4 || pdata['end_ip'].split('.').length < 4) {
+ layer.msg('起始IP或结束IP格式不正确!');
+ return;
+ }
+
+ owPost('add_ip_white', pdata, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ if (rdata.status) {
+ setTimeout(function(){
+ ipWhite(1);
+ },1000);
+ }
+ });
+}
+
+//从IP白名单删除IP段
+function removeIpWhite(index) {
+ owPost('remove_ip_white', { index: index }, function(data){
+ var rdata = $.parseJSON(data.data);
+ if (rdata.status) {
+ setTimeout(function(){
+ ipWhite(1);
+ },1000);
+ }
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+
+function funDownload(content, filename) {
+ // 创建隐藏的可下载链接
+ var eleLink = document.createElement('a');
+ eleLink.download = filename;
+ eleLink.style.display = 'none';
+ // 字符内容转变成blob地址
+ var blob = new Blob([content]);
+ eleLink.href = URL.createObjectURL(blob);
+ // 触发点击
+ document.body.appendChild(eleLink);
+ eleLink.click();
+ // 然后移除
+ document.body.removeChild(eleLink);
+}
+
+function outputLayer(rdata, name, type) {
+ window.Load_layer = layer.open({
+ type: 1,
+ title: type ? "导出数据" : "导入数据",
+ area: ['400px', '370px'],
+ shadeClose: false,
+ content: '' +
+ '
' +
+ '
' +
+ '
' + (rdata != '' ? JSON.stringify(rdata) : '') + ' ' +
+ '
导入格式如下:' +
+ (name == 'ip_white' || name == 'ip_black' ? "[[[127, 0, 0, 1],[127, 0, 0, 255]],[[192, 0, 0, 1],[192, 0, 0, 255]]]" : "[\"^/test\",\"^/web\"]") +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ (type ? '导出配置 ' : '保存 ') +
+ '
' +
+ '
',
+ });
+ var lead_error = CodeMirror.fromTextArea(document.getElementById("lead_data"), {
+ mode: 'html',
+ matchBrackets: true,
+ matchtags: true,
+ autoMatchParens: true
+ });
+ setTimeout(function () {
+ $('.btn_save').on('click', function () {
+ importData(name, lead_error.getValue(), function(){
+ layer.close(window.Load_layer);
+ ipWhiteLoadList();
+ });
+ })
+ $('.btn_save_to').on('click', function () {
+ funDownload(lead_error.getValue(), name + '.json');
+ });
+ $('#focus_tips').on('click', function () {
+ $('.placeholder').hide();
+ });
+ }, 100);
+}
+
+
+//导出数据
+function outputData(name, callback) {
+ var loadT = layer.msg('正在导出数据..', { icon: 16, time: 0 });
+
+ owPost('output_data', { sname: name } , function(data){
+ var tmp = $.parseJSON(data.data);
+ var rdata = $.parseJSON(tmp.data);
+ if (callback) callback(rdata,res);
+ outputLayer(rdata, name, true);
+ });
+}
+
+//导入数据
+function importData(name, pdata, callback) {
+ owPost('import_data', { sname: name, pdata: pdata } , function(data){
+ var rdata = $.parseJSON(data.data);
+ if (callback) callback();
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+function fileInput(name) {
+ outputLayer('', name, false);
+}
+
+
+function ipWhiteLoadList(){
+ getRuleByName('ip_white', function(data){
+ var tmp = $.parseJSON(data.data);
+ var rdata = $.parseJSON(tmp.data);
+ var tbody = ''
+ for (var i = 0; i < rdata.length; i++) {
+ tbody += '\
+ '+ rdata[i][0].join('.') + ' \
+ '+ rdata[i][1].join('.') + ' \
+ 删除 \
+ '
+ }
+ $("#ip_white_con").html(tbody);
+ });
+}
+//IP白名单
+function ipWhite(type) {
+ if (type == undefined) {
+ create_l = layer.open({
+ type: 1,
+ title: "管理IP白名单",
+ area: ['500px', '500px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: '\
+
\
+ \
+ \
+ 添加 \
\
+
\
+
\
+
\
+ \
+ \
+ 超始IP \
+ 结束IP \
+ 操作 \
+ \
+ \
+ \
+
\
+
\
+
\
+
\
+ 导入 \
+ 导出 \
+
\
+
\
+ 所有规则对白名单中的IP段无效,包括IP黑名单和URL黑名单,IP白名单具备最高优先权 \
+ \
+
\
+ \
+
',
+ success:function(index,layero){
+ // $('.tab_list .tab_block').click(function(){
+ // $(this).addClass('active').siblings().removeClass('active');
+ // console.log($(this).index());
+ // if($(this).index() === 0){
+ // $('.ipv4_list').show().next().hide();
+ // }else{
+ // $('.ipv4_list').hide().next().show();
+ // }
+ // });
+ // \
+ }
+ });
+ tableFixed("ipWhite");
+ }
+ ipWhiteLoadList();
+}
+
+//IP白名单
+function urlWhite(type) {
+
+ var ruleName = "url_white";
+
+ if (type == undefined) {
+ create_l = layer.open({
+ type: 1,
+ title: "管理URL白名单",
+ area: ['700px', '530px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: '\
+
\
+ \
+ \
+ 添加 \
\
+
\
+
\
+
\
+ \
+ \
+ 规则 \
+ 说明 \
+ 操作 \
+ 状态 \
+ \
+ \
+ \
+
\
+
\
+
\
+
\
+ 注意:如果您不了解正则表达式,请不要随意修改规则内容 \
+ 您可以添加或修改规则内容,但请使用正则表达式 \
+ 内置规则允许修改,但不可以直接删除,您可以设置规则状态来定义防火墙是否使用此规则 \
+ '
+ });
+ tableFixed("jc-file-table");
+ }
+
+ getRuleByName(ruleName, function(data){
+ var tmp = $.parseJSON(data.data);
+ var rdata = $.parseJSON(tmp.data);
+ console.log(rdata);
+ var tbody = ''
+ for (var i = 0; i < rdata.length; i++) {
+ var removeRule = ''
+ if (rdata[i][3] != 0) removeRule = ' | 删除 ';
+ tbody += '\
+ ' + rdata[i][1] + ' \
+ ' + rdata[i][2] + ' \
+ 编辑 ' + removeRule + ' \
+ \
+ \
+ \
+ \
+
\
+ \
+ '
+ }
+ $("#set_obj_conf_con").html(tbody);
+ });
+}
+
+
+// 获取IPV4黑名单
+function getIpv4Address(callback){
+ getRuleByName('ip_black', function(data){
+ var tmp = $.parseJSON(data.data);
+ var rdata = $.parseJSON(tmp.data);
+ callback(rdata);
+ });
+}
+
+// 获取IPV6黑名单
+function getIpv6Address(callback){
+ getRuleByName('ipv6_black', function(data){
+ var tmp = $.parseJSON(data.data);
+ var rdata = $.parseJSON(tmp.data);
+ callback(rdata);
+ });
+}
+
+
+// 添加ipv6请求
+function addIpv6Req(ip,callback){
+ var ip = ip.replace(/:/g, '_');
+ owPost('set_ipv6_black', {addr:ip}, function(data){
+ var rdata = $.parseJSON(data.data);
+ if(callback) callback(rdata);
+ });
+}
+
+// 添加ipv6请求
+function removeIpv6Black(ip,callback){
+ var ip = ip.replace(/:/g, '_');
+ owPost('del_ipv6_black', {addr:ip}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2});
+ $('.tab_list .tab_block:eq(1)').click();
+
+ if(callback) callback(rdata);
+ });
+}
+
+//添加IP段到IP黑名单
+function addIpBlack() {
+ var pdata = {
+ start_ip: $("input[name='start_ip']").val(),
+ end_ip: $("input[name='end_ip']").val()
+ }
+
+ if (pdata['start_ip'].split('.').length < 4 || pdata['end_ip'].split('.').length < 4) {
+ layer.msg('起始IP或结束IP格式不正确!');
+ return;
+ }
+
+ owPost('add_ip_black', pdata, function(data){
+ var rdata = $.parseJSON(data.data);
+ if (rdata.status) {
+ ipBlack(1);
+ }
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+function addIpBlackArgs(ip) {
+ var pdata = {
+ start_ip: ip,
+ end_ip: ip,
+ }
+
+ if (pdata['start_ip'].split('.').length < 4 || pdata['end_ip'].split('.').length < 4) {
+ layer.msg('起始IP或结束IP格式不正确!');
+ return;
+ }
+
+ owPost('add_ip_black', pdata, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+
+//从IP黑名单删除IP段
+function removeIpBlack(index) {
+ owPost('remove_ip_black', { index: index }, function (data) {
+ var rdata = $.parseJSON(data.data);
+ if (rdata.status) {
+ ipBlack(1);
+ }
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+//IP黑名单
+function ipBlack(type) {
+ if (type == undefined) {
+ create_l = layer.open({
+ type: 1,
+ title: "管理IP黑名单",
+ area: ['500px', '500px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: '\
+ \
+
\
+ \
+ \
+ 添加 \
\
+
\
+
\
+
\
+ \
+ \
+ 超始IP \
+ 结束IP \
+ 操作 \
+ \
+ \
+ \
+
\
+
\
+
\
+ 导入 \
+ 导出 \
+
\
+
\
+
\
+ 黑名单中的IP段将被禁止访问,IP白名单中已存在的除外 \
+ \
+
\
+ \
+
\
+ \
+ 添加 \
+
\
+
\
+
\
+ 黑名单中的IP段将被禁止访问,IP白名单中已存在的除外 \
+ \
+
',
+ success:function(index,layero){
+ $('.tab_list .tab_block').click(function(){
+ $(this).addClass('active').siblings().removeClass('active');
+ if($(this).index() === 0){
+ $('.ipv4_block').show().next().hide();
+ getIpv4Address(function(rdata){
+ var tbody = ''
+ for (var i = 0; i < rdata.length; i++) {
+ tbody += '\
+ '+ rdata[i][0].join('.') + ' \
+ '+ rdata[i][1].join('.') + ' \
+ 删除 \
+ '
+ }
+ $("#ip_black_con").html(tbody);
+ });
+ }else{
+ $('.ipv4_block').hide().next().show();
+ getIpv6Address(function(res){
+ var tbody = '',rdata = res;
+ for (var i = 0; i < rdata.length; i++) {
+ tbody += '\
+ '+ rdata[i] + ' \
+ 删除 \
+ '
+ }
+ $("#ipv6_black_con").html(tbody);
+ });
+ }
+ });
+ $('.btn_add_ipv6').click(function(){
+ var ipv6 = $('[name="ipv6_address"]').val();
+ addIpv6Req(ipv6, function(res){
+ layer.msg(res.msg,{icon:res.status?1:2});
+ if(res.status){
+ $('[name="ipv6_address"]').val('');
+ $('.tab_list .tab_block:eq(1)').click();
+ }
+ });
+ });
+ $('.tab_list .tab_block:eq(0)').click();
+ }
+ });
+ tableFixed("ipBlack");
+ } else {
+ $('.tab_list .tab_block:eq(0)').click();
+ }
+}
+
+function wafScreen(){
+
+ owPost('waf_srceen', {}, function(data){
+ var rdata = $.parseJSON(data.data);
+
+ var end_time = Date.now();
+ var cos_time = (end_time/1000) - parseInt(rdata['start_time']);
+ var cos_day = parseInt(parseInt(cos_time)/86400);
+
+ var con = '总拦截'+rdata.total+' 次
';
+ con += '安全防护'+cos_day+' 天
';
+
+ con += '\
+
POST渗透 '+rdata.rules.post+'
\
+
GET渗透 '+rdata.rules.args+'
\
+
CC攻击 '+rdata.rules.cc+'
\
+
恶意User-Agent '+rdata.rules.user_agent+'
\
+
Cookie渗透 '+rdata.rules.cookie+'
\
+
恶意扫描 '+rdata.rules.scan+'
\
+
恶意HEAD请求 0
\
+
URI自定义拦截 '+rdata.rules.url+'
\
+
URI保护 '+rdata.rules.args+'
\
+
恶意文件上传 '+rdata.rules.upload_ext+'
\
+
禁止的扩展名 '+rdata.rules.path+'
\
+
禁止PHP脚本 '+rdata.rules.php_path+'
\
+
';
+
+ con += '\
+ 在此处关闭防火墙后,所有站点将失去保护 \
+ 网站防火墙会使nginx有一定的性能损失(<5% 10C静态并发测试结果) \
+ 网站防火墙仅主要针对网站渗透攻击,暂时不具备系统加固功能 \
+ ';
+
+ $(".soft-man-con").html(con);
+ });
+}
+
+function wafGloablRefresh(time){
+ setTimeout(function(){
+ wafGloabl();
+ }, time);
+}
+
+function wafGloabl(){
+ owPost('waf_conf', {}, function(data){
+ var rdata = $.parseJSON(data.data);
+
+ var con = '';
+
+
+ con += '\
+ 继承: 全局设置将在站点配置中自动继承为默认值 \
+ 优先级: IP白名单>IP黑名单>URL白名单>URL黑名单>CC防御>User-Agent>URI过滤>URL参数>Cookie>POST \
+ ';
+ $(".soft-man-con").html(con);
+ });
+}
+
+//返回css
+function back_css(v) {
+ if (v > 0) {
+ return 'tipsval'
+ }
+ else {
+ return 'tipsval tipsvalnull'
+ }
+}
+
+function html_encode(value) {
+ return $('
').html(value).text();
+}
+
+function html_decode(value) {
+ return $('
').text(value).html();
+}
+
+//添加站点过滤规则
+function addSiteRule(siteName, ruleName) {
+ var pdata = {
+ ruleValue: $("input[name='site_rule_value']").val(),
+ siteName: siteName,
+ ruleName: ruleName
+ }
+
+ if (pdata['ruleValue'] == '') {
+ layer.msg('过滤规则不能为空');
+ $("input[name='site_rule_value']").focus();
+ return;
+ }
+
+ owPost('add_site_rule', pdata, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ if (rdata.status) {
+ setTimeout(function(){
+ siteRuleAdmin(siteName, ruleName, 1);
+ },1000);
+ }
+ });
+}
+
+//删除站点过滤规则
+function removeSiteRule(siteName, ruleName, index) {
+ var pdata = {
+ index: index,
+ siteName: siteName,
+ ruleName: ruleName
+ }
+
+ owPost('remove_site_rule', pdata, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ if (rdata.status) {
+ if (ruleName == 'url_tell') {
+ site_url_tell(siteName, 1);
+ return;
+ }
+
+ if (ruleName == 'url_rule') {
+ site_url_rule(siteName, 1);
+ return;
+ }
+
+ setTimeout(function(){
+ siteRuleAdmin(siteName, ruleName, 1);
+ },1000);
+ }
+ });
+}
+
+//网站规则管理
+function siteRuleAdmin(siteName, ruleName, type) {
+ var placeho = '';
+ var ps = '';
+ var title = '';
+ switch (ruleName) {
+ case 'disable_php_path':
+ placeho = 'URI地址,支持正则表达式';
+ ps = '此处请不要包含URI参数,一般针对目录URL,示例:/admin '
+ title = '禁止运行PHP的URL地址'
+ break;
+ case 'disable_path':
+ placeho = 'URI地址,支持正则表达式';
+ ps = '此处请不要包含URI参数,一般针对目录URL,示例:/admin '
+ title = '禁止访问的URL地址'
+ break;
+ case 'disable_ext':
+ placeho = '扩展名,不包含点(.),示例:sql';
+ ps = '直接填要被禁止访问的扩展名,如我希望禁止访问*.sql文件:sql '
+ title = '禁止访问的扩展名'
+ break;
+ case 'disable_upload_ext':
+ placeho = '扩展名,不包含点(.),示例:sql';
+ ps = '直接填要被禁止访问的扩展名,如我希望禁止上传*.php文件:php '
+ title = '禁止上传的文件类型'
+ break;
+ }
+ if (type == undefined) {
+ create_l = layer.open({
+ type: 1,
+ title: "管理网站过滤规则【" + title + "】",
+ area: ['500px', '500px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: '\
+
\
+ \
+ 添加 \
\
+
\
+
\
+
\
+ \
+ \
+ 规则 \
+ 操作 \
+ \
+ \
+ \
+
\
+
\
+
\
+
\
+ 除正则表达式语句外规则值对大小写不敏感,建议统一使用小写 '+ ps + '\
+ '
+ });
+ tableFixed("siteRuleAdmin");
+ }
+
+ owPost('get_site_rule', { siteName: siteName, ruleName: ruleName }, function(data){
+ var tmp = $.parseJSON(data.data);
+ var rdata = $.parseJSON(tmp.data);
+ var tbody = ''
+ for (var i = 0; i < rdata.length; i++) {
+ tbody += '\
+ '+ rdata[i] + ' \
+ 删除 \
+ '
+ }
+ $("#site_rule_admin_con").html(tbody);
+ });
+}
+
+//CDN-Header配置
+function cdnHeader(siteName, type) {
+ if (type == undefined) {
+ create_l = layer.open({
+ type: 1,
+ title: "管理网站【" + siteName + "】CDN-Headers",
+ area: ['500px', '500px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: '\
+
\
+ \
+ 添加 \
\
+
\
+ \
+
\
+
\
+ 防火墙将尝试在以上header中获取客户IP \
+ '
+ });
+ tableFixed("cdnHeader");
+ }
+
+ owPost('get_site_config_byname', { siteName: siteName }, function(data){
+ var tmp = $.parseJSON(data.data);
+ var t1 = tmp.data;
+ var rdata = t1['cdn_header'];
+ var tbody = ''
+ for (var i = 0; i < rdata.length; i++) {
+ tbody += '\
+ '+ rdata[i] + ' \
+ 删除 \
+ '
+ }
+ $("#cdn_header_con").html(tbody);
+ });
+}
+
+//添加CDN-Header
+function addCdnHeader(siteName) {
+ var pdata = {
+ cdn_header: $("input[name='cdn_header_key']").val(),
+ siteName: siteName
+ }
+
+ if (pdata['cdn_header'] == '') {
+ layer.msg('header不能为空');
+ $("input[name='cdn_header_key']").focus();
+ return;
+ }
+
+ owPost('add_site_cdn_header', pdata, function(data){
+ var rdata = $.parseJSON(data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ if (rdata.status) {
+ setTimeout(function(){
+ cdnHeader(siteName, 1);
+ },1000);
+ }
+ });
+}
+
+ //删除CDN-Header
+function removeCdnHeader(siteName, cdn_header_key) {
+ owPost('remove_site_cdn_header', { siteName: siteName, cdn_header: cdn_header_key }, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ if (rdata.status) {
+ setTimeout(function(){
+ cdnHeader(siteName, 1);
+ },1000);
+ }
+ });
+}
+
+//设置网站防御功能
+function setSiteObjState(siteName, obj) {
+ // var loadT = layer.msg('正在处理,请稍候..', { icon: 16, time: 0 });
+ owPost('set_site_obj_open', { siteName: siteName, obj: obj } , function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ setTimeout(function(){
+ siteWafConfig(siteName, 1);
+ // siteConfig();
+ },1000);
+ });
+ // $.post('/plugin?action=a&name=btwaf&s=set_site_obj_open', { siteName: siteName, obj: obj }, function (rdata) {
+ // layer.close(loadT);
+ // site_waf_config(siteName, 1);
+ // siteconfig();
+ // layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ // });
+}
+
+
+//网站规则设置
+function setSiteObjConf(siteName, ruleName, type) {
+ if (type == undefined) {
+ create_l = layer.open({
+ type: 1,
+ title: "编辑网站【" + siteName + "】规则【" + ruleName + "】",
+ area: ['700px', '530px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: '\
+
\
+
\
+
\
+ \
+ \
+ 规则 \
+ 说明 \
+ 状态 \
+ \
+ \
+ \
+
\
+
\
+
\
+
\
+ 此处继承全局设置中已启用的规则 \
+ 此处的设置仅对当前站点有效 \
+ '
+ });
+ tableFixed("SetSiteObjConf");
+ }
+
+ getRuleByName(ruleName, function(data){
+ var tmp = $.parseJSON(data.data);
+ var rdata = $.parseJSON(tmp.data);
+ var tbody = '';
+ var tbody = '';
+ for (var i = 0; i < rdata.length; i++) {
+ if (rdata[i][0] == -1) continue;
+ tbody += '\
+ '+ rdata[i][1] + ' \
+ '+ rdata[i][2] + ' \
+ \
+ \
+
\
+ \
+ '
+ }
+ $("#set_site_obj_conf_con").html(tbody)
+ });
+}
+
+//网站设置
+function siteWafConfig(siteName, type) {
+ if (type == undefined) {
+ create_2 = layer.open({
+ type: 1,
+ title: "网站配置【" + siteName + "】",
+ area: ['700px', '500px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: '
'
+ });
+ }
+
+ owPost('get_site_config_byname', { siteName: siteName }, function(data){
+ var tmp = $.parseJSON(data.data);
+ var rdata = tmp.data;
+ nginx_config = rdata;
+ var con = '\
+
\
+
网站防火墙开关 \
+
\
+ \
+ \
+
\
+
\
+
\
+
\
+ 注意: 此处大部分配置,仅对当前站点有效! \
+ \
+
';
+ $("#s_w_c").html(con);
+ });
+}
+
+
+
+function wafSite(){
+ owPost('get_site_config', {}, function(data){
+ var tmp = $.parseJSON(data.data);
+ var rdata = $.parseJSON(tmp.data);
+ var tbody = '';
+ var i = 0;
+ $.each(rdata, function (k, v) {
+ i += 1;
+ tbody += '\
+ ' + k + ' \
+ ' + v.total[1].value + ' \
+ ' + v.total[0].value + ' \
+ ' + v.total[3].value + ' \
+ ' + v.total[4].value + ' \
+ \
+ ' + v.total[2].value + ' \
+ \
+ \
+ \
+ \
+
\
+ \
+ 日志 \
+ ';
+ //| 设置
+ });
+
+ var con = '\
+
\
+
\
+
\
+
\
+ \
+ \
+ 站点 \
+ GET \
+ POST \
+ UA \
+ Cookie \
+ CDN \
+ CC防御 \
+ 状态 \
+ 操作 \
+ \
+ \
+ '+ tbody + ' \
+
\
+
\
+
\
+
\
+
';
+ $(".soft-man-con").html(con);
+ tableFixed("siteCon_fix");
+ });
+}
+
+
+function wafAreaLimitRender(){
+ function keyVal(obj){
+ var str = [];
+ $.each(obj, function (index, item) {
+ if (item == 1) {
+ if (index == 'allsite') index = '所有站点';
+ if (index == '海外') index = '中国大陆以外的地区(包括[港,澳,台])';
+ if (index == '中国') index = '中国大陆(不包括[港,澳,台])';
+ str.push(index);
+ }
+ });
+ return str.toString();
+ }
+ owPost('get_area_limit', {}, function(rdata) {
+ var rdata = $.parseJSON(rdata.data);
+ if (!rdata.status) {
+ layer.msg(rdata.msg, { icon: 2, time: 2000 });
+ return;
+ }
+
+ var list = '';
+ var rlist = rdata.data;
+
+ for (var i = 0; i < rlist.length; i++) {
+ var op = '';
+ var type = rlist[i]['types'] === 'refuse' ? '拦截' : '只放行';
+ var region_str = keyVal(rlist[i]['region']);
+ var site_str = keyVal(rlist[i]['site']);
+
+ op += '删除 ';
+
+ list += '';
+ list += '' + region_str + ' ';
+ list += '' + site_str + ' ';
+ list += '' + type + ' ';
+
+ list += '' + op + ' ';
+ list += ' ';
+ }
+
+ $('#con_list tbody').html(list);
+ $('.area_limit_del').click(function(){
+ var data_id = $(this).data('id');
+
+ var site = [],region = [];
+ $.each(rlist[data_id]['site'], function (index, item) {
+ site.push(index);
+ });
+ $.each(rlist[data_id]['region'], function (index, item) {
+ region.push(index);
+ });
+
+ var type = rlist[data_id]['types'];
+
+ owPost('del_area_limit', {
+ site:site.toString(),
+ region:region.toString(),
+ types:type,
+ }, function(rdata) {
+ var rdata = $.parseJSON(rdata.data);
+ showMsg(rdata.msg, function(){
+ if (rdata.status){
+ wafAreaLimit();
+ }
+ },{ icon: rdata.status ? 1 : 2 });
+ });
+ });
+ });
+}
+
+function wafAreaLimitSwitch(){
+ owPostN('waf_conf', {}, function(data){
+ var rdata = $.parseJSON(data.data);
+ if (rdata['area_limit']){
+ $('#area_limit_switch').prop('checked', true);
+ } else{
+ $('#area_limit_switch').prop('checked',false);
+ }
+ });
+}
+
+function setWafAreaLimitSwitch(){
+ var area_limit_switch = $('#area_limit_switch').prop('checked');
+ // console.log(area_limit_switch);
+ var area_limit = 'off';
+ if (!area_limit_switch){
+ area_limit = 'on';
+ }
+ owPostN('area_limit_switch', {'area_limit': area_limit}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+// 地区限制
+function wafAreaLimit(){
+ var con = '\
+
添加地区限制 \
+
\
+
\
+
\
+
';
+ $(".soft-man-con").html(con);
+ wafAreaLimitRender();
+ wafAreaLimitSwitch();
+
+ $('#create_area_limit').click(function(){
+ var site_list;
+ var area_list;
+ var site_length = 0;
+ layer.open({
+ type: 1,
+ title: '添加地区限制',
+ area: ['450px','280px'],
+ closeBtn: 1,
+ btn: ['添加', '取消'],
+ content: '',
+ success: function (layers, index) {
+ document.getElementById('layui-layer' + index).getElementsByClassName('layui-layer-content')[0].style.overflow = 'unset';
+
+ site_list = xmSelect.render({
+ el: '#site_list',
+ language: 'zn',
+ toolbar: {show: true,},
+ paging: true,
+ pageSize: 10,
+ data: [],
+ });
+
+ owPostN('get_default_site','', function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var rlist = rdata.data.list;
+
+
+ var pdata = [];
+ for (var i = 0; i < rlist.length; i++) {
+ var tval = rlist[i];
+ if (tval != 'unset'){
+ var t = {name:rlist[i],value:rlist[i]};
+ pdata.push(t);
+ }
+ }
+ site_length = pdata.length;
+ site_list.update({data:pdata});
+ });
+
+ area_list = xmSelect.render({
+ el: '#area_list',
+ language: 'zn',
+ toolbar: {show: true,},
+ filterable: true,
+ data: [],
+ });
+ owPostN('get_country','', function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var rlist = rdata.data;
+
+ var pdata = [];
+ for (var i = 0; i < rlist.length; i++) {
+ var tval = rlist[i];
+ if (tval != 'unset'){
+ var t = {name:tval,value:tval};
+ pdata.push(t);
+ }
+ }
+
+ area_list.update({data:pdata});
+ });
+ },
+ yes: function (indexs) {
+
+ var reg_type = $('select[name="type"]').val();
+ var site_val = site_list.getValue('value');
+ var area_val = area_list.getValue('value');
+
+ if (area_val.length <1) return layer.msg('地区最少选一个!', { icon: 2 });
+ if (site_val.length <1) return layer.msg('站点最少选一个!', { icon: 2 });
+
+ var site = '';
+ if (site_length === site_val.length) {
+ site = 'allsite';
+ } else {
+ site = site_val.join();
+ }
+
+ var area = area_val.join();
+ var region = area.replace('中国大陆以外的地区(包括[中国特别行政区:港,澳,台])', '海外')
+ .replace('中国大陆(不包括[中国特别行政区:港,澳,台])', '中国')
+ .replace('中国香港', '香港')
+ .replace('中国澳门', '澳门')
+ .replace('中国台湾', '台湾');
+
+ owPost('add_area_limit',{
+ site:site,
+ types:reg_type,
+ region:region,
+ }, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ showMsg(rdata.msg, function(){
+ if (rdata.status){
+ layer.close(indexs);
+ wafAreaLimit();
+ }
+ },{ icon: rdata.status ? 1 : 2 });
+ });
+
+ },
+ });
+ });
+}
+
+function wafLogRequest(page){
+ var args = {};
+ args['page'] = page;
+ args['page_size'] = 10;
+ args['site'] = $('select[name="site"]').val();
+
+ var query_date = 'today';
+ if ($('#time_choose').attr("data-name") != ''){
+ query_date = $('#time_choose').attr("data-name");
+ } else {
+ query_date = $('#search_time button.cur').attr("data-name");
+ }
+
+ args['query_date'] = query_date;
+ args['tojs'] = 'wafLogRequest';
+
+ owPost('get_logs_list', args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var list = '';
+ var data = rdata.data.data;
+ if (data.length > 0){
+ for(i in data){
+ list += '';
+ list += '' + getLocalTime(data[i]['time'])+' ';
+ list += '' + data[i]['domain'] +' ';
+ list += '' + data[i]['ip'] +' ';
+ list += '' + data[i]['uri'] +' ';// data[i]['uri']
+ list += '' + data[i]['rule_name'] +' ';
+ list += '' + entitiesEncode(data[i]['reason']) +' ';//data[i]['reason']
+ list += '详情 ';
+ list += ' ';
+ }
+ } else{
+ list += '封锁日志为空 ';
+ }
+
+ var table = '\
+
';
+ $('#ws_table').html(table);
+ $('#wsPage').html(rdata.data.page);
+
+ $(".tablescroll .details").click(function(){
+ var index = $(this).attr('data-id');
+ var res = data[index];
+ var ip = res.ip;
+ var time = getLocalTime(res.time);
+ layer.open({
+ type: 1,
+ title: "【"+res.domain + "】详情",
+ area: '600px',
+ closeBtn: 1,
+ shadeClose: false,
+ content: '\
+
\
+ 时间 '+ time + ' 用户IP ' + escapeHTML(ip) + ' 类型 ' + escapeHTML(res.method) + ' 过滤器 ' + escapeHTML(res.rule_name) + '
\
+
URI地址
\
+
'+ escapeHTML(res.uri) + '
\
+
User-Agent
\
+
'+ escapeHTML(res.user_agent) + '
\
+
过滤规则
\
+
'+ escapeHTML(res.rule_name) + '
\
+
Reason
\
+
'+ escapeHTML(res.reason) + '
\
+
'
+ })
+ });
+ });
+}
+
+function wafLogs(){
+ var randstr = getRandomString(10);
+
+
+ var html = '\
+
\
+
网站: \
+
\
+ 未设置 \
+ \
+
时间: \
+
\
+
\
+ 解封所有 \
+ 测试 \
+
\
+
\
+
\
+
';
+ $(".soft-man-con").html(html);
+ // wafLogRequest(1);
+
+ $("#UncoverAll").click(function(){
+ owPost('clean_drop_ip',{},function(data){
+ var rdata = $.parseJSON(data.data);
+ var ndata = $.parseJSON(rdata.data);
+ if (ndata.status == 0){
+ layer.msg("解封所有成功",{icon:1,time:2000,shade: [0.3, '#000']});
+ } else{
+ layer.msg("解封所有异常:"+ndata.msg,{icon:5,time:2000,shade: [0.3, '#000']});
+ }
+ });
+ });
+
+ //测试demo
+ $("#testRun").click(function(){
+ owPost('test_run',{},function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg, function(){
+ wafLogRequest(1);
+ },{icon:1,shade: [0.3, '#000']},2000);
+ });
+ });
+
+ //日期范围
+ laydate.render({
+ elem: '#time_choose',
+ value:'',
+ range:true,
+ done:function(value, startDate, endDate){
+ if(!value){
+ return false;
+ }
+
+ $('#search_time button').each(function(){
+ $(this).removeClass('cur');
+ });
+
+ var timeA = value.split('-');
+ var start = $.trim(timeA[0]+'-'+timeA[1]+'-'+timeA[2])
+ var end = $.trim(timeA[3]+'-'+timeA[4]+'-'+timeA[5])
+ query_txt = toUnixTime(start + " 00:00:00") + "-"+ toUnixTime(end + " 00:00:00")
+
+ $('#time_choose').attr("data-name",query_txt);
+ $('#time_choose').addClass("cur");
+
+ wafLogRequest(1);
+ },
+ });
+
+ $('#search_time button:eq(0)').addClass('cur');
+ $('#search_time button').click(function(){
+ $('#search_time button').each(function(){
+ if ($(this).hasClass('cur')){
+ $(this).removeClass('cur');
+ }
+ });
+ $('#time_choose').attr("data-name",'');
+ $('#time_choose').removeClass("cur");
+
+ $(this).addClass('cur');
+
+ wafLogRequest(1);
+ });
+
+ owPostN('get_default_site',{},function(rdata){
+ $('select[name="site"]').html('');
+
+ var rdata = $.parseJSON(rdata.data);
+ var rdata = rdata.data;
+ var default_site = rdata["default"];
+ var select = '';
+ for (var i = 0; i < rdata["list"].length; i++) {
+ if (default_site == rdata["list"][i]){
+ select += ''+rdata["list"][i]+' ';
+ } else{
+ select += ''+rdata["list"][i]+' ';
+ }
+ }
+ $('select[name="site"]').html(select);
+ wafLogRequest(1);
+
+ $('select[name="site"]').change(function(){
+ wafLogRequest(1);
+ });
+ });
+
+}
+
+
+function wafOpLogs(){
+ var con = '\
+
\
+ 名称 \
+ 描述 \
+ 响应 \
+ 状态 \
+ 操作 \
+ \
+
\
+
';
+ $(".soft-man-con").html(con);
+}
diff --git a/plugins/op_waf/shell/cpu_usage.sh b/plugins/op_waf/shell/cpu_usage.sh
new file mode 100644
index 000000000..4de0bf2c3
--- /dev/null
+++ b/plugins/op_waf/shell/cpu_usage.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+DST_DIR=/www/server/op_waf
+DIS_FILE=${DST_DIR}/cpu.info
+
+CPU_USAGE=`top -bn 1 | fgrep 'Cpu(s)' | awk '{print 100 -$8}' | awk -F . '{print $1}'`
+echo $CPU_USAGE
+echo $CPU_USAGE > $DIS_FILE
+echo "done success!"
\ No newline at end of file
diff --git a/plugins/op_waf/shell/cpu_usage_file.sh b/plugins/op_waf/shell/cpu_usage_file.sh
new file mode 100644
index 000000000..d7df7e19a
--- /dev/null
+++ b/plugins/op_waf/shell/cpu_usage_file.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+DST_DIR=/www/server/op_waf
+DIS_FILE=${DST_DIR}/cpu.info
+
+function GetCpuUsage(){
+ cpu_info=`cat /proc/stat | head -n 1`
+ idle_cpu=`echo $cpu_info|awk '{print $2}'`
+ cpu_total_time=0
+ for ci in ${cpu_info[@]}; do
+ if [ "$ci" == "cpu" ];then
+ continue
+ else
+ #echo $ci
+ cpu_total_time=`expr $cpu_total_time + $ci`
+ fi
+ done
+ #echo "idle_cpu:${idle_cpu}"
+ #echo "cpu_total_time:${cpu_total_time}"
+
+ cpu_percet=$(awk "BEGIN{print ((${cpu_total_time}-${idle_cpu})/${cpu_total_time})*100}")
+ echo "${cpu_percet}"
+ return 0
+}
+
+# one value detal
+getOne=`GetCpuUsage`
+
+CPU_USAGE=`echo $getOne | awk -F . '{print $1}'`
+echo "cpu usage:${CPU_USAGE}"
+echo $CPU_USAGE > $DIS_FILE
+echo "done success!"
+
+
+# two value compare
+
+# getOne=`GetCpuUsage`
+# echo "getOne:$getOne"
+# sleep 1
+# getTwo=`GetCpuUsage`
+# echo "getTwo:$getTwo"
+
+# cpu_percet_calc=$(awk "BEGIN{print (${getOne}+${getTwo})/2}")
+# echo "cpu_percet_calc:${cpu_percet_calc}"
+
+# #echo '0.61212' | awk -F . '{print $1}'
+# CPU_USAGE=`echo $cpu_percet_calc | awk -F . '{print $1}'`
+# echo "cpu usage:${CPU_USAGE}"
+# echo $CPU_USAGE > $DIS_FILE
+# echo "done success!"
\ No newline at end of file
diff --git a/plugins/op_waf/t/bench/bench.sh b/plugins/op_waf/t/bench/bench.sh
new file mode 100755
index 000000000..e0f3acd36
--- /dev/null
+++ b/plugins/op_waf/t/bench/bench.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+# echo $rootPath
+
+resty=$rootPath/openresty/bin/resty
+
+RUN_CMD=$resty
+if [ ! -f $resty ];then
+ RUN_CMD=/www/server/openresty/bin/resty
+fi
+
+
+# test
+# $RUN_CMD simple.lua
+# $RUN_CMD test_gsub.lua
+
+# $RUN_CMD --shdict 'limit 10m' test_find_server_name.lua
+# $RUN_CMD --stap --shdict 'limit 10m' test_find_server_name.lua
+
+# $RUN_CMD test_rand.lua
+# $RUN_CMD test_ffi_time.lua
+$RUN_CMD test_get_cpu.lua
\ No newline at end of file
diff --git a/plugins/op_waf/t/bench/simple.lua b/plugins/op_waf/t/bench/simple.lua
new file mode 100755
index 000000000..7f52523a0
--- /dev/null
+++ b/plugins/op_waf/t/bench/simple.lua
@@ -0,0 +1,18 @@
+local function target()
+ ngx.re.find("hello, world.", [[\w+\.]], "jo")
+end
+for i = 1, 100 do
+ target()
+end
+
+collectgarbage()
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e7
+for i = 1, N do
+ target()
+end
+ngx.update_time()
+
+ngx.say("elapsed: ", (ngx.now() - begin) / N)
\ No newline at end of file
diff --git a/plugins/op_waf/t/bench/test_ffi_time.lua b/plugins/op_waf/t/bench/test_ffi_time.lua
new file mode 100644
index 000000000..1e8cfb866
--- /dev/null
+++ b/plugins/op_waf/t/bench/test_ffi_time.lua
@@ -0,0 +1,62 @@
+local function target()
+ ngx.re.find("hello, world.", [[\w+\.]], "jo")
+end
+for i = 1, 100 do
+ target()
+end
+
+-- 以上为预热操作
+collectgarbage()
+
+local ffi = require("ffi")
+ffi.cdef[[
+ struct timeval {
+ long int tv_sec;
+ long int tv_usec;
+ };
+ int gettimeofday(struct timeval *tv, void *tz);
+]];
+local tm = ffi.new("struct timeval");
+
+-- 返回微秒级时间戳
+local function current_time_millis()
+ ffi.C.gettimeofday(tm,nil);
+ local sec = tonumber(tm.tv_sec);
+ local usec = tonumber(tm.tv_usec);
+ return sec + usec * 10^-6;
+end
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e7
+for i = 1, N do
+ target()
+end
+ngx.update_time()
+
+ngx.say("elapsed: ", (ngx.now() - begin) / N)
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e7
+for i = 1, N do
+ target()
+end
+ngx.update_time()
+
+ngx.say("elapsed[1]: ", (ngx.now() - begin) / N)
+
+
+
+
+ngx.update_time()
+local begin = current_time_millis()
+local N = 1e7
+for i = 1, N do
+ target()
+end
+ngx.update_time()
+
+ngx.say("ffi elapsed: ", (current_time_millis() - begin) / N)
\ No newline at end of file
diff --git a/plugins/op_waf/t/bench/test_find_server_name.lua b/plugins/op_waf/t/bench/test_find_server_name.lua
new file mode 100644
index 000000000..89d50dd09
--- /dev/null
+++ b/plugins/op_waf/t/bench/test_find_server_name.lua
@@ -0,0 +1,75 @@
+local function target()
+ ngx.re.find("hello, world.", [[\w+\.]], "jo")
+end
+for i = 1, 100 do
+ target()
+end
+
+-- 以上为预热操作
+collectgarbage()
+
+local config_domains = {
+ [1] = {
+ ["name"] = "t1.cn",
+ ["path"] = "/www/wwwroot/t1.cn",
+ ["domains"] = {
+ [1] = "t1.cn",
+ [2] = "t3.cn"
+ }
+ }
+}
+
+local function get_server_name(request_name)
+ for _,v in ipairs(config_domains)
+ do
+ for _,cd_name in ipairs(v['domains'])
+ do
+ if request_name == cd_name then
+ return v['name']
+ end
+ end
+ end
+ return request_name
+end
+
+
+local function get_server_name_cache(request_name)
+ local cache_name = ngx.shared.limit:get(request_name)
+ if cache_name then return cache_name end
+
+ for _,v in ipairs(config_domains)
+ do
+ for _,cd_name in ipairs(v['domains'])
+ do
+ if request_name == cd_name then
+ ngx.shared.limit:set(cd_name,v['name'],3600)
+ return v['name']
+ end
+ end
+ end
+ return request_name
+end
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e7
+for i = 1, N do
+ get_server_name("t3.cn")
+end
+ngx.update_time()
+
+ngx.say("test get_server_name elapsed: ", (ngx.now() - begin) / N)
+
+
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e7
+for i = 1, N do
+ get_server_name_cache("t3.cn")
+end
+ngx.update_time()
+
+ngx.say("test get_server_name_cache elapsed: ", (ngx.now() - begin) / N)
\ No newline at end of file
diff --git a/plugins/op_waf/t/bench/test_get_cpu.lua b/plugins/op_waf/t/bench/test_get_cpu.lua
new file mode 100644
index 000000000..bfd566084
--- /dev/null
+++ b/plugins/op_waf/t/bench/test_get_cpu.lua
@@ -0,0 +1,69 @@
+-- cd /www/server/mdserver-web/plugins/op_waf/t/bench && bash bench.sh
+
+local function target()
+ ngx.re.find("hello, world.", [[\w+\.]], "jo")
+end
+for i = 1, 100 do
+ target()
+end
+
+local function file_exists(path)
+ local file = io.open(path, "rb")
+ if file then file:close() end
+ return file ~= nil
+end
+
+-- 以上为预热操作
+collectgarbage()
+
+local json = require "cjson"
+local ngx_re = require "ngx.re"
+
+local function data_split(self, str,reps )
+ local rsList = {}
+ string.gsub(str,'[^'..reps..']+',function(w)
+ table.insert(rsList,w)
+ end)
+ return rsList
+end
+
+local function get_cpu_stat()
+ local cpu_total = 0
+ local fp = io.open('/proc/stat','r')
+ local cpu_line = fp:read()
+ fp:close()
+
+ local list = ngx_re.split(cpu_line," ")
+ table.remove(list,1)
+ table.remove(list,1)
+
+ local idie = list[4]
+ for i,v in pairs(list)
+ do
+ cpu_total = cpu_total + v
+ end
+
+ local use_percent = tonumber(100-(idie/cpu_total)*100)
+
+ return cpu_total,idie,use_percent
+end
+
+local function get_cpu_percent()
+ local cpu_total,idie,use_percent = get_cpu_stat()
+ ngx.sleep(2)
+ local cpu_total2,idie2,use_percent2 = get_cpu_stat()
+ local cpu_usage_percent = tonumber(100-(((idie2-idie)/(cpu_total2-cpu_total))*100))
+ ngx.say("cpu_usage_percent:"..cpu_usage_percent)
+ return cpu_usage_percent
+end
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e1
+for i = 1, N do
+ get_cpu_stat()
+end
+ngx.update_time()
+
+ngx.say("test_get_cpu elapsed: ", (ngx.now() - begin))
+
diff --git a/plugins/op_waf/t/bench/test_gsub.lua b/plugins/op_waf/t/bench/test_gsub.lua
new file mode 100644
index 000000000..6d9a7a972
--- /dev/null
+++ b/plugins/op_waf/t/bench/test_gsub.lua
@@ -0,0 +1,47 @@
+
+local function target()
+ ngx.re.find("hello, world.", [[\w+\.]], "jo")
+end
+for i = 1, 100 do
+ target()
+end
+-- 以上为预热操作
+collectgarbage()
+
+local function test_string_gsub(str,reps)
+ local resultStrList = {}
+ string.gsub(str,'[^'..reps..']+', function(w)
+ table.insert(resultStrList,w)
+ return w
+ end)
+end
+
+
+local function test_ngx_string_gsub(str,reps)
+ local resultStrList = {}
+ ngx.re.gsub(str,'[^'..reps..']+', function(w)
+ table.insert(resultStrList,w[0])
+ return w
+ end, "ijo")
+end
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e6
+for i = 1, N do
+ test_string_gsub("2409:8a62:e20:95f0:45b7:233e:f003:c0ab",",")
+end
+ngx.update_time()
+
+ngx.say("test_string_gsub elapsed: ", (ngx.now() - begin) / N)
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e6
+for i = 1, N do
+ test_ngx_string_gsub("2409:8a62:e20:95f0:45b7:233e:f003:c0ab",",")
+end
+ngx.update_time()
+
+ngx.say("test_ngx_string_gsub elapsed: ", (ngx.now() - begin) / N)
diff --git a/plugins/op_waf/t/bench/test_rand.lua b/plugins/op_waf/t/bench/test_rand.lua
new file mode 100644
index 000000000..2c0b51f3c
--- /dev/null
+++ b/plugins/op_waf/t/bench/test_rand.lua
@@ -0,0 +1,72 @@
+local function target()
+ ngx.re.find("hello, world.", [[\w+\.]], "jo")
+end
+for i = 1, 100 do
+ target()
+end
+
+-- 以上为预热操作
+collectgarbage()
+
+
+
+local function get_random_t1(n)
+ math.randomseed(ngx.time())
+ local t = {
+ "0","1","2","3","4","5","6","7","8","9",
+ "a","b","c","d","e","f","g","h","i","j",
+ "k","l","m","n","o","p","q","r","s","t",
+ "u","v","w","x","y","z",
+ "A","B","C","D","E","F","G","H","I","J",
+ "K","L","M","N","O","P","Q","R","S","T",
+ "U","V","W","X","Y","Z",
+ }
+ local s = ""
+ for i = 1, n do
+ s = s .. t[math.random(#t)]
+ end
+ return s
+end
+
+
+
+local function get_random_t2(n)
+ local t = {
+ "0","1","2","3","4","5","6","7","8","9",
+ "a","b","c","d","e","f","g","h","i","j",
+ "k","l","m","n","o","p","q","r","s","t",
+ "u","v","w","x","y","z",
+ "A","B","C","D","E","F","G","H","I","J",
+ "K","L","M","N","O","P","Q","R","S","T",
+ "U","V","W","X","Y","Z",
+ }
+ local s = ""
+ for i = 1, n do
+ s = s .. t[math.random(#t)]
+ end
+ return s
+end
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e5
+for i = 1, N do
+ get_random_t1(16)
+end
+ngx.update_time()
+
+ngx.say("test get_random_t1 elapsed: ", (ngx.now() - begin) / N)
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e5
+math.randomseed(ngx.time())
+for i = 1, N do
+ get_random_t2(16)
+end
+ngx.update_time()
+
+ngx.say("test get_random_t2 elapsed: ", (ngx.now() - begin) / N)
+
+
diff --git a/plugins/op_waf/t/index.py b/plugins/op_waf/t/index.py
new file mode 100644
index 000000000..98bd75f28
--- /dev/null
+++ b/plugins/op_waf/t/index.py
@@ -0,0 +1,328 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import json
+
+import os
+import sys
+import time
+import string
+import json
+import hashlib
+import shlex
+import datetime
+import subprocess
+import re
+from random import Random
+
+
+TEST_URL = "http://t1.cn/"
+# TEST_URL = "https://www.zzzvps.com/"
+
+
+def writeFile(filename, str):
+ # 写文件内容
+ try:
+ fp = open(filename, 'w+')
+ fp.write(str)
+ fp.close()
+ return True
+ except Exception as e:
+ return False
+
+
+def httpGet(url, timeout=10):
+ import urllib.request
+
+ try:
+ req = urllib.request.urlopen(url, timeout=timeout)
+ result = req.read().decode('utf-8')
+ return result
+
+ except Exception as e:
+ return str(e)
+
+
+def httpGet__Header(url, headers, timeout=10):
+ import urllib.request
+ try:
+ req = urllib.request.Request(url, headers=headers)
+ response = urllib.request.urlopen(req)
+ result = response.read().decode('utf-8')
+ return result
+
+ except Exception as e:
+ return str(e)
+
+
+def httpUpload(url, timeout=10):
+ try:
+ import requests
+
+ files = {
+ 'file': open('/Users/midoks/Desktop/mwdev/server/op_waf/version.pl', 'rb')
+ }
+ res = requests.post(url=url, files=files)
+ return res
+ except Exception as e:
+ return "http.upload:" + str(e)
+
+
+def httpUploadPhp(url, timeout=10):
+ try:
+ import requests
+
+ writeFile("/tmp/tmp.php", "")
+
+ files = {
+ 'file': open('/tmp/tmp.php', 'rb')
+ }
+ res = requests.post(url=url, files=files)
+ return res
+ except Exception as e:
+ return "http.upload:" + str(e)
+
+
+def httpUploadPhpData(url, timeout=10):
+ try:
+ import requests
+
+ writeFile("/tmp/tmp.py", "")
+
+ files = {
+ 'file': open('/tmp/tmp.py', 'rb')
+ }
+ res = requests.post(url=url, files=files)
+ return res
+ except Exception as e:
+ return "http.upload:" + str(e)
+
+
+def httpGet__UA(url, ua, timeout=10):
+ import urllib.request
+ headers = {'user-agent': ua}
+ try:
+ req = urllib.request.Request(url, headers=headers)
+ response = urllib.request.urlopen(req)
+ result = response.read().decode('utf-8')
+ return result
+
+ except Exception as e:
+ return str(e)
+
+
+def httpGet__cdn(url, ip, timeout=10):
+ import urllib.request
+ headers = {'x-forwarded-for': ip}
+ try:
+ req = urllib.request.Request(url, headers=headers)
+ response = urllib.request.urlopen(req)
+ result = response.read().decode('utf-8')
+ return result
+
+ except Exception as e:
+ return str(e)
+
+
+def httpPost(url, data, timeout=10):
+ """
+ 发送POST请求
+ @url 被请求的URL地址(必需)
+ @data POST参数,可以是字符串或字典(必需)
+ @timeout 超时时间默认60秒
+ return string
+ """
+ if sys.version_info[0] == 2:
+ try:
+ import urllib
+ import urllib2
+ import ssl
+ ssl._create_default_https_context = ssl._create_unverified_context
+ data = urllib.urlencode(data)
+ req = urllib2.Request(url, data)
+ response = urllib2.urlopen(req, timeout=timeout)
+ return response.read()
+ except Exception as ex:
+ return str(ex)
+ else:
+ try:
+ import urllib.request
+ import ssl
+ try:
+ ssl._create_default_https_context = ssl._create_unverified_context
+ except:
+ pass
+ data = urllib.parse.urlencode(data).encode('utf-8')
+ req = urllib.request.Request(url, data)
+ response = urllib.request.urlopen(req, timeout=timeout)
+ result = response.read()
+ if type(result) == bytes:
+ result = result.decode('utf-8')
+ return result
+ except Exception as ex:
+ return str(ex)
+
+
+def test_Dir():
+ '''
+ 目录保存
+ '''
+ url = TEST_URL + '?t=../etc/passwd'
+ print("args test start")
+ url_val = httpGet(url, 10)
+ print(url_val)
+ print("args test end")
+
+
+def test_UA():
+ '''
+ user-agent 过滤
+ '''
+ url = TEST_URL
+ print("user-agent test start")
+ url_val = httpGet__UA(url, 'ApacheBench')
+ print(url_val)
+ print("user-agent test end")
+
+
+def test_Header():
+ '''
+ user-agent 过滤
+ '''
+ url = TEST_URL
+ print("user-agent test start")
+ url_val = httpGet__Header(url, {'X-forwarded-For': '../etc/passwd'})
+ print(url_val)
+ print("user-agent test end")
+
+
+def test_UA_for(num):
+ '''
+ user-agent 过滤
+ '''
+ url = TEST_URL
+ print("user-agent test start")
+ for x in range(num):
+ url_val = httpGet__UA(url, 'ApacheBench')
+ print(url_val)
+ print("user-agent test end")
+
+
+def test_cdn():
+ '''
+ user-agent 过滤
+ '''
+ url = TEST_URL
+ print("cdn test start")
+ url_val = httpGet__cdn(url, '2409:8a62:e20:95f0:45b7:233e:f003:c0ab')
+ print(url_val)
+
+ url_val2 = httpGet__cdn(url, '91.245.227.173')
+ print(url_val2)
+ print("cdn test end")
+
+
+def test_POST():
+ '''
+ user-agent 过滤
+ '''
+ url = TEST_URL
+ print("POST test start")
+ url_val = httpPost(url, {'data': "substr($mmsss,0,1)"})
+ # url_val = httpPost(url, {'data': "123123"})
+ print(url_val)
+ print("POST test end")
+
+
+def test_scan():
+ '''
+ 目录保存
+ '''
+ url = TEST_URL + 'acunetix_wvs_security_test?t=1'
+ print("scan test start")
+ url_val = httpGet(url, 10)
+ print(url_val)
+ print("scan test end")
+
+
+def test_CC():
+ '''
+ 目录保存
+ '''
+ url = TEST_URL + 'ok.txt'
+ print("CC test start")
+
+ for x in range(122):
+ url_val = httpGet(url, 10)
+ print(url_val)
+
+ print("CC test end")
+
+
+def test_url_ext():
+ '''
+ 目录保存
+ '''
+ url = TEST_URL + 't.sql'
+ print("url_ext start")
+ url_val = httpGet(url, 10)
+ print(url_val)
+
+ print("url_ext end")
+
+
+def test_OK():
+ '''
+ 目录保存
+ '''
+ url = TEST_URL
+ print("ok test start")
+ url_val = httpGet(url, 10)
+ print(url_val)
+ print("ok test end")
+
+
+def test_Upload():
+ '''
+ 上传文件
+ '''
+ url = TEST_URL
+ print("upload test start")
+ url_val = httpUpload(url, 10)
+ print(url_val)
+
+ print("upload test end")
+
+ print("upload php test start")
+ url_val = httpUploadPhp(url, 10)
+ print(url_val)
+ print("upload php test start")
+
+ print("upload php data test start")
+ url_val = httpUploadPhpData(url, 10)
+ print(url_val)
+ print("upload php data test start")
+
+
+def test_start():
+ # test_OK()
+ test_Dir()
+ # test_UA()
+ # test_Header()
+ # test_UA_for(1000)
+ # test_POST()
+ # test_scan()
+ # test_CC()
+ # test_url_ext()
+ # test_cdn()
+ # test_Upload()
+
+
+if __name__ == "__main__":
+ # os.system('cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/op_waf && sh install.sh uninstall 0.2.2 && sh install.sh install 0.2.2')
+ os.system('cd /Users/midoks/Desktop/mwdev/server/mdserver-web/ && python3 /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/op_waf/index.py reload')
+ # os.system('cd /Users/midoks/Desktop/mwdev/server/mdserver-web/ && python3 plugins/openresty/index.py stop && python3 plugins/openresty/index.py start')
+ test_start()
diff --git a/plugins/op_waf/t/ngx_debug.sh b/plugins/op_waf/t/ngx_debug.sh
new file mode 100644
index 000000000..1e50adadb
--- /dev/null
+++ b/plugins/op_waf/t/ngx_debug.sh
@@ -0,0 +1,148 @@
+#!/bin/sh
+export PATH=$PATH:/opt/stap/bin:/opt/stapxx
+
+# cd /www/server/mdserver-web/plugins/op_waf/t && bash ngx_debug.sh lua ok
+# cd /www/server/mdserver-web/plugins/op_waf/t && bash ngx_debug.sh c ok
+
+
+if [ ${_os} == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eq "openSUSE" /etc/*-release; then
+ OSNAME='opensuse'
+ zypper refresh
+ zypper install cron wget curl zip unzip
+elif grep -Eq "FreeBSD" /etc/*-release; then
+ OSNAME='freebsd'
+elif grep -Eqi "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "Fedora" /etc/issue || grep -Eq "Fedora" /etc/*-release; then
+ OSNAME='fedora'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "Rocky" /etc/issue || grep -Eq "Rocky" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eq "AlmaLinux" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eq "Amazon Linux" /etc/*-release; then
+ OSNAME='amazon'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "Debian" /etc/issue || grep -Eq "Debian" /etc/os-release; then
+ OSNAME='debian'
+ apt update -y
+ apt install -y wget curl zip unzip tar cron
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/os-release; then
+ OSNAME='ubuntu'
+ apt update -y
+ apt install -y wget curl zip unzip tar cron
+else
+ OSNAME='unknow'
+fi
+
+
+# https://moonbingbing.gitbooks.io/openresty-best-practices/content/flame_graph/install.html
+# apt install elfutils
+# sudo apt-get install -y systemtap gcc
+# sudo apt-get install linux-headers-generic gcc libcap-dev
+# apt-get install -y libdw-dev
+# apt-get install -y fakeroot build-essential crash kexec-tools makedumpfile kernel-wedge kernel-package
+# apt-get install -y git-core libncurses5 libncurses5-dev libelf-dev asciidoc binutils-dev
+# apt-get build-dep linux
+
+# cat > /etc/apt/sources.list.d/ddebs.list << EOF
+# deb http://ddebs.ubuntu.com/ precise main restricted universe multiverse
+# EOF
+#
+# apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ECDCAD72428D7C01
+# apt-get update
+
+if [ $# -ne 2 ]
+then
+ echo "Usage: ./`basename $0` lua/c NAME"
+ exit
+fi
+
+pids=`ps -ef|grep nginx | grep -v grep | awk '{print $2}'`
+name=$2
+
+
+# /opt/openresty-systemtap-toolkit/ngx-active-reqs -p 496435
+# /opt/openresty-systemtap-toolkit/sample-bt -p 496435 -t 5 -k > a.bt
+# kernel-debuginfo-common kernel-debuginfo
+# apt install -y kernel-debuginfo-common kernel-debuginfo
+# apt install -y kernel-*
+
+if [ "$OSNAME" == "debian" ];then
+ apt install -y systemtap
+ apt-get install -y build-essential
+ apt-get install -y linux-headers-$(uname -r)
+elif [ "$OSNAME" == "centos" ];then
+ yum install -y kernel-devel-$(uname -r)
+fi
+
+# /opt/stapxx/samples/lj-lua-stacks.sxx --arg time=5 --skip-badvars -x 45266 > tmp.bt
+
+
+if [ ! -d /opt/openresty-systemtap-toolkit ];then
+ cd /opt && git clone https://github.com/openresty/openresty-systemtap-toolkit
+fi
+
+if [ ! -d /opt/stapxx ];then
+ cd /opt && git clone https://github.com/openresty/stapxx
+fi
+
+# stap++ -I ./tapset -x 45266 --arg limit=10 samples/ngx-upstream-post-conn.sxx
+# dpkg -i --force-overwrite /var/cache/apt/archives/linux-tools-common_5.4.0-128.144_all.deb
+
+# /opt/openresty-systemtap-toolkit/ngx-active-reqs -p 45266
+
+# git clone git://sourceware.org/git/systemtap.git
+# ./configure --prefix=/opt/stap --disable-docs --disable-publican --disable-refdocs CFLAGS="-g -O2"
+
+if [ ! -d /opt/FlameGraph ];then
+ cd /opt && git clone https://github.com/brendangregg/FlameGraph
+fi
+
+for pid in ${pids[@]}; do
+ echo "strace:$pid"
+ if [ $1 == "lua" ]; then
+ # --without-luajit-gc64 | lua 模式编译时需要使用此参数
+ /opt/openresty-systemtap-toolkit/ngx-sample-lua-bt -p $pid --luajit20 -t 30 >temp.bt
+ /opt/openresty-systemtap-toolkit/fix-lua-bt temp.bt >${name}_${pid}.bt
+ elif [ $1 == "c" ]; then
+ /opt/openresty-systemtap-toolkit/sample-bt -p $pid -t 10 -u > ${name}_${pid}.bt
+ else
+ echo "type is only lua/c"
+ exit
+ fi
+
+ /opt/FlameGraph/stackcollapse-stap.pl ${name}_${pid}.bt >${name}_${pid}.cbt
+ /opt/FlameGraph/flamegraph.pl ${name}_${pid}.cbt >${name}_${pid}.svg
+ rm -f temp.bt ${name}_${pid}.bt ${name}_${pid}.cbt
+ echo "strace:$pid, end!"
+ echo "${name}_${pid}.svg -- make ok"
+done
+
+# if [ $1 == "lua" ]; then
+# # /opt/openresty-systemtap-toolkit/ngx-sample-lua-bt -p 377452 --luajit20 -t 30 >temp.bt
+# /opt/openresty-systemtap-toolkit/ngx-sample-lua-bt -p $pid --luajit20 -t 30 >temp.bt
+# # /opt/openresty-systemtap-toolkit/fix-lua-bt temp.bt >t1.bt
+# /opt/openresty-systemtap-toolkit/fix-lua-bt temp.bt >${name}.bt
+# elif [ $1 == "c" ]; then
+# # /opt/openresty-systemtap-toolkit/sample-bt -p 496435 -t 10 -u > t2.bt
+# /opt/openresty-systemtap-toolkit/sample-bt -p $pid -t 10 -u > ${name}.bt
+# else
+# echo "type is only lua/c"
+# exit
+# fi
+
+
+# # debuginfo-install kernel-3.10.0-1160.80.1.el7.x86_64
+# # /opt/FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded
+# # /opt/FlameGraph/flamegraph.pl perf.folded > perf.svg
+
+# /opt/FlameGraph/stackcollapse-stap.pl ${name}.bt >${name}.cbt
+# /opt/FlameGraph/flamegraph.pl ${name}.cbt >${name}.svg
+# rm -f temp.bt ${name}.bt ${name}.cbt
+
diff --git a/plugins/op_waf/t/ngx_demo.sh b/plugins/op_waf/t/ngx_demo.sh
new file mode 100644
index 000000000..08ea0473b
--- /dev/null
+++ b/plugins/op_waf/t/ngx_demo.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# cd /www/server/mdserver-web/plugins/op_waf/t && sh ngx_demo.sh
+# cd /www/wwwroot/dev156.cachecha.com && sh ngx_demo.sh
+
+
+# only openresty
+# pid=`ps -ef|grep openresty | grep -v grep | awk '{print $2}'`
+# perf record -F 99 -p $pid -g -- sleep 60
+
+
+#全部
+perf record -F 99 -g -a -- sleep 60
+
+
+perf script -i perf.data &> perf.unfold
+/opt/FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded
+/opt/FlameGraph/flamegraph.pl perf.folded > perf.svg
\ No newline at end of file
diff --git a/plugins/op_waf/t/readme.md b/plugins/op_waf/t/readme.md
new file mode 100755
index 000000000..f43425fd0
--- /dev/null
+++ b/plugins/op_waf/t/readme.md
@@ -0,0 +1,29 @@
+# 火焰图安装 [ubuntu 20.04]
+```
+
+sudo apt-get install -y linux-tools-common linux-tools-generic linux-tools-`uname -r`
+apt-get update -y
+sudo apt -y install elfutils
+apt-get install -y systemtap gcc
+sudo apt-get install -y linux-headers-generic gcc libcap-dev
+apt install -y kernel-debuginfo-common kernel-debuginfo
+```
+
+# 测试有效性
+```
+stap -ve 'probe begin { log("hello systemtap!") exit() }'
+
+
+stap -e 'probe kernel.function("sys_open") {log("hello world") exit()}'
+
+
+stap -v -e 'probe vfs.read {printf("read performed\n"); exit()}'
+```
+
+
+# openresty 测试
+```
+
+cd /www/server/mdserver-web/plugins/op_waf/t && sh ngx_debug.sh lua t1
+cd /www/server/mdserver-web/plugins/op_waf/t && sh ngx_debug.sh c t2
+```
diff --git a/plugins/op_waf/t/test.sh b/plugins/op_waf/t/test.sh
new file mode 100755
index 000000000..182eb1876
--- /dev/null
+++ b/plugins/op_waf/t/test.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+# apt -y install apache2-utils
+# yum -y install httpd-tools
+
+# ab -c 3000 -n 10000000 http://www.zzzvps.com/
+# /cc https://www.zzzvps.com/ 120
+# ab -c 10 -n 1000 http://t1.cn/wp-admin/index.php
+# ab -c 1000 -n 1000000 http://dev156.cachecha.com/
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+python3 index.py
diff --git a/plugins/op_waf/tool_task.py b/plugins/op_waf/tool_task.py
new file mode 100644
index 000000000..3387569fc
--- /dev/null
+++ b/plugins/op_waf/tool_task.py
@@ -0,0 +1,167 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import json
+
+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
+from utils.crontab import crontab as MwCrontab
+
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'op_waf'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getTaskConf():
+ conf = getServerDir() + "/task_config.json"
+ return conf
+
+
+def getConfigData():
+ conf = getTaskConf()
+ if os.path.exists(conf):
+ return json.loads(mw.readFile(getTaskConf()))
+ return {
+ "task_id": -1,
+ "period": "day-n",
+ "where1": "7",
+ "hour": "0",
+ "minute": "15",
+ }
+
+
+def createBgTask():
+ removeBgTask()
+ createBgTaskByName(getPluginName())
+
+
+def createBgTaskByName(name):
+ cfg = getConfigData()
+
+ _name = "[勿删]OP防火墙后台任务"
+ res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
+ if res:
+ return True
+
+ if "task_id" in cfg.keys() and cfg["task_id"] > 0:
+ res = mw.M("crontab").field("id, name").where(
+ "id=?", (cfg["task_id"],)).find()
+ if res and res["id"] == cfg["task_id"]:
+ print("计划任务已经存在!")
+ return True
+
+ mw_dir = mw.getPanelDir()
+ cmd = '''
+mw_dir=%s
+rname=%s
+plugin_path=%s
+script_path=%s
+logs_file=$plugin_path/${rname}.log
+''' % (mw_dir, name, getServerDir(), getPluginDir())
+ cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n"
+ cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n"
+
+ cmd += 'echo "cd $mw_dir && source bin/activate && python3 $script_path/tool_task.py run >> $logs_file 2>&1"' + "\n"
+ cmd += 'cd $mw_dir && source bin/activate && python3 $script_path/tool_task.py run >> $logs_file 2>&1' + "\n"
+
+
+ cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
+ cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
+
+ params = {
+ 'name': _name,
+ 'type': cfg['period'],
+ 'week': "",
+ 'where1': cfg['where1'],
+ 'hour': cfg['hour'],
+ 'minute': cfg['minute'],
+ 'save': "",
+ 'backup_to': "",
+ 'stype': "toShell",
+ 'sname': '',
+ 'sbody': cmd,
+ 'url_address': '',
+ }
+
+ task_id = MwCrontab.instance().add(params)
+ if task_id > 0:
+ cfg["task_id"] = task_id
+ mw.writeFile(getTaskConf(), json.dumps(cfg))
+
+
+def removeBgTask():
+ cfg = getConfigData()
+ for x in range(len(cfg)):
+ if "task_id" in cfg.keys() and cfg["task_id"] > 0:
+ res = mw.M("crontab").field("id, name").where(
+ "id=?", (cfg["task_id"],)).find()
+ if res and res["id"] == cfg["task_id"]:
+ data = MwCrontab.instance().delete(cfg["task_id"])
+ if data['status']:
+ cfg["task_id"] = -1
+ mw.writeFile(getTaskConf(), json.dumps(cfg))
+ return True
+ return False
+
+
+def getCpuUsed():
+ path = getServerDir() + "/cpu.info"
+ if mw.isAppleSystem():
+ import psutil
+ used = psutil.cpu_percent(interval=1)
+ mw.writeFile(path, str(int(used)))
+ else:
+ cmd = "top -bn 1 | fgrep 'Cpu(s)' | awk '{print 100 -$8}' | awk -F . '{print $1}'"
+ data = mw.execShell(cmd)
+ mw.writeFile(path, str(int(data[0].strip())))
+
+
+def pSqliteDb(dbname='logs'):
+ db_dir = getServerDir() + '/logs/'
+ conn = mw.M(dbname).dbPos(db_dir, "waf")
+
+ conn.execute("PRAGMA synchronous = 0")
+ conn.execute("PRAGMA cache_size = 8000")
+ conn.execute("PRAGMA page_size = 32768")
+ conn.execute("PRAGMA journal_mode = wal")
+ conn.execute("PRAGMA journal_size_limit = 1073741824")
+ return conn
+
+def run():
+ now_t = int(time.time())
+ logs_conn = pSqliteDb('logs')
+ del_hot_log = "delete from logs where time<{}".format(now_t)
+ print(del_hot_log)
+ r = logs_conn.execute(del_hot_log)
+ return 'ok'
+
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ action = sys.argv[1]
+ if action == "remove":
+ removeBgTask()
+ elif action == "add":
+ createBgTask()
+ elif action == "run":
+ run()
diff --git a/plugins/op_waf/waf/area_limit.json b/plugins/op_waf/waf/area_limit.json
new file mode 100644
index 000000000..0637a088a
--- /dev/null
+++ b/plugins/op_waf/waf/area_limit.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/plugins/op_waf/waf/conf/readme.md b/plugins/op_waf/waf/conf/readme.md
new file mode 100755
index 000000000..2bbbd3c5c
--- /dev/null
+++ b/plugins/op_waf/waf/conf/readme.md
@@ -0,0 +1 @@
+自动生成配置文件
\ No newline at end of file
diff --git a/plugins/op_waf/waf/config.json b/plugins/op_waf/waf/config.json
new file mode 100755
index 000000000..9e74eba4e
--- /dev/null
+++ b/plugins/op_waf/waf/config.json
@@ -0,0 +1,67 @@
+{
+ "reqfile_path": "{$WAF_PATH}/html",
+ "retry": {
+ "retry_time": 180,
+ "is_open_global": 0,
+ "retry": 6,
+ "retry_cycle": 60
+ },
+ "log": true,
+ "scan": {
+ "status": 444,
+ "ps": "过滤常见扫描测试工具的渗透测试",
+ "open": true,
+ "reqfile": ""
+ },
+ "cc": {
+ "status": 444,
+ "ps": "过虑CC攻击",
+ "limit": 120,
+ "endtime": 300,
+ "open": true,
+ "cycle": 60
+ },
+ "safe_verify": {
+ "status": 200,
+ "ps": "强制安全校验",
+ "reqfile": "safe_js.html",
+ "open": false,
+ "mode":"local",
+ "cpu": 50,
+ "auto": true,
+ "time": 86400
+ },
+ "get": {
+ "status": 200,
+ "ps": "过滤uri、uri参数中常见sql注入、xss等攻击",
+ "open": true,
+ "reqfile": "get.html"
+ },
+ "log_save": 30,
+ "user-agent": {
+ "status": 200,
+ "ps": "通常用于过滤浏览器、蜘蛛及一些自动扫描器",
+ "open": true,
+ "reqfile": "user_agent.html"
+ },
+ "other": {
+ "status": 200,
+ "ps": "其它非通用过滤",
+ "reqfile": "other.html"
+ },
+ "cookie": {
+ "status": 200,
+ "ps": "过滤利用Cookie发起的渗透攻击",
+ "open": true,
+ "reqfile": "cookie.html"
+ },
+ "logs_path": "/www/wwwlogs/waf",
+ "post": {
+ "status": 200,
+ "ps": "过滤POST参数中常见sql注入、xss等攻击",
+ "open": true,
+ "reqfile": "post.html"
+ },
+ "area_limit":false,
+ "open": true
+}
\ No newline at end of file
diff --git a/plugins/op_waf/waf/domains.json b/plugins/op_waf/waf/domains.json
new file mode 100644
index 000000000..0637a088a
--- /dev/null
+++ b/plugins/op_waf/waf/domains.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/plugins/op_waf/waf/html/cookie.html b/plugins/op_waf/waf/html/cookie.html
new file mode 100755
index 000000000..b0e79a85e
--- /dev/null
+++ b/plugins/op_waf/waf/html/cookie.html
@@ -0,0 +1,38 @@
+
+
+
+
+OP网站防火墙
+
+
+
+
+
+
OP网站防火墙
+
+
您的请求带有不合法参数,已被网站管理员设置拦截!
+
可能原因:
+
+ 您提交的内容包含危险的攻击请求
+
+
如何解决:
+
+ 检查提交内容;
+ 如网站托管,请联系空间提供商;
+ 普通网站访客,请联系网站管理员;
+
+
+
+
+
+
diff --git a/plugins/op_waf/waf/html/get.html b/plugins/op_waf/waf/html/get.html
new file mode 100755
index 000000000..b0e79a85e
--- /dev/null
+++ b/plugins/op_waf/waf/html/get.html
@@ -0,0 +1,38 @@
+
+
+
+
+OP网站防火墙
+
+
+
+
+
+
OP网站防火墙
+
+
您的请求带有不合法参数,已被网站管理员设置拦截!
+
可能原因:
+
+ 您提交的内容包含危险的攻击请求
+
+
如何解决:
+
+ 检查提交内容;
+ 如网站托管,请联系空间提供商;
+ 普通网站访客,请联系网站管理员;
+
+
+
+
+
+
diff --git a/plugins/op_waf/waf/html/other.html b/plugins/op_waf/waf/html/other.html
new file mode 100755
index 000000000..b0e79a85e
--- /dev/null
+++ b/plugins/op_waf/waf/html/other.html
@@ -0,0 +1,38 @@
+
+
+
+
+OP网站防火墙
+
+
+
+
+
+
OP网站防火墙
+
+
您的请求带有不合法参数,已被网站管理员设置拦截!
+
可能原因:
+
+ 您提交的内容包含危险的攻击请求
+
+
如何解决:
+
+ 检查提交内容;
+ 如网站托管,请联系空间提供商;
+ 普通网站访客,请联系网站管理员;
+
+
+
+
+
+
diff --git a/plugins/op_waf/waf/html/post.html b/plugins/op_waf/waf/html/post.html
new file mode 100755
index 000000000..b0e79a85e
--- /dev/null
+++ b/plugins/op_waf/waf/html/post.html
@@ -0,0 +1,38 @@
+
+
+
+
+OP网站防火墙
+
+
+
+
+
+
OP网站防火墙
+
+
您的请求带有不合法参数,已被网站管理员设置拦截!
+
可能原因:
+
+ 您提交的内容包含危险的攻击请求
+
+
如何解决:
+
+ 检查提交内容;
+ 如网站托管,请联系空间提供商;
+ 普通网站访客,请联系网站管理员;
+
+
+
+
+
+
diff --git a/plugins/op_waf/waf/html/safe_js.html b/plugins/op_waf/waf/html/safe_js.html
new file mode 100755
index 000000000..d65eef4c6
--- /dev/null
+++ b/plugins/op_waf/waf/html/safe_js.html
@@ -0,0 +1,167 @@
+
+
+
+
+OP网站防火墙|安全校验
+
+
+
+
+
+
OP网站防火墙|安全校验
+
+
false
+
+
+
+
+
+
diff --git a/plugins/op_waf/waf/html/user_agent.html b/plugins/op_waf/waf/html/user_agent.html
new file mode 100755
index 000000000..b0e79a85e
--- /dev/null
+++ b/plugins/op_waf/waf/html/user_agent.html
@@ -0,0 +1,38 @@
+
+
+
+
+OP网站防火墙
+
+
+
+
+
+
OP网站防火墙
+
+
您的请求带有不合法参数,已被网站管理员设置拦截!
+
可能原因:
+
+ 您提交的内容包含危险的攻击请求
+
+
如何解决:
+
+ 检查提交内容;
+ 如网站托管,请联系空间提供商;
+ 普通网站访客,请联系网站管理员;
+
+
+
+
+
+
diff --git a/plugins/op_waf/waf/lua/init.lua b/plugins/op_waf/waf/lua/init.lua
new file mode 100644
index 000000000..e3d2d5dd0
--- /dev/null
+++ b/plugins/op_waf/waf/lua/init.lua
@@ -0,0 +1,749 @@
+
+local json = require "cjson"
+local ngx_match = ngx.re.find
+
+local __WAF = require "waf_common"
+
+-- print(json.encode(__C))
+local C = __WAF:getInstance()
+
+local config = require "waf_config"
+local site_config = require "waf_site"
+local config_domains = require "waf_domains"
+
+
+C:setConfData(config, site_config)
+C:setDebug(true)
+
+-- C:D("config:"..C:to_json(config))
+
+local get_html = require "html_get"
+local post_html = require "html_post"
+local other_html = require "html_other"
+local user_agent_html = require "html_user_agent"
+local cc_safe_js_html = require "html_safe_js"
+local cookie_html = require "html_cookie"
+
+local args_rules = require "rule_args"
+local ip_white_rules = require "rule_ip_white"
+local ip_black_rules = require "rule_ip_black"
+local ipv6_black_rules = require "rule_ipv6_black"
+local scan_black_rules = require "rule_scan_black"
+local user_agent_rules = require "rule_user_agent"
+local post_rules = require "rule_post"
+local cookie_rules = require "rule_cookie"
+local url_rules = require "rule_url"
+local url_white_rules = require "rule_url_white"
+
+local waf_area_limit = require "waf_area_limit"
+
+-- local server_name = string.gsub(C:get_sn(config_domains),'_','.')
+local server_name = C:get_sn(config_domains)
+local function initParams()
+ local data = {}
+ data['server_name'] = server_name
+ data['ip'] = C:get_real_ip(server_name)
+ data['ipn'] = C:arrip(data['ip'])
+ data['request_header'] = ngx.req.get_headers()
+ data['uri'] = tostring(ngx.unescape_uri(ngx.var.uri))
+ data['uri_request_args'] = ngx.req.get_uri_args()
+ data['method'] = ngx.req.get_method()
+ data['request_uri'] = tostring(ngx.var.request_uri)
+ data['status_code'] = ngx.status
+ data['user_agent'] = data['request_header']['user-agent']
+ data['cookie'] = ngx.var.http_cookie
+ data['time'] = ngx.time()
+
+ return data
+end
+
+local params = initParams()
+-- C:D(C:to_json(params))
+C:setParams(params)
+
+local cpu_percent = ngx.shared.waf_limit:get("cpu_usage")
+if not cpu_percent then
+ cpu_percent = 0
+end
+
+local function get_return_state(rstate,rmsg)
+ result = {}
+ result['status'] = rstate
+ result['msg'] = rmsg
+ return result
+end
+
+local function get_waf_drop_ip()
+ local data = ngx.shared.waf_drop_ip:get_keys(0)
+ return data
+end
+
+local function return_json(status,msg)
+ ngx.header.content_type = "application/json"
+ result = {}
+ result['status'] = status
+ result['msg'] = msg
+ ngx.say(json.encode(data))
+ ngx.exit(200)
+end
+
+local function is_chekc_table(data,strings)
+ if type(data) ~= 'table' then return 1 end
+ if not data then return 1 end
+ data = chekc_ip_timeout(data)
+ for k,v in pairs(data)
+ do
+ if strings == v['ip'] then
+ return 3
+ end
+ end
+ return 2
+end
+
+local function remove_waf_drop_ip()
+ ngx.header.content_type = "application/json"
+ local ip = params['uri_request_args']['ip']
+
+ if not ip or not C:is_ipaddr(ip) then
+ local data = get_return_state(-1, "格式错误")
+ ngx.say(json.encode(data))
+ ngx.exit(200)
+ return true
+ end
+
+ local sign = "remove_waf_drop_ip"
+ if C:is_working(sign) then
+ local data = get_return_state(-1, "fail")
+ ngx.say(json.encode(data))
+ ngx.exit(200)
+ return true
+ end
+
+ C:lock_working(sign)
+ ngx.shared.waf_drop_ip:delete(ip)
+ C:unlock_working(sign)
+
+ local data = get_return_state(0, "ok")
+ ngx.say(json.encode(data))
+ ngx.exit(200)
+end
+
+local function clean_waf_drop_ip()
+ ngx.header.content_type = "application/json"
+
+ local sign = "clean_waf_drop_ip"
+ if C:is_working(sign) then
+ local data = get_return_state(-1, "fail")
+ ngx.say(json.encode(data))
+ ngx.exit(200)
+ return true
+ end
+
+ C:lock_working(sign)
+ ngx.shared.waf_drop_ip:flush_all()
+ C:unlock_working(sign)
+
+ local data = get_return_state(0, "ok")
+ ngx.say(json.encode(data))
+ ngx.exit(200)
+end
+
+local function min_route()
+ if ngx.var.remote_addr ~= '127.0.0.1' then return false end
+ local uri = params['uri']
+ if uri == '/get_waf_drop_ip' then
+ ngx.header.content_type = "application/json"
+ local data = get_return_state(0, get_waf_drop_ip())
+ ngx.say(json.encode(data))
+ ngx.exit(200)
+ elseif uri == '/remove_waf_drop_ip' then
+ remove_waf_drop_ip()
+ elseif uri == '/clean_waf_drop_ip' then
+ clean_waf_drop_ip()
+ end
+end
+
+local function waf_get_args()
+ if not config['get']['open'] or not C:is_site_config('get') then return false end
+ -- C:D("waf_get_args:"..C:to_json(args_rules)..":"..json.encode(params['uri_request_args']))
+ if C:ngx_match_list(args_rules, params['uri_request_args']) then
+ C:write_log('args','regular')
+ C:return_html(config['get']['status'], get_html)
+ return true
+ end
+ return false
+end
+
+
+local function waf_ip_white()
+ for _,rule in ipairs(ip_white_rules)
+ do
+ if C:compare_ip(rule) then
+ return true
+ end
+ end
+ return false
+end
+
+local function waf_url_white()
+ if C:ngx_match_list(url_white_rules, params['uri']) then
+ return true
+ end
+ return false
+end
+
+local function waf_ip_black()
+ -- ipv4 ip black
+ for _,rule in ipairs(ip_black_rules)
+ do
+ if C:compare_ip(rule) then
+ ngx.exit(config['cc']['status'])
+ return true
+ end
+ end
+
+ -- ipv6 ip black
+ for _,rule in ipairs(ipv6_black_rules)
+ do
+ if rule == params['ip'] then
+ ngx.exit(config['cc']['status'])
+ return true
+ end
+ end
+ return false
+end
+
+
+local function waf_user_agent()
+ -- user_agent 过滤
+ -- if not config['user-agent']['open'] or not C:is_site_config('user-agent') then return false end
+
+ -- C:D("waf_user_agent;user_agent_rules:"..json.encode(user_agent_rules)..",ua:"..tostring(params['request_header']['user-agent']))
+ if C:ngx_match_list(user_agent_rules, params['request_header']['user-agent']) then
+ -- C:D("waf_user_agent........... true")
+ C:write_log('user_agent','regular')
+ C:return_html(config['user-agent']['status'], user_agent_html)
+ return true
+ end
+
+ -- C:D("waf_user_agent........... false")
+ return false
+end
+
+
+local function waf_drop_ip()
+ local ip = params['ip']
+ local count = ngx.shared.waf_drop_ip:get(ip)
+ if not count then return false end
+
+ local retry = config['retry']['retry']
+ -- C:D("waf_drop;count:"..tostring(count)..",retry:"..tostring(retry))
+ -- C:D("waf_drop;count > retry:"..tostring(count > retry))
+ if count > retry then
+ -- C:D("waf_drop_ip........... true")
+ ngx.exit(config['cc']['status'])
+ return true
+ end
+ -- C:D("waf_drop_ip........... false")
+ return false
+end
+
+local function waf_cc()
+ if not config['cc']['open'] then return false end
+ if not C:is_site_config('cc') then return false end
+
+ local ip = params['ip']
+
+ -- 多次cc,才封禁。
+ -- local ip_lock = ngx.shared.waf_drop_ip:get(ip)
+ -- if ip_lock then
+ -- if ip_lock > 0 then
+ -- ngx.exit(config['cc']['status'])
+ -- return true
+ -- end
+ -- end
+
+ local request_uri = params['request_uri']
+
+ local token = ngx.md5(ip .. '_' .. request_uri)
+ local count = ngx.shared.waf_limit:get(token)
+
+ local endtime = config['cc']['endtime']
+ local waf_limit = config['cc']['limit']
+ local cycle = config['cc']['cycle']
+
+ if count then
+ if count > waf_limit then
+
+ local safe_count, _ = ngx.shared.waf_drop_sum:get(ip)
+ if not safe_count then
+ ngx.shared.waf_drop_sum:set(ip, 1, 86400)
+ safe_count = 1
+ else
+ ngx.shared.waf_drop_sum:incr(ip, 1)
+ end
+ local lock_time = (endtime * safe_count)
+ if lock_time > 86400 then lock_time = 86400 end
+
+ ngx.shared.waf_drop_ip:set(ip, 1, lock_time)
+ local reason = cycle..'秒内累计超过'..waf_limit..'次请求,封锁' .. lock_time .. '秒'
+ C:write_log('cc', reason)
+ C:log(params, 'cc',reason)
+ ngx.exit(config['cc']['status'])
+ return true
+ else
+ ngx.shared.waf_limit:incr(token, 1)
+ end
+ else
+ ngx.shared.waf_drop_sum:set(ip, 1, 86400)
+ ngx.shared.waf_limit:set(token, 1, cycle)
+ end
+ return false
+end
+
+-- 是否符合开强制验证条件
+local function is_open_waf_cc_increase()
+
+ if config['safe_verify']['open'] then
+ return true
+ end
+
+ -- C:D("waf config:"..json.encode(config))
+ if config['safe_verify']['auto'] then
+ if cpu_percent >= config['safe_verify']['cpu'] then
+ return true
+ end
+
+ if site_config[server_name] and site_config[server_name]['safe_verify']['open'] then
+ if cpu_percent >= site_config[server_name]['safe_verify']['cpu'] then
+ return true
+ end
+ end
+ end
+
+ return false
+end
+
+
+--强制验证是否使用正常浏览器访问网站
+local function waf_cc_increase()
+ if not is_open_waf_cc_increase() then return false end
+
+ local ip = params['ip']
+ local uri = params['uri']
+ local cache_token = ngx.md5(ip .. '_' .. server_name)
+
+ --判断是否已经通过验证
+ if ngx.shared.waf_limit:get(cache_token) then return false end
+
+ local cache_rand_key = ip..':rand'
+ local cache_rand = ngx.shared.waf_limit:get(cache_rand_key)
+ if not cache_rand then
+ cache_rand = C:get_random(8)
+ ngx.shared.waf_limit:set(cache_rand_key,cache_rand,30)
+ end
+
+ local make_token = "waf_unbind_"..cache_rand.."_"..cache_token
+ local make_uri_str = "?token="..make_token
+ local make_uri = "/"..make_uri_str
+
+ -- C:D("token:"..tostring(params['uri_request_args']['token']))
+ if params['uri_request_args']['token'] then
+ ngx.header.content_type = "application/json"
+ local args_token = params['uri_request_args']['token']
+ if args_token == make_token then
+ ngx.shared.waf_limit:set(cache_token, 1, tonumber(config['safe_verify']['time']))
+ local data = get_return_state(0, "ok")
+ ngx.say(json.encode(data))
+ ngx.exit(200)
+ end
+ local data = get_return_state(0, "unset")
+ ngx.say(json.encode(data))
+ ngx.exit(200)
+ end
+
+ if not config['safe_verify']['mode'] then return false end
+
+ -- C:D(C:to_json(params['uri_request_args']))
+ if config['safe_verify']['mode'] == 'url' then
+ local page = 'safe_verify_'..cache_token
+ local request_uri = params['request_uri']
+ local to_url = '/?'..page..'&f='..request_uri
+
+ local cache_url_key = cache_token..':url'
+ local cache_url_val = ngx.shared.waf_limit:get(cache_url_key)
+
+ if params['uri_request_args'][page] then
+ local cc_html = ngx.re.gsub(cc_safe_js_html, "{uri}", make_uri_str)
+ return C:return_html(200, cc_html)
+ end
+
+ if not cache_url_val then
+ ngx.shared.waf_limit:set(cache_url_key, request_uri,30)
+ return ngx.redirect(to_url)
+ end
+ end
+
+ if config['safe_verify']['mode'] == 'local' then
+ local cc_html = ngx.re.gsub(cc_safe_js_html, "{uri}", make_uri_str)
+ C:return_html(200, cc_html)
+ end
+end
+
+
+local function waf_url()
+ if not config['get']['open'] or not C:is_site_config('get') then return false end
+ --正则--
+ -- C:D("waf_url:"..json.encode(url_rules)..":uri:"..params["uri"])
+ if C:ngx_match_list(url_rules, params["uri"]) then
+ C:write_log('url','regular')
+ C:return_html(config['get']['status'], get_html)
+ return true
+ end
+ return false
+end
+
+
+local function waf_scan_black()
+ -- 扫描软件禁止
+ if not config['scan']['open'] or not C:is_site_config('scan') then return false end
+ if not params["cookie"] then
+ if C:ngx_match_string(scan_black_rules['cookie'], tostring(params["cookie"]),'scan') then
+ C:write_log('scan','regular')
+ ngx.exit(config['scan']['status'])
+ return true
+ end
+ end
+
+ if C:ngx_match_string(scan_black_rules['args'], params["request_uri"], 'scan') then
+ C:write_log('scan','regular')
+ ngx.exit(config['scan']['status'])
+ return true
+ end
+
+ for key,value in pairs(params["request_header"])
+ do
+ if C:ngx_match_string(scan_black_rules['header'], key, 'scan') then
+ C:write_log('scan','regular')
+ ngx.exit(config['scan']['status'])
+ return true
+ end
+ end
+ return false
+end
+
+local function waf_post()
+ if not config['post']['open'] or not C:is_site_config('post') then return false end
+ if params['method'] ~= "POST" then return false end
+ local content_length = tonumber(params["request_header"]['content-length'])
+ local max_len = 640 * 1020000
+ if content_length > max_len then return false end
+ if C:get_boundary() then return false end
+ ngx.req.read_body()
+
+ local request_args = params['uri_request_args']
+ if not request_args then return false end
+
+ for key, val in pairs(request_args) do
+ if type(val) == "table" then
+ if type(val[1]) == "boolean" then
+ return false
+ end
+ data = table.concat(val, ", ")
+ else
+ data = val
+ end
+ end
+
+ -- C:D("post:"..json.encode(data))
+ if C:ngx_match_list(post_rules, data) then
+ C:write_log('post','regular')
+ C:return_html(config['post']['status'], post_html)
+ return true
+ end
+ return false
+end
+
+local function X_Forwarded()
+
+ if params['method'] ~= "GET" then return false end
+ if not config['get']['open'] or not C:is_site_config('get') then return false end
+ if not params["request_header"]['X-forwarded-For'] then return false end
+
+ if C:ngx_match_list(args_rules, params["request_header"]['X-forwarded-For']) then
+ C:write_log('args','regular')
+ C:return_html(config['get']['status'], get_html)
+ return true
+ end
+ return false
+end
+
+
+local function post_X_Forwarded()
+ if not config['post']['open'] or not C:is_site_config('post') then return false end
+ if params['method'] ~= "POST" then return false end
+ if not params["request_header"]['X-forwarded-For'] then return false end
+ if C:ngx_match_list(post_rules, params["request_header"]['X-forwarded-For']) then
+ C:write_log('post','regular')
+ C:return_html(config['post']['status'], post_html)
+ return true
+ end
+ return false
+end
+
+local function url_ext()
+ if site_config[server_name] == nil then return false end
+ for _,rule in ipairs(site_config[server_name]['disable_ext'])
+ do
+ if C:ngx_match_string("\\."..rule.."$", params['uri'],'url_ext') then
+ if rule == "php" then
+ C:write_log('php_path','regular')
+ else
+ C:write_log('path','regular')
+ end
+ C:return_html(config['other']['status'], other_html)
+ return true
+ end
+ end
+ return false
+end
+
+local function disable_upload_ext(ext)
+ if not ext then return false end
+ local ext = string.lower(ext)
+ if C:is_key(site_config[server_name]['disable_upload_ext'], ext) then
+ C:write_log('upload_ext', '上传扩展名黑名单')
+ C:return_html(config['other']['status'],other_html)
+ return true
+ end
+ return false
+end
+
+local function post_data()
+ if params["method"] ~= "POST" then return false end
+ -- C:D("content-length:"..params["request_header"]['content-length'])
+ local content_length = tonumber(params["request_header"]['content-length'])
+ if not content_length then return false end
+ local max_len = 2560 * 1024000
+ if content_length > max_len then return false end
+ local boundary = C:get_boundary()
+ -- C:D("boundary:".. tostring( boundary) )
+ if boundary then
+ ngx.req.read_body()
+ local data = ngx.req.get_body_data()
+ if not data then return false end
+ local tmp = ngx.re.match(data,[[filename=\"(.+)\.(.*)\"]])
+ if not tmp or not tmp[2] then return false end
+ -- C:D("upload_ext:".. tostring(tmp[2]) )
+ disable_upload_ext(tmp[2])
+ end
+ return false
+end
+
+local function waf_cookie()
+ if not config['cookie']['open'] or not C:is_site_config('cookie') then return false end
+ if not params["request_header"]['cookie'] then return false end
+ if type(params["request_header"]['cookie']) ~= "string" then return false end
+ request_cookie = string.lower(params["request_header"]['cookie'])
+ if C:ngx_match_list(cookie_rules,request_cookie,'cookie') then
+ C:write_log('cookie','regular')
+ C:return_html(config['cookie']['status'],cookie_html)
+ return true
+ end
+ return false
+end
+
+local geo=nil
+local waf_country=""
+
+local function initmaxminddb()
+ if geo==nil then
+ maxminddb ,geo = pcall(function() return require 'waf_maxminddb' end)
+ if not maxminddb then
+ C:D("debug waf error on :"..tostring(geo))
+ return nil
+ end
+ end
+ if type(geo)=='number' then return nil end
+ local ok2,data=pcall(function()
+ if not geo.initted() then
+ geo.init("{$WAF_ROOT}/GeoLite2-City.mmdb")
+ end
+ end )
+ if not ok2 then
+ geo=nil
+ end
+end
+
+
+local function get_ip_country(ip)
+ initmaxminddb()
+ if type(geo)=='number' then return "2" end
+ if geo==nil then return "2" end
+ if geo.lookup==nil then return "2" end
+ local res,err=geo.lookup(ip or ngx.var.remote_addr)
+ if not res then
+ return "2"
+ else
+ -- C:D("res:"..tostring(res))
+ return res
+ end
+end
+
+local function get_country()
+ local ip = params['ip']
+ local ip_local = ngx.shared.waf_limit:get("get_country"..ip)
+ if ip_local then
+ return ip_local
+ end
+ local ip_postion=get_ip_country(ip)
+ if ip_postion=="2" then return false end
+ if ip_postion["country"]==nil then return false end
+ if ip_postion["country"]["names"]==nil then return false end
+ if ip_postion["country"]["names"]["zh-CN"]==nil then return false end
+ ngx.shared.waf_limit:set("get_country"..ip,ip_postion["country"]["names"]["zh-CN"],3600)
+ return ip_postion["country"]["names"]["zh-CN"]
+end
+
+local function area_limit(overall_country, server_name, status)
+
+ if not status then
+ return false
+ end
+
+ if overall_country and overall_country~="" and C:count_size(waf_area_limit)>=1 then
+ for k, val in pairs(waf_area_limit) do
+ -- C:D(tostring(k)..':'..tostring(val['site']['allsite']) ..':'.. tostring(val['site']['allsite'] == '1') ..':'.. tostring(val['site']['allsite']))
+ if val['site']['allsite'] and val['site']['allsite'] == '1' and val['types'] == 'refuse' then
+ for rk, reg_val in pairs(val['region']) do
+ if rk == overall_country then
+ ngx.exit(403)
+ return true
+ end
+ end
+ end
+
+ if val['site'][server_name] and val['site'][server_name] == '1' and val['types'] == 'refuse' then
+ for rk, reg_val in pairs(val['region']) do
+ if rk == overall_country then
+ ngx.exit(403)
+ return true
+ end
+ end
+ end
+
+ if val['site']['allsite'] and val['site']['allsite'] == '1' and val['types'] == 'accept' then
+ for rk, reg_val in pairs(val['region']) do
+ if rk == overall_country then
+ return false
+ end
+ end
+ ngx.exit(403)
+ return true
+ end
+
+ if val['site'][server_name] and val['site'][server_name] == '1' and val['types'] == 'accept' then
+ for rk, reg_val in pairs(val['region']) do
+ if rk == overall_country then
+ return false
+ end
+ end
+ ngx.exit(403)
+ return true
+ end
+ end
+ end
+ return false
+end
+
+function run_app_waf()
+ min_route()
+ -- C:D("min_route")
+
+ if site_config[server_name] and site_config[server_name]['open'] then
+
+
+ -- white ip
+ if waf_ip_white() then return true end
+ -- C:D("waf_ip_white")
+
+
+ -- url white
+ if waf_url_white() then return true end
+ -- C:D("waf_url_white")
+
+ -- black ip
+ if waf_ip_black() then return true end
+ -- C:D("waf_ip_black")
+
+ -- 封禁ip返回
+ if waf_drop_ip() then return true end
+ -- C:D("waf_drop_ip")
+
+ -- country limit
+ if config['area_limit'] then
+ local waf_country = get_country()
+ if waf_country then
+ if area_limit(waf_country, server_name, site_config[server_name]['open']) then return true end
+ end
+ end
+
+ -- ua check
+ if waf_user_agent() then return true end
+ -- C:D("waf_user_agent")
+ if waf_url() then return true end
+ -- C:D("waf_url")
+
+ -- cc setting
+ if waf_cc_increase() then return true end
+ -- C:D("waf_cc_increase")
+ if waf_cc() then return true end
+ -- C:D("waf_cc")
+
+ -- cookie检查
+ if waf_cookie() then return true end
+ -- C:D("waf_cookie")
+
+ -- args参数拦截
+ if waf_get_args() then return true end
+ -- C:D("waf_get_args")
+
+ -- 扫描软件禁止
+ if waf_scan_black() then return true end
+ -- C:D("waf_scan_black")
+ if waf_post() then return true end
+ -- C:D("waf_post")
+ --------------------------------------------------------
+ --------------------------------------------------------
+ if X_Forwarded() then return true end
+ -- C:D("X_Forwarded")
+ if post_X_Forwarded() then return true end
+ -- C:D("post_X_Forwarded")
+ if url_ext() then return true end
+ -- C:D("url_ext")
+ if post_data() then return true end
+ -- C:D("post_data")
+ end
+end
+
+
+local waf_run_status = nil
+function waf()
+ if waf_run_status then
+ run_app_waf()
+ else
+ local ok,waf_err=pcall(function()
+ run_app_waf()
+ end)
+
+ if waf_err ~= nil then
+ C:D("----waf error-----"..tostring(waf_err))
+ end
+
+ if ok then
+ waf_run_status = true
+ end
+ end
+end
+
+waf()
\ No newline at end of file
diff --git a/plugins/op_waf/waf/lua/init_preload.lua b/plugins/op_waf/waf/lua/init_preload.lua
new file mode 100644
index 000000000..8087b0065
--- /dev/null
+++ b/plugins/op_waf/waf/lua/init_preload.lua
@@ -0,0 +1,11 @@
+local waf_root = "{$WAF_ROOT}"
+local waf_cpath = waf_root.."/waf/lua/?.lua;"..waf_root.."/waf/conf/?.lua;"..waf_root.."/waf/html/?.lua;"
+local waf_sopath = waf_root.."/waf/conf/?.so;"
+
+if not package.path:find(waf_cpath) then
+ package.path = waf_cpath .. package.path
+end
+
+if not package.cpath:find(waf_sopath) then
+ package.cpath = waf_sopath .. package.cpath
+end
\ No newline at end of file
diff --git a/plugins/op_waf/waf/lua/init_worker.lua b/plugins/op_waf/waf/lua/init_worker.lua
new file mode 100644
index 000000000..73ebb4125
--- /dev/null
+++ b/plugins/op_waf/waf/lua/init_worker.lua
@@ -0,0 +1,58 @@
+local waf_root = "{$WAF_ROOT}"
+
+-- local waf_cpath = waf_root.."/waf/lua/?.lua;"..waf_root.."/waf/conf/?.lua;"..waf_root.."/waf/html/?.lua;"
+-- local waf_sopath = waf_root.."/waf/conf/?.so;"
+-- if not package.path:find(waf_cpath) then
+-- package.path = waf_cpath .. package.path
+-- end
+
+-- if not package.cpath:find(waf_sopath) then
+-- package.cpath = waf_sopath .. package.cpath
+-- end
+
+local json = require "cjson"
+
+local __WAF_C = require "waf_common"
+local WAF_C = __WAF_C:getInstance()
+
+local waf_config = require "waf_config"
+local waf_site_config = require "waf_site"
+WAF_C:setConfData(waf_config, waf_site_config)
+WAF_C:setDebug(true)
+
+-- C:D("init worker"..tostring(ngx.worker.id()))
+
+local function waf_timer_stats_total_log(premature)
+ WAF_C:timer_stats_total()
+end
+
+local function waf_clean_expire_data(premature)
+ WAF_C:clean_log()
+end
+
+ngx.shared.waf_limit:set("cpu_usage", 0, 10)
+function waf_timer_every_get_cpu(premature)
+ if WAF_C:file_exists('/proc/stat') then
+ local lua_cpu_percent = WAF_C:get_cpu_percent()
+ -- WAF_C:D("lua_cpu_percent:"..tostring(lua_cpu_percent))
+ ngx.shared.waf_limit:set("cpu_usage", math.floor(lua_cpu_percent), 10)
+ else
+ local cpu_percent = WAF_C:read_file_body(waf_root.."/cpu.info")
+ -- WAF_C:D("cpu_usage:"..tostring(cpu_percent ))
+ if cpu_percent then
+ ngx.shared.waf_limit:set("cpu_usage", tonumber(cpu_percent), 10)
+ else
+ ngx.shared.waf_limit:set("cpu_usage", 0, 10)
+ end
+ end
+end
+
+if ngx.worker.id() == 0 then
+
+ ngx.timer.every(6, waf_timer_every_get_cpu)
+ -- 异步执行
+ ngx.timer.every(3, waf_timer_stats_total_log)
+ ngx.timer.every(10, waf_clean_expire_data)
+
+ WAF_C:cron()
+end
\ No newline at end of file
diff --git a/plugins/op_waf/waf/lua/waf_common.lua b/plugins/op_waf/waf/lua/waf_common.lua
new file mode 100644
index 000000000..2e2b6adca
--- /dev/null
+++ b/plugins/op_waf/waf/lua/waf_common.lua
@@ -0,0 +1,836 @@
+local waf_root = "{$WAF_ROOT}"
+local waf_cpath = waf_root.."/waf/lua/?.lua;"..waf_root.."/waf/conf/?.lua;"..waf_root.."/waf/html/?.lua;"
+local waf_sopath = waf_root.."/waf/conf/?.so;"
+
+if not package.path:find(waf_cpath) then
+ package.path = waf_cpath .. package.path
+end
+
+if not package.cpath:find(waf_sopath) then
+ package.cpath = waf_sopath .. package.cpath
+end
+
+local setmetatable = setmetatable
+local _M = { _VERSION = '0.02' }
+local mt = { __index = _M }
+
+local json = require "cjson"
+local sqlite3 = require "lsqlite3"
+
+local ngx_re = require "ngx.re"
+local ngx_match = ngx.re.find
+
+local debug_mode = false
+
+local cpath = waf_root.."/waf/"
+local log_dir = waf_root.."/logs/"
+local rpath = cpath.."/rule/"
+
+function _M.new(self)
+
+ local self = {
+ waf_root = waf_root,
+ cpath = cpath,
+ rpath = rpath,
+ logdir = log_dir,
+ config = '',
+ site_config = '',
+ server_name = '',
+ global_tatal = nil,
+ params = nil,
+ }
+ return setmetatable(self, mt)
+end
+
+
+-- function _M.getInstance(self)
+-- if rawget(self, "instance") == nil then
+-- rawset(self, "instance", self.new())
+-- end
+-- assert(self.instance ~= nil)
+-- return self.instance
+-- end
+
+function _M.getInstance(self)
+ if self.instance == nil then
+ self.instance = self:new()
+ end
+ assert(self.instance ~= nil)
+ return self.instance
+end
+
+-- 后台任务
+function _M.cron(self)
+
+ local timer_every_import_data = function(premature)
+
+ local llen, _ = ngx.shared.waf_limit:llen('waf_limit_logs')
+ if llen == 0 then
+ return true
+ end
+
+ local db = self:initDB()
+
+ db:exec([[BEGIN TRANSACTION]])
+
+ local stmt2 = db:prepare[[INSERT INTO logs(time, ip, domain, server_name, method, status_code, uri, user_agent, rule_name, reason)
+ VALUES(:time, :ip, :domain, :server_name, :method, :status_code, :uri, :user_agent, :rule_name, :reason)]]
+
+ if not stmt2 then
+ self:D("waf timer db:prepare fail!:"..tostring(stmt2))
+ return false
+ end
+
+ for i=1,llen do
+ local data, _ = ngx.shared.waf_limit:lpop('waf_limit_logs')
+ -- self:D("waf_limit_logs:"..data)
+ if not data then
+ break
+ end
+
+ local info = json.decode(data)
+
+ stmt2:bind_names{
+ time=info["time"],
+ ip=info["ip"],
+ domain=info["server_name"],
+ server_name=info["server_name"],
+ method=info["method"],
+ status_code=info["status_code"],
+ user_agent=info["user_agent"],
+ uri=info["request_uri"],
+ rule_name=info['rule_name'],
+ reason=info['reason']
+ }
+
+ local res, err = stmt2:step()
+ if tostring(res) == "5" then
+ self:D("waf the step database connection is busy, so it will be stored later.")
+ return false
+ end
+ stmt2:reset()
+ end
+
+ local res, err = db:execute([[COMMIT]])
+ if db and db:isopen() then
+ db:close()
+ end
+
+ end
+ ngx.timer.every(0.5, timer_every_import_data)
+end
+
+function _M.initDB(self)
+ local path = log_dir .. "/waf.db"
+ local db, err = sqlite3.open(path)
+
+ if err then
+ self:D("initDB err:"..tostring(err))
+ return nil
+ end
+
+ db:exec([[PRAGMA synchronous = 0]])
+ db:exec([[PRAGMA cache_size = 8000]])
+ db:exec([[PRAGMA page_size = 32768]])
+ db:exec([[PRAGMA journal_mode = wal]])
+ db:exec([[PRAGMA journal_size_limit = 1073741824]])
+ return db
+end
+
+function _M.clean_log(self)
+ local db = self:initDB()
+ local now_date = os.date("*t")
+ local save_day = 90
+ local save_date_timestamp = os.time{year=now_date.year,
+ month=now_date.month, day=now_date.day-save_day, hour=0}
+ -- delete expire data
+ db:exec("DELETE FROM web_logs WHERE time<"..tostring(save_date_timestamp))
+end
+
+function _M.log(self, args, rule_name, reason)
+
+ args["rule_name"] = rule_name
+ args["reason"] = reason
+
+ local push_data = json.encode(args)
+
+ ngx.shared.waf_limit:rpush("waf_limit_logs", push_data)
+ -- self:D("push_data:"..push_data)
+
+ -- local db = self:initDB()
+
+ -- local stmt2 = db:prepare[[INSERT INTO logs(time, ip, domain, server_name, method, status_code, uri, user_agent, rule_name, reason)
+ -- VALUES(:time, :ip, :domain, :server_name, :method, :status_code, :uri, :user_agent, :rule_name, :reason)]]
+
+ -- db:exec([[BEGIN TRANSACTION]])
+
+ -- stmt2:bind_names{
+ -- time=args["time"],
+ -- ip=args["ip"],
+ -- domain=args["server_name"],
+ -- server_name=args["server_name"],
+ -- method=args["method"],
+ -- status_code=args["status_code"],
+ -- user_agent=args["user_agent"],
+ -- uri=args["request_uri"],
+ -- rule_name=rule_name,
+ -- reason=reason
+ -- }
+
+ -- local res, err = stmt2:step()
+ -- -- self:D("LOG[1]:"..tostring(res)..":"..tostring(err))
+
+ -- if tostring(res) == "5" then
+ -- self.D("waf the step database connection is busy, so it will be stored later.")
+ -- return false
+ -- end
+ -- stmt2:reset()
+
+ -- local res, err = db:execute([[COMMIT]])
+ -- -- self:D("LOG[2]:"..tostring(res)..":"..tostring(err))
+ -- if db and db:isopen() then
+ -- db:close()
+ -- end
+ -- return true
+end
+
+
+function _M.setDebug(self, mode)
+ debug_mode = mode
+end
+
+
+-- 调试方式
+function _M.D(self, msg)
+ if not debug_mode then return true end
+
+ local _msg = ''
+ if type(msg) == 'table' then
+ _msg = _msg.."args->\n"
+ for key, val in pairs(msg) do
+ _msg = _msg..tostring(key)..':'..tostring(val).."\n"
+ end
+ elseif type(msg) == 'string' then
+ _msg = msg
+ elseif type(msg) == 'nil' then
+ _msg = 'nil'
+ else
+ _msg = msg
+ end
+
+ local fp = io.open(waf_root.."/debug.log", "ab")
+ if fp == nil then
+ return nil
+ end
+
+ -- local localtime = os.date("%Y-%m-%d %H:%M:%S")
+ local localtime = ngx.localtime()
+ if server_name then
+ fp:write(tostring(_msg) .. "\n")
+ else
+ fp:write(localtime..":"..tostring(_msg) .. "\n")
+ end
+
+ fp:flush()
+ fp:close()
+ return true
+end
+
+function _M.is_working(self,sign)
+ local work_status = ngx.shared.waf_limit:get(sign.."_working")
+ if work_status ~= nil and work_status == true then
+ return true
+ end
+ return false
+end
+
+function _M.lock_working(self, sign)
+ local working_key = sign.."_working"
+ ngx.shared.waf_limit:set(working_key, true, 60)
+end
+
+function _M.unlock_working(self, sign)
+ local working_key = sign.."_working"
+ ngx.shared.waf_limit:set(working_key, false)
+end
+
+
+local function write_file_clear(filename, body)
+ fp = io.open(filename,'w')
+ if fp == nil then
+ return nil
+ end
+ fp:write(body)
+ fp:flush()
+ fp:close()
+ return true
+end
+
+function _M.setConfData( self, config, site_config )
+ self.config = config
+ self.site_config = site_config
+
+end
+
+
+function _M.setParams( self, params )
+ self.params = params
+end
+
+function _M.count_size(data)
+ local count=0
+ if type(data)~="table" then return count end
+ for k,v in pairs(data)
+ do
+ count=count+1
+ end
+ return count
+end
+
+
+function _M.is_min(self, ip1, ip2)
+ n = 0
+ for _,v in ipairs({1,2,3,4})
+ do
+ if ip1[v] == ip2[v] then
+ n = n + 1
+ elseif ip1[v] > ip2[v] then
+ break
+ else
+ return false
+ end
+ end
+ return true
+end
+
+function _M.is_max(self,ip1,ip2)
+ n = 0
+ for _,v in ipairs({1,2,3,4})
+ do
+ if ip1[v] == ip2[v] then
+ n = n + 1
+ elseif ip1[v] < ip2[v] then
+ break
+ else
+ return false
+ end
+ end
+ return true
+end
+
+function _M.split(self, str,reps )
+ local rsList = {}
+ string.gsub(str,'[^'..reps..']+',function(w)
+ table.insert(rsList,w)
+ end)
+ return rsList
+end
+
+function _M.arrip(self, ipstr)
+ if ipstr == 'unknown' then return {0,0,0,0} end
+ if string.find(ipstr,':') then return ipstr end
+ iparr = self:split(ipstr,'.')
+ iparr[1] = tonumber(iparr[1])
+ iparr[2] = tonumber(iparr[2])
+ iparr[3] = tonumber(iparr[3])
+ iparr[4] = tonumber(iparr[4])
+ return iparr
+end
+
+
+function _M.compare_ip(self,ips)
+ local ip = self.params["ip"]
+ local ipn = self.params["ipn"]
+ if ip == 'unknown' then return true end
+ if string.find(ip,':') then return false end
+ if not self:is_max(ipn,ips[2]) then return false end
+ if not self:is_min(ipn,ips[1]) then return false end
+ return true
+end
+
+
+
+function _M.to_json(self, msg)
+ return json.encode(msg)
+end
+
+function _M.return_state(status,msg)
+ result = {}
+ result['status'] = status
+ result['msg'] = msg
+ return result
+end
+
+function _M.return_message(self, status, msg)
+ ngx.header.content_type = "application/json"
+ local data = self:return_state(status, msg)
+ ngx.say(json.encode(data))
+ ngx.exit(200)
+end
+
+function _M.return_html(self, status, html)
+ ngx.header.content_type = "text/html"
+ ngx.header.Cache_Control = "no-cache"
+ status = tonumber(status)
+
+ -- self:D("return_html:"..tostring(status))
+ if status == 200 then
+ ngx.say(html)
+ end
+ ngx.exit(status)
+end
+
+function _M.read_file_body(self, filename)
+ local fp = io.open(filename, 'r')
+ if fp == nil then
+ return nil
+ end
+ local fbody = fp:read("*a")
+ fp:close()
+ if fbody == '' then
+ return nil
+ end
+ return fbody
+end
+
+function _M.read_file(self, name)
+ f = self.rpath .. name .. '.json'
+ local fbody = self:read_file_body(f)
+ if fbody == nil then
+ return {}
+ end
+
+ local data = json.decode(fbody)
+ return data
+end
+
+function _M.file_exists(self,path)
+ local file = io.open(path, "rb")
+ if file then file:close() end
+ return file ~= nil
+end
+
+
+function _M.select_rule(self, rules)
+ if not rules then return {} end
+ new_rules = {}
+ for i,v in ipairs(rules)
+ do
+ if v[1] == 1 then
+ table.insert(new_rules,v[2])
+ end
+ end
+ return new_rules
+end
+
+function _M.read_file_table( self, name )
+ return self:select_rule(self:read_file(name))
+end
+
+
+function _M.read_file_body_decode(self, name)
+ return json.decode(self:read_file_body(name))
+end
+
+function _M.write_file(self, filename, body)
+ fp = io.open(filename,'ab')
+ if fp == nil then
+ return nil
+ end
+ fp:write(body)
+ fp:flush()
+ fp:close()
+ return true
+end
+
+function _M.write_file_clear(self, filename, body)
+ return write_file_clear(filename, body)
+end
+
+function _M.write_to_file(self, logstr)
+ local server_name = self.params['server_name']
+ local filename = self.logdir .. '/' .. server_name .. '_' .. ngx.today() .. '.log'
+ self:write_file(filename, logstr)
+ return true
+end
+
+-- 是否文件迁入数据库中
+function _M.is_migrating(self)
+ local migrating = self.waf_root +"/migrating"
+ local file = io.open(migrating, "rb")
+ if file then return true end
+ return false
+end
+
+
+function _M.continue_key(self,key)
+ key = tostring(key)
+ if string.len(key) > 64 then return false end;
+ local keys = { "content", "contents", "body", "msg", "file", "files", "img", "newcontent" }
+ for _,k in ipairs(keys)
+ do
+ if k == key then return false end;
+ end
+ return true;
+end
+
+
+function _M.array_len(self, arr)
+ if not arr then return 0 end
+ local count = 0
+ for _,v in ipairs(arr)
+ do
+ count = count + 1
+ end
+ return count
+end
+
+function _M.is_ipaddr(self, client_ip)
+ local cipn = self:split(client_ip,'.')
+ if self:array_len(cipn) < 4 then return false end
+ for _,v in ipairs({1,2,3,4})
+ do
+ local ipv = tonumber(cipn[v])
+ if ipv == nil then return false end
+ if ipv > 255 or ipv < 0 then return false end
+ end
+ return true
+end
+
+-- 定时异步同步统计信息
+function _M.timer_stats_total(self)
+ local total_path = self.cpath .. 'total.json'
+ local total = ngx.shared.waf_limit:get(total_path)
+ if not total then
+ return false
+ end
+ return self:write_file_clear(total_path,total)
+end
+
+function _M.stats_total(self, name, rule)
+ local server_name = self.params['server_name']
+ local total_path = cpath .. 'total.json'
+ local total = ngx.shared.waf_limit:get(total_path)
+
+ if not total then
+ local tbody = self:read_file_body(total_path)
+ total = json.decode(tbody)
+ else
+ total = json.decode(total)
+ end
+
+ if not total then return false end
+
+ -- 开始计算
+ if not total['sites'] then total['sites'] = {} end
+ if not total['sites'][server_name] then total['sites'][server_name] = {} end
+ if not total['sites'][server_name][name] then total['sites'][server_name][name] = 0 end
+ if not total['rules'] then total['rules'] = {} end
+ if not total['rules'][name] then total['rules'][name] = 0 end
+ if not total['total'] then total['total'] = 0 end
+ total['total'] = total['total'] + 1
+ total['sites'][server_name][name] = total['sites'][server_name][name] + 1
+ total['rules'][name] = total['rules'][name] + 1
+
+ ngx.shared.waf_limit:set(total_path,json.encode(total))
+
+ -- 异步执行
+ -- 现在改再init_workder.lua 定时执行
+ -- ngx.timer.every(3, timer_stats_total_log)
+end
+
+
+-- 获取配置域名
+function _M.get_sn(self, config_domains)
+ local request_name = ngx.var.server_name
+ local cache_name = ngx.shared.waf_limit:get(request_name)
+ if cache_name then return cache_name end
+
+ for _,v in ipairs(config_domains)
+ do
+ for _,cd_name in ipairs(v['domains'])
+ do
+ if request_name == cd_name then
+ ngx.shared.waf_limit:set(request_name,v['name'],86400)
+ return v['name']
+ end
+ end
+ end
+ return "unset"
+end
+
+function _M.get_random(self,n)
+ math.randomseed(ngx.time())
+ local t = {
+ "0","1","2","3","4","5","6","7","8","9",
+ "a","b","c","d","e","f","g","h","i","j",
+ "k","l","m","n","o","p","q","r","s","t",
+ "u","v","w","x","y","z",
+ "A","B","C","D","E","F","G","H","I","J",
+ "K","L","M","N","O","P","Q","R","S","T",
+ "U","V","W","X","Y","Z",
+ }
+ local s = ""
+ for i =1, n do
+ s = s .. t[math.random(#t)]
+ end
+ return s
+end
+
+
+
+function _M.is_ngx_match_orgin(self,rule, match, sign)
+ if ngx_match(ngx.unescape_uri(match), rule, "isjo") then
+ error_rule = rule .. ' >> ' .. sign .. ':' .. match
+ return true
+ end
+ return false
+end
+
+
+function _M.ngx_match_string(self, rule, content,sign)
+ local t = self:is_ngx_match_orgin(rule, content, sign)
+ if t then
+ return true
+ end
+
+ return false
+end
+
+function _M.ngx_match_list(self, rules, content)
+ local args_type = type(content)
+ for i,rule in ipairs(rules)
+ do
+ if rule[1] == 1 then
+ if args_type == 'string' then
+ -- self:D("string: "..tostring(rule[2])..":".. tostring(content)..":"..tostring(rule[3]))
+ local t = self:is_ngx_match_orgin(rule[2], content, rule[3])
+ if t then
+ return true
+ end
+ end
+
+ if args_type == 'table' then
+ for _,arg_v in pairs(content) do
+ -- self:D("table : "..tostring(rule[2])..":".. tostring(arg_v)..":"..tostring(rule[3]))
+ local t = self:is_ngx_match_orgin(rule[2], arg_v, rule[3])
+ if t then
+ return true
+ end
+ end
+ end
+
+ end
+ end
+ return false
+end
+
+function _M.is_ngx_match_ua(self, rules, content)
+ -- ngx.header.content_type = "text/html"
+ for i,rule in ipairs(rules)
+ do
+ -- 开启的规则,才匹配。
+ if rule[1] == 1 then
+ local t = self:is_ngx_match_orgin(rule[2], content, rule[3])
+ if t then
+ return true
+ end
+ end
+ end
+ return false
+end
+
+function _M.is_ngx_match_post(self, rules, content)
+ for i,rule in ipairs(rules)
+ do
+ -- 开启的规则,才匹配。
+ if rule[1] == 1 then
+ local t = self:is_ngx_match_orgin(rule[2],content, rule[3])
+ if t then
+ return true
+ end
+ end
+ end
+ return false
+end
+
+
+function _M.write_log(self, name, rule)
+ local config = self.config
+ local params = self.params
+
+ local ip = params['ip']
+ local ngx_time = ngx.time()
+
+ local retry = config['retry']['retry']
+ local retry_time = config['retry']['retry_time']
+ local retry_cycle = config['retry']['retry_cycle']
+
+ local count = ngx.shared.waf_drop_ip:get(ip)
+ if count then
+ ngx.shared.waf_drop_ip:incr(ip, 1)
+ else
+ ngx.shared.waf_drop_ip:set(ip, 1, retry_cycle)
+ end
+
+ if config['log'] ~= true or self:is_site_config('log') ~= true then return false end
+ local method = params['method']
+ if error_rule then
+ rule = error_rule
+ error_rule = nil
+ end
+
+ local count = ngx.shared.waf_drop_ip:get(ip)
+ -- self:D("write_log; count:" ..tostring(count).. ",retry:" .. tostring(retry) )
+ if (count > retry and name ~= 'cc') then
+ local safe_count,_ = ngx.shared.waf_drop_sum:get(ip)
+ if not safe_count then
+ ngx.shared.waf_drop_sum:set(ip, 1, 86400)
+ safe_count = 1
+ else
+ ngx.shared.waf_drop_sum:incr(ip, 1)
+ end
+ local lock_time = retry_time * safe_count
+ if lock_time > 86400 then lock_time = 86400 end
+
+ retry_times = retry + 1
+ ngx.shared.waf_drop_ip:set(ip, retry_times, lock_time)
+
+ local reason = retry_cycle .. '秒以内累计超过'..retry..'次以上非法请求,封锁'.. lock_time ..'秒'
+ self:log(params, name, reason)
+ elseif name ~= 'cc' then
+ self:log(params, name, rule)
+ end
+
+ self:stats_total(name, rule)
+end
+
+
+function _M.get_real_ip(self, server_name)
+ local client_ip = "unknown"
+ local site_config = self.site_config
+ if site_config[server_name] then
+ if site_config[server_name]['cdn'] then
+ local request_header = ngx.req.get_headers()
+ for _,v in ipairs(site_config[server_name]['cdn_header'])
+ do
+ if request_header[v] ~= nil and request_header[v] ~= "" then
+ local header_tmp = request_header[v]
+ if type(header_tmp) == "table" then header_tmp = header_tmp[1] end
+ client_ip = self:split(header_tmp,',')[1]
+ -- return client_ip
+ break;
+ end
+ end
+ end
+ end
+
+
+ -- ipv6
+ if type(client_ip) == 'table' then client_ip = "" end
+ if client_ip ~= "unknown" and ngx.re.match(client_ip,"^([a-fA-F0-9]*):") then
+ return client_ip
+ end
+
+ -- ipv4
+ if not ngx.re.match(client_ip,"\\d+\\.\\d+\\.\\d+\\.\\d+") == nil or not self:is_ipaddr(client_ip) then
+ client_ip = ngx.var.remote_addr
+ if client_ip == nil then
+ client_ip = "unknown"
+ end
+ end
+ return client_ip
+end
+
+
+function _M.is_site_config(self,cname)
+ local site_config = self.site_config
+ if site_config[server_name] ~= nil then
+ if cname == 'cc' then
+ return site_config[server_name][cname]['open']
+ else
+ return site_config[server_name][cname]
+ end
+ end
+ return true
+end
+
+function _M.get_boundary(self)
+ local header = self.params["request_header"]["content-type"]
+ if not header then return nil end
+ if type(header) == "table" then
+ header = header[1]
+ end
+
+ local m = string.match(header, ";%s*boundary=\"([^\"]+)\"")
+ if m then
+ return m
+ end
+ return string.match(header, ";%s*boundary=([^\",;]+)")
+end
+
+
+function _M.is_key(self, arr, key)
+ for _,v in ipairs(arr) do
+ if v == key then
+ return true
+ end
+ end
+ return false
+end
+
+function _M.get_cpu_stat(self)
+ local cpu_total = 0
+ local fp = io.open('/proc/stat','r')
+ local cpu_line = fp:read()
+ fp:close()
+
+ local list = ngx_re.split(cpu_line," ")
+ table.remove(list,1)
+ table.remove(list,1)
+
+ local idie = list[4]
+ for i,v in pairs(list)
+ do
+ cpu_total = cpu_total + v
+ end
+
+ local use_percent = tonumber(100-(idie/cpu_total)*100)
+ return cpu_total,idie,use_percent
+end
+
+function _M.get_cpu_percent(self)
+ local cpu_total,idie,use_percent = self:get_cpu_stat()
+ ngx.sleep(2)
+ local cpu_total2,idie2,use_percent2 = self:get_cpu_stat()
+ local cpu_usage_percent = tonumber(100-(((idie2-idie)/(cpu_total2-cpu_total))*100))
+ return cpu_usage_percent
+end
+
+
+function _M.return_post_data(self)
+ if method ~= "POST" then return false end
+ content_length = tonumber(self.params["request_header"]['content-length'])
+ if not content_length then return false end
+ max_len = 2560 * 1024000
+ if content_length > max_len then return false end
+ local boundary = self:get_boundary()
+ if boundary then
+ ngx.req.read_body()
+ local data = ngx.req.get_body_data()
+ if not data then return false end
+ local tmp = ngx.re.match(data,[[filename=\"(.+)\.(.*)\"]])
+ if not tmp then return false end
+ if not tmp[2] then return false end
+ return tmp[2]
+
+ end
+ return false
+end
+
+
+function _M.t(self)
+ ngx.say(',,,')
+end
+
+return _M
diff --git a/plugins/op_waf/waf/lua/waf_maxminddb.lua b/plugins/op_waf/waf/lua/waf_maxminddb.lua
new file mode 100644
index 000000000..d98f03630
--- /dev/null
+++ b/plugins/op_waf/waf/lua/waf_maxminddb.lua
@@ -0,0 +1,362 @@
+--[[
+ Copyright 2017-now anjia (anjia0532@gmail.com)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+]]
+
+-- copy from https://github.com/lilien1010/lua-resty-maxminddb/blob/f96633e2428f8f7bcc1e2a7a28b747b33233a8db/resty/maxminddb.lua#L5-L12
+
+local ffi = require ('ffi')
+local ffi_new = ffi.new
+local ffi_str = ffi.string
+local ffi_cast = ffi.cast
+local ffi_gc = ffi.gc
+local C = ffi.C
+
+local _M ={}
+_M._VERSION = '1.3.3'
+local mt = { __index = _M }
+
+-- copy from https://github.com/lilien1010/lua-resty-maxminddb/blob/f96633e2428f8f7bcc1e2a7a28b747b33233a8db/resty/maxminddb.lua#L36-L126
+ffi.cdef[[
+
+typedef long int ssize_t;
+
+typedef unsigned int mmdb_uint128_t __attribute__ ((__mode__(TI)));
+
+typedef struct MMDB_entry_s {
+ struct MMDB_s *mmdb;
+ uint32_t offset;
+} MMDB_entry_s;
+
+typedef struct MMDB_lookup_result_s {
+ bool found_entry;
+ MMDB_entry_s entry;
+ uint16_t netmask;
+} MMDB_lookup_result_s;
+
+typedef struct MMDB_entry_data_s {
+ bool has_data;
+ union {
+ uint32_t pointer;
+ const char *utf8_string;
+ double double_value;
+ const uint8_t *bytes;
+ uint16_t uint16;
+ uint32_t uint32;
+ int32_t int32;
+ uint64_t uint64;
+ mmdb_uint128_t uint128;
+ bool boolean;
+ float float_value;
+ };
+
+ uint32_t offset;
+ uint32_t offset_to_next;
+ uint32_t data_size;
+ uint32_t type;
+} MMDB_entry_data_s;
+
+typedef struct MMDB_entry_data_list_s {
+ MMDB_entry_data_s entry_data;
+ struct MMDB_entry_data_list_s *next;
+} MMDB_entry_data_list_s;
+
+typedef struct MMDB_description_s {
+ const char *language;
+ const char *description;
+} MMDB_description_s;
+
+typedef struct MMDB_metadata_s {
+ uint32_t node_count;
+ uint16_t record_size;
+ uint16_t ip_version;
+ const char *database_type;
+ struct {
+ size_t count;
+ const char **names;
+ } languages;
+ uint16_t binary_format_major_version;
+ uint16_t binary_format_minor_version;
+ uint64_t build_epoch;
+ struct {
+ size_t count;
+ MMDB_description_s **descriptions;
+ } description;
+} MMDB_metadata_s;
+
+typedef struct MMDB_ipv4_start_node_s {
+ uint16_t netmask;
+ uint32_t node_value;
+} MMDB_ipv4_start_node_s;
+
+typedef struct MMDB_s {
+ uint32_t flags;
+ const char *filename;
+ ssize_t file_size;
+ const uint8_t *file_content;
+ const uint8_t *data_section;
+ uint32_t data_section_size;
+ const uint8_t *metadata_section;
+ uint32_t metadata_section_size;
+ uint16_t full_record_byte_size;
+ uint16_t depth;
+ MMDB_ipv4_start_node_s ipv4_start_node;
+ MMDB_metadata_s metadata;
+} MMDB_s;
+
+typedef char * pchar;
+
+MMDB_lookup_result_s MMDB_lookup_string(MMDB_s *const mmdb, const char *const ipstr, int *const gai_error,int *const mmdb_error);
+int MMDB_open(const char *const filename, uint32_t flags, MMDB_s *const mmdb);
+int MMDB_aget_value(MMDB_entry_s *const start, MMDB_entry_data_s *const entry_data, const char *const *const path);
+char *MMDB_strerror(int error_code);
+
+int MMDB_get_entry_data_list(MMDB_entry_s *start, MMDB_entry_data_list_s **const entry_data_list);
+void MMDB_free_entry_data_list(MMDB_entry_data_list_s *const entry_data_list);
+void MMDB_close(MMDB_s *const mmdb);
+const char *gai_strerror(int errcode);
+]]
+
+-- error codes
+-- https://github.com/maxmind/libmaxminddb/blob/master/include/maxminddb.h#L66
+local MMDB_SUCCESS = 0
+local MMDB_FILE_OPEN_ERROR = 1
+local MMDB_CORRUPT_SEARCH_TREE_ERROR = 2
+local MMDB_INVALID_METADATA_ERROR = 3
+local MMDB_IO_ERROR = 4
+local MMDB_OUT_OF_MEMORY_ERROR = 5
+local MMDB_UNKNOWN_DATABASE_FORMAT_ERROR = 6
+local MMDB_INVALID_DATA_ERROR = 7
+local MMDB_INVALID_LOOKUP_PATH_ERROR = 8
+local MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR = 9
+local MMDB_INVALID_NODE_NUMBER_ERROR = 10
+local MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR = 11
+
+-- data type
+-- https://github.com/maxmind/libmaxminddb/blob/master/include/maxminddb.h#L40
+local MMDB_DATA_TYPE_EXTENDED = 0
+local MMDB_DATA_TYPE_POINTER = 1
+local MMDB_DATA_TYPE_UTF8_STRING = 2
+local MMDB_DATA_TYPE_DOUBLE = 3
+local MMDB_DATA_TYPE_BYTES = 4
+local MMDB_DATA_TYPE_UINT16 = 5
+local MMDB_DATA_TYPE_UINT32 = 6
+local MMDB_DATA_TYPE_MAP = 7
+local MMDB_DATA_TYPE_INT32 = 8
+local MMDB_DATA_TYPE_UINT64 = 9
+local MMDB_DATA_TYPE_UINT128 = 10
+local MMDB_DATA_TYPE_ARRAY = 11
+local MMDB_DATA_TYPE_CONTAINER = 12
+local MMDB_DATA_TYPE_END_MARKER = 13
+local MMDB_DATA_TYPE_BOOLEAN = 14
+local MMDB_DATA_TYPE_FLOAT = 15
+
+-- copy from https://github.com/lilien1010/lua-resty-maxminddb/blob/f96633e2428f8f7bcc1e2a7a28b747b33233a8db/resty/maxminddb.lua#L136-L138
+-- you should install the libmaxminddb to your system
+local maxm = ffi.load('{$WAF_ROOT}/waf/mmdb/lib/libmaxminddb.{$MMDB_FILE_SUFFIX}')
+--https://github.com/maxmind/libmaxminddb
+local mmdb = ffi_new('MMDB_s')
+local initted = false
+
+local function mmdb_strerror(rc)
+ return ffi_str(maxm.MMDB_strerror(rc))
+end
+
+local function gai_strerror(rc)
+ return ffi_str(C.gai_strerror(rc))
+end
+
+function _M.init(dbfile)
+ if not initted then
+ local maxmind_ready = maxm.MMDB_open(dbfile,0,mmdb)
+
+ if maxmind_ready ~= MMDB_SUCCESS then
+ return nil, mmdb_strerror(maxmind_ready)
+ end
+
+ initted = true
+
+ ffi_gc(mmdb, maxm.MMDB_close)
+ end
+ return initted
+end
+
+function _M.initted()
+ return initted
+end
+
+-- https://github.com/maxmind/libmaxminddb/blob/master/src/maxminddb.c#L1938
+-- LOCAL MMDB_entry_data_list_s *dump_entry_data_list( FILE *stream, MMDB_entry_data_list_s *entry_data_list, int indent, int *status)
+local function _dump_entry_data_list(entry_data_list,status)
+
+ if not entry_data_list then
+ return nil,MMDB_INVALID_DATA_ERROR
+ end
+
+ local entry_data_item = entry_data_list[0].entry_data
+ local data_type = entry_data_item.type
+ local data_size = entry_data_item.data_size
+ local result
+
+ if data_type == MMDB_DATA_TYPE_MAP then
+ result = {}
+
+ local size = entry_data_item.data_size
+
+ entry_data_list = entry_data_list[0].next
+
+ while(size > 0 and entry_data_list)
+ do
+ entry_data_item = entry_data_list[0].entry_data
+ data_type = entry_data_item.type
+ data_size = entry_data_item.data_size
+
+ if MMDB_DATA_TYPE_UTF8_STRING ~= data_type then
+ return nil,MMDB_INVALID_DATA_ERROR
+ end
+
+ local key = ffi_str(entry_data_item.utf8_string,data_size)
+
+ if not key then
+ return nil,MMDB_OUT_OF_MEMORY_ERROR
+ end
+
+ local val
+ entry_data_list = entry_data_list[0].next
+ entry_data_list,status,val = _dump_entry_data_list(entry_data_list)
+
+ if status ~= MMDB_SUCCESS then
+ return nil,status
+ end
+
+ result[key] = val
+
+ size = size -1
+ end
+
+
+ elseif entry_data_list[0].entry_data.type == MMDB_DATA_TYPE_ARRAY then
+ local size = entry_data_list[0].entry_data.data_size
+ result = {}
+
+ entry_data_list = entry_data_list[0].next
+
+ local i = 1
+ while(i <= size and entry_data_list)
+ do
+ local val
+ entry_data_list,status,val = _dump_entry_data_list(entry_data_list)
+
+ if status ~= MMDB_SUCCESS then
+ return nil,nil,val
+ end
+
+ result[i] = val
+ i = i + 1
+ end
+
+
+ else
+ entry_data_item = entry_data_list[0].entry_data
+ data_type = entry_data_item.type
+ data_size = entry_data_item.data_size
+
+ local val
+ -- string type "key":"val"
+ -- other type "key":val
+ -- default other type
+ if data_type == MMDB_DATA_TYPE_UTF8_STRING then
+ val = ffi_str(entry_data_item.utf8_string,data_size)
+ if not val then
+ status = MMDB_OUT_OF_MEMORY_ERROR
+ return nil,status
+ end
+ elseif data_type == MMDB_DATA_TYPE_BYTES then
+ val = ffi_str(ffi_cast('char * ',entry_data_item.bytes),data_size)
+ if not val then
+ status = MMDB_OUT_OF_MEMORY_ERROR
+ return nil,status
+ end
+ elseif data_type == MMDB_DATA_TYPE_DOUBLE then
+ val = entry_data_item.double_value
+ elseif data_type == MMDB_DATA_TYPE_FLOAT then
+ val = entry_data_item.float_value
+ elseif data_type == MMDB_DATA_TYPE_UINT16 then
+ val = entry_data_item.uint16
+ elseif data_type == MMDB_DATA_TYPE_UINT32 then
+ val = entry_data_item.uint32
+ elseif data_type == MMDB_DATA_TYPE_BOOLEAN then
+ val = entry_data_item.boolean
+ elseif data_type == MMDB_DATA_TYPE_UINT64 then
+ val = entry_data_item.uint64
+ elseif data_type == MMDB_DATA_TYPE_INT32 then
+ val = entry_data_item.int32
+ else
+ return nil,MMDB_INVALID_DATA_ERROR
+ end
+
+ result = val
+ entry_data_list = entry_data_list[0].next
+ end
+
+ status = MMDB_SUCCESS
+ return entry_data_list,status,result
+end
+
+function _M.lookup(ip)
+
+ if not initted then
+ return nil, "not initialized"
+ end
+
+ -- copy from https://github.com/lilien1010/lua-resty-maxminddb/blob/f96633e2428f8f7bcc1e2a7a28b747b33233a8db/resty/maxminddb.lua#L159-L176
+ local gai_error = ffi_new('int[1]')
+ local mmdb_error = ffi_new('int[1]')
+
+ local result = maxm.MMDB_lookup_string(mmdb,ip,gai_error,mmdb_error)
+
+ if mmdb_error[0] ~= MMDB_SUCCESS then
+ return nil,'lookup failed: ' .. mmdb_strerror(mmdb_error[0])
+ end
+
+ if gai_error[0] ~= MMDB_SUCCESS then
+ return nil,'lookup failed: ' .. gai_strerror(gai_error[0])
+ end
+
+ if true ~= result.found_entry then
+ return nil,'not found'
+ end
+
+ local entry_data_list = ffi_cast('MMDB_entry_data_list_s **const',ffi_new("MMDB_entry_data_list_s"))
+
+ local status = maxm.MMDB_get_entry_data_list(result.entry,entry_data_list)
+
+ if status ~= MMDB_SUCCESS then
+ return nil,'get entry data failed: ' .. mmdb_strerror(status)
+ end
+
+ local head = entry_data_list[0] -- Save so this can be passed to free fn.
+ local _,status,result = _dump_entry_data_list(entry_data_list)
+ maxm.MMDB_free_entry_data_list(head)
+
+ if status ~= MMDB_SUCCESS then
+ return nil,'dump entry data failed: ' .. mmdb_strerror(status)
+ end
+
+
+ return result
+end
+
+-- copy from https://github.com/lilien1010/lua-resty-maxminddb/blob/master/resty/maxminddb.lua#L208
+-- https://www.maxmind.com/en/geoip2-databases you should download the mmdb file from maxmind
+
+return _M;
\ No newline at end of file
diff --git a/plugins/op_waf/waf/rule/args.json b/plugins/op_waf/waf/rule/args.json
new file mode 100644
index 000000000..6cfbbd73d
--- /dev/null
+++ b/plugins/op_waf/waf/rule/args.json
@@ -0,0 +1,28 @@
+[
+ [1, "\\.\\./\\.\\./", "\u76ee\u5f55\u4fdd\u62a41", 0],
+ [1, "/\\*", "\u76ee\u5f55\u4fdd\u62a42", 0],
+ [1, "(?:etc\\/\\W*passwd)", "\u76ee\u5f55\u4fdd\u62a43", 0],
+ [1, "(gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data)\\:\\/", "PHP\u6d41\u534f\u8bae\u8fc7\u6ee41", 0],
+ [1, "\\:\\$", "\u4e00\u53e5\u8bdd\u6728\u9a6c\u8fc7\u6ee41", 0],
+ [1, "\\$\\{", "\u4e00\u53e5\u8bdd\u6728\u9a6c\u8fc7\u6ee42", 0],
+ [1, "base64_decode\\(", "\u4e00\u53e5\u8bdd\u6728\u9a6c\u8fc7\u6ee43", 0],
+ [1, "(?:define|eval|file_get_contents|include|require|require_once|shell_exec|phpinfo|system|passthru|char|chr|preg_\\w+|execute|echo|print|print_r|var_dump|(fp)open|alert|showmodaldialog)\\(", "\u4e00\u53e5\u8bdd\u6728\u9a6c\u8fc7\u6ee44", 0],
+ [1, "\\$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)\\[", "\u4e00\u53e5\u8bdd\u6728\u9a6c\u8fc7\u6ee45", 0],
+ [1, "\\s+(or|xor|and)\\s+.*(=|<|>|'|\")", "SQL\u6ce8\u5165\u8fc7\u6ee41", 0],
+ [1, "select.+(from|limit)", "SQL\u6ce8\u5165\u8fc7\u6ee42", 0],
+ [1, "(?:(union(.*?)select))", "SQL\u6ce8\u5165\u8fc7\u6ee43", 0],
+ [1, "sleep\\((\\s*)(\\d*)(\\s*)\\)", "SQL\u6ce8\u5165\u8fc7\u6ee45", 0],
+ [1, "benchmark\\((.*)\\,(.*)\\)", "SQL\u6ce8\u5165\u8fc7\u6ee46", 0],
+ [1, "(?:from\\W+information_schema\\W)", "SQL\u6ce8\u5165\u8fc7\u6ee47", 0],
+ [1, "(?:(?:current_)user|database|schema|connection_id)\\s*\\(", "SQL\u6ce8\u5165\u8fc7\u6ee48", 0],
+ [1, "into(\\s+)+(?:dump|out)file\\s*", "SQL\u6ce8\u5165\u8fc7\u6ee49", 0],
+ [1, "group\\s+by.+\\(", "SQL\u6ce8\u5165\u8fc7\u6ee410", 0],
+ [1, "\\<(iframe|script|body|img|layer|div|meta|style|base|object|input)", "XSS\u8fc7\u6ee41", 0],
+ [0, "(onmouseover|onerror|onload)\\=", "XSS\u8fc7\u6ee42", 0],
+ [1, "(invokefunction|call_user_func_array|\\\\think\\\\)", "ThinkPHP payload\u5c01\u5835", 0],
+ [1, "^url_array\\[.*\\]$", "Metinfo6.x XSS\u6f0f\u6d1e", 0],
+ [1, "(extractvalue\\(|concat\\(0x|user\\(\\)|substring\\(|count\\(\\*\\)|substring\\(hex\\(|updatexml\\()", "SQL\u62a5\u9519\u6ce8\u5165\u8fc7\u6ee401", 0],
+ [1, "(@@version|load_file\\(|NAME_CONST\\(|exp\\(\\~|floor\\(rand\\(|geometrycollection\\(|multipoint\\(|polygon\\(|multipolygon\\(|linestring\\(|multilinestring\\()", "SQL\u62a5\u9519\u6ce8\u5165\u8fc7\u6ee402", 0],
+ [1, "(substr\\()", "SQL\u6ce8\u5165\u8fc7\u6ee410", 0],
+ [1, "\\|+\\s+[\\w\\W]+=[\\w\\W]+", "SQL\u6ce8\u5165\u8fc7\u6ee41", 0]
+]
\ No newline at end of file
diff --git a/plugins/op_waf/waf/rule/cookie.json b/plugins/op_waf/waf/rule/cookie.json
new file mode 100755
index 000000000..51d5e565e
--- /dev/null
+++ b/plugins/op_waf/waf/rule/cookie.json
@@ -0,0 +1,21 @@
+[
+ [1, "\\.\\./\\.\\./", "\u76ee\u5f55\u4fdd\u62a41", 0],
+ [1, "(?:etc\\/\\W*passwd)", "\u76ee\u5f55\u4fdd\u62a43", 0],
+ [1, "(gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data)\\:\\/", "PHP\u6d41\u534f\u8bae\u8fc7\u6ee41", 0],
+ [1, "\\:\\$", "\u4e00\u53e5\u8bdd\u6728\u9a6c\u8fc7\u6ee41", 0],
+ [1, "\\$\\{", "\u4e00\u53e5\u8bdd\u6728\u9a6c\u8fc7\u6ee42", 0],
+ [1, "base64_decode\\(", "\u4e00\u53e5\u8bdd\u6728\u9a6c\u8fc7\u6ee43", 0],
+ [1, "\\$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)\\[", "\u4e00\u53e5\u8bdd\u6728\u9a6c\u8fc7\u6ee45", 0],
+ [1, "\\b(or|xor|and)\\b.*(=|<|>|'|\")", "SQL\u6ce8\u5165\u8fc7\u6ee41", 0],
+ [1, "select.+(from|limit)", "SQL\u6ce8\u5165\u8fc7\u6ee42", 0],
+ [1, "(?:(union(.*?)select))", "SQL\u6ce8\u5165\u8fc7\u6ee43", 0],
+ [0, "having|load_file", "SQL\u6ce8\u5165\u8fc7\u6ee44", 0],
+ [1, "sleep\\((\\s*)(\\d*)(\\s*)\\)", "SQL\u6ce8\u5165\u8fc7\u6ee45", 0],
+ [1, "benchmark\\((.*)\\,(.*)\\)", "SQL\u6ce8\u5165\u8fc7\u6ee46", 0],
+ [1, "(?:from\\W+information_schema\\W)", "SQL\u6ce8\u5165\u8fc7\u6ee47", 0],
+ [1, "(?:(?:current_)user|database|schema|connection_id)\\s*\\(", "SQL\u6ce8\u5165\u8fc7\u6ee48", 0],
+ [1, "into(\\s+)+(?:dump|out)file\\s*", "SQL\u6ce8\u5165\u8fc7\u6ee49", 0],
+ [1, "group\\s+by.+\\(", "SQL\u6ce8\u5165\u8fc7\u6ee410", 0],
+ [0, "\\<(iframe|script|body|img|layer|div|meta|style|base|object|input)", "XSS\u8fc7\u6ee41", 0],
+ [1, "(onmouseover|onerror|onload)\\=", "XSS\u8fc7\u6ee42", 0]
+]
\ No newline at end of file
diff --git a/plugins/op_waf/waf/rule/ip_black.json b/plugins/op_waf/waf/rule/ip_black.json
new file mode 100755
index 000000000..86bb9fea1
--- /dev/null
+++ b/plugins/op_waf/waf/rule/ip_black.json
@@ -0,0 +1 @@
+[[[94, 130, 9, 116], [94, 130, 9, 116]]]
\ No newline at end of file
diff --git a/plugins/op_waf/waf/rule/ip_white.json b/plugins/op_waf/waf/rule/ip_white.json
new file mode 100755
index 000000000..ae76d5de3
--- /dev/null
+++ b/plugins/op_waf/waf/rule/ip_white.json
@@ -0,0 +1 @@
+[[[127,0,0,1], [127, 0, 0, 255]]]
\ No newline at end of file
diff --git a/plugins/op_waf/waf/rule/ipv6_black.json b/plugins/op_waf/waf/rule/ipv6_black.json
new file mode 100755
index 000000000..0637a088a
--- /dev/null
+++ b/plugins/op_waf/waf/rule/ipv6_black.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/plugins/op_waf/waf/rule/post.json b/plugins/op_waf/waf/rule/post.json
new file mode 100755
index 000000000..92213f01e
--- /dev/null
+++ b/plugins/op_waf/waf/rule/post.json
@@ -0,0 +1,23 @@
+[
+ [1, "(gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data)\\:\\/", "PHP\u6d41\u534f\u8bae\u8fc7\u6ee41", 0],
+ [1, "base64_decode\\(", "\u4e00\u53e5\u8bdd*\u5c4f\u853d\u7684\u5173\u952e\u5b57*\u8fc7\u6ee41", 0],
+ [1, "(?:define|eval|file_get_contents|include|require_once|shell_exec|phpinfo|system|passthru|chr|char|preg_\\w+|execute|echo|print|print_r|var_dump|(fp)open|alert|showmodaldialog|file_put_contents|fopen|urldecode|scandir)\\(", "\u4e00\u53e5\u8bdd*\u5c4f\u853d\u7684\u5173\u952e\u5b57*\u8fc7\u6ee42", 0],
+ [1, "\\$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)", "\u4e00\u53e5\u8bdd*\u5c4f\u853d\u7684\u5173\u952e\u5b57*\u8fc7\u6ee43", 0], [1, "\\s+(or|xor|and)\\s+(=|<|>|'|\")", "SQL\u6ce8\u5165\u8fc7\u6ee41", 0],
+ [1, "select\\s+.+(from|limit)\\s+", "SQL\u6ce8\u5165\u8fc7\u6ee42", 0],
+ [1, "(?:(union(.*?)select))", "SQL\u6ce8\u5165\u8fc7\u6ee43", 0],
+ [1, "sleep\\((\\s*)(\\d*)(\\s*)\\)", "SQL\u6ce8\u5165\u8fc7\u6ee45", 0],
+ [1, "benchmark\\((.*)\\,(.*)\\)", "SQL\u6ce8\u5165\u8fc7\u6ee46", 0],
+ [1, "(?:from\\W+information_schema\\W)", "SQL\u6ce8\u5165\u8fc7\u6ee47", 0],
+ [1, "(?:(?:current_)user|database|schema|connection_id)\\s*\\(", "SQL\u6ce8\u5165\u8fc7\u6ee48", 0],
+ [1, "into(\\s+)+(?:dump|out)file\\s*", "SQL\u6ce8\u5165\u8fc7\u6ee49", 0],
+ [1, "group\\s+by.+\\(", "SQL\u6ce8\u5165\u8fc7\u6ee410", 0],
+ [0, "\\<(iframe|script|body|img|layer|div|meta|style|base|object|input)", "XSS\u8fc7\u6ee41", 0],
+ [0, "(onmouseover|onerror|onload)\\=", "XSS\u8fc7\u6ee42", 0],
+ [1, "(extractvalue\\(|concat\\(0x|user\\(\\)|substring\\(|count\\(\\*\\)|substring\\(hex\\(|updatexml\\()", "SQL\u62a5\u9519\u6ce8\u5165\u8fc7\u6ee401", 0],
+ [1, "(@@version|load_file\\(|NAME_CONST\\(|exp\\(\\~|floor\\(rand\\(|geometrycollection\\(|multipoint\\(|polygon\\(|multipolygon\\(|linestring\\(|multilinestring\\()", "SQL\u62a5\u9519\u6ce8\u5165\u8fc7\u6ee402", 0],
+ [1, "(substr\\()", "SQL\u6ce8\u5165\u8fc7\u6ee410", 0],
+ [1, "(ORD\\(|MID\\(|IFNULL\\(|CAST\\(|CHAR\\))", "SQL\u6ce8\u5165\u8fc7\u6ee41", 0],
+ [1, "(EXISTS\\(|SELECT\\#|\\(SELECT)", "SQL\u6ce8\u5165\u8fc7\u6ee41", 0],
+ [1, "(array_map\\(\"ass)", "\u83dc\u5200\u6d41\u91cf\u8fc7\u6ee4", 0],
+ [1, "(?:define|eval|file_get_contents|include|require_once|shell_exec|phpinfo|system|passthru|chr|char|preg_\\w+|execute|echo|print|print_r|var_dump|(fp)open|alert|showmodaldialog|file_put_contents|fopen|urldecode)\\(", "\u4e00\u53e5\u8bdd*\u5c4f\u853d\u7684\u5173\u952e\u5b57*\u8fc7\u6ee42", 0]
+]
\ No newline at end of file
diff --git a/plugins/op_waf/waf/rule/scan_black.json b/plugins/op_waf/waf/rule/scan_black.json
new file mode 100755
index 000000000..a567c4a80
--- /dev/null
+++ b/plugins/op_waf/waf/rule/scan_black.json
@@ -0,0 +1 @@
+{"header": "(Acunetix-Aspect|Acunetix-Aspect-Password|Acunetix-Aspect-Queries|X-WIPP|X-RequestManager-Memo|X-Request-Memo|X-Scan-Memo)", "args": "(/acunetix-wvs-test-for-some-inexistent-file|netsparker|acunetix_wvs_security_test|AppScan|XSS@HERE)", "cookie": "(CustomCookie|acunetixCookie)"}
\ No newline at end of file
diff --git a/plugins/op_waf/waf/rule/url.json b/plugins/op_waf/waf/rule/url.json
new file mode 100755
index 000000000..28604915d
--- /dev/null
+++ b/plugins/op_waf/waf/rule/url.json
@@ -0,0 +1 @@
+[[1, "\\.(htaccess|mysql_history|bash_history|DS_Store|idea|user\\.ini)", "\u6587\u4ef6\u76ee\u5f55\u8fc7\u6ee41", 0], [1, "\\.(bak|inc|old|mdb|sql|php~|swp|java|class)$", "\u6587\u4ef6\u76ee\u5f55\u8fc7\u6ee42", 0], [1, "^/(vhost|bbs|host|wwwroot|www|site|root|backup|data|ftp|db|admin|website|web).*\\.(rar|sql|zip|tar\\.gz|tar)$", "\u6587\u4ef6\u76ee\u5f55\u8fc7\u6ee43", 0], [1, "/(hack|shell|spy|phpspy)\\.php$", "PHP\u811a\u672c\u6267\u884c\u8fc7\u6ee41", 0], [1, "^/(attachments|css|uploadfiles|static|forumdata|cache|avatar)/(\\w+).(php|jsp)$", "PHP\u811a\u672c\u6267\u884c\u8fc7\u6ee42", 0], [1, "(?:(union(.*?)select))", "SQL\u6ce8\u5165\u8fc7\u6ee41", 0], [1, "(?:define|eval|file_get_contents|include|require|require_once|shell_exec|phpinfo|system|passthru|preg_\\w+|execute|echo|print|print_r|var_dump|(fp)open|alert|showmodaldialog)\\(", "\u4e00\u53e5\u8bdd\u6728\u9a6c\u8fc7\u6ee41", 1]]
\ No newline at end of file
diff --git a/plugins/op_waf/waf/rule/url_white.json b/plugins/op_waf/waf/rule/url_white.json
new file mode 100755
index 000000000..2293f0683
--- /dev/null
+++ b/plugins/op_waf/waf/rule/url_white.json
@@ -0,0 +1 @@
+[[1,"^/(phpmyadmin)","MySQL[phpMyAdmin]", 0]]
\ No newline at end of file
diff --git a/plugins/op_waf/waf/rule/user_agent.json b/plugins/op_waf/waf/rule/user_agent.json
new file mode 100755
index 000000000..3e8a87a05
--- /dev/null
+++ b/plugins/op_waf/waf/rule/user_agent.json
@@ -0,0 +1,8 @@
+[
+ [1,"(HTTrack|Apache-HttpClient|harvest|audit|dirbuster|pangolin|nmap|sqln|hydra|Parser|libwww|BBBike|sqlmap|w3af|owasp|Nikto|fimap|havij|zmeu|BabyKrokodil|netsparker|httperf| SF/)","关键词过滤1",0],
+ [1,"(ApacheBench)","AB测试",0],
+ [1,"(Amazonbot)","Amazon爬虫",0],
+ [1,"(SemrushBot)","Semrush爬虫",0],
+ [1,"(^$|Apache-HttpClient|colly|curl|okhttp|Go-http-client|python-requests|Python-urllib|python-httpx|Scrapy|aiohttp|Nmap Scripting Engine|Java|fasthttp|Wget)","非法脚本",0],
+ [1,"(CensysInspect|intelx\\.io_bot|InternetMeasurement|ips-agent|MJ12Bot|NetcraftSurveyAgent|SemrushBot|l9scan|SEOlyt|kirkland-signature|ZoominfoBot|Expanse|CheckMarkNetwork|dotbot|Pandalytics|Screaming Frog SEO Spider|W3C_CSS_Validator_JFouffa)","非法扫描器",0]
+]
diff --git a/plugins/op_waf/waf/site.json b/plugins/op_waf/waf/site.json
new file mode 100755
index 000000000..9e26dfeeb
--- /dev/null
+++ b/plugins/op_waf/waf/site.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/plugins/op_waf/waf/total.json b/plugins/op_waf/waf/total.json
new file mode 100644
index 000000000..f9a5f91d2
--- /dev/null
+++ b/plugins/op_waf/waf/total.json
@@ -0,0 +1 @@
+{"rules":{"path":0,"php_path":0,"upload_ext":0,"user_agent":0,"scan":0,"cookie":0,"post":0,"args":0,"url":0,"cc":0},"sites":{},"total":0}
\ No newline at end of file
diff --git a/plugins/openresty/check.sh b/plugins/openresty/check.sh
new file mode 100755
index 000000000..e6b36e6de
--- /dev/null
+++ b/plugins/openresty/check.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# OpenResty服务名称
+service_name="openresty"
+
+# 检查OpenResty是否正在运行
+if systemctl is-active --quiet "$service_name"; then
+ # 检查是否存在僵尸进程
+ zombie_processes=$(ps -ef | grep -i openresty | grep -v grep | awk '{print $2}' | xargs ps -o state= -p 2>/dev/null | grep -c Z)
+ if [ "$zombie_processes" -gt 0 ]; then
+ echo "kill nginx 僵尸进程"
+ ps -ef|grep nginx| grep -v grep| awk '{print $2}' | xargs kill -9
+ echo "检测到OpenResty僵尸进程,正在重启服务..."
+ systemctl restart "$service_name"
+ echo "服务已重启"
+ else
+ echo "OpenResty运行正常"
+ fi
+else
+ echo "kill nginx"
+ ps -ef|grep nginx| grep -v grep| awk '{print $2}' | xargs kill -9
+ echo "OpenResty未运行,正在启动服务..."
+ systemctl start "$service_name"
+ echo "服务已启动"
+fi
+
+NGINX_IDS=`ps -ef|grep nginx | grep -v grep| awk '{print $2}'`
+if [ "$NGINX_IDS" == "" ];then
+ ps -ef|grep nginx| grep -v grep| awk '{print $2}' | xargs kill -9
+ systemctl start "$service_name"
+ echo "OpenResty未运行,正在启动服务..."
+fi
+
diff --git a/plugins/openresty/conf/lua.conf b/plugins/openresty/conf/lua.conf
new file mode 100644
index 000000000..fd0ddba03
--- /dev/null
+++ b/plugins/openresty/conf/lua.conf
@@ -0,0 +1,13 @@
+lua_package_path "{$SERVER_PATH}/web_conf/nginx/lua/?.lua;{$SERVER_PATH}/openresty/lualib/?.lua;;";
+lua_package_cpath "{$SERVER_PATH}/web_conf/nginx/lua/?.so;{$SERVER_PATH}/openresty/lualib/?.so;;";
+
+lua_code_cache on;
+
+#init_by_lua_file
+init_by_lua_file {$SERVER_PATH}/web_conf/nginx/lua/empty.lua;
+
+#init_worker_by_lua
+init_worker_by_lua_file {$SERVER_PATH}/web_conf/nginx/lua/empty.lua;
+
+#access_by_lua_file
+access_by_lua_file {$SERVER_PATH}/web_conf/nginx/lua/empty.lua;
\ No newline at end of file
diff --git a/plugins/openresty/conf/nginx.conf b/plugins/openresty/conf/nginx.conf
new file mode 100644
index 000000000..d0b66ff63
--- /dev/null
+++ b/plugins/openresty/conf/nginx.conf
@@ -0,0 +1,93 @@
+user {$OS_USER} {$OS_USER_GROUP};
+worker_processes auto;
+worker_cpu_affinity auto;
+error_log {$SERVER_PATH}/openresty/nginx/logs/error.log crit;
+pid {$SERVER_PATH}/openresty/nginx/logs/nginx.pid;
+
+worker_rlimit_nofile 65535;
+
+events
+{
+ use {$EVENT_MODEL};
+ worker_connections 51200;
+ multi_accept on;
+
+ # file/video close the mutex lock when under high load
+ # accept_mutex off;
+}
+
+http
+{
+ include mime.types;
+
+ include {$SERVER_PATH}/web_conf/nginx/lua/lua.conf;
+ #include proxy.conf;
+
+ default_type application/octet-stream;
+
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for" "$request_time"';
+
+ server_names_hash_bucket_size 512;
+ client_header_buffer_size 32k;
+ large_client_header_buffers 4 32k;
+ client_body_buffer_size 50m;
+ client_max_body_size 50m;
+
+ # video big file opt
+ # aio threads;
+ directio 4m;
+ output_buffers 16 512k;
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+
+ keepalive_timeout 60;
+
+ fastcgi_connect_timeout 300;
+ fastcgi_send_timeout 300;
+ fastcgi_read_timeout 300;
+ fastcgi_buffer_size 64k;
+ fastcgi_buffers 4 64k;
+ fastcgi_busy_buffers_size 128k;
+ fastcgi_temp_file_write_size 256k;
+ fastcgi_intercept_errors on;
+
+ gzip on;
+ gzip_min_length 1k;
+ gzip_buffers 4 16k;
+ gzip_http_version 1.1;
+ gzip_comp_level 9;
+ gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml;
+ gzip_vary on;
+ gzip_proxied expired no-cache no-store private auth;
+ gzip_disable "MSIE [1-6]\.";
+
+ limit_conn_zone $binary_remote_addr zone=perip:10m;
+ limit_conn_zone $server_name zone=perserver:10m;
+
+ # CACEH_BEGIN
+ proxy_buffering on;
+ proxy_buffer_size 1024k;
+ proxy_buffers 16 1024k;
+ proxy_busy_buffers_size 2048k;
+ proxy_temp_file_write_size 2048k;
+ proxy_cache_path {$SERVER_PATH}/openresty/nginx/proxy_cache_temp levels=1:2 keys_zone=mw_cache:512m inactive=5m max_size=2g use_temp_path=off;
+ #proxy timeout
+ proxy_connect_timeout 3s;
+ proxy_read_timeout 5s;
+ proxy_send_timeout 5s;
+
+ fastcgi_cache_key "$scheme$request_method$host$request_uri";
+ fastcgi_cache_path {$SERVER_PATH}/openresty/nginx/fastcgi_cache_temp levels=1:2 keys_zone=mw_cache_fcgi:100m inactive=60m max_size=5g;
+ fastcgi_cache_use_stale error timeout invalid_header http_500;
+ fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
+ # CACEH_END
+
+ server_tokens off;
+ access_log /dev/null;
+
+ include {$SERVER_PATH}/web_conf/nginx/vhost/*.conf;
+}
+
diff --git a/plugins/openresty/conf/vhost/0.nginx_status.conf b/plugins/openresty/conf/vhost/0.nginx_status.conf
new file mode 100644
index 000000000..751645a4c
--- /dev/null
+++ b/plugins/openresty/conf/vhost/0.nginx_status.conf
@@ -0,0 +1,10 @@
+server {
+ listen 80;
+ listen [::]:80;
+ server_name 127.0.0.1;
+ allow 127.0.0.1;
+ location /nginx_status {
+ stub_status on;
+ access_log off;
+ }
+}
\ No newline at end of file
diff --git a/plugins/openresty/conf/vhost/0.websocket.conf b/plugins/openresty/conf/vhost/0.websocket.conf
new file mode 100644
index 000000000..60e726a51
--- /dev/null
+++ b/plugins/openresty/conf/vhost/0.websocket.conf
@@ -0,0 +1,4 @@
+map $http_upgrade $connection_upgrade {
+ default upgrade;
+ '' close;
+}
diff --git a/plugins/openresty/ico.png b/plugins/openresty/ico.png
new file mode 100644
index 000000000..39d56b20c
Binary files /dev/null and b/plugins/openresty/ico.png differ
diff --git a/plugins/openresty/index.html b/plugins/openresty/index.html
new file mode 100755
index 000000000..8b7a409df
--- /dev/null
+++ b/plugins/openresty/index.html
@@ -0,0 +1,24 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/openresty/index.py b/plugins/openresty/index.py
new file mode 100755
index 000000000..58c921fab
--- /dev/null
+++ b/plugins/openresty/index.py
@@ -0,0 +1,668 @@
+# 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 'openresty'
+
+
+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() + "/nginx"
+ mw.execShell('rm -rf ' + path_bin + '/client_body_temp')
+ mw.execShell('rm -rf ' + path_bin + '/fastcgi_temp')
+ mw.execShell('rm -rf ' + path_bin + '/proxy_temp')
+ mw.execShell('rm -rf ' + path_bin + '/scgi_temp')
+ mw.execShell('rm -rf ' + path_bin + '/uwsgi_temp')
+
+
+def getConf():
+ path = getServerDir() + "/nginx/conf/nginx.conf"
+ return path
+
+
+def getConfTpl():
+ path = getPluginDir() + '/conf/nginx.conf'
+ return path
+
+
+def getOs():
+ data = {}
+ data['os'] = mw.getOs()
+ ng_exe_bin = getServerDir() + "/nginx/sbin/nginx"
+
+ # if mw.isAppleSystem():
+ # data['auth'] = True
+ # return mw.getJson(data)
+
+ if checkAuthEq(ng_exe_bin, 'root'):
+ data['auth'] = True
+ else:
+ data['auth'] = False
+ return mw.getJson(data)
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/nginx.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)
+
+ user = 'www'
+ user_group = 'www'
+
+ current_os = mw.getOs()
+ if current_os == 'darwin':
+ # macosx do
+ # user = mw.execShell(
+ # "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
+ user = 'midoks'
+ user_group = 'staff'
+ content = content.replace('{$EVENT_MODEL}', 'kqueue')
+ elif current_os.startswith('freebsd'):
+ content = content.replace('{$EVENT_MODEL}', 'kqueue')
+ else:
+ content = content.replace('{$EVENT_MODEL}', 'epoll')
+
+ content = content.replace('{$OS_USER}', user)
+ content = content.replace('{$OS_USER_GROUP}', user_group)
+
+ # ng_conf_md5 = ''
+ # ng_conf_md5_file = getServerDir() + '/nginx_conf.md5'
+ # if not os.path.exists(ng_conf_md5_file):
+ # ng_conf_md5 = mw.md5(content)
+ # mw.writeFile(ng_conf_md5_file, ng_conf_md5)
+ # else:
+ # ng_conf_md5 = mw.writeFile(ng_conf_md5_file).strip()
+
+ # 主配置文件
+ nconf = getServerDir() + '/nginx/conf/nginx.conf'
+ mw.writeFile(nconf, content)
+
+ # lua配置
+ lua_conf_dir = mw.getServerDir() + '/web_conf/nginx/lua'
+ if not os.path.exists(lua_conf_dir):
+ mw.execShell('mkdir -p ' + lua_conf_dir)
+
+ lua_conf = lua_conf_dir + '/lua.conf'
+ lua_conf_tpl = getPluginDir() + '/conf/lua.conf'
+ lua_content = mw.readFile(lua_conf_tpl)
+ lua_content = lua_content.replace('{$SERVER_PATH}', service_path)
+ mw.writeFile(lua_conf, lua_content)
+
+ empty_lua = lua_conf_dir + '/empty.lua'
+ if not os.path.exists(empty_lua):
+ mw.writeFile(empty_lua, '')
+
+ mw.opLuaMakeAll()
+
+ # 静态配置
+ php_conf = mw.getServerDir() + '/web_conf/php/conf'
+ if not os.path.exists(php_conf):
+ mw.execShell('mkdir -p ' + php_conf)
+ static_conf = mw.getServerDir() + '/web_conf/php/conf/enable-php-00.conf'
+ if not os.path.exists(static_conf):
+ mw.writeFile(static_conf, 'set $PHP_ENV 0;')
+
+ # vhost
+ vhost_dir = mw.getServerDir() + '/web_conf/nginx/vhost'
+ vhost_tpl_dir = getPluginDir() + '/conf/vhost'
+ if not os.path.exists(vhost_dir):
+ mw.execShell('mkdir -p ' + vhost_dir)
+
+ vhost_list = ['0.websocket.conf', '0.nginx_status.conf']
+ for f in vhost_list:
+ a_conf = vhost_dir + '/' + f
+ a_conf_tpl = vhost_tpl_dir + '/' + f
+ if not os.path.exists(a_conf):
+ mw.writeFile(a_conf, mw.readFile(a_conf_tpl))
+
+ # copy resty lib
+ src_resty_dir = getPluginDir()+'/resty/*'
+ dst_resty_dir = getServerDir()+'/lualib/resty'
+ mw.execShell('cp -rf ' + src_resty_dir + ' ' + dst_resty_dir)
+
+
+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()
+
+ # give nginx root permission
+ ng_exe_bin = getServerDir() + "/nginx/sbin/nginx"
+ if not checkAuthEq(ng_exe_bin, 'root'):
+ user = 'www'
+ user_group = 'www'
+ current_os = mw.getOs()
+ if current_os == 'darwin':
+ user = 'root'
+ user_group = 'staff'
+ args = getArgs()
+ if not 'pwd' in args:
+ print("权限不足,需要认证启动!")
+ exit(0)
+
+ sudoPwd = args['pwd']
+ cmd_own = 'chown -R ' + user+':' + user_group + ' ' + ng_exe_bin
+ mw.execShell('echo %s|sudo -S %s' % (sudoPwd, cmd_own))
+ cmd_mod = 'chmod 755 ' + ng_exe_bin
+ mw.execShell('echo %s|sudo -S %s' % (sudoPwd, cmd_mod))
+ cmd_s = 'chmod u+s ' + ng_exe_bin
+ mw.execShell('echo %s|sudo -S %s' % (sudoPwd, cmd_s))
+
+ # systemd
+ # /usr/lib/systemd/system
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/openresty.service'
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ systemServiceTpl = getPluginDir() + '/init.d/openresty.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():
+ pid_file = getPidFile()
+ if not os.path.exists(pid_file):
+ return 'stop'
+ return 'start'
+
+
+def restyOp(method):
+ file = initDreplace()
+
+ # 启动时,先检查一下配置文件
+ check = getServerDir() + "/bin/openresty -t"
+ check_data = mw.execShell(check)
+ if not check_data[1].find('test is successful') > -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"):
+ data = mw.execShell('service openresty ' + method)
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+ data = mw.execShell('systemctl ' + method + ' openresty')
+ 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 openresty restart')
+ return True
+
+ mw.execShell('systemctl restart openresty')
+ return True
+
+
+def op_submit_init_restart(file):
+ mw.execShell(file + ' restart')
+
+
+def restyOp_restart():
+ file = initDreplace()
+
+ # 启动时,先检查一下配置文件
+ check = getServerDir() + "/bin/openresty -t"
+ check_data = mw.execShell(check)
+ if not check_data[1].find('test is successful') > -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')
+ pid_file = getPidFile()
+ if os.path.exists(pid_file):
+ os.remove(pid_file)
+ 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 openresty | 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 ' + getPluginName() + '_enable="YES"')
+ return 'ok'
+
+ mw.execShell('systemctl enable openresty')
+ 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 ' + getPluginName() + '_enable="NO"')
+ return 'ok'
+
+ mw.execShell('systemctl disable openresty')
+ return 'ok'
+
+def getNgxStatusPort():
+ ngx_status_file = mw.getServerDir() + '/web_conf/nginx/vhost/0.nginx_status.conf'
+ content = mw.readFile(ngx_status_file)
+ rep = r'listen\s*(.*);'
+ tmp = re.search(rep, content)
+ port = tmp.groups()[0].strip()
+ return port
+
+
+def runInfo():
+ op_status = status()
+ if op_status == 'stop':
+ return mw.returnJson(False, "未启动!")
+
+ port = getNgxStatusPort()
+ # 取Openresty负载状态
+ try:
+ url = 'http://127.0.0.1:%s/nginx_status' % port
+ result = mw.httpGet(url, timeout=3)
+ tmp = result.split()
+ data = {}
+ data['active'] = tmp[2]
+ data['accepts'] = tmp[9]
+ data['handled'] = tmp[7]
+ data['requests'] = tmp[8]
+ data['Reading'] = tmp[11]
+ data['Writing'] = tmp[13]
+ data['Waiting'] = tmp[15]
+ return mw.getJson(data)
+ except Exception as e:
+ try:
+ url = 'http://' + mw.getHostAddr() + ':%s/nginx_status' % port
+ result = mw.httpGet(url)
+ tmp = result.split()
+ data = {}
+ data['active'] = tmp[2]
+ data['accepts'] = tmp[9]
+ data['handled'] = tmp[7]
+ data['requests'] = tmp[8]
+ data['Reading'] = tmp[11]
+ data['Writing'] = tmp[13]
+ data['Waiting'] = tmp[15]
+ return mw.getJson(data)
+ except Exception as e:
+ return mw.returnJson(False, "oprenresty异常!")
+
+ except Exception as e:
+ return mw.returnJson(False, "oprenresty not started!")
+
+
+def errorLogPath():
+ return getServerDir() + '/nginx/logs/error.log'
+
+
+def getCfg():
+ cfg = getConf()
+ content = mw.readFile(cfg)
+
+ unitrep = "[kmgKMG]"
+ cfg_args = [
+ {"name": "worker_processes", "ps": "处理进程,auto表示自动,数字表示进程数", 'type': 2},
+ {"name": "worker_connections", "ps": "最大并发链接数", 'type': 2},
+ {"name": "keepalive_timeout", "ps": "连接超时时间", 'type': 2},
+ {"name": "gzip", "ps": "是否开启压缩传输", 'type': 1},
+ {"name": "gzip_min_length", "ps": "最小压缩文件", 'type': 2},
+ {"name": "gzip_comp_level", "ps": "压缩率", 'type': 2},
+ {"name": "client_max_body_size", "ps": "最大上传文件", 'type': 2},
+ {"name": "server_names_hash_bucket_size",
+ "ps": "服务器名字的hash表大小", 'type': 2},
+ {"name": "client_header_buffer_size", "ps": "客户端请求头buffer大小", 'type': 2},
+ ]
+
+ # {"name": "client_body_buffer_size", "ps": "请求主体缓冲区"}
+ rdata = []
+ for i in cfg_args:
+ rep = r"(%s)\s+(\w+)" % i["name"]
+ k = re.search(rep, content)
+ if not k:
+ return mw.returnJson(False, "获取 key {} 失败".format(k))
+ k = k.group(1)
+ v = re.search(rep, content)
+ if not v:
+ return mw.returnJson(False, "获取 value {} 失败".format(v))
+ v = v.group(2)
+
+ 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": k, "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 makeWorkerCpuAffinity(val):
+ if val == "auto":
+ return "auto"
+
+ if mw.isNumber(val):
+ core_num = int(val)
+ default_core_str = "0"*core_num
+ core_num_arr = []
+ for x in range(core_num):
+ t = replaceChar(default_core_str, x , "1")
+ core_num_arr.append(t)
+ return " ".join(core_num_arr)
+
+ return 'auto'
+
+def setCfg():
+
+ args = getArgs()
+ data = checkArgs(args, [
+ 'worker_processes', 'worker_connections', 'keepalive_timeout',
+ 'gzip', 'gzip_min_length', 'gzip_comp_level', 'client_max_body_size',
+ 'server_names_hash_bucket_size', 'client_header_buffer_size'
+ ])
+ if not data[0]:
+ return data[1]
+
+ cfg = getConf()
+ mw.backFile(cfg)
+ content = mw.readFile(cfg)
+
+ unitrep = "[kmgKMG]"
+ cfg_args = [
+ {"name": "worker_processes", "ps": "处理进程,auto表示自动,数字表示进程数", 'type': 2},
+ {"name": "worker_connections", "ps": "最大并发链接数", 'type': 2},
+ {"name": "keepalive_timeout", "ps": "连接超时时间", 'type': 2},
+ {"name": "gzip", "ps": "是否开启压缩传输", 'type': 1},
+ {"name": "gzip_min_length", "ps": "最小压缩文件", 'type': 2},
+ {"name": "gzip_comp_level", "ps": "压缩率", 'type': 2},
+ {"name": "client_max_body_size", "ps": "最大上传文件", 'type': 2},
+ {"name": "server_names_hash_bucket_size",
+ "ps": "服务器名字的hash表大小", 'type': 2},
+ {"name": "client_header_buffer_size", "ps": "客户端请求头buffer大小", 'type': 2},
+ ]
+
+ # print(args)
+ for k, v in args.items():
+ # print(k, v)
+ rep = r"%s\s+[^kKmMgG\;\n]+" % k
+ if k == "worker_processes" or k == "gzip":
+ if not re.search(r"auto|on|off|\d+", v):
+ return mw.returnJson(False, '参数值错误')
+ else:
+ if not re.search(r"\d+", v):
+ return mw.returnJson(False, '参数值错误,请输入数字整数')
+
+ if k == "worker_processes" :
+ k_wca = "worker_cpu_affinity"
+ rep_wca = r"%s\s+[^\;\n]+" % k_wca
+ v_wca = makeWorkerCpuAffinity(v)
+ newconf = "%s %s" % (k_wca, v_wca)
+ content = re.sub(rep_wca, newconf, content)
+
+
+ if re.search(rep, content):
+ newconf = "%s %s" % (k, v)
+ content = re.sub(rep, newconf, content)
+ elif re.search(rep, content):
+ newconf = "%s %s" % (k, v)
+ content = re.sub(rep, newconf, content)
+
+ mw.writeFile(cfg, content)
+ isError = mw.checkWebConfig()
+ 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 = '1.27.1'
+ 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')
diff --git a/plugins/openresty/info.json b/plugins/openresty/info.json
new file mode 100755
index 000000000..5fb252408
--- /dev/null
+++ b/plugins/openresty/info.json
@@ -0,0 +1,18 @@
+{
+ "sort": 0,
+ "title":"OpenResty",
+ "tip":"soft",
+ "name":"openresty",
+ "type":"其他插件",
+ "ps":"轻量级,占有内存少,并发能力强",
+ "shell":"install.sh",
+ "install_pre_inspection":true,
+ "checks":"server/openresty",
+ "path":"server/openresty",
+ "author":"agentzh",
+ "home":"http://openresty.org",
+ "date":"2017-11-24",
+ "pid": "1",
+ "versions": ["1.17.8","1.19.3","1.21.4","1.25.3","1.27.1","rtmp"],
+ "updates": ["1.17.8.2","1.19.3.1","1.21.4.2","1.25.3.2"]
+}
\ No newline at end of file
diff --git a/plugins/openresty/init.d/nginx.tpl b/plugins/openresty/init.d/nginx.tpl
new file mode 100644
index 000000000..d773f931a
--- /dev/null
+++ b/plugins/openresty/init.d/nginx.tpl
@@ -0,0 +1,118 @@
+#! /bin/sh
+# chkconfig: 2345 55 25
+# Description: Startup script for nginx webserver on Debian. Place in /etc/init.d and
+# run 'update-rc.d -f nginx defaults', or use the appropriate command on your
+# distro. For CentOS/Redhat run: 'chkconfig --add openresty'
+
+### BEGIN INIT INFO
+# Provides: nginx
+# Required-Start: $all
+# Required-Stop: $all
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts the nginx web server
+# Description: starts nginx using start-stop-daemon
+### END INIT INFO
+
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/homebrew/bin
+NAME=nginx
+NGINX_BIN={$SERVER_PATH}/openresty/bin/openresty
+CONFIGFILE={$SERVER_PATH}/openresty/nginx/conf/$NAME.conf
+PIDFILE={$SERVER_PATH}/openresty/nginx/logs/$NAME.pid
+
+case "$1" in
+ start)
+ echo -n "Starting $NAME... "
+ if [ -f $PIDFILE ];then
+ mPID=`cat $PIDFILE`
+ isStart=`ps ax | awk '{ print $1 }' | grep -e "^${mPID}$"`
+ if [ "$isStart" != '' ];then
+ echo "$NAME (pid `pidof $NAME`) already running."
+ exit 1
+ fi
+ fi
+
+ $NGINX_BIN -c $CONFIGFILE
+
+ if [ "$?" != 0 ] ; then
+ echo " failed"
+ exit 1
+ else
+ echo " done"
+ fi
+ ;;
+
+ stop)
+ echo -n "Stoping $NAME... "
+ if [ -f $PIDFILE ];then
+ mPID=`cat $PIDFILE`
+ isStart=`ps ax | awk '{ print $1 }' | grep -e "^${mPID}$"`
+ if [ "$isStart" = '' ];then
+ echo "$NAME is not running."
+ exit 1
+ fi
+ else
+ echo "$NAME is not running."
+ exit 1
+ fi
+ $NGINX_BIN -s stop
+
+ if [ "$?" != 0 ] ; then
+ echo " failed. Use force-quit"
+ exit 1
+ else
+ echo " done"
+ fi
+ ;;
+
+ status)
+ if [ -f $PIDFILE ];then
+ mPID=`cat $PIDFILE`
+ isStart=`ps ax | awk '{ print $1 }' | grep -e "^${mPID}$"`
+ if [ "$isStart" != '' ];then
+ echo "$NAME (pid `pidof $NAME`) already running."
+ exit 1
+ else
+ echo "$NAME is stopped"
+ exit 0
+ fi
+ else
+ echo "$NAME is stopped"
+ exit 0
+ fi
+ ;;
+ restart)
+ $0 stop
+ sleep 1
+ $0 start
+ ;;
+
+ reload)
+ echo -n "Reload service $NAME... "
+ if [ -f $PIDFILE ];then
+ mPID=`cat $PIDFILE`
+ isStart=`ps ax | awk '{ print $1 }' | grep -e "^${mPID}$"`
+ if [ "$isStart" != '' ];then
+ $NGINX_BIN -s reload
+ echo " done"
+ else
+ echo "$NAME is not running, can't reload."
+ exit 1
+ fi
+ else
+ echo "$NAME is not running, can't reload."
+ exit 1
+ fi
+ ;;
+
+ configtest)
+ echo -n "Test $NAME configure files... "
+ $NGINX_BIN -t
+ ;;
+
+ *)
+ echo "Usage: $0 {start|stop|restart|reload|status|configtest}"
+ exit 1
+ ;;
+esac
diff --git a/plugins/openresty/init.d/openresty.service.tpl b/plugins/openresty/init.d/openresty.service.tpl
new file mode 100644
index 000000000..b3369a3b8
--- /dev/null
+++ b/plugins/openresty/init.d/openresty.service.tpl
@@ -0,0 +1,14 @@
+[Unit]
+Description=OpenResty is a dynamic web platform based on NGINX and LuaJIT.
+After=network.target
+
+[Service]
+Type=forking
+ExecStart={$SERVER_PATH}/openresty/bin/openresty -c {$SERVER_PATH}/openresty/nginx/conf/nginx.conf
+ExecStop={$SERVER_PATH}/openresty/bin/openresty -s stop
+ExecReload={$SERVER_PATH}/openresty/bin/openresty -s reload
+KillMode=process
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/plugins/openresty/install.sh b/plugins/openresty/install.sh
new file mode 100755
index 000000000..66ac75122
--- /dev/null
+++ b/plugins/openresty/install.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4
+# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4
+
+# cd /www/server/mdserver-web && python3 plugins/openresty/index.py run_info
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+sysName=`uname`
+action=$1
+type=$2
+
+VERSION=$2
+openrestyDir=${serverPath}/source/openresty
+
+if id www &> /dev/null ;then
+ echo "www uid is `id -u www`"
+ echo "www shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `"
+else
+ groupadd www
+ useradd -g www -s /bin/bash www
+fi
+
+if [ "${2}" == "" ];then
+ echo '缺少安装脚本版本...'
+ exit 0
+fi
+
+if [ "${action}" == "uninstall" ];then
+ if [ -f /usr/lib/systemd/system/openresty.service ] || [ -f /lib/systemd/system/openresty.service ];then
+ systemctl stop openresty
+ rm -rf /usr/systemd/system/openresty.service
+ rm -rf /lib/systemd/system/openresty.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/openresty/init.d/openresty ];then
+ $serverPath/openresty/init.d/openresty stop
+ fi
+
+ rm -rf $serverPath/openresty
+fi
+
+sh -x $curPath/versions/$2/install.sh $1
+
+if [ "${action}" == "install" ] && [ -d $serverPath/openresty ];then
+ echo "${VERSION}" > $serverPath/openresty/version.pl
+
+ mkdir -p $serverPath/web_conf/php/conf
+ echo 'set $PHP_ENV 0;' > $serverPath/web_conf/php/conf/enable-php-00.conf
+
+ #初始化
+ cd ${rootPath} && python3 ${rootPath}/plugins/openresty/index.py start
+ cd ${rootPath} && python3 ${rootPath}/plugins/openresty/index.py initd_install
+fi
diff --git a/plugins/openresty/js/openresty.js b/plugins/openresty/js/openresty.js
new file mode 100755
index 000000000..8b538a0f8
--- /dev/null
+++ b/plugins/openresty/js/openresty.js
@@ -0,0 +1,236 @@
+function orPost(method, args, callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+ $.post('/plugins/run', {name:'openresty', func:method, args:JSON.stringify(args)}, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function orPluginService(_name, version){
+ var data = {name:_name, func:'status'}
+ if ( typeof(version) != 'undefined' ){
+ data['version'] = version;
+ } else {
+ version = '';
+ }
+
+ orPost('status', data, function(data){
+ if (data.data == 'start'){
+ orPluginSetService(_name, true, version);
+ } else {
+ orPluginSetService(_name, false, version);
+ }
+ });
+}
+
+function orPluginSetService(_name ,status, version){
+ var serviceCon ='当前状态:'+(status ? '开启' : '关闭' )+
+ '
\
+ '+(status?'停止':'启动')+' \
+ 重启 \
+ 重载配置 \
+
';
+ $(".soft-man-con").html(serviceCon);
+}
+
+
+function orPluginOpService(a, b, v,request_callback) {
+
+ var c = "name=" + a + "&func=" + b;
+ if(v != ''){
+ c = c + '&version='+v;
+ }
+
+ var d = "";
+
+ switch(b) {
+ case "stop":d = '停止';break;
+ case "start":d = '启动';break;
+ case "restart":d = '重启';break;
+ case "reload":d = '重载';break;
+ }
+ layer.confirm( msgTpl('您真的要{1}{2}{3}服务吗?', [d,a,v]), {icon:3,closeBtn: 2}, function() {
+ orPost('get_os',{},function(data){
+ var rdata = $.parseJSON(data.data);
+ if (!rdata['auth']){
+ layer.prompt({title: '检查到权限不足,需要输入密码!', formType: 1},function(pwd, index){
+
+ layer.close(index);
+ var data = {'pwd':pwd};
+ c += '&args='+JSON.stringify(data);
+ orPluginOpServiceOp(a,b,c,d,a,v,request_callback);
+ });
+ } else {
+ orPluginOpServiceOp(a,b,c,d,a,v,request_callback);
+
+ }
+ });
+ })
+}
+
+function orPluginOpServiceOp(a,b,c,d,a,v,request_callback){
+
+ var request_path = "/plugins/run";
+ if (request_callback == 'yes'){
+ request_path = "/plugins/callback";
+ }
+
+ var e = layer.msg(msgTpl('正在{1}{2}{3}服务,请稍候...',[d,a,v]), {icon: 16,time: 0});
+ $.post(request_path, c, function(g) {
+ layer.close(e);
+
+ var f = g.data == 'ok' ? msgTpl('{1}{2}服务已{3}',[a,v,d]) : msgTpl('{1}{2}服务{3}失败!',[a,v,d]);
+ layer.msg(f, {icon: g.data == 'ok' ? 1 : 2});
+
+ if( b != "reload" && g.data == 'ok' ) {
+ if ( b == 'start' ) {
+ orPluginSetService(a, true, v);
+ } else if ( b == 'stop' ){
+ orPluginSetService(a, false, v);
+ }
+ }
+
+ if( g.status && g.data != 'ok' ) {
+ layer.msg(g.data, {icon: 2,time: 10000,shade: 0.3});
+ }
+
+ },'json').error(function() {
+ layer.close(e);
+ layer.msg('操作异常!', {icon: 2});
+ });
+}
+
+
+//查看Nginx负载状态
+function getOpStatus() {
+ var loadT = layer.msg('正在处理,请稍后...', { icon: 16, time: 0, shade: 0.3 });
+ $.post('/plugins/run', {name:'openresty', func:'run_info'}, function(data) {
+ layer.close(loadT);
+ try {
+ var rdata = $.parseJSON(data.data);
+ if ('status' in rdata && !rdata.status){
+ showMsg(rdata.msg, function(){}, null,3000);
+ return;
+ }
+
+ var con = "\
+ 活动连接(Active connections) " + rdata.active + " \
+ 总连接次数(accepts) " + rdata.accepts + " \
+ 总握手次数(handled) " + rdata.handled + " \
+ 总请求数(requests) " + rdata.requests + " \
+ 请求数(Reading) " + rdata.Reading + " \
+ 响应数(Writing) " + rdata.Writing + " \
+ 驻留进程(Waiting) " + rdata.Waiting + " \
+
";
+ $(".soft-man-con").html(con);
+ }catch(err){
+ showMsg(data.data, function(){}, null,3000);
+ }
+ },'json');
+}
+
+
+function setOpCfg(){
+ orPost('get_cfg', {}, function(data){
+ var rdata = $.parseJSON(data.data);
+ var rdata = rdata.data;
+ // console.log(rdata);
+
+ var mlist = '';
+ for (var i = 0; i < rdata.length; i++) {
+ var w = '70'
+ var ibody = ' ';
+ switch (rdata[i].type) {
+ case 0:
+ var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
+ var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
+ ibody = '\
+ 开启 \
+ 关闭 \
+ ';
+ break;
+ case 1:
+ var selected_1 = (rdata[i].value == 'on') ? 'selected' : '';
+ var selected_0 = (rdata[i].value == 'off') ? 'selected' : '';
+ ibody = '\
+ 开启 \
+ 关闭 \
+ ';
+ break;
+ }
+ mlist += '' + rdata[i].name + ' ' + ibody + ""+rdata[i].unit+" " +', ' + rdata[i].ps + '
';
+ }
+ var con = '\
+ ' + mlist + '\
+
\
+ 刷新 \
+ 保存 \
+
\
+
'
+ $(".soft-man-con").html(con);
+ });
+}
+
+function submitConf() {
+ var data = {
+ worker_processes: $("input[name='worker_processes']").val(),
+ worker_connections: $("input[name='worker_connections']").val(),
+ keepalive_timeout: $("input[name='keepalive_timeout']").val(),
+ gzip: $("select[name='gzip']").val() || 'on',
+ gzip_min_length: $("input[name='gzip_min_length']").val(),
+ gzip_comp_level: $("input[name='gzip_comp_level']").val(),
+ client_max_body_size: $("input[name='client_max_body_size']").val(),
+ server_names_hash_bucket_size: $("input[name='server_names_hash_bucket_size']").val(),
+ client_header_buffer_size: $("input[name='client_header_buffer_size']").val(),
+ };
+
+ // console.log(data);
+ orPost('set_cfg', data, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ // console.log(rdata);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+function otherFunc(){
+ var con = '\
+ 添加检查任务 \
+ 删除检查任务 \
+
';
+ $(".soft-man-con").html(con);
+}
+
+function cronAddCheck(){
+ orPost('cron_add_check', {}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+function cronDelCheck(){
+ orPost('cron_del_check', {}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/openresty/tool_task.py b/plugins/openresty/tool_task.py
new file mode 100644
index 000000000..6029ad9ee
--- /dev/null
+++ b/plugins/openresty/tool_task.py
@@ -0,0 +1,124 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import json
+
+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
+from utils.crontab import crontab as MwCrontab
+
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'openresty'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getTaskConf():
+ conf = getServerDir() + "/task_config.json"
+ return conf
+
+
+def getConfigData():
+ conf = getTaskConf()
+ if os.path.exists(conf):
+ return json.loads(mw.readFile(getTaskConf()))
+ return {
+ "task_id": -1,
+ "period": "minute-n",
+ "where1": "3",
+ "hour": "0",
+ "minute": "0",
+ }
+
+
+def createBgTask():
+ removeBgTask()
+ createBgTaskByName(getPluginName())
+
+
+def createBgTaskByName(name):
+ args = getConfigData()
+ _name = "[OpenResty]检查任务"
+ res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
+ if res:
+ return True
+
+ if "task_id" in args and args["task_id"] > 0:
+ res = mw.M("crontab").field("id, name").where(
+ "id=?", (args["task_id"],)).find()
+ if res and res["id"] == args["task_id"]:
+ print("计划任务已经存在!")
+ return True
+
+ mw_dir = mw.getPanelDir()
+ cmd = '''
+mw_dir=%s
+rname=%s
+plugin_path=%s
+script_path=%s
+''' % (mw_dir, name, getServerDir(), getPluginDir())
+ cmd += 'echo "bash $script_path/check.sh"' + "\n"
+ cmd += 'cd $mw_dir && bash $script_path/check.sh' + "\n"
+
+ params = {
+ 'name': _name,
+ 'type': args['period'],
+ 'week': "",
+ 'where1': args['where1'],
+ 'hour': args['hour'],
+ 'minute': args['minute'],
+ 'save': "",
+ 'backup_to': "",
+ 'stype': "toShell",
+ 'sname': '',
+ 'sbody': cmd,
+ 'url_address': '',
+ }
+
+ task_id = MwCrontab.instance().add(params)
+ if task_id > 0:
+ args["task_id"] = task_id
+ args["name"] = name
+ mw.writeFile(getTaskConf(), json.dumps(args))
+
+
+def removeBgTask():
+ cfg = getConfigData()
+ if "task_id" in cfg and cfg["task_id"] > 0:
+ res = mw.M("crontab").field("id, name").where(
+ "id=?", (cfg["task_id"],)).find()
+ if res and res["id"] == cfg["task_id"]:
+ data = MwCrontab.instance().delete(cfg["task_id"])
+ if data["status"]:
+ cfg["task_id"] = -1
+ mw.writeFile(getTaskConf(), json.dumps(cfg))
+ return True
+ return False
+
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ action = sys.argv[1]
+ if action == "remove":
+ removeBgTask()
+ elif action == "add":
+ createBgTask()
diff --git a/plugins/openresty/versions/1.17.8/install.sh b/plugins/openresty/versions/1.17.8/install.sh
new file mode 100644
index 000000000..544c97b9f
--- /dev/null
+++ b/plugins/openresty/versions/1.17.8/install.sh
@@ -0,0 +1,168 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2
+# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+sysName=`uname`
+action=$1
+type=$2
+
+VERSION=1.17.8.2
+openrestyDir=${serverPath}/source/openresty
+
+Install_openresty()
+{
+ if [ -d $serverPath/openresty ];then
+ exit 0
+ fi
+
+ # ----- cpu start ------
+ if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+ fi
+
+ if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+ fi
+
+ MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+ if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+ else
+ cpuCore="1"
+ fi
+
+ if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+ else
+ cpuCore="1"
+ fi
+ # ----- cpu end ------
+
+ mkdir -p ${openrestyDir}
+ echo '正在安装脚本文件...'
+
+ # wget -O openresty-1.21.4.1.tar.gz https://openresty.org/download/openresty-1.21.4.1.tar.gz
+ if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz https://openresty.org/download/openresty-${VERSION}.tar.gz -T 3
+ fi
+
+ DOWNLOAD_SIZE=`wc -c ${openrestyDir}/openresty-${VERSION}.tar.gz | awk '{print $1}'`
+ if [ "$DOWNLOAD_SIZE" == "0" ];then
+ echo 'download failed, download again'
+ rm -rf ${openrestyDir}/openresty-${VERSION}.tar.gz
+ fi
+
+ # Last Download Method
+ if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz http://dl.midoks.icu/soft/openresty/openresty-${VERSION}.tar.gz -T 3
+ fi
+
+ cd ${openrestyDir} && tar -zxvf openresty-${VERSION}.tar.gz
+
+ OPTIONS=''
+
+ opensslVersion="1.1.1p"
+ libresslVersion="3.9.1"
+ pcreVersion='8.38'
+ if [ "$sysName" == "Darwin" ];then
+
+ if [ ! -f ${openrestyDir}/pcre-${pcreVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/pcre-${pcreVersion}.tar.gz https://netix.dl.sourceforge.net/project/pcre/pcre/${pcreVersion}/pcre-${pcreVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/pcre-${pcreVersion} ];then
+ cd ${openrestyDir} && tar -zxvf pcre-${pcreVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-pcre=${openrestyDir}/pcre-${pcreVersion}"
+
+
+ if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}"
+
+ # BREW_DIR=`which brew`
+ # BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ # brew info openssl@1.1 | grep /opt/homebrew/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'
+ # OPENSSL_LIB_DEPEND_DIR=`brew info openssl@1.1 | grep ${BREW_DIR}/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'`
+ # OPTIONS="${OPTIONS} --with-openssl=${OPENSSL_LIB_DEPEND_DIR}"
+ else
+ echo "openssl"
+ # if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then
+ # wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ # fi
+
+ # if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ # cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz
+ # fi
+ # OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}"
+ fi
+
+
+ # --with-openssl=$serverPath/source/lib/openssl-1.0.2q
+ cd ${openrestyDir}/openresty-${VERSION} && ./configure \
+ --prefix=$serverPath/openresty \
+ $OPTIONS \
+ --with-stream \
+ --with-http_v2_module \
+ --with-http_ssl_module \
+ --with-http_slice_module \
+ --with-http_stub_status_module \
+ --with-http_sub_module \
+ --with-http_realip_module
+ # --without-luajit-gc64
+ # --with-debug
+ # 用于调式
+
+ CMD_MAKE=`which gmake`
+ if [ "$?" == "0" ];then
+ gmake -j${cpuCore} && gmake install && gmake clean
+ else
+ make -j${cpuCore} && make install && make clean
+ fi
+
+ if [ -d ${openrestyDir}/pcre-${pcreVersion} ];then
+ rm -rf ${openrestyDir}/pcre-${pcreVersion}
+ fi
+
+ if [ -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ rm -rf ${openrestyDir}/openssl-${opensslVersion}
+ fi
+
+ if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then
+ rm -rf ${openrestyDir}/libressl-${libresslVersion}
+ fi
+
+ if [ -d $openrestyDir/openresty-${VERSION} ];then
+ rm -rf $openrestyDir/openresty-${VERSION}
+ fi
+
+ echo '安装完成'
+}
+
+Uninstall_openresty()
+{
+ echo '卸载完成'
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_openresty
+else
+ Uninstall_openresty
+fi
diff --git a/plugins/openresty/versions/1.19.3/install.sh b/plugins/openresty/versions/1.19.3/install.sh
new file mode 100644
index 000000000..2af69a9b1
--- /dev/null
+++ b/plugins/openresty/versions/1.19.3/install.sh
@@ -0,0 +1,168 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2
+# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+sysName=`uname`
+action=$1
+type=$2
+
+VERSION=1.19.3.1
+openrestyDir=${serverPath}/source/openresty
+
+Install_openresty()
+{
+ if [ -d $serverPath/openresty ];then
+ exit 0
+ fi
+
+ # ----- cpu start ------
+ if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+ fi
+
+ if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+ fi
+
+ MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+ if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+ else
+ cpuCore="1"
+ fi
+
+ if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+ else
+ cpuCore="1"
+ fi
+ # ----- cpu end ------
+
+ mkdir -p ${openrestyDir}
+ echo '正在安装脚本文件...'
+
+ # wget -O openresty-1.21.4.1.tar.gz https://openresty.org/download/openresty-1.21.4.1.tar.gz
+ if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz https://openresty.org/download/openresty-${VERSION}.tar.gz -T 3
+ fi
+
+ DOWNLOAD_SIZE=`wc -c ${openrestyDir}/openresty-${VERSION}.tar.gz | awk '{print $1}'`
+ if [ "$DOWNLOAD_SIZE" == "0" ];then
+ echo 'download failed, download again'
+ rm -rf ${openrestyDir}/openresty-${VERSION}.tar.gz
+ fi
+
+ # Last Download Method
+ if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz http://dl.midoks.icu/soft/openresty/openresty-${VERSION}.tar.gz -T 3
+ fi
+
+ cd ${openrestyDir} && tar -zxvf openresty-${VERSION}.tar.gz
+
+ OPTIONS=''
+ OPTIONS="${OPTIONS} --with-ipv6"
+
+ opensslVersion="1.1.1p"
+ libresslVersion="3.9.1"
+ pcreVersion='8.38'
+ if [ "$sysName" == "Darwin" ];then
+
+ if [ ! -f ${openrestyDir}/pcre-${pcreVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/pcre-${pcreVersion}.tar.gz https://netix.dl.sourceforge.net/project/pcre/pcre/${pcreVersion}/pcre-${pcreVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/pcre-${pcreVersion} ];then
+ cd ${openrestyDir} && tar -zxvf pcre-${pcreVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-pcre=${openrestyDir}/pcre-${pcreVersion}"
+
+
+ if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}"
+
+ # BREW_DIR=`which brew`
+ # BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ # brew info openssl@1.1 | grep /opt/homebrew/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'
+ # OPENSSL_LIB_DEPEND_DIR=`brew info openssl@1.1 | grep ${BREW_DIR}/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'`
+ # OPTIONS="${OPTIONS} --with-openssl=${OPENSSL_LIB_DEPEND_DIR}"
+ else
+ echo "openssl"
+ # if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then
+ # wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ # fi
+
+ # if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ # cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz
+ # fi
+ # OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}"
+ fi
+
+
+ # --with-openssl=$serverPath/source/lib/openssl-1.0.2q
+ cd ${openrestyDir}/openresty-${VERSION} && ./configure \
+ --prefix=$serverPath/openresty \
+ $OPTIONS \
+ --with-stream \
+ --with-http_v2_module \
+ --with-http_ssl_module \
+ --with-http_slice_module \
+ --with-http_stub_status_module \
+ --with-http_sub_module \
+ --with-http_realip_module
+ # --without-luajit-gc64
+ # --with-debug
+ # 用于调式
+
+ CMD_MAKE=`which gmake`
+ if [ "$?" == "0" ];then
+ gmake -j${cpuCore} && gmake install && gmake clean
+ else
+ make -j${cpuCore} && make install && make clean
+ fi
+
+ if [ -d ${openrestyDir}/pcre-${pcreVersion} ];then
+ rm -rf ${openrestyDir}/pcre-${pcreVersion}
+ fi
+
+ if [ -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ rm -rf ${openrestyDir}/openssl-${opensslVersion}
+ fi
+
+ if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then
+ rm -rf ${openrestyDir}/libressl-${libresslVersion}
+ fi
+
+ if [ -d $openrestyDir/openresty-${VERSION} ];then
+ rm -rf $openrestyDir/openresty-${VERSION}
+ fi
+ echo '安装完成'
+}
+
+Uninstall_openresty()
+{
+ echo '卸载完成'
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_openresty
+else
+ Uninstall_openresty
+fi
diff --git a/plugins/openresty/versions/1.21.4/install.sh b/plugins/openresty/versions/1.21.4/install.sh
new file mode 100644
index 000000000..0ceaa3a91
--- /dev/null
+++ b/plugins/openresty/versions/1.21.4/install.sh
@@ -0,0 +1,168 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2
+# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+sysName=`uname`
+action=$1
+type=$2
+
+VERSION=1.21.4.4
+
+openrestyDir=${serverPath}/source/openresty
+
+Install_openresty()
+{
+ if [ -d $serverPath/openresty ];then
+ exit 0
+ fi
+
+ # ----- cpu start ------
+ if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+ fi
+
+ if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+ fi
+
+ MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+ if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+ else
+ cpuCore="1"
+ fi
+
+ if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+ else
+ cpuCore="1"
+ fi
+ # ----- cpu end ------
+
+ mkdir -p ${openrestyDir}
+ echo '正在安装脚本文件...'
+
+ # wget -O openresty-1.21.4.1.tar.gz https://openresty.org/download/openresty-1.21.4.1.tar.gz
+ if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz https://openresty.org/download/openresty-${VERSION}.tar.gz -T 3
+ fi
+
+ DOWNLOAD_SIZE=`wc -c ${openrestyDir}/openresty-${VERSION}.tar.gz | awk '{print $1}'`
+ if [ "$DOWNLOAD_SIZE" == "0" ];then
+ echo 'download failed, download again'
+ rm -rf ${openrestyDir}/openresty-${VERSION}.tar.gz
+ fi
+
+ # Last Download Method
+ if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz http://dl.midoks.icu/soft/openresty/openresty-${VERSION}.tar.gz -T 3
+ fi
+
+ cd ${openrestyDir} && tar -zxvf openresty-${VERSION}.tar.gz
+
+ OPTIONS=''
+
+ opensslVersion="1.1.1p"
+ libresslVersion="3.9.1"
+ pcreVersion='8.38'
+ if [ "$sysName" == "Darwin" ];then
+
+ if [ ! -f ${openrestyDir}/pcre-${pcreVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/pcre-${pcreVersion}.tar.gz https://netix.dl.sourceforge.net/project/pcre/pcre/${pcreVersion}/pcre-${pcreVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/pcre-${pcreVersion} ];then
+ cd ${openrestyDir} && tar -zxvf pcre-${pcreVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-pcre=${openrestyDir}/pcre-${pcreVersion}"
+
+
+ if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}"
+
+ # BREW_DIR=`which brew`
+ # BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ # brew info openssl@1.1 | grep /opt/homebrew/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'
+ # OPENSSL_LIB_DEPEND_DIR=`brew info openssl@1.1 | grep ${BREW_DIR}/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'`
+ # OPTIONS="${OPTIONS} --with-openssl=${OPENSSL_LIB_DEPEND_DIR}"
+ else
+ if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}"
+ fi
+
+
+ # --with-openssl=$serverPath/source/lib/openssl-1.0.2q
+ cd ${openrestyDir}/openresty-${VERSION} && ./configure \
+ --prefix=$serverPath/openresty \
+ $OPTIONS \
+ --with-stream \
+ --with-http_v2_module \
+ --with-http_ssl_module \
+ --with-http_slice_module \
+ --with-http_stub_status_module \
+ --with-http_sub_module \
+ --with-http_realip_module
+ # --without-luajit-gc64
+ # --with-debug
+ # 用于调式
+
+ CMD_MAKE=`which gmake`
+ if [ "$?" == "0" ];then
+ gmake -j${cpuCore} && gmake install && gmake clean
+ else
+ make -j${cpuCore} && make install && make clean
+ fi
+
+
+ if [ -d ${openrestyDir}/pcre-${pcreVersion} ];then
+ rm -rf ${openrestyDir}/pcre-${pcreVersion}
+ fi
+
+ if [ -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ rm -rf ${openrestyDir}/openssl-${opensslVersion}
+ fi
+
+ if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then
+ rm -rf ${openrestyDir}/libressl-${libresslVersion}
+ fi
+
+ if [ -d $openrestyDir/openresty-${VERSION} ];then
+ rm -rf $openrestyDir/openresty-${VERSION}
+ fi
+ echo '安装完成'
+}
+
+Uninstall_openresty()
+{
+ echo '卸载完成'
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_openresty
+else
+ Uninstall_openresty
+fi
diff --git a/plugins/openresty/versions/1.25.3/install.sh b/plugins/openresty/versions/1.25.3/install.sh
new file mode 100644
index 000000000..0becee2f0
--- /dev/null
+++ b/plugins/openresty/versions/1.25.3/install.sh
@@ -0,0 +1,183 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2
+# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+sysName=`uname`
+action=$1
+type=$2
+
+VERSION=1.25.3.2
+
+openrestyDir=${serverPath}/source/openresty
+
+Install_openresty()
+{
+ if [ -d $serverPath/openresty ];then
+ exit 0
+ fi
+
+ # ----- cpu start ------
+ if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+ fi
+
+ if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+ fi
+
+ MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+ if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+ else
+ cpuCore="1"
+ fi
+
+ if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+ else
+ cpuCore="1"
+ fi
+ # ----- cpu end ------
+
+ mkdir -p ${openrestyDir}
+ echo '正在安装脚本文件...'
+
+ # wget -O openresty-1.21.4.1.tar.gz https://openresty.org/download/openresty-1.21.4.1.tar.gz
+ if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz https://openresty.org/download/openresty-${VERSION}.tar.gz -T 3
+ fi
+
+ DOWNLOAD_SIZE=`wc -c ${openrestyDir}/openresty-${VERSION}.tar.gz | awk '{print $1}'`
+ if [ "$DOWNLOAD_SIZE" == "0" ];then
+ echo 'download failed, download again'
+ rm -rf ${openrestyDir}/openresty-${VERSION}.tar.gz
+ fi
+
+ # Last Download Method
+ if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz http://dl.midoks.icu/soft/openresty/openresty-${VERSION}.tar.gz -T 3
+ fi
+
+ cd ${openrestyDir} && tar -zxvf openresty-${VERSION}.tar.gz
+
+ OPTIONS=''
+
+ opensslVersion="1.1.1p"
+ libresslVersion="3.9.1"
+ pcreVersion='8.38'
+ if [ "$sysName" == "Darwin" ];then
+
+ if [ ! -f ${openrestyDir}/pcre-${pcreVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/pcre-${pcreVersion}.tar.gz https://netix.dl.sourceforge.net/project/pcre/pcre/${pcreVersion}/pcre-${pcreVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/pcre-${pcreVersion} ];then
+ cd ${openrestyDir} && tar -zxvf pcre-${pcreVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-pcre=${openrestyDir}/pcre-${pcreVersion}"
+
+
+ if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}"
+
+ # BREW_DIR=`which brew`
+ # BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ # brew info openssl@1.1 | grep /opt/homebrew/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'
+ # OPENSSL_LIB_DEPEND_DIR=`brew info openssl@1.1 | grep ${BREW_DIR}/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'`
+ # OPTIONS="${OPTIONS} --with-openssl=${OPENSSL_LIB_DEPEND_DIR}"
+ else
+ if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}"
+
+ fi
+
+ if [[ "$VERSION" =~ "1.25.3" ]]; then
+ OPTIONS="${OPTIONS} --with-http_v3_module"
+
+ if [ ! -f ${openrestyDir}/libressl-${libresslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/libressl-${libresslVersion}.tar.gz https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${libresslVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/libressl-${libresslVersion} ];then
+ cd ${openrestyDir} && tar -zxvf libressl-${libresslVersion}.tar.gz
+ fi
+
+ OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/include"
+ OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/lib"
+ fi
+
+
+ cd ${openrestyDir}/openresty-${VERSION} && ./configure \
+ --prefix=$serverPath/openresty \
+ $OPTIONS \
+ --with-stream \
+ --with-http_v2_module \
+ --with-http_ssl_module \
+ --with-http_slice_module \
+ --with-http_stub_status_module \
+ --with-http_sub_module \
+ --with-http_realip_module
+ # --without-luajit-gc64
+ # --with-debug
+ # 用于调式
+
+ CMD_MAKE=`which gmake`
+ if [ "$?" == "0" ];then
+ gmake -j${cpuCore} && gmake install && gmake clean
+ else
+ make -j${cpuCore} && make install && make clean
+ fi
+
+
+ if [ -d ${openrestyDir}/pcre-${pcreVersion} ];then
+ rm -rf ${openrestyDir}/pcre-${pcreVersion}
+ fi
+
+ if [ -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ rm -rf ${openrestyDir}/openssl-${opensslVersion}
+ fi
+
+ if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then
+ rm -rf ${openrestyDir}/libressl-${libresslVersion}
+ fi
+
+ if [ -d $openrestyDir/openresty-${VERSION} ];then
+ rm -rf $openrestyDir/openresty-${VERSION}
+ fi
+ echo '安装完成'
+}
+
+Uninstall_openresty()
+{
+ echo '卸载完成'
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_openresty
+else
+ Uninstall_openresty
+fi
diff --git a/plugins/openresty/versions/1.27.1/install.sh b/plugins/openresty/versions/1.27.1/install.sh
new file mode 100644
index 000000000..42704f27c
--- /dev/null
+++ b/plugins/openresty/versions/1.27.1/install.sh
@@ -0,0 +1,183 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2
+# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.27.1
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+sysName=`uname`
+action=$1
+type=$2
+
+VERSION=1.27.1.2
+
+openrestyDir=${serverPath}/source/openresty
+
+Install_openresty()
+{
+ if [ -d $serverPath/openresty ];then
+ exit 0
+ fi
+
+ # ----- cpu start ------
+ if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+ fi
+
+ if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+ fi
+
+ MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+ if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+ else
+ cpuCore="1"
+ fi
+
+ if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+ else
+ cpuCore="1"
+ fi
+ # ----- cpu end ------
+
+ mkdir -p ${openrestyDir}
+ echo '正在安装脚本文件...'
+
+ # wget -O openresty-1.21.4.1.tar.gz https://openresty.org/download/openresty-1.21.4.1.tar.gz
+ if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz https://openresty.org/download/openresty-${VERSION}.tar.gz -T 3
+ fi
+
+ DOWNLOAD_SIZE=`wc -c ${openrestyDir}/openresty-${VERSION}.tar.gz | awk '{print $1}'`
+ if [ "$DOWNLOAD_SIZE" == "0" ];then
+ echo 'download failed, download again'
+ rm -rf ${openrestyDir}/openresty-${VERSION}.tar.gz
+ fi
+
+ # Last Download Method
+ if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz http://dl.midoks.icu/soft/openresty/openresty-${VERSION}.tar.gz -T 3
+ fi
+
+ cd ${openrestyDir} && tar -zxvf openresty-${VERSION}.tar.gz
+
+ OPTIONS=''
+
+ opensslVersion="1.1.1p"
+ libresslVersion="3.9.1"
+ pcreVersion='8.38'
+ if [ "$sysName" == "Darwin" ];then
+
+ if [ ! -f ${openrestyDir}/pcre-${pcreVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/pcre-${pcreVersion}.tar.gz https://netix.dl.sourceforge.net/project/pcre/pcre/${pcreVersion}/pcre-${pcreVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/pcre-${pcreVersion} ];then
+ cd ${openrestyDir} && tar -zxvf pcre-${pcreVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-pcre=${openrestyDir}/pcre-${pcreVersion}"
+
+
+ if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}"
+
+ # BREW_DIR=`which brew`
+ # BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ # brew info openssl@1.1 | grep /opt/homebrew/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'
+ # OPENSSL_LIB_DEPEND_DIR=`brew info openssl@1.1 | grep ${BREW_DIR}/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'`
+ # OPTIONS="${OPTIONS} --with-openssl=${OPENSSL_LIB_DEPEND_DIR}"
+ else
+ if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}"
+
+ fi
+
+ if [[ "$VERSION" =~ "1.25.3" ]] || [[ "$VERSION" =~ "1.27.1" ]];then
+ OPTIONS="${OPTIONS} --with-http_v3_module"
+
+ if [ ! -f ${openrestyDir}/libressl-${libresslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/libressl-${libresslVersion}.tar.gz https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${libresslVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/libressl-${libresslVersion} ];then
+ cd ${openrestyDir} && tar -zxvf libressl-${libresslVersion}.tar.gz
+ fi
+
+ OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/include"
+ OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/lib"
+ fi
+
+
+ cd ${openrestyDir}/openresty-${VERSION} && ./configure \
+ --prefix=$serverPath/openresty \
+ $OPTIONS \
+ --with-stream \
+ --with-http_v2_module \
+ --with-http_ssl_module \
+ --with-http_slice_module \
+ --with-http_stub_status_module \
+ --with-http_sub_module \
+ --with-http_realip_module
+ # --without-luajit-gc64
+ # --with-debug
+ # 用于调式
+
+ CMD_MAKE=`which gmake`
+ if [ "$?" == "0" ];then
+ gmake -j${cpuCore} && gmake install && gmake clean
+ else
+ make -j${cpuCore} && make install && make clean
+ fi
+
+
+ if [ -d ${openrestyDir}/pcre-${pcreVersion} ];then
+ rm -rf ${openrestyDir}/pcre-${pcreVersion}
+ fi
+
+ if [ -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ rm -rf ${openrestyDir}/openssl-${opensslVersion}
+ fi
+
+ if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then
+ rm -rf ${openrestyDir}/libressl-${libresslVersion}
+ fi
+
+ if [ -d $openrestyDir/openresty-${VERSION} ];then
+ rm -rf $openrestyDir/openresty-${VERSION}
+ fi
+ echo 'Installation of Openresty completed'
+}
+
+Uninstall_openresty()
+{
+ echo 'Uninstalling Openresty completed'
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_openresty
+else
+ Uninstall_openresty
+fi
diff --git a/plugins/openresty/versions/1.29.2/install.sh b/plugins/openresty/versions/1.29.2/install.sh
new file mode 100644
index 000000000..bdfb1f969
--- /dev/null
+++ b/plugins/openresty/versions/1.29.2/install.sh
@@ -0,0 +1,220 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.29.2
+# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.29.2
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+sysName=`uname`
+action=$1
+type=$2
+
+VERSION=1.29.2.3
+
+openrestyDir=${serverPath}/source/openresty
+
+Install_openresty()
+{
+ if [ "${action}" == "install" ];then
+ if [ -d $serverPath/openresty ];then
+ exit 0
+ fi
+ fi
+
+ # ----- cpu start ------
+ if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+ fi
+
+ if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+ fi
+
+ MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+ if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+ else
+ cpuCore="1"
+ fi
+
+ if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+ else
+ cpuCore="1"
+ fi
+ # ----- cpu end ------
+
+ mkdir -p ${openrestyDir}
+ echo '正在安装脚本文件...'
+
+ # wget -O openresty-1.21.4.1.tar.gz https://openresty.org/download/openresty-1.21.4.1.tar.gz
+ if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz https://openresty.org/download/openresty-${VERSION}.tar.gz -T 3
+ fi
+
+ DOWNLOAD_SIZE=`wc -c ${openrestyDir}/openresty-${VERSION}.tar.gz | awk '{print $1}'`
+ if [ "$DOWNLOAD_SIZE" == "0" ];then
+ echo 'download failed, download again'
+ rm -rf ${openrestyDir}/openresty-${VERSION}.tar.gz
+ fi
+
+ # Last Download Method
+ if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz http://dl.midoks.icu/soft/openresty/openresty-${VERSION}.tar.gz -T 3
+ fi
+
+ cd ${openrestyDir} && tar -zxvf openresty-${VERSION}.tar.gz
+
+ OPTIONS=''
+
+ opensslVersion="3.5.5"
+ libresslVersion="3.9.1"
+ pcreVersion='8.45'
+ if [ "$sysName" == "Darwin" ];then
+
+ if [ ! -f ${openrestyDir}/pcre-${pcreVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/pcre-${pcreVersion}.tar.gz https://netix.dl.sourceforge.net/project/pcre/pcre/${pcreVersion}/pcre-${pcreVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/pcre-${pcreVersion} ];then
+ cd ${openrestyDir} && tar -zxvf pcre-${pcreVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-pcre=${openrestyDir}/pcre-${pcreVersion}"
+
+
+ if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}"
+
+ # BREW_DIR=`which brew`
+ # BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ # brew info openssl@1.1 | grep /opt/homebrew/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'
+ # OPENSSL_LIB_DEPEND_DIR=`brew info openssl@1.1 | grep ${BREW_DIR}/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'`
+ # OPTIONS="${OPTIONS} --with-openssl=${OPENSSL_LIB_DEPEND_DIR}"
+ else
+ if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}"
+
+ fi
+
+ if [[ "$VERSION" =~ "1.29.2" ]];then
+ OPTIONS="${OPTIONS} --with-http_v3_module"
+
+ # if [ ! -f ${openrestyDir}/libressl-${libresslVersion}.tar.gz ];then
+ # wget --no-check-certificate -O ${openrestyDir}/libressl-${libresslVersion}.tar.gz https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${libresslVersion}.tar.gz
+ # fi
+
+ # if [ ! -d ${openrestyDir}/libressl-${libresslVersion} ];then
+ # cd ${openrestyDir} && tar -zxvf libressl-${libresslVersion}.tar.gz
+ # fi
+
+ # OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/include"
+ # OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/lib"
+ fi
+
+ # br
+ if [ ! -d ${openrestyDir}/openresty-${VERSION}/ngx_brotli ];then
+ cd ${openrestyDir}/openresty-${VERSION} && git clone https://github.com/wxx9248/ngx_brotli.git
+ cd ${openrestyDir}/openresty-${VERSION}/ngx_brotli && git submodule update --init
+
+ OPTIONS="${OPTIONS} --add-module=${openrestyDir}/openresty-${VERSION}/ngx_brotli"
+ fi
+
+ OPTIONS="${OPTIONS} --with-threads"
+ OPTIONS="${OPTIONS} --with-file-aio"
+ OPTIONS="${OPTIONS} --with-pcre-jit"
+ OPTIONS="${OPTIONS} --with-http_gzip_static_module"
+
+ #zstd
+ if [ ! -d ${openrestyDir}/zstd-nginx-module ];then
+ cd ${openrestyDir} && wget -O $openrestyDir/zstd-nginx-module.tar.gz https://github.com/tokers/zstd-nginx-module/archive/refs/heads/master.tar.gz
+ cd ${openrestyDir} && tar -zxvf zstd-nginx-module.tar.gz
+
+ pkg-config --exists --print-errors libzstd
+ if [ "$?" == "0" ];then
+ OPTIONS="${OPTIONS} --add-module=${openrestyDir}/zstd-nginx-module-master"
+ fi
+ fi
+
+
+
+ cd ${openrestyDir}/openresty-${VERSION} && ./configure \
+ --prefix=$serverPath/openresty \
+ $OPTIONS \
+ --with-stream \
+ --with-http_v2_module \
+ --with-http_ssl_module \
+ --with-http_slice_module \
+ --with-http_stub_status_module \
+ --with-http_sub_module \
+ --with-http_realip_module
+ # --without-luajit-gc64
+ # --with-debug
+ # 用于调式
+
+ CMD_MAKE=`which gmake`
+ if [ "$?" == "0" ];then
+ gmake -j${cpuCore} && gmake install && gmake clean
+ else
+ make -j${cpuCore} && make install && make clean
+ fi
+
+
+ if [ -d ${openrestyDir}/pcre-${pcreVersion} ];then
+ rm -rf ${openrestyDir}/pcre-${pcreVersion}
+ fi
+
+ if [ -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ rm -rf ${openrestyDir}/openssl-${opensslVersion}
+ fi
+
+ if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then
+ rm -rf ${openrestyDir}/libressl-${libresslVersion}
+ fi
+
+ if [ -d $openrestyDir/openresty-${VERSION} ];then
+ rm -rf $openrestyDir/openresty-${VERSION}
+ fi
+
+ if [ -d $openrestyDir/zstd-nginx-module-master ];then
+ rm -rf $openrestyDir/zstd-nginx-module-master
+ fi
+
+ # if [ -d $openrestyDir/ngx_brotli ];then
+ # rm -rf $openrestyDir/ngx_brotli
+ # fi
+ echo 'Installation of Openresty completed'
+}
+
+Uninstall_openresty()
+{
+ echo 'Uninstalling Openresty completed'
+}
+
+action=$1
+if [ "${1}" == "install" ];then
+ Install_openresty
+elif [ "${1}" == "upgrade" ];then
+ Install_openresty
+else
+ Uninstall_openresty
+fi
diff --git a/plugins/openresty/versions/rtmp/install.sh b/plugins/openresty/versions/rtmp/install.sh
new file mode 100644
index 000000000..11a8f5d88
--- /dev/null
+++ b/plugins/openresty/versions/rtmp/install.sh
@@ -0,0 +1,199 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.27.1.1
+# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.27.1.1
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+sysName=`uname`
+action=$1
+type=$2
+
+VERSION=1.27.1.1
+
+openrestyDir=${serverPath}/source/openresty
+
+Install_openresty()
+{
+ if [ -d $serverPath/openresty ];then
+ exit 0
+ fi
+
+ # ----- cpu start ------
+ if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+ fi
+
+ if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+ fi
+
+ MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+ if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+ else
+ cpuCore="1"
+ fi
+
+ if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+ else
+ cpuCore="1"
+ fi
+ # ----- cpu end ------
+
+ mkdir -p ${openrestyDir}
+ echo '正在安装脚本文件...'
+
+ # wget -O openresty-1.21.4.1.tar.gz https://openresty.org/download/openresty-1.21.4.1.tar.gz
+ if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz https://openresty.org/download/openresty-${VERSION}.tar.gz -T 3
+ fi
+
+ DOWNLOAD_SIZE=`wc -c ${openrestyDir}/openresty-${VERSION}.tar.gz | awk '{print $1}'`
+ if [ "$DOWNLOAD_SIZE" == "0" ];then
+ echo 'download failed, download again'
+ rm -rf ${openrestyDir}/openresty-${VERSION}.tar.gz
+ fi
+
+ # Last Download Method
+ if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz http://dl.midoks.icu/soft/openresty/openresty-${VERSION}.tar.gz -T 3
+ fi
+
+ cd ${openrestyDir} && tar -zxvf openresty-${VERSION}.tar.gz
+
+ OPTIONS=''
+
+ opensslVersion="1.1.1p"
+ libresslVersion="3.9.1"
+ pcreVersion='8.38'
+ if [ "$sysName" == "Darwin" ];then
+
+ if [ ! -f ${openrestyDir}/pcre-${pcreVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/pcre-${pcreVersion}.tar.gz https://netix.dl.sourceforge.net/project/pcre/pcre/${pcreVersion}/pcre-${pcreVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/pcre-${pcreVersion} ];then
+ cd ${openrestyDir} && tar -zxvf pcre-${pcreVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-pcre=${openrestyDir}/pcre-${pcreVersion}"
+
+
+ if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}"
+
+ # BREW_DIR=`which brew`
+ # BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ # brew info openssl@1.1 | grep /opt/homebrew/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'
+ # OPENSSL_LIB_DEPEND_DIR=`brew info openssl@1.1 | grep ${BREW_DIR}/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'`
+ # OPTIONS="${OPTIONS} --with-openssl=${OPENSSL_LIB_DEPEND_DIR}"
+ else
+ if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}"
+
+ fi
+
+ if [[ "$VERSION" =~ "1.25.3" ]]; then
+ OPTIONS="${OPTIONS} --with-http_v3_module"
+
+ if [ ! -f ${openrestyDir}/libressl-${libresslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/libressl-${libresslVersion}.tar.gz https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${libresslVersion}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/libressl-${libresslVersion} ];then
+ cd ${openrestyDir} && tar -zxvf libressl-${libresslVersion}.tar.gz
+ fi
+
+ OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/include"
+ OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/lib"
+ fi
+
+ # rtmp推流功能
+ nginx_rtmp_ver=1.2.2
+ if [ ! -f ${openrestyDir}/nginx-rtmp-module.tar.gz ];then
+ wget --no-check-certificate -O ${openrestyDir}/nginx-rtmp-module.tar.gz https://github.com/arut/nginx-rtmp-module/archive/refs/tags/v${nginx_rtmp_ver}.tar.gz
+ fi
+
+ if [ ! -d ${openrestyDir}/nginx-rtmp-module.tar.gz ];then
+ cd ${openrestyDir} && tar -zxvf nginx-rtmp-module.tar.gz
+ fi
+ OPTIONS="${OPTIONS} --add-module=${openrestyDir}/nginx-rtmp-module-${nginx_rtmp_ver}"
+
+
+ cd ${openrestyDir}/openresty-${VERSION} && ./configure \
+ --prefix=$serverPath/openresty \
+ $OPTIONS \
+ --with-stream \
+ --with-http_v2_module \
+ --with-http_ssl_module \
+ --with-http_slice_module \
+ --with-http_stub_status_module \
+ --with-http_sub_module \
+ --with-http_realip_module
+ # --without-luajit-gc64
+ # --with-debug
+ # 用于调式
+
+ CMD_MAKE=`which gmake`
+ if [ "$?" == "0" ];then
+ gmake -j${cpuCore} && gmake install && gmake clean
+ else
+ make -j${cpuCore} && make install && make clean
+ fi
+
+
+ if [ -d ${openrestyDir}/pcre-${pcreVersion} ];then
+ rm -rf ${openrestyDir}/pcre-${pcreVersion}
+ fi
+
+ if [ -d ${openrestyDir}/openssl-${opensslVersion} ];then
+ rm -rf ${openrestyDir}/openssl-${opensslVersion}
+ fi
+
+ if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then
+ rm -rf ${openrestyDir}/libressl-${libresslVersion}
+ fi
+
+ if [ -d ${openrestyDir}/nginx-rtmp-module-${nginx_rtmp_ver} ];then
+ rm -rf ${openrestyDir}/nginx-rtmp-module-${nginx_rtmp_ver}
+ fi
+
+ if [ -d $openrestyDir/openresty-${VERSION} ];then
+ rm -rf $openrestyDir/openresty-${VERSION}
+ fi
+
+ echo '安装完成'
+}
+
+Uninstall_openresty()
+{
+ echo '卸载完成'
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_openresty
+else
+ Uninstall_openresty
+fi
diff --git a/plugins/pgadmin/conf/config_local.py b/plugins/pgadmin/conf/config_local.py
new file mode 100644
index 000000000..fa416ed04
--- /dev/null
+++ b/plugins/pgadmin/conf/config_local.py
@@ -0,0 +1,6 @@
+LOG_FILE = '{$DATA_PATH}/pgadmin4/pgadmin4.log'
+SQLITE_PATH = '{$DATA_PATH}/pgadmin4/pgadmin4.db'
+SESSION_DB_PATH = '{$DATA_PATH}/pgadmin4/sessions'
+STORAGE_DIR = '{$DATA_PATH}/pgadmin4/storage'
+AZURE_CREDENTIAL_CACHE_DIR = '{$DATA_PATH}/pgadmin4/azurecredentialcache'
+SERVER_MODE = True
\ No newline at end of file
diff --git a/plugins/pgadmin/conf/pgadmin.conf b/plugins/pgadmin/conf/pgadmin.conf
new file mode 100755
index 000000000..3d30beb94
--- /dev/null
+++ b/plugins/pgadmin/conf/pgadmin.conf
@@ -0,0 +1,21 @@
+server
+{
+ listen 5051;
+ server_name 127.0.0.1;
+ index index.html index.htm index.php;
+ root {$SERVER_PATH}/pgadmin;
+
+ #error_page 404 /404.html;
+
+ #AUTH_START
+ auth_basic "Authorization";
+ auth_basic_user_file {$SERVER_PATH}/pgadmin/pg.pass;
+ #AUTH_END
+
+ location / {
+ proxy_pass http://unix:/tmp/pgadmin4.sock;
+ }
+
+ access_log {$SERVER_PATH}/pgadmin/access.log;
+ error_log {$SERVER_PATH}/pgadmin/error.log;
+}
\ No newline at end of file
diff --git a/plugins/pgadmin/ico.png b/plugins/pgadmin/ico.png
new file mode 100644
index 000000000..7749e148d
Binary files /dev/null and b/plugins/pgadmin/ico.png differ
diff --git a/plugins/pgadmin/index.html b/plugins/pgadmin/index.html
new file mode 100755
index 000000000..be24cd8d6
--- /dev/null
+++ b/plugins/pgadmin/index.html
@@ -0,0 +1,22 @@
+
+
\ No newline at end of file
diff --git a/plugins/pgadmin/index.py b/plugins/pgadmin/index.py
new file mode 100755
index 000000000..fd973ebff
--- /dev/null
+++ b/plugins/pgadmin/index.py
@@ -0,0 +1,422 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+
+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 'pgadmin'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+
+ 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 getConf():
+ return mw.getServerDir() + '/web_conf/nginx/vhost/pgadmin.conf'
+
+
+def getPort():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'listen\s*(.*);'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+
+
+def getHomePage():
+ try:
+ port = getPort()
+ ip = '127.0.0.1'
+ if not mw.isAppleSystem():
+ ip = mw.getLocalIp()
+
+ cfg = getCfg()
+ auth = cfg['username']+':'+cfg['password']
+ url = 'http://' + auth + '@' + ip + ':' + port + '/'
+ return mw.returnJson(True, 'OK', url)
+ except Exception as e:
+ return mw.returnJson(False, '插件未启动!')
+
+
+
+def contentReplace(content):
+ cfg = getCfg()
+ service_path = mw.getServerDir()
+
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$APP_PATH}', service_path+'/'+getPluginName()+'/data')
+
+ port = cfg["port"]
+ rep = r'listen\s*(.*);'
+ content = re.sub(rep, "listen " + port + ';', content)
+ return content
+
+
+def initCfg():
+ cfg = getServerDir() + "/cfg.json"
+ if not os.path.exists(cfg):
+ data = {}
+ data['port'] = '5051'
+ data['path'] = ''
+ data['username'] = 'admin'
+ data['password'] = 'admin'
+
+ data['web_pg_username'] = 'mdserver-web@gmail.com'
+ data['web_pg_password'] = 'admin'
+ mw.writeFile(cfg, json.dumps(data))
+
+
+def setCfg(key, val):
+ cfg = getServerDir() + "/cfg.json"
+ data = mw.readFile(cfg)
+ data = json.loads(data)
+ data[key] = val
+ mw.writeFile(cfg, json.dumps(data))
+
+
+def getCfg():
+ cfg = getServerDir() + "/cfg.json"
+ data = mw.readFile(cfg)
+ data = json.loads(data)
+ return data
+
+
+def returnCfg():
+ cfg = getServerDir() + "/cfg.json"
+ data = mw.readFile(cfg)
+ return data
+
+
+def __release_port(port):
+ from collections import namedtuple
+ try:
+ from utils.firewall import Firewall as MwFirewall
+ MwFirewall.instance().addAcceptPort(port, 'pgAdmin默认端口', 'port')
+ return port
+ except Exception as e:
+ return "Release failed {}".format(e)
+
+
+def __delete_port(port):
+ from collections import namedtuple
+ try:
+ from utils.firewall import Firewall as MwFirewall
+ MwFirewall.instance().delAcceptPort(port, 'tcp')
+ return port
+ except Exception as e:
+ return "Release failed {}".format(e)
+
+
+def openPort():
+ conf = getCfg()
+ port = conf['port']
+ for i in [port]:
+ __release_port(i)
+ return True
+
+
+def delPort():
+ conf = getCfg()
+ port = conf['port']
+ for i in [port]:
+ __delete_port(i)
+ return True
+
+
+def cleanNginxLog():
+ log_a = accessLog()
+ log_e = errorLog()
+
+ for i in [log_a, log_e]:
+ if os.path.exists(i):
+ cmd = "echo '' > " + i
+ mw.execShell(cmd)
+
+def getPythonName():
+ cmd = "ls /www/server/pgadmin/run/lib/ | grep python | cut -d \\ -f 1 | awk 'END {print}'"
+ data = mw.execShell(cmd)
+ return data[0].strip();
+
+def initPgConfFile():
+ pyname = getPythonName()
+ file_tpl = getPluginDir() + '/conf/config_local.py'
+ dst_file = getServerDir()+'/run/lib/'+pyname+'/site-packages/pgadmin4/config_local.py'
+ if not os.path.exists(dst_file):
+ service_path = mw.getServerDir()
+ content = mw.readFile(file_tpl)
+ content = content.replace('{$DATA_PATH}', service_path+'/'+getPluginName()+'/data')
+ mw.writeFile(dst_file, content)
+
+
+def initReplace():
+ initPgConfFile()
+
+ file_tpl = getPluginDir() + '/conf/pgadmin.conf'
+ file_run = getConf()
+ if not os.path.exists(file_run):
+ content = mw.readFile(file_tpl)
+ content = contentReplace(content)
+ mw.writeFile(file_run, content)
+
+ pass_path = getServerDir() + '/pg.pass'
+ if not os.path.exists(pass_path):
+ username = mw.getRandomString(8)
+ password = mw.getRandomString(10)
+ pass_cmd = username + ':' + mw.hasPwd(password)
+ setCfg('username', username)
+ setCfg('password', password)
+ mw.writeFile(pass_path, pass_cmd)
+
+ # pg_conf = getCfg()
+ judge_file = getServerDir()+'/data/pgadmin4/pgadmin4.db'
+ if not os.path.exists(judge_file):
+ pg_init_bash = getPluginDir()+'/pg_init.sh'
+ pg_rand = mw.getRandomString(8)
+ pg_username = "mw."+pg_rand+"@gmail.com"
+ setCfg('web_pg_username', pg_username)
+ pg_password = mw.getRandomString(10)
+ setCfg('web_pg_password', pg_password)
+ t = mw.execShell("bash "+ pg_init_bash + " " + pg_username + " " + pg_password)
+ # print(t)
+
+ # systemd
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/pgadmin.service'
+
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ systemServiceTpl = getPluginDir() + '/init.d/pgadmin.service.tpl'
+ service_path = mw.getServerDir()
+ content = mw.readFile(systemServiceTpl)
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$PY_VER}', getPythonName())
+
+
+ mw.writeFile(systemService, content)
+ mw.execShell('systemctl daemon-reload')
+
+
+def pgOp(method):
+ file = initReplace()
+
+ current_os = mw.getOs()
+ if current_os == "darwin":
+ return 'ok'
+
+ if current_os.startswith("freebsd"):
+ data = mw.execShell('service' + getPluginName() + ' ' + method)
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+ data = mw.execShell('systemctl ' + method+ ' ' + getPluginName())
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+def status():
+ sock = '/tmp/pgadmin4.sock'
+ if os.path.exists(sock):
+ return 'start'
+ return 'stop'
+
+
+def start():
+ initCfg()
+ openPort()
+
+ pgOp('start')
+
+ mw.restartWeb()
+ return 'ok'
+
+
+def stop():
+ pgOp('stop')
+
+ conf = getConf()
+ if os.path.exists(conf):
+ os.remove(conf)
+
+ delPort()
+ mw.restartWeb()
+ return 'ok'
+
+
+def restart():
+ cleanNginxLog()
+ state = pgOp('restart')
+ mw.restartWeb()
+ return state
+
+
+def reload():
+ cleanNginxLog()
+ return pgOp('reload')
+
+def getPgOption():
+ data = getCfg()
+ return mw.returnJson(True, 'ok', data)
+
+
+def getPgPort():
+ try:
+ port = getPort()
+ return mw.returnJson(True, 'OK', port)
+ except Exception as e:
+ # print(e)
+ return mw.returnJson(False, '插件未启动!')
+
+
+def setPgPort():
+ args = getArgs()
+ data = checkArgs(args, ['port'])
+ if not data[0]:
+ return data[1]
+
+ port = args['port']
+ if port == '80':
+ return mw.returnJson(False, '80端不能使用!')
+
+ file = getConf()
+ if not os.path.exists(file):
+ return mw.returnJson(False, '插件未启动!')
+ content = mw.readFile(file)
+ rep = r'listen\s*(.*);'
+ content = re.sub(rep, "listen " + port + ';', content)
+ mw.writeFile(file, content)
+
+ setCfg("port", port)
+ mw.restartWeb()
+ return mw.returnJson(True, '修改成功!')
+
+
+def setPgUsername():
+ args = getArgs()
+ data = checkArgs(args, ['username'])
+ if not data[0]:
+ return data[1]
+
+ username = args['username']
+ setCfg('username', username)
+
+ cfg = getCfg()
+ pma_path = getServerDir() + '/pg.pass'
+ username = mw.getRandomString(10)
+ pass_cmd = cfg['username'] + ':' + mw.hasPwd(cfg['password'])
+ mw.writeFile(pma_path, pass_cmd)
+
+ mw.restartWeb()
+ return mw.returnJson(True, '修改成功!')
+
+
+def setPgPassword():
+ args = getArgs()
+ data = checkArgs(args, ['password'])
+ if not data[0]:
+ return data[1]
+
+ password = args['password']
+ setCfg('password', password)
+
+ cfg = getCfg()
+ pma_path = getServerDir() + '/pg.pass'
+ username = mw.getRandomString(10)
+ pass_cmd = cfg['username'] + ':' + mw.hasPwd(cfg['password'])
+ mw.writeFile(pma_path, pass_cmd)
+
+ mw.restartWeb()
+ return mw.returnJson(True, '修改成功!')
+
+
+def accessLog():
+ return getServerDir() + '/access.log'
+
+def errorLog():
+ return getServerDir() + '/error.log'
+
+
+def installVersion():
+ return mw.readFile(getServerDir() + '/version.pl')
+
+if __name__ == "__main__":
+ 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 == 'conf':
+ print(getConf())
+ elif func == 'version':
+ print(installVersion())
+ elif func == 'get_cfg':
+ print(returnCfg())
+ elif func == 'get_home_page':
+ print(getHomePage())
+ elif func == 'get_pg_port':
+ print(getPgPort())
+ elif func == 'set_pg_port':
+ print(setPgPort())
+ elif func == 'get_pg_option':
+ print(getPgOption())
+ elif func == 'set_pg_username':
+ print(setPgUsername())
+ elif func == 'set_pg_password':
+ print(setPgPassword())
+ elif func == 'access_log':
+ print(accessLog())
+ elif func == 'error_log':
+ print(errorLog())
+ else:
+ print('error')
diff --git a/plugins/pgadmin/info.json b/plugins/pgadmin/info.json
new file mode 100755
index 000000000..e1f4eb44e
--- /dev/null
+++ b/plugins/pgadmin/info.json
@@ -0,0 +1,15 @@
+{
+ "title":"pgadmin",
+ "tip":"soft",
+ "name":"pgadmin",
+ "type":"运行环境",
+ "ps":"全能Postgres数据库Web管理工具",
+ "versions":["4"],
+ "shell":"install.sh",
+ "checks":"server/pgadmin",
+ "path": "server/pgadmin",
+ "author":"pgadmin",
+ "home":"https://www.pgadmin.org/download/pgadmin-4-python/",
+ "date":"2024-10-1",
+ "pid": "2"
+}
\ No newline at end of file
diff --git a/plugins/pgadmin/init.d/pgadmin.service.tpl b/plugins/pgadmin/init.d/pgadmin.service.tpl
new file mode 100644
index 000000000..1d709f353
--- /dev/null
+++ b/plugins/pgadmin/init.d/pgadmin.service.tpl
@@ -0,0 +1,17 @@
+# It's not recommended to modify this file in-place, because it
+# will be overwritten during upgrades. If you want to customize,
+# the best way is to use the "systemctl edit" command.
+# systemctl daemon-reload
+
+[Unit]
+Description=pgadmin service
+After=network.target
+
+[Service]
+
+ExecStart={$SERVER_PATH}/pgadmin/run/bin/gunicorn --bind unix:/tmp/pgadmin4.sock --workers=1 --threads=25 --chdir {$SERVER_PATH}/pgadmin/run/lib/{$PY_VER}/site-packages/pgadmin4 pgAdmin4:app
+ExecReload=/bin/kill -USR2 $MAINPID
+PrivateTmp=false
+
+[Install]
+WantedBy=multi-user.target
diff --git a/plugins/pgadmin/install.sh b/plugins/pgadmin/install.sh
new file mode 100755
index 000000000..682ddd2dd
--- /dev/null
+++ b/plugins/pgadmin/install.sh
@@ -0,0 +1,127 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }
+function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; }
+function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; }
+function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; }
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+
+P_VER=`python3 -V | awk '{print $2}'`
+echo "python:$P_VER"
+
+# https://cn.linux-console.net/?p=6560
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/pgadmin && bash install.sh install 4
+# cd /www/server/mdserver-web/plugins/pgadmin && bash install.sh install 4
+
+# source /www/server/pgadmin/run/bin/activate
+# python /www/server/pgadmin/run/lib/python3.10/site-packages/pgadmin4/setup.py --help
+# python /www/server/pgadmin/run/lib/python3.10/site-packages/pgadmin4/setup.py add-user mdserver-web@gmail.com 123123
+# cd /www/server/mdserver-web && python3 plugins/pgadmin/index.py start
+# cd /www/server/mdserver-web && python3 plugins/pgadmin/index.py stop
+
+if [ "$sys_os" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+sysName=`uname`
+echo "use system: ${sysName}"
+
+if [ "${sysName}" == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eqi "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release; then
+ OSNAME='centos'
+elif grep -Eqi "Fedora" /etc/issue || grep -Eq "Fedora" /etc/*-release; then
+ OSNAME='fedora'
+elif grep -Eqi "Debian" /etc/issue || grep -Eq "Debian" /etc/*-release; then
+ OSNAME='debian'
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/*-release; then
+ OSNAME='ubuntu'
+elif grep -Eqi "Raspbian" /etc/issue || grep -Eq "Raspbian" /etc/*-release; then
+ OSNAME='raspbian'
+else
+ OSNAME='unknow'
+fi
+
+Install_pgadmin()
+{
+ # if [ -d $serverPath/pgadmin ];then
+ # exit 0
+ # fi
+
+ if version_lt "$P_VER" "3.8.0" ;then
+ echo 'Python版本太低,无法安装'
+ exit 0
+ fi
+ PG_DIR=${serverPath}/pgadmin/run
+ PG_DATA_DIR=${serverPath}/pgadmin/data
+ mkdir -p $PG_DIR
+ mkdir -p $PG_DATA_DIR
+ echo "${1}" > ${serverPath}/pgadmin/version.pl
+
+ if [ ! -f $PG_DIR/bin/activate ];then
+ if version_ge "$P_VER" "3.11.0" ;then
+ echo "python3 > 3.11"
+ cd $PG_DIR && python3 -m venv $PG_DIR
+ else
+ echo "python3 < 3.10"
+ cd $PG_DIR && python3 -m venv .
+ fi
+ fi
+
+ if [ -f ${PG_DIR}/bin/activate ];then
+ source ${PG_DIR}/bin/activate
+ fi
+ pip install gunicorn
+ pip install pgadmin4
+
+ # pip install https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v8.10/pip/pgadmin4-8.10-py3-none-any.whl
+ # pip install https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v8.13/pip/pgadmin4-8.13-py3-none-any.whl
+
+ cd ${rootPath} && python3 ${rootPath}/plugins/pgadmin/index.py start
+ echo '安装完成'
+}
+
+Uninstall_pgadmin()
+{
+ cd ${rootPath} && python3 ${rootPath}/plugins/pgadmin/index.py stop
+
+ if [ -f /usr/lib/systemd/system/pgadmin.service ];then
+ systemctl stop pgadmin
+ systemctl disable pgadmin
+ rm -rf /usr/lib/systemd/system/pgadmin.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f /lib/systemd/system/pgadmin.service ];then
+ systemctl stop pgadmin
+ systemctl disable pgadmin
+ rm -rf /lib/systemd/system/pgadmin.service
+ systemctl daemon-reload
+ fi
+
+ rm -rf ${serverPath}/pgadmin
+ # rm -rf /var/lib/pgadmin
+ # rm -rf /var/log/pgadmin
+ echo '卸载完成'
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_pgadmin $2
+else
+ Uninstall_pgadmin $2
+fi
diff --git a/plugins/pgadmin/js/pgadmin.js b/plugins/pgadmin/js/pgadmin.js
new file mode 100755
index 000000000..0cccb0db9
--- /dev/null
+++ b/plugins/pgadmin/js/pgadmin.js
@@ -0,0 +1,119 @@
+function pgPost(method,args,callback){
+
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(toArrayObject(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+ $.post('/plugins/run', {name:'pgadmin', func:method, args:_args}, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+function pgAsyncPost(method,args){
+
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(toArrayObject(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+ return syncPost('/plugins/run', {name:'pgadmin', func:method, args:_args});
+}
+
+function homePage(){
+ pgPost('get_home_page', '', function(data){
+ var rdata = $.parseJSON(data.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ var con = '主页 ';
+ $(".soft-man-con").html(con);
+ });
+}
+
+
+//phpmyadmin安全设置
+function safeConf() {
+ pgPost('get_pg_option', {}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:2,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ var cfg = rdata.data;
+ var con = '\
+ 访问端口 \
+ \
+ 保存 \
+
\
+ \
+ 用户名 \
+ \
+ 保存 \
+
\
+ \
+ 密码 \
+ \
+ 保存 \
+
\
+ \
+ pgadmin登录信息
\
+ \
+ PG登录用户名 \
+ \
+ 保存 \
+
\
+ \
+ PG登录密码 \
+ \
+ 保存 \
+
';
+ $(".soft-man-con").html(con);
+ });
+}
+
+function setPgUsername(){
+ var username = $("input[name=username]").val();
+ pgPost('set_pg_username',{'username':username}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+function setPgPassword(){
+ var password = $("input[name=password]").val();
+ pgPost('set_pg_password',{'password':password}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+//修改phpmyadmin端口
+function setPgPort() {
+ var pmport = $("#pmport").val();
+ if (pmport < 80 || pmport > 65535) {
+ layer.msg('端口范围不合法!', { icon: 2 });
+ return;
+ }
+ var data = 'port=' + pmport;
+
+ pgPost('set_pg_port',data, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
\ No newline at end of file
diff --git a/plugins/pgadmin/pg_init.sh b/plugins/pgadmin/pg_init.sh
new file mode 100644
index 000000000..31c9dd6bb
--- /dev/null
+++ b/plugins/pgadmin/pg_init.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+python_ver=`ls /www/server/pgadmin/run/lib/ | grep python | cut -d \ -f 1 | awk 'END {print}'`
+
+email=$1
+email_pwd=$2
+
+expect <<-EOF
+set time 10
+spawn gunicorn --bind unix:/tmp/pgadmin4.sock \
+--workers=1 \
+--threads=25 \
+--chdir /www/server/pgadmin/run/lib/${python_ver}/site-packages/pgadmin4 pgAdmin4:app
+expect {
+ "Email address:" { send "${email}\r"; exp_continue }
+ "Password" { send "${email_pwd}\r"; exp_continue }
+ "Retype password" { send "${email_pwd}\r" }
+}
+expect eof
+EOF
+
+pids=$(ps aux | grep 'pgAdmin4:app' | grep -v grep | awk '{print $2}')
+arr=($pids)
+for p in ${arr[@]}
+do
+ kill -9 $p > /dev/null 2>&1
+done
+
+
diff --git a/plugins/php-apt/conf/app_start.php b/plugins/php-apt/conf/app_start.php
new file mode 100644
index 000000000..5d1b7ce8c
--- /dev/null
+++ b/plugins/php-apt/conf/app_start.php
@@ -0,0 +1,55 @@
+save_run($xhprof_data, 'xhprof_foo');
+
+ $profiler_url = sprintf('http://{$LOCAL_IP}:5858/index.php?run=%s&source=xhprof_foo', $run_id);
+ // echo "";
+
+ $style_css = 'position:fixed;bottom: 0px;right: 0px;z-index: 100000000;color: red;background-color: black;padding: 0px;margin: 0px;';
+ echo 'XHProf分析结果 ';
+
+}
+
+if (extension_loaded('xhprof')
+ && isset($_GET[XHProf_Name]) && $_GET[XHProf_Name] == 'ok' &&
+ (! in_array($_SERVER['SCRIPT_NAME'], ['/xhprof_html/callgraph.php',
+ '/xhprof_html/index.php']))) {
+ app_xhprof_start();
+ include_once $_SERVER['SCRIPT_FILENAME'];
+ app_xhprof_end();
+ exit;
+}
+
+?>
diff --git a/plugins/php-apt/conf/enable-php.conf b/plugins/php-apt/conf/enable-php.conf
new file mode 100644
index 000000000..612058e6a
--- /dev/null
+++ b/plugins/php-apt/conf/enable-php.conf
@@ -0,0 +1,9 @@
+set $PHP_ENV 1;
+location ~ [^/]\.php(/|$)
+{
+ try_files $uri =404;
+ fastcgi_pass unix:/run/php/php{$PHP_VERSION}-fpm.sock;
+ fastcgi_index index.php;
+ include fastcgi.conf;
+ include {$SERVER_PATH}/web_conf/php/pathinfo.conf;
+}
\ No newline at end of file
diff --git a/plugins/php-apt/conf/pathinfo.conf b/plugins/php-apt/conf/pathinfo.conf
new file mode 100644
index 000000000..47b573684
--- /dev/null
+++ b/plugins/php-apt/conf/pathinfo.conf
@@ -0,0 +1,8 @@
+set $real_script_name $fastcgi_script_name;
+if ($fastcgi_script_name ~ "^(.+?\.php)(/.+)$") {
+ set $real_script_name $1;
+ set $path_info $2;
+ }
+fastcgi_param SCRIPT_FILENAME $document_root$real_script_name;
+fastcgi_param SCRIPT_NAME $real_script_name;
+fastcgi_param PATH_INFO $path_info;
\ No newline at end of file
diff --git a/plugins/php-apt/conf/php-fpm.conf b/plugins/php-apt/conf/php-fpm.conf
new file mode 100644
index 000000000..aeee58e60
--- /dev/null
+++ b/plugins/php-apt/conf/php-fpm.conf
@@ -0,0 +1,5 @@
+[global]
+pid = /run/php/php{$PHP_VERSION}-fpm.pid
+error_log = /var/log/php{$PHP_VERSION}-fpm.log
+include=/etc/php/{$PHP_VERSION}/fpm/pool.d/*.conf
+php_value[auto_prepend_file]={$SERVER_PATH}/php-apt/app_start.php
\ No newline at end of file
diff --git a/plugins/php-apt/conf/phpinfo.conf b/plugins/php-apt/conf/phpinfo.conf
new file mode 100644
index 000000000..401302116
--- /dev/null
+++ b/plugins/php-apt/conf/phpinfo.conf
@@ -0,0 +1,4 @@
+location /{$PHP_VERSION} {
+ root {$ROOT_PATH}/phpinfo;
+ include {$SERVER_PATH}/web_conf/php/conf/enable-php-apt{$PHP_VERSION}.conf;
+}
\ No newline at end of file
diff --git a/plugins/php-apt/conf/www.conf b/plugins/php-apt/conf/www.conf
new file mode 100644
index 000000000..5d9015a48
--- /dev/null
+++ b/plugins/php-apt/conf/www.conf
@@ -0,0 +1,23 @@
+[www]
+user = {$PHP_USER}
+group = {$PHP_GROUP}
+
+listen = /run/php/php{$PHP_VERSION}-fpm.sock
+listen.owner = {$PHP_USER}
+listen.group = {$PHP_GROUP}
+listen.backlog = 4096
+pm = dynamic
+pm.max_children = 2
+pm.start_servers = 1
+pm.min_spare_servers = 1
+pm.max_spare_servers = 2
+pm.status_path = /phpfpm_status_apt{$PHP_VERSION}
+pm.max_requests = 1000
+request_terminate_timeout = 30
+request_slowlog_timeout = 10
+slowlog = /var/log/fpm-php{$PHP_VERSION}.www.slow.log
+
+;php_admin_flag[log_errors] = on
+;php_admin_value[error_log] = /var/log/fpm-php{$PHP_VERSION}.www.log
+
+
diff --git a/plugins/php-apt/ico.png b/plugins/php-apt/ico.png
new file mode 100755
index 000000000..59def5702
Binary files /dev/null and b/plugins/php-apt/ico.png differ
diff --git a/plugins/php-apt/index.html b/plugins/php-apt/index.html
new file mode 100755
index 000000000..9e72b6b57
--- /dev/null
+++ b/plugins/php-apt/index.html
@@ -0,0 +1,67 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/php-apt/index.py b/plugins/php-apt/index.py
new file mode 100755
index 000000000..8b5a38f23
--- /dev/null
+++ b/plugins/php-apt/index.py
@@ -0,0 +1,928 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import shutil
+
+
+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 localVersion(v):
+ return v[0:1]+v[2:3]
+
+def getPluginName():
+ return 'php-apt'
+
+
+def getAppDir():
+ return mw.getServerDir()+'/'+getPluginName()
+
+def getServerDir():
+ return '/etc/php'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getArgs():
+ args = sys.argv[3:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+
+ 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 getConf(version):
+ path = getServerDir() + '/' + version + '/fpm/php.ini'
+ return path
+
+
+def getFpmConfFile(version):
+ return getServerDir() + '/' + version + '/fpm/pool.d/mw.conf'
+
+def getFpmFile(version):
+ return getServerDir() + '/' + version + '/fpm/php-fpm.conf'
+
+def status(version):
+ # ps -ef|grep 'php/81' |grep -v grep | grep -v python | awk '{print $2}
+ cmd = "ps -ef|grep 'php/" + version + "' |grep -v grep | grep -v python | awk '{print $2}'"
+ data = mw.execShell(cmd)
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def contentReplace(content, version):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$PHP_VERSION}', version)
+ content = content.replace('{$LOCAL_IP}', mw.getLocalIp())
+
+ if mw.isAppleSystem():
+ # user = mw.execShell(
+ # "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
+ content = content.replace('{$PHP_USER}', 'nobody')
+ content = content.replace('{$PHP_GROUP}', 'nobody')
+
+ rep = r'listen.owner\s*=\s*(.+)\r?\n'
+ val = ';listen.owner = nobody\n'
+ content = re.sub(rep, val, content)
+
+ rep = r'listen.group\s*=\s*(.+)\r?\n'
+ val = ';listen.group = nobody\n'
+ content = re.sub(rep, val, content)
+
+ rep = r'user\s*=\s*(.+)\r?\n'
+ val = ';user = nobody\n'
+ content = re.sub(rep, val, content)
+
+ rep = r'[^\.]group\s*=\s*(.+)\r?\n'
+ val = ';group = nobody\n'
+ content = re.sub(rep, val, content)
+
+ else:
+ content = content.replace('{$PHP_USER}', 'www')
+ content = content.replace('{$PHP_GROUP}', 'www')
+ return content
+
+
+def getDstEnablePHP(version):
+ sdir = mw.getServerDir()
+ dfile = sdir + '/web_conf/php/conf/enable-php-apt' + version + '.conf'
+ return dfile
+
+
+def makeOpConf(version):
+
+ sdir = mw.getServerDir()
+
+ dst_dir_conf = sdir + '/web_conf/php/conf'
+ if not os.path.exists(dst_dir_conf):
+ mw.execShell('mkdir -p ' + dst_dir_conf)
+
+ pathinfo = sdir + '/web_conf/php/pathinfo.conf'
+ if not os.path.exists(pathinfo):
+ source_pathinfo = getPluginDir() + '/conf/pathinfo.conf'
+ shutil.copyfile(source_pathinfo, pathinfo)
+
+ info = getPluginDir() + '/info.json'
+ content = mw.readFile(info)
+ content = json.loads(content)
+ versions = content['versions']
+ tpl = getPluginDir() + '/conf/enable-php.conf'
+ tpl_content = mw.readFile(tpl)
+ dfile = getDstEnablePHP(version)
+ if not os.path.exists(dfile):
+ w_content = contentReplace(tpl_content, version)
+ mw.writeFile(dfile, w_content)
+
+
+def phpFpmWwwReplace(version):
+ service_php_fpm_dir = getServerDir() + '/' + version + '/fpm/pool.d'
+ if not os.path.exists(service_php_fpm_dir):
+ os.mkdir(service_php_fpm_dir)
+
+ service_php_fpmwww = service_php_fpm_dir + '/www.conf'
+ if os.path.exists(service_php_fpmwww):
+ # 原来文件备份
+ mw.execShell('mv ' + service_php_fpmwww +
+ ' ' + service_php_fpmwww + '.bak')
+
+ service_php_fpm_mw = service_php_fpm_dir + '/mw.conf'
+ if not os.path.exists(service_php_fpm_mw):
+ tpl_php_fpmwww = getPluginDir() + '/conf/www.conf'
+ content = mw.readFile(tpl_php_fpmwww)
+ content = contentReplace(content, version)
+ mw.writeFile(service_php_fpm_mw, content)
+
+
+def deleteConfList(version):
+ enable_conf = getDstEnablePHP(version)
+ if os.path.exists(enable_conf):
+ os.remove(enable_conf)
+
+def phpPrependFile(version):
+ app_start = getAppDir() + '/app_start.php'
+ if not os.path.exists(app_start):
+ tpl = getPluginDir() + '/conf/app_start.php'
+ content = mw.readFile(tpl)
+ content = contentReplace(content, version)
+ mw.writeFile(app_start, content)
+
+def phpFpmReplace(version):
+ desc_php_fpm = getServerDir() + '/' + version + '/fpm/php-fpm.conf'
+
+ tpl_php_fpm = getPluginDir() + '/conf/php-fpm.conf'
+ content = mw.readFile(tpl_php_fpm)
+ content = contentReplace(content, version)
+ mw.writeFile(desc_php_fpm, content)
+ return True
+
+
+def initReplace(version):
+ makeOpConf(version)
+ phpFpmWwwReplace(version)
+
+ install_ok = getAppDir() + "/" + localVersion(version) + "/install.ok"
+ if not os.path.exists(install_ok):
+ phpFpmReplace(version)
+
+ phpini = getConf(version)
+ ssl_crt = mw.getSslCrt()
+
+ cmd_openssl = "sed -i \"s#;openssl.cafile=#openssl.cafile=" + ssl_crt + "#\" " + phpini
+ mw.execShell(cmd_openssl)
+ cmd_curl = "sed -i \"s#;curl.cainfo =#curl.cainfo=" + ssl_crt + "#\" " + phpini
+ mw.execShell(cmd_curl)
+
+ mw.writeFile(install_ok, 'ok')
+
+ phpPrependFile(version)
+ # systemd
+ # mw.execShell('systemctl daemon-reload')
+ return 'ok'
+
+
+def phpOp(version, method):
+ if method == 'start':
+ initReplace(version)
+
+ if mw.isAppleSystem():
+ return 'fail'
+
+ data = mw.execShell('systemctl ' + method + ' ' +'php' + version + '-fpm')
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def start(version):
+ return phpOp(version, 'start')
+
+
+def stop(version):
+ status = phpOp(version, 'stop')
+ deleteConfList(version)
+ return status
+
+
+def restart(version):
+ return phpOp(version, 'restart')
+
+
+def reload(version):
+ return phpOp(version, 'reload')
+
+
+def initdStatus(version):
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status php' + version + \
+ '-fpm | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+
+def initdInstall(version):
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl enable php' + version + '-fpm')
+ return 'ok'
+
+
+def initdUinstall(version):
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl disable php' + version + '-fpm')
+ return 'ok'
+
+
+def fpmLog(version):
+ return '/var/log/php' + version + '-fpm.log'
+
+
+def fpmSlowLog(version):
+ return '/var/log/fpm-php' + version + '.www.slow.log'
+
+
+def getPhpConf(version):
+ gets = [
+ {'name': 'short_open_tag', 'type': 1, 'ps': '短标签支持'},
+ {'name': 'asp_tags', 'type': 1, 'ps': 'ASP标签支持'},
+ {'name': 'max_execution_time', 'type': 2, 'ps': '最大脚本运行时间'},
+ {'name': 'max_input_time', 'type': 2, 'ps': '最大输入时间'},
+ {'name': 'max_input_vars', 'type': 2, 'ps': '最大输入数量'},
+ {'name': 'memory_limit', 'type': 2, 'ps': '脚本内存限制'},
+ {'name': 'post_max_size', 'type': 2, 'ps': 'POST数据最大尺寸'},
+ {'name': 'file_uploads', 'type': 1, 'ps': '是否允许上传文件'},
+ {'name': 'upload_max_filesize', 'type': 2, 'ps': '允许上传文件的最大尺寸'},
+ {'name': 'max_file_uploads', 'type': 2, 'ps': '允许同时上传文件的最大数量'},
+ {'name': 'default_socket_timeout', 'type': 2, 'ps': 'Socket超时时间'},
+ {'name': 'error_reporting', 'type': 3, 'ps': '错误级别'},
+ {'name': 'display_errors', 'type': 1, 'ps': '是否输出详细错误信息'},
+ {'name': 'cgi.fix_pathinfo', 'type': 0, 'ps': '是否开启pathinfo'},
+ {'name': 'date.timezone', 'type': 3, 'ps': '时区'}
+ ]
+ phpini = mw.readFile(getConf(version))
+ result = []
+ for g in gets:
+ rep = g['name'] + r'\s*=\s*([0-9A-Za-z_& ~]+)(\s*;?|\r?\n)'
+ tmp = re.search(rep, phpini)
+ if not tmp:
+ continue
+ g['value'] = tmp.groups()[0]
+ result.append(g)
+ return mw.getJson(result)
+
+
+def submitPhpConf(version):
+ gets = ['display_errors', 'cgi.fix_pathinfo', 'date.timezone', 'short_open_tag',
+ 'asp_tags', 'max_execution_time', 'max_input_time', 'max_input_vars', 'memory_limit',
+ 'post_max_size', 'file_uploads', 'upload_max_filesize', 'max_file_uploads',
+ 'default_socket_timeout', 'error_reporting']
+ args = getArgs()
+ filename = getConf(version)
+ phpini = mw.readFile(filename)
+ for g in gets:
+ if g in args:
+ rep = g + r'\s*=\s*(.+)\r?\n'
+ val = g + ' = ' + args[g] + '\n'
+ phpini = re.sub(rep, val, phpini)
+ mw.writeFile(filename, phpini)
+ reload(version)
+ return mw.returnJson(True, '设置成功')
+
+
+def getLimitConf(version):
+ fileini = getConf(version)
+ phpini = mw.readFile(fileini)
+ filefpm = getFpmConfFile(version)
+ phpfpm = mw.readFile(filefpm)
+
+ # print fileini, filefpm
+ data = {}
+ try:
+ rep = r"upload_max_filesize\s*=\s*([0-9]+)M"
+ tmp = re.search(rep, phpini).groups()
+ data['max'] = tmp[0]
+ except:
+ data['max'] = '50'
+
+ try:
+ rep = r"request_terminate_timeout\s*=\s*([0-9]+)\n"
+ tmp = re.search(rep, phpfpm).groups()
+ data['maxTime'] = tmp[0]
+ except:
+ data['maxTime'] = 0
+
+ try:
+ rep = r"\n;*\s*cgi\.fix_pathinfo\s*=\s*([0-9]+)\s*\n"
+ tmp = re.search(rep, phpini).groups()
+
+ if tmp[0] == '1':
+ data['pathinfo'] = True
+ else:
+ data['pathinfo'] = False
+ except:
+ data['pathinfo'] = False
+
+ return mw.getJson(data)
+
+
+def setMaxTime(version):
+ args = getArgs()
+ data = checkArgs(args, ['time'])
+ if not data[0]:
+ return data[1]
+
+ time = args['time']
+ if int(time) < 30 or int(time) > 86400:
+ return mw.returnJson(False, '请填写30-86400间的值!')
+
+ filefpm = getFpmConfFile(version)
+ conf = mw.readFile(filefpm)
+ rep = r"request_terminate_timeout\s*=\s*([0-9]+)\n"
+ conf = re.sub(rep, "request_terminate_timeout = " + time + "\n", conf)
+ mw.writeFile(filefpm, conf)
+
+ fileini = getConf(version)
+ phpini = mw.readFile(fileini)
+ rep = r"max_execution_time\s*=\s*([0-9]+)\r?\n"
+ phpini = re.sub(rep, "max_execution_time = " + time + "\n", phpini)
+ rep = r"max_input_time\s*=\s*([0-9]+)\r?\n"
+ phpini = re.sub(rep, "max_input_time = " + time + "\n", phpini)
+ mw.writeFile(fileini, phpini)
+ return mw.returnJson(True, '设置成功!')
+
+
+def setMaxSize(version):
+ args = getArgs()
+ data = checkArgs(args, ['max'])
+ if not data[0]:
+ return data[1]
+
+ maxVal = args['max']
+ if int(maxVal) < 2:
+ return mw.returnJson(False, '上传大小限制不能小于2MB!')
+
+ path = getConf(version)
+ conf = mw.readFile(path)
+ rep = r"\nupload_max_filesize\s*=\s*[0-9]+M"
+ conf = re.sub(rep, u'\nupload_max_filesize = ' + maxVal + 'M', conf)
+ rep = r"\npost_max_size\s*=\s*[0-9]+M"
+ conf = re.sub(rep, u'\npost_max_size = ' + maxVal + 'M', conf)
+ mw.writeFile(path, conf)
+
+ msg = mw.getInfo('设置PHP-{1}最大上传大小为[{2}MB]!', (version, maxVal,))
+ mw.writeLog('插件管理[PHP]', msg)
+ return mw.returnJson(True, '设置成功!')
+
+
+def getFpmConfig(version):
+
+ filefpm = getFpmConfFile(version)
+ conf = mw.readFile(filefpm)
+ data = {}
+ rep = r"\s*pm.max_children\s*=\s*([0-9]+)\s*"
+ tmp = re.search(rep, conf).groups()
+ data['max_children'] = tmp[0]
+
+ rep = r"\s*pm.start_servers\s*=\s*([0-9]+)\s*"
+ tmp = re.search(rep, conf).groups()
+ data['start_servers'] = tmp[0]
+
+ rep = r"\s*pm.min_spare_servers\s*=\s*([0-9]+)\s*"
+ tmp = re.search(rep, conf).groups()
+ data['min_spare_servers'] = tmp[0]
+
+ rep = r"\s*pm.max_spare_servers \s*=\s*([0-9]+)\s*"
+ tmp = re.search(rep, conf).groups()
+ data['max_spare_servers'] = tmp[0]
+
+ rep = r"\s*pm\s*=\s*(\w+)\s*"
+ tmp = re.search(rep, conf).groups()
+ data['pm'] = tmp[0]
+ return mw.getJson(data)
+
+
+def setFpmConfig(version):
+ args = getArgs()
+ # if not 'max' in args:
+ # return 'missing time args!'
+
+ # version = args['version']
+ max_children = args['max_children']
+ start_servers = args['start_servers']
+ min_spare_servers = args['min_spare_servers']
+ max_spare_servers = args['max_spare_servers']
+ pm = args['pm']
+
+ filefpm = getFpmConfFile(version)
+ conf = mw.readFile(filefpm)
+
+ rep = r"\s*pm.max_children\s*=\s*([0-9]+)\s*"
+ conf = re.sub(rep, "\npm.max_children = " + max_children, conf)
+
+ rep = r"\s*pm.start_servers\s*=\s*([0-9]+)\s*"
+ conf = re.sub(rep, "\npm.start_servers = " + start_servers, conf)
+
+ rep = r"\s*pm.min_spare_servers\s*=\s*([0-9]+)\s*"
+ conf = re.sub(rep, "\npm.min_spare_servers = " + min_spare_servers, conf)
+
+ rep = r"\s*pm.max_spare_servers \s*=\s*([0-9]+)\s*"
+ conf = re.sub(rep, "\npm.max_spare_servers = " + max_spare_servers + "\n", conf)
+
+ rep = r"\s*pm\s*=\s*(\w+)\s*"
+ conf = re.sub(rep, "\npm = " + pm + "\n", conf)
+
+ mw.writeFile(filefpm, conf)
+ reload(version)
+
+ msg = mw.getInfo('设置PHP-{1}并发设置,max_children={2},start_servers={3},min_spare_servers={4},max_spare_servers={5}',
+ (version, max_children, start_servers, min_spare_servers, max_spare_servers,))
+ mw.writeLog('插件管理[PHP]', msg)
+ return mw.returnJson(True, '设置成功!')
+
+
+def getFpmAddress(version):
+ fpm_address = '/run/php/php{}-fpm.sock'.format(version)
+ php_fpm_file = getFpmConfFile(version)
+ try:
+ content = readFile(php_fpm_file)
+ tmp = re.findall(r"listen\s*=\s*(.+)", content)
+ if not tmp:
+ return fpm_address
+ if tmp[0].find('sock') != -1:
+ return fpm_address
+ if tmp[0].find(':') != -1:
+ listen_tmp = tmp[0].split(':')
+ if bind:
+ fpm_address = (listen_tmp[0], int(listen_tmp[1]))
+ else:
+ fpm_address = ('127.0.0.1', int(listen_tmp[1]))
+ else:
+ fpm_address = ('127.0.0.1', int(tmp[0]))
+ return fpm_address
+ except:
+ return fpm_address
+
+
+def getFpmStatus(version):
+
+ stat = status(version)
+ if stat == 'stop':
+ return mw.returnJson(False, 'PHP[' + version + ']未启动!!!')
+
+ sock_file = getFpmAddress(version)
+ try:
+ sock_data = mw.requestFcgiPHP(sock_file, '/phpfpm_status_apt' + version + '?json')
+
+ result = str(sock_data, encoding='utf-8')
+ data = json.loads(result)
+ fTime = time.localtime(int(data['start time']))
+ data['start time'] = time.strftime('%Y-%m-%d %H:%M:%S', fTime)
+ except Exception as e:
+ return mw.returnJson(False, str(e))
+
+ return mw.returnJson(True, "OK", data)
+
+
+def getSessionConf(version):
+ filename = getConf(version)
+ if not os.path.exists(filename):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+
+ phpini = mw.readFile(filename)
+
+ rep = r'session.save_handler\s*=\s*([0-9A-Za-z_& ~]+)(\s*;?|\r?\n)'
+ save_handler = re.search(rep, phpini)
+ if save_handler:
+ save_handler = save_handler.group(1)
+ else:
+ save_handler = "files"
+
+ reppath = r'\nsession.save_path\s*=\s*"tcp\:\/\/([\d\.]+):(\d+).*\r?\n'
+ passrep = r'\nsession.save_path\s*=\s*"tcp://[\w\.\?\:]+=(.*)"\r?\n'
+ memcached = r'\nsession.save_path\s*=\s*"([\d\.]+):(\d+)"'
+ save_path = re.search(reppath, phpini)
+ if not save_path:
+ save_path = re.search(memcached, phpini)
+ passwd = re.search(passrep, phpini)
+ port = ""
+ if passwd:
+ passwd = passwd.group(1)
+ else:
+ passwd = ""
+ if save_path:
+ port = save_path.group(2)
+ save_path = save_path.group(1)
+
+ else:
+ save_path = ""
+
+ data = {"save_handler": save_handler, "save_path": save_path,
+ "passwd": passwd, "port": port}
+ return mw.returnJson(True, 'ok', data)
+
+
+def setSessionConf(version):
+
+ args = getArgs()
+
+ ip = args['ip']
+ port = args['port']
+ passwd = args['passwd']
+ save_handler = args['save_handler']
+
+ if save_handler != "files":
+ iprep = r"(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})"
+ if not re.search(iprep, ip):
+ return mw.returnJson(False, '请输入正确的IP地址')
+
+ try:
+ port = int(port)
+ if port >= 65535 or port < 1:
+ return mw.returnJson(False, '请输入正确的端口号')
+ except:
+ return mw.returnJson(False, '请输入正确的端口号')
+ prep = r"[\~\`\/\=]"
+ if re.search(prep, passwd):
+ return mw.returnJson(False, '请不要输入以下特殊字符 " ~ ` / = "')
+
+ filename = getConf(version)
+ if not os.path.exists(filename):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+ phpini = mw.readFile(filename)
+
+ session_tmp = getServerDir() + "/tmp/session"
+
+ rep = r'session.save_handler\s*=\s*(.+)\r?\n'
+ val = r'session.save_handler = ' + save_handler + '\n'
+ phpini = re.sub(rep, val, phpini)
+
+ content = mw.execShell(
+ 'cat /etc/php/' + version + '/fpm/conf.d/*' + " | grep -v '^;' |tr -s '\n'")
+ content = content[0]
+
+ if save_handler == "memcached":
+ if not re.search("memcached.so", phpini):
+ return mw.returnJson(False, '请先安装%s扩展' % save_handler)
+ rep = r'\nsession.save_path\s*=\s*(.+)\r?\n'
+ val = r'\nsession.save_path = "%s:%s" \n' % (ip, port)
+ if re.search(rep, phpini):
+ phpini = re.sub(rep, val, phpini)
+ else:
+ phpini = re.sub('\n;session.save_path = "' + "/var/lib/php/sessions" + '"',
+ '\n;session.save_path = "' + "/var/lib/php/sessions" + '"' + val, phpini)
+
+ if save_handler == "memcache":
+ if not content.find('memcache') > -1:
+ return mw.returnJson(False, '请先安装%s扩展' % save_handler)
+ rep = r'\nsession.save_path\s*=\s*(.+)\r?\n'
+ val = r'\nsession.save_path = "%s:%s" \n' % (ip, port)
+ if re.search(rep, phpini):
+ phpini = re.sub(rep, val, phpini)
+ else:
+ phpini = re.sub('\n;session.save_path = "' + "/var/lib/php/sessions" + '"',
+ '\n;session.save_path = "' + "/var/lib/php/sessions" + '"' + val, phpini)
+
+ if save_handler == "redis":
+ if not content.find('redis') > -1:
+ return mw.returnJson(False, '请先安装%s扩展' % save_handler)
+ if passwd:
+ passwd = "?auth=" + passwd
+ else:
+ passwd = ""
+ rep = r'\nsession.save_path\s*=\s*(.+)\r?\n'
+ val = r'\nsession.save_path = "tcp://%s:%s%s"\n' % (ip, port, passwd)
+ res = re.search(rep, phpini)
+ if res:
+ phpini = re.sub(rep, val, phpini)
+ else:
+ phpini = re.sub('\n;session.save_path = "' + "/var/lib/php/sessions" + '"',
+ '\n;session.save_path = "' + "/var/lib/php/sessions" + '"' + val, phpini)
+
+ if save_handler == "files":
+ rep = r'\nsession.save_path\s*=\s*(.+)\r?\n'
+ val = r'\nsession.save_path = "' + session_tmp + '"\n'
+ if re.search(rep, phpini):
+ phpini = re.sub(rep, val, phpini)
+ else:
+ phpini = re.sub('\n;session.save_path = "' + "/var/lib/php/sessions" + '"',
+ '\n;session.save_path = "' + "/var/lib/php/sessions" + '"' + val, phpini)
+
+ mw.writeFile(filename, phpini)
+ reload(version)
+ return mw.returnJson(True, '设置成功!')
+
+
+def getSessionCount_Origin(version):
+ session_tmp = getServerDir() + "/tmp/session"
+ d = ["/tmp", "/var/lib/php/sessions", session_tmp]
+ count = 0
+ for i in d:
+ if not os.path.exists(i):
+ mw.execShell('mkdir -p %s' % i)
+ list = os.listdir(i)
+ for l in list:
+ if os.path.isdir(i + "/" + l):
+ l1 = os.listdir(i + "/" + l)
+ for ll in l1:
+ if "sess_" in ll:
+ count += 1
+ continue
+ if "sess_" in l:
+ count += 1
+
+ s = "find /tmp -mtime +1 |grep 'sess_' | wc -l"
+ old_file = int(mw.execShell(s)[0].split("\n")[0])
+
+ s = "find " + session_tmp + " -mtime +1 |grep 'sess_'|wc -l"
+ old_file += int(mw.execShell(s)[0].split("\n")[0])
+ return {"total": count, "oldfile": old_file}
+
+
+def getSessionCount(version):
+ data = getSessionCount_Origin(version)
+ return mw.returnJson(True, 'ok!', data)
+
+
+def cleanSessionOld(version):
+ s = "find /tmp -mtime +1 |grep 'sess_'|xargs rm -f"
+ mw.execShell(s)
+
+ session_tmp = getServerDir() + "/tmp/session"
+ s = "find " + session_tmp + " -mtime +1 |grep 'sess_' |xargs rm -f"
+ mw.execShell(s)
+ old_file_conf = getSessionCount_Origin(version)["oldfile"]
+ if old_file_conf == 0:
+ return mw.returnJson(True, '清理成功')
+ else:
+ return mw.returnJson(True, '清理失败')
+
+
+def getDisableFunc(version):
+ filename = getConf(version)
+ if not os.path.exists(filename):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+
+ phpini = mw.readFile(filename)
+ data = {}
+ rep = r"disable_functions\s*=\s{0,1}(.*)\n"
+ tmp = re.search(rep, phpini).groups()
+ data['disable_functions'] = tmp[0]
+ return mw.getJson(data)
+
+
+def setDisableFunc(version):
+ filename = getConf(version)
+ if not os.path.exists(filename):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+
+ args = getArgs()
+ disable_functions = args['disable_functions']
+
+ phpini = mw.readFile(filename)
+ rep = r"disable_functions\s*=\s*.*\n"
+ phpini = re.sub(rep, 'disable_functions = ' +
+ disable_functions + "\n", phpini)
+
+ msg = mw.getInfo('修改PHP-{1}的禁用函数为[{2}]', (version, disable_functions,))
+ mw.writeLog('插件管理[PHP-YUM]', msg)
+ mw.writeFile(filename, phpini)
+ reload(version)
+ return mw.returnJson(True, '设置成功!')
+
+
+def getPhpinfo(version):
+ stat = status(version)
+ if stat == 'stop':
+ return 'PHP[' + version + ']未启动,不可访问!!!'
+
+ sock_file = getFpmAddress(version)
+ root_dir = mw.getFatherDir() + '/phpinfo'
+
+ mw.execShell("rm -rf " + root_dir)
+ mw.execShell("mkdir -p " + root_dir)
+ mw.writeFile(root_dir + '/phpinfo.php', '')
+ sock_data = mw.requestFcgiPHP(sock_file, '/phpinfo.php', root_dir)
+ os.system("rm -rf " + root_dir)
+ phpinfo = str(sock_data, encoding='utf-8')
+ return phpinfo
+
+
+def get_php_info(args):
+ inputVer = args['version']
+ version = inputVer[0] + '.' + inputVer[1]
+ return getPhpinfo(version)
+
+
+def getLibConf(version):
+ fname = getConf(version)
+ if not os.path.exists(fname):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+
+ # phpini = mw.readFile(fname)
+ content = mw.execShell('cat /etc/php/' + version + '/fpm/conf.d/*' + " | grep -v '^;' |tr -s '\n'")
+ content = content[0]
+
+ libpath = getPluginDir() + '/versions/phplib.conf'
+ phplib = json.loads(mw.readFile(libpath))
+
+ libs = []
+ tasks = mw.M('tasks').where("status!=?", ('1',)).field('status,name').select()
+ for lib in phplib:
+ lib['task'] = '1'
+ for task in tasks:
+ tmp = mw.getStrBetween('[', ']', task['name'])
+ if not tmp:
+ continue
+ tmp1 = tmp.split('-')
+ if tmp1[0].lower() == lib['name'].lower():
+ lib['task'] = task['status']
+ lib['phpversions'] = []
+ lib['phpversions'].append(tmp1[1].replace('.',''))
+ if content.find(lib['check']) == -1:
+ lib['status'] = False
+ else:
+ lib['status'] = True
+ libs.append(lib)
+ return mw.returnJson(True, 'OK!', libs)
+
+
+def installLib(version):
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ name = args['name']
+ cmd = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + version + ' install ' + name
+ install_name = '安装PHPAPT[' + name + '-' + version + ']'
+ import thisdb
+ thisdb.addTask(name=install_name,cmd=cmd)
+
+ mw.triggerTask()
+ return mw.returnJson(True, '已将下载任务添加到队列!')
+
+
+def uninstallLib(version):
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ name = args['name']
+ execstr = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + version + ' uninstall ' + name
+
+ data = mw.execShell(execstr)
+ # data[0] == '' and
+ if data[1] == '':
+ return mw.returnJson(True, '已经卸载成功!')
+ else:
+ return mw.returnJson(False, '卸载错误信息!:' + data[1])
+
+def getConfAppStart():
+ pstart = mw.getServerDir() + '/php-apt/app_start.php'
+ return pstart
+
+def opcacheBlacklistFile():
+ op_bl = mw.getServerDir() + '/php-apt/opcache-blacklist.txt'
+ return op_bl
+
+def installPreInspection(version):
+ sys = mw.execShell(
+ "cat /etc/*-release | grep PRETTY_NAME |awk -F = '{print $2}' | awk -F '\"' '{print $2}'| awk '{print $1}'")
+
+ if sys[1] != '':
+ return '不支持改系统'
+
+ sys_id = mw.execShell(
+ "cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F '\"' '{print $2}'")
+
+ sysName = sys[0].strip().lower()
+ sysId = sys_id[0].strip()
+
+ if not sysName in ('debian', 'ubuntu'):
+ return '仅支持debian,ubuntu'
+
+ return 'ok'
+
+if __name__ == "__main__":
+
+ if len(sys.argv) < 3:
+ print('missing parameters')
+ exit(0)
+
+ func = sys.argv[1]
+
+ inputVer = sys.argv[2]
+ version = inputVer[0] + '.' + inputVer[1]
+
+ if func == 'status':
+ print(status(version))
+ elif func == 'start':
+ print(start(version))
+ elif func == 'stop':
+ print(stop(version))
+ elif func == 'restart':
+ print(restart(version))
+ elif func == 'reload':
+ print(reload(version))
+ elif func == 'install_pre_inspection':
+ print(installPreInspection(version))
+ elif func == 'initd_status':
+ print(initdStatus(version))
+ elif func == 'initd_install':
+ print(initdInstall(version))
+ elif func == 'initd_uninstall':
+ print(initdUinstall(version))
+ elif func == 'fpm_log':
+ print(fpmLog(version))
+ elif func == 'fpm_slow_log':
+ print(fpmSlowLog(version))
+ elif func == 'conf':
+ print(getConf(version))
+ elif func == 'app_start':
+ print(getConfAppStart())
+ elif func == 'opcache_blacklist_file':
+ print(opcacheBlacklistFile())
+ elif func == 'get_php_conf':
+ print(getPhpConf(version))
+ elif func == 'get_fpm_conf_file':
+ print(getFpmConfFile(version))
+ elif func == 'get_fpm_file':
+ print(getFpmFile(version))
+ elif func == 'submit_php_conf':
+ print(submitPhpConf(version))
+ elif func == 'get_limit_conf':
+ print(getLimitConf(version))
+ elif func == 'set_max_time':
+ print(setMaxTime(version))
+ elif func == 'set_max_size':
+ print(setMaxSize(version))
+ elif func == 'get_fpm_conf':
+ print(getFpmConfig(version))
+ elif func == 'set_fpm_conf':
+ print(setFpmConfig(version))
+ elif func == 'get_fpm_status':
+ print(getFpmStatus(version))
+ elif func == 'get_session_conf':
+ print(getSessionConf(version))
+ elif func == 'set_session_conf':
+ print(setSessionConf(version))
+ elif func == 'get_session_count':
+ print(getSessionCount(version))
+ elif func == 'clean_session_old':
+ print(cleanSessionOld(version))
+ elif func == 'get_disable_func':
+ print(getDisableFunc(version))
+ elif func == 'set_disable_func':
+ print(setDisableFunc(version))
+ elif func == 'get_phpinfo':
+ print(getPhpinfo(version))
+ elif func == 'get_lib_conf':
+ print(getLibConf(version))
+ elif func == 'install_lib':
+ print(installLib(version))
+ elif func == 'uninstall_lib':
+ print(uninstallLib(version))
+ else:
+ print("fail")
diff --git a/plugins/php-apt/index_php_apt.py b/plugins/php-apt/index_php_apt.py
new file mode 100755
index 000000000..15eaed2b6
--- /dev/null
+++ b/plugins/php-apt/index_php_apt.py
@@ -0,0 +1,171 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import shutil
+
+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
+
+if mw.isAppleSystem():
+ cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\''
+ info = mw.execShell(cmd)
+ p = "/usr/local/lib/" + info[0].strip() + "/site-packages"
+ sys.path.append(p)
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'php-apt'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return '/etc/php'
+
+
+def getInitDFile(version):
+ 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() + version
+
+
+def getConf(version):
+ path = getServerDir() + '/' + version + '/fpm/php.ini'
+ return path
+
+
+def getFpmConfFile(version):
+ return getServerDir() + '/' + version + '/fpm/pool.d/mw.conf'
+
+
+def status_progress(version):
+ # ps -ef|grep 'php/81' |grep -v grep | grep -v python | awk '{print $2}
+ cmd = "ps aux|grep 'php/" + version + \
+ "' |grep -v grep | grep -v python | awk '{print $2}'"
+ data = mw.execShell(cmd)
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def getPhpSocket(version):
+ path = getFpmConfFile(version)
+ content = mw.readFile(path)
+ rep = r'listen\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+
+def status(version):
+ '''
+ sock文件判断是否启动
+ '''
+ sock = getPhpSocket(version)
+ if sock.find(':'):
+ return status_progress(version)
+
+ if not os.path.exists(sock):
+ return 'stop'
+ return 'start'
+
+
+def getFpmAddress(version):
+ fpm_address = '/run/php/php{}-fpm.sock'.format(version)
+ php_fpm_file = getFpmConfFile(version)
+ try:
+ content = readFile(php_fpm_file)
+ tmp = re.findall(r"listen\s*=\s*(.+)", content)
+ if not tmp:
+ return fpm_address
+ if tmp[0].find('sock') != -1:
+ return fpm_address
+ if tmp[0].find(':') != -1:
+ listen_tmp = tmp[0].split(':')
+ if bind:
+ fpm_address = (listen_tmp[0], int(listen_tmp[1]))
+ else:
+ fpm_address = ('127.0.0.1', int(listen_tmp[1]))
+ else:
+ fpm_address = ('127.0.0.1', int(tmp[0]))
+ return fpm_address
+ except:
+ return fpm_address
+
+
+def getPhpinfo(version):
+ stat = status(version)
+ if stat == 'stop':
+ return 'PHP[' + version + ']未启动,不可访问!!!'
+
+ sock_file = getFpmAddress(version)
+ # print(sock_file)
+ root_dir = mw.getFatherDir() + '/phpinfo'
+
+ mw.execShell("rm -rf " + root_dir)
+ mw.execShell("mkdir -p " + root_dir)
+ mw.writeFile(root_dir + '/phpinfo.php', '')
+ sock_data = mw.requestFcgiPHP(sock_file, '/phpinfo.php', root_dir)
+ os.system("rm -rf " + root_dir)
+ phpinfo = str(sock_data, encoding='utf-8')
+ return phpinfo
+
+
+def libConfCommon(version):
+ fname = getConf(version)
+ if not os.path.exists(fname):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+
+ phpini = mw.readFile(fname)
+
+ libpath = getPluginDir() + '/versions/phplib.conf'
+ phplib = json.loads(mw.readFile(libpath))
+
+ libs = []
+ tasks = mw.M('tasks').where("status!=?", ('1',)).field('status,name').select()
+ for lib in phplib:
+ lib['task'] = '1'
+ for task in tasks:
+ tmp = mw.getStrBetween('[', ']', task['name'])
+ if not tmp:
+ continue
+ tmp1 = tmp.split('-')
+ if tmp1[0].lower() == lib['name'].lower():
+ lib['task'] = task['status']
+ lib['phpversions'] = []
+ lib['phpversions'].append(tmp1[1])
+ if phpini.find(lib['check']) == -1:
+ lib['status'] = False
+ else:
+ lib['status'] = True
+ libs.append(lib)
+ return libs
+
+
+def get_php_info(args):
+ version = args['version']
+ apt_ver = version[0:1]+'.'+version[1:2]
+ return getPhpinfo(apt_ver)
+
+
+def get_lib_conf(data):
+ libs = libConfCommon(data['version'])
+ return mw.returnData(True, 'OK!', libs)
diff --git a/plugins/php-apt/info.json b/plugins/php-apt/info.json
new file mode 100755
index 000000000..d13b200c6
--- /dev/null
+++ b/plugins/php-apt/info.json
@@ -0,0 +1,19 @@
+{
+ "sort": 7,
+ "ps": "PHP是世界上最好的编程语言(极速安装)",
+ "shell": "install.sh",
+ "name": "php-apt",
+ "title": "PHP[APT]",
+ "coexist": true,
+ "versions": ["56","70","71","72","73","74","80","81","82","83","84", "85"],
+ "install_pre_inspection":true,
+ "tip": "soft",
+ "checks": "server/php-apt/VERSION",
+ "path": "server/php-apt/VERSION",
+ "display": 1,
+ "author": "Zend",
+ "date": "2022-07-07",
+ "home": "https://www.php.net",
+ "type": "PHP语言解释器",
+ "pid": "6"
+}
\ No newline at end of file
diff --git a/plugins/php-apt/install.sh b/plugins/php-apt/install.sh
new file mode 100755
index 000000000..ac4eefb2b
--- /dev/null
+++ b/plugins/php-apt/install.sh
@@ -0,0 +1,127 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+# cd /www/server/mdserver-web/plugins/php-apt && bash install.sh install 56
+
+if id www &> /dev/null ;then
+ echo "www uid is `id -u www`"
+ echo "www shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `"
+else
+ groupadd www
+ useradd -g www -s /sbin/nologin www
+ # useradd -g www -s /bin/bash www
+fi
+
+_os=`uname`
+if [ ${_os} == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eqi "Debian" /etc/issue || grep -Eq "Debian" /etc/*-release; then
+ OSNAME='debian'
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/*-release; then
+ OSNAME='ubuntu'
+else
+ OSNAME='unknow'
+fi
+
+action=$1
+type=$2
+apt_ver=${type:0:1}.${type:1:2}
+
+if [ "${2}" == "" ];then
+ echo '缺少安装脚本...'
+ exit 0
+fi
+
+if [ ! -d $curPath/versions/$2 ];then
+ echo '缺少安装脚本2...'
+ exit 0
+fi
+
+
+if [ "$OSNAME" == "ubuntu" ];then
+ find_source=`ls /etc/apt/sources.list.d | grep ondrej-ubuntu-php`
+ if [ "$find_source" == "" ];then
+ echo "y" | LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php && apt update -y
+ fi
+fi
+# apt install $(grep-aptavail -S PHP-defaults -s Package -n)
+
+
+if [ ! -f /etc/apt/sources.list.d/php.list ] && [ "$OSNAME" == "debian" ];then
+ # install php source
+ apt install -y apt-transport-https lsb-release ca-certificates curl
+ cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ if [ ! -z "$cn" ];then
+ curl -o /usr/share/keyrings/deb.sury.org-php.gpg https://mirror.sjtu.edu.cn/sury/php/apt.gpg
+ sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://mirror.sjtu.edu.cn/sury/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
+ else
+ curl -o /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg
+ sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
+ fi
+ apt update -y
+fi
+
+
+if [ "${action}" == "uninstall" ] && [ -d ${serverPath}/php-apt/${type} ];then
+ #初始化
+ cd ${rootPath} && python3 ${rootPath}/plugins/php-apt/index.py stop ${type}
+ cd ${rootPath} && python3 ${rootPath}/plugins/php-apt/index.py initd_uninstall ${type}
+
+ if [ -f /lib/systemd/system/php${apt_ver}-fpm.service ];then
+ rm -rf /lib/systemd/system/php${apt_ver}-fpm.service
+ fi
+
+ if [ -f /lib/systemd/system/system/php${apt_ver}-fpm.service ];then
+ rm -rf /lib/systemd/system/php${apt_ver}-fpm.service
+ fi
+
+ systemctl daemon-reload
+fi
+
+cd ${curPath} && sh -x $curPath/versions/$2/install.sh $1
+
+if [ "${action}" == "install" ] && [ -d ${serverPath}/php-apt/${type} ];then
+ apt update -y
+
+ #初始化
+ cd ${rootPath} && python3 ${rootPath}/plugins/php-apt/index.py start ${type}
+ cd ${rootPath} && python3 ${rootPath}/plugins/php-apt/index.py restart ${type}
+ cd ${rootPath} && python3 ${rootPath}/plugins/php-apt/index.py initd_install ${type}
+
+ # 安装通用扩展
+ echo "install PHP-APT[${type}] extend start"
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install curl
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install gd
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install iconv
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install exif
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install intl
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install xml
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install mcrypt
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install bcmath
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install mysqlnd
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install mysql
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install gettext
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install redis
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install memcached
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install mbstring
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install zip
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install mongodb
+ cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${apt_ver} install opcache
+ echo "install PHP-APT[${type}] extend end"
+
+ if [ ! -f /usr/local/bin/composer ];then
+ cd /tmp
+ curl -sS https://getcomposer.org/installer | /usr/bin/php${apt_ver}
+ mv composer.phar /usr/local/bin/composer
+ fi
+
+ systemctl restart php${apt_ver}-fpm
+fi
+
+
diff --git a/plugins/php-apt/js/php.js b/plugins/php-apt/js/php.js
new file mode 100755
index 000000000..3f1d4055c
--- /dev/null
+++ b/plugins/php-apt/js/php.js
@@ -0,0 +1,747 @@
+function phpPost(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'php-apt';
+ req_data['func'] = method;
+ req_data['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ //错误展示10S
+ layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function phpPostCallbak(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'php-apt';
+ req_data['func'] = method;
+ req_data['script']='index_php_apt';
+ args['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+//配置修改
+function phpSetConfig(version) {
+ phpPost('get_php_conf', version,'',function(data){
+ // console.log(data);
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata);
+ var mlist = '';
+ for (var i = 0; i < rdata.length; i++) {
+ var w = '70'
+ if (rdata[i].name == 'error_reporting') w = '250';
+ var ibody = ' ';
+ switch (rdata[i].type) {
+ case 0:
+ var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
+ var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
+ ibody = '开启 关闭 '
+ break;
+ case 1:
+ var selected_1 = (rdata[i].value == 'On') ? 'selected' : '';
+ var selected_0 = (rdata[i].value == 'Off') ? 'selected' : '';
+ ibody = '开启 关闭 '
+ break;
+ }
+ mlist += '' + rdata[i].name + ' ' + ibody + ', ' + rdata[i].ps + '
'
+ }
+ var phpCon = '\
+ ' + mlist + '\
+
刷新 保存
\
+
'
+ $(".soft-man-con").html(phpCon);
+ });
+}
+
+
+//提交PHP配置
+function submitConf(version) {
+ var data = {
+ version: version,
+ display_errors: $("select[name='display_errors']").val(),
+ 'cgi.fix_pathinfo': $("select[name='cgi.fix_pathinfo']").val(),
+ 'date.timezone': $("input[name='date.timezone']").val(),
+ short_open_tag: $("select[name='short_open_tag']").val(),
+ asp_tags: $("select[name='asp_tags']").val() || 'On',
+ safe_mode: $("select[name='safe_mode']").val(),
+ max_execution_time: $("input[name='max_execution_time']").val(),
+ max_input_time: $("input[name='max_input_time']").val(),
+ max_input_vars: $("input[name='max_input_vars']").val(),
+ memory_limit: $("input[name='memory_limit']").val(),
+ post_max_size: $("input[name='post_max_size']").val(),
+ file_uploads: $("select[name='file_uploads']").val(),
+ upload_max_filesize: $("input[name='upload_max_filesize']").val(),
+ max_file_uploads: $("input[name='max_file_uploads']").val(),
+ default_socket_timeout: $("input[name='default_socket_timeout']").val(),
+ error_reporting: $("input[name='error_reporting']").val() || 'On'
+ };
+
+ phpPost('submit_php_conf', version, data, function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ // console.log(rdata);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+//php超时限制
+function phpCommonFunc(version){
+ phpPost('get_limit_conf', version, '', function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ var con = '\
+ 超时限制 \
+ , 秒\
+ 保存 \
+
';
+
+ con += '\
+ 上传限制 \
+ ,MB\
+ 保存 \
+
';
+
+ // 预加载脚本 \
+ con += '\
+ 查看phpinfo() \
+ 预加载脚本 \
+ OPCACHE黑名单 \
+ PHP-FPM(global) \
+
';
+
+ $(".soft-man-con").html(con);
+ });
+}
+
+//设置超时限制
+function setPHPMaxTime(version) {
+ var max = $(".phpTimeLimit").val();
+ phpPost('set_max_time',version,{'time':max},function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg,function(){
+ phpCommonFunc(version);
+ },{ icon: rdata.status ? 1 : 2 });
+
+ });
+}
+//设置PHP上传限制
+function setPHPMaxSize(version) {
+ max = $(".phpUploadLimit").val();
+ if (max < 2) {
+ alert(max);
+ layer.msg('上传大小限制不能小于2M', { icon: 2 });
+ return;
+ }
+
+ phpPost('set_max_size',version,{'max':max},function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+function phpPreload(version){
+ phpPost('app_start',version,{},function(data){
+ onlineEditFile(0, data['data']);
+ });
+}
+
+function phpOpcacheBlacklist(version){
+ phpPost('opcache_blacklist_file',version,{},function(data){
+ onlineEditFile(0, data['data']);
+ });
+}
+
+function phpFpmRoot(version){
+ phpPost('get_fpm_file',version,{},function(data){
+ onlineEditFile(0, data['data']);
+ });
+}
+
+function getFpmConfig(version, pool = 'www'){
+ phpPost('get_fpm_conf', version, {'pool':pool}, function(data){
+ // console.log(data);
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata);
+ var limitList = "自定义 " +
+ "2并发 " +
+ "5并发 " +
+ "10并发 " +
+ "30并发 " +
+ "50并发 " +
+ "100并发 " +
+ "200并发 " +
+ "300并发 " +
+ "500并发 " +
+ "2000并发 ";
+ var pms = [{ 'name': 'static', 'title': '静态' }, { 'name': 'dynamic', 'title': '动态' },{ 'name': 'ondemand', 'title': '按需' }];
+ var pmList = '';
+ for (var i = 0; i < pms.length; i++) {
+ pmList += '' + pms[i].title + ' ';
+ }
+
+ var poolHtml = "www " +
+ "backup ";
+
+ var body = "" +
+ "
应用池[pool]: " + poolHtml + "
" +
+ "
并发方案: " + limitList + "
" +
+ "
运行模式: " + pmList + " *PHP-FPM运行模式
" +
+ "
max_children: *允许创建的最大子进程数
" +
+ "
start_servers: *起始进程数(服务启动后初始进程数量)
" +
+ "
min_spare_servers: *最小空闲进程数(清理空闲进程后的保留数量)
" +
+ "
max_spare_servers: *最大空闲进程数(当空闲进程达到此值时清理)
" +
+ "
保存
" +
+ "
";
+
+ $(".soft-man-con").html(body);
+ $("select[name='limit']").change(function() {
+ var type = $(this).val();
+ var max_children = rdata.max_children;
+ var start_servers = rdata.start_servers;
+ var min_spare_servers = rdata.min_spare_servers;
+ var max_spare_servers = rdata.max_spare_servers;
+ switch (type) {
+ case '0':
+ max_children = 2;
+ start_servers = 1;
+ min_spare_servers = 1;
+ max_spare_servers = 2;
+ break;
+ case '1':
+ max_children = 5;
+ start_servers = 2;
+ min_spare_servers = 1;
+ max_spare_servers = 5;
+ break;
+ case '2':
+ max_children = 10;
+ start_servers = 2;
+ min_spare_servers = 1;
+ max_spare_servers = 10;
+ break;
+ case '3':
+ max_children = 30;
+ start_servers = 5;
+ min_spare_servers = 5;
+ max_spare_servers = 20;
+ break;
+ case '4':
+ max_children = 50;
+ start_servers = 15;
+ min_spare_servers = 15;
+ max_spare_servers = 35;
+ break;
+ case '5':
+ max_children = 100;
+ start_servers = 20;
+ min_spare_servers = 20;
+ max_spare_servers = 70;
+ break;
+ case '6':
+ max_children = 200;
+ start_servers = 25;
+ min_spare_servers = 25;
+ max_spare_servers = 150;
+ break;
+ case '7':
+ max_children = 300;
+ start_servers = 30;
+ min_spare_servers = 30;
+ max_spare_servers = 180;
+ break;
+ case '8':
+ max_children = 500;
+ start_servers = 35;
+ min_spare_servers = 35;
+ max_spare_servers = 250;
+ break;
+ case '9':
+ max_children = 2000;
+ start_servers = 40;
+ min_spare_servers = 40;
+ max_spare_servers = 255;
+ break;
+ }
+
+ $("input[name='max_children']").val(max_children);
+ $("input[name='start_servers']").val(start_servers);
+ $("input[name='min_spare_servers']").val(min_spare_servers);
+ $("input[name='max_spare_servers']").val(max_spare_servers);
+ });
+
+ $('select[name="pool"]').change(function(){
+ var pool = $(this).val();
+ getFpmConfig(version, pool);
+ });
+ });
+}
+
+function setFpmConfig(version){
+ var max_children = Number($("input[name='max_children']").val());
+ var start_servers = Number($("input[name='start_servers']").val());
+ var min_spare_servers = Number($("input[name='min_spare_servers']").val());
+ var max_spare_servers = Number($("input[name='max_spare_servers']").val());
+ var pm = $("select[name='pm']").val();
+
+ if (max_children < max_spare_servers) {
+ layer.msg('max_spare_servers 不能大于 max_children', { icon: 2 });
+ return;
+ }
+
+ if (min_spare_servers > start_servers) {
+ layer.msg('min_spare_servers 不能大于 start_servers', { icon: 2 });
+ return;
+ }
+
+ if (max_spare_servers < min_spare_servers) {
+ layer.msg('min_spare_servers 不能大于 max_spare_servers', { icon: 2 });
+ return;
+ }
+
+ if (max_children < start_servers) {
+ layer.msg('start_servers 不能大于 max_children', { icon: 2 });
+ return;
+ }
+
+ if (max_children < 1 || start_servers < 1 || min_spare_servers < 1 || max_spare_servers < 1) {
+ layer.msg('配置值不能小于1', { icon: 2 });
+ return;
+ }
+
+ var data = {
+ version:version,
+ max_children:max_children,
+ start_servers:start_servers,
+ min_spare_servers:min_spare_servers,
+ max_spare_servers:max_spare_servers,
+ pm:pm,
+ };
+ phpPost('set_fpm_conf', version, data, function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+
+function getFpmStatus(version){
+ phpPost('get_fpm_status', version, '', function(ret_data){
+ var tmp_data = $.parseJSON(ret_data.data);
+ if(!tmp_data.status){
+ layer.msg(tmp_data.msg, { icon: tmp_data.status ? 1 : 2 });
+ return;
+ }
+
+ var rdata = tmp_data.data;
+ var php_fpm_status = '动态';
+ if (rdata['process manager'] == 'dynamic'){
+ php_fpm_status = '动态';
+ } else if(rdata['process manager'] == 'static'){
+ php_fpm_status = '静态';
+ } else if(rdata['process manager'] == 'ondemand'){
+ php_fpm_status = '按需';
+ }
+
+
+ var con = "\
+ 应用池(pool) " + rdata.pool + " \
+ 进程管理方式(process manager) " + php_fpm_status + " \
+ 启动日期(start time) " + rdata['start time'] + " \
+ 请求数(accepted conn) " + rdata['accepted conn'] + " \
+ 请求队列(listen queue) " + rdata['listen queue'] + " \
+ 最大等待队列(max listen queue) " + rdata['max listen queue'] + " \
+ socket队列长度(listen queue len) " + rdata['listen queue len'] + " \
+ 空闲进程数量(idle processes) " + rdata['idle processes'] + " \
+ 活跃进程数量(active processes) " + rdata['active processes'] + " \
+ 总进程数量(total processes) " + rdata['total processes'] + " \
+ 最大活跃进程数量(max active processes) " + rdata['max active processes'] + " \
+ 到达进程上限次数(max children reached) " + rdata['max children reached'] + " \
+ 慢请求数量(slow requests) " + rdata['slow requests'] + " \
+
";
+ $(".soft-man-con").html(con);
+ $(".GetPHPStatus td,.GetPHPStatus th").css("padding", "7px");
+ });
+}
+
+//禁用函数
+function disableFunc(version) {
+ phpPost('get_disable_func', version,'',function(data){
+ var rdata = $.parseJSON(data.data);
+ var disable_functions = rdata.disable_functions.split(',');
+ var dbody = ''
+ for (var i = 0; i < disable_functions.length; i++) {
+ if (disable_functions[i] == '') continue;
+ dbody += "" + disable_functions[i] + " 删除 ";
+ }
+
+ var con = "" +
+ " " +
+ "添加 " +
+ "
" +
+ "" +
+ "名称 操作 " +
+ "" + dbody + " " +
+ "
";
+
+ con += '\
+ 在此处可以禁用指定函数的调用,以增强环境安全性! \
+ 强烈建议禁用如exec,system等危险函数! \
+ ';
+
+ $(".soft-man-con").html(con);
+ });
+}
+
+
+
+function getSessionConfig(version){
+ phpPost('get_session_conf', version, '', function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ if(!rdata.status){
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ return;
+ }
+ var rdata = rdata.data;
+
+ var cacheList = "files " +
+ "redis " +
+ "memcache " +
+ "memcached ";
+
+
+ var info = rdata.save_path.split(":");
+ var con = "" +
+ "
存储模式: " + cacheList + "
" +
+ "
IP地址:
" +
+ "
端口:
" +
+ "
密码:
" +
+ "
保存
" +
+ "
\
+ \
+ 若你的站点并发比较高,使用Redis,Memcache能有效提升PHP并发能力 \
+ 若调整Session模式后,网站访问异常,请切换回原来的模式 \
+ 切换Session模式会使在线的用户会话丢失,请在流量小的时候切换 \
+ \
+ \
+
\
+ ";
+
+ $(".soft-man-con").html(con);
+
+ if (rdata.save_handler == 'files'){
+ $('input[name="ip"]').attr('disabled','disabled');
+ $('input[name="port"]').attr('disabled','disabled');
+ $('input[name="passwd"]').attr('placeholder','如果没有密码留空');
+ $('input[name="passwd"]').attr('disabled','disabled');
+ }
+
+ // change event
+ $("select[name='save_handler']").change(function() {
+ var type = $(this).val();
+
+ var passwd = $('input[name="passwd"]').val();
+ if (passwd == ""){
+ $('input[name="passwd"]').attr('placeholder','如果没有密码留空');
+ }
+
+ var ip = $('input[name="ip"]').val();
+ if (ip == ""){
+ $('input[name="ip"]').val('127.0.0.1');
+ }
+
+ switch (type) {
+ case 'redis':
+ var port = $('input[name="port"]').val();
+ if (port == ""){
+ $('input[name="port"]').val('6379');
+ }
+ $('input[name="ip"]').removeAttr('disabled');
+ $('input[name="port"]').removeAttr('disabled');
+ $('input[name="passwd"]').removeAttr('disabled');
+ break;
+ case 'files':
+ $('input[name="ip"]').val("").attr('disabled','disabled');
+ $('input[name="port"]').val("").attr('disabled','disabled');
+ $('input[name="passwd"]').val("").attr('disabled','disabled');
+ break;
+ case 'memcache':
+ var port = $('input[name="port"]').val();
+ if (port == ""){
+ $('input[name="port"]').val('11211');
+ }
+ $('input[name="ip"]').removeAttr('disabled');
+ $('input[name="port"]').removeAttr('disabled');
+ $('input[name="passwd"]').removeAttr('disabled');
+ break;
+ case 'memcached':
+ var port = $('input[name="port"]').val();
+ if (port == ""){
+ $('input[name="port"]').val('11211');
+ }
+ $('input[name="ip"]').removeAttr('disabled');
+ $('input[name="port"]').removeAttr('disabled');
+ $('input[name="passwd"]').removeAttr('disabled');
+ break;
+ }
+ });
+
+ //load session stats
+ phpPost('get_session_count', version, '', function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ if(!rdata.status){
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ return;
+ }
+ var rdata = rdata.data;
+
+ var html_var = "清理Session文件
\
+ \
+
\
+
总Session文件数量 "+rdata.total+"
\
+
可清理的Session文件数量 "+rdata.oldfile+"
\
+
\
+
清理session文件 ";
+
+ $("#session_clear").html(html_var);
+
+
+ $('#clean_func').click(function(){
+ phpPost('clean_session_old', version, '', function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ showMsg(rdata.msg,function(){
+ getSessionConfig(version);
+ },{ icon: rdata.status ? 1 : 2 });
+ });
+ });
+ });
+ });
+
+}
+
+function setSessionConfig(version){
+ var ip = $('input[name="ip"]').val();
+ var port = $('input[name="port"]').val();
+ var passwd = $('input[name="passwd"]').val();
+ var save_handler = $("select[name='save_handler']").val();
+ var data = {
+ ip:ip,
+ port:port,
+ passwd:passwd,
+ save_handler:save_handler,
+ };
+ phpPost('set_session_conf', version, data, function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+
+//设置禁用函数
+function setDisableFunc(version, act, fs) {
+ var fsArr = fs.split(',');
+ if (act == 1) {
+ var functions = $("#disable_function_val").val();
+ for (var i = 0; i < fsArr.length; i++) {
+ if (functions == fsArr[i]) {
+ layer.msg(lan.soft.fun_msg, { icon: 5 });
+ return;
+ }
+ }
+ fs += ',' + functions;
+ msg = '添加成功';
+ } else {
+
+ fs = '';
+ for (var i = 0; i < fsArr.length; i++) {
+ if (act == fsArr[i]) continue;
+ fs += fsArr[i] + ','
+ }
+ msg = '删除成功';
+ fs = fs.substr(0, fs.length - 1);
+ }
+
+ var data = {
+ 'version':version,
+ 'disable_functions':fs,
+ };
+
+ phpPost('set_disable_func', version,data,function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.status ? msg : rdata.msg, function(){
+ disableFunc(version);
+ } ,{ icon: rdata.status ? 1 : 2 });
+ });
+}
+
+
+//phpinfo
+function getPhpinfo(version) {
+ var con = '
查看phpinfo() ';
+ $(".soft-man-con").html(con);
+}
+
+//获取PHPInfo
+function getPHPInfo_old(version) {
+ phpPost('get_phpinfo', version, '', function(data){
+ var rdata = data.data;
+ layer.open({
+ type: 1,
+ title: "PHP-" + version + "-PHPINFO",
+ area: ['90%', '90%'],
+ closeBtn: 2,
+ shadeClose: true,
+ content: rdata
+ });
+ });
+}
+
+function getPHPInfo(version) {
+ phpPostCallbak('get_php_info', version, {}, function(data){
+ if (!data.status){
+ layer.msg(rdata.msg, { icon: 2 });
+ return;
+ }
+
+ layer.open({
+ type: 1,
+ title: "PHP-" + version + "-PHPINFO",
+ area: ['70%', '90%'],
+ closeBtn: 2,
+ shadeClose: true,
+ content: data.data.replace('a:link {color: #009; text-decoration: none; background-color: #fff;}', '').replace('a:link {color: #000099; text-decoration: none; background-color: #ffffff;}', '')
+ });
+ })
+}
+
+
+
+function phpLibConfig(version){
+
+ phpPost('get_lib_conf', version, '', function(data){
+ var rdata = $.parseJSON(data.data);
+
+ if (!rdata.status){
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ return;
+ }
+
+ var libs = rdata.data;
+ var body = '';
+ var opt = '';
+
+ for (var i = 0; i < libs.length; i++) {
+ if (libs[i].versions.indexOf(version) == -1){
+ continue;
+ }
+
+ if (libs[i]['task'] == '-1' && libs[i].phpversions.indexOf(version) != -1) {
+ opt = '
安装. '
+ } else if (libs[i]['task'] == '0' && libs[i].phpversions.indexOf(version) != -1) {
+ opt = '
等待. '
+ } else if (libs[i].status) {
+ opt = '
卸载 '
+ } else {
+ opt = '
安装 '
+ }
+
+ body += '
' +
+ '' + libs[i].name + ' ' +
+ '' + libs[i].type + ' ' +
+ '' + libs[i].msg + ' ' +
+ ' ' +
+ '' + opt + ' ' +
+ ' ';
+ }
+
+
+ var con = '
' +
+ '
' +
+ '' +
+ '' +
+ '名称 ' +
+ '类型 ' +
+ '说明 ' +
+ '状态 ' +
+ '操作 ' +
+ ' ' +
+ ' ' +
+ '' + body + ' ' +
+ '
' +
+ '
' +
+ '
\
+ 请按实际需求安装扩展,不要安装不必要的PHP扩展,这会影响PHP执行效率,甚至出现异常 \
+ Redis扩展只允许在1个PHP版本中使用,安装到其它PHP版本请在[软件管理]重装Redis \
+ opcache/xcache/apc等脚本缓存扩展,请只安装其中1个,否则可能导致您的站点程序异常 \
+ ioncube要在ZendGuardLoader/opcache前安装,否则可能导致您的站点程序异常 \
+ ';
+ $('.soft-man-con').html(con);
+ });
+
+}
+
+//安装扩展
+function installPHPLib(version, name, title, pathinfo) {
+ layer.confirm('您真的要卸载{1}吗?'.replace('{1}', name), { icon: 3, closeBtn: 2 }, function() {
+ name = name.toLowerCase();
+ var data = "name=" + name + "&version=" + version + "&type=1";
+
+ phpPost('install_lib', version, data, function(data){
+ var rdata = $.parseJSON(data.data);
+ // layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ showMsg(rdata.msg, function(){
+ getTaskCount();
+ phpLibConfig(version);
+ },{ icon: rdata.status ? 1 : 2 });
+
+ });
+ });
+}
+
+//卸载扩展
+function uninstallPHPLib(version, name, title, pathinfo) {
+ layer.confirm('您真的要安装{1}吗?'.replace('{1}', name), { icon: 3, closeBtn: 2 }, function() {
+ name = name.toLowerCase();
+ var data = 'name=' + name + '&version=' + version;
+ phpPost('uninstall_lib', version, data, function(data){
+ var rdata = $.parseJSON(data.data);
+ // layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ showMsg(rdata.msg, function(){
+ getTaskCount();
+ phpLibConfig(version);
+ },{ icon: rdata.status ? 1 : 2 },5000);
+
+ });
+ });
+}
\ No newline at end of file
diff --git a/plugins/php-apt/versions/56/install.sh b/plugins/php-apt/versions/56/install.sh
new file mode 100755
index 000000000..ba3973dc5
--- /dev/null
+++ b/plugins/php-apt/versions/56/install.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+
+#获取信息和版本
+# bash /www/server/mdsever-web/scripts/getos.sh
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+version=5.6
+PHP_VER=56
+
+# apt -y install php5.6 php5.6-fpm php5.6-dev
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+apt -y install php${version} php${version}-fpm php${version}-dev
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-apt/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+
+# apt -y remove php5.6 php5.6-*
+Uninstall_php()
+{
+#------------------------ uninstall start ------------------------------------#
+apt -y remove php${version} php${version}-*
+rm -rf $serverPath/php-apt/${PHP_VER}
+echo "卸载php-${version}..."
+#------------------------ uninstall start ------------------------------------#
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-apt/versions/70/install.sh b/plugins/php-apt/versions/70/install.sh
new file mode 100755
index 000000000..3631acf80
--- /dev/null
+++ b/plugins/php-apt/versions/70/install.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+#获取信息和版本
+# bash /www/server/mdsever-web/scripts/getos.sh
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+version=7.0
+PHP_VER=70
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+apt -y install php${version} php${version}-fpm php${version}-dev
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-apt/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+#------------------------ uninstall start ------------------------------------#
+apt -y remove php${version} php${version}-*
+rm -rf $serverPath/php-apt/${PHP_VER}
+echo "卸载php-${version}..."
+#------------------------ uninstall start ------------------------------------#
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-apt/versions/71/install.sh b/plugins/php-apt/versions/71/install.sh
new file mode 100755
index 000000000..c8955a33a
--- /dev/null
+++ b/plugins/php-apt/versions/71/install.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+
+#获取信息和版本
+# bash /www/server/mdsever-web/scripts/getos.sh
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+version=7.1
+PHP_VER=71
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+apt -y install php${version} php${version}-fpm php${version}-dev
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-apt/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+#------------------------ uninstall start ------------------------------------#
+apt -y remove php${version} php${version}-*
+rm -rf $serverPath/php-apt/${PHP_VER}
+echo "卸载php-${version}..."
+#------------------------ uninstall start ------------------------------------#
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-apt/versions/72/install.sh b/plugins/php-apt/versions/72/install.sh
new file mode 100755
index 000000000..816f7f4db
--- /dev/null
+++ b/plugins/php-apt/versions/72/install.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+
+#获取信息和版本
+# bash /www/server/mdsever-web/scripts/getos.sh
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+version=7.2
+PHP_VER=72
+
+
+# apt -y install php7.2 php7.2-fpm php7.2-dev
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+apt -y install php${version} php${version}-fpm php${version}-dev
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-apt/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+# systemctl status php7.2-fpm
+# apt -y remove php7.2 php7.2-fpm php7.2-dev
+Uninstall_php()
+{
+#------------------------ uninstall start ------------------------------------#
+apt -y remove php${version} php${version}-*
+rm -rf $serverPath/php-apt/${PHP_VER}
+echo "卸载php-${version}..."
+#------------------------ uninstall start ------------------------------------#
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-apt/versions/73/install.sh b/plugins/php-apt/versions/73/install.sh
new file mode 100755
index 000000000..475825e55
--- /dev/null
+++ b/plugins/php-apt/versions/73/install.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+
+#获取信息和版本
+# bash /www/server/mdsever-web/scripts/getos.sh
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+version=7.3
+PHP_VER=73
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+apt -y install php${version} php${version}-fpm php${version}-dev
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-apt/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+#------------------------ uninstall start ------------------------------------#
+apt -y remove php${version} php${version}-*
+rm -rf $serverPath/php-apt/${PHP_VER}
+echo "卸载php-${version}..."
+#------------------------ uninstall start ------------------------------------#
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-apt/versions/74/install.sh b/plugins/php-apt/versions/74/install.sh
new file mode 100755
index 000000000..956a864ff
--- /dev/null
+++ b/plugins/php-apt/versions/74/install.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+#获取信息和版本
+# bash /www/server/mdsever-web/scripts/getos.sh
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+version=7.4
+PHP_VER=74
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+apt -y install php${version} php${version}-fpm php${version}-dev
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-apt/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+#------------------------ uninstall start ------------------------------------#
+apt -y remove php${version} php${version}-*
+rm -rf $serverPath/php-apt/${PHP_VER}
+echo "卸载php-${version}..."
+#------------------------ uninstall start ------------------------------------#
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-apt/versions/80/install.sh b/plugins/php-apt/versions/80/install.sh
new file mode 100755
index 000000000..f30fba31c
--- /dev/null
+++ b/plugins/php-apt/versions/80/install.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+#获取信息和版本
+# bash /www/server/mdsever-web/scripts/getos.sh
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+version=8.0
+PHP_VER=80
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+apt -y install php${version} php${version}-fpm php${version}-dev
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-apt/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+#------------------------ uninstall start ------------------------------------#
+apt -y remove php${version} php${version}-*
+rm -rf $serverPath/php-apt/${PHP_VER}
+echo "卸载php-${version}..."
+#------------------------ uninstall start ------------------------------------#
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-apt/versions/81/install.sh b/plugins/php-apt/versions/81/install.sh
new file mode 100755
index 000000000..124c765ad
--- /dev/null
+++ b/plugins/php-apt/versions/81/install.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+#获取信息和版本
+# bash /www/server/mdsever-web/scripts/getos.sh
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+version=8.1
+PHP_VER=81
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+apt -y install php${version} php${version}-fpm php${version}-dev
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-apt/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+#------------------------ uninstall start ------------------------------------#
+apt -y remove php${version} php${version}-*
+rm -rf $serverPath/php-apt/${PHP_VER}
+echo "卸载php-${version}..."
+#------------------------ uninstall start ------------------------------------#
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-apt/versions/82/install.sh b/plugins/php-apt/versions/82/install.sh
new file mode 100755
index 000000000..b23415303
--- /dev/null
+++ b/plugins/php-apt/versions/82/install.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+#获取信息和版本
+# bash /www/server/mdsever-web/scripts/getos.sh
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+version=8.2
+PHP_VER=82
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+apt -y install php${version} php${version}-fpm php${version}-dev
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-apt/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+#------------------------ uninstall start ------------------------------------#
+apt -y remove php${version} php${version}-*
+rm -rf $serverPath/php-apt/${PHP_VER}
+echo "卸载php-${version}..."
+#------------------------ uninstall start ------------------------------------#
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-apt/versions/83/install.sh b/plugins/php-apt/versions/83/install.sh
new file mode 100755
index 000000000..e0ace4c94
--- /dev/null
+++ b/plugins/php-apt/versions/83/install.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+#获取信息和版本
+# bash /www/server/mdsever-web/scripts/getos.sh
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+version=8.3
+PHP_VER=83
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+apt -y install php${version} php${version}-fpm php${version}-dev
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-apt/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+#------------------------ uninstall start ------------------------------------#
+apt -y remove php${version} php${version}-*
+rm -rf $serverPath/php-apt/${PHP_VER}
+echo "卸载php-${version}..."
+#------------------------ uninstall start ------------------------------------#
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-apt/versions/84/install.sh b/plugins/php-apt/versions/84/install.sh
new file mode 100755
index 000000000..435102c7a
--- /dev/null
+++ b/plugins/php-apt/versions/84/install.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+#获取信息和版本
+# bash /www/server/mdsever-web/scripts/getos.sh
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+version=8.4
+PHP_VER=84
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+apt -y install php${version} php${version}-fpm php${version}-dev
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-apt/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+#------------------------ uninstall start ------------------------------------#
+apt -y remove php${version} php${version}-*
+rm -rf $serverPath/php-apt/${PHP_VER}
+echo "卸载php-${version}..."
+#------------------------ uninstall start ------------------------------------#
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-apt/versions/85/install.sh b/plugins/php-apt/versions/85/install.sh
new file mode 100755
index 000000000..ba55c8dae
--- /dev/null
+++ b/plugins/php-apt/versions/85/install.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+#获取信息和版本
+# bash /www/server/mdsever-web/scripts/getos.sh
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+version=8.5
+PHP_VER=85
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+apt -y install php${version} php${version}-fpm php${version}-dev
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-apt/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+#------------------------ uninstall start ------------------------------------#
+apt -y remove php${version} php${version}-*
+rm -rf $serverPath/php-apt/${PHP_VER}
+echo "卸载php-${version}..."
+#------------------------ uninstall start ------------------------------------#
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-apt/versions/all_test.sh b/plugins/php-apt/versions/all_test.sh
new file mode 100644
index 000000000..b74035747
--- /dev/null
+++ b/plugins/php-apt/versions/all_test.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+# cd /www/server/mdserver-web/plugins/php-apt/versions && /bin/bash all_test.sh
+# cd /www/server/mdserver-web/plugins/php-apt && bash install.sh install 85
+
+
+# cd /www/server/mdserver-web && python3 /www/server/mdserver-web/plugins/php-apt/index.py start 5.6
+
+
+# cd /www/server/mdserver-web
+# cd /www/server/mdserver-web/plugins/php-apt/versions && /bin/bash common.sh 5.6 install yaf
+# cd /www/server/mdserver-web/plugins/php-apt/versions && /bin/bash common.sh 7.1 install swoole
+
+
+PHP_VER_LIST=(56 70 71 72 73 74 80 81 82 83 84)
+for PHP_VER in ${PHP_VER_LIST[@]}; do
+ echo "php${PHP_VER} -- start"
+ if [ ! -d /www/server/php-apt/${PHP_VER} ];then
+ cd /www/server/mdserver-web/plugins/php-apt && bash install.sh install ${PHP_VER}
+ fi
+ echo "php${PHP_VER} -- end"
+done
+
+
+cd $curPath
+
+PHP_VER_LIST_EXT=(56 70 71 72 73 74 80 81 82 83 84)
+PHP_EXT_LIST=(ioncube pdo mysqlnd sqlite3 odbc opcache mcrypt fileinfo \
+ exif gd intl memcache memcached redis imagick xdebug xhprof \
+ swoole yaf phalcon yar mongodb yac solr seaslog mbstring zip zstd)
+for PHP_VER in ${PHP_VER_LIST_EXT[@]}; do
+ echo "php${PHP_VER} EXT -- start"
+ version=${PHP_VER:0:1}.${PHP_VER:1:2}
+ extVer=`bash $curPath/lib.sh $version`
+
+ for EXT in ${PHP_EXT_LIST[@]}; do
+ extFile=/usr/lib/php/${extVer}/${EXT}.so
+ echo "${PHP_VER} ${EXT} start"
+ if [ ! -f $extFile ];then
+ /bin/bash common.sh $version install ${EXT}
+ fi
+ echo "${PHP_VER} ${EXT} end"
+ done
+
+ echo "php${PHP_VER} EXT -- end"
+done
+
+
diff --git a/plugins/php-apt/versions/common.sh b/plugins/php-apt/versions/common.sh
new file mode 100644
index 000000000..42e859fb4
--- /dev/null
+++ b/plugins/php-apt/versions/common.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+version=$1
+action=$2
+extName=$3
+
+which bc
+if [ "$?" != "0" ];then
+ apt install -y bc
+fi
+
+
+# echo $1,$2,$3
+
+# echo $curPath
+# echo $rootPath
+# echo $serverPath
+
+FILE=${curPath}/${version}/${extName}.sh
+FILE_COMMON=${curPath}/common/${extName}.sh
+
+# apt install -y php81-php-yar
+if [ "$action" == 'install' ];then
+
+ if [ -f $FILE ];then
+ bash ${curPath}/${version}/${extName}.sh install $version
+ elif [ -f $FILE_COMMON ];then
+ bash ${FILE_COMMON} install ${version}
+ else
+ apt install -y php${version}-${extName}
+ fi
+fi
+
+# apt remove -y php81-php-yar
+if [ "$action" == 'uninstall' ];then
+ if [ -f $FILE ];then
+ bash ${curPath}/${version}/${extName}.sh uninstall $version
+ elif [ -f $FILE_COMMON ];then
+ bash ${FILE_COMMON} uninstall ${version}
+ else
+ apt remove -y php${version}-${extName}
+ fi
+fi
+
+echo "apt install -y php${version}-${extName}"
+echo "apt remove -y php${version}-${extName}"
+
+php_status=`systemctl status php${version}-fpm | grep inactive`
+echo "status:$php_status"
+if [ "$php_status" == "" ];then
+ systemctl restart php${version}-fpm
+fi
\ No newline at end of file
diff --git a/plugins/php-apt/versions/common/brotli.sh b/plugins/php-apt/versions/common/brotli.sh
new file mode 100755
index 000000000..89769c8e4
--- /dev/null
+++ b/plugins/php-apt/versions/common/brotli.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+sysName=`uname`
+LIBNAME=brotli
+LIBV=0.18.3
+
+extVer=`bash $curPath/lib.sh $version`
+extFile=/usr/lib/php/${extVer}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ #cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n'
+ isInstall=`cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n' |grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ /usr/bin/phpize${version}
+ ./configure --with-php-config=/usr/bin/php-config${version}
+ make && make install && make clean
+
+ fi
+ echo "$extFile checking ..."
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return;
+ fi
+
+
+ echo "" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+ echo "[${LIBNAME}]" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+ echo "extension=${LIBNAME}.so" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+
+ systemctl restart php${version}-fpm
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "/usr/bin/php-config${version}" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+
+ if [ -f /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini ];then
+ rm -rf /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+ rm -rf $extFile
+ fi
+
+ systemctl restart php${version}-fpm
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php-apt/versions/common/ioncube.sh b/plugins/php-apt/versions/common/ioncube.sh
new file mode 100755
index 000000000..9beeb3cbf
--- /dev/null
+++ b/plugins/php-apt/versions/common/ioncube.sh
@@ -0,0 +1,99 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+sysName=`uname`
+LIBNAME=ioncube
+LIBV=0
+
+if [ `echo "$version > 8.3"|bc` -eq 1 ];then
+ echo "I won't support it"
+ exit 0
+fi
+
+
+extVer=`bash $curPath/lib.sh $version`
+extFile=/usr/lib/php/${extVer}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+SORT_LIBNAME="10-${LIBNAME}"
+
+Install_lib()
+{
+
+ isInstall=`cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n' |grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -f $php_lib/ioncube_loaders_lin.tar.gz ];then
+ wget -O $php_lib/ioncube_loaders_lin.tar.gz https://downloads.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz
+ cd $php_lib && tar -zxvf ioncube_loaders_lin.tar.gz
+ fi
+ cd $php_lib/ioncube
+
+ cp -rf $php_lib/ioncube/ioncube_loader_lin_${version}.so $extFile
+
+ fi
+
+ echo "$extFile checking ..."
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return;
+ fi
+
+ echo "" >> /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini
+ echo "[${LIBNAME}]" >> /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini
+ echo "zend_extension=${LIBNAME}.so" >> /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini
+
+ systemctl restart php${version}-fpm
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "/usr/bin/php-config${version}" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+
+ if [ -f /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini ];then
+ rm -rf /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini
+ rm -rf $extFile
+ fi
+
+ systemctl restart php${version}-fpm
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php-apt/versions/common/opcache.sh b/plugins/php-apt/versions/common/opcache.sh
new file mode 100755
index 000000000..b73b83b57
--- /dev/null
+++ b/plugins/php-apt/versions/common/opcache.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+sysName=`uname`
+LIBNAME=opcache
+
+ext_dir=/etc/php/${version}/fpm/conf.d
+ext_file=${ext_dir}/10-opcache.ini
+
+echo $ext_file
+
+OP_BL=${serverPath}/server/php-apt/opcache-blacklist.txt
+if [ ! -f $OP_BL ];then
+ touch $OP_BL
+fi
+
+if [ "$actionType" == 'install' ];then
+ apt install -y php${version}-${LIBNAME}
+
+ echo "ls ${ext_dir} | grep "${LIBNAME}.ini"| cut -d \ -f 1"
+ find_opcache=`ls ${ext_dir} | grep "${LIBNAME}.ini"| cut -d \ -f 1`
+ echo $find_opcache
+ if [ "$find_opcache" != "" ];then
+ ext_file=${ext_dir}/${find_opcache}
+ fi
+ echo $ext_file
+ echo "zend_extension=${LIBNAME}.so" >> $ext_file
+ echo "opcache.enable=1" >> $ext_file
+ echo "opcache.memory_consumption=128" >> $ext_file
+ echo "opcache.interned_strings_buffer=8" >> $ext_file
+ echo "opcache.max_accelerated_files=4000" >> $ext_file
+ echo "opcache.revalidate_freq=60" >> $ext_file
+ echo "opcache.fast_shutdown=1" >> $ext_file
+ echo "opcache.enable_cli=1" >> $ext_file
+ echo "opcache.jit=1205" >> $ext_file
+ echo "opcache.jit_buffer_size=64M" >> $ext_file
+ echo "opcache.save_comments=0" >> $ext_file
+ echo "opcache.blacklist_filename=${OP_BL}" >> $ext_file
+
+elif [ "$actionType" == 'uninstall' ];then
+ rm -rf $ext_file
+ echo 'cannot uninstall'
+fi
\ No newline at end of file
diff --git a/plugins/php-apt/versions/common/phalcon.sh b/plugins/php-apt/versions/common/phalcon.sh
new file mode 100755
index 000000000..758ee0961
--- /dev/null
+++ b/plugins/php-apt/versions/common/phalcon.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+sysName=`uname`
+LIBNAME=phalcon
+LIBV=0
+
+
+# if [ `echo "$version > 7.4"|bc` -eq 1 ];then
+# echo "I won't support it"
+# exit 0
+# fi
+
+CMD='apt '
+if [ "$actionType" == 'install' ];then
+ CMD="$CMD install -y php${version}-"
+elif [ "$actionType" == 'uninstall' ];then
+ CMD="$CMD uninstall -y php${version}-"
+fi
+
+if [ "$version" == '5.6' ];then
+ CMD="${CMD}phalcon3"
+elif [[ "$version" == '7.0' ]]; then
+ CMD="${CMD}phalcon3"
+elif [[ "$version" == '7.1' ]]; then
+ CMD="${CMD}phalcon3"
+elif [[ "$version" == '7.2' ]]; then
+ CMD="${CMD}phalcon4"
+elif [[ "$version" == '7.3' ]]; then
+ CMD="${CMD}phalcon3"
+elif [[ "$version" == '7.4' ]]; then
+ CMD="${CMD}phalcon4"
+elif [[ "$version" == '8.0' ]]; then
+ CMD="${CMD}phalcon5"
+elif [[ "$version" == '8.1' ]]; then
+ CMD="${CMD}phalcon5"
+elif [[ "$version" == '8.2' ]]; then
+ CMD="${CMD}phalcon5"
+elif [[ "$version" == '8.3' ]]; then
+ CMD="${CMD}phalcon5"
+elif [[ "$version" == '8.4' ]]; then
+ CMD="${CMD}phalcon5"
+fi
+
+$CMD
+echo "$CMD"
diff --git a/plugins/php-apt/versions/common/seaslog.sh b/plugins/php-apt/versions/common/seaslog.sh
new file mode 100755
index 000000000..bf99d75b0
--- /dev/null
+++ b/plugins/php-apt/versions/common/seaslog.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+LIBNAME=SeasLog
+_LIBNAME=$(echo $LIBNAME | tr '[A-Z]' '[a-z]')
+
+sysName=`uname`
+LIBV=2.2.0
+
+extVer=`bash $curPath/lib.sh $version`
+extFile=/usr/lib/php/${extVer}/${_LIBNAME}.so
+
+
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ #cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n'
+ isInstall=`cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n' |grep "${_LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${_LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ /usr/bin/phpize${version}
+ ./configure --with-php-config=/usr/bin/php-config${version}
+ make && make install && make clean
+
+ fi
+ echo "$extFile checking ..."
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return;
+ fi
+
+ _LIBNAME=$(echo $LIBNAME | tr '[A-Z]' '[a-z]')
+ echo "" >> /etc/php/${version}/fpm/conf.d/${_LIBNAME}.ini
+ echo "[${_LIBNAME}]" >> /etc/php/${version}/fpm/conf.d/${_LIBNAME}.ini
+ echo "extension=${_LIBNAME}.so" >> /etc/php/${version}/fpm/conf.d/${_LIBNAME}.ini
+
+ systemctl restart php${version}-fpm
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "/usr/bin/php-config${version}" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ -f /etc/php/${version}/fpm/conf.d/${_LIBNAME}.ini ];then
+ rm -rf /etc/php/${version}/fpm/conf.d/${_LIBNAME}.ini
+ rm -rf $extFile
+ fi
+
+ systemctl restart php${version}-fpm
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php-apt/versions/common/sg11.sh b/plugins/php-apt/versions/common/sg11.sh
new file mode 100644
index 000000000..e10e2d3bf
--- /dev/null
+++ b/plugins/php-apt/versions/common/sg11.sh
@@ -0,0 +1,123 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+# https://www.sourceguardian.com/loaders.html
+
+# support 52-81
+
+LIBNAME=sg11
+LIBV=0
+sysName=`uname`
+actionType=$1
+version=$2
+# SG_VER=${version:0:1}.${version:1:2}
+SG_VER=${version}
+
+extVer=`bash $curPath/lib.sh $version`
+extFile=/usr/lib/php/${extVer}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+SORT_LIBNAME="10-${LIBNAME}"
+
+Install_lib()
+{
+ bash ${rootPath}/scripts/getos.sh
+ OSNAME=`cat ${rootPath}/data/osname.pl`
+
+ echo "${OSNAME}"
+
+ DEFAULT_OSNAME=linux-x86_64
+ SUFFIX_NAME=lin
+ if [ "$OSNAME" == 'macos' ];then
+ DEFAULT_OSNAME=macosx
+ SUFFIX_NAME=dar
+ fi
+
+ isInstall=`cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n' |grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ mkdir -p $php_lib/sg11
+ if [ ! -f $php_lib/sg11_loaders.tar.bz2 ];then
+ curl -sSLo $php_lib/sg11_loaders.tar.bz2 https://www.sourceguardian.com/loaders/download/loaders.tar.bz2
+ echo "cd $php_lib && tar -jxvf $php_lib/sg11_loaders.tar.bz2 -C $php_lib/sg11"
+ cd $php_lib && tar -jxvf $php_lib/sg11_loaders.tar.bz2 -C $php_lib/sg11
+ fi
+
+
+ if [ ! -d $php_lib/sg11/macosx ];then
+ cd $php_lib && tar -jxvf $php_lib/sg11_loaders.tar.bz2 -C $php_lib/sg11
+ fi
+ cd $php_lib/sg11
+ # echo "mv $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.lin $extFile"
+ if [ -f $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.${SUFFIX_NAME} ];then
+ cp -rf $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.${SUFFIX_NAME} $extFile
+ else
+ echo 'Not supported temporarily'
+ exit
+ fi
+
+ if [ "$OSNAME" == 'macos' ];then
+ xattr -c * $extFile
+ fi
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini
+ echo "[${LIBNAME}]" >> /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini
+ echo "extension=${LIBNAME}.so" >> /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini
+
+ systemctl restart php${version}-fpm
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "/usr/bin/php-config${version}" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+
+ if [ -f /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini ];then
+ rm -rf /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini
+ rm -rf $extFile
+ fi
+
+ systemctl restart php${version}-fpm
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php-apt/versions/common/swoole.sh b/plugins/php-apt/versions/common/swoole.sh
new file mode 100755
index 000000000..adfca0639
--- /dev/null
+++ b/plugins/php-apt/versions/common/swoole.sh
@@ -0,0 +1,124 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+sysName=`uname`
+LIBNAME=swoole
+LIBV=6.1.6
+
+
+APT_INSTALL=0
+
+if [ `echo "$version < 7.0"|bc` -eq 1 ];then
+ LIBV=1.10.1
+elif [ "$version" == "7.1" ];then
+ LIBV=4.5.2
+elif [ "$version" == "7.0" ];then
+ LIBV=4.3.0
+elif [ "$version" == "8.5" ];then
+ LIBV=6.1.6
+else
+ echo 'ok'
+ APT_INSTALL=1
+fi
+
+
+# to Apt
+if [ "$APT_INSTALL" == "1" ];then
+ if [ "$actionType" == 'install' ];then
+ apt install -y php${version}-${LIBNAME}
+ elif [ "$actionType" == 'uninstall' ];then
+ apt remove -y php${version}-${LIBNAME}
+ fi
+ exit 0
+fi
+
+
+extVer=`bash $curPath/lib.sh $version`
+extFile=/usr/lib/php/${extVer}/${LIBNAME}.so
+
+
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ #cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n'
+ isInstall=`cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n' |grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ /usr/bin/phpize${version}
+ ./configure --with-php-config=/usr/bin/php-config${version}
+ make && make install && make clean
+
+ fi
+ echo "$extFile checking ..."
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return;
+ fi
+
+
+ echo "" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+ echo "[${LIBNAME}]" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+ echo "extension=${LIBNAME}.so" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+
+ systemctl restart php${version}-fpm
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "/usr/bin/php-config${version}" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+
+ if [ -f /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini ];then
+ rm -rf /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+ rm -rf $extFile
+ fi
+
+ systemctl restart php${version}-fpm
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php-apt/versions/common/yaf.sh b/plugins/php-apt/versions/common/yaf.sh
new file mode 100755
index 000000000..ccbca3c91
--- /dev/null
+++ b/plugins/php-apt/versions/common/yaf.sh
@@ -0,0 +1,101 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+sysName=`uname`
+LIBNAME=yaf
+LIBV=3.3.6
+
+if [ `echo "$version < 7.0"|bc` -eq 1 ];then
+ LIBV=2.3.5
+fi
+
+extVer=`bash $curPath/lib.sh $version`
+extFile=/usr/lib/php/${extVer}/${LIBNAME}.so
+
+
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ #cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n'
+ isInstall=`cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n' |grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ /usr/bin/phpize${version}
+ ./configure --with-php-config=/usr/bin/php-config${version}
+ make && make install && make clean
+
+ fi
+ echo "$extFile checking ..."
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return;
+ fi
+
+
+ echo "" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+ echo "[${LIBNAME}]" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+ echo "extension=${LIBNAME}.so" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+ echo "${LIBNAME}.use_namespace=1" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+
+ systemctl restart php${version}-fpm
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "/usr/bin/php-config${version}" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+
+ if [ -f /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini ];then
+ rm -rf /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+ rm -rf $extFile
+ fi
+
+ systemctl restart php${version}-fpm
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php-apt/versions/common/yar.sh b/plugins/php-apt/versions/common/yar.sh
new file mode 100755
index 000000000..4e1aaa64a
--- /dev/null
+++ b/plugins/php-apt/versions/common/yar.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+sysName=`uname`
+LIBNAME=yar
+LIBV=2.3.3
+
+if [[ "$version" =~ "5.0" ]];then
+ LIBV=1.2.5
+fi
+
+if [[ "$version" =~ "7.0" ]];then
+ LIBV=2.3.3
+fi
+
+extVer=`bash $curPath/lib.sh $version`
+extFile=/usr/lib/php/${extVer}/${LIBNAME}.so
+
+
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ #cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n'
+ isInstall=`cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n' |grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ /usr/bin/phpize${version}
+ ./configure --with-php-config=/usr/bin/php-config${version}
+ make && make install && make clean
+
+ fi
+ echo "$extFile checking ..."
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return;
+ fi
+
+
+ echo "" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+ echo "[${LIBNAME}]" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+ echo "extension=${LIBNAME}.so" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+ echo "${LIBNAME}.expose_info=false" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+
+ systemctl restart php${version}-fpm
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "/usr/bin/php-config${version}" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+
+ if [ -f /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini ];then
+ rm -rf /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini
+ rm -rf $extFile
+ fi
+
+ systemctl restart php${version}-fpm
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php-apt/versions/lib.sh b/plugins/php-apt/versions/lib.sh
new file mode 100644
index 000000000..27114a858
--- /dev/null
+++ b/plugins/php-apt/versions/lib.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+version=$1
+
+if [ "$version" == '5.6' ];then
+ echo '20131226'
+elif [[ "$version" == '7.0' ]]; then
+ echo '20151012'
+elif [[ "$version" == '7.1' ]]; then
+ echo '20160303'
+elif [[ "$version" == '7.2' ]]; then
+ echo '20170718'
+elif [[ "$version" == '7.3' ]]; then
+ echo '20180731'
+elif [[ "$version" == '7.4' ]]; then
+ echo '20190902'
+elif [[ "$version" == '8.0' ]]; then
+ echo '20200930'
+elif [[ "$version" == '8.1' ]]; then
+ echo '20210902'
+elif [[ "$version" == '8.2' ]]; then
+ echo '20220829'
+elif [[ "$version" == '8.3' ]]; then
+ echo '20230831'
+elif [[ "$version" == '8.4' ]]; then
+ echo '20240924'
+elif [[ "$version" == '8.5' ]]; then
+ echo '20250925'
+fi
\ No newline at end of file
diff --git a/plugins/php-apt/versions/phplib.conf b/plugins/php-apt/versions/phplib.conf
new file mode 100755
index 000000000..eec09e6cf
--- /dev/null
+++ b/plugins/php-apt/versions/phplib.conf
@@ -0,0 +1,802 @@
+[
+ {
+ "name": "sg11",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83"
+ ],
+ "type": "脚本解密",
+ "msg": "用于解密SG11加密脚本!",
+ "shell": "sg11.sh",
+ "check": "sg11.so"
+ },
+ {
+ "name": "ionCube",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "81",
+ "82",
+ "83"
+ ],
+ "type": "脚本解密",
+ "msg": "用于解密ionCube Encoder加密脚本!",
+ "shell": "ioncube.sh",
+ "check": "ioncube"
+ },
+ {
+ "name": "pdo",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "数据库",
+ "msg": "数据库访问抽象模块!",
+ "shell": "pdo.sh",
+ "check": "pdo"
+ },
+ {
+ "name": "mysqlnd",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "数据库",
+ "msg": "用于使用MySQL数据库的模块!",
+ "shell": "mysqlnd.sh",
+ "check": "mysqlnd"
+ },
+ {
+ "name": "mysql",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "数据库",
+ "msg": "用于使用MySQL数据库的模块!",
+ "shell": "mysql.sh",
+ "check": "mysql"
+ },
+ {
+ "name": "sqlite3",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "数据库",
+ "msg": "用于使用sqlite3数据库的模块!",
+ "shell": "sqlite3.sh",
+ "check": "sqlite3"
+ },
+ {
+ "name": "oci8",
+ "versions": [],
+ "type": "数据库",
+ "msg": "用于使用OCI8数据库的模块!",
+ "shell": "oci8.sh",
+ "check": "oci8"
+ },
+ {
+ "name": "odbc",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "数据库",
+ "msg": "用于使用ODBC数据库的模块!",
+ "shell": "odbc.sh",
+ "check": "odbc"
+ },
+ {
+ "name": "ZendGuardLoader",
+ "versions": [
+ ],
+ "type": "脚本解密",
+ "msg": "用于解密ZendGuard加密脚本!",
+ "shell": "zend_guard_loader.sh",
+ "check": "ZendGuardLoader"
+ },
+ {
+ "name": "ZendOptimizer",
+ "versions": [
+ "52"
+ ],
+ "type": "脚本解密",
+ "msg": "用于解密ZendOptimizer加密脚本!",
+ "shell": "zend_optimizer.sh",
+ "check": "ZendOptimizer"
+ },
+ {
+ "name": "opcache",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "缓存器",
+ "msg": "用于加速PHP脚本!",
+ "shell": "opcache.sh",
+ "check": "opcache"
+ },
+ {
+ "name": "mcrypt",
+ "versions": [
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81"
+ ],
+ "type": "通用扩展",
+ "msg": "加密软件!",
+ "shell": "mcrypt.sh",
+ "check": "mcrypt"
+ },
+ {
+ "name": "ldap",
+ "versions": [
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "轻型目录访问协议",
+ "shell": "ldap.sh",
+ "check": "ldap"
+ },
+ {
+ "name": "gmp",
+ "versions": [
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "数学扩展",
+ "shell": "gmp.sh",
+ "check": "gmp"
+ },
+ {
+ "name": "brotli",
+ "versions": [
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "压缩",
+ "msg": "压缩算法",
+ "shell": "brotli.sh",
+ "check": "brotli.so"
+ },
+ {
+ "name": "bcmath",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "高精度计算!",
+ "shell": "bcmath.sh",
+ "check": "bcmath.so"
+ },
+ {
+ "name": "fileinfo",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "用于FILE!",
+ "shell": "fileinfo.sh",
+ "check": "fileinfo"
+ },
+ {
+ "name": "exif",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "用于图像文件格式!",
+ "shell": "exif.sh",
+ "check": "exif"
+ },
+ {
+ "name": "igbinary",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "序列化扩展!",
+ "shell": "igbinary.sh",
+ "check": "igbinary.so"
+ },
+ {
+ "name": "xml",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "用于xml解析!",
+ "shell": "xml.sh",
+ "check": "xml"
+ },
+ {
+ "name": "curl",
+ "versions": [
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "通用CURL库!",
+ "shell": "curl.sh",
+ "check": "curl"
+ },
+ {
+ "name": "gd",
+ "versions": [
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "通用GD库!",
+ "shell": "gd.sh",
+ "check": "gd"
+ },
+ {
+ "name": "intl",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "提供国际化支持",
+ "shell": "intl.sh",
+ "check": "intl"
+ },
+ {
+ "name": "memcache",
+ "versions": [
+ "74",
+ "80",
+ "81"
+ ],
+ "type": "缓存器",
+ "msg": "强大的内容缓存器,不支持集群",
+ "shell": "memcache.sh",
+ "check": "memcache"
+ },
+ {
+ "name": "memcached",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "缓存器",
+ "msg": "强大的内容缓存器,支持集群",
+ "shell": "memcached.sh",
+ "check": "memcached"
+ },
+ {
+ "name": "redis",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "缓存器",
+ "msg": "更强大的内容缓存器,支持集群",
+ "shell": "redis.sh",
+ "check": "redis"
+ },
+ {
+ "name": "apcu",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "缓存器",
+ "msg": "脚本缓存器",
+ "shell": "apcu.sh",
+ "check": "apcu"
+ },
+ {
+ "name": "imagick",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "比GD更强大的图形库",
+ "shell": "imagick.sh",
+ "check": "imagick"
+ },
+ {
+ "name": "xdebug",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "调试器",
+ "msg": "不多说,不了解的不要安装",
+ "shell": "xdebug.sh",
+ "check": "xdebug"
+ },
+ {
+ "name": "xhprof",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "性能分析",
+ "msg": "不多说,不了解的不要安装!",
+ "shell": "xhprof.sh",
+ "check": "xhprof"
+ },
+ {
+ "name": "Swoole",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "异步、并行、高性能网络通信引擎",
+ "shell": "swoole.sh",
+ "check": "swoole"
+ },
+ {
+ "name": "eAccelerator",
+ "versions": [
+ "52",
+ "53"
+ ],
+ "type": "缓存器",
+ "msg": "内容缓存器",
+ "shell": "eaccelerator.sh",
+ "check": "eaccelerator"
+ },
+ {
+ "name": "yaf",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83"
+ ],
+ "type": "框架",
+ "msg": "Yaf是一个C语言编写的PHP框架",
+ "shell": "yaf.sh",
+ "check": "yaf"
+ },
+ {
+ "name": "phalcon",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "框架",
+ "msg": "PHP框架",
+ "shell": "phalcon.sh",
+ "check": "phalcon"
+ },
+ {
+ "name": "yar",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74"
+ ],
+ "type": "框架",
+ "msg": "Yar是一个RPC框架",
+ "shell": "yar.sh",
+ "check": "yar"
+ },
+ {
+ "name": "mongo",
+ "versions": [
+ "56"
+ ],
+ "type": "通用扩展",
+ "msg": "Mongodb数据库连接驱动",
+ "shell": "mongo.sh",
+ "check": "mongo"
+ },
+ {
+ "name": "mongodb",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "Mongodb数据库连接驱动",
+ "shell": "mongodb.sh",
+ "check": "mongodb"
+ },
+ {
+ "name": "yac",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81"
+ ],
+ "type": "缓存器",
+ "msg": "高性能无锁共享内存Cache",
+ "shell": "yac.sh",
+ "check": "yac"
+ },
+ {
+ "name": "solr",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81"
+ ],
+ "type": "大数据",
+ "msg": "SOLR全文搜索服务",
+ "shell": "solr.sh",
+ "check": "solr"
+ },
+ {
+ "name": "seaslog",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "日志",
+ "msg": "SeasLog高性能日志记录",
+ "shell": "seaslog.sh",
+ "check": "seaslog"
+ },
+ {
+ "name": "mbstring",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "用于需要多字节字符串处理的模块",
+ "shell": "mbstring.sh",
+ "check": "mbstring"
+ },
+ {
+ "name": "zip",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "压缩",
+ "msg": "压缩组件",
+ "shell": "zip.sh",
+ "check": "zip"
+ },
+ {
+ "name": "zstd",
+ "versions": [
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "压缩",
+ "msg": "压缩组件",
+ "shell": "zstd.sh",
+ "check": "zstd"
+ }
+]
\ No newline at end of file
diff --git a/plugins/php-guard/ico.png b/plugins/php-guard/ico.png
new file mode 100755
index 000000000..93c14c00a
Binary files /dev/null and b/plugins/php-guard/ico.png differ
diff --git a/plugins/php-guard/index.html b/plugins/php-guard/index.html
new file mode 100755
index 000000000..70715852c
--- /dev/null
+++ b/plugins/php-guard/index.html
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/plugins/php-guard/index.py b/plugins/php-guard/index.py
new file mode 100755
index 000000000..6846179e9
--- /dev/null
+++ b/plugins/php-guard/index.py
@@ -0,0 +1,70 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import shutil
+
+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 'php-guard'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getInitDFile(version):
+ if app_debug:
+ return '/tmp/' + getPluginName()
+ return '/etc/init.d/' + getPluginName() + version
+
+
+def getArgs():
+ args = sys.argv[3:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+
+ 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'))
+
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ if func == 'status':
+ print('start')
+ else:
+ print("fail")
diff --git a/plugins/php-guard/info.json b/plugins/php-guard/info.json
new file mode 100755
index 000000000..6493c64f7
--- /dev/null
+++ b/plugins/php-guard/info.json
@@ -0,0 +1,18 @@
+{
+ "sort": 5,
+ "ps": "监控PHP-FPM运行状态,防止大批量出现502错误!",
+ "shell": "install.sh",
+ "name": "php-guard",
+ "title": "PHP守护",
+ "versions": "1.0",
+ "updates": "1.0",
+ "tip": "soft",
+ "checks": "server/php-guard",
+ "path": "server/php-guard",
+ "display": 1,
+ "author": "midoks",
+ "date": "2019-03-01",
+ "home": "https://github.com/midoks",
+ "type": "语言解释器",
+ "pid": "1"
+}
\ No newline at end of file
diff --git a/plugins/php-guard/install.sh b/plugins/php-guard/install.sh
new file mode 100755
index 000000000..b1edae490
--- /dev/null
+++ b/plugins/php-guard/install.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+Install_pg()
+{
+ echo '安装PHP守护中...'
+ echo 'True' > ${rootPath}/data/502Task.pl
+
+ mkdir -p $serverPath/php-guard
+ echo '1.0' > $serverPath/php-guard/version.pl
+ echo '安装PHP守护成功!!'
+}
+
+Uninstall_pg()
+{
+ rm -rf ${rootPath}/data/502Task.pl
+ rm -rf $serverPath/php-guard
+ echo '卸载PHP守护成功!!'
+}
+
+
+action=$1
+host=$2
+if [ "${1}" == 'install' ];then
+ Install_pg
+else
+ Uninstall_pg
+fi
diff --git a/plugins/php-yum/all_test.sh b/plugins/php-yum/all_test.sh
new file mode 100644
index 000000000..eabbe6db5
--- /dev/null
+++ b/plugins/php-yum/all_test.sh
@@ -0,0 +1,41 @@
+#! /bin/sh
+export PATH=$PATH:/opt/local/bin:/opt/local/sbin:/opt/local/share/man:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin
+DIR=$(cd "$(dirname "$0")"; pwd)
+
+
+PHP_VER=71
+echo "php${PHP_VER} -- start"
+cmd_ext=$(ls -l $DIR/versions/$PHP_VER/ |awk '{print $9}')
+cd $DIR && /bin/bash install.sh install $PHP_VER
+for ii in $cmd_ext
+do
+ if [ "install.sh" == "$ii" ];then
+ echo '' > /tmp/t.log
+ else
+ cd $DIR/versions/$PHP_VER && /bin/bash $ii install $PHP_VER
+ fi
+done
+echo "php${PHP_VER} -- end"
+
+
+PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82 83)
+# PHP_VER_LIST=(81)
+for PHP_VER in ${PHP_VER_LIST[@]}; do
+ echo "php${PHP_VER} -- start"
+ if [ -d /www/server/php/${PHP_VER} ];then
+ cmd_ext=$(ls -l $DIR/versions/$PHP_VER/ |awk '{print $9}')
+ for ii in $cmd_ext
+ do
+ echo "${ii}"
+ if [ "install.sh" == "$ii" ];then
+ echo '' > /tmp/t.log
+ else
+ cd $DIR/versions/$PHP_VER/ && bash $ii install ${PHP_VER}
+ fi
+ done
+ fi
+ echo "php${PHP_VER} -- end"
+done
+
+
+rm -rf /tmp/t.log
diff --git a/plugins/php-yum/conf/app_start.php b/plugins/php-yum/conf/app_start.php
new file mode 100644
index 000000000..5d1b7ce8c
--- /dev/null
+++ b/plugins/php-yum/conf/app_start.php
@@ -0,0 +1,55 @@
+save_run($xhprof_data, 'xhprof_foo');
+
+ $profiler_url = sprintf('http://{$LOCAL_IP}:5858/index.php?run=%s&source=xhprof_foo', $run_id);
+ // echo "";
+
+ $style_css = 'position:fixed;bottom: 0px;right: 0px;z-index: 100000000;color: red;background-color: black;padding: 0px;margin: 0px;';
+ echo '
XHProf分析结果 ';
+
+}
+
+if (extension_loaded('xhprof')
+ && isset($_GET[XHProf_Name]) && $_GET[XHProf_Name] == 'ok' &&
+ (! in_array($_SERVER['SCRIPT_NAME'], ['/xhprof_html/callgraph.php',
+ '/xhprof_html/index.php']))) {
+ app_xhprof_start();
+ include_once $_SERVER['SCRIPT_FILENAME'];
+ app_xhprof_end();
+ exit;
+}
+
+?>
diff --git a/plugins/php-yum/conf/enable-php.conf b/plugins/php-yum/conf/enable-php.conf
new file mode 100644
index 000000000..9f8461da7
--- /dev/null
+++ b/plugins/php-yum/conf/enable-php.conf
@@ -0,0 +1,9 @@
+set $PHP_ENV 1;
+location ~ [^/]\.php(/|$)
+{
+ try_files $uri =404;
+ fastcgi_pass unix:/var/opt/remi/php{$PHP_VERSION}/run/php-fpm/www.sock;
+ fastcgi_index index.php;
+ include fastcgi.conf;
+ include {$SERVER_PATH}/web_conf/php/pathinfo.conf;
+}
\ No newline at end of file
diff --git a/plugins/php-yum/conf/pathinfo.conf b/plugins/php-yum/conf/pathinfo.conf
new file mode 100644
index 000000000..47b573684
--- /dev/null
+++ b/plugins/php-yum/conf/pathinfo.conf
@@ -0,0 +1,8 @@
+set $real_script_name $fastcgi_script_name;
+if ($fastcgi_script_name ~ "^(.+?\.php)(/.+)$") {
+ set $real_script_name $1;
+ set $path_info $2;
+ }
+fastcgi_param SCRIPT_FILENAME $document_root$real_script_name;
+fastcgi_param SCRIPT_NAME $real_script_name;
+fastcgi_param PATH_INFO $path_info;
\ No newline at end of file
diff --git a/plugins/php-yum/conf/php-fpm.conf b/plugins/php-yum/conf/php-fpm.conf
new file mode 100644
index 000000000..6152806c0
--- /dev/null
+++ b/plugins/php-yum/conf/php-fpm.conf
@@ -0,0 +1,4 @@
+[global]
+pid = /var/opt/remi/php{$PHP_VERSION}/run/php-fpm/php-fpm.pid
+include=/etc/opt/remi/php{$PHP_VERSION}/php-fpm.d/*.conf
+php_value[auto_prepend_file]={$SERVER_PATH}/php-yum/app_start.php
\ No newline at end of file
diff --git a/plugins/php-yum/conf/phpinfo.conf b/plugins/php-yum/conf/phpinfo.conf
new file mode 100644
index 000000000..b3c51e657
--- /dev/null
+++ b/plugins/php-yum/conf/phpinfo.conf
@@ -0,0 +1,4 @@
+location /{$PHP_VERSION} {
+ root {$ROOT_PATH}/phpinfo;
+ include {$SERVER_PATH}/web_conf/php/conf/enable-php-yum{$PHP_VERSION}.conf;
+}
\ No newline at end of file
diff --git a/plugins/php-yum/conf/www.conf b/plugins/php-yum/conf/www.conf
new file mode 100644
index 000000000..a8149e3db
--- /dev/null
+++ b/plugins/php-yum/conf/www.conf
@@ -0,0 +1,22 @@
+[www]
+user = {$PHP_USER}
+group = {$PHP_GROUP}
+
+listen = /var/opt/remi/php{$PHP_VERSION}/run/php-fpm/www.sock
+listen.owner = {$PHP_USER}
+listen.group = {$PHP_GROUP}
+listen.backlog = 4096
+
+pm = dynamic
+pm.max_children = 50
+pm.start_servers = 5
+pm.min_spare_servers = 5
+pm.max_spare_servers = 35
+pm.status_path = /phpfpm_status_yum{$PHP_VERSION}
+pm.max_requests = 1000
+request_terminate_timeout = 30
+request_slowlog_timeout = 10
+slowlog = /var/opt/remi/php{$PHP_VERSION}/log/php-fpm/www-slow.log
+
+php_admin_flag[log_errors] = on
+php_admin_value[error_log] = /var/opt/remi/php{$PHP_VERSION}/log/php-fpm/error.log
\ No newline at end of file
diff --git a/plugins/php-yum/ico.png b/plugins/php-yum/ico.png
new file mode 100755
index 000000000..59def5702
Binary files /dev/null and b/plugins/php-yum/ico.png differ
diff --git a/plugins/php-yum/index.html b/plugins/php-yum/index.html
new file mode 100755
index 000000000..d94adc433
--- /dev/null
+++ b/plugins/php-yum/index.html
@@ -0,0 +1,66 @@
+
+
+
+
\ No newline at end of file
diff --git a/plugins/php-yum/index.py b/plugins/php-yum/index.py
new file mode 100755
index 000000000..6f92b5572
--- /dev/null
+++ b/plugins/php-yum/index.py
@@ -0,0 +1,931 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import shutil
+
+
+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 'php-yum'
+
+
+def getAppDir():
+ return mw.getServerDir()+'/'+getPluginName()
+
+def getServerDir():
+ return '/etc/opt/remi'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getArgs():
+ args = sys.argv[3:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+
+ 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 getConf(version):
+ path = getServerDir() + '/php' + version + '/php.ini'
+ return path
+
+
+def status(version):
+ # ps -ef|grep 'php/81' |grep -v grep | grep -v python | awk '{print $2}
+ cmd = "ps -ef|grep 'remi/php" + version + \
+ "' |grep -v grep | grep -v python | awk '{print $2}'"
+ data = mw.execShell(cmd)
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def contentReplace(content, version):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$PHP_VERSION}', version)
+ content = content.replace('{$LOCAL_IP}', mw.getLocalIp())
+
+ if mw.isAppleSystem():
+ # user = mw.execShell(
+ # "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
+ content = content.replace('{$PHP_USER}', 'nobody')
+ content = content.replace('{$PHP_GROUP}', 'nobody')
+
+ rep = r'listen.owner\s*=\s*(.+)\r?\n'
+ val = ';listen.owner = nobody\n'
+ content = re.sub(rep, val, content)
+
+ rep = r'listen.group\s*=\s*(.+)\r?\n'
+ val = ';listen.group = nobody\n'
+ content = re.sub(rep, val, content)
+
+ rep = r'user\s*=\s*(.+)\r?\n'
+ val = ';user = nobody\n'
+ content = re.sub(rep, val, content)
+
+ rep = r'[^\.]group\s*=\s*(.+)\r?\n'
+ val = ';group = nobody\n'
+ content = re.sub(rep, val, content)
+
+ else:
+ content = content.replace('{$PHP_USER}', 'www')
+ content = content.replace('{$PHP_GROUP}', 'www')
+ return content
+
+
+def makeOpenrestyConf(version):
+
+ sdir = mw.getServerDir()
+
+ dst_dir = sdir + '/web_conf/php'
+ dst_dir_conf = sdir + '/web_conf/php/conf'
+
+ if not os.path.exists(dst_dir):
+ mw.execShell('mkdir -p ' + dst_dir)
+
+ if not os.path.exists(dst_dir_conf):
+ mw.execShell('mkdir -p ' + dst_dir_conf)
+
+ d_pathinfo = sdir + '/web_conf/php/pathinfo.conf'
+ if not os.path.exists(d_pathinfo):
+ s_pathinfo = getPluginDir() + '/conf/pathinfo.conf'
+ shutil.copyfile(s_pathinfo, d_pathinfo)
+
+ info = getPluginDir() + '/info.json'
+ content = mw.readFile(info)
+ content = json.loads(content)
+ versions = content['versions']
+ tpl = getPluginDir() + '/conf/enable-php.conf'
+ tpl_content = mw.readFile(tpl)
+ dfile = sdir + '/web_conf/php/conf/enable-php-yum' + version + '.conf'
+ if not os.path.exists(dfile):
+ w_content = contentReplace(tpl_content, version)
+ mw.writeFile(dfile, w_content)
+
+
+def phpFpmWwwReplace(version):
+ service_php_fpm_dir = getServerDir() + '/php' + version + '/php-fpm.d/'
+ if not os.path.exists(service_php_fpm_dir):
+ os.mkdir(service_php_fpm_dir)
+
+ service_php_fpmwww = service_php_fpm_dir + '/www.conf'
+ if os.path.exists(service_php_fpmwww):
+ # 原来文件备份
+ mw.execShell('mv ' + service_php_fpmwww +
+ ' ' + service_php_fpmwww + '.bak')
+
+ service_php_fpm_mw = service_php_fpm_dir + '/mw.conf'
+ if not os.path.exists(service_php_fpm_mw):
+ tpl_php_fpmwww = getPluginDir() + '/conf/www.conf'
+ content = mw.readFile(tpl_php_fpmwww)
+ content = contentReplace(content, version)
+ mw.writeFile(service_php_fpm_mw, content)
+
+def phpPrependFile(version):
+ app_start = getAppDir() + '/app_start.php'
+ if not os.path.exists(app_start):
+ tpl = getPluginDir() + '/conf/app_start.php'
+ content = mw.readFile(tpl)
+ content = contentReplace(content, version)
+ mw.writeFile(app_start, content)
+
+def getFpmConfFile(version):
+ return getServerDir() + '/php' + version + '/php-fpm.d/mw.conf'
+
+
+def getFpmFile(version):
+ return getServerDir() + '/php' + version + '/php-fpm.conf'
+
+
+def getDstEnablePHP(version):
+ sdir = mw.getServerDir()
+ dfile = sdir + '/web_conf/php/conf/enable-php-yum' + version + '.conf'
+ return dfile
+
+def deleteConfList(version):
+ enable_conf = getDstEnablePHP(version)
+ if os.path.exists(enable_conf):
+ os.remove(enable_conf)
+
+
+def phpFpmReplace(version):
+ desc_php_fpm = getFpmFile(version)
+ tpl_php_fpm = getPluginDir() + '/conf/php-fpm.conf'
+ content = mw.readFile(tpl_php_fpm)
+ content = contentReplace(content, version)
+ mw.writeFile(desc_php_fpm, content)
+ return True
+
+
+def initReplace(version):
+ makeOpenrestyConf(version)
+ phpFpmWwwReplace(version)
+
+ install_ok = getAppDir() + "/" + version + "/install.ok"
+ if not os.path.exists(install_ok):
+ phpFpmReplace(version)
+
+ phpini = getConf(version)
+ ssl_crt = mw.getSslCrt()
+
+ cmd_openssl = "sed -i \"s#;openssl.cafile=#openssl.cafile=" + ssl_crt + "#\" " + phpini
+ mw.execShell(cmd_openssl)
+ cmd_curl = "sed -i \"s#;curl.cainfo =#curl.cainfo=" + ssl_crt + "#\" " + phpini
+ mw.execShell(cmd_curl)
+ mw.writeFile(install_ok, 'ok')
+
+ phpPrependFile(version)
+ # systemd
+ # mw.execShell('systemctl daemon-reload')
+ return 'ok'
+
+
+def phpOp(version, method):
+ if method == 'start':
+ initReplace(version)
+
+ if mw.isAppleSystem():
+ return 'fail'
+ data = mw.execShell('systemctl ' + method + ' ' +
+ 'php' + version + '-php-fpm')
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def start(version):
+ return phpOp(version, 'start')
+
+
+def stop(version):
+ status = phpOp(version, 'stop')
+ deleteConfList(version)
+ return status
+
+
+def restart(version):
+ return phpOp(version, 'restart')
+
+
+def reload(version):
+ return phpOp(version, 'reload')
+
+
+def initdStatus(version):
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status php' + version + '-php-fpm | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+
+def initdInstall(version):
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl enable php' + version + '-php-fpm')
+ return 'ok'
+
+
+def initdUinstall(version):
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl disable php' + version + '-php-fpm')
+ return 'ok'
+
+
+def fpmLog(version):
+ f = '/var/opt/remi/php' + version + '/log/php-fpm.log'
+ if os.path.exists(f):
+ return f
+ return '/var/opt/remi/php' + version + '/log/php-fpm/error.log'
+
+
+def fpmSlowLog(version):
+ return '/var/opt/remi/php' + version + '/log/php-fpm/www-slow.log'
+
+
+def getPhpConf(version):
+ gets = [
+ {'name': 'short_open_tag', 'type': 1, 'ps': '短标签支持'},
+ {'name': 'asp_tags', 'type': 1, 'ps': 'ASP标签支持'},
+ {'name': 'max_execution_time', 'type': 2, 'ps': '最大脚本运行时间'},
+ {'name': 'max_input_time', 'type': 2, 'ps': '最大输入时间'},
+ {'name': 'max_input_vars', 'type': 2, 'ps': '最大输入数量'},
+ {'name': 'memory_limit', 'type': 2, 'ps': '脚本内存限制'},
+ {'name': 'post_max_size', 'type': 2, 'ps': 'POST数据最大尺寸'},
+ {'name': 'file_uploads', 'type': 1, 'ps': '是否允许上传文件'},
+ {'name': 'upload_max_filesize', 'type': 2, 'ps': '允许上传文件的最大尺寸'},
+ {'name': 'max_file_uploads', 'type': 2, 'ps': '允许同时上传文件的最大数量'},
+ {'name': 'default_socket_timeout', 'type': 2, 'ps': 'Socket超时时间'},
+ {'name': 'error_reporting', 'type': 3, 'ps': '错误级别'},
+ {'name': 'display_errors', 'type': 1, 'ps': '是否输出详细错误信息'},
+ {'name': 'cgi.fix_pathinfo', 'type': 0, 'ps': '是否开启pathinfo'},
+ {'name': 'date.timezone', 'type': 3, 'ps': '时区'}
+ ]
+ phpini = mw.readFile(getConf(version))
+ result = []
+ for g in gets:
+ rep = g['name'] + r'\s*=\s*([0-9A-Za-z_& ~]+)(\s*;?|\r?\n)'
+ tmp = re.search(rep, phpini)
+ if not tmp:
+ continue
+ g['value'] = tmp.groups()[0]
+ result.append(g)
+ return mw.getJson(result)
+
+
+def submitPhpConf(version):
+ gets = ['display_errors', 'cgi.fix_pathinfo', 'date.timezone', 'short_open_tag',
+ 'asp_tags', 'max_execution_time', 'max_input_time', 'max_input_vars', 'memory_limit',
+ 'post_max_size', 'file_uploads', 'upload_max_filesize', 'max_file_uploads',
+ 'default_socket_timeout', 'error_reporting']
+ args = getArgs()
+ filename = getConf(version)
+ phpini = mw.readFile(filename)
+ for g in gets:
+ if g in args:
+ rep = g + r'\s*=\s*(.+)\r?\n'
+ val = g + ' = ' + args[g] + '\n'
+ phpini = re.sub(rep, val, phpini)
+ mw.writeFile(filename, phpini)
+ reload(version)
+ return mw.returnJson(True, '设置成功')
+
+
+def getLimitConf(version):
+ fileini = getConf(version)
+ phpini = mw.readFile(fileini)
+ filefpm = getFpmConfFile(version)
+ phpfpm = mw.readFile(filefpm)
+
+ # print fileini, filefpm
+ data = {}
+ try:
+ rep = r"upload_max_filesize\s*=\s*([0-9]+)M"
+ tmp = re.search(rep, phpini).groups()
+ data['max'] = tmp[0]
+ except:
+ data['max'] = '50'
+
+ try:
+ rep = r"request_terminate_timeout\s*=\s*([0-9]+)\n"
+ tmp = re.search(rep, phpfpm).groups()
+ data['maxTime'] = tmp[0]
+ except:
+ data['maxTime'] = 0
+
+ try:
+ rep = r"\n;*\s*cgi\.fix_pathinfo\s*=\s*([0-9]+)\s*\n"
+ tmp = re.search(rep, phpini).groups()
+
+ if tmp[0] == '1':
+ data['pathinfo'] = True
+ else:
+ data['pathinfo'] = False
+ except:
+ data['pathinfo'] = False
+
+ return mw.getJson(data)
+
+
+def setMaxTime(version):
+ args = getArgs()
+ data = checkArgs(args, ['time'])
+ if not data[0]:
+ return data[1]
+
+ time = args['time']
+ if int(time) < 30 or int(time) > 86400:
+ return mw.returnJson(False, '请填写30-86400间的值!')
+
+ filefpm = getFpmConfFile(version)
+ conf = mw.readFile(filefpm)
+ rep = r"request_terminate_timeout\s*=\s*([0-9]+)\n"
+ conf = re.sub(rep, "request_terminate_timeout = " + time + "\n", conf)
+ mw.writeFile(filefpm, conf)
+
+ fileini = getConf(version)
+ phpini = mw.readFile(fileini)
+ rep = r"max_execution_time\s*=\s*([0-9]+)\r?\n"
+ phpini = re.sub(rep, "max_execution_time = " + time + "\n", phpini)
+ rep = r"max_input_time\s*=\s*([0-9]+)\r?\n"
+ phpini = re.sub(rep, "max_input_time = " + time + "\n", phpini)
+ mw.writeFile(fileini, phpini)
+ return mw.returnJson(True, '设置成功!')
+
+
+def setMaxSize(version):
+ args = getArgs()
+ data = checkArgs(args, ['max'])
+ if not data[0]:
+ return data[1]
+
+ maxVal = args['max']
+ if int(maxVal) < 2:
+ return mw.returnJson(False, '上传大小限制不能小于2MB!')
+
+ path = getConf(version)
+ conf = mw.readFile(path)
+ rep = r"\nupload_max_filesize\s*=\s*[0-9]+M"
+ conf = re.sub(rep, u'\nupload_max_filesize = ' + maxVal + 'M', conf)
+ rep = r"\npost_max_size\s*=\s*[0-9]+M"
+ conf = re.sub(rep, u'\npost_max_size = ' + maxVal + 'M', conf)
+ mw.writeFile(path, conf)
+
+ msg = mw.getInfo('设置PHP-{1}最大上传大小为[{2}MB]!', (version, maxVal,))
+ mw.writeLog('插件管理[PHP]', msg)
+ return mw.returnJson(True, '设置成功!')
+
+
+def getFpmConfig(version):
+
+ filefpm = getFpmConfFile(version)
+ conf = mw.readFile(filefpm)
+ data = {}
+ rep = r"\s*pm.max_children\s*=\s*([0-9]+)\s*"
+ tmp = re.search(rep, conf).groups()
+ data['max_children'] = tmp[0]
+
+ rep = r"\s*pm.start_servers\s*=\s*([0-9]+)\s*"
+ tmp = re.search(rep, conf).groups()
+ data['start_servers'] = tmp[0]
+
+ rep = r"\s*pm.min_spare_servers\s*=\s*([0-9]+)\s*"
+ tmp = re.search(rep, conf).groups()
+ data['min_spare_servers'] = tmp[0]
+
+ rep = r"\s*pm.max_spare_servers \s*=\s*([0-9]+)\s*"
+ tmp = re.search(rep, conf).groups()
+ data['max_spare_servers'] = tmp[0]
+
+ rep = r"\s*pm\s*=\s*(\w+)\s*"
+ tmp = re.search(rep, conf).groups()
+ data['pm'] = tmp[0]
+ return mw.getJson(data)
+
+
+def setFpmConfig(version):
+ args = getArgs()
+ # if not 'max' in args:
+ # return 'missing time args!'
+
+ max_children = args['max_children']
+ start_servers = args['start_servers']
+ min_spare_servers = args['min_spare_servers']
+ max_spare_servers = args['max_spare_servers']
+ pm = args['pm']
+
+ # file = getServerDir() + '/php' + version + '/php-fpm.d/www.conf'
+ filefpm = getFpmConfFile(version)
+ conf = mw.readFile(filefpm)
+
+ rep = r"\s*pm.max_children\s*=\s*([0-9]+)\s*"
+ conf = re.sub(rep, "\npm.max_children = " + max_children, conf)
+
+ rep = r"\s*pm.start_servers\s*=\s*([0-9]+)\s*"
+ conf = re.sub(rep, "\npm.start_servers = " + start_servers, conf)
+
+ rep = r"\s*pm.min_spare_servers\s*=\s*([0-9]+)\s*"
+ conf = re.sub(rep, "\npm.min_spare_servers = " +
+ min_spare_servers, conf)
+
+ rep = r"\s*pm.max_spare_servers \s*=\s*([0-9]+)\s*"
+ conf = re.sub(rep, "\npm.max_spare_servers = " +
+ max_spare_servers + "\n", conf)
+
+ rep = r"\s*pm\s*=\s*(\w+)\s*"
+ conf = re.sub(rep, "\npm = " + pm + "\n", conf)
+
+ mw.writeFile(filefpm, conf)
+ reload(version)
+
+ msg = mw.getInfo('设置PHP-{1}并发设置,max_children={2},start_servers={3},min_spare_servers={4},max_spare_servers={5}', (version, max_children,
+ start_servers, min_spare_servers, max_spare_servers,))
+ mw.writeLog('插件管理[PHP]', msg)
+ return mw.returnJson(True, '设置成功!')
+
+
+def getFpmAddress(version):
+ fpm_address = '/var/opt/remi/php{}/run/php-fpm/www.sock'.format(version)
+ php_fpm_file = getFpmConfFile(version)
+ try:
+ content = readFile(php_fpm_file)
+ tmp = re.findall(r"listen\s*=\s*(.+)", content)
+ if not tmp:
+ return fpm_address
+ if tmp[0].find('sock') != -1:
+ return fpm_address
+ if tmp[0].find(':') != -1:
+ listen_tmp = tmp[0].split(':')
+ if bind:
+ fpm_address = (listen_tmp[0], int(listen_tmp[1]))
+ else:
+ fpm_address = ('127.0.0.1', int(listen_tmp[1]))
+ else:
+ fpm_address = ('127.0.0.1', int(tmp[0]))
+ return fpm_address
+ except:
+ return fpm_address
+
+
+def getFpmStatus(version):
+ stat = status(version)
+ if stat == 'stop':
+ return mw.returnJson(False, 'PHP[' + version + ']未启动!!!')
+
+ sock_file = getFpmAddress(version)
+ try:
+ sock_data = mw.requestFcgiPHP(sock_file, '/phpfpm_status_yum' + version + '?json')
+
+ result = str(sock_data, encoding='utf-8')
+ data = json.loads(result)
+ fTime = time.localtime(int(data['start time']))
+ data['start time'] = time.strftime('%Y-%m-%d %H:%M:%S', fTime)
+ except Exception as e:
+ return mw.returnJson(False, str(e))
+
+ # print(data)
+ return mw.returnJson(True, "OK", data)
+
+
+def getSessionConf(version):
+ filename = getConf(version)
+ if not os.path.exists(filename):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+
+ phpini = mw.readFile(filename)
+
+ rep = r'session.save_handler\s*=\s*([0-9A-Za-z_& ~]+)(\s*;?|\r?\n)'
+ save_handler = re.search(rep, phpini)
+ if save_handler:
+ save_handler = save_handler.group(1)
+ else:
+ save_handler = "files"
+
+ reppath = r'\nsession.save_path\s*=\s*"tcp\:\/\/([\d\.]+):(\d+).*\r?\n'
+ passrep = r'\nsession.save_path\s*=\s*"tcp://[\w\.\?\:]+=(.*)"\r?\n'
+ memcached = r'\nsession.save_path\s*=\s*"([\d\.]+):(\d+)"'
+ save_path = re.search(reppath, phpini)
+ if not save_path:
+ save_path = re.search(memcached, phpini)
+ passwd = re.search(passrep, phpini)
+ port = ""
+ if passwd:
+ passwd = passwd.group(1)
+ else:
+ passwd = ""
+ if save_path:
+ port = save_path.group(2)
+ save_path = save_path.group(1)
+
+ else:
+ save_path = ""
+
+ data = {"save_handler": save_handler, "save_path": save_path,
+ "passwd": passwd, "port": port}
+ return mw.returnJson(True, 'ok', data)
+
+
+def setSessionConf(version):
+
+ args = getArgs()
+
+ ip = args['ip']
+ port = args['port']
+ passwd = args['passwd']
+ save_handler = args['save_handler']
+
+ if save_handler != "files":
+ iprep = r"(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})"
+ if not re.search(iprep, ip):
+ return mw.returnJson(False, '请输入正确的IP地址')
+
+ try:
+ port = int(port)
+ if port >= 65535 or port < 1:
+ return mw.returnJson(False, '请输入正确的端口号')
+ except:
+ return mw.returnJson(False, '请输入正确的端口号')
+ prep = r"[\~\`\/\=]"
+ if re.search(prep, passwd):
+ return mw.returnJson(False, '请不要输入以下特殊字符 " ~ ` / = "')
+
+ filename = getConf(version)
+ if not os.path.exists(filename):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+ phpini = mw.readFile(filename)
+
+ session_tmp = getServerDir() + "/tmp/session"
+
+ rep = r'session.save_handler\s*=\s*(.+)\r?\n'
+ val = r'session.save_handler = ' + save_handler + '\n'
+ phpini = re.sub(rep, val, phpini)
+
+ content = mw.execShell('cat /etc/opt/remi/php' +
+ version + "/php.d/* | grep -v '^;' |tr -s '\n'")
+ content = content[0]
+
+ if save_handler == "memcached":
+ if not content.find("memcached.so") > -1:
+ return mw.returnJson(False, '请先安装%s扩展' % save_handler)
+ rep = r'\nsession.save_path\s*=\s*(.+)\r?\n'
+ val = r'\nsession.save_path = "%s:%s" \n' % (ip, port)
+ if re.search(rep, phpini):
+ phpini = re.sub(rep, val, phpini)
+ else:
+ phpini = re.sub('\n;session.save_path = "/tmp"',
+ '\n;session.save_path = "/tmp"' + val, phpini)
+
+ if save_handler == "memcache":
+ if not content.find("memcache.so") > -1:
+ return mw.returnJson(False, '请先安装%s扩展' % save_handler)
+ rep = r'\nsession.save_path\s*=\s*(.+)\r?\n'
+ val = r'\nsession.save_path = "%s:%s" \n' % (ip, port)
+ if re.search(rep, phpini):
+ phpini = re.sub(rep, val, phpini)
+ else:
+ phpini = re.sub('\n;session.save_path = "/tmp"',
+ '\n;session.save_path = "/tmp"' + val, phpini)
+
+ if save_handler == "redis":
+ if not content.find("redis.so") > -1:
+ return mw.returnJson(False, '请先安装%s扩展' % save_handler)
+ if passwd:
+ passwd = "?auth=" + passwd
+ else:
+ passwd = ""
+ rep = r'\nsession.save_path\s*=\s*(.+)\r?\n'
+ val = r'\nsession.save_path = "tcp://%s:%s%s"\n' % (ip, port, passwd)
+ res = re.search(rep, phpini)
+ if res:
+ phpini = re.sub(rep, val, phpini)
+ else:
+ phpini = re.sub('\n;session.save_path = "/tmp"',
+ '\n;session.save_path = "/tmp"' + val, phpini)
+
+ if save_handler == "files":
+ rep = r'\nsession.save_path\s*=\s*(.+)\r?\n'
+ val = r'\nsession.save_path = "' + session_tmp + '"\n'
+ if re.search(rep, phpini):
+ phpini = re.sub(rep, val, phpini)
+ else:
+ phpini = re.sub('\n;session.save_path = "/tmp"',
+ '\n;session.save_path = "/tmp"' + val, phpini)
+
+ mw.writeFile(filename, phpini)
+ restart(version)
+ return mw.returnJson(True, '设置成功!')
+
+
+def getSessionCount_Origin(version):
+ session_tmp = getServerDir() + "/tmp/session"
+ d = ["/tmp", session_tmp]
+ count = 0
+ for i in d:
+ if not os.path.exists(i):
+ mw.execShell('mkdir -p %s' % i)
+ list = os.listdir(i)
+ for l in list:
+ if os.path.isdir(i + "/" + l):
+ l1 = os.listdir(i + "/" + l)
+ for ll in l1:
+ if "sess_" in ll:
+ count += 1
+ continue
+ if "sess_" in l:
+ count += 1
+
+ s = "find /tmp -mtime +1 |grep 'sess_' | wc -l"
+ old_file = int(mw.execShell(s)[0].split("\n")[0])
+
+ s = "find " + session_tmp + " -mtime +1 |grep 'sess_'|wc -l"
+ old_file += int(mw.execShell(s)[0].split("\n")[0])
+ return {"total": count, "oldfile": old_file}
+
+
+def getSessionCount(version):
+ data = getSessionCount_Origin(version)
+ return mw.returnJson(True, 'ok!', data)
+
+
+def cleanSessionOld(version):
+ s = "find /tmp -mtime +1 |grep 'sess_'|xargs rm -f"
+ mw.execShell(s)
+
+ session_tmp = getServerDir() + "/tmp/session"
+ s = "find " + session_tmp + " -mtime +1 |grep 'sess_' |xargs rm -f"
+ mw.execShell(s)
+ old_file_conf = getSessionCount_Origin(version)["oldfile"]
+ if old_file_conf == 0:
+ return mw.returnJson(True, '清理成功')
+ else:
+ return mw.returnJson(True, '清理失败')
+
+
+def getDisableFunc(version):
+ filename = getConf(version)
+ if not os.path.exists(filename):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+
+ phpini = mw.readFile(filename)
+ data = {}
+ rep = r"disable_functions\s*=\s{0,1}(.*)\n"
+ tmp = re.search(rep, phpini).groups()
+ data['disable_functions'] = tmp[0]
+ return mw.getJson(data)
+
+
+def setDisableFunc(version):
+ filename = getConf(version)
+ if not os.path.exists(filename):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+
+ args = getArgs()
+ disable_functions = args['disable_functions']
+
+ phpini = mw.readFile(filename)
+ rep = r"disable_functions\s*=\s*.*\n"
+ phpini = re.sub(rep, 'disable_functions = ' + disable_functions + "\n", phpini)
+
+ msg = mw.getInfo('修改PHP-{1}的禁用函数为[{2}]', (version, disable_functions,))
+ mw.writeLog('插件管理[PHP-YUM]', msg)
+ mw.writeFile(filename, phpini)
+ reload(version)
+ return mw.returnJson(True, '设置成功!')
+
+
+def getPhpinfo(version):
+ stat = status(version)
+ if stat == 'stop':
+ return 'PHP[' + version + ']未启动,不可访问!'
+
+ sock_file = getFpmAddress(version)
+ root_dir = mw.getFatherDir() + '/phpinfo'
+
+ mw.execShell("rm -rf " + root_dir)
+ mw.execShell("mkdir -p " + root_dir)
+ mw.writeFile(root_dir + '/phpinfo.php', '')
+ sock_data = mw.requestFcgiPHP(sock_file, '/phpinfo.php', root_dir)
+ os.system("rm -rf " + root_dir)
+ phpinfo = str(sock_data, encoding='utf-8')
+ return phpinfo
+
+
+def get_php_info(args):
+ return getPhpinfo(args['version'])
+
+
+def getLibConf(version):
+ fname = getConf(version)
+ if not os.path.exists(fname):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+
+ # phpini = mw.readFile(fname)
+ cmd = 'cat /etc/opt/remi/php' +version + "/php.d/* | grep -v '^;' |tr -s '\n'"
+ content = mw.execShell(cmd)
+ content = content[0]
+
+ libpath = getPluginDir() + '/versions/phplib.conf'
+ phplib = json.loads(mw.readFile(libpath))
+
+ libs = []
+ tasks = mw.M('tasks').where("status!=?", ('1',)).field('status,name').select()
+ for lib in phplib:
+ lib['task'] = '1'
+ for task in tasks:
+ tmp = mw.getStrBetween('[', ']', task['name'])
+ if not tmp:
+ continue
+ tmp1 = tmp.split('-')
+ if tmp1[0].lower() == lib['name'].lower():
+ lib['task'] = task['status']
+ lib['phpversions'] = []
+ lib['phpversions'].append(tmp1[1])
+ if content.find(lib['check']) == -1:
+ lib['status'] = False
+ else:
+ lib['status'] = True
+ libs.append(lib)
+ return mw.returnJson(True, 'OK!', libs)
+
+
+def installLib(version):
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ name = args['name']
+ cmd = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + version + ' install ' + name
+ install_name = '安装PHPYUM[' + name + '-' + version + ']'
+ import thisdb
+ thisdb.addTask(name=install_name,cmd=cmd)
+
+ mw.triggerTask()
+ return mw.returnJson(True, '已将下载任务添加到队列!')
+
+
+def uninstallLib(version):
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ name = args['name']
+ execstr = "cd " + getPluginDir() + '/versions/' + " && /bin/bash common.sh " + version + ' uninstall ' + name
+
+ data = mw.execShell(execstr)
+ # data[0] == '' and
+ if data[1] == '':
+ return mw.returnJson(True, '已经卸载成功!')
+ else:
+ return mw.returnJson(False, '卸载错误信息!:' + data[1])
+
+def getConfAppStart():
+ pstart = mw.getServerDir() + '/php-yum/app_start.php'
+ return pstart
+
+def opcacheBlacklistFile():
+ op_bl = mw.getServerDir() + '/php-yum/opcache-blacklist.txt'
+ return op_bl
+
+def installPreInspection(version):
+
+ cmd = "cat /etc/*-release | grep PRETTY_NAME |awk -F = '{print $2}' | awk -F '\"' '{print $2}'| awk '{print $1}'"
+ sys = mw.execShell(cmd)
+ if sys[1] != '':
+ return '不支持该系统'
+
+ cmd = "cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F '\"' '{print $2}'"
+ sys_id = mw.execShell(cmd)
+
+ sysName = sys[0].strip().lower()
+ sysId = sys_id[0].strip()
+
+ if not sysName in ['centos','almalinux','fedora','rocky']:
+ return '暂时仅支持centos,almalinux,fedora,rocky'
+ return 'ok'
+
+
+
+if __name__ == "__main__":
+
+ if len(sys.argv) < 3:
+ print('missing parameters')
+ exit(0)
+
+ func = sys.argv[1]
+ version = sys.argv[2]
+
+ if func == 'status':
+ print(status(version))
+ elif func == 'start':
+ print(start(version))
+ elif func == 'stop':
+ print(stop(version))
+ elif func == 'restart':
+ print(restart(version))
+ elif func == 'reload':
+ print(reload(version))
+ elif func == 'install_pre_inspection':
+ print(installPreInspection(version))
+ elif func == 'initd_status':
+ print(initdStatus(version))
+ elif func == 'initd_install':
+ print(initdInstall(version))
+ elif func == 'initd_uninstall':
+ print(initdUinstall(version))
+ elif func == 'fpm_log':
+ print(fpmLog(version))
+ elif func == 'fpm_slow_log':
+ print(fpmSlowLog(version))
+ elif func == 'conf':
+ print(getConf(version))
+ elif func == 'app_start':
+ print(getConfAppStart())
+ elif func == 'opcache_blacklist_file':
+ print(opcacheBlacklistFile())
+ elif func == 'get_php_conf':
+ print(getPhpConf(version))
+ elif func == 'get_fpm_conf_file':
+ print(getFpmConfFile(version))
+ elif func == 'get_fpm_file':
+ print(getFpmFile(version))
+ elif func == 'submit_php_conf':
+ print(submitPhpConf(version))
+ elif func == 'get_limit_conf':
+ print(getLimitConf(version))
+ elif func == 'set_max_time':
+ print(setMaxTime(version))
+ elif func == 'set_max_size':
+ print(setMaxSize(version))
+ elif func == 'get_fpm_conf':
+ print(getFpmConfig(version))
+ elif func == 'set_fpm_conf':
+ print(setFpmConfig(version))
+ elif func == 'get_fpm_status':
+ print(getFpmStatus(version))
+ elif func == 'get_session_conf':
+ print(getSessionConf(version))
+ elif func == 'set_session_conf':
+ print(setSessionConf(version))
+ elif func == 'get_session_count':
+ print(getSessionCount(version))
+ elif func == 'clean_session_old':
+ print(cleanSessionOld(version))
+ elif func == 'get_disable_func':
+ print(getDisableFunc(version))
+ elif func == 'set_disable_func':
+ print(setDisableFunc(version))
+ elif func == 'get_phpinfo':
+ print(getPhpinfo(version))
+ elif func == 'get_lib_conf':
+ print(getLibConf(version))
+ elif func == 'install_lib':
+ print(installLib(version))
+ elif func == 'uninstall_lib':
+ print(uninstallLib(version))
+ else:
+ print("fail")
diff --git a/plugins/php-yum/index_php_yum.py b/plugins/php-yum/index_php_yum.py
new file mode 100755
index 000000000..07f623974
--- /dev/null
+++ b/plugins/php-yum/index_php_yum.py
@@ -0,0 +1,149 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import shutil
+
+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 'php'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return '/etc/opt/remi'
+
+
+def getInitDFile(version):
+ 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() + version
+
+
+def getConf(version):
+ path = getServerDir() + '/php' + version + '/php.ini'
+ return path
+
+
+def getFpmConfFile(version):
+ return getServerDir() + '/php' + version + '/php-fpm.d/mw.conf'
+
+
+def getPhpSocket(version):
+ path = getFpmConfFile(version)
+ content = mw.readFile(path)
+ rep = r'listen\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+
+def status(version):
+ # ps -ef|grep 'php/81' |grep -v grep | grep -v python | awk '{print $2}
+ cmd = "ps -ef|grep 'remi/php" + version + "' |grep -v grep | grep -v python | awk '{print $2}'"
+ data = mw.execShell(cmd)
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def getFpmAddress(version):
+ fpm_address = '/var/opt/remi/php{}/run/php-fpm/www.sock'.format(version)
+ php_fpm_file = getFpmConfFile(version)
+ try:
+ content = readFile(php_fpm_file)
+ tmp = re.findall(r"listen\s*=\s*(.+)", content)
+ if not tmp:
+ return fpm_address
+ if tmp[0].find('sock') != -1:
+ return fpm_address
+ if tmp[0].find(':') != -1:
+ listen_tmp = tmp[0].split(':')
+ if bind:
+ fpm_address = (listen_tmp[0], int(listen_tmp[1]))
+ else:
+ fpm_address = ('127.0.0.1', int(listen_tmp[1]))
+ else:
+ fpm_address = ('127.0.0.1', int(tmp[0]))
+ return fpm_address
+ except:
+ return fpm_address
+
+
+def getPhpinfo(version):
+ stat = status(version)
+ if stat == 'stop':
+ return 'PHP[' + version + ']未启动,不可访问!!!'
+
+ sock_file = getFpmAddress(version)
+ root_dir = mw.getFatherDir() + '/phpinfo'
+
+ mw.execShell("rm -rf " + root_dir)
+ mw.execShell("mkdir -p " + root_dir)
+ mw.writeFile(root_dir + '/phpinfo.php', '')
+ sock_data = mw.requestFcgiPHP(sock_file, '/phpinfo.php', root_dir)
+ os.system("rm -rf " + root_dir)
+ phpinfo = str(sock_data, encoding='utf-8')
+ return phpinfo
+
+
+def libConfCommon(version):
+ fname = getConf(version)
+ if not os.path.exists(fname):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+
+ phpini = mw.readFile(fname)
+
+ libpath = getPluginDir() + '/versions/phplib.conf'
+ phplib = json.loads(mw.readFile(libpath))
+
+ libs = []
+ tasks = mw.M('tasks').where(
+ "status!=?", ('1',)).field('status,name').select()
+ for lib in phplib:
+ lib['task'] = '1'
+ for task in tasks:
+ tmp = mw.getStrBetween('[', ']', task['name'])
+ if not tmp:
+ continue
+ tmp1 = tmp.split('-')
+ if tmp1[0].lower() == lib['name'].lower():
+ lib['task'] = task['status']
+ lib['phpversions'] = []
+ lib['phpversions'].append(tmp1[1])
+ if phpini.find(lib['check']) == -1:
+ lib['status'] = False
+ else:
+ lib['status'] = True
+ libs.append(lib)
+ return libs
+
+
+def get_php_info(args):
+ return getPhpinfo(args['version'])
+
+
+def get_lib_conf(data):
+ libs = libConfCommon(data['version'])
+ return mw.returnData(True, 'OK!', libs)
diff --git a/plugins/php-yum/info.json b/plugins/php-yum/info.json
new file mode 100755
index 000000000..1c58a264d
--- /dev/null
+++ b/plugins/php-yum/info.json
@@ -0,0 +1,19 @@
+{
+ "sort": 7,
+ "ps": "PHP是世界上最好的编程语言(极速安装)",
+ "shell": "install.sh",
+ "name": "php-yum",
+ "title": "PHP[YUM]",
+ "coexist": true,
+ "versions": ["74","80","81","82","83","84","85"],
+ "install_pre_inspection":true,
+ "tip": "soft",
+ "checks": "server/php-yum/VERSION",
+ "path": "server/php-yum/VERSION",
+ "display": 1,
+ "author": "Zend",
+ "date": "2022-07-07",
+ "home": "https://www.php.net",
+ "type": "PHP语言解释器",
+ "pid": "6"
+}
\ No newline at end of file
diff --git a/plugins/php-yum/install.sh b/plugins/php-yum/install.sh
new file mode 100755
index 000000000..4ed4d994b
--- /dev/null
+++ b/plugins/php-yum/install.sh
@@ -0,0 +1,116 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+if id www &> /dev/null ;then
+ echo "www uid is `id -u www`"
+ echo "www shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `"
+else
+ groupadd www
+ useradd -g www -s /sbin/nologin www
+ # useradd -g www -s /bin/bash www
+fi
+
+action=$1
+type=$2
+
+if [ "${2}" == "" ];then
+ echo '缺少安装脚本...'
+ exit 0
+fi
+
+if [ ! -d $curPath/versions/$2 ];then
+ echo '缺少安装脚本2...'
+ exit 0
+fi
+
+# cd /www/server/mdserver-web/plugins/php-yum/versions && bash common.sh 83 install opcache
+
+#获取信息和版本
+# bash /www/server/mdserver-web/scripts/getos.sh
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+if [ "$OSNAME" == "alma" ];then
+ rpm -Uvh http://rpms.remirepo.net/enterprise/remi-release-${VERSION_ID}.rpm
+fi
+
+if [ "$OSNAME" == "rocky" ];then
+ rpm -Uvh http://rpms.remirepo.net/enterprise/remi-release-${VERSION_ID}.rpm
+fi
+
+if [ "$OSNAME" == "centos" ];then
+ rpm -Uvh http://rpms.remirepo.net/enterprise/remi-release-${VERSION_ID}.rpm
+fi
+
+
+# rpm -Uvh http://rpms.remirepo.net/fedora/remi-release-31.rpm
+if [ "$OSNAME" == "fedora" ];then
+ rpm -Uvh http://rpms.remirepo.net/fedora/remi-release-${VERSION_ID}.rpm
+fi
+
+
+
+if [ "${action}" == "uninstall" ] && [ -d ${serverPath}/php-yum/${type} ];then
+ #初始化
+ cd ${rootPath} && python3 ${rootPath}/plugins/php-yum/index.py stop ${type}
+ cd ${rootPath} && python3 ${rootPath}/plugins/php-yum/index.py initd_uninstall ${type}
+
+ if [ -f /lib/systemd/system/php${type}-php-fpm.service ];then
+ rm -rf /lib/systemd/system/php${type}-fpm.service
+ fi
+
+ if [ -f /lib/systemd/system/system/php${type}-php-fpm.service ];then
+ rm -rf /lib/systemd/system/php${type}-php-fpm.service
+ fi
+
+ systemctl daemon-reload
+fi
+
+cd ${curPath} && sh -x $curPath/versions/$2/install.sh $1
+
+if [ "${action}" == "install" ] && [ -d ${serverPath}/php-yum/${type} ];then
+
+ # 安装通用扩展
+ echo "install PHP-YUM[${type}] extend start"
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install mysqlnd
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install mysql
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install gd
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install iconv
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install exif
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install intl
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install mcrypt
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install bcmath
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install openssl
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install gettext
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install redis
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install memcached
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install mbstring
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install mongodb
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install zip
+ cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install simplexml
+
+ echo "install PHP-YUM[${type}] extend end"
+
+ #初始化
+ cd ${rootPath} && python3 plugins/php-yum/index.py start ${type}
+ cd ${rootPath} && python3 plugins/php-yum/index.py initd_install ${type}
+
+ if [ ! -f /usr/local/bin/composer ];then
+ cd /tmp
+ curl -sS https://getcomposer.org/installer | /opt/remi/php${type}/root/usr/bin/php
+ mv composer.phar /usr/local/bin/composer
+ fi
+
+ echo "PHP-YUM[${type}] start ..."
+ systemctl restart php${type}-php-fpm
+ echo "PHP-YUM[${type}] start ok"
+fi
+
+
diff --git a/plugins/php-yum/js/php.js b/plugins/php-yum/js/php.js
new file mode 100755
index 000000000..5629b8983
--- /dev/null
+++ b/plugins/php-yum/js/php.js
@@ -0,0 +1,743 @@
+function phpPost(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'php-yum';
+ req_data['func'] = method;
+ req_data['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ //错误展示10S
+ layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function phpPostCallback(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'php-yum';
+ req_data['func'] = method;
+ req_data['script']='index_php_yum';
+ args['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+//配置修改
+function phpSetConfig(version) {
+ phpPost('get_php_conf', version,'',function(data){
+ // console.log(data);
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata);
+ var mlist = '';
+ for (var i = 0; i < rdata.length; i++) {
+ var w = '70'
+ if (rdata[i].name == 'error_reporting') w = '250';
+ var ibody = '
';
+ switch (rdata[i].type) {
+ case 0:
+ var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
+ var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
+ ibody = '
开启 关闭 '
+ break;
+ case 1:
+ var selected_1 = (rdata[i].value == 'On') ? 'selected' : '';
+ var selected_0 = (rdata[i].value == 'Off') ? 'selected' : '';
+ ibody = '
开启 关闭 '
+ break;
+ }
+ mlist += '
' + rdata[i].name + ' ' + ibody + ', ' + rdata[i].ps + '
'
+ }
+ var phpCon = '
\
+ ' + mlist + '\
+
刷新 保存
\
+
'
+ $(".soft-man-con").html(phpCon);
+ });
+}
+
+
+//提交PHP配置
+function submitConf(version) {
+ var data = {
+ version: version,
+ display_errors: $("select[name='display_errors']").val(),
+ 'cgi.fix_pathinfo': $("select[name='cgi.fix_pathinfo']").val(),
+ 'date.timezone': $("input[name='date.timezone']").val(),
+ short_open_tag: $("select[name='short_open_tag']").val(),
+ asp_tags: $("select[name='asp_tags']").val() || 'On',
+ safe_mode: $("select[name='safe_mode']").val(),
+ max_execution_time: $("input[name='max_execution_time']").val(),
+ max_input_time: $("input[name='max_input_time']").val(),
+ max_input_vars: $("input[name='max_input_vars']").val(),
+ memory_limit: $("input[name='memory_limit']").val(),
+ post_max_size: $("input[name='post_max_size']").val(),
+ file_uploads: $("select[name='file_uploads']").val(),
+ upload_max_filesize: $("input[name='upload_max_filesize']").val(),
+ max_file_uploads: $("input[name='max_file_uploads']").val(),
+ default_socket_timeout: $("input[name='default_socket_timeout']").val(),
+ error_reporting: $("input[name='error_reporting']").val() || 'On'
+ };
+
+ phpPost('submit_php_conf', version, data, function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ // console.log(rdata);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+
+
+//php超时限制
+function phpCommonFunc(version){
+ phpPost('get_limit_conf', version, '', function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ var con = '
\
+ 超时限制 \
+ , 秒\
+ 保存 \
+
';
+
+ con += '
\
+ 上传限制 \
+ ,MB\
+ 保存 \
+
';
+
+ con += '
\
+ 查看phpinfo() \
+ 预加载脚本 \
+ OPCACHE黑名单 \
+ PHP-FPM(global) \
+
';
+
+ $(".soft-man-con").html(con);
+ });
+}
+
+//设置超时限制
+function setPHPMaxTime(version) {
+ var max = $(".phpTimeLimit").val();
+ phpPost('set_max_time',version,{'time':max},function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg,function(){
+ phpCommonFunc(version);
+ },{ icon: rdata.status ? 1 : 2 });
+
+ });
+}
+//设置PHP上传限制
+function setPHPMaxSize(version) {
+ max = $(".phpUploadLimit").val();
+ if (max < 2) {
+ alert(max);
+ layer.msg('上传大小限制不能小于2M', { icon: 2 });
+ return;
+ }
+
+ phpPost('set_max_size',version,{'max':max},function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+function phpPreload(version){
+ phpPost('app_start',version,{},function(data){
+ onlineEditFile(0, data['data']);
+ });
+}
+
+function phpOpcacheBlacklist(version){
+ phpPost('opcache_blacklist_file',version,{},function(data){
+ onlineEditFile(0, data['data']);
+ });
+}
+
+function phpFpmRoot(version){
+ phpPost('get_fpm_file',version,{},function(data){
+ onlineEditFile(0, data['data']);
+ });
+}
+
+function getFpmConfig(version, pool = 'www'){
+ phpPost('get_fpm_conf', version, {'pool':pool}, function(data){
+ // console.log(data);
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata);
+ var limitList = "
自定义 " +
+ "
2并发 " +
+ "
5并发 " +
+ "
10并发 " +
+ "
30并发 " +
+ "
50并发 " +
+ "
100并发 " +
+ "
200并发 " +
+ "
300并发 " +
+ "
500并发 " +
+ "
2000并发 ";
+ var pms = [{ 'name': 'static', 'title': '静态' }, { 'name': 'dynamic', 'title': '动态' },{ 'name': 'ondemand', 'title': '按需' }];
+ var pmList = '';
+ for (var i = 0; i < pms.length; i++) {
+ pmList += '
' + pms[i].title + ' ';
+ }
+
+ var poolHtml = "
www " +
+ "
backup ";
+
+ var body = "
" +
+ "
应用池[pool]: " + poolHtml + "
" +
+ "
并发方案: " + limitList + "
" +
+ "
运行模式: " + pmList + " *PHP-FPM运行模式
" +
+ "
max_children: *允许创建的最大子进程数
" +
+ "
start_servers: *起始进程数(服务启动后初始进程数量)
" +
+ "
min_spare_servers: *最小空闲进程数(清理空闲进程后的保留数量)
" +
+ "
max_spare_servers: *最大空闲进程数(当空闲进程达到此值时清理)
" +
+ "
保存
" +
+ "
";
+
+ $(".soft-man-con").html(body);
+ $("select[name='limit']").change(function() {
+ var type = $(this).val();
+ var max_children = rdata.max_children;
+ var start_servers = rdata.start_servers;
+ var min_spare_servers = rdata.min_spare_servers;
+ var max_spare_servers = rdata.max_spare_servers;
+ switch (type) {
+ case '0':
+ max_children = 2;
+ start_servers = 1;
+ min_spare_servers = 1;
+ max_spare_servers = 2;
+ break;
+ case '1':
+ max_children = 5;
+ start_servers = 2;
+ min_spare_servers = 1;
+ max_spare_servers = 5;
+ break;
+ case '2':
+ max_children = 10;
+ start_servers = 2;
+ min_spare_servers = 1;
+ max_spare_servers = 10;
+ break;
+ case '3':
+ max_children = 30;
+ start_servers = 5;
+ min_spare_servers = 5;
+ max_spare_servers = 20;
+ break;
+ case '4':
+ max_children = 50;
+ start_servers = 15;
+ min_spare_servers = 15;
+ max_spare_servers = 35;
+ break;
+ case '5':
+ max_children = 100;
+ start_servers = 20;
+ min_spare_servers = 20;
+ max_spare_servers = 70;
+ break;
+ case '6':
+ max_children = 200;
+ start_servers = 25;
+ min_spare_servers = 25;
+ max_spare_servers = 150;
+ break;
+ case '7':
+ max_children = 300;
+ start_servers = 30;
+ min_spare_servers = 30;
+ max_spare_servers = 180;
+ break;
+ case '8':
+ max_children = 500;
+ start_servers = 35;
+ min_spare_servers = 35;
+ max_spare_servers = 250;
+ break;
+ case '9':
+ max_children = 2000;
+ start_servers = 40;
+ min_spare_servers = 40;
+ max_spare_servers = 255;
+ break;
+ }
+
+ $("input[name='max_children']").val(max_children);
+ $("input[name='start_servers']").val(start_servers);
+ $("input[name='min_spare_servers']").val(min_spare_servers);
+ $("input[name='max_spare_servers']").val(max_spare_servers);
+ });
+
+ $('select[name="pool"]').change(function(){
+ var pool = $(this).val();
+ getFpmConfig(version, pool);
+ });
+ });
+}
+
+function setFpmConfig(version){
+ var max_children = Number($("input[name='max_children']").val());
+ var start_servers = Number($("input[name='start_servers']").val());
+ var min_spare_servers = Number($("input[name='min_spare_servers']").val());
+ var max_spare_servers = Number($("input[name='max_spare_servers']").val());
+ var pm = $("select[name='pm']").val();
+
+ if (max_children < max_spare_servers) {
+ layer.msg('max_spare_servers 不能大于 max_children', { icon: 2 });
+ return;
+ }
+
+ if (min_spare_servers > start_servers) {
+ layer.msg('min_spare_servers 不能大于 start_servers', { icon: 2 });
+ return;
+ }
+
+ if (max_spare_servers < min_spare_servers) {
+ layer.msg('min_spare_servers 不能大于 max_spare_servers', { icon: 2 });
+ return;
+ }
+
+ if (max_children < start_servers) {
+ layer.msg('start_servers 不能大于 max_children', { icon: 2 });
+ return;
+ }
+
+ if (max_children < 1 || start_servers < 1 || min_spare_servers < 1 || max_spare_servers < 1) {
+ layer.msg('配置值不能小于1', { icon: 2 });
+ return;
+ }
+
+ var data = {
+ version:version,
+ max_children:max_children,
+ start_servers:start_servers,
+ min_spare_servers:min_spare_servers,
+ max_spare_servers:max_spare_servers,
+ pm:pm,
+ };
+ phpPost('set_fpm_conf', version, data, function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+
+function getFpmStatus(version){
+ phpPost('get_fpm_status', version, '', function(ret_data){
+ var tmp_data = $.parseJSON(ret_data.data);
+ if(!tmp_data.status){
+ layer.msg(tmp_data.msg, { icon: tmp_data.status ? 1 : 2 });
+ return;
+ }
+
+ var rdata = tmp_data.data;
+ var php_fpm_status = '动态';
+ if (rdata['process manager'] == 'dynamic'){
+ php_fpm_status = '动态';
+ } else if(rdata['process manager'] == 'static'){
+ php_fpm_status = '静态';
+ } else if(rdata['process manager'] == 'ondemand'){
+ php_fpm_status = '按需';
+ }
+
+ var con = "
\
+ 应用池(pool) " + rdata.pool + " \
+ 进程管理方式(process manager) " + php_fpm_status + " \
+ 启动日期(start time) " + rdata['start time'] + " \
+ 请求数(accepted conn) " + rdata['accepted conn'] + " \
+ 请求队列(listen queue) " + rdata['listen queue'] + " \
+ 最大等待队列(max listen queue) " + rdata['max listen queue'] + " \
+ socket队列长度(listen queue len) " + rdata['listen queue len'] + " \
+ 空闲进程数量(idle processes) " + rdata['idle processes'] + " \
+ 活跃进程数量(active processes) " + rdata['active processes'] + " \
+ 总进程数量(total processes) " + rdata['total processes'] + " \
+ 最大活跃进程数量(max active processes) " + rdata['max active processes'] + " \
+ 到达进程上限次数(max children reached) " + rdata['max children reached'] + " \
+ 慢请求数量(slow requests) " + rdata['slow requests'] + " \
+
";
+ $(".soft-man-con").html(con);
+ $(".GetPHPStatus td,.GetPHPStatus th").css("padding", "7px");
+ });
+}
+
+function getSessionConfig(version){
+ phpPost('get_session_conf', version, '', function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ if(!rdata.status){
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ return;
+ }
+ var rdata = rdata.data;
+
+ var cacheList = "
files " +
+ "
redis " +
+ "
memcache " +
+ "
memcached ";
+
+
+ var info = rdata.save_path.split(":");
+ var con = "
" +
+ "
存储模式: " + cacheList + "
" +
+ "
IP地址:
" +
+ "
端口:
" +
+ "
密码:
" +
+ "
保存
" +
+ "
\
+
\
+ 若你的站点并发比较高,使用Redis,Memcache能有效提升PHP并发能力 \
+ 若调整Session模式后,网站访问异常,请切换回原来的模式 \
+ 切换Session模式会使在线的用户会话丢失,请在流量小的时候切换 \
+ \
+
\
+
\
+
";
+
+ $(".soft-man-con").html(con);
+
+ if (rdata.save_handler == 'files'){
+ $('input[name="ip"]').attr('disabled','disabled');
+ $('input[name="port"]').attr('disabled','disabled');
+ $('input[name="passwd"]').attr('placeholder','如果没有密码留空');
+ $('input[name="passwd"]').attr('disabled','disabled');
+ }
+
+ // change event
+ $("select[name='save_handler']").change(function() {
+ var type = $(this).val();
+
+ var passwd = $('input[name="passwd"]').val();
+ if (passwd == ""){
+ $('input[name="passwd"]').attr('placeholder','如果没有密码留空');
+ }
+
+ var ip = $('input[name="ip"]').val();
+ if (ip == ""){
+ $('input[name="ip"]').val('127.0.0.1');
+ }
+
+ switch (type) {
+ case 'redis':
+ var port = $('input[name="port"]').val();
+ if (port == ""){
+ $('input[name="port"]').val('6379');
+ }
+ $('input[name="ip"]').removeAttr('disabled');
+ $('input[name="port"]').removeAttr('disabled');
+ $('input[name="passwd"]').removeAttr('disabled');
+ break;
+ case 'files':
+ $('input[name="ip"]').val("").attr('disabled','disabled');
+ $('input[name="port"]').val("").attr('disabled','disabled');
+ $('input[name="passwd"]').val("").attr('disabled','disabled');
+ break;
+ case 'memcache':
+ var port = $('input[name="port"]').val();
+ if (port == ""){
+ $('input[name="port"]').val('11211');
+ }
+ $('input[name="ip"]').removeAttr('disabled');
+ $('input[name="port"]').removeAttr('disabled');
+ $('input[name="passwd"]').removeAttr('disabled');
+ break;
+ case 'memcached':
+ var port = $('input[name="port"]').val();
+ if (port == ""){
+ $('input[name="port"]').val('11211');
+ }
+ $('input[name="ip"]').removeAttr('disabled');
+ $('input[name="port"]').removeAttr('disabled');
+ $('input[name="passwd"]').removeAttr('disabled');
+ break;
+ }
+ });
+
+ //load session stats
+ phpPost('get_session_count', version, '', function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ if(!rdata.status){
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ return;
+ }
+ var rdata = rdata.data;
+
+ var html_var = "清理Session文件
\
+ \
+
\
+
总Session文件数量 "+rdata.total+"
\
+
可清理的Session文件数量 "+rdata.oldfile+"
\
+
\
+
清理session文件 ";
+
+ $("#session_clear").html(html_var);
+
+
+ $('#clean_func').click(function(){
+ phpPost('clean_session_old', version, '', function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ showMsg(rdata.msg,function(){
+ getSessionConfig(version);
+ },{ icon: rdata.status ? 1 : 2 });
+ });
+ });
+ });
+ });
+
+}
+
+function setSessionConfig(version){
+ var ip = $('input[name="ip"]').val();
+ var port = $('input[name="port"]').val();
+ var passwd = $('input[name="passwd"]').val();
+ var save_handler = $("select[name='save_handler']").val();
+ var data = {
+ ip:ip,
+ port:port,
+ passwd:passwd,
+ save_handler:save_handler,
+ };
+ phpPost('set_session_conf', version, data, function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+//禁用函数
+function disableFunc(version) {
+ phpPost('get_disable_func', version,'',function(data){
+ var rdata = $.parseJSON(data.data);
+ var disable_functions = rdata.disable_functions.split(',');
+ var dbody = ''
+ for (var i = 0; i < disable_functions.length; i++) {
+ if (disable_functions[i] == '') continue;
+ dbody += "
" + disable_functions[i] + " 删除 ";
+ }
+
+ var con = "
" +
+ " " +
+ "添加 " +
+ "
" +
+ "
" +
+ "名称 操作 " +
+ "" + dbody + " " +
+ "
";
+
+ con += '
\
+ 在此处可以禁用指定函数的调用,以增强环境安全性! \
+ 强烈建议禁用如exec,system等危险函数! \
+ ';
+
+ $(".soft-man-con").html(con);
+ });
+}
+//设置禁用函数
+function setDisableFunc(version, act, fs) {
+ var fsArr = fs.split(',');
+ if (act == 1) {
+ var functions = $("#disable_function_val").val();
+ for (var i = 0; i < fsArr.length; i++) {
+ if (functions == fsArr[i]) {
+ layer.msg(lan.soft.fun_msg, { icon: 5 });
+ return;
+ }
+ }
+ fs += ',' + functions;
+ msg = '添加成功';
+ } else {
+
+ fs = '';
+ for (var i = 0; i < fsArr.length; i++) {
+ if (act == fsArr[i]) continue;
+ fs += fsArr[i] + ','
+ }
+ msg = '删除成功';
+ fs = fs.substr(0, fs.length - 1);
+ }
+
+ var data = {
+ 'version':version,
+ 'disable_functions':fs,
+ };
+
+ phpPost('set_disable_func', version,data,function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.status ? msg : rdata.msg, function(){
+ disableFunc(version);
+ } ,{ icon: rdata.status ? 1 : 2 });
+ });
+}
+
+
+//phpinfo
+// function getPhpinfo(version) {
+// var con = '
查看phpinfo() ';
+// $(".soft-man-con").html(con);
+// }
+
+//获取PHPInfo
+function getPHPInfo_old(version) {
+ phpPost('get_phpinfo', version, '', function(data){
+ var rdata = data.data;
+ layer.open({
+ type: 1,
+ title: "PHP-" + version + "-PHPINFO",
+ area: ['90%', '90%'],
+ closeBtn: 2,
+ shadeClose: true,
+ content: rdata
+ });
+ });
+}
+
+function getPHPInfo(version) {
+ phpPostCallback('get_php_info', version, {}, function(data){
+ if (!data.status){
+ layer.msg(rdata.msg, { icon: 2 });
+ return;
+ }
+
+ layer.open({
+ type: 1,
+ title: "PHP-" + version + "-PHPINFO",
+ area: ['70%', '90%'],
+ closeBtn: 2,
+ shadeClose: true,
+ content: data.data.replace('a:link {color: #009; text-decoration: none; background-color: #fff;}', '').replace('a:link {color: #000099; text-decoration: none; background-color: #ffffff;}', '')
+ });
+ })
+}
+
+
+
+function phpLibConfig(version){
+
+ phpPost('get_lib_conf', version, '', function(data){
+ var rdata = $.parseJSON(data.data);
+
+ if (!rdata.status){
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ return;
+ }
+
+ var libs = rdata.data;
+ var body = '';
+ var opt = '';
+
+ for (var i = 0; i < libs.length; i++) {
+ if (libs[i].versions.indexOf(version) == -1){
+ continue;
+ }
+
+ if (libs[i]['task'] == '-1' && libs[i].phpversions.indexOf(version) != -1) {
+ opt = '
安装. '
+ } else if (libs[i]['task'] == '0' && libs[i].phpversions.indexOf(version) != -1) {
+ opt = '
等待. '
+ } else if (libs[i].status) {
+ opt = '
卸载 '
+ } else {
+ opt = '
安装 '
+ }
+
+ body += '
' +
+ '' + libs[i].name + ' ' +
+ '' + libs[i].type + ' ' +
+ '' + libs[i].msg + ' ' +
+ ' ' +
+ '' + opt + ' ' +
+ ' ';
+ }
+
+
+ var con = '
' +
+ '
' +
+ '' +
+ '' +
+ '名称 ' +
+ '类型 ' +
+ '说明 ' +
+ '状态 ' +
+ '操作 ' +
+ ' ' +
+ ' ' +
+ '' + body + ' ' +
+ '
' +
+ '
' +
+ '
\
+ 请按实际需求安装扩展,不要安装不必要的PHP扩展,这会影响PHP执行效率,甚至出现异常 \
+ Redis扩展只允许在1个PHP版本中使用,安装到其它PHP版本请在[软件管理]重装Redis \
+ opcache/xcache/apc等脚本缓存扩展,请只安装其中1个,否则可能导致您的站点程序异常 \
+ ioncube要在ZendGuardLoader/opcache前安装,否则可能导致您的站点程序异常 \
+ ';
+ $('.soft-man-con').html(con);
+ });
+
+}
+
+//安装扩展
+function installPHPLib(version, name, title, pathinfo) {
+ layer.confirm('您真的要安装{1}吗?'.replace('{1}', name), { icon: 3, closeBtn: 2 }, function() {
+ name = name.toLowerCase();
+ var data = "name=" + name + "&version=" + version + "&type=1";
+
+ phpPost('install_lib', version, data, function(data){
+ var rdata = $.parseJSON(data.data);
+ // layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ showMsg(rdata.msg, function(){
+ getTaskCount();
+ phpLibConfig(version);
+ },{ icon: rdata.status ? 1 : 2 });
+
+ });
+ });
+}
+
+//卸载扩展
+function uninstallPHPLib(version, name, title, pathinfo) {
+ layer.confirm('您真的要卸载{1}吗?'.replace('{1}', name), { icon: 3, closeBtn: 2 }, function() {
+ name = name.toLowerCase();
+ var data = 'name=' + name + '&version=' + version;
+ phpPost('uninstall_lib', version, data, function(data){
+ var rdata = $.parseJSON(data.data);
+ // layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ showMsg(rdata.msg, function(){
+ getTaskCount();
+ phpLibConfig(version);
+ },{ icon: rdata.status ? 1 : 2 },5000);
+
+ });
+ });
+}
\ No newline at end of file
diff --git a/plugins/php-yum/versions/74/install.sh b/plugins/php-yum/versions/74/install.sh
new file mode 100755
index 000000000..b2098e1e5
--- /dev/null
+++ b/plugins/php-yum/versions/74/install.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+version=7.4.x
+PHP_VER=74
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+
+yum install -y php74 php74-php-fpm
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-yum/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ yum remove -y php74 php74-php-fpm php74-*
+ rm -rf $serverPath/php-yum/${PHP_VER}
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-yum/versions/80/install.sh b/plugins/php-yum/versions/80/install.sh
new file mode 100755
index 000000000..070c2544c
--- /dev/null
+++ b/plugins/php-yum/versions/80/install.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+version=8.0.x
+PHP_VER=80
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+
+
+yum install -y php80 php80-php-fpm
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-yum/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ # $serverPath/php-ya/init.d/php${PHP_VER} stop
+ rm -rf $serverPath/php-yum/${PHP_VER}
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-yum/versions/81/install.sh b/plugins/php-yum/versions/81/install.sh
new file mode 100755
index 000000000..5877e21df
--- /dev/null
+++ b/plugins/php-yum/versions/81/install.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+version=8.1.x
+PHP_VER=81
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+yum install -y php81 php81-php-fpm
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-yum/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ yum remove -y php81 php81-php-fpm php81-*
+ rm -rf $serverPath/php-yum/${PHP_VER}
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-yum/versions/82/install.sh b/plugins/php-yum/versions/82/install.sh
new file mode 100755
index 000000000..6440c2966
--- /dev/null
+++ b/plugins/php-yum/versions/82/install.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+version=8.2.x
+PHP_VER=82
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+yum install -y php82 php82-php-fpm
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-yum/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ yum remove -y php82 php82-php-fpm php82-*
+ rm -rf $serverPath/php-yum/${PHP_VER}
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-yum/versions/83/install.sh b/plugins/php-yum/versions/83/install.sh
new file mode 100755
index 000000000..1a195b8c9
--- /dev/null
+++ b/plugins/php-yum/versions/83/install.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+version=8.3.x
+PHP_VER=83
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+yum install -y php83 php83-php-fpm
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-yum/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ yum remove -y php83 php83-php-fpm php83-*
+ rm -rf $serverPath/php-yum/${PHP_VER}
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-yum/versions/84/install.sh b/plugins/php-yum/versions/84/install.sh
new file mode 100755
index 000000000..105495362
--- /dev/null
+++ b/plugins/php-yum/versions/84/install.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+version=8.4.x
+PHP_VER=84
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+yum install -y php84 php84-php-fpm
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-yum/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ yum remove -y php84 php84-php-fpm php84-*
+ rm -rf $serverPath/php-yum/${PHP_VER}
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-yum/versions/85/install.sh b/plugins/php-yum/versions/85/install.sh
new file mode 100755
index 000000000..98c778845
--- /dev/null
+++ b/plugins/php-yum/versions/85/install.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+version=8.5.x
+PHP_VER=85
+
+
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+yum install -y php85 php85-php-fpm
+if [ "$?" == "0" ];then
+ mkdir -p $serverPath/php-yum/${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ yum remove -y php85 php85-php-fpm php85-*
+ rm -rf $serverPath/php-yum/${PHP_VER}
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php-yum/versions/common.sh b/plugins/php-yum/versions/common.sh
new file mode 100644
index 000000000..7eb72bfd8
--- /dev/null
+++ b/plugins/php-yum/versions/common.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+version=$1
+action=$2
+extName=$3
+
+# echo $1,$2,$3
+
+# echo $curPath
+# echo $rootPath
+# echo $serverPath
+
+FILE=${curPath}/${version}/${extName}.sh
+FILE_COMMON=${curPath}/common/${extName}.sh
+# yum install -y php81-php-yar
+# yum install -y php74-php-pecl-mysql
+
+
+if [ "$action" == 'install' ];then
+
+ if [ -f $FILE ];then
+ bash ${curPath}/${version}/${extName}.sh install
+ elif [ -f $FILE_COMMON ];then
+ bash ${FILE_COMMON} install ${version}
+ else
+ yum install -y php${version}-php-${extName}
+ yum install -y php${version}-php-pecl-${extName}
+ fi
+
+ # if [ "${extName}" == "mysql" ];then
+ # yum install -y php74-php-pecl-mysql
+ # fi
+fi
+
+# yum remove -y php81-php-yar
+if [ "$action" == 'uninstall' ];then
+
+ if [ -f $FILE ];then
+ bash ${curPath}/${version}/${extName}.sh uninstall
+ elif [ -f $FILE_COMMON ];then
+ bash ${FILE_COMMON} uninstall ${version}
+ else
+ yum remove -y php${version}-php-${extName}
+ yum remove -y php${version}-php-pecl-${extName}
+ fi
+fi
+
+echo "yum install -y php${version}-php-${extName}"
+echo "yum install -y php${version}-php-pecl-${extName}"
+echo "-----------------------------------------------"
+echo "yum remove -y php${version}-php-${extName}"
+echo "yum remove -y php${version}-php-pecl-${extName}"
+
+
+echo "systemctl restart php${version}-php-fpm"
+php_status=`systemctl status php${version}-php-fpm | grep inactive`
+echo "php_status:${php_status}"
+if [ "$php_status" == "" ];then
+ systemctl restart php${version}-php-fpm
+fi
+
diff --git a/plugins/php-yum/versions/common/bak_brotli.sh b/plugins/php-yum/versions/common/bak_brotli.sh
new file mode 100755
index 000000000..a23a344b0
--- /dev/null
+++ b/plugins/php-yum/versions/common/bak_brotli.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+sysName=`uname`
+LIBNAME=brotli
+LIBV=0.15.2
+
+SORT_LIBNAME="10-${LIBNAME}"
+extVer=`bash $curPath/lib.sh $version`
+extFile=/opt/remi/php${version}/root/usr/lib64/php
+extSoFile=$extFile/modules/${LIBNAME}.so
+cfgDir=/etc/opt/remi/php${version}/php.d
+extIni=${cfgDir}/10-${LIBNAME}.ini
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n' |grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ /opt/remi/php${version}/root/usr/bin/phpize
+ ./configure --with-php-config=/opt/remi/php${version}/root/usr/bin/
+ make && make install && make clean
+
+ fi
+ echo "$extFile checking ..."
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return;
+ fi
+
+ cho "" >> $extIni
+ echo "[${LIBNAME}]" >> $extIni
+ echo "extension=${LIBNAME}.so" >> $extIni
+
+ systemctl restart php${version}-fpm
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ -f $extIni ];then
+ rm -rf $extIni
+ fi
+
+ systemctl restart php${version}-fpm
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php-yum/versions/common/ioncube.sh b/plugins/php-yum/versions/common/ioncube.sh
new file mode 100755
index 000000000..feb87ab73
--- /dev/null
+++ b/plugins/php-yum/versions/common/ioncube.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+sysName=`uname`
+LIBNAME=ioncube
+LIBV=0
+
+if [ `echo "$version > 82"|bc` -eq 1 ];then
+ echo "I won't support it"
+ exit 0
+fi
+
+SORT_LIBNAME="10-${LIBNAME}"
+extVer=`bash $curPath/lib.sh $version`
+extFile=/opt/remi/php${version}/root/usr/lib64/php
+extSoFile=$extFile/modules/${LIBNAME}.so
+cfgDir=/etc/opt/remi/php${version}/php.d
+extIni=${cfgDir}/10-${LIBNAME}.ini
+
+echo $extSoFile
+echo $extIni
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+
+min_ver=${version:0:1}.${version:1:2}
+
+Install_lib()
+{
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -f $php_lib/ioncube_loaders_lin.tar.gz ];then
+ wget -O $php_lib/ioncube_loaders_lin.tar.gz https://downloads.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz
+ cd $php_lib && tar -zxvf ioncube_loaders_lin.tar.gz
+ fi
+ cd $php_lib/ioncube
+
+ cp -rf $php_lib/ioncube/ioncube_loader_lin_${min_ver}.so $extSoFile
+
+ fi
+
+ echo "$extSoFile checking ..."
+ if [ ! -f "$extSoFile" ];then
+ echo "ERROR!"
+ return;
+ fi
+
+ echo "" >> $extIni
+ echo "[${LIBNAME}]" >> $extIni
+ echo "zend_extension=${LIBNAME}.so" >> $extIni
+
+ systemctl restart php${version}-fpm
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+
+ if [ -f $extIni ];then
+ rm -rf $extIni
+ fi
+
+ systemctl restart php${version}-fpm
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php-yum/versions/common/opcache.sh b/plugins/php-yum/versions/common/opcache.sh
new file mode 100755
index 000000000..873253b79
--- /dev/null
+++ b/plugins/php-yum/versions/common/opcache.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+sysName=`uname`
+LIBNAME=opcache
+
+cfgDir=/etc/opt/remi
+
+OP_BL=${serverPath}/server/php-yum/opcache-blacklist.txt
+if [ ! -f $OP_BL ];then
+ touch $OP_BL
+fi
+ext_dir=${cfgDir}/php${version}/php.d
+ext_file=${ext_dir}/10-opcache.ini
+
+echo $ext_file
+
+if [ "$actionType" == 'install' ];then
+ yum install -y php${version}-php-${LIBNAME}
+ echo "ls ${cfgDir}/php${version}/php.d | grep "${LIBNAME}.ini"| cut -d \ -f 1"
+ find_opcache=`ls ${cfgDir}/php${version}/php.d | grep "${LIBNAME}.ini"| cut -d \ -f 1`
+ echo $find_opcache
+ if [ "$find_opcache" != "" ];then
+ ext_file=${ext_dir}/${find_opcache}
+ fi
+ echo $ext_file
+ echo "zend_extension=${LIBNAME}" >> $ext_file
+ echo "opcache.enable=1" >> $ext_file
+ echo "opcache.memory_consumption=128" >> $ext_file
+ echo "opcache.interned_strings_buffer=8" >> $ext_file
+ echo "opcache.max_accelerated_files=4000" >> $ext_file
+ echo "opcache.revalidate_freq=60" >> $ext_file
+ echo "opcache.fast_shutdown=1" >> $ext_file
+ echo "opcache.enable_cli=1" >> $ext_file
+ echo "opcache.jit=1205" >> $ext_file
+ echo "opcache.jit_buffer_size=64M" >> $ext_file
+ echo "opcache.save_comments=0" >> $ext_file
+ echo "opcache.blacklist_filename=${OP_BL}" >> $ext_file
+elif [ "$actionType" == 'uninstall' ];then
+ if [ -f ${ext_dir}/10-opcache.ini.rpmsave ];then
+ ext_file=${ext_dir}/10-opcache.ini.rpmsave
+ fi
+
+ # yum remove -y php83-php-opcache
+ yum remove -y php${version}-php-${LIBNAME}
+ rm -rf $ext_file
+ echo 'cannot uninstall'
+fi
\ No newline at end of file
diff --git a/plugins/php-yum/versions/common/sg11.sh b/plugins/php-yum/versions/common/sg11.sh
new file mode 100644
index 000000000..afe03f329
--- /dev/null
+++ b/plugins/php-yum/versions/common/sg11.sh
@@ -0,0 +1,122 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+# https://www.sourceguardian.com/loaders.html
+
+# support 52-83
+
+LIBNAME=sg11
+LIBV=0
+
+sysName=`uname`
+actionType=$1
+version=$2
+SG_VER=${version:0:1}.${version:1:2}
+
+
+SORT_LIBNAME="10-${LIBNAME}"
+extVer=`bash $curPath/lib.sh $version`
+extFile=/opt/remi/php${version}/root/usr/lib64/php
+extSoFile=$extFile/modules/${LIBNAME}.so
+cfgDir=/etc/opt/remi/php${version}/php.d
+extIni=${cfgDir}/10-${LIBNAME}.ini
+
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ bash ${rootPath}/scripts/getos.sh
+ OSNAME=`cat ${rootPath}/data/osname.pl`
+ if [ "$OSNAME" == 'macos' ];then
+ VERSION_ID=none
+ else
+ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+ fi
+
+ echo "${OSNAME}:${VERSION_ID}"
+
+ DEFAULT_OSNAME=linux-x86_64
+ SUFFIX_NAME=lin
+ if [ "$OSNAME" == 'macos' ];then
+ DEFAULT_OSNAME=macosx
+ SUFFIX_NAME=dar
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+
+ mkdir -p $php_lib
+ mkdir -p $php_lib/sg11
+
+ if [ ! -f $php_lib/sg11_loaders.tar.bz2 ];then
+ curl -sSLo $php_lib/sg11_loaders.tar.bz2 https://www.sourceguardian.com/loaders/download/loaders.tar.bz2
+ echo "cd $php_lib && tar -jxvf $php_lib/sg11_loaders.tar.bz2 -C $php_lib/sg11"
+ cd $php_lib && tar -jxvf $php_lib/sg11_loaders.tar.bz2 -C $php_lib/sg11
+ fi
+
+
+ if [ ! -d $php_lib/sg11/macosx ];then
+ cd $php_lib && tar -jxvf $php_lib/sg11_loaders.tar.bz2 -C $php_lib/sg11
+ fi
+ cd $php_lib/sg11
+
+
+ if [ -f $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.${SUFFIX_NAME} ];then
+ cp -rf $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.${SUFFIX_NAME} $extSoFile
+ else
+ echo 'Not supported temporarily'
+ exit
+ fi
+
+ if [ "$OSNAME" == 'macos' ];then
+ xattr -c * $extSoFile
+ fi
+ fi
+
+ if [ ! -f "$extSoFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $extIni
+ echo "[${LIBNAME}]" >> $extIni
+ echo "extension=${LIBNAME}.so" >> $extIni
+
+ systemctl restart php${version}-fpm
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ -f $extIni ];then
+ rm -rf $extIni
+ fi
+
+ systemctl restart php${version}-fpm
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php-yum/versions/lib.sh b/plugins/php-yum/versions/lib.sh
new file mode 100644
index 000000000..91a8e8d27
--- /dev/null
+++ b/plugins/php-yum/versions/lib.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+version=$1
+action=$2
+
+
+php_fpm_service_file=/lib/systemd/system/php${version}-php-fpm.service
+if [ -f /usr/lib/systemd/system/php${version}-php-fpm.service ];then
+ php_fpm_service_file=/lib/systemd/system/php${version}-php-fpm.service
+fi
+
+php_status=`systemctl status php${version}-php-fpm | grep inactive`
+if [ "$php_status" != "" ];then
+ systemctl ${action} php${version}-php-fpm
+fi
+
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+version=$1
+
+if [ "$version" == '5.6' ];then
+ echo '20131226'
+elif [[ "$version" == '7.0' ]]; then
+ echo '20151012'
+elif [[ "$version" == '7.1' ]]; then
+ echo '20160303'
+elif [[ "$version" == '7.2' ]]; then
+ echo '20170718'
+elif [[ "$version" == '7.3' ]]; then
+ echo '20180731'
+elif [[ "$version" == '7.4' ]]; then
+ echo '20190902'
+elif [[ "$version" == '8.0' ]]; then
+ echo '20200930'
+elif [[ "$version" == '8.1' ]]; then
+ echo '20210902'
+elif [[ "$version" == '8.2' ]]; then
+ echo '20220829'
+elif [[ "$version" == '8.3' ]]; then
+ echo '20230831'
+elif [[ "$version" == '8.4' ]]; then
+ echo '20240924'
+elif [[ "$version" == '8.5' ]]; then
+ echo '20250925'
+fi
\ No newline at end of file
diff --git a/plugins/php-yum/versions/phplib.conf b/plugins/php-yum/versions/phplib.conf
new file mode 100755
index 000000000..eec09e6cf
--- /dev/null
+++ b/plugins/php-yum/versions/phplib.conf
@@ -0,0 +1,802 @@
+[
+ {
+ "name": "sg11",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83"
+ ],
+ "type": "脚本解密",
+ "msg": "用于解密SG11加密脚本!",
+ "shell": "sg11.sh",
+ "check": "sg11.so"
+ },
+ {
+ "name": "ionCube",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "81",
+ "82",
+ "83"
+ ],
+ "type": "脚本解密",
+ "msg": "用于解密ionCube Encoder加密脚本!",
+ "shell": "ioncube.sh",
+ "check": "ioncube"
+ },
+ {
+ "name": "pdo",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "数据库",
+ "msg": "数据库访问抽象模块!",
+ "shell": "pdo.sh",
+ "check": "pdo"
+ },
+ {
+ "name": "mysqlnd",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "数据库",
+ "msg": "用于使用MySQL数据库的模块!",
+ "shell": "mysqlnd.sh",
+ "check": "mysqlnd"
+ },
+ {
+ "name": "mysql",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "数据库",
+ "msg": "用于使用MySQL数据库的模块!",
+ "shell": "mysql.sh",
+ "check": "mysql"
+ },
+ {
+ "name": "sqlite3",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "数据库",
+ "msg": "用于使用sqlite3数据库的模块!",
+ "shell": "sqlite3.sh",
+ "check": "sqlite3"
+ },
+ {
+ "name": "oci8",
+ "versions": [],
+ "type": "数据库",
+ "msg": "用于使用OCI8数据库的模块!",
+ "shell": "oci8.sh",
+ "check": "oci8"
+ },
+ {
+ "name": "odbc",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "数据库",
+ "msg": "用于使用ODBC数据库的模块!",
+ "shell": "odbc.sh",
+ "check": "odbc"
+ },
+ {
+ "name": "ZendGuardLoader",
+ "versions": [
+ ],
+ "type": "脚本解密",
+ "msg": "用于解密ZendGuard加密脚本!",
+ "shell": "zend_guard_loader.sh",
+ "check": "ZendGuardLoader"
+ },
+ {
+ "name": "ZendOptimizer",
+ "versions": [
+ "52"
+ ],
+ "type": "脚本解密",
+ "msg": "用于解密ZendOptimizer加密脚本!",
+ "shell": "zend_optimizer.sh",
+ "check": "ZendOptimizer"
+ },
+ {
+ "name": "opcache",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "缓存器",
+ "msg": "用于加速PHP脚本!",
+ "shell": "opcache.sh",
+ "check": "opcache"
+ },
+ {
+ "name": "mcrypt",
+ "versions": [
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81"
+ ],
+ "type": "通用扩展",
+ "msg": "加密软件!",
+ "shell": "mcrypt.sh",
+ "check": "mcrypt"
+ },
+ {
+ "name": "ldap",
+ "versions": [
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "轻型目录访问协议",
+ "shell": "ldap.sh",
+ "check": "ldap"
+ },
+ {
+ "name": "gmp",
+ "versions": [
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "数学扩展",
+ "shell": "gmp.sh",
+ "check": "gmp"
+ },
+ {
+ "name": "brotli",
+ "versions": [
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "压缩",
+ "msg": "压缩算法",
+ "shell": "brotli.sh",
+ "check": "brotli.so"
+ },
+ {
+ "name": "bcmath",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "高精度计算!",
+ "shell": "bcmath.sh",
+ "check": "bcmath.so"
+ },
+ {
+ "name": "fileinfo",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "用于FILE!",
+ "shell": "fileinfo.sh",
+ "check": "fileinfo"
+ },
+ {
+ "name": "exif",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "用于图像文件格式!",
+ "shell": "exif.sh",
+ "check": "exif"
+ },
+ {
+ "name": "igbinary",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "序列化扩展!",
+ "shell": "igbinary.sh",
+ "check": "igbinary.so"
+ },
+ {
+ "name": "xml",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "用于xml解析!",
+ "shell": "xml.sh",
+ "check": "xml"
+ },
+ {
+ "name": "curl",
+ "versions": [
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "通用CURL库!",
+ "shell": "curl.sh",
+ "check": "curl"
+ },
+ {
+ "name": "gd",
+ "versions": [
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "通用GD库!",
+ "shell": "gd.sh",
+ "check": "gd"
+ },
+ {
+ "name": "intl",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "提供国际化支持",
+ "shell": "intl.sh",
+ "check": "intl"
+ },
+ {
+ "name": "memcache",
+ "versions": [
+ "74",
+ "80",
+ "81"
+ ],
+ "type": "缓存器",
+ "msg": "强大的内容缓存器,不支持集群",
+ "shell": "memcache.sh",
+ "check": "memcache"
+ },
+ {
+ "name": "memcached",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "缓存器",
+ "msg": "强大的内容缓存器,支持集群",
+ "shell": "memcached.sh",
+ "check": "memcached"
+ },
+ {
+ "name": "redis",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "缓存器",
+ "msg": "更强大的内容缓存器,支持集群",
+ "shell": "redis.sh",
+ "check": "redis"
+ },
+ {
+ "name": "apcu",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "缓存器",
+ "msg": "脚本缓存器",
+ "shell": "apcu.sh",
+ "check": "apcu"
+ },
+ {
+ "name": "imagick",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "比GD更强大的图形库",
+ "shell": "imagick.sh",
+ "check": "imagick"
+ },
+ {
+ "name": "xdebug",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "调试器",
+ "msg": "不多说,不了解的不要安装",
+ "shell": "xdebug.sh",
+ "check": "xdebug"
+ },
+ {
+ "name": "xhprof",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "性能分析",
+ "msg": "不多说,不了解的不要安装!",
+ "shell": "xhprof.sh",
+ "check": "xhprof"
+ },
+ {
+ "name": "Swoole",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "异步、并行、高性能网络通信引擎",
+ "shell": "swoole.sh",
+ "check": "swoole"
+ },
+ {
+ "name": "eAccelerator",
+ "versions": [
+ "52",
+ "53"
+ ],
+ "type": "缓存器",
+ "msg": "内容缓存器",
+ "shell": "eaccelerator.sh",
+ "check": "eaccelerator"
+ },
+ {
+ "name": "yaf",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83"
+ ],
+ "type": "框架",
+ "msg": "Yaf是一个C语言编写的PHP框架",
+ "shell": "yaf.sh",
+ "check": "yaf"
+ },
+ {
+ "name": "phalcon",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "框架",
+ "msg": "PHP框架",
+ "shell": "phalcon.sh",
+ "check": "phalcon"
+ },
+ {
+ "name": "yar",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74"
+ ],
+ "type": "框架",
+ "msg": "Yar是一个RPC框架",
+ "shell": "yar.sh",
+ "check": "yar"
+ },
+ {
+ "name": "mongo",
+ "versions": [
+ "56"
+ ],
+ "type": "通用扩展",
+ "msg": "Mongodb数据库连接驱动",
+ "shell": "mongo.sh",
+ "check": "mongo"
+ },
+ {
+ "name": "mongodb",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "Mongodb数据库连接驱动",
+ "shell": "mongodb.sh",
+ "check": "mongodb"
+ },
+ {
+ "name": "yac",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81"
+ ],
+ "type": "缓存器",
+ "msg": "高性能无锁共享内存Cache",
+ "shell": "yac.sh",
+ "check": "yac"
+ },
+ {
+ "name": "solr",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81"
+ ],
+ "type": "大数据",
+ "msg": "SOLR全文搜索服务",
+ "shell": "solr.sh",
+ "check": "solr"
+ },
+ {
+ "name": "seaslog",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "日志",
+ "msg": "SeasLog高性能日志记录",
+ "shell": "seaslog.sh",
+ "check": "seaslog"
+ },
+ {
+ "name": "mbstring",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "用于需要多字节字符串处理的模块",
+ "shell": "mbstring.sh",
+ "check": "mbstring"
+ },
+ {
+ "name": "zip",
+ "versions": [
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "压缩",
+ "msg": "压缩组件",
+ "shell": "zip.sh",
+ "check": "zip"
+ },
+ {
+ "name": "zstd",
+ "versions": [
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "压缩",
+ "msg": "压缩组件",
+ "shell": "zstd.sh",
+ "check": "zstd"
+ }
+]
\ No newline at end of file
diff --git a/plugins/php/conf/app_start.php b/plugins/php/conf/app_start.php
new file mode 100644
index 000000000..c03b949d9
--- /dev/null
+++ b/plugins/php/conf/app_start.php
@@ -0,0 +1,56 @@
+save_run($xhprof_data, 'xhprof_foo');
+
+ $profiler_url = sprintf('http://{$LOCAL_IP}:5858/index.php?run=%s&source=xhprof_foo', $run_id);
+ // echo "";
+
+ $style_css = 'position:fixed;bottom: 0px;right: 0px;z-index: 100000000;color: red;background-color: black;padding: 0px;margin: 0px;';
+ echo '
XHProf分析结果 ';
+
+}
+
+if (extension_loaded('xhprof')
+ && isset($_GET[XHProf_Name]) && $_GET[XHProf_Name] == 'ok' &&
+ (! in_array($_SERVER['SCRIPT_NAME'], array('/xhprof_html/callgraph.php','/xhprof_html/index.php')))) {
+ app_xhprof_start();
+ register_shutdown_function('app_xhprof_end');
+ include_once $_SERVER['SCRIPT_FILENAME'];
+ exit;
+}
+
+?>
diff --git a/plugins/php/conf/backup.conf b/plugins/php/conf/backup.conf
new file mode 100644
index 000000000..eaa1fd87c
--- /dev/null
+++ b/plugins/php/conf/backup.conf
@@ -0,0 +1,19 @@
+[backup]
+user = {$PHP_USER}
+group = {$PHP_GROUP}
+
+listen = /tmp/php-cgi-{$PHP_VERSION}.backup.sock
+listen.owner = {$PHP_USER}
+listen.group = {$PHP_GROUP}
+listen.backlog = 4096
+
+pm = dynamic
+pm.max_children = 30
+pm.start_servers = 5
+pm.min_spare_servers = 5
+pm.max_spare_servers = 20
+pm.status_path = /phpfpm_status_{$PHP_VERSION}_backup
+pm.max_requests = 1000
+request_terminate_timeout = 30
+request_slowlog_timeout = 10
+slowlog = {$SERVER_PATH}/php/{$PHP_VERSION}/var/log/www-slow.log
\ No newline at end of file
diff --git a/plugins/php/conf/enable-php-upstream.conf b/plugins/php/conf/enable-php-upstream.conf
new file mode 100644
index 000000000..80d75206c
--- /dev/null
+++ b/plugins/php/conf/enable-php-upstream.conf
@@ -0,0 +1,4 @@
+upstream MW-UPSTREAM-PHP{$PHP_VERSION} {
+ server unix:/tmp/php-cgi-{$PHP_VERSION}.sock;
+ server unix:/tmp/php-cgi-{$PHP_VERSION}.backup.sock;
+}
\ No newline at end of file
diff --git a/plugins/php/conf/enable-php.bak.conf b/plugins/php/conf/enable-php.bak.conf
new file mode 100644
index 000000000..2046ac2cb
--- /dev/null
+++ b/plugins/php/conf/enable-php.bak.conf
@@ -0,0 +1,9 @@
+set $PHP_ENV 1;
+location ~ [^/]\.php(/|$)
+{
+ try_files $uri =404;
+ fastcgi_pass unix:/tmp/php-cgi-{$PHP_VERSION}.sock;
+ fastcgi_index index.php;
+ include fastcgi.conf;
+ include {$SERVER_PATH}/web_conf/php/pathinfo.conf;
+}
\ No newline at end of file
diff --git a/plugins/php/conf/enable-php.conf b/plugins/php/conf/enable-php.conf
new file mode 100644
index 000000000..230b25418
--- /dev/null
+++ b/plugins/php/conf/enable-php.conf
@@ -0,0 +1,10 @@
+set $PHP_ENV 1;
+location ~ [^/]\.php(/|$)
+{
+ try_files $uri =404;
+ #fastcgi_pass unix:/tmp/php-cgi-{$PHP_VERSION}.sock;
+ fastcgi_pass MW-UPSTREAM-PHP{$PHP_VERSION};
+ fastcgi_index index.php;
+ include fastcgi.conf;
+ include {$SERVER_PATH}/web_conf/php/pathinfo.conf;
+}
\ No newline at end of file
diff --git a/plugins/php/conf/pathinfo.conf b/plugins/php/conf/pathinfo.conf
new file mode 100644
index 000000000..fc5ad5219
--- /dev/null
+++ b/plugins/php/conf/pathinfo.conf
@@ -0,0 +1,11 @@
+set $real_script_name $fastcgi_script_name;
+if ($fastcgi_script_name ~ "^(.+?\.php)(/.+)$") {
+ set $real_script_name $1;
+ set $path_info $2;
+ }
+fastcgi_param SCRIPT_FILENAME $document_root$real_script_name;
+fastcgi_param SCRIPT_NAME $real_script_name;
+fastcgi_param PATH_INFO $path_info;
+
+#修复http3不传HOST问题
+fastcgi_param HTTP_HOST $host;
\ No newline at end of file
diff --git a/plugins/php/conf/php-fpm-52.conf b/plugins/php/conf/php-fpm-52.conf
new file mode 100644
index 000000000..444c2c816
--- /dev/null
+++ b/plugins/php/conf/php-fpm-52.conf
@@ -0,0 +1,156 @@
+
+
+
+ All relative paths in this config are relative to php's install prefix
+
+
+
+ Pid file
+ /www/server/php/52/var/run/php-fpm.pid
+
+ Error log file
+ /www/server/php/52/var/log/php-fpm.log
+
+ Log level
+ notice
+
+ When this amount of php processes exited with SIGSEGV or SIGBUS ...
+ 10
+
+ ... in a less than this interval of time, a graceful restart will be initiated.
+ Useful to work around accidental curruptions in accelerator's shared memory.
+ 1m
+
+ Time limit on waiting child's reaction on signals from master
+ 5s
+
+ Set to 'no' to debug fpm
+ yes
+
+
+
+
+
+
+
+ Name of pool. Used in logs and stats.
+ default
+
+ Address to accept fastcgi requests on.
+ Valid syntax is 'ip.ad.re.ss:port' or just 'port' or '/path/to/unix/socket'
+ /tmp/php-cgi-52.sock
+
+
+
+ Set listen(2) backlog
+ -1
+
+ Set permissions for unix socket, if one used.
+ In Linux read/write permissions must be set in order to allow connections from web server.
+ Many BSD-derrived systems allow connections regardless of permissions.
+ www
+ www
+ 0666
+
+
+ Additional php.ini defines, specific to this pool of workers.
+
+
+
+
+
+ Unix user of processes
+ www
+
+ Unix group of processes
+ www
+
+ Process manager settings
+
+
+ Sets style of controling worker process count.
+ Valid values are 'static' and 'apache-like'
+ static
+
+ Sets the limit on the number of simultaneous requests that will be served.
+ Equivalent to Apache MaxClients directive.
+ Equivalent to PHP_FCGI_CHILDREN environment in original php.fcgi
+ Used with any pm_style.
+ 5
+
+ Settings group for 'apache-like' pm style
+
+
+ Sets the number of server processes created on startup.
+ Used only when 'apache-like' pm_style is selected
+ 20
+
+ Sets the desired minimum number of idle server processes.
+ Used only when 'apache-like' pm_style is selected
+ 5
+
+ Sets the desired maximum number of idle server processes.
+ Used only when 'apache-like' pm_style is selected
+ 35
+
+
+
+
+
+ The timeout (in seconds) for serving a single request after which the worker process will be terminated
+ Should be used when 'max_execution_time' ini option does not stop script execution for some reason
+ '0s' means 'off'
+ 0s
+
+ The timeout (in seconds) for serving of single request after which a php backtrace will be dumped to slow.log file
+ '0s' means 'off'
+ 0s
+
+ The log file for slow requests
+ var/log/www-slow.log
+
+ Set open file desc rlimit
+ 1024
+
+ Set max core size rlimit
+ 0
+
+ Chroot to this directory at the start, absolute path
+
+
+ Chdir to this directory at the start, absolute path
+
+
+ Redirect workers' stdout and stderr into main error log.
+ If not set, they will be redirected to /dev/null, according to FastCGI specs
+ yes
+
+ How much requests each process should execute before respawn.
+ Useful to work around memory leaks in 3rd party libraries.
+ For endless request processing please specify 0
+ Equivalent to PHP_FCGI_MAX_REQUESTS
+ 500
+
+ Comma separated list of ipv4 addresses of FastCGI clients that allowed to connect.
+ Equivalent to FCGI_WEB_SERVER_ADDRS environment in original php.fcgi (5.2.2+)
+ Makes sense only with AF_INET listening socket.
+ 127.0.0.1
+
+ Pass environment variables like LD_LIBRARY_PATH
+ All $VARIABLEs are taken from current environment
+
+ $HOSTNAME
+ /usr/local/bin:/usr/bin:/bin
+ /tmp
+ /tmp
+ /tmp
+ $OSTYPE
+ $MACHTYPE
+ 2
+
+
+
+
+
+
+
diff --git a/plugins/php/conf/php-fpm.conf b/plugins/php/conf/php-fpm.conf
new file mode 100644
index 000000000..004ea8e87
--- /dev/null
+++ b/plugins/php/conf/php-fpm.conf
@@ -0,0 +1,6 @@
+
+[global]
+pid = run/php-fpm.pid
+error_log = log/php-fpm.log
+include={$SERVER_PATH}/php/{$PHP_VERSION}/etc/php-fpm.d/*.conf
+php_value[auto_prepend_file]={$SERVER_PATH}/php/app_start.php
\ No newline at end of file
diff --git a/plugins/php/conf/php5.ini b/plugins/php/conf/php5.ini
new file mode 100644
index 000000000..4a0fa3bd1
--- /dev/null
+++ b/plugins/php/conf/php5.ini
@@ -0,0 +1,204 @@
+[PHP]
+engine = On
+short_open_tag = On
+asp_tags = Off
+precision = 14
+output_buffering = 4096
+zlib.output_compression = Off
+implicit_flush = Off
+unserialize_callback_func =
+serialize_precision = 17
+zend.enable_gc = On
+expose_php = Off
+max_execution_time = 30
+max_input_time = 60
+max_input_vars = 1000
+memory_limit = 128M
+error_reporting = E_ALL & ~E_NOTICE
+display_errors = On
+display_startup_errors = On
+log_errors = On
+log_errors_max_len = 1024
+ignore_repeated_errors = Off
+ignore_repeated_source = Off
+report_memleaks = On
+track_errors = On
+html_errors = On
+variables_order = "GPCS"
+request_order = "GP"
+register_argc_argv = Off
+auto_globals_jit = On
+post_max_size = 20M
+auto_prepend_file =
+auto_append_file =
+default_mimetype = "text/html"
+doc_root =
+user_dir =
+enable_dl = Off
+always_populate_raw_post_data=-1
+
+
+file_uploads = On
+upload_tmp_dir = "{$SERVER_PATH}/php/tmp/upload"
+upload_max_filesize = 2M
+max_file_uploads = 20
+
+allow_url_fopen = On
+allow_url_include = Off
+default_socket_timeout = 60
+
+disable_functions = exec,passthru,shell_exec,system,popen,show_source,fastcgi_finish_request
+
+[CLI Server]
+cli_server.color = On
+
+[Date]
+date.timezone = PRC
+
+[filter]
+[iconv]
+iconv.input_encoding = ISO-8859-1
+iconv.internal_encoding = ISO-8859-1
+iconv.output_encoding = ISO-8859-1
+
+
+[Pdo_mysql]
+pdo_mysql.cache_size = 2000
+pdo_mysql.default_socket=/www/server/mysql/mysql.sock
+#pdo_mysql.default_socket=/www/server/mariadb/mysql.sock
+#pdo_mysql.default_socket=/www/server/mysql-community/mysql.sock
+
+[Phar]
+
+[mail function]
+SMTP = localhost
+smtp_port = 25
+sendmail_path = /usr/sbin/sendmail -t -i
+mail.add_x_header = On
+
+
+[SQL]
+sql.safe_mode = Off
+
+[ODBC]
+odbc.allow_persistent = On
+odbc.check_persistent = On
+odbc.max_persistent = -1
+odbc.max_links = -1
+odbc.defaultlrl = 4096
+odbc.defaultbinmode = 1
+
+
+[Interbase]
+ibase.allow_persistent = 1
+ibase.max_persistent = -1
+ibase.max_links = -1
+ibase.timestampformat = "%Y-%m-%d %H:%M:%S"
+ibase.dateformat = "%Y-%m-%d"
+ibase.timeformat = "%H:%M:%S"
+
+[MySQL]
+mysql.allow_local_infile = On
+mysql.allow_persistent = On
+mysql.cache_size = 2000
+mysql.max_persistent = -1
+mysql.max_links = -1
+mysql.default_port =
+mysql.default_host =
+mysql.default_user =
+mysql.default_password =
+mysql.connect_timeout = 60
+mysql.trace_mode = Off
+#mysql.default_socket =
+mysql.default_socket=/www/server/mysql/mysql.sock
+#mysql.default_socket=/www/server/mariadb/mysql.sock
+#mysql.default_socket=/www/server/mysql-community/mysql.sock
+
+[MySQLi]
+mysqli.max_persistent = -1
+mysqli.allow_persistent = On
+mysqli.max_links = -1
+mysqli.cache_size = 2000
+mysqli.default_port = 3306
+mysqli.default_host =
+mysqli.default_user =
+mysqli.default_pw =
+mysqli.reconnect = Off
+#mysql.default_socket =
+mysqli.default_socket=/www/server/mysql/mysql.sock
+#mysqli.default_socket=/www/server/mariadb/mysql.sock
+#mysqli.default_socket=/www/server/mysql-community/mysql.sock
+
+[mysqlnd]
+mysqlnd.collect_statistics = On
+mysqlnd.collect_memory_statistics = On
+
+[OCI8]
+
+
+[PostgreSQL]
+pgsql.allow_persistent = On
+pgsql.auto_reset_persistent = Off
+pgsql.max_persistent = -1
+pgsql.max_links = -1
+pgsql.ignore_notice = 0
+pgsql.log_notice = 0
+
+[Sybase-CT]
+sybct.allow_persistent = On
+sybct.max_persistent = -1
+sybct.max_links = -1
+sybct.min_server_severity = 10
+sybct.min_client_severity = 10
+
+[curl]
+curl.cainfo = {$SSL_CRT}
+
+[browscap]
+
+[Session]
+session.save_handler = files
+session.save_path = "{$SERVER_PATH}/php/tmp/session"
+session.use_strict_mode = 0
+session.use_cookies = 1
+session.use_only_cookies = 1
+session.name = PHPSESSID
+session.auto_start = 0
+session.cookie_lifetime = 0
+session.cookie_path = /
+session.cookie_domain =
+session.cookie_httponly =
+session.serialize_handler = php
+session.gc_probability = 1
+session.gc_divisor = 1000
+session.gc_maxlifetime = 1440
+session.referer_check =
+session.cache_limiter = nocache
+session.cache_expire = 180
+session.use_trans_sid = 0
+session.hash_function = 0
+session.hash_bits_per_character = 5
+url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=fakeentry"
+
+
+[MSSQL]
+mssql.allow_persistent = On
+mssql.max_persistent = -1
+mssql.max_links = -1
+mssql.min_error_severity = 10
+mssql.min_message_severity = 10
+mssql.compatibility_mode = Off
+mssql.secure_connection = Off
+
+[Tidy]
+tidy.clean_output = Off
+
+[soap]
+soap.wsdl_cache_enabled=1
+soap.wsdl_cache_dir="{$SERVER_PATH}/php/tmp"
+soap.wsdl_cache_ttl=86400
+soap.wsdl_cache_limit = 5
+
+
+[ldap]
+ldap.max_links = -1
diff --git a/plugins/php/conf/php7.ini b/plugins/php/conf/php7.ini
new file mode 100644
index 000000000..576936e02
--- /dev/null
+++ b/plugins/php/conf/php7.ini
@@ -0,0 +1,206 @@
+[PHP]
+engine = On
+short_open_tag = On
+asp_tags = Off
+precision = 14
+output_buffering = 4096
+zlib.output_compression = Off
+implicit_flush = Off
+unserialize_callback_func =
+serialize_precision = 17
+zend.enable_gc = On
+expose_php = Off
+max_execution_time = 30
+max_input_time = 60
+max_input_vars = 1000
+memory_limit = 128M
+error_reporting = E_ALL & ~E_NOTICE
+display_errors = On
+display_startup_errors = On
+log_errors = On
+log_errors_max_len = 1024
+ignore_repeated_errors = Off
+ignore_repeated_source = Off
+report_memleaks = On
+html_errors = On
+variables_order = "GPCS"
+request_order = "GP"
+register_argc_argv = Off
+auto_globals_jit = On
+post_max_size = 20M
+auto_prepend_file =
+auto_append_file =
+default_mimetype = "text/html"
+doc_root =
+user_dir =
+enable_dl = Off
+always_populate_raw_post_data=-1
+
+
+file_uploads = On
+upload_tmp_dir = "{$SERVER_PATH}/php/tmp/upload"
+upload_max_filesize = 2M
+max_file_uploads = 20
+
+allow_url_fopen = On
+allow_url_include = Off
+default_socket_timeout = 60
+
+disable_functions = exec,passthru,shell_exec,system,popen,show_source,fastcgi_finish_request
+
+[CLI Server]
+cli_server.color = On
+
+[Date]
+date.timezone = PRC
+
+[filter]
+[iconv]
+iconv.input_encoding = ISO-8859-1
+iconv.internal_encoding = ISO-8859-1
+iconv.output_encoding = ISO-8859-1
+
+
+[Pdo_mysql]
+pdo_mysql.cache_size = 2000
+#mysql.default_socket =
+pdo_mysql.default_socket=/www/server/mysql/mysql.sock
+#pdo_mysql.default_socket=/www/server/mariadb/mysql.sock
+#pdo_mysql.default_socket=/www/server/mysql-community/mysql.sock
+
+
+[mail function]
+SMTP = localhost
+smtp_port = 25
+sendmail_path = /usr/sbin/sendmail -t -i
+mail.add_x_header = On
+
+
+[SQL]
+sql.safe_mode = Off
+
+[ODBC]
+odbc.allow_persistent = On
+odbc.check_persistent = On
+odbc.max_persistent = -1
+odbc.max_links = -1
+odbc.defaultlrl = 4096
+odbc.defaultbinmode = 1
+
+
+[Interbase]
+ibase.allow_persistent = 1
+ibase.max_persistent = -1
+ibase.max_links = -1
+ibase.timestampformat = "%Y-%m-%d %H:%M:%S"
+ibase.dateformat = "%Y-%m-%d"
+ibase.timeformat = "%H:%M:%S"
+
+[MySQL]
+mysql.allow_local_infile = On
+mysql.allow_persistent = On
+mysql.cache_size = 2000
+mysql.max_persistent = -1
+mysql.max_links = -1
+mysql.default_port =
+mysql.default_host =
+mysql.default_user =
+mysql.default_password =
+mysql.connect_timeout = 60
+mysql.trace_mode = Off
+#mysql.default_socket =
+mysql.default_socket=/www/server/mysql/mysql.sock
+#mysql.default_socket=/www/server/mariadb/mysql.sock
+#mysql.default_socket=/www/server/mysql-community/mysql.sock
+
+[MySQLi]
+mysqli.max_persistent = -1
+mysqli.allow_persistent = On
+mysqli.max_links = -1
+mysqli.cache_size = 2000
+mysqli.default_port = 3306
+mysqli.default_host =
+mysqli.default_user =
+mysqli.default_pw =
+mysqli.reconnect = Off
+#mysqli.default_socket =
+mysqli.default_socket=/www/server/mysql/mysql.sock
+#mysqli.default_socket=/www/server/mariadb/mysql.sock
+#mysqli.default_socket=/www/server/mysql-community/mysql.sock
+
+[mysqlnd]
+mysqlnd.collect_statistics = On
+mysqlnd.collect_memory_statistics = On
+
+
+[PostgreSQL]
+pgsql.allow_persistent = On
+pgsql.auto_reset_persistent = Off
+pgsql.max_persistent = -1
+pgsql.max_links = -1
+pgsql.ignore_notice = 0
+pgsql.log_notice = 0
+
+[Sybase-CT]
+sybct.allow_persistent = On
+sybct.max_persistent = -1
+sybct.max_links = -1
+sybct.min_server_severity = 10
+sybct.min_client_severity = 10
+
+[curl]
+curl.cainfo = {$SSL_CRT}
+
+[bcmath]
+bcmath.scale = 0
+
+[browscap]
+
+[Session]
+session.save_handler = files
+session.save_path = "{$SERVER_PATH}/php/tmp/session"
+session.use_strict_mode = 0
+session.use_cookies = 1
+session.use_only_cookies = 1
+session.name = PHPSESSID
+session.auto_start = 0
+session.cookie_lifetime = 0
+session.cookie_path = /
+session.cookie_domain =
+session.cookie_httponly =
+session.serialize_handler = php
+session.gc_probability = 1
+session.gc_divisor = 1000
+session.gc_maxlifetime = 1440
+session.referer_check =
+session.cache_limiter = nocache
+session.cache_expire = 180
+session.use_trans_sid = 0
+session.hash_function = 0
+session.hash_bits_per_character = 5
+url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=fakeentry"
+
+
+[MSSQL]
+mssql.allow_persistent = On
+mssql.max_persistent = -1
+mssql.max_links = -1
+mssql.min_error_severity = 10
+mssql.min_message_severity = 10
+mssql.compatibility_mode = Off
+mssql.secure_connection = Off
+
+
+[Tidy]
+tidy.clean_output = Off
+
+[soap]
+soap.wsdl_cache_enabled=1
+soap.wsdl_cache_dir="{$SERVER_PATH}/php/tmp"
+soap.wsdl_cache_ttl=86400
+soap.wsdl_cache_limit = 5
+
+[sysvshm]
+
+[ldap]
+ldap.max_links = -1
diff --git a/plugins/php/conf/php8.ini b/plugins/php/conf/php8.ini
new file mode 100644
index 000000000..96381c231
--- /dev/null
+++ b/plugins/php/conf/php8.ini
@@ -0,0 +1,208 @@
+[PHP]
+engine = On
+short_open_tag = On
+asp_tags = Off
+precision = 14
+output_buffering = 4096
+zlib.output_compression = Off
+implicit_flush = Off
+unserialize_callback_func =
+serialize_precision = 17
+zend.enable_gc = On
+expose_php = Off
+max_execution_time = 30
+max_input_time = 60
+max_input_vars = 1000
+memory_limit = 128M
+error_reporting = E_ALL & ~E_NOTICE
+display_errors = On
+display_startup_errors = On
+log_errors = On
+log_errors_max_len = 1024
+ignore_repeated_errors = Off
+ignore_repeated_source = Off
+report_memleaks = On
+html_errors = On
+variables_order = "GPCS"
+request_order = "GP"
+register_argc_argv = Off
+auto_globals_jit = On
+post_max_size = 20M
+auto_prepend_file =
+auto_append_file =
+default_mimetype = "text/html"
+doc_root =
+user_dir =
+enable_dl = Off
+always_populate_raw_post_data=-1
+
+
+file_uploads = On
+upload_tmp_dir = "{$SERVER_PATH}/php/tmp/upload"
+upload_max_filesize = 2M
+max_file_uploads = 20
+
+allow_url_fopen = On
+allow_url_include = Off
+default_socket_timeout = 60
+
+disable_functions = exec,passthru,shell_exec,system,popen,show_source,fastcgi_finish_request
+
+[CLI Server]
+cli_server.color = On
+
+[Date]
+date.timezone = PRC
+
+[filter]
+[iconv]
+iconv.input_encoding = ISO-8859-1
+iconv.internal_encoding = ISO-8859-1
+iconv.output_encoding = ISO-8859-1
+
+
+[Pdo_mysql]
+pdo_mysql.cache_size = 2000
+#mysql.default_socket =
+pdo_mysql.default_socket=/www/server/mysql/mysql.sock
+#pdo_mysql.default_socket=/www/server/mariadb/mysql.sock
+#pdo_mysql.default_socket=/www/server/mysql-community/mysql.sock
+
+
+
+[mail function]
+SMTP = localhost
+smtp_port = 25
+sendmail_path = /usr/sbin/sendmail -t -i
+mail.add_x_header = On
+
+
+[SQL]
+sql.safe_mode = Off
+
+[ODBC]
+odbc.allow_persistent = On
+odbc.check_persistent = On
+odbc.max_persistent = -1
+odbc.max_links = -1
+odbc.defaultlrl = 4096
+odbc.defaultbinmode = 1
+
+
+[Interbase]
+ibase.allow_persistent = 1
+ibase.max_persistent = -1
+ibase.max_links = -1
+ibase.timestampformat = "%Y-%m-%d %H:%M:%S"
+ibase.dateformat = "%Y-%m-%d"
+ibase.timeformat = "%H:%M:%S"
+
+[MySQL]
+mysql.allow_local_infile = On
+mysql.allow_persistent = On
+mysql.cache_size = 2000
+mysql.max_persistent = -1
+mysql.max_links = -1
+mysql.default_port =
+mysql.default_host =
+mysql.default_user =
+mysql.default_password =
+mysql.connect_timeout = 60
+mysql.trace_mode = Off
+#mysql.default_socket =
+mysql.default_socket=/www/server/mysql/mysql.sock
+#mysql.default_socket=/www/server/mariadb/mysql.sock
+#mysql.default_socket=/www/server/mysql-community/mysql.sock
+
+
+[MySQLi]
+mysqli.max_persistent = -1
+mysqli.allow_persistent = On
+mysqli.max_links = -1
+mysqli.cache_size = 2000
+mysqli.default_port = 3306
+mysqli.default_host =
+mysqli.default_user =
+mysqli.default_pw =
+mysqli.reconnect = Off
+#mysqli.default_socket =
+mysqli.default_socket=/www/server/mysql/mysql.sock
+#mysqli.default_socket=/www/server/mariadb/mysql.sock
+#mysqli.default_socket=/www/server/mysql-community/mysql.sock
+
+[mysqlnd]
+mysqlnd.collect_statistics = On
+mysqlnd.collect_memory_statistics = On
+
+[OCI8]
+
+
+[PostgreSQL]
+pgsql.allow_persistent = On
+pgsql.auto_reset_persistent = Off
+pgsql.max_persistent = -1
+pgsql.max_links = -1
+pgsql.ignore_notice = 0
+pgsql.log_notice = 0
+
+[Sybase-CT]
+sybct.allow_persistent = On
+sybct.max_persistent = -1
+sybct.max_links = -1
+sybct.min_server_severity = 10
+sybct.min_client_severity = 10
+
+[curl]
+curl.cainfo = {$SSL_CRT}
+
+[bcmath]
+bcmath.scale = 0
+
+[browscap]
+
+[Session]
+session.save_handler = files
+session.save_path = "{$SERVER_PATH}/php/tmp/session"
+session.use_strict_mode = 0
+session.use_cookies = 1
+session.use_only_cookies = 1
+session.name = PHPSESSID
+session.auto_start = 0
+session.cookie_lifetime = 0
+session.cookie_path = /
+session.cookie_domain =
+session.cookie_httponly =
+session.serialize_handler = php
+session.gc_probability = 1
+session.gc_divisor = 1000
+session.gc_maxlifetime = 1440
+session.referer_check =
+session.cache_limiter = nocache
+session.cache_expire = 180
+session.use_trans_sid = 0
+session.hash_function = 0
+session.hash_bits_per_character = 5
+url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=fakeentry"
+
+
+[MSSQL]
+mssql.allow_persistent = On
+mssql.max_persistent = -1
+mssql.max_links = -1
+mssql.min_error_severity = 10
+mssql.min_message_severity = 10
+mssql.compatibility_mode = Off
+mssql.secure_connection = Off
+
+[Tidy]
+tidy.clean_output = Off
+
+[soap]
+soap.wsdl_cache_enabled=1
+soap.wsdl_cache_dir="{$SERVER_PATH}/php/tmp"
+soap.wsdl_cache_ttl=86400
+soap.wsdl_cache_limit = 5
+
+
+[ldap]
+ldap.max_links = -1
diff --git a/plugins/php/conf/phpfpm_status.conf b/plugins/php/conf/phpfpm_status.conf
new file mode 100644
index 000000000..4da6f2816
--- /dev/null
+++ b/plugins/php/conf/phpfpm_status.conf
@@ -0,0 +1,5 @@
+location /phpfpm_status_{$PHP_VERSION} {
+ fastcgi_pass unix:/tmp/php-cgi-{$PHP_VERSION}.sock;
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME \$fastcgi_script_name;
+}
\ No newline at end of file
diff --git a/plugins/php/conf/phpinfo.conf b/plugins/php/conf/phpinfo.conf
new file mode 100644
index 000000000..577a70ec0
--- /dev/null
+++ b/plugins/php/conf/phpinfo.conf
@@ -0,0 +1,4 @@
+location /{$PHP_VERSION} {
+ root {$ROOT_PATH}/phpinfo;
+ include {$SERVER_PATH}/web_conf/php/conf/enable-php-{$PHP_VERSION}.conf;
+}
\ No newline at end of file
diff --git a/plugins/php/conf/www.conf b/plugins/php/conf/www.conf
new file mode 100644
index 000000000..9a7b3fd77
--- /dev/null
+++ b/plugins/php/conf/www.conf
@@ -0,0 +1,19 @@
+[www]
+user = {$PHP_USER}
+group = {$PHP_GROUP}
+
+listen = /tmp/php-cgi-{$PHP_VERSION}.sock
+listen.owner = {$PHP_USER}
+listen.group = {$PHP_GROUP}
+listen.backlog = 4096
+
+pm = dynamic
+pm.max_children = 30
+pm.start_servers = 5
+pm.min_spare_servers = 5
+pm.max_spare_servers = 20
+pm.status_path = /phpfpm_status_{$PHP_VERSION}
+pm.max_requests = 1000
+request_terminate_timeout = 30
+request_slowlog_timeout = 10
+slowlog = {$SERVER_PATH}/php/{$PHP_VERSION}/var/log/www-slow.log
\ No newline at end of file
diff --git a/plugins/php/ico.png b/plugins/php/ico.png
new file mode 100755
index 000000000..59def5702
Binary files /dev/null and b/plugins/php/ico.png differ
diff --git a/plugins/php/index.html b/plugins/php/index.html
new file mode 100755
index 000000000..4c67f0531
--- /dev/null
+++ b/plugins/php/index.html
@@ -0,0 +1,67 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/php/index.py b/plugins/php/index.py
new file mode 100755
index 000000000..c5c354c22
--- /dev/null
+++ b/plugins/php/index.py
@@ -0,0 +1,1128 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import shutil
+
+# reload(sys)
+# sys.setdefaultencoding('utf8')
+
+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 'php'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getInitDFile(version):
+ 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() + version
+
+
+def getArgs():
+ args = sys.argv[3:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ if t.strip() == '':
+ tmp = []
+ else:
+ t = t.split(':', 1)
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ 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 getConf(version):
+ path = getServerDir() + '/' + version + '/etc/php.ini'
+ return path
+
+
+def getFpmConfFile(version, pool='www'):
+ args = getArgs()
+ if 'pool' in args:
+ pool = args['pool']
+ return getServerDir() + '/' + version + '/etc/php-fpm.d/'+pool+'.conf'
+
+def getFpmFile(version):
+ return getServerDir() + '/' + version + '/etc/php-fpm.conf'
+
+
+def status_progress(version):
+ # ps -ef|grep 'php/81' |grep -v grep | grep -v python | awk '{print $2}
+ cmd = "ps aux|grep 'php/" + version + \
+ "' |grep -v grep | grep -v python | awk '{print $2}'"
+ data = mw.execShell(cmd)
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def getPhpSocket(version):
+ path = getFpmConfFile(version)
+ content = mw.readFile(path)
+ rep = r'listen\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+
+def status(version):
+ '''
+ sock文件判断是否启动
+ '''
+ sock = getPhpSocket(version)
+ if sock.find(':'):
+ return status_progress(version)
+
+ if not os.path.exists(sock):
+ return 'stop'
+ return 'start'
+
+
+def contentReplace(content, version):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$PHP_VERSION}', version)
+ content = content.replace('{$LOCAL_IP}', mw.getLocalIp())
+ content = content.replace('{$SSL_CRT}', mw.getSslCrt())
+
+ if mw.isAppleSystem():
+ # user = mw.execShell(
+ # "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
+ content = content.replace('{$PHP_USER}', 'nobody')
+ content = content.replace('{$PHP_GROUP}', 'nobody')
+
+ rep = r'listen.owner\s*=\s*(.+)\r?\n'
+ val = ';listen.owner = nobody\n'
+ content = re.sub(rep, val, content)
+
+ rep = r'listen.group\s*=\s*(.+)\r?\n'
+ val = ';listen.group = nobody\n'
+ content = re.sub(rep, val, content)
+
+ rep = r'user\s*=\s*(.+)\r?\n'
+ val = ';user = nobody\n'
+ content = re.sub(rep, val, content)
+
+ rep = r'[^\.]group\s*=\s*(.+)\r?\n'
+ val = ';group = nobody\n'
+ content = re.sub(rep, val, content)
+
+ else:
+ content = content.replace('{$PHP_USER}', 'www')
+ content = content.replace('{$PHP_GROUP}', 'www')
+ return content
+
+
+def makeOpenrestyConf():
+ phpversions = ['00', '52', '53', '54', '55', '56',
+ '70', '71', '72', '73', '74', '80', '81', '82', '83','84','85']
+
+ sdir = mw.getServerDir()
+
+ dst_dir = sdir + '/web_conf/php'
+ if not os.path.exists(dst_dir):
+ mw.execShell('mkdir -p ' + dst_dir)
+
+ dst_dir_conf = sdir + '/web_conf/php/conf'
+ if not os.path.exists(dst_dir_conf):
+ mw.execShell('mkdir -p ' + dst_dir_conf)
+
+ dst_dir_upstream = sdir + '/web_conf/php/upstream'
+ if not os.path.exists(dst_dir_upstream):
+ mw.execShell('mkdir -p ' + dst_dir_upstream)
+
+ dst_pathinfo = sdir + '/web_conf/php/pathinfo.conf'
+ if not os.path.exists(dst_pathinfo):
+ src_pathinfo = getPluginDir() + '/conf/pathinfo.conf'
+ shutil.copyfile(src_pathinfo, dst_pathinfo)
+
+ info = getPluginDir() + '/info.json'
+ content = mw.readFile(info)
+ content = json.loads(content)
+ versions = content['versions']
+ tpl = getPluginDir() + '/conf/enable-php.conf'
+ tpl_content = mw.readFile(tpl)
+ for x in phpversions:
+ dfile = sdir + '/web_conf/php/conf/enable-php-' + x + '.conf'
+ if not os.path.exists(dfile):
+ if x == '00':
+ mw.writeFile(dfile, '')
+ else:
+ content = contentReplace(tpl_content, x)
+ mw.writeFile(dfile, content)
+
+ upstream_tpl = getPluginDir() + '/conf/enable-php-upstream.conf'
+ upstream_tpl_content = mw.readFile(upstream_tpl)
+ for x in phpversions:
+ dfile = sdir + '/web_conf/php/upstream/enable-php-' + x + '.conf'
+ if not os.path.exists(dfile):
+ if x == '00':
+ mw.writeFile(dfile, '')
+ else:
+ content = contentReplace(upstream_tpl_content, x)
+ mw.writeFile(dfile, content)
+
+ vhost_dir = mw.getServerDir() + '/web_conf/nginx/vhost'
+ write_php_upstream_conf = mw.getServerDir()+'/web_conf/php/upstream/*.conf;'
+ if not os.path.exists(vhost_dir):
+ mw.execShell('mkdir -p ' + vhost_dir)
+
+ vhost_php_upstream = vhost_dir+'/0.php_upstream.conf'
+ if not os.path.exists(vhost_php_upstream):
+ mw.writeFile(vhost_php_upstream,'include '+write_php_upstream_conf)
+
+
+
+def phpPrependFile(version):
+ app_start = getServerDir() + '/app_start.php'
+ if not os.path.exists(app_start):
+ tpl = getPluginDir() + '/conf/app_start.php'
+ content = mw.readFile(tpl)
+ content = contentReplace(content, version)
+ mw.writeFile(app_start, content)
+
+
+def phpFpmReplace(version):
+ desc_php_fpm = getServerDir() + '/' + version + '/etc/php-fpm.conf'
+ if not os.path.exists(desc_php_fpm):
+ tpl_php_fpm = getPluginDir() + '/conf/php-fpm.conf'
+ content = mw.readFile(tpl_php_fpm)
+ content = contentReplace(content, version)
+ mw.writeFile(desc_php_fpm, content)
+ else:
+ if version == '52':
+ tpl_php_fpm = tpl_php_fpm = getPluginDir() + '/conf/php-fpm-52.conf'
+ content = mw.readFile(tpl_php_fpm)
+ mw.writeFile(desc_php_fpm, content)
+
+
+
+def phpFpmPoolReplace(version, pool = 'www'):
+ service_php_fpm_dir = getServerDir() + '/' + version + '/etc/php-fpm.d/'
+
+ if not os.path.exists(service_php_fpm_dir):
+ os.mkdir(service_php_fpm_dir)
+
+ service_php_fpmwww = service_php_fpm_dir + '/'+pool+'.conf'
+ if not os.path.exists(service_php_fpmwww):
+ tpl_php_fpmwww = getPluginDir() + '/conf/'+pool+'.conf'
+ content = mw.readFile(tpl_php_fpmwww)
+ content = contentReplace(content, version)
+ mw.writeFile(service_php_fpmwww, content)
+
+
+def makePhpIni(version):
+ dst_ini = getConf(version)
+ if not os.path.exists(dst_ini):
+ src_ini = getPluginDir() + '/conf/php' + version[0:1] + '.ini'
+ # shutil.copyfile(s_ini, d_ini)
+ content = mw.readFile(src_ini)
+ if version == '52':
+ content = content + "auto_prepend_file=/www/server/php/app_start.php"
+
+ content = contentReplace(content, version)
+ mw.writeFile(dst_ini, content)
+
+
+def initReplace(version):
+ makeOpenrestyConf()
+ makePhpIni(version)
+
+ initD_path = getServerDir() + '/init.d'
+ if not os.path.exists(initD_path):
+ os.mkdir(initD_path)
+
+ file_bin = initD_path + '/php' + version
+ if not os.path.exists(file_bin):
+ file_tpl = getPluginDir() + '/init.d/php.tpl'
+
+ if version == '52':
+ file_tpl = getPluginDir() + '/init.d/php52.tpl'
+
+ content = mw.readFile(file_tpl)
+ content = contentReplace(content, version)
+
+ mw.writeFile(file_bin, content)
+ mw.execShell('chmod +x ' + file_bin)
+
+ phpPrependFile(version)
+ phpFpmPoolReplace(version, 'www')
+ phpFpmPoolReplace(version, 'backup')
+ phpFpmReplace(version)
+
+ session_path = getServerDir() + '/tmp/session'
+ if not os.path.exists(session_path):
+ mw.execShell('mkdir -p ' + session_path)
+ mw.execShell('chown -R www:www ' + session_path)
+
+ upload_path = getServerDir() + '/tmp/upload'
+ if not os.path.exists(upload_path):
+ mw.execShell('mkdir -p ' + upload_path)
+ mw.execShell('chown -R www:www ' + upload_path)
+
+ # systemd
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/php' + version + '.service'
+
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ systemServiceTpl = getPluginDir() + '/init.d/php.service.tpl'
+ if version == '52':
+ systemServiceTpl = getPluginDir() + '/init.d/php.service.52.tpl'
+ service_path = mw.getServerDir()
+ se_content = mw.readFile(systemServiceTpl)
+ se_content = se_content.replace('{$VERSION}', version)
+ se_content = se_content.replace('{$SERVER_PATH}', service_path)
+ mw.writeFile(systemService, se_content)
+ mw.execShell('systemctl daemon-reload')
+
+ return file_bin
+
+
+def phpOp(version, method):
+ file = initReplace(version)
+
+ 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"):
+ data = mw.execShell('service php' + version + ' ' + method)
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+ if method == 'stop' or method == 'restart':
+ mw.execShell(file + ' ' + 'stop')
+
+ data = mw.execShell('systemctl ' + method + ' php' + version)
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def start(version):
+ cmd = 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/www/server/lib/icu/lib:/usr/lib/x86_64-linux-gnu/:/opt/homebrew/lib'
+ mw.execShell(cmd)
+ return phpOp(version, 'start')
+
+
+def stop(version):
+ status = phpOp(version, 'stop')
+
+ if version == '52':
+ file = initReplace(version)
+ data = mw.execShell(file + ' ' + 'stop')
+ if data[1] == '':
+ return 'ok'
+ return status
+
+
+def restart(version):
+ return phpOp(version, 'restart')
+
+
+def reload(version):
+ if version == '52':
+ return phpOp(version, 'restart')
+ return phpOp(version, 'reload')
+
+
+def initdStatus(version):
+ current_os = mw.getOs()
+ if current_os == 'darwin':
+ return "Apple Computer does not support"
+
+ if current_os.startswith('freebsd'):
+ initd_bin = getInitDFile(version)
+ if os.path.exists(initd_bin):
+ return 'ok'
+
+ shell_cmd = 'systemctl status php' + version + ' | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+
+def initdInstall(version):
+ current_os = mw.getOs()
+ if current_os == 'darwin':
+ return "Apple Computer does not support"
+
+ if current_os.startswith('freebsd'):
+ import shutil
+ source_bin = initReplace(version)
+ initd_bin = getInitDFile(version)
+ shutil.copyfile(source_bin, initd_bin)
+ mw.execShell('chmod +x ' + initd_bin)
+ return 'ok'
+
+ mw.execShell('systemctl enable php' + version)
+ return 'ok'
+
+
+def initdUinstall(version):
+ current_os = mw.getOs()
+ if current_os == 'darwin':
+ return "Apple Computer does not support"
+
+ if current_os.startswith('freebsd'):
+ initd_bin = getInitDFile(version)
+ os.remove(initd_bin)
+ return 'ok'
+
+ mw.execShell('systemctl disable php' + version)
+ return 'ok'
+
+
+def fpmLog(version):
+ return getServerDir() + '/' + version + '/var/log/php-fpm.log'
+
+
+def fpmSlowLog(version):
+ return getServerDir() + '/' + version + '/var/log/www-slow.log'
+
+
+def getPhpConf(version):
+ gets = [
+ {'name': 'short_open_tag', 'type': 1, 'ps': '短标签支持'},
+ {'name': 'asp_tags', 'type': 1, 'ps': 'ASP标签支持'},
+ {'name': 'max_execution_time', 'type': 2, 'ps': '最大脚本运行时间'},
+ {'name': 'max_input_time', 'type': 2, 'ps': '最大输入时间'},
+ {'name': 'max_input_vars', 'type': 2, 'ps': '最大输入数量'},
+ {'name': 'memory_limit', 'type': 2, 'ps': '脚本内存限制'},
+ {'name': 'post_max_size', 'type': 2, 'ps': 'POST数据最大尺寸'},
+ {'name': 'file_uploads', 'type': 1, 'ps': '是否允许上传文件'},
+ {'name': 'upload_max_filesize', 'type': 2, 'ps': '允许上传文件的最大尺寸'},
+ {'name': 'max_file_uploads', 'type': 2, 'ps': '允许同时上传文件的最大数量'},
+ {'name': 'default_socket_timeout', 'type': 2, 'ps': 'Socket超时时间'},
+ {'name': 'error_reporting', 'type': 3, 'ps': '错误级别'},
+ {'name': 'display_errors', 'type': 1, 'ps': '是否输出详细错误信息'},
+ {'name': 'cgi.fix_pathinfo', 'type': 0, 'ps': '是否开启pathinfo'},
+ {'name': 'date.timezone', 'type': 3, 'ps': '时区'}
+ ]
+ phpini = mw.readFile(getConf(version))
+ result = []
+ for g in gets:
+ rep = g['name'] + r'\s*=\s*([0-9A-Za-z_& ~]+)(\s*;?|\r?\n)'
+ tmp = re.search(rep, phpini)
+ if not tmp:
+ continue
+ g['value'] = tmp.groups()[0]
+ result.append(g)
+ return mw.getJson(result)
+
+
+def submitPhpConf(version):
+ gets = ['display_errors', 'cgi.fix_pathinfo', 'date.timezone', 'short_open_tag',
+ 'asp_tags', 'max_execution_time', 'max_input_time', 'max_input_vars', 'memory_limit',
+ 'post_max_size', 'file_uploads', 'upload_max_filesize', 'max_file_uploads',
+ 'default_socket_timeout', 'error_reporting']
+ args = getArgs()
+ filename = getServerDir() + '/' + version + '/etc/php.ini'
+ phpini = mw.readFile(filename)
+ for g in gets:
+ if g in args:
+ rep = g + r'\s*=\s*(.+)\r?\n'
+ val = g + ' = ' + args[g] + '\n'
+ phpini = re.sub(rep, val, phpini)
+ mw.writeFile(filename, phpini)
+ # mw.execShell(getServerDir() + '/init.d/php' + version + ' reload')
+ reload(version)
+ return mw.returnJson(True, '设置成功')
+
+
+def getLimitConf(version):
+ fileini = getConf(version)
+ phpini = mw.readFile(fileini)
+ filefpm = getFpmConfFile(version)
+ phpfpm = mw.readFile(filefpm)
+
+ # print fileini, filefpm
+ data = {}
+ try:
+ rep = r"upload_max_filesize\s*=\s*([0-9]+)M"
+ tmp = re.search(rep, phpini).groups()
+ data['max'] = tmp[0]
+ except:
+ data['max'] = '50'
+
+ try:
+ rep = r"request_terminate_timeout\s*=\s*([0-9]+)\n"
+ tmp = re.search(rep, phpfpm).groups()
+ data['maxTime'] = tmp[0]
+ except:
+ data['maxTime'] = 0
+
+ try:
+ rep = r"\n;*\s*cgi\.fix_pathinfo\s*=\s*([0-9]+)\s*\n"
+ tmp = re.search(rep, phpini).groups()
+
+ if tmp[0] == '1':
+ data['pathinfo'] = True
+ else:
+ data['pathinfo'] = False
+ except:
+ data['pathinfo'] = False
+
+ return mw.getJson(data)
+
+
+def setMaxTime(version):
+ args = getArgs()
+ data = checkArgs(args, ['time'])
+ if not data[0]:
+ return data[1]
+
+ time = args['time']
+ if int(time) < 30 or int(time) > 86400:
+ return mw.returnJson(False, '请填写30-86400间的值!')
+
+ filefpm = getFpmConfFile(version)
+ conf = mw.readFile(filefpm)
+ rep = r"request_terminate_timeout\s*=\s*([0-9]+)\n"
+ conf = re.sub(rep, "request_terminate_timeout = " + time + "\n", conf)
+ mw.writeFile(filefpm, conf)
+
+ fileini = getServerDir() + "/" + version + "/etc/php.ini"
+ phpini = mw.readFile(fileini)
+ rep = r"max_execution_time\s*=\s*([0-9]+)\r?\n"
+ phpini = re.sub(rep, "max_execution_time = " + time + "\n", phpini)
+ rep = r"max_input_time\s*=\s*([0-9]+)\r?\n"
+ phpini = re.sub(rep, "max_input_time = " + time + "\n", phpini)
+ mw.writeFile(fileini, phpini)
+ return mw.returnJson(True, '设置成功!')
+
+
+def setMaxSize(version):
+ args = getArgs()
+ data = checkArgs(args, ['max'])
+ if not data[0]:
+ return data[1]
+
+ maxVal = args['max']
+ if int(maxVal) < 2:
+ return mw.returnJson(False, '上传大小限制不能小于2MB!')
+
+ path = getConf(version)
+ conf = mw.readFile(path)
+ rep = r"\nupload_max_filesize\s*=\s*[0-9]+M"
+ conf = re.sub(rep, u'\nupload_max_filesize = ' + maxVal + 'M', conf)
+ rep = r"\npost_max_size\s*=\s*[0-9]+M"
+ conf = re.sub(rep, u'\npost_max_size = ' + maxVal + 'M', conf)
+ mw.writeFile(path, conf)
+
+ msg = mw.getInfo('设置PHP-{1}最大上传大小为[{2}MB]!', (version, maxVal,))
+ mw.writeLog('插件管理[PHP]', msg)
+ return mw.returnJson(True, '设置成功!')
+
+
+def getFpmConfig(version, pool = 'www'):
+ args = getArgs()
+ pool = 'www'
+ if 'pool' in args:
+ pool = args['pool']
+
+ filefpm = getServerDir() + '/' + version + '/etc/php-fpm.d/'+pool+'.conf'
+ conf = mw.readFile(filefpm)
+ data = {}
+ rep = r"\s*pm.max_children\s*=\s*([0-9]+)\s*"
+ tmp = re.search(rep, conf).groups()
+ data['max_children'] = tmp[0]
+
+ rep = r"\s*pm.start_servers\s*=\s*([0-9]+)\s*"
+ tmp = re.search(rep, conf).groups()
+ data['start_servers'] = tmp[0]
+
+ rep = r"\s*pm.min_spare_servers\s*=\s*([0-9]+)\s*"
+ tmp = re.search(rep, conf).groups()
+ data['min_spare_servers'] = tmp[0]
+
+ rep = r"\s*pm.max_spare_servers \s*=\s*([0-9]+)\s*"
+ tmp = re.search(rep, conf).groups()
+ data['max_spare_servers'] = tmp[0]
+
+ rep = r"\s*pm\s*=\s*(\w+)\s*"
+ tmp = re.search(rep, conf).groups()
+ data['pm'] = tmp[0]
+ return mw.getJson(data)
+
+
+def setFpmConfig(version):
+ args = getArgs()
+ # if not 'max' in args:
+ # return 'missing time args!'
+
+ version = args['version']
+ max_children = args['max_children']
+ start_servers = args['start_servers']
+ min_spare_servers = args['min_spare_servers']
+ max_spare_servers = args['max_spare_servers']
+ pm = args['pm']
+ pool = args['pool']
+
+
+ file = getServerDir() + '/' + version + '/etc/php-fpm.d/'+pool+'.conf'
+ conf = mw.readFile(file)
+
+ rep = r"\s*pm.max_children\s*=\s*([0-9]+)\s*"
+ conf = re.sub(rep, "\npm.max_children = " + max_children, conf)
+
+ rep = r"\s*pm.start_servers\s*=\s*([0-9]+)\s*"
+ conf = re.sub(rep, "\npm.start_servers = " + start_servers, conf)
+
+ rep = r"\s*pm.min_spare_servers\s*=\s*([0-9]+)\s*"
+ conf = re.sub(rep, "\npm.min_spare_servers = " +
+ min_spare_servers, conf)
+
+ rep = r"\s*pm.max_spare_servers \s*=\s*([0-9]+)\s*"
+ conf = re.sub(rep, "\npm.max_spare_servers = " +
+ max_spare_servers + "\n", conf)
+
+ rep = r"\s*pm\s*=\s*(\w+)\s*"
+ conf = re.sub(rep, "\npm = " + pm + "\n", conf)
+
+ mw.writeFile(file, conf)
+ reload(version)
+
+ msg = mw.getInfo('设置PHP-{1}并发设置,max_children={2},start_servers={3},min_spare_servers={4},max_spare_servers={5}', (version, max_children,
+ start_servers, min_spare_servers, max_spare_servers,))
+ mw.writeLog('插件管理[PHP]', msg)
+ return mw.returnJson(True, '设置成功!')
+
+
+# def checkFpmStatusFile(version):
+# if not mw.isInstalledWeb():
+# return False
+
+# dfile = getServerDir() + '/nginx/conf/php_status/phpfpm_status_' + version + '.conf'
+# if not os.path.exists(dfile):
+# tpl = getPluginDir() + '/conf/phpfpm_status.conf'
+# content = mw.readFile(tpl)
+# content = contentReplace(content, version)
+# mw.writeFile(dfile, content)
+# mw.restartWeb()
+# return True
+
+
+def getFpmAddress(version, pool='www'):
+ fpm_address = '/tmp/php-cgi-{}.sock'.format(version)
+ if pool != 'www':
+ fpm_address = '/tmp/php-cgi-{}.{}.sock'.format(version,pool)
+ php_fpm_file = getFpmConfFile(version)
+ try:
+ content = readFile(php_fpm_file)
+ tmp = re.findall(r"listen\s*=\s*(.+)", content)
+ if not tmp:
+ return fpm_address
+ if tmp[0].find('sock') != -1:
+ return fpm_address
+ if tmp[0].find(':') != -1:
+ listen_tmp = tmp[0].split(':')
+ if bind:
+ fpm_address = (listen_tmp[0], int(listen_tmp[1]))
+ else:
+ fpm_address = ('127.0.0.1', int(listen_tmp[1]))
+ else:
+ fpm_address = ('127.0.0.1', int(tmp[0]))
+ return fpm_address
+ except:
+ return fpm_address
+
+
+def getFpmStatus(version):
+
+ if version == '52':
+ return mw.returnJson(False, 'PHP[' + version + ']不支持!!!')
+
+ stat = status(version)
+ if stat == 'stop':
+ return mw.returnJson(False, 'PHP[' + version + ']未启动!!!')
+
+ args = getArgs()
+ pool = 'www'
+ if 'pool' in args:
+ pool = args['pool']
+
+ sock_file = getFpmAddress(version, pool)
+ uri = '/phpfpm_status_' + version + '?json'
+ if pool != 'www':
+ uri = '/phpfpm_status_' + version + '_'+pool+'?json'
+ try:
+ sock_data = mw.requestFcgiPHP(sock_file, uri)
+ except Exception as e:
+ return mw.returnJson(False, str(e))
+
+
+ result = str(sock_data, encoding='utf-8')
+ data = json.loads(result)
+ fTime = time.localtime(int(data['start time']))
+ data['start time'] = time.strftime('%Y-%m-%d %H:%M:%S', fTime)
+ return mw.returnJson(True, "OK", data)
+
+
+def getSessionConf(version):
+ filename = getConf(version)
+ if not os.path.exists(filename):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+
+ phpini = mw.readFile(filename)
+
+ rep = r'session.save_handler\s*=\s*([0-9A-Za-z_& ~]+)(\s*;?|\r?\n)'
+ save_handler = re.search(rep, phpini)
+ if save_handler:
+ save_handler = save_handler.group(1)
+ else:
+ save_handler = "files"
+
+ reppath = r'\nsession.save_path\s*=\s*"tcp\:\/\/([\d\.]+):(\d+).*\r?\n'
+ passrep = r'\nsession.save_path\s*=\s*"tcp://[\w\.\?\:]+=(.*)"\r?\n'
+ memcached = r'\nsession.save_path\s*=\s*"([\d\.]+):(\d+)"'
+ save_path = re.search(reppath, phpini)
+ if not save_path:
+ save_path = re.search(memcached, phpini)
+ passwd = re.search(passrep, phpini)
+ port = ""
+ if passwd:
+ passwd = passwd.group(1)
+ else:
+ passwd = ""
+ if save_path:
+ port = save_path.group(2)
+ save_path = save_path.group(1)
+
+ else:
+ save_path = ""
+
+ data = {"save_handler": save_handler, "save_path": save_path,
+ "passwd": passwd, "port": port}
+ return mw.returnJson(True, 'ok', data)
+
+
+def setSessionConf(version):
+
+ args = getArgs()
+
+ ip = args['ip']
+ port = args['port']
+ passwd = args['passwd']
+ save_handler = args['save_handler']
+
+ if save_handler != "files":
+ iprep = r"(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})"
+ if not re.search(iprep, ip):
+ return mw.returnJson(False, '请输入正确的IP地址')
+
+ try:
+ port = int(port)
+ if port >= 65535 or port < 1:
+ return mw.returnJson(False, '请输入正确的端口号')
+ except:
+ return mw.returnJson(False, '请输入正确的端口号')
+ prep = r"[\~\`\/\=]"
+ if re.search(prep, passwd):
+ return mw.returnJson(False, '请不要输入以下特殊字符 " ~ ` / = "')
+
+ filename = getConf(version)
+ if not os.path.exists(filename):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+ phpini = mw.readFile(filename)
+
+ session_tmp = getServerDir() + "/tmp/session"
+
+ rep = r'session.save_handler\s*=\s*(.+)\r?\n'
+ val = r'session.save_handler = ' + save_handler + '\n'
+ phpini = re.sub(rep, val, phpini)
+
+ if save_handler == "memcached":
+ if not re.search("memcached.so", phpini):
+ return mw.returnJson(False, '请先安装%s扩展' % save_handler)
+ rep = r'\nsession.save_path\s*=\s*(.+)\r?\n'
+ val = r'\nsession.save_path = "%s:%s" \n' % (ip, port)
+ if re.search(rep, phpini):
+ phpini = re.sub(rep, val, phpini)
+ else:
+ phpini = re.sub('\n;session.save_path = "' + session_tmp + '"',
+ '\n;session.save_path = "' + session_tmp + '"' + val, phpini)
+
+ if save_handler == "memcache":
+ if not re.search("memcache.so", phpini):
+ return mw.returnJson(False, '请先安装%s扩展' % save_handler)
+ rep = r'\nsession.save_path\s*=\s*(.+)\r?\n'
+ val = r'\nsession.save_path = "%s:%s" \n' % (ip, port)
+ if re.search(rep, phpini):
+ phpini = re.sub(rep, val, phpini)
+ else:
+ phpini = re.sub('\n;session.save_path = "' + session_tmp + '"',
+ '\n;session.save_path = "' + session_tmp + '"' + val, phpini)
+
+ if save_handler == "redis":
+ if not re.search("redis.so", phpini):
+ return mw.returnJson(False, '请先安装%s扩展' % save_handler)
+ if passwd:
+ passwd = "?auth=" + passwd
+ else:
+ passwd = ""
+ rep = r'\nsession.save_path\s*=\s*(.+)\r?\n'
+ val = r'\nsession.save_path = "tcp://%s:%s%s"\n' % (ip, port, passwd)
+ res = re.search(rep, phpini)
+ if res:
+ phpini = re.sub(rep, val, phpini)
+ else:
+ phpini = re.sub('\n;session.save_path = "' + session_tmp + '"',
+ '\n;session.save_path = "' + session_tmp + '"' + val, phpini)
+
+ if save_handler == "files":
+ rep = r'\nsession.save_path\s*=\s*(.+)\r?\n'
+ val = r'\nsession.save_path = "' + session_tmp + '"\n'
+ if re.search(rep, phpini):
+ phpini = re.sub(rep, val, phpini)
+ else:
+ phpini = re.sub('\n;session.save_path = "' + session_tmp + '"',
+ '\n;session.save_path = "' + session_tmp + '"' + val, phpini)
+
+ mw.writeFile(filename, phpini)
+ reload(version)
+ return mw.returnJson(True, '设置成功!')
+
+
+def getSessionCount_Origin(version):
+ session_tmp = getServerDir() + "/tmp/session"
+ d = [session_tmp]
+ count = 0
+ for i in d:
+ if not os.path.exists(i):
+ mw.execShell('mkdir -p %s' % i)
+ list = os.listdir(i)
+ for l in list:
+ if os.path.isdir(i + "/" + l):
+ l1 = os.listdir(i + "/" + l)
+ for ll in l1:
+ if "sess_" in ll:
+ count += 1
+ continue
+ if "sess_" in l:
+ count += 1
+
+ s = "find /tmp -mtime +1 |grep 'sess_' | wc -l"
+ old_file = int(mw.execShell(s)[0].split("\n")[0])
+
+ s = "find " + session_tmp + " -mtime +1 |grep 'sess_'|wc -l"
+ old_file += int(mw.execShell(s)[0].split("\n")[0])
+ return {"total": count, "oldfile": old_file}
+
+
+def getSessionCount(version):
+ data = getSessionCount_Origin(version)
+ return mw.returnJson(True, 'ok!', data)
+
+
+def cleanSessionOld(version):
+ s = "find /tmp -mtime +1 |grep 'sess_'|xargs rm -f"
+ mw.execShell(s)
+
+ session_tmp = getServerDir() + "/tmp/session"
+ s = "find " + session_tmp + " -mtime +1 |grep 'sess_' |xargs rm -f"
+ mw.execShell(s)
+ old_file_conf = getSessionCount_Origin(version)["oldfile"]
+ if old_file_conf == 0:
+ return mw.returnJson(True, '清理成功')
+ else:
+ return mw.returnJson(True, '清理失败')
+
+
+def getDisableFunc(version):
+ filename = getConf(version)
+ if not os.path.exists(filename):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+
+ phpini = mw.readFile(filename)
+ data = {}
+ rep = r"disable_functions\s*=\s{0,1}(.*)\n"
+ tmp = re.search(rep, phpini).groups()
+ data['disable_functions'] = tmp[0]
+ return mw.getJson(data)
+
+
+def setDisableFunc(version):
+ filename = getConf(version)
+ if not os.path.exists(filename):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+
+ args = getArgs()
+ disable_functions = args['disable_functions']
+
+ phpini = mw.readFile(filename)
+ rep = r"disable_functions\s*=\s*.*\n"
+ phpini = re.sub(rep, 'disable_functions = ' +
+ disable_functions + "\n", phpini)
+
+ msg = mw.getInfo('修改PHP-{1}的禁用函数为[{2}]', (version, disable_functions,))
+ mw.writeLog('插件管理[PHP]', msg)
+ mw.writeFile(filename, phpini)
+ reload(version)
+ return mw.returnJson(True, '设置成功!')
+
+
+def getPhpinfo(version):
+ stat = status(version)
+ if stat == 'stop':
+ return 'PHP[' + version + ']未启动,不可访问!!!'
+
+ sock_file = getFpmAddress(version)
+ root_dir = mw.getFatherDir() + '/phpinfo'
+
+ mw.execShell("rm -rf " + root_dir)
+ mw.execShell("mkdir -p " + root_dir)
+ mw.writeFile(root_dir + '/phpinfo.php', '')
+ sock_data = mw.requestFcgiPHP(sock_file, '/phpinfo.php', root_dir)
+ os.system("rm -rf " + root_dir)
+ phpinfo = str(sock_data, encoding='utf-8')
+ return phpinfo
+
+
+def get_php_info(args):
+ return getPhpinfo(args['version'])
+
+
+def libConfCommon(version):
+ fname = getConf(version)
+ if not os.path.exists(fname):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+
+ phpini = mw.readFile(fname)
+
+ libpath = getPluginDir() + '/versions/phplib.conf'
+ phplib = json.loads(mw.readFile(libpath))
+
+ libs = []
+ tasks = mw.M('tasks').where("status!=?", ('1',)).field('status,name').select()
+ for lib in phplib:
+ lib['task'] = '1'
+ for task in tasks:
+ tmp = mw.getStrBetween('[', ']', task['name'])
+ if not tmp:
+ continue
+ tmp1 = tmp.split('-')
+ if tmp1[0].lower() == lib['name'].lower():
+ lib['task'] = task['status']
+ lib['phpversions'] = []
+ lib['phpversions'].append(tmp1[1])
+ if phpini.find(lib['check']) == -1:
+ lib['status'] = False
+ else:
+ lib['status'] = True
+ libs.append(lib)
+ return libs
+
+
+def get_lib_conf(data):
+ libs = libConfCommon(data['version'])
+ # print(libs)
+ return mw.returnData(True, 'OK!', libs)
+
+
+def getLibConf(version):
+ libs = libConfCommon(version)
+ return mw.returnJson(True, 'OK!', libs)
+
+
+def installLib(version):
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ name = args['name']
+ cmd = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + version + ' install ' + name
+ install_name = '安装[' + name + '-' + version + ']'
+ import thisdb
+ thisdb.addTask(name=install_name,cmd=cmd)
+
+ mw.triggerTask()
+ return mw.returnJson(True, '已将下载任务添加到队列!')
+
+
+def uninstallLib(version):
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ name = args['name']
+ execstr = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + version + ' uninstall ' + name
+
+ data = mw.execShell(execstr)
+ # data[0] == '' and
+ if data[1] == '':
+ return mw.returnJson(True, '已经卸载成功!')
+ else:
+ return mw.returnJson(False, '卸载信息![通道0]:' + data[0] + "[通道0]:" + data[1])
+
+
+def getConfAppStart():
+ pstart = mw.getServerDir() + '/php/app_start.php'
+ return pstart
+
+def opcacheBlacklistFile():
+ op_bl = mw.getServerDir() + '/php/opcache-blacklist.txt'
+ return op_bl
+
+
+def installPreInspection(version):
+ # 仅对PHP52检查
+ if version != '52':
+ return 'ok'
+
+ sys = mw.execShell(
+ "cat /etc/*-release | grep PRETTY_NAME |awk -F = '{print $2}' | awk -F '\"' '{print $2}'| awk '{print $1}'")
+
+ if sys[1] != '':
+ return '不支持改系统'
+
+ sys_id = mw.execShell(
+ "cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F '\"' '{print $2}'")
+
+ sysName = sys[0].strip().lower()
+ sysId = sys_id[0].strip()
+
+ if sysName == 'ubuntu':
+ return 'ubuntu已经安装不了'
+
+ if sysName == 'debian' and int(sysId) > 10:
+ return 'debian10可以安装'
+
+ if sysName == 'centos' and int(sysId) > 8:
+ return 'centos[{}]不可以安装'.format(sysId)
+
+ if sysName == 'fedora':
+ sys_id = mw.execShell(
+ "cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}'")
+ sysId = sys_id[0].strip()
+ if int(sysId) > 31:
+ return 'fedora[{}]不可安装'.format(sysId)
+ return 'ok'
+
+
+if __name__ == "__main__":
+
+ if len(sys.argv) < 3:
+ print('missing parameters')
+ exit(0)
+
+ func = sys.argv[1]
+ version = sys.argv[2]
+
+ if func == 'status':
+ print(status(version))
+ elif func == 'start':
+ print(start(version))
+ elif func == 'stop':
+ print(stop(version))
+ elif func == 'restart':
+ print(restart(version))
+ elif func == 'reload':
+ print(reload(version))
+ elif func == 'install_pre_inspection':
+ print(installPreInspection(version))
+ elif func == 'initd_status':
+ print(initdStatus(version))
+ elif func == 'initd_install':
+ print(initdInstall(version))
+ elif func == 'initd_uninstall':
+ print(initdUinstall(version))
+ elif func == 'fpm_log':
+ print(fpmLog(version))
+ elif func == 'fpm_slow_log':
+ print(fpmSlowLog(version))
+ elif func == 'conf':
+ print(getConf(version))
+ elif func == 'app_start':
+ print(getConfAppStart())
+ elif func == 'opcache_blacklist_file':
+ print(opcacheBlacklistFile())
+ elif func == 'get_php_conf':
+ print(getPhpConf(version))
+ elif func == 'get_fpm_conf_file':
+ print(getFpmConfFile(version))
+ elif func == 'get_fpm_file':
+ print(getFpmFile(version))
+ elif func == 'submit_php_conf':
+ print(submitPhpConf(version))
+ elif func == 'get_limit_conf':
+ print(getLimitConf(version))
+ elif func == 'set_max_time':
+ print(setMaxTime(version))
+ elif func == 'set_max_size':
+ print(setMaxSize(version))
+ elif func == 'get_fpm_conf':
+ print(getFpmConfig(version))
+ elif func == 'set_fpm_conf':
+ print(setFpmConfig(version))
+ elif func == 'get_fpm_status':
+ print(getFpmStatus(version))
+ elif func == 'get_session_conf':
+ print(getSessionConf(version))
+ elif func == 'set_session_conf':
+ print(setSessionConf(version))
+ elif func == 'get_session_count':
+ print(getSessionCount(version))
+ elif func == 'clean_session_old':
+ print(cleanSessionOld(version))
+ elif func == 'get_disable_func':
+ print(getDisableFunc(version))
+ elif func == 'set_disable_func':
+ print(setDisableFunc(version))
+ elif func == 'get_phpinfo':
+ print(getPhpinfo(version))
+ elif func == 'get_lib_conf':
+ print(getLibConf(version))
+ elif func == 'install_lib':
+ print(installLib(version))
+ elif func == 'uninstall_lib':
+ print(uninstallLib(version))
+ else:
+ print("fail")
diff --git a/plugins/php/index_php.py b/plugins/php/index_php.py
new file mode 100755
index 000000000..49edd6d0a
--- /dev/null
+++ b/plugins/php/index_php.py
@@ -0,0 +1,176 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import shutil
+
+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 'php'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getInitDFile(version):
+ 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() + version
+
+
+def getConf(version):
+ path = getServerDir() + '/' + version + '/etc/php.ini'
+ return path
+
+
+def getFpmConfFile(version):
+ return getServerDir() + '/' + version + '/etc/php-fpm.d/www.conf'
+
+
+def status_progress(version):
+ # ps -ef|grep 'php/81' |grep -v grep | grep -v python | awk '{print $2}
+ cmd = "ps aux|grep 'php/" + version + \
+ "' |grep -v grep | grep -v python | awk '{print $2}'"
+ data = mw.execShell(cmd)
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def getPhpSocket(version):
+ path = getFpmConfFile(version)
+ content = mw.readFile(path)
+ rep = r'listen\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+
+def status(version):
+ '''
+ sock文件判断是否启动
+ '''
+ sock = getPhpSocket(version)
+ if sock.find(':'):
+ return status_progress(version)
+
+ if not os.path.exists(sock):
+ return 'stop'
+ return 'start'
+
+
+def getFpmAddress(version):
+ fpm_address = '/tmp/php-cgi-{}.sock'.format(version)
+ php_fpm_file = getFpmConfFile(version)
+ try:
+ content = readFile(php_fpm_file)
+ tmp = re.findall(r"listen\s*=\s*(.+)", content)
+ if not tmp:
+ return fpm_address
+ if tmp[0].find('sock') != -1:
+ return fpm_address
+ if tmp[0].find(':') != -1:
+ listen_tmp = tmp[0].split(':')
+ if bind:
+ fpm_address = (listen_tmp[0], int(listen_tmp[1]))
+ else:
+ fpm_address = ('127.0.0.1', int(listen_tmp[1]))
+ else:
+ fpm_address = ('127.0.0.1', int(tmp[0]))
+ return fpm_address
+ except:
+ return fpm_address
+
+
+def getPhpinfo(version):
+ stat = status(version)
+ if stat == 'stop':
+ return 'PHP[' + version + ']未启动,不可访问!!!'
+
+ sock_file = getFpmAddress(version)
+ root_dir = mw.getFatherDir() + '/phpinfo'
+
+ mw.execShell("rm -rf " + root_dir)
+ mw.execShell("mkdir -p " + root_dir)
+ mw.writeFile(root_dir + '/phpinfo.php', '')
+ sock_data = mw.requestFcgiPHP(sock_file, '/phpinfo.php', root_dir)
+ os.system("rm -rf " + root_dir)
+ phpinfo = str(sock_data, encoding='utf-8')
+ return phpinfo
+
+
+def libConfCommon(version):
+ fname = getConf(version)
+ if not os.path.exists(fname):
+ return mw.returnJson(False, '指定PHP版本不存在!')
+
+ phpini = mw.readFile(fname)
+
+ libpath = getPluginDir() + '/versions/phplib.conf'
+ phplib = json.loads(mw.readFile(libpath))
+
+ php_dir = getServerDir() + "/" + version
+ ext_dir = php_dir+"/lib/php/extensions"
+
+ ext_list = os.listdir(ext_dir)
+ for sodir in ext_list:
+ if sodir.find("no-debug-non-zts") > -1:
+ ext_dir += "/"+ sodir
+ break
+
+ libs = []
+ tasks = mw.M('tasks').where("status!=?", ('1',)).field('status,name').select()
+ for lib in phplib:
+ lib['task'] = '1'
+ for task in tasks:
+ tmp = mw.getStrBetween('[', ']', task['name'])
+ if not tmp:
+ continue
+ tmp1 = tmp.split('-')
+ if tmp1[0].lower() == lib['name'].lower():
+ lib['task'] = task['status']
+ lib['phpversions'] = []
+ lib['phpversions'].append(tmp1[1])
+
+ lib['status'] = False
+ if phpini.find(lib['check']) > -1:
+ lib['status'] = True
+ sofile = ext_dir+"/"+lib['check']
+ # 自定义,比较特殊的方式
+ if os.path.exists(sofile):
+ if os.path.getsize(sofile) == 7:
+ lib['status'] = True
+ libs.append(lib)
+ return libs
+
+
+def get_php_info(args):
+ return getPhpinfo(args['version'])
+
+
+def get_lib_conf(data):
+ libs = libConfCommon(data['version'])
+ return mw.returnData(True, 'OK!', libs)
diff --git a/plugins/php/info.json b/plugins/php/info.json
new file mode 100755
index 000000000..0fd7d34e6
--- /dev/null
+++ b/plugins/php/info.json
@@ -0,0 +1,20 @@
+{
+ "sort": 7,
+ "ps": "PHP是世界上最好的编程语言",
+ "shell": "install.sh",
+ "name": "php",
+ "title": "PHP",
+ "coexist": true,
+ "versions": ["53","54","55","56","70","71","72","73","74","80","81","82","83","84","85"],
+ "updates": ["5.3.29","5.4.45","5.6.36","7.0.30","7.1.33","7.2.32","7.3.20","7.4.30","8.0.30","8.1.29","8.2.27","8.3.15","8.4.2","8.5.0"],
+ "tip": "soft",
+ "install_pre_inspection":true,
+ "checks": "server/php/VERSION/bin/php",
+ "path": "server/php/VERSION",
+ "display": 1,
+ "author": "Zend",
+ "date": "2017-04-01",
+ "home": "https://www.php.net",
+ "type": "PHP语言解释器",
+ "pid": "1"
+}
\ No newline at end of file
diff --git a/plugins/php/init.d/php.service.52.tpl b/plugins/php/init.d/php.service.52.tpl
new file mode 100644
index 000000000..d837568e8
--- /dev/null
+++ b/plugins/php/init.d/php.service.52.tpl
@@ -0,0 +1,16 @@
+# It's not recommended to modify this file in-place, because it
+# will be overwritten during upgrades. If you want to customize,
+# the best way is to use the "systemctl edit" command.
+
+[Unit]
+Description=The PHP {$VERSION} FastCGI Process Manager
+After=network.target
+
+[Service]
+Type=forking
+ExecStart={$SERVER_PATH}/php/init.d/php{$VERSION} start
+ExecStop={$SERVER_PATH}/php/init.d/php{$VERSION} stop
+PrivateTmp=false
+
+[Install]
+WantedBy=multi-user.target
diff --git a/plugins/php/init.d/php.service.tpl b/plugins/php/init.d/php.service.tpl
new file mode 100644
index 000000000..a1fe07178
--- /dev/null
+++ b/plugins/php/init.d/php.service.tpl
@@ -0,0 +1,18 @@
+# It's not recommended to modify this file in-place, because it
+# will be overwritten during upgrades. If you want to customize,
+# the best way is to use the "systemctl edit" command.
+# systemctl daemon-reload
+
+[Unit]
+Description=The PHP {$VERSION} FastCGI Process Manager
+After=network.target
+
+[Service]
+Environment="LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/www/server/lib/icu/lib"
+PIDFile={$SERVER_PATH}/php/{$VERSION}/var/run/php-fpm.pid
+ExecStart={$SERVER_PATH}/php/{$VERSION}/sbin/php-fpm --nodaemonize --fpm-config {$SERVER_PATH}/php/{$VERSION}/etc/php-fpm.conf
+ExecReload=/bin/kill -USR2 $MAINPID
+PrivateTmp=false
+
+[Install]
+WantedBy=multi-user.target
diff --git a/plugins/php/init.d/php.tpl b/plugins/php/init.d/php.tpl
new file mode 100644
index 000000000..bbf46fddd
--- /dev/null
+++ b/plugins/php/init.d/php.tpl
@@ -0,0 +1,154 @@
+#! /bin/sh
+# chkconfig: 2345 55 25
+# description: PHP Service
+
+### BEGIN INIT INFO
+# Provides: php-fpm
+# Required-Start: $remote_fs $network
+# Required-Stop: $remote_fs $network
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts php-fpm
+# Description: starts the PHP FastCGI Process Manager daemon
+### END INIT INFO
+
+prefix={$SERVER_PATH}/php/{$PHP_VERSION}
+exec_prefix=${prefix}
+
+php_fpm_BIN=${exec_prefix}/sbin/php-fpm
+php_fpm_CONF=${prefix}/etc/php-fpm.conf
+php_fpm_PID=${prefix}/var/run/php-fpm.pid
+
+
+php_opts="--fpm-config $php_fpm_CONF --pid $php_fpm_PID"
+
+
+wait_for_pid () {
+ try=0
+
+ while test $try -lt 35 ; do
+
+ case "$1" in
+ 'created')
+ if [ -f "$2" ] ; then
+ try=''
+ break
+ fi
+ ;;
+
+ 'removed')
+ if [ ! -f "$2" ] ; then
+ try=''
+ break
+ fi
+ ;;
+ esac
+
+ echo -n .
+ try=`expr $try + 1`
+ sleep 1
+
+ done
+
+}
+
+case "$1" in
+ start)
+ echo -n "Starting php-fpm "
+
+ $php_fpm_BIN --daemonize $php_opts
+
+ if [ "$?" != 0 ] ; then
+ echo " failed"
+ exit 1
+ fi
+
+ wait_for_pid created $php_fpm_PID
+
+ if [ -n "$try" ] ; then
+ echo " failed"
+ exit 1
+ else
+ echo " done"
+ fi
+ ;;
+
+ stop)
+ echo -n "Gracefully shutting down php-fpm "
+
+ if [ ! -r $php_fpm_PID ] ; then
+ echo "warning, no pid file found - php-fpm is not running ?"
+ exit 1
+ fi
+
+ kill -QUIT `cat $php_fpm_PID`
+
+ wait_for_pid removed $php_fpm_PID
+
+ if [ -n "$try" ] ; then
+ echo " failed. Use force-quit"
+ exit 1
+ else
+ echo " done"
+ fi
+ ;;
+
+ status)
+ if [ ! -r $php_fpm_PID ] ; then
+ echo "php-fpm is stopped"
+ exit 0
+ fi
+
+ PID=`cat $php_fpm_PID`
+ if ps -p $PID | grep -q $PID; then
+ echo "php-fpm (pid $PID) is running..."
+ else
+ echo "php-fpm dead but pid file exists"
+ fi
+ ;;
+
+ force-quit)
+ echo -n "Terminating php-fpm "
+
+ if [ ! -r $php_fpm_PID ] ; then
+ echo "warning, no pid file found - php-fpm is not running ?"
+ exit 1
+ fi
+
+ kill -TERM `cat $php_fpm_PID`
+
+ wait_for_pid removed $php_fpm_PID
+
+ if [ -n "$try" ] ; then
+ echo " failed"
+ exit 1
+ else
+ echo " done"
+ fi
+ ;;
+
+ restart)
+ $0 stop
+ $0 start
+ ;;
+
+ reload)
+
+ echo -n "Reload service php-fpm "
+
+ if [ ! -r $php_fpm_PID ] ; then
+ echo "warning, no pid file found - php-fpm is not running ?"
+ exit 1
+ fi
+
+ kill -USR2 `cat $php_fpm_PID`
+
+ echo " done"
+ ;;
+
+ *)
+ echo "Usage: $0 {start|stop|force-quit|restart|reload|status}"
+ exit 1
+ ;;
+
+esac
diff --git a/plugins/php/init.d/php52.tpl b/plugins/php/init.d/php52.tpl
new file mode 100644
index 000000000..73f96406b
--- /dev/null
+++ b/plugins/php/init.d/php52.tpl
@@ -0,0 +1,139 @@
+#! /bin/sh
+
+php_fpm_BIN=/www/server/php/52/bin/php-cgi
+php_fpm_CONF=/www/server/php/52/etc/php-fpm.conf
+php_fpm_PID=/www/server/php/52/var/run/php-fpm.pid
+
+
+php_opts="--fpm-config $php_fpm_CONF"
+
+
+wait_for_pid () {
+ try=0
+
+ while test $try -lt 35 ; do
+
+ case "$1" in
+ 'created')
+ if [ -f "$2" ] ; then
+ try=''
+ break
+ fi
+ ;;
+
+ 'removed')
+ if [ ! -f "$2" ] ; then
+ try=''
+ break
+ fi
+ ;;
+ esac
+
+ echo -n .
+ try=`expr $try + 1`
+ sleep 1
+
+ done
+
+}
+
+case "$1" in
+ start)
+ echo -n "Starting php_fpm "
+
+ $php_fpm_BIN --fpm $php_opts
+
+ if [ "$?" != 0 ] ; then
+ echo " failed"
+ exit 1
+ fi
+
+ wait_for_pid created $php_fpm_PID
+
+ if [ -n "$try" ] ; then
+ echo " failed"
+ exit 1
+ else
+ echo " done"
+ fi
+ ;;
+
+ stop)
+ echo -n "Shutting down php_fpm "
+
+ if [ ! -r $php_fpm_PID ] ; then
+ echo "warning, no pid file found - php-fpm is not running ?"
+ exit 1
+ fi
+
+ kill -TERM `cat $php_fpm_PID`
+
+ wait_for_pid removed $php_fpm_PID
+
+ if [ -n "$try" ] ; then
+ echo " failed"
+ exit 1
+ else
+ echo " done"
+ fi
+ ;;
+
+ quit)
+ echo -n "Gracefully shutting down php_fpm "
+
+ if [ ! -r $php_fpm_PID ] ; then
+ echo "warning, no pid file found - php-fpm is not running ?"
+ exit 1
+ fi
+
+ kill -QUIT `cat $php_fpm_PID`
+
+ wait_for_pid removed $php_fpm_PID
+
+ if [ -n "$try" ] ; then
+ echo " failed"
+ exit 1
+ else
+ echo " done"
+ fi
+ ;;
+
+ restart)
+ $0 stop
+ $0 start
+ ;;
+
+ reload)
+
+ echo -n "Reload service php-fpm "
+
+ if [ ! -r $php_fpm_PID ] ; then
+ echo "warning, no pid file found - php-fpm is not running ?"
+ exit 1
+ fi
+
+ kill -USR2 `cat $php_fpm_PID`
+
+ echo " done"
+ ;;
+
+ logrotate)
+
+ echo -n "Re-opening php-fpm log file "
+
+ if [ ! -r $php_fpm_PID ] ; then
+ echo "warning, no pid file found - php-fpm is not running ?"
+ exit 1
+ fi
+
+ kill -USR1 `cat $php_fpm_PID`
+
+ echo " done"
+ ;;
+
+ *)
+ echo "Usage: $0 {start|stop|quit|restart|reload|logrotate}"
+ exit 1
+ ;;
+
+esac
diff --git a/plugins/php/install.sh b/plugins/php/install.sh
new file mode 100755
index 000000000..21611e98e
--- /dev/null
+++ b/plugins/php/install.sh
@@ -0,0 +1,95 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sysName=`uname`
+
+# cd /www/server/mdserver-web/plugins/php && bash install.sh install 73
+# cd /www/server/mdserver-web/plugins/php && bash install.sh install 85
+# https://www.php.net/releases
+
+if id www &> /dev/null ;then
+ echo "www uid is `id -u www`"
+ echo "www shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `"
+else
+ groupadd www
+ useradd -g www -s /sbin/nologin www
+ # useradd -g www -s /bin/bash www
+fi
+
+action=$1
+type=$2
+
+if [ "${2}" == "" ];then
+ echo '缺少安装脚本...'
+ exit 0
+fi
+
+if [ ! -d $curPath/versions/$2 ];then
+ echo '缺少安装脚本2...'
+ exit 0
+fi
+
+
+# if [ "${action}" == "install" ] && [ -d $serverPath/php/${type} ];then
+# exit 0
+# fi
+
+if [ "${action}" == "uninstall" ];then
+
+ if [ -f /usr/lib/systemd/system/php${type}.service ] || [ -f /lib/systemd/system/php${type}.service ] ;then
+ systemctl stop php${type}
+ systemctl disable php${type}
+ rm -rf /usr/lib/systemd/system/php${type}.service
+ rm -rf /lib/systemd/system/php${type}.service
+ systemctl daemon-reload
+ fi
+fi
+
+cd ${curPath} && sh -x $curPath/versions/$2/install.sh $1
+
+
+if [ "${action}" == "install" ] && [ -d ${serverPath}/php/${type} ];then
+
+ #初始化
+ cd ${rootPath} && python3 ${rootPath}/plugins/php/index.py start ${type}
+ cd ${rootPath} && python3 ${rootPath}/plugins/php/index.py initd_install ${type}
+
+ # 安装通用扩展
+ echo "install PHP${type} extend start"
+
+ # cd /www/server/mdserver-web/plugins/php/versions/common && bash iconv.sh install 53
+ # cd /www/server/mdserver-web/plugins/php/versions/common && bash intl.sh install 73
+ # cd /www/server/mdserver-web/plugins/php/versions/common && bash gd.sh install 56
+ # cd /www/server/mdserver-web/plugins/php/versions/common && bash openssl.sh install 56
+ # cd /www/server/mdserver-web/plugins/php/versions/common && bash fileinfo.sh install 81
+ cd ${rootPath}/plugins/php/versions/common && bash curl.sh install ${type}
+ cd ${rootPath}/plugins/php/versions/common && bash gd.sh install ${type}
+ cd ${rootPath}/plugins/php/versions/common && bash readline.sh install ${type}
+ cd ${rootPath}/plugins/php/versions/common && bash iconv.sh install ${type}
+ cd ${rootPath}/plugins/php/versions/common && bash exif.sh install ${type}
+ cd ${rootPath}/plugins/php/versions/common && bash intl.sh install ${type}
+ cd ${rootPath}/plugins/php/versions/common && bash mcrypt.sh install ${type}
+ cd ${rootPath}/plugins/php/versions/common && bash openssl.sh install ${type}
+ cd ${rootPath}/plugins/php/versions/common && bash bcmath.sh install ${type}
+ cd ${rootPath}/plugins/php/versions/common && bash gettext.sh install ${type}
+ cd ${rootPath}/plugins/php/versions/common && bash redis.sh install ${type}
+ cd ${rootPath}/plugins/php/versions/common && bash memcached.sh install ${type}
+ cd ${rootPath}/plugins/php/versions/common && bash pcntl.sh install ${type}
+ cd ${rootPath}/plugins/php/versions/common && bash zip.sh install ${type}
+ cd ${rootPath}/plugins/php/versions/common && bash zlib.sh install ${type}
+
+ echo "install PHP${type} extend end"
+
+ if [ ! -f /usr/local/bin/composer ] && [ "$sysName" != "Darwin" ] ;then
+ cd /tmp
+ curl -sS https://getcomposer.org/installer | /www/server/php/${type}/bin/php
+ mv composer.phar /usr/local/bin/composer
+ fi
+fi
+
+
diff --git a/plugins/php/js/php.js b/plugins/php/js/php.js
new file mode 100755
index 000000000..c1bdb81d4
--- /dev/null
+++ b/plugins/php/js/php.js
@@ -0,0 +1,858 @@
+function phpPost(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'php';
+ req_data['func'] = method;
+ req_data['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ //错误展示10S
+ layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function phpPostCallback(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'php';
+ req_data['func'] = method;
+ req_data['script']='index_php';
+ args['version'] = version;
+
+
+ if (typeof(args) == 'string' && args == ''){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+//配置修改
+function phpSetConfig(version) {
+ phpPost('get_php_conf', version,'',function(data){
+ // console.log(data);
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata);
+ var mlist = '';
+ for (var i = 0; i < rdata.length; i++) {
+ var w = '70'
+ if (rdata[i].name == 'error_reporting') w = '250';
+ var ibody = '
';
+ switch (rdata[i].type) {
+ case 0:
+ var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
+ var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
+ ibody = '
开启 关闭 '
+ break;
+ case 1:
+ var selected_1 = (rdata[i].value == 'On') ? 'selected' : '';
+ var selected_0 = (rdata[i].value == 'Off') ? 'selected' : '';
+ ibody = '
开启 关闭 '
+ break;
+ }
+ mlist += '
' + rdata[i].name + ' ' + ibody + ', ' + rdata[i].ps + '
';
+ }
+ var phpCon = '
\
+ ' + mlist + '\
+
\
+ 刷新 \
+ 保存 \
+
\
+
'
+ $(".soft-man-con").html(phpCon);
+ });
+}
+
+
+//提交PHP配置
+function submitConf(version) {
+ var data = {
+ version: version,
+ display_errors: $("select[name='display_errors']").val(),
+ 'cgi.fix_pathinfo': $("select[name='cgi.fix_pathinfo']").val(),
+ 'date.timezone': $("input[name='date.timezone']").val(),
+ short_open_tag: $("select[name='short_open_tag']").val(),
+ asp_tags: $("select[name='asp_tags']").val() || 'On',
+ safe_mode: $("select[name='safe_mode']").val(),
+ max_execution_time: $("input[name='max_execution_time']").val(),
+ max_input_time: $("input[name='max_input_time']").val(),
+ max_input_vars: $("input[name='max_input_vars']").val(),
+ memory_limit: $("input[name='memory_limit']").val(),
+ post_max_size: $("input[name='post_max_size']").val(),
+ file_uploads: $("select[name='file_uploads']").val(),
+ upload_max_filesize: $("input[name='upload_max_filesize']").val(),
+ max_file_uploads: $("input[name='max_file_uploads']").val(),
+ default_socket_timeout: $("input[name='default_socket_timeout']").val(),
+ error_reporting: $("input[name='error_reporting']").val() || 'On'
+ };
+
+ phpPost('submit_php_conf', version, data, function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ // console.log(rdata);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+
+//php超时限制
+function phpCommonFunc(version){
+ phpPost('get_limit_conf', version, '', function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ var con = '
\
+ 超时限制 \
+ , 秒\
+ 保存 \
+
';
+
+ con += '
\
+ 上传限制 \
+ ,MB\
+ 保存 \
+
';
+
+ con += '
\
+ 查看phpinfo() \
+ 预加载脚本 \
+ OPCACHE黑名单 \
+ PHP-FPM(global) \
+
';
+
+ $(".soft-man-con").html(con);
+ });
+}
+
+//设置超时限制
+function setPHPMaxTime(version) {
+ var max = $(".phpTimeLimit").val();
+ phpPost('set_max_time',version,{'time':max},function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg,function(){
+ phpCommonFunc(version);
+ },{ icon: rdata.status ? 1 : 2 });
+
+ });
+}
+//设置PHP上传限制
+function setPHPMaxSize(version) {
+ max = $(".phpUploadLimit").val();
+ if (max < 2) {
+ alert(max);
+ layer.msg('上传大小限制不能小于2M', { icon: 2 });
+ return;
+ }
+
+ phpPost('set_max_size',version,{'max':max},function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+function phpPreload(version){
+ phpPost('app_start',version,{},function(data){
+ onlineEditFile(0, data['data']);
+ });
+}
+
+function phpOpcacheBlacklist(version){
+ phpPost('opcache_blacklist_file',version,{},function(data){
+ onlineEditFile(0, data['data']);
+ });
+}
+
+
+function phpFpmRoot(version){
+ phpPost('get_fpm_file',version,{},function(data){
+ onlineEditFile(0, data['data']);
+ });
+}
+
+function phpFpmConfigFile(version, func, pool = 'www'){
+ var _name = 'php';
+ if ( typeof(version) == 'undefined' ){
+ version = '';
+ }
+
+ var func_name = 'conf';
+ if ( typeof(func) != 'undefined' ){
+ func_name = func;
+ }
+
+ var poolSelect = "
\
+ www \
+ backup \
+ ";
+
+ var con = '
提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换!
\
+
\
+
保存 \
+ '+poolSelect+'\
+
\
+ 此处为'+ _name + version +'应用池配置文件,若您不了解配置规则,请勿随意修改。 \
+ ';
+
+
+ var loadT = layer.msg('配置文件路径获取中...',{icon:16,time:0,shade: [0.3, '#000']});
+
+ var request_data = {name:_name, func:func_name,version:version};
+ request_data['args'] = JSON.stringify({'pool':pool});
+
+ $.post('/plugins/run', request_data, function (data) {
+ layer.close(loadT);
+
+ try{
+ var jdata = $.parseJSON(data.data);
+ if (!jdata['status']){
+ layer.msg(jdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ }catch(err){/*console.log(err);*/}
+
+ $(".soft-man-con").html(con);
+
+ var loadT2 = layer.msg('文件内容获取中...',{icon:16,time:0,shade: [0.3, '#000']});
+ var fileName = data.data;
+ $.post('/files/get_body', 'path=' + fileName, function(rdata) {
+ layer.close(loadT2);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ $("#textBody").empty().text(rdata.data.data);
+ $(".CodeMirror").remove();
+ var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), {
+ extraKeys: {
+ "Ctrl-Space": "autocomplete",
+ "Ctrl-F": "findPersistent",
+ "Ctrl-H": "replaceAll",
+ "Ctrl-S": function() {
+ $("#textBody").text(editor.getValue());
+ pluginConfigSave(fileName);
+ }
+ },
+ lineNumbers: true,
+ matchBrackets:true,
+ });
+ editor.focus();
+ $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0});
+ $("#onlineEditFileBtn").click(function(){
+ $("#textBody").text(editor.getValue());
+ pluginConfigSave(fileName);
+ });
+ },'json');
+
+ $('select[name="pool"]').change(function(){
+ var pool = $(this).val();
+ phpFpmConfigFile(version, func, pool);
+ });
+ },'json');
+}
+
+function getFpmConfig(version, pool = 'www'){
+ phpPost('get_fpm_conf', version, {'pool':pool}, function(data){
+ // console.log(data);
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata);
+ var limitList = "
自定义 " +
+ "
2并发 " +
+ "
5并发 " +
+ "
10并发 " +
+ "
30并发 " +
+ "
50并发 " +
+ "
100并发 " +
+ "
200并发 " +
+ "
300并发 " +
+ "
500并发 " +
+ "
2000并发 ";
+ var pms = [{ 'name': 'static', 'title': '静态' }, { 'name': 'dynamic', 'title': '动态' },{ 'name': 'ondemand', 'title': '按需' }];
+ var pmList = '';
+ for (var i = 0; i < pms.length; i++) {
+ pmList += '
' + pms[i].title + ' ';
+ }
+
+ var poolHtml = "
www " +
+ "
backup ";
+
+ var body = "
" +
+ "
应用池[pool]: " + poolHtml + "
" +
+ "
并发方案: " + limitList + "
" +
+ "
运行模式: " + pmList + " *PHP-FPM运行模式
" +
+ "
max_children: *允许创建的最大子进程数
" +
+ "
start_servers: *起始进程数(服务启动后初始进程数量)
" +
+ "
min_spare_servers: *最小空闲进程数(清理空闲进程后的保留数量)
" +
+ "
max_spare_servers: *最大空闲进程数(当空闲进程达到此值时清理)
" +
+ "
保存
" +
+ "
";
+
+ $(".soft-man-con").html(body);
+ $("select[name='limit']").change(function() {
+ var type = $(this).val();
+ var max_children = rdata.max_children;
+ var start_servers = rdata.start_servers;
+ var min_spare_servers = rdata.min_spare_servers;
+ var max_spare_servers = rdata.max_spare_servers;
+ switch (type) {
+ case '0':
+ max_children = 2;
+ start_servers = 1;
+ min_spare_servers = 1;
+ max_spare_servers = 2;
+ break;
+ case '1':
+ max_children = 5;
+ start_servers = 2;
+ min_spare_servers = 1;
+ max_spare_servers = 5;
+ break;
+ case '2':
+ max_children = 10;
+ start_servers = 2;
+ min_spare_servers = 1;
+ max_spare_servers = 10;
+ break;
+ case '3':
+ max_children = 30;
+ start_servers = 5;
+ min_spare_servers = 5;
+ max_spare_servers = 20;
+ break;
+ case '4':
+ max_children = 50;
+ start_servers = 15;
+ min_spare_servers = 15;
+ max_spare_servers = 35;
+ break;
+ case '5':
+ max_children = 100;
+ start_servers = 20;
+ min_spare_servers = 20;
+ max_spare_servers = 70;
+ break;
+ case '6':
+ max_children = 200;
+ start_servers = 25;
+ min_spare_servers = 25;
+ max_spare_servers = 150;
+ break;
+ case '7':
+ max_children = 300;
+ start_servers = 30;
+ min_spare_servers = 30;
+ max_spare_servers = 180;
+ break;
+ case '8':
+ max_children = 500;
+ start_servers = 35;
+ min_spare_servers = 35;
+ max_spare_servers = 250;
+ break;
+ case '9':
+ max_children = 2000;
+ start_servers = 40;
+ min_spare_servers = 40;
+ max_spare_servers = 255;
+ break;
+ }
+
+ $("input[name='max_children']").val(max_children);
+ $("input[name='start_servers']").val(start_servers);
+ $("input[name='min_spare_servers']").val(min_spare_servers);
+ $("input[name='max_spare_servers']").val(max_spare_servers);
+ });
+
+ $('select[name="pool"]').change(function(){
+ var pool = $(this).val();
+ getFpmConfig(version, pool);
+ });
+ });
+}
+
+function setFpmConfig(version){
+ var max_children = Number($("input[name='max_children']").val());
+ var pool = $("select[name='pool']").val();
+ var start_servers = Number($("input[name='start_servers']").val());
+ var min_spare_servers = Number($("input[name='min_spare_servers']").val());
+ var max_spare_servers = Number($("input[name='max_spare_servers']").val());
+ var pm = $("select[name='pm']").val();
+
+ if (max_children < max_spare_servers) {
+ layer.msg('max_spare_servers 不能大于 max_children', { icon: 2 });
+ return;
+ }
+
+ if (min_spare_servers > start_servers) {
+ layer.msg('min_spare_servers 不能大于 start_servers', { icon: 2 });
+ return;
+ }
+
+ if (max_spare_servers < min_spare_servers) {
+ layer.msg('min_spare_servers 不能大于 max_spare_servers', { icon: 2 });
+ return;
+ }
+
+ if (max_children < start_servers) {
+ layer.msg('start_servers 不能大于 max_children', { icon: 2 });
+ return;
+ }
+
+ if (max_children < 1 || start_servers < 1 || min_spare_servers < 1 || max_spare_servers < 1) {
+ layer.msg('配置值不能小于1', { icon: 2 });
+ return;
+ }
+
+ var data = {
+ version:version,
+ max_children:max_children,
+ start_servers:start_servers,
+ min_spare_servers:min_spare_servers,
+ max_spare_servers:max_spare_servers,
+ pm:pm,
+ pool:pool,
+ };
+ phpPost('set_fpm_conf', version, data, function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+
+function getFpmStatus(version, pool = 'www'){
+ phpPost('get_fpm_status', version, {'pool':pool}, function(ret_data){
+ var tmp_data = $.parseJSON(ret_data.data);
+ if(!tmp_data.status){
+ layer.msg(tmp_data.msg, { icon: tmp_data.status ? 1 : 2 });
+ return;
+ }
+
+ var rdata = tmp_data.data;
+ var php_fpm_status = '动态';
+ if (rdata['process manager'] == 'dynamic'){
+ php_fpm_status = '动态';
+ } else if(rdata['process manager'] == 'static'){
+ php_fpm_status = '静态';
+ } else if(rdata['process manager'] == 'ondemand'){
+ php_fpm_status = '按需';
+ }
+
+ var select_pool_www = '';
+ var select_pool_backup = '';
+ if (pool == 'www'){
+ select_pool_www = 'selected';
+ }
+ if (pool == 'backup'){
+ select_pool_backup = 'selected';
+ }
+
+ var con = "
\
+
\
+ \
+ www \
+ backup \
+ \
+
\
+
\
+ 应用池(pool) " + rdata.pool + " \
+ 进程管理方式(process manager) " + php_fpm_status + " \
+ 启动日期(start time) " + rdata['start time'] + " \
+ 请求数(accepted conn) " + rdata['accepted conn'] + " \
+ 请求队列(listen queue) " + rdata['listen queue'] + " \
+ 最大等待队列(max listen queue) " + rdata['max listen queue'] + " \
+ socket队列长度(listen queue len) " + rdata['listen queue len'] + " \
+ 空闲进程数量(idle processes) " + rdata['idle processes'] + " \
+ 活跃进程数量(active processes) " + rdata['active processes'] + " \
+ 总进程数量(total processes) " + rdata['total processes'] + " \
+ 最大活跃进程数量(max active processes) " + rdata['max active processes'] + " \
+ 到达进程上限次数(max children reached) " + rdata['max children reached'] + " \
+ 慢请求数量(slow requests) " + rdata['slow requests'] + " \
+
\
+
";
+ $(".soft-man-con").html(con);
+ $(".get_fpm_status td,.get_fpm_status th").css("padding", "7px");
+
+
+ $('select[name="pool"]').change(function(){
+ var pool = $(this).val();
+ getFpmStatus(version, pool);
+ });
+ });
+}
+
+
+function getSessionConfig(version){
+ phpPost('get_session_conf', version, '', function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ if(!rdata.status){
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ return;
+ }
+ var rdata = rdata.data;
+
+ var cacheList = "
files " +
+ "
redis " +
+ "
memcache " +
+ "
memcached ";
+
+
+ var info = rdata.save_path.split(":");
+ var con = "
" +
+ "
存储模式: " + cacheList + "
" +
+ "
IP地址:
" +
+ "
端口:
" +
+ "
密码:
" +
+ "
保存
" +
+ "
\
+
\
+ 若你的站点并发比较高,使用Redis,Memcache能有效提升PHP并发能力 \
+ 若调整Session模式后,网站访问异常,请切换回原来的模式 \
+ 切换Session模式会使在线的用户会话丢失,请在流量小的时候切换 \
+ \
+
\
+
\
+
";
+
+ $(".soft-man-con").html(con);
+
+ if (rdata.save_handler == 'files'){
+ $('input[name="ip"]').attr('disabled','disabled');
+ $('input[name="port"]').attr('disabled','disabled');
+ $('input[name="passwd"]').attr('placeholder','如果没有密码留空');
+ $('input[name="passwd"]').attr('disabled','disabled');
+ }
+
+ // change event
+ $("select[name='save_handler']").change(function() {
+ var type = $(this).val();
+
+ var passwd = $('input[name="passwd"]').val();
+ if (passwd == ""){
+ $('input[name="passwd"]').attr('placeholder','如果没有密码留空');
+ }
+
+ var ip = $('input[name="ip"]').val();
+ if (ip == ""){
+ $('input[name="ip"]').val('127.0.0.1');
+ }
+
+ switch (type) {
+ case 'redis':
+ var port = $('input[name="port"]').val();
+ if (port == ""){
+ $('input[name="port"]').val('6379');
+ }
+ $('input[name="ip"]').removeAttr('disabled');
+ $('input[name="port"]').removeAttr('disabled');
+ $('input[name="passwd"]').removeAttr('disabled');
+ break;
+ case 'files':
+ $('input[name="ip"]').val("").attr('disabled','disabled');
+ $('input[name="port"]').val("").attr('disabled','disabled');
+ $('input[name="passwd"]').val("").attr('disabled','disabled');
+ break;
+ case 'memcache':
+ var port = $('input[name="port"]').val();
+ if (port == ""){
+ $('input[name="port"]').val('11211');
+ }
+ $('input[name="ip"]').removeAttr('disabled');
+ $('input[name="port"]').removeAttr('disabled');
+ $('input[name="passwd"]').removeAttr('disabled');
+ break;
+ case 'memcached':
+ var port = $('input[name="port"]').val();
+ if (port == ""){
+ $('input[name="port"]').val('11211');
+ }
+ $('input[name="ip"]').removeAttr('disabled');
+ $('input[name="port"]').removeAttr('disabled');
+ $('input[name="passwd"]').removeAttr('disabled');
+ break;
+ }
+ });
+
+ //load session stats
+ phpPost('get_session_count', version, '', function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ if(!rdata.status){
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ return;
+ }
+ var rdata = rdata.data;
+
+ var html_var = "清理Session文件
\
+ \
+
\
+
总Session文件数量 "+rdata.total+"
\
+
可清理的Session文件数量 "+rdata.oldfile+"
\
+
\
+
清理session文件 ";
+
+ $("#session_clear").html(html_var);
+
+
+ $('#clean_func').click(function(){
+ phpPost('clean_session_old', version, '', function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ showMsg(rdata.msg,function(){
+ getSessionConfig(version);
+ },{ icon: rdata.status ? 1 : 2 });
+ });
+ });
+ });
+ });
+
+}
+
+function setSessionConfig(version){
+ var ip = $('input[name="ip"]').val();
+ var port = $('input[name="port"]').val();
+ var passwd = $('input[name="passwd"]').val();
+ var save_handler = $("select[name='save_handler']").val();
+ var data = {
+ ip:ip,
+ port:port,
+ passwd:passwd,
+ save_handler:save_handler,
+ };
+ phpPost('set_session_conf', version, data, function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+
+//禁用函数
+function disableFunc(version) {
+ phpPost('get_disable_func', version,'',function(data){
+ var rdata = $.parseJSON(data.data);
+ var disable_functions = rdata.disable_functions.split(',');
+ var dbody = ''
+ for (var i = 0; i < disable_functions.length; i++) {
+ if (disable_functions[i] == '') continue;
+ dbody += "
" + disable_functions[i] + " 删除 ";
+ }
+
+ var con = "
" +
+ " " +
+ "添加 " +
+ "
" +
+ "
" +
+ "名称 操作 " +
+ "" + dbody + " " +
+ "
";
+
+ con += '
\
+ 在此处可以禁用指定函数的调用,以增强环境安全性! \
+ 强烈建议禁用如exec,system等危险函数! \
+ ';
+
+ $(".soft-man-con").html(con);
+ });
+}
+//设置禁用函数
+function setDisableFunc(version, act, fs) {
+ var fsArr = fs.split(',');
+ if (act == 1) {
+ var functions = $("#disable_function_val").val();
+ for (var i = 0; i < fsArr.length; i++) {
+ if (functions == fsArr[i]) {
+ layer.msg(lan.soft.fun_msg, { icon: 5 });
+ return;
+ }
+ }
+ fs += ',' + functions;
+ msg = '添加成功';
+ } else {
+
+ fs = '';
+ for (var i = 0; i < fsArr.length; i++) {
+ if (act == fsArr[i]) continue;
+ fs += fsArr[i] + ','
+ }
+ msg = '删除成功';
+ fs = fs.substr(0, fs.length - 1);
+ }
+
+ var data = {
+ 'version':version,
+ 'disable_functions':fs,
+ };
+
+ phpPost('set_disable_func', version,data,function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.status ? msg : rdata.msg, function(){
+ disableFunc(version);
+ } ,{ icon: rdata.status ? 1 : 2 });
+ });
+}
+
+
+//phpinfo
+// function getPhpinfo(version) {
+// var con = '
查看phpinfo() ';
+// $(".soft-man-con").html(con);
+// }
+
+//获取PHPInfo
+function getPHPInfo_old(version) {
+ phpPost('get_phpinfo', version, '', function(data){
+ var rdata = data.data;
+ layer.open({
+ type: 1,
+ title: "PHP-" + version + "-PHPINFO",
+ area: ['90%', '90%'],
+ closeBtn: 2,
+ shadeClose: true,
+ content: rdata
+ });
+ });
+}
+
+function getPHPInfo(version) {
+ phpPostCallback('get_php_info', version, {}, function(data){
+ if (!data.status){
+ layer.msg(rdata.msg, { icon: 2 });
+ return;
+ }
+
+ layer.open({
+ type: 1,
+ title: "PHP-" + version + "-PHPINFO",
+ area: ['70%', '90%'],
+ closeBtn: 2,
+ shadeClose: true,
+ content: data.data.replace('a:link {color: #009; text-decoration: none; background-color: #fff;}', '').replace('a:link {color: #000099; text-decoration: none; background-color: #ffffff;}', '')
+ });
+ })
+}
+
+
+function phpLibConfig(version){
+
+ // phpPost('get_lib_conf', version, {}, function(rdata){
+ // var rdata = $.parseJSON(rdata.data);
+ // });
+
+ phpPostCallback('get_lib_conf', version, {}, function(rdata){
+ var rdata = rdata.data;
+
+ if (!rdata.status){
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ return;
+ }
+
+ var libs = rdata.data;
+ var body = '';
+ var opt = '';
+
+ for (var i = 0; i < libs.length; i++) {
+ if (libs[i].versions.indexOf(version) == -1){
+ continue;
+ }
+
+ if (libs[i]['task'] == '-1' && libs[i].phpversions.indexOf(version) != -1) {
+ opt = '
安装 '
+ } else if (libs[i]['task'] == '0' && libs[i].phpversions.indexOf(version) != -1) {
+ opt = '
等待安装... '
+ } else if (libs[i].status) {
+ opt = '
卸载 '
+ } else {
+ opt = '
安装 '
+ }
+
+ body += '
' +
+ '' + libs[i].name + ' ' +
+ '' + libs[i].type + ' ' +
+ '' + libs[i].msg + ' ' +
+ ' ' +
+ '' + opt + ' ' +
+ ' ';
+ }
+
+
+ var con = '
' +
+ '
' +
+ '' +
+ '' +
+ '名称 ' +
+ '类型 ' +
+ '说明 ' +
+ '状态 ' +
+ '操作 ' +
+ ' ' +
+ ' ' +
+ '' + body + ' ' +
+ '
' +
+ '
' +
+ '
\
+ 请按实际需求安装扩展,不要安装不必要的PHP扩展,这会影响PHP执行效率,甚至出现异常 \
+ Redis扩展只允许在1个PHP版本中使用,安装到其它PHP版本请在[软件管理]重装Redis \
+ opcache/xcache/apc等脚本缓存扩展,请只安装其中1个,否则可能导致您的站点程序异常 \
+ ioncube要在ZendGuardLoader/opcache前安装,否则可能导致您的站点程序异常 \
+ ';
+ $('.soft-man-con').html(con);
+ });
+
+}
+
+//安装扩展
+function installPHPLib(version, name, title, pathinfo) {
+ layer.confirm('您真的要安装{1}吗?'.replace('{1}', name), { icon: 3, closeBtn: 2 }, function() {
+ name = name.toLowerCase();
+ var data = "name=" + name + "&version=" + version + "&type=1";
+
+ phpPost('install_lib', version, data, function(data){
+ var rdata = $.parseJSON(data.data);
+ // layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ showMsg(rdata.msg, function(){
+ getTaskCount();
+ phpLibConfig(version);
+ },{ icon: rdata.status ? 1 : 2 });
+
+ });
+ });
+}
+
+//卸载扩展
+function uninstallPHPLib(version, name, title, pathinfo) {
+ layer.confirm('您真的要卸载{1}吗?'.replace('{1}', name), { icon: 3, closeBtn: 2 }, function() {
+ name = name.toLowerCase();
+ var data = 'name=' + name + '&version=' + version;
+ phpPost('uninstall_lib', version, data, function(data){
+ var rdata = $.parseJSON(data.data);
+ // layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ showMsg(rdata.msg, function(){
+ getTaskCount();
+ phpLibConfig(version);
+ },{ icon: rdata.status ? 1 : 2 },5000);
+
+ });
+ });
+}
\ No newline at end of file
diff --git a/plugins/php/lib/freetype_new.sh b/plugins/php/lib/freetype_new.sh
new file mode 100644
index 000000000..f68ab52b3
--- /dev/null
+++ b/plugins/php/lib/freetype_new.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+
+if [ ! -d ${SERVER_ROOT}/freetype ];then
+ cd $SOURCE_ROOT
+
+ if [ ! -f $SOURCE_ROOT/freetype-2.12.1.tar.gz ];then
+ wget -O freetype-2.12.1.tar.gz --no-check-certificate https://download.savannah.gnu.org/releases/freetype/freetype-2.12.1.tar.gz -T 5
+ fi
+
+ if [ ! -d $SOURCE_ROOT/freetype-2.12.1 ];then
+ tar zxvf freetype-2.12.1.tar.gz
+ cd freetype-2.12.1
+ else
+ cd freetype-2.12.1
+ fi
+
+ ./configure --prefix=${SERVER_ROOT}/freetype && make && make install
+ cd $SOURCE_ROOT && rm -rf freetype-2.12.1
+ #rm -rf freetype-2.12.1.tar.gz
+ cd $SOURCE_ROOT && rm -rf $SOURCE_ROOT/freetype-2.12.1
+
+fi
\ No newline at end of file
diff --git a/plugins/php/lib/freetype_old.sh b/plugins/php/lib/freetype_old.sh
new file mode 100644
index 000000000..e9c559ce2
--- /dev/null
+++ b/plugins/php/lib/freetype_old.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+
+if [ ! -d ${SERVER_ROOT}/freetype_old ];then
+ cd $SOURCE_ROOT
+
+ if [ ! -f $SOURCE_ROOT/freetype-2.7.1.tar.gz ];then
+ wget -O freetype-2.7.1.tar.gz --no-check-certificate https://download.savannah.gnu.org/releases/freetype/freetype-2.7.1.tar.gz -T 5
+ fi
+
+ if [ ! -d $SOURCE_ROOT/freetype-2.7.1 ];then
+ tar zxvf freetype-2.7.1.tar.gz
+ cd freetype-2.7.1
+ else
+ cd freetype-2.7.1
+ fi
+
+ ./configure --prefix=${SERVER_ROOT}/freetype_old && make && make install
+ cd $SOURCE_ROOT && rm -rf freetype-2.7.1
+ cd $SOURCE_ROOT && rm -rf $SOURCE_ROOT/freetype-2.7.1
+ #rm -rf freetype-2.7.1.tar.gz
+fi
\ No newline at end of file
diff --git a/plugins/php/lib/icu.sh b/plugins/php/lib/icu.sh
new file mode 100644
index 000000000..b635f5ff8
--- /dev/null
+++ b/plugins/php/lib/icu.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+
+HTTP_PREFIX="https://"
+LOCAL_ADDR=common
+cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
+if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ HTTP_PREFIX="https://mirror.ghproxy.com/"
+fi
+
+if [ ! -d ${SERVER_ROOT}/icu ];then
+
+ cd ${SOURCE_ROOT}
+
+ if [ "$LOCAL_ADDR" == 'cn' ];then
+ if [ ! -f ${SOURCE_ROOT}/icu4c-52_2-src.tgz ];then
+ wget --no-check-certificate -O ${SOURCE_ROOT}/icu4c-52_2-src.tgz https://dl.midoks.icu/lib/icu4c-52_2-src.tgz -T 20
+ fi
+ fi
+
+ if [ ! -f ${SOURCE_ROOT}/icu4c-52_2-src.tgz ];then
+ wget --no-check-certificate -O ${SOURCE_ROOT}/icu4c-52_2-src.tgz https://github.com/unicode-org/icu/releases/download/release-52-2/icu4c-52_2-src.tgz
+ fi
+
+ if [ ! -d ${SERVER_ROOT}/lib/icu/lib ];then
+ cd ${SOURCE_ROOT} && tar -zxvf icu4c-52_2-src.tgz
+
+ cd ${SOURCE_ROOT}/icu/source
+ ./runConfigureICU Linux --prefix=${SERVER_ROOT}/icu && make CXXFLAGS="-g -O2 -std=c++11" && make install
+
+ # export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/www/server/lib/icu/lib
+ if [ -d /etc/ld.so.conf.d ];then
+ echo "/www/server/lib/icu/lib" > /etc/ld.so.conf.d/mw-icu.conf
+ elif [ -f /etc/ld.so.conf ]; then
+ echo "/www/server/lib/icu/lib" >> /etc/ld.so.conf
+ fi
+
+ ldconfig
+
+ cd $SOURCE_ROOT && rm -rf ${SOURCE_ROOT}/icu
+ fi
+
+fi
\ No newline at end of file
diff --git a/plugins/php/lib/imagemagick.sh b/plugins/php/lib/imagemagick.sh
new file mode 100644
index 000000000..8e1d074e0
--- /dev/null
+++ b/plugins/php/lib/imagemagick.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+igmVersion="7.1.1-15"
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+
+if [ ! -d ${SERVER_ROOT}/ImageMagick ];then
+ cd ${SOURCE_ROOT}
+ if [ ! -f ${SOURCE_ROOT}/ImageMagick-${igmVersion}.tar.gz ];then
+ wget --no-check-certificate -O ImageMagick-${igmVersion}.tar.gz https://imagemagick.org/archive/ImageMagick-${igmVersion}.tar.gz -T 20
+ fi
+
+ tar -zxf ImageMagick-${igmVersion}.tar.gz
+ cd ImageMagick-${igmVersion}
+ ./configure --prefix=${SERVER_ROOT}/ImageMagick --disable-openmp
+ make && make install
+
+ if [ -d /etc/ld.so.conf.d ];then
+ echo "/www/server/lib/ImageMagick/lib" > /etc/ld.so.conf.d/ImageMagick.conf
+ elif [ -f /etc/ld.so.conf ]; then
+ echo "/www/server/lib/ImageMagick/lib" >> /etc/ld.so.conf
+ fi
+
+ ldconfig
+
+ cd $SOURCE_ROOT && rm -rf $SOURCE_ROOT/ImageMagick-${igmVersion}
+fi
+
+
diff --git a/plugins/php/lib/libedit.sh b/plugins/php/lib/libedit.sh
new file mode 100644
index 000000000..405ed0f4a
--- /dev/null
+++ b/plugins/php/lib/libedit.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+# cd /www/server/mdserver-web/plugins/php/lib && bash libedit.sh
+
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+
+
+# LOCAL_ADDR=common
+# cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
+# if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+# LOCAL_ADDR=cn
+# fi
+
+if [ ! -d ${SERVER_ROOT}/libedit ];then
+ cd $SOURCE_ROOT
+
+ VERSION="20230828-3.1"
+
+ if [ ! -f ${SOURCE_ROOT}/libedit-${VERSION}.tar.gz ];then
+ wget --no-check-certificate -O ${SOURCE_ROOT}/libedit-${VERSION}.tar.gz https://thrysoee.dk/editline/libedit-${VERSION}.tar.gz
+ fi
+
+ if [ ! -d ${SOURCE_ROOT}/libedit-${VERSION} ];then
+ cd $SOURCE_ROOT && tar -zxvf libedit-${VERSION}.tar.gz
+ fi
+
+ cd ${SOURCE_ROOT}/libedit-${VERSION}
+
+ ./configure --prefix=${SERVER_ROOT}/libedit && make && make install
+
+ if [ -d $SOURCE_ROOT/libedit-${VERSION} ];then
+ cd $SOURCE_ROOT && rm -rf $SOURCE_ROOT/libedit-${VERSION}
+ fi
+fi
\ No newline at end of file
diff --git a/plugins/php/lib/libiconv.sh b/plugins/php/lib/libiconv.sh
new file mode 100644
index 000000000..92d3735c0
--- /dev/null
+++ b/plugins/php/lib/libiconv.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+# cd /www/server/mdserver-web/plugins/php/lib && bash libiconv.sh
+
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+
+HTTP_PREFIX="https://"
+LOCAL_ADDR=common
+cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
+if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ HTTP_PREFIX="https://mirror.ghproxy.com/"
+fi
+
+if [ ! -d ${SERVER_ROOT}/libiconv ];then
+ cd $SOURCE_ROOT
+
+ if [ "$LOCAL_ADDR" == 'cn' ];then
+ if [ ! -f ${SOURCE_ROOT}/libiconv-1.15.tar.gz ];then
+ wget --no-check-certificate -O ${SOURCE_ROOT}/libiconv-1.15.tar.gz https://dl.midoks.icu/lib/libiconv-1.15.tar.gz -T 20
+ fi
+ fi
+
+ if [ ! -f ${SOURCE_ROOT}/libiconv-1.15.tar.gz ];then
+ wget --no-check-certificate -O ${SOURCE_ROOT}/libiconv-1.15.tar.gz https://github.com/midoks/mdserver-web/releases/download/init/libiconv-1.15.tar.gz -T 5
+ fi
+
+ if [ ! -d ${SOURCE_ROOT}/libiconv-1.15 ];then
+ cd $SOURCE_ROOT && tar -zxvf libiconv-1.15.tar.gz
+ fi
+
+ cd ${SOURCE_ROOT}/libiconv-1.15
+
+ ./configure --prefix=${SERVER_ROOT}/libiconv --enable-static && make && make install
+
+ if [ -d $SOURCE_ROOT/libiconv-1.15 ];then
+ cd $SOURCE_ROOT && rm -rf $SOURCE_ROOT/libiconv-1.15
+ fi
+fi
\ No newline at end of file
diff --git a/plugins/php/lib/libmcrypt.sh b/plugins/php/lib/libmcrypt.sh
new file mode 100644
index 000000000..57b9e6a48
--- /dev/null
+++ b/plugins/php/lib/libmcrypt.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+
+
+ISFIND="0"
+SYS_DIR=(/usr/local /usr)
+for S_DIR in ${SYS_DIR[@]}; do
+ if [ -f $S_DIR/include/mcrypt.h ];then
+ ISFIND="1"
+ fi
+done
+
+if [ $ISFIND == "0" ];then
+ cd $SOURCE_ROOT
+ if [ ! -f ${SOURCE_ROOT}/libmcrypt-2.5.8.tar.gz ];then
+ wget --no-check-certificate -O libmcrypt-2.5.8.tar.gz https://sourceforge.net/projects/mcrypt/files/Libmcrypt/2.5.8/libmcrypt-2.5.8.tar.gz -T 20
+ fi
+
+ tar -zxvf libmcrypt-2.5.8.tar.gz
+ cd libmcrypt-2.5.8
+ ./configure && make && make install && make clean
+
+ cd $SOURCE_ROOT && rm -rf $SOURCE_ROOT/libmcrypt-2.5.8
+fi
+
+
+# if [ ! -d ${SERVER_ROOT}/libmcrypt ];then
+
+# cd $SOURCE_ROOT
+# if [ ! -f ${SOURCE_ROOT}/libmcrypt-2.5.8.tar.gz ];then
+# wget -O libmcrypt-2.5.8.tar.gz --no-check-certificate https://sourceforge.net/projects/mcrypt/files/Libmcrypt/2.5.8/libmcrypt-2.5.8.tar.gz -T 20
+# fi
+
+# tar -zxvf libmcrypt-2.5.8.tar.gz
+# cd libmcrypt-2.5.8
+
+# ./configure --prefix=${SERVER_ROOT}/libmcrypt && make && make install
+# fi
\ No newline at end of file
diff --git a/plugins/php/lib/libmemcached.sh b/plugins/php/lib/libmemcached.sh
new file mode 100644
index 000000000..8352be814
--- /dev/null
+++ b/plugins/php/lib/libmemcached.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+
+#----------------------------- libmemcached start -------------------------#
+# if [ ! -d ${SERVER_ROOT}/libmemcached ];then
+# cd ${SOURCE_ROOT}
+# if [ ! -f ${SOURCE_ROOT}/libmemcached-1.0.4.tar.gz ];then
+# wget -O libmemcached-1.0.4.tar.gz https://launchpad.net/libmemcached/1.0/1.0.4/+download/libmemcached-1.0.4.tar.gz -T 20
+# fi
+# tar -zxf libmemcached-1.0.4.tar.gz
+# cd libmemcached-1.0.4
+# ./configure --prefix=${SERVER_ROOT}/libmemcached -with-memcached && make && make install
+# fi
+#----------------------------- libmemcached end -------------------------#
+
+
+#----------------------------- libmemcached start -------------------------#
+if [ ! -d ${SERVER_ROOT}/libmemcached ];then
+ cd ${SOURCE_ROOT}
+ if [ ! -f ${SOURCE_ROOT}/libmemcached-1.0.18.tar.gz ];then
+ wget --no-check-certificate -O libmemcached-1.0.18.tar.gz https://launchpad.net/libmemcached/1.0/1.0.18/+download/libmemcached-1.0.18.tar.gz -T 20
+ fi
+ tar -zxf libmemcached-1.0.18.tar.gz
+ cd libmemcached-1.0.18
+
+ # sed -i '_bak' "41,52s#opt_servers == false#opt_servers#g" ${SERVER_ROOT}/libmemcached-1.0.18/clients/memflush.cc
+ sed -i "s#opt_servers == false#\!opt_servers#g" ${SERVER_ROOT}/libmemcached-1.0.18/clients/memflush.cc
+ # sed -i "s#opt_servers == false#\!opt_servers#g" /www/server/source/lib/libmemcached-1.0.18/clients/memflush.cc
+ ./configure --prefix=${SERVER_ROOT}/libmemcached -with-memcached && make && make install
+
+ cd $SOURCE_ROOT && rm -rf $SOURCE_ROOT/libmemcached-1.0.18
+
+fi
+#----------------------------- libmemcached end -------------------------#
\ No newline at end of file
diff --git a/plugins/php/lib/libsodium.sh b/plugins/php/lib/libsodium.sh
new file mode 100644
index 000000000..6c2b389f2
--- /dev/null
+++ b/plugins/php/lib/libsodium.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+
+
+VERSION=1.0.18
+#----------------------------- libsodium start -------------------------#
+if [ ! -f /usr/local/lib/libsodium.so ];then
+ cd ${SOURCE_ROOT}
+ if [ ! -f ${SOURCE_ROOT}/libsodium-${VERSION}-stable.tar.gz ];then
+ # wget --no-check-certificate -O libsodium-1.0.18-stable.tar.gz https://download.libsodium.org/libsodium/releases/libsodium-1.0.18-stable.tar.gz -T 20
+ wget --no-check-certificate -O libsodium-${VERSION}-stable.tar.gz https://download.libsodium.org/libsodium/releases/libsodium-${VERSION}-stable.tar.gz -T 20
+ fi
+ tar -zxvf libsodium-${VERSION}-stable.tar.gz
+ cd libsodium-stable
+ ./configure && make && make check && make install
+
+ cd $SOURCE_ROOT && rm -rf $SOURCE_ROOT/libsodium-stable
+fi
+#----------------------------- libsodium end -------------------------#
\ No newline at end of file
diff --git a/plugins/php/lib/libzip.sh b/plugins/php/lib/libzip.sh
new file mode 100644
index 000000000..01d80b248
--- /dev/null
+++ b/plugins/php/lib/libzip.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+
+mkdir -p $SOURCE_ROOT
+
+HTTP_PREFIX="https://"
+LOCAL_ADDR=common
+cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
+if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ HTTP_PREFIX="https://mirror.ghproxy.com/"
+fi
+# HTTP_PREFIX="https://"
+
+if [ ! -d ${SERVER_ROOT}/libzip ];then
+
+ cd $SOURCE_ROOT
+
+ if [ "$LOCAL_ADDR" == 'cn' ];then
+ if [ ! -f ${SOURCE_ROOT}/libzip-1.3.2.tar.gz ];then
+ wget --no-check-certificate -O libzip-1.3.2.tar.gz https://dl.midoks.icu/lib/libzip-1.3.2.tar.gz -T 20
+ fi
+ fi
+
+ # if [ ! -f ${SOURCE_ROOT}/libzip-1.3.2.tar.gz ];then
+ # wget --no-check-certificate -O libzip-1.3.2.tar.gz ${HTTP_PREFIX}github.com/midoks/mdserver-web/releases/download/init/libzip-1.3.2.tar.gz -T 20
+ # fi
+
+ if [ ! -f ${SOURCE_ROOT}/libzip-1.3.2.tar.gz ];then
+ wget --no-check-certificate -O libzip-1.3.2.tar.gz https://github.com/midoks/mdserver-web/releases/download/init/libzip-1.3.2.tar.gz -T 20
+ fi
+
+ if [ ! -d ${SOURCE_ROOT}/libzip-1.3.2 ];then
+ cd $SOURCE_ROOT && tar -zxvf libzip-1.3.2.tar.gz
+ fi
+
+ cd ${SOURCE_ROOT}/libzip-1.3.2
+
+ ./configure --prefix=${SERVER_ROOT}/libzip && make && make install
+ #cd $SOURCE_ROOT
+
+ if [ "$?" == "0" ];then
+ rm -rf ${SOURCE_ROOT}/libzip-1.3.2
+ rm -rf ${SOURCE_ROOT}/libzip-1.3.2.tar.gz
+ fi
+
+fi
\ No newline at end of file
diff --git a/plugins/php/lib/oniguruma.sh b/plugins/php/lib/oniguruma.sh
new file mode 100644
index 000000000..33c8ab0fb
--- /dev/null
+++ b/plugins/php/lib/oniguruma.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+
+HTTP_PREFIX="https://"
+cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
+if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ HTTP_PREFIX="https://mirror.ghproxy.com/"
+fi
+
+which onig-config
+if [ "$?" != "0" ];then
+ cd ${SOURCE_ROOT}
+ if [ ! -f ${SOURCE_ROOT}/oniguruma-6.9.4.tar.gz ];then
+ wget --no-check-certificate -O ${SOURCE_ROOT}/oniguruma-6.9.4.tar.gz ${HTTP_PREFIX}github.com/kkos/oniguruma/archive/v6.9.4.tar.gz
+ fi
+
+ if [ ! -d cd ${SOURCE_ROOT}/oniguruma-6.9.4 ];then
+ cd ${SOURCE_ROOT} && tar -zxvf oniguruma-6.9.4.tar.gz
+ fi
+
+ cd ${SOURCE_ROOT}/oniguruma-6.9.4 && ./autogen.sh && ./configure --prefix=/usr && make && make install
+ cd $SOURCE_ROOT && rm -rf $SOURCE_ROOT/oniguruma-6.9.4
+fi
+
diff --git a/plugins/php/lib/openssl.sh b/plugins/php/lib/openssl.sh
new file mode 100644
index 000000000..b5d635d01
--- /dev/null
+++ b/plugins/php/lib/openssl.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+opensslVersion="3.5.2"
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+mkdir -p $SOURCE_ROOT
+
+if [ ! -d ${SERVER_ROOT}/openssl ];then
+ cd ${SOURCE_ROOT}
+ if [ ! -f ${SOURCE_ROOT}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${SOURCE_ROOT}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+ tar -zxvf openssl-${opensslVersion}.tar.gz
+ cd openssl-${opensslVersion}
+ ./config --prefix=${SERVER_ROOT}/openssl zlib-dynamic shared
+ make && make install
+
+ cd $SOURCE_ROOT && rm -rf $SOURCE_ROOT/openssl-${opensslVersion}
+fi
+
diff --git a/plugins/php/lib/openssl_10.sh b/plugins/php/lib/openssl_10.sh
new file mode 100644
index 000000000..d369d4f25
--- /dev/null
+++ b/plugins/php/lib/openssl_10.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+opensslVersion="1.0.2q"
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+
+HTTP_PREFIX="https://"
+LOCAL_ADDR=common
+cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
+if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ HTTP_PREFIX="https://mirror.ghproxy.com/"
+fi
+
+if [ ! -d ${SERVER_ROOT}/openssl10 ];then
+ cd ${SOURCE_ROOT}
+
+ if [ "$LOCAL_ADDR" == 'cn' ];then
+ if [ ! -f ${SOURCE_ROOT}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O openssl-${opensslVersion}.tar.gz https://dl.midoks.icu/lib/openssl-${opensslVersion}.tar.gz -T 20
+ fi
+ fi
+
+ # if [ ! -f ${SOURCE_ROOT}/openssl-${opensslVersion}.tar.gz ];then
+ # wget --no-check-certificate ${HTTP_PREFIX}/midoks/mdserver-web/releases/download/init/openssl-${opensslVersion}.tar.gz -T 20
+ # fi
+
+ if [ ! -f ${SOURCE_ROOT}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O openssl-${opensslVersion}.tar.gz https://github.com/midoks/mdserver-web/releases/download/init/openssl-${opensslVersion}.tar.gz -T 20
+ fi
+
+ tar -zxf openssl-${opensslVersion}.tar.gz
+ cd openssl-${opensslVersion}
+ ./config --openssldir=${SERVER_ROOT}/openssl10 zlib-dynamic shared
+ make && make install
+
+ # export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/www/server/lib/openssl10/lib
+ if [ -d /etc/ld.so.conf.d ];then
+ echo "/www/server/lib/openssl10/lib" > /etc/ld.so.conf.d/openssl10.conf
+ elif [ -f /etc/ld.so.conf ]; then
+ echo "/www/server/lib/openssl10/lib" >> /etc/ld.so.conf
+ fi
+
+ ldconfig
+ # ldconfig -p | grep openssl
+
+ cd $SOURCE_ROOT && rm -rf $SOURCE_ROOT/openssl-${opensslVersion}
+fi
+
+
diff --git a/plugins/php/lib/openssl_11.sh b/plugins/php/lib/openssl_11.sh
new file mode 100644
index 000000000..09655ab85
--- /dev/null
+++ b/plugins/php/lib/openssl_11.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+opensslVersion="1.1.1p"
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+mkdir -p $SOURCE_ROOT
+
+if [ ! -d ${SERVER_ROOT}/openssl11 ];then
+ cd ${SOURCE_ROOT}
+ if [ ! -f ${SOURCE_ROOT}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${SOURCE_ROOT}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+ tar -zxvf openssl-${opensslVersion}.tar.gz
+ cd openssl-${opensslVersion}
+ ./config --prefix=${SERVER_ROOT}/openssl11 zlib-dynamic shared
+ make && make install
+
+ # export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/www/server/lib/openssl11/lib
+ if [ -d /etc/ld.so.conf.d ];then
+ echo "/www/server/lib/openssl11/lib" > /etc/ld.so.conf.d/openssl11.conf
+ elif [ -f /etc/ld.so.conf ]; then
+ echo "/www/server/lib/openssl11/lib" >> /etc/ld.so.conf
+ fi
+
+ ldconfig
+ # ldconfig -p | grep openssl
+
+ cd $SOURCE_ROOT && rm -rf $SOURCE_ROOT/openssl-${opensslVersion}
+fi
+
diff --git a/plugins/php/lib/openssl_30.sh b/plugins/php/lib/openssl_30.sh
new file mode 100644
index 000000000..9bd748954
--- /dev/null
+++ b/plugins/php/lib/openssl_30.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+opensslVersion="3.0.10"
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+mkdir -p $SOURCE_ROOT
+
+if [ ! -d ${SERVER_ROOT}/openssl30 ];then
+ cd ${SOURCE_ROOT}
+ if [ ! -f ${SOURCE_ROOT}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${SOURCE_ROOT}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+ tar -zxvf openssl-${opensslVersion}.tar.gz
+ cd openssl-${opensslVersion}
+ ./config --prefix=${SERVER_ROOT}/openssl30 zlib-dynamic shared
+ make && make install
+
+
+ # export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/www/server/lib/openssl30/lib
+ # if [ -d /etc/ld.so.conf.d ];then
+ # echo "/www/server/lib/openssl30/lib64" > /etc/ld.so.conf.d/openssl30.conf
+ # elif [ -f /etc/ld.so.conf ]; then
+ # echo "/www/server/lib/openssl30/lib64" >> /etc/ld.so.conf
+ # fi
+
+ ldconfig
+ # ldconfig -p | grep openssl
+
+ cd $SOURCE_ROOT && rm -rf $SOURCE_ROOT/openssl-${opensslVersion}
+fi
+
diff --git a/plugins/php/lib/openssl_35.sh b/plugins/php/lib/openssl_35.sh
new file mode 100644
index 000000000..7a4e619e9
--- /dev/null
+++ b/plugins/php/lib/openssl_35.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+opensslVersion="3.5.2"
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+mkdir -p $SOURCE_ROOT
+
+if [ ! -d ${SERVER_ROOT}/openssl35 ];then
+ cd ${SOURCE_ROOT}
+ if [ ! -f ${SOURCE_ROOT}/openssl-${opensslVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${SOURCE_ROOT}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz
+ fi
+ tar -zxvf openssl-${opensslVersion}.tar.gz
+ cd openssl-${opensslVersion}
+ ./config --prefix=${SERVER_ROOT}/openssl35 zlib-dynamic shared
+ make && make install
+
+
+ # export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/www/server/lib/openssl35/lib
+ # if [ -d /etc/ld.so.conf.d ];then
+ # echo "/www/server/lib/openssl35/lib64" > /etc/ld.so.conf.d/openssl35.conf
+ # elif [ -f /etc/ld.so.conf ]; then
+ # echo "/www/server/lib/openssl35/lib64" >> /etc/ld.so.conf
+ # fi
+
+ # ldconfig
+ # ldconfig -p | grep openssl
+
+ cd $SOURCE_ROOT && rm -rf $SOURCE_ROOT/openssl-${opensslVersion}
+fi
+
diff --git a/plugins/php/lib/pcre.sh b/plugins/php/lib/pcre.sh
new file mode 100644
index 000000000..96f6033fb
--- /dev/null
+++ b/plugins/php/lib/pcre.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+mkdir -p $SOURCE_ROOT
+
+pcreVersion='8.38'
+
+if [ ! -d ${SERVER_ROOT}/pcre ];then
+ cd ${SOURCE_ROOT}
+
+ if [ ! -f ${SOURCE_ROOT}/pcre-${pcreVersion}.tar.gz ];then
+ wget --no-check-certificate -O ${SOURCE_ROOT}/pcre-${pcreVersion}.tar.gz https://netix.dl.sourceforge.net/project/pcre/pcre/${pcreVersion}/pcre-${pcreVersion}.tar.gz
+ fi
+
+
+ if [ ! -d ${SOURCE_ROOT}/pcre-${pcreVersion} ];then
+ cd ${SOURCE_ROOT} && tar -zxvf pcre-${pcreVersion}.tar.gz
+
+ fi
+
+ cd ${SOURCE_ROOT}/pcre-${pcreVersion}
+ ./configure --prefix=${SERVER_ROOT}/pcre
+ make && make install
+
+ cd $SOURCE_ROOT && rm -rf ${SOURCE_ROOT}/pcre-${pcreVersion}
+fi
+
diff --git a/plugins/php/lib/zlib.sh b/plugins/php/lib/zlib.sh
new file mode 100644
index 000000000..239e4d7c4
--- /dev/null
+++ b/plugins/php/lib/zlib.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+# echo $rootPath
+
+SERVER_ROOT=$rootPath/lib
+SOURCE_ROOT=$rootPath/source/lib
+
+mkdir -p $SOURCE_ROOT
+
+HTTP_PREFIX="https://"
+LOCAL_ADDR=common
+cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
+if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ HTTP_PREFIX="https://mirror.ghproxy.com/"
+fi
+# HTTP_PREFIX="https://"
+
+if [ ! -d ${SERVER_ROOT}/zlib ];then
+
+ cd $SOURCE_ROOT
+
+ if [ "$LOCAL_ADDR" == 'cn' ];then
+ if [ ! -f ${SOURCE_ROOT}/${SOURCE_ROOT}/zlib-1.2.11.tar.gz ];then
+ wget --no-check-certificate -O ${SOURCE_ROOT}/zlib-1.2.11.tar.gz https://dl.midoks.icu/lib/zlib-1.2.11.tar.gz -T 20
+ fi
+ fi
+
+ # if [ ! -f ${SOURCE_ROOT}/zlib-1.2.11.tar.gz ];then
+ # wget --no-check-certificate -O ${SOURCE_ROOT}/zlib-1.2.11.tar.gz ${HTTP_PREFIX}github.com/madler/zlib/archive/v1.2.11.tar.gz -T 20
+ # fi
+
+ if [ ! -f ${SOURCE_ROOT}/zlib-1.2.11.tar.gz ];then
+ wget --no-check-certificate -O ${SOURCE_ROOT}/zlib-1.2.11.tar.gz https://github.com/madler/zlib/archive/v1.2.11.tar.gz -T 20
+ fi
+
+ if [ ! -d ${SOURCE_ROOT}/zlib-1.2.11 ];then
+ cd $SOURCE_ROOT && tar -zxvf zlib-1.2.11.tar.gz
+ fi
+ cd ${SOURCE_ROOT}/zlib-1.2.11
+
+ ./configure --prefix=${SERVER_ROOT}/zlib && make && make install
+
+ cd $SOURCE_ROOT && rm -rf $SOURCE_ROOT/zlib-1.2.11
+ #rm -rf zlib-1.2.11
+ #rm -rf zlib-1.2.11.tar.gz
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/52/eaccelerator.sh b/plugins/php/versions/52/eaccelerator.sh
new file mode 100755
index 000000000..edeb19619
--- /dev/null
+++ b/plugins/php/versions/52/eaccelerator.sh
@@ -0,0 +1,115 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# php 5.2.17 + eaccelerator 0.9.5.3
+# php 5.3.24 + eaccelerator 0.9.6.1
+# php 5.4.14 + eaccelerator 1.0 dev
+
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+LIBNAME=eaccelerator
+LIBV=0.9.6
+sysName=`uname`
+actionType=$1
+version=$2
+
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/lib/php/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/lib/php/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ wget -O $php_lib/${LIBNAME}-${LIBV}.tar.gz https://github.com/eaccelerator/eaccelerator/archive/${LIBV}.tar.gz
+ # wget -O $php_lib/${LIBNAME}-${LIBV}.tar.bz2 http://dl.wdlinux.cn:5180/soft/eaccelerator-0.9.6.1.tar.bz2
+ cd $php_lib && tar -zxvf ${LIBNAME}-${LIBV}.tar.gz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config \
+ --enable-eaccelerator=shared
+ make && make install && make clean
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ EA_DIR=/tmp/eaccelerator
+ mkdir -p $EA_DIR
+ chmod 777 -R $EA_DIR
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+ echo "${LIBNAME}.enable=1" >> $serverPath/php/$version/etc/php.ini
+ echo "${LIBNAME}.optimizer=1" >> $serverPath/php/$version/etc/php.ini
+ echo "${LIBNAME}.shm_size=64" >> $serverPath/php/$version/etc/php.ini
+ echo "${LIBNAME}.cache_dir=${EA_DIR}" >> $serverPath/php/$version/etc/php.ini
+ echo "${LIBNAME}.allowed_admin_path=/www/wwwroot/you_project_dir" >> $serverPath/php/$version/etc/php.ini
+
+
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$vphp not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/52/gd.sh b/plugins/php/versions/52/gd.sh
new file mode 100755
index 000000000..109e5806e
--- /dev/null
+++ b/plugins/php/versions/52/gd.sh
@@ -0,0 +1,137 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+LIBNAME=gd
+LIBV=0
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/lib/php/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/lib/php/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+
+sysName=`uname`
+echo "use system: ${sysName}"
+
+if [ ${sysName} == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eqi "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release; then
+ OSNAME='centos'
+elif grep -Eqi "Fedora" /etc/issue || grep -Eq "Fedora" /etc/*-release; then
+ OSNAME='fedora'
+elif grep -Eqi "Debian" /etc/issue || grep -Eq "Debian" /etc/*-release; then
+ OSNAME='debian'
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/*-release; then
+ OSNAME='ubuntu'
+elif grep -Eqi "Raspbian" /etc/issue || grep -Eq "Raspbian" /etc/*-release; then
+ OSNAME='raspbian'
+else
+ OSNAME='unknow'
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+
+ # cp -frp /usr/lib64/libldap* /usr/lib/
+
+ # ln -s /usr/lib64/libjpeg.so /usr/lib/libjpeg.so
+ # ln -s /usr/lib64/libpng.so /usr/lib/libpng.so
+
+ if [ ! -f /usr/lib/libjpeg.so ];then
+ ln -s /usr/lib64/libjpeg.so /usr/lib/libjpeg.so
+ fi
+
+ if [ ! -f /usr/lib/libpng.so ];then
+ ln -s /usr/lib64/libpng.so /usr/lib/libpng.so
+ fi
+
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash install.sh install ${version}
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config \
+ --with-gd \
+ --with-jpeg-dir=/usr/lib \
+ --with-freetype-dir=${serverPath}/lib/freetype_old \
+ --enable-gd-native-ttf
+
+ make clean && make && make install && make clean
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/52/install.sh b/plugins/php/versions/52/install.sh
new file mode 100755
index 000000000..caee453a0
--- /dev/null
+++ b/plugins/php/versions/52/install.sh
@@ -0,0 +1,163 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+SYS_ARCH=`arch`
+
+version=5.2.17
+PHP_VER=52
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-${version} ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.gz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.gz https://museum.php.net/php5/php-${version}.tar.gz
+ fi
+
+ if [ ! -f $sourcePath/php/php-5.2.17-fpm-0.5.14.diff.gz ]; then
+ wget --no-check-certificate -O $sourcePath/php/php-5.2.17-fpm-0.5.14.diff.gz http://php-fpm.org/downloads/php-5.2.17-fpm-0.5.14.diff.gz
+ fi
+
+
+ if [ ! -f $sourcePath/php/php-5.2.17-max-input-vars.patch ]; then
+ wget --no-check-certificate -O $sourcePath/php/php-5.2.17-max-input-vars.patch https://raw.github.com/laruence/laruence.github.com/master/php-5.2-max-input-vars/php-5.2.17-max-input-vars.patch
+ fi
+
+ if [ ! -f $sourcePath/php/php-5.x.x.patch ]; then
+ wget --no-check-certificate -O $sourcePath/php/php-5.x.x.patch https://mail.gnome.org/archives/xml/2012-August/txtbgxGXAvz4N.txt
+ fi
+
+
+ cd $sourcePath/php && tar -zxvf $sourcePath/php/php-${version}.tar.gz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+
+
+ cd $sourcePath/php
+ gzip -cd php-5.2.17-fpm-0.5.14.diff.gz | patch -d php${PHP_VER} -p1
+ cd $sourcePath/php/php${PHP_VER}
+ patch -p1 < ../php-5.2.17-max-input-vars.patch
+ patch -p0 -b < ../php-5.x.x.patch
+ sed -i "s/\!png_check_sig (sig, 8)/png_sig_cmp (sig, 0, 8)/" ext/gd/libgd/gd_png.c
+fi
+
+
+if [ -f $serverPath/php/${PHP_VER}/bin/php.dSYM ];then
+ mv $serverPath/php/${PHP_VER}/bin/php.dSYM $serverPath/php/${PHP_VER}/bin/php
+fi
+
+if [ -f $serverPath/php/${PHP_VER}/sbin/php-fpm.dSYM ];then
+ mv $serverPath/php/${PHP_VER}/sbin/php-fpm.dSYM $serverPath/php/${PHP_VER}/sbin/php-fpm
+fi
+
+
+if [ -f $serverPath/php/${PHP_VER}/bin/php ];then
+ return
+fi
+
+OPTIONS='--without-iconv'
+if [ $sysName == 'Darwin' ]; then
+ OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype"
+fi
+
+IS_64BIT=`getconf LONG_BIT`
+if [ "$IS_64BIT" == "64" ];then
+ OPTIONS="${OPTIONS} --with-libdir=lib64"
+fi
+
+if [ "${SYS_ARCH}" == "aarch64" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+fi
+
+if [ ! -d $serverPath/php/${PHP_VER} ];then
+
+ export MYSQL_LIB_DIR=/usr/lib64/mysql
+
+ cd $sourcePath/php/php${PHP_VER} && ./configure \
+ --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \
+ --enable-xml \
+ --enable-shared \
+ --with-mysql=mysqlnd \
+ --enable-embedded-mysqli=shared \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ $OPTIONS \
+ --enable-fastcgi \
+ --enable-fpm
+ # ZEND_EXTRA_LIBS='-liconv'
+ make && make install && make clean
+fi
+
+if [ "$?" != "0" ];then
+ echo "install fail!!"
+ rm -rf $sourcePath/php/php${PHP_VER}
+ exit 2
+fi
+
+
+if [ -f $serverPath/php/${PHP_VER}/bin/php.dSYM ];then
+ mv $serverPath/php/${PHP_VER}/bin/php.dSYM $serverPath/php/${PHP_VER}/bin/php
+fi
+
+if [ -f $serverPath/php/${PHP_VER}/sbin/php-fpm.dSYM ];then
+ mv $serverPath/php/${PHP_VER}/sbin/php-fpm.dSYM $serverPath/php/${PHP_VER}/sbin/php-fpm
+fi
+
+if [ ! -d $serverPath/php/${PHP_VER}/lib/php/extensions/no-debug-non-zts-20060613 ]; then
+ mkdir -p $serverPath/php/${PHP_VER}/lib/php/extensions/no-debug-non-zts-20060613
+fi
+
+# ps -ef|grep php/52 |grep -v grep |awk '{print $2}'|xargs kill
+# /www/server/php/init.d/php52 start
+# /www/server/php/52/sbin/php-fpm start
+mkdir -p $serverPath/php/${PHP_VER}/var/log
+mkdir -p $serverPath/php/${PHP_VER}/var/run
+
+#------------------------ install end ------------------------------------#
+}
+
+
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php${PHP_VER} stop
+ rm -rf $serverPath/php/${PHP_VER}
+ echo "uninstall php-${version} ..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/52/intl.sh b/plugins/php/versions/52/intl.sh
new file mode 100755
index 000000000..803fbeba9
--- /dev/null
+++ b/plugins/php/versions/52/intl.sh
@@ -0,0 +1,100 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+LIBNAME=intl
+LIBV=3.0.0
+sysName=`uname`
+actionType=$1
+version=$2
+
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/lib/php/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/lib/php/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ cd $rootPath/plugins/php/lib && /bin/bash icu.sh
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ cd ${LIBNAME}-${LIBV}
+ $serverPath/php/$version/bin/phpize
+
+ # --with-icu-dir=${serverPath}/lib/icu
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config
+ make clean && make && make install && make clean
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$vphp not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/52/memcache.sh b/plugins/php/versions/52/memcache.sh
new file mode 100755
index 000000000..f8cd093be
--- /dev/null
+++ b/plugins/php/versions/52/memcache.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+LIBNAME=memcache
+LIBV=2.2.7
+sysName=`uname`
+actionType=$1
+version=$2
+
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/lib/php/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/lib/php/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ cd ${LIBNAME}-${LIBV}
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config --enable-memcache --with-zlib-dir
+ make && make install && make clean
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install memcache, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/52/zendoptimizer.sh b/plugins/php/versions/52/zendoptimizer.sh
new file mode 100755
index 000000000..787a3e498
--- /dev/null
+++ b/plugins/php/versions/52/zendoptimizer.sh
@@ -0,0 +1,104 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+LIBNAME=ZendGuardLoader
+
+sysName=`uname`
+actionType=$1
+version=$2
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/lib/php/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/lib/php/extensions/${NON_ZTS_FILENAME}/ZendOptimizer.so
+
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "ZendOptimizer.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ $sysName == 'Darwin' ]; then
+ wget -O $php_lib/zend-loader-php5.3.tar.gz http://downloads.zend.com/guard/5.5.0/ZendGuardLoader-php-5.3-darwin-i386.tar.gz
+ else
+ wget -O $php_lib/ZendOptimizer-3.3.3-linux-glibc23-x86_64.tar.gz http://downloads.zend.com/optimizer/3.3.3/ZendOptimizer-3.3.3-linux-glibc23-x86_64.tar.gz
+ fi
+
+ cd $php_lib && tar zxvf ZendOptimizer-3.3.3-linux-glibc23-x86_64.tar.gz
+ cd $php_lib/ZendOptimizer-3.3.3-linux-glibc23-x86_64
+ cp $php_lib/ZendOptimizer-3.3.3-linux-glibc23-x86_64/data/5_2_x_comp/ZendOptimizer.so $serverPath/php/$version/lib/php/extensions/no-debug-non-zts-20060613/
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[Zend ZendGuard Loader]" >> $serverPath/php/$version/etc/php.ini
+ echo "zend_extension=$serverPath/php/$version/lib/php/extensions/no-debug-non-zts-20060613/ZendOptimizer.so" >> $serverPath/php/$version/etc/php.ini
+ echo "zend_loader.enable=1" >> $serverPath/php/$version/etc/php.ini
+ echo "zend_loader.disable_licensing=0" >> $serverPath/php/$version/etc/php.ini
+ echo "zend_loader.obfuscation_level_support=3" >> $serverPath/php/$version/etc/php.ini
+ echo "zend_loader.license_path=" >> $serverPath/php/$version/etc/php.ini
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ sed -i $BAK "/ZendOptimizer.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/zend_loader/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/\[Zend ZendGuard Loader\]/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/53/apc.sh b/plugins/php/versions/53/apc.sh
new file mode 100755
index 000000000..a34967930
--- /dev/null
+++ b/plugins/php/versions/53/apc.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=apc
+LIBV=3.1.9
+sysName=`uname`
+actionType=$1
+version=$2
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/lib/php/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/lib/php/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ _LIBNAME=$(echo $LIBNAME | tr '[a-z]' '[A-Z]')
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${_LIBNAME}-${LIBV} ];then
+ wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${_LIBNAME}-${LIBV}.tgz
+ cd $php_lib
+ tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${_LIBNAME}-${LIBV}
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS && \
+ make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${_LIBNAME}-${LIBV}
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ echo $extFile
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/53/eaccelerator.sh b/plugins/php/versions/53/eaccelerator.sh
new file mode 100755
index 000000000..027e70147
--- /dev/null
+++ b/plugins/php/versions/53/eaccelerator.sh
@@ -0,0 +1,109 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# php 5.2.17 + eaccelerator 0.9.5.3
+# php 5.3.24 + eaccelerator 0.9.6.1
+# php 5.4.14 + eaccelerator 1.0 dev
+
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=eaccelerator
+LIBV=0.9.6
+sysName=`uname`
+actionType=$1
+version=$2
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/lib/php/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/lib/php/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ wget -O $php_lib/${LIBNAME}-${LIBV}.tar.gz https://github.com/eaccelerator/eaccelerator/archive/${LIBV}.tar.gz
+ # wget -O $php_lib/${LIBNAME}-${LIBV}.tar.bz2 http://dl.wdlinux.cn:5180/soft/eaccelerator-0.9.6.1.tar.bz2
+ cd $php_lib && tar -zxvf ${LIBNAME}-${LIBV}.tar.gz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config \
+ $OPTIONS \
+ --enable-eaccelerator=shared
+ make && make install && make clean
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$vphp not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/53/install.sh b/plugins/php/versions/53/install.sh
new file mode 100755
index 000000000..feb326607
--- /dev/null
+++ b/plugins/php/versions/53/install.sh
@@ -0,0 +1,172 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+SYS_ARCH=`arch`
+
+version=5.3.29
+PHP_VER=53
+md5_file_ok=dcff9c881fe436708c141cfc56358075
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-5.3.29 ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://museum.php.net/php5/php-${version}.tar.xz
+ fi
+
+ #检测文件是否损坏.
+ if [ -f $sourcePath/php/php-${version}.tar.xz ];then
+ md5_file=`md5sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'`
+ if [ "${md5_file}" != "${md5_file_ok}" ]; then
+ echo "PHP${version} 下载文件不完整,重新安装"
+ rm -rf $sourcePath/php/php-${version}.tar.xz
+ fi
+ fi
+
+ cd $sourcePath/php && tar -Jxf $sourcePath/php/php-${version}.tar.xz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+fi
+
+
+if [ -f $serverPath/php/53/bin/php ];then
+ return
+fi
+
+# OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype_old"
+# OPTIONS="${OPTIONS} --with-gd --enable-gd-native-ttf"
+# OPTIONS="${OPTIONS} --with-jpeg --with-jpeg-dir=/usr/lib"
+OPTIONS='--without-iconv'
+
+if [ $sysName == 'Darwin' ]; then
+ OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype"
+fi
+
+IS_64BIT=`getconf LONG_BIT`
+if [ "$IS_64BIT" == "64" ];then
+ OPTIONS="${OPTIONS} --with-libdir=lib64"
+fi
+
+# ----- cpu start ------
+if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+fi
+
+if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+fi
+
+MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+else
+ cpuCore="1"
+fi
+
+if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+else
+ cpuCore="1"
+fi
+# ----- cpu end ------
+
+if [ "${SYS_ARCH}" == "aarch64" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+fi
+
+if [ ! -d $serverPath/php/${PHP_VER}/bin ];then
+ cd $sourcePath/php/php${PHP_VER} && ./configure \
+ --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \
+ --enable-mysqlnd \
+ --with-mysql=mysqlnd \
+ --with-pdo-mysql=mysqlnd \
+ --with-mysqli=mysqlnd \
+ --enable-mbstring \
+ --enable-exif \
+ --enable-hash \
+ --enable-libxml \
+ --enable-simplexml \
+ --enable-dom \
+ --enable-filter \
+ --enable-xml \
+ --enable-ftp \
+ --enable-soap \
+ --enable-posix \
+ --enable-sockets \
+ --enable-mbstring \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ --disable-fileinfo \
+ $OPTIONS \
+ --enable-fpm
+ make clean && make -j${cpuCore} && make install && make clean
+
+ # rm -rf $sourcePath/php/php${PHP_VER}
+ echo "安装php-${version}成功"
+fi
+
+
+if [ -f $serverPath/php/53/bin/php.dSYM ];then
+ mv $serverPath/php/53/bin/php.dSYM $serverPath/php/53/bin/php
+fi
+
+if [ -f $serverPath/php/53/sbin/php-fpm.dSYM ];then
+ mv $serverPath/php/53/sbin/php-fpm.dSYM $serverPath/php/53/sbin/php-fpm
+fi
+
+
+if [ -d $serverPath/php/53 ] && [ ! -d $serverPath/php/53/lib/php/extensions/no-debug-non-zts-20090626 ]; then
+ mkdir -p $serverPath/php/53/lib/php/extensions/no-debug-non-zts-20090626
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php53 stop
+ rm -rf $serverPath/php/53
+ echo "uninstall php-5.3.29 ..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/53/intl.sh b/plugins/php/versions/53/intl.sh
new file mode 100755
index 000000000..3b8c2f83d
--- /dev/null
+++ b/plugins/php/versions/53/intl.sh
@@ -0,0 +1,108 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=intl
+LIBV=3.0.0
+sysName=`uname`
+actionType=$1
+version=$2
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/lib/php/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/lib/php/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ cd ${rootPath}/plugins/php/lib && /bin/bash icu.sh
+ if [ -d $php_lib/${LIBNAME}-${LIBV} ];then
+ wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config \
+ $OPTIONS \
+ --with-icu-dir=${serverPath}/lib/icu
+
+ make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$vphp not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/53/opcache.sh b/plugins/php/versions/53/opcache.sh
new file mode 100755
index 000000000..783c64d5f
--- /dev/null
+++ b/plugins/php/versions/53/opcache.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=opcache
+LIBV=7.0.5
+sysName=`uname`
+actionType=$1
+version=$2
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/lib/php/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/lib/php/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ ! -d $php_lib/zendopcache-7.0.5 ];then
+ wget -O $php_lib/zendopcache-7.0.5.tgz http://pecl.php.net/get/zendopcache-7.0.5.tgz
+ cd $php_lib && tar xvf zendopcache-7.0.5.tgz
+ fi
+ cd $php_lib/zendopcache-7.0.5
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make && make install && make clean
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "zend_extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.enable=1" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.memory_consumption=128" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.interned_strings_buffer=8" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.max_accelerated_files=4000" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.revalidate_freq=60" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.fast_shutdown=1" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.enable_cli=1" >> $serverPath/php/$version/etc/php.ini
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/53/zendguardloader.sh b/plugins/php/versions/53/zendguardloader.sh
new file mode 100755
index 000000000..77baa7a89
--- /dev/null
+++ b/plugins/php/versions/53/zendguardloader.sh
@@ -0,0 +1,103 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+LIBNAME=ZendGuardLoader
+
+sysName=`uname`
+actionType=$1
+version=$2
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/lib/php/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/lib/php/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ $sysName == 'Darwin' ]; then
+ wget -O $php_lib/zend-loader-php5.3.tar.gz http://downloads.zend.com/guard/5.5.0/ZendGuardLoader-php-5.3-darwin-i386.tar.gz
+ else
+ wget -O $php_lib/zend-loader-php5.3.tar.gz http://downloads.zend.com/guard/5.5.0/ZendGuardLoader-php-5.3-linux-glibc23-x86_64.tar.gz
+ fi
+
+ cd $php_lib && tar xvf zend-loader-php5.3.tar.gz
+ cd ZendGuardLoader-php* && cd php-5.3.x
+ cp ZendGuardLoader.so $serverPath/php/$version/lib/php/extensions/no-debug-non-zts-20090626/
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[Zend ZendGuard Loader]" >> $serverPath/php/$version/etc/php.ini
+ echo "zend_extension=$serverPath/php/$version/lib/php/extensions/no-debug-non-zts-20090626/ZendGuardLoader.so" >> $serverPath/php/$version/etc/php.ini
+ echo "zend_loader.enable=1" >> $serverPath/php/$version/etc/php.ini
+ echo "zend_loader.disable_licensing=0" >> $serverPath/php/$version/etc/php.ini
+ echo "zend_loader.obfuscation_level_support=3" >> $serverPath/php/$version/etc/php.ini
+ echo "zend_loader.license_path=" >> $serverPath/php/$version/etc/php.ini
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ sed -i $BAK "/ZendGuardLoader.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/zend_loader/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/\[Zend ZendGuard Loader\]/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/54/apc.sh b/plugins/php/versions/54/apc.sh
new file mode 100755
index 000000000..5dd169c07
--- /dev/null
+++ b/plugins/php/versions/54/apc.sh
@@ -0,0 +1,103 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=apc
+_LIBNAME=$(echo $LIBNAME | tr '[a-z]' '[A-Z]')
+LIBV=3.1.9
+sysName=`uname`
+actionType=$1
+version=$2
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/lib/php/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/lib/php/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${_LIBNAME}-${LIBV} ];then
+ wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${_LIBNAME}-${LIBV}.tgz
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${_LIBNAME}-${LIBV}
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${_LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ echo $extFile
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/54/install.sh b/plugins/php/versions/54/install.sh
new file mode 100755
index 000000000..1c57bd48d
--- /dev/null
+++ b/plugins/php/versions/54/install.sh
@@ -0,0 +1,180 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+SYS_ARCH=`arch`
+
+version=5.4.45
+PHP_VER=54
+md5_file_ok=ba580e774ed1ab256f22d1fa69a59311
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-${version} ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.gz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.gz https://museum.php.net/php5/php-${version}.tar.gz
+ fi
+
+ #检测文件是否损坏.
+ if [ -f $sourcePath/php/php-${version}.tar.xz ];then
+ md5_file=`md5sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'`
+ if [ "${md5_file}" != "${md5_file_ok}" ]; then
+ echo "PHP${version} 下载文件不完整,重新安装"
+ rm -rf $sourcePath/php/php-${version}.tar.xz
+ fi
+ fi
+
+ cd $sourcePath/php && tar -zxvf $sourcePath/php/php-${version}.tar.gz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+fi
+
+OPTIONS='--without-iconv'
+if [ $sysName == 'Darwin' ]; then
+ OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype"
+ # OPTIONS="${OPTIONS} --with-pcre-dir=${serverPath}/lib/pcre"
+ # OPTIONS="${OPTIONS} --with-external-pcre=${serverPath}/lib/pcre"
+else
+ OPTIONS="${OPTIONS} --with-readline"
+fi
+
+IS_64BIT=`getconf LONG_BIT`
+if [ "$IS_64BIT" == "64" ];then
+ OPTIONS="${OPTIONS} --with-libdir=lib64"
+fi
+
+# ----- cpu start ------
+if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+fi
+
+if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+fi
+
+MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+else
+ cpuCore="1"
+fi
+
+if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+else
+ cpuCore="1"
+fi
+# ----- cpu end ------
+
+if [ "${SYS_ARCH}" == "aarch64" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+fi
+
+if [ "${SYS_ARCH}" == "arm64" ] && [ "$sysName" == "Darwin" ] ;then
+ # 修复mac arm64架构下php安装
+ # 修复不能识别到sys_icache_invalidate
+ cat ${curPath}/versions/${PHP_VER}/src/ext/pcre/sljitConfigInternal.h > $sourcePath/php/php${PHP_VER}/ext/pcre/pcrelib/sljit/sljitConfigInternal.h
+ cat ${curPath}/versions/${PHP_VER}/src/reentrancy.c > $sourcePath/php/php${PHP_VER}/main/reentrancy.c
+fi
+
+if [ ! -d $serverPath/php/${PHP_VER} ];then
+ cd $sourcePath/php/php${PHP_VER} && ./configure \
+ --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \
+ --enable-mysqlnd \
+ --with-mysql=mysqlnd \
+ --with-pdo-mysql=mysqlnd \
+ --with-mysqli=mysqlnd \
+ --enable-mbstring \
+ --enable-sockets \
+ --enable-ftp \
+ --enable-simplexml \
+ --enable-soap \
+ --enable-posix \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ --disable-fileinfo \
+ $OPTIONS \
+ --enable-fpm
+
+ make clean && make -j${cpuCore}
+
+ #debian11,没有生成php54 man
+ if [ ! -f sapi/cli/php.1 ];then
+ cp -rf sapi/cli/php.1.in sapi/cli/php.1
+ fi
+
+ if [ ! -f sapi/cgi/php-cgi.1 ];then
+ cp -rf sapi/cgi/php-cgi.1.in sapi/cgi/php-cgi.1
+ fi
+
+ if [ ! -f scripts/man1/phpize.1 ];then
+ cp -rf scripts/man1/phpize.1.in scripts/man1/phpize.1
+ fi
+
+ if [ ! -f scripts/man1/php-config.1 ];then
+ cp -rf scripts/man1/php-config.1.in scripts/man1/php-config.1
+ fi
+
+ if [ ! -f ext/phar/phar.1 ];then
+ cp -rf ext/phar/phar.1.in ext/phar/phar.1
+ fi
+
+ if [ ! -f ext/phar/phar.phar.1 ];then
+ cp -rf ext/phar/phar.phar.1.in ext/phar/phar.phar.1
+ fi
+
+
+ make install && make clean
+
+ # rm -rf $sourcePath/php/php${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php54 stop
+ rm -rf $serverPath/php/54
+ echo "卸载php-5.4.45 ..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/54/opcache.sh b/plugins/php/versions/54/opcache.sh
new file mode 100755
index 000000000..bb46a63df
--- /dev/null
+++ b/plugins/php/versions/54/opcache.sh
@@ -0,0 +1,115 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=opcache
+LIBV=7.0.5
+sysName=`uname`
+actionType=$1
+version=$2
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/lib/php/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/lib/php/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ ! -d $php_lib/zendopcache-7.0.5 ];then
+ wget -O $php_lib/zendopcache-7.0.5.tgz http://pecl.php.net/get/zendopcache-7.0.5.tgz
+ cd $php_lib && tar xvf zendopcache-7.0.5.tgz
+ fi
+ cd $php_lib/zendopcache-7.0.5
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make && make install && make clean
+
+ # cp modules/opcache.la $serverPath/php/${version}/lib/php/extensions/no-debug-non-zts-20100525/
+
+ cd $php_lib
+ rm -rf zendopcache-7.0.5
+ rm -rf zendopcache-7.0.5.tgz
+ rm -rf package.xml
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "zend_extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.enable=1" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.memory_consumption=128" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.interned_strings_buffer=8" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.max_accelerated_files=4000" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.revalidate_freq=60" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.fast_shutdown=1" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.enable_cli=1" >> $serverPath/php/$version/etc/php.ini
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/54/src/ext/pcre/sljitConfigInternal.h b/plugins/php/versions/54/src/ext/pcre/sljitConfigInternal.h
new file mode 100644
index 000000000..23c33609d
--- /dev/null
+++ b/plugins/php/versions/54/src/ext/pcre/sljitConfigInternal.h
@@ -0,0 +1,702 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SLJIT_CONFIG_INTERNAL_H_
+#define _SLJIT_CONFIG_INTERNAL_H_
+
+/*
+ SLJIT defines the following architecture dependent types and macros:
+
+ Types:
+ sljit_sb, sljit_ub : signed and unsigned 8 bit byte
+ sljit_sh, sljit_uh : signed and unsigned 16 bit half-word (short) type
+ sljit_si, sljit_ui : signed and unsigned 32 bit integer type
+ sljit_sw, sljit_uw : signed and unsigned machine word, enough to store a pointer
+ sljit_p : unsgined pointer value (usually the same as sljit_uw, but
+ some 64 bit ABIs may use 32 bit pointers)
+ sljit_s : single precision floating point value
+ sljit_d : double precision floating point value
+
+ Macros for feature detection (boolean):
+ SLJIT_32BIT_ARCHITECTURE : 32 bit architecture
+ SLJIT_64BIT_ARCHITECTURE : 64 bit architecture
+ SLJIT_LITTLE_ENDIAN : little endian architecture
+ SLJIT_BIG_ENDIAN : big endian architecture
+ SLJIT_UNALIGNED : allows unaligned memory accesses for non-fpu operations (only!)
+ SLJIT_INDIRECT_CALL : see SLJIT_FUNC_OFFSET() for more information
+
+ Constants:
+ SLJIT_NUMBER_OF_REGISTERS : number of available registers
+ SLJIT_NUMBER_OF_SCRATCH_REGISTERS : number of available scratch registers
+ SLJIT_NUMBER_OF_SAVED_REGISTERS : number of available saved registers
+ SLJIT_NUMBER_OF_FLOAT_REGISTERS : number of available floating point registers
+ SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS : number of available floating point scratch registers
+ SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS : number of available floating point saved registers
+ SLJIT_WORD_SHIFT : the shift required to apply when accessing a sljit_sw/sljit_uw array by index
+ SLJIT_DOUBLE_SHIFT : the shift required to apply when accessing
+ a double precision floating point array by index
+ SLJIT_SINGLE_SHIFT : the shift required to apply when accessing
+ a single precision floating point array by index
+ SLJIT_LOCALS_OFFSET : local space starting offset (SLJIT_SP + SLJIT_LOCALS_OFFSET)
+ SLJIT_RETURN_ADDRESS_OFFSET : a return instruction always adds this offset to the return address
+
+ Other macros:
+ SLJIT_CALL : C calling convention define for both calling JIT form C and C callbacks for JIT
+ SLJIT_W(number) : defining 64 bit constants on 64 bit architectures (compiler independent helper)
+*/
+
+/*****************/
+/* Sanity check. */
+/*****************/
+
+#if !((defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ || (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \
+ || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ || (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ || (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ || (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \
+ || (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) \
+ || (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \
+ || (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED))
+#error "An architecture must be selected"
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ + (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ + (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \
+ + (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ + (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ + (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ + (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ + (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ + (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) \
+ + (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ + (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ + (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \
+ + (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \
+ + (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) >= 2
+#error "Multiple architectures are selected"
+#endif
+
+/********************************************************/
+/* Automatic CPU detection (requires compiler support). */
+/********************************************************/
+
+#if (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO)
+
+#ifndef _WIN32
+
+#if defined(__i386__) || defined(__i386)
+#define SLJIT_CONFIG_X86_32 1
+#elif defined(__x86_64__)
+#define SLJIT_CONFIG_X86_64 1
+#elif defined(__arm__) || defined(__ARM__)
+#ifdef __thumb2__
+#define SLJIT_CONFIG_ARM_THUMB2 1
+#elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__)
+#define SLJIT_CONFIG_ARM_V7 1
+#else
+#define SLJIT_CONFIG_ARM_V5 1
+#endif
+#elif defined (__aarch64__)
+#define SLJIT_CONFIG_ARM_64 1
+#elif defined(__ppc64__) || defined(__powerpc64__) || defined(_ARCH_PPC64) || (defined(_POWER) && defined(__64BIT__))
+#define SLJIT_CONFIG_PPC_64 1
+#elif defined(__ppc__) || defined(__powerpc__) || defined(_ARCH_PPC) || defined(_ARCH_PWR) || defined(_ARCH_PWR2) || defined(_POWER)
+#define SLJIT_CONFIG_PPC_32 1
+#elif defined(__mips__) && !defined(_LP64)
+#define SLJIT_CONFIG_MIPS_32 1
+#elif defined(__mips64)
+#define SLJIT_CONFIG_MIPS_64 1
+#elif defined(__sparc__) || defined(__sparc)
+#define SLJIT_CONFIG_SPARC_32 1
+#elif defined(__tilegx__)
+#define SLJIT_CONFIG_TILEGX 1
+#else
+/* Unsupported architecture */
+#define SLJIT_CONFIG_UNSUPPORTED 1
+#endif
+
+#else /* !_WIN32 */
+
+#if defined(_M_X64) || defined(__x86_64__)
+#define SLJIT_CONFIG_X86_64 1
+#elif defined(_ARM_)
+#define SLJIT_CONFIG_ARM_V5 1
+#else
+#define SLJIT_CONFIG_X86_32 1
+#endif
+
+#endif /* !WIN32 */
+#endif /* SLJIT_CONFIG_AUTO */
+
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+#undef SLJIT_EXECUTABLE_ALLOCATOR
+#endif
+
+/******************************/
+/* CPU family type detection. */
+/******************************/
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+#define SLJIT_CONFIG_ARM_32 1
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+#define SLJIT_CONFIG_X86 1
+#elif (defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+#define SLJIT_CONFIG_ARM 1
+#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define SLJIT_CONFIG_PPC 1
+#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+#define SLJIT_CONFIG_MIPS 1
+#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) || (defined SLJIT_CONFIG_SPARC_64 && SLJIT_CONFIG_SPARC_64)
+#define SLJIT_CONFIG_SPARC 1
+#endif
+
+/**********************************/
+/* External function definitions. */
+/**********************************/
+
+#if !(defined SLJIT_STD_MACROS_DEFINED && SLJIT_STD_MACROS_DEFINED)
+
+/* These libraries are needed for the macros below. */
+#include
+#include
+
+#endif /* SLJIT_STD_MACROS_DEFINED */
+
+/* General macros:
+ Note: SLJIT is designed to be independent from them as possible.
+
+ In release mode (SLJIT_DEBUG is not defined) only the following
+ external functions are needed:
+*/
+
+#ifndef SLJIT_MALLOC
+#define SLJIT_MALLOC(size, allocator_data) malloc(size)
+#endif
+
+#ifndef SLJIT_FREE
+#define SLJIT_FREE(ptr, allocator_data) free(ptr)
+#endif
+
+#ifndef SLJIT_MEMMOVE
+#define SLJIT_MEMMOVE(dest, src, len) memmove(dest, src, len)
+#endif
+
+#ifndef SLJIT_ZEROMEM
+#define SLJIT_ZEROMEM(dest, len) memset(dest, 0, len)
+#endif
+
+/***************************/
+/* Compiler helper macros. */
+/***************************/
+
+#if !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY)
+
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define SLJIT_LIKELY(x) __builtin_expect((x), 1)
+#define SLJIT_UNLIKELY(x) __builtin_expect((x), 0)
+#else
+#define SLJIT_LIKELY(x) (x)
+#define SLJIT_UNLIKELY(x) (x)
+#endif
+
+#endif /* !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY) */
+
+#ifndef SLJIT_INLINE
+/* Inline functions. Some old compilers do not support them. */
+#if defined(__SUNPRO_C) && __SUNPRO_C <= 0x510
+#define SLJIT_INLINE
+#else
+#define SLJIT_INLINE __inline
+#endif
+#endif /* !SLJIT_INLINE */
+
+#ifndef SLJIT_NOINLINE
+/* Not inline functions. */
+#if defined(__GNUC__)
+#define SLJIT_NOINLINE __attribute__ ((noinline))
+#else
+#define SLJIT_NOINLINE
+#endif
+#endif /* !SLJIT_INLINE */
+
+#ifndef SLJIT_CONST
+/* Const variables. */
+#define SLJIT_CONST const
+#endif
+
+#ifndef SLJIT_UNUSED_ARG
+/* Unused arguments. */
+#define SLJIT_UNUSED_ARG(arg) (void)arg
+#endif
+
+/*********************************/
+/* Type of public API functions. */
+/*********************************/
+
+#if (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC)
+/* Static ABI functions. For all-in-one programs. */
+
+#if defined(__GNUC__)
+/* Disable unused warnings in gcc. */
+#define SLJIT_API_FUNC_ATTRIBUTE static __attribute__((unused))
+#else
+#define SLJIT_API_FUNC_ATTRIBUTE static
+#endif
+
+#else
+#define SLJIT_API_FUNC_ATTRIBUTE
+#endif /* (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC) */
+
+/****************************/
+/* Instruction cache flush. */
+/****************************/
+
+#ifndef SLJIT_CACHE_FLUSH
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+
+/* Not required to implement on archs with unified caches. */
+#define SLJIT_CACHE_FLUSH(from, to)
+
+#elif defined __APPLE__
+
+/* Supported by all macs since Mac OS 10.5.
+ However, it does not work on non-jailbroken iOS devices,
+ although the compilation is successful. */
+#include
+#define SLJIT_CACHE_FLUSH(from, to) \
+ sys_icache_invalidate((char*)(from), (char*)(to) - (char*)(from))
+
+#elif defined __ANDROID__
+
+/* Android lacks __clear_cache; instead, cacheflush should be used. */
+
+#define SLJIT_CACHE_FLUSH(from, to) \
+ cacheflush((long)(from), (long)(to), 0)
+
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+/* The __clear_cache() implementation of GCC is a dummy function on PowerPC. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ ppc_cache_flush((from), (to))
+
+#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+
+/* The __clear_cache() implementation of GCC is a dummy function on Sparc. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ sparc_cache_flush((from), (to))
+
+#else
+
+/* Calls __ARM_NR_cacheflush on ARM-Linux. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ __clear_cache((char*)(from), (char*)(to))
+
+#endif
+
+#endif /* !SLJIT_CACHE_FLUSH */
+
+/******************************************************/
+/* Byte/half/int/word/single/double type definitions. */
+/******************************************************/
+
+/* 8 bit byte type. */
+typedef unsigned char sljit_ub;
+typedef signed char sljit_sb;
+
+/* 16 bit half-word type. */
+typedef unsigned short int sljit_uh;
+typedef signed short int sljit_sh;
+
+/* 32 bit integer type. */
+typedef unsigned int sljit_ui;
+typedef signed int sljit_si;
+
+/* Machine word type. Enough for storing a pointer.
+ 32 bit for 32 bit machines.
+ 64 bit for 64 bit machines. */
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+/* Just to have something. */
+#define SLJIT_WORD_SHIFT 0
+typedef unsigned long int sljit_uw;
+typedef long int sljit_sw;
+#elif !(defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ && !(defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ && !(defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ && !(defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX)
+#define SLJIT_32BIT_ARCHITECTURE 1
+#define SLJIT_WORD_SHIFT 2
+typedef unsigned int sljit_uw;
+typedef int sljit_sw;
+#else
+#define SLJIT_64BIT_ARCHITECTURE 1
+#define SLJIT_WORD_SHIFT 3
+#ifdef _WIN32
+typedef unsigned __int64 sljit_uw;
+typedef __int64 sljit_sw;
+#else
+typedef unsigned long int sljit_uw;
+typedef long int sljit_sw;
+#endif
+#endif
+
+typedef sljit_uw sljit_p;
+
+/* Floating point types. */
+typedef float sljit_s;
+typedef double sljit_d;
+
+/* Shift for pointer sized data. */
+#define SLJIT_POINTER_SHIFT SLJIT_WORD_SHIFT
+
+/* Shift for double precision sized data. */
+#define SLJIT_DOUBLE_SHIFT 3
+#define SLJIT_SINGLE_SHIFT 2
+
+#ifndef SLJIT_W
+
+/* Defining long constants. */
+#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+#define SLJIT_W(w) (w##ll)
+#else
+#define SLJIT_W(w) (w)
+#endif
+
+#endif /* !SLJIT_W */
+
+/*************************/
+/* Endianness detection. */
+/*************************/
+
+#if !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN)
+
+/* These macros are mostly useful for the applications. */
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+
+#ifdef __LITTLE_ENDIAN__
+#define SLJIT_LITTLE_ENDIAN 1
+#else
+#define SLJIT_BIG_ENDIAN 1
+#endif
+
+#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+
+#ifdef __MIPSEL__
+#define SLJIT_LITTLE_ENDIAN 1
+#else
+#define SLJIT_BIG_ENDIAN 1
+#endif
+
+#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+
+#define SLJIT_BIG_ENDIAN 1
+
+#else
+#define SLJIT_LITTLE_ENDIAN 1
+#endif
+
+#endif /* !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN) */
+
+/* Sanity check. */
+#if (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#error "Exactly one endianness must be selected"
+#endif
+
+#if !(defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && !(defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#error "Exactly one endianness must be selected"
+#endif
+
+#ifndef SLJIT_UNALIGNED
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ || (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define SLJIT_UNALIGNED 1
+#endif
+
+#endif /* !SLJIT_UNALIGNED */
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+/* Auto detect SSE2 support using CPUID.
+ On 64 bit x86 cpus, sse2 must be present. */
+#define SLJIT_DETECT_SSE2 1
+#endif
+
+/*****************************************************************************************/
+/* Calling convention of functions generated by SLJIT or called from the generated code. */
+/*****************************************************************************************/
+
+#ifndef SLJIT_CALL
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+
+#if defined(__GNUC__) && !defined(__APPLE__)
+
+#define SLJIT_CALL __attribute__ ((fastcall))
+#define SLJIT_X86_32_FASTCALL 1
+
+#elif defined(_MSC_VER)
+
+#define SLJIT_CALL __fastcall
+#define SLJIT_X86_32_FASTCALL 1
+
+#elif defined(__BORLANDC__)
+
+#define SLJIT_CALL __msfastcall
+#define SLJIT_X86_32_FASTCALL 1
+
+#else /* Unknown compiler. */
+
+/* The cdecl attribute is the default. */
+#define SLJIT_CALL
+
+#endif
+
+#else /* Non x86-32 architectures. */
+
+#define SLJIT_CALL
+
+#endif /* SLJIT_CONFIG_X86_32 */
+
+#endif /* !SLJIT_CALL */
+
+#ifndef SLJIT_INDIRECT_CALL
+#if ((defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) && (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN)) \
+ || ((defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) && defined _AIX)
+/* It seems certain ppc compilers use an indirect addressing for functions
+ which makes things complicated. */
+#define SLJIT_INDIRECT_CALL 1
+#endif
+#endif /* SLJIT_INDIRECT_CALL */
+
+/* The offset which needs to be substracted from the return address to
+determine the next executed instruction after return. */
+#ifndef SLJIT_RETURN_ADDRESS_OFFSET
+#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+#define SLJIT_RETURN_ADDRESS_OFFSET 8
+#else
+#define SLJIT_RETURN_ADDRESS_OFFSET 0
+#endif
+#endif /* SLJIT_RETURN_ADDRESS_OFFSET */
+
+/***************************************************/
+/* Functions of the built-in executable allocator. */
+/***************************************************/
+
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void);
+#define SLJIT_MALLOC_EXEC(size) sljit_malloc_exec(size)
+#define SLJIT_FREE_EXEC(ptr) sljit_free_exec(ptr)
+#endif
+
+/**********************************************/
+/* Registers and locals offset determination. */
+/**********************************************/
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+
+#define SLJIT_NUMBER_OF_REGISTERS 10
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7
+#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
+#define SLJIT_LOCALS_OFFSET_BASE ((2 + 4) * sizeof(sljit_sw))
+#else
+/* Maximum 3 arguments are passed on the stack, +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((3 + 1 + 4) * sizeof(sljit_sw))
+#endif /* SLJIT_X86_32_FASTCALL */
+
+#elif (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+
+#ifndef _WIN64
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 6
+#define SLJIT_LOCALS_OFFSET_BASE (sizeof(sljit_sw))
+#else
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE ((4 + 2) * sizeof(sljit_sw))
+#endif /* _WIN64 */
+
+#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+
+#define SLJIT_NUMBER_OF_REGISTERS 11
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+
+#define SLJIT_NUMBER_OF_REGISTERS 11
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#elif (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+
+#define SLJIT_NUMBER_OF_REGISTERS 25
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 10
+#define SLJIT_LOCALS_OFFSET_BASE (2 * sizeof(sljit_sw))
+
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+#define SLJIT_NUMBER_OF_REGISTERS 22
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 17
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) || (defined _AIX)
+#define SLJIT_LOCALS_OFFSET_BASE ((6 + 8) * sizeof(sljit_sw))
+#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+/* Add +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((3 + 1) * sizeof(sljit_sw))
+#else
+#define SLJIT_LOCALS_OFFSET_BASE (3 * sizeof(sljit_sw))
+#endif /* SLJIT_CONFIG_PPC_64 || _AIX */
+
+#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+
+#define SLJIT_NUMBER_OF_REGISTERS 17
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#define SLJIT_LOCALS_OFFSET_BASE (4 * sizeof(sljit_sw))
+#else
+#define SLJIT_LOCALS_OFFSET_BASE 0
+#endif
+
+#elif (defined SLJIT_CONFIG_SPARC && SLJIT_CONFIG_SPARC)
+
+#define SLJIT_NUMBER_OF_REGISTERS 18
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 14
+#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+/* Add +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((23 + 1) * sizeof(sljit_sw))
+#endif
+
+#elif (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+
+#define SLJIT_NUMBER_OF_REGISTERS 0
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 0
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#endif
+
+#define SLJIT_LOCALS_OFFSET (SLJIT_LOCALS_OFFSET_BASE)
+
+#define SLJIT_NUMBER_OF_SCRATCH_REGISTERS \
+ (SLJIT_NUMBER_OF_REGISTERS - SLJIT_NUMBER_OF_SAVED_REGISTERS)
+
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 6
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) && (defined _WIN64)
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 1
+#else
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0
+#endif
+
+#define SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS \
+ (SLJIT_NUMBER_OF_FLOAT_REGISTERS - SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS)
+
+/*************************************/
+/* Debug and verbose related macros. */
+/*************************************/
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+#include
+#endif
+
+#if (defined SLJIT_DEBUG && SLJIT_DEBUG)
+
+#if !defined(SLJIT_ASSERT) || !defined(SLJIT_ASSERT_STOP)
+
+/* SLJIT_HALT_PROCESS must halt the process. */
+#ifndef SLJIT_HALT_PROCESS
+#include
+
+#define SLJIT_HALT_PROCESS() \
+ abort();
+#endif /* !SLJIT_HALT_PROCESS */
+
+#include
+
+#endif /* !SLJIT_ASSERT || !SLJIT_ASSERT_STOP */
+
+/* Feel free to redefine these two macros. */
+#ifndef SLJIT_ASSERT
+
+#define SLJIT_ASSERT(x) \
+ do { \
+ if (SLJIT_UNLIKELY(!(x))) { \
+ printf("Assertion failed at " __FILE__ ":%d\n", __LINE__); \
+ SLJIT_HALT_PROCESS(); \
+ } \
+ } while (0)
+
+#endif /* !SLJIT_ASSERT */
+
+#ifndef SLJIT_ASSERT_STOP
+
+#define SLJIT_ASSERT_STOP() \
+ do { \
+ printf("Should never been reached " __FILE__ ":%d\n", __LINE__); \
+ SLJIT_HALT_PROCESS(); \
+ } while (0)
+
+#endif /* !SLJIT_ASSERT_STOP */
+
+#else /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
+
+/* Forcing empty, but valid statements. */
+#undef SLJIT_ASSERT
+#undef SLJIT_ASSERT_STOP
+
+#define SLJIT_ASSERT(x) \
+ do { } while (0)
+#define SLJIT_ASSERT_STOP() \
+ do { } while (0)
+
+#endif /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
+
+#ifndef SLJIT_COMPILE_ASSERT
+
+/* Should be improved eventually. */
+#define SLJIT_COMPILE_ASSERT(x, description) \
+ SLJIT_ASSERT(x)
+
+#endif /* !SLJIT_COMPILE_ASSERT */
+
+#endif
diff --git a/plugins/php/versions/54/src/reentrancy.c b/plugins/php/versions/54/src/reentrancy.c
new file mode 100644
index 000000000..e7c5d7171
--- /dev/null
+++ b/plugins/php/versions/54/src/reentrancy.c
@@ -0,0 +1,450 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#include
+#include
+#include
+#ifdef HAVE_DIRENT_H
+#include
+#endif
+
+#include "php_reentrancy.h"
+#include "ext/standard/php_rand.h" /* for PHP_RAND_MAX */
+
+enum {
+ LOCALTIME_R,
+ CTIME_R,
+ ASCTIME_R,
+ GMTIME_R,
+ READDIR_R,
+ NUMBER_OF_LOCKS
+};
+
+#if defined(PHP_NEED_REENTRANCY)
+
+#include
+
+static MUTEX_T reentrant_locks[NUMBER_OF_LOCKS];
+
+#define local_lock(x) tsrm_mutex_lock(reentrant_locks[x])
+#define local_unlock(x) tsrm_mutex_unlock(reentrant_locks[x])
+
+#else
+
+#define local_lock(x)
+#define local_unlock(x)
+
+#endif
+
+#if defined(PHP_IRIX_TIME_R)
+
+#define HAVE_CTIME_R 1
+#define HAVE_ASCTIME_R 1
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ if (ctime_r(clock, buf) == buf)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ if (asctime_r(tm, buf) == buf)
+ return (buf);
+ return (NULL);
+}
+
+#endif
+
+#if defined(PHP_HPUX_TIME_R)
+
+#define HAVE_LOCALTIME_R 1
+#define HAVE_CTIME_R 1
+#define HAVE_ASCTIME_R 1
+#define HAVE_GMTIME_R 1
+
+PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ if (localtime_r(timep, p_tm) == 0)
+ return (p_tm);
+ return (NULL);
+}
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ if (ctime_r(clock, buf, 26) != -1)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ if (asctime_r(tm, buf, 26) != -1)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ if (gmtime_r(timep, p_tm) == 0)
+ return (p_tm);
+ return (NULL);
+}
+
+#endif
+
+#if defined(__BEOS__)
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ /* Modified according to LibC definition */
+ if (((struct tm*)gmtime_r(timep, p_tm)) == p_tm)
+ return (p_tm);
+ return (NULL);
+}
+
+#endif /* BEOS */
+
+#if !defined(HAVE_POSIX_READDIR_R)
+
+PHPAPI int php_readdir_r(DIR *dirp, struct dirent *entry,
+ struct dirent **result)
+{
+#if defined(HAVE_OLD_READDIR_R)
+ int ret = 0;
+
+ /* We cannot rely on the return value of readdir_r
+ as it differs between various platforms
+ (HPUX returns 0 on success whereas Solaris returns non-zero)
+ */
+ entry->d_name[0] = '\0';
+ readdir_r(dirp, entry, result);
+
+ if (entry->d_name[0] == '\0') {
+ *result = NULL;
+ ret = errno;
+ } else {
+ *result = entry;
+ }
+ return ret;
+#else
+ struct dirent *ptr;
+ int ret = 0;
+
+ local_lock(READDIR_R);
+
+ errno = 0;
+
+ ptr = readdir(dirp);
+
+ if (!ptr && errno != 0)
+ ret = errno;
+
+ if (ptr)
+ memcpy(entry, ptr, sizeof(*ptr));
+
+ *result = ptr;
+
+ local_unlock(READDIR_R);
+
+ return ret;
+#endif
+}
+
+#endif
+
+#if !defined(HAVE_LOCALTIME_R) && defined(HAVE_LOCALTIME)
+
+PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ struct tm *tmp;
+
+ local_lock(LOCALTIME_R);
+
+ tmp = localtime(timep);
+ if (tmp) {
+ memcpy(p_tm, tmp, sizeof(struct tm));
+ tmp = p_tm;
+ }
+
+ local_unlock(LOCALTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if !defined(HAVE_CTIME_R) && defined(HAVE_CTIME)
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ char *tmp;
+
+ local_lock(CTIME_R);
+
+ tmp = ctime(clock);
+ strcpy(buf, tmp);
+
+ local_unlock(CTIME_R);
+
+ return buf;
+}
+
+#endif
+
+#if !defined(HAVE_ASCTIME_R) && defined(HAVE_ASCTIME)
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ char *tmp;
+
+ local_lock(ASCTIME_R);
+
+ tmp = asctime(tm);
+ strcpy(buf, tmp);
+
+ local_unlock(ASCTIME_R);
+
+ return buf;
+}
+
+#endif
+
+#if !defined(HAVE_GMTIME_R) && defined(HAVE_GMTIME)
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ struct tm *tmp;
+
+ local_lock(GMTIME_R);
+
+ tmp = gmtime(timep);
+ if (tmp) {
+ memcpy(p_tm, tmp, sizeof(struct tm));
+ tmp = p_tm;
+ }
+
+ local_unlock(GMTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if defined(PHP_NEED_REENTRANCY)
+
+void reentrancy_startup(void)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_LOCKS; i++) {
+ reentrant_locks[i] = tsrm_mutex_alloc();
+ }
+}
+
+void reentrancy_shutdown(void)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_LOCKS; i++) {
+ tsrm_mutex_free(reentrant_locks[i]);
+ }
+}
+
+#endif
+
+#ifndef HAVE_RAND_R
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Posix rand_r function added May 1999 by Wes Peters .
+ */
+
+#include
+#include
+
+static int
+do_rand(unsigned long *ctx)
+{
+ return ((*ctx = *ctx * 1103515245 + 12345) % ((u_long)PHP_RAND_MAX + 1));
+}
+
+
+PHPAPI int
+php_rand_r(unsigned int *ctx)
+{
+ u_long val = (u_long) *ctx;
+ *ctx = do_rand(&val);
+ return (int) *ctx;
+}
+
+#endif
+
+
+#ifndef HAVE_STRTOK_R
+
+/*
+ * Copyright (c) 1998 Softweyr LLC. All rights reserved.
+ *
+ * strtok_r, from Berkeley strtok
+ * Oct 13, 1998 by Wes Peters
+ *
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notices, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notices, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Softweyr LLC, the
+ * University of California, Berkeley, and its contributors.
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SOFTWEYR LLC, THE REGENTS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTWEYR LLC, THE
+ * REGENTS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+
+PHPAPI char *
+php_strtok_r(char *s, const char *delim, char **last)
+{
+ char *spanp;
+ int c, sc;
+ char *tok;
+
+ if (s == NULL && (s = *last) == NULL)
+ {
+ return NULL;
+ }
+
+ /*
+ * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
+ */
+cont:
+ c = *s++;
+ for (spanp = (char *)delim; (sc = *spanp++) != 0; )
+ {
+ if (c == sc)
+ {
+ goto cont;
+ }
+ }
+
+ if (c == 0) /* no non-delimiter characters */
+ {
+ *last = NULL;
+ return NULL;
+ }
+ tok = s - 1;
+
+ /*
+ * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
+ * Note that delim must have one NUL; we stop if we see that, too.
+ */
+ for (;;)
+ {
+ c = *s++;
+ spanp = (char *)delim;
+ do
+ {
+ if ((sc = *spanp++) == c)
+ {
+ if (c == 0)
+ {
+ s = NULL;
+ }
+ else
+ {
+ char *w = s - 1;
+ *w = '\0';
+ }
+ *last = s;
+ return tok;
+ }
+ }
+ while (sc != 0);
+ }
+ /* NOTREACHED */
+}
+
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/plugins/php/versions/54/zendguardloader.sh b/plugins/php/versions/54/zendguardloader.sh
new file mode 100755
index 000000000..197a5cbb6
--- /dev/null
+++ b/plugins/php/versions/54/zendguardloader.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+LIBNAME=ZendGuardLoader
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ NON_ZTS_FILENAME=`ls $serverPath/php/${version}/lib/php/extensions | grep no-debug-non-zts`
+ extFile=$serverPath/php/${version}/lib/php/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ $sysName == 'Darwin' ]; then
+ wget -O $php_lib/zend-loader-php5.6.tar.gz http://downloads.zend.com/guard/7.0.0/zend-loader-php5.6-darwin10.7-x86_64_update1.tar.gz
+ else
+ wget -O $php_lib/ZendGuardLoader-70429-PHP-5.4-linux-glibc23-x86_64.tar.gz http://downloads.zend.com/guard/6.0.0/ZendGuardLoader-70429-PHP-5.4-linux-glibc23-x86_64.tar.gz
+ fi
+
+ cd $php_lib && tar xvf ZendGuardLoader-70429-PHP-5.4-linux-glibc23-x86_64.tar.gz
+ cd ZendGuardLoader-70429-PHP-5.4-linux-glibc23-x86_64/php-5.4.x
+ cp ZendGuardLoader.so $serverPath/php/$version/lib/php/extensions/no-debug-non-zts-20100525/
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo -e "[Zend ZendGuard Loader]\nzend_extension=ZendGuardLoader.so\nzend_loader.enable=1\nzend_loader.disable_licensing=0\nzend_loader.obfuscation_level_support=3\nzend_loader.license_path=" >> $serverPath/php/$version/etc/php.ini
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ extFile=$serverPath/php/${version}/lib/php/extensions/no-debug-non-zts-20131226/${LIBNAME}.so
+ if [ ! -f "$extFile" ];then
+ echo "php$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ sed -i $BAK '/ZendGuardLoader.so/d' $serverPath/php/$version/etc/php.ini
+ sed -i $BAK '/zend_loader/d' $serverPath/php/$version/etc/php.ini
+ sed -i $BAK '/\[Zend ZendGuard Loader\]/d' $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+actionType=$1
+version=$2
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/55/install.sh b/plugins/php/versions/55/install.sh
new file mode 100755
index 000000000..d5cad76e8
--- /dev/null
+++ b/plugins/php/versions/55/install.sh
@@ -0,0 +1,154 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+SYS_ARCH=`arch`
+
+version=5.5.38
+PHP_VER=55
+md5_file_ok=72302e26f153687e2ca922909f927443
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-5.5.38 ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+cd ${rootPath}/plugins/php/lib && /bin/bash freetype_old.sh
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://museum.php.net/php5/php-${version}.tar.xz
+ fi
+
+ #检测文件是否损坏.
+ if [ -f $sourcePath/php/php-${version}.tar.xz ];then
+ md5_file=`md5sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'`
+ if [ "${md5_file}" != "${md5_file_ok}" ]; then
+ echo "PHP${version} 下载文件不完整,重新安装"
+ rm -rf $sourcePath/php/php-${version}.tar.xz
+ fi
+ fi
+
+ cd $sourcePath/php && tar -Jxf $sourcePath/php/php-${version}.tar.xz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+fi
+
+OPTIONS='--without-iconv'
+if [ $sysName == 'Darwin' ]; then
+ OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype"
+else
+ OPTIONS="${OPTIONS} --with-readline"
+fi
+
+IS_64BIT=`getconf LONG_BIT`
+if [ "$IS_64BIT" == "64" ];then
+ OPTIONS="${OPTIONS} --with-libdir=lib64"
+fi
+
+# ----- cpu start ------
+if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+fi
+
+if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+fi
+
+MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+else
+ cpuCore="1"
+fi
+
+if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+else
+ cpuCore="1"
+fi
+# ----- cpu end ------
+
+if [ "${SYS_ARCH}" == "aarch64" ];then
+ # 修复aarch64架构下安装
+ # /www/server/mdserver-web/plugins/php/versions/56/src/zend_multiply.h > /www/server/source/php/php56/Zend/zend_multiply.h
+ cat ${curPath}/versions/${PHP_VER}/src/zend_multiply.h > $sourcePath/php/php${PHP_VER}/Zend/zend_multiply.h
+fi
+
+if [ "${SYS_ARCH}" == "arm64" ] && [ "$sysName" == "Darwin" ] ;then
+ # 修复mac arm64架构下php安装
+ # 修复不能识别到sys_icache_invalidate
+ cat ${curPath}/versions/${PHP_VER}/src/ext/pcre/sljitConfigInternal.h > $sourcePath/php/php${PHP_VER}/ext/pcre/pcrelib/sljit/sljitConfigInternal.h
+ cat ${curPath}/versions/${PHP_VER}/src/reentrancy.c > $sourcePath/php/php${PHP_VER}/main/reentrancy.c
+fi
+
+if [ ! -d $serverPath/php/${PHP_VER} ];then
+ cd $sourcePath/php/php${PHP_VER} && ./configure \
+ --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \
+ --enable-mysqlnd \
+ --with-mysql=mysqlnd \
+ --with-pdo-mysql=mysqlnd \
+ --with-mysqli=mysqlnd \
+ --enable-mbstring \
+ --enable-simplexml \
+ --enable-sockets \
+ --enable-ftp \
+ --enable-soap \
+ --enable-posix \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ --disable-intl \
+ --disable-fileinfo \
+ $OPTIONS \
+ --enable-fpm
+ make clean && make -j${cpuCore} && make install && make clean
+
+ # rm -rf $sourcePath/php/php${PHP_VER}
+ echo "安装php-${version}成功"
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php55 stop
+ rm -rf $serverPath/php/55
+ echo "卸载php-5.5.38 ..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/55/src/ext/pcre/sljitConfigInternal.h b/plugins/php/versions/55/src/ext/pcre/sljitConfigInternal.h
new file mode 100644
index 000000000..23c33609d
--- /dev/null
+++ b/plugins/php/versions/55/src/ext/pcre/sljitConfigInternal.h
@@ -0,0 +1,702 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SLJIT_CONFIG_INTERNAL_H_
+#define _SLJIT_CONFIG_INTERNAL_H_
+
+/*
+ SLJIT defines the following architecture dependent types and macros:
+
+ Types:
+ sljit_sb, sljit_ub : signed and unsigned 8 bit byte
+ sljit_sh, sljit_uh : signed and unsigned 16 bit half-word (short) type
+ sljit_si, sljit_ui : signed and unsigned 32 bit integer type
+ sljit_sw, sljit_uw : signed and unsigned machine word, enough to store a pointer
+ sljit_p : unsgined pointer value (usually the same as sljit_uw, but
+ some 64 bit ABIs may use 32 bit pointers)
+ sljit_s : single precision floating point value
+ sljit_d : double precision floating point value
+
+ Macros for feature detection (boolean):
+ SLJIT_32BIT_ARCHITECTURE : 32 bit architecture
+ SLJIT_64BIT_ARCHITECTURE : 64 bit architecture
+ SLJIT_LITTLE_ENDIAN : little endian architecture
+ SLJIT_BIG_ENDIAN : big endian architecture
+ SLJIT_UNALIGNED : allows unaligned memory accesses for non-fpu operations (only!)
+ SLJIT_INDIRECT_CALL : see SLJIT_FUNC_OFFSET() for more information
+
+ Constants:
+ SLJIT_NUMBER_OF_REGISTERS : number of available registers
+ SLJIT_NUMBER_OF_SCRATCH_REGISTERS : number of available scratch registers
+ SLJIT_NUMBER_OF_SAVED_REGISTERS : number of available saved registers
+ SLJIT_NUMBER_OF_FLOAT_REGISTERS : number of available floating point registers
+ SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS : number of available floating point scratch registers
+ SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS : number of available floating point saved registers
+ SLJIT_WORD_SHIFT : the shift required to apply when accessing a sljit_sw/sljit_uw array by index
+ SLJIT_DOUBLE_SHIFT : the shift required to apply when accessing
+ a double precision floating point array by index
+ SLJIT_SINGLE_SHIFT : the shift required to apply when accessing
+ a single precision floating point array by index
+ SLJIT_LOCALS_OFFSET : local space starting offset (SLJIT_SP + SLJIT_LOCALS_OFFSET)
+ SLJIT_RETURN_ADDRESS_OFFSET : a return instruction always adds this offset to the return address
+
+ Other macros:
+ SLJIT_CALL : C calling convention define for both calling JIT form C and C callbacks for JIT
+ SLJIT_W(number) : defining 64 bit constants on 64 bit architectures (compiler independent helper)
+*/
+
+/*****************/
+/* Sanity check. */
+/*****************/
+
+#if !((defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ || (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \
+ || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ || (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ || (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ || (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \
+ || (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) \
+ || (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \
+ || (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED))
+#error "An architecture must be selected"
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ + (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ + (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \
+ + (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ + (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ + (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ + (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ + (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ + (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) \
+ + (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ + (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ + (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \
+ + (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \
+ + (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) >= 2
+#error "Multiple architectures are selected"
+#endif
+
+/********************************************************/
+/* Automatic CPU detection (requires compiler support). */
+/********************************************************/
+
+#if (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO)
+
+#ifndef _WIN32
+
+#if defined(__i386__) || defined(__i386)
+#define SLJIT_CONFIG_X86_32 1
+#elif defined(__x86_64__)
+#define SLJIT_CONFIG_X86_64 1
+#elif defined(__arm__) || defined(__ARM__)
+#ifdef __thumb2__
+#define SLJIT_CONFIG_ARM_THUMB2 1
+#elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__)
+#define SLJIT_CONFIG_ARM_V7 1
+#else
+#define SLJIT_CONFIG_ARM_V5 1
+#endif
+#elif defined (__aarch64__)
+#define SLJIT_CONFIG_ARM_64 1
+#elif defined(__ppc64__) || defined(__powerpc64__) || defined(_ARCH_PPC64) || (defined(_POWER) && defined(__64BIT__))
+#define SLJIT_CONFIG_PPC_64 1
+#elif defined(__ppc__) || defined(__powerpc__) || defined(_ARCH_PPC) || defined(_ARCH_PWR) || defined(_ARCH_PWR2) || defined(_POWER)
+#define SLJIT_CONFIG_PPC_32 1
+#elif defined(__mips__) && !defined(_LP64)
+#define SLJIT_CONFIG_MIPS_32 1
+#elif defined(__mips64)
+#define SLJIT_CONFIG_MIPS_64 1
+#elif defined(__sparc__) || defined(__sparc)
+#define SLJIT_CONFIG_SPARC_32 1
+#elif defined(__tilegx__)
+#define SLJIT_CONFIG_TILEGX 1
+#else
+/* Unsupported architecture */
+#define SLJIT_CONFIG_UNSUPPORTED 1
+#endif
+
+#else /* !_WIN32 */
+
+#if defined(_M_X64) || defined(__x86_64__)
+#define SLJIT_CONFIG_X86_64 1
+#elif defined(_ARM_)
+#define SLJIT_CONFIG_ARM_V5 1
+#else
+#define SLJIT_CONFIG_X86_32 1
+#endif
+
+#endif /* !WIN32 */
+#endif /* SLJIT_CONFIG_AUTO */
+
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+#undef SLJIT_EXECUTABLE_ALLOCATOR
+#endif
+
+/******************************/
+/* CPU family type detection. */
+/******************************/
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+#define SLJIT_CONFIG_ARM_32 1
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+#define SLJIT_CONFIG_X86 1
+#elif (defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+#define SLJIT_CONFIG_ARM 1
+#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define SLJIT_CONFIG_PPC 1
+#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+#define SLJIT_CONFIG_MIPS 1
+#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) || (defined SLJIT_CONFIG_SPARC_64 && SLJIT_CONFIG_SPARC_64)
+#define SLJIT_CONFIG_SPARC 1
+#endif
+
+/**********************************/
+/* External function definitions. */
+/**********************************/
+
+#if !(defined SLJIT_STD_MACROS_DEFINED && SLJIT_STD_MACROS_DEFINED)
+
+/* These libraries are needed for the macros below. */
+#include
+#include
+
+#endif /* SLJIT_STD_MACROS_DEFINED */
+
+/* General macros:
+ Note: SLJIT is designed to be independent from them as possible.
+
+ In release mode (SLJIT_DEBUG is not defined) only the following
+ external functions are needed:
+*/
+
+#ifndef SLJIT_MALLOC
+#define SLJIT_MALLOC(size, allocator_data) malloc(size)
+#endif
+
+#ifndef SLJIT_FREE
+#define SLJIT_FREE(ptr, allocator_data) free(ptr)
+#endif
+
+#ifndef SLJIT_MEMMOVE
+#define SLJIT_MEMMOVE(dest, src, len) memmove(dest, src, len)
+#endif
+
+#ifndef SLJIT_ZEROMEM
+#define SLJIT_ZEROMEM(dest, len) memset(dest, 0, len)
+#endif
+
+/***************************/
+/* Compiler helper macros. */
+/***************************/
+
+#if !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY)
+
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define SLJIT_LIKELY(x) __builtin_expect((x), 1)
+#define SLJIT_UNLIKELY(x) __builtin_expect((x), 0)
+#else
+#define SLJIT_LIKELY(x) (x)
+#define SLJIT_UNLIKELY(x) (x)
+#endif
+
+#endif /* !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY) */
+
+#ifndef SLJIT_INLINE
+/* Inline functions. Some old compilers do not support them. */
+#if defined(__SUNPRO_C) && __SUNPRO_C <= 0x510
+#define SLJIT_INLINE
+#else
+#define SLJIT_INLINE __inline
+#endif
+#endif /* !SLJIT_INLINE */
+
+#ifndef SLJIT_NOINLINE
+/* Not inline functions. */
+#if defined(__GNUC__)
+#define SLJIT_NOINLINE __attribute__ ((noinline))
+#else
+#define SLJIT_NOINLINE
+#endif
+#endif /* !SLJIT_INLINE */
+
+#ifndef SLJIT_CONST
+/* Const variables. */
+#define SLJIT_CONST const
+#endif
+
+#ifndef SLJIT_UNUSED_ARG
+/* Unused arguments. */
+#define SLJIT_UNUSED_ARG(arg) (void)arg
+#endif
+
+/*********************************/
+/* Type of public API functions. */
+/*********************************/
+
+#if (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC)
+/* Static ABI functions. For all-in-one programs. */
+
+#if defined(__GNUC__)
+/* Disable unused warnings in gcc. */
+#define SLJIT_API_FUNC_ATTRIBUTE static __attribute__((unused))
+#else
+#define SLJIT_API_FUNC_ATTRIBUTE static
+#endif
+
+#else
+#define SLJIT_API_FUNC_ATTRIBUTE
+#endif /* (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC) */
+
+/****************************/
+/* Instruction cache flush. */
+/****************************/
+
+#ifndef SLJIT_CACHE_FLUSH
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+
+/* Not required to implement on archs with unified caches. */
+#define SLJIT_CACHE_FLUSH(from, to)
+
+#elif defined __APPLE__
+
+/* Supported by all macs since Mac OS 10.5.
+ However, it does not work on non-jailbroken iOS devices,
+ although the compilation is successful. */
+#include
+#define SLJIT_CACHE_FLUSH(from, to) \
+ sys_icache_invalidate((char*)(from), (char*)(to) - (char*)(from))
+
+#elif defined __ANDROID__
+
+/* Android lacks __clear_cache; instead, cacheflush should be used. */
+
+#define SLJIT_CACHE_FLUSH(from, to) \
+ cacheflush((long)(from), (long)(to), 0)
+
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+/* The __clear_cache() implementation of GCC is a dummy function on PowerPC. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ ppc_cache_flush((from), (to))
+
+#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+
+/* The __clear_cache() implementation of GCC is a dummy function on Sparc. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ sparc_cache_flush((from), (to))
+
+#else
+
+/* Calls __ARM_NR_cacheflush on ARM-Linux. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ __clear_cache((char*)(from), (char*)(to))
+
+#endif
+
+#endif /* !SLJIT_CACHE_FLUSH */
+
+/******************************************************/
+/* Byte/half/int/word/single/double type definitions. */
+/******************************************************/
+
+/* 8 bit byte type. */
+typedef unsigned char sljit_ub;
+typedef signed char sljit_sb;
+
+/* 16 bit half-word type. */
+typedef unsigned short int sljit_uh;
+typedef signed short int sljit_sh;
+
+/* 32 bit integer type. */
+typedef unsigned int sljit_ui;
+typedef signed int sljit_si;
+
+/* Machine word type. Enough for storing a pointer.
+ 32 bit for 32 bit machines.
+ 64 bit for 64 bit machines. */
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+/* Just to have something. */
+#define SLJIT_WORD_SHIFT 0
+typedef unsigned long int sljit_uw;
+typedef long int sljit_sw;
+#elif !(defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ && !(defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ && !(defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ && !(defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX)
+#define SLJIT_32BIT_ARCHITECTURE 1
+#define SLJIT_WORD_SHIFT 2
+typedef unsigned int sljit_uw;
+typedef int sljit_sw;
+#else
+#define SLJIT_64BIT_ARCHITECTURE 1
+#define SLJIT_WORD_SHIFT 3
+#ifdef _WIN32
+typedef unsigned __int64 sljit_uw;
+typedef __int64 sljit_sw;
+#else
+typedef unsigned long int sljit_uw;
+typedef long int sljit_sw;
+#endif
+#endif
+
+typedef sljit_uw sljit_p;
+
+/* Floating point types. */
+typedef float sljit_s;
+typedef double sljit_d;
+
+/* Shift for pointer sized data. */
+#define SLJIT_POINTER_SHIFT SLJIT_WORD_SHIFT
+
+/* Shift for double precision sized data. */
+#define SLJIT_DOUBLE_SHIFT 3
+#define SLJIT_SINGLE_SHIFT 2
+
+#ifndef SLJIT_W
+
+/* Defining long constants. */
+#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+#define SLJIT_W(w) (w##ll)
+#else
+#define SLJIT_W(w) (w)
+#endif
+
+#endif /* !SLJIT_W */
+
+/*************************/
+/* Endianness detection. */
+/*************************/
+
+#if !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN)
+
+/* These macros are mostly useful for the applications. */
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+
+#ifdef __LITTLE_ENDIAN__
+#define SLJIT_LITTLE_ENDIAN 1
+#else
+#define SLJIT_BIG_ENDIAN 1
+#endif
+
+#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+
+#ifdef __MIPSEL__
+#define SLJIT_LITTLE_ENDIAN 1
+#else
+#define SLJIT_BIG_ENDIAN 1
+#endif
+
+#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+
+#define SLJIT_BIG_ENDIAN 1
+
+#else
+#define SLJIT_LITTLE_ENDIAN 1
+#endif
+
+#endif /* !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN) */
+
+/* Sanity check. */
+#if (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#error "Exactly one endianness must be selected"
+#endif
+
+#if !(defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && !(defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#error "Exactly one endianness must be selected"
+#endif
+
+#ifndef SLJIT_UNALIGNED
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ || (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define SLJIT_UNALIGNED 1
+#endif
+
+#endif /* !SLJIT_UNALIGNED */
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+/* Auto detect SSE2 support using CPUID.
+ On 64 bit x86 cpus, sse2 must be present. */
+#define SLJIT_DETECT_SSE2 1
+#endif
+
+/*****************************************************************************************/
+/* Calling convention of functions generated by SLJIT or called from the generated code. */
+/*****************************************************************************************/
+
+#ifndef SLJIT_CALL
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+
+#if defined(__GNUC__) && !defined(__APPLE__)
+
+#define SLJIT_CALL __attribute__ ((fastcall))
+#define SLJIT_X86_32_FASTCALL 1
+
+#elif defined(_MSC_VER)
+
+#define SLJIT_CALL __fastcall
+#define SLJIT_X86_32_FASTCALL 1
+
+#elif defined(__BORLANDC__)
+
+#define SLJIT_CALL __msfastcall
+#define SLJIT_X86_32_FASTCALL 1
+
+#else /* Unknown compiler. */
+
+/* The cdecl attribute is the default. */
+#define SLJIT_CALL
+
+#endif
+
+#else /* Non x86-32 architectures. */
+
+#define SLJIT_CALL
+
+#endif /* SLJIT_CONFIG_X86_32 */
+
+#endif /* !SLJIT_CALL */
+
+#ifndef SLJIT_INDIRECT_CALL
+#if ((defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) && (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN)) \
+ || ((defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) && defined _AIX)
+/* It seems certain ppc compilers use an indirect addressing for functions
+ which makes things complicated. */
+#define SLJIT_INDIRECT_CALL 1
+#endif
+#endif /* SLJIT_INDIRECT_CALL */
+
+/* The offset which needs to be substracted from the return address to
+determine the next executed instruction after return. */
+#ifndef SLJIT_RETURN_ADDRESS_OFFSET
+#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+#define SLJIT_RETURN_ADDRESS_OFFSET 8
+#else
+#define SLJIT_RETURN_ADDRESS_OFFSET 0
+#endif
+#endif /* SLJIT_RETURN_ADDRESS_OFFSET */
+
+/***************************************************/
+/* Functions of the built-in executable allocator. */
+/***************************************************/
+
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void);
+#define SLJIT_MALLOC_EXEC(size) sljit_malloc_exec(size)
+#define SLJIT_FREE_EXEC(ptr) sljit_free_exec(ptr)
+#endif
+
+/**********************************************/
+/* Registers and locals offset determination. */
+/**********************************************/
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+
+#define SLJIT_NUMBER_OF_REGISTERS 10
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7
+#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
+#define SLJIT_LOCALS_OFFSET_BASE ((2 + 4) * sizeof(sljit_sw))
+#else
+/* Maximum 3 arguments are passed on the stack, +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((3 + 1 + 4) * sizeof(sljit_sw))
+#endif /* SLJIT_X86_32_FASTCALL */
+
+#elif (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+
+#ifndef _WIN64
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 6
+#define SLJIT_LOCALS_OFFSET_BASE (sizeof(sljit_sw))
+#else
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE ((4 + 2) * sizeof(sljit_sw))
+#endif /* _WIN64 */
+
+#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+
+#define SLJIT_NUMBER_OF_REGISTERS 11
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+
+#define SLJIT_NUMBER_OF_REGISTERS 11
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#elif (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+
+#define SLJIT_NUMBER_OF_REGISTERS 25
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 10
+#define SLJIT_LOCALS_OFFSET_BASE (2 * sizeof(sljit_sw))
+
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+#define SLJIT_NUMBER_OF_REGISTERS 22
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 17
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) || (defined _AIX)
+#define SLJIT_LOCALS_OFFSET_BASE ((6 + 8) * sizeof(sljit_sw))
+#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+/* Add +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((3 + 1) * sizeof(sljit_sw))
+#else
+#define SLJIT_LOCALS_OFFSET_BASE (3 * sizeof(sljit_sw))
+#endif /* SLJIT_CONFIG_PPC_64 || _AIX */
+
+#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+
+#define SLJIT_NUMBER_OF_REGISTERS 17
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#define SLJIT_LOCALS_OFFSET_BASE (4 * sizeof(sljit_sw))
+#else
+#define SLJIT_LOCALS_OFFSET_BASE 0
+#endif
+
+#elif (defined SLJIT_CONFIG_SPARC && SLJIT_CONFIG_SPARC)
+
+#define SLJIT_NUMBER_OF_REGISTERS 18
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 14
+#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+/* Add +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((23 + 1) * sizeof(sljit_sw))
+#endif
+
+#elif (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+
+#define SLJIT_NUMBER_OF_REGISTERS 0
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 0
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#endif
+
+#define SLJIT_LOCALS_OFFSET (SLJIT_LOCALS_OFFSET_BASE)
+
+#define SLJIT_NUMBER_OF_SCRATCH_REGISTERS \
+ (SLJIT_NUMBER_OF_REGISTERS - SLJIT_NUMBER_OF_SAVED_REGISTERS)
+
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 6
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) && (defined _WIN64)
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 1
+#else
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0
+#endif
+
+#define SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS \
+ (SLJIT_NUMBER_OF_FLOAT_REGISTERS - SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS)
+
+/*************************************/
+/* Debug and verbose related macros. */
+/*************************************/
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+#include
+#endif
+
+#if (defined SLJIT_DEBUG && SLJIT_DEBUG)
+
+#if !defined(SLJIT_ASSERT) || !defined(SLJIT_ASSERT_STOP)
+
+/* SLJIT_HALT_PROCESS must halt the process. */
+#ifndef SLJIT_HALT_PROCESS
+#include
+
+#define SLJIT_HALT_PROCESS() \
+ abort();
+#endif /* !SLJIT_HALT_PROCESS */
+
+#include
+
+#endif /* !SLJIT_ASSERT || !SLJIT_ASSERT_STOP */
+
+/* Feel free to redefine these two macros. */
+#ifndef SLJIT_ASSERT
+
+#define SLJIT_ASSERT(x) \
+ do { \
+ if (SLJIT_UNLIKELY(!(x))) { \
+ printf("Assertion failed at " __FILE__ ":%d\n", __LINE__); \
+ SLJIT_HALT_PROCESS(); \
+ } \
+ } while (0)
+
+#endif /* !SLJIT_ASSERT */
+
+#ifndef SLJIT_ASSERT_STOP
+
+#define SLJIT_ASSERT_STOP() \
+ do { \
+ printf("Should never been reached " __FILE__ ":%d\n", __LINE__); \
+ SLJIT_HALT_PROCESS(); \
+ } while (0)
+
+#endif /* !SLJIT_ASSERT_STOP */
+
+#else /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
+
+/* Forcing empty, but valid statements. */
+#undef SLJIT_ASSERT
+#undef SLJIT_ASSERT_STOP
+
+#define SLJIT_ASSERT(x) \
+ do { } while (0)
+#define SLJIT_ASSERT_STOP() \
+ do { } while (0)
+
+#endif /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
+
+#ifndef SLJIT_COMPILE_ASSERT
+
+/* Should be improved eventually. */
+#define SLJIT_COMPILE_ASSERT(x, description) \
+ SLJIT_ASSERT(x)
+
+#endif /* !SLJIT_COMPILE_ASSERT */
+
+#endif
diff --git a/plugins/php/versions/55/src/reentrancy.c b/plugins/php/versions/55/src/reentrancy.c
new file mode 100644
index 000000000..e7c5d7171
--- /dev/null
+++ b/plugins/php/versions/55/src/reentrancy.c
@@ -0,0 +1,450 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#include
+#include
+#include
+#ifdef HAVE_DIRENT_H
+#include
+#endif
+
+#include "php_reentrancy.h"
+#include "ext/standard/php_rand.h" /* for PHP_RAND_MAX */
+
+enum {
+ LOCALTIME_R,
+ CTIME_R,
+ ASCTIME_R,
+ GMTIME_R,
+ READDIR_R,
+ NUMBER_OF_LOCKS
+};
+
+#if defined(PHP_NEED_REENTRANCY)
+
+#include
+
+static MUTEX_T reentrant_locks[NUMBER_OF_LOCKS];
+
+#define local_lock(x) tsrm_mutex_lock(reentrant_locks[x])
+#define local_unlock(x) tsrm_mutex_unlock(reentrant_locks[x])
+
+#else
+
+#define local_lock(x)
+#define local_unlock(x)
+
+#endif
+
+#if defined(PHP_IRIX_TIME_R)
+
+#define HAVE_CTIME_R 1
+#define HAVE_ASCTIME_R 1
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ if (ctime_r(clock, buf) == buf)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ if (asctime_r(tm, buf) == buf)
+ return (buf);
+ return (NULL);
+}
+
+#endif
+
+#if defined(PHP_HPUX_TIME_R)
+
+#define HAVE_LOCALTIME_R 1
+#define HAVE_CTIME_R 1
+#define HAVE_ASCTIME_R 1
+#define HAVE_GMTIME_R 1
+
+PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ if (localtime_r(timep, p_tm) == 0)
+ return (p_tm);
+ return (NULL);
+}
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ if (ctime_r(clock, buf, 26) != -1)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ if (asctime_r(tm, buf, 26) != -1)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ if (gmtime_r(timep, p_tm) == 0)
+ return (p_tm);
+ return (NULL);
+}
+
+#endif
+
+#if defined(__BEOS__)
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ /* Modified according to LibC definition */
+ if (((struct tm*)gmtime_r(timep, p_tm)) == p_tm)
+ return (p_tm);
+ return (NULL);
+}
+
+#endif /* BEOS */
+
+#if !defined(HAVE_POSIX_READDIR_R)
+
+PHPAPI int php_readdir_r(DIR *dirp, struct dirent *entry,
+ struct dirent **result)
+{
+#if defined(HAVE_OLD_READDIR_R)
+ int ret = 0;
+
+ /* We cannot rely on the return value of readdir_r
+ as it differs between various platforms
+ (HPUX returns 0 on success whereas Solaris returns non-zero)
+ */
+ entry->d_name[0] = '\0';
+ readdir_r(dirp, entry, result);
+
+ if (entry->d_name[0] == '\0') {
+ *result = NULL;
+ ret = errno;
+ } else {
+ *result = entry;
+ }
+ return ret;
+#else
+ struct dirent *ptr;
+ int ret = 0;
+
+ local_lock(READDIR_R);
+
+ errno = 0;
+
+ ptr = readdir(dirp);
+
+ if (!ptr && errno != 0)
+ ret = errno;
+
+ if (ptr)
+ memcpy(entry, ptr, sizeof(*ptr));
+
+ *result = ptr;
+
+ local_unlock(READDIR_R);
+
+ return ret;
+#endif
+}
+
+#endif
+
+#if !defined(HAVE_LOCALTIME_R) && defined(HAVE_LOCALTIME)
+
+PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ struct tm *tmp;
+
+ local_lock(LOCALTIME_R);
+
+ tmp = localtime(timep);
+ if (tmp) {
+ memcpy(p_tm, tmp, sizeof(struct tm));
+ tmp = p_tm;
+ }
+
+ local_unlock(LOCALTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if !defined(HAVE_CTIME_R) && defined(HAVE_CTIME)
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ char *tmp;
+
+ local_lock(CTIME_R);
+
+ tmp = ctime(clock);
+ strcpy(buf, tmp);
+
+ local_unlock(CTIME_R);
+
+ return buf;
+}
+
+#endif
+
+#if !defined(HAVE_ASCTIME_R) && defined(HAVE_ASCTIME)
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ char *tmp;
+
+ local_lock(ASCTIME_R);
+
+ tmp = asctime(tm);
+ strcpy(buf, tmp);
+
+ local_unlock(ASCTIME_R);
+
+ return buf;
+}
+
+#endif
+
+#if !defined(HAVE_GMTIME_R) && defined(HAVE_GMTIME)
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ struct tm *tmp;
+
+ local_lock(GMTIME_R);
+
+ tmp = gmtime(timep);
+ if (tmp) {
+ memcpy(p_tm, tmp, sizeof(struct tm));
+ tmp = p_tm;
+ }
+
+ local_unlock(GMTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if defined(PHP_NEED_REENTRANCY)
+
+void reentrancy_startup(void)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_LOCKS; i++) {
+ reentrant_locks[i] = tsrm_mutex_alloc();
+ }
+}
+
+void reentrancy_shutdown(void)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_LOCKS; i++) {
+ tsrm_mutex_free(reentrant_locks[i]);
+ }
+}
+
+#endif
+
+#ifndef HAVE_RAND_R
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Posix rand_r function added May 1999 by Wes Peters .
+ */
+
+#include
+#include
+
+static int
+do_rand(unsigned long *ctx)
+{
+ return ((*ctx = *ctx * 1103515245 + 12345) % ((u_long)PHP_RAND_MAX + 1));
+}
+
+
+PHPAPI int
+php_rand_r(unsigned int *ctx)
+{
+ u_long val = (u_long) *ctx;
+ *ctx = do_rand(&val);
+ return (int) *ctx;
+}
+
+#endif
+
+
+#ifndef HAVE_STRTOK_R
+
+/*
+ * Copyright (c) 1998 Softweyr LLC. All rights reserved.
+ *
+ * strtok_r, from Berkeley strtok
+ * Oct 13, 1998 by Wes Peters
+ *
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notices, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notices, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Softweyr LLC, the
+ * University of California, Berkeley, and its contributors.
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SOFTWEYR LLC, THE REGENTS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTWEYR LLC, THE
+ * REGENTS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+
+PHPAPI char *
+php_strtok_r(char *s, const char *delim, char **last)
+{
+ char *spanp;
+ int c, sc;
+ char *tok;
+
+ if (s == NULL && (s = *last) == NULL)
+ {
+ return NULL;
+ }
+
+ /*
+ * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
+ */
+cont:
+ c = *s++;
+ for (spanp = (char *)delim; (sc = *spanp++) != 0; )
+ {
+ if (c == sc)
+ {
+ goto cont;
+ }
+ }
+
+ if (c == 0) /* no non-delimiter characters */
+ {
+ *last = NULL;
+ return NULL;
+ }
+ tok = s - 1;
+
+ /*
+ * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
+ * Note that delim must have one NUL; we stop if we see that, too.
+ */
+ for (;;)
+ {
+ c = *s++;
+ spanp = (char *)delim;
+ do
+ {
+ if ((sc = *spanp++) == c)
+ {
+ if (c == 0)
+ {
+ s = NULL;
+ }
+ else
+ {
+ char *w = s - 1;
+ *w = '\0';
+ }
+ *last = s;
+ return tok;
+ }
+ }
+ while (sc != 0);
+ }
+ /* NOTREACHED */
+}
+
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/plugins/php/versions/55/src/zend_multiply.h b/plugins/php/versions/55/src/zend_multiply.h
new file mode 100644
index 000000000..da96d12c6
--- /dev/null
+++ b/plugins/php/versions/55/src/zend_multiply.h
@@ -0,0 +1,97 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2015 Zend Technologies Ltd. (http://www.zend.com) |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.00 of the Zend license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.zend.com/license/2_00.txt. |
+ | If you did not receive a copy of the Zend license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@zend.com so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Sascha Schumann |
+ | Ard Biesheuvel |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#if defined(__i386__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__ ("imul %3,%0\n" \
+ "adc $0,%1" \
+ : "=r"(__tmpvar),"=r"(usedval) \
+ : "0"(a), "r"(b), "1"(0)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif defined(__x86_64__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__ ("imul %3,%0\n" \
+ "adc $0,%1" \
+ : "=r"(__tmpvar),"=r"(usedval) \
+ : "0"(a), "r"(b), "1"(0)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif defined(__arm__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__("smull %0, %1, %2, %3\n" \
+ "sub %1, %1, %0, asr #31" \
+ : "=r"(__tmpvar), "=r"(usedval) \
+ : "r"(a), "r"(b)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif defined(__aarch64__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__("mul %0, %2, %3\n" \
+ "smulh %1, %2, %3\n" \
+ "sub %1, %1, %0, asr #63\n" \
+ : "=&r"(__tmpvar), "=&r"(usedval) \
+ : "r"(a), "r"(b)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif SIZEOF_LONG == 4 && defined(HAVE_ZEND_LONG64)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ zend_long64 __result = (zend_long64) (a) * (zend_long64) (b); \
+ if (__result > LONG_MAX || __result < LONG_MIN) { \
+ (dval) = (double) __result; \
+ (usedval) = 1; \
+ } else { \
+ (lval) = (long) __result; \
+ (usedval) = 0; \
+ } \
+} while (0)
+
+#else
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __lres = (a) * (b); \
+ long double __dres = (long double)(a) * (long double)(b); \
+ long double __delta = (long double) __lres - __dres; \
+ if ( ((usedval) = (( __dres + __delta ) != __dres))) { \
+ (dval) = __dres; \
+ } else { \
+ (lval) = __lres; \
+ } \
+} while (0)
+
+#endif
diff --git a/plugins/php/versions/56/install.sh b/plugins/php/versions/56/install.sh
new file mode 100755
index 000000000..f89badba9
--- /dev/null
+++ b/plugins/php/versions/56/install.sh
@@ -0,0 +1,162 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+# cd /www/server/mdserver-web/plugins/php && /bin/bash install.sh install 56
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+SYS_ARCH=`arch`
+
+version=5.6.40
+PHP_VER=56
+md5_file_ok=c7dde3afb16ce7b761abf2805125d372
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-${version} ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+cd ${rootPath}/plugins/php/lib && /bin/bash freetype_old.sh
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://museum.php.net/php5/php-${version}.tar.xz
+ fi
+
+ #检测文件是否损坏.
+ if [ -f $sourcePath/php/php-${version}.tar.xz ];then
+ md5_file=`md5sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'`
+ if [ "${md5_file}" != "${md5_file_ok}" ]; then
+ echo "PHP${version} 下载文件不完整,重新安装"
+ rm -rf $sourcePath/php/php-${version}.tar.xz
+ fi
+ fi
+
+ cd $sourcePath/php && tar -Jxf $sourcePath/php/php-${version}.tar.xz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+fi
+
+OPTIONS='--without-iconv'
+if [ $sysName == 'Darwin' ]; then
+ OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype"
+ OPTIONS="${OPTIONS} --with-zlib-dir=$(brew --prefix zlib)"
+else
+ OPTIONS="${OPTIONS} --with-readline"
+fi
+
+IS_64BIT=`getconf LONG_BIT`
+if [ "$IS_64BIT" == "64" ];then
+ OPTIONS="${OPTIONS} --with-libdir=lib64"
+fi
+
+# ----- cpu start ------
+if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+fi
+
+if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+fi
+
+MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+else
+ cpuCore="1"
+fi
+
+if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+else
+ cpuCore="1"
+fi
+# ----- cpu end ------
+
+
+
+if [ "${SYS_ARCH}" == "aarch64" ];then
+ # 修复aarch64架构下安装
+ # /www/server/mdserver-web/plugins/php/versions/56/src/zend_multiply.h > /www/server/source/php/php56/Zend/zend_multiply.h
+ cat ${curPath}/versions/${PHP_VER}/src/zend_multiply.h > $sourcePath/php/php${PHP_VER}/Zend/zend_multiply.h
+fi
+
+
+if [ "${SYS_ARCH}" == "arm64" ] && [ "$sysName" == "Darwin" ] ;then
+ # 修复mac arm64架构下php安装
+ # 修复不能识别到sys_icache_invalidate
+ cat ${curPath}/versions/${PHP_VER}/src/ext/pcre/sljitConfigInternal.h > $sourcePath/php/php${PHP_VER}/ext/pcre/pcrelib/sljit/sljitConfigInternal.h
+ cat ${curPath}/versions/${PHP_VER}/src/reentrancy.c > $sourcePath/php/php${PHP_VER}/main/reentrancy.c
+ cat ${curPath}/versions/${PHP_VER}/src/mkstemp.c > $sourcePath/php/php${PHP_VER}/ext/zip/lib/mkstemp.c
+fi
+
+if [ ! -d $serverPath/php/${PHP_VER} ];then
+ cd $sourcePath/php/php${PHP_VER} && ./configure \
+ --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/56/etc \
+ --enable-mysqlnd \
+ --with-mysql=mysqlnd \
+ --with-pdo-mysql=mysqlnd \
+ --with-mysqli=mysqlnd \
+ --enable-mbstring \
+ --enable-simplexml \
+ --enable-ftp \
+ --enable-sockets \
+ --enable-shmop \
+ --enable-soap \
+ --enable-posix \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ --disable-intl \
+ --disable-fileinfo \
+ $OPTIONS \
+ --enable-fpm
+ make clean && make -j${cpuCore} && make install && make clean
+
+ # rm -rf $sourcePath/php/php${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php56 stop
+ rm -rf $serverPath/php/56
+ echo "卸载php-${version} ..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/56/src/ext/pcre/sljitConfigInternal.h b/plugins/php/versions/56/src/ext/pcre/sljitConfigInternal.h
new file mode 100644
index 000000000..23c33609d
--- /dev/null
+++ b/plugins/php/versions/56/src/ext/pcre/sljitConfigInternal.h
@@ -0,0 +1,702 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SLJIT_CONFIG_INTERNAL_H_
+#define _SLJIT_CONFIG_INTERNAL_H_
+
+/*
+ SLJIT defines the following architecture dependent types and macros:
+
+ Types:
+ sljit_sb, sljit_ub : signed and unsigned 8 bit byte
+ sljit_sh, sljit_uh : signed and unsigned 16 bit half-word (short) type
+ sljit_si, sljit_ui : signed and unsigned 32 bit integer type
+ sljit_sw, sljit_uw : signed and unsigned machine word, enough to store a pointer
+ sljit_p : unsgined pointer value (usually the same as sljit_uw, but
+ some 64 bit ABIs may use 32 bit pointers)
+ sljit_s : single precision floating point value
+ sljit_d : double precision floating point value
+
+ Macros for feature detection (boolean):
+ SLJIT_32BIT_ARCHITECTURE : 32 bit architecture
+ SLJIT_64BIT_ARCHITECTURE : 64 bit architecture
+ SLJIT_LITTLE_ENDIAN : little endian architecture
+ SLJIT_BIG_ENDIAN : big endian architecture
+ SLJIT_UNALIGNED : allows unaligned memory accesses for non-fpu operations (only!)
+ SLJIT_INDIRECT_CALL : see SLJIT_FUNC_OFFSET() for more information
+
+ Constants:
+ SLJIT_NUMBER_OF_REGISTERS : number of available registers
+ SLJIT_NUMBER_OF_SCRATCH_REGISTERS : number of available scratch registers
+ SLJIT_NUMBER_OF_SAVED_REGISTERS : number of available saved registers
+ SLJIT_NUMBER_OF_FLOAT_REGISTERS : number of available floating point registers
+ SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS : number of available floating point scratch registers
+ SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS : number of available floating point saved registers
+ SLJIT_WORD_SHIFT : the shift required to apply when accessing a sljit_sw/sljit_uw array by index
+ SLJIT_DOUBLE_SHIFT : the shift required to apply when accessing
+ a double precision floating point array by index
+ SLJIT_SINGLE_SHIFT : the shift required to apply when accessing
+ a single precision floating point array by index
+ SLJIT_LOCALS_OFFSET : local space starting offset (SLJIT_SP + SLJIT_LOCALS_OFFSET)
+ SLJIT_RETURN_ADDRESS_OFFSET : a return instruction always adds this offset to the return address
+
+ Other macros:
+ SLJIT_CALL : C calling convention define for both calling JIT form C and C callbacks for JIT
+ SLJIT_W(number) : defining 64 bit constants on 64 bit architectures (compiler independent helper)
+*/
+
+/*****************/
+/* Sanity check. */
+/*****************/
+
+#if !((defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ || (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \
+ || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ || (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ || (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ || (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \
+ || (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) \
+ || (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \
+ || (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED))
+#error "An architecture must be selected"
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ + (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ + (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \
+ + (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ + (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ + (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ + (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ + (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ + (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) \
+ + (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ + (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ + (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \
+ + (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \
+ + (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) >= 2
+#error "Multiple architectures are selected"
+#endif
+
+/********************************************************/
+/* Automatic CPU detection (requires compiler support). */
+/********************************************************/
+
+#if (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO)
+
+#ifndef _WIN32
+
+#if defined(__i386__) || defined(__i386)
+#define SLJIT_CONFIG_X86_32 1
+#elif defined(__x86_64__)
+#define SLJIT_CONFIG_X86_64 1
+#elif defined(__arm__) || defined(__ARM__)
+#ifdef __thumb2__
+#define SLJIT_CONFIG_ARM_THUMB2 1
+#elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__)
+#define SLJIT_CONFIG_ARM_V7 1
+#else
+#define SLJIT_CONFIG_ARM_V5 1
+#endif
+#elif defined (__aarch64__)
+#define SLJIT_CONFIG_ARM_64 1
+#elif defined(__ppc64__) || defined(__powerpc64__) || defined(_ARCH_PPC64) || (defined(_POWER) && defined(__64BIT__))
+#define SLJIT_CONFIG_PPC_64 1
+#elif defined(__ppc__) || defined(__powerpc__) || defined(_ARCH_PPC) || defined(_ARCH_PWR) || defined(_ARCH_PWR2) || defined(_POWER)
+#define SLJIT_CONFIG_PPC_32 1
+#elif defined(__mips__) && !defined(_LP64)
+#define SLJIT_CONFIG_MIPS_32 1
+#elif defined(__mips64)
+#define SLJIT_CONFIG_MIPS_64 1
+#elif defined(__sparc__) || defined(__sparc)
+#define SLJIT_CONFIG_SPARC_32 1
+#elif defined(__tilegx__)
+#define SLJIT_CONFIG_TILEGX 1
+#else
+/* Unsupported architecture */
+#define SLJIT_CONFIG_UNSUPPORTED 1
+#endif
+
+#else /* !_WIN32 */
+
+#if defined(_M_X64) || defined(__x86_64__)
+#define SLJIT_CONFIG_X86_64 1
+#elif defined(_ARM_)
+#define SLJIT_CONFIG_ARM_V5 1
+#else
+#define SLJIT_CONFIG_X86_32 1
+#endif
+
+#endif /* !WIN32 */
+#endif /* SLJIT_CONFIG_AUTO */
+
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+#undef SLJIT_EXECUTABLE_ALLOCATOR
+#endif
+
+/******************************/
+/* CPU family type detection. */
+/******************************/
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+#define SLJIT_CONFIG_ARM_32 1
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+#define SLJIT_CONFIG_X86 1
+#elif (defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+#define SLJIT_CONFIG_ARM 1
+#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define SLJIT_CONFIG_PPC 1
+#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+#define SLJIT_CONFIG_MIPS 1
+#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) || (defined SLJIT_CONFIG_SPARC_64 && SLJIT_CONFIG_SPARC_64)
+#define SLJIT_CONFIG_SPARC 1
+#endif
+
+/**********************************/
+/* External function definitions. */
+/**********************************/
+
+#if !(defined SLJIT_STD_MACROS_DEFINED && SLJIT_STD_MACROS_DEFINED)
+
+/* These libraries are needed for the macros below. */
+#include
+#include
+
+#endif /* SLJIT_STD_MACROS_DEFINED */
+
+/* General macros:
+ Note: SLJIT is designed to be independent from them as possible.
+
+ In release mode (SLJIT_DEBUG is not defined) only the following
+ external functions are needed:
+*/
+
+#ifndef SLJIT_MALLOC
+#define SLJIT_MALLOC(size, allocator_data) malloc(size)
+#endif
+
+#ifndef SLJIT_FREE
+#define SLJIT_FREE(ptr, allocator_data) free(ptr)
+#endif
+
+#ifndef SLJIT_MEMMOVE
+#define SLJIT_MEMMOVE(dest, src, len) memmove(dest, src, len)
+#endif
+
+#ifndef SLJIT_ZEROMEM
+#define SLJIT_ZEROMEM(dest, len) memset(dest, 0, len)
+#endif
+
+/***************************/
+/* Compiler helper macros. */
+/***************************/
+
+#if !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY)
+
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define SLJIT_LIKELY(x) __builtin_expect((x), 1)
+#define SLJIT_UNLIKELY(x) __builtin_expect((x), 0)
+#else
+#define SLJIT_LIKELY(x) (x)
+#define SLJIT_UNLIKELY(x) (x)
+#endif
+
+#endif /* !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY) */
+
+#ifndef SLJIT_INLINE
+/* Inline functions. Some old compilers do not support them. */
+#if defined(__SUNPRO_C) && __SUNPRO_C <= 0x510
+#define SLJIT_INLINE
+#else
+#define SLJIT_INLINE __inline
+#endif
+#endif /* !SLJIT_INLINE */
+
+#ifndef SLJIT_NOINLINE
+/* Not inline functions. */
+#if defined(__GNUC__)
+#define SLJIT_NOINLINE __attribute__ ((noinline))
+#else
+#define SLJIT_NOINLINE
+#endif
+#endif /* !SLJIT_INLINE */
+
+#ifndef SLJIT_CONST
+/* Const variables. */
+#define SLJIT_CONST const
+#endif
+
+#ifndef SLJIT_UNUSED_ARG
+/* Unused arguments. */
+#define SLJIT_UNUSED_ARG(arg) (void)arg
+#endif
+
+/*********************************/
+/* Type of public API functions. */
+/*********************************/
+
+#if (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC)
+/* Static ABI functions. For all-in-one programs. */
+
+#if defined(__GNUC__)
+/* Disable unused warnings in gcc. */
+#define SLJIT_API_FUNC_ATTRIBUTE static __attribute__((unused))
+#else
+#define SLJIT_API_FUNC_ATTRIBUTE static
+#endif
+
+#else
+#define SLJIT_API_FUNC_ATTRIBUTE
+#endif /* (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC) */
+
+/****************************/
+/* Instruction cache flush. */
+/****************************/
+
+#ifndef SLJIT_CACHE_FLUSH
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+
+/* Not required to implement on archs with unified caches. */
+#define SLJIT_CACHE_FLUSH(from, to)
+
+#elif defined __APPLE__
+
+/* Supported by all macs since Mac OS 10.5.
+ However, it does not work on non-jailbroken iOS devices,
+ although the compilation is successful. */
+#include
+#define SLJIT_CACHE_FLUSH(from, to) \
+ sys_icache_invalidate((char*)(from), (char*)(to) - (char*)(from))
+
+#elif defined __ANDROID__
+
+/* Android lacks __clear_cache; instead, cacheflush should be used. */
+
+#define SLJIT_CACHE_FLUSH(from, to) \
+ cacheflush((long)(from), (long)(to), 0)
+
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+/* The __clear_cache() implementation of GCC is a dummy function on PowerPC. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ ppc_cache_flush((from), (to))
+
+#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+
+/* The __clear_cache() implementation of GCC is a dummy function on Sparc. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ sparc_cache_flush((from), (to))
+
+#else
+
+/* Calls __ARM_NR_cacheflush on ARM-Linux. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ __clear_cache((char*)(from), (char*)(to))
+
+#endif
+
+#endif /* !SLJIT_CACHE_FLUSH */
+
+/******************************************************/
+/* Byte/half/int/word/single/double type definitions. */
+/******************************************************/
+
+/* 8 bit byte type. */
+typedef unsigned char sljit_ub;
+typedef signed char sljit_sb;
+
+/* 16 bit half-word type. */
+typedef unsigned short int sljit_uh;
+typedef signed short int sljit_sh;
+
+/* 32 bit integer type. */
+typedef unsigned int sljit_ui;
+typedef signed int sljit_si;
+
+/* Machine word type. Enough for storing a pointer.
+ 32 bit for 32 bit machines.
+ 64 bit for 64 bit machines. */
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+/* Just to have something. */
+#define SLJIT_WORD_SHIFT 0
+typedef unsigned long int sljit_uw;
+typedef long int sljit_sw;
+#elif !(defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ && !(defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ && !(defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ && !(defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX)
+#define SLJIT_32BIT_ARCHITECTURE 1
+#define SLJIT_WORD_SHIFT 2
+typedef unsigned int sljit_uw;
+typedef int sljit_sw;
+#else
+#define SLJIT_64BIT_ARCHITECTURE 1
+#define SLJIT_WORD_SHIFT 3
+#ifdef _WIN32
+typedef unsigned __int64 sljit_uw;
+typedef __int64 sljit_sw;
+#else
+typedef unsigned long int sljit_uw;
+typedef long int sljit_sw;
+#endif
+#endif
+
+typedef sljit_uw sljit_p;
+
+/* Floating point types. */
+typedef float sljit_s;
+typedef double sljit_d;
+
+/* Shift for pointer sized data. */
+#define SLJIT_POINTER_SHIFT SLJIT_WORD_SHIFT
+
+/* Shift for double precision sized data. */
+#define SLJIT_DOUBLE_SHIFT 3
+#define SLJIT_SINGLE_SHIFT 2
+
+#ifndef SLJIT_W
+
+/* Defining long constants. */
+#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+#define SLJIT_W(w) (w##ll)
+#else
+#define SLJIT_W(w) (w)
+#endif
+
+#endif /* !SLJIT_W */
+
+/*************************/
+/* Endianness detection. */
+/*************************/
+
+#if !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN)
+
+/* These macros are mostly useful for the applications. */
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+
+#ifdef __LITTLE_ENDIAN__
+#define SLJIT_LITTLE_ENDIAN 1
+#else
+#define SLJIT_BIG_ENDIAN 1
+#endif
+
+#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+
+#ifdef __MIPSEL__
+#define SLJIT_LITTLE_ENDIAN 1
+#else
+#define SLJIT_BIG_ENDIAN 1
+#endif
+
+#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+
+#define SLJIT_BIG_ENDIAN 1
+
+#else
+#define SLJIT_LITTLE_ENDIAN 1
+#endif
+
+#endif /* !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN) */
+
+/* Sanity check. */
+#if (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#error "Exactly one endianness must be selected"
+#endif
+
+#if !(defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && !(defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#error "Exactly one endianness must be selected"
+#endif
+
+#ifndef SLJIT_UNALIGNED
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ || (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define SLJIT_UNALIGNED 1
+#endif
+
+#endif /* !SLJIT_UNALIGNED */
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+/* Auto detect SSE2 support using CPUID.
+ On 64 bit x86 cpus, sse2 must be present. */
+#define SLJIT_DETECT_SSE2 1
+#endif
+
+/*****************************************************************************************/
+/* Calling convention of functions generated by SLJIT or called from the generated code. */
+/*****************************************************************************************/
+
+#ifndef SLJIT_CALL
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+
+#if defined(__GNUC__) && !defined(__APPLE__)
+
+#define SLJIT_CALL __attribute__ ((fastcall))
+#define SLJIT_X86_32_FASTCALL 1
+
+#elif defined(_MSC_VER)
+
+#define SLJIT_CALL __fastcall
+#define SLJIT_X86_32_FASTCALL 1
+
+#elif defined(__BORLANDC__)
+
+#define SLJIT_CALL __msfastcall
+#define SLJIT_X86_32_FASTCALL 1
+
+#else /* Unknown compiler. */
+
+/* The cdecl attribute is the default. */
+#define SLJIT_CALL
+
+#endif
+
+#else /* Non x86-32 architectures. */
+
+#define SLJIT_CALL
+
+#endif /* SLJIT_CONFIG_X86_32 */
+
+#endif /* !SLJIT_CALL */
+
+#ifndef SLJIT_INDIRECT_CALL
+#if ((defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) && (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN)) \
+ || ((defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) && defined _AIX)
+/* It seems certain ppc compilers use an indirect addressing for functions
+ which makes things complicated. */
+#define SLJIT_INDIRECT_CALL 1
+#endif
+#endif /* SLJIT_INDIRECT_CALL */
+
+/* The offset which needs to be substracted from the return address to
+determine the next executed instruction after return. */
+#ifndef SLJIT_RETURN_ADDRESS_OFFSET
+#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+#define SLJIT_RETURN_ADDRESS_OFFSET 8
+#else
+#define SLJIT_RETURN_ADDRESS_OFFSET 0
+#endif
+#endif /* SLJIT_RETURN_ADDRESS_OFFSET */
+
+/***************************************************/
+/* Functions of the built-in executable allocator. */
+/***************************************************/
+
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void);
+#define SLJIT_MALLOC_EXEC(size) sljit_malloc_exec(size)
+#define SLJIT_FREE_EXEC(ptr) sljit_free_exec(ptr)
+#endif
+
+/**********************************************/
+/* Registers and locals offset determination. */
+/**********************************************/
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+
+#define SLJIT_NUMBER_OF_REGISTERS 10
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7
+#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
+#define SLJIT_LOCALS_OFFSET_BASE ((2 + 4) * sizeof(sljit_sw))
+#else
+/* Maximum 3 arguments are passed on the stack, +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((3 + 1 + 4) * sizeof(sljit_sw))
+#endif /* SLJIT_X86_32_FASTCALL */
+
+#elif (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+
+#ifndef _WIN64
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 6
+#define SLJIT_LOCALS_OFFSET_BASE (sizeof(sljit_sw))
+#else
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE ((4 + 2) * sizeof(sljit_sw))
+#endif /* _WIN64 */
+
+#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+
+#define SLJIT_NUMBER_OF_REGISTERS 11
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+
+#define SLJIT_NUMBER_OF_REGISTERS 11
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#elif (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+
+#define SLJIT_NUMBER_OF_REGISTERS 25
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 10
+#define SLJIT_LOCALS_OFFSET_BASE (2 * sizeof(sljit_sw))
+
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+#define SLJIT_NUMBER_OF_REGISTERS 22
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 17
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) || (defined _AIX)
+#define SLJIT_LOCALS_OFFSET_BASE ((6 + 8) * sizeof(sljit_sw))
+#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+/* Add +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((3 + 1) * sizeof(sljit_sw))
+#else
+#define SLJIT_LOCALS_OFFSET_BASE (3 * sizeof(sljit_sw))
+#endif /* SLJIT_CONFIG_PPC_64 || _AIX */
+
+#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+
+#define SLJIT_NUMBER_OF_REGISTERS 17
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#define SLJIT_LOCALS_OFFSET_BASE (4 * sizeof(sljit_sw))
+#else
+#define SLJIT_LOCALS_OFFSET_BASE 0
+#endif
+
+#elif (defined SLJIT_CONFIG_SPARC && SLJIT_CONFIG_SPARC)
+
+#define SLJIT_NUMBER_OF_REGISTERS 18
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 14
+#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+/* Add +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((23 + 1) * sizeof(sljit_sw))
+#endif
+
+#elif (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+
+#define SLJIT_NUMBER_OF_REGISTERS 0
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 0
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#endif
+
+#define SLJIT_LOCALS_OFFSET (SLJIT_LOCALS_OFFSET_BASE)
+
+#define SLJIT_NUMBER_OF_SCRATCH_REGISTERS \
+ (SLJIT_NUMBER_OF_REGISTERS - SLJIT_NUMBER_OF_SAVED_REGISTERS)
+
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 6
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) && (defined _WIN64)
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 1
+#else
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0
+#endif
+
+#define SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS \
+ (SLJIT_NUMBER_OF_FLOAT_REGISTERS - SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS)
+
+/*************************************/
+/* Debug and verbose related macros. */
+/*************************************/
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+#include
+#endif
+
+#if (defined SLJIT_DEBUG && SLJIT_DEBUG)
+
+#if !defined(SLJIT_ASSERT) || !defined(SLJIT_ASSERT_STOP)
+
+/* SLJIT_HALT_PROCESS must halt the process. */
+#ifndef SLJIT_HALT_PROCESS
+#include
+
+#define SLJIT_HALT_PROCESS() \
+ abort();
+#endif /* !SLJIT_HALT_PROCESS */
+
+#include
+
+#endif /* !SLJIT_ASSERT || !SLJIT_ASSERT_STOP */
+
+/* Feel free to redefine these two macros. */
+#ifndef SLJIT_ASSERT
+
+#define SLJIT_ASSERT(x) \
+ do { \
+ if (SLJIT_UNLIKELY(!(x))) { \
+ printf("Assertion failed at " __FILE__ ":%d\n", __LINE__); \
+ SLJIT_HALT_PROCESS(); \
+ } \
+ } while (0)
+
+#endif /* !SLJIT_ASSERT */
+
+#ifndef SLJIT_ASSERT_STOP
+
+#define SLJIT_ASSERT_STOP() \
+ do { \
+ printf("Should never been reached " __FILE__ ":%d\n", __LINE__); \
+ SLJIT_HALT_PROCESS(); \
+ } while (0)
+
+#endif /* !SLJIT_ASSERT_STOP */
+
+#else /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
+
+/* Forcing empty, but valid statements. */
+#undef SLJIT_ASSERT
+#undef SLJIT_ASSERT_STOP
+
+#define SLJIT_ASSERT(x) \
+ do { } while (0)
+#define SLJIT_ASSERT_STOP() \
+ do { } while (0)
+
+#endif /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
+
+#ifndef SLJIT_COMPILE_ASSERT
+
+/* Should be improved eventually. */
+#define SLJIT_COMPILE_ASSERT(x, description) \
+ SLJIT_ASSERT(x)
+
+#endif /* !SLJIT_COMPILE_ASSERT */
+
+#endif
diff --git a/plugins/php/versions/56/src/mkstemp.c b/plugins/php/versions/56/src/mkstemp.c
new file mode 100644
index 000000000..d1df8a5ca
--- /dev/null
+++ b/plugins/php/versions/56/src/mkstemp.c
@@ -0,0 +1,153 @@
+/* Adapted from NetBSB libc by Dieter Baron */
+
+/* NetBSD: gettemp.c,v 1.13 2003/12/05 00:57:36 uebayasi Exp */
+
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#ifdef _WIN32
+#include
+#endif
+#include
+#include
+#ifndef _WIN32
+#include
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+
+int
+_zip_mkstemp(char *path)
+{
+#ifdef _WIN32
+ int ret;
+ ret = _creat(_mktemp(path), _S_IREAD|_S_IWRITE);
+ if (ret == -1) {
+ return 0;
+ } else {
+ return ret;
+ }
+#else
+ int fd;
+ char *start, *trv;
+ struct stat sbuf;
+ pid_t pid;
+
+ /* To guarantee multiple calls generate unique names even if
+ the file is not created. 676 different possibilities with 7
+ or more X's, 26 with 6 or less. */
+ static char xtra[2] = "aa";
+ int xcnt = 0;
+
+ pid = getpid();
+
+ /* Move to end of path and count trailing X's. */
+ for (trv = path; *trv; ++trv)
+ if (*trv == 'X')
+ xcnt++;
+ else
+ xcnt = 0;
+
+ /* Use at least one from xtra. Use 2 if more than 6 X's. */
+ if (*(trv - 1) == 'X')
+ *--trv = xtra[0];
+ if (xcnt > 6 && *(trv - 1) == 'X')
+ *--trv = xtra[1];
+
+ /* Set remaining X's to pid digits with 0's to the left. */
+ while (*--trv == 'X') {
+ *trv = (pid % 10) + '0';
+ pid /= 10;
+ }
+
+ /* update xtra for next call. */
+ if (xtra[0] != 'z')
+ xtra[0]++;
+ else {
+ xtra[0] = 'a';
+ if (xtra[1] != 'z')
+ xtra[1]++;
+ else
+ xtra[1] = 'a';
+ }
+
+ /*
+ * check the target directory; if you have six X's and it
+ * doesn't exist this runs for a *very* long time.
+ */
+ for (start = trv + 1;; --trv) {
+ if (trv <= path)
+ break;
+ if (*trv == '/') {
+ *trv = '\0';
+ if (stat(path, &sbuf))
+ return (0);
+ if (!S_ISDIR(sbuf.st_mode)) {
+ errno = ENOTDIR;
+ return (0);
+ }
+ *trv = '/';
+ break;
+ }
+ }
+
+ for (;;) {
+ if ((fd=open(path, O_CREAT|O_EXCL|O_RDWR|O_BINARY, 0600)) >= 0)
+ return (fd);
+ if (errno != EEXIST)
+ return (0);
+
+ /* tricky little algorithm for backward compatibility */
+ for (trv = start;;) {
+ if (!*trv)
+ return (0);
+ if (*trv == 'z')
+ *trv++ = 'a';
+ else {
+ if (isdigit((unsigned char)*trv))
+ *trv = 'a';
+ else
+ ++*trv;
+ break;
+ }
+ }
+ }
+ /*NOTREACHED*/
+#endif
+}
diff --git a/plugins/php/versions/56/src/reentrancy.c b/plugins/php/versions/56/src/reentrancy.c
new file mode 100644
index 000000000..e7c5d7171
--- /dev/null
+++ b/plugins/php/versions/56/src/reentrancy.c
@@ -0,0 +1,450 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#include
+#include
+#include
+#ifdef HAVE_DIRENT_H
+#include
+#endif
+
+#include "php_reentrancy.h"
+#include "ext/standard/php_rand.h" /* for PHP_RAND_MAX */
+
+enum {
+ LOCALTIME_R,
+ CTIME_R,
+ ASCTIME_R,
+ GMTIME_R,
+ READDIR_R,
+ NUMBER_OF_LOCKS
+};
+
+#if defined(PHP_NEED_REENTRANCY)
+
+#include
+
+static MUTEX_T reentrant_locks[NUMBER_OF_LOCKS];
+
+#define local_lock(x) tsrm_mutex_lock(reentrant_locks[x])
+#define local_unlock(x) tsrm_mutex_unlock(reentrant_locks[x])
+
+#else
+
+#define local_lock(x)
+#define local_unlock(x)
+
+#endif
+
+#if defined(PHP_IRIX_TIME_R)
+
+#define HAVE_CTIME_R 1
+#define HAVE_ASCTIME_R 1
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ if (ctime_r(clock, buf) == buf)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ if (asctime_r(tm, buf) == buf)
+ return (buf);
+ return (NULL);
+}
+
+#endif
+
+#if defined(PHP_HPUX_TIME_R)
+
+#define HAVE_LOCALTIME_R 1
+#define HAVE_CTIME_R 1
+#define HAVE_ASCTIME_R 1
+#define HAVE_GMTIME_R 1
+
+PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ if (localtime_r(timep, p_tm) == 0)
+ return (p_tm);
+ return (NULL);
+}
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ if (ctime_r(clock, buf, 26) != -1)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ if (asctime_r(tm, buf, 26) != -1)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ if (gmtime_r(timep, p_tm) == 0)
+ return (p_tm);
+ return (NULL);
+}
+
+#endif
+
+#if defined(__BEOS__)
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ /* Modified according to LibC definition */
+ if (((struct tm*)gmtime_r(timep, p_tm)) == p_tm)
+ return (p_tm);
+ return (NULL);
+}
+
+#endif /* BEOS */
+
+#if !defined(HAVE_POSIX_READDIR_R)
+
+PHPAPI int php_readdir_r(DIR *dirp, struct dirent *entry,
+ struct dirent **result)
+{
+#if defined(HAVE_OLD_READDIR_R)
+ int ret = 0;
+
+ /* We cannot rely on the return value of readdir_r
+ as it differs between various platforms
+ (HPUX returns 0 on success whereas Solaris returns non-zero)
+ */
+ entry->d_name[0] = '\0';
+ readdir_r(dirp, entry, result);
+
+ if (entry->d_name[0] == '\0') {
+ *result = NULL;
+ ret = errno;
+ } else {
+ *result = entry;
+ }
+ return ret;
+#else
+ struct dirent *ptr;
+ int ret = 0;
+
+ local_lock(READDIR_R);
+
+ errno = 0;
+
+ ptr = readdir(dirp);
+
+ if (!ptr && errno != 0)
+ ret = errno;
+
+ if (ptr)
+ memcpy(entry, ptr, sizeof(*ptr));
+
+ *result = ptr;
+
+ local_unlock(READDIR_R);
+
+ return ret;
+#endif
+}
+
+#endif
+
+#if !defined(HAVE_LOCALTIME_R) && defined(HAVE_LOCALTIME)
+
+PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ struct tm *tmp;
+
+ local_lock(LOCALTIME_R);
+
+ tmp = localtime(timep);
+ if (tmp) {
+ memcpy(p_tm, tmp, sizeof(struct tm));
+ tmp = p_tm;
+ }
+
+ local_unlock(LOCALTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if !defined(HAVE_CTIME_R) && defined(HAVE_CTIME)
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ char *tmp;
+
+ local_lock(CTIME_R);
+
+ tmp = ctime(clock);
+ strcpy(buf, tmp);
+
+ local_unlock(CTIME_R);
+
+ return buf;
+}
+
+#endif
+
+#if !defined(HAVE_ASCTIME_R) && defined(HAVE_ASCTIME)
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ char *tmp;
+
+ local_lock(ASCTIME_R);
+
+ tmp = asctime(tm);
+ strcpy(buf, tmp);
+
+ local_unlock(ASCTIME_R);
+
+ return buf;
+}
+
+#endif
+
+#if !defined(HAVE_GMTIME_R) && defined(HAVE_GMTIME)
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ struct tm *tmp;
+
+ local_lock(GMTIME_R);
+
+ tmp = gmtime(timep);
+ if (tmp) {
+ memcpy(p_tm, tmp, sizeof(struct tm));
+ tmp = p_tm;
+ }
+
+ local_unlock(GMTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if defined(PHP_NEED_REENTRANCY)
+
+void reentrancy_startup(void)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_LOCKS; i++) {
+ reentrant_locks[i] = tsrm_mutex_alloc();
+ }
+}
+
+void reentrancy_shutdown(void)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_LOCKS; i++) {
+ tsrm_mutex_free(reentrant_locks[i]);
+ }
+}
+
+#endif
+
+#ifndef HAVE_RAND_R
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Posix rand_r function added May 1999 by Wes Peters .
+ */
+
+#include
+#include
+
+static int
+do_rand(unsigned long *ctx)
+{
+ return ((*ctx = *ctx * 1103515245 + 12345) % ((u_long)PHP_RAND_MAX + 1));
+}
+
+
+PHPAPI int
+php_rand_r(unsigned int *ctx)
+{
+ u_long val = (u_long) *ctx;
+ *ctx = do_rand(&val);
+ return (int) *ctx;
+}
+
+#endif
+
+
+#ifndef HAVE_STRTOK_R
+
+/*
+ * Copyright (c) 1998 Softweyr LLC. All rights reserved.
+ *
+ * strtok_r, from Berkeley strtok
+ * Oct 13, 1998 by Wes Peters
+ *
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notices, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notices, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Softweyr LLC, the
+ * University of California, Berkeley, and its contributors.
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SOFTWEYR LLC, THE REGENTS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTWEYR LLC, THE
+ * REGENTS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+
+PHPAPI char *
+php_strtok_r(char *s, const char *delim, char **last)
+{
+ char *spanp;
+ int c, sc;
+ char *tok;
+
+ if (s == NULL && (s = *last) == NULL)
+ {
+ return NULL;
+ }
+
+ /*
+ * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
+ */
+cont:
+ c = *s++;
+ for (spanp = (char *)delim; (sc = *spanp++) != 0; )
+ {
+ if (c == sc)
+ {
+ goto cont;
+ }
+ }
+
+ if (c == 0) /* no non-delimiter characters */
+ {
+ *last = NULL;
+ return NULL;
+ }
+ tok = s - 1;
+
+ /*
+ * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
+ * Note that delim must have one NUL; we stop if we see that, too.
+ */
+ for (;;)
+ {
+ c = *s++;
+ spanp = (char *)delim;
+ do
+ {
+ if ((sc = *spanp++) == c)
+ {
+ if (c == 0)
+ {
+ s = NULL;
+ }
+ else
+ {
+ char *w = s - 1;
+ *w = '\0';
+ }
+ *last = s;
+ return tok;
+ }
+ }
+ while (sc != 0);
+ }
+ /* NOTREACHED */
+}
+
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/plugins/php/versions/56/src/zend_multiply.h b/plugins/php/versions/56/src/zend_multiply.h
new file mode 100644
index 000000000..8723551be
--- /dev/null
+++ b/plugins/php/versions/56/src/zend_multiply.h
@@ -0,0 +1,97 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 Zend Technologies Ltd. (http://www.zend.com) |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.00 of the Zend license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.zend.com/license/2_00.txt. |
+ | If you did not receive a copy of the Zend license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@zend.com so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Sascha Schumann |
+ | Ard Biesheuvel |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#if defined(__i386__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__ ("imul %3,%0\n" \
+ "adc $0,%1" \
+ : "=r"(__tmpvar),"=r"(usedval) \
+ : "0"(a), "r"(b), "1"(0)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif defined(__x86_64__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__ ("imul %3,%0\n" \
+ "adc $0,%1" \
+ : "=r"(__tmpvar),"=r"(usedval) \
+ : "0"(a), "r"(b), "1"(0)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif defined(__arm__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__("smull %0, %1, %2, %3\n" \
+ "sub %1, %1, %0, asr #31" \
+ : "=&r"(__tmpvar), "=&r"(usedval) \
+ : "r"(a), "r"(b)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif defined(__aarch64__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__("mul %0, %2, %3\n" \
+ "smulh %1, %2, %3\n" \
+ "sub %1, %1, %0, asr #63\n" \
+ : "=&r"(__tmpvar), "=&r"(usedval) \
+ : "r"(a), "r"(b)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif SIZEOF_LONG == 4 && defined(HAVE_ZEND_LONG64)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ zend_long64 __result = (zend_long64) (a) * (zend_long64) (b); \
+ if (__result > LONG_MAX || __result < LONG_MIN) { \
+ (dval) = (double) __result; \
+ (usedval) = 1; \
+ } else { \
+ (lval) = (long) __result; \
+ (usedval) = 0; \
+ } \
+} while (0)
+
+#else
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __lres = (a) * (b); \
+ long double __dres = (long double)(a) * (long double)(b); \
+ long double __delta = (long double) __lres - __dres; \
+ if ( ((usedval) = (( __dres + __delta ) != __dres))) { \
+ (dval) = __dres; \
+ } else { \
+ (lval) = __lres; \
+ } \
+} while (0)
+
+#endif
diff --git a/plugins/php/versions/56/zendguardloader.sh b/plugins/php/versions/56/zendguardloader.sh
new file mode 100755
index 000000000..89b87ae74
--- /dev/null
+++ b/plugins/php/versions/56/zendguardloader.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+LIBNAME=ZendGuardLoader
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ NON_ZTS_FILENAME=`ls $serverPath/php/${version}/lib/php/extensions | grep no-debug-non-zts`
+ extDir=$serverPath/php/${version}/lib/php/extensions/${NON_ZTS_FILENAME}
+ extFile=${extDir}/${LIBNAME}.so
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ $sysName == 'Darwin' ]; then
+ wget -O $php_lib/zend-loader-php5.6.tar.gz http://downloads.zend.com/guard/7.0.0/zend-loader-php5.6-darwin10.7-x86_64_update1.tar.gz
+ else
+ wget -O $php_lib/zend-loader-php5.6.tar.gz http://downloads.zend.com/guard/7.0.0/zend-loader-php5.6-linux-x86_64_update1.tar.gz
+ fi
+
+ cd $php_lib && tar xvf zend-loader-php5.6.tar.gz
+ cd zend-loader-php5.6-*
+ cp ZendGuardLoader.so ${extDir}
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo -e "[Zend ZendGuard Loader]\nzend_extension=ZendGuardLoader.so\nzend_loader.enable=1\nzend_loader.disable_licensing=0\nzend_loader.obfuscation_level_support=3\nzend_loader.license_path=" >> $serverPath/php/$version/etc/php.ini
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ extFile=$serverPath/php/${version}/lib/php/extensions/no-debug-non-zts-20131226/${LIBNAME}.so
+ if [ ! -f "$extFile" ];then
+ echo "php$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ sed -i $BAK '/ZendGuardLoader.so/d' $serverPath/php/$version/etc/php.ini
+ sed -i $BAK '/zend_loader/d' $serverPath/php/$version/etc/php.ini
+ sed -i $BAK '/\[Zend ZendGuard Loader\]/d' $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+actionType=$1
+version=$2
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/70/install.sh b/plugins/php/versions/70/install.sh
new file mode 100755
index 000000000..f97d34be7
--- /dev/null
+++ b/plugins/php/versions/70/install.sh
@@ -0,0 +1,165 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+SYS_ARCH=`arch`
+
+version=7.0.33
+PHP_VER=70
+md5_file_ok=a6d7c355d023301a1a9ec8b4f32a4856
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-${version} ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+cd ${rootPath}/plugins/php/lib && /bin/bash freetype_old.sh
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://museum.php.net/php7/php-${version}.tar.xz
+ fi
+
+ #检测文件是否损坏.
+ if [ -f $sourcePath/php/php-${version}.tar.xz ];then
+ md5_file=`md5sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'`
+ if [ "${md5_file}" != "${md5_file_ok}" ]; then
+ echo "PHP${version} 下载文件不完整,重新安装"
+ rm -rf $sourcePath/php/php-${version}.tar.xz
+ fi
+ fi
+
+ cd $sourcePath/php && tar -Jxf $sourcePath/php/php-${version}.tar.xz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+fi
+
+OPTIONS='--without-iconv'
+if [ $sysName == 'Darwin' ]; then
+ OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype"
+ # OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)"
+else
+ OPTIONS="${OPTIONS} --with-readline"
+fi
+
+IS_64BIT=`getconf LONG_BIT`
+if [ "$IS_64BIT" == "64" ];then
+ OPTIONS="${OPTIONS} --with-libdir=lib64"
+fi
+
+# ----- cpu start ------
+if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+fi
+
+if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+fi
+
+MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+else
+ cpuCore="1"
+fi
+
+if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+else
+ cpuCore="1"
+fi
+# ----- cpu end ------
+
+if [ "${SYS_ARCH}" == "arm64" ] && [ "$sysName" == "Darwin" ] ;then
+ # 修复mac arm64架构下php安装
+ # 修复不能识别到sys_icache_invalidate
+ cat ${curPath}/versions/${PHP_VER}/src/ext/pcre/sljitConfigInternal.h > $sourcePath/php/php${PHP_VER}/ext/pcre/pcrelib/sljit/sljitConfigInternal.h
+ cat ${curPath}/versions/${PHP_VER}/src/reentrancy.c > $sourcePath/php/php${PHP_VER}/main/reentrancy.c
+ cat ${curPath}/versions/${PHP_VER}/src/mkstemp.c > $sourcePath/php/php${PHP_VER}/ext/zip/lib/mkstemp.c
+fi
+
+if [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ LIB_DEPEND_DIR=`brew info openssl@1.0 | grep ${BREW_DIR}/Cellar/openssl@1.0 | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl@1.0)"
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include"
+ export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz"
+else
+ cd ${rootPath}/plugins/php/lib && /bin/bash openssl_10.sh
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl10/lib/pkgconfig
+ OPTIONS="$OPTIONS --with-openssl"
+fi
+
+if [ ! -d $serverPath/php/${PHP_VER} ];then
+ cd $sourcePath/php/php${PHP_VER} && ./configure \
+ --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \
+ --enable-mysqlnd \
+ --with-mysql=mysqlnd \
+ --with-mysqli=mysqlnd \
+ --with-pdo-mysql=mysqlnd \
+ --enable-mbstring \
+ --enable-simplexml \
+ --enable-ftp \
+ --enable-sockets \
+ --enable-soap \
+ --enable-posix \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ --disable-intl \
+ --disable-fileinfo \
+ $OPTIONS \
+ --enable-fpm
+ make clean && make -j${cpuCore} && make install && make clean
+
+ # rm -rf $sourcePath/php/php${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php70 stop
+ rm -rf $serverPath/php/70
+ echo "卸载php-7.0.30 ..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/70/src/ext/pcre/sljitConfigInternal.h b/plugins/php/versions/70/src/ext/pcre/sljitConfigInternal.h
new file mode 100644
index 000000000..23c33609d
--- /dev/null
+++ b/plugins/php/versions/70/src/ext/pcre/sljitConfigInternal.h
@@ -0,0 +1,702 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SLJIT_CONFIG_INTERNAL_H_
+#define _SLJIT_CONFIG_INTERNAL_H_
+
+/*
+ SLJIT defines the following architecture dependent types and macros:
+
+ Types:
+ sljit_sb, sljit_ub : signed and unsigned 8 bit byte
+ sljit_sh, sljit_uh : signed and unsigned 16 bit half-word (short) type
+ sljit_si, sljit_ui : signed and unsigned 32 bit integer type
+ sljit_sw, sljit_uw : signed and unsigned machine word, enough to store a pointer
+ sljit_p : unsgined pointer value (usually the same as sljit_uw, but
+ some 64 bit ABIs may use 32 bit pointers)
+ sljit_s : single precision floating point value
+ sljit_d : double precision floating point value
+
+ Macros for feature detection (boolean):
+ SLJIT_32BIT_ARCHITECTURE : 32 bit architecture
+ SLJIT_64BIT_ARCHITECTURE : 64 bit architecture
+ SLJIT_LITTLE_ENDIAN : little endian architecture
+ SLJIT_BIG_ENDIAN : big endian architecture
+ SLJIT_UNALIGNED : allows unaligned memory accesses for non-fpu operations (only!)
+ SLJIT_INDIRECT_CALL : see SLJIT_FUNC_OFFSET() for more information
+
+ Constants:
+ SLJIT_NUMBER_OF_REGISTERS : number of available registers
+ SLJIT_NUMBER_OF_SCRATCH_REGISTERS : number of available scratch registers
+ SLJIT_NUMBER_OF_SAVED_REGISTERS : number of available saved registers
+ SLJIT_NUMBER_OF_FLOAT_REGISTERS : number of available floating point registers
+ SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS : number of available floating point scratch registers
+ SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS : number of available floating point saved registers
+ SLJIT_WORD_SHIFT : the shift required to apply when accessing a sljit_sw/sljit_uw array by index
+ SLJIT_DOUBLE_SHIFT : the shift required to apply when accessing
+ a double precision floating point array by index
+ SLJIT_SINGLE_SHIFT : the shift required to apply when accessing
+ a single precision floating point array by index
+ SLJIT_LOCALS_OFFSET : local space starting offset (SLJIT_SP + SLJIT_LOCALS_OFFSET)
+ SLJIT_RETURN_ADDRESS_OFFSET : a return instruction always adds this offset to the return address
+
+ Other macros:
+ SLJIT_CALL : C calling convention define for both calling JIT form C and C callbacks for JIT
+ SLJIT_W(number) : defining 64 bit constants on 64 bit architectures (compiler independent helper)
+*/
+
+/*****************/
+/* Sanity check. */
+/*****************/
+
+#if !((defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ || (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \
+ || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ || (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ || (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ || (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \
+ || (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) \
+ || (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \
+ || (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED))
+#error "An architecture must be selected"
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ + (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ + (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \
+ + (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ + (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ + (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ + (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ + (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ + (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) \
+ + (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ + (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ + (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \
+ + (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \
+ + (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) >= 2
+#error "Multiple architectures are selected"
+#endif
+
+/********************************************************/
+/* Automatic CPU detection (requires compiler support). */
+/********************************************************/
+
+#if (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO)
+
+#ifndef _WIN32
+
+#if defined(__i386__) || defined(__i386)
+#define SLJIT_CONFIG_X86_32 1
+#elif defined(__x86_64__)
+#define SLJIT_CONFIG_X86_64 1
+#elif defined(__arm__) || defined(__ARM__)
+#ifdef __thumb2__
+#define SLJIT_CONFIG_ARM_THUMB2 1
+#elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__)
+#define SLJIT_CONFIG_ARM_V7 1
+#else
+#define SLJIT_CONFIG_ARM_V5 1
+#endif
+#elif defined (__aarch64__)
+#define SLJIT_CONFIG_ARM_64 1
+#elif defined(__ppc64__) || defined(__powerpc64__) || defined(_ARCH_PPC64) || (defined(_POWER) && defined(__64BIT__))
+#define SLJIT_CONFIG_PPC_64 1
+#elif defined(__ppc__) || defined(__powerpc__) || defined(_ARCH_PPC) || defined(_ARCH_PWR) || defined(_ARCH_PWR2) || defined(_POWER)
+#define SLJIT_CONFIG_PPC_32 1
+#elif defined(__mips__) && !defined(_LP64)
+#define SLJIT_CONFIG_MIPS_32 1
+#elif defined(__mips64)
+#define SLJIT_CONFIG_MIPS_64 1
+#elif defined(__sparc__) || defined(__sparc)
+#define SLJIT_CONFIG_SPARC_32 1
+#elif defined(__tilegx__)
+#define SLJIT_CONFIG_TILEGX 1
+#else
+/* Unsupported architecture */
+#define SLJIT_CONFIG_UNSUPPORTED 1
+#endif
+
+#else /* !_WIN32 */
+
+#if defined(_M_X64) || defined(__x86_64__)
+#define SLJIT_CONFIG_X86_64 1
+#elif defined(_ARM_)
+#define SLJIT_CONFIG_ARM_V5 1
+#else
+#define SLJIT_CONFIG_X86_32 1
+#endif
+
+#endif /* !WIN32 */
+#endif /* SLJIT_CONFIG_AUTO */
+
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+#undef SLJIT_EXECUTABLE_ALLOCATOR
+#endif
+
+/******************************/
+/* CPU family type detection. */
+/******************************/
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+#define SLJIT_CONFIG_ARM_32 1
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+#define SLJIT_CONFIG_X86 1
+#elif (defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+#define SLJIT_CONFIG_ARM 1
+#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define SLJIT_CONFIG_PPC 1
+#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+#define SLJIT_CONFIG_MIPS 1
+#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) || (defined SLJIT_CONFIG_SPARC_64 && SLJIT_CONFIG_SPARC_64)
+#define SLJIT_CONFIG_SPARC 1
+#endif
+
+/**********************************/
+/* External function definitions. */
+/**********************************/
+
+#if !(defined SLJIT_STD_MACROS_DEFINED && SLJIT_STD_MACROS_DEFINED)
+
+/* These libraries are needed for the macros below. */
+#include
+#include
+
+#endif /* SLJIT_STD_MACROS_DEFINED */
+
+/* General macros:
+ Note: SLJIT is designed to be independent from them as possible.
+
+ In release mode (SLJIT_DEBUG is not defined) only the following
+ external functions are needed:
+*/
+
+#ifndef SLJIT_MALLOC
+#define SLJIT_MALLOC(size, allocator_data) malloc(size)
+#endif
+
+#ifndef SLJIT_FREE
+#define SLJIT_FREE(ptr, allocator_data) free(ptr)
+#endif
+
+#ifndef SLJIT_MEMMOVE
+#define SLJIT_MEMMOVE(dest, src, len) memmove(dest, src, len)
+#endif
+
+#ifndef SLJIT_ZEROMEM
+#define SLJIT_ZEROMEM(dest, len) memset(dest, 0, len)
+#endif
+
+/***************************/
+/* Compiler helper macros. */
+/***************************/
+
+#if !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY)
+
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define SLJIT_LIKELY(x) __builtin_expect((x), 1)
+#define SLJIT_UNLIKELY(x) __builtin_expect((x), 0)
+#else
+#define SLJIT_LIKELY(x) (x)
+#define SLJIT_UNLIKELY(x) (x)
+#endif
+
+#endif /* !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY) */
+
+#ifndef SLJIT_INLINE
+/* Inline functions. Some old compilers do not support them. */
+#if defined(__SUNPRO_C) && __SUNPRO_C <= 0x510
+#define SLJIT_INLINE
+#else
+#define SLJIT_INLINE __inline
+#endif
+#endif /* !SLJIT_INLINE */
+
+#ifndef SLJIT_NOINLINE
+/* Not inline functions. */
+#if defined(__GNUC__)
+#define SLJIT_NOINLINE __attribute__ ((noinline))
+#else
+#define SLJIT_NOINLINE
+#endif
+#endif /* !SLJIT_INLINE */
+
+#ifndef SLJIT_CONST
+/* Const variables. */
+#define SLJIT_CONST const
+#endif
+
+#ifndef SLJIT_UNUSED_ARG
+/* Unused arguments. */
+#define SLJIT_UNUSED_ARG(arg) (void)arg
+#endif
+
+/*********************************/
+/* Type of public API functions. */
+/*********************************/
+
+#if (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC)
+/* Static ABI functions. For all-in-one programs. */
+
+#if defined(__GNUC__)
+/* Disable unused warnings in gcc. */
+#define SLJIT_API_FUNC_ATTRIBUTE static __attribute__((unused))
+#else
+#define SLJIT_API_FUNC_ATTRIBUTE static
+#endif
+
+#else
+#define SLJIT_API_FUNC_ATTRIBUTE
+#endif /* (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC) */
+
+/****************************/
+/* Instruction cache flush. */
+/****************************/
+
+#ifndef SLJIT_CACHE_FLUSH
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+
+/* Not required to implement on archs with unified caches. */
+#define SLJIT_CACHE_FLUSH(from, to)
+
+#elif defined __APPLE__
+
+/* Supported by all macs since Mac OS 10.5.
+ However, it does not work on non-jailbroken iOS devices,
+ although the compilation is successful. */
+#include
+#define SLJIT_CACHE_FLUSH(from, to) \
+ sys_icache_invalidate((char*)(from), (char*)(to) - (char*)(from))
+
+#elif defined __ANDROID__
+
+/* Android lacks __clear_cache; instead, cacheflush should be used. */
+
+#define SLJIT_CACHE_FLUSH(from, to) \
+ cacheflush((long)(from), (long)(to), 0)
+
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+/* The __clear_cache() implementation of GCC is a dummy function on PowerPC. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ ppc_cache_flush((from), (to))
+
+#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+
+/* The __clear_cache() implementation of GCC is a dummy function on Sparc. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ sparc_cache_flush((from), (to))
+
+#else
+
+/* Calls __ARM_NR_cacheflush on ARM-Linux. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ __clear_cache((char*)(from), (char*)(to))
+
+#endif
+
+#endif /* !SLJIT_CACHE_FLUSH */
+
+/******************************************************/
+/* Byte/half/int/word/single/double type definitions. */
+/******************************************************/
+
+/* 8 bit byte type. */
+typedef unsigned char sljit_ub;
+typedef signed char sljit_sb;
+
+/* 16 bit half-word type. */
+typedef unsigned short int sljit_uh;
+typedef signed short int sljit_sh;
+
+/* 32 bit integer type. */
+typedef unsigned int sljit_ui;
+typedef signed int sljit_si;
+
+/* Machine word type. Enough for storing a pointer.
+ 32 bit for 32 bit machines.
+ 64 bit for 64 bit machines. */
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+/* Just to have something. */
+#define SLJIT_WORD_SHIFT 0
+typedef unsigned long int sljit_uw;
+typedef long int sljit_sw;
+#elif !(defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ && !(defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ && !(defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ && !(defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX)
+#define SLJIT_32BIT_ARCHITECTURE 1
+#define SLJIT_WORD_SHIFT 2
+typedef unsigned int sljit_uw;
+typedef int sljit_sw;
+#else
+#define SLJIT_64BIT_ARCHITECTURE 1
+#define SLJIT_WORD_SHIFT 3
+#ifdef _WIN32
+typedef unsigned __int64 sljit_uw;
+typedef __int64 sljit_sw;
+#else
+typedef unsigned long int sljit_uw;
+typedef long int sljit_sw;
+#endif
+#endif
+
+typedef sljit_uw sljit_p;
+
+/* Floating point types. */
+typedef float sljit_s;
+typedef double sljit_d;
+
+/* Shift for pointer sized data. */
+#define SLJIT_POINTER_SHIFT SLJIT_WORD_SHIFT
+
+/* Shift for double precision sized data. */
+#define SLJIT_DOUBLE_SHIFT 3
+#define SLJIT_SINGLE_SHIFT 2
+
+#ifndef SLJIT_W
+
+/* Defining long constants. */
+#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+#define SLJIT_W(w) (w##ll)
+#else
+#define SLJIT_W(w) (w)
+#endif
+
+#endif /* !SLJIT_W */
+
+/*************************/
+/* Endianness detection. */
+/*************************/
+
+#if !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN)
+
+/* These macros are mostly useful for the applications. */
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+
+#ifdef __LITTLE_ENDIAN__
+#define SLJIT_LITTLE_ENDIAN 1
+#else
+#define SLJIT_BIG_ENDIAN 1
+#endif
+
+#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+
+#ifdef __MIPSEL__
+#define SLJIT_LITTLE_ENDIAN 1
+#else
+#define SLJIT_BIG_ENDIAN 1
+#endif
+
+#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+
+#define SLJIT_BIG_ENDIAN 1
+
+#else
+#define SLJIT_LITTLE_ENDIAN 1
+#endif
+
+#endif /* !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN) */
+
+/* Sanity check. */
+#if (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#error "Exactly one endianness must be selected"
+#endif
+
+#if !(defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && !(defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#error "Exactly one endianness must be selected"
+#endif
+
+#ifndef SLJIT_UNALIGNED
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ || (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define SLJIT_UNALIGNED 1
+#endif
+
+#endif /* !SLJIT_UNALIGNED */
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+/* Auto detect SSE2 support using CPUID.
+ On 64 bit x86 cpus, sse2 must be present. */
+#define SLJIT_DETECT_SSE2 1
+#endif
+
+/*****************************************************************************************/
+/* Calling convention of functions generated by SLJIT or called from the generated code. */
+/*****************************************************************************************/
+
+#ifndef SLJIT_CALL
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+
+#if defined(__GNUC__) && !defined(__APPLE__)
+
+#define SLJIT_CALL __attribute__ ((fastcall))
+#define SLJIT_X86_32_FASTCALL 1
+
+#elif defined(_MSC_VER)
+
+#define SLJIT_CALL __fastcall
+#define SLJIT_X86_32_FASTCALL 1
+
+#elif defined(__BORLANDC__)
+
+#define SLJIT_CALL __msfastcall
+#define SLJIT_X86_32_FASTCALL 1
+
+#else /* Unknown compiler. */
+
+/* The cdecl attribute is the default. */
+#define SLJIT_CALL
+
+#endif
+
+#else /* Non x86-32 architectures. */
+
+#define SLJIT_CALL
+
+#endif /* SLJIT_CONFIG_X86_32 */
+
+#endif /* !SLJIT_CALL */
+
+#ifndef SLJIT_INDIRECT_CALL
+#if ((defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) && (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN)) \
+ || ((defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) && defined _AIX)
+/* It seems certain ppc compilers use an indirect addressing for functions
+ which makes things complicated. */
+#define SLJIT_INDIRECT_CALL 1
+#endif
+#endif /* SLJIT_INDIRECT_CALL */
+
+/* The offset which needs to be substracted from the return address to
+determine the next executed instruction after return. */
+#ifndef SLJIT_RETURN_ADDRESS_OFFSET
+#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+#define SLJIT_RETURN_ADDRESS_OFFSET 8
+#else
+#define SLJIT_RETURN_ADDRESS_OFFSET 0
+#endif
+#endif /* SLJIT_RETURN_ADDRESS_OFFSET */
+
+/***************************************************/
+/* Functions of the built-in executable allocator. */
+/***************************************************/
+
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void);
+#define SLJIT_MALLOC_EXEC(size) sljit_malloc_exec(size)
+#define SLJIT_FREE_EXEC(ptr) sljit_free_exec(ptr)
+#endif
+
+/**********************************************/
+/* Registers and locals offset determination. */
+/**********************************************/
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+
+#define SLJIT_NUMBER_OF_REGISTERS 10
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7
+#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
+#define SLJIT_LOCALS_OFFSET_BASE ((2 + 4) * sizeof(sljit_sw))
+#else
+/* Maximum 3 arguments are passed on the stack, +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((3 + 1 + 4) * sizeof(sljit_sw))
+#endif /* SLJIT_X86_32_FASTCALL */
+
+#elif (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+
+#ifndef _WIN64
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 6
+#define SLJIT_LOCALS_OFFSET_BASE (sizeof(sljit_sw))
+#else
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE ((4 + 2) * sizeof(sljit_sw))
+#endif /* _WIN64 */
+
+#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+
+#define SLJIT_NUMBER_OF_REGISTERS 11
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+
+#define SLJIT_NUMBER_OF_REGISTERS 11
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#elif (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+
+#define SLJIT_NUMBER_OF_REGISTERS 25
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 10
+#define SLJIT_LOCALS_OFFSET_BASE (2 * sizeof(sljit_sw))
+
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+#define SLJIT_NUMBER_OF_REGISTERS 22
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 17
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) || (defined _AIX)
+#define SLJIT_LOCALS_OFFSET_BASE ((6 + 8) * sizeof(sljit_sw))
+#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+/* Add +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((3 + 1) * sizeof(sljit_sw))
+#else
+#define SLJIT_LOCALS_OFFSET_BASE (3 * sizeof(sljit_sw))
+#endif /* SLJIT_CONFIG_PPC_64 || _AIX */
+
+#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+
+#define SLJIT_NUMBER_OF_REGISTERS 17
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#define SLJIT_LOCALS_OFFSET_BASE (4 * sizeof(sljit_sw))
+#else
+#define SLJIT_LOCALS_OFFSET_BASE 0
+#endif
+
+#elif (defined SLJIT_CONFIG_SPARC && SLJIT_CONFIG_SPARC)
+
+#define SLJIT_NUMBER_OF_REGISTERS 18
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 14
+#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+/* Add +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((23 + 1) * sizeof(sljit_sw))
+#endif
+
+#elif (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+
+#define SLJIT_NUMBER_OF_REGISTERS 0
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 0
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#endif
+
+#define SLJIT_LOCALS_OFFSET (SLJIT_LOCALS_OFFSET_BASE)
+
+#define SLJIT_NUMBER_OF_SCRATCH_REGISTERS \
+ (SLJIT_NUMBER_OF_REGISTERS - SLJIT_NUMBER_OF_SAVED_REGISTERS)
+
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 6
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) && (defined _WIN64)
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 1
+#else
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0
+#endif
+
+#define SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS \
+ (SLJIT_NUMBER_OF_FLOAT_REGISTERS - SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS)
+
+/*************************************/
+/* Debug and verbose related macros. */
+/*************************************/
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+#include
+#endif
+
+#if (defined SLJIT_DEBUG && SLJIT_DEBUG)
+
+#if !defined(SLJIT_ASSERT) || !defined(SLJIT_ASSERT_STOP)
+
+/* SLJIT_HALT_PROCESS must halt the process. */
+#ifndef SLJIT_HALT_PROCESS
+#include
+
+#define SLJIT_HALT_PROCESS() \
+ abort();
+#endif /* !SLJIT_HALT_PROCESS */
+
+#include
+
+#endif /* !SLJIT_ASSERT || !SLJIT_ASSERT_STOP */
+
+/* Feel free to redefine these two macros. */
+#ifndef SLJIT_ASSERT
+
+#define SLJIT_ASSERT(x) \
+ do { \
+ if (SLJIT_UNLIKELY(!(x))) { \
+ printf("Assertion failed at " __FILE__ ":%d\n", __LINE__); \
+ SLJIT_HALT_PROCESS(); \
+ } \
+ } while (0)
+
+#endif /* !SLJIT_ASSERT */
+
+#ifndef SLJIT_ASSERT_STOP
+
+#define SLJIT_ASSERT_STOP() \
+ do { \
+ printf("Should never been reached " __FILE__ ":%d\n", __LINE__); \
+ SLJIT_HALT_PROCESS(); \
+ } while (0)
+
+#endif /* !SLJIT_ASSERT_STOP */
+
+#else /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
+
+/* Forcing empty, but valid statements. */
+#undef SLJIT_ASSERT
+#undef SLJIT_ASSERT_STOP
+
+#define SLJIT_ASSERT(x) \
+ do { } while (0)
+#define SLJIT_ASSERT_STOP() \
+ do { } while (0)
+
+#endif /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
+
+#ifndef SLJIT_COMPILE_ASSERT
+
+/* Should be improved eventually. */
+#define SLJIT_COMPILE_ASSERT(x, description) \
+ SLJIT_ASSERT(x)
+
+#endif /* !SLJIT_COMPILE_ASSERT */
+
+#endif
diff --git a/plugins/php/versions/70/src/mkstemp.c b/plugins/php/versions/70/src/mkstemp.c
new file mode 100644
index 000000000..d1df8a5ca
--- /dev/null
+++ b/plugins/php/versions/70/src/mkstemp.c
@@ -0,0 +1,153 @@
+/* Adapted from NetBSB libc by Dieter Baron */
+
+/* NetBSD: gettemp.c,v 1.13 2003/12/05 00:57:36 uebayasi Exp */
+
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#ifdef _WIN32
+#include
+#endif
+#include
+#include
+#ifndef _WIN32
+#include
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+
+int
+_zip_mkstemp(char *path)
+{
+#ifdef _WIN32
+ int ret;
+ ret = _creat(_mktemp(path), _S_IREAD|_S_IWRITE);
+ if (ret == -1) {
+ return 0;
+ } else {
+ return ret;
+ }
+#else
+ int fd;
+ char *start, *trv;
+ struct stat sbuf;
+ pid_t pid;
+
+ /* To guarantee multiple calls generate unique names even if
+ the file is not created. 676 different possibilities with 7
+ or more X's, 26 with 6 or less. */
+ static char xtra[2] = "aa";
+ int xcnt = 0;
+
+ pid = getpid();
+
+ /* Move to end of path and count trailing X's. */
+ for (trv = path; *trv; ++trv)
+ if (*trv == 'X')
+ xcnt++;
+ else
+ xcnt = 0;
+
+ /* Use at least one from xtra. Use 2 if more than 6 X's. */
+ if (*(trv - 1) == 'X')
+ *--trv = xtra[0];
+ if (xcnt > 6 && *(trv - 1) == 'X')
+ *--trv = xtra[1];
+
+ /* Set remaining X's to pid digits with 0's to the left. */
+ while (*--trv == 'X') {
+ *trv = (pid % 10) + '0';
+ pid /= 10;
+ }
+
+ /* update xtra for next call. */
+ if (xtra[0] != 'z')
+ xtra[0]++;
+ else {
+ xtra[0] = 'a';
+ if (xtra[1] != 'z')
+ xtra[1]++;
+ else
+ xtra[1] = 'a';
+ }
+
+ /*
+ * check the target directory; if you have six X's and it
+ * doesn't exist this runs for a *very* long time.
+ */
+ for (start = trv + 1;; --trv) {
+ if (trv <= path)
+ break;
+ if (*trv == '/') {
+ *trv = '\0';
+ if (stat(path, &sbuf))
+ return (0);
+ if (!S_ISDIR(sbuf.st_mode)) {
+ errno = ENOTDIR;
+ return (0);
+ }
+ *trv = '/';
+ break;
+ }
+ }
+
+ for (;;) {
+ if ((fd=open(path, O_CREAT|O_EXCL|O_RDWR|O_BINARY, 0600)) >= 0)
+ return (fd);
+ if (errno != EEXIST)
+ return (0);
+
+ /* tricky little algorithm for backward compatibility */
+ for (trv = start;;) {
+ if (!*trv)
+ return (0);
+ if (*trv == 'z')
+ *trv++ = 'a';
+ else {
+ if (isdigit((unsigned char)*trv))
+ *trv = 'a';
+ else
+ ++*trv;
+ break;
+ }
+ }
+ }
+ /*NOTREACHED*/
+#endif
+}
diff --git a/plugins/php/versions/70/src/reentrancy.c b/plugins/php/versions/70/src/reentrancy.c
new file mode 100644
index 000000000..e7c5d7171
--- /dev/null
+++ b/plugins/php/versions/70/src/reentrancy.c
@@ -0,0 +1,450 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#include
+#include
+#include
+#ifdef HAVE_DIRENT_H
+#include
+#endif
+
+#include "php_reentrancy.h"
+#include "ext/standard/php_rand.h" /* for PHP_RAND_MAX */
+
+enum {
+ LOCALTIME_R,
+ CTIME_R,
+ ASCTIME_R,
+ GMTIME_R,
+ READDIR_R,
+ NUMBER_OF_LOCKS
+};
+
+#if defined(PHP_NEED_REENTRANCY)
+
+#include
+
+static MUTEX_T reentrant_locks[NUMBER_OF_LOCKS];
+
+#define local_lock(x) tsrm_mutex_lock(reentrant_locks[x])
+#define local_unlock(x) tsrm_mutex_unlock(reentrant_locks[x])
+
+#else
+
+#define local_lock(x)
+#define local_unlock(x)
+
+#endif
+
+#if defined(PHP_IRIX_TIME_R)
+
+#define HAVE_CTIME_R 1
+#define HAVE_ASCTIME_R 1
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ if (ctime_r(clock, buf) == buf)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ if (asctime_r(tm, buf) == buf)
+ return (buf);
+ return (NULL);
+}
+
+#endif
+
+#if defined(PHP_HPUX_TIME_R)
+
+#define HAVE_LOCALTIME_R 1
+#define HAVE_CTIME_R 1
+#define HAVE_ASCTIME_R 1
+#define HAVE_GMTIME_R 1
+
+PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ if (localtime_r(timep, p_tm) == 0)
+ return (p_tm);
+ return (NULL);
+}
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ if (ctime_r(clock, buf, 26) != -1)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ if (asctime_r(tm, buf, 26) != -1)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ if (gmtime_r(timep, p_tm) == 0)
+ return (p_tm);
+ return (NULL);
+}
+
+#endif
+
+#if defined(__BEOS__)
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ /* Modified according to LibC definition */
+ if (((struct tm*)gmtime_r(timep, p_tm)) == p_tm)
+ return (p_tm);
+ return (NULL);
+}
+
+#endif /* BEOS */
+
+#if !defined(HAVE_POSIX_READDIR_R)
+
+PHPAPI int php_readdir_r(DIR *dirp, struct dirent *entry,
+ struct dirent **result)
+{
+#if defined(HAVE_OLD_READDIR_R)
+ int ret = 0;
+
+ /* We cannot rely on the return value of readdir_r
+ as it differs between various platforms
+ (HPUX returns 0 on success whereas Solaris returns non-zero)
+ */
+ entry->d_name[0] = '\0';
+ readdir_r(dirp, entry, result);
+
+ if (entry->d_name[0] == '\0') {
+ *result = NULL;
+ ret = errno;
+ } else {
+ *result = entry;
+ }
+ return ret;
+#else
+ struct dirent *ptr;
+ int ret = 0;
+
+ local_lock(READDIR_R);
+
+ errno = 0;
+
+ ptr = readdir(dirp);
+
+ if (!ptr && errno != 0)
+ ret = errno;
+
+ if (ptr)
+ memcpy(entry, ptr, sizeof(*ptr));
+
+ *result = ptr;
+
+ local_unlock(READDIR_R);
+
+ return ret;
+#endif
+}
+
+#endif
+
+#if !defined(HAVE_LOCALTIME_R) && defined(HAVE_LOCALTIME)
+
+PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ struct tm *tmp;
+
+ local_lock(LOCALTIME_R);
+
+ tmp = localtime(timep);
+ if (tmp) {
+ memcpy(p_tm, tmp, sizeof(struct tm));
+ tmp = p_tm;
+ }
+
+ local_unlock(LOCALTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if !defined(HAVE_CTIME_R) && defined(HAVE_CTIME)
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ char *tmp;
+
+ local_lock(CTIME_R);
+
+ tmp = ctime(clock);
+ strcpy(buf, tmp);
+
+ local_unlock(CTIME_R);
+
+ return buf;
+}
+
+#endif
+
+#if !defined(HAVE_ASCTIME_R) && defined(HAVE_ASCTIME)
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ char *tmp;
+
+ local_lock(ASCTIME_R);
+
+ tmp = asctime(tm);
+ strcpy(buf, tmp);
+
+ local_unlock(ASCTIME_R);
+
+ return buf;
+}
+
+#endif
+
+#if !defined(HAVE_GMTIME_R) && defined(HAVE_GMTIME)
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ struct tm *tmp;
+
+ local_lock(GMTIME_R);
+
+ tmp = gmtime(timep);
+ if (tmp) {
+ memcpy(p_tm, tmp, sizeof(struct tm));
+ tmp = p_tm;
+ }
+
+ local_unlock(GMTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if defined(PHP_NEED_REENTRANCY)
+
+void reentrancy_startup(void)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_LOCKS; i++) {
+ reentrant_locks[i] = tsrm_mutex_alloc();
+ }
+}
+
+void reentrancy_shutdown(void)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_LOCKS; i++) {
+ tsrm_mutex_free(reentrant_locks[i]);
+ }
+}
+
+#endif
+
+#ifndef HAVE_RAND_R
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Posix rand_r function added May 1999 by Wes Peters .
+ */
+
+#include
+#include
+
+static int
+do_rand(unsigned long *ctx)
+{
+ return ((*ctx = *ctx * 1103515245 + 12345) % ((u_long)PHP_RAND_MAX + 1));
+}
+
+
+PHPAPI int
+php_rand_r(unsigned int *ctx)
+{
+ u_long val = (u_long) *ctx;
+ *ctx = do_rand(&val);
+ return (int) *ctx;
+}
+
+#endif
+
+
+#ifndef HAVE_STRTOK_R
+
+/*
+ * Copyright (c) 1998 Softweyr LLC. All rights reserved.
+ *
+ * strtok_r, from Berkeley strtok
+ * Oct 13, 1998 by Wes Peters
+ *
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notices, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notices, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Softweyr LLC, the
+ * University of California, Berkeley, and its contributors.
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SOFTWEYR LLC, THE REGENTS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTWEYR LLC, THE
+ * REGENTS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+
+PHPAPI char *
+php_strtok_r(char *s, const char *delim, char **last)
+{
+ char *spanp;
+ int c, sc;
+ char *tok;
+
+ if (s == NULL && (s = *last) == NULL)
+ {
+ return NULL;
+ }
+
+ /*
+ * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
+ */
+cont:
+ c = *s++;
+ for (spanp = (char *)delim; (sc = *spanp++) != 0; )
+ {
+ if (c == sc)
+ {
+ goto cont;
+ }
+ }
+
+ if (c == 0) /* no non-delimiter characters */
+ {
+ *last = NULL;
+ return NULL;
+ }
+ tok = s - 1;
+
+ /*
+ * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
+ * Note that delim must have one NUL; we stop if we see that, too.
+ */
+ for (;;)
+ {
+ c = *s++;
+ spanp = (char *)delim;
+ do
+ {
+ if ((sc = *spanp++) == c)
+ {
+ if (c == 0)
+ {
+ s = NULL;
+ }
+ else
+ {
+ char *w = s - 1;
+ *w = '\0';
+ }
+ *last = s;
+ return tok;
+ }
+ }
+ while (sc != 0);
+ }
+ /* NOTREACHED */
+}
+
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/plugins/php/versions/70/src/zend_multiply.h b/plugins/php/versions/70/src/zend_multiply.h
new file mode 100644
index 000000000..8723551be
--- /dev/null
+++ b/plugins/php/versions/70/src/zend_multiply.h
@@ -0,0 +1,97 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 Zend Technologies Ltd. (http://www.zend.com) |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.00 of the Zend license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.zend.com/license/2_00.txt. |
+ | If you did not receive a copy of the Zend license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@zend.com so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Sascha Schumann |
+ | Ard Biesheuvel |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#if defined(__i386__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__ ("imul %3,%0\n" \
+ "adc $0,%1" \
+ : "=r"(__tmpvar),"=r"(usedval) \
+ : "0"(a), "r"(b), "1"(0)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif defined(__x86_64__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__ ("imul %3,%0\n" \
+ "adc $0,%1" \
+ : "=r"(__tmpvar),"=r"(usedval) \
+ : "0"(a), "r"(b), "1"(0)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif defined(__arm__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__("smull %0, %1, %2, %3\n" \
+ "sub %1, %1, %0, asr #31" \
+ : "=&r"(__tmpvar), "=&r"(usedval) \
+ : "r"(a), "r"(b)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif defined(__aarch64__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__("mul %0, %2, %3\n" \
+ "smulh %1, %2, %3\n" \
+ "sub %1, %1, %0, asr #63\n" \
+ : "=&r"(__tmpvar), "=&r"(usedval) \
+ : "r"(a), "r"(b)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif SIZEOF_LONG == 4 && defined(HAVE_ZEND_LONG64)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ zend_long64 __result = (zend_long64) (a) * (zend_long64) (b); \
+ if (__result > LONG_MAX || __result < LONG_MIN) { \
+ (dval) = (double) __result; \
+ (usedval) = 1; \
+ } else { \
+ (lval) = (long) __result; \
+ (usedval) = 0; \
+ } \
+} while (0)
+
+#else
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __lres = (a) * (b); \
+ long double __dres = (long double)(a) * (long double)(b); \
+ long double __delta = (long double) __lres - __dres; \
+ if ( ((usedval) = (( __dres + __delta ) != __dres))) { \
+ (dval) = __dres; \
+ } else { \
+ (lval) = __lres; \
+ } \
+} while (0)
+
+#endif
diff --git a/plugins/php/versions/71/install.sh b/plugins/php/versions/71/install.sh
new file mode 100755
index 000000000..e57cb7f5b
--- /dev/null
+++ b/plugins/php/versions/71/install.sh
@@ -0,0 +1,166 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+SYS_ARCH=`arch`
+
+version=7.1.33
+PHP_VER=71
+
+md5_file_ok=b27bb5ce72995bc0ea56276e156be889
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-${version} ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+cd ${rootPath}/plugins/php/lib && /bin/bash freetype_old.sh
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://museum.php.net/php7/php-${version}.tar.xz
+ fi
+
+ #检测文件是否损坏.
+ if [ -f $sourcePath/php/php-${version}.tar.xz ];then
+ md5_file=`md5sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'`
+ if [ "${md5_file}" != "${md5_file_ok}" ]; then
+ echo "PHP${version} 下载文件不完整,重新安装"
+ rm -rf $sourcePath/php/php-${version}.tar.xz
+ fi
+ fi
+
+ cd $sourcePath/php && tar -Jxf $sourcePath/php/php-${version}.tar.xz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+fi
+
+OPTIONS=''
+OPTIONS='--without-iconv'
+if [ $sysName == 'Darwin' ]; then
+ OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype"
+ OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)"
+else
+ OPTIONS="${OPTIONS} --with-readline"
+fi
+
+IS_64BIT=`getconf LONG_BIT`
+if [ "$IS_64BIT" == "64" ];then
+ OPTIONS="${OPTIONS} --with-libdir=lib64"
+fi
+
+# ----- cpu start ------
+if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+fi
+
+if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+fi
+
+MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+else
+ cpuCore="1"
+fi
+
+if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+else
+ cpuCore="1"
+fi
+# ----- cpu end ------
+
+
+if [ "${SYS_ARCH}" == "arm64" ] && [ "$sysName" == "Darwin" ] ;then
+ # 修复mac arm64架构下php安装
+ # 修复不能识别到sys_icache_invalidate
+ cat ${curPath}/versions/${PHP_VER}/src/ext/pcre/sljitConfigInternal.h > $sourcePath/php/php${PHP_VER}/ext/pcre/pcrelib/sljit/sljitConfigInternal.h
+ cat ${curPath}/versions/${PHP_VER}/src/reentrancy.c > $sourcePath/php/php${PHP_VER}/main/reentrancy.c
+ cat ${curPath}/versions/${PHP_VER}/src/mkstemp.c > $sourcePath/php/php${PHP_VER}/ext/zip/lib/mkstemp.c
+fi
+
+if [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ LIB_DEPEND_DIR=`brew info openssl@1.0 | grep ${BREW_DIR}/Cellar/openssl@1.0 | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl@1.0)"
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include"
+ export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz"
+else
+ cd ${rootPath}/plugins/php/lib && /bin/bash openssl_10.sh
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl10/lib/pkgconfig
+ OPTIONS="$OPTIONS --with-openssl"
+fi
+
+if [ ! -d $serverPath/php/${PHP_VER} ];then
+ cd $sourcePath/php/php${PHP_VER} && ./configure \
+ --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \
+ --enable-mysqlnd \
+ --with-mysql=mysqlnd \
+ --with-mysqli=mysqlnd \
+ --with-pdo-mysql=mysqlnd \
+ --enable-mbstring \
+ --enable-simplexml \
+ --enable-ftp \
+ --enable-sockets \
+ --enable-soap \
+ --enable-posix \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ --disable-intl \
+ --disable-fileinfo \
+ $OPTIONS \
+ --enable-fpm
+ make clean && make -j${cpuCore} && make install && make clean
+
+ # rm -rf $sourcePath/php/php${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php71 stop
+ rm -rf $serverPath/php/71
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/71/mcrypt.sh b/plugins/php/versions/71/mcrypt.sh
new file mode 100755
index 000000000..0ad94c70d
--- /dev/null
+++ b/plugins/php/versions/71/mcrypt.sh
@@ -0,0 +1,102 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+LIBNAME=mcrypt
+LIBV=0
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash install.sh install ${version}
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config
+ make clean && make && make install && make clean
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/71/src/ext/pcre/sljitConfigInternal.h b/plugins/php/versions/71/src/ext/pcre/sljitConfigInternal.h
new file mode 100644
index 000000000..cac7426be
--- /dev/null
+++ b/plugins/php/versions/71/src/ext/pcre/sljitConfigInternal.h
@@ -0,0 +1,713 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SLJIT_CONFIG_INTERNAL_H_
+#define _SLJIT_CONFIG_INTERNAL_H_
+
+/*
+ SLJIT defines the following architecture dependent types and macros:
+
+ Types:
+ sljit_sb, sljit_ub : signed and unsigned 8 bit byte
+ sljit_sh, sljit_uh : signed and unsigned 16 bit half-word (short) type
+ sljit_si, sljit_ui : signed and unsigned 32 bit integer type
+ sljit_sw, sljit_uw : signed and unsigned machine word, enough to store a pointer
+ sljit_p : unsgined pointer value (usually the same as sljit_uw, but
+ some 64 bit ABIs may use 32 bit pointers)
+ sljit_s : single precision floating point value
+ sljit_d : double precision floating point value
+
+ Macros for feature detection (boolean):
+ SLJIT_32BIT_ARCHITECTURE : 32 bit architecture
+ SLJIT_64BIT_ARCHITECTURE : 64 bit architecture
+ SLJIT_LITTLE_ENDIAN : little endian architecture
+ SLJIT_BIG_ENDIAN : big endian architecture
+ SLJIT_UNALIGNED : allows unaligned memory accesses for non-fpu operations (only!)
+ SLJIT_INDIRECT_CALL : see SLJIT_FUNC_OFFSET() for more information
+
+ Constants:
+ SLJIT_NUMBER_OF_REGISTERS : number of available registers
+ SLJIT_NUMBER_OF_SCRATCH_REGISTERS : number of available scratch registers
+ SLJIT_NUMBER_OF_SAVED_REGISTERS : number of available saved registers
+ SLJIT_NUMBER_OF_FLOAT_REGISTERS : number of available floating point registers
+ SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS : number of available floating point scratch registers
+ SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS : number of available floating point saved registers
+ SLJIT_WORD_SHIFT : the shift required to apply when accessing a sljit_sw/sljit_uw array by index
+ SLJIT_DOUBLE_SHIFT : the shift required to apply when accessing
+ a double precision floating point array by index
+ SLJIT_SINGLE_SHIFT : the shift required to apply when accessing
+ a single precision floating point array by index
+ SLJIT_LOCALS_OFFSET : local space starting offset (SLJIT_SP + SLJIT_LOCALS_OFFSET)
+ SLJIT_RETURN_ADDRESS_OFFSET : a return instruction always adds this offset to the return address
+
+ Other macros:
+ SLJIT_CALL : C calling convention define for both calling JIT form C and C callbacks for JIT
+ SLJIT_W(number) : defining 64 bit constants on 64 bit architectures (compiler independent helper)
+*/
+
+/*****************/
+/* Sanity check. */
+/*****************/
+
+#if !((defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ || (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \
+ || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ || (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ || (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ || (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \
+ || (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) \
+ || (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \
+ || (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED))
+#error "An architecture must be selected"
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ + (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ + (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \
+ + (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ + (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ + (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ + (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ + (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ + (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) \
+ + (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ + (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ + (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \
+ + (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \
+ + (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) >= 2
+#error "Multiple architectures are selected"
+#endif
+
+/********************************************************/
+/* Automatic CPU detection (requires compiler support). */
+/********************************************************/
+
+#if (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO)
+
+#ifndef _WIN32
+
+#if defined(__i386__) || defined(__i386)
+#define SLJIT_CONFIG_X86_32 1
+#elif defined(__x86_64__)
+#define SLJIT_CONFIG_X86_64 1
+#elif defined(__arm__) || defined(__ARM__)
+#ifdef __thumb2__
+#define SLJIT_CONFIG_ARM_THUMB2 1
+#elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__)
+#define SLJIT_CONFIG_ARM_V7 1
+#else
+#define SLJIT_CONFIG_ARM_V5 1
+#endif
+#elif defined (__aarch64__)
+#define SLJIT_CONFIG_ARM_64 1
+#elif defined(__ppc64__) || defined(__powerpc64__) || defined(_ARCH_PPC64) || (defined(_POWER) && defined(__64BIT__))
+#define SLJIT_CONFIG_PPC_64 1
+#elif defined(__ppc__) || defined(__powerpc__) || defined(_ARCH_PPC) || defined(_ARCH_PWR) || defined(_ARCH_PWR2) || defined(_POWER)
+#define SLJIT_CONFIG_PPC_32 1
+#elif defined(__mips__) && !defined(_LP64)
+#define SLJIT_CONFIG_MIPS_32 1
+#elif defined(__mips64)
+#define SLJIT_CONFIG_MIPS_64 1
+#elif defined(__sparc__) || defined(__sparc)
+#define SLJIT_CONFIG_SPARC_32 1
+#elif defined(__tilegx__)
+#define SLJIT_CONFIG_TILEGX 1
+#else
+/* Unsupported architecture */
+#define SLJIT_CONFIG_UNSUPPORTED 1
+#endif
+
+#else /* !_WIN32 */
+
+#if defined(_M_X64) || defined(__x86_64__)
+#define SLJIT_CONFIG_X86_64 1
+#elif defined(_ARM_)
+#define SLJIT_CONFIG_ARM_V5 1
+#else
+#define SLJIT_CONFIG_X86_32 1
+#endif
+
+#endif /* !WIN32 */
+#endif /* SLJIT_CONFIG_AUTO */
+
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+#undef SLJIT_EXECUTABLE_ALLOCATOR
+#endif
+
+/******************************/
+/* CPU family type detection. */
+/******************************/
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+#define SLJIT_CONFIG_ARM_32 1
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+#define SLJIT_CONFIG_X86 1
+#elif (defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+#define SLJIT_CONFIG_ARM 1
+#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define SLJIT_CONFIG_PPC 1
+#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+#define SLJIT_CONFIG_MIPS 1
+#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) || (defined SLJIT_CONFIG_SPARC_64 && SLJIT_CONFIG_SPARC_64)
+#define SLJIT_CONFIG_SPARC 1
+#endif
+
+/**********************************/
+/* External function definitions. */
+/**********************************/
+
+#if !(defined SLJIT_STD_MACROS_DEFINED && SLJIT_STD_MACROS_DEFINED)
+
+/* These libraries are needed for the macros below. */
+#include
+#include
+
+#endif /* SLJIT_STD_MACROS_DEFINED */
+
+/* General macros:
+ Note: SLJIT is designed to be independent from them as possible.
+
+ In release mode (SLJIT_DEBUG is not defined) only the following
+ external functions are needed:
+*/
+
+#ifndef SLJIT_MALLOC
+#define SLJIT_MALLOC(size, allocator_data) malloc(size)
+#endif
+
+#ifndef SLJIT_FREE
+#define SLJIT_FREE(ptr, allocator_data) free(ptr)
+#endif
+
+#ifndef SLJIT_MEMMOVE
+#define SLJIT_MEMMOVE(dest, src, len) memmove(dest, src, len)
+#endif
+
+#ifndef SLJIT_ZEROMEM
+#define SLJIT_ZEROMEM(dest, len) memset(dest, 0, len)
+#endif
+
+/***************************/
+/* Compiler helper macros. */
+/***************************/
+
+#if !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY)
+
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define SLJIT_LIKELY(x) __builtin_expect((x), 1)
+#define SLJIT_UNLIKELY(x) __builtin_expect((x), 0)
+#else
+#define SLJIT_LIKELY(x) (x)
+#define SLJIT_UNLIKELY(x) (x)
+#endif
+
+#endif /* !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY) */
+
+#ifndef SLJIT_INLINE
+/* Inline functions. Some old compilers do not support them. */
+#if defined(__SUNPRO_C) && __SUNPRO_C <= 0x510
+#define SLJIT_INLINE
+#else
+#define SLJIT_INLINE __inline
+#endif
+#endif /* !SLJIT_INLINE */
+
+#ifndef SLJIT_NOINLINE
+/* Not inline functions. */
+#if defined(__GNUC__)
+#define SLJIT_NOINLINE __attribute__ ((noinline))
+#else
+#define SLJIT_NOINLINE
+#endif
+#endif /* !SLJIT_INLINE */
+
+#ifndef SLJIT_CONST
+/* Const variables. */
+#define SLJIT_CONST const
+#endif
+
+#ifndef SLJIT_UNUSED_ARG
+/* Unused arguments. */
+#define SLJIT_UNUSED_ARG(arg) (void)arg
+#endif
+
+/*********************************/
+/* Type of public API functions. */
+/*********************************/
+
+#if (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC)
+/* Static ABI functions. For all-in-one programs. */
+
+#if defined(__GNUC__)
+/* Disable unused warnings in gcc. */
+#define SLJIT_API_FUNC_ATTRIBUTE static __attribute__((unused))
+#else
+#define SLJIT_API_FUNC_ATTRIBUTE static
+#endif
+
+#else
+#define SLJIT_API_FUNC_ATTRIBUTE
+#endif /* (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC) */
+
+/****************************/
+/* Instruction cache flush. */
+/****************************/
+
+#ifndef SLJIT_CACHE_FLUSH
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+
+/* Not required to implement on archs with unified caches. */
+#define SLJIT_CACHE_FLUSH(from, to)
+
+#elif defined __APPLE__
+
+/* Supported by all macs since Mac OS 10.5.
+ However, it does not work on non-jailbroken iOS devices,
+ although the compilation is successful. */
+#include
+#define SLJIT_CACHE_FLUSH(from, to) \
+ sys_icache_invalidate((char*)(from), (char*)(to) - (char*)(from))
+
+#elif defined __ANDROID__
+
+/* Android lacks __clear_cache; instead, cacheflush should be used. */
+
+#define SLJIT_CACHE_FLUSH(from, to) \
+ cacheflush((long)(from), (long)(to), 0)
+
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+/* The __clear_cache() implementation of GCC is a dummy function on PowerPC. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ ppc_cache_flush((from), (to))
+
+#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+
+/* The __clear_cache() implementation of GCC is a dummy function on Sparc. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ sparc_cache_flush((from), (to))
+
+#else
+
+/* Calls __ARM_NR_cacheflush on ARM-Linux. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ __clear_cache((char*)(from), (char*)(to))
+
+#endif
+
+#endif /* !SLJIT_CACHE_FLUSH */
+
+/******************************************************/
+/* Byte/half/int/word/single/double type definitions. */
+/******************************************************/
+
+/* 8 bit byte type. */
+typedef unsigned char sljit_ub;
+typedef signed char sljit_sb;
+
+/* 16 bit half-word type. */
+typedef unsigned short int sljit_uh;
+typedef signed short int sljit_sh;
+
+/* 32 bit integer type. */
+typedef unsigned int sljit_ui;
+typedef signed int sljit_si;
+
+/* Machine word type. Enough for storing a pointer.
+ 32 bit for 32 bit machines.
+ 64 bit for 64 bit machines. */
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+/* Just to have something. */
+#define SLJIT_WORD_SHIFT 0
+typedef unsigned long int sljit_uw;
+typedef long int sljit_sw;
+#elif !(defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ && !(defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ && !(defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ && !(defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX)
+#define SLJIT_32BIT_ARCHITECTURE 1
+#define SLJIT_WORD_SHIFT 2
+typedef unsigned int sljit_uw;
+typedef int sljit_sw;
+#else
+#define SLJIT_64BIT_ARCHITECTURE 1
+#define SLJIT_WORD_SHIFT 3
+#ifdef _WIN32
+typedef unsigned __int64 sljit_uw;
+typedef __int64 sljit_sw;
+#else
+typedef unsigned long int sljit_uw;
+typedef long int sljit_sw;
+#endif
+#endif
+
+typedef sljit_uw sljit_p;
+
+/* Floating point types. */
+typedef float sljit_s;
+typedef double sljit_d;
+
+/* Shift for pointer sized data. */
+#define SLJIT_POINTER_SHIFT SLJIT_WORD_SHIFT
+
+/* Shift for double precision sized data. */
+#define SLJIT_DOUBLE_SHIFT 3
+#define SLJIT_SINGLE_SHIFT 2
+
+#ifndef SLJIT_W
+
+/* Defining long constants. */
+#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+#define SLJIT_W(w) (w##ll)
+#else
+#define SLJIT_W(w) (w)
+#endif
+
+#endif /* !SLJIT_W */
+
+/*************************/
+/* Endianness detection. */
+/*************************/
+
+#if !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN)
+
+/* These macros are mostly useful for the applications. */
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+
+#ifdef __LITTLE_ENDIAN__
+#define SLJIT_LITTLE_ENDIAN 1
+#else
+#define SLJIT_BIG_ENDIAN 1
+#endif
+
+#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+
+#ifdef __MIPSEL__
+#define SLJIT_LITTLE_ENDIAN 1
+#else
+#define SLJIT_BIG_ENDIAN 1
+#endif
+
+#elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+
+#define SLJIT_BIG_ENDIAN 1
+
+#else
+#define SLJIT_LITTLE_ENDIAN 1
+#endif
+
+#endif /* !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN) */
+
+/* Sanity check. */
+#if (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#error "Exactly one endianness must be selected"
+#endif
+
+#if !(defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && !(defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#error "Exactly one endianness must be selected"
+#endif
+
+#ifndef SLJIT_UNALIGNED
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ || (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define SLJIT_UNALIGNED 1
+#endif
+
+#endif /* !SLJIT_UNALIGNED */
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+/* Auto detect SSE2 support using CPUID.
+ On 64 bit x86 cpus, sse2 must be present. */
+#define SLJIT_DETECT_SSE2 1
+#endif
+
+/*****************************************************************************************/
+/* Calling convention of functions generated by SLJIT or called from the generated code. */
+/*****************************************************************************************/
+
+#ifndef SLJIT_CALL
+
+#if (defined SLJIT_USE_CDECL_CALLING_CONVENTION && SLJIT_USE_CDECL_CALLING_CONVENTION)
+
+/* Force cdecl. */
+#define SLJIT_CALL
+
+#elif (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+
+#if defined(__GNUC__) && !defined(__APPLE__)
+
+#define SLJIT_CALL __attribute__ ((fastcall))
+#define SLJIT_X86_32_FASTCALL 1
+
+#elif defined(_MSC_VER)
+
+#define SLJIT_CALL __fastcall
+#define SLJIT_X86_32_FASTCALL 1
+
+#elif defined(__BORLANDC__)
+
+#define SLJIT_CALL __msfastcall
+#define SLJIT_X86_32_FASTCALL 1
+
+#else /* Unknown compiler. */
+
+/* The cdecl attribute is the default. */
+#define SLJIT_CALL
+
+#endif
+
+#else /* Non x86-32 architectures. */
+
+#define SLJIT_CALL
+
+#endif /* SLJIT_CONFIG_X86_32 */
+
+#endif /* !SLJIT_CALL */
+
+#ifndef SLJIT_INDIRECT_CALL
+#if ((defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) && (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN)) \
+ || ((defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) && defined _AIX)
+/* It seems certain ppc compilers use an indirect addressing for functions
+ which makes things complicated. */
+#define SLJIT_INDIRECT_CALL 1
+#endif
+#endif /* SLJIT_INDIRECT_CALL */
+
+/* The offset which needs to be substracted from the return address to
+determine the next executed instruction after return. */
+#ifndef SLJIT_RETURN_ADDRESS_OFFSET
+#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+#define SLJIT_RETURN_ADDRESS_OFFSET 8
+#else
+#define SLJIT_RETURN_ADDRESS_OFFSET 0
+#endif
+#endif /* SLJIT_RETURN_ADDRESS_OFFSET */
+
+/***************************************************/
+/* Functions of the built-in executable allocator. */
+/***************************************************/
+
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void);
+#define SLJIT_MALLOC_EXEC(size) sljit_malloc_exec(size)
+#define SLJIT_FREE_EXEC(ptr) sljit_free_exec(ptr)
+#endif
+
+/**********************************************/
+/* Registers and locals offset determination. */
+/**********************************************/
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+
+#define SLJIT_NUMBER_OF_REGISTERS 10
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7
+#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL)
+#define SLJIT_LOCALS_OFFSET_BASE ((2 + 4) * sizeof(sljit_sw))
+#else
+/* Maximum 3 arguments are passed on the stack, +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((3 + 1 + 4) * sizeof(sljit_sw))
+#endif /* SLJIT_X86_32_FASTCALL */
+
+#elif (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+
+#ifndef _WIN64
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 6
+#define SLJIT_LOCALS_OFFSET_BASE (sizeof(sljit_sw))
+#else
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE ((4 + 2) * sizeof(sljit_sw))
+#endif /* _WIN64 */
+
+#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+
+#define SLJIT_NUMBER_OF_REGISTERS 11
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+
+#define SLJIT_NUMBER_OF_REGISTERS 11
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#elif (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+
+#define SLJIT_NUMBER_OF_REGISTERS 25
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 10
+#define SLJIT_LOCALS_OFFSET_BASE (2 * sizeof(sljit_sw))
+
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+#define SLJIT_NUMBER_OF_REGISTERS 22
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 17
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) || (defined _AIX)
+#define SLJIT_LOCALS_OFFSET_BASE ((6 + 8) * sizeof(sljit_sw))
+#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+/* Add +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((3 + 1) * sizeof(sljit_sw))
+#else
+#define SLJIT_LOCALS_OFFSET_BASE (3 * sizeof(sljit_sw))
+#endif /* SLJIT_CONFIG_PPC_64 || _AIX */
+
+#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+
+#define SLJIT_NUMBER_OF_REGISTERS 17
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#define SLJIT_LOCALS_OFFSET_BASE (4 * sizeof(sljit_sw))
+#else
+#define SLJIT_LOCALS_OFFSET_BASE 0
+#endif
+
+#elif (defined SLJIT_CONFIG_SPARC && SLJIT_CONFIG_SPARC)
+
+#define SLJIT_NUMBER_OF_REGISTERS 18
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 14
+#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
+/* Add +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((23 + 1) * sizeof(sljit_sw))
+#endif
+
+#elif (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX)
+
+#define SLJIT_NUMBER_OF_REGISTERS 10
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 5
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#elif (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+
+#define SLJIT_NUMBER_OF_REGISTERS 0
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 0
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#endif
+
+#define SLJIT_LOCALS_OFFSET (SLJIT_LOCALS_OFFSET_BASE)
+
+#define SLJIT_NUMBER_OF_SCRATCH_REGISTERS \
+ (SLJIT_NUMBER_OF_REGISTERS - SLJIT_NUMBER_OF_SAVED_REGISTERS)
+
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 6
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) && (defined _WIN64)
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 1
+#else
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0
+#endif
+
+#define SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS \
+ (SLJIT_NUMBER_OF_FLOAT_REGISTERS - SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS)
+
+/*************************************/
+/* Debug and verbose related macros. */
+/*************************************/
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+#include
+#endif
+
+#if (defined SLJIT_DEBUG && SLJIT_DEBUG)
+
+#if !defined(SLJIT_ASSERT) || !defined(SLJIT_ASSERT_STOP)
+
+/* SLJIT_HALT_PROCESS must halt the process. */
+#ifndef SLJIT_HALT_PROCESS
+#include
+
+#define SLJIT_HALT_PROCESS() \
+ abort();
+#endif /* !SLJIT_HALT_PROCESS */
+
+#include
+
+#endif /* !SLJIT_ASSERT || !SLJIT_ASSERT_STOP */
+
+/* Feel free to redefine these two macros. */
+#ifndef SLJIT_ASSERT
+
+#define SLJIT_ASSERT(x) \
+ do { \
+ if (SLJIT_UNLIKELY(!(x))) { \
+ printf("Assertion failed at " __FILE__ ":%d\n", __LINE__); \
+ SLJIT_HALT_PROCESS(); \
+ } \
+ } while (0)
+
+#endif /* !SLJIT_ASSERT */
+
+#ifndef SLJIT_ASSERT_STOP
+
+#define SLJIT_ASSERT_STOP() \
+ do { \
+ printf("Should never been reached " __FILE__ ":%d\n", __LINE__); \
+ SLJIT_HALT_PROCESS(); \
+ } while (0)
+
+#endif /* !SLJIT_ASSERT_STOP */
+
+#else /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
+
+/* Forcing empty, but valid statements. */
+#undef SLJIT_ASSERT
+#undef SLJIT_ASSERT_STOP
+
+#define SLJIT_ASSERT(x) \
+ do { } while (0)
+#define SLJIT_ASSERT_STOP() \
+ do { } while (0)
+
+#endif /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
+
+#ifndef SLJIT_COMPILE_ASSERT
+
+/* Should be improved eventually. */
+#define SLJIT_COMPILE_ASSERT(x, description) \
+ SLJIT_ASSERT(x)
+
+#endif /* !SLJIT_COMPILE_ASSERT */
+
+#endif
diff --git a/plugins/php/versions/71/src/mkstemp.c b/plugins/php/versions/71/src/mkstemp.c
new file mode 100644
index 000000000..d1df8a5ca
--- /dev/null
+++ b/plugins/php/versions/71/src/mkstemp.c
@@ -0,0 +1,153 @@
+/* Adapted from NetBSB libc by Dieter Baron */
+
+/* NetBSD: gettemp.c,v 1.13 2003/12/05 00:57:36 uebayasi Exp */
+
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#ifdef _WIN32
+#include
+#endif
+#include
+#include
+#ifndef _WIN32
+#include
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+
+int
+_zip_mkstemp(char *path)
+{
+#ifdef _WIN32
+ int ret;
+ ret = _creat(_mktemp(path), _S_IREAD|_S_IWRITE);
+ if (ret == -1) {
+ return 0;
+ } else {
+ return ret;
+ }
+#else
+ int fd;
+ char *start, *trv;
+ struct stat sbuf;
+ pid_t pid;
+
+ /* To guarantee multiple calls generate unique names even if
+ the file is not created. 676 different possibilities with 7
+ or more X's, 26 with 6 or less. */
+ static char xtra[2] = "aa";
+ int xcnt = 0;
+
+ pid = getpid();
+
+ /* Move to end of path and count trailing X's. */
+ for (trv = path; *trv; ++trv)
+ if (*trv == 'X')
+ xcnt++;
+ else
+ xcnt = 0;
+
+ /* Use at least one from xtra. Use 2 if more than 6 X's. */
+ if (*(trv - 1) == 'X')
+ *--trv = xtra[0];
+ if (xcnt > 6 && *(trv - 1) == 'X')
+ *--trv = xtra[1];
+
+ /* Set remaining X's to pid digits with 0's to the left. */
+ while (*--trv == 'X') {
+ *trv = (pid % 10) + '0';
+ pid /= 10;
+ }
+
+ /* update xtra for next call. */
+ if (xtra[0] != 'z')
+ xtra[0]++;
+ else {
+ xtra[0] = 'a';
+ if (xtra[1] != 'z')
+ xtra[1]++;
+ else
+ xtra[1] = 'a';
+ }
+
+ /*
+ * check the target directory; if you have six X's and it
+ * doesn't exist this runs for a *very* long time.
+ */
+ for (start = trv + 1;; --trv) {
+ if (trv <= path)
+ break;
+ if (*trv == '/') {
+ *trv = '\0';
+ if (stat(path, &sbuf))
+ return (0);
+ if (!S_ISDIR(sbuf.st_mode)) {
+ errno = ENOTDIR;
+ return (0);
+ }
+ *trv = '/';
+ break;
+ }
+ }
+
+ for (;;) {
+ if ((fd=open(path, O_CREAT|O_EXCL|O_RDWR|O_BINARY, 0600)) >= 0)
+ return (fd);
+ if (errno != EEXIST)
+ return (0);
+
+ /* tricky little algorithm for backward compatibility */
+ for (trv = start;;) {
+ if (!*trv)
+ return (0);
+ if (*trv == 'z')
+ *trv++ = 'a';
+ else {
+ if (isdigit((unsigned char)*trv))
+ *trv = 'a';
+ else
+ ++*trv;
+ break;
+ }
+ }
+ }
+ /*NOTREACHED*/
+#endif
+}
diff --git a/plugins/php/versions/71/src/reentrancy.c b/plugins/php/versions/71/src/reentrancy.c
new file mode 100644
index 000000000..e7c5d7171
--- /dev/null
+++ b/plugins/php/versions/71/src/reentrancy.c
@@ -0,0 +1,450 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#include
+#include
+#include
+#ifdef HAVE_DIRENT_H
+#include
+#endif
+
+#include "php_reentrancy.h"
+#include "ext/standard/php_rand.h" /* for PHP_RAND_MAX */
+
+enum {
+ LOCALTIME_R,
+ CTIME_R,
+ ASCTIME_R,
+ GMTIME_R,
+ READDIR_R,
+ NUMBER_OF_LOCKS
+};
+
+#if defined(PHP_NEED_REENTRANCY)
+
+#include
+
+static MUTEX_T reentrant_locks[NUMBER_OF_LOCKS];
+
+#define local_lock(x) tsrm_mutex_lock(reentrant_locks[x])
+#define local_unlock(x) tsrm_mutex_unlock(reentrant_locks[x])
+
+#else
+
+#define local_lock(x)
+#define local_unlock(x)
+
+#endif
+
+#if defined(PHP_IRIX_TIME_R)
+
+#define HAVE_CTIME_R 1
+#define HAVE_ASCTIME_R 1
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ if (ctime_r(clock, buf) == buf)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ if (asctime_r(tm, buf) == buf)
+ return (buf);
+ return (NULL);
+}
+
+#endif
+
+#if defined(PHP_HPUX_TIME_R)
+
+#define HAVE_LOCALTIME_R 1
+#define HAVE_CTIME_R 1
+#define HAVE_ASCTIME_R 1
+#define HAVE_GMTIME_R 1
+
+PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ if (localtime_r(timep, p_tm) == 0)
+ return (p_tm);
+ return (NULL);
+}
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ if (ctime_r(clock, buf, 26) != -1)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ if (asctime_r(tm, buf, 26) != -1)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ if (gmtime_r(timep, p_tm) == 0)
+ return (p_tm);
+ return (NULL);
+}
+
+#endif
+
+#if defined(__BEOS__)
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ /* Modified according to LibC definition */
+ if (((struct tm*)gmtime_r(timep, p_tm)) == p_tm)
+ return (p_tm);
+ return (NULL);
+}
+
+#endif /* BEOS */
+
+#if !defined(HAVE_POSIX_READDIR_R)
+
+PHPAPI int php_readdir_r(DIR *dirp, struct dirent *entry,
+ struct dirent **result)
+{
+#if defined(HAVE_OLD_READDIR_R)
+ int ret = 0;
+
+ /* We cannot rely on the return value of readdir_r
+ as it differs between various platforms
+ (HPUX returns 0 on success whereas Solaris returns non-zero)
+ */
+ entry->d_name[0] = '\0';
+ readdir_r(dirp, entry, result);
+
+ if (entry->d_name[0] == '\0') {
+ *result = NULL;
+ ret = errno;
+ } else {
+ *result = entry;
+ }
+ return ret;
+#else
+ struct dirent *ptr;
+ int ret = 0;
+
+ local_lock(READDIR_R);
+
+ errno = 0;
+
+ ptr = readdir(dirp);
+
+ if (!ptr && errno != 0)
+ ret = errno;
+
+ if (ptr)
+ memcpy(entry, ptr, sizeof(*ptr));
+
+ *result = ptr;
+
+ local_unlock(READDIR_R);
+
+ return ret;
+#endif
+}
+
+#endif
+
+#if !defined(HAVE_LOCALTIME_R) && defined(HAVE_LOCALTIME)
+
+PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ struct tm *tmp;
+
+ local_lock(LOCALTIME_R);
+
+ tmp = localtime(timep);
+ if (tmp) {
+ memcpy(p_tm, tmp, sizeof(struct tm));
+ tmp = p_tm;
+ }
+
+ local_unlock(LOCALTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if !defined(HAVE_CTIME_R) && defined(HAVE_CTIME)
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ char *tmp;
+
+ local_lock(CTIME_R);
+
+ tmp = ctime(clock);
+ strcpy(buf, tmp);
+
+ local_unlock(CTIME_R);
+
+ return buf;
+}
+
+#endif
+
+#if !defined(HAVE_ASCTIME_R) && defined(HAVE_ASCTIME)
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ char *tmp;
+
+ local_lock(ASCTIME_R);
+
+ tmp = asctime(tm);
+ strcpy(buf, tmp);
+
+ local_unlock(ASCTIME_R);
+
+ return buf;
+}
+
+#endif
+
+#if !defined(HAVE_GMTIME_R) && defined(HAVE_GMTIME)
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ struct tm *tmp;
+
+ local_lock(GMTIME_R);
+
+ tmp = gmtime(timep);
+ if (tmp) {
+ memcpy(p_tm, tmp, sizeof(struct tm));
+ tmp = p_tm;
+ }
+
+ local_unlock(GMTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if defined(PHP_NEED_REENTRANCY)
+
+void reentrancy_startup(void)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_LOCKS; i++) {
+ reentrant_locks[i] = tsrm_mutex_alloc();
+ }
+}
+
+void reentrancy_shutdown(void)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_LOCKS; i++) {
+ tsrm_mutex_free(reentrant_locks[i]);
+ }
+}
+
+#endif
+
+#ifndef HAVE_RAND_R
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Posix rand_r function added May 1999 by Wes Peters .
+ */
+
+#include
+#include
+
+static int
+do_rand(unsigned long *ctx)
+{
+ return ((*ctx = *ctx * 1103515245 + 12345) % ((u_long)PHP_RAND_MAX + 1));
+}
+
+
+PHPAPI int
+php_rand_r(unsigned int *ctx)
+{
+ u_long val = (u_long) *ctx;
+ *ctx = do_rand(&val);
+ return (int) *ctx;
+}
+
+#endif
+
+
+#ifndef HAVE_STRTOK_R
+
+/*
+ * Copyright (c) 1998 Softweyr LLC. All rights reserved.
+ *
+ * strtok_r, from Berkeley strtok
+ * Oct 13, 1998 by Wes Peters
+ *
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notices, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notices, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Softweyr LLC, the
+ * University of California, Berkeley, and its contributors.
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SOFTWEYR LLC, THE REGENTS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTWEYR LLC, THE
+ * REGENTS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+
+PHPAPI char *
+php_strtok_r(char *s, const char *delim, char **last)
+{
+ char *spanp;
+ int c, sc;
+ char *tok;
+
+ if (s == NULL && (s = *last) == NULL)
+ {
+ return NULL;
+ }
+
+ /*
+ * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
+ */
+cont:
+ c = *s++;
+ for (spanp = (char *)delim; (sc = *spanp++) != 0; )
+ {
+ if (c == sc)
+ {
+ goto cont;
+ }
+ }
+
+ if (c == 0) /* no non-delimiter characters */
+ {
+ *last = NULL;
+ return NULL;
+ }
+ tok = s - 1;
+
+ /*
+ * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
+ * Note that delim must have one NUL; we stop if we see that, too.
+ */
+ for (;;)
+ {
+ c = *s++;
+ spanp = (char *)delim;
+ do
+ {
+ if ((sc = *spanp++) == c)
+ {
+ if (c == 0)
+ {
+ s = NULL;
+ }
+ else
+ {
+ char *w = s - 1;
+ *w = '\0';
+ }
+ *last = s;
+ return tok;
+ }
+ }
+ while (sc != 0);
+ }
+ /* NOTREACHED */
+}
+
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/plugins/php/versions/71/src/zend_multiply.h b/plugins/php/versions/71/src/zend_multiply.h
new file mode 100644
index 000000000..8723551be
--- /dev/null
+++ b/plugins/php/versions/71/src/zend_multiply.h
@@ -0,0 +1,97 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 Zend Technologies Ltd. (http://www.zend.com) |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.00 of the Zend license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.zend.com/license/2_00.txt. |
+ | If you did not receive a copy of the Zend license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@zend.com so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Sascha Schumann |
+ | Ard Biesheuvel |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#if defined(__i386__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__ ("imul %3,%0\n" \
+ "adc $0,%1" \
+ : "=r"(__tmpvar),"=r"(usedval) \
+ : "0"(a), "r"(b), "1"(0)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif defined(__x86_64__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__ ("imul %3,%0\n" \
+ "adc $0,%1" \
+ : "=r"(__tmpvar),"=r"(usedval) \
+ : "0"(a), "r"(b), "1"(0)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif defined(__arm__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__("smull %0, %1, %2, %3\n" \
+ "sub %1, %1, %0, asr #31" \
+ : "=&r"(__tmpvar), "=&r"(usedval) \
+ : "r"(a), "r"(b)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif defined(__aarch64__) && defined(__GNUC__)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ __asm__("mul %0, %2, %3\n" \
+ "smulh %1, %2, %3\n" \
+ "sub %1, %1, %0, asr #63\n" \
+ : "=&r"(__tmpvar), "=&r"(usedval) \
+ : "r"(a), "r"(b)); \
+ if (usedval) (dval) = (double) (a) * (double) (b); \
+ else (lval) = __tmpvar; \
+} while (0)
+
+#elif SIZEOF_LONG == 4 && defined(HAVE_ZEND_LONG64)
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ zend_long64 __result = (zend_long64) (a) * (zend_long64) (b); \
+ if (__result > LONG_MAX || __result < LONG_MIN) { \
+ (dval) = (double) __result; \
+ (usedval) = 1; \
+ } else { \
+ (lval) = (long) __result; \
+ (usedval) = 0; \
+ } \
+} while (0)
+
+#else
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __lres = (a) * (b); \
+ long double __dres = (long double)(a) * (long double)(b); \
+ long double __delta = (long double) __lres - __dres; \
+ if ( ((usedval) = (( __dres + __delta ) != __dres))) { \
+ (dval) = __dres; \
+ } else { \
+ (lval) = __lres; \
+ } \
+} while (0)
+
+#endif
diff --git a/plugins/php/versions/72/install.sh b/plugins/php/versions/72/install.sh
new file mode 100755
index 000000000..5b32cf2a9
--- /dev/null
+++ b/plugins/php/versions/72/install.sh
@@ -0,0 +1,209 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+SYS_ARCH=`arch`
+
+echo "use system: ${sysName}"
+if [ ${sysName} == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eq "openSUSE" /etc/*-release; then
+ OSNAME='opensuse'
+ zypper refresh
+elif grep -Eq "FreeBSD" /etc/*-release; then
+ OSNAME='freebsd'
+ pkg install -y wget unzip
+elif grep -Eqi "Arch" /etc/issue || grep -Eq "Arch" /etc/*-release; then
+ OSNAME='arch'
+ echo y | pacman -Sy unzip
+elif grep -Eqi "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release; then
+ OSNAME='centos'
+ yum install -y wget zip unzip
+elif grep -Eqi "Fedora" /etc/issue || grep -Eq "Fedora" /etc/*-release; then
+ OSNAME='fedora'
+ yum install -y wget zip unzip
+elif grep -Eqi "Rocky" /etc/issue || grep -Eq "Rocky" /etc/*-release; then
+ OSNAME='rocky'
+ yum install -y wget zip unzip
+elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eq "AlmaLinux" /etc/*-release; then
+ OSNAME='alma'
+ yum install -y wget zip unzip
+elif grep -Eqi "Debian" /etc/issue || grep -Eq "Debian" /etc/*-release; then
+ OSNAME='debian'
+ apt update -y
+ apt install -y devscripts
+ apt install -y wget zip unzip
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/*-release; then
+ OSNAME='ubuntu'
+ apt install -y wget zip unzip
+else
+ OSNAME='unknow'
+fi
+
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+
+
+version=7.2.31
+PHP_VER=72
+md5_file_ok=968adcb8d0c5652b6e191b025fc8ba41
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-${version} ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+cd ${rootPath}/plugins/php/lib && /bin/bash freetype_old.sh
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://museum.php.net/php7/php-${version}.tar.xz
+ fi
+
+ #检测文件是否损坏.
+ if [ -f $sourcePath/php/php-${version}.tar.xz ];then
+ md5_file=`md5sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'`
+ if [ "${md5_file}" != "${md5_file_ok}" ]; then
+ echo "PHP${version} 下载文件不完整,重新安装"
+ rm -rf $sourcePath/php/php-${version}.tar.xz
+ fi
+ fi
+
+ cd $sourcePath/php && tar -Jxf $sourcePath/php/php-${version}.tar.xz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+fi
+
+OPTIONS='--without-iconv'
+if [ $sysName == 'Darwin' ]; then
+ OPTIONS="${OPTIONS} --with-curl=$(brew --prefix curl)"
+ OPTIONS="${OPTIONS} --with-pcre-dir=$(brew --prefix pcre2)"
+else
+ OPTIONS="${OPTIONS} --with-readline"
+fi
+IS_64BIT=`getconf LONG_BIT`
+if [ "$IS_64BIT" == "64" ];then
+ OPTIONS="${OPTIONS} --with-libdir=lib64"
+fi
+
+# ----- cpu start ------
+if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+fi
+
+if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+fi
+
+MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+else
+ cpuCore="1"
+fi
+
+if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+else
+ cpuCore="1"
+fi
+# ----- cpu end ------
+
+
+RELOAD_CODE(){
+ cat ${curPath}/versions/${PHP_VER}/src/reentrancy.c > $sourcePath/php/php${PHP_VER}/main/reentrancy.c
+ echo "cat ${curPath}/versions/${PHP_VER}/src/reentrancy.c > $sourcePath/php/php${PHP_VER}/main/reentrancy.c"
+}
+
+if [ "${SYS_ARCH}" == "arm64" ];then
+ # 修复arm64架构下安装
+ RELOAD_CODE
+fi
+
+if [ "${OSNAME}" == "debian" ] && [ "${VERSION_ID}" == "13" ];then
+ RELOAD_CODE
+fi
+
+if [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ LIB_DEPEND_DIR=`brew info openssl@1.0 | grep ${BREW_DIR}/Cellar/openssl@1.0 | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl@1.0)"
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include"
+ export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz"
+else
+ cd ${rootPath}/plugins/php/lib && /bin/bash openssl_10.sh
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl10/lib/pkgconfig
+ OPTIONS="$OPTIONS --with-openssl"
+fi
+
+if [ ! -d $serverPath/php/${PHP_VER} ];then
+ cd $sourcePath/php/php${PHP_VER} && ./configure \
+ --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \
+ --enable-mysqlnd \
+ --with-mysql=mysqlnd \
+ --with-mysqli=mysqlnd \
+ --with-pdo-mysql=mysqlnd \
+ --enable-mbstring \
+ --enable-simplexml \
+ --enable-sockets \
+ --enable-ftp \
+ --enable-soap \
+ --enable-posix \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ --disable-intl \
+ --disable-fileinfo \
+ $OPTIONS \
+ --enable-fpm
+ make clean && make -j${cpuCore} && make install && make clean
+
+ # rm -rf $sourcePath/php/php${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php72 stop
+ rm -rf $serverPath/php/72
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/72/src/reentrancy.c b/plugins/php/versions/72/src/reentrancy.c
new file mode 100644
index 000000000..69096cbb1
--- /dev/null
+++ b/plugins/php/versions/72/src/reentrancy.c
@@ -0,0 +1,450 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2018 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#include
+#include
+#include
+#ifdef HAVE_DIRENT_H
+#include
+#endif
+
+#include "php_reentrancy.h"
+#include "ext/standard/php_rand.h" /* for PHP_RAND_MAX */
+
+enum {
+ LOCALTIME_R,
+ CTIME_R,
+ ASCTIME_R,
+ GMTIME_R,
+ READDIR_R,
+ NUMBER_OF_LOCKS
+};
+
+#if defined(PHP_NEED_REENTRANCY)
+
+#include
+
+static MUTEX_T reentrant_locks[NUMBER_OF_LOCKS];
+
+#define local_lock(x) tsrm_mutex_lock(reentrant_locks[x])
+#define local_unlock(x) tsrm_mutex_unlock(reentrant_locks[x])
+
+#else
+
+#define local_lock(x)
+#define local_unlock(x)
+
+#endif
+
+#if defined(PHP_IRIX_TIME_R)
+
+#define HAVE_CTIME_R 1
+#define HAVE_ASCTIME_R 1
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ if (ctime_r(clock, buf) == buf)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ if (asctime_r(tm, buf) == buf)
+ return (buf);
+ return (NULL);
+}
+
+#endif
+
+#if defined(PHP_HPUX_TIME_R)
+
+#define HAVE_LOCALTIME_R 1
+#define HAVE_CTIME_R 1
+#define HAVE_ASCTIME_R 1
+#define HAVE_GMTIME_R 1
+
+PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ if (localtime_r(timep, p_tm) == 0)
+ return (p_tm);
+ return (NULL);
+}
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ if (ctime_r(clock, buf, 26) != -1)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ if (asctime_r(tm, buf, 26) != -1)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ if (gmtime_r(timep, p_tm) == 0)
+ return (p_tm);
+ return (NULL);
+}
+
+#endif
+
+#if defined(__BEOS__)
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ /* Modified according to LibC definition */
+ if (((struct tm*)gmtime_r(timep, p_tm)) == p_tm)
+ return (p_tm);
+ return (NULL);
+}
+
+#endif /* BEOS */
+
+#if !defined(HAVE_POSIX_READDIR_R)
+
+PHPAPI int php_readdir_r(DIR *dirp, struct dirent *entry,
+ struct dirent **result)
+{
+#if defined(HAVE_OLD_READDIR_R)
+ int ret = 0;
+
+ /* We cannot rely on the return value of readdir_r
+ as it differs between various platforms
+ (HPUX returns 0 on success whereas Solaris returns non-zero)
+ */
+ entry->d_name[0] = '\0';
+ readdir_r(dirp, entry, result);
+
+ if (entry->d_name[0] == '\0') {
+ *result = NULL;
+ ret = errno;
+ } else {
+ *result = entry;
+ }
+ return ret;
+#else
+ struct dirent *ptr;
+ int ret = 0;
+
+ local_lock(READDIR_R);
+
+ errno = 0;
+
+ ptr = readdir(dirp);
+
+ if (!ptr && errno != 0)
+ ret = errno;
+
+ if (ptr)
+ memcpy(entry, ptr, sizeof(*ptr));
+
+ *result = ptr;
+
+ local_unlock(READDIR_R);
+
+ return ret;
+#endif
+}
+
+#endif
+
+#if !defined(HAVE_LOCALTIME_R) && defined(HAVE_LOCALTIME)
+
+PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ struct tm *tmp;
+
+ local_lock(LOCALTIME_R);
+
+ tmp = localtime(timep);
+ if (tmp) {
+ memcpy(p_tm, tmp, sizeof(struct tm));
+ tmp = p_tm;
+ }
+
+ local_unlock(LOCALTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if !defined(HAVE_CTIME_R) && defined(HAVE_CTIME)
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ char *tmp;
+
+ local_lock(CTIME_R);
+
+ tmp = ctime(clock);
+ strcpy(buf, tmp);
+
+ local_unlock(CTIME_R);
+
+ return buf;
+}
+
+#endif
+
+#if !defined(HAVE_ASCTIME_R) && defined(HAVE_ASCTIME)
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ char *tmp;
+
+ local_lock(ASCTIME_R);
+
+ tmp = asctime(tm);
+ strcpy(buf, tmp);
+
+ local_unlock(ASCTIME_R);
+
+ return buf;
+}
+
+#endif
+
+#if !defined(HAVE_GMTIME_R) && defined(HAVE_GMTIME)
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ struct tm *tmp;
+
+ local_lock(GMTIME_R);
+
+ tmp = gmtime(timep);
+ if (tmp) {
+ memcpy(p_tm, tmp, sizeof(struct tm));
+ tmp = p_tm;
+ }
+
+ local_unlock(GMTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if defined(PHP_NEED_REENTRANCY)
+
+void reentrancy_startup(void)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_LOCKS; i++) {
+ reentrant_locks[i] = tsrm_mutex_alloc();
+ }
+}
+
+void reentrancy_shutdown(void)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_LOCKS; i++) {
+ tsrm_mutex_free(reentrant_locks[i]);
+ }
+}
+
+#endif
+
+#ifndef HAVE_RAND_R
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Posix rand_r function added May 1999 by Wes Peters .
+ */
+
+#include
+#include
+
+static int
+do_rand(unsigned long *ctx)
+{
+ return ((*ctx = *ctx * 1103515245 + 12345) % ((u_long)PHP_RAND_MAX + 1));
+}
+
+
+PHPAPI int
+php_rand_r(unsigned int *ctx)
+{
+ u_long val = (u_long) *ctx;
+ *ctx = do_rand(&val);
+ return (int) *ctx;
+}
+
+#endif
+
+
+#ifndef HAVE_STRTOK_R
+
+/*
+ * Copyright (c) 1998 Softweyr LLC. All rights reserved.
+ *
+ * strtok_r, from Berkeley strtok
+ * Oct 13, 1998 by Wes Peters
+ *
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notices, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notices, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Softweyr LLC, the
+ * University of California, Berkeley, and its contributors.
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SOFTWEYR LLC, THE REGENTS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTWEYR LLC, THE
+ * REGENTS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+
+PHPAPI char *
+php_strtok_r(char *s, const char *delim, char **last)
+{
+ char *spanp;
+ int c, sc;
+ char *tok;
+
+ if (s == NULL && (s = *last) == NULL)
+ {
+ return NULL;
+ }
+
+ /*
+ * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
+ */
+cont:
+ c = *s++;
+ for (spanp = (char *)delim; (sc = *spanp++) != 0; )
+ {
+ if (c == sc)
+ {
+ goto cont;
+ }
+ }
+
+ if (c == 0) /* no non-delimiter characters */
+ {
+ *last = NULL;
+ return NULL;
+ }
+ tok = s - 1;
+
+ /*
+ * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
+ * Note that delim must have one NUL; we stop if we see that, too.
+ */
+ for (;;)
+ {
+ c = *s++;
+ spanp = (char *)delim;
+ do
+ {
+ if ((sc = *spanp++) == c)
+ {
+ if (c == 0)
+ {
+ s = NULL;
+ }
+ else
+ {
+ char *w = s - 1;
+ *w = '\0';
+ }
+ *last = s;
+ return tok;
+ }
+ }
+ while (sc != 0);
+ }
+ /* NOTREACHED */
+}
+
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/plugins/php/versions/73/install.sh b/plugins/php/versions/73/install.sh
new file mode 100755
index 000000000..17e538f7a
--- /dev/null
+++ b/plugins/php/versions/73/install.sh
@@ -0,0 +1,213 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+SYS_ARCH=`arch`
+
+echo "use system: ${sysName}"
+if [ ${sysName} == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eq "openSUSE" /etc/*-release; then
+ OSNAME='opensuse'
+ zypper refresh
+elif grep -Eq "FreeBSD" /etc/*-release; then
+ OSNAME='freebsd'
+ pkg install -y wget unzip
+elif grep -Eqi "Arch" /etc/issue || grep -Eq "Arch" /etc/*-release; then
+ OSNAME='arch'
+ echo y | pacman -Sy unzip
+elif grep -Eqi "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release; then
+ OSNAME='centos'
+ yum install -y wget zip unzip
+elif grep -Eqi "Fedora" /etc/issue || grep -Eq "Fedora" /etc/*-release; then
+ OSNAME='fedora'
+ yum install -y wget zip unzip
+elif grep -Eqi "Rocky" /etc/issue || grep -Eq "Rocky" /etc/*-release; then
+ OSNAME='rocky'
+ yum install -y wget zip unzip
+elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eq "AlmaLinux" /etc/*-release; then
+ OSNAME='alma'
+ yum install -y wget zip unzip
+elif grep -Eqi "Debian" /etc/issue || grep -Eq "Debian" /etc/*-release; then
+ OSNAME='debian'
+ apt update -y
+ apt install -y devscripts
+ apt install -y wget zip unzip
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/*-release; then
+ OSNAME='ubuntu'
+ apt install -y wget zip unzip
+else
+ OSNAME='unknow'
+fi
+
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+
+version=7.3.33
+PHP_VER=73
+md5_file_ok=eeabb2140c04da85c86389197421f890
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-${version} ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+cd ${rootPath}/plugins/php/lib && /bin/bash freetype_old.sh
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ] || [ "$?" != "0" ] ;then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://museum.php.net/php7/php-${version}.tar.xz
+ fi
+
+ #检测文件是否损坏.
+ if [ -f $sourcePath/php/php-${version}.tar.xz ];then
+ md5_file=`md5sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'`
+ if [ "${md5_file}" != "${md5_file_ok}" ]; then
+ echo "PHP${version} 下载文件不完整,重新安装"
+ rm -rf $sourcePath/php/php-${version}.tar.xz
+ fi
+ fi
+
+ cd $sourcePath/php && tar -Jxf $sourcePath/php/php-${version}.tar.xz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+fi
+
+# ZIP_OPTION='--enable-zip'
+# libzip_version=`pkg-config libzip --modversion`
+# if [ "$?" != "0" ] || version_lt "$libzip_version" "0.11.0" ;then
+# cd ${rootPath}/plugins/php/lib && /bin/bash libzip.sh
+# export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig
+# ZIP_OPTION="--with-libzip=$serverPath/lib/libzip"
+# fi
+
+OPTIONS='--without-iconv'
+if [ $sysName == 'Darwin' ]; then
+ OPTIONS="${OPTIONS} --with-curl=$(brew --prefix curl)"
+ OPTIONS="${OPTIONS} --with-pcre-dir=$(brew --prefix pcre2)"
+else
+ OPTIONS="${OPTIONS} --with-readline"
+fi
+
+IS_64BIT=`getconf LONG_BIT`
+if [ "$IS_64BIT" == "64" ];then
+ OPTIONS="${OPTIONS} --with-libdir=lib64"
+fi
+
+
+
+# ----- cpu start ------
+if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+fi
+
+if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+fi
+
+MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+else
+ cpuCore="1"
+fi
+
+if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+else
+ cpuCore="1"
+fi
+# ----- cpu end ------
+
+
+if [ "${OSNAME}" == "debian" ] && [ "${VERSION_ID}" == "13" ];then
+ # 修复arm64架构下安装
+ cat ${curPath}/versions/${PHP_VER}/src/reentrancy.c > $sourcePath/php/php${PHP_VER}/main/reentrancy.c
+ echo "cat ${curPath}/versions/${PHP_VER}/src/reentrancy.c > $sourcePath/php/php${PHP_VER}/main/reentrancy.c"
+fi
+
+if [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ LIB_DEPEND_DIR=`brew info openssl@1.0 | grep ${BREW_DIR}/Cellar/openssl@1.0 | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl@1.0)"
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include"
+ export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz"
+else
+ cd ${rootPath}/plugins/php/lib && /bin/bash openssl_10.sh
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl10/lib/pkgconfig
+ OPTIONS="$OPTIONS --with-openssl"
+fi
+
+if [ ! -d $serverPath/php/${PHP_VER} ];then
+ cd $sourcePath/php/php${PHP_VER}
+ ./configure --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \
+ --enable-mysqlnd \
+ --with-mysql=mysqlnd \
+ --with-mysqli=mysqlnd \
+ --with-pdo-mysql=mysqlnd \
+ --enable-ftp \
+ --enable-sockets \
+ --enable-simplexml \
+ --enable-mbstring \
+ --enable-soap \
+ --enable-posix \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ --disable-intl \
+ --disable-fileinfo \
+ $OPTIONS \
+ --enable-fpm
+
+ # make clean &&
+ make -j${cpuCore} && make install && make clean
+
+ # rm -rf $sourcePath/php/php${PHP_VER}
+fi
+
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php73 stop
+ rm -rf $serverPath/php/73
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/73/src/reentrancy.c b/plugins/php/versions/73/src/reentrancy.c
new file mode 100644
index 000000000..c72d81921
--- /dev/null
+++ b/plugins/php/versions/73/src/reentrancy.c
@@ -0,0 +1,442 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2018 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann |
+ +----------------------------------------------------------------------+
+ */
+
+#include
+#include
+#include
+#ifdef HAVE_DIRENT_H
+#include
+#endif
+
+#include "php_reentrancy.h"
+#include "ext/standard/php_rand.h" /* for PHP_RAND_MAX */
+
+enum {
+ LOCALTIME_R,
+ CTIME_R,
+ ASCTIME_R,
+ GMTIME_R,
+ READDIR_R,
+ NUMBER_OF_LOCKS
+};
+
+#if defined(PHP_NEED_REENTRANCY)
+
+#include
+
+static MUTEX_T reentrant_locks[NUMBER_OF_LOCKS];
+
+#define local_lock(x) tsrm_mutex_lock(reentrant_locks[x])
+#define local_unlock(x) tsrm_mutex_unlock(reentrant_locks[x])
+
+#else
+
+#define local_lock(x)
+#define local_unlock(x)
+
+#endif
+
+#if defined(PHP_IRIX_TIME_R)
+
+#define HAVE_CTIME_R 1
+#define HAVE_ASCTIME_R 1
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ if (ctime_r(clock, buf) == buf)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ if (asctime_r(tm, buf) == buf)
+ return (buf);
+ return (NULL);
+}
+
+#endif
+
+#if defined(PHP_HPUX_TIME_R)
+
+#define HAVE_LOCALTIME_R 1
+#define HAVE_CTIME_R 1
+#define HAVE_ASCTIME_R 1
+#define HAVE_GMTIME_R 1
+
+PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ if (localtime_r(timep, p_tm) == 0)
+ return (p_tm);
+ return (NULL);
+}
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ if (ctime_r(clock, buf, 26) != -1)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ if (asctime_r(tm, buf, 26) != -1)
+ return (buf);
+ return (NULL);
+}
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ if (gmtime_r(timep, p_tm) == 0)
+ return (p_tm);
+ return (NULL);
+}
+
+#endif
+
+#if !defined(HAVE_POSIX_READDIR_R)
+
+PHPAPI int php_readdir_r(DIR *dirp, struct dirent *entry,
+ struct dirent **result)
+{
+#if defined(HAVE_OLD_READDIR_R)
+ int ret = 0;
+
+ /* We cannot rely on the return value of readdir_r
+ as it differs between various platforms
+ (HPUX returns 0 on success whereas Solaris returns non-zero)
+ */
+ entry->d_name[0] = '\0';
+ readdir_r(dirp, entry, result);
+
+ if (entry->d_name[0] == '\0') {
+ *result = NULL;
+ ret = errno;
+ } else {
+ *result = entry;
+ }
+ return ret;
+#else
+ struct dirent *ptr;
+ int ret = 0;
+
+ local_lock(READDIR_R);
+
+ errno = 0;
+
+ ptr = readdir(dirp);
+
+ if (!ptr && errno != 0)
+ ret = errno;
+
+ if (ptr)
+ memcpy(entry, ptr, sizeof(*ptr));
+
+ *result = ptr;
+
+ local_unlock(READDIR_R);
+
+ return ret;
+#endif
+}
+
+#endif
+
+#if !defined(HAVE_LOCALTIME_R) && defined(HAVE_LOCALTIME)
+
+PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ struct tm *tmp;
+
+ local_lock(LOCALTIME_R);
+
+ tmp = localtime(timep);
+ if (tmp) {
+ memcpy(p_tm, tmp, sizeof(struct tm));
+ tmp = p_tm;
+ }
+
+ local_unlock(LOCALTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if !defined(HAVE_CTIME_R) && defined(HAVE_CTIME)
+
+PHPAPI char *php_ctime_r(const time_t *clock, char *buf)
+{
+ char *tmp;
+
+ local_lock(CTIME_R);
+
+ tmp = ctime(clock);
+ if (tmp) {
+ strcpy(buf, tmp);
+ tmp = buf;
+ }
+
+ local_unlock(CTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if !defined(HAVE_ASCTIME_R) && defined(HAVE_ASCTIME)
+
+PHPAPI char *php_asctime_r(const struct tm *tm, char *buf)
+{
+ char *tmp;
+
+ local_lock(ASCTIME_R);
+
+ tmp = asctime(tm);
+ if (tmp) {
+ strcpy(buf, tmp);
+ tmp = buf;
+ }
+
+ local_unlock(ASCTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if !defined(HAVE_GMTIME_R) && defined(HAVE_GMTIME)
+
+PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm)
+{
+ struct tm *tmp;
+
+ local_lock(GMTIME_R);
+
+ tmp = gmtime(timep);
+ if (tmp) {
+ memcpy(p_tm, tmp, sizeof(struct tm));
+ tmp = p_tm;
+ }
+
+ local_unlock(GMTIME_R);
+
+ return tmp;
+}
+
+#endif
+
+#if defined(PHP_NEED_REENTRANCY)
+
+void reentrancy_startup(void)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_LOCKS; i++) {
+ reentrant_locks[i] = tsrm_mutex_alloc();
+ }
+}
+
+void reentrancy_shutdown(void)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_LOCKS; i++) {
+ tsrm_mutex_free(reentrant_locks[i]);
+ }
+}
+
+#endif
+
+#ifndef HAVE_RAND_R
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Posix rand_r function added May 1999 by Wes Peters .
+ */
+
+#include
+#include
+
+static int
+do_rand(unsigned long *ctx)
+{
+ return ((*ctx = *ctx * 1103515245 + 12345) % ((u_long)PHP_RAND_MAX + 1));
+}
+
+
+PHPAPI int
+php_rand_r(unsigned int *ctx)
+{
+ u_long val = (u_long) *ctx;
+ *ctx = do_rand(&val);
+ return (int) *ctx;
+}
+
+#endif
+
+
+#ifndef HAVE_STRTOK_R
+
+/*
+ * Copyright (c) 1998 Softweyr LLC. All rights reserved.
+ *
+ * strtok_r, from Berkeley strtok
+ * Oct 13, 1998 by Wes Peters
+ *
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notices, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notices, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Softweyr LLC, the
+ * University of California, Berkeley, and its contributors.
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SOFTWEYR LLC, THE REGENTS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTWEYR LLC, THE
+ * REGENTS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+
+PHPAPI char *
+php_strtok_r(char *s, const char *delim, char **last)
+{
+ char *spanp;
+ int c, sc;
+ char *tok;
+
+ if (s == NULL && (s = *last) == NULL)
+ {
+ return NULL;
+ }
+
+ /*
+ * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
+ */
+cont:
+ c = *s++;
+ for (spanp = (char *)delim; (sc = *spanp++) != 0; )
+ {
+ if (c == sc)
+ {
+ goto cont;
+ }
+ }
+
+ if (c == 0) /* no non-delimiter characters */
+ {
+ *last = NULL;
+ return NULL;
+ }
+ tok = s - 1;
+
+ /*
+ * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
+ * Note that delim must have one NUL; we stop if we see that, too.
+ */
+ for (;;)
+ {
+ c = *s++;
+ spanp = (char *)delim;
+ do
+ {
+ if ((sc = *spanp++) == c)
+ {
+ if (c == 0)
+ {
+ s = NULL;
+ }
+ else
+ {
+ char *w = s - 1;
+ *w = '\0';
+ }
+ *last = s;
+ return tok;
+ }
+ }
+ while (sc != 0);
+ }
+ /* NOTREACHED */
+}
+
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/plugins/php/versions/74/install.sh b/plugins/php/versions/74/install.sh
new file mode 100755
index 000000000..f11be5134
--- /dev/null
+++ b/plugins/php/versions/74/install.sh
@@ -0,0 +1,170 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+version=7.4.26
+PHP_VER=74
+md5_file_ok=0cbaae3de6c02cf8d7b82843fdfdf53d
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-${version} ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/php/lib && /bin/bash libzip.sh
+# cd /www/server/mdserver-web/plugins/php/lib && /bin/bash libzip.sh
+cd ${rootPath}/plugins/php/lib && /bin/bash freetype_new.sh
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+cd ${rootPath}/plugins/php/lib && /bin/bash libzip.sh
+
+# redat ge 8
+which yum
+if [ "$?" == "0" ];then
+ cd ${rootPath}/plugins/php/lib && /bin/bash oniguruma.sh
+fi
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://museum.php.net/php7/php-${version}.tar.xz
+ fi
+
+ #检测文件是否损坏.
+ if [ -f $sourcePath/php/php-${version}.tar.xz ];then
+ md5_file=`md5sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'`
+ if [ "${md5_file}" != "${md5_file_ok}" ]; then
+ echo "PHP${version} 下载文件不完整,重新安装"
+ rm -rf $sourcePath/php/php-${version}.tar.xz
+ fi
+ fi
+
+ cd $sourcePath/php && tar -Jxf $sourcePath/php/php-${version}.tar.xz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+fi
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+ rm -rf $sourcePath/php/php-${version}.tar.xz
+ echo "reinstall php${version}"
+ exit 1
+fi
+
+cd $sourcePath/php/php${PHP_VER}
+
+OPTIONS='--without-iconv'
+# if [ $sysName == 'Darwin' ]; then
+# OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)"
+# fi
+
+IS_64BIT=`getconf LONG_BIT`
+if [ "$IS_64BIT" == "64" ];then
+ OPTIONS="${OPTIONS} --with-libdir=lib64"
+fi
+
+# ----- cpu start ------
+if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+fi
+
+if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+fi
+
+MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+else
+ cpuCore="1"
+fi
+
+if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+else
+ cpuCore="1"
+fi
+# ----- cpu end ------
+
+if [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ LIB_DEPEND_DIR=`brew info openssl@1.0 | grep ${BREW_DIR}/Cellar/openssl@1.0 | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl@1.0)"
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include"
+ export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz"
+else
+ cd ${rootPath}/plugins/php/lib && /bin/bash openssl_10.sh
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl10/lib/pkgconfig
+ OPTIONS="$OPTIONS --with-openssl"
+fi
+
+if [ ! -d $serverPath/php/${PHP_VER} ];then
+ cd $sourcePath/php/php${PHP_VER} && make clean
+ ./configure \
+ --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \
+ --enable-mysqlnd \
+ --with-mysql=mysqlnd \
+ --with-mysqli=mysqlnd \
+ --with-pdo-mysql=mysqlnd \
+ --enable-ftp \
+ --enable-mbstring \
+ --enable-sockets \
+ --enable-simplexml \
+ --enable-soap \
+ --enable-posix \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ --disable-intl \
+ --disable-fileinfo \
+ $OPTIONS \
+ --enable-fpm
+ make clean && make -j${cpuCore} && make install && make clean
+
+ # rm -rf $sourcePath/php/php${PHP_VER}
+
+ echo "安装php-${version}成功"
+fi
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php${PHP_VER} stop
+ rm -rf $serverPath/php/${PHP_VER}
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/80/install.sh b/plugins/php/versions/80/install.sh
new file mode 100755
index 000000000..cf579e517
--- /dev/null
+++ b/plugins/php/versions/80/install.sh
@@ -0,0 +1,169 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+version=8.0.30
+PHP_VER=80
+md5_file_ok=216ab305737a5d392107112d618a755dc5df42058226f1670e9db90e77d777d9
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-${version} ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+cd ${rootPath}/plugins/php/lib && /bin/bash freetype_new.sh
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+
+# redat ge 8
+which yum
+if [ "$?" == "0" ];then
+ cd ${rootPath}/plugins/php/lib && /bin/bash oniguruma.sh
+fi
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://www.php.net/distributions/php-${version}.tar.xz
+ fi
+
+ #检测文件是否损坏.
+ if [ -f $sourcePath/php/php-${version}.tar.xz ];then
+ md5_file=`sha256sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'`
+ if [ "${md5_file}" != "${md5_file_ok}" ]; then
+ echo "PHP${version} 下载文件不完整,重新安装"
+ rm -rf $sourcePath/php/php-${version}.tar.xz
+ fi
+ fi
+
+ cd $sourcePath/php && tar -Jxf $sourcePath/php/php-${version}.tar.xz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+fi
+
+cd $sourcePath/php/php${PHP_VER}
+
+OPTIONS='--without-iconv'
+
+if [ $sysName == 'Darwin' ]; then
+ OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)"
+fi
+
+IS_64BIT=`getconf LONG_BIT`
+if [ "$IS_64BIT" == "64" ];then
+ OPTIONS="${OPTIONS} --with-libdir=lib64"
+fi
+
+
+# argon_version=`pkg-config libargon2 --modversion`
+# if [ "$?" == "0" ];then
+# OPTIONS="${OPTIONS} --with-password-argon2"
+# fi
+
+# ----- cpu start ------
+if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+fi
+
+if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+fi
+
+MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+else
+ cpuCore="1"
+fi
+
+if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+else
+ cpuCore="1"
+fi
+# ----- cpu end ------
+
+# echo "$sourcePath/php/php${PHP_VER}"
+
+if [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ LIB_DEPEND_DIR=`brew info openssl@1.0 | grep ${BREW_DIR}/Cellar/openssl@1.0 | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl@1.0)"
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include"
+ export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz"
+else
+ cd ${rootPath}/plugins/php/lib && /bin/bash openssl_11.sh
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl11/lib/pkgconfig
+ OPTIONS="$OPTIONS --with-openssl"
+fi
+
+if [ ! -d $serverPath/php/${PHP_VER} ];then
+ cd $sourcePath/php/php${PHP_VER}
+ ./buildconf --force
+ ./configure \
+ --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \
+ --enable-mysqlnd \
+ --with-mysql=mysqlnd \
+ --with-mysqli=mysqlnd \
+ --with-pdo-mysql=mysqlnd \
+ --enable-ftp \
+ --enable-mbstring \
+ --enable-sockets \
+ --enable-simplexml \
+ --enable-soap \
+ --enable-posix \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ --disable-intl \
+ --disable-fileinfo \
+ $OPTIONS \
+ --enable-fpm
+ make clean && make -j${cpuCore} && make install && make clean
+
+ # rm -rf $sourcePath/php/php${PHP_VER}
+fi
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php${PHP_VER} stop
+ rm -rf $serverPath/php/${PHP_VER}
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/81/install.sh b/plugins/php/versions/81/install.sh
new file mode 100755
index 000000000..6ef34558b
--- /dev/null
+++ b/plugins/php/versions/81/install.sh
@@ -0,0 +1,161 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+version=8.1.33
+PHP_VER=81
+md5_file_ok=9db83bf4590375562bc1a10b353cccbcf9fcfc56c58b7c8fb814e6865bb928d1
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-${version} ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+cd ${rootPath}/plugins/php/lib && /bin/bash freetype_new.sh
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+
+# redat ge 8
+which yum
+if [ "$?" == "0" ];then
+ cd ${rootPath}/plugins/php/lib && /bin/bash oniguruma.sh
+fi
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://www.php.net/distributions/php-${version}.tar.xz
+ fi
+
+ #检测文件是否损坏.
+ if [ -f $sourcePath/php/php-${version}.tar.xz ];then
+ md5_file=`sha256sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'`
+ if [ "${md5_file}" != "${md5_file_ok}" ]; then
+ echo "PHP${version} 下载文件不完整,重新安装"
+ rm -rf $sourcePath/php/php-${version}.tar.xz
+ fi
+ fi
+
+ cd $sourcePath/php && tar -Jxf $sourcePath/php/php-${version}.tar.xz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+fi
+
+cd $sourcePath/php/php${PHP_VER}
+
+OPTIONS='--without-iconv'
+# if [ $sysName == 'Darwin' ]; then
+# OPTIONS="${OPTIONS} --with-curl"
+# fi
+
+argon_version=`pkg-config libargon2 --modversion`
+if [ "$?" == "0" ];then
+ OPTIONS="${OPTIONS} --with-password-argon2"
+fi
+
+# ----- cpu start ------
+if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+fi
+
+if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+fi
+
+MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+else
+ cpuCore="1"
+fi
+
+if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+else
+ cpuCore="1"
+fi
+# ----- cpu end ------
+
+echo "$sourcePath/php/php${PHP_VER}"
+
+if [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ LIB_DEPEND_DIR=`brew info openssl | grep ${BREW_DIR}/Cellar/openssl | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl)"
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include"
+ export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz"
+else
+ cd ${rootPath}/plugins/php/lib && /bin/bash openssl_11.sh
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl11/lib/pkgconfig
+ OPTIONS="$OPTIONS --with-openssl"
+fi
+
+if [ ! -d $serverPath/php/${PHP_VER} ];then
+ cd $sourcePath/php/php${PHP_VER}
+ ./configure \
+ --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \
+ --enable-mysqlnd \
+ --with-mysql=mysqlnd \
+ --with-mysqli=mysqlnd \
+ --with-pdo-mysql=mysqlnd \
+ --enable-ftp \
+ --enable-mbstring \
+ --enable-sockets \
+ --enable-simplexml \
+ --enable-soap \
+ --enable-posix \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ --disable-intl \
+ --disable-fileinfo \
+ $OPTIONS \
+ --enable-fpm
+ make clean && make -j${cpuCore} && make install && make clean
+
+ # rm -rf $sourcePath/php/php${PHP_VER}
+fi
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php${PHP_VER} stop
+ rm -rf $serverPath/php/${PHP_VER}
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/82/install.sh b/plugins/php/versions/82/install.sh
new file mode 100755
index 000000000..6514f7139
--- /dev/null
+++ b/plugins/php/versions/82/install.sh
@@ -0,0 +1,162 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+version=8.2.30
+PHP_VER=82
+md5_file_ok=bc90523e17af4db46157e75d0c9ef0b9d0030b0514e62c26ba7b513b8c4eb015
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-${version} ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+cd ${rootPath}/plugins/php/lib && /bin/bash freetype_new.sh
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+
+# redat ge 8
+which yum
+if [ "$?" == "0" ];then
+ cd ${rootPath}/plugins/php/lib && /bin/bash oniguruma.sh
+fi
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://www.php.net/distributions/php-${version}.tar.xz
+ fi
+
+ #检测文件是否损坏.
+ if [ -f $sourcePath/php/php-${version}.tar.xz ];then
+ md5_file=`sha256sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'`
+ if [ "${md5_file}" != "${md5_file_ok}" ]; then
+ echo "PHP${version} 下载文件不完整,重新安装"
+ rm -rf $sourcePath/php/php-${version}.tar.xz
+ fi
+ fi
+
+ cd $sourcePath/php && tar -Jxf $sourcePath/php/php-${version}.tar.xz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+fi
+
+cd $sourcePath/php/php${PHP_VER}
+
+OPTIONS='--without-iconv'
+
+IS_64BIT=`getconf LONG_BIT`
+if [ "$IS_64BIT" == "64" ];then
+ OPTIONS="${OPTIONS} --with-libdir=lib64"
+fi
+
+argon_version=`pkg-config libargon2 --modversion`
+if [ "$?" == "0" ];then
+ OPTIONS="${OPTIONS} --with-password-argon2"
+fi
+
+# ----- cpu start ------
+if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+fi
+
+if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+fi
+
+MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+else
+ cpuCore="1"
+fi
+
+if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+else
+ cpuCore="1"
+fi
+# ----- cpu end ------
+
+if [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ LIB_DEPEND_DIR=`brew info openssl | grep ${BREW_DIR}/Cellar/openssl | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl)"
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include"
+ export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz"
+else
+ cd ${rootPath}/plugins/php/lib && /bin/bash openssl_11.sh
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl11/lib/pkgconfig
+ OPTIONS="$OPTIONS --with-openssl"
+fi
+
+# echo "$sourcePath/php/php${PHP_VER}"
+if [ ! -d $serverPath/php/${PHP_VER} ];then
+ cd $sourcePath/php/php${PHP_VER}
+ ./configure \
+ --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \
+ --enable-mysqlnd \
+ --with-mysql=mysqlnd \
+ --with-mysqli=mysqlnd \
+ --with-pdo-mysql=mysqlnd \
+ --enable-mbstring \
+ --enable-ftp \
+ --enable-sockets \
+ --enable-simplexml \
+ --enable-soap \
+ --enable-posix \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ --disable-intl \
+ --disable-fileinfo \
+ $OPTIONS \
+ --enable-fpm
+ make clean && make -j${cpuCore} && make install && make clean
+
+ # rm -rf $sourcePath/php/php${PHP_VER}
+fi
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php${PHP_VER} stop
+ rm -rf $serverPath/php/${PHP_VER}
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/83/install.sh b/plugins/php/versions/83/install.sh
new file mode 100755
index 000000000..1c29a7ca6
--- /dev/null
+++ b/plugins/php/versions/83/install.sh
@@ -0,0 +1,163 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+version=8.3.29
+PHP_VER=83
+md5_file_ok=f7950ca034b15a78f5de9f1b22f4d9bad1dd497114d175cb1672a4ca78077af5
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-${version} ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+cd ${rootPath}/plugins/php/lib && /bin/bash freetype_new.sh
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+
+# redat ge 8
+which yum
+if [ "$?" == "0" ];then
+ cd ${rootPath}/plugins/php/lib && /bin/bash oniguruma.sh
+fi
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ];then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://www.php.net/distributions/php-${version}.tar.xz
+ fi
+
+ #检测文件是否损坏.
+ if [ -f $sourcePath/php/php-${version}.tar.xz ];then
+ md5_file=`sha256sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'`
+ if [ "${md5_file}" != "${md5_file_ok}" ]; then
+ echo "PHP${version} 下载文件不完整,重新安装"
+ rm -rf $sourcePath/php/php-${version}.tar.xz
+ fi
+ fi
+
+ cd $sourcePath/php && tar -Jxf $sourcePath/php/php-${version}.tar.xz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+fi
+
+cd $sourcePath/php/php${PHP_VER}
+
+OPTIONS='--without-iconv'
+
+argon_version=`pkg-config libargon2 --modversion`
+if [ "$?" == "0" ];then
+ OPTIONS="${OPTIONS} --with-password-argon2"
+fi
+
+IS_64BIT=`getconf LONG_BIT`
+if [ "$IS_64BIT" == "64" ];then
+ OPTIONS="${OPTIONS} --with-libdir=lib64"
+fi
+
+# ----- cpu start ------
+if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+fi
+
+if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+fi
+
+MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+else
+ cpuCore="1"
+fi
+
+if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+else
+ cpuCore="1"
+fi
+# ----- cpu end ------
+
+if [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ LIB_DEPEND_DIR=`brew info openssl | grep ${BREW_DIR}/Cellar/openssl | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl)"
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include"
+ export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz"
+else
+ cd ${rootPath}/plugins/php/lib && /bin/bash openssl_11.sh
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl11/lib/pkgconfig
+ OPTIONS="$OPTIONS --with-openssl"
+fi
+
+# echo "$sourcePath/php/php${PHP_VER}"
+if [ ! -d $serverPath/php/${PHP_VER} ];then
+ cd $sourcePath/php/php${PHP_VER}
+ ./configure \
+ --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \
+ --enable-mysqlnd \
+ --with-mysql=mysqlnd \
+ --with-mysqli=mysqlnd \
+ --with-pdo-mysql=mysqlnd \
+ --enable-mbstring \
+ --enable-ftp \
+ --enable-sockets \
+ --enable-simplexml \
+ --enable-soap \
+ --enable-posix \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ --disable-intl \
+ --disable-fileinfo \
+ $OPTIONS \
+ --enable-fpm
+ make clean && make -j${cpuCore} && make install && make clean
+
+ # rm -rf $sourcePath/php/php${PHP_VER}
+fi
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php${PHP_VER} stop
+ rm -rf $serverPath/php/${PHP_VER}
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/84/install.sh b/plugins/php/versions/84/install.sh
new file mode 100755
index 000000000..5173ee304
--- /dev/null
+++ b/plugins/php/versions/84/install.sh
@@ -0,0 +1,173 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+version=8.4.16
+PHP_VER=84
+md5_file_ok=f66f8f48db34e9e29f7bfd6901178e9cf4a1b163e6e497716dfcb8f88bcfae30
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-${version} ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+cd ${rootPath}/plugins/php/lib && /bin/bash freetype_new.sh
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+
+# redat ge 8
+which yum
+if [ "$?" == "0" ];then
+ cd ${rootPath}/plugins/php/lib && /bin/bash oniguruma.sh
+fi
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ];then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://www.php.net/distributions/php-${version}.tar.xz
+ fi
+
+ #检测文件是否损坏.
+ if [ -f $sourcePath/php/php-${version}.tar.xz ];then
+ md5_file=`sha256sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'`
+ if [ "${md5_file}" != "${md5_file_ok}" ]; then
+ echo "PHP${version} 下载文件不完整,重新安装"
+ rm -rf $sourcePath/php/php-${version}.tar.xz
+ fi
+ fi
+
+ cd $sourcePath/php && tar -Jxf $sourcePath/php/php-${version}.tar.xz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+fi
+
+cd $sourcePath/php/php${PHP_VER}
+
+OPTIONS='--without-iconv'
+
+# if [ $sysName == 'Darwin' ]; then
+# OPTIONS="${OPTIONS} --with-curl"
+# fi
+
+argon_version=`pkg-config libargon2 --modversion`
+if [ "$?" == "0" ];then
+ OPTIONS="${OPTIONS} --with-password-argon2"
+fi
+
+IS_64BIT=`getconf LONG_BIT`
+if [ "$IS_64BIT" == "64" ];then
+ OPTIONS="${OPTIONS} --with-libdir=lib64"
+fi
+
+# ----- cpu start ------
+if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+fi
+
+if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+fi
+
+MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+else
+ cpuCore="1"
+fi
+
+if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+else
+ cpuCore="1"
+fi
+# ----- cpu end ------
+
+# OPTIONS="${OPTIONS} --enable-debug"
+# OPTIONS="${OPTIONS} --enable-dtrace"
+
+if [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ LIB_DEPEND_DIR=`brew info openssl | grep ${BREW_DIR}/Cellar/openssl | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl)"
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include"
+ export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz"
+else
+ # cd ${rootPath}/plugins/php/lib && /bin/bash openssl_35.sh
+ # export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl35/lib/pkgconfig
+ OPTIONS="$OPTIONS --with-openssl"
+fi
+
+echo "$sourcePath/php/php${PHP_VER}"
+
+if [ ! -d $serverPath/php/${PHP_VER} ];then
+ cd $sourcePath/php/php${PHP_VER}
+ # ./buildconf --force
+ ./configure \
+ --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \
+ --enable-mysqlnd \
+ --with-mysql=mysqlnd \
+ --with-mysqli=mysqlnd \
+ --with-pdo-mysql=mysqlnd \
+ --with-mysqlnd-ssl \
+ --enable-mbstring \
+ --enable-ftp \
+ --enable-sockets \
+ --enable-simplexml \
+ --enable-soap \
+ --enable-posix \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ --disable-intl \
+ --disable-fileinfo \
+ $OPTIONS \
+ --enable-fpm
+ make clean && make -j${cpuCore} && make install && make clean
+
+ # rm -rf $sourcePath/php/php${PHP_VER}
+fi
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php${PHP_VER} stop
+ rm -rf $serverPath/php/${PHP_VER}
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/85/install.sh b/plugins/php/versions/85/install.sh
new file mode 100755
index 000000000..27e3f28f3
--- /dev/null
+++ b/plugins/php/versions/85/install.sh
@@ -0,0 +1,175 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+version=8.5.2
+PHP_VER=85
+md5_file_ok=cb75a9b00a2806f7390dd64858ef42a47b443b3475769c8af6af33a18b1381f1
+Install_php()
+{
+#------------------------ install start ------------------------------------#
+echo "安装php-${version} ..."
+mkdir -p $sourcePath/php
+mkdir -p $serverPath/php
+
+cd ${rootPath}/plugins/php/lib && /bin/bash freetype_new.sh
+cd ${rootPath}/plugins/php/lib && /bin/bash zlib.sh
+
+# redat ge 8
+which yum
+if [ "$?" == "0" ];then
+ cd ${rootPath}/plugins/php/lib && /bin/bash oniguruma.sh
+fi
+
+if [ ! -d $sourcePath/php/php${PHP_VER} ];then
+
+ # ----------------------------------------------------------------------- #
+ # 中国优化安装
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ LOCAL_ADDR=common
+ if [ ! -z "$cn" ];then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://mirrors.nju.edu.cn/php/php-${version}.tar.xz
+ fi
+ fi
+ # ----------------------------------------------------------------------- #
+
+
+ if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then
+ wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://www.php.net/distributions/php-${version}.tar.xz
+ # wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://downloads.php.net/~edorian/php-${version}.tar.xz
+ fi
+
+ #检测文件是否损坏.
+ # if [ -f $sourcePath/php/php-${version}.tar.xz ];then
+ # md5_file=`sha256sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'`
+ # if [ "${md5_file}" != "${md5_file_ok}" ]; then
+ # echo "PHP${version} 下载文件不完整,重新安装"
+ # rm -rf $sourcePath/php/php-${version}.tar.xz
+ # fi
+ # fi
+
+ cd $sourcePath/php && tar -Jxf $sourcePath/php/php-${version}.tar.xz
+ mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER}
+fi
+
+cd $sourcePath/php/php${PHP_VER}
+
+OPTIONS='--without-iconv'
+
+# if [ $sysName == 'Darwin' ]; then
+# OPTIONS="${OPTIONS} --with-curl"
+# fi
+
+argon_version=`pkg-config libargon2 --modversion`
+if [ "$?" == "0" ];then
+ OPTIONS="${OPTIONS} --with-password-argon2"
+fi
+
+IS_64BIT=`getconf LONG_BIT`
+if [ "$IS_64BIT" == "64" ];then
+ OPTIONS="${OPTIONS} --with-libdir=lib64"
+fi
+
+# ----- cpu start ------
+if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+fi
+
+if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+fi
+
+MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+else
+ cpuCore="1"
+fi
+
+if [ "$cpuCore" -gt "2" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+else
+ cpuCore="1"
+fi
+# ----- cpu end ------
+
+# OPTIONS="${OPTIONS} --enable-debug"
+# OPTIONS="${OPTIONS} --enable-dtrace"
+
+if [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ LIB_DEPEND_DIR=`brew info openssl | grep ${BREW_DIR}/Cellar/openssl | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl)"
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include"
+ export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz"
+else
+ echo "lib"
+ # cd ${rootPath}/plugins/php/lib && /bin/bash openssl_35.sh
+ # export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl35/lib/pkgconfig
+ OPTIONS="$OPTIONS --with-openssl"
+fi
+
+echo "$sourcePath/php/php${PHP_VER}"
+
+if [ ! -d $serverPath/php/${PHP_VER} ];then
+ cd $sourcePath/php/php${PHP_VER}
+ # ./buildconf --force
+ ./configure \
+ --prefix=$serverPath/php/${PHP_VER} \
+ --exec-prefix=$serverPath/php/${PHP_VER} \
+ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \
+ --enable-mysqlnd \
+ --with-mysql=mysqlnd \
+ --with-mysqli=mysqlnd \
+ --with-pdo-mysql=mysqlnd \
+ --with-mysqlnd-ssl \
+ --enable-mbstring \
+ --enable-ftp \
+ --enable-sockets \
+ --enable-simplexml \
+ --enable-soap \
+ --enable-posix \
+ --enable-sysvmsg \
+ --enable-sysvsem \
+ --enable-sysvshm \
+ --disable-intl \
+ --disable-fileinfo \
+ $OPTIONS \
+ --enable-fpm
+ make clean && make -j${cpuCore} && make install && make clean
+
+ # rm -rf $sourcePath/php/php${PHP_VER}
+fi
+#------------------------ install end ------------------------------------#
+}
+
+Uninstall_php()
+{
+ $serverPath/php/init.d/php${PHP_VER} stop
+ rm -rf $serverPath/php/${PHP_VER}
+ echo "卸载php-${version}..."
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_php
+else
+ Uninstall_php
+fi
diff --git a/plugins/php/versions/all_mac.sh b/plugins/php/versions/all_mac.sh
new file mode 100644
index 000000000..cc6997335
--- /dev/null
+++ b/plugins/php/versions/all_mac.sh
@@ -0,0 +1,70 @@
+#! /bin/sh
+export PATH=$PATH:/opt/local/bin:/opt/local/sbin:/opt/local/share/man:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/opt/homebrew/bin
+DIR=$(cd "$(dirname "$0")"; pwd)
+ROOT_DIR=$(cd "$(dirname "$0")"; pwd)
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/php && bash install.sh install 72
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/php/versions/common && bash mcrypt.sh install 82
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/php/lib && bash libmcrypt.sh
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/scripts/quick && bash debug.sh
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/php/versions && /bin/bash all_mac.sh
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/php && bash install.sh install 55
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/php/versions/common && bash gd.sh install 73
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/php/versions/common && bash swoole.sh install 54
+
+
+# PHP_VER=52
+# echo "php${PHP_VER} -- start"
+# cmd_ext=$(ls -l $DIR/versions/$PHP_VER/ |awk '{print $9}')
+# cd $DIR && /bin/bash install.sh install $PHP_VER
+# for ii in $cmd_ext
+# do
+# if [ "install.sh" == "$ii" ];then
+# echo '' > /tmp/t.log
+# else
+# cd $DIR/versions/$PHP_VER && /bin/bash $ii install $PHP_VER
+# fi
+# done
+# echo "php${PHP_VER} -- end"
+
+
+PHP_VER_LIST=(54 55 56 70 71 72 73 74 80 81 82 83 84 85)
+# PHP_VER_LIST=(81)
+for PHP_VER in ${PHP_VER_LIST[@]}; do
+ echo "php${PHP_VER} -- start"
+ if [ ! -d /Users/midoks/Desktop/mwdev/server/php/${PHP_VER} ];then
+ cd /Users/midoks/Desktop/mwdev//server/mdserver-web/plugins/php && bash install.sh install ${PHP_VER}
+ fi
+ echo "php${PHP_VER} -- end"
+done
+
+cd $DIR
+PHP_VER_LIST=(54 55 56 70 71 72 73 74 80 81 82 83 84 85)
+# yar
+PHP_EXT_LIST=(ZendGuardLoader pdo mysqlnd sqlite3 openssl opcache mcrypt fileinfo \
+ exif gd intl pcntl memcache memcached redis imagemagick xdebug \
+ swoole yac apc mongo mongodb solr seaslog mbstring iconv)
+
+for PHP_VER in ${PHP_VER_LIST[@]}; do
+ echo "php${PHP_VER} -- start"
+
+ if [ ! -d /Users/midoks/Desktop/mwdev/server/php/${PHP_VER} ];then
+ echo "php${PHP_VER} is not install!"
+ continue
+ fi
+
+ NON_ZTS_FILENAME=`ls /Users/midoks/Desktop/mwdev/server/php/${PHP_VER}/lib/php/extensions | grep no-debug-non-zts`
+ for EXT in ${PHP_EXT_LIST[@]}; do
+ extFile=/www/server/php/${PHP_VER}/lib/php/extensions/${NON_ZTS_FILENAME}/${EXT}.so
+ echo "${PHP_VER} ${EXT} start"
+ if [ ! -f $extFile ];then
+ bash common.sh $PHP_VER install ${EXT}
+ fi
+ echo "${PHP_VER} ${EXT} end"
+ done
+
+ echo "php${PHP_VER} -- end"
+done
+
diff --git a/plugins/php/versions/all_test.sh b/plugins/php/versions/all_test.sh
new file mode 100644
index 000000000..d9ebd21b9
--- /dev/null
+++ b/plugins/php/versions/all_test.sh
@@ -0,0 +1,68 @@
+#! /bin/sh
+export PATH=$PATH:/opt/local/bin:/opt/local/sbin:/opt/local/share/man:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+DIR=$(cd "$(dirname "$0")"; pwd)
+ROOT_DIR=$(cd "$(dirname "$0")"; pwd)
+
+# cd /www/server/mdserver-web/scripts/quick && bash debug.sh
+# cd /www/server/mdserver-web/plugins/php/versions && /bin/bash all_test.sh
+
+# cd /www/server/mdserver-web/plugins/php && bash install.sh install 82
+# cd /www/server/mdserver-web/plugins/php/versions/common && bash gd.sh install 73
+# cd /www/server/mdserver-web/plugins/php/versions/common && bash swoole.sh install 54
+
+
+# PHP_VER=52
+# echo "php${PHP_VER} -- start"
+# cmd_ext=$(ls -l $DIR/versions/$PHP_VER/ |awk '{print $9}')
+# cd $DIR && /bin/bash install.sh install $PHP_VER
+# for ii in $cmd_ext
+# do
+# if [ "install.sh" == "$ii" ];then
+# echo '' > /tmp/t.log
+# else
+# cd $DIR/versions/$PHP_VER && /bin/bash $ii install $PHP_VER
+# fi
+# done
+# echo "php${PHP_VER} -- end"
+
+
+PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82 83 84 85)
+# PHP_VER_LIST=(81)
+for PHP_VER in ${PHP_VER_LIST[@]}; do
+ echo "php${PHP_VER} -- start"
+ if [ ! -d /www/server/php/${PHP_VER} ];then
+ cd /www/server/mdserver-web/plugins/php && bash install.sh install ${PHP_VER}
+ fi
+ echo "php${PHP_VER} -- end"
+done
+
+cd $DIR
+PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82 83 84 85)
+# yar
+PHP_EXT_LIST=(ZendGuardLoader pdo mysqlnd sqlite3 openssl opcache mcrypt fileinfo \
+ exif gd intl pcntl memcache memcached redis xdebug \
+ swoole yac apc mongo mongodb seaslog mbstring iconv event)
+
+for PHP_VER in ${PHP_VER_LIST[@]}; do
+ echo "php${PHP_VER} -- start"
+
+ if [ ! -d /www/server/php/${PHP_VER} ];then
+ echo "php${PHP_VER} is not install!"
+ continue
+ fi
+
+ NON_ZTS_FILENAME=`ls /www/server/php/${PHP_VER}/lib/php/extensions | grep no-debug-non-zts`
+ for EXT in ${PHP_EXT_LIST[@]}; do
+ extFile=/www/server/php/${PHP_VER}/lib/php/extensions/${NON_ZTS_FILENAME}/${EXT}.so
+ echo "${PHP_VER} ${EXT} start"
+ if [ ! -f $extFile ];then
+ bash common.sh $PHP_VER install ${EXT}
+ fi
+ echo "${PHP_VER} ${EXT} end"
+ done
+
+ echo "php${PHP_VER} -- end"
+done
+
diff --git a/plugins/php/versions/common.sh b/plugins/php/versions/common.sh
new file mode 100644
index 000000000..e6d15c554
--- /dev/null
+++ b/plugins/php/versions/common.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+version=$1
+action=$2
+extName=$3
+
+# echo $1,$2,$3
+
+# echo $curPath
+# echo $rootPath
+# echo $serverPath
+
+FILE=${curPath}/${version}/${extName}.sh
+FILE_COMMON=${curPath}/common/${extName}.sh
+
+
+if [ "$action" == 'install' ];then
+
+ if [ -f $FILE ];then
+ cd ${curPath}/${version} && bash ${extName}.sh install ${version}
+ elif [ -f $FILE_COMMON ];then
+ cd ${curPath}/common && bash ${extName}.sh install ${version}
+ else
+ echo 'no such extension'
+ fi
+fi
+
+
+if [ "$action" == 'uninstall' ];then
+ if [ -f $FILE ];then
+ cd ${curPath}/${version} && bash ${extName}.sh uninstall ${version}
+ elif [ -f $FILE_COMMON ];then
+ cd ${curPath}/common && bash ${extName}.sh uninstall ${version}
+ else
+ echo 'no such extension'
+ fi
+fi
+
+echo "cd ${curPath}/common && bash ${extName}.sh install ${version}"
+echo "cd ${curPath}/${version} && bash ${extName}.sh install ${version}"
+echo "cd ${curPath}/common && bash ${extName}.sh uninstall ${version}"
+echo "cd ${curPath}/${version} && bash ${extName}.sh uninstall ${version}"
diff --git a/plugins/php/versions/common/apcu.sh b/plugins/php/versions/common/apcu.sh
new file mode 100755
index 000000000..a15080730
--- /dev/null
+++ b/plugins/php/versions/common/apcu.sh
@@ -0,0 +1,100 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+LIBNAME=apcu
+# _LIBNAME=$(echo $LIBNAME | tr '[a-z]' '[A-Z]')
+LIBV=5.1.22
+sysName=`uname`
+actionType=$1
+version=$2
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/lib/php/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/lib/php/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config
+ make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ echo $extFile
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/bcmath.sh b/plugins/php/versions/common/bcmath.sh
new file mode 100755
index 000000000..341ad1d4c
--- /dev/null
+++ b/plugins/php/versions/common/bcmath.sh
@@ -0,0 +1,120 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+appPath=$(dirname "$curPath")
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=bcmath
+LIBV=0
+
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+# export PKG_CONFIG_PATH=/www/server/lib/libzip/lib/pkgconfig
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+
+ if [ "$version" == "83" ];then
+ CFLAGS="-std=c99" ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ else
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ fi
+
+ make clean && make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/brotli.sh b/plugins/php/versions/common/brotli.sh
new file mode 100755
index 000000000..87141bf8d
--- /dev/null
+++ b/plugins/php/versions/common/brotli.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=brotli
+LIBV=0.15.2
+sysName=`uname`
+actionType=$1
+version=$2
+
+if [ "$version" -lt "70" ];then
+ echo "not need"
+ exit 1
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --enable-brotli \
+ --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/curl.sh b/plugins/php/versions/common/curl.sh
new file mode 100755
index 000000000..609974209
--- /dev/null
+++ b/plugins/php/versions/common/curl.sh
@@ -0,0 +1,123 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+
+actionType=$1
+version=$2
+
+
+LIBNAME=curl
+LIBV=0
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ if [ "$sysName" == "Darwin" ];then
+
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+ LIB_DEPEND_DIR=`brew info curl | grep ${BREW_DIR}/Cellar/curl | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-curl=$(brew --prefix curl)"
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$LIB_DEPEND_DIR/lib/pkgconfig
+ fi
+
+
+ $serverPath/php/$version/bin/phpize
+ echo "./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS"
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+
+ make clean && make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/event.sh b/plugins/php/versions/common/event.sh
new file mode 100644
index 000000000..711b7746f
--- /dev/null
+++ b/plugins/php/versions/common/event.sh
@@ -0,0 +1,122 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=event
+LIBV=3.1.4
+
+
+if [ "$version" -lt "55" ];then
+ echo 'not need'
+ exit 0
+fi
+
+if [ "$version" -lt "83" ];then
+ LIBV=3.1.4
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config \
+ $OPTIONS
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return;
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/\[${LIBNAME}\]/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/exif.sh b/plugins/php/versions/common/exif.sh
new file mode 100755
index 000000000..eb12a21aa
--- /dev/null
+++ b/plugins/php/versions/common/exif.sh
@@ -0,0 +1,123 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+appPath=$(dirname "$curPath")
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=exif
+LIBV=0
+
+if [ "$version" == "53" ];then
+ echo "i wont support it"
+ exit
+fi
+
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+
+ FIND_C99=`cat Makefile|grep c99`
+ if [ "$FIND_C99" == "" ];then
+ sed -i $BAK 's/CFLAGS \=/CFLAGS \= -std=c99/g' Makefile
+ fi
+
+ make clean && make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/fileinfo.sh b/plugins/php/versions/common/fileinfo.sh
new file mode 100755
index 000000000..e0a481d17
--- /dev/null
+++ b/plugins/php/versions/common/fileinfo.sh
@@ -0,0 +1,142 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=fileinfo
+LIBV=0
+
+if [ "$version" == "53" ];then
+ echo "i wont support it"
+ exit
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+OSNAME=`cat ${rootPath}/data/osname.pl`
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+
+
+ # It is considered as a temporary bug
+ if [ "$version" == "81" ] || [ "$version" == "82" ];then
+ bash ${rootPath}/scripts/getos.sh
+ if [ "$OSNAME" == 'centos' ];then
+ FILE_softmagic=$sourcePath/php${version}/ext/${LIBNAME}/libmagic/softmagic.c
+ FIND_UNDEF_STRNDUP=`cat $FILE_softmagic|grep '#undef strndup'`
+ if [ "$version" -gt "74" ] && [ "$FIND_UNDEF_STRNDUP" == "" ];then
+ sed -i $BAK "s/char \*strndup/#undef strndup\nchar \*strndup/g" $FILE_softmagic
+ fi
+ fi
+ fi
+
+ FIND_C99=`cat Makefile|grep c99`
+ if [ "$version" -gt "74" ] && [ "$FIND_C99" == "" ];then
+ sed -i $BAK 's/CFLAGS \=/CFLAGS \= -std=gnu99/g' Makefile
+ fi
+
+ if [ "$version" -gt "80" ] && [ "$OSNAME" == 'centos' ];then
+ sed -i $BAK "s#CFLAGS = -g -O2#CFLAGS = -std=c99 -g#g" $sourcePath/php${version}/ext/${LIBNAME}/Makefile
+ fi
+
+
+ make clean && make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/gd.sh b/plugins/php/versions/common/gd.sh
new file mode 100755
index 000000000..06b275eac
--- /dev/null
+++ b/plugins/php/versions/common/gd.sh
@@ -0,0 +1,135 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+LIBNAME=gd
+LIBV=0
+
+
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+OSNAME_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+
+if [ "centos" == "$OSNAME" ]; then
+ if [ "$OSNAME_ID" != "9" ];then
+ if [ "$version" -lt "74" ];then
+ bash $curPath/gd_old.sh $1 $2
+ exit 0
+ fi
+ fi
+else
+ if [ "$version" -lt "74" ];then
+ bash $curPath/gd_old.sh $1 $2
+ exit 0
+ fi
+fi
+
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config \
+ --enable-gd \
+ --with-webp \
+ --with-xpm \
+ --with-jpeg \
+ --with-png-dir \
+ --with-freetype
+ #--enable-gd-jis-conv
+
+ make clean && make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/gd_old.sh b/plugins/php/versions/common/gd_old.sh
new file mode 100755
index 000000000..de8a647a5
--- /dev/null
+++ b/plugins/php/versions/common/gd_old.sh
@@ -0,0 +1,150 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=gd
+LIBV=0
+
+
+
+# if [ "$version" -lt "74" ];then
+# echo "not need!"
+# exit 1
+# fi
+
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+
+# cd ${rootPath}/plugins/php/lib && /bin/bash freetype_old.sh
+# OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype_old"
+# OPTIONS="${OPTIONS} --with-gd --enable-gd-native-ttf"
+# OPTIONS="${OPTIONS} --with-jpeg --with-jpeg-dir=/usr/lib"
+
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ cd ${rootPath}/plugins/php/lib && /bin/bash freetype_old.sh
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+ $serverPath/php/$version/bin/phpize
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ if [ "$version" -lt "55" ];then
+ echo "not need xmp"
+ else
+ OPTIONS="$OPTIONS --with-xpm-dir"
+ fi
+
+ find_ft2=`pkg-config --list-all | grep freetype2`
+ if [ "$find_ft2" == "" ];then
+ OPTIONS="$OPTIONS --with-freetype-dir=${serverPath}/lib/freetype_old"
+ fi
+
+
+ #--with-xpm
+ # =${serverPath}/lib/freetype_old
+ # =/usr/lib
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config \
+ $OPTIONS \
+ --with-gd \
+ --with-jpeg-dir \
+ --with-png-dir \
+ --with-webp-dir \
+ --with-zlib-dir \
+ --enable-gd-jis-conv \
+ # --enable-gd-native-ttf
+ make clean && make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/gettext.sh b/plugins/php/versions/common/gettext.sh
new file mode 100755
index 000000000..857bcaa64
--- /dev/null
+++ b/plugins/php/versions/common/gettext.sh
@@ -0,0 +1,122 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=gettext
+LIBV=0
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ if [ "$sysName" == "Darwin" ];then
+ OPTIONS="$OPTIONS --with-gettext=$(brew --prefix gettext)"
+ fi
+
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+
+ # PHP52需要,因为52关闭。所有注释掉
+ # FIND_C99=`cat Makefile|grep c99`
+ # if [ "$FIND_C99" == "" ];then
+ # sed -i $BAK 's/CFLAGS \=/CFLAGS \= -std=c99/g' Makefile
+ # fi
+
+ make clean && make && make install && make clean
+
+ # if [ -d $sourcePath/php${version} ];then
+ # cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ # fi
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/gmp.sh b/plugins/php/versions/common/gmp.sh
new file mode 100755
index 000000000..48b168905
--- /dev/null
+++ b/plugins/php/versions/common/gmp.sh
@@ -0,0 +1,122 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+appPath=$(dirname "$curPath")
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+LIBNAME=gmp
+LIBV=0
+
+if [ "$version" == "53" ];then
+ echo "i wont support it"
+ exit
+fi
+
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config
+
+ # FIND_C99=`cat Makefile|grep c99`
+ # if [ "$FIND_C99" == "" ];then
+ # sed -i $BAK 's/CFLAGS \=/CFLAGS \= -std=c99/g' Makefile
+ # fi
+
+ if [ "$sysName" == "Darwin" ];then
+ OPTIONS="$OPTIONS --with-gmp=$(brew --prefix gmp)"
+ fi
+
+ make clean && make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/iconv.sh b/plugins/php/versions/common/iconv.sh
new file mode 100755
index 000000000..f0fd1ecd4
--- /dev/null
+++ b/plugins/php/versions/common/iconv.sh
@@ -0,0 +1,116 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+appPath=$(dirname "$curPath")
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=iconv
+LIBV=0
+
+
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ cd ${rootPath}/plugins/php/lib && /bin/bash libiconv.sh
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ if [ "$sysName" == "Darwin" ];then
+ OPTIONS="$OPTIONS --with-iconv=${serverPath}/lib/libiconv"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/igbinary.sh b/plugins/php/versions/common/igbinary.sh
new file mode 100755
index 000000000..5128c9e81
--- /dev/null
+++ b/plugins/php/versions/common/igbinary.sh
@@ -0,0 +1,111 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+
+actionType=$1
+version=$2
+
+LIBNAME=igbinary
+LIBV=3.2.16
+
+if [ "$version" -lt "70" ];then
+ echo "not need"
+ exit 1
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return;
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.use_namespace/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/\[${LIBNAME}\]/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/imagemagick.sh b/plugins/php/versions/common/imagemagick.sh
new file mode 100755
index 000000000..591e1b4b1
--- /dev/null
+++ b/plugins/php/versions/common/imagemagick.sh
@@ -0,0 +1,124 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=imagick
+LIBV=3.8.0
+sysName=`uname`
+actionType=$1
+version=$2
+
+
+if [ "$version" == "53" ];then
+ echo 'not need'
+ exit 0
+elif [ "$version" -lt "70" ];then
+ LIBV=3.6.0
+else
+ echo 'ok'
+fi
+
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ # if [ "$version" -gt "74" ];then
+ # OPTIONS="$OPTIONS --disable-openmp"
+ # fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/intl.sh b/plugins/php/versions/common/intl.sh
new file mode 100755
index 000000000..c3be5072d
--- /dev/null
+++ b/plugins/php/versions/common/intl.sh
@@ -0,0 +1,155 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+
+# _os=`uname`
+
+# if [ ${_os} == "Darwin" ]; then
+# OSNAME='macos'
+# elif grep -Eq "openSUSE" /etc/*-release; then
+# OSNAME='opensuse'
+# elif grep -Eq "FreeBSD" /etc/*-release; then
+# OSNAME='freebsd'
+# elif grep -Eqi "CentOS" /etc/issue || grep -Eqi "CentOS" /etc/*-release; then
+# OSNAME='rhel'
+# elif grep -Eqi "Fedora" /etc/issue || grep -Eqi "Fedora" /etc/*-release; then
+# OSNAME='rhel'
+# elif grep -Eqi "Rocky" /etc/issue || grep -Eqi "Rocky" /etc/*-release; then
+# OSNAME='rhel'
+# elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eqi "AlmaLinux" /etc/*-release; then
+# OSNAME='rhel'
+# elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eqi "Amazon Linux" /etc/*-release; then
+# OSNAME='amazon'
+# elif grep -Eqi "Debian" /etc/issue || grep -Eqi "Debian" /etc/*-release; then
+# OSNAME='debian'
+# elif grep -Eqi "Ubuntu" /etc/issue || grep -Eqi "Ubuntu" /etc/*-release; then
+# OSNAME='ubuntu'
+# else
+# OSNAME='unknow'
+# fi
+
+actionType=$1
+version=$2
+
+
+LIBNAME=intl
+LIBV=0
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+OPTIONS=''
+if [ "$version" -lt "74" ];then
+
+ # cd /www/server/mdserver-web/plugins/php/lib && /bin/bash icu.sh
+ cd ${rootPath}/plugins/php/lib && /bin/bash icu.sh
+ OPTIONS="--with-icu-dir=${serverPath}/lib/icu"
+fi
+
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ if [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+ LIB_DEPEND_DIR=`brew info icu4c | grep ${BREW_DIR}/Cellar/icu4c | cut -d \ -f 1 | awk 'END {print}'`
+
+ OPTIONS="$OPTIONS --with-icu-dir=${serverPath}/lib/icu"
+ OPTIONS="$OPTIONS --enable-intl"
+ fi
+
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/ioncube.sh b/plugins/php/versions/common/ioncube.sh
new file mode 100755
index 000000000..1d6065c7d
--- /dev/null
+++ b/plugins/php/versions/common/ioncube.sh
@@ -0,0 +1,123 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+# support 55-74
+
+LIBNAME=ioncube
+LIBV=0
+sysName=`uname`
+actionType=$1
+version=$2
+IC_VERSION=${version:0:1}.${version:1:2}
+ARCH=`uname -m`
+
+if [ "$version" -gt "82" ];then
+ echo "not need"
+ exit 1
+fi
+
+DEFAULT_ARCH='x86-64'
+if [ "$ARCH" == "aarch64" ];then
+ DEFAULT_ARCH='aarch64'
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -f $php_lib/ioncube_loaders_lin.tar.gz ];then
+ wget -O $php_lib/ioncube_loaders_lin.tar.gz https://downloads.ioncube.com/loader_downloads/ioncube_loaders_lin_${DEFAULT_ARCH}.tar.gz
+ cd $php_lib && tar -zxvf ioncube_loaders_lin.tar.gz
+ fi
+
+ if [ ! -d $php_lib/ioncube ];then
+ cd $php_lib && tar -zxvf ioncube_loaders_lin.tar.gz
+ fi
+ cd $php_lib/ioncube
+
+ cp -rf $php_lib/ioncube/ioncube_loader_lin_${IC_VERSION}.so $extFile
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ sed -i $BAK "1i\[${LIBNAME}]" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "2i\zend_extension=${LIBNAME}.so" $serverPath/php/$version/etc/php.ini
+ # echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ # echo "zend_extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+
+ if [ -d $php_lib/ioncube ];then
+ rm -rf $php_lib/ioncube
+ fi
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$vphp not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/ldap.sh b/plugins/php/versions/common/ldap.sh
new file mode 100755
index 000000000..bae5dae75
--- /dev/null
+++ b/plugins/php/versions/common/ldap.sh
@@ -0,0 +1,110 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=ldap
+LIBV=0
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/lib.md b/plugins/php/versions/common/lib.md
new file mode 100644
index 000000000..e69de29bb
diff --git a/plugins/php/versions/common/mcrypt.sh b/plugins/php/versions/common/mcrypt.sh
new file mode 100755
index 000000000..d23e8d264
--- /dev/null
+++ b/plugins/php/versions/common/mcrypt.sh
@@ -0,0 +1,133 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=mcrypt
+LIBV=1.0.7
+
+if [ "$version" -lt "72" ];then
+ echo "not need"
+ exit 1
+fi
+
+if [ "$version" -ge "84" ];then
+ LIBV=1.0.7
+ echo "not need"
+ exit 1
+fi
+
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ cd ${rootPath}/plugins/php/lib && bash libmcrypt.sh
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ if [ "$sysName" == "Darwin" ];then
+
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+ LIB_DEPEND_DIR=`brew info mcrypt | grep ${BREW_DIR}/Cellar/mcrypt | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-mcrypt=$(brew --prefix mcrypt)"
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$LIB_DEPEND_DIR/lib/pkgconfig
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ # cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return;
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.use_namespace/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/\[${LIBNAME}\]/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/memcache.sh b/plugins/php/versions/common/memcache.sh
new file mode 100755
index 000000000..e326d3119
--- /dev/null
+++ b/plugins/php/versions/common/memcache.sh
@@ -0,0 +1,119 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=memcache
+LIBV=2.2.7
+sysName=`uname`
+actionType=$1
+version=$2
+
+if [ "$version" -gt "56" ] && [ "$version" -lt "80" ];then
+ LIBV=4.0.5.2
+fi
+
+if [ "$version" -gt "74" ];then
+ LIBV=8.0
+fi
+
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config \
+ $OPTIONS \
+ --enable-memcache --with-zlib-dir
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install memcache, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/memcached.sh b/plugins/php/versions/common/memcached.sh
new file mode 100755
index 000000000..ee2e4142a
--- /dev/null
+++ b/plugins/php/versions/common/memcached.sh
@@ -0,0 +1,137 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=memcached
+LIBV=3.3.0
+sysName=`uname`
+actionType=$1
+version=$2
+
+if [ "$version" -lt "70" ];then
+ LIBV=2.2.0
+fi
+
+
+if [ "$version" == "85" ];then
+ LIBV=3.4.0
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+
+ if [ "$sysName" == "Darwin" ];then
+ OPTIONS="$OPTIONS --with-zlib-dir=$(brew --prefix zlib)"
+ OPTIONS="$OPTIONS --with-libmemcached-dir=$(brew --prefix libmemcached)"
+ else
+ pkg-config --exists libmemcached
+ if [ "$?" != "0" ]; then
+ cd ${rootPath}/plugins/php/lib && /bin/bash libmemcached.sh
+ OPTIONS="$OPTIONS --with-libmemcached-dir=${serverPath}/lib/libmemcached"
+ fi
+ fi
+
+ # sed -i '_bak' "3237,3238s#ulong#zend_ulong#g" $php_lib/${LIBNAME}-${LIBV}/php_memcached.c
+ cd $php_lib/${LIBNAME}-${LIBV}
+ $serverPath/php/$version/bin/phpize
+
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config \
+ $OPTIONS \
+ --enable-memcached \
+ --disable-memcached-sasl
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/mongo.sh b/plugins/php/versions/common/mongo.sh
new file mode 100755
index 000000000..ca3eb4b44
--- /dev/null
+++ b/plugins/php/versions/common/mongo.sh
@@ -0,0 +1,119 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=mongo
+LIBV=1.6.16
+sysName=`uname`
+actionType=$1
+version=$2
+
+if [ "$version" -ge "70" ];then
+ echo "not need"
+ exit 1
+fi
+
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ OPTIONS=''
+ if [ $sysName == 'Darwin' ]; then
+ LIB_DEPEND_DIR=`brew info openssl | grep /usr/local/Cellar/openssl | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="--with-openssl-dir=${LIB_DEPEND_DIR}"
+ fi
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$vphp not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/mongodb.sh b/plugins/php/versions/common/mongodb.sh
new file mode 100755
index 000000000..96228c86b
--- /dev/null
+++ b/plugins/php/versions/common/mongodb.sh
@@ -0,0 +1,129 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+LIBNAME=mongodb
+LIBV=1.13.0
+sysName=`uname`
+actionType=$1
+version=$2
+
+if [ "$version" -ge '74' ];then
+ LIBV=1.20.0
+fi
+
+if [ "$version" == '71' ];then
+ LIBV=1.11.1
+fi
+
+if [ "$version" == '70' ];then
+ LIBV=1.7.5
+fi
+
+if [ "$version" == '56' ];then
+ LIBV=1.7.4
+fi
+
+if [ "$version" == '55' ];then
+ LIBV=1.5.3
+fi
+
+if [ "$version" -lt '55' ];then
+ echo "not need"
+ exit 1
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$vphp not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/mysql_xdevapi.sh b/plugins/php/versions/common/mysql_xdevapi.sh
new file mode 100755
index 000000000..4905f78a1
--- /dev/null
+++ b/plugins/php/versions/common/mysql_xdevapi.sh
@@ -0,0 +1,110 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=mysql_xdevapi
+LIBV=8.0.30
+sysName=`uname`
+actionType=$1
+version=$2
+
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/${version}/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/opcache.sh b/plugins/php/versions/common/opcache.sh
new file mode 100755
index 000000000..678739614
--- /dev/null
+++ b/plugins/php/versions/common/opcache.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+LIBNAME=opcache
+sysName=`uname`
+actionType=$1
+version=$2
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ # OPcache 黑名单文件位置。
+ # 黑名单文件为文本文件,包含了不进行预编译优化的文件名,每行一个文件名。
+ # 黑名单中的文件名可以使用通配符,也可以使用前缀。 此文件中以分号(;)开头的行将被视为注释。
+ OP_BL=${serverPath}/php/opcache-blacklist.txt
+ if [ ! -f $OP_BL ];then
+ touch $OP_BL
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[opcache]" >> $serverPath/php/$version/etc/php.ini
+ echo "zend_extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.enable=1" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.memory_consumption=128" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.interned_strings_buffer=8" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.max_accelerated_files=4000" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.revalidate_freq=60" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.fast_shutdown=1" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.enable_cli=1" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.jit=1205" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.jit_buffer_size=64M" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.save_comments=0" >> $serverPath/php/$version/etc/php.ini
+ echo "opcache.blacklist_filename=${OP_BL}" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/openssl.sh b/plugins/php/versions/common/openssl.sh
new file mode 100755
index 000000000..2813eff2a
--- /dev/null
+++ b/plugins/php/versions/common/openssl.sh
@@ -0,0 +1,95 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=openssl
+LIBV=0
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ if [ "$version" -lt "70" ];then
+ bash $curPath/openssl_low_version.sh $actionType $version
+ return
+ fi
+
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "locked" > $extFile
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ if [ -f "/etc/ssl/certs/ca-certificates.crt" ];then
+ echo "openssl.cafile=/etc/ssl/certs/ca-certificates.crt" >> $serverPath/php/$version/etc/php.ini
+ elif [ -f "/etc/pki/tls/certs/ca-bundle.crt" ];then
+ echo "openssl.cafile=/etc/pki/tls/certs/ca-bundle.crt" >> $serverPath/php/$version/etc/php.ini
+ fi
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/openssl_bak.sh b/plugins/php/versions/common/openssl_bak.sh
new file mode 100755
index 000000000..b011245cb
--- /dev/null
+++ b/plugins/php/versions/common/openssl_bak.sh
@@ -0,0 +1,177 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=openssl
+LIBV=0
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ # cd ${rootPath}/plugins/php/lib && /bin/bash openssl_10.sh
+ if [ "$version" -lt "81" ];then
+ cd ${rootPath}/plugins/php/lib && /bin/bash openssl_10.sh
+ fi
+
+ if [ "$version" -gt "82" ];then
+ cd ${rootPath}/plugins/php/lib && /bin/bash openssl.sh
+ fi
+
+ if [ "$sysName" == "Darwin" ] ;then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ LIB_DEPEND_DIR=`brew info openssl@1.0 | grep ${BREW_DIR}/Cellar/openssl | cut -d \ -f 1 | awk 'END {print}'`
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+ if [ ! -f "config.m4" ];then
+ mv config0.m4 config.m4
+ fi
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ # openssl_version=`pkg-config openssl --modversion`
+ # export PKG_CONFIG_PATH=$serverPath/lib/openssl10/lib/pkgconfig
+ if [ "$version" -lt "81" ] && [ "$sysName" != "Darwin" ];then
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl10/lib/pkgconfig
+ fi
+
+ # Darwin
+ # otool -L /Users/midoks/Desktop/mwdev/server/php/83/bin/php
+ # lldb /Users/midoks/Desktop/mwdev/server/php/83/bin/php -r 'phpinfo()'
+ # otool -L /Users/midoks/Desktop/mwdev/server/php/83/lib/php/extensions/no-debug-non-zts-20230831/openssl.so
+ # ldd /www/server/php/83/bin/php
+
+ if [ "$version" -lt "84" ] && [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+ LIB_DEPEND_DIR=`brew info openssl@1.0 | grep ${BREW_DIR}/Cellar/openssl@1.0 | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl@1.0)"
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include"
+ export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz"
+
+ echo "$LIB_DEPEND_DIR/lib/pkgconfig"
+ fi
+
+ if [ "$version" -ge "84" ] && [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+ LIB_DEPEND_DIR=`brew info openssl | grep ${BREW_DIR}/Cellar/openssl | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl)"
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include"
+ export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz"
+ fi
+
+ # if [ "$version" -gt "82" ] && [ "$sysName" == "Darwin" ];then
+ # export PKG_CONFIG_PATH=$serverPath/lib/openssl/lib/pkgconfig
+ # fi
+
+
+ $serverPath/php/$version/bin/phpize
+ # --with-openssl
+ echo "./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS"
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+ if [ -f "/etc/ssl/certs/ca-certificates.crt" ];then
+ echo "openssl.cafile=/etc/ssl/certs/ca-certificates.crt" >> $serverPath/php/$version/etc/php.ini
+ elif [ -f "/etc/pki/tls/certs/ca-bundle.crt" ];then
+ echo "openssl.cafile=/etc/pki/tls/certs/ca-bundle.crt" >> $serverPath/php/$version/etc/php.ini
+ fi
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/openssl_low_version.sh b/plugins/php/versions/common/openssl_low_version.sh
new file mode 100755
index 000000000..b011245cb
--- /dev/null
+++ b/plugins/php/versions/common/openssl_low_version.sh
@@ -0,0 +1,177 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=openssl
+LIBV=0
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ # cd ${rootPath}/plugins/php/lib && /bin/bash openssl_10.sh
+ if [ "$version" -lt "81" ];then
+ cd ${rootPath}/plugins/php/lib && /bin/bash openssl_10.sh
+ fi
+
+ if [ "$version" -gt "82" ];then
+ cd ${rootPath}/plugins/php/lib && /bin/bash openssl.sh
+ fi
+
+ if [ "$sysName" == "Darwin" ] ;then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+
+ LIB_DEPEND_DIR=`brew info openssl@1.0 | grep ${BREW_DIR}/Cellar/openssl | cut -d \ -f 1 | awk 'END {print}'`
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+ if [ ! -f "config.m4" ];then
+ mv config0.m4 config.m4
+ fi
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ # openssl_version=`pkg-config openssl --modversion`
+ # export PKG_CONFIG_PATH=$serverPath/lib/openssl10/lib/pkgconfig
+ if [ "$version" -lt "81" ] && [ "$sysName" != "Darwin" ];then
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl10/lib/pkgconfig
+ fi
+
+ # Darwin
+ # otool -L /Users/midoks/Desktop/mwdev/server/php/83/bin/php
+ # lldb /Users/midoks/Desktop/mwdev/server/php/83/bin/php -r 'phpinfo()'
+ # otool -L /Users/midoks/Desktop/mwdev/server/php/83/lib/php/extensions/no-debug-non-zts-20230831/openssl.so
+ # ldd /www/server/php/83/bin/php
+
+ if [ "$version" -lt "84" ] && [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+ LIB_DEPEND_DIR=`brew info openssl@1.0 | grep ${BREW_DIR}/Cellar/openssl@1.0 | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl@1.0)"
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include"
+ export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz"
+
+ echo "$LIB_DEPEND_DIR/lib/pkgconfig"
+ fi
+
+ if [ "$version" -ge "84" ] && [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+ LIB_DEPEND_DIR=`brew info openssl | grep ${BREW_DIR}/Cellar/openssl | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl)"
+ export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig
+ export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include"
+ export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz"
+ fi
+
+ # if [ "$version" -gt "82" ] && [ "$sysName" == "Darwin" ];then
+ # export PKG_CONFIG_PATH=$serverPath/lib/openssl/lib/pkgconfig
+ # fi
+
+
+ $serverPath/php/$version/bin/phpize
+ # --with-openssl
+ echo "./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS"
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+ if [ -f "/etc/ssl/certs/ca-certificates.crt" ];then
+ echo "openssl.cafile=/etc/ssl/certs/ca-certificates.crt" >> $serverPath/php/$version/etc/php.ini
+ elif [ -f "/etc/pki/tls/certs/ca-bundle.crt" ];then
+ echo "openssl.cafile=/etc/pki/tls/certs/ca-bundle.crt" >> $serverPath/php/$version/etc/php.ini
+ fi
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/pcntl.sh b/plugins/php/versions/common/pcntl.sh
new file mode 100755
index 000000000..8e01f8a0e
--- /dev/null
+++ b/plugins/php/versions/common/pcntl.sh
@@ -0,0 +1,110 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=pcntl
+LIBV=0
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/pdo_pgsql.sh b/plugins/php/versions/common/pdo_pgsql.sh
new file mode 100755
index 000000000..7ff8e289d
--- /dev/null
+++ b/plugins/php/versions/common/pdo_pgsql.sh
@@ -0,0 +1,110 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+
+actionType=$1
+version=$2
+
+LIBNAME=pdo_pgsql
+LIBV=0
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+OPTIONS=''
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/pgsql.sh b/plugins/php/versions/common/pgsql.sh
new file mode 100755
index 000000000..86cd59f34
--- /dev/null
+++ b/plugins/php/versions/common/pgsql.sh
@@ -0,0 +1,110 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+
+actionType=$1
+version=$2
+
+LIBNAME=pgsql
+LIBV=0
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+OPTIONS=''
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/phalcon.sh b/plugins/php/versions/common/phalcon.sh
new file mode 100755
index 000000000..b80ddde9f
--- /dev/null
+++ b/plugins/php/versions/common/phalcon.sh
@@ -0,0 +1,129 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=phalcon
+LIBV=4.1.2
+
+if [ "$version" -lt "73" ];then
+ echo "not support!"
+ exit 1
+fi
+
+if [ "$version" -gt "73" ];then
+ LIBV=5.2.3
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ while [[ ! -f "$extFile" ]];
+ do
+ echo -e ".\c"
+ sleep 0.5
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ fi
+ let n+=1
+ if [ $n -gt 8 ];then
+ echo "WAIT " $n "TIMES FAIL!"
+ return;
+ fi
+ done
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/readline.sh b/plugins/php/versions/common/readline.sh
new file mode 100755
index 000000000..71c13ad56
--- /dev/null
+++ b/plugins/php/versions/common/readline.sh
@@ -0,0 +1,122 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+# cd /www/server/mdserver-web/plugins/php/versions/common && bash readline.sh install 81
+
+curPath=`pwd`
+
+appPath=$(dirname "$curPath")
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=readline
+LIBV=0
+
+if [ "$version" -lt "74" ];then
+ echo "not need"
+ exit 0
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+ if [ ! -f "config.m4" ];then
+ mv config0.m4 config.m4
+ fi
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ cd ${rootPath}/plugins/php/lib && /bin/bash libedit.sh
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${serverPath}/lib/libedit/lib/pkgconfig
+ OPTIONS="$OPTIONS --with-libedit=${serverPath}/lib/libedit"
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/redis.sh b/plugins/php/versions/common/redis.sh
new file mode 100755
index 000000000..994f83a0f
--- /dev/null
+++ b/plugins/php/versions/common/redis.sh
@@ -0,0 +1,122 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=redis
+LIBV=6.3.0
+sysName=`uname`
+actionType=$1
+version=$2
+
+if [ "$version" == "52" ];then
+ LIBV=2.2.7
+elif [ "$version" -lt "70" ];then
+ LIBV=4.2.0
+elif [ "$version" -lt "80" ];then
+ LIBV=5.3.7
+elif [ "$version" -gt "80" ];then
+ LIBV=6.3.0
+else
+ echo 'ok'
+fi
+
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/${version}/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/seaslog.sh b/plugins/php/versions/common/seaslog.sh
new file mode 100755
index 000000000..5ec0288fe
--- /dev/null
+++ b/plugins/php/versions/common/seaslog.sh
@@ -0,0 +1,120 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=SeasLog
+_LIBNAME=$(echo $LIBNAME | tr '[A-Z]' '[a-z]')
+LIBV=2.2.0
+sysName=`uname`
+actionType=$1
+version=$2
+
+
+if [ "$version" -lt "72" ];then
+ LIBV=2.0.2
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${_LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${_LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+
+ if [ ! -f "$extFile" ];then
+
+ OPTIONS=''
+ if [ $sysName == 'Darwin' ]; then
+ OPTIONS="${OPTIONS} --with-curl=${serverPath}/lib/curl"
+ fi
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+ sleep 1
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ _LIBNAME=$(echo $LIBNAME | tr '[A-Z]' '[a-z]')
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${_LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${_LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ _LIBNAME=$(echo $LIBNAME | tr '[A-Z]' '[a-z]')
+ sed -i $BAK "/${_LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${_LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/sg11.sh b/plugins/php/versions/common/sg11.sh
new file mode 100644
index 000000000..8bcf13f33
--- /dev/null
+++ b/plugins/php/versions/common/sg11.sh
@@ -0,0 +1,138 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+# https://www.sourceguardian.com/loaders.html
+
+# support 52-81
+
+LIBNAME=sg11
+LIBV=0
+sysName=`uname`
+actionType=$1
+version=$2
+SG_VER=${version:0:1}.${version:1:2}
+
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+
+Install_lib()
+{
+ bash ${rootPath}/scripts/getos.sh
+ OSNAME=`cat ${rootPath}/data/osname.pl`
+ if [ "$OSNAME" == 'macos' ];then
+ VERSION_ID=none
+ else
+ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+ fi
+
+ echo "${OSNAME}:${VERSION_ID}"
+
+ DEFAULT_OSNAME=linux-x86_64
+ SUFFIX_NAME=lin
+ if [ "$OSNAME" == 'macos' ];then
+ DEFAULT_OSNAME=macosx
+ SUFFIX_NAME=dar
+ fi
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ mkdir -p $php_lib/sg11
+ if [ ! -f $php_lib/sg11_loaders.tar.bz2 ];then
+ curl -sSLo $php_lib/sg11_loaders.tar.bz2 https://www.sourceguardian.com/loaders/download/loaders.tar.bz2
+ echo "cd $php_lib && tar -jxvf $php_lib/sg11_loaders.tar.bz2 -C $php_lib/sg11"
+ cd $php_lib && tar -jxvf $php_lib/sg11_loaders.tar.bz2 -C $php_lib/sg11
+ fi
+
+
+ if [ ! -d $php_lib/sg11/macosx ];then
+ cd $php_lib && tar -jxvf $php_lib/sg11_loaders.tar.bz2 -C $php_lib/sg11
+ fi
+ cd $php_lib/sg11
+ # echo "mv $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.lin $extFile"
+ if [ -f $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.${SUFFIX_NAME} ];then
+ cp -rf $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.${SUFFIX_NAME} $extFile
+ else
+ echo 'Not supported temporarily'
+ exit
+ fi
+
+ if [ "$OSNAME" == 'macos' ];then
+ xattr -c * $extFile
+ fi
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$vphp not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/sodium.sh b/plugins/php/versions/common/sodium.sh
new file mode 100755
index 000000000..25cb6bc07
--- /dev/null
+++ b/plugins/php/versions/common/sodium.sh
@@ -0,0 +1,125 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+
+actionType=$1
+version=$2
+
+sysName=`uname`
+LIBNAME=sodium
+LIBV=2.0.23
+
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ "$sysName" != "Darwin" ];then
+ cd ${rootPath}/plugins/php/lib && bash libsodium.sh
+ fi
+
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/lib${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/lib${LIBNAME}-${LIBV}
+
+
+ OPTIONS=""
+ if [ "$sysName" == "Darwin" ];then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+ LIB_DEPEND_DIR=`brew info libsodium | grep ${BREW_DIR}/Cellar/libsodium | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="$OPTIONS --with-sodium=${LIB_DEPEND_DIR}"
+ else
+ OPTIONS="$OPTIONS --with-sodium=$serverPath/lib/libsodium"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ if [ -d $php_lib/lib${LIBNAME}-${LIBV} ];then
+ cd $php_lib && rm -rf $php_lib/lib${LIBNAME}-${LIBV}
+ fi
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/\[${LIBNAME}\]/d" $serverPath/php/$version/etc/php.ini
+
+ rm -rf $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/solr.sh b/plugins/php/versions/common/solr.sh
new file mode 100755
index 000000000..55dec099d
--- /dev/null
+++ b/plugins/php/versions/common/solr.sh
@@ -0,0 +1,127 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=solr
+LIBV=2.8.1
+sysName=`uname`
+actionType=$1
+version=$2
+
+if [ "$version" -lt "72" ];then
+ LIBV=2.4.0
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "$isInstall" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ if [ $sysName == 'Darwin' ]; then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+ LIB_DEPEND_DIR=`brew info curl | grep ${BREW_DIR}/Cellar/curl | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="${OPTIONS} --with-curl=${LIB_DEPEND_DIR}"
+ fi
+
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ if [ -d $php_lib/lib${LIBNAME}-${LIBV} ];then
+ cd $php_lib && rm -rf $php_lib/lib${LIBNAME}-${LIBV}
+ fi
+ fi
+ sleep 1
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ extFile=$extDir${LIBNAME}.so
+ if [ ! -f "$extFile" ];then
+ echo "php$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$vphp not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/swoole.sh b/plugins/php/versions/common/swoole.sh
new file mode 100755
index 000000000..6e7475ed5
--- /dev/null
+++ b/plugins/php/versions/common/swoole.sh
@@ -0,0 +1,148 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=swoole
+LIBV=5.1.6
+
+if [ "$version" == "85" ];then
+ echo "not need"
+ exit 1
+fi
+
+if [ "$version" -lt "70" ];then
+ LIBV=1.10.1
+elif [ "$version" == "70" ];then
+ LIBV=4.3.0
+elif [ "$version" == "71" ];then
+ LIBV=4.5.2
+elif [ "$version" -le "74" ];then
+ LIBV=4.8.10
+elif [ "$version" -lt "80" ];then
+ LIBV=6.0.2
+elif [ "$version" -gt "80" ];then
+ LIBV=6.0.2
+else
+ echo 'other?'
+ exit 0
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ cd ${rootPath}/plugins/php/lib && /bin/bash openssl_11.sh
+
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config \
+ $OPTIONS \
+ --enable-openssl \
+ --with-openssl-dir=$serverPath/lib/openssl11 \
+ --enable-sockets
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ while [[ ! -f "$extFile" ]];
+ do
+ echo -e ".\c"
+ sleep 0.5
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ fi
+ let n+=1
+ if [ $n -gt 8 ];then
+ echo "WAIT " $n "TIMES FAIL!"
+ return;
+ fi
+ done
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/xdebug.sh b/plugins/php/versions/common/xdebug.sh
new file mode 100755
index 000000000..a09ad77a5
--- /dev/null
+++ b/plugins/php/versions/common/xdebug.sh
@@ -0,0 +1,128 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=xdebug
+LIBV=3.4.5
+sysName=`uname`
+actionType=$1
+version=$2
+
+if [ "$version" == "85" ];then
+ echo "not need"
+ exit 1
+fi
+
+
+if [ "$version" -lt "70" ];then
+ LIBV=2.2.7
+fi
+
+if [ "$version" -le "80" ];then
+ LIBV=2.7.0
+fi
+
+if [ "$version" -gt "80" ];then
+ LIBV=3.4.5
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/${version}/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "zend_extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/xhprof.sh b/plugins/php/versions/common/xhprof.sh
new file mode 100755
index 000000000..02198ee7a
--- /dev/null
+++ b/plugins/php/versions/common/xhprof.sh
@@ -0,0 +1,119 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+LIBNAME=xhprof
+LIBV=2.3.10
+sysName=`uname`
+actionType=$1
+version=$2
+
+if [ "$version" -lt "70" ];then
+ LIBV=0.9.4
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}/extension
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --enable-xhprof \
+ --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ if [ ! -f /tmp/xhprof ];then
+ mkdir -p /tmp/xhprof
+ chown -R www:www /tmp/xhprof
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+ echo "${LIBNAME}.output_dir=/tmp/xhprof" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/yac.sh b/plugins/php/versions/common/yac.sh
new file mode 100755
index 000000000..78af0c5b9
--- /dev/null
+++ b/plugins/php/versions/common/yac.sh
@@ -0,0 +1,113 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+LIBNAME=yac
+LIBV=2.3.1
+
+
+if [ "$version" -lt "70" ];then
+ echo "not need"
+ exit 1
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+
+Install_lib()
+{
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "$isInstall" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config
+ make clean && make && make install && make clean
+ cd ..
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return;
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+ echo "${LIBNAME}.use_namespace=1" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php$version 未安装yaf,请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/\[${LIBNAME}\]/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/yaf.sh b/plugins/php/versions/common/yaf.sh
new file mode 100755
index 000000000..5bb1af208
--- /dev/null
+++ b/plugins/php/versions/common/yaf.sh
@@ -0,0 +1,121 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+sysName=`uname`
+LIBNAME=yaf
+LIBV=3.3.6
+
+if [ "$version" -lt "70" ]; then
+ LIBV=2.3.5
+elif [ "$version" -lt "74" ]; then
+ LIBV=3.2.5
+elif [ "$version" -lt "80" ]; then
+ LIBV=3.3.5
+fi
+
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return;
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+ echo "${LIBNAME}.use_namespace=1" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.use_namespace/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/\[${LIBNAME}\]/d" $serverPath/php/$version/etc/php.ini
+
+ rm -rf $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/yaml.sh b/plugins/php/versions/common/yaml.sh
new file mode 100755
index 000000000..9c58704a5
--- /dev/null
+++ b/plugins/php/versions/common/yaml.sh
@@ -0,0 +1,117 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+sysName=`uname`
+LIBNAME=yaml
+LIBV=2.2.4
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ if [ $sysName == 'Darwin' ]; then
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+ LIB_DEPEND_DIR=`brew info libyaml | grep ${BREW_DIR}/Cellar/libyaml | cut -d \ -f 1 | awk 'END {print}'`
+ OPTIONS="${OPTIONS} --with-yaml=${LIB_DEPEND_DIR}"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return;
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/\[${LIBNAME}\]/d" $serverPath/php/$version/etc/php.ini
+
+ rm -rf $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/yar.sh b/plugins/php/versions/common/yar.sh
new file mode 100755
index 000000000..0b9c90e8b
--- /dev/null
+++ b/plugins/php/versions/common/yar.sh
@@ -0,0 +1,119 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=yar
+LIBV=2.3.2
+
+if [ "$version" -lt "70" ];then
+ LIBV=1.2.5
+fi
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ php_lib=$sourcePath/php_lib
+ mkdir -p $php_lib
+ if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then
+ if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then
+ wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
+ fi
+ cd $php_lib/${LIBNAME}-${LIBV}
+
+ OPTIONS=''
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config \
+ $OPTIONS \
+ --with-curl=$serverPath/lib/curl
+ make clean && make && make install && make clean
+
+ cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV}
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return;
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+ echo "${LIBNAME}.expose_info=false" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ echo "php-$version not install ${LIBNAME}, Plese select other version!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.use_namespace/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/\[${LIBNAME}\]/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/zip.sh b/plugins/php/versions/common/zip.sh
new file mode 100755
index 000000000..1c4021502
--- /dev/null
+++ b/plugins/php/versions/common/zip.sh
@@ -0,0 +1,127 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }
+function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; }
+function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; }
+function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; }
+
+curPath=`pwd`
+appPath=$(dirname "$curPath")
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+
+actionType=$1
+version=$2
+
+LIBNAME=zip
+LIBV=0
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+if [ ! -d $serverPath/lib/libzip ];then
+ cd ${rootPath}/plugins/php/lib && /bin/bash libzip.sh
+fi
+
+export PKG_CONFIG_PATH=${serverPath}/lib/libzip/lib/pkgconfig
+
+# ZIP_OPTION='--with-zip'
+# libzip_version=`pkg-config libzip --modversion`
+# if version_lt "$libzip_version" "0.11.0" ;then
+# cd ${rootPath}/plugins/php/lib && /bin/bash libzip.sh
+# export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig
+# ZIP_OPTION="--with-zip=$serverPath/lib/libzip"
+# fi
+
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config --with-zip
+
+ make clean && make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/common/zlib.sh b/plugins/php/versions/common/zlib.sh
new file mode 100755
index 000000000..81291da52
--- /dev/null
+++ b/plugins/php/versions/common/zlib.sh
@@ -0,0 +1,117 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+
+appPath=$(dirname "$curPath")
+
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source/php
+SYS_ARCH=`arch`
+actionType=$1
+version=$2
+
+LIBNAME=zlib
+LIBV=0
+
+LIB_PATH_NAME=lib/php
+if [ -d $serverPath/php/${version}/lib64 ];then
+ LIB_PATH_NAME=lib64
+fi
+
+NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
+extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
+
+sysName=`uname`
+if [ "$sysName" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+# --with-zlib-dir=$serverPath/lib/zlib
+Install_lib()
+{
+
+ isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
+ if [ "${isInstall}" != "" ];then
+ echo "php-$version 已安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+
+ if [ ! -d $sourcePath/php${version}/ext ];then
+ cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install
+ fi
+
+ cd $sourcePath/php${version}/ext/${LIBNAME}
+
+ if [ ! -f "config.m4" ];then
+ mv config0.m4 config.m4
+ fi
+
+ OPTIONS=""
+ if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then
+ OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu"
+ fi
+
+ $serverPath/php/$version/bin/phpize
+ ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS
+ make && make install && make clean
+
+ if [ -d $sourcePath/php${version} ];then
+ cd ${sourcePath} && rm -rf $sourcePath/php${version}
+ fi
+
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "ERROR!"
+ return
+ fi
+
+ echo "" >> $serverPath/php/$version/etc/php.ini
+ echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
+ echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
+
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==========================================================='
+ echo 'successful!'
+}
+
+
+Uninstall_lib()
+{
+ if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
+ echo "php-$version 未安装,请选择其它版本!"
+ return
+ fi
+
+ if [ ! -f "$extFile" ];then
+ echo "php-$version 未安装${LIBNAME},请选择其它版本!"
+ return
+ fi
+
+ echo $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
+ sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
+
+ rm -f $extFile
+ cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart
+ echo '==============================================='
+ echo 'successful!'
+}
+
+
+
+if [ "$actionType" == 'install' ];then
+ Install_lib
+elif [ "$actionType" == 'uninstall' ];then
+ Uninstall_lib
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/lib.sh b/plugins/php/versions/lib.sh
new file mode 100644
index 000000000..2e4584a82
--- /dev/null
+++ b/plugins/php/versions/lib.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+version=$1
+action=$2
+
+if [ -f /lib/systemd/system/php${version}.service ];then
+ systemctl ${action} php${version}
+elif [ -f /usr/lib/systemd/system/php${version}.service ]; then
+ systemctl ${action} php${version}
+else
+ $serverPath/php/init.d/php${version} ${action}
+fi
\ No newline at end of file
diff --git a/plugins/php/versions/phplib.conf b/plugins/php/versions/phplib.conf
new file mode 100755
index 000000000..9495df746
--- /dev/null
+++ b/plugins/php/versions/phplib.conf
@@ -0,0 +1,925 @@
+[
+ {
+ "name": "ZendGuardLoader",
+ "versions": [
+ "53",
+ "54",
+ "56"
+ ],
+ "type": "脚本解密",
+ "msg": "用于解密ZendGuard加密脚本!",
+ "shell": "zend_guard_loader.sh",
+ "check": "ZendGuardLoader.so"
+ },
+ {
+ "name": "ZendOptimizer",
+ "versions": [
+ "52"
+ ],
+ "type": "脚本解密",
+ "msg": "用于解密ZendOptimizer加密脚本!",
+ "shell": "zend_optimizer.sh",
+ "check": "ZendOptimizer.so"
+ },
+ {
+ "name": "sg11",
+ "versions": [
+ "52",
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83"
+ ],
+ "type": "脚本解密",
+ "msg": "用于解密SG11加密脚本!",
+ "shell": "sg11.sh",
+ "check": "sg11.so"
+ },
+ {
+ "name": "ionCube",
+ "versions": [
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83"
+ ],
+ "type": "脚本解密",
+ "msg": "用于解密ionCube Encoder加密脚本!",
+ "shell": "ioncube.sh",
+ "check": "ioncube.so"
+ },
+ {
+ "name": "sodium",
+ "versions": [
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "加密库",
+ "msg": "Sodium加密库的包装器",
+ "shell": "sodium.sh",
+ "check": "sodium.so"
+ },
+ {
+ "name": "brotli",
+ "versions": [
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "压缩",
+ "msg": "压缩算法",
+ "shell": "brotli.sh",
+ "check": "brotli.so"
+ },
+ {
+ "name": "gmp",
+ "versions": [
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "运算库",
+ "msg": "一个开源的数学运算库",
+ "shell": "gmp.sh",
+ "check": "gmp.so"
+ },
+ {
+ "name": "opcache",
+ "versions": [
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "缓存器",
+ "msg": "用于加速PHP脚本!",
+ "shell": "opcache.sh",
+ "check": "opcache.so"
+ },
+ {
+ "name": "openssl",
+ "versions": [
+ "52",
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "SSL",
+ "shell": "openssl.sh",
+ "check": "openssl.so"
+ },
+ {
+ "name": "mcrypt",
+ "versions": [
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "加密软件",
+ "shell": "mcrypt.sh",
+ "check": "mcrypt.so"
+ },
+ {
+ "name": "ldap",
+ "versions": [
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "轻型目录访问协议",
+ "shell": "ldap.sh",
+ "check": "ldap.so"
+ },
+ {
+ "name": "mysql_xdevapi",
+ "versions": [
+ "71",
+ "72",
+ "73"
+ ],
+ "type": "通用扩展",
+ "msg": "PHP71-73连接MySQL",
+ "shell": "mysql_xdevapi.sh",
+ "check": "mysql_xdevapi.so"
+ },
+ {
+ "name": "bcmath",
+ "versions": [
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "高精度计算!",
+ "shell": "bcmath.sh",
+ "check": "bcmath.so"
+ },
+ {
+ "name": "pcntl",
+ "versions": [
+ "52",
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "缓存器",
+ "msg": "用于信号控制!",
+ "shell": "pcntl.sh",
+ "check": "pcntl.so"
+ },
+ {
+ "name": "iconv",
+ "versions": [
+ "52",
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "编码转换!",
+ "shell": "iconv.sh",
+ "check": "iconv.so"
+ },
+ {
+ "name": "fileinfo",
+ "versions": [
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "用于FILE!",
+ "shell": "fileinfo.sh",
+ "check": "fileinfo.so"
+ },
+ {
+ "name": "event",
+ "versions": [
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "提供到libevent库的接口",
+ "shell": "event.sh",
+ "check": "event.so"
+ },
+ {
+ "name": "exif",
+ "versions": [
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "用于图像文件格式!",
+ "shell": "exif.sh",
+ "check": "exif.so"
+ },
+ {
+ "name": "igbinary",
+ "versions": [
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "序列化扩展!",
+ "shell": "igbinary.sh",
+ "check": "igbinary.so"
+ },
+ {
+ "name": "gettext",
+ "versions": [
+ "52",
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "国际化与本地化!",
+ "shell": "gettext.sh",
+ "check": "gettext.so"
+ },
+ {
+ "name": "gd",
+ "versions": [
+ "52",
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "通用GD库!",
+ "shell": "gd.sh",
+ "check": "gd.so"
+ },
+ {
+ "name": "curl",
+ "versions": [
+ "52",
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "通用网络",
+ "shell": "curl.sh",
+ "check": "curl.so"
+ },
+ {
+ "name": "intl",
+ "versions": [
+ "52",
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "提供国际化支持",
+ "shell": "intl.sh",
+ "check": "intl.so"
+ },
+ {
+ "name": "readline",
+ "versions": [
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "命令行的支持",
+ "shell": "readline.sh",
+ "check": "readline.so"
+ },
+ {
+ "name": "memcache",
+ "versions": [
+ "52",
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83"
+ ],
+ "type": "缓存器",
+ "msg": "强大的内容缓存器,不支持集群",
+ "shell": "memcache.sh",
+ "check": "memcache.so"
+ },
+ {
+ "name": "memcached",
+ "versions": [
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "缓存器",
+ "msg": "强大的内容缓存器,支持集群",
+ "shell": "memcached.sh",
+ "check": "memcached.so"
+ },
+ {
+ "name": "redis",
+ "versions": [
+ "52",
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "缓存器",
+ "msg": "更强大的内容缓存器,支持集群",
+ "shell": "redis.sh",
+ "check": "redis.so"
+ },
+ {
+ "name": "apc",
+ "versions": [
+ "53",
+ "54"
+ ],
+ "type": "缓存器",
+ "msg": "脚本缓存器",
+ "shell": "apc.sh",
+ "check": "apc.so"
+ },
+ {
+ "name": "apcu",
+ "versions": [
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81"
+ ],
+ "type": "缓存器",
+ "msg": "脚本缓存器",
+ "shell": "apcu.sh",
+ "check": "apcu"
+ },
+ {
+ "name": "imagemagick",
+ "versions": [
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "比GD更强大的图形库",
+ "shell": "imagemagick.sh",
+ "check": "imagick.so"
+ },
+ {
+ "name": "xdebug",
+ "versions": [
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "调试器",
+ "msg": "不多说,不了解的不要安装",
+ "shell": "xdebug.sh",
+ "check": "xdebug.so"
+ },
+ {
+ "name": "xhprof",
+ "versions": [
+ "52",
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "性能分析",
+ "msg": "不多说,不了解的不要安装!",
+ "shell": "xhprof.sh",
+ "check": "xhprof.so"
+ },
+ {
+ "name": "Swoole",
+ "versions": [
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84"
+ ],
+ "type": "通用扩展",
+ "msg": "异步、并行、高性能网络通信引擎",
+ "shell": "swoole.sh",
+ "check": "swoole.so"
+ },
+ {
+ "name": "eAccelerator",
+ "versions": [
+ "52",
+ "53"
+ ],
+ "type": "缓存器",
+ "msg": "内容缓存器",
+ "shell": "eaccelerator.sh",
+ "check": "eaccelerator.so"
+ },
+ {
+ "name": "phalcon",
+ "versions": [
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "框架",
+ "msg": "Phalcon是一个全栈PHP框架。",
+ "shell": "phalcon.sh",
+ "check": "phalcon.so"
+ },
+ {
+ "name": "yaf",
+ "versions": [
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83"
+ ],
+ "type": "框架",
+ "msg": "Yaf是一个C语言编写的PHP框架",
+ "shell": "yaf.sh",
+ "check": "yaf.so"
+ },
+ {
+ "name": "yaml",
+ "versions": [
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "YAML-1.1解析器和发射器",
+ "shell": "yaml.sh",
+ "check": "yaml.so"
+ },
+ {
+ "name": "mongo",
+ "versions": [
+ "53",
+ "54",
+ "55",
+ "56"
+ ],
+ "type": "通用扩展",
+ "msg": "Mongodb数据库连接驱动",
+ "shell": "mongo.sh",
+ "check": "mongo.so"
+ },
+ {
+ "name": "mongodb",
+ "versions": [
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "通用扩展",
+ "msg": "Mongodb数据库连接驱动",
+ "shell": "mongodb.sh",
+ "check": "mongodb.so"
+ },
+ {
+ "name": "yac",
+ "versions": [
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "缓存器",
+ "msg": "高性能无锁共享内存Cache",
+ "shell": "yac.sh",
+ "check": "yac.so"
+ },
+ {
+ "name": "solr",
+ "versions": [
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "大数据",
+ "msg": "SOLR全文搜索服务",
+ "shell": "solr.sh",
+ "check": "solr.so"
+ },
+ {
+ "name": "seaslog",
+ "versions": [
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "日志",
+ "msg": "SeasLog高性能日志记录",
+ "shell": "seaslog.sh",
+ "check": "seaslog.so"
+ },
+ {
+ "name": "zip",
+ "versions": [
+ "52",
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "压缩",
+ "msg": "压缩组件",
+ "shell": "zip.sh",
+ "check": "zip.so"
+ },
+ {
+ "name": "zlib",
+ "versions": [
+ "52",
+ "53",
+ "54",
+ "55",
+ "56",
+ "70",
+ "71",
+ "72",
+ "73",
+ "74",
+ "80",
+ "81",
+ "82",
+ "83",
+ "84",
+ "85"
+ ],
+ "type": "压缩",
+ "msg": "压缩组件",
+ "shell": "zlib.sh",
+ "check": "zlib.so"
+ }
+]
\ No newline at end of file
diff --git a/plugins/phpldapadmin/conf/config.php b/plugins/phpldapadmin/conf/config.php
new file mode 100644
index 000000000..fc868e835
--- /dev/null
+++ b/plugins/phpldapadmin/conf/config.php
@@ -0,0 +1,661 @@
+custom variable to do so.
+ * For example, the default for defining the language in config_default.php
+ *
+ * $this->default->appearance['language'] = array(
+ * 'desc'=>'Language',
+ * 'default'=>'auto');
+ *
+ * to override this, use $config->custom->appearance['language'] = 'en_EN';
+ *
+ * This file is also used to configure your LDAP server connections.
+ *
+ * You must specify at least one LDAP server there. You may add
+ * as many as you like. You can also specify your language, and
+ * many other options.
+ *
+ * NOTE: Commented out values in this file prefixed by //, represent the
+ * defaults that have been defined in config_default.php.
+ * Commented out values prefixed by #, dont reflect their default value, you can
+ * check config_default.php if you want to see what the default is.
+ *
+ * DONT change config_default.php, you changes will be lost by the next release
+ * of PLA. Instead change this file - as it will NOT be replaced by a new
+ * version of phpLDAPadmin.
+ */
+
+/*********************************************
+ * Useful important configuration overrides *
+ *********************************************/
+
+/* If you are asked to put PLA in debug mode, this is how you do it: */
+# $config->custom->debug['level'] = 255;
+# $config->custom->debug['syslog'] = true;
+# $config->custom->debug['file'] = '/tmp/pla_debug.log';
+
+/* phpLDAPadmin can encrypt the content of sensitive cookies if you set this
+ to a big random string. */
+// $config->custom->session['blowfish'] = null;
+
+/* If your auth_type is http, you can override your HTTP Authentication Realm. */
+// $config->custom->session['http_realm'] = sprintf('%s %s',app_name(),'login');
+
+/* The language setting. If you set this to 'auto', phpLDAPadmin will attempt
+ to determine your language automatically.
+ If PLA doesnt show (all) strings in your language, then you can do some
+ translation at http://translations.launchpad.net/phpldapadmin and download
+ the translation files, replacing those provided with PLA.
+ (We'll pick up the translations before making the next release too!) */
+// $config->custom->appearance['language'] = 'auto';
+
+/* The temporary storage directory where we will put jpegPhoto data
+ This directory must be readable and writable by your web server. */
+// $config->custom->jpeg['tmpdir'] = '/tmp'; // Example for Unix systems
+# $config->custom->jpeg['tmpdir'] = 'c:\\temp'; // Example for Windows systems
+
+/* Set this to (bool)true if you do NOT want a random salt used when
+ calling crypt(). Instead, use the first two letters of the user's
+ password. This is insecure but unfortunately needed for some older
+ environments. */
+# $config->custom->password['no_random_crypt_salt'] = true;
+
+/* If you want to restrict password available types (encryption algorithms)
+ Should be subset of:
+ array(
+ ''=>'clear',
+ 'bcrypt'=>'bcrypt',
+ 'blowfish'=>'blowfish',
+ 'crypt'=>'crypt',
+ 'ext_des'=>'ext_des',
+ 'md5'=>'md5',
+ 'k5key'=>'k5key',
+ 'md5crypt'=>'md5crypt',
+ 'sha'=>'sha',
+ 'smd5'=>'smd5',
+ 'ssha'=>'ssha',
+ 'sha256'=>'sha256',
+ 'ssha256'=>'ssha256',
+ 'sha384'=>'sha384',
+ 'ssha384'=>'ssha384',
+ 'sha512'=>'sha512',
+ 'ssha512'=>'ssha512',
+ 'sha256crypt'=>'sha256crypt',
+ 'sha512crypt'=>'sha512crypt',
+ 'argon2i'=>'argon2i',
+ 'argon2id'=>'argon2id',
+ )*/
+# $config->custom->password['available_types'] = array(''=>'clear','md5'=>'md5');
+
+/* PHP script timeout control. If php runs longer than this many seconds then
+ PHP will stop with an Maximum Execution time error. Increase this value from
+ the default if queries to your LDAP server are slow. The default is either
+ 30 seconds or the setting of max_exection_time if this is null. */
+// $config->custom->session['timelimit'] = 30;
+
+/* Our local timezone
+ This is to make sure that when we ask the system for the current time, we
+ get the right local time. If this is not set, all time() calculations will
+ assume UTC if you have not set PHP date.timezone. */
+// $config->custom->appearance['timezone'] = null;
+# $config->custom->appearance['timezone'] = 'Australia/Melbourne';
+
+/*********************************************
+ * Commands *
+ *********************************************/
+
+/* Command availability ; if you don't authorize a command the command
+ links will not be shown and the command action will not be permitted.
+ For better security, set also ACL in your ldap directory. */
+/*
+$config->custom->commands['cmd'] = array(
+ 'entry_internal_attributes_show' => true,
+ 'entry_refresh' => true,
+ 'oslinks' => true,
+ 'switch_template' => true
+);
+
+$config->custom->commands['script'] = array(
+ 'add_attr_form' => true,
+ 'add_oclass_form' => true,
+ 'add_value_form' => true,
+ 'collapse' => true,
+ 'compare' => true,
+ 'compare_form' => true,
+ 'copy' => true,
+ 'copy_form' => true,
+ 'create' => true,
+ 'create_confirm' => true,
+ 'delete' => true,
+ 'delete_attr' => true,
+ 'delete_form' => true,
+ 'draw_tree_node' => true,
+ 'expand' => true,
+ 'export' => true,
+ 'export_form' => true,
+ 'import' => true,
+ 'import_form' => true,
+ 'login' => true,
+ 'logout' => true,
+ 'login_form' => true,
+ 'mass_delete' => true,
+ 'mass_edit' => true,
+ 'mass_update' => true,
+ 'modify_member_form' => true,
+ 'monitor' => true,
+ 'purge_cache' => true,
+ 'query_engine' => true,
+ 'rename' => true,
+ 'rename_form' => true,
+ 'rdelete' => true,
+ 'refresh' => true,
+ 'schema' => true,
+ 'server_info' => true,
+ 'show_cache' => true,
+ 'template_engine' => true,
+ 'update_confirm' => true,
+ 'update' => true
+);
+*/
+
+/*********************************************
+ * Appearance *
+ *********************************************/
+
+/* If you want to choose the appearance of the tree, specify a class name which
+ inherits from the Tree class. */
+// $config->custom->appearance['tree'] = 'AJAXTree';
+# $config->custom->appearance['tree'] = 'HTMLTree';
+
+/* Just show your custom templates. */
+// $config->custom->appearance['custom_templates_only'] = false;
+
+/* Disable the default template. */
+// $config->custom->appearance['disable_default_template'] = false;
+
+/* Hide the warnings for invalid objectClasses/attributes in templates. */
+// $config->custom->appearance['hide_template_warning'] = false;
+
+/* Set to true if you would like to hide header and footer parts. */
+// $config->custom->appearance['minimalMode'] = false;
+
+/* Configure what objects are shown in left hand tree */
+// $config->custom->appearance['tree_filter'] = '(objectclass=*)';
+
+/* The height and width of the tree. If these values are not set, then
+ no tree scroll bars are provided. */
+// $config->custom->appearance['tree_height'] = null;
+# $config->custom->appearance['tree_height'] = 600;
+// $config->custom->appearance['tree_width'] = null;
+# $config->custom->appearance['tree_width'] = 250;
+
+/* Number of tree command icons to show, 0 = show all icons on 1 row. */
+// $config->custom->appearance['tree_icons'] = 0;
+# $config->custom->appearance['tree_icons'] = 4;
+
+/* Confirm create and update operations, allowing you to review the changes
+ and optionally skip attributes during the create/update operation. */
+// $config->custom->confirm['create'] = true;
+// $config->custom->confirm['update'] = true;
+
+/* Confirm copy operations, and treat them like create operations. This allows
+ you to edit the attributes (thus changing any that might conflict with
+ uniqueness) before creating the new entry. */
+// $config->custom->confirm['copy'] = true;
+
+/*********************************************
+ * User-friendly attribute translation *
+ *********************************************/
+
+/* Use this array to map attribute names to user friendly names. For example, if
+ you don't want to see "facsimileTelephoneNumber" but rather "Fax". */
+// $config->custom->appearance['friendly_attrs'] = array();
+$config->custom->appearance['friendly_attrs'] = array(
+ 'facsimileTelephoneNumber' => 'Fax',
+ 'gid' => 'Group',
+ 'mail' => 'Email',
+ 'telephoneNumber' => 'Telephone',
+ 'uid' => 'User Name',
+ 'userPassword' => 'Password'
+);
+
+/*********************************************
+ * Hidden attributes *
+ *********************************************/
+
+/* You may want to hide certain attributes from being edited. If you want to
+ hide attributes from the user, you should use your LDAP servers ACLs.
+ NOTE: The user must be able to read the hide_attrs_exempt entry to be
+ excluded. */
+// $config->custom->appearance['hide_attrs'] = array();
+# $config->custom->appearance['hide_attrs'] = array('objectClass');
+
+/* Members of this list will be exempt from the hidden attributes. */
+// $config->custom->appearance['hide_attrs_exempt'] = null;
+# $config->custom->appearance['hide_attrs_exempt'] = 'cn=PLA UnHide,ou=Groups,c=AU';
+
+/*********************************************
+ * Read-only attributes *
+ *********************************************/
+
+/* You may want to phpLDAPadmin to display certain attributes as read only,
+ meaning that users will not be presented a form for modifying those
+ attributes, and they will not be allowed to be modified on the "back-end"
+ either. You may configure this list here:
+ NOTE: The user must be able to read the readonly_attrs_exempt entry to be
+ excluded. */
+// $config->custom->appearance['readonly_attrs'] = array();
+
+/* Members of this list will be exempt from the readonly attributes. */
+// $config->custom->appearance['readonly_attrs_exempt'] = null;
+# $config->custom->appearance['readonly_attrs_exempt'] = 'cn=PLA ReadWrite,ou=Groups,c=AU';
+
+/*********************************************
+ * Group attributes *
+ *********************************************/
+
+/* Add "modify group members" link to the attribute. */
+// $config->custom->modify_member['groupattr'] = array('member','uniqueMember','memberUid','sudoUser');
+
+/* Configure filter for member search. This only applies to "modify group members" feature */
+// $config->custom->modify_member['filter'] = '(objectclass=Person)';
+
+/* Attribute that is added to the group member attribute. */
+// $config->custom->modify_member['attr'] = 'dn';
+
+/* For Posix attributes */
+// $config->custom->modify_member['posixattr'] = 'uid';
+// $config->custom->modify_member['posixfilter'] = '(uid=*)';
+// $config->custom->modify_member['posixgroupattr'] = 'memberUid';
+
+/*********************************************
+ * Support for attrs display order *
+ *********************************************/
+
+/* Use this array if you want to have your attributes displayed in a specific
+ order. You can use default attribute names or their fridenly names.
+ For example, "sn" will be displayed right after "givenName". All the other
+ attributes that are not specified in this array will be displayed after in
+ alphabetical order. */
+// $config->custom->appearance['attr_display_order'] = array();
+# $config->custom->appearance['attr_display_order'] = array(
+# 'givenName',
+# 'sn',
+# 'cn',
+# 'displayName',
+# 'uid',
+# 'uidNumber',
+# 'gidNumber',
+# 'homeDirectory',
+# 'mail',
+# 'userPassword'
+# );
+
+/*********************************************
+ * Define your LDAP servers in this section *
+ *********************************************/
+
+$servers = new Datastore();
+
+/* $servers->NewServer('ldap_pla') must be called before each new LDAP server
+ declaration. */
+$servers->newServer('ldap_pla');
+
+/* A convenient name that will appear in the tree viewer and throughout
+ phpLDAPadmin to identify this LDAP server to users. */
+$servers->setValue('server','name','My LDAP Server');
+
+/* Examples:
+ 'ldap.example.com',
+ 'ldaps://ldap.example.com/',
+ 'ldapi://%2fusr%local%2fvar%2frun%2fldapi'
+ (Unix socket at /usr/local/var/run/ldap) */
+// $servers->setValue('server','host','127.0.0.1');
+
+/* The port your LDAP server listens on (no quotes). 389 is standard. */
+// $servers->setValue('server','port',389);
+
+/* Array of base DNs of your LDAP server. Leave this blank to have phpLDAPadmin
+ auto-detect it for you. */
+// $servers->setValue('server','base',array(''));
+
+/* Five options for auth_type:
+ 1. 'cookie': you will login via a web form, and a client-side cookie will
+ store your login dn and password.
+ 2. 'session': same as cookie but your login dn and password are stored on the
+ web server in a persistent session variable.
+ 3. 'http': same as session but your login dn and password are retrieved via
+ HTTP authentication.
+ 4. 'config': specify your login dn and password here in this config file. No
+ login will be required to use phpLDAPadmin for this server.
+ 5. 'sasl': login will be taken from the webserver's kerberos authentication.
+ Currently only GSSAPI has been tested (using mod_auth_kerb).
+ 6. 'sasl_external': login will be taken from SASL external mechanism.
+
+ Choose wisely to protect your authentication information appropriately for
+ your situation. If you choose 'cookie', your cookie contents will be
+ encrypted using blowfish and the secret your specify above as
+ session['blowfish']. */
+// $servers->setValue('login','auth_type','session');
+
+/* The DN of the user for phpLDAPadmin to bind with. For anonymous binds or
+ 'cookie','session' or 'sasl' auth_types, LEAVE THE LOGIN_DN AND LOGIN_PASS
+ BLANK. If you specify a login_attr in conjunction with a cookie or session
+ auth_type, then you can also specify the bind_id/bind_pass here for searching
+ the directory for users (ie, if your LDAP server does not allow anonymous
+ binds. */
+// $servers->setValue('login','bind_id','');
+# $servers->setValue('login','bind_id','cn=Manager,dc=example,dc=com');
+
+/* Your LDAP password. If you specified an empty bind_id above, this MUST also
+ be blank. */
+// $servers->setValue('login','bind_pass','');
+# $servers->setValue('login','bind_pass','secret');
+
+/* Use TLS (Transport Layer Security) to connect to the LDAP server. */
+// $servers->setValue('server','tls',false);
+
+/* TLS Certificate Authority file (overrides ldap.conf, PHP 7.1+) */
+// $servers->setValue('server','tls_cacert',null);
+# $servers->setValue('server','tls_cacert','/etc/openldap/certs/ca.crt');
+
+/* TLS Certificate Authority hashed directory (overrides ldap.conf, PHP 7.1+) */
+// $servers->setValue('server','tls_cacertdir',null);
+# $servers->setValue('server','tls_cacertdir','/etc/openldap/certs');
+
+/* TLS Client Certificate file (PHP 7.1+) */
+// $servers->setValue('server','tls_cert',null);
+# $servers->setValue('server','tls_cert','/etc/pki/tls/certs/ldap_user.crt');
+
+/* TLS Client Certificate Key file (PHP 7.1+) */
+// $servers->setValue('server','tls_key',null);
+# $servers->setValue('server','tls_key','/etc/pki/tls/private/ldap_user.key');
+
+/************************************
+ * SASL Authentication *
+ ************************************/
+
+/* Enable SASL authentication LDAP SASL authentication requires PHP 5.x
+ configured with --with-ldap-sasl=DIR. If this option is disabled (ie, set to
+ false), then all other sasl options are ignored. */
+# $servers->setValue('login','auth_type','sasl');
+
+/* SASL GSSAPI auth mechanism (requires auth_type of sasl) */
+// $servers->setValue('sasl','mech','GSSAPI');
+
+/* SASL PLAIN support... this mech converts simple binds to SASL
+ PLAIN binds using any auth_type (or other bind_id/pass) as credentials.
+ NOTE: auth_type must be simple auth compatible (ie not sasl) */
+# $servers->setValue('sasl','mech','PLAIN');
+
+/* SASL EXTERNAL support... really a different auth_type */
+# $servers->setValue('login','auth_type','sasl_external');
+
+/* SASL authentication realm name */
+// $servers->setValue('sasl','realm','');
+# $servers->setValue('sasl','realm','EXAMPLE.COM');
+
+/* SASL authorization ID name
+ If this option is undefined, authorization id will be computed from bind DN,
+ using authz_id_regex and authz_id_replacement. */
+// $servers->setValue('sasl','authz_id', null);
+
+/* SASL authorization id regex and replacement
+ When authz_id property is not set (default), phpLDAPAdmin will try to
+ figure out authorization id by itself from bind distinguished name (DN).
+
+ This procedure is done by calling preg_replace() php function in the
+ following way:
+
+ $authz_id = preg_replace($sasl_authz_id_regex,$sasl_authz_id_replacement,
+ $bind_dn);
+
+ For info about pcre regexes, see:
+ - pcre(3), perlre(3)
+ - http://www.php.net/preg_replace */
+// $servers->setValue('sasl','authz_id_regex',null);
+// $servers->setValue('sasl','authz_id_replacement',null);
+# $servers->setValue('sasl','authz_id_regex','/^uid=([^,]+)(.+)/i');
+# $servers->setValue('sasl','authz_id_replacement','$1');
+
+/* SASL auth security props.
+ See http://beepcore-tcl.sourceforge.net/tclsasl.html#anchor5 for explanation. */
+// $servers->setValue('sasl','props',null);
+
+/* Default password hashing algorithm. One of md5, ssha, sha, md5crpyt, smd5,
+ blowfish, crypt or leave blank for now default algorithm. */
+// $servers->setValue('appearance','pla_password_hash','md5');
+
+/* If you specified 'cookie' or 'session' as the auth_type above, you can
+ optionally specify here an attribute to use when logging in. If you enter
+ 'uid' and login as 'dsmith', phpLDAPadmin will search for (uid=dsmith)
+ and log in as that user.
+ Leave blank or specify 'dn' to use full DN for logging in. Note also that if
+ your LDAP server requires you to login to perform searches, you can enter the
+ DN to use when searching in 'bind_id' and 'bind_pass' above. */
+// $servers->setValue('login','attr','dn');
+
+/* Base DNs to used for logins. If this value is not set, then the LDAP server
+ Base DNs are used. */
+// $servers->setValue('login','base',array());
+
+/* If 'login,attr' is used above such that phpLDAPadmin will search for your DN
+ at login, you may restrict the search to a specific objectClasses. EG, set this
+ to array('posixAccount') or array('inetOrgPerson',..), depending upon your
+ setup. */
+// $servers->setValue('login','class',array());
+
+/* If login_attr was set to 'dn', it is possible to specify a template string to
+ build the DN from. Use '%s' where user input should be inserted. A user may
+ still enter the complete DN. In this case the template will not be used. */
+// $servers->setValue('login','bind_dn_template',null);
+# $servers->setValue('login','bind_dn_template','cn=%s,ou=people,dc=example,dc=com');
+
+/* If you specified something different from 'dn', for example 'uid', as the
+ login_attr above, you can optionally specify here to fall back to
+ authentication with dn.
+ This is useful, when users should be able to log in with their uid, but
+ the ldap administrator wants to log in with his root-dn, that does not
+ necessarily have the uid attribute.
+ When using this feature, login_class is ignored. */
+// $servers->setValue('login','fallback_dn',false);
+
+/* Specify true If you want phpLDAPadmin to not display or permit any
+ modification to the LDAP server. */
+// $servers->setValue('server','read_only',false);
+
+/* Specify false if you do not want phpLDAPadmin to draw the 'Create new' links
+ in the tree viewer. */
+// $servers->setValue('appearance','show_create',true);
+
+/* Set to true if you would like to initially open the first level of each tree. */
+// $servers->setValue('appearance','open_tree',false);
+
+/* Set to true to display authorization ID in place of login dn (PHP 7.2+) */
+// $servers->setValue('appearance','show_authz',false);
+
+/* This feature allows phpLDAPadmin to automatically determine the next
+ available uidNumber for a new entry. */
+// $servers->setValue('auto_number','enable',true);
+
+/* The mechanism to use when finding the next available uidNumber. Two possible
+ values: 'uidpool' or 'search'.
+ The 'uidpool' mechanism uses an existing uidPool entry in your LDAP server to
+ blindly lookup the next available uidNumber. The 'search' mechanism searches
+ for entries with a uidNumber value and finds the first available uidNumber
+ (slower). */
+// $servers->setValue('auto_number','mechanism','search');
+
+/* The DN of the search base when the 'search' mechanism is used above. */
+# $servers->setValue('auto_number','search_base','ou=People,dc=example,dc=com');
+
+/* The minimum number to use when searching for the next available number
+ (only when 'search' is used for auto_number. */
+// $servers->setValue('auto_number','min',array('uidNumber'=>1000,'gidNumber'=>500));
+
+/* If you set this, then phpldapadmin will bind to LDAP with this user ID when
+ searching for the uidnumber. The idea is, this user id would have full
+ (readonly) access to uidnumber in your ldap directory (the logged in user
+ may not), so that you can be guaranteed to get a unique uidnumber for your
+ directory. */
+// $servers->setValue('auto_number','dn',null);
+
+/* The password for the dn above. */
+// $servers->setValue('auto_number','pass',null);
+
+/* Enable anonymous bind login. */
+// $servers->setValue('login','anon_bind',true);
+
+/* Use customized page with prefix when available. */
+# $servers->setValue('custom','pages_prefix','custom_');
+
+/* If you set this, then only these DNs are allowed to log in. This array can
+ contain individual users, groups or ldap search filter(s). Keep in mind that
+ the user has not authenticated yet, so this will be an anonymous search to
+ the LDAP server, so make your ACLs allow these searches to return results! */
+# $servers->setValue('login','allowed_dns',array(
+# 'uid=stran,ou=People,dc=example,dc=com',
+# '(&(gidNumber=811)(objectClass=groupOfNames))',
+# '(|(uidNumber=200)(uidNumber=201))',
+# 'cn=callcenter,ou=Group,dc=example,dc=com'));
+
+/* Set this if you dont want this LDAP server to show in the tree */
+// $servers->setValue('server','visible',true);
+
+/* Set this if you want to hide the base DNs that dont exist instead of
+ displaying the message "The base entry doesnt exist, create it?"
+// $servers->setValue('server','hide_noaccess_base',false);
+# $servers->setValue('server','hide_noaccess_base',true);
+
+/* This is the time out value in minutes for the server. After as many minutes
+ of inactivity you will be automatically logged out. If not set, the default
+ value will be ( session_cache_expire()-1 ) */
+# $servers->setValue('login','timeout',30);
+
+/* Set this if you want phpldapadmin to perform rename operation on entry which
+ has children. Certain servers are known to allow it, certain are not. */
+// $servers->setValue('server','branch_rename',false);
+
+/* If you set this, then phpldapadmin will show these attributes as
+ internal attributes, even if they are not defined in your schema. */
+// $servers->setValue('server','custom_sys_attrs',array(''));
+# $servers->setValue('server','custom_sys_attrs',array('passwordExpirationTime','passwordAllowChangeTime'));
+
+/* If you set this, then phpldapadmin will show these attributes on
+ objects, even if they are not defined in your schema. */
+// $servers->setValue('server','custom_attrs',array(''));
+# $servers->setValue('server','custom_attrs',array('nsRoleDN','nsRole','nsAccountLock'));
+
+/* These attributes will be forced to MAY attributes and become option in the
+ templates. If they are not defined in the templates, then they wont appear
+ as per normal template processing. You may want to do this because your LDAP
+ server may automatically calculate a default value.
+ In Fedora Directory Server using the DNA Plugin one could ignore uidNumber,
+ gidNumber and sambaSID. */
+// $servers->setValue('server','force_may',array(''));
+# $servers->setValue('server','force_may',array('uidNumber','gidNumber','sambaSID'));
+
+/*********************************************
+ * Unique attributes *
+ *********************************************/
+
+/* You may want phpLDAPadmin to enforce some attributes to have unique values
+ (ie: not belong to other entries in your tree. This (together with
+ 'unique','dn' and 'unique','pass' option will not let updates to
+ occur with other attributes have the same value. */
+# $servers->setValue('unique','attrs',array('mail','uid','uidNumber'));
+
+/* If you set this, then phpldapadmin will bind to LDAP with this user ID when
+ searching for attribute uniqueness. The idea is, this user id would have full
+ (readonly) access to your ldap directory (the logged in user may not), so
+ that you can be guaranteed to get a unique uidnumber for your directory. */
+// $servers->setValue('unique','dn',null);
+
+/* The password for the dn above. */
+// $servers->setValue('unique','pass',null);
+
+/**************************************************************************
+ * If you want to configure additional LDAP servers, do so below. *
+ * Remove the commented lines and use this section as a template for all *
+ * your other LDAP servers. *
+ **************************************************************************/
+
+/*
+$servers->newServer('ldap_pla');
+$servers->setValue('server','name','LDAP Server');
+$servers->setValue('server','host','127.0.0.1');
+$servers->setValue('server','port',389);
+$servers->setValue('server','base',array(''));
+$servers->setValue('login','auth_type','cookie');
+$servers->setValue('login','bind_id','');
+$servers->setValue('login','bind_pass','');
+$servers->setValue('server','tls',false);
+
+# SASL auth
+$servers->setValue('login','auth_type','sasl');
+$servers->setValue('sasl','mech','GSSAPI');
+$servers->setValue('sasl','realm','EXAMPLE.COM');
+$servers->setValue('sasl','authz_id',null);
+$servers->setValue('sasl','authz_id_regex','/^uid=([^,]+)(.+)/i');
+$servers->setValue('sasl','authz_id_replacement','$1');
+$servers->setValue('sasl','props',null);
+
+$servers->setValue('appearance','pla_password_hash','md5');
+$servers->setValue('login','attr','dn');
+$servers->setValue('login','fallback_dn',false);
+$servers->setValue('login','class',null);
+$servers->setValue('server','read_only',false);
+$servers->setValue('appearance','show_create',true);
+
+$servers->setValue('auto_number','enable',true);
+$servers->setValue('auto_number','mechanism','search');
+$servers->setValue('auto_number','search_base',null);
+$servers->setValue('auto_number','min',array('uidNumber'=>1000,'gidNumber'=>500));
+$servers->setValue('auto_number','dn',null);
+$servers->setValue('auto_number','pass',null);
+
+$servers->setValue('login','anon_bind',true);
+$servers->setValue('custom','pages_prefix','custom_');
+$servers->setValue('unique','attrs',array('mail','uid','uidNumber'));
+$servers->setValue('unique','dn',null);
+$servers->setValue('unique','pass',null);
+
+$servers->setValue('server','visible',true);
+$servers->setValue('login','timeout',30);
+$servers->setValue('server','branch_rename',false);
+$servers->setValue('server','custom_sys_attrs',array('passwordExpirationTime','passwordAllowChangeTime'));
+$servers->setValue('server','custom_attrs',array('nsRoleDN','nsRole','nsAccountLock'));
+$servers->setValue('server','force_may',array('uidNumber','gidNumber','sambaSID'));
+*/
+
+$servers->setValue('server','host','127.0.0.1');
+$servers->setValue('server','port',389);
+$servers->setValue('login','auth_type','session');
+$servers->setValue('server','base',array('dc=local,dc=com'));
+$servers->setValue('login','bind_id','cn=admin,dc=local,dc=com');
+
+/***********************************************************************************
+ * If you want to configure Google reCAPTCHA on autentication form, do so below. *
+ * Remove the commented lines and use this section as a template for all *
+ * reCAPTCHA v2 Generate on https://www.google.com/recaptcha/ *
+ * *
+ * IMPORTANT: Select reCAPTCHA v2 on Type of reCAPTCHA *
+ ***********************************************************************************/
+
+
+$config->custom->session['reCAPTCHA-enable'] = false;
+$config->custom->session['reCAPTCHA-key-site'] = '';
+$config->custom->session['reCAPTCHA-key-server'] = '';
+
+?>
diff --git a/plugins/phpldapadmin/conf/phpldapadmin.conf b/plugins/phpldapadmin/conf/phpldapadmin.conf
new file mode 100755
index 000000000..046fb1967
--- /dev/null
+++ b/plugins/phpldapadmin/conf/phpldapadmin.conf
@@ -0,0 +1,38 @@
+server
+{
+ listen 888;
+ server_name 127.0.0.1;
+ index index.html index.htm index.php;
+ root {$SERVER_PATH}/phpldapadmin;
+
+ #error_page 404 /404.html;
+ include {$PHP_CONF_PATH}/enable-php-{$PHP_VER}.conf;
+
+ #AUTH_START
+ auth_basic "Authorization";
+ auth_basic_user_file {$SERVER_PATH}/phpldapadmin/pma.pass;
+ #AUTH_END
+
+ location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
+ {
+ expires 30d;
+ }
+
+ location ~ .*\.(js|css)?$
+ {
+ expires 12h;
+ }
+
+ location ~ /.*\.(log|pass|json|pl)$ {
+ deny all;
+ }
+
+
+ location ~ /\.
+ {
+ deny all;
+ }
+
+ access_log {$SERVER_PATH}/phpldapadmin/access.log;
+ error_log {$SERVER_PATH}/phpldapadmin/error.log;
+}
\ No newline at end of file
diff --git a/plugins/phpldapadmin/ico.png b/plugins/phpldapadmin/ico.png
new file mode 100644
index 000000000..8d3130d8b
Binary files /dev/null and b/plugins/phpldapadmin/ico.png differ
diff --git a/plugins/phpldapadmin/index.html b/plugins/phpldapadmin/index.html
new file mode 100755
index 000000000..01cf3286e
--- /dev/null
+++ b/plugins/phpldapadmin/index.html
@@ -0,0 +1,24 @@
+
+
\ No newline at end of file
diff --git a/plugins/phpldapadmin/index.py b/plugins/phpldapadmin/index.py
new file mode 100755
index 000000000..802971063
--- /dev/null
+++ b/plugins/phpldapadmin/index.py
@@ -0,0 +1,473 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+
+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
+import thisdb
+from utils.site import sites as MwSites
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'phpldapadmin'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+
+ 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 getConf():
+ return mw.getServerDir() + '/web_conf/nginx/vhost/phpldapadmin.conf'
+
+
+def getConfInc():
+ return getServerDir() + "/" + getCfg()['path'] + '/config/config.php'
+
+
+def getPort():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'listen\s*(.*);'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+
+def getHomePage():
+ try:
+ port = getPort()
+ ip = '127.0.0.1'
+ if not mw.isAppleSystem():
+ ip = mw.getLocalIp()
+
+ cfg = getCfg()
+ auth = cfg['username']+':'+cfg['password']
+ rand_path = cfg['path']
+ url = 'http://' + auth + '@' + ip + ':' + port + '/' + rand_path + '/index.php'
+ return mw.returnJson(True, 'OK', url)
+ except Exception as e:
+ return mw.returnJson(False, '插件未启动!')
+
+
+def getPhpVer(expect=74):
+ php_vers = MwSites.instance().getPhpVersion()
+ v = php_vers['data']
+ is_find = False
+ for i in range(len(v)):
+ t = str(v[i]['version'])
+ if (t == expect):
+ is_find = True
+ return str(t)
+ expect_str = str(expect)
+ new_ex = expect_str[0:1]+"."+expect_str[1:2]
+ if t.find(new_ex) > -1:
+ is_find = True
+ return str(t)
+ if not is_find:
+ if len(v) > 1:
+ return v[1]['version']
+ return v[0]['version']
+ return str(expect)
+
+
+def getCachePhpVer():
+ cacheFile = getServerDir() + '/php.pl'
+ v = ''
+ if os.path.exists(cacheFile):
+ v = mw.readFile(cacheFile)
+ else:
+ v = getPhpVer()
+ mw.writeFile(cacheFile, v)
+ return v
+
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ php_ver = getCachePhpVer()
+ tmp = mw.execShell('cat /dev/urandom | head -n 32 | md5sum | head -c 16')
+ blowfish_secret = tmp[0].strip()
+ # print php_ver
+ php_conf_dir = mw.getServerDir() + '/web_conf/php/conf'
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$PHP_CONF_PATH}', php_conf_dir)
+ content = content.replace('{$PHP_VER}', php_ver)
+ content = content.replace('{$BLOWFISH_SECRET}', blowfish_secret)
+
+ cfg = getCfg()
+ content = content.replace('{$PMA_PATH}', cfg['path'])
+
+ port = cfg["port"]
+ rep = r'listen\s*(.*);'
+ content = re.sub(rep, "listen " + port + ';', content)
+ return content
+
+
+def initCfg():
+ cfg = getServerDir() + "/cfg.json"
+ if not os.path.exists(cfg):
+ data = {}
+ data['port'] = '988'
+ data['path'] = ''
+ data['username'] = 'admin'
+ data['password'] = 'admin'
+ mw.writeFile(cfg, json.dumps(data))
+
+
+def setCfg(key, val):
+ cfg = getServerDir() + "/cfg.json"
+ data = mw.readFile(cfg)
+ data = json.loads(data)
+ data[key] = val
+ mw.writeFile(cfg, json.dumps(data))
+
+
+def getCfg():
+ cfg = getServerDir() + "/cfg.json"
+ data = mw.readFile(cfg)
+ data = json.loads(data)
+ return data
+
+
+def returnCfg():
+ cfg = getServerDir() + "/cfg.json"
+ data = mw.readFile(cfg)
+ return data
+
+
+def status():
+ conf = getConf()
+ conf_inc = getServerDir() + "/" + getCfg()["path"] + '/config/config.php'
+ # 两个文件都在,才算启动成功
+ if os.path.exists(conf) and os.path.exists(conf_inc):
+ return 'start'
+ return 'stop'
+
+
+def __release_port(port):
+ from collections import namedtuple
+ try:
+ from utils.firewall import Firewall as MwFirewall
+ MwFirewall.instance().addAcceptPort(port, 'phpLDAPadmin默认端口', 'port')
+ return port
+ except Exception as e:
+ return "Release failed {}".format(e)
+
+
+def __delete_port(port):
+ from collections import namedtuple
+ try:
+ from utils.firewall import Firewall as MwFirewall
+ MwFirewall.instance().delAcceptPortCmd(port, 'tcp')
+ return port
+ except Exception as e:
+ return "Release failed {}".format(e)
+
+
+def openPort():
+ conf = getCfg()
+ port = conf['port']
+ for i in [port]:
+ __release_port(i)
+ return True
+
+
+def delPort():
+ conf = getCfg()
+ port = conf['port']
+ for i in [port]:
+ __delete_port(i)
+ return True
+
+
+def start():
+ initCfg()
+ openPort()
+
+ pma_dir = getServerDir() + "/phpldapadmin"
+ if os.path.exists(pma_dir):
+ rand_str = mw.getRandomString(6)
+ rand_str = rand_str.lower()
+ pma_dir_dst = pma_dir + "_" + rand_str
+ mw.execShell("mv " + pma_dir + " " + pma_dir_dst)
+ setCfg('path', 'phpldapadmin_' + rand_str)
+
+ file_tpl = getPluginDir() + '/conf/phpldapadmin.conf'
+ file_run = getConf()
+ if not os.path.exists(file_run):
+ centent = mw.readFile(file_tpl)
+ centent = contentReplace(centent)
+ mw.writeFile(file_run, centent)
+
+ pma_path = getServerDir() + '/pma.pass'
+ if not os.path.exists(pma_path):
+ username = mw.getRandomString(8)
+ password = mw.getRandomString(10)
+ pass_cmd = username + ':' + mw.hasPwd(password)
+ setCfg('username', username)
+ setCfg('password', password)
+ mw.writeFile(pma_path, pass_cmd)
+
+ tmp = getServerDir() + "/" + getCfg()["path"] + '/tmp'
+ if not os.path.exists(tmp):
+ os.mkdir(tmp)
+ mw.execShell("chown -R www:www " + tmp)
+
+ conf_run = getServerDir() + "/" + getCfg()["path"] + '/config/config.php'
+ if not os.path.exists(conf_run):
+ conf_tpl = getPluginDir() + '/conf/config.php'
+ centent = mw.readFile(conf_tpl)
+ centent = contentReplace(centent)
+ mw.writeFile(conf_run, centent)
+
+ log_a = accessLog()
+ log_e = errorLog()
+
+ for i in [log_a, log_e]:
+ if os.path.exists(i):
+ cmd = "echo '' > " + i
+ mw.execShell(cmd)
+
+ mw.restartWeb()
+ return 'ok'
+
+
+def stop():
+ conf = getConf()
+ if os.path.exists(conf):
+ os.remove(conf)
+ delPort()
+ mw.restartWeb()
+ return 'ok'
+
+
+def restart():
+ return start()
+
+
+def reload():
+ file_tpl = getPluginDir() + '/conf/phpldapadmin.conf'
+ file_run = getConf()
+ if os.path.exists(file_run):
+ centent = mw.readFile(file_tpl)
+ centent = contentReplace(centent)
+ mw.writeFile(file_run, centent)
+ return start()
+
+
+def setPhpVer():
+ args = getArgs()
+
+ if not 'phpver' in args:
+ return 'phpver missing'
+
+ cacheFile = getServerDir() + '/php.pl'
+ mw.writeFile(cacheFile, args['phpver'])
+
+ file_tpl = getPluginDir() + '/conf/phpldapadmin.conf'
+ file_run = getConf()
+
+ content = mw.readFile(file_tpl)
+ content = contentReplace(content)
+ mw.writeFile(file_run, content)
+
+ mw.restartWeb()
+ return 'ok'
+
+
+def getSetPhpVer():
+ cacheFile = getServerDir() + '/php.pl'
+ if os.path.exists(cacheFile):
+ return mw.readFile(cacheFile).strip()
+ return ''
+
+
+def getPmaOption():
+ data = getCfg()
+ return mw.returnJson(True, 'ok', data)
+
+
+def getPmaPort():
+ try:
+ port = getPort()
+ return mw.returnJson(True, 'OK', port)
+ except Exception as e:
+ # print(e)
+ return mw.returnJson(False, '插件未启动!')
+
+
+def setPmaPort():
+ args = getArgs()
+ data = checkArgs(args, ['port'])
+ if not data[0]:
+ return data[1]
+
+ port = args['port']
+ if port == '80':
+ return mw.returnJson(False, '80端不能使用!')
+
+ file = getConf()
+ if not os.path.exists(file):
+ return mw.returnJson(False, '插件未启动!')
+ content = mw.readFile(file)
+ rep = r'listen\s*(.*);'
+ content = re.sub(rep, "listen " + port + ';', content)
+ mw.writeFile(file, content)
+
+ setCfg("port", port)
+ mw.restartWeb()
+ return mw.returnJson(True, '修改成功!')
+
+
+def setPmaUsername():
+ args = getArgs()
+ data = checkArgs(args, ['username'])
+ if not data[0]:
+ return data[1]
+
+ username = args['username']
+ setCfg('username', username)
+
+ cfg = getCfg()
+ pma_path = getServerDir() + '/pma.pass'
+ username = mw.getRandomString(10)
+ pass_cmd = cfg['username'] + ':' + mw.hasPwd(cfg['password'])
+ mw.writeFile(pma_path, pass_cmd)
+
+ mw.restartWeb()
+ return mw.returnJson(True, '修改成功!')
+
+
+def setPmaPassword():
+ args = getArgs()
+ data = checkArgs(args, ['password'])
+ if not data[0]:
+ return data[1]
+
+ password = args['password']
+ setCfg('password', password)
+
+ cfg = getCfg()
+ pma_path = getServerDir() + '/pma.pass'
+ username = mw.getRandomString(10)
+ pass_cmd = cfg['username'] + ':' + mw.hasPwd(cfg['password'])
+ mw.writeFile(pma_path, pass_cmd)
+
+ mw.restartWeb()
+ return mw.returnJson(True, '修改成功!')
+
+
+def setPmaPath():
+ args = getArgs()
+ data = checkArgs(args, ['path'])
+ if not data[0]:
+ return data[1]
+
+ path = args['path']
+
+ if len(path) < 5:
+ return mw.returnJson(False, '不能小于5位!')
+
+ old_path = getServerDir() + "/" + getCfg()['path']
+ new_path = getServerDir() + "/" + path
+
+ mw.execShell("mv " + old_path + " " + new_path)
+ setCfg('path', path)
+ return mw.returnJson(True, '修改成功!')
+
+
+def accessLog():
+ return getServerDir() + '/access.log'
+
+
+def errorLog():
+ return getServerDir() + '/error.log'
+
+
+def installVersion():
+ return mw.readFile(getServerDir() + '/version.pl')
+
+if __name__ == "__main__":
+ 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 == 'conf':
+ print(getConf())
+ elif func == 'version':
+ print(installVersion())
+ elif func == 'get_cfg':
+ print(returnCfg())
+ elif func == 'config_inc':
+ print(getConfInc())
+ elif func == 'get_home_page':
+ print(getHomePage())
+ elif func == 'set_php_ver':
+ print(setPhpVer())
+ elif func == 'get_set_php_ver':
+ print(getSetPhpVer())
+ elif func == 'get_pma_port':
+ print(getPmaPort())
+ elif func == 'set_pma_port':
+ print(setPmaPort())
+ elif func == 'get_pma_option':
+ print(getPmaOption())
+ elif func == 'set_pma_username':
+ print(setPmaUsername())
+ elif func == 'set_pma_password':
+ print(setPmaPassword())
+ elif func == 'set_pma_path':
+ print(setPmaPath())
+ elif func == 'access_log':
+ print(accessLog())
+ elif func == 'error_log':
+ print(errorLog())
+ else:
+ print('error')
diff --git a/plugins/phpldapadmin/info.json b/plugins/phpldapadmin/info.json
new file mode 100755
index 000000000..119efd016
--- /dev/null
+++ b/plugins/phpldapadmin/info.json
@@ -0,0 +1,15 @@
+{
+ "title":"phpLDAPadmin",
+ "tip":"soft",
+ "name":"phpldapadmin",
+ "type":"运行环境",
+ "ps":"LDAP管理工具",
+ "versions":["1.2.6.7"],
+ "shell":"install.sh",
+ "checks":"server/phpldapadmin",
+ "path": "server/phpldapadmin",
+ "author":"leenooks",
+ "home":"https://github.com/leenooks/phpLDAPadmin",
+ "date":"2025-1-28",
+ "pid": "2"
+}
\ No newline at end of file
diff --git a/plugins/phpldapadmin/install.sh b/plugins/phpldapadmin/install.sh
new file mode 100755
index 000000000..40a2b0823
--- /dev/null
+++ b/plugins/phpldapadmin/install.sh
@@ -0,0 +1,90 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+# cd /www/server/mdserver-web/plugins/phpldapadmin && bash install.sh install 1.2.6.7
+# cd /www/server/mdserver-web && python3 plugins/phpldapadmin/index.py start
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+if [ "$sys_os" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+sysName=`uname`
+echo "use system: ${sysName}"
+
+if [ "${sysName}" == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eqi "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release; then
+ OSNAME='centos'
+elif grep -Eqi "Fedora" /etc/issue || grep -Eq "Fedora" /etc/*-release; then
+ OSNAME='fedora'
+elif grep -Eqi "Debian" /etc/issue || grep -Eq "Debian" /etc/*-release; then
+ OSNAME='debian'
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/*-release; then
+ OSNAME='ubuntu'
+elif grep -Eqi "Raspbian" /etc/issue || grep -Eq "Raspbian" /etc/*-release; then
+ OSNAME='raspbian'
+else
+ OSNAME='unknow'
+fi
+
+Install_App()
+{
+ if [ -d $serverPath/phpldapadmin ];then
+ exit 0
+ fi
+
+ mkdir -p ${serverPath}/phpldapadmin
+ mkdir -p ${serverPath}/source/phpldapadmin
+ echo "${1}" > ${serverPath}/phpldapadmin/version.pl
+
+ VER=$1
+
+ # https://github.com/leenooks/phpLDAPadmin/archive/refs/tags/1.2.6.7.tar.gz
+ FDIR=phpLDAPadmin-${VER}
+ FILE=${VER}.tar.gz
+ DOWNLOAD=https://github.com/leenooks/phpLDAPadmin/archive/refs/tags/${FILE}
+
+
+ if [ ! -f $serverPath/source/phpmyadmin/$FILE ];then
+ wget --no-check-certificate -O $serverPath/source/phpldapadmin/$FILE $DOWNLOAD
+ fi
+
+ if [ ! -d $serverPath/source/phpldapadmin/$FDIR ];then
+ cd $serverPath/source/phpldapadmin && tar zxvf $FILE
+ fi
+
+ cp -r $serverPath/source/phpldapadmin/$FDIR $serverPath/phpldapadmin/
+ cd $serverPath/phpldapadmin/ && mv $FDIR phpldapadmin
+ # rm -rf $serverPath/source/phpldapadmin/$FDIR
+
+ cd ${rootPath} && python3 ${rootPath}/plugins/phpldapadmin/index.py start
+ echo '安装完成'
+
+}
+
+Uninstall_App()
+{
+ cd ${rootPath} && python3 ${rootPath}/plugins/phpldapadmin/index.py stop
+
+ rm -rf ${serverPath}/phpldapadmin
+ echo '卸载完成'
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App $2
+else
+ Uninstall_App $2
+fi
diff --git a/plugins/phpldapadmin/js/phpldapadmin.js b/plugins/phpldapadmin/js/phpldapadmin.js
new file mode 100755
index 000000000..030b55243
--- /dev/null
+++ b/plugins/phpldapadmin/js/phpldapadmin.js
@@ -0,0 +1,164 @@
+function str2Obj(str){
+ var data = {};
+ kv = str.split('&');
+ for(i in kv){
+ v = kv[i].split('=');
+ data[v[0]] = v[1];
+ }
+ return data;
+}
+
+function pmaPost(method,args,callback){
+
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(str2Obj(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+ $.post('/plugins/run', {name:'phpldapadmin', func:method, args:_args}, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+function pmaAsyncPost(method,args){
+
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(str2Obj(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+ return syncPost('/plugins/run', {name:'phpldapadmin', func:method, args:_args});
+}
+
+function homePage(){
+ pmaPost('get_home_page', '', function(data){
+ var rdata = $.parseJSON(data.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ var con = '主页 ';
+ $(".soft-man-con").html(con);
+ });
+}
+
+//phpmyadmin切换php版本
+function phpVer(version) {
+
+ var _version = pmaAsyncPost('get_set_php_ver','')
+ if (_version['data'] != ''){
+ version = _version['data'];
+ }
+
+ $.post('/site/get_php_version', function(data) {
+ var rdata = data['data'];
+ // console.log(rdata);
+ var body = "PHP版本 ";
+ var optionSelect = '';
+ for (var i = 0; i < rdata.length; i++) {
+ optionSelect = rdata[i].version == version ? 'selected' : '';
+ body += "" + rdata[i].name + " "
+ }
+ body += ' 保存
';
+ $(".soft-man-con").html(body);
+ },'json');
+}
+
+function phpVerChange(type, msg) {
+ var phpver = $("#phpver").val();
+ pmaPost('set_php_ver', 'phpver='+phpver, function(data){
+ if ( data.data == 'ok' ){
+ layer.msg('设置成功!',{icon:1,time:2000,shade: [0.3, '#000']});
+ } else {
+ layer.msg('设置失败!',{icon:2,time:2000,shade: [0.3, '#000']});
+ }
+ });
+}
+
+
+//phpmyadmin安全设置
+function safeConf() {
+ pmaPost('get_pma_option', {}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:2,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ var cfg = rdata.data;
+ var con = '\
+ 访问端口 \
+ \
+ 保存 \
+
\
+ \
+ 用户名 \
+ \
+ 保存 \
+
\
+ \
+ 密码 \
+ \
+ 保存 \
+
\
+ \
+ \
+ 路径名 \
+ \
+ 保存 \
+
';
+ $(".soft-man-con").html(con);
+ });
+}
+
+function setPmaUsername(){
+ var username = $("input[name=username]").val();
+ pmaPost('set_pma_username',{'username':username}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+function setPmaPassword(){
+ var password = $("input[name=password]").val();
+ pmaPost('set_pma_password',{'password':password}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+function setPmaPath(){
+ var path = $("input[name=path]").val();
+ pmaPost('set_pma_path',{'path':path}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+//修改phpmyadmin端口
+function setPamPort() {
+ var pmport = $("#pmport").val();
+ if (pmport < 80 || pmport > 65535) {
+ layer.msg('端口范围不合法!', { icon: 2 });
+ return;
+ }
+ var data = 'port=' + pmport;
+
+ pmaPost('set_pma_port',data, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
\ No newline at end of file
diff --git a/plugins/phpmyadmin/conf/config.inc.php b/plugins/phpmyadmin/conf/config.inc.php
new file mode 100644
index 000000000..ab6a8250f
--- /dev/null
+++ b/plugins/phpmyadmin/conf/config.inc.php
@@ -0,0 +1,16 @@
+
diff --git a/plugins/phpmyadmin/conf/phpmyadmin.conf b/plugins/phpmyadmin/conf/phpmyadmin.conf
new file mode 100755
index 000000000..01d734fae
--- /dev/null
+++ b/plugins/phpmyadmin/conf/phpmyadmin.conf
@@ -0,0 +1,38 @@
+server
+{
+ listen 888;
+ server_name 127.0.0.1;
+ index index.html index.htm index.php;
+ root {$SERVER_PATH}/phpmyadmin;
+
+ #error_page 404 /404.html;
+ include {$PHP_CONF_PATH}/enable-php-{$PHP_VER}.conf;
+
+ #AUTH_START
+ auth_basic "Authorization";
+ auth_basic_user_file {$SERVER_PATH}/phpmyadmin/pma.pass;
+ #AUTH_END
+
+ location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
+ {
+ expires 30d;
+ }
+
+ location ~ .*\.(js|css)?$
+ {
+ expires 12h;
+ }
+
+ location ~ /.*\.(log|pass|json|pl)$ {
+ deny all;
+ }
+
+
+ location ~ /\.
+ {
+ deny all;
+ }
+
+ access_log {$SERVER_PATH}/phpmyadmin/access.log;
+ error_log {$SERVER_PATH}/phpmyadmin/error.log;
+}
\ No newline at end of file
diff --git a/plugins/phpmyadmin/ico.png b/plugins/phpmyadmin/ico.png
new file mode 100755
index 000000000..b2700e7a1
Binary files /dev/null and b/plugins/phpmyadmin/ico.png differ
diff --git a/plugins/phpmyadmin/index.html b/plugins/phpmyadmin/index.html
new file mode 100755
index 000000000..89c6440a9
--- /dev/null
+++ b/plugins/phpmyadmin/index.html
@@ -0,0 +1,24 @@
+
+
\ No newline at end of file
diff --git a/plugins/phpmyadmin/index.py b/plugins/phpmyadmin/index.py
new file mode 100755
index 000000000..72c0de66c
--- /dev/null
+++ b/plugins/phpmyadmin/index.py
@@ -0,0 +1,554 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+
+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
+import thisdb
+from utils.site import sites as MwSites
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'phpmyadmin'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+
+ 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 getConf():
+ return mw.getServerDir() + '/web_conf/nginx/vhost/phpmyadmin.conf'
+
+
+def getConfInc():
+ return getServerDir() + "/" + getCfg()['path'] + '/config.inc.php'
+
+
+def getPort():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'listen\s*(.*);'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+
+def getHomePage():
+ try:
+ port = getPort()
+ ip = '127.0.0.1'
+ if not mw.isAppleSystem():
+ ip = mw.getLocalIp()
+
+ cfg = getCfg()
+ auth = cfg['username']+':'+cfg['password']
+ rand_path = cfg['path']
+ url = 'http://' + auth + '@' + ip + ':' + port + '/' + rand_path + '/index.php'
+ return mw.returnJson(True, 'OK', url)
+ except Exception as e:
+ return mw.returnJson(False, '插件未启动!')
+
+
+def getPhpVer(expect=55):
+ php_vers = MwSites.instance().getPhpVersion()
+ v = php_vers['data']
+ is_find = False
+ for i in range(len(v)):
+ t = str(v[i]['version'])
+ if (t == expect):
+ is_find = True
+ return str(t)
+ expect_str = str(expect)
+ new_ex = expect_str[0:1]+"."+expect_str[1:2]
+ if t.find(new_ex) > -1:
+ is_find = True
+ return str(t)
+ if not is_find:
+ if len(v) > 1:
+ return v[1]['version']
+ return v[0]['version']
+ return str(expect)
+
+
+def getCachePhpVer():
+ cacheFile = getServerDir() + '/php.pl'
+ v = ''
+ if os.path.exists(cacheFile):
+ v = mw.readFile(cacheFile)
+ else:
+ v = getPhpVer()
+ mw.writeFile(cacheFile, v)
+ return v
+
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ php_ver = getCachePhpVer()
+ tmp = mw.execShell(
+ 'cat /dev/urandom | head -n 32 | md5sum | head -c 16')
+ blowfish_secret = tmp[0].strip()
+ # print php_ver
+ php_conf_dir = mw.getServerDir() + '/web_conf/php/conf'
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$PHP_CONF_PATH}', php_conf_dir)
+ content = content.replace('{$PHP_VER}', php_ver)
+ content = content.replace('{$BLOWFISH_SECRET}', blowfish_secret)
+
+ cfg = getCfg()
+
+ if cfg['choose'] == "mysql":
+ content = content.replace('{$CHOOSE_DB}', 'mysql')
+ content = content.replace('{$CHOOSE_DB_DIR}', 'mysql')
+ elif cfg['choose'] == "mysql-community":
+ content = content.replace('{$CHOOSE_DB}', 'mysql-community')
+ content = content.replace('{$CHOOSE_DB_DIR}', 'mysql-community')
+ elif cfg['choose'] == "mysql-apt":
+ content = content.replace('{$CHOOSE_DB}', 'mysql')
+ content = content.replace('{$CHOOSE_DB_DIR}', 'mysql-apt')
+ elif cfg['choose'] == "mysql-yum":
+ content = content.replace('{$CHOOSE_DB}', 'mysql')
+ content = content.replace('{$CHOOSE_DB_DIR}', 'mysql-yum')
+ else:
+ content = content.replace('{$CHOOSE_DB}', 'MariaDB')
+ content = content.replace('{$CHOOSE_DB_DIR}', 'mariadb')
+
+ content = content.replace('{$PMA_PATH}', cfg['path'])
+
+ port = cfg["port"]
+ rep = r'listen\s*(.*);'
+ content = re.sub(rep, "listen " + port + ';', content)
+ return content
+
+
+def initCfg():
+ cfg = getServerDir() + "/cfg.json"
+ if not os.path.exists(cfg):
+ data = {}
+ data['port'] = '888'
+ data['choose'] = 'mysql'
+ data['path'] = ''
+ data['username'] = 'admin'
+ data['password'] = 'admin'
+ mw.writeFile(cfg, json.dumps(data))
+
+
+def setCfg(key, val):
+ cfg = getServerDir() + "/cfg.json"
+ data = mw.readFile(cfg)
+ data = json.loads(data)
+ data[key] = val
+ mw.writeFile(cfg, json.dumps(data))
+
+
+def getCfg():
+ cfg = getServerDir() + "/cfg.json"
+ data = mw.readFile(cfg)
+ data = json.loads(data)
+ return data
+
+
+def returnCfg():
+ cfg = getServerDir() + "/cfg.json"
+ data = mw.readFile(cfg)
+ return data
+
+
+def status():
+ conf = getConf()
+ conf_inc = getServerDir() + "/" + getCfg()["path"] + '/config.inc.php'
+ # 两个文件都在,才算启动成功
+ if os.path.exists(conf) and os.path.exists(conf_inc):
+ return 'start'
+ return 'stop'
+
+
+def __release_port(port):
+ from collections import namedtuple
+ try:
+ from utils.firewall import Firewall as MwFirewall
+ MwFirewall.instance().addAcceptPort(port, 'phpMyAdmin默认端口', 'port')
+ return port
+ except Exception as e:
+ return "Release failed {}".format(e)
+
+
+def __delete_port(port):
+ from collections import namedtuple
+ try:
+ from utils.firewall import Firewall as MwFirewall
+ MwFirewall.instance().delAcceptPortCmd(port, 'tcp')
+ return port
+ except Exception as e:
+ return "Release failed {}".format(e)
+
+
+def openPort():
+ conf = getCfg()
+ port = conf['port']
+ for i in [port]:
+ __release_port(i)
+ return True
+
+
+def delPort():
+ conf = getCfg()
+ port = conf['port']
+ for i in [port]:
+ __delete_port(i)
+ return True
+
+
+def start():
+ initCfg()
+ openPort()
+
+ pma_dir = getServerDir() + "/phpmyadmin"
+ if os.path.exists(pma_dir):
+ rand_str = mw.getRandomString(6)
+ rand_str = rand_str.lower()
+ pma_dir_dst = pma_dir + "_" + rand_str
+ mw.execShell("mv " + pma_dir + " " + pma_dir_dst)
+ setCfg('path', 'phpmyadmin_' + rand_str)
+
+ file_tpl = getPluginDir() + '/conf/phpmyadmin.conf'
+ file_run = getConf()
+ if not os.path.exists(file_run):
+ centent = mw.readFile(file_tpl)
+ centent = contentReplace(centent)
+ mw.writeFile(file_run, centent)
+
+ pma_path = getServerDir() + '/pma.pass'
+ if not os.path.exists(pma_path):
+ username = mw.getRandomString(8)
+ password = mw.getRandomString(10)
+ pass_cmd = username + ':' + mw.hasPwd(password)
+ setCfg('username', username)
+ setCfg('password', password)
+ mw.writeFile(pma_path, pass_cmd)
+
+ tmp = getServerDir() + "/" + getCfg()["path"] + '/tmp'
+ if not os.path.exists(tmp):
+ os.mkdir(tmp)
+ mw.execShell("chown -R www:www " + tmp)
+
+ conf_run = getServerDir() + "/" + getCfg()["path"] + '/config.inc.php'
+ if not os.path.exists(conf_run):
+ conf_tpl = getPluginDir() + '/conf/config.inc.php'
+ centent = mw.readFile(conf_tpl)
+ centent = contentReplace(centent)
+ mw.writeFile(conf_run, centent)
+
+ log_a = accessLog()
+ log_e = errorLog()
+
+ for i in [log_a, log_e]:
+ if os.path.exists(i):
+ cmd = "echo '' > " + i
+ mw.execShell(cmd)
+
+ mw.restartWeb()
+ return 'ok'
+
+
+def stop():
+ conf = getConf()
+ if os.path.exists(conf):
+ os.remove(conf)
+ delPort()
+ mw.restartWeb()
+ return 'ok'
+
+
+def restart():
+ return start()
+
+
+def reload():
+ file_tpl = getPluginDir() + '/conf/phpmyadmin.conf'
+ file_run = getConf()
+ if os.path.exists(file_run):
+ centent = mw.readFile(file_tpl)
+ centent = contentReplace(centent)
+ mw.writeFile(file_run, centent)
+ return start()
+
+
+def setPhpVer():
+ args = getArgs()
+
+ if not 'phpver' in args:
+ return 'phpver missing'
+
+ cacheFile = getServerDir() + '/php.pl'
+ mw.writeFile(cacheFile, args['phpver'])
+
+ file_tpl = getPluginDir() + '/conf/phpmyadmin.conf'
+ file_run = getConf()
+
+ content = mw.readFile(file_tpl)
+ content = contentReplace(content)
+ mw.writeFile(file_run, content)
+
+ mw.restartWeb()
+ return 'ok'
+
+
+def getSetPhpVer():
+ cacheFile = getServerDir() + '/php.pl'
+ if os.path.exists(cacheFile):
+ return mw.readFile(cacheFile).strip()
+ return ''
+
+
+def getPmaOption():
+ data = getCfg()
+ return mw.returnJson(True, 'ok', data)
+
+
+def getPmaPort():
+ try:
+ port = getPort()
+ return mw.returnJson(True, 'OK', port)
+ except Exception as e:
+ # print(e)
+ return mw.returnJson(False, '插件未启动!')
+
+
+def setPmaPort():
+ args = getArgs()
+ data = checkArgs(args, ['port'])
+ if not data[0]:
+ return data[1]
+
+ port = args['port']
+ if port == '80':
+ return mw.returnJson(False, '80端不能使用!')
+
+ file = getConf()
+ if not os.path.exists(file):
+ return mw.returnJson(False, '插件未启动!')
+ content = mw.readFile(file)
+ rep = r'listen\s*(.*);'
+ content = re.sub(rep, "listen " + port + ';', content)
+ mw.writeFile(file, content)
+
+ setCfg("port", port)
+ mw.restartWeb()
+ return mw.returnJson(True, '修改成功!')
+
+
+def setPmaChoose():
+ args = getArgs()
+ data = checkArgs(args, ['choose'])
+ if not data[0]:
+ return data[1]
+
+ choose = args['choose']
+ setCfg('choose', choose)
+
+ pma_path = getCfg()['path']
+ conf_run = getServerDir() + "/" + pma_path + '/config.inc.php'
+
+ conf_tpl = getPluginDir() + '/conf/config.inc.php'
+ content = mw.readFile(conf_tpl)
+ content = contentReplace(content)
+ mw.writeFile(conf_run, content)
+
+ mw.restartWeb()
+ return mw.returnJson(True, '修改成功!')
+
+
+def setPmaUsername():
+ args = getArgs()
+ data = checkArgs(args, ['username'])
+ if not data[0]:
+ return data[1]
+
+ username = args['username']
+ setCfg('username', username)
+
+ cfg = getCfg()
+ pma_path = getServerDir() + '/pma.pass'
+ username = mw.getRandomString(10)
+ pass_cmd = cfg['username'] + ':' + mw.hasPwd(cfg['password'])
+ mw.writeFile(pma_path, pass_cmd)
+
+ mw.restartWeb()
+ return mw.returnJson(True, '修改成功!')
+
+
+def setPmaPassword():
+ args = getArgs()
+ data = checkArgs(args, ['password'])
+ if not data[0]:
+ return data[1]
+
+ password = args['password']
+ setCfg('password', password)
+
+ cfg = getCfg()
+ pma_path = getServerDir() + '/pma.pass'
+ username = mw.getRandomString(10)
+ pass_cmd = cfg['username'] + ':' + mw.hasPwd(cfg['password'])
+ mw.writeFile(pma_path, pass_cmd)
+
+ mw.restartWeb()
+ return mw.returnJson(True, '修改成功!')
+
+
+def setPmaPath():
+ args = getArgs()
+ data = checkArgs(args, ['path'])
+ if not data[0]:
+ return data[1]
+
+ path = args['path']
+
+ if len(path) < 5:
+ return mw.returnJson(False, '不能小于5位!')
+
+ old_path = getServerDir() + "/" + getCfg()['path']
+ new_path = getServerDir() + "/" + path
+
+ mw.execShell("mv " + old_path + " " + new_path)
+ setCfg('path', path)
+ return mw.returnJson(True, '修改成功!')
+
+
+def accessLog():
+ return getServerDir() + '/access.log'
+
+
+def errorLog():
+ return getServerDir() + '/error.log'
+
+
+def installVersion():
+ return mw.readFile(getServerDir() + '/version.pl')
+
+def pluginsDbSupport():
+ data = {}
+
+ data['installed'] = 'no'
+ install_path = getServerDir()
+ if not os.path.exists(install_path):
+ return mw.returnJson(True, 'ok', data)
+
+ data['installed'] = 'ok'
+ data['status'] = status()
+ if (data['status'] == 'stop'):
+ return mw.returnJson(True, 'ok', data)
+
+ data['cfg'] = getCfg()
+ port = getPort()
+ ip = '127.0.0.1'
+ if not mw.isAppleSystem():
+ ip = thisdb.getOption('server_ip')
+
+ cfg = data['cfg']
+ auth = cfg['username']+':'+cfg['password']
+ rand_path = cfg['path']
+ home_page = 'http://' + auth + '@' + ip + ':' + port + '/' + rand_path + '/index.php'
+
+ data['home_page'] = home_page
+ data['version'] = installVersion().strip()
+
+ return mw.returnJson(True, 'ok', data)
+
+def installPreInspection():
+ php_confdir = mw.getServerDir()+'/web_conf/php/conf'
+ if not os.path.exists(php_confdir):
+ return "必须先安装一个php版本!"
+ return 'ok'
+
+if __name__ == "__main__":
+ 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 == 'install_pre_inspection':
+ print(installPreInspection())
+ elif func == 'conf':
+ print(getConf())
+ elif func == 'version':
+ print(installVersion())
+ elif func == 'get_cfg':
+ print(returnCfg())
+ elif func == 'config_inc':
+ print(getConfInc())
+ elif func == 'get_home_page':
+ print(getHomePage())
+ elif func == 'set_php_ver':
+ print(setPhpVer())
+ elif func == 'get_set_php_ver':
+ print(getSetPhpVer())
+ elif func == 'get_pma_port':
+ print(getPmaPort())
+ elif func == 'set_pma_port':
+ print(setPmaPort())
+ elif func == 'get_pma_option':
+ print(getPmaOption())
+ elif func == 'set_pma_choose':
+ print(setPmaChoose())
+ elif func == 'set_pma_username':
+ print(setPmaUsername())
+ elif func == 'set_pma_password':
+ print(setPmaPassword())
+ elif func == 'set_pma_path':
+ print(setPmaPath())
+ elif func == 'access_log':
+ print(accessLog())
+ elif func == 'error_log':
+ print(errorLog())
+ elif func == 'plugins_db_support':
+ print(pluginsDbSupport())
+ else:
+ print('error')
diff --git a/plugins/phpmyadmin/info.json b/plugins/phpmyadmin/info.json
new file mode 100755
index 000000000..5f021cf76
--- /dev/null
+++ b/plugins/phpmyadmin/info.json
@@ -0,0 +1,18 @@
+{
+ "title":"phpMyAdmin",
+ "tip":"soft",
+ "name":"phpmyadmin",
+ "type":"运行环境",
+ "ps":"著名Web端MySQL管理工具",
+ "to_ver":["4.8.4"],
+ "install_pre_inspection":true,
+ "versions":["4.4.15","4.9.11","5.2.1"],
+ "updates":["4.4.15","4.9.11","5.2.1"],
+ "shell":"install.sh",
+ "checks":"server/phpmyadmin",
+ "path": "server/phpmyadmin",
+ "author":"phpMyAdmin",
+ "home":"https://www.phpmyadmin.net/",
+ "date":"2017-11-24",
+ "pid": "2"
+}
\ No newline at end of file
diff --git a/plugins/phpmyadmin/install.sh b/plugins/phpmyadmin/install.sh
new file mode 100755
index 000000000..9d7f1790a
--- /dev/null
+++ b/plugins/phpmyadmin/install.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+# cd /www/server/mdserver-web/plugins/phpmyadmin && bash install.sh install 4.4.15
+# cd /www/server/mdserver-web && python3 plugins/phpmyadmin/index.py start
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+if [ "$sys_os" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+sysName=`uname`
+echo "use system: ${sysName}"
+
+if [ "${sysName}" == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eqi "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release; then
+ OSNAME='centos'
+elif grep -Eqi "Fedora" /etc/issue || grep -Eq "Fedora" /etc/*-release; then
+ OSNAME='fedora'
+elif grep -Eqi "Debian" /etc/issue || grep -Eq "Debian" /etc/*-release; then
+ OSNAME='debian'
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/*-release; then
+ OSNAME='ubuntu'
+elif grep -Eqi "Raspbian" /etc/issue || grep -Eq "Raspbian" /etc/*-release; then
+ OSNAME='raspbian'
+else
+ OSNAME='unknow'
+fi
+
+Install_phpmyadmin()
+{
+ if [ -d $serverPath/phpmyadmin ];then
+ exit 0
+ fi
+
+ mkdir -p ${serverPath}/source/phpmyadmin
+
+ VER=$1
+
+ FDIR=phpMyAdmin-${VER}-all-languages
+ FILE=phpMyAdmin-${VER}-all-languages.tar.gz
+ DOWNLOAD=https://files.phpmyadmin.net/phpMyAdmin/${VER}/$FILE
+
+
+ if [ ! -f $serverPath/source/phpmyadmin/$FILE ];then
+ wget --no-check-certificate -O $serverPath/source/phpmyadmin/$FILE $DOWNLOAD
+ fi
+
+ if [ ! -d $serverPath/source/phpmyadmin/$FDIR ];then
+ cd $serverPath/source/phpmyadmin && tar zxvf $FILE
+ fi
+
+ mkdir -p ${serverPath}/phpmyadmin
+ cp -r $serverPath/source/phpmyadmin/$FDIR $serverPath/phpmyadmin/
+ cd $serverPath/phpmyadmin/ && mv $FDIR phpmyadmin
+ rm -rf $serverPath/source/phpmyadmin/$FDIR
+
+ echo "${1}" > ${serverPath}/phpmyadmin/version.pl
+ cd ${rootPath} && python3 ${rootPath}/plugins/phpmyadmin/index.py start
+
+ echo '安装完成'
+}
+
+Uninstall_phpmyadmin()
+{
+ cd ${rootPath} && python3 ${rootPath}/plugins/phpmyadmin/index.py stop
+
+ rm -rf ${serverPath}/phpmyadmin
+ echo '卸载完成'
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_phpmyadmin $2
+else
+ Uninstall_phpmyadmin $2
+fi
diff --git a/plugins/phpmyadmin/js/phpmyadmin.js b/plugins/phpmyadmin/js/phpmyadmin.js
new file mode 100755
index 000000000..e81e05efc
--- /dev/null
+++ b/plugins/phpmyadmin/js/phpmyadmin.js
@@ -0,0 +1,184 @@
+function str2Obj(str){
+ var data = {};
+ kv = str.split('&');
+ for(i in kv){
+ v = kv[i].split('=');
+ data[v[0]] = v[1];
+ }
+ return data;
+}
+
+function pmaPost(method,args,callback){
+
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(str2Obj(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+ $.post('/plugins/run', {name:'phpmyadmin', func:method, args:_args}, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+function pmaAsyncPost(method,args){
+
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(str2Obj(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+ return syncPost('/plugins/run', {name:'phpmyadmin', func:method, args:_args});
+}
+
+function homePage(){
+ pmaPost('get_home_page', '', function(data){
+ var rdata = $.parseJSON(data.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ var con = '主页 ';
+ $(".soft-man-con").html(con);
+ });
+}
+
+//phpmyadmin切换php版本
+function phpVer(version) {
+
+ var _version = pmaAsyncPost('get_set_php_ver','')
+ if (_version['data'] != ''){
+ version = _version['data'];
+ }
+
+ $.post('/site/get_php_version', function(data) {
+ var rdata = data['data'];
+ // console.log(rdata);
+ var body = "PHP版本 ";
+ var optionSelect = '';
+ for (var i = 0; i < rdata.length; i++) {
+ optionSelect = rdata[i].version == version ? 'selected' : '';
+ body += "" + rdata[i].name + " "
+ }
+ body += ' 保存
';
+ $(".soft-man-con").html(body);
+ },'json');
+}
+
+function phpVerChange(type, msg) {
+ var phpver = $("#phpver").val();
+ pmaPost('set_php_ver', 'phpver='+phpver, function(data){
+ if ( data.data == 'ok' ){
+ layer.msg('设置成功!',{icon:1,time:2000,shade: [0.3, '#000']});
+ } else {
+ layer.msg('设置失败!',{icon:2,time:2000,shade: [0.3, '#000']});
+ }
+ });
+}
+
+
+//phpmyadmin安全设置
+function safeConf() {
+ pmaPost('get_pma_option', {}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:2,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ var cfg = rdata.data;
+ var con = '\
+ 访问端口 \
+ \
+ 保存 \
+
\
+ \
+ 访问切换 \
+ \
+ MariaDB \
+ MySQL \
+ MySQL[Tar] \
+ MySQL[APT] \
+ MySQL[YUM] \
+ \
+ 保存 \
+
\
+ \
+ 用户名 \
+ \
+ 保存 \
+
\
+ \
+ 密码 \
+ \
+ 保存 \
+
\
+ \
+ \
+ 路径名 \
+ \
+ 保存 \
+
';
+ $(".soft-man-con").html(con);
+ });
+}
+
+
+function setPmaChoose(){
+ var choose = $("#access_choose").val();
+ pmaPost('set_pma_choose',{'choose':choose}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+function setPmaUsername(){
+ var username = $("input[name=username]").val();
+ pmaPost('set_pma_username',{'username':username}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+function setPmaPassword(){
+ var password = $("input[name=password]").val();
+ pmaPost('set_pma_password',{'password':password}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+function setPmaPath(){
+ var path = $("input[name=path]").val();
+ pmaPost('set_pma_path',{'path':path}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+//修改phpmyadmin端口
+function setPamPort() {
+ var pmport = $("#pmport").val();
+ if (pmport < 80 || pmport > 65535) {
+ layer.msg('端口范围不合法!', { icon: 2 });
+ return;
+ }
+ var data = 'port=' + pmport;
+
+ pmaPost('set_pma_port',data, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
\ No newline at end of file
diff --git a/plugins/postgresql/class/pg.py b/plugins/postgresql/class/pg.py
new file mode 100755
index 000000000..0bf3b5bef
--- /dev/null
+++ b/plugins/postgresql/class/pg.py
@@ -0,0 +1,207 @@
+# coding: utf-8
+
+import re
+import os
+import sys
+
+import psycopg2
+
+
+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
+
+
+class ORM:
+ __DB_PASS = None
+ __DB_USER = 'postgres'
+ __DB_PORT = 5432
+ __DB_HOST = 'localhost'
+ __DB_CONN = None
+ __DB_CUR = None
+ __DB_ERR = None
+ __DB_CNF = '/etc/postgresql.cnf'
+ __DB_SOCKET = '/tmp/.s.PGSQL.5432'
+ __DB_CHARSET = "utf8"
+
+ __DB_TABLE = "" # 被操作的表名称
+ __OPT_WHERE = "" # where条件
+ __OPT_LIMIT = "" # limit条件
+ __OPT_GROUP = "" # group条件
+ __OPT_ORDER = "" # order条件
+ __OPT_FIELD = "*" # field条件
+ __OPT_PARAM = () # where值
+
+ def __Conn(self):
+ '''连接数据库'''
+ try:
+
+ if os.path.exists(self.__DB_SOCKET):
+ try:
+ self.__DB_CONN = psycopg2.connect(database='postgres',
+ user=self.__DB_USER,
+ password=self.__DB_PASS,
+ host=self.__DB_SOCKET,
+ port=int(self.__DB_PORT))
+ except Exception as e:
+ self.__DB_HOST = '127.0.0.1'
+ self.__DB_CONN = psycopg2.connect(database='postgres',
+ user=self.__DB_USER,
+ password=self.__DB_PASS,
+ host=self.__DB_SOCKET,
+ port=int(self.__DB_PORT))
+ else:
+ try:
+ self.__DB_CONN = psycopg2.connect(database='postgres',
+ user=self.__DB_USER,
+ password=self.__DB_PASS,
+ host=self.__DB_HOST,
+ port=int(self.__DB_PORT))
+ except Exception as e:
+ self.__DB_HOST = '127.0.0.1'
+ self.__DB_CONN = psycopg2.connect(database='postgres',
+ user=self.__DB_USER,
+ password=self.__DB_PASS,
+ host=self.__DB_HOST,
+ port=int(self.__DB_PORT))
+
+ self.__DB_CONN.autocommit = True
+ self.__DB_CUR = self.__DB_CONN.cursor()
+
+ return True
+ except Exception as e:
+ self.__DB_ERR = e
+ return False
+
+ def setDbConf(self, conf):
+ self.__DB_CNF = conf
+
+ def setSocket(self, sock):
+ self.__DB_SOCKET = sock
+
+ def setCharset(self, charset):
+ self.__DB_CHARSET = charset
+
+ def setPort(self, port):
+ self.__DB_PORT = port
+
+ def setHostAddr(self, host):
+ self.__DB_HOST = host
+
+ def setPwd(self, pwd):
+ self.__DB_PASS = pwd
+
+ def getPwd(self):
+ return self.__DB_PASS
+
+ def table(self, table):
+ # 设置表名
+ self.__DB_TABLE = table
+ return self
+
+ def where(self, where, param=()):
+ # WHERE条件
+ if where:
+ self.__OPT_WHERE = " WHERE " + where
+ self.__OPT_PARAM = param
+ return self
+
+ def andWhere(self, where, param):
+ # WHERE条件
+ if where:
+ self.__OPT_WHERE = self.__OPT_WHERE + " and " + where
+ # print(param)
+ # print(self.__OPT_PARAM)
+ self.__OPT_PARAM = self.__OPT_PARAM + param
+ return self
+
+ def order(self, order):
+ # ORDER条件
+ if len(order):
+ self.__OPT_ORDER = " ORDER BY " + order
+ else:
+ self.__OPT_ORDER = ""
+ return self
+
+ def group(self, group):
+ if len(group):
+ self.__OPT_GROUP = " GROUP BY " + group
+ else:
+ self.__OPT_GROUP = ""
+ return self
+
+ def limit(self, limit):
+ # LIMIT条件
+ if len(limit):
+ self.__OPT_LIMIT = " LIMIT " + limit
+ else:
+ self.__OPT_LIMIT = ""
+ return self
+
+ def field(self, field):
+ # FIELD条件
+ if len(field):
+ self.__OPT_FIELD = field
+ return self
+
+ def select(self):
+ # 查询数据集
+ if not self.__Conn():
+ return self.__DB_ERR
+ try:
+ sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \
+ self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT
+ # print(sql)
+ result = self.__DB_CUR.execute(sql, self.__OPT_PARAM)
+ data = self.__DB_CUR.fetchall()
+ ret = []
+ if self.__OPT_FIELD != "*":
+ field = self.__OPT_FIELD.split(',')
+ for row in data:
+ i = 0
+ field_k = {}
+ for key in field:
+ field_k[key] = row[i]
+ i += 1
+ ret.append(field_k)
+ else:
+ ret = map(list, data)
+ self.__Close()
+ return ret
+ except Exception as ex:
+ return "error: " + str(ex)
+
+ def execute(self, sql):
+ # 执行SQL语句返回受影响行
+ if not self.__Conn():
+ return self.__DB_ERR
+ try:
+ result = self.__DB_CUR.execute(sql)
+ self.__DB_CONN.commit()
+ self.__Close()
+ return result
+ except Exception as ex:
+ return ex
+
+ def query(self, sql):
+ # 执行SQL语句返回数据集
+ if not self.__Conn():
+ return self.__DB_ERR
+ try:
+ self.__DB_CUR.execute(sql)
+ result = self.__DB_CUR.fetchall()
+ # print(result)
+ # 将元组转换成列表
+ # data = map(list, result)
+ self.__Close()
+ return result
+ except Exception as ex:
+ return ex
+
+ def __Close(self):
+ # 关闭连接
+ self.__DB_CUR.close()
+ self.__DB_CONN.close()
diff --git a/plugins/postgresql/conf/pg_hba.conf b/plugins/postgresql/conf/pg_hba.conf
new file mode 100644
index 000000000..b411ed22f
--- /dev/null
+++ b/plugins/postgresql/conf/pg_hba.conf
@@ -0,0 +1,10 @@
+# PostgreSQL Client Authentication Configuration File
+
+# TYPE DATABASE USER ADDRESS METHOD
+# "local" is for Unix domain socket connections only
+local all all trust
+host all all 0.0.0.0/0 md5
+host all all ::1/128 md5
+local replication all trust
+host replication all 0.0.0.0/0 md5
+host replication all ::1/128 md5
\ No newline at end of file
diff --git a/plugins/postgresql/conf/pgsql.sql b/plugins/postgresql/conf/pgsql.sql
new file mode 100755
index 000000000..14ef6fb2d
--- /dev/null
+++ b/plugins/postgresql/conf/pgsql.sql
@@ -0,0 +1,43 @@
+CREATE TABLE IF NOT EXISTS `config` (
+ `id` INTEGER PRIMARY KEY AUTOINCREMENT,
+ `pg_root` TEXT
+);
+
+INSERT INTO `config` (`id`, `pg_root`) VALUES (1, 'admin');
+
+CREATE TABLE IF NOT EXISTS `databases` (
+ `id` INTEGER PRIMARY KEY AUTOINCREMENT,
+ `pid` INTEGER,
+ `name` TEXT,
+ `username` TEXT,
+ `password` TEXT,
+ `accept` TEXT,
+ `rw` TEXT DEFAULT 'rw',
+ `ps` TEXT,
+ `addtime` TEXT
+);
+-- ALTER TABLE `databases` ADD COLUMN `rw` TEXT DEFAULT 'rw';
+
+CREATE TABLE IF NOT EXISTS `master_replication_user` (
+ `id` INTEGER PRIMARY KEY AUTOINCREMENT,
+ `username` TEXT,
+ `password` TEXT,
+ `accept` TEXT,
+ `ps` TEXT,
+ `addtime` TEXT
+);
+
+-- 从库配置主库的[ssh private key]
+-- drop table `slave_id_rsa`;
+CREATE TABLE IF NOT EXISTS `slave_id_rsa` (
+ `id` INTEGER PRIMARY KEY AUTOINCREMENT,
+ `ip` TEXT,
+ `port` TEXT,
+ `user` TEXT,
+ `db_user` TEXT,
+ `id_rsa` TEXT,
+ `ps` TEXT,
+ `addtime` TEXT
+);
+
+
diff --git a/plugins/postgresql/conf/postgresql.conf b/plugins/postgresql/conf/postgresql.conf
new file mode 100644
index 000000000..dd7c5bb76
--- /dev/null
+++ b/plugins/postgresql/conf/postgresql.conf
@@ -0,0 +1,49 @@
+port = 5432
+listen_addresses='*'
+unix_socket_directories='/tmp,{$APP_PATH}'
+
+max_connections=200
+max_wal_size = 1GB
+min_wal_size = 80MB
+shared_buffers = 128MB
+work_mem = 4MB
+effective_cache_size = 4GB
+temp_buffers = 8MB
+max_prepared_transactions = 0
+max_stack_depth = 2MB
+bgwriter_lru_maxpages = 100
+max_worker_processes = 8
+dynamic_shared_memory_type = posix
+
+
+log_timezone = 'Asia/Shanghai'
+datestyle = 'iso, ymd'
+timezone = 'Asia/Shanghai'
+default_text_search_config = 'pg_catalog.simple'
+
+
+# 主配置
+#archive_mode = on
+#archive_command = 'test ! -f {$APP_PATH}/archivelog/%f && cp %p {$APP_PATH}/archivelog/%f'
+#wal_level = replica
+#max_wal_senders = 10
+#wal_sender_timeout = 60s
+
+
+# 从配置
+#hot_standby = on
+#primary_conninfo = 'host=192.168.0.100 port=5432 user=replica password=123456'
+#max_standby_streaming_delay = 30s
+#wal_receiver_status_interval = 10s
+#hot_standby_feedback = on
+#recovery_target_timeline= 'latest'
+
+
+logging_collector = on
+log_destination = 'stderr'
+log_directory = '{$APP_PATH}/logs'
+log_filename = 'postgresql-%Y-%m-%d.log'
+log_statement = all
+log_rotation_age = 7d
+log_rotation_size = 100MB
+log_min_duration_statement = 5000
diff --git a/plugins/postgresql/ico.png b/plugins/postgresql/ico.png
new file mode 100644
index 000000000..26e183c2f
Binary files /dev/null and b/plugins/postgresql/ico.png differ
diff --git a/plugins/postgresql/index.html b/plugins/postgresql/index.html
new file mode 100755
index 000000000..19647e423
--- /dev/null
+++ b/plugins/postgresql/index.html
@@ -0,0 +1,58 @@
+
+
+
+
\ No newline at end of file
diff --git a/plugins/postgresql/index.py b/plugins/postgresql/index.py
new file mode 100755
index 000000000..6ce89e219
--- /dev/null
+++ b/plugins/postgresql/index.py
@@ -0,0 +1,1652 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import subprocess
+import re
+import json
+
+
+# reload(sys)
+# sys.setdefaultencoding('utf-8')
+
+# python3 plugins/postgresql/index.py start 14.4
+# python3 plugins/postgresql/index.py run_info 14.4
+# ps -ef | grep -v grep| grep run_info | awk '{print $2}' | xargs kill -9
+# vi /etc/sysconfig/network-scripts/ifcfg-eth0
+
+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
+
+
+if mw.isAppleSystem():
+ cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\''
+ info = mw.execShell(cmd)
+ p = "/usr/local/lib/" + info[0].strip() + "/site-packages"
+ sys.path.append(p)
+
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'postgresql'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getInitDFile():
+ if app_debug:
+ return '/tmp/' + getPluginName()
+ return '/etc/init.d/' + getPluginName()
+
+
+def getArgs():
+ args = sys.argv[2:]
+
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+
+ return tmp
+
+
+def getBackupDir():
+ bk_path = mw.getBackupDir() + "/database/postgresql"
+ if not os.path.isdir(bk_path):
+ mw.execShell("mkdir -p {}".format(bk_path))
+ return bk_path
+
+
+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 getConf():
+ path = getServerDir() + '/data/postgresql.conf'
+ return path
+
+
+def configTpl():
+
+ clist = []
+
+ app_dir = getServerDir()
+ clist.append(app_dir + "/data/postgresql.conf")
+ clist.append(app_dir + "/data/pg_hba.conf")
+
+ return mw.getJson(clist)
+
+
+def pgHbaConf():
+ return getServerDir() + "/data/pg_hba.conf"
+
+
+def readConfigTpl():
+ args = getArgs()
+ data = checkArgs(args, ['file'])
+ if not data[0]:
+ return data[1]
+
+ content = mw.readFile(args['file'])
+ return mw.returnJson(True, 'ok', content)
+
+
+def getDbPort():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'port\s*=\s*(\d*)?'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+
+def getSocketFile():
+ # sock_name = '.s.PGSQL.' + getDbPort()
+ sock_name = ""
+ sock_tmp = '/tmp/' + sock_name
+ if os.path.exists(sock_tmp):
+ return sock_tmp
+
+ sock_app = getServerDir() + "/" + sock_name
+ if os.path.exists(sock_app):
+ return sock_app
+ return sock_app
+
+
+def getInitdTpl(version=''):
+ path = getPluginDir() + '/init.d/postgresql.tpl'
+ if not os.path.exists(path):
+ path = getPluginDir() + '/init.d/postgresql.tpl'
+ return path
+
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$APP_PATH}', service_path + '/postgresql')
+ return content
+
+
+def pSqliteDb(dbname='databases', name='pgsql'):
+ file = getServerDir() + '/' + name + '.db'
+ if not os.path.exists(file):
+ conn = mw.M(dbname).dbPos(getServerDir(), name)
+ csql = mw.readFile(getPluginDir() + '/conf/pgsql.sql')
+ csql_list = csql.split(';')
+ for index in range(len(csql_list)):
+ conn.execute(csql_list[index], ())
+ else:
+ # 现有run
+ # conn = mw.M(dbname).dbPos(getServerDir(), name)
+ # csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
+ # csql_list = csql.split(';')
+ # for index in range(len(csql_list)):
+ # conn.execute(csql_list[index], ())
+ conn = mw.M(dbname).dbPos(getServerDir(), name)
+ return conn
+
+
+def pgDb():
+
+ sys.path.append(getPluginDir() + "/class")
+ import pg
+
+ db = pg.ORM()
+
+ db.setPort(getDbPort())
+ db.setPwd(pSqliteDb('config').where('id=?', (1,)).getField('pg_root'))
+ db.setSocket(getSocketFile())
+ return db
+
+
+def initConfig(version=''):
+ conf_dir = getServerDir()
+ init_pl = conf_dir + "/init.pl"
+ if not os.path.exists(init_pl):
+ mw.writeFile(init_pl, 'ok')
+
+ # postgresql.conf
+ pg_conf = conf_dir + '/data/postgresql.conf'
+ tpl = getPluginDir() + '/conf/postgresql.conf'
+ content = mw.readFile(tpl)
+ content = contentReplace(content)
+ mw.writeFile(pg_conf, content)
+
+ # pg_hba.conf
+ tpl = getPluginDir() + '/conf/pg_hba.conf'
+ pg_hba_conf = conf_dir + '/data/pg_hba.conf'
+ content = mw.readFile(tpl)
+ mw.writeFile(pg_hba_conf, content)
+
+ logfile = runLog()
+ if not os.path.exists(logfile):
+ mw.writeFile(logfile, '')
+
+
+def initDreplace(version=''):
+
+ conf_dir = getServerDir()
+ conf_list = [
+ conf_dir + "/logs",
+ conf_dir + "/tmp",
+ conf_dir + "/archivelog",
+ ]
+ for c in conf_list:
+ if not os.path.exists(c):
+ os.mkdir(c)
+
+ # systemd
+ system_dir = mw.systemdCfgDir()
+ service = system_dir + '/postgresql.service'
+ if os.path.exists(system_dir) and not os.path.exists(service):
+ tpl = getPluginDir() + '/init.d/postgresql.service.tpl'
+ service_path = mw.getServerDir()
+ content = mw.readFile(tpl)
+ content = contentReplace(content)
+ mw.writeFile(service, content)
+ mw.execShell('systemctl daemon-reload')
+
+ if not mw.isAppleSystem():
+ mw.execShell('chown -R postgres:postgres ' + getServerDir())
+
+ initd_path = getServerDir() + '/init.d'
+ if not os.path.exists(initd_path):
+ os.mkdir(initd_path)
+
+ file_bin = initd_path + '/' + getPluginName()
+ if not os.path.exists(file_bin):
+ tpl = getInitdTpl(version)
+ content = mw.readFile(tpl)
+ content = contentReplace(content)
+ mw.writeFile(file_bin, content)
+ mw.execShell('chmod +x ' + file_bin)
+ return file_bin
+
+
+def status(version=''):
+ data = mw.execShell(
+ "ps -ef|grep postgres |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'")
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def pgCmd(cmd):
+ return "su - postgres -c \"" + cmd + "\""
+
+
+def execShellPg(cmd):
+ return mw.execShell(pgCmd(cmd))
+
+
+def pGetDbUser():
+ if mw.isAppleSystem():
+ user = mw.execShell(
+ "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
+ return user
+ return 'postgresql'
+
+
+def initPgData():
+ serverdir = getServerDir()
+ if not os.path.exists(serverdir + '/data'):
+ cmd = serverdir + '/bin/initdb -D ' + serverdir + "/data"
+ if not mw.isAppleSystem():
+ execShellPg(cmd)
+ return False
+ mw.execShell(cmd)
+ return False
+ return True
+
+
+def initPgPwd():
+
+ serverdir = getServerDir()
+ pwd = mw.getRandomString(16)
+
+ cmd_pass = serverdir + '/bin/createuser -s -r postgres'
+
+ if not mw.isAppleSystem():
+ cmd_pass = 'su - postgres -c "' + cmd_pass + '"'
+ data = mw.execShell(cmd_pass)
+
+ cmd_pass = "echo \"alter user postgres with password '" + pwd + "'\" | "
+ if not mw.isAppleSystem():
+ cmd = serverdir + '/bin/psql -d postgres'
+ cmd_pass = cmd_pass + ' ' + pgCmd(cmd)
+ else:
+ cmd_pass = cmd_pass + serverdir + '/bin/psql -d postgres'
+
+ data = mw.execShell(cmd_pass)
+ # print(cmd_pass)
+ # print(data)
+
+ pSqliteDb('config').where('id=?', (1,)).save('pg_root', (pwd,))
+ return True
+
+
+def pgOp(version, method):
+ # import commands
+ init_file = initDreplace()
+ cmd = init_file + ' ' + method
+ # print(cmd)
+ try:
+ isInited = initPgData()
+ initConfig(version)
+ if not isInited:
+ if mw.isAppleSystem():
+ cmd_init_start = init_file + ' start'
+ subprocess.Popen(cmd_init_start, stdout=subprocess.PIPE, shell=True,
+ bufsize=4096, stderr=subprocess.PIPE)
+
+ time.sleep(6)
+ else:
+ mw.execShell('systemctl start postgresql')
+
+ initPgPwd()
+
+ if mw.isAppleSystem():
+ cmd_init_stop = init_file + ' stop'
+ subprocess.Popen(cmd_init_stop, stdout=subprocess.PIPE, shell=True,
+ bufsize=4096, stderr=subprocess.PIPE)
+ time.sleep(3)
+ else:
+ mw.execShell('systemctl stop postgresql')
+
+ if mw.isAppleSystem():
+ sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True,
+ bufsize=4096, stderr=subprocess.PIPE)
+ sub.wait(5)
+ else:
+ mw.execShell('systemctl ' + method + ' postgresql')
+ return 'ok'
+ except Exception as e:
+ # raise
+ return method + ":" + str(e)
+
+
+def appCMD(version, action):
+ return pgOp(version, action)
+
+
+def start(version=''):
+ return appCMD(version, 'start')
+
+
+def stop(version=''):
+ return appCMD(version, 'stop')
+
+
+def restart(version=''):
+ return appCMD(version, 'restart')
+
+
+def reload(version=''):
+ logfile = runLog()
+ if os.path.exists(logfile):
+ mw.writeFile(logfile, '')
+ return appCMD(version, 'reload')
+
+
+def initdStatus():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status postgresql | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+
+def initdInstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl enable postgresql')
+ return 'ok'
+
+
+def initdUinstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl disable postgresql')
+ return 'ok'
+
+
+def getMyDbPos():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'datadir\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+
+def getPgPort():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'port\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+
+def setPgPort():
+ args = getArgs()
+ data = checkArgs(args, ['port'])
+ if not data[0]:
+ return data[1]
+
+ port = args['port']
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r"port\s*=\s*([0-9]+)\s*\n"
+ content = re.sub(rep, 'port = ' + port + '\n', content)
+ mw.writeFile(file, content)
+ restart()
+ return mw.returnJson(True, '编辑成功!')
+
+
+def runInfo():
+
+ if status(version) == 'stop':
+ return mw.returnJson(False, 'PG未启动', [])
+
+ db = pgDb()
+ data_dir = getServerDir() + "/data"
+ port = getPgPort()
+ result = {}
+
+ result['uptime'] = mw.execShell(
+ '''cat {}/postmaster.pid |sed -n 3p '''.format(data_dir))[0]
+ timestamp = result['uptime']
+ time_local = time.localtime(int(timestamp))
+ dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
+ result['uptime'] = dt
+
+ result['progress_num'] = mw.execShell(
+ "ps -ef |grep postgres |wc -l")[0].strip()
+ result['pid'] = mw.execShell(
+ '''cat {}/postmaster.pid |sed -n 1p '''.format(data_dir))[0].strip()
+ res = db.query(
+ "select count(*) from pg_stat_activity where not pid=pg_backend_pid()")
+
+ # print(res)
+ result['connections'] = res[0][0]
+
+ res = db.query("select pg_size_pretty(pg_database_size('postgres'))")
+ result['pg_size'] = res[0][0]
+ result['pg_mem'] = mw.execShell(
+ '''cat /proc/%s/status|grep VmRSS|awk -F: '{print $2}' ''' % (result['pid']))[0]
+
+ result['pg_vm_lock'] = mw.execShell(
+ '''cat /proc/%s/status|grep VmLck|awk -F: '{print $2}' ''' % (result['pid'].strip()))[0]
+ result['pg_vm_high'] = mw.execShell(
+ '''cat /proc/%s/status|grep VmHWM|awk -F: '{print $2}' ''' % (result['pid'].strip()))[0]
+ result['pg_vm_data_size'] = mw.execShell(
+ '''cat /proc/%s/status|grep VmData|awk -F: '{print $2}' ''' % (result['pid'].strip()))[0]
+ result['pg_vm_sk_size'] = mw.execShell(
+ '''cat /proc/%s/status|grep VmStk|awk -F: '{print $2}' ''' % (result['pid'].strip()))[0]
+ result['pg_vm_code_size'] = mw.execShell(
+ '''cat /proc/%s/status|grep VmExe|awk -F: '{print $2}' ''' % (result['pid'].strip()))[0]
+ result['pg_vm_lib_size'] = mw.execShell(
+ '''cat /proc/%s/status|grep VmLib|awk -F: '{print $2}' ''' % (result['pid'].strip()))[0]
+ result['pg_vm_swap_size'] = mw.execShell(
+ '''cat /proc/%s/status|grep VmSwap|awk -F: '{print $2}' ''' % (result['pid'].strip()))[0]
+ result['pg_vm_page_size'] = mw.execShell(
+ '''cat /proc/%s/status|grep VmPTE|awk -F: '{print $2}' ''' % (result['pid'].strip()))[0]
+ result['pg_sigq'] = mw.execShell(
+ '''cat /proc/%s/status|grep SigQ|awk -F: '{print $2}' ''' % (result['pid'].strip()))[0]
+
+ return mw.getJson(result)
+
+
+def runLog():
+ return getServerDir() + "/logs/server.log"
+
+
+def getSlowLog():
+ # pgsql慢日志查询
+ return getServerDir() + "/logs/" + time.strftime("postgresql-%Y-%m-%d.log")
+
+
+def getUnit(args):
+ unit = ''
+ if "GB" in args:
+ unit = "GB"
+ elif "MB" in args:
+ unit = "MB"
+ elif "KB" in args:
+ unit = "KB"
+ elif "kB" in args:
+ unit = "kB"
+ return unit
+
+
+def pgDbStatus():
+
+ data_directory = getServerDir() + "/data"
+ data = {}
+ shared_buffers, work_mem, effective_cache_size, maintence_work_mem, max_connections, temp_buffers, max_prepared_transactions, max_stack_depth, bgwriter_lru_maxpages, max_worker_processes, listen_addresses = '', '', '', '', '', '', '', '', '', '', ''
+ with open("{}/postgresql.conf".format(data_directory)) as f:
+ for i in f:
+ if i.strip().startswith("shared_buffers"):
+ shared_buffers = i.split("=")[1]
+ elif i.strip().startswith("#shared_buffers"):
+ shared_buffers = i.split("=")[1]
+
+ shared_buffers_num = re.match(
+ r'\d+', shared_buffers.strip()).group() if re.match(r'\d+', shared_buffers.strip()) else ""
+ data['shared_buffers'] = [shared_buffers_num, "MB",
+ "PG通过shared_buffers和内核和磁盘打交道,通常设置为实际内存的10%."]
+
+ if i.strip().startswith("work_mem"):
+ work_mem = i.split("=")[1]
+ elif i.strip().startswith("#work_mem"):
+ work_mem = i.split("=")[1]
+
+ work_mem_num = re.match(
+ r'\d+', work_mem.strip()).group() if re.match(r'\d+', work_mem.strip()) else ""
+ data['work_mem'] = [work_mem_num, "MB",
+ "增加work_mem有助于提高排序的速度。通常设置为实际RAM的2% -4%."]
+
+ if i.strip().startswith("effective_cache_size"):
+ effective_cache_size = i.split("=")[1]
+ elif i.strip().startswith("#effective_cache_size"):
+ effective_cache_size = i.split("=")[1]
+
+ effective_cache_size_num = re.match(r'\d+', effective_cache_size.strip(
+ )).group() if re.match(r'\d+', effective_cache_size.strip()) else ""
+ data['effective_cache_size'] = [effective_cache_size_num,
+ "GB", "PG能够使用的最大缓存,比如4G的内存,可以设置为3GB."]
+
+ if i.strip().startswith("temp_buffers "):
+ temp_buffers = i.split("=")[1]
+ elif i.strip().startswith("#temp_buffers "):
+ temp_buffers = i.split("=")[1]
+
+ temp_buffers_num = re.match(
+ r'\d+', temp_buffers.strip()).group() if re.match(r'\d+', temp_buffers.strip()) else ""
+ data['temp_buffers'] = [temp_buffers_num, "MB",
+ "设置每个数据库会话使用的临时缓冲区的最大数目,默认是8MB"]
+
+ if i.strip().startswith("max_connections"):
+ max_connections = i.split("=")[1]
+ elif i.strip().startswith("#max_connections"):
+ max_connections = i.split("=")[1]
+
+ max_connections_num = re.match(
+ r'\d+', max_connections.strip()).group() if re.match(r'\d+', max_connections.strip()) else ""
+ data['max_connections'] = [max_connections_num,
+ getUnit(max_connections), "最大连接数"]
+
+ if i.strip().startswith("max_prepared_transactions"):
+ max_prepared_transactions = i.split("=")[1]
+ elif i.strip().startswith("#max_prepared_transactions"):
+ max_prepared_transactions = i.split("=")[1]
+
+ max_prepared_transactions_num = re.match(r'\d+', max_prepared_transactions.strip(
+ )).group() if re.match(r'\d+', max_prepared_transactions.strip()) else ""
+ data['max_prepared_transactions'] = [max_prepared_transactions_num, getUnit(
+ max_prepared_transactions), "设置可以同时处于 prepared 状态的事务的最大数目"]
+
+ if i.strip().startswith("max_stack_depth "):
+ max_stack_depth = i.split("=")[1]
+ elif i.strip().startswith("#max_stack_depth "):
+ max_stack_depth = i.split("=")[1]
+
+ max_stack_depth_num = re.match(
+ r'\d+', max_stack_depth.strip()).group() if re.match(r'\d+', max_stack_depth.strip()) else ""
+ data['max_stack_depth'] = [max_stack_depth_num,
+ "MB", "指定服务器的执行堆栈的最大安全深度,默认是2MB"]
+
+ if i.strip().startswith("bgwriter_lru_maxpages "):
+ bgwriter_lru_maxpages = i.split("=")[1]
+ elif i.strip().startswith("#bgwriter_lru_maxpages "):
+ bgwriter_lru_maxpages = i.split("=")[1]
+
+ bgwriter_lru_maxpages_num = re.match(r'\d+', bgwriter_lru_maxpages.strip(
+ )).group() if re.match(r'\d+', bgwriter_lru_maxpages.strip()) else ""
+ data['bgwriter_lru_maxpages'] = [
+ bgwriter_lru_maxpages_num, "", "一个周期最多写多少脏页"]
+
+ if i.strip().startswith("max_worker_processes "):
+ max_worker_processes = i.split("=")[1]
+ elif i.strip().startswith("#max_worker_processes "):
+ max_worker_processes = i.split("=")[1]
+
+ max_worker_processes_num = re.match(r'\d+', max_worker_processes.strip(
+ )).group() if re.match(r'\d+', max_worker_processes.strip()) else ""
+ data['max_worker_processes'] = [max_worker_processes_num,
+ "", "如果要使用worker process, 最多可以允许fork 多少个worker进程."]
+
+ # if i.strip().startswith("listen_addresses"):
+ # listen_addresses = i.split("=")[1]
+ # elif i.strip().startswith("#listen_addresses"):
+ # listen_addresses = i.split("=")[1]
+
+ # listen_addresses = re.match(r"\'.*?\'", listen_addresses.strip()).group(
+ # ) if re.match(r"\'.*?\'", listen_addresses.strip()) else ""
+ # data['listen_addresses'] = [listen_addresses.replace(
+ # "'", '').replace("127.0.0.1", 'localhost'), "", "pgsql监听地址"]
+
+ # 返回数据到前端
+ data['status'] = True
+ return mw.getJson(data)
+
+
+def sedConf(name, val):
+ path = getServerDir() + "/data/postgresql.conf"
+ content = ''
+ with open(path) as f:
+ for i in f:
+ if i.strip().startswith(name):
+ i = "{} = {} \n".format(name, val)
+ content += i
+
+ mw.writeFile(path, content)
+ return True
+
+
+def pgSetDbStatus():
+ '''
+ 保存pgsql性能调整信息
+ '''
+ args = getArgs()
+ data = checkArgs(args, ['shared_buffers', 'work_mem', 'effective_cache_size',
+ 'temp_buffers', 'max_connections', 'max_prepared_transactions',
+ 'max_stack_depth', 'bgwriter_lru_maxpages', 'max_worker_processes'])
+ if not data[0]:
+ return data[1]
+
+ for k, v in args.items():
+ sedConf(k, v)
+
+ restart()
+ return mw.returnJson(True, '设置成功!')
+
+
+def setUserPwd(version=''):
+ args = getArgs()
+ data = checkArgs(args, ['password', 'name'])
+ if not data[0]:
+ return data[1]
+
+ newpwd = args['password']
+ username = args['name']
+ uid = args['id']
+ try:
+ pdb = pgDb()
+ psdb = pSqliteDb('databases')
+ name = psdb.where('id=?', (uid,)).getField('name')
+
+ r = pdb.execute(
+ "alter user {} with password '{}'".format(username, newpwd))
+
+ psdb.where("id=?", (uid,)).setField('password', newpwd)
+ return mw.returnJson(True, mw.getInfo('修改数据库[{1}]密码成功!', (name,)))
+ except Exception as ex:
+ return mw.returnJson(False, mw.getInfo('修改数据库[{1}]密码失败[{2}]!', (name, str(ex),)))
+
+
+def getDbBackupListFunc(dbname=''):
+ bkDir = getBackupDir()
+ blist = os.listdir(bkDir)
+ r = []
+
+ bname = 'db_' + dbname
+ blen = len(bname)
+ for x in blist:
+ fbstr = x[0:blen]
+ if fbstr == bname:
+ r.append(x)
+ return r
+
+
+def setDbBackup():
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ scDir = getPluginDir() + '/scripts/backup.py'
+ cmd = 'python3 ' + scDir + ' database ' + args['name'] + ' 3'
+ os.system(cmd)
+ return mw.returnJson(True, 'ok')
+
+def rootPwd():
+ return pSqliteDb('config').where('id=?', (1,)).getField('pg_root')
+
+def importDbBackup():
+ args = getArgs()
+ data = checkArgs(args, ['file', 'name'])
+ if not data[0]:
+ return data[1]
+
+ file = args['file']
+ name = args['name']
+
+ file_path = getBackupDir() + '/' + file
+ file_path_sql = getBackupDir() + '/' + file.replace('.gz', '')
+
+ if not os.path.exists(file_path_sql):
+ cmd = 'cd ' + getBackupDir() + ' && gzip -d ' + file
+ mw.execShell(cmd)
+
+ pwd = pSqliteDb('config').where('id=?', (1,)).getField('pg_root')
+
+ mysql_cmd = mw.getFatherDir() + '/server/mysql/bin/mysql -uroot -p' + pwd + ' ' + name + ' < ' + file_path_sql
+
+ # print(mysql_cmd)
+ os.system(mysql_cmd)
+ return mw.returnJson(True, 'ok')
+
+
+def deleteDbBackup():
+ args = getArgs()
+ data = checkArgs(args, ['filename'])
+ if not data[0]:
+ return data[1]
+
+ bkDir = getBackupDir()
+ os.remove(bkDir + '/' + args['filename'])
+ return mw.returnJson(True, 'ok')
+
+
+def getDbBackupList():
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ r = getDbBackupListFunc(args['name'])
+ bkDir = getBackupDir()
+ rr = []
+ for x in range(0, len(r)):
+ p = bkDir + '/' + r[x]
+ data = {}
+ data['name'] = r[x]
+
+ rsize = os.path.getsize(p)
+ data['size'] = mw.toSize(rsize)
+
+ t = os.path.getctime(p)
+ t = time.localtime(t)
+
+ data['time'] = time.strftime('%Y-%m-%d %H:%M:%S', t)
+ rr.append(data)
+
+ data['file'] = p
+
+ return mw.returnJson(True, 'ok', rr)
+
+
+def getDbList():
+ args = getArgs()
+ page = 1
+ page_size = 10
+ search = ''
+ data = {}
+ if 'page' in args:
+ page = int(args['page'])
+
+ if 'page_size' in args:
+ page_size = int(args['page_size'])
+
+ if 'search' in args:
+ search = args['search']
+
+ conn = pSqliteDb('databases')
+ limit = str((page - 1) * page_size) + ',' + str(page_size)
+ condition = ''
+ if not search == '':
+ condition = "name like '%" + search + "%'"
+ field = 'id,pid,name,username,password,accept,rw,ps,addtime'
+ clist = conn.where(condition, ()).field(
+ field).limit(limit).order('id desc').select()
+
+ for x in range(0, len(clist)):
+ dbname = clist[x]['name']
+ blist = getDbBackupListFunc(dbname)
+ # print(blist)
+ clist[x]['is_backup'] = False
+ if len(blist) > 0:
+ clist[x]['is_backup'] = True
+
+ count = conn.where(condition, ()).count()
+ _page = {}
+ _page['count'] = count
+ _page['p'] = page
+ _page['row'] = page_size
+ _page['tojs'] = 'dbList'
+ data['page'] = mw.getPage(_page)
+ data['data'] = clist
+
+ info = {}
+ info['root_pwd'] = pSqliteDb('config').where(
+ 'id=?', (1,)).getField('pg_root')
+ data['info'] = info
+
+ return mw.getJson(data)
+
+
+def syncGetDatabases():
+ pdb = pgDb()
+ psdb = pSqliteDb('databases')
+ data = pdb.table('pg_database').field(
+ 'datname').where("datistemplate=false").select()
+ nameArr = ['postgres']
+ n = 0
+
+ for value in data:
+ vdb_name = value["datname"]
+ b = False
+ for key in nameArr:
+ if vdb_name == key:
+ b = True
+ break
+ if b:
+ continue
+ if psdb.where("name=?", (vdb_name,)).count() > 0:
+ continue
+ host = '127.0.0.1/32'
+ ps = vdb_name
+ addTime = time.strftime('%Y-%m-%d %X', time.localtime())
+ if psdb.add('name,username,password,accept,ps,addtime', (vdb_name, vdb_name, '', host, ps, addTime)):
+ n += 1
+
+ msg = mw.getInfo('本次共从服务器获取了{1}个数据库!', (str(n),))
+ return mw.returnJson(True, msg)
+
+
+def addDb():
+ args = getArgs()
+ data = checkArgs(args, ['password', 'name', 'db_user', 'dataAccess'])
+ if not data[0]:
+ return data[1]
+
+ address = ''
+ if 'address' in args:
+ address = args['address'].strip()
+
+ dbname = args['name'].strip()
+ dbuser = args['db_user'].strip()
+ password = args['password'].strip()
+ dataAccess = args['dataAccess'].strip()
+
+ listen_ip = "127.0.0.1/32"
+ if 'listen_ip' in args:
+ listen_ip = args['listen_ip'].strip()
+
+ if not re.match(r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}/\d+", listen_ip):
+ return mw.returnJson(False, "你输入的权限不合法,添加失败!")
+
+ # 修改监听所有地址
+ if listen_ip not in ["127.0.0.1/32", "localhost", "127.0.0.1"]:
+ sedConf("listen_addresses", "'*'")
+
+ reg = r"^[\w\.-]+$"
+ if not re.match(reg, dbname):
+ return mw.returnJson(False, '数据库名称不能带有特殊符号!')
+
+ checks = ['root', 'mysql', 'test', 'sys', 'postgres', 'postgresql']
+ if dbuser in checks or len(dbuser) < 1:
+ return mw.returnJson(False, '数据库用户名不合法!')
+ if dbname in checks or len(dbname) < 1:
+ return mw.returnJson(False, '数据库名称不合法!')
+
+ if len(password) < 1:
+ password = mw.md5(time.time())[0:8]
+
+ pdb = pgDb()
+ psdb = pSqliteDb('databases')
+
+ if psdb.where("name=? or username=?", (dbname, dbuser)).count():
+ return mw.returnJson(False, '数据库或用户已存在!')
+
+ sql = "select pg_terminate_backend(pid) from pg_stat_activity where DATNAME = 'template1';"
+ pdb.execute(sql)
+ r = pdb.execute("create database " + dbname)
+ # print(r)
+ r = pdb.execute("create user " + dbuser)
+ # print(r)
+ pdb.execute("alter user {} with password '{}'".format(dbuser, password,))
+ pdb.execute(
+ "GRANT ALL PRIVILEGES ON DATABASE {} TO {}".format(dbname, dbuser,))
+
+ pg_hba = getServerDir() + "/data/pg_hba.conf"
+
+ content = mw.readFile(pg_hba)
+ content += "\nhost {} {} {} md5".format(
+ dbname, dbuser, listen_ip)
+ mw.writeFile(pg_hba, content)
+
+ addTime = time.strftime('%Y-%m-%d %X', time.localtime())
+ psdb.add('pid,name,username,password,accept,ps,addtime',
+ (0, dbname, dbuser, password, listen_ip, dbname, addTime))
+
+ restart()
+ return mw.returnJson(True, '添加成功!')
+
+
+def delDb():
+ args = getArgs()
+ data = checkArgs(args, ['id', 'name'])
+ if not data[0]:
+ return data[1]
+
+ did = args['id']
+ name = args['name']
+
+ pdb = pgDb()
+ psdb = pSqliteDb('databases')
+
+ username = psdb.where('id=?', (did,)).getField('username')
+ # print(username, len(username))
+ if len(username) > 0:
+ r = pdb.execute("drop user " + str(username))
+ # print(r)
+
+ sql = "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE datname='" + \
+ name + "' AND pid<>pg_backend_pid();"
+
+ r = pdb.execute(sql)
+ # print(r)
+ r = pdb.execute("drop database " + name)
+ # print(r)
+
+ pg_hba = pgHbaConf()
+ old_config = mw.readFile(pg_hba)
+ new_config = re.sub(r'host\s*{}.*'.format(name), '', old_config).strip()
+ mw.writeFile(pg_hba, new_config)
+
+ psdb.where("id=?", (did,)).delete()
+ return mw.returnJson(True, '删除成功!')
+
+
+def setDbRw(version=''):
+ args = getArgs()
+ data = checkArgs(args, ['username', 'id', 'rw'])
+ if not data[0]:
+ return data[1]
+
+ username = args['username']
+ uid = args['id']
+ rw = args['rw']
+
+ pdb = pgDb()
+ psdb = pSqliteDb('databases')
+ dbname = psdb.where("id=?", (uid,)).getField('name')
+
+ sql = "REVOKE ALL ON database " + dbname + " FROM " + username
+ pdb.query(sql)
+
+ if rw == 'rw':
+ sql = "GRANT SELECT, INSERT, UPDATE, DELETE ON database " + dbname + " TO " + username
+ elif rw == 'r':
+ sql = "GRANT SELECT ON database " + dbname + " TO " + username
+ else:
+ sql = "GRANT all ON database " + dbname + " TO " + username
+
+ r = pdb.execute(sql)
+ psdb.where("id=?", (uid,)).setField('rw', rw)
+ return mw.returnJson(True, '切换成功!')
+
+
+def getDbAccess():
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+ name = args['name']
+ psdb = pSqliteDb('databases')
+ accept = psdb.where("name=?", (name,)).getField('accept')
+ return mw.returnJson(True, accept)
+
+
+def setDbAccess():
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ name = args['name']
+ access = args['access']
+
+ psdb = pSqliteDb('databases')
+
+ conf = pgHbaConf()
+ data = mw.readFile(conf)
+ new_data = re.sub(r'host\s*{}.*'.format(name), '', data).strip()
+
+ new_data += "\nhost {} {} {} md5".format(
+ name, name, access)
+ mw.writeFile(conf, new_data)
+
+ psdb.where("name=?", (name,)).setField('accept', access)
+ return mw.returnJson(True, "设置成功")
+
+
+def pgBack():
+ # 备份数据库
+
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ bk_path_upload = getBackupDir()
+ database = args['name']
+ port = getPgPort()
+
+ cmd = '''su - postgres -c "/www/server/pgsql/bin/pg_dump -c {} -p {} "| gzip > {}/{}_{}.gz '''.format(
+ database, port, bk_path_upload, database, time.strftime("%Y%m%d_%H%M%S"))
+
+ if mw.isAppleSystem():
+ cmd = '''{}/bin/pg_dump -c {} -p {} | gzip > {}/{}_{}.gz '''.format(
+ getServerDir(), database, port, bk_path_upload, database, time.strftime("%Y%m%d_%H%M%S"))
+ mw.execShell(cmd)
+
+ return mw.returnJson(True, '备份成功!')
+
+
+def pgBackList():
+
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ database = args['name']
+
+ bk_path_upload = getBackupDir()
+ file_list = os.listdir(bk_path_upload)
+ data = []
+ for i in file_list:
+ if i.split("_")[0].startswith(database):
+ file_path = os.path.join(bk_path_upload, i)
+ file_info = os.stat(file_path)
+ create_time = file_info.st_ctime
+ time_local = time.localtime(int(create_time))
+ create_time = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
+ file_size = file_info.st_size
+ file_size = mw.toSize(file_size)
+ data.append({"name": i, "time": create_time,
+ "size": file_size, "file": file_path})
+
+ return mw.returnJson(True, 'ok', data)
+
+
+def importDbBackup():
+ args = getArgs()
+ data = checkArgs(args, ['file', 'name'])
+ if not data[0]:
+ return data[1]
+
+ file = args['file']
+ name = args['name']
+
+ bk_path_upload = getBackupDir()
+
+ file_path = os.path.join(bk_path_upload, name)
+ if not os.path.exists(file_path):
+ return mw.returnJson(False, '备份文件不存在')
+
+ port = getPgPort()
+ cmd = '''gunzip -c {}|su - postgres -c " /www/server/pgsql/bin/psql -d {} -p {} " '''.format(
+ file, name, port)
+
+ if mw.isAppleSystem():
+ cmd = '''gunzip -c {} | {}/bin/psql -d {} -p {}'''.format(
+ name, getServerDir(), port)
+
+ # print(cmd)
+
+ mw.execShell(cmd)
+ return mw.returnJson(True, '恢复数据库成功')
+
+
+def deleteDbBackup():
+ args = getArgs()
+ data = checkArgs(args, ['filename'])
+ if not data[0]:
+ return data[1]
+
+ bk_path_upload = getBackupDir()
+ os.remove(bk_path_upload + '/' + args['filename'])
+ return mw.returnJson(True, 'ok')
+
+
+############ 主从功能 ######################
+def getMasterStatus(version=''):
+
+ data = {}
+ data['mode'] = 'classic'
+ data['status'] = False
+ data['slave_status'] = False
+ pg_conf = getServerDir() + "/data/postgresql.conf"
+ pg_content = mw.readFile(pg_conf)
+
+ if pg_content.find('#archive_mode') > -1:
+ data['status'] = False
+ else:
+ data['status'] = True
+
+ if pg_content.find('#hot_standby') > -1:
+ data['slave_status'] = False
+ else:
+ data['slave_status'] = True
+
+ return mw.returnJson(True, '设置成功', data)
+
+
+def setMasterStatus(version=''):
+ pg_conf = getServerDir() + "/data/postgresql.conf"
+ data = mw.readFile(pg_conf)
+
+ if data.find('#archive_mode') > -1:
+ data = data.replace('#archive_mode', 'archive_mode')
+ data = data.replace('#archive_command', 'archive_command')
+ data = data.replace('#wal_level', 'wal_level')
+ data = data.replace('#max_wal_senders', 'max_wal_senders')
+ data = data.replace('#wal_sender_timeout', 'wal_sender_timeout')
+ else:
+ data = data.replace('archive_mode', '#archive_mode')
+ data = data.replace('archive_command', '#archive_command')
+ data = data.replace('wal_level', '#wal_level')
+ data = data.replace('max_wal_senders', '#max_wal_senders')
+ data = data.replace('wal_sender_timeout', '#wal_sender_timeout')
+
+ mw.writeFile(pg_conf, data)
+ restart(version)
+ return mw.returnJson(True, '设置成功')
+
+
+def setSlaveStatus(version):
+ pg_conf = getServerDir() + "/data/postgresql.conf"
+ data = mw.readFile(pg_conf)
+ if data.find('#hot_standby') > -1:
+ data = data.replace('#hot_standby', 'hot_standby')
+ data = data.replace('#primary_conninfo', 'primary_conninfo')
+ data = data.replace('#max_standby_streaming_delay',
+ 'max_standby_streaming_delay')
+ data = data.replace('#wal_receiver_status_interval',
+ 'wal_receiver_status_interval')
+ data = data.replace('#hot_standby_feedback', 'hot_standby_feedback')
+ data = data.replace('#recovery_target_timeline',
+ 'recovery_target_timeline')
+ else:
+ data = data.replace('hot_standby', '#hot_standby')
+ data = data.replace('primary_conninfo', '#primary_conninfo')
+ data = data.replace('max_standby_streaming_delay',
+ '#max_standby_streaming_delay')
+ data = data.replace('wal_receiver_status_interval',
+ '#wal_receiver_status_interval')
+ data = data.replace('hot_standby_feedback', '#hot_standby_feedback')
+ data = data.replace('#recovery_target_timeline',
+ 'recovery_target_timeline')
+
+ mw.writeFile(pg_conf, data)
+
+ restart(version)
+ return mw.returnJson(True, '设置成功')
+
+
+def getSlaveList(version=''):
+
+ db = pgDb()
+
+ res = db.execute('select * from pg_stat_replication')
+ print(res)
+ # dlist = db.query('show slave status')
+ # ret = []
+ # for x in range(0, len(dlist)):
+ # tmp = {}
+ # tmp['Master_User'] = dlist[x]["Master_User"]
+ # tmp['Master_Host'] = dlist[x]["Master_Host"]
+ # tmp['Master_Port'] = dlist[x]["Master_Port"]
+ # tmp['Master_Log_File'] = dlist[x]["Master_Log_File"]
+ # tmp['Slave_IO_Running'] = dlist[x]["Slave_IO_Running"]
+ # tmp['Slave_SQL_Running'] = dlist[x]["Slave_SQL_Running"]
+ # ret.append(tmp)
+ data = {}
+ data['data'] = []
+
+ return mw.getJson(data)
+
+
+def getSlaveSSHByIp(version=''):
+ args = getArgs()
+ data = checkArgs(args, ['ip'])
+ if not data[0]:
+ return data[1]
+
+ ip = args['ip']
+
+ conn = pSqliteDb('slave_id_rsa', 'pgsql_slave')
+ data = conn.field('ip,port,db_user,id_rsa').where("ip=?", (ip,)).select()
+ return mw.returnJson(True, 'ok', data)
+
+
+def getSlaveSSHList(version=''):
+ args = getArgs()
+ data = checkArgs(args, ['page', 'page_size'])
+ if not data[0]:
+ return data[1]
+
+ page = int(args['page'])
+ page_size = int(args['page_size'])
+
+ conn = pSqliteDb('slave_id_rsa', 'pgsql_slave')
+ limit = str((page - 1) * page_size) + ',' + str(page_size)
+
+ field = 'id,ip,port,db_user,id_rsa,ps,addtime'
+ clist = conn.field(field).limit(limit).order('id desc').select()
+ count = conn.count()
+
+ data = {}
+ _page = {}
+ _page['count'] = count
+ _page['p'] = page
+ _page['row'] = page_size
+ _page['tojs'] = args['tojs']
+ data['page'] = mw.getPage(_page)
+ data['data'] = clist
+
+ return mw.getJson(data)
+
+
+def addSlaveSSH(version=''):
+ import base64
+
+ args = getArgs()
+ data = checkArgs(args, ['ip'])
+ if not data[0]:
+ return data[1]
+
+ ip = args['ip']
+ if ip == "":
+ return mw.returnJson(True, 'ok')
+
+ data = checkArgs(args, ['port', 'id_rsa'])
+ if not data[0]:
+ return data[1]
+
+ id_rsa = args['id_rsa']
+ port = args['port']
+ user = 'root'
+ addTime = time.strftime('%Y-%m-%d %X', time.localtime())
+
+ conn = pSqliteDb('slave_id_rsa', 'pgsql_slave')
+ data = conn.field('ip,id_rsa').where("ip=?", (ip,)).select()
+ if len(data) > 0:
+ res = conn.where("ip=?", (ip,)).save(
+ 'port,id_rsa', (port, id_rsa,))
+ else:
+ conn.add('ip,port,user,id_rsa,ps,addtime',
+ (ip, port, user, id_rsa, '', addTime))
+
+ return mw.returnJson(True, '设置成功!')
+
+
+def delSlaveSSH(version=''):
+ args = getArgs()
+ data = checkArgs(args, ['ip'])
+ if not data[0]:
+ return data[1]
+
+ ip = args['ip']
+
+ conn = pSqliteDb('slave_id_rsa', 'pgsql_slave')
+ conn.where("ip=?", (ip,)).delete()
+ return mw.returnJson(True, 'ok')
+
+
+def updateSlaveSSH(version=''):
+ args = getArgs()
+ data = checkArgs(args, ['ip', 'id_rsa'])
+ if not data[0]:
+ return data[1]
+
+ ip = args['ip']
+ id_rsa = args['id_rsa']
+ conn = pSqliteDb('slave_id_rsa', 'pgsql_slave')
+ conn.where("ip=?", (ip,)).save('id_rsa', (id_rsa,))
+ return mw.returnJson(True, 'ok')
+
+
+def getMasterRepSlaveList(version=''):
+ args = getArgs()
+ data = checkArgs(args, ['page', 'page_size'])
+ if not data[0]:
+ return data[1]
+
+ page = int(args['page'])
+ page_size = int(args['page_size'])
+ data = {}
+
+ conn = pSqliteDb('master_replication_user')
+ limit = str((page - 1) * page_size) + ',' + str(page_size)
+
+ field = 'id,username,password,accept,ps,addtime'
+ clist = conn.field(field).limit(limit).order('id desc').select()
+ count = conn.count()
+
+ _page = {}
+ _page['count'] = count
+ _page['p'] = page
+ _page['row'] = page_size
+ _page['tojs'] = 'getMasterRepSlaveList'
+ data['page'] = mw.getPage(_page)
+ data['data'] = clist
+
+ return mw.getJson(data)
+
+
+def addMasterRepSlaveUser(version=''):
+ args = getArgs()
+ data = checkArgs(args, ['username', 'password', 'address'])
+ if not data[0]:
+ return data[1]
+
+ address = args['address'].strip()
+ username = args['username'].strip()
+ password = args['password'].strip()
+
+ if len(password) < 1:
+ password = mw.md5(time.time())[0:8]
+
+ if not re.match(r"^[\w\.-]+$", username):
+ return mw.returnJson(False, '用户名不能带有特殊符号!')
+ checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
+ if username in checks or len(username) < 1:
+ return mw.returnJson(False, '用户名不合法!')
+ if password in checks or len(password) < 1:
+ return mw.returnJson(False, '密码不合法!')
+
+ pdb = pgDb()
+ psdb = pSqliteDb('master_replication_user')
+
+ if psdb.where("username=?", (username)).count() > 0:
+ return mw.returnJson(False, '用户已存在!')
+
+ sql = "CREATE ROLE " + username + " login replication password '" + password + "'"
+ pdb.execute(sql)
+
+ pg_conf = pgHbaConf()
+ data = mw.readFile(pg_conf)
+
+ data = data + "\nhost replication " + \
+ username + " " + address + " md5"
+
+ mw.writeFile(pg_conf, data)
+
+ addTime = time.strftime('%Y-%m-%d %X', time.localtime())
+ psdb.add('username,password,accept,ps,addtime',
+ (username, password, address, '', addTime))
+ return mw.returnJson(True, '添加成功!')
+
+
+def delMasterRepSlaveUser(version=''):
+ args = getArgs()
+ data = checkArgs(args, ['username'])
+ if not data[0]:
+ return data[1]
+
+ name = args['username']
+
+ pdb = pgDb()
+ psdb = pSqliteDb('master_replication_user')
+
+ pdb.execute("drop user " + name)
+
+ pg_hba = pgHbaConf()
+ old_config = mw.readFile(pg_hba)
+ new_config = re.sub(
+ r'host\s*replication\s*{}.*'.format(name), '', old_config).strip()
+ mw.writeFile(pg_hba, new_config)
+
+ psdb.where("username=?", (args['username'],)).delete()
+ return mw.returnJson(True, '删除成功!')
+
+
+def getMasterRepSlaveUserCmd(version=''):
+ args = getArgs()
+ data = checkArgs(args, ['username', 'db'])
+ if not data[0]:
+ return data[1]
+
+ psdb = pSqliteDb('master_replication_user')
+ mdir = getServerDir()
+ port = getPgPort()
+ localIp = mw.getLocalIp()
+ f = 'username,password'
+ username = args['username']
+ if username == '':
+ clist = psdb.field(f).limit('1').order('id desc').select()
+ else:
+ clist = psdb.field(f).where(
+ "username=?", (username,)).order('id desc').select()
+
+ if len(clist) == 0:
+ return mw.returnJson(False, '请添加同步账户!')
+
+ cmd = 'echo "' + clist[0]['password'] + '" | ' + mdir + '/bin/pg_basebackup -Fp --progress -D ' + mdir + \
+ '/postgresql/data -h ' + localIp + ' -p ' + port + \
+ ' -U ' + clist[0]['username'] + ' --password'
+
+ data = {}
+ data['cmd'] = cmd
+ data['info'] = clist
+ return mw.returnJson(True, 'ok!', data)
+
+
+def slaveSyncCmd(version=''):
+ data = {}
+ data['cmd'] = 'cd /www/server/mdserver-web && python3 plugins/postgresql/index.py do_full_sync'
+ return mw.returnJson(True, 'ok!', data)
+
+
+def writeDbSyncStatus(data):
+ path = '/tmp/db_async_status.txt'
+ mw.writeFile(path, json.dumps(data))
+
+
+def doFullSync(version=''):
+
+ db = pgDb()
+
+ id_rsa_conn = pSqliteDb('slave_id_rsa', 'pgsql_slave')
+ data = id_rsa_conn.field('ip,port,db_user,id_rsa').find()
+
+ SSH_PRIVATE_KEY = "/tmp/pg_sync_id_rsa.txt"
+ id_rsa = data['id_rsa'].replace('\\n', '\n')
+ mw.writeFile(SSH_PRIVATE_KEY, id_rsa)
+
+ ip = data["ip"]
+ master_port = data['port']
+ print("master ip:", ip)
+
+ writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
+
+ import paramiko
+ paramiko.util.log_to_file('paramiko.log')
+ ssh = paramiko.SSHClient()
+
+ print(SSH_PRIVATE_KEY)
+ if not os.path.exists(SSH_PRIVATE_KEY):
+ writeDbSyncStatus({'code': 0, 'msg': '需要配置SSH......', 'progress': 0})
+ return 'fail'
+
+ try:
+ # ssh.load_system_host_keys()
+ mw.execShell("chmod 600 " + SSH_PRIVATE_KEY)
+ key = paramiko.RSAKey.from_private_key_file(SSH_PRIVATE_KEY)
+ ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+ print(ip, master_port)
+
+ # pkey=key
+ # key_filename=SSH_PRIVATE_KEY
+ ssh.connect(hostname=ip, port=int(master_port),
+ username='root', pkey=key)
+ except Exception as e:
+ print(str(e))
+ writeDbSyncStatus(
+ {'code': 0, 'msg': 'SSH配置错误:' + str(e), 'progress': 0})
+ return 'fail'
+
+ writeDbSyncStatus({'code': 0, 'msg': '登录Master成功...', 'progress': 5})
+
+ print("同步文件", "start")
+ t = ssh.get_transport()
+ sftp = paramiko.SFTPClient.from_transport(t)
+ copy_status = sftp.get(
+ "/www/server/postgresql/pgsql.db", getServerDir() + "/pgsql.db")
+ print("同步信息:", copy_status)
+ print("同步文件", "end")
+ if copy_status == None:
+ writeDbSyncStatus({'code': 2, 'msg': '数据库信息同步本地完成...', 'progress': 40})
+
+ cmd = 'cd /www/server/mdserver-web && python3 plugins/postgresql/index.py get_master_rep_slave_user_cmd {"username":"","db":""}'
+ stdin, stdout, stderr = ssh.exec_command(cmd)
+ result = stdout.read()
+ result = result.decode('utf-8')
+ cmd_data = json.loads(result)
+
+ print(cmd_data)
+ username = cmd_data['data']['info'][0]['username']
+ password = cmd_data['data']['info'][0]['password']
+
+ writeDbSyncStatus({'code': 3, 'msg': '数据库获取完成...', 'progress': 45})
+
+ mdir = getServerDir()
+ port = getPgPort()
+
+ cmd = mdir + '/bin/pg_basebackup -Fp --progress -D ' + mdir + \
+ '/postgresql/data -h ' + ip + ' -p ' + port + \
+ ' -U ' + username + ' --password'
+ print(cmd)
+
+ cmd_tmp = "/tmp/cmd_run.sh"
+
+ cmd_tmp_data = """#!/bin/expect
+spawn %s
+expect "Password:"
+
+send "%s\r"
+
+interact
+""" % (cmd, password)
+
+ mw.writeFile(cmd_tmp, cmd_tmp_data)
+
+ os.system("expect " + cmd_tmp)
+
+ writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
+
+ os.system("rm -rf " + SSH_PRIVATE_KEY)
+ os.system("rm -rf " + cmd_tmp)
+ return True
+
+
+def installPreInspection(version):
+ return 'ok'
+
+
+def uninstallPreInspection(version):
+ return 'ok'
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+
+ version = "14.4"
+ version_pl = getServerDir() + "/version.pl"
+ if os.path.exists(version_pl):
+ version = mw.readFile(version_pl).strip()
+
+ if func == 'status':
+ print(status(version))
+ elif func == 'start':
+ print(start(version))
+ elif func == 'stop':
+ print(stop(version))
+ elif func == 'restart':
+ print(restart(version))
+ elif func == 'reload':
+ print(reload(version))
+ 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(version))
+ elif func == 'uninstall_pre_inspection':
+ print(uninstallPreInspection(version))
+ elif func == 'conf':
+ print(getConf())
+ elif func == 'pg_hba_conf':
+ print(pgHbaConf())
+ elif func == 'config_tpl':
+ print(configTpl())
+ elif func == 'read_config_tpl':
+ print(readConfigTpl())
+ elif func == 'run_info':
+ print(runInfo())
+ elif func == 'run_log':
+ print(runLog())
+ elif func == 'slow_log':
+ print(getSlowLog())
+ elif func == 'db_status':
+ print(pgDbStatus())
+ elif func == 'set_db_status':
+ print(pgSetDbStatus())
+ elif func == 'set_user_pwd':
+ print(setUserPwd())
+ elif func == 'pg_port':
+ print(getPgPort())
+ elif func == 'set_pg_port':
+ print(setPgPort())
+ elif func == 'root_pwd':
+ print(rootPwd())
+ elif func == 'get_db_list':
+ print(getDbList())
+ elif func == 'add_db':
+ print(addDb())
+ elif func == 'del_db':
+ print(delDb())
+ elif func == 'set_db_rw':
+ print(setDbRw(version))
+ elif func == 'get_db_access':
+ print(getDbAccess())
+ elif func == 'set_db_access':
+ print(setDbAccess())
+ elif func == 'pg_back':
+ print(pgBack())
+ elif func == 'pg_back_list':
+ print(pgBackList())
+ elif func == 'import_db_backup':
+ print(importDbBackup())
+ elif func == 'delete_db_backup':
+ print(deleteDbBackup())
+ elif func == 'sync_get_databases':
+ print(syncGetDatabases())
+ elif func == 'add_slave_ssh':
+ print(addSlaveSSH(version))
+ elif func == 'get_slave_list':
+ print(getSlaveList(version))
+ elif func == 'del_slave_ssh':
+ print(delSlaveSSH(version))
+ elif func == 'update_slave_ssh':
+ print(updateSlaveSSH(version))
+ elif func == 'get_slave_ssh_list':
+ print(getSlaveSSHList(version))
+ elif func == 'get_slave_ssh_by_ip':
+ print(getSlaveSSHByIp(version))
+ elif func == 'get_master_status':
+ print(getMasterStatus(version))
+ elif func == 'set_master_status':
+ print(setMasterStatus(version))
+ elif func == 'set_slave_status':
+ print(setSlaveStatus(version))
+ elif func == 'get_master_rep_slave_list':
+ print(getMasterRepSlaveList(version))
+ elif func == 'add_master_rep_slave_user':
+ print(addMasterRepSlaveUser(version))
+ elif func == 'del_master_rep_slave_user':
+ print(delMasterRepSlaveUser(version))
+ elif func == 'get_master_rep_slave_user_cmd':
+ print(getMasterRepSlaveUserCmd(version))
+ elif func == 'slave_sync_cmd':
+ print(slaveSyncCmd(version))
+ elif func == 'do_full_sync':
+ print(doFullSync(version))
+ else:
+ print('error')
diff --git a/plugins/postgresql/info.json b/plugins/postgresql/info.json
new file mode 100755
index 000000000..3fcd8c84b
--- /dev/null
+++ b/plugins/postgresql/info.json
@@ -0,0 +1,20 @@
+{
+ "hook":["database"],
+ "sort": 6,
+ "title":"PostgreSQL",
+ "tip":"soft",
+ "name":"postgresql",
+ "type":"运行环境",
+ "ps":"功能强大的开源数据库",
+ "coexist": false,
+ "install_pre_inspection":true,
+ "uninstall_pre_inspection":true,
+ "versions":["14","15","16","17"],
+ "shell":"install.sh",
+ "checks":"server/postgresql",
+ "path":"server/postgresql",
+ "author":"postgresql",
+ "home":"https://www.postgresql.org/",
+ "date":"2022-08-07",
+ "pid": "2"
+}
\ No newline at end of file
diff --git a/plugins/postgresql/init.d/postgresql.service.tpl b/plugins/postgresql/init.d/postgresql.service.tpl
new file mode 100644
index 000000000..1dfaadd73
--- /dev/null
+++ b/plugins/postgresql/init.d/postgresql.service.tpl
@@ -0,0 +1,16 @@
+[Unit]
+Description=PostgreSQL: a powerful open source database
+After=network.target
+
+[Service]
+Type=forking
+User=postgres
+Group=postgres
+WorkingDirectory={$APP_PATH}
+ExecStart={$APP_PATH}/bin/pg_ctl start -D {$APP_PATH}/data
+ExecReload={$APP_PATH}/bin/pg_ctl restart -D {$APP_PATH}/data
+ExecStop={$APP_PATH}/bin/pg_ctl stop -D {$APP_PATH}/data
+PrivateTmp=false
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/plugins/postgresql/init.d/postgresql.tpl b/plugins/postgresql/init.d/postgresql.tpl
new file mode 100644
index 000000000..b4b2f7acd
--- /dev/null
+++ b/plugins/postgresql/init.d/postgresql.tpl
@@ -0,0 +1,67 @@
+#!/bin/bash
+# chkconfig: 2345 55 25
+# description: PostgreSQL Service
+
+### BEGIN INIT INFO
+# Provides: Midoks
+# Required-Start: $all
+# Required-Stop: $all
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts PostgreSQL
+# Description: starts the PostgreSQL
+### END INIT INFO
+
+
+PATH=/usr/local/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export LC_ALL="en_US.UTF-8"
+
+MW_PATH={$SERVER_PATH}
+PATH=$PATH:$MW_PATH/bin
+
+if [ -f $MW_PATH/bin/activate ];then
+ source $MW_PATH/bin/activate
+fi
+
+pg_start()
+{
+ touch {$APP_PATH}/logs/server.log
+ {$APP_PATH}/bin/pg_ctl -D {$APP_PATH}/data -l {$APP_PATH}/logs/server.log start
+}
+
+
+pg_stop()
+{
+ {$APP_PATH}/bin/pg_ctl -D {$APP_PATH}/data -l {$APP_PATH}/logs/server.log stop
+}
+
+
+pg_status()
+{
+ isStart=$(ps aux | grep 'postgres'| grep -v grep | grep -v 'postgresql status' | awk '{print $2}')
+ if [ "$isStart" != '' ];then
+ echo -e "\033[32mPostgreSQL (pid $isStart) already running\033[0m"
+ else
+ echo -e "\033[31mPostgreSQL not running\033[0m"
+ fi
+}
+
+
+pg_reload()
+{
+ echo '' > {$APP_PATH}/logs/server.log
+ {$APP_PATH}/bin/pg_ctl -D {$APP_PATH}/data -l {$APP_PATH}/logs/server.log reload
+}
+
+
+
+case "$1" in
+ 'start') pg_start;;
+ 'stop') pg_stop;;
+ 'reload') pg_reload;;
+ 'restart')
+ pg_stop
+ pg_start;;
+ 'status')
+ pg_status;;
+esac
diff --git a/plugins/postgresql/install.sh b/plugins/postgresql/install.sh
new file mode 100755
index 000000000..8d9b534ca
--- /dev/null
+++ b/plugins/postgresql/install.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+# cd /www/server/mdserver-web/plugins/postgresql && bash install.sh install 16
+
+action=$1
+type=$2
+
+VERSION=(${type//./ })
+
+pip install psycopg2-binary
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+ pip install psycopg2-binary
+fi
+
+
+if [ "${2}" == "" ];then
+ echo '缺少安装脚本...'
+ exit 0
+fi
+
+if [ ! -d $curPath/versions/$VERSION ];then
+ echo '缺少安装脚本2...'
+ exit 0
+fi
+
+if [ "${action}" == "uninstall" ];then
+ if [ -f /usr/lib/systemd/system/postgresql.service ] || [ -f /lib/systemd/system/postgresql.service ];then
+ systemctl stop postgresql
+ systemctl disable postgresql
+ rm -rf /usr/lib/systemd/system/postgresql.service
+ rm -rf /lib/systemd/system/postgresql.service
+ systemctl daemon-reload
+ fi
+fi
+
+sh -x $curPath/versions/$VERSION/install.sh $1
+
+if [ "${action}" == "install" ] && [ -d $serverPath/postgresql ];then
+ #初始化
+ cd ${rootPath} && python3 ${rootPath}/plugins/postgresql/index.py start ${type}
+ cd ${rootPath} && python3 ${rootPath}/plugins/postgresql/index.py initd_install ${type}
+fi
diff --git a/plugins/postgresql/js/postgresql.js b/plugins/postgresql/js/postgresql.js
new file mode 100755
index 000000000..5d2edf8c6
--- /dev/null
+++ b/plugins/postgresql/js/postgresql.js
@@ -0,0 +1,1332 @@
+function str2Obj(str){
+ var data = {};
+ kv = str.split('&');
+ for(i in kv){
+ v = kv[i].split('=');
+ data[v[0]] = v[1];
+ }
+ return data;
+}
+
+function randomStr(b) {
+ b = b || 32;
+ var c = "AaBbCcDdEeFfGHhiJjKkLMmNnPpRSrTsWtXwYxZyz";
+ var a = c.length;
+ var d = "";
+ for(i = 0; i < b; i++) {
+ d += c.charAt(Math.floor(Math.random() * a))
+ }
+ return d
+}
+
+function myPost(method,args,callback, title){
+
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(str2Obj(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+
+ var _title = '正在获取...';
+ if (typeof(title) != 'undefined'){
+ _title = title;
+ }
+
+ var loadT = layer.msg(_title, { icon: 16, time: 0, shade: 0.3 });
+ $.post('/plugins/run', {name:'postgresql', func:method, args:_args}, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function myPostN(method,args,callback, title){
+
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(str2Obj(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+
+ var _title = '正在获取...';
+ if (typeof(title) != 'undefined'){
+ _title = title;
+ }
+ $.post('/plugins/run', {name:'postgresql', func:method, args:_args}, function(data) {
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function myAsyncPost(method,args){
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(str2Obj(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+ return syncPost('/plugins/run', {name:'mysql', func:method, args:_args});
+}
+
+function runInfo(){
+ myPost('run_info','',function(data){
+
+ var rdata = $.parseJSON(data.data);
+ if (typeof(rdata['status']) != 'undefined'){
+ layer.msg(rdata['msg'],{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ var con = '\
+ \
+ 启动时间 ' + rdata.uptime + ' 进程数 ' +rdata.progress_num+ ' \
+ 总连接次数 ' + rdata.connections + ' PID ' +rdata.pid+ ' \
+ 占用空间 ' + rdata.pg_size + ' 占用内存 ' +rdata.pg_mem+ ' \
+ \
+
\
+
\
+ \
+ \
+ 表进程已经锁住的物理内存的大小 ' + rdata.pg_vm_lock + ' \
+ 数据库分配到物理内存的峰值 ' + rdata.pg_vm_high + ' \
+ 进程数据段的大小 ' + rdata.pg_vm_data_size + ' \
+ 进程堆栈段的大小 ' + rdata.pg_vm_sk_size + ' \
+ 进程代码的大小 ' + rdata.pg_vm_code_size + ' \
+ 进程所使用LIB库的大小 ' + rdata.pg_vm_lib_size + ' \
+ 进程占用Swap的大小 ' + rdata.pg_vm_swap_size + ' \
+ 占用的页表的大小 ' + rdata.pg_vm_page_size + ' \
+ 当前待处理信号的个数 ' + rdata.pg_sigq + ' \
+ \
+
';
+ $(".soft-man-con").html(con);
+ });
+}
+
+
+function pgPort(){
+ myPost('pg_port','',function(data){
+ var con = '';
+ $(".soft-man-con").html(con);
+
+ $('#btn_change_port').click(function(){
+ var port = $("input[name='port']").val();
+ myPost('set_pg_port','port='+port,function(data){
+ var rdata = $.parseJSON(data.data);
+ if (rdata.status){
+ layer.msg('修改成功!',{icon:1,time:2000,shade: [0.3, '#000']});
+ } else {
+ layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']});
+ }
+ });
+ });
+ });
+}
+
+
+//数据库配置状态
+function pgPerfOpt() {
+ //获取MySQL配置
+ myPost('db_status','',function(data){
+ var rdata = $.parseJSON(data.data);
+ var html_p = '';
+ for (i in rdata){
+ if (i != 'status' ){
+ var v = rdata[i];
+ html_p += ''+i+' '+v[1] +', ' + v[2] + '
'
+ }
+ }
+
+ var memCon = ' '
+
+ $(".soft-man-con").html(memCon);
+
+ $("#pg_conf").change(function (e) {
+ e.preventDefault();
+ var data = {};
+ $('#pg_conf p input').each(function (index, element) {
+ data[$(this).attr('name')] = $(this).val() + ($(this).attr('unit') || '');
+ })
+ // console.log(data);
+ myPost('set_db_status', data, function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg,function(){
+ },{ icon: rdata.status ? 1 : 2 });
+ });
+ return false;
+ });
+ });
+}
+
+function reBootPgSqld(){
+ pluginOpService('postgresql','restart','');
+}
+
+//设置PG配置参数
+function setPgConf() {
+ return false;
+}
+
+function syncGetDatabase(){
+ myPost('sync_get_databases', null, function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg,function(){
+ dbList();
+ },{ icon: rdata.status ? 1 : 2 });
+ });
+}
+
+function syncToDatabase(type){
+ var data = [];
+ $('input[type="checkbox"].check:checked').each(function () {
+ if (!isNaN($(this).val())) data.push($(this).val());
+ });
+ var postData = 'type='+type+'&ids='+JSON.stringify(data);
+ myPost('sync_to_databases', postData, function(data){
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata);
+ showMsg(rdata.msg,function(){
+ dbList();
+ },{ icon: rdata.status ? 1 : 2 });
+ });
+}
+
+function setRootPwd(type, pwd){
+ if (type==1){
+ var data = $("#mod_pwd").serialize();
+ myPost('set_root_pwd', data, function(data){
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata);
+ showMsg(rdata.msg,function(){
+ dbList();
+ $('.layui-layer-close1').click();
+ },{icon: rdata.status ? 1 : 2});
+ });
+ return;
+ }
+
+ var index = layer.open({
+ type: 1,
+ area: '500px',
+ title: '修改数据库密码',
+ closeBtn: 1,
+ shift: 5,
+ shadeClose: true,
+ btn:["取消","提交"],
+ content: "\
+ \
+ ",
+ yes:function(){
+ setRootPwd(1);
+ }
+ });
+}
+
+function showHidePass(obj){
+ var a = "glyphicon-eye-open";
+ var b = "glyphicon-eye-close";
+
+ if($(obj).hasClass(a)){
+ $(obj).removeClass(a).addClass(b);
+ $(obj).prev().text($(obj).prev().attr('data-pw'))
+ }
+ else{
+ $(obj).removeClass(b).addClass(a);
+ $(obj).prev().text('***');
+ }
+}
+
+function checkSelect(){
+ setTimeout(function () {
+ var num = $('input[type="checkbox"].check:checked').length;
+ // console.log(num);
+ if (num == 1) {
+ $('button[batch="true"]').hide();
+ $('button[batch="false"]').show();
+ }else if (num>1){
+ $('button[batch="true"]').show();
+ $('button[batch="false"]').show();
+ }else{
+ $('button[batch="true"]').hide();
+ $('button[batch="false"]').hide();
+ }
+ },5)
+}
+
+function setDbRw(id,username,val){
+ myPost('set_db_rw',{id:id,username:username,rw:val}, function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg, function(){
+ dbList();
+ },{icon:rdata.status ? 1 : 5,shade: [0.3, '#000']}, 2000);
+ });
+}
+
+function setDbAccess(name){
+ myPost('get_db_access','name='+name, function(data){
+ var rdata = $.parseJSON(data.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:2,shade: [0.3, '#000']});
+ return;
+ }
+
+ layer.open({
+ type: 1,
+ area: '500px',
+ title: '设置数据库权限',
+ closeBtn: 1,
+ shift: 5,
+ btn:["提交","取消"],
+ shadeClose: true,
+ content: "\
+ \
+
访问权限 \
+
\
+ \
+ 本地服务器 \
+ 所有人 \
+ 指定网段 \
+ \
+
\
+
\
+ ",
+ success:function(){
+ if (rdata.msg == '127.0.0.1/32'){
+ $('select[name="dataAccess"]').find("option[value='127.0.0.1/32']").attr("selected",true);
+ } else if (rdata.msg == '0.0.0.0/0'){
+ $('select[name="dataAccess"]').find('option[value="0.0.0.0/0"]').attr("selected",true);
+ } else {
+ $('select[name="dataAccess"]').find('option[value="ip"]').attr("selected",true);
+ $('select[name="dataAccess"]').after(" ");
+ }
+
+ $('select[name="dataAccess"]').change(function(){
+ var v = $(this).val();
+ if (v == 'ip'){
+ $(this).after(" ");
+ } else {
+ $('input[name="address"]').remove()
+ }
+ });
+ },
+ yes:function(index){
+ var data = $("#set_db_access").serialize();
+ data = decodeURIComponent(data);
+ var dataObj = str2Obj(data);
+ if(!dataObj['access']){
+ dataObj['access'] = dataObj['dataAccess'];
+ if ( dataObj['dataAccess'] == 'ip'){
+ if (dataObj['address']==''){
+ layer.msg('IP地址不能空!',{icon:2,shade: [0.3, '#000']});
+ return;
+ }
+ dataObj['access'] = dataObj['address'];
+ }
+ }
+ dataObj['name'] = name;
+ // console.log(dataObj);
+ myPost('set_db_access', dataObj, function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg,function(){
+ layer.close(index);
+ dbList();
+ },{icon: rdata.status ? 1 : 2});
+ });
+ }
+ });
+
+ });
+}
+
+function setDbPass(id, username, password){
+
+ var index = layer.open({
+ type: 1,
+ area: '500px',
+ title: '修改数据库密码',
+ closeBtn: 1,
+ shift: 5,
+ shadeClose: true,
+ btn:["提交","关闭"],
+ content: "\
+ \
+ \
+ \
+ ",
+ yes:function(index){
+ var data = $("#mod_pwd").serialize();
+ myPost('set_user_pwd', data, function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg,function(){
+ layer.close(index);
+ dbList();
+ },{icon: rdata.status ? 1 : 2});
+ });
+ }
+ });
+}
+
+function addDatabase(type,layer_index){
+ if (type == 1){
+ var data = $("#add_db").serialize();
+ data = decodeURIComponent(data);
+ var dataObj = str2Obj(data);
+ if(!dataObj['address']){
+ dataObj['address'] = dataObj['dataAccess'];
+ }
+
+ var ip_segment = $('[name="dataAccess"]').val();
+ if ($('[name="dataAccess"]').val() == 'ip'){
+ dataObj['listen_ip'] = ip_segment;
+ }
+ myPost('add_db', dataObj, function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg,function(){
+ layer.close(layer_index);
+ if (rdata.status){
+ dbList();
+ }
+ },{icon: rdata.status ? 1 : 2},2000);
+ });
+ return;
+ }
+
+ layer.open({
+ type: 1,
+ area: '450px',
+ title: '添加数据库',
+ closeBtn: 1,
+ shift: 5,
+ shadeClose: true,
+ btn:["提交","取消"],
+ content: "\
+ \
+ \
+ \
+ \
+
访问权限 \
+
\
+ \
+ 本地服务器 \
+ 所有人 \
+ 指定网段 \
+ \
+ \
+
\
+
\
+ ",
+ success:function(){
+
+ $("input[name='name']").keyup(function(){
+ var v = $(this).val();
+ $("input[name='db_user']").val(v);
+ });
+
+ $('select[name="dataAccess"]').change(function(){
+ var v = $(this).val();
+ if (v == 'ip'){
+ $('input[name="ip_segment"]').show();
+ } else {
+ $('input[name="ip_segment"]').hide();
+
+ }
+ });
+ },
+ yes:function(index){
+ addDatabase(1,index);
+ }
+ });
+
+}
+
+function delDb(id, name){
+ safeMessage('删除['+name+']','您真的要删除['+name+']吗?',function(){
+ var data='id='+id+'&name='+name
+ myPost('del_db', data, function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg,function(){
+ dbList();
+ $('.layui-layer-close1').click();
+ },{icon: rdata.status ? 1 : 2}, 600);
+ });
+ });
+}
+
+function delDbBatch(){
+ var arr = [];
+ $('input[type="checkbox"].check:checked').each(function () {
+ var _val = $(this).val();
+ var _name = $(this).parent().next().text();
+ if (!isNaN(_val)) {
+ arr.push({'id':_val,'name':_name});
+ }
+ });
+
+ safeMessage('批量删除数据库','您共选择了[2]个数据库,删除后将无法恢复,真的要删除吗? ',function(){
+ var i = 0;
+ $(arr).each(function(){
+ var data = myAsyncPost('del_db', this);
+ var rdata = $.parseJSON(data.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:2,time:2000,shade: [0.3, '#000']});
+ }
+ i++;
+ });
+
+ var msg = '成功删除['+i+']个数据库!';
+ showMsg(msg,function(){
+ dbList();
+ },{icon: 1}, 600);
+ });
+}
+
+
+function setDbPs(id, name, obj) {
+ var _span = $(obj);
+ var _input = $(" ");
+ _span.hide().after(_input);
+ _input.focus();
+ _input.blur(function(){
+ $(this).remove();
+ var ps = _input.val();
+ _span.text(ps).show();
+ var data = {name:name,id:id,ps:ps};
+ myPost('set_db_ps', data, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+ });
+ _input.keyup(function(){
+ if(event.keyCode == 13){
+ _input.trigger('blur');
+ }
+ });
+}
+
+function delBackup(filename,name){
+ myPost('delete_db_backup',{filename:filename},function(){
+ layer.msg('执行成功!');
+ setTimeout(function(){
+ $('.layui-layer-close2').click();
+ setBackup(name);
+ },2000);
+ });
+}
+
+function downloadBackup(file){
+ window.open('/files/download?filename='+encodeURIComponent(file));
+}
+
+function importBackup(file,name){
+ myPost('import_db_backup',{file:file,name:name}, function(data){
+ // console.log(data);
+ layer.msg('执行成功!');
+ });
+}
+
+function setBackup(db_name,obj){
+ myPost('pg_back_list', {name:db_name}, function(data){
+
+ var rdata = $.parseJSON(data.data);
+ console.log(rdata);
+ var tbody = '';
+ for (var i = 0; i < rdata.data.length; i++) {
+ tbody += '\
+ ' + rdata.data[i]['name'] + ' \
+ ' + rdata.data[i]['size'] + ' \
+ ' + rdata.data[i]['time'] + ' \
+ \
+ 导入 | \
+ 下载 | \
+ 删除 \
+ \
+ ';
+ }
+
+ var s = layer.open({
+ type: 1,
+ title: "数据库备份详情",
+ area: ['600px', '280px'],
+ closeBtn: 2,
+ shadeClose: false,
+ content: '\
+
\
+ 备份 \
+
\
+
\
+
\
+
\
+ \
+ \
+ 文件名称 \
+ 文件大小 \
+ 备份时间 \
+ 操作 \
+ \
+ \
+ ' + tbody + ' \
+
\
+
\
+
\
+
'
+ });
+
+ $('#btn_backup').click(function(){
+ myPost('pg_back',{name:db_name}, function(data){
+ layer.msg('执行成功!');
+
+ setTimeout(function(){
+ layer.close(s);
+ setBackup(db_name,obj);
+ },2000);
+ });
+ });
+ });
+}
+
+
+function dbList(page, search){
+ var _data = {};
+ if (typeof(page) =='undefined'){
+ var page = 1;
+ }
+
+ _data['page'] = page;
+ _data['page_size'] = 10;
+ if(typeof(search) != 'undefined'){
+ _data['search'] = search;
+ }
+ myPost('get_db_list', _data, function(data){
+ var rdata = $.parseJSON(data.data);
+ var list = '';
+ for(i in rdata.data){
+ list += '';
+ list +=' ';
+ list += '' + rdata.data[i]['name'] +' ';
+ list += '' + rdata.data[i]['username'] +' ';
+ list += '' +
+ '*** ' +
+ ' '+
+ ' '+
+ ' ';
+
+
+ list += ''+rdata.data[i]['ps']+' ';
+ list += '';
+
+ list += ''+(rdata.data[i]['is_backup']?'备份':'未备份') +' | ';
+
+ var rw = '';
+ var rw_change = 'all';
+ if (typeof(rdata.data[i]['rw'])!='undefined'){
+ var rw_val = '读写';
+ if (rdata.data[i]['rw'] == 'all'){
+ rw_val = "所有";
+ rw_change = 'rw';
+ } else if (rdata.data[i]['rw'] == 'rw'){
+ rw_val = "读写";
+ rw_change = 'r';
+ } else if (rdata.data[i]['rw'] == 'r'){
+ rw_val = "只读";
+ rw_change = 'all';
+ }
+ rw = ''+rw_val+' | ';
+ }
+
+
+ list += '权限 | ' +
+ rw +
+ '改密 | ' +
+ '删除 ' +
+ ' ';
+ list += ' ';
+ }
+
+ // 回收站
+ var con = '\
+
添加数据库 \
+
PG密码 \
+
\
+ 删除选中 \
+ \
+
\
+
\
+
\
+
\
+ 从服务器获取 \
+
\
+
\
+
';
+
+ con += '\
+ \
+ \
+ \
+ \
+ \
+ ';
+
+ $(".soft-man-con").html(con);
+ $('#databasePage').html(rdata.page);
+
+ readerTableChecked();
+ });
+}
+///////////////////////////////// 主从 /////////////////////////
+
+
+function setDbMaster(name){
+ myPost('set_db_master', {name:name}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 });
+ setTimeout(function(){
+ masterOrSlaveConf();
+ }, 2000);
+ });
+}
+
+
+function setDbSlave(name){
+ myPost('set_db_slave', {name:name}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 });
+ setTimeout(function(){
+ masterOrSlaveConf();
+ }, 2000);
+ });
+}
+
+
+function repeatMSPwd(a) {
+ $("#MyPassword").val(randomStr(a))
+}
+
+function addMasterRepSlaveUser(){
+ layer.open({
+ type: 1,
+ area: '500px',
+ title: '添加同步账户',
+ closeBtn: 1,
+ shift: 5,
+ shadeClose: true,
+ btn:["提交","取消"],
+ content: "\
+ \
+ \
+
密码 \
+
\
+ \
+ \
+
\
+
\
+ \
+ \
+ ",
+ success:function(){
+ $("input[name='name']").keyup(function(){
+ var v = $(this).val();
+ $("input[name='db_user']").val(v);
+ $("input[name='ps']").val(v);
+ });
+
+ $('select[name="dataAccess"]').change(function(){
+ var v = $(this).val();
+ if (v == 'ip'){
+ $(this).after(" ");
+ } else {
+ $('#dataAccess_subid').remove();
+ }
+ });
+ },
+ yes:function(index){
+ var data = $("#add_master").serialize();
+ data = decodeURIComponent(data);
+ var dataObj = str2Obj(data);
+ if(!dataObj['address']){
+ dataObj['address'] = dataObj['dataAccess'];
+ }
+
+ myPost('add_master_rep_slave_user', dataObj, function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg,function(){
+ layer.close(index);
+ if (rdata.status){
+ getMasterRepSlaveList();
+ }
+ },{icon: rdata.status ? 1 : 2},600);
+ });
+ }
+ });
+}
+
+
+
+function updateMasterRepSlaveUser(username){
+
+ var index = layer.open({
+ type: 1,
+ area: '500px',
+ title: '更新账户',
+ closeBtn: 1,
+ shift: 5,
+ shadeClose: true,
+ content: "\
+ \
+ \
+ \
+ \
+ 提交 \
+
\
+ ",
+ });
+
+ $('#submit_update_master').click(function(){
+ var data = $("#update_master").serialize();
+ data = decodeURIComponent(data);
+ var dataObj = str2Obj(data);
+ myPost('update_master_rep_slave_user', data, function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg,function(){
+ if (rdata.status){
+ getMasterRepSlaveList();
+ }
+ $('.layui-layer-close1').click();
+ },{icon: rdata.status ? 1 : 2},600);
+ });
+ });
+}
+
+function getMasterRepSlaveUserCmd(username, db=''){
+ myPost('get_master_rep_slave_user_cmd', {username:username,db:db}, function(data){
+ var rdata = $.parseJSON(data.data);
+
+ if (!rdata['status']){
+ layer.msg(rdata['msg']);
+ return;
+ }
+
+ var cmd = rdata.data['cmd'];
+
+ var loadOpen = layer.open({
+ type: 1,
+ title: '同步命令',
+ area: '500px',
+ content:"\
+ "+cmd+"
\
+ \
+ 复制 \
+
\
+ ",
+ });
+
+
+ copyPass(cmd);
+ $('.class-copy-cmd').click(function(){
+ copyPass(cmd);
+ });
+ });
+}
+
+function delMasterRepSlaveUser(username){
+ myPost('del_master_rep_slave_user', {username:username}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg);
+
+ $('.layui-layer-close1').click();
+
+ setTimeout(function(){
+ getMasterRepSlaveList();
+ },1000);
+ });
+}
+
+function getMasterRepSlaveList(){
+ var _data = {};
+ if (typeof(page) =='undefined'){
+ var page = 1;
+ }
+
+ _data['page'] = page;
+ _data['page_size'] = 10;
+ myPost('get_master_rep_slave_list', _data, function(data){
+ // console.log(data);
+ var rdata = [];
+ try {
+ rdata = $.parseJSON(data.data);
+ } catch(e){
+ console.log(e);
+ }
+ var list = '';
+ // console.log(rdata['data']);
+ var user_list = rdata['data'];
+ for (i in user_list) {
+ // console.log(i);
+ var name = user_list[i]['username'];
+ list += ''+name+' \
+ '+user_list[i]['password']+' \
+ \
+ 删除 | \
+ 从库同步命令 \
+ \
+ ';
+ }
+
+ $('#get_master_rep_slave_list_page tbody').html(list);
+ $('.dataTables_paginate_4').html(rdata['page']);
+ });
+}
+
+function getMasterRepSlaveListPage(){
+ var page = '
';
+ page += '添加同步账户
';
+
+ var loadOpen = layer.open({
+ type: 1,
+ title: '同步账户列表',
+ area: '500px',
+ content:"",
+ success:function(){
+ getMasterRepSlaveList();
+ }
+ });
+}
+
+
+function deleteSlave(){
+ myPost('delete_slave', {}, function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata['msg'], function(){
+ masterOrSlaveConf();
+ },{},3000);
+ });
+}
+
+
+function getFullSyncStatus(db){
+ var timeId = null;
+
+ var btn = '开始
';
+ var loadOpen = layer.open({
+ type: 1,
+ title: '全量同步['+db+']',
+ area: '500px',
+ content:"",
+ cancel: function(){
+ clearInterval(timeId);
+ }
+ });
+
+ function fullSync(db,begin){
+
+ myPostN('full_sync', {db:db,begin:begin}, function(data){
+ var rdata = $.parseJSON(data.data);
+ $('#full_msg').text(rdata['msg']);
+ $('.progress-bar').css('width',rdata['progress']+'%');
+ $('.progress-bar').text(rdata['progress']+'%');
+
+ if (rdata['code']==6 ||rdata['code']<0){
+ layer.msg(rdata['msg']);
+ clearInterval(timeId);
+ $("#begin_full_sync").attr('data-status','init');
+ }
+ });
+ }
+
+ $('#begin_full_sync').click(function(){
+ var val = $(this).attr('data-status');
+ if (val == 'init'){
+ fullSync(db,1);
+ timeId = setInterval(function(){
+ fullSync(db,0);
+ }, 1000);
+ $(this).attr('data-status','starting');
+ } else {
+ layer.msg("正在同步中..");
+ }
+ });
+}
+
+function addSlaveSSH(ip=''){
+
+ myPost('get_slave_ssh_by_ip', {ip:ip}, function(rdata){
+
+ var rdata = $.parseJSON(rdata.data);
+
+ var ip = '127.0.0.1';
+ var port = "22";
+ var id_rsa = '';
+ var db_user ='';
+
+ if (rdata.data.length>0){
+ ip = rdata.data[0]['ip'];
+ port = rdata.data[0]['port'];
+ id_rsa = rdata.data[0]['id_rsa'];
+ db_user = rdata.data[0]['db_user'];
+ }
+
+ layer.open({
+ type: 1,
+ area: ['500px','450px'],
+ title: '添加SSH',
+ closeBtn: 1,
+ shift: 5,
+ shadeClose: true,
+ btn:["确认","取消"],
+ content: "\
+ \
+ \
+ \
+ \
+ ",
+ success:function(){
+ $('textarea[name="id_rsa"]').html(id_rsa);
+ },
+ yes:function(index){
+ var ip = $('input[name="ip"]').val();
+ var port = $('input[name="port"]').val();
+ var db_user = $('input[name="db_user"]').val();
+ var id_rsa = $('textarea[name="id_rsa"]').val();
+
+ var data = {ip:ip,port:port,id_rsa:id_rsa,db_user:db_user};
+ myPost('add_slave_ssh', data, function(data){
+ layer.close(index);
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg,function(){
+ if (rdata.status){
+ getSlaveSSHPage();
+ }
+ },{icon: rdata.status ? 1 : 2},600);
+ });
+ }
+ });
+ });
+}
+
+
+function delSlaveSSH(ip){
+ myPost('del_slave_ssh', {ip:ip}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
+ getSlaveSSHPage();
+ });
+}
+
+function getSlaveSSHPage(page=1){
+ var _data = {};
+ _data['page'] = page;
+ _data['page_size'] = 5;
+ _data['tojs'] ='getSlaveSSHPage';
+ myPost('get_slave_ssh_list', _data, function(data){
+ var layerId = null;
+ var rdata = [];
+ try {
+ rdata = $.parseJSON(data.data);
+ } catch(e) {
+ console.log(e);
+ }
+ var list = '';
+ var ssh_list = rdata['data'];
+ for (i in ssh_list) {
+ var ip = ssh_list[i]['ip'];
+ var port = ssh_list[i]['port'];
+
+ var id_rsa = '未设置';
+ if ( ssh_list[i]['port'] != ''){
+ id_rsa = '已设置';
+ }
+
+ var db_user = '未设置';
+ if ( ssh_list[i]['db_user'] != ''){
+ db_user = ssh_list[i]['db_user'];
+ }
+
+ list += ''+ip+' \
+ '+port+' \
+ '+id_rsa+' \
+ \
+ 修改 | \
+ 删除 \
+ \
+ ';
+ }
+
+ $('.get-slave-ssh-list tbody').html(list);
+ $('.dataTables_paginate_4').html(rdata['page']);
+ });
+}
+
+
+function getSlaveSSHList(page=1){
+
+ var page = '
';
+ page += '添加SSH
';
+
+ layerId = layer.open({
+ type: 1,
+ title: 'SSH列表',
+ area: '500px',
+ content:"",
+ success:function(){
+ getSlaveSSHPage(1);
+ }
+ });
+}
+
+function handlerRun(){
+ myPostN('get_slave_sync_cmd', {}, function(data){
+ var rdata = $.parseJSON(data.data);
+ var cmd = rdata['data'];
+ var loadOpen = layer.open({
+ type: 1,
+ title: '手动执行',
+ area: '500px',
+ content:"\
+ "+cmd+"
\
+ \
+ 复制 \
+
\
+ ",
+ });
+ copyPass(cmd);
+ $('.class-copy-cmd').click(function(){
+ copyPass(cmd);
+ });
+ });
+}
+
+function slaveSyncCmd(){
+ myPost('slave_sync_cmd', {}, function(data){
+ var rdata = $.parseJSON(data.data);
+
+ if (!rdata['status']){
+ layer.msg(rdata['msg']);
+ return;
+ }
+
+ var cmd = rdata.data['cmd'];
+
+ var loadOpen = layer.open({
+ type: 1,
+ title: '同步命令',
+ area: '500px',
+ content:"\
+ "+cmd+"
\
+ \
+ 复制 \
+
\
+ ",
+ });
+
+ copyPass(cmd);
+ $('.class-copy-cmd').click(function(){
+ copyPass(cmd);
+ });
+ });
+}
+
+function masterOrSlaveConf(version=''){
+
+ function getAsyncMasterDbList(){
+ var _data = {};
+ if (typeof(page) =='undefined'){
+ var page = 1;
+ }
+
+ _data['page'] = page;
+ _data['page_size'] = 10;
+
+ myPost('get_slave_list', _data, function(data){
+ var rdata = $.parseJSON(data.data);
+ var list = '';
+ for(i in rdata.data){
+
+ var v = rdata.data[i];
+ var status = "异常";
+ if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
+ status = "正常";
+ }
+
+ list += '';
+ list += '' + rdata.data[i]['Master_Host'] +' ';
+ list += '' + rdata.data[i]['Master_Port'] +' ';
+ list += '' + rdata.data[i]['Master_User'] +' ';
+ list += '' + rdata.data[i]['Master_Log_File'] +' ';
+ list += '' + rdata.data[i]['Slave_IO_Running'] +' ';
+ list += '' + rdata.data[i]['Slave_SQL_Running'] +' ';
+ list += '' + status +' ';
+ list += '' +
+ '删除 ' +
+ ' ';
+ list += ' ';
+ }
+
+ var con = '';
+
+ //
\
+ // \
+ // 添加 \
+ //
+ $(".table_slave_status_list").html(con);
+ });
+ }
+
+ function getMasterStatus(){
+ myPost('get_master_status', '', function(data){
+ var rdata = $.parseJSON(data.data);
+ // console.log('mode:',rdata.data);
+ var rdata = rdata.data;
+ var limitCon = '\
+ \
+ 主从同步模式 \
+ 经典 \
+
\
+ \
+ \
+ Master[主]配置 \
+ '+(!rdata.status ? '未开启' : '已开启') +' \
+ 同步账户 \
+
\
+ \
+ \
+ \
+ Slave[从]配置 \
+ '+(!rdata.slave_status ? '未启动' : '已启动') +' \
+ [主]SSH配置 \
+ 同步命令 \
+
\
+ \
+ \
+
\
+ \
+
\
+ ';
+ $(".soft-man-con").html(limitCon);
+
+ //设置主服务器配置
+ $(".btn-master").click(function () {
+ myPost('set_master_status', 'close=change', function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 });
+ setTimeout(function(){
+ getMasterStatus();
+ }, 3000);
+ });
+ });
+
+ $(".btn-slave").click(function () {
+ myPost('set_slave_status', 'close=change', function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 });
+ setTimeout(function(){
+ getMasterStatus();
+ }, 3000);
+ });
+ });
+
+ if (rdata.slave_status){
+ getAsyncMasterDbList();
+ }
+ });
+ }
+ getMasterStatus();
+}
diff --git a/plugins/postgresql/versions/14/install.sh b/plugins/postgresql/versions/14/install.sh
new file mode 100755
index 000000000..54aa854eb
--- /dev/null
+++ b/plugins/postgresql/versions/14/install.sh
@@ -0,0 +1,128 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+#https://www.postgresql.org/ftp/source/
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sysName=`uname`
+
+postgreDir=${serverPath}/source/postgresql
+
+VERSION=14.18
+
+# su - postgres -c "/www/server/postgresql/bin/pg_ctl start -D /www/server/postgresql/data"
+
+Install_App()
+{
+ mkdir -p ${postgreDir}
+ echo '正在安装脚本文件...'
+
+ if id postgres &> /dev/null ;then
+ echo "postgres uid is `id -u postgres`"
+ echo "postgres shell is `grep "^postgres:" /etc/passwd |cut -d':' -f7 `"
+ else
+ groupadd postgres
+ useradd -g postgres postgres
+ fi
+
+ if [ ! -d /home/postgres ];then
+ mkdir -p /home/postgres
+ fi
+
+ # if [ "$sysName" != "Darwin" ];then
+ # mkdir -p /var/log/mariadb
+ # touch /var/log/mariadb/mariadb.log
+ # fi
+
+ # ----- cpu start ------
+ if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+ fi
+
+ if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+ fi
+
+ MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+ if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+ else
+ cpuCore="1"
+ fi
+
+ # for stable installation
+ if [ "$cpuCore" -gt "1" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+ fi
+ # ----- cpu end ------
+
+ if [ ! -f ${postgreDir}/postgresql-${VERSION}.tar.bz2 ];then
+ wget --no-check-certificate -O ${postgreDir}/postgresql-${VERSION}.tar.bz2 --tries=3 https://ftp.postgresql.org/pub/source/v${VERSION}/postgresql-${VERSION}.tar.bz2
+ fi
+
+ if [ ! -d ${postgreDir}/postgresql-${VERSION} ];then
+ cd ${postgreDir} && tar -jxvf ${postgreDir}/postgresql-${VERSION}.tar.bz2
+ fi
+
+
+ if [ ! -d $serverPath/postgresql ];then
+ cd ${postgreDir}/postgresql-${VERSION} && ./configure \
+ --prefix=$serverPath/postgresql \
+ --with-openssl
+ # --with-pgport=33206
+
+ echo "cd ${postgreDir}/postgresql-${VERSION} && ./configure \
+ --prefix=$serverPath/postgresql \
+ --with-openssl"
+ # --with-pgport=33206
+ make -j${cpuCore} && make install && make clean
+ fi
+
+ if [ -d $serverPath/postgresql ];then
+ echo "${VERSION}" > $serverPath/postgresql/version.pl
+ echo '安装postgresql成功'
+ else
+ echo '安装postgresql失败'
+ rm -rf ${postgreDir}/postgresql-${VERSION}.tar.bz2
+ fi
+}
+
+Uninstall_App()
+{
+ if [ -f /usr/lib/systemd/system/postgresql.service ];then
+ systemctl stop postgresql
+ systemctl disable postgresql
+ rm -rf /usr/lib/systemd/system/postgresql.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f /lib/systemd/system/postgresql.service ];then
+ systemctl stop postgresql
+ systemctl disable postgresql
+ rm -rf /lib/systemd/system/postgresql.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/postgresql/initd/postgresql ];then
+ $serverPath/postgresql/initd/postgresql stop
+ fi
+
+ if [ -d $serverPath/postgresql ];then
+ rm -rf $serverPath/postgresql
+ fi
+
+ echo '卸载[postgresql]完成'
+}
+
+action=$1
+if [ "${1}" == "install" ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/postgresql/versions/15/install.sh b/plugins/postgresql/versions/15/install.sh
new file mode 100755
index 000000000..936de20ea
--- /dev/null
+++ b/plugins/postgresql/versions/15/install.sh
@@ -0,0 +1,127 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+#https://www.postgresql.org/ftp/source/
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sysName=`uname`
+
+postgreDir=${serverPath}/source/postgresql
+VERSION=15.13
+
+# su - postgres -c "/www/server/postgresql/bin/pg_ctl start -D /www/server/postgresql/data"
+
+Install_App()
+{
+ mkdir -p ${postgreDir}
+ echo '正在安装脚本文件...'
+
+ if id postgres &> /dev/null ;then
+ echo "postgres uid is `id -u postgres`"
+ echo "postgres shell is `grep "^postgres:" /etc/passwd |cut -d':' -f7 `"
+ else
+ groupadd postgres
+ useradd -g postgres postgres
+ fi
+
+ if [ ! -d /home/postgres ];then
+ mkdir -p /home/postgres
+ fi
+
+ # if [ "$sysName" != "Darwin" ];then
+ # mkdir -p /var/log/mariadb
+ # touch /var/log/mariadb/mariadb.log
+ # fi
+
+ # ----- cpu start ------
+ if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+ fi
+
+ if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+ fi
+
+ MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+ if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+ else
+ cpuCore="1"
+ fi
+
+ # for stable installation
+ if [ "$cpuCore" -gt "1" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+ fi
+ # ----- cpu end ------
+
+ if [ ! -f ${postgreDir}/postgresql-${VERSION}.tar.bz2 ];then
+ wget --no-check-certificate -O ${postgreDir}/postgresql-${VERSION}.tar.bz2 --tries=3 https://ftp.postgresql.org/pub/source/v${VERSION}/postgresql-${VERSION}.tar.bz2
+ fi
+
+ if [ ! -d ${postgreDir}/postgresql-${VERSION} ];then
+ cd ${postgreDir} && tar -jxvf ${postgreDir}/postgresql-${VERSION}.tar.bz2
+ fi
+
+
+ if [ ! -d $serverPath/postgresql ];then
+ cd ${postgreDir}/postgresql-${VERSION} && ./configure \
+ --prefix=$serverPath/postgresql \
+ --with-openssl
+ # --with-pgport=33206
+
+ echo "cd ${postgreDir}/postgresql-${VERSION} && ./configure \
+ --prefix=$serverPath/postgresql \
+ --with-openssl"
+ # --with-pgport=33206
+ make -j${cpuCore} && make install && make clean
+ fi
+
+ if [ -d $serverPath/postgresql ];then
+ echo "${VERSION}" > $serverPath/postgresql/version.pl
+ echo '安装postgresql成功'
+ else
+ echo '安装postgresql失败'
+ rm -rf ${postgreDir}/postgresql-${VERSION}.tar.bz2
+ fi
+}
+
+Uninstall_App()
+{
+ if [ -f /usr/lib/systemd/system/postgresql.service ];then
+ systemctl stop postgresql
+ systemctl disable postgresql
+ rm -rf /usr/lib/systemd/system/postgresql.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f /lib/systemd/system/postgresql.service ];then
+ systemctl stop postgresql
+ systemctl disable postgresql
+ rm -rf /lib/systemd/system/postgresql.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/postgresql/initd/postgresql ];then
+ $serverPath/postgresql/initd/postgresql stop
+ fi
+
+ if [ -d $serverPath/postgresql ];then
+ rm -rf $serverPath/postgresql
+ fi
+
+ echo '卸载[postgresql]完成'
+}
+
+action=$1
+if [ "${1}" == "install" ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/postgresql/versions/16/install.sh b/plugins/postgresql/versions/16/install.sh
new file mode 100755
index 000000000..fea8b3986
--- /dev/null
+++ b/plugins/postgresql/versions/16/install.sh
@@ -0,0 +1,128 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+#https://www.postgresql.org/ftp/source/
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sysName=`uname`
+
+postgreDir=${serverPath}/source/postgresql
+
+VERSION=16.9
+
+# su - postgres -c "/www/server/postgresql/bin/pg_ctl start -D /www/server/postgresql/data"
+
+Install_App()
+{
+ mkdir -p ${postgreDir}
+ echo '正在安装脚本文件...'
+
+ if id postgres &> /dev/null ;then
+ echo "postgres uid is `id -u postgres`"
+ echo "postgres shell is `grep "^postgres:" /etc/passwd |cut -d':' -f7 `"
+ else
+ groupadd postgres
+ useradd -g postgres postgres
+ fi
+
+ if [ ! -d /home/postgres ];then
+ mkdir -p /home/postgres
+ fi
+
+ # if [ "$sysName" != "Darwin" ];then
+ # mkdir -p /var/log/mariadb
+ # touch /var/log/mariadb/mariadb.log
+ # fi
+
+ # ----- cpu start ------
+ if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+ fi
+
+ if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+ fi
+
+ MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+ if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+ else
+ cpuCore="1"
+ fi
+
+ # for stable installation
+ if [ "$cpuCore" -gt "1" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+ fi
+ # ----- cpu end ------
+
+ if [ ! -f ${postgreDir}/postgresql-${VERSION}.tar.bz2 ];then
+ wget --no-check-certificate -O ${postgreDir}/postgresql-${VERSION}.tar.bz2 --tries=3 https://ftp.postgresql.org/pub/source/v${VERSION}/postgresql-${VERSION}.tar.bz2
+ fi
+
+ if [ ! -d ${postgreDir}/postgresql-${VERSION} ];then
+ cd ${postgreDir} && tar -jxvf ${postgreDir}/postgresql-${VERSION}.tar.bz2
+ fi
+
+
+ if [ ! -d $serverPath/postgresql ];then
+ cd ${postgreDir}/postgresql-${VERSION} && ./configure \
+ --prefix=$serverPath/postgresql \
+ --with-openssl
+ # --with-pgport=33206
+
+ echo "cd ${postgreDir}/postgresql-${VERSION} && ./configure \
+ --prefix=$serverPath/postgresql \
+ --with-openssl"
+ # --with-pgport=33206
+ make -j${cpuCore} && make install && make clean
+ fi
+
+ if [ -d $serverPath/postgresql ];then
+ echo "${VERSION}" > $serverPath/postgresql/version.pl
+ echo '安装postgresql成功'
+ else
+ echo '安装postgresql失败'
+ rm -rf ${postgreDir}/postgresql-${VERSION}.tar.bz2
+ fi
+}
+
+Uninstall_App()
+{
+ if [ -f /usr/lib/systemd/system/postgresql.service ];then
+ systemctl stop postgresql
+ systemctl disable postgresql
+ rm -rf /usr/lib/systemd/system/postgresql.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f /lib/systemd/system/postgresql.service ];then
+ systemctl stop postgresql
+ systemctl disable postgresql
+ rm -rf /lib/systemd/system/postgresql.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/postgresql/initd/postgresql ];then
+ $serverPath/postgresql/initd/postgresql stop
+ fi
+
+ if [ -d $serverPath/postgresql ];then
+ rm -rf $serverPath/postgresql
+ fi
+
+ echo '卸载[postgresql]完成'
+}
+
+action=$1
+if [ "${1}" == "install" ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/postgresql/versions/17/install.sh b/plugins/postgresql/versions/17/install.sh
new file mode 100755
index 000000000..b9c9de79a
--- /dev/null
+++ b/plugins/postgresql/versions/17/install.sh
@@ -0,0 +1,128 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+#https://www.postgresql.org/ftp/source/
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sysName=`uname`
+
+postgreDir=${serverPath}/source/postgresql
+
+VERSION=17.5
+
+# su - postgres -c "/www/server/postgresql/bin/pg_ctl start -D /www/server/postgresql/data"
+
+Install_App()
+{
+ mkdir -p ${postgreDir}
+ echo '正在安装脚本文件...'
+
+ if id postgres &> /dev/null ;then
+ echo "postgres uid is `id -u postgres`"
+ echo "postgres shell is `grep "^postgres:" /etc/passwd |cut -d':' -f7 `"
+ else
+ groupadd postgres
+ useradd -g postgres postgres
+ fi
+
+ if [ ! -d /home/postgres ];then
+ mkdir -p /home/postgres
+ fi
+
+ # if [ "$sysName" != "Darwin" ];then
+ # mkdir -p /var/log/mariadb
+ # touch /var/log/mariadb/mariadb.log
+ # fi
+
+ # ----- cpu start ------
+ if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+ fi
+
+ if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+ fi
+
+ MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+ if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+ else
+ cpuCore="1"
+ fi
+
+ # for stable installation
+ if [ "$cpuCore" -gt "1" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+ fi
+ # ----- cpu end ------
+
+ if [ ! -f ${postgreDir}/postgresql-${VERSION}.tar.bz2 ];then
+ wget --no-check-certificate -O ${postgreDir}/postgresql-${VERSION}.tar.bz2 --tries=3 https://ftp.postgresql.org/pub/source/v${VERSION}/postgresql-${VERSION}.tar.bz2
+ fi
+
+ if [ ! -d ${postgreDir}/postgresql-${VERSION} ];then
+ cd ${postgreDir} && tar -jxvf ${postgreDir}/postgresql-${VERSION}.tar.bz2
+ fi
+
+
+ if [ ! -d $serverPath/postgresql ];then
+ cd ${postgreDir}/postgresql-${VERSION} && ./configure \
+ --prefix=$serverPath/postgresql \
+ --with-openssl
+ # --with-pgport=33206
+
+ echo "cd ${postgreDir}/postgresql-${VERSION} && ./configure \
+ --prefix=$serverPath/postgresql \
+ --with-openssl"
+ # --with-pgport=33206
+ make -j${cpuCore} && make install && make clean
+ fi
+
+ if [ -d $serverPath/postgresql ];then
+ echo "${VERSION}" > $serverPath/postgresql/version.pl
+ echo '安装postgresql成功'
+ else
+ echo '安装postgresql失败'
+ rm -rf ${postgreDir}/postgresql-${VERSION}.tar.bz2
+ fi
+}
+
+Uninstall_App()
+{
+ if [ -f /usr/lib/systemd/system/postgresql.service ];then
+ systemctl stop postgresql
+ systemctl disable postgresql
+ rm -rf /usr/lib/systemd/system/postgresql.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f /lib/systemd/system/postgresql.service ];then
+ systemctl stop postgresql
+ systemctl disable postgresql
+ rm -rf /lib/systemd/system/postgresql.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/postgresql/initd/postgresql ];then
+ $serverPath/postgresql/initd/postgresql stop
+ fi
+
+ if [ -d $serverPath/postgresql ];then
+ rm -rf $serverPath/postgresql
+ fi
+
+ echo '卸载[postgresql]完成'
+}
+
+action=$1
+if [ "${1}" == "install" ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/postgresql/versions/18/install.sh b/plugins/postgresql/versions/18/install.sh
new file mode 100755
index 000000000..e0d484cc1
--- /dev/null
+++ b/plugins/postgresql/versions/18/install.sh
@@ -0,0 +1,128 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+#https://www.postgresql.org/ftp/source/
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sysName=`uname`
+
+postgreDir=${serverPath}/source/postgresql
+
+VERSION=18.3
+
+# su - postgres -c "/www/server/postgresql/bin/pg_ctl start -D /www/server/postgresql/data"
+
+Install_App()
+{
+ mkdir -p ${postgreDir}
+ echo '正在安装脚本文件...'
+
+ if id postgres &> /dev/null ;then
+ echo "postgres uid is `id -u postgres`"
+ echo "postgres shell is `grep "^postgres:" /etc/passwd |cut -d':' -f7 `"
+ else
+ groupadd postgres
+ useradd -g postgres postgres
+ fi
+
+ if [ ! -d /home/postgres ];then
+ mkdir -p /home/postgres
+ fi
+
+ # if [ "$sysName" != "Darwin" ];then
+ # mkdir -p /var/log/mariadb
+ # touch /var/log/mariadb/mariadb.log
+ # fi
+
+ # ----- cpu start ------
+ if [ -z "${cpuCore}" ]; then
+ cpuCore="1"
+ fi
+
+ if [ -f /proc/cpuinfo ];then
+ cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
+ fi
+
+ MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
+ if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
+ if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
+ cpuCore="${MEM_INFO}"
+ fi
+ else
+ cpuCore="1"
+ fi
+
+ # for stable installation
+ if [ "$cpuCore" -gt "1" ];then
+ cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
+ fi
+ # ----- cpu end ------
+
+ if [ ! -f ${postgreDir}/postgresql-${VERSION}.tar.bz2 ];then
+ wget --no-check-certificate -O ${postgreDir}/postgresql-${VERSION}.tar.bz2 --tries=3 https://ftp.postgresql.org/pub/source/v${VERSION}/postgresql-${VERSION}.tar.bz2
+ fi
+
+ if [ ! -d ${postgreDir}/postgresql-${VERSION} ];then
+ cd ${postgreDir} && tar -jxvf ${postgreDir}/postgresql-${VERSION}.tar.bz2
+ fi
+
+
+ if [ ! -d $serverPath/postgresql ];then
+ cd ${postgreDir}/postgresql-${VERSION} && ./configure \
+ --prefix=$serverPath/postgresql \
+ --with-openssl
+ # --with-pgport=33206
+
+ echo "cd ${postgreDir}/postgresql-${VERSION} && ./configure \
+ --prefix=$serverPath/postgresql \
+ --with-openssl"
+ # --with-pgport=33206
+ make -j${cpuCore} && make install && make clean
+ fi
+
+ if [ -d $serverPath/postgresql ];then
+ echo "${VERSION}" > $serverPath/postgresql/version.pl
+ echo '安装postgresql成功'
+ else
+ echo '安装postgresql失败'
+ rm -rf ${postgreDir}/postgresql-${VERSION}.tar.bz2
+ fi
+}
+
+Uninstall_App()
+{
+ if [ -f /usr/lib/systemd/system/postgresql.service ];then
+ systemctl stop postgresql
+ systemctl disable postgresql
+ rm -rf /usr/lib/systemd/system/postgresql.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f /lib/systemd/system/postgresql.service ];then
+ systemctl stop postgresql
+ systemctl disable postgresql
+ rm -rf /lib/systemd/system/postgresql.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/postgresql/initd/postgresql ];then
+ $serverPath/postgresql/initd/postgresql stop
+ fi
+
+ if [ -d $serverPath/postgresql ];then
+ rm -rf $serverPath/postgresql
+ fi
+
+ echo '卸载[postgresql]完成'
+}
+
+action=$1
+if [ "${1}" == "install" ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/prometheus/ico.png b/plugins/prometheus/ico.png
new file mode 100644
index 000000000..3ed0b3ba0
Binary files /dev/null and b/plugins/prometheus/ico.png differ
diff --git a/plugins/prometheus/index.html b/plugins/prometheus/index.html
new file mode 100755
index 000000000..4cada1a7c
--- /dev/null
+++ b/plugins/prometheus/index.html
@@ -0,0 +1,30 @@
+
+
+
+
\ No newline at end of file
diff --git a/plugins/prometheus/index.py b/plugins/prometheus/index.py
new file mode 100755
index 000000000..bbf0e9db0
--- /dev/null
+++ b/plugins/prometheus/index.py
@@ -0,0 +1,230 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+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 'prometheus'
+
+
+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 getConf():
+ path = getServerDir() + "/prometheus.yml"
+ return path
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
+ return path
+
+
+def getArgs():
+ args = sys.argv[3:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ if t.strip() == '':
+ tmp = []
+ else:
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ 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 getPidFile():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'pidfile\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+def status():
+ cmd = "ps aux|grep prometheus |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'"
+ data = mw.execShell(cmd)
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+def getInstallVerion():
+ version_pl = getServerDir() + "/version.pl"
+ version = mw.readFile(version_pl).strip()
+ return version
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ return content
+
+
+def openPort():
+ try:
+ from utils.firewall import Firewall as MwFirewall
+ MwFirewall.instance().addAcceptPort('3000', 'prometheus', 'port')
+ return port
+ except Exception as e:
+ return "Release failed {}".format(e)
+ return True
+
+
+def initDreplace():
+ # 初始化OP配置
+ init_file = getServerDir() + '/init.pl'
+ if not os.path.exists(init_file):
+ # openPort()
+ mw.writeFile(init_file, 'ok')
+
+ # systemd
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/' + getPluginName() + '.service'
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl'
+ service_path = mw.getServerDir()
+ content = mw.readFile(systemServiceTpl)
+ content = content.replace('{$SERVER_PATH}', service_path)
+ mw.writeFile(systemService, content)
+ mw.execShell('systemctl daemon-reload')
+
+ return True
+
+
+def gOp(method):
+ initDreplace()
+
+ data = mw.execShell('systemctl ' + method + ' '+getPluginName())
+ mw.execShell('systemctl ' + method + ' '+getPluginName())
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def start():
+ return gOp('start')
+
+def stop():
+ return gOp('stop')
+
+def restart():
+ return gOp('restart')
+
+def reload():
+ return gOp('reload')
+
+def initdStatus():
+ current_os = mw.getOs()
+ if current_os == 'darwin':
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status prometheus|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"
+
+ data = mw.execShell('systemctl enable prometheus')
+ if data[1] != '':
+ return data[1]
+ return 'ok'
+
+
+def initdUinstall():
+ current_os = mw.getOs()
+ if current_os == 'darwin':
+ return "Apple Computer does not support"
+
+ data = mw.execShell('systemctl disable prometheus')
+ if data[1] != '':
+ return data[1]
+ return 'ok'
+
+def prometheusUrl():
+ ip = mw.getLocalIp()
+ return 'http://'+ip+':'+"9090"
+
+def installPreInspection():
+ return 'ok'
+
+
+def uninstallPreInspection():
+ return 'ok'
+
+
+if __name__ == "__main__":
+ 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 == 'uninstall_pre_inspection':
+ print(uninstallPreInspection())
+ elif func == 'conf':
+ print(getConf())
+ elif func == 'prometheus_url':
+ print(prometheusUrl())
+ else:
+ print('error')
diff --git a/plugins/prometheus/info.json b/plugins/prometheus/info.json
new file mode 100755
index 000000000..01fd38a09
--- /dev/null
+++ b/plugins/prometheus/info.json
@@ -0,0 +1,17 @@
+{
+ "sort": 7,
+ "ps": "监控系统与时间序列数据库",
+ "name": "prometheus",
+ "title": "prometheus",
+ "shell": "install.sh",
+ "versions":["3.5.0"],
+ "tip": "soft",
+ "checks": "server/prometheus",
+ "path": "server/prometheus",
+ "display": 1,
+ "author": "midoks",
+ "date": "2025-08-02",
+ "home": "https://prometheus.io/download/",
+ "type": 0,
+ "pid": "5"
+}
diff --git a/plugins/prometheus/init.d/prometheus.service.tpl b/plugins/prometheus/init.d/prometheus.service.tpl
new file mode 100644
index 000000000..c84941fb8
--- /dev/null
+++ b/plugins/prometheus/init.d/prometheus.service.tpl
@@ -0,0 +1,18 @@
+[Unit]
+Description=Prometheus
+Documentation=https://prometheus.io/docs/introduction/overview/
+Wants=network-online.target
+After=network-online.target
+[Service]
+Type=simple
+ExecReload=/bin/kill -HUP $MAINPID
+ExecStart={$SERVER_PATH}/prometheus/prometheus \
+ --config.file={$SERVER_PATH}/prometheus/prometheus.yml \
+ --storage.tsdb.path={$SERVER_PATH}/prometheus/data \
+ --web.console.templates={$SERVER_PATH}/prometheus/consoles \
+ --web.console.libraries={$SERVER_PATH}/prometheus/console_libraries \
+ --web.listen-address=0.0.0.0:9090
+SyslogIdentifier=prometheus
+Restart=always
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/plugins/prometheus/install.sh b/plugins/prometheus/install.sh
new file mode 100755
index 000000000..c321da9aa
--- /dev/null
+++ b/plugins/prometheus/install.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# https://www.cnblogs.com/n00dle/p/16916044.html
+# cd /www/server/mdserver-web/plugins/prometheus && /bin/bash install.sh install 3.5.0
+# cd /www/server/mdserver-web && python3 /www/server/mdserver-web/plugins/prometheus/index.py start
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+VERSION=$2
+
+sysArch=`arch`
+sysName=`uname`
+echo "use system: ${sysName}"
+
+OSNAME=`bash ${rootPath}/scripts/getos.sh`
+if [ "" == "$OSNAME" ];then
+ OSNAME=`cat ${rootPath}/data/osname.pl`
+fi
+
+if [ "macos" == "$OSNAME" ];then
+ echo "不支持Macox"
+ exit
+fi
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+# if id prometheus &> /dev/null ;then
+# echo "prometheus uid is `id -u prometheus`"
+# echo "prometheus shell is `grep "^prometheus:" /etc/passwd |cut -d':' -f7 `"
+# else
+# groupadd prometheus
+# useradd -g prometheus -s /bin/bash prometheus
+# fi
+
+Install_App()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/source/prometheus
+
+ mkdir -p $serverPath/prometheus
+ echo "${VERSION}" > $serverPath/prometheus/version.pl
+
+ shell_file=${curPath}/versions/${VERSION}/linux.sh
+
+ if [ -f $shell_file ];then
+ bash -x $shell_file install ${VERSION}
+ else
+ echo '不支持...'
+ exit 1
+ fi
+
+ #初始化
+ cd ${rootPath} && python3 ${rootPath}/plugins/prometheus/index.py start
+ cd ${rootPath} && python3 ${rootPath}/plugins/prometheus/index.py initd_install
+
+ echo 'Prometheus安装完成'
+}
+
+Uninstall_App()
+{
+ shell_file=${curPath}/versions/${VERSION}/linux.sh
+ if [ -f $shell_file ];then
+ bash -x $shell_file uninstall ${VERSION}
+ fi
+
+ cd ${rootPath} && python3 ${rootPath}/plugins/prometheus/index.py stop
+ cd ${rootPath} && python3 ${rootPath}/plugins/prometheus/index.py initd_uninstall
+
+ rm -rf $serverPath/prometheus
+ echo 'Prometheus卸载完成'
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/prometheus/js/prometheus.js b/plugins/prometheus/js/prometheus.js
new file mode 100755
index 000000000..c9c9218c8
--- /dev/null
+++ b/plugins/prometheus/js/prometheus.js
@@ -0,0 +1,96 @@
+function gPost(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'prometheus';
+ req_data['func'] = method;
+ req_data['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ //错误展示10S
+ layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function gPostCallbak(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'prometheus';
+ req_data['func'] = method;
+ args['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function gCommonFunc(){
+ var con = '\
+ 获取连接地址 \
+
';
+
+ $(".soft-man-con").html(con);
+
+ $('#prometheus_url').click(function(){
+ gPost('prometheus_url', '', {}, function(rdata){
+ layer.open({
+ title: "prometheus连接",
+ area: ['600px', '180px'],
+ type:1,
+ closeBtn: 1,
+ shadeClose: false,
+ btn:["复制","取消"],
+ content: '',
+ success:function(){
+ copyText(rdata.data);
+ },
+ yes:function(){
+ copyText(rdata.data);
+ }
+ });
+ });
+ });
+}
+
+
+function gReadme(){
+ var readme = '';
+ readme += 'https://prometheus.io/download ';
+ readme += ' ';
+
+ $('.soft-man-con').html(readme);
+}
+
diff --git a/plugins/prometheus/versions/3.5.0/linux.sh b/plugins/prometheus/versions/3.5.0/linux.sh
new file mode 100644
index 000000000..79fc102eb
--- /dev/null
+++ b/plugins/prometheus/versions/3.5.0/linux.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+VERSION=$2
+
+sysArch=`arch`
+sysName=`uname`
+
+ARCH_NAME=amd64
+if [ "$sysArch" == "arm64" ];then
+ ARCH_NAME=arm64
+elif [ "$sysArch" == "x86_64" ]; then
+ ARCH_NAME=amd64
+elif [ "$sysArch" == "aarch64" ]; then
+ ARCH_NAME=aarch64
+fi
+
+FILE_TGZ=prometheus-${VERSION}.linux-${ARCH_NAME}.tar.gz
+
+# 检查是否通
+Install_App()
+{
+ SourceDir=$serverPath/source/prometheus
+ InstallDir=$serverPath/prometheus
+ mkdir -p ${SourceDir}
+ mkdir -p ${InstallDir}/bin
+
+ if [ ! -f ${SourceDir}/${FILE_TGZ} ];then
+ wget --no-check-certificate -O ${SourceDir}/${FILE_TGZ} https://github.com/prometheus/prometheus/releases/download/v${VERSION}/${FILE_TGZ}
+ fi
+
+
+
+ if [ ! -d $InstallDir/bin/prometheus ];then
+ cd ${SourceDir} && tar -zxvf ${FILE_TGZ}
+ cd ${SourceDir}/prometheus-${VERSION}.linux-${ARCH_NAME}
+ cp -rf ./* $InstallDir
+ fi
+}
+
+Uninstall_App()
+{
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/pureftp/conf/ftps.sql b/plugins/pureftp/conf/ftps.sql
new file mode 100755
index 000000000..9db502897
--- /dev/null
+++ b/plugins/pureftp/conf/ftps.sql
@@ -0,0 +1,10 @@
+CREATE TABLE IF NOT EXISTS `ftps` (
+ `id` INTEGER PRIMARY KEY AUTOINCREMENT,
+ `pid` INTEGER,
+ `name` TEXT,
+ `password` TEXT,
+ `path` TEXT,
+ `status` TEXT,
+ `ps` TEXT,
+ `addtime` TEXT
+);
\ No newline at end of file
diff --git a/plugins/pureftp/conf/pure-ftpd.conf b/plugins/pureftp/conf/pure-ftpd.conf
new file mode 100644
index 000000000..d130c8aeb
--- /dev/null
+++ b/plugins/pureftp/conf/pure-ftpd.conf
@@ -0,0 +1,32 @@
+ChrootEveryone yes
+BrokenClientsCompatibility no
+MaxClientsNumber 50
+Daemonize yes
+MaxClientsPerIP 8
+VerboseLog no
+DisplayDotFiles yes
+AnonymousOnly no
+NoAnonymous no
+SyslogFacility ftp
+DontResolve yes
+MaxIdleTime 15
+LimitRecursion 10000 8
+AnonymousCanCreateDirs no
+MaxLoad 4
+AntiWarez yes
+Bind 0.0.0.0,21
+Umask 133:022
+MinUID 100
+PassivePortRange 39000 40000
+AllowUserFXP no
+AllowAnonymousFXP no
+ProhibitDotFilesWrite no
+ProhibitDotFilesRead no
+AutoRename no
+AnonymousCantUpload no
+MaxDiskUsage 99
+CustomerProof yes
+PIDFile {$SERVER_PATH}/pureftp/etc/pure-ftpd.pid
+PureDB {$SERVER_PATH}/pureftp/etc/pureftpd.pdb
+
+VerboseLog yes
\ No newline at end of file
diff --git a/plugins/pureftp/ico.png b/plugins/pureftp/ico.png
new file mode 100644
index 000000000..bd64461a3
Binary files /dev/null and b/plugins/pureftp/ico.png differ
diff --git a/plugins/pureftp/index.html b/plugins/pureftp/index.html
new file mode 100755
index 000000000..c3f111311
--- /dev/null
+++ b/plugins/pureftp/index.html
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/plugins/pureftp/index.py b/plugins/pureftp/index.py
new file mode 100755
index 000000000..0dc6a00e3
--- /dev/null
+++ b/plugins/pureftp/index.py
@@ -0,0 +1,491 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import shutil
+
+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 'pureftp'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getInitDFile():
+ if app_debug:
+ return '/tmp/' + getPluginName()
+ return '/etc/init.d/' + getPluginName()
+
+
+def getConf():
+ path = getServerDir() + "/etc/pure-ftpd.conf"
+ return path
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/pure-ftpd.tpl"
+ return path
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+
+ 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 status():
+ cmd = "ps -ef|grep pure-ftpd |grep -v grep | grep -v python | awk '{print $2}'"
+ data = mw.execShell(cmd)
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ return content
+
+
+def ftp_release_port(port):
+ from collections import namedtuple
+ try:
+ from utils.firewall import Firewall as MwFirewall
+ MwFirewall.instance().addAcceptPort(port, 'pure-ftpd', 'port')
+ return port
+ except Exception as e:
+ return "Release failed {}".format(e)
+
+
+def openFtpPort():
+ for i in ["21", "39000:40000"]:
+ ftp_release_port(i)
+ return True
+
+
+def initDreplace():
+
+ file_tpl = getInitDTpl()
+ service_path = mw.getServerDir()
+
+ initD_path = getServerDir() + '/init.d'
+ if not os.path.exists(initD_path):
+ os.mkdir(initD_path)
+ openFtpPort()
+ file_bin = initD_path + '/' + getPluginName()
+
+ # initd replace
+ if not os.path.exists(file_bin):
+ content = mw.readFile(file_tpl)
+ content = contentReplace(content)
+ mw.writeFile(file_bin, content)
+ mw.execShell('chmod +x ' + file_bin)
+
+ pureSbinConfig = getServerDir() + "/sbin/pure-config.pl"
+ if not os.path.exists(pureSbinConfig):
+ pureTplConfig = getPluginDir() + "/init.d/pure-config.pl"
+ content = mw.readFile(pureTplConfig)
+ content = contentReplace(content)
+ mw.writeFile(pureSbinConfig, content)
+ mw.execShell('chmod +x ' + pureSbinConfig)
+
+ pureFtpdConfig = getServerDir() + "/etc/pure-ftpd.conf"
+ pureFtpdConfigBak = getServerDir() + "/etc/pure-ftpd.bak.conf"
+ pureFtpdConfigTpl = getPluginDir() + "/conf/pure-ftpd.conf"
+
+ if not os.path.exists(pureFtpdConfigBak) or not os.path.exists(pureFtpdConfig):
+ if os.path.exists(pureFtpdConfig):
+ shutil.copyfile(pureFtpdConfig, pureFtpdConfigBak)
+ content = mw.readFile(pureFtpdConfigTpl)
+ content = contentReplace(content)
+ mw.writeFile(pureFtpdConfig, content)
+
+ # systemd
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/pureftp.service'
+ systemServiceTpl = getPluginDir() + '/init.d/pureftp.service.tpl'
+
+ if os.path.exists(systemDir):
+ content = mw.readFile(systemServiceTpl)
+ content = content.replace('{$SERVER_PATH}', service_path)
+ mw.writeFile(systemService, content)
+ mw.execShell('systemctl daemon-reload')
+
+ return file_bin
+
+
+def pfOp(method):
+ file = initDreplace()
+
+ if not mw.isAppleSystem():
+ data = mw.execShell('systemctl ' + method + ' pureftp')
+ if data[1] == '':
+ return 'ok'
+ return 'fail'
+
+ data = mw.execShell(file + ' ' + method)
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def start():
+ return pfOp('start')
+
+
+def stop():
+ return pfOp('stop')
+
+
+def restart():
+ return pfOp('restart')
+
+
+def reload():
+ return pfOp('reload')
+
+
+def initdStatus():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status pureftp | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+
+def initdInstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl enable pureftp')
+ return 'ok'
+
+
+def initdUinstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl disable pureftp')
+ return 'ok'
+
+
+def pftpDB():
+ file = getServerDir() + '/ftps.db'
+ if not os.path.exists(file):
+ conn = mw.M('ftps').dbPos(getServerDir(), 'ftps')
+ csql = mw.readFile(getPluginDir() + '/conf/ftps.sql')
+ csql_list = csql.split(';')
+ for index in range(len(csql_list)):
+ conn.execute(csql_list[index], ())
+ else:
+ conn = mw.M('ftps').dbPos(getServerDir(), 'ftps')
+ return conn
+
+
+def pftpUser():
+ if mw.isAppleSystem():
+ user = mw.execShell(
+ "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
+ return user
+ return 'www'
+
+
+def pftpAdd(username, password, path):
+ user = pftpUser()
+
+ if not os.path.exists(path):
+ os.makedirs(path)
+ if mw.isAppleSystem():
+ # pass
+ os.system('chown ' + user + '.staff ' + path)
+ else:
+ os.system('chown www.www ' + path)
+
+ cmd = getServerDir() + '/bin/pure-pw useradd ' + username + ' -u ' + user + ' -d ' + \
+ path + '< 65535:
+ return '端口范围不正确!'
+ file = file = getServerDir() + '/etc/pure-ftpd.conf'
+ conf = mw.readFile(file)
+ rep = r"\n#?\s*Bind\s+[0-9]+\.[0-9]+\.[0-9]+\.+[0-9]+,([0-9]+)"
+ # preg_match(rep,conf,tmp)
+ conf = re.sub(rep, "\nBind 0.0.0.0," + port, conf)
+ mw.writeFile(file, conf)
+ restart()
+ return 'ok'
+ except Exception as ex:
+ return str(ex)
+
+
+def stopPort():
+ args = getArgs()
+ if not 'id' in args:
+ return 'id missing'
+
+ if not 'username' in args:
+ return 'username missing'
+
+ if not 'status' in args:
+ return 'status missing'
+
+ data = pftpStop(args['username'])
+ pftpReload()
+ conn = pftpDB()
+ conn.where('id=?', (int(args['id']),)).save(
+ 'status', (args['status'],))
+
+ if data[1] == '':
+ return 'ok'
+ return data[0]
+
+
+def startPort():
+ args = getArgs()
+ if not 'id' in args:
+ return 'id missing'
+
+ if not 'username' in args:
+ return 'username missing'
+
+ if not 'status' in args:
+ return 'status missing'
+
+ data = pftpStart(args['username'])
+ pftpReload()
+ conn = pftpDB()
+ conn.where('id=?', (int(args['id']),)).save(
+ 'status', (args['status'],))
+
+ if data[1] == '':
+ return 'ok'
+ return data[0]
+
+
+if __name__ == "__main__":
+ 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 == 'conf':
+ print(getConf())
+ elif func == 'get_www_dir':
+ print(getWwwDir())
+ elif func == 'get_ftp_list':
+ print(getFtpList())
+ elif func == 'add_ftp':
+ print(addFtp())
+ elif func == 'del_ftp':
+ print(delFtp())
+ elif func == 'mod_ftp':
+ print(modFtp())
+ elif func == 'mod_ftp_port':
+ print(modFtpPort())
+ elif func == 'stop_ftp':
+ print(stopPort())
+ elif func == 'start_ftp':
+ print(startPort())
+ else:
+ print('error')
diff --git a/plugins/pureftp/info.json b/plugins/pureftp/info.json
new file mode 100755
index 000000000..6130a9cb1
--- /dev/null
+++ b/plugins/pureftp/info.json
@@ -0,0 +1,15 @@
+{
+ "title":"PureFtpd",
+ "tip":"soft",
+ "name":"pureftp",
+ "ps":"一款免费FTP服务器软件",
+ "versions": "1.0.49",
+ "shell":"install.sh",
+ "checks":"server/pureftp",
+ "path":"server/pureftp",
+ "author":"mdserver-web",
+ "home":"https://github.com/midoks/mdserver-web",
+ "date":"2018-11-30",
+ "dev_update":"2022-6-18",
+ "pid":"3"
+}
\ No newline at end of file
diff --git a/plugins/pureftp/init.d/pure-config.pl b/plugins/pureftp/init.d/pure-config.pl
new file mode 100644
index 000000000..7d722b7a6
--- /dev/null
+++ b/plugins/pureftp/init.d/pure-config.pl
@@ -0,0 +1,128 @@
+#! /usr/bin/perl
+
+# (C) 2001-2013 Aristotle Pagaltzis
+# derived from code (C) 2001-2002 Frank Denis and Matthias Andree
+
+use strict;
+
+my ($conffile, @flg) = @ARGV;
+
+my $PUREFTPD;
+-x && ($PUREFTPD=$_, last) for qw(
+ {$SERVER_PATH}/pureftp/sbin/pure-ftpd
+ /www/server/pureftp/sbin/pure-ftpd
+ /www/server/pureftpd/sbin/pure-ftpd
+ /www/server/sbin/pure-ftpd
+ /usr/sbin/pure-ftpd
+);
+
+my %simple_switch_for = (
+ IPV4Only => "-4",
+ IPV6Only => "-6",
+ ChrootEveryone => "-A",
+ BrokenClientsCompatibility => "-b",
+ Daemonize => "-B",
+ VerboseLog => "-d",
+ DisplayDotFiles => "-D",
+ AnonymousOnly => "-e",
+ NoAnonymous => "-E",
+ DontResolve => "-H",
+ AnonymousCanCreateDirs => "-M",
+ NATmode => "-N",
+ CallUploadScript => "-o",
+ AntiWarez => "-s",
+ AllowUserFXP => "-w",
+ AllowAnonymousFXP => "-W",
+ ProhibitDotFilesWrite => "-x",
+ ProhibitDotFilesRead => "-X",
+ AllowDotFiles => "-z",
+ AutoRename => "-r",
+ AnonymousCantUpload => "-i",
+ LogPID => "-1",
+ NoChmod => "-R",
+ KeepAllFiles => "-K",
+ CreateHomeDir => "-j",
+ NoRename => "-G",
+ CustomerProof => "-Z",
+ NoTruncate => "-0",
+);
+
+my %string_switch_for = (
+ FileSystemCharset => "-8",
+ ClientCharset => "-9",
+ SyslogFacility => "-f",
+ FortunesFile => "-F",
+ ForcePassiveIP => "-P",
+ Bind => "-S",
+ AnonymousBandwidth => "-t",
+ UserBandwidth => "-T",
+ TrustedIP => "-V",
+ AltLog => "-O",
+ PIDFile => "-g",
+ TLSCipherSuite => "-J",
+ CertFile => "-2",
+);
+
+my %numeric_switch_for = (
+ MaxIdleTime => "-I",
+ MaxDiskUsage => "-k",
+ TrustedGID => "-a",
+ MaxClientsNumber => "-c",
+ MaxClientsPerIP => "-C",
+ MaxLoad => "-m",
+ MinUID => "-u",
+ TLS => "-Y",
+);
+
+my %numpairb_switch_for = (
+ LimitRecursion => "-L",
+ PassivePortRange => "-p",
+ AnonymousRatio => "-q",
+ UserRatio => "-Q",
+);
+
+my %numpairc_switch_for = (
+ Umask => "-U",
+ Quota => "-n",
+ PerUserLimits => "-y",
+);
+
+my %auth_method_for = (
+ LDAPConfigFile => "ldap",
+ MySQLConfigFile => "mysql",
+ PGSQLConfigFile => "pgsql",
+ PureDB => "puredb",
+ ExtAuth => "extauth",
+);
+
+my $simple_switch = qr/(@{[join "|", keys %simple_switch_for ]})\s+yes/i;
+my $string_switch = qr/(@{[join "|", keys %string_switch_for ]})\s+(\S+)/i;
+my $numeric_switch = qr/(@{[join "|", keys %numeric_switch_for ]})\s+(\d+)/i;
+my $numpairb_switch = qr/(@{[join "|", keys %numpairb_switch_for ]})\s+(\d+)\s+(\d+)/i;
+my $numpairc_switch = qr/(@{[join "|", keys %numpairc_switch_for ]})\s+(\d+):(\d+)/i;
+my $auth_method = qr/(@{[join "|", keys %auth_method_for ]})\s+(\S+)/i;
+
+die "Usage: pure-config.pl [extra options]\n"
+ unless defined $conffile;
+
+open CONF, "< $conffile" or die "Can't open $conffile: $!\n";
+
+!/^\s*(?:$|#)/ and (chomp, push @flg,
+ /$simple_switch/i ? ($simple_switch_for{$1}) :
+ /$string_switch/i ? ($string_switch_for{$1} . $2) :
+ /$numeric_switch/i ? ($numeric_switch_for{$1} . $2) :
+ /$numpairb_switch/i ? ($numpairb_switch_for{$1} . "$2:$3") :
+ /$numpairc_switch/i ? ($numpairc_switch_for{$1} . "$2:$3") :
+ /$auth_method/i ? ("-l" . "$auth_method_for{$1}:$2") :
+ /UnixAuthentication\s+yes/i ? ("-l" . "unix") :
+ /PAMAuthentication\s+yes/i ? ("-l" . "pam") :
+ ()
+) while ;
+
+close CONF;
+
+if (-t STDOUT) {
+ print "Running: $PUREFTPD ", join(" ", @flg), "\n";
+}
+exec { $PUREFTPD } ($PUREFTPD, @flg) or die "cannot exec $PUREFTPD: $!";
+
diff --git a/plugins/pureftp/init.d/pure-ftpd.tpl b/plugins/pureftp/init.d/pure-ftpd.tpl
new file mode 100644
index 000000000..af51ca29d
--- /dev/null
+++ b/plugins/pureftp/init.d/pure-ftpd.tpl
@@ -0,0 +1,78 @@
+#!/bin/bash
+#
+# chkconfig: 2345 85 15
+# description: Pure-FTPd is an FTP server daemon based upon Troll-FTPd
+# processname: pure-ftpd
+
+### BEGIN INIT INFO
+# Provides: pureftpd
+# Required-Start: $all
+# Required-Stop: $all
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts pureftpd server
+# Description: starts pureftpd server
+### END INIT INFO
+
+# Pure-FTPd Settings
+PURE_PERL="{$SERVER_PATH}/pureftp/sbin/pure-config.pl"
+PURE_CONF="{$SERVER_PATH}/pureftp/etc/pure-ftpd.conf"
+PURE_PID="{$SERVER_PATH}/pureftp/etc/pure-ftpd.pid"
+RETVAL=0
+prog="Pure-FTPd"
+
+start() {
+ echo -n $"Starting $prog... "
+ $PURE_PERL $PURE_CONF --daemonize
+ if [ "$?" = 0 ] ; then
+ echo " done"
+ else
+ echo " failed"
+ fi
+}
+
+stop() {
+ echo -n $"Stopping $prog... "
+ if [ ! -e $PURE_PID ]; then
+ echo -n $"$prog is not running."
+ exit 1
+ fi
+ kill `cat $PURE_PID`
+ if [ "$?" = 0 ] ; then
+ echo " done"
+ else
+ echo " failed"
+ fi
+}
+
+restart(){
+ echo $"Restarting $prog..."
+ $0 stop
+ sleep 2
+ $0 start
+}
+
+status(){
+ if [ -e $PURE_PID ]; then
+ echo $"$prog is running."
+ else
+ echo $"$prog is not running."
+ fi
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ restart
+ ;;
+ status)
+ status
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|restart}"
+esac
diff --git a/plugins/pureftp/init.d/pureftp.service.tpl b/plugins/pureftp/init.d/pureftp.service.tpl
new file mode 100644
index 000000000..7d632eae8
--- /dev/null
+++ b/plugins/pureftp/init.d/pureftp.service.tpl
@@ -0,0 +1,15 @@
+[Unit]
+Description=Pure-FTPd is a fast, production-quality, standard-conformant FTP server
+After=network.target
+
+
+
+[Service]
+Type=forking
+ExecStart={$SERVER_PATH}/pureftp/sbin/pure-ftpd {$SERVER_PATH}/pureftp/etc/pure-ftpd.conf
+ExecStop=/bin/kill -HUP $MAINPID
+ExecReload=/bin/kill -USR2 $MAINPID
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/plugins/pureftp/install.sh b/plugins/pureftp/install.sh
new file mode 100755
index 000000000..874c46668
--- /dev/null
+++ b/plugins/pureftp/install.sh
@@ -0,0 +1,137 @@
+#!/bin/bash
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+sysName=`uname`
+echo "use system: ${sysName}"
+
+if [ ${sysName} == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eqi "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release; then
+ OSNAME='centos'
+elif grep -Eqi "Fedora" /etc/issue || grep -Eq "Fedora" /etc/*-release; then
+ OSNAME='fedora'
+elif grep -Eqi "Debian" /etc/issue || grep -Eq "Debian" /etc/*-release; then
+ OSNAME='debian'
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/*-release; then
+ OSNAME='ubuntu'
+elif grep -Eqi "Raspbian" /etc/issue || grep -Eq "Raspbian" /etc/*-release; then
+ OSNAME='raspbian'
+else
+ OSNAME='unknow'
+fi
+
+
+
+
+Install_pureftp()
+{
+ if id ftp &> /dev/null ;then
+ echo "ftp UID is `id -u ftp`"
+ echo "ftp Shell is `grep "^ftp:" /etc/passwd |cut -d':' -f7 `"
+ else
+ groupadd ftp
+ useradd -g ftp -s /sbin/nologin ftp
+ fi
+
+ # mkdir -p ${serverPath}/pureftp
+ mkdir -p ${serverPath}/source/pureftp
+
+ # https://github.com/jedisct1/pure-ftpd/releases/download/1.0.49/pure-ftpd-1.0.49.tar.gz
+ # https://download.pureftpd.org/pub/pure-ftpd/releases/pure-ftpd-1.0.49.tar.gz
+
+
+ VER=$1
+ DOWNLOAD=https://github.com/jedisct1/pure-ftpd/releases/download/${VER}/pure-ftpd-${VER}.tar.gz
+ # DOWNLOAD=https://download.pureftpd.org/pub/pure-ftpd/releases/pure-ftpd-${VER}.tar.gz
+
+ # curl -sSLo pure-ftpd-1.0.49.tar.gz https://download.pureftpd.org/pub/pure-ftpd/releases/pure-ftpd-1.0.49.tar.gz
+ if [ ! -f $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz ];then
+ # wget --no-check-certificate -O $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz $DOWNLOAD
+ curl -sSLo $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz $DOWNLOAD
+ fi
+
+ #检测文件是否损坏.
+ md5_ok=451879495ba61c1d7dcfca8dd231119f
+ if [ -f $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz ];then
+ md5_check=`md5sum $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz | awk '{print $1}'`
+ if [ "${md5_ok}" == "${md5_check}" ]; then
+ echo "pure-ftpd file check ok"
+ fi
+ fi
+
+ # Last Download Method
+ if [ ! -f $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz ];then
+ wget --no-check-certificate -O $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz https://dl.midoks.icu/soft/ftp/pure-ftpd-${VER}.tar.gz -T 3
+ fi
+
+ if [ ! -d $serverPath/source/pureftp/pure-ftpd-${VER} ];then
+ cd $serverPath/source/pureftp && tar zxvf pure-ftpd-${VER}.tar.gz
+ fi
+
+ cd $serverPath/source/pureftp/pure-ftpd-${VER} && ./configure --prefix=${serverPath}/pureftp \
+ CFLAGS=-O2 \
+ --with-puredb \
+ --with-quotas \
+ --with-cookie \
+ --with-virtualhosts \
+ --with-diraliases \
+ --with-sysquotas \
+ --with-ratios \
+ --with-altlog \
+ --with-paranoidmsg \
+ --with-shadow \
+ --with-welcomemsg \
+ --with-throttling \
+ --with-uploadscript \
+ --with-language=english \
+ --with-rfc2640 \
+ --with-ftpwho \
+ --with-tls && make && make install && make clean
+
+ if [ -d ${serverPath}/pureftp ];then
+ echo "${1}" > ${serverPath}/pureftp/version.pl
+ echo '安装完成'
+
+ cd ${rootPath} && python3 ${rootPath}/plugins/pureftp/index.py start
+ cd ${rootPath} && python3 ${rootPath}/plugins/pureftp/index.py initd_install
+ else
+ echo '安装失败'
+ fi
+}
+
+Uninstall_pureftp()
+{
+ if [ -f /usr/lib/systemd/system/pureftp.service ];then
+ systemctl stop pureftp
+ systemctl disable pureftp
+ rm -rf /usr/lib/systemd/system/pureftp.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/pureftp/initd/pureftp ];then
+ $serverPath/pureftp/initd/pureftp stop
+ fi
+
+ rm -rf ${serverPath}/pureftp
+ userdel ftp
+ groupdel ftp
+ echo '卸载完成'
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_pureftp $2
+else
+ Uninstall_pureftp $2
+fi
diff --git a/plugins/pureftp/js/ftp.js b/plugins/pureftp/js/ftp.js
new file mode 100755
index 000000000..0ddee424a
--- /dev/null
+++ b/plugins/pureftp/js/ftp.js
@@ -0,0 +1,308 @@
+
+function ftpPost(method,args,callback){
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(toArrayObject(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+ $.post('/plugins/run', {name:'pureftp', func:method, args:_args}, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+function ftpAsyncPost(method,args){
+ return syncPost('/plugins/run',
+ {name:'pureftp', func:method, args:JSON.stringify(args)}
+ );
+}
+
+function ftpListFind(){
+ var search = $('#ftp_find_user').val();
+ if (search==''){
+ layer.msg('搜索字符不能为空!',{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ ftpList(1, search);
+}
+
+function ftpList(page, search){
+ var _data = {};
+ if (typeof(page) =='undefined'){
+ var page = 1;
+ }
+
+ _data['page'] = page;
+ _data['page_size'] = 10;
+ if(typeof(search) != 'undefined'){
+ _data['search'] = search;
+ }
+
+ ftpPost('get_ftp_list', _data, function(data){
+
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata);
+ content = ' 当前FTP地址为:ftp://'+rdata['info']['ip']+':'+rdata['info']['port']+'
';
+ content += ' ';
+ content += '查找
';
+
+ content += '';
+ content += '';
+ content += '用户名 ';
+ content += '密码 ';
+ content += '状态 ';
+ content += '根目录 ';
+ content += '备注 ';
+ content += '操作(添加 |端口 ) ';
+ content += ' ';
+
+ content += '';
+
+ ulist = rdata.data;
+ for (i in ulist){
+ // console.log(ulist[i]);
+ status = '已停用 ';
+ if (ulist[i]['status'] == '1'){
+ status = '已启用 ';
+ }
+ content += ''+ulist[i]['name']+' '+
+ ''+ulist[i]['password']+' '+
+ ''+status+' ' +
+ ''+ulist[i]['path']+' ' +
+ ''+ulist[i]['ps']+' ' +
+ '改密 | ' +
+ '删除 ';
+ }
+
+ content += ' ';
+ content += '
';
+
+ page = '';
+
+ content += page;
+
+ $(".soft-man-con").html(content);
+ });
+}
+
+
+/**
+ *添加FTP帐户
+ */
+function addFtp() {
+
+ var data = ftpAsyncPost('get_www_dir');
+ var defaultPath = data.data;
+ var indexFtp = layer.open({
+ type: 1,
+ area: '500px',
+ title: '添加FTP帐户',
+ closeBtn: 2,
+ shift: 5,
+ shadeClose: false,
+ btn: ['提交','关闭'],
+ content: "\
+ \
+ \
+ \
+ \
+ ",
+ yes:function(index,layero){
+ var loadT = layer.load({shade: true,shadeClose: false});
+ var data = $("#ftpAdd").serialize();
+ ftpPost('add_ftp', data, function(rdata){
+ layer.close(loadT);
+ layer.close(indexFtp);
+ if (rdata.data == 'ok'){
+ layer.msg('添加成功!', {icon: 1,time:3000});
+ } else {
+ layer.msg(rdata.data, {icon: 5,time:3000});
+ }
+
+ setTimeout(function(){ftpList();},2000);
+ });
+ return true;
+ },
+ });
+
+ $("#ftpUser").keyup(function(){
+ var ftpName = $(this).val();
+ $("#inputPath").val(defaultPath+'/'+ftpName);
+ $("#ftp_ps").val(ftpName);
+ });
+}
+
+
+/**
+ * 删除FTP帐户
+ * @param {Number} id
+ * @param {String} ftp_username 欲被删除的用户名
+ * @return {bool}
+ */
+function ftpDelete(id,ftp_username){
+ safeMessage(lan.public.del+"["+ftp_username+"]",lan.get('confirm_del',[ftp_username]),function(){
+ layer.msg(lan.public.the_del,{icon:16,time:0,shade: [0.3, '#000']});
+ var data='&id='+id+'&username='+ftp_username;
+
+ ftpPost('del_ftp', data, function(data){
+ layer.msg('删除成功!', {icon: 1});
+ ftpList();
+ })
+ });
+}
+
+function modFtpPort(type, port){
+ var index = layer.open({
+ type: 1,
+ skin: 'demo-class',
+ area: '500px',
+ title: '修改FTP帐户端口',
+ content: "\
+ \
+ \
+ 关闭 \
+ 提交 \
+
\
+ ",
+ });
+
+ $('#ftp_port_close').click(function(){
+ $('.layui-layer-close1').click();
+ });
+
+ $('#ftp_port_submit').click(function(){
+ var port = $('#ftpPort').val();
+ data = 'port='+port
+ ftpPost('mod_ftp_port', data,function(data){
+ ftpList();
+ if (data.data == 'ok'){
+ layer.msg('修改成功!', {icon: 1});
+ } else {
+ layer.msg(data.data, {icon: 2});
+ }
+ $('.layui-layer-close1').click();
+ });
+ });
+
+}
+
+
+function ftpModPwd(id,name,password){
+ var index = layer.open({
+ type: 1,
+ skin: 'demo-class',
+ area: '500px',
+ title: '修改FTP帐户密码',
+ content: "\
+ \
+ \
+ \
+ \
+ 关闭 \
+ 提交 \
+
\
+ ",
+ });
+
+
+ $('#ftp_mod_close').click(function(){
+ $('.layui-layer-close1').click();
+ });
+
+ $('#ftp_mod_submit').click(function(){
+ pwd = $('#MyPassword').val();
+ data='id='+id+'&name='+name+'&password='+pwd
+ ftpPost('mod_ftp', data,function(data){
+ ftpList();
+ if (data.data == 'ok'){
+ layer.msg('修改成功!', {icon: 1});
+ }
+ $('.layui-layer-close1').click();
+ });
+ });
+}
+
+
+/**
+ * 停止FTP帐号
+ * @param {Number} id FTP的ID
+ * @param {String} username FTP用户名
+ */
+function ftpStop(id, username) {
+ layer.confirm('您真的要停止{1}的FTP吗?'.replace('{1}',username), {
+ title: 'FTP帐户',icon:3,
+ closeBtn:2
+ }, function(index) {
+ if (index > 0) {
+ var loadT = layer.load({shade: true,shadeClose: false});
+ var data='id=' + id + '&username=' + username + '&status=0';
+ ftpPost('stop_ftp', data, function(data){
+ layer.close(loadT);
+ if (data.data == 'ok'){
+ showMsg('启动成功!', function(){
+ ftpList();
+ },{icon: 1});
+ } else {
+ layer.msg(data.data, {icon: 2});
+ }
+ });
+ }
+ $('.layui-layer-close1').click();
+ });
+}
+
+/**
+ * 启动FTP帐号
+ * @param {Number} id FTP的ID
+ * @param {String} username FTP用户名
+ */
+function ftpStart(id, username) {
+ var loadT = layer.load({shade: true,shadeClose: false});
+ var data='id=' + id + '&username=' + username + '&status=1';
+ ftpPost('start_ftp', data, function(data){
+ layer.close(loadT);
+ if (data.data == 'ok'){
+ showMsg('启动成功!', function(){
+ ftpList();
+ },{icon: 1});
+ } else {
+ layer.msg(data.data, {icon: 2});
+ }
+
+ });
+}
+
diff --git a/plugins/redis/config/redis.conf b/plugins/redis/config/redis.conf
new file mode 100644
index 000000000..e630018a6
--- /dev/null
+++ b/plugins/redis/config/redis.conf
@@ -0,0 +1,90 @@
+daemonize yes
+pidfile {$SERVER_PATH}/redis/redis.pid
+
+bind 127.0.0.1
+port 6379
+requirepass {$REDIS_PASS}
+
+timeout 3
+tcp-keepalive 0
+
+loglevel notice
+
+logfile {$SERVER_PATH}/redis/data/redis.log
+databases 16
+
+################################ SNAPSHOTTING #################################
+
+save 900 1000
+save 300 10000
+save 60 1000000
+stop-writes-on-bgsave-error no
+rdbcompression yes
+rdbchecksum yes
+dbfilename dump.rdb
+dir {$SERVER_PATH}/redis/data/
+
+################################# REPLICATION #################################
+
+slave-serve-stale-data yes
+slave-read-only yes
+
+repl-disable-tcp-nodelay no
+slave-priority 100
+
+################################## SECURITY ###################################
+
+
+################################### LIMITS ####################################
+maxclients 10000
+#maxmemory-samples 3
+maxmemory 218mb
+#maxmemory-policy volatile-ttl
+maxmemory-policy allkeys-lru
+
+############################## APPEND ONLY MODE ###############################
+
+# appendonly no
+
+# appendfsync always
+# appendfsync everysec
+# appendfsync no
+
+# appendfilename "appendonly.aof"
+
+no-appendfsync-on-rewrite no
+auto-aof-rewrite-percentage 100
+auto-aof-rewrite-min-size 64mb
+
+################################ LUA SCRIPTING ###############################
+
+lua-time-limit 5000
+
+################################## SLOW LOG ###################################
+
+
+slowlog-log-slower-than 10000
+slowlog-max-len 128
+
+############################### ADVANCED CONFIG ###############################
+
+hash-max-ziplist-entries 512
+hash-max-ziplist-value 64
+
+list-max-ziplist-entries 512
+list-max-ziplist-value 64
+
+set-max-intset-entries 512
+
+zset-max-ziplist-entries 128
+zset-max-ziplist-value 64
+
+activerehashing yes
+
+client-output-buffer-limit normal 0 0 0
+client-output-buffer-limit slave 256mb 64mb 60
+client-output-buffer-limit pubsub 32mb 8mb 60
+
+hz 10
+
+aof-rewrite-incremental-fsync yes
\ No newline at end of file
diff --git a/plugins/redis/ico.png b/plugins/redis/ico.png
new file mode 100755
index 000000000..804758d8e
Binary files /dev/null and b/plugins/redis/ico.png differ
diff --git a/plugins/redis/index.html b/plugins/redis/index.html
new file mode 100755
index 000000000..3b3944cf5
--- /dev/null
+++ b/plugins/redis/index.html
@@ -0,0 +1,36 @@
+
+
+
+
\ No newline at end of file
diff --git a/plugins/redis/index.py b/plugins/redis/index.py
new file mode 100755
index 000000000..08d2dbba0
--- /dev/null
+++ b/plugins/redis/index.py
@@ -0,0 +1,574 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+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 'redis'
+
+
+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 getConf():
+ path = getServerDir() + "/redis.conf"
+ return path
+
+
+def getConfTpl():
+ path = getPluginDir() + "/config/redis.conf"
+ return path
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
+ return path
+
+
+def getArgs():
+ args = sys.argv[3:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ if t.strip() == '':
+ tmp = []
+ else:
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ 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 configTpl():
+ path = getPluginDir() + '/tpl'
+ pathFile = os.listdir(path)
+ tmp = []
+ for one in pathFile:
+ file = path + '/' + one
+ tmp.append(file)
+ return mw.getJson(tmp)
+
+
+def readConfigTpl():
+ args = getArgs()
+ data = checkArgs(args, ['file'])
+ if not data[0]:
+ return data[1]
+
+ content = mw.readFile(args['file'])
+ content = contentReplace(content)
+ return mw.returnJson(True, 'ok', content)
+
+def getPidFile():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'pidfile\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+def status():
+ pid_file = getPidFile()
+ if not os.path.exists(pid_file):
+ return 'stop'
+
+ # data = mw.execShell(
+ # "ps aux|grep redis |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'")
+
+ # if data[0] == '':
+ # return 'stop'
+ return 'start'
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName())
+ content = content.replace('{$REDIS_PASS}', mw.getRandomString(10))
+ return content
+
+
+
+def initDreplace():
+
+ file_tpl = getInitDTpl()
+ service_path = mw.getServerDir()
+
+ initD_path = getServerDir() + '/init.d'
+ if not os.path.exists(initD_path):
+ os.mkdir(initD_path)
+ file_bin = initD_path + '/' + getPluginName()
+
+ # initd replace
+ if not os.path.exists(file_bin):
+ content = mw.readFile(file_tpl)
+ content = content.replace('{$SERVER_PATH}', service_path)
+ mw.writeFile(file_bin, content)
+ mw.execShell('chmod +x ' + file_bin)
+
+ # log
+ dataLog = getServerDir() + '/data'
+ if not os.path.exists(dataLog):
+ mw.execShell('mkdir -p ' + dataLog)
+ mw.execShell('chmod +x ' + file_bin)
+
+ # config replace
+ dst_conf = getConf()
+ dst_conf_init = getServerDir() + '/init.pl'
+ if not os.path.exists(dst_conf_init):
+ conf_content = mw.readFile(getConfTpl())
+ conf_content = conf_content.replace('{$SERVER_PATH}', service_path)
+ conf_content = conf_content.replace('{$REDIS_PASS}', mw.getRandomString(10))
+
+ mw.writeFile(dst_conf, conf_content)
+ mw.writeFile(dst_conf_init, 'ok')
+
+ # systemd
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/' + getPluginName() + '.service'
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl'
+ service_path = mw.getServerDir()
+ content = mw.readFile(systemServiceTpl)
+ content = content.replace('{$SERVER_PATH}', service_path)
+ mw.writeFile(systemService, content)
+ mw.execShell('systemctl daemon-reload')
+
+ return file_bin
+
+
+def redisOp(method):
+ file = initDreplace()
+
+ 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"):
+ data = mw.execShell('service ' + getPluginName() + ' ' + method)
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+ data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def start():
+ return redisOp('start')
+
+
+def stop():
+ return redisOp('stop')
+
+
+def restart():
+ status = redisOp('restart')
+
+ log_file = runLog()
+ mw.execShell("echo '' > " + log_file)
+ return status
+
+
+def reload():
+ return redisOp('reload')
+
+
+def getPort():
+ conf = getConf()
+ content = mw.readFile(conf)
+
+ rep = r"^(port)\s*([.0-9A-Za-z_& ~]+)"
+ tmp = re.search(rep, content, re.M)
+ if tmp:
+ return tmp.groups()[1]
+
+ return '6379'
+
+
+def getRedisCmd():
+ requirepass = ""
+ conf = getConf()
+ content = mw.readFile(conf)
+ rep = r"^(requirepass)\s*([.0-9A-Za-z_& ~]+)"
+ tmp = re.search(rep, content, re.M)
+ if tmp:
+ requirepass = tmp.groups()[1]
+
+ default_ip = '127.0.0.1'
+ port = getPort()
+ # findDebian = mw.execShell('cat /etc/issue |grep Debian')
+ # if findDebian[0] != '':
+ # default_ip = mw.getLocalIp()
+ cmd = getServerDir() + "/bin/redis-cli -h " + default_ip + ' -p ' + port + " "
+
+ if requirepass != "":
+ cmd = getServerDir() + '/bin/redis-cli -h ' + default_ip + ' -p ' + port + ' -a "' + requirepass + '" '
+
+ return cmd
+
+def runInfo():
+ s = status()
+ if s == 'stop':
+ return mw.returnJson(False, '未启动')
+
+
+ cmd = getRedisCmd()
+ cmd = cmd + 'info'
+
+ # print(cmd)
+ data = mw.execShell(cmd)[0]
+ # print(data)
+ res = [
+ 'tcp_port',
+ 'uptime_in_days', # 已运行天数
+ 'connected_clients', # 连接的客户端数量
+ 'used_memory', # Redis已分配的内存总量
+ 'used_memory_rss', # Redis占用的系统内存总量
+ 'used_memory_peak', # Redis所用内存的高峰值
+ 'mem_fragmentation_ratio', # 内存碎片比率
+ 'total_connections_received', # 运行以来连接过的客户端的总数量
+ 'total_commands_processed', # 运行以来执行过的命令的总数量
+ 'instantaneous_ops_per_sec', # 服务器每秒钟执行的命令数量
+ 'keyspace_hits', # 查找数据库键成功的次数
+ 'keyspace_misses', # 查找数据库键失败的次数
+ 'latest_fork_usec' # 最近一次 fork() 操作耗费的毫秒数
+ ]
+ data = data.split("\n")
+ result = {}
+ for d in data:
+ if len(d) < 3:
+ continue
+ t = d.strip().split(':')
+ if not t[0] in res:
+ continue
+ result[t[0]] = t[1]
+ return mw.getJson(result)
+
+def infoReplication():
+ # 复制信息
+ s = status()
+ if s == 'stop':
+ return mw.returnJson(False, '未启动')
+
+ cmd = getRedisCmd()
+ cmd = cmd + 'info replication'
+
+ # print(cmd)
+ data = mw.execShell(cmd)[0]
+ # print(data)
+ res = [
+ #slave
+ 'role',#角色
+ 'master_host', # 连接主库HOST
+ 'master_port', # 连接主库PORT
+ 'master_link_status', # 连接主库状态
+ 'master_last_io_seconds_ago', # 上次同步时间
+ 'master_sync_in_progress', # 正在同步中
+ 'slave_read_repl_offset', # 从库读取复制位置
+ 'slave_repl_offset', # 从库复制位置
+ 'slave_priority', # 从库同步优先级
+ 'slave_read_only', # 从库是否仅读
+ 'replica_announced', # 已复制副本
+ 'connected_slaves', # 连接从库数量
+ 'master_failover_state', # 主库故障状态
+ 'master_replid', # 主库复制ID
+ 'master_repl_offset', # 主库复制位置
+ 'second_repl_offset', # 主库复制位置时间
+ 'repl_backlog_active', # 复制状态
+ 'repl_backlog_size', # 复制大小
+ 'repl_backlog_first_byte_offset', # 第一个字节偏移量
+ 'repl_backlog_histlen', # backlog中数据的长度
+ ]
+
+ data = data.split("\n")
+ result = {}
+ for d in data:
+ if len(d) < 3:
+ continue
+ t = d.strip().split(':')
+ if not t[0] in res:
+ continue
+ result[t[0]] = t[1]
+
+ if 'role' in result and result['role'] == 'master':
+ connected_slaves = int(result['connected_slaves'])
+ slave_l = []
+ for x in range(connected_slaves):
+ slave_l.append('slave'+str(x))
+
+ for d in data:
+ if len(d) < 3:
+ continue
+ t = d.strip().split(':')
+ if not t[0] in slave_l:
+ continue
+ result[t[0]] = t[1]
+
+ return mw.getJson(result)
+
+
+def clusterInfo():
+ #集群信息
+ # https://redis.io/commands/cluster-info/
+ s = status()
+ if s == 'stop':
+ return mw.returnJson(False, '未启动')
+
+ cmd = getRedisCmd()
+ cmd = cmd + 'cluster info'
+
+ # print(cmd)
+ data = mw.execShell(cmd)[0]
+ # print(data)
+
+ res = [
+ 'cluster_state',#状态
+ 'cluster_slots_assigned', # 被分配的槽
+ 'cluster_slots_ok', # 被分配的槽状态
+ 'cluster_slots_pfail', # 连接主库状态
+ 'cluster_slots_fail', # 失败的槽
+ 'cluster_known_nodes', # 知道的节点
+ 'cluster_size', # 大小
+ 'cluster_current_epoch', #
+ 'cluster_my_epoch', #
+ 'cluster_stats_messages_sent', # 发送
+ 'cluster_stats_messages_received', # 接受
+ 'total_cluster_links_buffer_limit_exceeded', #
+ ]
+
+ data = data.split("\n")
+ result = {}
+ for d in data:
+ if len(d) < 3:
+ continue
+ t = d.strip().split(':')
+ if not t[0] in res:
+ continue
+ result[t[0]] = t[1]
+
+ return mw.getJson(result)
+
+def clusterNodes():
+ s = status()
+ if s == 'stop':
+ return mw.returnJson(False, '未启动')
+
+ cmd = getRedisCmd()
+ cmd = cmd + 'cluster nodes'
+
+ # print(cmd)
+ data = mw.execShell(cmd)[0]
+ # print(data)
+
+ data = data.strip().split("\n")
+ return mw.getJson(data)
+
+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 ' + \
+ getPluginName() + ' | 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 ' + getPluginName() + '_enable="YES"')
+ return 'ok'
+
+ mw.execShell('systemctl enable ' + getPluginName())
+ 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 ' + getPluginName() + '_enable="NO"')
+ return 'ok'
+
+ mw.execShell('systemctl disable ' + getPluginName())
+ return 'ok'
+
+
+def runLog():
+ return getServerDir() + '/data/redis.log'
+
+
+def getRedisConfInfo():
+ conf = getConf()
+
+ gets = [
+ {'name': 'bind', 'type': 2, 'ps': '绑定IP(修改绑定IP可能会存在安全隐患)','must_show':1},
+ {'name': 'port', 'type': 2, 'ps': '绑定端口','must_show':1},
+ {'name': 'timeout', 'type': 2, 'ps': '空闲链接超时时间,0表示不断开','must_show':1},
+ {'name': 'maxclients', 'type': 2, 'ps': '最大连接数','must_show':1},
+ {'name': 'databases', 'type': 2, 'ps': '数据库数量','must_show':1},
+ {'name': 'requirepass', 'type': 2, 'ps': 'redis密码,留空代表没有设置密码','must_show':1},
+ {'name': 'maxmemory', 'type': 2, 'ps': 'MB,最大使用内存,0表示不限制','must_show':1},
+ {'name': 'slaveof', 'type': 2, 'ps': '同步主库地址','must_show':0},
+ {'name': 'masterauth', 'type': 2, 'ps': '同步主库密码', 'must_show':0}
+ ]
+ content = mw.readFile(conf)
+
+ result = []
+ for g in gets:
+ rep = r"^(" + g['name'] + r')\s*([.0-9A-Za-z_& ~]+)'
+ tmp = re.search(rep, content, re.M)
+ if not tmp:
+ if g['must_show'] == 0:
+ continue
+
+ g['value'] = ''
+ result.append(g)
+ continue
+ g['value'] = tmp.groups()[1]
+ if g['name'] == 'maxmemory':
+ g['value'] = g['value'].strip("mb")
+ result.append(g)
+
+ return result
+
+
+def getRedisConf():
+ data = getRedisConfInfo()
+ return mw.getJson(data)
+
+
+def submitRedisConf():
+ gets = ['bind', 'port', 'timeout', 'maxclients',
+ 'databases', 'requirepass', 'maxmemory','slaveof','masterauth']
+ args = getArgs()
+ conf = getConf()
+ content = mw.readFile(conf)
+ for g in gets:
+ if g in args:
+ rep = g + r'\s*([.0-9A-Za-z_& ~]+)'
+ val = g + ' ' + args[g]
+
+ if g == 'maxmemory':
+ val = g + ' ' + args[g] + "mb"
+
+ if g == 'requirepass' and args[g] == '':
+ content = re.sub('requirepass', '#requirepass', content)
+ if g == 'requirepass' and args[g] != '':
+ content = re.sub('#requirepass', 'requirepass', content)
+ content = re.sub(rep, val, content)
+
+ if g != 'requirepass':
+ content = re.sub(rep, val, content)
+ mw.writeFile(conf, content)
+ reload()
+ return mw.returnJson(True, '设置成功')
+
+if __name__ == "__main__":
+ 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 == 'run_info':
+ print(runInfo())
+ elif func == 'info_replication':
+ print(infoReplication())
+ elif func == 'cluster_info':
+ print(clusterInfo())
+ elif func == 'cluster_nodes':
+ print(clusterNodes())
+ elif func == 'conf':
+ print(getConf())
+ elif func == 'run_log':
+ print(runLog())
+ elif func == 'get_redis_conf':
+ print(getRedisConf())
+ elif func == 'submit_redis_conf':
+ print(submitRedisConf())
+ elif func == 'config_tpl':
+ print(configTpl())
+ elif func == 'read_config_tpl':
+ print(readConfigTpl())
+ else:
+ print('error')
diff --git a/plugins/redis/info.json b/plugins/redis/info.json
new file mode 100755
index 000000000..76b9a9a39
--- /dev/null
+++ b/plugins/redis/info.json
@@ -0,0 +1,17 @@
+{
+ "sort":4,
+ "ps": "一个高性能的KV数据库",
+ "name": "redis",
+ "title": "Redis",
+ "shell": "install.sh",
+ "versions":["4.0.14","5.0.14","6.0.20","6.2.16","7.0.15","7.2.9", "7.4.7", "8.0.5", "8.2.3"],
+ "tip": "soft",
+ "checks": "server/redis",
+ "path": "server/redis",
+ "display": 1,
+ "author": "redis",
+ "date": "2017-04-01",
+ "home": "https://redis.io",
+ "type": 0,
+ "pid": "2"
+}
diff --git a/plugins/redis/init.d/redis.service.tpl b/plugins/redis/init.d/redis.service.tpl
new file mode 100644
index 000000000..8e8eb5693
--- /dev/null
+++ b/plugins/redis/init.d/redis.service.tpl
@@ -0,0 +1,12 @@
+[Unit]
+Description=Redis In-Memory Data Store
+After=network.target
+
+[Service]
+Type=forking
+ExecStart={$SERVER_PATH}/redis/bin/redis-server {$SERVER_PATH}/redis/redis.conf
+ExecReload=/bin/kill -USR2 $MAINPID
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/plugins/redis/init.d/redis.tpl b/plugins/redis/init.d/redis.tpl
new file mode 100644
index 000000000..fb4f76e46
--- /dev/null
+++ b/plugins/redis/init.d/redis.tpl
@@ -0,0 +1,77 @@
+#!/bin/sh
+# chkconfig: 2345 55 25
+# description: Redis Service
+
+### BEGIN INIT INFO
+# Provides: Redis
+# Required-Start: $all
+# Required-Stop: $all
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts Redis
+# Description: starts the MDW-Web
+### END INIT INFO
+
+# Simple Redis init.d script conceived to work on Linux systems
+# as it does use of the /proc filesystem.
+
+CONF="{$SERVER_PATH}/redis/redis.conf"
+REDISPORT=$(cat $CONF |grep port|grep -v '#'|awk '{print $2}')
+REDISPASS=$(cat $CONF |grep requirepass|grep -v '#'|awk '{print $2}')
+if [ "$REDISPASS" != "" ];then
+ REDISPASS=" -a $REDISPASS"
+fi
+EXEC={$SERVER_PATH}/redis/bin/redis-server
+CLIEXEC="{$SERVER_PATH}/redis/bin/redis-cli -p $REDISPORT$REDISPASS"
+PIDFILE={$SERVER_PATH}/redis/redis.pid
+
+echo $REDISPASS
+echo $REDISPORT
+echo $CLIEXEC
+
+mkdir -p {$SERVER_PATH}/redis/data
+
+redis_start(){
+ if [ -f $PIDFILE ];then
+ kill -9 `cat $PIDFILE`
+ fi
+
+ echo "Starting Redis server..."
+ nohup $EXEC $CONF >> {$SERVER_PATH}/redis/logs.pl 2>&1 &
+}
+redis_stop(){
+ if [ ! -f $PIDFILE ]
+ then
+ echo "$PIDFILE does not exist, process is not running"
+ else
+ PID=$(cat $PIDFILE)
+ echo "Stopping ..."
+ $CLIEXEC shutdown save 2>/dev/null
+ while [ -x /proc/${PID} ]
+ do
+ echo "Waiting for Redis to shutdown ..."
+ sleep 1
+ done
+ echo "Redis stopped"
+ rm -rf $PIDFILE
+ fi
+}
+
+
+case "$1" in
+ start)
+ redis_start
+ ;;
+ stop)
+ redis_stop
+ ;;
+ restart|reload)
+ redis_stop
+ sleep 0.3
+ redis_start
+ ;;
+ *)
+ echo "Please use start or stop as first argument"
+ ;;
+esac
+
diff --git a/plugins/redis/install.sh b/plugins/redis/install.sh
new file mode 100755
index 000000000..681e88717
--- /dev/null
+++ b/plugins/redis/install.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+# https://www.cnblogs.com/zlonger/p/16177595.html
+# https://www.cnblogs.com/BNTang/articles/15841688.html
+
+# ps -ef|grep redis |grep -v grep | awk '{print $2}' | xargs kill
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/redis && bash install.sh install 7.2.2
+
+# cmd查看| info replication
+# /Users/midoks/Desktop/mwdev/server/redis/bin/redis-cli -h 127.0.0.1 -p 6399
+# /www/server/redis/bin/redis-cli -h 127.0.0.1 -p 6399
+
+VERSION=$2
+
+Install_App()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/source
+ mkdir -p $serverPath/source/redis
+
+ FILE_TGZ=redis-${VERSION}.tar.gz
+ REDIS_DIR=$serverPath/source/redis
+
+ if [ ! -f $REDIS_DIR/${FILE_TGZ} ];then
+ wget --no-check-certificate -O $REDIS_DIR/${FILE_TGZ} https://download.redis.io/releases/${FILE_TGZ}
+ fi
+
+ cd $REDIS_DIR && tar -zxvf ${FILE_TGZ}
+
+ CMD_MAKE=`which gmake`
+ if [ "$?" == "0" ];then
+ cd redis-${VERSION} && gmake PREFIX=$serverPath/redis install
+ else
+ cd redis-${VERSION} && make PREFIX=$serverPath/redis install
+ fi
+
+ if [ -d $serverPath/redis ];then
+ mkdir -p $serverPath/redis/data
+ sed '/^ *#/d' redis.conf > $serverPath/redis/redis.conf
+
+ echo "${VERSION}" > $serverPath/redis/version.pl
+ echo '安装完成'
+
+ cd ${rootPath} && python3 ${rootPath}/plugins/redis/index.py start
+ cd ${rootPath} && python3 ${rootPath}/plugins/redis/index.py initd_install
+
+ else
+ echo '安装失败!'
+ fi
+
+ if [ -d ${REDIS_DIR}/redis-${VERSION} ];then
+ rm -rf ${REDIS_DIR}/redis-${VERSION}
+ fi
+}
+
+Uninstall_App()
+{
+ if [ -f /usr/lib/systemd/system/redis.service ];then
+ systemctl stop redis
+ systemctl disable redis
+ rm -rf /usr/lib/systemd/system/redis.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f /lib/systemd/system/redis.service ];then
+ systemctl stop redis
+ systemctl disable redis
+ rm -rf /lib/systemd/system/redis.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/redis/initd/redis ];then
+ $serverPath/redis/initd/redis stop
+ fi
+
+ if [ -d $serverPath/redis ];then
+ rm -rf $serverPath/redis
+ fi
+
+ echo "卸载redis成功"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/redis/js/redis.js b/plugins/redis/js/redis.js
new file mode 100755
index 000000000..1697e96aa
--- /dev/null
+++ b/plugins/redis/js/redis.js
@@ -0,0 +1,297 @@
+function redisPost(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'redis';
+ req_data['func'] = method;
+ req_data['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ //错误展示10S
+ layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function redisPostCallbak(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'redis';
+ req_data['func'] = method;
+ args['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+//redis状态 start
+function redisStatus(version) {
+
+ redisPost('run_info',version, {},function(data){
+ var rdata = $.parseJSON(data.data);
+
+ if ('status' in rdata && !rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ hit = (parseInt(rdata.keyspace_hits) / (parseInt(rdata.keyspace_hits) + parseInt(rdata.keyspace_misses)) * 100).toFixed(2);
+ var con = '\
+
\
+ 字段 当前值 说明 \
+ \
+ uptime_in_days ' + rdata.uptime_in_days + ' 已运行天数 \
+ tcp_port ' + rdata.tcp_port + ' 当前监听端口 \
+ connected_clients ' + rdata.connected_clients + ' 连接的客户端数量 \
+ used_memory_rss ' + toSize(rdata.used_memory_rss) + ' Redis当前占用的系统内存总量 \
+ used_memory ' + toSize(rdata.used_memory) + ' Redis当前已分配的内存总量 \
+ used_memory_peak ' + toSize(rdata.used_memory_peak) + ' Redis历史分配内存的峰值 \
+ mem_fragmentation_ratio ' + rdata.mem_fragmentation_ratio + '% 内存碎片比率 \
+ total_connections_received ' + rdata.total_connections_received + ' 运行以来连接过的客户端的总数量 \
+ total_commands_processed ' + rdata.total_commands_processed + ' 运行以来执行过的命令的总数量 \
+ instantaneous_ops_per_sec ' + rdata.instantaneous_ops_per_sec + ' 服务器每秒钟执行的命令数量 \
+ keyspace_hits ' + rdata.keyspace_hits + ' 查找数据库键成功的次数 \
+ keyspace_misses ' + rdata.keyspace_misses + ' 查找数据库键失败的次数 \
+ hit ' + hit + '% 查找数据库键命中率 \
+ latest_fork_usec ' + rdata.latest_fork_usec + ' 最近一次 fork() 操作耗费的微秒数 \
+ \
+
';
+ $(".soft-man-con").html(con);
+ });
+}
+
+function replStatus(version){
+ redisPost('info_replication', version, {},function(data){
+ var rdata = $.parseJSON(data.data);
+
+ if ('status' in rdata && !rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ var kv = {
+ 'role':'角色',
+ 'master_host':'连接主库HOST',
+ 'master_port':'连接主库PORT',
+ 'master_link_status':'连接主库状态',
+ 'master_last_io_seconds_ago':'上次同步时间',
+ 'master_sync_in_progress':'正在同步中',
+ 'slave_read_repl_offset':'从库读取复制位置',
+ 'slave_repl_offset':'从库复制位置',
+ 'slave_read_only':'从库是否仅读',
+ 'replica_announced':'已复制副本',
+ 'connected_slaves':'连接数量',
+ 'master_failover_state':'主库故障状态',
+ 'master_replid':'主库复制ID',
+ 'master_repl_offset':'主库复制位置',
+ 'repl_backlog_size':'backlog复制大小',
+ 'second_repl_offset':'复制位置时间',
+ 'repl_backlog_first_byte_offset':'第一个字节偏移量',
+ 'repl_backlog_histlen':'backlog中数据的长度',
+ 'repl_backlog_active':'开启复制缓冲区',
+ 'slave_priority':'同步优先级',
+ }
+
+ var tbody_text = '';
+ for (k in rdata){
+ if (k == 'master_replid'){
+ tbody_text += ''+k+' ' + rdata[k] + ' '+kv[k]+' ';
+ } else{
+
+ if (k.substring(0,5) == 'slave' && !isNaN(k.substring(5))){
+ tbody_text += ''+k+' ' + rdata[k] + ' 从库配置信息 ';
+ } else{
+ tbody_text += ''+k+' ' + rdata[k] + ' '+kv[k]+' ';
+ }
+
+
+ }
+ }
+
+ var con = '\
+
\
+ 字段 当前值 说明 \
+ '+tbody_text+' \
+
';
+ $(".soft-man-con").html(con);
+ });
+}
+
+function clusterStatus(version){
+ redisPost('cluster_info', version, {},function(data){
+ var rdata = $.parseJSON(data.data);
+
+ if ('status' in rdata && !rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ var kv = {
+ 'cluster_state':'集群状态',
+ 'cluster_slots_assigned':'被分配的槽',
+ 'cluster_slots_ok':'被分配的槽状态',
+ 'cluster_known_nodes':'知道的节点',
+ 'cluster_size':'大小',
+ 'cluster_stats_messages_sent':'发送',
+ 'cluster_stats_messages_received':'接收',
+ 'cluster_current_epoch':'集群当前epoch',
+ 'cluster_my_epoch':'当前我的epoch',
+ 'cluster_slots_pfail':'处于PFAIL状态的槽数',
+ 'cluster_slots_fail':'处于FAIL状态的槽数',
+ 'total_cluster_links_buffer_limit_exceeded':'超出缓冲区总数',
+ }
+
+ var tbody_text = '';
+ for (k in rdata){
+ var desc = k;
+ if (k in kv){
+ desc = kv[k];
+ }
+
+ if (k == 'master_replid'){
+ tbody_text += ''+k+' ' + rdata[k] + ' '+desc+' ';
+ } else{
+ tbody_text += ''+k+' ' + rdata[k] + ' '+desc+' ';
+ }
+ }
+
+ if (tbody_text == ''){
+ tbody_text += '无数据/未设置集群 ';
+ }
+
+ var con = '\
+
\
+ 字段 当前值 说明 \
+ '+tbody_text+' \
+
';
+ $(".soft-man-con").html(con);
+ });
+}
+
+function clusterNodes(version){
+ redisPost('cluster_nodes', version, {},function(data){
+ var rdata = $.parseJSON(data.data);
+
+ if ('status' in rdata && !rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ // console.log(rdata);
+ var tbody_text = '';
+ for (k in rdata){
+ tbody_text += ''+ rdata[k] +' ';
+ }
+
+ if (tbody_text == ''){
+ tbody_text += '无数据/未设置集群 ';
+ }
+
+ var con = '\
+
\
+ 节点信息 \
+ '+tbody_text+' \
+
';
+ $(".soft-man-con").html(con);
+ });
+}
+
+//redis状态 end
+
+//配置修改
+function getRedisConfig(version) {
+ redisPost('get_redis_conf', version,'',function(data){
+ // console.log(data);
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata);
+ var mlist = '';
+ for (var i = 0; i < rdata.length; i++) {
+ var w = '70'
+ if (rdata[i].name == 'error_reporting') w = '250';
+ var ibody = ' ';
+ switch (rdata[i].type) {
+ case 0:
+ var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
+ var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
+ ibody = '开启 关闭 '
+ break;
+ case 1:
+ var selected_1 = (rdata[i].value == 'On') ? 'selected' : '';
+ var selected_0 = (rdata[i].value == 'Off') ? 'selected' : '';
+ ibody = '开启 关闭 '
+ break;
+ }
+ mlist += '' + rdata[i].name + ' ' + ibody + ', ' + rdata[i].ps + '
'
+ }
+ var con = '' + mlist + '\
+
刷新 \
+ 保存
\
+
'
+ $(".soft-man-con").html(con);
+ });
+}
+
+//提交配置
+function submitConf(version) {
+ var data = {
+ version: version,
+ bind: $("input[name='bind']").val(),
+ 'port': $("input[name='port']").val(),
+ 'timeout': $("input[name='timeout']").val(),
+ maxclients: $("input[name='maxclients']").val(),
+ databases: $("input[name='databases']").val(),
+ requirepass: $("input[name='requirepass']").val(),
+ maxmemory: $("input[name='maxmemory']").val(),
+ };
+
+ redisPost('submit_redis_conf', version, data, function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+
+function redisReadme(){
+ var cmd_01 = '/www/server/redis/bin/redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 --cluster-replicas 0';
+ var cmd_02 = '/www/server/redis/bin/redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 --cluster-replicas 1';
+
+
+ var readme = '';
+ readme += '集群创建1 ';
+ readme += ''+cmd_01+' ';
+ readme += '集群创建2 ';
+ readme += ''+cmd_02+' ';
+ readme += ' ';
+
+ $('.soft-man-con').html(readme);
+}
+
diff --git a/plugins/redis/tpl/redis_cluster.conf b/plugins/redis/tpl/redis_cluster.conf
new file mode 100644
index 000000000..8cf018c78
--- /dev/null
+++ b/plugins/redis/tpl/redis_cluster.conf
@@ -0,0 +1,94 @@
+daemonize yes
+pidfile {$SERVER_PATH}/redis/redis.pid
+
+loglevel notice
+logfile {$SERVER_PATH}/redis/data/redis.log
+databases 16
+
+timeout 0
+tcp-keepalive 0
+
+bind 127.0.0.1
+port 6379
+
+################################ SNAPSHOTTING #################################
+
+save 900 100
+save 300 1000
+save 60 1000000
+stop-writes-on-bgsave-error yes
+rdbcompression yes
+rdbchecksum yes
+dbfilename dump.rdb
+dir {$SERVER_PATH}/redis/data/
+
+################################# CLUSTER #################################
+
+
+protected-mode no
+cluster-enabled yes
+cluster-config-file nodes_{$REDIS_PASS}.conf
+cluster-require-full-coverage no
+cluster-node-timeout 15000
+
+################################# REPLICATION #################################
+
+slave-serve-stale-data yes
+slave-read-only yes
+
+repl-disable-tcp-nodelay no
+slave-priority 100
+
+################################## SECURITY ###################################
+
+
+################################### LIMITS ####################################
+maxclients 10000
+#maxmemory-samples 3
+maxmemory 218mb
+maxmemory-policy volatile-ttl
+
+############################## APPEND ONLY MODE ###############################
+
+
+#appendonly no
+# appendfsync always
+#appendfsync everysec
+# appendfsync no
+#no-appendfsync-on-rewrite no
+
+auto-aof-rewrite-percentage 100
+auto-aof-rewrite-min-size 64mb
+
+################################ LUA SCRIPTING ###############################
+
+lua-time-limit 5000
+
+################################## SLOW LOG ###################################
+
+
+slowlog-log-slower-than 10000
+slowlog-max-len 128
+
+############################### ADVANCED CONFIG ###############################
+
+hash-max-ziplist-entries 512
+hash-max-ziplist-value 64
+
+list-max-ziplist-entries 512
+list-max-ziplist-value 64
+
+set-max-intset-entries 512
+
+zset-max-ziplist-entries 128
+zset-max-ziplist-value 64
+
+activerehashing yes
+
+client-output-buffer-limit normal 0 0 0
+client-output-buffer-limit slave 256mb 64mb 60
+client-output-buffer-limit pubsub 32mb 8mb 60
+
+hz 10
+
+aof-rewrite-incremental-fsync yes
\ No newline at end of file
diff --git a/plugins/redis/tpl/redis_simple.conf b/plugins/redis/tpl/redis_simple.conf
new file mode 100644
index 000000000..f8d5ce16d
--- /dev/null
+++ b/plugins/redis/tpl/redis_simple.conf
@@ -0,0 +1,87 @@
+daemonize yes
+pidfile {$SERVER_PATH}/redis/redis.pid
+
+bind 127.0.0.1
+port 6379
+requirepass {$REDIS_PASS}
+
+timeout 0
+tcp-keepalive 0
+
+loglevel notice
+
+logfile {$SERVER_PATH}/redis/data/redis.log
+databases 16
+
+################################ SNAPSHOTTING #################################
+
+save 900 100
+save 300 1000
+save 60 1000000
+stop-writes-on-bgsave-error yes
+rdbcompression yes
+rdbchecksum yes
+dbfilename dump.rdb
+dir {$SERVER_PATH}/redis/data/
+
+################################# REPLICATION #################################
+
+slave-serve-stale-data yes
+slave-read-only yes
+
+repl-disable-tcp-nodelay no
+slave-priority 100
+
+################################## SECURITY ###################################
+
+
+################################### LIMITS ####################################
+maxclients 10000
+#maxmemory-samples 3
+maxmemory 218mb
+maxmemory-policy volatile-ttl
+
+############################## APPEND ONLY MODE ###############################
+
+
+#appendonly no
+# appendfsync always
+#appendfsync everysec
+# appendfsync no
+#no-appendfsync-on-rewrite no
+
+auto-aof-rewrite-percentage 100
+auto-aof-rewrite-min-size 64mb
+
+################################ LUA SCRIPTING ###############################
+
+lua-time-limit 5000
+
+################################## SLOW LOG ###################################
+
+
+slowlog-log-slower-than 10000
+slowlog-max-len 128
+
+############################### ADVANCED CONFIG ###############################
+
+hash-max-ziplist-entries 512
+hash-max-ziplist-value 64
+
+list-max-ziplist-entries 512
+list-max-ziplist-value 64
+
+set-max-intset-entries 512
+
+zset-max-ziplist-entries 128
+zset-max-ziplist-value 64
+
+activerehashing yes
+
+client-output-buffer-limit normal 0 0 0
+client-output-buffer-limit slave 256mb 64mb 60
+client-output-buffer-limit pubsub 32mb 8mb 60
+
+hz 10
+
+aof-rewrite-incremental-fsync yes
\ No newline at end of file
diff --git a/plugins/redis/tpl/redis_slave.conf b/plugins/redis/tpl/redis_slave.conf
new file mode 100644
index 000000000..9acff6985
--- /dev/null
+++ b/plugins/redis/tpl/redis_slave.conf
@@ -0,0 +1,91 @@
+daemonize yes
+pidfile {$SERVER_PATH}/redis/redis.pid
+
+bind 127.0.0.1
+port 6379
+requirepass {$REDIS_PASS}
+
+timeout 0
+tcp-keepalive 0
+
+loglevel notice
+
+logfile {$SERVER_PATH}/redis/data/redis.log
+databases 16
+
+################################ SNAPSHOTTING #################################
+
+save 900 100
+save 300 1000
+save 60 1000000
+stop-writes-on-bgsave-error yes
+rdbcompression yes
+rdbchecksum yes
+dbfilename dump.rdb
+dir {$SERVER_PATH}/redis/data/
+
+################################# REPLICATION #################################
+
+slave-serve-stale-data yes
+slave-read-only yes
+
+repl-disable-tcp-nodelay no
+slave-priority 100
+
+# 填写主库信息
+#slaveof 127.0.0.1 6379
+#masterauth 123123
+
+################################## SECURITY ###################################
+
+
+################################### LIMITS ####################################
+maxclients 10000
+#maxmemory-samples 3
+maxmemory 0mb
+maxmemory-policy volatile-ttl
+
+############################## APPEND ONLY MODE ###############################
+
+
+#appendonly no
+# appendfsync always
+#appendfsync everysec
+# appendfsync no
+#no-appendfsync-on-rewrite no
+
+auto-aof-rewrite-percentage 100
+auto-aof-rewrite-min-size 64mb
+
+################################ LUA SCRIPTING ###############################
+
+lua-time-limit 5000
+
+################################## SLOW LOG ###################################
+
+
+slowlog-log-slower-than 10000
+slowlog-max-len 128
+
+############################### ADVANCED CONFIG ###############################
+
+hash-max-ziplist-entries 512
+hash-max-ziplist-value 64
+
+list-max-ziplist-entries 512
+list-max-ziplist-value 64
+
+set-max-intset-entries 512
+
+zset-max-ziplist-entries 128
+zset-max-ziplist-value 64
+
+activerehashing yes
+
+client-output-buffer-limit normal 0 0 0
+client-output-buffer-limit slave 256mb 64mb 60
+client-output-buffer-limit pubsub 32mb 8mb 60
+
+hz 10
+
+aof-rewrite-incremental-fsync yes
\ No newline at end of file
diff --git a/plugins/redis/tpl/redis_slave_mem.conf b/plugins/redis/tpl/redis_slave_mem.conf
new file mode 100644
index 000000000..bbe35527f
--- /dev/null
+++ b/plugins/redis/tpl/redis_slave_mem.conf
@@ -0,0 +1,75 @@
+daemonize yes
+pidfile {$SERVER_PATH}/redis/redis.pid
+
+loglevel notice
+logfile {$SERVER_PATH}/redis/data/redis.log
+databases 16
+
+timeout 0
+tcp-keepalive 0
+
+bind 127.0.0.1
+port 6379
+requirepass {$REDIS_PASS}
+
+################################ SNAPSHOTTING #################################
+
+save ""
+stop-writes-on-bgsave-error no
+
+################################# REPLICATION #################################
+
+slave-serve-stale-data yes
+slave-read-only yes
+
+repl-disable-tcp-nodelay no
+slave-priority 100
+
+# 填写主库信息
+#slaveof 127.0.0.1 6379
+#masterauth 123123
+
+################################## SECURITY ###################################
+
+
+################################### LIMITS ####################################
+maxclients 10000
+#maxmemory-samples 3
+maxmemory 218mb
+maxmemory-policy allkeys-lru
+
+############################## APPEND ONLY MODE ###############################
+
+
+################################ LUA SCRIPTING ###############################
+
+lua-time-limit 5000
+
+################################## SLOW LOG ###################################
+
+
+slowlog-log-slower-than 10000
+slowlog-max-len 128
+
+############################### ADVANCED CONFIG ###############################
+
+hash-max-ziplist-entries 512
+hash-max-ziplist-value 64
+
+list-max-ziplist-entries 512
+list-max-ziplist-value 64
+
+set-max-intset-entries 512
+
+zset-max-ziplist-entries 128
+zset-max-ziplist-value 64
+
+activerehashing no
+
+client-output-buffer-limit normal 0 0 0
+client-output-buffer-limit slave 256mb 64mb 60
+client-output-buffer-limit pubsub 32mb 8mb 60
+
+hz 10
+
+aof-rewrite-incremental-fsync yes
\ No newline at end of file
diff --git a/plugins/rsyncd/conf/config.json b/plugins/rsyncd/conf/config.json
new file mode 100644
index 000000000..be3fd10a8
--- /dev/null
+++ b/plugins/rsyncd/conf/config.json
@@ -0,0 +1,26 @@
+{
+ "receive":{
+ "default":{
+ "uid": "root",
+ "use chroot": "no",
+ "dont compress": "*.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2 *.mp4 *.avi *.swf *.rar",
+ "hosts allow": "",
+ "max connections": 200,
+ "gid": "root",
+ "timeout": 600,
+ "pid file": "/var/run/rsyncd.pid",
+ "log file": "/var/log/rsyncd.log",
+ "port": 873
+ },
+ "list":[]
+ },
+ "send":{
+ "default":{
+ "logfile": "/www/server/rsyncd/lsyncd.log",
+ "inotifyMode": "CloseWrite",
+ "maxProcesses": 8,
+ "statusFile": "/www/server/rsyncd/lsyncd.status"
+ },
+ "list":[]
+ }
+}
\ No newline at end of file
diff --git a/plugins/rsyncd/conf/lsyncd.conf b/plugins/rsyncd/conf/lsyncd.conf
new file mode 100644
index 000000000..b2ed2416f
--- /dev/null
+++ b/plugins/rsyncd/conf/lsyncd.conf
@@ -0,0 +1,6 @@
+settings {
+ logfile = "{$SERVER_PATH}/rsyncd/lsyncd.log",
+ inotifyMode = "CloseWrite",
+ maxProcesses = 8,
+ statusFile = "{$SERVER_PATH}/rsyncd/lsyncd.status"
+}
\ No newline at end of file
diff --git a/plugins/rsyncd/conf/rsyncd.conf b/plugins/rsyncd/conf/rsyncd.conf
new file mode 100644
index 000000000..43d17f270
--- /dev/null
+++ b/plugins/rsyncd/conf/rsyncd.conf
@@ -0,0 +1,8 @@
+uid = www
+gid = www
+use chroot = no
+max connections = 100
+log file = /var/log/rsyncd.log
+pid file = /var/run/rsyncd.pid
+list = false
+hosts allow = *
\ No newline at end of file
diff --git a/plugins/rsyncd/ico.png b/plugins/rsyncd/ico.png
new file mode 100644
index 000000000..8dd3748bd
Binary files /dev/null and b/plugins/rsyncd/ico.png differ
diff --git a/plugins/rsyncd/index.html b/plugins/rsyncd/index.html
new file mode 100755
index 000000000..21a803d7b
--- /dev/null
+++ b/plugins/rsyncd/index.html
@@ -0,0 +1,95 @@
+
+
+
+
\ No newline at end of file
diff --git a/plugins/rsyncd/index.py b/plugins/rsyncd/index.py
new file mode 100755
index 000000000..5967c5a2e
--- /dev/null
+++ b/plugins/rsyncd/index.py
@@ -0,0 +1,997 @@
+# coding: utf-8
+
+import time
+import random
+import os
+import json
+import re
+import sys
+
+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 'rsyncd'
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
+ return path
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getInitDFile():
+ if app_debug:
+ return '/tmp/' + getPluginName()
+ return '/etc/init.d/' + getPluginName()
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+
+ 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 contentReplace(content):
+ service_path = mw.getServerDir()
+ content = content.replace('{$SERVER_PATH}', service_path)
+ return content
+
+
+def status():
+ data = mw.execShell(
+ "ps -ef|grep rsync |grep -v grep | grep -v python | awk '{print $2}'")
+ if data[0] == '':
+ return 'stop'
+
+ # data = mw.execShell(
+ # "ps -ef|grep lsyncd |grep -v grep | grep -v python | awk '{print $2}'")
+ # if data[0] == '':
+ # return 'stop'
+
+ return 'start'
+
+
+def appConf():
+ return getServerDir() + '/rsyncd.conf'
+
+
+def appAuthPwd(name):
+ nameDir = getServerDir() + '/receive/' + name
+ if not os.path.exists(nameDir):
+ mw.execShell("mkdir -p " + nameDir)
+ return nameDir + '/auth.db'
+
+
+def getLog():
+ conf_path = appConf()
+ conf = mw.readFile(conf_path)
+ rep = r'log file\s*=\s*(.*)'
+ tmp = re.search(rep, conf)
+ if not tmp:
+ return ''
+ return tmp.groups()[0]
+
+
+def getLsyncdLog():
+ path = getServerDir() + "/lsyncd.conf"
+ conf = mw.readFile(path)
+ rep = r'logfile\s*=\s*\"(.*)\"'
+ tmp = re.search(rep, conf)
+ if not tmp:
+ return ''
+ return tmp.groups()[0]
+
+
+def __release_port(port):
+ from collections import namedtuple
+ try:
+ from utils.firewall import Firewall as MwFirewall
+ MwFirewall.instance().addAcceptPort(port, 'RSYNC同步', 'port')
+ return port
+ except Exception as e:
+ return "Release failed {}".format(e)
+
+
+def openPort():
+ for i in ["873"]:
+ __release_port(i)
+ return True
+
+
+def initDReceive():
+ # conf
+ conf_path = appConf()
+ conf_tpl_path = getPluginDir() + '/conf/rsyncd.conf'
+ if not os.path.exists(conf_path):
+ content = mw.readFile(conf_tpl_path)
+ mw.writeFile(conf_path, content)
+
+ initD_path = getServerDir() + '/init.d'
+ if not os.path.exists(initD_path):
+ os.mkdir(initD_path)
+
+ file_bin = initD_path + '/' + getPluginName()
+ file_tpl = getInitDTpl()
+ # print(file_bin, file_tpl)
+ # initd replace
+ if not os.path.exists(file_bin):
+ content = mw.readFile(file_tpl)
+ content = contentReplace(content)
+ mw.writeFile(file_bin, content)
+ mw.execShell('chmod +x ' + file_bin)
+
+ lock_file = getServerDir() + "/installed_rsyncd.pl"
+ # systemd
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/rsyncd.service'
+ systemServiceTpl = getPluginDir() + '/init.d/rsyncd.service.tpl'
+ if not os.path.exists(lock_file):
+
+ rsync_bin = mw.execShell('which rsync')[0].strip()
+ if rsync_bin == '':
+ print('rsync missing!')
+ exit(0)
+
+ service_path = mw.getServerDir()
+ se = mw.readFile(systemServiceTpl)
+ se = se.replace('{$SERVER_PATH}', service_path)
+ se = se.replace('{$RSYNC_BIN}', rsync_bin)
+ mw.writeFile(systemService, se)
+ mw.execShell('systemctl daemon-reload')
+
+ mw.writeFile(lock_file, "ok")
+ openPort()
+
+ rlog = getLog()
+ if os.path.exists(rlog):
+ mw.writeFile(rlog, '')
+ return file_bin
+
+
+def initDSend():
+
+ service_path = mw.getServerDir()
+
+ conf_path = getServerDir() + '/lsyncd.conf'
+ conf_tpl_path = getPluginDir() + '/conf/lsyncd.conf'
+ if not os.path.exists(conf_path):
+ content = mw.readFile(conf_tpl_path)
+ content = content.replace('{$SERVER_PATH}', service_path)
+ mw.writeFile(conf_path, content)
+
+ initD_path = getServerDir() + '/init.d'
+ if not os.path.exists(initD_path):
+ os.mkdir(initD_path)
+
+ # initd replace
+ file_bin = initD_path + '/lsyncd'
+ file_tpl = getPluginDir() + "/init.d/lsyncd.tpl"
+ if not os.path.exists(file_bin):
+ content = mw.readFile(file_tpl)
+ content = contentReplace(content)
+ mw.writeFile(file_bin, content)
+ mw.execShell('chmod +x ' + file_bin)
+
+ lock_file = getServerDir() + "/installed.pl"
+ # systemd
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/lsyncd.service'
+ systemServiceTpl = getPluginDir() + '/init.d/lsyncd.service.tpl'
+ if not os.path.exists(lock_file):
+ lsyncd_bin = mw.execShell('which lsyncd')[0].strip()
+ if lsyncd_bin == '':
+ print('lsyncd missing!')
+ exit(0)
+
+ content = mw.readFile(systemServiceTpl)
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$LSYNCD_BIN}', lsyncd_bin)
+ mw.writeFile(systemService, content)
+ mw.execShell('systemctl daemon-reload')
+
+ mw.writeFile(lock_file, "ok")
+
+ lslog = getLsyncdLog()
+ if os.path.exists(lslog):
+ mw.writeFile(lslog, '')
+
+ return file_bin
+
+
+def getDefaultConf():
+ path = getServerDir() + "/config.json"
+ data = mw.readFile(path)
+ data = json.loads(data)
+ return data
+
+
+def setDefaultConf(data):
+ path = getServerDir() + "/config.json"
+ mw.writeFile(path, json.dumps(data))
+ return True
+
+
+def initConfigJson():
+ path = getServerDir() + "/config.json"
+ tpl = getPluginDir() + "/conf/config.json"
+ if not os.path.exists(path):
+ data = mw.readFile(tpl)
+ data = json.loads(data)
+ mw.writeFile(path, json.dumps(data))
+
+
+def initDreplace():
+
+ initDSend()
+
+ # conf
+ file_bin = initDReceive()
+ initConfigJson()
+
+ return file_bin
+
+
+def rsyncOp(method):
+ file = initDreplace()
+ if not mw.isAppleSystem():
+ data = mw.execShell('systemctl ' + method + ' rsyncd')
+ if data[1] == '':
+ return 'ok'
+ return 'fail'
+
+ data = mw.execShell(file + ' ' + method)
+ if data[1] == '':
+ return 'ok'
+ return 'fail'
+
+
+def lsyncdOp(method):
+ if not mw.isAppleSystem():
+ data = mw.execShell('systemctl ' + method + ' lsyncd')
+ if data[1] == '':
+ return 'ok'
+ return 'fail'
+ return 'fail'
+
+def start():
+ status = rsyncOp('start')
+ lsyncdOp('start')
+ return status
+
+def stop():
+ status = rsyncOp('stop')
+ lsyncdOp('stop')
+ return status
+
+def restart():
+ status = rsyncOp('restart')
+ lsyncdOp('restart')
+ return status
+
+def reload():
+ status = rsyncOp('reload')
+ lsyncdOp('reload')
+ return status
+
+def initdStatus():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status rsyncd | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+
+ shell_cmd = 'systemctl status lsyncd | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+
+ return 'ok'
+
+
+def initdInstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl enable lsyncd')
+ mw.execShell('systemctl enable rsyncd')
+ return 'ok'
+
+
+def initdUinstall():
+ if not app_debug:
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl diable lsyncd')
+ mw.execShell('systemctl diable rsyncd')
+ return 'ok'
+
+
+def getRecListData():
+ path = appConf()
+ content = mw.readFile(path)
+
+ flist = re.findall("\\[(.*)\\]", content)
+
+ flist_len = len(flist)
+ ret_list = []
+ for i in range(flist_len):
+ tmp = {}
+ tmp['name'] = flist[i]
+ n = i + 1
+ reg = ''
+ if n == flist_len:
+ reg = r'\[' + flist[i] + r'\](.*)\[?'
+ else:
+ reg = r'\[' + flist[i] + r'\](.*)\[' + flist[n] + r'\]'
+
+ t1 = re.search(reg, content, re.S)
+ if t1:
+ args = t1.groups()[0]
+ # print('args start', args, 'args_end')
+ t2 = re.findall(r'\s*(.*)\s*\=\s*?(.*)?', args, re.M | re.I)
+ for i in range(len(t2)):
+ tmp[t2[i][0].strip()] = t2[i][1].strip()
+ ret_list.append(tmp)
+
+ return ret_list
+
+
+def getRecListDataBy(name):
+ l = getRecListData()
+ for x in range(len(l)):
+ if name == l[x]["name"]:
+ return l[x]
+
+
+def getRecList():
+ ret_list = getRecListData()
+ return mw.returnJson(True, 'ok', ret_list)
+
+
+def addRec():
+ args = getArgs()
+ data = checkArgs(args, ['name', 'path', 'pwd', 'ps'])
+ if not data[0]:
+ return data[1]
+
+ args_name = args['name']
+ args_pwd = args['pwd']
+ args_path = args['path']
+ args_ps = args['ps']
+
+ if not mw.isAppleSystem():
+ if os.path.exists(args_path):
+ import utils.file as utils_file
+ info = utils_file.getAccess(args_path)
+ file_chown = info['chown']
+ if file_chown != 'www':
+ return mw.returnJson(False, '建议手动执行命令: chown -R www:www '+ args_path)
+ else:
+ os.system("mkdir -p " + args_path + " &")
+ os.system("chown -R www:www " + args_path + " &")
+ os.system("chmod -R 755 " + args_path + " &")
+
+ delRecBy(args_name)
+
+ auth_path = appAuthPwd(args_name)
+ pwd_content = args_name + ':' + args_pwd + "\n"
+ mw.writeFile(auth_path, pwd_content)
+ mw.execShell("chmod 600 " + auth_path)
+
+ path = appConf()
+ content = mw.readFile(path)
+
+ con = "\n\n" + '[' + args_name + ']' + "\n"
+ con += 'path = ' + args_path + "\n"
+ con += 'comment = ' + args_ps + "\n"
+ con += 'auth users = ' + args_name + "\n"
+ con += 'ignore errors' + "\n"
+ con += 'secrets file = ' + auth_path + "\n"
+ con += 'read only = false'
+
+ content = content.strip() + "\n" + con
+ mw.writeFile(path, content)
+ return mw.returnJson(True, '添加成功')
+
+
+def getRec():
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ name = args['name']
+
+ if name == "":
+ tmp = {}
+ tmp["name"] = ""
+ tmp["comment"] = ""
+ tmp["path"] = mw.getWwwDir()
+ tmp["pwd"] = mw.getRandomString(16)
+ return mw.returnJson(True, 'OK', tmp)
+
+ data = getRecListDataBy(name)
+
+ content = mw.readFile(data['secrets file'])
+ pwd = content.strip().split(":")
+ data['pwd'] = pwd[1]
+ return mw.returnJson(True, 'OK', data)
+
+
+def delRecBy(name):
+ try:
+ path = appConf()
+ content = mw.readFile(path)
+
+ reclist = getRecListData()
+ ret_list_len = len(reclist)
+ is_end = False
+ next_name = ''
+ for x in range(ret_list_len):
+ tmp = reclist[x]
+ if tmp['name'] == name:
+
+ secrets_file = tmp['secrets file']
+ tp = os.path.dirname(secrets_file)
+ if os.path.exists(tp):
+ mw.execShell("rm -rf " + tp)
+
+ if x + 1 == ret_list_len:
+ is_end = True
+ else:
+ next_name = reclist[x + 1]['name']
+ reg = ''
+ if is_end:
+ reg = r'\[' + name + r'\]\s*(.*)'
+ else:
+ reg = r'\\[' + name + r'\]\s*(.*)\s*\[' + next_name + r'\]'
+
+ conre = re.search(reg, content, re.S)
+ content = content.replace("[" + name + "]\n" + conre.groups()[0], '')
+ mw.writeFile(path, content)
+ except Exception as e:
+ return False
+ return True
+
+
+def delRec():
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+ name = args['name']
+ ok = delRecBy(name)
+ if ok:
+ return mw.returnJson(True, '删除成功!')
+ return mw.returnJson(False, '删除失败!')
+
+
+def cmdRecSecretKey():
+ import base64
+
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ name = args['name']
+ info = getRecListDataBy(name)
+
+ secrets_file = info['secrets file']
+ content = mw.readFile(info['secrets file'])
+ pwd = content.strip().split(":")
+
+ m = {"A": info['name'], "B": pwd[1], "C": "873"}
+ m = json.dumps(m)
+ m = m.encode("utf-8")
+ m = base64.b64encode(m)
+ cmd = m.decode("utf-8")
+ return mw.returnJson(True, 'OK!', cmd)
+
+
+def cmdRecCmd():
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ name = args['name']
+ info = getRecListDataBy(name)
+ ip = mw.getLocalIp()
+
+ content = mw.readFile(info['secrets file'])
+ pwd = content.strip().split(":")
+
+ tmp_name = '/tmp/' + name + '.pass'
+
+ cmd = 'echo "' + pwd[1] + '" > ' + tmp_name + ' '
+ cmd += 'chmod 600 ' + tmp_name + ' '
+ cmd += 'rsync -arv --password-file=' + tmp_name + \
+ ' --progress --delete /project ' + name + '@' + ip + '::' + name
+ return mw.returnJson(True, 'OK!', cmd)
+
+
+# ----------------------------- rsyncdSend start -------------------------
+
+def lsyncdReload():
+ data = mw.execShell(
+ "ps -ef|grep lsyncd |grep -v grep | grep -v python | awk '{print $2}'")
+ if data[0] == '':
+ mw.execShell('systemctl start lsyncd')
+ else:
+ mw.execShell('systemctl restart lsyncd')
+
+
+def makeLsyncdConf(data):
+ # print(data)
+
+ lsyncd_data = data['send']
+ lsyncd_setting = lsyncd_data['default']
+
+ content = "settings {\n"
+ for x in lsyncd_setting:
+ v = lsyncd_setting[x]
+ # print(v, type(v))
+ if type(v) == str:
+ content += "\t" + x + ' = "' + v + "\",\n"
+ elif type(v) == int:
+ content += "\t" + x + ' = ' + str(v) + ",\n"
+ content += "}\n\n"
+
+ lsyncd_list = lsyncd_data['list']
+
+ rsync_bin = mw.execShell('which rsync')[0].strip()
+ send_dir = getServerDir() + "/send"
+
+ if len(lsyncd_list) > 0:
+ for x in range(len(lsyncd_list)):
+
+ t = lsyncd_list[x]
+ name_dir = send_dir + "/" + t["name"]
+ if not os.path.exists(name_dir):
+ mw.execShell("mkdir -p " + name_dir)
+
+ cmd_exclude = name_dir + "/exclude"
+ cmd_exclude_txt = ""
+ for x in t['exclude']:
+ cmd_exclude_txt += x + "\n"
+ mw.writeFile(cmd_exclude, cmd_exclude_txt)
+ cmd_pass = name_dir + "/pass"
+ if os.path.exists(cmd_pass):
+ mw.execShell("chmod 755 " + cmd_pass)
+ mw.writeFile(cmd_pass, t['password'])
+ mw.execShell("chmod 600 " + cmd_pass)
+
+ delete_ok = ' '
+ if t['delete'] == "true":
+ delete_ok = ' --delete '
+
+ remote_addr = t['name'] + '@' + t['ip'] + "::" + t['name']
+ cmd = rsync_bin + " -avzP " + "--port=" + str(t['rsync']['port']) + " --bwlimit=" + t['rsync'][
+ 'bwlimit'] + delete_ok + " --exclude-from=" + cmd_exclude + " --password-file=" + cmd_pass + " " + t["path"] + " " + remote_addr
+ mw.writeFile(name_dir + "/cmd", cmd)
+ mw.execShell("cmod +x " + name_dir + "/cmd")
+
+ if t['realtime'] == "false":
+ continue
+
+ # print(x, t)
+ content += "sync {\n"
+ content += "\tdefault.rsync,\n"
+ content += "\tsource = \"" + t['path'] + "\",\n"
+ content += "\ttarget = \"" + remote_addr + "\",\n"
+ content += "\tdelete = " + t['delete'] + ",\n"
+ content += "\tdelay = " + t['delay'] + ",\n"
+ content += "\tinit = false,\n"
+
+ exclude_str = json.dumps(t['exclude'])
+ exclude_str = exclude_str.replace("[", "{")
+ exclude_str = exclude_str.replace("]", "}")
+ # print(exclude_str)
+ content += "\texclude = " + exclude_str + ",\n"
+
+ # rsync
+ content += "\trsync = {\n"
+ content += "\t\tbinary = \"" + rsync_bin + "\",\n"
+ content += "\t\tarchive = true,\n"
+ content += "\t\tverbose = true,\n"
+ content += "\t\tcompress = " + t['rsync']['compress'] + ",\n"
+ content += "\t\tpassword_file = \"" + cmd_pass + "\",\n"
+
+ content += "\t\t_extra = {\"--bwlimit=" + t['rsync'][
+ 'bwlimit'] + "\", \"--port=" + str(t['rsync']['port']) + "\"},\n"
+
+ content += "\t}\n"
+ content += "}\n"
+
+ path = getServerDir() + "/lsyncd.conf"
+ mw.writeFile(path, content)
+
+ lsyncdReload()
+
+ import tool_task
+ tool_task.createBgTask(lsyncd_list)
+
+
+def lsyncdListFindIp(slist, ip):
+ for x in range(len(slist)):
+ if slist[x]["ip"] == ip:
+ return (True, x)
+ return (False, -1)
+
+
+def lsyncdListFindName(slist, name):
+ for x in range(len(slist)):
+ if slist[x]["name"] == name:
+ return (True, x)
+ return (False, -1)
+
+
+def lsyncdList():
+ data = getDefaultConf()
+ send = data['send']
+ return mw.returnJson(True, "设置成功!", send)
+
+
+def lsyncdGet():
+ import base64
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ name = args['name']
+ data = getDefaultConf()
+
+ slist = data['send']["list"]
+ res = lsyncdListFindName(slist, name)
+
+ rsync = {
+ 'bwlimit': "1024",
+ "compress": "true",
+ "archive": "true",
+ "verbose": "true"
+ }
+
+ info = {
+ "secret_key": '',
+ "ip": '',
+ "path": mw.getServerDir(),
+ 'rsync': rsync,
+ 'realtime': "true",
+ 'delete': "false",
+ }
+ if res[0]:
+ list_index = res[1]
+ info = slist[list_index]
+ m = {"A": info['name'], "B": info["password"], "C": "873"}
+ m = json.dumps(m)
+ m = m.encode("utf-8")
+ m = base64.b64encode(m)
+ info['secret_key'] = m.decode("utf-8")
+ return mw.returnJson(True, "OK", info)
+
+
+def lsyncdDelete():
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ name = args['name']
+ data = getDefaultConf()
+ slist = data['send']["list"]
+ res = lsyncdListFindName(slist, name)
+ retdata = {}
+ if res[0]:
+ list_index = res[1]
+ slist.pop(list_index)
+
+ data['send']["list"] = slist
+ setDefaultConf(data)
+ makeLsyncdConf(data)
+ return mw.returnJson(True, "OK")
+
+
+def lsyncdAdd():
+ import base64
+
+ args = getArgs()
+ data = checkArgs(args, ['ip', 'conn_type', 'path', 'delay', 'period', 'bwlimit'])
+ if not data[0]:
+ return data[1]
+
+ ip = args['ip']
+ path = args['path']
+
+ if not mw.isAppleSystem():
+ if os.path.exists(path):
+ import utils.file as utils_file
+ info = utils_file.getAccess(path)
+ file_chown = info['chown']
+ if file_chown != 'www':
+ return mw.returnJson(False, '建议手动执行命令: chown -R www:www '+ args_path)
+ else:
+ os.system("mkdir -p " + path + " &")
+ os.system("chown -R www:www " + path + " &")
+ os.system("chmod -R 755 " + path + " &")
+
+ conn_type = args['conn_type']
+
+ delete = args['delete']
+ realtime = args['realtime']
+ delay = args['delay']
+ bwlimit = args['bwlimit']
+ compress = args['compress']
+ period = args['period']
+
+ hour = args['hour']
+ minute = args['minute']
+ minute_n = args['minute-n']
+
+ info = {
+ "ip": ip,
+ "path": path,
+ "delete": delete,
+ "realtime": realtime,
+ 'delay': delay,
+ "conn_type": conn_type,
+ "period": period,
+ "hour": hour,
+ "minute": minute,
+ "minute-n": minute_n,
+ }
+
+ if conn_type == "key":
+
+ secret_key_check = checkArgs(args, ['secret_key'])
+ if not secret_key_check[0]:
+ return secret_key_check[1]
+
+ secret_key = args['secret_key']
+ try:
+ m = base64.b64decode(secret_key)
+ m = json.loads(m)
+ info['name'] = m['A']
+ info['password'] = m['B']
+ info['port'] = m['C']
+ except Exception as e:
+ return mw.returnJson(False, "接收密钥格式错误!")
+ else:
+ data = checkArgs(args, ['sname', 'password'])
+ if not data[0]:
+ return data[1]
+
+ info['name'] = args['sname']
+ info['password'] = args['password']
+ info['port'] = args['port']
+
+ rsync = {
+ 'bwlimit': bwlimit,
+ "port": info['port'],
+ "compress": compress,
+ "archive": "true",
+ "verbose": "true"
+ }
+
+ info['rsync'] = rsync
+
+ data = getDefaultConf()
+
+ slist = data['send']["list"]
+ res = lsyncdListFindName(slist, info['name'])
+
+ if not 'exclude' in info:
+ if res[0]:
+ info["exclude"] = slist[res[1]]['exclude']
+ else:
+ info["exclude"] = [
+ "/**.upload.tmp", "**/*.log", "**/*.tmp",
+ "**/*.temp", ".git", ".gitignore", ".user.ini",
+ ]
+
+ if res[0]:
+ list_index = res[1]
+ slist[list_index] = info
+ else:
+ slist.append(info)
+
+ data['send']["list"] = slist
+
+ setDefaultConf(data)
+ makeLsyncdConf(data)
+ return mw.returnJson(True, "设置成功!")
+
+
+def lsyncdRun():
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ send_dir = getServerDir() + "/send"
+ name = args['name']
+ app_dir = send_dir + "/" + name
+
+ cmd = "bash " + app_dir + "/cmd >> " + app_dir + "/run.log" + " 2>&1 &"
+ mw.execShell(cmd)
+ return mw.returnJson(True, "执行成功!")
+
+
+def lsyncdConfLog():
+ logs_path = getServerDir() + "/lsyncd.log"
+ return logs_path
+
+
+def lsyncdLog():
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ send_dir = getServerDir() + "/send"
+ name = args['name']
+ app_dir = send_dir + "/" + name
+ return app_dir + "/run.log"
+
+
+def lsyncdGetExclude():
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ data = getDefaultConf()
+ slist = data['send']["list"]
+ res = lsyncdListFindName(slist, args['name'])
+ i = res[1]
+ info = slist[i]
+ return mw.returnJson(True, "OK!", info['exclude'])
+
+
+def lsyncdRemoveExclude():
+ args = getArgs()
+ data = checkArgs(args, ['name', 'exclude'])
+ if not data[0]:
+ return data[1]
+
+ exclude = args['exclude']
+
+ data = getDefaultConf()
+ slist = data['send']["list"]
+ res = lsyncdListFindName(slist, args['name'])
+ i = res[1]
+ info = slist[i]
+
+ exclude_list = info['exclude']
+ exclude_pop_key = -1
+ for x in range(len(exclude_list)):
+ if exclude_list[x] == exclude:
+ exclude_pop_key = x
+
+ if exclude_pop_key > -1:
+ exclude_list.pop(exclude_pop_key)
+
+ data['send']["list"][i]['exclude'] = exclude_list
+ setDefaultConf(data)
+ makeLsyncdConf(data)
+ return mw.returnJson(True, "OK!", exclude_list)
+
+
+def lsyncdAddExclude():
+ args = getArgs()
+ data = checkArgs(args, ['name', 'exclude'])
+ if not data[0]:
+ return data[1]
+
+ exclude = args['exclude']
+
+ data = getDefaultConf()
+ slist = data['send']["list"]
+ res = lsyncdListFindName(slist, args['name'])
+ i = res[1]
+ info = slist[i]
+
+ exclude_list = info['exclude']
+ exclude_list.append(exclude)
+
+ data['send']["list"][i]['exclude'] = exclude_list
+ setDefaultConf(data)
+ makeLsyncdConf(data)
+ return mw.returnJson(True, "OK!", exclude_list)
+
+if __name__ == "__main__":
+ 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 == 'conf':
+ print(appConf())
+ elif func == 'run_log':
+ print(getLog())
+ elif func == 'rec_list':
+ print(getRecList())
+ elif func == 'add_rec':
+ print(addRec())
+ elif func == 'del_rec':
+ print(delRec())
+ elif func == 'get_rec':
+ print(getRec())
+ elif func == 'cmd_rec_secret_key':
+ print(cmdRecSecretKey())
+ elif func == 'cmd_rec_cmd':
+ print(cmdRecCmd())
+ elif func == 'lsyncd_list':
+ print(lsyncdList())
+ elif func == 'lsyncd_add':
+ print(lsyncdAdd())
+ elif func == 'lsyncd_get':
+ print(lsyncdGet())
+ elif func == 'lsyncd_delete':
+ print(lsyncdDelete())
+ elif func == 'lsyncd_run':
+ print(lsyncdRun())
+ elif func == 'lsyncd_log':
+ print(lsyncdLog())
+ elif func == 'lsyncd_conf_log':
+ print(lsyncdConfLog())
+ elif func == 'lsyncd_get_exclude':
+ print(lsyncdGetExclude())
+ elif func == 'lsyncd_remove_exclude':
+ print(lsyncdRemoveExclude())
+ elif func == 'lsyncd_add_exclude':
+ print(lsyncdAddExclude())
+ else:
+ print('error')
diff --git a/plugins/rsyncd/info.json b/plugins/rsyncd/info.json
new file mode 100755
index 000000000..1f8b746e8
--- /dev/null
+++ b/plugins/rsyncd/info.json
@@ -0,0 +1,16 @@
+{
+ "sort":2,
+ "title":"rsyncd",
+ "tip":"soft",
+ "name":"rsyncd",
+ "type":"软件",
+ "ps":"rsyncd同步助手",
+ "versions":"2.0",
+ "shell":"install.sh",
+ "checks":"server/rsyncd",
+ "path": "server/rsyncd",
+ "author":"samba",
+ "home":"https://rsync.samba.org/",
+ "date":"2019-03-05",
+ "pid":"4"
+}
\ No newline at end of file
diff --git a/plugins/rsyncd/init.d/lsyncd.service.tpl b/plugins/rsyncd/init.d/lsyncd.service.tpl
new file mode 100755
index 000000000..7c6a03c60
--- /dev/null
+++ b/plugins/rsyncd/init.d/lsyncd.service.tpl
@@ -0,0 +1,10 @@
+[Unit]
+Description=Lightweight inotify based sync daemon
+ConditionPathExists={$SERVER_PATH}/rsyncd/lsyncd.conf
+
+[Service]
+Type=simple
+ExecStart={$LSYNCD_BIN} -nodaemon {$SERVER_PATH}/rsyncd/lsyncd.conf
+
+[Install]
+WantedBy=multi-user.target
diff --git a/plugins/rsyncd/init.d/lsyncd.tpl b/plugins/rsyncd/init.d/lsyncd.tpl
new file mode 100755
index 000000000..a5ccdcad6
--- /dev/null
+++ b/plugins/rsyncd/init.d/lsyncd.tpl
@@ -0,0 +1,108 @@
+#!/bin/bash
+#
+# chkconfig: - 85 15
+# description: Lightweight inotify based sync daemon
+#
+# processname: lsyncd
+# config: /etc/lsyncd.conf
+# config: /etc/sysconfig/lsyncd
+# pidfile: /var/run/lsyncd.pid
+# Source function library
+
+if [ -f /etc/init.d/functions ];then
+ . /etc/init.d/functions
+fi
+
+if [ -f /lib/lsb/init-functions ];then
+ . /lib/lsb/init-functions
+fi
+
+# Source networking configuration.
+if [ -f /etc/sysconfig/network ];then
+ . /etc/sysconfig/network
+fi
+
+LSYNCD_OPTIONS="-pidfile /var/run/lsyncd.pid {$SERVER_PATH}/rsyncd/lsyncd.conf"
+if [ -e /etc/sysconfig/lsyncd ]; then
+ . /etc/sysconfig/lsyncd
+fi
+RETVAL=0
+prog="lsyncd"
+thelock=/var/lock/subsys/lsyncd
+LSYNCD_USER=root
+
+start() {
+ [ -f {$SERVER_PATH}/rsyncd/lsyncd.conf ] || exit 6
+ echo -n $"Starting $prog: "
+ if [ $UID -ne 0 ]; then
+ RETVAL=1
+ failure
+ else
+ nohup /usr/bin/lsyncd $LSYNCD_OPTIONS > /dev/null &
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && touch $thelock
+ fi;
+ echo
+ return $RETVAL
+}
+
+stop() {
+ echo -n $"Stopping $prog: "
+ if [ $UID -ne 0 ]; then
+ RETVAL=1
+ failure
+ else
+ killproc lsyncd
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && rm -f $thelock
+ fi;
+ echo
+ return $RETVAL
+}
+
+reload(){
+ echo -n $"Reloading $prog: "
+ killproc lsyncd -HUP
+ RETVAL=$?
+ echo
+ return $RETVAL
+}
+
+restart(){
+ stop
+ start
+}
+
+condrestart(){
+ [ -e $thelock ] && restart
+ return 0
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+
+ restart)
+ restart
+ ;;
+ reload)
+ reload
+ ;;
+ condrestart)
+ condrestart
+ ;;
+
+ status)
+ status lsyncd
+ RETVAL=$?
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}"
+ RETVAL=1
+esac
+
+exit $RETVAL
diff --git a/plugins/rsyncd/init.d/rsyncd.service.tpl b/plugins/rsyncd/init.d/rsyncd.service.tpl
new file mode 100644
index 000000000..72dd0efc9
--- /dev/null
+++ b/plugins/rsyncd/init.d/rsyncd.service.tpl
@@ -0,0 +1,9 @@
+[Unit]
+Description=fast remote file copy program daemon
+ConditionPathExists={$SERVER_PATH}/rsyncd/rsyncd.conf
+
+[Service]
+ExecStart={$RSYNC_BIN} --config={$SERVER_PATH}/rsyncd/rsyncd.conf --daemon --no-detach
+
+[Install]
+WantedBy=multi-user.target
diff --git a/plugins/rsyncd/init.d/rsyncd.tpl b/plugins/rsyncd/init.d/rsyncd.tpl
new file mode 100755
index 000000000..2ee441060
--- /dev/null
+++ b/plugins/rsyncd/init.d/rsyncd.tpl
@@ -0,0 +1,70 @@
+#!/bin/sh
+# chkconfig: 2345 55 25
+# description: rsyncd Service
+
+### BEGIN INIT INFO
+# Provides: rsyncd
+# Required-Start: $all
+# Required-Stop: $all
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts rsyncd
+# Description: starts the rsyncd
+### END INIT INFO
+
+ROOT_PATH={$SERVER_PATH}
+
+p_start(){
+ isStart=$(ps -ef | grep rsync | grep 'daemon' | grep -v grep | grep -v python | awk '{print $2}')
+ if [ "$isStart" == '' ];then
+ echo -e "Starting rsync... \c"
+ if [ -f /var/run/rsyncd.pid ]; then
+ rm -rf /var/run/rsyncd.pid
+ fi
+ /usr/bin/rsync --daemon --config=/etc/rsyncd.conf
+ sleep 0.3
+ isStart=$(ps -ef | grep rsync | grep 'daemon' | grep -v grep | grep -v python | awk '{print $2}')
+ if [ "$isStart" == '' ];then
+ echo -e "\033[31mError: rsyncd service startup failed.\033[0m"
+ return;
+ fi
+ echo -e "\033[32mdone\033[0m"
+ else
+ echo "Starting rsyncd(pid $isStart) already running"
+ fi
+}
+
+p_stop(){
+ echo -e "Stopping rsyncd... \c";
+ pids=$(ps -ef | grep rsync | grep 'daemon' | grep -v grep | grep -v python | awk '{print $2}')
+ arr=($pids)
+
+ for p in ${arr[@]}
+ do
+ kill -9 $p
+ done
+
+ if [ -f /var/run/rsyncd.pid ]; then
+ rm -rf /var/run/rsyncd.pid
+ fi
+ echo -e "\033[32mdone\033[0m"
+}
+
+
+case "$1" in
+ start)
+ p_start
+ ;;
+ stop)
+ p_stop
+ ;;
+ restart|reload)
+ p_stop
+ sleep 0.3
+ p_start
+ ;;
+ *)
+ echo "Please use start or stop as first argument"
+ ;;
+esac
+
diff --git a/plugins/rsyncd/install.sh b/plugins/rsyncd/install.sh
new file mode 100755
index 000000000..a8a13d86a
--- /dev/null
+++ b/plugins/rsyncd/install.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sysName=`uname`
+
+
+#获取信息和版本
+# bash /www/server/mdsever-web/scripts/getos.sh
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+if id www &> /dev/null ;then
+ echo "www uid is `id -u www`"
+ echo "www shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `"
+else
+ groupadd www
+ useradd -g www -s /bin/bash www
+fi
+
+# echo $OSNAME
+
+Install_rsyncd()
+{
+ echo '正在安装脚本文件...'
+
+
+ if [ "$OSNAME" == "debian" ] || [ "$OSNAME" == "ubuntu" ];then
+ apt install -y rsync
+ apt install -y lsyncd
+ elif [[ "$OSNAME" == "arch" ]]; then
+ echo y | pacman -Sy rsync
+ echo y | pacman -Sy lsyncd
+ elif [[ "$OSNAME" == "macos" ]]; then
+ # brew install rsync
+ # brew install lsyncd
+ echo "ok"
+ else
+ yum install -y rsync
+ yum install -y lsyncd
+ fi
+
+ mkdir -p $serverPath/rsyncd
+ mkdir -p $serverPath/rsyncd/receive
+ mkdir -p $serverPath/rsyncd/send
+
+ echo '2.0' > $serverPath/rsyncd/version.pl
+ echo '安装完成'
+ cd ${rootPath} && python3 ${rootPath}/plugins/rsyncd/index.py start
+ cd ${rootPath} && python3 ${rootPath}/plugins/rsyncd/index.py initd_install
+}
+
+Uninstall_rsyncd()
+{
+ if [ -f /usr/lib/systemd/system/rsyncd.service ] || [ -f /lib/systemd/system/rsyncd.service ];then
+ systemctl stop rsyncd
+ systemctl disable rsyncd
+ rm -rf /usr/lib/systemd/system/rsyncd.service
+ rm -rf /lib/systemd/system/rsyncd.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f /usr/lib/systemd/system/lsyncd.service ] || [ -f /lib/systemd/system/lsyncd.service ];then
+ systemctl stop lsyncd
+ systemctl disable lsyncd
+ rm -rf /usr/lib/systemd/system/lsyncd.service
+ rm -rf /lib/systemd/system/lsyncd.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/rsyncd/initd/rsyncd ];then
+ $serverPath/rsyncd/initd/rsyncd stop
+ fi
+
+ rm -rf $serverPath/rsyncd
+ echo "卸载完成"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_rsyncd
+else
+ Uninstall_rsyncd
+fi
diff --git a/plugins/rsyncd/js/base64.js b/plugins/rsyncd/js/base64.js
new file mode 100644
index 000000000..4a5caa2a7
--- /dev/null
+++ b/plugins/rsyncd/js/base64.js
@@ -0,0 +1,149 @@
+//base64.js
+function base64_encode(str) {
+ var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ var out, i, len;
+ var c1, c2, c3;
+
+ len = str.length;
+ i = 0;
+ out = "";
+ while(i < len) {
+ c1 = str.charCodeAt(i++) & 0xff;
+ if(i == len)
+ {
+ out += base64EncodeChars.charAt(c1 >> 2);
+ out += base64EncodeChars.charAt((c1 & 0x3) << 4);
+ out += "==";
+ break;
+ }
+ c2 = str.charCodeAt(i++);
+ if(i == len)
+ {
+ out += base64EncodeChars.charAt(c1 >> 2);
+ out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
+ out += base64EncodeChars.charAt((c2 & 0xF) << 2);
+ out += "=";
+ break;
+ }
+ c3 = str.charCodeAt(i++);
+ out += base64EncodeChars.charAt(c1 >> 2);
+ out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
+ out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6));
+ out += base64EncodeChars.charAt(c3 & 0x3F);
+ }
+ return out;
+}
+
+function base64_decode(str) {
+ var base64DecodeChars = new Array(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);
+ var c1, c2, c3, c4;
+ var i, len, out;
+
+ len = str.length;
+ i = 0;
+ out = "";
+ while(i < len) {
+ /* c1 */
+ do {
+ c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
+ } while(i < len && c1 == -1);
+ if(c1 == -1)
+ break;
+
+ /* c2 */
+ do {
+ c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
+ } while(i < len && c2 == -1);
+ if(c2 == -1)
+ break;
+
+ out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
+
+ /* c3 */
+ do {
+ c3 = str.charCodeAt(i++) & 0xff;
+ if(c3 == 61)
+ return out;
+ c3 = base64DecodeChars[c3];
+ } while(i < len && c3 == -1);
+ if(c3 == -1)
+ break;
+
+ out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
+
+ /* c4 */
+ do {
+ c4 = str.charCodeAt(i++) & 0xff;
+ if(c4 == 61)
+ return out;
+ c4 = base64DecodeChars[c4];
+ } while(i < len && c4 == -1);
+ if(c4 == -1)
+ break;
+ out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
+ }
+ return out;
+}
+
+function utf16to8(str) {
+ var out, i, len, c;
+
+ out = "";
+ len = str.length;
+ for(i = 0; i < len; i++) {
+ c = str.charCodeAt(i);
+ if ((c >= 0x0001) && (c <= 0x007F)) {
+ out += str.charAt(i);
+ } else if (c > 0x07FF) {
+ out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
+ out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
+ out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
+ } else {
+ out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
+ out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
+ }
+ }
+ return out;
+}
+
+function utf8to16(str) {
+ var out, i, len, c;
+ var char2, char3;
+
+ out = "";
+ len = str.length;
+ i = 0;
+ while(i < len) {
+ c = str.charCodeAt(i++);
+ switch(c >> 4)
+ {
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ // 0xxxxxxx
+ out += str.charAt(i-1);
+ break;
+ case 12: case 13:
+ // 110x xxxx 10xx xxxx
+ char2 = str.charCodeAt(i++);
+ out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
+ break;
+ case 14:
+ // 1110 xxxx 10xx xxxx 10xx xxxx
+ char2 = str.charCodeAt(i++);
+ char3 = str.charCodeAt(i++);
+ out += String.fromCharCode(((c & 0x0F) << 12) |
+ ((char2 & 0x3F) << 6) |
+ ((char3 & 0x3F) << 0));
+ break;
+ }
+ }
+
+ return out;
+}
\ No newline at end of file
diff --git a/plugins/rsyncd/js/rsyncd.js b/plugins/rsyncd/js/rsyncd.js
new file mode 100755
index 000000000..f29c7af07
--- /dev/null
+++ b/plugins/rsyncd/js/rsyncd.js
@@ -0,0 +1,741 @@
+function rsPost(method,args,callback, title){
+
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(toArrayObject(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+
+ var _title = '正在获取...';
+ if (typeof(title) != 'undefined'){
+ _title = title;
+ }
+
+ var loadT = layer.msg(_title, { icon: 16, time: 0, shade: 0.3 });
+ $.post('/plugins/run', {name:'rsyncd', func:method, args:_args}, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+///////////////// ----------------- 发送配置 ---------------- //////////////
+
+function createSendTask(name = ''){
+ var args = {};
+ args["name"] = name;
+ rsPost('lsyncd_get', args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var data = rdata.data;
+ console.log(data);
+
+ var layerName = '创建';
+ if (name!=''){
+ layerName = '编辑';
+ }
+
+ var compress_true = "";
+ var compress_false = "";
+ if (data['rsync']['compress'] == 'true'){
+ compress_true = "selected";
+ compress_false = "";
+ } else {
+ compress_true = "";
+ compress_false = "selected";
+ }
+
+
+ var delete_true = "";
+ var delete_false = "";
+ if (data['delete'] == 'false'){
+ delete_true = "selected";
+ delete_false = "";
+ } else {
+ delete_true = "";
+ delete_false = "selected";
+ }
+
+
+ var realtime_true = "";
+ var realtime_false = "";
+ if (data['realtime'] == 'true'){
+ realtime_true = "selected";
+ realtime_false = "";
+ } else {
+ realtime_true = "";
+ realtime_false = "selected";
+ }
+
+
+ var period_day = "";
+ var period_minute_n = "";
+ if (data['period'] == 'day'){
+ period_day = "selected";
+ period_minute_n = "";
+ } else {
+ period_day = "";
+ period_minute_n = "selected";
+ }
+
+ var bwlimit = "1024";
+ if ('rsync' in data){
+ bwlimit = data['rsync']['bwlimit'];
+ }
+
+ var delay = "3";
+ if ('delay' in data){
+ delay = data['delay'];
+ }
+
+ var layerID = layer.open({
+ type: 1,
+ area: ['600px','500px'],
+ title: layerName+"发送任务",
+ closeBtn: 1,
+ shift: 0,
+ shadeClose: false,
+ btn: ['提交','取消'],
+ content:"\
+ \
+ \
+
同步目录 \
+
\
+ \
+ ? \
+
\
+
\
+ \
+
同步方式 \
+
\
+ \
+ 增量 \
+ 完全 \
+ \
+ ? \
+ 同步周期 \
+ \
+ 实时同步 \
+ 定时同步 \
+ \
+
\
+
\
+ \
+ \
+ \
+
连接方式 \
+
\
+ \
+ 密钥 \
+ 帐号 \
+ \
+ 压缩传输 \
+ \
+ 开启 \
+ 关闭 \
+ \
+ ? \
+
\
+
\
+ \
+
接收密钥 \
+
\
+ "+data['secret_key']+" \
+
\
+
\
+ \
+ \
+ \
+ \
+ ",
+ success:function(){
+ $('[data-toggle="tooltip"]').tooltip();
+
+ $(".conn-user").hide();
+ $("select[name='conn_type']").change(function(){
+ if($(this).val() == 'key'){
+ $(".conn-user").hide();
+ $(".conn-key").show();
+ }else{
+ $(".conn-user").show();
+ $(".conn-key").hide();
+ }
+ });
+
+
+ var selVal = $('.synchronization option:selected').val();
+ if (selVal == "false"){
+ $('#period').show();
+ }else{
+ $('#period').hide();
+ $('.hour input,.minute input').val('0');
+ $('.minute-n input').val('1');
+ }
+ $('.synchronization').change(function(event) {
+ var selVal = $('.synchronization option:selected').val();
+ if (selVal == "false"){
+ $('#period').show();
+ }else{
+ $('#period').hide();
+ $('.hour input,.minute input').val('0');
+ $('.minute-n input').val('1');
+ }
+ });
+
+ $("select[name='delete']").change(function(){
+ if($(this).val() == 'true'){
+ var mpath = $('input[name="path"]').val();
+ var msg = '警告:您选择了完全同步,将会使本机同步与目标机器指定目录的文件保持一致,'
+ +' 请确认目录设置是否有误,一但设置错误,可能导致目标机器的目录文件被删除! '
+ +' 注意: 同步程序将本机目录:'
+ +mpath+'的所有数据同步到目标服务器,若目标服务器的同步目录存在其它文件将被删除! 已了解风险,请按确定继续
';
+
+ layer.confirm(msg,{title:'数据安全风险警告',icon:2,closeBtn: 1,shift: 5,
+ btn2:function(){
+ setTimeout(function(){$($("select[name='delete']").children("option")[0]).prop('selected',true);},100);
+ }
+ });
+ }
+ });
+
+
+ var selVal = $('#period select option:selected').val();
+ if (selVal == 'day'){
+ $('.hour,.minute').show();
+ if ($('.hour input').val() == ''){
+ $('.hour input,.minute input').val('0');
+ }
+ $('.minute-n').hide();
+ }else{
+ $('.hour,.minute').hide();
+ $('.minute-n').show();
+ if ($('.minute-n input').val() == ''){
+ $('.minute-n input').val('1');
+ }
+ }
+ $('#period').change(function(event) {
+ var selVal = $('#period select option:selected').val();
+ if (selVal == 'day'){
+ $('.hour,.minute').show();
+ if ($('.hour input').val() == ''){
+ $('.hour input,.minute input').val('0');
+ }
+ $('.minute-n').hide();
+ }else{
+ $('.hour,.minute').hide();
+ $('.minute-n').show();
+ if ($('.minute-n input').val() == ''){
+ $('.minute-n input').val('1');
+ }
+ }
+ });
+ },
+ yes:function(index){
+ var args = {};
+ var conn_type = $("select[name='conn_type']").val();
+
+ if(conn_type == 'key'){
+ if ( $('textarea[name="secret_key"]').val() != ''){
+ args['secret_key'] = $('textarea[name="secret_key"]').val();
+ } else {
+ layer.msg('请输入接收密钥!');
+ return false;
+ }
+ } else {
+ args['sname'] = $("input[name='u_user']").val();
+ args['password'] = $("input[name='u_pass']").val();
+ var port = Number($("input[name='u_port']").val());
+ args['port'] = port;
+ if (!args['sname'] || !args['password'] || !args['port']){
+ layer.msg('请输入帐号、密码、端口信息');
+ return false;
+ }
+ }
+
+ if ($('input[name="ip"]').val() == ''){
+ layer.msg('请输入服务器IP地址!');
+ return false;
+ }
+
+ args['sname'] = $("input[name='u_user']").val();
+ args['password'] = $("input[name='u_pass']").val();
+ var port = Number($("input[name='u_port']").val());
+ args['port'] = port;
+
+
+ args['ip'] = $('input[name="ip"]').val();
+ args['path'] = $('input[name="path"]').val();
+ args['delete'] = $('select[name="delete"]').val();
+ args['realtime'] = $('select[name="realtime"]').val();
+ args['delay'] = $('input[name="delay"]').val();
+
+ args['bwlimit'] = $('input[name="bwlimit"]').val();
+ args['conn_type'] = $('select[name="conn_type"]').val();
+ args['compress'] = $('select[name="compress"]').val();
+
+ args['period'] = $('select[name="period"]').val();
+ args['hour'] = $('input[name="hour"]').val();
+ args['minute'] = $('input[name="minute"]').val();
+ args['minute-n'] = $('input[name="minute-n"]').val();
+
+ rsPost('lsyncd_add', args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2,time:2000,shade: [0.3, '#000']});
+
+ if (rdata.status){
+ setTimeout(function(){
+ layer.close(index);
+ lsyncdSend();
+ },2000);
+ return;
+ }
+ });
+ return true;
+ }
+ });
+ });
+}
+
+function lsyncdDelete(name){
+ safeMessage('删除['+name+']', '您真的要删除['+name+']吗?', function(){
+ var args = {};
+ args['name'] = name;
+ rsPost('lsyncd_delete', args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2,time:2000,shade: [0.3, '#000']});
+ setTimeout(function(){lsyncdSend();},2000);
+ });
+ });
+}
+
+
+function lsyncdRun(name){
+ var args = {};
+ args["name"] = name;
+ rsPost('lsyncd_run', args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2,time:2000,shade: [0.3, '#000']});
+ });
+}
+
+function lsyncdLog(name){
+ var args = {};
+ args["name"] = name;
+ pluginStandAloneLogs("rsyncd", '', "lsyncd_log", JSON.stringify(args));
+}
+
+
+function lsyncdExclude(name){
+ layer.open({
+ type:1,
+ title:'过滤器',
+ area: '400px',
+ shadeClose:false,
+ closeBtn:2,
+ content:'\
+
\
+
\
+
\
+ 排除的文件和目录是指当前目录下不需要同步的目录或者文件 \
+ 如果规则以斜线 /开头,则从头开始要匹配全部 \
+ 如果规则以 /结尾,则要匹配监控路径的末尾 \
+ ? 匹配任何字符,但不包括/ \
+ * 匹配0或多个字符,但不包括/ \
+ ** 匹配0或多个字符,可以是/ \
+ \
+
\
+
'
+ });
+
+ function getIncludeExclude(mName){
+ loadT = layer.msg('正在获取数据...',{icon:16,time:0,shade: [0.3, '#000']});
+ rsPost('lsyncd_get_exclude',{"name":mName}, function(rdata) {
+ layer.close(loadT);
+
+ var rdata = $.parseJSON(rdata.data);
+ var res = rdata.data;
+
+ var list=''
+ for (var i = 0; i < res.length; i++) {
+ list += ''+ res[i] +' 删除 ';
+ }
+ $('.lsyncd_exclude .BlockList tbody').empty().append(list);
+ });
+ }
+ getIncludeExclude(name);
+
+
+ function addArgs(name,exclude){
+ loadT = layer.msg('正在添加...',{icon:16,time:0,shade: [0.3, '#000']});
+ rsPost('lsyncd_add_exclude', {name:name,exclude:exclude}, function(res){
+ layer.close(loadT);
+
+ console.log('addArgs:',res);
+
+ if (res.status){
+ getIncludeExclude(name);
+ $('.lsyncd_exclude input').val('');
+ layer.msg(res.msg);
+ }else{
+ layer.msg(res.msg);
+ }
+ });
+ }
+ $('.addList').click(function(event) {
+ var val = $(this).prev().val();
+ if(val == ''){
+ layer.msg('当前输入内容为空,请输入');
+ return false;
+ }
+ addArgs(name,val);
+ });
+ $('.lsyncd_exclude input').keyup(function(event){
+ if (event.which == 13){
+ var val = $(this).val();
+ if(val == ''){
+ layer.msg('当前输入内容为空,请输入');
+ return false;
+ }
+ addArgs(name,val);
+ }
+ });
+
+
+ $('.lsyncd_exclude').on('click', '.delList', function(event) {
+ loadT = layer.msg('正在删除...',{icon:16,time:0,shade: [0.3, '#000']});
+ var val = $(this).parent().prev().text();
+ rsPost('lsyncd_remove_exclude',{"name":name,exclude:val}, function(rdata) {
+ layer.close(loadT);
+
+ console.log(rdata)
+ var rdata = $.parseJSON(rdata.data);
+ var res = rdata.data;
+
+ var list=''
+ for (var i = 0; i < res.length; i++) {
+ list += ''+ res[i] +' 删除 ';
+ }
+ $('.lsyncd_exclude .BlockList tbody').empty().append(list);
+ });
+ });
+}
+
+function lsyncdConfLog(){
+ pluginRollingLogs("rsyncd","","lsyncd_conf_log");;
+}
+
+function lsyncdSend(){
+ rsPost('lsyncd_list', '', function(data){
+ var rdata = $.parseJSON(data.data);
+ console.log(rdata);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:rdata.status?1:2,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ var list = rdata.data.list;
+ var con = '';
+
+ con += '\
+ 创建发送任务 \
+ 自动同步日志 \
+
';
+
+ con += '';
+ con += '';
+ con += '名称(标识) ';
+ con += '源目录 ';
+ con += '同步到 ';
+ con += '模式 ';
+ con += '周期 ';
+ con += '操作 ';
+ con += ' ';
+
+ con += '';
+
+
+
+ for (var i = 0; i < list.length; i++) {
+ var mode = '增量';
+ if (list[i]['delete'] == 'true'){
+ mode = '完全';
+ } else {
+ mode = '增量';
+ }
+
+ var period = "实时";
+ if (list[i]['realtime'] == 'true'){
+ period = '实时';
+ } else {
+ period = '定时';
+ }
+
+ con += ''+
+ '' + list[i]['name']+' ' +
+ '' + list[i]['path']+' ' +
+ '' + list[i]['ip']+":"+list[i]['name']+' ' +
+ '' + mode+' ' +
+ '' + period +' ' +
+ '\
+ 同步 \
+ | 手动日志 \
+ | 过滤器 \
+ | 编辑 \
+ | 删除 \
+ \
+ ';
+ }
+
+ con += ' ';
+ con += '
';
+
+ $(".soft-man-con").html(con);
+ });
+}
+
+
+
+///////////////// ----------------- 接收配置 ---------------- //////////////
+function rsyncdConf(){
+ rsPost('conf', {}, function(rdata){
+ rpath = rdata['data'];
+ if (rdata['status']){
+ onlineEditFile(0, rpath);
+ } else {
+ layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']});
+ }
+ });
+}
+
+function rsyncdLog(){
+ pluginRollingLogs("rsyncd","","run_log");
+}
+
+
+function rsyncdReceive(){
+ rsPost('rec_list', '', function(data){
+ var rdata = $.parseJSON(data.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:rdata.status?1:2,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ // console.log(rdata);
+ var list = rdata.data;
+ var con = '';
+
+ con += '\
+ 配置 \
+ 日志 \
+
';
+
+ con += '';
+ con += '';
+ con += '服务名 ';
+ con += '路径 ';
+ con += '备注 ';
+ con += '操作(添加 ) ';
+ con += ' ';
+
+ con += '';
+
+ //编辑
+ for (var i = 0; i < list.length; i++) {
+ con += ''+
+ '' + list[i]['name']+' ' +
+ '' + list[i]['path']+' ' +
+ '' + list[i]['comment']+' ' +
+ '\
+ 命令 \
+ | 密钥 \
+ | 编辑 \
+ | 删除 \
+ ';
+ }
+
+ con += ' ';
+ con += '
';
+
+ $(".soft-man-con").html(con);
+ });
+}
+
+
+function addReceive(name = ""){
+ rsPost('get_rec',{"name":name},function(rdata) {
+ var rdata = $.parseJSON(rdata.data);
+ var data = rdata.data;
+
+ var readonly = "";
+ if (name !=""){
+ readonly = 'readonly="readonly"'
+ }
+
+ var loadOpen = layer.open({
+ type: 1,
+ title: '创建接收',
+ area: '400px',
+ btn:['确认','取消'],
+ content:"",
+ success:function(layero, index){},
+ yes:function(){
+ var args = {};
+ args['name'] = $('#name').val();
+ args['pwd'] = $('#MyPassword').val();
+ args['path'] = $('#inputPath').val();
+ args['ps'] = $('#ps').val();
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+ rsPost('add_rec', args, function(data){
+ var rdata = $.parseJSON(data.data);
+ if (rdata['status']){
+ layer.close(loadOpen);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2,time:2000,shade: [0.3, '#000']});
+ setTimeout(function(){rsyncdReceive();},2000);
+ } else {
+ layer.msg(rdata.msg,{icon:rdata.status?1:2,time:10000,shade: [0.3, '#000']});
+ }
+ });
+ }
+ });
+ })
+}
+
+
+function delReceive(name){
+ safeMessage('删除['+name+']', '您真的要删除['+name+']吗?', function(){
+ var _data = {};
+ _data['name'] = name;
+ rsPost('del_rec', _data, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2,time:2000,shade: [0.3, '#000']});
+ setTimeout(function(){rsyncdReceive();},2000);
+ });
+ });
+}
+
+function cmdRecSecretKey(name){
+ var _data = {};
+ _data['name'] = name;
+ rsPost('cmd_rec_secret_key', _data, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.open({
+ type: 1,
+ title: '接收密钥',
+ area: '400px',
+ content:""+rdata.data+"
"
+ });
+ });
+}
+
+function cmdRecCmd(name){
+ var _data = {};
+ _data['name'] = name;
+ rsPost('cmd_rec_cmd', _data, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.open({
+ type: 1,
+ title: '接收命令例子',
+ area: '400px',
+ content:""+rdata.data+"
"
+ });
+ });
+}
+
+
+function rsRead(){
+ var readme = '';
+ readme += '如需将其他服务器数据同步到本地服务器,请在接受配置中 "创建接收任务" ';
+ readme += '如果开启防火墙,需要放行873端口 ';
+ readme += ' ';
+
+ $('.soft-man-con').html(readme);
+}
\ No newline at end of file
diff --git a/plugins/rsyncd/tool_task.py b/plugins/rsyncd/tool_task.py
new file mode 100644
index 000000000..e38a29626
--- /dev/null
+++ b/plugins/rsyncd/tool_task.py
@@ -0,0 +1,149 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import json
+
+
+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
+from utils.crontab import crontab as MwCrontab
+
+
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'rsyncd'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getTaskConf():
+ conf = getServerDir() + "/task_config.json"
+ return conf
+
+
+def getConfigData():
+ conf = getTaskConf()
+ if os.path.exists(conf):
+ return json.loads(mw.readFile(conf))
+ return []
+
+
+def getConfigTpl():
+ tpl = {
+ "name": "",
+ "task_id": -1,
+ }
+ return tpl
+
+
+def createBgTask(data):
+ removeBgTask()
+ for d in data:
+ if d['realtime'] == "false":
+ createBgTaskByName(d['name'], d)
+
+
+def createBgTaskByName(name, args):
+ cfg = getConfigTpl()
+ _name = "[勿删]同步插件定时任务[" + name + "]"
+ res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
+ if res:
+ return True
+
+ if "task_id" in cfg.keys() and cfg["task_id"] > 0:
+ res = mw.M("crontab").field("id, name").where("id=?", (cfg["task_id"],)).find()
+ if res and res["id"] == cfg["task_id"]:
+ print("计划任务已经存在!")
+ return True
+
+ period = args['period']
+ _hour = ''
+ _minute = ''
+ _where1 = ''
+ _type_day = "day"
+ if period == 'day':
+ _type_day = 'day'
+ _hour = args['hour']
+ _minute = args['minute']
+ elif period == 'minute-n':
+ _type_day = 'minute-n'
+ _where1 = args['minute-n']
+ _minute = ''
+
+ cmd = '''
+rname=%s
+plugin_path=%s
+logs_file=$plugin_path/send/${rname}/run.log
+''' % (name, getServerDir())
+ cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT" >> $logs_file' + "\n"
+ cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n"
+ cmd += 'bash $plugin_path/send/${rname}/cmd >> $logs_file 2>&1' + "\n"
+ cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
+ cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
+
+ params = {
+ 'name': _name,
+ 'type': _type_day,
+ 'week': "",
+ 'where1': _where1,
+ 'hour': _hour,
+ 'minute': _minute,
+ 'save': "",
+ 'backup_to': "",
+ 'stype': "toShell",
+ 'sname': '',
+ 'sbody': cmd,
+ 'url_address': '',
+ }
+
+ task_id = MwCrontab.instance().add(params)
+ if task_id > 0:
+ cfg["task_id"] = task_id
+ cfg["name"] = name
+
+ _dd = getConfigData()
+ _dd.append(cfg)
+ mw.writeFile(getTaskConf(), json.dumps(_dd))
+
+
+def removeBgTask():
+ cfg_list = getConfigData()
+ for x in range(len(cfg_list)):
+ cfg = cfg_list[x]
+ if "task_id" in cfg.keys() and cfg["task_id"] > 0:
+ res = mw.M("crontab").field("id, name").where("id=?", (cfg["task_id"],)).find()
+ if res and res["id"] == cfg["task_id"]:
+ data = MwCrontab.instance().delete(cfg["task_id"])
+ if data[0]:
+ cfg["task_id"] = -1
+ cfg_list[x] = cfg
+ mw.writeFile(getTaskConf(), '[]')
+ return True
+ return False
+
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ action = sys.argv[1]
+ if action == "remove":
+ removeBgTask()
+ elif action == "add":
+ createBgTask()
diff --git a/plugins/simpleping/ico.png b/plugins/simpleping/ico.png
new file mode 100644
index 000000000..348efe3f4
Binary files /dev/null and b/plugins/simpleping/ico.png differ
diff --git a/plugins/simpleping/index.html b/plugins/simpleping/index.html
new file mode 100755
index 000000000..83d25d8f9
--- /dev/null
+++ b/plugins/simpleping/index.html
@@ -0,0 +1,25 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/simpleping/index.py b/plugins/simpleping/index.py
new file mode 100755
index 000000000..6fc8e92fa
--- /dev/null
+++ b/plugins/simpleping/index.py
@@ -0,0 +1,305 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+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 getHomeDir():
+ if mw.isAppleSystem():
+ user = mw.execShell(
+ "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
+ return '/Users/' + user
+ else:
+ return '/root'
+
+
+def getRunUser():
+ if mw.isAppleSystem():
+ user = mw.execShell(
+ "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
+ return user
+ else:
+ return 'root'
+
+__SR = '''#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+export USER=%s
+export HOME=%s && ''' % ( getRunUser(), getHomeDir())
+
+def getPluginName():
+ return 'simpleping'
+
+
+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 getConf():
+ path = getServerDir() + "/conf/app.conf"
+ return path
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
+ return path
+
+
+def getArgs():
+ args = sys.argv[3:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ if t.strip() == '':
+ tmp = []
+ else:
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ 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 configTpl():
+ path = getPluginDir() + '/tpl'
+ pathFile = os.listdir(path)
+ tmp = []
+ for one in pathFile:
+ file = path + '/' + one
+ tmp.append(file)
+ return mw.getJson(tmp)
+
+
+def readConfigTpl():
+ args = getArgs()
+ data = checkArgs(args, ['file'])
+ if not data[0]:
+ return data[1]
+
+ content = mw.readFile(args['file'])
+ content = contentReplace(content)
+ return mw.returnJson(True, 'ok', content)
+
+def getPidFile():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'pidfile\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+def status():
+ data = mw.execShell(
+ "ps aux|grep simpleping |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'")
+
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$SERVER_APP}', service_path + '/simpleping')
+ return content
+
+
+
+def initDreplace():
+
+ file_tpl = getInitDTpl()
+ service_path = mw.getServerDir()
+
+ initD_path = getServerDir() + '/init.d'
+ if not os.path.exists(initD_path):
+ os.mkdir(initD_path)
+ file_bin = initD_path + '/' + getPluginName()
+
+ # print(file_bin)
+ # initd replace
+ if not os.path.exists(file_bin):
+ content = mw.readFile(file_tpl)
+ content = content.replace('{$SERVER_PATH}', service_path)
+ mw.writeFile(file_bin, content)
+ mw.execShell('chmod +x ' + file_bin)
+
+ # systemd
+ # systemctl start simpleping.service
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/' + getPluginName() + '.service'
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl'
+ content = mw.readFile(systemServiceTpl)
+ content = content.replace('{$SERVER_PATH}', service_path)
+ mw.writeFile(systemService, content)
+ mw.execShell('systemctl daemon-reload')
+
+ return file_bin
+
+
+def appOp(method):
+ file = initDreplace()
+
+ current_os = mw.getOs()
+ if current_os == "darwin":
+ data = mw.execShell(__SR + file + ' ' + method)
+ # print(data)
+ return 'ok'
+
+ if current_os.startswith("freebsd"):
+ data = mw.execShell('service ' + getPluginName() + ' ' + method)
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+ data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def start():
+ return appOp('start')
+
+
+def stop():
+ return appOp('stop')
+
+
+def restart():
+ status = appOp('restart')
+ return status
+
+
+def reload():
+ return redisOp('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 ' + \
+ getPluginName() + ' | 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 ' + getPluginName() + '_enable="YES"')
+ return 'ok'
+
+ mw.execShell('systemctl enable ' + getPluginName())
+ 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 ' + getPluginName() + '_enable="NO"')
+ return 'ok'
+
+ mw.execShell('systemctl disable ' + getPluginName())
+ return 'ok'
+
+
+def runLog():
+ return getServerDir() + '/data/log.pl'
+
+
+def ipList():
+ config = getServerDir() + '/conf/app.conf'
+ content = mw.readFile(config)
+ rep = r'ip\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ if not tmp:
+ return ''
+ ip = tmp.groups()[0]
+ ips = ip.split(",")
+ return mw.returnJson(True, 'OK', ips)
+
+if __name__ == "__main__":
+ 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 == 'conf':
+ print(getConf())
+ elif func == 'ip_list':
+ print(ipList())
+ else:
+ print('error')
diff --git a/plugins/simpleping/info.json b/plugins/simpleping/info.json
new file mode 100755
index 000000000..ae3d31e55
--- /dev/null
+++ b/plugins/simpleping/info.json
@@ -0,0 +1,17 @@
+{
+ "sort": 999,
+ "ps": "简单Ping服务,检查连通性",
+ "name": "simpleping",
+ "title": "SimplePing",
+ "shell": "install.sh",
+ "versions":["1.0"],
+ "tip": "soft",
+ "checks": "server/simpleping",
+ "path": "server/simpleping",
+ "display": 1,
+ "author": "midoks",
+ "date": "2024-07-10",
+ "home": "https://github.com/midoks/simpleping",
+ "type": 0,
+ "pid": "5"
+}
diff --git a/plugins/simpleping/init.d/simpleping.service.tpl b/plugins/simpleping/init.d/simpleping.service.tpl
new file mode 100644
index 000000000..f7d3d3411
--- /dev/null
+++ b/plugins/simpleping/init.d/simpleping.service.tpl
@@ -0,0 +1,21 @@
+[Unit]
+Description=SimplePing Server
+After=network.service
+After=syslog.target
+
+[Service]
+User=root
+Group=root
+Type=simple
+WorkingDirectory={$SERVER_PATH}/simpleping
+ExecStart={$SERVER_PATH}/simpleping/simpleping service
+ExecReload=/bin/kill -USR2 $MAINPID
+PermissionsStartOnly=true
+LimitNOFILE=5000
+Restart=on-failure
+RestartSec=10
+RestartPreventExitStatus=1
+PrivateTmp=false
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/plugins/simpleping/init.d/simpleping.tpl b/plugins/simpleping/init.d/simpleping.tpl
new file mode 100644
index 000000000..682ae56bc
--- /dev/null
+++ b/plugins/simpleping/init.d/simpleping.tpl
@@ -0,0 +1,46 @@
+#!/bin/sh
+# chkconfig: 2345 55 25
+# description: SimplePing Service
+
+### BEGIN INIT INFO
+# Provides: SimplePing
+# Required-Start: $all
+# Required-Stop: $all
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts SimplePing
+# Description: starts the MDW-Web
+### END INIT INFO
+
+# Simple SimplePing init.d script conceived to work on Linux systems
+# as it does use of the /proc filesystem.
+
+app_start(){
+ echo "Starting SimplePing server..."
+ cd {$SERVER_PATH}/simpleping
+ ./simpleping service >> {$SERVER_PATH}/simpleping/logs.pl 2>&1 &
+}
+
+app_stop(){
+ echo "SimplePing stopped"
+ ps -ef| grep simpleping | grep -v grep | grep -v python | grep -v sh | awk '{print $2}'| xargs kill
+}
+
+
+case "$1" in
+ start)
+ app_start
+ ;;
+ stop)
+ app_stop
+ ;;
+ restart|reload)
+ app_stop
+ sleep 0.3
+ app_start
+ ;;
+ *)
+ echo "Please use start or stop as first argument"
+ ;;
+esac
+
diff --git a/plugins/simpleping/install.sh b/plugins/simpleping/install.sh
new file mode 100755
index 000000000..7a39c14de
--- /dev/null
+++ b/plugins/simpleping/install.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+ARCH=`uname -m`
+sysName=`uname`
+VERSION=$2
+
+# 开启可以PING
+# sysctl -w net.ipv4.ping_group_range="0 2147483647"
+
+Install_App()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/source
+ mkdir -p $serverPath/source/simpleping
+
+ name=linux
+ if [ "$sysName" == "Darwin" ];then
+ name="darwin"
+ else
+ sysctl -w net.ipv4.ping_group_range="0 2147483647"
+ fi
+
+ if [ "$ARCH" == "x86_64" ];then
+ ARCH="amd64"
+ fi
+
+ FILE_TGZ=simpleping_${name}_${ARCH}.tar.gz
+ APP_DIR=$serverPath/source/simpleping
+
+ # https://github.com/midoks/simpleping/releases/download/1.0/simpleping_linux_amd64.tar.gz
+ if [ ! -f $APP_DIR/${FILE_TGZ} ];then
+ wget -O $APP_DIR/${FILE_TGZ} https://github.com/midoks/simpleping/releases/download/2.0/${FILE_TGZ}
+ fi
+
+ mkdir -p $serverPath/simpleping
+ cd $APP_DIR && tar -zxvf ${FILE_TGZ} -C $serverPath/simpleping
+ echo "${VERSION}" > $serverPath/simpleping/version.pl
+
+ cd ${rootPath} && python3 ${rootPath}/plugins/simpleping/index.py start
+ cd ${rootPath} && python3 ${rootPath}/plugins/simpleping/index.py initd_install
+
+ echo '安装SimplePing成功!'
+
+}
+
+Uninstall_App()
+{
+ if [ -f /usr/lib/systemd/system/simpleping.service ];then
+ systemctl stop simpleping
+ systemctl disable simpleping
+ rm -rf /usr/lib/systemd/system/simpleping.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f /lib/systemd/system/simpleping.service ];then
+ systemctl stop simpleping
+ systemctl disable simpleping
+ rm -rf /lib/systemd/system/simpleping.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/simpleping/initd/simpleping ];then
+ $serverPath/simpleping/initd/simpleping stop
+ fi
+
+ if [ -d $serverPath/simpleping ];then
+ rm -rf $serverPath/simpleping
+ fi
+
+ echo "卸载SimplePing成功"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/simpleping/js/simpleping.js b/plugins/simpleping/js/simpleping.js
new file mode 100755
index 000000000..9c89ec66a
--- /dev/null
+++ b/plugins/simpleping/js/simpleping.js
@@ -0,0 +1,531 @@
+
+function pingPost(method, args, callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'simpleping';
+ req_data['func'] = method;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ //错误展示10S
+ layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function pingPostCallbak(method, args, callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'simpleping';
+ req_data['script'] = 'simpleping_index';
+ req_data['func'] = method;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function pingPostCallbakN(method, args, callback){
+ var req_data = {};
+ req_data['name'] = 'simpleping';
+ req_data['script'] = 'simpleping_index';
+ req_data['func'] = method;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+function appReadme(){
+
+ var readme = '';
+ readme += '简单说明 ';
+ readme += '主要是检查内网连通性 ';
+ readme += ' ';
+ $('.soft-man-con').html(readme);
+}
+
+var chartPing;
+var chartPingData = [];
+var posTimer;
+
+// 把Unix时间戳转化成普通日期
+function toCommonTime(unix_time){
+ var unixTimestamp = new Date(unix_time*1000);
+ var commonTime = unixTimestamp.toLocaleString();
+ return commonTime;
+}
+
+function getToday(){
+ var mydate = new Date();
+ var str = "" + mydate.getFullYear() + "/";
+ str += (mydate.getMonth()+1) + "/";
+ str += mydate.getDate();
+ return str;
+}
+
+//定义周期时间
+function getBeforeDate(n){
+ var n = n;
+ var d = new Date();
+ var year = d.getFullYear();
+ var mon=d.getMonth()+1;
+ var day=d.getDate();
+ if(day <= n){
+ if(mon>1) {
+ mon = mon-1;
+ } else {
+ year = year-1;
+ mon = 12;
+ }
+ }
+ d.setDate(d.getDate()-n);
+ year = d.getFullYear();
+ mon=d.getMonth()+1;
+ day=d.getDate();
+ s = year+"/"+(mon<10?('0'+mon):mon)+"/"+(day<10?('0'+day):day);
+ return s;
+}
+
+
+function pingDataGraphPosData(){
+
+ var isOk = document.getElementById('pingview');
+ if (!isOk){
+ clearInterval(posTimer);
+ }
+
+ if (chartPingData.length>0){
+ var dlen = chartPingData.length;
+ last_pos = chartPingData[dlen-1];
+ // console.log(start,end);
+ var cur_ip = $('select[name="ip_list"]').val();
+ pingPostCallbakN('pingData', {'type':'pos', 'pos':last_pos['created_unix'],"ip":cur_ip}, function(data){
+ var tmp_data = data.data;
+ for (x in tmp_data){
+ chartPingData.push(tmp_data[x]);
+ }
+ pingDataGraphRender();
+ });
+
+ }
+}
+
+
+function pingDataGraphData(day){
+
+ var now = (new Date().getTime())/1000;
+ if(day==0){
+ var start = (new Date(getToday() + " 00:00:01").getTime())/1000;
+ start = Math.round(start);
+ var end = Math.round(now);
+ }
+ if(day==1){
+ var start = (new Date(getBeforeDate(day) + " 00:00:01").getTime())/1000;
+ var end = (new Date(getBeforeDate(day) + " 23:59:59").getTime())/1000;
+ start = Math.round(start);
+ end = Math.round(end);
+ } else {
+ var start = (new Date(getBeforeDate(day) + " 00:00:01").getTime())/1000;
+ start = Math.round(start);
+ var end = Math.round(now);
+ }
+
+ var cur_ip = $('select[name="ip_list"]').val();
+ // console.log(start,end);
+ pingPostCallbak('pingData', {'type':'range', 'start':start, 'end':end, 'ip':cur_ip}, function(data){
+ chartPingData = data.data;
+ pingDataGraphRender();
+
+ if (day!=1){
+ clearInterval(posTimer);
+ posTimer = setInterval(function() {
+ pingDataGraphPosData();
+ }, 3000);
+ }
+ });
+}
+
+function pingDataGraphRender(){
+ var xData = [];
+ var yData = [];
+ var rdata = chartPingData;
+ for(var i = 0; i < rdata.length; i++){
+ xData.push(toCommonTime(rdata[i].created_unix));
+ yData.push(rdata[i].speed/1000000);
+ }
+ var option = {
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: { type: 'cross' },
+ formatter: '{b} {a}: {c}'
+ },
+ xAxis: {
+ type: 'category',
+ boundaryGap: false,
+ data: xData,
+ axisLine:{ lineStyle:{ color:"#666"} }
+ },
+ yAxis: {
+ type: 'value',
+ name: "PING延迟(ms)",
+ // boundaryGap: [0, '100%'],
+ // min:0,
+ // max: 100,
+ splitLine:{ lineStyle:{ color:"#ddd" } },
+ axisLine:{ lineStyle:{ color:"#666" } }
+ },
+ series: [
+ {
+ name:'PING',
+ type:'line',
+ smooth:true,
+ symbol: 'none',
+ sampling: 'average',
+ itemStyle: { normal: { color: 'rgb(0, 153, 238)' } },
+ data: yData
+ }
+ ]
+ };
+ chartPing.setOption(option);
+}
+
+function pingIpList(){
+ pingPost('ip_list', {}, function(data){
+ var rdata = $.parseJSON(data.data);
+ var ips = rdata.data;
+
+ var option = '';
+ option += '所有 ';
+
+ for (var i = 0; i < ips.length; i++) {
+ option += ''+ips[i]+' ';
+ }
+ $('select[name="ip_list"]').html(option);
+
+
+ $('select[name="ip_list"]').change(function(){
+ chartPingData = [];
+ pingDataGraphData(0);
+ });
+
+ pingDataGraphData(0);
+ });
+
+}
+
+// console.log('pingDataGraph');
+function pingDataGraph(){
+ var tpl = '\
+ \
+
\
+
\
+
\
+
连通性 \
+
\
+ \
+ 所有 \
+ \
+ 昨天 \
+ 今天 \
+ 最近7天 \
+
\
+
\
+
\
+
\
+
\
+
';
+ $('.soft-man-con').html(tpl);
+
+ $('.searcTime span').click(function(e){
+ $('.searcTime span').removeClass('on');
+ $(this).addClass('on');
+ });
+
+ chartPing = echarts.init(document.getElementById('pingview'));
+
+ var option = {
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: { type: 'cross' },
+ formatter: '{b} {a}: {c}'
+ },
+ xAxis: {
+ type: 'category',
+ boundaryGap: false,
+ data: [],
+ axisLine:{ lineStyle:{ color:"#666"} }
+ },
+ yAxis: {
+ type: 'value',
+ name: "PING延迟(ms)",
+ splitLine:{ lineStyle:{ color:"#ddd" } },
+ axisLine:{ lineStyle:{ color:"#666" } }
+ },
+ dataZoom: [{
+ type: 'inside',
+ start: 0,
+ end: 100,
+ zoomLock:true
+ }, {
+ start: 0,
+ end: 100,
+ handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
+ handleSize: '80%',
+ handleStyle: {
+ color: '#fff',
+ shadowBlur: 3,
+ shadowColor: 'rgba(0, 0, 0, 0.6)',
+ shadowOffsetX: 2,
+ shadowOffsetY: 2
+ }
+ }],
+ series: [
+ {
+ name:'PING',
+ type:'line',
+ smooth:true,
+ symbol: 'none',
+ sampling: 'average',
+ itemStyle: { normal: { color: 'rgb(0, 153, 238)' } },
+ data: []
+ }
+ ]
+ };
+ chartPing.setOption(option);
+ window.addEventListener("resize",function(){
+ chartPing.resize();
+ });
+
+ pingIpList();
+}
+
+
+
+
+
+// --------------------------------------------------------------------------------------------------------------
+// MYSQL PING
+// --------------------------------------------------------------------------------------------------------------
+
+var chartMySQLPingData = [];
+var chartMySQLPing;
+var posMySQLTimer;
+
+function pingMySQLDataGraphPosData(){
+
+ var isOk = document.getElementById('mysqlview');
+ if (!isOk){
+ clearInterval(posMySQLTimer);
+ }
+
+ if (chartMySQLPingData.length>0){
+ var dlen = chartMySQLPingData.length;
+ last_pos = chartMySQLPingData[dlen-1];
+ // console.log(start,end);
+ pingPostCallbakN('pingMySQLData', {'type':'pos', 'pos':last_pos['created_unix']}, function(data){
+ var tmp_data = data.data;
+ for (x in tmp_data){
+ chartMySQLPingData.push(tmp_data[x]);
+ }
+ pingDataMySQLGraphRender();
+ });
+
+ }
+}
+
+function pingDataMySQLGraphRender(){
+ var xData = [];
+ var yData = [];
+ var rdata = chartMySQLPingData;
+ for(var i = 0; i < rdata.length; i++){
+ xData.push(toCommonTime(rdata[i].created_unix));
+ yData.push(rdata[i].value);
+ }
+ var option = {
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: { type: 'cross' },
+ formatter: '{b} {a}: {c}'
+ },
+ xAxis: {
+ type: 'category',
+ boundaryGap: false,
+ data: xData,
+ axisLine:{ lineStyle:{ color:"#666"} }
+ },
+ yAxis: {
+ type: 'value',
+ name: "MySQL同步延迟",
+ splitLine:{ lineStyle:{ color:"#ddd" } },
+ axisLine:{ lineStyle:{ color:"#666" } }
+ },
+ series: [
+ {
+ name:'同步延迟',
+ type:'line',
+ smooth:true,
+ symbol: 'none',
+ sampling: 'average',
+ itemStyle: { normal: { color: 'rgb(0, 153, 238)' } },
+ data: yData
+ }
+ ]
+ };
+ chartMySQLPing.setOption(option);
+}
+
+function pingMySQLDataGraphData(day){
+
+ var now = (new Date().getTime())/1000;
+ if(day==0){
+ var start = (new Date(getToday() + " 00:00:01").getTime())/1000;
+ start = Math.round(start);
+ var end = Math.round(now);
+ }
+ if(day==1){
+ var start = (new Date(getBeforeDate(day) + " 00:00:01").getTime())/1000;
+ var end = (new Date(getBeforeDate(day) + " 23:59:59").getTime())/1000;
+ start = Math.round(start);
+ end = Math.round(end);
+ } else {
+ var start = (new Date(getBeforeDate(day) + " 00:00:01").getTime())/1000;
+ start = Math.round(start);
+ var end = Math.round(now);
+ }
+
+ var cur_ip = $('select[name="ip_list"]').val();
+ // console.log(start,end);
+ pingPostCallbak('pingMySQLData', {'type':'range', 'start':start, 'end':end, 'ip':cur_ip}, function(data){
+ chartMySQLPingData = data.data;
+ pingDataMySQLGraphRender();
+ if (day!=1){
+ clearInterval(posMySQLTimer);
+ posMySQLTimer = setInterval(function() {
+ pingMySQLDataGraphPosData();
+ }, 3000);
+ }
+ });
+}
+
+
+function pingMySQLDataGraph(){
+ var tpl = '\
+ \
+
\
+
\
+
\
+
MySQL检查 \
+
\
+ 昨天 \
+ 今天 \
+ 最近7天 \
+
\
+
\
+
\
+
\
+
\
+
';
+ $('.soft-man-con').html(tpl);
+
+ $('.searcTime span').click(function(e){
+ $('.searcTime span').removeClass('on');
+ $(this).addClass('on');
+ });
+
+ chartMySQLPing = echarts.init(document.getElementById('mysqlview'));
+
+ var option = {
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: { type: 'cross' },
+ formatter: '{b} {a}: {c}'
+ },
+ xAxis: {
+ type: 'category',
+ boundaryGap: false,
+ data: [],
+ axisLine:{ lineStyle:{ color:"#666"} }
+ },
+ yAxis: {
+ type: 'value',
+ name: "MySQL延迟",
+ splitLine:{ lineStyle:{ color:"#ddd" } },
+ axisLine:{ lineStyle:{ color:"#666" } }
+ },
+ dataZoom: [{
+ type: 'inside',
+ start: 0,
+ end: 100,
+ zoomLock:true
+ }, {
+ start: 0,
+ end: 100,
+ handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
+ handleSize: '80%',
+ handleStyle: {
+ color: '#fff',
+ shadowBlur: 3,
+ shadowColor: 'rgba(0, 0, 0, 0.6)',
+ shadowOffsetX: 2,
+ shadowOffsetY: 2
+ }
+ }],
+ series: [
+ {
+ name:'延迟',
+ type:'line',
+ smooth:true,
+ symbol: 'none',
+ sampling: 'average',
+ itemStyle: { normal: { color: 'rgb(0, 153, 238)' } },
+ data: []
+ }
+ ]
+ };
+ chartMySQLPing.setOption(option);
+ window.addEventListener("resize",function(){
+ chartMySQLPing.resize();
+ });
+
+ pingMySQLDataGraphData(0);
+}
+
+
diff --git a/plugins/simpleping/simpleping_index.py b/plugins/simpleping/simpleping_index.py
new file mode 100755
index 000000000..4d6292d81
--- /dev/null
+++ b/plugins/simpleping/simpleping_index.py
@@ -0,0 +1,72 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import shutil
+
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+def getPluginName():
+ return 'simpleping'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+def pingData(args = ()):
+ # print(args)
+ conn = mw.M('sp_ping').dbPos(getServerDir()+'/data', 'simpleping', 'db3')
+ field = 'id,speed,created_unix'
+ conn = conn.field(field)
+ data = []
+ atype = args['type']
+
+ if not 'ip' in args:
+ ip = 'all'
+ else:
+ ip = args['ip']
+
+ if atype == 'pos':
+ if 'pos' in args:
+ pos = args['pos']
+ conn.where('created_unix>?',(pos,)).limit("3000")
+ if ip != 'all':
+ conn.andWhere('ip=?',(ip,))
+ elif atype == 'range':
+ start = args['start']
+ end = args['end']
+ conn.where('created_unix>=? and created_unix<=?',(start,end,)).limit("1000")
+ if ip != 'all':
+ conn.andWhere('ip=?',(ip,))
+
+ data = conn.select()
+ return data
+
+
+def pingMySQLData(args = ()):
+ conn = mw.M('sp_mysql_ping').dbPos(getServerDir()+'/data', 'simpleping', 'db3')
+ field = 'id,value,created_unix'
+ conn = conn.field(field)
+ data = []
+ atype = args['type']
+ if atype == 'pos':
+ pos = args['pos']
+ data = conn.where('created_unix>?',(pos,)).limit("3000").select()
+ elif atype == 'range':
+ start = args['start']
+ end = args['end']
+ data = conn.where('created_unix>=? and created_unix<=?',(start,end)).limit("1000").select()
+ return data
+
diff --git a/plugins/sphinx/class/sphinx_make.py b/plugins/sphinx/class/sphinx_make.py
new file mode 100644
index 000000000..239064fc5
--- /dev/null
+++ b/plugins/sphinx/class/sphinx_make.py
@@ -0,0 +1,490 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import subprocess
+import re
+import json
+
+
+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
+
+
+def getServerDir():
+ return mw.getServerDir() + '/mysql'
+
+def getPluginDir():
+ return mw.getPluginDir() + '/mysql'
+
+def getConf():
+ path = getServerDir() + '/etc/my.cnf'
+ return path
+
+def getDbPort():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'port\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+def getSocketFile():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'socket\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+def pSqliteDb(dbname='databases'):
+ file = getServerDir() + '/mysql.db'
+ name = 'mysql'
+
+ conn = mw.M(dbname).dbPos(getServerDir(), name)
+ return conn
+
+def pMysqlDb():
+ # pymysql
+ db = mw.getMyORM()
+
+ db.setPort(getDbPort())
+ db.setSocket(getSocketFile())
+ # db.setCharset("utf8")
+ db.setPwd(pSqliteDb('config').where('id=?', (1,)).getField('mysql_root'))
+ return db
+
+class sphinxMake():
+
+ pdb = None
+ psdb = None
+
+ pkey_name_cache = {}
+ delta = 'sph_counter'
+ ver = ''
+
+
+ def __init__(self):
+ self.pdb = pMysqlDb()
+
+ def setDeltaName(self, name):
+ self.delta = name
+ return True
+
+ def setVersion(self, ver):
+ self.ver = ver
+
+ def createSql(self, db):
+ conf = '''
+CREATE TABLE IF NOT EXISTS `{$DB_NAME}`.`{$TABLE_NAME}` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `table` varchar(200) NOT NULL,
+ `max_id` bigint(20) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `table_uniq` (`table`),
+ KEY `table` (`table`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4;
+'''
+ conf = conf.replace("{$TABLE_NAME}", self.delta)
+ conf = conf.replace("{$DB_NAME}", db)
+ return conf
+
+ def eqVerField(self, field):
+ ver = self.ver.replace(".1",'')
+ if float(ver) >= 3.6:
+ if field == 'sql_attr_timestamp':
+ return 'attr_bigint'
+
+ if field == 'sql_attr_bigint':
+ return 'attr_bigint'
+
+ if field == 'sql_attr_float':
+ return 'attr_float'
+
+ if field == 'sql_field_string':
+ return 'field_string'
+
+ if float(ver) >= 3.3:
+ if field == 'sql_attr_timestamp':
+ return 'sql_attr_bigint'
+
+ return field
+
+ def pathVerName(self):
+ ver = self.ver.replace(".1",'')
+ # if float(ver) >= 3.6:
+ # return 'datadir'
+ return 'path'
+
+ def getTablePk(self, db, table):
+ key = db+'_'+table
+ if key in self.pkey_name_cache:
+ return self.pkey_name_cache[key]
+
+ # SHOW INDEX FROM bbs.bbs_ucenter_vars WHERE Key_name = 'PRIMARY'
+ pkey_sql = "SHOW INDEX FROM {}.{} WHERE Key_name = 'PRIMARY';".format(db,table,);
+ pkey_data = self.pdb.query(pkey_sql)
+
+ # print(db, table)
+ # print(pkey_data)
+ key = ''
+ if len(pkey_data) == 1:
+ pkey_name = pkey_data[0]['Column_name']
+ sql = "select COLUMN_NAME,DATA_TYPE from information_schema.COLUMNS where `TABLE_SCHEMA`='{}' and `TABLE_NAME` = '{}' and `COLUMN_NAME`='{}';"
+ sql = sql.format(db,table,pkey_name,)
+ # print(sql)
+ fields = self.pdb.query(sql)
+
+ if len(fields) == 1:
+ # print(fields[0]['DATA_TYPE'])
+ if mw.inArray(['bigint','smallint','tinyint','int','mediumint'], fields[0]['DATA_TYPE']):
+ key = pkey_name
+ return key
+
+
+ def getTableFieldStr(self, db, table):
+ sql = "select COLUMN_NAME,DATA_TYPE from information_schema.COLUMNS where `TABLE_SCHEMA`='{}' and `TABLE_NAME` = '{}';"
+ sql = sql.format(db,table,)
+ fields = self.pdb.query(sql)
+
+ field_str = ''
+ for x in range(len(fields)):
+ field_str += '`'+fields[x]['COLUMN_NAME']+'`,'
+
+ field_str = field_str.strip(',')
+ return field_str
+
+ def makeSphinxHeader(self):
+ conf = '''
+indexer
+{
+ mem_limit = 128M
+}
+
+searchd
+{
+ listen = 9312
+ listen = 9306:mysql41
+ log = {$server_dir}/sphinx/index/searchd.log
+ query_log = {$server_dir}/sphinx/index/query.log
+ read_timeout = 5
+ max_children = 0
+ pid_file = {$server_dir}/sphinx/index/searchd.pid
+ seamless_rotate = 1
+ preopen_indexes = 1
+ unlink_old = 1
+ #workers = threads # for RT to work
+ binlog_path = {$server_dir}/sphinx/index/binlog
+}
+ '''
+ conf = conf.replace("{$server_dir}", mw.getServerDir())
+ return conf
+
+ def makeSphinxDbSourceRangeSql(self, db, table):
+ pkey_name = self.getTablePk(db,table)
+ sql = "SELECT min("+pkey_name+"), max("+pkey_name+") FROM "+table
+ return sql
+
+ def makeSphinxDbSourceQuerySql(self, db, table):
+ pkey_name = self.getTablePk(db,table)
+ field_str = self.getTableFieldStr(db,table)
+ # print(field_str)
+ if pkey_name == 'id':
+ sql = "SELECT " + field_str + " FROM " + table + " where id >= $start AND id <= $end"
+ else:
+ sql = "SELECT `"+pkey_name+'` as `id`,' + field_str + " FROM " + table + " where "+pkey_name+" >= $start AND "+pkey_name+" <= $end"
+ return sql
+
+
+ def makeSphinxDbSourceDeltaRange(self, db, table):
+ pkey_name = self.getTablePk(db,table)
+ conf = "SELECT (SELECT max_id FROM `{$SPH_TABLE}` where `table`='{$TABLE_NAME}') as min, (SELECT max({$PK_NAME}) FROM {$TABLE_NAME}) as max"
+ conf = conf.replace("{$DB_NAME}", db)
+ conf = conf.replace("{$TABLE_NAME}", table)
+ conf = conf.replace("{$SPH_TABLE}", self.delta)
+ conf = conf.replace("{$PK_NAME}", pkey_name)
+ return conf
+
+ def makeSphinxDbSourcePost(self, db, table):
+ pkey_name = self.getTablePk(db,table)
+ conf = "sql_query_post = UPDATE {$SPH_TABLE} SET max_id=(SELECT MAX({$PK_NAME}) FROM {$TABLE_NAME}) where `table`='{$TABLE_NAME}'"
+ # conf = "REPLACE INTO {$SPH_TABLE} (`table`,`max_id`) VALUES ('{$TABLE_NAME}',(SELECT MAX({$PK_NAME}) FROM {$TABLE_NAME}))"
+ conf = conf.replace("{$DB_NAME}", db)
+ conf = conf.replace("{$TABLE_NAME}", table)
+ conf = conf.replace("{$SPH_TABLE}", self.delta)
+ conf = conf.replace("{$PK_NAME}", pkey_name)
+ return conf
+
+ def makeSphinxDbSourceDelta(self, db, table):
+ conf = '''
+source {$DB_NAME}_{$TABLE_NAME}_delta:{$DB_NAME}_{$TABLE_NAME}
+{
+ sql_query_pre = SET NAMES utf8
+ sql_query_range = {$DELTA_RANGE}
+ sql_query = {$DELTA_QUERY}
+ {$DELTA_UPDATE}
+
+{$SPH_FIELD}
+}
+
+index {$DB_NAME}_{$TABLE_NAME}_delta:{$DB_NAME}_{$TABLE_NAME}
+{
+ source = {$DB_NAME}_{$TABLE_NAME}_delta
+ {$PATH_NAME} = {$server_dir}/sphinx/index/db/{$DB_NAME}.{$TABLE_NAME}/delta
+
+ html_strip = 1
+ ngram_len = 1
+ ngram_chars = U+3000..U+2FA1F
+
+{$SPH_FIELD_INDEX}
+}
+''';
+ conf = conf.replace("{$server_dir}", mw.getServerDir())
+ conf = conf.replace("{$PATH_NAME}", self.pathVerName())
+
+ conf = conf.replace("{$DB_NAME}", db)
+ conf = conf.replace("{$TABLE_NAME}", table)
+
+ delta_range = self.makeSphinxDbSourceDeltaRange(db, table)
+ conf = conf.replace("{$DELTA_RANGE}", delta_range)
+
+ delta_query = self.makeSphinxDbSourceQuerySql(db, table)
+ conf = conf.replace("{$DELTA_QUERY}", delta_query)
+
+ delta_update = self.makeSphinxDbSourcePost(db, table)
+ conf = conf.replace("{$DELTA_UPDATE}", delta_update)
+
+
+ sph_field = self.makeSqlToSphinxTable(db, table)
+ conf = self.makeSphinxDbFieldRepalce(conf, sph_field)
+
+ return conf;
+
+ def makeSphinxDbSource(self, db, table, create_sphinx_table = False):
+ db_info = pSqliteDb('databases').field('username,password').where('name=?', (db,)).find()
+ port = getDbPort()
+
+ conf = '''
+source {$DB_NAME}_{$TABLE_NAME}
+{
+ type = mysql
+ sql_host = 127.0.0.1
+ sql_user = {$DB_USER}
+ sql_pass = {$DB_PASS}
+ sql_db = {$DB_NAME}
+ sql_port = {$DB_PORT}
+
+ sql_query_pre = SET NAMES utf8
+
+ {$UPDATE}
+
+ sql_query_range = {$DB_RANGE_SQL}
+ sql_range_step = 1000
+
+ sql_query = {$DB_QUERY_SQL}
+
+{$SPH_FIELD}
+}
+
+index {$DB_NAME}_{$TABLE_NAME}
+{
+ source = {$DB_NAME}_{$TABLE_NAME}
+ {$PATH_NAME} = {$server_dir}/sphinx/index/db/{$DB_NAME}.{$TABLE_NAME}/index
+
+ ngram_len = 1
+ ngram_chars = U+3000..U+2FA1F
+
+{$SPH_FIELD_INDEX}
+}
+ '''
+ conf = conf.replace("{$server_dir}", mw.getServerDir())
+ conf = conf.replace("{$PATH_NAME}", self.pathVerName())
+
+ conf = conf.replace("{$DB_NAME}", db)
+ conf = conf.replace("{$TABLE_NAME}", table)
+ conf = conf.replace("{$DB_USER}", db_info['username'])
+ conf = conf.replace("{$DB_PASS}", db_info['password'])
+ conf = conf.replace("{$DB_PORT}", port)
+
+ range_sql = self.makeSphinxDbSourceRangeSql(db, table)
+ conf = conf.replace("{$DB_RANGE_SQL}", range_sql)
+
+ query_sql = self.makeSphinxDbSourceQuerySql(db, table)
+ conf = conf.replace("{$DB_QUERY_SQL}", query_sql)
+
+ sph_field = self.makeSqlToSphinxTable(db, table)
+ # conf = conf.replace("{$SPH_FIELD}", sph_field)
+
+
+ conf = self.makeSphinxDbFieldRepalce(conf, sph_field)
+
+ if create_sphinx_table:
+ update = self.makeSphinxDbSourcePost(db, table)
+ conf = conf.replace("{$UPDATE}", update)
+ else:
+ conf = conf.replace("{$UPDATE}", '')
+
+ if create_sphinx_table:
+ sph_sql = self.createSql(db)
+ self.pdb.query(sph_sql)
+ sql_find = "select * from {}.{} where `table`='{}'".format(db,self.delta,table)
+ find_data = self.pdb.query(sql_find)
+ if len(find_data) == 0:
+ insert_sql = "insert into `{}`.`{}`(`table`,`max_id`) values ('{}',{}) ".format(db,self.delta,table,0)
+ # print(insert_sql)
+ self.pdb.execute(insert_sql)
+ conf += self.makeSphinxDbSourceDelta(db,table)
+
+ # print(ver)
+ # print(conf)
+
+ return conf
+
+ def makeSphinxDbFieldRepalce(self, content, sph_field):
+ ver = self.ver.replace(".1",'')
+ ver = float(ver)
+ if ver >= 3.6:
+ content = content.replace("{$SPH_FIELD}", '')
+ content = content.replace("{$SPH_FIELD_INDEX}", '')
+ else:
+ content = content.replace("{$SPH_FIELD}", sph_field)
+ content = content.replace("{$SPH_FIELD_INDEX}", '')
+
+ return content
+
+
+ def makeSqlToSphinxDb(self, db, table = [], is_delta = False):
+ conf = ''
+
+
+ for tn in table:
+ pkey_name = self.getTablePk(db,tn)
+ if pkey_name == '':
+ continue
+ conf += self.makeSphinxDbSource(db, tn,is_delta)
+
+ if len(table) == 0:
+ tables = self.pdb.query("show tables in "+ db)
+ for x in range(len(tables)):
+ key = 'Tables_in_'+db
+ table_name = tables[x][key]
+ pkey_name = self.getTablePk(db, table_name, is_delta)
+ if pkey_name == '':
+ continue
+
+ if self.makeSqlToSphinxTableIsHaveFulltext(db, table_name):
+ conf += self.makeSphinxDbSource(db, table_name)
+ return conf
+
+ def makeSqlToSphinxTableIsHaveFulltext(self, db, table):
+ sql = "select COLUMN_NAME,DATA_TYPE from information_schema.COLUMNS where `TABLE_SCHEMA`='{}' and `TABLE_NAME` = '{}';"
+ sql = sql.format(db,table,)
+ cols = self.pdb.query(sql)
+ cols_len = len(cols)
+
+ for x in range(cols_len):
+ data_type = cols[x]['DATA_TYPE']
+ column_name = cols[x]['COLUMN_NAME']
+
+ if mw.inArray(['varchar'], data_type):
+ return True
+ if mw.inArray(['text','mediumtext','tinytext','longtext'], data_type):
+ return True
+ return False
+
+ def makeSqlToSphinxTable(self,db,table):
+ pkey_name = self.getTablePk(db,table)
+ sql = "select COLUMN_NAME,DATA_TYPE from information_schema.COLUMNS where `TABLE_SCHEMA`='{}' and `TABLE_NAME` = '{}';"
+ sql = sql.format(db,table,)
+ cols = self.pdb.query(sql)
+ cols_len = len(cols)
+ conf = ''
+ run_pos = 0
+ for x in range(cols_len):
+ data_type = cols[x]['DATA_TYPE']
+ column_name = cols[x]['COLUMN_NAME']
+ # print(column_name+":"+data_type)
+
+ # if mw.inArray(['tinyint'], data_type):
+ # conf += 'sql_attr_bool = '+ column_name + "\n"
+
+ if pkey_name == column_name:
+ # run_pos += 1
+ # conf += '\tsql_attr_bigint = '+column_name+"\n"
+ continue
+
+ if mw.inArray(['enum'], data_type):
+ run_pos += 1
+ conf += '\t'+self.eqVerField('sql_attr_string')+' = '+ column_name + "\n"
+ continue
+
+ if mw.inArray(['decimal'], data_type):
+ run_pos += 1
+ conf += '\t'+self.eqVerField('sql_attr_float')+' = '+ column_name + "\n"
+ continue
+
+ if mw.inArray(['bigint','smallint','tinyint','int','mediumint'], data_type):
+ run_pos += 1
+ conf += '\t'+self.eqVerField('sql_attr_bigint')+' = '+ column_name + "\n"
+ continue
+
+
+ if mw.inArray(['float'], data_type):
+ run_pos += 1
+ conf += '\t'+self.eqVerField('sql_attr_float')+' = '+ column_name + "\n"
+ continue
+
+ if mw.inArray(['char'], data_type):
+ conf += '\t'+self.eqVerField('sql_attr_string')+' = '+ column_name + "\n"
+ continue
+
+ if mw.inArray(['varchar'], data_type):
+ run_pos += 1
+ conf += '\t'+self.eqVerField('sql_field_string')+' = '+ column_name + "\n"
+ continue
+
+ if mw.inArray(['text','mediumtext','tinytext','longtext'], data_type):
+ run_pos += 1
+ conf += '\t'+self.eqVerField('sql_field_string')+' = '+ column_name + "\n"
+ continue
+
+ if mw.inArray(['datetime','date'], data_type):
+ run_pos += 1
+ conf += '\t'+self.eqVerField('sql_attr_timestamp')+' = '+ column_name + "\n"
+ continue
+
+ return conf
+
+ def checkDbName(self, db):
+ filter_db = ['information_schema','performance_schema','sys','mysql']
+ if db in filter_db:
+ return False
+ return True
+
+ def makeSqlToSphinx(self, db, tables = [], is_delta = False):
+ conf = ''
+ conf += self.makeSphinxHeader()
+ conf += self.makeSqlToSphinxDb(db, tables, is_delta)
+ return conf
+
+ def makeSqlToSphinxAll(self):
+ filter_db = ['information_schema','performance_schema','sys','mysql']
+
+ dblist = self.pdb.query('show databases')
+
+ conf = ''
+ conf += self.makeSphinxHeader()
+
+ # conf += makeSqlToSphinxDb(pdb, 'bbs')
+ for x in range(len(dblist)):
+ dbname = dblist[x]['Database']
+ if mw.inArray(filter_db, dbname):
+ continue
+ conf += self.makeSqlToSphinxDb(dbname)
+ return conf
+
+
diff --git a/plugins/sphinx/class/sphinxapi.py b/plugins/sphinx/class/sphinxapi.py
new file mode 100644
index 000000000..88f5afde4
--- /dev/null
+++ b/plugins/sphinx/class/sphinxapi.py
@@ -0,0 +1,1256 @@
+#
+# $Id$
+#
+# Python version of Sphinx searchd client (Python API)
+#
+# Copyright (c) 2006, Mike Osadnik
+# Copyright (c) 2006-2016, Andrew Aksyonoff
+# Copyright (c) 2008-2016, Sphinx Technologies Inc
+# All rights reserved
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Library General Public License. You should
+# have received a copy of the LGPL license along with this program; if you
+# did not, you can find it at http://www.gnu.org/
+#
+# WARNING!!!
+#
+# As of 2015, we strongly recommend to use either SphinxQL or REST APIs
+# rather than the native SphinxAPI.
+#
+# While both the native SphinxAPI protocol and the existing APIs will
+# continue to exist, and perhaps should not even break (too much), exposing
+# all the new features via multiple different native API implementations
+# is too much of a support complication for us.
+#
+# That said, you're welcome to overtake the maintenance of any given
+# official API, and remove this warning ;)
+#
+
+from __future__ import print_function
+import sys
+import select
+import socket
+import re
+from struct import *
+
+if sys.version_info > (3,):
+ long = int
+ text_type = str
+else:
+ text_type = unicode
+
+# known searchd commands
+SEARCHD_COMMAND_SEARCH = 0
+SEARCHD_COMMAND_EXCERPT = 1
+SEARCHD_COMMAND_UPDATE = 2
+SEARCHD_COMMAND_KEYWORDS = 3
+SEARCHD_COMMAND_PERSIST = 4
+SEARCHD_COMMAND_STATUS = 5
+SEARCHD_COMMAND_FLUSHATTRS = 7
+
+# current client-side command implementation versions
+VER_COMMAND_SEARCH = 0x120
+VER_COMMAND_EXCERPT = 0x104
+VER_COMMAND_UPDATE = 0x103
+VER_COMMAND_KEYWORDS = 0x100
+VER_COMMAND_STATUS = 0x101
+VER_COMMAND_FLUSHATTRS = 0x100
+
+# known searchd status codes
+SEARCHD_OK = 0
+SEARCHD_ERROR = 1
+SEARCHD_RETRY = 2
+SEARCHD_WARNING = 3
+
+# known ranking modes (extended2 mode only)
+SPH_RANK_PROXIMITY_BM15 = 0 # default mode, phrase proximity major factor and BM15 minor one
+SPH_RANK_BM15 = 1 # statistical mode, BM15 ranking only (faster but worse quality)
+SPH_RANK_NONE = 2 # no ranking, all matches get a weight of 1
+SPH_RANK_WORDCOUNT = 3 # simple word-count weighting, rank is a weighted sum of per-field keyword occurence counts
+SPH_RANK_PROXIMITY = 4
+SPH_RANK_MATCHANY = 5
+SPH_RANK_FIELDMASK = 6
+SPH_RANK_SPH04 = 7
+SPH_RANK_EXPR = 8
+SPH_RANK_TOTAL = 9
+
+# aliases; to be retired
+SPH_RANK_PROXIMITY_BM25 = 0
+SPH_RANK_BM25 = 1
+
+# known sort modes
+SPH_SORT_RELEVANCE = 0
+SPH_SORT_ATTR_DESC = 1
+SPH_SORT_ATTR_ASC = 2
+SPH_SORT_TIME_SEGMENTS = 3
+SPH_SORT_EXTENDED = 4
+
+# known filter types
+SPH_FILTER_VALUES = 0
+SPH_FILTER_RANGE = 1
+SPH_FILTER_FLOATRANGE = 2
+SPH_FILTER_STRING = 3
+SPH_FILTER_STRING_LIST = 6
+
+# known attribute types
+SPH_ATTR_NONE = 0
+SPH_ATTR_INTEGER = 1
+SPH_ATTR_TIMESTAMP = 2
+SPH_ATTR_ORDINAL = 3
+SPH_ATTR_BOOL = 4
+SPH_ATTR_FLOAT = 5
+SPH_ATTR_BIGINT = 6
+SPH_ATTR_STRING = 7
+SPH_ATTR_FACTORS = 1001
+SPH_ATTR_MULTI = long(0X40000001)
+SPH_ATTR_MULTI64 = long(0X40000002)
+
+SPH_ATTR_TYPES = (SPH_ATTR_NONE,
+ SPH_ATTR_INTEGER,
+ SPH_ATTR_TIMESTAMP,
+ SPH_ATTR_ORDINAL,
+ SPH_ATTR_BOOL,
+ SPH_ATTR_FLOAT,
+ SPH_ATTR_BIGINT,
+ SPH_ATTR_STRING,
+ SPH_ATTR_MULTI,
+ SPH_ATTR_MULTI64)
+
+# known grouping functions
+SPH_GROUPBY_DAY = 0
+SPH_GROUPBY_WEEK = 1
+SPH_GROUPBY_MONTH = 2
+SPH_GROUPBY_YEAR = 3
+SPH_GROUPBY_ATTR = 4
+SPH_GROUPBY_ATTRPAIR = 5
+
+
+class SphinxClient:
+ def __init__ (self):
+ """
+ Create a new client object, and fill defaults.
+ """
+ self._host = 'localhost' # searchd host (default is "localhost")
+ self._port = 9312 # searchd port (default is 9312)
+ self._path = None # searchd unix-domain socket path
+ self._socket = None
+ self._offset = 0 # how much records to seek from result-set start (default is 0)
+ self._limit = 20 # how much records to return from result-set starting at offset (default is 20)
+ self._weights = [] # per-field weights (default is 1 for all fields)
+ self._sort = SPH_SORT_RELEVANCE # match sorting mode (default is SPH_SORT_RELEVANCE)
+ self._sortby = bytearray() # attribute to sort by (defualt is "")
+ self._min_id = 0 # min ID to match (default is 0)
+ self._max_id = 0 # max ID to match (default is UINT_MAX)
+ self._filters = [] # search filters
+ self._groupby = bytearray() # group-by attribute name
+ self._groupfunc = SPH_GROUPBY_DAY # group-by function (to pre-process group-by attribute value with)
+ self._groupsort = str_bytes('@group desc') # group-by sorting clause (to sort groups in result set with)
+ self._groupdistinct = bytearray() # group-by count-distinct attribute
+ self._maxmatches = 1000 # max matches to retrieve
+ self._cutoff = 0 # cutoff to stop searching at
+ self._retrycount = 0 # distributed retry count
+ self._retrydelay = 0 # distributed retry delay
+ self._indexweights = {} # per-index weights
+ self._ranker = SPH_RANK_PROXIMITY_BM15 # ranking mode
+ self._rankexpr = bytearray() # ranking expression for SPH_RANK_EXPR
+ self._maxquerytime = 0 # max query time, milliseconds (default is 0, do not limit)
+ self._timeout = 1.0 # connection timeout
+ self._fieldweights = {} # per-field-name weights
+ self._select = str_bytes('*') # select-list (attributes or expressions, with optional aliases)
+ self._query_flags = SetBit ( 0, 6, True ) # default idf=tfidf_normalized
+ self._predictedtime = 0 # per-query max_predicted_time
+ self._outerorderby = bytearray() # outer match sort by
+ self._outeroffset = 0 # outer offset
+ self._outerlimit = 0 # outer limit
+ self._hasouter = False # sub-select enabled
+ self._tokenfilterlibrary = bytearray() # token_filter plugin library name
+ self._tokenfiltername = bytearray() # token_filter plugin name
+ self._tokenfilteropts = bytearray() # token_filter plugin options
+
+ self._error = '' # last error message
+ self._warning = '' # last warning message
+ self._reqs = [] # requests array for multi-query
+
+ def __del__ (self):
+ if self._socket:
+ self._socket.close()
+
+
+ def GetLastError (self):
+ """
+ Get last error message (string).
+ """
+ return self._error
+
+
+ def GetLastWarning (self):
+ """
+ Get last warning message (string).
+ """
+ return self._warning
+
+
+ def SetServer (self, host, port = None):
+ """
+ Set searchd server host and port.
+ """
+ assert(isinstance(host, str))
+ if host.startswith('/'):
+ self._path = host
+ return
+ elif host.startswith('unix://'):
+ self._path = host[7:]
+ return
+ self._host = host
+ if isinstance(port, int):
+ assert(port>0 and port<65536)
+ self._port = port
+ self._path = None
+
+ def SetConnectTimeout ( self, timeout ):
+ """
+ Set connection timeout ( float second )
+ """
+ assert (isinstance(timeout, float))
+ # set timeout to 0 make connaection non-blocking that is wrong so timeout got clipped to reasonable minimum
+ self._timeout = max ( 0.001, timeout )
+
+ def _Connect (self):
+ """
+ INTERNAL METHOD, DO NOT CALL. Connects to searchd server.
+ """
+ if self._socket:
+ # we have a socket, but is it still alive?
+ sr, sw, _ = select.select ( [self._socket], [self._socket], [], 0 )
+
+ # this is how alive socket should look
+ if len(sr)==0 and len(sw)==1:
+ return self._socket
+
+ # oops, looks like it was closed, lets reopen
+ self._socket.close()
+ self._socket = None
+
+ try:
+ if self._path:
+ af = socket.AF_UNIX
+ addr = self._path
+ desc = self._path
+ else:
+ af = socket.AF_INET
+ addr = ( self._host, self._port )
+ desc = '%s;%s' % addr
+ sock = socket.socket ( af, socket.SOCK_STREAM )
+ sock.settimeout ( self._timeout )
+ sock.connect ( addr )
+ except socket.error as msg:
+ if sock:
+ sock.close()
+ self._error = 'connection to %s failed (%s)' % ( desc, msg )
+ return
+
+ v = unpack('>L', sock.recv(4))[0]
+ if v<1:
+ sock.close()
+ self._error = 'expected searchd protocol version, got %s' % v
+ return
+
+ # all ok, send my version
+ sock.send(pack('>L', 1))
+ return sock
+
+
+ def _GetResponse (self, sock, client_ver):
+ """
+ INTERNAL METHOD, DO NOT CALL. Gets and checks response packet from searchd server.
+ """
+ (status, ver, length) = unpack('>2HL', sock.recv(8))
+ response = bytearray()
+ left = length
+ while left>0:
+ chunk = sock.recv(left)
+ if chunk:
+ response += chunk
+ left -= len(chunk)
+ else:
+ break
+
+ if not self._socket:
+ sock.close()
+
+ # check response
+ read = len(response)
+ if not response or read!=length:
+ if length:
+ self._error = 'failed to read searchd response (status=%s, ver=%s, len=%s, read=%s)' \
+ % (status, ver, length, read)
+ else:
+ self._error = 'received zero-sized searchd response'
+ return None
+
+ # check status
+ if status==SEARCHD_WARNING:
+ wend = 4 + unpack ( '>L', response[0:4] )[0]
+ self._warning = bytes_str(response[4:wend])
+ return response[wend:]
+
+ if status==SEARCHD_ERROR:
+ self._error = 'searchd error: ' + bytes_str(response[4:])
+ return None
+
+ if status==SEARCHD_RETRY:
+ self._error = 'temporary searchd error: ' + bytes_str(response[4:])
+ return None
+
+ if status!=SEARCHD_OK:
+ self._error = 'unknown status code %d' % status
+ return None
+
+ # check version
+ if ver>8, ver&0xff, client_ver>>8, client_ver&0xff)
+
+ return response
+
+
+ def _Send ( self, sock, req ):
+ """
+ INTERNAL METHOD, DO NOT CALL. send request to searchd server.
+ """
+ total = 0
+ while True:
+ sent = sock.send ( req[total:] )
+ if sent<=0:
+ break
+
+ total = total + sent
+
+ return total
+
+
+ def SetLimits (self, offset, limit, maxmatches=0, cutoff=0):
+ """
+ Set offset and count into result set, and optionally set max-matches and cutoff limits.
+ """
+ assert ( type(offset) in [int,long] and 0<=offset<16777216 )
+ assert ( type(limit) in [int,long] and 0=0)
+ self._offset = offset
+ self._limit = limit
+ if maxmatches>0:
+ self._maxmatches = maxmatches
+ if cutoff>=0:
+ self._cutoff = cutoff
+
+
+ def SetMaxQueryTime (self, maxquerytime):
+ """
+ Set maximum query time, in milliseconds, per-index. 0 means 'do not limit'.
+ """
+ assert(isinstance(maxquerytime,int) and maxquerytime>0)
+ self._maxquerytime = maxquerytime
+
+
+ def SetRankingMode ( self, ranker, rankexpr='' ):
+ """
+ Set ranking mode.
+ """
+ assert(ranker>=0 and ranker=0)
+ assert(isinstance(delay,int) and delay>=0)
+ self._retrycount = count
+ self._retrydelay = delay
+
+
+ def SetSelect (self, select):
+ assert(isinstance(select, (str,text_type)))
+ self._select = str_bytes(select)
+
+
+ def SetQueryFlag ( self, name, value ):
+ known_names = [ "reverse_scan", "sort_method", "max_predicted_time", "boolean_simplify", "idf", "global_idf" ]
+ flags = { "reverse_scan":[0, 1], "sort_method":["pq", "kbuffer"],"max_predicted_time":[0], "boolean_simplify":[True, False], "idf":["normalized", "plain", "tfidf_normalized", "tfidf_unnormalized"], "global_idf":[True, False] }
+ assert ( name in known_names )
+ assert ( value in flags[name] or ( name=="max_predicted_time" and isinstance(value, (int, long)) and value>=0))
+
+ if name=="reverse_scan":
+ self._query_flags = SetBit ( self._query_flags, 0, value==1 )
+ if name=="sort_method":
+ self._query_flags = SetBit ( self._query_flags, 1, value=="kbuffer" )
+ if name=="max_predicted_time":
+ self._query_flags = SetBit ( self._query_flags, 2, value>0 )
+ self._predictedtime = int(value)
+ if name=="boolean_simplify":
+ self._query_flags= SetBit ( self._query_flags, 3, value )
+ if name=="idf" and ( value=="plain" or value=="normalized" ) :
+ self._query_flags = SetBit ( self._query_flags, 4, value=="plain" )
+ if name=="global_idf":
+ self._query_flags= SetBit ( self._query_flags, 5, value )
+ if name=="idf" and ( value=="tfidf_normalized" or value=="tfidf_unnormalized" ) :
+ self._query_flags = SetBit ( self._query_flags, 6, value=="tfidf_normalized" )
+
+ def SetOuterSelect ( self, orderby, offset, limit ):
+ assert(isinstance(orderby, (str,text_type)))
+ assert(isinstance(offset, (int, long)))
+ assert(isinstance(limit, (int, long)))
+ assert ( offset>=0 )
+ assert ( limit>0 )
+
+ self._outerorderby = str_bytes(orderby)
+ self._outeroffset = offset
+ self._outerlimit = limit
+ self._hasouter = True
+
+ def SetTokenFilter ( self, library, name, opts='' ):
+ assert(isinstance(library, str))
+ assert(isinstance(name, str))
+ assert(isinstance(opts, str))
+
+ self._tokenfilterlibrary = str_bytes(library)
+ self._tokenfiltername = str_bytes(name)
+ self._tokenfilteropts = str_bytes(opts)
+
+
+ def ResetFilters (self):
+ """
+ Clear all filters (for multi-queries).
+ """
+ self._filters = []
+
+
+ def ResetGroupBy (self):
+ """
+ Clear groupby settings (for multi-queries).
+ """
+ self._groupby = bytearray()
+ self._groupfunc = SPH_GROUPBY_DAY
+ self._groupsort = str_bytes('@group desc')
+ self._groupdistinct = bytearray()
+
+ def ResetQueryFlag (self):
+ self._query_flags = SetBit ( 0, 6, True ) # default idf=tfidf_normalized
+ self._predictedtime = 0
+
+ def ResetOuterSelect (self):
+ self._outerorderby = bytearray()
+ self._outeroffset = 0
+ self._outerlimit = 0
+ self._hasouter = False
+
+ def Query (self, query, index='*', comment=''):
+ """
+ Connect to searchd server and run given search query.
+ Returns None on failure; result set hash on success (see documentation for details).
+ """
+ assert(len(self._reqs)==0)
+ self.AddQuery(query,index,comment)
+ results = self.RunQueries()
+ self._reqs = [] # we won't re-run erroneous batch
+
+ if not results or len(results)==0:
+ return None
+ self._error = results[0]['error']
+ self._warning = results[0]['warning']
+ if results[0]['status'] == SEARCHD_ERROR:
+ return None
+ return results[0]
+
+
+ def AddQuery (self, query, index='*', comment=''):
+ """
+ Add query to batch.
+ """
+ # build request
+ # 6 == match_mode extended2
+ req = bytearray()
+ req.extend(pack('>5L', self._query_flags, self._offset, self._limit, 6, self._ranker))
+ if self._ranker==SPH_RANK_EXPR:
+ req.extend(pack('>L', len(self._rankexpr)))
+ req.extend(self._rankexpr)
+ req.extend(pack('>L', self._sort))
+ req.extend(pack('>L', len(self._sortby)))
+ req.extend(self._sortby)
+
+ query = str_bytes(query)
+ assert(isinstance(query,bytearray))
+
+ req.extend(pack('>L', len(query)))
+ req.extend(query)
+
+ req.extend(pack('>L', len(self._weights)))
+ for w in self._weights:
+ req.extend(pack('>L', w))
+ index = str_bytes(index)
+ assert(isinstance(index,bytearray))
+ req.extend(pack('>L', len(index)))
+ req.extend(index)
+ req.extend(pack('>L',1)) # id64 range marker
+ req.extend(pack('>Q', self._min_id))
+ req.extend(pack('>Q', self._max_id))
+
+ # filters
+ req.extend ( pack ( '>L', len(self._filters) ) )
+ for f in self._filters:
+ attr = str_bytes(f['attr'])
+ req.extend ( pack ( '>L', len(f['attr'])) + attr)
+ filtertype = f['type']
+ req.extend ( pack ( '>L', filtertype))
+ if filtertype == SPH_FILTER_VALUES:
+ req.extend ( pack ('>L', len(f['values'])))
+ for val in f['values']:
+ req.extend ( pack ('>q', val))
+ elif filtertype == SPH_FILTER_RANGE:
+ req.extend ( pack ('>2q', f['min'], f['max']))
+ elif filtertype == SPH_FILTER_FLOATRANGE:
+ req.extend ( pack ('>2f', f['min'], f['max']))
+ elif filtertype == SPH_FILTER_STRING:
+ val = str_bytes(f['value'])
+ req.extend ( pack ( '>L', len(val) ) )
+ req.extend ( val )
+ elif filtertype == SPH_FILTER_STRING_LIST:
+ req.extend ( pack ('>L', len(f['values'])))
+ for sval in f['values']:
+ val = str_bytes( sval )
+ req.extend ( pack ( '>L', len(val) ) )
+ req.extend(val)
+ req.extend ( pack ( '>L', f['exclude'] ) )
+
+ # group-by, max-matches, group-sort
+ req.extend ( pack ( '>2L', self._groupfunc, len(self._groupby) ) )
+ req.extend ( self._groupby )
+ req.extend ( pack ( '>2L', self._maxmatches, len(self._groupsort) ) )
+ req.extend ( self._groupsort )
+ req.extend ( pack ( '>LLL', self._cutoff, self._retrycount, self._retrydelay))
+ req.extend ( pack ( '>L', len(self._groupdistinct)))
+ req.extend ( self._groupdistinct)
+
+ # geoanchor point
+ req.extend ( pack ('>L', 0) )
+
+ # per-index weights
+ req.extend ( pack ('>L',len(self._indexweights)))
+ for indx,weight in list(self._indexweights.items()):
+ indx = str_bytes(indx)
+ req.extend ( pack ('>L',len(indx)) + indx + pack ('>L',weight))
+
+ # max query time
+ req.extend ( pack ('>L', self._maxquerytime) )
+
+ # per-field weights
+ req.extend ( pack ('>L',len(self._fieldweights) ) )
+ for field,weight in list(self._fieldweights.items()):
+ field = str_bytes(field)
+ req.extend ( pack ('>L',len(field)) + field + pack ('>L',weight) )
+
+ # comment
+ comment = str_bytes(comment)
+ req.extend ( pack('>L',len(comment)) + comment )
+
+ # attribute overrides
+ req.extend ( pack('>L', 0) )
+
+ # select-list
+ req.extend ( pack('>L', len(self._select)) )
+ req.extend ( self._select )
+ if self._predictedtime>0:
+ req.extend ( pack('>L', self._predictedtime ) )
+
+ # outer
+ req.extend ( pack('>L',len(self._outerorderby)) + self._outerorderby )
+ req.extend ( pack ( '>2L', self._outeroffset, self._outerlimit ) )
+ if self._hasouter:
+ req.extend ( pack('>L', 1) )
+ else:
+ req.extend ( pack('>L', 0) )
+
+ # token_filter
+ req.extend ( pack('>L',len(self._tokenfilterlibrary)) + self._tokenfilterlibrary )
+ req.extend ( pack('>L',len(self._tokenfiltername)) + self._tokenfiltername )
+ req.extend ( pack('>L',len(self._tokenfilteropts)) + self._tokenfilteropts )
+
+ # send query, get response
+
+ self._reqs.append(req)
+ return
+
+
+ def RunQueries (self):
+ """
+ Run queries batch.
+ Returns None on network IO failure; or an array of result set hashes on success.
+ """
+ if len(self._reqs)==0:
+ self._error = 'no queries defined, issue AddQuery() first'
+ return None
+
+ sock = self._Connect()
+ if not sock:
+ return None
+
+ req = bytearray()
+ for r in self._reqs:
+ req.extend(r)
+ length = len(req)+8
+ req_all = bytearray()
+ req_all.extend(pack('>HHLLL', SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, length, 0, len(self._reqs)))
+ req_all.extend(req)
+ self._Send ( sock, req_all )
+
+ response = self._GetResponse(sock, VER_COMMAND_SEARCH)
+ if not response:
+ return None
+
+ nreqs = len(self._reqs)
+
+ # parse response
+ max_ = len(response)
+ p = 0
+
+ results = []
+ for i in range(0,nreqs,1):
+ result = {}
+ results.append(result)
+
+ result['error'] = ''
+ result['warning'] = ''
+ status = unpack('>L', response[p:p+4])[0]
+ p += 4
+ result['status'] = status
+ if status != SEARCHD_OK:
+ length = unpack('>L', response[p:p+4])[0]
+ p += 4
+ message = bytes_str(response[p:p+length])
+ p += length
+
+ if status == SEARCHD_WARNING:
+ result['warning'] = message
+ else:
+ result['error'] = message
+ continue
+
+ # read schema
+ fields = []
+ attrs = []
+
+ nfields = unpack('>L', response[p:p+4])[0]
+ p += 4
+ while nfields>0 and pL', response[p:p+4])[0]
+ p += 4
+ fields.append(bytes_str(response[p:p+length]))
+ p += length
+
+ result['fields'] = fields
+
+ nattrs = unpack('>L', response[p:p+4])[0]
+ p += 4
+ while nattrs>0 and pL', response[p:p+4])[0]
+ p += 4
+ attr = bytes_str(response[p:p+length])
+ p += length
+ type_ = unpack('>L', response[p:p+4])[0]
+ p += 4
+ attrs.append([attr,type_])
+
+ result['attrs'] = attrs
+
+ # read match count
+ count = unpack('>L', response[p:p+4])[0]
+ p += 4
+ id64 = unpack('>L', response[p:p+4])[0]
+ p += 4
+
+ # read matches
+ result['matches'] = []
+ while count>0 and pQL', response[p:p+12])
+ p += 12
+ else:
+ doc, weight = unpack('>2L', response[p:p+8])
+ p += 8
+
+ match = { 'id':doc, 'weight':weight, 'attrs':{} }
+ for i in range(len(attrs)):
+ if attrs[i][1] == SPH_ATTR_FLOAT:
+ match['attrs'][attrs[i][0]] = unpack('>f', response[p:p+4])[0]
+ elif attrs[i][1] == SPH_ATTR_BIGINT:
+ match['attrs'][attrs[i][0]] = unpack('>q', response[p:p+8])[0]
+ p += 4
+ elif attrs[i][1] == SPH_ATTR_STRING:
+ slen = unpack('>L', response[p:p+4])[0]
+ p += 4
+ match['attrs'][attrs[i][0]] = ''
+ if slen>0:
+ match['attrs'][attrs[i][0]] = bytes_str(response[p:p+slen])
+ p += slen-4
+ elif attrs[i][1] == SPH_ATTR_FACTORS:
+ slen = unpack('>L', response[p:p+4])[0]
+ p += 4
+ match['attrs'][attrs[i][0]] = ''
+ if slen>0:
+ match['attrs'][attrs[i][0]] = response[p:p+slen-4]
+ p += slen-4
+ p -= 4
+ elif attrs[i][1] == SPH_ATTR_MULTI:
+ match['attrs'][attrs[i][0]] = []
+ nvals = unpack('>L', response[p:p+4])[0]
+ p += 4
+ for n in range(0,nvals,1):
+ match['attrs'][attrs[i][0]].append(unpack('>L', response[p:p+4])[0])
+ p += 4
+ p -= 4
+ elif attrs[i][1] == SPH_ATTR_MULTI64:
+ match['attrs'][attrs[i][0]] = []
+ nvals = unpack('>L', response[p:p+4])[0]
+ nvals = nvals/2
+ p += 4
+ for n in range(0,nvals,1):
+ match['attrs'][attrs[i][0]].append(unpack('>q', response[p:p+8])[0])
+ p += 8
+ p -= 4
+ else:
+ match['attrs'][attrs[i][0]] = unpack('>L', response[p:p+4])[0]
+ p += 4
+
+ result['matches'].append ( match )
+
+ result['total'], result['total_found'], result['time'], words = unpack('>4L', response[p:p+16])
+
+ result['time'] = '%.3f' % (result['time']/1000.0)
+ p += 16
+
+ result['words'] = []
+ while words>0:
+ words -= 1
+ length = unpack('>L', response[p:p+4])[0]
+ p += 4
+ word = bytes_str(response[p:p+length])
+ p += length
+ docs, hits = unpack('>2L', response[p:p+8])
+ p += 8
+
+ result['words'].append({'word':word, 'docs':docs, 'hits':hits})
+
+ self._reqs = []
+ return results
+
+
+ def BuildExcerpts (self, docs, index, words, opts=None):
+ """
+ Connect to searchd server and generate exceprts from given documents.
+ """
+ if not opts:
+ opts = {}
+
+ assert(isinstance(docs, list))
+ assert(isinstance(index, (str,text_type)))
+ assert(isinstance(words, (str,text_type)))
+ assert(isinstance(opts, dict))
+
+ sock = self._Connect()
+
+ if not sock:
+ return None
+
+ # fixup options
+ opts.setdefault('before_match', '')
+ opts.setdefault('after_match', ' ')
+ opts.setdefault('chunk_separator', ' ... ')
+ opts.setdefault('html_strip_mode', 'index')
+ opts.setdefault('limit', 256)
+ opts.setdefault('limit_passages', 0)
+ opts.setdefault('limit_words', 0)
+ opts.setdefault('around', 5)
+ opts.setdefault('start_passage_id', 1)
+ opts.setdefault('passage_boundary', 'none')
+
+ # build request
+ # v.1.0 req
+
+ flags = 1 # (remove spaces)
+ if opts.get('exact_phrase'): flags |= 2
+ if opts.get('single_passage'): flags |= 4
+ if opts.get('use_boundaries'): flags |= 8
+ if opts.get('weight_order'): flags |= 16
+ if opts.get('query_mode'): flags |= 32
+ if opts.get('force_all_words'): flags |= 64
+ if opts.get('load_files'): flags |= 128
+ if opts.get('allow_empty'): flags |= 256
+ if opts.get('emit_zones'): flags |= 512
+ if opts.get('load_files_scattered'): flags |= 1024
+
+ # mode=0, flags
+ req = bytearray()
+ req.extend(pack('>2L', 0, flags))
+
+ # req index
+ index = str_bytes(index)
+ req.extend(pack('>L', len(index)))
+ req.extend(index)
+
+ # req words
+ words = str_bytes(words)
+ req.extend(pack('>L', len(words)))
+ req.extend(words)
+
+ # options
+ opts_before_match = str_bytes(opts['before_match'])
+ req.extend(pack('>L', len(opts_before_match)))
+ req.extend(opts_before_match)
+
+ opts_after_match = str_bytes(opts['after_match'])
+ req.extend(pack('>L', len(opts_after_match)))
+ req.extend(opts_after_match)
+
+ opts_chunk_separator = str_bytes(opts['chunk_separator'])
+ req.extend(pack('>L', len(opts_chunk_separator)))
+ req.extend(opts_chunk_separator)
+
+ req.extend(pack('>L', int(opts['limit'])))
+ req.extend(pack('>L', int(opts['around'])))
+
+ req.extend(pack('>L', int(opts['limit_passages'])))
+ req.extend(pack('>L', int(opts['limit_words'])))
+ req.extend(pack('>L', int(opts['start_passage_id'])))
+ opts_html_strip_mode = str_bytes(opts['html_strip_mode'])
+ req.extend(pack('>L', len(opts_html_strip_mode)))
+ req.extend(opts_html_strip_mode)
+ opts_passage_boundary = str_bytes(opts['passage_boundary'])
+ req.extend(pack('>L', len(opts_passage_boundary)))
+ req.extend(opts_passage_boundary)
+
+ # documents
+ req.extend(pack('>L', len(docs)))
+ for doc in docs:
+ doc = str_bytes(doc)
+ req.extend(pack('>L', len(doc)))
+ req.extend(doc)
+
+ # send query, get response
+ length = len(req)
+
+ # add header
+ req_head = bytearray()
+ req_head.extend(pack('>2HL', SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, length))
+ req_all = req_head + req
+ self._Send ( sock, req_all )
+
+ response = self._GetResponse(sock, VER_COMMAND_EXCERPT )
+ if not response:
+ return []
+
+ # parse response
+ pos = 0
+ res = []
+ rlen = len(response)
+
+ for i in range(len(docs)):
+ length = unpack('>L', response[pos:pos+4])[0]
+ pos += 4
+
+ if pos+length > rlen:
+ self._error = 'incomplete reply'
+ return []
+
+ res.append(bytes_str(response[pos:pos+length]))
+ pos += length
+
+ return res
+
+
+ def UpdateAttributes ( self, index, attrs, values, mva=False, ignorenonexistent=False ):
+ """
+ Update given attribute values on given documents in given indexes.
+ Returns amount of updated documents (0 or more) on success, or -1 on failure.
+
+ 'attrs' must be a list of strings.
+ 'values' must be a dict with int key (document ID) and list of int values (new attribute values).
+ optional boolean parameter 'mva' points that there is update of MVA attributes.
+ In this case the 'values' must be a dict with int key (document ID) and list of lists of int values
+ (new MVA attribute values).
+ Optional boolean parameter 'ignorenonexistent' points that the update will silently ignore any warnings about
+ trying to update a column which is not exists in current index schema.
+
+ Example:
+ res = cl.UpdateAttributes ( 'test1', [ 'group_id', 'date_added' ], { 2:[123,1000000000], 4:[456,1234567890] } )
+ """
+ assert ( isinstance ( index, str ) )
+ assert ( isinstance ( attrs, list ) )
+ assert ( isinstance ( values, dict ) )
+ for attr in attrs:
+ assert ( isinstance ( attr, str ) )
+ for docid, entry in list(values.items()):
+ AssertUInt32(docid)
+ assert ( isinstance ( entry, list ) )
+ assert ( len(attrs)==len(entry) )
+ for val in entry:
+ if mva:
+ assert ( isinstance ( val, list ) )
+ for vals in val:
+ AssertInt32(vals)
+ else:
+ AssertInt32(val)
+
+ # build request
+ req = bytearray()
+ index = str_bytes(index)
+ req.extend( pack('>L',len(index)) + index )
+
+ req.extend ( pack('>L',len(attrs)) )
+ ignore_absent = 0
+ if ignorenonexistent: ignore_absent = 1
+ req.extend ( pack('>L', ignore_absent ) )
+ mva_attr = 0
+ if mva: mva_attr = 1
+ for attr in attrs:
+ attr = str_bytes(attr)
+ req.extend ( pack('>L',len(attr)) + attr )
+ req.extend ( pack('>L', mva_attr ) )
+
+ req.extend ( pack('>L',len(values)) )
+ for docid, entry in list(values.items()):
+ req.extend ( pack('>Q',docid) )
+ for val in entry:
+ val_len = val
+ if mva: val_len = len ( val )
+ req.extend ( pack('>L',val_len ) )
+ if mva:
+ for vals in val:
+ req.extend ( pack ('>L',vals) )
+
+ # connect, send query, get response
+ sock = self._Connect()
+ if not sock:
+ return None
+
+ length = len(req)
+ req_all = bytearray()
+ req_all.extend( pack ( '>2HL', SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, length ) )
+ req_all.extend( req )
+ self._Send ( sock, req_all )
+
+ response = self._GetResponse ( sock, VER_COMMAND_UPDATE )
+ if not response:
+ return -1
+
+ # parse response
+ updated = unpack ( '>L', response[0:4] )[0]
+ return updated
+
+
+ def BuildKeywords ( self, query, index, hits ):
+ """
+ Connect to searchd server, and generate keywords list for a given query.
+ Returns None on failure, or a list of keywords on success.
+ """
+ assert ( isinstance ( query, str ) )
+ assert ( isinstance ( index, str ) )
+ assert ( isinstance ( hits, int ) )
+
+ # build request
+ req = bytearray()
+ query = str_bytes(query)
+ req.extend(pack ( '>L', len(query) ) + query)
+ index = str_bytes(index)
+ req.extend ( pack ( '>L', len(index) ) + index )
+ req.extend ( pack ( '>L', hits ) )
+
+ # connect, send query, get response
+ sock = self._Connect()
+ if not sock:
+ return None
+
+ length = len(req)
+ req_all = bytearray()
+ req_all.extend(pack ( '>2HL', SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, length ))
+ req_all.extend(req)
+ self._Send ( sock, req_all )
+
+ response = self._GetResponse ( sock, VER_COMMAND_KEYWORDS )
+ if not response:
+ return None
+
+ # parse response
+ res = []
+
+ nwords = unpack ( '>L', response[0:4] )[0]
+ p = 4
+ max_ = len(response)
+
+ while nwords>0 and pL', response[p:p+4] )[0]
+ p += 4
+ tokenized = response[p:p+length]
+ p += length
+
+ length = unpack ( '>L', response[p:p+4] )[0]
+ p += 4
+ normalized = response[p:p+length]
+ p += length
+
+ entry = { 'tokenized':bytes_str(tokenized), 'normalized':bytes_str(normalized) }
+ if hits:
+ entry['docs'], entry['hits'] = unpack ( '>2L', response[p:p+8] )
+ p += 8
+
+ res.append ( entry )
+
+ if nwords>0 or p>max_:
+ self._error = 'incomplete reply'
+ return None
+
+ return res
+
+ def Status ( self, session=False ):
+ """
+ Get the status
+ """
+
+ # connect, send query, get response
+ sock = self._Connect()
+ if not sock:
+ return None
+
+ sess = 1
+ if session:
+ sess = 0
+
+ req = pack ( '>2HLL', SEARCHD_COMMAND_STATUS, VER_COMMAND_STATUS, 4, sess )
+ self._Send ( sock, req )
+
+ response = self._GetResponse ( sock, VER_COMMAND_STATUS )
+ if not response:
+ return None
+
+ # parse response
+ res = []
+
+ p = 8
+ max_ = len(response)
+
+ while pL', response[p:p+4] )[0]
+ k = response[p+4:p+length+4]
+ p += 4+length
+ length = unpack ( '>L', response[p:p+4] )[0]
+ v = response[p+4:p+length+4]
+ p += 4+length
+ res += [[bytes_str(k), bytes_str(v)]]
+
+ return res
+
+ ### persistent connections
+
+ def Open(self):
+ if self._socket:
+ self._error = 'already connected'
+ return None
+
+ server = self._Connect()
+ if not server:
+ return None
+
+ # command, command version = 0, body length = 4, body = 1
+ request = pack ( '>hhII', SEARCHD_COMMAND_PERSIST, 0, 4, 1 )
+ self._Send ( server, request )
+
+ self._socket = server
+ return True
+
+ def Close(self):
+ if not self._socket:
+ self._error = 'not connected'
+ return
+ self._socket.close()
+ self._socket = None
+
+ def EscapeString(self, string):
+ return re.sub(r"([=\(\)|\-!@~\"&/\\\^\$\=\<])", r"\\\1", string)
+
+
+ def FlushAttributes(self):
+ sock = self._Connect()
+ if not sock:
+ return -1
+
+ request = pack ( '>hhI', SEARCHD_COMMAND_FLUSHATTRS, VER_COMMAND_FLUSHATTRS, 0 ) # cmd, ver, bodylen
+ self._Send ( sock, request )
+
+ response = self._GetResponse ( sock, VER_COMMAND_FLUSHATTRS )
+ if not response or len(response)!=4:
+ self._error = 'unexpected response length'
+ return -1
+
+ tag = unpack ( '>L', response[0:4] )[0]
+ return tag
+
+def AssertInt32 ( value ):
+ assert(isinstance(value, (int, long)))
+ assert(value>=-2**32-1 and value<=2**32-1)
+
+def AssertUInt32 ( value ):
+ assert(isinstance(value, (int, long)))
+ assert(value>=0 and value<=2**32-1)
+
+def SetBit ( flag, bit, on ):
+ if on:
+ flag += ( 1< (3,):
+ def str_bytes(x):
+ return bytearray(x, 'utf-8')
+else:
+ def str_bytes(x):
+ if isinstance(x,unicode):
+ return bytearray(x, 'utf-8')
+ else:
+ return bytearray(x)
+
+def bytes_str(x):
+ assert (isinstance(x, bytearray))
+ return x.decode('utf-8')
+
+#
+# $Id$
+#
diff --git a/plugins/sphinx/conf/sphinx.conf b/plugins/sphinx/conf/sphinx.conf
new file mode 100755
index 000000000..de9e5f270
--- /dev/null
+++ b/plugins/sphinx/conf/sphinx.conf
@@ -0,0 +1,28 @@
+#
+# Minimal Sphinx configuration sample (clean, simple, functional)
+#
+
+
+searchd
+{
+ listen = 9312
+ listen = 9306:mysql41
+ log = {$SERVER_APP}/index/searchd.log
+ query_log = {$SERVER_APP}/index/query.log
+ pid_file = {$SERVER_APP}/index/searchd.pid
+ #workers = threads # for RT to work
+ binlog_path = {$SERVER_APP}/index/binlog
+ read_timeout = 5
+ max_children = 0
+ seamless_rotate = 1
+ preopen_indexes = 1
+ unlink_old = 1
+}
+
+index mydocs
+{
+ type = rt
+ path = {$SERVER_APP}/bin/doc
+ rt_field = title
+ rt_attr_json = j
+}
\ No newline at end of file
diff --git a/plugins/sphinx/ico.png b/plugins/sphinx/ico.png
new file mode 100644
index 000000000..b80d938b7
Binary files /dev/null and b/plugins/sphinx/ico.png differ
diff --git a/plugins/sphinx/index.html b/plugins/sphinx/index.html
new file mode 100755
index 000000000..b62232b49
--- /dev/null
+++ b/plugins/sphinx/index.html
@@ -0,0 +1,36 @@
+
+
+
+
\ No newline at end of file
diff --git a/plugins/sphinx/index.py b/plugins/sphinx/index.py
new file mode 100755
index 000000000..2e8e8ca7f
--- /dev/null
+++ b/plugins/sphinx/index.py
@@ -0,0 +1,484 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import string
+import subprocess
+
+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 'sphinx'
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+sys.path.append(getPluginDir() +"/class")
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getInitDFile():
+ if app_debug:
+ return '/tmp/' + getPluginName()
+ return '/etc/init.d/' + getPluginName()
+
+
+def getConfTpl():
+ path = getPluginDir() + "/conf/sphinx.conf"
+ return path
+
+
+def getConf():
+ path = getServerDir() + "/sphinx.conf"
+ return path
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
+ return path
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ 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 configTpl():
+ path = getPluginDir() + '/tpl'
+ pathFile = os.listdir(path)
+ tmp = []
+ for one in pathFile:
+ file = path + '/' + one
+ tmp.append(file)
+ return mw.getJson(tmp)
+
+
+def readConfigTpl():
+ args = getArgs()
+ data = checkArgs(args, ['file'])
+ if not data[0]:
+ return data[1]
+
+ content = mw.readFile(args['file'])
+ content = contentReplace(content)
+ return mw.returnJson(True, 'ok', content)
+
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName())
+ return content
+
+
+def status():
+ data = mw.execShell(
+ "ps -ef|grep sphinx |grep -v grep | grep -v mdserver-web | awk '{print $2}'")
+ # print(data)
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def mkdirAll():
+ content = mw.readFile(getConf())
+ rep = r'path\s*=\s*(.*)'
+ p = re.compile(rep)
+ tmp = p.findall(content)
+
+ for x in tmp:
+ if x.find('binlog') != -1:
+ mw.execShell('mkdir -p ' + x)
+ else:
+ mw.execShell('mkdir -p ' + os.path.dirname(x))
+
+
+def initDreplace():
+
+ file_tpl = getInitDTpl()
+ service_path = mw.getServerDir()
+
+ initD_path = getServerDir() + '/init.d'
+ if not os.path.exists(initD_path):
+ os.mkdir(initD_path)
+ file_bin = initD_path + '/' + getPluginName()
+
+ # initd replace
+ if not os.path.exists(file_bin):
+ content = mw.readFile(file_tpl)
+ content = contentReplace(content)
+ mw.writeFile(file_bin, content)
+ mw.execShell('chmod +x ' + file_bin)
+
+ # config replace
+ conf_bin = getConf()
+ if not os.path.exists(conf_bin):
+ conf_content = mw.readFile(getConfTpl())
+ conf_content = contentReplace(conf_content)
+ mw.writeFile(getServerDir() + '/sphinx.conf', conf_content)
+
+ # systemd
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/sphinx.service'
+ systemServiceTpl = getPluginDir() + '/init.d/sphinx.service.tpl'
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ se_content = mw.readFile(systemServiceTpl)
+ se_content = se_content.replace('{$SERVER_PATH}', service_path)
+ mw.writeFile(systemService, se_content)
+ mw.execShell('systemctl daemon-reload')
+
+ mkdirAll()
+ return file_bin
+
+
+def checkIndexSph():
+ content = mw.readFile(getConf())
+ rep = r'path\s*=\s*(.*)'
+ p = re.compile(rep)
+ tmp = p.findall(content)
+ for x in tmp:
+ if x.find('binlog') != -1:
+ continue
+ else:
+ p = x + '.sph'
+ if os.path.exists(p):
+ return False
+ return True
+
+
+def sphOp(method):
+ file = initDreplace()
+
+ if not mw.isAppleSystem():
+ data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
+ if data[1] == '':
+ return 'ok'
+ return 'fail'
+
+ data = mw.execShell(file + ' ' + method)
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def start():
+ import tool_cron
+ tool_cron.createBgTask()
+ return sphOp('start')
+
+
+def stop():
+ import tool_cron
+ tool_cron.removeBgTask()
+ return sphOp('stop')
+
+
+def restart():
+ return sphOp('restart')
+
+
+def reload():
+ return sphOp('reload')
+
+
+def rebuild():
+ file = initDreplace()
+ cmd = file + ' rebuild'
+ data = mw.execShell(cmd)
+ if data[0].find('successfully')<0:
+ return data[0].replace("\n"," ")
+ return 'ok'
+
+
+def initdStatus():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status sphinx | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+
+def initdInstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl enable sphinx')
+ return 'ok'
+
+
+def initdUinstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl disable sphinx')
+ return 'ok'
+
+
+def runLog():
+ path = getConf()
+ content = mw.readFile(path)
+ rep = r'log\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0]
+
+
+def getPort():
+ path = getConf()
+ content = mw.readFile(path)
+ rep = r'listen\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0]
+
+
+def queryLog():
+ path = getConf()
+ content = mw.readFile(path)
+ rep = r'query_log\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0]
+
+
+def runStatus():
+ s = status()
+ if s != 'start':
+ return mw.returnJson(False, '没有启动程序')
+
+ sys.path.append(getPluginDir() + "/class")
+ import sphinxapi
+
+ sh = sphinxapi.SphinxClient()
+ port = getPort()
+ sh.SetServer('127.0.0.1', port)
+ info_status = sh.Status()
+
+ rData = {}
+ for x in range(len(info_status)):
+ rData[info_status[x][0]] = info_status[x][1]
+
+ return mw.returnJson(True, 'ok', rData)
+
+
+def sphinxConfParse():
+ file = getConf()
+ bin_dir = getServerDir()
+ content = mw.readFile(file)
+ rep = r'index\s(.*)'
+ sindex = re.findall(rep, content)
+ indexlen = len(sindex)
+ cmd = {}
+ cmd['cmd'] = bin_dir + '/bin/bin/indexer -c ' + bin_dir + '/sphinx.conf'
+
+ cmd['index'] = []
+ cmd_index = []
+ cmd_delta = []
+ if indexlen > 0:
+ for x in range(indexlen):
+ name = sindex[x].strip()
+ if name == '':
+ continue
+ if name.find(':') != -1:
+ cmd_delta.append(name.strip())
+ else:
+ cmd_index.append(name.strip())
+
+ # print(cmd_index)
+ # print(cmd_delta)
+
+ for ci in cmd_index:
+ val = {}
+ val['index'] = ci
+
+ for cd in cmd_delta:
+ cd = cd.replace(" ", '')
+ if cd.find(":"+ci) > -1:
+ val['delta'] = cd.split(":")[0].strip()
+ break
+
+ cmd['index'].append(val)
+ return cmd
+
+
+def sphinxCmd():
+ data = sphinxConfParse()
+ if 'index' in data:
+ return mw.returnJson(True, 'ok', data)
+ else:
+ return mw.returnJson(False, 'no index')
+
+def makeDbToSphinxTest():
+ conf_file = getConf()
+ import sphinx_make
+ sph_make = sphinx_make.sphinxMake()
+ conf = sph_make.makeSqlToSphinxAll()
+
+ mw.writeFile(conf_file,conf)
+ print(conf)
+ # makeSqlToSphinxTable()
+ return True
+
+def makeDbToSphinx():
+ args = getArgs()
+ check = checkArgs(args, ['db','tables','is_delta','is_cover'])
+ if not check[0]:
+ return check[1]
+
+ db = args['db']
+ tables = args['tables']
+ is_delta = args['is_delta']
+ is_cover = args['is_cover']
+
+ if is_cover != 'yes':
+ return mw.returnJson(False,'暂时仅支持覆盖!')
+
+ sph_file = getConf()
+
+ import sphinx_make
+ sph_make = sphinx_make.sphinxMake()
+
+ version_pl = getServerDir() + "/version.pl"
+ if os.path.exists(version_pl):
+ version = mw.readFile(version_pl).strip()
+ sph_make.setVersion(version)
+
+ if not sph_make.checkDbName(db):
+ return mw.returnJson(False,'保留数据库名称,不可用!')
+ is_delta_bool = False
+ if is_delta == 'yes':
+ is_delta_bool = True
+ if is_cover == 'yes':
+ tables = tables.split(',')
+ content = sph_make.makeSqlToSphinx(db, tables, is_delta_bool)
+ mw.writeFile(sph_file,content)
+ mkdirAll()
+ return mw.returnJson(True,'设置成功!')
+
+ return mw.returnJson(True,'测试中')
+
+
+# 全量更新
+def updateAll():
+ data = sphinxConfParse()
+ cmd = data['cmd']
+ if not 'index' in data:
+ return '无更新'
+ index = data['index']
+
+ for x in range(len(index)):
+ cmd_index = cmd + ' ' + index[x]['index'] + ' --rotate'
+ print(cmd_index)
+ os.system(cmd_index)
+ return ''
+
+#增量更新
+def updateDelta():
+ data = sphinxConfParse()
+ cmd = data['cmd']
+ if not 'index' in data:
+ return '无更新'
+ index = data['index']
+
+ for x in range(len(index)):
+ if 'delta' in index[x]:
+ cmd_index = cmd + ' ' + index[x]['delta'] + ' --rotate'
+ print(cmd_index)
+ os.system(cmd_index)
+
+ cmd_index_merge = cmd + ' --merge ' + index[x]['index'] + ' ' + index[x]['delta'] + ' --rotate'
+ print(cmd_index_merge)
+ os.system(cmd_index_merge)
+ else:
+ print(index[x]['index'],'no delta')
+
+ return ''
+
+def installPreInspection(version):
+ data = mw.execShell('arch')
+ if data[0].strip().startswith('aarch'):
+ return '不支持aarch架构'
+ return 'ok'
+
+if __name__ == "__main__":
+ version = "3.1.1"
+ version_pl = getServerDir() + "/version.pl"
+ if os.path.exists(version_pl):
+ version = mw.readFile(version_pl).strip()
+
+ 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 == 'rebuild':
+ print(rebuild())
+ 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(version))
+ elif func == 'conf':
+ print(getConf())
+ elif func == 'config_tpl':
+ print(configTpl())
+ elif func == 'read_config_tpl':
+ print(readConfigTpl())
+ elif func == 'run_log':
+ print(runLog())
+ elif func == 'query_log':
+ print(queryLog())
+ elif func == 'run_status':
+ print(runStatus())
+ elif func == 'sphinx_cmd':
+ print(sphinxCmd())
+ elif func == 'db_to_sphinx':
+ print(makeDbToSphinx())
+ elif func == 'update_all':
+ print(updateAll())
+ elif func == 'update_delta':
+ print(updateDelta())
+ else:
+ print('error')
diff --git a/plugins/sphinx/info.json b/plugins/sphinx/info.json
new file mode 100755
index 000000000..b99d3164d
--- /dev/null
+++ b/plugins/sphinx/info.json
@@ -0,0 +1,20 @@
+{
+ "sort": 7,
+ "ps": "简单高效的全文搜索引擎",
+ "name": "sphinx",
+ "title": "sphinx",
+ "shell": "install.sh",
+ "versions":["3.1.1","3.2.1","3.3.1","3.4.1","3.5.1","3.6.1","3.7.1","3.8.1"],
+ "tip": "soft",
+ "install_pre_inspection":true,
+ "checks": "server/sphinx",
+ "path": "server/sphinx",
+ "display": 1,
+ "author": "midoks",
+ "date": "2017-04-01",
+ "home": "http://sphinxsearch.com/",
+ "doc1": "http://sphinxsearch.com/docs/sphinx3.html",
+ "doc2": "http://sphinxsearch.com/docs/manual-2.3.2.html",
+ "type": 0,
+ "pid": "2"
+}
\ No newline at end of file
diff --git a/plugins/sphinx/init.d/sphinx.service.tpl b/plugins/sphinx/init.d/sphinx.service.tpl
new file mode 100644
index 000000000..5524cef02
--- /dev/null
+++ b/plugins/sphinx/init.d/sphinx.service.tpl
@@ -0,0 +1,12 @@
+[Unit]
+Description=Open Source Search Server
+After=network.target
+
+[Service]
+Type=forking
+ExecStart={$SERVER_PATH}/sphinx/bin/bin/searchd -c {$SERVER_PATH}/sphinx/sphinx.conf
+ExecReload=/bin/kill -USR2 $MAINPID
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/plugins/sphinx/init.d/sphinx.tpl b/plugins/sphinx/init.d/sphinx.tpl
new file mode 100644
index 000000000..8de34003c
--- /dev/null
+++ b/plugins/sphinx/init.d/sphinx.tpl
@@ -0,0 +1,82 @@
+#! /bin/bash
+#
+# searchd: sphinx Daemon
+#
+# chkconfig: - 90 25
+# description: sphinx Daemon
+#
+### BEGIN INIT INFO
+# Provides: sphinx
+# Required-Start: $syslog
+# Required-Stop: $syslog
+# Should-Start: $local_fs
+# Should-Stop: $local_fs
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: sphinx - Document Index Daemon
+# Description: sphinx - Document Index Daemon
+### END INIT INFO
+
+APP_PATH={$SERVER_APP}
+APP_CONF={$SERVER_APP}/sphinx.conf
+prog="sphinx"
+
+start () {
+ echo -n $"Starting $prog: "
+ ${APP_PATH}/bin/bin/searchd -c ${APP_CONF}
+ if [ "$?" != 0 ] ; then
+ echo " failed"
+ exit 1
+ else
+ echo " done"
+ fi
+}
+
+rebuild () {
+ ${APP_PATH}/bin/bin/indexer -c ${APP_CONF} --all --rotate
+}
+
+
+stop () {
+ echo -n $"Stopping $prog: "
+ if [ ! -e ${APP_PATH}/index/searchd.pid ]; then
+ echo -n $"$prog is not running."
+ exit 1
+ fi
+ kill `cat ${APP_PATH}/index/searchd.pid`
+ if [ "$?" != 0 ] ; then
+ echo " failed"
+ exit 1
+ else
+ rm -f ${APP_PATH}/index/searchd.pid
+ echo " done"
+ fi
+}
+
+restart () {
+ $0 stop
+ sleep 2
+ $0 start
+}
+
+# See how we were called.
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart|reload)
+ restart
+ ;;
+ rebuild)
+ rebuild
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|status|restart|reload}"
+ exit 1
+ ;;
+esac
+
+exit $?
\ No newline at end of file
diff --git a/plugins/sphinx/install.sh b/plugins/sphinx/install.sh
new file mode 100755
index 000000000..7e4ee305f
--- /dev/null
+++ b/plugins/sphinx/install.sh
@@ -0,0 +1,171 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sysName=`uname`
+sysArch=`arch`
+
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+# cd /www/server/mdserver-web && source bin/activate && python3 plugins/sphinx/index.py rebuild
+# cd /www/server/mdserver-web/plugins/sphinx && bash install.sh install 3.6.1
+# cd /www/server/mdserver-web && source bin/activate && python3 plugins/sphinx/index.py db_to_sphinx && /www/server/sphinx/bin/bin/indexer -c /www/server/sphinx/sphinx.conf --all --rotate
+# /Users/midoks/Desktop/mwdev/server/sphinx/bin/bin/indexer /Users/midoks/Desktop/mwdev/server/sphinx/sphinx.conf --all --rotate
+
+# cd /www/server/mdserver-web && source bin/activate && python3 plugins/sphinx/index.py sphinx_cmd
+
+# /Users/midoks/Desktop/mwdev/server/sphinx/bin/bin/indexer /Users/midoks/Desktop/mwdev/server/sphinx/sphinx.conf --all --rotate
+
+# cd /www/server/mdserver-web && source bin/activate && python3 plugins/sphinx/index.py start
+bash ${rootPath}/scripts/getos.sh
+# echo "bash ${rootPath}/scripts/getos.sh"
+OSNAME="macos"
+if [ -f ${rootPath}/data/osname.pl ];then
+ OSNAME=`cat ${rootPath}/data/osname.pl`
+fi
+
+if [ "${OSNAME}" == "centos" ] ||
+ [ "${OSNAME}" == "fedora" ] ||
+ [ "${OSNAME}" == "alma" ]; then
+ yum install -y postgresql-libs unixODBC
+fi
+
+# http://sphinxsearch.com/files/sphinx-3.7.1-da9f8a4-linux-amd64.tar.gz
+
+VERSION=$2
+
+# echo $VERSION
+
+if [ "$VERSION" == "3.1.1" ];then
+ VERSION_NUM=${VERSION}-612d99f
+elif [ "$VERSION" == "3.2.1" ]; then
+ VERSION_NUM=${VERSION}-f152e0b
+elif [ "$VERSION" == "3.3.1" ]; then
+ VERSION_NUM=${VERSION}-b72d67b
+elif [ "$VERSION" == "3.4.1" ]; then
+ VERSION_NUM=${VERSION}-efbcc65
+elif [ "$VERSION" == "3.5.1" ]; then
+ VERSION_NUM=${VERSION}-82c60cb
+elif [ "$VERSION" == "3.6.1" ]; then
+ VERSION_NUM=${VERSION}-c9dbeda
+elif [ "$VERSION" == "3.7.1" ]; then
+ VERSION_NUM=${VERSION}-da9f8a4
+elif [ "$VERSION" == "3.8.1" ]; then
+ VERSION_NUM=${VERSION}-d25e0bb
+fi
+
+# echo $VERSION_NUM
+
+Install_sphinx()
+{
+ echo '正在安装Sphinx...'
+ mkdir -p $serverPath/sphinx
+
+ SPHINX_DIR=${serverPath}/source/sphinx
+ mkdir -p $SPHINX_DIR
+
+ SPH_NAME=amd64
+ if [ "$sysArch" == "arm64" ];then
+ SPH_NAME=amd64
+ elif [ "$sysArch" == "x86_64" ]; then
+ SPH_NAME=amd64
+ elif [ "$sysArch" == "aarch64" ]; then
+ SPH_NAME=aarch64
+ fi
+
+ if [ "$sysName" == "Darwin" ] && [ "$VERSION" == "3.7.1" ];then
+ SPH_NAME=aarch64
+ fi
+
+ SPH_SYSNAME=linux
+ if [ $sysName == 'Darwin' ]; then
+ SPH_SYSNAME=darwin
+ elif [ "$sysName" == "aarch64" ]; then
+ SPH_NAME=aarch64
+ elif [ "$sysName" == "freebsd" ]; then
+ SPH_NAME=freebsd
+ fi
+
+ if [ "$SPH_SYSNAME" == "linux" ];then
+ glibc_ver=`ldd --version | grep libc | awk -F ')' '{print $2}'|awk '{gsub(/^\s+|\s+$/, "");print}'`
+ if [ "$VERSION" == "3.7.1" ] && [ `echo "2.29 > $glibc_ver " | bc` -eq 1 ];then
+ SPH_NAME=${SPH_NAME}-glibc2.17
+ fi
+ if [ "$VERSION" == "3.6.1" ] && [ `echo "2.29 > $glibc_ver " | bc` -eq 1 ];then
+ SPH_NAME=${SPH_NAME}-glibc2.17
+ fi
+ fi
+
+
+ FILE_NAME=sphinx-${VERSION_NUM}-${SPH_SYSNAME}-${SPH_NAME}
+ FILE_TGZ=${FILE_NAME}.tar.gz
+
+ echo $FILE_TGZ
+ # curl -sSLo ${SPHINX_DIR}/${FILE_TGZ} http://sphinxsearch.com/files/${FILE_TGZ}
+ if [ ! -f ${SPHINX_DIR}/${FILE_TGZ} ];then
+ wget --no-check-certificate -O ${SPHINX_DIR}/${FILE_TGZ} http://sphinxsearch.com/files/${FILE_TGZ}
+ fi
+
+ cd ${SPHINX_DIR} && tar -zxvf ${FILE_TGZ}
+
+ if [ "$?" == "0" ];then
+ mkdir -p $SPHINX_DIR
+ cp -rf ${SPHINX_DIR}/sphinx-${VERSION}/ $serverPath/sphinx/bin
+ fi
+
+ if [ -d $serverPath/sphinx ];then
+ echo "${VERSION}" > $serverPath/sphinx/version.pl
+ echo '安装Sphinx完成'
+ cd ${rootPath} && python3 ${rootPath}/plugins/sphinx/index.py start
+
+ if [ $sysName != 'Darwin' ]; then
+ cd ${rootPath} && python3 ${rootPath}/plugins/sphinx/index.py initd_install
+ fi
+ fi
+
+ if [ -d ${SPHINX_DIR}/sphinx-${VERSION} ];then
+ rm -rf ${SPHINX_DIR}/sphinx-${VERSION}
+ fi
+}
+
+Uninstall_sphinx()
+{
+ if [ -f /usr/lib/systemd/system/sphinx.service ] || [ -f /lib/systemd/system/sphinx.service ];then
+ systemctl stop sphinx
+ systemctl disable sphinx
+
+ if [ -f /usr/lib/systemd/system/sphinx.service ];then
+ rm -rf /usr/lib/systemd/system/sphinx.service
+ fi
+
+ if [ -f /lib/systemd/system/sphinx.service ];then
+ rm -rf /lib/systemd/system/sphinx.service
+ fi
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/sphinx/initd/sphinx ];then
+ $serverPath/sphinx/initd/sphinx stop
+ fi
+
+ if [ -d $serverPath/sphinx ];then
+ echo "rm -rf $serverPath/sphinx"
+ rm -rf $serverPath/sphinx
+ fi
+
+ echo "卸载sphinx成功"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_sphinx
+else
+ Uninstall_sphinx
+fi
diff --git a/plugins/sphinx/js/sphinx.js b/plugins/sphinx/js/sphinx.js
new file mode 100755
index 000000000..1fd4aa637
--- /dev/null
+++ b/plugins/sphinx/js/sphinx.js
@@ -0,0 +1,311 @@
+function spPostMin(method, args, callback){
+
+ var req_data = {};
+ req_data['name'] = 'sphinx';
+ req_data['func'] = method;
+
+ if (typeof(args) != 'undefined' && args!=''){
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function myPost(method, args, callback, title){
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(toArrayObject(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+
+ var _title = '正在获取...';
+ if (typeof(title) != 'undefined'){
+ _title = title;
+ }
+
+ $.post('/plugins/run', {name:'mysql', func:method, args:_args}, function(data) {
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+function spPost(method, args, callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+ spPostMin(method,args,function(data){
+ layer.close(loadT);
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ });
+}
+
+function commonFunc(){
+ var con = '重建索引 ';
+ con += ' 自动创建配置 ';
+ $(".soft-man-con").html(con);
+}
+
+function autoMakeConf(){
+ var xm_db_list;
+
+ var con = '';
+ con += '如果数据量比较大,第一次启动会失败!(可通过手动建立索引) ';
+ con += '以下内容,需手动加入计划任务。 ';
+ layer.open({
+ type: 1,
+ area: ['380px','350px'],
+ title: '自动创建配置',
+ closeBtn: 1,
+ shift: 5,
+ shadeClose: true,
+ btn:["提交","关闭"],
+ content: " \
+ \
+
选择数据库 \
+
\
+ \
+ 无 \
+ \
+
\
+
\
+ \
+ \
+
是否增量 \
+
\
+ \
+ 否 \
+ 是 \
+ \
+
\
+
\
+ \
+
是否覆盖配置 \
+
\
+ \
+ 是 \
+ 否 \
+ \
+
\
+
\
+ \
+ 具体配置,仍须手动修改!!! \
+ 增量索引,需要有更新权限,主从分离时,需要主库配置 \
+ \
+ \
+ ",
+
+ success:function(l,i){
+ $(l).find('.layui-layer-content').css('overflow','visible');
+
+ xm_db_list = xmSelect.render({
+ el: '#table',
+ repeat: false,
+ toolbar: {show: true},
+ data: [],
+ });
+
+ myPost('get_db_list', {"page":1,"page_size":20}, function(data){
+ var rdata = $.parseJSON(data.data);
+ var dblist = rdata.data;
+
+ var db_html = '';
+ for (var i = 0; i < dblist.length; i++) {
+ db_html += ""+dblist[i]['name']+" ";
+ }
+
+ if (dblist.length > 0){
+ initDbSelect(dblist[0]['name']);
+ }
+ $('select[name="dbname"]').html(db_html);
+ });
+
+ $('select[name="dbname"]').change(function(){
+ var db = $('select[name="dbname"]').val();
+ initDbSelect(db);
+ });
+
+ },
+ yes:function(index){
+ var args = {}
+ args['db'] = $('select[name="dbname"]').val();
+ args['is_delta'] = $('select[name="is_delta"]').val();
+ args['is_cover'] = $('select[name="is_cover"]').val();
+ args['tables'] = xm_db_list.getValue('value').join(',');
+ // console.log(args);
+ spPost('db_to_sphinx', args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ // console.log(rdata);
+ showMsg(rdata.msg,function(){
+ if (rdata.status){
+ layer.close(index);
+ confirmRebuildIndex();
+ }
+ },{icon: rdata.status ? 1 : 2}, 2000);
+ });
+ }
+
+ });
+
+ function initDbSelect(db){
+ if (db == ''){
+ return;
+ }
+ getDbInfo(db, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var tables = rdata.tables;
+
+ var idx_db = [];
+ for (var i = 0; i < tables.length; i++) {
+ var t = {};
+ t['name'] = tables[i]['table_name'];
+ t['value'] = tables[i]['table_name'];
+ idx_db.push(t);
+ }
+ xm_db_list = xmSelect.render({el: '#table', filterable: true,repeat: false,toolbar: {show: true},data: idx_db,});
+ });
+ }
+
+ function getDbInfo(db_name, callback){
+ myPost('get_db_info', {name:db_name}, function(data){
+ callback(data);
+ });
+ }
+}
+
+function rebuildIndex(){
+ spPost('rebuild', '', function(data){
+ if (data.data == 'ok'){
+ layer.msg('重建成功!',{icon:1,time:2000,shade: [0.3, '#000']});
+ } else {
+ layer.msg(data.data,{icon:2,time:10000,shade: [0.3, '#000']});
+ }
+ });
+}
+
+function confirmRebuildIndex(){
+ layer.confirm("是否重建索引?", {icon:3,closeBtn: 1} , function(){
+ rebuildIndex();
+ });
+}
+
+
+function tryRebuildIndex(){
+ layer.confirm("修改配置后,是否尝试重建索引!", {icon:3,closeBtn: 1} , function(){
+ rebuildIndex();
+ });
+}
+
+
+function secToTime(s) {
+ var t;
+ if(s > -1){
+ var hour = Math.floor(s/3600);
+ var min = Math.floor(s/60) % 60;
+ var sec = s % 60;
+ if(hour < 10) {
+ t = '0'+ hour + ":";
+ } else {
+ t = hour + ":";
+ }
+
+ if(min < 10){t += "0";}
+ t += min + ":";
+ if(sec < 10){t += "0";}
+ t += sec.toFixed(2);
+ }
+ return t;
+}
+
+
+function runStatus(){
+ spPost('run_status', '', function(data){
+ var rdata = $.parseJSON(data.data);
+ if (!rdata['status']){
+ layer.msg(rdata['msg'],{icon:2,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ var idata = rdata.data;
+
+ var tbody = '';
+ for (var i in idata) {
+ tbody += ''+i+' ' + idata[i] + ' '+i+' ';
+ }
+
+ var con = '\
+ \
+ 运行时间 ' + secToTime(idata.uptime) + ' 每秒查询 ' + parseInt(parseInt(idata.queries) / parseInt(idata.uptime)) + ' \
+ 总连接次数 ' + idata.connections + ' work_queue_length ' +idata.work_queue_length + ' \
+ agent_connect ' + idata.agent_connect+ ' workers_active ' + idata.workers_active + ' \
+ agent_retry ' + idata.agent_retry + ' workers_total ' + idata.workers_total + ' \
+ \
+
\
+
\
+ \
+ \
+ '+tbody+'\
+ \
+
\
+
';
+
+ $(".soft-man-con").html(con);
+ });
+}
+
+function readme(){
+ spPost('sphinx_cmd', '', function(data){
+
+ var rdata = $.parseJSON(data.data);
+ if (!rdata['status']){
+ layer.msg(rdata['msg'],{icon:2,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ // console.log(rdata['data']);
+ var con = '';
+
+ con += '如果数据量比较大,第一次启动会失败!(可通过手动建立索引) ';
+ con += '以下内容,需手动加入计划任务。 ';
+
+ con += '全量:' + rdata['data']['cmd'] + ' --all --rotate ';
+
+ //主索引
+ for (var i = 0; i < rdata['data']['index'].length; i++) {
+ var index_kv = rdata['data']['index'][i];
+ var index = index_kv['index'];
+ // console.log(index);
+ con += '主索引 :' + rdata['data']['cmd'] + ' '+ index +' --rotate ';
+ if (typeof(index_kv['delta']) != 'undefined'){
+ var delta = index_kv['delta'];
+ con += '增量索引 :' + rdata['data']['cmd'] + ' '+ delta +' --rotate ';
+ con += '合并索引 :' + rdata['data']['cmd'] + ' --merge '+ index + ' ' + delta +' --rotate ';
+ }
+ }
+ con += ' ';
+
+ $(".soft-man-con").html(con);
+ });
+
+}
+
diff --git a/plugins/sphinx/tool_cron.py b/plugins/sphinx/tool_cron.py
new file mode 100644
index 000000000..7f7aeccc7
--- /dev/null
+++ b/plugins/sphinx/tool_cron.py
@@ -0,0 +1,211 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import json
+
+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
+from utils.crontab import crontab as MwCrontab
+
+
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'sphinx'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getTaskConf():
+ conf = getServerDir() + "/cron_config.json"
+ return conf
+
+def getTaskDeltaConf():
+ conf = getServerDir() + "/cron_delta_config.json"
+ return conf
+
+def getConfigData():
+ conf = getTaskConf()
+ if os.path.exists(conf):
+ return json.loads(mw.readFile(getTaskConf()))
+ return {
+ "task_id": -1,
+ "period": "day-n",
+ "where1": "1",
+ "hour": "0",
+ "minute": "15",
+ }
+
+def getConfigDeltaData():
+ conf = getTaskDeltaConf()
+ if os.path.exists(conf):
+ return json.loads(mw.readFile(getTaskDeltaConf()))
+ return {
+ "task_id": -1,
+ "period": "minute-n",
+ "where1": "3",
+ "hour": "0",
+ "minute": "0",
+ }
+
+
+def createBgTask():
+ removeBgTask()
+ removeDeltaBgTask()
+
+ createBgTaskByName(getPluginName())
+ createBgTaskDeltaByName(getPluginName())
+ return True
+
+
+def createBgTaskByName(name):
+ args = getConfigData()
+ _name = "[勿删]Sphinx全量更新[" + name + "]"
+ res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
+ if res:
+ return True
+
+ if "task_id" in args and args["task_id"] > 0:
+ res = mw.M("crontab").field("id, name").where("id=?", (args["task_id"],)).find()
+ if res and res["id"] == args["task_id"]:
+ print("计划任务已经存在!")
+ return True
+
+ mw_dir = mw.getPanelDir()
+ cmd = '''
+mw_dir=%s
+rname=%s
+plugin_path=%s
+script_path=%s
+logs_file=$plugin_path/${rname}.log
+''' % (mw_dir, name, getServerDir(), getPluginDir())
+ cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n"
+ cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n"
+ cmd += 'echo "python3 $script_path/index.py update_all"' + "\n"
+ cmd += 'cd $mw_dir && python3 $script_path/index.py update_all' + "\n"
+ cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
+ cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
+
+ params = {
+ 'name': _name,
+ 'type': args['period'],
+ 'week': "",
+ 'where1': args['where1'],
+ 'hour': args['hour'],
+ 'minute': args['minute'],
+ 'save': "",
+ 'backup_to': "",
+ 'stype': "toShell",
+ 'sname': '',
+ 'sbody': cmd,
+ 'url_address': '',
+ }
+
+ task_id = MwCrontab.instance().add(params)
+ if task_id > 0:
+ args["task_id"] = task_id
+ args["name"] = name
+ mw.writeFile(getTaskConf(), json.dumps(args))
+
+def createBgTaskDeltaByName(name):
+ args = getConfigDeltaData()
+ _name = "[勿删]Sphinx增量更新[" + name + "]"
+ res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
+ if res:
+ return True
+
+ if "task_id" in args and args["task_id"] > 0:
+ res = mw.M("crontab").field("id, name").where("id=?", (args["task_id"],)).find()
+ if res and res["id"] == args["task_id"]:
+ print("计划任务已经存在!")
+ return True
+
+ mw_dir = mw.getPanelDir()
+ cmd = '''
+mw_dir=%s
+rname=%s
+plugin_path=%s
+script_path=%s
+logs_file=$plugin_path/${rname}.log
+''' % (mw_dir, name, getServerDir(), getPluginDir())
+ cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n"
+ cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n"
+ cmd += 'echo "python3 $script_path/index.py update_delta"' + "\n"
+ cmd += 'cd $mw_dir && python3 $script_path/index.py update_delta' + "\n"
+ cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
+ cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
+
+ params = {
+ 'name': _name,
+ 'type': args['period'],
+ 'week': "",
+ 'where1': args['where1'],
+ 'hour': args['hour'],
+ 'minute': args['minute'],
+ 'save': "",
+ 'backup_to': "",
+ 'stype': "toShell",
+ 'sname': '',
+ 'sbody': cmd,
+ 'url_address': '',
+ }
+
+ task_id = MwCrontab.instance().add(params)
+ if task_id > 0:
+ args["task_id"] = task_id
+ args["name"] = name
+ mw.writeFile(getTaskConf(), json.dumps(args))
+
+
+def removeBgTask():
+ cfg = getConfigData()
+ if "task_id" in cfg and cfg["task_id"] > 0:
+ res = mw.M("crontab").field("id, name").where(
+ "id=?", (cfg["task_id"],)).find()
+ if res and res["id"] == cfg["task_id"]:
+ data = MwCrontab.instance().delete(cfg["task_id"])
+ if data['status']:
+ cfg["task_id"] = -1
+ mw.writeFile(getTaskConf(), json.dumps(cfg))
+ return True
+ return False
+
+def removeDeltaBgTask():
+ cfg = getConfigDeltaData()
+ if "task_id" in cfg and cfg["task_id"] > 0:
+ res = mw.M("crontab").field("id, name").where(
+ "id=?", (cfg["task_id"],)).find()
+ if res and res["id"] == cfg["task_id"]:
+ data = MwCrontab.instance().delete(cfg["task_id"])
+ if data['status']:
+ cfg["task_id"] = -1
+ mw.writeFile(getTaskDeltaConf(), json.dumps(cfg))
+ return True
+ return False
+
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ action = sys.argv[1]
+ if action == "remove":
+ removeBgTask()
+ removeDeltaBgTask()
+ elif action == "add":
+ createBgTask()
diff --git a/plugins/sphinx/tpl/discuz.conf b/plugins/sphinx/tpl/discuz.conf
new file mode 100644
index 000000000..ab2ba35fe
--- /dev/null
+++ b/plugins/sphinx/tpl/discuz.conf
@@ -0,0 +1,127 @@
+#
+# Discuz Sphinx Config File
+#
+
+indexer
+{
+ mem_limit = 32M
+ write_buffer = 64M
+}
+
+searchd
+{
+ listen = 9312
+ listen = 9306:mysql41
+ log = {$SERVER_APP}/index/searchd.log
+ query_log = {$SERVER_APP}/index/query.log
+ read_timeout = 5
+ max_children = 0
+ pid_file = {$SERVER_APP}/index/searchd.pid
+ seamless_rotate = 1
+ preopen_indexes = 1
+ unlink_old = 1
+ #workers = threads # for RT to work
+ binlog_path = {$SERVER_APP}/index/binlog
+}
+
+
+source pre_forum_thread
+{
+ type = mysql
+ sql_host = 127.0.0.1
+ sql_user = bbs
+ sql_pass = bbs
+ sql_db = bbs
+ sql_port = 3306
+ sql_query_pre = SET NAMES UTF8
+ sql_query_pre = SET SESSION query_cache_type=OFF
+ sql_query_pre = REPLACE INTO bbs_common_sphinxcounter SELECT 1, MAX(tid) FROM bbs_forum_thread
+ sql_query = SELECT t.tid as id,t.tid,t.subject,t.digest,t.displayorder,t.authorid,t.lastpost,t.special \
+ FROM bbs_forum_thread AS t WHERE t.tid>=$start AND t.tid<=$end
+ sql_query_range = SELECT (SELECT MIN(tid) FROM bbs_forum_thread),maxid FROM bbs_common_sphinxcounter WHERE indexid=1
+
+ sql_range_step = 5000
+ sql_attr_uint = tid
+
+ sql_attr_uint = digest
+ sql_attr_uint = displayorder
+ sql_attr_uint = authorid
+ sql_attr_uint = special
+ sql_attr_timestamp =lastpost
+}
+
+index pre_forum_thread
+{
+ source = pre_forum_thread
+ path = {$SERVER_APP}/index/db/pre_forum_thread/index
+
+ min_word_len = 2
+ html_strip = 1
+ ngram_len = 1
+ ngram_chars = U+3000..U+2FA1F
+}
+
+
+# threads_minute
+source pre_forum_thread_minute : pre_forum_thread
+{
+ sql_query_pre = SET NAMES UTF8
+ sql_query_pre = SET SESSION query_cache_type=OFF
+ sql_query_range = SELECT maxid-1,(SELECT MAX(tid) FROM bbs_forum_thread) FROM bbs_common_sphinxcounter WHERE indexid=1
+}
+
+# threads_minute
+index pre_forum_thread_minute : pre_forum_thread
+{
+ source = pre_forum_thread_minute
+ path = {$SERVER_APP}/index/db/pre_forum_thread/pre_forum_thread_minute
+}
+
+#posts
+source pre_forum_post : pre_forum_thread
+{
+ type = mysql
+ sql_query_pre =
+ sql_query_pre = SET NAMES UTF8
+ sql_query_pre = SET SESSION query_cache_type=OFF
+ sql_query_pre = REPLACE INTO bbs_common_sphinxcounter SELECT 2, MAX(pid) FROM bbs_forum_post
+ sql_query = SELECT p.pid AS id,p.tid,p.subject,p.message,t.digest,t.displayorder,t.authorid,t.lastpost,t.special \
+ FROM bbs_forum_post AS p LEFT JOIN bbs_forum_thread AS t USING(tid) where p.pid >=$start and p.pid <=$end \
+ AND p.first=1
+ sql_query_range = SELECT (SELECT MIN(pid) FROM bbs_forum_post),maxid FROM bbs_common_sphinxcounter WHERE indexid=2
+ sql_range_step = 5000
+ sql_attr_uint = tid
+ sql_attr_uint = digest
+ sql_attr_uint= displayorder
+ sql_attr_uint = authorid
+ sql_attr_uint = special
+ sql_attr_timestamp = lastpost
+}
+
+#posts
+index pre_forum_post
+{
+ source = pre_forum_post
+ path = {$SERVER_APP}/index/db/pre_forum_thread/pre_forum_post
+
+ min_word_len = 2
+ html_strip = 1
+ ngram_len = 1
+ ngram_chars = U+3000..U+2FA1F
+}
+
+#pre_forum_post_minute
+source pre_forum_post_minute : pre_forum_post
+{
+ sql_query_pre = SET NAMES UTF8
+ sql_query_pre = SET SESSION query_cache_type=OFF
+ sql_query_range = SELECT maxid-1,(SELECT MAX(pid) FROM bbs_forum_post) FROM bbs_common_sphinxcounter WHERE indexid=2
+}
+
+#pre_forum_post_minute
+index pre_forum_post_minute : pre_forum_post
+{
+ source = pre_forum_thread
+ path = {$SERVER_APP}/index/db/pre_forum_thread/pre_forum_post_minute
+}
+
diff --git a/plugins/sphinx/tpl/maccms.conf b/plugins/sphinx/tpl/maccms.conf
new file mode 100644
index 000000000..ad70ae6f8
--- /dev/null
+++ b/plugins/sphinx/tpl/maccms.conf
@@ -0,0 +1,95 @@
+#
+# Maccms Sphinx Config File
+#
+
+# 创建统计表
+# CREATE TABLE `sph_counter` ( `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT, `counter_id` bigint(20) unsigned NOT NULL,`max_doc_id` bigint(20) unsigned NOT NULL,PRIMARY KEY (`id`))
+
+indexer
+{
+ mem_limit = 32M
+ write_buffer = 64M
+}
+
+searchd
+{
+ listen = 9312
+ listen = 9306:mysql41
+ log = {$SERVER_APP}/index/searchd.log
+ query_log = {$SERVER_APP}/index/query.log
+ read_timeout = 5
+ max_children = 0
+ pid_file = {$SERVER_APP}/index/searchd.pid
+ seamless_rotate = 1
+ preopen_indexes = 1
+ unlink_old = 1
+ #workers = threads # for RT to work
+ binlog_path = {$SERVER_APP}/index/binlog
+}
+
+
+# -------- maccms 全量更新 start --------
+
+source mac_vod
+{
+ type = mysql
+ sql_host = 127.0.0.1
+ sql_user = maccms
+ sql_pass = maccms
+ sql_db = maccms
+ sql_port = 3306
+ sql_query_range = SELECT min(vod_id), max(vod_id) FROM mac_vod
+ sql_query_pre = SET NAMES UTF8
+ sql_query_pre = SET SESSION query_cache_type=OFF
+ sql_query = SELECT vod_id as id, vod_name, vod_tag, vod_class, vod_actor, vod_director, vod_time,vod_time_add FROM mac_vod WHERE vod_status=1 and vod_id>=$start AND vod_id<=$end
+ sql_range_step = 1000
+
+
+ sql_attr_uint = vod_id
+ sql_attr_timestamp = vod_time
+ sql_attr_timestamp = vod_time_add
+ sql_field_string = vod_name
+
+ sql_query_post = UPDATE sph_counter SET max_doc_id=(SELECT MAX(vod_id) FROM mac_vod) where counter_id=1
+}
+
+index mac_vod
+{
+ source = mac_vod
+ path = {$SERVER_APP}/index/db/maccms/index
+
+ min_word_len = 2
+ html_strip = 1
+ ngram_len = 1
+ ngram_chars = U+3000..U+2FA1F
+}
+
+# -------- maccms 全量更新 end --------
+
+
+# -------- maccms 增量更新 start --------
+
+
+source mac_vod_delta : mac_vod
+{
+ sql_query_pre = SET NAMES utf8
+
+ sql_query_range = SELECT (SELECT max_doc_id FROM `sph_counter` where counter_id=1) as min, (SELECT max(vod_id) FROM mac_vod) as max
+
+ sql_query = SELECT vod_id as id, vod_name, vod_tag, vod_class, vod_actor, vod_director, vod_time,vod_time_add FROM mac_vod WHERE vod_status=1 and vod_id>=$start AND vod_id<=$end
+
+ sql_query_post = UPDATE sph_counter SET max_doc_id=(SELECT MAX(vod_id) FROM mac_vod) where counter_id=1
+}
+
+index mac_vod_delta : mac_vod
+{
+ source = mac_vod_delta
+ path = {$SERVER_APP}/index/db/maccms_delta/index
+
+ min_word_len = 2
+ html_strip = 1
+ ngram_len = 1
+ ngram_chars = U+3000..U+2FA1F
+}
+
+# -------- maccms 增量更新 end --------
\ No newline at end of file
diff --git a/plugins/sphinx/tpl/none.conf b/plugins/sphinx/tpl/none.conf
new file mode 100755
index 000000000..de9e5f270
--- /dev/null
+++ b/plugins/sphinx/tpl/none.conf
@@ -0,0 +1,28 @@
+#
+# Minimal Sphinx configuration sample (clean, simple, functional)
+#
+
+
+searchd
+{
+ listen = 9312
+ listen = 9306:mysql41
+ log = {$SERVER_APP}/index/searchd.log
+ query_log = {$SERVER_APP}/index/query.log
+ pid_file = {$SERVER_APP}/index/searchd.pid
+ #workers = threads # for RT to work
+ binlog_path = {$SERVER_APP}/index/binlog
+ read_timeout = 5
+ max_children = 0
+ seamless_rotate = 1
+ preopen_indexes = 1
+ unlink_old = 1
+}
+
+index mydocs
+{
+ type = rt
+ path = {$SERVER_APP}/bin/doc
+ rt_field = title
+ rt_attr_json = j
+}
\ No newline at end of file
diff --git a/plugins/sphinx/tpl/qbittorrent.conf b/plugins/sphinx/tpl/qbittorrent.conf
new file mode 100755
index 000000000..46f61331d
--- /dev/null
+++ b/plugins/sphinx/tpl/qbittorrent.conf
@@ -0,0 +1,54 @@
+#
+# Minimal Sphinx configuration sample (clean, simple, functional)
+#
+
+indexer
+{
+ mem_limit = 32M
+}
+
+searchd
+{
+ listen = 9312
+ listen = 9306:mysql41
+ log = {$SERVER_APP}/index/searchd.log
+ query_log = {$SERVER_APP}/index/query.log
+ read_timeout = 5
+ max_children = 0
+ pid_file = {$SERVER_APP}/index/searchd.pid
+ seamless_rotate = 1
+ preopen_indexes = 1
+ unlink_old = 1
+ #workers = threads # for RT to work
+ binlog_path = {$SERVER_APP}/binlog
+}
+
+source qbittorrent
+{
+ type = mysql
+
+ sql_host = 127.0.0.1
+ sql_user = qbittorrent
+ sql_pass = qbittorrent
+ sql_db = qbittorrent
+ sql_port = 3306 # optional, default is 3306
+
+ sql_query_range = SELECT min(id), max(id) FROM pl_hash_list
+ sql_range_step = 1000
+
+ sql_query_pre = SET NAMES utf8
+ sql_query = SELECT id, name, UNIX_TIMESTAMP(create_time) AS create_time \
+ FROM pl_hash_list where status=0 and id >= $start AND id <= $end
+
+ sql_attr_timestamp = create_time
+}
+
+
+index qbittorrent
+{
+ source = qbittorrent
+ path = {$SERVER_APP}/index/db/qbittorrent/index
+
+ ngram_len = 1
+ ngram_chars = U+3000..U+2FA1F
+}
diff --git a/plugins/sphinx/tpl/simdht.conf b/plugins/sphinx/tpl/simdht.conf
new file mode 100755
index 000000000..51d5b26ea
--- /dev/null
+++ b/plugins/sphinx/tpl/simdht.conf
@@ -0,0 +1,59 @@
+#
+# Minimal Sphinx configuration sample (clean, simple, functional)
+#
+
+indexer
+{
+ mem_limit = 32M
+}
+
+searchd
+{
+ listen = 9312
+ listen = 9306:mysql41
+ log = {$SERVER_APP}/index/searchd.log
+ query_log = {$SERVER_APP}/index/query.log
+ read_timeout = 5
+ max_children = 0
+ pid_file = {$SERVER_APP}/index/searchd.pid
+ seamless_rotate = 1
+ preopen_indexes = 1
+ unlink_old = 1
+ #workers = threads # for RT to work
+ binlog_path = {$SERVER_APP}/index/binlog
+}
+
+source search_hash
+{
+ type = mysql
+
+ sql_host = 127.0.0.1
+ sql_user = ssbc
+ sql_pass = ssbc
+ sql_db = ssbc
+ sql_port = 3306 # optional, default is 3306
+
+ sql_query_range = SELECT min(id), max(id) FROM search_hash
+ sql_range_step = 1000
+
+ sql_query_pre = SET NAMES utf8
+ sql_query = \
+ SELECT id, name, CRC32(category) AS category, length, UNIX_TIMESTAMP(create_time) AS create_time, UNIX_TIMESTAMP(last_seen) AS last_seen\
+ FROM search_hash where id >= $start AND id <= $end AND is_has=1
+
+ sql_attr_bigint = length
+ sql_attr_timestamp = create_time
+ sql_attr_timestamp = last_seen
+ sql_attr_uint = category
+
+}
+
+
+index search_hash
+{
+ source = search_hash
+ path = {$SERVER_APP}/index/db/search_hash/index
+
+ ngram_len = 1
+ ngram_chars = U+3000..U+2FA1F
+}
diff --git a/plugins/sphinx/tpl/simdht_delta.conf b/plugins/sphinx/tpl/simdht_delta.conf
new file mode 100755
index 000000000..e20eed842
--- /dev/null
+++ b/plugins/sphinx/tpl/simdht_delta.conf
@@ -0,0 +1,79 @@
+#
+# Minimal Sphinx configuration sample (clean, simple, functional)
+#
+
+indexer
+{
+ mem_limit = 128M
+}
+
+searchd
+{
+ listen = 9312
+ listen = 9306:mysql41
+ log = {$SERVER_APP}/index/searchd.log
+ query_log = {$SERVER_APP}/index/query.log
+ read_timeout = 5
+ max_children = 0
+ pid_file = {$SERVER_APP}/index/searchd.pid
+ seamless_rotate = 1
+ preopen_indexes = 1
+ unlink_old = 1
+ #workers = threads # for RT to work
+ binlog_path = {$SERVER_APP}/index/binlog
+}
+
+source search_hash
+{
+ type = mysql
+
+ sql_host = 127.0.0.1
+ sql_user = ssbc
+ sql_pass = ssbc
+ sql_db = ssbc
+ sql_port = 3306 # optional, default is 3306
+
+ sql_query_pre = UPDATE sph_counter SET max_doc_id=(SELECT MAX(id) FROM search_hash) where counter_id=1
+
+ sql_query_range = SELECT min(id), max(id) FROM search_hash
+ sql_range_step = 1000
+
+ sql_query_pre = SET NAMES utf8
+ sql_query = SELECT * from ( SELECT s1.id id, s1.name name, s1.category category, s1.length length, UNIX_TIMESTAMP(s1.create_time) as create_time, UNIX_TIMESTAMP(s1.last_seen) as last_seen, s2.file_list file_list, s1.info_hash info_hash FROM search_hash s1 left join search_filelist s2 on s1.info_hash=s2.info_hash where s1.is_has=1 and s1.id >= $start AND s1.id <= $end ) as s WHERE s.file_list is not null
+
+ sql_attr_bigint = length
+ sql_attr_timestamp = create_time
+ sql_attr_timestamp = last_seen
+ sql_attr_uint = category
+}
+
+source search_hash_delta : search_hash
+{
+ sql_query_range = SELECT (SELECT max_doc_id FROM `sph_counter` where counter_id=1) as min, (SELECT max(id) FROM search_hash) as max
+ sql_query_pre = SET NAMES utf8
+ sql_query = SELECT * from ( SELECT s1.id id, s1.name name, s1.category category, s1.length length, UNIX_TIMESTAMP(s1.create_time) as create_time, UNIX_TIMESTAMP(s1.last_seen) as last_seen, s2.file_list file_list, s1.info_hash info_hash FROM search_hash s1 left join search_filelist s2 on s1.info_hash=s2.info_hash where s1.is_has=1 and s1.id >= $start AND s1.id <= $end ) as s WHERE s.file_list is not null
+ sql_attr_bigint = length
+ sql_attr_timestamp = create_time
+ sql_attr_timestamp = last_seen
+ sql_attr_uint = category
+ sql_query_post = UPDATE sph_counter SET max_doc_id=(SELECT MAX(id) FROM search_hash) where counter_id=1
+}
+
+
+index search_hash
+{
+ source = search_hash
+ path = {$SERVER_APP}/index/db/search_hash/index
+
+ ngram_len = 1
+ ngram_chars = U+3000..U+2FA1F
+}
+
+index search_hash_delta : search_hash
+{
+ source = search_hash_delta
+ path = {$SERVER_APP}/index/db/search_hash_delta/index
+
+ ngram_len = 1
+ ngram_chars = U+3000..U+2FA1F
+}
diff --git a/plugins/sphinx/tpl/video.conf b/plugins/sphinx/tpl/video.conf
new file mode 100755
index 000000000..2a93c22e5
--- /dev/null
+++ b/plugins/sphinx/tpl/video.conf
@@ -0,0 +1,58 @@
+#
+# Minimal Sphinx configuration sample (clean, simple, functional)
+#
+
+indexer
+{
+ mem_limit = 32M
+}
+
+searchd
+{
+ listen = 9312
+ listen = 9306:mysql41
+ log = {$SERVER_APP}/index/searchd.log
+ query_log = {$SERVER_APP}/index/query.log
+ read_timeout = 5
+ max_children = 0
+ pid_file = {$SERVER_APP}/index/searchd.pid
+ seamless_rotate = 1
+ preopen_indexes = 1
+ unlink_old = 1
+ #workers = threads # for RT to work
+ binlog_path = {$SERVER_APP}/binlog
+}
+
+source sp_video
+{
+ type = mysql
+
+ sql_host = 127.0.0.1
+ sql_user = video_spider
+ sql_pass = video_spider
+ sql_db = video_spider
+ sql_port = 3306 # optional, default is 3306
+
+ sql_query_range = SELECT min(id), max(id) FROM sp_video
+ sql_range_step = 1000
+
+ sql_query_pre = SET NAMES utf8
+ sql_query = SELECT id, name, UNIX_TIMESTAMP(create_time) AS create_time, UNIX_TIMESTAMP(update_time) AS update_time \
+ FROM sp_video where id >= $start AND id <= $end
+
+ sql_attr_bigint = length
+ sql_attr_timestamp = create_time
+ sql_attr_timestamp = update_time
+ sql_attr_uint = category
+
+}
+
+
+index sp_video
+{
+ source = sp_video
+ path = {$SERVER_APP}/index/db/sp_video/index
+
+ ngram_len = 1
+ ngram_chars = U+3000..U+2FA1F
+}
diff --git a/plugins/supervisor/conf/supervisor.conf b/plugins/supervisor/conf/supervisor.conf
new file mode 100644
index 000000000..d8df8fdd2
--- /dev/null
+++ b/plugins/supervisor/conf/supervisor.conf
@@ -0,0 +1,170 @@
+; Sample supervisor config file.
+;
+; For more information on the config file, please see:
+; http://supervisord.org/configuration.html
+;
+; Notes:
+; - Shell expansion ("~" or "$HOME") is not supported. Environment
+; variables can be expanded using this syntax: "%(ENV_HOME)s".
+; - Quotes around values are not supported, except in the case of
+; the environment= options as shown below.
+; - Comments must have a leading space: "a=b ;comment" not "a=b;comment".
+; - Command will be truncated if it looks like a config file comment, e.g.
+; "command=bash -c 'foo ; bar'" will truncate to "command=bash -c 'foo ".
+;
+; Warning:
+; Paths throughout this example file use /tmp because it is available on most
+; systems. You will likely need to change these to locations more appropriate
+; for your system. Some systems periodically delete older files in /tmp.
+; Notably, if the socket file defined in the [unix_http_server] section below
+; is deleted, supervisorctl will be unable to connect to supervisord.
+
+[unix_http_server]
+file={$SERVER_PATH}/supervisor/run/supervisor.sock
+;chmod=0700 ; socket file mode (default 0700)
+;chown=nobody:nogroup ; socket file uid:gid owner
+;username=user ; default is no username (open server)
+;password=123 ; default is no password (open server)
+
+; Security Warning:
+; The inet HTTP server is not enabled by default. The inet HTTP server is
+; enabled by uncommenting the [inet_http_server] section below. The inet
+; HTTP server is intended for use within a trusted environment only. It
+; should only be bound to localhost or only accessible from within an
+; isolated, trusted network. The inet HTTP server does not support any
+; form of encryption. The inet HTTP server does not use authentication
+; by default (see the username= and password= options to add authentication).
+; Never expose the inet HTTP server to the public internet.
+
+;[inet_http_server] ; inet (TCP) server disabled by default
+;port=127.0.0.1:9001 ; ip_address:port specifier, *:port for all iface
+;username=user ; default is no username (open server)
+;password=123 ; default is no password (open server)
+
+[supervisord]
+logfile={$SERVER_PATH}/supervisor/log/supervisor.log
+logfile_maxbytes=50MB ; max main logfile bytes b4 rotation; default 50MB
+logfile_backups=10 ; # of main logfile backups; 0 means none, default 10
+loglevel=info ; log level; default info; others: debug,warn,trace
+pidfile={$SERVER_PATH}/supervisor/run/supervisor.pid
+nodaemon=false ; start in foreground if true; default false
+silent=false ; no logs to stdout if true; default false
+minfds=1024 ; min. avail startup file descriptors; default 1024
+minprocs=200 ; min. avail process descriptors;default 200
+;umask=022 ; process file creation umask; default 022
+user={$OS_USER} ; setuid to this UNIX account at startup; recommended if root
+;identifier=supervisor ; supervisord identifier, default is 'supervisor'
+;directory=/tmp ; default is not to cd during start
+;nocleanup=true ; don't clean up tempfiles at start; default false
+;childlogdir=/tmp ; 'AUTO' child log dir, default $TEMP
+;environment=KEY="value" ; key value pairs to add to environment
+;strip_ansi=false ; strip ansi escape codes in logs; def. false
+
+; The rpcinterface:supervisor section must remain in the config file for
+; RPC (supervisorctl/web interface) to work. Additional interfaces may be
+; added by defining them in separate [rpcinterface:x] sections.
+
+[rpcinterface:supervisor]
+supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
+
+; The supervisorctl section configures how supervisorctl will connect to
+; supervisord. configure it match the settings in either the unix_http_server
+; or inet_http_server section.
+
+[supervisorctl]
+serverurl=unix:///{$SERVER_PATH}/supervisor/run/supervisor.sock
+;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket
+;username=chris ; should be same as in [*_http_server] if set
+;password=123 ; should be same as in [*_http_server] if set
+;prompt=mysupervisor ; cmd line prompt (default "supervisor")
+;history_file=~/.sc_history ; use readline history if available
+
+; The sample program section below shows all possible program subsection values.
+; Create one or more 'real' program: sections to be able to control them under
+; supervisor.
+
+;[program:theprogramname]
+;command=/bin/cat ; the program (relative uses PATH, can take args)
+;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
+;numprocs=1 ; number of processes copies to start (def 1)
+;directory=/tmp ; directory to cwd to before exec (def no cwd)
+;umask=022 ; umask for process (default None)
+;priority=999 ; the relative start priority (default 999)
+;autostart=true ; start at supervisord start (default: true)
+;startsecs=1 ; # of secs prog must stay up to be running (def. 1)
+;startretries=3 ; max # of serial start failures when starting (default 3)
+;autorestart=unexpected ; when to restart if exited after running (def: unexpected)
+;exitcodes=0 ; 'expected' exit codes used with autorestart (default 0)
+;stopsignal=QUIT ; signal used to kill process (default TERM)
+;stopwaitsecs=10 ; max num secs to wait b4 SIGKILL (default 10)
+;stopasgroup=false ; send stop signal to the UNIX process group (default false)
+;killasgroup=false ; SIGKILL the UNIX process group (def false)
+;user=chrism ; setuid to this UNIX account to run the program
+;redirect_stderr=true ; redirect proc stderr to stdout (default false)
+;stdout_logfile=/a/path ; stdout log path, NONE for none; default AUTO
+;stdout_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)
+;stdout_logfile_backups=10 ; # of stdout logfile backups (0 means none, default 10)
+;stdout_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0)
+;stdout_events_enabled=false ; emit events on stdout writes (default false)
+;stdout_syslog=false ; send stdout to syslog with process name (default false)
+;stderr_logfile=/a/path ; stderr log path, NONE for none; default AUTO
+;stderr_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)
+;stderr_logfile_backups=10 ; # of stderr logfile backups (0 means none, default 10)
+;stderr_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0)
+;stderr_events_enabled=false ; emit events on stderr writes (default false)
+;stderr_syslog=false ; send stderr to syslog with process name (default false)
+;environment=A="1",B="2" ; process environment additions (def no adds)
+;serverurl=AUTO ; override serverurl computation (childutils)
+
+; The sample eventlistener section below shows all possible eventlistener
+; subsection values. Create one or more 'real' eventlistener: sections to be
+; able to handle event notifications sent by supervisord.
+
+;[eventlistener:theeventlistenername]
+;command=/bin/eventlistener ; the program (relative uses PATH, can take args)
+;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
+;numprocs=1 ; number of processes copies to start (def 1)
+;events=EVENT ; event notif. types to subscribe to (req'd)
+;buffer_size=10 ; event buffer queue size (default 10)
+;directory=/tmp ; directory to cwd to before exec (def no cwd)
+;umask=022 ; umask for process (default None)
+;priority=-1 ; the relative start priority (default -1)
+;autostart=true ; start at supervisord start (default: true)
+;startsecs=1 ; # of secs prog must stay up to be running (def. 1)
+;startretries=3 ; max # of serial start failures when starting (default 3)
+;autorestart=unexpected ; autorestart if exited after running (def: unexpected)
+;exitcodes=0 ; 'expected' exit codes used with autorestart (default 0)
+;stopsignal=QUIT ; signal used to kill process (default TERM)
+;stopwaitsecs=10 ; max num secs to wait b4 SIGKILL (default 10)
+;stopasgroup=false ; send stop signal to the UNIX process group (default false)
+;killasgroup=false ; SIGKILL the UNIX process group (def false)
+;user=chrism ; setuid to this UNIX account to run the program
+;redirect_stderr=false ; redirect_stderr=true is not allowed for eventlisteners
+;stdout_logfile=/a/path ; stdout log path, NONE for none; default AUTO
+;stdout_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)
+;stdout_logfile_backups=10 ; # of stdout logfile backups (0 means none, default 10)
+;stdout_events_enabled=false ; emit events on stdout writes (default false)
+;stdout_syslog=false ; send stdout to syslog with process name (default false)
+;stderr_logfile=/a/path ; stderr log path, NONE for none; default AUTO
+;stderr_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)
+;stderr_logfile_backups=10 ; # of stderr logfile backups (0 means none, default 10)
+;stderr_events_enabled=false ; emit events on stderr writes (default false)
+;stderr_syslog=false ; send stderr to syslog with process name (default false)
+;environment=A="1",B="2" ; process environment additions
+;serverurl=AUTO ; override serverurl computation (childutils)
+
+; The sample group section below shows all possible group values. Create one
+; or more 'real' group: sections to create "heterogeneous" process groups.
+
+;[group:thegroupname]
+;programs=progname1,progname2 ; each refers to 'x' in [program:x] definitions
+;priority=999 ; the relative start priority (default 999)
+
+; The [include] section can just contain the "files" setting. This
+; setting can list multiple files (separated by whitespace or
+; newlines). It can also contain wildcards. The filenames are
+; interpreted as relative to this file. Included files *cannot*
+; include files themselves.
+
+[include]
+files = {$SERVER_PATH}/supervisor/conf.d/*.ini
diff --git a/plugins/supervisor/ico.png b/plugins/supervisor/ico.png
new file mode 100644
index 000000000..229e38752
Binary files /dev/null and b/plugins/supervisor/ico.png differ
diff --git a/plugins/supervisor/index.html b/plugins/supervisor/index.html
new file mode 100755
index 000000000..4300e73a2
--- /dev/null
+++ b/plugins/supervisor/index.html
@@ -0,0 +1,31 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/supervisor/index.py b/plugins/supervisor/index.py
new file mode 100755
index 000000000..1a9d69c6a
--- /dev/null
+++ b/plugins/supervisor/index.py
@@ -0,0 +1,652 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+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 'supervisor'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getConf():
+ path = getServerDir() + "/supervisor.conf"
+ return path
+
+
+def getConfTpl():
+ path = getPluginDir() + "/conf/supervisor.conf"
+ return path
+
+
+def getSubConfDir():
+ return getServerDir() + "/conf.d"
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
+ return path
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':', 1)
+ tmp[t[0]] = t[1]
+
+ 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 status():
+ data = mw.execShell(
+ "ps -ef|grep supervisor | grep -v grep | grep -v index.py | awk '{print $2}'")
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def initDreplace():
+
+ confD = getServerDir() + "/conf.d"
+ conf = getServerDir() + "/supervisor.conf"
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/supervisor.service'
+ systemServiceTpl = getPluginDir() + '/init.d/supervisor.service'
+
+ service_path = mw.getServerDir()
+
+ if not os.path.exists(confD):
+ os.mkdir(confD)
+
+ if not os.path.exists(conf):
+ # config replace
+ user = 'root'
+ if mw.isAppleSystem():
+ cmd = "who | sed -n '2, 1p' |awk '{print $1}'"
+ user = mw.execShell(cmd)[0].strip()
+
+ conf_content = mw.readFile(getConfTpl())
+ conf_content = conf_content.replace('{$SERVER_PATH}', service_path)
+ conf_content = conf_content.replace('{$OS_USER}', user)
+ mw.writeFile(conf, conf_content)
+
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ activate_file = mw.getPanelDir() + '/bin/activate'
+ if os.path.exists(activate_file):
+ supervisord_bin = mw.execShell(
+ 'source ' + activate_file + '&& which supervisord')[0].strip()
+ else:
+ supervisord_bin = mw.execShell('which supervisord')[0].strip()
+
+ se_content = mw.readFile(systemServiceTpl)
+ se_content = se_content.replace('{$SERVER_PATH}', service_path)
+ se_content = se_content.replace('{$SUP_BIN}', supervisord_bin)
+ mw.writeFile(systemService, se_content)
+ mw.execShell('systemctl daemon-reload')
+
+ return True
+
+
+def supOp(method):
+ initDreplace()
+
+ if not mw.isAppleSystem():
+ data = mw.execShell('systemctl ' + method + ' supervisor')
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+ if method in ('reload', 'restart'):
+ return 'ok'
+
+ cmd = 'supervisord -c ' + getServerDir() + '/supervisor.conf'
+ if method == 'stop':
+ cmd = "ps -ef|grep supervisor | grep -v grep | grep -v index.py | awk '{print $2}'|xargs kill"
+ data = mw.execShell(cmd)
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def start():
+ return supOp('start')
+
+
+def stop():
+ return supOp('stop')
+
+
+def restart():
+ return supOp('restart')
+
+
+def reload():
+ return supOp('reload')
+
+
+def initdStatus():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status supervisor | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+
+def initdInstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl enable supervisor')
+ return 'ok'
+
+
+def initdUinstall():
+ if not app_debug:
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl diable supervisor')
+ return 'ok'
+
+
+def getSupList():
+ data = {}
+
+ statusFile = getServerDir() + "/status.txt"
+ supCtl = 'supervisorctl -c ' + getServerDir() + "/supervisor.conf"
+ cmd = "%s update; %s status > %s" % (supCtl, supCtl, statusFile)
+ mw.execShell(cmd)
+
+ with open(statusFile, "r") as fr:
+ lines = fr.readlines()
+
+ array_list = []
+ process_list = []
+ for r in lines:
+ array = r.split()
+ if array:
+ d = dict()
+ program = array[0].split(':')[0]
+ if program in process_list:
+ continue
+ process_list.append(program)
+ d["program"] = program
+ d["runStatus"] = array[1]
+ if array[1] == "RUNNING":
+ d["status"] = "1"
+ d["pid"] = array[3][:-1]
+ else:
+ d["status"] = "0"
+ d["pid"] = ""
+ file = getServerDir() + '/conf.d/' + program + ".ini"
+ if not os.path.exists(file):
+ continue
+ with open(file, "r") as fr:
+ infos = fr.readlines()
+ for line in infos:
+ if "command=" in line.strip():
+ # d["command"] = line.strip().split('=')[1]
+ d["command"] = "子配置查看"
+ if "user=" in line.strip():
+ d["user"] = line.strip().split('=')[1]
+ if "priority=" in line.strip():
+ d["priority"] = line.strip().split('=')[1]
+ if "numprocs=" in line.strip():
+ d["numprocs"] = line.strip().split('=')[1]
+ array_list.append(d)
+
+ # print(array_list)
+ data = {}
+ data['data'] = array_list
+ return mw.getJson(data)
+
+def confDList():
+ confd_dir = getServerDir() + '/conf.d'
+ clist = os.listdir(confd_dir)
+ array_list = []
+ for x in range(len(clist)):
+ t = {}
+ t['name'] = clist[x]
+ array_list.append(t)
+
+ data = {}
+ data['data'] = array_list
+ return mw.getJson(data)
+
+
+def confDlistTraceLog():
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ confd_dir = getServerDir() + '/conf.d/' + args['name']
+ content = mw.readFile(confd_dir)
+ rep = r'stdout_logfile\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+
+def confDlistErrorLog():
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+
+ confd_dir = getServerDir() + '/conf.d/' + args['name']
+ content = mw.readFile(confd_dir)
+ rep = r'stderr_logfile\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+def getUserListData():
+ user = getServerDir() + "/user.txt"
+ if not os.path.isfile(user):
+ os.system(r"touch {}".format(user))
+ res = mw.execShell("cat /etc/passwd > " + user)
+ with open(user, "r") as fr:
+ users = fr.readlines()
+ fr.close()
+ os.remove(user)
+
+ user_list = []
+ special = ["bin", "daemon", "adm", "lp", "shutdown", "halt", "mail", "operator", "games",
+ "avahi-autoipd", "systemd-bus-proxy", "systemd-network", "dbus", "polkitd", "tss", "ntp"]
+ for u in users:
+ user = re.split(':', u)[0]
+ if user[0] == '#':
+ continue
+ if user in special:
+ continue
+ user_list.append(user)
+
+ if mw.isAppleSystem():
+ cmd = "who | sed -n '2, 1p' |awk '{print $1}'"
+ user = mw.execShell(cmd)[0].strip()
+ user_list.append(user)
+ return user_list
+
+
+def getUserList():
+ user_list = getUserListData()
+ return mw.getJson(user_list)
+
+
+def addJob():
+ args = getArgs()
+ data = checkArgs(args, ['name', 'user', 'path', 'command', 'numprocs'])
+ if not data[0]:
+ return data[1]
+
+ program = args['name']
+ command = args['command']
+ path = args['path']
+ numprocs = args['numprocs']
+ user = args['user']
+
+ log_dir = getServerDir() + '/log/'
+
+ w_body = ""
+ w_body += "[program:" + program + "]" + "\n"
+ w_body += "command=" + command + "\n"
+ w_body += "directory=" + path + "\n"
+ w_body += "autorestart=true" + "\n"
+ w_body += "startsecs=3" + "\n"
+ w_body += "startretries=3" + "\n"
+ w_body += "stdout_logfile=" + log_dir + program + ".out.log" + "\n"
+ w_body += "stderr_logfile=" + log_dir + program + ".err.log" + "\n"
+ w_body += "stdout_logfile_maxbytes=1MB" + "\n"
+ w_body += "stderr_logfile_maxbytes=1MB" + "\n"
+ w_body += "user=" + user + "\n"
+ w_body += "priority=999" + "\n"
+ w_body += "numprocs={0}".format(numprocs) + "\n"
+ w_body += "process_name=%(program_name)s_%(process_num)02d"
+ # _%(process_num)02d
+
+ dstFile = getSubConfDir() + "/" + program + '.ini'
+
+ mw.writeFile(dstFile, w_body)
+
+ return mw.returnJson(True, '增加守护进程成功!')
+
+
+def startJob():
+ args = getArgs()
+ data = checkArgs(args, ['name', 'status'])
+ if not data[0]:
+ return data[1]
+
+ supCtl = 'supervisorctl -c ' + getServerDir() + "/supervisor.conf"
+
+ name = args['name']
+ status = args['status']
+
+ action = "启动"
+ cmd = supCtl + " start " + name + ":"
+ if status == 'start':
+ action = "停止"
+ cmd = supCtl + " stop " + name + ":"
+ # print(cmd)
+ data = mw.execShell(cmd)
+ # print(data)
+
+ if data[1] != '':
+ return mw.returnJson(False, action + '[' + name + ']失败!')
+ return mw.returnJson(True, action + '[' + name + ']成功!')
+
+
+def restartJob():
+ args = getArgs()
+ data = checkArgs(args, ['name', 'status'])
+ if not data[0]:
+ return data[1]
+
+ supCtl = 'supervisorctl -c ' + getServerDir() + "/supervisor.conf"
+
+ name = args['name']
+ status = args['status']
+
+ cmd = supCtl + " stop " + name + ":"
+ data = mw.execShell(cmd)
+ cmd = supCtl + " start " + name + ":"
+ data = mw.execShell(cmd)
+
+ if data[1] != '':
+ return mw.returnJson(False, '[' + name + ']重启失败!')
+ return mw.returnJson(True, '[' + name + ']重启成功!')
+
+
+def delJob():
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+ name = args['name']
+
+ supCtl = 'supervisorctl'
+ log_dir = getServerDir() + '/log/'
+
+ result = mw.execShell("{0} stop ".format(supCtl) + name + ":")
+ program = getServerDir() + "/conf.d/" + name + ".ini"
+
+ # 删除日志文件
+ outlog = log_dir + name + ".out.log"
+ if os.path.isfile(outlog):
+ os.remove(outlog)
+ errlog = log_dir + name + ".err.log"
+ if os.path.isfile(errlog):
+ os.remove(errlog)
+
+ # 删除ini文件
+ if os.path.isfile(program):
+ os.remove(program)
+ result = mw.execShell(
+ "{0} update".format(supCtl))
+ return mw.returnJson(True, '删除守护进程成功!')
+ else:
+ result = mw.execShell(
+ "{0} update".format(supCtl))
+ return mw.returnJson(False, '该守护进程不存在!')
+
+
+def updateJob():
+ args = getArgs()
+ data = checkArgs(args, ["name", 'user', 'numprocs', 'priority'])
+ if not data[0]:
+ return data[1]
+ user = args['user']
+ numprocs = args['numprocs']
+ priority = args['priority']
+ name = args['name']
+ programFile = getServerDir() + "/conf.d/" + name + ".ini"
+
+ mess = {}
+ infos = []
+ with open(programFile, "r") as fr:
+ infos = fr.readlines()
+
+ for line in infos:
+ if "command=" in line.strip():
+ mess["command"] = line.strip().split('=')[1]
+ if "directory=" in line.strip():
+ mess["path"] = line.strip().split('=')[1]
+
+ # print(mess)
+ log_file_name = getServerDir() + '/log/' + name
+
+ w_body = ""
+ w_body += "[program:" + name + "]" + "\n"
+ w_body += "command=" + mess["command"] + "\n"
+ w_body += "directory=" + mess["path"] + "\n"
+ w_body += "autorestart=true" + "\n"
+ w_body += "startsecs=3" + "\n"
+ w_body += "startretries=3" + "\n"
+ w_body += "stdout_logfile=" + log_file_name + ".out.log" + "\n"
+ w_body += "stderr_logfile=" + log_file_name + ".err.log" + "\n"
+ w_body += "stdout_logfile_maxbytes=2MB" + "\n"
+ w_body += "stderr_logfile_maxbytes=2MB" + "\n"
+ w_body += "user=" + user + "\n"
+ w_body += "priority=" + priority + "\n"
+ w_body += "numprocs={0}".format(numprocs) + "\n"
+ w_body += "process_name=%(program_name)s_%(process_num)02d"
+
+ mw.writeFile(programFile, w_body)
+
+ return mw.returnJson(True, '修改守护进程成功!')
+
+
+def getJobInfo():
+ args = getArgs()
+ data = checkArgs(args, ['name'])
+ if not data[0]:
+ return data[1]
+ name = args['name']
+
+ mess = {}
+ infos = []
+ info = {}
+ program = getServerDir() + "/conf.d/" + name + ".ini"
+ with open(program, "r") as fr:
+ infos = fr.readlines()
+ mess = {}
+ for line in infos:
+ if "user=" in line.strip():
+ mess["user"] = line.strip().split('=')[1]
+ if "numprocs=" in line.strip():
+ mess["numprocs"] = line.strip().split('=')[1]
+ if "priority=" in line.strip():
+ mess["priority"] = line.strip().split('=')[1]
+ userlist = getUserListData()
+ info["userlist"] = userlist
+ info["daemoninfo"] = mess
+ return mw.getJson(info)
+
+
+def configTpl():
+ path = getServerDir() + '/conf.d'
+ pathFile = os.listdir(path)
+ tmp = []
+ for one in pathFile:
+ if one.endswith(".ini"):
+ file = path + '/' + one
+ tmp.append(file)
+ return mw.getJson(tmp)
+
+
+def readConfigTpl():
+ args = getArgs()
+ data = checkArgs(args, ['file'])
+ if not data[0]:
+ return data[1]
+
+ content = mw.readFile(args['file'])
+ return mw.returnJson(True, 'ok', content)
+
+
+def readConfigLogTpl():
+ args = getArgs()
+ data = checkArgs(args, ['file'])
+ if not data[0]:
+ return data[1]
+ file_log = args['file']
+ line_log = args['line']
+
+ with open(file_log, "r") as fr:
+ infos = fr.readlines()
+
+ stdout_logfile = ''
+ for line in infos:
+ if "stdout_logfile=" in line.strip():
+ stdout_logfile = line.strip().split('=')[1]
+
+ if stdout_logfile != '':
+ data = mw.getLastLine(stdout_logfile, int(line_log))
+ return mw.returnJson(True, 'OK', data)
+ return mw.returnJson(False, 'OK', '')
+
+
+def readConfigLogErrorTpl():
+ args = getArgs()
+ data = checkArgs(args, ['file'])
+ if not data[0]:
+ return data[1]
+ file_log = args['file']
+ line_log = args['line']
+
+ with open(file_log, "r") as fr:
+ infos = fr.readlines()
+
+ stderr_logfile = ''
+ for line in infos:
+ if "stderr_logfile=" in line.strip():
+ stderr_logfile = line.strip().split('=')[1]
+
+ if stderr_logfile != '':
+ data = mw.getLastLine(stderr_logfile, int(line_log))
+ return mw.returnJson(True, 'OK', data)
+ return mw.returnJson(False, 'OK', '')
+
+
+def supClearLog():
+ args = getArgs()
+ data = checkArgs(args, ['file'])
+ if not data[0]:
+ return data[1]
+ file_log = args['file']
+
+ with open(file_log, "r") as fr:
+ infos = fr.readlines()
+
+ stdout_logfile = ''
+ stderr_logfile = ''
+ for line in infos:
+ if "stdout_logfile=" in line.strip():
+ stdout_logfile = line.strip().split('=')[1]
+ if "stderr_logfile=" in line.strip():
+ stderr_logfile = line.strip().split('=')[1]
+
+ cmd_stdout = "echo '' > " + stdout_logfile
+ cmd_stderr = "echo '' > " + stderr_logfile
+ mw.execShell(cmd_stdout)
+ mw.execShell(cmd_stderr)
+ return mw.returnJson(True, '清空成功')
+
+
+def runLog():
+ return getServerDir() + '/log/supervisor.log'
+
+if __name__ == "__main__":
+ 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 == 'config_tpl':
+ print(configTpl())
+ elif func == 'read_config_tpl':
+ print(readConfigTpl())
+ elif func == 'read_config_log_tpl':
+ print(readConfigLogTpl())
+ elif func == 'read_config_log_error_tpl':
+ print(readConfigLogErrorTpl())
+ elif func == 'sup_clear_log':
+ print(supClearLog())
+ elif func == 'conf':
+ print(getConf())
+ elif func == 'run_log':
+ print(runLog())
+ elif func == 'get_user_list':
+ print(getUserList())
+ elif func == 'get_sup_list':
+ print(getSupList())
+ elif func == 'confd_list':
+ print(confDList())
+ elif func == 'confd_list_trace_log':
+ print(confDlistTraceLog())
+ elif func == 'confd_list_error_log':
+ print(confDlistErrorLog())
+ elif func == 'add_job':
+ print(addJob())
+ elif func == 'start_job':
+ print(startJob())
+ elif func == 'restart_job':
+ print(restartJob())
+ elif func == 'del_job':
+ print(delJob())
+ elif func == 'update_job':
+ print(updateJob())
+ elif func == 'get_job_info':
+ print(getJobInfo())
+ else:
+ print('error')
diff --git a/plugins/supervisor/info.json b/plugins/supervisor/info.json
new file mode 100755
index 000000000..7b6d71416
--- /dev/null
+++ b/plugins/supervisor/info.json
@@ -0,0 +1,18 @@
+{
+ "sort": 1,
+ "ps": "一个Python开发的通用的进程管理程序",
+ "name": "supervisor",
+ "title": "supervisor",
+ "shell": "install.sh",
+ "versions":["1.0"],
+ "updates":["1.0"],
+ "tip": "soft",
+ "checks": "server/supervisor",
+ "path": "server/supervisor",
+ "display": 1,
+ "author": "python",
+ "date": "2022-06-15",
+ "home": "https://pypi.org/project/supervisor/",
+ "type": 0,
+ "pid": "4"
+}
\ No newline at end of file
diff --git a/plugins/supervisor/init.d/supervisor.service b/plugins/supervisor/init.d/supervisor.service
new file mode 100644
index 000000000..1da932a35
--- /dev/null
+++ b/plugins/supervisor/init.d/supervisor.service
@@ -0,0 +1,14 @@
+[Unit]
+Description=supervisor server daemon
+After=network.target
+
+[Service]
+Type=forking
+ExecStart={$SUP_BIN} -c {$SERVER_PATH}/supervisor/supervisor.conf
+ExecStop=kill -s TERM $MAINPID
+ExecReload=kill -s HUP $MAINPID
+KillMode=process
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/plugins/supervisor/install.sh b/plugins/supervisor/install.sh
new file mode 100755
index 000000000..96ece61ac
--- /dev/null
+++ b/plugins/supervisor/install.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+VERSION=$2
+
+# /www/server/mdserver-web/bin/supervisorctl -c /www/server/supervisor/supervisor.conf
+# cmd | status
+
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+OSNAME_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+Install_app()
+{
+ echo '正在安装[supervisor]...'
+ mkdir -p $serverPath/source
+ mkdir -p $serverPath/supervisor
+ mkdir -p $serverPath/supervisor/log
+ mkdir -p $serverPath/supervisor/run
+
+ echo 'supervisor install...'
+ if [ "centos" == "$OSNAME" ] || [ "fedora" == "$OSNAME" ];then
+ pip install supervisor
+ elif [ "ubuntu" == "$OSNAME" ] || [ "debian" == "$OSNAME" ] ;then
+ pip install supervisor
+ else
+ pip install supervisor
+ # brew install supervisor
+ fi
+
+ echo "${VERSION}" > $serverPath/supervisor/version.pl
+
+ cd ${rootPath} && python3 plugins/supervisor/index.py start
+ cd ${rootPath} && python3 plugins/supervisor/index.py initd_install
+
+ echo '安装完成[supervisor]'
+}
+
+Uninstall_app()
+{
+
+ if [ -f /usr/lib/systemd/system/supervisor.service ] || [ -f /lib/systemd/system/supervisor.service ];then
+ systemctl stop supervisor
+ systemctl disable supervisor
+ rm -rf /usr/lib/systemd/system/supervisor.service
+ rm -rf /lib/systemd/system/supervisor.service
+ systemctl daemon-reload
+ fi
+
+ pip uninstall supervisor -y
+
+ rm -rf $serverPath/supervisor
+
+ echo "卸载完成[supervisor]"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_app
+else
+ Uninstall_app
+fi
diff --git a/plugins/supervisor/js/supervisor.js b/plugins/supervisor/js/supervisor.js
new file mode 100755
index 000000000..a3780da0f
--- /dev/null
+++ b/plugins/supervisor/js/supervisor.js
@@ -0,0 +1,624 @@
+
+function myPost(method,args,callback, title){
+
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(str2Obj(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+
+ var _title = '正在获取...';
+ if (typeof(title) != 'undefined'){
+ _title = title;
+ }
+
+ var loadT = layer.msg(_title, { icon: 16, time: 0, shade: 0.3 });
+ $.post('/plugins/run', {name:'supervisor', func:method, args:_args}, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+
+function supList(page, search){
+ var _data = {};
+ if (typeof(page) =='undefined'){
+ var page = 1;
+ }
+
+ _data['page'] = page;
+ _data['page_size'] = 10;
+ if(typeof(search) != 'undefined'){
+ _data['search'] = search;
+ }
+
+ myPost('get_sup_list', _data, function(data){
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata.data);
+ var list = '';
+ for(i in rdata.data){
+ list += '';
+ list += '' + rdata.data[i]['program'] +' ';
+ list += '' + rdata.data[i]['command'] +' ';
+ list += '' + rdata.data[i]['user'] +' ';
+ list += '' + rdata.data[i]['pid'] +' ';
+ list += '' + rdata.data[i]['numprocs'] +' ';
+ list += '' + rdata.data[i]['priority'] +' ';
+
+ sup_status = 'start'
+ sup_status_desc = 'start'
+ if (rdata.data[i]['runStatus'] == 'RUNNING' ){
+ sup_status = 'start'
+ sup_status_desc = '已启动'
+ } else{
+ sup_status = 'stop'
+ sup_status_desc = '已停止'
+ }
+
+ list += ''+sup_status_desc+' ';
+
+ list += '' + rdata.data[i]['runStatus'] +' ';
+
+ list += '\
+ '+sup_status_desc+' | ' +
+ '重启 | ' +
+ '修改 | ' +
+ '删除 ' +
+ ' ';
+
+ list += ' ';
+ }
+
+ if(rdata.data.length==0){
+ list = "当前没有数据 ";
+ }
+
+ var con = '';
+
+ con += '\
+ supervisord 常见进程状态详细如下: \
+ 1:STOPPED:该进程已停止。 2:STOPPING:由于停止请求,该进程正在停止。 \
+ 3:RUNNING:该进程正在运行。 4:STARTING:该进程由于启动请求而开始。 \
+ 5:FATAL:该进程无法成功启动。 \
+ 6:BACKOFF:该进程进入“ 启动”状态,但随后退出的速度太快而无法移至“ 运行”状态。 \
+
'
+
+ $(".soft-man-con").html(con);
+ $('#databasePage').html(rdata.page);
+ });
+}
+
+
+function startOrStop(name,status){
+ myPost('start_job',{'name':name,'status':status}, function(data){
+ var rdata = $.parseJSON(data.data);
+ showMsg(rdata.msg, function(){
+ supList(1,10);
+ },{icon:rdata.status?1:2}, rdata.status ? 2000 : 10000);
+ });
+}
+
+function restartJob(name,status){
+ myPost('restart_job',{'name':name,'status':status}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2});
+ setTimeout(function(){
+ supList(1,10);
+ },2000);
+ });
+}
+
+function updateJob(name){
+ myPost('get_job_info',{'name':name},function(data){
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata);
+ var defaultPath = $("#defaultPath").html();
+ var ulist = "启动用户 ";
+ for (var i=0;i"+rdata['userlist'][i]+"";
+ } else {
+ ulist += ""+rdata['userlist'][i]+" ";
+ }
+ }
+
+ ulist += "
";
+ layer.open({
+ type: 1,
+ area: '500px',
+ title: '修改守护进程',
+ closeBtn: 2,
+ shift: 0,
+ shadeClose: false,
+ btn: ['确定', '取消'],
+ content: "",
+ yes: function(index, layero){
+ // console.log(index,layero);
+
+ var options = ['name','user','numprocs','priority'];
+ var opval = {};
+ for(var i in options){
+ opval[options[i]] = $('[name="'+ options[i] +'"]').val();
+ if(opval[options[i]] == ''){
+ if(options[i] == 'user'){
+ layer.msg('启动用户不能为空');
+ return false;
+ }else if(options[i] == 'priority'){
+ layer.msg('启动优先级不能为空');
+ return false;
+ }else if(options[i] == 'numprocs'){
+ layer.msg('进程数量不能为空!');
+ return false;
+ }
+ }
+ }
+
+ var numprocs = $('[name=numprocs]').val();
+ if (!(/(^[1-9]\d*$)/.test(numprocs))) {
+ layer.msg('进程数量请输入正整数')
+ return false;
+ }
+
+ myPost("update_job", opval, function(data){
+ rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2});
+ rdata.status ? layer.close(index):'';
+ supList(1,10);
+ })
+ return;
+ }
+ });
+ });
+}
+
+
+//卸载软件
+function delJob(name) {
+ layer.confirm(msgTpl('是否删除守护进程[{1}]?', [name]), { icon: 3, closeBtn: 2 }, function() {
+ ///////////////////////////////////////
+ var data = {'name': name};
+ var loadT = layer.msg('正在处理,请稍候...', { icon: 16, time: 0, shade: [0.3, '#000'] });
+ myPost('del_job', data, function(rdata){
+ layer.close(loadT)
+
+ rdata = $.parseJSON(rdata.data);
+ supList(1,10);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+ ///////////////////////////////////////
+ });
+}
+
+//添加站点
+function supAdd() {
+ myPost('get_user_list',{},function(data){
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata);
+
+ var defaultPath = $("#defaultPath").html();
+ var ulist = "启动用户 ";
+ for (var i=0;i"+rdata[i]+"";
+ }
+
+ var www = syncPost('/site/get_root_dir');
+ ulist += "
";
+ layer.open({
+ type: 1,
+ area: '500px',
+ title: '添加守护进程',
+ closeBtn: 2,
+ shift: 0,
+ shadeClose: false,
+ btn: ['确定', '取消'],
+ content: "",
+ yes: function(index, layero){
+ // console.log(index,layero);
+
+ var options = ['name','user','path','command','numprocs'];
+ var opval = {};
+ for(var i in options){
+ opval[options[i]] = $('[name="'+ options[i] +'"]').val();
+ if(opval[options[i]] == ''){
+ if(options[i] == 'name'){
+ layer.msg('进程名称不能为空');
+ return false;
+ }else if(options[i] == 'user'){
+ layer.msg('启动用户不能为空');
+ return false;
+ }else if(options[i] == 'path'){
+ layer.msg('运行目录不能为空');
+ return false;
+ }else if(options[i] == 'command'){
+ layer.msg('启动命令不能为空');
+ return false;
+ }else if(options[i] == 'numprocs'){
+ layer.msg('进程数量不能为空!');
+ return false;
+ }
+ }
+ }
+
+ var numprocs = $('[name=numprocs]').val();
+ if (!(/(^[1-9]\d*$)/.test(numprocs))) {
+ layer.msg('进程数量请输入正整数')
+ return false;
+ }
+
+ myPost("add_job", opval, function(data){
+ rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2});
+ rdata.status ? layer.close(index):'';
+ supList(1,10);
+ })
+ return;
+ }
+ });
+ });
+
+}
+
+
+//supervisor
+function supConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_func){
+ if ( typeof(version) == 'undefined' ){
+ version = '';
+ }
+
+ var func_name = 'conf';
+ if ( typeof(func) != 'undefined' ){
+ func_name = func;
+ }
+
+ var _config_tpl_func = 'config_tpl';
+ if ( typeof(config_tpl_func) != 'undefined' ){
+ _config_tpl_func = config_tpl_func;
+ }
+
+ var _read_config_tpl_func = 'read_config_tpl';
+ if ( typeof(read_config_tpl_func) != 'undefined' ){
+ _read_config_tpl_func = read_config_tpl_func;
+ }
+
+
+ var con = '提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换!
\
+ 请选择 \
+ \
+ 保存 \
+ \
+ 此处为'+ _name + version +'配置文件,若您不了解配置规则,请勿随意修改。 \
+ ';
+ $(".soft-man-con").html(con);
+
+ function getFileName(file){
+ var list = file.split('/');
+ var f = list[list.length-1];
+ return f
+ }
+
+ var fileName = '';
+ $.post('/plugins/run',{name:_name, func:_config_tpl_func,version:version}, function(data){
+ var rdata = $.parseJSON(data.data);
+ for (var i = 0; i < rdata.length; i++) {
+ $('#config_tpl').append(''+getFileName(rdata[i])+' ');
+ }
+
+
+ $("#textBody").empty().text('');
+ $(".CodeMirror").remove();
+ var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), {
+ extraKeys: {
+ "Ctrl-Space": "autocomplete",
+ "Ctrl-F": "findPersistent",
+ "Ctrl-H": "replaceAll",
+ "Ctrl-S": function() {}
+ },
+ lineNumbers: true,
+ matchBrackets:true,
+ });
+ editor.focus();
+
+ $('#config_tpl').change(function(){
+ var selected = $(this).val();
+ if (selected != '0'){
+ fileName = selected;
+ var loadT = layer.msg('配置获取中...',{icon:16,time:0,shade: [0.3, '#000']});
+
+ var _args = JSON.stringify({file:selected});
+ $.post('/plugins/run', {name:_name, func:_read_config_tpl_func,version:version,args:_args}, function(data){
+ layer.close(loadT);
+ var rdata = $.parseJSON(data.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ $("#textBody").empty().html(rdata.data);
+ $(".CodeMirror").remove();
+ var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), {
+ extraKeys: {
+ "Ctrl-Space": "autocomplete",
+ "Ctrl-F": "findPersistent",
+ "Ctrl-H": "replaceAll",
+ "Ctrl-S": function() {
+ $("#textBody").text(editor.getValue());
+ pluginConfigSave(fileName);
+ }
+ },
+ lineNumbers: true,
+ matchBrackets:true,
+ });
+ editor.focus();
+ $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0});
+ $("#onlineEditFileBtn").unbind('click');
+ $("#onlineEditFileBtn").click(function(){
+ $("#textBody").html(editor.getValue());
+ pluginConfigSave(fileName);
+ });
+ },'json');
+ }
+ });
+
+ },'json');
+
+}
+
+
+//保存
+function supConfigSave(fileName) {
+ var data = encodeURIComponent($("#textBody").val());
+ var encoding = 'utf-8';
+ var loadT = layer.msg('保存中...', {icon: 16,time: 0});
+ $.post('/files/save_body', 'data=' + data + '&path=' + fileName + '&encoding=' + encoding, function(rdata) {
+ layer.close(loadT);
+ layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
+ },'json');
+}
+
+
+
+function supLogs(_name, config_tpl_func, read_config_tpl_func,line){
+
+ var file_line = 100;
+ if ( typeof(line) != 'undefined' ){
+ file_line = line;
+ }
+
+ var _config_tpl_func = 'config_tpl';
+ if ( typeof(config_tpl_func) != 'undefined' ){
+ _config_tpl_func = config_tpl_func;
+ }
+
+ var _read_config_tpl_func = 'read_config_tpl';
+ if ( typeof(read_config_tpl_func) != 'undefined' ){
+ _read_config_tpl_func = read_config_tpl_func;
+ }
+
+ function getFileName(file){
+ var list = file.split('/');
+ var f = list[list.length-1];
+ return f
+ }
+
+ var con = '请选择 \
+ 清理日志 \
+ 查看错误日志 \
+
';
+ con += ' ';
+ $(".soft-man-con").html(con);
+ var ob = document.getElementById('info_log');
+ ob.scrollTop = ob.scrollHeight;
+
+ function clearLog(file){
+ $('#sup_clear_log').click(function(){
+ myPost('sup_clear_log', {'file':file}, function (data) {
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2,time:2000,shade: [0.3, '#000']});
+ });
+ });
+ }
+
+ function errorLog(file,file_line){
+ $('#sup_error_log').click(function(){
+ var _args = JSON.stringify({file:file,line:file_line});
+ $.post('/plugins/run', {name:_name, func:'read_config_log_error_tpl',args:_args}, function(data){
+ var rdata = $.parseJSON(data.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ $("#info_log").empty().html(rdata.data);
+ },'json');
+ });
+ }
+
+ var loadT = layer.msg('日志路径获取中...',{icon:16,time:0,shade: [0.3, '#000']});
+ $.post('/plugins/run', {name:_name, func:_config_tpl_func},function (data) {
+ layer.close(loadT);
+
+ var rdata = $.parseJSON(data.data);
+ for (var i = 0; i < rdata.length; i++) {
+ $('#config_tpl').append(''+getFileName(rdata[i])+' ');
+ }
+
+ $('#config_tpl').change(function(){
+ ///
+ var selected = $(this).val();
+ if (selected == '0'){
+ return;
+ }
+
+ fileName = selected;
+ var loadT = layer.msg('日志获取中...',{icon:16,time:0,shade: [0.3, '#000']});
+
+ var _args = JSON.stringify({file:selected,line:file_line});
+ $.post('/plugins/run', {name:_name, func:_read_config_tpl_func,args:_args}, function(data){
+ layer.close(loadT);
+ var rdata = $.parseJSON(data.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ $("#info_log").empty().html(rdata.data);
+ },'json');
+
+ clearLog(selected);
+ errorLog(selected,file_line);
+ ///
+ });
+
+ },'json');
+}
+
+
+function confdListTraceLog(name){
+ var args = {};
+ args["name"] = name;
+ pluginRollingLogs("supervisor", '', "confd_list_trace_log", JSON.stringify(args), 21);
+}
+
+function confdListErrLog(name){
+ var args = {};
+ args["name"] = name;
+ pluginRollingLogs("supervisor", '', "confd_list_error_log", JSON.stringify(args), 21);
+}
+
+function confdList(page, search){
+ var _data = {};
+ if (typeof(page) =='undefined'){
+ var page = 1;
+ }
+
+ _data['page'] = page;
+ _data['page_size'] = 10;
+ if(typeof(search) != 'undefined'){
+ _data['search'] = search;
+ }
+
+ myPost('confd_list', _data, function(data){
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata.data);
+ var list = '';
+ for(i in rdata.data){
+ list += '';
+ list += '' + rdata.data[i]['name'] +' ';
+
+ list += '\
+ 日志跟踪 | ' +
+ '查看错误日志 ' +
+ ' ';
+
+ list += ' ';
+ }
+
+ if( rdata.data.length == 0 ){
+ list = "当前没有数据 ";
+ }
+
+ var con = '';
+
+ con += '\
+ 方便查看日志 \
+
'
+
+ $(".soft-man-con").html(con);
+ $('#databasePage').html(rdata.page);
+ });
+}
+
diff --git a/plugins/swap/ico.png b/plugins/swap/ico.png
new file mode 100644
index 000000000..4ff64f09e
Binary files /dev/null and b/plugins/swap/ico.png differ
diff --git a/plugins/swap/index.html b/plugins/swap/index.html
new file mode 100755
index 000000000..0ea986471
--- /dev/null
+++ b/plugins/swap/index.html
@@ -0,0 +1,20 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/swap/index.py b/plugins/swap/index.py
new file mode 100755
index 000000000..db79fa596
--- /dev/null
+++ b/plugins/swap/index.py
@@ -0,0 +1,226 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+
+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 'swap'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getInitDFile():
+ if app_debug:
+ return '/tmp/' + getPluginName()
+ return '/etc/init.d/' + getPluginName()
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
+ return path
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+
+ 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 status():
+ data = mw.execShell("free -m|grep Swap|awk '{print $2}'")
+ if data[0].strip() == '0':
+ return 'stop'
+ return 'start'
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
+ return path
+
+
+def initDreplace():
+
+ file_tpl = getInitDTpl()
+ service_path = mw.getServerDir()
+
+ initD_path = getServerDir() + '/init.d'
+ if not os.path.exists(initD_path):
+ os.mkdir(initD_path)
+ file_bin = initD_path + '/' + getPluginName()
+
+ # initd replace
+ if not os.path.exists(file_bin):
+ content = mw.readFile(file_tpl)
+ content = content.replace(
+ '{$SERVER_PATH}', getServerDir() + '/swapfile')
+ mw.writeFile(file_bin, content)
+ mw.execShell('chmod +x ' + file_bin)
+
+ # systemd
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/swap.service'
+ systemServiceTpl = getPluginDir() + '/init.d/swap.service.tpl'
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ swapon_bin = mw.execShell('which swapon')[0].strip()
+ swapoff_bin = mw.execShell('which swapoff')[0].strip()
+ content = mw.readFile(systemServiceTpl)
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$SWAPON_BIN}', swapon_bin)
+ content = content.replace('{$SWAPOFF_BIN}', swapoff_bin)
+ mw.writeFile(systemService, content)
+ mw.execShell('systemctl daemon-reload')
+
+ return file_bin
+
+
+def swapOp(method):
+ file = initDreplace()
+
+ if not mw.isAppleSystem():
+ data = mw.execShell('systemctl ' + method + ' swap')
+ if data[1] == '':
+ return 'ok'
+ return 'fail'
+
+ data = mw.execShell(file + ' ' + method)
+ if data[1] == '':
+ return 'ok'
+ return 'fail'
+
+
+def start():
+ return swapOp('start')
+
+
+def stop():
+ return swapOp('stop')
+
+
+def restart():
+ return swapOp('restart')
+
+
+def reload():
+ return 'ok'
+
+
+def initdStatus():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status swap | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+
+def initdInstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl enable swap')
+ return 'ok'
+
+
+def initdUinstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl disable swap')
+ return 'ok'
+
+
+def swapStatus():
+ sfile = getServerDir() + '/swapfile'
+
+ if os.path.exists(sfile):
+ size = os.path.getsize(sfile) / 1024 / 1024
+ else:
+ size = '218'
+ data = {'size': size}
+ return mw.returnJson(True, "ok", data)
+
+
+def changeSwap():
+ args = getArgs()
+ data = checkArgs(args, ['size'])
+ if not data[0]:
+ return data[1]
+
+ size = args['size']
+ swapOp('stop')
+
+ gsdir = getServerDir()
+
+ cmd = 'dd if=/dev/zero of=' + gsdir + '/swapfile bs=1M count=' + size
+ cmd += ' && mkswap ' + gsdir + '/swapfile && chmod 600 ' + gsdir + '/swapfile'
+ msg = mw.execShell(cmd)
+ swapOp('start')
+
+ return mw.returnJson(True, "修改成功:\n" + msg[0])
+
+if __name__ == "__main__":
+ 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 == 'conf':
+ print(getConf())
+ elif func == "swap_status":
+ print(swapStatus())
+ elif func == "change_swap":
+ print(changeSwap())
+ else:
+ print('error')
diff --git a/plugins/swap/info.json b/plugins/swap/info.json
new file mode 100755
index 000000000..a1f0d1ea0
--- /dev/null
+++ b/plugins/swap/info.json
@@ -0,0 +1,17 @@
+{
+ "sort": 6,
+ "ps": "Linux虚拟内存",
+ "name": "swap",
+ "title": "swap",
+ "shell": "install.sh",
+ "versions":"1.1",
+ "tip": "soft",
+ "checks": "server/swap",
+ "path": "server/swap",
+ "display": 1,
+ "author": "swap",
+ "date": "2021-01-29",
+ "home": "swap",
+ "type": 0,
+ "pid": "4"
+}
\ No newline at end of file
diff --git a/plugins/swap/init.d/swap.service.tpl b/plugins/swap/init.d/swap.service.tpl
new file mode 100644
index 000000000..6765e6c55
--- /dev/null
+++ b/plugins/swap/init.d/swap.service.tpl
@@ -0,0 +1,14 @@
+
+[Unit]
+Description=Swap Process Manager
+After=network.target
+
+[Service]
+Type=forking
+ExecStart={$SERVER_PATH}/swap/init.d/swap start
+ExecStop={$SERVER_PATH}/swap/init.d/swap stop
+RemainAfterExit=yes
+
+
+[Install]
+WantedBy=multi-user.target
diff --git a/plugins/swap/init.d/swap.tpl b/plugins/swap/init.d/swap.tpl
new file mode 100644
index 000000000..86bcb6d57
--- /dev/null
+++ b/plugins/swap/init.d/swap.tpl
@@ -0,0 +1,55 @@
+#!/bin/bash
+# chkconfig: 2345 55 25
+# description: MW Cloud Service
+
+### BEGIN INIT INFO
+# Provides: bt
+# Required-Start: $all
+# Required-Stop: $all
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts mw
+# Description: starts the mw
+### END INIT INFO
+
+
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+
+app_file={$SERVER_PATH}
+
+app_start(){
+ isStart=`free -m|grep Swap|awk '{print $2}'`
+ if [ "$isStart" == '0' ];then
+ echo -e "Starting swap... \c"
+ swapon $app_file
+ echo -e "\033[32mdone\033[0m"
+ else
+ echo "Starting swap already running"
+ fi
+}
+
+app_stop()
+{
+ echo -e "Stopping swap... \c";
+ swapoff $app_file
+ echo -e "\033[32mdone\033[0m"
+}
+
+app_status()
+{
+ isStart=`free -m|grep Swap|awk '{print $2}'`
+ if [ "$isStart" == '0' ];then
+ echo -e "\033[32mswap already running\033[0m"
+ else
+ echo -e "\033[31mswap not running\033[0m"
+ fi
+}
+
+case "$1" in
+ 'start') app_start;;
+ 'stop') app_stop;;
+ 'restart'|'reload')
+ app_stop
+ app_start;;
+ 'status') app_status;;
+esac
\ No newline at end of file
diff --git a/plugins/swap/install.sh b/plugins/swap/install.sh
new file mode 100755
index 000000000..1f698fd13
--- /dev/null
+++ b/plugins/swap/install.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+SYSOS=`uname`
+
+VERSION=$2
+
+# cd /www/server/mdserver-web/plugins/swap && /bin/bash install.sh install 1.1
+
+Install_swap()
+{
+ if [ -d $serverPath/swap ];then
+ exit 0
+ fi
+
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/source
+ mkdir -p $serverPath/swap
+ echo "${VERSION}" > $serverPath/swap/version.pl
+
+ if [ "$sysName" == "Darwin" ];then
+ pass
+ else
+ dd if=/dev/zero of=$serverPath/swap/swapfile bs=1M count=1024
+ chmod 600 $serverPath/swap/swapfile
+ mkswap $serverPath/swap/swapfile
+ swapon $serverPath/swap/swapfile
+ fi
+
+ echo '安装完成'
+
+ cd ${rootPath} && python3 ${rootPath}/plugins/swap/index.py start
+ cd ${rootPath} && python3 ${rootPath}/plugins/swap/index.py initd_install
+}
+
+Uninstall_swap()
+{
+ swapoff $serverPath/swap/swapfile
+
+
+ if [ -f /usr/lib/systemd/system/swap.service ] || [ -f /lib/systemd/system/swap.service ];then
+ systemctl stop swap
+ systemctl disable swap
+ rm -rf /usr/lib/systemd/system/swap.service
+ rm -rf /lib/systemd/system/swap.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/swap/initd/swap ];then
+ $serverPath/swap/initd/swap stop
+ fi
+
+ rm -rf $serverPath/swap
+
+ echo "Uninstall_swap"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_swap
+else
+ Uninstall_swap
+fi
diff --git a/plugins/swap/js/swap.js b/plugins/swap/js/swap.js
new file mode 100755
index 000000000..a78e2db07
--- /dev/null
+++ b/plugins/swap/js/swap.js
@@ -0,0 +1,81 @@
+
+
+function swapPost(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'swap';
+ req_data['func'] = method;
+ req_data['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ //错误展示10S
+ layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+function swapStatus() {
+ swapPost('swap_status', '', {}, function(data){
+ var rdata = $.parseJSON(data.data);
+ var size = rdata.data['size'];
+
+ var spCon = '\
+
最大使用交换分区: \
+ \
+ 218MB \
+ 512MB \
+ 1GB \
+ 2GB \
+ 4GB \
+ \
+ 当前: MB\
+
\
+
修改 MB
\
+
提交
\
+
';
+
+ $(".soft-man-con").html(spCon);
+
+ $(".conf_p select[name='swap_set']").change(function() {
+ var swap_size = $(this).val();
+ if (swap_size.indexOf('GB')>-1){
+ swap_size = parseInt(swap_size)*1024;
+ } else{
+ swap_size = parseInt(swap_size);
+ }
+ $("input[name='cur_size']").val(swap_size);
+ $("input[name='size']").val(swap_size);
+ });
+ });
+}
+
+function submitSwap(){
+ var size = $("input[name='size']").val();
+ swapPost('change_swap', '',{"size":size}, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 });
+ });
+}
+
+function readme(){
+ var readme = '';
+ readme += 'dd if=/dev/zero of=/www/server/swap/swapfile bs=1M count=2048 ';
+ readme += 'mkswap /www/server/swap/swapfile ';
+ readme += ' ';
+ $('.soft-man-con').html(readme);
+}
diff --git a/plugins/sys-opt/ico.png b/plugins/sys-opt/ico.png
new file mode 100644
index 000000000..d262d40dc
Binary files /dev/null and b/plugins/sys-opt/ico.png differ
diff --git a/plugins/sys-opt/index.html b/plugins/sys-opt/index.html
new file mode 100755
index 000000000..e230b24c3
--- /dev/null
+++ b/plugins/sys-opt/index.html
@@ -0,0 +1,26 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/sys-opt/index.py b/plugins/sys-opt/index.py
new file mode 100755
index 000000000..661b57afc
--- /dev/null
+++ b/plugins/sys-opt/index.py
@@ -0,0 +1,172 @@
+# coding: utf-8
+
+import time
+import random
+import os
+import json
+import re
+import sys
+
+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
+
+
+def getPluginName():
+ return 'sys-opt'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ 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 contentReplace(content):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$SERVER_APP}', service_path + '/sys-opt')
+ return content
+
+
+def status():
+ return 'start'
+
+
+def start():
+ mw.execShell('sysctl -p')
+ return "ok"
+
+
+def stop():
+ mw.execShell('sysctl -p')
+ return 'ok'
+
+
+def restart():
+ mw.execShell('sysctl -p')
+ return 'ok'
+
+
+def reload():
+ mw.execShell('sysctl -p')
+ return 'ok'
+
+
+def configTpl():
+ path = getPluginDir() + '/tpl'
+ pathFile = os.listdir(path)
+ tmp = []
+ for one in pathFile:
+ file = path + '/' + one
+ tmp.append(file)
+ return mw.getJson(tmp)
+
+
+def readConfigTpl():
+ args = getArgs()
+ data = checkArgs(args, ['file'])
+ if not data[0]:
+ return data[1]
+
+ content = mw.readFile(args['file'])
+ content = contentReplace(content)
+ return mw.returnJson(True, 'ok', content)
+
+
+def sysConf():
+ return '/etc/sysctl.conf'
+
+
+def secRunLog():
+ if os.path.exists('/var/log/auth.log'):
+ return '/var/log/auth.log'
+ if os.path.exists('/var/log/messages'):
+ return '/var/log/messages'
+ return '/var/log/secure'
+
+
+def msgRunLog():
+ if os.path.exists('/var/log/kern.log'):
+ return '/var/log/kern.log'
+ return '/var/log/messages'
+
+
+def cronRunLog():
+ if os.path.exists('/var/log/syslog.log'):
+ return '/var/log/syslog.log'
+ if os.path.exists('/var/log/syslog'):
+ return '/var/log/syslog'
+ return '/var/log/cron'
+
+
+def systemRunLog():
+ return '/var/log/syslog'
+
+def showHosts():
+ return '/etc/hosts'
+
+def showResolv():
+ return '/etc/resolv.conf'
+
+if __name__ == "__main__":
+ 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 == 'conf':
+ print(sysConf())
+ elif func == 'config_tpl':
+ print(configTpl())
+ elif func == 'read_config_tpl':
+ print(readConfigTpl())
+ elif func == 'sec_run_log':
+ print(secRunLog())
+ elif func == 'msg_run_log':
+ print(msgRunLog())
+ elif func == 'cron_run_log':
+ print(cronRunLog())
+ elif func == 'sys_run_log':
+ print(systemRunLog())
+ elif func == 'hosts':
+ print(showHosts())
+ elif func == 'resolv':
+ print(showResolv())
+ else:
+ print('err')
diff --git a/plugins/sys-opt/info.json b/plugins/sys-opt/info.json
new file mode 100755
index 000000000..b9257f105
--- /dev/null
+++ b/plugins/sys-opt/info.json
@@ -0,0 +1,16 @@
+{
+ "sort":1,
+ "title":"系统优化",
+ "tip":"soft",
+ "name":"sys-opt",
+ "type":"扩展",
+ "ps":"仅Linux系统优化",
+ "versions":"1.0",
+ "shell":"install.sh",
+ "checks":"server/sys-opt",
+ "path": "server/sys-opt",
+ "author":"midoks",
+ "home":"",
+ "date":"2018-12-20",
+ "pid":"5"
+}
\ No newline at end of file
diff --git a/plugins/sys-opt/install.sh b/plugins/sys-opt/install.sh
new file mode 100755
index 000000000..03e7c82ff
--- /dev/null
+++ b/plugins/sys-opt/install.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+Install_sysopt()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/sys-opt
+ echo '1.0' > $serverPath/sys-opt/version.pl
+ echo '安装完成'
+
+}
+
+Uninstall_sysopt()
+{
+ rm -rf $serverPath/sys-opt
+ echo "卸载完成"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_sysopt
+else
+ Uninstall_sysopt
+fi
diff --git a/plugins/sys-opt/js/sys-opt.js b/plugins/sys-opt/js/sys-opt.js
new file mode 100755
index 000000000..b582d21a6
--- /dev/null
+++ b/plugins/sys-opt/js/sys-opt.js
@@ -0,0 +1,9 @@
+
+
+function pRead(){
+ var readme = '';
+ readme += '修改后,点击重启按钮 ';
+ readme += ' ';
+
+ $('.soft-man-con').html(readme);
+}
\ No newline at end of file
diff --git a/plugins/sys-opt/tpl/linux.conf b/plugins/sys-opt/tpl/linux.conf
new file mode 100755
index 000000000..f074f6516
--- /dev/null
+++ b/plugins/sys-opt/tpl/linux.conf
@@ -0,0 +1,63 @@
+# sysctl -w fs.file-max=6553560
+fs.file-max = 6553560
+
+net.ipv4.ip_forward = 0
+net.ipv4.conf.default.rp_filter = 1
+net.ipv4.conf.default.accept_source_route = 0
+kernel.sysrq = 0
+kernel.core_uses_pid = 1
+net.ipv4.tcp_syncookies = 1
+kernel.msgmnb = 65536
+kernel.msgmax = 65536
+kernel.shmmax = 68719476736
+kernel.shmall = 4294967296
+net.ipv4.tcp_max_tw_buckets = 6000
+net.ipv4.tcp_sack = 1
+net.ipv4.tcp_window_scaling = 1
+net.ipv4.tcp_rmem = 4096 87380 4194304
+net.ipv4.tcp_wmem = 4096 16384 4194304
+net.core.wmem_default = 8388608
+net.core.rmem_default = 8388608
+net.core.rmem_max = 16777216
+net.core.wmem_max = 16777216
+net.core.netdev_max_backlog = 262144
+net.core.somaxconn = 262144
+net.ipv4.tcp_max_orphans = 3276800
+net.ipv4.tcp_max_syn_backlog = 262144
+net.ipv4.tcp_timestamps = 0
+net.ipv4.tcp_synack_retries = 1
+net.ipv4.tcp_syn_retries = 1
+
+net.ipv4.tcp_tw_reuse = 1
+net.ipv4.tcp_mem = 94500000 915000000 927000000
+net.ipv4.tcp_fin_timeout = 1
+net.ipv4.tcp_keepalive_time = 30
+net.ipv4.ip_local_port_range = 1024 65001
+
+
+# vm opt
+vm.dirty_ratio = 40
+vm.dirty_background_ratio = 10
+vm.swappiness = 10
+vm.vfs_cache_pressure = 500
+vm.overcommit_memory=1
+vm.max_map_count = 262144
+
+# error:table full, dropping packet.
+net.nf_conntrack_max = 25000000
+net.netfilter.nf_conntrack_max = 25000000
+net.netfilter.ip_conntrack_tcp_timeout_established = 30
+net.netfilter.nf_conntrack_tcp_timeout_close_wait = 10
+net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 5
+net.netfilter.nf_conntrack_tcp_timeout_time_wait = 5
+
+#net.ipv4.tcp_tw_recycle = 1
+#net.ipv4.ip_conntrack_max = 81920
+#net.ipv4.netfilter.ip_conntrack_tcp_timeout_established = 1500
+
+#lvs
+#net.ipv4.conf.ens33.arp_ignore=1
+#net.ipv4.conf.ens33.arp_announce=2
+#net.ipv4.conf.all.arp_ignore=1
+#net.ipv4.conf.all.arp_announce=2
+#net.ipv4.ip_forward=1
\ No newline at end of file
diff --git a/plugins/sys-opt/tpl/linux_notes.conf b/plugins/sys-opt/tpl/linux_notes.conf
new file mode 100755
index 000000000..c43e244aa
--- /dev/null
+++ b/plugins/sys-opt/tpl/linux_notes.conf
@@ -0,0 +1,74 @@
+#表示开启SYN Cookies.当出现SYN等待队列溢出时,启用cookies来处理,
+#可防范少量SYN攻击,默认为0,表示关闭;
+net.ipv4.tcp_syncookies = 1
+
+#表示SYN队列的长度,默认为1024,加大队列长度为8192,
+#可以容纳更多等待连接的网络连接数;
+net.ipv4.tcp_max_syn_backlog = 65536
+
+#时间戳可以避免序列号的卷绕。一个1Gbps的链路肯定会遇到以前用过的序列号,
+#时间戳能够让内核接受这种"异常"的数据包.这里需要将其关掉;
+net.ipv4.tcp_timestamps = 0
+
+#tcp_synack_retries 显示或设定 Linux 核心在回应 SYN 要求时会尝试多少次重新发送初始 SYN,ACK 封包后才决定放弃。
+#这是所谓的三段交握 (threeway handshake) 的第二个步骤。即是说系统会尝试多少次去建立由远端启始的 TCP 连线。
+#tcp_synack_retries 的值必须为正整数,并不能超过 255。因为每一次重新发送封包都会耗费约 30 至 40 秒去等待才决定尝试下一次重新发送或决定放弃。
+#tcp_synack_retries 的缺省值为 5,即每一个连线要在约 180 秒 (3 分钟) 后才确定逾时。
+net.ipv4.tcp_synack_retries = 2
+
+#在内核放弃建立连接之前发送SYN包的数量.
+net.ipv4.tcp_syn_retries = 2
+
+#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭;
+net.ipv4.tcp_tw_recycle = 1
+#net.ipv4.tcp_tw_len = 1
+
+#表示开启重用.允许将TIME-WAIT sockets重新用于新的TCP连接,
+#默认为0,表示关闭;
+net.ipv4.tcp_tw_reuse = 1
+
+#同样有3个值,意思是:
+#net.ipv4.tcp_mem[0]:低于此值,TCP没有内存压力.
+#net.ipv4.tcp_mem[1]:在此值下,进入内存压力阶段.
+#net.ipv4.tcp_mem[2]:高于此值,TCP拒绝分配socket.
+#上述内存单位是页,而不是字节。可参考的优化值是:786432 1048576 1572864
+net.ipv4.tcp_mem = 94500000 91500000 92700000
+
+#系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。
+#如果超过这个数字,孤儿连接将即刻被复位并打印出警告信息。
+#这个限制仅仅是为了防止简单的DoS攻击,不能过分依靠它或者人为地减小这个值,
+#更应该增加这个值(如果增加了内存之后);
+net.ipv4.tcp_max_orphans = 3276800
+
+#表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间;
+net.ipv4.tcp_fin_timeout = 30
+
+#表示当keepalive起用的时候,TCP发送keepalive消息的频度,
+#缺省是2小时,改为20分钟
+net.ipv4.tcp_keeplive_time = 1200
+
+#表示用于向外连接的端口范围.缺省情况下很小:32768到61000,改为1024到65000;
+net.ipv4.ip_local_port_range = 1024 65535
+
+#表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,
+#TIME_WAIT套接字将立刻被清除并打印警告信息。默认为180000,改为 5000.
+#对于Apache、Nginx等服务器.上几行的参数可以很好地减少TIME_WAIT套接字数量;
+net.ipv4.tcp_max_tw_buckets = 5000
+
+#######################################
+
+#每个网络接口接收数据包的速率比内核处理这些包的速率快时,
+#允许送到队列的数据包的最大数目;
+net.core.netdev_max_backlog = 32768
+
+#web应用中listen函数的backlog默认会给我们内核参数的net.core.somaxconn
+#限制到128而nginx定义的NGX_LISTEN_BACKLOG默认为511,所以有必要调整这个值。
+net.core.somaxconn = 32768
+
+#最大socket读buffer,可参考的优化值:873200
+net.core.wmem_default = 8388608
+net.core.rmem_default = 8388608
+net.core.rmem_max = 16777216
+
+#最大socket写buffer,可参考的优化值:873200
+net.core.wmem_max = 16777216
\ No newline at end of file
diff --git a/plugins/system_safe/LICENSE b/plugins/system_safe/LICENSE
new file mode 100644
index 000000000..137069b82
--- /dev/null
+++ b/plugins/system_safe/LICENSE
@@ -0,0 +1,73 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+ (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/plugins/system_safe/conf/config.json b/plugins/system_safe/conf/config.json
new file mode 100755
index 000000000..4dcd92c66
--- /dev/null
+++ b/plugins/system_safe/conf/config.json
@@ -0,0 +1,960 @@
+{
+ "open": false,
+ "service": {
+ "open": true,
+ "name": "服务加固",
+ "ps": "保护系统服务,开启后将无法添加和删除服务,部分软件无法安装!",
+ "paths": [
+ {
+ "path": "/etc/rc.d",
+ "chattr": "i",
+ "s_mode": 509,
+ "d_mode": 509
+ },
+ {
+ "path": "/etc/rc.d/init.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rc.d/rc0.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rc.d/rc1.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rc.d/rc2.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rc.d/rc3.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rc.d/rc4.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rc.d/rc5.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rc.d/rc6.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rc.d/rc.local",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/init.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rc0.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rc1.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rc2.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rc3.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rc4.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rc5.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rc6.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/rcS.d",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/etc/systemd",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ },
+ {
+ "path": "/lib/systemd",
+ "chattr": "i",
+ "s_mode": 493,
+ "d_mode": 493
+ }
+ ]
+ },
+ "home": {
+ "open": true,
+ "name": "环境变量加固",
+ "ps": "保护用户环境变量不被修改,开启后无法自定义用户环境变量!",
+ "paths": [
+ {
+ "path": "/root/.bash_history",
+ "chattr": "a",
+ "s_mode": 420,
+ "d_mode": 420
+ },
+ {
+ "path": "/root/.bashrc",
+ "chattr": "i",
+ "s_mode": 420,
+ "d_mode": 420
+ },
+ {
+ "path": "/root/.bash_logout",
+ "chattr": "i",
+ "s_mode": 420,
+ "d_mode": 420
+ },
+ {
+ "path": "/root/.bash_profile",
+ "chattr": "i",
+ "s_mode": 420,
+ "d_mode": 420
+ },
+ {
+ "path": "/root/.profile",
+ "chattr": "i",
+ "s_mode": 420,
+ "d_mode": 420
+ },
+ {
+ "path": "/etc/profile",
+ "chattr": "i",
+ "s_mode": 420,
+ "d_mode": 420
+ },
+ {
+ "path": "/etc/bash.bashrc",
+ "chattr": "i",
+ "s_mode": 420,
+ "d_mode": 420
+ },
+ {
+ "path": "/etc/sysctl.conf",
+ "chattr": "i",
+ "s_mode": 420,
+ "d_mode": 420
+ },
+ {
+ "path": "/etc/sysctl.d",
+ "chattr": "i",
+ "s_mode": 420,
+ "d_mode": 420
+ }
+ ]
+ },
+ "user": {
+ "open": true,
+ "name": "用户加固",
+ "ps": "保护用户,开启后将无法添加删除用户和修改用户密码!",
+ "paths": [
+ {
+ "path": "/etc/passwd",
+ "chattr": "i",
+ "s_mode": 420,
+ "d_mode": 420
+ },
+ {
+ "path": "/etc/group",
+ "chattr": "i",
+ "s_mode": 420,
+ "d_mode": 420
+ },
+ {
+ "path": "/usr/sbin/groupadd",
+ "chattr": "i",
+ "s_mode": 488,
+ "d_mode": 420
+ },
+ {
+ "path": "/usr/sbin/useradd",
+ "chattr": "i",
+ "s_mode": 488,
+ "d_mode": 420
+ },
+ {
+ "path": "/usr/bin/passwd",
+ "chattr": "i",
+ "s_mode": 509,
+ "d_mode": 420
+ }
+ ]
+ },
+ "bin": {
+ "open": true,
+ "name": "关键目录加固",
+ "ps": "保护系统关键文件不被修改替换!",
+ "paths": [
+ {
+ "path": "/usr/bin",
+ "chattr": "i",
+ "s_mode": 365,
+ "d_mode": 365
+ },
+ {
+ "path": "/usr/sbin",
+ "chattr": "i",
+ "s_mode": 365,
+ "d_mode": 365
+ },
+ {
+ "path": "/etc/bashrc",
+ "chattr": "i",
+ "s_mode": 420,
+ "d_mode": 420
+ },
+ {
+ "path": "/usr/local/bin",
+ "chattr": "i",
+ "s_mode": 365,
+ "d_mode": 365
+ },
+ {
+ "path": "/usr/local/sbin",
+ "chattr": "i",
+ "s_mode": 365,
+ "d_mode": 365
+ },
+ {
+ "path": "/sbin",
+ "chattr": "i",
+ "s_mode": 365,
+ "d_mode": 365
+ },
+ {
+ "path": "/bin",
+ "chattr": "i",
+ "s_mode": 365,
+ "d_mode": 365
+ }
+ ]
+ },
+ "cron": {
+ "open": true,
+ "name": "计划任务加固",
+ "ps": "保护计划任务不被篡改,开启后无法添加删除计划任务!",
+ "paths": [
+ {
+ "path": "/usr/bin/crontab",
+ "chattr": "i",
+ "s_mode": 509,
+ "d_mode": 420
+ },
+ {
+ "path": "/etc/crontab",
+ "chattr": "i",
+ "s_mode": 420,
+ "d_mode": 420
+ },
+ {
+ "path": "/etc/cron.d",
+ "chattr": "i",
+ "s_mode": 509,
+ "d_mode": 509
+ },
+ {
+ "path": "/etc/cron.daily",
+ "chattr": "i",
+ "s_mode": 509,
+ "d_mode": 420
+ },
+ {
+ "path": "/etc/cron.hourly",
+ "chattr": "i",
+ "s_mode": 509,
+ "d_mode": 420
+ },
+ {
+ "path": "/etc/cron.monthly",
+ "chattr": "i",
+ "s_mode": 509,
+ "d_mode": 420
+ },
+ {
+ "path": "/etc/cron.weekly",
+ "chattr": "i",
+ "s_mode": 509,
+ "d_mode": 420
+ },
+ {
+ "path": "/etc/anacrontab",
+ "chattr": "i",
+ "s_mode": 384,
+ "d_mode": 384
+ },
+ {
+ "path": "/var/spool/cron",
+ "chattr": "i",
+ "s_mode": 448,
+ "d_mode": 448
+ },
+ {
+ "path": "/var/spool/cron/root",
+ "chattr": "i",
+ "s_mode": 384,
+ "d_mode": 384
+ },
+ {
+ "path": "/var/spool/anacron",
+ "chattr": "i",
+ "s_mode": 509,
+ "d_mode": 509
+ }
+ ]
+ },
+ "ssh": {
+ "open": true,
+ "name": "SSH服务加固",
+ "ps": "保护SSH不被暴力破解,记录用户登录日志",
+ "cycle": 120,
+ "limit": 3600,
+ "limit_count": 3
+ },
+ "process": {
+ "open": true,
+ "name": "异常进程监控",
+ "ps": "监控服务器进程列表,发现异常的进程后立即结束",
+ "process_white": [
+ "pip",
+ "pip3",
+ "yum",
+ "apt-get",
+ "apt",
+ "redis-cli",
+ "memcached",
+ "sshd",
+ "vm",
+ "vim",
+ "htop",
+ "top",
+ "sh",
+ "bash",
+ "zip",
+ "gzip",
+ "rsync",
+ "tar",
+ "unzip",
+ "rar",
+ "unrar",
+ "php",
+ "composer",
+ "pkill",
+ "mongo",
+ "mongod",
+ "php-fpm",
+ "php-fpm5.6",
+ "php-fpm7.0",
+ "php-fpm7.1",
+ "php-fpm7.2",
+ "php-fpm7.3",
+ "php-fpm7.4",
+ "php-fpm8.1",
+ "php-fpm8.2",
+ "nginx",
+ "httpd",
+ "lsof",
+ "ps",
+ "vi",
+ "vim",
+ "redis-server",
+ "mysqld",
+ "mysqld_safe",
+ "mysql",
+ "pure-ftpd",
+ "sparse_dd",
+ "stunnel",
+ "squeezed",
+ "vncterm",
+ "awk",
+ "ruby",
+ "postgres",
+ "mpathalert",
+ "vncterm",
+ "multipathd",
+ "fe",
+ "elasticsyslog",
+ "syslogd",
+ "v6d",
+ "xapi",
+ "screen",
+ "runsvdir",
+ "svlogd",
+ "java",
+ "udevd",
+ "ntpd",
+ "irqbalance",
+ "qmgr",
+ "wpa_supplicant",
+ "mysqld_safe",
+ "sftp-server",
+ "lvmetad",
+ "gitlab-web",
+ "pure-ftpd",
+ "auditd",
+ "master",
+ "dbus-daemon",
+ "tapdisk",
+ "init",
+ "ksoftirqd",
+ "kworker",
+ "kmpathd",
+ "kmpath_handlerd",
+ "python",
+ "kdmflush",
+ "bioset",
+ "crond",
+ "kthreadd",
+ "migration",
+ "rcu_sched",
+ "kjournald",
+ "gcc",
+ "gcc++",
+ "nginx",
+ "mysqld",
+ "php-cgi",
+ "login",
+ "firewalld",
+ "iptables",
+ "systemd",
+ "network",
+ "dhclient",
+ "systemd-journald",
+ "chrony",
+ "chronyd",
+ "chronyc",
+ "NetworkManager",
+ "systemd-logind",
+ "systemd-udevd",
+ "polkitd",
+ "tuned",
+ "rsyslogd",
+ "AliYunDunUpdate",
+ "AliYunDun",
+ "du",
+ "sendmail",
+ "gunicorn",
+ "python2",
+ "python3",
+ "python34",
+ "python2.6",
+ "runsv",
+ "dd",
+ "mkfs",
+ "fdisk",
+ "systemd-resolve",
+ "rpm",
+ "cc1",
+ "cc1plus",
+ "as",
+ "acme.sh",
+ "cc",
+ "du",
+ "grep",
+ "awk",
+ "sed",
+ "cut",
+ "free",
+ "df",
+ "firewall-cmd",
+ "ufw",
+ "echo",
+ "ls",
+ "cp",
+ "mv",
+ "rm",
+ "diff",
+ "ln",
+ "cat",
+ "more",
+ "wtmp",
+ "date",
+ "e2fsck",
+ "gunzip",
+ "ifconfig",
+ "ip",
+ "route",
+ "ping",
+ "scp",
+ "telnet",
+ "cd",
+ "comm",
+ "touch",
+ "man",
+ "w",
+ "who",
+ "last",
+ "clock",
+ "uname",
+ "su",
+ "uptime",
+ "vmstat",
+ "mount",
+ "umount",
+ "mkdir",
+ "mkswap",
+ "swapon",
+ "e2fsck",
+ "tune2fs",
+ "groupadd",
+ "useradd",
+ "passwd",
+ "userdel",
+ "chown",
+ "chmod",
+ "chgrp",
+ "id",
+ "mail",
+ "gzip",
+ "bzip2",
+ "bunzip2",
+ "networking",
+ "ssh",
+ "find",
+ "locate",
+ "slocate",
+ "dir",
+ "vdir",
+ "pwd",
+ "rename",
+ "rmdir",
+ "updatedb",
+ "whereis",
+ "which",
+ "compress",
+ "ar",
+ "arj",
+ "gzexe",
+ "bzip2",
+ "cksum",
+ "cmp",
+ "col",
+ "colrm",
+ "csplit",
+ "diff3",
+ "emacs",
+ "join",
+ "head",
+ "sort",
+ "wc",
+ "uniq",
+ "tail",
+ "sum",
+ "ulimit",
+ "pkill",
+ "kill",
+ "killall",
+ "pidof",
+ "pstree",
+ "watch",
+ "pgrep",
+ "dumpe2fs",
+ "mke2fs",
+ "sync",
+ "lsyncd",
+ "tune2fs",
+ "basename",
+ "file",
+ "locate/slocate",
+ "ls/dir/vdir",
+ "bzcat",
+ "bzip2recover",
+ "bzless/bzmore",
+ "cpio",
+ "dump",
+ "lha",
+ "resotre",
+ "unarj",
+ "uncompress",
+ "zcat",
+ "zforce",
+ "zipinfo",
+ "znew",
+ "diffstat",
+ "ed",
+ "ex",
+ "expand",
+ "fmt",
+ "fold",
+ "grep/egrep/fgrep",
+ "ispell",
+ "jed",
+ "joe",
+ "less",
+ "look",
+ "od",
+ "paste",
+ "pico",
+ "spell",
+ "split",
+ "tac",
+ "tee",
+ "tr",
+ "unexpand",
+ "alias",
+ "bg",
+ "bind",
+ "declare",
+ "dirs",
+ "enable",
+ "eval",
+ "exec",
+ "exit",
+ "export",
+ "fc",
+ "fg",
+ "hash",
+ "history",
+ "jobs",
+ "logout",
+ "popd",
+ "pushd",
+ "set",
+ "shopt",
+ "umask",
+ "unalias",
+ "unset",
+ "accept",
+ "cancel",
+ "disable",
+ "lp",
+ "lpadmin",
+ "lpc",
+ "lpq",
+ "lpr",
+ "lprm",
+ "lpstat",
+ "pr",
+ "reject",
+ "bc",
+ "cal",
+ "clear",
+ "consoletype",
+ "ctrlaltdel",
+ "dircolors",
+ "eject",
+ "halt",
+ "hostid",
+ "hwclock",
+ "info",
+ "md5sum",
+ "letsencrypt",
+ "mandb",
+ "mesg",
+ "mtools",
+ "mtoolstest",
+ "poweroff",
+ "reboot",
+ "shutdown",
+ "sleep",
+ "stat",
+ "talk",
+ "wall",
+ "whatis",
+ "whoami",
+ "write",
+ "cmsgoagent.linu",
+ "yes",
+ "chfn",
+ "chsh",
+ "finger",
+ "gpasswd",
+ "groupdel",
+ "groupmod",
+ "groups",
+ "grpck",
+ "grpconv",
+ "grpunconv",
+ "logname",
+ "pwck",
+ "pwconv",
+ "dd",
+ "mariadbd",
+ "pwunconv",
+ "usermod",
+ "users",
+ "nice",
+ "nohup",
+ "renice",
+ "badblocks",
+ "blockdev",
+ "chattr",
+ "convertquota",
+ "e2image",
+ "e2label",
+ "edquota",
+ "fail2ban-server",
+ "findfs",
+ "fsck",
+ "grub",
+ "hdparm",
+ "lilo",
+ "lsattr",
+ "mkbootdisk",
+ "mkinitrd",
+ "mkisofs",
+ "mknod",
+ "mktemp",
+ "parted",
+ "quota",
+ "quotacheck",
+ "xargs",
+ "bcm-agent",
+ "bcm-si",
+ "quotaoff",
+ "quotaon",
+ "quotastat",
+ "repquota",
+ "swapoff",
+ "depmod",
+ "dmesg",
+ "insmod",
+ "iostat",
+ "ipcs",
+ "kernelversion",
+ "lsmod",
+ "modinfo",
+ "modprobe",
+ "mpstat",
+ "rmmod",
+ "sar",
+ "slabtop",
+ "sysctl",
+ "tload",
+ "startx",
+ "xauth",
+ "xhost",
+ "xinit",
+ "xlsatoms",
+ "xlsclients",
+ "xlsfonts",
+ "xset",
+ "chroot",
+ "nmap",
+ "ntfs-3g",
+ "sftp",
+ "slogin",
+ "sudo",
+ "awk/gawk",
+ "expr",
+ "gdb",
+ "ldd",
+ "make",
+ "nm",
+ "perl",
+ "test",
+ "arch",
+ "at",
+ "atq",
+ "atrm",
+ "batch",
+ "chkconfig",
+ "crontab",
+ "lastb",
+ "logrotate",
+ "logsave",
+ "logwatch",
+ "lsusb",
+ "patch",
+ "runlevel",
+ "service",
+ "telinit",
+ "dnsdomainname",
+ "domainname",
+ "hostname",
+ "ifcfg",
+ "ifdown",
+ "ifup",
+ "stmpd",
+ "nisdomainname",
+ "ypdomainname",
+ "arp",
+ "arping",
+ "arpwatch",
+ "dig",
+ "elinks",
+ "elm",
+ "ftp",
+ "host",
+ "ipcalc",
+ "lynx",
+ "ncftp",
+ "netstat",
+ "nslookup",
+ "pine",
+ "dhclient-script",
+ "rsh",
+ "tftp",
+ "tracepath",
+ "traceroute",
+ "wget",
+ "arptables",
+ "iptables-save",
+ "iptables-restore",
+ "tcpdump",
+ "ab",
+ "apachectl",
+ "exportfs",
+ "htdigest",
+ "htpasswd",
+ "mailq",
+ "mysqladmin",
+ "msqldump",
+ "mysqlimport",
+ "mysqlshow",
+ "nfsstat",
+ "showmount",
+ "smbclient",
+ "smbmount",
+ "smbpasswd",
+ "squid",
+ "pickup",
+ "local",
+ "localedef",
+ "bounce",
+ "smtp",
+ "smtpd",
+ "trivial-rewrite",
+ "xe-daemon",
+ "cleanup",
+ "nm-dispatcher",
+ "lsinitrd",
+ "barad_agent",
+ "stop.sh",
+ "YDService",
+ "agetty",
+ "systemctl",
+ "AliSecureCheckA",
+ "containerd",
+ "containerd-shim",
+ "certbot-auto",
+ "oneav",
+ "oneavd",
+ "oneav_service_m",
+ "s3fs",
+ "bosfs",
+ "cosfs",
+ "flock",
+ "raidcheck",
+ "run-parts",
+ "man-db.cron",
+ "YDLive",
+ "sgagent"
+ ],
+ "process_white_rule": [
+ "vif",
+ "node",
+ "pm2",
+ "npm",
+ "yarn",
+ "qemu",
+ "scsi_eh",
+ "xcp",
+ "xen",
+ "docker",
+ "yunsuo",
+ "scsi",
+ "jbd2",
+ "ext4",
+ "xfs",
+ "aliyun",
+ "kswapd",
+ "PM2",
+ "yunsuo",
+ "mkfs",
+ "Ali",
+ "watchdog",
+ "kworker",
+ "ksoftirqd",
+ "migration",
+ "mysql",
+ "/www/server/",
+ "cmsgoagent",
+ "python",
+ "python3",
+ "ifdown",
+ "node",
+ "sd-pam"
+ ],
+ "process_exclude": [
+ "php-fpm",
+ "git",
+ "mysqld",
+ "mongod",
+ "dockerd",
+ "docker-containerd",
+ "memcached",
+ "jsvc",
+ "jsvc.exec",
+ "nginx",
+ "node",
+ "pm2",
+ "npm",
+ "yarn",
+ "httpd",
+ "gunicorn",
+ "configure",
+ "make",
+ "curl",
+ "wget",
+ "anacron",
+ "mysqldump",
+ "node",
+ "php",
+ "mysql",
+ "netstat",
+ "redis",
+ "postfix"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/plugins/system_safe/ico.png b/plugins/system_safe/ico.png
new file mode 100644
index 000000000..e31c1b3bf
Binary files /dev/null and b/plugins/system_safe/ico.png differ
diff --git a/plugins/system_safe/index.html b/plugins/system_safe/index.html
new file mode 100755
index 000000000..16a31b904
--- /dev/null
+++ b/plugins/system_safe/index.html
@@ -0,0 +1,24 @@
+
+
+
+
\ No newline at end of file
diff --git a/plugins/system_safe/info.json b/plugins/system_safe/info.json
new file mode 100755
index 000000000..52fe791ea
--- /dev/null
+++ b/plugins/system_safe/info.json
@@ -0,0 +1,20 @@
+{
+ "sort": 8,
+ "ps": "提供灵活的系统加固功能,防止系统被植入木马,支持服务器日志审计功能",
+ "name": "system_safe",
+ "title": "系统加固",
+ "shell": "install.sh",
+ "versions":["1.0"],
+ "updates":["1.0"],
+ "tip": "soft",
+ "icon":"ico.png",
+ "checks": "server/system_safe",
+ "path": "server/system_safe",
+ "display": 1,
+ "author": "midoks",
+ "date": "2022-01-12",
+ "home": "",
+ "type": 0,
+ "pid": "4"
+
+}
\ No newline at end of file
diff --git a/plugins/system_safe/init.d/system_safe.service.tpl b/plugins/system_safe/init.d/system_safe.service.tpl
new file mode 100644
index 000000000..707d60eb6
--- /dev/null
+++ b/plugins/system_safe/init.d/system_safe.service.tpl
@@ -0,0 +1,15 @@
+[Unit]
+Description=system_safe server daemon
+After=network.target
+
+[Service]
+Type=forking
+ExecStart={$SERVER_PATH}/init.d/system_safe start
+ExecStop={$SERVER_PATH}/init.d/system_safe stop
+ExecReload={$SERVER_PATH}/init.d/system_safe reload
+ExecRestart={$SERVER_PATH}/init.d/system_safe restart
+KillMode=process
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/plugins/system_safe/init.d/system_safe.tpl b/plugins/system_safe/init.d/system_safe.tpl
new file mode 100644
index 000000000..ae460a4b7
--- /dev/null
+++ b/plugins/system_safe/init.d/system_safe.tpl
@@ -0,0 +1,94 @@
+#!/bin/bash
+# chkconfig: 2345 55 25
+# description:system_safe
+
+### BEGIN INIT INFO
+# Provides: system_safe
+# Required-Start: $all
+# Required-Stop: $all
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts system_safe
+# Description: starts the system_safe
+### END INIT INFO
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+mw_path={$SERVER_PATH}
+rootPath=$(dirname "$mw_path")
+PATH=$PATH:$mw_path/bin
+
+if [ -f $rootPath/mdserver-web/bin/activate ];then
+ source $rootPath/mdserver-web/bin/activate
+fi
+
+sys_start()
+{
+ isStart=$(ps aux |grep -E "(system_safe)"|grep -v grep|grep -v '/bin/bash'|grep -v 'system_safe/system_safe.py start' | grep -v 'system_safe/system_safe.py reload' | grep -v 'system_safe/system_safe.py restart' | grep -v systemctl | grep -v '/bin/sh' | awk '{print $2}'|xargs)
+ if [ "$isStart" == '' ];then
+ echo -e "Starting system_safe service... \c"
+ cd $rootPath/mdserver-web
+ nohup python3 plugins/system_safe/system_safe.py bg_start &> $mw_path/service.log &
+ sleep 0.5
+ isStart=$(ps aux |grep -E "(system_safe)"|grep -v grep|awk '{print $2}'|xargs)
+ if [ "$isStart" == '' ];then
+ echo -e "\033[31mfailed\033[0m"
+ echo '------------------------------------------------------'
+ cat $mw_path/service.log
+ echo '------------------------------------------------------'
+ echo -e "\033[31mError: system_safe service startup failed.\033[0m"
+ return;
+ fi
+ echo -e "\033[32mdone\033[0m"
+ else
+ echo "Starting system_safe service (pid $isStart) already running"
+ fi
+}
+
+sys_stop()
+{
+ echo -e "Stopping system_safe service... \c";
+ pids=$(ps aux |grep -E "(system_safe)"|grep -v grep|grep -v '/bin/bash'|grep -v systemctl | grep -v 'system_safe/system_safe.py bg_stop'|grep -v 'system_safe/system_safe.py stop' | grep -v 'system_safe/system_safe.py reload' | grep -v 'system_safe/system_safe.py restart' |awk '{print $2}'|xargs)
+ arr=($pids)
+ for p in ${arr[@]}
+ do
+ kill -9 $p
+ done
+ cd $rootPath/mdserver-web
+ python3 plugins/system_safe/system_safe.py bg_stop
+ echo -e "\033[32mdone\033[0m"
+}
+
+sys_status()
+{
+ isStart=$(ps aux |grep -E "(system_safe)"|grep -v grep|grep -v systemctl|awk '{print $2}'|xargs)
+ if [ "$isStart" != '' ];then
+ echo -e "\033[32msystem_safe service (pid $isStart) already running\033[0m"
+ else
+ echo -e "\033[31msystem_safe service not running\033[0m"
+ fi
+}
+
+case "$1" in
+ 'start')
+ sys_start
+ ;;
+ 'stop')
+ sys_stop
+ ;;
+ 'restart')
+ sys_stop
+ sleep 0.2
+ sys_start
+ ;;
+ 'reload')
+ sys_stop
+ sleep 0.2
+ sys_start
+ ;;
+ 'status')
+ sys_status
+ ;;
+ *)
+ echo "Usage: systemctl {start|stop|restart|reload} system_safe"
+ ;;
+esac
\ No newline at end of file
diff --git a/plugins/system_safe/install.sh b/plugins/system_safe/install.sh
new file mode 100755
index 000000000..8140c6f18
--- /dev/null
+++ b/plugins/system_safe/install.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+SYSOS=`uname`
+VERSION=$2
+APP_NAME=system_safe
+
+# cd /www/server/mdserver-web && python3 plugins/system_safe/system_safe.py stop 1.0
+
+Install_App()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/system_safe
+ echo "$VERSION" > $serverPath/system_safe/version.pl
+ echo 'install complete'
+
+ #初始化
+ cd ${serverPath}/mdserver-web && python3 plugins/system_safe/system_safe.py start $VERSION
+ cd ${serverPath}/mdserver-web && python3 plugins/system_safe/system_safe.py initd_install $VERSION
+}
+
+Uninstall_App()
+{
+
+ if [ -f /usr/lib/systemd/system/${APP_NAME}.service ] || [ -f /lib/systemd/system/${APP_NAME}.service ] ;then
+ systemctl stop ${APP_NAME}
+ systemctl disable ${APP_NAME}
+ rm -rf /usr/lib/systemd/system/${APP_NAME}.service
+ rm -rf /lib/systemd/system/${APP_NAME}.service
+ systemctl daemon-reload
+ fi
+
+ rm -rf $serverPath/system_safe
+ echo "uninstall completed"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/system_safe/js/app.js b/plugins/system_safe/js/app.js
new file mode 100644
index 000000000..04aa5e5a9
--- /dev/null
+++ b/plugins/system_safe/js/app.js
@@ -0,0 +1,606 @@
+function ssPost(method,args,callback){
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(toArrayObject(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+
+
+ var req_data = {};
+ req_data['name'] = 'system_safe';
+ req_data['script'] = 'system_safe';
+ req_data['func'] = method;
+ req_data['args'] = _args;
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function ssAsyncPost(method,args){
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(toArrayObject(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+ return syncPost('/plugins/run', {name:'system_safe', func:method, args:_args});
+}
+
+function ssPostCallbak(method, args, callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'system_safe';
+ req_data['func'] = method;
+ req_data['script'] = 'system_safe';
+ args['version'] = '1.0';
+
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function getSafeConfigPathList(tag){
+ ssPost('get_safe_data', {tag:tag}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var slist = rdata.data.paths;
+
+ var body = '';
+ for (var i = 0; i < slist.length; i++) {
+ body += "";
+ body += ""+slist[i]['path']+" ";
+
+ if (slist[i]['chattr'] == 'i'){
+ body += "只读 ";
+ } else if (slist[i]['chattr'] == 'a'){
+ body += "追加 ";
+ } else{
+ body += "只读 ";
+ }
+
+ if (rdata.msg['open']){
+ body += ""+slist[i]['d_mode']+" ";
+ body += "已保护 ";
+ } else {
+ body += ""+slist[i]['s_mode']+" ";
+ body += "未保护 ";
+ }
+ body += "删除 ";
+ body += " ";
+ }
+
+ $('#safe-file-table tbody').html(body);
+ $('.safe_path_delete').click(function(){
+ var id = $(this).attr('row');
+ ssPost('del_safe_path',{tag:tag,index:id}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ showMsg(rdata.msg, function(){
+ getSafeConfigPathList(tag);
+ },{icon:rdata.status?1:2,shade: [0.3, '#000']},2000);
+ });
+ });
+ });
+}
+
+function setSafeConfigPath(tag, alist){
+
+ layer.open({
+ type: 1,
+ area: ['700px','535px'],
+ title: '配置【'+alist['name']+'】',
+ content: "\
+ \
+
\
+
\
+
\
+ 只读 \
+ 追加 \
+ \
+
\
+
添加 \
+
\
+ \
+
\
+
\
+ \
+ \
+ 路径 \
+ 模式 \
+ 权限 \
+ 状态 \
+ 操作 \
+ \
+ \
+ \
+
\
+
\
+
\
+ \
+ 【只读】无法修改、创建、删除文件和目录 \
+ 【追加】只能追加内容,不能删除或修改原有内容 \
+ 【权限】设置文件或目录在受保护状态下的权限(非继承),关闭保护后权限自动还原 \
+ 【如何填写权限】请填写Linux权限代号,如:644、755、600、555等,如果不填写,则使用文件原来的权限 \
+ \
+ ",
+ success:function(){
+
+ $('.add_safe_config').click(function(){
+ var path = $('input[name="s_path"]').val();
+ var chattr = $('select[name="chattr"]').val();
+ var d_mode = $('input[name="d_mode"]').val();
+ ssPost('add_safe_path',{tag:tag,path:path,chattr:chattr,d_mode:d_mode}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ showMsg(rdata.msg, function(){
+ getSafeConfigPathList(tag);
+ },{icon:rdata.status?1:2,shade: [0.3, '#000']},2000);
+ });
+ });
+ }
+ });
+ getSafeConfigPathList(tag);
+
+
+}
+
+function setSafeConfigSsh(tag, alist){
+
+
+ function setSafeConfigSshData(){
+ ssPost('get_ssh_data', {}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var info = rdata.data;
+
+ $('input[name="s_cycle"]').val(info['cycle']);
+ $('input[name="s_limit_count"]').val(info['limit_count']);
+ $('input[name="s_limit"]').val(info['limit']);
+ });
+ }
+ layer.open({
+ type: 1,
+ area: ['700px','210px'],
+ title: '配置【'+alist['name']+'】',
+ content: "\
+ \
+ 在(检测周期)\
+ \
+ 秒内,登录错误(检测阈值)\
+ \
+ 次,封锁(封锁时间)\
+ \
+ 保存 \
+
\
+ \
+ 触发以上策略后,客户端IP将被封锁一段时间 \
+ 请在面板日志或操作日志中查看封锁记录 \
+ 请在面板日志或操作日志中查看SSH成功登录的记录 \
+ \
+ ",
+ success:function(){
+ setSafeConfigSshData();
+
+ $('.save_safe_ssh').click(function(){
+ var cycle = $('input[name="s_cycle"]').val();
+ var limit_count = $('input[name="s_limit_count"]').val();
+ var limit = $('input[name="s_limit"]').val();
+ ssPost('save_safe_ssh',{cycle:cycle,limit_count:limit_count,limit:limit}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ showMsg(rdata.msg, function(){
+ setSafeConfigSshData();
+ },{icon:rdata.status?1:2,shade: [0.3, '#000']},2000);
+ });
+ });
+ }
+ });
+
+}
+
+function setSafeConfigProcessList(tag){
+ ssPost('get_process_data', {}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var slist = rdata.data.process_white;
+
+ var body = '';
+ for (var i = 0; i < slist.length; i++) {
+ body += "\
+ "+slist[i]+" \
+ 删除 \
+ ";
+ }
+
+ $('#safe-file-table tbody').html(body);
+ $('.safe_path_delete').click(function(){
+ var id = $(this).attr('row');
+ ssPost('del_safe_proccess_name',{tag:tag,index:id}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ showMsg(rdata.msg, function(){
+ setSafeConfigProcessList(tag);
+ },{icon:rdata.status?1:2,shade: [0.3, '#000']},2000);
+ });
+ });
+ });
+}
+
+function setSafeConfigProcess(tag, alist){
+ layer.open({
+ type: 1,
+ area: ['700px','535px'],
+ title: '配置【进程白名单】',
+ content: "\
+ \
+ \
+ 添加 \
+
\
+ \
+
\
+
\
+ \
+ \
+ 进程名称 \
+ 操作 \
+ \
+ \
+ \
+
\
+
\
+
\
+ \
+ 【进程名称】请填写完整的进程名称,如: mysqld \
+ 【说明】在白名单列表中的进程将不再检测范围,建议将常用软件的进程添加进白名单 \
+ \
+ ",
+ success:function(){
+ $('.add_process_white').click(function(){
+ var process_name = $('input[name="s_name"]').val();
+ ssPost('add_process_white',{process_name:process_name}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ showMsg(rdata.msg, function(){
+ setSafeConfigProcessList(tag);
+ },{icon:rdata.status?1:2,shade: [0.3, '#000']},2000);
+ });
+ });
+ }
+ });
+
+ setSafeConfigProcessList(tag);
+
+
+}
+
+function setSafeConfig(tag, alist){
+
+ if ($.inArray(tag, ['bin', 'service', 'home', 'user', 'bin', 'cron'])>-1){
+ setSafeConfigPath(tag,alist);
+ }
+
+ if ($.inArray(tag, ['ssh'])>-1){
+ setSafeConfigSsh(tag,alist);
+ }
+
+ if ($.inArray(tag, ['process'])>-1){
+ setSafeConfigProcess(tag,alist);
+ }
+}
+
+//设置安全状态
+function setSafeStatus(obj,tag){
+ var o = $(obj).prev();
+ var status = $(o).prop('checked');
+ ssPost('set_safe_status', {tag:tag,status:!status}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ $(o).prop('checked',status);
+ return;
+ }
+ layer.msg("配置成功!",{icon:1,time:2000,shade: [0.3, '#000']});
+ });
+}
+
+function ssConfigList(){
+
+ ssPost('conf',{},function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var libs = rdata.data;
+ // console.log(libs);
+ var body = '';
+ for (var i in libs) {
+
+ if (i == 'open'){
+ continue;
+ }
+
+ var checked = '';
+ if (libs[i].open){
+ checked = 'checked';
+ }
+
+ body += '' +
+ '' + libs[i].name + ' ' +
+ '' + libs[i].ps + ' ' +
+ '\
+ \
+ \
+ \
+
\
+ '+
+ '配置 ' +
+ ' ';
+ }
+
+ var con = '' +
+ '
' +
+ '' +
+ '' +
+ '名称 ' +
+ '描述 ' +
+ '状态 ' +
+ '操作 ' +
+ ' ' +
+ ' ' +
+ '' + body + ' ' +
+ '
' +
+ '
' +
+ '\
+ 开启系统加固功能后,一些如软件安装等敏感操作将被禁止 \
+ 开启【SSH服务加固】之后,用户登录SSH的行为将受到监控,若连续多次登录失败,将采取封IP措施 \
+ 【异常进程监控】与【SSH服务加固】会占用一定服务器开销 \
+ 【注意】如果您需要安装软件或插件,请先将系统加固关闭! \
+ ';
+
+ $('.soft-man-con').html(con);
+
+ $('#system_safe_list .service_switch').click(function(){
+ var val = $(this).prev().attr('id');
+ val = val.replace('sys_service_','');
+ setSafeStatus(this,val);
+ });
+
+ $('.set_safe_config').click(function(){
+ var name = $(this).attr('id');
+ // console.log(name);
+ name = name.replace('conf_sys_service_','');
+ setSafeConfig(name, libs[name]);
+ });
+
+ });
+
+}
+
+function ssOpLogList(p){
+ ssPost('op_log',{p:p},function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var plist = rdata.data.data;
+
+ var body = '';
+ for (var i = 0; i < plist.length; i++) {
+ body += "\
+ " + plist[i].addtime + " \
+ " + plist[i].log + " \
+ ";
+ }
+
+ $("#system_safe_log_list tbody").html(body);
+ $("#system_safe_log_page").html(rdata.data.page);
+ });
+}
+
+function ssOpLog(){
+ var con = '\
+
';
+ $('.soft-man-con').html(con);
+ ssOpLogList(1);
+}
+
+
+function ssLogAuditList(){
+ ssPost('get_sys_logfiles',{},function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var plist = rdata.data;
+ var option = '';
+ // console.log(plist);
+ for (var i = 0; i < plist.length; i++) {
+ option += ''+plist[i]['name'] +' - '+plist[i]['title']+' ';
+ }
+
+ $("#system_safe_log_audit").html(option);
+ var log_name = $('#system_safe_log_audit option:first').val();
+ ssLogAuditFile(log_name);
+
+ $('#system_safe_log_audit').change(function(){
+ var log_name = $('#system_safe_log_audit option:selected').val();
+ ssLogAuditFile(log_name);
+ });
+ });
+}
+
+function ssLogAuditFileRenderString(data){
+
+ var tbody = ' ';
+ $("#system_safe_log_audit_list").html(tbody);
+ var ob = document.getElementById('system_safe_log_audit_str');
+ ob.scrollTop = ob.scrollHeight;
+
+ $("#system_safe_log_audit_str").html(data);
+}
+
+function ssLogAuditFileRenderObject(plist){
+
+ var pre_html = '';
+ $("#system_safe_log_audit_list").html(pre_html);
+
+ if (plist.length>0){
+ var tmp = plist[0];
+ // console.log(tmp);
+ var thead = '';
+ tbody += ''
+ for (var i in tmp) {
+ tbody+=''+ i + ' ';
+ }
+ tbody += ' ';
+ $("#system_safe_log_audit_list thead").html(tbody);
+ }
+
+
+ var tbody = '';
+ for (var i = 0; i < plist.length; i++) {
+ tbody += '';
+ for (var vv in plist[i]) {
+ tbody+= ''+ plist[i][vv] + ' '
+ }
+ tbody += ' ';
+ }
+
+ $("#system_safe_log_audit_list tbody").html(tbody);
+}
+
+function ssLogAuditFile(log_name){
+ // ssPost('get_sys_log',{log_name:log_name},function(rdata){
+ // try{
+ // var rdata = $.parseJSON(rdata.data);
+ // if (typeof(rdata.data) == 'object'){
+
+ // if (!rdata.status){
+ // layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ // return;
+ // }
+ // var plist = rdata.data;
+ // ssLogAuditFileRenderObject(plist);
+ // }
+ // }catch(e){
+ // if (typeof(rdata.data) == 'string'){
+ // ssLogAuditFileRenderString(rdata.data);
+ // }
+ // }
+ // });
+
+ ssPostCallbak('get_sys_log',{log_name:log_name},function(rdata){
+ try{
+ var rdata = $.parseJSON(rdata.data);
+ if (typeof(rdata.data) == 'object'){
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ var plist = rdata.data;
+ ssLogAuditFileRenderObject(plist);
+ }
+ }catch(e){
+ if (typeof(rdata.data) == 'string'){
+ ssLogAuditFileRenderString(rdata.data);
+ }
+ }
+ });
+}
+
+function ssLogAudit(){
+ var con = '\
+ 请选择 \
+ \
+
';
+ $('.soft-man-con').html(con);
+ ssLogAuditList();
+}
+
+function ssLockAddressList(){
+ ssPost('get_ssh_limit_info',{},function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var libs = rdata.data;
+ var tbody = '';
+ for (var i in libs) {
+ var end_time = libs[i].end;
+ var time_title = '手动解封';
+ if (end_time != '0'){
+ time_title = '自动解封时间:'+end_time;
+ }
+ tbody += '' +
+ '' + libs[i].address + ' ' +
+ ''+time_title+' ' +
+ '立即解封 ' +
+ ' ';
+ }
+
+ $('#system_lock_address tbody').html(tbody);
+ $('#system_lock_address .remove_ssh_limit').click(function(){
+ var address = $(this).attr('ip');
+ ssPost('remove_ssh_limit', {ip:address}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ showMsg(rdata.msg, function(){
+ ssLockAddressList();
+ },{icon:rdata.status?1:2,shade: [0.3, '#000']},2000);
+ });
+ });
+ });
+}
+
+function ssLockAddress(){
+
+ var con = '\
+ \
+ 添加 \
+
\
+ ' +
+ '
' +
+ '' +
+ '' +
+ 'IP ' +
+ '解封时间 ' +
+ '操作 ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '
' +
+ '
' +
+ '\
+ 【封锁IP】此处封锁的IP仅针对SSH服务,即被封锁的IP将无法连接SSH \
+ 【添加】手动添加的IP封锁只能手动解封! \
+ ';
+
+ $('.soft-man-con').html(con);
+
+ $('.add_lock_address').click(function(){
+ var address = $('input[name="s_address"]').val();
+ ssPost('add_ssh_limit', {ip:address}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ showMsg(rdata.msg, function(){
+ ssLockAddressList();
+ },{icon:rdata.status?1:2,shade: [0.3, '#000']},2000);
+ });
+ });
+ ssLockAddressList();
+}
\ No newline at end of file
diff --git a/plugins/system_safe/system_safe.py b/plugins/system_safe/system_safe.py
new file mode 100755
index 000000000..62efb4e06
--- /dev/null
+++ b/plugins/system_safe/system_safe.py
@@ -0,0 +1,1145 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import json
+import re
+import psutil
+from datetime import datetime
+
+sys.dont_write_bytecode = True
+
+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
+
+
+class App:
+
+ __deny = '/etc/hosts.deny'
+ __allow = '/etc/hosts.allow'
+ __state = {True: '开启', False: '关闭'}
+ __months = {'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06',
+ 'Jul': '07', 'Aug': '08', 'Sep': '09', 'Sept': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'}
+ __name = '系统加固'
+ __deny_list = None
+
+ __config = None
+ __log_file = None
+ __last_ssh_time = 0
+ __last_ssh_size = 0
+
+ def __init__(self):
+ if mw.isAppleSystem():
+ self.__deny = self.getServerDir() + '/hosts.deny'
+ self.__allow = self.getServerDir() + '/hosts.allow'
+
+ def getPluginName(self):
+ return 'system_safe'
+
+ def getPluginDir(self):
+ return mw.getPluginDir() + '/' + self.getPluginName()
+
+ def getServerDir(self):
+ return mw.getServerDir() + '/' + self.getPluginName()
+
+ def getInitDFile(self):
+ if app_debug:
+ return '/tmp/' + self.getPluginName()
+ return '/etc/init.d/' + self.getPluginName()
+
+ def getInitDTpl(self):
+ path = self.getPluginDir() + "/init.d/" + self.getPluginName() + ".tpl"
+ return path
+
+ def getArgs(self):
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+
+ return tmp
+
+ def checkArgs(self, 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 getServerConfPath(self):
+ return self.getServerDir() + "/config.json"
+
+ def getConf(self):
+ cpath = self.getServerConfPath()
+ cpath_tpl = self.getPluginDir() + "/conf/config.json"
+
+ if not os.path.exists(cpath):
+ t_data = mw.readFile(cpath_tpl)
+ t_data = json.loads(t_data)
+ t = json.dumps(t_data)
+ mw.writeFile(cpath, t)
+ return t_data
+
+ t_data = mw.readFile(cpath)
+ t_data = json.loads(t_data)
+ return t_data
+
+ def writeConf(self, data):
+ cpath = self.getServerConfPath()
+ tmp_conf = json.dumps(data)
+ mw.writeFile(cpath, tmp_conf)
+
+ def initDreplace(self):
+
+ file_tpl = self.getInitDTpl()
+ service_path = self.getServerDir()
+
+ initD_path = service_path + '/init.d'
+ if not os.path.exists(initD_path):
+ os.mkdir(initD_path)
+
+ # init.d
+ file_bin = initD_path + '/' + self.getPluginName()
+ if not os.path.exists(file_bin):
+ # 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)
+
+ # systemd
+ # /usr/lib/systemd/system
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/system_safe.service'
+ systemServiceTpl = self.getPluginDir() + '/init.d/system_safe.service.tpl'
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ 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 ssOp(self, method):
+ file = self.initDreplace()
+
+ if not mw.isAppleSystem():
+ data = mw.execShell('systemctl ' + method +
+ ' ' + self.getPluginName())
+ if data[1] == '':
+ return 'ok'
+ return 'fail'
+
+ data = mw.execShell(file + ' ' + method)
+ if data[1] == '':
+ return 'ok'
+ return 'fail'
+
+ # def status(self):
+ # data = self.getConf()
+ # if not data['open']:
+ # return 'stop'
+ # return 'start'
+
+ def status(self):
+ data = mw.execShell(
+ 'ps -ef|grep "system_safe/system_safe.py bg_start" | grep -v grep | awk \'{print $2}\'')
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+ def start(self):
+ return self.ssOp('start')
+
+ def writeLog(self, msg):
+ mw.writeDbLog(self.__name, msg)
+
+ def getDenyList(self):
+ deny_file = self.getServerDir() + '/deny.json'
+ if not os.path.exists(deny_file):
+ mw.writeFile(deny_file, '{}')
+ self.__deny_list = json.loads(mw.readFile(deny_file))
+
+ # 存deny_list
+ def saveDeayList(self):
+ deny_file = self.getServerDir() + '/deny.json'
+ mw.writeFile(deny_file, json.dumps(self.__deny_list))
+
+ def get_ssh_limit(self):
+ data = self.getSshLimit()
+ return mw.returnJson(True, 'ok!', data)
+
+ # 获取当前SSH禁止IP
+ def getSshLimit(self):
+ denyConf = mw.readFile(self.__deny)
+ if not denyConf:
+ mw.writeFile(self.__deny, '')
+ return []
+ data = re.findall(
+ r"sshd:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):deny", denyConf)
+ return data
+
+ def get_ssh_limit_info(self):
+ data = self.getSshLimitInfo()
+ return mw.returnJson(True, 'ok!', data)
+
+ # 获取deny信息
+ def getSshLimitInfo(self):
+ self.getDenyList()
+ conf_list = self.getSshLimit()
+ data = []
+ for c_ip in conf_list:
+ tmp = {}
+ tmp['address'] = c_ip
+ tmp['end'] = 0
+ if c_ip in self.__deny_list:
+ tmp['end'] = mw.getDataFromInt(self.__deny_list[c_ip])
+ data.append(tmp)
+ return data
+
+ def add_ssh_limit(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['ip'])
+ if not check[0]:
+ return check[1]
+ ip = args['ip']
+ return self.addSshLimit(ip)
+
+ # 添加SSH目标IP
+ def addSshLimit(self, ip=None):
+ if ip == None:
+ ip = self.ip
+
+ if ip in self.getSshLimit():
+ return mw.returnJson(True, '指定IP黑名单已存在!')
+ denyConf = mw.readFile(self.__deny).strip()
+ while denyConf[-1:] == "\n" or denyConf[-1:] == " ":
+ denyConf = denyConf[:-1]
+ denyConf += "\nsshd:" + ip + ":deny\n"
+ mw.writeFile(self.__deny, denyConf)
+ if ip in self.getSshLimit():
+ msg = u'添加IP[%s]到SSH-IP黑名单' % ip
+ self.writeLog(msg)
+ self.getDenyList()
+ if not ip in self.__deny_list:
+ self.__deny_list[ip] = 0
+ self.saveDeayList()
+ return mw.returnJson(True, '添加成功!')
+ return mw.returnJson(False, '添加失败!')
+
+ def remove_ssh_limit(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['ip'])
+ if not check[0]:
+ return check[1]
+ ip = args['ip']
+ return self.removeSshLimit(ip)
+
+ # 删除IP黑名单
+ def removeSshLimit(self, ip=None):
+ if ip == None:
+ ip = self.ip
+
+ if not self.__deny_list:
+ self.getDenyList()
+ if ip in self.__deny_list:
+ del(self.__deny_list[ip])
+ self.saveDeayList()
+ if not ip in self.getSshLimit():
+ return mw.returnJson(True, '指定IP黑名单不存在!')
+
+ denyConf = mw.readFile(self.__deny).strip()
+ while denyConf[-1:] == "\n" or denyConf[-1:] == " ":
+ denyConf = denyConf[:-1]
+ denyConf = re.sub("\n?sshd:" + ip + ":deny\n?", "\n", denyConf)
+ mw.writeFile(self.__deny, denyConf + "\n")
+
+ msg = '从SSH-IP黑名单中解封[%s]' % ip
+ self.writeLog(msg)
+ return mw.returnJson(True, '解封成功!')
+
+ def sshLoginTask(self):
+ if not self.__log_file:
+ log_file = '/var/log/secure'
+ if not os.path.exists(log_file):
+ log_file = '/var/log/auth.log'
+ if not os.path.exists(log_file):
+ return
+ self.__log_file = log_file
+
+ if not self.__log_file:
+ return
+
+ log_size = os.path.getsize(self.__log_file)
+ if self.__last_ssh_size == log_size:
+ return
+ self.__last_ssh_size = log_size
+ try:
+ config = self.__config
+ ssh_config = self.__config['ssh']
+ line_num = ssh_config['limit_count'] * 20
+ secure_logs = mw.getLastLine(
+ self.__log_file, line_num).split('\n')
+
+ total_time = '/dev/shm/ssh_total_time.pl'
+ if not os.path.exists(total_time):
+ mw.writeFile(total_time, str(int(time.time())))
+
+ last_total_time = int(mw.readFile(total_time))
+ now_total_time = int(time.time())
+
+ # print("last_total_time:", last_total_time)
+ # print("now_total_time:", now_total_time)
+
+ self.getDenyList()
+ deny_list = list(self.__deny_list.keys())
+ for i in range(len(deny_list)):
+ c_ip = deny_list[i]
+ if self.__deny_list[c_ip] > now_total_time or self.__deny_list[c_ip] == 0:
+ continue
+ self.ip = c_ip
+ self.removeSshLimit(None)
+ ip_total = {}
+ for log in secure_logs:
+ if log.find('Failed password for') != -1:
+ login_time = self.__to_date(
+ re.search(r'^\w+\s+\d+\s+\d+:\d+:\d+', log).group())
+ if now_total_time - login_time >= ssh_config['cycle']:
+ continue
+
+ client_ip = re.search(r'(\d+\.)+\d+', log).group()
+ if client_ip in self.__deny_list:
+ continue
+ if not client_ip in ip_total:
+ ip_total[client_ip] = 0
+ ip_total[client_ip] += 1
+ if ip_total[client_ip] <= ssh_config['limit_count']:
+ continue
+ self.__deny_list[
+ client_ip] = now_total_time + ssh_config['limit']
+
+ self.saveDeayList()
+ self.ip = client_ip
+
+ self.addSshLimit(None)
+ self.writeLog("[%s]在[%s]秒内连续[%s]次登录SSH失败,封锁[%s]秒" % (
+ client_ip, ssh_config['cycle'], ssh_config['limit_count'], ssh_config['limit']))
+ elif log.find('Accepted p') != -1:
+ login_time = self.__to_date(
+ re.search(r'^\w+\s+\d+\s+\d+:\d+:\d+', log).group())
+ if login_time < last_total_time:
+ continue
+ client_ip = re.search(r'(\d+\.)+\d+', log).group()
+ login_user = re.findall(r"(\w+)\s+from", log)[0]
+ self.writeLog("用户[%s]成功登录服务器,用户IP:[%s],登录时间:[%s]" % (
+ login_user, client_ip, time.strftime('%Y-%m-%d %X', time.localtime(login_time))))
+ mw.writeFile(total_time, str(int(time.time())))
+ except Exception as e:
+ print(mw.getTracebackInfo())
+ return 'success'
+
+ __limit = 30
+ __vmsize = 1048576 * 100
+ __wlist = None
+ __wslist = None
+ __elist = None
+ __last_pid_count = 0
+ __last_return_time = 0 # 上次返回结果的时间
+ __return_timeout = 360
+
+ def getSysProcess(self, get):
+ # 是否直接从属性中获取
+ stime = time.time()
+ if stime - self.__last_return_time < self.__return_timeout:
+ if self.__sys_process:
+ return True
+ self.__last_return_time = stime
+
+ # 本地是否存在系统进程白名单文件
+ config_file = self.getServerDir() + '/sys_process.json'
+ is_force = False
+ if not os.path.exists(config_file):
+ mw.writeFile(config_file, json.dumps([]))
+ is_force = True
+
+ data = json.loads(mw.readFile(config_file))
+ self.__sys_process = data
+ return True
+
+ # 取进程白名单列表
+ def getProcessWhite(self, get):
+ data = self.getConf()
+ return data['process']['process_white']
+
+ # 取进程关键词
+ def getProcessRule(self, get):
+ data = self.getConf()
+ return data['process']['process_white_rule']
+
+ # 取进程排除名单
+ def getProcessExclude(self, get):
+ data = self.getConf()
+ return data['process']['process_exclude']
+
+ # 检查白名单
+ def checkWhite(self, name):
+ if not self.__elist:
+ self.__elist = self.getProcessExclude(None)
+ if not self.__wlist:
+ self.__wlist = self.getProcessWhite(None)
+ if not self.__wslist:
+ self.__wslist = self.getProcessRule(None)
+ self.getSysProcess(None)
+ if name in ['mw', 'dnf', 'yum',
+ 'apt', 'apt-get', 'grep',
+ 'awk', 'python', 'node', 'php', 'mysqld',
+ 'httpd', 'openresty', 'wget', 'curl', 'openssl',
+ 'rspamd', 'supervisord', 'tlsmgr']:
+ return True
+ if name in self.__elist:
+ return True
+ if name in self.__sys_process:
+ return True
+ if name in self.__wlist:
+ return True
+ for key in self.__wslist:
+ if name.find(key) != -1:
+ return True
+ return False
+
+ def checkMainProccess(self, pid):
+ if pid < 1100:
+ return
+ fname = '/proc/' + str(pid) + '/comm'
+ if not os.path.exists(fname):
+ return
+ name = mw.readFile(fname).strip()
+ is_num_name = re.match(r"^\d+$", name)
+ if not is_num_name:
+ if self.checkWhite(name):
+ return
+ try:
+ p = psutil.Process(pid)
+ percent = p.cpu_percent(interval=0.1)
+ vm = p.memory_info().vms
+ if percent > self.__limit or vm > self.__vmsize:
+ cmdline = ' '.join(p.cmdline())
+ if cmdline.find('/www/server/cron') != -1:
+ return
+ if cmdline.find('/www/server') != -1:
+ return
+ if name.find('kworker') != -1 or name.find('mw_') == 0:
+ return
+ p.kill()
+ self.writeLog("已强制结束异常进程:[%s],PID:[%s],CPU:[%s],CMD:[%s]" % (
+ name, pid, percent, cmdline))
+ except:
+ print(mw.getTracebackInfo())
+ return
+
+ def checkMain(self):
+ pids = psutil.pids()
+ pid_count = len(pids)
+ if self.__last_pid_count == pid_count:
+ return
+ self.__last_pid_count = pid_count
+
+ try:
+ for pid in pids:
+ self.checkMainProccess(pid)
+ except Exception as e:
+ print(mw.getTracebackInfo())
+
+ def processTask(self):
+
+ if not self.__config:
+ self.__config = self.getConf()
+ if not self.__config:
+ return
+
+ self.setOpen(1)
+ is_open = 0
+ while True:
+ if self.__config['ssh']['open']:
+ is_open += 1
+ self.sshLoginTask()
+ if self.__config['process']['open']:
+ is_open += 1
+ self.checkMain()
+
+ if is_open > 60:
+ self.__config = self.getConf()
+ is_open = 0
+ time.sleep(1)
+
+ def bg_start(self):
+ try:
+ self.processTask()
+ except Exception as e:
+ print(mw.getTracebackInfo())
+ self.bg_stop()
+ self.writeLog('【{}】系统加固监控进程启动异常关闭'.format(mw.getDate()))
+ return 'ok'
+
+ def bg_stop(self):
+ try:
+ self.setOpen(0)
+ except Exception as e:
+ print(mw.getTracebackInfo())
+ print('【{}】系统加固监控进程停止异常关闭'.format(mw.getDate()))
+ return ''
+
+ def stop(self):
+ return self.ssOp('stop')
+
+ def restart(self):
+ return self.ssOp('restart')
+
+ def reload(self):
+ return self.ssOp('reload')
+
+ def initd_status(self):
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+ shell_cmd = 'systemctl status %s | grep loaded | grep "enabled;"' % (
+ self.getPluginName())
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+ def initd_install(self):
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl enable ' + self.getPluginName())
+ return 'ok'
+
+ def initd_uninstall(self):
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl disable ' + self.getPluginName())
+ return 'ok'
+
+ def __lock_path(self, info):
+ try:
+ if not os.path.exists(info['path']):
+ return False
+ if info['d_mode']:
+ os.chmod(info['path'], info['d_mode'])
+ if info['chattr']:
+ cmd = "chattr -R +%s %s" % (info['chattr'], info['path'])
+ mw.execShell(cmd)
+ return True
+ except Exception as e:
+
+ return False
+
+ def __unlock_path(self, info):
+ try:
+ if not os.path.exists(info['path']):
+ return False
+ if info['chattr']:
+ cmd = "chattr -R -%s %s" % (info['chattr'], info['path'])
+ mw.execShell(cmd)
+
+ if info['s_mode']:
+ os.chmod(info['path'], info['s_mode'])
+ return True
+ except Exception as e:
+ mw.getTracebackInfo()
+ return False
+
+ def __set_safe_state(self, paths, lock=False):
+ for path_info in paths:
+ if lock:
+ self.__lock_path(path_info)
+ else:
+ self.__unlock_path(path_info)
+ return True
+
+ # 转换时间格式
+ def __to_date(self, date_str):
+ tmp = re.split(r'\s+', date_str)
+ s_date = str(datetime.now().year) + '-' + \
+ self.__months.get(tmp[0]) + '-' + tmp[1] + ' ' + tmp[2]
+ time_array = time.strptime(s_date, "%Y-%m-%d %H:%M:%S")
+ time_stamp = int(time.mktime(time_array))
+ return time_stamp
+
+ def conf(self):
+ data = self.getConf()
+ return mw.returnJson(True, 'ok', data)
+
+ def setOpen(self, is_open=-1):
+ cpath = self.getServerConfPath()
+ data = self.getConf()
+ if is_open != -1:
+ if is_open == 0:
+ data['open'] = False
+ else:
+ data['open'] = True
+
+ for s_name in data.keys():
+ if type(data[s_name]) == bool:
+ continue
+ if not 'name' in data[s_name]:
+ continue
+ if not 'paths' in data[s_name]:
+ continue
+ s_name_status = False
+ if data['open']:
+ s_name_status = True
+ # print(data[s_name]['paths'], data[s_name]['open'])
+ self.__set_safe_state(data[s_name]['paths'], s_name_status)
+ msg = '已[%s]系统加固功能' % self.__state[data['open']]
+ self.writeLog(msg)
+ self.writeConf(data)
+
+ def set_safe_status(self):
+ args = self.getArgs()
+ data = self.checkArgs(args, ['tag', 'status'])
+ if not data[0]:
+ return data[1]
+
+ tag = args['tag']
+ status = args['status']
+
+ # 转换格式
+ if status == 'false':
+ status = False
+ if status == 'true':
+ status = True
+
+ if not tag in ['bin', 'service', 'home', 'user', 'bin', 'cron', 'ssh', 'process']:
+ return mw.returnJson(False, '不存在此配置[{}]!'.format(tag))
+
+ data = self.getConf()
+ data[tag]['open'] = status
+ self.writeConf(data)
+ return mw.returnJson(True, '设置成功')
+
+ # 取文件或目录锁定状态
+ def __get_path_state(self, path):
+ if not os.path.exists(path):
+ return 'i'
+ if os.path.isfile(path):
+ shell_cmd = "lsattr %s|awk '{print $1}'" % path
+ else:
+ shell_cmd = "lsattr {}/ |grep '{}$'|awk '{{print $1}}'".format(
+ os.path.dirname(path), path)
+ result = mw.execShell(shell_cmd)[0]
+ if result.find('-i-') != -1:
+ return 'i'
+ if result.find('-a-') != -1:
+ return 'a'
+ return False
+
+ # 遍历当前防护状态
+ def __list_safe_state(self, paths):
+ result = []
+ for i in range(len(paths)):
+ if not os.path.exists(paths[i]['path']):
+ continue
+ if os.path.islink(paths[i]['path']):
+ continue
+ mstate = self.__get_path_state(paths[i]['path'])
+ paths[i]['state'] = mstate == paths[i]['chattr']
+ paths[i]['s_mode'] = oct(paths[i]['s_mode'])
+ paths[i]['d_mode'] = oct(paths[i]['d_mode'])
+ result.append(paths[i])
+ return result
+
+ def get_safe_data(self):
+
+ args = self.getArgs()
+ check = self.checkArgs(args, ['tag'])
+ if not check[0]:
+ return check[1]
+
+ tag = args['tag']
+ if not tag in ['bin', 'service', 'home', 'user', 'bin', 'cron']:
+ return mw.returnJson(False, '不存在此配置[{}]!'.format(tag))
+
+ cpath = self.getServerConfPath()
+ data = self.getConf()
+ tmp = data[tag]
+ tmp['paths'] = self.__list_safe_state(tmp['paths'])
+ return mw.returnJson(True, {'open': data['open']}, tmp)
+
+ def get_ssh_data(self):
+ data = self.getConf()
+ tmp = data['ssh']
+ return mw.returnJson(True, {'open': data['open']}, tmp)
+
+ def get_process_data(self):
+ data = self.getConf()
+ tmp = data['process']
+ return mw.returnJson(True, {'open': data['open']}, tmp)
+
+ # 添加防护对象
+ def add_safe_path(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['tag', 'path', 'chattr', 'd_mode'])
+ if not check[0]:
+ return check[1]
+
+ path = args['path']
+ tag = args['tag']
+ chattr = args['chattr']
+ d_mode = args['d_mode']
+ if path[-1] == '/':
+ path = path[:-1]
+ if not os.path.exists(path):
+ return mw.returnJson(False, '指定文件或目录不存在!')
+ data = self.getConf()
+
+ for m_path in data[tag]['paths']:
+ if path == m_path['path']:
+ return mw.returnJson(False, '指定文件或目录已经添加过了!')
+
+ path_info = {}
+ path_info['path'] = path
+ path_info['chattr'] = chattr
+ path_info['s_mode'] = int(oct(os.stat(path).st_mode)[-3:], 8)
+ if d_mode != '':
+ path_info['d_mode'] = int(d_mode, 8)
+ else:
+ path_info['d_mode'] = path_info['s_mode']
+
+ data[tag]['paths'].insert(0, path_info)
+ if 'paths' in data[tag]:
+ mw.execShell('chattr -R -%s %s' %
+ (path_info['chattr'], path_info['path']))
+ if data['open']:
+ self.__set_safe_state([path_info], data[tag]['open'])
+ msg = '添加防护对象[%s]到[%s]' % (path, data[tag]['name'])
+ self.writeLog(msg)
+ self.writeConf(data)
+ return mw.returnJson(True, msg)
+
+ def save_safe_ssh(self):
+
+ args = self.getArgs()
+ check = self.checkArgs(args, ['cycle', 'limit', 'limit_count'])
+ if not check[0]:
+ return check[1]
+
+ cycle = int(args['cycle'])
+ limit = int(args['limit'])
+ limit_count = int(args['limit_count'])
+
+ if cycle > limit:
+ return mw.returnJson(False, '封锁时间不能小于检测周期!')
+ if cycle < 30 or cycle > 1800:
+ return mw.returnJson(False, '检测周期的值必需在30 - 1800秒之间!')
+ if limit < 60:
+ return mw.returnJson(False, '封锁时间不能小于60秒')
+ if limit_count < 3 or limit_count > 100:
+ return mw.returnJson(False, '检测阈值必需在3 - 100秒之间!')
+ data = self.getConf()
+ data['ssh']['cycle'] = cycle
+ data['ssh']['limit'] = limit
+ data['ssh']['limit_count'] = limit_count
+ self.writeConf(data)
+ msg = '修改SSH策略: 在[%s]秒内,登录错误[%s]次,封锁[%s]秒' % (
+ data['ssh']['cycle'], data['ssh']['limit_count'], data['ssh']['limit'])
+ self.writeLog(msg)
+ self.restart()
+ return mw.returnJson(True, '配置已保存!')
+
+ # 添加进程白名单
+ def add_process_white(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['process_name'])
+ if not check[0]:
+ return check[1]
+
+ data = self.getConf()
+ process_name = args['process_name']
+ if process_name in data['process']['process_white']:
+ return mw.returnJson(False, '指定进程名已在白名单')
+ data['process']['process_white'].insert(0, process_name)
+ self.writeConf(data)
+ msg = '添加进程名[%s]到进程白名单' % process_name
+ self.writeLog(msg)
+ self.restart()
+ return mw.returnJson(True, msg)
+
+ def del_safe_proccess_name(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['tag', 'index'])
+ if not check[0]:
+ return check[1]
+
+ tag = args['tag']
+ index = int(args['index'])
+
+ cpath = self.getServerConfPath()
+ data = self.getConf()
+
+ del(data[tag]['process_white'][index])
+ t = json.dumps(data)
+ mw.writeFile(cpath, t)
+ return mw.returnJson(True, '删除成功')
+
+ def del_safe_path(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['tag', 'index'])
+ if not check[0]:
+ return check[1]
+
+ tag = args['tag']
+ index = int(args['index'])
+
+ cpath = self.getServerConfPath()
+ data = self.getConf()
+
+ del(data[tag]['paths'][index])
+ t = json.dumps(data)
+ mw.writeFile(cpath, t)
+ return mw.returnJson(True, '删除成功')
+
+ def get_log_title(self, log_name):
+ log_name = log_name.replace('.1', '')
+ if log_name in ['auth.log', 'secure'] or log_name.find('auth.') == 0:
+ return '授权日志'
+ if log_name in ['dmesg'] or log_name.find('dmesg') == 0:
+ return '内核缓冲区日志'
+ if log_name in ['syslog'] or log_name.find('syslog') == 0:
+ return '系统警告/错误日志'
+ if log_name in ['btmp']:
+ return '失败的登录记录'
+ if log_name in ['utmp', 'wtmp']:
+ return '登录和重启记录'
+ if log_name in ['lastlog']:
+ return '用户最后登录'
+ if log_name in ['yum.log']:
+ return 'yum包管理器日志'
+ if log_name in ['anaconda.log']:
+ return 'Anaconda日志'
+ if log_name in ['dpkg.log']:
+ return 'dpkg日志'
+ if log_name in ['daemon.log']:
+ return '系统后台守护进程日志'
+ if log_name in ['boot.log']:
+ return '启动日志'
+ if log_name in ['kern.log']:
+ return '内核日志'
+ if log_name in ['maillog', 'mail.log']:
+ return '邮件日志'
+ if log_name.find('Xorg') == 0:
+ return 'Xorg日志'
+ if log_name in ['cron.log']:
+ return '定时任务日志'
+ if log_name in ['alternatives.log']:
+ return '更新替代信息'
+ if log_name in ['debug']:
+ return '调试信息'
+ if log_name.find('apt') == 0:
+ return 'apt-get相关日志'
+ if log_name.find('installer') == 0:
+ return '系统安装相关日志'
+ if log_name in ['messages']:
+ return '综合日志'
+ return '{}日志'.format(log_name.split('.')[0])
+
+ def get_sys_logfiles(self):
+ log_dir = '/var/log'
+ log_files = []
+ for log_file in os.listdir(log_dir):
+
+ log_suffix = log_file.split('.')[-1:]
+ if log_suffix[0] in ['gz', 'xz', 'bz2', 'asl']:
+ continue
+
+ if log_file in ['.', '..', 'faillog', 'fontconfig.log', 'unattended-upgrades', 'tallylog']:
+ continue
+
+ # print(log_suffix)
+ filename = os.path.join(log_dir, log_file)
+ if os.path.isfile(filename):
+ file_size = os.path.getsize(filename)
+ if not file_size:
+ continue
+
+ tmp = {
+ 'name': log_file,
+ 'size': file_size,
+ 'log_file': filename,
+ 'title': self.get_log_title(log_file),
+ 'uptime': os.path.getmtime(filename)
+ }
+
+ log_files.append(tmp)
+ else:
+ for next_name in os.listdir(filename):
+ if next_name[-3:] in ['.gz', '.xz']:
+ continue
+ next_file = os.path.join(filename, next_name)
+ if not os.path.isfile(next_file):
+ continue
+ file_size = os.path.getsize(next_file)
+ if not file_size:
+ continue
+ log_name = '{}/{}'.format(log_file, next_name)
+ tmp = {
+ 'name': log_name,
+ 'size': file_size,
+ 'log_file': next_file,
+ 'title': self.get_log_title(log_name),
+ 'uptime': os.path.getmtime(next_file)
+ }
+ log_files.append(tmp)
+ log_files = sorted(log_files, key=lambda x: x['name'], reverse=True)
+ return mw.returnJson(True, 'ok', log_files)
+
+ def get_last(self, log_name):
+ # 获取日志
+ cmd = '''LANG=en_US.UTF-8 last -n 200 -x -f {} |grep -v 127.0.0.1|grep -v " begins"'''.format(
+ '/var/log/' + log_name)
+ result = mw.execShell(cmd)
+ lastlog_list = []
+ for _line in result[0].split("\n"):
+ if not _line:
+ continue
+ tmp = {}
+ sp_arr = _line.split()
+ tmp['用户'] = sp_arr[0]
+ if sp_arr[0] == 'runlevel':
+ tmp['来源'] = sp_arr[4]
+ tmp['端口'] = ' '.join(sp_arr[1:4])
+ tmp['时间'] = self.__to_date3(
+ ' '.join(sp_arr[5:])) + ' ' + ' '.join(sp_arr[-2:])
+ elif sp_arr[0] in ['reboot', 'shutdown']:
+ tmp['来源'] = sp_arr[3]
+ tmp['端口'] = ' '.join(sp_arr[1:3])
+ if sp_arr[-3] == '-':
+ tmp['时间'] = self.__to_date3(
+ ' '.join(sp_arr[4:])) + ' ' + ' '.join(sp_arr[-3:])
+ else:
+ tmp['时间'] = self.__to_date3(
+ ' '.join(sp_arr[4:])) + ' ' + ' '.join(sp_arr[-2:])
+ elif sp_arr[1] in ['tty1', 'tty', 'tty2', 'tty3', 'hvc0', 'hvc1', 'hvc2'] or len(sp_arr) == 9:
+ tmp['来源'] = ''
+ tmp['端口'] = sp_arr[1]
+ tmp['时间'] = self.__to_date3(
+ ' '.join(sp_arr[2:])) + ' ' + ' '.join(sp_arr[-3:])
+ else:
+ tmp['来源'] = sp_arr[2]
+ tmp['端口'] = sp_arr[1]
+ tmp['时间'] = self.__to_date3(
+ ' '.join(sp_arr[3:])) + ' ' + ' '.join(sp_arr[-3:])
+
+ # tmp['_line'] = _line
+ lastlog_list.append(tmp)
+ # lastlog_list = sorted(lastlog_list,key=lambda x:x['时间'],reverse=True)
+ return mw.returnJson(True, 'ok!', lastlog_list)
+
+ def get_lastlog(self):
+ cmd = '''LANG=en_US.UTF-8 lastlog|grep -v Username'''
+ result = mw.execShell(cmd)
+ lastlog_list = []
+ for _line in result[0].split("\n"):
+ if not _line:
+ continue
+ tmp = {}
+ sp_arr = _line.split()
+ tmp['用户'] = sp_arr[0]
+ # tmp['_line'] = _line
+ if _line.find('Never logged in') != -1:
+ tmp['最后登录时间'] = '0'
+ tmp['最后登录来源'] = '-'
+ tmp['最后登录端口'] = '-'
+ lastlog_list.append(tmp)
+ continue
+ tmp['最后登录来源'] = sp_arr[2]
+ tmp['最后登录端口'] = sp_arr[1]
+ tmp['最后登录时间'] = self.__to_date2(' '.join(sp_arr[3:]))
+
+ lastlog_list.append(tmp)
+ lastlog_list = sorted(lastlog_list, key=lambda x: x[
+ '最后登录时间'], reverse=True)
+ for i in range(len(lastlog_list)):
+ if lastlog_list[i]['最后登录时间'] == '0':
+ lastlog_list[i]['最后登录时间'] = '从未登录过'
+ return mw.returnJson(True, 'ok!', lastlog_list)
+
+ def get_sys_log_with_name(self, log_name):
+ if log_name in ['wtmp', 'btmp', 'utmp'] or log_name.find('wtmp') == 0 or log_name.find('btmp') == 0 or log_name.find('utmp') == 0:
+ return self.get_last(log_name)
+
+ if log_name.find('lastlog') == 0:
+ return self.get_lastlog()
+
+ if log_name.find('sa/sa') == 0:
+ if log_name.find('sa/sar') == -1:
+ return mw.execShell("sar -f /var/log/{}".format(log_name))[0]
+ log_dir = '/var/log'
+ log_file = log_dir + '/' + log_name
+ if not os.path.exists(log_file):
+ return mw.returnJson(False, '日志文件不存在!')
+ result = mw.getLastLine(log_file, 100)
+ log_list = []
+ is_string = True
+ for _line in result.split("\n"):
+ if not _line.strip():
+ continue
+ if log_name.find('sa/sa') == -1:
+ if _line[:3] in self.__months:
+ _msg = _line[16:]
+ _tmp = _msg.split(": ")
+ _act = ''
+ if len(_tmp) > 1:
+ _act = _tmp[0]
+ _msg = _tmp[1]
+ else:
+ _msg = _tmp[0]
+ _line = {
+ "时间": self.__to_date4(_line[:16].strip()),
+ "角色": _act,
+ "事件": _msg
+ }
+ is_string = False
+ elif _line[:2] in ['19', '20', '21', '22', '23', '24']:
+ _msg = _line[19:]
+ _tmp = _msg.split(" ")
+ _act = _tmp[1]
+ _msg = ' '.join(_tmp[2:])
+ _line = {
+ "时间": _line[:19].strip(),
+ "角色": _act,
+ "事件": _msg
+ }
+ is_string = False
+ elif log_name.find('alternatives') == 0:
+ _tmp = _line.split(": ")
+ _last = _tmp[0].split(" ")
+ _act = _last[0]
+ _msg = ' '.join(_tmp[1:])
+ _line = {
+ "时间": ' '.join(_last[1:]).strip(),
+ "角色": _act,
+ "事件": _msg
+ }
+ is_string = False
+ else:
+ if not is_string:
+ if type(_line) != dict:
+ continue
+
+ log_list.append(_line)
+ try:
+ # if len(log_list) > 1:
+ # if type(log_list[0]) != type(log_list[1]):
+ # del(log_list[0])
+ # log_list = sorted(log_list,key=lambda x:x['时间'],reverse=True)
+ # return log_list
+
+ _string = []
+ _dict = []
+ _list = []
+ for _line in log_list:
+ if isinstance(_line, str):
+ _string.append(_line.strip())
+ elif isinstance(_line, dict):
+ _dict.append(_line)
+ elif isinstance(_line, list):
+ _list.append(_line)
+ else:
+ continue
+ _str_len = len(_string)
+ _dict_len = len(_dict)
+ _list_len = len(_list)
+ if _str_len > _dict_len + _list_len:
+ return "\n".join(_string)
+ elif _dict_len > _str_len + _list_len:
+ return mw.returnJson(True, 'ok!', _dict)
+ else:
+ return mw.returnJson(True, 'ok!', _list)
+
+ except:
+ data = '\n'.join(log_list)
+ return mw.returnJson(True, 'ok!', data)
+
+ def get_sys_log(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['log_name'])
+ if not check[0]:
+ return check[1]
+
+ log_name = args['log_name']
+ return self.get_sys_log_with_name(log_name)
+
+ def __to_date2(self, date_str):
+ tmp = date_str.split()
+ s_date = str(tmp[-1]) + '-' + self.__months.get(tmp[1],
+ tmp[1]) + '-' + tmp[2] + ' ' + tmp[3]
+ return s_date
+
+ def __to_date3(self, date_str):
+ tmp = date_str.split()
+ s_date = str(datetime.now().year) + '-' + \
+ self.__months.get(tmp[1], tmp[1]) + '-' + tmp[2] + ' ' + tmp[3]
+ return s_date
+
+ def __to_date4(self, date_str):
+ tmp = date_str.split()
+ s_date = str(datetime.now().year) + '-' + \
+ self.__months.get(tmp[0], tmp[0]) + '-' + tmp[1] + ' ' + tmp[2]
+ return s_date
+
+ def op_log(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['p'])
+ if not check[0]:
+ return check[1]
+
+ p = int(args['p'])
+ limit = 10
+ start = (p - 1) * limit
+
+ _list = mw.M('logs').field(
+ 'id,type,log,addtime').where('type=?', (self.__name,)).limit(str(start) + ',' + str(limit)).order('id desc').select()
+ data = {}
+ data['data'] = _list
+ count = mw.M('logs').where('type=?', (self.__name,)).count()
+ _page = {}
+ _page['count'] = count
+ _page['tojs'] = 'ssOpLogList'
+ _page['p'] = p
+ data['page'] = mw.getPage(_page)
+ return mw.returnJson(True, 'ok', data)
+
+
+def get_sys_log(args):
+ classApp = App()
+ data = classApp.get_sys_log_with_name(args['log_name'])
+ return data
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ classApp = App()
+ try:
+ data = eval("classApp." + func + "()")
+ print(data)
+ except Exception as e:
+ print(mw.getTracebackInfo())
diff --git a/plugins/tamper_proof_py/conf/config.json b/plugins/tamper_proof_py/conf/config.json
new file mode 100644
index 000000000..8a372031a
--- /dev/null
+++ b/plugins/tamper_proof_py/conf/config.json
@@ -0,0 +1,5 @@
+{
+ "open": true,
+ "excludePath": [ "cache", "threadcache", "log", "logs", "config", "runtime", "temp","tmp","caches","tmps"],
+ "protectExt": [ "php", "html", "htm", "shtml", "tpl", "js", "css", "jsp", "do" ]
+}
\ No newline at end of file
diff --git a/plugins/tamper_proof_py/ico.png b/plugins/tamper_proof_py/ico.png
new file mode 100644
index 000000000..3dede4e91
Binary files /dev/null and b/plugins/tamper_proof_py/ico.png differ
diff --git a/plugins/tamper_proof_py/index.html b/plugins/tamper_proof_py/index.html
new file mode 100755
index 000000000..cb5de195f
--- /dev/null
+++ b/plugins/tamper_proof_py/index.html
@@ -0,0 +1,1163 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/tamper_proof_py/index.py b/plugins/tamper_proof_py/index.py
new file mode 100755
index 000000000..59d538d4b
--- /dev/null
+++ b/plugins/tamper_proof_py/index.py
@@ -0,0 +1,629 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import json
+import re
+import psutil
+from datetime import datetime
+
+sys.dont_write_bytecode = True
+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
+from utils.site import sites as MwSites
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+class App:
+
+ __total = 'total.json'
+ __sites = []
+
+ def __init__(self):
+ pass
+
+ def getPluginName(self):
+ return 'tamper_proof_py'
+
+ def getPluginDir(self):
+ return mw.getPluginDir() + '/' + self.getPluginName()
+
+ def getServerDir(self):
+ return mw.getServerDir() + '/' + self.getPluginName()
+
+ def getInitDFile(self):
+ if app_debug:
+ return '/tmp/' + self.getPluginName()
+ return '/etc/init.d/' + self.getPluginName()
+
+ def getInitDTpl(self):
+ path = self.getPluginDir() + "/init.d/" + self.getPluginName() + ".tpl"
+ return path
+
+ def getArgs(self):
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ if t.strip() == '':
+ tmp = []
+ else:
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ return tmp
+
+ def checkArgs(self, 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 getTotal(self, siteName=None, day=None):
+ defaultTotal = {"total": 0, "delete": 0,
+ "create": 0, "modify": 0, "move": 0}
+ if siteName:
+ total = {}
+ total_path = self.getServerDir() + '/sites/' + siteName + '/' + self.__total
+ if not os.path.exists(total_path):
+ total['site'] = defaultTotal
+ else:
+ total_data = mw.readFile(total_path)
+ if total_data['site']:
+ total['site'] = json.loads(total_data['site'])
+ else:
+ total['site'] = defaultTotal
+
+ if not day:
+ day = time.strftime("%Y-%m-%d", time.localtime())
+ total_day_path = self.getServerDir() + '/sites/' + siteName + '/day/total.json'
+ if not os.path.exists(total_day_path):
+ total['day'] = defaultTotal
+ else:
+ total['day'] = mw.readFile(total_day_path)
+ if total['day']:
+ total['day'] = json.loads(total['day'])
+ else:
+ total['day'] = defaultTotal
+ else:
+ filename = self.getServerDir() + '/sites/' + self.__total
+ if os.path.exists(filename):
+ total = json.loads(mw.readFile(filename))
+ else:
+ total = defaultTotal
+ return total
+
+ def getSites(self):
+ sites_path = self.getServerDir() + '/sites.json'
+ t = mw.readFile(sites_path)
+ if not os.path.exists(sites_path) or not t:
+ mw.writeFile(sites_path, '[]')
+ data = json.loads(mw.readFile(sites_path))
+
+ is_write = False
+ rm_keys = ['lock', 'bak_open']
+ for i in data:
+ i_keys = i.keys()
+ if not 'open' in i_keys:
+ i['open'] = False
+ for o in rm_keys:
+ if o in i_keys:
+ if i[o]:
+ i['open'] = True
+ i.pop(o)
+ is_write = True
+ if is_write:
+ mw.writeFile(sites_path, json.dumps(data))
+
+ self.__sites = data
+ return data
+
+ def writeSites(self, data):
+ mw.writeFile(self.getServerDir() + '/sites.json', json.dumps(data))
+ # mw.ExecShell('/etc/init.d/bt_tamper_proof reload')
+
+ def __getFind(self, siteName):
+ data = self.getSites()
+ for siteInfo in data:
+ if siteName == siteInfo['siteName']:
+ return siteInfo
+ return None
+
+ def writeLog(self, log):
+ mw.writeLog('防篡改程序', log)
+
+ def saveSiteConfig(self, siteInfo):
+ data = self.getSites()
+ for i in range(len(data)):
+ if data[i]['siteName'] != siteInfo['siteName']:
+ continue
+ data[i] = siteInfo
+ break
+ self.writeSites(data)
+
+ def syncSites(self):
+ data = self.getSites()
+ sites = mw.M('sites').field('name,path').select()
+
+ config_path = self.getPluginDir() + '/conf/config.json'
+ config = json.loads(mw.readFile(config_path))
+ names = []
+ n = 0
+
+ # print(config)
+ for siteTmp in sites:
+ names.append(siteTmp['name'])
+ siteInfo = self.__getFind(siteTmp['name'])
+ if siteInfo:
+ if siteInfo['path'] != siteTmp['path']:
+ siteInfo['path'] = siteTmp['path']
+ self.saveSiteConfig(siteInfo)
+ data = self.getSites()
+ continue
+ siteInfo = {}
+ siteInfo['siteName'] = siteTmp['name']
+ siteInfo['path'] = siteTmp['path']
+ siteInfo['open'] = False
+ siteInfo['excludePath'] = config['excludePath']
+ siteInfo['protectExt'] = config['protectExt']
+ data.append(siteInfo)
+ n += 1
+
+ newData = []
+ for siteInfoTmp in data:
+ if siteInfoTmp['siteName'] in names:
+ newData.append(siteInfoTmp)
+ else:
+ mw.execShell("rm -rf " + self.getServerDir() +
+ '/sites/' + siteInfoTmp['siteName'])
+ n += 1
+ if n > 0:
+ self.writeSites(newData)
+ self.__sites = None
+
+ def initDreplace(self):
+ file_tpl = self.getInitDTpl()
+ service_path = self.getServerDir()
+
+ initD_path = service_path + '/init.d'
+ if not os.path.exists(initD_path):
+ os.mkdir(initD_path)
+
+ # init.d
+ file_bin = initD_path + '/' + self.getPluginName()
+ if not os.path.exists(file_bin):
+ # 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)
+
+ # systemd
+ # /usr/lib/systemd/system
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/tamper_proof_py.service'
+ systemServiceTpl = self.getPluginDir() + '/init.d/tamper_proof_py.service.tpl'
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ 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 getDays(self, path):
+ days = []
+ if not os.path.exists(path):
+ os.makedirs(path)
+ for dirname in os.listdir(path):
+ if dirname == '..' or dirname == '.' or dirname == 'total.json':
+ continue
+ if not os.path.isdir(path + '/' + dirname):
+ continue
+ days.append(dirname)
+ days = sorted(days, reverse=True)
+ return days
+
+ def status(self):
+ '''
+ 状态
+ '''
+ initd_file = self.getServerDir() + '/init.d/' + self.getPluginName()
+ if not os.path.exists(initd_file):
+ return 'stop'
+ cmd = initd_file + ' status|grep already'
+ data = mw.execShell(cmd)
+ if data[0] != '':
+ return 'start'
+ return 'stop'
+
+ def tpOp(self, method):
+ file = self.initDreplace()
+ if not mw.isAppleSystem():
+ cmd = 'systemctl ' + method + ' ' + self.getPluginName()
+ data = mw.execShell(cmd)
+ if data[1] == '':
+ return mw.returnJson(True, '操作成功')
+ return mw.returnJson(False, '操作失败')
+
+ cmd = file + ' ' + method
+ data = mw.execShell(cmd)
+ if data[1] == '':
+ return mw.returnJson(True, '操作成功')
+ return mw.returnJson(False, '操作失败')
+
+ def start(self):
+ return self.tpOp('start')
+
+ def restart(self):
+ return self.tpOp('restart')
+
+ def service_admin(self):
+ if mw.isAppleSystem():
+ return mw.returnJson(False, '仅支持Linux!')
+
+ args = self.getArgs()
+ check = self.checkArgs(args, ['serviceStatus'])
+ if not check[0]:
+ return check[1]
+
+ method = args['serviceStatus']
+ return self.tpOp(method)
+
+ def initd_status(self):
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+ shell_cmd = 'systemctl status %s | grep loaded | grep "enabled;"' % (
+ self.getPluginName())
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+ def initd_install(self):
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl enable ' + self.getPluginName())
+ return 'ok'
+
+ def initd_uninstall(self):
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl disable ' + self.getPluginName())
+ return 'ok'
+
+ def set_site_status(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['siteName'])
+ if not check[0]:
+ return check[1]
+
+ siteName = args['siteName']
+ siteInfo = self.__getFind(siteName)
+ if not siteInfo:
+ return mw.returnJson(False, '指定站点不存在!')
+ try:
+ siteInfo['open'] = not siteInfo['open']
+ except:
+ siteInfo['open'] = not siteInfo['open']
+
+ m_logs = {True: '开启', False: '关闭'}
+ self.writeLog('%s站点[%s]防篡改保护' % (m_logs[siteInfo['open']], siteInfo['siteName']))
+ self.siteReload(siteInfo)
+ self.saveSiteConfig(siteInfo)
+ self.restart()
+ return mw.returnJson(True, '设置成功!')
+
+ def get_run_logs(self):
+ log_file = self.getServerDir() + '/service.log'
+ return mw.returnJson(True, mw.getLastLine(log_file, 200))
+
+ # 取文件指定尾行数
+ def getNumLines(self, path, num, p=1):
+ pyVersion = sys.version_info[0]
+ try:
+ import cgi
+ if not os.path.exists(path):
+ return ""
+ start_line = (p - 1) * num
+ count = start_line + num
+ fp = open(path, 'rb')
+ buf = ""
+ fp.seek(-1, 2)
+ if fp.read(1) == "\n":
+ fp.seek(-1, 2)
+ data = []
+ b = True
+ n = 0
+ for i in range(count):
+ while True:
+ newline_pos = str.rfind(str(buf), "\n")
+ pos = fp.tell()
+ if newline_pos != -1:
+ if n >= start_line:
+ line = buf[newline_pos + 1:]
+ try:
+ data.append(json.loads(cgi.escape(line)))
+ except:
+ pass
+ buf = buf[:newline_pos]
+ n += 1
+ break
+ else:
+ if pos == 0:
+ b = False
+ break
+ to_read = min(4096, pos)
+ fp.seek(-to_read, 1)
+ t_buf = fp.read(to_read)
+ if pyVersion == 3:
+ if type(t_buf) == bytes:
+ t_buf = t_buf.decode('utf-8')
+ buf = t_buf + buf
+ fp.seek(-to_read, 1)
+ if pos - to_read == 0:
+ buf = "\n" + buf
+ if not b:
+ break
+ fp.close()
+ except:
+ return []
+ if len(data) >= 2000:
+ arr = []
+ for d in data:
+ arr.insert(0, json.dumps(d))
+ mw.writeFile(path, "\n".join(arr))
+ return data
+
+ def get_safe_logs(self):
+
+ args = self.getArgs()
+ check = self.checkArgs(args, ['siteName'])
+ if not check[0]:
+ return check[1]
+
+ siteName = args['siteName']
+
+ data = {}
+ path = self.getPluginDir() + '/sites/' + siteName + '/day'
+ data['days'] = self.getDays(path)
+
+ if not data['days']:
+ data['logs'] = []
+ else:
+ p = 1
+ if hasattr(args, 'p'):
+ p = args['p']
+
+ day = data['days'][0]
+ if hasattr(args, 'day'):
+ day = args['day']
+ data['get_day'] = day
+ logs_path = path + '/' + day + '/logs.json'
+ data['logs'] = self.getNumLines(logs_path, 2000, int(p))
+ return mw.returnJson(True, 'ok', data)
+
+ def get_site_find(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['siteName'])
+ if not check[0]:
+ return check[1]
+
+ siteName = args['siteName']
+ data = self.__getFind(siteName)
+ return mw.returnJson(True, 'ok', data)
+
+ def siteReload(self, siteInfo):
+ cmd = "python3 {} {}".format(
+ mw.getPluginDir() + '/tamper_proof_service.py unlock', siteInfo['path'])
+ mw.execShell(cmd)
+ tip_file = mw.getServerDir() + '/tips/' + siteInfo['siteName'] + '.pl'
+ if os.path.exists(tip_file):
+ os.remove(tip_file)
+
+ def remove_protect_ext(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['siteName', 'protectExt'])
+ if not check[0]:
+ return check[1]
+
+ siteName = args['siteName']
+ protectExt = args['protectExt'].strip()
+
+ siteInfo = self.__getFind(siteName)
+
+ if not siteInfo:
+ return mw.returnJson(False, '指定站点不存在!')
+ if not protectExt:
+ return mw.returnJson(False, '被删除的保护列表不能为空')
+
+ for protectExt in protectExt.split(','):
+ if not protectExt in siteInfo['protectExt']:
+ continue
+ siteInfo['protectExt'].remove(protectExt)
+ self.writeLog('站点[%s]从受保护列表中删除[.%s]' % (siteInfo['siteName'], protectExt))
+ self.siteReload(siteInfo)
+ self.saveSiteConfig(siteInfo)
+ return mw.returnJson(True, '删除成功!')
+
+ def add_protect_ext(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['siteName', 'protectExt'])
+ if not check[0]:
+ return check[1]
+
+ siteName = args['siteName']
+ protectExt = args['protectExt'].strip()
+
+ siteInfo = self.__getFind(siteName)
+ if not siteInfo:
+ return mw.returnJson(False, '指定站点不存在!')
+ protectExt = protectExt.lower()
+ for protectExt in protectExt.split("\n"):
+ if protectExt[0] == '/':
+ if os.path.isdir(protectExt):
+ continue
+ if protectExt in siteInfo['protectExt']:
+ continue
+ siteInfo['protectExt'].insert(0, protectExt)
+ self.writeLog('站点[%s]添加文件类型或文件名[.%s]到受保护列表' %
+ (siteInfo['siteName'], protectExt))
+ self.siteReload(siteInfo)
+ self.saveSiteConfig(siteInfo)
+ return mw.returnJson(True, '添加成功!')
+
+ def add_excloud(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['siteName', 'excludePath'])
+ if not check[0]:
+ return check[1]
+
+ siteName = args['siteName']
+ excludePath = args['excludePath'].strip()
+ siteInfo = self.__getFind(siteName)
+ if not siteInfo:
+ return mw.returnJson(False, '指定站点不存在!')
+
+ if not excludePath:
+ return mw.returnJson(False, '排除内容不能为空')
+
+ for excludePath in excludePath.split('\n'):
+ if not excludePath:
+ continue
+ if excludePath.find('/') != -1:
+ if not os.path.exists(excludePath):
+ continue
+ excludePath = excludePath.lower()
+ if excludePath[-1] == '/':
+ excludePath = excludePath[:-1]
+ if excludePath in siteInfo['excludePath']:
+ continue
+ siteInfo['excludePath'].insert(0, excludePath)
+ self.writeLog('站点[%s]添加排除目录名[%s]到排除列表' %
+ (siteInfo['siteName'], excludePath))
+
+ self.siteReload(siteInfo)
+ self.saveSiteConfig(siteInfo)
+ return mw.returnJson(True, '添加成功!')
+
+ def remove_excloud(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['siteName', 'excludePath'])
+ if not check[0]:
+ return check[1]
+
+ siteName = args['siteName']
+ siteInfo = self.__getFind(siteName)
+ excludePath = args['excludePath'].strip()
+ if excludePath == '':
+ return mw.returnJson(False, '排除文件或目录不能为空')
+ if not siteInfo:
+ return mw.returnJson(False, '指定站点不存在!')
+
+ for excludePath in excludePath.split(','):
+ if not excludePath:
+ continue
+ if not excludePath in siteInfo['excludePath']:
+ continue
+ siteInfo['excludePath'].remove(excludePath)
+ self.writeLog('站点[%s]从排除列表中删除目录名[%s]' %
+ (siteInfo['siteName'], excludePath))
+ self.siteReload(siteInfo)
+ self.saveSiteConfig(siteInfo)
+ return mw.returnJson(True, '删除成功!')
+
+ def sim_test(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['path'])
+ if not check[0]:
+ return check[1]
+
+ path = args['path'].strip()
+ if not os.path.exists(path):
+ return mw.returnJson(False, "此目录不存在")
+
+ # 判断是否安装php
+ php_version = MwSites.instance().getPhpVersion()
+ if not php_version['data']:
+ return mw.returnJson(False, "未安装PHP测试失败")
+
+ php_path = '/www/server/php/' + php_version['data'][1]['version'] + '/bin/php'
+ php_name = path + "/" + str(int(time.time())) + ".php"
+ if os.path.exists(php_name):
+ mw.execShell("rm -rf %s" % php_name)
+ # 写入
+ cmd = php_path + \
+ " -r \"file_put_contents('{}','{}');\"".format(php_name, php_name)
+ mw.execShell(cmd)
+ time.sleep(0.5)
+ if os.path.exists(php_name):
+ if os.path.exists(php_name):
+ mw.execShell("rm -rf %s" % php_name)
+ return mw.returnJson(False, "拦截失败,可能未开启防篡改")
+ return mw.returnJson(True, "拦截成功")
+
+ def set_site_status_all(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['siteNames', 'siteState'])
+ if not check[0]:
+ return check[1]
+
+ sites = self.getSites()
+ siteState = True if args['siteState'] == '1' else False
+ siteNames = json.loads(args['siteNames'])
+ m_logs = {True: '开启', False: '关闭'}
+ for i in range(len(sites)):
+ if sites[i]['siteName'] in siteNames:
+ sites[i]['open'] = siteState
+ self.writeLog('%s站点[%s]防篡改保护' % (m_logs[siteState], sites[i]['siteName']))
+ self.writeSites(sites)
+ return mw.returnJson(True, '批量设置成功')
+
+ def get_index(self):
+ self.syncSites()
+ args = self.getArgs()
+ day = None
+ if 'day' in args:
+ day = args['day']
+
+ ser_status = self.status()
+ ser_status_bool = False
+ if ser_status == 'start':
+ ser_status_bool = True
+ data = {}
+ data['open'] = ser_status_bool
+ data['total'] = self.getTotal()
+ data['sites'] = self.getSites()
+ for i in range(len(data['sites'])):
+ data['sites'][i]['total'] = self.getTotal(
+ data['sites'][i]['siteName'], day)
+ return mw.returnJson(True, 'ok', data)
+
+ def get_speed(self):
+ print("12")
+
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ classApp = App()
+ try:
+ data = eval("classApp." + func + "()")
+ print(data)
+ except Exception as e:
+ print(mw.getTracebackInfo())
diff --git a/plugins/tamper_proof_py/info.json b/plugins/tamper_proof_py/info.json
new file mode 100644
index 000000000..29c3b599a
--- /dev/null
+++ b/plugins/tamper_proof_py/info.json
@@ -0,0 +1,15 @@
+{
+ "title": "网站防篡改程序PY",
+ "tip": "lib",
+ "name": "tamper_proof_py",
+ "type": "soft",
+ "ps": "事件型防篡改程序,可有效保护网站重要文件不被木马篡改[Python]",
+ "versions": "1.0",
+ "shell": "install.sh",
+ "checks": "server/tamper_proof_py",
+ "path": "server/tamper_proof_py",
+ "author": "midoks",
+ "home": "",
+ "date":"2022-01-18",
+ "pid":"4"
+}
\ No newline at end of file
diff --git a/plugins/tamper_proof_py/init.d/tamper_proof_py.service.tpl b/plugins/tamper_proof_py/init.d/tamper_proof_py.service.tpl
new file mode 100644
index 000000000..ee7907398
--- /dev/null
+++ b/plugins/tamper_proof_py/init.d/tamper_proof_py.service.tpl
@@ -0,0 +1,14 @@
+[Unit]
+Description=tamper_proof_py server daemon
+After=network.target
+
+[Service]
+Type=forking
+ExecStart={$SERVER_PATH}/init.d/tamper_proof_py start
+ExecStop={$SERVER_PATH}/init.d/tamper_proof_py stop
+ExecReload={$SERVER_PATH}/init.d/tamper_proof_py reload
+KillMode=process
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/plugins/tamper_proof_py/init.d/tamper_proof_py.tpl b/plugins/tamper_proof_py/init.d/tamper_proof_py.tpl
new file mode 100644
index 000000000..ae38bab4c
--- /dev/null
+++ b/plugins/tamper_proof_py/init.d/tamper_proof_py.tpl
@@ -0,0 +1,95 @@
+#!/bin/bash
+# chkconfig: 2345 55 25
+# description:tamper_proof_service
+
+### BEGIN INIT INFO
+# Provides: tamper_proof_service
+# Required-Start: $all
+# Required-Stop: $all
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts tamper_proof_service
+# Description: starts the tamper_proof_service
+### END INIT INFO
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+mw_path={$SERVER_PATH}
+rootPath=$(dirname "$mw_path")
+PATH=$PATH:$mw_path/bin
+
+if [ -f $rootPath/mdserver-web/bin/activate ];then
+ source $rootPath/mdserver-web/bin/activate
+fi
+
+# cd /www/server/mdserver-web && python3 plugins/tamper_proof_py/tamper_proof_service.py start
+sys_start()
+{
+ isStart=$(ps aux |grep -E "(tamper_proof_service)"|grep -v grep |grep -v 'tamper_proof_py/tamper_proof_service.py start' | grep -v 'tamper_proof_py/tamper_proof_service.py reload' | grep -v 'tamper_proof_py/tamper_proof_service.py restart' | grep -v systemctl | grep -v '/bin/sh' | grep -v '/bin/bash' | awk '{print $2}'|xargs)
+ if [ "$isStart" == '' ];then
+ echo -e "Starting tamper_proof_service... \c"
+ cd $rootPath/mdserver-web
+ nohup python3 plugins/tamper_proof_py/tamper_proof_service.py start &> $mw_path/service.log &
+ sleep 0.5
+ isStart=$(ps aux |grep -E "(tamper_proof_service)"|grep -v grep|awk '{print $2}'|xargs)
+ if [ "$isStart" == '' ];then
+ echo -e "\033[31mfailed\033[0m"
+ echo '------------------------------------------------------'
+ cat $mw_path/service.log
+ echo '------------------------------------------------------'
+ echo -e "\033[31mError: tamper_proof_service startup failed.\033[0m"
+ return;
+ fi
+ echo -e "\033[32mdone\033[0m"
+ else
+ echo "Starting tamper_proof_service (pid $isStart) already running"
+ fi
+}
+
+sys_stop()
+{
+ echo -e "Stopping tamper_proof_service... \c";
+ pids=$(ps aux |grep -E "(tamper_proof_service)"|grep -v grep|grep -v '/bin/bash'|grep -v systemctl | grep -v 'tamper_proof_py/tamper_proof_service.py stop' | grep -v 'tamper_proof_py/tamper_proof_service.py reload' | grep -v 'tamper_proof_py/tamper_proof_service.py restart' |awk '{print $2}'|xargs)
+ arr=($pids)
+ for p in ${arr[@]}
+ do
+ kill -9 $p
+ done
+ cd $rootPath/mdserver-web
+ python3 plugins/tamper_proof_py/tamper_proof_service.py stop
+ echo -e "\033[32mdone\033[0m"
+}
+
+sys_status()
+{
+ isStart=$(ps aux |grep -E "(tamper_proof_service)"|grep -v grep|grep -v "init.d/tamper_proof_py"|grep -v systemctl|awk '{print $2}'|xargs)
+ if [ "$isStart" != '' ];then
+ echo -e "\033[32mtamper_proof_service (pid $isStart) already running\033[0m"
+ else
+ echo -e "\033[32mtamper_proof_service not running\033[0m"
+ fi
+}
+
+case "$1" in
+ 'start')
+ sys_start
+ ;;
+ 'stop')
+ sys_stop
+ ;;
+ 'restart')
+ sys_stop
+ sleep 0.2
+ sys_start
+ ;;
+ 'reload')
+ sys_stop
+ sleep 0.2
+ sys_start
+ ;;
+ 'status')
+ sys_status
+ ;;
+ *)
+ echo "Usage: systemctl {start|stop|restart|reload} tamper_proof_service"
+ ;;
+esac
\ No newline at end of file
diff --git a/plugins/tamper_proof_py/install.sh b/plugins/tamper_proof_py/install.sh
new file mode 100755
index 000000000..ee9c89e6e
--- /dev/null
+++ b/plugins/tamper_proof_py/install.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+# pip install pyinotify
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+pip install pyinotify
+
+# cd /www/server/mdserver-web/plugins/tamper_proof_py && bash install.sh install 1.0
+
+# cd /www/server/mdserver-web && python3 plugins/tamper_proof_py/index.py start 1.0
+# cd /www/server/mdserver-web && python3 plugins/tamper_proof_py/index.py service_admin {"serviceStatus":"start"}
+# systemctl start tamper_proof_py
+# systemctl status tamper_proof_py
+
+SYSOS=`uname`
+VERSION=$2
+APP_NAME=tamper_proof_py
+
+Install_App()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/tamper_proof_py
+ echo "$VERSION" > $serverPath/tamper_proof_py/version.pl
+ echo 'install complete'
+
+ #初始化
+ cd ${serverPath}/mdserver-web && python3 plugins/tamper_proof_py/index.py start $VERSION
+ cd ${serverPath}/mdserver-web && python3 plugins/tamper_proof_py/index.py initd_install $VERSION
+}
+
+Uninstall_App()
+{
+ if [ -f /usr/lib/systemd/system/${APP_NAME}.service ] || [ -f /lib/systemd/system/${APP_NAME}.service ] ;then
+ systemctl stop ${APP_NAME}
+ systemctl disable ${APP_NAME}
+ rm -rf /usr/lib/systemd/system/${APP_NAME}.service
+ rm -rf /lib/systemd/system/${APP_NAME}.service
+ systemctl daemon-reload
+ fi
+
+ rm -rf $serverPath/tamper_proof_py
+ echo "uninstall completed"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/tamper_proof_py/tamper_proof_service.py b/plugins/tamper_proof_py/tamper_proof_service.py
new file mode 100644
index 000000000..7f5fa4f25
--- /dev/null
+++ b/plugins/tamper_proof_py/tamper_proof_service.py
@@ -0,0 +1,552 @@
+# coding=utf-8
+
+# +--------------------------------------------------------------------
+# | 事件型防篡改
+# +--------------------------------------------------------------------
+import sys
+import os
+import pyinotify
+import json
+import shutil
+import time
+import psutil
+import threading
+import datetime
+
+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
+
+
+class MyEventHandler(pyinotify.ProcessEvent):
+ _PLUGIN_PATH = '/www/server/tamper_proof_py'
+ _CONFIG = '/config.json'
+ _SITES = '/sites.json'
+ _SITES_DATA = None
+ _CONFIG_DATA = None
+ _DONE_FILE = None
+ bakcupChirdPath = []
+
+ def __init__(self):
+ self._PLUGIN_PATH = self.getServerDir()
+
+ def getPluginName(self):
+ return 'tamper_proof_py'
+
+ def getPluginDir(self):
+ return mw.getPluginDir() + '/' + self.getPluginName()
+
+ def getServerDir(self):
+ return mw.getServerDir() + '/' + self.getPluginName()
+
+ def rmdir(self, filename):
+ try:
+ shutil.rmtree(filename)
+ except:
+ pass
+
+ def check_site_logs(self, Stiename, datetime_time):
+ ret = []
+ cur_month = datetime_time.month
+ cur_day = datetime_time.day
+ cur_year = datetime_time.year
+ cur_hour = datetime_time.hour
+ cur_minute = datetime_time.minute
+ cur_second = int(datetime_time.second)
+ months = {'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06', 'Jul': '07',
+ 'Aug': '08', 'Sep': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'}
+ logs_data = self.get_site_logs(Stiename)
+ if not logs_data:
+ return False
+ for i2 in logs_data:
+ try:
+ i = i2.split()
+ # 判断状态码是否为200
+ if int(i[8]) not in [200, 500]:
+ continue
+ # 判断是否为POST
+ day_time = i[3].split('/')[0].split('[')[1]
+ if int(cur_day) != int(day_time):
+ continue
+ month_time = i[3].split('/')[1]
+ if int(months[month_time]) != int(cur_month):
+ continue
+ year_time = i[3].split('/')[2].split(':')[0]
+ if int(year_time) != int(cur_year):
+ continue
+ hour_time = i[3].split('/')[2].split(':')[1]
+ if int(hour_time) != int(cur_hour):
+ continue
+ minute_time = i[3].split('/')[2].split(':')[2]
+ if int(minute_time) != int(cur_minute):
+ continue
+ second_time = int(i[3].split('/')[2].split(':')[3])
+ if cur_second - second_time > 10:
+ continue
+ ret.append(i2)
+ except:
+ continue
+ ret2 = []
+ if len(ret) > 20:
+ for i2 in logs_data:
+ try:
+ i = i2.split()
+ if i[6] != 'POST':
+ continue
+ if int(i[8]) not in [200, 500]:
+ continue
+ # 判断是否为POST
+ day_time = i[3].split('/')[0].split('[')[1]
+ if int(cur_day) != int(day_time):
+ continue
+ month_time = i[3].split('/')[1]
+ if int(months[month_time]) != int(cur_month):
+ continue
+ year_time = i[3].split('/')[2].split(':')[0]
+ if int(year_time) != int(cur_year):
+ continue
+ hour_time = i[3].split('/')[2].split(':')[1]
+ if int(hour_time) != int(cur_hour):
+ continue
+ minute_time = i[3].split('/')[2].split(':')[2]
+ if int(minute_time) != int(cur_minute):
+ continue
+ ret2.append(i2)
+ except:
+ continue
+ if ret2:
+ ret = ret2
+ if len(ret) > 20:
+ return ret[0:20]
+ return ret
+
+ def get_site_logs(self, site_name):
+ try:
+ pythonV = sys.version_info[0]
+ path = '/www/wwwlogs/' + site_name + '.log'
+ num = 500
+ if not os.path.exists(path):
+ return []
+ p = 1
+ start_line = (p - 1) * num
+ count = start_line + num
+ fp = open(path, 'rb')
+ buf = ""
+ try:
+ fp.seek(-1, 2)
+ except:
+ return []
+ if fp.read(1) == "\n":
+ fp.seek(-1, 2)
+ data = []
+ b = True
+ n = 0
+ c = 0
+ while c < count:
+ while True:
+ newline_pos = str.rfind(buf, "\n")
+ pos = fp.tell()
+ if newline_pos != -1:
+ if n >= start_line:
+ line = buf[newline_pos + 1:]
+ if line:
+ try:
+ data.append(line)
+ except:
+ c -= 1
+ n -= 1
+ pass
+ else:
+ c -= 1
+ n -= 1
+ buf = buf[:newline_pos]
+ n += 1
+ c += 1
+ break
+ else:
+ if pos == 0:
+ b = False
+ break
+ to_read = min(4096, pos)
+ fp.seek(-to_read, 1)
+ t_buf = fp.read(to_read)
+ if pythonV == 3:
+ t_buf = t_buf.decode('utf-8', errors="ignore")
+ buf = t_buf + buf
+ fp.seek(-to_read, 1)
+ if pos - to_read == 0:
+ buf = "\n" + buf
+ if not b:
+ break
+ fp.close()
+ except:
+ data = []
+ return data
+
+ def process_IN_CREATE(self, event):
+ siteInfo = self.get_SITE_CONFIG(event.pathname)
+ if not self.check_FILE(event, siteInfo, True):
+ return False
+ self._DONE_FILE = event.pathname
+ if event.dir:
+ if os.path.exists(event.pathname):
+ self.rmdir(event.pathname)
+ self.write_LOG('create', siteInfo[
+ 'siteName'], event.pathname, datetime.datetime.now())
+
+ else:
+ if os.path.exists(event.pathname):
+ try:
+ src_path = os.path.dirname(event.pathname)
+ os.system("chattr -a {}".format(src_path))
+ os.remove(event.pathname)
+ os.system("chattr +a {}".format(src_path))
+ self.write_LOG('create', siteInfo[
+ 'siteName'], event.pathname, datetime.datetime.now())
+ except:
+ pass
+
+ def process_IN_MOVED_TO(self, event):
+ # 检查是否受保护
+ siteInfo = self.get_SITE_CONFIG(event.pathname)
+ if not self.check_FILE(event, siteInfo):
+ return False
+
+ if not getattr(event, 'src_pathname', None):
+ if os.path.isdir(event.pathname):
+ self.rmdir(event.pathname)
+ else:
+ os.remove(event.pathname)
+ self.write_LOG('move', siteInfo[
+ 'siteName'], '未知 -> ' + event.pathname)
+ return True
+
+ # 是否为标记文件
+ if event.src_pathname == self._DONE_FILE:
+ return False
+
+ if not os.path.exists(event.src_pathname):
+ # 标记
+ self._DONE_FILE = event.pathname
+ # 还原
+ os.renames(event.pathname, event.src_pathname)
+
+ # 记录日志
+ self.write_LOG('move', siteInfo['siteName'],
+ event.src_pathname + ' -> ' + event.pathname)
+
+ def check_FILE(self, event, siteInfo, create=False):
+ if not siteInfo:
+ return False
+ if self.exclude_PATH(event.pathname):
+ return False
+ if event.dir and create:
+ return True
+ if not event.dir:
+ if not self.protect_EXT(event.pathname):
+ return False
+ return True
+
+ def protect_EXT(self, pathname):
+ if pathname.find('.') == -1:
+ return False
+ extName = pathname.split('.')[-1].lower()
+ siteData = self.get_SITE_CONFIG(pathname)
+ if siteData:
+ if extName in siteData['protectExt']:
+ return True
+ return False
+
+ def exclude_PATH(self, pathname):
+ if pathname.find('/') == -1:
+ return False
+ siteData = self.get_SITE_CONFIG(pathname)
+ return self.exclude_PATH_OF_SITE(pathname, siteData['excludePath'])
+
+ def exclude_PATH_OF_SITE(self, pathname, excludePath):
+ pathname = pathname.lower()
+ dirNames = pathname.split('/')
+ if excludePath:
+ if pathname in excludePath:
+ return True
+ if pathname + '/' in excludePath:
+ return True
+ for ePath in excludePath:
+ if ePath in dirNames:
+ return True
+ if pathname.find(ePath) == 0:
+ return True
+ return False
+
+ def get_SITE_CONFIG(self, pathname):
+ if not self._SITES_DATA:
+ self._SITES_DATA = json.loads(
+ mw.readFile(self._PLUGIN_PATH + self._SITES))
+ for site in self._SITES_DATA:
+ length = len(site['path'])
+ if len(pathname) < length:
+ continue
+ if site['path'] != pathname[:length]:
+ continue
+ return site
+ return None
+
+ def get_CONFIG(self):
+ if self._CONFIG_DATA:
+ return self._CONFIG_DATA
+ self._CONFIG_DATA = json.loads(
+ mw.readFile(self._PLUGIN_PATH + self._CONFIG))
+
+ def list_DIR(self, path, siteInfo): # path 站点路径
+ if not os.path.exists(path):
+ return
+ lock_files = []
+ lock_dirs = []
+ explode_a = ['log', 'logs', 'cache', 'templates', 'template', 'upload', 'img',
+ 'image', 'images', 'public', 'static', 'js', 'css', 'tmp', 'temp', 'update', 'data']
+ for name in os.listdir(path):
+ try:
+ filename = "{}/{}".format(path, name).replace('//', '/')
+ lower_name = name.lower()
+ lower_filename = filename.lower()
+ if os.path.isdir(filename): # 是否为目录
+ if lower_name in siteInfo['excludePath']:
+ continue # 是否为排除的文件名
+ # 是否为排除目录
+ if not self.exclude_PATH_OF_SITE(filename, siteInfo['excludePath']):
+ if not lower_name in explode_a: # 是否为固定不锁定目录
+ lock_dirs.append('"' + name + '"')
+ self.list_DIR(filename, siteInfo)
+ continue
+
+ # 是否为受保护的文件名或文件全路径
+ if not lower_name in siteInfo['protectExt'] and not lower_filename in siteInfo['protectExt']:
+ if not self.get_EXT_NAME(lower_name) in siteInfo['protectExt']:
+ continue # 是否为受保护文件类型
+
+ if lower_filename in siteInfo['excludePath']:
+ continue # 是否为排除文件
+ if lower_name in siteInfo['excludePath']:
+ continue # 是否为排除的文件名
+ lock_files.append('"' + name + '"')
+ except:
+ print(mw.getTracebackInfo())
+ if lock_files:
+ self.thread_exec(lock_files, path, 'i')
+ if lock_dirs:
+ self.thread_exec(lock_dirs, path, 'a')
+
+ _thread_count = 0
+ _thread_max = 2 * psutil.cpu_count()
+
+ def thread_exec(self, file_list, cwd, i='i'):
+ while self._thread_count > self._thread_max:
+ time.sleep(0.1)
+
+ self._thread_count += 1
+ cmd = "cd {} && chattr +{} {} > /dev/null".format(
+ cwd, i, ' '.join(file_list))
+ p = threading.Thread(target=self.run_thread, args=(cmd,))
+ p.start()
+
+ def run_thread(self, cmd):
+ os.system(cmd)
+ self._thread_count -= 1
+
+ def get_EXT_NAME(self, fileName):
+ return fileName.split('.')[-1]
+
+ def write_LOG(self, eventType, siteName, pathname, datetime):
+ # 获取网站时间的top100 记录
+ site_log = '/www/wwwlogs/%s.log' % siteName
+ logs_data = []
+ if os.path.exists(site_log):
+ logs_data = self.check_site_logs(siteName, datetime)
+ dateDay = time.strftime("%Y-%m-%d", time.localtime())
+ logPath = self._PLUGIN_PATH + '/sites/' + \
+ siteName + '/day/' + dateDay
+ if not os.path.exists(logPath):
+ os.makedirs(logPath)
+ logFile = os.path.join(logPath, 'logs.json')
+ logVar = [int(time.time()), eventType, pathname, logs_data]
+ fp = open(logFile, 'a+')
+ fp.write(json.dumps(logVar) + "\n")
+ fp.close()
+ logFiles = [
+ logPath + '/total.json',
+ self._PLUGIN_PATH + '/sites/' + siteName + '/day/total.json',
+ self._PLUGIN_PATH + '/sites/total.json'
+ ]
+
+ for totalLogFile in logFiles:
+ if not os.path.exists(totalLogFile):
+ totalData = {"total": 0, "delete": 0,
+ "create": 0, "modify": 0, "move": 0}
+ else:
+ dataTmp = mw.readFile(totalLogFile)
+ if dataTmp:
+ totalData = json.loads(dataTmp)
+ else:
+ totalData = {"total": 0, "delete": 0,
+ "create": 0, "modify": 0, "move": 0}
+
+ totalData['total'] += 1
+ totalData[eventType] += 1
+ mw.writeFile(totalLogFile, json.dumps(totalData))
+
+ # 设置.user.ini
+ def set_user_ini(self, path, up=0):
+ os.chdir(path)
+ useriniPath = path + '/.user.ini'
+ if os.path.exists(useriniPath):
+ os.system('chattr +i ' + useriniPath)
+ for p1 in os.listdir(path):
+ try:
+ npath = path + '/' + p1
+ if not os.path.isdir(npath):
+ continue
+ useriniPath = npath + '/.user.ini'
+ if os.path.exists(useriniPath):
+ os.system('chattr +i ' + useriniPath)
+ if up < 3:
+ self.set_user_ini(npath, up + 1)
+ except:
+ continue
+ return True
+
+ def unlock(self, path):
+ os.system('chattr -R -i {} &> /dev/null'.format(path))
+ os.system('chattr -R -a {} &> /dev/null'.format(path))
+ self.set_user_ini(path)
+
+ def close(self, close_reload=False):
+ # 解除锁定
+ sites = self.get_sites()
+ print("")
+ print("=" * 60)
+ print("{}Disabling anti tampering, please wait...".format(mw.formatDate()))
+ print("-" * 60)
+ for siteInfo in sites:
+ tip = self._PLUGIN_PATH + '/tips/' + siteInfo['siteName'] + '.pl'
+ if not siteInfo['open'] and not os.path.exists(tip):
+ continue
+ if close_reload and siteInfo['open']:
+ continue
+ if sys.version_info[0] == 2:
+ print("【{}】|-Unlock website[{}]".format(mw.formatDate(), siteInfo['siteName'])),
+ else:
+ os.system("echo -e '{}|-Unlock website[{}]\c'".format(mw.formatDate(), siteInfo['siteName']))
+ #print("【{}】|-解锁网站[{}]".format(mw.format_date(),siteInfo['siteName']),end=" ")
+ self.unlock(siteInfo['path'])
+ if os.path.exists(tip):
+ os.remove(tip)
+ print("\t=> complete")
+ print("-" * 60)
+ print('|-Anti tampering has been turned off')
+ print("=" * 60)
+ print(">>>>>>>>>>END<<<<<<<<<<")
+
+ # 获取网站配置列表
+ def get_sites(self):
+ siteconf = self._PLUGIN_PATH + '/sites.json'
+ d = mw.readFile(siteconf)
+ if not os.path.exists(siteconf) or not d:
+ mw.writeFile(siteconf, "[]")
+ data = json.loads(mw.readFile(siteconf))
+
+ # 处理多余字段开始 >>>>>>>>>>
+ is_write = False
+ rm_keys = ['lock', 'bak_open']
+ for i in data:
+ i_keys = i.keys()
+ if not 'open' in i_keys:
+ i['open'] = False
+ for o in rm_keys:
+ if o in i_keys:
+ if i[o]:
+ i['open'] = True
+ i.pop(o)
+ is_write = True
+ if is_write:
+ mw.writeFile(siteconf, json.dumps(data))
+ # 处理多余字段结束 <<<<<<<<<<<<<
+ return data
+
+ def __enter__(self):
+ self.close()
+
+ def __exit__(self, a, b, c):
+ self.close()
+
+
+def run():
+ # 初始化inotify对像
+ event = MyEventHandler()
+ watchManager = pyinotify.WatchManager()
+ starttime = time.time()
+ mode = pyinotify.IN_CREATE | pyinotify.IN_MOVED_TO
+
+ # 处理网站属性
+ sites = event.get_sites()
+ print("=" * 60)
+ print("{} Starting anti tampering, please wait...".format(mw.formatDate()))
+ print("-" * 60)
+ tip_path = event._PLUGIN_PATH + '/tips/'
+ if not os.path.exists(tip_path):
+ os.makedirs(tip_path)
+ speed_file = event._PLUGIN_PATH + '/speed.pl'
+ for siteInfo in sites:
+ s = time.time()
+ tip = tip_path + siteInfo['siteName'] + '.pl'
+ if not siteInfo['open']:
+ continue
+ if sys.version_info[0] == 2:
+ print("{}|-website[{}]".format(mw.formatDate(),siteInfo['siteName'])),
+ else:
+ os.system("echo -e '{}|-website[{}]\c'".format(mw.formatDate(), siteInfo['siteName']))
+ # print("【{}】|-网站[{}]".format(public.format_date(),siteInfo['siteName']),end=" ")
+ mw.writeFile(speed_file, "Processing website[{}]please wait a moment...".format(
+ siteInfo['siteName']))
+ if not os.path.exists(tip):
+ event.list_DIR(siteInfo['path'], siteInfo)
+ try:
+ watchManager.add_watch(siteInfo['path'], mode, auto_add=True, rec=True)
+ except:
+ print(mw.getTracebackInfo())
+ tout = round(time.time() - s, 2)
+ mw.writeFile(tip, '1')
+ print("\t\t=> Completed, time-consuming {}s".format(tout))
+
+ # 启动服务
+ endtime = round(time.time() - starttime, 2)
+ mw.writeLog('防篡改程序', "The website anti tampering service has been successfully started,[%s]s" % endtime)
+ notifier = pyinotify.Notifier(watchManager, event)
+ print("-" * 60)
+ print('|-Anti tampering service has been started')
+ print("=" * 60)
+ end_tips = ">>>>>>>>>>END<<<<<<<<<<"
+ print(end_tips)
+ mw.writeFile(speed_file, end_tips)
+ notifier.loop()
+
+
+if __name__ == '__main__':
+ if len(sys.argv) > 1:
+ if 'stop' in sys.argv:
+ event = MyEventHandler()
+ event.close()
+ elif 'start' in sys.argv:
+ run()
+ elif 'unlock' in sys.argv:
+ event = MyEventHandler()
+ event.unlock(sys.argv[2])
+ elif 'reload' in sys.argv:
+ event = MyEventHandler()
+ event.close(True)
+ else:
+ print('error')
+ else:
+ run()
diff --git a/plugins/task_manager/ico.png b/plugins/task_manager/ico.png
new file mode 100644
index 000000000..60fa27d66
Binary files /dev/null and b/plugins/task_manager/ico.png differ
diff --git a/plugins/task_manager/index.html b/plugins/task_manager/index.html
new file mode 100755
index 000000000..12445cd4c
--- /dev/null
+++ b/plugins/task_manager/index.html
@@ -0,0 +1,553 @@
+
+
+
+
+
+
+
+
+
+
+ CPU
+ 内存
+ 磁盘
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/task_manager/index.py b/plugins/task_manager/index.py
new file mode 100755
index 000000000..90173fb97
--- /dev/null
+++ b/plugins/task_manager/index.py
@@ -0,0 +1,66 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+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 'task_manager'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+def getArgs():
+ args = sys.argv[3:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ if t.strip() == '':
+ tmp = []
+ else:
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ 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 status():
+ return 'start'
+
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ if func == 'status':
+ print(status())
+ else:
+ print('error')
diff --git a/plugins/task_manager/info.json b/plugins/task_manager/info.json
new file mode 100755
index 000000000..4a9f7c981
--- /dev/null
+++ b/plugins/task_manager/info.json
@@ -0,0 +1,17 @@
+{
+ "sort": 5,
+ "ps": "轻松管理进程、流量监控、启动项、用户、服务、计划任务、会话",
+ "name": "task_manager",
+ "title": "任务管理器",
+ "shell": "install.sh",
+ "versions":"1.0",
+ "tip": "soft",
+ "checks": "server/task_manager",
+ "path": "server/task_manager",
+ "display": 1,
+ "author": "task_manager",
+ "date": "2024-06-01",
+ "home": "task_manager",
+ "type": 0,
+ "pid": "4"
+}
\ No newline at end of file
diff --git a/plugins/task_manager/install.sh b/plugins/task_manager/install.sh
new file mode 100755
index 000000000..896b1af89
--- /dev/null
+++ b/plugins/task_manager/install.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+VERSION=$2
+
+# python3 plugins/task_manager/task_manager_index.py
+# /www/server/mdserver-web/bin/python3 /www/server/mdserver-web/plugins/task_manager/process_network_total.py
+# ps -ef|grep process_network_total| grep -v grep | awk '{print $2}' | xargs kill -9
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+Install_App()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/task_manager
+
+ if [ -f /usr/bin/apt ]; then
+ apt install libpcap-dev -y
+ fi
+
+ if [ -f /usr/bin/yum ]; then
+ yum install libpcap-devel -y
+ fi
+
+ if [ -f /usr/bin/dnf ]; then
+ dnf install libpcap-devel -y
+ fi
+
+ pip3 install pypcap
+
+ echo "$VERSION" > $serverPath/task_manager/version.pl
+ echo "安装任务管理器成功"
+}
+
+Uninstall_App()
+{
+ rm -rf $serverPath/task_manager
+ echo "卸载任务管理器成功"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/task_manager/js/task_manager.js b/plugins/task_manager/js/task_manager.js
new file mode 100644
index 000000000..16eab7e58
--- /dev/null
+++ b/plugins/task_manager/js/task_manager.js
@@ -0,0 +1,1117 @@
+
+function tmPostCallback(method, args, callback, version='1.0'){
+ var req_data = {};
+ req_data['name'] = 'task_manager';
+ req_data['func'] = method;
+ req_data['script']='task_manager_index';
+ args['version'] = version;
+
+ if (typeof(args) == 'string' && args == ''){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+var tab_name = 'p_list';
+var search_val = '';
+var select_pid = undefined; // 当前选中进程的id
+var realProcess = []; // 进程数组
+var originProcess = []; // 当前进程展示的列表
+var wrapPid = {}; // 展开的父进程
+var TaskProcessLayerIndex = ''; // 进程详情弹窗的index
+var canScroll2TaskMangerPossess = true; // 是否可以定位到选中的进程
+
+$('.t-mana .man-menu-sub span').click(function () {
+ $(this).siblings().removeClass("on");
+ tab_name = $(this).attr('class');
+ $(this).addClass("on")
+ // console.log(tab_name);
+ if (tab_name == 'p_resource') {
+ $('.resource-panel').addClass('resource-panel-show')
+ $('.taskdivtable').addClass('divtable-hide')
+ $('.ts-line').addClass('divtable-hide')
+ } else if (tab_name !== 'p_resource on') {
+ $('.resource-panel').removeClass('resource-panel-show')
+ $('.taskdivtable').removeClass('divtable-hide')
+ $('.ts-line').removeClass('divtable-hide')
+ }
+ search_val = $('.search-bar .search_input').val('')
+ get_list_bytab(false);
+});
+
+$('.search-bar .search_input').on({
+ 'keyup': function (e) {
+ if (e.keyCode == 13) {
+ get_list_bytab();
+ }
+ },
+ 'blur': function () {
+ get_list_bytab(true);
+ },
+});
+
+$('.search-bar .glyphicon').click(function (e) {
+ get_list_bytab();
+});
+
+var process_list_s = 0;
+get_process_list();
+
+function setTableHead(data) {
+ $.each(Object.keys(data.meter_head), function (index, item) {
+ if (!$('.' + item).hasClass('disabled')) {
+ if (data.meter_head[item]) {
+ $('.' + item).addClass('active');
+ } else {
+ $('.' + item).removeClass('active');
+ }
+ }
+ });
+
+ $('#change_thead').bind("contextmenu", function () {
+ return false;
+ })
+ $(".plug_menu").bind("contextmenu", function () {
+ return false;
+ })
+ $('#change_thead').mousedown(function (e) {
+ e.preventDefault();
+ if (e.which == 3) { // 1 = 鼠标左键; 2 = 鼠标中键; 3 = 鼠标右键
+ var menu = $('.setting_ul');
+ var offset = $(this).offset();
+ var x = e.pageX - offset.left;
+ var y = e.pageY - offset.top;
+ var width = menu.outerWidth();
+ $(".plug_menu").removeAttr('style');
+ $(".setting_ul").removeClass('undisplay');
+ if ($('#change_thead').width() - x < width) {
+ $(".plug_menu").css({
+ position: 'absolute',
+ left: x - width + 150 + "px",
+ top: y + 50 + "px",
+ }).show();
+ } else {
+ $(".plug_menu").css({
+ position: 'absolute',
+ left: x + width + "px",
+ top: y + 50 + "px",
+ }).show();
+ }
+ }
+ $(".setting_ul").show();
+ });
+}
+
+
+$('.layui-layer').not($('.setting_ul_li')).click(function () {
+ $(".plug_menu").hide()
+ $(".setting_ul").hide();
+});
+var isProcessing = false; // 设置标志
+
+$('.setting_ul_li').off('click').click(function (e) {
+ var that = $(this);
+ e.stopPropagation();
+ // 检查标志
+ if (isProcessing) {
+ return;
+ }
+ if (!$(this).hasClass('disabled')) {
+ isProcessing = true; // 点击事件被触发,设置标志
+ var name = $(this).attr("name");
+ clearInterval(process_list_s);
+
+ tmPostCallback('set_meter_head',{meter_head_name: name}, function(data){
+ isProcessing = false; // 操作完成,清除标志
+ if (!data) {
+ layer.msg('设置失败');
+ }
+ that.toggleClass('active');
+ get_process_list(null, null, false);
+ clearInterval(process_list_s);
+ process_list_s = setInterval(function () {
+ if ($(".t-mana").length == 0) {
+ clearInterval(process_list_s);
+ process_list_s = 0;
+ console.log('进程列表轮询任务已停止');
+ }
+ get_process_list(null, null, true);
+ }, 3000);
+ });
+ }
+});
+
+$('.setting_btn').on('click',function (e) {
+ e.stopPropagation();
+ var offset = $('.man-menu-sub').offset();
+ var x = e.pageX - offset.left;
+ var y = e.pageY - offset.top;
+ var width = $('.man-menu-sub').outerWidth();
+ if ($('.man-menu-sub').width() - x < width) {
+ $(".plug_menu").css({
+ position: 'absolute',
+ right: "14.5px",
+ top:"40px",
+ }).toggle();
+ $('.setting_ul').addClass('undisplay')
+ }
+ $(".setting_ul").toggle();
+});
+
+if (process_list_s === 0) {
+ process_list_s = setInterval(function () {
+ if ($(".t-mana").length == 0) {
+ clearInterval(process_list_s);
+ process_list_s = 0;
+ console.log('进程列表轮询任务已停止');
+ }
+ get_process_list(null, null, true);
+ }, 3000);
+}
+
+function get_list_bytab(isblur) {
+ if (isblur && $('.search-bar .search_input').val() === search_val) {
+ return;
+ }
+ search_val = $('.search-bar .search_input').val();
+ get_process_list();
+
+ switch (tab_name) {
+ case 'p_list':
+ $('.table_config').show();
+ $('.search-bar span').addClass('r56');
+ select_pid = '';
+ wrapPid = {};
+ canScroll2TaskMangerPossess = true;
+ get_process_list();
+ break;
+ case 'p_resource':
+ $('.table_config').hide();
+ $('.search-bar span').removeClass('r56');
+ get_resource_list();
+ break;
+ case 'p_run':
+ $('.table_config').hide();
+ $('.search-bar span').removeClass('r56');
+ get_run_list();
+ break;
+ case 'p_service':
+ $('.table_config').hide();
+ $('.search-bar span').removeClass('r56');
+ get_service_list();
+ break;
+ case 'p_network':
+ $('.table_config').hide();
+ $('.search-bar span').removeClass('r56');
+ get_network_list();
+ break;
+ case 'p_user':
+ $('.table_config').hide();
+ $('.search-bar span').removeClass('r56');
+ get_user_list();
+ break;
+ case 'p_cron':
+ $('.table_config').hide();
+ $('.search-bar span').removeClass('r56');
+ get_cron_list();
+ break;
+ case 'p_session':
+ $('.table_config').hide();
+ $('.search-bar span').removeClass('r56');
+ get_who_list();
+ break;
+ }
+}
+
+function get_process_list(sortx, reverse, rx) {
+ if ($('.t-mana .man-menu-sub .on').attr('class') != 'p_list on') return;
+ var cookie_key = 'task_process_sort';
+ var s_tmp = getCookie(cookie_key);
+ if (sortx == undefined || sortx == null) {
+ if (s_tmp) {
+ sortx_arr = s_tmp.split('|');
+ sortx = sortx_arr[0];
+ reverse = sortx_arr[1];
+ } else {
+ sortx = 'cpu_percent';
+ }
+ }
+
+ res_list = {True: 'False',False: 'True'};
+ setCookie(cookie_key, sortx + '|' + reverse);
+ if (!rx) {
+ var loadT = layer.msg('正在获取进程列表..', {icon: 16, time: 0, shade: [0.3, '#000']})
+ }
+
+ tmPostCallback('get_process_list', {sortx: sortx,reverse: reverse,search:search_val}, function(rdata){
+ // console.log(rdata);
+ if (!rx) layer.close(loadT);
+ if ($('.t-mana .man-menu-sub .on').attr('class') != 'p_list on') return;
+ if (rdata.status === false) {
+ layer.closeAll();
+ layer.msg(rdata.msg, {icon: 2});
+ return;
+ }
+ var data = rdata.data;
+
+ var year = new Date().getFullYear();
+ realProcess = [];
+ originProcess = [];
+ var list = data.process_list;
+ for (var i = 0; i < list.length; i++) {
+ list[i].haschild = false;
+ if (list[i].children && list[i].children.length > 0) {
+ list[i].haschild = true;
+ }
+ originProcess.push(list[i]);
+ realProcess.push(list[i]);
+ }
+ var selectline = buildRealProcess();
+ var tbody_tr = createProcessTable(true, data);
+ var tbody = '\
+ \
+ 应用名称 \
+ PID \
+ 线程 \
+ 用户 \
+ CPU \
+ 内存 \
+ io读 \
+ io写 \
+ 上行 \
+ 下行 \
+ 连接 \
+ 状态 \
+ 操作 \
+ \
+ \
+ ' + tbody_tr + ' ';
+ $("#TaskManagement").html(tbody);
+ var topMsg = '\
+
CPU: ' + data.info.cpu + '%
内存: ' + toSize(data.info.mem) + '
\
+
负载(load average)
' + data.info.load_average[1] + ', ' + data.info.load_average[5] + ', ' + data.info.load_average[15] + '
\
+
进程数: ' + data.process_list.length + '
磁盘: ' + toSize(data.info.disk) + '
\
+
';
+ $("#load_average").html(topMsg).show();
+ $(".pro_" + sortx).append(' ');
+ $(".table-cont").css("height", "500px");
+ scropll2selectPossess(selectline);
+ show_task();
+ setTableHead(data);
+ if(getCookie('table_config_tip')=='false'||!getCookie('table_config_tip')){
+ layer.tips('点击可设置表头', '.setting_btn', {
+ tips: [1, '#20a53a'],
+ time: 3000
+ });
+ setCookie('table_config_tip',true);
+ }
+ // 清除掉之前绑定的滚动事件
+ // $("#table-cont").unbind('scroll');
+ // 重新绑定滚动事件
+ // $('#table-cont').scroll(task_manager_possess_scroll());
+ });
+}
+
+function task_manager_possess_scroll() {
+ var timer = null;
+ var set2selected = null;
+ // 滚动节流
+ return function () {
+ if (timer !== null) return;
+ timer = setTimeout(function () {
+ timer = null;
+ canScroll2TaskMangerPossess = false;
+ if (set2selected !== null) clearTimeout(set2selected)
+ // 最后一次滚动后2秒内不再滚动,才能滚动到选中的行
+ set2selected = setTimeout(function () {
+ canScroll2TaskMangerPossess = true;
+ }, 2000);
+ }, 500);
+ }
+}
+
+function scropll2selectPossess(selectline) {
+ if (canScroll2TaskMangerPossess && selectline !== -1) {
+ var top = $('#table-cont')[0].scrollTop;
+ // if(selectline > 2)
+ if (selectline * 38 > top + 500 || selectline * 38 < top) {
+ $('#table-cont')[0].scrollTo(0, (selectline - 2) * 38, 'smooth');
+ }
+ }
+}
+
+function show_process_child(pid) {
+ wrapPid[pid + ''] = true;
+ // buildRealProcess()
+ // createProcessTable()
+}
+
+function colp_process_child(pid) {
+ // select_pid = pid+'';
+ wrapPid[pid + ''] = false;
+ // buildRealProcess()
+ // createProcessTable()
+}
+
+function click_process_tr(e, pid, fpid) {
+ select_pid = pid + '';
+ if (e.target.innerText === '结束' && fpid) {
+ select_pid = fpid + '';
+ }
+ var selectline = buildRealProcess()
+ createProcessTable()
+ scropll2selectPossess(selectline)
+}
+
+// 构建真实进程列表
+function buildRealProcess() {
+ realProcess = [];
+ var len = originProcess.length;
+ for (var i = 0; i < len; i++) {
+ realProcess.push(originProcess[i]);
+ if (wrapPid[originProcess[i].pid + '']) {
+ // console.log(originProcess[i]);
+ if (!originProcess[i].children) {
+ wrapPid[originProcess[i].pid + ''] = false;
+ continue;
+ }
+ var childSelected = false;
+ for (var j = 0; j < originProcess[i].children.length; j++) {
+ originProcess[i].children[j].fpid = originProcess[i].pid + ''
+ originProcess[i].children[j].ischild = true
+ originProcess[i].children[j].isselect = false
+ if (originProcess[i].pid + '' === select_pid) {
+ originProcess[i].children[j].isselect = true
+ }
+ if (originProcess[i].children[j].pid + '' === select_pid) {
+ var childSelected = true;
+ }
+ realProcess.push(originProcess[i].children[j]);
+ // 显示选中的父子进程
+ }
+ if (childSelected) {
+ for (var k = 0; k <= originProcess[i].children.length; k++) {
+ realProcess.pop()
+ }
+ originProcess[i].isselect = true
+ realProcess.push(originProcess[i])
+ for (var l = 0; l < originProcess[i].children.length; l++) {
+ originProcess[i].children[l].isselect = true
+ realProcess.push(originProcess[i].children[l]);
+ }
+ }
+ }
+ }
+ for (var i = 0; i < realProcess.length; i++) {
+ if (realProcess[i].pid + '' === select_pid) {
+ return i// 返回选中的下标
+ }
+ }
+ return -1
+}
+
+// 生成进程表格内容
+function createProcessTable(getboday, data) {
+ var tbody_tr = '';
+ for (var i = 0; i < realProcess.length; i++) {
+ if (realProcess[i].status == '活动') realProcess[i].status = '活动 ';
+ var colp = realProcess[i].haschild ? '\
+ \
+ ' : '';
+ if (wrapPid[realProcess[i].pid + '']) {
+ colp = '\
+ \
+ ';
+ }
+ var childNums = realProcess[i].haschild ? '(' + realProcess[i].children.length + ') ' : '';
+ var namewidth = "max-width:120px;"
+ if (realProcess[i].ischild) {
+ namewidth = "max-width:80px;"
+ }
+ if (realProcess[i].haschild) {
+ namewidth = "max-width:100px;"
+ }
+ var processName = '' + realProcess[i].ps + ' ';
+ var isProcessChild = realProcess[i].ischild ? 'process-child' : '';
+ var childStyle = realProcess[i].ischild ? 'width:100px' : '';
+ var selected = realProcess[i].pid + '' === select_pid || realProcess[i].isselect ? 'class="process-select"' : '';
+ var selected_one = realProcess[i].pid + '' === select_pid ? 'style="background-color:#F6F6F6;"' : '';
+ var kill_process = realProcess[i].haschild ? 'kill_process_all' : 'kill_process';
+
+ var tbody_td = '';
+ if ('io_read_bytes' in realProcess[i]){
+ tbody_td += '' + toSize(realProcess[i].io_read_speed).replace(' ', '') + ' ';
+ tbody_td += '' + toSize(realProcess[i].io_write_speed).replace(' ', '') + ' ';
+
+ tbody_td += '' + toSize(realProcess[i].up).replace(' ', '') + ' ';
+ tbody_td += '' + toSize(realProcess[i].down).replace(' ', '') + ' ';
+ }
+
+ tbody_tr += '\
+ \
+ ' + colp + '\
+ \
+ ' + processName + '\
+ ' + childNums + '\
+ \
+ ' + realProcess[i].pid + ' \
+ ' + realProcess[i].threads + ' \
+ ' + realProcess[i].user + ' \
+ ' + realProcess[i].cpu_percent + '% \
+ ' + toSize(realProcess[i].memory_used).replace(' ', '') + ' \
+ '+tbody_td+'\
+ ' + realProcess[i].connects + ' \
+ ' + realProcess[i].status + ' \
+ 结束 \
+ ';
+ }
+ if (getboday) return tbody_tr;
+ $("#TaskManagement tbody").html(tbody_tr);
+}
+
+// 获取资源列表
+function get_resource_list() {
+ // console.log('get_resource_list----------');
+ var url = '/plugin?action=a&name=task_manager&s=cpu_status'
+ // url = 'plugin?action=a&name=task_manager&s=get_disk_staus'
+
+ var res_list = {
+ True: 'False',
+ False: 'True'
+ }
+ var reverse = 'True'
+ $.post(url, function (data) {
+ console.log(data);
+ realProcess = []
+ originProcess = []
+ var list = data.process_list
+ for (var i = 0; i < list.length; i++) {
+ list[i].haschild = false
+ if (list[i].children && list[i].children.length > 0) {
+ list[i].haschild = true
+ }
+ originProcess.push(list[i])
+ realProcess.push(list[i])
+ }
+ // console.log('realllll');
+
+ buildRealProcess()
+ var tbody_tr = createProcessTable(true);
+ var tbody = '\
+ \
+ 应用名称 \
+ PID \
+ 线程 \
+ 用户 \
+ CPU \
+ 内存 \
+ io读 \
+ io写 \
+ 上行 \
+ 下行 \
+ 连接 \
+ 状态 \
+ 操作 \
+ \
+ \
+ ' + tbody_tr + ' ';
+ $('#taskResourceTable').html(tbody);
+ $(".table-cont").css("height", "220px");
+ })
+
+ $("#load_average").html('')
+}
+
+//查看计划任务列表
+function get_cron_list() {
+ var loadT = layer.msg('获取计划任务列表..', {icon: 16, time: 0, shade: [0.3, '#000']});
+ tmPostCallback('get_cron_list', {search:search_val}, function(rdata){
+ layer.close(loadT);
+
+ var rdata = rdata.data;
+ var tbody_tr = '';
+ for (var i = 0; i < rdata.length; i++) {
+ tbody_tr += '\
+ ' + rdata[i].cycle + ' \
+ ' + rdata[i].exe + ' \
+ ' + rdata[i].ps + ' \
+ 删除 \
+ ';
+ }
+ var tbody = '\
+ \
+ 周期 \
+ 执行 \
+ 描述 \
+ 操作 \
+ \
+ \
+ ' + tbody_tr + ' ';
+ $("#TaskManagement").html(tbody);
+ var topMsg = '';
+ $("#load_average").html(topMsg).hide();
+ $(".table-cont").css("height", "597px");
+ show_task();
+ });
+}
+
+
+//查看网络状态
+function get_network_list(rflush) {
+ var loadT = layer.msg(lan.public.the_get, {icon: 16, time: 0, shade: [0.3, '#000']});
+ tmPostCallback('get_network_list', {search:search_val}, function(rdata){
+ layer.close(loadT);
+ if (rdata.data['is_mac']){
+ tbody_tr += "mac无法使用 ";
+ tbody = "\
+ \
+ " + lan.index.net_protocol + " \
+ " + lan.index.net_address_dst + " \
+ " + lan.index.net_address_src + " \
+ " + lan.index.net_address_status + " \
+ " + lan.index.net_process + " \
+ " + lan.index.net_process_pid + " \
+ \
+ \
+ " + tbody_tr + " ";
+ $("#TaskManagement").html(tbody);
+ show_task();
+ return;
+ }
+
+ var rdata = rdata.data;
+
+ var tbody_tr = "";
+ for (var i = 0; i < rdata.list.length; i++) {
+ tbody_tr += ""
+ + "" + rdata.list[i].type + " "
+ + "" + rdata.list[i].laddr[0] + ":" + rdata.list[i].laddr[1] + " "
+ + "" + (rdata.list[i].raddr.length > 1 ? "" + rdata.list[i].raddr[0] + " :" + rdata.list[i].raddr[1] : 'NONE') + " "
+ + "" + rdata.list[i].status + " "
+ + "" + rdata.list[i].process + " "
+ + "" + rdata.list[i].pid + " "
+ + " ";
+ }
+
+ tbody = "\
+ \
+ " + lan.index.net_protocol + " \
+ " + lan.index.net_address_dst + " \
+ " + lan.index.net_address_src + " \
+ " + lan.index.net_address_status + " \
+ " + lan.index.net_process + " \
+ " + lan.index.net_process_pid + " \
+ \
+ \
+ " + tbody_tr + " ";
+
+ $("#TaskManagement").html(tbody);
+ var topMsg = '\
+
\
+
总发送: ' + toSize(rdata.state.upTotal) + '
\
+
总接收: ' + toSize(rdata.state.downTotal) + '
\
+
\
+
\
+
上行: ' + toSize(rdata.state.up) + '
\
+
下行: ' + toSize(rdata.state.down) + '
\
+
\
+
\
+
总发包: ' + to_max(rdata.state.upPackets) + '
\
+
总收包: ' + to_max(rdata.state.downPackets) + '
\
+
\
+
\
+
包发送/秒: ' + to_max(rdata.state.upPackets_s) + '
\
+
包接收/秒: ' + to_max(rdata.state.downPackets_s) + '
\
+
\
+
';
+ $("#load_average").html(topMsg).show();
+ $(".table-cont").css("height", "500px");
+ show_task();
+ });
+}
+
+function to_max(num) {
+ if (num > 10000) {
+ num = num / 10000;
+ if (num > 10000) {
+ num = num / 10000;
+ return num.toFixed(5) + ' 亿';
+ }
+ return num.toFixed(5) + ' 万';
+ }
+ return num;
+}
+
+//获取会话列表
+function get_who_list() {
+ var loadT = layer.msg('正在获取用户会话列表..', {icon: 16, time: 0, shade: [0.3, '#000']});
+ tmPostCallback('get_who', {search:search_val}, function(data){
+ layer.close(loadT);
+ var rdata = data.data;
+ var tbody_tr = '';
+ for (var i = 0; i < rdata.length; i++) {
+ tbody_tr += '\
+ ' + rdata[i].user + ' \
+ ' + rdata[i].pts + ' \
+ ' + rdata[i].ip + ' \
+ ' + rdata[i].date + ' \
+ 强制断开 \
+ ';
+ }
+ var tbody = '\
+ \
+ 用户 \
+ PTS \
+ 登陆IP \
+ 登陆时间 \
+ 操作 \
+ \
+ \
+ ' + tbody_tr + ' ';
+ $("#TaskManagement").html(tbody);
+ var topMsg = '';
+ $("#load_average").html(topMsg).hide();
+ $(".table-cont").css("height", "597px");
+ show_task();
+ });
+}
+
+//获取启动列表
+function get_run_list() {
+ var loadT = layer.msg('正在获取启动项列表..', {icon: 16, time: 0, shade: [0.3, '#000']});
+ tmPostCallback('get_run_list', {search:search_val}, function(rdata){
+ layer.close(loadT);
+ if (rdata.data['is_mac']){
+ tbody_tr += "mac无法使用 ";
+ var tbody = '\
+ \
+ 名称 \
+ 启动路径 \
+ 文件大小 \
+ 文件权限 \
+ 描述 \
+ 操作 \
+ \
+ \
+ ' + tbody_tr + ' ';
+ $("#TaskManagement").html(tbody);
+ return;
+ }
+
+ var rdata = rdata.data;
+ var tbody_tr = '';
+ for (var i = 0; i < rdata.run_list.length; i++) {
+ tbody_tr += '\
+ ' + rdata.run_list[i].name + ' \
+ ' + rdata.run_list[i].srcfile + ' \
+ ' + toSize(rdata.run_list[i].size) + ' \
+ ' + rdata.run_list[i].access + ' \
+ ' + rdata.run_list[i].ps + ' \
+ 编辑 \
+ ';
+ }
+ var tbody = '\
+ \
+ 名称 \
+ 启动路径 \
+ 文件大小 \
+ 文件权限 \
+ 描述 \
+ 操作 \
+ \
+ \
+ ' + tbody_tr + ' ';
+ $("#TaskManagement").html(tbody);
+ var topMsg = '当前运行级别: level-' + rdata.run_level + '
';
+ $("#load_average").html(topMsg).show();
+ $(".table-cont").css("height", "500px");
+ show_task();
+ });
+}
+
+//获取服务列表
+function get_service_list() {
+ var loadT = layer.msg('正在获取服务列表..', {icon: 16, time: 0, shade: [0.3, '#000']});
+ tmPostCallback('get_service_list', {search:search_val}, function(rdata){
+ layer.close(loadT);
+ if (rdata.data['is_mac']){
+ tbody_tr += "mac无法使用 ";
+ var tbody = '\
+ \
+ 名称 \
+ Level-0 \
+ Level-1 \
+ Level-2 \
+ Level-3 \
+ Level-4 \
+ Level-5 \
+ Level-6 \
+ 描述 \
+ 操作 \
+ \
+ \
+ ' + tbody_tr + ' ';
+ $("#TaskManagement").html(tbody);
+ return;
+ }
+
+ var rdata = rdata.data;
+
+ var tbody_tr = '';
+ for (var i = 0; i < rdata.serviceList.length; i++) {
+ tbody_tr += '\
+ ' + rdata.serviceList[i].name + ' \
+ ' + rdata.serviceList[i].runlevel_0 + ' \
+ ' + rdata.serviceList[i].runlevel_1 + ' \
+ ' + rdata.serviceList[i].runlevel_2 + ' \
+ ' + rdata.serviceList[i].runlevel_3 + ' \
+ ' + rdata.serviceList[i].runlevel_4 + ' \
+ ' + rdata.serviceList[i].runlevel_5 + ' \
+ ' + rdata.serviceList[i].runlevel_6 + ' \
+ ' + rdata.serviceList[i].ps + ' \
+ 删除 \
+ ';
+ }
+ var tbody = '\
+ \
+ 名称 \
+ Level-0 \
+ Level-1 \
+ Level-2 \
+ Level-3 \
+ Level-4 \
+ Level-5 \
+ Level-6 \
+ 描述 \
+ 操作 \
+ \
+ \
+ ' + tbody_tr + ' ';
+ $("#TaskManagement").html(tbody);
+ var topMsg = '当前运行级别: level-' + rdata.runlevel + '
';
+ $("#load_average").html(topMsg).show();
+ $(".table-cont").css("height", "500px");
+ show_task();
+ });
+}
+
+//取用户列表
+function get_user_list() {
+ var loadT = layer.msg('正在获取用户列表..', {icon: 16, time: 0, shade: [0.3, '#000']});
+ tmPostCallback('get_user_list', {search:search_val}, function(data){
+ layer.close(loadT);
+ var rdata = data.data;
+ var tbody_tr = '';
+ for (var i = 0; i < rdata.length; i++) {
+ tbody_tr += '\
+ ' + rdata[i].username + ' \
+ ' + rdata[i].home + ' \
+ ' + rdata[i].group + ' \
+ ' + rdata[i].uid + ' \
+ ' + rdata[i].gid + ' \
+ ' + rdata[i].login_shell + ' \
+ ' + rdata[i].ps + ' \
+ 删除 \
+ ';
+ }
+ var tbody = '\
+ \
+ 用户名 \
+ home \
+ 用户组 \
+ uid \
+ gid \
+ 登陆脚本 \
+ 描述 \
+ 操作 \
+ \
+ \
+ ' + tbody_tr + ' ';
+ $("#TaskManagement").html(tbody);
+ var topMsg = '';
+ $("#load_average").html(topMsg).hide();
+ $(".table-cont").css("height", "597px");
+ show_task();
+ });
+}
+
+//删除用户
+function userdel(user) {
+ safeMessage('删除用户【' + user + '】', '删除后可能导致您的环境无法正常运行,继续吗?', function () {
+ var loadT = layer.msg('正在删除用户[' + user + ']..', {icon: 16, time: 0, shade: [0.3, '#000']});
+ tmPostCallback('remove_user', {user:user}, function(rdata){
+ layer.close(loadT);
+ var rdata = rdata.data;
+ showMsg(rdata.msg, function(){
+ if (rdata.status) {
+ get_user_list();
+ }
+ },{icon: rdata.status ? 1 : 2});
+ });
+ });
+}
+
+//结束进程
+function kill_process(pid, fpid) {
+ if (fpid) {
+ select_pid = fpid;
+ }
+ var w = layer.confirm('您是否要结束 (' + pid + ') 进程?', {
+ btn: ['结束', '取消'], //按钮
+ title: '结束' + pid,
+ closeBtn: 2
+ }, function () {
+ var loadT = layer.msg('正在结束进程[' + pid + ']..', {icon: 16, time: 0, shade: [0.3, '#000']});
+ tmPostCallback('kill_process', {pid:pid}, function(data){
+ layer.close(loadT);
+ var rdata = data.data;
+ layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
+ if (rdata.status) {
+ get_process_list();
+ }
+ });
+ }, function () {
+ layer.close(w);
+ })
+}
+
+//结束进程树
+function kill_process_all(pid) {
+ var w = layer.confirm('您是否要结束 (' + pid + ') 进程?', {
+ btn: ['结束', '取消'], //按钮
+ title: '结束' + pid,
+ closeBtn: 2
+ }, function () {
+ var loadT = layer.msg('正在结束父进程[' + pid + ']..', {icon: 16, time: 0, shade: [0.3, '#000']});
+ tmPostCallback('kill_process_all', {pid:pid}, function(data){
+ layer.close(loadT);
+ var rdata = data.data;
+ showMsg(rdata.msg, function(){
+ if (rdata.status) {
+ get_process_list();
+ }
+ },{icon: rdata.status ? 1 : 2});
+ });
+ }, function () {
+ layer.close(w);
+ });
+}
+
+//打开文件所在位置
+function open_path(path) {
+ var tmp = path.split('/');
+ tmp[tmp.length - 1] = '';
+ var path = '/' + tmp.join('/');
+ openPath(path);
+}
+
+//删除服务
+function remove_service(serviceName) {
+ safeMessage('删除服务【' + serviceName + '】', '删除后可能导致您的环境无法正常运行,继续吗?', function () {
+ var loadT = layer.msg('正在删除服务[' + serviceName + ']..', {icon: 16, time: 0, shade: [0.3, '#000']});
+ tmPostCallback('remove_service', {serviceName:serviceName}, function(data){
+ var rdata = data.data;
+ layer.close(loadT);
+ showMsg(rdata.msg, function(){
+ if (rdata.status){
+ get_service_list();
+ }
+ },{icon: rdata.status ? 1 : 2})
+ });
+ });
+}
+
+//在线编辑文件
+function online_edit_file(fileName) {
+ onlineEditFile(0, fileName);
+}
+
+//删除计划任务
+function remove_cron(index) {
+ safeMessage('删除计划任务[' + index + ']', '删除后将无法恢复,继续吗?', function () {
+ var loadT = layer.msg('正在删除计划任务..', {icon: 16, time: 0, shade: [0.3, '#000']});
+ tmPostCallback('remove_cron', {index:index}, function(rdata){
+ layer.close(loadT);
+ var rdata = rdata.data;
+ showMsg(rdata.msg, function(){
+ if (rdata.status) {
+ get_cron_list();
+ }
+ },{icon: rdata.status ? 1 : 2});
+ });
+ });
+}
+
+//强制断开会话
+function pkill_session(pts) {
+ safeMessage('强制断开会话[' + pts + ']', '强制断开此会话吗?', function () {
+ var loadT = layer.msg('正在断开会话..', {icon: 16, time: 0, shade: [0.3, '#000']});
+ tmPostCallback('pkill_session', {pts:pts}, function(data){
+ layer.close(loadT);
+
+ var rdata = data.data;
+ layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
+ if (rdata.status){
+ get_who_list();
+ }
+ });
+ });
+}
+
+//设置服务启动级别状态
+function set_runlevel_state(runlevel, serviceName) {
+ var loadT = layer.msg('正在设置服务[' + serviceName + ']..', {icon: 16, time: 0, shade: [0.3, '#000']});
+ tmPostCallback('set_runlevel_state', {runlevel: runlevel,serviceName: serviceName}, function(data){
+ layer.close(loadT);
+ var rdata = data.data;
+ showMsg(rdata.msg, function(){
+ if (rdata.status) {
+ get_service_list();
+ }
+ },{icon: rdata.status ? 1 : 2});
+ });
+}
+
+//查看进程详情
+function get_process_info(pid) {
+ var loadT = layer.msg('正在获取进程信息[' + pid + ']..', {icon: 16, time: 0, shade: [0.3, '#000']});
+ tmPostCallback('get_process_info', {pid:pid}, function(data){
+ layer.close(loadT);
+ var rdata = data.data;
+ fileBody = '';
+ for (var i = 0; i < rdata.open_files.length; i++) {
+ fileBody += '' + rdata.open_files[i].path + ' \
+ ' + rdata.open_files[i].mode + ' \
+ ' + rdata.open_files[i].position + ' \
+ ' + rdata.open_files[i].flags + ' \
+ ' + rdata.open_files[i].fd + ' \
+ ';
+ }
+ var cbody = '\
+
\
+
\
+ \
+ \
+ 名称 ' + rdata.name + ' \
+ PID ' + rdata.pid + ' \
+ 状态 ' + rdata.status + ' \
+ \
+ \
+ 父进程 ' + rdata.pname + '(' + rdata.ppid + ') \
+ 用户 ' + rdata.user + ' \
+ 线程 ' + rdata.threads + ' \
+ \
+ \
+ Socket ' + rdata.connects + ' \
+ io读 ' + toSize(rdata.io_read_bytes) + ' \
+ io写 ' + toSize(rdata.io_write_bytes) + ' \
+ \
+ \
+ 启动时间 ' + getLocalTime(rdata.create_time) + ' \
+ 描述 ' + rdata.ps + ' \
+ \
+ \
+ 启动命令 ' + rdata.comline.join(" ") + ' \
+ \
+ \
+
\
+
\
+
内存 \
+
\
+
\
+ \
+ \
+ rss ' + toSize(rdata.memory_full.rss) + ' \
+ pss ' + toSize(rdata.memory_full.pss) + ' \
+ uss ' + toSize(rdata.memory_full.uss) + ' \
+ \
+ \
+ vms ' + toSize(rdata.memory_full.vms) + ' \
+ swap ' + toSize(rdata.memory_full.swap) + ' \
+ shared ' + toSize(rdata.memory_full.shared) + ' \
+ \
+ \
+ data ' + toSize(rdata.memory_full.data) + ' \
+ text ' + toSize(rdata.memory_full.text) + ' \
+ dirty ' + toSize(rdata.memory_full.dirty) + ' \
+ \
+ \
+
\
+
\
+
打开的文件列表 \
+
\
+
\
+
\
+ \
+ \
+ 文件 \
+ mode \
+ position \
+ flags \
+ fd \
+ \
+ \
+ ' + fileBody + ' \
+
\
+
\
+
\
+
\
+ \
+ 打开文件位置 \
+ 结束进程树 \
+
';
+
+ TaskProcessLayerIndex = layer.open({
+ type: 1,
+ title: '进程属性[' + rdata.name + '] -- ' + rdata.exe,
+ area: '750px',
+ closeBtn: 2,
+ shadeClose: false,
+ content: cbody
+ });
+ show_jc_flie();
+ });
+}
+
+//屏蔽指定IP
+function dropAddress(address) {
+ layer.confirm(lan.index.net_doup_ip_msg, {icon: 3, closeBtn: 2}, function () {
+ loadT = layer.msg(lan.index.net_doup_ip_to, {icon: 16, time: 0, shade: [0.3, '#000']});
+ $.post('/firewall/add_drop_address', 'type=address&protocol=tcp&port=' + address + '&ps=手动屏蔽', function (rdata) {
+ layer.close(loadT);
+ layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
+ });
+ });
+}
+
+function show_task() {
+ $(".ts-line").width($("#TaskManagement").width());
+ $("#TaskManagement tbody td").click(function () {
+ // console.log('---');
+ $(this).parents("tr").addClass("active").siblings().removeClass("active");
+ });
+ var tableCont = document.querySelector('#table-cont');
+ //表格固定
+ var sct = tableCont.scrollTop;
+ tableCont.querySelector('thead').style.transform = 'translateY(' + sct + 'px)';
+ tableCont.addEventListener('scroll', scrollHandle);
+}
+
+function show_jc_flie() {
+ var tableJc = document.querySelector('#jc-file-table');
+ //文件表格固定
+ tableJc.addEventListener('scroll', scrollHandle);
+}
+
+function scrollHandle(e) {
+ var scrollTop = this.scrollTop;
+ this.querySelector('thead').style.transform = 'translateY(' + scrollTop + 'px)';
+}
\ No newline at end of file
diff --git a/plugins/task_manager/process_network_total.py b/plugins/task_manager/process_network_total.py
new file mode 100644
index 000000000..d941b64e2
--- /dev/null
+++ b/plugins/task_manager/process_network_total.py
@@ -0,0 +1,230 @@
+#coding: utf-8
+
+import sys
+import time
+import os
+import struct
+
+os.chdir('/www/server/mdserver-web')
+if 'class/' in sys.path: sys.path.insert(0,"class/")
+import copy
+try:
+ import pcap
+except ImportError:
+ if os.path.exists('/usr/bin/apt'):
+ os.system("apt install libpcap-dev -y")
+ elif os.path.exists('/usr/bin/dnf'):
+ red_file = '/etc/redhat-release'
+ if os.path.exists(red_file):
+ f = open(red_file,'r')
+ red_body = f.read()
+ f.close()
+ if red_body.find('CentOS Linux release 8.') != -1:
+ rpm_file = '/root/libpcap-1.9.1.rpm'
+ down_url = "wget -O {} https://repo.almalinux.org/almalinux/8/PowerTools/x86_64/os/Packages/libpcap-devel-1.9.1-5.el8.x86_64.rpm --no-check-certificate -T 10".format(rpm_file)
+ print(down_url)
+ os.system(down_url)
+ os.system("rpm -ivh {}".format(rpm_file))
+ if os.path.exists(rpm_file): os.remove(rpm_file)
+ else:
+ os.system("dnf install libpcap-devel -y")
+ else:
+ os.system("dnf install libpcap-devel -y")
+ elif os.path.exists('/usr/bin/yum'):
+ os.system("yum install libpcap-devel -y")
+
+ os.system("pip install pypcap")
+ try:
+ import pcap
+ except ImportError:
+ print("pypcap module install failed.")
+ sys.exit()
+
+class process_network_total:
+ __pid_file = 'logs/process_network_total.pid'
+ __inode_list = {}
+ __net_process_list = {}
+ __net_process_size = {}
+ __last_stat = 0
+ __last_write_time = 0
+ __end_time = 0
+
+ def start(self,timeout = 0):
+ stime = time.time()
+ self.__end_time = timeout + stime
+ self.__last_stat = stime
+ try:
+ p = pcap.pcap() # 监听所有网卡
+ p.setfilter('tcp') # 只监听TCP数据包
+ for p_time,p_data in p:
+ self.handle_packet(p_data)
+ # 过期停止监听
+ if timeout > 0:
+ if p_time > self.__end_time:
+ self.rm_pid_file()
+ break
+
+ except:
+ self.rm_pid_file()
+
+ def handle_packet(self, pcap_data):
+ # 获取IP协议头
+ ip_header = pcap_data[14:34]
+ # 解析src/dst地址
+ src_ip = ip_header[12:16]
+ dst_ip = ip_header[16:20]
+ # 解析sport/dport端口
+ src_port = pcap_data[34:36]
+ dst_port = pcap_data[36:38]
+
+ src = src_ip + b':' + src_port
+ dst = dst_ip + b':' + dst_port
+ # 计算数据包长度
+ pack_size = len(pcap_data)
+ # 统计进程流量
+ self.total_net_process(dst,src,pack_size)
+
+ def total_net_process(self,dst,src,pack_size):
+ self.get_tcp_stat()
+ direction = None
+ mtime = time.time()
+ if dst in self.__net_process_list:
+ pid = self.__net_process_list[dst]
+ direction = 'down'
+ elif src in self.__net_process_list:
+ pid = self.__net_process_list[src]
+ direction = 'up'
+ else:
+ if mtime - self.__last_stat > 3:
+ self.__last_stat = mtime
+ self.get_tcp_stat(True)
+ if dst in self.__net_process_list:
+ pid = self.__net_process_list[dst]
+ direction = 'down'
+ elif src in self.__net_process_list:
+ pid = self.__net_process_list[src]
+ direction = 'up'
+
+ if not direction: return False
+ if not pid: return False
+ if not pid in self.__net_process_size:
+ self.__net_process_size[pid] = {}
+ self.__net_process_size[pid]['down'] = 0
+ self.__net_process_size[pid]['up'] = 0
+ self.__net_process_size[pid]['up_package'] = 0
+ self.__net_process_size[pid]['down_package'] = 0
+
+ self.__net_process_size[pid][direction] += pack_size
+ self.__net_process_size[pid][direction + '_package'] += 1
+
+ # 写入到文件
+ if mtime - self.__last_write_time > 1:
+ self.__last_write_time = mtime
+ self.write_net_process()
+
+ def write_net_process(self):
+ w_file = '/dev/shm/mw_net_process'
+ process_size = copy.deepcopy(self.__net_process_size)
+ net_process = []
+ for pid in process_size.keys():
+ net_process.append(str(pid) + " " + str(process_size[pid]['down']) + " " + str(process_size[pid]['up']) + " " + str(process_size[pid]['down_package']) + " " + str(process_size[pid]['up_package']))
+
+ f = open(w_file,'w+',encoding='utf-8')
+ f.write('\n'.join(net_process))
+ f.close()
+
+ def hex_to_ip(self, hex_ip):
+ hex_ip,hex_port = hex_ip.split(':')
+ ip = '.'.join([str(int(hex_ip[i:i+2], 16)) for i in range(0, len(hex_ip), 2)][::-1])
+ port = int(hex_port, 16)
+ return ip,port
+
+ def get_tcp_stat(self,force = False):
+ if not force and self.__net_process_list: return self.__net_process_list
+ self.__net_process_list = {}
+ tcp_stat_file = '/proc/net/tcp'
+ tcp_stat = open(tcp_stat_file, 'rb')
+ tcp_stat_list = tcp_stat.read().decode('utf-8').split('\n')
+ tcp_stat.close()
+ tcp_stat_list = tcp_stat_list[1:]
+ if force: self.get_process_inodes(force)
+ for i in tcp_stat_list:
+ tcp_tmp = i.split()
+ if len(tcp_tmp) < 10: continue
+ inode = tcp_tmp[9]
+ if inode == '0': continue
+ local_ip,local_port = self.hex_to_ip(tcp_tmp[1])
+ if local_ip == '127.0.0.1': continue
+ remote_ip,remote_port = self.hex_to_ip(tcp_tmp[2])
+ if local_ip == remote_ip: continue
+ if remote_ip == '0.0.0.0': continue
+
+ pid = self.inode_to_pid(inode,force)
+ if not pid: continue
+
+ key = self.get_ip_pack(local_ip) + b':' + self.get_port_pack(local_port)
+ self.__net_process_list[key] = pid
+ return self.__net_process_list
+
+
+ def get_port_pack(self,port):
+ return struct.pack('H',int(port))[::-1]
+
+ def get_ip_pack(self,ip):
+ ip_arr = ip.split('.')
+ ip_pack = b''
+ for i in ip_arr:
+ ip_pack += struct.pack('B',int(i))
+ return ip_pack
+
+ def inode_to_pid(self,inode,force = False):
+ inode_list = self.get_process_inodes()
+ if inode in inode_list:
+ return inode_list[inode]
+ return None
+
+ def get_process_inodes(self,force = False):
+ if not force and self.__inode_list: return self.__inode_list
+ proc_path = '/proc'
+ inode_list = {}
+ for pid in os.listdir(proc_path):
+ try:
+ if not pid.isdigit(): continue
+ inode_path = proc_path + '/' + pid + '/fd'
+ for fd in os.listdir(inode_path):
+ try:
+ fd_file = inode_path + '/' + fd
+ fd_link = os.readlink(fd_file)
+ if fd_link.startswith('socket:['):
+ inode = fd_link[8:-1]
+ inode_list[inode] = pid
+ except:
+ continue
+ except:
+ continue
+ self.__inode_list = inode_list
+ return inode_list
+
+ def get_process_name(self,pid):
+ pid_path = '/proc/' + pid + '/comm'
+ if not os.path.exists(pid_path): return ''
+ pid_file = open(pid_path, 'rb')
+ pid_name = pid_file.read().decode('utf-8').strip()
+ pid_file.close()
+ return pid_name
+
+
+ def write_pid(self):
+ self_pid = os.getpid()
+ pid_file = open(self.__pid_file,'w')
+ pid_file.write(str(self_pid))
+ pid_file.close()
+
+ def rm_pid_file(self):
+ if os.path.exists(self.__pid_file):
+ os.remove(self.__pid_file)
+
+if __name__ == '__main__':
+ p = process_network_total()
+ p.write_pid()
+ p.start(600)
\ No newline at end of file
diff --git a/plugins/task_manager/task_manager_index.py b/plugins/task_manager/task_manager_index.py
new file mode 100755
index 000000000..22a44fb91
--- /dev/null
+++ b/plugins/task_manager/task_manager_index.py
@@ -0,0 +1,1641 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import threading
+import psutil
+import json
+
+from typing import List, Dict
+
+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 'task_manager'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+class mainClass(object):
+
+ pids = None
+
+ new_info = {}
+ old_info = {}
+ new_net_info = {}
+ old_net_info = {}
+ panel_pid = None
+ task_pid = None
+ __process_net_list = {}
+ __cpu_time = None
+
+ meter_head = {}
+
+ last_net_process = None
+ last_net_process_time = 0
+
+ # lock
+ _instance_lock = threading.Lock()
+ is_mac = False
+
+ def __init__(self):
+ # print("__init__")
+ self.is_mac = mw.isAppleSystem()
+ self.old_path = getServerDir()+'/task_old.json'
+ self.old_net_path = getServerDir()+'/network_old.json'
+
+ self.old_info['cpu_time'] = self.get_cpu_time()
+ self.old_info['time'] = time.time()
+
+ @classmethod
+ def instance(cls, *args, **kwargs):
+ if not hasattr(mainClass, "_instance"):
+ with mainClass._instance_lock:
+ if not hasattr(mainClass, "_instance"):
+ mainClass._instance = mainClass(*args, **kwargs)
+ return mainClass._instance
+
+
+ def get_process_net_list(self):
+ import copy
+ w_file = '/dev/shm/mw_net_process'
+ if not os.path.exists(w_file): return
+ net_process_body = mw.readFile(w_file)
+ if not net_process_body: return
+ net_process = net_process_body.split('\n')
+ for np in net_process:
+ if not np: continue
+ tmp = {}
+ np_list = np.split()
+ if len(np_list) < 5: continue
+ tmp['pid'] = int(np_list[0])
+ tmp['down'] = int(np_list[1])
+ tmp['up'] = int(np_list[2])
+ tmp['down_package'] = int(np_list[3])
+ tmp['up_package'] = int(np_list[4])
+ self.__process_net_list[tmp['pid']] = tmp
+
+ if time.time() - self.last_net_process_time > 12 or self.last_net_process_time == 0:
+ self.last_net_process = copy.deepcopy(self.__process_net_list)
+ self.last_net_process_time = time.time()
+
+ # 获取进程连接数
+ def get_connects(self, pid):
+ connects = 0
+ try:
+ if pid == 1: return connects
+ tp = '/proc/' + str(pid) + '/fd/'
+ if not os.path.exists(tp): return connects
+ for d in os.listdir(tp):
+ fname = tp + d
+ if os.path.islink(fname):
+ l = os.readlink(fname)
+ if l.find('socket:') != -1: connects += 1
+ except:
+ pass
+ return connects
+
+ # 获取进程io写
+ def get_io_write(self, pid, io_write):
+ disk_io_write = 0
+ if not self.old_info: self.old_info = {}
+ if not pid in self.old_info:
+ self.old_info[pid] = {}
+ self.old_info[pid]['io_write'] = io_write
+ return disk_io_write
+
+ if not 'io_write' in self.old_info[pid]:
+ self.old_info[pid]['io_write'] = io_write
+ return disk_io_write
+
+ io_end = (io_write - self.old_info[pid]['io_write'])
+ if io_end > 0:
+ disk_io_write = io_end / (time.time() - self.old_info['time'])
+ self.old_info[pid]['io_write'] = io_write
+ if disk_io_write > 0: return int(disk_io_write)
+ return 0
+
+ # 获取io读
+ def get_io_read(self, pid, io_read):
+ disk_io_read = 0
+ if not pid in self.old_info:
+ self.old_info[pid] = {}
+ self.old_info[pid]['io_read'] = io_read
+ return disk_io_read
+
+ if not 'io_read' in self.old_info[pid]:
+ self.old_info[pid]['io_read'] = io_read
+ return disk_io_read
+
+ io_end = (io_read - self.old_info[pid]['io_read'])
+ if io_end > 0:
+ disk_io_read = io_end / (time.time() - self.old_info['time'])
+ self.old_info[pid]['io_read'] = io_read
+ if disk_io_read > 0: return int(disk_io_read)
+ return 0
+
+ def get_mem_info(self, get=None):
+ mem = psutil.virtual_memory()
+ if self.is_mac:
+ memInfo = {'memTotal': mem.total}
+ memInfo['memRealUsed'] = memInfo['memTotal'] * (mem.percent / 100)
+ return memInfo['memRealUsed']
+
+ memInfo = {'memTotal': mem.total, 'memFree': mem.free, 'memBuffers': mem.buffers, 'memCached': mem.cached}
+ memInfo['memRealUsed'] = memInfo['memTotal'] - memInfo['memFree'] - memInfo['memBuffers'] - memInfo['memCached']
+ return memInfo['memRealUsed']
+
+ # 进程备注,name,pid,启动命令
+ def get_process_ps(self, name, pid, p_exe=None, p=None):
+ processPs = {
+ 'irqbalance': '系统进程-优化系统性能服务',
+ 'containerd': 'docker管理服务',
+ 'qmgr': '系统进程-管理和控制邮件进程',
+ 'pickup': '系统进程-接收和处理待发送的邮件',
+ 'cleanup': '系统进程-服务器释放资源进程',
+ 'trivial-rewrite': '系统进程-邮件重写和转发服务',
+ 'bt-ipfilter': '宝塔网络的IP过滤器',
+ 'oneav': '微步木马检测服务',
+ 'rhsmcertd': '系统进程-验证系统的订阅状态服务',
+ 'tamperuser': '宝塔-企业级防篡改服务',
+ 'lvmetad': '系统进程-元数据守护进程',
+ 'containerd-shim-runc-v2': 'docker容器管理服务',
+ 'tuned': '系统进程-优化系统服务',
+ 'chronyd': '系统进程-时间同步服务',
+ 'auditd': '系统进程-系统安全日志记录服务',
+ 'gssproxy': '系统进程-身份验证和授权服务',
+ 'ossfs': '阿里云对象存储挂载服务',
+ 'cosfs': '腾讯云对象存储挂载服务',
+ 'obsfs': '华为云对象存储挂载服务',
+ 's3fs': '对象存储挂载服务',
+ 'bosfs': '百度云对象存储挂载服务',
+ 'jsvc': 'tomcat服务',
+ 'mysqld': 'MySQL服务',
+ 'php-fpm': 'PHP子进程',
+ 'php-cgi': 'PHP-CGI进程',
+ 'nginx': 'Nginx服务',
+ 'httpd': 'Apache服务',
+ 'sshd': 'SSH服务',
+ 'pure-ftpd': 'FTP服务',
+ 'sftp-server': 'SFTP服务',
+ 'mysqld_safe': 'MySQL服务',
+ 'firewalld': '防火墙服务',
+ 'BT-Panel': '宝塔面板-主进程',
+ 'BT-Task': '宝塔面板-后台任务进程',
+ 'NetworkManager': '网络管理服务',
+ 'svlogd': '日志守护进程',
+ 'memcached': 'Memcached缓存器',
+ 'gunicorn': "python项目",
+ "BTPanel": '宝塔面板',
+ 'baota_coll': "堡塔云控-主控端",
+ 'baota_client': "堡塔云控-被控端",
+ 'node': 'Node.js程序',
+ 'supervisord': 'Supervisor进程',
+ 'rsyslogd': 'rsyslog日志服务',
+ 'crond': '计划任务服务',
+ 'cron': '计划任务服务',
+ 'rsync': 'rsync文件同步进程',
+ 'ntpd': '网络时间同步服务',
+ 'rpc.mountd': 'NFS网络文件系统挂载服务',
+ 'sendmail': 'sendmail邮件服务',
+ 'postfix': 'postfix邮件服务',
+ 'npm': 'Node.js NPM管理器',
+ 'PM2': 'Node.js PM2进程管理器',
+ 'htop': 'htop进程监控软件',
+ 'btpython': '宝塔面板-独立Python环境进程',
+ 'btappmanagerd': '宝塔应用管理器插件',
+ 'dockerd': 'Docker容器管理器',
+ 'docker-proxy': 'Docker容器管理器',
+ 'docker-registry': 'Docker容器管理器',
+ 'docker-distribution': 'Docker容器管理器',
+ 'docker-network': 'Docker容器管理器',
+ 'docker-volume': 'Docker容器管理器',
+ 'docker-swarm': 'Docker容器管理器',
+ 'docker-systemd': 'Docker容器管理器',
+ 'docker-containerd': 'Docker容器管理器',
+ 'docker-containerd-shim': 'Docker容器管理器',
+ 'docker-runc': 'Docker容器管理器',
+ 'docker-init': 'Docker容器管理器',
+ 'docker-init-systemd': 'Docker容器管理器',
+ 'docker-init-upstart': 'Docker容器管理器',
+ 'docker-init-sysvinit': 'Docker容器管理器',
+ 'docker-init-openrc': 'Docker容器管理器',
+ 'docker-init-runit': 'Docker容器管理器',
+ 'docker-init-systemd-resolved': 'Docker容器管理器',
+ 'rpcbind': 'NFS网络文件系统服务',
+ 'dbus-daemon': 'D-Bus消息总线守护进程',
+ 'systemd-logind': '登录管理器',
+ 'systemd-journald': 'Systemd日志管理服务',
+ 'systemd-udevd': '系统设备管理服务',
+ 'systemd-timedated': '系统时间日期服务',
+ 'systemd-timesyncd': '系统时间同步服务',
+ 'systemd-resolved': '系统DNS解析服务',
+ 'systemd-hostnamed': '系统主机名服务',
+ 'systemd-networkd': '系统网络管理服务',
+ 'systemd-resolvconf': '系统DNS解析服务',
+ 'systemd-local-resolv': '系统DNS解析服务',
+ 'systemd-sysctl': '系统系统参数服务',
+ 'systemd-modules-load': '系统模块加载服务',
+ 'systemd-modules-restore': '系统模块恢复服务',
+ 'agetty': 'TTY登陆验证程序',
+ 'sendmail-mta': 'MTA邮件传送代理',
+ 'bash': 'bash命令行进程',
+ '(sd-pam)': '可插入认证模块',
+ 'polkitd': '授权管理服务',
+ 'mongod': 'MongoDB数据库服务',
+ 'mongodb': 'MongoDB数据库服务',
+ 'mongodb-mms-monitor': 'MongoDB数据库服务',
+ 'mongodb-mms-backup': 'MongoDB数据库服务',
+ 'mongodb-mms-restore': 'MongoDB数据库服务',
+ 'mongodb-mms-agent': 'MongoDB数据库服务',
+ 'mongodb-mms-analytics': 'MongoDB数据库服务',
+ 'mongodb-mms-tools': 'MongoDB数据库服务',
+ 'mongodb-mms-backup-agent': 'MongoDB数据库服务',
+ 'mongodb-mms-backup-tools': 'MongoDB数据库服务',
+ 'mongodb-mms-restore-agent': 'MongoDB数据库服务',
+ 'mongodb-mms-restore-tools': 'MongoDB数据库服务',
+ 'mongodb-mms-analytics-agent': 'MongoDB数据库服务',
+ 'mongodb-mms-analytics-tools': 'MongoDB数据库服务',
+ 'dhclient': 'DHCP协议客户端',
+ 'dhcpcd': 'DHCP协议客户端',
+ 'dhcpd': 'DHCP服务器',
+ 'isc-dhcp-server': 'DHCP服务器',
+ 'isc-dhcp-server6': 'DHCP服务器',
+ 'dhcp6c': 'DHCP服务器',
+ 'dhcpcd': 'DHCP服务器',
+ 'dhcpd': 'DHCP服务器',
+ 'avahi-daemon': 'Zeroconf守护进程',
+ 'login': '登录进程',
+ 'systemd': '系统管理服务',
+ 'systemd-sysv': '系统管理服务',
+ 'systemd-journal-gateway': '系统管理服务',
+ 'systemd-journal-remote': '系统管理服务',
+ 'systemd-journal-upload': '系统管理服务',
+ 'systemd-networkd': '系统网络管理服务',
+ 'rpc.idmapd': 'NFS网络文件系统相关服务',
+ 'cupsd': '打印服务',
+ 'cups-browsed': '打印服务',
+ 'sh': 'shell进程',
+ 'php': 'PHP CLI模式进程',
+ 'blkmapd': 'NFS映射服务',
+ 'lsyncd': '文件同步服务',
+ 'sleep': '延迟进程'
+ }
+ if p_exe:
+ # print(name.lower(), pid, p_exe)
+ # if p:
+ # cmdline = ' '.join(p.cmdline()).strip()
+ # print(cmdline)
+ if name == 'php-fpm':
+ try:
+ if self.is_mac:
+ php_version = p_exe.split('/')[-3][3:]
+ else:
+ php_version = p_exe.split('/')[-3]
+ return 'PHP' + php_version + '进程'
+ except:
+ pass
+ elif name.lower() == 'python' or name.lower() == 'python3':
+ p_exe_arr = p_exe.split('/')
+ if p_exe_arr[-1] in ['task.py']:
+ return 'MW面板-后台任务进程'
+ elif p_exe_arr[-1] in ['BT-Panel', 'runserver.py']:
+ return '面板-主进程'
+ elif p_exe.find('process_network_total') != -1:
+ return '面板-进程网络监控'
+
+ if p:
+ cmdline = ' '.join(p.cmdline()).strip()
+ cmdline_arr = cmdline.split('/')
+ if cmdline.find('process_network_total') != -1:
+ return '进程网络监控'
+ if cmdline_arr[-1] in ['BT-Task', 'task.py']:
+ return '后台任务进程'
+ elif cmdline_arr[-1] in ['BT-Panel', 'runserver.py']:
+ return '主进程'
+ elif cmdline.find('process_network_total') != -1:
+ return '进程网络监控'
+ elif cmdline.find('tamper_proof_service') != -1:
+ return '网站防篡改'
+ elif cmdline.find('syssafe') != -1:
+ return '系统加固'
+ elif cmdline.find('opwaf') != -1:
+ return 'WAF防火墙'
+ elif cmdline.find('acme') != -1:
+ return 'SSL证书签发'
+ elif cmdline.find('psync') != -1:
+ return '面板一键迁移'
+ elif cmdline.find('mdserver-web/plugins') != -1:
+ return '面板插件进程'
+ elif cmdline.find('/www/server/cron/') != -1:
+ return '面板计划任务'
+ elif cmdline.find('task.py') != -1:
+ return 'MW面板-后台任务'
+ elif cmdline.find('mdserver-web') != -1 and cmdline.find('gunicorn -c setting.py app:app') != -1:
+ return 'MW面板'
+ elif name.lower() == 'gunicorn':
+ if p:
+ cmdline = ' '.join(p.cmdline()).strip()
+ if cmdline.find('mdserver-web') != -1 and cmdline.find('gunicorn -c setting.py app:app') != -1:
+ return 'MW面板'
+
+ elif name == 'nginx':
+ default_name = 'Nginx'
+ if p_exe.find('openresty/nginx') != -1:
+ default_name = 'OpenResty'
+
+ if p.username() == 'www':
+ return default_name+'子进程'
+ else:
+ return default_name+'主进程'
+ elif name == 'openresty':
+ if p.username() == 'www':
+ return 'OpenResty子进程'
+ return 'OpenResty主进程'
+ elif name == 'mw':
+ return 'MW面板-命令'
+ elif p_exe == '/usr/bin/bash':
+ cmdline = ' '.join(p.cmdline()).strip()
+ if cmdline.find('task.py') != -1:
+ return 'MW面板-后台任务'
+ if cmdline.find('/www/server/cron/') != -1:
+ return '面板计划任务'
+ elif cmdline.find('mdserver-web/plugins') != -1:
+ return '面板插件进程'
+
+
+ if name in processPs: return processPs[name]
+
+ # if self.is_panel_process(pid): return 'MW面板'
+
+ if p_exe:
+ exe_keys = {
+ '/www/server/mdserver-web/plugins/': '面板插件',
+ '/www/server/cron/': '计划任务进程',
+ 'pm2': 'PM2进程管理器',
+ 'PM2': 'PM2进程管理器',
+ 'nvm': 'NVM Node版本管理器',
+ 'npm': 'NPM Node包管理器'
+ }
+
+ for k in exe_keys.keys():
+ if p_exe.find(k) != -1: return exe_keys[k]
+ if name.find(k) != -1: return exe_keys[k]
+
+ return name
+
+ def get_process_network(self, pid):
+ if not self.__process_net_list:
+ self.get_process_net_list()
+ if not self.last_net_process_time: return 0, 0, 0, 0
+ if not pid in self.__process_net_list: return 0, 0, 0, 0
+
+ if not pid in self.last_net_process:
+ return self.__process_net_list[pid]['up'], self.__process_net_list[pid]['up_package'], \
+ self.__process_net_list[pid]['down'], self.__process_net_list[pid]['down_package']
+
+ now_t = time.time()
+ # print(pid, self.__process_net_list[pid]['up'], self.last_net_process[pid]['up'],time.time(),self.last_net_process_time)
+ up = int((self.__process_net_list[pid]['up'] - self.last_net_process[pid]['up']) / ( now_t - self.last_net_process_time))
+ down = int((self.__process_net_list[pid]['down'] - self.last_net_process[pid]['down']) / ( now_t - self.last_net_process_time))
+ up_package = int((self.__process_net_list[pid]['up_package'] - self.last_net_process[pid]['up_package']) / ( now_t - self.last_net_process_time))
+ down_package = int((self.__process_net_list[pid]['down_package'] - self.last_net_process[pid]['down_package']) / (now_t - self.last_net_process_time))
+ # print('get_process_network', up, up_package, down, down_package)
+ return up, up_package, down, down_package
+
+ def get_process_cpu_time(self, cpu_times):
+ cpu_time = 0.00
+ for s in cpu_times: cpu_time += s
+ return cpu_time
+
+ # 获取cpu使用率
+ def get_cpu_percent(self, pid, cpu_times, cpu_time):
+ percent = 0.00
+ process_cpu_time = self.get_process_cpu_time(cpu_times)
+
+ if not self.old_info: self.old_info = {}
+ if not pid in self.old_info:
+ self.old_info[pid] = {}
+ self.old_info[pid]['cpu_time'] = process_cpu_time
+ return percent
+
+ if cpu_time == self.old_info['cpu_time']:
+ return 0.00
+
+ percent = round(100.00 * (process_cpu_time - self.old_info[pid]['cpu_time']) / (cpu_time - self.old_info['cpu_time']), 2)
+ # self.old_info[pid]['cpu_time'] = process_cpu_time
+
+ if percent > 0: return percent
+ return 0.00
+
+ # 获取平均负载
+ def get_load_average(self):
+ c = os.getloadavg()
+ data = {}
+ data['1'] = round(float(c[0]), 3) # float(c[0])
+ data['5'] = round(float(c[1]), 3) # float(c[1])
+ data['15'] = round(float(c[2]), 3) # float(c[2])
+ return data
+
+ def set_meter_head(self, get):
+ if not 'meter_head_name' in get:
+ return False
+
+ meter_head_name = get['meter_head_name']
+ meter_head_file = getServerDir()+'/meter_head.json'
+ try:
+ self.get_meter_head()
+ if meter_head_name not in self.meter_head.keys():
+ return False
+ if meter_head_name in ['ps', 'memory_used', 'cpu_percent', 'name']:
+ return False
+ self.meter_head[meter_head_name] = not self.meter_head[meter_head_name]
+ mw.writeFile(meter_head_file, json.dumps(self.meter_head))
+ return True
+ except:
+ return False
+
+ def get_meter_head(self, get=None):
+ meter_head_file = getServerDir()+'/meter_head.json'
+ if os.path.exists(meter_head_file):
+ self.meter_head = json.loads(mw.readFile(meter_head_file))
+ else:
+ self.meter_head = {
+ 'name': True,
+ 'pid': True,
+ 'cpu_percent': True,
+ 'down': True,
+ 'up': True,
+ 'status': True,
+ 'threads': True,
+ 'user': True,
+ 'ps': True,
+ 'memory_used': True,
+ 'io_read_bytes': True,
+ 'io_write_bytes': True,
+ 'connects': True
+ }
+ mw.writeFile(meter_head_file, json.dumps(self.meter_head))
+ return self.meter_head
+
+ # 添加进程查找
+ def search_pro(self, data, search):
+ try:
+ ldata = []
+ for i in data:
+ if search in i['name'] or search in i['exe'] or search in i['ps'] or search in i[
+ 'user'] or search in str(i['pid']) or search in i['status']:
+ ldata.append(i)
+ elif 'children' in i:
+ for k in i['children']:
+ if search in k['name'] or search in k['exe'] or search in k['ps'] or search in k[
+ 'user'] or search in str(k['pid']):
+ ldata.append(i)
+ return ldata
+ except:
+ print(mw.getTracebackInfo())
+ return data
+
+ def get_cpu_time(self):
+ cpu_times = psutil.cpu_times()
+
+ cpu_time = 0.00
+ for s in cpu_times: cpu_time += s
+ return cpu_time
+ # return s.user + s.system + s.nice + s.idle
+
+ # 获取python的路径
+ def get_python_bin(self):
+ mw_dir = mw.getServerDir() + '/mdserver-web'
+ bin_file = mw_dir + '/bin/python3'
+ if os.path.exists(bin_file):
+ return bin_file
+ return '/usr/bin/python3'
+
+ # 检查process_network_total.py是否运行
+ def check_process_net_total(self):
+ mw_dir = mw.getServerDir() + '/mdserver-web'
+ _pid_file = mw_dir+'/logs/process_network_total.pid'
+ if os.path.exists(_pid_file):
+ pid = mw.readFile(_pid_file)
+ if os.path.exists('/proc/' + pid): return True
+
+ cmd_file = mw_dir+'/plugins/task_manager/process_network_total.py'
+ python_bin = self.get_python_bin()
+ _cmd = 'nohup {} {} &> /tmp/net.log &'.format(python_bin, cmd_file)
+ mw.execShell(_cmd)
+
+ # 进程折叠,将子进程折叠到父进程下,并将使用资源累加。
+ def __pro_s_s(self, data: List) -> List:
+ """
+ 将子进程合并到父进程中
+ :param data:进程列表
+ :return:合并后的进程列表增加children字段
+ """
+ data1 = []
+ children_set = {'childrens': []}
+ for i in data:
+ if i['pid'] > 30 and i['ppid'] == 1:
+ children = self.__get_children(i['pid'])
+ s4 = time.time()
+ if children != []: children_set[i['pid']] = children
+ children_set['childrens'] += children
+ for i in data:
+ if i['pid'] in children_set:
+ i['children'] = []
+ for j in data:
+ if j['pid'] in children_set[i['pid']]:
+ i['children'].append(j)
+ i['memory_used'] += j['memory_used']
+ i['cpu_percent'] = round(i['cpu_percent'] + j['cpu_percent'], 2)
+ i['connects'] += j['connects']
+ i['threads'] += j['threads']
+
+ if 'io_write_bytes' in j:
+ i['io_write_bytes'] += j['io_write_bytes']
+ if 'io_read_bytes' in j:
+ i['io_read_bytes'] += j['io_read_bytes']
+ if 'io_write_speed' in j:
+ i['io_write_speed'] += j['io_write_speed']
+ if 'io_read_speed' in j:
+ i['io_read_speed'] += j['io_read_speed']
+
+ if 'up' in j:
+ i['up'] += j['up']
+ if 'up_package' in j:
+ i['up_package'] += j['up_package']
+
+ if 'down' in j:
+ i['down'] += j['down']
+ if 'down_package' in j:
+ i['down_package'] += j['down_package']
+ data1.append(i)
+ elif i['pid'] not in children_set['childrens']:
+ data1.append(i)
+ return data1
+
+ def __get_children(self, pid: int) -> List:
+ try:
+ p = psutil.Process(pid) # pid为指定进程的进程号
+ psutil.process_iter()
+ children = p.children(recursive=True) # 获取指定进程的所有子进程
+ pids = []
+ for child in children:
+ pids.append(child.pid)
+ return pids
+ except:
+ return []
+
+ # 外部接口,结束进程,pid30以上
+ def kill_process(self, get):
+ pid = int(get['pid'])
+ if pid < 30: return mw.returnData(False, '不能结束系统关键进程!')
+ if not pid in psutil.pids(): return mw.returnData(False, '指定进程不存在!')
+ if not 'killall' in get:
+ p = psutil.Process(pid)
+ if self.is_panel_process(pid): return mw.returnData(False, '不能结束面板服务进程')
+ p.kill()
+ return mw.returnData(True, '进程已结束')
+ return self.kill_process_all(pid)
+
+ # 是否为面板进程
+ def is_panel_process(self, pid):
+ if not self.panel_pid:
+ self.panel_pid = os.getpid()
+ if pid == self.panel_pid: return True
+ if not self.task_pid:
+ try:
+ self.task_pid = int(mw.execShell("ps aux | grep 'python3 task.py' |grep -v grep|head -n1|awk '{print $2}'")[0])
+ except:
+ self.task_pid = -1
+ if pid == self.task_pid: return True
+ return False
+
+ # 遍历结束pid的子进程 kill_process_all——>引用kill_process_lower
+ def kill_process_lower(self, pid):
+ pids = psutil.pids()
+ for lpid in pids:
+ if lpid < 30: continue
+ if self.is_panel_process(lpid): continue
+ p = psutil.Process(lpid)
+ ppid = p.ppid()
+ if ppid == pid:
+ p.kill()
+ return self.kill_process_lower(lpid)
+ return True
+
+ # 结束进程树 kill_process——>引用kill_process_all
+ def kill_process_all(self, pid):
+ if pid < 30: return mw.returnData(True, '已结束此进程树!')
+ if self.is_panel_process(pid): return mw.returnData(False, '不能结束面板服务进程')
+ try:
+ if not pid in psutil.pids(): mw.returnData(True, '已结束此进程树!')
+ p = psutil.Process(pid)
+ ppid = p.ppid()
+ name = p.name()
+ p.kill()
+ mw.execShell('pkill -9 ' + name)
+ if name.find('php-') != -1:
+ mw.execShell("rm -f /tmp/php-cgi-*.sock")
+ elif name.find('mysql') != -1:
+ mw.execShell("rm -f /tmp/mysql.sock")
+ elif name.find('nginx') != -1:
+ mw.execShell("rm -f /tmp/mysql.sock")
+ self.kill_process_lower(pid)
+ if ppid: return self.kill_process_all(ppid)
+ except:
+ pass
+ return mw.returnData(True, '已结束此进程树!')
+
+
+
+ def get_process_list(self, args = {}):
+ # https://hellowac.github.io/psutil-doc-zh/processes/process_class/oneshot.html
+ if self.is_mac:
+ return self.get_process_list_mac(args)
+ return self.get_process_list_linux(args)
+
+ def get_process_list_mac(self, args = {}):
+ self.new_info['cpu_time'] = self.get_cpu_time()
+ self.new_info['time'] = time.time()
+
+ sortx = 'all'
+ if 'sortx' in args: sortx = args['sortx']
+
+ if not 'sortx' in args:
+ args['sortx'] = 'status'
+ if args['sortx'] == 'status': res = False
+ if 'reverse' in args:
+ if args['reverse'] in ['undefined', 'null']:
+ args['reverse'] = 'True'
+ args['sortx'] = 'all'
+ if not args['reverse'] in ['True', 'False']: args['reverse'] = 'True'
+ res_list = {'True': True, 'False': False}
+ res = res_list[args['reverse']]
+ else:
+ args['reverse'] = True
+ if args['reverse'] in ['undefined', 'null']:
+ args['reverse'] = 'True'
+ args['sortx'] = 'all'
+
+ info = {}
+ info['activity'] = 0
+ info['cpu'] = 0.00
+ info['mem'] = 0
+ info['disk'] = 0
+ status_ps = {'sleeping': '睡眠', 'running': '活动'}
+ ppids = psutil.pids()
+ processList = []
+ for pid in ppids:
+ tmp = {}
+ try:
+ try:
+ p = psutil.Process(pid)
+ except Exception as e:
+ continue
+
+ with p.oneshot():
+ p_state = p.status()
+ try:
+ tmp['exe'] = p.exe()
+ except Exception as e:
+ continue
+
+ tmp['name'] = p.name()
+ tmp['pid'] = pid
+ tmp['ppid'] = p.ppid()
+ tmp['create_time'] = int(p.create_time())
+ tmp['status'] = p_state
+ tmp['user'] = p.username()
+ tmp['connects'] = self.get_connects(pid)
+
+ if p_state == 'running': info['activity'] += 1
+ if p_state in status_ps: p_state = status_ps[p_state]
+
+ try:
+ tmp['threads'] = p.num_threads()
+ except Exception as e:
+ continue
+
+
+ tmp['ps'] = self.get_process_ps(tmp['name'], pid, tmp['exe'], p)
+ tmp['up'], tmp['up_package'], tmp['down'], tmp['down_package'] = self.get_process_network(pid)
+
+
+ try:
+ p_cpus = p.cpu_times()
+ except Exception as e:
+ continue
+
+ try:
+ p_mem = p.memory_info()
+ except Exception as e:
+ continue
+ if p_mem.rss == 0: continue
+
+ tmp['memory_used'] = p_mem.rss
+ tmp['cpu_percent'] = self.get_cpu_percent(str(pid), p_cpus, self.new_info['cpu_time'])
+ # print(tmp['cpu_percent'])
+ if tmp['cpu_percent'] > 100: tmp['cpu_percent'] = 0.1
+ info['cpu'] += tmp['cpu_percent']
+ info['disk'] += 0
+
+ processList.append(tmp)
+ del (p)
+ del (tmp)
+ except Exception as e:
+ print("err:", mw.getTracebackInfo())
+ continue
+
+
+ processList = self.__pro_s_s(processList)
+ res = True
+
+ if args['sortx'] not in ['all']:
+ processList = sorted(processList, key=lambda x: x[args['sortx']], reverse=res)
+ else:
+ processList = sorted(processList, key=lambda x: [x['cpu_percent'], x['connects'], x['threads'],
+ x['memory_used']], reverse=res)
+
+ info['load_average'] = self.get_load_average()
+ data = {}
+ data['is_mac'] = self.is_mac
+ data['process_list'] = processList
+ info['cpu'] = round(info['cpu'], 3)
+ info['mem'] = self.get_mem_info()
+ data['info'] = info
+ if 'search' in args:
+ if args['search'] != '':
+ data['process_list'] = self.search_pro(data['process_list'], args['search'])
+ self.get_meter_head()
+ data['meter_head'] = self.meter_head
+
+ data['meter_head']['io_read_bytes'] = False
+ data['meter_head']['io_write_bytes'] = False
+ data['meter_head']['down'] = False
+ data['meter_head']['up'] = False
+ return data
+
+ # 获取进程信息
+ def get_process_list_linux(self, get = {}):
+ self.check_process_net_total()
+ self.pids = psutil.pids()
+ processList = []
+ if type(self.new_info) != dict: self.new_info = {}
+ self.new_info['cpu_time'] = self.get_cpu_time()
+ self.new_info['time'] = time.time()
+ self.get_process_net_list()
+
+
+ if not 'sortx' in get:
+ get['sortx'] = 'all'
+
+ res = True
+ if get['sortx'] == 'status':
+ res = False
+
+ if 'reverse' in get:
+ if get['reverse'] in ['undefined', 'null']:
+ get['reverse'] = 'True'
+ get['sortx'] = 'all'
+ if not get['reverse'] in ['True', 'False']: get['reverse'] = 'True'
+ res_list = {'True': True, 'False': False}
+ res = res_list[get['reverse']]
+ else:
+ get['reverse'] = True
+ if get['reverse'] in ['undefined', 'null']:
+ get['reverse'] = 'True'
+ get['sortx'] = 'all'
+
+ info = {}
+ info['activity'] = 0
+ info['cpu'] = 0.00
+ info['mem'] = 0
+ info['disk'] = 0
+ status_ps = {'sleeping': '睡眠', 'running': '活动'}
+ for pid in self.pids:
+ tmp = {}
+ try:
+ try:
+ p = psutil.Process(pid)
+ except Exception as e:
+ continue
+ with p.oneshot():
+
+ try:
+ p_mem = p.memory_full_info()
+ except Exception as e:
+ continue
+
+ if p_mem.rss == 0: continue
+ pio = p.io_counters()
+ p_cpus = p.cpu_times()
+ p_state = p.status()
+ if p_state == 'running': info['activity'] += 1
+ if p_state in status_ps: p_state = status_ps[p_state]
+ tmp['exe'] = p.exe()
+
+ try:
+ tmp['name'] = p.name()
+ except Exception as e:
+ continue
+ tmp['pid'] = pid
+ tmp['ppid'] = p.ppid()
+ tmp['create_time'] = int(p.create_time())
+ tmp['status'] = p_state
+ tmp['user'] = p.username()
+ tmp['memory_used'] = p_mem.uss
+ tmp['cpu_percent'] = self.get_cpu_percent(str(pid), p_cpus, self.new_info['cpu_time'])
+ if tmp['name'] == 'BT-Panel' and tmp['cpu_percent'] > 1:
+ tmp['cpu_percent'] = round(tmp['cpu_percent'] % 1, 2)
+ tmp['io_write_bytes'] = pio.write_bytes
+ tmp['io_read_bytes'] = pio.read_bytes
+ tmp['io_write_speed'] = self.get_io_write(str(pid), pio.write_bytes)
+ tmp['io_read_speed'] = self.get_io_read(str(pid), pio.read_bytes)
+ tmp['connects'] = self.get_connects(pid)
+ tmp['threads'] = p.num_threads()
+ tmp['ps'] = self.get_process_ps(tmp['name'], pid, tmp['exe'], p)
+ tmp['up'], tmp['up_package'], tmp['down'], tmp['down_package'] = self.get_process_network(pid)
+ # print(pid,tmp['up'], tmp['up_package'], tmp['down'], tmp['down_package'])
+ if tmp['cpu_percent'] > 100: tmp['cpu_percent'] = 0.1
+ info['cpu'] += tmp['cpu_percent']
+ info['disk'] += tmp['io_write_speed'] + tmp['io_read_speed']
+ processList.append(tmp)
+ del (p)
+ del (tmp)
+ except Exception as e:
+ print(mw.getTracebackInfo())
+ continue
+
+ processList = self.__pro_s_s(processList)
+
+ if get['sortx'] not in ['all']:
+ processList = sorted(processList, key=lambda x: x[get['sortx']], reverse=res)
+ else:
+ processList = sorted(processList, key=lambda x: [x['cpu_percent'], x['up'], x['down'], x['io_write_speed'],
+ x['io_read_speed'], x['connects'], x['threads'],
+ x['memory_used']], reverse=res)
+ info['load_average'] = self.get_load_average()
+ data = {}
+ data['process_list'] = processList
+ info['cpu'] = round(info['cpu'], 2)
+ info['mem'] = self.get_mem_info()
+ data['info'] = info
+ if 'search' in get:
+ if get['search'] != '':
+ data['process_list'] = self.search_pro(data['process_list'], get['search'])
+ self.get_meter_head()
+ data['meter_head'] = self.meter_head
+ return data
+
+ def object_to_dict(self, obj):
+ result = {}
+ for name in dir(obj):
+ value = getattr(obj, name)
+ if not name.startswith('__') and not callable(value) and not name.startswith('_'): result[name] = value
+ return result
+
+ def list_to_dict(self, data):
+ result = []
+ for s in data:
+ result.append(self.object_to_dict(s))
+ return result
+
+ # 获取进程的详细信息
+ def get_process_info(self, args={}):
+ pid = int(args['pid'])
+ try:
+ p = psutil.Process(pid)
+ processInfo = {}
+
+ if self.is_mac:
+ p_mem = self.object_to_dict(p.memory_info())
+ else:
+ p_mem = self.object_to_dict(p.memory_full_info())
+ pio = p.io_counters()
+ processInfo['io_write_bytes'] = pio.write_bytes;
+ processInfo['io_read_bytes'] = pio.read_bytes;
+ # p_cpus= p.cpu_times()
+ processInfo['exe'] = p.exe()
+ processInfo['name'] = p.name();
+ processInfo['pid'] = pid;
+ processInfo['ppid'] = p.ppid()
+ processInfo['pname'] = 'sys'
+ if processInfo['ppid'] != 0: processInfo['pname'] = psutil.Process(processInfo['ppid']).name()
+ processInfo['comline'] = p.cmdline()
+ processInfo['create_time'] = int(p.create_time())
+ processInfo['open_files'] = self.list_to_dict(p.open_files())
+ processInfo['status'] = p.status();
+ processInfo['user'] = p.username();
+ processInfo['memory_full'] = p_mem
+
+ processInfo['connects'] = self.get_connects(pid)
+ processInfo['threads'] = p.num_threads()
+ processInfo['ps'] = self.get_process_ps(processInfo['name'], pid, processInfo['exe'], p)
+ except Exception as e:
+ # print(mw.getTracebackInfo())
+ return mw.returnData(False, '指定进程已关闭!')
+ return processInfo
+
+ # 获取用户组处理函数 get_user_list——>引用get_group_name
+ def get_group_name(self, gid):
+ for g in self.groupList:
+ if g['gid'] == gid: return g['group']
+ return ''
+
+ # 用户名注释:ps get_user_list——>引用get_user_ps
+ def get_user_ps(self, name, ps):
+ userPs = {'www': '面板', 'root': '超级管理员', 'mysql': '用于运行MySQL的用户',
+ 'mongo': '用于运行MongoDB的用户',
+ 'git': 'git用户', 'mail': 'mail', 'nginx': '第三方nginx用户', 'postfix': 'postfix邮局用户',
+ 'lp': '打印服务帐号',
+ 'daemon': '控制后台进程的系统帐号', 'nobody': '匿名帐户', 'bin': '管理大部分命令的帐号',
+ 'adm': '管理某些管理文件的帐号', 'smtp': 'smtp邮件'}
+ if name in userPs: return userPs[name]
+ if not ps: return name
+ return ps
+
+ # 获取服务器的组名和id,从/etc/group中读取。存储到self.groupList,get_user_list——>引用get_group_list
+ def get_group_list(self, get):
+ tmpList = mw.readFile('/etc/group').split("\n")
+ groupList = []
+ for gl in tmpList:
+ tmp = gl.split(':')
+ if len(tmp) < 3: continue
+ groupInfo = {}
+ groupInfo['group'] = tmp[0]
+ groupInfo['gid'] = tmp[2]
+ groupList.append(groupInfo)
+ return groupList;
+
+ # 外部接口,删除用户,不能删除系统运行环境用户
+ def remove_user(self, get):
+ if self.is_mac:
+ return mw.returnData(False, '无法操作!')
+ users = ['www', 'root', 'mysql', 'shutdown', 'postfix',
+ 'smmsp', 'sshd', 'systemd-network', 'systemd-bus-proxy',
+ 'avahi-autoipd', 'mail', 'sync', 'lp',
+ 'adm', 'bin', 'mailnull', 'ntp', 'daemon', 'sys'];
+
+ if not 'user' in get:
+ return mw.returnData(False, '缺少参数!')
+
+ if get['user'] in users: return mw.returnData(False, '不能删除系统和环境关键用户!')
+
+ user = get['user']
+
+ r = mw.execShell("userdel " + user)
+ if r[1].find('process') != -1:
+ try:
+ pid = r[1].split()[-1]
+ p = psutil.Process(int(pid))
+ pname = p.name()
+ p.kill()
+ mw.execShell("pkill -9 " + pname)
+ r = mw.execShell("userdel " + user)
+ except:
+ pass
+ if r[1].find('userdel:') != -1: return mw.returnData(False, r[1]);
+ return mw.returnData(True, '删除成功!')
+
+ # 获取用户列表 从/etc/passwd文件中读取
+ def get_user_list(self, get={}):
+ tmpList = mw.readFile('/etc/passwd').strip().split("\n")
+ userList = []
+ self.groupList = self.get_group_list(get)
+ for ul in tmpList:
+ tmp = ul.split(':')
+ if len(tmp) < 6: continue
+ userInfo = {}
+ userInfo['username'] = tmp[0]
+ userInfo['uid'] = tmp[2]
+ userInfo['gid'] = tmp[3]
+ userInfo['group'] = self.get_group_name(tmp[3])
+ userInfo['ps'] = self.get_user_ps(tmp[0], tmp[4])
+ userInfo['home'] = tmp[5]
+ userInfo['login_shell'] = tmp[6]
+ userList.append(userInfo)
+
+ # print(userList)
+ if 'search' in get:
+ if get['search'] != '':
+ userList = self.search_user(userList, get['search'])
+ return userList
+
+ # 查询用户
+ def search_user(self, data, search):
+ try:
+ ldata = []
+ for i in data:
+ if search in i['username'] or search in i['ps'] or search in i['login_shell'] or search in i[
+ 'home'] or search in i['group']:
+ ldata.append(i)
+ return ldata
+ except:
+ mw.writeLog('任务管理', traceback.format_exc())
+ return data
+
+ # get_network_list ——>引用get_network
+ def get_network(self):
+ try:
+ networkIo = psutil.net_io_counters()[:4]
+ self.new_net_info['upTotal'] = networkIo[0]
+ self.new_net_info['downTotal'] = networkIo[1]
+ self.new_net_info['upPackets'] = networkIo[2]
+ self.new_net_info['downPackets'] = networkIo[3]
+ self.new_net_info['time'] = time.time()
+
+ if not self.old_net_info: self.old_net_info = {}
+ if not 'upTotal' in self.old_net_info:
+ time.sleep(0.1)
+ networkIo = psutil.net_io_counters()[:4]
+ self.old_net_info['upTotal'] = networkIo[0]
+ self.old_net_info['downTotal'] = networkIo[1]
+ self.old_net_info['upPackets'] = networkIo[2]
+ self.old_net_info['downPackets'] = networkIo[3]
+ self.old_net_info['time'] = time.time()
+
+ s = self.new_net_info['time'] - self.old_net_info['time']
+ networkInfo = {}
+ networkInfo['upTotal'] = networkIo[0]
+ networkInfo['downTotal'] = networkIo[1]
+ networkInfo['up'] = round((float(networkIo[0]) - self.old_net_info['upTotal']) / s, 2)
+ networkInfo['down'] = round((float(networkIo[1]) - self.old_net_info['downTotal']) / s, 2)
+ networkInfo['downPackets'] = networkIo[3]
+ networkInfo['upPackets'] = networkIo[2]
+ networkInfo['downPackets_s'] = int((networkIo[3] - self.old_net_info['downPackets']) / s)
+ networkInfo['upPackets_s'] = int((networkIo[2] - self.old_net_info['upPackets']) / s)
+ return networkInfo
+ except:
+ return None
+
+
+ # 获取当前网络连接信息
+ def get_network_list(self, get = {}):
+ netstats = psutil.net_connections()
+ data = {}
+ data['is_mac'] = False
+ if self.is_mac:
+ data['is_mac'] = self.is_mac
+ return data
+
+ netstats = psutil.net_connections()
+ networkList = []
+ for netstat in netstats:
+ tmp = {}
+ if netstat.type == 1:
+ tmp['type'] = 'tcp'
+ else:
+ tmp['type'] = 'udp'
+ tmp['family'] = netstat.family
+ tmp['laddr'] = netstat.laddr
+ tmp['raddr'] = netstat.raddr
+ tmp['status'] = netstat.status
+ p = psutil.Process(netstat.pid)
+ tmp['process'] = p.name()
+ if tmp['process'] in ['gunicorn']: continue
+ tmp['pid'] = netstat.pid
+ networkList.append(tmp)
+ del (p)
+ del (tmp)
+ networkList = sorted(networkList, key=lambda x: x['status'], reverse=True)
+
+ data['list'] = networkList
+ data['state'] = self.get_network()
+ if 'search' in get:
+ if get['search'] != '':
+ data['list'] = self.search_network(data['list'], get['search'])
+ return data
+
+ # 查询网络
+ def search_network(self, data, search):
+ try:
+ ldata = []
+ for i in data:
+ if search in i['process'] or search in i['status'] or search in str(i['pid']) or search in i['laddr'][
+ 0] or search in str(
+ i['laddr'][1]):
+ ldata.append(i)
+ continue
+ if i['raddr'] != 'NONE':
+ for j in i['raddr']:
+ if search in str(j):
+ ldata.append(i)
+ return ldata
+ except:
+ return data
+
+ # 获取当前运行级别 get_service_list ——> 引用get_my_runlevel
+ def get_my_runlevel(self):
+ try:
+ runlevel = mw.execShell('runlevel')[0].split()[1]
+ except:
+ runlevel_dict = {"multi-user.target": '3', 'rescue.target': '1', 'poweroff.target': '0',
+ 'graphical.target': '5', "reboot.target": '6'}
+ r_tmp = mw.execShell('systemctl get-default')[0].strip()
+ if r_tmp in runlevel_dict:
+ runlevel = runlevel_dict[r_tmp]
+ else:
+ runlevel = '3'
+ return runlevel
+
+ # 服务注释 get_service_list——>引用 get_run_ps
+ def get_run_ps(self, name):
+ runPs = {'netconsole': '网络控制台日志', 'network': '网络服务', 'jexec': 'JAVA', 'tomcat8': 'Apache Tomcat',
+ 'tomcat7': 'Apache Tomcat', 'mariadb': 'Mariadb',
+ 'tomcat9': 'Apache Tomcat', 'tomcat': 'Apache Tomcat', 'memcached': 'Memcached缓存器',
+ 'php-fpm-53': 'PHP-5.3', 'php-fpm-52': 'PHP-5.2',
+ 'php-fpm-54': 'PHP-5.4', 'php-fpm-55': 'PHP-5.5', 'php-fpm-56': 'PHP-5.6', 'php-fpm-70': 'PHP-7.0',
+ 'php-fpm-71': 'PHP-7.1',
+ 'php-fpm-72': 'PHP-7.2', 'rsync_inotify': 'rsync实时同步', 'pure-ftpd': 'FTP服务',
+ 'mongodb': 'MongoDB', 'nginx': 'Web服务器(Nginx)',
+ 'httpd': 'Web服务器(Apache)', 'mw': '面板', 'mysqld': 'MySQL数据库', 'rsynd': 'rsync主服务',
+ 'php-fpm': 'PHP服务', 'systemd': '系统核心服务',
+ '/etc/rc.local': '用户自定义启动脚本', '/etc/profile': '全局用户环境变量',
+ '/etc/inittab': '用于自定义系统运行级别', '/etc/rc.sysinit': '系统初始化时调用的脚本',
+ 'sshd': 'SSH服务', 'crond': '计划任务服务', 'udev-post': '设备管理系统', 'auditd': '审核守护进程',
+ 'rsyslog': 'rsyslog服务', 'sendmail': '邮件发送服务', 'blk-availability': 'lvm2相关',
+ 'local': '用户自定义启动脚本', 'netfs': '网络文件系统', 'lvm2-monitor': 'lvm2相关',
+ 'xensystem': 'xen云平台相关', 'iptables': 'iptables防火墙', 'ip6tables': 'iptables防火墙 for IPv6',
+ 'firewalld': 'firewall防火墙'}
+ if name in runPs: return runPs[name]
+ return name
+
+ # 清除注释
+ def clear_comments(self, body):
+ bodyTmp = body.split("\n")
+ bodyR = ""
+ for tmp in bodyTmp:
+ if tmp.startswith('#'): continue
+ if tmp.strip() == '': continue
+ bodyR += tmp
+ return bodyR
+
+ # 获取启动项
+ def get_run_list(self, get={}):
+ data = {}
+ data['is_mac'] = False
+ if self.is_mac:
+ data['is_mac'] = self.is_mac
+ return data
+
+ runFile = ['/etc/rc.local', '/etc/profile', '/etc/inittab', '/etc/rc.sysinit']
+ runList = []
+ for rfile in runFile:
+ if not os.path.exists(rfile): continue
+ bodyR = self.clear_comments(mw.readFile(rfile))
+ if not bodyR: continue
+ stat = os.stat(rfile)
+ accept = str(oct(stat.st_mode)[-3:])
+ if accept == '644': continue
+ tmp = {}
+ tmp['name'] = rfile
+ tmp['srcfile'] = rfile
+ tmp['size'] = os.path.getsize(rfile)
+ tmp['access'] = accept
+ tmp['ps'] = self.get_run_ps(rfile)
+ # tmp['body'] = bodyR
+ runList.append(tmp)
+ runlevel = self.get_my_runlevel()
+ runPath = ['/etc/init.d', '/etc/rc' + runlevel + '.d']
+ tmpAll = []
+ islevel = False
+ for rpath in runPath:
+ if not os.path.exists(rpath): continue
+ if runPath[1] == rpath: islevel = True
+ for f in os.listdir(rpath):
+ if f[:1] != 'S': continue
+ filename = rpath + '/' + f
+ if not os.path.exists(filename): continue
+ if os.path.isdir(filename): continue
+ if os.path.islink(filename):
+ flink = os.readlink(filename).replace('../', '/etc/')
+ if not os.path.exists(flink): continue
+ filename = flink
+ tmp = {}
+ tmp['name'] = f
+ if islevel: tmp['name'] = f[3:]
+ if tmp['name'] in tmpAll: continue
+ stat = os.stat(filename)
+ accept = str(oct(stat.st_mode)[-3:])
+ if accept == '644': continue
+ tmp['srcfile'] = filename
+ tmp['access'] = accept
+ tmp['size'] = os.path.getsize(filename)
+ tmp['ps'] = self.get_run_ps(tmp['name'])
+ runList.append(tmp)
+ tmpAll.append(tmp['name'])
+ data = {}
+ data['run_list'] = runList
+ data['run_level'] = runlevel
+ if 'search' in get:
+ if get['search'] != '':
+ data['run_list'] = self.search_run(data['run_list'], get['search'])
+ return data
+
+ # 检查服务是否为系统服务get_systemctl_list——>引用cont_systemctl
+ def cont_systemctl(self, name):
+ conts = ['systemd', 'rhel', 'plymouth', 'rc-', '@', 'init', 'ipr', 'dbus', '-local']
+ for c in conts:
+ if name.find(c) != -1: return False
+ return True
+
+ # 获取系统服务运行级别 get_service_list——>引用get_systemctl_list
+ def get_systemctl_list(self, serviceList, runlevel):
+ systemctl_user_path = '/usr/lib/systemd/system/'
+ systemctl_run_path = '/etc/systemd/system/multi-user.target.wants/'
+ if not os.path.exists(systemctl_user_path) or not os.path.exists(systemctl_run_path): return serviceList
+ r = '.service'
+ for d in os.listdir(systemctl_user_path):
+ if d.find(r) == -1: continue;
+ if not self.cont_systemctl(d): continue;
+ isrun = '关闭 '
+ serviceInfo = {}
+ serviceInfo['name'] = d.replace(r, '')
+ serviceInfo['runlevel_0'] = isrun
+ serviceInfo['runlevel_1'] = isrun
+ serviceInfo['runlevel_2'] = isrun
+ serviceInfo['runlevel_3'] = isrun
+ serviceInfo['runlevel_4'] = isrun
+ serviceInfo['runlevel_5'] = isrun
+ serviceInfo['runlevel_6'] = isrun
+ if os.path.exists(systemctl_run_path + d):
+ isrun = '开启 '
+ serviceInfo['runlevel_' + runlevel] = isrun
+ serviceInfo['runlevel_3'] = isrun
+ serviceInfo['runlevel_5'] = isrun
+
+ serviceInfo['ps'] = self.get_run_ps(serviceInfo['name'])
+ serviceList.append(serviceInfo)
+ return serviceList
+
+ # 外部接口,删除服务。不能删除mw
+ def remove_service(self, get):
+ if not 'serviceName' in get:
+ return mw.returnData(False,'缺少参数');
+
+ serviceName = get['serviceName']
+ if serviceName == 'mw': return mw.returnData(False, '不能通过面板结束面板服务!')
+ systemctl_user_path = '/usr/lib/systemd/system/'
+ if os.path.exists(systemctl_user_path + serviceName + '.service'):
+ return mw.returnData(False,'Systemctl托管的服务不能通过面板删除');
+
+ mw.execShell('service ' + serviceName + ' stop')
+ if os.path.exists('/usr/sbin/update-rc.d'):
+ mw.execShell('update-rc.d ' + serviceName + ' remove')
+ elif os.path.exists('/usr/sbin/chkconfig'):
+ mw.execShell('chkconfig --del ' + serviceName)
+ else:
+ mw.execShell("rm -f /etc/rc0.d/*" + serviceName)
+ mw.execShell("rm -f /etc/rc1.d/*" + serviceName)
+ mw.execShell("rm -f /etc/rc2.d/*" + serviceName)
+ mw.execShell("rm -f /etc/rc3.d/*" + serviceName)
+ mw.execShell("rm -f /etc/rc4.d/*" + serviceName)
+ mw.execShell("rm -f /etc/rc5.d/*" + serviceName)
+ mw.execShell("rm -f /etc/rc6.d/*" + serviceName)
+ filename = '/etc/init.d/' + serviceName
+ if os.path.exists(filename): os.remove(filename)
+ return mw.returnData(True, '删除成功!')
+
+ # 外部接口,设置软件运行环境,不能设置0,6
+ def set_runlevel_state(self, get):
+ if not 'runlevel' in get:
+ return mw.returnData(False,'缺少参数[runlevel]')
+
+ if not 'serviceName' in get:
+ return mw.returnData(False,'缺少参数[serviceName]')
+
+ runlevel = get['runlevel']
+ serviceName = get['serviceName']
+ if runlevel == '0' or runlevel == '6':
+ return mw.returnData(False,'为安全考虑,不能通过面板直接修改此运行级别')
+
+ systemctl_user_path = '/usr/lib/systemd/system/'
+ systemctl_run_path = '/etc/systemd/system/multi-user.target.wants/'
+ if os.path.exists(systemctl_user_path + serviceName + '.service'):
+ runlevel_cmd = mw.execShell('runlevel')[0].split()[1]
+ if runlevel_cmd != runlevel:
+ return mw.returnData(False,'Systemctl托管的服务不能设置非当前运行级别的状态')
+ action = 'enable'
+ if os.path.exists(systemctl_run_path + serviceName + '.service'):
+ action = 'disable'
+ mw.execShell('systemctl ' + action + ' ' + serviceName + '.service')
+ return mw.returnData(True, '设置成功!')
+
+ rc_d = '/etc/rc' + runlevel + '.d/'
+ import shutil
+ for d in os.listdir(rc_d):
+ if d[3:] != serviceName: continue
+ sfile = rc_d + d
+ c = 'S'
+ if d[:1] == 'S': c = 'K'
+ dfile = rc_d + c + d[1:]
+ shutil.move(sfile, dfile)
+ return mw.returnData(True, '设置成功!')
+ return mw.returnData(False, '设置失败!')
+
+
+ # 外部接口 查询服务启动级别 /etc/init.d/
+ def get_service_list(self, get = {}):
+ data = {}
+ data['is_mac'] = False
+ if self.is_mac:
+ data['is_mac'] = self.is_mac
+ return data
+
+ init_d = '/etc/init.d/'
+ serviceList = []
+ for sname in os.listdir(init_d):
+ try:
+ if str(oct(os.stat(init_d + sname).st_mode)[-3:]) == '644': continue
+ serviceInfo = {}
+ runlevels = self.get_runlevel(sname)
+ serviceInfo['name'] = sname
+ serviceInfo['runlevel_0'] = runlevels[0]
+ serviceInfo['runlevel_1'] = runlevels[1]
+ serviceInfo['runlevel_2'] = runlevels[2]
+ serviceInfo['runlevel_3'] = runlevels[3]
+ serviceInfo['runlevel_4'] = runlevels[4]
+ serviceInfo['runlevel_5'] = runlevels[5]
+ serviceInfo['runlevel_6'] = runlevels[6]
+ serviceInfo['ps'] = self.get_run_ps(sname)
+ serviceList.append(serviceInfo)
+ except:
+ continue
+
+ data['runlevel'] = self.get_my_runlevel()
+ data['serviceList'] = sorted(serviceList, key=lambda x: x['name'], reverse=False)
+ data['serviceList'] = self.get_systemctl_list(data['serviceList'], data['runlevel'])
+ if 'search' in get:
+ if get['search'] != '':
+ data['serviceList'] = self.search_service(data['serviceList'], get['search'])
+ return data
+
+ def search_service(self, data, search):
+ try:
+ ldata = []
+ for i in data:
+ if search in i['name'] or search in i['ps']:
+ ldata.append(i)
+ return ldata
+ except:
+ return data
+
+ # 获取存放计划任务的路径
+ def get_cron_file(self):
+ filename = '/var/spool/cron/crontabs/root'
+ if os.path.exists(filename): return filename
+ filename = '/var/spool/cron/root'
+ if not os.path.exists(filename):
+ mw.writeFile(filename, "")
+ return filename
+
+ # 数转周
+ def toWeek(self, num):
+ if num > 6: return ''
+ wheres = {
+ 0: '日',
+ 1: '一',
+ 2: '二',
+ 3: '三',
+ 4: '四',
+ 5: '五',
+ 6: '六',
+ }
+ return wheres[num]
+
+ # 解析计划任务执行周期
+ def decode_cron_cycle(self, tmp):
+ if not tmp[4]: tmp[4] = '*'
+ if tmp[4] != '*':
+ cycle = '每周' + self.toWeek(int(tmp[4])) + '的' + tmp[1] + '时' + tmp[0] + '分'
+ elif tmp[2] != '*':
+ if tmp[2].find('*') == -1:
+ cycle = '每月的' + tmp[2] + '日,' + tmp[1] + '时' + tmp[0] + '分'
+ else:
+ cycle = '每隔' + tmp[2].split('/')[1] + '天' + tmp[1] + '时' + tmp[0] + '分'
+ elif tmp[1] != '*':
+ if tmp[1].find('*') == -1:
+ cycle = '每天的' + tmp[1] + '时' + tmp[0] + '分'
+ else:
+ cycle = '每隔' + tmp[1].split('/')[1] + '小时' + tmp[0] + '分钟'
+ elif tmp[0] != '*':
+ if tmp[0].find('*') == -1:
+ cycle = '每小时的第' + tmp[0] + '分钟'
+ else:
+ cycle = '每隔' + tmp[0].split('/')[1] + '分钟'
+ else:
+ return None
+ return cycle
+
+ # 添加计划任务备注
+ def decode_cron_connand(self, tmp):
+ command = ''
+ for i in range(len(tmp)):
+ if i < 5: continue
+ command += tmp[i] + ' '
+ ps = '未知任务'
+ if command.find('/www/server/cron') != -1:
+ ps = '通过面板添加的计划任务'
+ elif command.find('.acme.sh') != -1:
+ ps = '基于acme.sh的证书续签任务'
+ elif command.find('certbot-auto renew') != -1:
+ ps = '基于certbot的证书续签任务'
+
+ tmpScript = command.split('>')[0].strip()
+ filename = tmpScript.replace('"', '').split()[0]
+ # if not os.path.exists(filename): filename = '';
+ return command.strip(), ps, filename
+
+ # 外部接口,获取计划任务列表
+ def get_cron_list(self, get = {}):
+ filename = self.get_cron_file()
+ cronList = []
+ if not os.path.exists(filename):
+ return cronList
+ tmpList = mw.readFile(filename).split("\n")
+ for c in tmpList:
+ c = c.strip()
+ if c.startswith('#'): continue
+ tmp = c.split(' ')
+ if len(tmp) < 6: continue
+ cronInfo = {}
+ cronInfo['cycle'] = self.decode_cron_cycle(tmp)
+ if not cronInfo['cycle']: continue
+ ctmp = self.decode_cron_connand(tmp)
+ cronInfo['command'] = c
+ cronInfo['ps'] = ctmp[1]
+ cronInfo['exe'] = ctmp[2]
+ cronInfo['test'] = ctmp[0]
+ cronList.append(cronInfo)
+ if 'search' in get:
+ if get['search'] != '':
+ cronList = self.search_cron(cronList, get['search'])
+ return cronList
+
+ def search_cron(self, data, search):
+ try:
+ ldata = []
+ for i in data:
+ if search in i['command'] or search in i['cycle'] or search in i['ps']:
+ ldata.append(i)
+ return ldata
+ except:
+ return data
+
+ # 重启cron服务
+ def crondReload(self):
+ if os.path.exists('/etc/init.d/crond'):
+ mw.execShell('/etc/init.d/crond reload')
+ elif os.path.exists('/etc/init.d/cron'):
+ mw.execShell('service cron restart')
+ else:
+ mw.execShell("systemctl reload crond")
+
+ # 外部接口,删除计划任务
+ def remove_cron(self, get):
+ if not 'index' in get:
+ return mw.returnData(False, '参数不存在[index]!')
+
+ index = int(get['index'])
+ cronList = self.get_cron_list({})
+ if index > len(cronList) + 1: return mw.returnData(False, '指定任务不存在!')
+ toCron = []
+ for i in range(len(cronList)):
+ if i == index: continue
+ toCron.append(cronList[i]['command'])
+ cronStr = "\n".join(toCron) + "\n\n"
+ filename = self.get_cron_file()
+ mw.writeFile(filename, cronStr)
+ mw.execShell("chmod 600 " + filename)
+ self.crondReload()
+ return mw.returnData(True, '删除成功!')
+
+ # 外部接口 强制结束会话
+ def pkill_session(self, get= {}):
+ if not 'pts' in get:
+ return mw.returnData(False, '缺少参数!')
+ mw.execShell("pkill -kill -t " + get['pts'])
+ return mw.returnData(True, '已强行结束会话[' + get['pts'] + ']')
+
+ # 获取当前会话
+ def get_who(self, get = {}):
+ whoTmp = mw.execShell('who')[0]
+ tmpList = whoTmp.split("\n")
+ whoList = []
+ for w in tmpList:
+ tmp = w.split()
+ if len(tmp) < 5: continue
+ whoInfo = {}
+ whoInfo['user'] = tmp[0]
+ whoInfo['pts'] = tmp[1]
+ whoInfo['date'] = tmp[2] + ' ' + tmp[3]
+ whoInfo['ip'] = tmp[4].replace('(', '').replace(')', '')
+ if len(tmp) > 5:
+ whoInfo['date'] = tmp[2] + ' ' + tmp[3] + ' ' + tmp[4]
+ whoInfo['ip'] = tmp[5].replace('(', '').replace(')', '')
+ whoList.append(whoInfo)
+ if hasattr(get, 'search'):
+ if get.search != '':
+ whoList = self.search_who(whoList, get.search)
+ return whoList
+
+ def test_cpu(self):
+ pid = 43046
+ p = psutil.Process(pid)
+ tmp = {}
+ self.new_info['cpu_time'] = self.get_cpu_time()
+ self.new_info['time'] = time.time()
+ with p.oneshot():
+ p_cpus = p.cpu_times()
+ print(p_cpus)
+
+ tmp['cpu_percent'] = self.get_cpu_percent(str(pid), p_cpus, self.new_info['cpu_time'])
+ print(tmp['cpu_percent'])
+
+
+def get_network_list(args = {}):
+ return mainClass.instance().get_network_list(args)
+
+def get_process_list(args = {}):
+ try:
+ return mainClass.instance().get_process_list(args)
+ except Exception as e:
+ return str(e)
+
+
+def kill_process(args = {}):
+ return mainClass.instance().kill_process(args)
+
+def kill_process_all(args = {}):
+ if not 'pid' in args:
+ return mw.returnData(False, '缺少参数!')
+ return mainClass.instance().kill_process_all(int(args['pid']))
+
+def set_meter_head(args = {}):
+ return mainClass.instance().set_meter_head(args)
+
+def remove_service(args = {}):
+ return mainClass.instance().remove_service(args)
+
+def set_runlevel_state(args = {}):
+ return mainClass.instance().set_runlevel_state(args)
+
+def get_service_list(args = {}):
+ return mainClass.instance().get_service_list(args)
+
+def get_run_list(args = {}):
+ return mainClass.instance().get_run_list(args)
+
+def get_cron_list(args = {}):
+ return mainClass.instance().get_cron_list(args)
+
+def remove_cron(args = {}):
+ return mainClass.instance().remove_cron(args)
+
+def pkill_session(args = {}):
+ return mainClass.instance().pkill_session(args)
+
+def get_who(args = {}):
+ return mainClass.instance().get_who(args)
+
+def get_process_info(args = {}):
+ return mainClass.instance().get_process_info(args)
+
+def get_user_list(args = {}):
+ return mainClass.instance().get_user_list(args)
+
+def remove_user(args = {}):
+ return mainClass.instance().remove_user(args)
+
+# if __name__ == "__main__":
+ # print(mc_instance.get_process_list())
+ # print(mc_instance.get_network_list())
+ # print(mc_instance.get_process_info({'pid':66647}))
+ # for x in range(10):
+ # mc_instance.test_cpu()
+ # time.sleep(1)
+
+ # mc_instance.test_cpu()
+ # time.sleep(1)
+ # mc_instance.test_cpu()
diff --git a/plugins/tgbot/ico.png b/plugins/tgbot/ico.png
new file mode 100644
index 000000000..9416145b2
Binary files /dev/null and b/plugins/tgbot/ico.png differ
diff --git a/plugins/tgbot/index.html b/plugins/tgbot/index.html
new file mode 100755
index 000000000..973239f62
--- /dev/null
+++ b/plugins/tgbot/index.html
@@ -0,0 +1,26 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/tgbot/index.py b/plugins/tgbot/index.py
new file mode 100755
index 000000000..0be333a8e
--- /dev/null
+++ b/plugins/tgbot/index.py
@@ -0,0 +1,377 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import base64
+
+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 'tgbot'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getInitDFile():
+ if app_debug:
+ return '/tmp/' + getPluginName()
+ return '/etc/init.d/' + getPluginName()
+
+
+def getConfigData():
+ cfg_path = getServerDir() + "/data.cfg"
+ if not os.path.exists(cfg_path):
+ mw.writeFile(cfg_path, '{}')
+ t = mw.readFile(cfg_path)
+ return json.loads(t)
+
+
+def writeConf(data):
+ cfg_path = getServerDir() + "/data.cfg"
+ mw.writeFile(cfg_path, json.dumps(data))
+ return True
+
+
+def getExtCfg():
+ cfg_path = getServerDir() + "/extend.cfg"
+ if not os.path.exists(cfg_path):
+ mw.writeFile(cfg_path, '{}')
+ t = mw.readFile(cfg_path)
+ return json.loads(t)
+
+
+def writeExtCfg(data):
+ cfg_path = getServerDir() + "/extend.cfg"
+ return mw.writeFile(cfg_path, json.dumps(data))
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
+ return path
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ if t.strip() == '':
+ tmp = []
+ else:
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ 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 status():
+ data = mw.execShell(
+ "ps -ef|grep tgbot |grep -v grep | grep -v mdserver-web | awk '{print $2}'")
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def initDreplace():
+
+ file_tpl = getInitDTpl()
+ service_path = mw.getServerDir()
+ app_path = service_path + '/' + getPluginName()
+
+ initD_path = getServerDir() + '/init.d'
+ if not os.path.exists(initD_path):
+ os.mkdir(initD_path)
+ file_bin = initD_path + '/' + getPluginName()
+
+ # initd replace
+ # if not os.path.exists(file_bin):
+ content = mw.readFile(file_tpl)
+ content = content.replace('{$SERVER_PATH}', service_path + '/mdserver-web')
+ content = content.replace('{$APP_PATH}', app_path)
+
+ mw.writeFile(file_bin, content)
+ mw.execShell('chmod +x ' + file_bin)
+
+ # systemd
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/tgbot.service'
+ systemServiceTpl = getPluginDir() + '/init.d/tgbot.service.tpl'
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ service_path = mw.getServerDir()
+ se_content = mw.readFile(systemServiceTpl)
+ se_content = se_content.replace('{$APP_PATH}', app_path)
+ mw.writeFile(systemService, se_content)
+ mw.execShell('systemctl daemon-reload')
+
+ return file_bin
+
+
+def tbOp(method):
+ file = initDreplace()
+
+ if not mw.isAppleSystem():
+ data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+ data = mw.execShell(file + ' ' + method)
+ # print(data)
+ if data[1] == '':
+ return 'ok'
+ return 'ok'
+
+
+def start():
+ return tbOp('start')
+
+
+def stop():
+ return tbOp('stop')
+
+
+def restart():
+ status = tbOp('restart')
+ return status
+
+
+def reload():
+
+ tgbot_tpl = getPluginDir() + '/startup/tgbot.py'
+ tgbot_dst = getServerDir() + '/tgbot.py'
+
+ content = mw.readFile(tgbot_tpl)
+ mw.writeFile(tgbot_dst, content)
+
+ tgpush_tpl = getPluginDir() + '/startup/tgpush.py'
+ tgpush_dst = getServerDir() + '/tgpush.py'
+
+ content = mw.readFile(tgpush_tpl)
+ mw.writeFile(tgpush_dst, content)
+
+ ext_src = getPluginDir() + '/startup/extend'
+ ext_dst = getServerDir()
+
+ mw.execShell('cp -rf ' + ext_src + ' ' + ext_dst)
+
+ return tbOp('restart')
+
+
+def initdStatus():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status ' + \
+ getPluginName() + ' | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+
+def initdInstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl enable ' + getPluginName())
+ return 'ok'
+
+
+def initdUinstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl disable ' + getPluginName())
+ return 'ok'
+
+
+def getBotConf():
+ data = getConfigData()
+ if 'bot' in data:
+
+ return mw.returnJson(True, 'ok', data['bot'])
+ return mw.returnJson(False, 'ok', {})
+
+
+def setBotConf():
+ args = getArgs()
+ data_args = checkArgs(args, ['app_token'])
+ if not data_args[0]:
+ return data_args[1]
+
+ data = getConfigData()
+ args['app_token'] = base64.b64decode(args['app_token']).decode('ascii')
+ data['bot'] = args
+ writeConf(data)
+
+ return mw.returnJson(True, '保存成功!', [])
+
+
+def installPreInspection():
+ i = sys.version_info
+ if i[0] < 3 or i[1] < 7:
+ return "telebot在python小于3.7无法正常使用"
+ return 'ok'
+
+
+def uninstallPreInspection():
+ stop()
+ return "请手动删除 rm -rf {}".format(getServerDir())
+
+
+def getExtCfgByName(name):
+ elist = getExtCfg()
+ for x in elist:
+ if x['name'] == name:
+ return x
+ return None
+
+
+def botExtList():
+
+ args = getArgs()
+ data_args = checkArgs(args, ['p'])
+ if not data_args[0]:
+ return data_args[1]
+
+ ext_path = getServerDir() + '/extend'
+ if not os.path.exists(ext_path):
+ return mw.returnJson(False, 'ok', [])
+ elist_source = os.listdir(ext_path)
+
+ elist = []
+ for e in elist_source:
+ if e.endswith('py'):
+ elist.append(e)
+
+ page = int(args['p'])
+ page_size = 5
+
+ make_ext_list = []
+ for ex in elist:
+ tmp = {}
+ tmp['name'] = ex
+ edata = getExtCfgByName(ex)
+ if edata:
+ tmp['status'] = edata['status']
+ else:
+ tmp['status'] = 'stop'
+
+ tmp['tag'] = ex.split('_')[0]
+ make_ext_list.append(tmp)
+
+ writeExtCfg(make_ext_list)
+ dlist_sum = len(make_ext_list)
+
+ page_start = int((page - 1) * page_size)
+ page_end = page_start + page_size
+
+ if page_end >= dlist_sum:
+ ret_data = make_ext_list[page_start:]
+ else:
+ ret_data = make_ext_list[page_start:page_end]
+
+ data = {}
+ data['data'] = ret_data
+ data['args'] = args
+ data['list'] = mw.getPage(
+ {'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'botExtListP'})
+
+ return mw.returnJson(True, 'ok', data)
+
+
+def setExtStatus():
+ args = getArgs()
+ data_args = checkArgs(args, ['name', 'status'])
+ if not data_args[0]:
+ return data_args[1]
+
+ elist = getExtCfg()
+ name = args['name']
+ status = args['status']
+ for x in range(len(elist)):
+ if elist[x]['name'] == name:
+ elist[x]['status'] = status
+ break
+
+ writeExtCfg(elist)
+
+ action = '开启'
+ if status == 'stop':
+ action = '关闭'
+
+ return mw.returnJson(True, action + '[' + name + ']扩展成功')
+
+
+def runLog():
+ p = getServerDir() + '/task.log'
+ return p
+
+
+if __name__ == "__main__":
+ 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 == 'uninstall_pre_inspection':
+ print(uninstallPreInspection())
+ elif func == 'get_bot_conf':
+ print(getBotConf())
+ elif func == 'set_bot_conf':
+ print(setBotConf())
+ elif func == 'bot_ext_list':
+ print(botExtList())
+ elif func == 'set_ext_status':
+ print(setExtStatus())
+ elif func == 'run_log':
+ print(runLog())
+
+ else:
+ print('error')
diff --git a/plugins/tgbot/info.json b/plugins/tgbot/info.json
new file mode 100755
index 000000000..6490e66ef
--- /dev/null
+++ b/plugins/tgbot/info.json
@@ -0,0 +1,20 @@
+{
+ "sort": 7,
+ "ps": "简单Telegram机器人",
+ "name": "tgbot",
+ "title": "tgbot",
+ "shell": "install.sh",
+ "versions":["1.0"],
+ "tip": "soft",
+ "checks": "server/tgbot",
+ "path": "server/tgbot",
+ "install_pre_inspection":true,
+ "uninstall_pre_inspection":true,
+ "display": 1,
+ "author": "midoks",
+ "date": "2023-03-06",
+ "home": "https://core.telegram.org/bots/api",
+ "depend_doc1":"https://github.com/eternnoir/pyTelegramBotAPI",
+ "type": 0,
+ "pid": "5"
+}
\ No newline at end of file
diff --git a/plugins/tgbot/init.d/tgbot.service.tpl b/plugins/tgbot/init.d/tgbot.service.tpl
new file mode 100644
index 000000000..04ee1b952
--- /dev/null
+++ b/plugins/tgbot/init.d/tgbot.service.tpl
@@ -0,0 +1,14 @@
+[Unit]
+Description=Tgbot Service
+After=network.target
+
+[Service]
+Type=forking
+ExecStart={$APP_PATH}/init.d/tgbot start
+ExecStop={$APP_PATH}/init.d/tgbot stop
+ExecReload={$APP_PATH}/init.d/tgbot reload
+KillMode=process
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/plugins/tgbot/init.d/tgbot.tpl b/plugins/tgbot/init.d/tgbot.tpl
new file mode 100644
index 000000000..55e6097f2
--- /dev/null
+++ b/plugins/tgbot/init.d/tgbot.tpl
@@ -0,0 +1,95 @@
+#!/bin/sh
+# chkconfig: 2345 55 25
+# description: Tgbot Service
+
+### BEGIN INIT INFO
+# Provides: Tgbot
+# Required-Start: $all
+# Required-Stop: $all
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts Tgbot
+# Description: starts the MDW-Web
+### END INIT INFO
+
+# Simple Tgbot init.d script conceived to work on Linux systems
+# as it does use of the /proc filesystem.
+
+PATH=/usr/local/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export LANG=en_US.UTF-8
+
+
+mw_path={$SERVER_PATH}
+PATH=$PATH:$mw_path/bin
+
+if [ -f $mw_path/bin/activate ];then
+ source $mw_path/bin/activate
+fi
+
+tg_start(){
+
+ isStart=`ps -ef|grep 'tgbot.py' |grep -v grep | awk '{print $2}'`
+ if [ "$isStart" == '' ];then
+ echo -e "starting tgbot... \c"
+ cd $mw_path
+ python3 {$APP_PATH}/tgbot.py >> {$APP_PATH}/task.log &
+ python3 {$APP_PATH}/tgpush.py >> {$APP_PATH}/push.log &
+ isStart=""
+ while [[ "$isStart" == "" ]];
+ do
+ echo -e ".\c"
+ sleep 0.5
+ isStart=`ps -ef|grep 'tgbot.py' |grep -v grep | awk '{print $2}'`
+ let n+=1
+ if [ $n -gt 20 ];then
+ break;
+ fi
+ done
+ if [ "$isStart" == '' ];then
+ echo -e "\033[31mfailed\033[0m"
+ echo -e "\033[31mError: tgbot service startup failed.\033[0m"
+ return;
+ fi
+ echo -e "\033[32mdone\033[0m"
+ else
+ echo "starting tgbot...(pid $(echo $isStart)) already running"
+ fi
+}
+
+
+tg_stop(){
+ echo -e "stopping tgbot ... \c";
+ arr=`ps aux|grep 'tgbot.py'|grep -v grep|awk '{print $2}'`
+ for p in ${arr[@]}
+ do
+ kill -9 $p > /dev/null 2>&1
+ done
+ echo -e "\033[32mdone\033[0m"
+
+
+ echo -e "stopping tgpush ... \c";
+ arr=`ps aux|grep 'tgpush.py'|grep -v grep|awk '{print $2}'`
+ for p in ${arr[@]}
+ do
+ kill -9 $p > /dev/null 2>&1
+ done
+ echo -e "\033[32mdone\033[0m"
+}
+
+case "$1" in
+ start)
+ tg_start
+ ;;
+ stop)
+ tg_stop
+ ;;
+ restart|reload)
+ tg_stop
+ sleep 0.3
+ tg_start
+ ;;
+ *)
+ echo "Please use start or stop as first argument"
+ ;;
+esac
+
diff --git a/plugins/tgbot/install.sh b/plugins/tgbot/install.sh
new file mode 100755
index 000000000..31d91f8e0
--- /dev/null
+++ b/plugins/tgbot/install.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+VERSION=$2
+
+# pip3 install ccxt
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+pip3 install pyTelegramBotAPI
+pip3 install telebot
+
+Install_App()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/source
+ mkdir -p $serverPath/tgbot
+ echo "${VERSION}" > $serverPath/tgbot/version.pl
+
+ cp -rf ${rootPath}/plugins/tgbot/startup/* $serverPath/tgbot
+
+ cd ${rootPath} && python3 ${rootPath}/plugins/tgbot/index.py start
+ cd ${rootPath} && python3 ${rootPath}/plugins/tgbot/index.py initd_install
+ echo '安装完成'
+}
+
+Uninstall_App()
+{
+ if [ -f /usr/lib/systemd/system/tgbot.service ];then
+ systemctl stop tgbot
+ systemctl disable tgbot
+ rm -rf /usr/lib/systemd/system/tgbot.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/tgbot/initd/tgbot ];then
+ $serverPath/tgbot/initd/tgbot stop
+ fi
+
+ rm -rf $serverPath/tgbot
+ echo "Uninstall_redis"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/tgbot/js/tgbot.js b/plugins/tgbot/js/tgbot.js
new file mode 100755
index 000000000..30e53c214
--- /dev/null
+++ b/plugins/tgbot/js/tgbot.js
@@ -0,0 +1,148 @@
+function appPost(method, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'tgbot';
+ req_data['func'] = method;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function appPostCallbak(method, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'tgbot';
+ req_data['func'] = method;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function botConf(){
+ appPost('get_bot_conf','',function(data){
+ var rdata = $.parseJSON(data.data);
+ var app_token = 'app_token';
+ if(rdata['status']){
+ db_data = rdata['data'];
+ app_token = db_data['app_token'];
+
+ }
+
+ var mlist = '';
+ mlist += 'app_token 必填写
'
+ var option = '\
+ \
+ ' + mlist + '\
+
\
+ 保存 \
+
\
+
';
+ $(".soft-man-con").html(option);
+ });
+}
+
+function submitBotConf(){
+ var pull_data = {};
+ pull_data['app_token'] = base64_encode($('input[name="app_token"]').val());
+ appPost('set_bot_conf',pull_data,function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata['msg'],{icon:rdata['status']?1:2,time:2000,shade: [0.3, '#000']});
+ });
+}
+
+
+function botExtList(){
+ var body = '\
+
\
+ \
+ \
+ 脚本 \
+ 类型 \
+ 状态 \
+ \
+ \
+ \
+
\
+ \
+
';
+ $('.soft-man-con').html(body);
+ botExtListP(1);
+}
+
+function setBotExtStatus(name,status){
+ appPost('set_ext_status',{'name':name,'status':status}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ layer.msg(rdata['msg'],);
+ showMsg(rdata['msg'], function(){
+ botExtListP(1);
+ },{icon:rdata['status']?1:2,shade: [0.3, '#000']},2000);
+ });
+}
+
+function botExtListP(p=1){
+ appPost('bot_ext_list',{'p':p}, function(rdata){
+ // console.log(rdata);
+ var rdata = $.parseJSON(rdata.data);
+ // console.log(rdata);
+ var tBody = '';
+
+ if (!rdata.status && rdata.data.length == 0 ){
+ var tBody = '无数据
';
+ } else{
+ var ldata = rdata.data.data;
+ for (var i = 0; i < ldata.length; i++) {
+ tBody += ''
+ tBody += ''+ldata[i]['name']+' ';
+ tBody += ''+ldata[i]['tag']+' ';
+
+ if (ldata[i]['status'] == 'start'){
+ tBody += ' ';
+ } else{
+ tBody += ' ';
+ }
+ tBody +=' ';
+ }
+ }
+
+ $('#ext_list').html(tBody);
+ $('#ext_list_page').html(rdata.data.list);
+
+ $('#ext_list .ext_status').click(function(){
+ var name = $(this).parent().parent().data('name');
+ var status = 'stop';
+ if ($(this).hasClass('glyphicon-pause')){
+ status = 'start';
+ }
+ setBotExtStatus(name,status);
+ });
+ });
+}
diff --git a/plugins/tgbot/startup/extend/init_cmd.py b/plugins/tgbot/startup/extend/init_cmd.py
new file mode 100644
index 000000000..1efb672d5
--- /dev/null
+++ b/plugins/tgbot/startup/extend/init_cmd.py
@@ -0,0 +1,32 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import base64
+import threading
+
+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
+
+import telebot
+from telebot import types
+from telebot.util import quick_markup
+
+
+def init(bot):
+ bot.delete_my_commands(scope=None, language_code=None)
+ bot.set_my_commands(
+ commands=[
+ telebot.types.BotCommand("start", "查看帮助信息"),
+ telebot.types.BotCommand("faq", "BBS帮助"),
+ telebot.types.BotCommand("music", "搜索网易音乐"),
+ ],
+ )
diff --git a/plugins/tgbot/startup/extend/push_ad.py b/plugins/tgbot/startup/extend/push_ad.py
new file mode 100644
index 000000000..19cdbc82c
--- /dev/null
+++ b/plugins/tgbot/startup/extend/push_ad.py
@@ -0,0 +1,94 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import base64
+import threading
+
+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
+
+import telebot
+from telebot import types
+from telebot.util import quick_markup
+
+# 广告推送实例
+
+
+chat_id = -1001578009023
+# chat_id = 5568699210
+
+
+def send_msg(bot, tag='ad', trigger_time=300):
+ # 信号只在一个周期内执行一次|start
+ lock_file = mw.getServerDir() + '/tgbot/lock.json'
+ if not os.path.exists(lock_file):
+ mw.writeFile(lock_file, '{}')
+
+ lock_data = json.loads(mw.readFile(lock_file))
+ if tag in lock_data:
+ diff_time = time.time() - lock_data[tag]['do_time']
+ if diff_time >= trigger_time:
+ lock_data[tag]['do_time'] = time.time()
+ else:
+ return False, 0, 0
+ else:
+ lock_data[tag] = {'do_time': time.time()}
+ mw.writeFile(lock_file, json.dumps(lock_data))
+ # 信号只在一个周期内执行一次|end
+ keyboard = [
+ [
+ types.InlineKeyboardButton(
+ text="高价收一切流量 @caifutong555", url='https://t.me/caifutong555')
+ ],
+ [
+ types.InlineKeyboardButton(
+ text="18+资源采集", url='https://ckzy1.com')
+ ],
+ [
+ types.InlineKeyboardButton(
+ text="代付-代实名-备案域名-国际云服务器", url='https://t.me/gjgzs2022')
+ ],
+ [
+ types.InlineKeyboardButton(
+ text="实名认证/过人脸🕵️♀️各种账号处理✅", url='https://t.me/niuniu234')
+ ],
+ [
+ types.InlineKeyboardButton(
+ text="官网", url='https://github.com/midoks/mdserver-web'),
+ types.InlineKeyboardButton(
+ text="💎DigitalVirt(赞助商)", url='https://digitalvirt.com/aff.php?aff=154')
+ ],
+ [
+ types.InlineKeyboardButton(
+ text="论坛", url='https://bbs.midoks.icu'),
+ types.InlineKeyboardButton(
+ text="搜索", url='https://bbs.midoks.icu/search.php'),
+ types.InlineKeyboardButton(
+ text="@ME", url='tg://user?id=5568699210'),
+ types.InlineKeyboardButton(
+ text="300rmb/月", url='tg://user?id=5568699210')
+ ]
+ ]
+ markup = types.InlineKeyboardMarkup(keyboard)
+ image_file = mw.getPluginDir() + '/tgbot/static/image/ad.png'
+
+ telebot_image = telebot.types.InputFile(image_file)
+ msg = bot.send_photo(chat_id, telebot_image, reply_markup=markup)
+
+ # print(msg.message_id)
+ time.sleep(5 * 60)
+ del_msg = bot.delete_message(chat_id=chat_id, message_id=msg.message_id)
+ # print(del_msg)
+
+
+def run(bot):
+ send_msg(bot, 'ad', 1 * 60 * 60)
diff --git a/plugins/tgbot/startup/extend/push_bbs_ntid.py b/plugins/tgbot/startup/extend/push_bbs_ntid.py
new file mode 100644
index 000000000..88411fe38
--- /dev/null
+++ b/plugins/tgbot/startup/extend/push_bbs_ntid.py
@@ -0,0 +1,113 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import base64
+import threading
+
+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
+
+import telebot
+from telebot import types
+from telebot.util import quick_markup
+
+# 推送最新的帖子
+
+chat_id = -1001578009023
+# chat_id = 5568699210
+
+
+def writeLog(log_str):
+ if __name__ == "__main__":
+ print(log_str)
+
+ now = mw.getDateFromNow()
+ log_file = mw.getServerDir() + '/tgbot/task.log'
+ mw.writeFileLog(now + ':' + log_str, log_file, limit_size=5 * 1024)
+ return True
+
+
+def get_newest_tid():
+
+ api_new = 'https://bbs.midoks.icu/plugin.php?id=external_api&f=bbs_newest'
+ api_next = 'https://bbs.midoks.icu/plugin.php?id=external_api&f=bbs_next_tid&tid='
+
+ tid_push = mw.getServerDir() + '/tgbot/bbs_newest_push.json'
+
+ if not os.path.exists(tid_push):
+ data = mw.httpGet(api_new)
+ data = json.loads(data)
+ if data['code'] == 0:
+ tid = data['data'][0]['tid']
+ mw.writeFile(tid_push, tid)
+ return True, data['data'][0]
+
+ tid = mw.readFile(tid_push)
+ data = mw.httpGet(api_next + tid)
+ data = json.loads(data)
+ if data['code'] == 0 and len(data['data']) > 0:
+ # print(data)
+ tid = data['data'][0]['tid']
+ mw.writeFile(tid_push, tid)
+ return True, data['data'][0]
+ return False, None
+
+
+def send_msg(bot, tag='ad', trigger_time=300):
+ # 信号只在一个周期内执行一次|start
+ lock_file = mw.getServerDir() + '/tgbot/lock.json'
+ if not os.path.exists(lock_file):
+ mw.writeFile(lock_file, '{}')
+
+ lock_data = json.loads(mw.readFile(lock_file))
+ if tag in lock_data:
+ diff_time = time.time() - lock_data[tag]['do_time']
+ if diff_time >= trigger_time:
+ lock_data[tag]['do_time'] = time.time()
+ else:
+ return False, 0, 0
+ else:
+ lock_data[tag] = {'do_time': time.time()}
+ mw.writeFile(lock_file, json.dumps(lock_data))
+ # 信号只在一个周期内执行一次|end
+
+ yes, info = get_newest_tid()
+ if yes:
+ url = 'https://bbs.midoks.icu/thread-' + info['tid'] + '-1-1.html'
+ keyboard = [
+ [
+ types.InlineKeyboardButton(text=info['subject'], url=url)
+ ],
+ [
+ types.InlineKeyboardButton(
+ text="论坛", url='https://bbs.midoks.icu'),
+ types.InlineKeyboardButton(
+ text="搜索", url='https://bbs.midoks.icu/search.php')
+ ]
+ ]
+ markup = types.InlineKeyboardMarkup(keyboard)
+ bot.send_message(
+ chat_id, "由【" + info['author'] + "】发帖!", reply_markup=markup)
+
+
+def run(bot):
+
+ try:
+ send_msg(bot, 'push_bbs_newest_tid', 300)
+ except Exception as e:
+ writeLog('-----push_bbs_newest_tid error start -------')
+ print(mw.getTracebackInfo())
+ writeLog('-----push_bbs_newest_tid error start -------')
+
+
+if __name__ == "__main__":
+ print(get_newest_tid())
diff --git a/plugins/tgbot/startup/extend/push_notice_msg.py b/plugins/tgbot/startup/extend/push_notice_msg.py
new file mode 100644
index 000000000..c109fb95c
--- /dev/null
+++ b/plugins/tgbot/startup/extend/push_notice_msg.py
@@ -0,0 +1,116 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import base64
+import threading
+
+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
+
+import telebot
+from telebot import types
+from telebot.util import quick_markup
+
+# 轮播实例
+
+chat_id = -1001578009023
+# chat_id = 5568699210
+
+
+def writeLog(log_str):
+ if __name__ == "__main__":
+ print(log_str)
+
+ now = mw.getDateFromNow()
+ log_file = mw.getServerDir() + '/tgbot/task.log'
+ mw.writeFileLog(now + ':' + log_str, log_file, limit_size=5 * 1024)
+ return True
+
+
+def send_msg(bot, tag='ad', trigger_time=300):
+ # 信号只在一个周期内执行一次|start
+ lock_file = mw.getServerDir() + '/tgbot/lock.json'
+ if not os.path.exists(lock_file):
+ mw.writeFile(lock_file, '{}')
+
+ lock_data = json.loads(mw.readFile(lock_file))
+ if tag in lock_data:
+ diff_time = time.time() - lock_data[tag]['do_time']
+ if diff_time >= trigger_time:
+ lock_data[tag]['do_time'] = time.time()
+ else:
+ return False, 0, 0
+ else:
+ lock_data[tag] = {'do_time': time.time()}
+ mw.writeFile(lock_file, json.dumps(lock_data))
+ # 信号只在一个周期内执行一次|end
+
+ # https://t.me/gjgzs2022 | 22/m | @GJ_gzs
+ # 实名认证/过人脸🕵️♀️各种账号处理✅ | 30/m| next,6/30 | @nngzs
+ # 18+资源采集| 4/m | next,7/14 | @liuxingyu123
+
+ keyboard = [
+ [
+ types.InlineKeyboardButton(
+ text="高价收一切流量 @caifutong555", url='https://t.me/caifutong555')
+ ],
+ [
+ types.InlineKeyboardButton(
+ text="18+资源采集", url='https://ckzy1.com')
+ ],
+ [
+ types.InlineKeyboardButton(
+ text="代付-代实名-备案域名-国际云服务器", url='https://t.me/gjgzs2022')
+ ],
+ [
+ types.InlineKeyboardButton(
+ text="实名认证/过人脸🕵️♀️各种账号处理✅", url='https://t.me/niuniu234')
+ ],
+ [
+ types.InlineKeyboardButton(
+ text="官网", url='https://github.com/midoks/mdserver-web'),
+ types.InlineKeyboardButton(
+ text="💎DigitalVirt(赞助商)", url='https://digitalvirt.com/aff.php?aff=154')
+ ],
+ [
+ types.InlineKeyboardButton(
+ text="论坛", url='https://bbs.midoks.icu'),
+ types.InlineKeyboardButton(
+ text="搜索", url='https://bbs.midoks.icu/search.php'),
+ types.InlineKeyboardButton(
+ text="@ME", url='tg://user?id=5568699210'),
+ types.InlineKeyboardButton(
+ text="300RMB/月", url='tg://user?id=5568699210')
+ ]
+ ]
+ markup = types.InlineKeyboardMarkup(keyboard)
+
+ msg_notice = "由于在解决的问题的时候,不给信息,无法了解情况。以后不再群里回答技术问题。全部去论坛提问。在解决问题的过程中,可能需要面板信息,和SSH信息,如无法提供请不要提问。为了让群里都知晓。轮播一年!\n"
+ msg_notice += "为了不打扰双方,私聊解决问题先转1000U,否则无视!\n"
+ msg = bot.send_message(chat_id, msg_notice, reply_markup=markup)
+
+ # print(msg.message_id)
+ time.sleep(90)
+ try:
+ bot.delete_message(
+ chat_id=chat_id, message_id=msg.message_id)
+ except Exception as e:
+ pass
+
+
+def run(bot):
+ try:
+ send_msg(bot, 'notice_msg', 90)
+ except Exception as e:
+ writeLog('-----push_notice_msg error start -------')
+ print(mw.getTracebackInfo())
+ writeLog('-----push_notice_msg error start -------')
diff --git a/plugins/tgbot/startup/extend/readme.md b/plugins/tgbot/startup/extend/readme.md
new file mode 100755
index 000000000..d52224d88
--- /dev/null
+++ b/plugins/tgbot/startup/extend/readme.md
@@ -0,0 +1 @@
+push_*.py 识别为推送插件
diff --git a/plugins/tgbot/startup/extend/receive_faq.py b/plugins/tgbot/startup/extend/receive_faq.py
new file mode 100644
index 000000000..cbb1a2760
--- /dev/null
+++ b/plugins/tgbot/startup/extend/receive_faq.py
@@ -0,0 +1,217 @@
+import sys
+import io
+import os
+import time
+import re
+import json
+import base64
+import threading
+
+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
+
+import telebot
+from telebot import types
+from telebot.util import quick_markup
+
+
+def isThisCmd(cmd, msg):
+ clen = len(cmd)
+ msg_len = len(msg)
+ if msg_len < clen:
+ return False
+
+ check_msg = msg[0:clen]
+ if cmd == check_msg:
+ return True
+ return False
+
+
+def getReadCmd(cmd, msg):
+ clen = len(cmd)
+ msg_len = len(msg)
+ real_msg = msg[clen:]
+ return real_msg
+
+
+def getFaqKw(cmd):
+ matchObj = re.match(r'寻找【(.*?)】问题如下', cmd, re.M | re.I)
+ data = matchObj.groups()
+ if len(data) > 0:
+ return True, data[0]
+ return False, ''
+
+
+def searchHttpPage(kw='', p=1, size=1):
+ import urllib
+ kw = kw.strip()
+ kw = urllib.parse.quote_plus(kw)
+
+ api = 'https://bbs.midoks.icu/plugin.php?id=external_api&f=bbs_search&q=' + kw + \
+ '&size=' + str(size) + '&p=' + str(p)
+
+ # print('url', api)
+ data = mw.httpGet(api)
+ # print(data)
+ data = json.loads(data)
+ # print(data)
+ if data['code'] > -1:
+
+ alist = data['data']['list']
+ r = []
+ for x in alist:
+ tmp = {}
+ tmp['tid'] = x['tid']
+ tmp['subject'] = x['subject']
+ tmp['url'] = 'https://bbs.midoks.icu/thread-' + \
+ x['tid'] + '-1-1.html'
+ r.append(tmp)
+ data['data']['list'] = r
+ return data
+
+
+def searchFaq(bot, message, cmd_text):
+ # cmd_text = 'mw'
+ data = searchHttpPage(cmd_text, 1, 5)
+ if data['code'] == 0 and len(data['data']['list']) > 0:
+ keyboard = []
+
+ dlist = data['data']['list']
+ for x in dlist:
+ keyboard.append([types.InlineKeyboardButton(
+ text=x['subject'], url=x['url'])])
+
+ keyboard.append([
+ types.InlineKeyboardButton(
+ text="下一页", callback_data='bbs_next_page_2'),
+ types.InlineKeyboardButton(
+ text="第" + str(data['data']['p']) + "页,共" + str(data['data']['page_num']) + "页", callback_data='bbs_page_total')
+ ])
+
+ keyboard.append([types.InlineKeyboardButton(
+ text="关闭消息", callback_data='bbs_search_close')])
+
+ # print(keyboard)
+ markup = types.InlineKeyboardMarkup(keyboard)
+ bot.send_message(message.chat.id, "寻找【" +
+ cmd_text.strip() + "】问题如下:", reply_markup=markup)
+ else:
+ keyboard = [
+ [
+ types.InlineKeyboardButton(
+ text="论坛", url='https://bbs.midoks.icu'),
+ types.InlineKeyboardButton(
+ text="搜索", url='https://bbs.midoks.icu/search.php')
+ ],
+ [
+ types.InlineKeyboardButton(
+ text="关闭消息", callback_data='bbs_search_close')
+ ]
+
+ ]
+ markup = types.InlineKeyboardMarkup(keyboard)
+ bot.send_message(
+ message.chat.id, "未找到合适内容,请在官方论坛[bbs.midoks.icu]提问!", reply_markup=markup)
+
+ return True
+
+
+def searchDebug(bot, message, cmd_text):
+ searchFaq(bot, message, cmd_text)
+ return True
+
+
+def answer_callback_query(bot, call):
+
+ keyword = call.data
+
+ if keyword == 'bbs_search_close':
+ bot.delete_message(chat_id=call.message.chat.id,
+ message_id=call.message.message_id)
+ return
+
+ is_bbs_page = False
+ p = 1
+ if keyword.startswith('bbs_next_page'):
+ is_bbs_page = True
+ p = keyword.replace('bbs_next_page_', '')
+
+ if keyword.startswith('bbs_pre_page'):
+ is_bbs_page = True
+ p = keyword.replace('bbs_pre_page_', '')
+
+ # print("p", p)
+ if is_bbs_page:
+ is_match, cmd_text = getFaqKw(call.message.text)
+ if not is_match:
+ bot.edit_message_text(
+ chat_id=call.message.chat.id, message_id=call.message.message_id, text="出现错误!")
+ return
+
+ data = searchHttpPage(cmd_text, int(p), 5)
+
+ dlist = data['data']['list']
+ # print(data)
+ keyboard = []
+ for x in dlist:
+ keyboard.append([types.InlineKeyboardButton(
+ text=x['subject'], url=x['url'])])
+
+ page_nav = []
+ if int(data['data']['p']) > 1:
+ page_nav.append(types.InlineKeyboardButton(
+ text="上一页", callback_data='bbs_pre_page_' + str(int(p) - 1)))
+
+ if data['data']['page_num'] != data['data']['p']:
+ page_nav.append(types.InlineKeyboardButton(
+ text="下一页", callback_data='bbs_next_page_' + str(int(p) + 1)))
+
+ page_nav.append(types.InlineKeyboardButton(
+ text="第" + str(data['data']['p']) + "页,共" + str(data['data']['page_num']) + "页", callback_data='bbs_page_total'))
+
+ keyboard.append(page_nav)
+
+ keyboard.append([types.InlineKeyboardButton(
+ text="关闭消息", callback_data='bbs_search_close')])
+
+ markup = types.InlineKeyboardMarkup(keyboard)
+ bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
+ text=call.message.text, reply_markup=markup)
+
+
+def run(bot, message):
+ text_body = message.text
+
+ # 过滤URL
+ is_has_url = re.search(
+ '(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]', text_body)
+ if is_has_url:
+ return bot
+
+ # print(text_body)
+ if isThisCmd('/?:', text_body):
+ cmd_text = getReadCmd('/?:', text_body)
+ cmd_text = cmd_text.strip().strip(":")
+ if cmd_text == "":
+ return bot.send_message(message.chat.id, "搜索内容不能为空, 例如:/?: 数据库")
+ return searchFaq(bot, message, cmd_text)
+
+ if isThisCmd('/faq', text_body):
+ cmd_text = getReadCmd('/faq', text_body)
+ cmd_text = cmd_text.strip().strip(":")
+ if cmd_text == "":
+ return bot.send_message(message.chat.id, "搜索内容不能为空, 例如:/faq 数据库")
+ return searchFaq(bot, message, cmd_text)
+
+ return bot
+
+
+if __name__ == "__main__":
+ # print(isThisCmd('/?:', '/?:如何在安装面板'))
+ # print(getReadCmd('/?:', '/?:如何在安装面板'))
+ # print(searchHttpPage('mw'))
+ print(getFaqKw('寻找【mw】问题如下:'))
diff --git a/plugins/tgbot/startup/extend/receive_music163_search.py b/plugins/tgbot/startup/extend/receive_music163_search.py
new file mode 100644
index 000000000..f3a872021
--- /dev/null
+++ b/plugins/tgbot/startup/extend/receive_music163_search.py
@@ -0,0 +1,347 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import base64
+import threading
+
+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
+
+import telebot
+from telebot import types
+from telebot.util import quick_markup
+
+# 网易音乐搜索
+
+
+def isThisCmd(cmd, msg):
+ clen = len(cmd)
+ msg_len = len(msg)
+ if msg_len < clen:
+ return False
+
+ check_msg = msg[0:clen]
+ if cmd == check_msg:
+ return True
+ return False
+
+
+def getReadCmd(cmd, msg):
+ clen = len(cmd)
+ msg_len = len(msg)
+ real_msg = msg[clen:]
+ return real_msg
+
+
+def ip2long(ip):
+ import struct
+ import socket
+ return struct.unpack("!L", socket.inet_aton(ip))[0]
+
+
+def long2ip(longip):
+ import struct
+ import socket
+ return socket.inet_ntoa(struct.pack('!L', longip))
+
+
+def mt_rand(a, b):
+ import random
+ return random.randint(a, b)
+
+
+def httpPost(url, data, timeout=10):
+ """
+ 发送POST请求
+ @url 被请求的URL地址(必需)
+ @data POST参数,可以是字符串或字典(必需)
+ @timeout 超时时间默认60秒
+ return string
+ """
+ try:
+ import urllib.request
+ import ssl
+ try:
+ ssl._create_default_https_context = ssl._create_unverified_context
+ except:
+ pass
+
+ headers = {
+ 'Referer': 'https://music.163.com/',
+ 'Cookie': 'appver=8.2.30; os=iPhone OS; osver=15.0; EVNSM=1.0.0; buildver=2206; channel=distribution; machineid=iPhone13.3',
+ 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 CloudMusic/0.1.1 NeteaseMusic/8.2.30',
+ 'X-Real-IP': long2ip(mt_rand(1884815360, 1884890111)),
+ 'Accept': '*/*',
+ 'Accept-Language': 'zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4',
+ 'Connection': 'keep-alive',
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ }
+ data = urllib.parse.urlencode(data).encode('utf-8')
+ req = urllib.request.Request(url, data, headers=headers)
+ response = urllib.request.urlopen(req, timeout=timeout)
+ result = response.read()
+ if type(result) == bytes:
+ result = result.decode('utf-8')
+ return result
+ except Exception as ex:
+ return str(ex)
+
+
+def musicSearch(kw, page=1, page_size=5):
+ m_offset = (int(page) - 1) * int(page_size)
+ data = httpPost('http://music.163.com/api/cloudsearch/pc', {
+ 's': kw,
+ 'type': '1',
+ 'total': 'true',
+ 'limit': page_size,
+ 'offset': m_offset,
+ })
+ # data_a = json.loads(data)
+ # print(data)
+ # exit()
+ return json.loads(data)
+
+
+def musicSongD(mid):
+ data = httpPost('http://music.163.com/api/v3/song/detail/', {
+ 'c': '[{"id":' + str(mid) + ',"v":0}]',
+ })
+ # print(data)
+ return json.loads(data)
+
+
+def musicSongDataUrl(mid):
+ data = httpPost('http://music.163.com/api/song/enhance/player/url', {
+ 'br': 320 * 1000,
+ 'ids': [mid],
+ })
+ return json.loads(data)
+
+
+def writeLog(log_str):
+ if __name__ == "__main__":
+ print(log_str)
+
+ now = mw.getDateFromNow()
+ log_file = mw.getServerDir() + '/tgbot/task.log'
+ mw.writeFileLog(now + ':' + log_str, log_file, limit_size=5 * 1024)
+ return True
+
+
+def tgSearchMusic_t(cmd_text):
+ data = musicSearch(cmd_text, 1, 5)
+
+ if data['code'] == 200 and len(data['result']['songs']) > 0:
+ slist = data['result']['songs']
+ print(slist)
+ else:
+ keyboard = [
+ [
+ types.InlineKeyboardButton(
+ text="论坛", url='https://bbs.midoks.icu'),
+ types.InlineKeyboardButton(
+ text="搜索", url='https://bbs.midoks.icu/search.php')
+ ],
+ [
+ types.InlineKeyboardButton(
+ text="关闭消息", callback_data='bbs_search_close')
+ ]
+
+ ]
+ markup = types.InlineKeyboardMarkup(keyboard)
+ bot.send_message(
+ message.chat.id, "未找到合适内容,请在官方论坛[bbs.midoks.icu]提问!", reply_markup=markup)
+ return True
+
+
+def tgSearchMusic(bot, message, cmd_text):
+ import math
+ data = musicSearch(cmd_text, 1, 5)
+ if data['code'] == 200 and len(data['result']['songs']) > 0:
+ keyboard = []
+ slist = data['result']['songs']
+ page_total = math.ceil(data['result']['songCount'] / 5)
+
+ for x in slist:
+ author = ''
+ if len(x['ar']) > 0:
+ author = ' - ' + x['ar'][0]['name']
+ keyboard.append([types.InlineKeyboardButton(
+ text=x['name'] + author, callback_data='m163_id:' + str(x['id']))])
+
+ keyboard.append([
+ types.InlineKeyboardButton(
+ text="下一页", callback_data='m163_next_page_2'),
+ types.InlineKeyboardButton(
+ text="第1页,共" + str(page_total) + "页", callback_data='m163_page_total')
+ ])
+
+ keyboard.append([types.InlineKeyboardButton(
+ text="关闭消息", callback_data='m163_search_close')])
+
+ # print(keyboard)
+ markup = types.InlineKeyboardMarkup(keyboard)
+ bot.send_message(message.chat.id, "寻找【" +
+ cmd_text.strip() + "】歌曲如下:", reply_markup=markup)
+ else:
+ bot.send_message(message.chat.id, "未找到合适内容")
+ return True
+
+
+def getFaqKw(cmd):
+ matchObj = re.match(r'寻找【(.*?)】歌曲如下', cmd, re.M | re.I)
+ data = matchObj.groups()
+ if len(data) > 0:
+ return True, data[0]
+ return False, ''
+
+
+def cleanMusicFileExpire(dir):
+ pass
+
+
+def downloadAndUpMusic(bot, chat_id, mid, title):
+ import requests
+ murl_data = musicSongDataUrl(int(mid))
+ murl = murl_data['data'][0]['url']
+ def_dir = '/tmp/tgbot_music'
+ if not os.path.exists(def_dir):
+ os.mkdir(def_dir)
+
+ def_abs_path = def_dir + '/' + title + '.mp3'
+ # print('downloadAndUpMusic' + ":" + str(murl))
+ if murl:
+ msg_t = bot.send_message(chat_id, "已经获取资源URL,本地下载中...")
+ response = requests.get(murl)
+
+ with open(def_abs_path, "wb") as f:
+ f.write(response.content)
+
+ bot.edit_message_text(
+ chat_id=chat_id, message_id=msg_t.message_id, text="本地下载完,正在上传中...")
+
+ audio = open(def_abs_path, 'rb')
+ bot.send_audio(chat_id, audio)
+
+ bot.edit_message_text(
+ chat_id=chat_id, message_id=msg_t.message_id, text="上传结束...1s自动删除")
+
+ time.sleep(1)
+ bot.delete_message(chat_id=chat_id, message_id=msg_t.message_id)
+ else:
+ bot.send_message(chat_id, "无效资源")
+
+ if os.path.exists(def_abs_path):
+ os.remove(def_abs_path)
+
+ # cleanMusicFileExpire(def_dir)
+ return True
+
+
+def answer_callback_query(bot, call):
+ import math
+ keyword = call.data
+ # print(keyword)
+ if keyword == 'm163_search_close':
+ bot.delete_message(chat_id=call.message.chat.id,
+ message_id=call.message.message_id)
+ return
+
+ # 音乐下载
+ if keyword.startswith('m163_id:'):
+ t = keyword.split(":")
+ inline_keyboard = call.json['message'][
+ "reply_markup"]["inline_keyboard"]
+
+ def_file_name = 'demo'
+ for x in inline_keyboard:
+ # print(x)
+ if x[0]['callback_data'] == keyword:
+ def_file_name = x[0]['text']
+ # print(call.message)
+ downloadAndUpMusic(bot, call.message.chat.id, t[1], def_file_name)
+ bot.delete_message(chat_id=call.message.chat.id,
+ message_id=call.message.message_id)
+ return True
+
+ is_m163_page = False
+ p = 1
+ if keyword.startswith('m163_next_page'):
+ is_m163_page = True
+ p = keyword.replace('m163_next_page_', '')
+
+ if keyword.startswith('m163_pre_page'):
+ is_m163_page = True
+ p = keyword.replace('m163_pre_page_', '')
+
+ if is_m163_page:
+ is_match, cmd_text = getFaqKw(call.message.text)
+ if not is_match:
+ bot.edit_message_text(
+ chat_id=call.message.chat.id, message_id=call.message.message_id, text="出现错误!")
+ return
+
+ data = musicSearch(cmd_text, p, 5)
+
+ dlist = data['result']['songs']
+ page_total = math.ceil(data['result']['songCount'] / 5)
+
+ keyboard = []
+ for x in dlist:
+ author = ''
+ if len(x['ar']) > 0:
+ author = ' - ' + x['ar'][0]['name']
+ keyboard.append([types.InlineKeyboardButton(
+ text=x['name'] + author, callback_data='m163_id:' + str(x['id']))])
+
+ page_nav = []
+ if int(p) > 1:
+ page_nav.append(types.InlineKeyboardButton(
+ text="上一页", callback_data='m163_pre_page_' + str(int(p) - 1)))
+
+ if int(p) < page_total:
+ page_nav.append(types.InlineKeyboardButton(
+ text="下一页", callback_data='m163_next_page_' + str(int(p) + 1)))
+
+ page_nav.append(types.InlineKeyboardButton(
+ text="第" + str(p) + "页,共" + str(page_total) + "页", callback_data='m163_page_total'))
+
+ keyboard.append(page_nav)
+
+ keyboard.append([types.InlineKeyboardButton(
+ text="关闭消息", callback_data='m163_search_close')])
+
+ markup = types.InlineKeyboardMarkup(keyboard)
+ bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
+ text=call.message.text, reply_markup=markup)
+
+
+def run(bot, message):
+ text_body = message.text
+
+ if isThisCmd('/music', text_body):
+ cmd_text = getReadCmd('/music', text_body)
+ cmd_text = cmd_text.strip().strip(":")
+ if cmd_text == "":
+ return bot.send_message(message.chat.id, "搜索内容不能为空, 例如:/music 刀郎")
+ return tgSearchMusic(bot, message, cmd_text)
+
+ return bot
+
+
+if __name__ == '__main__':
+ # cleanMusicFileExpire("/tmp/tgbot_music")
+ # tgSearchMusic_t("一夜泥工")
+ # print(long2ip(mt_rand(1884815360, 1884890111)))
+ t = musicSongDataUrl(2063487880)
+ print(t['data'])
+ print("111")
diff --git a/plugins/tgbot/startup/tgbot.py b/plugins/tgbot/startup/tgbot.py
new file mode 100644
index 000000000..60b4a5546
--- /dev/null
+++ b/plugins/tgbot/startup/tgbot.py
@@ -0,0 +1,150 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import base64
+import threading
+
+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
+
+import telebot
+
+
+def getPluginName():
+ return 'tgbot'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+sys.path.append(getServerDir() + "/extend")
+
+
+def getConfigData():
+ cfg_path = getServerDir() + "/data.cfg"
+ if not os.path.exists(cfg_path):
+ mw.writeFile(cfg_path, '{}')
+ t = mw.readFile(cfg_path)
+ return json.loads(t)
+
+
+def writeConf(data):
+ cfg_path = getServerDir() + "/data.cfg"
+ mw.writeFile(cfg_path, json.dumps(data))
+ return True
+
+
+def getExtCfg():
+ cfg_path = getServerDir() + "/extend.cfg"
+ if not os.path.exists(cfg_path):
+ mw.writeFile(cfg_path, '{}')
+ t = mw.readFile(cfg_path)
+ return json.loads(t)
+
+
+def getStartExtCfgByTag(tag='push'):
+ # 获取开启的扩展
+ elist = getExtCfg()
+ rlist = []
+ for x in elist:
+ if x['tag'] == tag and x['status'] == 'start':
+ rlist.append(x)
+ return rlist
+
+
+def writeLog(log_str):
+ if __name__ == "__main__":
+ print(log_str)
+
+ now = mw.getDateFromNow()
+ log_file = getServerDir() + '/task.log'
+ mw.writeFileLog(now + ':' + log_str, log_file, limit_size=5 * 1024)
+ return True
+
+# start tgbot
+cfg = getConfigData()
+while True:
+ cfg = getConfigData()
+ if 'bot' in cfg and 'app_token' in cfg['bot']:
+ if cfg['bot']['app_token'] != '' and cfg['bot']['app_token'] != 'app_token':
+ break
+ writeLog('等待输入配置,填写app_token')
+ time.sleep(3)
+
+
+bot = telebot.TeleBot(cfg['bot']['app_token'])
+
+
+init_list = getStartExtCfgByTag('init')
+for p in init_list:
+ try:
+ script = p['name'].split('.')[0]
+ __import__(script).init(bot)
+ except Exception as e:
+ writeLog('-----init error start -------')
+ writeLog(mw.getTracebackInfo())
+ writeLog('-----init error end -------')
+
+
+@bot.message_handler(commands=['chat_id'])
+def hanle_get_chat_id(message):
+ bot.reply_to(message, message.chat.id)
+
+
+@bot.message_handler(func=lambda message: True)
+def all_message(message):
+ rlist = getStartExtCfgByTag('receive')
+ for r in rlist:
+ try:
+ script = r['name'].split('.')[0]
+ __import__(script).run(bot, message)
+ except Exception as e:
+ writeLog('-----all_message error start -------')
+ writeLog(mw.getTracebackInfo())
+ writeLog('-----all_message error end -------')
+
+
+@bot.callback_query_handler(func=lambda call: True)
+def callback_query_handler(call):
+ rlist = getStartExtCfgByTag('receive')
+ for r in rlist:
+ try:
+ script = r['name'].split('.')[0]
+ __import__(script).answer_callback_query(bot, call)
+ except Exception as e:
+ writeLog('-----callback_query_handler error start -------')
+ writeLog(mw.getTracebackInfo())
+ writeLog('-----callback_query_handler error end -------')
+
+
+def runBot(bot):
+ try:
+ bot.polling()
+ except Exception as e:
+ writeLog('-----runBot error start -------')
+ writeLog(str(e))
+ writeLog('-----runBot error end -------')
+ time.sleep(1)
+ runBot(bot)
+
+if __name__ == "__main__":
+
+ writeLog('启动成功')
+ runBot(bot)
+
+
+# asyncio.run(bot.polling())
diff --git a/plugins/tgbot/startup/tgpush.py b/plugins/tgbot/startup/tgpush.py
new file mode 100644
index 000000000..57e2cd6f4
--- /dev/null
+++ b/plugins/tgbot/startup/tgpush.py
@@ -0,0 +1,136 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import base64
+import threading
+
+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
+
+import telebot
+
+
+def getPluginName():
+ return 'tgbot'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+sys.path.append(getServerDir() + "/extend")
+
+
+def getConfigData():
+ cfg_path = getServerDir() + "/data.cfg"
+ if not os.path.exists(cfg_path):
+ mw.writeFile(cfg_path, '{}')
+ t = mw.readFile(cfg_path)
+ return json.loads(t)
+
+
+def writeConf(data):
+ cfg_path = getServerDir() + "/data.cfg"
+ mw.writeFile(cfg_path, json.dumps(data))
+ return True
+
+
+def getExtCfg():
+ cfg_path = getServerDir() + "/extend.cfg"
+ if not os.path.exists(cfg_path):
+ mw.writeFile(cfg_path, '{}')
+ t = mw.readFile(cfg_path)
+ return json.loads(t)
+
+
+def getStartExtCfgByTag(tag='push'):
+ # 获取开启的扩展
+ elist = getExtCfg()
+ rlist = []
+ for x in elist:
+ if x['tag'] == tag and x['status'] == 'start':
+ rlist.append(x)
+ return rlist
+
+
+def writeLog(log_str):
+ if __name__ == "__main__":
+ print(log_str)
+
+ now = mw.getDateFromNow()
+ log_file = getServerDir() + '/push.log'
+ mw.writeFileLog(now + ':' + log_str, log_file, limit_size=5 * 1024)
+ return True
+
+# start tgbot
+cfg = getConfigData()
+while True:
+ cfg = getConfigData()
+ if 'bot' in cfg and 'app_token' in cfg['bot']:
+ if cfg['bot']['app_token'] != '' and cfg['bot']['app_token'] != 'app_token':
+ break
+ writeLog('等待输入配置,填写app_token')
+ time.sleep(3)
+
+
+bot = telebot.TeleBot(cfg['bot']['app_token'])
+
+
+def runBotPushTask():
+ plist = getStartExtCfgByTag('push')
+ for p in plist:
+ try:
+ script = p['name'].split('.')[0]
+ __import__(script).run(bot)
+ except Exception as e:
+ writeLog('-----runBotPushTask error start -------')
+ writeLog(mw.getTracebackInfo())
+ writeLog('-----runBotPushTask error end -------')
+
+
+def botPush():
+ while True:
+ runBotPushTask()
+ time.sleep(1)
+
+
+def runBotPushOtherTask():
+ plist = getStartExtCfgByTag('other')
+ for p in plist:
+ try:
+ script = p['name'].split('.')[0]
+ __import__(script).run(bot)
+ except Exception as e:
+ writeLog('-----runBotPushOtherTask error start -------')
+ writeLog(mw.getTracebackInfo())
+ writeLog('-----runBotPushOtherTask error end -------')
+
+
+def botPushOther():
+ while True:
+ runBotPushOtherTask()
+ time.sleep(1)
+
+
+if __name__ == "__main__":
+
+ # 机器人推送任务
+ botPushTask = threading.Thread(target=botPush)
+ botPushTask.start()
+
+ # 机器人其他推送任务
+ botPushOtherTask = threading.Thread(target=botPushOther)
+ botPushOtherTask.start()
diff --git a/plugins/tgbot/static/image/ad.png b/plugins/tgbot/static/image/ad.png
new file mode 100644
index 000000000..32bcd144b
Binary files /dev/null and b/plugins/tgbot/static/image/ad.png differ
diff --git a/plugins/tgclient/ico.png b/plugins/tgclient/ico.png
new file mode 100644
index 000000000..9416145b2
Binary files /dev/null and b/plugins/tgclient/ico.png differ
diff --git a/plugins/tgclient/index.html b/plugins/tgclient/index.html
new file mode 100755
index 000000000..77da84f68
--- /dev/null
+++ b/plugins/tgclient/index.html
@@ -0,0 +1,27 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/tgclient/index.py b/plugins/tgclient/index.py
new file mode 100755
index 000000000..ec2eb7238
--- /dev/null
+++ b/plugins/tgclient/index.py
@@ -0,0 +1,375 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import base64
+
+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 'tgclient'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getInitDFile():
+ if app_debug:
+ return '/tmp/' + getPluginName()
+ return '/etc/init.d/' + getPluginName()
+
+
+def getConfigData():
+ cfg_path = getServerDir() + "/data.cfg"
+ if not os.path.exists(cfg_path):
+ mw.writeFile(cfg_path, '{}')
+ t = mw.readFile(cfg_path)
+ return json.loads(t)
+
+
+def writeConf(data):
+ cfg_path = getServerDir() + "/data.cfg"
+ mw.writeFile(cfg_path, json.dumps(data))
+ return True
+
+
+def getExtCfg():
+ cfg_path = getServerDir() + "/extend.cfg"
+ if not os.path.exists(cfg_path):
+ mw.writeFile(cfg_path, '{}')
+ t = mw.readFile(cfg_path)
+ return json.loads(t)
+
+
+def writeExtCfg(data):
+ cfg_path = getServerDir() + "/extend.cfg"
+ return mw.writeFile(cfg_path, json.dumps(data))
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
+ return path
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ if t.strip() == '':
+ tmp = []
+ else:
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ 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 status():
+ data = mw.execShell(
+ "ps -ef|grep tgclient |grep -v grep | grep -v mdserver-web | awk '{print $2}'")
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def initDreplace():
+
+ file_tpl = getInitDTpl()
+ service_path = mw.getServerDir()
+ app_path = service_path + '/' + getPluginName()
+
+ initD_path = getServerDir() + '/init.d'
+ if not os.path.exists(initD_path):
+ os.mkdir(initD_path)
+ file_bin = initD_path + '/' + getPluginName()
+
+ # initd replace
+ # if not os.path.exists(file_bin):
+ content = mw.readFile(file_tpl)
+ content = content.replace('{$SERVER_PATH}', service_path + '/mdserver-web')
+ content = content.replace('{$APP_PATH}', app_path)
+
+ mw.writeFile(file_bin, content)
+ mw.execShell('chmod +x ' + file_bin)
+
+ pyMainTplContent = mw.readFile(getPluginDir() + '/startup/tgclient.py')
+ toPyMainPath = mw.getServerDir() + '/tgclient.py'
+ mw.writeFile(toPyMainPath, pyMainTplContent)
+
+ # systemd
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/tgclient.service'
+ systemServiceTpl = getPluginDir() + '/init.d/tgclient.service.tpl'
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ service_path = mw.getServerDir()
+ se_content = mw.readFile(systemServiceTpl)
+ se_content = se_content.replace('{$APP_PATH}', app_path)
+ mw.writeFile(systemService, se_content)
+ mw.execShell('systemctl daemon-reload')
+
+ return file_bin
+
+
+def tbOp(method):
+ file = initDreplace()
+
+ if not mw.isAppleSystem():
+ data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+ data = mw.execShell(file + ' ' + method)
+ # print(data)
+ if data[1] == '':
+ return 'ok'
+ return 'ok'
+
+
+def start():
+ return tbOp('start')
+
+
+def stop():
+ return tbOp('stop')
+
+
+def restart():
+ status = tbOp('restart')
+ return status
+
+
+def reload():
+
+ tgbot_tpl = getPluginDir() + '/startup/tgclient.py'
+ tgbot_dst = getServerDir() + '/tgclient.py'
+
+ content = mw.readFile(tgbot_tpl)
+ mw.writeFile(tgbot_dst, content)
+
+ ext_src = getPluginDir() + '/startup/extend'
+ ext_dst = getServerDir()
+
+ mw.execShell('cp -rf ' + ext_src + ' ' + ext_dst)
+
+ return tbOp('restart')
+
+
+def initdStatus():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status ' + \
+ getPluginName() + ' | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+
+def initdInstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl enable ' + getPluginName())
+ return 'ok'
+
+
+def initdUinstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl disable ' + getPluginName())
+ return 'ok'
+
+
+def getClientConf():
+ data = getConfigData()
+ if 'bot' in data:
+ return mw.returnJson(True, 'ok', data['bot'])
+ return mw.returnJson(False, 'ok', {})
+
+
+def setClientConf():
+ args = getArgs()
+ data_args = checkArgs(args, ['api_id', 'api_hash'])
+ if not data_args[0]:
+ return data_args[1]
+
+ data = getConfigData()
+ args['api_id'] = base64.b64decode(args['api_id']).decode('ascii')
+ args['api_hash'] = base64.b64decode(args['api_hash']).decode('ascii')
+ data['bot'] = args
+ writeConf(data)
+
+ return mw.returnJson(True, '保存成功!', [])
+
+
+def installPreInspection():
+ i = sys.version_info
+ if i[0] < 3 or i[1] < 7:
+ return "telebot在python小于3.7无法正常使用"
+ return 'ok'
+
+
+def uninstallPreInspection():
+ stop()
+ return "请手动删除 rm -rf {}".format(getServerDir())
+
+
+def getExtCfgByName(name):
+ elist = getExtCfg()
+ for x in elist:
+ if x['name'] == name:
+ return x
+ return None
+
+
+def clientExtList():
+
+ args = getArgs()
+ data_args = checkArgs(args, ['p'])
+ if not data_args[0]:
+ return data_args[1]
+
+ ext_path = getServerDir() + '/extend'
+ if not os.path.exists(ext_path):
+ return mw.returnJson(False, 'ok', [])
+ elist_source = os.listdir(ext_path)
+
+ elist = []
+ for e in elist_source:
+ if e.endswith('py'):
+ elist.append(e)
+
+ page = int(args['p'])
+ page_size = 5
+
+ make_ext_list = []
+ for ex in elist:
+ tmp = {}
+ tmp['name'] = ex
+ edata = getExtCfgByName(ex)
+ if edata:
+ tmp['status'] = edata['status']
+ else:
+ tmp['status'] = 'stop'
+
+ tmp['tag'] = ex.split('_')[0]
+ make_ext_list.append(tmp)
+
+ writeExtCfg(make_ext_list)
+ dlist_sum = len(make_ext_list)
+
+ page_start = int((page - 1) * page_size)
+ page_end = page_start + page_size
+
+ if page_end >= dlist_sum:
+ ret_data = make_ext_list[page_start:]
+ else:
+ ret_data = make_ext_list[page_start:page_end]
+
+ data = {}
+ data['data'] = ret_data
+ data['args'] = args
+ data['list'] = mw.getPage(
+ {'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'botExtListP'})
+
+ return mw.returnJson(True, 'ok', data)
+
+
+def setExtStatus():
+ args = getArgs()
+ data_args = checkArgs(args, ['name', 'status'])
+ if not data_args[0]:
+ return data_args[1]
+
+ elist = getExtCfg()
+ name = args['name']
+ status = args['status']
+ for x in range(len(elist)):
+ if elist[x]['name'] == name:
+ elist[x]['status'] = status
+ break
+
+ writeExtCfg(elist)
+
+ action = '开启'
+ if status == 'stop':
+ action = '关闭'
+
+ return mw.returnJson(True, action + '[' + name + ']扩展成功')
+
+
+def runLog():
+ p = getServerDir() + '/task.log'
+ return p
+
+
+if __name__ == "__main__":
+ 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 == 'uninstall_pre_inspection':
+ print(uninstallPreInspection())
+ elif func == 'get_client_conf':
+ print(getClientConf())
+ elif func == 'set_client_conf':
+ print(setClientConf())
+ elif func == 'client_ext_list':
+ print(clientExtList())
+ elif func == 'set_ext_status':
+ print(setExtStatus())
+ elif func == 'run_log':
+ print(runLog())
+
+ else:
+ print('error')
diff --git a/plugins/tgclient/info.json b/plugins/tgclient/info.json
new file mode 100755
index 000000000..76bd12c43
--- /dev/null
+++ b/plugins/tgclient/info.json
@@ -0,0 +1,20 @@
+{
+ "sort": 7,
+ "ps": "简单Telegram客服端管理",
+ "name": "tgclient",
+ "title": "tgclient",
+ "shell": "install.sh",
+ "versions":["1.1"],
+ "tip": "soft",
+ "checks": "server/tgclient",
+ "path": "server/tgclient",
+ "install_pre_inspection":true,
+ "uninstall_pre_inspection":true,
+ "display": 1,
+ "author": "midoks",
+ "date": "2023-03-06",
+ "home": "https://my.telegram.org/apps",
+ "depend_doc1":"https://docs.telethon.dev/en/stable/basic/installation.html",
+ "type": 0,
+ "pid": "5"
+}
\ No newline at end of file
diff --git a/plugins/tgclient/init.d/tgclient.service.tpl b/plugins/tgclient/init.d/tgclient.service.tpl
new file mode 100644
index 000000000..4edee4e7e
--- /dev/null
+++ b/plugins/tgclient/init.d/tgclient.service.tpl
@@ -0,0 +1,14 @@
+[Unit]
+Description=Tgbot Service
+After=network.target
+
+[Service]
+Type=forking
+ExecStart={$APP_PATH}/init.d/tgclient start
+ExecStop={$APP_PATH}/init.d/tgclient stop
+ExecReload={$APP_PATH}/init.d/tgclient reload
+KillMode=process
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/plugins/tgclient/init.d/tgclient.tpl b/plugins/tgclient/init.d/tgclient.tpl
new file mode 100644
index 000000000..b44d3fa01
--- /dev/null
+++ b/plugins/tgclient/init.d/tgclient.tpl
@@ -0,0 +1,86 @@
+#!/bin/sh
+# chkconfig: 2345 55 25
+# description: Tgbot Service
+
+### BEGIN INIT INFO
+# Provides: Tgbot
+# Required-Start: $all
+# Required-Stop: $all
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts Tgbot
+# Description: starts the MDW-Web
+### END INIT INFO
+
+# Simple Tgbot init.d script conceived to work on Linux systems
+# as it does use of the /proc filesystem.
+
+PATH=/usr/local/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export LANG=en_US.UTF-8
+
+
+mw_path={$SERVER_PATH}
+PATH=$PATH:$mw_path/bin
+
+if [ -f $mw_path/bin/activate ];then
+ source $mw_path/bin/activate
+fi
+
+tg_start(){
+
+ isStart=`ps -ef|grep 'tgclient.py' |grep -v grep | awk '{print $2}'`
+ if [ "$isStart" == '' ];then
+ echo -e "starting tgclient... \c"
+ cd $mw_path
+ echo "python3 {$APP_PATH}/tgclient.py"
+ python3 {$APP_PATH}/tgclient.py >> {$APP_PATH}/task.log &
+ isStart=""
+ while [[ "$isStart" == "" ]];
+ do
+ echo -e ".\c"
+ sleep 0.5
+ isStart=`ps -ef|grep 'tgclient.py' |grep -v grep | awk '{print $2}'`
+ let n+=1
+ if [ $n -gt 20 ];then
+ break;
+ fi
+ done
+ if [ "$isStart" == '' ];then
+ echo -e "\033[31mfailed\033[0m"
+ echo -e "\033[31mError: tgclient service startup failed.\033[0m"
+ return;
+ fi
+ echo -e "\033[32mdone\033[0m"
+ else
+ echo "starting tgclient...(pid $(echo $isStart)) already running"
+ fi
+}
+
+
+tg_stop(){
+ echo -e "stopping tgclient ... \c";
+ arr=`ps aux|grep 'tgclient.py'|grep -v grep|awk '{print $2}'`
+ for p in ${arr[@]}
+ do
+ kill -9 $p > /dev/null 2>&1
+ done
+ echo -e "\033[32mdone\033[0m"
+}
+
+case "$1" in
+ start)
+ tg_start
+ ;;
+ stop)
+ tg_stop
+ ;;
+ restart|reload)
+ tg_stop
+ sleep 0.3
+ tg_start
+ ;;
+ *)
+ echo "Please use start or stop as first argument"
+ ;;
+esac
+
diff --git a/plugins/tgclient/install.sh b/plugins/tgclient/install.sh
new file mode 100755
index 000000000..6757ba902
--- /dev/null
+++ b/plugins/tgclient/install.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+VERSION=$2
+
+# pip3 install ccxt
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+pip3 install telethon
+
+Install_App()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/source
+ mkdir -p $serverPath/tgclient
+ echo "${VERSION}" > $serverPath/tgclient/version.pl
+
+ cp -rf ${rootPath}/plugins/tgclient/startup/* $serverPath/tgclient
+
+ cd ${rootPath} && python3 ${rootPath}/plugins/tgclient/index.py start
+ cd ${rootPath} && python3 ${rootPath}/plugins/tgclient/index.py initd_install
+ echo '安装完成'
+}
+
+Uninstall_App()
+{
+ if [ -f /usr/lib/systemd/system/tgclient.service ];then
+ systemctl stop tgclient
+ systemctl disable tgclient
+ rm -rf /usr/lib/systemd/system/tgclient.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/tgclient/initd/tgclient ];then
+ $serverPath/tgclient/initd/tgclient stop
+ fi
+
+ rm -rf $serverPath/tgclient
+ echo "Uninstall_redis"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/tgclient/js/tgclient.js b/plugins/tgclient/js/tgclient.js
new file mode 100755
index 000000000..39b11fac1
--- /dev/null
+++ b/plugins/tgclient/js/tgclient.js
@@ -0,0 +1,163 @@
+function readme(){
+ var readme = '';
+ readme += '在填写好配置信息好后,还要执行下面命令。进行手机号和短信码验证。再重启,即可正常使用 ';
+ readme += 'cd /www/server/mdserver-web && source bin/activate && python3 /www/server/tgclient/tgclient.py ';
+ readme += 'https://my.telegram.org/auth ';
+ readme += ' ';
+ $('.soft-man-con').html(readme);
+}
+
+function appPost(method, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'tgclient';
+ req_data['func'] = method;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function appPostCallbak(method, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'tgclient';
+ req_data['func'] = method;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function clientConf(){
+ appPost('get_client_conf','',function(data){
+ var rdata = $.parseJSON(data.data);
+ var api_id = 'api_id';
+ var api_hash = 'api_hash';
+ if(rdata['status']){
+ db_data = rdata['data'];
+
+ // api_id, api_hash
+ api_id = db_data['api_id'];
+ api_hash = db_data['api_hash'];
+
+ }
+
+ var mlist = '';
+ mlist += 'api_id 必填写
';
+ mlist += 'api_hash 必填写
';
+ var option = '\
+ \
+ ' + mlist + '\
+
\
+ 保存 \
+
\
+
';
+ $(".soft-man-con").html(option);
+ });
+}
+
+function submitBotConf(){
+ var pull_data = {};
+ pull_data['api_id'] = base64_encode($('input[name="api_id"]').val());
+ pull_data['api_hash'] = base64_encode($('input[name="api_hash"]').val());
+ appPost('set_client_conf',pull_data,function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata['msg'],{icon:rdata['status']?1:2,time:2000,shade: [0.3, '#000']});
+ });
+}
+
+
+function botExtList(){
+ var body = '\
+
\
+ \
+ \
+ 脚本 \
+ 类型 \
+ 状态 \
+ \
+ \
+ \
+
\
+ \
+
';
+ $('.soft-man-con').html(body);
+ botExtListP(1);
+}
+
+function setBotExtStatus(name,status){
+ appPost('set_ext_status',{'name':name,'status':status}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ layer.msg(rdata['msg'],);
+ showMsg(rdata['msg'], function(){
+ botExtListP(1);
+ },{icon:rdata['status']?1:2,shade: [0.3, '#000']},2000);
+ });
+}
+
+function botExtListP(p=1){
+ appPost('client_ext_list',{'p':p}, function(rdata){
+ // console.log(rdata);
+ var rdata = $.parseJSON(rdata.data);
+ // console.log(rdata);
+ var tBody = '';
+
+ if (!rdata.status && rdata.data.length == 0 ){
+ var tBody = ' 无数据
';
+ } else{
+ var ldata = rdata.data.data;
+ for (var i = 0; i < ldata.length; i++) {
+ tBody += ''
+ tBody += ''+ldata[i]['name']+' ';
+ tBody += ''+ldata[i]['tag']+' ';
+
+ if (ldata[i]['status'] == 'start'){
+ tBody += ' ';
+ } else{
+ tBody += ' ';
+ }
+ tBody +=' ';
+ }
+ }
+
+ $('#ext_list').html(tBody);
+ $('#ext_list_page').html(rdata.data.list);
+
+ $('#ext_list .ext_status').click(function(){
+ var name = $(this).parent().parent().data('name');
+ var status = 'stop';
+ if ($(this).hasClass('glyphicon-pause')){
+ status = 'start';
+ }
+ setBotExtStatus(name,status);
+ });
+ });
+}
diff --git a/plugins/tgclient/startup/extend/client_ad.py b/plugins/tgclient/startup/extend/client_ad.py
new file mode 100644
index 000000000..52a18e486
--- /dev/null
+++ b/plugins/tgclient/startup/extend/client_ad.py
@@ -0,0 +1,97 @@
+# coding:utf-8
+
+# func: 在其他发送推送AD
+# url: https://docs.telethon.dev/en/stable/modules/client.html
+import sys
+import io
+import os
+import time
+import re
+import json
+import base64
+import threading
+import asyncio
+
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+from telethon import utils
+from telethon import functions, types
+from telethon.tl.functions.messages import AddChatUserRequest
+from telethon.tl.functions.channels import InviteToChannelRequest
+# 指定群ID
+chat_id_list = [-1001578009023]
+filter_g_id = [-1001771526434]
+
+
+msg_ad = "本人软件推广(10s)\n\n"
+msg_ad += "开源Linux面板【mdserver-web】,站长必备,无毒,源码为证。\n"
+msg_ad += "不收费,全靠TG乞讨! \n"
+msg_ad += "看个人简介,加入群聊,一起进步!\n"
+# msg_ad += "https://github.com/midoks/mdserver-web \n"
+# msg_ad += "\n"
+# msg_ad += "加入群聊,一起进步! \n"
+# msg_ad += "https://t.me/mdserver_web \n"
+# msg_ad += "不收费,无毒。源码为证。全靠TG乞讨!😭\n\n"
+# msg_ad += "捐赠地址 USDT(TRC20)\n"
+# msg_ad += "TVbNgrpeGBGZVm5gTLa21ADP7RpnPFhjya\n"
+# msg_ad += "日行一善,以后必定大富大贵\n"
+
+
+async def writeLog(log_str):
+ if __name__ == "__main__":
+ print(log_str)
+
+ now = mw.getDateFromNow()
+ log_file = mw.getServerDir() + '/tgclient/task.log'
+ mw.writeFileLog(now + ':' + log_str, log_file, limit_size=5 * 1024)
+ return True
+
+async def send_msg(client, chat_id, tag='ad', trigger_time=600):
+ # 信号只在一个周期内执行一次|start
+ lock_file = mw.getServerDir() + '/tgclient/lock.json'
+ if not os.path.exists(lock_file):
+ mw.writeFile(lock_file, '{}')
+
+ lock_data = json.loads(mw.readFile(lock_file))
+ if tag in lock_data:
+ diff_time = time.time() - lock_data[tag]['do_time']
+ if diff_time >= trigger_time:
+ lock_data[tag]['do_time'] = time.time()
+ else:
+ return False, 0, 0
+ else:
+ lock_data[tag] = {'do_time': time.time()}
+ mw.writeFile(lock_file, json.dumps(lock_data))
+ # 信号只在一个周期内执行一次|end
+
+ msg = await client.send_message(chat_id, msg_ad)
+ await asyncio.sleep(10)
+ await client.delete_messages(chat_id, msg)
+ await asyncio.sleep(3)
+
+async def run(client):
+ client.parse_mode = 'html'
+ # for chat_id in chat_id_list:
+ # await send_msg(client, chat_id)
+ # await asyncio.sleep(30)
+
+ info = await client.get_dialogs()
+ for chat in info:
+ if chat.is_group and not chat.id in filter_g_id:
+ chat_id = str(chat.id)
+ if chat_id[0:4] != '-100':
+ continue
+
+ # print(chat)
+ await writeLog('name:{0} id:{1} is_user:{2} is_channel:{3} is_group:{4}'.format(
+ chat.name, chat.id, chat.is_user, chat.is_channel, chat.is_group))
+ try:
+ await send_msg(client, chat.id, 'ad_' + str(chat.id))
+ except Exception as e:
+ await writeLog(str(chat))
+ await writeLog(str(e))
+
+
+if __name__ == "__main__":
+ pass
diff --git a/plugins/tgclient/startup/extend/client_check_member.py b/plugins/tgclient/startup/extend/client_check_member.py
new file mode 100644
index 000000000..3593c5396
--- /dev/null
+++ b/plugins/tgclient/startup/extend/client_check_member.py
@@ -0,0 +1,69 @@
+# coding:utf-8
+
+# func: 自动检测已经注销群成员
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import base64
+import threading
+import asyncio
+
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+import telebot
+from telebot import types
+from telebot.util import quick_markup
+
+
+# 指定群ID
+chat_id_list = [-1001979545570]
+# 别人群ID[有API调用限制]
+chat_id_list_other = [-1001578009023, -1001771526434]
+
+async def writeLog(log_str):
+ if __name__ == "__main__":
+ print(log_str)
+
+ now = mw.getDateFromNow()
+ log_file = mw.getServerDir() + '/tgclient/task.log'
+ mw.writeFileLog(now + ':' + log_str, log_file, limit_size=5 * 1024)
+ return True
+
+async def run(client):
+ for chat_id in chat_id_list:
+ try:
+ s = await client.send_message(chat_id, '开始自动检测已经注销群成员...')
+ count = 0
+ async for user in client.iter_participants(chat_id):
+ if user.deleted:
+ count += 1
+ msg = await client.kick_participant(chat_id, user)
+
+ await client.edit_message(chat_id, s.id, '已经检测到有(%d)个账户已失效' % (count))
+ await asyncio.sleep(3)
+ await client.edit_message(chat_id, s.id, '自动检测已经注销群成员完毕!!!')
+ await asyncio.sleep(3)
+ await client.delete_messages(chat_id, s)
+ except Exception as e:
+ print(str(e))
+ writeLog(str(e))
+
+ for chat_id in chat_id_list_other:
+ try:
+ async for user in client.iter_participants(chat_id):
+ if user.deleted:
+ msg = await client.kick_participant(chat_id, user)
+ except Exception as e:
+ print(str(e))
+ writeLog(str(e))
+
+ await asyncio.sleep(300)
+
+
+if __name__ == "__main__":
+ pass
diff --git a/plugins/tgclient/startup/extend/client_holding.py b/plugins/tgclient/startup/extend/client_holding.py
new file mode 100644
index 000000000..6950efeb9
--- /dev/null
+++ b/plugins/tgclient/startup/extend/client_holding.py
@@ -0,0 +1,55 @@
+# coding:utf-8
+
+# func: 自动邀请群成员
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import base64
+import threading
+import asyncio
+
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+from telethon import utils
+from telethon import functions, types
+from telethon.tl.functions.messages import AddChatUserRequest
+from telethon.tl.functions.channels import InviteToChannelRequest
+# 指定群ID
+chat_id = -1001979545570
+filter_user_id = 5568699210
+filter_g_id = [-1001771526434]
+
+
+async def run(client):
+ info = await client.get_dialogs()
+ for chat in info:
+ is_sleep = True
+ print('name:{0} id:{1} is_user:{2} is_channel:{3} is_group:{4}'.format(
+ chat.name, chat.id, chat.is_user, chat.is_channel, chat.is_group))
+ if chat.is_group and chat.id != chat_id:
+ list_user = []
+ async for user in client.iter_participants(chat.id):
+ if chat.id in filter_g_id:
+ is_sleep = False
+ continue
+
+ if filter_user_id != user.id and user.username != None and user.bot == False:
+ list_user.append(user.username)
+ print(list_user)
+ try:
+ await client(InviteToChannelRequest(
+ channel=chat_id, # chat_id
+ users=list_user, # 被邀请人id
+ ))
+ except Exception as e:
+ print(str(e))
+ if is_sleep:
+ await asyncio.sleep(90000)
+
+if __name__ == "__main__":
+ pass
diff --git a/plugins/tgclient/startup/extend/client_temp.py b/plugins/tgclient/startup/extend/client_temp.py
new file mode 100644
index 000000000..4071e77f0
--- /dev/null
+++ b/plugins/tgclient/startup/extend/client_temp.py
@@ -0,0 +1,54 @@
+# coding:utf-8
+
+# func: 临时测试
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import base64
+import threading
+import asyncio
+
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+from telethon import utils
+from telethon import functions, types
+from telethon.tl.functions.messages import AddChatUserRequest
+from telethon.tl.functions.channels import InviteToChannelRequest
+# 指定群ID
+chat_id = -1001979545570
+filter_user_id = 5568699210
+filter_g_id = [-1001771526434]
+
+
+async def run(client):
+
+ my_channel = await client.get_entity(PeerChannel(-1001173826177))
+ print(my_channel)
+
+ for i in range(9999999999):
+ try:
+ v = -1001000000000 - i
+ my_channel = await client.get_entity(PeerChannel(v))
+ print(my_channel)
+ except Exception as e:
+ pass
+
+ # -1001809140739
+ # -1001800000000
+ # -1000000000001
+
+ info = await client.get_dialogs()
+ for chat in info:
+ if not chat.is_group and chat.is_channel:
+ print('name:{0} id:{1} is_user:{2} is_channel:{3} is_group:{4}'.format(
+ chat.name, chat.id, chat.is_user, chat.is_channel, chat.is_group))
+ await asyncio.sleep(10)
+
+
+if __name__ == "__main__":
+ pass
diff --git a/plugins/tgclient/startup/extend/readme.md b/plugins/tgclient/startup/extend/readme.md
new file mode 100755
index 000000000..d52224d88
--- /dev/null
+++ b/plugins/tgclient/startup/extend/readme.md
@@ -0,0 +1 @@
+push_*.py 识别为推送插件
diff --git a/plugins/tgclient/startup/tgclient.py b/plugins/tgclient/startup/tgclient.py
new file mode 100644
index 000000000..933ba7170
--- /dev/null
+++ b/plugins/tgclient/startup/tgclient.py
@@ -0,0 +1,132 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+import base64
+import threading
+import asyncio
+import logging
+
+# python /Users/midoks/Desktop/mwdev/server/tgclient/tgclient.py
+
+'''
+cd /www/server/mdserver-web && source bin/activate && python3 /www/server/tgclient/tgclient.py
+'''
+
+from telethon import TelegramClient
+
+
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+logging.basicConfig(level=logging.INFO,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+logger = logging.getLogger(__name__)
+
+
+def getPluginName():
+ return 'tgclient'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+sys.path.append(getServerDir() + "/extend")
+
+
+def getConfigData():
+ cfg_path = getServerDir() + "/data.cfg"
+ if not os.path.exists(cfg_path):
+ mw.writeFile(cfg_path, '{}')
+ t = mw.readFile(cfg_path)
+ return json.loads(t)
+
+
+def writeConf(data):
+ cfg_path = getServerDir() + "/data.cfg"
+ mw.writeFile(cfg_path, json.dumps(data))
+ return True
+
+
+def getExtCfg():
+ cfg_path = getServerDir() + "/extend.cfg"
+ if not os.path.exists(cfg_path):
+ mw.writeFile(cfg_path, '{}')
+ t = mw.readFile(cfg_path)
+ return json.loads(t)
+
+
+def getStartExtCfgByTag(tag='push'):
+ # 获取开启的扩展
+ elist = getExtCfg()
+ rlist = []
+ for x in elist:
+ if x['tag'] == tag and x['status'] == 'start':
+ rlist.append(x)
+ return rlist
+
+
+def writeLog(log_str):
+ if __name__ == "__main__":
+ print(log_str)
+
+ now = mw.getDateFromNow()
+ log_file = getServerDir() + '/task.log'
+ mw.writeFileLog(now + ':' + log_str, log_file, limit_size=5 * 1024)
+ return True
+
+
+# start tgbot
+cfg = getConfigData()
+while True:
+ cfg = getConfigData()
+ if 'bot' in cfg and 'api_id' in cfg['bot']:
+ if cfg['bot']['api_id'] != '' and cfg['bot']['api_id'] != 'api_id':
+ break
+ if cfg['bot']['api_hash'] != '' and cfg['bot']['api_hash'] != 'api_hash':
+ break
+ writeLog('等待输入配置,api_id')
+ time.sleep(3)
+
+client = TelegramClient('mdioks', cfg['bot']['api_id'], cfg['bot']['api_hash'])
+
+async def plugins_run_task():
+ plist = getStartExtCfgByTag('client')
+ for p in plist:
+ try:
+ script = p['name'].split('.')[0]
+ await __import__(script).run(client)
+ except Exception as e:
+ writeLog('----- client error start -------')
+ writeLog(mw.getTracebackInfo())
+ writeLog('----- client error end -------')
+
+async def plugins_run():
+ while True:
+ await plugins_run_task()
+ time.sleep(1)
+
+async def main(loop):
+ await client.start()
+
+ # create new task
+ writeLog('creating plugins_run task.')
+ task = loop.create_task(plugins_run())
+ await task
+
+ writeLog('It works.')
+ await client.run_until_disconnected()
+ task.cancel()
+
+if __name__ == '__main__':
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(main(loop))
diff --git a/plugins/valkey/config/valkey.conf b/plugins/valkey/config/valkey.conf
new file mode 100644
index 000000000..318a13c0a
--- /dev/null
+++ b/plugins/valkey/config/valkey.conf
@@ -0,0 +1,90 @@
+daemonize yes
+pidfile {$SERVER_PATH}/valkey/valkey.pid
+
+bind 127.0.0.1
+port 6389
+requirepass {$VALKEY_PASS}
+
+timeout 3
+tcp-keepalive 0
+
+loglevel notice
+
+logfile {$SERVER_PATH}/valkey/data/valkey.log
+databases 16
+
+################################ SNAPSHOTTING #################################
+
+save 900 1000
+save 300 10000
+save 60 1000000
+stop-writes-on-bgsave-error no
+rdbcompression yes
+rdbchecksum yes
+dbfilename dump.rdb
+dir {$SERVER_PATH}/valkey/data/
+
+################################# REPLICATION #################################
+
+slave-serve-stale-data yes
+slave-read-only yes
+
+repl-disable-tcp-nodelay no
+slave-priority 100
+
+################################## SECURITY ###################################
+
+
+################################### LIMITS ####################################
+maxclients 10000
+#maxmemory-samples 3
+maxmemory 218mb
+#maxmemory-policy volatile-ttl
+maxmemory-policy allkeys-lru
+
+############################## APPEND ONLY MODE ###############################
+
+# appendonly no
+
+# appendfsync always
+# appendfsync everysec
+# appendfsync no
+
+# appendfilename "appendonly.aof"
+
+no-appendfsync-on-rewrite no
+auto-aof-rewrite-percentage 100
+auto-aof-rewrite-min-size 64mb
+
+################################ LUA SCRIPTING ###############################
+
+lua-time-limit 5000
+
+################################## SLOW LOG ###################################
+
+
+slowlog-log-slower-than 10000
+slowlog-max-len 128
+
+############################### ADVANCED CONFIG ###############################
+
+hash-max-ziplist-entries 512
+hash-max-ziplist-value 64
+
+list-max-ziplist-entries 512
+list-max-ziplist-value 64
+
+set-max-intset-entries 512
+
+zset-max-ziplist-entries 128
+zset-max-ziplist-value 64
+
+activerehashing yes
+
+client-output-buffer-limit normal 0 0 0
+client-output-buffer-limit slave 256mb 64mb 60
+client-output-buffer-limit pubsub 32mb 8mb 60
+
+hz 10
+
+aof-rewrite-incremental-fsync yes
\ No newline at end of file
diff --git a/plugins/valkey/ico.png b/plugins/valkey/ico.png
new file mode 100644
index 000000000..80a6300fd
Binary files /dev/null and b/plugins/valkey/ico.png differ
diff --git a/plugins/valkey/index.html b/plugins/valkey/index.html
new file mode 100755
index 000000000..894b2ffe6
--- /dev/null
+++ b/plugins/valkey/index.html
@@ -0,0 +1,36 @@
+
+
+
+
\ No newline at end of file
diff --git a/plugins/valkey/index.py b/plugins/valkey/index.py
new file mode 100755
index 000000000..11d175df9
--- /dev/null
+++ b/plugins/valkey/index.py
@@ -0,0 +1,570 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+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 'valkey'
+
+
+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 getConf():
+ path = getServerDir() + "/valkey.conf"
+ return path
+
+
+def getConfTpl():
+ path = getPluginDir() + "/config/valkey.conf"
+ return path
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
+ return path
+
+
+def getArgs():
+ args = sys.argv[3:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ if t.strip() == '':
+ tmp = []
+ else:
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ 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 configTpl():
+ path = getPluginDir() + '/tpl'
+ pathFile = os.listdir(path)
+ tmp = []
+ for one in pathFile:
+ file = path + '/' + one
+ tmp.append(file)
+ return mw.getJson(tmp)
+
+
+def readConfigTpl():
+ args = getArgs()
+ data = checkArgs(args, ['file'])
+ if not data[0]:
+ return data[1]
+
+ content = mw.readFile(args['file'])
+ content = contentReplace(content)
+ return mw.returnJson(True, 'ok', content)
+
+def getPidFile():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'pidfile\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+def status():
+ pid_file = getPidFile()
+ if not os.path.exists(pid_file):
+ return 'stop'
+
+ # data = mw.execShell(
+ # "ps aux|grep redis |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'")
+
+ # if data[0] == '':
+ # return 'stop'
+ return 'start'
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName())
+ content = content.replace('{$VALKEY_PASS}', mw.getRandomString(10))
+ return content
+
+
+
+def initDreplace():
+
+ file_tpl = getInitDTpl()
+ service_path = mw.getServerDir()
+
+ initD_path = getServerDir() + '/init.d'
+ if not os.path.exists(initD_path):
+ os.mkdir(initD_path)
+ file_bin = initD_path + '/' + getPluginName()
+
+ # initd replace
+ if not os.path.exists(file_bin):
+ content = mw.readFile(file_tpl)
+ content = content.replace('{$SERVER_PATH}', service_path)
+ mw.writeFile(file_bin, content)
+ mw.execShell('chmod +x ' + file_bin)
+
+ # log
+ dataLog = getServerDir() + '/data'
+ if not os.path.exists(dataLog):
+ mw.execShell('mkdir -p ' + dataLog)
+ mw.execShell('chmod +x ' + file_bin)
+
+ # config replace
+ dst_conf = getConf()
+ dst_conf_init = getServerDir() + '/init.pl'
+ if not os.path.exists(dst_conf_init):
+ content = mw.readFile(getConfTpl())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$VALKEY_PASS}', mw.getRandomString(10))
+
+ mw.writeFile(dst_conf, content)
+ mw.writeFile(dst_conf_init, 'ok')
+
+ # systemd
+ systemDir = mw.systemdCfgDir()
+ systemService = systemDir + '/' + getPluginName() + '.service'
+ if os.path.exists(systemDir) and not os.path.exists(systemService):
+ systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl'
+ content = mw.readFile(systemServiceTpl)
+ content = content.replace('{$SERVER_PATH}', service_path)
+ mw.writeFile(systemService, content)
+ mw.execShell('systemctl daemon-reload')
+
+ return file_bin
+
+
+def wkOp(method):
+ file = initDreplace()
+
+ 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"):
+ data = mw.execShell('service ' + getPluginName() + ' ' + method)
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+ data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def start():
+ return wkOp('start')
+
+
+def stop():
+ return wkOp('stop')
+
+
+def restart():
+ status = wkOp('restart')
+
+ log_file = runLog()
+ mw.execShell("echo '' > " + log_file)
+ return status
+
+
+def reload():
+ return wkOp('reload')
+
+
+def getPort():
+ conf = getServerDir() + '/valkey.conf'
+ content = mw.readFile(conf)
+
+ rep = r"^(port)\s*([.0-9A-Za-z_& ~]+)"
+ tmp = re.search(rep, content, re.M)
+ if tmp:
+ return tmp.groups()[1]
+
+ return '6379'
+
+
+def getRedisCmd():
+ requirepass = ""
+ conf = getConf()
+ content = mw.readFile(conf)
+ rep = r"^(requirepass)\s*([.0-9A-Za-z_& ~]+)"
+ tmp = re.search(rep, content, re.M)
+ if tmp:
+ requirepass = tmp.groups()[1]
+
+ default_ip = '127.0.0.1'
+ port = getPort()
+ # findDebian = mw.execShell('cat /etc/issue |grep Debian')
+ # if findDebian[0] != '':
+ # default_ip = mw.getLocalIp()
+ cmd = getServerDir() + "/bin/valkey-cli -h " + default_ip + ' -p ' + port + " "
+
+ if requirepass != "":
+ cmd = getServerDir() + '/bin/valkey-cli -h ' + default_ip + ' -p ' + port + ' -a "' + requirepass + '" '
+
+ return cmd
+
+def runInfo():
+ s = status()
+ if s == 'stop':
+ return mw.returnJson(False, '未启动')
+
+
+ cmd = getRedisCmd()
+ cmd = cmd + 'info'
+ data = mw.execShell(cmd)[0]
+ res = [
+ 'tcp_port',
+ 'uptime_in_days', # 已运行天数
+ 'connected_clients', # 连接的客户端数量
+ 'used_memory', # Redis已分配的内存总量
+ 'used_memory_rss', # Redis占用的系统内存总量
+ 'used_memory_peak', # Redis所用内存的高峰值
+ 'mem_fragmentation_ratio', # 内存碎片比率
+ 'total_connections_received', # 运行以来连接过的客户端的总数量
+ 'total_commands_processed', # 运行以来执行过的命令的总数量
+ 'instantaneous_ops_per_sec', # 服务器每秒钟执行的命令数量
+ 'keyspace_hits', # 查找数据库键成功的次数
+ 'keyspace_misses', # 查找数据库键失败的次数
+ 'latest_fork_usec' # 最近一次 fork() 操作耗费的毫秒数
+ ]
+ data = data.split("\n")
+ result = {}
+ for d in data:
+ if len(d) < 3:
+ continue
+ t = d.strip().split(':')
+ if not t[0] in res:
+ continue
+ result[t[0]] = t[1]
+ return mw.getJson(result)
+
+def infoReplication():
+ # 复制信息
+ s = status()
+ if s == 'stop':
+ return mw.returnJson(False, '未启动')
+
+ cmd = getRedisCmd()
+ cmd = cmd + 'info replication'
+
+ # print(cmd)
+ data = mw.execShell(cmd)[0]
+ # print(data)
+ res = [
+ #slave
+ 'role',#角色
+ 'master_host', # 连接主库HOST
+ 'master_port', # 连接主库PORT
+ 'master_link_status', # 连接主库状态
+ 'master_last_io_seconds_ago', # 上次同步时间
+ 'master_sync_in_progress', # 正在同步中
+ 'slave_read_repl_offset', # 从库读取复制位置
+ 'slave_repl_offset', # 从库复制位置
+ 'slave_priority', # 从库同步优先级
+ 'slave_read_only', # 从库是否仅读
+ 'replica_announced', # 已复制副本
+ 'connected_slaves', # 连接从库数量
+ 'master_failover_state', # 主库故障状态
+ 'master_replid', # 主库复制ID
+ 'master_repl_offset', # 主库复制位置
+ 'second_repl_offset', # 主库复制位置时间
+ 'repl_backlog_active', # 复制状态
+ 'repl_backlog_size', # 复制大小
+ 'repl_backlog_first_byte_offset', # 第一个字节偏移量
+ 'repl_backlog_histlen', # backlog中数据的长度
+ ]
+
+ data = data.split("\n")
+ result = {}
+ for d in data:
+ if len(d) < 3:
+ continue
+ t = d.strip().split(':')
+ if not t[0] in res:
+ continue
+ result[t[0]] = t[1]
+
+ if 'role' in result and result['role'] == 'master':
+ connected_slaves = int(result['connected_slaves'])
+ slave_l = []
+ for x in range(connected_slaves):
+ slave_l.append('slave'+str(x))
+
+ for d in data:
+ if len(d) < 3:
+ continue
+ t = d.strip().split(':')
+ if not t[0] in slave_l:
+ continue
+ result[t[0]] = t[1]
+
+ return mw.getJson(result)
+
+
+def clusterInfo():
+ #集群信息
+ # https://redis.io/commands/cluster-info/
+ s = status()
+ if s == 'stop':
+ return mw.returnJson(False, '未启动')
+
+ cmd = getRedisCmd()
+ cmd = cmd + 'cluster info'
+
+ # print(cmd)
+ data = mw.execShell(cmd)[0]
+ # print(data)
+
+ res = [
+ 'cluster_state',#状态
+ 'cluster_slots_assigned', # 被分配的槽
+ 'cluster_slots_ok', # 被分配的槽状态
+ 'cluster_slots_pfail', # 连接主库状态
+ 'cluster_slots_fail', # 失败的槽
+ 'cluster_known_nodes', # 知道的节点
+ 'cluster_size', # 大小
+ 'cluster_current_epoch', #
+ 'cluster_my_epoch', #
+ 'cluster_stats_messages_sent', # 发送
+ 'cluster_stats_messages_received', # 接受
+ 'total_cluster_links_buffer_limit_exceeded', #
+ ]
+
+ data = data.split("\n")
+ result = {}
+ for d in data:
+ if len(d) < 3:
+ continue
+ t = d.strip().split(':')
+ if not t[0] in res:
+ continue
+ result[t[0]] = t[1]
+
+ return mw.getJson(result)
+
+def clusterNodes():
+ s = status()
+ if s == 'stop':
+ return mw.returnJson(False, '未启动')
+
+ cmd = getRedisCmd()
+ cmd = cmd + 'cluster nodes'
+
+ # print(cmd)
+ data = mw.execShell(cmd)[0]
+ # print(data)
+
+ data = data.strip().split("\n")
+ return mw.getJson(data)
+
+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 ' + \
+ getPluginName() + ' | 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 ' + getPluginName() + '_enable="YES"')
+ return 'ok'
+
+ mw.execShell('systemctl enable ' + getPluginName())
+ 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 ' + getPluginName() + '_enable="NO"')
+ return 'ok'
+
+ mw.execShell('systemctl disable ' + getPluginName())
+ return 'ok'
+
+
+def runLog():
+ return getServerDir() + '/data/valkey.log'
+
+
+def getRedisConfInfo():
+ conf = getConf()
+
+ gets = [
+ {'name': 'bind', 'type': 2, 'ps': '绑定IP(修改绑定IP可能会存在安全隐患)','must_show':1},
+ {'name': 'port', 'type': 2, 'ps': '绑定端口','must_show':1},
+ {'name': 'timeout', 'type': 2, 'ps': '空闲链接超时时间,0表示不断开','must_show':1},
+ {'name': 'maxclients', 'type': 2, 'ps': '最大连接数','must_show':1},
+ {'name': 'databases', 'type': 2, 'ps': '数据库数量','must_show':1},
+ {'name': 'requirepass', 'type': 2, 'ps': 'redis密码,留空代表没有设置密码','must_show':1},
+ {'name': 'maxmemory', 'type': 2, 'ps': 'MB,最大使用内存,0表示不限制','must_show':1},
+ {'name': 'slaveof', 'type': 2, 'ps': '同步主库地址','must_show':0},
+ {'name': 'masterauth', 'type': 2, 'ps': '同步主库密码', 'must_show':0}
+ ]
+ content = mw.readFile(conf)
+
+ result = []
+ for g in gets:
+ rep = r"^(" + g['name'] + r')\s*([.0-9A-Za-z_& ~]+)'
+ tmp = re.search(rep, content, re.M)
+ if not tmp:
+ if g['must_show'] == 0:
+ continue
+
+ g['value'] = ''
+ result.append(g)
+ continue
+ g['value'] = tmp.groups()[1]
+ if g['name'] == 'maxmemory':
+ g['value'] = g['value'].strip("mb")
+ result.append(g)
+
+ return result
+
+
+def getRedisConf():
+ data = getRedisConfInfo()
+ return mw.getJson(data)
+
+
+def submitRedisConf():
+ gets = ['bind', 'port', 'timeout', 'maxclients',
+ 'databases', 'requirepass', 'maxmemory','slaveof','masterauth']
+ args = getArgs()
+ conf = getConf()
+ content = mw.readFile(conf)
+ for g in gets:
+ if g in args:
+ rep = g + r'\s*([.0-9A-Za-z_& ~]+)'
+ val = g + ' ' + args[g]
+
+ if g == 'maxmemory':
+ val = g + ' ' + args[g] + "mb"
+
+ if g == 'requirepass' and args[g] == '':
+ content = re.sub('requirepass', '#requirepass', content)
+ if g == 'requirepass' and args[g] != '':
+ content = re.sub('#requirepass', 'requirepass', content)
+ content = re.sub(rep, val, content)
+
+ if g != 'requirepass':
+ content = re.sub(rep, val, content)
+ mw.writeFile(conf, content)
+ reload()
+ return mw.returnJson(True, '设置成功')
+
+if __name__ == "__main__":
+ 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 == 'run_info':
+ print(runInfo())
+ elif func == 'info_replication':
+ print(infoReplication())
+ elif func == 'cluster_info':
+ print(clusterInfo())
+ elif func == 'cluster_nodes':
+ print(clusterNodes())
+ elif func == 'conf':
+ print(getConf())
+ elif func == 'run_log':
+ print(runLog())
+ elif func == 'get_redis_conf':
+ print(getRedisConf())
+ elif func == 'submit_redis_conf':
+ print(submitRedisConf())
+ elif func == 'config_tpl':
+ print(configTpl())
+ elif func == 'read_config_tpl':
+ print(readConfigTpl())
+ else:
+ print('error')
diff --git a/plugins/valkey/info.json b/plugins/valkey/info.json
new file mode 100755
index 000000000..f52232dcf
--- /dev/null
+++ b/plugins/valkey/info.json
@@ -0,0 +1,17 @@
+{
+ "sort": 8,
+ "ps": "一个高性能键值数据存储系统",
+ "name": "valkey",
+ "title": "valkey",
+ "shell": "install.sh",
+ "versions":["8.0.1","8.1.3"],
+ "tip": "soft",
+ "checks": "server/valkey",
+ "path": "server/valkey",
+ "display": 1,
+ "author": "valkey",
+ "date": "2024-12-08",
+ "home": "https://valkey.io/download/",
+ "type": 0,
+ "pid": "2"
+}
diff --git a/plugins/valkey/init.d/valkey.service.tpl b/plugins/valkey/init.d/valkey.service.tpl
new file mode 100644
index 000000000..ac2e6201b
--- /dev/null
+++ b/plugins/valkey/init.d/valkey.service.tpl
@@ -0,0 +1,12 @@
+[Unit]
+Description=Redis In-Memory Data Store
+After=network.target
+
+[Service]
+Type=forking
+ExecStart={$SERVER_PATH}/valkey/bin/valkey-server {$SERVER_PATH}/valkey/valkey.conf
+ExecReload=/bin/kill -USR2 $MAINPID
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/plugins/valkey/init.d/valkey.tpl b/plugins/valkey/init.d/valkey.tpl
new file mode 100644
index 000000000..c2d01c3c2
--- /dev/null
+++ b/plugins/valkey/init.d/valkey.tpl
@@ -0,0 +1,78 @@
+#!/bin/sh
+# chkconfig: 2345 55 25
+# description: Redis Service
+
+### BEGIN INIT INFO
+# Provides: Redis
+# Required-Start: $all
+# Required-Stop: $all
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts Redis
+# Description: starts the MDW-Web
+### END INIT INFO
+
+# Simple Redis init.d script conceived to work on Linux systems
+# as it does use of the /proc filesystem.
+
+CONF="{$SERVER_PATH}/valkey/valkey.conf"
+REDISPORT=$(cat $CONF |grep port|grep -v '#'|awk '{print $2}')
+REDISPASS=$(cat $CONF |grep requirepass|grep -v '#'|awk '{print $2}')
+if [ "$REDISPASS" != "" ];then
+ REDISPASS=" -a $REDISPASS"
+fi
+EXEC={$SERVER_PATH}/valkey/bin/valkey-server
+CLIEXEC="{$SERVER_PATH}/valkey/bin/valkey-cli -p $REDISPORT$REDISPASS"
+PIDFILE={$SERVER_PATH}/valkey/valkey.pid
+
+echo $REDISPASS
+echo $REDISPORT
+echo $CLIEXEC
+
+mkdir -p {$SERVER_PATH}/valkey/data
+
+valkey_start(){
+ if [ -f $PIDFILE ];then
+ kill -9 `cat $PIDFILE`
+ fi
+
+ echo "Starting valkey server..."
+ nohup $EXEC $CONF >> {$SERVER_PATH}/valkey/logs.pl 2>&1 &
+}
+
+valkey_stop(){
+ if [ ! -f $PIDFILE ]
+ then
+ echo "$PIDFILE does not exist, process is not running"
+ else
+ PID=$(cat $PIDFILE)
+ echo "Stopping ..."
+ $CLIEXEC shutdown save 2>/dev/null
+ while [ -x /proc/${PID} ]
+ do
+ echo "Waiting for valkey to shutdown ..."
+ sleep 1
+ done
+ echo "valkey stopped"
+ rm -rf $PIDFILE
+ fi
+}
+
+
+case "$1" in
+ start)
+ valkey_start
+ ;;
+ stop)
+ valkey_stop
+ ;;
+ restart|reload)
+ valkey_stop
+ sleep 0.3
+ valkey_start
+ ;;
+ *)
+ echo "Please use start or stop as first argument"
+ ;;
+esac
+
diff --git a/plugins/valkey/install.sh b/plugins/valkey/install.sh
new file mode 100755
index 000000000..a8343e137
--- /dev/null
+++ b/plugins/valkey/install.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+# https://www.cnblogs.com/zlonger/p/16177595.html
+# https://www.cnblogs.com/BNTang/articles/15841688.html
+
+# ps -ef|grep valkey |grep -v grep | awk '{print $2}' | xargs kill
+
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/valkey && bash install.sh install 7.2.2
+
+# cmd查看| info replication
+# /Users/midoks/Desktop/mwdev/server/valkey/bin/valkey-cli -h 127.0.0.1 -p 6399
+# /www/server/valkey/bin/valkey-cli -h 127.0.0.1 -p 6399
+
+VERSION=$2
+
+Install_App()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/source
+ mkdir -p $serverPath/source/valkey
+
+ FILE_TGZ=${VERSION}.tar.gz
+ VALKEY_DIR=$serverPath/source/valkey
+
+ if [ ! -f $VALKEY_DIR/${FILE_TGZ} ];then
+ wget --no-check-certificate -O $VALKEY_DIR/${FILE_TGZ} https://github.com/valkey-io/valkey/archive/refs/tags/${FILE_TGZ}
+ fi
+
+ cd $VALKEY_DIR && tar -zxvf ${FILE_TGZ}
+
+ CMD_MAKE=`which gmake`
+ if [ "$?" == "0" ];then
+ cd valkey-${VERSION} && gmake PREFIX=$serverPath/valkey install
+ else
+ cd valkey-${VERSION} && make PREFIX=$serverPath/valkey install
+ fi
+
+ if [ -d $serverPath/valkey ];then
+ mkdir -p $serverPath/valkey/data
+ sed '/^ *#/d' valkey.conf > $serverPath/valkey/valkey.conf
+
+ echo "${VERSION}" > $serverPath/valkey/version.pl
+ echo '安装完成'
+
+ cd ${rootPath} && python3 plugins/valkey/index.py start
+ cd ${rootPath} && python3 plugins/valkey/index.py initd_install
+
+ else
+ echo '安装失败!'
+ fi
+
+ if [ -d ${REDIS_DIR}/valkey-${VERSION} ];then
+ rm -rf ${REDIS_DIR}/valkey-${VERSION}
+ fi
+}
+
+Uninstall_App()
+{
+ if [ -f /usr/lib/systemd/system/valkey.service ];then
+ systemctl stop valkey
+ systemctl disable valkey
+ rm -rf /usr/lib/systemd/system/valkey.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f /lib/systemd/system/valkey.service ];then
+ systemctl stop valkey
+ systemctl disable valkey
+ rm -rf /lib/systemd/system/valkey.service
+ systemctl daemon-reload
+ fi
+
+ if [ -f $serverPath/valkey/initd/valkey ];then
+ $serverPath/valkey/initd/valkey stop
+ fi
+
+ if [ -d $serverPath/valkey ];then
+ rm -rf $serverPath/valkey
+ fi
+
+ echo "卸载valkey成功"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/valkey/js/valkey.js b/plugins/valkey/js/valkey.js
new file mode 100755
index 000000000..9540415f4
--- /dev/null
+++ b/plugins/valkey/js/valkey.js
@@ -0,0 +1,297 @@
+function redisPost(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'valkey';
+ req_data['func'] = method;
+ req_data['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ //错误展示10S
+ layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function redisPostCallbak(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'valkey';
+ req_data['func'] = method;
+ args['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+//redis状态 start
+function redisStatus(version) {
+
+ redisPost('run_info',version, {},function(data){
+ var rdata = $.parseJSON(data.data);
+
+ if ('status' in rdata && !rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ hit = (parseInt(rdata.keyspace_hits) / (parseInt(rdata.keyspace_hits) + parseInt(rdata.keyspace_misses)) * 100).toFixed(2);
+ var con = '\
+
\
+ 字段 当前值 说明 \
+ \
+ uptime_in_days ' + rdata.uptime_in_days + ' 已运行天数 \
+ tcp_port ' + rdata.tcp_port + ' 当前监听端口 \
+ connected_clients ' + rdata.connected_clients + ' 连接的客户端数量 \
+ used_memory_rss ' + toSize(rdata.used_memory_rss) + ' Redis当前占用的系统内存总量 \
+ used_memory ' + toSize(rdata.used_memory) + ' Redis当前已分配的内存总量 \
+ used_memory_peak ' + toSize(rdata.used_memory_peak) + ' Redis历史分配内存的峰值 \
+ mem_fragmentation_ratio ' + rdata.mem_fragmentation_ratio + '% 内存碎片比率 \
+ total_connections_received ' + rdata.total_connections_received + ' 运行以来连接过的客户端的总数量 \
+ total_commands_processed ' + rdata.total_commands_processed + ' 运行以来执行过的命令的总数量 \
+ instantaneous_ops_per_sec ' + rdata.instantaneous_ops_per_sec + ' 服务器每秒钟执行的命令数量 \
+ keyspace_hits ' + rdata.keyspace_hits + ' 查找数据库键成功的次数 \
+ keyspace_misses ' + rdata.keyspace_misses + ' 查找数据库键失败的次数 \
+ hit ' + hit + '% 查找数据库键命中率 \
+ latest_fork_usec ' + rdata.latest_fork_usec + ' 最近一次 fork() 操作耗费的微秒数 \
+ \
+
';
+ $(".soft-man-con").html(con);
+ });
+}
+
+function replStatus(version){
+ redisPost('info_replication', version, {},function(data){
+ var rdata = $.parseJSON(data.data);
+
+ if ('status' in rdata && !rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ var kv = {
+ 'role':'角色',
+ 'master_host':'连接主库HOST',
+ 'master_port':'连接主库PORT',
+ 'master_link_status':'连接主库状态',
+ 'master_last_io_seconds_ago':'上次同步时间',
+ 'master_sync_in_progress':'正在同步中',
+ 'slave_read_repl_offset':'从库读取复制位置',
+ 'slave_repl_offset':'从库复制位置',
+ 'slave_read_only':'从库是否仅读',
+ 'replica_announced':'已复制副本',
+ 'connected_slaves':'连接数量',
+ 'master_failover_state':'主库故障状态',
+ 'master_replid':'主库复制ID',
+ 'master_repl_offset':'主库复制位置',
+ 'repl_backlog_size':'backlog复制大小',
+ 'second_repl_offset':'复制位置时间',
+ 'repl_backlog_first_byte_offset':'第一个字节偏移量',
+ 'repl_backlog_histlen':'backlog中数据的长度',
+ 'repl_backlog_active':'开启复制缓冲区',
+ 'slave_priority':'同步优先级',
+ }
+
+ var tbody_text = '';
+ for (k in rdata){
+ if (k == 'master_replid'){
+ tbody_text += ' '+k+' ' + rdata[k] + ' '+kv[k]+' ';
+ } else{
+
+ if (k.substring(0,5) == 'slave' && !isNaN(k.substring(5))){
+ tbody_text += ''+k+' ' + rdata[k] + ' 从库配置信息 ';
+ } else{
+ tbody_text += ''+k+' ' + rdata[k] + ' '+kv[k]+' ';
+ }
+
+
+ }
+ }
+
+ var con = '\
+
\
+ 字段 当前值 说明 \
+ '+tbody_text+' \
+
';
+ $(".soft-man-con").html(con);
+ });
+}
+
+function clusterStatus(version){
+ redisPost('cluster_info', version, {},function(data){
+ var rdata = $.parseJSON(data.data);
+
+ if ('status' in rdata && !rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ var kv = {
+ 'cluster_state':'集群状态',
+ 'cluster_slots_assigned':'被分配的槽',
+ 'cluster_slots_ok':'被分配的槽状态',
+ 'cluster_known_nodes':'知道的节点',
+ 'cluster_size':'大小',
+ 'cluster_stats_messages_sent':'发送',
+ 'cluster_stats_messages_received':'接收',
+ 'cluster_current_epoch':'集群当前epoch',
+ 'cluster_my_epoch':'当前我的epoch',
+ 'cluster_slots_pfail':'处于PFAIL状态的槽数',
+ 'cluster_slots_fail':'处于FAIL状态的槽数',
+ 'total_cluster_links_buffer_limit_exceeded':'超出缓冲区总数',
+ }
+
+ var tbody_text = '';
+ for (k in rdata){
+ var desc = k;
+ if (k in kv){
+ desc = kv[k];
+ }
+
+ if (k == 'master_replid'){
+ tbody_text += ''+k+' ' + rdata[k] + ' '+desc+' ';
+ } else{
+ tbody_text += ''+k+' ' + rdata[k] + ' '+desc+' ';
+ }
+ }
+
+ if (tbody_text == ''){
+ tbody_text += '无数据/未设置集群 ';
+ }
+
+ var con = '\
+
\
+ 字段 当前值 说明 \
+ '+tbody_text+' \
+
';
+ $(".soft-man-con").html(con);
+ });
+}
+
+function clusterNodes(version){
+ redisPost('cluster_nodes', version, {},function(data){
+ var rdata = $.parseJSON(data.data);
+
+ if ('status' in rdata && !rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ // console.log(rdata);
+ var tbody_text = '';
+ for (k in rdata){
+ tbody_text += ''+ rdata[k] +' ';
+ }
+
+ if (tbody_text == ''){
+ tbody_text += '无数据/未设置集群 ';
+ }
+
+ var con = '\
+
\
+ 节点信息 \
+ '+tbody_text+' \
+
';
+ $(".soft-man-con").html(con);
+ });
+}
+
+//redis状态 end
+
+//配置修改
+function getRedisConfig(version) {
+ redisPost('get_redis_conf', version,'',function(data){
+ // console.log(data);
+ var rdata = $.parseJSON(data.data);
+ // console.log(rdata);
+ var mlist = '';
+ for (var i = 0; i < rdata.length; i++) {
+ var w = '70'
+ if (rdata[i].name == 'error_reporting') w = '250';
+ var ibody = ' ';
+ switch (rdata[i].type) {
+ case 0:
+ var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
+ var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
+ ibody = '开启 关闭 '
+ break;
+ case 1:
+ var selected_1 = (rdata[i].value == 'On') ? 'selected' : '';
+ var selected_0 = (rdata[i].value == 'Off') ? 'selected' : '';
+ ibody = '开启 关闭 '
+ break;
+ }
+ mlist += '' + rdata[i].name + ' ' + ibody + ', ' + rdata[i].ps + '
'
+ }
+ var con = '' + mlist + '\
+
刷新 \
+ 保存
\
+
'
+ $(".soft-man-con").html(con);
+ });
+}
+
+//提交配置
+function submitConf(version) {
+ var data = {
+ version: version,
+ bind: $("input[name='bind']").val(),
+ 'port': $("input[name='port']").val(),
+ 'timeout': $("input[name='timeout']").val(),
+ maxclients: $("input[name='maxclients']").val(),
+ databases: $("input[name='databases']").val(),
+ requirepass: $("input[name='requirepass']").val(),
+ maxmemory: $("input[name='maxmemory']").val(),
+ };
+
+ redisPost('submit_redis_conf', version, data, function(ret_data){
+ var rdata = $.parseJSON(ret_data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
+
+
+function valkeyReadme(){
+ var cmd_01 = '/www/server/valkey/bin/valkey-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 --cluster-replicas 0';
+ var cmd_02 = '/www/server/valkey/bin/valkey-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 --cluster-replicas 1';
+
+
+ var readme = '';
+ readme += '集群创建1 ';
+ readme += ''+cmd_01+' ';
+ readme += '集群创建2 ';
+ readme += ''+cmd_02+' ';
+ readme += ' ';
+
+ $('.soft-man-con').html(readme);
+}
+
diff --git a/plugins/valkey/tpl/valkey_cluster.conf b/plugins/valkey/tpl/valkey_cluster.conf
new file mode 100644
index 000000000..100d86cef
--- /dev/null
+++ b/plugins/valkey/tpl/valkey_cluster.conf
@@ -0,0 +1,94 @@
+daemonize yes
+pidfile {$SERVER_PATH}/valkey/valkey.pid
+
+loglevel notice
+logfile {$SERVER_PATH}/valkey/data/valkey.log
+databases 16
+
+timeout 0
+tcp-keepalive 0
+
+bind 127.0.0.1
+port 6379
+
+################################ SNAPSHOTTING #################################
+
+save 900 100
+save 300 1000
+save 60 1000000
+stop-writes-on-bgsave-error yes
+rdbcompression yes
+rdbchecksum yes
+dbfilename dump.rdb
+dir {$SERVER_PATH}/valkey/data/
+
+################################# CLUSTER #################################
+
+
+protected-mode no
+cluster-enabled yes
+cluster-config-file nodes_{$REDIS_PASS}.conf
+cluster-require-full-coverage no
+cluster-node-timeout 15000
+
+################################# REPLICATION #################################
+
+slave-serve-stale-data yes
+slave-read-only yes
+
+repl-disable-tcp-nodelay no
+slave-priority 100
+
+################################## SECURITY ###################################
+
+
+################################### LIMITS ####################################
+maxclients 10000
+#maxmemory-samples 3
+maxmemory 218mb
+maxmemory-policy volatile-ttl
+
+############################## APPEND ONLY MODE ###############################
+
+
+#appendonly no
+# appendfsync always
+#appendfsync everysec
+# appendfsync no
+#no-appendfsync-on-rewrite no
+
+auto-aof-rewrite-percentage 100
+auto-aof-rewrite-min-size 64mb
+
+################################ LUA SCRIPTING ###############################
+
+lua-time-limit 5000
+
+################################## SLOW LOG ###################################
+
+
+slowlog-log-slower-than 10000
+slowlog-max-len 128
+
+############################### ADVANCED CONFIG ###############################
+
+hash-max-ziplist-entries 512
+hash-max-ziplist-value 64
+
+list-max-ziplist-entries 512
+list-max-ziplist-value 64
+
+set-max-intset-entries 512
+
+zset-max-ziplist-entries 128
+zset-max-ziplist-value 64
+
+activerehashing yes
+
+client-output-buffer-limit normal 0 0 0
+client-output-buffer-limit slave 256mb 64mb 60
+client-output-buffer-limit pubsub 32mb 8mb 60
+
+hz 10
+
+aof-rewrite-incremental-fsync yes
\ No newline at end of file
diff --git a/plugins/valkey/tpl/valkey_simple.conf b/plugins/valkey/tpl/valkey_simple.conf
new file mode 100644
index 000000000..ba0e9ae21
--- /dev/null
+++ b/plugins/valkey/tpl/valkey_simple.conf
@@ -0,0 +1,87 @@
+daemonize yes
+pidfile {$SERVER_PATH}/valkey/valkey.pid
+
+bind 127.0.0.1
+port 6379
+requirepass {$VALKEY_PASS}
+
+timeout 0
+tcp-keepalive 0
+
+loglevel notice
+
+logfile {$SERVER_PATH}/valkey/data/valkey.log
+databases 16
+
+################################ SNAPSHOTTING #################################
+
+save 900 100
+save 300 1000
+save 60 1000000
+stop-writes-on-bgsave-error yes
+rdbcompression yes
+rdbchecksum yes
+dbfilename dump.rdb
+dir {$SERVER_PATH}/valkey/data/
+
+################################# REPLICATION #################################
+
+slave-serve-stale-data yes
+slave-read-only yes
+
+repl-disable-tcp-nodelay no
+slave-priority 100
+
+################################## SECURITY ###################################
+
+
+################################### LIMITS ####################################
+maxclients 10000
+#maxmemory-samples 3
+maxmemory 218mb
+maxmemory-policy volatile-ttl
+
+############################## APPEND ONLY MODE ###############################
+
+
+#appendonly no
+# appendfsync always
+#appendfsync everysec
+# appendfsync no
+#no-appendfsync-on-rewrite no
+
+auto-aof-rewrite-percentage 100
+auto-aof-rewrite-min-size 64mb
+
+################################ LUA SCRIPTING ###############################
+
+lua-time-limit 5000
+
+################################## SLOW LOG ###################################
+
+
+slowlog-log-slower-than 10000
+slowlog-max-len 128
+
+############################### ADVANCED CONFIG ###############################
+
+hash-max-ziplist-entries 512
+hash-max-ziplist-value 64
+
+list-max-ziplist-entries 512
+list-max-ziplist-value 64
+
+set-max-intset-entries 512
+
+zset-max-ziplist-entries 128
+zset-max-ziplist-value 64
+
+activerehashing yes
+
+client-output-buffer-limit normal 0 0 0
+client-output-buffer-limit slave 256mb 64mb 60
+client-output-buffer-limit pubsub 32mb 8mb 60
+
+hz 10
+
+aof-rewrite-incremental-fsync yes
\ No newline at end of file
diff --git a/plugins/valkey/tpl/valkey_slave.conf b/plugins/valkey/tpl/valkey_slave.conf
new file mode 100644
index 000000000..de9184030
--- /dev/null
+++ b/plugins/valkey/tpl/valkey_slave.conf
@@ -0,0 +1,91 @@
+daemonize yes
+pidfile {$SERVER_PATH}/valkey/valkey.pid
+
+bind 127.0.0.1
+port 6379
+requirepass {$VALKEY_PASS}
+
+timeout 0
+tcp-keepalive 0
+
+loglevel notice
+
+logfile {$SERVER_PATH}/valkey/data/valkey.log
+databases 16
+
+################################ SNAPSHOTTING #################################
+
+save 900 100
+save 300 1000
+save 60 1000000
+stop-writes-on-bgsave-error yes
+rdbcompression yes
+rdbchecksum yes
+dbfilename dump.rdb
+dir {$SERVER_PATH}/valkey/data/
+
+################################# REPLICATION #################################
+
+slave-serve-stale-data yes
+slave-read-only yes
+
+repl-disable-tcp-nodelay no
+slave-priority 100
+
+# 填写主库信息
+#slaveof 127.0.0.1 6379
+#masterauth 123123
+
+################################## SECURITY ###################################
+
+
+################################### LIMITS ####################################
+maxclients 10000
+#maxmemory-samples 3
+maxmemory 0mb
+maxmemory-policy volatile-ttl
+
+############################## APPEND ONLY MODE ###############################
+
+
+#appendonly no
+# appendfsync always
+#appendfsync everysec
+# appendfsync no
+#no-appendfsync-on-rewrite no
+
+auto-aof-rewrite-percentage 100
+auto-aof-rewrite-min-size 64mb
+
+################################ LUA SCRIPTING ###############################
+
+lua-time-limit 5000
+
+################################## SLOW LOG ###################################
+
+
+slowlog-log-slower-than 10000
+slowlog-max-len 128
+
+############################### ADVANCED CONFIG ###############################
+
+hash-max-ziplist-entries 512
+hash-max-ziplist-value 64
+
+list-max-ziplist-entries 512
+list-max-ziplist-value 64
+
+set-max-intset-entries 512
+
+zset-max-ziplist-entries 128
+zset-max-ziplist-value 64
+
+activerehashing yes
+
+client-output-buffer-limit normal 0 0 0
+client-output-buffer-limit slave 256mb 64mb 60
+client-output-buffer-limit pubsub 32mb 8mb 60
+
+hz 10
+
+aof-rewrite-incremental-fsync yes
\ No newline at end of file
diff --git a/plugins/valkey/tpl/valkey_slave_mem.conf b/plugins/valkey/tpl/valkey_slave_mem.conf
new file mode 100644
index 000000000..baf66f54f
--- /dev/null
+++ b/plugins/valkey/tpl/valkey_slave_mem.conf
@@ -0,0 +1,75 @@
+daemonize yes
+pidfile {$SERVER_PATH}/valkey/valkey.pid
+
+loglevel notice
+logfile {$SERVER_PATH}/valkey/data/valkey.log
+databases 16
+
+timeout 0
+tcp-keepalive 0
+
+bind 127.0.0.1
+port 6379
+requirepass {$VALKEY_PASS}
+
+################################ SNAPSHOTTING #################################
+
+save ""
+stop-writes-on-bgsave-error no
+
+################################# REPLICATION #################################
+
+slave-serve-stale-data yes
+slave-read-only yes
+
+repl-disable-tcp-nodelay no
+slave-priority 100
+
+# 填写主库信息
+#slaveof 127.0.0.1 6379
+#masterauth 123123
+
+################################## SECURITY ###################################
+
+
+################################### LIMITS ####################################
+maxclients 10000
+#maxmemory-samples 3
+maxmemory 218mb
+maxmemory-policy allkeys-lru
+
+############################## APPEND ONLY MODE ###############################
+
+
+################################ LUA SCRIPTING ###############################
+
+lua-time-limit 5000
+
+################################## SLOW LOG ###################################
+
+
+slowlog-log-slower-than 10000
+slowlog-max-len 128
+
+############################### ADVANCED CONFIG ###############################
+
+hash-max-ziplist-entries 512
+hash-max-ziplist-value 64
+
+list-max-ziplist-entries 512
+list-max-ziplist-value 64
+
+set-max-intset-entries 512
+
+zset-max-ziplist-entries 128
+zset-max-ziplist-value 64
+
+activerehashing no
+
+client-output-buffer-limit normal 0 0 0
+client-output-buffer-limit slave 256mb 64mb 60
+client-output-buffer-limit pubsub 32mb 8mb 60
+
+hz 10
+
+aof-rewrite-incremental-fsync yes
\ No newline at end of file
diff --git a/plugins/varnish/ico.png b/plugins/varnish/ico.png
new file mode 100644
index 000000000..3d8c4eb1b
Binary files /dev/null and b/plugins/varnish/ico.png differ
diff --git a/plugins/varnish/index.html b/plugins/varnish/index.html
new file mode 100755
index 000000000..d250b0686
--- /dev/null
+++ b/plugins/varnish/index.html
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/plugins/varnish/index.py b/plugins/varnish/index.py
new file mode 100755
index 000000000..5336dc01a
--- /dev/null
+++ b/plugins/varnish/index.py
@@ -0,0 +1,212 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+
+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 'varnish'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getInitDFile():
+ if app_debug:
+ return '/tmp/' + getPluginName()
+ return '/etc/init.d/' + getPluginName()
+
+
+def getConf():
+ path = '/etc/varnish/vcl.conf'
+ if os.path.exists(path):
+ return path
+ path = "/etc/varnish/default.vcl"
+ return path
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
+ return path
+
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$SERVER_APP}', service_path + '/varnish')
+ return content
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ 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 status():
+ data = mw.execShell(
+ "ps -ef|grep varnish |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'")
+
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def vaOp(method):
+ mw.execShell("systemctl daemon-reload")
+ data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
+ if data[1] == '':
+ return 'ok'
+ return 'fail'
+
+
+def start():
+ return vaOp('start')
+
+
+def stop():
+ return vaOp('stop')
+
+
+def restart():
+ return vaOp('restart')
+
+
+def reload():
+ return vaOp('reload')
+
+
+def runInfo():
+ cmd = "varnishstat -j"
+ data = mw.execShell(cmd)[0].strip()
+ return data
+
+
+def configTpl():
+ path = getPluginDir() + '/tpl'
+ pathFile = os.listdir(path)
+ tmp = []
+ for one in pathFile:
+ file = path + '/' + one
+ tmp.append(file)
+ return mw.getJson(tmp)
+
+
+def readConfigTpl():
+ args = getArgs()
+ data = checkArgs(args, ['file'])
+ if not data[0]:
+ return data[1]
+
+ content = mw.readFile(args['file'])
+ content = contentReplace(content)
+ return mw.returnJson(True, 'ok', content)
+
+
+def initdStatus():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status ' + \
+ getPluginName() + ' | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+
+def initdInstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl enable ' + getPluginName())
+ return 'ok'
+
+
+def initdUinstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl disable ' + getPluginName())
+ return 'ok'
+
+
+def runLog():
+
+ if os.path.exists("/var/log/varnish/varnish.log"):
+ return "/var/log/varnish/varnish.log"
+
+ return "/var/log/varnish/varnishncsa.log"
+
+
+def confService():
+ return mw.systemdCfgDir() + '/varnish.service'
+
+if __name__ == "__main__":
+ 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 == 'run_info':
+ print(runInfo())
+ elif func == 'conf':
+ print(getConf())
+ elif func == 'conf_service':
+ print(confService())
+ elif func == 'run_log':
+ print(runLog())
+ elif func == 'config_tpl':
+ print(configTpl())
+ elif func == 'read_config_tpl':
+ print(readConfigTpl())
+ else:
+ print('error')
diff --git a/plugins/varnish/info.json b/plugins/varnish/info.json
new file mode 100755
index 000000000..a550cd2a7
--- /dev/null
+++ b/plugins/varnish/info.json
@@ -0,0 +1,18 @@
+{
+ "sort": 7,
+ "ps": "一款高性能的开源HTTP加速器",
+ "name": "varnish",
+ "title": "varnish",
+ "shell": "install.sh",
+ "versions":["1.0"],
+ "updates":["1.0"],
+ "tip": "soft",
+ "checks": "server/varnish",
+ "path": "server/varnish",
+ "display": 1,
+ "author": "midoks",
+ "date": "2022-06-11",
+ "home": "https://varnish-cache.org",
+ "type": 0,
+ "pid": "4"
+}
\ No newline at end of file
diff --git a/plugins/varnish/install.sh b/plugins/varnish/install.sh
new file mode 100755
index 000000000..2ce3ad69f
--- /dev/null
+++ b/plugins/varnish/install.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+VERSION=$2
+
+
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+OSNAME_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+
+Install_varnish()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/source
+
+ if [ "${OSNAME}" == "macos" ]; then
+ brew install varnish
+ elif [ "${OSNAME}" == "centos" ] || [ "${OSNAME}" == "fedora" ] || [ "${OSNAME}" == "alma" ] || [ "${OSNAME}" == "rocky" ]; then
+ yum install varnish -y
+ elif [ "${OSNAME}" == "debian" ] || [ "${OSNAME}" == "ubuntu" ]; then
+ apt install varnish -y
+ elif [[ "$OSNAME" == "arch" ]]; then
+ echo y | pacman -Sy varnish
+ elif [ "${OSNAME}" == "opensuse" ];then
+ zypper install -y varnish
+ else
+ echo "I won't support it"
+ exit 1
+ fi
+
+ mkdir -p $serverPath/varnish
+ echo "1.0" > $serverPath/varnish/version.pl
+ echo '安装完成'
+
+ cd ${rootPath} && python3 ${rootPath}/plugins/varnish/index.py start
+ cd ${rootPath} && python3 ${rootPath}/plugins/varnish/index.py initd_install
+}
+
+Uninstall_varnish()
+{
+ cd ${rootPath} && python3 ${rootPath}/plugins/varnish/index.py stop
+ cd ${rootPath} && python3 ${rootPath}/plugins/varnish/index.py initd_uninstall
+
+ if [ "${OSNAME}" == "macos" ]; then
+ brew uninstall varnish
+ elif [ "${OSNAME}" == "centos" ] || [ "${OSNAME}" == "fedora" ] || [ "${OSNAME}" == "alma" ] || [ "${OSNAME}" == "rocky" ]; then
+ yum remove varnish -y
+ elif [ "${OSNAME}" == "debian" ] || [ "${OSNAME}" == "ubuntu" ]; then
+ apt remove varnish -y
+ elif [[ "$OSNAME" == "arch" ]]; then
+ echo y | pacman -Rv varnish
+ elif [ "${OSNAME}" == "opensuse" ];then
+ zypper remove -y varnish
+ else
+ echo "I won't support it"
+ fi
+ rm -rf $serverPath/varnish
+ echo "uninstall varnish"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_varnish
+else
+ Uninstall_varnish
+fi
diff --git a/plugins/varnish/js/varnish.js b/plugins/varnish/js/varnish.js
new file mode 100644
index 000000000..16adeba78
--- /dev/null
+++ b/plugins/varnish/js/varnish.js
@@ -0,0 +1,101 @@
+
+
+function pRead(){
+ var readme = '';
+ readme += '修改后,点击重启按钮 ';
+ readme += ' ';
+
+ $('.soft-man-con').html(readme);
+}
+
+
+//varnish负载状态 start
+function varnishStatus() {
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+ $.post('/plugins/run', {name:'varnish', func:'run_info'}, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ var rdata = $.parseJSON(data.data);
+ console.log(rdata);
+
+ var tmp = "";
+ for (let i in rdata) {
+ // console.log(i,rdata[i]);
+ if (i == 'timestamp'){
+ tmp += ""+i+" "+rdata[i]+" "
+ } else{
+ tmp += ""+i+" "+rdata[i]['value']+" "+rdata[i]['description']+" "
+ }
+ }
+
+ hit = (parseInt(rdata.keyspace_hits) / (parseInt(rdata.keyspace_hits) + parseInt(rdata.keyspace_misses)) * 100).toFixed(2);
+ var Con = '\
+
\
+ 字段 当前值 说明 \
+ '+tmp+' \
+
'
+ $(".soft-man-con").html(Con);
+ },'json');
+}
+//varnish负载状态 end
+
+
+//varnish service ---
+function varnishPluginConfig(_name, version, func){
+ if ( typeof(version) == 'undefined' ){
+ version = '';
+ }
+
+ var func_name = 'conf';
+ if ( typeof(func) != 'undefined' ){
+ func_name = func;
+ }
+
+ var con = '提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换!
\
+ \
+ 保存 \
+ \
+ 此处为'+ _name + version +'主配置文件,若您不了解配置规则,请勿随意修改。 \
+ ';
+ $(".soft-man-con").html(con);
+
+ var loadT = layer.msg('配置文件路径获取中...',{icon:16,time:0,shade: [0.3, '#000']});
+ $.post('/plugins/run', {name:_name, func:func_name,version:version},function (data) {
+ layer.close(loadT);
+
+ var loadT2 = layer.msg('文件内容获取中...',{icon:16,time:0,shade: [0.3, '#000']});
+ var fileName = data.data;
+ $.post('/files/get_body', 'path=' + fileName, function(rdata) {
+ layer.close(loadT2);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ $("#textBody").empty().text(rdata.data.data);
+ $(".CodeMirror").remove();
+ var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), {
+ extraKeys: {
+ "Ctrl-Space": "autocomplete",
+ "Ctrl-F": "findPersistent",
+ "Ctrl-H": "replaceAll",
+ "Ctrl-S": function() {
+ $("#textBody").text(editor.getValue());
+ pluginConfigSave(fileName);
+ }
+ },
+ lineNumbers: true,
+ matchBrackets:true,
+ });
+ editor.focus();
+ $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0});
+ $("#onlineEditFileBtn").click(function(){
+ $("#textBody").text(editor.getValue());
+ pluginConfigSave(fileName);
+ });
+ },'json');
+ },'json');
+}
diff --git a/plugins/varnish/tpl/default.vcl b/plugins/varnish/tpl/default.vcl
new file mode 100755
index 000000000..b86560303
--- /dev/null
+++ b/plugins/varnish/tpl/default.vcl
@@ -0,0 +1,17 @@
+# https://www.varnish-cache.org/docs/
+vcl 4.0;
+
+# Default backend definition. Set this to point to your content server.
+backend default {
+ .host = "127.0.0.1";
+ .port = "8080";
+}
+
+sub vcl_recv {
+}
+
+sub vcl_backend_response {
+}
+
+sub vcl_deliver {
+}
diff --git a/plugins/varnish/tpl/default_note.vcl b/plugins/varnish/tpl/default_note.vcl
new file mode 100755
index 000000000..b86560303
--- /dev/null
+++ b/plugins/varnish/tpl/default_note.vcl
@@ -0,0 +1,17 @@
+# https://www.varnish-cache.org/docs/
+vcl 4.0;
+
+# Default backend definition. Set this to point to your content server.
+backend default {
+ .host = "127.0.0.1";
+ .port = "8080";
+}
+
+sub vcl_recv {
+}
+
+sub vcl_backend_response {
+}
+
+sub vcl_deliver {
+}
diff --git a/plugins/webhook/ico.png b/plugins/webhook/ico.png
new file mode 100644
index 000000000..633088a7e
Binary files /dev/null and b/plugins/webhook/ico.png differ
diff --git a/plugins/webhook/index.html b/plugins/webhook/index.html
new file mode 100755
index 000000000..0de594878
--- /dev/null
+++ b/plugins/webhook/index.html
@@ -0,0 +1,33 @@
+
+
+
添加Hook
+
+
+
+ 名称
+ 添加时间
+ 近期调用
+ 调用次数
+ 密钥
+ 操作
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/webhook/index.py b/plugins/webhook/index.py
new file mode 100755
index 000000000..473f3bbfe
--- /dev/null
+++ b/plugins/webhook/index.py
@@ -0,0 +1,296 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+
+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 'webhook'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ if t.strip() == '':
+ tmp = []
+ else:
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ 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 getCfgFilePath():
+ return getServerDir() + "/cfg.json"
+
+
+def initCfg():
+ cfg = getCfgFilePath()
+ data = []
+ mw.writeFile(cfg, json.dumps(data))
+
+
+def getCfg():
+ cfg = getCfgFilePath()
+ if not os.path.exists(cfg):
+ initCfg()
+
+ data = mw.readFile(cfg)
+ data = json.loads(data)
+ return data
+
+
+def addCfg(val):
+ cfg = getCfgFilePath()
+ data = getCfg()
+ data.append(val)
+ mw.writeFile(cfg, json.dumps(data))
+
+
+def status():
+ return 'start'
+
+
+def addHook():
+ args = getArgs()
+ data = checkArgs(args, ['title', "shell"])
+ if not data[0]:
+ return data[1]
+
+ hook = {}
+ hook['title'] = args['title']
+ if hook['title'] == '':
+ return mw.returnJson(False, '名称不能为空!')
+
+ hook['access_key'] = mw.md5(hook['title'])
+ hook['count'] = 0
+ hook['addtime'] = int(time.time())
+ hook['uptime'] = 0
+
+ script_dir = getServerDir() + "/scripts"
+ if not os.path.exists(script_dir):
+ os.mkdir(script_dir)
+
+ addCfg(hook)
+ shellFile = script_dir + '/' + hook['access_key']
+ mw.writeFile(shellFile, args['shell'])
+ return mw.returnJson(True, '添加成功!')
+
+def addHookShell(args):
+ if args['title'] == '':
+ return mw.returnJson(False, '名称不能为空!')
+
+ hook = {}
+ hook['title'] = args['title']
+ hook['access_key'] = mw.md5(hook['title'])
+ hook['count'] = 0
+ hook['addtime'] = int(time.time())
+ hook['uptime'] = 0
+
+ script_dir = getServerDir() + "/scripts"
+ if not os.path.exists(script_dir):
+ os.mkdir(script_dir)
+
+ addCfg(hook)
+ shellFile = script_dir + '/' + hook['access_key']
+ mw.writeFile(shellFile, args['shell'])
+ return mw.returnJson(True, '添加成功!')
+
+
+def getList():
+ data = getCfg()
+
+ rdata = {}
+ rdata['list'] = data
+ rdata['script_dir'] = getServerDir() + "/scripts"
+ return mw.returnJson(True, 'ok', rdata)
+
+
+def getLog():
+ args = getArgs()
+ check_arg = checkArgs(args, ['path'])
+ if not check_arg[0]:
+ return check_arg[1]
+
+ logPath = args['path']
+
+ content = mw.getLastLine(logPath, 500)
+ return mw.returnJson(True, 'ok', content)
+
+def getLogCb(args):
+ # print(args)
+ logPath = args['path']
+ content = mw.getLastLine(logPath, 3000)
+ return mw.returnData(True, 'ok', content)
+
+
+def runShellArgs(args):
+ data = getCfg()
+ for i in range(len(data)):
+ if data[i]['access_key'] == args['access_key']:
+ script_dir = getServerDir() + "/scripts"
+ shellFile = script_dir + '/' + args['access_key']
+ param = args['params']
+ if param == '':
+ param = 'no-parameters'
+
+ param = re.sub("\"", '', param)
+
+ cmd = "bash {} {} >> {}.log 2>&1 &".format(
+ shellFile, param, shellFile)
+ # print(cmd)
+ os.system(cmd)
+ data[i]['count'] += 1
+ data[i]['uptime'] = int(time.time())
+ mw.writeFile(getCfgFilePath(), json.dumps(data))
+ return mw.returnJson(True, '运行成功!')
+ return mw.returnJson(False, '指定Hook不存在!')
+
+def getRunShellCmd():
+ args = getArgs()
+ check_arg = checkArgs(args, ['access_key'])
+ if not check_arg[0]:
+ return check_arg[1]
+
+ script_dir = getServerDir() + "/scripts"
+ shellFile = script_dir + '/' + args['access_key']
+ param = ''
+ if 'params' in args:
+ param = args['params']
+ param = re.sub("\"", '', param)
+
+ cmd = ""
+ cmd += "git config --global credential.helper store\n"
+ cmd += "git config --global pull.rebase false\n"
+ cmd += "bash {} {}".format(shellFile, param)
+ return mw.returnJson(True, 'ok', cmd)
+
+
+def runShell():
+ args = getArgs()
+ check_arg = checkArgs(args, ['access_key'])
+ if not check_arg[0]:
+ return check_arg[1]
+
+ args['params'] = 'panel-test'
+ return runShellArgs(args)
+
+
+def delHook():
+ args = getArgs()
+ check_arg = checkArgs(args, ['access_key'])
+ if not check_arg[0]:
+ return check_arg[1]
+
+ data = getCfg()
+ newdata = []
+ for hook in data:
+ if hook['access_key'] == args['access_key']:
+ continue
+ newdata.append(hook)
+
+ jsonFile = getCfgFilePath()
+ shellFile = getServerDir() + "/scripts/" + args['access_key']
+ if not os.path.exists(shellFile):
+ return mw.returnJson(False, '删除失败!')
+ os.remove(shellFile)
+ log_file = "{}.log".format(shellFile)
+ if os.path.exists(log_file):
+ os.remove(log_file)
+
+ mw.writeFile(jsonFile, json.dumps(newdata))
+ return mw.returnJson(True, '删除成功!')
+
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ return content
+
+
+def configTpl():
+ path = getPluginDir() + '/tpl'
+ pathFile = os.listdir(path)
+ tmp = []
+ for one in pathFile:
+ file = path + '/' + one
+ tmp.append(file)
+ return mw.getJson(tmp)
+
+def readConfigTpl():
+ args = getArgs()
+ data = checkArgs(args, ['file', 'title'])
+ if not data[0]:
+ return data[1]
+
+ if args['title'] == '':
+ return mw.returnJson(False, '名称不能为空!')
+
+ content = mw.readFile(args['file'])
+ content = contentReplace(content)
+
+ content = content.replace('{$REPO}', args['title'])
+ content = content.replace('{$REPO_NAME}', mw.md5(args['title']))
+
+ return mw.returnJson(True, 'ok', content)
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ if func == 'status':
+ print(status())
+ elif func == "add_hook":
+ print(addHook())
+ elif func == "get_list":
+ print(getList())
+ elif func == "run_shell":
+ print(runShell())
+ elif func == 'run_shell_cmd':
+ print(getRunShellCmd())
+ elif func == 'del_hook':
+ print(delHook())
+ elif func == 'get_log':
+ print(getLog())
+ elif func == 'config_tpl':
+ print(configTpl())
+ elif func == 'read_config_tpl':
+ print(readConfigTpl())
+ else:
+ print('error')
diff --git a/plugins/webhook/info.json b/plugins/webhook/info.json
new file mode 100755
index 000000000..251feb70e
--- /dev/null
+++ b/plugins/webhook/info.json
@@ -0,0 +1,18 @@
+{
+ "sort": 4,
+ "ps": "WebHook,可设置回调脚本,通常用于第三方回调通知!",
+ "name": "webhook",
+ "title": "WebHook",
+ "shell": "install.sh",
+ "versions":["1.0"],
+ "updates":["1.0"],
+ "tip": "soft",
+ "checks": "server/webhook",
+ "path": "server/webhook",
+ "display": 1,
+ "author": "",
+ "date": "2022-11-02",
+ "home": "",
+ "type": 0,
+ "pid": "4"
+}
\ No newline at end of file
diff --git a/plugins/webhook/install.sh b/plugins/webhook/install.sh
new file mode 100755
index 000000000..7386b71d4
--- /dev/null
+++ b/plugins/webhook/install.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+VERSION=1.0
+
+Install_App()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/webhook
+ echo "${VERSION}" > $serverPath/webhook/version.pl
+ echo '安装完成'
+
+ which mw && mw restart
+}
+
+Uninstall_App()
+{
+ rm -rf $serverPath/webhook
+ echo "Uninstall_App"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/webhook/js/webhook.js b/plugins/webhook/js/webhook.js
new file mode 100755
index 000000000..dc3a30192
--- /dev/null
+++ b/plugins/webhook/js/webhook.js
@@ -0,0 +1,408 @@
+
+function whPost(method, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'webhook';
+ req_data['func'] = method;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ //错误展示10S
+ layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function whPostNoMessage(method, args,callback){
+
+ var req_data = {};
+ req_data['name'] = 'webhook';
+ req_data['func'] = method;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {;
+ if (!data.status){
+ //错误展示10S
+ layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function whPostCallbak(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'webhook';
+ req_data['func'] = method;
+ args['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function whPostCallbakN(method, version, args,callback){
+ var req_data = {};
+ req_data['name'] = 'webhook';
+ req_data['func'] = method;
+ args['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function getFileName(file){
+ var list = file.split('/');
+ var f = list[list.length-1];
+ return f;
+}
+
+var whEditor = null;
+
+//添加
+function addHook(){
+ layer.open({
+ type: 1,
+ area: '650px',
+ title: '添加Hook',
+ closeBtn: 2,
+ shift: 5,
+ shadeClose: false,
+ btn:['提交','关闭'],
+ content: "",
+ success:function(){
+
+ $("#hook_shell").empty().text('');
+ $(".CodeMirror").remove();
+ whEditor = CodeMirror.fromTextArea(document.getElementById("hook_shell"), {
+ extraKeys: {
+ "Ctrl-Space": "autocomplete",
+ "Ctrl-F": "findPersistent",
+ "Ctrl-H": "replaceAll",
+ "Ctrl-S": function() {}
+ },
+ lineNumbers: true,
+ matchBrackets:true,
+ mode:"sh",
+ });
+
+ $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0});
+ whEditor.focus();
+
+
+ whPostNoMessage('config_tpl','', function(data){
+ var rdata = $.parseJSON(data.data);
+ for (var i = 0; i < rdata.length; i++) {
+ $('#hook_tpl').append(''+getFileName(rdata[i])+' ');
+ }
+
+ $('#hook_tpl').change(function(){
+ var selected = $(this).val();
+ if (selected == '0') {
+ return;
+ }
+
+ var title = $('#hook_title').val();
+ var loadT = layer.msg('配置模版获取中...',{icon:16,time:0,shade: [0.3, '#000']});
+ whPostNoMessage('read_config_tpl', {file:selected,title:title}, function(data){
+ layer.close(loadT);
+ var rdata = $.parseJSON(data.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:16,time:2000,shade: [5, '#000']});
+ return;
+ }
+
+ $("#hook_shell").empty().text(rdata.data);
+ $(".CodeMirror").remove();
+ whEditor = CodeMirror.fromTextArea(document.getElementById("hook_shell"), {
+ extraKeys: {
+ "Ctrl-Space": "autocomplete",
+ "Ctrl-F": "findPersistent",
+ "Ctrl-H": "replaceAll",
+ "Ctrl-S": function() {}
+ },
+ lineNumbers: true,
+ matchBrackets:true,
+ mode:"sh",
+ });
+
+ $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0});
+ whEditor.focus();
+
+ });
+ });
+ });
+
+ },
+ yes:function(indexs){
+ var loadT = layer.msg("提交中...",{icon:16,time:0});
+ var data = {
+ title: $("#hook_title").val(),
+ shell: whEditor.getValue(),
+ }
+ // whPost('add_hook', data, function(rdata){
+ // var rdata = $.parseJSON(rdata.data);
+ // if (!rdata.status){
+ // layer.msg(rdata.msg,{icon:2});
+ // return;
+ // }
+ // layer.close(indexs);
+ // showMsg(rdata.msg, function(){
+ // getHookList();
+ // }, {icon:1}, 2000);
+ // });
+
+ whPostCallbak('addHookShell', '', data, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:2});
+ return;
+ }
+ layer.close(indexs);
+ showMsg(rdata.msg, function(){
+ getHookList();
+ }, {icon:1}, 2000);
+ });
+ }
+ });
+}
+//获取列表
+function getHookList(){
+ whPost('get_list', {}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var zbody = '';
+ var mlist = rdata.data.list;
+ var script_dir = rdata.data.script_dir;
+ for(var i=0;i'
+ +''+mlist[i].title+' '
+ +''+getLocalTime(mlist[i].addtime)+' '
+ +''+getLocalTime(mlist[i].uptime)+' '
+ +''+mlist[i].count+' '
+ +'查看密钥 '
+ +'测试 | '
+ +'命令 | '
+ +'编辑 | '
+ +'日志 | '
+ +'删除 '
+ +''
+ }
+ $("#zipBody").html(zbody);
+ });
+}
+
+//查看密钥
+function showWebHookCode(url,code){
+ layer.open({
+ type:1,
+ title:'查看密钥',
+ area: '600px',
+ shadeClose:false,
+ closeBtn:2,
+ content:''
+ })
+}
+
+function getLogsTimer(path,success,error){
+ whPostNoMessage('get_log', {"path":path}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ if (!rdata.status){
+ if (typeof(error) == 'function'){
+ error();
+ }
+ return;
+ }
+
+ $('[name="site_logs"]').text(rdata.data);
+ if (typeof(success) == 'function'){
+ success();
+ }
+ });
+}
+
+function getLogsTimerCb(path,success,error){
+ whPostCallbakN('getLogCb', "", {"path":path}, function(rdata){
+ var rdata = rdata.data;
+ if (!rdata.status){
+ if (typeof(error) == 'function'){
+ error();
+ }
+ return;
+ }
+
+ $('[name="site_logs"]').text(rdata.data);
+ if (typeof(success) == 'function'){
+ success();
+ }
+ });
+}
+
+//查看日志
+function getLogs(path){
+ logs_web = layer.open({
+ type:1,
+ title:'任务执行日志',
+ area: ['600px','400px'],
+ shadeClose:false,
+ closeBtn:2,
+ content:'\
+ 正在获取中... \
+
',
+ success:function(){
+ $('[name="site_logs"]').scrollTop($('[name="site_logs"]')[0].scrollHeight);
+
+ logs_timer = setInterval(function(){
+ getLogsTimerCb(path,function(){
+
+ },function(){
+ layer.msg(rdata.msg,{icon:2});
+ layer.close(logs_web);
+ });
+ },1000);
+ },
+ cancel:function(){
+ clearInterval(logs_timer);
+ }
+ });
+}
+
+//运行
+function runHook(key){
+ whPost('run_shell', {"access_key":key}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:2});
+ }
+
+ showMsg(rdata.msg,function(){
+ getHookList();
+ },{icon:1},2000);
+ });
+}
+
+
+function getRunHookCmd(key) {
+ whPost('run_shell_cmd', {"access_key":key}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:2});
+ }
+ layer.open({
+ title: "手动执行命令CMD",
+ area: ['600px', '180px'],
+ type:1,
+ closeBtn: 1,
+ shadeClose: false,
+ btn:["复制","取消"],
+ content: '',
+ success:function(){
+ copyText(rdata.data);
+ },
+ yes:function(){
+ copyText(rdata.data);
+ }
+ });
+ });
+}
+
+//删除
+function deleteHook(key, title){
+ layer.confirm('删除Hook-['+ title +']',{
+ title:'是否删除Hook-['+ title +']任务,是否继续'
+ },function(){
+ whPost('del_hook', {"access_key":key}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:2});
+ }
+
+ showMsg(rdata.msg,function(){
+ getHookList();
+ },{icon:1},2000);
+ });
+ });
+}
+
diff --git a/plugins/webhook/tpl/git.tpl b/plugins/webhook/tpl/git.tpl
new file mode 100644
index 000000000..26004bb91
--- /dev/null
+++ b/plugins/webhook/tpl/git.tpl
@@ -0,0 +1,34 @@
+echo "" > {$ROOT_PATH}/server/webhook/scripts/{$REPO_NAME}.log
+
+export HOME=/tmp
+date "+%Y-%m-%d %H:%M:%S"
+
+# 和手动命令一起执行一次
+git config --global credential.helper store
+git config --global pull.rebase false
+
+
+if [ ! -d {$ROOT_PATH}/gitcode ];then
+ mkdir -p {$ROOT_PATH}/gitcode
+fi
+
+if [ -d {$ROOT_PATH}/gitcode/{$REPO} ];then
+ which sudo
+ if [ "$?" == "0" ];then
+ cd {$ROOT_PATH}/gitcode/{$REPO} && sudo git pull
+ else
+ cd {$ROOT_PATH}/gitcode/{$REPO} && git pull
+ fi
+else
+ cd {$ROOT_PATH}/gitcode && git clone http://0.0.0.0:6660/xx/{$REPO}
+fi
+
+if [ ! -d {$ROOT_PATH}/wwwroot/{$REPO} ];then
+ mkdir -p {$ROOT_PATH}/wwwroot/{$REPO}
+fi
+
+rsync -vauP '--exclude=.*' {$ROOT_PATH}/gitcode/{$REPO}/ {$ROOT_PATH}/wwwroot/{$REPO}
+chown -R www:www {$ROOT_PATH}/wwwroot/{$REPO}
+
+
+
diff --git a/plugins/webhook/webhook_index.py b/plugins/webhook/webhook_index.py
new file mode 100755
index 000000000..dc60912d3
--- /dev/null
+++ b/plugins/webhook/webhook_index.py
@@ -0,0 +1,67 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+
+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 'webhook'
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getCfgFilePath():
+ return getServerDir() + "/cfg.json"
+
+
+def getCfg():
+ cfg = getCfgFilePath()
+ if not os.path.exists(cfg):
+ initCfg()
+
+ data = mw.readFile(cfg)
+ data = json.loads(data)
+ return data
+
+
+def runShellArgs(args):
+ data = getCfg()
+ for i in range(len(data)):
+ if data[i]['access_key'] == args['access_key']:
+ script_dir = getServerDir() + "/scripts"
+ shellFile = script_dir + '/' + args['access_key']
+ param = args['params']
+ if param == '':
+ param = 'no-parameters'
+
+ param = re.sub("\"", '', param)
+
+ cmd = "bash {} {} >> {}.log 2>&1 &".format(
+ shellFile, param, shellFile)
+ # print(cmd)
+ os.system(cmd)
+ data[i]['count'] += 1
+ data[i]['uptime'] = int(time.time())
+ mw.writeFile(getCfgFilePath(), json.dumps(data))
+ return mw.returnJson(True, '运行成功!')
+ return mw.returnJson(False, '指定Hook不存在!')
diff --git a/plugins/webssh/ico.png b/plugins/webssh/ico.png
new file mode 100755
index 000000000..5a6983475
Binary files /dev/null and b/plugins/webssh/ico.png differ
diff --git a/plugins/webssh/img/ico-cmd.png b/plugins/webssh/img/ico-cmd.png
new file mode 100644
index 000000000..c88796d6b
Binary files /dev/null and b/plugins/webssh/img/ico-cmd.png differ
diff --git a/plugins/webssh/index.html b/plugins/webssh/index.html
new file mode 100755
index 000000000..59bacbcf1
--- /dev/null
+++ b/plugins/webssh/index.html
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/plugins/webssh/index.py b/plugins/webssh/index.py
new file mode 100755
index 000000000..3793914bb
--- /dev/null
+++ b/plugins/webssh/index.py
@@ -0,0 +1,232 @@
+# coding: utf-8
+
+import time
+import random
+import os
+import json
+import re
+import sys
+
+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
+
+
+class App():
+
+ __cmd_file = 'cmd.json'
+ __cmd_path = ''
+ __host_dir = ''
+
+ def __init__(self):
+ self.__cmd_path = self.getServerDir() + '/' + self.__cmd_file
+
+ if not os.path.exists(self.__cmd_path):
+ mw.writeFile(self.__cmd_path, '[]')
+
+ self.__host_dir = self.getServerDir() + '/host'
+ if not os.path.exists(self.__host_dir):
+ mw.execShell('mkdir -p ' + self.__host_dir)
+
+ def getPluginName(self):
+ return 'webssh'
+
+ def getPluginDir(self):
+ return mw.getPluginDir() + '/' + self.getPluginName()
+
+ def getServerDir(self):
+ return mw.getServerDir() + '/' + self.getPluginName()
+
+ def getArgs(self):
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ if t.strip() == '':
+ tmp = []
+ else:
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ return tmp
+
+ def checkArgs(self, 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 status(self):
+ return 'start'
+
+ def saveCmd(self, t):
+ data_tmp = json.loads(mw.readFile(self.__cmd_path))
+ is_has = False
+ for x in range(0, len(data_tmp) - 1):
+ if data_tmp[x]['title'] == t['title']:
+ is_has = True
+ data_tmp[x]['cmd'] = t['cmd']
+ if not is_has:
+ data_tmp.append(t)
+ mw.writeFile(self.__cmd_path, json.dumps(data_tmp))
+
+ def add_cmd(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['title', 'cmd'])
+ if not check[0]:
+ return check[1]
+
+ title = args['title'].strip()
+ cmd = args['cmd']
+
+ t = {
+ 'title': title,
+ 'cmd': cmd
+ }
+ self.saveCmd(t)
+
+ return mw.returnJson(True, '添加成功!')
+
+ def del_cmd(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['title'])
+ if not check[0]:
+ return check[1]
+
+ title = args['title'].strip()
+ data_tmp = json.loads(mw.readFile(self.__cmd_path))
+ for x in range(0, len(data_tmp)):
+ if data_tmp[x]['title'] == title:
+ del(data_tmp[x])
+ mw.writeFile(self.__cmd_path, json.dumps(data_tmp))
+ return mw.returnJson(True, '删除成功')
+ return mw.returnJson(False, '删除无效')
+
+ def get_cmd_list(self):
+ alist = json.loads(mw.readFile(self.__cmd_path))
+ return mw.returnJson(True, 'ok', alist)
+
+ def getSshInfo(self, file):
+ rdata = mw.readFile(file)
+ destr = mw.deDoubleCrypt('mdserver-web', rdata)
+ return json.loads(destr)
+
+ def get_server_by_host_data(self, host):
+ info_file = self.__host_dir + '/' + host + '/info.json'
+ info_data = self.getSshInfo(info_file)
+ return info_data
+
+ def get_server_by_host(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['host'])
+ if not check[0]:
+ return check[1]
+
+ info_file = self.__host_dir + '/' + args['host'] + '/info.json'
+ if os.path.exists(info_file):
+ try:
+ info_tmp = self.getSshInfo(info_file)
+ host_info = {}
+ host_info['host'] = args['host']
+ host_info['port'] = info_tmp['port']
+ host_info['ps'] = info_tmp['ps']
+ host_info['type'] = info_tmp['type']
+ if 'password' in info_tmp:
+ host_info['password'] = info_tmp['password']
+ if 'pkey' in info_tmp:
+ host_info['pkey'] = info_tmp['pkey']
+ if 'pkey_passwd' in info_tmp:
+ host_info['pkey_passwd'] = info_tmp['pkey_passwd']
+ except Exception as e:
+ return mw.returnJson(False, '错误:' + str(e))
+
+ return mw.returnJson(True, 'ok!', host_info)
+ return mw.returnJson(False, '不存在此配置')
+
+ def get_server_list(self):
+ host_list = []
+ if os.path.exists(self.__host_dir):
+ for name in os.listdir(self.__host_dir):
+ info_file = self.__host_dir + '/' + name + '/info.json'
+ # print(info_file)
+ if not os.path.exists(info_file):
+ continue
+
+
+ host_info = {}
+ try:
+ info_tmp = self.getSshInfo(info_file)
+
+ host_info['host'] = name
+ host_info['port'] = info_tmp['port']
+ host_info['ps'] = info_tmp['ps']
+ # host_info['sort'] = int(info_tmp['sort'])
+ except Exception as e:
+ # print(e)
+ return mw.returnJson(False, str(e))
+
+ # if os.path.exists(info_file):
+ # os.remove(info_file)
+ # continue
+
+ host_list.append(host_info)
+
+ host_list = sorted(host_list, key=lambda x: x['host'], reverse=False)
+ return mw.returnJson(True, 'ok!', host_list)
+
+ def del_server(self):
+ args = self.getArgs()
+ check = self.checkArgs(args, ['host'])
+ if not check[0]:
+ return check[1]
+ host = args['host']
+ info_file = self.__host_dir + '/' + host
+ mw.execShell('rm -rf {}'.format(info_file))
+ return mw.returnJson(True, '删除成功!')
+
+ def add_server(self):
+ args = self.getArgs()
+ check = self.checkArgs(
+ args, ['host', 'port', 'type', 'username', 'ps'])
+ if not check[0]:
+ return check[1]
+
+ host = args['host']
+ info = {
+ 'port': args['port'],
+ 'username': args['username'],
+ 'ps': args['ps'],
+ 'type': args['type'],
+ }
+
+ if args['type'] == '0':
+ info['password'] = args['password']
+ else:
+ info['pkey'] = args['pkey']
+ info['pkey_passwd'] = args['pkey_passwd']
+
+ dst_host_dir = self.__host_dir + '/' + host
+ if not os.path.exists(dst_host_dir):
+ os.makedirs(dst_host_dir)
+
+ enstr = mw.enDoubleCrypt('mdserver-web', json.dumps(info))
+ mw.writeFile(dst_host_dir + '/info.json', enstr)
+ return mw.returnJson(True, '添加成功!')
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ classApp = App()
+ try:
+ data = eval("classApp." + func + "()")
+ print(data)
+ except Exception as e:
+ print(mw.getTracebackInfo())
diff --git a/plugins/webssh/info.json b/plugins/webssh/info.json
new file mode 100755
index 000000000..dea473672
--- /dev/null
+++ b/plugins/webssh/info.json
@@ -0,0 +1,36 @@
+{
+ "hook":[
+ {
+ "tag":"menu",
+ "menu": {
+ "title":"终端管理",
+ "name":"webssh",
+ "path":"menu/index.html",
+ "css_path":"menu/index.css",
+ "js_path":"js/webssh.js"
+ }
+ },
+ {
+ "tag":"global_static",
+ "global_static": {
+ "title":"终端管理",
+ "name":"webssh",
+ "css_path":"menu/ico.css"
+ }
+ }
+ ],
+ "sort":3,
+ "title":"SSH终端",
+ "tip":"lib",
+ "name":"webssh",
+ "type":"扩展",
+ "ps":"完整功能的SSH客户端,仅用于连接本服务器",
+ "versions":"2.0",
+ "shell":"install.sh",
+ "checks":"server/webssh",
+ "path": "server/webssh",
+ "author":"ssh",
+ "home":"",
+ "date":"2018-12-20",
+ "pid":"4"
+}
\ No newline at end of file
diff --git a/plugins/webssh/install.sh b/plugins/webssh/install.sh
new file mode 100755
index 000000000..31782e34c
--- /dev/null
+++ b/plugins/webssh/install.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+VERSION=$2
+
+Install_webssh()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/webssh
+ echo "${VERSION}" > $serverPath/webssh/version.pl
+ echo '安装完成'
+
+}
+
+Uninstall_webssh()
+{
+ rm -rf $serverPath/webssh
+ echo "卸载完成"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_webssh
+else
+ Uninstall_webssh
+fi
diff --git a/plugins/webssh/js/webssh.js b/plugins/webssh/js/webssh.js
new file mode 100755
index 000000000..0079d8f11
--- /dev/null
+++ b/plugins/webssh/js/webssh.js
@@ -0,0 +1,601 @@
+
+//全局
+var host_ssh_list = [];
+
+//刷新页面
+$(window).unload(function(){
+ for (i in host_ssh_list) {
+ host_ssh_list[i].close();
+ }
+ return false;
+});
+
+function appPost(method,args,callback){
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(toArrayObject(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+ $.post('/plugins/run', {name:'webssh', func:method, args:_args}, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function appAsyncPost(method,args){
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(toArrayObject(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+ return syncPost('/plugins/run', {name:'webssh', func:method, args:_args});
+}
+
+function appPostCallbak(method, args, callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'webssh';
+ req_data['func'] = method;
+ args['version'] = '1.0';
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+$(document).ready(function(){
+ var tag = $.getUrlParam('tag');
+ if(tag == 'webssh'){
+ webShell_Load();
+ }
+});
+
+function webShell_Resize(){
+ var cur_ssh = $('.term_item_tab .list .active');
+ if (cur_ssh.length > 0){
+ var data = $(cur_ssh).data();
+ var item = host_ssh_list[data.id];
+ item.term.fit();
+ item.resize({ cols: item.term.cols, rows: item.term.rows});
+ item.term.focus();
+ }
+}
+
+function webShell_Load(){
+ changeDivH();
+ $(window).resize(function(){
+ changeDivH();
+ webShell_Resize();
+ });
+
+ $('.term_content_tab .term-tool-button').click(function(){
+ if ($(this).hasClass('tool-show')){
+ $('#term_box_view').css('margin-right',300);
+ $('.term_tootls').css('display',"block");
+ $(this).removeClass('tool-show').addClass('tool-hide');
+ $(this).find('span').removeClass('glyphicon-menu-left').addClass('glyphicon-menu-right');
+ } else {
+ $('#term_box_view').css('margin-right',0);
+ $('.term_tootls').css('display',"none");
+ $(this).removeClass('tool-hide').addClass('tool-show');
+ $(this).find('span').removeClass('glyphicon-menu-right').addClass('glyphicon-menu-left');
+ }
+ webShell_Resize();
+ });
+
+
+
+ $('.full_exit_screen').click(function(ele){
+ if($(this).hasClass('glyphicon-resize-full')){
+ requestFullScreen($('#term_box_view')[0]);
+ $(this).removeClass('glyphicon-resize-full').addClass('glyphicon-resize-small');
+ } else{
+ exitFull();
+ $(this).removeClass('glyphicon-resize-small').addClass('glyphicon-resize-full');
+ }
+ });
+
+ $('.addServer').click(function(){
+ webShell_addServer();
+ });
+
+ $('.tootls_host_btn').click(function(){
+ webShell_addServer();
+ });
+
+ $('.tootls_commonly_btn').click(function(){
+ webShell_cmd();
+ });
+
+ // 切换服务器终端视图
+ $('.term_item_tab .list').on('click', 'span.item', function (ev) {
+ var index = $(this).index();
+ var data = $(this).data();
+ if ($(this).hasClass('addServer')) {
+ } else if ($(this).hasClass('tab_tootls')) {
+ } else {
+ $(this).addClass('active').siblings().removeClass('active');
+ $('.term_content_tab .term_item:eq(' + index + ')').addClass('active').siblings().removeClass('active');
+ webShell_Resize();
+ }
+ });
+
+ $('.term_item_tab').on('click', '.icon-trem-close', function () {
+ var id = $(this).parent().data('id');
+ webShell_removeTermView(id);
+ })
+
+ //服务器列表和常用命令
+ webShell_getHostList();
+
+ //服务器列表和命令切换
+ $('.term_tootls .tab-nav span').click(function(){
+ var list_type = $(this).attr('data-type');
+ if (!$(this).hasClass('on')){
+
+ $('.term_tootls .tab-nav span').removeClass('on');
+ $(this).addClass('on');
+
+ $('.term_tootls .tab-con .tab-block').removeClass('on')
+ if (list_type == 'host'){
+ $('.term_tootls .tab-con .tab-block').eq(0).addClass('on');
+ webShell_getHostList();
+ }
+
+ if (list_type == 'shell'){
+ $('.term_tootls .tab-con .tab-block').eq(1).addClass('on');
+ webShell_getCmdList();
+ }
+ }
+ });
+
+ webShell_Menu();
+}
+
+
+function changeDivH(){
+ var l = $(window).height();
+ $('#term_box_view').parent().css('height',l-80);
+ $('#xterm-viewport').css('height',l-80);
+
+ $('.tootls_host_list').css('display','block').css('height',l-192);
+ $('.tootls_commonly_list').css('display','block').css('height',l-192);
+}
+
+
+// 窗口状态改变
+function fullScreenChange(el, callback) {
+ el.addEventListener("fullscreenchange", function () {
+ callback && callback(document.fullscreen);
+ });
+
+ el.addEventListener("webkitfullscreenchange", function () {
+ callback && callback(document.webkitIsFullScreen);
+ });
+
+ el.addEventListener("mozfullscreenchange", function () {
+ callback && callback(document.mozFullScreen);
+ });
+
+ el.addEventListener("msfullscreenchange", function () {
+ callback && callback(document.msFullscreenElement);
+ });
+}
+
+// 全屏
+function requestFullScreen(element) {
+ var requestMethod = element.requestFullScreen || //W3C
+ element.webkitRequestFullScreen || //Chrome等
+ element.mozRequestFullScreen || //FireFox
+ element.msRequestFullScreen; //IE11
+ if (requestMethod) {
+ requestMethod.call(element);
+ }else if (typeof window.ActiveXObject !== "undefined") {//for Internet Explorer
+ var wscript = new ActiveXObject("WScript.Shell");
+ if (wscript !== null) {
+ wscript.SendKeys("{F11}");
+ }
+ }
+}
+
+//退出全屏
+function exitFull() {
+ var exitMethod = document.exitFullscreen || //W3C
+ document.mozCancelFullScreen || //Chrome等
+ document.webkitExitFullscreen || //FireFox
+ document.webkitExitFullscreen; //IE11
+ if (exitMethod) {
+ exitMethod.call(document);
+ }else if (typeof window.ActiveXObject !== "undefined") {//for Internet Explorer
+ var wscript = new ActiveXObject("WScript.Shell");
+ if (wscript !== null) {
+ wscript.SendKeys("{F11}");
+ }
+ }
+}
+
+
+function webShell_getCmdList(){
+ appPost('get_cmd_list', {}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var alist = rdata.data;
+
+ var tli = '';
+ for (var i = 0; i < alist.length; i++) {
+ tli+='\
+ \
+ '+alist[i]['title']+' \
+ \
+ \
+ \
+ \
+ ';
+ }
+
+ $('.tootls_commonly_list').html(tli);
+
+ $('.data-cmd-list .glyphicon-edit').click(function(){
+ var index = $(this).parent().parent().attr('data-index');
+ var t = alist[index];
+ webShell_cmd(t['title'],t['cmd']);
+ });
+
+ $('.data-cmd-list .glyphicon-trash').click(function(){
+ var index = $(this).parent().parent().attr('data-index');
+ var t = alist[index];
+ appPost('del_cmd', {title:t['title']}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ showMsg(rdata.msg, function(){
+ webShell_getCmdList();
+ },{ icon: rdata.status ? 1 : 2 });
+ });
+ });
+
+ $('.data-cmd-list .span_title').click(function(e){
+ copyText($(this).parent().attr('data-clipboard-text'));
+ e.preventDefault();
+ });
+ });
+}
+
+var n;
+function Terms_WebSocketIO_Create(ip, random){
+ n = new Terms_WebSocketIO('#'+random, { ssh_info: { host: ip, ps: "22", id: random } });
+ n.registerCloseCallBack(function(){
+ webShell_removeTermView(random);
+ layer.msg('已经关闭【'+ip+'】', { icon: 1, time: 3000 });
+ });
+
+ n.registerConnectedCallBack(function(){
+ var that = this;
+ $('.term_item_tab .item').each(function(){
+ var id = $(this).data('id');
+ if (id == that.id){
+ $(this).find('.icon').removeClass('icon-warning').addClass('icon-sucess');
+ }
+ });
+ webShell_Resize();
+ });
+
+ n.registerExitCallBack(function(){
+ var that = this;
+ $('.term_item_tab .item').each(function(){
+ var id = $(this).data('id');
+ if (id == that.id){
+ $(this).find('.icon').removeClass('icon-sucess').addClass('icon-warning');
+ }
+ });
+ });
+ return n;
+}
+
+
+function webShell_Menu(){
+ var random = 'localhost';
+ // host_ssh_list[random] = new Terms_WebSocketIO('#'+random, { ssh_info: { host: "38.6.224.67", ps: "22", id: random } });
+ host_ssh_list[random] = Terms_WebSocketIO_Create('127.0.0.1',random);
+}
+
+function webShell_openTermView(info) {
+ if (typeof info === "undefined") {
+ info = { host: '127.0.0.1', ps: '本地服务器' }
+ }
+ var random = getRandomString(9);
+ var tab_content = $('.term_content_tab');
+ var item_list = $('.term_item_tab .list');
+ tab_content.find('.term_item').removeClass('active').siblings().removeClass('active');
+ tab_content.append('
');
+ item_list.find('.item').removeClass('active');
+ if (info.ps == ''){
+ info.ps = info.host;
+ }
+ item_list.append('' + info.ps + '
');
+ host_ssh_list[random] = Terms_WebSocketIO_Create(info.host, random);
+}
+
+
+function webShell_removeTermView(id){
+ var item = $('[data-id="' + id + '"]');
+ var next = item.next();
+ var prev = item.prev();
+ $('#' + id).remove();
+ item.remove();
+ try {
+ host_ssh_list[id].close();
+ } catch (error) {
+ console.log(error);
+ }
+
+ delete host_ssh_list[id];
+ if (item.hasClass('active')) {
+ if (next.length > 0) {
+ next.click();
+ } else {
+ prev.click();
+ }
+ }
+}
+
+function webShell_getHostList(info){
+ appPost('get_server_list', {}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var alist = rdata.data;
+
+ var tli = '';
+ for (var i = 0; i < alist.length; i++) {
+ tli+='\
+ \
+ '+alist[i]['host']+' \
+ \
+ \
+ \
+ \
+ ';
+ }
+
+ $('.tootls_host_list').html(tli);
+
+ $('.data-host-list .glyphicon-edit').click(function(e){
+ var index = $(this).parent().parent().attr('data-index');
+ var info = alist[index];
+ webShell_addServer(info);
+ });
+
+ $('.data-host-list .glyphicon-trash').click(function(e){
+ var index = $(this).parent().parent().attr('data-index');
+ var t = alist[index];
+ appPost('del_server', {host:t['host']}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ showMsg(rdata.msg, function(){
+ webShell_getHostList();
+ },{ icon: rdata.status ? 1 : 2 });
+ });
+ });
+
+
+ $('.tootls_host_list .host').on('click', function (e) {
+ var _this = $(this).parent();
+ var index = $(_this).index();
+ var host = $(_this).data('host');
+ $(_this).find('i').addClass('active');
+ if ($('.item[data-host="' + host + '"]').length > 0) {
+ layer.msg('已经打开!', { icon: 0, time: 3000 });
+ } else {
+ webShell_openTermView(alist[index]);
+ }
+ e.preventDefault();
+ });
+ });
+}
+
+function webShell_addServer(info=[]){
+ layer.open({
+ type: 1,
+ title: '添加主机信息',
+ area: '500px',
+ btn:["确定","取消"],
+ content:'',
+ success:function(){
+ if (typeof(info['host'])!='undefined'){
+ $('input[name="host"]').val(info['host']);
+ appPost('get_server_by_host',{host:info['host']},function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var jdata = rdata.data;
+ if (jdata['type'] == 0){
+ $('input[name="password"]').val(jdata['password']);
+ } else{
+ $('textarea[name="pkey"]').val(jdata['pkey']);
+ // $('input[name="pkey_passwd"]').val(jdata['pkey_passwd']);
+ }
+ $('input[name="ps"]').val(jdata['ps']);
+ $('input[name="port"]').val(jdata['port']);
+ $('.auth_type_checkbox').each(function(){
+ if ($(this).data('ctype') == jdata['type']){
+ $(this).addClass('btn-success');
+ } else {
+ $(this).removeClass('btn-success');
+ }
+ });
+ if (jdata['type'] == 0){
+ $('.c_password_view').removeClass('show').addClass('show');
+ $('.c_pkey_view').removeClass('show').addClass('hide');
+ // $('.key_pwd_line').removeClass('show').addClass('hide');
+
+ }else{
+ $('.c_password_view').removeClass('show').addClass('hide');
+ $('.c_pkey_view').removeClass('show').addClass('show');
+ // $('.key_pwd_line').removeClass('show').addClass('show');
+ }
+ });
+ }
+
+ $('.auth_type_checkbox').click(function(){
+ var ctype = $(this).attr('data-ctype');
+ $('.auth_type_checkbox').removeClass('btn-success');
+ $(this).addClass('btn-success');
+
+ if (ctype == 0){
+ $('.c_password_view').removeClass('show').addClass('show');
+ $('.c_pkey_view').removeClass('show').addClass('hide');
+ // $('.key_pwd_line').removeClass('show').addClass('hide');
+
+ }else{
+ $('.c_password_view').removeClass('show').addClass('hide');
+ $('.c_pkey_view').removeClass('show').addClass('show');
+ // $('.key_pwd_line').removeClass('show').addClass('show');
+ }
+ });
+ },
+ yes:function(l,layeror){
+ var host = $('input[name="host"]').val();
+ var port = $('input[name="port"]').val();
+ var username = $('input[name="username"]').val();
+ var password = '';
+ var pkey = '';
+ var pkey_passwd = '';
+ var type = $('.btn-group .btn-success').attr('data-ctype');
+ if (type == "0"){
+ password = $('input[name="password"]').val();
+ } else{
+ pkey = $('textarea[name="pkey"]').val();
+ pkey_passwd = $('input[name="pkey_passwd"]').val();
+ }
+
+ var ps = $('input[name="ps"]').val();
+
+ var req_data = {
+ type:type,
+ host:host,
+ port:port,
+ username:username,
+ password:password,
+ pkey:pkey,
+ pkey_passwd:pkey_passwd,
+ ps:ps,
+ };
+
+ console.log(req_data);
+
+ appPost('add_server',req_data,function(rdata){
+ layer.close(l);
+ var rdata = $.parseJSON(rdata.data);
+ showMsg(rdata.msg, function(){
+ webShell_getHostList();
+ },{ icon: rdata.status ? 1 : 2 });
+ });
+ },
+ });
+}
+
+function webShell_cmd(title='', cmd=''){
+ layer.open({
+ type: 1,
+ title: '添加常用命令信息',
+ area: '500px',
+ btn:["确定","取消"],
+ content:'',
+ success:function(){
+ },
+ yes:function(l,layer_id){
+ var title = $('input[name="title"]').val();
+ var cmd = $('textarea[name="cmd"]').val();
+
+ appPost('add_cmd', {title:title,cmd:cmd}, function(rdata){
+ layer.close(l);
+ var rdata = $.parseJSON(rdata.data);
+ showMsg(rdata.msg, function(){
+ webShell_getCmdList();
+ },{ icon: rdata.status ? 1 : 2 });
+ });
+ return false;
+ }
+ });
+}
+
diff --git a/plugins/webssh/menu/ico.css b/plugins/webssh/menu/ico.css
new file mode 100644
index 000000000..287bb6d2b
--- /dev/null
+++ b/plugins/webssh/menu/ico.css
@@ -0,0 +1,10 @@
+
+/* menu start */
+.menu .menu_plugin_webssh {
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAMBJREFUeNpiYKAQMM6cOVMASBuQozk9Pf0AC5AuAOJ6cgwAWt7IhMQvRKIfAPEEYgxhQWL3o9EFpBgwAeifQhKdD7YI5oWPUMEEmAQR4COyATBwAIgdgIacB2IFYkxhQouWB0BsCA3E86QGIsxvDSBXgGIDSxr5ALTgAk4XADWAQt4fiB2BChcAaQVoGoHheLwuAGqagBz/UNsciQ4DEgE/zAUfQM4DOt+eRANAYTMRZADIrxfIdMUFSnMzA0CAAQD0hjqnYxWD2gAAAABJRU5ErkJggg==");
+}
+
+.menu .current .menu_plugin_webssh:hover {
+ background-image: url("data:image/gif;base64,R0lGODlhEAAQAMQXAJ6ipUFITySRPIiMkDFRQuXm5jJKQy1kQCGeOzBXQSl3PiWLPVhfZSxrQCpxP9na2yeEPTREQ8LExmRqb5OXm/Hx8TU9RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjMyQjYwMTU3OTA2QTExRUJCN0NFQUUwQjRDMzM4RDJDIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjMyQjYwMTU4OTA2QTExRUJCN0NFQUUwQjRDMzM4RDJDIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MzJCNjAxNTU5MDZBMTFFQkI3Q0VBRTBCNEMzMzhEMkMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MzJCNjAxNTY5MDZBMTFFQkI3Q0VBRTBCNEMzMzhEMkMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFMgAXACwAAAAAEAAQAAAFU6AljmRpBVOqrqtFVXAsy9Q7VMMDzHHN/5UawGQC1CiWg4IoOiINAoGB6BxBENSjyIE4oFKMprYhIFgYkvTQpWWOqm6RcVBI2+/2AuXL6k8CcSYhACH5BAUyABcALAcACQAFAAIAAAUIYJSMBLGcSggAIfkEBTIAFwAsBwAJAAUAAgAABQhgMI0MI51ACAAh+QQFMgAXACwHAAkABQACAAAFCGCUjASxnEoIADs=");
+}
+/* menu end */
\ No newline at end of file
diff --git a/plugins/webssh/menu/index.css b/plugins/webssh/menu/index.css
new file mode 100644
index 000000000..68b348085
--- /dev/null
+++ b/plugins/webssh/menu/index.css
@@ -0,0 +1,582 @@
+
+.xterm{
+ position: inherit;
+}
+
+.tab-nav {
+ border-bottom: #cacad9 1px solid;
+}
+
+.tab-nav span {
+ background-color: #f0f0f1;
+ height: 32px;
+ line-height: 32px;
+ padding: 0 15px;
+ border: #cacad9 1px solid;
+ color: #444;
+ display: inline-block;
+ margin: 0 -1px -1px 0;
+ cursor: pointer;
+ position: relative;
+}
+
+.tab-nav .on {
+ background: #fff;
+ border-bottom: #fff 1px solid;
+ color: #444
+}
+
+.tab-con {
+ padding: 10px;
+ position: relative;
+}
+
+.tab-con .tab-block {
+ display: none;
+ height: 100%;
+ width: 100%;
+}
+
+.tab-con .tab-block.on {
+ display: inline-block;
+}
+
+.tab-con ul.cmdlist {
+ list-style-type: decimal
+}
+ul.cmdlist {
+ list-style-type: decimal;
+ height: 505px;
+ overflow-y: auto;
+}
+
+.tab-con ul.cmdlist li {
+ position: relative;
+ list-style-type: decimal;
+ list-style-position: inside;
+ line-height: 40px;
+ border-bottom: #dbdbea 1px solid;
+ margin-top: 6px
+}
+
+.tab-con ul.cmdlist li .com-progress,
+.tab-con ul.cmdlist li .state,
+.opencmd {
+ float: right;
+ margin-left: 20px;
+ color: #535362
+}
+
+.tab-con ul.cmdlist li .line-progress {
+ position: absolute;
+ bottom: -1px;
+ left: 0;
+ height: 1px;
+ background-color: #20a53a
+}
+
+.tab-con ul.cmdlist li .cmd {
+ border: 0 none;
+ border-radius: 0;
+ display: block;
+ width: 570px;
+ height: 200px;
+ line-height: 22px;
+ padding: 0 10px;
+ background-color: #333;
+ color: #eee;
+ overflow: auto
+}
+
+.term_item_tab .list {
+ display: inline-block;
+ webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ /* display: flex; */
+ height: 38px;
+ overflow: hidden;
+ overflow-x: auto;
+}
+
+.term_item_tab .list>span {
+ height: 38px;
+ line-height: 38px;
+ text-align: left;
+ padding: 0 25px 0 22px;
+ display: inline-block;
+ vertical-align: middle;
+ border-right: 1px solid #e2e2e2;
+ cursor: pointer;
+ width: 150px;
+ position: relative;
+ flex-shrink: 0;
+}
+
+.icon-trem-close {
+ height: 18px;
+ width: 18px;
+ position: absolute;
+ top: 50%;
+ margin-top: -8.5px;
+ right: 8px;
+ display: none !important;
+}
+
+.icon-trem-close::after,
+.icon-trem-close::before {
+ content: '';
+ height: 14px;
+ width: 2px;
+ display: inline-block;
+ background: #fb0000;
+ z-index: 999;
+ position: absolute;
+ position: absolute;
+ top: 1.5px;
+ left: 8px;
+ transform: rotate(-45deg);
+}
+
+.icon-trem-close::after {
+ transform: rotate(45deg);
+}
+
+.icon-trem-close::before {
+ transform: rotate(-45deg);
+}
+
+/*term_item_tab*/
+.term_item_tab .list>span.active .icon-trem-close,
+.term_item_tab .list>span:hover .icon-trem-close {
+ display: inline-block !important;
+}
+
+.term_item_tab {
+ background: #f1f1f1;
+ font-size: 0;
+}
+
+.term_item_tab .list>span i {
+ transition: all 50ms;
+}
+
+.term_item_tab .list>span .icon {
+ height: 9px;
+ width: 9px;
+ border-radius: 5px;
+ display: inline-block;
+ background: #fff;
+ position: absolute;
+ left: 8px;
+ top: 50%;
+ margin-top: -4.5px;
+}
+
+.term_item_tab .list>span .icon.icon-sucess {
+ background-color: #10952a;
+}
+
+.term_item_tab .list>span .icon.icon-info {
+ background-color: #fc6d26;
+}
+
+.term_item_tab .list>span .icon.icon-warning {
+ background-color: #ff5d2c;
+}
+
+.term_item_tab .list>span:hover {
+ background-color: #dadada;
+}
+
+.term_item_tab .list>span.active {
+ background-color: #424242;
+}
+
+.term_item_tab .list span.active span {
+ color: #fff;
+}
+
+.term_item_tab .list .content {
+ position: relative;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.term_item_tab .item .glyphicon {
+ vertical-align: top;
+ position: absolute;
+ right: 12px;
+ font-size: 15px;
+ height: 38px;
+ line-height: 38px;
+ color: #ff7070 !important;
+ display: none;
+ transition: all 500ms;
+}
+
+.term_item_tab .item .glyphicon:hover {
+ color: red;
+}
+
+.term_item_tab .list span span {
+ vertical-align: middle;
+ font-size: 13px;
+ font-weight: 500;
+ color: #666;
+ display: inline-block;
+ line-height: 37px;
+}
+
+.term_item_tab .glyphicon {
+ font-size: 15px;
+ margin-left: 8px;
+ vertical-align: middle;
+ transition: all 500ms;
+ cursor: pointer;
+ display: inline-block;
+ color: #ea7575;
+}
+
+.term_item_tab span.glyphicon {
+ color: #888;
+}
+
+.term_item_tab .glyphicon:hover {
+ color: red;
+}
+
+.term_item_tab .tab_tootls {
+ padding: 0;
+ float: right;
+ /* padding-right: 15px; */
+ /* display: inline-block; */
+ /* display: none; */
+}
+
+
+/* .term_item_tab .tab_tootls span{
+margin-left: 0;
+font-size: 12px;
+display: block;
+height: 19px;
+color: #bbb;
+/* vertical-align: bottom;
+} */
+
+.term_item_tab .tab_tootls .glyphicon-resize-full,
+.term_item_tab .tab_tootls .glyphicon-resize-small {
+ height: 38px;
+ line-height: 38px;
+ width: 40px;
+ text-align: center;
+ margin: 0
+}
+
+.term_item_tab .addServer {
+ display: inline-block;
+ width: 40px;
+ text-align: center;
+ padding: 0;
+ height: 38px;
+ line-height: 38px;
+ vertical-align: top;
+}
+
+.term_item_tab .addServer span {
+ margin: 0;
+ color: #20a53a;
+}
+
+.term_item_tab>span:hover {
+ background-color: #ececec;
+ cursor: pointer;
+}
+
+.term_item_tab .addServer span:hover {
+ color: #4c4c4c;
+}
+
+
+
+.term_content_tab {
+ background-color: rgb(51, 51, 51);
+ position: absolute;
+ top: 38px;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ padding: 10px 10px 10px 10px;
+ overflow: hidden;
+}
+
+.term_content_tab .term_item {
+ height: 100%;
+ width: 100%;
+ display: none;
+}
+
+.term_content_tab .term_item.active {
+ display: inline-block;
+}
+
+.term-tool-button {
+ position: absolute;
+ right: -7px;
+ top: 50%;
+ width: 28px;
+ height: 60px;
+ background-color: #565656;
+ border-top-left-radius: 30px;
+ border-bottom-left-radius: 30px;
+ cursor: pointer;
+ z-index: 999;
+ margin-top: -30px;
+}
+
+.term-tool-button span {
+ font-size: 12px;
+ position: relative;
+ left: 15%;
+ top: 35%;
+ font-size: 18px;
+ color: #FFFFFF;
+ cursor: pointer;
+}
+
+.term-tool-button:hover {
+ background-color: #D8D8D8;
+}
+
+.term_box {
+ height: 100%;
+ border-radius: 3px;
+ margin-right: 300px;
+ position: relative;
+}
+
+.term_tootls {
+ width: 290px;
+ position: absolute;
+ right: 15px;
+ top: 15px;
+ /* border: 1px solid #ececec; */
+}
+
+.term_tootls .tootls_tab {
+ display: inline-block;
+ /* border: 1px solid #20a53a; */
+ width: 100%;
+ position: relative;
+}
+
+.term_tootls .tootls_tab a {
+ display: inline-block;
+ width: 38px;
+ text-align: center;
+ height: 38px;
+ line-height: 38px;
+ position: absolute;
+ right: 0;
+ color: #ececec;
+ background-color: #c7c7c71a;
+}
+
+.term_tootls .tab-con {
+/* position: absolute;*/
+ top: 10px;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ padding: 0;
+ overflow: hidden;
+}
+
+.term_tootls .tootls_tab a:hover {
+ background-color: #8282824d;
+}
+
+.term_tootls .tootls_tab i {
+ font-style: normal;
+ font-size: 12px;
+ color: #ececec;
+ margin-left: 5px;
+}
+
+.main-content .safe {
+ position: relative;
+}
+
+.term_content_tab .xterm .xterm-viewport{
+ /* On OS X this is required in order for the scroll bar to appear fully opaque */
+ background-color: #000;
+ overflow-y: auto;
+ cursor: default;
+ position: absolute;
+ right: 0;
+ left: 0;
+ top: 0;
+ bottom: 0;
+}
+
+.term_content_tab .xterm .composition-view.active {
+ display: block;
+}
+
+.term_content_tab .xterm .xterm-viewport::-webkit-scrollbar {
+ width: 8px;
+ height: 5px;
+ border-radius: 4px;
+}
+
+.term_content_tab .xterm .xterm-viewport::-webkit-scrollbar-thumb {
+ border-radius: 0;
+ box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
+ background: #666;
+ border-radius: 4px;
+ transition: all 1s;
+}
+
+.term_content_tab .xterm .xterm-viewport:hover::-webkit-scrollbar-thumb {
+ background: #aaa;
+}
+
+.term_content_tab .xterm .xterm-viewport::-webkit-scrollbar-track {
+ box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
+ border-radius: 0;
+ background: #222;
+ border-radius: 4px;
+ transition: all 1s;
+}
+
+.term_content_tab .xterm .xterm-viewport:hover::-webkit-scrollbar-track {
+ background-color: #444;
+}
+
+.tootls_host_list,
+.tootls_commonly_list {
+ list-style: none;
+ border-top: none;
+ border: 1px solid #ececec;
+ overflow-y: auto;
+}
+
+.tootls_host_list,
+.tootls_commonly_list {
+ overflow: auto;
+ overflow-x: hidden;
+ font-size: 12px;
+ bottom: 0;
+ right: 0;
+ left: 0;
+ margin-top: 10px;
+}
+
+.tootls_commonly_list li,
+.tootls_host_list li {
+ display: inline-block;
+ height: 38px;
+ line-height: 38px;
+ color: #444;
+ border-bottom: 1px solid #ececec;
+ font-size: 13px;
+ cursor: pointer;
+ position: relative;
+ width: 100%;
+}
+
+.tootls_commonly_list li {
+ padding-left: 15px;
+}
+
+.tootls_commonly_list li:hover,
+.tootls_host_list li:hover {
+ background-color: #eee;
+}
+
+.tootls_commonly_list li:hover .tootls,
+.tootls_host_list li:hover .tootls {
+ display: inline-block;
+}
+
+.tootls_host_list li i {
+ display: inline-block;
+ height: 100%;
+ width: 38px;
+ background-size: 16px;
+ background: url('/plugins/file?name=webssh&f=img/ico-cmd.png') no-repeat center center;
+}
+
+.tootls_commonly_list li>span,
+.tootls_host_list li>span {
+ vertical-align: top;
+ display: inline-block;
+}
+
+.tootls_commonly_list li:hover>span:nth-child(2),
+.tootls_host_list li:hover>span:nth-child(2) {
+ width: 120px;
+}
+
+.tootls_commonly_list li>span:nth-child(2),
+.tootls_host_list li>span:nth-child(2) {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ font-size: 12px;
+ max-width: 190px;
+}
+
+
+/* .tootls_host_list li>span:nth-child(2){
+margin-left: 35px;
+} */
+
+.tootls_commonly_list li>span:nth-child(2) {
+ max-width: 190px;
+}
+
+.tootls_commonly_list li span:nth-child(3),
+.tootls_host_list li span:nth-child(3) {
+ padding-left: 10px;
+}
+
+.tootls_commonly_list li .tootls,
+.tootls_host_list li .tootls {
+ width: 70px;
+ text-align: right;
+ padding-right: 10px;
+ display: none;
+ position: absolute;
+ right: 5px;
+}
+
+.tootls_commonly_list li .tootls {
+ width: 90px;
+}
+
+.tootls_commonly_list li .tootls span,
+.tootls_host_list li .tootls span {
+ width: 22px;
+ height: 22px;
+ line-height: 22px;
+ text-align: center;
+}
+
+.tootls_commonly_list li .tootls span:nth-child(1),
+.tootls_host_list li .tootls span:nth-child(1) {
+ color: #888;
+ font-size: 14px;
+}
+
+.tootls_commonly_list li .tootls span:nth-child(2),
+.tootls_host_list li .tootls span:nth-child(2) {
+ color: #ea7575;
+}
+
+.tootls_commonly_list li .tootls span:nth-child(2):hover,
+.tootls_host_list li .tootls span:nth-child(2):hover {
+ color: red;
+}
diff --git a/plugins/webssh/menu/index.html b/plugins/webssh/menu/index.html
new file mode 100755
index 000000000..2ef622f5e
--- /dev/null
+++ b/plugins/webssh/menu/index.html
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+ 服务器列表
+ 常用命令
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/webstats/class/LuaMaker.py b/plugins/webstats/class/LuaMaker.py
new file mode 100644
index 000000000..f9b0a8ade
--- /dev/null
+++ b/plugins/webstats/class/LuaMaker.py
@@ -0,0 +1,56 @@
+import sys
+import os
+
+
+class LuaMaker:
+ """
+ lua 处理器
+ """
+ @staticmethod
+ def makeLuaTable(table):
+ """
+ table 转换为 lua table 字符串
+ """
+ _tableMask = {}
+ _keyMask = {}
+
+ def analysisTable(_table, _indent, _parent):
+ if isinstance(_table, tuple):
+ _table = list(_table)
+ if isinstance(_table, list):
+ _table = dict(zip(range(1, len(_table) + 1), _table))
+ if isinstance(_table, dict):
+ _tableMask[id(_table)] = _parent
+ cell = []
+ thisIndent = _indent + " "
+ for k in _table:
+ if sys.version_info[0] == 2:
+ if type(k) not in [int, float, bool, list, dict, tuple]:
+ k = k.encode()
+
+ if not (isinstance(k, str) or isinstance(k, int) or isinstance(k, float)):
+ return
+ key = isinstance(
+ k, int) and "[" + str(k) + "]" or "[\"" + str(k) + "\"]"
+ if _parent + key in _keyMask.keys():
+ return
+ _keyMask[_parent + key] = True
+ var = None
+ v = _table[k]
+ if sys.version_info[0] == 2:
+ if type(v) not in [int, float, bool, list, dict, tuple]:
+ v = v.encode()
+ if isinstance(v, str):
+ var = "\"" + v + "\""
+ elif isinstance(v, bool):
+ var = v and "true" or "false"
+ elif isinstance(v, int) or isinstance(v, float):
+ var = str(v)
+ else:
+ var = analysisTable(v, thisIndent, _parent + key)
+ cell.append(thisIndent + key + " = " + str(var))
+ lineJoin = ",\n"
+ return "{\n" + lineJoin.join(cell) + "\n" + _indent + "}"
+ else:
+ pass
+ return analysisTable(table, "", "root")
diff --git a/plugins/webstats/conf/config.json b/plugins/webstats/conf/config.json
new file mode 100644
index 000000000..080561990
--- /dev/null
+++ b/plugins/webstats/conf/config.json
@@ -0,0 +1,54 @@
+{
+ "global": {
+ "monitor": true,
+ "save_day": 1,
+ "autorefresh": false,
+ "refresh_interval": 3,
+ "cdn": true,
+ "cdn_headers": [
+ "x-forwarded-for",
+ "x-real-ip",
+ "x-forwarded",
+ "forwarded-for",
+ "forwarded",
+ "true-client-ip",
+ "client-ip",
+ "ali-cdn-real-ip",
+ "cdn-src-ip",
+ "cdn-real-ip",
+ "cf-connecting-ip",
+ "x-cluster-client-ip",
+ "wl-proxy-client-ip",
+ "proxy-client-ip",
+ "true-client-ip"
+ ],
+ "exclude_extension": [
+ "png",
+ "gif",
+ "jpg",
+ "css",
+ "svg",
+ "js",
+ "ico",
+ "woff2"
+ ],
+ "exclude_status": [
+ 301,
+ 302,
+ 303
+ ],
+ "exclude_ip":[
+ "192.168.1.1"
+ ],
+ "exclude_url":[],
+ "ip_top_num": 10,
+ "uri_top_num": 10,
+ "statistics_machine_access": true,
+ "statistics_ip": true,
+ "statistics_uri": true,
+ "record_post_args": false,
+ "record_get_403_args": false,
+ "push_report": false,
+ "data_dir": "/www/server/webstats/logs"
+ }
+}
\ No newline at end of file
diff --git a/plugins/webstats/conf/init.sql b/plugins/webstats/conf/init.sql
new file mode 100644
index 000000000..86051d8f3
--- /dev/null
+++ b/plugins/webstats/conf/init.sql
@@ -0,0 +1,271 @@
+PRAGMA synchronous = 0;
+PRAGMA page_size = 4096;
+PRAGMA journal_mode = wal;
+PRAGMA journal_size_limit = 1073741824;
+
+
+CREATE TABLE IF NOT EXISTS `web_logs` (
+ `time` INTEGER,
+ `ip` TEXT,
+ `domain` TEXT,
+ `server_name` TEXT,
+ `method` TEXT,
+ `status_code` INTEGER,
+ `uri` TEXT,
+ `body_length` INTEGER,
+ `referer` TEXT DEFAULT "",
+ `user_agent` TEXT,
+ `is_spider` INTEGER DEFAULT 0,
+ `protocol` TEXT,
+ `request_time` INTEGER,
+ `request_headers` TEXT DEFAULT "",
+ `ip_list` TEXT DEFAULT "",
+ `client_port` INTEGER DEFAULT -1
+);
+
+CREATE INDEX time_idx ON web_logs(`time`);
+CREATE INDEX uri_idx ON web_logs (`uri`);
+CREATE INDEX ip_idx ON web_logs (`ip`);
+CREATE INDEX referer_idx ON web_logs (`referer`);
+CREATE INDEX method_idx ON web_logs (`method`);
+CREATE INDEX status_code_idx ON web_logs (`status_code`);
+CREATE INDEX request_time_idx ON web_logs (`request_time`);
+CREATE INDEX is_spider_idx ON web_logs (`is_spider`);
+CREATE INDEX body_length_idx ON web_logs (`body_length`);
+CREATE INDEX all_union_idx ON web_logs(`status_code`, `time`, `ip`, `domain`, `server_name`, `method`, `is_spider`, `protocol`, `request_headers`, `ip_list`, `client_port`, `body_length`, `user_agent`, `referer`, `request_time`, `uri`);
+
+
+CREATE TABLE IF NOT EXISTS `client_stat`(
+ `time` INTEGER PRIMARY KEY,
+ `weixin` INTEGER DEFAULT 0,
+ `android` INTEGER DEFAULT 0,
+ `iphone` INTEGER DEFAULT 0,
+ `mac` INTEGER DEFAULT 0,
+ `windows` INTEGER DEFAULT 0,
+ `linux` INTEGER DEFAULT 0,
+ `edeg` INTEGER DEFAULT 0,
+ `firefox` INTEGER DEFAULT 0,
+ `msie` INTEGER DEFAULT 0,
+ `metasr` INTEGER DEFAULT 0,
+ `qh360` INTEGER DEFAULT 0,
+ `theworld` INTEGER DEFAULT 0,
+ `tt` INTEGER DEFAULT 0,
+ `maxthon` INTEGER DEFAULT 0,
+ `opera` INTEGER DEFAULT 0,
+ `qq` INTEGER DEFAULT 0,
+ `uc` INTEGER DEFAULT 0,
+ `pc2345` INTEGER DEFAULT 0,
+ `safari` INTEGER DEFAULT 0,
+ `chrome` INTEGER DEFAULT 0,
+ `machine` INTEGER DEFAULT 0,
+ `mobile` INTEGER DEFAULT 0,
+ `other` INTEGER DEFAULT 0
+);
+
+CREATE TABLE `request_stat`(
+ `time` INTEGER PRIMARY KEY,
+ `req` INTEGER DEFAULT 0,
+ `pv` INTEGER DEFAULT 0,
+ `uv` INTEGER DEFAULT 0,
+ `ip` INTEGER DEFAULT 0,
+ `length` INTEGER DEFAULT 0,
+ `spider` INTEGER DEFAULT 0,
+ `status_500` INTEGER DEFAULT 0,
+ `status_501` INTEGER DEFAULT 0,
+ `status_502` INTEGER DEFAULT 0,
+ `status_503` INTEGER DEFAULT 0,
+ `status_504` INTEGER DEFAULT 0,
+ `status_505` INTEGER DEFAULT 0,
+ `status_506` INTEGER DEFAULT 0,
+ `status_507` INTEGER DEFAULT 0,
+ `status_509` INTEGER DEFAULT 0,
+ `status_510` INTEGER DEFAULT 0,
+ `status_400` INTEGER DEFAULT 0,
+ `status_401` INTEGER DEFAULT 0,
+ `status_402` INTEGER DEFAULT 0,
+ `status_403` INTEGER DEFAULT 0,
+ `status_404` INTEGER DEFAULT 0,
+ `status_499` INTEGER DEFAULT 0,
+ `http_get` INTEGER DEFAULT 0,
+ `http_post` INTEGER DEFAULT 0,
+ `http_put` INTEGER DEFAULT 0,
+ `http_patch` INTEGER DEFAULT 0,
+ `http_delete` INTEGER DEFAULT 0
+);
+
+CREATE TABLE `spider_stat`(
+ `time` INTEGER PRIMARY KEY,
+ `bytes` INTEGER DEFAULT 0,
+ `bing` INTEGER DEFAULT 0,
+ `soso` INTEGER DEFAULT 0,
+ `yahoo` INTEGER DEFAULT 0,
+ `sogou` INTEGER DEFAULT 0,
+ `google` INTEGER DEFAULT 0,
+ `baidu` INTEGER DEFAULT 0,
+ `qh360` INTEGER DEFAULT 0,
+ `youdao` INTEGER DEFAULT 0,
+ `yandex` INTEGER DEFAULT 0,
+ `dnspod` INTEGER DEFAULT 0,
+ `yisou` INTEGER DEFAULT 0,
+ `mpcrawler` INTEGER DEFAULT 0,
+ `duckduckgo` INTEGER DEFAULT 0,
+ `bytes_flow` INTEGER DEFAULT 0,
+ `bing_flow` INTEGER DEFAULT 0,
+ `soso_flow` INTEGER DEFAULT 0,
+ `yahoo_flow` INTEGER DEFAULT 0,
+ `sogou_flow` INTEGER DEFAULT 0,
+ `google_flow` INTEGER DEFAULT 0,
+ `baidu_flow` INTEGER DEFAULT 0,
+ `qh360_flow` INTEGER DEFAULT 0,
+ `youdao_flow` INTEGER DEFAULT 0,
+ `yandex_flow` INTEGER DEFAULT 0,
+ `dnspod_flow` INTEGER DEFAULT 0,
+ `yisou_flow` INTEGER DEFAULT 0,
+ `other_flow` INTEGER DEFAULT 0,
+ `mpcrawler_flow` INTEGER DEFAULT 0,
+ `duckduckgo_flow` INTEGER DEFAULT 0,
+ `other` INTEGER DEFAULT 0
+);
+
+CREATE TABLE IF NOT EXISTS `uri_stat` (
+ `uri_md5` CHAR(32) PRIMARY KEY,
+ `uri` TEXT,
+ `day1` INTEGER DEFAULT 0,
+ `day2` INTEGER DEFAULT 0,
+ `day3` INTEGER DEFAULT 0,
+ `day4` INTEGER DEFAULT 0,
+ `day5` INTEGER DEFAULT 0,
+ `day6` INTEGER DEFAULT 0,
+ `day7` INTEGER DEFAULT 0,
+ `day8` INTEGER DEFAULT 0,
+ `day9` INTEGER DEFAULT 0,
+ `day10` INTEGER DEFAULT 0,
+ `day11` INTEGER DEFAULT 0,
+ `day12` INTEGER DEFAULT 0,
+ `day13` INTEGER DEFAULT 0,
+ `day14` INTEGER DEFAULT 0,
+ `day15` INTEGER DEFAULT 0,
+ `day16` INTEGER DEFAULT 0,
+ `day17` INTEGER DEFAULT 0,
+ `day18` INTEGER DEFAULT 0,
+ `day19` INTEGER DEFAULT 0,
+ `day20` INTEGER DEFAULT 0,
+ `day21` INTEGER DEFAULT 0,
+ `day22` INTEGER DEFAULT 0,
+ `day23` INTEGER DEFAULT 0,
+ `day24` INTEGER DEFAULT 0,
+ `day25` INTEGER DEFAULT 0,
+ `day26` INTEGER DEFAULT 0,
+ `day27` INTEGER DEFAULT 0,
+ `day28` INTEGER DEFAULT 0,
+ `day29` INTEGER DEFAULT 0,
+ `day30` INTEGER DEFAULT 0,
+ `day31` INTEGER DEFAULT 0
+);
+
+ALTER TABLE `uri_stat` ADD COLUMN `flow1` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow2` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow3` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow4` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow5` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow6` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow7` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow8` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow9` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow10` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow11` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow12` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow13` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow14` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow15` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow16` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow17` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow18` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow19` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow20` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow21` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow22` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow23` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow24` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow25` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow26` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow27` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow28` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow29` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow30` INTEGER DEFAULT 0;
+ALTER TABLE `uri_stat` ADD COLUMN `flow31` INTEGER DEFAULT 0;
+
+CREATE TABLE IF NOT EXISTS `ip_stat` (
+ `ip` CHAR(15) PRIMARY KEY,
+ `area` CHAR(8) DEFAULT "",
+ `day1` INTEGER DEFAULT 0,
+ `day2` INTEGER DEFAULT 0,
+ `day3` INTEGER DEFAULT 0,
+ `day4` INTEGER DEFAULT 0,
+ `day5` INTEGER DEFAULT 0,
+ `day6` INTEGER DEFAULT 0,
+ `day7` INTEGER DEFAULT 0,
+ `day8` INTEGER DEFAULT 0,
+ `day9` INTEGER DEFAULT 0,
+ `day10` INTEGER DEFAULT 0,
+ `day11` INTEGER DEFAULT 0,
+ `day12` INTEGER DEFAULT 0,
+ `day13` INTEGER DEFAULT 0,
+ `day14` INTEGER DEFAULT 0,
+ `day15` INTEGER DEFAULT 0,
+ `day16` INTEGER DEFAULT 0,
+ `day17` INTEGER DEFAULT 0,
+ `day18` INTEGER DEFAULT 0,
+ `day19` INTEGER DEFAULT 0,
+ `day20` INTEGER DEFAULT 0,
+ `day21` INTEGER DEFAULT 0,
+ `day22` INTEGER DEFAULT 0,
+ `day23` INTEGER DEFAULT 0,
+ `day24` INTEGER DEFAULT 0,
+ `day25` INTEGER DEFAULT 0,
+ `day26` INTEGER DEFAULT 0,
+ `day27` INTEGER DEFAULT 0,
+ `day28` INTEGER DEFAULT 0,
+ `day29` INTEGER DEFAULT 0,
+ `day30` INTEGER DEFAULT 0,
+ `day31` INTEGER DEFAULT 0
+);
+
+ALTER TABLE `ip_stat` ADD COLUMN `flow1` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow2` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow3` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow4` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow5` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow6` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow7` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow8` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow9` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow10` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow11` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow12` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow13` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow14` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow15` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow16` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow17` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow18` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow19` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow20` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow21` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow22` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow23` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow24` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow25` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow26` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow27` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow28` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow29` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow30` INTEGER DEFAULT 0;
+ALTER TABLE `ip_stat` ADD COLUMN `flow31` INTEGER DEFAULT 0;
+
+
+CREATE TABLE `referer_stat`(
+ `time` INTEGER,
+ `domain` TEXT,
+ `count` INTEGER DEFAULT 0
+);
diff --git a/plugins/webstats/conf/webstats.conf b/plugins/webstats/conf/webstats.conf
new file mode 100644
index 000000000..5b3277ea2
--- /dev/null
+++ b/plugins/webstats/conf/webstats.conf
@@ -0,0 +1,3 @@
+lua_shared_dict mw_total 100m;
+lua_need_request_body off;
+include {$SERVER_APP}/lua/webstats_log.lua;
\ No newline at end of file
diff --git a/plugins/webstats/ico.png b/plugins/webstats/ico.png
new file mode 100644
index 000000000..33f6c09f0
Binary files /dev/null and b/plugins/webstats/ico.png differ
diff --git a/plugins/webstats/index.html b/plugins/webstats/index.html
new file mode 100755
index 000000000..028f37282
--- /dev/null
+++ b/plugins/webstats/index.html
@@ -0,0 +1,221 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/webstats/index.py b/plugins/webstats/index.py
new file mode 100755
index 000000000..4b56d657d
--- /dev/null
+++ b/plugins/webstats/index.py
@@ -0,0 +1,1452 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import json
+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 'webstats'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+sys.path.append(getPluginDir() + "/class")
+from LuaMaker import LuaMaker
+
+
+def listToLuaFile(path, lists):
+ content = LuaMaker.makeLuaTable(lists)
+ content = "return " + content
+ mw.writeFile(path, content)
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getConf():
+ conf = getServerDir() + "/lua/config.json"
+ return conf
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+
+ 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 luaConf():
+ return mw.getServerDir() + '/web_conf/nginx/vhost/webstats.conf'
+
+
+def status():
+ path = luaConf()
+ if not os.path.exists(path):
+ return 'stop'
+ return 'start'
+
+
+def loadLuaFile(name):
+ lua_dir = getServerDir() + "/lua"
+ lua_dst = lua_dir + "/" + name
+
+ if not os.path.exists(lua_dst):
+ lua_tpl = getPluginDir() + '/lua/' + name
+ content = mw.readFile(lua_tpl)
+ content = content.replace('{$SERVER_APP}', getServerDir())
+ content = content.replace('{$ROOT_PATH}', mw.getServerDir())
+ mw.writeFile(lua_dst, content)
+
+
+def loadLuaFileReload(name):
+ lua_dir = getServerDir() + "/lua"
+ lua_dst = lua_dir + "/" + name
+
+ lua_tpl = getPluginDir() + '/lua/' + name
+ content = mw.readFile(lua_tpl)
+ content = content.replace('{$SERVER_APP}', getServerDir())
+ content = content.replace('{$ROOT_PATH}', mw.getServerDir())
+ mw.writeFile(lua_dst, content)
+
+
+def loadConfigFile():
+ lua_dir = getServerDir() + "/lua"
+ conf_tpl = getPluginDir() + "/conf/config.json"
+
+ content = mw.readFile(conf_tpl)
+ content = json.loads(content)
+
+ dst_conf_json = getServerDir() + "/lua/config.json"
+ if not os.path.exists(dst_conf_json):
+ mw.writeFile(dst_conf_json, json.dumps(content))
+
+ dst_conf_lua = getServerDir() + "/lua/webstats_config.lua"
+ if not os.path.exists(dst_conf_lua):
+ listToLuaFile(dst_conf_lua, content)
+
+
+# def loadConfigFileReload():
+# -- 配置生活或可使用
+# lua_dir = getServerDir() + "/lua"
+# conf_tpl = getPluginDir() + "/conf/config.json"
+
+# content = mw.readFile(conf_tpl)
+# content = json.loads(content)
+
+# dst_conf_json = getServerDir() + "/lua/config.json"
+# mw.writeFile(dst_conf_json, json.dumps(content))
+
+# dst_conf_lua = getServerDir() + "/lua/webstats_config.lua"
+# listToLuaFile(dst_conf_lua, content)
+
+
+def loadLuaSiteFile():
+ lua_dir = getServerDir() + "/lua"
+
+ content = makeSiteConfig()
+ for index in range(len(content)):
+ pSqliteDb('web_log', content[index]['name'])
+
+ lua_site_json = lua_dir + "/sites.json"
+ mw.writeFile(lua_site_json, json.dumps(content))
+
+ # 设置默认列表
+ default_json = lua_dir + "/default.json"
+ ddata = {}
+ dlist = []
+ for i in content:
+ dlist.append(i["name"])
+
+ dlist.append('unset')
+ ddata["list"] = dlist
+ if len(ddata["list"]) < 1:
+ ddata["default"] = "unset"
+ else:
+ ddata["default"] = dlist[0]
+
+ mw.writeFile(default_json, json.dumps(ddata))
+
+ lua_site = lua_dir + "/webstats_sites.lua"
+
+ tmp = {
+ "name": "unset",
+ "domains": [],
+ }
+ content.append(tmp)
+ listToLuaFile(lua_site, content)
+
+
+def loadDebugLogFile():
+ debug_log = getServerDir() + "/debug.log"
+ lua_dir = getServerDir() + "/lua"
+ mw.writeFile(debug_log, '')
+
+
+def pSqliteDb(dbname='web_logs', site_name='unset', name="logs"):
+
+ db_dir = getServerDir() + '/logs/' + site_name
+ if not os.path.exists(db_dir):
+ mw.execShell('mkdir -p ' + db_dir)
+
+ file = db_dir + '/' + name + '.db'
+ if not os.path.exists(file):
+ conn = mw.M(dbname).dbPos(db_dir, name)
+ sql = mw.readFile(getPluginDir() + '/conf/init.sql')
+ sql_list = sql.split(';')
+ for index in range(len(sql_list)):
+ conn.execute(sql_list[index])
+ else:
+ conn = mw.M(dbname).dbPos(db_dir, name)
+
+ conn.execute("PRAGMA synchronous = 0")
+ conn.execute("PRAGMA cache_size = 8000")
+ conn.execute("PRAGMA page_size = 32768")
+ conn.execute("PRAGMA journal_mode = wal")
+ conn.execute("PRAGMA journal_size_limit = 1073741824")
+ return conn
+
+
+def makeSiteConfig():
+ siteM = mw.M('sites')
+ domainM = mw.M('domain')
+ slist = siteM.field('id,name').where(
+ 'status=?', (1,)).order('id desc').select()
+
+ data = []
+ for s in slist:
+ tmp = {}
+ tmp['name'] = s['name']
+
+ dlist = domainM.field('id,name').where(
+ 'pid=?', (s['id'],)).order('id desc').select()
+
+ _t = []
+ for d in dlist:
+ _t.append(d['name'])
+
+ tmp['domains'] = _t
+ data.append(tmp)
+
+ return data
+
+
+def initDreplace():
+
+ service_path = getServerDir()
+
+ pSqliteDb()
+
+ path = luaConf()
+ path_tpl = getPluginDir() + '/conf/webstats.conf'
+ if not os.path.exists(path):
+ content = mw.readFile(path_tpl)
+ content = content.replace('{$SERVER_APP}', service_path)
+ content = content.replace('{$ROOT_PATH}', mw.getServerDir())
+ mw.writeFile(path, content)
+
+ # 已经安装的
+ al_config = getServerDir() + "/lua/config.json"
+ if os.path.exists(al_config):
+ tmp = json.loads(mw.readFile(al_config))
+ if tmp['global']['record_post_args'] or tmp['global']['record_get_403_args']:
+ openLuaNeedRequestBody()
+ else:
+ closeLuaNeedRequestBody()
+
+ lua_dir = getServerDir() + "/lua"
+ if not os.path.exists(lua_dir):
+ mw.execShell('mkdir -p ' + lua_dir)
+
+ log_path = getServerDir() + "/logs"
+ if not os.path.exists(log_path):
+ mw.execShell('mkdir -p ' + log_path)
+
+ file_list = [
+ 'webstats_common.lua',
+ 'webstats_log.lua',
+ ]
+
+ for fl in file_list:
+ loadLuaFile(fl)
+
+ loadConfigFile()
+ loadLuaSiteFile()
+ loadDebugLogFile()
+
+ if not mw.isAppleSystem():
+ mw.execShell("chown -R www:www " + getServerDir())
+ return 'ok'
+
+
+def luaRestart():
+ mw.opWeb("stop")
+ mw.opWeb("start")
+
+
+def start():
+ initDreplace()
+
+ import tool_task
+ tool_task.createBgTask()
+
+ # issues:326
+ luaRestart()
+ return 'ok'
+
+
+def stop():
+ path = luaConf()
+ if os.path.exists(path):
+ os.remove(path)
+
+ import tool_task
+ tool_task.removeBgTask()
+
+ luaRestart()
+ return 'ok'
+
+
+def restart():
+ initDreplace()
+ loadDebugLogFile()
+ luaRestart()
+ return 'ok'
+
+
+def reload():
+ initDreplace()
+
+ file_list = [
+ 'webstats_common.lua',
+ 'webstats_log.lua',
+ ]
+ for fl in file_list:
+ loadLuaFileReload(fl)
+
+ loadDebugLogFile()
+
+ luaRestart()
+ return 'ok'
+
+
+def getGlobalConf():
+ conf = getConf()
+ content = mw.readFile(conf)
+ content = json.loads(content)
+ return mw.returnJson(True, 'ok', content)
+
+
+def openLuaNeedRequestBody():
+ conf = luaConf()
+ content = mw.readFile(conf)
+ content = re.sub(r"lua_need_request_body (.*);",
+ 'lua_need_request_body on;', content)
+ mw.writeFile(conf, content)
+
+
+def closeLuaNeedRequestBody():
+ conf = luaConf()
+ content = mw.readFile(conf)
+ content = re.sub(r"lua_need_request_body (.*);",
+ 'lua_need_request_body off;', content)
+ mw.writeFile(conf, content)
+
+
+def setGlobalConf():
+ args = getArgs()
+
+ conf = getConf()
+ content = mw.readFile(conf)
+ content = json.loads(content)
+
+ open_force_get_request_body = False
+ for v in ['record_post_args', 'record_get_403_args']:
+ data = checkArgs(args, [v])
+ if data[0]:
+ rval = False
+ if args[v] == "true":
+ rval = True
+ open_force_get_request_body = True
+
+ content['global'][v] = rval
+
+ # 开启强制获取日志配置
+ if open_force_get_request_body:
+ openLuaNeedRequestBody()
+ else:
+ closeLuaNeedRequestBody()
+
+ for v in ['ip_top_num', 'uri_top_num', 'save_day']:
+ data = checkArgs(args, [v])
+ if data[0]:
+ content['global'][v] = int(args[v])
+
+ for v in ['cdn_headers', 'exclude_extension', 'exclude_status', 'exclude_ip']:
+ data = checkArgs(args, [v])
+ if data[0]:
+ content['global'][v] = args[v].split("\\n")
+
+ data = checkArgs(args, ['exclude_url'])
+ if data[0]:
+ exclude_url = args['exclude_url'].strip(";")
+ exclude_url_val = []
+ if exclude_url != "":
+ exclude_url_list = exclude_url.split(";")
+ for i in exclude_url_list:
+ t = i.split("|")
+ val = {}
+ val['mode'] = t[0]
+ val['url'] = t[1]
+ exclude_url_val.append(val)
+ content['global']['exclude_url'] = exclude_url_val
+
+ mw.writeFile(conf, json.dumps(content))
+ conf_lua = getServerDir() + "/lua/webstats_config.lua"
+ listToLuaFile(conf_lua, content)
+ luaRestart()
+ return mw.returnJson(True, '设置成功')
+
+
+def getSiteConf():
+ args = getArgs()
+
+ check = checkArgs(args, ['site'])
+ if not check[0]:
+ return check[1]
+
+ domain = args['site']
+ conf = getConf()
+ content = mw.readFile(conf)
+ content = json.loads(content)
+
+ site_conf = {}
+ if domain in content:
+ site_conf = content[domain]
+ else:
+ site_conf["cdn_headers"] = content['global']['cdn_headers']
+ site_conf["exclude_extension"] = content['global']['exclude_extension']
+ site_conf["exclude_status"] = content['global']['exclude_status']
+ site_conf["exclude_ip"] = content['global']['exclude_ip']
+ site_conf["exclude_url"] = content['global']['exclude_url']
+ site_conf["record_post_args"] = content['global']['record_post_args']
+ site_conf["record_get_403_args"] = content[
+ 'global']['record_get_403_args']
+
+ return mw.returnJson(True, 'ok', site_conf)
+
+
+def setSiteConf():
+ args = getArgs()
+ check = checkArgs(args, ['site'])
+ if not check[0]:
+ return check[1]
+
+ domain = args['site']
+ conf = getConf()
+ content = mw.readFile(conf)
+ content = json.loads(content)
+
+ site_conf = {}
+ if domain in content:
+ site_conf = content[domain]
+ else:
+ site_conf["cdn_headers"] = content['global']['cdn_headers']
+ site_conf["exclude_extension"] = content['global']['exclude_extension']
+ site_conf["exclude_status"] = content['global']['exclude_status']
+ site_conf["exclude_ip"] = content['global']['exclude_ip']
+ site_conf["exclude_url"] = content['global']['exclude_url']
+ site_conf["record_post_args"] = content['global']['record_post_args']
+ site_conf["record_get_403_args"] = content[
+ 'global']['record_get_403_args']
+
+ for v in ['record_post_args', 'record_get_403_args']:
+ data = checkArgs(args, [v])
+ if data[0]:
+ rval = False
+ if args[v] == "true":
+ rval = True
+ site_conf[v] = rval
+
+ for v in ['ip_top_num', 'uri_top_num', 'save_day']:
+ data = checkArgs(args, [v])
+ if data[0]:
+ site_conf[v] = int(args[v])
+
+ for v in ['cdn_headers', 'exclude_extension', 'exclude_status', 'exclude_ip']:
+ data = checkArgs(args, [v])
+ if data[0]:
+ site_conf[v] = args[v].strip().split("\\n")
+
+ data = checkArgs(args, ['exclude_url'])
+ if data[0]:
+ exclude_url = args['exclude_url'].strip(";")
+ exclude_url_val = []
+ if exclude_url != "":
+ exclude_url_list = exclude_url.split(";")
+ for i in exclude_url_list:
+ t = i.split("|")
+ val = {}
+ val['mode'] = t[0]
+ val['url'] = t[1]
+ exclude_url_val.append(val)
+ site_conf['exclude_url'] = exclude_url_val
+
+ content[domain] = site_conf
+
+ mw.writeFile(conf, json.dumps(content))
+ conf_lua = getServerDir() + "/lua/webstats_config.lua"
+ listToLuaFile(conf_lua, content)
+ luaRestart()
+ return mw.returnJson(True, '设置成功')
+
+
+def getSiteListData():
+ lua_dir = getServerDir() + "/lua"
+ path = lua_dir + "/default.json"
+ data = mw.readFile(path)
+ return json.loads(data)
+
+
+def getDefaultSite():
+ data = getSiteListData()
+ return mw.returnJson(True, 'OK', data)
+
+
+def setDefaultSite(name):
+ lua_dir = getServerDir() + "/lua"
+ path = lua_dir + "/default.json"
+ data = mw.readFile(path)
+ data = json.loads(data)
+ data['default'] = name
+ mw.writeFile(path, json.dumps(data))
+ return mw.returnJson(True, 'OK')
+
+
+def toSumField(sql):
+ l = sql.split(",")
+ field = ""
+ for x in l:
+ field += "sum(" + x + ") as " + x + ","
+ field = field.strip(',')
+ return field
+
+
+def getSiteStatInfo(domain, query_date):
+ conn = pSqliteDb('request_stat', domain)
+ conn = conn.where("1=1", ())
+
+ field = 'time,req,pv,uv,ip,length'
+ field_sum = toSumField(field.replace("time,", ""))
+
+ time_field = "substr(time,1,6),"
+
+ field_sum = time_field + field_sum
+ conn = conn.field(field_sum)
+ if query_date == "today":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 0 * 86400))
+ conn.andWhere("time >= ?", (todayTime,))
+ elif query_date == "yesterday":
+ startTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 1 * 86400))
+ endTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time()))
+ conn.andWhere("time>=? and time<=?", (startTime, endTime))
+ elif query_date == "l7":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 7 * 86400))
+ conn.andWhere("time >= ?", (todayTime,))
+ elif query_date == "l30":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 30 * 86400))
+ conn.andWhere("time >= ?", (todayTime,))
+ else:
+ exlist = query_date.split("-")
+ start = time.strftime(
+ '%Y%m%d00', time.localtime(int(exlist[0])))
+ end = time.strftime(
+ '%Y%m%d23', time.localtime(int(exlist[1])))
+ conn.andWhere("time >= ? and time <= ? ", (start, end,))
+
+ # 统计总数
+ stat_list = conn.inquiry(field)
+ del(stat_list[0]['time'])
+ return stat_list[0]
+
+
+def getOverviewList():
+ args = getArgs()
+ check = checkArgs(args, ['site', 'query_date', 'order'])
+ if not check[0]:
+ return check[1]
+
+ domain = args['site']
+ query_date = args['query_date']
+ order = args['order']
+
+ setDefaultSite(domain)
+ conn = pSqliteDb('request_stat', domain)
+ conn = conn.where("1=1", ())
+
+ field = 'time,req,pv,uv,ip,length'
+ field_sum = toSumField(field.replace("time,", ""))
+
+ time_field = "substr(time,1,8),"
+ if order == "hour":
+ time_field = "substr(time,9,10),"
+
+ field_sum = time_field + field_sum
+ conn = conn.field(field_sum)
+ if query_date == "today":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 0 * 86400))
+ conn.andWhere("time >= ?", (todayTime,))
+ elif query_date == "yesterday":
+ startTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 1 * 86400))
+ endTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time()))
+ conn.andWhere("time>=? and time<=?", (startTime, endTime))
+ elif query_date == "l7":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 7 * 86400))
+ conn.andWhere("time >= ?", (todayTime,))
+ elif query_date == "l30":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 30 * 86400))
+ conn.andWhere("time >= ?", (todayTime,))
+ else:
+ exlist = query_date.split("-")
+ start = time.strftime(
+ '%Y%m%d00', time.localtime(int(exlist[0])))
+ end = time.strftime(
+ '%Y%m%d23', time.localtime(int(exlist[1])))
+ conn.andWhere("time >= ? and time <= ? ", (start, end,))
+
+ # 统计总数
+ stat_list = conn.inquiry(field)
+ del(stat_list[0]['time'])
+
+ # 分组统计
+ dlist = conn.group(time_field.strip(",")).inquiry(field)
+
+ data = {}
+ data['data'] = dlist
+ data['stat_list'] = stat_list[0]
+
+ return mw.returnJson(True, 'ok', data)
+
+
+def getSiteList():
+ args = getArgs()
+ check = checkArgs(args, ['query_date'])
+ if not check[0]:
+ return check[1]
+
+ query_date = args['query_date']
+
+ data = getSiteListData()
+ data_list = data["list"]
+
+ rdata = []
+ for x in data_list:
+ tmp = getSiteStatInfo(x, query_date)
+ tmp["site"] = x
+ rdata.append(tmp)
+ return mw.returnJson(True, 'ok', rdata)
+
+
+def getLogsRealtimeInfo():
+ '''
+ 实时信息
+ '''
+ import datetime
+ args = getArgs()
+ check = checkArgs(args, ['site', 'type','second'])
+ if not check[0]:
+ return check[1]
+
+ domain = args['site']
+ dtype = args['type']
+ second = int(args['second'])
+
+
+ conn = pSqliteDb('web_logs', domain)
+ timeInt = time.mktime(datetime.datetime.now().timetuple())
+
+ conn = conn.where("time>=?", (int(timeInt) - second,))
+
+ field = 'time,body_length'
+ field_sum = toSumField(field.replace("time,", ""))
+ time_field = "substr(time,1,2) as time,"
+ time_field = time_field + field_sum
+ clist = conn.field(time_field.strip(",")).group(
+ 'substr(time,1,2)').inquiry(field)
+
+ body_count = 0
+ if len(clist) > 0:
+ body_count = clist[0]['body_length']
+
+ req_count = conn.count()
+
+ data = {}
+ data['realtime_traffic'] = body_count
+ data['realtime_request'] = req_count
+
+ return mw.returnJson(True, 'ok', data)
+
+
+def attacHistoryLogHack(conn, site_name, query_date='today'):
+ if query_date == "today":
+ return
+ db_dir = getServerDir() + '/logs/' + site_name
+ file = db_dir + '/history_logs.db'
+ if os.path.exists(file):
+ attach = "ATTACH DATABASE '" + file + "' as 'history_logs'"
+ # print(attach)
+ r = conn.originExecute(attach)
+ sql_table = "(select * from web_logs union all select * from history_logs.web_logs)"
+ # print(sql_table)
+ conn.table(sql_table)
+
+
+def getLogsList():
+ args = getArgs()
+ check = checkArgs(args, ['page', 'page_size','site', 'method',
+ 'status_code', 'spider_type', 'request_time', 'query_date', 'search_uri'])
+ if not check[0]:
+ return check[1]
+
+ page = int(args['page'])
+ page_size = int(args['page_size'])
+ domain = args['site']
+ tojs = args['tojs']
+ method = args['method']
+ status_code = args['status_code']
+ request_time = args['request_time']
+ request_size = args['request_size']
+ spider_type = args['spider_type']
+ query_date = args['query_date']
+ search_uri = args['search_uri']
+ referer = args['referer']
+ ip = args['ip']
+ setDefaultSite(domain)
+
+ limit = str(page_size) + ' offset ' + str(page_size * (page - 1))
+ conn = pSqliteDb('web_logs', domain)
+
+ field = 'time,ip,domain,server_name,method,is_spider,protocol,status_code,request_headers,ip_list,client_port,body_length,user_agent,referer,request_time,uri,body_length'
+ condition = ''
+ conn = conn.field(field)
+ conn = conn.where("1=1", ())
+
+ if referer != 'all':
+ if referer == '1':
+ conn = conn.andWhere("referer <> ? ", ('',))
+ elif referer == '-1':
+ conn = conn.andWhere("referer is null ", ())
+
+ if ip != '':
+ conn = conn.andWhere("ip=?", (ip,))
+
+ if method != "all":
+ conn = conn.andWhere("method=?", (method,))
+
+ if request_time != "all":
+ request_time_s = request_time.strip().split('-')
+ # print(request_time_s)
+ if len(request_time_s) == 2:
+ conn = conn.andWhere("request_time>=? and request_time", (request_time_s[0],request_time_s[1],))
+ if len(request_time_s) == 1:
+ conn = conn.andWhere("request_time>=?", (request_time,))
+
+ if request_size != "all":
+ request_size_s = request_size.strip().split('-')
+ # print(int(request_size_s[0])*1024)
+ if len(request_size_s) == 2:
+ conn = conn.andWhere("body_length>=? and body_length", (int(request_size_s[0])*1024,int(request_size_s[1])*1024,))
+ if len(request_size_s) == 1:
+ conn = conn.andWhere("body_length>=?", (int(request_size_s[0])*1024,))
+
+ if spider_type == "normal":
+ pass
+ elif spider_type == "only_spider":
+ conn = conn.andWhere("is_spider>?", (0,))
+ elif spider_type == "no_spider":
+ conn = conn.andWhere("is_spider=?", (0,))
+ elif int(spider_type) > 0:
+ conn = conn.andWhere("is_spider=?", (spider_type,))
+
+ todayTime = time.strftime('%Y-%m-%d 00:00:00', time.localtime())
+ todayUt = int(time.mktime(time.strptime(todayTime, "%Y-%m-%d %H:%M:%S")))
+ if query_date == 'today':
+ conn = conn.andWhere("time>=?", (todayUt,))
+ elif query_date == "yesterday":
+ conn = conn.andWhere("time>=? and time<=?", (todayUt - 86400, todayUt))
+ elif query_date == "l7":
+ conn = conn.andWhere("time>=?", (todayUt - 7 * 86400,))
+ elif query_date == "l30":
+ conn = conn.andWhere("time>=?", (todayUt - 30 * 86400,))
+ else:
+ exlist = query_date.split("-")
+ conn = conn.andWhere("time>=? and time<=?", (exlist[0], exlist[1]))
+
+ if search_uri != "":
+ conn = conn.andWhere("uri like '%" + search_uri + "%'", ())
+
+ if status_code != "all":
+ conn = conn.andWhere("status_code=?", (status_code,))
+
+ attacHistoryLogHack(conn, domain, query_date)
+
+ conn.changeTextFactoryToBytes()
+ clist = conn.limit(limit).order('time desc').inquiry()
+
+ for x in range(len(clist)):
+ req_line = clist[x]
+ for cx in req_line:
+ v = req_line[cx]
+ if type(v) == bytes:
+ try:
+ clist[x][cx] = v.decode('utf-8')
+ except Exception as e:
+ v = str(v)
+ v = v.replace("b'",'').strip("'")
+ clist[x][cx] = v
+ else:
+ clist[x][cx] = v
+
+ # print(clist)
+ count_key = "count(*) as num"
+ count = conn.field(count_key).limit('').order('').inquiry()
+ # print(count)
+ count = count[0][count_key]
+
+ data = {}
+ _page = {}
+ _page['count'] = count
+ _page['p'] = page
+ _page['row'] = page_size
+ _page['tojs'] = tojs
+ data['page'] = mw.getPage(_page)
+ data['data'] = clist
+
+ return mw.returnJson(True, 'ok', data)
+
+
+def getLogsErrorList():
+ args = getArgs()
+ check = checkArgs(args, ['page', 'page_size',
+ 'site', 'status_code', 'query_date'])
+ if not check[0]:
+ return check[1]
+
+ page = int(args['page'])
+ page_size = int(args['page_size'])
+ domain = args['site']
+ tojs = args['tojs']
+ status_code = args['status_code']
+ query_date = args['query_date']
+ setDefaultSite(domain)
+
+ limit = str(page_size) + ' offset ' + str(page_size * (page - 1))
+ conn = pSqliteDb('web_logs', domain)
+
+ field = 'time,ip,domain,server_name,method,protocol,status_code,ip_list,client_port,body_length,user_agent,referer,request_time,uri,body_length'
+ conn = conn.field(field)
+ conn = conn.where("1=1", ())
+
+ if status_code != "all":
+ if status_code.find("x") > -1:
+ status_code = status_code.replace("x", "%")
+ conn = conn.andWhere("status_code like ?", (status_code,))
+ else:
+ conn = conn.andWhere("status_code=?", (status_code,))
+ else:
+ conn = conn.andWhere(
+ "(status_code like '50%' or status_code like '40%')", ())
+
+ todayTime = time.strftime('%Y-%m-%d 00:00:00', time.localtime())
+ todayUt = int(time.mktime(time.strptime(todayTime, "%Y-%m-%d %H:%M:%S")))
+ if query_date == 'today':
+ conn = conn.andWhere("time>=?", (todayUt,))
+ elif query_date == "yesterday":
+ conn = conn.andWhere("time>=? and time<=?", (todayUt - 86400, todayUt))
+ elif query_date == "l7":
+ conn = conn.andWhere("time>=?", (todayUt - 7 * 86400,))
+ elif query_date == "l30":
+ conn = conn.andWhere("time>=?", (todayUt - 30 * 86400,))
+ else:
+ exlist = query_date.split("-")
+ conn = conn.andWhere("time>=? and time<=?", (exlist[0], exlist[1]))
+
+ attacHistoryLogHack(conn, domain, query_date)
+
+ clist = conn.limit(limit).order('time desc').inquiry()
+ count_key = "count(*) as num"
+ count = conn.field(count_key).limit('').order('').inquiry()
+ count = count[0][count_key]
+
+ data = {}
+ _page = {}
+ _page['count'] = count
+ _page['p'] = page
+ _page['row'] = page_size
+ _page['tojs'] = tojs
+ data['page'] = mw.getPage(_page)
+ data['data'] = clist
+
+ return mw.returnJson(True, 'ok', data)
+
+
+def getClientStatList():
+ args = getArgs()
+ check = checkArgs(args, ['page', 'page_size',
+ 'site', 'query_date'])
+ if not check[0]:
+ return check[1]
+
+ page = int(args['page'])
+ page_size = int(args['page_size'])
+ domain = args['site']
+ tojs = args['tojs']
+ query_date = args['query_date']
+ setDefaultSite(domain)
+
+ conn = pSqliteDb('client_stat', domain)
+ stat = pSqliteDb('client_stat', domain)
+
+ # 列表
+ limit = str(page_size) + ' offset ' + str(page_size * (page - 1))
+ field = 'time,weixin,android,iphone,mac,windows,linux,edeg,firefox,msie,metasr,qh360,theworld,tt,maxthon,opera,qq,uc,pc2345,safari,chrome,machine,mobile,other'
+ field_sum = toSumField(field.replace("time,", ""))
+ time_field = "substr(time,1,8),"
+ field_sum = time_field + field_sum
+
+ stat = stat.field(field_sum)
+ if query_date == "today":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 0 * 86400))
+ stat.where("time >= ?", (todayTime,))
+ elif query_date == "yesterday":
+ startTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 1 * 86400))
+ endTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time()))
+ stat.where("time>=? and time<=?", (startTime, endTime))
+ elif query_date == "l7":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 7 * 86400))
+ stat.where("time >= ?", (todayTime,))
+ elif query_date == "l30":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 30 * 86400))
+ stat.where("time >= ?", (todayTime,))
+ else:
+ exlist = query_date.split("-")
+ start = time.strftime(
+ '%Y%m%d00', time.localtime(int(exlist[0])))
+ end = time.strftime(
+ '%Y%m%d23', time.localtime(int(exlist[1])))
+ stat.where("time >= ? and time <= ? ", (start, end,))
+
+ # 图表数据
+ statlist = stat.group('substr(time,1,4)').inquiry(field)
+
+ if len(statlist) > 0:
+ del(statlist[0]['time'])
+
+ pc = 0
+ pc_key_list = ['chrome', 'qh360', 'edeg', 'firefox', 'safari', 'msie',
+ 'metasr', 'theworld', 'tt', 'maxthon', 'opera', 'qq', 'pc2345']
+
+ for x in pc_key_list:
+ pc += statlist[0][x]
+
+ mobile = 0
+ mobile_key_list = ['mobile', 'android', 'iphone', 'weixin']
+ for x in mobile_key_list:
+ mobile += statlist[0][x]
+ reqest_total = pc + mobile
+
+ sum_data = {
+ "pc": pc,
+ "mobile": mobile,
+ "reqest_total": reqest_total,
+ }
+
+ statlist = sorted(statlist[0].items(),
+ key=lambda x: x[1], reverse=True)
+ _statlist = statlist[0:10]
+ __statlist = {}
+ statlist = []
+ for x in _statlist:
+ __statlist[x[0]] = x[1]
+ statlist.append(__statlist)
+ else:
+ sum_data = {
+ "pc": 0,
+ "mobile": 0,
+ "reqest_total": 0,
+ }
+ statlist = []
+
+ # 列表数据
+ conn = conn.field(field_sum)
+ clist = conn.group('substr(time,1,8)').limit(
+ limit).order('time desc').inquiry(field)
+
+ sql = "SELECT count(*) num from (\
+ SELECT count(*) as num FROM client_stat GROUP BY substr(time,1,8)\
+ )"
+ result = conn.query(sql, ())
+ result = list(result)
+ count = result[0][0]
+
+ data = {}
+ _page = {}
+ _page['count'] = count
+ _page['p'] = page
+ _page['row'] = page_size
+ _page['tojs'] = tojs
+ data['page'] = mw.getPage(_page)
+ data['data'] = clist
+ data['stat_list'] = statlist
+ data['sum_data'] = sum_data
+
+ return mw.returnJson(True, 'ok', data)
+
+
+def getDateRangeList(start, end):
+ dlist = []
+ if start > end:
+ for x in list(range(start, 32, 1)):
+ dlist.append(x)
+
+ for x in list(range(1, end, 1)):
+ dlist.append(x)
+ else:
+ for x in list(range(start, end, 1)):
+ dlist.append(x)
+
+ return dlist
+
+
+def getIpStatList():
+ args = getArgs()
+ check = checkArgs(args, ['site', 'query_date'])
+ if not check[0]:
+ return check[1]
+
+ domain = args['site']
+ tojs = args['tojs']
+ query_date = args['query_date']
+ setDefaultSite(domain)
+
+ conn = pSqliteDb('ip_stat', domain)
+
+ origin_field = "ip,day,flow"
+
+ if query_date == "today":
+ ftime = time.localtime(time.time())
+ day = ftime.tm_mday
+
+ field_day = "day" + str(day)
+ field_flow = "flow" + str(day)
+ # print(field_day, field_flow)
+
+ field = "ip," + field_day + ' as day,' + field_flow + " as flow"
+
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+
+ elif query_date == "yesterday":
+
+ ftime = time.localtime(time.time() - 86400)
+ day = ftime.tm_mday
+
+ field_day = "day" + str(day)
+ field_flow = "flow" + str(day)
+
+ field = "ip," + field_day + ' as day,' + field_flow + " as flow"
+
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+ elif query_date == "l7":
+
+ field_day = ""
+ field_flow = ""
+
+ now_time = time.localtime(time.time())
+ end_day = now_time.tm_mday
+
+ start_time = time.localtime(time.time() - 7 * 86400)
+ start_day = start_time.tm_mday
+
+ rlist = getDateRangeList(start_day, end_day)
+
+ for x in rlist:
+ field_day += "+cast(day" + str(x) + " as TEXT)"
+ field_flow += "+cast(flow" + str(x) + " as TEXT)"
+
+ field_day = field_day.strip("+")
+ field_flow = field_flow.strip("+")
+
+ field = "ip,(" + field_day + ') as day,(' + field_flow + ") as flow"
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+
+ elif query_date == "l30":
+
+ field_day = ""
+ field_flow = ""
+
+ for x in list(range(1, 32, 1)):
+ field_day += "+cast(day" + str(x) + " as TEXT)"
+ field_flow += "+cast(flow" + str(x) + " as TEXT)"
+
+ field_day = field_day.strip("+")
+ field_flow = field_flow.strip("+")
+
+ # print(field_day)
+ # print(field_flow)
+ field = "ip,(" + field_day + ') as day,(' + field_flow + ") as flow"
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+
+ clist = conn.order("flow desc").limit("50").inquiry(origin_field)
+ # print(clist)
+
+ total_req = 0
+ total_flow = 0
+
+ gepip_mmdb = getServerDir() + '/GeoLite2-City.mmdb'
+ geoip_exists = False
+ if os.path.exists(gepip_mmdb):
+ import geoip2.database
+ reader = geoip2.database.Reader(gepip_mmdb)
+ geoip_exists = True
+ # response = reader.city("172.70.206.144")
+ # print(response.country.names["zh-CN"])
+ # print(response.subdivisions.most_specific.names["zh-CN"])
+ # print(response.city.names["zh-CN"])
+
+ for x in clist:
+ total_req += x['day']
+ total_flow += x['flow']
+
+ for i in range(len(clist)):
+ clist[i]['day_rate'] = round((clist[i]['day'] / total_req) * 100, 2)
+ clist[i]['flow_rate'] = round((clist[i]['flow'] / total_flow) * 100, 2)
+ ip = clist[i]['ip']
+
+ if ip == "127.0.0.1":
+ clist[i]['area'] = "本地"
+ elif geoip_exists:
+ try:
+ response = reader.city(ip)
+ country = response.country.names["zh-CN"]
+
+ # print(ip, response.subdivisions)
+ _subdivisions = response.subdivisions
+ try:
+ if len(_subdivisions) < 1:
+ subdivisions = ""
+ else:
+ subdivisions = "," + response.subdivisions.most_specific.names[
+ "zh-CN"]
+ except Exception as e:
+ subdivisions = ""
+
+ try:
+ if 'zh-CN' in response.city.names:
+ city = "," + response.city.names["zh-CN"]
+ else:
+ city = "," + response.city.names["en"]
+ except Exception as e:
+ city = ""
+
+ clist[i]['area'] = country + subdivisions + city
+ except Exception as e:
+ clist[i]['area'] = "内网?"
+
+ return mw.returnJson(True, 'ok', clist)
+
+
+def getUriStatList():
+ args = getArgs()
+ check = checkArgs(args, ['site', 'query_date'])
+ if not check[0]:
+ return check[1]
+
+ domain = args['site']
+ tojs = args['tojs']
+ query_date = args['query_date']
+ setDefaultSite(domain)
+
+ conn = pSqliteDb('uri_stat', domain)
+
+ origin_field = "uri,day,flow"
+
+ if query_date == "today":
+ ftime = time.localtime(time.time())
+ day = ftime.tm_mday
+
+ field_day = "day" + str(day)
+ field_flow = "flow" + str(day)
+ # print(field_day, field_flow)
+
+ field = "uri," + field_day + ' as day,' + field_flow + " as flow"
+
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+
+ elif query_date == "yesterday":
+
+ ftime = time.localtime(time.time() - 86400)
+ day = ftime.tm_mday
+
+ field_day = "day" + str(day)
+ field_flow = "flow" + str(day)
+
+ field = "uri," + field_day + ' as day,' + field_flow + " as flow"
+
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+ elif query_date == "l7":
+
+ field_day = ""
+ field_flow = ""
+
+ now_time = time.localtime(time.time())
+ end_day = now_time.tm_mday
+
+ start_time = time.localtime(time.time() - 7 * 86400)
+ start_day = start_time.tm_mday
+
+ rlist = getDateRangeList(start_day, end_day)
+
+ for x in rlist:
+ field_day += "+cast(day" + str(x) + " as TEXT)"
+ field_flow += "+cast(flow" + str(x) + " as TEXT)"
+
+ field_day = field_day.strip("+")
+ field_flow = field_flow.strip("+")
+
+ field = "uri,(" + field_day + ') as day,(' + field_flow + ") as flow"
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+
+ elif query_date == "l30":
+
+ field_day = ""
+ field_flow = ""
+
+ for x in list(range(1, 32, 1)):
+ field_day += "+cast(day" + str(x) + " as TEXT)"
+ field_flow += "+cast(flow" + str(x) + " as TEXT)"
+
+ field_day = field_day.strip("+")
+ field_flow = field_flow.strip("+")
+
+ # print(field_day)
+ # print(field_flow)
+ field = "uri,(" + field_day + ') as day,(' + field_flow + ") as flow"
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+
+ clist = conn.order("flow desc").limit("50").inquiry(origin_field)
+
+ total_req = 0
+ total_flow = 0
+
+ for x in clist:
+ total_req += x['day']
+ total_flow += x['flow']
+
+ for i in range(len(clist)):
+ clist[i]['day_rate'] = round((clist[i]['day'] / total_req) * 100, 2)
+ clist[i]['flow_rate'] = round((clist[i]['flow'] / total_flow) * 100, 2)
+
+ return mw.returnJson(True, 'ok', clist)
+
+
+def getWebLogCount(domain, query_date):
+ conn = pSqliteDb('web_logs', domain)
+
+ todayTime = time.strftime('%Y-%m-%d 00:00:00', time.localtime())
+ todayUt = int(time.mktime(time.strptime(todayTime, "%Y-%m-%d %H:%M:%S")))
+ if query_date == 'today':
+ conn = conn.where("time>=?", (todayUt,))
+ elif query_date == "yesterday":
+ conn = conn.where("time>=? and time<=?", (todayUt - 86400, todayUt))
+ elif query_date == "l7":
+ conn = conn.where("time>=?", (todayUt - 7 * 86400,))
+ elif query_date == "l30":
+ conn = conn.where("time>=?", (todayUt - 30 * 86400,))
+ else:
+ exlist = query_date.split("-")
+ conn = conn.where("time>=? and time<=?", (exlist[0], exlist[1]))
+
+ count_key = "count(*) as num"
+ count = conn.field(count_key).limit('').order('').inquiry()
+ count = count[0][count_key]
+ return count
+
+
+def getSpiderStatList():
+ args = getArgs()
+ check = checkArgs(args, ['page', 'page_size',
+ 'site', 'query_date'])
+ if not check[0]:
+ return check[1]
+
+ page = int(args['page'])
+ page_size = int(args['page_size'])
+ domain = args['site']
+ tojs = args['tojs']
+ query_date = args['query_date']
+ setDefaultSite(domain)
+
+ conn = pSqliteDb('spider_stat', domain)
+ stat = pSqliteDb('spider_stat', domain)
+
+ total_req = getWebLogCount(domain, query_date)
+
+ # 列表
+ limit = str(page_size) + ' offset ' + str(page_size * (page - 1))
+ field = 'time,bytes,bing,soso,yahoo,sogou,google,baidu,qh360,youdao,yandex,dnspod,other'
+ field_sum = toSumField(field.replace("time,", ""))
+ time_field = "substr(time,1,8),"
+ field_sum = time_field + field_sum
+
+ stat = stat.field(field_sum)
+ if query_date == "today":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 0 * 86400))
+ stat.where("time >= ?", (todayTime,))
+ elif query_date == "yesterday":
+ startTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 1 * 86400))
+ endTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time()))
+ stat.where("time>=? and time<=?", (startTime, endTime))
+ elif query_date == "l7":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 7 * 86400))
+ stat.where("time >= ?", (todayTime,))
+ elif query_date == "l30":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 30 * 86400))
+ stat.where("time >= ?", (todayTime,))
+ else:
+ exlist = query_date.split("-")
+ start = time.strftime(
+ '%Y%m%d00', time.localtime(int(exlist[0])))
+ end = time.strftime(
+ '%Y%m%d23', time.localtime(int(exlist[1])))
+ stat.where("time >= ? and time <= ? ", (start, end,))
+
+ # 图表数据
+ statlist = stat.group('substr(time,1,4)').inquiry(field)
+
+ if len(statlist) > 0:
+ del(statlist[0]['time'])
+
+ spider_total = 0
+ for x in statlist[0]:
+ spider_total += statlist[0][x]
+
+ sum_data = {"spider": spider_total, "reqest_total": total_req}
+ statlist = sorted(statlist[0].items(),
+ key=lambda x: x[1], reverse=True)
+ _statlist = statlist[0:9]
+ __statlist = {}
+ statlist = []
+ for x in _statlist:
+ __statlist[x[0]] = x[1]
+ statlist.append(__statlist)
+ else:
+ sum_data = {"spider": 0, "reqest_total": total_req}
+ statlist = []
+
+ # 列表数据
+ conn = conn.field(field_sum)
+ clist = conn.group('substr(time,1,8)').limit(
+ limit).order('time desc').inquiry(field)
+
+ sql = "SELECT count(*) num from (\
+ SELECT count(*) as num FROM spider_stat GROUP BY substr(time,1,8)\
+ )"
+ result = conn.query(sql, ())
+ result = list(result)
+ count = result[0][0]
+
+ data = {}
+ _page = {}
+ _page['count'] = count
+ _page['p'] = page
+ _page['row'] = page_size
+ _page['tojs'] = tojs
+ data['page'] = mw.getPage(_page)
+ data['data'] = clist
+ data['stat_list'] = statlist
+ data['sum_data'] = sum_data
+
+ return mw.returnJson(True, 'ok', data)
+
+
+def installPreInspection():
+ check_op = mw.getServerDir() + "/openresty"
+ if not os.path.exists(check_op):
+ return "请先安装OpenResty"
+ return 'ok'
+
+def uninstallPreInspection():
+ stop()
+ return 'ok'
+
+if __name__ == "__main__":
+ 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 == 'install_pre_inspection':
+ print(installPreInspection())
+ elif func == 'uninstall_pre_inspection':
+ print(uninstallPreInspection())
+ elif func == 'run_info':
+ print(runInfo())
+ elif func == 'get_global_conf':
+ print(getGlobalConf())
+ elif func == 'set_global_conf':
+ print(setGlobalConf())
+ elif func == 'get_site_conf':
+ print(getSiteConf())
+ elif func == 'set_site_conf':
+ print(setSiteConf())
+ elif func == 'get_default_site':
+ print(getDefaultSite())
+ elif func == 'get_overview_list':
+ print(getOverviewList())
+ elif func == 'get_site_list':
+ print(getSiteList())
+ elif func == 'get_logs_list':
+ print(getLogsList())
+ elif func == 'get_logs_error_list':
+ print(getLogsErrorList())
+ elif func == 'get_logs_realtime_info':
+ print(getLogsRealtimeInfo())
+ elif func == 'get_client_stat_list':
+ print(getClientStatList())
+ elif func == 'get_ip_stat_list':
+ print(getIpStatList())
+ elif func == 'get_uri_stat_list':
+ print(getUriStatList())
+ elif func == 'get_spider_stat_list':
+ print(getSpiderStatList())
+ else:
+ print('error')
diff --git a/plugins/webstats/info.json b/plugins/webstats/info.json
new file mode 100755
index 000000000..0eadd5e46
--- /dev/null
+++ b/plugins/webstats/info.json
@@ -0,0 +1,31 @@
+{
+ "hook":[
+ {
+ "tag":"site_cb",
+ "site_cb": {
+ "title":"网站统计",
+ "name":"webstats",
+ "add":{"func":"reload"},
+ "update":{"func":"reload"},
+ "delete":{"func":"reload"}
+ }
+ }
+ ],
+ "sort": "2",
+ "ps": "网站统计报表",
+ "name": "webstats",
+ "title": "网站统计",
+ "shell": "install.sh",
+ "versions":"0.2.5",
+ "tip": "soft",
+ "install_pre_inspection":true,
+ "uninstall_pre_inspection":true,
+ "checks": "server/webstats/version.pl",
+ "path": "server/webstats",
+ "display": 1,
+ "author": "midoks",
+ "date": "2022-07-18",
+ "home": "https://github.com/midoks/mdserver-web",
+ "type": 0,
+ "pid": "1"
+}
\ No newline at end of file
diff --git a/plugins/webstats/install.old.sh b/plugins/webstats/install.old.sh
new file mode 100755
index 000000000..bbf82e3fd
--- /dev/null
+++ b/plugins/webstats/install.old.sh
@@ -0,0 +1,160 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+
+## https://www.yangshuaibin.com/detail/392251
+# cd /www/server/mdserver-web/plugins/webstats && bash install.sh install 0.2.5
+# /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/webstats && bash install.sh install 0.2.5
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+VERSION=$2
+sys_os=`uname`
+
+HTTP_PREFIX="https://"
+LOCAL_ADDR=common
+cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ HTTP_PREFIX="https://"
+fi
+
+PIPSRC="https://pypi.python.org/simple"
+if [ "$LOCAL_ADDR" != "common" ];then
+ PIPSRC="https://pypi.tuna.tsinghua.edu.cn/simple"
+fi
+
+
+if [ "$sys_os" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+get_latest_release() {
+ curl -sL "https://api.github.com/repos/$1/releases/latest" | grep '"tag_name":' | cut -d'"' -f4
+}
+
+Install_App()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/source/webstats
+ mkdir -p $serverPath/webstats
+
+ # 下载源码安装包
+ # curl -O $serverPath/source/webstats/lua-5.1.5.tar.gz https://www.lua.org/ftp/lua-5.1.5.tar.gz
+ # cd $serverPath/source/webstats && tar xvf lua-5.1.5.tar.gz
+ # cd lua-5.1.5
+ # make linux test && make install
+
+
+ # luarocks
+ if [ ! -f $serverPath/source/webstats/luarocks-3.5.0.tar.gz ];then
+ wget --no-check-certificate -O $serverPath/source/webstats/luarocks-3.5.0.tar.gz http://luarocks.org/releases/luarocks-3.5.0.tar.gz
+ fi
+
+ if [ ! -d $serverPath/source/webstats/luarocks-3.5.0 ];then
+ cd $serverPath/source/webstats && tar xvf luarocks-3.5.0.tar.gz
+ fi
+
+ cd $serverPath/source/webstats/luarocks-3.5.0 && ./configure --prefix=$serverPath/webstats/luarocks \
+ --with-lua-include=$serverPath/openresty/luajit/include/luajit-2.1 \
+ --with-lua-bin=$serverPath/openresty/luajit/bin
+ make -I${serverPath}/openresty/luajit/bin
+ make install
+
+
+ # lsqlite3_fsl09y
+ if [ ! -f $serverPath/source/webstats/lsqlite3_fsl09y.zip ];then
+ wget --no-check-certificate -O $serverPath/source/webstats/lsqlite3_fsl09y.zip http://lua.sqlite.org/index.cgi/zip/lsqlite3_fsl09y.zip?uuid=fsl_9y
+
+ fi
+
+ if [ ! -d $serverPath/source/webstats/lsqlite3_fsl09y ];then
+ cd $serverPath/source/webstats && unzip lsqlite3_fsl09y.zip
+ fi
+
+ PATH=${serverPath}/openresty/luajit:${serverPath}/openresty/luajit/include/luajit-2.1:$PATH
+ export PATH=$PATH:$serverPath/webstats/luarocks/bin
+
+ if [ ! -f $serverPath/webstats/lua/lsqlite3.so ];then
+ if [ "${sys_os}" == "Darwin" ];then
+ cd $serverPath/source/webstats/lsqlite3_fsl09y
+ # SQLITE_DIR=/usr/local/Cellar/sqlite/3.36.0
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+ echo "BREW_DIR:"${BREW_DIR}
+ find_cfg=`cat Makefile | grep 'SQLITE_DIR'`
+ if [ "$find_cfg" == "" ];then
+ LIB_SQLITE_DIR=`brew info sqlite | grep ${BREW_DIR}/Cellar/sqlite | cut -d \ -f 1 | awk 'END {print}'`
+ echo "LIB_SQLITE_DIR:"${LIB_SQLITE_DIR}
+ sed -i $BAK "s#\$(ROCKSPEC)#\$(ROCKSPEC) SQLITE_DIR=${LIB_SQLITE_DIR}#g" Makefile
+ fi
+ make
+ else
+ cd $serverPath/source/webstats/lsqlite3_fsl09y && make
+ fi
+ fi
+
+ # copy to code path
+ DEFAULT_DIR=$serverPath/webstats/luarocks/lib/lua/5.1
+ if [ -f ${DEFAULT_DIR}/lsqlite3.so ];then
+ mkdir -p $serverPath/webstats/lua
+ cp -rf ${DEFAULT_DIR}/lsqlite3.so $serverPath/webstats/lua/lsqlite3.so
+ fi
+
+ # https://github.com/P3TERX/GeoLite.mmdb
+ pip install geoip2 -i $PIPSRC
+ # if [ ! -f $serverPath/webstats/GeoLite2-City.mmdb ];then
+ # wget --no-check-certificate -O $serverPath/webstats/GeoLite2-City.mmdb https://github.com/P3TERX/GeoLite.mmdb/releases/download/2022.10.16/GeoLite2-City.mmdb
+ # fi
+
+ # 缓存数据
+ GEO_VERSION=$(get_latest_release "P3TERX/GeoLite.mmdb")
+ if [ ! -f $serverPath/source/webstats/GeoLite2-City.mmdb ];then
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ wget --no-check-certificate -O $serverPath/source/webstats/GeoLite2-City.mmdb https://dl.midoks.icu/soft/webstats/GeoLite2-City.mmdb
+ else
+ wget --no-check-certificate -O $serverPath/source/webstats/GeoLite2-City.mmdb https://github.com/P3TERX/GeoLite.mmdb/releases/download/${GEO_VERSION}/GeoLite2-City.mmdb
+ fi
+ fi
+
+ if [ -f $serverPath/source/webstats/GeoLite2-City.mmdb ];then
+ cp -rf $serverPath/source/webstats/GeoLite2-City.mmdb $serverPath/webstats/GeoLite2-City.mmdb
+ fi
+
+ cd $rootPath && python3 plugins/webstats/index.py start
+ echo "${VERSION}" > $serverPath/webstats/version.pl
+
+ echo '网站统计安装完成'
+
+ # delete install data
+ if [ -d $serverPath/source/webstats/lsqlite3_fsl09y ];then
+ rm -rf $serverPath/source/webstats/lsqlite3_fsl09y
+ fi
+ if [ -d $serverPath/source/webstats/luarocks-3.5.0 ];then
+ rm -rf $serverPath/source/webstats/luarocks-3.5.0
+ fi
+}
+
+Uninstall_App()
+{
+ cd $rootPath && python3 plugins/webstats/index.py stop
+ rm -rf $serverPath/webstats
+ echo "网站统计卸载完成"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/webstats/install.sh b/plugins/webstats/install.sh
new file mode 100755
index 000000000..426ca07ec
--- /dev/null
+++ b/plugins/webstats/install.sh
@@ -0,0 +1,158 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH=$PATH:/opt/homebrew/bin
+
+
+## https://www.yangshuaibin.com/detail/392251
+# cd /www/server/mdserver-web/plugins/webstats && bash install.sh install 0.2.5
+# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/webstats && bash install.sh install 0.2.5
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+VERSION=$2
+sys_os=`uname`
+
+HTTP_PREFIX="https://"
+LOCAL_ADDR=common
+cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ HTTP_PREFIX="https://"
+fi
+
+PIPSRC="https://pypi.python.org/simple"
+if [ "$LOCAL_ADDR" != "common" ];then
+ PIPSRC="https://pypi.tuna.tsinghua.edu.cn/simple"
+fi
+
+
+if [ "$sys_os" == "Darwin" ];then
+ BAK='_bak'
+else
+ BAK=''
+fi
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+get_latest_release() {
+ curl -sL "https://api.github.com/repos/$1/releases/latest" | grep '"tag_name":' | cut -d'"' -f4
+}
+
+Install_App()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/source/webstats
+ mkdir -p $serverPath/webstats
+
+ # 下载源码安装包
+ # curl -O $serverPath/source/webstats/lua-5.1.5.tar.gz https://www.lua.org/ftp/lua-5.1.5.tar.gz
+ # cd $serverPath/source/webstats && tar xvf lua-5.1.5.tar.gz
+ # cd lua-5.1.5
+ # make linux test && make install
+
+
+ # luarocks
+ if [ ! -f $serverPath/source/webstats/luarocks-3.5.0.tar.gz ];then
+ wget --no-check-certificate -O $serverPath/source/webstats/luarocks-3.5.0.tar.gz http://luarocks.org/releases/luarocks-3.5.0.tar.gz
+ fi
+
+ if [ ! -d $serverPath/source/webstats/luarocks-3.5.0 ];then
+ cd $serverPath/source/webstats && tar xvf luarocks-3.5.0.tar.gz
+ fi
+
+ cd $serverPath/source/webstats/luarocks-3.5.0 && ./configure --prefix=$serverPath/webstats/luarocks \
+ --with-lua-include=$serverPath/openresty/luajit/include/luajit-2.1 \
+ --with-lua-bin=$serverPath/openresty/luajit/bin
+ make -I${serverPath}/openresty/luajit/bin
+ make install
+
+ if [ ! -f $serverPath/source/webstats/lsqlite3_v096.zip ];then
+ wget --no-check-certificate -O $serverPath/source/webstats/lsqlite3_v096.zip https://github.com/midoks/mdserver-web/releases/download/0.18.4/lsqlite3_v096.zip
+ fi
+
+ if [ ! -d $serverPath/source/webstats/lsqlite3_v096 ];then
+ cd $serverPath/source/webstats && unzip lsqlite3_v096.zip
+ fi
+
+
+ PATH=${serverPath}/openresty/luajit:${serverPath}/openresty/luajit/include/luajit-2.1:$PATH
+ export PATH=$PATH:$serverPath/webstats/luarocks/bin
+
+ if [ ! -f $serverPath/webstats/lua/lsqlite3.so ];then
+ if [ "${sys_os}" == "Darwin" ];then
+ cd $serverPath/source/webstats/lsqlite3_v096
+ # SQLITE_DIR=/usr/local/Cellar/sqlite/3.36.0
+ BREW_DIR=`which brew`
+ BREW_DIR=${BREW_DIR/\/bin\/brew/}
+ echo "BREW_DIR:"${BREW_DIR}
+ find_cfg=`cat Makefile | grep 'SQLITE_DIR'`
+ if [ "$find_cfg" == "" ];then
+ LIB_SQLITE_DIR=`brew info sqlite | grep ${BREW_DIR}/Cellar/sqlite | cut -d \ -f 1 | awk 'END {print}'`
+ echo "LIB_SQLITE_DIR:"${LIB_SQLITE_DIR}
+ sed -i $BAK "s#\$(ROCKSPEC)#\$(ROCKSPEC) SQLITE_DIR=${LIB_SQLITE_DIR}#g" Makefile
+ fi
+ make
+ else
+ cd $serverPath/source/webstats/lsqlite3_v096 && make
+ fi
+ fi
+
+ # copy to code path
+ DEFAULT_DIR=$serverPath/webstats/luarocks/lib/lua/5.1
+ if [ -f ${DEFAULT_DIR}/lsqlite3.so ];then
+ mkdir -p $serverPath/webstats/lua
+ cp -rf ${DEFAULT_DIR}/lsqlite3.so $serverPath/webstats/lua/lsqlite3.so
+ fi
+
+ # https://github.com/P3TERX/GeoLite.mmdb
+ pip install geoip2 -i $PIPSRC
+ # if [ ! -f $serverPath/webstats/GeoLite2-City.mmdb ];then
+ # wget --no-check-certificate -O $serverPath/webstats/GeoLite2-City.mmdb https://github.com/P3TERX/GeoLite.mmdb/releases/download/2022.10.16/GeoLite2-City.mmdb
+ # fi
+
+ # 缓存数据
+ GEO_VERSION=$(get_latest_release "P3TERX/GeoLite.mmdb")
+ if [ ! -s $serverPath/source/webstats/GeoLite2-City.mmdb ];then
+ if [ "$LOCAL_ADDR" == "cn" ];then
+ wget --no-check-certificate -O $serverPath/source/webstats/GeoLite2-City.mmdb https://dl.midoks.icu/soft/webstats/GeoLite2-City.mmdb
+ else
+ wget --no-check-certificate -O $serverPath/source/webstats/GeoLite2-City.mmdb https://github.com/P3TERX/GeoLite.mmdb/releases/download/${GEO_VERSION}/GeoLite2-City.mmdb
+ fi
+ fi
+
+ if [ -s $serverPath/source/webstats/GeoLite2-City.mmdb ];then
+ cp -rf $serverPath/source/webstats/GeoLite2-City.mmdb $serverPath/webstats/GeoLite2-City.mmdb
+ fi
+
+ cd $rootPath && python3 plugins/webstats/index.py start
+ echo "${VERSION}" > $serverPath/webstats/version.pl
+
+ echo '网站统计安装完成'
+
+ # delete install data
+ if [ -d $serverPath/source/webstats/lsqlite3_v096 ];then
+ rm -rf $serverPath/source/webstats/lsqlite3_v096
+ fi
+ if [ -d $serverPath/source/webstats/luarocks-3.5.0 ];then
+ rm -rf $serverPath/source/webstats/luarocks-3.5.0
+ fi
+}
+
+Uninstall_App()
+{
+ cd $rootPath && python3 plugins/webstats/index.py stop
+ rm -rf $serverPath/webstats
+ echo "网站统计卸载完成"
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/webstats/js/setting.js b/plugins/webstats/js/setting.js
new file mode 100644
index 000000000..53350a607
--- /dev/null
+++ b/plugins/webstats/js/setting.js
@@ -0,0 +1,269 @@
+
+function wsGlobalSetting(){
+////////////////////////////////////////////////
+wsPost('get_global_conf', '' ,{}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var rdata = rdata.data;
+ var html = '\
+
\
+
统计:
\
+
\
+
\
+
IP统计
\
+
\
+
保存 \
+
? \
+
\
+
\
+
URI统计
\
+
\
+
保存 \
+
? \
+
\
+
\
+
\
+
\
+
\
+
日志:
\
+
\
+
\
+
日志保存天数
\
+
/天 \
+
保存 \
+
? \
+
\
+
\
+
\
+
\
+
\
+
监控配置:
\
+
\
+
\
+ CDN headers \
+ 排除扩展 \
+ 排除响应状态 \
+ 排除路径 \
+ 排除IP \
+ 记录请求原文 \
+
\
+
\
+ * 准确识别CDN网络IP地址,请注意大小写,如需多个请换行填写 \
+ \
+
\
+
\
+ 保存 \
+ 同步所有站点 \
+ ? \
+
\
+
\
+
\
+
';
+ $(".soft-man-con").html(html);
+ $('[data-toggle="tooltip"]').tooltip();
+
+ var common_tpl_tips = '* 准确识别CDN网络IP地址,请注意大小写,如需多个请换行填写 ';
+ var common_tpl_area = ' ';
+
+ $('#webstats .tab-con textarea').text(rdata['global']['cdn_headers'].join('\n'));
+ $('#webstats .tab-nav span').click(function(e){
+ $('#webstats .tab-nav span').removeClass('on');
+ $(this).addClass('on');
+ $('#webstats .tab-con').html('');
+
+ var typename = $(this).attr('data-type');
+ if (typename == 'cdn_headers'){
+ var content = $(common_tpl_tips).html('* 准确识别CDN网络IP地址,请注意大小写,如需多个请换行填写').prop('outerHTML');
+ var area = $(common_tpl_area).html(rdata['global']['cdn_headers'].join('\n')).prop('outerHTML');
+
+ content += area;
+ $('#webstats .tab-con').html(content);
+ } else if (typename == 'exclude_extension'){
+
+ var content = $(common_tpl_tips).html('* 排除的请求不写入网站日志,不统计PV、UV、IP,只累计总请求、总流量数,如需多个请换行填写').prop('outerHTML');
+ var area = $(common_tpl_area).html(rdata['global']['exclude_extension'].join('\n')).prop('outerHTML');
+ content += area;
+ $('#webstats .tab-con').html(content);
+ } else if (typename == 'exclude_status'){
+ var content = $(common_tpl_tips).html('* 排除的请求不写入网站日志,不统计PV、UV、IP,只累计总请求、总流量数,如需多个请换行填写').prop('outerHTML');
+ var area = $(common_tpl_area).html(rdata['global']['exclude_status'].join('\n')).prop('outerHTML');
+ content += area;
+ $('#webstats .tab-con').html(content);
+ } else if (typename == 'exclude_ip'){
+ var txt = '* 排除的IP不写入网站日志,不统计PV、UV、IP,只累计总请求、总流量数,如需多个请换行填写
\
+ * 支持 192.168.1.1-192.168.1.10格式排除区间IP
'
+ var content = $(common_tpl_tips).html(txt).prop('outerHTML');
+ var area = $(common_tpl_area).html(rdata['global']['exclude_ip'].join('\n')).prop('outerHTML');
+ content += area;
+ $('#webstats .tab-con').html(content);
+ } else if (typename == 'record_post_args'){
+ var txt = '记录请求原文说明:HTTP请求原文包括客户端请求详细参数,有助于分析或排查异常请求;
\
+ 考虑到HTTP请求原文会占用额外存储空间 ,默认仅记录500错误请求原文。
'
+ var content = $(common_tpl_tips).html(txt).prop('outerHTML');
+
+ var record_post_args = '';
+ if (rdata['global']['record_post_args']){
+ record_post_args = 'checked';
+ }
+ var record_get_403_args = '';
+ if (rdata['global']['record_get_403_args']){
+ record_get_403_args = 'checked';
+ }
+
+
+ var check = '\
+ \
+ 记录POST请求原文\
+ \
+ \
+ 记录403错误请求原文 \
+ \
+
';
+ content+=check;
+
+ $('#webstats .tab-con').html(content);
+ } else if ( typename == 'exclude_url'){
+ var txt = '* 排除的请求不写入网站日志,不统计PV、UV、IP,只累计总请求、总流量数'
+ var content = $(common_tpl_tips).html(txt).prop('outerHTML');
+
+ var _text = '';
+ var _tmp = rdata['global']['exclude_url'];
+ for(var i = 0; i<10; i++){
+ if(typeof _tmp[i] == 'undefined'){
+ _tmp[i] = {mode:'regular',url:''}
+ }
+
+ _text += '\
+ \
+ \
+ 完整匹配 \
+ 模糊匹配 \
+ \
+ \
+ \
+ ';
+ }
+
+ var list = '\
+
\
+ \
+ 排除方式 排除路径 \
+ \
+ '+_text+' \
+
\
+
';
+
+ content += list;
+ $('#webstats .tab-con').html(content);
+ }
+
+ });
+
+ $('#ip_top_num').click(function(){
+ var num = $('input[name="ip_top_num"]').val();
+ if(num == '' || num <= 0 || num > 2000) return layer.msg('请设置1-2000范围的统计数量',{icon:2});
+ wsPost('set_global_conf','',{ip_top_num:num}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2});
+ });
+ });
+
+ $('#uri_top_num').click(function(){
+ var num = $('input[name="uri_top_num"]').val();
+ if(num == '' || num <= 0 || num > 2000) return layer.msg('请设置1-2000范围的统计数量',{icon:2})
+ wsPost('set_global_conf','',{uri_top_num:num}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2});
+ });
+ });
+
+ $('#save_day').click(function(){
+ var num = $('input[name="save_day"]').val();
+ wsPost('set_global_conf','',{save_day:num}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2});
+ });
+ });
+
+ $('#submitSetting').click(function(){
+ var select = $('#webstats .tab-nav span');
+ var select_pos = 0;
+ $('#webstats .tab-nav span').each(function(i){
+ if ($(this).hasClass('on')){select_pos = i;}
+ });
+
+ if ( [0,1,2,4].indexOf(select_pos)>-1 ){
+ var setting_cdn = $('textarea[name="setting-cdn"]').val();
+
+ // var list = setting_cdn.split('\n')
+ var args = {}
+
+ if ( select_pos == 0 ){
+ args['cdn_headers'] = setting_cdn;
+ } else if ( select_pos == 1 ){
+ args['exclude_extension'] = setting_cdn;
+ } else if ( select_pos == 2 ){
+ args['exclude_status'] = setting_cdn;
+ } else if ( select_pos == 4 ){
+ args['exclude_ip'] = setting_cdn;
+ }
+
+ wsPost('set_global_conf','', args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2});
+ });
+ }
+
+ if (select_pos == 3 ){
+
+ var list = "";
+ for (var i = 0; i<10; i++) {
+ var tmp = "";
+ var url_type = $('select[name="url_type_'+i+'"]').val();
+ var url_val = $('input[name="url_val_'+i+'"]').val();
+
+ if (url_val != ""){
+ list += url_type +'|' + url_val +';';
+ }
+ }
+ wsPost('set_global_conf','', {"exclude_url":list}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2});
+ });
+ }
+
+ if (select_pos == 5){
+
+ var record_post_args = $('input[name="record_post_args"]').prop('checked');
+ var record_get_403_args = $('input[name="record_get_403_args"]').prop('checked');
+ wsPost('set_global_conf','', {"record_post_args":record_post_args,'record_get_403_args':record_get_403_args}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2});
+ });
+ }
+
+ wsGlobalSetting();
+ });
+
+
+ $('#setAll').click(function(){
+ var args = "name=webstats&func=reload";
+ layer.confirm('您真的要同步所有站点吗?', {icon:3,closeBtn: 1}, function() {
+ var e = layer.msg('正在同步,请稍候...', {icon: 16,time: 0});
+ $.post("/plugins/run", args, function(g) {
+ layer.close(e);
+ if( g.status && g.data != 'ok' ) {
+ layer.msg(g.data, {icon: 2,time: 3000,shade: 0.3,shadeClose: true});
+ } else {
+ layer.msg('同步成功!', {icon: 1,time: 0});
+ }
+ },'json').error(function() {
+ layer.close(e);
+ layer.msg('操作异常!', {icon: 1});
+ });
+ })
+ });
+
+
+});
+///////////////////////////////////////////////
+}
\ No newline at end of file
diff --git a/plugins/webstats/js/stats.js b/plugins/webstats/js/stats.js
new file mode 100644
index 000000000..ab5130ca1
--- /dev/null
+++ b/plugins/webstats/js/stats.js
@@ -0,0 +1,2604 @@
+function str2Obj(str){
+ var data = {};
+ kv = str.split('&');
+ for(i in kv){
+ v = kv[i].split('=');
+ data[v[0]] = v[1];
+ }
+ return data;
+}
+
+function wsOriginPost(method, version, args, callback){
+
+ var req_data = {};
+ req_data['name'] = 'webstats';
+ req_data['func'] = method;
+ req_data['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(str2Obj(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ if (!data.status){
+ //错误展示10S
+ layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
+ return;
+ }
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+
+function wsPost(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+ wsOriginPost(method, version, args,function(data){
+ layer.close(loadT);
+ callback(data);
+ });
+}
+
+
+
+function wsPostCallbak(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'webstats';
+ req_data['script']='webstats_index';
+ req_data['func'] = method;
+ args['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(str2Obj(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+function toSecond(val){
+ if (val>=1000){
+ val = (val / 1000).toFixed()+"s";
+ return val;
+ }
+ return val + "ms";
+}
+
+
+function makeHoursData(data, type="ip"){
+ var list = [];
+ var rlist = [];
+ for (var i = 0; i < 24; i++) {
+ if (i<10){
+ list.push("0"+i)
+ } else {
+ list.push(i+"")
+ }
+ rlist.push(i+"")
+ }
+
+ var rdata = {};
+ rdata['key'] = rlist;
+
+ var tmpdata = {};
+ for (var i = 0; i < data.length; i++) {
+ var tk = data[i]['time'];
+
+ var v = data[i];
+ if (type=='length'){
+ v['length'] = (v['length']/1024).toFixed();
+ }
+ tmpdata[tk] = v;
+ }
+
+ var val = [];
+ for (var i = 0; i < list.length; i++) {
+ var tk = list[i];
+
+ if (tmpdata[tk]){
+ val.push(tmpdata[tk][type]);
+ }else{
+ val.push(0);
+ }
+ }
+
+ rdata['value'] = val;
+ // console.log(rdata);
+ return rdata
+}
+
+function makeDayData(data, type="ip") {
+ var rdata = {};
+
+ var rdata_key = [];
+ var rdata_val = [];
+
+ for (var i = 0; i < data.length; i++) {
+ var tk = data[i]['time'];
+
+ rdata_key.push(tk);
+
+ var v = data[i][type];
+ if (type=='length'){
+ v = (v/1024).toFixed();
+ }
+ rdata_val.push(v)
+ }
+
+ rdata['key'] = rdata_key;
+ rdata['value'] = rdata_val;
+
+ return rdata
+}
+
+function getTime() {
+ var now = new Date();
+ var hour = now.getHours();
+ var minute = now.getMinutes();
+ var second = now.getSeconds();
+ if (minute < 10) {
+ minute = "0" + minute;
+ }
+ if (second < 10) {
+ second = "0" + second;
+ }
+ var nowdate = hour + ":" + minute + ":" + second;
+ return nowdate;
+}
+
+var ovTimer = null;
+function wsOverviewRequest(page){
+ clearInterval(ovTimer);
+
+ var args = {};
+
+ args['site'] = $('select[name="site"]').val();
+
+ var query_date = 'today';
+ if ($('#time_choose').attr("data-name") != ''){
+ query_date = $('#time_choose').attr("data-name");
+ } else {
+ query_date = $('#search_time button.cur').attr("data-name");
+ }
+ args['query_date'] = query_date;
+ args['order'] = $('#time_order button.cur').attr('data-name');
+
+ var select_option = $('.indicators-container input:checked').parent().attr('data-name');
+
+ // console.log($('.indicators-container input:checked').parent().find('span').text());
+ // console.log(select_option);
+
+ wsPost('get_overview_list', '' ,args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var list = '';
+ var data = rdata.data.data;
+ var statData = rdata.data.stat_list;
+
+ // console.log(statData, data);
+
+ var stat_pv = statData['pv'] == null?0:statData['pv'];
+ var stat_uv = statData['uv'] == null?0:statData['uv'];
+ var stat_ip = statData['ip'] == null?0:statData['ip'];
+ var stat_length = statData['length'] == null?0:statData['length'];
+ var stat_req = statData['req'] == null?0:statData['req'];
+
+ $('.overview_list .overview_box:eq(0) .ov_num').text(stat_pv);
+ $('.overview_list .overview_box:eq(1) .ov_num').text(stat_uv);
+ $('.overview_list .overview_box:eq(2) .ov_num').text(stat_ip);
+ $('.overview_list .overview_box:eq(3) .ov_num').text(toSize(stat_length));
+ $('.overview_list .overview_box:eq(4) .ov_num').text(stat_req);
+
+ var list = [];
+ for (var i = 0; i < data.length; i++) {
+ list.push(data[i][select_option]);
+ }
+
+ // console.log("list",list);
+ var chat = {};
+ var is_compare = false;
+
+ var tmpChatData = {
+ "key":[],
+ "value":[]
+ }
+
+ if (select_option == 'realtime_traffic' || select_option == 'realtime_request'){
+ } else {
+ if (args['order'] == 'hour'){
+ tmpChatData = makeHoursData(data, select_option);
+ } else {
+ tmpChatData = makeDayData(data, select_option);
+ }
+ }
+
+
+ chat['yAxis'] = [{
+ type: 'value',
+ splitNumber: 5,
+ axisLabel: {
+ textStyle: {
+ color: '#a8aab0',
+ fontStyle: 'normal',
+ fontFamily: '微软雅黑',
+ fontSize: 12,
+ },
+ },
+ axisLine:{
+ show: false
+ },
+ axisTick:{
+ show: false
+ },
+ splitLine: {
+ show: true,
+ lineStyle: {
+ color: '#E5E9ED'
+ }
+ }
+ }];
+
+ chat['tooltip'] = {
+ show:true,
+ trigger: 'axis',
+ backgroundColor: 'rgba(255,255,255,0.8)',
+ axisPointer: { // 坐标轴指示器,坐标轴触发有效
+ type: 'line', // 默认为直线,可选为:'line' | 'shadow'
+ lineStyle: {
+ color: 'rgba(150,150,150,0.2)'
+ }
+ },
+ textStyle: {
+ color: '#666',
+ fontSize: '14px',
+ },
+ extraCssText: 'width:220px;height:'+(is_compare?'30%':'22%')+';padding:0;box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);"',
+ formatter: function (params) {
+ var htmlStr = "";
+ for (var i = 0; i < params.length; i++) {
+ var tem = params[i].name;
+ var val = params[i].value;
+ if(args['order'] == 'hour'){
+ if (tem.indexOf('/') < 0) {
+ tem > 9 ? tem = tem + ":00 - " + tem + ":59" : tem = "0" + tem + ":00 - " +
+ "0" + tem + ":59";
+ }
+ val > 0 ? val = val : val = '--'
+ }
+
+ htmlStr +=
+ '' +
+ '' +
+ ' ' + params[i].seriesName + ' ' +
+ '' + val + ' ' + '
'
+ }
+ var res ='' +
+ tem + '' + (is_compare?trend_name:'') +
+ ' ' + htmlStr + '
'
+ return res;
+ }
+ }
+
+ var legendName = $('.indicators-container input:checked').parent().find('span').text()
+ chat['legendData'] = [legendName];
+
+ var statEc = echarts.init(document.getElementById('total_num_echart'));
+ var option = {
+ tooltip:chat['tooltip'],
+ backgroundColor:'#fff',
+ legend:{
+ data:chat['legendData'],
+ left:'center',
+ top:'94%',
+ },
+ grid: {
+ bottom: '9%',
+ containLabel: true,
+ x: 20,
+ y: 20,
+ x2: 20,
+ y2: 20
+ },
+ xAxis: {
+ type: 'category',
+ boundaryGap: false,
+ boundaryGap: false,
+ axisTick: {
+ alignWithLabel: true
+ },
+ data: tmpChatData["key"],
+ },
+ yAxis: chat['yAxis'],
+ graphic:[{
+ type: 'group',
+ right: 330,
+ top: 0,
+ z: 100,
+ children: [{
+ type: 'text',
+ left: 'center',
+ top: 'center',
+ z: 100,
+ style: {
+ fill: '#ccc',
+ text: args['site'],
+ font: '16px Arial'
+ }
+ }]
+ }],
+ series: [
+ {
+ name:legendName,
+ data: tmpChatData["value"],
+ type: 'line',
+ smooth: true,
+ itemStyle: {
+ normal: {
+ color:'#3A84FF',
+ lineStyle: {
+ color: "#3A84FF",
+ width:1,
+ },
+ areaStyle: {
+ color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [{
+ offset: 0,
+ color: 'rgba(58,132,255,0)'
+ }, {
+ offset: 1,
+ color: 'rgba(58,132,255,0.35)'
+ }]),
+ }
+ }
+ },
+ areaStyle: {}
+ }
+ ]
+ };
+
+ statEc.setOption(option);
+
+ if (select_option == 'realtime_traffic' || select_option == 'realtime_request'){
+
+ var xData = [];
+ var yData = [];
+ ovTimer = setInterval(function(){
+ var select_option = $('.indicators-container input:checked').parent().attr('data-name');
+ if (select_option != "realtime_traffic" && select_option != 'realtime_request' ){
+ clearInterval(ovTimer);
+ // console.log("get_logs_realtime_info over:"+select_option);
+ return;
+ }
+
+ var second = $('#check_realtime_second').val();
+
+ wsOriginPost("get_logs_realtime_info",'',{"site":args["site"], "type":select_option,'second':second} , function(rdata){
+
+ var rdata = $.parseJSON(rdata.data);
+
+ var realtime_traffic = rdata.data['realtime_traffic'];
+ var realtime_request = rdata.data['realtime_request'];
+
+ realtime_traffic_calc = toSize(realtime_traffic);
+
+
+ $('.overview_list .overview_box:eq(5) .ov_num').text(realtime_traffic_calc);
+ $('.overview_list .overview_box:eq(6) .ov_num').text(realtime_request);
+
+
+ var realtime_name = select_option == 'realtime_traffic' ? '实时流量':'每秒请求';
+ var val = realtime_request;
+ if (select_option == 'realtime_traffic'){
+ val = realtime_traffic_calc.split(' ')[0];
+ realtime_name = realtime_traffic_calc;
+ }
+
+ xData.push(getTime());
+ yData.push(val);
+
+ if (xData.length > 20){
+ xData.shift();
+ yData.shift();
+ }
+
+ statEc.setOption({
+ xAxis: {
+ data: xData
+ },
+ series: [{
+ name: realtime_name,
+ data: yData
+ }]
+ });
+ });
+ },2000);
+ }
+ });
+}
+
+
+function wsOverview(){
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+var randstr = getRandomString(10);
+
+var html = '\
+
\
+
网站: \
+
\
+ 未设置 \
+ \
+
时间: \
+
\
+
时段: \
+
\
+
\
+
\
+ \
+
\
+
\
+
\
+
\
+
\
+
\
+
\
+
\
+
\
+
\
+
\
+
';
+$(".soft-man-con").html(html);
+$('[data-toggle="tooltip"]').tooltip();
+//日期范围
+laydate.render({
+ elem: '#time_choose',
+ value:'',
+ range:true,
+ done:function(value, startDate, endDate){
+ if(!value){
+ return false;
+ }
+
+ $('#search_time button').each(function(){
+ $(this).removeClass('cur');
+ });
+
+ var timeA = value.split('-')
+ var start = $.trim(timeA[0]+'-'+timeA[1]+'-'+timeA[2])
+ var end = $.trim(timeA[3]+'-'+timeA[4]+'-'+timeA[5])
+ query_txt = toUnixTime(start + " 00:00:00") + "-"+ toUnixTime(end + " 00:00:00")
+
+ $('#time_choose').attr("data-name",query_txt);
+ $('#time_choose').addClass("cur");
+
+ wsOverviewRequest(1);
+ },
+});
+
+$('#ov_refresh').click(function(){
+ wsOverviewRequest(1);
+});
+
+$('#time_order button:eq(0)').addClass('cur');
+$('#time_order button').click(function(){
+ $('#time_order button').each(function(){
+ if ($(this).hasClass('cur')){
+ $(this).removeClass('cur');
+ }
+ });
+ $(this).addClass('cur');
+ wsOverviewRequest(1);
+});
+
+
+
+$('#search_time button:eq(0)').addClass('cur');
+$('#search_time button').click(function(){
+ $('#search_time button').each(function(){
+ if ($(this).hasClass('cur')){
+ $(this).removeClass('cur');
+ }
+ });
+ $('#time_choose').attr("data-name",'');
+ $('#time_choose').removeClass("cur");
+
+ $(this).addClass('cur');
+
+ wsOverviewRequest(1);
+});
+
+
+function initRealtimeTraffic(){
+ var check_realtime_second = $('#check_realtime_second').val();
+ if (check_realtime_second<1){
+ check_realtime_second = 1;
+ $('#check_realtime_second').val(check_realtime_second);
+ }
+
+ if (check_realtime_second>10){
+ check_realtime_second = 10;
+ $('#check_realtime_second').val(check_realtime_second);
+ }
+ var title = "每秒请求";
+ if (check_realtime_second > 1){
+ title = '每'+check_realtime_second+'秒请求'
+ }
+
+ $('#ov_title_req_second').text(title)
+ $('.check_realtime_request').text(title);
+}
+
+
+initRealtimeTraffic();
+$('#check_realtime_second').change(function(){
+ initRealtimeTraffic();
+});
+
+$('.indicators-container input[type=radio]').click(function(){
+ $('.indicators-container input[type=radio]').each(function(){
+ $(this).removeAttr('checked');
+ });
+ $(this).prop({'checked':true});
+
+ wsOverviewRequest(1);
+});
+
+wsPost('get_default_site','',{},function(rdata){
+ $('select[name="site"]').html('');
+
+ var rdata = $.parseJSON(rdata.data);
+ var rdata = rdata.data;
+ var default_site = rdata["default"];
+ var select = '';
+ for (var i = 0; i < rdata["list"].length; i++) {
+ if (default_site == rdata["list"][i]){
+ select += ''+rdata["list"][i]+' ';
+ } else{
+ select += ''+rdata["list"][i]+' ';
+ }
+ }
+ $('select[name="site"]').html(select);
+ wsOverviewRequest(1);
+
+ $('select[name="site"]').change(function(){
+ wsOverviewRequest(1);
+ });
+});
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+}
+
+
+function wsSitesListRequest(page){
+
+ var args = {};
+ var query_date = 'today';
+ if ($('#time_choose').attr("data-name") != ''){
+ query_date = $('#time_choose').attr("data-name");
+ } else {
+ query_date = $('#search_time button.cur').attr("data-name");
+ }
+ args['query_date'] = query_date;
+
+
+ wsPost('get_site_list', '' ,args, function(rdata){
+
+ var rdata = $.parseJSON(rdata.data);
+ var data = rdata.data;
+
+
+ var stat_pv = 0;
+ var stat_uv = 0;
+ var stat_ip = 0;
+ var stat_length = 0;
+ var stat_req = 0;
+
+ // console.log(rdata, data);
+ var list = '';
+ if (data.length > 0){
+ for(i in data){
+
+ var tmp_pv = 0;
+ var tmp_uv = 0;
+ var tmp_ip = 0;
+ var tmp_length = 0;
+ var tmp_req = 0;
+
+ if (data[i]['pv'] != null){
+ tmp_pv = data[i]['pv'];
+ stat_pv += data[i]['pv'];
+ }
+
+ if (data[i]['uv'] != null){
+ tmp_uv = data[i]['uv'];
+ stat_uv += data[i]['uv'];
+ }
+
+ if (data[i]['ip'] != null){
+ tmp_ip = data[i]['ip'];
+ stat_ip += data[i]['ip'];
+ }
+
+ if (data[i]['length'] != null){
+ tmp_length = data[i]['length'];
+ stat_length += data[i]['length'];
+ }
+
+ if (data[i]['req'] != null){
+ tmp_req = data[i]['req'];
+ stat_req += data[i]['req'];
+ }
+
+ list += '';
+ list += '' + data[i]['site']+' ';
+ list += '' + tmp_pv +' ';
+ list += '' + tmp_uv +' ';
+ list += '' + tmp_ip +' ';
+ list += '' + tmp_req +' ';
+ list += '' + toSize(tmp_length) +' ';
+ list += '设置 ';
+ list += ' ';
+ }
+ } else{
+ list += '网站列表为空 ';
+ }
+
+ $('.overview_list .overview_box:eq(0) .ov_num').text(stat_pv);
+ $('.overview_list .overview_box:eq(1) .ov_num').text(stat_uv);
+ $('.overview_list .overview_box:eq(2) .ov_num').text(stat_ip);
+ $('.overview_list .overview_box:eq(3) .ov_num').text(toSize(stat_length));
+ $('.overview_list .overview_box:eq(4) .ov_num').text(stat_req);
+
+ var table = '\
+
';
+ $('#ws_table').html(table);
+
+
+ $(".tablescroll .web_set").click(function(){
+ var index = $(this).attr('data-id');
+
+ var domain = data[index]["site"];
+ wsPost('get_site_conf', '' ,{"site":domain}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var rdata = rdata.data;
+ console.log(rdata);
+ layer.open({
+ type: 1,
+ title: "【"+domain + "】监控配置",
+ btn: ['保存','取消'],
+ area: ['600px',"380px"],
+ closeBtn: 1,
+ shadeClose: false,
+ content: '\
+
\
+
\
+ CDN headers \
+ 排除扩展 \
+ 排除响应状态 \
+ 排除路径 \
+ 排除IP \
+ 记录请求原文 \
+
\
+
\
+ * 准确识别CDN网络IP地址,请注意大小写,如需多个请换行填写 \
+ \
+
\
+
\
+
',
+ success:function(){
+ var common_tpl_tips = '* 准确识别CDN网络IP地址,请注意大小写,如需多个请换行填写 ';
+ var common_tpl_area = ' ';
+
+
+ $('#site_conf .tab-con textarea').text(rdata['cdn_headers'].join('\n'));
+ $('#site_conf .tab-nav span').click(function(e){
+ $('#site_conf .tab-nav span').removeClass('on');
+ $(this).addClass('on');
+ $('#site_conf .tab-con').html('');
+
+ var typename = $(this).attr('data-type');
+ if (typename == 'cdn_headers'){
+ var content = $(common_tpl_tips).html('* 准确识别CDN网络IP地址,请注意大小写,如需多个请换行填写').prop('outerHTML');
+ var area = $(common_tpl_area).html(rdata['cdn_headers'].join('\n')).prop('outerHTML');
+
+ content += area;
+ $('#site_conf .tab-con').html(content);
+ } else if (typename == 'exclude_extension'){
+
+ var content = $(common_tpl_tips).html('* 排除的请求不写入网站日志,不统计PV、UV、IP,只累计总请求、总流量数,如需多个请换行填写').prop('outerHTML');
+ var area = $(common_tpl_area).html(rdata['exclude_extension'].join('\n')).prop('outerHTML');
+ content += area;
+ $('#site_conf .tab-con').html(content);
+ } else if (typename == 'exclude_status'){
+ var content = $(common_tpl_tips).html('* 排除的请求不写入网站日志,不统计PV、UV、IP,只累计总请求、总流量数,如需多个请换行填写').prop('outerHTML');
+ var area = $(common_tpl_area).html(rdata['exclude_status'].join('\n')).prop('outerHTML');
+ content += area;
+ $('#site_conf .tab-con').html(content);
+ } else if (typename == 'exclude_ip'){
+ var txt = '* 排除的IP不写入网站日志,不统计PV、UV、IP,只累计总请求、总流量数,如需多个请换行填写
\
+ * 支持 192.168.1.1-192.168.1.10格式排除区间IP
'
+ var content = $(common_tpl_tips).html(txt).prop('outerHTML');
+ var area = $(common_tpl_area).html(rdata['exclude_ip'].join('\n')).prop('outerHTML');
+ content += area;
+ $('#site_conf .tab-con').html(content);
+ } else if (typename == 'record_post_args'){
+ var txt = '记录请求原文说明:HTTP请求原文包括客户端请求详细参数,有助于分析或排查异常请求;
\
+ 考虑到HTTP请求原文会占用额外存储空间 ,默认仅记录500错误请求原文。
'
+ var content = $(common_tpl_tips).html(txt).prop('outerHTML');
+
+ var record_post_args = '';
+ if (rdata['record_post_args']){
+ record_post_args = 'checked';
+ }
+ var record_get_403_args = '';
+ if (rdata['record_get_403_args']){
+ record_get_403_args = 'checked';
+ }
+
+
+ var check = '\
+ \
+ 记录POST请求原文\
+ \
+ \
+ 记录403错误请求原文 \
+ \
+
';
+ content+=check;
+
+ $('#site_conf .tab-con').html(content);
+ } else if ( typename == 'exclude_url'){
+ var txt = '* 排除的请求不写入网站日志,不统计PV、UV、IP,只累计总请求、总流量数'
+ var content = $(common_tpl_tips).html(txt).prop('outerHTML');
+
+ var _text = '';
+ var _tmp = rdata['exclude_url'];
+ for(var i = 0; i<10; i++){
+ if(typeof _tmp[i] == 'undefined'){
+ _tmp[i] = {mode:'regular',url:''}
+ }
+
+ _text += '\
+ \
+ \
+ 完整匹配 \
+ 模糊匹配 \
+ \
+ \
+ \
+ ';
+ }
+
+ var list = '\
+
\
+ \
+ 排除方式 排除路径 \
+ \
+ '+_text+' \
+
\
+
';
+
+ content += list;
+ $('#site_conf .tab-con').html(content);
+ }
+ });
+ },
+ yes:function(){
+ var select_pos = 0;
+ $('#site_conf .tab-nav span').each(function(i){
+ if ($(this).hasClass('on')){select_pos = i;}
+ });
+ var args = {"site":domain};
+ if ( [0,1,2,4].indexOf(select_pos)>-1 ){
+ var setting_cdn = $('textarea[name="setting-cdn"]').val();
+ // var list = setting_cdn.split('\n')
+ if ( select_pos == 0 ){
+ args['cdn_headers'] = setting_cdn;
+ } else if ( select_pos == 1 ){
+ args['exclude_extension'] = setting_cdn;
+ } else if ( select_pos == 2 ){
+ args['exclude_status'] = setting_cdn;
+ } else if ( select_pos == 4 ){
+ args['exclude_ip'] = setting_cdn;
+ }
+
+ wsPost('set_site_conf','', args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2});
+ });
+ }
+
+ if (select_pos == 3 ){
+
+ var list = "";
+ for (var i = 0; i<10; i++) {
+ var tmp = "";
+ var url_type = $('select[name="url_type_'+i+'"]').val();
+ var url_val = $('input[name="url_val_'+i+'"]').val();
+
+ if (url_val != ""){
+ list += url_type +'|' + url_val +';';
+ }
+ }
+ args['exclude_url'] = list;
+ wsPost('set_site_conf','', args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2});
+ });
+ }
+
+ if (select_pos == 5){
+ var record_post_args = $('input[name="record_post_args"]').prop('checked');
+ var record_get_403_args = $('input[name="record_get_403_args"]').prop('checked');
+ args["record_post_args"] = record_post_args;
+ args['record_get_403_args'] = record_get_403_args;
+ wsPost('set_site_conf','', args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ layer.msg(rdata.msg,{icon:rdata.status?1:2});
+ });
+ }
+ },
+ });
+ });
+ });
+ });
+}
+
+
+function wsSitesList(){
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+var randstr = getRandomString(10);
+
+var html = '';
+$(".soft-man-con").html(html);
+$('[data-toggle="tooltip"]').tooltip();
+//日期范围
+laydate.render({
+ elem: '#time_choose',
+ value:'',
+ range:true,
+ done:function(value, startDate, endDate){
+ if(!value){
+ return false;
+ }
+
+ $('#search_time button').each(function(){
+ $(this).removeClass('cur');
+ });
+
+ var timeA = value.split('-')
+ var start = $.trim(timeA[0]+'-'+timeA[1]+'-'+timeA[2])
+ var end = $.trim(timeA[3]+'-'+timeA[4]+'-'+timeA[5])
+ query_txt = toUnixTime(start + " 00:00:00") + "-"+ toUnixTime(end + " 00:00:00")
+
+ $('#time_choose').attr("data-name",query_txt);
+ $('#time_choose').addClass("cur");
+
+ wsSitesListRequest(1);
+ },
+});
+
+
+$('#time_order button:eq(0)').addClass('cur');
+$('#time_order button').click(function(){
+ $('#time_order button').each(function(){
+ if ($(this).hasClass('cur')){
+ $(this).removeClass('cur');
+ }
+ });
+ $(this).addClass('cur');
+ wsSitesListRequest(1);
+});
+
+
+
+$('#search_time button:eq(0)').addClass('cur');
+$('#search_time button').click(function(){
+ $('#search_time button').each(function(){
+ if ($(this).hasClass('cur')){
+ $(this).removeClass('cur');
+ }
+ });
+ $('#time_choose').attr("data-name",'');
+ $('#time_choose').removeClass("cur");
+
+ $(this).addClass('cur');
+
+ wsSitesListRequest(1);
+});
+
+wsSitesListRequest(1);
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+}
+
+
+function wsSpiderStatLogRequest(page){
+
+ var args = {};
+ args['page'] = page;
+ args['page_size'] = 10;
+
+ args['site'] = $('select[name="site"]').val();
+ args['status_code'] = $('select[name="status_code"]').val();
+
+ var query_date = 'today';
+ if ($('#time_choose').attr("data-name") != ''){
+ query_date = $('#time_choose').attr("data-name");
+ } else {
+ query_date = $('#search_time button.cur').attr("data-name");
+ }
+ args['query_date'] = query_date;
+
+ args['tojs'] = 'wsSpiderStatLogRequest';
+ wsPost('get_spider_stat_list', '' ,args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var list = '';
+ var data = rdata.data.data;
+ if (data.length > 0){
+ for(i in data){
+ list += '';
+ list += '' + data[i]['time']+' ';
+ list += '' + data[i]['baidu'] +' ';
+ list += '' + data[i]['bing'] +' ';
+ list += '' + data[i]['qh360'] +' ';
+ list += '' + data[i]['google'] +' ';
+ list += '' + data[i]['bytes'] +' ';
+ list += '' + data[i]['sogou'] +' ';
+ list += '' + data[i]['soso'] +' ';
+ list += '' + data[i]['youdao'] +' ';
+ list += '' + data[i]['youdao'] +' ';
+ list += '' + data[i]['dnspod'] +' ';
+ list += '' + data[i]['yandex'] +' ';
+ list += '' + data[i]['other'] +' ';
+ list += '' + data[i]['other'] +' ';
+ list += ' ';
+ }
+ } else{
+ list += '蜘蛛列表为空 ';
+ }
+
+ var table = '\
+
';
+ $('#ws_table').html(table);
+ $('#wsPage').html(rdata.data.page);
+ $('[data-toggle="tooltip"]').tooltip();
+
+ var sumData = rdata.data.sum_data;
+
+ var percent = ((sumData.spider/sumData.reqest_total)*100).toFixed();
+
+ $('#spider_left_total .request_spider').text(sumData.spider+"("+percent+"%)");
+ $('#spider_left_total .request_total').text(sumData.reqest_total);
+
+ // 图形化
+ var initData = rdata.data.stat_list;
+
+ var colorList = ['#6ec71e','#4885FF'];
+ var source_name = {baidu:'百度',google:'Google',bytes:'头条',soso:'搜搜',bing:'必应',qh360:'奇虎360',youdao:'有道',yandex:'Yandex',dnspod:'DNSPOD',mpcrawler:'mpcrawler',other:'其他',};
+ var lenend2_obj = {};
+
+ var rightEc = echarts.init(document.getElementById('echart_right_total'));
+
+ var xAxixName = $('#search_time button.cur').text();
+ var is_compare = false;
+
+ var lenend = [];
+ var serData = [];
+ for(var i = 0;i (is_compare?2:4)) {
+ lenend2_obj[lenend[i]] = false;
+ } else {
+ lenend2_obj[lenend[i]] = true;
+ }
+ }
+
+ var rightOption = {
+ backgroundColor:'#fff',
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: {
+ type: 'shadow' ,
+ textStyle: {
+ color: '#fff',
+ fontSize: '26'
+ },
+ }
+ },
+ legend: {
+ top:'0%',
+ data: lenend,
+ selected:lenend2_obj,
+ textStyle:{
+ fontSize:12,
+ color:'#808080'
+ },
+ icon:'rect'
+ },
+ grid: {
+ top:60,
+ left:60,
+ right:0,
+ bottom:50
+ },
+ xAxis: [{
+ type: 'category',
+ axisLabel:{
+ color:'#4D4D4D',
+ fontSize:14,
+ fontWeight:'bold'
+ },
+ data: [xAxixName],
+ }],
+ color:['#4fa8f9', '#6ec71e', '#f56e6a', '#fc8b40', '#818af8', '#31c9d7', '#f35e7a', '#ab7aee',
+ '#14d68b', '#cde5ff'],
+ yAxis: [{
+ type: 'value',
+ axisLine: {
+ show: false,
+ },
+ axisTick: {
+ show: false
+ },
+ splitNumber:4, //y轴分割线数量
+ axisLabel:{
+ color:'#8C8C8C'
+ },
+ splitLine:{
+ lineStyle:{
+ type:'dashed'
+ }
+ }
+ }],
+ series: serData
+ }
+
+
+ rightEc.setOption(rightOption);
+
+ var oop = lenend.slice(0, (is_compare?3:5));
+ rightEc.on('legendselectchanged', function (params) {
+ var legend_option = this.getOption(),newAxisName = [];
+ $.each(legend_option['xAxis'][0]['data'],function(index,item){
+ newAxisName.push(item.replace(/\([^\)]*\)/g,""))
+ })
+ legend_option['xAxis'][0]['data'] = newAxisName;
+
+ var num = 0;
+ for(var e in params.selected){
+ if(params.selected.hasOwnProperty(e)){
+ params.selected[e]? num++ : '';
+ }
+ }
+ if(num > (is_compare?3:5)){
+ oop.push(params.name)
+ }
+ if (num > (is_compare?3:5)) {
+ var hah = oop.slice(oop.length - (is_compare?4:6), oop.length - (is_compare?3:4))[0] + '';
+ legend_option.legend[0].selected[hah] = false;
+ }
+ if (num < 1){
+ legend_option.legend[0].selected[params.name] = true;
+ }
+ this.setOption(legend_option);
+ });
+ });
+}
+
+
+function wsSpiderStat(){
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+var randstr = getRandomString(10);
+
+var html = '\
+
\
+
网站: \
+
\
+ 未设置 \
+ \
+
时间: \
+
\
+
\
+
\
+
\
+
';
+$(".soft-man-con").html(html);
+
+//日期范围
+laydate.render({
+ elem: '#time_choose',
+ value:'',
+ range:true,
+ done:function(value, startDate, endDate){
+ if(!value){
+ return false;
+ }
+
+ $('#search_time button').each(function(){
+ $(this).removeClass('cur');
+ });
+
+ var timeA = value.split('-')
+ var start = $.trim(timeA[0]+'-'+timeA[1]+'-'+timeA[2])
+ var end = $.trim(timeA[3]+'-'+timeA[4]+'-'+timeA[5])
+ query_txt = toUnixTime(start + " 00:00:00") + "-"+ toUnixTime(end + " 00:00:00")
+
+ $('#time_choose').attr("data-name",query_txt);
+ $('#time_choose').addClass("cur");
+
+ wsSpiderStatLogRequest(1);
+ },
+});
+
+$('#search_time button:eq(0)').addClass('cur');
+$('#search_time button').click(function(){
+ $('#search_time button').each(function(){
+ if ($(this).hasClass('cur')){
+ $(this).removeClass('cur');
+ }
+ });
+ $('#time_choose').attr("data-name",'');
+ $('#time_choose').removeClass("cur");
+
+ $(this).addClass('cur');
+
+ wsSpiderStatLogRequest(1);
+});
+
+
+$('select[name="status_code"]').change(function(){
+ wsSpiderStatLogRequest(1);
+});
+
+wsPost('get_default_site','',{},function(rdata){
+ $('select[name="site"]').html('');
+
+ var rdata = $.parseJSON(rdata.data);
+ var rdata = rdata.data;
+ var default_site = rdata["default"];
+ var select = '';
+ for (var i = 0; i < rdata["list"].length; i++) {
+ if (default_site == rdata["list"][i]){
+ select += ''+rdata["list"][i]+' ';
+ } else{
+ select += ''+rdata["list"][i]+' ';
+ }
+ }
+ $('select[name="site"]').html(select);
+ wsSpiderStatLogRequest(1);
+
+ $('select[name="site"]').change(function(){
+ wsSpiderStatLogRequest(1);
+ });
+});
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+}
+
+
+function wsClientStatLogRequest(page){
+
+ var args = {};
+ args['page'] = page;
+ args['page_size'] = 10;
+
+ args['site'] = $('select[name="site"]').val();
+ args['status_code'] = $('select[name="status_code"]').val();
+
+ var query_date = 'today';
+ if ($('#time_choose').attr("data-name") != ''){
+ query_date = $('#time_choose').attr("data-name");
+ } else {
+ query_date = $('#search_time button.cur').attr("data-name");
+ }
+ args['query_date'] = query_date;
+
+ args['tojs'] = 'wsClientStatLogRequest';
+ wsPost('get_client_stat_list', '' ,args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var list = '';
+ var data = rdata.data.data;
+ if (data.length > 0){
+ for(i in data){
+ list += '';
+ list += '' + data[i]['time']+' ';
+ list += '' + data[i]['android'] +' ';
+ list += '' + data[i]['iphone'] +' ';
+ list += '' + data[i]['windows'] +' ';
+ list += '' + data[i]['chrome'] +' ';
+ list += '' + data[i]['weixin'] +' ';
+ list += '' + data[i]['qh360'] +' ';
+ list += '' + data[i]['edeg'] +' ';
+ list += '' + data[i]['firefox'] +' ';
+ list += '' + data[i]['safari'] +' ';
+ list += '' + data[i]['mac'] +' ';
+ list += '' + data[i]['msie'] +' ';
+ list += '' + data[i]['machine'] +' ';
+ list += '' + data[i]['other'] +' ';
+ list += ' ';
+ }
+ } else{
+ list += '客服端列表为空 ';
+ }
+
+ var table = '\
+
';
+ $('#ws_table').html(table);
+ $('#wsPage').html(rdata.data.page);
+ $('[data-toggle="tooltip"]').tooltip();
+
+
+ // 图形化
+ var initData = rdata.data.stat_list;
+ var sumData = rdata.data.sum_data;
+ var colorList = ['#6ec71e','#4885FF'];
+ var source_name = {android:'安卓',iphone:'iOS',windows:'Windows',chrome:'Chrome',weixin:'微信',qh360:'360',edeg:'Edge',firefox:'火狐',safari:'Safari',mac:'Mac',linux:'Linux',msie:'IE',metasr:'搜狗',theworld:'世界之窗',tt:'腾讯TT',maxthon:'遨游',opera:'Opera',qq:'QQ浏览器',uc:'UC',pc2345:'2345',other:'其他',machine:'Machine'};
+ var lenend2_obj = {};
+
+ var leftEc = echarts.init(document.getElementById('echart_left_total'));
+ var rightEc = echarts.init(document.getElementById('echart_right_total'));
+
+
+ var datas = [
+ { value: sumData.pc, name: 'PC客服端' },
+ { value: sumData.mobile, name: '移动客服端' },
+ ];
+
+ var leftOption = {
+ backgroundColor:'#fff',
+ title: {
+ text: sumData.reqest_total,
+ textStyle: {
+ color: '#484848',
+ fontSize: 17
+ },
+ subtext: '总请求数',
+ subtextStyle: {
+ color: '#717171',
+ fontSize: 15
+ },
+ itemGap: 20,
+ left: 'center',
+ top: '42%'
+ },
+ tooltip: {
+ trigger: 'item'
+ },
+ series: [{
+ type: 'pie',
+ radius: ['45%', '55%'],
+ center: ["50%", "50%"],
+ clockwise: true,
+ avoidLabelOverlap: false,
+ hoverOffset: 15,
+ itemStyle: {
+ normal: {
+ label: {
+ show: true,
+ position: 'outside',
+ color: '#666',
+ formatter: function(params) {
+ var percent = 0;
+ var total = 0;
+ for (var i = 0; i < datas.length; i++) {
+ total += datas[i].value;
+ }
+ if(params.name !== '') {
+ return params.name + '\n' + '\n' + params.value + '/次';
+ }else {
+ return '';
+ }
+ },
+ },
+ labelLine: {
+ length: 20,
+ length2: 10
+ },
+ color: function(params) {
+ return colorList[params.dataIndex]
+ }
+ }
+ },
+ data: datas
+ },{
+ itemStyle: {
+ normal: {
+ color: '#F5F6FA',
+ }
+ },
+ type: 'pie',
+ hoverAnimation: false,
+ radius: ['42%', '58%'],
+ center: ["50%", "50%"],
+ label: {
+ normal: {
+ show:false,
+ }
+ },
+ data: [],
+ z:-1
+ }]
+ }
+ leftEc.setOption(leftOption);
+
+ var xAxixName = $('#search_time button.cur').text();
+ var is_compare = false;
+
+ var lenend = [];
+ var serData = [];
+ for(var i = 0;i (is_compare?2:4)) {
+ lenend2_obj[lenend[i]] = false;
+ } else {
+ lenend2_obj[lenend[i]] = true;
+ }
+ }
+
+ var rightOption = {
+ backgroundColor:'#fff',
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: {
+ type: 'shadow' ,
+ textStyle: {
+ color: '#fff',
+ fontSize: '26'
+ },
+ }
+ },
+ legend: {
+ top:'0%',
+ data: lenend,
+ selected:lenend2_obj,
+ textStyle:{
+ fontSize:12,
+ color:'#808080'
+ },
+ icon:'rect'
+ },
+ grid: {
+ top:60,
+ left:60,
+ right:0,
+ bottom:50
+ },
+ xAxis: [{
+ type: 'category',
+ axisLabel:{
+ color:'#4D4D4D',
+ fontSize:14,
+ fontWeight:'bold'
+ },
+ data: [xAxixName],
+ }],
+ color:['#4fa8f9', '#6ec71e', '#f56e6a', '#fc8b40', '#818af8', '#31c9d7', '#f35e7a', '#ab7aee',
+ '#14d68b', '#cde5ff'],
+ yAxis: [{
+ type: 'value',
+ axisLine: {
+ show: false,
+ },
+ axisTick: {
+ show: false
+ },
+ splitNumber:4, //y轴分割线数量
+ axisLabel:{
+ color:'#8C8C8C'
+ },
+ splitLine:{
+ lineStyle:{
+ type:'dashed'
+ }
+ }
+ }],
+ series: serData
+ }
+
+
+ rightEc.setOption(rightOption);
+
+ var oop = lenend.slice(0, (is_compare?3:5));
+ rightEc.on('legendselectchanged', function (params) {
+ var legend_option = this.getOption(),newAxisName = [];
+ $.each(legend_option['xAxis'][0]['data'],function(index,item){
+ newAxisName.push(item.replace(/\([^\)]*\)/g,""))
+ })
+ legend_option['xAxis'][0]['data'] = newAxisName;
+
+ var num = 0;
+ for(var e in params.selected){
+ if(params.selected.hasOwnProperty(e)){
+ params.selected[e]? num++ : '';
+ }
+ }
+ if(num > (is_compare?3:5)){
+ oop.push(params.name)
+ }
+ if (num > (is_compare?3:5)) {
+ var hah = oop.slice(oop.length - (is_compare?4:6), oop.length - (is_compare?3:4))[0] + '';
+ legend_option.legend[0].selected[hah] = false;
+ }
+ if (num < 1){
+ legend_option.legend[0].selected[params.name] = true;
+ }
+ this.setOption(legend_option)
+ });
+ });
+}
+
+
+function wsClientStat(){
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+var randstr = getRandomString(10);
+
+var html = '\
+
\
+
网站: \
+
\
+ 未设置 \
+ \
+
时间: \
+
\
+
\
+
\
+
\
+
';
+$(".soft-man-con").html(html);
+
+//日期范围
+laydate.render({
+ elem: '#time_choose',
+ value:'',
+ range:true,
+ done:function(value, startDate, endDate){
+ if(!value){
+ return false;
+ }
+
+ $('#search_time button').each(function(){
+ $(this).removeClass('cur');
+ });
+
+ var timeA = value.split('-')
+ var start = $.trim(timeA[0]+'-'+timeA[1]+'-'+timeA[2])
+ var end = $.trim(timeA[3]+'-'+timeA[4]+'-'+timeA[5])
+ query_txt = toUnixTime(start + " 00:00:00") + "-"+ toUnixTime(end + " 00:00:00")
+
+ $('#time_choose').attr("data-name",query_txt);
+ $('#time_choose').addClass("cur");
+
+ wsClientStatLogRequest(1);
+ },
+});
+
+$('#search_time button:eq(0)').addClass('cur');
+$('#search_time button').click(function(){
+ $('#search_time button').each(function(){
+ if ($(this).hasClass('cur')){
+ $(this).removeClass('cur');
+ }
+ });
+ $('#time_choose').attr("data-name",'');
+ $('#time_choose').removeClass("cur");
+
+ $(this).addClass('cur');
+
+ wsClientStatLogRequest(1);
+});
+
+
+$('select[name="status_code"]').change(function(){
+ wsClientStatLogRequest(1);
+});
+
+wsPost('get_default_site','',{},function(rdata){
+ $('select[name="site"]').html('');
+
+ var rdata = $.parseJSON(rdata.data);
+ var rdata = rdata.data;
+ var default_site = rdata["default"];
+ var select = '';
+ for (var i = 0; i < rdata["list"].length; i++) {
+ if (default_site == rdata["list"][i]){
+ select += ''+rdata["list"][i]+' ';
+ } else{
+ select += ''+rdata["list"][i]+' ';
+ }
+ }
+ $('select[name="site"]').html(select);
+ wsClientStatLogRequest(1);
+
+ $('select[name="site"]').change(function(){
+ wsClientStatLogRequest(1);
+ });
+});
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+}
+
+
+
+function wsIpStatLogRequest(page){
+
+ var args = {}
+ args['site'] = $('select[name="site"]').val();
+ var query_date = 'today';
+ query_date = $('#search_time button.cur').attr("data-name");
+ args['query_date'] = query_date;
+
+ args['tojs'] = 'wsIpStatLogRequest';
+ wsPost('get_ip_stat_list', '' ,args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var list = '';
+ var data = rdata.data;
+ // console.log(rdata,data);
+ if (data.length > 0){
+ for(i in data){
+ list += '';
+ list += '' + (parseInt(i)+1)+' ';
+ list += '' + data[i]['ip']+' ';
+ list += '' + data[i]['area'] +' ';
+ list += '' + data[i]['day'] +'('+data[i]['day_rate']+'%) ';
+ list += '' + toSize(data[i]['flow']) +'('+data[i]['flow_rate']+'%) ';
+ list += '
' +' ';
+ list += ' ';
+ }
+
+
+ } else{
+ list += 'IP列表为空 ';
+ }
+
+ var table = '\
+
';
+ $('#ws_table').html(table);
+ });
+}
+
+
+function wsIpStat(){
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+var randstr = getRandomString(10);
+
+var html = '\
+
\
+
网站: \
+
\
+ 未设置 \
+ \
+
时间: \
+
\
+
\
+
\
+
';
+$(".soft-man-con").html(html);
+
+
+$('#search_time button:eq(0)').addClass('cur');
+$('#search_time button').click(function(){
+ $('#search_time button').each(function(){
+ if ($(this).hasClass('cur')){
+ $(this).removeClass('cur');
+ }
+ });
+ $('#time_choose').attr("data-name",'');
+ $('#time_choose').removeClass("cur");
+
+ $(this).addClass('cur');
+
+ wsIpStatLogRequest(1);
+});
+
+wsPost('get_default_site','',{},function(rdata){
+ $('select[name="site"]').html('');
+
+ var rdata = $.parseJSON(rdata.data);
+ var rdata = rdata.data;
+ var default_site = rdata["default"];
+ var select = '';
+ for (var i = 0; i < rdata["list"].length; i++) {
+ if (default_site == rdata["list"][i]){
+ select += ''+rdata["list"][i]+' ';
+ } else{
+ select += ''+rdata["list"][i]+' ';
+ }
+ }
+ $('select[name="site"]').html(select);
+ wsIpStatLogRequest(1);
+
+ $('select[name="site"]').change(function(){
+ wsIpStatLogRequest(1);
+ });
+});
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+}
+
+
+function wsUriStatLogRequest(page){
+
+ var args = {}
+ args['site'] = $('select[name="site"]').val();
+ var query_date = 'today';
+ query_date = $('#search_time button.cur').attr("data-name");
+ args['query_date'] = query_date;
+
+ args['tojs'] = 'wsUriStatLogRequest';
+ wsPost('get_uri_stat_list', '' ,args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var list = '';
+ var data = rdata.data;
+ // console.log(rdata,data);
+ if (data.length > 0){
+ for(i in data){
+ list += '';
+ list += '' + (parseInt(i)+1)+' ';
+ list += '' + data[i]['uri']+' ';
+ list += '' + data[i]['day'] +'('+data[i]['day_rate']+'%) ';
+ list += '' + toSize(data[i]['flow']) +'('+data[i]['flow_rate']+'%) ';
+ list += '
' +' ';
+ list += ' ';
+ }
+
+
+ } else{
+ list += 'URI列表为空 ';
+ }
+
+ var table = '\
+
';
+ $('#ws_table').html(table);
+ });
+}
+
+
+function wsUriStat(){
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+var randstr = getRandomString(10);
+
+var html = '\
+
\
+
网站: \
+
\
+ 未设置 \
+ \
+
时间: \
+
\
+
\
+
\
+
';
+$(".soft-man-con").html(html);
+
+
+$('#search_time button:eq(0)').addClass('cur');
+$('#search_time button').click(function(){
+ $('#search_time button').each(function(){
+ if ($(this).hasClass('cur')){
+ $(this).removeClass('cur');
+ }
+ });
+ $('#time_choose').attr("data-name",'');
+ $('#time_choose').removeClass("cur");
+
+ $(this).addClass('cur');
+
+ wsUriStatLogRequest(1);
+});
+
+wsPost('get_default_site','',{},function(rdata){
+ $('select[name="site"]').html('');
+
+ var rdata = $.parseJSON(rdata.data);
+ var rdata = rdata.data;
+ var default_site = rdata["default"];
+ var select = '';
+ for (var i = 0; i < rdata["list"].length; i++) {
+ if (default_site == rdata["list"][i]){
+ select += ''+rdata["list"][i]+' ';
+ } else{
+ select += ''+rdata["list"][i]+' ';
+ }
+ }
+ $('select[name="site"]').html(select);
+ wsUriStatLogRequest(1);
+
+ $('select[name="site"]').change(function(){
+ wsUriStatLogRequest(1);
+ });
+});
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+}
+
+
+
+
+
+function wsTableErrorLogRequest(page){
+
+ var args = {};
+ args['page'] = page;
+ args['page_size'] = 10;
+
+ args['site'] = $('select[name="site"]').val();
+ args['status_code'] = $('select[name="status_code"]').val();
+
+ var query_date = 'today';
+ if ($('#time_choose').attr("data-name") != ''){
+ query_date = $('#time_choose').attr("data-name");
+ } else {
+ query_date = $('#search_time button.cur').attr("data-name");
+ }
+ args['query_date'] = query_date;
+
+ args['tojs'] = 'wsTableErrorLogRequest';
+ wsPost('get_logs_error_list', '' ,args, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var list = '';
+ var data = rdata.data.data;
+ if (data.length > 0){
+ for(i in data){
+ list += '';
+ list += '' + getLocalTime(data[i]['time'])+' ';
+ list += '' + data[i]['domain'] +' ';
+ list += '' + data[i]['ip'] +' ';
+ list += '' + toSize(data[i]['body_length']) +' ';
+ list += '' + toSecond(data[i]['request_time']) +' ';
+ list += '' + data[i]['uri'] +' ';
+ list += '' + data[i]['status_code']+'/' + data[i]['method'] +' ';
+ list += '详情 ';
+ list += ' ';
+ }
+ } else{
+ list += '错误日志为空 ';
+ }
+
+ var table = '\
+
';
+ $('#ws_table').html(table);
+ $('#wsPage').html(rdata.data.page);
+
+
+ $(".tablescroll .details").click(function(){
+ var index = $(this).attr('data-id');
+ var res = data[index];
+ layer.open({
+ type: 1,
+ title: "【"+res.domain + "】详情信息",
+ area: '600px',
+ closeBtn: 1,
+ shadeClose: false,
+ content: '\
+
\
+ \
+ 时间 ' + getLocalTime(res.time) + ' 真实IP ' + res.ip + ' 客户端端口 '+(res.client_port>0 && res.client_port != ''?res.client_port:'')+' \
+ 类型 ' + res.method + ' 状态 ' + res.status_code + ' 响应大小 ' + toSize(res.body_length) + ' \ \
+
\
+
协议
\
+
\
+
URL
\
+
' + $('
').text(res.uri).html() + '
\
+
完整IP列表
\
+
' + $('
').text(res.ip_list).html() + '
\
+
来路
\
+
' + $('
').text(res.referer == null ?'None':res.referer).html() + '
\
+
User-Agent
\
+
' + $('
').text(res.user_agent).html() + '
\
+
处理耗时
\
+
' +res.request_time + ' ms
\
+
',
+ });
+ });
+ });
+}
+
+
+function wsSitesErrorLog(){
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+var randstr = getRandomString(10);
+
+var html = '\
+
\
+
网站: \
+
\
+ 未设置 \
+ \
+
状态码: \
+
\
+ 所有 \
+ 50x \
+ 40x \
+ 500 \
+ 501 \
+ 502 \
+ 503 \
+ 403 \
+ 404 \
+ 499 \
+ \
+
时间: \
+
\
+
\
+
\
+
';
+$(".soft-man-con").html(html);
+
+//日期范围
+laydate.render({
+ elem: '#time_choose',
+ value:'',
+ range:true,
+ done:function(value, startDate, endDate){
+ if(!value){
+ return false;
+ }
+
+ $('#search_time button').each(function(){
+ $(this).removeClass('cur');
+ });
+
+ var timeA = value.split('-');
+ var start = $.trim(timeA[0]+'-'+timeA[1]+'-'+timeA[2])
+ var end = $.trim(timeA[3]+'-'+timeA[4]+'-'+timeA[5])
+ query_txt = toUnixTime(start + " 00:00:00") + "-"+ toUnixTime(end + " 00:00:00")
+
+ $('#time_choose').attr("data-name",query_txt);
+ $('#time_choose').addClass("cur");
+
+ wsTableErrorLogRequest(1);
+ },
+});
+
+$('#search_time button:eq(0)').addClass('cur');
+$('#search_time button').click(function(){
+ $('#search_time button').each(function(){
+ if ($(this).hasClass('cur')){
+ $(this).removeClass('cur');
+ }
+ });
+ $('#time_choose').attr("data-name",'');
+ $('#time_choose').removeClass("cur");
+
+ $(this).addClass('cur');
+
+ wsTableErrorLogRequest(1);
+});
+
+
+$('select[name="status_code"]').change(function(){
+ wsTableErrorLogRequest(1);
+});
+
+wsPost('get_default_site','',{},function(rdata){
+ $('select[name="site"]').html('');
+
+ var rdata = $.parseJSON(rdata.data);
+ var rdata = rdata.data;
+ var default_site = rdata["default"];
+ var select = '';
+ for (var i = 0; i < rdata["list"].length; i++) {
+ if (default_site == rdata["list"][i]){
+ select += ''+rdata["list"][i]+' ';
+ } else{
+ select += ''+rdata["list"][i]+' ';
+ }
+ }
+ $('select[name="site"]').html(select);
+ wsTableErrorLogRequest(1);
+
+ $('select[name="site"]').change(function(){
+ wsTableErrorLogRequest(1);
+ });
+});
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+}
+
+
+function wsTableLogRequest(page){
+
+ var args = {};
+ args['page'] = page;
+ args['page_size'] = 9;
+
+ args['site'] = $('select[name="site"]').val();
+ args['method'] = $('select[name="method"]').val();
+ args['status_code'] = $('select[name="status_code"]').val();
+ args['request_time'] = $('select[name="request_time"]').val();
+ args['request_size'] = $('select[name="request_size"]').val();
+ args['spider_type'] = $('select[name="spider_type"]').val();
+ args['referer'] = $('select[name="referer"]').val();
+ args['ip'] = $('input[name="ip"]').val();
+
+ var query_date = 'today';
+ if ($('#time_choose').attr("data-name") != ''){
+ query_date = $('#time_choose').attr("data-name");
+ } else {
+ query_date = $('#search_time button.cur').attr("data-name");
+ }
+ args['query_date'] = query_date;
+ // console.log("query_date:",query_date);
+
+
+ var search_uri = $('input[name="search_uri"]').val();
+ args['search_uri'] = search_uri;
+
+ args['tojs'] = 'wsTableLogRequest';
+
+ var spider_table = {
+ "1":"百度",
+ "2":"必应",
+ "3":"奇虎360",
+ "4":"Google",
+ "5":"头条",
+ "6":"搜狗",
+ "7":"有道",
+ "8":"搜搜",
+ "9":"Dnspod",
+ "10":"Yandex",
+ "11":"一搜",
+ "12":"其他",
+ }
+
+ var req_status = $('#logs_search').attr('req');
+ // console.log(req_status);
+ if (typeof(req_status) != 'undefined'){
+ if (req_status == 'start'){
+ layer.msg("正在请求中,请稍候!");
+ return;
+ }
+ }
+
+ $('#logs_search').attr('req','start');
+ // wsPost('get_logs_list', '' ,args, function(rdata){
+ wsPostCallbak('get_logs_list', '' ,args, function(rdata){
+ $('#logs_search').attr('req','end');
+ var rdata = $.parseJSON(rdata.data);
+ var list = '';
+ var data = rdata.data.data;
+ // console.log(data);
+
+ if (data.length > 0){
+ for(i in data){
+
+ var spider_tip = '';
+ if (data[i]['is_spider']>0){
+ spider_tip_name = spider_table[data[i]['is_spider']]
+ spider_tip = '
';
+ }
+
+ list += '';
+ list += '' + getLocalTime(data[i]['time'])+' ';
+ list += '' + data[i]['domain'] +' ';
+ list += '' + data[i]['ip'] +' ';
+ list += '' + toSize(data[i]['body_length']) +' ';
+ list += '' + toSecond(data[i]['request_time']) +' ';
+ list += '' + data[i]['uri'] +' ';
+ list += ''+spider_tip+'' + data[i]['status_code']+'/' + data[i]['method'] +' ';
+
+ var http_data = ' ';
+ if (data[i]['request_headers']!=''){
+ http_data = 'HTTP | ';
+ }
+ list += ''+http_data+'详情 ';
+ list += ' ';
+ }
+ } else{
+ list += '网站日志为空 ';
+ }
+
+ var table = '\
+
';
+ $('#ws_table').html(table);
+ $('#wsPage').html(rdata.data.page);
+
+ $(".tablescroll .details").click(function(){
+ var index = $(this).attr('data-id');
+ var res = data[index];
+ layer.open({
+ type: 1,
+ title: "【"+res.domain + "】详情信息",
+ area: '600px',
+ closeBtn: 1,
+ shadeClose: false,
+ content: '\
+
\
+ \
+ 时间 ' + getLocalTime(res.time) + ' 真实IP ' + res.ip + ' 客户端端口 '+(res.client_port>0 && res.client_port != ''?res.client_port:'')+' \
+ 类型 ' + res.method + ' 状态 ' + res.status_code + ' 响应大小 ' + toSize(res.body_length) + ' \ \
+
\
+
协议
\
+
\
+
URL
\
+
' + $('
').text(res.uri).html() + '
\
+
完整IP列表
\
+
' + $('
').text(res.ip_list).html() + '
\
+
来路
\
+
' + $('
').text(res.referer == null ?'None':res.referer).html() + '
\
+
User-Agent
\
+
' + $('
').text(res.user_agent).html() + '
\
+
处理耗时
\
+
' +res.request_time + ' ms
\
+
',
+ });
+ });
+
+ $(".tablescroll .http_data").click(function(){
+ var index = $(this).attr('data-id');
+ var res = data[index];
+ var request_headers = res.request_headers;
+
+ var req_data_html = res.method +' ' + res.uri + ' ';
+
+ try {
+ var req_data = $.parseJSON(request_headers);
+ for (var d in req_data) {
+ if (d == 'payload'){
+ req_data_html += ''+d +" :"+req_data[d]+" ";
+ } else{
+ req_data_html += d+":"+req_data[d]+" ";
+ }
+ }
+ } catch (error) {
+ req_data_html += request_headers;
+ }
+
+
+ layer.open({
+ type: 1,
+ title: "【"+res.domain + "】HTTP详情",
+ area: ['600px','375px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: '\
+
\
+
\
+ payload: POST请求中客户端提交的参数。 \
+ \
+
',
+ });
+ });
+
+ $('[data-toggle="tooltip"]').tooltip();
+ });
+}
+
+function wsSitesLog(){
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+var randstr = getRandomString(10);
+
+var html = '\
+
\
+
网站: \
+
\
+ 未设置 \
+ \
+
时间: \
+
\
+
\
+
\
+
请求类型: \
+
\
+ 所有 \
+ GET \
+ POST \
+ HEAD \
+ PUT \
+ DELETE \
+ \
+
状态码: \
+
\
+ 所有 \
+ 500 \
+ 502 \
+ 503 \
+ 400 \
+ 404 \
+ 405 \
+ 499 \
+ 301 \
+ 302 \
+ 206 \
+ 200 \
+ \
+
来源: \
+
\
+ 所有 \
+ 无 \
+ 有 \
+ \
+
蜘蛛过滤: \
+
\
+ 不过滤 \
+ 仅显示蜘蛛 \
+ 不显示蜘蛛 \
+ 百度 \
+ 必应 \
+ 奇虎360 \
+ Google \
+ 头条 \
+ 搜狗 \
+ 有道 \
+ 搜搜 \
+ Dnspod \
+ Yandex \
+ 一搜 \
+ 其他 \
+ \
+
IP: \
+
\
+ \
+
\
+
\
+
\
+
耗时: \
+
\
+ 所有 \
+ 0-50(ms) \
+ 50-200(ms) \
+ 200-500(ms) \
+ 500ms-1s \
+ 大于1s \
+ \
+
大小: \
+
\
+ 所有 \
+ 0-1(kb) \
+ 1-20(kb) \
+ 20-50(kb) \
+ 50-100(kb) \
+ 大于100kb \
+ \
+
URL过滤: \
+
\
+
\
+
\
+
';
+$(".soft-man-con").html(html);
+
+$('input[name="ip"]').bind('focus', function(e){
+ $(this).keyup(function(e){
+ if(e.keyCode == 13) {
+ wsTableLogRequest(1);
+ }
+ });
+});
+
+$('input[name="search_uri"]').bind('focus', function(e){
+ $(this).keyup(function(e){
+ if(e.keyCode == 13) {
+ wsTableLogRequest(1);
+ }
+ });
+});
+
+//日期范围
+laydate.render({
+ elem: '#time_choose',
+ value:'',
+ range:'~',
+ type:'datetime',
+ done:function(value, startDate, endDate){
+ console.log(value, startDate, endDate);
+ if(!value){
+ return false;
+ }
+
+ $('#search_time button').each(function(){
+ $(this).removeClass('cur');
+ });
+
+ var timeArr = value.split('~');
+ var start = $.trim(timeArr[0]);
+ var end = $.trim(timeArr[1]);
+ query_txt = toUnixTime(start) + "-"+ toUnixTime(end);
+
+ $('#time_choose').attr("data-name",query_txt);
+ $('#time_choose').addClass("cur");
+
+ wsTableLogRequest(1);
+ },
+});
+
+$('#search_time button:eq(0)').addClass('cur');
+$('#search_time button').click(function(){
+ $('#search_time button').each(function(){
+ if ($(this).hasClass('cur')){
+ $(this).removeClass('cur');
+ }
+ });
+ $('#time_choose').attr("data-name",'');
+ $('#time_choose').removeClass("cur");
+
+ $(this).addClass('cur');
+
+ wsTableLogRequest(1);
+});
+
+$('select[name="method"]').change(function(){
+ wsTableLogRequest(1);
+});
+
+$('select[name="status_code"]').change(function(){
+ wsTableLogRequest(1);
+});
+
+$('select[name="spider_type"]').change(function(){
+ wsTableLogRequest(1);
+});
+
+$('select[name="referer"]').change(function(){
+ wsTableLogRequest(1);
+});
+
+$('select[name="request_time"]').change(function(){
+ wsTableLogRequest(1);
+});
+
+$('select[name="request_size"]').change(function(){
+ wsTableLogRequest(1);
+});
+
+$('#logs_search').click(function(){
+ wsTableLogRequest(1);
+});
+
+wsPost('get_default_site','',{},function(rdata){
+ $('select[name="site"]').html('');
+
+ var rdata = $.parseJSON(rdata.data);
+ var rdata = rdata.data;
+ var default_site = rdata["default"];
+ var select = '';
+ for (var i = 0; i < rdata["list"].length; i++) {
+ if (default_site == rdata["list"][i]){
+ select += ''+rdata["list"][i]+' ';
+ } else{
+ select += ''+rdata["list"][i]+' ';
+ }
+ }
+ $('select[name="site"]').html(select);
+ wsTableLogRequest(1);
+
+ $('select[name="site"]').change(function(){
+ wsTableLogRequest(1);
+ });
+});
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+}
+
+
diff --git a/plugins/webstats/lua/webstats_common.lua b/plugins/webstats/lua/webstats_common.lua
new file mode 100644
index 000000000..d73c0142c
--- /dev/null
+++ b/plugins/webstats/lua/webstats_common.lua
@@ -0,0 +1,1220 @@
+
+local setmetatable = setmetatable
+local _M = { _VERSION = '1.0' }
+local mt = { __index = _M }
+
+local json = require "cjson"
+local sqlite3 = require "lsqlite3"
+local config = require "webstats_config"
+local sites = require "webstats_sites"
+
+local debug_mode = true
+local total_key = "log_kv_total"
+
+local unset_server_name = "unset"
+local max_log_id = 99999999999999
+local cache = ngx.shared.mw_total
+
+local today = ngx.re.gsub(ngx.today(),'-','')
+local request_header = ngx.req.get_headers()
+local method = ngx.req.get_method()
+
+local day = os.date("%d")
+local number_day = tonumber(day)
+local day_column = "day"..number_day
+local flow_column = "flow"..number_day
+local spider_column = "spider_flow"..number_day
+
+-- _M.setInputSn | need
+local auto_config = nil
+
+local log_dir = "{$SERVER_APP}/logs"
+
+function _M.new(self)
+ local self = {
+ total_key = total_key,
+ params = nil,
+ site_config = nil,
+ config = nil,
+ }
+ -- self.dbs = {}
+ return setmetatable(self, mt)
+end
+
+
+-- function _M.getInstance(self)
+-- if rawget(self, "instance") == nil then
+-- rawset(self, "instance", self.new())
+-- self.cron()
+-- end
+-- assert(self.instance ~= nil)
+-- return self.instance
+-- end
+
+
+function _M.getInstance(self)
+ if self.instance == nil then
+ self.instance = self:new()
+ self:cron()
+ end
+ assert(self.instance ~= nil)
+ return self.instance
+end
+
+
+function _M.initDB(self, input_sn)
+ local path = log_dir .. '/' .. input_sn .. "/logs.db"
+ db, err = sqlite3.open(path)
+
+ if err then
+ return nil
+ end
+
+ db:exec([[PRAGMA synchronous = 0]])
+ db:exec([[PRAGMA cache_size = 8000]])
+ db:exec([[PRAGMA page_size = 32768]])
+ db:exec([[PRAGMA journal_mode = wal]])
+ db:exec([[PRAGMA journal_size_limit = 21474836480]])
+ return db
+end
+
+function _M.getTotalKey(self)
+ return self.total_key
+end
+
+function _M.to_json(self, msg)
+ return json.encode(msg)
+end
+
+function _M.setConfData( self, config, site_config )
+ self.config = config
+ self.site_config = site_config
+end
+
+function _M.setParams( self, params )
+ self.params = params
+end
+
+function _M.setInputSn(self, input_sn)
+ local global_config = config["global"]
+ if config[input_sn] == nil then
+ auto_config = global_config
+ else
+ auto_config = config[input_sn]
+ for k, v in pairs(global_config) do
+ if auto_config[k] == nil then
+ auto_config[k] = v
+ end
+ end
+ end
+ return auto_config
+end
+
+function _M.get_domain(self)
+ local domain = ngx.req.get_headers()['host']
+ -- domain = ngx.re.gsub(domain, "_", ".")
+ if domain == nil then
+ domain = "unknown"
+ end
+ return domain
+end
+
+function _M.split(self, str, reps)
+ local arr = {}
+ -- 修复反向代理代过来的数据
+ if "table" == type(str) then
+ return str
+ end
+ string.gsub(str,'[^'..reps..']+',function(w) table.insert(arr,w) end)
+ return arr
+end
+
+function _M.arrlen(self, arr)
+ if not arr then return 0 end
+ local count = 0
+ for _,v in ipairs(arr) do
+ count = count + 1
+ end
+ return count
+end
+
+function _M.is_ipaddr(self, client_ip)
+ local cipn = self:split(client_ip,'.')
+ if self:arrlen(cipn) < 4 then return false end
+ for _,v in ipairs({1,2,3,4})
+ do
+ local ipv = tonumber(cipn[v])
+ if ipv == nil then return false end
+ if ipv > 255 or ipv < 0 then return false end
+ end
+ return true
+end
+
+
+function _M.get_sn(self, input_sn)
+ local dst_name = cache:get(input_sn)
+ if dst_name then return dst_name end
+
+ -- self:D(json.encode(sites))
+ for _,v in ipairs(sites)
+ do
+ if input_sn == v["name"] then
+ cache:set(input_sn, v['name'], 86400)
+ return v["name"]
+ end
+ -- self:D("get_sn:"..json.encode(v))
+ for _,dst_domain in ipairs(v['domains'])
+ do
+ if input_sn == dst_domain then
+ cache:set(input_sn, v['name'], 86400)
+ return v['name']
+ elseif string.find(dst_domain, "*") then
+ local new_domain = string.gsub(dst_domain, '*', '.*')
+ if string.find(input_sn, new_domain) then
+ dst_domain = v['name']
+ cache:set(input_sn, dst_domain, 86400)
+ end
+ end
+ end
+ end
+
+ cache:set(input_sn, unset_server_name, 86400)
+ return unset_server_name
+end
+
+
+function _M.get_store_key(self)
+ return os.date("%Y%m%d%H", ngx.time())
+end
+
+function _M.get_store_key_with_time(self, htime)
+ return os.date("%Y%m%d%H", htime)
+end
+
+function _M.get_length(self)
+ local clen = ngx.var.body_bytes_sent
+ if clen == nil then clen = 0 end
+ return tonumber(clen)
+end
+
+function _M.get_last_id(self, input_sn)
+ local last_insert_id_key = input_sn .. "_last_id"
+ local new_id, err = cache:incr(last_insert_id_key, 1, 0)
+ cache:incr(cache_count_id_key, 1, 0)
+ if new_id >= max_log_id then
+ cache:set(last_insert_id_key, 1)
+ new_id = cache:get(last_insert_id_key)
+ end
+ return new_id
+end
+
+function _M.get_http_origin(self)
+ local data = ""
+ local headers = ngx.req.get_headers()
+ if not headers then return data end
+ local req_method = ngx.req.get_method()
+ if req_method ~='GET' then
+
+ -- proxy_pass, fastcgi_pass, uwsgi_pass, and scgi_pass
+ data = ngx.var.request_body
+ if not data then
+ data = ngx.req.get_body_data()
+ end
+
+ -- API disabled in the context of log_by_lua
+ -- if not data then
+ -- ngx.req.read_body()
+ -- data = ngx.req.get_post_args(1000000)
+ -- end
+
+ if "string" == type(data) then
+ headers["payload"] = data
+ end
+
+ if "table" == type(data) then
+ -- headers = table.concat(headers, data)
+ headers["payload"] = table.concat(data, "&")
+ end
+ end
+ return json.encode(headers)
+end
+
+function _M.cronPre(self)
+ self:lock_working('cron_init_stat')
+
+ local time_key = self:get_store_key()
+ local time_key_next = self:get_store_key_with_time(ngx.time()+3600)
+
+
+ for site_k, site_v in ipairs(sites) do
+ local input_sn = site_v["name"]
+
+ local db = self:initDB(input_sn)
+
+ local wc_stat = {
+ 'request_stat',
+ 'client_stat',
+ 'spider_stat'
+ }
+
+ local v1 = true
+ local v2 = true
+ for _,ws_v in pairs(wc_stat) do
+ v1 = self:_update_stat_pre(db, ws_v, time_key)
+ v2 = self:_update_stat_pre(db, ws_v, time_key_next)
+ end
+
+ if db and db:isopen() then
+ db:execute([[COMMIT]])
+ db:close()
+ end
+
+ if not v1 or not v2 then
+ return false
+ end
+ end
+
+ self:unlock_working('cron_init_stat')
+
+ return true
+end
+
+-- 后台任务
+function _M.cron(self)
+
+ local timer_every_get_data = function (premature)
+
+ local llen, _ = ngx.shared.mw_total:llen(total_key)
+ -- self:D("PID:"..tostring(ngx.worker.id())..",llen:"..tostring(llen))
+ if llen == 0 then
+ return true
+ end
+
+ -- self:D("dedebide:cron task is busy!")
+ local ready_ok = self:cronPre()
+ if not ready_ok then
+ -- self:D("cron task is busy!")
+ return true
+ end
+
+ local cron_key = 'cron_every_1s'
+ if self:is_working(cron_key) then
+ return true
+ end
+
+ ngx.update_time()
+ local begin = ngx.now()
+
+ local dbs = {}
+ local stmts = {}
+ local stat_fields = {}
+ local ip_stats = {}
+ local url_stats = {}
+
+ local time_key = self:get_store_key()
+
+ for site_k, site_v in ipairs(sites) do
+ local input_sn = site_v["name"]
+ -- self:D("input_sn:"..input_sn)
+ -- 迁移合并时不执行
+ if self:is_migrating(input_sn) then
+ return true
+ end
+
+ -- 初始化统计表时不执行
+ if self:is_working('cron_init_stat') then
+ return true
+ end
+
+ local db = self:initDB(input_sn)
+
+ stat_fields[input_sn] = {}
+
+ if db then
+ dbs[input_sn] = db
+ self:clean_stats(db,input_sn)
+
+ local tmp_stmt = {}
+ local stmt = db:prepare[[INSERT INTO web_logs(
+ time, ip, domain, server_name, method, status_code, uri, body_length,
+ referer, user_agent, protocol, request_time, is_spider, request_headers, ip_list, client_port)
+ VALUES(:time, :ip, :domain, :server_name, :method, :status_code, :uri,
+ :body_length, :referer, :user_agent, :protocol, :request_time, :is_spider,
+ :request_headers, :ip_list, :client_port)]]
+ tmp_stmt["web_logs"] = stmt
+ stmts[input_sn] = tmp_stmt
+
+ db:exec([[BEGIN TRANSACTION]])
+ end
+ end
+
+ self:lock_working(cron_key)
+
+ -- 每秒100条
+ for i=1,llen do
+ local data, _ = ngx.shared.mw_total:lpop(total_key)
+ if not data then
+ self:unlock_working(cron_key)
+ break
+ end
+
+ local info = json.decode(data)
+
+ -- self:D("info:"..json.encode(info))
+ local input_sn = info['server_name']
+ -- self:D("insert data input_sn:"..input_sn)
+ local db = dbs[input_sn]
+ local stat_fields_is = stat_fields[input_sn]
+ if not db then
+ ngx.shared.mw_total:rpush(total_key, data)
+ self:unlock_working(cron_key)
+ break
+ end
+
+ local input_stmts = stmts[input_sn]["web_logs"]
+ if not input_stmts then
+ ngx.shared.mw_total:rpush(total_key, data)
+ self:unlock_working(cron_key)
+ break
+ end
+
+ local insert_ok = self:store_logs_line(db, input_stmts, input_sn, info)
+ if not insert_ok then
+ ngx.shared.mw_total:rpush(total_key, data)
+ self:unlock_working(cron_key)
+ break
+ end
+
+ local excluded = info["log_kv"]['excluded']
+ local stat_tmp_fields = info['stat_fields']
+
+ -- 合并统计数据
+ for stf_k,stf_v in pairs(stat_tmp_fields) do
+ -- 排除请求过滤
+ if excluded then
+ if stf_k == "spider_stat_fields" or stf_k == "client_stat_fields" then
+ break
+ end
+ end
+
+ if not stat_fields_is[stf_k] then
+ stat_fields_is[stf_k] = {}
+ end
+
+ for sv_k,sv_v in pairs(stf_v) do
+ if not stat_fields_is[stf_k][sv_k] then
+ stat_fields_is[stf_k][sv_k] = sv_v
+ else
+ stat_fields_is[stf_k][sv_k] = stat_fields_is[stf_k][sv_k] + 1
+ end
+ end
+ end
+
+ stat_fields[input_sn] = stat_fields_is
+ -- ip 统计合并
+ -- url 统计
+ if not excluded then
+ local ip = info["log_kv"]['ip']
+ local body_length = info["log_kv"]["body_length"]
+
+ if not ip_stats[input_sn] then
+ ip_stats[input_sn] = {}
+ end
+
+ if not ip_stats[input_sn][ip] then
+ local tmp = {
+ ip_num=1,
+ body_length=body_length
+ }
+ ip_stats[input_sn][ip] = tmp
+ else
+ ip_stats[input_sn][ip]["ip_num"] = ip_stats[input_sn][ip]["ip_num"]+1
+ ip_stats[input_sn][ip]["body_length"] = ip_stats[input_sn][ip]["body_length"]+body_length
+ end
+
+
+ -- uri统计
+ if not url_stats[input_sn] then
+ url_stats[input_sn] = {}
+ end
+ local request_uri = info["log_kv"]["request_uri"]
+ local request_uri_md5 = ngx.md5(request_uri)
+ if not url_stats[input_sn][request_uri_md5] then
+ local tmp = {
+ url_num=1,
+ uri=request_uri,
+ body_length=body_length
+ }
+ url_stats[input_sn][request_uri_md5] = tmp
+ else
+ url_stats[input_sn][request_uri_md5]["url_num"] = url_stats[input_sn][request_uri_md5]["url_num"]+1
+ url_stats[input_sn][request_uri_md5]["body_length"] = url_stats[input_sn][request_uri_md5]["body_length"]+body_length
+ end
+ end
+ -- self:D("url_stats:.."..json.encode(url_stats))
+ end
+
+ for site_k, site_v in ipairs(sites) do
+ local input_sn = site_v["name"]
+
+ if stmts[input_sn] then
+ for stmts_k,stmts_v in pairs(stmts[input_sn]) do
+ -- self:D("stmts_k:"..tostring(stmts_k))
+ local res, err = stmts_v:finalize()
+ if tostring(res) == "5" then
+ self:D(stmts_k..":Finalize res:"..tostring(res)..",Finalize err:"..tostring(err))
+ end
+ end
+ end
+
+
+ local stat_fields_is = stat_fields[input_sn]
+ local db = dbs[input_sn]
+ local local_ip_stats = ip_stats[input_sn]
+ local local_url_stats = url_stats[input_sn]
+
+ if db then
+ -- 统计【spider_stat,client_stat,request_stat】
+ for sti_k,sti_v in pairs(stat_fields_is) do
+ local vkk = ""
+ for sv_k,sv_v in pairs(sti_v) do
+ vkk = vkk..sv_k.."="..sv_k.."+"..sv_v..","
+ end
+ if vkk ~= "" then
+ vkk = string.sub(vkk,1,string.len(vkk)-1)
+ end
+ if sti_k == 'request_stat_fields' and vkk ~= '' then
+ self:update_stat( db, "request_stat", time_key, vkk)
+ end
+
+ if sti_k == 'client_stat_fields' and vkk ~= '' then
+ self:update_stat( db, "client_stat", time_key, vkk)
+ end
+
+ if sti_k == 'spider_stat_fields' and vkk ~= '' then
+ self:update_stat( db, "spider_stat", time_key, vkk)
+ end
+ end
+
+ -- ip统计
+ if local_ip_stats then
+ for ip_addr,ip_val in pairs(local_ip_stats) do
+ self:update_statistics_ip( db, ip_addr,ip_val["ip_num"], ip_val["body_length"])
+ end
+ end
+
+ -- url统计
+ if local_url_stats then
+ for url_md5,url_val in pairs(local_url_stats) do
+ self:update_statistics_uri( db, tostring(url_val["uri"]),url_md5, url_val["url_num"], url_val["body_length"])
+ end
+ end
+
+ -- delete expire data
+ local now_date = os.date("*t")
+ local save_day = config['global']["save_day"]
+ local save_date_timestamp = os.time{year=now_date.year,
+ month=now_date.month, day=now_date.day-save_day, hour=0}
+
+ db:exec("DELETE FROM web_logs WHERE time<"..tostring(save_date_timestamp))
+ end
+
+ if db and db:isopen() then
+ db:execute([[COMMIT]])
+ db:close()
+ end
+ end
+
+ self:unlock_working(cron_key)
+ ngx.update_time()
+ -- self:D("PID:"..tostring(ngx.worker.id()).."--【"..tostring(llen).."】, elapsed: " .. tostring(ngx.now() - begin))
+ end
+
+
+ function timer_every_get_data_try()
+ local presult, err = pcall( function() timer_every_get_data() end)
+ if not presult then
+ self:D("debug cron error on :"..tostring(err))
+ return true
+ end
+ end
+
+ ngx.timer.every(0.5, timer_every_get_data_try)
+end
+
+
+function _M.store_logs_line(self, db, stmt, input_sn, info)
+ local logline = info['log_kv']
+
+ local time = logline["time"]
+ local id = logline["id"]
+ local protocol = logline["protocol"]
+ local client_port = logline["client_port"]
+ local status_code = logline["status_code"]
+ local uri = logline["uri"]
+ local request_uri = logline["request_uri"]
+ local method = logline["method"]
+ local body_length = logline["body_length"]
+ local referer = logline["referer"]
+ local ip = logline["ip"]
+ local ip_list = logline["ip_list"]
+ local request_time = logline["request_time"]
+ local is_spider = logline["is_spider"]
+ local domain = logline["domain"]
+ local server_name = logline["server_name"]
+ local user_agent = logline["user_agent"]
+ local request_headers = logline["request_headers"]
+ local excluded = logline["excluded"]
+ local time_key = logline["time_key"]
+
+ if "table" == type(user_agent) then
+ user_agent = self:to_json(user_agent)
+ end
+
+ if not excluded then
+ stmt:bind_names {
+ time=time,
+ ip=ip,
+ domain=domain,
+ server_name=server_name,
+ method=method,
+ status_code=status_code,
+ uri=request_uri,
+ body_length=body_length,
+ referer=referer,
+ user_agent=user_agent,
+ protocol=protocol,
+ request_time=request_time,
+ is_spider=is_spider,
+ request_headers=request_headers,
+ ip_list=ip_list,
+ client_port=client_port,
+ }
+
+ local res, err = stmt:step()
+ if tostring(res) == "5" then
+ -- self:D("json:"..json.encode(logline))
+ -- self:D("the step database connection is busy, so it will be stored later | step res:"..tostring(res) ..",step err:"..tostring(err))
+ if stmt then
+ stmt:reset()
+ end
+ return false
+ end
+ stmt:reset()
+ end
+ return true
+end
+
+function _M.statistics_ipc(self, input_sn, ip)
+ -- 判断IP是否重复的时间限定范围是请求的当前时间+24小时
+ local ipc = 0
+ local ip_token = input_sn..'_'..ip
+ if not cache:get(ip_token) then
+ ipc = 1
+ cache:set(ip_token,1, self:get_end_time())
+ end
+ return ipc
+end
+
+function _M.statistics_request(self, ip, is_spider, body_length)
+ -- 计算pv uv
+ local pvc = 0
+ local uvc = 0
+ local request_header = ngx.req.get_headers()
+ if not is_spider and ngx.status == 200 and body_length > 0 then
+ local ua = ''
+ if request_header['user-agent'] then
+ if "table" == type(request_header['user-agent']) then
+ ua = self:to_json(request_header['user-agent'])
+ else
+ ua = request_header['user-agent']
+ end
+ ua = string.lower(ua)
+ end
+
+ pvc = 1
+ if ua then
+ local today = ngx.today()
+ local uv_token = ngx.md5(ip .. ua .. today)
+ if not cache:get(uv_token) then
+ uvc = 1
+ cache:set(uv_token,1, self:get_end_time())
+ end
+ end
+ end
+ return pvc, uvc
+end
+
+-- 仅计算GET/HTML
+function _M.statistics_request_old(self, ip, is_spider, body_length)
+ -- 计算pv uv
+ local pvc = 0
+ local uvc = 0
+ local method = ngx.req.get_method()
+ if not is_spider and method == 'GET' and ngx.status == 200 and body_length > 512 then
+ local ua = ''
+ if request_header['user-agent'] then
+ ua = string.lower(request_header['user-agent'])
+ end
+
+ out_header = ngx.resp.get_headers()
+ if out_header['content-type'] then
+ if string.find(out_header['content-type'], 'text/html', 1, true) then
+ pvc = 1
+ if request_header['user-agent'] then
+ if string.find(ua,'mozilla') then
+ local today = ngx.today()
+ local uv_token = ngx.md5(ip .. request_header['user-agent'] .. today)
+ if not cache:get(uv_token) then
+ uvc = 1
+ cache:set(uv_token,1, self:get_end_time())
+ end
+ end
+ end
+ end
+ end
+ end
+ return pvc, uvc
+end
+
+--------------------- db start ---------------------------
+
+
+function _M.update_statistics_uri(self, db, uri, uri_md5, day_num, body_length)
+ -- count the number of URI requests and traffic
+ local open_statistics_uri = config['global']["statistics_uri"]
+ if not open_statistics_uri then return true end
+
+ local stat_sql = nil
+ stat_sql = "INSERT INTO uri_stat(uri_md5,uri) SELECT \""..uri_md5.."\",\""..uri.."\" WHERE NOT EXISTS (SELECT uri_md5 FROM uri_stat WHERE uri_md5=\""..uri_md5.."\");"
+ local res, err = db:exec(stat_sql)
+
+ stat_sql = "UPDATE uri_stat SET "..day_column.."="..day_column.."+"..day_num..","..flow_column.."="..flow_column.."+"..body_length.." WHERE uri_md5=\""..uri_md5.."\";"
+ local res, err = db:exec(stat_sql)
+ return true
+end
+
+function _M.statistics_uri(self, db, uri, uri_md5, body_length)
+ -- count the number of URI requests and traffic
+ local open_statistics_uri = config['global']["statistics_uri"]
+ if not open_statistics_uri then return true end
+
+ local stat_sql = nil
+ stat_sql = "INSERT INTO uri_stat(uri_md5,uri) SELECT \""..uri_md5.."\",\""..uri.."\" WHERE NOT EXISTS (SELECT uri_md5 FROM uri_stat WHERE uri_md5=\""..uri_md5.."\");"
+ local res, err = db:exec(stat_sql)
+
+ stat_sql = "UPDATE uri_stat SET "..day_column.."="..day_column.."+1,"..flow_column.."="..flow_column.."+"..body_length.." WHERE uri_md5=\""..uri_md5.."\";"
+ local res, err = db:exec(stat_sql)
+ return true
+end
+
+
+function _M.update_statistics_ip(self, db, ip, day_num ,body_length)
+ local open_statistics_ip = config['global']["statistics_ip"]
+ if not open_statistics_ip then return true end
+
+ local stat_sql = nil
+ stat_sql = "INSERT INTO ip_stat(ip) SELECT \""..ip.."\" WHERE NOT EXISTS (SELECT ip FROM ip_stat WHERE ip=\""..ip.."\");"
+ local res, err = db:exec(stat_sql)
+
+ stat_sql = "UPDATE ip_stat SET "..day_column.."="..day_column.."+"..day_num..","..flow_column.."="..flow_column.."+"..body_length.." WHERE ip=\""..ip.."\""
+ local res, err = db:exec(stat_sql)
+ return true
+end
+
+function _M.statistics_ip(self, db, ip, body_length)
+ local open_statistics_ip = config['global']["statistics_ip"]
+ if not open_statistics_ip then return true end
+
+ local stat_sql = nil
+ stat_sql = "INSERT INTO ip_stat(ip) SELECT \""..ip.."\" WHERE NOT EXISTS (SELECT ip FROM ip_stat WHERE ip=\""..ip.."\");"
+ local res, err = db:exec(stat_sql)
+
+ stat_sql = "UPDATE ip_stat SET "..day_column.."="..day_column.."+1,"..flow_column.."="..flow_column.."+"..body_length.." WHERE ip=\""..ip.."\""
+ local res, err = db:exec(stat_sql)
+ return true
+end
+
+
+function _M._update_stat_pre(self, db, stat_table, key)
+ local local_sql = string.format("INSERT INTO %s(time) SELECT :time WHERE NOT EXISTS(SELECT time FROM %s WHERE time=:time);", stat_table, stat_table)
+ local update_stat_stmt = db:prepare(local_sql)
+
+ if update_stat_stmt then
+ update_stat_stmt:bind_names{time=key}
+ update_stat_stmt:step()
+ update_stat_stmt:finalize()
+ return true
+ end
+ return false
+end
+
+function _M.update_stat_quick(self, db, stat_table, key,columns)
+ if not columns then return end
+ local update_sql = "UPDATE ".. stat_table .. " SET " .. columns .. " WHERE time=" .. key
+ return db:exec(update_sql)
+end
+
+
+function _M.update_stat(self, db, stat_table, key, columns)
+ -- 根据指定表名,更新统计数据
+ if not columns then return end
+ local local_sql = string.format("INSERT INTO %s(time) SELECT :time WHERE NOT EXISTS(SELECT time FROM %s WHERE time=:time);", stat_table, stat_table)
+ local stmt = db:prepare(local_sql)
+ stmt:bind_names{time=key}
+ local res, err = stmt:step()
+ stmt:finalize()
+ local update_sql = "UPDATE ".. stat_table .. " SET " .. columns .. " WHERE time=" .. key
+ return db:exec(update_sql)
+end
+--------------------- db end ---------------------------
+
+-- debug func
+function _M.D(self,msg)
+ if not debug_mode then return true end
+ local fp = io.open('{$SERVER_APP}/debug.log', 'ab')
+ if fp == nil then
+ return nil
+ end
+ local localtime = os.date("%Y-%m-%d %H:%M:%S")
+ if server_name then
+ fp:write(tostring(msg) .. "\n")
+ else
+ fp:write(localtime..":"..tostring(msg) .. "\n")
+ end
+ fp:flush()
+ fp:close()
+ return true
+end
+
+function _M.is_migrating(self, input_sn)
+ local file = io.open("{$SERVER_APP}/migrating", "rb")
+ if file then return true end
+ local file = io.open("{$SERVER_APP}/logs/"..input_sn.."/migrating", "rb")
+ if file then return true end
+ return false
+end
+
+function _M.is_working(self,sign)
+ local work_status = cache:get(sign.."_working")
+ if work_status ~= nil and work_status == true then
+ return true
+ end
+ return false
+end
+
+function _M.lock_working(self, sign)
+ local working_key = sign.."_working"
+ cache:set(working_key, true, 60)
+end
+
+function _M.unlock_working(self, sign)
+ local working_key = sign.."_working"
+ cache:set(working_key, false)
+end
+
+function _M.write_file(self, filename, body, mode)
+ local fp = io.open(filename, mode)
+ if fp == nil then
+ return nil
+ end
+ fp:write(body)
+ fp:flush()
+ fp:close()
+ return true
+ end
+
+function _M.read_file_body(self, filename)
+ local fp = io.open(filename,'rb')
+ if not fp then
+ return nil
+ end
+ fbody = fp:read("*a")
+ fp:close()
+ if fbody == '' then
+ return nil
+ end
+ return fbody
+end
+
+function _M.load_update_day(self, input_sn)
+ local _file = "{$SERVER_APP}/logs/"..input_sn.."/update_day.log"
+ return self:read_file_body(_file)
+end
+
+function _M.write_update_day(self, input_sn)
+ local _file = "{$SERVER_APP}/logs/"..input_sn.."/update_day.log"
+ return self:write_file(_file, today, "w")
+end
+
+function _M.clean_stats(self, db, input_sn)
+ -- 清空 uri,ip 汇总的信息[昨日]
+ local update_day = self:load_update_day(input_sn)
+ if not update_day or update_day ~= today then
+
+ local update_sql = "UPDATE uri_stat SET "..day_column.."=0,"..flow_column.."=0"
+ db:exec(update_sql)
+
+ update_sql = "UPDATE ip_stat SET "..day_column.."=0,"..flow_column.."=0"
+ db:exec(update_sql)
+ self:write_update_day(input_sn)
+ end
+end
+
+function _M.lpop(self)
+ local cache = ngx.shared.mw_total
+ return cache:lpop(total_key)
+end
+
+function _M.rpop(self)
+ local cache = ngx.shared.mw_total
+ return cache:rpop(total_key)
+end
+
+
+function _M.get_update_field(self, field, value)
+ return field.."="..field.."+"..tostring(value)
+end
+
+function _M.get_request_time(self)
+ local request_time = math.floor((ngx.now() - ngx.req.start_time()) * 1000)
+ if request_time == 0 then request_time = 1 end
+ return request_time
+end
+
+
+function _M.get_end_time(self)
+ local s_time = ngx.time()
+ local n_date = os.date("*t",s_time + 86400)
+ n_date.hour = 0
+ n_date.min = 0
+ n_date.sec = 0
+ local d_time = ngx.time(n_date)
+ return d_time - s_time
+end
+
+
+function _M.match_spider(self, ua)
+ -- 匹配蜘蛛请求
+ local is_spider = false
+ local spider_name = ""
+ local spider_match = ""
+
+ local spider_table = {
+ ["baidu"] = 1, -- check
+ ["bing"] = 2, -- check
+ ["qh360"] = 3, -- check
+ ["google"] = 4,
+ ["bytes"] = 5, -- check
+ ["sogou"] = 6, -- check
+ ["youdao"] = 7,
+ ["soso"] = 8,
+ ["dnspod"] = 9,
+ ["yandex"] = 10,
+ ["yisou"] = 11,
+ ["other"] = 12,
+ ["mpcrawler"] = 13,
+ ["yahoo"] = 14, -- check
+ ["duckduckgo"] = 15
+ }
+
+ local find_spider, _ = ngx.re.match(ua, "(Baiduspider|Bytespider|360Spider|Sogou web spider|Sosospider|Googlebot|bingbot|AdsBot-Google|Google-Adwords|YoudaoBot|Yandex|DNSPod-Monitor|YisouSpider|mpcrawler)", "ijo")
+ if find_spider then
+ is_spider = true
+ spider_match = string.lower(find_spider[0])
+ if string.find(spider_match, "baidu", 1, true) then
+ spider_name = "baidu"
+ elseif string.find(spider_match, "bytes", 1, true) then
+ spider_name = "bytes"
+ elseif string.find(spider_match, "360", 1, true) then
+ spider_name = "qh360"
+ elseif string.find(spider_match, "sogou", 1, true) then
+ spider_name = "sogou"
+ elseif string.find(spider_match, "soso", 1, true) then
+ spider_name = "soso"
+ elseif string.find(spider_match, "google", 1, true) then
+ spider_name = "google"
+ elseif string.find(spider_match, "bingbot", 1, true) then
+ spider_name = "bing"
+ elseif string.find(spider_match, "youdao", 1, true) then
+ spider_name = "youdao"
+ elseif string.find(spider_match, "dnspod", 1, true) then
+ spider_name = "dnspod"
+ elseif string.find(spider_match, "yandex", 1, true) then
+ spider_name = "yandex"
+ elseif string.find(spider_match, "yisou", 1, true) then
+ spider_name = "yisou"
+ elseif string.find(spider_match, "mpcrawler", 1, true) then
+ spider_name = "mpcrawler"
+ end
+ end
+
+ if is_spider then
+ return is_spider, spider_name, spider_table[spider_name]
+ end
+
+ -- Curl|Yahoo|HeadlessChrome|包含bot|Wget|Spider|Crawler|Scrapy|zgrab|python|java|Adsbot|DuckDuckGo
+ find_spider, _ = ngx.re.match(ua, "(Yahoo|Slurp|DuckDuckGo|Semrush|Spider|Bot|crawler)", "ijo")
+ if find_spider then
+ spider_match = string.lower(find_spider[0])
+ if string.find(spider_match, "yahoo", 1, true) then
+ spider_name = "other"
+ elseif string.find(spider_match, "slurp", 1, true) then
+ spider_name = "other"
+ elseif string.find(spider_match, "duckduckgo", 1, true) then
+ spider_name = "other"
+ elseif string.find(spider_match, "semrush", 1, true) then
+ spider_name = "other"
+ elseif string.find(spider_match, "spider", 1, true) then
+ spider_name = "other"
+ elseif string.find(spider_match, "bot", 1, true) then
+ spider_name = "other"
+ elseif string.find(spider_match, "crawler", 1, true) then
+ spider_name = "other"
+ end
+ return true, spider_name, spider_table[spider_name]
+ end
+ return false, "", 0
+end
+
+
+function _M.match_client(self, ua)
+ local client_stat_fields = ""
+
+ if not ua then
+ return client_stat_fields
+ end
+
+ local clients_map = {
+ ["android"] = "android",
+ ["iphone"] = "iphone",
+ ["ipod"] = "iphone",
+ ["ipad"] = "iphone",
+ ["firefox"] = "firefox",
+ ["msie"] = "msie",
+ ["trident"] = "msie",
+ ["360se"] = "qh360",
+ ["360ee"] = "qh360",
+ ["360browser"] = "qh360",
+ ["qihoo"] = "qh360",
+ ["the world"] = "theworld",
+ ["theworld"] = "theworld",
+ ["tencenttraveler"] = "tt",
+ ["maxthon"] = "maxthon",
+ ["opera"] = "opera",
+ ["qqbrowser"] = "qq",
+ ["ucweb"] = "uc",
+ ["ubrowser"] = "uc",
+ ["safari"] = "safari",
+ ["chrome"] = "chrome",
+ ["metasr"] = "metasr",
+ ["2345explorer"] = "pc2345",
+ ["edge"] = "edeg",
+ ["edg"] = "edeg",
+ ["windows"] = "windows",
+ ["linux"] = "linux",
+ ["macintosh"] = "mac",
+ ["mobile"] = "mobile"
+ }
+ local mobile_regx = "(Mobile|Android|iPhone|iPod|iPad)"
+ local mobile_res = ngx.re.match(ua, mobile_regx, "ijo")
+ --mobile
+ if mobile_res then
+ client_stat_fields = client_stat_fields..","..self:get_update_field("mobile", 1)
+ mobile_res = string.lower(mobile_res[0])
+ if mobile_res ~= "mobile" then
+ client_stat_fields = client_stat_fields..","..self:get_update_field(clients_map[mobile_res], 1)
+ end
+ else
+ --pc
+ -- 匹配结果的顺序,与ua中关键词的顺序有关
+ -- lua的正则不支持|语法
+ -- 短字符串string.find效率要比ngx正则高
+ local pc_regx1 = "(360SE|360EE|360browser|Qihoo|TheWorld|TencentTraveler|Maxthon|Opera|QQBrowser|UCWEB|UBrowser|MetaSr|2345Explorer|Edg[e]*)"
+ local pc_res = ngx.re.match(ua, pc_regx1, "ijo")
+ local cls_pc = nil
+ if not pc_res then
+ if ngx.re.find(ua, "[Ff]irefox") then
+ cls_pc = "firefox"
+ elseif string.find(ua, "MSIE") or string.find(ua, "Trident") then
+ cls_pc = "msie"
+ elseif string.find(ua, "[Cc]hrome") then
+ cls_pc = "chrome"
+ elseif string.find(ua, "[Ss]afari") then
+ cls_pc = "safari"
+ end
+ else
+ cls_pc = string.lower(pc_res[0])
+ end
+ -- self::D("UA:"..ua)
+ -- D("PC cls:"..tostring(cls_pc))
+ if cls_pc then
+ client_stat_fields = client_stat_fields..","..self:get_update_field(clients_map[cls_pc], 1)
+ else
+ -- machine and other
+ local machine_res, err = ngx.re.match(ua, "(ApacheBench|[Cc]url|HeadlessChrome|[a-zA-Z]+[Bb]ot|[Ww]get|[Ss]pider|[Cc]rawler|[Ss]crapy|zgrab|[Pp]ython|java)", "ijo")
+ if machine_res then
+ client_stat_fields = client_stat_fields..","..self:get_update_field("machine", 1)
+ else
+ -- 移动端+PC端+机器以外 归类到 其他
+ client_stat_fields = client_stat_fields..","..self:get_update_field("other", 1)
+ end
+ end
+
+ local os_regx = "(Windows|Linux|Macintosh)"
+ local os_res = ngx.re.match(ua, os_regx, "ijo")
+ if os_res then
+ os_res = string.lower(os_res[0])
+ client_stat_fields = client_stat_fields..","..self:get_update_field(clients_map[os_res], 1)
+ end
+ end
+
+ local other_regx = "MicroMessenger"
+ local other_res = ngx.re.find(ua, other_regx)
+ if other_res then
+ client_stat_fields = client_stat_fields..","..self:get_update_field("weixin", 1)
+ end
+ if client_stat_fields then
+ client_stat_fields = string.sub(client_stat_fields, 2)
+ end
+ return client_stat_fields
+end
+
+
+function _M.match_client_arr(self, ua)
+ local client_stat_fields = {}
+
+ if not ua then
+ return client_stat_fields
+ end
+
+ local clients_map = {
+ ["android"] = "android",
+ ["iphone"] = "iphone",
+ ["ipod"] = "iphone",
+ ["ipad"] = "iphone",
+ ["firefox"] = "firefox",
+ ["msie"] = "msie",
+ ["trident"] = "msie",
+ ["360se"] = "qh360",
+ ["360ee"] = "qh360",
+ ["360browser"] = "qh360",
+ ["qihoo"] = "qh360",
+ ["the world"] = "theworld",
+ ["theworld"] = "theworld",
+ ["tencenttraveler"] = "tt",
+ ["maxthon"] = "maxthon",
+ ["opera"] = "opera",
+ ["qqbrowser"] = "qq",
+ ["ucweb"] = "uc",
+ ["ubrowser"] = "uc",
+ ["safari"] = "safari",
+ ["chrome"] = "chrome",
+ ["metasr"] = "metasr",
+ ["2345explorer"] = "pc2345",
+ ["edge"] = "edeg",
+ ["edg"] = "edeg",
+ ["windows"] = "windows",
+ ["linux"] = "linux",
+ ["macintosh"] = "mac",
+ ["mobile"] = "mobile"
+ }
+ local mobile_regx = "(Mobile|Android|iPhone|iPod|iPad)"
+ local mobile_res = ngx.re.match(ua, mobile_regx, "ijo")
+ --mobile
+ if mobile_res then
+ client_stat_fields['mobile'] = 1
+ mobile_res = string.lower(mobile_res[0])
+ if mobile_res ~= "mobile" then
+ client_stat_fields[clients_map[mobile_res]] = 1
+ end
+ else
+ --pc
+ -- 匹配结果的顺序,与ua中关键词的顺序有关
+ -- lua的正则不支持|语法
+ -- 短字符串string.find效率要比ngx正则高
+ local pc_regx1 = "(360SE|360EE|360browser|Qihoo|TheWorld|TencentTraveler|Maxthon|Opera|QQBrowser|UCWEB|UBrowser|MetaSr|2345Explorer|Edg[e]*)"
+ local pc_res = ngx.re.match(ua, pc_regx1, "ijo")
+ local cls_pc = nil
+
+ -- self:D("UA-JSON:"..self:to_json(ua))
+ if "table" == type(ua) then
+ ua = self:to_json(ua)
+ end
+
+ if not pc_res then
+ if ngx.re.find(ua, "[Ff]irefox") then
+ cls_pc = "firefox"
+ elseif string.find(ua, "MSIE") or string.find(ua, "Trident") then
+ cls_pc = "msie"
+ elseif string.find(ua, "[Cc]hrome") then
+ cls_pc = "chrome"
+ elseif string.find(ua, "[Ss]afari") then
+ cls_pc = "safari"
+ end
+ else
+ cls_pc = string.lower(pc_res[0])
+ end
+ -- self:D("UA:"..ua)
+ -- self:D("PC cls:"..tostring(cls_pc))
+ if cls_pc then
+ client_stat_fields[clients_map[cls_pc]] = 1
+ else
+ -- machine and other
+ local machine_res, err = ngx.re.match(ua, "(ApacheBench|[Cc]url|HeadlessChrome|[a-zA-Z]+[Bb]ot|[Ww]get|[Ss]pider|[Cc]rawler|[Ss]crapy|zgrab|[Pp]ython|java)", "ijo")
+ if machine_res then
+ client_stat_fields["machine"] = 1
+ else
+ -- 移动端+PC端+机器以外 归类到 其他
+ client_stat_fields["other"] = 1
+ end
+ end
+
+ local os_regx = "(Windows|Linux|Macintosh)"
+ local os_res = ngx.re.match(ua, os_regx, "ijo")
+ if os_res then
+ os_res = string.lower(os_res[0])
+ client_stat_fields[clients_map[os_res]] = 1
+ end
+ end
+
+ local other_regx = "MicroMessenger"
+ local other_res = ngx.re.find(ua, other_regx)
+ if other_res then
+ client_stat_fields["weixin"] = 1
+ end
+ return client_stat_fields
+end
+
+function _M.get_client_ip(self)
+ local client_ip = "unknown"
+ local cdn = auto_config['cdn']
+ local request_header = ngx.req.get_headers()
+ if cdn == true then
+ for _,v in ipairs(auto_config['cdn_headers']) do
+ if request_header[v] ~= nil and request_header[v] ~= "" then
+ local ip_list = request_header[v]
+ client_ip = self:split(ip_list,',')[1]
+ break;
+ end
+ end
+ end
+
+ -- ipv6
+ if type(client_ip) == 'table' then client_ip = "" end
+ if client_ip ~= "unknown" and ngx.re.match(client_ip,"^([a-fA-F0-9]*):") then
+ return client_ip
+ end
+
+ -- ipv4
+ if not ngx.re.match(client_ip,"\\d+\\.\\d+\\.\\d+\\.\\d+") == nil or not self:is_ipaddr(client_ip) then
+ client_ip = ngx.var.remote_addr
+ if client_ip == nil then
+ client_ip = "unknown"
+ end
+ end
+
+ return client_ip
+end
+
+return _M
\ No newline at end of file
diff --git a/plugins/webstats/lua/webstats_log.lua b/plugins/webstats/lua/webstats_log.lua
new file mode 100644
index 000000000..693424770
--- /dev/null
+++ b/plugins/webstats/lua/webstats_log.lua
@@ -0,0 +1,667 @@
+log_by_lua_block {
+
+ local cpath = "{$SERVER_APP}/lua/"
+ if not package.cpath:find(cpath) then
+ package.cpath = cpath .. "?.so;" .. package.cpath
+ end
+ if not package.path:find(cpath) then
+ package.path = cpath .. "?.lua;" .. package.path
+ end
+
+ local ver = '0.2.4'
+ local debug_mode = true
+
+ local __C = require "webstats_common"
+ local C = __C:getInstance()
+
+ -- cache start ---
+ local cache = ngx.shared.mw_total
+ local function cache_set(server_name, id ,key, val)
+ local line_kv = "log_kv_"..server_name..'_'..id.."_"..key
+ -- cache:set(line_kv, val)
+ cache:set(line_kv, val)
+ end
+
+ local function cache_clear(server_name, id, key)
+ local line_kv = "log_kv_"..server_name..'_'..id.."_"..key
+ cache:delete(line_kv)
+ end
+
+ local function cache_get(server_name, id, key)
+ local line_kv = "log_kv_"..server_name..'_'..id.."_"..key
+ local value = cache:get(line_kv)
+ return value
+ end
+
+
+ -- cache end ---
+
+ -- domain config is import
+ local db = nil
+ local json = require "cjson"
+ local sqlite3 = require "lsqlite3"
+ local config = require "webstats_config"
+ local sites = require "webstats_sites"
+
+ -- string.gsub(C:get_sn(ngx.var.server_name),'_','.')
+ local server_name = C:get_sn(ngx.var.server_name)
+
+
+ C:setConfData(config, sites)
+
+ local auto_config = C:setInputSn(server_name)
+
+ local request_header = ngx.req.get_headers()
+ local method = ngx.req.get_method()
+ local excluded = false
+
+ local day = os.date("%d")
+ local number_day = tonumber(day)
+ local day_column = "day"..number_day
+ local flow_column = "flow"..number_day
+ local spider_column = "spider_flow"..number_day
+ --- default common var end ---
+
+ local function init_var()
+ return true
+ end
+
+ --------------------- exclude_func start --------------------------
+ local function load_global_exclude_ip()
+ local load_key = "global_exclude_ip_load"
+ -- update global exclude ip
+ local global_exclude_ip = auto_config["exclude_ip"]
+ if global_exclude_ip then
+ for i, _ip in pairs(global_exclude_ip)
+ do
+ -- global
+ -- D("set global exclude ip: ".._ip)
+ if not cache:get("global_exclude_ip_".._ip) then
+ cache:set("global_exclude_ip_".._ip, true)
+ end
+ end
+ end
+ -- set tag
+ cache:set(load_key, true)
+ end
+
+ local function load_exclude_ip(input_server_name)
+
+ local load_key = input_server_name .. "_exclude_ip_load"
+ local site_config = config[input_server_name]
+
+ local site_exclude_ip = nil
+ if site_config then
+ site_exclude_ip = site_config["exclude_ip"]
+ end
+
+ -- update server_name exclude ip
+ if site_exclude_ip then
+ for i, _ip in pairs(site_exclude_ip)
+ do
+ cache:set(input_server_name .. "_exclude_ip_".._ip, true)
+ end
+ end
+
+ -- set tag
+ cache:set(load_key, true)
+ return true
+ end
+
+ local function filter_status()
+ if not auto_config['exclude_status'] then return false end
+ local the_status = tostring(ngx.status)
+ for _,v in ipairs(auto_config['exclude_status'])
+ do
+ if the_status == v then
+ return true
+ end
+ end
+ return false
+ end
+
+ local function exclude_extension()
+ if not ngx.var.uri then return false end
+ if not auto_config['exclude_extension'] then return false end
+ for _,v in ipairs(auto_config['exclude_extension'])
+ do
+ if ngx.re.find(ngx.var.uri,"[.]"..v.."$",'ijo') then
+ return true
+ end
+ end
+ return false
+ end
+
+ local function exclude_url()
+ if not ngx.var.uri then return false end
+ if not ngx.var.request_uri then return false end
+ if not auto_config['exclude_url'] then return false end
+ local the_uri = string.sub(ngx.var.request_uri, 2)
+ local url_conf = auto_config["exclude_url"]
+ for i,conf in ipairs(url_conf)
+ do
+ local mode = conf["mode"]
+ local url = conf["url"]
+ if mode == "regular" then
+ if ngx.re.find(the_uri, url, "ijo") then
+ return true
+ end
+ else
+ if the_uri == url then
+ return true
+ end
+ end
+ end
+ return false
+ end
+
+ local function exclude_ip(input_server_name, ip)
+ -- 排除IP匹配,分网站单独的配置和全局配置两种方式
+ local site_config = config[input_server_name]
+ local server_exclude_ips = nil
+ local check_server_exclude_ip = false
+ if site_config then
+ server_exclude_ips = site_config["exclude_ip"]
+ if not server_exclude_ips then
+ return false
+ end
+ for k, _ip in pairs(server_exclude_ips)
+ do
+ check_server_exclude_ip = true
+ break
+ end
+ end
+ -- D("server[" ..input_server_name.."]check exclude ip : "..tostring(check_server_exclude_ip))
+ if check_server_exclude_ip then
+ if cache:get(input_server_name .. "_exclude_ip_"..ip) then
+ -- D("-Exclude server ip:"..ip)
+ return true
+ end
+ else
+ if cache:get("global_exclude_ip_"..ip) then
+ -- D("*Excluded global ip:"..ip)
+ return true
+ end
+ end
+ return false
+ end
+ --------------------- exclude_func end ---------------------------
+
+ local function cache_logs_old(server_name)
+
+ -- make new id
+ local new_id = C:get_last_id(server_name)
+
+ local excluded = false
+ local ip = C:get_client_ip()
+ excluded = filter_status() or exclude_extension() or exclude_url() or exclude_ip(server_name, ip)
+
+ local ip_list = request_header["x-forwarded-for"]
+ if ip and not ip_list then
+ ip_list = ip
+ end
+
+ local remote_addr = ngx.var.remote_addr
+ if not string.find(ip_list, remote_addr) then
+ if remote_addr then
+ ip_list = ip_list .. "," .. remote_addr
+ end
+ end
+
+ -- local request_time = ngx.var.request_time
+ local request_time = C:get_request_time()
+ local client_port = ngx.var.remote_port
+ local real_server_name = ngx.var.server_name
+ local uri = ngx.var.uri
+ local status_code = ngx.status
+ local protocol = ngx.var.server_protocol
+ local request_uri = ngx.var.request_uri
+ local time_key = C:get_store_key()
+ local method = method
+ local body_length = C:get_length()
+ local domain = C:get_domain()
+ local referer = ngx.var.http_referer
+
+ local kv = {
+ id=new_id,
+ time_key=time_key,
+ time=ngx.time(),
+ ip=ip,
+ domain=domain,
+ server_name=server_name,
+ real_server_name=real_server_name,
+ method=method,
+ status_code=status_code,
+ uri=uri,
+ request_uri=request_uri,
+ body_length=body_length,
+ referer=referer,
+ user_agent=request_header['user-agent'],
+ protocol=protocol,
+ is_spider=0,
+ request_time=request_time,
+ excluded=excluded,
+ request_headers='',
+ ip_list=ip_list,
+ client_port=client_port
+ }
+
+ local request_stat_fields = "req=req+1,length=length+"..body_length
+ local spider_stat_fields = "x"
+ local client_stat_fields = "x"
+
+ if not excluded then
+
+ if status_code == 500 or (method=="POST" and config["record_post_args"] == true) or (status_code==403 and config["record_get_403_args"] == true) then
+ local data = ""
+ local ok, err = pcall(function() data = C:get_http_origin() end)
+ if ok and not err then
+ kv["request_headers"] = data
+ end
+ end
+
+ if ngx.re.find("500,501,502,503,504,505,506,507,509,510,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,421,422,423,424,425,426,449,451,499", tostring(status_code), "jo") then
+ local field = "status_"..status_code
+ request_stat_fields = request_stat_fields .. ","..field.."="..field.."+1"
+ end
+
+ local lower_method = string.lower(method)
+ if ngx.re.find("get,post,put,patch,delete", lower_method, "ijo") then
+ local field = "http_"..lower_method
+ request_stat_fields = request_stat_fields .. ","..field.."="..field.."+1"
+ end
+
+
+ local ipc = 0
+ local pvc = 0
+ local uvc = 0
+
+ local is_spider, request_spider, spider_index = C:match_spider(kv['user_agent'])
+ if not is_spider then
+
+ client_stat_fields = C:match_client(kv['user_agent'])
+ if not client_stat_fields or #client_stat_fields == 0 then
+ client_stat_fields = request_stat_fields..",other=other+1"
+ end
+
+ pvc, uvc = C:statistics_request(ip, is_spider,body_length)
+ ipc = C:statistics_ipc(server_name,ip)
+ else
+ kv["is_spider"] = spider_index
+ local field = "spider"
+ spider_stat_fields = request_spider.."="..request_spider.."+"..1
+ request_stat_fields = request_stat_fields .. ","..field.."="..field.."+"..1
+ end
+
+ if ipc > 0 then
+ request_stat_fields = request_stat_fields..",ip=ip+1"
+ end
+ if uvc > 0 then
+ request_stat_fields = request_stat_fields..",uv=uv+1"
+ end
+ if pvc > 0 then
+ request_stat_fields = request_stat_fields..",pv=pv+1"
+ end
+ end
+
+ local stat_fields = request_stat_fields..";"..client_stat_fields..";"..spider_stat_fields
+
+ cache_set(server_name, new_id, "stat_fields", stat_fields)
+ cache_set(server_name, new_id, "log_kv", json.encode(kv))
+ end
+
+ local function cache_logs(input_sn)
+
+ -- make new id
+ local new_id = C:get_last_id(input_sn)
+
+ local excluded = false
+ local ip = C:get_client_ip()
+ excluded = filter_status() or exclude_extension() or exclude_url() or exclude_ip(input_sn, ip)
+
+ local ip_list = request_header["x-forwarded-for"]
+ if ip and not ip_list then
+ ip_list = ip
+ end
+
+ local remote_addr = ngx.var.remote_addr
+
+ -- 修复反向代理代过来的数据
+ if "table" == type(ip_list) then
+ ip_list = json.encode(ip_list)
+ end
+
+ if not string.find(ip_list, remote_addr) then
+ if remote_addr then
+ ip_list = ip_list .. "," .. remote_addr
+ end
+ end
+
+ -- local request_time = ngx.var.request_time
+ local request_time = C:get_request_time()
+ local client_port = ngx.var.remote_port
+ local real_server_name = input_sn
+ local uri = tostring(ngx.var.uri)
+ local status_code = ngx.status
+ local protocol = ngx.var.server_protocol
+ local request_uri = ngx.var.request_uri
+ local time_key = C:get_store_key()
+ local method = method
+ local body_length = C:get_length()
+ local domain = C:get_domain()
+ local referer = ngx.var.http_referer
+
+ local kv = {
+ id=new_id,
+ time_key=time_key,
+ time=ngx.time(),
+ ip=ip,
+ domain=domain,
+ server_name=input_sn,
+ real_server_name=real_server_name,
+ method=method,
+ status_code=status_code,
+ uri=uri,
+ request_uri=request_uri,
+ body_length=body_length,
+ referer=referer,
+ user_agent=request_header['user-agent'],
+ protocol=protocol,
+ is_spider=0,
+ request_time=request_time,
+ excluded=excluded,
+ request_headers='',
+ ip_list=ip_list,
+ client_port=client_port
+ }
+
+ -- C:D(json.encode(kv))
+ local request_stat_fields = {req=1,length=body_length}
+
+ local spider_stat_fields = {}
+ local client_stat_fields = {}
+
+ if not excluded then
+ if status_code == 500 or (method=="POST" and config['global']["record_post_args"] == true) or (status_code==403 and config['global']["record_get_403_args"] == true) then
+ local data = ""
+ local ok, err = pcall(function() data = C:get_http_origin() end)
+ if ok and not err then
+ kv["request_headers"] = data
+ else
+ C:D("debug request_headers error:"..tostring(err))
+ end
+ end
+
+ if ngx.re.find("500,501,502,503,504,505,506,507,509,510,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,421,422,423,424,425,426,449,451,499", tostring(status_code), "jo") then
+ local field = "status_"..status_code
+ request_stat_fields[field] = 1
+ end
+
+ -- D("method:"..method)
+ local lower_method = string.lower(method)
+ if ngx.re.find("get,post,put,patch,delete", lower_method, "ijo") then
+ local field = "http_"..lower_method
+ request_stat_fields[field] = 1
+ end
+
+
+ local ipc = 0
+ local pvc = 0
+ local uvc = 0
+
+ local is_spider, request_spider, spider_index = C:match_spider(kv['user_agent'])
+ if not is_spider then
+
+ client_stat_fields = C:match_client_arr(kv['user_agent'])
+ pvc, uvc = C:statistics_request(ip, is_spider,body_length)
+ ipc = C:statistics_ipc(input_sn,ip)
+ else
+ kv["is_spider"] = spider_index
+ local field = "spider"
+ spider_stat_fields[request_spider] = 1
+ request_stat_fields[field] = 1
+ end
+
+ if ipc > 0 then
+ request_stat_fields["ip"] = 1
+ end
+ if uvc > 0 then
+ request_stat_fields["uv"] = 1
+ end
+ if pvc > 0 then
+ request_stat_fields["pv"] = 1
+ end
+ end
+
+ local stat_fields = {
+ request_stat_fields=request_stat_fields,
+ client_stat_fields=client_stat_fields,
+ spider_stat_fields=spider_stat_fields,
+ }
+
+ local data = {
+ server_name=input_sn,
+ stat_fields=stat_fields,
+ log_kv=kv,
+ }
+
+ local push_data = json.encode(data)
+ -- C:D(json.encode(push_data))
+ local key = C:getTotalKey()
+ ngx.shared.mw_total:rpush(key, push_data)
+ end
+
+ local function store_logs_line(db, stmt, input_server_name, lineno)
+ local logvalue = cache_get(input_server_name, lineno, "log_kv")
+ if not logvalue then return false end
+ local logline = json.decode(logvalue)
+
+ local time = logline["time"]
+ local id = logline["id"]
+ local protocol = logline["protocol"]
+ local client_port = logline["client_port"]
+ local status_code = logline["status_code"]
+ local uri = logline["uri"]
+ local request_uri = logline["request_uri"]
+ local method = logline["method"]
+ local body_length = logline["body_length"]
+ local referer = logline["referer"]
+ local ip = logline["ip"]
+ local ip_list = logline["ip_list"]
+ local request_time = logline["request_time"]
+ local is_spider = logline["is_spider"]
+ local domain = logline["domain"]
+ local server_name = logline["server_name"]
+ local user_agent = logline["user_agent"]
+ local request_headers = logline["request_headers"]
+ local excluded = logline["excluded"]
+
+ local request_stat_fields = nil
+ local client_stat_fields = nil
+ local spider_stat_fields = nil
+ local stat_fields = cache_get(input_server_name, id, "stat_fields")
+ if stat_fields == nil then
+ -- D("Log stat fields is nil.")
+ -- D("Logdata:"..logvalue)
+ else
+ stat_fields = C:split(stat_fields, ";")
+ request_stat_fields = stat_fields[1]
+ client_stat_fields = stat_fields[2]
+ spider_stat_fields = stat_fields[3]
+
+ if "x" == client_stat_fields then
+ client_stat_fields = nil
+ end
+
+ if "x" == spider_stat_fields then
+ spider_stat_fields = nil
+ end
+ end
+
+ local time_key = logline["time_key"]
+ if not excluded then
+ stmt:bind_names{
+ time=time,
+ ip=ip,
+ domain=domain,
+ server_name=server_name,
+ method=method,
+ status_code=status_code,
+ uri=request_uri,
+ body_length=body_length,
+ referer=referer,
+ user_agent=user_agent,
+ protocol=protocol,
+ request_time=request_time,
+ is_spider=is_spider,
+ request_headers=request_headers,
+ ip_list=ip_list,
+ client_port=client_port,
+ }
+
+ local res, err = stmt:step()
+ if tostring(res) == "5" then
+ -- D("step res:"..tostring(res))
+ -- D("step err:"..tostring(err))
+ -- D("the step database connection is busy, so it will be stored later.")
+ return false
+ end
+ stmt:reset()
+ -- D("store_logs_line ok")
+
+ C:update_stat( db, "client_stat", time_key, client_stat_fields)
+ C:update_stat( db, "spider_stat", time_key, spider_stat_fields)
+ -- C:D("stat ok")
+
+ -- only count non spider requests
+ local ok, err = C:statistics_uri(db, request_uri, ngx.md5(request_uri), body_length)
+ local ok, err = C:statistics_ip(db, ip, body_length)
+ end
+
+ C:update_stat( db, "request_stat", time_key, request_stat_fields)
+ return true
+ end
+
+ local function store_logs(input_sn)
+ if C:is_migrating(input_sn) == true then
+ -- D("migrating...")
+ return
+ end
+
+ local last_insert_id_key = input_sn.."_last_id"
+ local store_start_id_key = input_sn.."_store_start"
+ local last_id = cache:get(last_insert_id_key)
+ local store_start = cache:get(store_start_id_key)
+ if store_start == nil then
+ store_start = 1
+ end
+ local store_end = last_id
+ if store_end == nil then
+ store_end = 1
+ end
+
+ local worker_id = ngx.worker.id()
+ if C:is_working(input_sn) then
+ -- D("other workers are being stored, please store later.")
+ -- cache:delete(flush_data_key)
+ return true
+ end
+ C:lock_working(input_sn)
+
+ local log_dir = "{$SERVER_APP}/logs"
+ local db_path = log_dir .. '/' .. input_sn .. "/logs.db"
+ local db, err = sqlite3.open(db_path)
+
+ if tostring(err) ~= 'nil' then
+ C:D("sqlite3 open error:"..tostring(err))
+ return true
+ end
+
+ db:exec([[PRAGMA synchronous = 0]])
+ db:exec([[PRAGMA page_size = 4096]])
+ db:exec([[PRAGMA journal_mode = wal]])
+ db:exec([[PRAGMA journal_size_limit = 1073741824]])
+
+ local stmt2 = nil
+ if db ~= nil then
+ stmt2 = db:prepare[[INSERT INTO web_logs(
+ time, ip, domain, server_name, method, status_code, uri, body_length,
+ referer, user_agent, protocol, request_time, is_spider, request_headers, ip_list, client_port)
+ VALUES(:time, :ip, :domain, :server_name, :method, :status_code, :uri,
+ :body_length, :referer, :user_agent, :protocol, :request_time, :is_spider,
+ :request_headers, :ip_list, :client_port)]]
+ end
+
+ if db == nil or stmt2 == nil then
+ -- D("web data db error")
+ -- cache:set(storing_key, false)
+ if db and db:isopen() then
+ db:close()
+ end
+ return true
+ end
+
+ status, errorString = db:exec([[BEGIN TRANSACTION]])
+
+ C:clean_stats(db, input_sn)
+
+ if store_end >= store_start then
+ for i=store_start, store_end, 1 do
+ -- D("store_start:"..store_start..":store_end:".. store_end)
+ if store_logs_line(db, stmt2, input_sn, i) then
+ cache_clear(input_sn, i, "log_kv")
+ cache_clear(input_sn, i, "stat_fields")
+ end
+ end
+ end
+
+ local res, err = stmt2:finalize()
+ if tostring(res) == "5" then
+ C:D("Finalize res:"..tostring(res)..",Finalize err:"..tostring(err))
+ end
+
+ local now_date = os.date("*t")
+ local save_day = config['global']["save_day"]
+ local save_date_timestamp = os.time{year=now_date.year,
+ month=now_date.month, day=now_date.day-save_day, hour=0}
+ -- delete expire data
+ db:exec("DELETE FROM web_logs WHERE time<"..tostring(save_date_timestamp))
+
+ local res, err = db:execute([[COMMIT]])
+ if db and db:isopen() then
+ db:close()
+ end
+
+ cache:set(store_start_id_key, store_end+1)
+ C:unlock_working(input_sn)
+ end
+
+ local function run_app()
+ -- C:D("------------ debug start ------------")
+ init_var()
+
+ load_global_exclude_ip()
+ load_exclude_ip(server_name)
+
+ cache_logs(server_name)
+ -- C:cron()
+
+ -- cache_logs_old(server_name)
+ -- store_logs(server_name)
+ -- C:D("------------ debug end -------------")
+ end
+
+
+ local function run_app_ok()
+ if not debug_mode then return run_app() end
+
+ local presult, err = pcall( function() run_app() end)
+ if not presult then
+ C:D("debug error on :"..tostring(err))
+ return true
+ end
+ end
+
+ return run_app_ok()
+}
+
diff --git a/plugins/webstats/t/bench/bench.sh b/plugins/webstats/t/bench/bench.sh
new file mode 100755
index 000000000..1882c65ac
--- /dev/null
+++ b/plugins/webstats/t/bench/bench.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+rootPath=$(dirname "$rootPath")
+
+# echo $rootPath
+
+resty=$rootPath/openresty/bin/resty
+
+RUN_CMD=$resty
+if [ ! -f $resty ];then
+ RUN_CMD=/www/server/openresty/bin/resty
+fi
+
+
+# test
+# $RUN_CMD simple.lua
+
+# $RUN_CMD test_today.lua
+# $RUN_CMD test_time.lua
+
+# $RUN_CMD test_ngx_find.lua
+
+$RUN_CMD test_match_spider.lua
\ No newline at end of file
diff --git a/plugins/webstats/t/bench/simple.lua b/plugins/webstats/t/bench/simple.lua
new file mode 100755
index 000000000..7f52523a0
--- /dev/null
+++ b/plugins/webstats/t/bench/simple.lua
@@ -0,0 +1,18 @@
+local function target()
+ ngx.re.find("hello, world.", [[\w+\.]], "jo")
+end
+for i = 1, 100 do
+ target()
+end
+
+collectgarbage()
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e7
+for i = 1, N do
+ target()
+end
+ngx.update_time()
+
+ngx.say("elapsed: ", (ngx.now() - begin) / N)
\ No newline at end of file
diff --git a/plugins/webstats/t/bench/test_match_spider.lua b/plugins/webstats/t/bench/test_match_spider.lua
new file mode 100644
index 000000000..833b75f56
--- /dev/null
+++ b/plugins/webstats/t/bench/test_match_spider.lua
@@ -0,0 +1,106 @@
+
+local function target()
+ ngx.re.find("hello, world.", [[\w+\.]], "jo")
+end
+for i = 1, 100 do
+ target()
+end
+-- 以上为预热操作
+collectgarbage()
+
+local function match_spider(ua)
+ -- 匹配蜘蛛请求
+ local is_spider = false
+ local spider_name = ""
+ local spider_match = ""
+
+ local spider_table = {
+ ["baidu"] = 1, -- check
+ ["bing"] = 2, -- check
+ ["qh360"] = 3, -- check
+ ["google"] = 4,
+ ["bytes"] = 5, -- check
+ ["sogou"] = 6, -- check
+ ["youdao"] = 7,
+ ["soso"] = 8,
+ ["dnspod"] = 9,
+ ["yandex"] = 10,
+ ["yisou"] = 11,
+ ["other"] = 12,
+ ["mpcrawler"] = 13,
+ ["yahoo"] = 14, -- check
+ ["duckduckgo"] = 15
+ }
+
+ local find_spider, _ = ngx.re.match(ua, "(Baiduspider|Bytespider|360Spider|Sogou web spider|Sosospider|Googlebot|bingbot|AdsBot-Google|Google-Adwords|YoudaoBot|Yandex|DNSPod-Monitor|YisouSpider|mpcrawler)", "ijo")
+ if find_spider then
+ is_spider = true
+ spider_match = string.lower(find_spider[0])
+ if string.find(spider_match, "baidu", 1, true) then
+ spider_name = "baidu"
+ elseif string.find(spider_match, "bytes", 1, true) then
+ spider_name = "bytes"
+ elseif string.find(spider_match, "360", 1, true) then
+ spider_name = "qh360"
+ elseif string.find(spider_match, "sogou", 1, true) then
+ spider_name = "sogou"
+ elseif string.find(spider_match, "soso", 1, true) then
+ spider_name = "soso"
+ elseif string.find(spider_match, "google", 1, true) then
+ spider_name = "google"
+ elseif string.find(spider_match, "bingbot", 1, true) then
+ spider_name = "bing"
+ elseif string.find(spider_match, "youdao", 1, true) then
+ spider_name = "youdao"
+ elseif string.find(spider_match, "dnspod", 1, true) then
+ spider_name = "dnspod"
+ elseif string.find(spider_match, "yandex", 1, true) then
+ spider_name = "yandex"
+ elseif string.find(spider_match, "yisou", 1, true) then
+ spider_name = "yisou"
+ elseif string.find(spider_match, "mpcrawler", 1, true) then
+ spider_name = "mpcrawler"
+ end
+ end
+
+ if is_spider then
+ return is_spider, spider_name, spider_table[spider_name]
+ end
+
+ -- Curl|Yahoo|HeadlessChrome|包含bot|Wget|Spider|Crawler|Scrapy|zgrab|python|java|Adsbot|DuckDuckGo
+ find_spider, _ = ngx.re.match(ua, "(Yahoo|Slurp|DuckDuckGo)", "ijo")
+ if res then
+ spider_match = string.lower(find_spider[0])
+ if string.find(spider_match, "yahoo", 1, true) then
+ spider_name = "yahoo"
+ elseif string.find(spider_match, "slurp", 1, true) then
+ spider_name = "yahoo"
+ elseif string.find(spider_match, "duckduckgo", 1, true) then
+ spider_name = "duckduckgo"
+ end
+ return true, spider_name, spider_table[spider_name]
+ end
+ return false, "", 0
+end
+
+
+
+-- local is_spider, request_spider, spider_index = match_spider("Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)")
+
+-- ngx.say(is_spider,request_spider, spider_index)
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e6
+for i = 1, N do
+ match_spider("Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)")
+end
+ngx.update_time()
+
+ngx.say("match_spider elapsed: ", (ngx.now() - begin) / N)
+
+
+
+
+
+
diff --git a/plugins/webstats/t/bench/test_ngx_find.lua b/plugins/webstats/t/bench/test_ngx_find.lua
new file mode 100644
index 000000000..67a045d6e
--- /dev/null
+++ b/plugins/webstats/t/bench/test_ngx_find.lua
@@ -0,0 +1,35 @@
+
+local function target()
+ ngx.re.find("hello, world.", [[\w+\.]], "jo")
+end
+for i = 1, 100 do
+ target()
+end
+-- 以上为预热操作
+collectgarbage()
+
+local spider_match = "aa 220"
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e7
+for i = 1, N do
+ ngx.re.find(spider_match, "360", "ijo")
+end
+ngx.update_time()
+
+ngx.say("ngx.re.find elapsed: ", (ngx.now() - begin) / N)
+
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e7
+for i = 1, N do
+ string.find(spider_match, "360", 1, true)
+end
+ngx.update_time()
+
+ngx.say("string.find elapsed: ", (ngx.now() - begin) / N)
+
diff --git a/plugins/webstats/t/bench/test_time.lua b/plugins/webstats/t/bench/test_time.lua
new file mode 100644
index 000000000..492b79bed
--- /dev/null
+++ b/plugins/webstats/t/bench/test_time.lua
@@ -0,0 +1,118 @@
+
+local function target()
+ ngx.re.find("hello, world.", [[\w+\.]], "jo")
+end
+for i = 1, 100 do
+ target()
+end
+-- 以上为预热操作
+collectgarbage()
+
+
+local function get_store_key()
+ return os.date("%Y%m%d%H", os.time())
+end
+
+local function get_store_key2()
+ return os.date("%Y%m%d%H", ngx.time())
+end
+
+
+local function get_end_time()
+ local s_time = os.time()
+ local n_date = os.date("*t",s_time + 86400)
+ n_date.hour = 0
+ n_date.min = 0
+ n_date.sec = 0
+ local d_time = os.time(n_date)
+ return d_time - s_time
+end
+
+
+
+
+local function get_end_time2()
+ local s_time = ngx.time()
+ local n_date = os.date("*t",s_time + 86400)
+ n_date.hour = 0
+ n_date.min = 0
+ n_date.sec = 0
+ local d_time = ngx.time(n_date)
+ return d_time - s_time
+end
+
+local function get_update_field(field, value)
+ return field.."="..field.."+"..value
+end
+
+local function get_update_field2(field, value)
+ return field.."="..field.."+"..tostring(value)
+end
+
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e3
+for i = 1, N do
+ get_store_key()
+end
+ngx.update_time()
+
+ngx.say("get_store_key elapsed: ", (ngx.now() - begin) / N)
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e3
+for i = 1, N do
+ get_store_key2()
+end
+ngx.update_time()
+
+ngx.say("get_store_key2 elapsed: ", (ngx.now() - begin) / N)
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e5
+for i = 1, N do
+ get_end_time()
+end
+ngx.update_time()
+
+ngx.say("get_end_time elapsed: ", (ngx.now() - begin) / N)
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e5
+for i = 1, N do
+ get_end_time2()
+end
+ngx.update_time()
+
+ngx.say("get_end_time2 elapsed: ", (ngx.now() - begin) / N)
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e9
+for i = 1, N do
+ get_update_field("ss","1")
+end
+ngx.update_time()
+
+ngx.say("get_update_field elapsed: ", (ngx.now() - begin) / N)
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e9
+for i = 1, N do
+ get_update_field2("ss",1)
+end
+ngx.update_time()
+
+ngx.say("get_update_field2 elapsed: ", (ngx.now() - begin) / N)
+
diff --git a/plugins/webstats/t/bench/test_today.lua b/plugins/webstats/t/bench/test_today.lua
new file mode 100644
index 000000000..433710420
--- /dev/null
+++ b/plugins/webstats/t/bench/test_today.lua
@@ -0,0 +1,33 @@
+
+local function target()
+ ngx.re.find("hello, world.", [[\w+\.]], "jo")
+end
+for i = 1, 100 do
+ target()
+end
+-- 以上为预热操作
+collectgarbage()
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e6
+for i = 1, N do
+ os.date("%Y%m%d")
+ -- ngx.say(t)
+end
+ngx.update_time()
+
+ngx.say("os.date elapsed: ", (ngx.now() - begin) / N)
+
+
+ngx.update_time()
+local begin = ngx.now()
+local N = 1e6
+for i = 1, N do
+ ngx.re.gsub(ngx.today(),'-','')
+ -- ngx.say(t)
+end
+ngx.update_time()
+
+ngx.say("ngx.today() elapsed: ", (ngx.now() - begin) / N)
diff --git a/plugins/webstats/t/index.py b/plugins/webstats/t/index.py
new file mode 100644
index 000000000..3b89c9149
--- /dev/null
+++ b/plugins/webstats/t/index.py
@@ -0,0 +1,122 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import json
+
+import os
+import sys
+import time
+import string
+import json
+import hashlib
+import shlex
+import datetime
+import subprocess
+import re
+from random import Random
+
+
+TEST_URL = "http://t1.cn/"
+# TEST_URL = "https://www.zzzvps.com/"
+
+
+def httpGet(url, timeout=10):
+ import urllib.request
+
+ try:
+ req = urllib.request.urlopen(url, timeout=timeout)
+ result = req.read().decode('utf-8')
+ return result
+
+ except Exception as e:
+ return str(e)
+
+
+def httpPost(url, data, timeout=10):
+ """
+ 发送POST请求
+ @url 被请求的URL地址(必需)
+ @data POST参数,可以是字符串或字典(必需)
+ @timeout 超时时间默认60秒
+ return string
+ """
+ if sys.version_info[0] == 2:
+ try:
+ import urllib
+ import urllib2
+ import ssl
+ ssl._create_default_https_context = ssl._create_unverified_context
+ data = urllib.urlencode(data)
+ req = urllib2.Request(url, data)
+ response = urllib2.urlopen(req, timeout=timeout)
+ return response.read()
+ except Exception as ex:
+ return str(ex)
+ else:
+ try:
+ import urllib.request
+ import ssl
+ try:
+ ssl._create_default_https_context = ssl._create_unverified_context
+ except:
+ pass
+ data = urllib.parse.urlencode(data).encode('utf-8')
+ req = urllib.request.Request(url, data)
+ response = urllib.request.urlopen(req, timeout=timeout)
+ result = response.read()
+ if type(result) == bytes:
+ result = result.decode('utf-8')
+ return result
+ except Exception as ex:
+ return str(ex)
+
+
+def httpGet__UA(url, ua, timeout=10):
+ import urllib.request
+ headers = {'user-agent': ua}
+ try:
+ req = urllib.request.Request(url, headers=headers)
+ response = urllib.request.urlopen(req)
+ result = response.read().decode('utf-8')
+ return result
+
+ except Exception as e:
+ return str(e)
+
+
+def test_OK():
+ '''
+ 目录保存
+ '''
+ url = TEST_URL + "ok.txt"
+ print("ok test start")
+ url_val = httpGet__UA(
+ url, "Mozilla / 5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit / 537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36")
+ print(url_val)
+ print("ok test end")
+
+
+def test_Spider():
+ '''
+ 目录保存
+ '''
+ url = TEST_URL + "ok.txt"
+ print("spider test start")
+ url_val = httpGet__UA(
+ url, "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5249.103 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)")
+ print(url_val)
+ print("spider test end")
+
+
+def test_start():
+ test_OK()
+ test_Spider()
+
+
+if __name__ == "__main__":
+ os.system('cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/webstats && sh install.sh uninstall 0.2.2 && sh install.sh install 0.2.2')
+ os.system('cd /Users/midoks/Desktop/mwdev/server/mdserver-web/ && python3 plugins/openresty/index.py stop && python3 plugins/openresty/index.py start')
+ test_start()
diff --git a/plugins/webstats/t/test.sh b/plugins/webstats/t/test.sh
new file mode 100755
index 000000000..bf823a160
--- /dev/null
+++ b/plugins/webstats/t/test.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+python3 index.py
+
diff --git a/plugins/webstats/tool_migrate.py b/plugins/webstats/tool_migrate.py
new file mode 100644
index 000000000..c0febe2e7
--- /dev/null
+++ b/plugins/webstats/tool_migrate.py
@@ -0,0 +1,209 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import json
+
+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
+from utils.crontab import crontab as MwCrontab
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'webstats'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getConf():
+ conf = getServerDir() + "/lua/config.json"
+ return conf
+
+
+def getGlobalConf():
+ conf = getConf()
+ content = mw.readFile(conf)
+ result = json.loads(content)
+ return result
+
+
+def pSqliteDb(dbname='web_logs', site_name='unset', fn="logs"):
+
+ db_dir = getServerDir() + '/logs/' + site_name
+ if not os.path.exists(db_dir):
+ mw.execShell('mkdir -p ' + db_dir)
+
+ name = fn
+ file = db_dir + '/' + name + '.db'
+
+ if not os.path.exists(file):
+ conn = mw.M(dbname).dbPos(db_dir, name)
+ sql = mw.readFile(getPluginDir() + '/conf/init.sql')
+ sql_list = sql.split(';')
+ for index in range(len(sql_list)):
+ conn.execute(sql_list[index], ())
+ else:
+ conn = mw.M(dbname).dbPos(db_dir, name)
+
+ conn.execute("PRAGMA synchronous = 0", ())
+ conn.execute("PRAGMA page_size = 4096", ())
+ conn.execute("PRAGMA journal_mode = wal", ())
+
+ conn.autoTextFactory()
+
+ # conn.text_factory = lambda x: str(x, encoding="utf-8", errors='ignore')
+ # conn.text_factory = lambda x: unicode(x, "utf-8", "ignore")
+ return conn
+
+
+def migrateSiteHotLogs(site_name, query_date):
+ print(site_name, query_date)
+
+ migrating_flag = getServerDir() + "/logs/%s/migrating" % site_name
+ hot_db = getServerDir() + "/logs/%s/logs.db" % site_name
+ hot_db_tmp = getServerDir() + "/logs/%s/logs_tmp.db" % site_name
+ history_logs_db = getServerDir() + "/logs/%s/history_logs.db" % site_name
+ # 1. copy to tmp file
+ try:
+ import shutil
+ print("coping {} to {} ...".format(hot_db, hot_db_tmp))
+ mw.writeFile(migrating_flag, "yes")
+ time.sleep(3)
+ shutil.copy(hot_db, hot_db_tmp)
+ if not os.path.exists(hot_db_tmp):
+ return mw.returnMsg(False, "migrating fail, copy tmp file!")
+ except:
+ return mw.returnMsg(False, "{} migrating fail.".format(site_name))
+ finally:
+ if os.path.exists(migrating_flag):
+ os.remove(migrating_flag)
+
+ # 2. 从临时备份中迁移热日志数据到历史日志
+ print("begin tmp to hot log data ...")
+ try:
+ print("history file: {}".format(history_logs_db))
+ logs_conn = pSqliteDb('web_log', site_name, 'logs_tmp')
+ history_logs_conn = pSqliteDb('web_log', site_name, 'history_logs')
+
+ hot_db_columns = logs_conn.originExecute(
+ "PRAGMA table_info([web_logs])")
+ _columns = ",".join([c[1] for c in hot_db_columns if c[1] != "id"])
+ query_start = 0
+ todayTime = time.strftime('%Y-%m-%d 00:00:00', time.localtime())
+ todayUt = int(time.mktime(time.strptime(
+ todayTime, "%Y-%m-%d %H:%M:%S")))
+
+ logs_sql = "select {} from web_logs where time<{}".format(
+ _columns, todayUt)
+ selector = logs_conn.originExecute(logs_sql)
+ log = selector.fetchone()
+ while log:
+ params = ""
+ for field in log:
+ if params:
+ params += ","
+ if field is None:
+ field = "\'\'"
+ elif type(field) == str:
+ field = "\'" + field.replace("\'", "\"") + "\'"
+ params += str(field)
+ insert_sql = "insert into web_logs(" + \
+ _columns + ") values(" + params + ")"
+ history_logs_conn.execute(insert_sql)
+ log = selector.fetchone()
+
+ print("sorting historical data, this action takes a long time...")
+ history_logs_conn.execute("VACUUM;")
+
+ gcfg = getGlobalConf()
+ save_day = gcfg['global']["save_day"]
+ print("delete historical data {} days ago...".format(save_day))
+ time_now = time.localtime()
+ save_timestamp = time.mktime((time_now.tm_year, time_now.tm_mon, time_now.tm_mday - save_day, 0, 0, 0, 0, 0, 0))
+ delete_sql = "delete from web_logs where time <= {}".format(
+ save_timestamp)
+ print('delete history_logs')
+ print(delete_sql)
+ history_logs_conn.execute(delete_sql)
+ history_logs_conn.commit()
+
+ # 3. delete merged data and clean up statistics
+ print("delete merged thermal data...")
+ mw.writeFile(migrating_flag, "yes")
+
+ hot_db_conn = pSqliteDb('web_logs', site_name)
+ del_hot_log = "delete from web_logs where time<{}".format(todayUt)
+ print(del_hot_log)
+ r = hot_db_conn.execute(del_hot_log)
+ print("delete:", r)
+ print("deleting statistics over 180 days...")
+ save_time_key = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 180 * 86400))
+
+ del_request_stat_sql = "delete from request_stat where time<={}".format(
+ save_time_key)
+ hot_db_conn.execute(del_request_stat_sql)
+
+ hot_db_conn.execute(
+ "delete from spider_stat where time<={}".format(save_time_key))
+ hot_db_conn.execute(
+ "delete from client_stat where time<={}".format(save_time_key))
+ hot_db_conn.execute(
+ "delete from referer_stat where time<={}".format(save_time_key))
+ hot_db_conn.commit()
+ print("clean up the hot database...")
+ hot_db_conn.execute("VACUUM;")
+ hot_db_conn.commit()
+
+ if os.path.exists(migrating_flag):
+ os.remove(migrating_flag)
+ except Exception as e:
+ if site_name:
+ print("{} logs to history error:{}".format(site_name, e))
+ else:
+ print("logs to history error:{}".format(e))
+ finally:
+ if os.path.exists(hot_db_tmp):
+ os.remove(hot_db_tmp)
+
+ print("{} logs migrate ok.".format(site_name))
+
+ if not mw.isAppleSystem():
+ mw.execShell("chown -R www:www " + getServerDir())
+
+ mw.opWeb('restart')
+ return mw.returnMsg(True, "{} logs migrate ok".format(site_name))
+
+
+def migrateHotLogs(query_date="today"):
+ print("begin migrate hot logs")
+ sites = mw.M('sites').field('name').order("add_time").select()
+
+ unset_site = {"name": "unset"}
+ sites.append(unset_site)
+
+ # migrateSiteHotLogs('t1.cn', query_date)
+
+ for site_info in sites:
+ # print(site_info['name'])
+ site_name = site_info["name"]
+ migrate_res = migrateSiteHotLogs(site_name, query_date)
+ if not migrate_res["status"]:
+ print(migrate_res["msg"])
+ print("end migrate hot logs")
diff --git a/plugins/webstats/tool_task.py b/plugins/webstats/tool_task.py
new file mode 100644
index 000000000..95f923e28
--- /dev/null
+++ b/plugins/webstats/tool_task.py
@@ -0,0 +1,132 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import json
+
+
+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
+from utils.crontab import crontab as MwCrontab
+
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'webstats'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getTaskConf():
+ conf = getServerDir() + "/task_config.json"
+ return conf
+
+
+def getConfigData():
+ conf = getTaskConf()
+ if os.path.exists(conf):
+ return json.loads(mw.readFile(getTaskConf()))
+ return {
+ "task_id": -1,
+ "task_list": ["migrate_hot_logs"],
+ "default_execute_hour": 3,
+ "default_execute_minute": 15,
+ }
+
+
+def createBgTask():
+ cfg = getConfigData()
+ name = "[勿删]网站统计插件定时任务"
+ res = mw.M("crontab").field("id, name").where("name=?", (name,)).find()
+ if res:
+ return True
+
+ if "task_id" in cfg.keys() and cfg["task_id"] > 0:
+ res = mw.M("crontab").field("id, name").where("id=?", (cfg["task_id"],)).find()
+ if res and res["id"] == cfg["task_id"]:
+ print("计划任务已经存在!")
+ return True
+
+
+ cmd = "cd " + mw.getPanelDir() + " && nice -n 10 python3 " + getPluginDir() + "/tool_task.py execute"
+ params = {
+ 'name': name,
+ 'type': 'day',
+ 'week': "",
+ 'where1': "",
+ 'hour': cfg['default_execute_hour'],
+ 'minute': cfg['default_execute_minute'],
+ 'save': "",
+ 'backup_to': "",
+ 'stype': "toShell",
+ 'sname': '',
+ 'sbody': cmd,
+ 'url_address': '',
+ }
+
+ task_id = MwCrontab.instance().add(params)
+ if task_id > 0:
+ cfg["task_id"] = task_id
+ mw.writeFile(getTaskConf(), json.dumps(cfg))
+
+
+def removeBgTask():
+ cfg = getConfigData()
+ if "task_id" in cfg.keys() and cfg["task_id"] > 0:
+ res = mw.M("crontab").field("id, name").where(
+ "id=?", (cfg["task_id"],)).find()
+ if res and res["id"] == cfg["task_id"]:
+ data = MwCrontab.instance().delete(cfg["task_id"])
+ if data['status']:
+ # print(data[1])
+ cfg["task_id"] = -1
+ mw.writeFile(getTaskConf(), json.dumps(cfg))
+ return True
+ return False
+
+
+def execute():
+ try:
+ import time
+ now = time.strftime("%Y-%m-%d", time.localtime())
+ print("-" * 30)
+ cfg = getConfigData()
+ task_list = cfg["task_list"]
+ for task in task_list:
+ # print(task)
+ if task == "migrate_hot_logs":
+ try:
+ import tool_migrate
+ tool_migrate.migrateHotLogs("yesterday")
+ except Exception as e:
+ print(e)
+ print(now)
+ print("-" * 30)
+ except Exception as e:
+ print(e)
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ action = sys.argv[1]
+ if action == "execute":
+ execute()
+ elif action == "remove":
+ removeBgTask()
+ elif action == "add":
+ createBgTask()
diff --git a/plugins/webstats/webstats_index.py b/plugins/webstats/webstats_index.py
new file mode 100644
index 000000000..0a8ab8d92
--- /dev/null
+++ b/plugins/webstats/webstats_index.py
@@ -0,0 +1,1397 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import json
+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
+from utils.crontab import crontab as MwCrontab
+
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'webstats'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+sys.path.append(getPluginDir() + "/class")
+from LuaMaker import LuaMaker
+
+
+def listToLuaFile(path, lists):
+ content = LuaMaker.makeLuaTable(lists)
+ content = "return " + content
+ mw.writeFile(path, content)
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getConf():
+ conf = getServerDir() + "/lua/config.json"
+ return conf
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+
+ 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 luaConf():
+ return mw.getServerDir() + '/web_conf/nginx/vhost/webstats.conf'
+
+
+def status():
+ path = luaConf()
+ if not os.path.exists(path):
+ return 'stop'
+ return 'start'
+
+
+def loadLuaFile(name):
+ lua_dir = getServerDir() + "/lua"
+ lua_dst = lua_dir + "/" + name
+
+ if not os.path.exists(lua_dst):
+ lua_tpl = getPluginDir() + '/lua/' + name
+ content = mw.readFile(lua_tpl)
+ content = content.replace('{$SERVER_APP}', getServerDir())
+ content = content.replace('{$ROOT_PATH}', mw.getServerDir())
+ mw.writeFile(lua_dst, content)
+
+
+def loadLuaFileReload(name):
+ lua_dir = getServerDir() + "/lua"
+ lua_dst = lua_dir + "/" + name
+
+ lua_tpl = getPluginDir() + '/lua/' + name
+ content = mw.readFile(lua_tpl)
+ content = content.replace('{$SERVER_APP}', getServerDir())
+ content = content.replace('{$ROOT_PATH}', mw.getServerDir())
+ mw.writeFile(lua_dst, content)
+
+
+def loadConfigFile():
+ lua_dir = getServerDir() + "/lua"
+ conf_tpl = getPluginDir() + "/conf/config.json"
+
+ content = mw.readFile(conf_tpl)
+ content = json.loads(content)
+
+ dst_conf_json = getServerDir() + "/lua/config.json"
+ if not os.path.exists(dst_conf_json):
+ mw.writeFile(dst_conf_json, json.dumps(content))
+
+ dst_conf_lua = getServerDir() + "/lua/webstats_config.lua"
+ if not os.path.exists(dst_conf_lua):
+ listToLuaFile(dst_conf_lua, content)
+
+
+# def loadConfigFileReload():
+# -- 配置生活或可使用
+# lua_dir = getServerDir() + "/lua"
+# conf_tpl = getPluginDir() + "/conf/config.json"
+
+# content = mw.readFile(conf_tpl)
+# content = json.loads(content)
+
+# dst_conf_json = getServerDir() + "/lua/config.json"
+# mw.writeFile(dst_conf_json, json.dumps(content))
+
+# dst_conf_lua = getServerDir() + "/lua/webstats_config.lua"
+# listToLuaFile(dst_conf_lua, content)
+
+
+def loadLuaSiteFile():
+ lua_dir = getServerDir() + "/lua"
+
+ content = makeSiteConfig()
+ for index in range(len(content)):
+ pSqliteDb('web_log', content[index]['name'])
+
+ lua_site_json = lua_dir + "/sites.json"
+ mw.writeFile(lua_site_json, json.dumps(content))
+
+ # 设置默认列表
+ default_json = lua_dir + "/default.json"
+ ddata = {}
+ dlist = []
+ for i in content:
+ dlist.append(i["name"])
+
+ dlist.append('unset')
+ ddata["list"] = dlist
+ if len(ddata["list"]) < 1:
+ ddata["default"] = "unset"
+ else:
+ ddata["default"] = dlist[0]
+
+ mw.writeFile(default_json, json.dumps(ddata))
+
+ lua_site = lua_dir + "/webstats_sites.lua"
+
+ tmp = {
+ "name": "unset",
+ "domains": [],
+ }
+ content.append(tmp)
+ listToLuaFile(lua_site, content)
+
+
+def loadDebugLogFile():
+ debug_log = getServerDir() + "/debug.log"
+ lua_dir = getServerDir() + "/lua"
+ mw.writeFile(debug_log, '')
+
+
+def pSqliteDb(dbname='web_logs', site_name='unset', name="logs"):
+
+ db_dir = getServerDir() + '/logs/' + site_name
+ if not os.path.exists(db_dir):
+ mw.execShell('mkdir -p ' + db_dir)
+
+ file = db_dir + '/' + name + '.db'
+ if not os.path.exists(file):
+ conn = mw.M(dbname).dbPos(db_dir, name)
+ sql = mw.readFile(getPluginDir() + '/conf/init.sql')
+ sql_list = sql.split(';')
+ for index in range(len(sql_list)):
+ conn.execute(sql_list[index])
+ else:
+ conn = mw.M(dbname).dbPos(db_dir, name)
+
+ conn.execute("PRAGMA synchronous = 0")
+ conn.execute("PRAGMA cache_size = 8000")
+ conn.execute("PRAGMA page_size = 32768")
+ conn.execute("PRAGMA journal_mode = wal")
+ conn.execute("PRAGMA journal_size_limit = 1073741824")
+ return conn
+
+
+def makeSiteConfig():
+ siteM = mw.M('sites')
+ domainM = mw.M('domain')
+ slist = siteM.field('id,name').where(
+ 'status=?', (1,)).order('id desc').select()
+
+ data = []
+ for s in slist:
+ tmp = {}
+ tmp['name'] = s['name']
+
+ dlist = domainM.field('id,name').where(
+ 'pid=?', (s['id'],)).order('id desc').select()
+
+ _t = []
+ for d in dlist:
+ _t.append(d['name'])
+
+ tmp['domains'] = _t
+ data.append(tmp)
+
+ return data
+
+
+def initDreplace():
+
+ service_path = getServerDir()
+
+ pSqliteDb()
+
+ path = luaConf()
+ path_tpl = getPluginDir() + '/conf/webstats.conf'
+ if not os.path.exists(path):
+ content = mw.readFile(path_tpl)
+ content = content.replace('{$SERVER_APP}', service_path)
+ content = content.replace('{$ROOT_PATH}', mw.getServerDir())
+ mw.writeFile(path, content)
+
+ # 已经安装的
+ al_config = getServerDir() + "/lua/config.json"
+ if os.path.exists(al_config):
+ tmp = json.loads(mw.readFile(al_config))
+ if tmp['global']['record_post_args'] or tmp['global']['record_get_403_args']:
+ openLuaNeedRequestBody()
+ else:
+ closeLuaNeedRequestBody()
+
+ lua_dir = getServerDir() + "/lua"
+ if not os.path.exists(lua_dir):
+ mw.execShell('mkdir -p ' + lua_dir)
+
+ log_path = getServerDir() + "/logs"
+ if not os.path.exists(log_path):
+ mw.execShell('mkdir -p ' + log_path)
+
+ file_list = [
+ 'webstats_common.lua',
+ 'webstats_log.lua',
+ ]
+
+ for fl in file_list:
+ loadLuaFile(fl)
+
+ loadConfigFile()
+ loadLuaSiteFile()
+ loadDebugLogFile()
+
+ if not mw.isAppleSystem():
+ mw.execShell("chown -R www:www " + getServerDir())
+ return 'ok'
+
+
+def luaRestart():
+ mw.opWeb("stop")
+ mw.opWeb("start")
+
+
+def start():
+ initDreplace()
+
+ import tool_task
+ tool_task.createBgTask()
+
+ # issues:326
+ luaRestart()
+ return 'ok'
+
+
+def stop():
+ path = luaConf()
+ if os.path.exists(path):
+ os.remove(path)
+
+ import tool_task
+ tool_task.removeBgTask()
+
+ luaRestart()
+ return 'ok'
+
+
+def restart():
+ initDreplace()
+
+ luaRestart()
+ return 'ok'
+
+
+def reload():
+ initDreplace()
+
+ file_list = [
+ 'webstats_common.lua',
+ 'webstats_log.lua',
+ ]
+ for fl in file_list:
+ loadLuaFileReload(fl)
+
+ loadDebugLogFile()
+
+ luaRestart()
+ return 'ok'
+
+
+def getGlobalConf():
+ conf = getConf()
+ content = mw.readFile(conf)
+ content = json.loads(content)
+ return mw.returnJson(True, 'ok', content)
+
+
+def openLuaNeedRequestBody():
+ conf = luaConf()
+ content = mw.readFile(conf)
+ content = re.sub("lua_need_request_body (.*);",
+ 'lua_need_request_body on;', content)
+ mw.writeFile(conf, content)
+
+
+def closeLuaNeedRequestBody():
+ conf = luaConf()
+ content = mw.readFile(conf)
+ content = re.sub("lua_need_request_body (.*);",
+ 'lua_need_request_body off;', content)
+ mw.writeFile(conf, content)
+
+
+def setGlobalConf():
+ args = getArgs()
+
+ conf = getConf()
+ content = mw.readFile(conf)
+ content = json.loads(content)
+
+ open_force_get_request_body = False
+ for v in ['record_post_args', 'record_get_403_args']:
+ data = checkArgs(args, [v])
+ if data[0]:
+ rval = False
+ if args[v] == "true":
+ rval = True
+ open_force_get_request_body = True
+
+ content['global'][v] = rval
+
+ # 开启强制获取日志配置
+ if open_force_get_request_body:
+ openLuaNeedRequestBody()
+ else:
+ closeLuaNeedRequestBody()
+
+ for v in ['ip_top_num', 'uri_top_num', 'save_day']:
+ data = checkArgs(args, [v])
+ if data[0]:
+ content['global'][v] = int(args[v])
+
+ for v in ['cdn_headers', 'exclude_extension', 'exclude_status', 'exclude_ip']:
+ data = checkArgs(args, [v])
+ if data[0]:
+ content['global'][v] = args[v].split("\\n")
+
+ data = checkArgs(args, ['exclude_url'])
+ if data[0]:
+ exclude_url = args['exclude_url'].strip(";")
+ exclude_url_val = []
+ if exclude_url != "":
+ exclude_url_list = exclude_url.split(";")
+ for i in exclude_url_list:
+ t = i.split("|")
+ val = {}
+ val['mode'] = t[0]
+ val['url'] = t[1]
+ exclude_url_val.append(val)
+ content['global']['exclude_url'] = exclude_url_val
+
+ mw.writeFile(conf, json.dumps(content))
+ conf_lua = getServerDir() + "/lua/webstats_config.lua"
+ listToLuaFile(conf_lua, content)
+ luaRestart()
+ return mw.returnJson(True, '设置成功')
+
+
+def getSiteConf():
+ args = getArgs()
+
+ check = checkArgs(args, ['site'])
+ if not check[0]:
+ return check[1]
+
+ domain = args['site']
+ conf = getConf()
+ content = mw.readFile(conf)
+ content = json.loads(content)
+
+ site_conf = {}
+ if domain in content:
+ site_conf = content[domain]
+ else:
+ site_conf["cdn_headers"] = content['global']['cdn_headers']
+ site_conf["exclude_extension"] = content['global']['exclude_extension']
+ site_conf["exclude_status"] = content['global']['exclude_status']
+ site_conf["exclude_ip"] = content['global']['exclude_ip']
+ site_conf["exclude_url"] = content['global']['exclude_url']
+ site_conf["record_post_args"] = content['global']['record_post_args']
+ site_conf["record_get_403_args"] = content[
+ 'global']['record_get_403_args']
+
+ return mw.returnJson(True, 'ok', site_conf)
+
+
+def setSiteConf():
+ args = getArgs()
+ check = checkArgs(args, ['site'])
+ if not check[0]:
+ return check[1]
+
+ domain = args['site']
+ conf = getConf()
+ content = mw.readFile(conf)
+ content = json.loads(content)
+
+ site_conf = {}
+ if domain in content:
+ site_conf = content[domain]
+ else:
+ site_conf["cdn_headers"] = content['global']['cdn_headers']
+ site_conf["exclude_extension"] = content['global']['exclude_extension']
+ site_conf["exclude_status"] = content['global']['exclude_status']
+ site_conf["exclude_ip"] = content['global']['exclude_ip']
+ site_conf["exclude_url"] = content['global']['exclude_url']
+ site_conf["record_post_args"] = content['global']['record_post_args']
+ site_conf["record_get_403_args"] = content[
+ 'global']['record_get_403_args']
+
+ for v in ['record_post_args', 'record_get_403_args']:
+ data = checkArgs(args, [v])
+ if data[0]:
+ rval = False
+ if args[v] == "true":
+ rval = True
+ site_conf[v] = rval
+
+ for v in ['ip_top_num', 'uri_top_num', 'save_day']:
+ data = checkArgs(args, [v])
+ if data[0]:
+ site_conf[v] = int(args[v])
+
+ for v in ['cdn_headers', 'exclude_extension', 'exclude_status', 'exclude_ip']:
+ data = checkArgs(args, [v])
+ if data[0]:
+ site_conf[v] = args[v].strip().split("\\n")
+
+ data = checkArgs(args, ['exclude_url'])
+ if data[0]:
+ exclude_url = args['exclude_url'].strip(";")
+ exclude_url_val = []
+ if exclude_url != "":
+ exclude_url_list = exclude_url.split(";")
+ for i in exclude_url_list:
+ t = i.split("|")
+ val = {}
+ val['mode'] = t[0]
+ val['url'] = t[1]
+ exclude_url_val.append(val)
+ site_conf['exclude_url'] = exclude_url_val
+
+ content[domain] = site_conf
+
+ mw.writeFile(conf, json.dumps(content))
+ conf_lua = getServerDir() + "/lua/webstats_config.lua"
+ listToLuaFile(conf_lua, content)
+ luaRestart()
+ return mw.returnJson(True, '设置成功')
+
+
+def getSiteListData():
+ lua_dir = getServerDir() + "/lua"
+ path = lua_dir + "/default.json"
+ data = mw.readFile(path)
+ return json.loads(data)
+
+
+def getDefaultSite():
+ data = getSiteListData()
+ return mw.returnJson(True, 'OK', data)
+
+
+def setDefaultSite(name):
+ lua_dir = getServerDir() + "/lua"
+ path = lua_dir + "/default.json"
+ data = mw.readFile(path)
+ data = json.loads(data)
+ data['default'] = name
+ mw.writeFile(path, json.dumps(data))
+ return mw.returnJson(True, 'OK')
+
+
+def toSumField(sql):
+ l = sql.split(",")
+ field = ""
+ for x in l:
+ field += "sum(" + x + ") as " + x + ","
+ field = field.strip(',')
+ return field
+
+
+def getSiteStatInfo(domain, query_date):
+ conn = pSqliteDb('request_stat', domain)
+ conn = conn.where("1=1", ())
+
+ field = 'time,req,pv,uv,ip,length'
+ field_sum = toSumField(field.replace("time,", ""))
+
+ time_field = "substr(time,1,6),"
+
+ field_sum = time_field + field_sum
+ conn = conn.field(field_sum)
+ if query_date == "today":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 0 * 86400))
+ conn.andWhere("time >= ?", (todayTime,))
+ elif query_date == "yesterday":
+ startTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 1 * 86400))
+ endTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time()))
+ conn.andWhere("time>=? and time<=?", (startTime, endTime))
+ elif query_date == "l7":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 7 * 86400))
+ conn.andWhere("time >= ?", (todayTime,))
+ elif query_date == "l30":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 30 * 86400))
+ conn.andWhere("time >= ?", (todayTime,))
+ else:
+ exlist = query_date.split("-")
+ start = time.strftime(
+ '%Y%m%d00', time.localtime(int(exlist[0])))
+ end = time.strftime(
+ '%Y%m%d23', time.localtime(int(exlist[1])))
+ conn.andWhere("time >= ? and time <= ? ", (start, end,))
+
+ # 统计总数
+ stat_list = conn.inquiry(field)
+ del(stat_list[0]['time'])
+ return stat_list[0]
+
+
+def getOverviewList():
+ args = getArgs()
+ check = checkArgs(args, ['site', 'query_date', 'order'])
+ if not check[0]:
+ return check[1]
+
+ domain = args['site']
+ query_date = args['query_date']
+ order = args['order']
+
+ setDefaultSite(domain)
+ conn = pSqliteDb('request_stat', domain)
+ conn = conn.where("1=1", ())
+
+ field = 'time,req,pv,uv,ip,length'
+ field_sum = toSumField(field.replace("time,", ""))
+
+ time_field = "substr(time,1,8),"
+ if order == "hour":
+ time_field = "substr(time,9,10),"
+
+ field_sum = time_field + field_sum
+ conn = conn.field(field_sum)
+ if query_date == "today":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 0 * 86400))
+ conn.andWhere("time >= ?", (todayTime,))
+ elif query_date == "yesterday":
+ startTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 1 * 86400))
+ endTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time()))
+ conn.andWhere("time>=? and time<=?", (startTime, endTime))
+ elif query_date == "l7":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 7 * 86400))
+ conn.andWhere("time >= ?", (todayTime,))
+ elif query_date == "l30":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 30 * 86400))
+ conn.andWhere("time >= ?", (todayTime,))
+ else:
+ exlist = query_date.split("-")
+ start = time.strftime(
+ '%Y%m%d00', time.localtime(int(exlist[0])))
+ end = time.strftime(
+ '%Y%m%d23', time.localtime(int(exlist[1])))
+ conn.andWhere("time >= ? and time <= ? ", (start, end,))
+
+ # 统计总数
+ stat_list = conn.inquiry(field)
+ del(stat_list[0]['time'])
+
+ # 分组统计
+ dlist = conn.group(time_field.strip(",")).inquiry(field)
+
+ data = {}
+ data['data'] = dlist
+ data['stat_list'] = stat_list[0]
+
+ return mw.returnJson(True, 'ok', data)
+
+
+def getSiteList():
+ args = getArgs()
+ check = checkArgs(args, ['query_date'])
+ if not check[0]:
+ return check[1]
+
+ query_date = args['query_date']
+
+ data = getSiteListData()
+ data_list = data["list"]
+
+ rdata = []
+ for x in data_list:
+ tmp = getSiteStatInfo(x, query_date)
+ tmp["site"] = x
+ rdata.append(tmp)
+ return mw.returnJson(True, 'ok', rdata)
+
+
+def getLogsRealtimeInfo():
+ '''
+ 实时信息
+ '''
+ import datetime
+ args = getArgs()
+ check = checkArgs(args, ['site', 'type'])
+ if not check[0]:
+ return check[1]
+
+ domain = args['site']
+ dtype = args['type']
+
+ conn = pSqliteDb('web_logs', domain)
+ timeInt = time.mktime(datetime.datetime.now().timetuple())
+
+ conn = conn.where("time>=?", (int(timeInt) - 10,))
+
+ field = 'time,body_length'
+ field_sum = toSumField(field.replace("time,", ""))
+ time_field = "substr(time,1,2) as time,"
+ time_field = time_field + field_sum
+ clist = conn.field(time_field.strip(",")).group(
+ 'substr(time,1,2)').inquiry(field)
+
+ body_count = 0
+ if len(clist) > 0:
+ body_count = clist[0]['body_length']
+
+ req_count = conn.count()
+
+ data = {}
+ data['realtime_traffic'] = body_count
+ data['realtime_request'] = req_count
+
+ return mw.returnJson(True, 'ok', data)
+
+
+def attacHistoryLogHack(conn, site_name, query_date='today'):
+ if query_date == "today":
+ return
+ db_dir = getServerDir() + '/logs/' + site_name
+ file = db_dir + '/history_logs.db'
+ if os.path.exists(file):
+ attach = "ATTACH DATABASE '" + file + "' as 'history_logs'"
+ # print(attach)
+ r = conn.originExecute(attach)
+ sql_table = "(select * from web_logs union all select * from history_logs.web_logs)"
+ # print(sql_table)
+ conn.table(sql_table)
+
+
+def get_logs_list(args):
+
+ start_time = time.time()
+
+ check = checkArgs(args, ['page', 'page_size','site', 'method',
+ 'status_code', 'spider_type', 'request_time', 'request_size', 'query_date', 'search_uri'])
+ if not check[0]:
+ return check[1]
+
+ page = int(args['page'])
+ page_size = int(args['page_size'])
+ domain = args['site']
+ tojs = args['tojs']
+ method = args['method']
+ status_code = args['status_code']
+ request_time = args['request_time']
+ request_size = args['request_size']
+ spider_type = args['spider_type']
+ query_date = args['query_date']
+ search_uri = args['search_uri']
+ referer = args['referer']
+ ip = args['ip']
+ setDefaultSite(domain)
+
+ limit = str(page_size) + ' offset ' + str(page_size * (page - 1))
+ conn = pSqliteDb('web_logs', domain)
+
+ field = 'time,ip,domain,server_name,method,is_spider,protocol,status_code,request_headers,ip_list,client_port,body_length,user_agent,referer,request_time,uri'
+ condition = ''
+ conn = conn.field(field)
+ conn = conn.where("1=1", ())
+
+ todayTime = time.strftime('%Y-%m-%d 00:00:00', time.localtime())
+ todayUt = int(time.mktime(time.strptime(todayTime, "%Y-%m-%d %H:%M:%S")))
+ if query_date == 'today':
+ conn = conn.andWhere("time>=?", (todayUt,))
+ elif query_date == "yesterday":
+ conn = conn.andWhere("time>=? and time<=?", (todayUt - 86400, todayUt))
+ elif query_date == "l7":
+ conn = conn.andWhere("time>=?", (todayUt - 7 * 86400,))
+ elif query_date == "l30":
+ conn = conn.andWhere("time>=?", (todayUt - 30 * 86400,))
+ else:
+ exlist = query_date.split("-")
+ conn = conn.andWhere("time>=? and time<=?", (exlist[0], exlist[1]))
+
+ if ip != '':
+ conn = conn.andWhere("ip=?", (ip,))
+
+ if method != "all":
+ conn = conn.andWhere("method=?", (method,))
+
+ if request_time != "all":
+ request_time_s = request_time.strip().split('-')
+ # print(request_time_s)
+ if len(request_time_s) == 2:
+ conn = conn.andWhere("request_time>=? and request_time", (request_time_s[0],request_time_s[1],))
+ if len(request_time_s) == 1:
+ conn = conn.andWhere("request_time>=?", (request_time,))
+
+ if request_size != "all":
+ request_size_s = request_size.strip().split('-')
+ # print(int(request_size_s[0])*1024)
+ if len(request_size_s) == 2:
+ conn = conn.andWhere("body_length>=? and body_length", (int(request_size_s[0])*1024,int(request_size_s[1])*1024,))
+ if len(request_size_s) == 1:
+ conn = conn.andWhere("body_length>=?", (int(request_size_s[0])*1024,))
+
+ if spider_type == "normal":
+ pass
+ elif spider_type == "only_spider":
+ conn = conn.andWhere("is_spider>?", (0,))
+ elif spider_type == "no_spider":
+ conn = conn.andWhere("is_spider=?", (0,))
+ elif int(spider_type) > 0:
+ conn = conn.andWhere("is_spider=?", (spider_type,))
+
+ if referer != 'all':
+ if referer == '1':
+ conn = conn.andWhere("referer <> ? ", ('',))
+ elif referer == '-1':
+ conn = conn.andWhere("referer is null ", ())
+
+ if search_uri != "":
+ conn = conn.andWhere("uri like '%" + search_uri + "%'", ())
+
+ if status_code != "all":
+ conn = conn.andWhere("status_code=?", (status_code,))
+
+ attacHistoryLogHack(conn, domain, query_date)
+
+ conn.changeTextFactoryToBytes()
+ clist = conn.limit(limit).order('time desc').inquiry()
+ for x in range(len(clist)):
+ req_line = clist[x]
+ for cx in req_line:
+ v = req_line[cx]
+ if type(v) == bytes:
+ try:
+ clist[x][cx] = v.decode('utf-8')
+ except Exception as e:
+ v = str(v)
+ v = v.replace("b'",'').strip("'")
+ clist[x][cx] = v
+ else:
+ clist[x][cx] = v
+
+ count_key = "count(*) as num"
+ count = conn.field(count_key).limit('').order('').inquiry()
+ # print(count)
+ count = count[0][count_key]
+
+ end_time = time.time()
+ cos_time = end_time-start_time
+
+ data = {}
+ data['cos_time'] = cos_time
+ _page = {}
+ _page['count'] = count
+ _page['p'] = page
+ _page['row'] = page_size
+ _page['tojs'] = tojs
+ data['page'] = mw.getPage(_page)
+ data['data'] = clist
+
+
+ return mw.returnJson(True, 'ok', data)
+
+
+def getLogsErrorList():
+ args = getArgs()
+ check = checkArgs(args, ['page', 'page_size',
+ 'site', 'status_code', 'query_date'])
+ if not check[0]:
+ return check[1]
+
+ page = int(args['page'])
+ page_size = int(args['page_size'])
+ domain = args['site']
+ tojs = args['tojs']
+ status_code = args['status_code']
+ query_date = args['query_date']
+ setDefaultSite(domain)
+
+ limit = str(page_size) + ' offset ' + str(page_size * (page - 1))
+ conn = pSqliteDb('web_logs', domain)
+
+ field = 'time,ip,domain,server_name,method,protocol,status_code,ip_list,client_port,body_length,user_agent,referer,request_time,uri'
+ conn = conn.field(field)
+ conn = conn.where("1=1", ())
+
+ if status_code != "all":
+ if status_code.find("x") > -1:
+ status_code = status_code.replace("x", "%")
+ conn = conn.andWhere("status_code like ?", (status_code,))
+ else:
+ conn = conn.andWhere("status_code=?", (status_code,))
+ else:
+ conn = conn.andWhere(
+ "(status_code like '50%' or status_code like '40%')", ())
+
+ todayTime = time.strftime('%Y-%m-%d 00:00:00', time.localtime())
+ todayUt = int(time.mktime(time.strptime(todayTime, "%Y-%m-%d %H:%M:%S")))
+ if query_date == 'today':
+ conn = conn.andWhere("time>=?", (todayUt,))
+ elif query_date == "yesterday":
+ conn = conn.andWhere("time>=? and time<=?", (todayUt - 86400, todayUt))
+ elif query_date == "l7":
+ conn = conn.andWhere("time>=?", (todayUt - 7 * 86400,))
+ elif query_date == "l30":
+ conn = conn.andWhere("time>=?", (todayUt - 30 * 86400,))
+ else:
+ exlist = query_date.split("-")
+ conn = conn.andWhere("time>=? and time<=?", (exlist[0], exlist[1]))
+
+ attacHistoryLogHack(conn, domain, query_date)
+
+ clist = conn.limit(limit).order('time desc').inquiry()
+ count_key = "count(*) as num"
+ count = conn.field(count_key).limit('').order('').inquiry()
+ count = count[0][count_key]
+
+ data = {}
+ _page = {}
+ _page['count'] = count
+ _page['p'] = page
+ _page['row'] = page_size
+ _page['tojs'] = tojs
+ data['page'] = mw.getPage(_page)
+ data['data'] = clist
+
+ return mw.returnJson(True, 'ok', data)
+
+
+def getClientStatList():
+ args = getArgs()
+ check = checkArgs(args, ['page', 'page_size',
+ 'site', 'query_date'])
+ if not check[0]:
+ return check[1]
+
+ page = int(args['page'])
+ page_size = int(args['page_size'])
+ domain = args['site']
+ tojs = args['tojs']
+ query_date = args['query_date']
+ setDefaultSite(domain)
+
+ conn = pSqliteDb('client_stat', domain)
+ stat = pSqliteDb('client_stat', domain)
+
+ # 列表
+ limit = str(page_size) + ' offset ' + str(page_size * (page - 1))
+ field = 'time,weixin,android,iphone,mac,windows,linux,edeg,firefox,msie,metasr,qh360,theworld,tt,maxthon,opera,qq,uc,pc2345,safari,chrome,machine,mobile,other'
+ field_sum = toSumField(field.replace("time,", ""))
+ time_field = "substr(time,1,8),"
+ field_sum = time_field + field_sum
+
+ stat = stat.field(field_sum)
+ if query_date == "today":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 0 * 86400))
+ stat.where("time >= ?", (todayTime,))
+ elif query_date == "yesterday":
+ startTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 1 * 86400))
+ endTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time()))
+ stat.where("time>=? and time<=?", (startTime, endTime))
+ elif query_date == "l7":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 7 * 86400))
+ stat.where("time >= ?", (todayTime,))
+ elif query_date == "l30":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 30 * 86400))
+ stat.where("time >= ?", (todayTime,))
+ else:
+ exlist = query_date.split("-")
+ start = time.strftime(
+ '%Y%m%d00', time.localtime(int(exlist[0])))
+ end = time.strftime(
+ '%Y%m%d23', time.localtime(int(exlist[1])))
+ stat.where("time >= ? and time <= ? ", (start, end,))
+
+ # 图表数据
+ statlist = stat.group('substr(time,1,4)').inquiry(field)
+
+ if len(statlist) > 0:
+ del(statlist[0]['time'])
+
+ pc = 0
+ pc_key_list = ['chrome', 'qh360', 'edeg', 'firefox', 'safari', 'msie',
+ 'metasr', 'theworld', 'tt', 'maxthon', 'opera', 'qq', 'pc2345']
+
+ for x in pc_key_list:
+ pc += statlist[0][x]
+
+ mobile = 0
+ mobile_key_list = ['mobile', 'android', 'iphone', 'weixin']
+ for x in mobile_key_list:
+ mobile += statlist[0][x]
+ reqest_total = pc + mobile
+
+ sum_data = {
+ "pc": pc,
+ "mobile": mobile,
+ "reqest_total": reqest_total,
+ }
+
+ statlist = sorted(statlist[0].items(),
+ key=lambda x: x[1], reverse=True)
+ _statlist = statlist[0:10]
+ __statlist = {}
+ statlist = []
+ for x in _statlist:
+ __statlist[x[0]] = x[1]
+ statlist.append(__statlist)
+ else:
+ sum_data = {
+ "pc": 0,
+ "mobile": 0,
+ "reqest_total": 0,
+ }
+ statlist = []
+
+ # 列表数据
+ conn = conn.field(field_sum)
+ clist = conn.group('substr(time,1,8)').limit(
+ limit).order('time desc').inquiry(field)
+
+ sql = "SELECT count(*) num from (\
+ SELECT count(*) as num FROM client_stat GROUP BY substr(time,1,8)\
+ )"
+ result = conn.query(sql, ())
+ result = list(result)
+ count = result[0][0]
+
+ data = {}
+ _page = {}
+ _page['count'] = count
+ _page['p'] = page
+ _page['row'] = page_size
+ _page['tojs'] = tojs
+ data['page'] = mw.getPage(_page)
+ data['data'] = clist
+ data['stat_list'] = statlist
+ data['sum_data'] = sum_data
+
+ return mw.returnJson(True, 'ok', data)
+
+
+def getDateRangeList(start, end):
+ dlist = []
+ if start > end:
+ for x in list(range(start, 32, 1)):
+ dlist.append(x)
+
+ for x in list(range(1, end, 1)):
+ dlist.append(x)
+ else:
+ for x in list(range(start, end, 1)):
+ dlist.append(x)
+
+ return dlist
+
+
+def getIpStatList():
+ args = getArgs()
+ check = checkArgs(args, ['site', 'query_date'])
+ if not check[0]:
+ return check[1]
+
+ domain = args['site']
+ tojs = args['tojs']
+ query_date = args['query_date']
+ setDefaultSite(domain)
+
+ conn = pSqliteDb('ip_stat', domain)
+
+ origin_field = "ip,day,flow"
+
+ if query_date == "today":
+ ftime = time.localtime(time.time())
+ day = ftime.tm_mday
+
+ field_day = "day" + str(day)
+ field_flow = "flow" + str(day)
+ # print(field_day, field_flow)
+
+ field = "ip," + field_day + ' as day,' + field_flow + " as flow"
+
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+
+ elif query_date == "yesterday":
+
+ ftime = time.localtime(time.time() - 86400)
+ day = ftime.tm_mday
+
+ field_day = "day" + str(day)
+ field_flow = "flow" + str(day)
+
+ field = "ip," + field_day + ' as day,' + field_flow + " as flow"
+
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+ elif query_date == "l7":
+
+ field_day = ""
+ field_flow = ""
+
+ now_time = time.localtime(time.time())
+ end_day = now_time.tm_mday
+
+ start_time = time.localtime(time.time() - 7 * 86400)
+ start_day = start_time.tm_mday
+
+ rlist = getDateRangeList(start_day, end_day)
+
+ for x in rlist:
+ field_day += "+cast(day" + str(x) + " as TEXT)"
+ field_flow += "+cast(flow" + str(x) + " as TEXT)"
+
+ field_day = field_day.strip("+")
+ field_flow = field_flow.strip("+")
+
+ field = "ip,(" + field_day + ') as day,(' + field_flow + ") as flow"
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+
+ elif query_date == "l30":
+
+ field_day = ""
+ field_flow = ""
+
+ for x in list(range(1, 32, 1)):
+ field_day += "+cast(day" + str(x) + " as TEXT)"
+ field_flow += "+cast(flow" + str(x) + " as TEXT)"
+
+ field_day = field_day.strip("+")
+ field_flow = field_flow.strip("+")
+
+ # print(field_day)
+ # print(field_flow)
+ field = "ip,(" + field_day + ') as day,(' + field_flow + ") as flow"
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+
+ clist = conn.order("flow desc").limit("50").inquiry(origin_field)
+ # print(clist)
+
+ total_req = 0
+ total_flow = 0
+
+ gepip_mmdb = getServerDir() + '/GeoLite2-City.mmdb'
+ geoip_exists = False
+ if os.path.exists(gepip_mmdb):
+ import geoip2.database
+ reader = geoip2.database.Reader(gepip_mmdb)
+ geoip_exists = True
+ # response = reader.city("172.70.206.144")
+ # print(response.country.names["zh-CN"])
+ # print(response.subdivisions.most_specific.names["zh-CN"])
+ # print(response.city.names["zh-CN"])
+
+ for x in clist:
+ total_req += x['day']
+ total_flow += x['flow']
+
+ for i in range(len(clist)):
+ clist[i]['day_rate'] = round((clist[i]['day'] / total_req) * 100, 2)
+ clist[i]['flow_rate'] = round((clist[i]['flow'] / total_flow) * 100, 2)
+ ip = clist[i]['ip']
+
+ if ip == "127.0.0.1":
+ clist[i]['area'] = "本地"
+ elif geoip_exists:
+ try:
+ response = reader.city(ip)
+ country = response.country.names["zh-CN"]
+
+ # print(ip, response.subdivisions)
+ _subdivisions = response.subdivisions
+ try:
+ if len(_subdivisions) < 1:
+ subdivisions = ""
+ else:
+ subdivisions = "," + response.subdivisions.most_specific.names[
+ "zh-CN"]
+ except Exception as e:
+ subdivisions = ""
+
+ try:
+ if 'zh-CN' in response.city.names:
+ city = "," + response.city.names["zh-CN"]
+ else:
+ city = "," + response.city.names["en"]
+ except Exception as e:
+ city = ""
+
+ clist[i]['area'] = country + subdivisions + city
+ except Exception as e:
+ clist[i]['area'] = "内网?"
+
+ return mw.returnJson(True, 'ok', clist)
+
+
+def getUriStatList():
+ args = getArgs()
+ check = checkArgs(args, ['site', 'query_date'])
+ if not check[0]:
+ return check[1]
+
+ domain = args['site']
+ tojs = args['tojs']
+ query_date = args['query_date']
+ setDefaultSite(domain)
+
+ conn = pSqliteDb('uri_stat', domain)
+
+ origin_field = "uri,day,flow"
+
+ if query_date == "today":
+ ftime = time.localtime(time.time())
+ day = ftime.tm_mday
+
+ field_day = "day" + str(day)
+ field_flow = "flow" + str(day)
+ # print(field_day, field_flow)
+
+ field = "uri," + field_day + ' as day,' + field_flow + " as flow"
+
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+
+ elif query_date == "yesterday":
+
+ ftime = time.localtime(time.time() - 86400)
+ day = ftime.tm_mday
+
+ field_day = "day" + str(day)
+ field_flow = "flow" + str(day)
+
+ field = "uri," + field_day + ' as day,' + field_flow + " as flow"
+
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+ elif query_date == "l7":
+
+ field_day = ""
+ field_flow = ""
+
+ now_time = time.localtime(time.time())
+ end_day = now_time.tm_mday
+
+ start_time = time.localtime(time.time() - 7 * 86400)
+ start_day = start_time.tm_mday
+
+ rlist = getDateRangeList(start_day, end_day)
+
+ for x in rlist:
+ field_day += "+cast(day" + str(x) + " as TEXT)"
+ field_flow += "+cast(flow" + str(x) + " as TEXT)"
+
+ field_day = field_day.strip("+")
+ field_flow = field_flow.strip("+")
+
+ field = "uri,(" + field_day + ') as day,(' + field_flow + ") as flow"
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+
+ elif query_date == "l30":
+
+ field_day = ""
+ field_flow = ""
+
+ for x in list(range(1, 32, 1)):
+ field_day += "+cast(day" + str(x) + " as TEXT)"
+ field_flow += "+cast(flow" + str(x) + " as TEXT)"
+
+ field_day = field_day.strip("+")
+ field_flow = field_flow.strip("+")
+
+ # print(field_day)
+ # print(field_flow)
+ field = "uri,(" + field_day + ') as day,(' + field_flow + ") as flow"
+ conn = conn.field(field)
+ conn = conn.where("day>? and flow>?", (0, 0,))
+
+ clist = conn.order("flow desc").limit("50").inquiry(origin_field)
+
+ total_req = 0
+ total_flow = 0
+
+ for x in clist:
+ total_req += x['day']
+ total_flow += x['flow']
+
+ for i in range(len(clist)):
+ clist[i]['day_rate'] = round((clist[i]['day'] / total_req) * 100, 2)
+ clist[i]['flow_rate'] = round((clist[i]['flow'] / total_flow) * 100, 2)
+
+ return mw.returnJson(True, 'ok', clist)
+
+
+def getWebLogCount(domain, query_date):
+ conn = pSqliteDb('web_logs', domain)
+
+ todayTime = time.strftime('%Y-%m-%d 00:00:00', time.localtime())
+ todayUt = int(time.mktime(time.strptime(todayTime, "%Y-%m-%d %H:%M:%S")))
+ if query_date == 'today':
+ conn = conn.where("time>=?", (todayUt,))
+ elif query_date == "yesterday":
+ conn = conn.where("time>=? and time<=?", (todayUt - 86400, todayUt))
+ elif query_date == "l7":
+ conn = conn.where("time>=?", (todayUt - 7 * 86400,))
+ elif query_date == "l30":
+ conn = conn.where("time>=?", (todayUt - 30 * 86400,))
+ else:
+ exlist = query_date.split("-")
+ conn = conn.where("time>=? and time<=?", (exlist[0], exlist[1]))
+
+ count_key = "count(*) as num"
+ count = conn.field(count_key).limit('').order('').inquiry()
+ count = count[0][count_key]
+ return count
+
+
+def getSpiderStatList():
+ args = getArgs()
+ check = checkArgs(args, ['page', 'page_size',
+ 'site', 'query_date'])
+ if not check[0]:
+ return check[1]
+
+ page = int(args['page'])
+ page_size = int(args['page_size'])
+ domain = args['site']
+ tojs = args['tojs']
+ query_date = args['query_date']
+ setDefaultSite(domain)
+
+ conn = pSqliteDb('spider_stat', domain)
+ stat = pSqliteDb('spider_stat', domain)
+
+ total_req = getWebLogCount(domain, query_date)
+
+ # 列表
+ limit = str(page_size) + ' offset ' + str(page_size * (page - 1))
+ field = 'time,bytes,bing,soso,yahoo,sogou,google,baidu,qh360,youdao,yandex,dnspod,other'
+ field_sum = toSumField(field.replace("time,", ""))
+ time_field = "substr(time,1,8),"
+ field_sum = time_field + field_sum
+
+ stat = stat.field(field_sum)
+ if query_date == "today":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 0 * 86400))
+ stat.where("time >= ?", (todayTime,))
+ elif query_date == "yesterday":
+ startTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 1 * 86400))
+ endTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time()))
+ stat.where("time>=? and time<=?", (startTime, endTime))
+ elif query_date == "l7":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 7 * 86400))
+ stat.where("time >= ?", (todayTime,))
+ elif query_date == "l30":
+ todayTime = time.strftime(
+ '%Y%m%d00', time.localtime(time.time() - 30 * 86400))
+ stat.where("time >= ?", (todayTime,))
+ else:
+ exlist = query_date.split("-")
+ start = time.strftime(
+ '%Y%m%d00', time.localtime(int(exlist[0])))
+ end = time.strftime(
+ '%Y%m%d23', time.localtime(int(exlist[1])))
+ stat.where("time >= ? and time <= ? ", (start, end,))
+
+ # 图表数据
+ statlist = stat.group('substr(time,1,4)').inquiry(field)
+
+ if len(statlist) > 0:
+ del(statlist[0]['time'])
+
+ spider_total = 0
+ for x in statlist[0]:
+ spider_total += statlist[0][x]
+
+ sum_data = {"spider": spider_total, "reqest_total": total_req}
+ statlist = sorted(statlist[0].items(),
+ key=lambda x: x[1], reverse=True)
+ _statlist = statlist[0:9]
+ __statlist = {}
+ statlist = []
+ for x in _statlist:
+ __statlist[x[0]] = x[1]
+ statlist.append(__statlist)
+ else:
+ sum_data = {"spider": 0, "reqest_total": total_req}
+ statlist = []
+
+ # 列表数据
+ conn = conn.field(field_sum)
+ clist = conn.group('substr(time,1,8)').limit(
+ limit).order('time desc').inquiry(field)
+
+ sql = "SELECT count(*) num from (\
+ SELECT count(*) as num FROM spider_stat GROUP BY substr(time,1,8)\
+ )"
+ result = conn.query(sql, ())
+ result = list(result)
+ count = result[0][0]
+
+ data = {}
+ _page = {}
+ _page['count'] = count
+ _page['p'] = page
+ _page['row'] = page_size
+ _page['tojs'] = tojs
+ data['page'] = mw.getPage(_page)
+ data['data'] = clist
+ data['stat_list'] = statlist
+ data['sum_data'] = sum_data
+
+ return mw.returnJson(True, 'ok', data)
diff --git a/plugins/xhprof/conf/xhprof.conf b/plugins/xhprof/conf/xhprof.conf
new file mode 100755
index 000000000..92df8fb24
--- /dev/null
+++ b/plugins/xhprof/conf/xhprof.conf
@@ -0,0 +1,27 @@
+server
+{
+ listen 5858;
+ server_name 127.0.0.1;
+ index index.html index.htm index.php;
+ root {$SERVER_PATH}/xhprof/xhprof_html;
+
+ #error_page 404 /404.html;
+ include {$SERVER_PATH}/web_conf/php/conf/enable-php-{$PHP_VER}.conf;
+
+ location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
+ {
+ expires 30d;
+ }
+
+ location ~ .*\.(js|css)?$
+ {
+ expires 12h;
+ }
+
+ location ~ /\.
+ {
+ deny all;
+ }
+
+ access_log {$SERVER_PATH}/xhprof/access.log main;
+}
\ No newline at end of file
diff --git a/plugins/xhprof/ico.png b/plugins/xhprof/ico.png
new file mode 100644
index 000000000..c710c5861
Binary files /dev/null and b/plugins/xhprof/ico.png differ
diff --git a/plugins/xhprof/index.html b/plugins/xhprof/index.html
new file mode 100755
index 000000000..754cf36ef
--- /dev/null
+++ b/plugins/xhprof/index.html
@@ -0,0 +1,20 @@
+
+
\ No newline at end of file
diff --git a/plugins/xhprof/index.py b/plugins/xhprof/index.py
new file mode 100755
index 000000000..5e4b3619c
--- /dev/null
+++ b/plugins/xhprof/index.py
@@ -0,0 +1,257 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import json
+
+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
+from utils.site import sites as MwSites
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'xhprof'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+
+ return tmp
+
+
+def getConf():
+ return mw.getServerDir() + '/web_conf/nginx/vhost/xhprof.conf'
+
+
+def getPort():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'listen\s*(.*);'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+
+def getHomePage():
+ try:
+ port = getPort()
+ ip = '127.0.0.1'
+ if not mw.isAppleSystem():
+ ip = mw.getLocalIp()
+ url = 'http://' + ip + ':' + port + '/index.php'
+ return mw.returnJson(True, 'OK', url)
+ except Exception as e:
+ return mw.returnJson(False, '插件未启动!')
+
+
+def getPhpVer(expect=74):
+ php_vers = MwSites.instance().getPhpVersion()
+ v = php_vers['data']
+ for i in range(len(v)):
+ t = int(v[i]['version'])
+ if (t >= expect):
+ return str(t)
+ return str(expect)
+
+
+def getCachePhpVer():
+ cacheFile = getServerDir() + '/php.pl'
+ v = ''
+ if os.path.exists(cacheFile):
+ v = mw.readFile(cacheFile)
+ else:
+ v = getPhpVer()
+ mw.writeFile(cacheFile, v)
+ return v
+
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ php_ver = getCachePhpVer()
+ # print php_ver
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$PHP_VER}', php_ver)
+ content = content.replace('{$LOCAL_IP}', mw.getLocalIp())
+ return content
+
+
+def contentReplacePHP(content, version):
+ service_path = mw.getServerDir()
+ # print php_ver
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$PHP_VER}', version)
+ return content
+
+
+def status():
+ conf = getConf()
+ if os.path.exists(conf):
+ return 'start'
+ return 'stop'
+
+def getConfAppStart():
+ pstart = mw.getServerDir() + '/php/app_start.php'
+ return pstart
+
+
+def phpPrependFile():
+ app_start = getConfAppStart()
+ tpl = mw.getPluginDir() + '/php/conf/app_start.php'
+ content = mw.readFile(tpl)
+ content = contentReplace(content)
+ mw.writeFile(app_start, content)
+ return True
+
+def start():
+ phpPrependFile()
+
+ file_tpl = getPluginDir() + '/conf/xhprof.conf'
+ file_run = getConf()
+
+ if not os.path.exists(file_run):
+ centent = mw.readFile(file_tpl)
+ centent = contentReplace(centent)
+ mw.writeFile(file_run, centent)
+
+ mw.restartWeb()
+ return 'ok'
+
+
+def stop():
+ conf = getConf()
+ if os.path.exists(conf):
+ os.remove(conf)
+ mw.restartWeb()
+ return 'ok'
+
+
+def restart():
+ return start()
+
+
+def reload():
+ return start()
+
+
+def setPhpVer():
+ args = getArgs()
+
+ if not 'phpver' in args:
+ return 'phpver missing'
+
+ cacheFile = getServerDir() + '/php.pl'
+ mw.writeFile(cacheFile, args['phpver'])
+
+ file_tpl = getPluginDir() + '/conf/xhprof.conf'
+ file_run = getConf()
+
+ content = mw.readFile(file_tpl)
+ content = contentReplacePHP(content, args['phpver'])
+ mw.writeFile(file_run, content)
+
+ mw.restartWeb()
+ return 'ok'
+
+
+def getSetPhpVer():
+ cacheFile = getServerDir() + '/php.pl'
+ if os.path.exists(cacheFile):
+ return mw.readFile(cacheFile).strip()
+ return ''
+
+
+def getXhPort():
+ try:
+ port = getPort()
+ return mw.returnJson(True, 'OK', port)
+ except Exception as e:
+ return mw.returnJson(False, '插件未启动!')
+
+
+def setXhPort():
+ args = getArgs()
+ if not 'port' in args:
+ return mw.returnJson(False, 'port missing!')
+
+ port = args['port']
+ if port == '80':
+ return mw.returnJson(False, '80端不能使用!')
+
+ file = getConf()
+ if not os.path.exists(file):
+ return mw.returnJson(False, '插件未启动!')
+ content = mw.readFile(file)
+ rep = r'listen\s*(.*);'
+ content = re.sub(rep, "listen " + port + ';', content)
+ mw.writeFile(file, content)
+ mw.restartWeb()
+ return mw.returnJson(True, '修改成功!')
+
+
+def installPreInspection():
+ path = mw.getServerDir() + '/php'
+ if not os.path.exists(path):
+ return "先安装一个可用的PHP版本!"
+ return 'ok'
+
+if __name__ == "__main__":
+ 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 == 'install_pre_inspection':
+ print(installPreInspection())
+ elif func == 'conf':
+ print(getConf())
+ elif func == 'get_home_page':
+ print(getHomePage())
+ elif func == 'set_php_ver':
+ print(setPhpVer())
+ elif func == 'get_set_php_ver':
+ print(getSetPhpVer())
+ elif func == 'get_xhprof_port':
+ print(getXhPort())
+ elif func == 'set_xhprof_port':
+ print(setXhPort())
+ elif func == 'app_start':
+ print(getConfAppStart())
+ else:
+ print('error')
diff --git a/plugins/xhprof/info.json b/plugins/xhprof/info.json
new file mode 100755
index 000000000..f93d85531
--- /dev/null
+++ b/plugins/xhprof/info.json
@@ -0,0 +1,19 @@
+{
+ "sort": 4,
+ "title":"xhprof",
+ "tip":"soft",
+ "name":"xhprof",
+ "type":"运行环境",
+ "ps":"PHP性能瓶颈查找工具",
+ "to_ver":["1.0"],
+ "versions":["1.0"],
+ "updates":["1.0"],
+ "install_pre_inspection":true,
+ "shell":"install.sh",
+ "checks":"server/xhprof",
+ "path": "server/xhprof",
+ "author":"midoks",
+ "home":"http://pecl.php.net/package/xhprof",
+ "date":"2020-07-12",
+ "pid": "1"
+}
\ No newline at end of file
diff --git a/plugins/xhprof/install.sh b/plugins/xhprof/install.sh
new file mode 100755
index 000000000..45eabd0e1
--- /dev/null
+++ b/plugins/xhprof/install.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+sys_os=`uname`
+Install_xh()
+{
+ mkdir -p ${serverPath}/xhprof
+
+ if [ ! -d ${serverPath}/xhprof/xhprof_lib ];then
+ cp -rf $curPath/lib/* ${serverPath}/xhprof
+ fi
+
+ echo "${1}" > ${serverPath}/xhprof/version.pl
+ echo '安装完成'
+
+ if [ "$sys_os" != "Darwin" ];then
+ cd $rootPath && python3 ${rootPath}/plugins/xhprof/index.py start
+ fi
+}
+
+Uninstall_xh()
+{
+ if [ "$sys_os" != "Darwin" ];then
+ cd $rootPath && python3 ${rootPath}/plugins/xhprof/index.py stop
+ fi
+
+ rm -rf ${serverPath}/xhprof
+ cd /tmp/xhprof && rm -rf *.xhprof
+
+ if [ -f ${serverPath}/web_conf/nginx/vhost/xhprof.conf ];then
+ rm -f ${serverPath}/web_conf/nginx/vhost/xhprof.conf
+ fi
+ echo '卸载完成'
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_xh $2
+else
+ Uninstall_xh $2
+fi
diff --git a/plugins/xhprof/js/xhprof.js b/plugins/xhprof/js/xhprof.js
new file mode 100755
index 000000000..af6939fbf
--- /dev/null
+++ b/plugins/xhprof/js/xhprof.js
@@ -0,0 +1,108 @@
+function xhPost(method,args,callback){
+
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(toArrayObject(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+ $.post('/plugins/run', {name:'xhprof', func:method, args:_args}, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function xhAsyncPost(method,args){
+ var _args = null;
+ if (typeof(args) == 'string'){
+ _args = JSON.stringify(toArrayObject(args));
+ } else {
+ _args = JSON.stringify(args);
+ }
+ return syncPost('/plugins/run', {name:'xhprof', func:method, args:_args});
+}
+
+function homePage(){
+ xhPost('get_home_page', '', function(data){
+ var rdata = $.parseJSON(data.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ var con = '主页 ';
+ $(".soft-man-con").html(con);
+ });
+}
+
+//phpmyadmin切换php版本
+function phpVer(version) {
+
+ var _version = xhAsyncPost('get_set_php_ver','')
+ if (_version['data'] != ''){
+ version = _version['data'];
+ }
+
+ $.post('/site/get_php_version', function(rdata) {
+ // console.log(rdata);
+ var body = "PHP版本 ";
+ var optionSelect = '';
+ for (var i = 0; i < rdata.length; i++) {
+ optionSelect = rdata[i].version == version ? 'selected' : '';
+ body += "" + rdata[i].name + " "
+ }
+ body += ' 保存
';
+ $(".soft-man-con").html(body);
+ },'json');
+}
+
+function phpVerChange(type, msg) {
+ var phpver = $("#phpver").val();
+ xhPost('set_php_ver', 'phpver='+phpver, function(data){
+ if ( data.data == 'ok' ){
+ layer.msg('设置成功!',{icon:1,time:2000,shade: [0.3, '#000']});
+ } else {
+ layer.msg('设置失败!',{icon:2,time:2000,shade: [0.3, '#000']});
+ }
+ });
+}
+
+
+//xhprf 安全设置
+function safeConf() {
+ var data = xhAsyncPost('get_xhprof_port');
+ var rdata = $.parseJSON(data.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:2,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ var con = '\
+ 访问端口 \
+ \
+ 保存 \
+
';
+ $(".soft-man-con").html(con);
+}
+
+//修改 xhprf 端口
+function xhprofPort() {
+ var pmport = $("#pmport").val();
+ if (pmport < 80 || pmport > 65535) {
+ layer.msg('端口范围不合法!', { icon: 2 });
+ return;
+ }
+ var data = 'port=' + pmport;
+
+ xhPost('set_xhprof_port',data, function(data){
+ var rdata = $.parseJSON(data.data);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ });
+}
\ No newline at end of file
diff --git a/plugins/xhprof/lib/xhprof_html/callgraph.php b/plugins/xhprof/lib/xhprof_html/callgraph.php
new file mode 100755
index 000000000..46fc6a524
--- /dev/null
+++ b/plugins/xhprof/lib/xhprof_html/callgraph.php
@@ -0,0 +1,91 @@
+ array(XHPROF_STRING_PARAM, ''),
+
+ // source/namespace/type of run
+ 'source' => array(XHPROF_STRING_PARAM, 'xhprof'),
+
+ // the focus function, if it is set, only directly
+ // parents/children functions of it will be shown.
+ 'func' => array(XHPROF_STRING_PARAM, ''),
+
+ // image type, can be 'jpg', 'gif', 'ps', 'png'
+ 'type' => array(XHPROF_STRING_PARAM, 'png'),
+
+ // only functions whose exclusive time over the total time
+ // is larger than this threshold will be shown.
+ // default is 0.01.
+ 'threshold' => array(XHPROF_FLOAT_PARAM, 0.01),
+
+ // whether to show critical_path
+ 'critical' => array(XHPROF_BOOL_PARAM, true),
+
+ // first run in diff mode.
+ 'run1' => array(XHPROF_STRING_PARAM, ''),
+
+ // second run in diff mode.
+ 'run2' => array(XHPROF_STRING_PARAM, '')
+ );
+
+// pull values of these params, and create named globals for each param
+xhprof_param_init($params);
+
+// if invalid value specified for threshold, then use the default
+if ($threshold < 0 || $threshold > 1) {
+ $threshold = $params['threshold'][1];
+}
+
+// if invalid value specified for type, use the default
+if (!array_key_exists($type, $xhprof_legal_image_types)) {
+ $type = $params['type'][1]; // default image type.
+}
+
+$xhprof_runs_impl = new XHProfRuns_Default();
+
+if (!empty($run)) {
+ // single run call graph image generation
+ xhprof_render_image($xhprof_runs_impl, $run, $type,
+ $threshold, $func, $source, $critical);
+} else {
+ // diff report call graph image generation
+ xhprof_render_diff_image($xhprof_runs_impl, $run1, $run2,
+ $type, $threshold, $source);
+}
diff --git a/plugins/xhprof/lib/xhprof_html/css/xhprof.css b/plugins/xhprof/lib/xhprof_html/css/xhprof.css
new file mode 100755
index 000000000..c3abf8622
--- /dev/null
+++ b/plugins/xhprof/lib/xhprof_html/css/xhprof.css
@@ -0,0 +1,81 @@
+/* Copyright (c) 2009 Facebook
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+td.sorted {
+ color:#0000FF;
+}
+
+td.vbar, th.vbar {
+ text-align: right;
+ border-left:
+ solid 1px #bdc7d8;
+}
+
+td.vbbar, th.vbar {
+ text-align: right;
+ border-left:
+ solid 1px #bdc7d8;
+ color:blue;
+}
+
+/* diff reports: display regressions in red */
+td.vrbar {
+ text-align: right;
+ border-left:solid 1px #bdc7d8;
+ color:red;
+}
+
+/* diff reports: display improvements in green */
+td.vgbar {
+ text-align: right;
+ border-left: solid 1px #bdc7d8;
+ color:green;
+}
+
+td.vwbar, th.vwbar {
+ text-align: right;
+ border-left: solid 1px white;
+}
+
+td.vwlbar, th.vwlbar {
+ text-align: left;
+ border-left: solid 1px white;
+}
+
+p.blue {
+ color:blue
+}
+
+.bubble {
+ background-color:#C3D9FF
+}
+
+ul.xhprof_actions {
+ float: right;
+ padding-left: 16px;
+ list-style-image: none;
+ list-style-type: none;
+ margin:10px 10px 10px 3em;
+ position:relative;
+}
+
+ul.xhprof_actions li {
+ border-bottom:1px solid #D8DFEA;
+}
+
+ul.xhprof_actions li a:hover {
+ background:#3B5998 none repeat scroll 0 0;
+ color:#FFFFFF;
+}
diff --git a/plugins/xhprof/lib/xhprof_html/index.php b/plugins/xhprof/lib/xhprof_html/index.php
new file mode 100755
index 000000000..f21d32fd5
--- /dev/null
+++ b/plugins/xhprof/lib/xhprof_html/index.php
@@ -0,0 +1,90 @@
+ array(XHPROF_STRING_PARAM, ''),
+ 'wts' => array(XHPROF_STRING_PARAM, ''),
+ 'symbol' => array(XHPROF_STRING_PARAM, ''),
+ 'sort' => array(XHPROF_STRING_PARAM, 'wt'), // wall time
+ 'run1' => array(XHPROF_STRING_PARAM, ''),
+ 'run2' => array(XHPROF_STRING_PARAM, ''),
+ 'source' => array(XHPROF_STRING_PARAM, 'xhprof'),
+ 'all' => array(XHPROF_UINT_PARAM, 0),
+ );
+
+// pull values of these params, and create named globals for each param
+xhprof_param_init($params);
+
+/* reset params to be a array of variable names to values
+ by the end of this page, param should only contain values that need
+ to be preserved for the next page. unset all unwanted keys in $params.
+ */
+foreach ($params as $k => $v) {
+ $params[$k] = $$k;
+
+ // unset key from params that are using default values. So URLs aren't
+ // ridiculously long.
+ if ($params[$k] == $v[1]) {
+ unset($params[$k]);
+ }
+}
+
+echo "";
+
+echo "XHProf: Hierarchical Profiler Report ";
+xhprof_include_js_css();
+echo "";
+
+echo "";
+
+$vbar = ' class="vbar"';
+$vwbar = ' class="vwbar"';
+$vwlbar = ' class="vwlbar"';
+$vbbar = ' class="vbbar"';
+$vrbar = ' class="vrbar"';
+$vgbar = ' class="vgbar"';
+
+$xhprof_runs_impl = new XHProfRuns_Default();
+
+displayXHProfReport($xhprof_runs_impl, $params, $source, $run, $wts,
+ $symbol, $sort, $run1, $run2);
+
+
+echo "";
+echo "";
diff --git a/plugins/xhprof/lib/xhprof_html/jquery/indicator.gif b/plugins/xhprof/lib/xhprof_html/jquery/indicator.gif
new file mode 100755
index 000000000..9c26a717c
Binary files /dev/null and b/plugins/xhprof/lib/xhprof_html/jquery/indicator.gif differ
diff --git a/plugins/xhprof/lib/xhprof_html/jquery/jquery-1.2.6.js b/plugins/xhprof/lib/xhprof_html/jquery/jquery-1.2.6.js
new file mode 100755
index 000000000..5b8bfc2dd
--- /dev/null
+++ b/plugins/xhprof/lib/xhprof_html/jquery/jquery-1.2.6.js
@@ -0,0 +1,3549 @@
+(function(){
+/*
+ * jQuery 1.2.6 - New Wave Javascript
+ *
+ * Copyright (c) 2008 John Resig (jquery.com)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2009-03-17 18:35:18 $
+ * $Rev: 5685 $
+ */
+
+// Map over jQuery in case of overwrite
+var _jQuery = window.jQuery,
+// Map over the $ in case of overwrite
+ _$ = window.$;
+
+var jQuery = window.jQuery = window.$ = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context );
+};
+
+// A simple way to check for HTML strings or ID strings
+// (both of which we optimize for)
+var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,
+
+// Is it a simple selector
+ isSimple = /^.[^:#\[\.]*$/,
+
+// Will speed up references to undefined, and allows munging its name.
+ undefined;
+
+jQuery.fn = jQuery.prototype = {
+ init: function( selector, context ) {
+ // Make sure that a selection was provided
+ selector = selector || document;
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this[0] = selector;
+ this.length = 1;
+ return this;
+ }
+ // Handle HTML strings
+ if ( typeof selector == "string" ) {
+ // Are we dealing with HTML string or an ID?
+ var match = quickExpr.exec( selector );
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] )
+ selector = jQuery.clean( [ match[1] ], context );
+
+ // HANDLE: $("#id")
+ else {
+ var elem = document.getElementById( match[3] );
+
+ // Make sure an element was located
+ if ( elem ){
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id != match[3] )
+ return jQuery().find( selector );
+
+ // Otherwise, we inject the element directly into the jQuery object
+ return jQuery( elem );
+ }
+ selector = [];
+ }
+
+ // HANDLE: $(expr, [context])
+ // (which is just equivalent to: $(content).find(expr)
+ } else
+ return jQuery( context ).find( selector );
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) )
+ return jQuery( document )[ jQuery.fn.ready ? "ready" : "load" ]( selector );
+
+ return this.setArray(jQuery.makeArray(selector));
+ },
+
+ // The current version of jQuery being used
+ jquery: "1.2.6",
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ // The number of elements contained in the matched element set
+ length: 0,
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == undefined ?
+
+ // Return a 'clean' array
+ jQuery.makeArray( this ) :
+
+ // Return just the object
+ this[ num ];
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems ) {
+ // Build a new jQuery matched element set
+ var ret = jQuery( elems );
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Force the current matched set of elements to become
+ // the specified array of elements (destroying the stack in the process)
+ // You should use pushStack() in order to do this, but maintain the stack
+ setArray: function( elems ) {
+ // Resetting the length to 0, then using the native Array push
+ // is a super-fast way to populate an object with array-like properties
+ this.length = 0;
+ Array.prototype.push.apply( this, elems );
+
+ return this;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+ var ret = -1;
+
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem && elem.jquery ? elem[0] : elem
+ , this );
+ },
+
+ attr: function( name, value, type ) {
+ var options = name;
+
+ // Look for the case where we're accessing a style value
+ if ( name.constructor == String )
+ if ( value === undefined )
+ return this[0] && jQuery[ type || "attr" ]( this[0], name );
+
+ else {
+ options = {};
+ options[ name ] = value;
+ }
+
+ // Check to see if we're setting style values
+ return this.each(function(i){
+ // Set all the styles
+ for ( name in options )
+ jQuery.attr(
+ type ?
+ this.style :
+ this,
+ name, jQuery.prop( this, options[ name ], type, i, name )
+ );
+ });
+ },
+
+ css: function( key, value ) {
+ // ignore negative width and height values
+ if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
+ value = undefined;
+ return this.attr( key, value, "curCSS" );
+ },
+
+ text: function( text ) {
+ if ( typeof text != "object" && text != null )
+ return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
+
+ var ret = "";
+
+ jQuery.each( text || this, function(){
+ jQuery.each( this.childNodes, function(){
+ if ( this.nodeType != 8 )
+ ret += this.nodeType != 1 ?
+ this.nodeValue :
+ jQuery.fn.text( [ this ] );
+ });
+ });
+
+ return ret;
+ },
+
+ wrapAll: function( html ) {
+ if ( this[0] )
+ // The elements to wrap the target around
+ jQuery( html, this[0].ownerDocument )
+ .clone()
+ .insertBefore( this[0] )
+ .map(function(){
+ var elem = this;
+
+ while ( elem.firstChild )
+ elem = elem.firstChild;
+
+ return elem;
+ })
+ .append(this);
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ return this.each(function(){
+ jQuery( this ).contents().wrapAll( html );
+ });
+ },
+
+ wrap: function( html ) {
+ return this.each(function(){
+ jQuery( this ).wrapAll( html );
+ });
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, false, function(elem){
+ if (this.nodeType == 1)
+ this.appendChild( elem );
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, true, function(elem){
+ if (this.nodeType == 1)
+ this.insertBefore( elem, this.firstChild );
+ });
+ },
+
+ before: function() {
+ return this.domManip(arguments, false, false, function(elem){
+ this.parentNode.insertBefore( elem, this );
+ });
+ },
+
+ after: function() {
+ return this.domManip(arguments, false, true, function(elem){
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ },
+
+ end: function() {
+ return this.prevObject || jQuery( [] );
+ },
+
+ find: function( selector ) {
+ var elems = jQuery.map(this, function(elem){
+ return jQuery.find( selector, elem );
+ });
+
+ return this.pushStack( /[^+>] [^+>]/.test( selector ) || selector.indexOf("..") > -1 ?
+ jQuery.unique( elems ) :
+ elems );
+ },
+
+ clone: function( events ) {
+ // Do the clone
+ var ret = this.map(function(){
+ if ( jQuery.browser.msie && !jQuery.isXMLDoc(this) ) {
+ // IE copies events bound via attachEvent when
+ // using cloneNode. Calling detachEvent on the
+ // clone will also remove the events from the orignal
+ // In order to get around this, we use innerHTML.
+ // Unfortunately, this means some modifications to
+ // attributes in IE that are actually only stored
+ // as properties will not be copied (such as the
+ // the name attribute on an input).
+ var clone = this.cloneNode(true),
+ container = document.createElement("div");
+ container.appendChild(clone);
+ return jQuery.clean([container.innerHTML])[0];
+ } else
+ return this.cloneNode(true);
+ });
+
+ // Need to set the expando to null on the cloned set if it exists
+ // removeData doesn't work here, IE removes it from the original as well
+ // this is primarily for IE but the data expando shouldn't be copied over in any browser
+ var clone = ret.find("*").andSelf().each(function(){
+ if ( this[ expando ] != undefined )
+ this[ expando ] = null;
+ });
+
+ // Copy the events from the original to the clone
+ if ( events === true )
+ this.find("*").andSelf().each(function(i){
+ if (this.nodeType == 3)
+ return;
+ var events = jQuery.data( this, "events" );
+
+ for ( var type in events )
+ for ( var handler in events[ type ] )
+ jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data );
+ });
+
+ // Return the cloned set
+ return ret;
+ },
+
+ filter: function( selector ) {
+ return this.pushStack(
+ jQuery.isFunction( selector ) &&
+ jQuery.grep(this, function(elem, i){
+ return selector.call( elem, i );
+ }) ||
+
+ jQuery.multiFilter( selector, this ) );
+ },
+
+ not: function( selector ) {
+ if ( selector.constructor == String )
+ // test special case where just one selector is passed in
+ if ( isSimple.test( selector ) )
+ return this.pushStack( jQuery.multiFilter( selector, this, true ) );
+ else
+ selector = jQuery.multiFilter( selector, this );
+
+ var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
+ return this.filter(function() {
+ return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
+ });
+ },
+
+ add: function( selector ) {
+ return this.pushStack( jQuery.unique( jQuery.merge(
+ this.get(),
+ typeof selector == 'string' ?
+ jQuery( selector ) :
+ jQuery.makeArray( selector )
+ )));
+ },
+
+ is: function( selector ) {
+ return !!selector && jQuery.multiFilter( selector, this ).length > 0;
+ },
+
+ hasClass: function( selector ) {
+ return this.is( "." + selector );
+ },
+
+ val: function( value ) {
+ if ( value == undefined ) {
+
+ if ( this.length ) {
+ var elem = this[0];
+
+ // We need to handle select boxes special
+ if ( jQuery.nodeName( elem, "select" ) ) {
+ var index = elem.selectedIndex,
+ values = [],
+ options = elem.options,
+ one = elem.type == "select-one";
+
+ // Nothing was selected
+ if ( index < 0 )
+ return null;
+
+ // Loop through all the selected options
+ for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
+ var option = options[ i ];
+
+ if ( option.selected ) {
+ // Get the specifc value for the option
+ value = jQuery.browser.msie && !option.attributes.value.specified ? option.text : option.value;
+
+ // We don't need an array for one selects
+ if ( one )
+ return value;
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ return values;
+
+ // Everything else, we just grab the value
+ } else
+ return (this[0].value || "").replace(/\r/g, "");
+
+ }
+
+ return undefined;
+ }
+
+ if( value.constructor == Number )
+ value += '';
+
+ return this.each(function(){
+ if ( this.nodeType != 1 )
+ return;
+
+ if ( value.constructor == Array && /radio|checkbox/.test( this.type ) )
+ this.checked = (jQuery.inArray(this.value, value) >= 0 ||
+ jQuery.inArray(this.name, value) >= 0);
+
+ else if ( jQuery.nodeName( this, "select" ) ) {
+ var values = jQuery.makeArray(value);
+
+ jQuery( "option", this ).each(function(){
+ this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
+ jQuery.inArray( this.text, values ) >= 0);
+ });
+
+ if ( !values.length )
+ this.selectedIndex = -1;
+
+ } else
+ this.value = value;
+ });
+ },
+
+ html: function( value ) {
+ return value == undefined ?
+ (this[0] ?
+ this[0].innerHTML :
+ null) :
+ this.empty().append( value );
+ },
+
+ replaceWith: function( value ) {
+ return this.after( value ).remove();
+ },
+
+ eq: function( i ) {
+ return this.slice( i, i + 1 );
+ },
+
+ slice: function() {
+ return this.pushStack( Array.prototype.slice.apply( this, arguments ) );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function(elem, i){
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ andSelf: function() {
+ return this.add( this.prevObject );
+ },
+
+ data: function( key, value ){
+ var parts = key.split(".");
+ parts[1] = parts[1] ? "." + parts[1] : "";
+
+ if ( value === undefined ) {
+ var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+ if ( data === undefined && this.length )
+ data = jQuery.data( this[0], key );
+
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+ } else
+ return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
+ jQuery.data( this, key, value );
+ });
+ },
+
+ removeData: function( key ){
+ return this.each(function(){
+ jQuery.removeData( this, key );
+ });
+ },
+
+ domManip: function( args, table, reverse, callback ) {
+ var clone = this.length > 1, elems;
+
+ return this.each(function(){
+ if ( !elems ) {
+ elems = jQuery.clean( args, this.ownerDocument );
+
+ if ( reverse )
+ elems.reverse();
+ }
+
+ var obj = this;
+
+ if ( table && jQuery.nodeName( this, "table" ) && jQuery.nodeName( elems[0], "tr" ) )
+ obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") );
+
+ var scripts = jQuery( [] );
+
+ jQuery.each(elems, function(){
+ var elem = clone ?
+ jQuery( this ).clone( true )[0] :
+ this;
+
+ // execute all scripts after the elements have been injected
+ if ( jQuery.nodeName( elem, "script" ) )
+ scripts = scripts.add( elem );
+ else {
+ // Remove any inner scripts for later evaluation
+ if ( elem.nodeType == 1 )
+ scripts = scripts.add( jQuery( "script", elem ).remove() );
+
+ // Inject the elements into the document
+ callback.call( obj, elem );
+ }
+ });
+
+ scripts.each( evalScript );
+ });
+ }
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+function evalScript( i, elem ) {
+ if ( elem.src )
+ jQuery.ajax({
+ url: elem.src,
+ async: false,
+ dataType: "script"
+ });
+
+ else
+ jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
+
+ if ( elem.parentNode )
+ elem.parentNode.removeChild( elem );
+}
+
+function now(){
+ return +new Date;
+}
+
+jQuery.extend = jQuery.fn.extend = function() {
+ // copy reference to target object
+ var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
+
+ // Handle a deep copy situation
+ if ( target.constructor == Boolean ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target != "object" && typeof target != "function" )
+ target = {};
+
+ // extend jQuery itself if only one argument is passed
+ if ( length == i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ )
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null )
+ // Extend the base object
+ for ( var name in options ) {
+ var src = target[ name ], copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy )
+ continue;
+
+ // Recurse if we're merging object values
+ if ( deep && copy && typeof copy == "object" && !copy.nodeType )
+ target[ name ] = jQuery.extend( deep,
+ // Never move original objects, clone them
+ src || ( copy.length != null ? [ ] : { } )
+ , copy );
+
+ // Don't bring in undefined values
+ else if ( copy !== undefined )
+ target[ name ] = copy;
+
+ }
+
+ // Return the modified object
+ return target;
+};
+
+var expando = "jQuery" + now(), uuid = 0, windowData = {},
+ // exclude the following css properties to add px
+ exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
+ // cache defaultView
+ defaultView = document.defaultView || {};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ window.$ = _$;
+
+ if ( deep )
+ window.jQuery = _jQuery;
+
+ return jQuery;
+ },
+
+ // See test/unit/core.js for details concerning this function.
+ isFunction: function( fn ) {
+ return !!fn && typeof fn != "string" && !fn.nodeName &&
+ fn.constructor != Array && /^[\s[]?function/.test( fn + "" );
+ },
+
+ // check if an element is in a (or is an) XML document
+ isXMLDoc: function( elem ) {
+ return elem.documentElement && !elem.body ||
+ elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
+ },
+
+ // Evalulates a script in a global context
+ globalEval: function( data ) {
+ data = jQuery.trim( data );
+
+ if ( data ) {
+ // Inspired by code by Andrea Giammarchi
+ // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
+ var head = document.getElementsByTagName("head")[0] || document.documentElement,
+ script = document.createElement("script");
+
+ script.type = "text/javascript";
+ if ( jQuery.browser.msie )
+ script.text = data;
+ else
+ script.appendChild( document.createTextNode( data ) );
+
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709).
+ head.insertBefore( script, head.firstChild );
+ head.removeChild( script );
+ }
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
+ },
+
+ cache: {},
+
+ data: function( elem, name, data ) {
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var id = elem[ expando ];
+
+ // Compute a unique ID for the element
+ if ( !id )
+ id = elem[ expando ] = ++uuid;
+
+ // Only generate the data cache if we're
+ // trying to access or manipulate it
+ if ( name && !jQuery.cache[ id ] )
+ jQuery.cache[ id ] = {};
+
+ // Prevent overriding the named cache with undefined values
+ if ( data !== undefined )
+ jQuery.cache[ id ][ name ] = data;
+
+ // Return the named cache data, or the ID for the element
+ return name ?
+ jQuery.cache[ id ][ name ] :
+ id;
+ },
+
+ removeData: function( elem, name ) {
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var id = elem[ expando ];
+
+ // If we want to remove a specific section of the element's data
+ if ( name ) {
+ if ( jQuery.cache[ id ] ) {
+ // Remove the section of cache data
+ delete jQuery.cache[ id ][ name ];
+
+ // If we've removed all the data, remove the element's cache
+ name = "";
+
+ for ( name in jQuery.cache[ id ] )
+ break;
+
+ if ( !name )
+ jQuery.removeData( elem );
+ }
+
+ // Otherwise, we want to remove all of the element's data
+ } else {
+ // Clean up the element expando
+ try {
+ delete elem[ expando ];
+ } catch(e){
+ // IE has trouble directly removing the expando
+ // but it's ok with using removeAttribute
+ if ( elem.removeAttribute )
+ elem.removeAttribute( expando );
+ }
+
+ // Completely remove the data cache
+ delete jQuery.cache[ id ];
+ }
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0, length = object.length;
+
+ if ( args ) {
+ if ( length == undefined ) {
+ for ( name in object )
+ if ( callback.apply( object[ name ], args ) === false )
+ break;
+ } else
+ for ( ; i < length; )
+ if ( callback.apply( object[ i++ ], args ) === false )
+ break;
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( length == undefined ) {
+ for ( name in object )
+ if ( callback.call( object[ name ], name, object[ name ] ) === false )
+ break;
+ } else
+ for ( var value = object[0];
+ i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
+ }
+
+ return object;
+ },
+
+ prop: function( elem, value, type, i, name ) {
+ // Handle executable functions
+ if ( jQuery.isFunction( value ) )
+ value = value.call( elem, i );
+
+ // Handle passing in a number to a CSS property
+ return value && value.constructor == Number && type == "curCSS" && !exclude.test( name ) ?
+ value + "px" :
+ value;
+ },
+
+ className: {
+ // internal only, use addClass("class")
+ add: function( elem, classNames ) {
+ jQuery.each((classNames || "").split(/\s+/), function(i, className){
+ if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
+ elem.className += (elem.className ? " " : "") + className;
+ });
+ },
+
+ // internal only, use removeClass("class")
+ remove: function( elem, classNames ) {
+ if (elem.nodeType == 1)
+ elem.className = classNames != undefined ?
+ jQuery.grep(elem.className.split(/\s+/), function(className){
+ return !jQuery.className.has( classNames, className );
+ }).join(" ") :
+ "";
+ },
+
+ // internal only, use hasClass("class")
+ has: function( elem, className ) {
+ return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
+ }
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var old = {};
+ // Remember the old values, and insert the new ones
+ for ( var name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ callback.call( elem );
+
+ // Revert the old values
+ for ( var name in options )
+ elem.style[ name ] = old[ name ];
+ },
+
+ css: function( elem, name, force ) {
+ if ( name == "width" || name == "height" ) {
+ var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
+
+ function getWH() {
+ val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
+ var padding = 0, border = 0;
+ jQuery.each( which, function() {
+ padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
+ border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
+ });
+ val -= Math.round(padding + border);
+ }
+
+ if ( jQuery(elem).is(":visible") )
+ getWH();
+ else
+ jQuery.swap( elem, props, getWH );
+
+ return Math.max(0, val);
+ }
+
+ return jQuery.curCSS( elem, name, force );
+ },
+
+ curCSS: function( elem, name, force ) {
+ var ret, style = elem.style;
+
+ // A helper method for determining if an element's values are broken
+ function color( elem ) {
+ if ( !jQuery.browser.safari )
+ return false;
+
+ // defaultView is cached
+ var ret = defaultView.getComputedStyle( elem, null );
+ return !ret || ret.getPropertyValue("color") == "";
+ }
+
+ // We need to handle opacity special in IE
+ if ( name == "opacity" && jQuery.browser.msie ) {
+ ret = jQuery.attr( style, "opacity" );
+
+ return ret == "" ?
+ "1" :
+ ret;
+ }
+ // Opera sometimes will give the wrong display answer, this fixes it, see #2037
+ if ( jQuery.browser.opera && name == "display" ) {
+ var save = style.outline;
+ style.outline = "0 solid black";
+ style.outline = save;
+ }
+
+ // Make sure we're using the right name for getting the float value
+ if ( name.match( /float/i ) )
+ name = styleFloat;
+
+ if ( !force && style && style[ name ] )
+ ret = style[ name ];
+
+ else if ( defaultView.getComputedStyle ) {
+
+ // Only "float" is needed here
+ if ( name.match( /float/i ) )
+ name = "float";
+
+ name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
+
+ var computedStyle = defaultView.getComputedStyle( elem, null );
+
+ if ( computedStyle && !color( elem ) )
+ ret = computedStyle.getPropertyValue( name );
+
+ // If the element isn't reporting its values properly in Safari
+ // then some display: none elements are involved
+ else {
+ var swap = [], stack = [], a = elem, i = 0;
+
+ // Locate all of the parent display: none elements
+ for ( ; a && color(a); a = a.parentNode )
+ stack.unshift(a);
+
+ // Go through and make them visible, but in reverse
+ // (It would be better if we knew the exact display type that they had)
+ for ( ; i < stack.length; i++ )
+ if ( color( stack[ i ] ) ) {
+ swap[ i ] = stack[ i ].style.display;
+ stack[ i ].style.display = "block";
+ }
+
+ // Since we flip the display style, we have to handle that
+ // one special, otherwise get the value
+ ret = name == "display" && swap[ stack.length - 1 ] != null ?
+ "none" :
+ ( computedStyle && computedStyle.getPropertyValue( name ) ) || "";
+
+ // Finally, revert the display styles back
+ for ( i = 0; i < swap.length; i++ )
+ if ( swap[ i ] != null )
+ stack[ i ].style.display = swap[ i ];
+ }
+
+ // We should always get a number back from opacity
+ if ( name == "opacity" && ret == "" )
+ ret = "1";
+
+ } else if ( elem.currentStyle ) {
+ var camelCase = name.replace(/\-(\w)/g, function(all, letter){
+ return letter.toUpperCase();
+ });
+
+ ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
+ // Remember the original values
+ var left = style.left, rsLeft = elem.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ style.left = ret || 0;
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret;
+ },
+
+ clean: function( elems, context ) {
+ var ret = [];
+ context = context || document;
+ // !context.createElement fails in IE with an error but returns typeof 'object'
+ if (typeof context.createElement == 'undefined')
+ context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+
+ jQuery.each(elems, function(i, elem){
+ if ( !elem )
+ return;
+
+ if ( elem.constructor == Number )
+ elem += '';
+
+ // Convert html string into DOM nodes
+ if ( typeof elem == "string" ) {
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
+ return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
+ all :
+ front + ">" + tag + ">";
+ });
+
+ // Trim whitespace, otherwise indexOf won't work as expected
+ var tags = jQuery.trim( elem ).toLowerCase(), div = context.createElement("div");
+
+ var wrap =
+ // option or optgroup
+ !tags.indexOf("", "" ] ||
+
+ !tags.indexOf("", "" ] ||
+
+ tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
+ [ 1, "" ] ||
+
+ !tags.indexOf("", " " ] ||
+
+ // matched above
+ (!tags.indexOf(" ", " " ] ||
+
+ !tags.indexOf("", " " ] ||
+
+ // IE can't serialize and ";
+ echo "";
+ echo "";
+ echo "";
+}
+
+
+/*
+ * Formats call counts for XHProf reports.
+ *
+ * Description:
+ * Call counts in single-run reports are integer values.
+ * However, call counts for aggregated reports can be
+ * fractional. This function will print integer values
+ * without decimal point, but with commas etc.
+ *
+ * 4000 ==> 4,000
+ *
+ * It'll round fractional values to decimal precision of 3
+ * 4000.1212 ==> 4,000.121
+ * 4000.0001 ==> 4,000
+ *
+ */
+function xhprof_count_format($num) {
+ $num = round($num, 3);
+ if (round($num) == $num) {
+ return number_format($num);
+ } else {
+ return number_format($num, 3);
+ }
+}
+
+function xhprof_percent_format($s, $precision = 1) {
+ return sprintf('%.'.$precision.'f%%', 100 * $s);
+}
+
+/**
+ * Implodes the text for a bunch of actions (such as links, forms,
+ * into a HTML list and returns the text.
+ */
+function xhprof_render_actions($actions) {
+ $out = array();
+
+ if (count($actions)) {
+ $out[] = '';
+ foreach ($actions as $action) {
+ $out[] = ''.$action.' ';
+ }
+ $out[] = ' ';
+ }
+
+ return implode('', $out);
+}
+
+
+/**
+ * @param html-str $content the text/image/innerhtml/whatever for the link
+ * @param raw-str $href
+ * @param raw-str $class
+ * @param raw-str $id
+ * @param raw-str $title
+ * @param raw-str $target
+ * @param raw-str $onclick
+ * @param raw-str $style
+ * @param raw-str $access
+ * @param raw-str $onmouseover
+ * @param raw-str $onmouseout
+ * @param raw-str $onmousedown
+ * @param raw-str $dir
+ * @param raw-str $rel
+ */
+function xhprof_render_link($content, $href, $class='', $id='', $title='',
+ $target='',
+ $onclick='', $style='', $access='', $onmouseover='',
+ $onmouseout='', $onmousedown='') {
+
+ if (!$content) {
+ return '';
+ }
+
+ if ($href) {
+ $link = ' 1,
+ "ct" => 1,
+ "wt" => 1,
+ "excl_wt" => 1,
+ "ut" => 1,
+ "excl_ut" => 1,
+ "st" => 1,
+ "excl_st" => 1,
+ "mu" => 1,
+ "excl_mu" => 1,
+ "pmu" => 1,
+ "excl_pmu" => 1,
+ "cpu" => 1,
+ "excl_cpu" => 1,
+ "samples" => 1,
+ "excl_samples" => 1
+ );
+
+// Textual descriptions for column headers in "single run" mode
+$descriptions = array(
+ "fn" => "Function Name",
+ "ct" => "Calls",
+ "Calls%" => "Calls%",
+
+ "wt" => "Incl. Wall Time (microsec)",
+ "IWall%" => "IWall%",
+ "excl_wt" => "Excl. Wall Time (microsec)",
+ "EWall%" => "EWall%",
+
+ "ut" => "Incl. User (microsecs)",
+ "IUser%" => "IUser%",
+ "excl_ut" => "Excl. User (microsec)",
+ "EUser%" => "EUser%",
+
+ "st" => "Incl. Sys (microsec)",
+ "ISys%" => "ISys%",
+ "excl_st" => "Excl. Sys (microsec)",
+ "ESys%" => "ESys%",
+
+ "cpu" => "Incl. CPU (microsecs)",
+ "ICpu%" => "ICpu%",
+ "excl_cpu" => "Excl. CPU (microsec)",
+ "ECpu%" => "ECPU%",
+
+ "mu" => "Incl. MemUse (bytes)",
+ "IMUse%" => "IMemUse%",
+ "excl_mu" => "Excl. MemUse (bytes)",
+ "EMUse%" => "EMemUse%",
+
+ "pmu" => "Incl. PeakMemUse (bytes)",
+ "IPMUse%" => "IPeakMemUse%",
+ "excl_pmu" => "Excl. PeakMemUse (bytes)",
+ "EPMUse%" => "EPeakMemUse%",
+
+ "samples" => "Incl. Samples",
+ "ISamples%" => "ISamples%",
+ "excl_samples" => "Excl. Samples",
+ "ESamples%" => "ESamples%",
+ );
+
+// Formatting Callback Functions...
+$format_cbk = array(
+ "fn" => "",
+ "ct" => "xhprof_count_format",
+ "Calls%" => "xhprof_percent_format",
+
+ "wt" => "number_format",
+ "IWall%" => "xhprof_percent_format",
+ "excl_wt" => "number_format",
+ "EWall%" => "xhprof_percent_format",
+
+ "ut" => "number_format",
+ "IUser%" => "xhprof_percent_format",
+ "excl_ut" => "number_format",
+ "EUser%" => "xhprof_percent_format",
+
+ "st" => "number_format",
+ "ISys%" => "xhprof_percent_format",
+ "excl_st" => "number_format",
+ "ESys%" => "xhprof_percent_format",
+
+ "cpu" => "number_format",
+ "ICpu%" => "xhprof_percent_format",
+ "excl_cpu" => "number_format",
+ "ECpu%" => "xhprof_percent_format",
+
+ "mu" => "number_format",
+ "IMUse%" => "xhprof_percent_format",
+ "excl_mu" => "number_format",
+ "EMUse%" => "xhprof_percent_format",
+
+ "pmu" => "number_format",
+ "IPMUse%" => "xhprof_percent_format",
+ "excl_pmu" => "number_format",
+ "EPMUse%" => "xhprof_percent_format",
+
+ "samples" => "number_format",
+ "ISamples%" => "xhprof_percent_format",
+ "excl_samples" => "number_format",
+ "ESamples%" => "xhprof_percent_format",
+ );
+
+
+// Textual descriptions for column headers in "diff" mode
+$diff_descriptions = array(
+ "fn" => "Function Name",
+ "ct" => "Calls Diff",
+ "Calls%" => "Calls Diff%",
+
+ "wt" => "Incl. Wall Diff (microsec)",
+ "IWall%" => "IWall Diff%",
+ "excl_wt" => "Excl. Wall Diff (microsec)",
+ "EWall%" => "EWall Diff%",
+
+ "ut" => "Incl. User Diff (microsec)",
+ "IUser%" => "IUser Diff%",
+ "excl_ut" => "Excl. User Diff (microsec)",
+ "EUser%" => "EUser Diff%",
+
+ "cpu" => "Incl. CPU Diff (microsec)",
+ "ICpu%" => "ICpu Diff%",
+ "excl_cpu" => "Excl. CPU Diff (microsec)",
+ "ECpu%" => "ECpu Diff%",
+
+ "st" => "Incl. Sys Diff (microsec)",
+ "ISys%" => "ISys Diff%",
+ "excl_st" => "Excl. Sys Diff (microsec)",
+ "ESys%" => "ESys Diff%",
+
+ "mu" => "Incl. MemUse Diff (bytes)",
+ "IMUse%" => "IMemUse Diff%",
+ "excl_mu" => "Excl. MemUse Diff (bytes)",
+ "EMUse%" => "EMemUse Diff%",
+
+ "pmu" => "Incl. PeakMemUse Diff (bytes)",
+ "IPMUse%" => "IPeakMemUse Diff%",
+ "excl_pmu" => "Excl. PeakMemUse Diff (bytes)",
+ "EPMUse%" => "EPeakMemUse Diff%",
+
+ "samples" => "Incl. Samples Diff",
+ "ISamples%" => "ISamples Diff%",
+ "excl_samples" => "Excl. Samples Diff",
+ "ESamples%" => "ESamples Diff%",
+ );
+
+// columns that'll be displayed in a top-level report
+$stats = array();
+
+// columns that'll be displayed in a function's parent/child report
+$pc_stats = array();
+
+// Various total counts
+$totals = 0;
+$totals_1 = 0;
+$totals_2 = 0;
+
+/*
+ * The subset of $possible_metrics that is present in the raw profile data.
+ */
+$metrics = null;
+
+/**
+ * Callback comparison operator (passed to usort() for sorting array of
+ * tuples) that compares array elements based on the sort column
+ * specified in $sort_col (global parameter).
+ *
+ * @author Kannan
+ */
+function sort_cbk($a, $b) {
+ global $sort_col;
+ global $diff_mode;
+
+ if ($sort_col == "fn") {
+
+ // case insensitive ascending sort for function names
+ $left = strtoupper($a["fn"]);
+ $right = strtoupper($b["fn"]);
+
+ if ($left == $right)
+ return 0;
+ return ($left < $right) ? -1 : 1;
+
+ } else {
+
+ // descending sort for all others
+ $left = $a[$sort_col];
+ $right = $b[$sort_col];
+
+ // if diff mode, sort by absolute value of regression/improvement
+ if ($diff_mode) {
+ $left = abs($left);
+ $right = abs($right);
+ }
+
+ if ($left == $right)
+ return 0;
+ return ($left > $right) ? -1 : 1;
+ }
+}
+
+/**
+ * Get the appropriate description for a statistic
+ * (depending upon whether we are in diff report mode
+ * or single run report mode).
+ *
+ * @author Kannan
+ */
+function stat_description($stat) {
+ global $descriptions;
+ global $diff_descriptions;
+ global $diff_mode;
+
+ if ($diff_mode) {
+ return $diff_descriptions[$stat];
+ } else {
+ return $descriptions[$stat];
+ }
+}
+
+
+/**
+ * Analyze raw data & generate the profiler report
+ * (common for both single run mode and diff mode).
+ *
+ * @author: Kannan
+ */
+function profiler_report ($url_params,
+ $rep_symbol,
+ $sort,
+ $run1,
+ $run1_desc,
+ $run1_data,
+ $run2 = 0,
+ $run2_desc = "",
+ $run2_data = array()) {
+ global $totals;
+ global $totals_1;
+ global $totals_2;
+ global $stats;
+ global $pc_stats;
+ global $diff_mode;
+ global $base_path;
+
+ // if we are reporting on a specific function, we can trim down
+ // the report(s) to just stuff that is relevant to this function.
+ // That way compute_flat_info()/compute_diff() etc. do not have
+ // to needlessly work hard on churning irrelevant data.
+ if (!empty($rep_symbol)) {
+ $run1_data = xhprof_trim_run($run1_data, array($rep_symbol));
+ if ($diff_mode) {
+ $run2_data = xhprof_trim_run($run2_data, array($rep_symbol));
+ }
+ }
+
+ if ($diff_mode) {
+ $run_delta = xhprof_compute_diff($run1_data, $run2_data);
+ $symbol_tab = xhprof_compute_flat_info($run_delta, $totals);
+ $symbol_tab1 = xhprof_compute_flat_info($run1_data, $totals_1);
+ $symbol_tab2 = xhprof_compute_flat_info($run2_data, $totals_2);
+ } else {
+ $symbol_tab = xhprof_compute_flat_info($run1_data, $totals);
+ }
+
+ $run1_txt = sprintf("Run #%s: %s",
+ $run1, $run1_desc);
+
+ $base_url_params = xhprof_array_unset(xhprof_array_unset($url_params,
+ 'symbol'),
+ 'all');
+
+ $top_link_query_string = "$base_path/?" . http_build_query($base_url_params);
+
+ if ($diff_mode) {
+ $diff_text = "Diff";
+ $base_url_params = xhprof_array_unset($base_url_params, 'run1');
+ $base_url_params = xhprof_array_unset($base_url_params, 'run2');
+ $run1_link = xhprof_render_link('View Run #' . $run1,
+ "$base_path/?" .
+ http_build_query(xhprof_array_set($base_url_params,
+ 'run',
+ $run1)));
+ $run2_txt = sprintf("Run #%s: %s",
+ $run2, $run2_desc);
+
+ $run2_link = xhprof_render_link('View Run #' . $run2,
+ "$base_path/?" .
+ http_build_query(xhprof_array_set($base_url_params,
+ 'run',
+ $run2)));
+ } else {
+ $diff_text = "Run";
+ }
+
+ // set up the action links for operations that can be done on this report
+ $links = array();
+ $links [] = xhprof_render_link("View Top Level $diff_text Report",
+ $top_link_query_string);
+
+ if ($diff_mode) {
+ $inverted_params = $url_params;
+ $inverted_params['run1'] = $url_params['run2'];
+ $inverted_params['run2'] = $url_params['run1'];
+
+ // view the different runs or invert the current diff
+ $links [] = $run1_link;
+ $links [] = $run2_link;
+ $links [] = xhprof_render_link('Invert ' . $diff_text . ' Report',
+ "$base_path/?".
+ http_build_query($inverted_params));
+ }
+
+ // lookup function typeahead form
+ $links [] = ' ';
+
+ echo xhprof_render_actions($links);
+
+
+ echo
+ '' .
+ ' ' . $diff_text . ' Report ' .
+ ' ' . ($diff_mode ?
+ $run1_txt . 'vs. ' . $run2_txt :
+ $run1_txt) .
+ ' ' .
+ ' Tip ' .
+ ' Click a function name below to drill down. ' .
+ ' ' .
+ '
';
+
+ // data tables
+ if (!empty($rep_symbol)) {
+ if (!isset($symbol_tab[$rep_symbol])) {
+ echo " Symbol $rep_symbol not found in XHProf run ";
+ return;
+ }
+
+ /* single function report with parent/child information */
+ if ($diff_mode) {
+ $info1 = isset($symbol_tab1[$rep_symbol]) ?
+ $symbol_tab1[$rep_symbol] : null;
+ $info2 = isset($symbol_tab2[$rep_symbol]) ?
+ $symbol_tab2[$rep_symbol] : null;
+ symbol_report($url_params, $run_delta, $symbol_tab[$rep_symbol],
+ $sort, $rep_symbol,
+ $run1, $info1,
+ $run2, $info2);
+ } else {
+ symbol_report($url_params, $run1_data, $symbol_tab[$rep_symbol],
+ $sort, $rep_symbol, $run1);
+ }
+ } else {
+ /* flat top-level report of all functions */
+ full_report($url_params, $symbol_tab, $sort, $run1, $run2);
+ }
+}
+
+/**
+ * Computes percentage for a pair of values, and returns it
+ * in string format.
+ */
+function pct($a, $b) {
+ if ($b == 0) {
+ return "N/A";
+ } else {
+ $res = (round(($a * 1000 / $b)) / 10);
+ return $res;
+ }
+}
+
+/**
+ * Given a number, returns the td class to use for display.
+ *
+ * For instance, negative numbers in diff reports comparing two runs (run1 & run2)
+ * represent improvement from run1 to run2. We use green to display those deltas,
+ * and red for regression deltas.
+ */
+function get_print_class($num, $bold) {
+ global $vbar;
+ global $vbbar;
+ global $vrbar;
+ global $vgbar;
+ global $diff_mode;
+
+ if ($bold) {
+ if ($diff_mode) {
+ if ($num <= 0) {
+ $class = $vgbar; // green (improvement)
+ } else {
+ $class = $vrbar; // red (regression)
+ }
+ } else {
+ $class = $vbbar; // blue
+ }
+ }
+ else {
+ $class = $vbar; // default (black)
+ }
+
+ return $class;
+}
+
+/**
+ * Prints a element with a numeric value.
+ */
+function print_td_num($num, $fmt_func, $bold=false, $attributes=null) {
+
+ $class = get_print_class($num, $bold);
+
+ if (!empty($fmt_func) && is_numeric($num) ) {
+ $num = call_user_func($fmt_func, $num);
+ }
+
+ print(" $num \n");
+}
+
+/**
+ * Prints a element with a pecentage.
+ */
+function print_td_pct($numer, $denom, $bold=false, $attributes=null) {
+ global $vbar;
+ global $vbbar;
+ global $diff_mode;
+
+ $class = get_print_class($numer, $bold);
+
+ if ($denom == 0) {
+ $pct = "N/A%";
+ } else {
+ $pct = xhprof_percent_format($numer / abs($denom));
+ }
+
+ print(" $pct \n");
+}
+
+/**
+ * Print "flat" data corresponding to one function.
+ *
+ * @author Kannan
+ */
+function print_function_info($url_params, $info, $sort, $run1, $run2) {
+ static $odd_even = 0;
+
+ global $totals;
+ global $sort_col;
+ global $metrics;
+ global $format_cbk;
+ global $display_calls;
+ global $base_path;
+
+ // Toggle $odd_or_even
+ $odd_even = 1 - $odd_even;
+
+ if ($odd_even) {
+ print("");
+ }
+ else {
+ print(' ');
+ }
+
+ $href = "$base_path/?" .
+ http_build_query(xhprof_array_set($url_params,
+ 'symbol', $info["fn"]));
+
+ print('');
+ print(xhprof_render_link($info["fn"], $href));
+ print_source_link($info);
+ print(" \n");
+
+ if ($display_calls) {
+ // Call Count..
+ print_td_num($info["ct"], $format_cbk["ct"], ($sort_col == "ct"));
+ print_td_pct($info["ct"], $totals["ct"], ($sort_col == "ct"));
+ }
+
+ // Other metrics..
+ foreach ($metrics as $metric) {
+ // Inclusive metric
+ print_td_num($info[$metric], $format_cbk[$metric],
+ ($sort_col == $metric));
+ print_td_pct($info[$metric], $totals[$metric],
+ ($sort_col == $metric));
+
+ // Exclusive Metric
+ print_td_num($info["excl_" . $metric],
+ $format_cbk["excl_" . $metric],
+ ($sort_col == "excl_" . $metric));
+ print_td_pct($info["excl_" . $metric],
+ $totals[$metric],
+ ($sort_col == "excl_" . $metric));
+ }
+
+ print(" \n");
+}
+
+/**
+ * Print non-hierarchical (flat-view) of profiler data.
+ *
+ * @author Kannan
+ */
+function print_flat_data($url_params, $title, $flat_data, $sort, $run1, $run2, $limit) {
+
+ global $stats;
+ global $sortable_columns;
+ global $vwbar;
+ global $base_path;
+
+ $size = count($flat_data);
+ if (!$limit) { // no limit
+ $limit = $size;
+ $display_link = "";
+ } else {
+ $display_link = xhprof_render_link(" [ display all ]",
+ "$base_path/?" .
+ http_build_query(xhprof_array_set($url_params,
+ 'all', 1)));
+ }
+
+ print("$title $display_link ");
+
+ print('');
+ print('');
+
+ foreach ($stats as $stat) {
+ $desc = stat_description($stat);
+ if (array_key_exists($stat, $sortable_columns)) {
+ $href = "$base_path/?"
+ . http_build_query(xhprof_array_set($url_params, 'sort', $stat));
+ $header = xhprof_render_link($desc, $href);
+ } else {
+ $header = $desc;
+ }
+
+ if ($stat == "fn")
+ print("$header ");
+ else print("$header ");
+ }
+ print(" \n");
+
+ if ($limit >= 0) {
+ $limit = min($size, $limit);
+ for ($i = 0; $i < $limit; $i++) {
+ print_function_info($url_params, $flat_data[$i], $sort, $run1, $run2);
+ }
+ } else {
+ // if $limit is negative, print abs($limit) items starting from the end
+ $limit = min($size, abs($limit));
+ for ($i = 0; $i < $limit; $i++) {
+ print_function_info($url_params, $flat_data[$size - $i - 1], $sort, $run1, $run2);
+ }
+ }
+ print("
");
+
+ // let's print the display all link at the bottom as well...
+ if ($display_link) {
+ echo '' . $display_link . '
';
+ }
+
+}
+
+/**
+ * Generates a tabular report for all functions. This is the top-level report.
+ *
+ * @author Kannan
+ */
+function full_report($url_params, $symbol_tab, $sort, $run1, $run2) {
+ global $vwbar;
+ global $vbar;
+ global $totals;
+ global $totals_1;
+ global $totals_2;
+ global $metrics;
+ global $diff_mode;
+ global $descriptions;
+ global $sort_col;
+ global $format_cbk;
+ global $display_calls;
+ global $base_path;
+
+ $possible_metrics = xhprof_get_possible_metrics();
+
+ if ($diff_mode) {
+
+ $base_url_params = xhprof_array_unset(xhprof_array_unset($url_params,
+ 'run1'),
+ 'run2');
+ $href1 = "$base_path/?" .
+ http_build_query(xhprof_array_set($base_url_params,
+ 'run', $run1));
+ $href2 = "$base_path/?" .
+ http_build_query(xhprof_array_set($base_url_params,
+ 'run', $run2));
+
+ print("Overall Diff Summary ");
+ print('' . "\n");
+ print('');
+ print(" ");
+ print("" . xhprof_render_link("Run #$run1", $href1) . " ");
+ print("" . xhprof_render_link("Run #$run2", $href2) . " ");
+ print("Diff ");
+ print("Diff% ");
+ print(' ');
+
+ if ($display_calls) {
+ print('');
+ print("Number of Function Calls ");
+ print_td_num($totals_1["ct"], $format_cbk["ct"]);
+ print_td_num($totals_2["ct"], $format_cbk["ct"]);
+ print_td_num($totals_2["ct"] - $totals_1["ct"], $format_cbk["ct"], true);
+ print_td_pct($totals_2["ct"] - $totals_1["ct"], $totals_1["ct"], true);
+ print(' ');
+ }
+
+ foreach ($metrics as $metric) {
+ $m = $metric;
+ print('');
+ print("" . str_replace(" ", " ", $descriptions[$m]) . " ");
+ print_td_num($totals_1[$m], $format_cbk[$m]);
+ print_td_num($totals_2[$m], $format_cbk[$m]);
+ print_td_num($totals_2[$m] - $totals_1[$m], $format_cbk[$m], true);
+ print_td_pct($totals_2[$m] - $totals_1[$m], $totals_1[$m], true);
+ print(' ');
+ }
+ print('
');
+
+ $callgraph_report_title = '[View Regressions/Improvements using Callgraph Diff]';
+
+ } else {
+ print("
\n");
+
+ print('' . "\n");
+ echo "";
+ echo "Overall Summary ";
+ echo " ";
+ echo " ";
+
+ foreach ($metrics as $metric) {
+ echo "";
+ echo "Total "
+ . str_replace(" ", " ", stat_description($metric)) . ": ";
+ echo "" . number_format($totals[$metric]) . " "
+ . $possible_metrics[$metric][1] . " ";
+ echo " ";
+ }
+
+ if ($display_calls) {
+ echo "";
+ echo "Number of Function Calls: ";
+ echo "" . number_format($totals['ct']) . " ";
+ echo " ";
+ }
+
+ echo "
";
+ print(" \n");
+
+ $callgraph_report_title = '[View Full Callgraph]';
+ }
+
+ print("" .
+ xhprof_render_link($callgraph_report_title,
+ "$base_path/callgraph.php" . "?" . http_build_query($url_params))
+ . " ");
+
+
+ $flat_data = array();
+ foreach ($symbol_tab as $symbol => $info) {
+ $tmp = $info;
+ $tmp["fn"] = $symbol;
+ $flat_data[] = $tmp;
+ }
+ usort($flat_data, 'sort_cbk');
+
+ print(" ");
+
+ if (!empty($url_params['all'])) {
+ $all = true;
+ $limit = 0; // display all rows
+ } else {
+ $all = false;
+ $limit = 100; // display only limited number of rows
+ }
+
+ $desc = str_replace(" ", " ", $descriptions[$sort_col]);
+
+ if ($diff_mode) {
+ if ($all) {
+ $title = "Total Diff Report: '
+ .'Sorted by absolute value of regression/improvement in $desc";
+ } else {
+ $title = "Top 100 Regressions /"
+ . "Improvements : "
+ . "Sorted by $desc Diff";
+ }
+ } else {
+ if ($all) {
+ $title = "Sorted by $desc";
+ } else {
+ $title = "Displaying top $limit functions: Sorted by $desc";
+ }
+ }
+ print_flat_data($url_params, $title, $flat_data, $sort, $run1, $run2, $limit);
+}
+
+
+/**
+ * Return attribute names and values to be used by javascript tooltip.
+ */
+function get_tooltip_attributes($type, $metric) {
+ return "type='$type' metric='$metric'";
+}
+
+/**
+ * Print info for a parent or child function in the
+ * parent & children report.
+ *
+ * @author Kannan
+ */
+function pc_info($info, $base_ct, $base_info, $parent) {
+ global $sort_col;
+ global $metrics;
+ global $format_cbk;
+ global $display_calls;
+
+ if ($parent)
+ $type = "Parent";
+ else $type = "Child";
+
+ if ($display_calls) {
+ $mouseoverct = get_tooltip_attributes($type, "ct");
+ /* call count */
+ print_td_num($info["ct"], $format_cbk["ct"], ($sort_col == "ct"), $mouseoverct);
+ print_td_pct($info["ct"], $base_ct, ($sort_col == "ct"), $mouseoverct);
+ }
+
+ /* Inclusive metric values */
+ foreach ($metrics as $metric) {
+ print_td_num($info[$metric], $format_cbk[$metric],
+ ($sort_col == $metric),
+ get_tooltip_attributes($type, $metric));
+ print_td_pct($info[$metric], $base_info[$metric], ($sort_col == $metric),
+ get_tooltip_attributes($type, $metric));
+ }
+}
+
+function print_pc_array($url_params, $results, $base_ct, $base_info, $parent,
+ $run1, $run2) {
+ global $base_path;
+
+ // Construct section title
+ if ($parent) {
+ $title = 'Parent function';
+ }
+ else {
+ $title = 'Child function';
+ }
+ if (count($results) > 1) {
+ $title .= 's';
+ }
+
+ print("");
+ print("" . $title . " ");
+ print(" ");
+
+ $odd_even = 0;
+ foreach ($results as $info) {
+ $href = "$base_path/?" .
+ http_build_query(xhprof_array_set($url_params,
+ 'symbol', $info["fn"]));
+
+ $odd_even = 1 - $odd_even;
+
+ if ($odd_even) {
+ print('');
+ }
+ else {
+ print(' ');
+ }
+
+ print("" . xhprof_render_link($info["fn"], $href));
+ print_source_link($info);
+ print(" ");
+ pc_info($info, $base_ct, $base_info, $parent);
+ print(" ");
+ }
+}
+
+function print_source_link($info) {
+ if (strncmp($info['fn'], 'run_init', 8) && $info['fn'] !== 'main()') {
+ if (defined('XHPROF_SYMBOL_LOOKUP_URL')) {
+ $link = xhprof_render_link(
+ 'source',
+ XHPROF_SYMBOL_LOOKUP_URL . '?symbol='.rawurlencode($info["fn"]));
+ print(' ('.$link.')');
+ }
+ }
+}
+
+
+function print_symbol_summary($symbol_info, $stat, $base) {
+
+ $val = $symbol_info[$stat];
+ $desc = str_replace(" ", " ", stat_description($stat));
+
+ print("$desc: ");
+ print(number_format($val));
+ print(" (" . pct($val, $base) . "% of overall)");
+ if (substr($stat, 0, 4) == "excl") {
+ $func_base = $symbol_info[str_replace("excl_", "", $stat)];
+ print(" (" . pct($val, $func_base) . "% of this function)");
+ }
+ print(" ");
+}
+
+/**
+ * Generates a report for a single function/symbol.
+ *
+ * @author Kannan
+ */
+function symbol_report($url_params,
+ $run_data, $symbol_info, $sort, $rep_symbol,
+ $run1,
+ $symbol_info1 = null,
+ $run2 = 0,
+ $symbol_info2 = null) {
+ global $vwbar;
+ global $vbar;
+ global $totals;
+ global $pc_stats;
+ global $sortable_columns;
+ global $metrics;
+ global $diff_mode;
+ global $descriptions;
+ global $format_cbk;
+ global $sort_col;
+ global $display_calls;
+ global $base_path;
+
+ $possible_metrics = xhprof_get_possible_metrics();
+
+ if ($diff_mode) {
+ $diff_text = "Diff ";
+ $regr_impr = "Regression /Improvement ";
+ } else {
+ $diff_text = "";
+ $regr_impr = "";
+ }
+
+ if ($diff_mode) {
+
+ $base_url_params = xhprof_array_unset(xhprof_array_unset($url_params,
+ 'run1'),
+ 'run2');
+ $href1 = "$base_path?"
+ . http_build_query(xhprof_array_set($base_url_params, 'run', $run1));
+ $href2 = "$base_path?"
+ . http_build_query(xhprof_array_set($base_url_params, 'run', $run2));
+
+ print("$regr_impr summary for $rep_symbol ");
+ print('' . "\n");
+ print('');
+ print("$rep_symbol ");
+ print("Run #$run1 ");
+ print("Run #$run2 ");
+ print("Diff ");
+ print("Diff% ");
+ print(' ');
+ print('');
+
+ if ($display_calls) {
+ print("Number of Function Calls ");
+ print_td_num($symbol_info1["ct"], $format_cbk["ct"]);
+ print_td_num($symbol_info2["ct"], $format_cbk["ct"]);
+ print_td_num($symbol_info2["ct"] - $symbol_info1["ct"],
+ $format_cbk["ct"], true);
+ print_td_pct($symbol_info2["ct"] - $symbol_info1["ct"],
+ $symbol_info1["ct"], true);
+ print(' ');
+ }
+
+
+ foreach ($metrics as $metric) {
+ $m = $metric;
+
+ // Inclusive stat for metric
+ print('');
+ print("" . str_replace(" ", " ", $descriptions[$m]) . " ");
+ print_td_num($symbol_info1[$m], $format_cbk[$m]);
+ print_td_num($symbol_info2[$m], $format_cbk[$m]);
+ print_td_num($symbol_info2[$m] - $symbol_info1[$m], $format_cbk[$m], true);
+ print_td_pct($symbol_info2[$m] - $symbol_info1[$m], $symbol_info1[$m], true);
+ print(' ');
+
+ // AVG (per call) Inclusive stat for metric
+ print('');
+ print("" . str_replace(" ", " ", $descriptions[$m]) . " per call ");
+ $avg_info1 = 'N/A';
+ $avg_info2 = 'N/A';
+ if ($symbol_info1['ct'] > 0) {
+ $avg_info1 = ($symbol_info1[$m] / $symbol_info1['ct']);
+ }
+ if ($symbol_info2['ct'] > 0) {
+ $avg_info2 = ($symbol_info2[$m] / $symbol_info2['ct']);
+ }
+ print_td_num($avg_info1, $format_cbk[$m]);
+ print_td_num($avg_info2, $format_cbk[$m]);
+ print_td_num($avg_info2 - $avg_info1, $format_cbk[$m], true);
+ print_td_pct($avg_info2 - $avg_info1, $avg_info1, true);
+ print(' ');
+
+ // Exclusive stat for metric
+ $m = "excl_" . $metric;
+ print('');
+ print("" . str_replace(" ", " ", $descriptions[$m]) . " ");
+ print_td_num($symbol_info1[$m], $format_cbk[$m]);
+ print_td_num($symbol_info2[$m], $format_cbk[$m]);
+ print_td_num($symbol_info2[$m] - $symbol_info1[$m], $format_cbk[$m], true);
+ print_td_pct($symbol_info2[$m] - $symbol_info1[$m], $symbol_info1[$m], true);
+ print(' ');
+ }
+
+ print('
');
+ }
+
+ print("");
+ print("Parent/Child $regr_impr report for $rep_symbol ");
+
+ $callgraph_href = "$base_path/callgraph.php?"
+ . http_build_query(xhprof_array_set($url_params, 'func', $rep_symbol));
+
+ print(" [View Callgraph $diff_text] ");
+
+ print(" ");
+
+ print('' . "\n");
+ print('');
+
+ foreach ($pc_stats as $stat) {
+ $desc = stat_description($stat);
+ if (array_key_exists($stat, $sortable_columns)) {
+
+ $href = "$base_path/?" .
+ http_build_query(xhprof_array_set($url_params,
+ 'sort', $stat));
+ $header = xhprof_render_link($desc, $href);
+ } else {
+ $header = $desc;
+ }
+
+ if ($stat == "fn")
+ print("$header ");
+ else print("$header ");
+ }
+ print(" ");
+
+ print("");
+ print("Current Function ");
+ print(" ");
+
+ print("");
+ // make this a self-reference to facilitate copy-pasting snippets to e-mails
+ print("$rep_symbol ");
+ print_source_link(array('fn' => $rep_symbol));
+ print(" ");
+
+ if ($display_calls) {
+ // Call Count
+ print_td_num($symbol_info["ct"], $format_cbk["ct"]);
+ print_td_pct($symbol_info["ct"], $totals["ct"]);
+ }
+
+ // Inclusive Metrics for current function
+ foreach ($metrics as $metric) {
+ print_td_num($symbol_info[$metric], $format_cbk[$metric], ($sort_col == $metric));
+ print_td_pct($symbol_info[$metric], $totals[$metric], ($sort_col == $metric));
+ }
+ print(" ");
+
+ print("");
+ print(""
+ ."Exclusive Metrics $diff_text for Current Function ");
+
+ if ($display_calls) {
+ // Call Count
+ print(" ");
+ print(" ");
+ }
+
+ // Exclusive Metrics for current function
+ foreach ($metrics as $metric) {
+ print_td_num($symbol_info["excl_" . $metric], $format_cbk["excl_" . $metric],
+ ($sort_col == $metric),
+ get_tooltip_attributes("Child", $metric));
+ print_td_pct($symbol_info["excl_" . $metric], $symbol_info[$metric],
+ ($sort_col == $metric),
+ get_tooltip_attributes("Child", $metric));
+ }
+ print(" ");
+
+ // list of callers/parent functions
+ $results = array();
+ if ($display_calls) {
+ $base_ct = $symbol_info["ct"];
+ } else {
+ $base_ct = 0;
+ }
+ foreach ($metrics as $metric) {
+ $base_info[$metric] = $symbol_info[$metric];
+ }
+ foreach ($run_data as $parent_child => $info) {
+ list($parent, $child) = xhprof_parse_parent_child($parent_child);
+ if (($child == $rep_symbol) && ($parent)) {
+ $info_tmp = $info;
+ $info_tmp["fn"] = $parent;
+ $results[] = $info_tmp;
+ }
+ }
+ usort($results, 'sort_cbk');
+
+ if (count($results) > 0) {
+ print_pc_array($url_params, $results, $base_ct, $base_info, true,
+ $run1, $run2);
+ }
+
+ // list of callees/child functions
+ $results = array();
+ $base_ct = 0;
+ foreach ($run_data as $parent_child => $info) {
+ list($parent, $child) = xhprof_parse_parent_child($parent_child);
+ if ($parent == $rep_symbol) {
+ $info_tmp = $info;
+ $info_tmp["fn"] = $child;
+ $results[] = $info_tmp;
+ if ($display_calls) {
+ $base_ct += $info["ct"];
+ }
+ }
+ }
+ usort($results, 'sort_cbk');
+
+ if (count($results)) {
+ print_pc_array($url_params, $results, $base_ct, $base_info, false,
+ $run1, $run2);
+ }
+
+ print("
");
+
+ // These will be used for pop-up tips/help.
+ // Related javascript code is in: xhprof_report.js
+ print("\n");
+ print('');
+ print("\n");
+
+}
+
+/**
+ * Generate the profiler report for a single run.
+ *
+ * @author Kannan
+ */
+function profiler_single_run_report ($url_params,
+ $xhprof_data,
+ $run_desc,
+ $rep_symbol,
+ $sort,
+ $run) {
+
+ init_metrics($xhprof_data, $rep_symbol, $sort, false);
+
+ profiler_report($url_params, $rep_symbol, $sort, $run, $run_desc,
+ $xhprof_data);
+}
+
+
+
+/**
+ * Generate the profiler report for diff mode (delta between two runs).
+ *
+ * @author Kannan
+ */
+function profiler_diff_report($url_params,
+ $xhprof_data1,
+ $run1_desc,
+ $xhprof_data2,
+ $run2_desc,
+ $rep_symbol,
+ $sort,
+ $run1,
+ $run2) {
+
+
+ // Initialize what metrics we'll display based on data in Run2
+ init_metrics($xhprof_data2, $rep_symbol, $sort, true);
+
+ profiler_report($url_params,
+ $rep_symbol,
+ $sort,
+ $run1,
+ $run1_desc,
+ $xhprof_data1,
+ $run2,
+ $run2_desc,
+ $xhprof_data2);
+}
+
+
+/**
+ * Generate a XHProf Display View given the various URL parameters
+ * as arguments. The first argument is an object that implements
+ * the iXHProfRuns interface.
+ *
+ * @param object $xhprof_runs_impl An object that implements
+ * the iXHProfRuns interface
+ *.
+ * @param array $url_params Array of non-default URL params.
+ *
+ * @param string $source Category/type of the run. The source in
+ * combination with the run id uniquely
+ * determines a profiler run.
+ *
+ * @param string $run run id, or comma separated sequence of
+ * run ids. The latter is used if an aggregate
+ * report of the runs is desired.
+ *
+ * @param string $wts Comma separate list of integers.
+ * Represents the weighted ratio in
+ * which which a set of runs will be
+ * aggregated. [Used only for aggregate
+ * reports.]
+ *
+ * @param string $symbol Function symbol. If non-empty then the
+ * parent/child view of this function is
+ * displayed. If empty, a flat-profile view
+ * of the functions is displayed.
+ *
+ * @param string $run1 Base run id (for diff reports)
+ *
+ * @param string $run2 New run id (for diff reports)
+ *
+ */
+function displayXHProfReport($xhprof_runs_impl, $url_params, $source,
+ $run, $wts, $symbol, $sort, $run1, $run2) {
+
+ if ($run) { // specific run to display?
+
+ // run may be a single run or a comma separate list of runs
+ // that'll be aggregated. If "wts" (a comma separated list
+ // of integral weights is specified), the runs will be
+ // aggregated in that ratio.
+ //
+ $runs_array = explode(",", $run);
+
+ if (count($runs_array) == 1) {
+ $xhprof_data = $xhprof_runs_impl->get_run($runs_array[0],
+ $source,
+ $description);
+ } else {
+ if (!empty($wts)) {
+ $wts_array = explode(",", $wts);
+ } else {
+ $wts_array = null;
+ }
+ $data = xhprof_aggregate_runs($xhprof_runs_impl,
+ $runs_array, $wts_array, $source, false);
+ $xhprof_data = $data['raw'];
+ $description = $data['description'];
+ }
+
+
+ profiler_single_run_report($url_params,
+ $xhprof_data,
+ $description,
+ $symbol,
+ $sort,
+ $run);
+
+ } else if ($run1 && $run2) { // diff report for two runs
+
+ $xhprof_data1 = $xhprof_runs_impl->get_run($run1, $source, $description1);
+ $xhprof_data2 = $xhprof_runs_impl->get_run($run2, $source, $description2);
+
+ profiler_diff_report($url_params,
+ $xhprof_data1,
+ $description1,
+ $xhprof_data2,
+ $description2,
+ $symbol,
+ $sort,
+ $run1,
+ $run2);
+
+ } else {
+ echo "No XHProf runs specified in the URL.";
+ if (method_exists($xhprof_runs_impl, 'list_runs')) {
+ $xhprof_runs_impl->list_runs();
+ }
+ }
+}
diff --git a/plugins/xhprof/lib/xhprof_lib/utils/callgraph_utils.php b/plugins/xhprof/lib/xhprof_lib/utils/callgraph_utils.php
new file mode 100755
index 000000000..90f4c31a2
--- /dev/null
+++ b/plugins/xhprof/lib/xhprof_lib/utils/callgraph_utils.php
@@ -0,0 +1,486 @@
+ 1,
+ "gif" => 1,
+ "png" => 1,
+ "svg" => 1, // support scalable vector graphic
+ "ps" => 1,
+ );
+
+/**
+ * Send an HTTP header with the response. You MUST use this function instead
+ * of header() so that we can debug header issues because they're virtually
+ * impossible to debug otherwise. If you try to commit header(), SVN will
+ * reject your commit.
+ *
+ * @param string HTTP header name, like 'Location'
+ * @param string HTTP header value, like 'http://www.example.com/'
+ *
+ */
+function xhprof_http_header($name, $value) {
+
+ if (!$name) {
+ xhprof_error('http_header usage');
+ return null;
+ }
+
+ if (!is_string($value)) {
+ xhprof_error('http_header value not a string');
+ }
+
+ header($name.': '.$value, true);
+}
+
+/**
+ * Genearte and send MIME header for the output image to client browser.
+ *
+ * @author cjiang
+ */
+function xhprof_generate_mime_header($type, $length) {
+ switch ($type) {
+ case 'jpg':
+ $mime = 'image/jpeg';
+ break;
+ case 'gif':
+ $mime = 'image/gif';
+ break;
+ case 'png':
+ $mime = 'image/png';
+ break;
+ case 'svg':
+ $mime = 'image/svg+xml'; // content type for scalable vector graphic
+ break;
+ case 'ps':
+ $mime = 'application/postscript';
+ default:
+ $mime = false;
+ }
+
+ if ($mime) {
+ xhprof_http_header('Content-type', $mime);
+ xhprof_http_header('Content-length', (string)$length);
+ }
+}
+
+/**
+ * Generate image according to DOT script. This function will spawn a process
+ * with "dot" command and pipe the "dot_script" to it and pipe out the
+ * generated image content.
+ *
+ * @param dot_script, string, the script for DOT to generate the image.
+ * @param type, one of the supported image types, see
+ * $xhprof_legal_image_types.
+ * @returns, binary content of the generated image on success. empty string on
+ * failure.
+ *
+ * @author cjiang
+ */
+function xhprof_generate_image_by_dot($dot_script, $type) {
+ $descriptorspec = array(
+ // stdin is a pipe that the child will read from
+ 0 => array("pipe", "r"),
+ // stdout is a pipe that the child will write to
+ 1 => array("pipe", "w"),
+ // stderr is a pipe that the child will write to
+ 2 => array("pipe", "w")
+ );
+
+ $cmd = " dot -T".$type;
+
+ $process = proc_open( $cmd, $descriptorspec, $pipes, sys_get_temp_dir(), array( 'PATH' => getenv( 'PATH' ) ) );
+ if (is_resource($process)) {
+ fwrite($pipes[0], $dot_script);
+ fclose($pipes[0]);
+
+ $output = stream_get_contents($pipes[1]);
+
+ $err = stream_get_contents($pipes[2]);
+ if (!empty($err)) {
+ print "failed to execute cmd: \"$cmd\". stderr: `$err'\n";
+ exit;
+ }
+
+ fclose($pipes[2]);
+ fclose($pipes[1]);
+ proc_close($process);
+ return $output;
+ }
+ print "failed to execute cmd \"$cmd\"";
+ exit();
+}
+
+/*
+ * Get the children list of all nodes.
+ */
+function xhprof_get_children_table($raw_data) {
+ $children_table = array();
+ foreach ($raw_data as $parent_child => $info) {
+ list($parent, $child) = xhprof_parse_parent_child($parent_child);
+ if (!isset($children_table[$parent])) {
+ $children_table[$parent] = array($child);
+ } else {
+ $children_table[$parent][] = $child;
+ }
+ }
+ return $children_table;
+}
+
+/**
+ * Generate DOT script from the given raw phprof data.
+ *
+ * @param raw_data, phprof profile data.
+ * @param threshold, float, the threshold value [0,1). The functions in the
+ * raw_data whose exclusive wall times ratio are below the
+ * threshold will be filtered out and won't apprear in the
+ * generated image.
+ * @param page, string(optional), the root node name. This can be used to
+ * replace the 'main()' as the root node.
+ * @param func, string, the focus function.
+ * @param critical_path, bool, whether or not to display critical path with
+ * bold lines.
+ * @returns, string, the DOT script to generate image.
+ *
+ * @author cjiang
+ */
+function xhprof_generate_dot_script($raw_data, $threshold, $source, $page,
+ $func, $critical_path, $right=null,
+ $left=null) {
+
+ $max_width = 5;
+ $max_height = 3.5;
+ $max_fontsize = 35;
+ $max_sizing_ratio = 20;
+
+ $totals;
+
+ if ($left === null) {
+ // init_metrics($raw_data, null, null);
+ }
+ $sym_table = xhprof_compute_flat_info($raw_data, $totals);
+
+ if ($critical_path) {
+ $children_table = xhprof_get_children_table($raw_data);
+ $node = "main()";
+ $path = array();
+ $path_edges = array();
+ $visited = array();
+ while ($node) {
+ $visited[$node] = true;
+ if (isset($children_table[$node])) {
+ $max_child = null;
+ foreach ($children_table[$node] as $child) {
+
+ if (isset($visited[$child])) {
+ continue;
+ }
+ if ($max_child === null ||
+ abs($raw_data[xhprof_build_parent_child_key($node,
+ $child)]["wt"]) >
+ abs($raw_data[xhprof_build_parent_child_key($node,
+ $max_child)]["wt"])) {
+ $max_child = $child;
+ }
+ }
+ if ($max_child !== null) {
+ $path[$max_child] = true;
+ $path_edges[xhprof_build_parent_child_key($node, $max_child)] = true;
+ }
+ $node = $max_child;
+ } else {
+ $node = null;
+ }
+ }
+ }
+
+ // if it is a benchmark callgraph, we make the benchmarked function the root.
+ if ($source == "bm" && array_key_exists("main()", $sym_table)) {
+ $total_times = $sym_table["main()"]["ct"];
+ $remove_funcs = array("main()",
+ "hotprofiler_disable",
+ "call_user_func_array",
+ "xhprof_disable");
+
+ foreach ($remove_funcs as $cur_del_func) {
+ if (array_key_exists($cur_del_func, $sym_table) &&
+ $sym_table[$cur_del_func]["ct"] == $total_times) {
+ unset($sym_table[$cur_del_func]);
+ }
+ }
+ }
+
+ // use the function to filter out irrelevant functions.
+ if (!empty($func)) {
+ $interested_funcs = array();
+ foreach ($raw_data as $parent_child => $info) {
+ list($parent, $child) = xhprof_parse_parent_child($parent_child);
+ if ($parent == $func || $child == $func) {
+ $interested_funcs[$parent] = 1;
+ $interested_funcs[$child] = 1;
+ }
+ }
+ foreach ($sym_table as $symbol => $info) {
+ if (!array_key_exists($symbol, $interested_funcs)) {
+ unset($sym_table[$symbol]);
+ }
+ }
+ }
+
+ $result = "digraph call_graph {\n";
+
+ // Filter out functions whose exclusive time ratio is below threshold, and
+ // also assign a unique integer id for each function to be generated. In the
+ // meantime, find the function with the most exclusive time (potentially the
+ // performance bottleneck).
+ $cur_id = 0; $max_wt = 0;
+ foreach ($sym_table as $symbol => $info) {
+ if (empty($func) && abs($info["wt"] / $totals["wt"]) < $threshold) {
+ unset($sym_table[$symbol]);
+ continue;
+ }
+ if ($max_wt == 0 || $max_wt < abs($info["excl_wt"])) {
+ $max_wt = abs($info["excl_wt"]);
+ }
+ $sym_table[$symbol]["id"] = $cur_id;
+ $cur_id ++;
+ }
+
+ // Generate all nodes' information.
+ foreach ($sym_table as $symbol => $info) {
+ if ($info["excl_wt"] == 0) {
+ $sizing_factor = $max_sizing_ratio;
+ } else {
+ $sizing_factor = $max_wt / abs($info["excl_wt"]) ;
+ if ($sizing_factor > $max_sizing_ratio) {
+ $sizing_factor = $max_sizing_ratio;
+ }
+ }
+ $fillcolor = (($sizing_factor < 1.5) ?
+ ", style=filled, fillcolor=red" : "");
+
+ if ($critical_path) {
+ // highlight nodes along critical path.
+ if (!$fillcolor && array_key_exists($symbol, $path)) {
+ $fillcolor = ", style=filled, fillcolor=yellow";
+ }
+ }
+
+ $fontsize = ", fontsize="
+ .(int)($max_fontsize / (($sizing_factor - 1) / 10 + 1));
+
+ $width = ", width=".sprintf("%.1f", $max_width / $sizing_factor);
+ $height = ", height=".sprintf("%.1f", $max_height / $sizing_factor);
+
+ if ($symbol == "main()") {
+ $shape = "octagon";
+ $name = "Total: ".($totals["wt"] / 1000.0)." ms\\n";
+ $name .= addslashes(isset($page) ? $page : $symbol);
+ } else {
+ $shape = "box";
+ $name = addslashes($symbol)."\\nInc: ". sprintf("%.3f",$info["wt"] / 1000) .
+ " ms (" . sprintf("%.1f%%", 100 * $info["wt"] / $totals["wt"]).")";
+ }
+ if ($left === null) {
+ $label = ", label=\"".$name."\\nExcl: "
+ .(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms ("
+ .sprintf("%.1f%%", 100 * $info["excl_wt"] / $totals["wt"])
+ . ")\\n".$info["ct"]." total calls\"";
+ } else {
+ if (isset($left[$symbol]) && isset($right[$symbol])) {
+ $label = ", label=\"".addslashes($symbol).
+ "\\nInc: ".(sprintf("%.3f",$left[$symbol]["wt"] / 1000.0))
+ ." ms - "
+ .(sprintf("%.3f",$right[$symbol]["wt"] / 1000.0))." ms = "
+ .(sprintf("%.3f",$info["wt"] / 1000.0))." ms".
+ "\\nExcl: "
+ .(sprintf("%.3f",$left[$symbol]["excl_wt"] / 1000.0))
+ ." ms - ".(sprintf("%.3f",$right[$symbol]["excl_wt"] / 1000.0))
+ ." ms = ".(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms".
+ "\\nCalls: ".(sprintf("%.3f",$left[$symbol]["ct"]))." - "
+ .(sprintf("%.3f",$right[$symbol]["ct"]))." = "
+ .(sprintf("%.3f",$info["ct"]))."\"";
+ } else if (isset($left[$symbol])) {
+ $label = ", label=\"".addslashes($symbol).
+ "\\nInc: ".(sprintf("%.3f",$left[$symbol]["wt"] / 1000.0))
+ ." ms - 0 ms = ".(sprintf("%.3f",$info["wt"] / 1000.0))
+ ." ms"."\\nExcl: "
+ .(sprintf("%.3f",$left[$symbol]["excl_wt"] / 1000.0))
+ ." ms - 0 ms = "
+ .(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms".
+ "\\nCalls: ".(sprintf("%.3f",$left[$symbol]["ct"]))." - 0 = "
+ .(sprintf("%.3f",$info["ct"]))."\"";
+ } else {
+ $label = ", label=\"".addslashes($symbol).
+ "\\nInc: 0 ms - "
+ .(sprintf("%.3f",$right[$symbol]["wt"] / 1000.0))
+ ." ms = ".(sprintf("%.3f",$info["wt"] / 1000.0))." ms".
+ "\\nExcl: 0 ms - "
+ .(sprintf("%.3f",$right[$symbol]["excl_wt"] / 1000.0))
+ ." ms = ".(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms".
+ "\\nCalls: 0 - ".(sprintf("%.3f",$right[$symbol]["ct"]))
+ ." = ".(sprintf("%.3f",$info["ct"]))."\"";
+ }
+ }
+ $result .= "N" . $sym_table[$symbol]["id"];
+ $result .= "[shape=$shape ".$label.$width
+ .$height.$fontsize.$fillcolor."];\n";
+ }
+
+ // Generate all the edges' information.
+ foreach ($raw_data as $parent_child => $info) {
+ list($parent, $child) = xhprof_parse_parent_child($parent_child);
+
+ if (isset($sym_table[$parent]) && isset($sym_table[$child]) &&
+ (empty($func) ||
+ (!empty($func) && ($parent == $func || $child == $func)))) {
+
+ $label = $info["ct"] == 1 ? $info["ct"]." call" : $info["ct"]." calls";
+
+ $headlabel = $sym_table[$child]["wt"] > 0 ?
+ sprintf("%.1f%%", 100 * $info["wt"]
+ / $sym_table[$child]["wt"])
+ : "0.0%";
+
+ $taillabel = ($sym_table[$parent]["wt"] > 0) ?
+ sprintf("%.1f%%",
+ 100 * $info["wt"] /
+ ($sym_table[$parent]["wt"] - $sym_table["$parent"]["excl_wt"]))
+ : "0.0%";
+
+ $linewidth = 1;
+ $arrow_size = 1;
+
+ if ($critical_path &&
+ isset($path_edges[xhprof_build_parent_child_key($parent, $child)])) {
+ $linewidth = 10; $arrow_size = 2;
+ }
+
+ $result .= "N" . $sym_table[$parent]["id"] . " -> N"
+ . $sym_table[$child]["id"];
+ $result .= "[arrowsize=$arrow_size, color=grey, style=\"setlinewidth($linewidth)\","
+ ." label=\""
+ .$label."\", headlabel=\"".$headlabel
+ ."\", taillabel=\"".$taillabel."\" ]";
+ $result .= ";\n";
+
+ }
+ }
+ $result = $result . "\n}";
+
+ return $result;
+}
+
+function xhprof_render_diff_image($xhprof_runs_impl, $run1, $run2,
+ $type, $threshold, $source) {
+ $total1;
+ $total2;
+
+ $raw_data1 = $xhprof_runs_impl->get_run($run1, $source, $desc_unused);
+ $raw_data2 = $xhprof_runs_impl->get_run($run2, $source, $desc_unused);
+
+ // init_metrics($raw_data1, null, null);
+ $children_table1 = xhprof_get_children_table($raw_data1);
+ $children_table2 = xhprof_get_children_table($raw_data2);
+ $symbol_tab1 = xhprof_compute_flat_info($raw_data1, $total1);
+ $symbol_tab2 = xhprof_compute_flat_info($raw_data2, $total2);
+ $run_delta = xhprof_compute_diff($raw_data1, $raw_data2);
+ $script = xhprof_generate_dot_script($run_delta, $threshold, $source,
+ null, null, true,
+ $symbol_tab1, $symbol_tab2);
+ $content = xhprof_generate_image_by_dot($script, $type);
+
+ xhprof_generate_mime_header($type, strlen($content));
+ echo $content;
+}
+
+/**
+ * Generate image content from phprof run id.
+ *
+ * @param object $xhprof_runs_impl An object that implements
+ * the iXHProfRuns interface
+ * @param run_id, integer, the unique id for the phprof run, this is the
+ * primary key for phprof database table.
+ * @param type, string, one of the supported image types. See also
+ * $xhprof_legal_image_types.
+ * @param threshold, float, the threshold value [0,1). The functions in the
+ * raw_data whose exclusive wall times ratio are below the
+ * threshold will be filtered out and won't apprear in the
+ * generated image.
+ * @param func, string, the focus function.
+ * @returns, string, the DOT script to generate image.
+ *
+ * @author cjiang
+ */
+function xhprof_get_content_by_run($xhprof_runs_impl, $run_id, $type,
+ $threshold, $func, $source,
+ $critical_path) {
+ if (!$run_id)
+ return "";
+
+ $raw_data = $xhprof_runs_impl->get_run($run_id, $source, $description);
+ if (!$raw_data) {
+ xhprof_error("Raw data is empty");
+ return "";
+ }
+
+ $script = xhprof_generate_dot_script($raw_data, $threshold, $source,
+ $description, $func, $critical_path);
+
+ $content = xhprof_generate_image_by_dot($script, $type);
+ return $content;
+}
+
+/**
+ * Generate image from phprof run id and send it to client.
+ *
+ * @param object $xhprof_runs_impl An object that implements
+ * the iXHProfRuns interface
+ * @param run_id, integer, the unique id for the phprof run, this is the
+ * primary key for phprof database table.
+ * @param type, string, one of the supported image types. See also
+ * $xhprof_legal_image_types.
+ * @param threshold, float, the threshold value [0,1). The functions in the
+ * raw_data whose exclusive wall times ratio are below the
+ * threshold will be filtered out and won't apprear in the
+ * generated image.
+ * @param func, string, the focus function.
+ * @param bool, does this run correspond to a PHProfLive run or a dev run?
+ * @author cjiang
+ */
+function xhprof_render_image($xhprof_runs_impl, $run_id, $type, $threshold,
+ $func, $source, $critical_path) {
+
+ $content = xhprof_get_content_by_run($xhprof_runs_impl, $run_id, $type,
+ $threshold,
+ $func, $source, $critical_path);
+ if (!$content) {
+ print "Error: either we can not find profile data for run_id ".$run_id
+ ." or the threshold ".$threshold." is too small or you do not"
+ ." have 'dot' image generation utility installed.";
+ exit();
+ }
+
+ xhprof_generate_mime_header($type, strlen($content));
+ echo $content;
+}
diff --git a/plugins/xhprof/lib/xhprof_lib/utils/xhprof_lib.php b/plugins/xhprof/lib/xhprof_lib/utils/xhprof_lib.php
new file mode 100755
index 000000000..4a07e900f
--- /dev/null
+++ b/plugins/xhprof/lib/xhprof_lib/utils/xhprof_lib.php
@@ -0,0 +1,945 @@
+ array("Wall", "microsecs", "walltime"),
+ "ut" => array("User", "microsecs", "user cpu time"),
+ "st" => array("Sys", "microsecs", "system cpu time"),
+ "cpu" => array("Cpu", "microsecs", "cpu time"),
+ "mu" => array("MUse", "bytes", "memory usage"),
+ "pmu" => array("PMUse", "bytes", "peak memory usage"),
+ "samples" => array("Samples", "samples", "cpu time"));
+ return $possible_metrics;
+}
+
+/**
+ * Initialize the metrics we'll display based on the information
+ * in the raw data.
+ *
+ * @author Kannan
+ */
+function init_metrics($xhprof_data, $rep_symbol, $sort, $diff_report = false) {
+ global $stats;
+ global $pc_stats;
+ global $metrics;
+ global $diff_mode;
+ global $sortable_columns;
+ global $sort_col;
+ global $display_calls;
+
+ $diff_mode = $diff_report;
+
+ if (!empty($sort)) {
+ if (array_key_exists($sort, $sortable_columns)) {
+ $sort_col = $sort;
+ } else {
+ print("Invalid Sort Key $sort specified in URL");
+ }
+ }
+
+ // For C++ profiler runs, walltime attribute isn't present.
+ // In that case, use "samples" as the default sort column.
+ if (!isset($xhprof_data["main()"]["wt"])) {
+
+ if ($sort_col == "wt") {
+ $sort_col = "samples";
+ }
+
+ // C++ profiler data doesn't have call counts.
+ // ideally we should check to see if "ct" metric
+ // is present for "main()". But currently "ct"
+ // metric is artificially set to 1. So, relying
+ // on absence of "wt" metric instead.
+ $display_calls = false;
+ } else {
+ $display_calls = true;
+ }
+
+ // parent/child report doesn't support exclusive times yet.
+ // So, change sort hyperlinks to closest fit.
+ if (!empty($rep_symbol)) {
+ $sort_col = str_replace("excl_", "", $sort_col);
+ }
+
+ if ($display_calls) {
+ $stats = array("fn", "ct", "Calls%");
+ } else {
+ $stats = array("fn");
+ }
+
+ $pc_stats = $stats;
+
+ $possible_metrics = xhprof_get_possible_metrics();
+ foreach ($possible_metrics as $metric => $desc) {
+ if (isset($xhprof_data["main()"][$metric])) {
+ $metrics[] = $metric;
+ // flat (top-level reports): we can compute
+ // exclusive metrics reports as well.
+ $stats[] = $metric;
+ $stats[] = "I" . $desc[0] . "%";
+ $stats[] = "excl_" . $metric;
+ $stats[] = "E" . $desc[0] . "%";
+
+ // parent/child report for a function: we can
+ // only breakdown inclusive times correctly.
+ $pc_stats[] = $metric;
+ $pc_stats[] = "I" . $desc[0] . "%";
+ }
+ }
+}
+
+/*
+ * Get the list of metrics present in $xhprof_data as an array.
+ *
+ * @author Kannan
+ */
+function xhprof_get_metrics($xhprof_data) {
+
+ // get list of valid metrics
+ $possible_metrics = xhprof_get_possible_metrics();
+
+ // return those that are present in the raw data.
+ // We'll just look at the root of the subtree for this.
+ $metrics = array();
+ foreach ($possible_metrics as $metric => $desc) {
+ if (isset($xhprof_data["main()"][$metric])) {
+ $metrics[] = $metric;
+ }
+ }
+
+ return $metrics;
+}
+
+/**
+ * Takes a parent/child function name encoded as
+ * "a==>b" and returns array("a", "b").
+ *
+ * @author Kannan
+ */
+function xhprof_parse_parent_child($parent_child) {
+ $ret = explode("==>", $parent_child);
+
+ // Return if both parent and child are set
+ if (isset($ret[1])) {
+ return $ret;
+ }
+
+ return array(null, $ret[0]);
+}
+
+/**
+ * Given parent & child function name, composes the key
+ * in the format present in the raw data.
+ *
+ * @author Kannan
+ */
+function xhprof_build_parent_child_key($parent, $child) {
+ if ($parent) {
+ return $parent . "==>" . $child;
+ } else {
+ return $child;
+ }
+}
+
+
+/**
+ * Checks if XHProf raw data appears to be valid and not corrupted.
+ *
+ * @param int $run_id Run id of run to be pruned.
+ * [Used only for reporting errors.]
+ * @param array $raw_data XHProf raw data to be pruned
+ * & validated.
+ *
+ * @return bool true on success, false on failure
+ *
+ * @author Kannan
+ */
+function xhprof_valid_run($run_id, $raw_data) {
+
+ $main_info = $raw_data["main()"];
+ if (empty($main_info)) {
+ xhprof_error("XHProf: main() missing in raw data for Run ID: $run_id");
+ return false;
+ }
+
+ // raw data should contain either wall time or samples information...
+ if (isset($main_info["wt"])) {
+ $metric = "wt";
+ } else if (isset($main_info["samples"])) {
+ $metric = "samples";
+ } else {
+ xhprof_error("XHProf: Wall Time information missing from Run ID: $run_id");
+ return false;
+ }
+
+ foreach ($raw_data as $info) {
+ $val = $info[$metric];
+
+ // basic sanity checks...
+ if ($val < 0) {
+ xhprof_error("XHProf: $metric should not be negative: Run ID $run_id"
+ . serialize($info));
+ return false;
+ }
+ if ($val > (86400000000)) {
+ xhprof_error("XHProf: $metric > 1 day found in Run ID: $run_id "
+ . serialize($info));
+ return false;
+ }
+ }
+ return true;
+}
+
+
+/**
+ * Return a trimmed version of the XHProf raw data. Note that the raw
+ * data contains one entry for each unique parent/child function
+ * combination.The trimmed version of raw data will only contain
+ * entries where either the parent or child function is in the list
+ * of $functions_to_keep.
+ *
+ * Note: Function main() is also always kept so that overall totals
+ * can still be obtained from the trimmed version.
+ *
+ * @param array XHProf raw data
+ * @param array array of function names
+ *
+ * @return array Trimmed XHProf Report
+ *
+ * @author Kannan
+ */
+function xhprof_trim_run($raw_data, $functions_to_keep) {
+
+ // convert list of functions to a hash with function as the key
+ $function_map = array_fill_keys($functions_to_keep, 1);
+
+ // always keep main() as well so that overall totals can still
+ // be computed if need be.
+ $function_map['main()'] = 1;
+
+ $new_raw_data = array();
+ foreach ($raw_data as $parent_child => $info) {
+ list($parent, $child) = xhprof_parse_parent_child($parent_child);
+
+ if (isset($function_map[$parent]) || isset($function_map[$child])) {
+ $new_raw_data[$parent_child] = $info;
+ }
+ }
+
+ return $new_raw_data;
+}
+
+/**
+ * Takes raw XHProf data that was aggregated over "$num_runs" number
+ * of runs averages/nomalizes the data. Essentially the various metrics
+ * collected are divided by $num_runs.
+ *
+ * @author Kannan
+ */
+function xhprof_normalize_metrics($raw_data, $num_runs) {
+
+ if (empty($raw_data) || ($num_runs == 0)) {
+ return $raw_data;
+ }
+
+ $raw_data_total = array();
+
+ if (isset($raw_data["==>main()"]) && isset($raw_data["main()"])) {
+ xhprof_error("XHProf Error: both ==>main() and main() set in raw data...");
+ }
+
+ foreach ($raw_data as $parent_child => $info) {
+ foreach ($info as $metric => $value) {
+ $raw_data_total[$parent_child][$metric] = ($value / $num_runs);
+ }
+ }
+
+ return $raw_data_total;
+}
+
+
+/**
+ * Get raw data corresponding to specified array of runs
+ * aggregated by certain weightage.
+ *
+ * Suppose you have run:5 corresponding to page1.php,
+ * run:6 corresponding to page2.php,
+ * and run:7 corresponding to page3.php
+ *
+ * and you want to accumulate these runs in a 2:4:1 ratio. You
+ * can do so by calling:
+ *
+ * xhprof_aggregate_runs(array(5, 6, 7), array(2, 4, 1));
+ *
+ * The above will return raw data for the runs aggregated
+ * in 2:4:1 ratio.
+ *
+ * @param object $xhprof_runs_impl An object that implements
+ * the iXHProfRuns interface
+ * @param array $runs run ids of the XHProf runs..
+ * @param array $wts integral (ideally) weights for $runs
+ * @param string $source source to fetch raw data for run from
+ * @param bool $use_script_name If true, a fake edge from main() to
+ * to __script:: is introduced
+ * in the raw data so that after aggregations
+ * the script name is still preserved.
+ *
+ * @return array Return aggregated raw data
+ *
+ * @author Kannan
+ */
+function xhprof_aggregate_runs($xhprof_runs_impl, $runs,
+ $wts, $source="phprof",
+ $use_script_name=false) {
+
+ $raw_data_total = null;
+ $raw_data = null;
+ $metrics = array();
+
+ $run_count = count($runs);
+ $wts_count = count($wts);
+
+ if (($run_count == 0) ||
+ (($wts_count > 0) && ($run_count != $wts_count))) {
+ return array('description' => 'Invalid input..',
+ 'raw' => null);
+ }
+
+ $bad_runs = array();
+ foreach ($runs as $idx => $run_id) {
+
+ $raw_data = $xhprof_runs_impl->get_run($run_id, $source, $description);
+
+ // use the first run to derive what metrics to aggregate on.
+ if ($idx == 0) {
+ foreach ($raw_data["main()"] as $metric => $val) {
+ if ($metric != "pmu") {
+ // for now, just to keep data size small, skip "peak" memory usage
+ // data while aggregating.
+ // The "regular" memory usage data will still be tracked.
+ if (isset($val)) {
+ $metrics[] = $metric;
+ }
+ }
+ }
+ }
+
+ if (!xhprof_valid_run($run_id, $raw_data)) {
+ $bad_runs[] = $run_id;
+ continue;
+ }
+
+ if ($use_script_name) {
+ $page = $description;
+
+ // create a fake function '__script::$page', and have and edge from
+ // main() to '__script::$page'. We will also need edges to transfer
+ // all edges originating from main() to now originate from
+ // '__script::$page' to all function called from main().
+ //
+ // We also weight main() ever so slightly higher so that
+ // it shows up above the new entry in reports sorted by
+ // inclusive metrics or call counts.
+ if ($page) {
+ foreach ($raw_data["main()"] as $metric => $val) {
+ $fake_edge[$metric] = $val;
+ $new_main[$metric] = $val + 0.00001;
+ }
+ $raw_data["main()"] = $new_main;
+ $raw_data[xhprof_build_parent_child_key("main()",
+ "__script::$page")]
+ = $fake_edge;
+ } else {
+ $use_script_name = false;
+ }
+ }
+
+ // if no weights specified, use 1 as the default weightage..
+ $wt = ($wts_count == 0) ? 1 : $wts[$idx];
+
+ // aggregate $raw_data into $raw_data_total with appropriate weight ($wt)
+ foreach ($raw_data as $parent_child => $info) {
+ if ($use_script_name) {
+ // if this is an old edge originating from main(), it now
+ // needs to be from '__script::$page'
+ if (substr($parent_child, 0, 9) == "main()==>") {
+ $child = substr($parent_child, 9);
+ // ignore the newly added edge from main()
+ if (substr($child, 0, 10) != "__script::") {
+ $parent_child = xhprof_build_parent_child_key("__script::$page",
+ $child);
+ }
+ }
+ }
+
+ if (!isset($raw_data_total[$parent_child])) {
+ foreach ($metrics as $metric) {
+ $raw_data_total[$parent_child][$metric] = ($wt * $info[$metric]);
+ }
+ } else {
+ foreach ($metrics as $metric) {
+ $raw_data_total[$parent_child][$metric] += ($wt * $info[$metric]);
+ }
+ }
+ }
+ }
+
+ $runs_string = implode(",", $runs);
+
+ if (isset($wts)) {
+ $wts_string = "in the ratio (" . implode(":", $wts) . ")";
+ $normalization_count = array_sum($wts);
+ } else {
+ $wts_string = "";
+ $normalization_count = $run_count;
+ }
+
+ $run_count = $run_count - count($bad_runs);
+
+ $data['description'] = "Aggregated Report for $run_count runs: ".
+ "$runs_string $wts_string\n";
+ $data['raw'] = xhprof_normalize_metrics($raw_data_total,
+ $normalization_count);
+ $data['bad_runs'] = $bad_runs;
+
+ return $data;
+}
+
+
+/**
+ * Analyze hierarchical raw data, and compute per-function (flat)
+ * inclusive and exclusive metrics.
+ *
+ * Also, store overall totals in the 2nd argument.
+ *
+ * @param array $raw_data XHProf format raw profiler data.
+ * @param array &$overall_totals OUT argument for returning
+ * overall totals for various
+ * metrics.
+ * @return array Returns a map from function name to its
+ * call count and inclusive & exclusive metrics
+ * (such as wall time, etc.).
+ *
+ * @author Kannan Muthukkaruppan
+ */
+function xhprof_compute_flat_info($raw_data, &$overall_totals) {
+
+ global $display_calls;
+
+ $metrics = xhprof_get_metrics($raw_data);
+
+ $overall_totals = array("ct" => 0,
+ "wt" => 0,
+ "ut" => 0,
+ "st" => 0,
+ "cpu" => 0,
+ "mu" => 0,
+ "pmu" => 0,
+ "samples" => 0
+ );
+
+ // compute inclusive times for each function
+ $symbol_tab = xhprof_compute_inclusive_times($raw_data);
+
+ /* total metric value is the metric value for "main()" */
+ foreach ($metrics as $metric) {
+ $overall_totals[$metric] = $symbol_tab["main()"][$metric];
+ }
+
+ /*
+ * initialize exclusive (self) metric value to inclusive metric value
+ * to start with.
+ * In the same pass, also add up the total number of function calls.
+ */
+ foreach ($symbol_tab as $symbol => $info) {
+ foreach ($metrics as $metric) {
+ $symbol_tab[$symbol]["excl_" . $metric] = $symbol_tab[$symbol][$metric];
+ }
+ if ($display_calls) {
+ /* keep track of total number of calls */
+ $overall_totals["ct"] += $info["ct"];
+ }
+ }
+
+ /* adjust exclusive times by deducting inclusive time of children */
+ foreach ($raw_data as $parent_child => $info) {
+ list($parent, $child) = xhprof_parse_parent_child($parent_child);
+
+ if ($parent) {
+ foreach ($metrics as $metric) {
+ // make sure the parent exists hasn't been pruned.
+ if (isset($symbol_tab[$parent])) {
+ $symbol_tab[$parent]["excl_" . $metric] -= $info[$metric];
+ }
+ }
+ }
+ }
+
+ return $symbol_tab;
+}
+
+/**
+ * Hierarchical diff:
+ * Compute and return difference of two call graphs: Run2 - Run1.
+ *
+ * @author Kannan
+ */
+function xhprof_compute_diff($xhprof_data1, $xhprof_data2) {
+ global $display_calls;
+
+ // use the second run to decide what metrics we will do the diff on
+ $metrics = xhprof_get_metrics($xhprof_data2);
+
+ $xhprof_delta = $xhprof_data2;
+
+ foreach ($xhprof_data1 as $parent_child => $info) {
+
+ if (!isset($xhprof_delta[$parent_child])) {
+
+ // this pc combination was not present in run1;
+ // initialize all values to zero.
+ if ($display_calls) {
+ $xhprof_delta[$parent_child] = array("ct" => 0);
+ } else {
+ $xhprof_delta[$parent_child] = array();
+ }
+ foreach ($metrics as $metric) {
+ $xhprof_delta[$parent_child][$metric] = 0;
+ }
+ }
+
+ if ($display_calls) {
+ $xhprof_delta[$parent_child]["ct"] -= $info["ct"];
+ }
+
+ foreach ($metrics as $metric) {
+ $xhprof_delta[$parent_child][$metric] -= $info[$metric];
+ }
+ }
+
+ return $xhprof_delta;
+}
+
+
+/**
+ * Compute inclusive metrics for function. This code was factored out
+ * of xhprof_compute_flat_info().
+ *
+ * The raw data contains inclusive metrics of a function for each
+ * unique parent function it is called from. The total inclusive metrics
+ * for a function is therefore the sum of inclusive metrics for the
+ * function across all parents.
+ *
+ * @return array Returns a map of function name to total (across all parents)
+ * inclusive metrics for the function.
+ *
+ * @author Kannan
+ */
+function xhprof_compute_inclusive_times($raw_data) {
+ global $display_calls;
+
+ $metrics = xhprof_get_metrics($raw_data);
+
+ $symbol_tab = array();
+
+ /*
+ * First compute inclusive time for each function and total
+ * call count for each function across all parents the
+ * function is called from.
+ */
+ foreach ($raw_data as $parent_child => $info) {
+
+ list($parent, $child) = xhprof_parse_parent_child($parent_child);
+
+ if ($parent == $child) {
+ /*
+ * XHProf PHP extension should never trigger this situation any more.
+ * Recursion is handled in the XHProf PHP extension by giving nested
+ * calls a unique recursion-depth appended name (for example, foo@1).
+ */
+ xhprof_error("Error in Raw Data: parent & child are both: $parent");
+ return;
+ }
+
+ if (!isset($symbol_tab[$child])) {
+
+ if ($display_calls) {
+ $symbol_tab[$child] = array("ct" => $info["ct"]);
+ } else {
+ $symbol_tab[$child] = array();
+ }
+ foreach ($metrics as $metric) {
+ $symbol_tab[$child][$metric] = $info[$metric];
+ }
+ } else {
+ if ($display_calls) {
+ /* increment call count for this child */
+ $symbol_tab[$child]["ct"] += $info["ct"];
+ }
+
+ /* update inclusive times/metric for this child */
+ foreach ($metrics as $metric) {
+ $symbol_tab[$child][$metric] += $info[$metric];
+ }
+ }
+ }
+
+ return $symbol_tab;
+}
+
+
+/*
+ * Prunes XHProf raw data:
+ *
+ * Any node whose inclusive walltime accounts for less than $prune_percent
+ * of total walltime is pruned. [It is possible that a child function isn't
+ * pruned, but one or more of its parents get pruned. In such cases, when
+ * viewing the child function's hierarchical information, the cost due to
+ * the pruned parent(s) will be attributed to a special function/symbol
+ * "__pruned__()".]
+ *
+ * @param array $raw_data XHProf raw data to be pruned & validated.
+ * @param double $prune_percent Any edges that account for less than
+ * $prune_percent of time will be pruned
+ * from the raw data.
+ *
+ * @return array Returns the pruned raw data.
+ *
+ * @author Kannan
+ */
+function xhprof_prune_run($raw_data, $prune_percent) {
+
+ $main_info = $raw_data["main()"];
+ if (empty($main_info)) {
+ xhprof_error("XHProf: main() missing in raw data");
+ return false;
+ }
+
+ // raw data should contain either wall time or samples information...
+ if (isset($main_info["wt"])) {
+ $prune_metric = "wt";
+ } else if (isset($main_info["samples"])) {
+ $prune_metric = "samples";
+ } else {
+ xhprof_error("XHProf: for main() we must have either wt "
+ ."or samples attribute set");
+ return false;
+ }
+
+ // determine the metrics present in the raw data..
+ $metrics = array();
+ foreach ($main_info as $metric => $val) {
+ if (isset($val)) {
+ $metrics[] = $metric;
+ }
+ }
+
+ $prune_threshold = (($main_info[$prune_metric] * $prune_percent) / 100.0);
+
+ init_metrics($raw_data, null, null, false);
+ $flat_info = xhprof_compute_inclusive_times($raw_data);
+
+ foreach ($raw_data as $parent_child => $info) {
+
+ list($parent, $child) = xhprof_parse_parent_child($parent_child);
+
+ // is this child's overall total from all parents less than threshold?
+ if ($flat_info[$child][$prune_metric] < $prune_threshold) {
+ unset($raw_data[$parent_child]); // prune the edge
+ } else if ($parent &&
+ ($parent != "__pruned__()") &&
+ ($flat_info[$parent][$prune_metric] < $prune_threshold)) {
+
+ // Parent's overall inclusive metric is less than a threshold.
+ // All edges to the parent node will get nuked, and this child will
+ // be a dangling child.
+ // So instead change its parent to be a special function __pruned__().
+ $pruned_edge = xhprof_build_parent_child_key("__pruned__()", $child);
+
+ if (isset($raw_data[$pruned_edge])) {
+ foreach ($metrics as $metric) {
+ $raw_data[$pruned_edge][$metric]+=$raw_data[$parent_child][$metric];
+ }
+ } else {
+ $raw_data[$pruned_edge] = $raw_data[$parent_child];
+ }
+
+ unset($raw_data[$parent_child]); // prune the edge
+ }
+ }
+
+ return $raw_data;
+}
+
+
+/**
+ * Set one key in an array and return the array
+ *
+ * @author Kannan
+ */
+function xhprof_array_set($arr, $k, $v) {
+ $arr[$k] = $v;
+ return $arr;
+}
+
+/**
+ * Removes/unsets one key in an array and return the array
+ *
+ * @author Kannan
+ */
+function xhprof_array_unset($arr, $k) {
+ unset($arr[$k]);
+ return $arr;
+}
+
+/**
+ * Type definitions for URL params
+ */
+define('XHPROF_STRING_PARAM', 1);
+define('XHPROF_UINT_PARAM', 2);
+define('XHPROF_FLOAT_PARAM', 3);
+define('XHPROF_BOOL_PARAM', 4);
+
+
+/**
+ * Internal helper function used by various
+ * xhprof_get_param* flavors for various
+ * types of parameters.
+ *
+ * @param string name of the URL query string param
+ *
+ * @author Kannan
+ */
+function xhprof_get_param_helper($param) {
+ $val = null;
+ if (isset($_GET[$param]))
+ $val = $_GET[$param];
+ else if (isset($_POST[$param])) {
+ $val = $_POST[$param];
+ }
+ return $val;
+}
+
+/**
+ * Extracts value for string param $param from query
+ * string. If param is not specified, return the
+ * $default value.
+ *
+ * @author Kannan
+ */
+function xhprof_get_string_param($param, $default = '') {
+ $val = xhprof_get_param_helper($param);
+
+ if ($val === null)
+ return $default;
+
+ return $val;
+}
+
+/**
+ * Extracts value for unsigned integer param $param from
+ * query string. If param is not specified, return the
+ * $default value.
+ *
+ * If value is not a valid unsigned integer, logs error
+ * and returns null.
+ *
+ * @author Kannan
+ */
+function xhprof_get_uint_param($param, $default = 0) {
+ $val = xhprof_get_param_helper($param);
+
+ if ($val === null)
+ $val = $default;
+
+ // trim leading/trailing whitespace
+ $val = trim($val);
+
+ // if it only contains digits, then ok..
+ if (ctype_digit($val)) {
+ return $val;
+ }
+
+ xhprof_error("$param is $val. It must be an unsigned integer.");
+ return null;
+}
+
+
+/**
+ * Extracts value for a float param $param from
+ * query string. If param is not specified, return
+ * the $default value.
+ *
+ * If value is not a valid unsigned integer, logs error
+ * and returns null.
+ *
+ * @author Kannan
+ */
+function xhprof_get_float_param($param, $default = 0) {
+ $val = xhprof_get_param_helper($param);
+
+ if ($val === null)
+ $val = $default;
+
+ // trim leading/trailing whitespace
+ $val = trim($val);
+
+ // TBD: confirm the value is indeed a float.
+ if (true) // for now..
+ return (float)$val;
+
+ xhprof_error("$param is $val. It must be a float.");
+ return null;
+}
+
+/**
+ * Extracts value for a boolean param $param from
+ * query string. If param is not specified, return
+ * the $default value.
+ *
+ * If value is not a valid unsigned integer, logs error
+ * and returns null.
+ *
+ * @author Kannan
+ */
+function xhprof_get_bool_param($param, $default = false) {
+ $val = xhprof_get_param_helper($param);
+
+ if ($val === null)
+ $val = $default;
+
+ // trim leading/trailing whitespace
+ $val = trim($val);
+
+ switch (strtolower($val)) {
+ case '0':
+ case '1':
+ $val = (bool)$val;
+ break;
+ case 'true':
+ case 'on':
+ case 'yes':
+ $val = true;
+ break;
+ case 'false':
+ case 'off':
+ case 'no':
+ $val = false;
+ break;
+ default:
+ xhprof_error("$param is $val. It must be a valid boolean string.");
+ return null;
+ }
+
+ return $val;
+
+}
+
+/**
+ * Initialize params from URL query string. The function
+ * creates globals variables for each of the params
+ * and if the URL query string doesn't specify a particular
+ * param initializes them with the corresponding default
+ * value specified in the input.
+ *
+ * @params array $params An array whose keys are the names
+ * of URL params who value needs to
+ * be retrieved from the URL query
+ * string. PHP globals are created
+ * with these names. The value is
+ * itself an array with 2-elems (the
+ * param type, and its default value).
+ * If a param is not specified in the
+ * query string the default value is
+ * used.
+ * @author Kannan
+ */
+function xhprof_param_init($params) {
+ /* Create variables specified in $params keys, init defaults */
+ foreach ($params as $k => $v) {
+ switch ($v[0]) {
+ case XHPROF_STRING_PARAM:
+ $p = xhprof_get_string_param($k, $v[1]);
+ break;
+ case XHPROF_UINT_PARAM:
+ $p = xhprof_get_uint_param($k, $v[1]);
+ break;
+ case XHPROF_FLOAT_PARAM:
+ $p = xhprof_get_float_param($k, $v[1]);
+ break;
+ case XHPROF_BOOL_PARAM:
+ $p = xhprof_get_bool_param($k, $v[1]);
+ break;
+ default:
+ xhprof_error("Invalid param type passed to xhprof_param_init: "
+ . $v[0]);
+ exit();
+ }
+
+ if ($k === 'run') {
+ $p = implode(',', array_filter(explode(',', $p), 'ctype_xdigit'));
+ }
+
+ // create a global variable using the parameter name.
+ $GLOBALS[$k] = $p;
+ }
+}
+
+
+/**
+ * Given a partial query string $q return matching function names in
+ * specified XHProf run. This is used for the type ahead function
+ * selector.
+ *
+ * @author Kannan
+ */
+function xhprof_get_matching_functions($q, $xhprof_data) {
+
+ $matches = array();
+
+ foreach ($xhprof_data as $parent_child => $info) {
+ list($parent, $child) = xhprof_parse_parent_child($parent_child);
+ if (stripos($parent, $q) !== false) {
+ $matches[$parent] = 1;
+ }
+ if (stripos($child, $q) !== false) {
+ $matches[$child] = 1;
+ }
+ }
+
+ $res = array_keys($matches);
+
+ // sort it so the answers are in some reliable order...
+ asort($res);
+
+ return ($res);
+}
diff --git a/plugins/xhprof/lib/xhprof_lib/utils/xhprof_runs.php b/plugins/xhprof/lib/xhprof_lib/utils/xhprof_runs.php
new file mode 100755
index 000000000..7af62a966
--- /dev/null
+++ b/plugins/xhprof/lib/xhprof_lib/utils/xhprof_runs.php
@@ -0,0 +1,167 @@
+suffix;
+
+ if (!empty($this->dir)) {
+ $file = $this->dir . "/" . $file;
+ }
+ return $file;
+ }
+
+ public function __construct($dir = null) {
+
+ // if user hasn't passed a directory location,
+ // we use the xhprof.output_dir ini setting
+ // if specified, else we default to the directory
+ // in which the error_log file resides.
+
+ if (empty($dir)) {
+ $dir = ini_get("xhprof.output_dir");
+ if (empty($dir)) {
+
+ $dir = sys_get_temp_dir();
+
+ xhprof_error("Warning: Must specify directory location for XHProf runs. " .
+ "Trying {$dir} as default. You can either pass the " .
+ "directory location as an argument to the constructor " .
+ "for XHProfRuns_Default() or set xhprof.output_dir " .
+ "ini param.");
+ }
+ }
+ $this->dir = $dir;
+ }
+
+ public function get_run($run_id, $type, &$run_desc) {
+ $file_name = $this->file_name($run_id, $type);
+
+ if (!file_exists($file_name)) {
+ xhprof_error("Could not find file $file_name");
+ $run_desc = "Invalid Run Id = $run_id";
+ return null;
+ }
+
+ $contents = file_get_contents($file_name);
+ $run_desc = "XHProf Run (Namespace=$type)";
+ return unserialize($contents);
+ }
+
+ public function save_run($xhprof_data, $type, $run_id = null) {
+
+ // Use PHP serialize function to store the XHProf's
+ // raw profiler data.
+ $xhprof_data = serialize($xhprof_data);
+
+ if ($run_id === null) {
+ $run_id = $this->gen_run_id($type);
+ }
+
+ $file_name = $this->file_name($run_id, $type);
+ $file = fopen($file_name, 'w');
+
+ if ($file) {
+ fwrite($file, $xhprof_data);
+ fclose($file);
+ } else {
+ xhprof_error("Could not open $file_name\n");
+ }
+
+ // echo "Saved run in {$file_name}.\nRun id = {$run_id}.\n";
+ return $run_id;
+ }
+
+ function list_runs_callback($a, $b) {
+ return filemtime($b) - filemtime($a);
+ }
+
+ function list_runs() {
+ if (is_dir($this->dir)) {
+ echo " Existing runs:\n\n";
+ $files = glob("{$this->dir}/*.{$this->suffix}");
+ usort($files, 'list_runs_callback');
+ foreach ($files as $file) {
+ list($run, $source) = explode('.', basename($file));
+ echo ''
+ . htmlentities(basename($file)) . " "
+ . date("Y-m-d H:i:s", filemtime($file)) . " \n";
+ }
+ echo " \n";
+ }
+ }
+}
diff --git a/plugins/xui/ico.png b/plugins/xui/ico.png
new file mode 100644
index 000000000..b7f2362c5
Binary files /dev/null and b/plugins/xui/ico.png differ
diff --git a/plugins/xui/index.html b/plugins/xui/index.html
new file mode 100755
index 000000000..81e32de12
--- /dev/null
+++ b/plugins/xui/index.html
@@ -0,0 +1,25 @@
+
+
+
+
\ No newline at end of file
diff --git a/plugins/xui/index.py b/plugins/xui/index.py
new file mode 100755
index 000000000..0fc9d7764
--- /dev/null
+++ b/plugins/xui/index.py
@@ -0,0 +1,223 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+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 'xui'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+
+ return tmp
+
+def status():
+ cmd = "ps -ef|grep x-ui |grep -v grep | grep -v python | awk '{print $2}'"
+ data = mw.execShell(cmd)
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+
+def getServiceFile():
+ systemDir = mw.systemdCfgDir()
+ return systemDir + '/xui.service'
+
+
+def getXuiPort():
+ return '8349'
+
+
+def __release_port(port):
+ from collections import namedtuple
+ try:
+ from utils.firewall import Firewall as MwFirewall
+ MwFirewall.instance().addAcceptPort(port, 'xui', 'port')
+ return port
+ except Exception as e:
+ return "Release failed {}".format(e)
+
+def __delete_port(port):
+ from collections import namedtuple
+ try:
+ from utils.firewall import Firewall as MwFirewall
+ MwFirewall.instance().delAcceptPortCmd(port, 'tcp')
+ return port
+ except Exception as e:
+ return "Delete failed {}".format(e)
+
+
+def pSqliteDb(dbname='databases'):
+ conn = mw.M(dbname).dbPos('/etc/x-ui', 'x-ui')
+ return conn
+
+def initDreplace():
+ return 'ok'
+
+
+def xuiOp(method):
+ file = initDreplace()
+
+ if not mw.isAppleSystem():
+ mw.execShell('systemctl daemon-reload')
+ data = mw.execShell('systemctl ' + method + ' x-ui')
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+ return 'fail'
+
+
+def start():
+ openPort()
+ return xuiOp('start')
+
+
+def stop():
+ closePort()
+ return xuiOp('stop')
+
+
+def restart():
+ return xuiOp('restart')
+
+
+def reload():
+ return redisOp('reload')
+
+
+def initdStatus():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status x-ui | grep loaded | grep "enabled;"'
+ data = mw.execShell(shell_cmd)
+ if data[0] == '':
+ return 'fail'
+ return 'ok'
+
+def initdInstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+ mw.execShell('systemctl enable x-ui')
+ return 'ok'
+
+
+def initdUinstall():
+ if mw.isAppleSystem():
+ return "Apple Computer does not support"
+ mw.execShell('systemctl disable x-ui')
+ return 'ok'
+
+def openPort():
+ setting = pSqliteDb('settings')
+ port_data = setting.field('id,key,value').where("key=?", ('webPort',)).find()
+ port = port_data['value']
+ __release_port(port)
+
+def closePort():
+ setting = pSqliteDb('settings')
+ port_data = setting.field('id,key,value').where("key=?", ('webPort',)).find()
+ port = port_data['value']
+ __delete_port(port)
+
+def getXuiInfo():
+
+ data = {}
+ user = pSqliteDb('users')
+ info = user.field('username,password').where("id=?", (1,)).find()
+
+ setting = pSqliteDb('settings')
+ port_data = setting.field('id,key,value').where("key=?", ('webPort',)).find()
+ path_data = setting.field('id,key,value').where("key=?", ('webBasePath',)).find()
+
+ if path_data is not None:
+ data['path'] = path_data['value']
+ else:
+ data['path'] = '/'
+
+ data['username'] = info['username']
+ data['password'] = info['password']
+ data['port'] = port_data['value']
+
+ data['ip'] = mw.getHostAddr()
+ return mw.returnJson(True, 'ok', data)
+
+def installPreInspection():
+ sys = mw.execShell("cat /etc/*-release | grep PRETTY_NAME |awk -F = '{print $2}' | awk -F '\"' '{print $2}'| awk '{print $1}'")
+
+ if sys[1] != '':
+ return '不支持该系统'
+
+ sys_id = mw.execShell("cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F '\"' '{print $2}'")
+
+ sysName = sys[0].strip().lower()
+ sysId = sys_id[0].strip()
+
+ if sysName in ('opensuse'):
+ return '不支持该系统'
+
+ return 'ok'
+
+
+if __name__ == "__main__":
+ 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(getServiceFile())
+ elif func == 'info':
+ print(getXuiInfo())
+ else:
+ print('error')
diff --git a/plugins/xui/info.json b/plugins/xui/info.json
new file mode 100755
index 000000000..4959bfc54
--- /dev/null
+++ b/plugins/xui/info.json
@@ -0,0 +1,19 @@
+{
+ "sort": 7,
+ "ps": "支持多协议多用户的xray面板",
+ "install_pre_inspection":true,
+ "name": "xui",
+ "title": "xui",
+ "shell": "install.sh",
+ "versions":["1.0"],
+ "updates":["1.0"],
+ "tip": "soft",
+ "checks": "server/xui",
+ "path": "server/xui",
+ "display": 1,
+ "author": "FranzKafkaYu",
+ "date": "2024-12-03",
+ "home": "https://github.com/FranzKafkaYu/x-ui",
+ "type": 0,
+ "pid": "5"
+}
\ No newline at end of file
diff --git a/plugins/xui/install.sh b/plugins/xui/install.sh
new file mode 100755
index 000000000..4b90aeac1
--- /dev/null
+++ b/plugins/xui/install.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+sysArch=`arch`
+sysName=`uname`
+
+
+# cd /www/server/mdserver-web && python3 plugins/xui/index.py info
+# cd /www/server/mdserver-web/plugins/xui && /bin/bash install.sh install 1.0
+
+VERSION=$2
+Install_app()
+{
+ mkdir -p $serverPath/source
+ mkdir -p $serverPath/source/xui
+ mkdir -p $serverPath/xui
+
+ echo "$VERSION" > $serverPath/xui/version.pl
+
+ bash ${curPath}/install_xui.sh
+
+
+ # start xray
+ cd /usr/local/x-ui && bin/xray-linux-amd64 -c bin/config.json
+
+ cd ${rootPath} && python3 plugins/xui/index.py start
+ echo '安装完成'
+}
+
+Uninstall_app()
+{
+ cd ${rootPath} && python3 plugins/xui/index.py stop
+
+ rm -rf $serverPath/xui
+ echo 'y' | x-ui uninstall
+ rm -rf /usr/bin/x-ui
+ echo '卸载完成'
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_app $2
+else
+ Uninstall_app $2
+fi
diff --git a/plugins/xui/install_xui.sh b/plugins/xui/install_xui.sh
new file mode 100644
index 000000000..0309c9460
--- /dev/null
+++ b/plugins/xui/install_xui.sh
@@ -0,0 +1,156 @@
+#!/usr/bin/env bash
+
+red='\033[0;31m'
+green='\033[0;32m'
+yellow='\033[0;33m'
+plain='\033[0m'
+
+cur_dir=$(pwd)
+
+# check root
+[[ $EUID -ne 0 ]] && echo -e "${red}错误:${plain} 必须使用root用户运行此脚本!\n" && exit 1
+
+# check os
+if [[ -f /etc/redhat-release ]]; then
+ release="centos"
+elif cat /etc/issue | grep -Eqi "debian"; then
+ release="debian"
+elif cat /etc/issue | grep -Eqi "ubuntu"; then
+ release="ubuntu"
+elif cat /etc/issue | grep -Eqi "centos|red hat|redhat"; then
+ release="centos"
+elif cat /proc/version | grep -Eqi "debian"; then
+ release="debian"
+elif cat /proc/version | grep -Eqi "ubuntu"; then
+ release="ubuntu"
+elif cat /proc/version | grep -Eqi "centos|red hat|redhat"; then
+ release="centos"
+else
+ echo -e "${red}未检测到系统版本,请联系脚本作者!${plain}\n" && exit 1
+fi
+
+arch=$(arch)
+
+if [[ $arch == "x86_64" || $arch == "x64" || $arch == "s390x" || $arch == "amd64" ]]; then
+ arch="amd64"
+elif [[ $arch == "aarch64" || $arch == "arm64" ]]; then
+ arch="arm64"
+else
+ arch="amd64"
+ echo -e "${red}检测架构失败,使用默认架构: ${arch}${plain}"
+fi
+
+echo "架构: ${arch}"
+
+if [ $(getconf WORD_BIT) != '32' ] && [ $(getconf LONG_BIT) != '64' ]; then
+ echo "本软件不支持 32 位系统(x86),请使用 64 位系统(x86_64),如果检测有误,请联系作者"
+ exit -1
+fi
+
+os_version=""
+
+# os version
+if [[ -f /etc/os-release ]]; then
+ os_version=$(awk -F'[= ."]' '/VERSION_ID/{print $3}' /etc/os-release)
+fi
+if [[ -z "$os_version" && -f /etc/lsb-release ]]; then
+ os_version=$(awk -F'[= ."]+' '/DISTRIB_RELEASE/{print $2}' /etc/lsb-release)
+fi
+
+if [[ x"${release}" == x"centos" ]]; then
+ if [[ ${os_version} -le 6 ]]; then
+ echo -e "${red}请使用 CentOS 7 或更高版本的系统!${plain}\n" && exit 1
+ fi
+elif [[ x"${release}" == x"ubuntu" ]]; then
+ if [[ ${os_version} -lt 16 ]]; then
+ echo -e "${red}请使用 Ubuntu 16 或更高版本的系统!${plain}\n" && exit 1
+ fi
+elif [[ x"${release}" == x"debian" ]]; then
+ if [[ ${os_version} -lt 8 ]]; then
+ echo -e "${red}请使用 Debian 8 或更高版本的系统!${plain}\n" && exit 1
+ fi
+fi
+
+install_base() {
+ if [[ x"${release}" == x"centos" ]]; then
+ yum install wget curl tar jq -y
+ else
+ apt install wget curl tar jq -y
+ fi
+}
+
+#This function will be called when user installed x-ui out of sercurity
+config_after_install() {
+ echo -e "${yellow}出于安全考虑,安装/更新完成后需要强制修改端口与账户密码${plain}"
+ if [[ ! -f "/etc/x-ui/x-ui.db" ]]; then
+ local usernameTemp=$(head -c 6 /dev/urandom | base64)
+ local passwordTemp=$(head -c 6 /dev/urandom | base64)
+ local portTemp=$(echo $RANDOM)
+ /usr/local/x-ui/x-ui setting -username ${usernameTemp} -password ${passwordTemp}
+ /usr/local/x-ui/x-ui setting -port ${portTemp}
+ echo -e "检测到您属于全新安装,出于安全考虑已自动为您生成随机用户与端口:"
+ echo -e "###############################################"
+ echo -e "${green}面板登录用户名:${usernameTemp}${plain}"
+ echo -e "${green}面板登录用户密码:${passwordTemp}${plain}"
+ echo -e "${red}面板登录端口:${portTemp}${plain}"
+ echo -e "###############################################"
+ echo -e "${red}如您遗忘了面板登录相关信息,可在安装完成后输入x-ui,输入选项7查看面板登录信息${plain}"
+ else
+ echo -e "${red}当前属于版本升级,保留之前设置项,登录方式保持不变,可输入x-ui后键入数字7查看面板登录信息${plain}"
+ fi
+}
+
+install_x-ui() {
+ systemctl stop x-ui
+ cd /usr/local/
+
+ if [ $# == 0 ]; then
+ last_version=$(curl -Lsk "https://api.github.com/repos/FranzKafkaYu/x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
+ if [[ ! -n "$last_version" ]]; then
+ echo -e "${red}检测 x-ui 版本失败,可能是超出 Github API 限制,请稍后再试,或手动指定 x-ui 版本安装${plain}"
+ exit 1
+ fi
+ echo -e "检测到 x-ui 最新版本:${last_version},开始安装"
+ wget -N --no-check-certificate -O /usr/local/x-ui-linux-${arch}.tar.gz https://github.com/FranzKafkaYu/x-ui/releases/download/${last_version}/x-ui-linux-${arch}.tar.gz
+ if [[ $? -ne 0 ]]; then
+ echo -e "${red}下载 x-ui 失败,请确保你的服务器能够下载 Github 的文件${plain}"
+ exit 1
+ fi
+ else
+ last_version=$1
+ url="https://github.com/FranzKafkaYu/x-ui/releases/download/${last_version}/x-ui-linux-${arch}.tar.gz"
+ echo -e "开始安装 x-ui v$1"
+ wget -N --no-check-certificate -O /usr/local/x-ui-linux-${arch}.tar.gz ${url}
+ if [[ $? -ne 0 ]]; then
+ echo -e "${red}下载 x-ui v$1 失败,请确保此版本存在${plain}"
+ exit 1
+ fi
+ fi
+
+ if [[ -e /usr/local/x-ui/ ]]; then
+ rm /usr/local/x-ui/ -rf
+ fi
+
+ tar zxvf x-ui-linux-${arch}.tar.gz
+ rm x-ui-linux-${arch}.tar.gz -f
+ cd x-ui
+ chmod +x x-ui bin/xray-linux-${arch}
+ cp -f x-ui.service /etc/systemd/system/
+ wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/FranzKafkaYu/x-ui/main/x-ui.sh
+ chmod +x /usr/local/x-ui/x-ui.sh
+ chmod +x /usr/bin/x-ui
+ config_after_install
+ #echo -e "如果是全新安装,默认网页端口为 ${green}54321${plain},用户名和密码默认都是 ${green}admin${plain}"
+ #echo -e "请自行确保此端口没有被其他程序占用,${yellow}并且确保 54321 端口已放行${plain}"
+ # echo -e "若想将 54321 修改为其它端口,输入 x-ui 命令进行修改,同样也要确保你修改的端口也是放行的"
+ #echo -e ""
+ #echo -e "如果是更新面板,则按你之前的方式访问面板"
+ #echo -e ""
+ systemctl daemon-reload
+ systemctl enable x-ui
+ systemctl start x-ui
+}
+
+echo -e "${green}开始安装${plain}"
+install_base
+install_x-ui $1
\ No newline at end of file
diff --git a/plugins/xui/js/xui.js b/plugins/xui/js/xui.js
new file mode 100644
index 000000000..e9bb21e61
--- /dev/null
+++ b/plugins/xui/js/xui.js
@@ -0,0 +1,64 @@
+function xuiPost(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'xui';
+ req_data['func'] = method;
+ req_data['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ //错误展示10S
+ layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function xuiCommonFunc(){
+
+ xuiPost('info', '', {}, function(rdata){
+ var rdata = $.parseJSON(rdata.data);
+ var info = rdata['data'];
+ var con = '\
+ 用户名 \
+ \
+
';
+
+ con += '\
+ 密码 \
+ \
+
';
+ con += '\
+ 端口 \
+ \
+
';
+
+ con += '\
+ 路径 \
+ \
+
';
+
+ con += '\
+ 打开XUI \
+
';
+
+ $(".soft-man-con").html(con);
+ $('#open_url').click(function(){
+ var url = 'http://' + info.ip + ':' + info.port + info.path;
+ window.open(url);
+ copyText(url);
+ });
+ });
+}
\ No newline at end of file
diff --git a/plugins/zabbix/conf/zabbix.conf.php b/plugins/zabbix/conf/zabbix.conf.php
new file mode 100644
index 000000000..882a34034
--- /dev/null
+++ b/plugins/zabbix/conf/zabbix.conf.php
@@ -0,0 +1,58 @@
+ 'http://localhost:9200',
+// 'text' => 'http://localhost:9200'
+//];
+// Value types stored in Elasticsearch.
+//$HISTORY['types'] = ['uint', 'text'];
+
+// Used for SAML authentication.
+// Uncomment to override the default paths to SP private key, SP and IdP X.509 certificates, and to set extra settings.
+//$SSO['SP_KEY'] = 'conf/certs/sp.key';
+//$SSO['SP_CERT'] = 'conf/certs/sp.crt';
+//$SSO['IDP_CERT'] = 'conf/certs/idp.crt';
+//$SSO['SETTINGS'] = [];
+
+// If set to false, support for HTTP authentication will be disabled.
+// $ALLOW_HTTP_AUTH = true;
diff --git a/plugins/zabbix/conf/zabbix.nginx.conf b/plugins/zabbix/conf/zabbix.nginx.conf
new file mode 100644
index 000000000..5a1e6eb45
--- /dev/null
+++ b/plugins/zabbix/conf/zabbix.nginx.conf
@@ -0,0 +1,45 @@
+server
+{
+ listen {$ZABBIX_PORT};
+ server_name 127.0.0.1;
+ index index.php index.html index.htm default.php default.htm default.html;
+ root {$ZABBIX_ROOT};
+
+ #SSL-START
+ #error_page 404/404.html;
+ #SSL-END
+
+ #301-START
+
+ #PROXY-START
+
+ #ERROR-PAGE-START
+ #error_page 404 /404.html;
+ #error_page 502 /502.html;
+ #ERROR-PAGE-END
+
+ #PHP-INFO-START
+ include {$SERVER_PATH}/web_conf/php/conf/enable-{$PHP_VER}.conf;
+ #PHP-INFO-END
+
+ #禁止访问的文件或目录
+ location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
+ {
+ return 404;
+ }
+
+ #一键申请SSL证书验证目录相关设置
+ location ~ \.well-known{
+ allow all;
+ }
+
+ location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf|js|css)$
+ {
+ expires 30d;
+ error_log /dev/null;
+ access_log /dev/null;
+ }
+
+ access_log /www/wwwlogs/zabbix.log main;
+ error_log /www/wwwlogs/zabbix.error.log;
+}
\ No newline at end of file
diff --git a/plugins/zabbix/conf/zabbix_agentd.conf b/plugins/zabbix/conf/zabbix_agentd.conf
new file mode 100644
index 000000000..9ffe32e8b
--- /dev/null
+++ b/plugins/zabbix/conf/zabbix_agentd.conf
@@ -0,0 +1,18 @@
+PidFile=/run/zabbix/zabbix_agentd.pid
+LogFile=/var/log/zabbix/zabbix_agentd.log
+LogFileSize=1
+
+ListenIP=0.0.0.0
+ListenPort=10050
+EnableRemoteCommands=1
+Timeout=3
+
+Server=127.0.0.1
+ServerActive=127.0.0.1
+
+Hostname=Zabbix server
+Include=/etc/zabbix/zabbix_agentd.d/*.conf
+
+# Include=/usr/local/etc/zabbix_agentd.userparams.conf
+# Include=/usr/local/etc/zabbix_agentd.conf.d/
+# Include=/usr/local/etc/zabbix_agentd.conf.d/*.conf
diff --git a/plugins/zabbix/conf/zabbix_server.conf b/plugins/zabbix/conf/zabbix_server.conf
new file mode 100644
index 000000000..b0241a422
--- /dev/null
+++ b/plugins/zabbix/conf/zabbix_server.conf
@@ -0,0 +1,13 @@
+LogFile=/var/log/zabbix/zabbix_server.log
+LogFileSize=1
+PidFile=/run/zabbix/zabbix_server.pid
+SocketDir=/run/zabbix
+DBHost=127.0.0.1
+DBPort={$ZABBIX_DB_PORT}
+DBName=zabbix
+DBUser=zabbix
+DBPassword={$ZABBIX_DB_PASS}
+ListenPort=10051
+#EnableGlobalScripts=1
+SNMPTrapperFile=/var/log/snmptrap/snmptrap.log
+
diff --git a/plugins/zabbix/conf/zabbix_server.conf.6.bak b/plugins/zabbix/conf/zabbix_server.conf.6.bak
new file mode 100644
index 000000000..02d1eaa2e
--- /dev/null
+++ b/plugins/zabbix/conf/zabbix_server.conf.6.bak
@@ -0,0 +1,991 @@
+# This is a configuration file for Zabbix server daemon
+# To get more information about Zabbix, visit http://www.zabbix.com
+
+############ GENERAL PARAMETERS #################
+
+### Option: ListenPort
+# Listen port for trapper.
+#
+# Mandatory: no
+# Range: 1024-32767
+# Default:
+# ListenPort=10051
+
+### Option: SourceIP
+# Source IP address for outgoing connections.
+#
+# Mandatory: no
+# Default:
+# SourceIP=
+
+### Option: LogType
+# Specifies where log messages are written to:
+# system - syslog
+# file - file specified with LogFile parameter
+# console - standard output
+#
+# Mandatory: no
+# Default:
+# LogType=file
+
+### Option: LogFile
+# Log file name for LogType 'file' parameter.
+#
+# Mandatory: yes, if LogType is set to file, otherwise no
+# Default:
+# LogFile=
+
+LogFile=/var/log/zabbixsrv/zabbix_server.log
+
+### Option: LogFileSize
+# Maximum size of log file in MB.
+# 0 - disable automatic log rotation.
+#
+# Mandatory: no
+# Range: 0-1024
+# Default:
+# LogFileSize=1
+LogFileSize=0
+
+### Option: DebugLevel
+# Specifies debug level:
+# 0 - basic information about starting and stopping of Zabbix processes
+# 1 - critical information
+# 2 - error information
+# 3 - warnings
+# 4 - for debugging (produces lots of information)
+# 5 - extended debugging (produces even more information)
+#
+# Mandatory: no
+# Range: 0-5
+# Default:
+# DebugLevel=3
+
+### Option: PidFile
+# Name of PID file.
+#
+# Mandatory: no
+# Default:
+# PidFile=/tmp/zabbix_server.pid
+PidFile=/run/zabbixsrv/zabbix_server.pid
+
+### Option: SocketDir
+# IPC socket directory.
+# Directory to store IPC sockets used by internal Zabbix services.
+#
+# Mandatory: no
+# Default:
+# SocketDir=/tmp
+
+### Option: DBHost
+# Database host name.
+# If set to localhost, socket is used for MySQL.
+# If set to empty string, socket is used for PostgreSQL.
+# If set to empty string, the Net Service Name connection method is used to connect to Oracle database; also see
+# the TNS_ADMIN environment variable to specify the directory where the tnsnames.ora file is located.
+#
+# Mandatory: no
+# Default:
+# DBHost=localhost
+
+### Option: DBName
+# Database name.
+# If the Net Service Name connection method is used to connect to Oracle database, specify the service name from
+# the tnsnames.ora file or set to empty string; also see the TWO_TASK environment variable if DBName is set to
+# empty string.
+#
+# Mandatory: yes
+# Default:
+# DBName=
+
+DBName=zabbix
+
+### Option: DBSchema
+# Schema name. Used for PostgreSQL.
+#
+# Mandatory: no
+# Default:
+# DBSchema=
+
+### Option: DBUser
+# Database user.
+#
+# Mandatory: no
+# Default:
+# DBUser=
+
+DBUser=zabbix
+
+### Option: DBPassword
+# Database password.
+# Comment this line if no password is used.
+#
+# Mandatory: no
+# Default:
+# DBPassword=
+
+### Option: DBSocket
+# Path to MySQL socket.
+#
+# Mandatory: no
+# Default:
+DBSocket=/var/lib/mysql/mysql.sock
+
+### Option: DBPort
+# Database port when not using local socket.
+# If the Net Service Name connection method is used to connect to Oracle database, the port number from the
+# tnsnames.ora file will be used. The port number set here will be ignored.
+#
+# Mandatory: no
+# Range: 1024-65535
+# Default:
+# DBPort=
+
+### Option: AllowUnsupportedDBVersions
+# Allow server to work with unsupported database versions.
+# 0 - do not allow
+# 1 - allow
+#
+# Mandatory: no
+# Default:
+# AllowUnsupportedDBVersions=0
+
+### Option: HistoryStorageURL
+# History storage HTTP[S] URL.
+#
+# Mandatory: no
+# Default:
+# HistoryStorageURL=
+
+### Option: HistoryStorageTypes
+# Comma separated list of value types to be sent to the history storage.
+#
+# Mandatory: no
+# Default:
+# HistoryStorageTypes=uint,dbl,str,log,text
+
+### Option: HistoryStorageDateIndex
+# Enable preprocessing of history values in history storage to store values in different indices based on date.
+# 0 - disable
+# 1 - enable
+#
+# Mandatory: no
+# Default:
+# HistoryStorageDateIndex=0
+
+### Option: ExportDir
+# Directory for real time export of events, history and trends in newline delimited JSON format.
+# If set, enables real time export.
+#
+# Mandatory: no
+# Default:
+# ExportDir=
+
+### Option: ExportFileSize
+# Maximum size per export file in bytes.
+# Only used for rotation if ExportDir is set.
+#
+# Mandatory: no
+# Range: 1M-1G
+# Default:
+# ExportFileSize=1G
+
+### Option: ExportType
+# List of comma delimited types of real time export - allows to control export entities by their
+# type (events, history, trends) individually.
+# Valid only if ExportDir is set.
+#
+# Mandatory: no
+# Default:
+# ExportType=events,history,trends
+
+############ ADVANCED PARAMETERS ################
+
+### Option: StartPollers
+# Number of pre-forked instances of pollers.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartPollers=5
+
+### Option: StartIPMIPollers
+# Number of pre-forked instances of IPMI pollers.
+# The IPMI manager process is automatically started when at least one IPMI poller is started.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartIPMIPollers=0
+
+### Option: StartPreprocessors
+# Number of pre-forked instances of preprocessing workers.
+# The preprocessing manager process is automatically started when preprocessor worker is started.
+#
+# Mandatory: no
+# Range: 1-1000
+# Default:
+# StartPreprocessors=3
+
+### Option: StartPollersUnreachable
+# Number of pre-forked instances of pollers for unreachable hosts (including IPMI and Java).
+# At least one poller for unreachable hosts must be running if regular, IPMI or Java pollers
+# are started.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartPollersUnreachable=1
+
+### Option: StartHistoryPollers
+# Number of pre-forked instances of history pollers.
+# Only required for calculated and internal checks.
+# A database connection is required for each history poller instance.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartHistoryPollers=5
+
+### Option: StartTrappers
+# Number of pre-forked instances of trappers.
+# Trappers accept incoming connections from Zabbix sender, active agents and active proxies.
+# At least one trapper process must be running to display server availability and view queue
+# in the frontend.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartTrappers=5
+
+### Option: StartPingers
+# Number of pre-forked instances of ICMP pingers.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartPingers=1
+
+### Option: StartDiscoverers
+# Number of pre-forked instances of discoverers.
+#
+# Mandatory: no
+# Range: 0-250
+# Default:
+# StartDiscoverers=1
+
+### Option: StartHTTPPollers
+# Number of pre-forked instances of HTTP pollers.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartHTTPPollers=1
+
+### Option: StartTimers
+# Number of pre-forked instances of timers.
+# Timers process maintenance periods.
+# Only the first timer process handles host maintenance updates. Problem suppression updates are shared
+# between all timers.
+#
+# Mandatory: no
+# Range: 1-1000
+# Default:
+# StartTimers=1
+
+### Option: StartEscalators
+# Number of pre-forked instances of escalators.
+#
+# Mandatory: no
+# Range: 1-100
+# Default:
+# StartEscalators=1
+
+### Option: StartAlerters
+# Number of pre-forked instances of alerters.
+# Alerters send the notifications created by action operations.
+#
+# Mandatory: no
+# Range: 1-100
+# Default:
+# StartAlerters=3
+
+### Option: JavaGateway
+# IP address (or hostname) of Zabbix Java gateway.
+# Only required if Java pollers are started.
+#
+# Mandatory: no
+# Default:
+# JavaGateway=
+
+### Option: JavaGatewayPort
+# Port that Zabbix Java gateway listens on.
+#
+# Mandatory: no
+# Range: 1024-32767
+# Default:
+# JavaGatewayPort=10052
+
+### Option: StartJavaPollers
+# Number of pre-forked instances of Java pollers.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartJavaPollers=0
+
+### Option: StartVMwareCollectors
+# Number of pre-forked vmware collector instances.
+#
+# Mandatory: no
+# Range: 0-250
+# Default:
+# StartVMwareCollectors=0
+
+### Option: VMwareFrequency
+# How often Zabbix will connect to VMware service to obtain a new data.
+#
+# Mandatory: no
+# Range: 10-86400
+# Default:
+# VMwareFrequency=60
+
+### Option: VMwarePerfFrequency
+# How often Zabbix will connect to VMware service to obtain performance data.
+#
+# Mandatory: no
+# Range: 10-86400
+# Default:
+# VMwarePerfFrequency=60
+
+### Option: VMwareCacheSize
+# Size of VMware cache, in bytes.
+# Shared memory size for storing VMware data.
+# Only used if VMware collectors are started.
+#
+# Mandatory: no
+# Range: 256K-2G
+# Default:
+# VMwareCacheSize=8M
+
+### Option: VMwareTimeout
+# Specifies how many seconds vmware collector waits for response from VMware service.
+#
+# Mandatory: no
+# Range: 1-300
+# Default:
+# VMwareTimeout=10
+
+### Option: SNMPTrapperFile
+# Temporary file used for passing data from SNMP trap daemon to the server.
+# Must be the same as in zabbix_trap_receiver.pl or SNMPTT configuration file.
+#
+# Mandatory: no
+# Default:
+# SNMPTrapperFile=/tmp/zabbix_traps.tmp
+
+### Option: StartSNMPTrapper
+# If 1, SNMP trapper process is started.
+#
+# Mandatory: no
+# Range: 0-1
+# Default:
+# StartSNMPTrapper=0
+
+### Option: ListenIP
+# List of comma delimited IP addresses that the trapper should listen on.
+# Trapper will listen on all network interfaces if this parameter is missing.
+#
+# Mandatory: no
+# Default:
+# ListenIP=0.0.0.0
+
+### Option: HousekeepingFrequency
+# How often Zabbix will perform housekeeping procedure (in hours).
+# Housekeeping is removing outdated information from the database.
+# To prevent Housekeeper from being overloaded, no more than 4 times HousekeepingFrequency
+# hours of outdated information are deleted in one housekeeping cycle, for each item.
+# To lower load on server startup housekeeping is postponed for 30 minutes after server start.
+# With HousekeepingFrequency=0 the housekeeper can be only executed using the runtime control option.
+# In this case the period of outdated information deleted in one housekeeping cycle is 4 times the
+# period since the last housekeeping cycle, but not less than 4 hours and not greater than 4 days.
+#
+# Mandatory: no
+# Range: 0-24
+# Default:
+# HousekeepingFrequency=1
+
+### Option: MaxHousekeeperDelete
+# The table "housekeeper" contains "tasks" for housekeeping procedure in the format:
+# [housekeeperid], [tablename], [field], [value].
+# No more than 'MaxHousekeeperDelete' rows (corresponding to [tablename], [field], [value])
+# will be deleted per one task in one housekeeping cycle.
+# If set to 0 then no limit is used at all. In this case you must know what you are doing!
+#
+# Mandatory: no
+# Range: 0-1000000
+# Default:
+# MaxHousekeeperDelete=5000
+
+### Option: CacheSize
+# Size of configuration cache, in bytes.
+# Shared memory size for storing host, item and trigger data.
+#
+# Mandatory: no
+# Range: 128K-64G
+# Default:
+# CacheSize=32M
+
+### Option: CacheUpdateFrequency
+# How often Zabbix will perform update of configuration cache, in seconds.
+#
+# Mandatory: no
+# Range: 1-3600
+# Default:
+# CacheUpdateFrequency=60
+
+### Option: StartDBSyncers
+# Number of pre-forked instances of DB Syncers.
+#
+# Mandatory: no
+# Range: 1-100
+# Default:
+# StartDBSyncers=4
+
+### Option: HistoryCacheSize
+# Size of history cache, in bytes.
+# Shared memory size for storing history data.
+#
+# Mandatory: no
+# Range: 128K-2G
+# Default:
+# HistoryCacheSize=16M
+
+### Option: HistoryIndexCacheSize
+# Size of history index cache, in bytes.
+# Shared memory size for indexing history cache.
+#
+# Mandatory: no
+# Range: 128K-2G
+# Default:
+# HistoryIndexCacheSize=4M
+
+### Option: TrendCacheSize
+# Size of trend write cache, in bytes.
+# Shared memory size for storing trends data.
+#
+# Mandatory: no
+# Range: 128K-2G
+# Default:
+# TrendCacheSize=4M
+
+### Option: TrendFunctionCacheSize
+# Size of trend function cache, in bytes.
+# Shared memory size for caching calculated trend function data.
+#
+# Mandatory: no
+# Range: 128K-2G
+# Default:
+# TrendFunctionCacheSize=4M
+
+### Option: ValueCacheSize
+# Size of history value cache, in bytes.
+# Shared memory size for caching item history data requests.
+# Setting to 0 disables value cache.
+#
+# Mandatory: no
+# Range: 0,128K-64G
+# Default:
+# ValueCacheSize=8M
+
+### Option: Timeout
+# Specifies how long we wait for agent, SNMP device or external check (in seconds).
+#
+# Mandatory: no
+# Range: 1-30
+# Default:
+# Timeout=3
+
+Timeout=4
+
+### Option: TrapperTimeout
+# Specifies how many seconds trapper may spend processing new data.
+#
+# Mandatory: no
+# Range: 1-300
+# Default:
+# TrapperTimeout=300
+
+### Option: UnreachablePeriod
+# After how many seconds of unreachability treat a host as unavailable.
+#
+# Mandatory: no
+# Range: 1-3600
+# Default:
+# UnreachablePeriod=45
+
+### Option: UnavailableDelay
+# How often host is checked for availability during the unavailability period, in seconds.
+#
+# Mandatory: no
+# Range: 1-3600
+# Default:
+# UnavailableDelay=60
+
+### Option: UnreachableDelay
+# How often host is checked for availability during the unreachability period, in seconds.
+#
+# Mandatory: no
+# Range: 1-3600
+# Default:
+# UnreachableDelay=15
+
+### Option: AlertScriptsPath
+# Full path to location of custom alert scripts.
+# Default depends on compilation options.
+# To see the default path run command "zabbix_server --help".
+#
+# Mandatory: no
+# Default:
+# AlertScriptsPath=/usr/share/zabbix/alertscripts
+AlertScriptsPath=/var/lib/zabbixsrv/alertscripts
+
+### Option: ExternalScripts
+# Full path to location of external scripts.
+# Default depends on compilation options.
+# To see the default path run command "zabbix_server --help".
+#
+# Mandatory: no
+# Default:
+# ExternalScripts=/usr/share/zabbix/externalscripts
+ExternalScripts=/var/lib/zabbixsrv/externalscripts
+
+### Option: FpingLocation
+# Location of fping.
+# Make sure that fping binary has root ownership and SUID flag set.
+#
+# Mandatory: no
+# Default:
+# FpingLocation=/usr/sbin/fping
+
+### Option: Fping6Location
+# Location of fping6.
+# Make sure that fping6 binary has root ownership and SUID flag set.
+# Make empty if your fping utility is capable to process IPv6 addresses.
+#
+# Mandatory: no
+# Default:
+# Fping6Location=/usr/sbin/fping6
+
+### Option: SSHKeyLocation
+# Location of public and private keys for SSH checks and actions.
+#
+# Mandatory: no
+# Default:
+# SSHKeyLocation=
+
+### Option: LogSlowQueries
+# How long a database query may take before being logged (in milliseconds).
+# Only works if DebugLevel set to 3, 4 or 5.
+# 0 - don't log slow queries.
+#
+# Mandatory: no
+# Range: 1-3600000
+# Default:
+# LogSlowQueries=0
+
+LogSlowQueries=3000
+
+### Option: TmpDir
+# Temporary directory.
+#
+# Mandatory: no
+# Default:
+# TmpDir=/tmp
+TmpDir=/var/lib/zabbixsrv/tmp
+
+### Option: StartProxyPollers
+# Number of pre-forked instances of pollers for passive proxies.
+#
+# Mandatory: no
+# Range: 0-250
+# Default:
+# StartProxyPollers=1
+
+### Option: ProxyConfigFrequency
+# How often Zabbix Server sends configuration data to a Zabbix Proxy in seconds.
+# This parameter is used only for proxies in the passive mode.
+#
+# Mandatory: no
+# Range: 1-3600*24*7
+# Default:
+# ProxyConfigFrequency=3600
+
+### Option: ProxyDataFrequency
+# How often Zabbix Server requests history data from a Zabbix Proxy in seconds.
+# This parameter is used only for proxies in the passive mode.
+#
+# Mandatory: no
+# Range: 1-3600
+# Default:
+# ProxyDataFrequency=1
+
+### Option: StartLLDProcessors
+# Number of pre-forked instances of low level discovery processors.
+#
+# Mandatory: no
+# Range: 1-100
+# Default:
+# StartLLDProcessors=2
+
+### Option: AllowRoot
+# Allow the server to run as 'root'. If disabled and the server is started by 'root', the server
+# will try to switch to the user specified by the User configuration option instead.
+# Has no effect if started under a regular user.
+# 0 - do not allow
+# 1 - allow
+#
+# Mandatory: no
+# Default:
+# AllowRoot=0
+
+### Option: User
+# Drop privileges to a specific, existing user on the system.
+# Only has effect if run as 'root' and AllowRoot is disabled.
+#
+# Mandatory: no
+# Default:
+# User=zabbix
+
+### Option: Include
+# You may include individual files or all files in a directory in the configuration file.
+# Installing Zabbix will create include directory in /etc, unless modified during the compile time.
+#
+# Mandatory: no
+# Default:
+# Include=
+
+# Include=/etc/zabbix_server.general.conf
+# Include=/etc/zabbix_server.conf.d/
+# Include=/etc/zabbix_server.conf.d/*.conf
+
+### Option: SSLCertLocation
+# Location of SSL client certificates.
+# This parameter is used only in web monitoring.
+# Default depends on compilation options.
+# To see the default path run command "zabbix_server --help".
+#
+# Mandatory: no
+# Default:
+# SSLCertLocation=/usr/share/zabbix/ssl/certs
+
+### Option: SSLKeyLocation
+# Location of private keys for SSL client certificates.
+# This parameter is used only in web monitoring.
+# Default depends on compilation options.
+# To see the default path run command "zabbix_server --help".
+#
+# Mandatory: no
+# Default:
+# SSLKeyLocation=/usr/share/zabbix/ssl/keys
+
+### Option: SSLCALocation
+# Override the location of certificate authority (CA) files for SSL server certificate verification.
+# If not set, system-wide directory will be used.
+# This parameter is used in web monitoring, SMTP authentication, HTTP agent items and for communication with Vault.
+#
+# Mandatory: no
+# Default:
+# SSLCALocation=
+
+### Option: StatsAllowedIP
+# List of comma delimited IP addresses, optionally in CIDR notation, or DNS names of external Zabbix instances.
+# Stats request will be accepted only from the addresses listed here. If this parameter is not set no stats requests
+# will be accepted.
+# If IPv6 support is enabled then '127.0.0.1', '::127.0.0.1', '::ffff:127.0.0.1' are treated equally
+# and '::/0' will allow any IPv4 or IPv6 address.
+# '0.0.0.0/0' can be used to allow any IPv4 address.
+# Example: StatsAllowedIP=127.0.0.1,192.168.1.0/24,::1,2001:db8::/32,zabbix.example.com
+#
+# Mandatory: no
+# Default:
+# StatsAllowedIP=
+StatsAllowedIP=127.0.0.1
+
+####### LOADABLE MODULES #######
+
+### Option: LoadModulePath
+# Full path to location of server modules.
+# Default depends on compilation options.
+# To see the default path run command "zabbix_server --help".
+#
+# Mandatory: no
+# Default:
+# LoadModulePath=${libdir}/modules
+
+### Option: LoadModule
+# Module to load at server startup. Modules are used to extend functionality of the server.
+# Formats:
+# LoadModule=
+# LoadModule=
+# LoadModule=
+# Either the module must be located in directory specified by LoadModulePath or the path must precede the module name.
+# If the preceding path is absolute (starts with '/') then LoadModulePath is ignored.
+# It is allowed to include multiple LoadModule parameters.
+#
+# Mandatory: no
+# Default:
+# LoadModule=
+
+####### TLS-RELATED PARAMETERS #######
+
+### Option: TLSCAFile
+# Full pathname of a file containing the top-level CA(s) certificates for
+# peer certificate verification.
+#
+# Mandatory: no
+# Default:
+# TLSCAFile=
+
+### Option: TLSCRLFile
+# Full pathname of a file containing revoked certificates.
+#
+# Mandatory: no
+# Default:
+# TLSCRLFile=
+
+### Option: TLSCertFile
+# Full pathname of a file containing the server certificate or certificate chain.
+#
+# Mandatory: no
+# Default:
+# TLSCertFile=
+
+### Option: TLSKeyFile
+# Full pathname of a file containing the server private key.
+#
+# Mandatory: no
+# Default:
+# TLSKeyFile=
+
+####### For advanced users - TLS ciphersuite selection criteria #######
+
+### Option: TLSCipherCert13
+# Cipher string for OpenSSL 1.1.1 or newer in TLS 1.3.
+# Override the default ciphersuite selection criteria for certificate-based encryption.
+#
+# Mandatory: no
+# Default:
+# TLSCipherCert13=
+
+### Option: TLSCipherCert
+# GnuTLS priority string or OpenSSL (TLS 1.2) cipher string.
+# Override the default ciphersuite selection criteria for certificate-based encryption.
+# Example for GnuTLS:
+# NONE:+VERS-TLS1.2:+ECDHE-RSA:+RSA:+AES-128-GCM:+AES-128-CBC:+AEAD:+SHA256:+SHA1:+CURVE-ALL:+COMP-NULL:+SIGN-ALL:+CTYPE-X.509
+# Example for OpenSSL:
+# EECDH+aRSA+AES128:RSA+aRSA+AES128
+#
+# Mandatory: no
+# Default:
+# TLSCipherCert=
+
+### Option: TLSCipherPSK13
+# Cipher string for OpenSSL 1.1.1 or newer in TLS 1.3.
+# Override the default ciphersuite selection criteria for PSK-based encryption.
+# Example:
+# TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
+#
+# Mandatory: no
+# Default:
+# TLSCipherPSK13=
+
+### Option: TLSCipherPSK
+# GnuTLS priority string or OpenSSL (TLS 1.2) cipher string.
+# Override the default ciphersuite selection criteria for PSK-based encryption.
+# Example for GnuTLS:
+# NONE:+VERS-TLS1.2:+ECDHE-PSK:+PSK:+AES-128-GCM:+AES-128-CBC:+AEAD:+SHA256:+SHA1:+CURVE-ALL:+COMP-NULL:+SIGN-ALL
+# Example for OpenSSL:
+# kECDHEPSK+AES128:kPSK+AES128
+#
+# Mandatory: no
+# Default:
+# TLSCipherPSK=
+
+### Option: TLSCipherAll13
+# Cipher string for OpenSSL 1.1.1 or newer in TLS 1.3.
+# Override the default ciphersuite selection criteria for certificate- and PSK-based encryption.
+# Example:
+# TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
+#
+# Mandatory: no
+# Default:
+# TLSCipherAll13=
+
+### Option: TLSCipherAll
+# GnuTLS priority string or OpenSSL (TLS 1.2) cipher string.
+# Override the default ciphersuite selection criteria for certificate- and PSK-based encryption.
+# Example for GnuTLS:
+# NONE:+VERS-TLS1.2:+ECDHE-RSA:+RSA:+ECDHE-PSK:+PSK:+AES-128-GCM:+AES-128-CBC:+AEAD:+SHA256:+SHA1:+CURVE-ALL:+COMP-NULL:+SIGN-ALL:+CTYPE-X.509
+# Example for OpenSSL:
+# EECDH+aRSA+AES128:RSA+aRSA+AES128:kECDHEPSK+AES128:kPSK+AES128
+#
+# Mandatory: no
+# Default:
+# TLSCipherAll=
+
+### Option: DBTLSConnect
+# Setting this option enforces to use TLS connection to database.
+# required - connect using TLS
+# verify_ca - connect using TLS and verify certificate
+# verify_full - connect using TLS, verify certificate and verify that database identity specified by DBHost
+# matches its certificate
+# On MySQL starting from 5.7.11 and PostgreSQL following values are supported: "required", "verify_ca" and
+# "verify_full".
+# On MariaDB starting from version 10.2.6 "required" and "verify_full" values are supported.
+# Default is not to set any option and behavior depends on database configuration
+#
+# Mandatory: no
+# Default:
+# DBTLSConnect=
+
+### Option: DBTLSCAFile
+# Full pathname of a file containing the top-level CA(s) certificates for database certificate verification.
+# Supported only for MySQL and PostgreSQL
+#
+# Mandatory: no
+# (yes, if DBTLSConnect set to one of: verify_ca, verify_full)
+# Default:
+# DBTLSCAFile=
+
+### Option: DBTLSCertFile
+# Full pathname of file containing Zabbix server certificate for authenticating to database.
+# Supported only for MySQL and PostgreSQL
+#
+# Mandatory: no
+# Default:
+# DBTLSCertFile=
+
+### Option: DBTLSKeyFile
+# Full pathname of file containing the private key for authenticating to database.
+# Supported only for MySQL and PostgreSQL
+#
+# Mandatory: no
+# Default:
+# DBTLSKeyFile=
+
+### Option: DBTLSCipher
+# The list of encryption ciphers that Zabbix server permits for TLS protocols up through TLSv1.2
+# Supported only for MySQL
+#
+# Mandatory no
+# Default:
+# DBTLSCipher=
+
+### Option: DBTLSCipher13
+# The list of encryption ciphersuites that Zabbix server permits for TLSv1.3 protocol
+# Supported only for MySQL, starting from version 8.0.16
+#
+# Mandatory no
+# Default:
+# DBTLSCipher13=
+
+### Option: VaultToken
+# Vault authentication token that should have been generated exclusively for Zabbix server with read only permission
+# to paths specified in Vault macros and read only permission to path specified in optional VaultDBPath
+# configuration parameter.
+# It is an error if VaultToken and VAULT_TOKEN environment variable are defined at the same time.
+#
+# Mandatory: no
+# Default:
+# VaultToken=
+
+### Option: VaultURL
+# Vault server HTTP[S] URL. System-wide CA certificates directory will be used if SSLCALocation is not specified.
+#
+# Mandatory: no
+# Default:
+# VaultURL=https://127.0.0.1:8200
+
+### Option: VaultDBPath
+# Vault path from where credentials for database will be retrieved by keys 'password' and 'username'.
+# Example: secret/zabbix/database
+# This option can only be used if DBUser and DBPassword are not specified.
+#
+# Mandatory: no
+# Default:
+# VaultDBPath=
+
+### Option: StartReportWriters
+# Number of pre-forked report writer instances.
+#
+# Mandatory: no
+# Range: 0-100
+# Default:
+# StartReportWriters=0
+
+### Option: WebServiceURL
+# URL to Zabbix web service, used to perform web related tasks.
+# Example: http://localhost:10053/report
+#
+# Mandatory: no
+# Default:
+# WebServiceURL=
+
+### Option: ServiceManagerSyncFrequency
+# How often Zabbix will synchronize configuration of a service manager (in seconds).
+#
+# Mandatory: no
+# Range: 1-3600
+# Default:
+# ServiceManagerSyncFrequency=60
+
+### Option: ProblemHousekeepingFrequency
+# How often Zabbix will delete problems for deleted triggers (in seconds).
+#
+# Mandatory: no
+# Range: 1-3600
+# Default:
+# ProblemHousekeepingFrequency=60
+
+## Option: StartODBCPollers
+# Number of pre-forked ODBC poller instances.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartODBCPollers=1
+
+####### For advanced users - TCP-related fine-tuning parameters #######
+
+## Option: ListenBacklog
+# The maximum number of pending connections in the queue. This parameter is passed to
+# listen() function as argument 'backlog' (see "man listen").
+#
+# Mandatory: no
+# Range: 0 - INT_MAX (depends on system, too large values may be silently truncated to implementation-specified maximum)
+# Default: SOMAXCONN (hard-coded constant, depends on system)
+# ListenBacklog=
+
+
+####### High availability cluster parameters #######
+
+## Option: HANodeName
+# The high availability cluster node name.
+# When empty, server is working in standalone mode; a node with empty name is registered with address for the frontend to connect to.
+#
+# Mandatory: no
+# Default:
+# HANodeName=
+
+## Option: NodeAddress
+# IP or hostname with optional port to specify how frontend should connect to the server.
+# Format: [:]
+#
+# If IP or hostname is not set, then ListenIP value will be used. In case ListenIP is not set, localhost will be used.
+# If port is not set, then ListenPort value will be used. In case ListenPort is not set, 10051 will be used.
+# This option can be overridden by address specified in frontend configuration.
+#
+# Mandatory: no
+# Default:
+# NodeAddress=localhost:10051
diff --git a/plugins/zabbix/conf/zabbix_server.conf.bak b/plugins/zabbix/conf/zabbix_server.conf.bak
new file mode 100644
index 000000000..b33d1926c
--- /dev/null
+++ b/plugins/zabbix/conf/zabbix_server.conf.bak
@@ -0,0 +1,1130 @@
+# This is a configuration file for Zabbix server daemon
+# To get more information about Zabbix, visit https://www.zabbix.com
+
+############ GENERAL PARAMETERS #################
+
+### Option: ListenPort
+# Listen port for trapper.
+#
+# Mandatory: no
+# Range: 1024-32767
+# Default:
+# ListenPort=10051
+
+### Option: SourceIP
+# Source IP address for outgoing connections.
+#
+# Mandatory: no
+# Default:
+# SourceIP=
+
+### Option: LogType
+# Specifies where log messages are written to:
+# system - syslog
+# file - file specified with LogFile parameter
+# console - standard output
+#
+# Mandatory: no
+# Default:
+# LogType=file
+
+### Option: LogFile
+# Log file name for LogType 'file' parameter.
+#
+# Mandatory: yes, if LogType is set to file, otherwise no
+# Default:
+# LogFile=
+
+LogFile=/var/log/zabbix/zabbix_server.log
+
+### Option: LogFileSize
+# Maximum size of log file in MB.
+# 0 - disable automatic log rotation.
+#
+# Mandatory: no
+# Range: 0-1024
+# Default:
+# LogFileSize=1
+
+LogFileSize=0
+
+### Option: DebugLevel
+# Specifies debug level:
+# 0 - basic information about starting and stopping of Zabbix processes
+# 1 - critical information
+# 2 - error information
+# 3 - warnings
+# 4 - for debugging (produces lots of information)
+# 5 - extended debugging (produces even more information)
+#
+# Mandatory: no
+# Range: 0-5
+# Default:
+# DebugLevel=3
+
+### Option: PidFile
+# Name of PID file.
+#
+# Mandatory: no
+# Default:
+# PidFile=/tmp/zabbix_server.pid
+
+PidFile=/run/zabbix/zabbix_server.pid
+
+### Option: SocketDir
+# IPC socket directory.
+# Directory to store IPC sockets used by internal Zabbix services.
+#
+# Mandatory: no
+# Default:
+# SocketDir=/tmp
+
+SocketDir=/run/zabbix
+
+### NOTE: Support for Oracle DB is deprecated since Zabbix 7.0 and will be removed in future versions.
+
+### Option: DBHost
+# Database host name.
+# If set to localhost, socket is used for MySQL.
+# If set to empty string, socket is used for PostgreSQL.
+# If set to empty string, the Net Service Name connection method is used to connect to Oracle database; also see
+# the TNS_ADMIN environment variable to specify the directory where the tnsnames.ora file is located.
+#
+# Mandatory: no
+# Default:
+DBHost=127.0.0.1
+
+### Option: DBName
+# Database name.
+# If the Net Service Name connection method is used to connect to Oracle database, specify the service name from
+# the tnsnames.ora file or set to empty string; also see the TWO_TASK environment variable if DBName is set to
+# empty string.
+#
+# Mandatory: yes
+# Default:
+# DBName=
+
+DBName=zabbix
+
+### Option: DBSchema
+# Schema name. Used for PostgreSQL.
+#
+# Mandatory: no
+# Default:
+# DBSchema=
+
+### Option: DBUser
+# Database user.
+#
+# Mandatory: no
+# Default:
+# DBUser=
+
+DBUser=zabbix
+
+### Option: DBPassword
+# Database password.
+# Comment this line if no password is used.
+#
+# Mandatory: no
+# Default:
+DBPassword=XXX
+
+### Option: DBSocket
+# Path to MySQL socket.
+#
+# Mandatory: no
+# Default:
+# DBSocket=
+
+### Option: DBPort
+# Database port when not using local socket.
+# If the Net Service Name connection method is used to connect to Oracle database, the port number from the
+# tnsnames.ora file will be used. The port number set here will be ignored.
+#
+# Mandatory: no
+# Range: 1024-65535
+# Default:
+# DBPort=
+
+### Option: AllowUnsupportedDBVersions
+# Allow server to work with unsupported database versions.
+# 0 - do not allow
+# 1 - allow
+#
+# Mandatory: no
+# Default:
+# AllowUnsupportedDBVersions=0
+
+### Option: HistoryStorageURL
+# History storage HTTP[S] URL.
+#
+# Mandatory: no
+# Default:
+# HistoryStorageURL=
+
+### Option: HistoryStorageTypes
+# Comma separated list of value types to be sent to the history storage.
+#
+# Mandatory: no
+# Default:
+# HistoryStorageTypes=uint,dbl,str,log,text
+
+### Option: HistoryStorageDateIndex
+# Enable preprocessing of history values in history storage to store values in different indices based on date.
+# 0 - disable
+# 1 - enable
+#
+# Mandatory: no
+# Default:
+# HistoryStorageDateIndex=0
+
+### Option: ExportDir
+# Directory for real time export of events, history and trends in newline delimited JSON format.
+# If set, enables real time export.
+#
+# Mandatory: no
+# Default:
+# ExportDir=
+
+### Option: ExportFileSize
+# Maximum size per export file in bytes.
+# Only used for rotation if ExportDir is set.
+#
+# Mandatory: no
+# Range: 1M-1G
+# Default:
+# ExportFileSize=1G
+
+### Option: ExportType
+# List of comma delimited types of real time export - allows to control export entities by their
+# type (events, history, trends) individually.
+# Valid only if ExportDir is set.
+#
+# Mandatory: no
+# Default:
+# ExportType=events,history,trends
+
+############ ADVANCED PARAMETERS ################
+
+### Option: StartPollers
+# Number of pre-forked instances of pollers.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartPollers=5
+
+### Option: StartAgentPollers
+# Number of pre-forked instances of asynchronous Zabbix agent pollers. Also see MaxConcurrentChecksPerPoller.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartAgentPollers=1
+
+### Option: StartHTTPAgentPollers
+# Number of pre-forked instances of asynchronous HTTP agent pollers. Also see MaxConcurrentChecksPerPoller.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartHTTPAgentPollers=1
+
+### Option: StartSNMPPollers
+# Number of pre-forked instances of asynchronous SNMP pollers. Also see MaxConcurrentChecksPerPoller.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartSNMPPollers=1
+
+### Option: MaxConcurrentChecksPerPoller
+# Maximum number of asynchronous checks that can be executed at once by each HTTP agent poller or agent poller.
+#
+# Mandatory: no
+# Range: 1-1000
+# Default:
+# MaxConcurrentChecksPerPoller=1000
+
+### Option: StartIPMIPollers
+# Number of pre-forked instances of IPMI pollers.
+# The IPMI manager process is automatically started when at least one IPMI poller is started.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartIPMIPollers=0
+
+
+### Option: StartPreprocessors
+# Number of pre-started instances of preprocessing workers.
+#
+# Mandatory: no
+# Range: 1-1000
+# Default:
+# StartPreprocessors=3
+
+### Option: StartConnectors
+# Number of pre-forked instances of connector workers.
+# The connector manager process is automatically started when connector worker is started.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartConnectors=0
+
+### Option: StartPollersUnreachable
+# Number of pre-forked instances of pollers for unreachable hosts (including IPMI and Java).
+# At least one poller for unreachable hosts must be running if regular, IPMI or Java pollers
+# are started.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartPollersUnreachable=1
+
+### Option: StartHistoryPollers
+# Number of pre-forked instances of history pollers.
+# Only required for calculated checks.
+# A database connection is required for each history poller instance.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartHistoryPollers=5
+
+### Option: StartTrappers
+# Number of pre-forked instances of trappers.
+# Trappers accept incoming connections from Zabbix sender, active agents and active proxies.
+# At least one trapper process must be running to display server availability and view queue
+# in the frontend.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartTrappers=5
+
+### Option: StartPingers
+# Number of pre-forked instances of ICMP pingers.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartPingers=1
+
+### Option: StartDiscoverers
+# Number of pre-started instances of discovery workers.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartDiscoverers=5
+
+### Option: StartHTTPPollers
+# Number of pre-forked instances of HTTP pollers.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartHTTPPollers=1
+
+### Option: StartTimers
+# Number of pre-forked instances of timers.
+# Timers process maintenance periods.
+# Only the first timer process handles host maintenance updates. Problem suppression updates are shared
+# between all timers.
+#
+# Mandatory: no
+# Range: 1-1000
+# Default:
+# StartTimers=1
+
+### Option: StartEscalators
+# Number of pre-forked instances of escalators.
+#
+# Mandatory: no
+# Range: 1-100
+# Default:
+# StartEscalators=1
+
+### Option: StartAlerters
+# Number of pre-forked instances of alerters.
+# Alerters send the notifications created by action operations.
+#
+# Mandatory: no
+# Range: 1-100
+# Default:
+# StartAlerters=3
+
+### Option: JavaGateway
+# IP address (or hostname) of Zabbix Java gateway.
+# Only required if Java pollers are started.
+#
+# Mandatory: no
+# Default:
+# JavaGateway=
+
+### Option: JavaGatewayPort
+# Port that Zabbix Java gateway listens on.
+#
+# Mandatory: no
+# Range: 1024-32767
+# Default:
+# JavaGatewayPort=10052
+
+### Option: StartJavaPollers
+# Number of pre-forked instances of Java pollers.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartJavaPollers=0
+
+### Option: StartVMwareCollectors
+# Number of pre-forked vmware collector instances.
+#
+# Mandatory: no
+# Range: 0-250
+# Default:
+# StartVMwareCollectors=0
+
+### Option: VMwareFrequency
+# How often Zabbix will connect to VMware service to obtain a new data.
+#
+# Mandatory: no
+# Range: 10-86400
+# Default:
+# VMwareFrequency=60
+
+### Option: VMwarePerfFrequency
+# How often Zabbix will connect to VMware service to obtain performance data.
+#
+# Mandatory: no
+# Range: 10-86400
+# Default:
+# VMwarePerfFrequency=60
+
+### Option: VMwareCacheSize
+# Size of VMware cache, in bytes.
+# Shared memory size for storing VMware data.
+# Only used if VMware collectors are started.
+#
+# Mandatory: no
+# Range: 256K-2G
+# Default:
+# VMwareCacheSize=8M
+
+### Option: VMwareTimeout
+# Specifies how many seconds vmware collector waits for response from VMware service.
+#
+# Mandatory: no
+# Range: 1-300
+# Default:
+# VMwareTimeout=10
+
+### Option: SNMPTrapperFile
+# Temporary file used for passing data from SNMP trap daemon to the server.
+# Must be the same as in zabbix_trap_receiver.pl or SNMPTT configuration file.
+#
+# Mandatory: no
+# Default:
+# SNMPTrapperFile=/tmp/zabbix_traps.tmp
+
+SNMPTrapperFile=/var/log/snmptrap/snmptrap.log
+
+### Option: StartSNMPTrapper
+# If 1, SNMP trapper process is started.
+#
+# Mandatory: no
+# Range: 0-1
+# Default:
+# StartSNMPTrapper=0
+
+### Option: ListenIP
+# List of comma delimited IP addresses that the trapper should listen on.
+# Trapper will listen on all network interfaces if this parameter is missing.
+#
+# Mandatory: no
+# Default:
+# ListenIP=0.0.0.0
+
+### Option: HousekeepingFrequency
+# How often Zabbix will perform housekeeping procedure (in hours).
+# Housekeeping is removing outdated information from the database.
+# To prevent Housekeeper from being overloaded, no more than 4 times HousekeepingFrequency
+# hours of outdated information are deleted in one housekeeping cycle, for each item.
+# To lower load on server startup housekeeping is postponed for 30 minutes after server start.
+# With HousekeepingFrequency=0 the housekeeper can be only executed using the runtime control option.
+# In this case the period of outdated information deleted in one housekeeping cycle is 4 times the
+# period since the last housekeeping cycle, but not less than 4 hours and not greater than 4 days.
+#
+# Mandatory: no
+# Range: 0-24
+# Default:
+# HousekeepingFrequency=1
+
+### Option: MaxHousekeeperDelete
+# The table "housekeeper" contains "tasks" for housekeeping procedure in the format:
+# [housekeeperid], [tablename], [field], [value].
+# No more than 'MaxHousekeeperDelete' rows (corresponding to [tablename], [field], [value])
+# will be deleted per one task in one housekeeping cycle.
+# If set to 0 then no limit is used at all. In this case you must know what you are doing!
+#
+# Mandatory: no
+# Range: 0-1000000
+# Default:
+# MaxHousekeeperDelete=5000
+
+### Option: CacheSize
+# Size of configuration cache, in bytes.
+# Shared memory size for storing host, item and trigger data.
+#
+# Mandatory: no
+# Range: 128K-64G
+# Default:
+# CacheSize=32M
+
+### Option: CacheUpdateFrequency
+# How often Zabbix will perform update of configuration cache, in seconds.
+#
+# Mandatory: no
+# Range: 1-3600
+# Default:
+# CacheUpdateFrequency=10
+
+### Option: StartDBSyncers
+# Number of pre-forked instances of DB Syncers.
+#
+# Mandatory: no
+# Range: 1-100
+# Default:
+# StartDBSyncers=4
+
+### Option: HistoryCacheSize
+# Size of history cache, in bytes.
+# Shared memory size for storing history data.
+#
+# Mandatory: no
+# Range: 128K-2G
+# Default:
+# HistoryCacheSize=16M
+
+### Option: HistoryIndexCacheSize
+# Size of history index cache, in bytes.
+# Shared memory size for indexing history cache.
+#
+# Mandatory: no
+# Range: 128K-2G
+# Default:
+# HistoryIndexCacheSize=4M
+
+### Option: TrendCacheSize
+# Size of trend write cache, in bytes.
+# Shared memory size for storing trends data.
+#
+# Mandatory: no
+# Range: 128K-2G
+# Default:
+# TrendCacheSize=4M
+
+### Option: TrendFunctionCacheSize
+# Size of trend function cache, in bytes.
+# Shared memory size for caching calculated trend function data.
+#
+# Mandatory: no
+# Range: 128K-2G
+# Default:
+# TrendFunctionCacheSize=4M
+
+### Option: ValueCacheSize
+# Size of history value cache, in bytes.
+# Shared memory size for caching item history data requests.
+# Setting to 0 disables value cache.
+#
+# Mandatory: no
+# Range: 0,128K-64G
+# Default:
+# ValueCacheSize=8M
+
+### Option: Timeout
+# Specifies timeout for communications (in seconds).
+#
+# Mandatory: no
+# Range: 1-30
+# Default:
+# Timeout=3
+
+Timeout=4
+
+### Option: TrapperTimeout
+# Specifies how many seconds trapper may spend processing new data.
+#
+# Mandatory: no
+# Range: 1-300
+# Default:
+# TrapperTimeout=300
+
+### Option: UnreachablePeriod
+# After how many seconds of unreachability treat a host as unavailable.
+#
+# Mandatory: no
+# Range: 1-3600
+# Default:
+# UnreachablePeriod=45
+
+### Option: UnavailableDelay
+# How often host is checked for availability during the unavailability period, in seconds.
+#
+# Mandatory: no
+# Range: 1-3600
+# Default:
+# UnavailableDelay=60
+
+### Option: UnreachableDelay
+# How often host is checked for availability during the unreachability period, in seconds.
+#
+# Mandatory: no
+# Range: 1-3600
+# Default:
+# UnreachableDelay=15
+
+### Option: AlertScriptsPath
+# Full path to location of custom alert scripts.
+# Default depends on compilation options.
+# To see the default path run command "zabbix_server --help".
+#
+# Mandatory: no
+# Default:
+# AlertScriptsPath=/usr/lib/zabbix/alertscripts
+
+### Option: ExternalScripts
+# Full path to location of external scripts.
+# Default depends on compilation options.
+# To see the default path run command "zabbix_server --help".
+#
+# Mandatory: no
+# Default:
+# ExternalScripts=/usr/lib/zabbix/externalscripts
+
+### Option: FpingLocation
+# Location of fping.
+# Make sure that fping binary has root ownership and SUID flag set.
+#
+# Mandatory: no
+# Default:
+# FpingLocation=/usr/sbin/fping
+
+FpingLocation=/usr/bin/fping
+
+### Option: Fping6Location
+# Location of fping6.
+# Make sure that fping6 binary has root ownership and SUID flag set.
+# Make empty if your fping utility is capable to process IPv6 addresses.
+#
+# Mandatory: no
+# Default:
+# Fping6Location=/usr/sbin/fping6
+
+Fping6Location=/usr/bin/fping6
+
+### Option: SSHKeyLocation
+# Location of public and private keys for SSH checks and actions.
+#
+# Mandatory: no
+# Default:
+# SSHKeyLocation=
+
+### Option: LogSlowQueries
+# How long a database query may take before being logged (in milliseconds).
+# Only works if DebugLevel set to 3, 4 or 5.
+# 0 - don't log slow queries.
+#
+# Mandatory: no
+# Range: 1-3600000
+# Default:
+# LogSlowQueries=0
+
+LogSlowQueries=3000
+
+### Option: TmpDir
+# Temporary directory.
+#
+# Mandatory: no
+# Default:
+# TmpDir=/tmp
+
+### Option: StartProxyPollers
+# Number of pre-forked instances of pollers for passive proxies.
+#
+# Mandatory: no
+# Range: 0-250
+# Default:
+# StartProxyPollers=1
+
+### Option: ProxyConfigFrequency
+# How often Zabbix Server sends configuration data to a Zabbix Proxy in seconds.
+# This parameter is used only for proxies in the passive mode.
+#
+# Mandatory: no
+# Range: 1-3600*24*7
+# Default:
+# ProxyConfigFrequency=10
+
+### Option: ProxyDataFrequency
+# How often Zabbix Server requests history data from a Zabbix Proxy in seconds.
+# This parameter is used only for proxies in the passive mode.
+#
+# Mandatory: no
+# Range: 1-3600
+# Default:
+# ProxyDataFrequency=1
+
+### Option: StartLLDProcessors
+# Number of pre-forked instances of low level discovery processors.
+#
+# Mandatory: no
+# Range: 1-100
+# Default:
+# StartLLDProcessors=2
+
+### Option: AllowRoot
+# Allow the server to run as 'root'. If disabled and the server is started by 'root', the server
+# will try to switch to the user specified by the User configuration option instead.
+# Has no effect if started under a regular user.
+# 0 - do not allow
+# 1 - allow
+#
+# Mandatory: no
+# Default:
+# AllowRoot=0
+
+### Option: User
+# Drop privileges to a specific, existing user on the system.
+# Only has effect if run as 'root' and AllowRoot is disabled.
+#
+# Mandatory: no
+# Default:
+# User=zabbix
+
+### Option: Include
+# You may include individual files or all files in a directory in the configuration file.
+# Installing Zabbix will create include directory in /usr/local/etc, unless modified during the compile time.
+#
+# Mandatory: no
+# Default:
+# Include=
+
+# Include=/usr/local/etc/zabbix_server.general.conf
+# Include=/usr/local/etc/zabbix_server.conf.d/
+# Include=/usr/local/etc/zabbix_server.conf.d/*.conf
+
+### Option: SSLCertLocation
+# Location of SSL client certificates.
+# This parameter is used in web monitoring and for communication with Vault.
+# Default depends on compilation options.
+# To see the default path run command "zabbix_server --help".
+#
+# Mandatory: no
+# Default:
+# SSLCertLocation=${datadir}/zabbix/ssl/certs
+
+### Option: SSLKeyLocation
+# Location of private keys for SSL client certificates.
+# This parameter is used in web monitoring and for communication with Vault.
+# Default depends on compilation options.
+# To see the default path run command "zabbix_server --help".
+#
+# Mandatory: no
+# Default:
+# SSLKeyLocation=${datadir}/zabbix/ssl/keys
+
+### Option: SSLCALocation
+# Override the location of certificate authority (CA) files for SSL server certificate verification.
+# If not set, system-wide directory will be used.
+# This parameter is used in web monitoring, SMTP authentication, HTTP agent items and for communication with Vault.
+#
+# Mandatory: no
+# Default:
+# SSLCALocation=
+
+### Option: StatsAllowedIP
+# List of comma delimited IP addresses, optionally in CIDR notation, or DNS names of external Zabbix instances.
+# Stats request will be accepted only from the addresses listed here. If this parameter is not set no stats requests
+# will be accepted.
+# If IPv6 support is enabled then '127.0.0.1', '::127.0.0.1', '::ffff:127.0.0.1' are treated equally
+# and '::/0' will allow any IPv4 or IPv6 address.
+# '0.0.0.0/0' can be used to allow any IPv4 address.
+# Example: StatsAllowedIP=127.0.0.1,192.168.1.0/24,::1,2001:db8::/32,zabbix.example.com
+#
+# Mandatory: no
+# Default:
+# StatsAllowedIP=
+StatsAllowedIP=127.0.0.1
+
+####### LOADABLE MODULES #######
+
+### Option: LoadModulePath
+# Full path to location of server modules.
+# Default depends on compilation options.
+# To see the default path run command "zabbix_server --help".
+#
+# Mandatory: no
+# Default:
+# LoadModulePath=${libdir}/modules
+
+### Option: LoadModule
+# Module to load at server startup. Modules are used to extend functionality of the server.
+# Formats:
+# LoadModule=
+# LoadModule=
+# LoadModule=
+# Either the module must be located in directory specified by LoadModulePath or the path must precede the module name.
+# If the preceding path is absolute (starts with '/') then LoadModulePath is ignored.
+# It is allowed to include multiple LoadModule parameters.
+#
+# Mandatory: no
+# Default:
+# LoadModule=
+
+####### TLS-RELATED PARAMETERS #######
+
+### Option: TLSCAFile
+# Full pathname of a file containing the top-level CA(s) certificates for
+# peer certificate verification.
+#
+# Mandatory: no
+# Default:
+# TLSCAFile=
+
+### Option: TLSCRLFile
+# Full pathname of a file containing revoked certificates.
+#
+# Mandatory: no
+# Default:
+# TLSCRLFile=
+
+### Option: TLSCertFile
+# Full pathname of a file containing the server certificate or certificate chain.
+#
+# Mandatory: no
+# Default:
+# TLSCertFile=
+
+### Option: TLSKeyFile
+# Full pathname of a file containing the server private key.
+#
+# Mandatory: no
+# Default:
+# TLSKeyFile=
+
+####### For advanced users - TLS ciphersuite selection criteria #######
+
+### Option: TLSCipherCert13
+# Cipher string for OpenSSL 1.1.1 or newer in TLS 1.3.
+# Override the default ciphersuite selection criteria for certificate-based encryption.
+#
+# Mandatory: no
+# Default:
+# TLSCipherCert13=
+
+### Option: TLSCipherCert
+# GnuTLS priority string or OpenSSL (TLS 1.2) cipher string.
+# Override the default ciphersuite selection criteria for certificate-based encryption.
+# Example for GnuTLS:
+# NONE:+VERS-TLS1.2:+ECDHE-RSA:+RSA:+AES-128-GCM:+AES-128-CBC:+AEAD:+SHA256:+SHA1:+CURVE-ALL:+COMP-NULL:+SIGN-ALL:+CTYPE-X.509
+# Example for OpenSSL:
+# EECDH+aRSA+AES128:RSA+aRSA+AES128
+#
+# Mandatory: no
+# Default:
+# TLSCipherCert=
+
+### Option: TLSCipherPSK13
+# Cipher string for OpenSSL 1.1.1 or newer in TLS 1.3.
+# Override the default ciphersuite selection criteria for PSK-based encryption.
+# Example:
+# TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
+#
+# Mandatory: no
+# Default:
+# TLSCipherPSK13=
+
+### Option: TLSCipherPSK
+# GnuTLS priority string or OpenSSL (TLS 1.2) cipher string.
+# Override the default ciphersuite selection criteria for PSK-based encryption.
+# Example for GnuTLS:
+# NONE:+VERS-TLS1.2:+ECDHE-PSK:+PSK:+AES-128-GCM:+AES-128-CBC:+AEAD:+SHA256:+SHA1:+CURVE-ALL:+COMP-NULL:+SIGN-ALL
+# Example for OpenSSL:
+# kECDHEPSK+AES128:kPSK+AES128
+#
+# Mandatory: no
+# Default:
+# TLSCipherPSK=
+
+### Option: TLSCipherAll13
+# Cipher string for OpenSSL 1.1.1 or newer in TLS 1.3.
+# Override the default ciphersuite selection criteria for certificate- and PSK-based encryption.
+# Example:
+# TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
+#
+# Mandatory: no
+# Default:
+# TLSCipherAll13=
+
+### Option: TLSCipherAll
+# GnuTLS priority string or OpenSSL (TLS 1.2) cipher string.
+# Override the default ciphersuite selection criteria for certificate- and PSK-based encryption.
+# Example for GnuTLS:
+# NONE:+VERS-TLS1.2:+ECDHE-RSA:+RSA:+ECDHE-PSK:+PSK:+AES-128-GCM:+AES-128-CBC:+AEAD:+SHA256:+SHA1:+CURVE-ALL:+COMP-NULL:+SIGN-ALL:+CTYPE-X.509
+# Example for OpenSSL:
+# EECDH+aRSA+AES128:RSA+aRSA+AES128:kECDHEPSK+AES128:kPSK+AES128
+#
+# Mandatory: no
+# Default:
+# TLSCipherAll=
+
+### Option: DBTLSConnect
+# Setting this option enforces to use TLS connection to database.
+# required - connect using TLS
+# verify_ca - connect using TLS and verify certificate
+# verify_full - connect using TLS, verify certificate and verify that database identity specified by DBHost
+# matches its certificate
+# On MySQL starting from 5.7.11 and PostgreSQL following values are supported: "required", "verify_ca" and
+# "verify_full".
+# On MariaDB starting from version 10.2.6 "required" and "verify_full" values are supported.
+# Default is not to set any option and behavior depends on database configuration
+#
+# Mandatory: no
+# Default:
+# DBTLSConnect=
+
+### Option: DBTLSCAFile
+# Full pathname of a file containing the top-level CA(s) certificates for database certificate verification.
+# Supported only for MySQL and PostgreSQL
+#
+# Mandatory: no
+# (yes, if DBTLSConnect set to one of: verify_ca, verify_full)
+# Default:
+# DBTLSCAFile=
+
+### Option: DBTLSCertFile
+# Full pathname of file containing Zabbix server certificate for authenticating to database.
+# Supported only for MySQL and PostgreSQL
+#
+# Mandatory: no
+# Default:
+# DBTLSCertFile=
+
+### Option: DBTLSKeyFile
+# Full pathname of file containing the private key for authenticating to database.
+# Supported only for MySQL and PostgreSQL
+#
+# Mandatory: no
+# Default:
+# DBTLSKeyFile=
+
+### Option: DBTLSCipher
+# The list of encryption ciphers that Zabbix server permits for TLS protocols up through TLSv1.2
+# Supported only for MySQL
+#
+# Mandatory no
+# Default:
+# DBTLSCipher=
+
+### Option: DBTLSCipher13
+# The list of encryption ciphersuites that Zabbix server permits for TLSv1.3 protocol
+# Supported only for MySQL, starting from version 8.0.16
+#
+# Mandatory no
+# Default:
+# DBTLSCipher13=
+
+### Option: Vault
+# Specifies vault:
+# HashiCorp - HashiCorp KV Secrets Engine - Version 2
+# CyberArk - CyberArk Central Credential Provider
+#
+# Mandatory: no
+# Default:
+# Vault=HashiCorp
+
+### Option: VaultToken
+# Vault authentication token that should have been generated exclusively for Zabbix server with read only permission
+# to paths specified in Vault macros and read only permission to path specified in optional VaultDBPath
+# configuration parameter.
+# It is an error if VaultToken and VAULT_TOKEN environment variable are defined at the same time.
+#
+# Mandatory: no
+# (yes, if Vault is explicitly set to HashiCorp)
+# Default:
+# VaultToken=
+
+### Option: VaultURL
+# Vault server HTTP[S] URL. System-wide CA certificates directory will be used if SSLCALocation is not specified.
+#
+# Mandatory: no
+# Default:
+# VaultURL=https://127.0.0.1:8200
+
+### Option: VaultPrefix
+# Custom prefix for Vault path or query depending on the Vault.
+# Most suitable defaults will be used if not specified.
+# Note that 'data' is automatically appended after mountpoint for HashiCorp if VaultPrefix is not specified.
+# Example prefix for HashiCorp:
+# /v1/secret/data/
+# Example prefix for CyberArk:
+# /AIMWebService/api/Accounts?
+# Mandatory: no
+# Default:
+# VaultPrefix=
+
+### Option: VaultDBPath
+# Vault path or query depending on the Vault from where credentials for database will be retrieved by keys.
+# Keys used for HashiCorp are 'password' and 'username'.
+# Example path:
+# secret/zabbix/database
+# Keys used for CyberArk are 'Content' and 'UserName'.
+# Example query:
+# AppID=zabbix_server&Query=Safe=passwordSafe;Object=zabbix_server_database
+# This option can only be used if DBUser and DBPassword are not specified.
+#
+# Mandatory: no
+# Default:
+# VaultDBPath=
+
+### Option: VaultTLSCertFile
+# Name of the SSL certificate file used for client authentication. The certificate file must be in PEM1 format.
+# If the certificate file contains also the private key, leave the SSL key file field empty. The directory
+# containing this file is specified by configuration parameter SSLCertLocation.
+#
+# Mandatory: no
+# Default:
+# VaultTLSCertFile=
+
+### Option: VaultTLSKeyFile
+# Name of the SSL private key file used for client authentication. The private key file must be in PEM1 format.
+# The directory containing this file is specified by configuration parameter SSLKeyLocation.
+#
+# Mandatory: no
+# Default:
+# VaultTLSKeyFile=
+
+### Option: StartReportWriters
+# Number of pre-forked report writer instances.
+#
+# Mandatory: no
+# Range: 0-100
+# Default:
+# StartReportWriters=0
+
+### Option: WebServiceURL
+# URL to Zabbix web service, used to perform web related tasks.
+# Example: http://localhost:10053/report
+#
+# Mandatory: no
+# Default:
+# WebServiceURL=
+
+### Option: ServiceManagerSyncFrequency
+# How often Zabbix will synchronize configuration of a service manager (in seconds).
+#
+# Mandatory: no
+# Range: 1-3600
+# Default:
+# ServiceManagerSyncFrequency=60
+
+### Option: ProblemHousekeepingFrequency
+# How often Zabbix will delete problems for deleted triggers (in seconds).
+#
+# Mandatory: no
+# Range: 1-3600
+# Default:
+# ProblemHousekeepingFrequency=60
+
+## Option: StartODBCPollers
+# Number of pre-forked ODBC poller instances.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartODBCPollers=1
+
+### Option: EnableGlobalScripts
+# Enable global scripts on Zabbix server.
+# 0 - disable
+# 1 - enable
+#
+# Mandatory: no
+# Default:
+# EnableGlobalScripts=1
+EnableGlobalScripts=0
+
+# Option: AllowSoftwareUpdateCheck
+# Allow Zabbix UI to receive information about software updates from zabbix.com
+# 0 - disable software update checks
+# 1 - enable software update checks
+#
+# Mandatory: no
+# Default:
+# AllowSoftwareUpdateCheck=1
+
+### Option: SMSDevices
+# List of comma delimited modem files allowed to use Zabbix server
+# SMS sending not possible if this parameter is not set
+# Example: SMSDevices=/dev/ttyUSB0,/dev/ttyUSB1
+#
+# Mandatory: no
+# Default:
+# SMSDevices=
+
+####### For advanced users - TCP-related fine-tuning parameters #######
+
+## Option: ListenBacklog
+# The maximum number of pending connections in the queue. This parameter is passed to
+# listen() function as argument 'backlog' (see "man listen").
+#
+# Mandatory: no
+# Range: 0 - INT_MAX (depends on system, too large values may be silently truncated to implementation-specified maximum)
+# Default: SOMAXCONN (hard-coded constant, depends on system)
+# ListenBacklog=
+
+
+####### High availability cluster parameters #######
+
+## Option: HANodeName
+# The high availability cluster node name.
+# When empty, server is working in standalone mode; a node with empty name is registered with address for the frontend to connect to.
+#
+# Mandatory: no
+# Default:
+# HANodeName=
+
+## Option: NodeAddress
+# IP or hostname with optional port to specify how frontend should connect to the server.
+# Format: [:]
+#
+# If IP or hostname is not set, then ListenIP value will be used. In case ListenIP is not set, localhost will be used.
+# If port is not set, then ListenPort value will be used. In case ListenPort is not set, 10051 will be used.
+# This option can be overridden by address specified in frontend configuration.
+#
+# Mandatory: no
+# Default:
+# NodeAddress=localhost:10051
+
+####### Browser monitoring #######
+
+### Option: WebDriverURL
+# WebDriver interface HTTP[S] URL. For example http://localhost:4444 used with Selenium WebDriver standalone server.
+#
+# Mandatory: no
+# Default:
+# WebDriverURL=
+
+### Option: StartBrowserPollers
+# Number of pre-forked instances of browser item pollers.
+#
+# Mandatory: no
+# Range: 0-1000
+# Default:
+# StartBrowserPollers=1
diff --git a/plugins/zabbix/conf/zabbix_server6.conf b/plugins/zabbix/conf/zabbix_server6.conf
new file mode 100644
index 000000000..3c20f6938
--- /dev/null
+++ b/plugins/zabbix/conf/zabbix_server6.conf
@@ -0,0 +1,13 @@
+LogFile=/var/log/zabbixsrv/zabbix_server.log
+LogFileSize=1
+ListenPort=10051
+PidFile=/run/zabbixsrv/zabbix_server.pid
+
+DBHost=127.0.0.1
+DBPort={$ZABBIX_DB_PORT}
+DBName=zabbix
+DBUser=zabbix
+DBPassword={$ZABBIX_DB_PASS}
+AlertScriptsPath=/var/lib/zabbixsrv/alertscripts
+ExternalScripts=/var/lib/zabbixsrv/externalscripts
+TmpDir=/var/lib/zabbixsrv/tmp
diff --git a/plugins/zabbix/data/server6.0.sql.gz b/plugins/zabbix/data/server6.0.sql.gz
new file mode 100644
index 000000000..32513529d
Binary files /dev/null and b/plugins/zabbix/data/server6.0.sql.gz differ
diff --git a/plugins/zabbix/data/server7.0.sql.gz b/plugins/zabbix/data/server7.0.sql.gz
new file mode 100644
index 000000000..d0118365d
Binary files /dev/null and b/plugins/zabbix/data/server7.0.sql.gz differ
diff --git a/plugins/zabbix/ico.png b/plugins/zabbix/ico.png
new file mode 100644
index 000000000..01024eb07
Binary files /dev/null and b/plugins/zabbix/ico.png differ
diff --git a/plugins/zabbix/ico.svg b/plugins/zabbix/ico.svg
new file mode 100644
index 000000000..9425964a3
--- /dev/null
+++ b/plugins/zabbix/ico.svg
@@ -0,0 +1 @@
+
diff --git a/plugins/zabbix/index.html b/plugins/zabbix/index.html
new file mode 100755
index 000000000..3fa1f338c
--- /dev/null
+++ b/plugins/zabbix/index.html
@@ -0,0 +1,34 @@
+
+
+
+
\ No newline at end of file
diff --git a/plugins/zabbix/index.py b/plugins/zabbix/index.py
new file mode 100755
index 000000000..7be515276
--- /dev/null
+++ b/plugins/zabbix/index.py
@@ -0,0 +1,537 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+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 'zabbix'
+
+
+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 getConf():
+ path = getServerDir() + "/web_conf/nginx/vhost/zabbix.conf"
+ return path
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
+ return path
+
+
+def getArgs():
+ args = sys.argv[3:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ if t.strip() == '':
+ tmp = []
+ else:
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ 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 getPidFile():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'pidfile\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+def status():
+ cmd = "ps aux|grep zabbix_server |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'"
+ data = mw.execShell(cmd)
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+def getInstallVerion():
+ version_pl = getServerDir() + "/version.pl"
+ version = mw.readFile(version_pl).strip()
+ return version
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace('{$ZABBIX_ROOT}', '/usr/share/zabbix')
+ content = content.replace('{$ZABBIX_PORT}', '18888')
+
+ psdb = pSqliteDb('databases')
+ db_pass = psdb.where('name = ?', ('zabbix',)).getField('password')
+ content = content.replace('{$ZABBIX_DB_PORT}', getMySQLPort())
+ content = content.replace('{$ZABBIX_DB_PASS}', db_pass)
+ return content
+
+
+def getMySQLConf():
+ choose_mysql = getServerDir()+'/mysql.pl'
+ if os.path.exists(choose_mysql):
+ ver = mw.readFile(choose_mysql)
+ return mw.getServerDir() + '/'+ver+'/etc/my.cnf'
+
+ apt_path = mw.getServerDir() + '/mysql-apt/etc/my.cnf'
+ if os.path.exists(apt_path):
+ mw.writeFile(choose_mysql, 'mysql-apt')
+ return apt_path
+
+ yum_path = mw.getServerDir() + '/mysql-yum/etc/my.cnf'
+ if os.path.exists(yum_path):
+ mw.writeFile(choose_mysql, 'mysql-yum')
+ return yum_path
+
+ path = mw.getServerDir() + '/mysql/etc/my.cnf'
+ if os.path.exists(path):
+ mw.writeFile(choose_mysql, 'mysql')
+ return path
+ return path
+
+
+def getMySQLPort():
+ file = getMySQLConf()
+ content = mw.readFile(file)
+ rep = r'port\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+def getMySQLSocketFile():
+ file = getMySQLConf()
+ content = mw.readFile(file)
+ rep = r'socket\s*=\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+def getMySQLBin():
+ choose_mysql = getServerDir()+'/mysql.pl'
+ ver = mw.readFile(choose_mysql)
+ mysql_dir = mw.getServerDir() + '/'+ver
+
+ if ver == 'mysql-apt':
+ return '/www/server/mysql-apt/bin/usr/bin/mysql'
+ if ver == 'mysql-yum':
+ return '/www/server/mysql-yum/bin/usr/bin/mysql'
+ return '/www/server/mysql/bin/mysql'
+
+def getMySQLBinLink():
+ choose_mysql = getServerDir()+'/mysql.pl'
+ ver = mw.readFile(choose_mysql)
+ mysql_dir = mw.getServerDir() + '/'+ver
+
+ if ver == 'mysql-apt':
+ return '/www/server/mysql-apt/bin/usr/bin/mysql -S /www/server/mysql-apt/mysql.sock'
+ if ver == 'mysql-yum':
+ return '/www/server/mysql-yum/bin/usr/bin/mysql -S /www/server/mysql-yum/mysql.sock'
+ return '/www/server/mysql/bin/mysql -S /www/server/mysql/mysql.sock'
+
+def pSqliteDb(dbname='databases'):
+ choose_mysql = getServerDir()+'/mysql.pl'
+ ver = mw.readFile(choose_mysql)
+
+ mysql_dir = mw.getServerDir() + '/'+ver
+ conn = mw.M(dbname).dbPos(mysql_dir, 'mysql')
+ return conn
+
+
+def pMysqlDb():
+ # pymysql
+ db = mw.getMyORM()
+ db.setDbName('zabbix')
+ db.setPort(getMySQLPort())
+ db.setSocket(getMySQLSocketFile())
+ db.setPwd(pSqliteDb('config').where('id=?', (1,)).getField('mysql_root'))
+ return db
+
+
+def getInstalledPhpConfDir():
+ phpver = ["80","81","82","83","84"]
+ php_type = ['php-apt','php-yum', 'php'];
+
+ for pt in php_type:
+ for ver in phpver:
+ php_install_dir = mw.getServerDir() + '/'+ pt+'/'+ver
+ if os.path.exists(php_install_dir):
+ if pt == 'php-apt':
+ return pt + ver[0:1]+'.'+ver[1:2]
+ if pt == 'php':
+ return pt + '-' + ver
+ if pt == 'php-yum':
+ return pt + '-' + ver
+ return pt + ver
+ return 'php-80'
+
+def isInstalledPhp():
+ phpver = ["80","81","82","83","84","85"]
+ php_type = ['php-apt','php-yum', 'php'];
+
+ for pt in php_type:
+ for ver in phpver:
+ php_install_dir = mw.getServerDir() + '/'+ pt+'/'+ver
+ if os.path.exists(php_install_dir):
+ return True
+ return False
+
+def isInstalledMySQL():
+ mysql_type = ['mysql-apt','mysql-yum', 'mysql'];
+ for mt in mysql_type:
+ mysql_install_dir = mw.getServerDir() + '/'+ mt
+ if os.path.exists(mysql_install_dir):
+ return True
+ return False
+
+
+def zabbixNginxConf():
+ return mw.getServerDir()+'/web_conf/nginx/vhost/zabbix.conf'
+
+def zabbixPhpConf():
+ # ver = getInstallVerion()
+ # if ver == '6.0':
+ # return '/usr/share/zabbix/conf/zabbix.conf.php'
+ return '/etc/zabbix/web/zabbix.conf.php'
+
+def zabbixServerConf():
+ ver = getInstallVerion()
+ if ver == '6.0':
+ return '/etc/zabbix_server.conf'
+ return '/etc/zabbix/zabbix_server.conf'
+
+def zabbixAgentConf():
+ return '/etc/zabbix/zabbix_agentd.conf'
+
+def zabbixImportMySQLDataFile():
+ tgz_file = getPluginDir()+"/data/server6.0.sql.gz"
+ ver = getInstallVerion()
+ if ver == '6.0':
+ return tgz_file
+ return '/usr/share/zabbix-sql-scripts/mysql/server.sql.gz'
+
+def zabbixImportMySQLData():
+ mysql_conf = getMySQLConf()
+ if not os.path.exists(mysql_conf):
+ exit("需要安装MySQL")
+
+ choose_mysql = getServerDir()+'/mysql.pl'
+ ver = mw.readFile(choose_mysql)
+
+ pmdb = pMysqlDb()
+ psdb = pSqliteDb('databases')
+ find_ps_zabbix = psdb.field('id').where('name = ?', ('zabbix',)).select()
+ if len(find_ps_zabbix) < 1:
+ db_pass = mw.getRandomString(16)
+ # 创建数据
+ cmd = 'python3 plugins/'+ver+'/index.py add_db {"name":"zabbix","codeing":"utf8mb4","db_user":"zabbix","password":"'+db_pass+'","dataAccess":"127.0.0.1","ps":"zabbix","address":"127.0.0.1"}'
+ # print(cmd)
+ mw.execShell(cmd)
+ pmdb.query("ALTER DATABASE `zabbix` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin")
+ pmdb.query("grant all privileges on zabbix.* to zabbix@127.0.0.1")
+
+
+ db_pass = psdb.where('name = ?', ('zabbix',)).getField('password')
+ find_zabbix_version = pmdb.query("show tables like 'dbversion'")
+ if len(find_zabbix_version) == 0:
+ # 初始化导入数据
+ pmdb.query("set global log_bin_trust_function_creators=1")
+
+ mysql_bin = getMySQLBinLink()
+
+ tgz_file = zabbixImportMySQLDataFile()
+ # zcat /usr/share/zabbix-sql-scripts/mysql/server.sql.gz | /www/server/mysql/bin/mysql --default-character-set=utf8mb4 -uzabbix -p"LGhb1f7QG6SDL5CX" zabbix
+ import_data_cmd = 'zcat '+tgz_file+' | '+mysql_bin+' --default-character-set=utf8mb4 -uzabbix -p"'+db_pass+'" zabbix'
+ # print(import_data_cmd)
+ mw.execShell(import_data_cmd)
+ # pmdb.query("set global log_bin_trust_function_creators=0")
+
+
+ ver = getInstallVerion()
+ if ver == '6.0':
+ pmdb.query("update dbversion set mandatory=6000000")
+
+ return True
+
+def initOpConf():
+ nginx_src_tpl = getPluginDir()+'/conf/zabbix.nginx.conf'
+ nginx_dst_vhost = zabbixNginxConf()
+
+ phpver = getInstalledPhpConfDir()
+
+ # nginx配置
+ if not os.path.exists(nginx_dst_vhost):
+ content = mw.readFile(nginx_src_tpl)
+ content = contentReplace(content)
+ content = content.replace('{$PHP_VER}',phpver)
+ mw.writeFile(nginx_dst_vhost, content)
+
+def initZsConf():
+ ver = getInstallVerion()
+ zs_src_tpl = getPluginDir()+'/conf/zabbix_server.conf'
+ if ver == '6.0':
+ zs_src_tpl = getPluginDir()+'/conf/zabbix_server6.conf'
+
+ zs_dst_path = zabbixServerConf()
+
+ # zabbix_server配置
+ content = mw.readFile(zs_src_tpl)
+ content = contentReplace(content)
+ mw.writeFile(zs_dst_path, content)
+
+def initPhpConf():
+ php_src_tpl = getPluginDir()+'/conf/zabbix.conf.php'
+ php_dst_path = zabbixPhpConf()
+ # php配置
+ # if not os.path.exists(php_dst_path):
+ content = mw.readFile(php_src_tpl)
+ content = contentReplace(content)
+ mw.writeFile(php_dst_path, content)
+
+def initAgentConf():
+ za_src_tpl = getPluginDir()+'/conf/zabbix_agentd.conf'
+ za_dst_path = zabbixAgentConf()
+
+ # zabbix_agent配置
+ content = mw.readFile(za_src_tpl)
+ content = contentReplace(content)
+ mw.writeFile(za_dst_path, content)
+
+def openPort():
+ try:
+ from utils.firewall import Firewall as MwFirewall
+ MwFirewall.instance().addAcceptPort('18888', 'zabbix-web', 'port')
+ MwFirewall.instance().addAcceptPort('10051', 'zabbix-server', 'port')
+ MwFirewall.instance().addAcceptPort('10050', 'zabbix-agent', 'port')
+ return port
+ except Exception as e:
+ return "Release failed {}".format(e)
+ return True
+
+
+def initDreplace():
+ # 导入MySQL配置
+ zabbixImportMySQLData()
+
+ # 初始化OP配置
+ initOpConf()
+
+
+ init_file = getServerDir() + '/init.pl'
+ if not os.path.exists(init_file):
+ initZsConf()
+ initAgentConf()
+ initPhpConf()
+ openPort()
+ mw.writeFile(init_file, 'ok')
+ return True
+
+
+def zOp(method):
+
+ initDreplace()
+
+ data = mw.execShell('systemctl ' + method + ' zabbix-server')
+ mw.execShell('systemctl ' + method + ' zabbix-agent')
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def start():
+ val = zOp('start')
+ mw.restartWeb()
+ return val
+
+
+def stop():
+ val = zOp('stop')
+
+ # 删除nginx配置
+ nginx_dst_vhost = zabbixNginxConf()
+ if os.path.exists(nginx_dst_vhost):
+ os.remove(nginx_dst_vhost)
+
+ mw.restartWeb()
+
+ return val
+
+def restart():
+ status = zOp('restart')
+ return status
+
+def reload():
+ initZsConf()
+ initAgentConf()
+ initPhpConf()
+ return zOp('reload')
+
+def initdStatus():
+ current_os = mw.getOs()
+ if current_os == 'darwin':
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status zabbix-server | 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"
+
+ data = mw.execShell('systemctl enable zabbix-server')
+ if data[1] != '':
+ return data[1]
+ return 'ok'
+
+
+def initdUinstall():
+ current_os = mw.getOs()
+ if current_os == 'darwin':
+ return "Apple Computer does not support"
+
+ data = mw.execShell('systemctl disable zabbix-server')
+ if data[1] != '':
+ return data[1]
+ return 'ok'
+
+def runLog():
+ zs_conf = zabbixServerConf()
+ content = mw.readFile(zs_conf)
+
+ rep = r'LogFile=\s*(.*)'
+ tmp = re.search(rep, content)
+
+ if tmp.groups():
+ return tmp.groups()[0].strip()
+ return '/var/log/zabbix/zabbix_server.log'
+
+def zabbixAgentLog():
+ za_conf = zabbixAgentConf()
+ content = mw.readFile(za_conf)
+
+ rep = r'LogFile=\s*(.*)'
+ tmp = re.search(rep, content)
+
+ if tmp.groups():
+ return tmp.groups()[0].strip()
+ return '/var/log/zabbix/zabbix_agentd.log'
+
+
+def installPreInspection():
+ cmd = "cat /etc/*-release | grep PRETTY_NAME |awk -F = '{print $2}' | awk -F '\"' '{print $2}'| awk '{print $1}'"
+ sys = mw.execShell(cmd)
+
+
+ cmd = "cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F '\"' '{print $2}'"
+ sys_id = mw.execShell(cmd)
+
+ sysName = sys[0].strip().lower()
+ sysId = sys_id[0].strip().lower()
+
+ # opensuse
+ if not sysName in ['debian','centos','ubuntu','almalinux','rocky']:
+ return '不支持该系统'
+
+ if sysName == 'debian' and not sysId in ['12']:
+ return '不支持,'+sysName+'['+sysId+'],仅支持debian12!'
+
+ openresty_dir = mw.getServerDir() + "/openresty"
+ if not os.path.exists(openresty_dir):
+ return '需要安装Openresty插件'
+
+ is_installed_php = isInstalledPhp()
+ if not is_installed_php:
+ return '需要安装PHP/PHP-APT/PHP-YUM插件,至少8.0!'
+
+
+ is_installed_mysql = isInstalledMySQL()
+ if not is_installed_mysql:
+ return '需要安装MySQL/MySQL-APT/MySQL-YUM插件,至少8.0!'
+
+ return 'ok'
+
+
+def uninstallPreInspection():
+ return 'ok'
+
+
+if __name__ == "__main__":
+ 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 == 'uninstall_pre_inspection':
+ print(uninstallPreInspection())
+ elif func == 'conf':
+ print(zabbixNginxConf())
+ elif func == 'php_conf':
+ print(zabbixPhpConf())
+ elif func == 'zabbix_server_conf':
+ print(zabbixServerConf())
+ elif func == 'zabbix_agent_conf':
+ print(zabbixAgentConf())
+ elif func == 'run_log':
+ print(runLog())
+ elif func == 'zabbix_agent_log':
+ print(zabbixAgentLog())
+ else:
+ print('error')
diff --git a/plugins/zabbix/info.json b/plugins/zabbix/info.json
new file mode 100755
index 000000000..c77a1334b
--- /dev/null
+++ b/plugins/zabbix/info.json
@@ -0,0 +1,19 @@
+{
+ "sort": 7,
+ "ps": "Zabbix是一个成熟、易用的企业级开源监控解决方案,适用于百万级指标的网络监控和应用监控[开发中 ]",
+ "name": "zabbix",
+ "title": "Zabbix",
+ "shell": "install.sh",
+ "versions":["6.0","7.0"],
+ "tip": "soft",
+ "install_pre_inspection":true,
+ "uninstall_pre_inspection":true,
+ "checks": "server/zabbix",
+ "path": "server/zabbix",
+ "display": 1,
+ "author": "midoks",
+ "date": "2022-07-14",
+ "home": "https://www.zabbix.com/",
+ "type": 0,
+ "pid": "5"
+}
diff --git a/plugins/zabbix/install.sh b/plugins/zabbix/install.sh
new file mode 100755
index 000000000..349265683
--- /dev/null
+++ b/plugins/zabbix/install.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# https://www.zabbix.com
+
+# cd /www/server/mdserver-web/plugins/zabbix && /bin/bash install.sh install 7.0
+# cd /www/server/mdserver-web && python3 /www/server/mdserver-web/plugins/zabbix/index.py start
+
+
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+VERSION=$2
+
+sysName=`uname`
+echo "use system: ${sysName}"
+
+OSNAME=`bash ${rootPath}/scripts/getos.sh`
+
+if [ "" == "$OSNAME" ];then
+ OSNAME=`cat ${rootPath}/data/osname.pl`
+fi
+
+if [ "macos" == "$OSNAME" ];then
+ echo "不支持Macox"
+ exit
+fi
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+Install_App()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/source/zabbix
+
+ mkdir -p $serverPath/zabbix
+ echo "${VERSION}" > $serverPath/zabbix/version.pl
+
+ shell_file=${curPath}/versions/${VERSION}/${OSNAME}.sh
+
+ if [ -f $shell_file ];then
+ bash -x $shell_file install
+ else
+ echo '不支持...'
+ exit 1
+ fi
+
+ #初始化
+ cd ${rootPath} && python3 ${rootPath}/plugins/zabbix/index.py start
+ cd ${rootPath} && python3 ${rootPath}/plugins/zabbix/index.py initd_install
+
+
+ if [ -d /etc/zabbix/web ];then
+ chown -R www:www /etc/zabbix/web
+ fi
+ echo 'Zabbix安装完成'
+}
+
+Uninstall_App()
+{
+ shell_file=${curPath}/versions/${VERSION}/${OSNAME}.sh
+ if [ -f $shell_file ];then
+ bash -x $shell_file uninstall
+ fi
+
+ cd ${rootPath} && python3 ${rootPath}/plugins/zabbix/index.py stop
+ cd ${rootPath} && python3 ${rootPath}/plugins/zabbix/index.py initd_uninstall
+
+ rm -rf $serverPath/zabbix
+ rm -rf $serverPath/source/zabbix
+ echo 'Zabbix卸载完成'
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix/js/zabbix.js b/plugins/zabbix/js/zabbix.js
new file mode 100755
index 000000000..da1d95bc2
--- /dev/null
+++ b/plugins/zabbix/js/zabbix.js
@@ -0,0 +1,66 @@
+function zabbixPost(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'zabbix';
+ req_data['func'] = method;
+ req_data['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ //错误展示10S
+ layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function zabbixPostCallbak(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'zabbix';
+ req_data['func'] = method;
+ args['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+function zabbixReadme(){
+ var readme = '';
+ readme += '默认配置OpenResty端口:18888 ';
+ readme += '初始化账户:Admin/zabbix ';
+ readme += 'https://www.zabbix.com/download ';
+ readme += ' ';
+
+ $('.soft-man-con').html(readme);
+}
+
diff --git a/plugins/zabbix/versions/6.0/alma.sh b/plugins/zabbix/versions/6.0/alma.sh
new file mode 100644
index 000000000..d432a18e4
--- /dev/null
+++ b/plugins/zabbix/versions/6.0/alma.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ yum install -y glibc-langpack-zh
+
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release-6.0-5.el${SYS_VERSION_ID:0:1}.noarch.rpm
+
+ rpm -Uvh https://repo.zabbix.com/zabbix/6.0/alma/${SYS_VERSION_ID:0:1}/x86_64/${ZABBIX_NAME}
+
+ cd $serverPath/source/zabbix && rpm -Uvh ${ZABBIX_NAME}
+ dnf install -y zabbix-server-mysql zabbix-web-mysql zabbix-sql-scripts zabbix-selinux-policy
+ dnf install -y zabbix-agent
+}
+
+Uninstall_App()
+{
+ dnf remove -y zabbix-server-mysql zabbix-web-mysql zabbix-sql-scripts zabbix-selinux-policy
+ dnf install -y zabbix-agent
+ rm -rf /etc/zabbix
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix/versions/6.0/centos.sh b/plugins/zabbix/versions/6.0/centos.sh
new file mode 100644
index 000000000..5ddb82e7a
--- /dev/null
+++ b/plugins/zabbix/versions/6.0/centos.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ yum install -y glibc-langpack-zh
+
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release-6.0-5.el${SYS_VERSION_ID}.noarch.rpm
+
+ rpm -Uvh https://repo.zabbix.com/zabbix/6.0/rhel/${SYS_VERSION_ID}/x86_64/${ZABBIX_NAME}
+
+ cd $serverPath/source/zabbix && rpm -Uvh ${ZABBIX_NAME}
+ dnf install -y zabbix-server-mysql zabbix-web-mysql zabbix-sql-scripts zabbix-selinux-policy zabbix-agent
+
+ # dnf module switch-to zabbix-web-1:7.0.4
+}
+
+Uninstall_App()
+{
+ dnf remove -y zabbix-server-mysql zabbix-web-mysql zabbix-sql-scripts zabbix-selinux-policy zabbix-agent
+ rm -rf /etc/zabbix
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix/versions/6.0/debian.sh b/plugins/zabbix/versions/6.0/debian.sh
new file mode 100644
index 000000000..3df4767ee
--- /dev/null
+++ b/plugins/zabbix/versions/6.0/debian.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+debian_suffix=
+if [ "$SYS_ARCH" == "aarch64" ];then
+ debian_suffix="-arm64"
+fi
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release_6.0-5+debian${SYS_VERSION_ID}_all.deb
+ echo "wget -O $serverPath/source/zabbix/${ZABBIX_NAME} https://repo.zabbix.com/zabbix/6.0/debian${debian_suffix}/pool/main/z/zabbix-release/${ZABBIX_NAME}"
+ if [ ! -f $serverPath/source/zabbix/${ZABBIX_NAME} ];then
+ wget -O $serverPath/source/zabbix/${ZABBIX_NAME} https://repo.zabbix.com/zabbix/6.0/debian${debian_suffix}/pool/main/z/zabbix-release/${ZABBIX_NAME}
+ fi
+
+ dpkg --configure -a
+ cd $serverPath/source/zabbix && dpkg -i ${ZABBIX_NAME}
+ apt update -y
+ rm -rf /etc/zabbix/zabbix_server.conf.dpkg-new
+ rm -rf /etc/zabbix/zabbix_server.conf
+ apt install -y zabbix-server-mysql zabbix-frontend-php zabbix-sql-scripts zabbix-agent zabbix-get
+}
+
+Uninstall_App()
+{
+ apt remove -y zabbix-server-mysql zabbix-frontend-php zabbix-sql-scripts zabbix-agent zabbix-get
+ rm -rf /etc/zabbix
+ dpkg --configure -a
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix/versions/6.0/opensuse.sh b/plugins/zabbix/versions/6.0/opensuse.sh
new file mode 100644
index 000000000..3b316159b
--- /dev/null
+++ b/plugins/zabbix/versions/6.0/opensuse.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ yum install -y glibc-langpack-zh
+
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release-6.0-4.sles${SYS_VERSION_ID:0:2}.noarch.rpm
+
+ rpm -Uvh https://repo.zabbix.com/zabbix/6.0/sles/${SYS_VERSION_ID:0:2}/x86_64/${ZABBIX_NAME}
+ echo "rpm -Uvh https://repo.zabbix.com/zabbix/6.0/sles/${SYS_VERSION_ID:0:2}/x86_64/${ZABBIX_NAME}"
+
+ # cd $serverPath/source/zabbix && rpm -Uvh ${ZABBIX_NAME}
+ zypper install -y zabbix-server-mysql zabbix-web-mysql zabbix-sql-scripts zabbix-selinux-policy
+ zypper install -y zabbix-agent
+}
+
+Uninstall_App()
+{
+ zypper remove -y zabbix-server-mysql zabbix-web-mysql zabbix-sql-scripts zabbix-selinux-policy
+ zypper remove -y zabbix-agent
+ rm -rf /etc/zabbix
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix/versions/6.0/rocky.sh b/plugins/zabbix/versions/6.0/rocky.sh
new file mode 100644
index 000000000..b9e8cfbec
--- /dev/null
+++ b/plugins/zabbix/versions/6.0/rocky.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ yum install -y glibc-langpack-zh
+
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release-6.0-5.el${SYS_VERSION_ID:0:1}.noarch.rpm
+
+ rpm -Uvh https://repo.zabbix.com/zabbix/6.0/rocky/${SYS_VERSION_ID:0:1}/x86_64/${ZABBIX_NAME}
+
+ cd $serverPath/source/zabbix && rpm -Uvh ${ZABBIX_NAME}
+ dnf install -y zabbix-server-mysql zabbix-web-mysql zabbix-sql-scripts zabbix-selinux-policy
+ dnf install -y zabbix-agent
+}
+
+Uninstall_App()
+{
+ dnf remove -y zabbix-server-mysql zabbix-web-mysql zabbix-sql-scripts zabbix-selinux-policy
+ dnf install -y zabbix-agent
+ rm -rf /etc/zabbix
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix/versions/6.0/ubuntu.sh b/plugins/zabbix/versions/6.0/ubuntu.sh
new file mode 100644
index 000000000..b564164a0
--- /dev/null
+++ b/plugins/zabbix/versions/6.0/ubuntu.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+export DEBIAN_FRONTEND=noninteractive
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+SYS_ARCH=`arch`
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+ubuntu_suffix=
+if [ "$SYS_ARCH" == "aarch64" ];then
+ ubuntu_suffix="-arm64"
+fi
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release_6.0-5+ubuntu${SYS_VERSION_ID}_all.deb
+ echo "wget -O $serverPath/source/zabbix/${ZABBIX_NAME} https://repo.zabbix.com/zabbix/6.0/ubuntu${ubuntu_suffix}/pool/main/z/zabbix-release/${ZABBIX_NAME}"
+ if [ ! -f $serverPath/source/zabbix/${ZABBIX_NAME} ];then
+ wget -O $serverPath/source/zabbix/${ZABBIX_NAME} https://repo.zabbix.com/zabbix/6.0/ubuntu${ubuntu_suffix}/pool/main/z/zabbix-release/${ZABBIX_NAME}
+ fi
+
+ # apt-get -f install
+ # dpkg --configure -a
+
+ cd $serverPath/source/zabbix && dpkg -i ${ZABBIX_NAME}
+ apt update -y
+
+ apt install -y zabbix-server-mysql zabbix-frontend-php zabbix-sql-scripts zabbix-get
+ apt install -y zabbix-agent
+}
+
+Uninstall_App()
+{
+ apt remove -y zabbix-server-mysql zabbix-frontend-php zabbix-sql-scripts zabbix-get
+ apt remove -y zabbix-agent
+ rm -rf /etc/zabbix
+
+ # dpkg --configure -a
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix/versions/7.0/alma.sh b/plugins/zabbix/versions/7.0/alma.sh
new file mode 100644
index 000000000..d53b56da3
--- /dev/null
+++ b/plugins/zabbix/versions/7.0/alma.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ yum install -y glibc-langpack-zh
+
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release-7.0-4.el${SYS_VERSION_ID:0:1}.noarch.rpm
+
+ rpm -Uvh https://repo.zabbix.com/zabbix/7.0/alma/${SYS_VERSION_ID:0:1}/x86_64/${ZABBIX_NAME}
+
+ # cd $serverPath/source/zabbix && rpm -Uvh ${ZABBIX_NAME}
+ dnf install -y zabbix-server-mysql zabbix-web-mysql zabbix-sql-scripts zabbix-selinux-policy
+ dnf install -y zabbix-agent
+}
+
+Uninstall_App()
+{
+ dnf remove -y zabbix-server-mysql zabbix-web-mysql zabbix-sql-scripts zabbix-selinux-policy
+ dnf install -y zabbix-agent
+ rm -rf /etc/zabbix
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix/versions/7.0/centos.sh b/plugins/zabbix/versions/7.0/centos.sh
new file mode 100644
index 000000000..8bfddec8c
--- /dev/null
+++ b/plugins/zabbix/versions/7.0/centos.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ yum install -y glibc-langpack-zh
+
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release-7.0-4.el${SYS_VERSION_ID}.noarch.rpm
+
+ rpm -Uvh https://repo.zabbix.com/zabbix/7.0/centos/${SYS_VERSION_ID}/x86_64/${ZABBIX_NAME}
+
+ # cd $serverPath/source/zabbix && rpm -Uvh ${ZABBIX_NAME}
+ dnf install -y zabbix-server-mysql zabbix-web-mysql zabbix-sql-scripts zabbix-selinux-policy zabbix-agent
+
+ # dnf module switch-to zabbix-web-1:7.0.4
+}
+
+Uninstall_App()
+{
+ dnf remove -y zabbix-server-mysql zabbix-web-mysql zabbix-sql-scripts zabbix-selinux-policy zabbix-agent
+ rm -rf /etc/zabbix
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix/versions/7.0/debian.sh b/plugins/zabbix/versions/7.0/debian.sh
new file mode 100644
index 000000000..3492f1786
--- /dev/null
+++ b/plugins/zabbix/versions/7.0/debian.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+sysArch=`arch`
+
+debian_suffix=
+if [ "$sysArch" == "aarch64" ];then
+ debian_suffix="-arm64"
+fi
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release_7.0-2+debian${SYS_VERSION_ID}_all.deb
+ echo "wget -O $serverPath/source/zabbix/${ZABBIX_NAME} https://repo.zabbix.com/zabbix/7.0/debian${debian_suffix}/pool/main/z/zabbix-release/${ZABBIX_NAME}"
+ if [ ! -f $serverPath/source/zabbix/${ZABBIX_NAME} ];then
+ wget -O $serverPath/source/zabbix/${ZABBIX_NAME} https://repo.zabbix.com/zabbix/7.0/debian${debian_suffix}/pool/main/z/zabbix-release/${ZABBIX_NAME}
+ fi
+
+ # apt-get -f install
+ # dpkg --configure -a
+
+ cd $serverPath/source/zabbix && dpkg -i ${ZABBIX_NAME}
+ apt update -y
+
+ apt install -y zabbix-server-mysql zabbix-frontend-php zabbix-sql-scripts zabbix-get
+ apt install -y zabbix-agent
+}
+
+Uninstall_App()
+{
+ apt remove -y zabbix-server-mysql zabbix-frontend-php zabbix-sql-scripts zabbix-get
+ apt remove -y zabbix-agent
+ rm -rf /etc/zabbix
+
+ # dpkg --configure -a
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix/versions/7.0/opensuse.sh b/plugins/zabbix/versions/7.0/opensuse.sh
new file mode 100644
index 000000000..6b093949f
--- /dev/null
+++ b/plugins/zabbix/versions/7.0/opensuse.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ yum install -y glibc-langpack-zh
+
+ mkdir -p $serverPath/source/zabbix
+
+
+
+ ZABBIX_NAME=zabbix-release-7.0-2.sles${SYS_VERSION_ID:0:2}.noarch.rpm
+
+ rpm -Uvh https://repo.zabbix.com/zabbix/7.0/sles/${SYS_VERSION_ID:0:2}/x86_64/${ZABBIX_NAME}
+ echo "rpm -Uvh https://repo.zabbix.com/zabbix/7.0/sles/${SYS_VERSION_ID:0:2}/x86_64/${ZABBIX_NAME}"
+
+
+ # debug
+ # symbol lookup error: /usr/sbin/zabbix_server: undefined symbol: usmAES256CiscoPrivProtocol
+ # /usr/sbin/zabbix_server -c /etc/zabbix/zabbix_server.conf
+
+ # zypper update -y net-snmp
+ # zypper update -y net-snmp-utils
+ # zypper install -y net-snmp-devel
+
+ zypper install -y zabbix-server-mysql
+ zypper install -y zabbix-web-mysql
+ zypper install -y zabbix-sql-scripts
+ zypper install -y zabbix-agent
+}
+
+Uninstall_App()
+{
+ zypper remove -y zabbix-server-mysql
+ zypper remove -y zabbix-web-mysql
+ zypper remove -y zabbix-sql-scripts
+ zypper remove -y zabbix-agent
+ rm -rf /etc/zabbix
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix/versions/7.0/rocky.sh b/plugins/zabbix/versions/7.0/rocky.sh
new file mode 100644
index 000000000..7d19a292f
--- /dev/null
+++ b/plugins/zabbix/versions/7.0/rocky.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ yum install -y glibc-langpack-zh
+
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release-7.0-4.el${SYS_VERSION_ID:0:1}.noarch.rpm
+
+ rpm -Uvh https://repo.zabbix.com/zabbix/7.0/rocky/${SYS_VERSION_ID:0:1}/x86_64/${ZABBIX_NAME}
+
+ # cd $serverPath/source/zabbix && rpm -Uvh ${ZABBIX_NAME}
+ dnf install -y zabbix-server-mysql zabbix-web-mysql zabbix-sql-scripts zabbix-selinux-policy
+ dnf install -y zabbix-agent
+}
+
+Uninstall_App()
+{
+ dnf remove -y zabbix-server-mysql zabbix-web-mysql zabbix-sql-scripts zabbix-selinux-policy
+ dnf install -y zabbix-agent
+ rm -rf /etc/zabbix
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix/versions/7.0/ubuntu.sh b/plugins/zabbix/versions/7.0/ubuntu.sh
new file mode 100644
index 000000000..1e34aa830
--- /dev/null
+++ b/plugins/zabbix/versions/7.0/ubuntu.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+export DEBIAN_FRONTEND=noninteractive
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+sysArch=`arch`
+
+ubuntu_suffix=
+if [ "$sysArch" == "aarch64" ];then
+ ubuntu_suffix="-arm64"
+fi
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release_7.0-2+ubuntu${SYS_VERSION_ID}_all.deb
+ echo "wget -O $serverPath/source/zabbix/${ZABBIX_NAME} https://repo.zabbix.com/zabbix/7.0/ubuntu${ubuntu_suffix}/pool/main/z/zabbix-release/${ZABBIX_NAME}"
+ if [ ! -f $serverPath/source/zabbix/${ZABBIX_NAME} ];then
+ wget -O $serverPath/source/zabbix/${ZABBIX_NAME} https://repo.zabbix.com/zabbix/7.0/ubuntu${ubuntu_suffix}/pool/main/z/zabbix-release/${ZABBIX_NAME}
+ fi
+
+ # apt-get -f install
+ # dpkg --configure -a
+
+ cd $serverPath/source/zabbix && dpkg -i ${ZABBIX_NAME}
+ apt update -y
+
+ apt install -y zabbix-server-mysql
+ apt install -y zabbix-frontend-php zabbix-sql-scripts
+ apt install -y zabbix-agent
+}
+
+Uninstall_App()
+{
+ apt remove -y zabbix-server-mysql
+ apt remove -y zabbix-frontend-php zabbix-sql-scripts
+ apt remove -y zabbix-agent
+ rm -rf /etc/zabbix
+
+ # dpkg --configure -a
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix_agent/conf/zabbix_agentd.conf b/plugins/zabbix_agent/conf/zabbix_agentd.conf
new file mode 100644
index 000000000..2dba7375f
--- /dev/null
+++ b/plugins/zabbix_agent/conf/zabbix_agentd.conf
@@ -0,0 +1,18 @@
+PidFile=/run/zabbix/zabbix_agentd.pid
+LogFile=/var/log/zabbix/zabbix_agentd.log
+LogFileSize=1
+
+ListenIP=0.0.0.0
+ListenPort=10050
+#EnableRemoteCommands=1
+Timeout=3
+
+Server=127.0.0.1
+ServerActive=127.0.0.1
+
+Hostname=Zabbix server
+Include=/etc/zabbix/zabbix_agentd.d/*.conf
+
+# Include=/usr/local/etc/zabbix_agentd.userparams.conf
+# Include=/usr/local/etc/zabbix_agentd.conf.d/
+# Include=/usr/local/etc/zabbix_agentd.conf.d/*.conf
diff --git a/plugins/zabbix_agent/conf/zabbix_agentd.conf.bak b/plugins/zabbix_agent/conf/zabbix_agentd.conf.bak
new file mode 100644
index 000000000..b81c79ba6
--- /dev/null
+++ b/plugins/zabbix_agent/conf/zabbix_agentd.conf.bak
@@ -0,0 +1,554 @@
+# This is a configuration file for Zabbix agent daemon (Unix)
+# To get more information about Zabbix, visit https://www.zabbix.com
+
+############ GENERAL PARAMETERS #################
+
+### Option: PidFile
+# Name of PID file.
+#
+# Mandatory: no
+# Default:
+# PidFile=/tmp/zabbix_agentd.pid
+
+PidFile=/run/zabbix/zabbix_agentd.pid
+
+### Option: LogType
+# Specifies where log messages are written to:
+# system - syslog
+# file - file specified with LogFile parameter
+# console - standard output
+#
+# Mandatory: no
+# Default:
+# LogType=file
+
+### Option: LogFile
+# Log file name for LogType 'file' parameter.
+#
+# Mandatory: yes, if LogType is set to file, otherwise no
+# Default:
+# LogFile=
+
+LogFile=/var/log/zabbix/zabbix_agentd.log
+
+### Option: LogFileSize
+# Maximum size of log file in MB.
+# 0 - disable automatic log rotation.
+#
+# Mandatory: no
+# Range: 0-1024
+# Default:
+# LogFileSize=1
+
+LogFileSize=0
+
+### Option: DebugLevel
+# Specifies debug level:
+# 0 - basic information about starting and stopping of Zabbix processes
+# 1 - critical information
+# 2 - error information
+# 3 - warnings
+# 4 - for debugging (produces lots of information)
+# 5 - extended debugging (produces even more information)
+#
+# Mandatory: no
+# Range: 0-5
+# Default:
+# DebugLevel=3
+
+### Option: SourceIP
+# Source IP address for outgoing connections.
+#
+# Mandatory: no
+# Default:
+# SourceIP=
+
+### Option: AllowKey
+# Allow execution of item keys matching pattern.
+# Multiple keys matching rules may be defined in combination with DenyKey.
+# Key pattern is wildcard expression, which support "*" character to match any number of any characters in certain position. It might be used in both key name and key arguments.
+# Parameters are processed one by one according their appearance order.
+# If no AllowKey or DenyKey rules defined, all keys are allowed.
+#
+# Mandatory: no
+
+### Option: DenyKey
+# Deny execution of items keys matching pattern.
+# Multiple keys matching rules may be defined in combination with AllowKey.
+# Key pattern is wildcard expression, which support "*" character to match any number of any characters in certain position. It might be used in both key name and key arguments.
+# Parameters are processed one by one according their appearance order.
+# If no AllowKey or DenyKey rules defined, all keys are allowed.
+# Unless another system.run[*] rule is specified DenyKey=system.run[*] is added by default.
+#
+# Mandatory: no
+# Default:
+# DenyKey=system.run[*]
+
+### Option: EnableRemoteCommands - Deprecated, use AllowKey=system.run[*] or DenyKey=system.run[*] instead
+# Internal alias for AllowKey/DenyKey parameters depending on value:
+# 0 - DenyKey=system.run[*]
+# 1 - AllowKey=system.run[*]
+#
+# Mandatory: no
+
+### Option: LogRemoteCommands
+# Enable logging of executed shell commands as warnings.
+# 0 - disabled
+# 1 - enabled
+#
+# Mandatory: no
+# Default:
+# LogRemoteCommands=0
+
+##### Passive checks related
+
+### Option: Server
+# List of comma delimited IP addresses, optionally in CIDR notation, or DNS names of Zabbix servers and Zabbix proxies.
+# Incoming connections will be accepted only from the hosts listed here.
+# If IPv6 support is enabled then '127.0.0.1', '::127.0.0.1', '::ffff:127.0.0.1' are treated equally
+# and '::/0' will allow any IPv4 or IPv6 address.
+# '0.0.0.0/0' can be used to allow any IPv4 address.
+# Example: Server=127.0.0.1,192.168.1.0/24,::1,2001:db8::/32,zabbix.example.com
+#
+# Mandatory: yes, if StartAgents is not explicitly set to 0
+# Default:
+# Server=
+
+Server=127.0.0.1
+
+### Option: ListenPort
+# Agent will listen on this port for connections from the server.
+#
+# Mandatory: no
+# Range: 1024-32767
+# Default:
+# ListenPort=10050
+
+### Option: ListenIP
+# List of comma delimited IP addresses that the agent should listen on.
+# First IP address is sent to Zabbix server if connecting to it to retrieve list of active checks.
+#
+# Mandatory: no
+# Default:
+# ListenIP=0.0.0.0
+
+### Option: StartAgents
+# Number of pre-forked instances of zabbix_agentd that process passive checks.
+# If set to 0, disables passive checks and the agent will not listen on any TCP port.
+#
+# Mandatory: no
+# Range: 0-100
+# Default:
+# StartAgents=10
+
+##### Active checks related
+
+### Option: ServerActive
+# Zabbix server/proxy address or cluster configuration to get active checks from.
+# Server/proxy address is IP address or DNS name and optional port separated by colon.
+# Cluster configuration is one or more server addresses separated by semicolon.
+# Multiple Zabbix servers/clusters and Zabbix proxies can be specified, separated by comma.
+# More than one Zabbix proxy should not be specified from each Zabbix server/cluster.
+# If Zabbix proxy is specified then Zabbix server/cluster for that proxy should not be specified.
+# Multiple comma-delimited addresses can be provided to use several independent Zabbix servers in parallel. Spaces are allowed.
+# If port is not specified, default port is used.
+# IPv6 addresses must be enclosed in square brackets if port for that host is specified.
+# If port is not specified, square brackets for IPv6 addresses are optional.
+# If this parameter is not specified, active checks are disabled.
+# Example for Zabbix proxy:
+# ServerActive=127.0.0.1:10051
+# Example for multiple servers:
+# ServerActive=127.0.0.1:20051,zabbix.domain,[::1]:30051,::1,[12fc::1]
+# Example for high availability:
+# ServerActive=zabbix.cluster.node1;zabbix.cluster.node2:20051;zabbix.cluster.node3
+# Example for high availability with two clusters and one server:
+# ServerActive=zabbix.cluster.node1;zabbix.cluster.node2:20051,zabbix.cluster2.node1;zabbix.cluster2.node2,zabbix.domain
+#
+# Mandatory: no
+# Default:
+# ServerActive=
+
+ServerActive=127.0.0.1
+
+### Option: Hostname
+# List of comma delimited unique, case sensitive hostnames.
+# Required for active checks and must match hostnames as configured on the server.
+# Value is acquired from HostnameItem if undefined.
+#
+# Mandatory: no
+# Default:
+# Hostname=
+
+Hostname=Zabbix server
+
+### Option: HostnameItem
+# Item used for generating Hostname if it is undefined. Ignored if Hostname is defined.
+# Does not support UserParameters or aliases.
+#
+# Mandatory: no
+# Default:
+# HostnameItem=system.hostname
+
+### Option: HostMetadata
+# Optional parameter that defines host metadata.
+# Host metadata is used at host auto-registration process.
+# An agent will issue an error and not start if the value is over limit of 2034 bytes.
+# If not defined, value will be acquired from HostMetadataItem.
+#
+# Mandatory: no
+# Range: 0-2034 bytes
+# Default:
+# HostMetadata=
+
+### Option: HostMetadataItem
+# Optional parameter that defines an item used for getting host metadata.
+# Host metadata is used at host auto-registration process.
+# During an auto-registration request an agent will log a warning message if
+# the value returned by specified item is over limit of 65535 characters.
+# This option is only used when HostMetadata is not defined.
+#
+# Mandatory: no
+# Default:
+# HostMetadataItem=
+
+### Option: HostInterface
+# Optional parameter that defines host interface.
+# Host interface is used at host auto-registration process.
+# An agent will issue an error and not start if the value is over limit of 255 characters.
+# If not defined, value will be acquired from HostInterfaceItem.
+#
+# Mandatory: no
+# Range: 0-255 characters
+# Default:
+# HostInterface=
+
+### Option: HostInterfaceItem
+# Optional parameter that defines an item used for getting host interface.
+# Host interface is used at host auto-registration process.
+# During an auto-registration request an agent will log a warning message if
+# the value returned by specified item is over limit of 255 characters.
+# This option is only used when HostInterface is not defined.
+#
+# Mandatory: no
+# Default:
+# HostInterfaceItem=
+
+### Option: RefreshActiveChecks
+# How often list of active checks is refreshed, in seconds.
+#
+# Mandatory: no
+# Range: 1-86400
+# Default:
+# RefreshActiveChecks=5
+
+### Option: BufferSend
+# Do not keep data longer than N seconds in buffer.
+#
+# Mandatory: no
+# Range: 1-3600
+# Default:
+# BufferSend=5
+
+### Option: BufferSize
+# Maximum number of values in a memory buffer. The agent will send
+# all collected data to Zabbix Server or Proxy if the buffer is full.
+#
+# Mandatory: no
+# Range: 2-65535
+# Default:
+# BufferSize=100
+
+### Option: MaxLinesPerSecond
+# Maximum number of new lines the agent will send per second to Zabbix Server
+# or Proxy processing 'log' and 'logrt' active checks.
+# The provided value will be overridden by the parameter 'maxlines',
+# provided in 'log' or 'logrt' item keys.
+#
+# Mandatory: no
+# Range: 1-1000
+# Default:
+# MaxLinesPerSecond=20
+
+### Option: HeartbeatFrequency
+# Frequency of heartbeat messages in seconds.
+# Used for monitoring availability of active checks.
+# 0 - heartbeat messages disabled.
+#
+# Mandatory: no
+# Range: 0-3600
+# Default: 60
+# HeartbeatFrequency=
+
+############ ADVANCED PARAMETERS #################
+
+### Option: Alias
+# Sets an alias for an item key. It can be used to substitute long and complex item key with a smaller and simpler one.
+# Multiple Alias parameters may be present. Multiple parameters with the same Alias key are not allowed.
+# Different Alias keys may reference the same item key.
+# For example, to retrieve the ID of user 'zabbix':
+# Alias=zabbix.userid:vfs.file.regexp[/etc/passwd,^zabbix:.:([0-9]+),,,,\1]
+# Now shorthand key zabbix.userid may be used to retrieve data.
+# Aliases can be used in HostMetadataItem but not in HostnameItem parameters.
+#
+# Mandatory: no
+# Range:
+# Default:
+
+### Option: Timeout
+# Specifies timeout for communications (in seconds).
+#
+# Mandatory: no
+# Range: 1-30
+# Default:
+# Timeout=3
+
+### Option: AllowRoot
+# Allow the agent to run as 'root'. If disabled and the agent is started by 'root', the agent
+# will try to switch to the user specified by the User configuration option instead.
+# Has no effect if started under a regular user.
+# 0 - do not allow
+# 1 - allow
+#
+# Mandatory: no
+# Default:
+# AllowRoot=0
+
+### Option: User
+# Drop privileges to a specific, existing user on the system.
+# Only has effect if run as 'root' and AllowRoot is disabled.
+#
+# Mandatory: no
+# Default:
+# User=zabbix
+# NOTE: This option is overriden by settings in systemd service file!
+
+### Option: Include
+# You may include individual files or all files in a directory in the configuration file.
+# Installing Zabbix will create include directory in /usr/local/etc, unless modified during the compile time.
+#
+# Mandatory: no
+# Default:
+# Include=
+
+Include=/etc/zabbix/zabbix_agentd.d/*.conf
+
+# Include=/usr/local/etc/zabbix_agentd.userparams.conf
+# Include=/usr/local/etc/zabbix_agentd.conf.d/
+# Include=/usr/local/etc/zabbix_agentd.conf.d/*.conf
+
+####### USER-DEFINED MONITORED PARAMETERS #######
+
+### Option: UnsafeUserParameters
+# Allow all characters to be passed in arguments to user-defined parameters.
+# The following characters are not allowed:
+# \ ' " ` * ? [ ] { } ~ $ ! & ; ( ) < > | # @
+# Additionally, newline characters are not allowed.
+# 0 - do not allow
+# 1 - allow
+#
+# Mandatory: no
+# Range: 0-1
+# Default:
+# UnsafeUserParameters=0
+
+### Option: UserParameter
+# User-defined parameter to monitor. There can be several user-defined parameters.
+# Format: UserParameter=,
+# See 'zabbix_agentd' directory for examples.
+#
+# Mandatory: no
+# Default:
+# UserParameter=
+
+### Option: UserParameterDir
+# Directory to execute UserParameter commands from. Only one entry is allowed.
+# When executing UserParameter commands the agent will change the working directory to the one
+# specified in the UserParameterDir option.
+# This way UserParameter commands can be specified using the relative ./ prefix.
+#
+# Mandatory: no
+# Default:
+# UserParameterDir=
+
+####### LOADABLE MODULES #######
+
+### Option: LoadModulePath
+# Full path to location of agent modules.
+# Default depends on compilation options.
+# To see the default path run command "zabbix_agentd --help".
+#
+# Mandatory: no
+# Default:
+# LoadModulePath=${libdir}/modules
+
+### Option: LoadModule
+# Module to load at agent startup. Modules are used to extend functionality of the agent.
+# Formats:
+# LoadModule=
+# LoadModule=
+# LoadModule=
+# Either the module must be located in directory specified by LoadModulePath or the path must precede the module name.
+# If the preceding path is absolute (starts with '/') then LoadModulePath is ignored.
+# It is allowed to include multiple LoadModule parameters.
+#
+# Mandatory: no
+# Default:
+# LoadModule=
+
+####### TLS-RELATED PARAMETERS #######
+
+### Option: TLSConnect
+# How the agent should connect to server or proxy. Used for active checks.
+# Only one value can be specified:
+# unencrypted - connect without encryption
+# psk - connect using TLS and a pre-shared key
+# cert - connect using TLS and a certificate
+#
+# Mandatory: yes, if TLS certificate or PSK parameters are defined (even for 'unencrypted' connection)
+# Default:
+# TLSConnect=unencrypted
+
+### Option: TLSAccept
+# What incoming connections to accept.
+# Multiple values can be specified, separated by comma:
+# unencrypted - accept connections without encryption
+# psk - accept connections secured with TLS and a pre-shared key
+# cert - accept connections secured with TLS and a certificate
+#
+# Mandatory: yes, if TLS certificate or PSK parameters are defined (even for 'unencrypted' connection)
+# Default:
+# TLSAccept=unencrypted
+
+### Option: TLSCAFile
+# Full pathname of a file containing the top-level CA(s) certificates for
+# peer certificate verification.
+#
+# Mandatory: no
+# Default:
+# TLSCAFile=
+
+### Option: TLSCRLFile
+# Full pathname of a file containing revoked certificates.
+#
+# Mandatory: no
+# Default:
+# TLSCRLFile=
+
+### Option: TLSServerCertIssuer
+# Allowed server certificate issuer.
+#
+# Mandatory: no
+# Default:
+# TLSServerCertIssuer=
+
+### Option: TLSServerCertSubject
+# Allowed server certificate subject.
+#
+# Mandatory: no
+# Default:
+# TLSServerCertSubject=
+
+### Option: TLSCertFile
+# Full pathname of a file containing the agent certificate or certificate chain.
+#
+# Mandatory: no
+# Default:
+# TLSCertFile=
+
+### Option: TLSKeyFile
+# Full pathname of a file containing the agent private key.
+#
+# Mandatory: no
+# Default:
+# TLSKeyFile=
+
+### Option: TLSPSKIdentity
+# Unique, case sensitive string used to identify the pre-shared key.
+#
+# Mandatory: no
+# Default:
+# TLSPSKIdentity=
+
+### Option: TLSPSKFile
+# Full pathname of a file containing the pre-shared key.
+#
+# Mandatory: no
+# Default:
+# TLSPSKFile=
+
+####### For advanced users - TLS ciphersuite selection criteria #######
+
+### Option: TLSCipherCert13
+# Cipher string for OpenSSL 1.1.1 or newer in TLS 1.3.
+# Override the default ciphersuite selection criteria for certificate-based encryption.
+#
+# Mandatory: no
+# Default:
+# TLSCipherCert13=
+
+### Option: TLSCipherCert
+# GnuTLS priority string or OpenSSL (TLS 1.2) cipher string.
+# Override the default ciphersuite selection criteria for certificate-based encryption.
+# Example for GnuTLS:
+# NONE:+VERS-TLS1.2:+ECDHE-RSA:+RSA:+AES-128-GCM:+AES-128-CBC:+AEAD:+SHA256:+SHA1:+CURVE-ALL:+COMP-NULL:+SIGN-ALL:+CTYPE-X.509
+# Example for OpenSSL:
+# EECDH+aRSA+AES128:RSA+aRSA+AES128
+#
+# Mandatory: no
+# Default:
+# TLSCipherCert=
+
+### Option: TLSCipherPSK13
+# Cipher string for OpenSSL 1.1.1 or newer in TLS 1.3.
+# Override the default ciphersuite selection criteria for PSK-based encryption.
+# Example:
+# TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
+#
+# Mandatory: no
+# Default:
+# TLSCipherPSK13=
+
+### Option: TLSCipherPSK
+# GnuTLS priority string or OpenSSL (TLS 1.2) cipher string.
+# Override the default ciphersuite selection criteria for PSK-based encryption.
+# Example for GnuTLS:
+# NONE:+VERS-TLS1.2:+ECDHE-PSK:+PSK:+AES-128-GCM:+AES-128-CBC:+AEAD:+SHA256:+SHA1:+CURVE-ALL:+COMP-NULL:+SIGN-ALL
+# Example for OpenSSL:
+# kECDHEPSK+AES128:kPSK+AES128
+#
+# Mandatory: no
+# Default:
+# TLSCipherPSK=
+
+### Option: TLSCipherAll13
+# Cipher string for OpenSSL 1.1.1 or newer in TLS 1.3.
+# Override the default ciphersuite selection criteria for certificate- and PSK-based encryption.
+# Example:
+# TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
+#
+# Mandatory: no
+# Default:
+# TLSCipherAll13=
+
+### Option: TLSCipherAll
+# GnuTLS priority string or OpenSSL (TLS 1.2) cipher string.
+# Override the default ciphersuite selection criteria for certificate- and PSK-based encryption.
+# Example for GnuTLS:
+# NONE:+VERS-TLS1.2:+ECDHE-RSA:+RSA:+ECDHE-PSK:+PSK:+AES-128-GCM:+AES-128-CBC:+AEAD:+SHA256:+SHA1:+CURVE-ALL:+COMP-NULL:+SIGN-ALL:+CTYPE-X.509
+# Example for OpenSSL:
+# EECDH+aRSA+AES128:RSA+aRSA+AES128:kECDHEPSK+AES128:kPSK+AES128
+#
+# Mandatory: no
+# Default:
+# TLSCipherAll=
+
+####### For advanced users - TCP-related fine-tuning parameters #######
+
+## Option: ListenBacklog
+# The maximum number of pending connections in the queue. This parameter is passed to
+# listen() function as argument 'backlog' (see "man listen").
+#
+# Mandatory: no
+# Range: 0 - INT_MAX (depends on system, too large values may be silently truncated to implementation-specified maximum)
+# Default: SOMAXCONN (hard-coded constant, depends on system)
+# ListenBacklog=
diff --git a/plugins/zabbix_agent/conf/zabbix_agentd/userparameter_examples.conf b/plugins/zabbix_agent/conf/zabbix_agentd/userparameter_examples.conf
new file mode 100644
index 000000000..d8a09ee64
--- /dev/null
+++ b/plugins/zabbix_agent/conf/zabbix_agentd/userparameter_examples.conf
@@ -0,0 +1,28 @@
+# Emulating built-in agent parameter 'system.users.num'
+#UserParameter=system.test,who | wc -l
+
+# Get size of a directory
+# Defaults to /tmp
+#UserParameter=vfs.dir.size[*],dir="$1"; du -s -B 1 "${dir:-/tmp}" | cut -f1
+
+# Total CPU utilisation by all processes with a given name.
+# Returns empty value if no such processes are present, numeric items will turn unsupported
+# Defaults to zabbix_agentd
+#UserParameter=proc.cpu[*],proc="$1"; ps -o pcpu= -C "${proc:-zabbix_agentd}" | awk '{sum += $$1} END {print sum}'
+
+# Mail queue length from mailq
+#UserParameter=unix_mail.queue,mailq | grep -v "Mail queue is empty" | grep -c '^[0-9A-Z]'
+
+# Partition discovery on Linux
+#UserParameter=vfs.partitions.discovery.linux,for partition in $(awk 'NR > 2 {print $4}' /proc/partitions); do #partitionlist="$partitionlist,"'{"{#PARTITION}":"'$partition'"}'; done; echo '{"data":['${partitionlist#,}']}'
+
+# Partition discovery on Solaris (using iostat output)
+# On Solaris bash usually is not the one linked from /bin/sh, so a wrapper script is suggested
+#UserParameter=vfs.partitions.discovery.solaris,/somewhere/solaris_partitions.sh
+
+# Wrapper script (solaris_partitions.sh) contents:
+##!/bin/bash
+#for partition in $(iostat -x | tail +3 | awk '{print $1}'); do
+# partitionlist="$partitionlist,"'{"{#PARTITION}":"'$partition'"}'
+#done
+#echo '{"data":['${partitionlist#,}']}'
diff --git a/plugins/zabbix_agent/conf/zabbix_agentd/userparameter_mysql.conf b/plugins/zabbix_agent/conf/zabbix_agentd/userparameter_mysql.conf
new file mode 100644
index 000000000..97a4fb1f1
--- /dev/null
+++ b/plugins/zabbix_agent/conf/zabbix_agentd/userparameter_mysql.conf
@@ -0,0 +1,16 @@
+#template_db_mysql.conf created by Zabbix for "Template DB MySQL" and Zabbix 4.2
+#For OS Linux: You need create .my.cnf in zabbix-agent home directory (/var/lib/zabbix by default)
+#For OS Windows: You need add PATH to mysql and mysqladmin and create my.cnf in %WINDIR%\my.cnf,C:\my.cnf,BASEDIR\my.cnf https://dev.mysql.com/doc/refman/5.7/en/option-files.html
+#The file must have three strings:
+#[client]
+#user=zbx_monitor
+#password=
+#
+
+#UserParameter=mysql.ping[*], mysqladmin -h"$1" -P"$2" ping
+#UserParameter=mysql.get_status_variables[*], mysql -h"$1" -P"$2" -sNX -e "show global status"
+#UserParameter=mysql.version[*], mysqladmin -s -h"$1" -P"$2" version
+#UserParameter=mysql.db.discovery[*], mysql -h"$1" -P"$2" -sN -e "show databases"
+#UserParameter=mysql.dbsize[*], mysql -h"$1" -P"$2" -sN -e "SELECT SUM(DATA_LENGTH + INDEX_LENGTH) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='$3'"
+#UserParameter=mysql.replication.discovery[*], mysql -h"$1" -P"$2" -sNX -e "show slave status"
+#UserParameter=mysql.slave_status[*], mysql -h"$1" -P"$2" -sNX -e "show slave status"
diff --git a/plugins/zabbix_agent/ico.png b/plugins/zabbix_agent/ico.png
new file mode 100644
index 000000000..01024eb07
Binary files /dev/null and b/plugins/zabbix_agent/ico.png differ
diff --git a/plugins/zabbix_agent/index.html b/plugins/zabbix_agent/index.html
new file mode 100755
index 000000000..de22c4356
--- /dev/null
+++ b/plugins/zabbix_agent/index.html
@@ -0,0 +1,31 @@
+
+
+
+
\ No newline at end of file
diff --git a/plugins/zabbix_agent/index.py b/plugins/zabbix_agent/index.py
new file mode 100755
index 000000000..d87b1b689
--- /dev/null
+++ b/plugins/zabbix_agent/index.py
@@ -0,0 +1,269 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+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 'zabbix_agent'
+
+
+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()
+ return '/etc/init.d/' + getPluginName()
+
+
+def getInitDTpl():
+ path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
+ return path
+
+
+def getArgs():
+ args = sys.argv[3:]
+ tmp = {}
+ args_len = len(args)
+
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ if t.strip() == '':
+ tmp = []
+ else:
+ t = t.split(':')
+ tmp[t[0]] = t[1]
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+ for i in range(len(args)):
+ t = args[i].split(':')
+ tmp[t[0]] = t[1]
+ 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 getPidFile():
+ file = getConf()
+ content = mw.readFile(file)
+ rep = r'pidfile\s*(.*)'
+ tmp = re.search(rep, content)
+ return tmp.groups()[0].strip()
+
+def status():
+ cmd = "ps aux|grep zabbix_agentd |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'"
+ data = mw.execShell(cmd)
+ if data[0] == '':
+ return 'stop'
+ return 'start'
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ return content
+
+def zabbixAgentConf():
+ return '/etc/zabbix/zabbix_agentd.conf'
+
+def runLog():
+ za_conf = zabbixAgentConf()
+ content = mw.readFile(za_conf)
+
+ rep = r'LogFile=\s*(.*)'
+ tmp = re.search(rep, content)
+
+ if tmp.groups() == 0:
+ return tmp.groups()[0].strip()
+ return '/var/log/zabbix/zabbix_agentd.log'
+
+def initAgentConf():
+ za_src_tpl = getPluginDir()+'/conf/zabbix_agentd.conf'
+ za_dst_path = zabbixAgentConf()
+
+ # zabbix_agent配置
+ content = mw.readFile(za_src_tpl)
+ content = contentReplace(content)
+ mw.writeFile(za_dst_path, content)
+
+def initAgentDConf():
+ clist = ['userparameter_mysql.conf', 'userparameter_examples.conf']
+ dst_dir = '/etc/zabbix/zabbix_agentd.d'
+ for c in clist:
+ za_src_tpl = getPluginDir()+'/conf/zabbix_agentd/'+c
+ dst_path = dst_dir+'/'+c
+ if not os.path.exists(dst_path):
+ content = mw.readFile(za_src_tpl)
+ mw.writeFile(dst_path,content)
+
+def initDreplace():
+
+ init_file = getServerDir() + '/init.pl'
+ if not os.path.exists(init_file):
+ initAgentDConf()
+ initAgentConf()
+ openPort()
+ mw.writeFile(init_file, 'ok')
+ return True
+
+def openPort():
+ try:
+ from utils.firewall import Firewall as MwFirewall
+ MwFirewall.instance().addAcceptPort('10050', 'zabbix-agent', 'port')
+ return port
+ except Exception as e:
+ return "Release failed {}".format(e)
+ return True
+
+def zOp(method):
+
+ initDreplace()
+
+ data = mw.execShell('systemctl ' + method + ' zabbix-agent')
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def start():
+
+ return zOp('start')
+
+
+def stop():
+ val = zOp('stop')
+ return val
+
+def restart():
+ status = zOp('restart')
+ return status
+
+def reload():
+ return zOp('reload')
+
+def initdStatus():
+ current_os = mw.getOs()
+ if current_os == 'darwin':
+ return "Apple Computer does not support"
+
+ shell_cmd = 'systemctl status zabbix-agent | 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"
+
+ mw.execShell('systemctl enable zabbix-agent')
+ return 'ok'
+
+
+def initdUinstall():
+ current_os = mw.getOs()
+ if current_os == 'darwin':
+ return "Apple Computer does not support"
+
+ mw.execShell('systemctl disable zabbix-agent')
+ return 'ok'
+
+
+def installPreInspection():
+ zabbix_dir = mw.getServerDir()+'/zabbix'
+ if os.path.exists(zabbix_dir):
+ return '已经安装zabbix插件'
+ return 'ok'
+
+
+def uninstallPreInspection():
+ return 'ok'
+
+
+def agentdDefaultConf():
+ return '/etc/zabbix/zabbix_agentd.d/userparameter_mysql.conf'
+
+
+def agentdConf():
+ path = '/etc/zabbix/zabbix_agentd.d'
+ pathFile = os.listdir(path)
+ tmp = []
+ for one in pathFile:
+ file = path + '/' + one
+ tmp.append(file)
+ return mw.getJson(tmp)
+
+def agentdReadConf():
+ args = getArgs()
+ data = checkArgs(args, ['file'])
+ if not data[0]:
+ return data[1]
+
+ content = mw.readFile(args['file'])
+ content = contentReplace(content)
+ return mw.returnJson(True, 'ok', content)
+
+
+
+if __name__ == "__main__":
+ 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 == 'uninstall_pre_inspection':
+ print(uninstallPreInspection())
+ elif func == 'conf':
+ print(zabbixAgentConf())
+ elif func == 'zabbix_agent_conf':
+ print(zabbixAgentConf())
+ elif func == 'run_log':
+ print(runLog())
+ elif func == 'agentd_default_conf':
+ print(agentdDefaultConf())
+ elif func == 'agentd_conf':
+ print(agentdConf())
+ elif func == 'agentd_read_conf':
+ print(agentdReadConf())
+ else:
+ print('error')
diff --git a/plugins/zabbix_agent/info.json b/plugins/zabbix_agent/info.json
new file mode 100755
index 000000000..23bc1d7e7
--- /dev/null
+++ b/plugins/zabbix_agent/info.json
@@ -0,0 +1,19 @@
+{
+ "sort": 7,
+ "ps": "Zabbix被控服务器[开发中 ]",
+ "name": "zabbix_agent",
+ "title": "Zabbix Agent",
+ "shell": "install.sh",
+ "versions":["7.0"],
+ "tip": "soft",
+ "install_pre_inspection":true,
+ "uninstall_pre_inspection":true,
+ "checks": "server/zabbix_agent",
+ "path": "server/zabbix_agent",
+ "display": 1,
+ "author": "midoks",
+ "date": "2022-07-14",
+ "home": "https://www.zabbix.com",
+ "type": 0,
+ "pid": "5"
+}
diff --git a/plugins/zabbix_agent/install.sh b/plugins/zabbix_agent/install.sh
new file mode 100755
index 000000000..955c8c072
--- /dev/null
+++ b/plugins/zabbix_agent/install.sh
@@ -0,0 +1,78 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+# https://www.zabbix.com
+
+# cd /www/server/mdserver-web/plugins/zabbix_agent && /bin/bash install.sh install 7.0
+# cd /www/server/mdserver-web && python3 /www/server/mdserver-web/plugins/zabbix_agent/index.py start
+
+
+# /usr/sbin/zabbix_agentd -c /etc/zabbix/zabbix_agentd.conf
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+
+VERSION=$2
+
+sysName=`uname`
+echo "use system: ${sysName}"
+
+OSNAME=`bash ${rootPath}/scripts/getos.sh`
+
+if [ "" == "$OSNAME" ];then
+ OSNAME=`cat ${rootPath}/data/osname.pl`
+fi
+
+if [ "macos" == "$OSNAME" ];then
+ echo "不支持Macox"
+ exit
+fi
+
+if [ -f ${rootPath}/bin/activate ];then
+ source ${rootPath}/bin/activate
+fi
+
+Install_App()
+{
+ echo '正在安装脚本文件...'
+ mkdir -p $serverPath/source/zabbix_agent
+ shell_file=${curPath}/versions/${VERSION}/${OSNAME}.sh
+
+ if [ -f $shell_file ];then
+ bash -x $shell_file install
+ else
+ echo '不支持...'
+ exit 1
+ fi
+
+
+ mkdir -p $serverPath/zabbix_agent
+ echo "${VERSION}" > $serverPath/zabbix_agent/version.pl
+
+ #初始化
+ cd ${rootPath} && python3 ${rootPath}/plugins/zabbix_agent/index.py start
+ cd ${rootPath} && python3 ${rootPath}/plugins/zabbix_agent/index.py initd_install
+
+
+ echo 'Zabbix安装完成'
+}
+
+Uninstall_App()
+{
+ cd ${rootPath} && python3 ${rootPath}/plugins/zabbix_agent/index.py stop
+ cd ${rootPath} && python3 ${rootPath}/plugins/zabbix_agent/index.py initd_uninstall
+
+ rm -rf $serverPath/zabbix_agent
+ rm -rf $serverPath/source/zabbix_agent
+ echo 'Zabbix卸载完成'
+}
+
+action=$1
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix_agent/js/zabbix.js b/plugins/zabbix_agent/js/zabbix.js
new file mode 100755
index 000000000..00221700f
--- /dev/null
+++ b/plugins/zabbix_agent/js/zabbix.js
@@ -0,0 +1,187 @@
+function zabbixPost(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'zabbix';
+ req_data['func'] = method;
+ req_data['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/run', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ //错误展示10S
+ layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+function zabbixPostCallbak(method, version, args,callback){
+ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
+
+ var req_data = {};
+ req_data['name'] = 'zabbix';
+ req_data['func'] = method;
+ args['version'] = version;
+
+ if (typeof(args) == 'string'){
+ req_data['args'] = JSON.stringify(toArrayObject(args));
+ } else {
+ req_data['args'] = JSON.stringify(args);
+ }
+
+ $.post('/plugins/callback', req_data, function(data) {
+ layer.close(loadT);
+ if (!data.status){
+ layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ if(typeof(callback) == 'function'){
+ callback(data);
+ }
+ },'json');
+}
+
+
+function zabbixReadme(){
+ var readme = '';
+ readme += '需要手动配置【默认配置】 ';
+ readme += ' ';
+
+ $('.soft-man-con').html(readme);
+}
+
+//配置修改模版 --- start
+function zagentDConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_func, save_callback_func){
+ if ( typeof(version) == 'undefined' ){
+ version = '';
+ }
+
+ var func_name = 'conf';
+ if ( typeof(func) != 'undefined' ){
+ func_name = func;
+ }
+
+ var _config_tpl_func = 'config_tpl';
+ if ( typeof(config_tpl_func) != 'undefined' ){
+ _config_tpl_func = config_tpl_func;
+ }
+
+ var _read_config_tpl_func = 'read_config_tpl';
+ if ( typeof(read_config_tpl_func) != 'undefined' ){
+ _read_config_tpl_func = read_config_tpl_func;
+ }
+
+
+ var con = '提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换!
\
+ 请选择 \
+ \
+ 保存 \
+ \
+ 此处为【'+ _name + version +'】主配置文件,若您不了解配置规则,请勿随意修改。 \
+ ';
+ $(".soft-man-con").html(con);
+
+ function getFileName(file){
+ var list = file.split('/');
+ var f = list[list.length-1];
+ return f
+ }
+
+ var fileName = '';
+ $.post('/plugins/run',{name:_name, func:_config_tpl_func,version:version}, function(data){
+ var rdata = $.parseJSON(data.data);
+ for (var i = 0; i < rdata.length; i++) {
+ $('#config_tpl').append(''+getFileName(rdata[i])+' ');
+ }
+
+ $('#config_tpl').change(function(){
+ var selected = $(this).val();
+ if (selected != '0'){
+ var loadT = layer.msg('配置模版获取中...',{icon:16,time:0,shade: [0.3, '#000']});
+ fileName = selected;
+ var _args = JSON.stringify({file:selected});
+ $.post('/plugins/run', {name:_name, func:_read_config_tpl_func,version:version,args:_args}, function(data){
+ layer.close(loadT);
+ var rdata = $.parseJSON(data.data);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+
+ $("#textBody").empty().text(rdata.data);
+ $(".CodeMirror").remove();
+ var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), {
+ extraKeys: {
+ "Ctrl-Space": "autocomplete",
+ "Ctrl-F": "findPersistent",
+ "Ctrl-H": "replaceAll",
+ "Ctrl-S": function() {
+ $("#textBody").text(editor.getValue());
+ pluginConfigSave(fileName,save_callback_func);
+ }
+ },
+ lineNumbers: true,
+ matchBrackets:true,
+ });
+ editor.focus();
+ $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0});
+ $("#onlineEditFileBtn").unbind('click');
+ $("#onlineEditFileBtn").click(function(){
+ $("#textBody").text(editor.getValue());
+ pluginConfigSave(fileName, save_callback_func);
+ });
+ },'json');
+ }
+ });
+
+ },'json');
+
+ var loadT = layer.msg('配置文件路径获取中...',{icon:16,time:0,shade: [0.3, '#000']});
+ $.post('/plugins/run', {name:_name, func:func_name,version:version}, function (data) {
+ layer.close(loadT);
+
+ var loadT2 = layer.msg('文件内容获取中...',{icon:16,time:0,shade: [0.3, '#000']});
+ fileName = data.data;
+ $.post('/files/get_body', 'path=' + fileName, function(rdata) {
+ layer.close(loadT2);
+ if (!rdata.status){
+ layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
+ return;
+ }
+ $("#textBody").empty().text(rdata.data.data);
+ $(".CodeMirror").remove();
+ var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), {
+ extraKeys: {
+ "Ctrl-Space": "autocomplete",
+ "Ctrl-F": "findPersistent",
+ "Ctrl-H": "replaceAll",
+ "Ctrl-S": function() {
+ $("#textBody").text(editor.getValue());
+ pluginConfigSave(fileName,save_callback_func);
+ }
+ },
+ lineNumbers: true,
+ matchBrackets:true,
+ });
+ editor.focus();
+ $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0});
+ $("#onlineEditFileBtn").click(function(){
+ $("#textBody").text(editor.getValue());
+ pluginConfigSave(fileName,save_callback_func);
+ });
+ },'json');
+ },'json');
+}
+
diff --git a/plugins/zabbix_agent/versions/6.0/debian.sh b/plugins/zabbix_agent/versions/6.0/debian.sh
new file mode 100644
index 000000000..dd284d4e2
--- /dev/null
+++ b/plugins/zabbix_agent/versions/6.0/debian.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+Install_App()
+{
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release_6.0-5+debian${SYS_VERSION_ID}_all.deb
+ echo "wget -O $serverPath/source/zabbix/${ZABBIX_NAME} https://repo.zabbix.com/zabbix/6.0/debian/pool/main/z/zabbix-release/${ZABBIX_NAME}"
+ if [ ! -f $serverPath/source/zabbix/${ZABBIX_NAME} ];then
+ wget -O $serverPath/source/zabbix/${ZABBIX_NAME} https://repo.zabbix.com/zabbix/6.0/debian/pool/main/z/zabbix-release/${ZABBIX_NAME}
+ fi
+
+ cd $serverPath/source/zabbix && dpkg -i ${ZABBIX_NAME}
+ apt update -y
+
+ apt install -y zabbix-agent
+}
+
+Uninstall_App()
+{
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix_agent/versions/7.0/alma.sh b/plugins/zabbix_agent/versions/7.0/alma.sh
new file mode 100644
index 000000000..0c72c43b7
--- /dev/null
+++ b/plugins/zabbix_agent/versions/7.0/alma.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ yum install -y glibc-langpack-zh
+
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release-7.0-4.el${SYS_VERSION_ID:0:1}.noarch.rpm
+
+ rpm -Uvh https://repo.zabbix.com/zabbix/7.0/alma/${SYS_VERSION_ID:0:1}/x86_64/${ZABBIX_NAME}
+
+ cd $serverPath/source/zabbix && rpm -Uvh ${ZABBIX_NAME}
+ dnf install -y zabbix-agent
+}
+
+Uninstall_App()
+{
+ dnf remove -y zabbix-agent
+ rm -rf /etc/zabbix
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix_agent/versions/7.0/centos.sh b/plugins/zabbix_agent/versions/7.0/centos.sh
new file mode 100644
index 000000000..8c4e391d5
--- /dev/null
+++ b/plugins/zabbix_agent/versions/7.0/centos.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ yum install -y glibc-langpack-zh
+
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release-7.0-4.el${SYS_VERSION_ID}.noarch.rpm
+
+ rpm -Uvh https://repo.zabbix.com/zabbix/7.0/centos/${SYS_VERSION_ID}/x86_64/${ZABBIX_NAME}
+
+ cd $serverPath/source/zabbix && rpm -Uvh ${ZABBIX_NAME}
+ dnf install -y zabbix-agent
+
+ # dnf module switch-to zabbix-web-1:7.0.4
+}
+
+Uninstall_App()
+{
+ dnf remove -y zabbix-agent
+ rm -rf /etc/zabbix
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix_agent/versions/7.0/debian.sh b/plugins/zabbix_agent/versions/7.0/debian.sh
new file mode 100644
index 000000000..f29fb7992
--- /dev/null
+++ b/plugins/zabbix_agent/versions/7.0/debian.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+Install_App()
+{
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release_7.0-2+debian${SYS_VERSION_ID}_all.deb
+ echo "wget -O $serverPath/source/zabbix/${ZABBIX_NAME} https://repo.zabbix.com/zabbix/7.0/debian/pool/main/z/zabbix-release/${ZABBIX_NAME}"
+ if [ ! -f $serverPath/source/zabbix/${ZABBIX_NAME} ];then
+ wget -O $serverPath/source/zabbix/${ZABBIX_NAME} https://repo.zabbix.com/zabbix/7.0/debian/pool/main/z/zabbix-release/${ZABBIX_NAME}
+ fi
+
+ cd $serverPath/source/zabbix && dpkg -i ${ZABBIX_NAME}
+ apt update -y
+
+ apt install -y zabbix-agent
+}
+
+Uninstall_App()
+{
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix_agent/versions/7.0/opensuse.sh b/plugins/zabbix_agent/versions/7.0/opensuse.sh
new file mode 100644
index 000000000..2b08e20a5
--- /dev/null
+++ b/plugins/zabbix_agent/versions/7.0/opensuse.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ yum install -y glibc-langpack-zh
+
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release-7.0-2.sles${SYS_VERSION_ID:0:2}.noarch.rpm
+
+ rpm -Uvh https://repo.zabbix.com/zabbix/7.0/sles/${SYS_VERSION_ID:0:2}/x86_64/${ZABBIX_NAME}
+ echo "rpm -Uvh https://repo.zabbix.com/zabbix/7.0/sles/${SYS_VERSION_ID:0:2}/x86_64/${ZABBIX_NAME}"
+
+ # cd $serverPath/source/zabbix && rpm -Uvh ${ZABBIX_NAME}
+ zypper install -y zabbix-agent
+}
+
+Uninstall_App()
+{
+ zypper remove -y zabbix-agent
+ rm -rf /etc/zabbix
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix_agent/versions/7.0/rocky.sh b/plugins/zabbix_agent/versions/7.0/rocky.sh
new file mode 100644
index 000000000..9d1f8e95c
--- /dev/null
+++ b/plugins/zabbix_agent/versions/7.0/rocky.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ yum install -y glibc-langpack-zh
+
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release-7.0-4.el${SYS_VERSION_ID:0:1}.noarch.rpm
+
+ rpm -Uvh https://repo.zabbix.com/zabbix/7.0/rocky/${SYS_VERSION_ID:0:1}/x86_64/${ZABBIX_NAME}
+
+ cd $serverPath/source/zabbix && rpm -Uvh ${ZABBIX_NAME}
+ dnf install -y zabbix-agent
+}
+
+Uninstall_App()
+{
+ dnf install -y zabbix-agent
+ rm -rf /etc/zabbix
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/plugins/zabbix_agent/versions/7.0/ubuntu.sh b/plugins/zabbix_agent/versions/7.0/ubuntu.sh
new file mode 100644
index 000000000..98d7c1cf6
--- /dev/null
+++ b/plugins/zabbix_agent/versions/7.0/ubuntu.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+export DEBIAN_FRONTEND=noninteractive
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+rootPath=$(dirname "$rootPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=${serverPath}/source
+sysName=`uname`
+
+
+SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# 检查是否通
+# zabbix_get -s 127.0.0.1 -k agent.ping
+Install_App()
+{
+ mkdir -p $serverPath/source/zabbix
+
+ ZABBIX_NAME=zabbix-release_7.0-2+ubuntu${SYS_VERSION_ID}_all.deb
+ echo "wget -O $serverPath/source/zabbix/${ZABBIX_NAME} https://repo.zabbix.com/zabbix/7.0/ubuntu/pool/main/z/zabbix-release/${ZABBIX_NAME}"
+ if [ ! -f $serverPath/source/zabbix/${ZABBIX_NAME} ];then
+ wget -O $serverPath/source/zabbix/${ZABBIX_NAME} https://repo.zabbix.com/zabbix/7.0/ubuntu/pool/main/z/zabbix-release/${ZABBIX_NAME}
+ fi
+
+ # apt-get -f install
+ # dpkg --configure -a
+
+ cd $serverPath/source/zabbix && dpkg -i ${ZABBIX_NAME}
+ apt update -y
+
+ apt install -y zabbix-agent
+}
+
+Uninstall_App()
+{
+ apt remove -y zabbix-agent
+ rm -rf /etc/zabbix
+
+ # dpkg --configure -a
+ echo "卸载成功"
+}
+
+action=${1}
+if [ "${1}" == 'install' ];then
+ Install_App
+else
+ Uninstall_App
+fi
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 000000000..c7f897d97
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,48 @@
+setuptools>=33.1.1
+Werkzeug>=1.0.1
+wheel>=0.37.1
+gunicorn>=21.2.0
+requests>=2.27.1
+urllib3>=1.21.1
+flask>=2.0.3
+flask-session==0.3.2
+flask-helper==0.19
+flask-bcrypt==1.0.1
+flask-caching>=1.10.1
+cache==1.0.3
+gevent>=22.10.2
+gevent-websocket==0.10.1
+eventlet>=0.24.1
+psutil==5.9.1
+chardet==3.0.4
+SQLAlchemy>=1.4.54
+Flask-SQLAlchemy>=2.5.1
+#Flask-Migrate==4.*
+#Flask-Security-Too==5.5.*; python_version >= '3.10'
+#Flask-Security-Too==5.4.*; python_version <= '3.9'
+pyOpenSSL==23.2.0
+cryptography>=40.0.2
+configparser==5.2.0
+python-engineio>=4.3.2
+python-socketio>=4.2.0
+flask-socketio>=5.2.0
+flask-sockets>=0.2.1
+zmq==0.0.0
+paramiko>=2.8.0
+pymongo
+pymemcache
+redis
+pillow
+Jinja2>=2.11.2
+PyMySQL>=1.0.2
+whitenoise==5.3.0
+tldextract
+pyotp
+pytz
+supervisor
+pyTelegramBotAPI
+telebot
+pyyaml
+croniter
+bcrypt
+packaging
diff --git a/scripts/backup.py b/scripts/backup.py
new file mode 100755
index 000000000..14673b286
--- /dev/null
+++ b/scripts/backup.py
@@ -0,0 +1,300 @@
+# coding: utf-8
+#-----------------------------
+# 网站备份工具
+#-----------------------------
+
+import sys
+import os
+import re
+import time
+
+if sys.platform != 'darwin':
+ os.chdir('/www/server/mdserver-web')
+
+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
+import core.db as db
+
+
+class backupTools:
+
+ def backupSite(self, name, count, echo=None):
+ exclude_dir_cmd = self.makeExcludeDirCmd(echo)
+
+ sql = db.Sql()
+ path = sql.table('sites').where('name=?', (name,)).getField('path')
+ startTime = time.time()
+ if not path:
+ endDate = time.strftime('%Y/%m/%d %X', time.localtime())
+ log = "网站[" + name + "]不存在!"
+ print("★[" + endDate + "] " + log)
+ print(
+ "----------------------------------------------------------------------------")
+ return
+
+ backup_path = mw.getBackupDir() + '/site'
+ if not os.path.exists(backup_path):
+ mw.execShell("mkdir -p " + backup_path)
+
+ filename = backup_path + "/web_" + name + "_" + \
+ time.strftime('%Y%m%d_%H%M%S', time.localtime()) + '.tar.gz'
+
+ cmd = "cd " + os.path.dirname(path) + " && tar zcvf '" + \
+ filename + "' " + exclude_dir_cmd + " '" + os.path.basename(path) + "' > /dev/null"
+
+ # print(cmd)
+ mw.execShell(cmd)
+
+ endDate = time.strftime('%Y/%m/%d %X', time.localtime())
+ # print(filename)
+ if not os.path.exists(filename):
+ log = "网站[" + name + "]备份失败!"
+ print("★[" + endDate + "] " + log)
+ print("----------------------------------------------------------------------------")
+ return
+
+ outTime = time.time() - startTime
+ pid = sql.table('sites').where('name=?', (name,)).getField('id')
+ sql.table('backup').add('type,name,pid,filename,add_time,size', ('0', os.path.basename(
+ filename), pid, filename, endDate, os.path.getsize(filename)))
+ log = "网站[" + name + "]备份成功,用时[" + str(round(outTime, 2)) + "]秒"
+ mw.writeLog('计划任务', log)
+ print("★[" + endDate + "] " + log)
+ print("|---保留最新的[" + count + "]份备份")
+ print("|---文件名:" + filename)
+
+ # 清理多余备份
+ backups = sql.table('backup').where(
+ 'type=? and pid=?', ('0', pid)).field('id,filename').select()
+
+ num = len(backups) - int(count)
+ if num > 0:
+ for backup in backups:
+ mw.execShell("rm -f " + backup['filename'])
+ sql.table('backup').where('id=?', (backup['id'],)).delete()
+ num -= 1
+ print("|---已清理过期备份文件:" + backup['filename'])
+ if num < 1:
+ break
+
+ def getConf(self, mtype='mysql'):
+ path = mw.getServerDir() + '/' + mtype + '/etc/my.cnf'
+ return path
+
+ def recognizeDbMode(self, mtype='mysql'):
+ conf = self.getConf(mtype)
+ con = mw.readFile(conf)
+ rep = r"!include %s/(.*)?\.cnf" % (mw.getServerDir() +'/'+ mtype +"/etc/mode",)
+ mode = 'none'
+ try:
+ data = re.findall(rep, con, re.M)
+ mode = data[0]
+ except Exception as e:
+ pass
+ return mode
+
+ # 数据库密码处理
+ def mypass(self, act, root):
+ conf_file = self.getConf('mysql')
+ mw.execShell("sed -i '/user=root/d' {}".format(conf_file))
+ mw.execShell("sed -i '/password=/d' {}".format(conf_file))
+ if act:
+ mycnf = mw.readFile(conf_file)
+ src_dump = "[mysqldump]\n"
+ sub_dump = src_dump + "user=root\npassword=\"{}\"\n".format(root)
+ if not mycnf:
+ return False
+ mycnf = mycnf.replace(src_dump, sub_dump)
+ if len(mycnf) > 100:
+ mw.writeFile(conf_file, mycnf)
+ return True
+ return True
+
+ def backupDatabase(self, name, count):
+ db_path = mw.getServerDir() + '/mysql'
+ db_name = 'mysql'
+ name = mw.M('databases').dbPos(db_path, 'mysql').where(
+ 'name=?', (name,)).getField('name')
+ startTime = time.time()
+ if not name:
+ endDate = time.strftime('%Y/%m/%d %X', time.localtime())
+ log = "数据库[" + name + "]不存在!"
+ print("★[" + endDate + "] " + log)
+ print(
+ "----------------------------------------------------------------------------")
+ return
+
+ backup_path = mw.getBackupDir() + '/database'
+ if not os.path.exists(backup_path):
+ mw.execShell("mkdir -p " + backup_path)
+
+ filename = backup_path + "/db_" + name + "_" + \
+ time.strftime('%Y%m%d_%H%M%S', time.localtime()) + ".sql.gz"
+
+ mysql_root = mw.M('config').dbPos(db_path, db_name).where(
+ "id=?", (1,)).getField('mysql_root')
+
+ my_cnf = self.getConf('mysql')
+ self.mypass(True, mysql_root)
+
+ # mw.execShell(db_path + "/bin/mysqldump --opt --default-character-set=utf8 " +
+ # name + " | gzip > " + filename)
+
+ # mw.execShell(db_path + "/bin/mysqldump --single-transaction --quick --default-character-set=utf8 " +
+ # name + " | gzip > " + filename)
+
+ # 开启一致性事务 会lock表
+ # cmd = db_path + "/bin/mysqldump --defaults-file=" + my_cnf + " --force --opt --default-character-set=utf8 " + \
+ # name + " | gzip > " + filename
+ option = ''
+ mode = self.recognizeDbMode('mysql')
+ if mode == 'gtid':
+ option = ' --set-gtid-purged=off '
+
+ # skip-opt 不会lock表
+ # --skip-opt --create-options
+ cmd = db_path + "/bin/mysqldump --defaults-file=" + my_cnf +" " + option +" --single-transaction -q --default-character-set=utf8mb4 " + \
+ name + " | gzip > " + filename
+ # print(cmd)
+ mw.execShell(cmd)
+
+ if not os.path.exists(filename):
+ endDate = time.strftime('%Y/%m/%d %X', time.localtime())
+ log = "数据库[" + name + "]备份失败!"
+ print("★[" + endDate + "] " + log)
+ print(
+ "----------------------------------------------------------------------------")
+ return
+
+ self.mypass(False, mysql_root)
+
+ endDate = time.strftime('%Y/%m/%d %X', time.localtime())
+ outTime = time.time() - startTime
+ pid = mw.M('databases').dbPos(db_path, db_name).where(
+ 'name=?', (name,)).getField('id')
+
+ mw.M('backup').add('type,name,pid,filename,add_time,size', (1, os.path.basename(
+ filename), pid, filename, endDate, os.path.getsize(filename)))
+ log = "数据库[" + name + "]备份成功,用时[" + str(round(outTime, 2)) + "]秒"
+ mw.writeLog('计划任务', log)
+ print("★[" + endDate + "] " + log)
+ print("|---保留最新的[" + count + "]份备份")
+ print("|---文件名:" + filename)
+
+ # 清理多余备份
+ backups = mw.M('backup').where(
+ 'type=? and pid=?', ('1', pid)).field('id,filename').select()
+
+ num = len(backups) - int(count)
+ if num > 0:
+ for backup in backups:
+ mw.execShell("rm -f " + backup['filename'])
+ mw.M('backup').where('id=?', (backup['id'],)).delete()
+ num -= 1
+ print("|---已清理过期备份文件:" + backup['filename'])
+ if num < 1:
+ break
+
+ def backupSiteAll(self, save):
+ sites = mw.M('sites').field('name').select()
+ for site in sites:
+ self.backupSite(site['name'], save)
+
+ def backupDatabaseAll(self, save):
+ db_path = mw.getServerDir() + '/mysql'
+ db_name = 'mysql'
+ databases = mw.M('databases').dbPos(
+ db_path, db_name).field('name').select()
+ for database in databases:
+ self.backupDatabase(database['name'], save)
+
+ def findPathName(self, path, filename):
+ f = os.scandir(path)
+ l = []
+ for ff in f:
+ if ff.name.find(filename) > -1:
+ l.append(ff.name)
+ return l
+
+ def makeExcludeDirCmd(self,echo):
+ exclude_dirs = []
+ crontab_list = mw.M('crontab').where('echo=?', (echo,)).field('attr').find()
+ if crontab_list:
+ attr = crontab_list['attr']
+ if attr != "":
+ ed_arrs = attr.split("\n")
+ for ed in ed_arrs:
+ exclude_dirs.append(ed.strip())
+
+
+ cmd = ""
+ for v in exclude_dirs:
+ cmd += " --exclude='"+v+"'"
+ return cmd
+
+ def backupPath(self, path, count, echo=None):
+
+ exclude_dir_cmd = self.makeExcludeDirCmd(echo)
+ # print(exclude_dir_cmd)
+ mw.echoStart('备份')
+
+ backup_path = mw.getBackupDir() + '/path'
+ if not os.path.exists(backup_path):
+ mw.execShell("mkdir -p " + backup_path)
+
+ dirname = os.path.basename(path)
+ fname = 'path_{}_{}.tar.gz'.format(
+ dirname, mw.formatDate("%Y%m%d_%H%M%S"))
+ dfile = os.path.join(backup_path, fname)
+
+ p_size = mw.getPathSize(path)
+ stime = time.time()
+
+ cmd = "cd " + os.path.dirname(path) + " && tar zcvf '" + dfile + "' " + exclude_dir_cmd + " '" + dirname + "' 2>{err_log} 1> /dev/null".format(
+ err_log='/tmp/backup_err.log')
+ # print(cmd)
+ mw.execShell(cmd)
+
+ tar_size = os.path.getsize(dfile)
+
+ mw.echoInfo('备份目录:' + path)
+ mw.echoInfo('目录已备份到:' + dfile)
+ mw.echoInfo("目录大小:{}".format(mw.toSize(p_size)))
+ mw.echoInfo("开始压缩文件:{}".format(mw.formatDate(times=stime)))
+ mw.echoInfo("文件压缩完成,耗时{:.2f}秒,压缩包大小:{}".format(
+ time.time() - stime, mw.toSize(tar_size)))
+ mw.echoInfo('保留最新的备份数:' + count + '份')
+
+ backups = self.findPathName(backup_path, 'path_{}'.format(dirname))
+ num = len(backups) - int(count)
+ backups.sort()
+ if num > 0:
+ for backup in backups:
+ abspath_bk = backup_path + "/" + backup
+ mw.execShell("rm -f " + abspath_bk)
+ mw.echoInfo("已清理过期备份文件:" + abspath_bk)
+ num -= 1
+ if num < 1:
+ break
+
+ mw.echoEnd('备份')
+
+if __name__ == "__main__":
+ backup = backupTools()
+ stype = sys.argv[1]
+ if stype == 'site':
+ if sys.argv[2] == 'ALL':
+ backup.backupSiteAll(sys.argv[3])
+ else:
+ backup.backupSite(sys.argv[2], sys.argv[3], sys.argv[4])
+ elif stype == 'database':
+ if sys.argv[2] == 'ALL':
+ backup.backupDatabaseAll(sys.argv[3])
+ else:
+ backup.backupDatabase(sys.argv[2], sys.argv[3])
+ elif stype == 'path':
+ backup.backupPath(sys.argv[2], sys.argv[3], sys.argv[4])
diff --git a/scripts/getos.sh b/scripts/getos.sh
new file mode 100755
index 000000000..2e15da244
--- /dev/null
+++ b/scripts/getos.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+#获取信息和版本
+_os=`uname`
+if [ ${_os} == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eq "openSUSE" /etc/*-release; then
+ OSNAME='opensuse'
+elif grep -Eq "FreeBSD" /etc/*-release; then
+ OSNAME='freebsd'
+elif grep -Eqi "EulerOS" /etc/*-release || grep -Eqi "openEuler" /etc/*-release; then
+ OSNAME='euler'
+elif grep -Eqi "Arch" /etc/issue || grep -Eq "Arch" /etc/*-release; then
+ OSNAME='arch'
+elif grep -Eqi "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release; then
+ OSNAME='centos'
+elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eq "AlmaLinux" /etc/*-release; then
+ OSNAME='alma'
+elif grep -Eqi "Rocky" /etc/issue || grep -Eq "Rocky" /etc/*-release; then
+ OSNAME='rocky'
+elif grep -Eqi "Red Hat Enterprise Linux Server" /etc/issue || grep -Eq "Red Hat Enterprise Linux Server" /etc/*-release; then
+ OSNAME='rhel'
+elif grep -Eqi "Aliyun" /etc/issue || grep -Eq "Aliyun" /etc/*-release; then
+ OSNAME='aliyun'
+elif grep -Eqi "Fedora" /etc/issue || grep -Eq "Fedora" /etc/*-release; then
+ OSNAME='fedora'
+elif grep -Eqi "Amazon Linux AMI" /etc/issue || grep -Eq "Amazon Linux AMI" /etc/*-release; then
+ OSNAME='amazon'
+elif grep -Eqi "Debian" /etc/issue || grep -Eq "Debian" /etc/*-release; then
+ OSNAME='debian'
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/*-release; then
+ OSNAME='ubuntu'
+elif grep -Eqi "Raspbian" /etc/issue || grep -Eq "Raspbian" /etc/*-release; then
+ OSNAME='raspbian'
+elif grep -Eqi "Deepin" /etc/issue || grep -Eq "Deepin" /etc/*-release; then
+ OSNAME='deepin'
+else
+ OSNAME='unknow'
+fi
+
+if [ -d /www/server/mdserver-web ];then
+ echo "$OSNAME" > /www/server/mdserver-web/data/osname.pl
+fi
+
+if [ "$OSNAME" == "macos" ];then
+ echo "$OSNAME"
+fi
\ No newline at end of file
diff --git a/scripts/init.d/mw-task.service.tpl b/scripts/init.d/mw-task.service.tpl
new file mode 100755
index 000000000..405d9138d
--- /dev/null
+++ b/scripts/init.d/mw-task.service.tpl
@@ -0,0 +1,16 @@
+[Unit]
+Description=mw-task daemon
+After=network.target
+
+[Service]
+Type=simple
+WorkingDirectory={$SERVER_PATH}
+EnvironmentFile={$SERVER_PATH}/scripts/init.d/service.sh
+ExecStart=python3 panel_task.py
+ExecStop=kill -HUP $MAINID
+ExecReload=kill -HUP $MAINID
+KillMode=process
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/scripts/init.d/mw.service.tpl b/scripts/init.d/mw.service.tpl
new file mode 100755
index 000000000..8c92d1214
--- /dev/null
+++ b/scripts/init.d/mw.service.tpl
@@ -0,0 +1,21 @@
+[Unit]
+Description=mw-panel daemon
+After=network.target
+
+[Service]
+Type=simple
+WorkingDirectory={$SERVER_PATH}
+EnvironmentFile={$SERVER_PATH}/scripts/init.d/service.sh
+ExecStart=cd web && gunicorn -c setting.py app:app
+ExecStop=kill -HUP $MAINID
+ExecReload=kill -HUP $MAINID
+KillMode=process
+Restart=on-failure
+
+[Timer]
+# 每日凌晨点重启
+OnCalendar=*-*-* 03:30:00
+Unit=mw.service
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl
new file mode 100755
index 000000000..aa1307f5c
--- /dev/null
+++ b/scripts/init.d/mw.tpl
@@ -0,0 +1,781 @@
+#!/bin/bash
+# chkconfig: 2345 55 25
+# description: MW Cloud Service
+
+### BEGIN INIT INFO
+# Provides: Midoks
+# Required-Start: $all
+# Required-Stop: $all
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts mw
+# Description: starts the mw
+### END INIT INFO
+
+RED='\033[31m'
+GREEN='\033[32m'
+YELLOW='\033[33m'
+BLUE='\033[34m'
+PLAIN='\033[0m'
+BOLD='\033[1m'
+SUCCESS='[\033[32mOK\033[0m]'
+COMPLETE='[\033[32mDONE\033[0m]'
+WARN='[\033[33mWARN\033[0m]'
+ERROR='[\033[31mERROR\033[0m]'
+WORKING='[\033[34m*\033[0m]'
+
+
+PATH=/usr/local/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export LANG=en_US.UTF-8
+
+PANEL_DIR={$SERVER_PATH}
+ROOT_PATH=$(dirname "$PANEL_DIR")
+PATH=$PATH:${PANEL_DIR}/bin
+
+
+if [ -f ${PANEL_DIR}/bin/activate ];then
+ source ${PANEL_DIR}/bin/activate
+ if [ "$?" != "0" ];then
+ echo "load local python env fail!"
+ fi
+fi
+
+mw_start_panel()
+{
+ isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+ if [ "$isStart" == '' ];then
+ echo -e "starting mw-panel... \c"
+ cd ${PANEL_DIR}/web && gunicorn -c setting.py app:app
+ port=$(cat ${PANEL_DIR}/data/port.pl)
+ isStart=""
+ while [[ "$isStart" == "" ]];
+ do
+ echo -e ".\c"
+ sleep 0.5
+ isStart=$(lsof -n -P -i:$port|grep LISTEN|grep -v grep|awk '{print $2}'|xargs)
+ if [[ "$isStart" == "" ]];then
+ isStart=$(ps -ef|grep python3|grep mdserver-web|grep app:app|awk '{print $2}'|xargs)
+ fi
+ let n+=1
+ if [ $n -gt 60 ];then
+ break;
+ fi
+ done
+ if [ "$isStart" == '' ];then
+ echo -e "\033[31mfailed\033[0m"
+ echo '------------------------------------------------------'
+ tail -n 20 ${PANEL_DIR}/logs/panel_error.log
+ echo '------------------------------------------------------'
+ echo -e "\033[31mError: mw-panel service startup failed.\033[0m"
+ return;
+ fi
+ echo -e "\033[32mdone\033[0m"
+ else
+ echo "starting mw-panel... mw(pid $(echo $isStart)) already running"
+ fi
+}
+
+
+mw_start_task()
+{
+ isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}')
+ if [ "$isStart" == '' ];then
+ echo -e "starting mw-tasks... \c"
+ cd ${PANEL_DIR} && python3 panel_task.py >> ${PANEL_DIR}/logs/panel_task.log 2>&1 &
+ sleep 0.3
+ isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}')
+ if [ "$isStart" == '' ];then
+ echo -e "\033[31mfailed\033[0m"
+ echo '------------------------------------------------------'
+ tail -n 20 ${PANEL_DIR}/logs/panel_task.log
+ echo '------------------------------------------------------'
+ echo -e "\033[31mError: mw-tasks service startup failed.\033[0m"
+ return;
+ fi
+ echo -e "\033[32mdone\033[0m"
+ else
+ echo "starting mw-tasks... mw-tasks (pid $(echo $isStart)) already running"
+ fi
+}
+
+mw_start()
+{
+ mw_start_task
+ mw_start_panel
+}
+
+# /www/server/mdserver-web/logs/panel_task.lock && service mw restart_task
+mw_stop_task()
+{
+ if [ -f ${PANEL_DIR}/logs/panel_task.lock ];then
+ echo -e "\033[32mthe task is running and cannot be stopped\033[0m"
+ return 0
+ fi
+
+ echo -e "stopping mw-tasks... \c";
+ panel_task=$(ps aux | grep 'panel_task.py'|grep -v grep|awk '{print $2}')
+ panel_task=($panel_task)
+ for p in ${panel_task[@]}
+ do
+ kill -9 $p > /dev/null 2>&1
+ done
+
+ zzpids=$(ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]' | awk '{print $2}')
+ zzpids=($zzpids)
+ for p in ${zzpids[@]}
+ do
+ kill -9 $p > /dev/null 2>&1
+ done
+ echo -e "\033[32mdone\033[0m"
+}
+
+mw_stop_panel()
+{
+ echo -e "stopping mw-panel... \c";
+ pidfile=${PANEL_DIR}/logs/mw.pid
+ if [ -f $pidfile ];then
+ pid=`cat $pidfile`
+ kill -9 $pid > /dev/null 2>&1
+ rm -f $pidfile
+ fi
+
+ APP_LIST=`ps aux|grep 'gunicorn -c setting.py app:app'|grep -v grep|awk '{print $2}'`
+ APP_LIST=($APP_LIST)
+ for p in ${APP_LIST[@]}
+ do
+ kill -9 $p > /dev/null 2>&1
+ done
+
+ APP_LIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'`
+ APP_LIST=($APP_LIST)
+ for i in ${APP_LIST[@]}
+ do
+ kill -9 $i > /dev/null 2>&1
+ done
+ echo -e "\033[32mdone\033[0m"
+}
+
+mw_stop()
+{
+ mw_stop_task
+ mw_stop_panel
+}
+
+mw_status()
+{
+ isStart=$(ps aux|grep 'gunicorn -c setting.py app:app'|grep -v grep|awk '{print $2}')
+ if [ "$isStart" != '' ];then
+ echo -e "\033[32mmw (pid $(echo $isStart)) already running\033[0m"
+ else
+ echo -e "\033[31mmw not running\033[0m"
+ fi
+
+ isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}')
+ if [ "$isStart" != '' ];then
+ echo -e "\033[32mmw-task (pid $isStart) already running\033[0m"
+ else
+ echo -e "\033[31mmw-task not running\033[0m"
+ fi
+}
+
+
+mw_reload()
+{
+ isStart=$(ps aux|grep 'gunicorn -c setting.py app:app'|grep -v grep|awk '{print $2}')
+
+ if [ "$isStart" != '' ];then
+ echo -e "reload mw... \c";
+ arr=`ps aux|grep 'gunicorn -c setting.py app:app'|grep -v grep|awk '{print $2}'`
+ for p in ${arr[@]}
+ do
+ kill -9 $p
+ done
+ cd ${PANEL_DIR}/web && gunicorn -c setting.py app:app
+ isStart=`ps aux|grep 'gunicorn -c setting.py app:app'|grep -v grep|awk '{print $2}'`
+ if [ "$isStart" == '' ];then
+ echo -e "\033[31mfailed\033[0m"
+ echo '------------------------------------------------------'
+ tail -n 20 $mw_path/logs/error.log
+ echo '------------------------------------------------------'
+ echo -e "\033[31mError: mw service startup failed.\033[0m"
+ return;
+ fi
+ echo -e "\033[32mdone\033[0m"
+ else
+ echo -e "\033[31mmw not running\033[0m"
+ mw_start
+ fi
+}
+
+mw_close(){
+ cd ${PANEL_DIR} && python3 panel_tools.py cli 14
+}
+
+mw_open()
+{
+ cd ${PANEL_DIR} && python3 panel_tools.py cli 15
+}
+
+mw_unbind_domain()
+{
+ if [ -f ${PANEL_DIR}/data/bind_domain.pl ];then
+ rm -rf ${PANEL_DIR}/data/bind_domain.pl
+ fi
+}
+
+mw_unbind_ssl()
+{
+ if [ -f ${PANEL_DIR}/local ];then
+ rm -rf ${PANEL_DIR}/local
+ fi
+
+ if [ -f $mw_path/nginx ];then
+ rm -rf $mw_path/nginx
+ fi
+
+ if [ -f $mw_path/ssl/choose.pl ];then
+ rm -rf $mw_path/ssl/choose.pl
+ fi
+}
+
+error_logs()
+{
+ tail -n 100 ${PANEL_DIR}/logs/panel_error.log
+}
+
+# 00----00----00----00----00----00----00----00----00----00----00----00----00----00
+
+function AutoSizeStr(){
+ NAME_STR=$1
+ NAME_NUM=$2
+
+ NAME_STR_LEN=`echo "$NAME_STR" | wc -L`
+ NAME_NUM_LEN=`echo "$NAME_NUM" | wc -L`
+
+ fix_len=35
+ remaining_len=`expr $fix_len - $NAME_STR_LEN - $NAME_NUM_LEN`
+ FIX_SPACE=' '
+ for ((ass_i=1;ass_i<=$remaining_len;ass_i++))
+ do
+ FIX_SPACE="$FIX_SPACE "
+ done
+ echo -e " ❖ ${1}${FIX_SPACE}${2})"
+}
+
+function ChooseProxyURL(){
+ clear
+ echo -e '+---------------------------------------------------+'
+ echo -e '| |'
+ echo -e '| ============================================= |'
+ echo -e '| |'
+ echo -e '| 欢迎使用 Linux 一键安装mdserver-web面板源码 |'
+ echo -e '| |'
+ echo -e '| ============================================= |'
+ echo -e '| |'
+ echo -e '+---------------------------------------------------+'
+ echo -e ''
+ echo -e '#####################################################'
+ echo -e ''
+ echo -e ' 提供以下国内代理地址可供选择: '
+ echo -e ''
+ echo -e '#####################################################'
+ echo -e ''
+ cm_i=0
+ for V in ${SOURCE_LIST_KEY[@]}; do
+ num=`expr $cm_i + 1`
+ AutoSizeStr "${V}" "$num"
+ cm_i=`expr $cm_i + 1`
+ done
+ echo -e ''
+ echo -e '#####################################################'
+ echo -e ''
+ echo -e " 系统时间 ${BLUE}$(date "+%Y-%m-%d %H:%M:%S")${PLAIN}"
+ echo -e ''
+ echo -e '#####################################################'
+ CHOICE_A=$(echo -e "\n${BOLD}└─ 请选择并输入你想使用的代理地址 [ 1-${SOURCE_LIST_LEN} ]:${PLAIN}")
+
+ read -p "${CHOICE_A}" INPUT
+ # echo $INPUT
+ if [ "$INPUT" == "" ];then
+ INPUT=1
+ TMP_INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$TMP_INPUT]}
+ echo -e "\n默认选择[${BLUE}${INPUT_KEY}${PLAIN}]安装!"
+ fi
+
+ if [ "$INPUT" -lt "0" ];then
+ INPUT=1
+ TMP_INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$TMP_INPUT]}
+ echo -e "\n低于边界错误!选择[${BLUE}${INPUT_KEY}${PLAIN}]安装!"
+ sleep 2s
+ fi
+
+ if [ "$INPUT" -gt "${SOURCE_LIST_LEN}" ];then
+ INPUT=${SOURCE_LIST_LEN}
+ TMP_INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$TMP_INPUT]}
+ echo -e "\n超出边界错误!选择[${BLUE}${INPUT_KEY}${PLAIN}]安装!"
+ sleep 2s
+ fi
+
+ INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$INPUT]}
+ HTTP_PREFIX=${PROXY_URL[$INPUT_KEY]}
+}
+
+
+mw_common_proxy(){
+ HTTP_PREFIX="https://"
+ LOCAL_ADDR=common
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" != "common" ];then
+ # https://github.akams.cn
+ declare -A PROXY_URL
+ PROXY_URL["gh_proxy_com"]="https://gh-proxy.com/"
+ PROXY_URL["github_do"]="https://github.do/"
+ PROXY_URL["gh_llkk_cc"]="https://gh.llkk.cc/https://"
+ PROXY_URL["gh_felicity_ac_cn"]="https://gh.felicity.ac.cn/https://"
+ PROXY_URL["ghfast_top"]="https://ghfast.top/"
+ PROXY_URL["ghproxy_net"]="https://ghproxy.net/"
+ PROXY_URL["gh_927223_xyz"]="https://gh.927223.xyz/https://"
+ PROXY_URL["gh_proxy_net"]="https://gh-proxy.net/"
+
+ PROXY_URL["source"]="https://"
+
+
+ SOURCE_LIST_KEY_SORT_TMP=$(echo ${!PROXY_URL[@]} | tr ' ' '\n' | sort -n)
+ SOURCE_LIST_KEY=(${SOURCE_LIST_KEY_SORT_TMP//'\n'/})
+ SOURCE_LIST_LEN=${#PROXY_URL[*]}
+ fi
+
+ if [ "$LOCAL_ADDR" != "common" ];then
+ ChooseProxyURL
+
+ if [ "$HTTP_PREFIX" != "https://" ];then
+ DOMAIN=`echo $HTTP_PREFIX | sed 's|https://||g'`
+ DOMAIN=`echo $DOMAIN | sed 's|/||g'`
+ ping -c 3 $DOMAIN > /dev/null 2>&1
+ if [ "$?" != "0" ];then
+ echo "无效代理地址:${HTTP_PREFIX}"
+ exit
+ fi
+ fi
+ fi
+}
+
+mw_install(){
+ if [ -f ${PANEL_DIR}/task.py ];then
+ echo "与后续版本差异太大,不再提供更新"
+ exit 0
+ fi
+
+ mw_common_proxy
+ echo "bash <(curl -fsSL "${HTTP_PREFIX}raw.githubusercontent.com/midoks/mdserver-web/master/scripts/install.sh")"
+ bash <(curl -fsSL "${HTTP_PREFIX}raw.githubusercontent.com/midoks/mdserver-web/master/scripts/install.sh")
+}
+
+mw_update()
+{
+ if [ -f ${PANEL_DIR}/task.py ];then
+ echo "与后续版本差异太大,不再提供更新"
+ exit 0
+ fi
+
+ mw_common_proxy
+ echo "bash <(curl -fsSL "${HTTP_PREFIX}raw.githubusercontent.com/midoks/mdserver-web/master/scripts/update.sh")"
+ bash <(curl -fsSL "${HTTP_PREFIX}raw.githubusercontent.com/midoks/mdserver-web/master/scripts/update.sh")
+}
+
+mw_update_dev()
+{
+ if [ -f ${PANEL_DIR}/task.py ];then
+ echo "与后续版本差异太大,不再提供更新"
+ exit 0
+ fi
+
+ mw_common_proxy
+ echo "bash <(curl -fsSL "${HTTP_PREFIX}raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh")"
+ bash <(curl -fsSL "${HTTP_PREFIX}raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh")
+
+ cd ${PANEL_DIR}
+}
+
+mw_update_venv()
+{
+ rm -rf ${PANEL_DIR}/bin
+ rm -rf ${PANEL_DIR}/lib64
+ rm -rf ${PANEL_DIR}/lib
+
+ mw_common_proxy
+ echo "bash <(curl -fsSL "${HTTP_PREFIX}raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh")"
+ bash <(curl -fsSL "${HTTP_PREFIX}raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh")
+
+ cd ${PANEL_DIR}
+}
+
+mw_mirror()
+{
+ LOCAL_ADDR=common
+ cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+ if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ fi
+
+ if [ "$LOCAL_ADDR" == "common" ];then
+ bash <(curl --insecure -sSL https://raw.githubusercontent.com/midoks/change-linux-mirrors/main/change-mirrors.sh)
+ else
+ bash <(curl -sSL https://linuxmirrors.cn/main.sh)
+ fi
+ cd ${ROOT_PATH}
+}
+
+mw_install_app()
+{
+ bash $mw_path/scripts/quick/app.sh
+}
+
+mw_close_admin_path(){
+ cd ${PANEL_DIR} && python3 panel_tools.py cli 6
+}
+
+mw_force_kill()
+{
+ PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'`
+ for i in $PLIST
+ do
+ kill -9 $i
+ done
+
+ pids=`ps -ef|grep task.py | grep -v grep |awk '{print $2}'`
+ arr=($pids)
+ for p in ${arr[@]}
+ do
+ kill -9 $p
+ done
+}
+
+mw_debug(){
+ mw_stop
+ mw_force_kill
+
+ port=7200
+ if [ -f ${PANEL_DIR}/data/port.pl ];then
+ port=$(cat ${PANEL_DIR}/data/port.pl)
+ fi
+
+ if [ -d ${PANEL_DIR}/web ];then
+ cd ${PANEL_DIR}/web
+ fi
+ # gunicorn -b :$port -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
+ gunicorn -b :$port -k eventlet -w 1 app:app
+}
+
+mw_connect_mysql(){
+ # choose mysql login
+
+ declare -A DB_TYPE
+
+ if [ -d "${ROOT_PATH}/mysql" ];then
+ DB_TYPE["mysql"]="mysql"
+ fi
+
+ if [ -d "${ROOT_PATH}/mariadb" ];then
+ DB_TYPE["mariadb"]="mariadb"
+ fi
+
+ if [ -d "${ROOT_PATH}/mysql-apt" ];then
+ DB_TYPE["mysql-apt"]="mysql-apt"
+ fi
+
+ if [ -d "${ROOT_PATH}/mysql-yum" ];then
+ DB_TYPE["mysql-yum"]="mysql-yum"
+ fi
+
+ if [ -d "${ROOT_PATH}/mysql-community" ];then
+ DB_TYPE["mysql-community"]="mysql-community"
+ fi
+
+ SOURCE_LIST_KEY_SORT_TMP=$(echo ${!DB_TYPE[@]} | tr ' ' '\n' | sort -n)
+ SOURCE_LIST_KEY=(${SOURCE_LIST_KEY_SORT_TMP//'\n'/})
+ SOURCE_LIST_LEN=${#DB_TYPE[*]}
+
+ if [ "$SOURCE_LIST_LEN" == "0" ]; then
+ echo -e "no data!"
+ exit 1
+ fi
+
+ cm_i=0
+ for M in ${SOURCE_LIST_KEY[@]}; do
+ num=`expr $cm_i + 1`
+ AutoSizeStr "${M}" "$num"
+ cm_i=`expr $cm_i + 1`
+ done
+ CHOICE_A=$(echo -e "\n${BOLD}└─ Please select and enter the database you want to log in to [ 1-${SOURCE_LIST_LEN} ]:${PLAIN}")
+ read -p "${CHOICE_A}" INPUT
+
+ if [ "$INPUT" == "" ]; then
+ INPUT=1
+ fi
+
+ if [ "$INPUT" -lt "0" ] || [ "$INPUT" -gt "${SOURCE_LIST_LEN}" ]; then
+ echo -e "\nBoundary error not selected!"
+ exit 1
+ fi
+
+ INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$INPUT]}
+ CHOICE_DB=${DB_TYPE[$INPUT_KEY]}
+ echo "login to ${CHOICE_DB}:"
+
+ pwd=$(cd ${ROOT_PATH}/mdserver-web && python3 ${ROOT_PATH}/mdserver-web/plugins/${CHOICE_DB}/index.py root_pwd)
+ if [ "$pwd" == "admin" ];then
+ pwd=""
+ fi
+
+ if [ "$CHOICE_DB" == "mysql" ];then
+ ${ROOT_PATH}/mysql/bin/mysql -uroot -p"${pwd}"
+ fi
+
+ if [ "$CHOICE_DB" == "mariadb" ];then
+ ${ROOT_PATH}/mariadb/bin/mariadb -S ${ROOT_PATH}/mariadb/mysql.sock -uroot -p"${pwd}"
+ fi
+
+ if [ "$CHOICE_DB" == "mysql-community" ];then
+ ${ROOT_PATH}/mysql-community/bin/mysql -S ${ROOT_PATH}/mysql-community/mysql.sock -uroot -p"${pwd}"
+ fi
+
+ if [ "$CHOICE_DB" == "mysql-apt" ];then
+ ${ROOT_PATH}/mysql-apt/bin/usr/bin/mysql -S ${ROOT_PATH}/mysql-apt/mysql.sock -uroot -p"${pwd}"
+ fi
+
+ if [ "$CHOICE_DB" == "mysql-yum" ];then
+ ${ROOT_PATH}/mysql-yum/bin/usr/bin/mysql -S ${ROOT_PATH}/mysql-yum/mysql.sock -uroot -p"${pwd}"
+ fi
+}
+
+
+mw_connect_pgdb(){
+ if [ ! -d "${ROOT_PATH}/postgresql" ];then
+ echo -e "postgresql not install!"
+ exit 1
+ fi
+
+
+ pwd=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/plugins/postgresql/index.py root_pwd)
+ export PGPASSWORD=${pwd}
+ echo "${ROOT_PATH}/postgresql/bin/psql -U postgres -W"
+ ${ROOT_PATH}/postgresql/bin/psql -U postgres -W
+}
+
+
+mw_mongodb(){
+ CONF="${ROOT_PATH}/mongodb/mongodb.conf"
+ if [ ! -f "$CONF" ]; then
+ echo -e "not install mongodb!"
+ exit 1
+ fi
+
+ MGDB_PORT=$(cat $CONF |grep port|grep -v '#'|awk '{print $2}')
+ MGDB_AUTH=$(cat $CONF |grep authorization | grep -v '#'|awk '{print $2}')
+
+ AUTH_STR=""
+ if [[ "$MGDB_AUTH" == "enabled" ]];then
+ pwd=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/plugins/mongodb/index.py root_pwd)
+ AUTH_STR="-u root -p ${pwd}"
+ fi
+
+ CLIEXEC="${ROOT_PATH}/mongodb/bin/mongosh --port ${MGDB_PORT} ${AUTH_STR}"
+ echo $CLIEXEC
+ ${CLIEXEC}
+}
+
+
+mw_redis(){
+ CONF="${ROOT_PATH}/redis/redis.conf"
+
+ if [ ! -f "$CONF" ]; then
+ echo -e "not install redis!"
+ exit 1
+ fi
+
+ REDISPORT=$(cat $CONF |grep port|grep -v '#'|awk '{print $2}')
+ REDISPASS=$(cat $CONF |grep requirepass|grep -v '#'|awk '{print $2}')
+ if [ "$REDISPASS" != "" ];then
+ REDISPASS=" -a $REDISPASS"
+ fi
+ CLIEXEC="${ROOT_PATH}/redis/bin/redis-cli -p $REDISPORT$REDISPASS"
+ echo $CLIEXEC
+ ${CLIEXEC}
+}
+
+mw_valkey(){
+ CONF="${ROOT_PATH}/valkey/valkey.conf"
+
+ if [ ! -f "$CONF" ]; then
+ echo -e "not install valkey!"
+ exit 1
+ fi
+
+ REDISPORT=$(cat $CONF |grep port|grep -v '#'|awk '{print $2}')
+ REDISPASS=$(cat $CONF |grep requirepass|grep -v '#'|awk '{print $2}')
+ if [ "$REDISPASS" != "" ];then
+ REDISPASS=" -a $REDISPASS"
+ fi
+ CLIEXEC="${ROOT_PATH}/valkey/bin/valkey-cli -p $REDISPORT$REDISPASS"
+ echo $CLIEXEC
+ ${CLIEXEC}
+}
+
+mw_ssh(){
+ cd ${PANEL_DIR} && python3 panel_tools.py cli 202
+
+ if [[ "$?" == "0" ]]; then
+ POS=$(echo -e "\n${BLUE}└─ 选择登陆终端:${PLAIN}")
+ read -p "${POS}" INPUT
+ SSS=`cd ${PANEL_DIR} && python3 panel_tools.py cli 202 $INPUT`
+ # echo "info:$SSS"
+ # SSS="127.0.0.1|22|root|xx"
+ IFS='|' read -r SERVER_IP SERVER_PORT SERVER_USER SERVER_PASS <<< "$SSS"
+ echo "Attempting SSH connection to $SERVER_USER@$SERVER_IP:$SERVER_PORT"
+ sshpass -p "$SERVER_PASS" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10 -p "$SERVER_PORT" "$SERVER_USER@$SERVER_IP"
+ fi
+}
+
+mw_venv(){
+ cd ${PANEL_DIR} && source bin/activate
+}
+
+mw_clean_lib(){
+ cd ${PANEL_DIR} && rm -rf lib
+ cd ${PANEL_DIR} && rm -rf lib64
+ cd ${PANEL_DIR} && rm -rf bin
+ cd ${PANEL_DIR} && rm -rf include
+}
+
+mw_list(){
+ echo -e "mw default - 显示面板默认信息"
+ echo -e "mw db - 连接MySQL"
+ echo -e "mw pgdb - 连接PostgreSQL"
+ echo -e "mw mongdb - 连接MongoDB"
+ echo -e "mw redis - 连接Redis"
+ echo -e "mw valkey - 连接WalKey"
+ echo -e "mw install - 执行安装脚本"
+ echo -e "mw update - 更新到正式环境最新代码"
+ echo -e "mw update_dev - 更新到测试环境最新代码"
+ echo -e "mw debug - 调式开发面板"
+ echo -e "mw list - 显示命令列表"
+}
+
+mw_default(){
+ cd ${PANEL_DIR}
+ port=7200
+ scheme=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py panel_ssl_type)
+
+ if [ -f ${PANEL_DIR}/data/port.pl ];then
+ port=$(cat ${PANEL_DIR}/data/port.pl)
+ fi
+
+ if [ ! -f ${PANEL_DIR}/data/default.pl ];then
+ echo -e "\033[33mInstall Failed\033[0m"
+ exit 1
+ fi
+
+ password=$(cat ${PANEL_DIR}/data/default.pl)
+
+ admin_path=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py admin_path)
+ if [ "$address" == "" ];then
+ v4=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py getServerIp 4)
+ v6=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py getServerIp 6)
+
+ if [ "$v4" != "" ] && [ "$v6" != "" ]; then
+
+ if [ ! -f ${PANEL_DIR}/data/ipv6.pl ];then
+ echo 'True' > ${PANEL_DIR}/data/ipv6.pl
+ mw_stop
+ mw_start
+ fi
+
+ address="MW-PANEL-URL-IPV4: ${scheme}://$v4:$port$admin_path \nMW-PANEL-URL-IPV6: ${scheme}://[$v6]:$port$admin_path"
+ elif [ "$v4" != "" ]; then
+ address="MW-PANEL-URL: ${scheme}://$v4:$port$admin_path"
+ elif [ "$v6" != "" ]; then
+
+ if [ ! -f ${PANEL_DIR}/data/ipv6.pl ];then
+ # Need to restart ipv6 to take effect
+ echo 'True' > ${PANEL_DIR}/data/ipv6.pl
+ mw_stop
+ mw_start
+ fi
+ address="MW-PANEL-URL: ${scheme}://[$v6]:$port$admin_path"
+ else
+ address="MW-PANEL-URL: ${scheme}://you-network-ip:$port$admin_path"
+ fi
+ else
+ address="MW-PANEL-URL: ${scheme}://$address:$port$admin_path"
+ fi
+
+ # bind domain check
+ panel_bind_domain=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py panel_bind_domain)
+ if [ "$panel_bind_domain" != "" ];then
+ address="MW-PANEL-URL: ${scheme}://$panel_bind_domain:$port$admin_path\n${address}"
+ fi
+
+ show_panel_ip="$port|"
+ echo -e "=================================================================="
+ echo -e "\033[32mMW-PANEL DEFAULT INFO!\033[0m"
+ echo -e "=================================================================="
+ echo -e "$address"
+ echo -e `cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py username`
+ echo -e `cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py password`
+ echo -e "\033[33mWarning:\033[0m"
+ echo -e "\033[33mIf you cannot access the panel. \033[0m"
+ echo -e "\033[33mrelease the following port (${show_panel_ip}80|443|22) in the security group.\033[0m"
+ echo -e "=================================================================="
+}
+
+case "$1" in
+ 'start') mw_start;;
+ 'stop') mw_stop;;
+ 'reload') mw_reload;;
+ 'restart')
+ mw_stop
+ mw_start;;
+ 'restart_panel')
+ mw_stop_panel
+ mw_start_panel;;
+ 'restart_task')
+ mw_stop_task
+ mw_start_task;;
+ 'status') mw_status;;
+ 'logs') error_logs;;
+ 'close') mw_close;;
+ 'open') mw_open;;
+ 'install') mw_install;;
+ 'update') mw_update;;
+ 'dev') mw_update_dev;;
+ 'update_dev') mw_update_dev;;
+ 'install_app') mw_install_app;;
+ 'close_admin_path') mw_close_admin_path;;
+ 'unbind_domain') mw_unbind_domain;;
+ 'unbind_ssl') mw_unbind_domain;;
+ 'debug') mw_debug;;
+ 'mirror') mw_mirror;;
+ 'db') mw_connect_mysql;;
+ 'pgdb') mw_connect_pgdb;;
+ 'redis') mw_redis;;
+ 'valkey')mw_valkey;;
+ 'mongodb') mw_mongodb;;
+ 'ssh') mw_ssh;;
+ 'venv') mw_update_venv;;
+ 'clean_lib') mw_clean_lib;;
+ 'list') mw_list;;
+ 'default') mw_default;;
+ *)
+ cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py cli $1
+ ;;
+esac
diff --git a/scripts/init.d/service.sh b/scripts/init.d/service.sh
new file mode 100644
index 000000000..fea074dbe
--- /dev/null
+++ b/scripts/init.d/service.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+cd /www/server/mdserver-web
+if [ -f bin/activate ];then
+ source bin/activate
+fi
\ No newline at end of file
diff --git a/scripts/install.sh b/scripts/install.sh
new file mode 100755
index 000000000..0561aff95
--- /dev/null
+++ b/scripts/install.sh
@@ -0,0 +1,299 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+RED='\033[31m'
+GREEN='\033[32m'
+YELLOW='\033[33m'
+BLUE='\033[34m'
+PLAIN='\033[0m'
+BOLD='\033[1m'
+SUCCESS='[\033[32mOK\033[0m]'
+COMPLETE='[\033[32mDONE\033[0m]'
+WARN='[\033[33mWARN\033[0m]'
+ERROR='[\033[31mERROR\033[0m]'
+WORKING='[\033[34m*\033[0m]'
+REPO_OWNER="AndyXeCM"
+REPO_NAME="PowerLinux"
+REPO_BRANCH="master"
+
+
+# LANG=en_US.UTF-8
+is64bit=`getconf LONG_BIT`
+
+if [ -f /www/server/mdserver-web/tools.py ];then
+ echo -e "检测到旧版代码,为避免翻车先停一下~"
+ echo -e "如确认继续,请先执行: rm -rf /www/server/mdserver-web"
+ echo -e "处理完再回来开装吧!需要帮忙也可以随时再问我。"
+ exit 0
+fi
+
+LOG_FILE=/var/log/mw-install.log
+{
+
+HTTP_PREFIX="https://"
+LOCAL_ADDR=common
+cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+fi
+
+if [ "$LOCAL_ADDR" != "common" ];then
+ declare -A PROXY_URL
+ PROXY_URL["gh_proxy_com"]="https://gh-proxy.com/"
+ PROXY_URL["github_do"]="https://github.do/"
+ PROXY_URL["gh_llkk_cc"]="https://gh.llkk.cc/https://"
+ PROXY_URL["gh_felicity_ac_cn"]="https://gh.felicity.ac.cn/https://"
+ PROXY_URL["ghfast_top"]="https://ghfast.top/"
+ PROXY_URL["ghproxy_net"]="https://ghproxy.net/"
+ PROXY_URL["gh_927223_xyz"]="https://gh.927223.xyz/https://"
+ PROXY_URL["gh_proxy_net"]="https://gh-proxy.net/"
+
+ PROXY_URL["source"]="https://"
+
+
+ SOURCE_LIST_KEY_SORT_TMP=$(echo ${!PROXY_URL[@]} | tr ' ' '\n' | sort -n)
+ SOURCE_LIST_KEY=(${SOURCE_LIST_KEY_SORT_TMP//'\n'/})
+ SOURCE_LIST_LEN=${#PROXY_URL[*]}
+fi
+
+
+function AutoSizeStr(){
+ NAME_STR=$1
+ NAME_NUM=$2
+
+ NAME_STR_LEN=`echo "$NAME_STR" | wc -L`
+ NAME_NUM_LEN=`echo "$NAME_NUM" | wc -L`
+
+ fix_len=35
+ remaining_len=`expr $fix_len - $NAME_STR_LEN - $NAME_NUM_LEN`
+ FIX_SPACE=' '
+ for ((ass_i=1;ass_i<=$remaining_len;ass_i++))
+ do
+ FIX_SPACE="$FIX_SPACE "
+ done
+ echo -e " ❖ ${1}${FIX_SPACE}${2})"
+}
+
+function ChooseProxyURL(){
+ clear
+ echo -e '+---------------------------------------------------+'
+ echo -e '| |'
+ echo -e '| ============================================= |'
+ echo -e '| |'
+ echo -e '| 欢迎使用 Linux 一键安装面板源码,马上开始吧! |'
+ echo -e '| |'
+ echo -e '| ============================================= |'
+ echo -e '| |'
+ echo -e '+---------------------------------------------------+'
+ echo -e ''
+ echo -e '#####################################################'
+ echo -e ''
+ echo -e ' 提供以下国内代理地址可供选择: '
+ echo -e ''
+ echo -e '#####################################################'
+ echo -e ''
+ cm_i=0
+ for V in ${SOURCE_LIST_KEY[@]}; do
+ num=`expr $cm_i + 1`
+ AutoSizeStr "${V}" "$num"
+ cm_i=`expr $cm_i + 1`
+ done
+ echo -e ''
+ echo -e '#####################################################'
+ echo -e ''
+ echo -e " 系统时间 ${BLUE}$(date "+%Y-%m-%d %H:%M:%S")${PLAIN}"
+ echo -e ''
+ echo -e '#####################################################'
+ CHOICE_A=$(echo -e "\n${BOLD}└─ 请选择并输入你想使用的代理地址 [ 1-${SOURCE_LIST_LEN} ](回车默认 1):${PLAIN}")
+
+ read -p "${CHOICE_A}" INPUT
+ # echo $INPUT
+ if [ "$INPUT" == "" ];then
+ INPUT=1
+ TMP_INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$TMP_INPUT]}
+ echo -e "\n默认选择[${BLUE}${INPUT_KEY}${PLAIN}],准备开始安装~"
+ fi
+
+ if [ "$INPUT" -lt "0" ];then
+ INPUT=1
+ TMP_INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$TMP_INPUT]}
+ echo -e "\n低于边界啦!已切回[${BLUE}${INPUT_KEY}${PLAIN}]继续安装~"
+ sleep 2s
+ fi
+
+ if [ "$INPUT" -gt "${SOURCE_LIST_LEN}" ];then
+ INPUT=${SOURCE_LIST_LEN}
+ TMP_INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$TMP_INPUT]}
+ echo -e "\n超出边界啦!已切回[${BLUE}${INPUT_KEY}${PLAIN}]继续安装~"
+ sleep 2s
+ fi
+
+ INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$INPUT]}
+ HTTP_PREFIX=${PROXY_URL[$INPUT_KEY]}
+}
+
+if [ "$LOCAL_ADDR" != "common" ];then
+ ChooseProxyURL
+
+ if [ "$HTTP_PREFIX" != "https://" ];then
+ DOMAIN=`echo $HTTP_PREFIX | sed 's|https://||g'`
+ DOMAIN=`echo $DOMAIN | sed 's|/||g'`
+ ping -c 3 $DOMAIN > /dev/null 2>&1
+ if [ "$?" != "0" ];then
+ echo "无效代理地址:${DOMAIN}"
+ exit
+ fi
+ fi
+fi
+
+if [ -f /etc/motd ];then
+ echo "welcome to mdserver-web panel" > /etc/motd
+fi
+
+startTime=`date +%s`
+
+_os=`uname`
+echo "检测到系统: ${_os}"
+
+if [ ${_os} == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eqi "openSUSE" /etc/*-release; then
+ OSNAME='opensuse'
+ zypper refresh
+ zypper install cron wget curl zip unzip
+elif grep -Eqi "FreeBSD" /etc/*-release; then
+ OSNAME='freebsd'
+ pkg install -y wget curl zip unzip unrar rar
+elif grep -Eqi "EulerOS" /etc/*-release || grep -Eqi "openEuler" /etc/*-release; then
+ OSNAME='euler'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "CentOS" /etc/issue || grep -Eqi "CentOS" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "Fedora" /etc/issue || grep -Eqi "Fedora" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "Rocky" /etc/issue || grep -Eqi "Rocky" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "Anolis" /etc/issue || grep -Eqi "Anolis" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eqi "AlmaLinux" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eqi "Amazon Linux" /etc/*-release; then
+ OSNAME='amazon'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "Debian" /etc/issue || grep -Eqi "Debian" /etc/os-release; then
+ OSNAME='debian'
+ # apt update -y
+ apt install -y wget curl zip unzip tar cron
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eqi "Ubuntu" /etc/os-release; then
+ OSNAME='ubuntu'
+ # apt update -y
+ apt install -y wget curl zip unzip tar cron
+elif grep -Eqi "Alpine" /etc/issue || grep -Eqi "Alpine" /etc/*-release; then
+ OSNAME='alpine'
+ apk update
+ apk add devscripts -force-broken-world
+ apk add wget zip unzip tar -force-broken-world
+else
+ OSNAME='unknow'
+fi
+
+if [ "$EUID" -ne 0 ] && [ "$OSNAME" != "macos" ];then
+ echo "需要使用 root 权限运行安装脚本,请用 sudo 或切换到 root 后重试~"
+ exit
+fi
+
+echo "LOCAL:${LOCAL_ADDR}"
+echo "OSNAME:${OSNAME}"
+
+if [ $OSNAME != "macos" ];then
+ if id www &> /dev/null ;then
+ echo ""
+ else
+ groupadd www
+ useradd -g www -s /usr/sbin/nologin www
+ fi
+
+ mkdir -p /www/server
+ mkdir -p /www/wwwroot
+ mkdir -p /www/wwwlogs
+ mkdir -p /www/backup/database
+ mkdir -p /www/backup/site
+
+ if [ ! -d /www/server/mdserver-web ];then
+ echo "开始下载面板源码(${REPO_OWNER}/${REPO_NAME},分支 ${REPO_BRANCH})..."
+ curl --insecure -sSLo /tmp/master.tar.gz ${HTTP_PREFIX}github.com/${REPO_OWNER}/${REPO_NAME}/archive/refs/heads/${REPO_BRANCH}.tar.gz
+ TARBALL_DIR=$(tar -tf /tmp/master.tar.gz | head -1 | cut -d/ -f1)
+ cd /tmp && tar -zxvf /tmp/master.tar.gz
+ mv -f /tmp/${TARBALL_DIR} /www/server/mdserver-web
+ rm -rf /tmp/master.tar.gz
+ rm -rf /tmp/${TARBALL_DIR}
+ fi
+
+ # install acme.sh
+ if [ ! -d /root/.acme.sh ];then
+ if [ "$LOCAL_ADDR" != "common" ];then
+ curl --insecure -sSLo /tmp/acme.sh-master.tar.gz ${HTTP_PREFIX}github.com/acmesh-official/acme.sh/archive/refs/heads/master.tar.gz
+ tar xvzf /tmp/acme.sh-master.tar.gz -C /tmp
+ cd /tmp/acme.sh-master
+ bash acme.sh install
+ else
+ curl -fsSL https://get.acme.sh | bash
+ fi
+ fi
+fi
+
+echo "use system version: ${OSNAME}"
+if [ "${OSNAME}" == "macos" ];then
+ curl --insecure -fsSL ${HTTP_PREFIX}raw.githubusercontent.com/${REPO_OWNER}/${REPO_NAME}/refs/heads/${REPO_BRANCH}/scripts/install/macos.sh | bash
+else
+ cd /www/server/mdserver-web && bash scripts/install/${OSNAME}.sh
+fi
+
+if [ "${OSNAME}" == "macos" ];then
+ echo "macos end"
+ exit 0
+fi
+
+cd /www/server/mdserver-web && bash cli.sh start
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [ ! -f /etc/rc.d/init.d/mw ];
+do
+ echo -e ".\c"
+ sleep 1
+ let n+=1
+ if [ $n -gt 20 ];then
+ echo -e "start mw fail"
+ exit 1
+ fi
+done
+
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw stop
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw start
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw default
+
+sleep 2
+if [ ! -e /usr/bin/mw ]; then
+ if [ -f /etc/rc.d/init.d/mw ];then
+ ln -s /etc/rc.d/init.d/mw /usr/bin/mw
+ fi
+fi
+
+endTime=`date +%s`
+((outTime=(${endTime}-${startTime})/60))
+echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
+
+} 1> >(tee $LOG_FILE) 2>&1
+
+echo -e "\n安装完成啦!如有报错,请带上日志 mw-install.log 来找我们~"
+echo "搞定!如果出现问题,请携带安装日志 mw-install.log 反馈哦。"
diff --git a/scripts/install/alma.sh b/scripts/install/alma.sh
new file mode 100755
index 000000000..016409759
--- /dev/null
+++ b/scripts/install/alma.sh
@@ -0,0 +1,129 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=C.UTF-8
+
+
+
+setenforce 0
+sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
+
+dnf install -y wget lsof
+dnf install -y rar unrar
+dnf install -y python3-devel
+dnf install -y python-devel
+dnf install -y crontabs
+dnf install -y mysql-devel
+dnf install -y pv
+yum install -y bzip2
+yum install -y bzip2-devel
+yum install -y ncurses-compat-libs
+yum install -y numactl
+yum install -y sshpass
+
+SSH_PORT=`netstat -ntpl|grep sshd|grep -v grep | sed -n "1,1p" | awk '{print $4}' | awk -F : '{print $2}'`
+if [ "$SSH_PORT" == "" ];then
+ SSH_PORT_LINE=`cat /etc/ssh/sshd_config | grep "Port \d*" | tail -1`
+ SSH_PORT=${SSH_PORT_LINE/"Port "/""}
+fi
+echo "SSH PORT:${SSH_PORT}"
+
+# if [ -f /usr/sbin/iptables ];then
+
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 888 -j ACCEPT
+# service iptables save
+
+# iptables_status=`service iptables status | grep 'not running'`
+# if [ "${iptables_status}" == '' ];then
+# service iptables restart
+# fi
+
+# #安装时不开启
+# service iptables stop
+# fi
+
+
+if [ ! -f /usr/sbin/iptables ];then
+ yum install firewalld -y
+ systemctl enable firewalld
+ systemctl start firewalld
+
+ if [ "$SSH_PORT" != "" ];then
+ firewall-cmd --permanent --zone=public --add-port=${SSH_PORT}/tcp
+ else
+ firewall-cmd --permanent --zone=public --add-port=22/tcp
+ fi
+
+ firewall-cmd --permanent --zone=public --add-port=80/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/udp
+ # firewall-cmd --permanent --zone=public --add-port=888/tcp
+
+ sed -i 's#AllowZoneDrifting=yes#AllowZoneDrifting=no#g' /etc/firewalld/firewalld.conf
+ firewall-cmd --reload
+fi
+
+
+#安装时不开启
+systemctl stop firewalld
+
+dnf upgrade -y
+dnf autoremove -y
+
+dnf groupinstall -y "Development Tools"
+dnf install -y epel-release
+dnf install -y zip unzip
+
+dnf install -y libevent libevent-devel libmcrypt libmcrypt-devel
+dnf install -y wget libicu-devel bzip2-devel gcc libxml2 libxml2-devel libjpeg-devel libpng-devel libwebp libwebp-devel pcre pcre-devel
+dnf install -y lsof net-tools
+dnf install -y ncurses-devel cmake
+
+dnf --enablerepo=crb install -y mysql-devel
+dnf --enablerepo=crb install -y oniguruma oniguruma-devel
+dnf --enablerepo=crb install -y rpcgen
+dnf --enablerepo=crb install -y libzip-devel
+dnf --enablerepo=crb install -y libmemcached-devel
+dnf --enablerepo=crb install -y libtirpc libtirpc-devel
+dnf --enablerepo=crb install -y patchelf
+
+dnf install -y langpacks-zh_CN langpacks-en langpacks-en_GB
+
+
+yum install -y libtirpc libtirpc-devel
+yum install -y rpcgen
+yum install -y openldap openldap-devel
+yum install -y bison re2c cmake
+yum install -y cmake3
+yum install -y autoconf
+yum install -y expect
+yum install -y bc
+
+yum install -y curl curl-devel
+yum install -y zlib zlib-devel
+yum install -y libzip libzip-devel
+yum install -y pcre pcre-devel
+yum install -y icu libicu-devel
+yum install -y freetype freetype-devel
+yum install -y openssl openssl-devel
+yum install -y graphviz libxml2 libxml2-devel
+yum install -y sqlite-devel
+yum install -y oniguruma oniguruma-devel
+yum install -y ImageMagick ImageMagick-devel
+yum install -y libargon2-devel
+
+
+for yumPack in make cmake gcc gcc-c++ flex bison file libtool libtool-libs autoconf kernel-devel patch wget gd gd-devel libxml2 libxml2-devel zlib zlib-devel glib2 glib2-devel tar bzip2 bzip2-devel libevent libevent-devel ncurses ncurses-devel curl curl-devel libcurl libcurl-devel e2fsprogs e2fsprogs-devel libidn libidn-devel vim-minimal gettext gettext-devel ncurses-devel gmp-devel libcap diffutils ca-certificates net-tools psmisc libXpm-devel git-core c-ares-devel libicu-devel libxslt libxslt-devel zip unzip glibc.i686 libstdc++.so.6 cairo-devel bison-devel ncurses-devel libaio-devel perl perl-devel perl-Data-Dumper lsof crontabs expat-devel readline-devel;
+do dnf --enablerepo=crb install -y $yumPack;done
+
+
+# findLD=`cat /etc/ld.so.conf | grep '/usr/local/lib64'`
+# echo "/usr/local/lib64" >> /etc/ld.so.conf
+
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
diff --git a/scripts/install/alpine.sh b/scripts/install/alpine.sh
new file mode 100644
index 000000000..cd1afdd2c
--- /dev/null
+++ b/scripts/install/alpine.sh
@@ -0,0 +1,93 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+
+# for debug
+apk add htop --force-broken-world
+apk add linux-headers --force-broken-world
+# for debug end
+apk add shadow --force-broken-world
+apk add build-base --force-broken-world
+apk add openssl openssl-devel --force-broken-world
+apk add bison re2c make cmake gcc --force-broken-world
+apk add gcc-c++ --force-broken-world
+apk add autoconf --force-broken-world
+apk add python3-pip --force-broken-world
+apk add pcre pcre-devel --force-broken-world
+apk add graphviz libxml2 libxml2-devel --force-broken-world
+apk add curl curl-devel --force-broken-world
+apk add freetype freetype-devel --force-broken-world
+apk add mysql-devel --force-broken-world
+apk add ImageMagick ImageMagick-devel --force-broken-world
+apk add libjpeg-devel libpng-devel --force-broken-world
+apk add libevent-devel --force-broken-world
+apk add libtirpc-devel --force-broken-world
+apk add rpcgen --force-broken-world
+apk add libstdc++6 --force-broken-world
+apk add expect --force-broken-world
+apk add pv --force-broken-world
+apk add bc --force-broken-world
+apk add bzip2 --force-broken-world
+
+apk add libzip libzip-devel --force-broken-world
+apk add unrar rar --force-broken-world
+apk add libmemcached libmemcached-devel --force-broken-world
+
+apk add icu libicu-devel --force-broken-world
+apk add sqlite3 sqlite3-devel --force-broken-world
+apk add oniguruma-devel --force-broken-world
+
+# apk add libmcrypt libmcrypt-devel
+# apk add protobuf
+# apk add zlib-devel
+
+apk add python3-devel --force-broken-world
+apk add python-devel --force-broken-world
+
+apk add libwebp-devel --force-broken-world
+apk add libtomcrypt --force-broken-world
+apk add libtomcrypt-devel --force-broken-world
+
+apk add libXpm-devel --force-broken-world
+apk add freetype2-devel --force-broken-world
+apk add libargon2-devel --force-broken-world
+
+apk add net-tools-deprecated --force-broken-world
+apk add numactl --force-broken-world
+
+# apk add php-config
+
+SSH_PORT=`netstat -ntpl|grep sshd|grep -v grep | sed -n "1,1p" | awk '{print $4}' | awk -F : '{print $2}'`
+if [ "$SSH_PORT" == "" ];then
+ SSH_PORT_LINE=`cat /etc/ssh/sshd_config | grep "Port \d*" | tail -1`
+ SSH_PORT=${SSH_PORT_LINE/"Port "/""}
+fi
+echo "SSH PORT:${SSH_PORT}"
+
+if [ ! -f /usr/sbin/firewalld ];then
+ apk add firewalld --force-broken-world
+ which systemctl && systemctl enable firewalld
+ which systemctl && systemctl start firewalld
+
+ if [ "$SSH_PORT" != "" ];then
+ which firewall-cmd && firewall-cmd --permanent --zone=public --add-port=${SSH_PORT}/tcp
+ else
+ which firewall-cmd && firewall-cmd --permanent --zone=public --add-port=22/tcp
+ fi
+
+ which firewall-cmd && firewall-cmd --permanent --zone=public --add-port=80/tcp
+ which firewall-cmd && firewall-cmd --permanent --zone=public --add-port=443/tcp
+ which firewall-cmd && firewall-cmd --permanent --zone=public --add-port=443/udp
+ # firewall-cmd --permanent --zone=public --add-port=888/tcp
+
+ sed -i 's#AllowZoneDrifting=yes#AllowZoneDrifting=no#g' /etc/firewalld/firewalld.conf
+ which systemctl && firewall-cmd --reload
+ #安装时不开启
+ which systemctl && systemctl stop firewalld
+fi
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
diff --git a/scripts/install/amazon.sh b/scripts/install/amazon.sh
new file mode 100755
index 000000000..9f3a9b5da
--- /dev/null
+++ b/scripts/install/amazon.sh
@@ -0,0 +1,157 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+if [ ! -f /usr/bin/applydeltarpm ];then
+ yum -y provides '*/applydeltarpm'
+ yum -y install deltarpm
+fi
+
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+setenforce 0
+sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
+
+yum install -y wget lsof crontabs
+yum install -y rar unrar
+yum install -y python3-devel
+yum install -y python3-pip
+yum install -y python-devel
+yum install -y curl-devel libmcrypt libmcrypt-devel
+yum install -y mysql-devel
+yum install -y expect
+yum install -y pv
+yum install -y bc
+yum install -y bzip2
+yum install -y bzip2-devel
+yum install -y ncurses-compat-libs
+yum install -y numactl
+yum install -y sshpass
+
+SSH_PORT=`netstat -ntpl|grep sshd|grep -v grep | sed -n "1,1p" | awk '{print $4}' | awk -F : '{print $2}'`
+if [ "$SSH_PORT" == "" ];then
+ SSH_PORT_LINE=`cat /etc/ssh/sshd_config | grep "Port \d*" | tail -1`
+ SSH_PORT=${SSH_PORT_LINE/"Port "/""}
+fi
+echo "SSH PORT:${SSH_PORT}"
+
+# if [ -f /usr/sbin/iptables ];then
+
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 888 -j ACCEPT
+# service iptables save
+
+# iptables_status=`service iptables status | grep 'not running'`
+# if [ "${iptables_status}" == '' ];then
+# service iptables restart
+# fi
+
+# #安装时不开启
+# service iptables stop
+# fi
+
+
+if [ ! -f /usr/sbin/firewalld ];then
+ yum install firewalld -y
+ systemctl enable firewalld
+ #取消服务锁定
+ systemctl unmask firewalld
+ systemctl start firewalld
+
+ if [ "$SSH_PORT" != "" ];then
+ firewall-cmd --permanent --zone=public --add-port=${SSH_PORT}/tcp
+ else
+ firewall-cmd --permanent --zone=public --add-port=22/tcp
+ fi
+
+ firewall-cmd --permanent --zone=public --add-port=80/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/udp
+ # firewall-cmd --permanent --zone=public --add-port=888/tcp
+
+ sed -i 's#AllowZoneDrifting=yes#AllowZoneDrifting=no#g' /etc/firewalld/firewalld.conf
+ firewall-cmd --reload
+ #安装时不开启
+ systemctl stop firewalld
+fi
+
+yum groupinstall -y "Development Tools"
+yum install -y epel-release
+
+yum install -y oniguruma oniguruma-devel
+#centos8 stream | use dnf
+if [ "$?" != "0" ];then
+ yum install -y dnf dnf-plugins-core
+ dnf config-manager --set-enabled powertools
+ yum install -y oniguruma oniguruma-devel
+ dnf upgrade -y libmodulemd
+fi
+
+
+yum install -y libtirpc libtirpc-devel
+yum install -y rpcgen
+yum install -y openldap openldap-devel
+yum install -y bison re2c
+yum install -y cmake3
+yum install -y autoconf
+yum install -y make cmake gcc gcc-c++
+
+yum install -y libmemcached libmemcached-devel
+yum install -y curl curl-devel
+yum install -y zlib zlib-devel
+yum install -y libzip libzip-devel
+yum install -y pcre pcre-devel
+yum install -y icu libicu-devel
+yum install -y freetype freetype-devel
+
+yum install -y openssl openssl-devel
+yum install -y libargon2-devel
+
+yum install -y graphviz libxml2 libxml2-devel
+yum install -y sqlite-devel
+yum install -y oniguruma oniguruma-devel
+yum install -y ImageMagick ImageMagick-devel
+
+
+yum install -y libzstd-devel
+yum install -y libevent libevent-devel unzip zip
+yum install -y python-imaging libicu-devel bzip2-devel pcre pcre-devel
+
+yum install -y gd gd-devel
+yum install -y libjpeg-devel libpng-devel libwebp libwebp-devel
+
+yum install -y net-tools
+yum install -y ncurses-devel
+
+
+for yumPack in flex file libtool libtool-libs kernel-devel patch wget glib2 glib2-devel tar bzip2 bzip2-devel libevent libevent-devel ncurses ncurses-devel curl curl-devel libcurl libcurl-devel e2fsprogs e2fsprogs-devel libidn libidn-devel vim-minimal gettext gettext-devel ncurses-devel gmp-devel libcap diffutils ca-certificates net-tools psmisc libXpm-devel git-core c-ares-devel libicu-devel libxslt libxslt-devel zip unzip glibc.i686 libstdc++.so.6 cairo-devel ncurses-devel libaio-devel perl perl-devel perl-Data-Dumper expat-devel readline-devel;
+do yum -y install $yumPack;done
+
+
+if [ "$VERSION_ID" -eq "8" ];then
+ dnf upgrade -y libmodulemd
+fi
+
+if [ "$VERSION_ID" -eq "9" ];then
+ yum install -y patchelf
+ dnf --enablerepo=crb install -y libtirpc-devel
+ dnf --enablerepo=crb install -y libmemcached libmemcached-devel
+ dnf --enablerepo=crb install -y libtool libtool-libs
+ dnf --enablerepo=crb install -y gnutls-devel
+ dnf --enablerepo=crb install -y mysql-devel
+
+ dnf --enablerepo=crb install -y libvpx-devel libXpm-devel libwebp libwebp-devel
+ dnf --enablerepo=crb install -y oniguruma oniguruma-devel
+ dnf --enablerepo=crb install -y libzip libzip-devel
+ # yum remove -y chardet
+fi
+
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+
+
diff --git a/scripts/install/arch.sh b/scripts/install/arch.sh
new file mode 100644
index 000000000..5131bfddf
--- /dev/null
+++ b/scripts/install/arch.sh
@@ -0,0 +1,113 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+
+
+echo y | pacman -Sy yaourt
+
+echo y | pacman -Sy gcc make cmake autoconf
+echo y | pacman -Sy pkg-config
+echo y | pacman -Sy unrar
+echo y | pacman -Sy rar
+echo y | pacman -Sy python3
+echo y | pacman -Sy lsof
+echo y | pacman -Sy python-pip
+echo y | pacman -Sy curl
+echo y | pacman -Sy libevent
+
+echo y | pacman -Sy libzip
+echo y | pacman -Sy libxml2
+echo y | pacman -Sy libtirpc
+echo y | pacman -Sy bzip2
+
+echo y | pacman -Sy cronie
+echo y | pacman -Sy vi
+echo y | pacman -Sy openssl
+echo y | pacman -Sy pcre
+echo y | pacman -Sy libmcrypt
+echo y | pacman -Sy oniguruma
+echo y | pacman -Sy libmemcached
+echo y | pacman -Sy bison re2c
+echo y | pacman -Sy graphviz
+echo y | pacman -Sy mhash
+echo y | pacman -Sy ncurses
+echo y | pacman -Sy sqlite
+echo y | pacman -Sy libtool
+echo y | pacman -Sy imagemagick
+echo y | pacman -Sy mariadb-clients
+echo y | pacman -Sy rpcsvc-proto
+echo y | pacman -Sy lemon
+echo y | pacman -Sy which
+echo y | pacman -Sy expect
+echo y | pacman -Sy pv
+echo y | pacman -Sy bc
+
+
+## gd start
+echo y | pacman -Sy gd
+# echo y | pacman -Sy libgd
+echo y | pacman -Sy libjpeg
+echo y | pacman -Sy libpng
+echo y | pacman -Sy libvpx
+echo y | pacman -Sy libwebp
+echo y | pacman -Sy libxpm
+echo y | pacman -Syu freetype2
+echo y | pacman -Syu sshpass
+## gd end
+
+echo y | pacman -Syu icu
+
+hwclock --systohc
+
+SSH_PORT=`netstat -ntpl|grep sshd|grep -v grep | sed -n "1,1p" | awk '{print $4}' | awk -F : '{print $2}'`
+if [ "$SSH_PORT" == "" ];then
+ SSH_PORT_LINE=`cat /etc/ssh/sshd_config | grep "Port \d*" | tail -1`
+ SSH_PORT=${SSH_PORT_LINE/"Port "/""}
+fi
+echo "SSH PORT:${SSH_PORT}"
+
+# if [ -f /usr/sbin/iptables ];then
+
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 888 -j ACCEPT
+# service iptables save
+
+# iptables_status=`service iptables status | grep 'not running'`
+# if [ "${iptables_status}" == '' ];then
+# service iptables restart
+# fi
+
+# #安装时不开启
+# service iptables stop
+# fi
+
+
+if [ ! -f /usr/sbin/firewalld ];then
+ echo y | pacman -Sy firewalld
+ systemctl enable firewalld
+ systemctl start firewalld
+
+ if [ "$SSH_PORT" != "" ];then
+ firewall-cmd --permanent --zone=public --add-port=${SSH_PORT}/tcp
+ else
+ firewall-cmd --permanent --zone=public --add-port=22/tcp
+ fi
+
+ firewall-cmd --permanent --zone=public --add-port=80/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/udp
+ # firewall-cmd --permanent --zone=public --add-port=888/tcp
+
+ sed -i 's#AllowZoneDrifting=yes#AllowZoneDrifting=no#g' /etc/firewalld/firewalld.conf
+ firewall-cmd --reload
+ #安装时不开启
+ systemctl stop firewalld
+fi
+
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
diff --git a/scripts/install/centos.sh b/scripts/install/centos.sh
new file mode 100755
index 000000000..12587a87c
--- /dev/null
+++ b/scripts/install/centos.sh
@@ -0,0 +1,161 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+if [ ! -f /usr/bin/applydeltarpm ];then
+ yum -y provides '*/applydeltarpm'
+ yum -y install deltarpm
+fi
+
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+setenforce 0
+sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
+
+SSH_PORT=`netstat -ntpl|grep sshd|grep -v grep | sed -n "1,1p" | awk '{print $4}' | awk -F : '{print $2}'`
+if [ "$SSH_PORT" == "" ];then
+ SSH_PORT_LINE=`cat /etc/ssh/sshd_config | grep "Port \d*" | tail -1`
+ SSH_PORT=${SSH_PORT_LINE/"Port "/""}
+fi
+echo "SSH PORT:${SSH_PORT}"
+
+yum install -y wget lsof crontabs
+yum install -y rar unrar
+yum install -y python3-devel
+yum install -y python3-pip
+yum install -y python-devel
+yum install -y vixie-cron
+yum install -y curl-devel
+yum install -y libmcrypt
+yum install -y libmcrypt-devel
+yum install -y mysql-devel
+yum install -y expect
+yum install -y pv
+yum install -y bc
+yum install -y bzip2
+yum install -y bzip2-devel
+yum install -y ncurses-compat-libs
+yum install -y numactl
+yum install -y sshpass
+
+# if [ -f /usr/sbin/iptables ];then
+
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 888 -j ACCEPT
+# service iptables save
+
+# iptables_status=`service iptables status | grep 'not running'`
+# if [ "${iptables_status}" == '' ];then
+# service iptables restart
+# fi
+
+# #安装时不开启
+# service iptables stop
+# fi
+
+
+if [ ! -f /usr/sbin/firewalld ];then
+ yum install firewalld -y
+ systemctl enable firewalld
+ #取消服务锁定
+ systemctl unmask firewalld
+ systemctl start firewalld
+
+ if [ "$SSH_PORT" != "" ];then
+ firewall-cmd --permanent --zone=public --add-port=${SSH_PORT}/tcp
+ else
+ firewall-cmd --permanent --zone=public --add-port=22/tcp
+ fi
+
+ firewall-cmd --permanent --zone=public --add-port=80/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/udp
+ # firewall-cmd --permanent --zone=public --add-port=888/tcp
+
+
+ sed -i 's#AllowZoneDrifting=yes#AllowZoneDrifting=no#g' /etc/firewalld/firewalld.conf
+ firewall-cmd --reload
+ #安装时不开启
+ systemctl stop firewalld
+fi
+
+yum groupinstall -y "Development Tools"
+yum install -y epel-release
+
+yum install -y oniguruma oniguruma-devel
+#centos8 stream | use dnf
+if [ "$?" != "0" ];then
+ yum install -y dnf dnf-plugins-core
+ dnf config-manager --set-enabled powertools
+ yum install -y oniguruma oniguruma-devel
+ dnf upgrade -y libmodulemd
+fi
+
+
+yum install -y libtirpc libtirpc-devel
+yum install -y rpcgen
+yum install -y openldap openldap-devel
+yum install -y bison re2c
+yum install -y cmake3
+yum install -y autoconf
+yum install -y make cmake gcc gcc-c++
+
+yum install -y libmemcached libmemcached-devel
+yum install -y curl curl-devel
+yum install -y zlib zlib-devel
+yum install -y libzip libzip-devel
+yum install -y pcre pcre-devel
+yum install -y icu libicu-devel
+yum install -y freetype freetype-devel
+yum install -y openssl openssl-devel
+yum install -y libxml2 libxml2-devel
+yum install -y graphviz
+yum install -y sqlite-devel
+yum install -y oniguruma oniguruma-devel
+yum install -y ImageMagick ImageMagick-devel
+
+
+yum install -y libzstd-devel
+yum install -y libevent libevent-devel unzip zip
+# yum install -y python-imaging
+yum install -y libicu-devel bzip2-devel pcre pcre-devel
+
+yum install -y gd gd-devel
+yum install -y libwebp-tools
+yum install -y libjpeg-devel libpng-devel libwebp libwebp-devel
+yum install -y net-tools
+yum install -y ncurses-devel
+yum install -y libXpm-devel
+yum install -y libargon2-devel
+
+
+for yumPack in flex file libtool libtool-libs kernel-devel patch wget glib2 glib2-devel tar bzip2 bzip2-devel libevent libevent-devel ncurses ncurses-devel curl curl-devel libcurl libcurl-devel e2fsprogs e2fsprogs-devel libidn libidn-devel vim-minimal gettext gettext-devel ncurses-devel gmp-devel libcap diffutils ca-certificates net-tools psmisc git-core c-ares-devel libicu-devel libxslt libxslt-devel zip unzip glibc.i686 libstdc++.so.6 cairo-devel ncurses-devel libaio-devel perl perl-devel perl-Data-Dumper expat-devel readline-devel;
+do yum -y install $yumPack;done
+
+
+if [ "$VERSION_ID" -eq "8" ];then
+ dnf upgrade -y libmodulemd
+fi
+
+if [ "$VERSION_ID" -eq "9" ];then
+ yum install -y patchelf
+ dnf --enablerepo=crb install -y libtirpc-devel
+ dnf --enablerepo=crb install -y libmemcached libmemcached-devel
+ dnf --enablerepo=crb install -y libtool libtool-libs
+ dnf --enablerepo=crb install -y gnutls-devel
+ dnf --enablerepo=crb install -y mysql-devel
+
+ dnf --enablerepo=crb install -y libvpx-devel libXpm-devel libwebp libwebp-devel
+ dnf --enablerepo=crb install -y libjpeg-devel libpng-devel
+ dnf --enablerepo=crb install -y oniguruma oniguruma-devel
+ dnf --enablerepo=crb install -y libzip libzip-devel
+ # yum remove -y chardet
+fi
+
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
diff --git a/scripts/install/debian.sh b/scripts/install/debian.sh
new file mode 100644
index 000000000..ad2081da6
--- /dev/null
+++ b/scripts/install/debian.sh
@@ -0,0 +1,290 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+export LANG=en_US.UTF-8
+export DEBIAN_FRONTEND=noninteractive
+
+function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }
+function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; }
+function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; }
+function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; }
+
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
+
+ln -sf /bin/bash /bin/sh
+
+__GET_BIT=`getconf LONG_BIT`
+if [ "$__GET_BIT" == "32" ];then
+ # install rust | 32bit need
+ # curl https://sh.rustup.rs -sSf | sh
+ apt install -y rustc
+fi
+
+if [ "$VERSION_ID" == "10" ];then
+ apt install -y rustc
+fi
+
+
+# synchronize server
+apt install chrony -y
+
+# synchronize time first
+apt install ntpdate -y
+# NTPHOST='time.nist.gov'
+# if [ ! -z "$cn" ];then
+# NTPHOST='ntp1.aliyun.com'
+# fi
+# ntpdate ntp1.aliyun.com | logger -t NTP
+# ntpdate $NTPHOST | logger -t NTP
+
+apt install -y net-tools
+
+SSH_PORT=`netstat -ntpl|grep sshd|grep -v grep | sed -n "1,1p" | awk '{print $4}' | awk -F : '{print $2}'`
+if [ "$SSH_PORT" == "" ];then
+ SSH_PORT_LINE=`cat /etc/ssh/sshd_config | grep "Port \d*" | tail -1`
+ SSH_PORT=${SSH_PORT_LINE/"Port "/""}
+fi
+echo "SSH PORT:${SSH_PORT}"
+
+
+
+# choose lang cmd
+# dpkg-reconfigure --frontend=noninteractive locales
+# dpkg-reconfigure locales
+if [ ! -f /usr/sbin/locale-gen ];then
+ apt install -y locales
+ sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen
+ locale-gen en_US.UTF-8
+ locale-gen zh_CN.UTF-8
+ localedef -v -c -i en_US -f UTF-8 en_US.UTF-8 > /dev/null 2>&1
+ update-locale LANG=en_US.UTF-8
+else
+ locale-gen en_US.UTF-8
+ locale-gen zh_CN.UTF-8
+ localedef -v -c -i en_US -f UTF-8 en_US.UTF-8 > /dev/null 2>&1
+fi
+
+apt update -y
+apt autoremove -y
+
+apt install -y wget curl lsof unzip tar cron expect locate lrzsz
+apt install -y xz-utils
+apt install -y rar
+apt install -y unrar
+apt install -y pv
+apt install -y bc
+apt install -y python3-pip
+apt install -y python3-dev
+apt install -y python3-venv
+apt install -y libncurses5
+apt install -y libncurses5-dev
+apt install -y bzip2
+apt install -y p7zip-full
+
+apt install -y libnuma1
+apt install -y libaio1
+apt install -y libaio-dev
+apt install -y libmecab2
+apt install -y libmm-dev
+
+apt install -y dnsutils
+apt install -y apache2-utils
+apt install -y numactl
+apt install -y xxd
+apt install -y sshpass
+
+P_VER=`python3 -V | awk '{print $2}'`
+if version_ge "$P_VER" "3.11.0" ;then
+ echo -e "\e[1;31mapt install python3.12-venv\e[0m"
+ apt install -y python3.12-venv
+fi
+
+
+if [ -f /usr/sbin/ufw ];then
+ # look
+ # ufw status
+ ufw enable
+ if [ "$SSH_PORT" != "" ];then
+ ufw allow $SSH_PORT/tcp
+ else
+ ufw allow 22/tcp
+ fi
+
+ ufw allow 80/tcp
+ ufw allow 443/tcp
+ ufw allow 443/udp
+ # ufw allow 888/tcp
+fi
+
+if [ ! -f /usr/sbin/ufw ];then
+ # look
+ # firewall-cmd --list-all
+ # apt remove -y firewalld
+
+ apt install -y firewalld
+ systemctl enable firewalld
+ #取消服务锁定
+ systemctl unmask firewalld
+
+
+ if [ "$SSH_PORT" != "" ];then
+ firewall-cmd --permanent --zone=public --add-port=${SSH_PORT}/tcp
+ else
+ firewall-cmd --permanent --zone=public --add-port=22/tcp
+ fi
+ firewall-cmd --permanent --zone=public --add-port=80/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/udp
+ # firewall-cmd --permanent --zone=public --add-port=888/tcp
+
+ systemctl start firewalld
+
+ # fix:debian10 firewalld faq
+ # https://kawsing.gitbook.io/opensystem/andoid-shou-ji/untitled/fang-huo-qiang#debian-10-firewalld-0.6.3-error-commandfailed-usrsbinip6tablesrestorewn-failed-ip6tablesrestore-v1.8
+ sed -i 's#IndividualCalls=no#IndividualCalls=yes#g' /etc/firewalld/firewalld.conf
+
+ firewall-cmd --reload
+fi
+
+#fix zlib1g-dev fail
+echo -e "\e[0;32mfix zlib1g-dev install question start\e[0m"
+Install_TmpFile=/tmp/debian-fix-zlib1g-dev.txt
+apt install -y zlib1g-dev > ${Install_TmpFile}
+if [ "$?" != "0" ];then
+ ZLIB1G_BASE_VER=$(cat ${Install_TmpFile} | grep zlib1g | awk -F "=" '{print $2}' | awk -F ")" '{print $1}')
+ ZLIB1G_BASE_VER=`echo ${ZLIB1G_BASE_VER} | sed "s/^[ \s]\{1,\}//g;s/[ \s]\{1,\}$//g"`
+ # echo "1${ZLIB1G_BASE_VER}1"
+ echo -e "\e[1;31mapt install zlib1g=${ZLIB1G_BASE_VER} zlib1g-dev\e[0m"
+ echo "Y" | apt install zlib1g=${ZLIB1G_BASE_VER} zlib1g-dev
+fi
+rm -rf ${Install_TmpFile}
+echo -e "\e[0;32mfix zlib1g-dev install question end\e[0m"
+
+
+#fix libunwind-dev fail
+echo -e "\e[0;32mfix libunwind-dev install question start\e[0m"
+Install_TmpFile=/tmp/debian-fix-libunwind-dev.txt
+apt install -y libunwind-dev > ${Install_TmpFile}
+if [ "$?" != "0" ];then
+ liblzma5_BASE_VER=$(cat ${Install_TmpFile} | grep liblzma-dev | awk -F "=" '{print $2}' | awk -F ")" '{print $1}')
+ liblzma5_BASE_VER=`echo ${liblzma5_BASE_VER} | sed "s/^[ \s]\{1,\}//g;s/[ \s]\{1,\}$//g"`
+ echo -e "\e[1;31mapt install liblzma5=${liblzma5_BASE_VER} libunwind-dev\e[0m"
+ echo "Y" | apt install liblzma5=${liblzma5_BASE_VER} libunwind-dev
+fi
+rm -rf ${Install_TmpFile}
+echo -e "\e[0;32mfix libunwind-dev install question end\e[0m"
+
+
+apt install -y libvpx-dev
+apt install -y libxpm-dev
+apt install -y libwebp-dev
+apt install -y libfreetype6
+apt install -y libfreetype6-dev
+apt install -y libjpeg-dev
+apt install -y libpng-dev
+
+localedef -i en_US -f UTF-8 en_US.UTF-8
+
+if [ "$VERSION_ID" == "9" ];then
+ sed "s/flask==2.0.3/flask==1.1.1/g" -i /www/server/mdserver-web/requirements.txt
+ sed "s/cryptography==3.3.2/cryptography==2.5/g" -i /www/server/mdserver-web/requirements.txt
+ sed "s/configparser==5.2.0/configparser==4.0.2/g" -i /www/server/mdserver-web/requirements.txt
+ sed "s/flask-socketio==5.2.0/flask-socketio==4.2.0/g" -i /www/server/mdserver-web/requirements.txt
+ sed "s/python-engineio==4.3.2/python-engineio==3.9.0/g" -i /www/server/mdserver-web/requirements.txt
+ # pip3 install -r /www/server/mdserver-web/requirements.txt
+fi
+
+apt install -y build-essential
+apt install -y devscripts
+
+apt install -y autoconf
+apt install -y gcc
+apt install -y patchelf
+
+apt install -y libffi-dev
+apt install -y cmake
+apt install -y automake make
+
+apt install -y webp scons
+apt install -y libwebp-dev
+apt install -y lzma lzma-dev
+apt install -y libunwind-dev
+
+apt install -y libpcre3 libpcre3-dev
+apt install -y openssl
+apt install -y libssl-dev
+apt install -y libargon2-dev
+
+apt install -y libmemcached-dev
+apt install -y libsasl2-dev
+apt install -y imagemagick
+apt install -y libmagickcore-dev
+apt install -y libmagickwand-dev
+
+apt install -y libxml2 libxml2-dev libbz2-dev libmcrypt-dev libpspell-dev librecode-dev
+apt install -y libgmp-dev libgmp3-dev libreadline-dev libxpm-dev
+apt install -y libpq-dev
+apt install -y dia
+
+apt install -y pkg-config
+apt install -y zlib1g-dev
+
+apt install -y libevent-dev libldap2-dev
+apt install -y libzip-dev
+apt install -y libicu-dev
+apt install -y libyaml-dev
+
+apt install -y xsltproc
+
+apt install -y libcurl4-openssl-dev
+apt install -y curl libcurl4-gnutls-dev
+
+# https://www.php.net/manual/zh/mysql-xdevapi.installation.php
+apt install -y libprotobuf-dev
+apt install -y protobuf-compiler
+apt install -y libboost-dev
+apt install -y liblz4-tool
+apt install -y zstd
+
+
+# Disabled due to dependency issues
+#apt install --ignore-missing -y autoconf automake cmake curl dia gcc imagemagick libbz2-dev libcurl4-gnutls-dev\
+# libcurl4-openssl-dev libevent-dev libffi-dev libfreetype6 libfreetype6-dev libgmp-dev libgmp3-dev libicu-dev \
+# libjpeg-dev libldap2-dev libmagickwand-dev libmcrypt-dev libmemcached-dev libncurses5-dev \
+# libpcre3 libpcre3-dev libpng-dev libpspell-dev libreadline-dev librecode-dev libsasl2-dev libssl-dev \
+# libunwind-dev libwebp-dev libxml2 libxml2-dev libxpm-dev libzip-dev lzma lzma-dev make net-tools openssl \
+# pkg-config python3-dev scons webp zlib1g-dev
+
+if [ "$VERSION_ID" != "9" ]; then
+ apt install -y libjpeg62-turbo-dev
+fi
+
+#https://blog.csdn.net/qq_36228377/article/details/123154344
+# ln -s /usr/include/x86_64-linux-gnu/curl /usr/include/curl
+if [ ! -d /usr/include/curl ];then
+ SYS_ARCH=`arch`
+ if [ -f /usr/include/x86_64-linux-gnu/curl ];then
+ ln -s /usr/include/x86_64-linux-gnu/curl /usr/include/curl
+ else
+ ln -s /usr/include/${SYS_ARCH}-linux-gnu/curl /usr/include/curl
+ fi
+fi
+
+apt install -y graphviz bison re2c flex libsqlite3-dev libonig-dev perl g++ libtool libxslt1-dev
+apt install -y libmariadb-dev libmariadb-dev-compat
+
+#apt install -y libmysqlclient-dev
+#apt install -y libmariadbclient-dev
+
+
+# find /usr/lib -name "*libaio*" 2>/dev/null
+if [ ! -f /usr/lib/libaio.so.1 ];then
+ if [ -f /usr/lib/x86_64-linux-gnu/libaio.so.1t64 ];then
+ ln -s /usr/lib/x86_64-linux-gnu/libaio.so.1t64 /usr/lib/libaio.so.1
+ fi
+fi
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
\ No newline at end of file
diff --git a/scripts/install/euler.sh b/scripts/install/euler.sh
new file mode 100755
index 000000000..36adf21ee
--- /dev/null
+++ b/scripts/install/euler.sh
@@ -0,0 +1,161 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+if [ ! -f /usr/bin/applydeltarpm ];then
+ yum -y provides '*/applydeltarpm'
+ yum -y install deltarpm
+fi
+
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+setenforce 0
+sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
+
+SSH_PORT=`netstat -ntpl|grep sshd|grep -v grep | sed -n "1,1p" | awk '{print $4}' | awk -F : '{print $2}'`
+if [ "$SSH_PORT" == "" ];then
+ SSH_PORT_LINE=`cat /etc/ssh/sshd_config | grep "Port \d*" | tail -1`
+ SSH_PORT=${SSH_PORT_LINE/"Port "/""}
+fi
+echo "SSH PORT:${SSH_PORT}"
+
+yum install -y wget lsof crontabs
+yum install -y rar unrar
+yum install -y python3-devel
+yum install -y python3-pip
+yum install -y python-devel
+yum install -y vixie-cron
+yum install -y curl-devel
+yum install -y libmcrypt
+yum install -y libmcrypt-devel
+yum install -y mysql-devel
+yum install -y expect
+yum install -y pv
+yum install -y bc
+yum install -y bzip2
+yum install -y bzip2-devel
+yum install -y ncurses-compat-libs
+yum install -y numactl
+yum install -y sshpass
+
+
+# if [ -f /usr/sbin/iptables ];then
+
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 888 -j ACCEPT
+# service iptables save
+
+# iptables_status=`service iptables status | grep 'not running'`
+# if [ "${iptables_status}" == '' ];then
+# service iptables restart
+# fi
+
+# #安装时不开启
+# service iptables stop
+# fi
+
+if [ ! -f /usr/sbin/firewalld ];then
+ yum install firewalld -y
+ systemctl enable firewalld
+ #取消服务锁定
+ systemctl unmask firewalld
+ systemctl start firewalld
+
+ if [ "$SSH_PORT" != "" ];then
+ firewall-cmd --permanent --zone=public --add-port=${SSH_PORT}/tcp
+ else
+ firewall-cmd --permanent --zone=public --add-port=22/tcp
+ fi
+
+ firewall-cmd --permanent --zone=public --add-port=80/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/udp
+ # firewall-cmd --permanent --zone=public --add-port=888/tcp
+
+
+ sed -i 's#AllowZoneDrifting=yes#AllowZoneDrifting=no#g' /etc/firewalld/firewalld.conf
+ firewall-cmd --reload
+ #安装时不开启
+ systemctl stop firewalld
+fi
+
+yum groupinstall -y "Development Tools"
+yum install -y epel-release
+
+yum install -y oniguruma oniguruma-devel
+#centos8 stream | use dnf
+if [ "$?" != "0" ];then
+ yum install -y dnf dnf-plugins-core
+ dnf config-manager --set-enabled powertools
+ yum install -y oniguruma oniguruma-devel
+ dnf upgrade -y libmodulemd
+fi
+
+
+yum install -y libtirpc libtirpc-devel
+yum install -y rpcgen
+yum install -y openldap openldap-devel
+yum install -y bison re2c
+yum install -y cmake3
+yum install -y autoconf
+yum install -y make cmake gcc gcc-c++
+
+yum install -y libmemcached libmemcached-devel
+yum install -y curl curl-devel
+yum install -y zlib zlib-devel
+yum install -y libzip libzip-devel
+yum install -y pcre pcre-devel
+yum install -y icu libicu-devel
+yum install -y freetype freetype-devel
+yum install -y openssl openssl-devel
+yum install -y libxml2 libxml2-devel
+yum install -y graphviz
+yum install -y sqlite-devel
+yum install -y oniguruma oniguruma-devel
+yum install -y ImageMagick ImageMagick-devel
+
+
+yum install -y libzstd-devel
+yum install -y libevent libevent-devel unzip zip
+# yum install -y python-imaging
+yum install -y libicu-devel bzip2-devel pcre pcre-devel
+
+yum install -y gd gd-devel
+yum install -y libjpeg-devel libpng-devel libwebp libwebp-devel
+
+yum install -y net-tools
+yum install -y ncurses-devel
+yum install -y libXpm-devel
+yum install -y libargon2-devel
+
+
+for yumPack in flex file libtool libtool-libs kernel-devel patch wget glib2 glib2-devel tar bzip2 bzip2-devel libevent libevent-devel ncurses ncurses-devel curl curl-devel libcurl libcurl-devel e2fsprogs e2fsprogs-devel libidn libidn-devel vim-minimal gettext gettext-devel ncurses-devel gmp-devel libcap diffutils ca-certificates net-tools psmisc git-core c-ares-devel libicu-devel libxslt libxslt-devel zip unzip glibc.i686 libstdc++.so.6 cairo-devel ncurses-devel libaio-devel perl perl-devel perl-Data-Dumper expat-devel readline-devel;
+do yum -y install $yumPack;done
+
+
+if [ "$VERSION_ID" -eq "8" ];then
+ dnf upgrade -y libmodulemd
+fi
+
+if [ "$VERSION_ID" -eq "9" ];then
+ yum install -y patchelf
+ dnf --enablerepo=crb install -y libtirpc-devel
+ dnf --enablerepo=crb install -y libmemcached libmemcached-devel
+ dnf --enablerepo=crb install -y libtool libtool-libs
+ dnf --enablerepo=crb install -y gnutls-devel
+ dnf --enablerepo=crb install -y mysql-devel
+
+ dnf --enablerepo=crb install -y libvpx-devel libXpm-devel libwebp libwebp-devel
+ dnf --enablerepo=crb install -y libjpeg-devel libpng-devel
+ dnf --enablerepo=crb install -y oniguruma oniguruma-devel
+ dnf --enablerepo=crb install -y libzip libzip-devel
+ # yum remove -y chardet
+fi
+
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
diff --git a/scripts/install/fedora.sh b/scripts/install/fedora.sh
new file mode 100644
index 000000000..235d9b037
--- /dev/null
+++ b/scripts/install/fedora.sh
@@ -0,0 +1,123 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+
+if [ ! -f /usr/bin/applydeltarpm ];then
+ yum -y provides '*/applydeltarpm'
+ yum -y install deltarpm
+fi
+
+
+setenforce 0
+sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
+
+yum install -y wget curl lsof unzip
+yum install -y expect
+yum install -y ncurses-compat-libs
+yum install -y numactl
+yum install -y sshpass
+dnf install crontabs -y
+
+SSH_PORT=`netstat -ntpl|grep sshd|grep -v grep | sed -n "1,1p" | awk '{print $4}' | awk -F : '{print $2}'`
+if [ "$SSH_PORT" == "" ];then
+ SSH_PORT_LINE=`cat /etc/ssh/sshd_config | grep "Port \d*" | tail -1`
+ SSH_PORT=${SSH_PORT_LINE/"Port "/""}
+fi
+echo "SSH PORT:${SSH_PORT}"
+
+# if [ -f /usr/sbin/iptables ];then
+
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 888 -j ACCEPT
+# # iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 7200 -j ACCEPT
+# # iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 3306 -j ACCEPT
+# # iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 30000:40000 -j ACCEPT
+# service iptables save
+
+# iptables_status=`service iptables status | grep 'not running'`
+# if [ "${iptables_status}" == '' ];then
+# service iptables restart
+# fi
+
+# #安装时不开启
+# service iptables stop
+# fi
+
+
+if [ ! -f /usr/sbin/iptables ];then
+ yum install firewalld -y
+ systemctl enable firewalld
+ systemctl start firewalld
+
+ if [ "$SSH_PORT" != "" ];then
+ firewall-cmd --permanent --zone=public --add-port=${SSH_PORT}/tcp
+ else
+ firewall-cmd --permanent --zone=public --add-port=22/tcp
+ fi
+
+ firewall-cmd --permanent --zone=public --add-port=80/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/udp
+ # firewall-cmd --permanent --zone=public --add-port=888/tcp
+ firewall-cmd --reload
+fi
+
+
+#安装时不开启
+systemctl stop firewalld
+
+
+yum groupinstall -y "Development Tools"
+yum install -y epel-release
+
+yum install -y libevent libevent-devel zip libmcrypt libmcrypt-devel
+yum install -y rar unrar
+yum install -y pv
+yum install -y bc
+yum install -y gcc libffi-devel python-devel openssl-devel
+yum install -y libmcrypt libmcrypt-devel python3-devel
+
+yum install -y wget python-devel python-imaging libicu-devel unzip gcc libxml2 libxml2-devel libjpeg-devel libpng-devel libwebp libwebp-devel pcre pcre-devel crontabs
+yum install -y net-tools
+yum install -y ncurses-devel
+yum install -y python-devel
+yum install -y MySQL-python
+yum install -y python3-devel
+yum install -y mysql-devel
+
+yum install -y bzip2
+yum install -y bzip2-devel
+
+yum install -y libtirpc libtirpc-devel
+yum install -y rpcgen
+yum install -y openldap openldap-devel
+yum install -y bison re2c
+yum install -y cmake cmake3
+yum install -y autoconf
+yum install -y libargon2-devel
+
+yum install -y libmemcached libmemcached-devel
+yum install -y curl curl-devel
+yum install -y zlib zlib-devel
+yum install -y libzip libzip-devel
+yum install -y pcre pcre-devel
+yum install -y icu libicu-devel
+yum install -y freetype freetype-devel
+yum install -y openssl openssl-devel
+yum install -y graphviz libxml2 libxml2-devel
+yum install -y sqlite-devel
+yum install -y oniguruma oniguruma-devel
+yum install -y ImageMagick ImageMagick-devel
+
+for yumPack in make cmake gcc gcc-c++ gcc-g77 flex bison file libtool libtool-libs autoconf kernel-devel patch wget libjpeg libjpeg-devel libpng libpng-devel gd gd-devel libxml2 libxml2-devel zlib zlib-devel glib2 glib2-devel tar bzip2 bzip2-devel libevent libevent-devel ncurses ncurses-devel curl curl-devel libcurl libcurl-devel e2fsprogs e2fsprogs-devel krb5 krb5-devel libidn libidn-devel vim-minimal gettext gettext-devel ncurses-devel gmp-devel pspell-devel libcap diffutils ca-certificates net-tools libc-client-devel psmisc libXpm-devel git-core c-ares-devel libicu-devel libxslt libxslt-devel zip unzip glibc.i686 libstdc++.so.6 cairo-devel bison-devel ncurses-devel libaio-devel perl perl-devel perl-Data-Dumper crontabs expat-devel readline-devel;
+do yum -y install $yumPack;done
+
+dnf install libxml2 libxml2-devel -y
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
diff --git a/scripts/install/freebsd.sh b/scripts/install/freebsd.sh
new file mode 100644
index 000000000..a2dbb9530
--- /dev/null
+++ b/scripts/install/freebsd.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+# -- debug start --
+
+# https://www.freebsd.org/where/
+
+# 手动升级到,可解决库找不到的问题。
+# freebsd-update -r 13.2-RELEASE upgrade
+# freebsd-update -r 14-RELEASE upgrade
+
+# pkg install -y python39
+# python3 -m ensurepip
+# pip3 install --upgrade setuptools
+# python3 -m pip install --upgrade pip
+
+# echo "y" | pkg upgrade
+
+# -- debug end --
+
+if grep -Eq "FreeBSD" /etc/*-release && [ ! -f /bin/bash ]; then
+ ln -sf /usr/local/bin/bash /bin/bash
+fi
+
+
+echo "y" | pkg update
+echo "y" | pkg bootstrap -f
+echo "y" | freebsd-update install
+
+pkg install -y python3
+pkg install -y py39-pip
+
+pkg install -y lsof
+pkg install -y rar
+pkg install -y unrar
+pkg install -y vim
+pkg install -y sqlite3
+
+pkg install -y py39-sqlite3
+
+pkg install -y gcc
+pkg install -y autoconf
+pkg install -y make
+pkg install -y gmake
+pkg install -y cmake
+pkg install -y libxslt
+pkg install -y libunwind
+pkg install -y influxpkg-config
+pkg install -y expect
+
+pkg install -y pcre
+pkg install -y libmemcached
+pkg install -y webp
+pkg install -y freetype
+pkg install -y oniguruma
+pkg install -y brotli
+pkg install -y harfbuzz
+pkg install -y libevent
+pkg install -y pidof
+pkg install -y pstree
+pkg install -y pv
+pkg install -y bc
+pkg install -y bzip2
+pkg install -y bzip2-devel
+pkg install -y numactl
+pkg install -y sshpass
+
+# curl https://sh.rustup.rs -sSf | sh
+pkg install -y rust
+
+pkg autoremove -y
+
+
+SSH_PORT_LINE=`cat /etc/ssh/sshd_config | grep -E "Port d*" | tail -1`
+SSH_PORT=${SSH_PORT_LINE/"Port "/""}
+
+echo "SSH PORT:${SSH_PORT}"
+
+# 检测防火墙是否开启
+FW_ENABLE=`cat /etc/rc.conf | grep firewall_enable`
+if [ "$FW_ENABLE" == "" ];then
+ sysrc firewall_enable="YES"
+ sysrc firewall_type="open"
+ sysrc firewall_script="/etc/ipfw.rules"
+ sysrc firewall_logging="YES"
+ sysrc firewall_logif="YES"
+fi
+
+# ipfw list
+service ipfw stop
+
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
diff --git a/scripts/install/macos.sh b/scripts/install/macos.sh
new file mode 100755
index 000000000..42a5c3983
--- /dev/null
+++ b/scripts/install/macos.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+USER=$(who | sed -n "2,1p" |awk '{print $1}')
+DEV="/Users/${USER}/Desktop/mwdev"
+
+
+mkdir -p $DEV
+mkdir -p $DEV/wwwroot
+mkdir -p $DEV/server
+mkdir -p $DEV/wwwlogs
+mkdir -p $DEV/backup/database
+mkdir -p $DEV/backup/site
+
+# install brew
+which brew
+if [ "$?" != "0" ];then
+ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
+ brew install python@2
+ brew install mysql
+fi
+
+brew install pv
+brew install libzip bzip2 gcc openssl re2c cmake
+brew install librdkafka
+brew install coreutils libxml2 xml2
+brew install md5sum libevent pidof bison
+brew install pcre2 libxpm libelf
+brew install automake icu4c libmemcached
+
+if [ ! -d $DEV/server/mdserver-web ]; then
+ wget -O /tmp/master.zip https://codeload.github.com/midoks/mdserver-web/zip/master
+ cd /tmp && unzip /tmp/master.zip
+ mv /tmp/mdserver-web-master $DEV/server/mdserver-web
+ rm -f /tmp/master.zip
+ rm -rf /tmp/mdserver-web-master
+fi
+
+if [ ! -d $DEV/server/lib ]; then
+ cd $DEV/server/mdserver-web/scripts && bash lib.sh
+fi
+
+chmod 755 $DEV/server/mdserver-web/data
+if [ -f $DEV/server/mdserver-web/bin/activate ];then
+ cd $DEV/server/mdserver-web && python3 -m venv $DEV/server/mdserver-web
+ source $DEV/server/mdserver-web/bin/activate
+ pip3 install -r $DEV/server/mdserver-web/requirements.txt
+else
+ cd $DEV/server/mdserver-web && pip3 install -r $DEV/server/mdserver-web/requirements.txt
+fi
+
+
+cd $DEV/server/mdserver-web && ./cli.sh start
+cd $DEV/server/mdserver-web && ./cli.sh stop
+
+sleep 5
+cd $DEV/server/mdserver-web && ./scripts/init.d/mw default
+cd $DEV/server/mdserver-web && ./cli.sh debug
\ No newline at end of file
diff --git a/scripts/install/opensuse.sh b/scripts/install/opensuse.sh
new file mode 100644
index 000000000..0358ed75b
--- /dev/null
+++ b/scripts/install/opensuse.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+# zypper refresh
+
+
+# systemctl stop SuSEfirewall2
+
+# for debug
+zypper install -y htop
+# for debug end
+
+zypper install -y openssl openssl-devel
+zypper install -y bison re2c make cmake gcc
+zypper install -y gcc-c++
+zypper install -y autoconf
+zypper install -y python3-pip
+zypper install -y pcre pcre-devel
+zypper install -y graphviz libxml2 libxml2-devel
+zypper install -y curl curl-devel
+zypper install -y freetype freetype-devel
+zypper install -y mysql-devel
+zypper install -y ImageMagick ImageMagick-devel
+zypper install -y libjpeg-devel libpng-devel
+zypper install -y libevent-devel
+zypper install -y libtirpc-devel
+zypper install -y rpcgen
+zypper install -y libstdc++6
+zypper install -y expect
+zypper install -y pv
+zypper install -y bc
+zypper install -y bzip2
+
+zypper install -y libzip libzip-devel
+zypper install -y unrar rar
+zypper install -y libmemcached libmemcached-devel
+
+zypper install -y icu libicu-devel
+zypper install -y sqlite3 sqlite3-devel
+zypper install -y oniguruma-devel
+
+# zypper install -y libmcrypt libmcrypt-devel
+# zypper install -y protobuf
+# zypper install -y zlib-devel
+
+zypper install -y python3-devel
+zypper install -y python-devel
+
+zypper install -y libwebp-devel
+zypper install -y libtomcrypt
+zypper install -y libtomcrypt-devel
+
+zypper install -y libXpm-devel
+zypper install -y freetype2-devel
+zypper install -y libargon2-devel
+
+zypper install -y net-tools-deprecated
+zypper install -y numactl
+zypper install -y sshpass
+
+# zypper install -y php-config
+
+SSH_PORT=`netstat -ntpl|grep sshd|grep -v grep | sed -n "1,1p" | awk '{print $4}' | awk -F : '{print $2}'`
+if [ "$SSH_PORT" == "" ];then
+ SSH_PORT_LINE=`cat /etc/ssh/sshd_config | grep "Port \d*" | tail -1`
+ SSH_PORT=${SSH_PORT_LINE/"Port "/""}
+fi
+echo "SSH PORT:${SSH_PORT}"
+
+if [ ! -f /usr/sbin/firewalld ];then
+ zypper install -y firewalld
+ systemctl enable firewalld
+ systemctl start firewalld
+
+ if [ "$SSH_PORT" != "" ];then
+ firewall-cmd --permanent --zone=public --add-port=${SSH_PORT}/tcp
+ else
+ firewall-cmd --permanent --zone=public --add-port=22/tcp
+ fi
+
+ firewall-cmd --permanent --zone=public --add-port=80/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/udp
+ # firewall-cmd --permanent --zone=public --add-port=888/tcp
+
+ sed -i 's#AllowZoneDrifting=yes#AllowZoneDrifting=no#g' /etc/firewalld/firewalld.conf
+ firewall-cmd --reload
+ #安装时不开启
+ systemctl stop firewalld
+fi
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
diff --git a/scripts/install/rhel.sh b/scripts/install/rhel.sh
new file mode 100644
index 000000000..dfab7acf2
--- /dev/null
+++ b/scripts/install/rhel.sh
@@ -0,0 +1,235 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+export LANG=en_US.UTF-8
+SYS_ARCH=`arch`
+
+if [ ! -f /usr/bin/applydeltarpm ];then
+ yum -y provides '*/applydeltarpm'
+ yum -y install deltarpm
+fi
+
+setenforce 0
+sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
+
+VERSION_ID=`grep -o -i 'release *[[:digit:]]\+\.*' /etc/redhat-release | grep -o '[[:digit:]]\+' `
+
+
+isStream=$(grep -o -i 'stream' /etc/redhat-release)
+
+cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
+
+yum -y update
+# CentOS Stream
+if [ ! -z "$stream" ];then
+ yum install -y dnf dnf-plugins-core
+ dnf upgrade -y libmodulemd
+fi
+
+PKGMGR='yum'
+if [ $VERSION_ID -ge 8 ];then
+ PKGMGR='dnf'
+fi
+
+# systemctl status chronyd -l
+$PKGMGR install -y chrony
+
+$PKGMGR install -y curl-devel libmcrypt libmcrypt-devel python3-devel
+$PKGMGR install -y net-tools
+$PKGMGR install -y unixODBC-devel
+
+$PKGMGR install -y p7zip
+$PKGMGR install -y p7zip-plugins
+$PKGMGR install -y mmap-devel
+
+$PKGMGR install -y libncurses*
+$PKGMGR install -y sshpass
+
+echo "install remi source"
+if [ "$VERSION_ID" == "9" ];then
+ # dnf upgrade --refresh -y
+ dnf config-manager --set-enabled crb
+
+ echo "install remi start"
+ if [ ! -f /etc/yum.repos.d/remi.repo ];then
+ rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-9.rpm
+ rpm --import http://rpms.famillecollet.com/RPM-GPG-KEY-remi
+ fi
+ echo "install remi end"
+fi
+
+#https need
+if [ ! -d /root/.acme.sh ];then
+ curl https://get.acme.sh | sh
+fi
+
+
+
+SSH_PORT=`netstat -ntpl|grep sshd|grep -v grep | sed -n "1,1p" | awk '{print $4}' | awk -F : '{print $2}'`
+if [ "$SSH_PORT" == "" ];then
+ SSH_PORT_LINE=`cat /etc/ssh/sshd_config | grep "Port \d*" | tail -1`
+ SSH_PORT=${SSH_PORT_LINE/"Port "/""}
+fi
+echo "SSH PORT:${SSH_PORT}"
+
+# redhat , iptables no default
+# echo "iptables wrap start"
+# if [ -f /usr/sbin/iptables ];then
+# $PKGMGR install -y iptables-services
+
+# # iptables -nL --line-number
+
+# echo "iptables start"
+# iptables_status=`systemctl status iptables | grep 'inactive'`
+# if [ "${iptables_status}" != '' ];then
+# service iptables restart
+
+# # iptables -P FORWARD DROP
+# iptables -P INPUT DROP
+# iptables -P OUTPUT ACCEPT
+# iptables -A INPUT -p tcp -s 127.0.0.1 -j ACCEPT
+
+# if [ "$SSH_PORT" != "" ];then
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ${SSH_PORT} -j ACCEPT
+# else
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
+# fi
+
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
+# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 888 -j ACCEPT
+# # iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 7200 -j ACCEPT
+# # iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 3306 -j ACCEPT
+# # iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 30000:40000 -j ACCEPT
+# service iptables save
+# fi
+
+# # 安装时不开启
+# # stop之后清空了所有规则,所以安装是不能stop.
+# # 要在代码修复这个问题,开启时,重新执行一下放行端口。
+# #service iptables stop
+
+# echo "iptables end"
+# fi
+# echo "iptables wrap start"
+
+
+echo "firewall open common port start"
+if [ ! -f /usr/sbin/firewalld ];then
+ $PKGMGR install firewalld -y
+ systemctl enable firewalld
+ #取消服务锁定
+ systemctl unmask firewalld
+ systemctl start firewalld
+
+ sed -i 's#AllowZoneDrifting=yes#AllowZoneDrifting=no#g' /etc/firewalld/firewalld.conf
+ firewall-cmd --reload
+
+ #安装就开启
+ systemctl restart firewalld
+fi
+
+if [ -f /usr/sbin/firewalld ];then
+ # look
+ # firewall-cmd --list-all
+ # systemctl status firewalld
+ # firewall-cmd --zone=public --remove-port=80/tcp --permanent
+
+ if [ "$SSH_PORT" != "" ];then
+ firewall-cmd --permanent --zone=public --add-port=${SSH_PORT}/tcp
+ else
+ firewall-cmd --permanent --zone=public --add-port=22/tcp
+ fi
+ firewall-cmd --permanent --zone=public --add-port=80/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/udp
+ # firewall-cmd --permanent --zone=public --add-port=888/tcp
+ # firewall-cmd --permanent --zone=public --add-port=7200/tcp
+ # firewall-cmd --permanent --zone=public --add-port=3306/tcp
+ # firewall-cmd --permanent --zone=public --add-port=30000-40000/tcp
+
+ firewall-cmd --reload
+
+fi
+echo "firewall open common port end"
+
+$PKGMGR install -y epel-release
+if [ ! -z "$cn" ];then
+ sed -e 's|^metalink=|#metalink=|g' \
+ -e 's|^#baseurl=|baseurl=|g' \
+ -e 's|//download\.fedoraproject\.org/pub|//mirrors.tuna.tsinghua.edu.cn|g' \
+ -e 's|//download\.example/pub|//mirrors.tuna.tsinghua.edu.cn|g' \
+ -i.bak /etc/yum.repos.d/epel*.repo
+fi
+$PKGMGR makecache
+$PKGMGR groupinstall -y "Development Tools"
+
+if [ "$VERSION_ID" -ge "8" ];then
+ # EL8 及以上
+
+ # find repo
+
+ REPO_LIST=(remi appstream baseos epel extras crb powertools)
+ REPOS='--enablerepo='
+ for REPO_VAR in ${REPO_LIST[@]}
+ do
+ if [ -f /etc/yum.repos.d/${REPO_VAR}.repo ];then
+ REPOS="${REPOS},${REPO_VAR}"
+ fi
+ done
+
+ if [ "$REPOS" == "--enablerepo=" ];then
+ # if not find, reset emtpy
+ REPOS=''
+ fi
+
+ REPOS=${REPOS//=,/=}
+ echo "REPOS:${REPOS}"
+
+ # if [ $VERSION_ID -ge 9 ];then
+ # REPOS='--enablerepo=remi,appstream,baseos,epel,extras,crb'
+ # else
+ # REPOS='--enablerepo=remi,appstream,baseos,epel,extras,powertools'
+ # fi
+ for rpms in gcc gcc-c++ lsof autoconf bzip2 bzip2-devel c-ares-devel \
+ ca-certificates cairo-devel cmake crontabs curl curl-devel diffutils e2fsprogs e2fsprogs-devel \
+ expat-devel expect file flex gd gd-devel gettext gettext-devel glib2 glib2-devel glibc.i686 \
+ gmp-devel kernel-devel libXpm-devel libaio-devel libcap libcurl libcurl-devel libevent libevent-devel \
+ libicu-devel libidn libidn-devel libmcrypt libmcrypt-devel libmemcached libmemcached-devel \
+ libpng libpng-devel libstdc++.so.6 libtirpc libtirpc-devel libtool libtool-libs libwebp libwebp-devel \
+ libxml2 libxml2-devel libxslt libxslt-devel libarchive make mysql-devel ncurses ncurses-devel net-tools \
+ oniguruma oniguruma-devel patch pcre pcre-devel perl perl-Data-Dumper perl-devel procps psmisc python3-devel \
+ openssl openssl-devel patchelf libargon2-devel \
+ ImageMagick ImageMagick-devel libyaml-devel \
+ pv bc bind-utils \
+ ncurses-compat-libs numactl \
+ readline-devel rpcgen sqlite-devel rar unrar tar unzip vim-minimal wget zip zlib zlib-devel;
+ do
+ # dnf --enablerepo=remi,appstream,baseos,epel,extras,powertools install -y oniguruma5php-devel
+ dnf $REPOS install -y $rpms;
+ if [ "$?" != "0" ];then
+ dnf install -y $rpms;
+ fi
+ done
+else
+ # CentOS 7
+ for rpms in gcc gcc-c++ lsof autoconf bison bzip2 bzip2-devel c-ares-devel ca-certificates cairo-devel \
+ cmake cmake3 crontabs curl curl-devel diffutils e2fsprogs e2fsprogs-devel expat-devel expect file \
+ flex freetype freetype-devel gd gd-devel gettext gettext-devel git-core glib2 glib2-devel \
+ glibc.i686 gmp-devel graphviz icu kernel-devel libXpm-devel libaio-devel libcap libcurl libcurl-devel \
+ libevent libevent-devel libicu-devel libidn libidn-devel libjpeg-devel libmcrypt libmcrypt-devel \
+ libmemcached libmemcached-devel libpng-devel libstdc++.so.6 libtirpc libtirpc-devel libtool libtool-libs \
+ libwebp libwebp-devel libxml2 libxml2-devel libxslt libxslt-devel libzip libzip-devel libzstd-devel \
+ make mysql-devel ncurses ncurses-devel net-tools oniguruma oniguruma-devel openldap openldap-devel \
+ openssl openssl-devel patch pcre pcre-devel perl perl-Data-Dumper perl-devel psmisc python-devel \
+ pv bc bind-utils \
+ ncurses-compat-libs numactl \
+ python3-devel python3-pip re2c readline-devel rpcgen sqlite-devel tar unzip rar unrar vim-minimal vixie-cron \
+ wget zip zlib zlib-devel ImageMagick ImageMagick-devel libyaml-devel patchelf libargon2-devel;
+ do
+ yum install -y $rpms;
+ done
+fi
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
diff --git a/scripts/install/rocky.sh b/scripts/install/rocky.sh
new file mode 100644
index 000000000..d5b2e15c8
--- /dev/null
+++ b/scripts/install/rocky.sh
@@ -0,0 +1,108 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+
+if [ ! -f /usr/bin/applydeltarpm ];then
+ yum -y provides '*/applydeltarpm'
+ yum -y install deltarpm
+fi
+
+
+setenforce 0
+sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
+
+yum install -y wget lsof
+yum install -y unrar rar
+yum install -y pv
+yum install -y bc
+yum install -y python3-devel
+yum install -y crontabs
+yum install -y expect
+yum install -y curl curl-devel libcurl libcurl-devel
+yum install -y bzip2
+yum install -y bzip2-devel
+yum install -y libzip-devel
+yum install -y re2c
+yum install -y ncurses-compat-libs
+yum install -y numactl
+apt install -y sshpass
+
+if [ -f /usr/sbin/iptables ];then
+
+ iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
+ iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
+ iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
+ # iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 888 -j ACCEPT
+ # iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 7200 -j ACCEPT
+ # iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 3306 -j ACCEPT
+ # iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 30000:40000 -j ACCEPT
+ service iptables save
+
+ iptables_status=`service iptables status | grep 'not running'`
+ if [ "${iptables_status}" == '' ];then
+ service iptables restart
+ fi
+
+ #安装时不开启
+ service iptables stop
+fi
+
+
+if [ ! -f /usr/sbin/iptables ];then
+ yum install firewalld -y
+ systemctl enable firewalld
+ systemctl start firewalld
+
+ firewall-cmd --permanent --zone=public --add-port=22/tcp
+ firewall-cmd --permanent --zone=public --add-port=80/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/udp
+ # firewall-cmd --permanent --zone=public --add-port=888/tcp
+ # firewall-cmd --permanent --zone=public --add-port=7200/tcp
+ # firewall-cmd --permanent --zone=public --add-port=3306/tcp
+ # firewall-cmd --permanent --zone=public --add-port=30000-40000/tcp
+
+
+ sed -i 's#AllowZoneDrifting=yes#AllowZoneDrifting=no#g' /etc/firewalld/firewalld.conf
+ firewall-cmd --reload
+fi
+
+
+#安装时不开启
+systemctl stop firewalld
+
+yum groupinstall -y "Development Tools"
+yum install -y epel-release
+
+yum install -y libevent libevent-devel zip unzip libmcrypt libmcrypt-devel
+yum install -y wget libicu-devel readline-devel zip bzip2 bzip2-devel libxml2 libxml2-devel
+yum install -y libpng libpng-devel libwebp libwebp-devel pcre pcre-devel gd gd-devel zlib zlib-devel gettext gettext-devel
+yum install -y net-tools
+yum install -y ncurses ncurses-devel mysql-devel make cmake
+yum install -y sqlite-devel
+yum install -y libargon2-dev
+
+# python-imaging
+# yum install -y MySQL-python
+
+
+yum install -y perl perl-devel perl-Data-Dumper
+
+for yumPack in gcc gcc-c++ flex file libtool libtool-libs autoconf kernel-devel patch glib2 glib2-devel tar e2fsprogs e2fsprogs-devel libidn libidn-devel vim-minimal gmp-devel libcap diffutils ca-certificates libc-client-devel psmisc libXpm-devel c-ares-devel libxslt libxslt-devel glibc.i686 libstdc++.so.6 cairo-devel libaio-devel expat-devel;
+do dnf --enablerepo=powertools install -y $yumPack;done
+
+yum install -y libtirpc libtirpc-devel
+dnf --enablerepo=powertools install -y boost-locale
+
+dnf --enablerepo=powertools install -y libmemcached libmemcached-devel
+dnf --enablerepo=powertools install -y rpcgen
+dnf --enablerepo=powertools install -y oniguruma oniguruma-devel
+dnf --enablerepo=powertools install -y re2c bison bison-devel
+dnf install -y libjpeg-turbo libjpeg-turbo-devel
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+echo "rocky ok"
diff --git a/scripts/install/ubuntu.sh b/scripts/install/ubuntu.sh
new file mode 100644
index 000000000..38e420b90
--- /dev/null
+++ b/scripts/install/ubuntu.sh
@@ -0,0 +1,239 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+export LANG=en_US.UTF-8
+export DEBIAN_FRONTEND=noninteractive
+
+function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }
+function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; }
+function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; }
+function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; }
+
+
+if grep -Eq "Ubuntu" /etc/*-release; then
+ sudo ln -sf /bin/bash /bin/sh
+ #sudo dpkg-reconfigure dash
+fi
+
+# synchronize server
+# systemctl status chronyd -l
+apt install chrony -y
+apt install ntpdate -y
+
+apt update -y
+apt autoremove -y
+
+apt install -y wget curl unzip
+apt install -y lsof
+apt install -y rar unrar
+apt install -y xz-utils
+apt install -y python3-pip
+apt install -y python3-venv
+apt install -y python3-dev
+apt install -y expect
+apt install -y pv
+apt install -y bc
+apt install -y cron
+apt install -y net-tools
+apt install -y libncurses5
+apt install -y libncurses5-dev
+apt install -y software-properties-common
+apt install -y bzip2
+apt install -y p7zip-full
+
+apt install -y libnuma1
+apt install -y libaio1
+apt install -y libaio-dev
+apt install -y libmecab2
+apt install -y numactl
+apt install -y libaio1t64
+apt install -y libmm-dev
+
+apt install -y dnsutils
+apt install -y numactl
+apt install -y xxd
+
+# https://www.php.net/manual/zh/mysql-xdevapi.installation.php
+apt install -y libprotobuf-dev
+apt install -y protobuf-compiler
+apt install -y libboost-dev
+apt install -y liblz4-tool
+apt install -y zstd
+apt install -y sshpass
+
+P_VER=`python3 -V | awk '{print $2}'`
+if version_ge "$P_VER" "3.11.0" ;then
+ echo -e "\e[1;31mapt install python3.12-venv\e[0m"
+ apt install -y python3.12-venv
+fi
+
+# choose lang cmd
+# dpkg-reconfigure --frontend=noninteractive locales
+# dpkg-reconfigure locales
+if [ ! -f /usr/sbin/locale-gen ];then
+ apt install -y locales
+ sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen
+ locale-gen en_US.UTF-8
+ locale-gen zh_CN.UTF-8
+ localedef -v -c -i en_US -f UTF-8 en_US.UTF-8 > /dev/null 2>&1
+ update-locale LANG=en_US.UTF-8
+else
+ locale-gen en_US.UTF-8
+ locale-gen zh_CN.UTF-8
+ localedef -v -c -i en_US -f UTF-8 en_US.UTF-8 > /dev/null 2>&1
+fi
+
+SSH_PORT=`netstat -ntpl|grep sshd|grep -v grep | sed -n "1,1p" | awk '{print $4}' | awk -F : '{print $2}'`
+if [ "$SSH_PORT" == "" ];then
+ SSH_PORT_LINE=`cat /etc/ssh/sshd_config | grep "Port \d*" | tail -1`
+ SSH_PORT=${SSH_PORT_LINE/"Port "/""}
+fi
+echo "SSH PORT:${SSH_PORT}"
+
+if [ -f /usr/sbin/ufw ];then
+ # look
+ # ufw status
+ echo 'y' | ufw enable
+
+ if [ "$SSH_PORT" != "" ];then
+ ufw allow $SSH_PORT/tcp
+ else
+ ufw allow 22/tcp
+ fi
+
+ ufw allow 80/tcp
+ ufw allow 443/tcp
+ ufw allow 443/udp
+ # ufw allow 888/tcp
+fi
+
+if [ ! -f /usr/sbin/ufw ];then
+ apt install -y firewalld
+ systemctl enable firewalld
+
+
+ if [ "$SSH_PORT" != "" ];then
+ firewall-cmd --permanent --zone=public --add-port=${SSH_PORT}/tcp
+ else
+ firewall-cmd --permanent --zone=public --add-port=22/tcp
+ fi
+
+ firewall-cmd --permanent --zone=public --add-port=80/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/tcp
+ firewall-cmd --permanent --zone=public --add-port=443/udp
+ # firewall-cmd --permanent --zone=public --add-port=888/tcp
+
+ systemctl start firewalld
+
+ # fix:debian10 firewalld faq
+ # https://kawsing.gitbook.io/opensystem/andoid-shou-ji/untitled/fang-huo-qiang#debian-10-firewalld-0.6.3-error-commandfailed-usrsbinip6tablesrestorewn-failed-ip6tablesrestore-v1.8
+ sed -i 's#IndividualCalls=no#IndividualCalls=yes#g' /etc/firewalld/firewalld.conf
+
+ firewall-cmd --reload
+
+ # #安装时不开启
+ # systemctl stop firewalld
+fi
+
+apt install -y devscripts
+apt install -y python3-dev
+apt install -y autoconf
+apt install -y gcc
+apt install -y lrzsz
+
+apt install -y libffi-dev
+apt install -y cmake automake make
+
+apt install -y webp scons
+apt install -y libwebp-dev
+apt install -y lzma lzma-dev
+apt install -y libunwind-dev
+
+apt install -y libpcre3 libpcre3-dev
+apt install -y openssl
+apt install -y libssl-dev
+apt install -y libargon2-dev
+
+apt install -y libmemcached-dev
+apt install -y libsasl2-dev
+apt install -y imagemagick
+apt install -y libmagickcore-dev
+apt install -y libmagickwand-dev
+
+apt install -y libxml2 libxml2-dev libbz2-dev libmcrypt-dev libpspell-dev librecode-dev
+apt install -y libgmp-dev libgmp3-dev libreadline-dev libxpm-dev
+apt install -y libpq-dev
+apt install -y dia
+
+apt install -y pkg-config
+apt install -y zlib1g-dev
+
+apt install -y libjpeg-dev libpng-dev
+apt install -y libfreetype6
+apt install -y libjpeg62-turbo-dev
+apt install -y libfreetype6-dev
+apt install -y libevent-dev libldap2-dev
+
+apt install -y libzip-dev
+apt install -y libicu-dev
+apt install -y libyaml-dev
+
+# mqtt
+apt install -y xsltproc
+
+apt install -y build-essential
+
+apt install -y libcurl4-openssl-dev
+apt install -y libcurl4-nss-dev
+apt install -y curl libcurl4-gnutls-dev
+#https://blog.csdn.net/qq_36228377/article/details/123154344
+# ln -s /usr/include/x86_64-linux-gnu/curl /usr/include/curl
+if [ ! -d /usr/include/curl ];then
+ SYS_ARCH=`arch`
+ if [ -f /usr/include/x86_64-linux-gnu/curl ];then
+ ln -s /usr/include/x86_64-linux-gnu/curl /usr/include/curl
+ else
+ # ln -s /usr/include/aarch64-linux-gnu/curl /usr/include/curl
+ ln -s /usr/include/${SYS_ARCH}-linux-gnu/curl /usr/include/curl
+ fi
+fi
+
+
+apt install -y graphviz bison re2c flex
+apt install -y libsqlite3-dev
+apt install -y libonig-dev
+
+apt install -y perl g++ libtool
+apt install -y libxslt1-dev
+
+apt install -y libmariadb-dev
+#apt install -y libmysqlclient-dev
+apt install -y libmariadb-dev-compat
+#apt install -y libmariadbclient-dev
+
+
+# mysql8.0 在ubuntu22需要的库
+apt install -y patchelf
+
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+if [ "${VERSION_ID}" == "22.04" ];then
+ apt install -y python3-cffi
+ pip3 install -U --force-reinstall --no-binary :all: gevent
+fi
+
+# find /usr/lib -name "*libaio*" 2>/dev/null
+if [ ! -f /usr/lib/libaio.so.1 ];then
+ if [ -f /usr/lib/x86_64-linux-gnu/libaio.so.1t64 ];then
+ ln -s /usr/lib/x86_64-linux-gnu/libaio.so.1t64 /usr/lib/libaio.so.1
+ fi
+fi
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+
+if [ "${VERSION_ID}" == "22.04" ];then
+ apt install -y python3-cffi
+ pip3 install -U --force-reinstall --no-binary :all: gevent
+fi
+
diff --git a/scripts/install/unknow.sh b/scripts/install/unknow.sh
new file mode 100644
index 000000000..5cea93ad0
--- /dev/null
+++ b/scripts/install/unknow.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+echo "unkown server!!!"
\ No newline at end of file
diff --git a/scripts/install_dev.sh b/scripts/install_dev.sh
new file mode 100755
index 000000000..97e16a742
--- /dev/null
+++ b/scripts/install_dev.sh
@@ -0,0 +1,303 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+RED='\033[31m'
+GREEN='\033[32m'
+YELLOW='\033[33m'
+BLUE='\033[34m'
+PLAIN='\033[0m'
+BOLD='\033[1m'
+SUCCESS='[\033[32mOK\033[0m]'
+COMPLETE='[\033[32mDONE\033[0m]'
+WARN='[\033[33mWARN\033[0m]'
+ERROR='[\033[31mERROR\033[0m]'
+WORKING='[\033[34m*\033[0m]'
+
+# LANG=en_US.UTF-8
+is64bit=`getconf LONG_BIT`
+
+
+if [ -f /www/server/mdserver-web/tools.py ];then
+ echo -e "存在旧版代码,不能安装!,已知风险的情况下"
+ echo -e "rm -rf /www/server/mdserver-web"
+ echo -e "可安装!"
+ exit 0
+fi
+
+echo -e "您正在安装的是\033[31mmdserver-web测试版\033[0m,非开发测试用途请使用正式版 install.sh !"
+echo -e "You are installing\033[31m mdserver-web dev version\033[0m, normally use install.sh for production.\n"
+sleep 1
+
+LOG_FILE=/var/log/mw-install.log
+
+{
+
+HTTP_PREFIX="https://"
+LOCAL_ADDR=common
+cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+fi
+
+if [ "$LOCAL_ADDR" != "common" ];then
+ declare -A PROXY_URL
+ PROXY_URL["github_do"]="https://github.do/"
+ PROXY_URL["gh_llkk_cc"]="https://gh.llkk.cc/https://"
+ PROXY_URL["gh_felicity_ac_cn"]="https://gh.felicity.ac.cn/https://"
+ PROXY_URL["ghfast_top"]="https://ghfast.top/"
+ PROXY_URL["ghproxy_net"]="https://ghproxy.net/"
+ PROXY_URL["gh_927223_xyz"]="https://gh.927223.xyz/https://"
+ PROXY_URL["gh_proxy_net"]="https://gh-proxy.net/"
+
+ PROXY_URL["source"]="https://"
+
+
+ SOURCE_LIST_KEY_SORT_TMP=$(echo ${!PROXY_URL[@]} | tr ' ' '\n' | sort -n)
+ SOURCE_LIST_KEY=(${SOURCE_LIST_KEY_SORT_TMP//'\n'/})
+ SOURCE_LIST_LEN=${#PROXY_URL[*]}
+fi
+
+
+function AutoSizeStr(){
+ NAME_STR=$1
+ NAME_NUM=$2
+
+ NAME_STR_LEN=`echo "$NAME_STR" | wc -L`
+ NAME_NUM_LEN=`echo "$NAME_NUM" | wc -L`
+
+ fix_len=35
+ remaining_len=`expr $fix_len - $NAME_STR_LEN - $NAME_NUM_LEN`
+ FIX_SPACE=' '
+ for ((ass_i=1;ass_i<=$remaining_len;ass_i++))
+ do
+ FIX_SPACE="$FIX_SPACE "
+ done
+ echo -e " ❖ ${1}${FIX_SPACE}${2})"
+}
+
+function ChooseProxyURL(){
+ clear
+ echo -e '+---------------------------------------------------+'
+ echo -e '| |'
+ echo -e '| ============================================= |'
+ echo -e '| |'
+ echo -e '| 欢迎使用 Linux 一键安装mdserver-web面板源码 |'
+ echo -e '| |'
+ echo -e '| ============================================= |'
+ echo -e '| |'
+ echo -e '+---------------------------------------------------+'
+ echo -e ''
+ echo -e '#####################################################'
+ echo -e ''
+ echo -e ' 提供以下国内代理地址可供选择: '
+ echo -e ''
+ echo -e '#####################################################'
+ echo -e ''
+ cm_i=0
+ for V in ${SOURCE_LIST_KEY[@]}; do
+ num=`expr $cm_i + 1`
+ AutoSizeStr "${V}" "$num"
+ cm_i=`expr $cm_i + 1`
+ done
+ echo -e ''
+ echo -e '#####################################################'
+ echo -e ''
+ echo -e " 系统时间 ${BLUE}$(date "+%Y-%m-%d %H:%M:%S")${PLAIN}"
+ echo -e ''
+ echo -e '#####################################################'
+ CHOICE_A=$(echo -e "\n${BOLD}└─ 请选择并输入你想使用的代理地址 [ 1-${SOURCE_LIST_LEN} ]:${PLAIN}")
+
+ read -p "${CHOICE_A}" INPUT
+ # echo $INPUT
+ if [ "$INPUT" == "" ];then
+ INPUT=1
+ TMP_INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$TMP_INPUT]}
+ echo -e "\n默认选择[${BLUE}${INPUT_KEY}${PLAIN}]安装!"
+ fi
+
+ if [ "$INPUT" -lt "0" ];then
+ INPUT=1
+ TMP_INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$TMP_INPUT]}
+ echo -e "\n低于边界错误!选择[${BLUE}${INPUT_KEY}${PLAIN}]安装!"
+ sleep 2s
+ fi
+
+ if [ "$INPUT" -gt "${SOURCE_LIST_LEN}" ];then
+ INPUT=${SOURCE_LIST_LEN}
+ TMP_INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$TMP_INPUT]}
+ echo -e "\n超出边界错误!选择[${BLUE}${INPUT_KEY}${PLAIN}]安装!"
+ sleep 2s
+ fi
+
+ INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$INPUT]}
+ HTTP_PREFIX=${PROXY_URL[$INPUT_KEY]}
+}
+
+if [ "$LOCAL_ADDR" != "common" ];then
+ ChooseProxyURL
+
+ if [ "$HTTP_PREFIX" != "https://" ];then
+ DOMAIN=`echo $HTTP_PREFIX | sed 's|https://||g'`
+ DOMAIN=`echo $DOMAIN | sed 's|/||g'`
+ ping -c 3 $DOMAIN > /dev/null 2>&1
+ if [ "$?" != "0" ];then
+ echo "无效代理地址:${DOMAIN}"
+ exit
+ fi
+ fi
+fi
+
+if [ -f /etc/motd ];then
+ echo "welcome to mdserver-web panel" > /etc/motd
+fi
+
+startTime=`date +%s`
+
+_os=`uname`
+echo "use system: ${_os}"
+
+
+if [ ${_os} == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eq "openSUSE" /etc/*-release; then
+ OSNAME='opensuse'
+ zypper refresh
+ zypper install -y wget curl zip unzip unrar rar
+elif grep -Eq "FreeBSD" /etc/*-release; then
+ OSNAME='freebsd'
+ pkg install -y wget curl zip unzip unrar rar
+elif grep -Eqi "EulerOS" /etc/*-release || grep -Eqi "openEuler" /etc/*-release; then
+ OSNAME='euler'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "CentOS" /etc/issue || grep -Eqi "CentOS" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget zip unzip tar
+elif grep -Eqi "Fedora" /etc/issue || grep -Eqi "Fedora" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget zip unzip tar
+elif grep -Eqi "Rocky" /etc/issue || grep -Eqi "Rocky" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget zip unzip
+elif grep -Eqi "Anolis" /etc/issue || grep -Eqi "Anolis" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eqi "AlmaLinux" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget zip unzip tar
+elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eqi "Amazon Linux" /etc/*-release; then
+ OSNAME='amazon'
+ yum install -y wget zip unzip tar
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eqi "Ubuntu" /etc/*-release; then
+ OSNAME='ubuntu'
+ apt install -y wget zip unzip tar
+elif grep -Eqi "Debian" /etc/issue || grep -Eqi "Debian" /etc/*-release; then
+ OSNAME='debian'
+ apt update -y
+ apt install -y devscripts
+ apt install -y wget zip unzip tar
+elif grep -Eqi "Alpine" /etc/issue || grep -Eqi "Alpine" /etc/*-release; then
+ OSNAME='alpine'
+ apk update
+ apk add devscripts -force-broken-world
+ apk add curl wget zip unzip tar -force-broken-world
+else
+ OSNAME='unknow'
+fi
+
+if [ "$EUID" -ne 0 ] && [ "$OSNAME" != "macos" ];then
+ echo "Please run as root!"
+ exit
+fi
+
+
+echo "LOCAL:${LOCAL_ADDR}"
+echo "OSNAME:${OSNAME}"
+
+if [ $OSNAME != "macos" ];then
+
+ if id www &> /dev/null ;then
+ echo ""
+ else
+ groupadd www
+ useradd -g www -s /usr/sbin/nologin www
+ fi
+
+ mkdir -p /www/server
+ mkdir -p /www/wwwroot
+ mkdir -p /www/wwwlogs
+ mkdir -p /www/backup/database
+ mkdir -p /www/backup/site
+
+ if [ ! -d /www/server/mdserver-web ];then
+ echo "downloading ${HTTP_PREFIX}github.com/midoks/mdserver-web/archive/refs/heads/dev.tar.gz"
+ curl --insecure -sSLo /tmp/dev.tar.gz ${HTTP_PREFIX}github.com/midoks/mdserver-web/archive/refs/heads/dev.tar.gz
+ cd /tmp && tar -zxvf /tmp/dev.tar.gz
+ mv -f /tmp/mdserver-web-dev /www/server/mdserver-web
+ rm -rf /tmp/dev.tar.gz
+ rm -rf /tmp/mdserver-web-dev
+ fi
+
+ # install acme.sh
+ if [ ! -d /root/.acme.sh ];then
+ if [ "$LOCAL_ADDR" != "common" ];then
+ curl -sSL -o /tmp/acme.tar.gz ${HTTP_PREFIX}github.com/acmesh-official/acme.sh/archive/master.tar.gz
+ tar xvzf /tmp/acme.tar.gz -C /tmp
+ cd /tmp/acme.sh-master
+ bash acme.sh install
+ else
+ curl -fsSL https://get.acme.sh | bash
+ fi
+ fi
+fi
+
+echo "use system version: ${OSNAME}"
+
+if [ "${OSNAME}" == "macos" ];then
+ curl --insecure -fsSL ${HTTP_PREFIX}raw.githubusercontent.com/midoks/mdserver-web/refs/heads/dev/scripts/install/macos.sh | bash
+else
+ cd /www/server/mdserver-web && bash scripts/install/${OSNAME}.sh
+fi
+
+if [ "${OSNAME}" == "macos" ];then
+ echo "macos end"
+ exit 0
+fi
+
+cd /www/server/mdserver-web && bash cli.sh start
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [ ! -f /etc/rc.d/init.d/mw ];
+do
+ echo -e ".\c"
+ sleep 1
+ let n+=1
+ if [ $n -gt 20 ];then
+ echo -e "start mw fail"
+ exit 1
+ fi
+done
+
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw stop
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw start
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw default
+
+sleep 2
+if [ ! -e /usr/bin/mw ]; then
+ if [ -f /etc/rc.d/init.d/mw ];then
+ ln -s /etc/rc.d/init.d/mw /usr/bin/mw
+ fi
+fi
+
+endTime=`date +%s`
+((outTime=(${endTime}-${startTime})/60))
+echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
+
+} 1> >(tee $LOG_FILE) 2>&1
+
+echo -e "\nInstall completed. If error occurs, please contact us with the log file mw-install.log ."
+echo "安装完毕,如果出现错误,请带上同目录下的安装日志 mw-install.log 联系我们反馈."
\ No newline at end of file
diff --git a/scripts/lib.sh b/scripts/lib.sh
new file mode 100755
index 000000000..ed3158bc3
--- /dev/null
+++ b/scripts/lib.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+
+function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }
+function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; }
+function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; }
+function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; }
+
+P_VER=`python3 -V | awk '{print $2}'`
+echo "python:$P_VER"
+sleep 1
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+serverPath=$(dirname "$rootPath")
+sourcePath=$serverPath/source/lib
+libPath=$serverPath/lib
+
+mkdir -p $sourcePath
+mkdir -p $libPath
+rm -rf ${libPath}/lib.pl
+
+
+bash ${rootPath}/scripts/getos.sh
+OSNAME=`cat ${rootPath}/data/osname.pl`
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+
+# system judge
+if [ "$OSNAME" == "macos" ]; then
+ brew install libmemcached
+ brew install curl
+ brew install zlib
+ brew install freetype
+ brew install openssl
+ brew install libzip
+elif [ "$OSNAME" == "opensuse" ];then
+ echo "opensuse lib"
+elif [ "$OSNAME" == "arch" ];then
+ echo "arch lib"
+elif [ "$OSNAME" == "freebsd" ];then
+ echo "freebsd lib"
+elif [ "$OSNAME" == "centos" ];then
+ echo "centos lib"
+elif [ "$OSNAME" == "rocky" ]; then
+ echo "rocky lib"
+elif [ "$OSNAME" == "fedora" ];then
+ echo "fedora lib"
+elif [ "$OSNAME" == "alma" ];then
+ echo "alma lib"
+elif [ "$OSNAME" == "ubuntu" ];then
+ echo "ubuntu lib"
+elif [ "$OSNAME" == "debian" ]; then
+ echo "debian lib"
+else
+ echo "OK"
+fi
+echo "system:${OSNAME}:${VERSION_ID}"
+
+
+HTTP_PREFIX="https://"
+LOCAL_ADDR=common
+cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+fi
+
+PIPSRC="https://pypi.python.org/simple"
+if [ "$LOCAL_ADDR" != "common" ];then
+ PIPSRC="https://pypi.tuna.tsinghua.edu.cn/simple"
+fi
+
+echo "local:${LOCAL_ADDR}"
+echo "pypi source:$PIPSRC"
+
+#面板需要的库
+if [ ! -f /usr/local/bin/pip3 ] && [ ! -f /usr/bin/pip3 ];then
+ python3 -m pip install --upgrade pip setuptools wheel -i $PIPSRC
+
+ which pip3 && pip3 install --upgrade pip -i $PIPSRC
+ pip3 install --upgrade pip setuptools wheel -i $PIPSRC
+fi
+
+if [ ! -f /www/server/mdserver-web/bin/activate ];then
+ if version_ge "$P_VER" "3.11.0" ;then
+ echo "python3 > 3.11"
+ cd /www/server/mdserver-web && python3 -m venv /www/server/mdserver-web
+ else
+ echo "python3 < 3.10"
+ cd /www/server/mdserver-web && python3 -m venv .
+ cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/requirements.txt -i $PIPSRC
+ fi
+ cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate
+else
+ cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate
+fi
+
+pip3 install --upgrade pip -i $PIPSRC
+pip3 install --upgrade setuptools -i $PIPSRC
+
+
+# repeated attempts
+if [ "$LOCAL_ADDR" != "common" ];then
+ cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/requirements.txt
+fi
+cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/requirements.txt -i $PIPSRC
+
+
+# Different versions use different python lib
+P_VER_D=`echo "$P_VER"|awk -F '.' '{print $1}'`
+P_VER_M=`echo "$P_VER"|awk -F '.' '{print $2}'`
+NEW_P_VER=${P_VER_D}.${P_VER_M}
+
+if [ -f /www/server/mdserver-web/version/r${NEW_P_VER}.txt ];then
+ echo "cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/version/r${NEW_P_VER}.txt"
+ cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/version/r${NEW_P_VER}.txt -i $PIPSRC
+fi
+
+echo "lib ok!"
\ No newline at end of file
diff --git a/scripts/logs_backup.py b/scripts/logs_backup.py
new file mode 100755
index 000000000..18203c08e
--- /dev/null
+++ b/scripts/logs_backup.py
@@ -0,0 +1,77 @@
+#!/usr/bin/python
+# coding: utf-8
+#-----------------------------
+# 网站日志切割脚本
+#-----------------------------
+import sys
+import os
+import shutil
+import time
+import glob
+
+if sys.platform != 'darwin':
+ os.chdir('/www/server/mdserver-web')
+
+
+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
+
+print('==================================================================')
+print('★[' + time.strftime("%Y/%m/%d %H:%M:%S") + '],切割日志')
+print('==================================================================')
+print('|--当前保留最新的[' + sys.argv[2] + ']份')
+logsPath = mw.getLogsDir()
+px = '.log'
+
+
+def split_logs(oldFileName, num):
+ global logsPath
+ if not os.path.exists(oldFileName):
+ print('|---' + oldFileName + '文件不存在!')
+ return
+
+ logs = sorted(glob.glob(oldFileName + "_*"))
+ count = len(logs)
+ num = count - num
+
+ for i in range(count):
+ if i > num:
+ break
+ os.remove(logs[i])
+ print('|---多余日志[' + logs[i] + ']已删除!')
+
+ newFileName = oldFileName + '_' + time.strftime("%Y-%m-%d_%H%M%S") + '.log'
+ shutil.move(oldFileName, newFileName)
+ print('|---已切割日志到:' + newFileName)
+
+
+def split_all(save):
+ sites = mw.M('sites').field('name').select()
+ for site in sites:
+ oldFileName = logsPath + '/' +site['name'] + px
+ split_logs(oldFileName, save)
+ errOldFileName = logsPath + '/' + site['name'] + ".error.log"
+ split_logs(errOldFileName, save)
+
+if __name__ == '__main__':
+ num = int(sys.argv[2])
+ if sys.argv[1].find('ALL') == 0:
+ split_all(num)
+ else:
+ siteName = sys.argv[1]
+ if siteName[-4:] == '.log':
+ siteName = siteName[:-4]
+ else:
+ siteName = siteName.replace("-access_log", '')
+ oldFileName = logsPath + '/' + sys.argv[1]
+ errOldFileName = logsPath + '/' + \
+ sys.argv[1].strip(".log") + ".error.log"
+ split_logs(oldFileName, num)
+ if os.path.exists(errOldFileName):
+ split_logs(errOldFileName, num)
+ path = mw.getServerDir()
+ os.system("kill -USR1 `cat " + path + "/openresty/nginx/logs/nginx.pid`")
diff --git a/scripts/old/install.sh b/scripts/old/install.sh
new file mode 100644
index 000000000..8971bfea2
--- /dev/null
+++ b/scripts/old/install.sh
@@ -0,0 +1,178 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+# LANG=en_US.UTF-8
+is64bit=`getconf LONG_BIT`
+
+VERSION=0.17.3
+
+LOG_FILE=/var/log/mw-install.log
+
+{
+
+if [ -f /etc/motd ];then
+ echo "welcome to mdserver-web panel" > /etc/motd
+fi
+
+startTime=`date +%s`
+
+_os=`uname`
+echo "use system: ${_os}"
+
+if [ ${_os} == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eqi "openSUSE" /etc/*-release; then
+ OSNAME='opensuse'
+ zypper refresh
+ zypper install cron wget curl zip unzip
+elif grep -Eqi "FreeBSD" /etc/*-release; then
+ OSNAME='freebsd'
+ pkg install -y wget curl zip unzip unrar rar
+elif grep -Eqi "EulerOS" /etc/*-release || grep -Eqi "openEuler" /etc/*-release; then
+ OSNAME='euler'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "CentOS" /etc/issue || grep -Eqi "CentOS" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "Fedora" /etc/issue || grep -Eqi "Fedora" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "Rocky" /etc/issue || grep -Eqi "Rocky" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eqi "AlmaLinux" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eqi "Amazon Linux" /etc/*-release; then
+ OSNAME='amazon'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "Debian" /etc/issue || grep -Eqi "Debian" /etc/os-release; then
+ OSNAME='debian'
+ apt update -y
+ apt install -y wget curl zip unzip tar cron
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eqi "Ubuntu" /etc/os-release; then
+ OSNAME='ubuntu'
+ apt update -y
+ apt install -y wget curl zip unzip tar cron
+else
+ OSNAME='unknow'
+fi
+
+if [ "$EUID" -ne 0 ] && [ "$OSNAME" != "macos" ];then
+ echo "Please run as root!"
+ exit
+fi
+
+
+# HTTP_PREFIX="https://"
+# LOCAL_ADDR=common
+# ping -c 1 github.com > /dev/null 2>&1
+# if [ "$?" != "0" ];then
+# LOCAL_ADDR=cn
+# HTTP_PREFIX="https://mirror.ghproxy.com/"
+# fi
+
+HTTP_PREFIX="https://"
+LOCAL_ADDR=common
+cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ HTTP_PREFIX="https://mirror.ghproxy.com/"
+fi
+
+echo "local:${LOCAL_ADDR}"
+
+if [ $OSNAME != "macos" ];then
+ if id www &> /dev/null ;then
+ echo ""
+ else
+ groupadd www
+ useradd -g www -s /usr/sbin/nologin www
+ fi
+
+ mkdir -p /www/server
+ mkdir -p /www/wwwroot
+ mkdir -p /www/wwwlogs
+ mkdir -p /www/backup/database
+ mkdir -p /www/backup/site
+
+ # https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh
+ if [ ! -d /www/server/mdserver-web ];then
+ if [ "$LOCAL_ADDR" == "common" ];then
+ curl --insecure -sSLo /tmp/master.zip ${HTTP_PREFIX}github.com/midoks/mdserver-web/archive/refs/tags/${VERSION}.zip
+ cd /tmp && unzip /tmp/master.zip
+ mv -f /tmp/mdserver-web-${VERSION} /www/server/mdserver-web
+ rm -rf /tmp/master.zip
+ rm -rf /tmp/mdserver-web-master
+ else
+ # curl --insecure -sSLo /tmp/master.zip https://code.midoks.icu/midoks/mdserver-web/archive/master.zip
+ wget --no-check-certificate -O /tmp/master.zip https://code.midoks.icu/midoks/mdserver-web/archive/${VERSION}.zip
+ cd /tmp && unzip /tmp/master.zip
+ mv -f /tmp/mdserver-web /www/server/mdserver-web
+ rm -rf /tmp/master.zip
+ rm -rf /tmp/mdserver-web
+ fi
+
+
+ fi
+
+ # install acme.sh
+ if [ ! -d /root/.acme.sh ];then
+ if [ "$LOCAL_ADDR" != "common" ];then
+ curl --insecure -sSLo /tmp/acme.tar.gz https://gitee.com/neilpang/acme.sh/repository/archive/master.tar.gz
+ tar xvzf /tmp/acme.tar.gz -C /tmp
+ cd /tmp/acme.sh-master
+ bash acme.sh install
+ fi
+
+ if [ ! -d /root/.acme.sh ];then
+ curl https://get.acme.sh | sh
+ fi
+ fi
+fi
+
+echo "use system version: ${OSNAME}"
+if [ "${OSNAME}" == "macos" ];then
+ curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/master/scripts/install/macos.sh | bash
+else
+ cd /www/server/mdserver-web && bash scripts/install/${OSNAME}.sh
+fi
+
+if [ "${OSNAME}" == "macos" ];then
+ echo "macos end"
+ exit 0
+fi
+
+cd /www/server/mdserver-web && bash cli.sh start
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [ ! -f /etc/rc.d/init.d/mw ];
+do
+ echo -e ".\c"
+ sleep 1
+ let n+=1
+ if [ $n -gt 20 ];then
+ echo -e "start mw fail"
+ exit 1
+ fi
+done
+
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw stop
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw start
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw default
+
+sleep 2
+if [ ! -e /usr/bin/mw ]; then
+ if [ -f /etc/rc.d/init.d/mw ];then
+ ln -s /etc/rc.d/init.d/mw /usr/bin/mw
+ fi
+fi
+
+endTime=`date +%s`
+((outTime=(${endTime}-${startTime})/60))
+echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
+
+} 1> >(tee $LOG_FILE) 2>&1
+
+echo -e "\nInstall completed. If error occurs, please contact us with the log file mw-install.log ."
+echo "安装完毕,如果出现错误,请带上同目录下的安装日志 mw-install.log 联系我们反馈."
diff --git a/scripts/old/update.sh b/scripts/old/update.sh
new file mode 100644
index 000000000..fb9527f91
--- /dev/null
+++ b/scripts/old/update.sh
@@ -0,0 +1,122 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+# LANG=en_US.UTF-8
+is64bit=`getconf LONG_BIT`
+
+startTime=`date +%s`
+
+VERSION=0.17.3
+
+_os=`uname`
+echo "use system: ${_os}"
+
+if [ "$EUID" -ne 0 ]
+ then echo "Please run as root!"
+ exit
+fi
+
+if [ ${_os} != "Darwin" ] && [ ! -d /www/server/mdserver-web/logs ]; then
+ mkdir -p /www/server/mdserver-web/logs
+fi
+
+LOG_FILE=/var/log/mw-update.log
+
+{
+
+if [ ${_os} == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eqi "openSUSE" /etc/*-release; then
+ OSNAME='opensuse'
+ zypper refresh
+elif grep -Eqi "EulerOS" /etc/*-release || grep -Eqi "openEuler" /etc/*-release; then
+ OSNAME='euler'
+elif grep -Eqi "FreeBSD" /etc/*-release; then
+ OSNAME='freebsd'
+elif grep -Eqi "CentOS" /etc/issue || grep -Eqi "CentOS" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget zip unzip
+elif grep -Eqi "Fedora" /etc/issue || grep -Eqi "Fedora" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget zip unzip
+elif grep -Eqi "Rocky" /etc/issue || grep -Eqi "Rocky" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget zip unzip
+elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eqi "AlmaLinux" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget zip unzip
+elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eqi "Amazon Linux" /etc/*-release; then
+ OSNAME='amazon'
+ yum install -y wget zip unzip
+elif grep -Eqi "Debian" /etc/issue || grep -Eqi "Debian" /etc/*-release; then
+ OSNAME='debian'
+ apt install -y wget zip unzip
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eqi "Ubuntu" /etc/*-release; then
+ OSNAME='ubuntu'
+ apt install -y wget zip unzip
+elif grep -Eqi "Raspbian" /etc/issue || grep -Eqi "Raspbian" /etc/*-release; then
+ OSNAME='raspbian'
+else
+ OSNAME='unknow'
+fi
+
+
+HTTP_PREFIX="https://"
+LOCAL_ADDR=common
+cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+ HTTP_PREFIX="https://mirror.ghproxy.com/"
+fi
+echo "local:${LOCAL_ADDR}"
+
+CP_CMD=/usr/bin/cp
+if [ -f /bin/cp ];then
+ CP_CMD=/bin/cp
+fi
+
+if [ "$LOCAL_ADDR" != "common" ];then
+ # curl --insecure -sSLo /tmp/master.zip https://code.midoks.icu/midoks/mdserver-web/archive/master.zip
+ wget --no-check-certificate -O /tmp/master.zip https://github.com/midoks/mdserver-web/archive/refs/tags/${VERSION}.zip
+ cd /tmp && unzip /tmp/master.zip
+
+ $CP_CMD -rf /tmp/mdserver-web-${VERSION}/* /www/server/mdserver-web
+ rm -rf /tmp/master.zip
+ rm -rf /tmp/mdserver-web-${VERSION}
+
+ pip install -r /www/server/mdserver-web/requirements.txt
+else
+ # curl --insecure -sSLo /tmp/master.zip https://github.com/midoks/mdserver-web/archive/refs/tags/0.17.3.zip
+ curl --insecure -sSLo /tmp/master.zip https://github.com/midoks/mdserver-web/archive/refs/tags/${VERSION}.zip
+
+ cd /tmp && unzip /tmp/master.zip
+ $CP_CMD -rf /tmp/mdserver-web-${VERSION}/* /www/server/mdserver-web
+ rm -rf /tmp/master.zip
+ rm -rf /tmp/mdserver-web-${VERSION}
+
+ pip install -r /www/server/mdserver-web/requirements.txt
+fi
+
+
+#pip uninstall public
+echo "use system version: ${OSNAME}"
+cd /www/server/mdserver-web && bash scripts/update/${OSNAME}.sh
+
+bash /etc/rc.d/init.d/mw restart
+bash /etc/rc.d/init.d/mw default
+
+if [ -f /usr/bin/mw ];then
+ rm -rf /usr/bin/mw
+fi
+
+if [ ! -e /usr/bin/mw ]; then
+ if [ ! -f /usr/bin/mw ];then
+ ln -s /etc/rc.d/init.d/mw /usr/bin/mw
+ fi
+fi
+
+endTime=`date +%s`
+((outTime=($endTime-$startTime)/60))
+echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
+
+} 1> >(tee $LOG_FILE) 2>&1
\ No newline at end of file
diff --git a/scripts/pick.sh b/scripts/pick.sh
new file mode 100755
index 000000000..75746c354
--- /dev/null
+++ b/scripts/pick.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+
+
+#----------------------------- 代码打包 -------------------------#
+
+echo $rootPath
+cd $rootPath
+rm -rf ./*.pyc
+rm -rf ./*/*.pyc
+
+startTime=`date +%s`
+
+zip -r -q -o mdserver-web.zip ./ -x@$curPath/pick_filter.txt
+
+
+
+mv mdserver-web.zip $rootPath/scripts
+
+endTime=`date +%s`
+((outTime=($endTime-$startTime)))
+echo -e "Time consumed:\033[32m $outTime \033[0mSec!"
\ No newline at end of file
diff --git a/scripts/pick_filter.txt b/scripts/pick_filter.txt
new file mode 100755
index 000000000..e6c398114
--- /dev/null
+++ b/scripts/pick_filter.txt
@@ -0,0 +1,139 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+scripts/.git/*
+.git/*
+.github/*
+bin/*
+lib/*
+include/*
+pyvenv.cfg
+__pycache__
+
+docker/*
+.mypy_cache/
+.gitignore
+.git
+.DS_Store
+.idea/*.xml
+logs/*
+tmp/*
+version/*
+data/json/index.json
+data/control.conf
+data/default.db
+data/system.db
+data/default.pl
+data/control.conf
+data/iplist.txt
+data/osname.pl
+data/edate.pl
+data/backup.pl
+data/default_site.pl
+data/port.pl
+data/api_login.txt
+data/json/index.json
+data/sessions/*
+
+
+
+script/init.d/mw
diff --git a/scripts/plugin_compress.sh b/scripts/plugin_compress.sh
new file mode 100755
index 000000000..58dd98fe0
--- /dev/null
+++ b/scripts/plugin_compress.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+###
+### 插件压缩
+###
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+is64bit=`getconf LONG_BIT`
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+
+startTime=`date +%s`
+PLUGIN_NAME='abkill'
+
+#echo $rootPath/plugins/$PLUGIN_NAME
+mkdir -p $rootPath/scripts/tmp
+cd $rootPath/plugins/$PLUGIN_NAME && zip $rootPath/scripts/tmp/${PLUGIN_NAME}_${startTime}.zip -r ./* > /tmp/t.log + 2>&1
+
+endTime=`date +%s`
+((outTime=($endTime-$startTime)))
+echo -e "Time consumed:\033[32m $outTime \033[0msecs.!"
\ No newline at end of file
diff --git a/scripts/quick/app.sh b/scripts/quick/app.sh
new file mode 100755
index 000000000..8212b179d
--- /dev/null
+++ b/scripts/quick/app.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+if [ ! -d /www/server/mdserver-web/logs ]; then
+ mkdir -p /www/server/mdserver-web/logs
+fi
+
+{
+
+echo "welcome to mdserver-web panel"
+
+startTime=`date +%s`
+
+if [ ! -d /www/server/mdserver-web ];then
+ echo "mdserver-web not exist!"
+ exit 1
+fi
+
+# openresty
+if [ ! -d /www/server/openresty ];then
+ cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.25.3
+else
+ echo "openresty alreay exist!"
+fi
+
+# redis
+if [ ! -d /www/server/redis ];then
+ cd /www/server/mdserver-web/plugins/redis && bash install.sh install 7.4.3
+else
+ echo "redis alreay exist!"
+fi
+
+
+# php
+if [ ! -d /www/server/php/71 ];then
+ cd /www/server/mdserver-web/plugins/php && bash install.sh install 71
+else
+ echo "php71 alreay exist!"
+fi
+
+
+# php
+if [ ! -d /www/server/php/74 ];then
+ cd /www/server/mdserver-web/plugins/php && bash install.sh install 74
+else
+ echo "php74 alreay exist!"
+fi
+
+
+# swap
+if [ ! -d /www/server/swap ];then
+ cd /www/server/mdserver-web/plugins/swap && bash install.sh install 1.1
+else
+ echo "swap alreay exist!"
+fi
+
+# mysql
+if [ ! -d /www/server/mysql ];then
+ cd /www/server/mdserver-web/plugins/mysql && bash install.sh install 5.7
+else
+ echo "mysql alreay exist!"
+fi
+
+# phpmyadmin
+if [ ! -d /www/server/phpmyadmin ];then
+ cd /www/server/mdserver-web/plugins/phpmyadmin && bash install.sh install 4.4.15
+else
+ echo "phpmyadmin alreay exist!"
+fi
+
+endTime=`date +%s`
+((outTime=(${endTime}-${startTime})/60))
+echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
+
+} 1> >(tee /www/server/mdserver-web/logs/mw-app.log) 2>&1
\ No newline at end of file
diff --git a/scripts/quick/debug.sh b/scripts/quick/debug.sh
new file mode 100755
index 000000000..69b9ca1de
--- /dev/null
+++ b/scripts/quick/debug.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+if [ ! -d /www/server/mdserver-web/logs ]; then
+ mkdir -p /www/server/mdserver-web/logs
+fi
+
+{
+
+echo "welcome to mdserver-web panel"
+
+startTime=`date +%s`
+
+if [ ! -d /www/server/mdserver-web ];then
+ echo "mdserver-web not exist!"
+ exit 1
+fi
+
+# openresty
+if [ ! -d /www/server/openresty ];then
+ cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.1
+fi
+
+
+# php
+# if [ ! -d /www/server/php/71 ];then
+# cd /www/server/mdserver-web/plugins/php && bash install.sh install 71
+# fi
+
+
+PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82)
+# PHP_VER_LIST=(81)
+for PHP_VER in ${PHP_VER_LIST[@]}; do
+ echo "php${PHP_VER} -- start"
+ if [ ! -d /www/server/php/${PHP_VER} ];then
+ cd /www/server/mdserver-web/plugins/php && bash install.sh install ${PHP_VER}
+ fi
+ echo "php${PHP_VER} -- end"
+done
+
+
+# cd /www/server/mdserver-web/plugins/php-yum && bash install.sh install 74
+
+
+# mysql
+if [ ! -d /www/server/mysql ];then
+ # cd /www/server/mdserver-web/plugins/mysql && bash install.sh install 5.7
+
+
+ cd /www/server/mdserver-web/plugins/mysql && bash install.sh install 5.6
+ # cd /www/server/mdserver-web/plugins/mysql && bash install.sh install 8.0
+fi
+
+endTime=`date +%s`
+((outTime=(${endTime}-${startTime})/60))
+echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
+
+} 1> >(tee /www/server/mdserver-web/logs/mw-debug.log) 2>&1
\ No newline at end of file
diff --git a/scripts/rememory.sh b/scripts/rememory.sh
new file mode 100755
index 000000000..5311e7aa6
--- /dev/null
+++ b/scripts/rememory.sh
@@ -0,0 +1,81 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+#+------------------------------------
+#+ 释放内存脚本
+#+------------------------------------
+
+endDate=`date +"%Y-%m-%d %H:%M:%S"`
+sysName=`uname`
+curPath=`pwd`
+rootPath=$(dirname "$curPath")
+
+log="释放内存!"
+echo "★[$endDate] $log"
+echo '----------------------------------------------------------------------------'
+
+if [ $sysName == 'Darwin' ]; then
+ echo '苹果内存释放!'
+else
+ echo 'do start!'
+fi
+
+
+echo "OpenResty -- START"
+if [ -f /usr/lib/systemd/system/openresty.service ];then
+ systemctl reload openresty
+elif [ -f $rootPath/openresty/nginx/sbin/nginx ];then
+ $rootPath/openresty/nginx/sbin/nginx -s reload
+else
+ echo "..."
+fi
+echo "OpenResty -- END"
+
+
+PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82 83 84)
+for PHP_VER in ${PHP_VER_LIST[@]}; do
+echo "PHP${PHP_VER} -- START"
+if [ -f /usr/lib/systemd/system/php${PHP_VER}.service ];then
+ systemctl reload php${PHP_VER}
+elif [ -f ${rootPath}/php/init.d/php${PHP_VER} ];then
+ ${rootPath}/php/init.d/php${PHP_VER} reload
+else
+ echo "..."
+fi
+echo "PHP${PHP_VER} -- END"
+done
+
+echo "MySQL -- START"
+if [ -f /usr/lib/systemd/system/mysql.service ];then
+ systemctl reload mysql
+elif [ -f ${rootPath}/php/init.d/mysql ];then
+ ${rootPath}/mysql/init.d/mysql reload
+else
+ echo "..."
+fi
+echo "MySQL -- END"
+
+
+
+echo "PureFTPD -- START"
+if [ -f /usr/lib/systemd/system/pureftp.service ];then
+ systemctl reload pureftp
+elif [ -f ${rootPath}/pureftp/init.d/pureftp ];then
+ ${rootPath}/pureftp/init.d/pureftp reload
+else
+ echo "..."
+fi
+echo "PureFTPD -- END"
+
+
+sync
+sleep 2
+sync
+
+if [ $sysName == 'Darwin' ]; then
+ echo 'done!'
+else
+ echo 3 > /proc/sys/vm/drop_caches
+fi
+
+echo '----------------------------------------------------------------------------'
\ No newline at end of file
diff --git a/scripts/tools/conntrace/script.sh b/scripts/tools/conntrace/script.sh
new file mode 100644
index 000000000..b03184780
--- /dev/null
+++ b/scripts/tools/conntrace/script.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+# 测试中.
+
+# /opt/iptables-switch.sh status | disable | enable
+ipt_mod_conf="/etc/modprobe.d/iptables.conf"
+ipt_mod_list="ip_vs iptable_nat nf_nat_ipv4 ipt_MASQUERADE nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_conntrack nf_conntrack iptable_filter ip_tables xt_tcpudp xt_multiport xt_length xt_addrtype x_tables"
+nf_max=$(sysctl -e -n net.nf_conntrack_max)
+nf_cur=$(sysctl -e -n net.netfilter.nf_conntrack_count)
+ipt_hsize=$(grep 'MemTotal' /proc/meminfo | awk '{printf("%d",$2/16)}')
+
+fuck_ipt_mod(){
+ echo '# disable iptables conntrack modules' > ${ipt_mod_conf}
+ for ipt_mod in ${ipt_mod_list}; do
+ echo "blacklist ${ipt_mod}" >> ${ipt_mod_conf}
+ modprobe -r ${ipt_mod}
+ done
+}
+
+clean_ipt_rule(){
+ iptables -F
+ iptables -Z
+ iptables -X
+ for ipt_table in $(cat /proc/net/ip_tables_names 2>/dev/null); do
+ iptables -t ${ipt_table} -F
+ iptables -t ${ipt_table} -Z
+ iptables -t ${ipt_table} -X
+ done
+ iptables -P INPUT ACCEPT
+ iptables -P OUTPUT ACCEPT
+ iptables -P FORWARD ACCEPT
+}
+
+ipt_enable(){
+ echo "options nf_conntrack hashsize=${ipt_hsize}" > ${ipt_mod_conf} # /sys/module/nf_conntrack/parameters/hashsize
+ for ipt_mod in ${ipt_mod_list}; do
+ modprobe -q -r ${ipt_mod} && modprobe -a ${ipt_mod}
+ done
+
+ dmesg --reltime | grep nf_conntrack | tail -2 2>/dev/null
+ sysctl -e -w net.nf_conntrack_max=4194304
+ sysctl -e -w net.ipv4.netfilter.ip_conntrack_max=4194304
+ sysctl -e -w net.netfilter.nf_conntrack_max=4194304
+ sysctl -e -w net.netfilter.nf_conntrack_tcp_timeout_established=1200
+ sysctl -e -w net.netfilter.nf_conntrack_tcp_timeout_close_wait=60
+ sysctl -e -w net.netfilter.nf_conntrack_tcp_timeout_fin_wait=120
+ sysctl -e -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=120
+}
+
+case "$1" in
+status)
+ if [[ -z ${nf_max} ]]; then
+ echo 'nf_conntrack disabled.'
+ else
+ echo "nf_conntrack used: ${nf_cur}/${nf_max}."
+ fi
+ ;;
+disable)
+ clean_ipt_rule
+ fuck_ipt_mod
+ $0 status
+ ;;
+enable)
+ ipt_enable
+ $0 status
+;;
+*)
+ echo "Usage: $0 {status|disable|enable}"
+ exit 2
+ ;;
+esac
+exit 0
\ No newline at end of file
diff --git a/scripts/tools/trace/trace.sh b/scripts/tools/trace/trace.sh
new file mode 100644
index 000000000..e69de29bb
diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh
new file mode 100755
index 000000000..c94c4f67a
--- /dev/null
+++ b/scripts/uninstall.sh
@@ -0,0 +1,161 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+export LANG=en_US.UTF-8
+is64bit=`getconf LONG_BIT`
+
+if [ -f /etc/motd ];then
+ echo "" > /etc/motd
+fi
+
+startTime=`date +%s`
+
+_os=`uname`
+echo "检测到系统: ${_os}"
+
+if [ "$EUID" -ne 0 ]
+ then echo "需要使用 root 权限运行卸载脚本,请用 sudo 或切换到 root 后重试~"
+ exit
+fi
+
+UNINSTALL_CHECK()
+{
+ echo -e "----------------------------------------------------"
+ echo -e "目前支持卸载 OpenResty/PHP/MySQL/Redis/Memcached"
+ echo -e "其他插件请先手动卸载哦!"
+ echo -e "----------------------------------------------------"
+ echo -e "温馨提示:输入 yes 才会继续卸载![yes/no]"
+ read -p "输入 yes 开始卸载(可随时 Ctrl+C 退出): " yes;
+ if [ "$yes" != "yes" ];then
+ echo -e "------------"
+ echo "已取消卸载,安全第一~"
+ exit 1
+ else
+ echo "开始卸载!我们走~"
+ fi
+}
+
+
+UNINSTALL_MySQL()
+{
+ MYSQLD_CHECK=$(ps -ef |grep mysqld | grep -v grep | grep /www/server/mysql)
+ if [ "$MYSQLD_CHECK" != "" ];then
+ echo -e "----------------------------------------------------"
+ echo -e "检测到 MySQL,卸载可能影响站点与数据"
+ echo -e "----------------------------------------------------"
+ echo -e "温馨提示:输入 yes 才会继续卸载![yes/no]"
+ read -p "输入 yes 强制卸载: " yes;
+ if [ "$yes" != "yes" ];then
+ echo -e "------------"
+ echo "已取消卸载 MySQL"
+ else
+ cd /www/server/mdserver-web/plugins/mysql && sh install.sh uninstall 8.0
+ echo "MySQL 卸载完成!"
+ fi
+ fi
+}
+
+UNINSTALL_OP()
+{
+ if [ -f /www/server/openresty ];then
+ echo -e "----------------------------------------------------"
+ echo -e "检测到 OpenResty,卸载可能影响站点与数据"
+ echo -e "----------------------------------------------------"
+ echo -e "温馨提示:输入 yes 才会继续卸载![yes/no]"
+ read -p "输入 yes 强制卸载: " yes;
+ if [ "$yes" != "yes" ];then
+ echo -e "------------"
+ echo "已取消卸载 OpenResty"
+ else
+ cd /www/server/mdserver-web/plugins/openresty && sh install.sh uninstall
+ echo "OpenResty 卸载完成!"
+ fi
+ fi
+}
+
+UNINSTALL_PHP()
+{
+ if [ -d /www/server/php ];then
+ echo -e "----------------------------------------------------"
+ echo -e "检测到 PHP,卸载可能影响站点与数据"
+ echo -e "----------------------------------------------------"
+ read -p "输入 yes 强制卸载所有 PHP [yes/no]: " yes;
+ if [ "$yes" != "yes" ];then
+ echo -e "------------"
+ echo "已取消卸载 PHP"
+ else
+ PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82)
+ for PHP_VER in ${PHP_VER_LIST[@]}; do
+ if [ -d /www/server/php/${PHP_VER} ];then
+ cd /www/server/mdserver-web/plugins/php && bash install.sh uninstall ${PHP_VER}
+ fi
+ echo "PHP${PHP_VER} 卸载完成!"
+ done
+ fi
+ fi
+}
+
+UNINSTALL_MEMCACHED()
+{
+ if [ -d /www/server/memcached ];then
+ echo -e "----------------------------------------------------"
+ echo -e "检测到 Memcached,卸载可能影响站点与数据"
+ echo -e "----------------------------------------------------"
+ read -p "输入 yes 强制卸载所有 Memcached [yes/no]: " yes;
+ if [ "$yes" != "yes" ];then
+ echo -e "------------"
+ echo "已取消卸载 Memcached"
+ else
+ cd /www/server/mdserver-web/plugins/memcached && bash install.sh uninstall
+ echo "Memcached 卸载完成!"
+ fi
+ fi
+}
+
+UNINSTALL_REDIS()
+{
+ if [ -d /www/server/redis ];then
+ echo -e "----------------------------------------------------"
+ echo -e "检测到 Redis,卸载可能影响站点与数据"
+ echo -e "----------------------------------------------------"
+ read -p "输入 yes 强制卸载所有 Redis [yes/no]: " yes;
+ if [ "$yes" != "yes" ];then
+ echo -e "------------"
+ echo "已取消卸载 Redis"
+ else
+ cd /www/server/mdserver-web/plugins/redis && bash install.sh uninstall 7.0.4
+ echo "Redis 卸载完成!"
+ fi
+ fi
+}
+
+UNINSTALL_MW()
+{
+ echo -e "----------------------------------------------------"
+ echo -e "检测到面板环境,卸载可能影响站点与数据"
+ echo -e "----------------------------------------------------"
+ read -p "输入 yes 强制卸载面板(仅移除面板,不清理网站/数据库数据): " yes;
+ if [ "$yes" != "yes" ];then
+ echo -e "------------"
+ echo "已取消卸载面板"
+ else
+ rm -rf /usr/bin/mw
+ rm -rf /etc/init.d/mw
+ systemctl daemon-reload
+ rm -rf /www/server/mdserver-web
+ echo "面板卸载完成!"
+ fi
+}
+
+UNINSTALL_CHECK
+
+UNINSTALL_OP
+UNINSTALL_PHP
+UNINSTALL_MySQL
+UNINSTALL_MEMCACHED
+UNINSTALL_REDIS
+UNINSTALL_MW
+
+endTime=`date +%s`
+((outTime=(${endTime}-${startTime})/60))
+echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
diff --git a/scripts/update.sh b/scripts/update.sh
new file mode 100755
index 000000000..501b935d4
--- /dev/null
+++ b/scripts/update.sh
@@ -0,0 +1,240 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+# LANG=en_US.UTF-8
+is64bit=`getconf LONG_BIT`
+REPO_OWNER="AndyXeCM"
+REPO_NAME="PowerLinux"
+REPO_BRANCH="master"
+
+startTime=`date +%s`
+
+if [ -f /www/server/mdserver-web/tools.py ];then
+ echo -e "检测到旧版代码,为避免翻车先停一下~"
+ echo -e "如确认继续,请先执行: rm -rf /www/server/mdserver-web"
+ echo -e "处理完再回来更新吧!需要帮忙可以随时再问我。"
+ exit 0
+fi
+
+
+_os=`uname`
+echo "检测到系统: ${_os}"
+
+if [ "$EUID" -ne 0 ]
+ then echo "需要使用 root 权限运行更新脚本,请用 sudo 或切换到 root 后重试~"
+ exit
+fi
+
+if [ ${_os} != "Darwin" ] && [ ! -d /www/server/mdserver-web/logs ]; then
+ mkdir -p /www/server/mdserver-web/logs
+fi
+
+LOG_FILE=/var/log/mw-update.log
+{
+
+HTTP_PREFIX="https://"
+LOCAL_ADDR=common
+cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+fi
+
+if [ "$LOCAL_ADDR" != "common" ];then
+ declare -A PROXY_URL
+ PROXY_URL["github_do"]="https://github.do/"
+ PROXY_URL["gh_llkk_cc"]="https://gh.llkk.cc/https://"
+ PROXY_URL["gh_felicity_ac_cn"]="https://gh.felicity.ac.cn/https://"
+ PROXY_URL["ghfast_top"]="https://ghfast.top/"
+ PROXY_URL["ghproxy_net"]="https://ghproxy.net/"
+ PROXY_URL["gh_927223_xyz"]="https://gh.927223.xyz/https://"
+ PROXY_URL["gh_proxy_net"]="https://gh-proxy.net/"
+
+ PROXY_URL["source"]="https://"
+
+
+ SOURCE_LIST_KEY_SORT_TMP=$(echo ${!PROXY_URL[@]} | tr ' ' '\n' | sort -n)
+ SOURCE_LIST_KEY=(${SOURCE_LIST_KEY_SORT_TMP//'\n'/})
+ SOURCE_LIST_LEN=${#PROXY_URL[*]}
+fi
+
+
+function AutoSizeStr(){
+ NAME_STR=$1
+ NAME_NUM=$2
+
+ NAME_STR_LEN=`echo "$NAME_STR" | wc -L`
+ NAME_NUM_LEN=`echo "$NAME_NUM" | wc -L`
+
+ fix_len=35
+ remaining_len=`expr $fix_len - $NAME_STR_LEN - $NAME_NUM_LEN`
+ FIX_SPACE=' '
+ for ((ass_i=1;ass_i<=$remaining_len;ass_i++))
+ do
+ FIX_SPACE="$FIX_SPACE "
+ done
+ echo -e " ❖ ${1}${FIX_SPACE}${2})"
+}
+
+function ChooseProxyURL(){
+ clear
+ echo -e '+---------------------------------------------------+'
+ echo -e '| |'
+ echo -e '| ============================================= |'
+ echo -e '| |'
+ echo -e '| 欢迎使用 Linux 一键更新面板源码,马上开始吧! |'
+ echo -e '| |'
+ echo -e '| ============================================= |'
+ echo -e '| |'
+ echo -e '+---------------------------------------------------+'
+ echo -e ''
+ echo -e '#####################################################'
+ echo -e ''
+ echo -e ' 提供以下国内代理地址可供选择: '
+ echo -e ''
+ echo -e '#####################################################'
+ echo -e ''
+ cm_i=0
+ for V in ${SOURCE_LIST_KEY[@]}; do
+ num=`expr $cm_i + 1`
+ AutoSizeStr "${V}" "$num"
+ cm_i=`expr $cm_i + 1`
+ done
+ echo -e ''
+ echo -e '#####################################################'
+ echo -e ''
+ echo -e " 系统时间 ${BLUE}$(date "+%Y-%m-%d %H:%M:%S")${PLAIN}"
+ echo -e ''
+ echo -e '#####################################################'
+ CHOICE_A=$(echo -e "\n${BOLD}└─ 请选择并输入你想使用的代理地址 [ 1-${SOURCE_LIST_LEN} ](回车默认 1):${PLAIN}")
+
+ read -p "${CHOICE_A}" INPUT
+ # echo $INPUT
+ if [ "$INPUT" == "" ];then
+ INPUT=1
+ TMP_INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$TMP_INPUT]}
+ echo -e "\n默认选择[${BLUE}${INPUT_KEY}${PLAIN}],准备开始更新~"
+ fi
+
+ if [ "$INPUT" -lt "0" ];then
+ INPUT=1
+ TMP_INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$TMP_INPUT]}
+ echo -e "\n低于边界啦!已切回[${BLUE}${INPUT_KEY}${PLAIN}]继续更新~"
+ sleep 2s
+ fi
+
+ if [ "$INPUT" -gt "${SOURCE_LIST_LEN}" ];then
+ INPUT=${SOURCE_LIST_LEN}
+ TMP_INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$TMP_INPUT]}
+ echo -e "\n超出边界啦!已切回[${BLUE}${INPUT_KEY}${PLAIN}]继续更新~"
+ sleep 2s
+ fi
+
+ INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$INPUT]}
+ HTTP_PREFIX=${PROXY_URL[$INPUT_KEY]}
+}
+
+
+if [ "$LOCAL_ADDR" != "common" ];then
+ ChooseProxyURL
+
+ if [ "$HTTP_PREFIX" != "https://" ];then
+ DOMAIN=`echo $HTTP_PREFIX | sed 's|https://||g'`
+ DOMAIN=`echo $DOMAIN | sed 's|/||g'`
+ ping -c 3 $DOMAIN > /dev/null 2>&1
+ if [ "$?" != "0" ];then
+ echo "无效代理地址:${DOMAIN}"
+ exit
+ fi
+ fi
+fi
+
+if [ ${_os} == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eqi "openSUSE" /etc/*-release; then
+ OSNAME='opensuse'
+ zypper refresh
+elif grep -Eqi "EulerOS" /etc/*-release || grep -Eqi "openEuler" /etc/*-release; then
+ OSNAME='euler'
+elif grep -Eqi "FreeBSD" /etc/*-release; then
+ OSNAME='freebsd'
+elif grep -Eqi "CentOS" /etc/issue || grep -Eqi "CentOS" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget zip unzip
+elif grep -Eqi "Fedora" /etc/issue || grep -Eqi "Fedora" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget zip unzip
+elif grep -Eqi "Rocky" /etc/issue || grep -Eqi "Rocky" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget zip unzip
+elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eqi "AlmaLinux" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget zip unzip
+elif grep -Eqi "Anolis" /etc/issue || grep -Eqi "Anolis" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eqi "Amazon Linux" /etc/*-release; then
+ OSNAME='amazon'
+ yum install -y wget zip unzip
+elif grep -Eqi "Debian" /etc/issue || grep -Eqi "Debian" /etc/*-release; then
+ OSNAME='debian'
+ apt install -y wget zip unzip
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eqi "Ubuntu" /etc/*-release; then
+ OSNAME='ubuntu'
+ apt install -y wget zip unzip
+elif grep -Eqi "Raspbian" /etc/issue || grep -Eqi "Raspbian" /etc/*-release; then
+ OSNAME='raspbian'
+elif grep -Eqi "Alpine" /etc/issue || grep -Eqi "Alpine" /etc/*-release; then
+ OSNAME='alpine'
+ apk update
+ apk add devscripts -force-broken-world
+ apk add wget zip unzip tar -force-broken-world
+else
+ OSNAME='unknow'
+fi
+
+echo "LOCAL:${LOCAL_ADDR}"
+
+CP_CMD=/usr/bin/cp
+if [ -f /bin/cp ];then
+ CP_CMD=/bin/cp
+fi
+
+echo "开始更新面板源码,冲冲冲~"
+
+echo "下载 ${REPO_OWNER}/${REPO_NAME}(分支 ${REPO_BRANCH})..."
+curl --insecure -sSLo /tmp/master.tar.gz ${HTTP_PREFIX}github.com/${REPO_OWNER}/${REPO_NAME}/archive/refs/heads/${REPO_BRANCH}.tar.gz
+TARBALL_DIR=$(tar -tf /tmp/master.tar.gz | head -1 | cut -d/ -f1)
+cd /tmp && tar -zxvf /tmp/master.tar.gz
+$CP_CMD -rf /tmp/${TARBALL_DIR}/* /www/server/mdserver-web
+rm -rf /tmp/master.tar.gz
+rm -rf /tmp/${TARBALL_DIR}
+
+echo "源码更新完成,继续收尾~"
+
+
+#pip uninstall public
+echo "use system version: ${OSNAME}"
+cd /www/server/mdserver-web && bash scripts/update/${OSNAME}.sh
+
+bash /etc/rc.d/init.d/mw restart
+bash /etc/rc.d/init.d/mw default
+
+if [ -f /usr/bin/mw ];then
+ rm -rf /usr/bin/mw
+fi
+
+if [ ! -e /usr/bin/mw ]; then
+ if [ ! -f /usr/bin/mw ];then
+ ln -s /etc/rc.d/init.d/mw /usr/bin/mw
+ fi
+fi
+
+endTime=`date +%s`
+((outTime=($endTime-$startTime)/60))
+echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
+
+} 1> >(tee $LOG_FILE) 2>&1
diff --git a/scripts/update/alma.sh b/scripts/update/alma.sh
new file mode 100755
index 000000000..3fecb6298
--- /dev/null
+++ b/scripts/update/alma.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=C.UTF-8
+
+
+if [ -f /etc/motd ];then
+ echo "welcome to mdserver-web panel" > /etc/motd
+fi
+
+sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
+
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+if [ -f /etc/rc.d/init.d/mw ];then
+ bash /etc/rc.d/init.d/mw stop && rm -rf /www/server/mdserver-web/scripts/init.d/mw && rm -rf /etc/rc.d/init.d/mw
+fi
+
+echo -e "stop mw"
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+
+port=7200
+if [ -f /www/server/mdserver-web/data/port.pl ]; then
+ port=$(cat /www/server/mdserver-web/data/port.pl)
+fi
+
+n=0
+while [[ "$isStart" != "" ]];
+do
+ echo -e ".\c"
+ sleep 0.5
+ isStart=$(lsof -n -P -i:$port|grep LISTEN|grep -v grep|awk '{print $2}'|xargs)
+ let n+=1
+ if [ $n -gt 15 ];then
+ break;
+ fi
+done
+
+
+echo -e "start mw"
+cd /www/server/mdserver-web && bash cli.sh start
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [[ ! -f /etc/rc.d/init.d/mw ]];
+do
+ echo -e ".\c"
+ sleep 1
+ let n+=1
+ if [ $n -gt 20 ];then
+ echo -e "start mw fail"
+ exit 1
+ fi
+done
+echo -e "start mw success"
diff --git a/scripts/update/alpine.sh b/scripts/update/alpine.sh
new file mode 100644
index 000000000..d0114e5bd
--- /dev/null
+++ b/scripts/update/alpine.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+
+# systemctl stop SuSEfirewall2
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+if [ -f /etc/rc.d/init.d/mw ];then
+ bash /etc/rc.d/init.d/mw stop && rm -rf /www/server/mdserver-web/scripts/init.d/mw && rm -rf /etc/rc.d/init.d/mw
+fi
+
+echo -e "start mw"
+cd /www/server/mdserver-web && bash cli.sh start
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [[ ! -f /etc/rc.d/init.d/mw ]];
+do
+ echo -e ".\c"
+ sleep 1
+ let n+=1
+ if [ $n -gt 20 ];then
+ echo -e "start mw fail"
+ exit 1
+ fi
+done
+echo -e "start mw success"
+
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw stop
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw start
diff --git a/scripts/update/amazon.sh b/scripts/update/amazon.sh
new file mode 100755
index 000000000..e31d9d869
--- /dev/null
+++ b/scripts/update/amazon.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+
+if [ -f /etc/motd ];then
+ echo "welcome to mdserver-web panel" > /etc/motd
+fi
+
+sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
+
+yum install -y curl-devel libmcrypt libmcrypt-devel python3-devel
+
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+if [ -f /etc/rc.d/init.d/mw ];then
+ bash /etc/rc.d/init.d/mw stop && rm -rf /www/server/mdserver-web/scripts/init.d/mw && rm -rf /etc/rc.d/init.d/mw
+fi
+
+echo -e "stop mw"
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+
+port=7200
+if [ -f /www/server/mdserver-web/data/port.pl ]; then
+ port=$(cat /www/server/mdserver-web/data/port.pl)
+fi
+
+n=0
+while [[ "$isStart" != "" ]];
+do
+ echo -e ".\c"
+ sleep 0.5
+ isStart=$(lsof -n -P -i:$port|grep LISTEN|grep -v grep|awk '{print $2}'|xargs)
+ let n+=1
+ if [ $n -gt 15 ];then
+ break;
+ fi
+done
+
+
+echo -e "start mw"
+cd /www/server/mdserver-web && bash cli.sh start
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [[ ! -f /etc/rc.d/init.d/mw ]];
+do
+ echo -e ".\c"
+ sleep 1
+ let n+=1
+ if [ $n -gt 20 ];then
+ echo -e "start mw fail"
+ exit 1
+ fi
+done
+echo -e "start mw success"
\ No newline at end of file
diff --git a/scripts/update/arch.sh b/scripts/update/arch.sh
new file mode 100644
index 000000000..edec63b06
--- /dev/null
+++ b/scripts/update/arch.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+
+
+# echo y | pacman -Sy yaourt
+# echo y | pacman -Sy python3
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+if [ -f /etc/rc.d/init.d/mw ];then
+ bash /etc/rc.d/init.d/mw stop && rm -rf /www/server/mdserver-web/scripts/init.d/mw && rm -rf /etc/rc.d/init.d/mw
+fi
+
+echo -e "start mw"
+cd /www/server/mdserver-web && bash cli.sh start
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [[ ! -f /etc/rc.d/init.d/mw ]];
+do
+ echo -e ".\c"
+ sleep 1
+ let n+=1
+ if [ $n -gt 20 ];then
+ echo -e "start mw fail"
+ exit 1
+ fi
+done
+echo -e "start mw success"
+
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw stop
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw start
\ No newline at end of file
diff --git a/scripts/update/centos.sh b/scripts/update/centos.sh
new file mode 100755
index 000000000..1ea9387e1
--- /dev/null
+++ b/scripts/update/centos.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+export LANG=en_US.UTF-8
+
+
+if [ -f /etc/motd ];then
+ echo "welcome to mdserver-web panel" > /etc/motd
+fi
+
+sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
+
+yum install -y curl-devel libmcrypt libmcrypt-devel python3-devel
+
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+if [ -f /etc/rc.d/init.d/mw ];then
+ bash /etc/rc.d/init.d/mw stop && rm -rf /www/server/mdserver-web/scripts/init.d/mw && rm -rf /etc/rc.d/init.d/mw
+fi
+
+echo -e "stop mw"
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+
+port=7200
+if [ -f /www/server/mdserver-web/data/port.pl ]; then
+ port=$(cat /www/server/mdserver-web/data/port.pl)
+fi
+
+n=0
+while [[ "$isStart" != "" ]];
+do
+ echo -e ".\c"
+ sleep 0.5
+ isStart=$(lsof -n -P -i:$port|grep LISTEN|grep -v grep|awk '{print $2}'|xargs)
+ let n+=1
+ if [ $n -gt 15 ];then
+ break;
+ fi
+done
+
+
+echo -e "start mw"
+cd /www/server/mdserver-web && bash cli.sh start
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [[ ! -f /etc/rc.d/init.d/mw ]];
+do
+ echo -e ".\c"
+ sleep 1
+ let n+=1
+ if [ $n -gt 20 ];then
+ echo -e "start mw fail"
+ exit 1
+ fi
+done
+echo -e "start mw success"
\ No newline at end of file
diff --git a/scripts/update/debian.sh b/scripts/update/debian.sh
new file mode 100644
index 000000000..dcc663cac
--- /dev/null
+++ b/scripts/update/debian.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+export DEBIAN_FRONTEND=noninteractive
+
+apt autoremove -y
+apt install -y locate
+if [ ! -d /usr/share/locale ];then
+ mkdir -d /usr/share/locale
+fi
+locale-gen en_US.UTF-8
+locale-gen zh_CN.UTF-8
+localedef -v -c -i en_US -f UTF-8 en_US.UTF-8 > /dev/null 2>&1
+export LC_CTYPE=en_US.UTF-8
+export LC_ALL=en_US.UTF-8
+export LANG=en_US.UTF-8
+
+# echo "LC_ALL=en_US.UTF-8" > /etc/default/locale
+# echo "LANG=en_US.UTF-8" > /etc/default/locale
+
+
+if grep -Eq "Ubuntu" /etc/*-release; then
+ sudo ln -sf /bin/bash /bin/sh
+ #sudo dpkg-reconfigure dash
+fi
+
+VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
+if [ "$VERSION_ID" == "9" ];then
+ sed "s/flask==2.0.3/flask==1.1.1/g" -i /www/server/mdserver-web/requirements.txt
+ sed "s/cryptography==3.3.2/cryptography==2.5/g" -i /www/server/mdserver-web/requirements.txt
+ sed "s/configparser==5.2.0/configparser==4.0.2/g" -i /www/server/mdserver-web/requirements.txt
+ sed "s/flask-socketio==5.2.0/flask-socketio==4.2.0/g" -i /www/server/mdserver-web/requirements.txt
+ sed "s/python-engineio==4.3.2/python-engineio==3.9.0/g" -i /www/server/mdserver-web/requirements.txt
+ # pip3 install -r /www/server/mdserver-web/requirements.txt
+fi
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+
+if [ -f /etc/rc.d/init.d/mw ];then
+ if [ -f /usr/bin/mw ];then
+ rm -rf /usr/bin/mw
+ fi
+ bash /etc/rc.d/init.d/mw stop && rm -rf /www/server/mdserver-web/scripts/init.d/mw && rm -rf /etc/rc.d/init.d/mw
+fi
+
+echo -e "stop mw"
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+
+port=7200
+if [ -f /www/server/mdserver-web/data/port.pl ];then
+ port=$(cat /www/server/mdserver-web/data/port.pl)
+fi
+
+n=0
+while [[ "$isStart" != "" ]];
+do
+ echo -e ".\c"
+ sleep 0.5
+ isStart=$(lsof -n -P -i:$port|grep LISTEN|grep -v grep|awk '{print $2}'|xargs)
+ let n+=1
+ if [ $n -gt 15 ];then
+ break;
+ fi
+done
+
+
+echo -e "start mw"
+cd /www/server/mdserver-web && bash cli.sh start
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [[ ! -f /etc/rc.d/init.d/mw ]];
+do
+ echo -e ".\c"
+ sleep 1
+ let n+=1
+ if [ $n -gt 20 ];then
+ echo -e "start mw fail"
+ exit 1
+ fi
+done
+echo -e "start mw success"
+
+systemctl daemon-reload
diff --git a/scripts/update/euler.sh b/scripts/update/euler.sh
new file mode 100755
index 000000000..1ea9387e1
--- /dev/null
+++ b/scripts/update/euler.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+export LANG=en_US.UTF-8
+
+
+if [ -f /etc/motd ];then
+ echo "welcome to mdserver-web panel" > /etc/motd
+fi
+
+sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
+
+yum install -y curl-devel libmcrypt libmcrypt-devel python3-devel
+
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+if [ -f /etc/rc.d/init.d/mw ];then
+ bash /etc/rc.d/init.d/mw stop && rm -rf /www/server/mdserver-web/scripts/init.d/mw && rm -rf /etc/rc.d/init.d/mw
+fi
+
+echo -e "stop mw"
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+
+port=7200
+if [ -f /www/server/mdserver-web/data/port.pl ]; then
+ port=$(cat /www/server/mdserver-web/data/port.pl)
+fi
+
+n=0
+while [[ "$isStart" != "" ]];
+do
+ echo -e ".\c"
+ sleep 0.5
+ isStart=$(lsof -n -P -i:$port|grep LISTEN|grep -v grep|awk '{print $2}'|xargs)
+ let n+=1
+ if [ $n -gt 15 ];then
+ break;
+ fi
+done
+
+
+echo -e "start mw"
+cd /www/server/mdserver-web && bash cli.sh start
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [[ ! -f /etc/rc.d/init.d/mw ]];
+do
+ echo -e ".\c"
+ sleep 1
+ let n+=1
+ if [ $n -gt 20 ];then
+ echo -e "start mw fail"
+ exit 1
+ fi
+done
+echo -e "start mw success"
\ No newline at end of file
diff --git a/scripts/update/fedora.sh b/scripts/update/fedora.sh
new file mode 100644
index 000000000..715239e0b
--- /dev/null
+++ b/scripts/update/fedora.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+
+if [ -f /etc/motd ];then
+ echo "welcome to mdserver-web panel" > /etc/motd
+fi
+
+sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+if [ -f /etc/rc.d/init.d/mw ];then
+ bash /etc/rc.d/init.d/mw stop && rm -rf /www/server/mdserver-web/scripts/init.d/mw && rm -rf /etc/rc.d/init.d/mw
+fi
+
+echo -e "stop mw"
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+
+port=7200
+if [ -f /www/server/mdserver-web/data/port.pl ];then
+ port=$(cat /www/server/mdserver-web/data/port.pl)
+fi
+
+n=0
+while [[ "$isStart" != "" ]];
+do
+ echo -e ".\c"
+ sleep 0.5
+ isStart=$(lsof -n -P -i:$port|grep LISTEN|grep -v grep|awk '{print $2}'|xargs)
+ let n+=1
+ if [ $n -gt 15 ];then
+ break;
+ fi
+done
+
+
+echo -e "start mw"
+cd /www/server/mdserver-web && bash cli.sh start
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [[ ! -f /etc/rc.d/init.d/mw ]];
+do
+ echo -e ".\c"
+ sleep 1
+ let n+=1
+ if [ $n -gt 20 ];then
+ echo -e "start mw fail"
+ exit 1
+ fi
+done
+echo -e "start mw success"
diff --git a/scripts/update/freebsd.sh b/scripts/update/freebsd.sh
new file mode 100644
index 000000000..bf519f07a
--- /dev/null
+++ b/scripts/update/freebsd.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+
+if [ -f /etc/rc.d/init.d/mw ];then
+ bash /etc/rc.d/init.d/mw stop && rm -rf /www/server/mdserver-web/scripts/init.d/mw && rm -rf /etc/rc.d/init.d/mw
+fi
+
+echo -e "start mw"
+cd /www/server/mdserver-web && bash cli.sh start
+isStart=`ps -aux|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [[ ! -f /etc/rc.d/init.d/mw ]];
+do
+ echo -e ".\c"
+ sleep 1
+ let n+=1
+ if [ $n -gt 20 ];then
+ echo -e "start mw fail"
+ exit 1
+ fi
+done
+echo -e "start mw success"
+
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw stop
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw start
diff --git a/scripts/update/macos.sh b/scripts/update/macos.sh
new file mode 100644
index 000000000..c90a59b5a
--- /dev/null
+++ b/scripts/update/macos.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+echo 'The development environment only needs to be downloaded again!'
+exit 0
\ No newline at end of file
diff --git a/scripts/update/opensuse.sh b/scripts/update/opensuse.sh
new file mode 100644
index 000000000..65bce9b2f
--- /dev/null
+++ b/scripts/update/opensuse.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+# zypper refresh
+
+
+# systemctl stop SuSEfirewall2
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+if [ -f /etc/rc.d/init.d/mw ];then
+ bash /etc/rc.d/init.d/mw stop && rm -rf /www/server/mdserver-web/scripts/init.d/mw && rm -rf /etc/rc.d/init.d/mw
+fi
+
+echo -e "start mw"
+cd /www/server/mdserver-web && bash cli.sh start
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [[ ! -f /etc/rc.d/init.d/mw ]];
+do
+ echo -e ".\c"
+ sleep 1
+ let n+=1
+ if [ $n -gt 20 ];then
+ echo -e "start mw fail"
+ exit 1
+ fi
+done
+echo -e "start mw success"
+
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw stop
+cd /www/server/mdserver-web && bash /etc/rc.d/init.d/mw start
diff --git a/scripts/update/rhel.sh b/scripts/update/rhel.sh
new file mode 100644
index 000000000..514c2c497
--- /dev/null
+++ b/scripts/update/rhel.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+export LANG=en_US.UTF-8
+
+sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
+
+
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+if [ -f /etc/rc.d/init.d/mw ];then
+ bash /etc/rc.d/init.d/mw stop && rm -rf /www/server/mdserver-web/scripts/init.d/mw && rm -rf /etc/rc.d/init.d/mw
+fi
+
+echo -e "stop mw"
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+
+port=7200
+if [ -f /www/server/mdserver-web/data/port.pl ]; then
+ port=$(cat /www/server/mdserver-web/data/port.pl)
+fi
+
+n=0
+while [[ "$isStart" != "" ]];
+do
+ echo -e ".\c"
+ sleep 0.5
+ isStart=$(lsof -n -P -i:$port|grep LISTEN|grep -v grep|awk '{print $2}'|xargs)
+ let n+=1
+ if [ $n -gt 15 ];then
+ break;
+ fi
+done
+
+
+echo -e "start mw"
+cd /www/server/mdserver-web && bash cli.sh start
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [[ ! -f /etc/rc.d/init.d/mw ]];
+do
+ echo -e ".\c"
+ sleep 1
+ let n+=1
+ if [ $n -gt 20 ];then
+ echo -e "start mw fail"
+ exit 1
+ fi
+done
+echo -e "start mw success"
\ No newline at end of file
diff --git a/scripts/update/rocky.sh b/scripts/update/rocky.sh
new file mode 100755
index 000000000..408ae8915
--- /dev/null
+++ b/scripts/update/rocky.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+
+if [ -f /etc/motd ];then
+ echo "welcome to mdserver-web panel" > /etc/motd
+fi
+
+sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
+
+
+
+yum install -y curl-devel libmcrypt libmcrypt-devel python36-devel
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+
+if [ -f /etc/rc.d/init.d/mw ];then
+ bash /etc/rc.d/init.d/mw stop && rm -rf /www/server/mdserver-web/scripts/init.d/mw && rm -rf /etc/rc.d/init.d/mw
+fi
+
+echo -e "stop mw"
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+
+port=7200
+if [ -f /www/server/mdserver-web/data/port.pl ]; then
+ port=$(cat /www/server/mdserver-web/data/port.pl)
+fi
+
+n=0
+while [[ "$isStart" != "" ]];
+do
+ echo -e ".\c"
+ sleep 0.5
+ isStart=$(lsof -n -P -i:$port|grep LISTEN|grep -v grep|awk '{print $2}'|xargs)
+ let n+=1
+ if [ $n -gt 15 ];then
+ break;
+ fi
+done
+
+
+echo -e "start mw"
+cd /www/server/mdserver-web && sh cli.sh start
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [[ ! -f /etc/init.d/mw ]];
+do
+ echo -e ".\c"
+ sleep 0.5
+ let n+=1
+ if [ $n -gt 15 ];then
+ break;
+ fi
+done
+echo -e "start mw success"
\ No newline at end of file
diff --git a/scripts/update/ubuntu.sh b/scripts/update/ubuntu.sh
new file mode 100644
index 000000000..1ee4def96
--- /dev/null
+++ b/scripts/update/ubuntu.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+export LANG=en_US.UTF-8
+export DEBIAN_FRONTEND=noninteractive
+
+# localedef -v -c -i en_US -f UTF-8 en_US.UTF-8
+
+if grep -Eq "Ubuntu" /etc/*-release; then
+ sudo ln -sf /bin/bash /bin/sh
+ #sudo dpkg-reconfigure dash
+fi
+
+
+cd /www/server/mdserver-web/scripts && bash lib.sh
+chmod 755 /www/server/mdserver-web/data
+
+
+if [ -f /etc/rc.d/init.d/mw ];then
+ if [ -f /usr/bin/mw ];then
+ rm -rf /usr/bin/mw
+ fi
+ bash /etc/rc.d/init.d/mw stop && rm -rf /www/server/mdserver-web/scripts/init.d/mw && rm -rf /etc/rc.d/init.d/mw
+fi
+
+echo -e "stop mw"
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+port=7200
+
+if [ -f /www/server/mdserver-web/data/port.pl ];then
+ port=$(cat /www/server/mdserver-web/data/port.pl)
+fi
+n=0
+while [[ "$isStart" != "" ]];
+do
+ echo -e ".\c"
+ sleep 0.5
+ isStart=$(lsof -n -P -i:$port|grep LISTEN|grep -v grep|awk '{print $2}'|xargs)
+ let n+=1
+ if [ $n -gt 15 ];then
+ break;
+ fi
+done
+
+
+echo -e "start mw"
+cd /www/server/mdserver-web && bash cli.sh start
+isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
+n=0
+while [[ ! -f /etc/rc.d/init.d/mw ]];
+do
+ echo -e ".\c"
+ sleep 1
+ let n+=1
+ if [ $n -gt 20 ];then
+ echo -e "start mw fail"
+ exit 1
+ fi
+done
+echo -e "start mw success"
+
+systemctl daemon-reload
diff --git a/scripts/update/unknow.sh b/scripts/update/unknow.sh
new file mode 100644
index 000000000..c9d5b5ca3
--- /dev/null
+++ b/scripts/update/unknow.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+LANG=en_US.UTF-8
+
+echo "update unkown server!!!"
\ No newline at end of file
diff --git a/scripts/update_dev.sh b/scripts/update_dev.sh
new file mode 100755
index 000000000..7fc281e8f
--- /dev/null
+++ b/scripts/update_dev.sh
@@ -0,0 +1,253 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
+export PATH
+
+RED='\033[31m'
+GREEN='\033[32m'
+YELLOW='\033[33m'
+BLUE='\033[34m'
+PLAIN='\033[0m'
+BOLD='\033[1m'
+SUCCESS='[\033[32mOK\033[0m]'
+COMPLETE='[\033[32mDONE\033[0m]'
+WARN='[\033[33mWARN\033[0m]'
+ERROR='[\033[31mERROR\033[0m]'
+WORKING='[\033[34m*\033[0m]'
+
+# LANG=en_US.UTF-8
+is64bit=`getconf LONG_BIT`
+
+startTime=`date +%s`
+
+if [ -f /www/server/mdserver-web/tools.py ];then
+ echo -e "存在旧版代码,不能安装!,已知风险的情况下"
+ echo -e "rm -rf /www/server/mdserver-web"
+ echo -e "可安装!"
+ exit 0
+fi
+
+_os=`uname`
+echo "use system: ${_os}"
+
+if [ "$EUID" -ne 0 ]
+ then echo "Please run as root!"
+ exit
+fi
+
+if [ ${_os} != "Darwin" ] && [ ! -d /www/server/mdserver-web/logs ]; then
+ mkdir -p /www/server/mdserver-web/logs
+fi
+
+LOG_FILE=/var/log/mw-update.log
+
+{
+
+HTTP_PREFIX="https://"
+LOCAL_ADDR=common
+cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"")
+if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then
+ LOCAL_ADDR=cn
+fi
+
+if [ "$LOCAL_ADDR" != "common" ];then
+ declare -A PROXY_URL
+ PROXY_URL["github_do"]="https://github.do/"
+ PROXY_URL["gh_llkk_cc"]="https://gh.llkk.cc/https://"
+ PROXY_URL["gh_felicity_ac_cn"]="https://gh.felicity.ac.cn/https://"
+ PROXY_URL["ghfast_top"]="https://ghfast.top/"
+ PROXY_URL["ghproxy_net"]="https://ghproxy.net/"
+ PROXY_URL["gh_927223_xyz"]="https://gh.927223.xyz/https://"
+ PROXY_URL["gh_proxy_net"]="https://gh-proxy.net/"
+
+ PROXY_URL["source"]="https://"
+
+
+ SOURCE_LIST_KEY_SORT_TMP=$(echo ${!PROXY_URL[@]} | tr ' ' '\n' | sort -n)
+ SOURCE_LIST_KEY=(${SOURCE_LIST_KEY_SORT_TMP//'\n'/})
+ SOURCE_LIST_LEN=${#PROXY_URL[*]}
+fi
+
+
+function AutoSizeStr(){
+ NAME_STR=$1
+ NAME_NUM=$2
+
+ NAME_STR_LEN=`echo "$NAME_STR" | wc -L`
+ NAME_NUM_LEN=`echo "$NAME_NUM" | wc -L`
+
+ fix_len=35
+ remaining_len=`expr $fix_len - $NAME_STR_LEN - $NAME_NUM_LEN`
+ FIX_SPACE=' '
+ for ((ass_i=1;ass_i<=$remaining_len;ass_i++))
+ do
+ FIX_SPACE="$FIX_SPACE "
+ done
+ echo -e " ❖ ${1}${FIX_SPACE}${2})"
+}
+
+function ChooseProxyURL(){
+ clear
+ echo -e '+---------------------------------------------------+'
+ echo -e '| |'
+ echo -e '| ============================================= |'
+ echo -e '| |'
+ echo -e '| 欢迎使用 Linux 一键安装mdserver-web面板源码 |'
+ echo -e '| |'
+ echo -e '| ============================================= |'
+ echo -e '| |'
+ echo -e '+---------------------------------------------------+'
+ echo -e ''
+ echo -e '#####################################################'
+ echo -e ''
+ echo -e ' 提供以下国内代理地址可供选择: '
+ echo -e ''
+ echo -e '#####################################################'
+ echo -e ''
+ cm_i=0
+ for V in ${SOURCE_LIST_KEY[@]}; do
+ num=`expr $cm_i + 1`
+ AutoSizeStr "${V}" "$num"
+ cm_i=`expr $cm_i + 1`
+ done
+ echo -e ''
+ echo -e '#####################################################'
+ echo -e ''
+ echo -e " 系统时间 ${BLUE}$(date "+%Y-%m-%d %H:%M:%S")${PLAIN}"
+ echo -e ''
+ echo -e '#####################################################'
+ CHOICE_A=$(echo -e "\n${BOLD}└─ 请选择并输入你想使用的代理地址 [ 1-${SOURCE_LIST_LEN} ]:${PLAIN}")
+
+ read -p "${CHOICE_A}" INPUT
+ # echo $INPUT
+ if [ "$INPUT" == "" ];then
+ INPUT=1
+ TMP_INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$TMP_INPUT]}
+ echo -e "\n默认选择[${BLUE}${INPUT_KEY}${PLAIN}]安装!"
+ fi
+
+ if [ "$INPUT" -lt "0" ];then
+ INPUT=1
+ TMP_INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$TMP_INPUT]}
+ echo -e "\n低于边界错误!选择[${BLUE}${INPUT_KEY}${PLAIN}]安装!"
+ sleep 2s
+ fi
+
+ if [ "$INPUT" -gt "${SOURCE_LIST_LEN}" ];then
+ INPUT=${SOURCE_LIST_LEN}
+ TMP_INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$TMP_INPUT]}
+ echo -e "\n超出边界错误!选择[${BLUE}${INPUT_KEY}${PLAIN}]安装!"
+ sleep 2s
+ fi
+
+ INPUT=`expr $INPUT - 1`
+ INPUT_KEY=${SOURCE_LIST_KEY[$INPUT]}
+ HTTP_PREFIX=${PROXY_URL[$INPUT_KEY]}
+}
+
+
+if [ "$LOCAL_ADDR" != "common" ];then
+ ChooseProxyURL
+
+ if [ "$HTTP_PREFIX" != "https://" ];then
+ DOMAIN=`echo $HTTP_PREFIX | sed 's|https://||g'`
+ DOMAIN=`echo $DOMAIN | sed 's|/||g'`
+ ping -c 3 $DOMAIN > /dev/null 2>&1
+ if [ "$?" != "0" ];then
+ echo "无效代理地址:${DOMAIN}"
+ exit
+ fi
+ fi
+fi
+
+if [ ${_os} == "Darwin" ]; then
+ OSNAME='macos'
+elif grep -Eqi "openSUSE" /etc/*-release; then
+ OSNAME='opensuse'
+ zypper refresh
+elif grep -Eqi "EulerOS" /etc/*-release || grep -Eqi "openEuler" /etc/*-release; then
+ OSNAME='euler'
+elif grep -Eqi "FreeBSD" /etc/*-release; then
+ OSNAME='freebsd'
+elif grep -Eqi "CentOS" /etc/issue || grep -Eqi "CentOS" /etc/*-release; then
+ OSNAME='centos'
+ yum install -y wget zip unzip
+elif grep -Eqi "Fedora" /etc/issue || grep -Eqi "Fedora" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget zip unzip
+elif grep -Eqi "Rocky" /etc/issue || grep -Eqi "Rocky" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget zip unzip
+elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eqi "AlmaLinux" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget zip unzip
+elif grep -Eqi "Anolis" /etc/issue || grep -Eqi "Anolis" /etc/*-release; then
+ OSNAME='rhel'
+ yum install -y wget curl zip unzip tar crontabs
+elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eqi "Amazon Linux" /etc/*-release; then
+ OSNAME='amazon'
+ yum install -y wget zip unzip
+elif grep -Eqi "Debian" /etc/issue || grep -Eqi "Debian" /etc/*-release; then
+ OSNAME='debian'
+ apt install -y wget zip unzip
+elif grep -Eqi "Ubuntu" /etc/issue || grep -Eqi "Ubuntu" /etc/*-release; then
+ OSNAME='ubuntu'
+ apt install -y wget zip unzip
+elif grep -Eqi "Raspbian" /etc/issue || grep -Eqi "Raspbian" /etc/*-release; then
+ OSNAME='raspbian'
+elif grep -Eqi "Alpine" /etc/issue || grep -Eqi "Alpine" /etc/*-release; then
+ OSNAME='alpine'
+else
+ OSNAME='unknow'
+fi
+
+echo "local:${LOCAL_ADDR}"
+
+CP_CMD=/usr/bin/cp
+if [ -f /bin/cp ];then
+ CP_CMD=/bin/cp
+fi
+
+if [ -f /tmp/dev.tar.gz ];then
+ rm -rf /tmp/dev.tar.gz
+fi
+
+if [ -d /tmp/mdserver-web-dev ];then
+ rm -rf /tmp/mdserver-web-dev
+fi
+
+echo "update mdserver-web dev code start"
+
+curl --insecure -sSLo /tmp/dev.tar.gz ${HTTP_PREFIX}github.com/midoks/mdserver-web/archive/refs/heads/dev.tar.gz
+cd /tmp && tar -zxvf /tmp/dev.tar.gz
+$CP_CMD -rf /tmp/mdserver-web-dev/* /www/server/mdserver-web
+rm -rf /tmp/dev.tar.gz
+rm -rf /tmp/mdserver-web-dev
+
+echo "update mdserver-web dev code end"
+
+
+#pip uninstall public
+echo "use system version: ${OSNAME}"
+cd /www/server/mdserver-web && bash scripts/update/${OSNAME}.sh
+
+bash /etc/rc.d/init.d/mw restart
+bash /etc/rc.d/init.d/mw default
+
+if [ -f /usr/bin/mw ];then
+ rm -rf /usr/bin/mw
+fi
+
+if [ ! -e /usr/bin/mw ]; then
+ if [ ! -f /usr/bin/mw ];then
+ ln -s /etc/rc.d/init.d/mw /usr/bin/mw
+ fi
+fi
+
+endTime=`date +%s`
+((outTime=($endTime-$startTime)/60))
+echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
+
+} 1> >(tee $LOG_FILE) 2>&1
\ No newline at end of file
diff --git a/ssl/README.md b/ssl/README.md
new file mode 100644
index 000000000..39c380ce8
--- /dev/null
+++ b/ssl/README.md
@@ -0,0 +1 @@
+# 数据目录
\ No newline at end of file
diff --git a/version/r3.6.txt b/version/r3.6.txt
new file mode 100644
index 000000000..0863401ee
--- /dev/null
+++ b/version/r3.6.txt
@@ -0,0 +1,39 @@
+cryptography==36.0.1
+flask==2.0.3
+pyOpenSSL==22.0.0
+requests==2.27.1
+gevent==22.10.2
+gunicorn==21.2.0
+setuptools>=33.1.1
+Werkzeug>=1.0.1,<3.0.0
+wheel>=0.37.1
+requests>=2.27.1
+urllib3>=1.21.1
+flask-session==0.3.2
+flask-helper==0.19
+flask-bcrypt==1.0.1
+flask-caching>=1.10.1
+cache==1.0.3
+gevent-websocket==0.10.1
+psutil==5.9.1
+chardet==3.0.4
+flask-sqlalchemy==2.3.2
+configparser==5.2.0
+python-engineio==4.3.2
+python-socketio>=4.2.0
+flask-socketio==5.2.0
+flask-sockets==0.2.1
+zmq==0.0.0
+paramiko>=2.8.0
+pymongo
+pymemcache
+redis
+pillow
+Jinja2>=2.11.2
+PyMySQL==1.0.2
+whitenoise==5.3.0
+pyotp
+pytz
+pyTelegramBotAPI
+telebot
+pyyaml
\ No newline at end of file
diff --git a/version/r3.7.txt b/version/r3.7.txt
new file mode 100644
index 000000000..0863401ee
--- /dev/null
+++ b/version/r3.7.txt
@@ -0,0 +1,39 @@
+cryptography==36.0.1
+flask==2.0.3
+pyOpenSSL==22.0.0
+requests==2.27.1
+gevent==22.10.2
+gunicorn==21.2.0
+setuptools>=33.1.1
+Werkzeug>=1.0.1,<3.0.0
+wheel>=0.37.1
+requests>=2.27.1
+urllib3>=1.21.1
+flask-session==0.3.2
+flask-helper==0.19
+flask-bcrypt==1.0.1
+flask-caching>=1.10.1
+cache==1.0.3
+gevent-websocket==0.10.1
+psutil==5.9.1
+chardet==3.0.4
+flask-sqlalchemy==2.3.2
+configparser==5.2.0
+python-engineio==4.3.2
+python-socketio>=4.2.0
+flask-socketio==5.2.0
+flask-sockets==0.2.1
+zmq==0.0.0
+paramiko>=2.8.0
+pymongo
+pymemcache
+redis
+pillow
+Jinja2>=2.11.2
+PyMySQL==1.0.2
+whitenoise==5.3.0
+pyotp
+pytz
+pyTelegramBotAPI
+telebot
+pyyaml
\ No newline at end of file
diff --git a/web/admin/__init__.py b/web/admin/__init__.py
new file mode 100644
index 000000000..0af3e9bc8
--- /dev/null
+++ b/web/admin/__init__.py
@@ -0,0 +1,212 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+import os
+import sys
+import json
+import time
+import uuid
+import logging
+
+from datetime import timedelta
+
+from flask import Flask
+from flask import request
+from flask import redirect
+from flask import Response
+from flask import Flask, abort, current_app, session, url_for
+from flask import Blueprint, render_template
+from flask import render_template_string
+
+from flask_socketio import SocketIO, emit, send
+
+from flask_caching import Cache
+from werkzeug.local import LocalProxy
+
+
+from admin.common import isLogined
+
+import core.mw as mw
+import config
+import utils.config as utils_config
+import thisdb
+
+# 初始化db
+from admin import setup
+setup.init()
+
+app = Flask(__name__, template_folder='templates/default')
+
+# 缓存配置
+cache = Cache(config={'CACHE_TYPE': 'simple'})
+cache.init_app(app, config={'CACHE_TYPE': 'simple'})
+
+# 静态文件配置
+from whitenoise import WhiteNoise
+app.wsgi_app = WhiteNoise(app.wsgi_app, root="../web/static/", prefix="static/", max_age=604800)
+app.jinja_env.trim_blocks = True
+
+# session配置
+# app.secret_key = uuid.UUID(int=uuid.getnode()).hex[-12:]
+app.config['SECRET_KEY'] = uuid.UUID(int=uuid.getnode()).hex[-12:]
+
+# app.config['sessions'] = dict()
+app.config['SESSION_PERMANENT'] = True
+app.config['SESSION_USE_SIGNER'] = True
+app.config['SESSION_KEY_PREFIX'] = 'MW_:'
+app.config['SESSION_COOKIE_NAME'] = "MW_VER_1"
+app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=31)
+app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 604800
+
+# db的配置
+# app.config['SQLALCHEMY_DATABASE_URI'] = mw.getSqitePrefix()+config.SQLITE_PATH+"?timeout=20" # 使用 SQLite 数据库
+app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
+
+# BASIC AUTH
+app.config['BASIC_AUTH_OPEN'] = False
+try:
+ basic_auth = thisdb.getOptionByJson('basic_auth', default={'open':False})
+ if basic_auth['open']:
+ app.config['BASIC_AUTH_OPEN'] = True
+except Exception as e:
+ pass
+
+# 加载模块
+from .submodules import get_submodules
+for module in get_submodules():
+ app.logger.info('Registering blueprint module: %s' % module)
+ if app.blueprints.get(module.name) is None:
+ app.register_blueprint(module)
+
+def sendAuthenticated():
+ # 发送http认证信息
+ request_host = mw.getHostAddr()
+ result = Response('', 401, {'WWW-Authenticate': 'Basic realm="%s"' % request_host.strip()})
+ if not 'login' in session and not 'admin_auth' in session:
+ session.clear()
+ return result
+
+@app.before_request
+def requestCheck():
+ request.start_time = time.time()
+
+ admin_close = thisdb.getOption('admin_close')
+ if admin_close == 'yes':
+ if not request.path.startswith('/close'):
+ return redirect('/close')
+ # 自定义basic auth认证
+ if app.config['BASIC_AUTH_OPEN']:
+ basic_auth = thisdb.getOptionByJson('basic_auth', default={'open':False})
+ if not basic_auth['open']:
+ return
+
+ auth = request.authorization
+ if request.path in ['/download', '/hook', '/down']:
+ return
+ if not auth:
+ return sendAuthenticated()
+
+ salt = basic_auth['salt']
+ basic_user = mw.md5(auth.username.strip() + salt)
+ basic_pwd = mw.md5(auth.password.strip() + salt)
+ if basic_user != basic_auth['basic_user'] or basic_pwd != basic_auth['basic_pwd']:
+ return sendAuthenticated()
+
+ # domain_check = mw.checkDomainPanel()
+ # if domain_check:
+ # return domain_check
+
+
+
+@app.after_request
+def requestAfter(response):
+ response.headers['soft'] = config.APP_NAME
+ response.headers['mw-version'] = config.APP_VERSION
+ response.headers['X-Response-Time'] = round(time.time() - request.start_time, 4)
+ return response
+
+
+@app.errorhandler(404)
+def page_unauthorized(error):
+ from flask import redirect
+ return redirect('/', code=302)
+ # return render_template_string('404 not found', error_info=error), 404
+
+
+# 设置模板全局变量
+@app.context_processor
+def inject_global_variables():
+ app_ver = config.APP_VERSION
+ if mw.isDebugMode():
+ app_ver = app_ver + str(time.time())
+
+ data = utils_config.getGlobalVar()
+ g_config = {
+ 'version': app_ver,
+ 'title' : data['title'],
+ 'ip' : data['ip']
+ }
+ return dict(config=g_config, data=data)
+
+# webssh
+# socketio = SocketIO(manage_session=False, async_mode='threading',
+# logger=False, engineio_logger=False, debug=False,
+# ping_interval=25, ping_timeout=120)
+socketio = SocketIO(logger=False,
+ engineio_logger=False,
+ cors_allowed_origins="*")
+socketio.init_app(app)
+
+@socketio.on('webssh_websocketio')
+def webssh_websocketio(data):
+ if not isLogined():
+ emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'})
+ return
+ import utils.ssh.ssh_terminal as ssh_terminal
+ shell_client = ssh_terminal.ssh_terminal.instance()
+ shell_client.run(request.sid, data)
+ return
+
+
+@socketio.on('webssh')
+def webssh(data):
+ if not isLogined():
+ emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'})
+ return None
+
+ import utils.ssh.ssh_local as ssh_local
+ shell = ssh_local.ssh_local.instance()
+ shell.run(data)
+ return
+
+
+# File logging
+logger = logging.getLogger('werkzeug')
+logger.setLevel(config.CONSOLE_LOG_LEVEL)
+
+from utils.enhanced_log_rotation import EnhancedRotatingFileHandler
+fh = EnhancedRotatingFileHandler(config.LOG_FILE,
+ config.LOG_ROTATION_SIZE,
+ config.LOG_ROTATION_AGE,
+ config.LOG_ROTATION_MAX_LOG_FILES)
+fh.setLevel(config.FILE_LOG_LEVEL)
+app.logger.addHandler(fh)
+logger.addHandler(fh)
+
+# Console logging
+ch = logging.StreamHandler()
+ch.setLevel(config.CONSOLE_LOG_LEVEL)
+ch.setFormatter(logging.Formatter(config.CONSOLE_LOG_FORMAT))
+
+# Log the startup
+app.logger.info('########################################################')
+app.logger.info('Starting %s v%s...', config.APP_NAME, config.APP_VERSION)
+app.logger.info('########################################################')
+app.logger.debug("Python syspath: %s", sys.path)
diff --git a/web/admin/common.py b/web/admin/common.py
new file mode 100644
index 000000000..235bbbdac
--- /dev/null
+++ b/web/admin/common.py
@@ -0,0 +1,37 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+import time
+
+from admin import session
+import thisdb
+
+def isLogined():
+ if 'login' in session and session['login'] == True and 'username' in session:
+ username = session['username']
+ info = thisdb.getUserByName(username)
+ if info is None:
+ return False
+
+ # print(userInfo)
+ if info['name'] != session['username']:
+ return False
+
+ now_time = int(time.time())
+
+ if 'overdue' in session and now_time > session['overdue']:
+ # 自动续期
+ session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60
+ return False
+
+ if 'tmp_login_expire' in session and now_time > int(session['tmp_login_expire']):
+ session.clear()
+ return False
+ return True
\ No newline at end of file
diff --git a/web/admin/crontab/__init__.py b/web/admin/crontab/__init__.py
new file mode 100644
index 000000000..b43c89e58
--- /dev/null
+++ b/web/admin/crontab/__init__.py
@@ -0,0 +1,131 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+
+from flask import Blueprint, render_template
+from flask import request
+
+from admin.user_login_check import panel_login_required
+
+from utils.crontab import crontab as MwCrontab
+import core.mw as mw
+import thisdb
+
+blueprint = Blueprint('crontab', __name__, url_prefix='/crontab', template_folder='../../templates')
+@blueprint.route('/index', endpoint='index')
+@panel_login_required
+def index():
+ name = thisdb.getOption('template', default='default')
+ return render_template('%s/crontab.html' % name)
+
+# 计划任务列表
+@blueprint.route('/list', endpoint='list', methods=['POST'])
+@panel_login_required
+def list():
+ page = request.args.get('p', '1').strip()
+ limit = request.args.get('limit', '10').strip()
+ return MwCrontab.instance().getCrontabList(page=int(page),size=int(limit))
+
+# 计划任务日志
+@blueprint.route('/logs', endpoint='logs', methods=['POST'])
+def logs():
+ cron_id = request.form.get('id', '')
+ return MwCrontab.instance().cronLog(cron_id)
+
+# 删除计划任务
+@blueprint.route('/del', endpoint='del', methods=['POST'])
+def crontab_del():
+ cron_id = request.form.get('id', '')
+ return MwCrontab.instance().delete(cron_id)
+
+# 删除计划任务日志
+@blueprint.route('/del_logs', endpoint='del_logs', methods=['POST'])
+def del_logs():
+ cron_id = request.form.get('id', '')
+ return MwCrontab.instance().delLogs(cron_id)
+
+
+# 设置计划任务状态
+@blueprint.route('/set_cron_status', endpoint='set_cron_status', methods=['POST'])
+def set_cron_status():
+ cron_id = request.form.get('id', '')
+ return MwCrontab.instance().setCronStatus(cron_id)
+
+# 设置计划任务状态
+@blueprint.route('/get_data_list', endpoint='get_data_list', methods=['POST'])
+def get_data_list():
+ stype = request.form.get('type', '')
+ return MwCrontab.instance().getDataList(stype)
+
+
+# 获取计划任务
+@blueprint.route('/get_crond_find', endpoint='get_crond_find', methods=['POST'])
+def get_crond_find():
+ cron_id = request.form.get('id', '')
+ data = MwCrontab.instance().getCrondFind(cron_id)
+ return data
+
+# 修改计划任务
+@blueprint.route('/modify_crond', endpoint='modify_crond', methods=['POST'])
+def modify_crond():
+ request_data = {}
+
+ request_data['name'] = request.form.get('name', '')
+ request_data['type'] = request.form.get('type', '')
+ request_data['week'] = request.form.get('week', '')
+ request_data['where1'] = request.form.get('where1', '')
+ request_data['hour'] = request.form.get('hour', '')
+ request_data['minute'] = request.form.get('minute', '')
+ request_data['save'] = request.form.get('save', '')
+ request_data['backup_to'] = request.form.get('backup_to', '')
+ request_data['stype'] = request.form.get('stype', '')
+ request_data['sname'] = request.form.get('sname', '')
+ request_data['sbody'] = request.form.get('sbody', '')
+ request_data['url_address'] = request.form.get('url_address', '')
+ request_data['attr'] = request.form.get('attr', '')
+ cron_id = request.form.get('id', '')
+ data = MwCrontab.instance().modifyCrond(cron_id,request_data)
+ return data
+
+# 执行计划任务
+@blueprint.route('/start_task', endpoint='start_task', methods=['POST'])
+def start_task():
+ cron_id = request.form.get('id', '')
+ return MwCrontab.instance().startTask(cron_id)
+
+# 添加计划任务
+@blueprint.route('/add', endpoint='add', methods=['POST'])
+@panel_login_required
+def add():
+ request_data = {}
+ request_data['name'] = request.form.get('name', '')
+ request_data['type'] = request.form.get('type', '')
+ request_data['week'] = request.form.get('week', '')
+ request_data['where1'] = request.form.get('where1', '')
+ request_data['hour'] = request.form.get('hour', '')
+ request_data['minute'] = request.form.get('minute', '')
+ request_data['save'] = request.form.get('save', '')
+ request_data['backup_to'] = request.form.get('backup_to', '')
+ request_data['stype'] = request.form.get('stype', '')
+ request_data['sname'] = request.form.get('sname', '')
+ request_data['sbody'] = request.form.get('sbody', '')
+ request_data['url_address'] = request.form.get('url_address', '')
+ request_data['attr'] = request.form.get('attr', '')
+
+ info = thisdb.getCronByName(request_data['name'])
+ if info is not None:
+ return mw.returnData(False, '任务名称重复')
+
+ cron_id = MwCrontab.instance().add(request_data)
+ if cron_id > 0:
+ return mw.returnData(True, '添加成功')
+ return mw.returnData(False, '添加失败')
+
+
diff --git a/web/admin/dashboard/__init__.py b/web/admin/dashboard/__init__.py
new file mode 100644
index 000000000..77a962a50
--- /dev/null
+++ b/web/admin/dashboard/__init__.py
@@ -0,0 +1,13 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+
+from .dashboard import *
+from .login import *
diff --git a/web/admin/dashboard/dashboard.py b/web/admin/dashboard/dashboard.py
new file mode 100644
index 000000000..b047eaf96
--- /dev/null
+++ b/web/admin/dashboard/dashboard.py
@@ -0,0 +1,140 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+import io
+import time
+import base64
+import json
+import os
+import sys
+
+from flask import Blueprint, render_template
+from flask import make_response
+from flask import redirect
+from flask import Response
+from flask import request,g
+
+from admin.common import isLogined
+from admin.user_login_check import panel_login_required
+from admin import cache,session
+
+import core.mw as mw
+import thisdb
+import utils.system as usys
+
+
+blueprint = Blueprint('dashboard', __name__, url_prefix='/', template_folder='../../templates')
+@blueprint.route('/', endpoint='index', methods=['GET'])
+@panel_login_required
+def index():
+ name = thisdb.getOption('template', default='default')
+ return render_template('%s/index.html' % name)
+
+
+@blueprint.route('/overview_stats', endpoint='overview_stats', methods=['GET'])
+@panel_login_required
+def overview_stats():
+ mem_info = usys.getMemInfo()
+ cpu_info = usys.getCpuInfo(interval=0)
+
+ mem_total_gb = 0
+ try:
+ mem_total_gb = round(float(mem_info.get('memTotal', 0)) / (1024 * 1024 * 1024), 2)
+ except Exception:
+ mem_total_gb = 0
+
+ data = {
+ 'site_count': thisdb.getSitesCount(),
+ 'pending_task_count': thisdb.getTaskUnexecutedCount(),
+ 'crontab_count': mw.M('crontab').count(),
+ 'firewall_count': mw.M('firewall').count(),
+ 'enabled_app_count': mw.M('app').where('status=?', (1,)).count(),
+ 'cpu_num': cpu_info[1] if isinstance(cpu_info, (list, tuple)) and len(cpu_info) > 1 else 0,
+ 'mem_total_gb': mem_total_gb,
+ }
+ return mw.returnData(True, 'ok', data)
+
+# 安全路径
+@blueprint.route('/',endpoint='admin_safe_path',methods=['GET'])
+def admin_safe_path(path):
+ login = request.args.get('login', '')
+ if login != '':
+ try:
+ # print(login)
+ login_str = base64.b64decode(login)
+ login_str = login_str.decode('utf-8')
+ data = json.loads(login_str)
+
+ time_now = time.time() * 1000
+ time_diff = time_now - data['time']
+
+ if time_diff > 2000:
+ return redirect('/')
+
+
+ info = thisdb.getUserByName(data['username'])
+ if info is None:
+ return redirect('/')
+
+ if info['password'] != mw.md5(data['password']):
+ return redirect('/')
+
+ session['login'] = True
+ session['username'] = info['name']
+ session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60
+
+ thisdb.updateUserLoginTime()
+ return redirect('/')
+ except Exception as e:
+ pass
+
+
+ db_path = thisdb.getOption('admin_path')
+ name = thisdb.getOption('template', default='default')
+ if isLogined():
+ return redirect('/')
+ if db_path == path:
+ return render_template('%s/login.html' % name)
+
+ unauthorized_status = thisdb.getOption('unauthorized_status')
+ if unauthorized_status == '0':
+ return render_template('%s/path.html' % name)
+ return Response(status=int(unauthorized_status))
+
+# 仅针对webhook插件
+@blueprint.route("/hook", methods=['POST', 'GET'])
+def webhook():
+ # 兼容获取关键数据
+ access_key = request.args.get('access_key', '').strip()
+ if access_key == '':
+ access_key = request.form.get('access_key', '').strip()
+
+ params = request.args.get('params', '').strip()
+ if params == '':
+ params = request.form.get('params', '').strip()
+
+ input_args = {
+ 'access_key': access_key,
+ 'params': params,
+ }
+
+ wh_install_path = mw.getServerDir() + '/webhook'
+ if not os.path.exists(wh_install_path):
+ return mw.returnData(False, '请先安装WebHook插件!')
+
+ package = mw.getPanelDir() + "/plugins/webhook"
+ if not package in sys.path:
+ sys.path.append(package)
+
+ try:
+ import webhook_index
+ return webhook_index.runShellArgs(input_args)
+ except Exception as e:
+ return str(e)
diff --git a/web/admin/dashboard/login.py b/web/admin/dashboard/login.py
new file mode 100644
index 000000000..fdeb25508
--- /dev/null
+++ b/web/admin/dashboard/login.py
@@ -0,0 +1,248 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+import io
+import time
+
+from flask import Blueprint, render_template
+from flask import make_response
+from flask import redirect
+from flask import Response
+from flask import request,g
+
+from admin.common import isLogined
+from admin.user_login_check import panel_login_required
+from admin import cache,session
+
+import core.mw as mw
+import thisdb
+
+from .dashboard import blueprint
+
+
+def getErrorNum(key, limit=None):
+ key = mw.md5(key)
+ num = cache.get(key)
+ if not num:
+ num = 0
+ if not limit:
+ return num
+ if limit > num:
+ return True
+ return False
+
+
+def setErrorNum(key, empty=False, expire=3600):
+ key = mw.md5(key)
+ num = cache.get(key)
+ if not num:
+ num = 0
+ else:
+ if empty:
+ cache.delete(key)
+ return True
+ cache.set(key, num + 1, expire)
+ return True
+
+def login_temp_user(token):
+ if len(token) != 32:
+ return '错误的参数!'
+
+ skey = mw.getClientIp() + '_temp_login'
+ if not getErrorNum(skey, 10):
+ return '连续10次验证失败,禁止1小时'
+
+ stime = int(time.time())
+
+ tmp_data = thisdb.getTempLoginByToken(token)
+ if not tmp_data:
+ setErrorNum(skey)
+ return '验证失败!'
+
+ if stime > int(tmp_data['expire']):
+ setErrorNum(skey)
+ return "过期"
+
+ user_data = thisdb.getUserById(1)
+ login_addr = mw.getClientIp() + ":" + str(request.environ.get('REMOTE_PORT'))
+ mw.writeLog('用户临时登录', "登录成功,帐号:{1},登录IP:{2}",(user_data['name'], login_addr))
+
+ mw.M('temp_login').where('id=?',(tmp_data['id'],)).update({"login_time": stime, 'state': 1, 'login_addr': login_addr})
+
+ session['login'] = True
+ session['username'] = user_data['name']
+ session['tmp_login'] = True
+ session['tmp_login_id'] = str(tmp_data['id'])
+ session['tmp_login_expire'] = int(tmp_data['expire'])
+ session['uid'] = user_data['id']
+
+ return redirect('/')
+
+# 登录页: 当设置了安全路径,本页失效。
+@blueprint.route('/login')
+def login():
+ name = thisdb.getOption('template', default='default')
+
+ # 临时登录功能
+ token = request.args.get('tmp_token', '').strip()
+ if token != '':
+ return login_temp_user(token)
+
+ # 注销登录
+ signout = request.args.get('signout', '')
+ if signout == 'True':
+ session.clear()
+ session['login'] = False
+ session['overdue'] = 0
+
+ admin_path = thisdb.getOption('admin_path')
+ if admin_path == '':
+ return render_template('%s/login.html' % name)
+ else:
+ unauthorized_status = thisdb.getOption('unauthorized_status')
+ if unauthorized_status == '0':
+ return render_template('%s/path.html' % name)
+ return Response(status=int(unauthorized_status))
+
+@blueprint.route('/close')
+def close():
+ name = thisdb.getOption('template', default='default')
+ admin_close = thisdb.getOption('admin_close')
+ if admin_close == 'no':
+ return redirect('/', code=302)
+ return render_template('%s/close.html' % name)
+
+
+# 验证码
+@blueprint.route('/code')
+def code():
+ import utils.vilidate as vilidate
+ vie = vilidate.vieCode()
+ codeImage = vie.GetCodeImage(80, 4)
+ out = io.BytesIO()
+ codeImage[0].save(out, "png")
+ session['code'] = mw.md5(''.join(codeImage[1]).lower())
+
+ img = Response(out.getvalue(), headers={'Content-Type': 'image/png'})
+ return make_response(img)
+
+# 检查是否登录
+@blueprint.route('/check_login',methods=['GET','POST'])
+def check_login():
+ if isLogined():
+ return mw.returnData(True,'已登录')
+ return mw.returnData(False,'未登录')
+
+@blueprint.route("/verify_login", methods=['POST'])
+def verifyLogin():
+ import pyotp
+
+ username = request.form.get('username', '').strip()
+ password = request.form.get('password', '').strip()
+ password = mw.md5(password)
+
+ info = thisdb.getUserByRoot()
+ if info['name'] != username or info['password'] != password:
+ return mw.returnJson(-1, "密码错误?")
+
+ auth = request.form.get('auth', '').strip()
+ two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False})
+ if two_step_verification['open']:
+ sec = mw.deDoubleCrypt('mdserver-web', two_step_verification['secret'])
+ totp = pyotp.TOTP(sec)
+ if totp.verify(auth):
+ session['login'] = True
+ session['username'] = info['name']
+ session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60
+
+ thisdb.updateUserLoginTime()
+ return mw.returnData(1, '二次验证成功!')
+ return mw.returnData(-1, '二次验证失败!')
+
+# 执行登录操作
+@blueprint.route('/do_login', endpoint='do_login', methods=['POST'])
+def do_login():
+ admin_close = thisdb.getOption('admin_close')
+ if admin_close == 'yes':
+ return mw.returnData(False, '面板已经关闭!')
+
+ username = request.form.get('username', '').strip()
+ password = request.form.get('password', '').strip()
+ code = request.form.get('code', '').strip()
+
+ login_cache_count = 5
+ login_cache_limit = cache.get('login_cache_limit')
+
+ if 'code' in session:
+ if session['code'] != mw.md5(code):
+ if login_cache_limit == None:
+ login_cache_limit = 1
+ else:
+ login_cache_limit = int(login_cache_limit) + 1
+
+ if login_cache_limit >= login_cache_count:
+ thisdb.setOption('admin_close', 'yes')
+ return mw.returnData(False, '面板已经关闭!')
+
+ cache.set('login_cache_limit', login_cache_limit, timeout=10000)
+ login_cache_limit = cache.get('login_cache_limit')
+ login_err_msg = mw.getInfo("验证码错误,您还可以尝试[{1}]次!", (str(login_cache_count - login_cache_limit)))
+ mw.writeLog('用户登录', login_err_msg)
+ return mw.returnData(False, login_err_msg)
+
+ info = thisdb.getUserByName(username)
+ password = mw.md5(password)
+
+ if info is None:
+ msg = mw.getInfo("密码错误 ,帐号:{1},密码:{2},登录IP:{3}", (username, '******', request.remote_addr))
+ if login_cache_limit == None:
+ login_cache_limit = 1
+ else:
+ login_cache_limit = int(login_cache_limit) + 1
+
+ if login_cache_limit >= login_cache_count:
+ thisdb.setOption('admin_close', 'yes')
+ return mw.returnData(False, '面板已经关闭!')
+
+ cache.set('login_cache_limit', login_cache_limit, timeout=10000)
+ login_cache_limit = cache.get('login_cache_limit')
+ mw.writeLog('用户登录', msg)
+ return mw.returnData(-1, mw.getInfo("用户名或密码错误,您还可以尝试[{1}]次!", (str(login_cache_count - login_cache_limit))))
+
+ # print(info)
+ if info['name'] != username or info['password'] != password:
+ msg = mw.getInfo("密码错误 ,帐号:{1},密码:{2},登录IP:{3}", (username, '******', request.remote_addr))
+
+ if login_cache_limit == None:
+ login_cache_limit = 1
+ else:
+ login_cache_limit = int(login_cache_limit) + 1
+
+ if login_cache_limit >= login_cache_count:
+ thisdb.setOption('admin_close', 'yes')
+ return mw.returnData(False, '面板已经关闭!')
+
+ cache.set('login_cache_limit', login_cache_limit, timeout=10000)
+ login_cache_limit = cache.get('login_cache_limit')
+ mw.writeLog('用户登录', msg)
+ return mw.returnData(-1, mw.getInfo("用户名或密码错误,您还可以尝试[{1}]次!", (str(login_cache_count - login_cache_limit))))
+
+ cache.delete('login_cache_limit')
+ # 二步验证密钥
+ two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False})
+ if two_step_verification['open']:
+ return mw.returnData(2, '需要两步验证!')
+
+ session['login'] = True
+ session['username'] = info['name']
+ session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60
+
+ thisdb.updateUserLoginTime()
+ return mw.returnData(1, '登录成功,正在跳转...')
diff --git a/web/admin/files/__init__.py b/web/admin/files/__init__.py
new file mode 100644
index 000000000..450673456
--- /dev/null
+++ b/web/admin/files/__init__.py
@@ -0,0 +1,12 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+from .files import *
+from .recycle import *
diff --git a/web/admin/files/files.py b/web/admin/files/files.py
new file mode 100644
index 000000000..e4debf4f7
--- /dev/null
+++ b/web/admin/files/files.py
@@ -0,0 +1,334 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+import os
+import time
+import json
+
+from flask import Blueprint, render_template
+from flask import request
+from flask import make_response
+from flask import send_file
+from flask import send_from_directory
+
+from werkzeug.utils import secure_filename
+
+from admin.user_login_check import panel_login_required
+from admin import session
+
+import core.mw as mw
+import utils.file as file
+import thisdb
+
+blueprint = Blueprint('files', __name__, url_prefix='/files', template_folder='../../templates')
+@blueprint.route('/index', endpoint='index')
+@panel_login_required
+def index():
+ name = thisdb.getOption('template', default='default')
+ return render_template('%s/files.html' % name)
+
+# 获取文件内容
+@blueprint.route('/check_exists_files', endpoint='check_exists_files', methods=['POST'])
+@panel_login_required
+def check_exists_files():
+ dfile = request.form.get('dfile', '')
+ filename = request.form.get('filename', '')
+ data = []
+ filesx = []
+ if filename == '':
+ filesx = json.loads(session['selected']['data'])
+ else:
+ filesx.append(filename)
+
+ for fn in filesx:
+ if fn == '.':
+ continue
+ filename = dfile + '/' + fn
+ if os.path.exists(filename):
+ tmp = {}
+ stat = os.stat(filename)
+ tmp['filename'] = fn
+ tmp['size'] = os.path.getsize(filename)
+ tmp['mtime'] = str(int(stat.st_mtime))
+ data.append(tmp)
+ return mw.returnData(True, 'ok', data)
+
+
+# 粘贴内容
+@blueprint.route('/batch_paste', endpoint='batch_paste', methods=['POST'])
+@panel_login_required
+def batch_paste():
+ path = request.form.get('path', '')
+ stype = request.form.get('type', '')
+ return file.batchPaste(path, stype)
+
+# 压缩文件
+@blueprint.route('/zip', endpoint='zip', methods=['POST'])
+@panel_login_required
+def zip():
+ sfile = request.form.get('sfile', '')
+ dfile = request.form.get('dfile', '')
+ stype = request.form.get('type', '')
+ path = request.form.get('path', '')
+ return file.zip(sfile, dfile, stype, path)
+
+# 文件权限查看
+@blueprint.route('/file_access', endpoint='file_access', methods=['POST'])
+@panel_login_required
+def file_access():
+ filename = request.form.get('filename', '')
+ data = file.getAccess(filename)
+ data['sys_users'] = file.getSysUserList()
+ return data
+
+# 设置权限
+@blueprint.route('/set_file_access', endpoint='set_file_access', methods=['POST'])
+@panel_login_required
+def set_file_access():
+ if mw.isAppleSystem():
+ return mw.returnData(True, '开发机不设置!')
+
+ filename = request.form.get('filename', '')
+ user = request.form.get('user', '')
+ access = request.form.get('access', '755')
+ return file.setFileAccess(filename, user, access)
+
+
+# 复制文件内容
+@blueprint.route('/copy_file', endpoint='copy_file', methods=['POST'])
+@panel_login_required
+def copy_file():
+ sfile = request.form.get('sfile', '')
+ dfile = request.form.get('dfile', '')
+ return file.copyFile(sfile, dfile)
+
+# 获取文件内容
+@blueprint.route('/get_body', endpoint='get_body', methods=['POST'])
+@panel_login_required
+def get_body():
+ path = request.form.get('path', '')
+ return file.getFileBody(path)
+
+# 获取文件内容
+@blueprint.route('/save_body', endpoint='save_body', methods=['POST'])
+@panel_login_required
+def save_body():
+ path = request.form.get('path', '')
+ data = request.form.get('data', '')
+ encoding = request.form.get('encoding', '')
+ return file.saveBody(path,data,encoding)
+
+# 获取文件内容(最新行数)
+@blueprint.route('/get_last_body', endpoint='get_file_last_body', methods=['POST'])
+@panel_login_required
+def get_file_last_body():
+ path = request.form.get('path', '')
+ line = request.form.get('line', '100')
+
+ if not os.path.exists(path):
+ return mw.returnData(False, '文件不存在', (path,))
+
+ try:
+ data = mw.getLastLine(path, int(line))
+ return mw.returnData(True, 'OK', data)
+ except Exception as ex:
+ return mw.returnData(False, '无法正确读取文件!' + str(ex))
+
+
+# 获取文件列表
+@blueprint.route('/get_dir', endpoint='get_dir', methods=['POST'])
+@panel_login_required
+def get_dir():
+ path = request.form.get('path', '')
+ if not os.path.exists(path):
+ path = mw.getFatherDir() + '/wwwroot'
+ search = request.form.get('search', '').strip().lower()
+ search_all = request.form.get('all', '').strip().lower()
+ page = request.form.get('p', '1').strip().lower()
+ row = request.form.get('row', '10')
+ order = request.form.get('order', '')
+
+ if search_all == 'yes' and search != '':
+ dir_list = file.getAllDirList(path, int(page), int(row), order, search)
+ else:
+ dir_list = file.getDirList(path, int(page), int(row), order, search)
+
+ dir_list['page'] = mw.getPage({'p':page, 'row': row, 'tojs':'getFiles', 'count': dir_list['count']}, '1,2,3,4,5,6,7,8')
+ return dir_list
+
+# 解压ZIP
+@blueprint.route('/unzip', endpoint='unzip', methods=['POST'])
+@panel_login_required
+def unzip():
+ sfile = request.form.get('sfile', '')
+ dfile = request.form.get('dfile', '')
+ stype = request.form.get('type', '')
+ path = request.form.get('path', '')
+ return file.unzip(sfile, dfile, stype, path)
+
+# 解压可解压文件
+@blueprint.route('/uncompress', endpoint='uncompress', methods=['POST'])
+@panel_login_required
+def uncompress():
+ sfile = request.form.get('sfile', '')
+ dfile = request.form.get('dfile', '')
+ path = request.form.get('path', '')
+ return file.uncompress(sfile, dfile, path)
+
+# 批量操作
+@blueprint.route('/set_batch_data', endpoint='set_batch_data', methods=['POST'])
+@panel_login_required
+def set_batch_data():
+ path = request.form.get('path', '')
+ stype = request.form.get('type', '')
+ access = request.form.get('access', '')
+ user = request.form.get('user', '')
+ data = request.form.get('data')
+ return file.setBatchData(path, stype, access, user, data)
+
+# 上传文件
+@blueprint.route('/upload_file', endpoint='upload_file', methods=['POST'])
+@panel_login_required
+def upload_file():
+ path = request.args.get('path', '')
+ if not os.path.exists(path):
+ os.makedirs(path)
+ f = request.files['zunfile']
+ filename = os.path.join(path, f.filename)
+
+ s_path = path
+ if os.path.exists(filename):
+ s_path = filename
+ p_stat = os.stat(s_path)
+
+ # print(filename)
+ f.save(filename)
+ os.chown(filename, p_stat.st_uid, p_stat.st_gid)
+ os.chmod(filename, p_stat.st_mode)
+
+ msg = mw.getInfo('上传文件[{1}] 到 [{2}]成功!', (filename, path))
+ mw.writeLog('文件管理', msg)
+ return mw.returnData(True, '上传成功!')
+
+
+# 上传文件
+@blueprint.route('/upload_segment', endpoint='upload_segment', methods=['POST'])
+@panel_login_required
+def upload_segment():
+ path = request.form.get('path', '')
+ name = request.form.get('name', '')
+ size = request.form.get('size')
+ start = request.form.get('start')
+ dir_mode = request.form.get('dir_mode', '')
+ file_mode = request.form.get('file_mode', '')
+ b64_data = request.form.get('b64_data', '0')
+ upload_files = request.files.getlist("blob")
+ return file.uploadSegment(path,name,size,start,dir_mode,file_mode,b64_data,upload_files)
+
+
+# 修改文件名
+@blueprint.route('/mv_file', endpoint='mv_file', methods=['POST'])
+@panel_login_required
+def mv_file():
+ sfile = request.form.get('sfile', '')
+ dfile = request.form.get('dfile', '')
+ return file.mvFile(sfile, dfile)
+
+# 创建文件
+@blueprint.route('/create_file', endpoint='create_file', methods=['POST'])
+@panel_login_required
+def create_file():
+ path = request.form.get('path', '')
+ return file.createFile(path)
+
+
+# 创建目录
+@blueprint.route('/create_dir', endpoint='create_dir', methods=['POST'])
+@panel_login_required
+def create_dir():
+ path = request.form.get('path', '')
+ return file.createDir(path)
+
+
+# 获取站点日志目录
+@blueprint.route('/get_dir_size', endpoint='get_dir_size', methods=['POST'])
+@panel_login_required
+def get_dir_size():
+ path = request.form.get('path', '')
+ size = file.getDirSizeByBash(path)
+ return mw.returnData(True, size)
+ # size = file.getDirSize(path)
+ # return mw.returnData(True, mw.toSize(size))
+
+
+# 删除文件
+@blueprint.route('/delete', endpoint='delete', methods=['POST'])
+@panel_login_required
+def delete():
+ path = request.form.get('path', '')
+ return file.fileDelete(path)
+
+
+# 删除文件
+@blueprint.route('/delete_dir', endpoint='delete_dir', methods=['POST'])
+@panel_login_required
+def delete_dir():
+ path = request.form.get('path', '')
+ return file.dirDelete(path)
+
+# 下载文件
+@blueprint.route('/download', endpoint='download', methods=['GET'])
+@panel_login_required
+def download():
+ filename = request.args.get('filename', '')
+ if not os.path.exists(filename):
+ return ''
+ is_attachment = True
+ if filename.endswith(".svg"):
+ is_attachment = False
+
+ response = make_response(send_from_directory(os.path.dirname(filename), os.path.basename(filename), as_attachment=is_attachment))
+ return response
+
+# 远程下载
+@blueprint.route('/download_file', endpoint='download_file', methods=['POST'])
+@panel_login_required
+def download_file():
+ url = request.form.get('url', '')
+ path = request.form.get('path', '')
+ filename = request.form.get('filename', '')
+
+ execstr = url + '|mw|' + path + '/' + filename
+ execstr = execstr.strip()
+
+ title = '下载文件[' + filename + ']'
+ thisdb.addTaskByDownload(name=title, cmd=execstr)
+ # self.setFileAccept(path + '/' + filename)
+ mw.triggerTask()
+ return mw.returnData(True, '已将下载任务添加到队列!')
+
+# 日志清空
+@blueprint.route('/close_logs', endpoint='close_logs', methods=['POST'])
+@panel_login_required
+def close_logs():
+ return file.closeLogs()
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/admin/files/recycle.py b/web/admin/files/recycle.py
new file mode 100644
index 000000000..e02b40a53
--- /dev/null
+++ b/web/admin/files/recycle.py
@@ -0,0 +1,67 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+import os
+
+from flask import Blueprint, render_template
+from flask import request
+from flask import make_response
+from flask import send_file
+from flask import send_from_directory
+
+from admin.user_login_check import panel_login_required
+import core.mw as mw
+import utils.file as file
+
+from .files import blueprint
+# 回收站文件列表
+@blueprint.route('/get_recycle_bin', endpoint='get_recycle_bin', methods=['POST'])
+@panel_login_required
+def get_recycle_bin():
+ return file.getRecycleBin()
+
+# 回收站文件恢复
+@blueprint.route('/re_recycle_bin', endpoint='re_recycle_bin', methods=['POST'])
+@panel_login_required
+def re_recycle_bin():
+ path = request.form.get('path', '')
+ return file.reRecycleBin(path)
+
+@blueprint.route('/del_recycle_bin', endpoint='del_recycle_bin', methods=['POST'])
+@panel_login_required
+def del_recycle_bin():
+ path = request.form.get('path', '')
+ return file.delRecycleBin(path)
+
+# 回收站文件
+@blueprint.route('/recycle_bin', endpoint='recycle_bin', methods=['POST'])
+@panel_login_required
+def recycle_bin():
+ return file.toggleRecycleBin()
+
+# 回收站文件
+@blueprint.route('/close_recycle_bin', endpoint='close_recycle_bin', methods=['POST'])
+@panel_login_required
+def close_recycle_bin():
+ return file.closeRecycleBin()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/admin/firewall/__init__.py b/web/admin/firewall/__init__.py
new file mode 100644
index 000000000..c3f655d1e
--- /dev/null
+++ b/web/admin/firewall/__init__.py
@@ -0,0 +1,125 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+
+from flask import Blueprint, render_template
+from flask import request
+
+from admin.user_login_check import panel_login_required
+
+from utils.firewall import Firewall as MwFirewall
+
+import core.mw as mw
+import thisdb
+
+blueprint = Blueprint('firewall', __name__, url_prefix='/firewall', template_folder='../../templates')
+@blueprint.route('/index', endpoint='index')
+@panel_login_required
+def index():
+ name = thisdb.getOption('template', default='default')
+ return render_template('%s/firewall.html' % name)
+
+
+# 防火墙列表
+@blueprint.route('/get_list', endpoint='get_list', methods=['POST'])
+@panel_login_required
+def get_list():
+ p = request.form.get('p', '1').strip()
+ limit = request.form.get('limit', '10').strip()
+ return MwFirewall.instance().getList(p,limit)
+
+# 获取站点日志目录
+@blueprint.route('/get_www_path', endpoint='get_www_path', methods=['POST'])
+@panel_login_required
+def get_www_path():
+ path = mw.getLogsDir()
+ return {'path': path}
+
+# 获取ssh信息
+@blueprint.route('/get_ssh_info', endpoint='get_ssh_info', methods=['POST'])
+@panel_login_required
+def get_ssh_info():
+ return MwFirewall.instance().getSshInfo()
+
+
+# 切换ping开关
+@blueprint.route('/set_ping', endpoint='set_ping', methods=['POST'])
+@panel_login_required
+def set_ping():
+ status = request.form.get('status')
+ return MwFirewall.instance().setPing(status)
+
+# 修改ssh端口
+@blueprint.route('/set_ssh_port', endpoint='set_ssh_port', methods=['POST'])
+@panel_login_required
+def set_ssh_port():
+ port = request.form.get('port', '1').strip()
+ return MwFirewall.instance().setSshPort(port)
+
+# 添加放行端口
+@blueprint.route('/add_accept_port', endpoint='add_accept_port', methods=['POST'])
+@panel_login_required
+def add_accept_port():
+ port = request.form.get('port', '').strip()
+ ps = request.form.get('ps', '').strip()
+ protocol = request.form.get('protocol', '').strip()
+ stype = request.form.get('type', '').strip()
+
+ return MwFirewall.instance().addAcceptPort(port, ps, stype, protocol=protocol)
+
+# 删除放行端口
+@blueprint.route('/del_accept_port', endpoint='del_accept_port', methods=['POST'])
+@panel_login_required
+def del_accept_port():
+ port = request.form.get('port', '').strip()
+ firewall_id = request.form.get('id', '').strip()
+ protocol = request.form.get('protocol', '').strip()
+ return MwFirewall.instance().delAcceptPort(firewall_id, port, protocol=protocol)
+
+
+# 设置防火墙状态
+@blueprint.route('/set_fw', endpoint='set_fw', methods=['POST'])
+@panel_login_required
+def set_fw():
+ if mw.isAppleSystem():
+ return mw.returnData(True, '开发机不能设置!')
+ status = request.form.get('status', '1')
+ return MwFirewall.instance().setFw(status)
+
+@blueprint.route('/set_ssh_root_status', endpoint='set_ssh_root_status', methods=['POST'])
+@panel_login_required
+def set_ssh_root_status():
+ if mw.isAppleSystem():
+ return mw.returnData(True, '开发机不能设置!')
+ status = request.form.get('status', '1')
+ return MwFirewall.instance().setSshRootStatus(status)
+
+@blueprint.route('/set_ssh_pass_status', endpoint='set_ssh_pass_status', methods=['POST'])
+@panel_login_required
+def set_ssh_pass_status():
+ if mw.isAppleSystem():
+ return mw.returnData(True, '开发机不能设置!')
+ status = request.form.get('status', '1')
+ return MwFirewall.instance().setSshPassStatus(status)
+
+@blueprint.route('/set_ssh_pubkey_status', endpoint='set_ssh_pubkey_status', methods=['POST'])
+@panel_login_required
+def set_ssh_pubkey_status():
+ if mw.isAppleSystem():
+ return mw.returnData(True, '开发机不能设置!')
+ status = request.form.get('status', '1')
+ return MwFirewall.instance().setSshPubkeyStatus(status)
+
+
+
+
+
+
+
diff --git a/web/admin/logs/__init__.py b/web/admin/logs/__init__.py
new file mode 100644
index 000000000..2d2218616
--- /dev/null
+++ b/web/admin/logs/__init__.py
@@ -0,0 +1,73 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+
+from flask import Blueprint, render_template
+from flask import request
+
+from admin.user_login_check import panel_login_required
+
+import core.mw as mw
+import utils.adult_log as adult_log
+import thisdb
+
+# 日志页面
+blueprint = Blueprint('logs', __name__, url_prefix='/logs', template_folder='../../templates')
+@blueprint.route('/index', endpoint='index')
+@panel_login_required
+def index():
+ name = thisdb.getOption('template', default='default')
+ return render_template('%s/logs.html' % name)
+
+# 日志列表
+@blueprint.route('/get_log_list', endpoint='get_log_list', methods=['POST'])
+@panel_login_required
+def get_log_list():
+ p = request.form.get('p', '1').strip()
+ size = request.form.get('limit', '10').strip()
+ search = request.form.get('search', '').strip()
+
+ info = thisdb.getLogsList(page=int(p),size=int(size), search=search)
+
+ data = {}
+ data['data'] = info['list']
+ data['page'] = mw.getPage({'count':info['count'],'tojs':'getLogs','p':p,'row':size})
+ return data
+
+# 日志清空
+@blueprint.route('/del_panel_logs', endpoint='del_panel_logs', methods=['POST'])
+@panel_login_required
+def del_panel_logs():
+ thisdb.clearLog()
+ mw.writeLog('面板设置', '面板操作日志已清空!')
+ return mw.returnData(True, '面板操作日志已清空!')
+
+# 系统审计日志列表
+@blueprint.route('/get_audit_logs_files', endpoint='get_audit_logs_files', methods=['POST'])
+@panel_login_required
+def get_audit_logs_files():
+ logs_file = adult_log.getAuditLogsFiles()
+ return mw.returnData(True, 'ok', logs_file)
+
+# 系统审计日志列表
+@blueprint.route('/get_audit_file', endpoint='get_audit_file', methods=['POST'])
+@panel_login_required
+def get_audit_file():
+ name = request.form.get('log_name', '').strip()
+ return adult_log.getAuditLogsName(name)
+
+
+
+
+
+
+
+
+
diff --git a/web/admin/monitor/__init__.py b/web/admin/monitor/__init__.py
new file mode 100644
index 000000000..044aff3f4
--- /dev/null
+++ b/web/admin/monitor/__init__.py
@@ -0,0 +1,199 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+
+import time
+
+from flask import Blueprint, render_template
+from flask import request
+
+from admin.user_login_check import panel_login_required
+
+import core.mw as mw
+import utils.system as sys
+
+import thisdb
+
+blueprint = Blueprint('monitor', __name__, url_prefix='/monitor', template_folder='../../templates')
+@blueprint.route('/index', endpoint='index')
+@panel_login_required
+def index():
+ name = thisdb.getOption('template', default='default')
+ return render_template('%s/monitor.html' % name)
+
+
+def _parse_range(range_key):
+ mapping = {
+ '1h': 3600,
+ '24h': 86400,
+ '7d': 7 * 86400,
+ '30d': 30 * 86400,
+ }
+ seconds = mapping.get(range_key, 86400)
+ end_time = int(time.time())
+ start_time = end_time - seconds
+ return start_time, end_time
+
+def _to_float(value, default=0.0):
+ try:
+ return float(value)
+ except (TypeError, ValueError):
+ return default
+
+
+def _to_num_or_none(value):
+ if value is None:
+ return None
+ try:
+ return float(value)
+ except (TypeError, ValueError):
+ return None
+
+
+def _build_series(start_time, end_time):
+ load_data = sys.getLoadAverageByDB(start_time, end_time)
+ cpu_data = sys.getCpuIoByDB(start_time, end_time)
+ disk_data = sys.getDiskIoByDB(start_time, end_time)
+ net_data = sys.getNetworkIoByDB(start_time, end_time)
+
+ def _to_float(value, default=0.0):
+ try:
+ return float(value)
+ except (TypeError, ValueError):
+ return default
+
+ series = {
+ 'load': {
+ 'labels': [item['addtime'] for item in load_data],
+ 'one': [_to_float(item.get('one')) for item in load_data],
+ 'five': [_to_float(item.get('five')) for item in load_data],
+ 'fifteen': [_to_float(item.get('fifteen')) for item in load_data],
+ },
+ 'cpu': {
+ 'labels': [item['addtime'] for item in cpu_data],
+ 'cpu': [_to_float(item.get('pro')) for item in cpu_data],
+ 'mem': [_to_float(item.get('mem')) for item in cpu_data],
+ },
+ 'disk': {
+ 'labels': [item['addtime'] for item in disk_data],
+ 'read': [round(_to_float(item.get('read_bytes')) / (1024 * 1024), 2) for item in disk_data],
+ 'write': [round(_to_float(item.get('write_bytes')) / (1024 * 1024), 2) for item in disk_data],
+ },
+ 'net': {
+ 'labels': [item['addtime'] for item in net_data],
+ 'up': [round((_to_float(item.get('up')) * 8) / 1024, 2) for item in net_data],
+ 'down': [round((_to_float(item.get('down')) * 8) / 1024, 2) for item in net_data],
+ },
+ }
+ return series, load_data, cpu_data, disk_data, net_data
+
+
+def _is_monitor_open():
+ monitor_status = thisdb.getOption('monitor_status', default='open', type='monitor')
+ return monitor_status == 'open'
+
+
+def _should_collect_sample(load_data, cpu_data, disk_data, net_data):
+ return not (load_data or cpu_data or disk_data or net_data)
+
+
+def _safe_latest(data, key):
+ if not data:
+ return None
+ return _to_num_or_none(data[-1].get(key))
+
+
+def _safe_peak(data, key, extra=None):
+ if not data:
+ return None
+ if extra:
+ values = [extra(item) for item in data]
+ else:
+ values = [_to_num_or_none(item.get(key)) for item in data]
+
+ values = [v for v in values if v is not None]
+ if not values:
+ return None
+ return max(values)
+
+
+@blueprint.route('/api/overview', endpoint='api_overview', methods=['GET'])
+@panel_login_required
+def api_overview():
+ range_key = request.args.get('range', '24h')
+ start_time, end_time = _parse_range(range_key)
+
+ series, load_data, cpu_data, disk_data, net_data = _build_series(start_time, end_time)
+ if _should_collect_sample(load_data, cpu_data, disk_data, net_data) and _is_monitor_open():
+ # 监控未采样或采样任务异常时,页面会出现空图。这里主动补采样并容错,避免接口直接报错。
+ try:
+ sys.monitor.instance().run()
+ except Exception:
+ pass
+ series, load_data, cpu_data, disk_data, net_data = _build_series(start_time, end_time)
+
+ latest_net = None
+ if net_data:
+ latest_net = max(float(net_data[-1].get('up', 0)), float(net_data[-1].get('down', 0)))
+ latest_disk = None
+ if disk_data:
+ latest_disk = float(disk_data[-1].get('read_bytes', 0)) + float(disk_data[-1].get('write_bytes', 0))
+
+ summary = {
+ 'cpu': {
+ 'latest': _safe_latest(cpu_data, 'pro'),
+ 'peak': _safe_peak(cpu_data, 'pro'),
+ },
+ 'mem': {
+ 'latest': _safe_latest(cpu_data, 'mem'),
+ 'peak': _safe_peak(cpu_data, 'mem'),
+ },
+ 'net': {
+ 'latest': latest_net,
+ 'peak': _safe_peak(net_data, 'up', lambda item: max(float(item.get('up', 0)), float(item.get('down', 0)))),
+ },
+ 'disk': {
+ 'latest': latest_disk,
+ 'peak': _safe_peak(disk_data, 'read_bytes', lambda item: float(item.get('read_bytes', 0)) + float(item.get('write_bytes', 0))),
+ },
+ 'load': {
+ 'latest': _safe_latest(load_data, 'one'),
+ 'peak': _safe_peak(load_data, 'one'),
+ },
+ }
+
+ events = []
+ if cpu_data:
+ top_cpu = max(cpu_data, key=lambda item: _to_float(item.get('pro')))
+ top_cpu_val = round(_to_float(top_cpu.get('pro')), 2)
+ events.append({'title': f"CPU 峰值 {top_cpu_val}%", 'time': top_cpu['addtime']})
+ if cpu_data:
+ top_mem = max(cpu_data, key=lambda item: _to_float(item.get('mem')))
+ top_mem_val = round(_to_float(top_mem.get('mem')), 2)
+ events.append({'title': f"内存峰值 {top_mem_val}%", 'time': top_mem['addtime']})
+ if net_data:
+ top_net = max(net_data, key=lambda item: max(float(item.get('up', 0)), float(item.get('down', 0))))
+ events.append({'title': f"网络峰值 {round((max(float(top_net.get('up', 0)), float(top_net.get('down', 0))) * 8) / 1024, 2)} Mbps", 'time': top_net['addtime']})
+ if disk_data:
+ top_disk = max(disk_data, key=lambda item: float(item.get('read_bytes', 0)) + float(item.get('write_bytes', 0)))
+ total_mb = round((float(top_disk.get('read_bytes', 0)) + float(top_disk.get('write_bytes', 0))) / (1024 * 1024), 2)
+ events.append({'title': f"磁盘 IO 峰值 {total_mb} MB", 'time': top_disk['addtime']})
+
+ data = {
+ 'range': {
+ 'start': start_time,
+ 'end': end_time,
+ 'key': range_key,
+ },
+ 'summary': summary,
+ 'events': events,
+ 'series': series,
+ }
+ return mw.returnData(True, 'ok', data)
diff --git a/web/admin/plugins/__init__.py b/web/admin/plugins/__init__.py
new file mode 100644
index 000000000..d152ae3e3
--- /dev/null
+++ b/web/admin/plugins/__init__.py
@@ -0,0 +1,211 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+import os
+import json
+
+from flask import Blueprint, render_template
+from flask import request
+
+from utils.plugin import plugin as MwPlugin
+from admin.user_login_check import panel_login_required
+
+
+import core.mw as mw
+import utils.config as utils_config
+import thisdb
+
+
+blueprint = Blueprint('plugins', __name__, url_prefix='/plugins', template_folder='../../templates')
+@blueprint.route('/index', endpoint='index')
+@panel_login_required
+def index():
+ name = thisdb.getOption('template', default='default')
+ return render_template('%s/plugins.html' % name)
+
+# 初始化检查,首页提示选择安装
+@blueprint.route('/init', endpoint='init', methods=['POST'])
+@panel_login_required
+def init():
+ return MwPlugin.instance().init()
+
+# 初始化安装
+@blueprint.route('/init_install', endpoint='init_install', methods=['POST'])
+@panel_login_required
+def init_install():
+ plugin_list = request.form.get('list', '')
+ return MwPlugin.instance().initInstall(plugin_list)
+
+# 首页软件展示
+@blueprint.route('/index_list', endpoint='index_list', methods=['GET','POST'])
+@panel_login_required
+def index_list():
+ pg = MwPlugin.instance()
+ return pg.getIndexList()
+
+# 插件列表
+@blueprint.route('/list', endpoint='list', methods=['GET'])
+@panel_login_required
+def list():
+ plugins_type = request.args.get('type', '0')
+ page = request.args.get('p', '1')
+ search = request.args.get('search', '').lower()
+
+ if not mw.isNumber(plugins_type):
+ plugins_type = 1
+
+ if not mw.isNumber(page):
+ page = 0
+
+ pg = MwPlugin.instance()
+ return pg.getList(plugins_type, search, int(page))
+
+# 插件设置是否在首页展示
+@blueprint.route('/set_index', endpoint='set_index', methods=['POST'])
+@panel_login_required
+def set_index():
+ name = request.form.get('name', '')
+ status = request.form.get('status', '0')
+ version = request.form.get('version', '')
+
+ pg = MwPlugin.instance()
+ if status == '1':
+ return pg.addIndex(name, version)
+ return pg.removeIndex(name, version)
+
+# 插件安装
+@blueprint.route('/install', endpoint='install', methods=['POST'])
+@panel_login_required
+def install():
+ name = request.form.get('name', '')
+ version = request.form.get('version', '')
+
+ upgrade = None
+ if hasattr(request.form, 'upgrade'):
+ upgrade = True
+
+ pg = MwPlugin.instance()
+ return pg.install(name, version, upgrade=upgrade)
+
+# 插件卸载
+@blueprint.route('/uninstall', endpoint='uninstall', methods=['POST'])
+@panel_login_required
+def uninstall():
+ name = request.form.get('name', '')
+ version = request.form.get('version', '')
+ pg = MwPlugin.instance()
+ return pg.uninstall(name, version)
+
+# 文件读取
+@blueprint.route('/menu', endpoint='menu', methods=['GET'])
+@panel_login_required
+def menu():
+ data = utils_config.getGlobalVar()
+ pg = MwPlugin.instance()
+ tag = request.args.get('tag', '')
+
+ hook_menu = thisdb.getOptionByJson('hook_menu',type='hook',default=[])
+ content = ''
+ for menu_data in hook_menu:
+ if tag == menu_data['name'] and 'path' in menu_data:
+ t = pg.menuGetAbsPath(tag, menu_data['path'])
+ content = mw.readFile(t)
+ #------------------------------------------------------------
+ data['hook_tag'] = tag
+ data['plugin_content'] = content
+ return render_template('plugin_menu.html', data=data)
+
+# 文件读取
+@blueprint.route('/file', endpoint='file', methods=['GET'])
+@panel_login_required
+def file():
+ name = request.args.get('name', '')
+ if name.strip() == '':
+ return ''
+
+ f = request.args.get('f', '')
+ if f.strip() == '':
+ return ''
+
+ file = mw.getPluginDir() + '/' + name + '/' + f
+ if not os.path.exists(file):
+ return ''
+
+ suffix = mw.getPathSuffix(file)
+ if suffix == '.css':
+ content = mw.readFile(file)
+ from flask import Response
+ from flask import make_response
+ v = Response(content, headers={'Content-Type': 'text/css; charset="utf-8"'})
+ return make_response(v)
+ content = open(file, 'rb').read()
+ return content
+
+
+# 插件上传
+@blueprint.route('/update_zip', endpoint='update_zip', methods=['POST'])
+@panel_login_required
+def update_zip():
+ request_zip = request.files['plugin_zip']
+ return MwPlugin.instance().updateZip(request_zip)
+
+
+@blueprint.route('/input_zip', endpoint='input_zip', methods=['POST'])
+@panel_login_required
+def input_zip():
+ plugin_name = request.form.get('plugin_name', '')
+ tmp_path = request.form.get('tmp_path', '')
+ return MwPlugin.instance().inputZipApi(plugin_name,tmp_path)
+
+
+# 插件设置页
+@blueprint.route('/setting', endpoint='setting', methods=['GET'])
+@panel_login_required
+def setting():
+ name = request.args.get('name', '')
+ html = mw.getPluginDir() + '/' + name + '/index.html'
+ return mw.readFile(html)
+
+
+# 插件统一回调入口API
+@blueprint.route('/run', endpoint='run', methods=['GET','POST'])
+@panel_login_required
+def run():
+ name = request.form.get('name', '')
+ func = request.form.get('func', '')
+ version = request.form.get('version', '')
+ args = request.form.get('args', '')
+ script = request.form.get('script', 'index')
+
+ pg = MwPlugin.instance()
+ data = pg.run(name, func, version, args, script)
+ if data[1] == '':
+ r = mw.returnData(True, "OK", data[0].strip())
+ else:
+ r = mw.returnData(False, data[1].strip())
+ return r
+
+
+# 插件统一回调入口API
+@blueprint.route('/callback', endpoint='callback', methods=['GET','POST'])
+@panel_login_required
+def callback():
+ name = request.form.get('name', '')
+ func = request.form.get('func', '')
+ args = request.form.get('args', '')
+ script = request.form.get('script', 'index')
+
+ pg = MwPlugin.instance()
+ data = pg.callback(name, func, args=args, script=script)
+ if data[0]:
+ return mw.returnData(True, "OK", data[1])
+ return mw.returnData(False, data[1])
+
+
diff --git a/web/admin/setting/__init__.py b/web/admin/setting/__init__.py
new file mode 100644
index 000000000..1dfb93200
--- /dev/null
+++ b/web/admin/setting/__init__.py
@@ -0,0 +1,23 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+from .setting import *
+
+from .temp_login import *
+from .timezone import *
+from .secondary_verifiy import *
+
+from .notify_email import *
+from .notify_tgbot import *
+from .app import *
+
+from .panel_ssl import *
+from .panel_bookmark import *
+
diff --git a/web/admin/setting/app.py b/web/admin/setting/app.py
new file mode 100644
index 000000000..273e19ce8
--- /dev/null
+++ b/web/admin/setting/app.py
@@ -0,0 +1,92 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+import re
+import json
+import os
+import time
+
+from flask import Blueprint, render_template
+from flask import request
+
+from admin import session
+from admin.user_login_check import panel_login_required
+
+import core.mw as mw
+import utils.config as utils_config
+
+from .setting import blueprint
+import thisdb
+
+# 设置API
+@blueprint.route('/set_panel_api', endpoint='set_panel_api', methods=['POST'])
+@panel_login_required
+def set_panel_api():
+ panel_api = thisdb.getOptionByJson('panel_api', default={'open':False})
+ if not panel_api['open']:
+ panel_api['open'] = True
+ thisdb.setOption('panel_api', json.dumps(panel_api))
+ return mw.returnData(True, '开启API成功!')
+ else:
+ panel_api['open'] = False
+ thisdb.setOption('panel_api', json.dumps(panel_api))
+ return mw.returnData(True, '关闭API成功!')
+
+
+# 获取APP列表
+@blueprint.route('/get_app_list', endpoint='get_app_list', methods=['POST'])
+@panel_login_required
+def get_app_list():
+ limit = request.form.get('limit', '5').strip()
+ page = request.form.get('page', '1').strip()
+ tojs = request.form.get('tojs', 'getAppList').strip()
+
+ info = thisdb.getAppList(page=int(page),size=int(limit))
+ data = {}
+ data['data'] = info['list']
+ data['page'] = mw.getPage({'count':info['count'],'tojs':tojs,'p':page,'row':limit})
+ return data
+
+
+# 添加APP列表
+@blueprint.route('/add_app', endpoint='add_app', methods=['POST'])
+@panel_login_required
+def add_app():
+ app_id = request.form.get('app_id', '').strip()
+ app_secret = request.form.get('app_secret', '1').strip()
+ limit_addr = request.form.get('limit_addr', '').strip()
+ if limit_addr == '':
+ return mw.returnData(False, 'IP限制不能为空!')
+
+ rid = thisdb.addApp(app_id,app_secret,limit_addr)
+ if rid > 0:
+ return mw.returnData(True, '添加成功!')
+ return mw.returnData(False, '添加失败!')
+
+# 添加APP列表
+@blueprint.route('/toggle_app_status', endpoint='toggle_app_status', methods=['POST'])
+@panel_login_required
+def toggle_app_status():
+ aid = request.form.get('id', '').strip()
+ rid = thisdb.toggleAppStatus(aid)
+ if rid > 0:
+ return mw.returnData(True, '切换成功!')
+ return mw.returnData(False, '切换失败!')
+
+
+# 获取APP列表
+@blueprint.route('/delete_app', endpoint='delete_app', methods=['POST'])
+@panel_login_required
+def delete_app():
+ aid = request.form.get('id', '').strip()
+ rid = thisdb.deleteAppById(aid)
+ if rid > 0:
+ return mw.returnData(True, '删除成功!')
+ return mw.returnData(False, '删除失败!')
diff --git a/web/admin/setting/notify_email.py b/web/admin/setting/notify_email.py
new file mode 100644
index 000000000..9bd8b3f2b
--- /dev/null
+++ b/web/admin/setting/notify_email.py
@@ -0,0 +1,89 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+import re
+import json
+import os
+import time
+
+from flask import Blueprint, render_template
+from flask import request
+
+from admin import session
+from admin.user_login_check import panel_login_required
+
+import core.mw as mw
+import utils.config as utils_config
+
+from .setting import blueprint
+import thisdb
+
+# 获取邮件信息
+@blueprint.route('/get_notify_email', endpoint='get_notify_email', methods=['POST'])
+@panel_login_required
+def get_notify_email():
+ notify_email = thisdb.getOptionByJson('notify_email', default={'open':False}, type='notify')
+
+ if 'cfg' in notify_email:
+ decrypt_data = mw.deDoubleCrypt('email', notify_email['cfg'])
+ notify_email['email'] = json.loads(decrypt_data)
+ else:
+ notify_email['email'] = {'smtp_host':'','smtp_port':'','smtp_ssl':'','to_mail_addr':'','username':'','password':''}
+
+ return mw.returnData(True,'ok',notify_email)
+
+
+# 设置邮件信息
+@blueprint.route('/set_notify_email', endpoint='set_notify_email', methods=['POST'])
+@panel_login_required
+def set_notify_email():
+ tag = request.form.get('tag', '').strip()
+ data = request.form.get('data', '').strip()
+
+ crypt_data = mw.enDoubleCrypt(tag, data)
+
+ notify_email = thisdb.getOptionByJson('notify_email', default={'open':False}, type='notify')
+ notify_email['cfg'] = crypt_data
+
+ thisdb.setOption('notify_email', json.dumps(notify_email), type='notify')
+ return mw.returnData(True,'设置成功')
+
+
+# 设置邮件测试
+@blueprint.route('/set_notify_email_test', endpoint='set_notify_email_test', methods=['POST'])
+@panel_login_required
+def set_notify_email_test():
+ tag = request.form.get('tag', '').strip()
+ tag_data = request.form.get('data', '').strip()
+
+ data = json.loads(tag_data)
+ test_pass = mw.emailNotifyTest(data)
+ if test_pass == True:
+ return mw.returnData(True, '验证成功')
+ return mw.returnData(False, '验证失败:'+test_pass)
+
+# 切换邮件开关
+@blueprint.route('/set_notify_email_enable', endpoint='set_notify_email_enable', methods=['POST'])
+@panel_login_required
+def set_notify_email_enable():
+ tag = request.form.get('tag', '').strip()
+ data = request.form.get('data', '').strip()
+
+ notify_email = thisdb.getOptionByJson('notify_email', default={'open':False}, type='notify')
+
+ if notify_email['open']:
+ op_action = '关闭'
+ notify_email['open'] = False
+ else:
+ op_action = '开启'
+ notify_email['open'] = True
+
+ thisdb.setOption('notify_email', json.dumps(notify_email), type='notify')
+ return mw.returnData(True, op_action+'成功')
\ No newline at end of file
diff --git a/web/admin/setting/notify_tgbot.py b/web/admin/setting/notify_tgbot.py
new file mode 100644
index 000000000..f3bc11a5d
--- /dev/null
+++ b/web/admin/setting/notify_tgbot.py
@@ -0,0 +1,85 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks
+# ---------------------------------------------------------------------------------
+
+import re
+import json
+import os
+import time
+
+from flask import Blueprint, render_template
+from flask import request
+
+from admin import session
+from admin.user_login_check import panel_login_required
+
+import core.mw as mw
+import utils.config as utils_config
+
+from .setting import blueprint
+import thisdb
+
+# 获取邮件信息
+@blueprint.route('/get_notify_tgbot', endpoint='get_notify_tgbot', methods=['POST'])
+@panel_login_required
+def get_notify_tgbot():
+ notify_tgbot = thisdb.getOptionByJson('notify_tgbot', default={'open':False}, type='notify')
+ if 'cfg' in notify_tgbot:
+ decrypt_data = mw.deDoubleCrypt('tgbot', notify_tgbot['cfg'])
+ notify_tgbot['tgbot'] = json.loads(decrypt_data)
+ else:
+ notify_tgbot['tgbot'] = []
+ return mw.returnData(True,'ok',notify_tgbot)
+
+
+# 设置邮件信息
+@blueprint.route('/set_notify_tgbot', endpoint='set_notify_tgbot', methods=['POST'])
+@panel_login_required
+def set_notify_tgbot():
+ data = request.form.get('data', '').strip()
+
+ crypt_data = mw.enDoubleCrypt('tgbot', data)
+
+ notify_tgbot = thisdb.getOptionByJson('notify_tgbot', default={'open':False}, type='notify')
+ notify_tgbot['cfg'] = crypt_data
+
+ thisdb.setOption('notify_tgbot', json.dumps(notify_tgbot), type='notify')
+ return mw.returnData(True,'设置成功')
+
+
+# 设置邮件测试
+@blueprint.route('/set_notify_tgbot_test', endpoint='set_notify_tgbot_test', methods=['POST'])
+@panel_login_required
+def set_notify_tgbot_test():
+ tag_data = request.form.get('data', '').strip()
+
+ tmp = json.loads(tag_data)
+ test_pass = mw.tgbotNotifyTest(tmp['app_token'], tmp['chat_id'])
+ if test_pass == True:
+ return mw.returnData(True, '验证成功')
+ return mw.returnData(False, '验证失败:'+test_pass)
+
+# 切换邮件开关
+@blueprint.route('/set_notify_tgbot_enable', endpoint='set_notify_tgbot_enable', methods=['POST'])
+@panel_login_required
+def set_notify_tgbot_enable():
+ tag = request.form.get('tag', '').strip()
+ data = request.form.get('data', '').strip()
+
+ notify_tgbot = thisdb.getOptionByJson('notify_tgbot', default={'open':False}, type='notify')
+
+ if notify_tgbot['open']:
+ op_action = '关闭'
+ notify_tgbot['open'] = False
+ else:
+ op_action = '开启'
+ notify_tgbot['open'] = True
+
+ thisdb.setOption('notify_tgbot', json.dumps(notify_tgbot), type='notify')
+ return mw.returnData(True, op_action+'成功')
\ No newline at end of file
diff --git a/web/admin/setting/panel_bookmark.py b/web/admin/setting/panel_bookmark.py
new file mode 100644
index 000000000..922269e8f
--- /dev/null
+++ b/web/admin/setting/panel_bookmark.py
@@ -0,0 +1,88 @@
+# coding:utf-8
+
+# ---------------------------------------------------------------------------------
+# MW-Linux面板
+# ---------------------------------------------------------------------------------
+# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
+# ---------------------------------------------------------------------------------
+# Author: midoks