vmspawn: add QmpClient userdata and VmspawnQmpBridge.setup_done flag

Add qmp_client_set_userdata()/qmp_client_get_userdata() accessors
mirroring the sd_varlink API, and wire up the VmspawnQmpBridge as
userdata on the QmpClient so that command callbacks can retrieve it.

Add a setup_done flag to VmspawnQmpBridge, set by on_cont_complete()
when the VM has booted and all boot-time device setup is finished.
This lets command callbacks differentiate boot-time errors (fatal —
exit event loop) from runtime errors (recoverable — log and continue).

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
Christian Brauner
2026-04-24 14:39:24 +02:00
parent 4ac6fc336d
commit 882049362d
5 changed files with 26 additions and 0 deletions
+17
View File
@@ -49,6 +49,8 @@ struct QmpClient {
QmpClientState state;
sd_json_variant *current; /* most recently parsed message, pending dispatch */
void *userdata;
};
static void qmp_slot_hash_func(const QmpSlot *p, struct siphash *state) {
@@ -508,6 +510,21 @@ bool qmp_client_is_disconnected(QmpClient *c) {
return c->state == QMP_CLIENT_DISCONNECTED;
}
void* qmp_client_set_userdata(QmpClient *c, void *userdata) {
void *old;
assert(c);
old = c->userdata;
c->userdata = userdata;
return old;
}
void* qmp_client_get_userdata(QmpClient *c) {
assert(c);
return c->userdata;
}
/* Map our state to the transport phase used for POLLIN / salvage / timeout decisions. */
static JsonStreamPhase qmp_client_phase(void *userdata) {
QmpClient *c = ASSERT_PTR(userdata);
+3
View File
@@ -54,6 +54,9 @@ bool qmp_client_is_idle(QmpClient *c);
/* True iff the connection is dead. Stable terminal state — once set, it stays set. */
bool qmp_client_is_disconnected(QmpClient *c);
void* qmp_client_set_userdata(QmpClient *c, void *userdata);
void* qmp_client_get_userdata(QmpClient *c);
/* Async send. Returns 0 on send (callback will fire later), negative errno on failure. If
* ret_slot is non-NULL, returns a reference to a QmpSlot which can be used to cancel the call
* (by unreffing it before the reply arrives). */
+4
View File
@@ -1042,6 +1042,8 @@ static int on_cont_complete(
int error,
void *userdata) {
VmspawnQmpBridge *bridge = ASSERT_PTR(qmp_client_get_userdata(client));
assert(client);
if (error < 0) {
@@ -1049,6 +1051,8 @@ static int on_cont_complete(
return sd_event_exit(qmp_client_get_event(client), error);
}
/* VM is running — all boot-time device setup has completed. */
bridge->setup_done = true;
return 0;
}
+1
View File
@@ -28,6 +28,7 @@ typedef struct VmspawnQmpBridge {
QmpClient *qmp;
Hashmap *pending_jobs; /* job_id (string, owned) -> PendingJob* */
VmspawnQmpFeatureFlags features;
bool setup_done;
} VmspawnQmpBridge;
VmspawnQmpBridge* vmspawn_qmp_bridge_free(VmspawnQmpBridge *b);
+1
View File
@@ -387,6 +387,7 @@ int vmspawn_varlink_setup(
ctx->bridge = bridge;
qmp_client_bind_event(ctx->bridge->qmp, on_qmp_event, ctx);
qmp_client_bind_disconnect(ctx->bridge->qmp, on_qmp_disconnect, ctx);
qmp_client_set_userdata(ctx->bridge->qmp, ctx->bridge);
log_debug("Varlink control server listening on %s", listen_address);