static void on_socket_close (WebSocketConnection *socket, gpointer user_data) { CockpitChannelSocket *self = COCKPIT_CHANNEL_SOCKET (user_data); CockpitChannel *channel = COCKPIT_CHANNEL (user_data); const gchar *problem = NULL; gushort code; if (self->closed) return; code = web_socket_connection_get_close_code (socket); if (code == WEB_SOCKET_CLOSE_NORMAL) { cockpit_channel_control (channel, "done", NULL); } else { problem = web_socket_connection_get_close_data (socket); if (problem == NULL) problem = "disconnected"; } cockpit_channel_close (channel, problem); }
static void cockpit_channel_constructed (GObject *object) { CockpitChannel *self = COCKPIT_CHANNEL (object); G_OBJECT_CLASS (cockpit_channel_parent_class)->constructed (object); g_return_if_fail (self->priv->id != NULL); self->priv->capabilities = NULL; self->priv->recv_sig = g_signal_connect (self->priv->transport, "recv", G_CALLBACK (on_transport_recv), self); self->priv->control_sig = g_signal_connect (self->priv->transport, "control", G_CALLBACK (on_transport_control), self); self->priv->close_sig = g_signal_connect (self->priv->transport, "closed", G_CALLBACK (on_transport_closed), self); /* * If the caller didn't ask us to be frozen, then we temporarily * freeze things and run the prepare. Message input will be frozen until * cockpit_channel_ready() is called. */ if (!self->priv->frozen) { self->priv->frozen = g_queue_new (); self->priv->prepare_tag = g_idle_add_full (G_PRIORITY_HIGH, on_idle_prepare, self, NULL); } }
static gboolean on_idle_protocol_error (gpointer user_data) { CockpitChannel *channel = COCKPIT_CHANNEL (user_data); cockpit_channel_close (channel, "protocol-error"); return FALSE; }
static void on_web_socket_open (WebSocketConnection *connection, gpointer user_data) { CockpitWebSocketStream *self = COCKPIT_WEB_SOCKET_STREAM (user_data); CockpitChannel *channel = COCKPIT_CHANNEL (user_data); JsonObject *object; JsonObject *headers; GHashTableIter iter; gpointer key, value; headers = json_object_new (); g_hash_table_iter_init (&iter, web_socket_client_get_headers (WEB_SOCKET_CLIENT (self->client))); while (g_hash_table_iter_next (&iter, &key, &value)) json_object_set_string_member (headers, key, value); object = json_object_new (); json_object_set_object_member (object, "headers", headers); cockpit_channel_control (channel, "response", object); json_object_unref (object); cockpit_channel_ready (channel, NULL); }
static void cockpit_channel_real_prepare (CockpitChannel *channel) { CockpitChannel *self = COCKPIT_CHANNEL (channel); JsonObject *options; const gchar *binary; options = cockpit_channel_get_options (self); if (!cockpit_channel_ensure_capable (self, options)) return; if (G_OBJECT_TYPE (channel) == COCKPIT_TYPE_CHANNEL) { cockpit_channel_close (channel, "not-supported"); return; } if (!cockpit_json_get_string (options, "binary", NULL, &binary)) { cockpit_channel_fail (self, "protocol-error", "channel has invalid \"binary\" option"); } else if (binary != NULL) { self->priv->binary_ok = TRUE; if (!g_str_equal (binary, "raw")) { cockpit_channel_fail (self, "protocol-error", "channel has invalid \"binary\" option: %s", binary); } } }
static void cockpit_channel_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { CockpitChannel *self = COCKPIT_CHANNEL (object); switch (prop_id) { case PROP_TRANSPORT: self->priv->transport = g_value_dup_object (value); break; case PROP_ID: self->priv->id = g_value_dup_string (value); break; case PROP_OPTIONS: self->priv->open_options = g_value_dup_boxed (value); break; case PROP_CAPABILITIES: g_return_if_fail (self->priv->capabilities == NULL); self->priv->capabilities = g_value_dup_boxed (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void on_throttle_pressure (GObject *object, gboolean throttle, gpointer user_data) { CockpitChannel *self = COCKPIT_CHANNEL (user_data); GQueue *throttled; JsonObject *ping; if (throttle) { if (!self->priv->throttled) self->priv->throttled = g_queue_new (); } else { throttled = self->priv->throttled; self->priv->throttled = NULL; while (throttled) { ping = g_queue_pop_head (throttled); if (!ping) { g_queue_free (throttled); throttled = NULL; } else { if (!process_ping (self, ping)) g_assert_not_reached (); /* Because throttle is FALSE */ json_object_unref (ping); } } } }
static gboolean on_timeout_tick (gpointer data) { CockpitMetrics *self = data; CockpitMetricsClass *klass; gint64 next_interval; if (self->priv->timeout > 0) { g_source_remove (self->priv->timeout); self->priv->timeout = 0; } klass = COCKPIT_METRICS_GET_CLASS (self); if (klass->tick) (klass->tick) (self, self->priv->next); self->priv->next += self->priv->interval; next_interval = self->priv->next - g_get_monotonic_time() / 1000; if (next_interval < 0) next_interval = 0; if (next_interval <= G_MAXUINT) self->priv->timeout = g_timeout_add (next_interval, on_timeout_tick, self); else if (next_interval / 1000 <= G_MAXUINT) self->priv->timeout = g_timeout_add_seconds (next_interval / 1000, on_timeout_tick, self); else cockpit_channel_close (COCKPIT_CHANNEL (self), "internal-error"); return FALSE; }
static void close_with_errno (CockpitFswrite *self, const gchar *diagnostic, int err) { cockpit_channel_close (COCKPIT_CHANNEL (self), prepare_for_close_with_errno (self, diagnostic, err)); }
static gboolean on_web_socket_closing (WebSocketConnection *connection, gpointer user_data) { CockpitChannel *channel = COCKPIT_CHANNEL (user_data); cockpit_channel_control (channel, "done", NULL); return TRUE; }
static void on_socket_message (WebSocketConnection *connection, WebSocketDataType data_type, GBytes *payload, gpointer user_data) { CockpitChannel *channel = COCKPIT_CHANNEL (user_data); cockpit_channel_send (channel, payload, data_type == WEB_SOCKET_DATA_TEXT); }
static gboolean on_idle_prepare (gpointer data) { CockpitChannel *self = COCKPIT_CHANNEL (data); g_object_ref (self); cockpit_channel_prepare (self); g_object_unref (self); return FALSE; }
static void on_changed (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, gpointer user_data) { CockpitFslist *self = COCKPIT_FSLIST(user_data); cockpit_fswatch_emit_event (COCKPIT_CHANNEL(self), file, other_file, event_type); }
static void write_builder (CockpitDBusJson1 *self, JsonBuilder *builder) { GBytes *bytes; json_builder_end_object (builder); bytes = _json_builder_to_bytes (self, builder); cockpit_channel_send (COCKPIT_CHANNEL (self), bytes, TRUE); g_bytes_unref (bytes); }
static void cockpit_channel_finalize (GObject *object) { CockpitChannel *self = COCKPIT_CHANNEL (object); g_object_unref (self->priv->transport); json_object_unref (self->priv->open_options); if (self->priv->close_options) json_object_unref (self->priv->close_options); G_OBJECT_CLASS (cockpit_channel_parent_class)->finalize (object); }
static void cockpit_channel_constructed (GObject *object) { CockpitChannel *self = COCKPIT_CHANNEL (object); G_OBJECT_CLASS (cockpit_channel_parent_class)->constructed (object); self->priv->recv_sig = g_signal_connect (self->priv->transport, "recv", G_CALLBACK (on_transport_recv), self); self->priv->close_sig = g_signal_connect (self->priv->transport, "closed", G_CALLBACK (on_transport_closed), self); }
static void on_transport_closed (CockpitTransport *transport, const gchar *problem, gpointer user_data) { CockpitChannel *self = COCKPIT_CHANNEL (user_data); if (!self->priv->closed) { self->priv->closed = TRUE; g_signal_emit (self, cockpit_channel_sig_closed, 0, problem); } }
static void on_transport_closed (CockpitTransport *transport, const gchar *problem, gpointer user_data) { CockpitChannel *self = COCKPIT_CHANNEL (user_data); self->priv->transport_closed = TRUE; if (problem == NULL) problem = "disconnected"; if (!self->priv->emitted_close) cockpit_channel_close (self, problem); }
static void mock_case_channel_constructed (GObject *obj) { MockCaseChannel *self = (MockCaseChannel *)obj; const gchar *payload = NULL; JsonObject *options; G_OBJECT_CLASS (mock_case_channel_parent_class)->constructed (obj); options = cockpit_channel_get_options (COCKPIT_CHANNEL (obj)); if (!cockpit_json_get_string (options, "payload", NULL, &payload)) g_assert_not_reached (); if (g_strcmp0 (payload, "upper") == 0) self->function = g_ascii_toupper; else if (g_strcmp0 (payload, "lower") == 0) self->function = g_ascii_tolower; else g_assert_not_reached (); cockpit_channel_ready (COCKPIT_CHANNEL (self)); }
static void cockpit_text_stream_constructed (GObject *object) { CockpitTextStream *self = COCKPIT_TEXT_STREAM (object); CockpitChannel *channel = COCKPIT_CHANNEL (self); GSocketAddress *address; const gchar *unix_path; const gchar **argv; const gchar **env; G_OBJECT_CLASS (cockpit_text_stream_parent_class)->constructed (object); unix_path = cockpit_channel_get_option (channel, "unix"); argv = cockpit_channel_get_strv_option (channel, "spawn"); if (argv == NULL && unix_path == NULL) { g_warning ("did not receive a unix or spawn option"); g_idle_add_full (G_PRIORITY_DEFAULT, on_idle_protocol_error, g_object_ref (channel), g_object_unref); return; } else if (argv != NULL && unix_path != NULL) { g_warning ("received both a unix and spawn option"); g_idle_add_full (G_PRIORITY_DEFAULT, on_idle_protocol_error, g_object_ref (channel), g_object_unref); return; } else if (unix_path) { self->name = unix_path; address = g_unix_socket_address_new (unix_path); self->pipe = cockpit_pipe_connect (self->name, address); g_object_unref (address); } else if (argv) { self->name = argv[0]; env = cockpit_channel_get_strv_option (channel, "environ"); if (cockpit_channel_get_bool_option (channel, "pty")) self->pipe = cockpit_pipe_pty (argv, env, NULL); else self->pipe = cockpit_pipe_spawn (argv, env, NULL); } self->sig_read = g_signal_connect (self->pipe, "read", G_CALLBACK (on_pipe_read), self); self->sig_close = g_signal_connect (self->pipe, "close", G_CALLBACK (on_pipe_close), self); self->open = TRUE; cockpit_channel_ready (channel); }
static void on_enumerator_ready (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; GFileEnumerator *enumerator; JsonObject *options; const gchar *problem; enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, &error); if (enumerator == NULL) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { CockpitFslist *self = COCKPIT_FSLIST (user_data); problem = error_to_problem (error); if (problem) g_debug ("%s: couldn't list directory: %s", self->path, error->message); else g_warning ("%s: couldn't list directory: %s", self->path, error->message); options = cockpit_channel_close_options (COCKPIT_CHANNEL (self)); json_object_set_string_member (options, "message", error->message); cockpit_channel_close (COCKPIT_CHANNEL (self), problem ? problem : "internal-error"); } g_clear_error (&error); return; } CockpitFslist *self = COCKPIT_FSLIST (user_data); g_file_enumerator_next_files_async (enumerator, 10, G_PRIORITY_DEFAULT, self->cancellable, on_files_listed, self); }
static void cockpit_channel_real_prepare (CockpitChannel *channel) { CockpitChannel *self = COCKPIT_CHANNEL (channel); JsonObject *options; const gchar *binary; const gchar *payload; options = cockpit_channel_get_options (self); if (!cockpit_channel_ensure_capable (self, options)) return; if (G_OBJECT_TYPE (channel) == COCKPIT_TYPE_CHANNEL) { if (!cockpit_json_get_string (options, "payload", NULL, &payload)) payload = NULL; if (payload) { g_warning ("bridge doesn't support payloads of type: %s", payload); cockpit_channel_close (channel, "not-supported"); } else { g_warning ("no payload type present in request to open channel"); cockpit_channel_close (channel, "protocol-error"); } return; } if (!cockpit_json_get_string (options, "binary", NULL, &binary)) { g_warning ("%s: channel has invalid \"binary\" option", self->priv->id); cockpit_channel_close (self, "protocol-error"); } else if (binary != NULL) { self->priv->binary_ok = TRUE; if (g_str_equal (binary, "base64")) { self->priv->base64_encoding = TRUE; } else if (!g_str_equal (binary, "raw")) { g_warning ("%s: channel has invalid \"binary\" option: %s", self->priv->id, binary); cockpit_channel_close (self, "protocol-error"); } } }
static void on_web_socket_close (WebSocketConnection *connection, gpointer user_data) { CockpitWebSocketStream *self = COCKPIT_WEB_SOCKET_STREAM (user_data); CockpitChannel *channel = COCKPIT_CHANNEL (user_data); const gchar *problem; gushort code; code = web_socket_connection_get_close_code (connection); problem = web_socket_connection_get_close_data (connection); if (code == WEB_SOCKET_CLOSE_NORMAL || code == WEB_SOCKET_CLOSE_GOING_AWAY) { problem = NULL; } else if (problem == NULL || !problem[0]) { /* If we don't have a code but have a last error * use it's code */ if (code == 0) code = self->last_error_code; switch (code) { case WEB_SOCKET_CLOSE_NO_STATUS: case WEB_SOCKET_CLOSE_ABNORMAL: problem = "disconnected"; break; case WEB_SOCKET_CLOSE_PROTOCOL: case WEB_SOCKET_CLOSE_UNSUPPORTED_DATA: case WEB_SOCKET_CLOSE_BAD_DATA: case WEB_SOCKET_CLOSE_POLICY_VIOLATION: case WEB_SOCKET_CLOSE_TOO_BIG: case WEB_SOCKET_CLOSE_TLS_HANDSHAKE: problem = "protocol-error"; break; case WEB_SOCKET_CLOSE_NO_EXTENSION: problem = "unsupported"; break; default: problem = "internal-error"; break; } } cockpit_channel_close (channel, problem); }
static void cockpit_channel_constructed (GObject *object) { CockpitChannel *self = COCKPIT_CHANNEL (object); G_OBJECT_CLASS (cockpit_channel_parent_class)->constructed (object); g_return_if_fail (self->priv->id != NULL); self->priv->capabilities = NULL; self->priv->recv_sig = g_signal_connect (self->priv->transport, "recv", G_CALLBACK (on_transport_recv), self); self->priv->control_sig = g_signal_connect (self->priv->transport, "control", G_CALLBACK (on_transport_control), self); self->priv->close_sig = g_signal_connect (self->priv->transport, "closed", G_CALLBACK (on_transport_closed), self); }
static void cockpit_channel_dispose (GObject *object) { CockpitChannel *self = COCKPIT_CHANNEL (object); /* * This object was destroyed before going to the main loop * no need to wait until later before we fire various signals. */ if (self->priv->prepare_tag) { g_source_remove (self->priv->prepare_tag); self->priv->prepare_tag = 0; } 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; if (!self->priv->emitted_close) cockpit_channel_close (self, "terminated"); if (self->priv->buffer_timeout) g_source_remove(self->priv->buffer_timeout); self->priv->buffer_timeout = 0; if (self->priv->out_buffer) g_bytes_unref (self->priv->out_buffer); self->priv->out_buffer = NULL; cockpit_flow_throttle (COCKPIT_FLOW (self), NULL); g_assert (self->priv->pressure == NULL); if (self->priv->throttled) g_queue_free_full (self->priv->throttled, (GDestroyNotify)json_object_unref); self->priv->throttled = NULL; G_OBJECT_CLASS (cockpit_channel_parent_class)->dispose (object); }
static void on_socket_open (WebSocketConnection *connection, gpointer user_data) { CockpitChannel *channel = COCKPIT_CHANNEL (user_data); JsonObject *open; /* * Actually open the channel. We wait until the WebSocket is open * before doing this, so we don't receive messages from the bridge * before the websocket is open. */ open = cockpit_channel_get_options (channel); cockpit_channel_control (channel, "open", open); /* Tell the channel we're ready */ cockpit_channel_ready (channel, NULL); }
static void write_builder (CockpitDBusJson *self, JsonBuilder *builder) { GBytes *bytes; JsonNode *root; gsize length; gchar *ret; json_builder_end_object (builder); root = json_builder_get_root (builder); ret = cockpit_json_write (root, &length); json_node_free (root); bytes = g_bytes_new_take (ret, length); cockpit_channel_send (COCKPIT_CHANNEL (self), bytes); g_bytes_unref (bytes); }
static void cockpit_channel_real_prepare (CockpitChannel *channel) { CockpitChannel *self = COCKPIT_CHANNEL (channel); JsonObject *options; const gchar *binary; options = cockpit_channel_get_options (self); if (!cockpit_channel_ensure_capable (self, options)) return; if (G_OBJECT_TYPE (channel) == COCKPIT_TYPE_CHANNEL) { cockpit_channel_close (channel, "not-supported"); return; } if (!cockpit_json_get_string (options, "binary", NULL, &binary)) { cockpit_channel_fail (self, "protocol-error", "channel has invalid \"binary\" option"); } else if (binary != NULL) { self->priv->binary_ok = TRUE; if (!g_str_equal (binary, "raw")) { cockpit_channel_fail (self, "protocol-error", "channel has invalid \"binary\" option: %s", binary); } } /* * The default here, can change from FALSE to TRUE over time once we assume that all * cockpit-ws participants have been upgraded sufficiently. The default when we're * on the channel creation side is to handle flow control. */ if (!cockpit_json_get_bool (options, "flow-control", FALSE, &self->priv->flow_control)) { cockpit_channel_fail (self, "protocol-error", "channel has invalid \"flow-control\" option"); } }
static void cockpit_channel_throttle (CockpitFlow *flow, CockpitFlow *controlling) { CockpitChannel *self = COCKPIT_CHANNEL (flow); if (self->priv->pressure) { g_signal_handler_disconnect (self->priv->pressure, self->priv->pressure_sig); g_object_remove_weak_pointer (G_OBJECT (self->priv->pressure), (gpointer *)&self->priv->pressure); self->priv->pressure = NULL; } if (controlling) { self->priv->pressure = controlling; g_object_add_weak_pointer (G_OBJECT (self->priv->pressure), (gpointer *)&self->priv->pressure); self->priv->pressure_sig = g_signal_connect (controlling, "pressure", G_CALLBACK (on_throttle_pressure), self); } }
static void cockpit_channel_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { CockpitChannel *self = COCKPIT_CHANNEL (object); switch (prop_id) { case PROP_TRANSPORT: g_value_set_object (value, self->priv->transport); break; case PROP_ID: g_value_set_string (value, self->priv->id); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }