UNPKG

5.47 kBJavaScriptView Raw
1var ChildProcess = require('child_process');
2var IS_WIN = process.platform === 'win32';
3var TableParser = require('table-parser');
4/**
5 * End of line.
6 * Basically, the EOL should be:
7 * - windows: \r\n
8 * - *nix: \n
9 * But i'm trying to get every possibilities covered.
10 */
11var EOL = /(\r\n)|(\n\r)|\n|\r/;
12var SystemEOL = require('os').EOL;
13
14/**
15 * Execute child process
16 * @type {Function}
17 * @param {String[]} args
18 * @param {Function} callback
19 * @param {Object=null} callback.err
20 * @param {Object[]} callback.stdout
21 */
22
23var Exec = module.exports = exports = function (args, callback) {
24 if (Array.isArray(args)) {
25 args = args.join(' ');
26 }
27
28 // windows 下直接通过exec方法来wmic process get没有stdout,因此通过调用CMD的方法来做
29 if (IS_WIN) {
30
31 var spawn = ChildProcess.spawn;
32 var CMD = spawn('cmd');
33 var stdout = [];
34 var stderr = null;
35
36 CMD.stdout.on('data', function (data) {
37 stdout += data.toString();
38 });
39
40 CMD.stderr.on('data', function (data) {
41
42 if (stderr === null) {
43 stderr = data.toString();
44 }
45 else {
46 stderr += data.toString();
47 }
48 });
49
50 CMD.on('exit', function () {
51
52 var beginRow;
53 stdout = stdout.split(EOL);
54
55 // 寻找到数据的第一行(标题行)
56 stdout.forEach(function (out, index) {
57 if (out && typeof beginRow == 'undefined' && out.indexOf('CommandLine') === 0) {
58 beginRow = index;
59 }
60 });
61
62 // 去掉开头和结尾 (开头为CMD的版权等,结尾为当前路径)
63 stdout.splice(stdout.length - 1, 1);
64 stdout.splice(0, beginRow);
65
66 callback(stderr, stdout.join(SystemEOL) || false);
67 });
68
69 CMD.stdin.write('wmic process get ProcessId,CommandLine \n');
70 CMD.stdin.end();
71 }
72 else {
73 ChildProcess.exec('ps ' + args, function (err, stdout, stderr) {
74
75 if (err || stderr) {
76 return callback(err || stderr.toString());
77 }
78 else {
79 stdout = stdout.toString();
80 callback(null, stdout || false);
81 }
82 });
83 }
84};
85
86/**
87 * Query Process: Focus on pid & cmd
88 * @param query
89 * @param {String|String[]} query.pid
90 * @param {String} query.command RegExp String
91 * @param {String} query.arguments RegExp String
92 * @param {Function} callback
93 * @param {Object=null} callback.err
94 * @param {Object[]} callback.processList
95 * @return {Object}
96 */
97
98exports.lookup = function (query, callback) {
99
100 /**
101 * add 'l' as default ps arguments, since the default ps output in linux like "ubuntu", wont include command arguments
102 */
103 var exeArgs = query.psargs || ['l'];
104 var filter = {};
105 var idList;
106
107 // Lookup by PID
108 if (query.pid) {
109
110 if (Array.isArray(query.pid)) {
111 idList = query.pid;
112 }
113 else {
114 idList = [query.pid];
115 }
116
117 // Cast all PIDs as Strings
118 idList = idList.map(function (v) {
119 return String(v);
120 });
121
122 }
123
124
125 if (query.command) {
126 filter['command'] = new RegExp(query.command, 'i');
127 }
128
129 if (query.arguments) {
130 filter['arguments'] = new RegExp(query.arguments, 'i');
131 }
132
133 if (query.ppid) {
134 filter['ppid'] = new RegExp(query.ppid);
135 }
136
137 return Exec(exeArgs, function (err, output) {
138 if (err) {
139 return callback(err);
140 }
141 else {
142 var processList = parseGrid(output);
143 var resultList = [];
144
145 processList.forEach(function (p) {
146
147 var flt;
148 var type;
149 var result = true;
150 // 若限定了id列表
151 if (idList && idList.indexOf(String(p.pid)) < 0) {
152 return;
153 }
154
155 for (type in filter) {
156 flt = filter[type];
157 result = flt.test(p[type]) ? result : false;
158 }
159
160 if (result) {
161 resultList.push(p);
162 }
163 });
164
165 callback(null, resultList);
166 }
167 });
168};
169
170/**
171 * Kill process
172 * @param pid
173 * @param next
174 */
175
176exports.kill = function (pid, next) {
177 var killCommand = IS_WIN ? 'taskkill ' : 'kill ';
178 var command = killCommand + ( IS_WIN ? '/F /PID ' + pid : pid );
179 ChildProcess.exec(command, function (err, stdout, stderr) {
180 if (typeof next !== 'function') return;
181 if ((err || stderr)) {
182 next(err || stderr.toString());
183 }
184 else {
185 stdout = stdout.toString();
186
187 // 在windows下,kill完马上查询会出现还能找到刚刚被kill的进程的情况,因此等待200ms,然后再认为kill结束
188 if (IS_WIN) {
189 setTimeout(function () {
190 next(null, stdout);
191 }, 200);
192 }
193 else {
194 next(null, stdout);
195 }
196 }
197 });
198};
199
200/**
201 * Parse the stdout into readable object.
202 * @param {String} output
203 */
204
205function parseGrid(output) {
206 if (!output) {
207 return output;
208 }
209 return formatOutput(TableParser.parse(output));
210}
211
212/**
213 * 格式化输出结构,从里面提取出 pid, command, arguments
214 * @param data
215 * @return {Array}
216 */
217
218function formatOutput(data) {
219 var formatedData = [];
220 data.forEach(function (d) {
221 var pid = ( d.PID && d.PID[0] ) || ( d.ProcessId && d.ProcessId[0] ) || undefined;
222 var cmd = d.CMD || d.CommandLine || d.COMMAND || undefined;
223 var ppid = ( d.PPID && d.PPID[0] ) || undefined;
224
225 if (pid && cmd) {
226 var command = cmd[0];
227 var args = '';
228
229 if (cmd.length > 1) {
230 args = cmd.slice(1);
231 }
232
233 formatedData.push({
234 pid: pid,
235 command: command,
236 arguments: args,
237 ppid: ppid
238 });
239 }
240 });
241
242 return formatedData;
243}