On this page
Job Model
A job is a single supervised process execution. Every time peinit forks a process -- starting a service's main binary, running a pre-exec hook, executing a health check, or handling an ad-hoc run request -- that execution is a job with a GUID, lifecycle tracking, and log correlation.
Jobs are the observable unit of "what actually ran." Services are definitions that encode identity, policy, and configuration. Jobs are execution instances. A service restart creates a new job. Job history is visible via eventd.
§7.1.1 Lifecycle
Created --> Running --> Completed
|
+--> Failed
|
+--> Abandoned
| State | Meaning |
|---|---|
| Created | Job object exists but the process has not been forked yet (pre-exec hooks may be running). |
| Running | The main process is alive. |
| Completed | The process exited successfully (exit code 0 or configured success codes). |
| Failed | The process exited with a failure (non-zero exit, signal, timeout, health check failure). |
| Abandoned | The process survived SIGKILL (D-state). |
Job states are simpler than service states. A service has Starting, Active, Stopping, Reloading, etc. because those represent policy. A job tracks a process: it is running, or it finished, or it is stuck.
§7.1.2 Fields
Job {
id: GUID // unique identifier
service: string? // parent service name (null for ad-hoc)
type: enum // ServiceMain, PreExecHook,
// PostExecHook, HealthCheck, AdHoc
state: enum // Created, Running, Completed,
// Failed, Abandoned
pid: pid_t? // main process PID (null until forked)
pidfd: fd? // pidfd for race-free tracking
token_summary: object // identity (SID, groups, privileges)
image_path: string // binary that was exec'd
arguments: string[] // argv
created_at: timestamp // when the job object was created
started_at: timestamp? // when the process was forked
ended_at: timestamp? // when the process exited
exit_code: int? // exit code (null if signal death)
exit_signal: int? // signal (null if clean exit)
failure_cause: string? // structured cause
cgroup_id: string // cgroup path
cgroup_gen: int // cgroup generation number
operation_id: GUID? // the operation that created this
// job (null for ad-hoc jobs)
}
The id MUST be assigned before fork. The pid and pidfd are
populated at fork time. The ended_at, exit_code, and
exit_signal are populated when the process exits.
§7.1.3 Job types
§7.1.3.1 ServiceMain
The main process of a service. Created when a start operation executes the fork/exec sequence. The service tracks its current ServiceMain job GUID. On restart, a new job is created and the service's job GUID updates.
§7.1.3.2 PreExecHook
A pre-exec hook command (ExecStartPre). Each hook invocation is a
separate job. Hook jobs run in the hooks/ sub-cgroup.
§7.1.3.3 PostExecHook
A post-exec hook command (ExecStartPost). Each hook invocation is
a separate job. Hook jobs run in the hooks/ sub-cgroup.
§7.1.3.4 HealthCheck
A periodic health check invocation. Each health check run is a
separate job. Health check jobs run in the health/ sub-cgroup.
§7.1.3.5 AdHoc
An arbitrary process submitted via JFS. Not associated with a persistent service definition. Runs once, reports result. See the Ad-Hoc Jobs section.
§7.1.4 Relationship to services
A service is a long-lived definition with policy. A job is a short-lived process execution.
- A service tracks its current job GUID. Status queries include it.
- When a service restarts, a new job is created. The old job's data was already emitted to eventd.
- Job history for a service is queryable via eventd.
§7.1.4.1 Ownership
| Concern | Owner |
|---|---|
| Restart policy | Service |
| Dependencies | Service |
| Health check schedule | Service |
| ErrorControl | Service |
| Current state (Active, Failed, etc.) | Service |
| Process PID / pidfd | Job |
| Exit code / signal | Job |
| Execution timestamps | Job |
| Log correlation | Job |
| Identity / token | Job |
| cgroup assignment | Job |
§7.1.5 Retention
peinit tracks active jobs in memory. When a job reaches a terminal state (Completed, Failed, Abandoned), peinit MUST emit a structured event to eventd containing the full job record, then drop the job from memory.
peinit does not maintain job history. eventd is the historian.
§7.1.6 eventd integration
peinit MUST emit structured events to eventd at each job lifecycle transition:
job.created -- job object created. Includes job_id, service name, type, image_path, identity, operation_id.
job.started -- process forked. Includes job_id, pid, cgroup path.
job.ended -- process exited or was killed. Includes job_id, final state, exit_code or exit_signal, duration, failure_cause.
All stdout/stderr from a job's process MUST be tagged with the job GUID when forwarded to eventd. This allows queries like "show me logs for job X" to return exactly the output from that execution, not interleaved with previous or subsequent runs.