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) + '
\ +
\ +

打开的文件列表

\ +
\ +
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + ' + fileBody + '\ +
文件modepositionflagsfd
\ +
\ +
\ +
\ +
\ + \ + \ +
'; + + 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; }