(info: DaemonLockInfo, timeoutMs = 1_000)
| 108 | * after an OOM/SIGKILL (#1553). |
| 109 | */ |
| 110 | export function probeDaemonIdentity(info: DaemonLockInfo, timeoutMs = 1_000): Promise<boolean> { |
| 111 | if (!Number.isInteger(info.pid) || info.pid <= 0 || !info.socketPath) return Promise.resolve(false); |
| 112 | return new Promise<boolean>((resolve) => { |
| 113 | let socket: net.Socket; |
| 114 | let buffer = ''; |
| 115 | let done = false; |
| 116 | const finish = (ok: boolean) => { |
| 117 | if (done) return; |
| 118 | done = true; |
| 119 | clearTimeout(timer); |
| 120 | socket.destroy(); |
| 121 | resolve(ok); |
| 122 | }; |
| 123 | const timer = setTimeout(() => finish(false), timeoutMs); |
| 124 | timer.unref?.(); |
| 125 | try { |
| 126 | socket = net.createConnection(info.socketPath); |
| 127 | } catch { |
| 128 | clearTimeout(timer); |
| 129 | resolve(false); |
| 130 | return; |
| 131 | } |
| 132 | socket.setEncoding('utf8'); |
| 133 | socket.on('data', (chunk) => { |
| 134 | buffer += String(chunk); |
| 135 | if (buffer.length > 4096) return finish(false); |
| 136 | const newline = buffer.indexOf('\n'); |
| 137 | if (newline < 0) return; |
| 138 | try { |
| 139 | const hello = JSON.parse(buffer.slice(0, newline)) as Record<string, unknown>; |
| 140 | finish( |
| 141 | hello.protocol === 1 && |
| 142 | hello.pid === info.pid && |
| 143 | (info.version === 'unknown' || hello.codegraph === info.version) |
| 144 | ); |
| 145 | } catch { |
| 146 | finish(false); |
| 147 | } |
| 148 | }); |
| 149 | socket.on('error', () => finish(false)); |
| 150 | socket.on('close', () => finish(false)); |
| 151 | }); |
| 152 | } |
| 153 | |
| 154 | /** |
| 155 | * Serialize a {@link DaemonLockInfo} for writing to the pidfile. JSON for |
no test coverage detected