10.9 Control Client Examples
These examples connect the svctl operator guide to the control interface. PSPU §4 owns the wire contract; the socket and dispatch mechanisms are in §10.1 and §10.2.
10.9.1 How the control interface works #
The socket speaks newline-delimited JSON: one request object per line, one response object per line. A request and its success response look like this:
Two properties are worth knowing even if you only ever use svctl:
- Every command is access-controlled. By default, anyone who is signed in may connect. When you connect, peinit captures your token from the kernel and runs AccessCheck against the target service's descriptor for every command. There is no "trust localhost," no override. Who may do what is the subject of Who can manage a service.
- Lifecycle commands create operations. A
start/stop/restart/reload/resetreturns anoperation_id— a GUID you can poll. Conflict resolution between concurrent commands happens at the operation layer, which is why two simultaneousstarts merge instead of colliding.
10.9.2 Reading status #
status returns the full picture for one service:
display_nameanddescriptionare the definition's, ornull.stateandcauseare the lifecycle pair — read them together.status_textis the latestSTATUS=string the service sent via sd_notify (nullif never sent; cleared on each restart).progressis how far the service last said it had got, fromPROGRESS=andPROGRESS_UNIT=:{"current": 3, "total": 10, "bounded": true, "unit": "items"}, withtotalandunitnullwhen it didn't say them.nullif it never sent one; cleared on each restart.current_jobandcurrent_operationare the job and operation GUIDs, ornull.healthishealthy,unhealthy,unknown, ornull(no health check).definition_removedistruewhen the definition was deleted but an instance is still draining.warningslists leaked sub-cgroups and other operator-relevant notices.timershas one entry for each timer trigger, described below.grantedis what you may do to this service, as peinit checks it: the rights the service's permissions give you, out ofquery_status,start,stopandinterrogate(reload). A program shows a control only where its right is listed, rather than offering one that will be refused.
Each entry in timers describes one schedule as peinit has it armed:
scheduled_atis the schedule's next occurrence.fires_atis when the timer will actually fire. It is later thanscheduled_atby the random delayTimerJitterdrew for this firing.last_fired_atis when it last fired, ornullif it has not fired since peinit started and has no recorded run.not_armedis set only for a schedule peinit refused, such as one that never comes round. It gives the reason, and the times are thennull.
Times are in UTC. svctl status prints them to the second:
$ svctl status logrotate
logrotate: inactive
cause: clean_exit
timers:
*-*-* 02:00:00
next: 2026-06-02T02:00:00Z, firing at 2026-06-02T02:07:12Z after jitter
last fired: 2026-06-01T02:03:40Z
list returns a compact summary of every service you can query — services you lack SERVICE_QUERY_STATUS on are simply omitted, not denied:
next_timer_at is the soonest fires_at of the service's timers. svctl list shows it in a NEXT TIMER column.
operation-status returns one operation by GUID; an unknown or expired GUID is the UNKNOWN_OPERATION error. (Operations are dropped after a short retention grace once terminal — long enough for a polling client to read the result, not forever.)
job status returns one submitted job's view under "job", and job list returns the views you can query under "jobs":
The fields are explained in Jobs and operations. A job is retained for 60 seconds after it ends; after that its GUID is the UNKNOWN_JOB error. Like list, job list omits the jobs you lack JOB_QUERY on rather than denying them.