Exemple #1
0
/*
 * 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);
}
Exemple #2
0
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;
}
Exemple #3
0
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;
}
Exemple #5
0
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);
}
Exemple #6
0
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";
    }
}
Exemple #7
0
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);
}
Exemple #8
0
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);
}
Exemple #9
0
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);
}