Update syscall lists for Linux 6.6
[glibc.git] / posix / tst-spawn-chdir.c
blobc01ca6692ddb792db3194c10098ee2c08d7a9250
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>
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 <sys/wait.h>
33 #include <unistd.h>
34 #include <tst-spawn.h>
36 /* Reads the file at PATH, which must consist of exactly one line.
37 Removes the line terminator at the end of the file. */
38 static char *
39 read_one_line (const char *path)
41 FILE *fp = xfopen (path, "r");
42 char *buffer = NULL;
43 size_t length = 0;
44 ssize_t ret = getline (&buffer, &length, fp);
45 if (ferror (fp))
46 FAIL_EXIT1 ("getline: %m");
47 if (ret < 1)
48 FAIL_EXIT1 ("getline returned %zd", ret);
49 if (fgetc (fp) != EOF)
50 FAIL_EXIT1 ("trailing bytes in %s", path);
51 if (ferror (fp))
52 FAIL_EXIT1 ("fgetc: %m");
53 xfclose (fp);
54 if (buffer[ret - 1] != '\n')
55 FAIL_EXIT1 ("missing line terminator in %s", path);
56 buffer[ret - 1] = 0;
57 return buffer;
60 /* Return the path to the "pwd" program. */
61 const char *
62 get_pwd_program (void)
64 const char *const paths[] = { "/bin/pwd", "/usr/bin/pwd" };
65 for (size_t i = 0; i < array_length (paths); ++i)
66 if (access (paths[i], X_OK) == 0)
67 return paths[i];
68 FAIL_EXIT1 ("cannot find pwd program");
71 /* Adds chdir operations to ACTIONS, using PATH. If DO_FCHDIR, use
72 the open function and TMPFD to emulate chdir using fchdir. */
73 static void
74 add_chdir (posix_spawn_file_actions_t *actions, const char *path,
75 bool do_fchdir, int tmpfd)
77 if (do_fchdir)
79 TEST_COMPARE (posix_spawn_file_actions_addopen
80 (actions, tmpfd, path, O_DIRECTORY | O_RDONLY, 0), 0);
81 TEST_COMPARE (posix_spawn_file_actions_addfchdir_np
82 (actions, tmpfd), 0);
83 TEST_COMPARE (posix_spawn_file_actions_addclose (actions, tmpfd), 0);
85 else
86 TEST_COMPARE (posix_spawn_file_actions_addchdir_np (actions, path), 0);
89 static int
90 do_test (void)
92 /* Directory for temporary file data. Each subtest uses a numeric
93 subdirectory. */
94 char *directory = support_create_temp_directory ("tst-spawn-chdir-");
96 /* Avoid symbolic links, to get more consistent behavior from the
97 pwd command. */
98 char *tmp = realpath (directory, NULL);
99 if (tmp == NULL)
100 FAIL_EXIT1 ("realpath: %m");
101 free (directory);
102 directory = tmp;
105 char *original_cwd = get_current_dir_name ();
106 if (original_cwd == NULL)
107 FAIL_EXIT1 ("get_current_dir_name: %m");
109 int iteration = 0;
110 for (int do_spawnp = 0; do_spawnp < 2; ++do_spawnp)
111 for (int do_overwrite = 0; do_overwrite < 2; ++do_overwrite)
112 for (int do_fchdir = 0; do_fchdir < 2; ++do_fchdir)
114 /* This subtest does not make sense for fchdir. */
115 if (do_overwrite && do_fchdir)
116 continue;
118 ++iteration;
119 if (test_verbose > 0)
120 printf ("info: iteration=%d do_spawnp=%d do_overwrite=%d"
121 " do_fchdir=%d\n",
122 iteration, do_spawnp, do_overwrite, do_fchdir);
124 /* The "pwd" program runs in this directory. */
125 char *iteration_directory = xasprintf ("%s/%d", directory, iteration);
126 add_temp_file (iteration_directory);
127 xmkdir (iteration_directory, 0777);
129 /* This file receives output from "pwd". */
130 char *output_file_path
131 = xasprintf ("%s/output-file", iteration_directory);
132 add_temp_file (output_file_path);
134 /* This subdirectory is used for chdir ordering checks. */
135 char *subdir_path = xasprintf ("%s/subdir", iteration_directory);
136 add_temp_file (subdir_path);
137 xmkdir (subdir_path, 0777);
139 /* Also used for checking the order of actions. */
140 char *probe_file_path
141 = xasprintf ("%s/subdir/probe-file", iteration_directory);
142 add_temp_file (probe_file_path);
143 TEST_COMPARE (access (probe_file_path, F_OK), -1);
144 TEST_COMPARE (errno, ENOENT);
146 /* This symbolic link is used in a relative path with
147 posix_spawn. */
148 char *pwd_symlink_path
149 = xasprintf ("%s/subdir/pwd-symlink", iteration_directory);
150 xsymlink (get_pwd_program (), pwd_symlink_path);
151 add_temp_file (pwd_symlink_path);
153 posix_spawn_file_actions_t actions;
154 TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0);
155 add_chdir (&actions, subdir_path, do_fchdir, 4);
156 TEST_COMPARE (posix_spawn_file_actions_addopen
157 (&actions, 3, /* Arbitrary unused descriptor. */
158 "probe-file",
159 O_WRONLY | O_CREAT | O_EXCL, 0777), 0);
160 TEST_COMPARE (posix_spawn_file_actions_addclose (&actions, 3), 0);
161 /* Run the actual in iteration_directory. */
162 add_chdir (&actions, "..", do_fchdir, 5);
163 TEST_COMPARE (posix_spawn_file_actions_addopen
164 (&actions, STDOUT_FILENO, "output-file",
165 O_WRONLY | O_CREAT | O_EXCL, 0777), 0);
167 /* Check that posix_spawn_file_actions_addchdir_np made a copy
168 of the path. */
169 if (do_overwrite)
170 subdir_path[0] = '\0';
172 char *const argv[] = { (char *) "pwd", NULL };
173 char *const envp[] = { NULL } ;
174 PID_T_TYPE pid;
175 if (do_spawnp)
176 TEST_COMPARE (POSIX_SPAWNP (&pid, "pwd", &actions,
177 NULL, argv, envp), 0);
178 else
179 TEST_COMPARE (POSIX_SPAWN (&pid, "subdir/pwd-symlink", &actions,
180 NULL, argv, envp), 0);
181 TEST_VERIFY (pid > 0);
182 siginfo_t sinfo;
183 TEST_COMPARE (WAITID (P_ALL, 0, &sinfo, WEXITED), 0);
184 TEST_COMPARE (sinfo.si_code, CLD_EXITED);
185 TEST_COMPARE (sinfo.si_status, 0);
187 /* Check that the current directory did not change. */
189 char *cwd = get_current_dir_name ();
190 if (cwd == NULL)
191 FAIL_EXIT1 ("get_current_dir_name: %m");
192 TEST_COMPARE_BLOB (original_cwd, strlen (original_cwd),
193 cwd, strlen (cwd));
194 free (cwd);
198 /* Check the output from "pwd". */
200 char *pwd = read_one_line (output_file_path);
201 TEST_COMPARE_BLOB (iteration_directory, strlen (iteration_directory),
202 pwd, strlen (pwd));
203 free (pwd);
206 /* This file must now exist. */
207 TEST_COMPARE (access (probe_file_path, F_OK), 0);
209 TEST_COMPARE (posix_spawn_file_actions_destroy (&actions), 0);
210 free (pwd_symlink_path);
211 free (probe_file_path);
212 free (subdir_path);
213 free (output_file_path);
216 free (directory);
218 return 0;
221 #include <support/test-driver.c>