/* * Copyright 2021 Collabora Ltd. * * This library 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.1 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 * . */ #include #include #include #ifdef G_OS_UNIX #include #include #endif static void child_setup (gpointer user_data) { } typedef struct { int wait_status; gboolean done; } ChildStatus; static ChildStatus child_status = { -1, FALSE }; static void child_watch_cb (GPid pid, gint status, gpointer user_data) { child_status.wait_status = status; child_status.done = TRUE; } int main (int argc, char **argv) { gboolean search_path = FALSE; gboolean search_path_from_envp = FALSE; gboolean slow_path = FALSE; gboolean unset_path_in_envp = FALSE; gchar *chdir_child = NULL; gchar *set_path_in_envp = NULL; gchar **envp = NULL; GOptionEntry entries[] = { { "chdir-child", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &chdir_child, "Run PROGRAM in this working directory", NULL }, { "search-path", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &search_path, "Search PATH for PROGRAM", NULL }, { "search-path-from-envp", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &search_path_from_envp, "Search PATH from specified environment", NULL }, { "set-path-in-envp", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &set_path_in_envp, "Set PATH in specified environment to this value", "PATH", }, { "unset-path-in-envp", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &unset_path_in_envp, "Unset PATH in specified environment", NULL }, { "slow-path", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &slow_path, "Use a child-setup function to avoid the posix_spawn fast path", NULL }, { NULL } }; GError *error = NULL; int ret = 1; GSpawnFlags spawn_flags = G_SPAWN_DO_NOT_REAP_CHILD; GPid pid; GOptionContext *context = NULL; context = g_option_context_new ("PROGRAM [ARGS...]"); g_option_context_add_main_entries (context, entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { ret = 2; goto out; } if (argc < 2) { g_set_error (&error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "Usage: %s [OPTIONS] PROGRAM [ARGS...]", argv[0]); ret = 2; goto out; } envp = g_get_environ (); if (set_path_in_envp != NULL && unset_path_in_envp) { g_set_error (&error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "Cannot both set PATH and unset it"); ret = 2; goto out; } if (set_path_in_envp != NULL) envp = g_environ_setenv (envp, "PATH", set_path_in_envp, TRUE); if (unset_path_in_envp) envp = g_environ_unsetenv (envp, "PATH"); if (search_path) spawn_flags |= G_SPAWN_SEARCH_PATH; if (search_path_from_envp) spawn_flags |= G_SPAWN_SEARCH_PATH_FROM_ENVP; if (!g_spawn_async_with_pipes (chdir_child, &argv[1], envp, spawn_flags, slow_path ? child_setup : NULL, NULL, /* user_data */ &pid, NULL, /* stdin */ NULL, /* stdout */ NULL, /* stderr */ &error)) { ret = 1; goto out; } g_child_watch_add (pid, child_watch_cb, NULL); while (!child_status.done) g_main_context_iteration (NULL, TRUE); g_spawn_close_pid (pid); #ifdef G_OS_UNIX if (WIFEXITED (child_status.wait_status)) ret = WEXITSTATUS (child_status.wait_status); else ret = 1; #else ret = child_status.wait_status; #endif out: if (error != NULL) fprintf (stderr, "%s\n", error->message); g_free (set_path_in_envp); g_free (chdir_child); g_clear_error (&error); g_strfreev (envp); g_option_context_free (context); return ret; }