index.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. 'use strict';
  2. var spawn = require('child_process').spawn,
  3. es = require('event-stream');
  4. module.exports = function childrenOfPid(pid, callback) {
  5. var headers = null;
  6. if (typeof callback !== 'function') {
  7. throw new Error('childrenOfPid(pid, callback) expects callback');
  8. }
  9. if (typeof pid === 'number') {
  10. pid = pid.toString();
  11. }
  12. //
  13. // The `ps-tree` module behaves differently on *nix vs. Windows
  14. // by spawning different programs and parsing their output.
  15. //
  16. // Linux:
  17. // 1. " <defunct> " need to be striped
  18. // ```bash
  19. // $ ps -A -o comm,ppid,pid,stat
  20. // COMMAND PPID PID STAT
  21. // bbsd 2899 16958 Ss
  22. // watch <defunct> 1914 16964 Z
  23. // ps 20688 16965 R+
  24. // ```
  25. //
  26. // Win32:
  27. // 1. wmic PROCESS WHERE ParentProcessId=4604 GET Name,ParentProcessId,ProcessId,Status)
  28. // 2. The order of head columns is fixed
  29. // ```shell
  30. // > wmic PROCESS GET Name,ProcessId,ParentProcessId,Status
  31. // Name ParentProcessId ProcessId Status
  32. // System Idle Process 0 0
  33. // System 0 4
  34. // smss.exe 4 228
  35. // ```
  36. var processLister;
  37. if (process.platform === 'win32') {
  38. // See also: https://github.com/nodejs/node-v0.x-archive/issues/2318
  39. processLister = spawn('wmic.exe', ['PROCESS', 'GET', 'Name,ProcessId,ParentProcessId,Status']);
  40. } else {
  41. processLister = spawn('ps', ['-A', '-o', 'ppid,pid,stat,comm']);
  42. }
  43. es.connect(
  44. // spawn('ps', ['-A', '-o', 'ppid,pid,stat,comm']).stdout,
  45. processLister.stdout,
  46. es.split(),
  47. es.map(function (line, cb) { //this could parse alot of unix command output
  48. var columns = line.trim().split(/\s+/);
  49. if (!headers) {
  50. headers = columns;
  51. //
  52. // Rename Win32 header name, to as same as the linux, for compatible.
  53. //
  54. headers = headers.map(normalizeHeader);
  55. return cb();
  56. }
  57. var row = {};
  58. // For each header
  59. var h = headers.slice();
  60. while (h.length) {
  61. row[h.shift()] = h.length ? columns.shift() : columns.join(' ');
  62. }
  63. return cb(null, row);
  64. }),
  65. es.writeArray(function (err, ps) {
  66. var parents = {},
  67. children = [];
  68. parents[pid] = true;
  69. ps.forEach(function (proc) {
  70. if (parents[proc.PPID]) {
  71. parents[proc.PID] = true;
  72. children.push(proc)
  73. }
  74. });
  75. callback(null, children);
  76. })
  77. ).on('error', callback)
  78. }
  79. /**
  80. * Normalizes the given header `str` from the Windows
  81. * title to the *nix title.
  82. *
  83. * @param {string} str Header string to normalize
  84. */
  85. function normalizeHeader(str) {
  86. if (process.platform !== 'win32') {
  87. return str;
  88. }
  89. switch (str) {
  90. case 'Name':
  91. return 'COMMAND';
  92. break;
  93. case 'ParentProcessId':
  94. return 'PPID';
  95. break;
  96. case 'ProcessId':
  97. return 'PID';
  98. break;
  99. case 'Status':
  100. return 'STAT';
  101. break;
  102. default:
  103. throw new Error('Unknown process listing header: ' + str);
  104. }
  105. }