mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-28 14:59:47 +00:00
fix(infrastructure): chmod 644 autopilot supervisor files on install (#2963)
📝 Summary: • launchd rejects group/world-writable agent plists — when the installer runs under a umask-0 parent shell, `writeFileSync(plistPath(), plist)` produces a 0666 plist that makes `launchctl load`/`bootstrap` fail with the opaque `Bootstrap failed: 5: Input/output error` and the login-time LaunchAgents scan skip the file silently • on an affected machine the daemon never registers while everything looks installed — the plist exists, launchd's disabled-table says enabled, and no log file is ever created 🔧 Technical Improvements: • `installLaunchd`: write plist with `{ mode: 0o644 }` AND `chmodSync(0o644)` — writeFileSync mode applies only on create, so a reinstall over an existing 0666 plist must normalize explicitly • `installSystemd`: same hardening on the unit file (systemd warns on world-writable units); symmetric with the launchd path • Restart-policy rewrite path (`generateSystemdUnit` rewrite of an existing unit): chmod is load-bearing here — the file always exists, so the write mode never applies • `chmodSync` added to the fs import 📊 Code Changes: 18 insertions, 4 deletions (net +14) 📦 Files Modified: • src/commands/autopilot.ts (minor updates) — mode + chmod on the three supervisor-file writers; comments carry the launchd failure signature so the next EIO hunt greps straight to it
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
* gbrain autopilot --status [--json]
|
||||
*/
|
||||
|
||||
import { existsSync, readFileSync, writeFileSync, mkdirSync, appendFileSync, utimesSync, unlinkSync } from 'fs';
|
||||
import { existsSync, readFileSync, writeFileSync, mkdirSync, appendFileSync, utimesSync, unlinkSync, chmodSync } from 'fs';
|
||||
import { setCliExitVerdict } from '../core/cli-force-exit.ts';
|
||||
import { join } from 'path';
|
||||
import { execSync } from 'child_process';
|
||||
@@ -1263,7 +1263,14 @@ function installLaunchd(wrapperPath: string, home: string, repoPath: string) {
|
||||
try {
|
||||
const agentsDir = join(home, 'Library', 'LaunchAgents');
|
||||
mkdirSync(agentsDir, { recursive: true });
|
||||
writeFileSync(plistPath(), plist);
|
||||
writeFileSync(plistPath(), plist, { mode: 0o644 });
|
||||
// launchd rejects group/world-writable agent plists: bootstrap/load fails
|
||||
// with the opaque "Bootstrap failed: 5: Input/output error" and the login
|
||||
// scan skips the file silently. writeFileSync's mode only applies on
|
||||
// create — a reinstall over an existing plist keeps the old bits (a 0666
|
||||
// plist written under an umask-0 parent stays 0666 forever) — so
|
||||
// normalize unconditionally.
|
||||
chmodSync(plistPath(), 0o644);
|
||||
execSync(`launchctl load "${plistPath()}"`, { stdio: 'pipe' });
|
||||
console.log('Installed launchd service: com.gbrain.autopilot');
|
||||
console.log(` Repo: ${repoPath}`);
|
||||
@@ -1353,7 +1360,11 @@ export function migrateSystemdUnitToRestartAlways(): { rewritten: boolean; reaso
|
||||
return { rewritten: false, reason: 'hand-edited' };
|
||||
}
|
||||
try {
|
||||
writeFileSync(unitPath, generateSystemdUnit(execMatch![1]));
|
||||
writeFileSync(unitPath, generateSystemdUnit(execMatch![1]), { mode: 0o644 });
|
||||
// This path always rewrites an EXISTING unit, so writeFileSync's mode
|
||||
// never applies — chmod is the only thing that normalizes a unit born
|
||||
// 0666 under a umask-0 parent (systemd warns on world-writable units).
|
||||
chmodSync(unitPath, 0o644);
|
||||
try {
|
||||
execSync('systemctl --user daemon-reload', { stdio: 'pipe', timeout: 10_000 });
|
||||
} catch {
|
||||
@@ -1370,7 +1381,10 @@ function installSystemd(wrapperPath: string, repoPath: string) {
|
||||
try {
|
||||
const unitPath = systemdUnitPath();
|
||||
mkdirSync(join(process.env.HOME || '', '.config', 'systemd', 'user'), { recursive: true });
|
||||
writeFileSync(unitPath, unit);
|
||||
writeFileSync(unitPath, unit, { mode: 0o644 });
|
||||
// Same umask-0 hardening as the launchd path (systemd warns on
|
||||
// world-writable units); mode only applies on create, so normalize.
|
||||
chmodSync(unitPath, 0o644);
|
||||
execSync('systemctl --user daemon-reload', { stdio: 'pipe', timeout: 10_000 });
|
||||
execSync('systemctl --user enable --now gbrain-autopilot.service', { stdio: 'pipe', timeout: 15_000 });
|
||||
console.log('Installed systemd user service: gbrain-autopilot.service');
|
||||
|
||||
Reference in New Issue
Block a user