Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | 26x 9x 7x 5x 5x 5x 1x 4x 4x 4x 1x 1x 3x 3x 3x 5x 1x 2x 1x 1x 1x 2x 2x 2x 3x 3x 1x 2x 1x 2x 2x 7x 7x 2x 1x 2x 2x 5x 5x 5x 5x 3x 4x 1x 1x 1x 4x 3x 3x 1x 4x | import { startRelay } from '@metamask/kernel-utils/libp2p';
import type { Logger } from '@metamask/logger';
import { mkdir, rm, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { getOcapHome } from '../ocap-home.ts';
import { isProcessAlive, readPidFile, sendSignal, waitFor } from '../utils.ts';
/**
* Get the relay PID file path.
*
* @returns The relay PID file path.
*/
export function getRelayPidPath(): string {
return join(getOcapHome(), 'relay.pid');
}
/**
* Get the relay address file path.
*
* @returns The relay address file path.
*/
export function getRelayAddrPath(): string {
return join(getOcapHome(), 'relay.addr');
}
/**
* Remove the relay PID and address files.
*/
async function removeRelayFiles(): Promise<void> {
await Promise.all([
rm(getRelayPidPath(), { force: true }),
rm(getRelayAddrPath(), { force: true }),
]);
}
/**
* Start the relay server, write a PID file, and register signal handlers for
* cleanup on exit.
*
* @param logger - The logger instance.
*/
export async function startRelayWithBookkeeping(logger: Logger): Promise<void> {
await mkdir(getOcapHome(), { recursive: true });
const existingPid = await readPidFile(getRelayPidPath());
if (existingPid !== undefined && isProcessAlive(existingPid)) {
throw new Error(`Relay is already running (PID: ${existingPid}).`);
}
await writeFile(getRelayPidPath(), String(process.pid));
let libp2p;
try {
libp2p = await startRelay(logger);
} catch (error) {
await removeRelayFiles();
throw error;
}
try {
const relayAddr = libp2p
.getMultiaddrs()
.find((ma) => ma.toString().includes('/ip4/127.0.0.1/tcp/9001/ws/'))
?.toString();
if (relayAddr === undefined) {
throw new Error(
'Relay started but no WS multiaddr found on 127.0.0.1:9001',
);
}
await writeFile(getRelayAddrPath(), relayAddr);
} catch (error) {
await Promise.resolve(libp2p.stop()).catch(() => undefined);
await removeRelayFiles();
throw error;
}
const cleanup = (): void => {
Promise.resolve(libp2p.stop())
.catch(() => undefined)
.finally(() => {
removeRelayFiles()
.catch(() => undefined)
// eslint-disable-next-line n/no-process-exit -- signal handler must force exit after cleanup
.finally(() => process.exit(0));
});
};
process.on('SIGTERM', cleanup);
process.on('SIGINT', cleanup);
}
/**
* Print whether the relay process is running.
*/
export async function printRelayStatus(): Promise<void> {
const pid = await readPidFile(getRelayPidPath());
if (pid !== undefined && isProcessAlive(pid)) {
process.stderr.write(`Relay is running (PID: ${pid}).\n`);
} else {
if (pid !== undefined) {
await removeRelayFiles();
}
process.stderr.write('Relay is not running.\n');
process.exitCode = 1;
}
}
/**
* Stop the relay process. Sends SIGTERM and waits; escalates to SIGKILL if
* `force` is true and SIGTERM is ignored.
*
* @param options - Options.
* @param options.force - Send SIGKILL if SIGTERM fails to stop the relay.
* @returns True if the relay was stopped (or was not running), false otherwise.
*/
export async function stopRelay({
force = false,
}: { force?: boolean } = {}): Promise<boolean> {
const pid = await readPidFile(getRelayPidPath());
if (pid === undefined || !isProcessAlive(pid)) {
if (pid !== undefined) {
await removeRelayFiles();
}
process.stderr.write('Relay is not running.\n');
return true;
}
process.stderr.write('Stopping relay...\n');
let stopped = false;
// Strategy 1: SIGTERM.
stopped = !sendSignal(pid, 'SIGTERM');
if (!stopped) {
stopped = await waitFor(() => !isProcessAlive(pid), 5_000);
}
// Strategy 2: SIGKILL (only with --force).
if (!stopped && force) {
stopped = !sendSignal(pid, 'SIGKILL');
Eif (!stopped) {
stopped = await waitFor(() => !isProcessAlive(pid), 2_000);
}
}
if (stopped) {
await removeRelayFiles();
process.stderr.write('Relay stopped.\n');
} else {
process.stderr.write('Relay did not stop within timeout.\n');
}
return stopped;
}
|