static gboolean dispatch_output (gint fd, GIOCondition cond, gpointer user_data) { CockpitPipe *self = (CockpitPipe *)user_data; struct iovec iov[4]; gsize partial; gssize ret; gint i, count; GList *l; /* A non-blocking connect is processed here */ if (self->priv->connecting && !dispatch_connect (self)) return TRUE; g_return_val_if_fail (self->priv->out_source, FALSE); /* Note we fall through when nothing to write */ partial = self->priv->out_partial; for (l = self->priv->out_queue->head, i = 0; i < G_N_ELEMENTS (iov) && l != NULL; i++, l = g_list_next (l)) { iov[i].iov_base = (gpointer)g_bytes_get_data (l->data, &iov[i].iov_len); if (partial) { g_assert (partial < iov[i].iov_len); iov[i].iov_len -= partial; iov[i].iov_base = ((gchar *)iov[i].iov_base) + partial; partial = 0; } } count = i; if (count == 0) ret = 0; else ret = writev (self->priv->out_fd, iov, count); if (ret < 0) { if (errno != EAGAIN && errno != EINTR) { if (errno == EPIPE) { g_debug ("%s: couldn't write: %s", self->priv->name, g_strerror (errno)); close_immediately (self, "terminated"); } else { set_problem_from_errno (self, "couldn't write", errno); close_immediately (self, NULL); /* already set */ } } return FALSE; } /* Figure out what was written */ for (i = 0; ret > 0 && i < count; i++) { if (ret >= iov[i].iov_len) { g_debug ("%s: wrote %d bytes", self->priv->name, (int)iov[i].iov_len); g_bytes_unref (g_queue_pop_head (self->priv->out_queue)); self->priv->out_partial = 0; ret -= iov[i].iov_len; } else { g_debug ("%s: partial write %d of %d bytes", self->priv->name, (int)ret, (int)iov[i].iov_len); self->priv->out_partial += ret; ret = 0; } } if (self->priv->out_queue->head) return TRUE; g_debug ("%s: output queue empty", self->priv->name); /* If all messages are done, then stop polling out fd */ stop_output (self); if (self->priv->closing) close_output (self); else close_maybe (self); return TRUE; }
static gboolean dispatch_output (GPollableOutputStream *os, gpointer user_data) { CockpitStream *self = (CockpitStream *)user_data; GError *error = NULL; const gint8 *data; gsize len; gssize ret; g_return_val_if_fail (self->priv->out_source, FALSE); while (self->priv->out_queue->head) { data = g_bytes_get_data (self->priv->out_queue->head->data, &len); g_assert (self->priv->out_partial <= len); ret = g_pollable_output_stream_write_nonblocking (os, data + self->priv->out_partial, len - self->priv->out_partial, NULL, &error); if (ret < 0) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { g_debug ("%s: output would block", self->priv->name); g_error_free (error); return TRUE; } else { set_problem_from_error (self, "couldn't write", error); g_error_free (error); close_immediately (self, NULL); return FALSE; } } self->priv->out_partial += ret; if (self->priv->out_partial >= len) { g_debug ("%s: wrote %d bytes", self->priv->name, (int)len); g_bytes_unref (g_queue_pop_head (self->priv->out_queue)); self->priv->out_partial = 0; } else { if (ret > 0) g_debug ("%s: partial write %d of %d bytes", self->priv->name, (int)ret, (int)len); return TRUE; } } g_debug ("%s: output queue empty", self->priv->name); /* If all messages are done, then stop polling out fd */ stop_output (self); if (self->priv->closing) close_output (self); else close_maybe (self); return TRUE; }
static gboolean cockpit_ssh_source_prepare (GSource *source, gint *timeout) { CockpitSshSource *cs = (CockpitSshSource *)source; CockpitSshTransport *self = cs->transport; GThread *thread; gint status; *timeout = 1; /* Connecting, check if done */ if (G_UNLIKELY (!self->data)) { if (g_atomic_int_get (&self->connecting)) return FALSE; g_object_ref (self); /* Get the result from connecting thread */ thread = self->connect_thread; self->connect_fd = -1; self->connect_thread = NULL; self->data = g_thread_join (thread); g_assert (self->data != NULL); if (!self->result_emitted) { self->result_emitted = TRUE; g_signal_emit_by_name (self, "result", self->data->problem); } if (self->data->problem) { close_immediately (self, self->data->problem); g_object_unref (self); return FALSE; } g_object_unref (self); ssh_event_add_session (self->event, self->data->session); ssh_set_channel_callbacks (self->data->channel, &self->channel_cbs); /* Start watching the fd */ ssh_set_blocking (self->data->session, 0); cs->pfd.fd = ssh_get_fd (self->data->session); g_source_add_poll (source, &cs->pfd); g_debug ("%s: starting io", self->logname); } status = ssh_get_status (self->data->session); /* Short cut this ... we're ready now */ if (self->drain_buffer) return TRUE; /* * Channel completely closed, and output buffers * are empty. We're in a good place to close the * SSH session and thus the transport. */ if (close_maybe (self, status)) return FALSE; cs->pfd.revents = 0; cs->pfd.events = G_IO_IN | G_IO_ERR | G_IO_NVAL | G_IO_HUP; /* libssh has something in its buffer: want to write */ if (status & SSH_WRITE_PENDING) cs->pfd.events |= G_IO_OUT; /* We have something in our queue: want to write */ else if (!g_queue_is_empty (self->queue)) cs->pfd.events |= G_IO_OUT; /* We are closing and need to send eof: want to write */ else if (self->closing && !self->sent_eof) cs->pfd.events |= G_IO_OUT; /* Need to reply to an EOF or close */ if ((self->received_eof && self->sent_eof && !self->sent_close) || (self->received_close && !self->sent_close)) cs->pfd.events |= G_IO_OUT; return cockpit_ssh_source_check (source); }
static gboolean dispatch_input (GPollableInputStream *is, gpointer user_data) { CockpitStream *self = (CockpitStream *)user_data; GError *error = NULL; gboolean read = FALSE; gssize ret = 0; gsize len; gboolean eof; for (;;) { g_return_val_if_fail (self->priv->in_source, FALSE); len = self->priv->in_buffer->len; g_byte_array_set_size (self->priv->in_buffer, len + 1024); ret = g_pollable_input_stream_read_nonblocking (is, self->priv->in_buffer->data + len, 1024, NULL, &error); if (ret < 0) { g_byte_array_set_size (self->priv->in_buffer, len); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { g_error_free (error); break; } else { set_problem_from_error (self, "couldn't read", error); g_error_free (error); close_immediately (self, NULL); return FALSE; } } g_byte_array_set_size (self->priv->in_buffer, len + ret); if (ret == 0) { g_debug ("%s: end of input", self->priv->name); stop_input (self); break; } else if (ret > 0) { g_debug ("%s: read %d bytes", self->priv->name, (int)ret); self->priv->received = TRUE; read = TRUE; } } g_object_ref (self); eof = (self->priv->in_source == NULL); if (eof || read) g_signal_emit (self, cockpit_stream_sig_read, 0, self->priv->in_buffer, eof); if (eof) close_maybe (self); g_object_unref (self); return TRUE; }