File size: 4,183 Bytes
064bfd6 | 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 | /**
* Minimal module for firing MDM subprocess reads without blocking the event loop.
* Has minimal imports — only child_process, fs, and mdmConstants (which only imports os).
*
* Two usage patterns:
* 1. Startup: startMdmRawRead() fires at main.tsx module evaluation, results consumed later via getMdmRawReadPromise()
* 2. Poll/fallback: fireRawRead() creates a fresh read on demand (used by changeDetector and SDK entrypoint)
*
* Raw stdout is consumed by mdmSettings.ts via consumeRawReadResult().
*/
import { execFile } from 'child_process'
import { existsSync } from 'fs'
import {
getMacOSPlistPaths,
MDM_SUBPROCESS_TIMEOUT_MS,
PLUTIL_ARGS_PREFIX,
PLUTIL_PATH,
WINDOWS_REGISTRY_KEY_PATH_HKCU,
WINDOWS_REGISTRY_KEY_PATH_HKLM,
WINDOWS_REGISTRY_VALUE_NAME,
} from './constants.js'
export type RawReadResult = {
plistStdouts: Array<{ stdout: string; label: string }> | null
hklmStdout: string | null
hkcuStdout: string | null
}
let rawReadPromise: Promise<RawReadResult> | null = null
function execFilePromise(
cmd: string,
args: string[],
): Promise<{ stdout: string; code: number | null }> {
return new Promise(resolve => {
execFile(
cmd,
args,
{ encoding: 'utf-8', timeout: MDM_SUBPROCESS_TIMEOUT_MS },
(err, stdout) => {
// biome-ignore lint/nursery/noFloatingPromises: resolve() is not a floating promise
resolve({ stdout: stdout ?? '', code: err ? 1 : 0 })
},
)
})
}
/**
* Fire fresh subprocess reads for MDM settings and return raw stdout.
* On macOS: spawns plutil for each plist path in parallel, picks first winner.
* On Windows: spawns reg query for HKLM and HKCU in parallel.
* On Linux: returns empty (no MDM equivalent).
*/
export function fireRawRead(): Promise<RawReadResult> {
return (async (): Promise<RawReadResult> => {
if (process.platform === 'darwin') {
const plistPaths = getMacOSPlistPaths()
const allResults = await Promise.all(
plistPaths.map(async ({ path, label }) => {
// Fast-path: skip the plutil subprocess if the plist file does not
// exist. Spawning plutil takes ~5ms even for an immediate ENOENT,
// and non-MDM machines never have these files.
// Uses synchronous existsSync to preserve the spawn-during-imports
// invariant: execFilePromise must be the first await so plutil
// spawns before the event loop polls (see main.tsx:3-4).
if (!existsSync(path)) {
return { stdout: '', label, ok: false }
}
const { stdout, code } = await execFilePromise(PLUTIL_PATH, [
...PLUTIL_ARGS_PREFIX,
path,
])
return { stdout, label, ok: code === 0 && !!stdout }
}),
)
// First source wins (array is in priority order)
const winner = allResults.find(r => r.ok)
return {
plistStdouts: winner
? [{ stdout: winner.stdout, label: winner.label }]
: [],
hklmStdout: null,
hkcuStdout: null,
}
}
if (process.platform === 'win32') {
const [hklm, hkcu] = await Promise.all([
execFilePromise('reg', [
'query',
WINDOWS_REGISTRY_KEY_PATH_HKLM,
'/v',
WINDOWS_REGISTRY_VALUE_NAME,
]),
execFilePromise('reg', [
'query',
WINDOWS_REGISTRY_KEY_PATH_HKCU,
'/v',
WINDOWS_REGISTRY_VALUE_NAME,
]),
])
return {
plistStdouts: null,
hklmStdout: hklm.code === 0 ? hklm.stdout : null,
hkcuStdout: hkcu.code === 0 ? hkcu.stdout : null,
}
}
return { plistStdouts: null, hklmStdout: null, hkcuStdout: null }
})()
}
/**
* Fire raw subprocess reads once for startup. Called at main.tsx module evaluation.
* Results are consumed via getMdmRawReadPromise().
*/
export function startMdmRawRead(): void {
if (rawReadPromise) return
rawReadPromise = fireRawRead()
}
/**
* Get the startup promise. Returns null if startMdmRawRead() wasn't called.
*/
export function getMdmRawReadPromise(): Promise<RawReadResult> | null {
return rawReadPromise
}
|