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);
}
Пример #2
0
static void
cockpit_channel_constructed (GObject *object)
{
  CockpitChannel *self = COCKPIT_CHANNEL (object);

  G_OBJECT_CLASS (cockpit_channel_parent_class)->constructed (object);

  g_return_if_fail (self->priv->id != NULL);

  self->priv->capabilities = NULL;
  self->priv->recv_sig = g_signal_connect (self->priv->transport, "recv",
                                           G_CALLBACK (on_transport_recv), self);
  self->priv->control_sig = g_signal_connect (self->priv->transport, "control",
                                              G_CALLBACK (on_transport_control), self);
  self->priv->close_sig = g_signal_connect (self->priv->transport, "closed",
                                            G_CALLBACK (on_transport_closed), self);

  /*
   * If the caller didn't ask us to be frozen, then we temporarily
   * freeze things and run the prepare. Message input will be frozen until
   * cockpit_channel_ready() is called.
   */
  if (!self->priv->frozen)
    {
      self->priv->frozen = g_queue_new ();
      self->priv->prepare_tag = g_idle_add_full (G_PRIORITY_HIGH, on_idle_prepare, self, NULL);
    }
}
Пример #3
0
static gboolean
on_idle_protocol_error (gpointer user_data)
{
  CockpitChannel *channel = COCKPIT_CHANNEL (user_data);
  cockpit_channel_close (channel, "protocol-error");
  return FALSE;
}
Пример #4
0
static void
on_web_socket_open (WebSocketConnection *connection,
                    gpointer user_data)
{
  CockpitWebSocketStream *self = COCKPIT_WEB_SOCKET_STREAM (user_data);
  CockpitChannel *channel = COCKPIT_CHANNEL (user_data);
  JsonObject *object;
  JsonObject *headers;
  GHashTableIter iter;
  gpointer key, value;

  headers = json_object_new ();

  g_hash_table_iter_init (&iter, web_socket_client_get_headers (WEB_SOCKET_CLIENT (self->client)));
  while (g_hash_table_iter_next (&iter, &key, &value))
    json_object_set_string_member (headers, key, value);

  object = json_object_new ();
  json_object_set_object_member (object, "headers", headers);

  cockpit_channel_control (channel, "response", object);
  json_object_unref (object);

  cockpit_channel_ready (channel, NULL);
}
Пример #5
0
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);
        }
    }
}
Пример #6
0
static void
cockpit_channel_set_property (GObject *object,
                              guint prop_id,
                              const GValue *value,
                              GParamSpec *pspec)
{
  CockpitChannel *self = COCKPIT_CHANNEL (object);

  switch (prop_id)
    {
    case PROP_TRANSPORT:
      self->priv->transport = g_value_dup_object (value);
      break;
    case PROP_ID:
      self->priv->id = g_value_dup_string (value);
      break;
    case PROP_OPTIONS:
      self->priv->open_options = g_value_dup_boxed (value);
      break;
    case PROP_CAPABILITIES:
      g_return_if_fail (self->priv->capabilities == NULL);
      self->priv->capabilities = g_value_dup_boxed (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}
Пример #7
0
static void
on_throttle_pressure (GObject *object,
                      gboolean throttle,
                      gpointer user_data)
{
  CockpitChannel *self = COCKPIT_CHANNEL (user_data);
  GQueue *throttled;
  JsonObject *ping;

  if (throttle)
    {
      if (!self->priv->throttled)
        self->priv->throttled = g_queue_new ();
    }
  else
    {
      throttled = self->priv->throttled;
      self->priv->throttled = NULL;
      while (throttled)
        {
          ping = g_queue_pop_head (throttled);
          if (!ping)
            {
              g_queue_free (throttled);
              throttled = NULL;
            }
          else
            {
              if (!process_ping (self, ping))
                g_assert_not_reached (); /* Because throttle is FALSE */
              json_object_unref (ping);
            }
        }
    }
}
Пример #8
0
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;
}
Пример #9
0
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));
}
Пример #10
0
static gboolean
on_web_socket_closing (WebSocketConnection *connection,
                       gpointer user_data)
{
  CockpitChannel *channel = COCKPIT_CHANNEL (user_data);
  cockpit_channel_control (channel, "done", NULL);
  return TRUE;
}
Пример #11
0
static void
on_socket_message (WebSocketConnection *connection,
                   WebSocketDataType data_type,
                   GBytes *payload,
                   gpointer user_data)
{
  CockpitChannel *channel = COCKPIT_CHANNEL (user_data);
  cockpit_channel_send (channel, payload, data_type == WEB_SOCKET_DATA_TEXT);
}
Пример #12
0
static gboolean
on_idle_prepare (gpointer data)
{
  CockpitChannel *self = COCKPIT_CHANNEL (data);
  g_object_ref (self);
  cockpit_channel_prepare (self);
  g_object_unref (self);
  return FALSE;
}
Пример #13
0
static void
on_changed (GFileMonitor      *monitor,
            GFile             *file,
            GFile             *other_file,
            GFileMonitorEvent  event_type,
            gpointer           user_data)
{
  CockpitFslist *self = COCKPIT_FSLIST(user_data);
  cockpit_fswatch_emit_event (COCKPIT_CHANNEL(self), file, other_file, event_type);
}
Пример #14
0
static void
write_builder (CockpitDBusJson1 *self,
               JsonBuilder *builder)
{
  GBytes *bytes;

  json_builder_end_object (builder);
  bytes = _json_builder_to_bytes (self, builder);
  cockpit_channel_send (COCKPIT_CHANNEL (self), bytes, TRUE);
  g_bytes_unref (bytes);
}
Пример #15
0
static void
cockpit_channel_finalize (GObject *object)
{
  CockpitChannel *self = COCKPIT_CHANNEL (object);

  g_object_unref (self->priv->transport);
  json_object_unref (self->priv->open_options);
  if (self->priv->close_options)
    json_object_unref (self->priv->close_options);

  G_OBJECT_CLASS (cockpit_channel_parent_class)->finalize (object);
}
Пример #16
0
static void
cockpit_channel_constructed (GObject *object)
{
  CockpitChannel *self = COCKPIT_CHANNEL (object);

  G_OBJECT_CLASS (cockpit_channel_parent_class)->constructed (object);

  self->priv->recv_sig = g_signal_connect (self->priv->transport, "recv",
                                           G_CALLBACK (on_transport_recv), self);
  self->priv->close_sig = g_signal_connect (self->priv->transport, "closed",
                                           G_CALLBACK (on_transport_closed), self);
}
Пример #17
0
static void
on_transport_closed (CockpitTransport *transport,
                     const gchar *problem,
                     gpointer user_data)
{
  CockpitChannel *self = COCKPIT_CHANNEL (user_data);
  if (!self->priv->closed)
    {
      self->priv->closed = TRUE;
      g_signal_emit (self, cockpit_channel_sig_closed, 0, problem);
    }
}
Пример #18
0
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);
}
Пример #19
0
static void
mock_case_channel_constructed (GObject *obj)
{
  MockCaseChannel *self = (MockCaseChannel *)obj;
  const gchar *payload = NULL;
  JsonObject *options;

  G_OBJECT_CLASS (mock_case_channel_parent_class)->constructed (obj);

  options = cockpit_channel_get_options (COCKPIT_CHANNEL (obj));
  if (!cockpit_json_get_string (options, "payload", NULL, &payload))
    g_assert_not_reached ();

  if (g_strcmp0 (payload, "upper") == 0)
    self->function = g_ascii_toupper;
  else if (g_strcmp0 (payload, "lower") == 0)
    self->function = g_ascii_tolower;
  else
    g_assert_not_reached ();

  cockpit_channel_ready (COCKPIT_CHANNEL (self));
}
Пример #20
0
static void
cockpit_text_stream_constructed (GObject *object)
{
  CockpitTextStream *self = COCKPIT_TEXT_STREAM (object);
  CockpitChannel *channel = COCKPIT_CHANNEL (self);
  GSocketAddress *address;
  const gchar *unix_path;
  const gchar **argv;
  const gchar **env;

  G_OBJECT_CLASS (cockpit_text_stream_parent_class)->constructed (object);

  unix_path = cockpit_channel_get_option (channel, "unix");
  argv = cockpit_channel_get_strv_option (channel, "spawn");

  if (argv == NULL && unix_path == NULL)
    {
      g_warning ("did not receive a unix or spawn option");
      g_idle_add_full (G_PRIORITY_DEFAULT, on_idle_protocol_error,
                       g_object_ref (channel), g_object_unref);
      return;
    }
  else if (argv != NULL && unix_path != NULL)
    {
      g_warning ("received both a unix and spawn option");
      g_idle_add_full (G_PRIORITY_DEFAULT, on_idle_protocol_error,
                       g_object_ref (channel), g_object_unref);
      return;
    }
  else if (unix_path)
    {
      self->name = unix_path;
      address = g_unix_socket_address_new (unix_path);
      self->pipe = cockpit_pipe_connect (self->name, address);
      g_object_unref (address);
    }
  else if (argv)
    {
      self->name = argv[0];
      env = cockpit_channel_get_strv_option (channel, "environ");
      if (cockpit_channel_get_bool_option (channel, "pty"))
        self->pipe = cockpit_pipe_pty (argv, env, NULL);
      else
        self->pipe = cockpit_pipe_spawn (argv, env, NULL);
    }

  self->sig_read = g_signal_connect (self->pipe, "read", G_CALLBACK (on_pipe_read), self);
  self->sig_close = g_signal_connect (self->pipe, "close", G_CALLBACK (on_pipe_close), self);
  self->open = TRUE;
  cockpit_channel_ready (channel);
}
Пример #21
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);
}
Пример #22
0
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");
        }
    }
}
Пример #23
0
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);
}
Пример #24
0
static void
cockpit_channel_constructed (GObject *object)
{
  CockpitChannel *self = COCKPIT_CHANNEL (object);

  G_OBJECT_CLASS (cockpit_channel_parent_class)->constructed (object);

  g_return_if_fail (self->priv->id != NULL);

  self->priv->capabilities = NULL;
  self->priv->recv_sig = g_signal_connect (self->priv->transport, "recv",
                                           G_CALLBACK (on_transport_recv), self);
  self->priv->control_sig = g_signal_connect (self->priv->transport, "control",
                                              G_CALLBACK (on_transport_control), self);
  self->priv->close_sig = g_signal_connect (self->priv->transport, "closed",
                                            G_CALLBACK (on_transport_closed), self);
}
Пример #25
0
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);
}
Пример #26
0
static void
on_socket_open (WebSocketConnection *connection,
                gpointer user_data)
{
  CockpitChannel *channel = COCKPIT_CHANNEL (user_data);
  JsonObject *open;

  /*
   * Actually open the channel. We wait until the WebSocket is open
   * before doing this, so we don't receive messages from the bridge
   * before the websocket is open.
   */

  open = cockpit_channel_get_options (channel);
  cockpit_channel_control (channel, "open", open);

  /* Tell the channel we're ready */
  cockpit_channel_ready (channel, NULL);
}
Пример #27
0
static void
write_builder (CockpitDBusJson *self,
               JsonBuilder *builder)
{
  GBytes *bytes;
  JsonNode *root;
  gsize length;
  gchar *ret;

  json_builder_end_object (builder);

  root = json_builder_get_root (builder);
  ret = cockpit_json_write (root, &length);
  json_node_free (root);

  bytes = g_bytes_new_take (ret, length);
  cockpit_channel_send (COCKPIT_CHANNEL (self), bytes);
  g_bytes_unref (bytes);
}
Пример #28
0
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");
    }
}
Пример #29
0
static void
cockpit_channel_throttle (CockpitFlow *flow,
                          CockpitFlow *controlling)
{
  CockpitChannel *self = COCKPIT_CHANNEL (flow);

  if (self->priv->pressure)
    {
      g_signal_handler_disconnect (self->priv->pressure, self->priv->pressure_sig);
      g_object_remove_weak_pointer (G_OBJECT (self->priv->pressure), (gpointer *)&self->priv->pressure);
      self->priv->pressure = NULL;
    }

  if (controlling)
    {
      self->priv->pressure = controlling;
      g_object_add_weak_pointer (G_OBJECT (self->priv->pressure), (gpointer *)&self->priv->pressure);
      self->priv->pressure_sig = g_signal_connect (controlling, "pressure", G_CALLBACK (on_throttle_pressure), self);
    }
}
Пример #30
0
static void
cockpit_channel_get_property (GObject *object,
                              guint prop_id,
                              GValue *value,
                              GParamSpec *pspec)
{
  CockpitChannel *self = COCKPIT_CHANNEL (object);

  switch (prop_id)
    {
      case PROP_TRANSPORT:
        g_value_set_object (value, self->priv->transport);
        break;
      case PROP_ID:
        g_value_set_string (value, self->priv->id);
        break;
      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}