Skip to content

Commit a2926c2

Browse files
committed
feat: Implement systemd socket activation
1 parent 5c4e894 commit a2926c2

2 files changed

Lines changed: 61 additions & 3 deletions

File tree

components/index.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,18 @@ import { cheapLoadUserData, setupFileStructure } from "./databaseHandler"
7979
import { getFlag, saveFlags } from "./flags"
8080
import { initializePeacockMenu } from "./menus/settings"
8181
import { ConfigRouteParams } from "./types/gameSchemas"
82+
import { getSocketActivationFileDescriptors } from "./socketActivation"
8283

8384
const host = process.env.HOST || "0.0.0.0"
8485
const port = process.env.PORT || 80
8586

87+
const listenFds = getSocketActivationFileDescriptors()
88+
let listenFd: number | null = null
89+
90+
if (listenFds !== null && listenFds.length > 0) {
91+
listenFd = listenFds[0]
92+
}
93+
8694
function uncaught(error: Error): void {
8795
if (
8896
(error.message || "").includes("EADDRINUSE") ||
@@ -577,9 +585,19 @@ export async function startServer(options: {
577585

578586
const httpServer = http.createServer(app)
579587

580-
log(LogLevel.INFO, `Listening on ${host}:${port}...`)
581-
// @ts-expect-error Non-matching method sig
582-
httpServer.listen(port, host)
588+
if (listenFd !== null) {
589+
log(LogLevel.INFO, `Listening on systemd://${listenFd}...`)
590+
httpServer.listen({
591+
fd: listenFd,
592+
})
593+
} else {
594+
log(LogLevel.INFO, `Listening on ${host}:${port}...`)
595+
httpServer.listen({
596+
host,
597+
port,
598+
})
599+
}
600+
583601
log(LogLevel.INFO, "Server started.")
584602

585603
if (getFlag("discordRp") === true) {

components/socketActivation.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* The Peacock Project - a HITMAN server replacement.
3+
* Copyright (C) 2021-2026 The Peacock Project Team
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
19+
// Partially based on https://github.com/derhuerst/node-systemd/blob/master/lib/systemd.js
20+
21+
const SD_LISTEN_FDS_START = 3
22+
23+
export function getSocketActivationFileDescriptors(): number[] | null {
24+
if (process.env.LISTEN_PID === undefined) {
25+
return null
26+
}
27+
28+
const pid = parseInt(process.env.LISTEN_PID)
29+
30+
if (pid !== process.pid) {
31+
return null
32+
}
33+
34+
if (process.env.LISTEN_FDS === undefined) {
35+
return null
36+
}
37+
38+
const fdCount = parseInt(process.env.LISTEN_FDS)
39+
return new Array(fdCount).fill(null).map((_, i) => SD_LISTEN_FDS_START + i)
40+
}

0 commit comments

Comments
 (0)