static void cockpit_pipe_transport_send (CockpitTransport *transport, guint channel, GBytes *payload) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (transport); GBytes *prefix; gchar *prefix_str; gsize prefix_len; guint32 size; prefix_str = g_strdup_printf ("xxxx%u\n", channel); prefix_len = strlen (prefix_str); /* See doc/protocol.md */ size = GUINT32_TO_BE (g_bytes_get_size (payload) + prefix_len - 4); memcpy (prefix_str, &size, 4); prefix = g_bytes_new_take (prefix_str, prefix_len); cockpit_pipe_write (self->pipe, prefix); cockpit_pipe_write (self->pipe, payload); g_bytes_unref (prefix); g_debug ("%s: queued %d byte payload", self->name, (int)g_bytes_get_size (payload)); }
static void cockpit_pipe_transport_close (CockpitTransport *transport, const gchar *problem) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (transport); cockpit_pipe_close (self->pipe, problem); }
static void cockpit_pipe_transport_send (CockpitTransport *transport, const gchar *channel_id, GBytes *payload) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (transport); GBytes *prefix; gchar *prefix_str; gsize payload_len; gsize channel_len; if (self->closed) { g_debug ("dropping message on closed transport"); return; } channel_len = channel_id ? strlen (channel_id) : 0; payload_len = g_bytes_get_size (payload); prefix_str = g_strdup_printf ("%" G_GSIZE_FORMAT "\n%s\n", channel_len + 1 + payload_len, channel_id ? channel_id : ""); prefix = g_bytes_new_take (prefix_str, strlen (prefix_str)); cockpit_pipe_write (self->pipe, prefix); cockpit_pipe_write (self->pipe, payload); g_bytes_unref (prefix); g_debug ("%s: queued %" G_GSIZE_FORMAT " byte payload", self->name, payload_len); }
static void on_pipe_close (CockpitPipe *pipe, const gchar *problem, gpointer user_data) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (user_data); gboolean is_cockpit; GError *error = NULL; gint status; self->closed = TRUE; /* This function is called by the base class when it is closed */ if (cockpit_pipe_get_pid (pipe, NULL)) { is_cockpit = g_str_equal (self->name, "cockpit-bridge") || g_str_equal (self->name, "cockpit-session"); if (problem == NULL || g_str_equal (problem, "internal-error")) { status = cockpit_pipe_exit_status (pipe); if (WIFSIGNALED (status) && WTERMSIG (status) == SIGTERM) problem = "terminated"; else if (is_cockpit && WIFEXITED (status) && WEXITSTATUS (status) == 127) problem = "no-cockpit"; // cockpit-bridge not installed else if (WIFEXITED (status) && WEXITSTATUS (status) == 255) problem = "terminated"; // failed or got a signal, etc. else if (!g_spawn_check_exit_status (status, &error)) { problem = "internal-error"; if (is_cockpit) g_warning ("%s: bridge program failed: %s", self->name, error->message); else g_debug ("%s: process failed: %s", self->name, error->message); g_error_free (error); } } else if (g_str_equal (problem, "not-found")) { if (is_cockpit) { g_message ("%s: failed to execute bridge: not found", self->name); problem = "no-cockpit"; } else { g_debug ("%s: failed to run: not found", self->name); } } } g_debug ("%s: closed%s%s", self->name, problem ? ": " : "", problem ? problem : ""); cockpit_transport_emit_closed (COCKPIT_TRANSPORT (self), problem); }
static void on_pipe_read (CockpitPipe *pipe, GByteArray *input, gboolean end_of_data, gpointer user_data) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (user_data); cockpit_transport_read_from_pipe (COCKPIT_TRANSPORT (self), self->name, pipe, &self->closed, input, end_of_data); }
static void cockpit_pipe_transport_constructed (GObject *object) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (object); G_OBJECT_CLASS (cockpit_pipe_transport_parent_class)->constructed (object); g_return_if_fail (self->pipe != NULL); g_object_get (self->pipe, "name", &self->name, NULL); self->read_sig = g_signal_connect (self->pipe, "read", G_CALLBACK (on_pipe_read), self); self->close_sig = g_signal_connect (self->pipe, "close", G_CALLBACK (on_pipe_close), self); }
static void cockpit_pipe_transport_finalize (GObject *object) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (object); g_signal_handler_disconnect (self->pipe, self->read_sig); g_signal_handler_disconnect (self->pipe, self->close_sig); g_free (self->name); g_clear_object (&self->pipe); G_OBJECT_CLASS (cockpit_pipe_transport_parent_class)->finalize (object); }
static void on_pipe_read (CockpitPipe *pipe, GByteArray *input, gboolean end_of_data, gpointer user_data) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (user_data); GBytes *message; GBytes *payload; gchar *channel; guint32 size; for (;;) { if (input->len < sizeof (size)) { if (!end_of_data) g_debug ("%s: want more data", self->name); break; } memcpy (&size, input->data, sizeof (size)); size = GUINT32_FROM_BE (size); if (input->len < size + sizeof (size)) { g_debug ("%s: want more data", self->name); break; } message = cockpit_pipe_consume (input, sizeof (size), size); payload = cockpit_transport_parse_frame (message, &channel); if (payload) { g_debug ("%s: received a %d byte payload", self->name, (int)size); cockpit_transport_emit_recv ((CockpitTransport *)self, channel, payload); g_bytes_unref (payload); g_free (channel); } g_bytes_unref (message); } if (end_of_data) { /* Received a partial message */ if (input->len > 0) { g_warning ("%s: received truncated %d byte frame", self->name, input->len); cockpit_pipe_close (pipe, "internal-error"); } } }
static void cockpit_pipe_transport_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (object); switch (prop_id) { case PROP_PIPE: self->pipe = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void on_pipe_close (CockpitPipe *pipe, const gchar *problem, gpointer user_data) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (user_data); GError *error = NULL; gint status; /* This function is called by the base class when it is closed */ if (cockpit_pipe_get_pid (pipe, NULL)) { if (problem == NULL) { status = cockpit_pipe_exit_status (pipe); if (WIFSIGNALED (status) && WTERMSIG (status) == SIGTERM) problem = "terminated"; else if (WIFEXITED (status) && WEXITSTATUS (status) == 5) problem = "not-authorized"; // wrong password else if (WIFEXITED (status) && WEXITSTATUS (status) == 6) problem = "unknown-hostkey"; else if (WIFEXITED (status) && WEXITSTATUS (status) == 127) problem = "no-agent"; // cockpit-agent not installed else if (WIFEXITED (status) && WEXITSTATUS (status) == 255) problem = "terminated"; // ssh failed or got a signal, etc. else if (!g_spawn_check_exit_status (status, &error)) { problem = "internal-error"; g_warning ("%s: agent program failed: %s", self->name, error->message); g_error_free (error); } } else if (g_str_equal (problem, "not-found")) { g_message ("%s: failed to execute agent: not found", self->name); problem = "no-agent"; } } g_debug ("%s: closed%s%s", self->name, problem ? ": " : "", problem ? problem : ""); cockpit_transport_emit_closed (COCKPIT_TRANSPORT (self), problem); }
static void cockpit_pipe_transport_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (object); switch (prop_id) { case PROP_NAME: g_value_set_string (value, self->name); break; case PROP_PIPE: g_value_set_object (value, cockpit_pipe_transport_get_pipe (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void on_other_closed (CockpitTransport *transport, const gchar *problem, gpointer user_data) { CockpitPortal *self = user_data; const gchar **argv; CockpitPipe *pipe; gint status; if (!problem) problem = "disconnected"; if (self->state == PORTAL_OPENING) { pipe = cockpit_pipe_transport_get_pipe (COCKPIT_PIPE_TRANSPORT (self->other)); status = cockpit_pipe_exit_status (pipe); if (status != -1) { argv = current_argv (self); /* These are the problem codes from pkexec. */ if (WIFEXITED (status)) { if (g_str_equal (argv[0], PATH_PKEXEC) && (WEXITSTATUS (status) == 127 || WEXITSTATUS (status) == 126)) problem = "access-denied"; else if (g_str_equal (argv[0], PATH_SUDO) && WEXITSTATUS (status) == 1) problem = "access-denied"; } } g_debug ("other bridge failed: %s", problem); if (self->argvs[self->argvi + 1] != NULL && (g_str_equal (problem, "no-cockpit") || g_str_equal (problem, "not-found") || g_str_equal (problem, "access-denied"))) { self->argvi += 1; disconnect_portal_bridge (self); spawn_portal_bridge (self); return; } if (g_str_equal (problem, "no-cockpit") || g_str_equal (problem, "not-found")) problem = "not-supported"; } else { g_debug ("other bridge closed: %s", problem); } g_free (self->problem); self->problem = g_strdup (problem); if (self->state == PORTAL_OPENING) transition_failed (self); else transition_none (self); }
static void on_pipe_read (CockpitPipe *pipe, GByteArray *input, gboolean end_of_data, gpointer user_data) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (user_data); GBytes *message; GBytes *payload; gchar *channel; guint32 i, size; gchar *data; g_object_ref (self); for (;;) { size = 0; data = (gchar *)input->data; for (i = 0; i < input->len; i++) { /* Check invalid characters, prevent integer overflow, limit max length */ if (i > 7 || data[i] < '0' || data[i] > '9') break; size *= 10; size += data[i] - '0'; } if (i == input->len) { if (!end_of_data) g_debug ("%s: want more data", self->name); break; } if (data[i] != '\n') { g_warning ("%s: incorrect protocol: received invalid length prefix", self->name); cockpit_pipe_close (pipe, "protocol-error"); break; } if (input->len < i + 1 + size) { g_debug ("%s: want more data 2", self->name); break; } message = cockpit_pipe_consume (input, i + 1, size, 0); payload = cockpit_transport_parse_frame (message, &channel); if (payload) { g_debug ("%s: received a %d byte payload", self->name, (int)size); cockpit_transport_emit_recv ((CockpitTransport *)self, channel, payload); g_bytes_unref (payload); g_free (channel); } g_bytes_unref (message); } if (end_of_data) { /* Received a partial message */ if (input->len > 0) { g_debug ("%s: received truncated %d byte frame", self->name, input->len); cockpit_pipe_close (pipe, "disconnected"); } } g_object_unref (self); }