/* * 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 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 on_rejected_cert (CockpitStream *stream, const gchar *pem_data, gpointer user_data) { CockpitHttpStream *self = user_data; CockpitChannel *channel = user_data; JsonObject *close_options = NULL; // owned by channel if (self->state != FINISHED) { close_options = cockpit_channel_close_options (channel); json_object_set_string_member (close_options, "rejected-certificate", pem_data); } }
static gboolean on_rejected_certificate (GTlsConnection *conn, GTlsCertificate *peer_cert, GTlsCertificateFlags errors, gpointer user_data) { CockpitChannel *channel = user_data; JsonObject *close_options = NULL; // owned by channel gchar *pem_data = NULL; g_return_val_if_fail (peer_cert != NULL, FALSE); g_object_get (peer_cert, "certificate-pem", &pem_data, NULL); close_options = cockpit_channel_close_options (channel); json_object_set_string_member (close_options, "rejected-certificate", pem_data); g_free (pem_data); return FALSE; }
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 const gchar * prepare_for_close_with_errno (CockpitFswrite *self, const gchar *diagnostic, int err) { JsonObject *options; if (err == EPERM || err == EACCES) { g_debug ("%s: %s: %s", self->path, diagnostic, strerror (err)); return "not-authorized"; } else { g_message ("%s: %s: %s", self->path, diagnostic, strerror (err)); options = cockpit_channel_close_options (COCKPIT_CHANNEL (self)); json_object_set_string_member (options, "message", strerror (err)); return "internal-error"; } }
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 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); }
static void cockpit_fslist_prepare (CockpitChannel *channel) { CockpitFslist *self = COCKPIT_FSLIST (channel); const gchar *problem = "protocol-error"; JsonObject *options; GError *error = NULL; GFile *file = NULL; gboolean watch; COCKPIT_CHANNEL_CLASS (cockpit_fslist_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 fslist1 channel"); goto out; } if (self->path == NULL || *(self->path) == 0) { g_warning ("missing \"path\" option for fslist1 channel"); goto out; } if (!cockpit_json_get_bool (options, "watch", TRUE, &watch)) { g_warning ("invalid \"watch\" option for fslist1 channel"); goto out; } self->cancellable = g_cancellable_new (); file = g_file_new_for_path (self->path); g_file_enumerate_children_async (file, G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, self->cancellable, on_enumerator_ready, self); if (watch) { self->monitor = g_file_monitor_directory (file, 0, NULL, &error); if (self->monitor == NULL) { g_message ("%s: couldn't monitor directory: %s", self->path, error->message); options = cockpit_channel_close_options (channel); json_object_set_string_member (options, "message", error->message); problem = "internal-error"; goto out; } self->sig_changed = g_signal_connect (self->monitor, "changed", G_CALLBACK (on_changed), self); } cockpit_channel_ready (channel); problem = NULL; out: g_clear_error (&error); if (file) g_object_unref (file); if (problem) cockpit_channel_close (channel, problem); }
static void on_socket_connect (GObject *object, GAsyncResult *result, gpointer user_data) { CockpitWebSocketStream *self = COCKPIT_WEB_SOCKET_STREAM (user_data); CockpitChannel *channel = COCKPIT_CHANNEL (self); const gchar *problem = "protocol-error"; gchar **protocols = NULL; GList *l, *names = NULL; GError *error = NULL; JsonObject *options; JsonObject *headers; const gchar *value; JsonNode *node; GIOStream *io; io = cockpit_connect_stream_finish (result, &error); if (error) { problem = cockpit_stream_problem (error, self->origin, "couldn't connect", cockpit_channel_close_options (channel)); cockpit_channel_close (channel, problem); goto out; } options = cockpit_channel_get_options (channel); if (!cockpit_json_get_strv (options, "protocols", NULL, &protocols)) { cockpit_channel_fail (channel, "protocol-error", "%s: invalid \"protocol\" value in WebSocket stream request", self->origin); goto out; } if (G_IS_TLS_CONNECTION (io)) { self->sig_accept_cert = g_signal_connect (G_TLS_CONNECTION (io), "accept-certificate", G_CALLBACK (on_rejected_certificate), self); } else { self->sig_accept_cert = 0; } self->client = web_socket_client_new_for_stream (self->url, self->origin, (const gchar **)protocols, io); node = json_object_get_member (options, "headers"); if (node) { if (!JSON_NODE_HOLDS_OBJECT (node)) { cockpit_channel_fail (channel, "protocol-error", "%s: invalid \"headers\" field in WebSocket stream request", self->origin); goto out; } headers = json_node_get_object (node); names = json_object_get_members (headers); for (l = names; l != NULL; l = g_list_next (l)) { node = json_object_get_member (headers, l->data); if (!node || !JSON_NODE_HOLDS_VALUE (node) || json_node_get_value_type (node) != G_TYPE_STRING) { cockpit_channel_fail (channel, "protocol-error", "%s: invalid header value in WebSocket stream request: %s", self->origin, (gchar *)l->data); goto out; } value = json_node_get_string (node); g_debug ("%s: sending header: %s %s", self->origin, (gchar *)l->data, value); web_socket_client_include_header (WEB_SOCKET_CLIENT (self->client), l->data, value); } } self->sig_open = g_signal_connect (self->client, "open", G_CALLBACK (on_web_socket_open), self); self->sig_message = g_signal_connect (self->client, "message", G_CALLBACK (on_web_socket_message), self); self->sig_closing = g_signal_connect (self->client, "closing", G_CALLBACK (on_web_socket_closing), self); self->sig_close = g_signal_connect (self->client, "close", G_CALLBACK (on_web_socket_close), self); self->sig_error = g_signal_connect (self->client, "error", G_CALLBACK (on_web_socket_error), self); problem = NULL; out: g_clear_error (&error); g_strfreev (protocols); if (io) g_object_unref (io); g_list_free (names); }