@sshkeda/task 0.0.0-stage → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -2
  3. package/package.json +41 -4
  4. package/src/index.ts +2 -0
  5. package/src/lib/errors/index.ts +15 -0
  6. package/src/lib/errors/internal/errors.ts +147 -0
  7. package/src/lib/kill-leftover-processes/index.ts +17 -0
  8. package/src/lib/kill-leftover-processes/internal/command-words.ts +16 -0
  9. package/src/lib/kill-leftover-processes/internal/find-process-ids.ts +36 -0
  10. package/src/lib/kill-leftover-processes/internal/kill-process-groups.ts +270 -0
  11. package/src/lib/kill-leftover-processes/internal/list-linux-processes-from-proc.ts +191 -0
  12. package/src/lib/kill-leftover-processes/internal/list-macos-processes-with-sysctl.ts +335 -0
  13. package/src/lib/kill-leftover-processes/internal/list-processes.ts +58 -0
  14. package/src/lib/kill-leftover-processes/internal/mark-run-processes.ts +367 -0
  15. package/src/lib/kill-leftover-processes/internal/process-cleanup-errors.ts +17 -0
  16. package/src/lib/kill-leftover-processes/internal/send-signal.ts +32 -0
  17. package/src/lib/kill-leftover-processes/internal/signal-run-processes.ts +48 -0
  18. package/src/lib/kill-leftover-processes/internal/started-process-groups.ts +107 -0
  19. package/src/lib/main-thread/index.ts +3 -0
  20. package/src/lib/main-thread/internal/listen-for-stop-requests.ts +102 -0
  21. package/src/lib/main-thread/internal/run-main-thread.ts +81 -0
  22. package/src/lib/main-thread/internal/run-root-task-fiber.ts +191 -0
  23. package/src/lib/main-thread/internal/stops-at-first-failure.ts +20 -0
  24. package/src/lib/main-thread/internal/trace-the-run.ts +107 -0
  25. package/src/lib/main-thread/lib/find-code-each-task-depends-on/index.ts +7 -0
  26. package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/follow-imports-from-entry-files.ts +306 -0
  27. package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/load-all-reporting-first-failure.ts +11 -0
  28. package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/make-module-graph.ts +48 -0
  29. package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/read-configured-loaders.ts +64 -0
  30. package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/resolve-module-specifiers.ts +60 -0
  31. package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/rewrite-worker-imports.ts +118 -0
  32. package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/worker-import-errors.ts +19 -0
  33. package/src/lib/main-thread/lib/run-tasks-on-workers/index.ts +7 -0
  34. package/src/lib/main-thread/lib/run-tasks-on-workers/internal/handle-worker-events.ts +348 -0
  35. package/src/lib/main-thread/lib/run-tasks-on-workers/internal/make-task-runs.ts +207 -0
  36. package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/index.ts +1 -0
  37. package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/await-within-counted-limit.ts +29 -0
  38. package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/execute-task.ts +272 -0
  39. package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/make-task-setup-clock.ts +129 -0
  40. package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/run-requested-tasks.ts +135 -0
  41. package/src/lib/main-thread/lib/run-tasks-on-workers/shared/define-task-run-state.ts +288 -0
  42. package/src/lib/main-thread/lib/run-tasks-on-workers/shared/make-stopwatch.ts +39 -0
  43. package/src/lib/main-thread/lib/run-tasks-on-workers/shared/share-standard-input-with-root-task.ts +36 -0
  44. package/src/lib/main-thread/lib/run-tasks-on-workers/shared/stop-task.ts +166 -0
  45. package/src/lib/main-thread/lib/scan-module-source/index.ts +1 -0
  46. package/src/lib/main-thread/lib/scan-module-source/internal/find-lexical-bindings.ts +154 -0
  47. package/src/lib/main-thread/lib/scan-module-source/internal/find-task-declarations.ts +205 -0
  48. package/src/lib/main-thread/lib/scan-module-source/internal/parse-module-text.ts +34 -0
  49. package/src/lib/main-thread/lib/scan-module-source/internal/read-literal-string.ts +15 -0
  50. package/src/lib/main-thread/lib/scan-module-source/internal/read-module-loads.ts +316 -0
  51. package/src/lib/main-thread/lib/scan-module-source/internal/scan-module-text.ts +70 -0
  52. package/src/lib/main-thread/lib/scan-module-source/internal/unwrap-type-assertions.ts +10 -0
  53. package/src/lib/main-thread/lib/scan-module-source/internal/visit-non-reference-identifiers.ts +21 -0
  54. package/src/lib/main-thread/shared/fail-run.ts +36 -0
  55. package/src/lib/main-thread/shared/find-failure-reason.ts +43 -0
  56. package/src/lib/main-thread/shared/find-package-directories.ts +34 -0
  57. package/src/lib/main-thread/shared/find-tasks-reachable-from-root-task-file.ts +246 -0
  58. package/src/lib/main-thread/shared/list-directory-paths-up-to-root.ts +13 -0
  59. package/src/lib/main-thread/shared/main-thread-errors.ts +373 -0
  60. package/src/lib/main-thread/shared/package-json-errors.ts +93 -0
  61. package/src/lib/main-thread/shared/provide-discovered-tasks.ts +26 -0
  62. package/src/lib/main-thread/shared/provide-process-groups.ts +63 -0
  63. package/src/lib/main-thread/shared/provide-run-root-task-config.ts +66 -0
  64. package/src/lib/main-thread/shared/provide-stop-state.ts +150 -0
  65. package/src/lib/main-thread/shared/provide-worker-pool.ts +607 -0
  66. package/src/lib/main-thread/shared/read-bare-package-name.ts +3 -0
  67. package/src/lib/main-thread/shared/read-package-json.ts +149 -0
  68. package/src/lib/main-thread/shared/report-task-failure.ts +31 -0
  69. package/src/lib/preload-rewritten-imports-into-each-worker/index.ts +5 -0
  70. package/src/lib/preload-rewritten-imports-into-each-worker/internal/load-rewritten-imports.ts +34 -0
  71. package/src/lib/supervise-the-run/index.ts +15 -0
  72. package/src/lib/supervise-the-run/internal/adopt-orphans-on-linux.ts +73 -0
  73. package/src/lib/supervise-the-run/internal/check-root-task-file.ts +8 -0
  74. package/src/lib/supervise-the-run/internal/report-ready.ts +12 -0
  75. package/src/lib/supervise-the-run/internal/share-process-groups.ts +25 -0
  76. package/src/lib/supervise-the-run/internal/start-watching-the-supervisor.ts +6 -0
  77. package/src/lib/supervise-the-run/internal/supervise-run.ts +155 -0
  78. package/src/lib/supervise-the-run/internal/talk-to-the-supervisor.ts +53 -0
  79. package/src/lib/supervise-the-run/internal/watch-the-supervisor.ts +61 -0
  80. package/src/lib/task/index.ts +5 -0
  81. package/src/lib/task/internal/make-tasks.ts +82 -0
  82. package/src/lib/task/internal/run-command.ts +194 -0
  83. package/src/lib/task/internal/run-root-task.ts +263 -0
  84. package/src/lib/task/internal/run-task-lists.ts +51 -0
  85. package/src/lib/task/internal/trust-output-mode-names.ts +18 -0
  86. package/src/lib/task/internal/trust-task-results.ts +35 -0
  87. package/src/lib/task/internal/use-task-services.ts +4 -0
  88. package/src/lib/worker-thread/index.ts +3 -0
  89. package/src/lib/worker-thread/internal/answer-main-thread-messages.ts +141 -0
  90. package/src/lib/worker-thread/internal/describe-task-body-result.ts +91 -0
  91. package/src/lib/worker-thread/internal/name-config-key-not-set.ts +57 -0
  92. package/src/lib/worker-thread/internal/run-task-body.ts +117 -0
  93. package/src/lib/worker-thread/internal/serve-on-the-parent-port.ts +63 -0
  94. package/src/lib/worker-thread/internal/start-worker-thread.ts +41 -0
  95. package/src/lib/worker-thread/lib/provide-task-services/index.ts +1 -0
  96. package/src/lib/worker-thread/lib/provide-task-services/internal/make-task-logger.ts +34 -0
  97. package/src/lib/worker-thread/lib/provide-task-services/internal/provide-task-services.ts +86 -0
  98. package/src/lib/worker-thread/lib/provide-task-services/internal/read-standard-input-from-main-thread.ts +58 -0
  99. package/src/lib/worker-thread/lib/provide-task-services/internal/resolve-paths-from-package-directory.ts +61 -0
  100. package/src/lib/worker-thread/lib/provide-task-services/internal/run-commands-in-package-directory.ts +24 -0
  101. package/src/lib/worker-thread/lib/provide-task-services/internal/spawn-commands-with-bun.ts +545 -0
  102. package/src/lib/worker-thread/lib/provide-task-services/internal/spawn-reported-process.ts +151 -0
  103. package/src/lib/worker-thread/shared/await-main-thread-answers.ts +76 -0
  104. package/src/lib/worker-thread/shared/decode-task-result-json.ts +114 -0
  105. package/src/lib/worker-thread/shared/define-task-body-record.ts +11 -0
  106. package/src/lib/worker-thread/shared/describe-issue-input.ts +52 -0
  107. package/src/lib/worker-thread/shared/plain-object.ts +8 -0
  108. package/src/lib/worker-thread/shared/provide-worker-state.ts +160 -0
  109. package/src/lib/worker-thread/shared/worker-thread-errors.ts +41 -0
  110. package/src/shared/attribute-reported-failures.ts +25 -0
  111. package/src/shared/claim-processes-to-report.ts +22 -0
  112. package/src/shared/command-errors.ts +44 -0
  113. package/src/shared/complete-discarded-calls-once-sent.ts +12 -0
  114. package/src/shared/cut-to-graphemes.ts +12 -0
  115. package/src/shared/environment-config-errors.ts +74 -0
  116. package/src/shared/error-fields.ts +13 -0
  117. package/src/shared/find-bun-loader-for-script.ts +32 -0
  118. package/src/shared/find-node-modules-package-directory.ts +35 -0
  119. package/src/shared/find-user-locations-in-stack-traces.ts +64 -0
  120. package/src/shared/format-error-lines.ts +53 -0
  121. package/src/shared/format-task-lines.ts +21 -0
  122. package/src/shared/hold-task-logs.ts +24 -0
  123. package/src/shared/invariant-errors.ts +28 -0
  124. package/src/shared/mark-task-worker-thread-contexts.ts +29 -0
  125. package/src/shared/module-graph-errors.ts +49 -0
  126. package/src/shared/name-config-key.ts +42 -0
  127. package/src/shared/names-from-package-json.ts +7 -0
  128. package/src/shared/print-to-standard-error.ts +48 -0
  129. package/src/shared/process-errors.ts +191 -0
  130. package/src/shared/read-environment-config.ts +239 -0
  131. package/src/shared/record-tasks-made.ts +10 -0
  132. package/src/shared/report-cause.ts +140 -0
  133. package/src/shared/report-errors.ts +121 -0
  134. package/src/shared/resolve-task-options.ts +20 -0
  135. package/src/shared/rpc.ts +161 -0
  136. package/src/shared/run-in-child-fiber-and-join.ts +16 -0
  137. package/src/shared/run-marker.ts +7 -0
  138. package/src/shared/save-standard-stream-flags.ts +57 -0
  139. package/src/shared/select-cause-reasons.ts +14 -0
  140. package/src/shared/signal-run-called-twice.ts +11 -0
  141. package/src/shared/standard-input-streams.ts +41 -0
  142. package/src/shared/stop-signals.ts +41 -0
  143. package/src/shared/task-errors.ts +224 -0
  144. package/src/shared/task-package.ts +5 -0
  145. package/src/shared/task-results.ts +55 -0
  146. package/src/shared/task-services.ts +30 -0
  147. package/src/shared/write-trace-file.ts +112 -0
