|
1 | 1 | import process from 'node:process'
|
2 | 2 | import fs from 'node:fs'
|
| 3 | +import { EOL as SystemEOL } from 'node:os' |
3 | 4 | import { parse, TIngridResponse } from '@webpod/ingrid'
|
4 | 5 | import { exec, TSpawnCtx } from 'zurk/spawn'
|
5 |
| -import { EOL as SystemEOL } from 'node:os' |
6 | 6 |
|
7 | 7 | const EOL = /(\r\n)|(\n\r)|\n|\r/
|
8 | 8 | const IS_WIN = process.platform === 'win32'
|
@@ -46,44 +46,76 @@ export type TPsNext = (err?: any, data?: any) => void
|
46 | 46 | * @param {String} query.command RegExp String
|
47 | 47 | * @param {String} query.arguments RegExp String
|
48 | 48 | * @param {String|String[]} query.psargs
|
49 |
| - * @param {Function} cb |
| 49 | + * @param {Function} [cb] |
| 50 | + * @param {Object=null} cb.err |
| 51 | + * @param {TPsLookupEntry[]} cb.processList |
| 52 | + * @return {Promise<TPsLookupEntry[]>} |
| 53 | + */ |
| 54 | +export const lookup = (query: TPsLookupQuery = {}, cb: TPsLookupCallback = noop): Promise<TPsLookupEntry[]> => |
| 55 | + _lookup({query, cb, sync: false}) |
| 56 | + |
| 57 | +/** |
| 58 | + * Looks up the process list synchronously |
| 59 | + * @param query |
| 60 | + * @param {String|String[]} query.pid |
| 61 | + * @param {String} query.command RegExp String |
| 62 | + * @param {String} query.arguments RegExp String |
| 63 | + * @param {String|String[]} query.psargs |
| 64 | + * @param {Function} [cb] |
50 | 65 | * @param {Object=null} cb.err
|
51 | 66 | * @param {Object[]} cb.processList
|
52 |
| - * @return {Object} |
| 67 | + * @return {TPsLookupEntry[]} |
53 | 68 | */
|
54 |
| -export const lookup = (query: TPsLookupQuery = {}, cb: TPsLookupCallback = noop) => { |
55 |
| - const { promise, resolve, reject } = makeDeferred<TPsLookupEntry[]>() |
| 69 | +export const lookupSync = (query: TPsLookupQuery = {}, cb: TPsLookupCallback = noop): TPsLookupEntry[] => |
| 70 | + _lookup({query, cb, sync: true}) |
| 71 | + |
| 72 | +lookup.sync = lookupSync |
| 73 | + |
| 74 | +const _lookup = ({ |
| 75 | + query = {}, |
| 76 | + cb = noop, |
| 77 | + sync = false |
| 78 | + }: { |
| 79 | + sync?: boolean |
| 80 | + cb?: TPsLookupCallback |
| 81 | + query?: TPsLookupQuery |
| 82 | + }) => { |
| 83 | + const pFactory = sync ? makePseudoDeferred.bind(null, []) : makeDeferred |
| 84 | + const { promise, resolve, reject } = pFactory() |
56 | 85 | const { psargs = ['-lx'] } = query // add 'lx' as default ps arguments, since the default ps output in linux like "ubuntu", wont include command arguments
|
57 |
| - const args = typeof psargs === 'string' ? psargs.split(/\s+/) : psargs |
| 86 | + const args = Array.isArray(psargs) ? psargs : psargs.split(/\s+/) |
58 | 87 | const extract = IS_WIN ? extractWmic : identity
|
| 88 | + |
| 89 | + let result: TPsLookupEntry[] = [] |
59 | 90 | const callback: TSpawnCtx['callback'] = (err, {stdout}) => {
|
60 | 91 | if (err) {
|
61 | 92 | reject(err)
|
62 | 93 | cb(err)
|
63 | 94 | return
|
64 | 95 | }
|
65 |
| - |
66 |
| - const list = parseProcessList(extract(stdout), query) |
67 |
| - resolve(list) |
68 |
| - cb(null, list) |
| 96 | + result = parseProcessList(extract(stdout), query) |
| 97 | + resolve(result) |
| 98 | + cb(null, result) |
69 | 99 | }
|
70 | 100 | const ctx: TSpawnCtx = IS_WIN
|
71 | 101 | ? {
|
72 | 102 | cmd: 'cmd',
|
73 | 103 | input: 'wmic process get ProcessId,ParentProcessId,CommandLine \n',
|
74 | 104 | callback,
|
| 105 | + sync, |
75 | 106 | run(cb) {cb()}
|
76 | 107 | }
|
77 | 108 | : {
|
78 | 109 | cmd: 'ps',
|
79 | 110 | args,
|
80 | 111 | run(cb) {cb()},
|
| 112 | + sync, |
81 | 113 | callback,
|
82 | 114 | }
|
83 | 115 |
|
84 | 116 | exec(ctx)
|
85 | 117 |
|
86 |
| - return promise |
| 118 | + return Object.assign(promise, result) |
87 | 119 | }
|
88 | 120 |
|
89 | 121 | export const parseProcessList = (output: string, query: TPsLookupQuery = {}) => {
|
@@ -128,26 +160,45 @@ export const pickTree = (list: TPsLookupEntry[], pid: string | number, recursive
|
128 | 160 | ]
|
129 | 161 | }
|
130 | 162 |
|
131 |
| -export const tree = async (opts?: string | number | TPsTreeOpts | undefined, cb: TPsLookupCallback = noop): Promise<TPsLookupEntry[]> => { |
| 163 | +const _tree = ({ |
| 164 | + cb = noop, |
| 165 | + opts, |
| 166 | + sync = false |
| 167 | +}: { |
| 168 | + opts?: string | number | TPsTreeOpts | undefined |
| 169 | + cb?: TPsLookupCallback |
| 170 | + sync?: boolean |
| 171 | +}): any => { |
132 | 172 | if (typeof opts === 'string' || typeof opts === 'number') {
|
133 |
| - return tree({ pid: opts }, cb) |
| 173 | + return _tree({opts: {pid: opts}, cb, sync}) |
134 | 174 | }
|
135 |
| - |
136 |
| - try { |
137 |
| - const all = await lookup() |
| 175 | + const handle = (all: TPsLookupEntry[]) => { |
138 | 176 | if (opts === undefined) return all
|
139 | 177 |
|
140 | 178 | const {pid, recursive = false} = opts
|
141 | 179 | const list = pickTree(all, pid, recursive)
|
142 | 180 |
|
143 | 181 | cb(null, list)
|
144 | 182 | return list
|
| 183 | + } |
| 184 | + |
| 185 | + try { |
| 186 | + const all = _lookup({sync}) |
| 187 | + return sync ? handle(all) : all.then(handle) |
145 | 188 | } catch (err) {
|
146 | 189 | cb(err)
|
147 | 190 | throw err
|
148 | 191 | }
|
149 | 192 | }
|
150 | 193 |
|
| 194 | +export const tree = async (opts?: string | number | TPsTreeOpts | undefined, cb?: TPsLookupCallback): Promise<TPsLookupEntry[]> => |
| 195 | + _tree({opts, cb}) |
| 196 | + |
| 197 | +export const treeSync = (opts?: string | number | TPsTreeOpts | undefined, cb?: TPsLookupCallback): TPsLookupEntry[] => |
| 198 | + _tree({opts, cb, sync: true}) |
| 199 | + |
| 200 | +tree.sync = treeSync |
| 201 | + |
151 | 202 | /**
|
152 | 203 | * Kill process
|
153 | 204 | * @param pid
|
@@ -253,13 +304,23 @@ export const formatOutput = (data: TIngridResponse): TPsLookupEntry[] =>
|
253 | 304 |
|
254 | 305 | export type PromiseResolve<T = any> = (value?: T | PromiseLike<T>) => void
|
255 | 306 |
|
256 |
| -export const makeDeferred = <T = any, E = any>(): { promise: Promise<T>, resolve: PromiseResolve<T>, reject: PromiseResolve<E> } => { |
| 307 | +const makeDeferred = <T = any, E = any>(): { promise: Promise<T>, resolve: PromiseResolve<T>, reject: PromiseResolve<E> } => { |
257 | 308 | let resolve
|
258 | 309 | let reject
|
259 | 310 | const promise = new Promise<T>((res, rej) => { resolve = res; reject = rej })
|
260 | 311 | return { resolve, reject, promise } as any
|
261 | 312 | }
|
262 | 313 |
|
263 |
| -export const noop = () => {/* noop */} |
| 314 | +const makePseudoDeferred = <T = any, E = any>(r = {}): { promise: any, resolve: any, reject: any } => { |
| 315 | + return { |
| 316 | + promise: r as any, |
| 317 | + resolve: identity, |
| 318 | + reject(e: any) { |
| 319 | + throw e |
| 320 | + } |
| 321 | + } |
| 322 | +} |
| 323 | + |
| 324 | +const noop = () => {/* noop */} |
264 | 325 |
|
265 |
| -export const identity = <T>(v: T): T => v |
| 326 | +const identity = <T>(v: T): T => v |
0 commit comments