| 147 | } |
| 148 | |
| 149 | export async function stop(name: string, signalArg: NodeJS.Signals = 'SIGTERM'): Promise<boolean> { |
| 150 | const proc = processes.get(name); |
| 151 | if (!proc) return false; |
| 152 | if (proc.exitCode !== null) return true; // already exited |
| 153 | try { |
| 154 | proc.child.kill(signalArg); |
| 155 | // Wait up to 5s for graceful exit, then SIGKILL |
| 156 | const exited = await new Promise<boolean>((resolve) => { |
| 157 | const timeout = setTimeout(() => resolve(false), 5000); |
| 158 | proc.child.once('exit', () => { |
| 159 | clearTimeout(timeout); |
| 160 | resolve(true); |
| 161 | }); |
| 162 | }); |
| 163 | if (!exited) { |
| 164 | logger.warn(`Process '${name}' did not exit on ${signalArg}; sending SIGKILL`); |
| 165 | proc.child.kill('SIGKILL'); |
| 166 | } |
| 167 | return true; |
| 168 | } catch (e: any) { |
| 169 | logger.warn(`Failed to stop '${name}': ${e?.message}`); |
| 170 | return false; |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | export async function stopAll(): Promise<void> { |
| 175 | await Promise.all(Array.from(processes.keys()).map(name => stop(name))); |