fix(web): update foreground systemd services safely (#2542)
* fix(web): update foreground systemd services safely * clarify desktop and remote updates * Revert "clarify desktop and remote updates" This reverts commit aaf4516277de003e132d549d5281811fae96cfd5. * test-web-systemd-updates
This commit is contained in:
@@ -1,3 +1,21 @@
|
||||
const SYSTEMD_SERVICE_UNIT_PATTERN = /^[A-Za-z0-9:_.@-]+\.service$/;
|
||||
|
||||
function resolveSystemdServiceUnit(environment) {
|
||||
if (!environment.INVOCATION_ID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const configuredUnit = typeof environment.OPENCHAMBER_SYSTEMD_UNIT === 'string'
|
||||
? environment.OPENCHAMBER_SYSTEMD_UNIT.trim()
|
||||
: '';
|
||||
const unit = configuredUnit || 'openchamber.service';
|
||||
return SYSTEMD_SERVICE_UNIT_PATTERN.test(unit) ? unit : null;
|
||||
}
|
||||
|
||||
function quotePosixShell(value) {
|
||||
return `'${String(value).replace(/'/g, "'\\''")}'`;
|
||||
}
|
||||
|
||||
export const registerOpenChamberRoutes = (app, dependencies) => {
|
||||
const {
|
||||
fs,
|
||||
@@ -54,7 +72,7 @@ export const registerOpenChamberRoutes = (app, dependencies) => {
|
||||
|
||||
app.post('/api/openchamber/update-install', async (_req, res) => {
|
||||
try {
|
||||
const { spawn: spawnChild } = await import('child_process');
|
||||
const { spawn: spawnChild, spawnSync } = await import('child_process');
|
||||
const {
|
||||
checkForUpdates,
|
||||
getUpdateCommand,
|
||||
@@ -110,6 +128,55 @@ export const registerOpenChamberRoutes = (app, dependencies) => {
|
||||
}
|
||||
const launchMode = storedOptions.launchMode === 'foreground' ? 'foreground' : 'daemon';
|
||||
const isForegroundService = launchMode === 'foreground';
|
||||
const systemdServiceUnit = isForegroundService ? resolveSystemdServiceUnit(process.env) : null;
|
||||
|
||||
if (isForegroundService) {
|
||||
if (!systemdServiceUnit) {
|
||||
return res.status(409).json({
|
||||
error: 'Foreground servers must be updated by their service manager. Set OPENCHAMBER_SYSTEMD_UNIT when running under systemd, or run openchamber update and restart the service.',
|
||||
});
|
||||
}
|
||||
|
||||
const updateJobName = `openchamber-update-${Date.now()}`;
|
||||
const updateLogPath = `journalctl --user-unit ${updateJobName}.service`;
|
||||
const updateScript = [
|
||||
'set -eu',
|
||||
updateCmd,
|
||||
`systemctl --user restart ${quotePosixShell(systemdServiceUnit)}`,
|
||||
].join('\n');
|
||||
const systemdRun = spawnSync('systemd-run', [
|
||||
'--user',
|
||||
`--unit=${updateJobName}`,
|
||||
'--collect',
|
||||
'--service-type=exec',
|
||||
`--setenv=PATH=${process.env.PATH || ''}`,
|
||||
'/bin/sh',
|
||||
'-c',
|
||||
updateScript,
|
||||
], {
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
if (systemdRun.status !== 0) {
|
||||
const detail = (systemdRun.stderr || systemdRun.stdout || '').trim();
|
||||
return res.status(409).json({
|
||||
error: detail || `Could not queue update job for ${systemdServiceUnit}`,
|
||||
});
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: 'Update queued; OpenChamber will restart after installation completes',
|
||||
version: updateInfo.version,
|
||||
packageManager: pm,
|
||||
autoRestart: true,
|
||||
restartManager: 'systemd',
|
||||
jobId: updateJobName,
|
||||
logPath: updateLogPath,
|
||||
});
|
||||
}
|
||||
|
||||
const isWindows = process.platform === 'win32';
|
||||
const quotePosix = (value) => `'${String(value).replace(/'/g, "'\\''")}'`;
|
||||
|
||||
Reference in New Issue
Block a user