1 /* Capture output from a subprocess.
2 Copyright (C) 2017-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 <support/subprocess.h>
20 #include <support/capture_subprocess.h>
25 #include <support/check.h>
26 #include <support/xunistd.h>
27 #include <support/xsocket.h>
28 #include <support/xspawn.h>
29 #include <support/support.h>
30 #include <support/test-driver.h>
33 transfer (const char *what
, struct pollfd
*pfd
, struct xmemstream
*stream
)
35 if (pfd
->revents
!= 0)
38 ssize_t ret
= TEMP_FAILURE_RETRY (read (pfd
->fd
, buf
, sizeof (buf
)));
41 support_record_failure ();
42 printf ("error: reading from subprocess %s: %m\n", what
);
48 /* EOF reached. Stop listening. */
53 /* Store the data just read. */
54 TEST_VERIFY (fwrite (buf
, ret
, 1, stream
->out
) == 1);
59 support_capture_poll (struct support_capture_subprocess
*result
,
60 struct support_subprocess
*proc
)
62 struct pollfd fds
[2] =
64 { .fd
= proc
->stdout_pipe
[0], .events
= POLLIN
},
65 { .fd
= proc
->stderr_pipe
[0], .events
= POLLIN
},
71 transfer ("stdout", &fds
[0], &result
->out
);
72 transfer ("stderr", &fds
[1], &result
->err
);
74 while (fds
[0].events
!= 0 || fds
[1].events
!= 0);
76 xfclose_memstream (&result
->out
);
77 xfclose_memstream (&result
->err
);
79 result
->status
= support_process_wait (proc
);
82 struct support_capture_subprocess
83 support_capture_subprocess (void (*callback
) (void *), void *closure
)
85 struct support_capture_subprocess result
;
86 xopen_memstream (&result
.out
);
87 xopen_memstream (&result
.err
);
89 struct support_subprocess proc
= support_subprocess (callback
, closure
);
91 support_capture_poll (&result
, &proc
);
95 struct support_capture_subprocess
96 support_capture_subprogram (const char *file
, char *const argv
[])
98 struct support_capture_subprocess result
;
99 xopen_memstream (&result
.out
);
100 xopen_memstream (&result
.err
);
102 struct support_subprocess proc
= support_subprogram (file
, argv
);
104 support_capture_poll (&result
, &proc
);
108 /* Copies the executable into a restricted directory, so that we can
109 safely make it SGID with the TARGET group ID. Then runs the
112 copy_and_spawn_sgid (char *child_id
, gid_t gid
)
114 char *dirname
= xasprintf ("%s/tst-tunables-setuid.%jd",
115 test_dir
, (intmax_t) getpid ());
116 char *execname
= xasprintf ("%s/bin", dirname
);
119 int ret
= 1, status
= 1;
121 TEST_VERIFY (mkdir (dirname
, 0700) == 0);
122 if (support_record_failure_is_failed ())
125 infd
= open ("/proc/self/exe", O_RDONLY
);
127 FAIL_UNSUPPORTED ("unsupported: Cannot read binary from procfs\n");
129 outfd
= open (execname
, O_WRONLY
| O_CREAT
| O_EXCL
, 0700);
130 TEST_VERIFY (outfd
>= 0);
131 if (support_record_failure_is_failed ())
137 ssize_t rdcount
= read (infd
, buf
, sizeof (buf
));
138 TEST_VERIFY (rdcount
>= 0);
139 if (support_record_failure_is_failed ())
144 char *end
= buf
+ rdcount
;
147 ssize_t wrcount
= write (outfd
, buf
, end
- p
);
150 TEST_VERIFY (wrcount
> 0);
151 if (support_record_failure_is_failed ())
157 bool chowned
= false;
158 TEST_VERIFY ((chowned
= fchown (outfd
, getuid (), gid
) == 0)
160 if (support_record_failure_is_failed ())
168 TEST_VERIFY (fchmod (outfd
, 02750) == 0);
169 if (support_record_failure_is_failed ())
171 TEST_VERIFY (close (outfd
) == 0);
172 if (support_record_failure_is_failed ())
174 TEST_VERIFY (close (infd
) == 0);
175 if (support_record_failure_is_failed ())
178 /* We have the binary, now spawn the subprocess. Avoid using
179 support_subprogram because we only want the program exit status, not the
184 char * const args
[] = {execname
, child_id
, NULL
};
186 status
= support_subprogram_wait (args
[0], args
);
193 if (execname
!= NULL
)
205 FAIL_UNSUPPORTED ("Failed to make sgid executable for test\n");
207 FAIL_EXIT1 ("Failed to make sgid executable for test\n");
213 support_capture_subprogram_self_sgid (char *child_id
)
216 const int count
= 64;
219 /* Get a GID which is not our current GID, but is present in the
220 supplementary group list. */
221 int ret
= getgroups (count
, groups
);
223 FAIL_UNSUPPORTED("Could not get group list for user %jd\n",
224 (intmax_t) getuid ());
226 gid_t current
= getgid ();
227 for (int i
= 0; i
< ret
; ++i
)
229 if (groups
[i
] != current
)
237 FAIL_UNSUPPORTED("Could not find a suitable GID for user %jd\n",
238 (intmax_t) getuid ());
240 return copy_and_spawn_sgid (child_id
, target
);
244 support_capture_subprocess_free (struct support_capture_subprocess
*p
)
246 free (p
->out
.buffer
);
247 free (p
->err
.buffer
);