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) {
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);
/* 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). */
int error,
void *userdata) {
+ VmspawnQmpBridge *bridge = ASSERT_PTR(qmp_client_get_userdata(client));
+
assert(client);
if (error < 0) {
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;
}
QmpClient *qmp;
Hashmap *pending_jobs; /* job_id (string, owned) -> PendingJob* */
VmspawnQmpFeatureFlags features;
+ bool setup_done;
} VmspawnQmpBridge;
VmspawnQmpBridge* vmspawn_qmp_bridge_free(VmspawnQmpBridge *b);
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);