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