@sshkeda/task 0.0.0-stage → 1.0.0
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.
- package/LICENSE +21 -0
- package/README.md +131 -2
- package/package.json +41 -4
- package/src/index.ts +2 -0
- package/src/lib/errors/index.ts +15 -0
- package/src/lib/errors/internal/errors.ts +147 -0
- package/src/lib/kill-leftover-processes/index.ts +17 -0
- package/src/lib/kill-leftover-processes/internal/command-words.ts +16 -0
- package/src/lib/kill-leftover-processes/internal/find-process-ids.ts +36 -0
- package/src/lib/kill-leftover-processes/internal/kill-process-groups.ts +270 -0
- package/src/lib/kill-leftover-processes/internal/list-linux-processes-from-proc.ts +191 -0
- package/src/lib/kill-leftover-processes/internal/list-macos-processes-with-sysctl.ts +335 -0
- package/src/lib/kill-leftover-processes/internal/list-processes.ts +58 -0
- package/src/lib/kill-leftover-processes/internal/mark-run-processes.ts +367 -0
- package/src/lib/kill-leftover-processes/internal/process-cleanup-errors.ts +17 -0
- package/src/lib/kill-leftover-processes/internal/send-signal.ts +32 -0
- package/src/lib/kill-leftover-processes/internal/signal-run-processes.ts +48 -0
- package/src/lib/kill-leftover-processes/internal/started-process-groups.ts +107 -0
- package/src/lib/main-thread/index.ts +3 -0
- package/src/lib/main-thread/internal/listen-for-stop-requests.ts +102 -0
- package/src/lib/main-thread/internal/run-main-thread.ts +81 -0
- package/src/lib/main-thread/internal/run-root-task-fiber.ts +191 -0
- package/src/lib/main-thread/internal/stops-at-first-failure.ts +20 -0
- package/src/lib/main-thread/internal/trace-the-run.ts +107 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/index.ts +7 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/follow-imports-from-entry-files.ts +306 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/load-all-reporting-first-failure.ts +11 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/make-module-graph.ts +48 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/read-configured-loaders.ts +64 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/resolve-module-specifiers.ts +60 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/rewrite-worker-imports.ts +118 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/worker-import-errors.ts +19 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/index.ts +7 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/internal/handle-worker-events.ts +348 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/internal/make-task-runs.ts +207 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/index.ts +1 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/await-within-counted-limit.ts +29 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/execute-task.ts +272 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/make-task-setup-clock.ts +129 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/run-requested-tasks.ts +135 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/shared/define-task-run-state.ts +288 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/shared/make-stopwatch.ts +39 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/shared/share-standard-input-with-root-task.ts +36 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/shared/stop-task.ts +166 -0
- package/src/lib/main-thread/lib/scan-module-source/index.ts +1 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/find-lexical-bindings.ts +154 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/find-task-declarations.ts +205 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/parse-module-text.ts +34 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/read-literal-string.ts +15 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/read-module-loads.ts +316 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/scan-module-text.ts +70 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/unwrap-type-assertions.ts +10 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/visit-non-reference-identifiers.ts +21 -0
- package/src/lib/main-thread/shared/fail-run.ts +36 -0
- package/src/lib/main-thread/shared/find-failure-reason.ts +43 -0
- package/src/lib/main-thread/shared/find-package-directories.ts +34 -0
- package/src/lib/main-thread/shared/find-tasks-reachable-from-root-task-file.ts +246 -0
- package/src/lib/main-thread/shared/list-directory-paths-up-to-root.ts +13 -0
- package/src/lib/main-thread/shared/main-thread-errors.ts +373 -0
- package/src/lib/main-thread/shared/package-json-errors.ts +93 -0
- package/src/lib/main-thread/shared/provide-discovered-tasks.ts +26 -0
- package/src/lib/main-thread/shared/provide-process-groups.ts +63 -0
- package/src/lib/main-thread/shared/provide-run-root-task-config.ts +66 -0
- package/src/lib/main-thread/shared/provide-stop-state.ts +150 -0
- package/src/lib/main-thread/shared/provide-worker-pool.ts +607 -0
- package/src/lib/main-thread/shared/read-bare-package-name.ts +3 -0
- package/src/lib/main-thread/shared/read-package-json.ts +149 -0
- package/src/lib/main-thread/shared/report-task-failure.ts +31 -0
- package/src/lib/preload-rewritten-imports-into-each-worker/index.ts +5 -0
- package/src/lib/preload-rewritten-imports-into-each-worker/internal/load-rewritten-imports.ts +34 -0
- package/src/lib/supervise-the-run/index.ts +15 -0
- package/src/lib/supervise-the-run/internal/adopt-orphans-on-linux.ts +73 -0
- package/src/lib/supervise-the-run/internal/check-root-task-file.ts +8 -0
- package/src/lib/supervise-the-run/internal/report-ready.ts +12 -0
- package/src/lib/supervise-the-run/internal/share-process-groups.ts +25 -0
- package/src/lib/supervise-the-run/internal/start-watching-the-supervisor.ts +6 -0
- package/src/lib/supervise-the-run/internal/supervise-run.ts +155 -0
- package/src/lib/supervise-the-run/internal/talk-to-the-supervisor.ts +53 -0
- package/src/lib/supervise-the-run/internal/watch-the-supervisor.ts +61 -0
- package/src/lib/task/index.ts +5 -0
- package/src/lib/task/internal/make-tasks.ts +82 -0
- package/src/lib/task/internal/run-command.ts +194 -0
- package/src/lib/task/internal/run-root-task.ts +263 -0
- package/src/lib/task/internal/run-task-lists.ts +51 -0
- package/src/lib/task/internal/trust-output-mode-names.ts +18 -0
- package/src/lib/task/internal/trust-task-results.ts +35 -0
- package/src/lib/task/internal/use-task-services.ts +4 -0
- package/src/lib/worker-thread/index.ts +3 -0
- package/src/lib/worker-thread/internal/answer-main-thread-messages.ts +141 -0
- package/src/lib/worker-thread/internal/describe-task-body-result.ts +91 -0
- package/src/lib/worker-thread/internal/name-config-key-not-set.ts +57 -0
- package/src/lib/worker-thread/internal/run-task-body.ts +117 -0
- package/src/lib/worker-thread/internal/serve-on-the-parent-port.ts +63 -0
- package/src/lib/worker-thread/internal/start-worker-thread.ts +41 -0
- package/src/lib/worker-thread/lib/provide-task-services/index.ts +1 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/make-task-logger.ts +34 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/provide-task-services.ts +86 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/read-standard-input-from-main-thread.ts +58 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/resolve-paths-from-package-directory.ts +61 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/run-commands-in-package-directory.ts +24 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/spawn-commands-with-bun.ts +545 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/spawn-reported-process.ts +151 -0
- package/src/lib/worker-thread/shared/await-main-thread-answers.ts +76 -0
- package/src/lib/worker-thread/shared/decode-task-result-json.ts +114 -0
- package/src/lib/worker-thread/shared/define-task-body-record.ts +11 -0
- package/src/lib/worker-thread/shared/describe-issue-input.ts +52 -0
- package/src/lib/worker-thread/shared/plain-object.ts +8 -0
- package/src/lib/worker-thread/shared/provide-worker-state.ts +160 -0
- package/src/lib/worker-thread/shared/worker-thread-errors.ts +41 -0
- package/src/shared/attribute-reported-failures.ts +25 -0
- package/src/shared/claim-processes-to-report.ts +22 -0
- package/src/shared/command-errors.ts +44 -0
- package/src/shared/complete-discarded-calls-once-sent.ts +12 -0
- package/src/shared/cut-to-graphemes.ts +12 -0
- package/src/shared/environment-config-errors.ts +74 -0
- package/src/shared/error-fields.ts +13 -0
- package/src/shared/find-bun-loader-for-script.ts +32 -0
- package/src/shared/find-node-modules-package-directory.ts +35 -0
- package/src/shared/find-user-locations-in-stack-traces.ts +64 -0
- package/src/shared/format-error-lines.ts +53 -0
- package/src/shared/format-task-lines.ts +21 -0
- package/src/shared/hold-task-logs.ts +24 -0
- package/src/shared/invariant-errors.ts +28 -0
- package/src/shared/mark-task-worker-thread-contexts.ts +29 -0
- package/src/shared/module-graph-errors.ts +49 -0
- package/src/shared/name-config-key.ts +42 -0
- package/src/shared/names-from-package-json.ts +7 -0
- package/src/shared/print-to-standard-error.ts +48 -0
- package/src/shared/process-errors.ts +191 -0
- package/src/shared/read-environment-config.ts +239 -0
- package/src/shared/record-tasks-made.ts +10 -0
- package/src/shared/report-cause.ts +140 -0
- package/src/shared/report-errors.ts +121 -0
- package/src/shared/resolve-task-options.ts +20 -0
- package/src/shared/rpc.ts +161 -0
- package/src/shared/run-in-child-fiber-and-join.ts +16 -0
- package/src/shared/run-marker.ts +7 -0
- package/src/shared/save-standard-stream-flags.ts +57 -0
- package/src/shared/select-cause-reasons.ts +14 -0
- package/src/shared/signal-run-called-twice.ts +11 -0
- package/src/shared/standard-input-streams.ts +41 -0
- package/src/shared/stop-signals.ts +41 -0
- package/src/shared/task-errors.ts +224 -0
- package/src/shared/task-package.ts +5 -0
- package/src/shared/task-results.ts +55 -0
- package/src/shared/task-services.ts +30 -0
- 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
|
+
);
|