static gboolean on_transport_recv (CockpitTransport *transport, const gchar *channel_id, GBytes *data, gpointer user_data) { CockpitChannel *self = user_data; CockpitChannelClass *klass; if (g_strcmp0 (channel_id, self->priv->id) != 0) return FALSE; if (self->priv->ready) { klass = COCKPIT_CHANNEL_GET_CLASS (self); g_assert (klass->recv); (klass->recv) (self, data); } else { if (!self->priv->received) self->priv->received = g_queue_new (); g_queue_push_tail (self->priv->received, g_bytes_ref (data)); } return TRUE; }
/** * cockpit_channel_close: * @self: a channel * @problem: the problem or NULL * * Close the channel. This can be called mulitple times. * * It may be that the channel doesn't close immediately. * The channel will emit the CockpitChannel::closed signal when the * channel actually closes. * * If this is called immediately after or during construction then * the closing will happen after the main loop so that handlers * can connect appropriately. * * A @problem of NULL represents an orderly close. */ void cockpit_channel_close (CockpitChannel *self, const gchar *problem) { CockpitChannelClass *klass; g_return_if_fail (COCKPIT_IS_CHANNEL (self)); /* No further messages should be received */ if (self->priv->recv_sig) g_signal_handler_disconnect (self->priv->transport, self->priv->recv_sig); self->priv->recv_sig = 0; if (self->priv->control_sig) g_signal_handler_disconnect (self->priv->transport, self->priv->control_sig); self->priv->control_sig = 0; if (self->priv->close_sig) g_signal_handler_disconnect (self->priv->transport, self->priv->close_sig); self->priv->close_sig = 0; klass = COCKPIT_CHANNEL_GET_CLASS (self); g_assert (klass->close != NULL); self->priv->emitted_close = TRUE; (klass->close) (self, problem); }
/** * cockpit_channel_ready: * @self: a pipe * * Called by channel implementations to signal when they're * ready. Any messages received before the channel was ready * will be delivered to the channel's recv() vfunc in the order * that they were received. */ void cockpit_channel_ready (CockpitChannel *self) { CockpitChannelClass *klass; GBytes *payload; GQueue *queue; klass = COCKPIT_CHANNEL_GET_CLASS (self); g_assert (klass->recv != NULL); while (self->priv->received) { queue = self->priv->received; self->priv->received = NULL; for (;;) { payload = g_queue_pop_head (queue); if (payload == NULL) break; (klass->recv) (self, payload); g_bytes_unref (payload); } g_queue_free (queue); } self->priv->ready = TRUE; }
static void process_control (CockpitChannel *self, const gchar *command, JsonObject *options) { CockpitChannelClass *klass; const gchar *problem; if (g_str_equal (command, "close")) { g_debug ("close channel %s", self->priv->id); if (!cockpit_json_get_string (options, "problem", NULL, &problem)) problem = NULL; cockpit_channel_close (self, problem); return; } if (g_str_equal (command, "done")) { if (self->priv->received_done) cockpit_channel_fail (self, "protocol-error", "channel received second done"); else self->priv->received_done = TRUE; } klass = COCKPIT_CHANNEL_GET_CLASS (self); if (klass->control) (klass->control) (self, command, options); }
/** * cockpit_channel_close: * @self: a channel * @problem: the problem or NULL * * Close the channel. This can be called mulitple times. * * It may be that the channel doesn't close immediately. * The channel will emit the CockpitChannel::closed signal when the * channel actually closes. * * A @reason of NULL represents an orderly close. */ void cockpit_channel_close (CockpitChannel *self, const gchar *reason) { CockpitChannelClass *klass; g_return_if_fail (COCKPIT_IS_CHANNEL (self)); klass = COCKPIT_CHANNEL_GET_CLASS (self); g_assert (klass->close != NULL); (klass->close) (self, reason); }
static gboolean on_transport_control (CockpitTransport *transport, const char *command, const gchar *channel_id, JsonObject *options, GBytes *payload, gpointer user_data) { CockpitChannel *self = user_data; CockpitChannelClass *klass; const gchar *problem; if (g_strcmp0 (channel_id, self->priv->id) != 0) return FALSE; klass = COCKPIT_CHANNEL_GET_CLASS (self); if (g_str_equal (command, "options")) { if (klass->options) (klass->options) (self, options); return TRUE; } else if (g_str_equal (command, "done")) { if (self->priv->received_done) { g_warning ("%s: channel received second done", self->priv->id); cockpit_channel_close (self, "protocol-error"); } else { self->priv->received_done = TRUE; if (self->priv->ready) { if (klass->done) (klass->done) (self); } } return TRUE; } else if (g_str_equal (command, "close")) { g_debug ("close channel %s", channel_id); if (!cockpit_json_get_string (options, "problem", NULL, &problem)) problem = NULL; cockpit_channel_close (self, problem); } return FALSE; }
/** * cockpit_channel_ready: * @self: a pipe * * Called by channel implementations to signal when they're * ready. Any messages received before the channel was ready * will be delivered to the channel's recv() vfunc in the order * that they were received. * * If this is called immediately after or during construction then * the closing will happen after the main loop so that handlers * can connect appropriately. */ void cockpit_channel_ready (CockpitChannel *self) { CockpitChannelClass *klass; GBytes *decoded; GBytes *payload; GQueue *queue; klass = COCKPIT_CHANNEL_GET_CLASS (self); g_assert (klass->recv != NULL); g_assert (klass->close != NULL); g_object_ref (self); while (self->priv->received) { queue = self->priv->received; self->priv->received = NULL; for (;;) { payload = g_queue_pop_head (queue); if (payload == NULL) break; if (self->priv->base64_encoding) { decoded = base64_decode (payload); g_bytes_unref (payload); payload = decoded; } (klass->recv) (self, payload); g_bytes_unref (payload); } g_queue_free (queue); } cockpit_channel_control (self, "ready", NULL); self->priv->ready = TRUE; /* No more data coming? */ if (self->priv->received_done) { if (klass->control) (klass->control) (self, "done", NULL); } g_object_unref (self); }
static void process_recv (CockpitChannel *self, GBytes *payload) { CockpitChannelClass *klass; if (self->priv->received_done) { cockpit_channel_fail (self, "protocol-error", "channel received message after done"); } else { klass = COCKPIT_CHANNEL_GET_CLASS (self); if (klass->recv) (klass->recv) (self, payload); } }
/** * cockpit_channel_prepare: * @self: the channel * * Usually this is automatically called after the channel is * created and control returns to the mainloop. However you * can preempt that by calling this function. */ void cockpit_channel_prepare (CockpitChannel *self) { CockpitChannelClass *klass; g_return_if_fail (COCKPIT_IS_CHANNEL (self)); if (!self->priv->prepare_tag) return; g_source_remove (self->priv->prepare_tag); self->priv->prepare_tag = 0; if (!self->priv->emitted_close) { klass = COCKPIT_CHANNEL_GET_CLASS (self); g_assert (klass->prepare); (klass->prepare) (self); } }
static gboolean on_transport_recv (CockpitTransport *transport, const gchar *channel_id, GBytes *data, gpointer user_data) { CockpitChannel *self = user_data; CockpitChannelClass *klass; GBytes *decoded = NULL; if (g_strcmp0 (channel_id, self->priv->id) != 0) return FALSE; if (self->priv->received_done) { g_warning ("%s: channel received message after done", self->priv->id); cockpit_channel_close (self, "protocol-error"); return TRUE; } if (self->priv->ready) { if (self->priv->base64_encoding) data = decoded = base64_decode (data); klass = COCKPIT_CHANNEL_GET_CLASS (self); g_assert (klass->recv); (klass->recv) (self, data); } else { if (!self->priv->received) self->priv->received = g_queue_new (); g_queue_push_tail (self->priv->received, g_bytes_ref (data)); } if (decoded) g_bytes_unref (decoded); return TRUE; }