diff --git a/.gitignore b/.gitignore index e2ef12599..f19ace758 100644 --- a/.gitignore +++ b/.gitignore @@ -180,4 +180,3 @@ plugins/frp plugins/file_search plugins/proxysql plugins/tidb -plugins/task_manager diff --git a/plugins/task_manager/ico.png b/plugins/task_manager/ico.png new file mode 100644 index 000000000..60fa27d66 Binary files /dev/null and b/plugins/task_manager/ico.png differ diff --git a/plugins/task_manager/index.html b/plugins/task_manager/index.html new file mode 100755 index 000000000..b51b420c2 --- /dev/null +++ b/plugins/task_manager/index.html @@ -0,0 +1,554 @@ + + +
+
+
+ 进程 + + 启动项 + 服务 + 网络 + 用户 + 计划任务 + 会话 +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+ CPU + 内存 + 磁盘 +
+ +
+
+
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+ + + + diff --git a/plugins/task_manager/index.py b/plugins/task_manager/index.py new file mode 100755 index 000000000..f2f22f90e --- /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 000000000..bdfcdda38 --- /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 000000000..6cb2d3597 --- /dev/null +++ b/plugins/task_manager/install.sh @@ -0,0 +1,33 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + +install_tmp=${rootPath}/tmp/mw_install.pl +VERSION=$2 + +Install_App() +{ + echo '正在安装脚本文件...' > $install_tmp + mkdir -p $serverPath/task_manager + + echo "$VERSION" > $serverPath/task_manager/version.pl + echo "安装任务管理器成功" +} + +Uninstall_App() +{ + rm -rf $serverPath/task_manager + echo "卸载任务管理器成功" +} + +action=$1 +if [ "${1}" == 'install' ];then + Install_App +else + Uninstall_App +fi diff --git a/plugins/task_manager/js/task_manager.js b/plugins/task_manager/js/task_manager.js new file mode 100644 index 000000000..413d045bd --- /dev/null +++ b/plugins/task_manager/js/task_manager.js @@ -0,0 +1,1103 @@ + +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); + bt_tools.send({ + url: 'plugin?action=a&name=task_manager&s=set_meter_head', + data: {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) { + console.log('createProcessTable',getboday, data); + console.log('realProcess',realProcess); + 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(' ', '') + ''; + } + + // ' + ToSize(realProcess[i].up).replace(' ', '') + '\ + // ' + 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 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; + } + + console.log(rdata); + + 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 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 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']}); + $.post('/plugin?action=a&name=task_manager&s=remove_user', {user: user}, function (rdata) { + layer.close(loadT); + layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); + if (rdata.status) get_user_list(); + }); + }); +} + +//结束进程 +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']}); + $.post('/plugin?action=a&name=task_manager&s=kill_process', {pid: pid}, function (rdata) { + layer.close(loadT); + 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']}); + $.post('/plugin?action=a&name=task_manager&s=kill_process_all', {pid: pid}, function (rdata) { + layer.close(loadT); + layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); + if (rdata.status) get_process_list(); + }) + }, function () { + layer.close(w) + }) +} + +//打开文件所在位置 +function open_path(path) { + tmp = path.split('/') + tmp[tmp.length - 1] = ''; + 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']}); + $.post('/plugin?action=a&name=task_manager&s=remove_service', {serviceName: serviceName}, function (rdata) { + layer.close(loadT); + layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); + if (rdata.status) get_service_list(); + }); + }); +} + +//在线编辑文件 +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']}); + $.post('/plugin?action=a&name=task_manager&s=remove_cron', {index: index}, function (rdata) { + layer.close(loadT); + layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); + if (rdata.status) get_cron_list(); + }); + }); +} + +//强制断开会话 +function pkill_session(pts) { + safeMessage('强制断开会话[' + pts + ']', '强制断开此会话吗?', function () { + var loadT = layer.msg('正在断开会话..', {icon: 16, time: 0, shade: [0.3, '#000']}); + $.post('/plugin?action=a&name=task_manager&s=pkill_session', {pts: pts}, function (rdata) { + layer.close(loadT); + 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']}); + $.post('/plugin?action=a&name=task_manager&s=set_runlevel_state', { + runlevel: runlevel, + serviceName: serviceName + }, function (rdata) { + layer.close(loadT); + layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); + if (rdata.status) get_service_list(); + }); +} + +//查看进程详情 +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){ + console.log('get_process_info',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?action=AddDropAddress', 'port=' + address + '&ps=' + lan.index.net_doup_ip_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)'; +} + +var masterPluginTitle = $('.TaskManView').parents('.layui-layer'); +// masterPluginTitle.append('
需求反馈
'); +masterPluginTitle.on('click', '.bt-desired', function(){ + bt_tools.nps({name:'任务管理器',type:26}) +}) \ No newline at end of file diff --git a/plugins/task_manager/process_network_total.py b/plugins/task_manager/process_network_total.py new file mode 100644 index 000000000..cd317721c --- /dev/null +++ b/plugins/task_manager/process_network_total.py @@ -0,0 +1,318 @@ +#coding: utf-8 +#------------------------------------------------------------------- +# 宝塔Linux面板 +#------------------------------------------------------------------- +# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved. +#------------------------------------------------------------------- +# Author: hwliang +#------------------------------------------------------------------- + +import sys +import time +import os +import struct + +os.chdir('/www/server/panel') +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("btpip 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): + ''' + @name 启动进程网络监控 + @author hwliang<2021-09-13> + @param timeout 结束时间(秒),0表示持久运行,默认为0 + @return void + ''' + 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): + ''' + @name 处理pcap数据包 + @author hwliang<2021-09-12> + @param pcap_data pcap数据包 + @return void + ''' + # 获取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): + ''' + @name 统计进程流量 + @author hwliang<2021-09-13> + @param dst 目标地址 + @param src 源地址 + @param pack_size 数据包长度 + @return void + ''' + 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): + ''' + @name 写入进程流量 + @author hwliang<2021-09-13> + @return void + ''' + w_file = '/dev/shm/bt_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): + ''' + @name 将16进制的IP地址转换为字符串IP地址 + @author hwliang<2021-09-13> + @param hex_ip 16进制的IP地址:16进程端口 + @return tuple(ip,port) 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): + ''' + @name 获取当前TCP连接状态表 + @author hwliang<2021-09-13> + @param force 是否强制刷新 + @return dict + ''' + 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): + ''' + @name 将端口转换为字节流 + @author hwliang<2021-09-13> + @param port 端口 + @return bytes + ''' + return struct.pack('H',int(port))[::-1] + + def get_ip_pack(self,ip): + ''' + @name 将IP地址转换为字节流 + @author hwliang<2021-09-13> + @param ip IP地址 + @return bytes + ''' + 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): + ''' + @name 将inode转换为进程ID + @author hwliang<2021-09-13> + @param inode inode + @param force 是否强制刷新 + @return int + ''' + inode_list = self.get_process_inodes() + if inode in inode_list: + return inode_list[inode] + return None + + def get_process_inodes(self,force = False): + ''' + @name 获取进程inode列表 + @author hwliang<2021-09-13> + @param force 是否强制刷新 + @return dict + ''' + 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): + ''' + @name 获取进程名称 + @author hwliang<2021-09-13> + @param pid 进程ID + @return str + ''' + 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): + ''' + @name 写入进程ID到PID文件 + @author hwliang<2021-09-13> + @return void + ''' + 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): + ''' + @name 删除进程pid文件 + @author hwliang<2021-09-13> + @return void + ''' + if os.path.exists(self.__pid_file): + os.remove(self.__pid_file) + +if __name__ == '__main__': + if len(sys.argv) > 1: + timeout = int(sys.argv[-1]) + else: + timeout = 0 + p = process_network_total() + p.write_pid() + p.start(timeout) \ No newline at end of file diff --git a/plugins/task_manager/task_manager_index.py b/plugins/task_manager/task_manager_index.py new file mode 100755 index 000000000..230b5d114 --- /dev/null +++ b/plugins/task_manager/task_manager_index.py @@ -0,0 +1,1182 @@ +# 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 = {} + old_path = '/www/server/task_manager/task_old.json' + old_net_path = '/www/server/task_manager/network_old.json' + 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_old(self): + if self.old_info: return True + # if not os.path.exists(self.old_path): return False + data = cache.get(self.old_path) + if not data: return False + # data = json.loads(data) + if not data: return False + self.old_info = data + del (data) + return True + + def get_net_old(self): + if self.old_net_info: return True + # if not os.path.exists(self.old_net_path): return False + data = cache.get(self.old_net_path) + if not data: return False + # data = json.loads(data) + if not data: return False + self.old_net_info = data + del (data) + return True + + def get_process_net_list(self): + w_file = '/dev/shm/bt_net_process' + if not os.path.exists(w_file): return + self.last_net_process = cache.get('net_process') + self.last_net_process_time = cache.get('last_net_process') + net_process_body = public.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 + cache.set('net_process', self.__process_net_list, 600) + cache.set('last_net_process', time.time(), 600) + + # 获取进程连接数 + 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 + + 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服务', + 'oneavd': '微步木马检测服务', + 'btsync': '宝塔文件同步服务', + '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: + if name == 'php-fpm': + try: + php_version = '.'.join(p_exe.split('/')[-3]) + return 'PHP' + php_version + '进程' + except: + pass + elif name == 'python': + p_exe_arr = p_exe.split('/') + if p_exe_arr[-1] in ['BT-Task', 'task.py']: + return '面板-后台任务进程' + 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('/panel/plugin') != -1: + return '面板插件进程' + elif cmdline.find('/www/server/cron/') != -1: + return '面板计划任务' + elif name == 'nginx': + if p.username() == 'www': + return 'Nginx子进程' + else: + return 'Nginx主进程' + elif p_exe == '/usr/bin/bash': + cmdline = ' '.join(p.cmdline()).strip() + if cmdline.find('/www/server/cron/') != -1: + return '面板计划任务' + elif cmdline.find('/www/server/mdserver-web/plugins') != -1: + return '面板插件进程' + + if name in processPs: return processPs[name] + if name == 'python': + 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): + ''' + @name 获取进程网络流量 + @author hwliang<2021-09-13> + @param pid 进程ID + @return tuple + ''' + 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'] + + up = int((self.__process_net_list[pid]['up'] - self.last_net_process[pid]['up']) / ( + time.time() - self.last_net_process_time)) + down = int((self.__process_net_list[pid]['down'] - self.last_net_process[pid]['down']) / ( + time.time() - self.last_net_process_time)) + up_package = int((self.__process_net_list[pid]['up_package'] - self.last_net_process[pid]['up_package']) / ( + time.time() - self.last_net_process_time)) + down_package = int( + (self.__process_net_list[pid]['down_package'] - self.last_net_process[pid]['down_package']) / ( + time.time() - self.last_net_process_time)) + 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 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 hasattr(i, 'children'): + 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/python' + if os.path.exists(bin_file): + return bin_file + return '/usr/bin/python' + + # 检查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 = public.readFile(_pid_file) + if os.path.exists('/proc/' + pid): return True + + cmd_file = getServerDir()+'/process_network_total.py' + python_bin = self.get_python_bin() + _cmd = 'nohup {} {} 600 &> /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 [] + + 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: + p = psutil.Process(pid) + 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 hasattr(args, 'search'): + 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' + 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: + p = psutil.Process(pid) + with p.oneshot(): + p_mem = p.memory_full_info() + 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) + 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: + continue + + cache.set(self.old_path, self.new_info, 600) + processList = self.__pro_s_s(processList) + 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' + 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 'user' in get: + return mw.returnData(False, '缺少参数!') + + if get['user'] in users: return mw.returnData(False, '不能删除系统和环境关键用户!') + + r = mw.execShell("userdel " + get['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 " + get.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 get_network_list(self, get = {}): + 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 ['BT-Panel', 'gunicorn', 'baota_coll', 'baota_client']: 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 hasattr(get, 'search'): + if get.search != '': + data['list'] = self.search_network(data['list'], get.search) + 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 + + # 外部接口 查询服务启动级别 /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 hasattr(get, 'search'): + if get.search != '': + data['serviceList'] = self.search_service(data['serviceList'], get.search) + 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 get_cron_list(self, get = {}): + filename = self.get_cron_file() + tmpList = public.readFile(filename).split("\n") + cronList = [] + 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 hasattr(get, 'search'): + if get.search != '': + cronList = self.search_cron(cronList, get.search) + return cronList + + # 获取当前会话 + 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 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 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__": + # mc_instance.get_process_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()