diff --git a/.gitignore b/.gitignore
index 806ca813f5..f19ace7587 100644
--- a/.gitignore
+++ b/.gitignore
@@ -115,7 +115,22 @@ tmp/*
*.swp
*.pem
*.zip
+debug.out
+mdioks.session-journal
+*.session
+
logs/*
+
+ssl/local
+ssl/nginx
+ssl/choose.pl
+
+scripts/init.d/mw
+scripts/mdserver-web
+
+data/api_login.txt
+data/sessions
+
data/*.db
data/iplist.txt
data/json/index.json
@@ -131,12 +146,6 @@ data/debug.pl
data/default_site.pl
data/ssl.pl
-scripts/init.d/mw
-scripts/mdserver-web
-
-data/api_login.txt
-data/sessions
-
data/port.pl
data/ipv6.pl
data/restart.pl
@@ -153,10 +162,6 @@ data/bind_domain.pl
data/unauthorized_status.pl
data/auth_secret.pl
-ssl/local
-ssl/nginx
-ssl/choose.pl
-
plugins/vip_*
plugins/own_*
plugins/my_*
@@ -175,8 +180,3 @@ plugins/frp
plugins/file_search
plugins/proxysql
plugins/tidb
-debug.out
-
-
-mdioks.session-journal
-*.session
diff --git a/README.md b/README.md
index 47ba88475a..dad5d30436 100644
--- a/README.md
+++ b/README.md
@@ -110,9 +110,9 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:
```
-### 版本更新 0.17.0
+### 版本更新 0.17.1
-- 面板SSL调整。
+- 任务管理器-插件。
### JSDelivr安装地址
diff --git a/class/core/config_api.py b/class/core/config_api.py
index bfd27b69c4..02688fa957 100755
--- a/class/core/config_api.py
+++ b/class/core/config_api.py
@@ -28,7 +28,7 @@
class config_api:
- __version = '0.17.0'
+ __version = '0.17.1'
__api_addr = 'data/api.json'
# 统一默认配置文件
diff --git a/class/core/mw.py b/class/core/mw.py
index 04d8d61866..add6338e2a 100755
--- a/class/core/mw.py
+++ b/class/core/mw.py
@@ -1466,7 +1466,7 @@ def makeConf():
file = getRunDir() + '/data/json/config.json'
if not os.path.exists(file):
c = {}
- c['title'] = '老子面板'
+ c['title'] = '后羿面板'
c['home'] = 'http://github/midoks/mdserver-web'
c['recycle_bin'] = True
c['template'] = 'default'
@@ -2110,6 +2110,15 @@ def notifyMessage(msg, stype='common', trigger_time=300, is_write_log=True):
##################### notify end #########################################
+def getGlibcVersion():
+ try:
+ cmd_result = execShell("ldd --version")[0]
+ if not cmd_result: return ''
+ glibc_version = cmd_result.split("\n")[0].split()[-1]
+ except:
+ return ''
+ return glibc_version
+
##################### ssh start #########################################
def getSshDir():
if isAppleSystem():
diff --git a/plugins/mysql/js/mysql.js b/plugins/mysql/js/mysql.js
index ca65c75701..bafe7cfc4b 100755
--- a/plugins/mysql/js/mysql.js
+++ b/plugins/mysql/js/mysql.js
@@ -2936,10 +2936,10 @@ function masterOrSlaveConf(version=''){
getMasterDbList();
}
- if (rdata.slave_status){
+ // if (rdata.slave_status){
getAsyncMasterDbList();
getAsyncDataList()
- }
+ // }
});
}
getMasterStatus();
diff --git a/plugins/php/js/php.js b/plugins/php/js/php.js
index 5c369d2ba8..0e041ad546 100755
--- a/plugins/php/js/php.js
+++ b/plugins/php/js/php.js
@@ -26,7 +26,7 @@ function phpPost(method, version, args,callback){
},'json');
}
-function phpPostCallbak(method, version, args,callback){
+function phpPostCallback(method, version, args,callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
@@ -715,7 +715,7 @@ function getPHPInfo_old(version) {
}
function getPHPInfo(version) {
- phpPostCallbak('get_php_info', version, {}, function(data){
+ phpPostCallback('get_php_info', version, {}, function(data){
if (!data.status){
layer.msg(rdata.msg, { icon: 2 });
return;
@@ -739,7 +739,7 @@ function phpLibConfig(version){
// var rdata = $.parseJSON(rdata.data);
// });
- phpPostCallbak('get_lib_conf', version, {}, function(rdata){
+ phpPostCallback('get_lib_conf', version, {}, function(rdata){
var rdata = rdata.data;
if (!rdata.status){
diff --git a/plugins/task_manager/ico.png b/plugins/task_manager/ico.png
new file mode 100644
index 0000000000..60fa27d663
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 0000000000..12445cd4c6
--- /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 0000000000..f2f22f90ee
--- /dev/null
+++ b/plugins/task_manager/index.py
@@ -0,0 +1,62 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+
+sys.path.append(os.getcwd() + "/class/core")
+import 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 0000000000..13ef84a2f0
--- /dev/null
+++ b/plugins/task_manager/info.json
@@ -0,0 +1,17 @@
+{
+ "sort": 7,
+ "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 0000000000..1884c74a85
--- /dev/null
+++ b/plugins/task_manager/install.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")
+serverPath=$(dirname "$rootPath")
+
+install_tmp=${rootPath}/tmp/mw_install.pl
+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 '正在安装脚本文件...' > $install_tmp
+ 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 0000000000..6c3f7f36e6
--- /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 0000000000..d941b64e2b
--- /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 0000000000..f172e4b25f
--- /dev/null
+++ b/plugins/task_manager/task_manager_index.py
@@ -0,0 +1,1630 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import threading
+import psutil
+import json
+
+from typing import List, Dict
+
+sys.path.append(os.getcwd() + "/class/core")
+import 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()
+ 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['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'])
+
+
+mc_instance = mainClass.instance()
+
+def get_network_list(args = {}):
+ return mc_instance.get_network_list(args)
+
+def get_process_list(args = {}):
+ return mc_instance.get_process_list(args)
+
+def kill_process(args = {}):
+ return mc_instance.kill_process(args)
+
+def kill_process_all(args = {}):
+ if not 'pid' in args:
+ return mw.returnData(False, '缺少参数!')
+ return mc_instance.kill_process_all(int(args['pid']))
+
+def set_meter_head(args = {}):
+ return mc_instance.set_meter_head(args)
+
+def remove_service(args = {}):
+ return mc_instance.remove_service(args)
+
+def set_runlevel_state(args = {}):
+ return mc_instance.set_runlevel_state(args)
+
+def get_service_list(args = {}):
+ return mc_instance.get_service_list(args)
+
+def get_run_list(args = {}):
+ return mc_instance.get_run_list(args)
+
+def get_cron_list(args = {}):
+ return mc_instance.get_cron_list(args)
+
+def remove_cron(args = {}):
+ return mc_instance.remove_cron(args)
+
+def pkill_session(args = {}):
+ return mc_instance.pkill_session(args)
+
+def get_who(args = {}):
+ return mc_instance.get_who(args)
+
+def get_process_info(args = {}):
+ return mc_instance.get_process_info(args)
+
+def get_user_list(args = {}):
+ return mc_instance.get_user_list(args)
+
+def remove_user(args = {}):
+ return mc_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/webstats/js/stats.js b/plugins/webstats/js/stats.js
index 570329d973..29aa9730d7 100644
--- a/plugins/webstats/js/stats.js
+++ b/plugins/webstats/js/stats.js
@@ -2420,8 +2420,9 @@ var html = '\
\
\
\
- \
+ \
\
+ \
\
\
\
diff --git a/route/static/app/public.js b/route/static/app/public.js
index 08d2e6761f..b73fe3d810 100755
--- a/route/static/app/public.js
+++ b/route/static/app/public.js
@@ -610,7 +610,7 @@ function showMsg(msg, callback ,icon, time){
function openPath(a) {
setCookie("open_dir_path", a);
- window.location.href = "/files/"
+ window.location.href = "/files/";
}
function onlineEditFile(k, f, callback) {
diff --git a/route/static/css/site.css b/route/static/css/site.css
index 7b0960491e..358ac8c93a 100755
--- a/route/static/css/site.css
+++ b/route/static/css/site.css
@@ -276,6 +276,10 @@ html {
margin-left: 5px;
}
+.ml10 {
+ margin-left: 10px;
+}
+
.mr5 {
margin-right: 5px;
}
@@ -283,6 +287,7 @@ html {
.mr20 {
margin-right: 20px;
}
+
.ml0{
margin-left: 0;
}