diff options
author | Alexander Larsson <alexl@redhat.com> | 2015-09-24 19:23:24 +0200 |
---|---|---|
committer | Alexander Larsson <alexl@redhat.com> | 2015-09-24 19:23:24 +0200 |
commit | 41af86dc69e52f53c4181acc4e762e82f121f25c (patch) | |
tree | ac25331f88179bfa8635d432070471e94330df05 | |
parent | 2cfbf4ff1a5db91a2aa496e58e403f3747456a7f (diff) | |
download | xdg-app-41af86dc69e52f53c4181acc4e762e82f121f25c.tar.gz |
Add xdg-app enter command
This lets you enter a sandbox and run a command there, which is useful
for debugging purposes.
-rw-r--r-- | app/Makefile.am.inc | 1 | ||||
-rw-r--r-- | app/xdg-app-builtins-enter.c | 288 | ||||
-rw-r--r-- | app/xdg-app-builtins.h | 1 | ||||
-rw-r--r-- | app/xdg-app-main.c | 1 | ||||
-rwxr-xr-x | completion/xdg-app | 2 | ||||
-rw-r--r-- | doc/Makefile.am | 1 | ||||
-rw-r--r-- | doc/xdg-app-enter.xml | 110 | ||||
-rw-r--r-- | doc/xdg-app-run.xml | 1 | ||||
-rw-r--r-- | lib/xdg-app-helper.c | 50 |
9 files changed, 431 insertions, 24 deletions
diff --git a/app/Makefile.am.inc b/app/Makefile.am.inc index 4b9c72a..7b8839f 100644 --- a/app/Makefile.am.inc +++ b/app/Makefile.am.inc @@ -16,6 +16,7 @@ xdg_app_SOURCES = \ app/xdg-app-builtins-uninstall.c \ app/xdg-app-builtins-list.c \ app/xdg-app-builtins-run.c \ + app/xdg-app-builtins-enter.c \ app/xdg-app-builtins-build-init.c \ app/xdg-app-builtins-build.c \ app/xdg-app-builtins-build-finish.c \ diff --git a/app/xdg-app-builtins-enter.c b/app/xdg-app-builtins-enter.c new file mode 100644 index 0000000..64cd986 --- /dev/null +++ b/app/xdg-app-builtins-enter.c @@ -0,0 +1,288 @@ +/* + * Copyright © 2014 Red Hat, Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Alexander Larsson <alexl@redhat.com> + */ + +#include "config.h" + +#include <locale.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +#include "libgsystem.h" +#include "libglnx/libglnx.h" + +#include "xdg-app-builtins.h" +#include "xdg-app-utils.h" +#include "xdg-app-dbus.h" +#include "xdg-app-run.h" + + +static GOptionEntry options[] = { + { NULL } +}; + +static gboolean +write_to_file (int fd, const char *content, ssize_t len) +{ + ssize_t res; + + while (len > 0) + { + res = write (fd, content, len); + if (res < 0 && errno == EINTR) + continue; + if (res <= 0) + return FALSE; + len -= res; + content += res; + } + + return TRUE; +} + +static gboolean +write_file (const char *path, const char *content) +{ + int fd; + gboolean res; + int errsv; + + fd = open (path, O_RDWR | O_CLOEXEC, 0); + if (fd == -1) + return FALSE; + + res = TRUE; + if (content) + res = write_to_file (fd, content, strlen (content)); + + errsv = errno; + close (fd); + errno = errsv; + + return res; +} + +static uid_t uid; +static gid_t gid; + +static void +child_setup (gpointer user_data) +{ +#ifndef DISABLE_USERNS + g_autofree char *uid_map = NULL; + g_autofree char *gid_map = NULL; + + /* Work around user namespace devpts issue by creating a new + userspace and map our uid like the helper does */ + + if (unshare (CLONE_NEWUSER)) + { + g_warning ("Can't unshare user namespace: %s", strerror (errno)); + return; + } + + uid_map = g_strdup_printf ("%d 0 1\n", uid); + if (!write_file ("/proc/self/uid_map", uid_map)) + g_warning ("setting up uid map"); + + gid_map = g_strdup_printf ("%d 0 1\n", gid); + if (!write_file ("/proc/self/gid_map", gid_map)) + g_warning ("setting up gid map"); + +#endif +} + +gboolean +xdg_app_builtin_enter (int argc, + char **argv, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GOptionContext) context = NULL; + int rest_argv_start, rest_argc; + g_autoptr(XdgAppContext) arg_context = NULL; + const char *ns_name[5] = { "user", "ipc", "net", "pid", "mnt" }; + int ns_fd[G_N_ELEMENTS(ns_name)]; + char pid_ns[256]; + ssize_t pid_ns_len; + char self_ns[256]; + ssize_t self_ns_len; + char *pid_s; + int pid, i; + g_autofree char *environment_path = NULL; + g_autoptr(GPtrArray) argv_array = NULL; + g_autoptr(GPtrArray) envp_array = NULL; + g_autofree char *environment = NULL; + gsize environment_len; + char *e; + g_autofree char *pulse_path = NULL; + g_autofree char *session_bus_path = NULL; + g_autofree char *xdg_runtime_dir = NULL; + int status; + + uid = getuid (); + gid = getgid (); + + context = g_option_context_new ("MONITORPID [COMMAND [args...]] - Run a command inside a running sandbox"); + + rest_argc = 0; + for (i = 1; i < argc; i++) + { + /* The non-option is the command, take it out of the arguments */ + if (argv[i][0] != '-') + { + rest_argv_start = i; + rest_argc = argc - i; + argc = i; + break; + } + } + + arg_context = xdg_app_context_new (); + g_option_context_add_group (context, xdg_app_context_get_options (arg_context)); + + if (!xdg_app_option_context_parse (context, options, &argc, &argv, XDG_APP_BUILTIN_FLAG_NO_DIR, NULL, cancellable, error)) + return FALSE; + + if (rest_argc < 2) + { + usage_error (context, "MONITORPID and COMMAND must be specified", error); + return FALSE; + } + + pid_s = argv[rest_argv_start]; + + pid = atoi (pid_s); + if (pid <= 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid pid %s\n", pid_s); + return FALSE; + } + + environment_path = g_strdup_printf ("/proc/%d/environ", pid); + if (!g_file_get_contents (environment_path, &environment, &environment_len, error)) + return FALSE; + + for (i = 0; i < G_N_ELEMENTS(ns_name); i++) + { + g_autofree char *path = g_strdup_printf ("/proc/%d/ns/%s", pid, ns_name[i]); + g_autofree char *self_path = g_strdup_printf ("/proc/self/ns/%s", ns_name[i]); + + pid_ns_len = readlink (path, pid_ns, sizeof (pid_ns) - 1); + if (pid_ns_len <= 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid %s namespace for pid %d\n", ns_name[i], pid); + return FALSE; + } + pid_ns[pid_ns_len] = 0; + + self_ns_len = readlink (self_path, self_ns, sizeof (self_ns) - 1); + if (self_ns_len <= 0) + if (pid_ns_len <= 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid %s namespace for self\n", ns_name[i]); + return FALSE; + } + self_ns[self_ns_len] = 0; + + if (strcmp (self_ns, pid_ns) == 0) + { + /* No need to setns to the same namespace, it will only fail */ + ns_fd[i] = -1; + } + else + { + ns_fd[i] = open (path, O_RDONLY); + if (ns_fd[i] == -1) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't open %s namespace: %s", ns_name[i], strerror (errno)); + return FALSE; + } + } + } + + for (i = 0; i < G_N_ELEMENTS(ns_fd); i++) + { + if (ns_fd[i] != -1) + { + if (setns (ns_fd[i], 0) == -1) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't enter %s namespace: %s", ns_name[i], strerror (errno)); + return FALSE; + } + close (ns_fd[i]); + } + } + + envp_array = g_ptr_array_new_with_free_func (g_free); + for (e = environment; e < environment + environment_len; e = e + strlen (e) + 1) + { + if (*e != 0 && + !g_str_has_prefix (e, "DISPLAY=") && + !g_str_has_prefix (e, "PULSE_SERVER=") && + !g_str_has_prefix (e, "PULSE_CLIENTCONFIG=") && + !g_str_has_prefix (e, "XDG_RUNTIME_DIR=") && + !g_str_has_prefix (e, "DBUS_SYSTEM_BUS_ADDRESS=") && + !g_str_has_prefix (e, "DBUS_SESSION_BUS_ADDRESS=")) + { + if (g_str_has_prefix (e, "_LD_LIBRARY_PATH=")) + e++; + g_ptr_array_add (envp_array, g_strdup (e)); + } + } + + xdg_runtime_dir = g_strdup_printf ("/run/user/%d", uid); + g_ptr_array_add (envp_array, g_strdup_printf ("XDG_RUNTIME_DIR=%s", xdg_runtime_dir)); + + if (g_file_test ("/tmp/.X11-unix/X99", G_FILE_TEST_EXISTS)) + g_ptr_array_add (envp_array, g_strdup ("DISPLAY=:99.0")); + + pulse_path = g_strdup_printf ("/run/user/%d/pulse/native", uid); + if (g_file_test (pulse_path, G_FILE_TEST_EXISTS)) + { + g_ptr_array_add (envp_array, g_strdup_printf ("PULSE_SERVER=unix:%s", pulse_path)); + g_ptr_array_add (envp_array, g_strdup_printf ("PULSE_CLIENTCONFIG=/run/user/%d/pulse/config", uid)); + } + + session_bus_path = g_strdup_printf ("/run/user/%d/bus", uid); + if (g_file_test (session_bus_path, G_FILE_TEST_EXISTS)) + g_ptr_array_add (envp_array, g_strdup_printf ("DBUS_SESSION_BUS_ADDRESS=unix:%s", session_bus_path)); + + if (g_file_test ("/run/dbus/system_bus_socket", G_FILE_TEST_EXISTS)) + g_ptr_array_add (envp_array, g_strdup ("DBUS_SYSTEM_BUS_ADDRESS=unix:/run/dbus/system_bus_socket")); + + g_ptr_array_add (envp_array, NULL); + + argv_array = g_ptr_array_new_with_free_func (g_free); + for (i = 1; i < rest_argc; i++) + g_ptr_array_add (argv_array, g_strdup (argv[rest_argv_start + i])); + g_ptr_array_add (argv_array, NULL); + + if (!g_spawn_sync (NULL, (char **)argv_array->pdata, (char **)envp_array->pdata, + G_SPAWN_SEARCH_PATH_FROM_ENVP | G_SPAWN_CHILD_INHERITS_STDIN, + child_setup, NULL, + NULL, NULL, + &status, error)) + return FALSE; + + exit (status); +} diff --git a/app/xdg-app-builtins.h b/app/xdg-app-builtins.h index 5f446d5..b519848 100644 --- a/app/xdg-app-builtins.h +++ b/app/xdg-app-builtins.h @@ -63,6 +63,7 @@ BUILTINPROTO(update_app); BUILTINPROTO(uninstall_app); BUILTINPROTO(list_apps); BUILTINPROTO(run); +BUILTINPROTO(enter); BUILTINPROTO(build_init); BUILTINPROTO(build); BUILTINPROTO(build_finish); diff --git a/app/xdg-app-main.c b/app/xdg-app-main.c index b9ac78a..0bd5017 100644 --- a/app/xdg-app-main.c +++ b/app/xdg-app-main.c @@ -56,6 +56,7 @@ static XdgAppCommand commands[] = { { "uninstall-app", xdg_app_builtin_uninstall_app }, { "list-apps", xdg_app_builtin_list_apps }, { "run", xdg_app_builtin_run }, + { "enter", xdg_app_builtin_enter }, { "override", xdg_app_builtin_override }, { "export-file", xdg_app_builtin_export_file }, { "build-init", xdg_app_builtin_build_init }, diff --git a/completion/xdg-app b/completion/xdg-app index 6b9c992..3adb211 100755 --- a/completion/xdg-app +++ b/completion/xdg-app @@ -23,7 +23,7 @@ _xdg-app() { local dir cmd sdk loc local -A VERBS=( - [ALL]='add-remote modify-remote delete-remote ls-remote list-remotes install-runtime update-runtime uninstall-runtime list-runtimes install-app update-app uninstall-app list-apps run override export-file build-init build build-finish build-export repo-update make-app-current' + [ALL]='add-remote modify-remote delete-remote ls-remote list-remotes install-runtime update-runtime uninstall-runtime list-runtimes install-app update-app uninstall-app list-apps run override enter export-file build-init build build-finish build-export repo-update make-app-current' [MODE]='add-remote modify-remote delete-remote ls-remote list-remotes install-runtime update-runtime uninstall-runtime list-runtimes install-app update-app uninstall-app list-apps make-app-current' [PERMS]='run override build build-finish' [UNINSTALL]='uninstall-runtime uninstall-app' diff --git a/doc/Makefile.am b/doc/Makefile.am index b754976..a451912 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -30,6 +30,7 @@ man_MANS = \ xdg-app-list-apps.1 \ xdg-app-run.1 \ xdg-app-override.1 \ + xdg-app-enter.1 \ xdg-app-export-file.1 \ xdg-app-build-init.1 \ xdg-app-build.1 \ diff --git a/doc/xdg-app-enter.xml b/doc/xdg-app-enter.xml new file mode 100644 index 0000000..e0f6a12 --- /dev/null +++ b/doc/xdg-app-enter.xml @@ -0,0 +1,110 @@ +<?xml version='1.0'?> <!--*-nxml-*--> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<refentry id="xdg-app-enter"> + + <refentryinfo> + <title>xdg-app enter</title> + <productname>xdg-app</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Alexander</firstname> + <surname>Larsson</surname> + <email>alexl@redhat.com</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>xdg-app enter</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>xdg-app-enter</refname> + <refpurpose>Enter an application</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>xdg-app enter</command> + <arg choice="opt" rep="repeat">OPTION</arg> + <arg choice="plain">MONITORPID</arg> + <arg choice="plain">COMMAND</arg> + <arg choice="opt" rep="repeat">ARG</arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + Enter a running sandbox. + <arg choice="plain">MONITORPID</arg> must be the pid of the monitor process for a running sandbox. + <arg choice="plain">COMMAND</arg> is the command to run in the sandbox. + Extra arguments are passed on to the command. + </para> + <para> + This creates a new process within the running sandbox, with the same environment. This is useful + when you want to debug a problem with a running application. + </para> + + </refsect1> + + <refsect1> + <title>Options</title> + + <para>The following options are understood:</para> + + <variablelist> + <varlistentry> + <term><option>-h</option></term> + <term><option>--help</option></term> + + <listitem><para> + Show help options and exit. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-v</option></term> + <term><option>--verbose</option></term> + + <listitem><para> + Print debug information during command processing. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>--version</option></term> + + <listitem><para> + Print version information and exit. + </para></listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + <command>$ xdg-app enter 15345 sh</command> + </para> + + </refsect1> + + <refsect1> + <title>See also</title> + + <para> + <citerefentry><refentrytitle>xdg-app</refentrytitle><manvolnum>1</manvolnum></citerefentry> + <citerefentry><refentrytitle>xdg-app-run</refentrytitle><manvolnum>1</manvolnum></citerefentry> + </para> + + </refsect1> + +</refentry> diff --git a/doc/xdg-app-run.xml b/doc/xdg-app-run.xml index 1d92743..53f1756 100644 --- a/doc/xdg-app-run.xml +++ b/doc/xdg-app-run.xml @@ -274,6 +274,7 @@ <para> <citerefentry><refentrytitle>xdg-app</refentrytitle><manvolnum>1</manvolnum></citerefentry> <citerefentry><refentrytitle>xdg-app-override</refentrytitle><manvolnum>1</manvolnum></citerefentry> + <citerefentry><refentrytitle>xdg-app-enter</refentrytitle><manvolnum>1</manvolnum></citerefentry> </para> </refsect1> diff --git a/lib/xdg-app-helper.c b/lib/xdg-app-helper.c index 18ef248..3cc5805 100644 --- a/lib/xdg-app-helper.c +++ b/lib/xdg-app-helper.c @@ -2428,29 +2428,6 @@ main (int argc, free (tz_val); } -#ifndef DISABLE_USERNS - { - char *uid_map, *gid_map; - /* Now that devpts is mounted we can create a new userspace and map - our uid 1:1 */ - if (unshare (CLONE_NEWUSER)) - die_with_error ("unshare user ns"); - - uid_map = strdup_printf ("%d 0 1\n", uid); - if (!write_file ("/proc/self/uid_map", uid_map)) - die_with_error ("setting up uid map"); - free (uid_map); - - gid_map = strdup_printf ("%d 0 1\n", gid); - if (!write_file ("/proc/self/gid_map", gid_map)) - die_with_error ("setting up gid map"); - free (gid_map); - } -#endif - - __debug__(("setting up seccomp\n")); - setup_seccomp (devel); - __debug__(("forking for child\n")); pid = fork (); @@ -2461,6 +2438,30 @@ main (int argc, { __debug__(("launch executable %s\n", args[0])); +#ifndef DISABLE_USERNS + { + char *uid_map, *gid_map; + /* Now that devpts is mounted we can create a new userspace and map + our uid 1:1 */ + + if (unshare (CLONE_NEWUSER)) + die_with_error ("unshare user ns"); + + uid_map = strdup_printf ("%d 0 1\n", uid); + if (!write_file ("/proc/self/uid_map", uid_map)) + die_with_error ("setting up uid map"); + free (uid_map); + + gid_map = strdup_printf ("%d 0 1\n", gid); + if (!write_file ("/proc/self/gid_map", gid_map)) + die_with_error ("setting up gid map"); + free (gid_map); + } +#endif + + __debug__(("setting up seccomp in child\n")); + setup_seccomp (devel); + if (sync_fd != -1) close (sync_fd); @@ -2469,6 +2470,9 @@ main (int argc, return 0; } + __debug__(("setting up seccomp in monitor\n")); + setup_seccomp (devel); + /* Close all extra fds in pid 1. Any passed in fds have been passed on to the child anyway. */ { |