Branch data Line data Source code
1 : : /*
2 : : * Copyright 2021 Collabora Ltd.
3 : : *
4 : : * SPDX-License-Identifier: LGPL-2.1-or-later
5 : : *
6 : : * This library is free software; you can redistribute it and/or
7 : : * modify it under the terms of the GNU Lesser General Public
8 : : * License as published by the Free Software Foundation; either
9 : : * version 2.1 of the License, or (at your option) any later version.
10 : : *
11 : : * This library is distributed in the hope that it will be useful,
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : * Lesser General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU Lesser General Public
17 : : * License along with this library; if not, see
18 : : * <http://www.gnu.org/licenses/>.
19 : : */
20 : :
21 : : #include <errno.h>
22 : : #include <stdio.h>
23 : :
24 : : #include <glib.h>
25 : :
26 : : #ifdef G_OS_UNIX
27 : : #include <sys/types.h>
28 : : #include <sys/wait.h>
29 : : #endif
30 : :
31 : : static void
32 : 0 : child_setup (gpointer user_data)
33 : : {
34 : 0 : }
35 : :
36 : : typedef struct
37 : : {
38 : : int wait_status;
39 : : gboolean done;
40 : : } ChildStatus;
41 : :
42 : : static ChildStatus child_status = { -1, FALSE };
43 : :
44 : : static void
45 : 7 : child_watch_cb (GPid pid,
46 : : gint status,
47 : : gpointer user_data)
48 : : {
49 : 7 : child_status.wait_status = status;
50 : 7 : child_status.done = TRUE;
51 : 7 : }
52 : :
53 : : int
54 : 7 : main (int argc,
55 : : char **argv)
56 : : {
57 : 7 : gboolean search_path = FALSE;
58 : 7 : gboolean search_path_from_envp = FALSE;
59 : 7 : gboolean slow_path = FALSE;
60 : 7 : gboolean unset_path_in_envp = FALSE;
61 : 7 : gchar *chdir_child = NULL;
62 : 7 : gchar *set_path_in_envp = NULL;
63 : 7 : gchar **envp = NULL;
64 : 7 : GOptionEntry entries[] =
65 : : {
66 : : { "chdir-child", '\0',
67 : : G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &chdir_child,
68 : : "Run PROGRAM in this working directory", NULL },
69 : : { "search-path", '\0',
70 : : G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &search_path,
71 : : "Search PATH for PROGRAM", NULL },
72 : : { "search-path-from-envp", '\0',
73 : : G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &search_path_from_envp,
74 : : "Search PATH from specified environment", NULL },
75 : : { "set-path-in-envp", '\0',
76 : : G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &set_path_in_envp,
77 : : "Set PATH in specified environment to this value", "PATH", },
78 : : { "unset-path-in-envp", '\0',
79 : : G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &unset_path_in_envp,
80 : : "Unset PATH in specified environment", NULL },
81 : : { "slow-path", '\0',
82 : : G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &slow_path,
83 : : "Use a child-setup function to avoid the posix_spawn fast path", NULL },
84 : : G_OPTION_ENTRY_NULL
85 : : };
86 : 7 : GError *error = NULL;
87 : 7 : int ret = 1;
88 : 7 : GSpawnFlags spawn_flags = G_SPAWN_DO_NOT_REAP_CHILD;
89 : : GPid pid;
90 : 7 : GOptionContext *context = NULL;
91 : :
92 : 7 : context = g_option_context_new ("PROGRAM [ARGS...]");
93 : 7 : g_option_context_add_main_entries (context, entries, NULL);
94 : :
95 [ - + ]: 7 : if (!g_option_context_parse (context, &argc, &argv, &error))
96 : : {
97 : 0 : ret = 2;
98 : 0 : goto out;
99 : : }
100 : :
101 [ - + ]: 7 : if (argc < 2)
102 : : {
103 : 0 : g_set_error (&error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
104 : : "Usage: %s [OPTIONS] PROGRAM [ARGS...]", argv[0]);
105 : 0 : ret = 2;
106 : 0 : goto out;
107 : : }
108 : :
109 : 7 : envp = g_get_environ ();
110 : :
111 [ + + - + ]: 7 : if (set_path_in_envp != NULL && unset_path_in_envp)
112 : : {
113 : 0 : g_set_error (&error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
114 : : "Cannot both set PATH and unset it");
115 : 0 : ret = 2;
116 : 0 : goto out;
117 : : }
118 : :
119 [ + + ]: 7 : if (set_path_in_envp != NULL)
120 : 3 : envp = g_environ_setenv (envp, "PATH", set_path_in_envp, TRUE);
121 : :
122 [ + + ]: 7 : if (unset_path_in_envp)
123 : 1 : envp = g_environ_unsetenv (envp, "PATH");
124 : :
125 [ + + ]: 7 : if (search_path)
126 : 4 : spawn_flags |= G_SPAWN_SEARCH_PATH;
127 : :
128 [ + + ]: 7 : if (search_path_from_envp)
129 : 3 : spawn_flags |= G_SPAWN_SEARCH_PATH_FROM_ENVP;
130 : :
131 [ - + ]: 14 : if (!g_spawn_async_with_pipes (chdir_child,
132 : 7 : &argv[1],
133 : : envp,
134 : : spawn_flags,
135 [ - + ]: 7 : slow_path ? child_setup : NULL,
136 : : NULL, /* user_data */
137 : : &pid,
138 : : NULL, /* stdin */
139 : : NULL, /* stdout */
140 : : NULL, /* stderr */
141 : : &error))
142 : : {
143 : 0 : ret = 1;
144 : 0 : goto out;
145 : : }
146 : :
147 : 7 : g_child_watch_add (pid, child_watch_cb, NULL);
148 : :
149 [ + + ]: 16 : while (!child_status.done)
150 : 9 : g_main_context_iteration (NULL, TRUE);
151 : :
152 : 7 : g_spawn_close_pid (pid);
153 : :
154 : : #ifdef G_OS_UNIX
155 [ + - ]: 7 : if (WIFEXITED (child_status.wait_status))
156 : 7 : ret = WEXITSTATUS (child_status.wait_status);
157 : : else
158 : 0 : ret = 1;
159 : : #else
160 : : ret = child_status.wait_status;
161 : : #endif
162 : :
163 : 7 : out:
164 [ - + ]: 7 : if (error != NULL)
165 : 0 : fprintf (stderr, "%s\n", error->message);
166 : :
167 : 7 : g_free (set_path_in_envp);
168 : 7 : g_free (chdir_child);
169 : 7 : g_clear_error (&error);
170 : 7 : g_strfreev (envp);
171 : 7 : g_option_context_free (context);
172 : 7 : return ret;
173 : : }
|