[gnome-terminal/gsettings] server: Implement FD passing
- From: Christian Persch <chpe src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-terminal/gsettings] server: Implement FD passing
- Date: Mon, 16 Apr 2012 11:08:51 +0000 (UTC)
commit 7ccc102d6a1da08fe3314935795d35687a8fe475
Author: Christian Persch <chpe gnome org>
Date: Mon Apr 16 12:50:38 2012 +0200
server: Implement FD passing
https://bugzilla.gnome.org/show_bug.cgi?id=81560
configure.ac | 2 +-
src/client.c | 11 ++++
src/terminal-gdbus.c | 62 ++++++++++++++++++++----
src/terminal-screen.c | 128 +++++++++++++++++++++++++++++++++++++++++++++----
src/terminal-screen.h | 2 +
5 files changed, 185 insertions(+), 20 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 063f916..9002144 100644
--- a/configure.ac
+++ b/configure.ac
@@ -56,7 +56,7 @@ case "$with_gtk" in
3.0) GTK_API_VERSION=3.0
GTK_REQUIRED=3.4.0
VTE_PC_VERSION=-2.90
- VTE_REQUIRED=0.27.3
+ VTE_REQUIRED=0.32.1
;;
esac
diff --git a/src/client.c b/src/client.c
index 3f96b44..4595515 100644
--- a/src/client.c
+++ b/src/client.c
@@ -280,6 +280,17 @@ option_fd_cb (const gchar *option_name,
g_assert_not_reached ();
}
+#if 1
+ if (fd == STDIN_FILENO ||
+ fd == STDOUT_FILENO ||
+ fd == STDERR_FILENO) {
+ g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+ "FD passing of std%s is not supported",
+ fd == STDIN_FILENO ? "in" : fd == STDOUT_FILENO ? "out" : "err");
+ return FALSE;
+ }
+#endif
+
if (data->fd_list == NULL) {
data->fd_list = g_unix_fd_list_new ();
data->fd_array = g_array_new (FALSE, FALSE, sizeof (PassFdElement));
diff --git a/src/terminal-gdbus.c b/src/terminal-gdbus.c
index f7f1966..9e1ecf0 100644
--- a/src/terminal-gdbus.c
+++ b/src/terminal-gdbus.c
@@ -20,6 +20,9 @@
#include "terminal-gdbus.h"
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+
#include "terminal-app.h"
#include "terminal-debug.h"
#include "terminal-defines.h"
@@ -88,17 +91,17 @@ terminal_receiver_impl_set_screen (TerminalReceiverImpl *impl,
static gboolean
terminal_receiver_impl_exec (TerminalReceiver *receiver,
- GDBusMethodInvocation *invocation,
- GUnixFDList *fd_list,
- GVariant *options,
- GVariant *arguments)
+ GDBusMethodInvocation *invocation,
+ GUnixFDList *fd_list,
+ GVariant *options,
+ GVariant *arguments)
{
TerminalReceiverImpl *impl = TERMINAL_RECEIVER_IMPL (receiver);
TerminalReceiverImplPrivate *priv = impl->priv;
const char *working_directory;
char **exec_argv, **envv;
gsize exec_argc;
- GVariantIter *fd_iter;
+ GVariant *fd_array;
GError *error;
if (priv->screen == NULL) {
@@ -113,8 +116,48 @@ terminal_receiver_impl_exec (TerminalReceiver *receiver,
working_directory = NULL;
if (!g_variant_lookup (options, "environ", "^a&ay", &envv))
envv = NULL;
- if (!g_variant_lookup (options, "fd-set", "a(ih)", &fd_iter))
- fd_iter = NULL;
+
+ if (!g_variant_lookup (options, "fd-set", "@a(ih)", &fd_array))
+ fd_array = NULL;
+
+ /* Check FD passing */
+ if ((fd_list != NULL) ^ (fd_array != NULL)) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Must pass both fd-set options and a FD list");
+ goto out;
+ }
+ if (fd_list != NULL && fd_array != NULL) {
+ const int *fd_array_data;
+ gsize fd_array_data_len, i;
+ int n_fds;
+
+ fd_array_data = g_variant_get_fixed_array (fd_array, &fd_array_data_len, 2 * sizeof (int));
+ n_fds = g_unix_fd_list_get_length (fd_list);
+ for (i = 0; i < fd_array_data_len; i++) {
+ const int fd = fd_array_data[2 * i];
+ const int idx = fd_array_data[2 * i + 1];
+
+ if (fd == STDIN_FILENO ||
+ fd == STDOUT_FILENO ||
+ fd == STDERR_FILENO) {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Passing of std%s not supported",
+ fd == STDIN_FILENO ? "in" : fd == STDOUT_FILENO ? "out" : "err");
+ goto out;
+ }
+ if (idx < 0 || idx >= n_fds) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Handle out of range");
+ goto out;
+ }
+ }
+ }
if (working_directory != NULL)
_terminal_debug_print (TERMINAL_DEBUG_FACTORY,
@@ -127,6 +170,7 @@ terminal_receiver_impl_exec (TerminalReceiver *receiver,
exec_argc > 0 ? exec_argv : NULL,
envv,
working_directory,
+ fd_list, fd_array,
&error)) {
g_dbus_method_invocation_take_error (invocation, error);
} else {
@@ -135,8 +179,8 @@ terminal_receiver_impl_exec (TerminalReceiver *receiver,
g_free (exec_argv);
g_free (envv);
- if (fd_iter)
- g_variant_iter_free (fd_iter);
+ if (fd_array)
+ g_variant_unref (fd_array);
out:
diff --git a/src/terminal-screen.c b/src/terminal-screen.c
index 9afad16..09050be 100644
--- a/src/terminal-screen.c
+++ b/src/terminal-screen.c
@@ -17,11 +17,20 @@
*/
#include <config.h>
+#define _GNU_SOURCE /* for dup3 */
+#include "terminal-screen.h"
+
+#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
+#include <fcntl.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
#include <gtk/gtk.h>
@@ -45,6 +54,12 @@
#define URL_MATCH_CURSOR (GDK_HAND2)
+typedef struct {
+ GUnixFDList *fd_list;
+ int *fd_array;
+ gsize fd_array_len;
+} FDSetupData;
+
typedef struct
{
int tag;
@@ -116,7 +131,9 @@ static void terminal_screen_change_font (TerminalScreen *screen);
static gboolean terminal_screen_popup_menu (GtkWidget *widget);
static gboolean terminal_screen_button_press (GtkWidget *widget,
GdkEventButton *event);
-static gboolean terminal_screen_do_exec (TerminalScreen *screen, GError **error);
+static gboolean terminal_screen_do_exec (TerminalScreen *screen,
+ FDSetupData *data,
+ GError **error);
static void terminal_screen_child_exited (VteTerminal *terminal);
static void terminal_screen_window_title_changed (VteTerminal *vte_terminal,
@@ -684,9 +701,12 @@ terminal_screen_exec (TerminalScreen *screen,
char **argv,
char **envv,
const char *cwd,
+ GUnixFDList *fd_list,
+ GVariant *fd_array,
GError **error)
{
TerminalScreenPrivate *priv;
+ FDSetupData *data;
g_return_val_if_fail (TERMINAL_IS_SCREEN (screen), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
@@ -701,7 +721,17 @@ terminal_screen_exec (TerminalScreen *screen,
g_free (priv->initial_working_directory);
priv->initial_working_directory = g_strdup (cwd);
- return terminal_screen_do_exec (screen, error);
+ if (fd_list) {
+ const int *fd_array_data;
+
+ data = g_new (FDSetupData, 1);
+ data->fd_list = fd_list;
+ fd_array_data = g_variant_get_fixed_array (fd_array, &data->fd_array_len, 2 * sizeof (int));
+ data->fd_array = g_memdup (fd_array_data, data->fd_array_len * 2 * sizeof (int));
+ } else
+ data = NULL;
+
+ return terminal_screen_do_exec (screen, data, error);
}
const char*
@@ -1344,9 +1374,83 @@ info_bar_response_cb (GtkWidget *info_bar,
}
}
+static void
+free_fd_setup_data (FDSetupData *data)
+{
+ g_free (data->fd_array);
+ g_free (data);
+}
+
+static void
+terminal_screen_child_setup (FDSetupData *data)
+{
+ int *fd_array = data->fd_array;
+ gsize fd_array_len = data->fd_array_len;
+ gsize i;
+ int *fds, n_fds, j, r;
+
+ /* At this point, vte_pty_child_setup() has been called,
+ * so all FDs are FD_CLOEXEC.
+ */
+
+ if (fd_array_len == 0)
+ return;
+
+ fds = g_unix_fd_list_steal_fds (data->fd_list, &n_fds);
+
+ for (i = 0; i < fd_array_len; i++) {
+ int target_fd = fd_array[2 * i];
+ int idx = fd_array[2 * i + 1];
+ int fd;
+
+ /* We want to move fds[idx] to target_fd */
+
+ if (target_fd == fds[idx])
+ goto next; /* noting to do */
+
+ /* First need to check if @target_fd is one of the FDs in the FD list! */
+ for (j = 0; j < n_fds; j++) {
+ if (fds[j] == target_fd) {
+ do {
+ fd = dup (fds[j]);
+ } while (fd == -1 && errno == EINTR);
+ if (fd == -1)
+ _exit (127);
+ fds[j] = fd;
+
+ /* Need to mark the dup'd FD as FD_CLOEXEC */
+ do {
+ r = fcntl (fd, F_SETFD, FD_CLOEXEC);
+ } while (r == -1 && errno == EINTR);
+ if (r == -1)
+ _exit (127);
+
+ break;
+ }
+ }
+
+ /* Check again */
+ if (target_fd == fds[idx])
+ goto next; /* noting to do */
+
+ /* Now we know that target_fd can be safely overwritten. */
+ errno = 0;
+ do {
+ fd = dup3 (fds[idx], target_fd, 0 /* no FD_CLOEXEC */);
+ } while (fd == -1 && errno == EINTR);
+ if (fd != target_fd)
+ _exit (127);
+
+ next:
+ /* Don't need to close it here since it's FD_CLOEXEC. */
+ fds[idx] = -1;
+ }
+}
+
static gboolean
terminal_screen_do_exec (TerminalScreen *screen,
- GError **error)
+ FDSetupData *data /* adopting */,
+ GError **error)
{
TerminalScreenPrivate *priv = screen->priv;
VteTerminal *terminal = VTE_TERMINAL (screen);
@@ -1358,6 +1462,7 @@ terminal_screen_do_exec (TerminalScreen *screen,
VtePtyFlags pty_flags = VTE_PTY_DEFAULT;
GSpawnFlags spawn_flags = 0;
GPid pid;
+ gboolean result = FALSE;
if (priv->child_pid != -1) {
g_set_error_literal (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
@@ -1385,6 +1490,7 @@ terminal_screen_do_exec (TerminalScreen *screen,
if (!g_settings_get_boolean (profile, TERMINAL_PROFILE_UPDATE_RECORDS_KEY))
pty_flags |= VTE_PTY_NO_UTMP | VTE_PTY_NO_WTMP;
+ argv = NULL;
if (!get_child_command (screen, shell, &spawn_flags, &argv, &err) ||
!vte_terminal_fork_command_full (terminal,
pty_flags,
@@ -1392,7 +1498,8 @@ terminal_screen_do_exec (TerminalScreen *screen,
argv,
env,
spawn_flags,
- NULL, NULL,
+ (GSpawnChildSetupFunc) (data ? terminal_screen_child_setup : NULL),
+ data,
&pid,
&err)) {
GtkWidget *info_bar;
@@ -1413,27 +1520,28 @@ terminal_screen_do_exec (TerminalScreen *screen,
gtk_info_bar_set_default_response (GTK_INFO_BAR (info_bar), GTK_RESPONSE_CANCEL);
gtk_widget_show (info_bar);
- g_strfreev (env);
- g_free (shell);
-
g_propagate_error (error, err);
- return FALSE;
+ goto out;
}
priv->child_pid = pid;
priv->pty_fd = vte_terminal_get_pty (terminal);
+ result = TRUE;
+
+out:
g_free (shell);
g_strfreev (argv);
g_strfreev (env);
+ free_fd_setup_data (data);
- return TRUE;
+ return result;
}
static gboolean
terminal_screen_launch_child_cb (TerminalScreen *screen)
{
- terminal_screen_do_exec (screen, NULL /* don't care */);
+ terminal_screen_do_exec (screen, NULL, NULL /* don't care */);
return FALSE; /* don't run again */
}
diff --git a/src/terminal-screen.h b/src/terminal-screen.h
index bba7d8a..fd2b56f 100644
--- a/src/terminal-screen.h
+++ b/src/terminal-screen.h
@@ -85,6 +85,8 @@ gboolean terminal_screen_exec (TerminalScreen *screen,
char **argv,
char **envv,
const char *cwd,
+ GUnixFDList *fd_list,
+ GVariant *fd_array,
GError **error);
void _terminal_screen_launch_child_on_idle (TerminalScreen *screen);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]