1/* Test the posix_spawn_file_actions_addchdir_np function.
2 Copyright (C) 2018-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <array_length.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <spawn.h>
23#include <stdbool.h>
24#include <stdlib.h>
25#include <string.h>
26#include <support/check.h>
27#include <support/support.h>
28#include <support/temp_file.h>
29#include <support/test-driver.h>
30#include <support/xstdio.h>
31#include <support/xunistd.h>
32#include <unistd.h>
33
34/* Reads the file at PATH, which must consist of exactly one line.
35 Removes the line terminator at the end of the file. */
36static char *
37read_one_line (const char *path)
38{
39 FILE *fp = xfopen (path, mode: "r");
40 char *buffer = NULL;
41 size_t length = 0;
42 ssize_t ret = getline (lineptr: &buffer, n: &length, stream: fp);
43 if (ferror (stream: fp))
44 FAIL_EXIT1 ("getline: %m");
45 if (ret < 1)
46 FAIL_EXIT1 ("getline returned %zd", ret);
47 if (fgetc (stream: fp) != EOF)
48 FAIL_EXIT1 ("trailing bytes in %s", path);
49 if (ferror (stream: fp))
50 FAIL_EXIT1 ("fgetc: %m");
51 xfclose (fp);
52 if (buffer[ret - 1] != '\n')
53 FAIL_EXIT1 ("missing line terminator in %s", path);
54 buffer[ret - 1] = 0;
55 return buffer;
56}
57
58/* Return the path to the "pwd" program. */
59const char *
60get_pwd_program (void)
61{
62 const char *const paths[] = { "/bin/pwd", "/usr/bin/pwd" };
63 for (size_t i = 0; i < array_length (paths); ++i)
64 if (access (name: paths[i], X_OK) == 0)
65 return paths[i];
66 FAIL_EXIT1 ("cannot find pwd program");
67}
68
69/* Adds chdir operations to ACTIONS, using PATH. If DO_FCHDIR, use
70 the open function and TMPFD to emulate chdir using fchdir. */
71static void
72add_chdir (posix_spawn_file_actions_t *actions, const char *path,
73 bool do_fchdir, int tmpfd)
74{
75 if (do_fchdir)
76 {
77 TEST_COMPARE (posix_spawn_file_actions_addopen
78 (actions, tmpfd, path, O_DIRECTORY | O_RDONLY, 0), 0);
79 TEST_COMPARE (posix_spawn_file_actions_addfchdir_np
80 (actions, tmpfd), 0);
81 TEST_COMPARE (posix_spawn_file_actions_addclose (actions, tmpfd), 0);
82 }
83 else
84 TEST_COMPARE (posix_spawn_file_actions_addchdir_np (actions, path), 0);
85}
86
87static int
88do_test (void)
89{
90 /* Directory for temporary file data. Each subtest uses a numeric
91 subdirectory. */
92 char *directory = support_create_temp_directory (base: "tst-spawn-chdir-");
93 {
94 /* Avoid symbolic links, to get more consistent behavior from the
95 pwd command. */
96 char *tmp = realpath (name: directory, NULL);
97 if (tmp == NULL)
98 FAIL_EXIT1 ("realpath: %m");
99 free (ptr: directory);
100 directory = tmp;
101 }
102
103 char *original_cwd = get_current_dir_name ();
104 if (original_cwd == NULL)
105 FAIL_EXIT1 ("get_current_dir_name: %m");
106
107 int iteration = 0;
108 for (int do_spawnp = 0; do_spawnp < 2; ++do_spawnp)
109 for (int do_overwrite = 0; do_overwrite < 2; ++do_overwrite)
110 for (int do_fchdir = 0; do_fchdir < 2; ++do_fchdir)
111 {
112 /* This subtest does not make sense for fchdir. */
113 if (do_overwrite && do_fchdir)
114 continue;
115
116 ++iteration;
117 if (test_verbose > 0)
118 printf (format: "info: iteration=%d do_spawnp=%d do_overwrite=%d"
119 " do_fchdir=%d\n",
120 iteration, do_spawnp, do_overwrite, do_fchdir);
121
122 /* The "pwd" program runs in this directory. */
123 char *iteration_directory = xasprintf (format: "%s/%d", directory, iteration);
124 add_temp_file (name: iteration_directory);
125 xmkdir (path: iteration_directory, 0777);
126
127 /* This file receives output from "pwd". */
128 char *output_file_path
129 = xasprintf (format: "%s/output-file", iteration_directory);
130 add_temp_file (name: output_file_path);
131
132 /* This subdirectory is used for chdir ordering checks. */
133 char *subdir_path = xasprintf (format: "%s/subdir", iteration_directory);
134 add_temp_file (name: subdir_path);
135 xmkdir (path: subdir_path, 0777);
136
137 /* Also used for checking the order of actions. */
138 char *probe_file_path
139 = xasprintf (format: "%s/subdir/probe-file", iteration_directory);
140 add_temp_file (name: probe_file_path);
141 TEST_COMPARE (access (probe_file_path, F_OK), -1);
142 TEST_COMPARE (errno, ENOENT);
143
144 /* This symbolic link is used in a relative path with
145 posix_spawn. */
146 char *pwd_symlink_path
147 = xasprintf (format: "%s/subdir/pwd-symlink", iteration_directory);
148 xsymlink (target: get_pwd_program (), linkpath: pwd_symlink_path);
149 add_temp_file (name: pwd_symlink_path);
150
151 posix_spawn_file_actions_t actions;
152 TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0);
153 add_chdir (actions: &actions, path: subdir_path, do_fchdir, tmpfd: 4);
154 TEST_COMPARE (posix_spawn_file_actions_addopen
155 (&actions, 3, /* Arbitrary unused descriptor. */
156 "probe-file",
157 O_WRONLY | O_CREAT | O_EXCL, 0777), 0);
158 TEST_COMPARE (posix_spawn_file_actions_addclose (&actions, 3), 0);
159 /* Run the actual in iteration_directory. */
160 add_chdir (actions: &actions, path: "..", do_fchdir, tmpfd: 5);
161 TEST_COMPARE (posix_spawn_file_actions_addopen
162 (&actions, STDOUT_FILENO, "output-file",
163 O_WRONLY | O_CREAT | O_EXCL, 0777), 0);
164
165 /* Check that posix_spawn_file_actions_addchdir_np made a copy
166 of the path. */
167 if (do_overwrite)
168 subdir_path[0] = '\0';
169
170 char *const argv[] = { (char *) "pwd", NULL };
171 char *const envp[] = { NULL } ;
172 pid_t pid;
173 if (do_spawnp)
174 TEST_COMPARE (posix_spawnp (&pid, "pwd", &actions,
175 NULL, argv, envp), 0);
176 else
177 TEST_COMPARE (posix_spawn (&pid, "subdir/pwd-symlink", &actions,
178 NULL, argv, envp), 0);
179 TEST_VERIFY (pid > 0);
180 int status;
181 xwaitpid (pid, status: &status, flags: 0);
182 TEST_COMPARE (status, 0);
183
184 /* Check that the current directory did not change. */
185 {
186 char *cwd = get_current_dir_name ();
187 if (cwd == NULL)
188 FAIL_EXIT1 ("get_current_dir_name: %m");
189 TEST_COMPARE_BLOB (original_cwd, strlen (original_cwd),
190 cwd, strlen (cwd));
191 free (ptr: cwd);
192 }
193
194
195 /* Check the output from "pwd". */
196 {
197 char *pwd = read_one_line (path: output_file_path);
198 TEST_COMPARE_BLOB (iteration_directory, strlen (iteration_directory),
199 pwd, strlen (pwd));
200 free (ptr: pwd);
201 }
202
203 /* This file must now exist. */
204 TEST_COMPARE (access (probe_file_path, F_OK), 0);
205
206 TEST_COMPARE (posix_spawn_file_actions_destroy (&actions), 0);
207 free (ptr: pwd_symlink_path);
208 free (ptr: probe_file_path);
209 free (ptr: subdir_path);
210 free (ptr: output_file_path);
211 }
212
213 free (ptr: directory);
214
215 return 0;
216}
217
218#include <support/test-driver.c>
219

source code of glibc/posix/tst-spawn-chdir.c