1 /* Test the posix_spawn_file_actions_addchdir_np function.
2 Copyright (C) 2018-2023 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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/>. */
19 #include <array_length.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>
34 /* Reads the file at PATH, which must consist of exactly one line.
35 Removes the line terminator at the end of the file. */
37 read_one_line (const char *path
)
39 FILE *fp
= xfopen (path
, "r");
42 ssize_t ret
= getline (&buffer
, &length
, fp
);
44 FAIL_EXIT1 ("getline: %m");
46 FAIL_EXIT1 ("getline returned %zd", ret
);
47 if (fgetc (fp
) != EOF
)
48 FAIL_EXIT1 ("trailing bytes in %s", path
);
50 FAIL_EXIT1 ("fgetc: %m");
52 if (buffer
[ret
- 1] != '\n')
53 FAIL_EXIT1 ("missing line terminator in %s", path
);
58 /* Return the path to the "pwd" program. */
60 get_pwd_program (void)
62 const char *const paths
[] = { "/bin/pwd", "/usr/bin/pwd" };
63 for (size_t i
= 0; i
< array_length (paths
); ++i
)
64 if (access (paths
[i
], X_OK
) == 0)
66 FAIL_EXIT1 ("cannot find pwd program");
69 /* Adds chdir operations to ACTIONS, using PATH. If DO_FCHDIR, use
70 the open function and TMPFD to emulate chdir using fchdir. */
72 add_chdir (posix_spawn_file_actions_t
*actions
, const char *path
,
73 bool do_fchdir
, int tmpfd
)
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
81 TEST_COMPARE (posix_spawn_file_actions_addclose (actions
, tmpfd
), 0);
84 TEST_COMPARE (posix_spawn_file_actions_addchdir_np (actions
, path
), 0);
90 /* Directory for temporary file data. Each subtest uses a numeric
92 char *directory
= support_create_temp_directory ("tst-spawn-chdir-");
94 /* Avoid symbolic links, to get more consistent behavior from the
96 char *tmp
= realpath (directory
, NULL
);
98 FAIL_EXIT1 ("realpath: %m");
103 char *original_cwd
= get_current_dir_name ();
104 if (original_cwd
== NULL
)
105 FAIL_EXIT1 ("get_current_dir_name: %m");
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
)
112 /* This subtest does not make sense for fchdir. */
113 if (do_overwrite
&& do_fchdir
)
117 if (test_verbose
> 0)
118 printf ("info: iteration=%d do_spawnp=%d do_overwrite=%d"
120 iteration
, do_spawnp
, do_overwrite
, do_fchdir
);
122 /* The "pwd" program runs in this directory. */
123 char *iteration_directory
= xasprintf ("%s/%d", directory
, iteration
);
124 add_temp_file (iteration_directory
);
125 xmkdir (iteration_directory
, 0777);
127 /* This file receives output from "pwd". */
128 char *output_file_path
129 = xasprintf ("%s/output-file", iteration_directory
);
130 add_temp_file (output_file_path
);
132 /* This subdirectory is used for chdir ordering checks. */
133 char *subdir_path
= xasprintf ("%s/subdir", iteration_directory
);
134 add_temp_file (subdir_path
);
135 xmkdir (subdir_path
, 0777);
137 /* Also used for checking the order of actions. */
138 char *probe_file_path
139 = xasprintf ("%s/subdir/probe-file", iteration_directory
);
140 add_temp_file (probe_file_path
);
141 TEST_COMPARE (access (probe_file_path
, F_OK
), -1);
142 TEST_COMPARE (errno
, ENOENT
);
144 /* This symbolic link is used in a relative path with
146 char *pwd_symlink_path
147 = xasprintf ("%s/subdir/pwd-symlink", iteration_directory
);
148 xsymlink (get_pwd_program (), pwd_symlink_path
);
149 add_temp_file (pwd_symlink_path
);
151 posix_spawn_file_actions_t actions
;
152 TEST_COMPARE (posix_spawn_file_actions_init (&actions
), 0);
153 add_chdir (&actions
, subdir_path
, do_fchdir
, 4);
154 TEST_COMPARE (posix_spawn_file_actions_addopen
155 (&actions
, 3, /* Arbitrary unused descriptor. */
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
, "..", do_fchdir
, 5);
161 TEST_COMPARE (posix_spawn_file_actions_addopen
162 (&actions
, STDOUT_FILENO
, "output-file",
163 O_WRONLY
| O_CREAT
| O_EXCL
, 0777), 0);
165 /* Check that posix_spawn_file_actions_addchdir_np made a copy
168 subdir_path
[0] = '\0';
170 char *const argv
[] = { (char *) "pwd", NULL
};
171 char *const envp
[] = { NULL
} ;
174 TEST_COMPARE (posix_spawnp (&pid
, "pwd", &actions
,
175 NULL
, argv
, envp
), 0);
177 TEST_COMPARE (posix_spawn (&pid
, "subdir/pwd-symlink", &actions
,
178 NULL
, argv
, envp
), 0);
179 TEST_VERIFY (pid
> 0);
181 xwaitpid (pid
, &status
, 0);
182 TEST_COMPARE (status
, 0);
184 /* Check that the current directory did not change. */
186 char *cwd
= get_current_dir_name ();
188 FAIL_EXIT1 ("get_current_dir_name: %m");
189 TEST_COMPARE_BLOB (original_cwd
, strlen (original_cwd
),
195 /* Check the output from "pwd". */
197 char *pwd
= read_one_line (output_file_path
);
198 TEST_COMPARE_BLOB (iteration_directory
, strlen (iteration_directory
),
203 /* This file must now exist. */
204 TEST_COMPARE (access (probe_file_path
, F_OK
), 0);
206 TEST_COMPARE (posix_spawn_file_actions_destroy (&actions
), 0);
207 free (pwd_symlink_path
);
208 free (probe_file_path
);
210 free (output_file_path
);
218 #include <support/test-driver.c>