@izykitten/ce 0.0.1 → 0.0.3-dev.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 +11 -0
- package/README.md +72 -10
- package/bin/ce +2 -2
- package/bin/docker-credential-ce-forgejo +2 -2
- package/dist/ce.js +1456 -0
- package/package.json +12 -5
- package/src/ce.ts +0 -1189
- package/src/libc.ts +0 -104
package/src/libc.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
// The two libc calls Bun has no API for: flock(2) and execve(2).
|
|
2
|
-
//
|
|
3
|
-
// - flock: the Python `ce` serializes credential-helper state with
|
|
4
|
-
// `fcntl.flock(<state>.lock, LOCK_EX)`. Taking the very same kernel lock (not
|
|
5
|
-
// a lockfile scheme) keeps this port mutually exclusive with a Python `ce`
|
|
6
|
-
// sharing the same ~/.docker, and the kernel drops it when a holder dies.
|
|
7
|
-
// - execve: the Python `ce` ends with `os.execvp`, replacing itself with the
|
|
8
|
-
// engine so it keeps the PID, signals, TTY and exit status. A real execve does
|
|
9
|
-
// the same here, with no signal-forwarding or exit-code-relaying layer.
|
|
10
|
-
//
|
|
11
|
-
// libc is opened lazily so commands that never lock or exec (help, argument
|
|
12
|
-
// errors) do not depend on bun:ffi.
|
|
13
|
-
|
|
14
|
-
import { dlopen, FFIType, ptr, read } from "bun:ffi";
|
|
15
|
-
|
|
16
|
-
const LIBC_CANDIDATES: Record<string, string[]> = {
|
|
17
|
-
// musl resolves these reserved names to its built-in libc as well.
|
|
18
|
-
linux: ["libc.so.6", "libc.so", "libc.musl-x86_64.so.1", "libc.musl-aarch64.so.1"],
|
|
19
|
-
darwin: ["libc.dylib", "/usr/lib/libSystem.B.dylib"],
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const symbols = {
|
|
23
|
-
flock: { args: [FFIType.i32, FFIType.i32], returns: FFIType.i32 },
|
|
24
|
-
execve: { args: [FFIType.ptr, FFIType.ptr, FFIType.ptr], returns: FFIType.i32 },
|
|
25
|
-
strerror: { args: [FFIType.i32], returns: FFIType.cstring },
|
|
26
|
-
} as const;
|
|
27
|
-
|
|
28
|
-
type Libc = ReturnType<typeof dlopen<typeof symbols>>["symbols"] & {
|
|
29
|
-
errnoLocation: () => number | null;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
let cached: Libc | undefined;
|
|
33
|
-
|
|
34
|
-
function libc(): Libc {
|
|
35
|
-
if (cached) return cached;
|
|
36
|
-
const errnoSymbol = process.platform === "darwin" ? "__error" : "__errno_location";
|
|
37
|
-
const errors: string[] = [];
|
|
38
|
-
for (const name of LIBC_CANDIDATES[process.platform] ?? ["libc.so.6"]) {
|
|
39
|
-
try {
|
|
40
|
-
const lib = dlopen(name, {
|
|
41
|
-
...symbols,
|
|
42
|
-
[errnoSymbol]: { args: [], returns: FFIType.ptr },
|
|
43
|
-
});
|
|
44
|
-
const s = lib.symbols as unknown as Record<string, (...a: unknown[]) => unknown>;
|
|
45
|
-
cached = {
|
|
46
|
-
...(lib.symbols as unknown as Libc),
|
|
47
|
-
errnoLocation: s[errnoSymbol] as () => number | null,
|
|
48
|
-
};
|
|
49
|
-
return cached;
|
|
50
|
-
} catch (exc) {
|
|
51
|
-
errors.push(`${name}: ${(exc as Error).message}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
throw new Error(`ce: cannot load libc via bun:ffi (${errors.join("; ")})`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** The calling thread's errno, read right after a failed libc call. */
|
|
58
|
-
export function errno(): number {
|
|
59
|
-
const location = libc().errnoLocation();
|
|
60
|
-
return location ? read.i32(location, 0) : 0;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** strerror(3) text, as Python puts in "[Errno N] <text>". */
|
|
64
|
-
export function strerror(code: number): string {
|
|
65
|
-
try {
|
|
66
|
-
return String(libc().strerror(code));
|
|
67
|
-
} catch {
|
|
68
|
-
return `Unknown error ${code}`;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export const LOCK_EX = 2;
|
|
73
|
-
export const EINTR = 4;
|
|
74
|
-
|
|
75
|
-
/** Blocking flock(2). Returns 0 or the failing errno (EINTR is retried, as Python's PEP 475 does). */
|
|
76
|
-
export function flock(fd: number, operation: number): number {
|
|
77
|
-
for (;;) {
|
|
78
|
-
if (libc().flock(fd, operation) === 0) return 0;
|
|
79
|
-
const code = errno();
|
|
80
|
-
if (code !== EINTR) return code;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function cString(value: string): Buffer {
|
|
85
|
-
return Buffer.from(`${value}\0`, "utf8");
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* execve(2). Only returns on failure, with the errno. The buffers stay
|
|
90
|
-
* referenced until the call returns, so the pointers handed to libc are valid.
|
|
91
|
-
*/
|
|
92
|
-
export function execve(path: string, argv: string[], env: Record<string, string | undefined>): number {
|
|
93
|
-
const pathBuf = cString(path);
|
|
94
|
-
const argBufs = argv.map(cString);
|
|
95
|
-
const envBufs = Object.entries(env)
|
|
96
|
-
.filter((entry): entry is [string, string] => entry[1] !== undefined)
|
|
97
|
-
.map(([key, value]) => cString(`${key}=${value}`));
|
|
98
|
-
const argPtrs = new BigUint64Array(argBufs.length + 1);
|
|
99
|
-
argBufs.forEach((buf, i) => (argPtrs[i] = BigInt(ptr(buf))));
|
|
100
|
-
const envPtrs = new BigUint64Array(envBufs.length + 1);
|
|
101
|
-
envBufs.forEach((buf, i) => (envPtrs[i] = BigInt(ptr(buf))));
|
|
102
|
-
libc().execve(pathBuf, argPtrs, envPtrs);
|
|
103
|
-
return errno();
|
|
104
|
-
}
|