static void on_stream_close (CockpitStream *stream, const gchar *problem, gpointer user_data) { CockpitHttpStream *self = user_data; CockpitChannel *channel = user_data; self->keep_alive = FALSE; if (self->state != FINISHED) { if (problem) { cockpit_channel_close (channel, problem); } else if (self->state == RELAY_DATA && !self->response_chunked && self->response_length <= 0) { g_debug ("%s: end of stream is end of data", self->name); cockpit_channel_close (channel, NULL); } else { cockpit_channel_fail (channel, "protocol-error", "%s: received truncated HTTP response", self->name); } } }
static gboolean cockpit_channel_ensure_capable (CockpitChannel *channel, JsonObject *options) { gchar **capabilities = NULL; JsonObject *close_options = NULL; // owned by channel gboolean missing = FALSE; gboolean ret = FALSE; gint len; gint i; if (!cockpit_json_get_strv (options, "capabilities", NULL, &capabilities)) { g_message ("got invalid capabilities field in open message"); cockpit_channel_close (channel, "protocol-error"); goto out; } if (!capabilities) { ret = TRUE; goto out; } len = g_strv_length (capabilities); for (i = 0; i < len; i++) { if (channel->priv->capabilities == NULL || !strv_contains(channel->priv->capabilities, capabilities[i])) { g_message ("unsupported capability required: %s", capabilities[i]); missing = TRUE; } } if (missing) { JsonArray *arr = json_array_new (); // owned by closed options if (channel->priv->capabilities != NULL) { len = g_strv_length (channel->priv->capabilities); for (i = 0; i < len; i++) json_array_add_string_element (arr, channel->priv->capabilities[i]); } close_options = cockpit_channel_close_options (channel); json_object_set_array_member (close_options, "capabilities", arr); cockpit_channel_close (channel, "not-supported"); } ret = !missing; out: g_free (capabilities); return ret; }
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 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_open: * @transport: the transport to send/receive messages on * @number: the channel number * @options: the options to open the channel. * * Open a channel for the 'payload' field in @options. Other fields * in @options are dependent on the channel type. * * Guarantee: channel will not close immediately, even on invalid input. * * Returns: (transfer full): the new channel */ CockpitChannel * cockpit_channel_open (CockpitTransport *transport, const gchar *id, JsonObject *options) { CockpitChannel *channel; GType channel_type; const gchar *payload; if (!cockpit_json_get_string (options, "payload", NULL, &payload)) payload = NULL; /* TODO: We need to migrate away from dbus-json1 */ if (g_strcmp0 (payload, "dbus-json1") == 0) channel_type = COCKPIT_TYPE_DBUS_JSON1; else if (g_strcmp0 (payload, "dbus-json2") == 0) channel_type = COCKPIT_TYPE_DBUS_JSON; else if (g_strcmp0 (payload, "rest-json1") == 0) channel_type = COCKPIT_TYPE_REST_JSON; else if (g_strcmp0 (payload, "text-stream") == 0) channel_type = COCKPIT_TYPE_TEXT_STREAM; else if (g_strcmp0 (payload, "resource1") == 0) channel_type = COCKPIT_TYPE_RESOURCE; else if (g_strcmp0 (payload, "null") == 0) channel_type = COCKPIT_TYPE_NULL_CHANNEL; else channel_type = COCKPIT_TYPE_CHANNEL; channel = g_object_new (channel_type, "transport", transport, "id", id, "options", options, NULL); if (channel_type == COCKPIT_TYPE_CHANNEL) { if (payload) { g_warning ("agent 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 channel; }
static gboolean relay_length (CockpitHttpStream *self, CockpitChannel *channel, GByteArray *buffer) { GBytes *message = NULL; gsize block; g_assert (self->response_length >= 0); if (self->response_length == 0) { /* All done, yay */ g_debug ("%s: received enough bytes", self->name); cockpit_channel_close (channel, NULL); g_assert (self->state == FINISHED); } else if (buffer->len == 0) { /* Not enough data? */ return FALSE; } else { block = MIN (buffer->len, self->response_length); self->response_length -= block; message = cockpit_pipe_consume (buffer, 0, block, 0); relay_data (channel, message); g_bytes_unref (message); } return TRUE; }
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_pipe_close (CockpitPipe *pipe, const gchar *problem, gpointer user_data) { CockpitTextStream *self = user_data; CockpitChannel *channel = user_data; gint status; gchar *signal; self->open = FALSE; if (cockpit_pipe_get_pid (pipe, NULL)) { status = cockpit_pipe_exit_status (pipe); if (WIFEXITED (status)) cockpit_channel_close_int_option (channel, "exit-status", WEXITSTATUS (status)); else if (WIFSIGNALED (status)) { signal = cockpit_strsignal (WTERMSIG (status)); cockpit_channel_close_option (channel, "exit-signal", signal); g_free (signal); } else if (status) cockpit_channel_close_int_option (channel, "exit-status", -1); } cockpit_channel_close (channel, problem); }
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); }
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 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_metrics_recv (CockpitChannel *channel, GBytes *message) { g_warning ("received unexpected metrics1 payload"); cockpit_channel_close (channel, "protocol-error"); }
static void cockpit_fswrite_prepare (CockpitChannel *channel) { CockpitFswrite *self = COCKPIT_FSWRITE (channel); const gchar *problem = "protocol-error"; JsonObject *options; gchar *actual_tag = NULL; COCKPIT_CHANNEL_CLASS (cockpit_fswrite_parent_class)->prepare (channel); options = cockpit_channel_get_options (channel); if (!cockpit_json_get_string (options, "path", NULL, &self->path)) { g_warning ("invalid \"path\" option for fswrite1 channel"); goto out; } else if (self->path == NULL || g_str_equal (self->path, "")) { g_warning ("missing \"path\" option for fswrite1 channel"); goto out; } if (!cockpit_json_get_string (options, "tag", NULL, &self->expected_tag)) { g_warning ("%s: invalid \"tag\" option for fswrite1 channel", self->path); goto out; } actual_tag = cockpit_get_file_tag (self->path); if (self->expected_tag && g_strcmp0 (self->expected_tag, actual_tag)) { problem = "change-conflict"; goto out; } // TODO - delay the opening until the first content message. That // way, we don't create a useless temporary file (which might even // fail). for (int i = 1; i < 10000; i++) { self->tmp_path = g_strdup_printf ("%s.%d", self->path, i); self->fd = open (self->tmp_path, O_WRONLY | O_CREAT | O_EXCL, 0666); if (self->fd >= 0 || errno != EEXIST) break; g_free (self->tmp_path); self->tmp_path = NULL; } problem = NULL; if (self->fd < 0) close_with_errno (self, "couldn't open unique file", errno); else cockpit_channel_ready (channel); out: g_free (actual_tag); if (problem) cockpit_channel_close (channel, problem); }
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_fslist_recv (CockpitChannel *channel, GBytes *message) { g_warning ("received unexpected message in fslist1 channel"); cockpit_channel_close (channel, "protocol-error"); }
/* * cockpit_channel_fail: * @self: a channel * @problem: the problem * * Close the channel with a @problem. In addition a "message" field * will be set on the channel, using the @format argument to bulid * the message. The message will also be logged. * * See cockpit_channel_close() for further info. */ void cockpit_channel_fail (CockpitChannel *self, const gchar *problem, const gchar *format, ...) { JsonObject *options; gchar *message; va_list va; g_return_if_fail (problem != NULL); g_return_if_fail (COCKPIT_IS_CHANNEL (self)); va_start (va, format); message = g_strdup_vprintf (format, va); va_end (va); options = cockpit_channel_close_options (self); if (!json_object_has_member (options, "message")) json_object_set_string_member (options, "message", message); g_message ("%s: %s", self->priv->id, message); g_free (message); cockpit_channel_close (self, problem); }
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)); }
/** * cockpit_channel_control: * @self: the channel * @command: the control command * @options: optional control message or NULL * * Send a control message to the other side. * * If @options is not NULL, then it may be modified by this code. * * With @command of "done" will send an EOF to the other side. This * should only be called once. Whether an EOF should be sent or not * depends on the payload type. */ void cockpit_channel_control (CockpitChannel *self, const gchar *command, JsonObject *options) { JsonObject *object; GBytes *message; const gchar *problem; gchar *problem_copy = NULL; g_return_if_fail (COCKPIT_IS_CHANNEL (self)); g_return_if_fail (command != NULL); if (g_str_equal (command, "done")) { g_return_if_fail (self->priv->sent_done == FALSE); self->priv->sent_done = TRUE; } /* If closing save the close options * and let close send the message */ else if (g_str_equal (command, "close")) { if (!self->priv->close_options) { /* Ref for close_options, freed in parent */ self->priv->close_options = json_object_ref (options); } if (!cockpit_json_get_string (options, "problem", NULL, &problem)) problem = NULL; /* Use a problem copy so it out lasts the value in close_options */ problem_copy = g_strdup (problem); cockpit_channel_close (self, problem_copy); goto out; } if (options) object = json_object_ref (options); else object = json_object_new (); json_object_set_string_member (object, "command", command); json_object_set_string_member (object, "channel", self->priv->id); message = cockpit_json_write_bytes (object); json_object_unref (object); cockpit_transport_send (self->priv->transport, NULL, message); g_bytes_unref (message); out: g_free (problem_copy); }
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 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_web_socket_stream_prepare (CockpitChannel *channel) { CockpitWebSocketStream *self = COCKPIT_WEB_SOCKET_STREAM (channel); CockpitConnectable *connectable = NULL; JsonObject *options; const gchar *path; gboolean started = FALSE; COCKPIT_CHANNEL_CLASS (cockpit_web_socket_stream_parent_class)->prepare (channel); if (self->closed) goto out; connectable = cockpit_channel_parse_stream (channel); if (!connectable) goto out; options = cockpit_channel_get_options (channel); if (!cockpit_json_get_string (options, "path", NULL, &path)) { g_warning ("%s: bad \"path\" field in WebSocket stream request", self->origin); goto out; } else if (path == NULL || path[0] != '/') { g_warning ("%s: invalid or missing \"path\" field in WebSocket stream request", self->origin); goto out; } self->url = g_strdup_printf ("%s://%s%s", connectable->tls ? "wss" : "ws", connectable->name, path); self->origin = g_strdup_printf ("%s://%s", connectable->tls ? "https" : "http", connectable->name); /* Parsed elsewhere */ self->binary = json_object_has_member (options, "binary"); cockpit_connect_stream_full (connectable, NULL, on_socket_connect, g_object_ref (self)); started = TRUE; out: if (connectable) { cockpit_connectable_unref (connectable); if (!started) cockpit_channel_close (channel, "protocol-error"); } }
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 cockpit_fswrite_eof (CockpitChannel *channel) { CockpitFswrite *self = COCKPIT_FSWRITE (channel); const gchar *problem = NULL; JsonObject *options; /* Commit the changes when there was no problem */ if (xfsync (self->fd) < 0 || xclose (self->fd) < 0) { problem = prepare_for_close_with_errno (self, "couldn't sync", errno); } else { gchar *actual_tag = cockpit_get_file_tag (self->path); if (self->expected_tag && g_strcmp0 (self->expected_tag, actual_tag)) { problem = "out-of-date"; } else { options = cockpit_channel_close_options (channel); if (!self->got_content) { json_object_set_string_member (options, "tag", "-"); if (unlink (self->path) < 0 && errno != ENOENT) problem = prepare_for_close_with_errno (self, "couldn't unlink", errno); unlink (self->tmp_path); } else { gchar *new_tag = cockpit_get_file_tag (self->tmp_path); json_object_set_string_member (options, "tag", new_tag); if (rename (self->tmp_path, self->path) < 0) problem = prepare_for_close_with_errno (self, "couldn't rename", errno); g_free (new_tag); } } g_free (actual_tag); } self->fd = -1; cockpit_channel_close (channel, problem); }
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 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; }
static void cockpit_channel_dispose (GObject *object) { CockpitChannel *self = COCKPIT_CHANNEL (object); if (self->priv->recv_sig) g_signal_handler_disconnect (self->priv->transport, self->priv->recv_sig); self->priv->recv_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->received) g_queue_free_full (self->priv->received, (GDestroyNotify)g_bytes_unref); self->priv->received = NULL; if (!self->priv->closed) cockpit_channel_close (self, "terminated"); G_OBJECT_CLASS (cockpit_channel_parent_class)->dispose (object); }
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); }
GSocketAddress * cockpit_channel_parse_address (CockpitChannel *self, gchar **possible_name) { GSocketConnectable *connectable; GSocketAddressEnumerator *enumerator; GSocketAddress *address; GError *error = NULL; gchar *name = NULL; connectable = cockpit_channel_parse_connectable (self, &name); if (!connectable) return NULL; /* This is sync, but realistically, it doesn't matter for current use cases */ enumerator = g_socket_connectable_enumerate (connectable); g_object_unref (connectable); address = g_socket_address_enumerator_next (enumerator, NULL, &error); g_object_unref (enumerator); if (error != NULL) { g_warning ("couldn't find address: %s: %s", name, error->message); cockpit_channel_close (self, "not-found"); g_error_free (error); g_free (name); return NULL; } if (possible_name) *possible_name = name; else g_free (name); return address; }
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->frozen) g_queue_free_full (self->priv->frozen, frozen_message_free); self->priv->frozen = NULL; if (!self->priv->emitted_close) cockpit_channel_close (self, "terminated"); G_OBJECT_CLASS (cockpit_channel_parent_class)->dispose (object); }
static void on_files_listed (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; JsonObject *options; GList *files; files = g_file_enumerator_next_files_finish (G_FILE_ENUMERATOR (source_object), res, &error); if (error) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { CockpitFslist *self = COCKPIT_FSLIST (user_data); g_message ("%s: couldn't process files %s", COCKPIT_FSLIST(user_data)->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), "internal-error"); } g_clear_error (&error); return; } CockpitFslist *self = COCKPIT_FSLIST (user_data); if (files == NULL) { JsonObject *msg; GBytes *msg_bytes; msg = json_object_new (); json_object_set_string_member (msg, "event", "present-done"); msg_bytes = cockpit_json_write_bytes (msg); json_object_unref (msg); cockpit_channel_send (COCKPIT_CHANNEL(self), msg_bytes, FALSE); g_bytes_unref (msg_bytes); g_clear_object (&self->cancellable); g_object_unref (source_object); if (self->monitor == NULL) { cockpit_channel_done (COCKPIT_CHANNEL (self)); cockpit_channel_close (COCKPIT_CHANNEL (self), NULL); } return; } for (GList *l = files; l; l = l->next) { GFileInfo *info = G_FILE_INFO (l->data); JsonObject *msg; GBytes *msg_bytes; msg = json_object_new (); json_object_set_string_member (msg, "event", "present"); json_object_set_string_member (msg, "path", g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_NAME)); json_object_set_string_member (msg, "type", cockpit_file_type_to_string (g_file_info_get_file_type (info))); msg_bytes = cockpit_json_write_bytes (msg); json_object_unref (msg); cockpit_channel_send (COCKPIT_CHANNEL(self), msg_bytes, FALSE); g_bytes_unref (msg_bytes); } g_list_free_full (files, g_object_unref); g_file_enumerator_next_files_async (G_FILE_ENUMERATOR (source_object), 10, G_PRIORITY_DEFAULT, self->cancellable, on_files_listed, self); }