├── .editorconfig ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.txt ├── appveyor.yml ├── index.js ├── lib └── index.js ├── package.json ├── readme.md └── test ├── node_process_for_test.js └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [*.{js,jsx}] 14 | indent_style = space 15 | indent_size = 2 16 | 17 | [Makefile] 18 | indent_style = tab 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.ipr 3 | *.iws 4 | **/node_modules 5 | node_modules 6 | .idea -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - osx 3 | - linux 4 | language: node_js 5 | node_js: 6 | - '7' 7 | - '6' 8 | - '5' 9 | - '4' 10 | branches: 11 | only: 12 | - master 13 | - /release\/.*/ 14 | 15 | deploy: 16 | provider: npm 17 | email: "$NPM_EMAIL" 18 | api_key: 19 | secure: "$NPM_API_KEY" 20 | on: 21 | branch: master 22 | env: 23 | global: 24 | - secure: Wks3CAJcaU1kQmGN2K9CcPSxrCRRi979WbRvKGRAadklO17OptUEwJfaBNwk8jTRP/y2ZvS33EWJynlFEonGWpW6iEY8EBDgqbC5gqqvK0OANR/U/y8Hb7TBOY8qJmr1Ozr2oZeLf4bv62Y1GDZOwSLw7Jj0q7wsjWkd8pL4Vo8= 25 | - secure: jwRVk9eBLLNHBw0R0ggkfXcIAYCJidbPjAPsnlu5rMVQUWEnPw3y5LaeuQiNIRzs/VH6/8vmmDEX6Nk83HxKl5Su7z2Pbmpv12TZTJdBc1nUsxGRfewal8dwS71nK6Zs5lO3/b+v34/BRP/x08olbevzm0Bv+MtqAcOcUIzY8l4= 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [2017-04-21](https://github.com/neekey/ps/pull/48) 2 | - publish 0.1.6 3 | - use `lx` as default options for `ps` command 4 | - remove debugging console log for `kill()` 5 | - add timeout for `kill()`, default to 30s 6 | 7 | ## [2017-03-26](https://github.com/neekey/ps/pull/35) 8 | - publish 0.1.5 9 | - Add parent process ID support for windows 10 | - use `spawn` to replace `exec` for Linux/Unix system 11 | - add appVeyor integration 12 | - use Travis for npm publishing 13 | - refactor the implementation of `kill()`, now just a wrapper of built-in `process.kill()` 14 | 15 | ## 2016-06-23 16 | - Publish 0.1.2 17 | - change `command` and `argument` matching to case insensitive. 18 | 19 | ## 2016-05-05 20 | - Publish 0.1.1 update table-parser to 0.1.1 21 | - Integrate with Travis-CI linux / mac is fully tested now 22 | - Manually test on Win10 and Win7 23 | 24 | ## 2016-04-26 25 | - Publish 0.1.0 update table-parser to 0.1.0 26 | 27 | ## 2015-09-20 28 | 29 | - Publish 0.0.5. 30 | - Merge [#5](https://github.com/neekey/ps/pull/5): Add license type MIT. 31 | - Merge [#6](https://github.com/neekey/ps/pull/6): Allow for stdout to return the data in chunks. 32 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Neekey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: "7" 4 | - nodejs_version: '6' 5 | - nodejs_version: '4' 6 | install: 7 | - ps: Install-Product node $env:nodejs_version 8 | - set CI=true 9 | - npm -g install npm@latest 10 | - set PATH=%APPDATA%\npm;%PATH% 11 | - npm install 12 | matrix: 13 | fast_finish: true 14 | build: off 15 | shallow_clone: true 16 | test_script: 17 | - node --version 18 | - npm --version 19 | - npm test 20 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib'); 2 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | var ChildProcess = require('child_process'); 2 | var IS_WIN = process.platform === 'win32'; 3 | var 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 | */ 11 | var EOL = /(\r\n)|(\n\r)|\n|\r/; 12 | var 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 | 23 | var Exec = module.exports = exports = function (args, callback) { 24 | var spawn = ChildProcess.spawn; 25 | 26 | // on windows, if use ChildProcess.exec(`wmic process get`), the stdout will gives you nothing 27 | // that's why I use `cmd` instead 28 | if (IS_WIN) { 29 | 30 | var CMD = spawn('cmd'); 31 | var stdout = ''; 32 | var stderr = null; 33 | 34 | CMD.stdout.on('data', function (data) { 35 | stdout += data.toString(); 36 | }); 37 | 38 | CMD.stderr.on('data', function (data) { 39 | 40 | if (stderr === null) { 41 | stderr = data.toString(); 42 | } 43 | else { 44 | stderr += data.toString(); 45 | } 46 | }); 47 | 48 | CMD.on('exit', function () { 49 | 50 | var beginRow; 51 | stdout = stdout.split(EOL); 52 | 53 | // Find the line index for the titles 54 | stdout.forEach(function (out, index) { 55 | if (out && typeof beginRow == 'undefined' && out.indexOf('CommandLine') === 0) { 56 | beginRow = index; 57 | } 58 | }); 59 | 60 | // get rid of the start (copyright) and the end (current pwd) 61 | stdout.splice(stdout.length - 1, 1); 62 | stdout.splice(0, beginRow); 63 | 64 | callback(stderr, stdout.join(SystemEOL) || false); 65 | }); 66 | 67 | CMD.stdin.write('wmic process get ProcessId,ParentProcessId,CommandLine \n'); 68 | CMD.stdin.end(); 69 | } 70 | else { 71 | if (typeof args === 'string') { 72 | args = args.split(/\s+/); 73 | } 74 | const child = spawn('ps', args); 75 | var stdout = ''; 76 | var stderr = null; 77 | 78 | child.stdout.on('data', function (data) { 79 | stdout += data.toString(); 80 | }); 81 | 82 | child.stderr.on('data', function (data) { 83 | 84 | if (stderr === null) { 85 | stderr = data.toString(); 86 | } 87 | else { 88 | stderr += data.toString(); 89 | } 90 | }); 91 | 92 | child.on('exit', function () { 93 | if (stderr) { 94 | return callback(stderr.toString()); 95 | } 96 | else { 97 | callback(null, stdout || false); 98 | } 99 | }); 100 | } 101 | }; 102 | 103 | /** 104 | * Query Process: Focus on pid & cmd 105 | * @param query 106 | * @param {String|String[]} query.pid 107 | * @param {String} query.command RegExp String 108 | * @param {String} query.arguments RegExp String 109 | * @param {String|array} query.psargs 110 | * @param {Function} callback 111 | * @param {Object=null} callback.err 112 | * @param {Object[]} callback.processList 113 | * @return {Object} 114 | */ 115 | 116 | exports.lookup = function (query, callback) { 117 | 118 | /** 119 | * add 'lx' as default ps arguments, since the default ps output in linux like "ubuntu", wont include command arguments 120 | */ 121 | var exeArgs = query.psargs || ['lx']; 122 | var filter = {}; 123 | var idList; 124 | 125 | // Lookup by PID 126 | if (query.pid) { 127 | 128 | if (Array.isArray(query.pid)) { 129 | idList = query.pid; 130 | } 131 | else { 132 | idList = [query.pid]; 133 | } 134 | 135 | // Cast all PIDs as Strings 136 | idList = idList.map(function (v) { 137 | return String(v); 138 | }); 139 | 140 | } 141 | 142 | 143 | if (query.command) { 144 | filter['command'] = new RegExp(query.command, 'i'); 145 | } 146 | 147 | if (query.arguments) { 148 | filter['arguments'] = new RegExp(query.arguments, 'i'); 149 | } 150 | 151 | if (query.ppid) { 152 | filter['ppid'] = new RegExp(query.ppid); 153 | } 154 | 155 | return Exec(exeArgs, function (err, output) { 156 | if (err) { 157 | return callback(err); 158 | } 159 | else { 160 | var processList = parseGrid(output); 161 | var resultList = []; 162 | 163 | processList.forEach(function (p) { 164 | 165 | var flt; 166 | var type; 167 | var result = true; 168 | 169 | if (idList && idList.indexOf(String(p.pid)) < 0) { 170 | return; 171 | } 172 | 173 | for (type in filter) { 174 | flt = filter[type]; 175 | result = flt.test(p[type]) ? result : false; 176 | } 177 | 178 | if (result) { 179 | resultList.push(p); 180 | } 181 | }); 182 | 183 | callback(null, resultList); 184 | } 185 | }); 186 | }; 187 | 188 | /** 189 | * Kill process 190 | * @param pid 191 | * @param {Object|String} signal 192 | * @param {String} signal.signal 193 | * @param {number} signal.timeout 194 | * @param next 195 | */ 196 | 197 | exports.kill = function( pid, signal, next ){ 198 | //opts are optional 199 | if(arguments.length == 2 && typeof signal == 'function'){ 200 | next = signal; 201 | signal = undefined; 202 | } 203 | 204 | var checkTimeoutSeconds = (signal && signal.timeout) || 30; 205 | 206 | if (typeof signal === 'object') { 207 | signal = signal.signal; 208 | } 209 | 210 | try { 211 | process.kill(pid, signal); 212 | } catch(e) { 213 | return next && next(e); 214 | } 215 | 216 | var checkConfident = 0; 217 | var checkTimeoutTimer = null; 218 | var checkIsTimeout = false; 219 | 220 | function checkKilled(finishCallback) { 221 | exports.lookup({ pid: pid }, function(err, list) { 222 | if (checkIsTimeout) return; 223 | 224 | if (err) { 225 | clearTimeout(checkTimeoutTimer); 226 | finishCallback && finishCallback(err); 227 | } else if(list.length > 0) { 228 | checkConfident = (checkConfident - 1) || 0; 229 | checkKilled(finishCallback); 230 | } else { 231 | checkConfident++; 232 | if (checkConfident === 5) { 233 | clearTimeout(checkTimeoutTimer); 234 | finishCallback && finishCallback(); 235 | } else { 236 | checkKilled(finishCallback); 237 | } 238 | } 239 | }); 240 | } 241 | 242 | next && checkKilled(next); 243 | 244 | checkTimeoutTimer = next && setTimeout(function() { 245 | checkIsTimeout = true; 246 | next(new Error('Kill process timeout')); 247 | }, checkTimeoutSeconds * 1000); 248 | }; 249 | 250 | /** 251 | * Parse the stdout into readable object. 252 | * @param {String} output 253 | */ 254 | 255 | function parseGrid(output) { 256 | if (!output) { 257 | return []; 258 | } 259 | return formatOutput(TableParser.parse(output)); 260 | } 261 | 262 | /** 263 | * format the structure, extract pid, command, arguments, ppid 264 | * @param data 265 | * @return {Array} 266 | */ 267 | 268 | function formatOutput(data) { 269 | var formatedData = []; 270 | data.forEach(function (d) { 271 | var pid = ( d.PID && d.PID[0] ) || ( d.ProcessId && d.ProcessId[0] ) || undefined; 272 | var cmd = d.CMD || d.CommandLine || d.COMMAND || undefined; 273 | var ppid = ( d.PPID && d.PPID[0] ) || ( d.ParentProcessId && d.ParentProcessId[0] ) || undefined; 274 | 275 | if (pid && cmd) { 276 | var command = cmd[0]; 277 | var args = ''; 278 | 279 | if (cmd.length > 1) { 280 | args = cmd.slice(1); 281 | } 282 | 283 | formatedData.push({ 284 | pid: pid, 285 | command: command, 286 | arguments: args, 287 | ppid: ppid 288 | }); 289 | } 290 | }); 291 | 292 | return formatedData; 293 | } 294 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ps-node", 3 | "version": "0.1.6", 4 | "description": "A process lookup utility", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/neekey/ps.git" 9 | }, 10 | "scripts": { 11 | "test": "node ./node_modules/mocha/bin/mocha -t 0 -R spec test/test.js", 12 | "testWatch": "node ./node_modules/mocha/bin/mocha -t 0 -R spec --watch test/test.js" 13 | }, 14 | "keywords": [ 15 | "ps", 16 | "process", 17 | "lookup", 18 | "pid" 19 | ], 20 | "dependencies": { 21 | "table-parser": "^0.1.3" 22 | }, 23 | "license": "MIT", 24 | "devDependencies": { 25 | "sinon": "^2.1.0", 26 | "mocha": "^2.4.5" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # ps [![Build Status](https://travis-ci.org/neekey/ps.svg?branch=master)](https://travis-ci.org/neekey/ps) [![Build status](https://ci.appveyor.com/api/projects/status/fhom8ot12b6jxeyt?svg=true)](https://ci.appveyor.com/project/neekey/ps) 2 | 3 | A Node.js module for looking up running processes. This module uses [Table-Parser](https://github.com/neekey/table-parser) to parse the output. 4 | 5 | Before using this module, you should take look at section [Existing Bugs You Should Know](https://github.com/neekey/ps#user-content-existing-bugs-you-should-know) at the bottom of this doc. 6 | 7 | ## Install 8 | 9 | ```bash 10 | $ npm install ps-node 11 | ``` 12 | 13 | ## How Does It Work 14 | 15 | This module uses different tools to get process list: 16 | 17 | - Linux / Mac: use `ps` command. Since the default result from shell command `$ ps` will not contain "command arguments" in linux like "ubuntu", ps-node add arguments `lx` as default. Which means, the default value for option `psargs` is `lx`. 18 | - Win: use command `wmic process get ProcessId,CommandLine` through "cmd", more info about wmic is [here](https://social.technet.microsoft.com/Forums/windowsserver/en-US/ab6c7e6e-4ad4-4237-bab3-0349cd76c094/wmic-command-line-utilities?forum=winservercore). Anyway, there is also another tool name [tasklist](https://technet.microsoft.com/en-us/library/bb491010.aspx) in windows, which can also list all the running processes, but lack of command arguments infomation. But compared to wmic, I think this tool should have a higher performance. You should take a look at the wrapper for this tool [tasklist](https://github.com/sindresorhus/tasklist) by @sindresorhs if you are interested. 19 | 20 | ## Compatibility 21 | 22 | - Should work great in most *nix system. 23 | - Should work on Win10/7 more system versions need to be test. 24 | 25 | Any compatibility issue is welcomed. 26 | 27 | ## Usage 28 | 29 | Lookup process with specified `pid`: 30 | 31 | ```javascript 32 | var ps = require('ps-node'); 33 | 34 | // A simple pid lookup 35 | ps.lookup({ pid: 12345 }, function(err, resultList ) { 36 | if (err) { 37 | throw new Error( err ); 38 | } 39 | 40 | var process = resultList[ 0 ]; 41 | 42 | if( process ){ 43 | 44 | console.log( 'PID: %s, COMMAND: %s, ARGUMENTS: %s', process.pid, process.command, process.arguments ); 45 | } 46 | else { 47 | console.log( 'No such process found!' ); 48 | } 49 | }); 50 | 51 | ``` 52 | 53 | Or use RegExp to filter `command` and `arguments`: 54 | 55 | ```javascript 56 | var ps = require('ps-node'); 57 | 58 | // A simple pid lookup 59 | ps.lookup({ 60 | command: 'node', 61 | arguments: '--debug', 62 | }, function(err, resultList ) { 63 | if (err) { 64 | throw new Error( err ); 65 | } 66 | 67 | resultList.forEach(function( process ){ 68 | if( process ){ 69 | 70 | console.log( 'PID: %s, COMMAND: %s, ARGUMENTS: %s', process.pid, process.command, process.arguments ); 71 | } 72 | }); 73 | }); 74 | 75 | ``` 76 | 77 | Also, you can use `kill` to kill process by `pid`: 78 | 79 | ```javascript 80 | var ps = require('ps-node'); 81 | 82 | // A simple pid lookup 83 | ps.kill( '12345', function( err ) { 84 | if (err) { 85 | throw new Error( err ); 86 | } 87 | else { 88 | console.log( 'Process %s has been killed!', pid ); 89 | } 90 | }); 91 | ``` 92 | 93 | Method `kill` also supports a `signal` option to be passed. It's only a wrapper of `process.kill()` with checking of that killing is finished after the method is called. 94 | 95 | ```javascript 96 | var ps = require('ps-node'); 97 | 98 | // Pass signal SIGKILL for killing the process without allowing it to clean up 99 | ps.kill( '12345', 'SIGKILL', function( err ) { 100 | if (err) { 101 | throw new Error( err ); 102 | } 103 | else { 104 | console.log( 'Process %s has been killed without a clean-up!', pid ); 105 | } 106 | }); 107 | ``` 108 | 109 | you can use object as the second parameter to pass more options: 110 | 111 | ```js 112 | ps.kill( '12345', { 113 | signal: 'SIGKILL', 114 | timeout: 10, // will set up a ten seconds timeout if the killing is not successful 115 | }, function(){}); 116 | 117 | ``` 118 | 119 | Notice that the nodejs build-in `process.kill()` does not accept number as the signal, you will have to use string format. 120 | 121 | 122 | You can also pass arguments to `lookup` with `psargs` as arguments for `ps` command(Note that `psargs` is not available in windows): 123 | 124 | ```javascript 125 | var ps = require('ps-node'); 126 | 127 | // A simple pid lookup 128 | ps.lookup({ 129 | command: 'node', 130 | psargs: 'ux' 131 | }, function(err, resultList ) { 132 | if (err) { 133 | throw new Error( err ); 134 | } 135 | 136 | resultList.forEach(function( process ){ 137 | if( process ){ 138 | console.log( 'PID: %s, COMMAND: %s, ARGUMENTS: %s', process.pid, process.command, process.arguments ); 139 | } 140 | }); 141 | }); 142 | 143 | ``` 144 | 145 | Lastly, you can filter a list of items by their PPID by passing a PPID to filter on. You will need to pass in a `psarg` that provides the PPID in the results (`-l` or `-j` for instance). 146 | 147 | ```javascript 148 | var ps = require('ps-node'); 149 | 150 | // A simple pid lookup 151 | ps.lookup({ 152 | command: 'mongod', 153 | psargs: '-l', 154 | ppid: 82292 155 | }, function(err, resultList ) { 156 | if (err) { 157 | throw new Error( err ); 158 | } 159 | 160 | resultList.forEach(function( process ){ 161 | if( process ){ 162 | console.log( 'PID: %s, COMMAND: %s, ARGUMENTS: %s', process.pid, process.command, process.arguments ); 163 | } 164 | }); 165 | }); 166 | 167 | ``` 168 | 169 | ## Existing Bugs You Should Know 170 | 171 | I'm still working on these bugs at the moment, before using this module in any serious way, please take a look at them, and take your own risk. 172 | 173 | - [multiple-bytes characters may cause parse error](https://github.com/neekey/table-parser/issues/4). 174 | -------------------------------------------------------------------------------- /test/node_process_for_test.js: -------------------------------------------------------------------------------- 1 | var now = Date.now(); 2 | console.log('[child] child process start!'); 3 | 4 | function doSomething() { 5 | return null; 6 | } 7 | 8 | setInterval(function () { 9 | doSomething(); 10 | }, 50); 11 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var PS = require('../index'); 2 | var CP = require('child_process'); 3 | var assert = require('assert'); 4 | var Path = require('path'); 5 | var Sinon = require('sinon'); 6 | 7 | var serverPath = Path.resolve(__dirname, './node_process_for_test.js'); 8 | var UpperCaseArg = '--UPPER_CASE'; 9 | var child = null; 10 | var pid = null; 11 | 12 | function startProcess() { 13 | child = CP.fork(serverPath, [UpperCaseArg]); 14 | pid = child.pid; 15 | } 16 | 17 | function killProcess() { 18 | if (process.kill(pid, 0)) { 19 | process.kill(pid); 20 | } 21 | } 22 | 23 | var processKill = process.kill; 24 | 25 | function mockKill() { 26 | process.kill = function() {}; 27 | } 28 | 29 | function restoreKill() { 30 | process.kill = processKill; 31 | } 32 | 33 | describe('test', function () { 34 | before(function (done) { 35 | PS.lookup({arguments: 'node_process_for_test'}, function (err, list) { 36 | var processLen = list.length; 37 | var killedCount = 0; 38 | if (processLen) { 39 | list.forEach(function (item) { 40 | PS.kill(item.pid, function () { 41 | killedCount++; 42 | if (killedCount === processLen) { 43 | done(); 44 | } 45 | }); 46 | }); 47 | } else { 48 | done(); 49 | } 50 | }); 51 | }); 52 | 53 | beforeEach(startProcess); 54 | 55 | describe('#lookup()', function () { 56 | 57 | afterEach(killProcess); 58 | 59 | it('by id', function (done) { 60 | PS.lookup({pid: pid}, function (err, list) { 61 | assert.equal(list.length, 1); 62 | assert.equal(list[0].arguments[0], serverPath); 63 | 64 | done(); 65 | }); 66 | }); 67 | 68 | it('by command & arguments', function (done) { 69 | PS.lookup({command: '.*(node|iojs).*', arguments: 'node_process_for_test'}, function (err, list) { 70 | assert.equal(list.length, 1); 71 | assert.equal(list[0].pid, pid); 72 | assert.equal(list[0].arguments[0], serverPath); 73 | done(); 74 | }); 75 | }); 76 | 77 | it('by arguments, the matching should be case insensitive ', function (done) { 78 | PS.lookup({arguments: 'UPPER_CASE'}, function (err, list) { 79 | assert.equal(list.length, 1); 80 | assert.equal(list[0].pid, pid); 81 | assert.equal(list[0].arguments[0], serverPath); 82 | 83 | PS.lookup({arguments: 'upper_case'}, function (err, list) { 84 | assert.equal(list.length, 1); 85 | assert.equal(list[0].pid, pid); 86 | assert.equal(list[0].arguments[0], serverPath); 87 | done(); 88 | }); 89 | }); 90 | }); 91 | 92 | it('empty result list should be safe ', function (done) { 93 | PS.lookup({command: 'NOT_EXIST', psargs: 'l'}, function (err, list) { 94 | assert.equal(list.length, 0); 95 | done(); 96 | }); 97 | }); 98 | 99 | it('should work correctly with options `aux`', function (done) { 100 | PS.lookup({command: 'node', psargs: 'aux'}, function (err, list) { 101 | assert.equal(list.length > 0, true); 102 | list.forEach(function (row) { 103 | assert.equal(/^\d+$/.test(row.pid), true); 104 | }); 105 | done(); 106 | }); 107 | }); 108 | }); 109 | 110 | describe('#kill()', function () { 111 | 112 | it('kill', function (done) { 113 | PS.lookup({pid: pid}, function (err, list) { 114 | assert.equal(list.length, 1); 115 | PS.kill(pid, function (err) { 116 | assert.equal(err, null); 117 | PS.lookup({pid: pid}, function (err, list) { 118 | assert.equal(list.length, 0); 119 | done(); 120 | }); 121 | }); 122 | }); 123 | }); 124 | 125 | it('should not throw an exception if the callback is undefined', function (done) { 126 | assert.doesNotThrow(function () { 127 | PS.kill(pid); 128 | PS.kill(pid, function() { 129 | done(); 130 | }); 131 | }); 132 | }); 133 | 134 | it('should force kill when opts.signal is SIGKILL', function (done) { 135 | PS.lookup({pid: pid}, function (err, list) { 136 | assert.equal(list.length, 1); 137 | PS.kill(pid, {signal: 'SIGKILL'}, function (err) { 138 | assert.equal(err, null); 139 | PS.lookup({pid: pid}, function (err, list) { 140 | assert.equal(list.length, 0); 141 | done(); 142 | }); 143 | }); 144 | }); 145 | }); 146 | 147 | it('should throw error when opts.signal is invalid', function (done) { 148 | PS.lookup({pid: pid}, function (err, list) { 149 | assert.equal(list.length, 1); 150 | PS.kill(pid, {signal: 'INVALID'}, function (err) { 151 | assert.notEqual(err, null); 152 | PS.kill(pid, function(){ 153 | done(); 154 | }); 155 | }); 156 | }); 157 | }); 158 | }); 159 | 160 | describe('#kill() timeout: ', function () { 161 | it('it should timeout after 30secs by default if the killing is not successful', function(done) { 162 | mockKill(); 163 | var clock = Sinon.useFakeTimers(); 164 | var killStartDate = Date.now(); 165 | PS.lookup({pid: pid}, function (err, list) { 166 | assert.equal(list.length, 1); 167 | PS.kill(pid, function (err) { 168 | assert.equal(Date.now() - killStartDate >= 30 * 1000, true); 169 | assert.equal(err.message.indexOf('timeout') >= 0, true); 170 | restoreKill(); 171 | PS.kill(pid, function(){ 172 | clock.restore(); 173 | done(); 174 | }); 175 | }); 176 | clock.tick(30 * 1000); 177 | }); 178 | }); 179 | 180 | it('it should be able to set option to set the timeout', function(done) { 181 | mockKill(); 182 | var clock = Sinon.useFakeTimers(); 183 | var killStartDate = Date.now(); 184 | PS.lookup({pid: pid}, function (err, list) { 185 | assert.equal(list.length, 1); 186 | PS.kill(pid, { timeout: 5 }, function (err) { 187 | assert.equal(Date.now() - killStartDate >= 5 * 1000, true); 188 | assert.equal(err.message.indexOf('timeout') >= 0, true); 189 | restoreKill(); 190 | PS.kill(pid, function(){ 191 | clock.restore(); 192 | done(); 193 | }); 194 | }); 195 | clock.tick(5 * 1000); 196 | }); 197 | }); 198 | }); 199 | }); 200 | --------------------------------------------------------------------------------