1 | /* Check posix_spawn set controlling terminal extension. |
2 | Copyright (C) 2022-2024 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 <getopt.h> |
23 | #include <intprops.h> |
24 | #include <paths.h> |
25 | #include <spawn.h> |
26 | #include <stdbool.h> |
27 | #include <stdio.h> |
28 | #include <string.h> |
29 | #include <support/check.h> |
30 | #include <support/xunistd.h> |
31 | #include <sys/wait.h> |
32 | #include <sys/ioctl.h> |
33 | #include <stdlib.h> |
34 | #include <termios.h> |
35 | #include <tst-spawn.h> |
36 | |
37 | #ifndef PATH_MAX |
38 | # define PATH_MAX 1024 |
39 | #endif |
40 | static char ptmxpath[PATH_MAX]; |
41 | |
42 | static int |
43 | handle_restart (const char *argv1, const char *argv2) |
44 | { |
45 | /* If process group is not changed (POSIX_SPAWN_SETPGROUP), then check |
46 | the creating process one, otherwise check against the process group |
47 | itself. */ |
48 | pid_t pgrp; |
49 | if (strcmp (argv1, "setgrpr" ) != 0) |
50 | TEST_COMPARE (sscanf (argv1, "%d" , &pgrp), 1); |
51 | else |
52 | { |
53 | pgrp = getpgrp (); |
54 | /* Check if a new process group was actually created. */ |
55 | pid_t ppid = getppid (); |
56 | pid_t pgid = getpgid (pid: ppid); |
57 | TEST_VERIFY (pgid != pgrp); |
58 | } |
59 | |
60 | char *endptr; |
61 | long int tcfd = strtol (argv2, &endptr, 10); |
62 | if (*endptr != '\0' || tcfd > INT_MAX) |
63 | FAIL_EXIT1 ("invalid file descriptor name: %s" , argv2); |
64 | if (tcfd != -1) |
65 | { |
66 | TEST_COMPARE (fcntl (tcfd, F_GETFD), -1); |
67 | TEST_COMPARE (errno, EBADF); |
68 | } |
69 | |
70 | int fd = xopen (_PATH_TTY, O_RDONLY, 0600); |
71 | TEST_COMPARE (tcgetpgrp (fd), pgrp); |
72 | xclose (fd); |
73 | |
74 | return 0; |
75 | } |
76 | |
77 | static int restart; |
78 | #define CMDLINE_OPTIONS \ |
79 | { "restart", no_argument, &restart, 1 }, |
80 | |
81 | static void |
82 | run_subprogram (int argc, char *argv[], const posix_spawnattr_t *attr, |
83 | const posix_spawn_file_actions_t *actions, int tcfd, |
84 | int exp_err) |
85 | { |
86 | short int flags; |
87 | TEST_COMPARE (posix_spawnattr_getflags (attr, &flags), 0); |
88 | bool setpgrp = flags & POSIX_SPAWN_SETPGROUP; |
89 | |
90 | char *spargv[9]; |
91 | TEST_VERIFY_EXIT (((argc - 1) + 4) < array_length (spargv)); |
92 | char pgrp[INT_STRLEN_BOUND (pid_t)]; |
93 | char tcfdstr[INT_STRLEN_BOUND (int)]; |
94 | |
95 | int i = 0; |
96 | for (; i < argc - 1; i++) |
97 | spargv[i] = argv[i + 1]; |
98 | spargv[i++] = (char *) "--direct" ; |
99 | spargv[i++] = (char *) "--restart" ; |
100 | if (setpgrp) |
101 | spargv[i++] = (char *) "setgrpr" ; |
102 | else |
103 | { |
104 | snprintf (s: pgrp, maxlen: sizeof pgrp, format: "%d" , getpgrp ()); |
105 | spargv[i++] = pgrp; |
106 | } |
107 | snprintf (s: tcfdstr, maxlen: sizeof tcfdstr, format: "%d" , tcfd); |
108 | spargv[i++] = tcfdstr; |
109 | spargv[i] = NULL; |
110 | |
111 | pid_t pid; |
112 | TEST_COMPARE (POSIX_SPAWN (&pid, argv[1], actions, attr, spargv, environ), |
113 | exp_err); |
114 | if (exp_err != 0) |
115 | return; |
116 | |
117 | siginfo_t sinfo; |
118 | TEST_COMPARE (WAITID (P_ALL, 0, &sinfo, WEXITED), 0); |
119 | TEST_COMPARE (sinfo.si_code, CLD_EXITED); |
120 | TEST_COMPARE (sinfo.si_status, 0); |
121 | } |
122 | |
123 | static int |
124 | run_test (int argc, char *argv[]) |
125 | { |
126 | /* We must have either: |
127 | - four parameters left if called initially: |
128 | + path to ld.so optional |
129 | + "--library-path" optional |
130 | + the library path optional |
131 | + the application name |
132 | - six parameters left if called through re-execution: |
133 | + --setgrpr optional |
134 | */ |
135 | |
136 | int tcfd = xopen (path: ptmxpath, O_RDONLY, 0600); |
137 | |
138 | /* Check setting the controlling terminal without changing the group. */ |
139 | { |
140 | posix_spawnattr_t attr; |
141 | TEST_COMPARE (posix_spawnattr_init (&attr), 0); |
142 | posix_spawn_file_actions_t actions; |
143 | TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0); |
144 | TEST_COMPARE (posix_spawn_file_actions_addtcsetpgrp_np (&actions, tcfd), |
145 | 0); |
146 | |
147 | run_subprogram (argc, argv, attr: &attr, actions: &actions, tcfd: -1, exp_err: 0); |
148 | } |
149 | |
150 | /* Check setting both the controlling terminal and the create a new process |
151 | group. */ |
152 | { |
153 | posix_spawnattr_t attr; |
154 | TEST_COMPARE (posix_spawnattr_init (&attr), 0); |
155 | TEST_COMPARE (posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETPGROUP), 0); |
156 | posix_spawn_file_actions_t actions; |
157 | TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0); |
158 | TEST_COMPARE (posix_spawn_file_actions_addtcsetpgrp_np (&actions, tcfd), |
159 | 0); |
160 | |
161 | run_subprogram (argc, argv, attr: &attr, actions: &actions, tcfd: -1, exp_err: 0); |
162 | } |
163 | |
164 | /* Same as before, but check if the addclose file actions closes the terminal |
165 | file descriptor. */ |
166 | { |
167 | posix_spawnattr_t attr; |
168 | TEST_COMPARE (posix_spawnattr_init (&attr), 0); |
169 | TEST_COMPARE (posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETPGROUP), 0); |
170 | posix_spawn_file_actions_t actions; |
171 | TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0); |
172 | TEST_COMPARE (posix_spawn_file_actions_addtcsetpgrp_np (&actions, tcfd), |
173 | 0); |
174 | TEST_COMPARE (posix_spawn_file_actions_addclose (&actions, tcfd), 0); |
175 | |
176 | run_subprogram (argc, argv, attr: &attr, actions: &actions, tcfd, exp_err: 0); |
177 | } |
178 | |
179 | /* Trying to set the controlling terminal after a setsid incurs in a ENOTTY |
180 | from tcsetpgrp. */ |
181 | { |
182 | posix_spawnattr_t attr; |
183 | TEST_COMPARE (posix_spawnattr_init (&attr), 0); |
184 | TEST_COMPARE (posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETSID), 0); |
185 | posix_spawn_file_actions_t actions; |
186 | TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0); |
187 | TEST_COMPARE (posix_spawn_file_actions_addtcsetpgrp_np (&actions, tcfd), |
188 | 0); |
189 | |
190 | run_subprogram (argc, argv, attr: &attr, actions: &actions, tcfd: -1, ENOTTY); |
191 | } |
192 | |
193 | xclose (tcfd); |
194 | |
195 | return 0; |
196 | } |
197 | |
198 | static int |
199 | do_test (int argc, char *argv[]) |
200 | { |
201 | if (restart) |
202 | return handle_restart (argv1: argv[1], argv2: argv[2]); |
203 | |
204 | pid_t pid = xfork (); |
205 | if (pid == 0) |
206 | { |
207 | /* Create a pseudo-terminal to avoid interfering with the one using by |
208 | test itself, creates a new session (so there is no controlling |
209 | terminal), and set the pseudo-terminal as the controlling one. */ |
210 | int ptmx = posix_openpt (oflag: 0); |
211 | if (ptmx == -1) |
212 | { |
213 | if (errno == ENXIO) |
214 | FAIL_UNSUPPORTED ("terminal not available, skipping test" ); |
215 | FAIL_EXIT1 ("posix_openpt (0): %m" ); |
216 | } |
217 | TEST_VERIFY_EXIT (grantpt (ptmx) == 0); |
218 | TEST_VERIFY_EXIT (unlockpt (ptmx) == 0); |
219 | |
220 | TEST_VERIFY_EXIT (setsid () != -1); |
221 | TEST_VERIFY_EXIT (ioctl (ptmx, TIOCSCTTY, NULL) == 0); |
222 | while (dup2 (fd: ptmx, STDIN_FILENO) == -1 && errno == EBUSY) |
223 | ; |
224 | while (dup2 (fd: ptmx, STDOUT_FILENO) == -1 && errno == EBUSY) |
225 | ; |
226 | while (dup2 (fd: ptmx, STDERR_FILENO) == -1 && errno == EBUSY) |
227 | ; |
228 | TEST_VERIFY_EXIT (ptsname_r (ptmx, ptmxpath, sizeof ptmxpath) == 0); |
229 | xclose (ptmx); |
230 | |
231 | run_test (argc, argv); |
232 | _exit (0); |
233 | } |
234 | int status; |
235 | xwaitpid (pid, status: &status, flags: 0); |
236 | TEST_VERIFY (WIFEXITED (status)); |
237 | exit (0); |
238 | } |
239 | |
240 | #define TEST_FUNCTION_ARGV do_test |
241 | #include <support/test-driver.c> |
242 | |