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);
}
Example #8
0
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);
}
Example #11
0
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;
    }
}
Example #12
0
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);
}
Example #13
0
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);
}