]> git.ipfire.org Git - thirdparty/systemd.git/commit
qmp-client: eagerly enqueue qmp_capabilities on connect, drop the handshake state...
authorDaan De Meyer <daan@amutable.com>
Fri, 24 Apr 2026 07:51:44 +0000 (07:51 +0000)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 24 Apr 2026 12:29:31 +0000 (14:29 +0200)
commit466662c8bd360e96aabd8325afba82a710c4e02e
tree023e0e121a512ae772fd19fc2f38b1bee3f246da
parent5f420abe1a86eca2ca0a9509edf96979530e885d
qmp-client: eagerly enqueue qmp_capabilities on connect, drop the handshake state machine

QEMU's QMP greeting is an unsolicited, informational, server-initiated advertisement
— it doesn't gate commands. The server accepts (and pipelines) commands the instant
the socket is open. We were using it as a trigger to build qmp_capabilities and
blocking callers via qmp_client_ensure_running() until the reply came back.

Build qmp_capabilities inside qmp_client_connect_fd() instead and let the JsonStream
output queue preserve FIFO ordering between it and any user command a subsequent
invoke() enqueues. That satisfies QEMU's only ordering requirement (cap must precede
other commands) without any blocking in the send path.

Fallout:
  - Collapse QmpClientState to {RUNNING, DISCONNECTED}. The three HANDSHAKE_* states
    and the QMP_CLIENT_STATE_IS_HANDSHAKE() macro go away.
  - Drop qmp_client_dispatch_handshake(); fold greeting-drop into dispatch_reply as
    a one-line shape check.
  - Drop qmp_client_ensure_running() and its qmp_client_send() call site. send()
    now only refuses when state == DISCONNECTED.
  - The qmp_capabilities reply lands on an ordinary slot whose callback logs a
    protocol-level error and force-disconnects if cap negotiation failed, matching
    the old EPROTO behaviour at the same observable boundary.
  - qmp_client_phase() no longer special-cases the old handshake states; it maps
    directly to READING / AWAITING_REPLY based on whether slots are in flight.

Test updates:
  - qmp_client_first_invoke_with_fd → qmp_client_invoke_with_fd. The scenario it
    was pinned to (push_fd+invoke staging order) has been structurally impossible
    since the QmpClientArgs rework in 8ad4adcb6f; eager-cap removes it a second
    way. The test now covers end-to-end fd-passing on the first invoke, accepting
    either recv carrying the single SCM_RIGHTS fd (AF_UNIX absorbs non-scm skbs
    forward into the next scm-bearing skb's recv, so the fd may surface with cap
    or add-fd depending on kernel scheduling — QEMU's FIFO fd queue handles either).
  - qmp_client_invoke_failure_closes_fds restructured around the new invariant:
    invoke no longer blocks and no longer returns ENOTCONN for a dead peer, so
    the fd-leak assertion moves to "still open while the JsonStream queue owns it,
    closed on client teardown" and the nested block is flattened into an explicit
    qmp_client_unref().
src/shared/qmp-client.c
src/test/test-qmp-client.c