@@ -0,0 +1,335 @@
1
+ import { read, dlopen, FFIType } from "bun:ffi";
2
+ import { getSystemErrorName } from "node:util";
3
+
4
+ import { Effect } from "effect";
5
+
6
+ import { InternalInvariantBroken } from "../../../shared/invariant-errors.ts";
7
+ import {
8
+ SystemCallFailed,
9
+ ProcessArgumentsMalformed,
10
+ ProcessCommandLineNotRead,
11
+ ProcessTableNotRead,
12
+ SystemLibraryNotLoaded,
13
+ } from "../../../shared/process-errors.ts";
14
+ import { programNameOf } from "./command-words.ts";
15
+
16
+ const SYSTEM_LIBRARY_PATH = "/usr/lib/libSystem.B.dylib";
17
+
18
+ const KERNEL_PROCESS_ID = 0;
19
+
20
+ const makeSystemCallFailed = (call: string, processId: number | null, errorNumber: number) =>
21
+ new SystemCallFailed({
22
+ call,
23
+ processId,
24
+ errorNumber,
25
+ errorName: getSystemErrorName(-errorNumber),
26
+ });
27
+
28
+ const readErrorNumberAt = (errorNumberAddress: bigint | number | null) =>
29
+ errorNumberAddress === null
30
+ ? Effect.die(
31
+ new InternalInvariantBroken({
32
+ area: "system calls",
33
+ invariant: "the C library returns the address of the calling thread's errno",
34
+ }),
35
+ )
36
+ : Effect.succeed(read.i32(errorNumberAddress, 0));
37
+
38
+ const CTL_KERN = 1;
39
+ const KERN_ARGMAX = 8;
40
+ const KERN_PROC = 14;
41
+ const KERN_PROC_ALL = 0;
42
+ const KERN_PROC_PID = 1;
43
+ const KERN_PROCARGS2 = 49;
44
+ const KINFO_PROC_SIZE = 648;
45
+ const KINFO_PROC_START_SECONDS_OFFSET = 0;
46
+ const KINFO_PROC_START_MICROSECONDS_OFFSET = 8;
47
+ const KINFO_PROC_FLAGS_OFFSET = 32;
48
+ const KINFO_PROC_STATUS_OFFSET = 36;
49
+ const KINFO_PROC_PROCESS_ID_OFFSET = 40;
50
+ const KINFO_PROC_COMMAND_NAME_OFFSET = 243;
51
+ const KINFO_PROC_PARENT_PROCESS_ID_OFFSET = 560;
52
+ const KINFO_PROC_PROCESS_GROUP_ID_OFFSET = 564;
53
+ const MAXCOMLEN = 16;
54
+ const SIDL = 1;
55
+ const SRUN = 2;
56
+ const SSLEEP = 3;
57
+ const SSTOP = 4;
58
+ const SZOMB = 5;
59
+ const P_WEXIT = 0x20_00;
60
+ const ENOMEM = 12;
61
+ const EINVAL = 22;
62
+ const EXECUTABLE_KEY = "executable_path=";
63
+ const SIXTY_FOUR_BIT_PROCESS_POINTER_SIZE = 8;
64
+
65
+ const processStatusNames = new Map([
66
+ [SIDL, "created"],
67
+ [SRUN, "running"],
68
+ [SSLEEP, "sleeping"],
69
+ [SSTOP, "stopped"],
70
+ [SZOMB, "zombie"],
71
+ ]);
72
+
73
+ const openSystemLibrary = (taskName: string | null) =>
74
+ Effect.acquireRelease(
75
+ Effect.try({
76
+ try: () =>
77
+ dlopen(SYSTEM_LIBRARY_PATH, {
78
+ sysctl: {
79
+ args: [FFIType.ptr, FFIType.u32, FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.u64],
80
+ returns: FFIType.i32,
81
+ },
82
+ __error: { args: [], returns: FFIType.ptr },
83
+ getsid: { args: [FFIType.i32], returns: FFIType.i32 },
84
+ }),
85
+ catch: (cause) =>
86
+ new SystemLibraryNotLoaded({ taskName, libraryPath: SYSTEM_LIBRARY_PATH, cause }),
87
+ }),
88
+ (systemLibrary) =>
89
+ Effect.sync(() => {
90
+ systemLibrary.close();
91
+ }),
92
+ ).pipe(Effect.map((systemLibrary) => systemLibrary.symbols));
93
+
94
+ const callSysctl = (
95
+ symbols: Effect.Success<ReturnType<typeof openSystemLibrary>>,
96
+ managementInformationBaseName: readonly number[],
97
+ value: Uint8Array | null,
98
+ ) =>
99
+ Effect.suspend(() => {
100
+ const name = Int32Array.from(managementInformationBaseName);
101
+ const valueSize = new BigUint64Array([BigInt(value === null ? 0 : value.byteLength)]);
102
+ const returned = symbols.sysctl(name, name.length, value, valueSize, null, 0);
103
+ return readErrorNumberAt(symbols.__error()).pipe(
104
+ Effect.map((errorNumber) => ({
105
+ succeeded: returned === 0,
106
+ errorNumber,
107
+ filledSize: Number(valueSize[0]),
108
+ })),
109
+ );
110
+ });
111
+
112
+ const decodeKernelProcess = (kernelProcesses: Uint8Array, offset: number) => {
113
+ const fields = new DataView(kernelProcesses.buffer, offset, KINFO_PROC_SIZE);
114
+ const status = processStatusNames.get(fields.getInt8(KINFO_PROC_STATUS_OFFSET));
115
+ const commandNameBytes = kernelProcesses.subarray(
116
+ offset + KINFO_PROC_COMMAND_NAME_OFFSET,
117
+ offset + KINFO_PROC_COMMAND_NAME_OFFSET + MAXCOMLEN + 1,
118
+ );
119
+ const commandNameEnd = commandNameBytes.indexOf(0);
120
+ return status === undefined
121
+ ? Effect.die(
122
+ new InternalInvariantBroken({
123
+ area: "macOS process listing",
124
+ invariant: `kinfo_proc p_stat is one of the states in sys/proc.h, not ${fields.getInt8(KINFO_PROC_STATUS_OFFSET)}`,
125
+ }),
126
+ )
127
+ : Effect.succeed({
128
+ processId: fields.getInt32(KINFO_PROC_PROCESS_ID_OFFSET, true),
129
+ parentProcessId: fields.getInt32(KINFO_PROC_PARENT_PROCESS_ID_OFFSET, true),
130
+ processGroupId: fields.getInt32(KINFO_PROC_PROCESS_GROUP_ID_OFFSET, true),
131
+ status,
132
+ startTime: `${fields.getBigInt64(KINFO_PROC_START_SECONDS_OFFSET, true)}.${fields.getInt32(KINFO_PROC_START_MICROSECONDS_OFFSET, true)}`,
133
+ isExiting: (fields.getInt32(KINFO_PROC_FLAGS_OFFSET, true) & P_WEXIT) !== 0,
134
+ commandName: new TextDecoder().decode(
135
+ commandNameEnd === -1 ? commandNameBytes : commandNameBytes.subarray(0, commandNameEnd),
136
+ ),
137
+ });
138
+ };
139
+
140
+ const readKernelProcesses = (
141
+ symbols: Effect.Success<ReturnType<typeof openSystemLibrary>>,
142
+ call: string,
143
+ managementInformationBaseName: readonly number[],
144
+ ): Effect.Effect<
145
+ readonly Effect.Success<ReturnType<typeof decodeKernelProcess>>[],
146
+ SystemCallFailed
147
+ > =>
148
+ Effect.gen(function* () {
149
+ const sizeQuery = yield* callSysctl(symbols, managementInformationBaseName, null);
150
+ if (!sizeQuery.succeeded) {
151
+ return yield* makeSystemCallFailed(call, null, sizeQuery.errorNumber);
152
+ }
153
+ if (sizeQuery.filledSize === 0) {
154
+ return [];
155
+ }
156
+ const kernelProcesses = new Uint8Array(sizeQuery.filledSize);
157
+ const filled = yield* callSysctl(symbols, managementInformationBaseName, kernelProcesses);
158
+ const bufferOutgrownByNewProcesses = !filled.succeeded && filled.errorNumber === ENOMEM;
159
+ if (bufferOutgrownByNewProcesses) {
160
+ return yield* readKernelProcesses(symbols, call, managementInformationBaseName);
161
+ }
162
+ if (!filled.succeeded) {
163
+ return yield* makeSystemCallFailed(call, null, filled.errorNumber);
164
+ }
165
+ return yield* Effect.forEach(
166
+ Array.from({ length: filled.filledSize / KINFO_PROC_SIZE }, (_unused, index) => index),
167
+ (index) => decodeKernelProcess(kernelProcesses, index * KINFO_PROC_SIZE),
168
+ );
169
+ });
170
+
171
+ const decodeArgumentWords = (processId: number, processArguments: Uint8Array) => {
172
+ const argumentCount = new DataView(processArguments.buffer).getInt32(0, true);
173
+ const executablePathStart = Int32Array.BYTES_PER_ELEMENT;
174
+ const executablePathEnd = processArguments.indexOf(0, executablePathStart);
175
+ const executablePrefixLength =
176
+ EXECUTABLE_KEY.length + executablePathEnd + 1 - executablePathStart;
177
+ const paddedExecutablePathLength =
178
+ Math.ceil(executablePrefixLength / SIXTY_FOUR_BIT_PROCESS_POINTER_SIZE) *
179
+ SIXTY_FOUR_BIT_PROCESS_POINTER_SIZE -
180
+ EXECUTABLE_KEY.length;
181
+ const argumentWords =
182
+ executablePathEnd === -1
183
+ ? []
184
+ : new TextDecoder("utf-8", { ignoreBOM: true })
185
+ .decode(processArguments.subarray(executablePathStart + paddedExecutablePathLength))
186
+ .split("\0")
187
+ .slice(0, -1)
188
+ .slice(0, Math.max(argumentCount, 0));
189
+ if (argumentWords.length < argumentCount) {
190
+ return Effect.fail(
191
+ new ProcessArgumentsMalformed({
192
+ processId,
193
+ argumentCount,
194
+ argumentsFound: argumentWords.length,
195
+ }),
196
+ );
197
+ }
198
+ return Effect.succeed(argumentWords);
199
+ };
200
+
201
+ const readCommandLineUnlessGoneWith = (
202
+ symbols: Effect.Success<ReturnType<typeof openSystemLibrary>>,
203
+ processId: number,
204
+ commandName: string,
205
+ ) =>
206
+ Effect.gen(function* () {
207
+ const argumentMaximumValue = new Uint8Array(Int32Array.BYTES_PER_ELEMENT);
208
+ const argumentMaximum = yield* callSysctl(
209
+ symbols,
210
+ [CTL_KERN, KERN_ARGMAX],
211
+ argumentMaximumValue,
212
+ );
213
+ if (!argumentMaximum.succeeded) {
214
+ return yield* makeSystemCallFailed("sysctl kern.argmax", null, argumentMaximum.errorNumber);
215
+ }
216
+ const processArguments = new Uint8Array(
217
+ new DataView(argumentMaximumValue.buffer).getInt32(0, true),
218
+ );
219
+ const readProcessArguments = yield* callSysctl(
220
+ symbols,
221
+ [CTL_KERN, KERN_PROCARGS2, processId],
222
+ processArguments,
223
+ );
224
+ if (readProcessArguments.succeeded) {
225
+ return programNameOf(
226
+ yield* decodeArgumentWords(
227
+ processId,
228
+ processArguments.subarray(0, readProcessArguments.filledSize),
229
+ ),
230
+ commandName,
231
+ );
232
+ }
233
+ if (readProcessArguments.errorNumber !== EINVAL) {
234
+ return yield* makeSystemCallFailed(
235
+ "sysctl kern.procargs2",
236
+ processId,
237
+ readProcessArguments.errorNumber,
238
+ );
239
+ }
240
+ const [stillListed] = yield* readKernelProcesses(symbols, "sysctl kern.proc.pid", [
241
+ CTL_KERN,
242
+ KERN_PROC,
243
+ KERN_PROC_PID,
244
+ processId,
245
+ ]);
246
+ const isGone =
247
+ stillListed === undefined || stillListed.status === "zombie" || stillListed.isExiting;
248
+ return isGone ? null : commandName;
249
+ });
250
+
251
+ const readCommandLineUnlessGone = (
252
+ taskName: string | null,
253
+ processId: number,
254
+ commandName: string,
255
+ ) =>
256
+ Effect.scoped(
257
+ openSystemLibrary(taskName).pipe(
258
+ Effect.flatMap((symbols) =>
259
+ readCommandLineUnlessGoneWith(symbols, processId, commandName).pipe(
260
+ Effect.mapError((cause) => new ProcessCommandLineNotRead({ taskName, processId, cause })),
261
+ ),
262
+ ),
263
+ ),
264
+ );
265
+
266
+ const readKernelProcessTable = (taskName: string | null) =>
267
+ Effect.scoped(
268
+ openSystemLibrary(taskName).pipe(
269
+ Effect.flatMap((symbols) =>
270
+ readKernelProcesses(symbols, "sysctl kern.proc.all", [
271
+ CTL_KERN,
272
+ KERN_PROC,
273
+ KERN_PROC_ALL,
274
+ ]).pipe(
275
+ Effect.map((kernelProcesses) =>
276
+ kernelProcesses.map((kernelProcess) => ({
277
+ ...kernelProcess,
278
+ sessionId:
279
+ kernelProcess.processId === KERNEL_PROCESS_ID
280
+ ? KERNEL_PROCESS_ID
281
+ : symbols.getsid(kernelProcess.processId),
282
+ })),
283
+ ),
284
+ Effect.mapError((cause) => new ProcessTableNotRead({ taskName, cause })),
285
+ ),
286
+ ),
287
+ ),
288
+ );
289
+
290
+ export const listProcessesWithSysctl = (taskName: string | null) =>
291
+ readKernelProcessTable(taskName).pipe(
292
+ Effect.map(
293
+ (kernelProcesses) =>
294
+ new Map(
295
+ kernelProcesses.map((kernelProcess) => [
296
+ kernelProcess.processId,
297
+ {
298
+ parentProcessId: kernelProcess.parentProcessId,
299
+ processGroupId: kernelProcess.processGroupId,
300
+ sessionId: kernelProcess.sessionId,
301
+ startTime: kernelProcess.startTime,
302
+ hasEnded: kernelProcess.status === "zombie" || kernelProcess.isExiting,
303
+ readCommandLineUnlessGone: readCommandLineUnlessGone(
304
+ taskName,
305
+ kernelProcess.processId,
306
+ kernelProcess.commandName,
307
+ ),
308
+ },
309
+ ]),
310
+ ),
311
+ ),
312
+ );
313
+
314
+ export const readProcessWithSysctl = (taskName: string | null, processId: number) =>
315
+ Effect.scoped(
316
+ openSystemLibrary(taskName).pipe(
317
+ Effect.flatMap((symbols) =>
318
+ readKernelProcesses(symbols, "sysctl kern.proc.pid", [
319
+ CTL_KERN,
320
+ KERN_PROC,
321
+ KERN_PROC_PID,
322
+ processId,
323
+ ]).pipe(Effect.mapError((cause) => new ProcessTableNotRead({ taskName, cause }))),
324
+ ),
325
+ Effect.map(([kernelProcess]) =>
326
+ kernelProcess === undefined
327
+ ? null
328
+ : {
329
+ processGroupId: kernelProcess.processGroupId,
330
+ startTime: kernelProcess.startTime,
331
+ hasEnded: kernelProcess.status === "zombie" || kernelProcess.isExiting,
332
+ },
333
+ ),
334
+ ),
335
+ );
@@ -0,0 +1,58 @@
1
+ import { Effect } from "effect";
2
+
3
+ import {
4
+ listProcessesFromProcDirectory,
5
+ readProcessFromProcDirectory,
6
+ } from "./list-linux-processes-from-proc.ts";
7
+ import {
8
+ listProcessesWithSysctl,
9
+ readProcessWithSysctl,
10
+ } from "./list-macos-processes-with-sysctl.ts";
11
+
12
+ export const listProcesses = (taskName: string | null) =>
13
+ Effect.gen(function* () {
14
+ const listedProcesses =
15
+ process.platform === "linux"
16
+ ? yield* listProcessesFromProcDirectory(taskName)
17
+ : yield* listProcessesWithSysctl(taskName);
18
+ const listedProcessesOfEitherPlatform: ReadonlyMap<
19
+ number,
20
+ typeof listedProcesses extends ReadonlyMap<number, infer ListedProcess>
21
+ ? ListedProcess
22
+ : never
23
+ > = listedProcesses;
24
+ return listedProcessesOfEitherPlatform;
25
+ });
26
+
27
+ export const readProcessUnlessGone = (taskName: string | null, processId: number) =>
28
+ Effect.gen(function* () {
29
+ return process.platform === "linux"
30
+ ? yield* readProcessFromProcDirectory(taskName, processId)
31
+ : yield* readProcessWithSysctl(taskName, processId);
32
+ });
33
+
34
+ export const isStillListedAs = (
35
+ taskName: string | null,
36
+ identity: {
37
+ readonly processId: number;
38
+ readonly processGroupId: number;
39
+ readonly startTime: string;
40
+ },
41
+ ) =>
42
+ readProcessUnlessGone(taskName, identity.processId).pipe(
43
+ Effect.map(
44
+ (listedNow) =>
45
+ listedNow !== null &&
46
+ listedNow.startTime === identity.startTime &&
47
+ listedNow.processGroupId === identity.processGroupId,
48
+ ),
49
+ );
50
+
51
+ export const processGroupHasOnlyEndedMembers = Effect.fn("processGroupHasOnlyEndedMembers")(
52
+ function* (taskName: string | null, processGroupId: number) {
53
+ const listedProcesses = yield* listProcesses(taskName);
54
+ return [...listedProcesses.values()]
55
+ .filter((listedProcess) => listedProcess.processGroupId === processGroupId)
56
+ .every((listedProcess) => listedProcess.hasEnded);
57
+ },
58
+ );