{"version":3,"file":"spawnSyncWithTimeout.cjs","names":["os","wrapCommandWithSandboxUser","TIMEOUT_COMMAND","startSandboxTimeoutWatchdog","child_process","getSandboxUserEnvOverrides","sandboxUserName"],"sources":["../../src/helpers/spawnSyncWithTimeout.ts"],"sourcesContent":["import child_process from 'node:child_process';\nimport os from 'node:os';\n\nimport {\n  getSandboxUserEnvOverrides,\n  killSandboxUserProcesses,\n  sandboxUserName,\n  startSandboxTimeoutWatchdog,\n  TIMEOUT_COMMAND,\n  wrapCommandWithSandboxUser,\n} from './sandboxUser.js';\n\nconst TIME_COMMAND = [os.platform() === 'darwin' ? 'gtime' : '/usr/bin/time', '--format', '%e %M'] as const;\n\n/**\n * Run an untrusted (submission-derived) command with a timeout. When a judge server delegates\n * untrusted execution via `EXERCODE_SANDBOX_USER` (see `sandboxUser.ts`), the whole command,\n * including `timeout`, runs as the sandbox user so the timer can signal the sandboxed process; a\n * harness-owned watchdog enforces the deadline even if the submission kills its own `timeout`, and\n * leftover sandbox processes (e.g. daemonized children) are killed after every run.\n */\nexport function spawnSyncWithTimeout(\n  command: string,\n  args: readonly string[],\n  options: child_process.SpawnSyncOptionsWithStringEncoding,\n  timeoutSeconds: number\n): child_process.SpawnSyncReturns<string> & { timeSeconds: number; memoryBytes: number } {\n  const startTimeMilliseconds = Date.now();\n\n  const env = { ...(options.env ?? process.env) };\n  const wrappedCommand = wrapCommandWithSandboxUser([\n    TIMEOUT_COMMAND,\n    timeoutSeconds.toFixed(3),\n    ...TIME_COMMAND,\n    command,\n    ...args,\n  ]);\n  const watchdog = startSandboxTimeoutWatchdog(timeoutSeconds);\n  let spawnResult: child_process.SpawnSyncReturns<string>;\n  let watchdogFired: boolean;\n  try {\n    spawnResult = child_process.spawnSync(wrappedCommand[0], wrappedCommand.slice(1), {\n      ...options,\n      env: { ...env, ...getSandboxUserEnvOverrides(env) },\n    });\n  } finally {\n    // Read the watchdog state before cancelling, then always cancel: a leaked watchdog is detached\n    // and would SIGKILL a later request's submission.\n    watchdogFired = watchdog.fired();\n    watchdog.cancel();\n    if (sandboxUserName) killSandboxUserProcesses();\n  }\n\n  const stopTimeMilliseconds = Date.now();\n\n  const match = /(?:^|\\n)(\\d+\\.\\d+) (\\d+)\\s*$/.exec(spawnResult.stderr);\n  const stderr = match ? spawnResult.stderr.slice(0, match.index) : spawnResult.stderr;\n  const timeSeconds = Number(match?.[1]) || (stopTimeMilliseconds - startTimeMilliseconds) / 1000;\n  const memoryBytes = Number(match?.[2]) * 1024 || 0;\n\n  // `timeout` reports 124, but a sandboxed submission can kill its own same-UID `timeout` and be\n  // stopped by the watchdog instead, which surfaces as a signal-derived status. Both are timeouts:\n  // reporting the latter as a runtime error would mislabel a submission that outran its deadline.\n  if (spawnResult.status === 124 || watchdogFired) {\n    return { ...spawnResult, status: 0, stderr, timeSeconds: timeoutSeconds + 1e-3, memoryBytes };\n  }\n\n  return { ...spawnResult, stderr, timeSeconds, memoryBytes };\n}\n"],"mappings":"gMAYA,MAAM,EAAe,CAACA,EAAAA,QAAG,SAAS,IAAM,SAAW,QAAU,gBAAiB,WAAY,OAAO,EASjG,SAAgB,EACd,EACA,EACA,EACA,EACuF,CACvF,IAAM,EAAwB,KAAK,IAAI,EAEjC,EAAM,CAAE,GAAI,EAAQ,KAAO,QAAQ,GAAK,EACxC,EAAiBC,EAAAA,2BAA2B,CAChDC,EAAAA,gBACA,EAAe,QAAQ,CAAC,EACxB,GAAG,EACH,EACA,GAAG,CACL,CAAC,EACK,EAAWC,EAAAA,4BAA4B,CAAc,EACvD,EACA,EACJ,GAAI,CACF,EAAcC,EAAAA,QAAc,UAAU,EAAe,GAAI,EAAe,MAAM,CAAC,EAAG,CAChF,GAAG,EACH,IAAK,CAAE,GAAG,EAAK,GAAGC,EAAAA,2BAA2B,CAAG,CAAE,CACpD,CAAC,CACH,QAAU,CAGR,EAAgB,EAAS,MAAM,EAC/B,EAAS,OAAO,EACZC,EAAAA,iBAAiB,EAAA,yBAAyB,CAChD,CAEA,IAAM,EAAuB,KAAK,IAAI,EAEhC,EAAQ,+BAA+B,KAAK,EAAY,MAAM,EAC9D,EAAS,EAAQ,EAAY,OAAO,MAAM,EAAG,EAAM,KAAK,EAAI,EAAY,OACxE,EAAc,OAAO,IAAQ,EAAE,IAAM,EAAuB,GAAyB,IACrF,EAAc,OAAO,IAAQ,EAAE,EAAI,MAAQ,EASjD,OAJI,EAAY,SAAW,KAAO,EACzB,CAAE,GAAG,EAAa,OAAQ,EAAG,SAAQ,YAAa,EAAiB,KAAM,aAAY,EAGvF,CAAE,GAAG,EAAa,SAAQ,cAAa,aAAY,CAC5D"}