-
-
Notifications
You must be signed in to change notification settings - Fork 277
Expand file tree
/
Copy pathserver-lifecycle.ts
More file actions
117 lines (107 loc) · 3.1 KB
/
Copy pathserver-lifecycle.ts
File metadata and controls
117 lines (107 loc) · 3.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import fs from 'node:fs';
import { isAgentDeviceDaemonProcess } from '../daemon-process.ts';
export { readVersion } from '@agent-device/host-kit/version';
export { readProcessStartTime } from '@agent-device/host-kit/process';
export { resolveDaemonCodeSignature } from '@agent-device/host-kit/code-signature';
export type DaemonLockInfo = {
pid: number;
version: string;
startedAt: number;
processStartTime?: string;
};
export function writeInfo(
baseDir: string,
infoPath: string,
logPath: string,
opts: {
socketPort?: number;
httpPort?: number;
token: string;
version: string;
codeSignature: string;
processStartTime: string | undefined;
},
): void {
if (!fs.existsSync(baseDir)) fs.mkdirSync(baseDir, { recursive: true });
fs.writeFileSync(logPath, '');
const transport = opts.socketPort && opts.httpPort ? 'dual' : opts.httpPort ? 'http' : 'socket';
fs.writeFileSync(
infoPath,
JSON.stringify(
{
port: opts.socketPort,
httpPort: opts.httpPort,
transport,
token: opts.token,
pid: process.pid,
version: opts.version,
codeSignature: opts.codeSignature,
processStartTime: opts.processStartTime,
stateDir: baseDir,
},
null,
2,
),
{
mode: 0o600,
},
);
// writeFileSync only applies mode on creation; tighten pre-existing files too.
fs.chmodSync(infoPath, 0o600);
}
export function removeInfo(infoPath: string): void {
if (fs.existsSync(infoPath)) fs.unlinkSync(infoPath);
}
function readLockInfo(lockPath: string): DaemonLockInfo | null {
if (!fs.existsSync(lockPath)) return null;
try {
const parsed = JSON.parse(fs.readFileSync(lockPath, 'utf8')) as DaemonLockInfo;
if (!Number.isInteger(parsed.pid) || parsed.pid <= 0) return null;
return parsed;
} catch {
return null;
}
}
export function acquireDaemonLock(
baseDir: string,
lockPath: string,
lockData: DaemonLockInfo,
): boolean {
if (!fs.existsSync(baseDir)) fs.mkdirSync(baseDir, { recursive: true });
const payload = JSON.stringify(lockData, null, 2);
const tryWriteLock = (): boolean => {
try {
fs.writeFileSync(lockPath, payload, { flag: 'wx', mode: 0o600 });
return true;
} catch (error) {
if ((error as NodeJS.ErrnoException).code === 'EEXIST') return false;
throw error;
}
};
if (tryWriteLock()) return true;
const existing = readLockInfo(lockPath);
if (
existing?.pid &&
existing.pid !== process.pid &&
isAgentDeviceDaemonProcess(existing.pid, existing.processStartTime)
) {
return false;
}
try {
fs.unlinkSync(lockPath);
} catch {}
return tryWriteLock();
}
export function releaseDaemonLock(lockPath: string): void {
const existing = readLockInfo(lockPath);
if (existing && existing.pid !== process.pid) return;
try {
if (fs.existsSync(lockPath)) fs.unlinkSync(lockPath);
} catch {}
}
export function parseIntegerEnv(raw: string | undefined): number | undefined {
if (raw === undefined) return undefined;
const value = Number(raw);
if (!Number.isInteger(value)) return undefined;
return value;
}