xref: /freebsd/contrib/atf/atf-c/utils.c (revision c203bd70)
10677dfd1SJulio Merino /* Copyright (c) 2010 The NetBSD Foundation, Inc.
2c243e490SMarcel Moolenaar  * All rights reserved.
3c243e490SMarcel Moolenaar  *
4c243e490SMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
5c243e490SMarcel Moolenaar  * modification, are permitted provided that the following conditions
6c243e490SMarcel Moolenaar  * are met:
7c243e490SMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
8c243e490SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
9c243e490SMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
10c243e490SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
11c243e490SMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
12c243e490SMarcel Moolenaar  *
13c243e490SMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
14c243e490SMarcel Moolenaar  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
15c243e490SMarcel Moolenaar  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16c243e490SMarcel Moolenaar  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17c243e490SMarcel Moolenaar  * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
18c243e490SMarcel Moolenaar  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19c243e490SMarcel Moolenaar  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20c243e490SMarcel Moolenaar  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21c243e490SMarcel Moolenaar  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22c243e490SMarcel Moolenaar  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23c243e490SMarcel Moolenaar  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
240677dfd1SJulio Merino  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  */
25c243e490SMarcel Moolenaar 
26c243e490SMarcel Moolenaar #include "atf-c/utils.h"
27c243e490SMarcel Moolenaar 
28a18eacbeSJulio Merino #include <sys/stat.h>
29a18eacbeSJulio Merino #include <sys/wait.h>
30a18eacbeSJulio Merino 
31a18eacbeSJulio Merino #include <err.h>
32a18eacbeSJulio Merino #include <errno.h>
33a18eacbeSJulio Merino #include <fcntl.h>
34a18eacbeSJulio Merino #include <regex.h>
35a18eacbeSJulio Merino #include <stdio.h>
36a18eacbeSJulio Merino #include <stdlib.h>
37a18eacbeSJulio Merino #include <string.h>
38a18eacbeSJulio Merino #include <unistd.h>
39a18eacbeSJulio Merino 
40a18eacbeSJulio Merino #include <atf-c.h>
41a18eacbeSJulio Merino 
420677dfd1SJulio Merino #include "atf-c/detail/dynstr.h"
430677dfd1SJulio Merino 
44*c203bd70SAlex Richardson /* No prototype in header for this one, it's a little sketchy (internal). */
45*c203bd70SAlex Richardson void atf_tc_set_resultsfile(const char *);
46*c203bd70SAlex Richardson 
470677dfd1SJulio Merino /** Allocate a filename to be used by atf_utils_{fork,wait}.
480677dfd1SJulio Merino  *
490677dfd1SJulio Merino  * In case of a failure, marks the calling test as failed when in_parent is
500677dfd1SJulio Merino  * true, else terminates execution.
510677dfd1SJulio Merino  *
520677dfd1SJulio Merino  * \param [out] name String to contain the generated file.
530677dfd1SJulio Merino  * \param pid PID of the process that will write to the file.
540677dfd1SJulio Merino  * \param suffix Either "out" or "err".
550677dfd1SJulio Merino  * \param in_parent If true, fail with atf_tc_fail; else use err(3). */
560677dfd1SJulio Merino static void
init_out_filename(atf_dynstr_t * name,const pid_t pid,const char * suffix,const bool in_parent)570677dfd1SJulio Merino init_out_filename(atf_dynstr_t *name, const pid_t pid, const char *suffix,
580677dfd1SJulio Merino                   const bool in_parent)
590677dfd1SJulio Merino {
600677dfd1SJulio Merino     atf_error_t error;
610677dfd1SJulio Merino 
620677dfd1SJulio Merino     error = atf_dynstr_init_fmt(name, "atf_utils_fork_%d_%s.txt",
630677dfd1SJulio Merino                                 (int)pid, suffix);
640677dfd1SJulio Merino     if (atf_is_error(error)) {
650677dfd1SJulio Merino         char buffer[1024];
660677dfd1SJulio Merino         atf_error_format(error, buffer, sizeof(buffer));
670677dfd1SJulio Merino         if (in_parent) {
680677dfd1SJulio Merino             atf_tc_fail("Failed to create output file: %s", buffer);
690677dfd1SJulio Merino         } else {
700677dfd1SJulio Merino             err(EXIT_FAILURE, "Failed to create output file: %s", buffer);
710677dfd1SJulio Merino         }
720677dfd1SJulio Merino     }
730677dfd1SJulio Merino }
74a18eacbeSJulio Merino 
75a18eacbeSJulio Merino /** Searches for a regexp in a string.
76a18eacbeSJulio Merino  *
77a18eacbeSJulio Merino  * \param regex The regexp to look for.
78a18eacbeSJulio Merino  * \param str The string in which to look for the expression.
79a18eacbeSJulio Merino  *
80a18eacbeSJulio Merino  * \return True if there is a match; false otherwise. */
81a18eacbeSJulio Merino static
82a18eacbeSJulio Merino bool
grep_string(const char * regex,const char * str)83a18eacbeSJulio Merino grep_string(const char *regex, const char *str)
84a18eacbeSJulio Merino {
85a18eacbeSJulio Merino     int res;
86a18eacbeSJulio Merino     regex_t preg;
87a18eacbeSJulio Merino 
88a18eacbeSJulio Merino     printf("Looking for '%s' in '%s'\n", regex, str);
89a18eacbeSJulio Merino     ATF_REQUIRE(regcomp(&preg, regex, REG_EXTENDED) == 0);
90a18eacbeSJulio Merino 
91a18eacbeSJulio Merino     res = regexec(&preg, str, 0, NULL, 0);
92a18eacbeSJulio Merino     ATF_REQUIRE(res == 0 || res == REG_NOMATCH);
93a18eacbeSJulio Merino 
94a18eacbeSJulio Merino     regfree(&preg);
95a18eacbeSJulio Merino 
96a18eacbeSJulio Merino     return res == 0;
97a18eacbeSJulio Merino }
98a18eacbeSJulio Merino 
99a18eacbeSJulio Merino /** Prints the contents of a file to stdout.
100a18eacbeSJulio Merino  *
101a18eacbeSJulio Merino  * \param name The name of the file to be printed.
102a18eacbeSJulio Merino  * \param prefix An string to be prepended to every line of the printed
103a18eacbeSJulio Merino  *     file. */
104a18eacbeSJulio Merino void
atf_utils_cat_file(const char * name,const char * prefix)105a18eacbeSJulio Merino atf_utils_cat_file(const char *name, const char *prefix)
106a18eacbeSJulio Merino {
107a18eacbeSJulio Merino     const int fd = open(name, O_RDONLY);
108a18eacbeSJulio Merino     ATF_REQUIRE_MSG(fd != -1, "Cannot open %s", name);
109a18eacbeSJulio Merino 
110a18eacbeSJulio Merino     char buffer[1024];
111a18eacbeSJulio Merino     ssize_t count;
112a18eacbeSJulio Merino     bool continued = false;
113a18eacbeSJulio Merino     while ((count = read(fd, buffer, sizeof(buffer) - 1)) > 0) {
114a18eacbeSJulio Merino         buffer[count] = '\0';
115a18eacbeSJulio Merino 
116a18eacbeSJulio Merino         if (!continued)
117a18eacbeSJulio Merino             printf("%s", prefix);
118a18eacbeSJulio Merino 
119a18eacbeSJulio Merino         char *iter = buffer;
120a18eacbeSJulio Merino         char *end;
121a18eacbeSJulio Merino         while ((end = strchr(iter, '\n')) != NULL) {
122a18eacbeSJulio Merino             *end = '\0';
123a18eacbeSJulio Merino             printf("%s\n", iter);
124a18eacbeSJulio Merino 
125a18eacbeSJulio Merino             iter = end + 1;
126a18eacbeSJulio Merino             if (iter != buffer + count)
127a18eacbeSJulio Merino                 printf("%s", prefix);
128a18eacbeSJulio Merino             else
129a18eacbeSJulio Merino                 continued = false;
130a18eacbeSJulio Merino         }
131a18eacbeSJulio Merino         if (iter < buffer + count) {
132a18eacbeSJulio Merino             printf("%s", iter);
133a18eacbeSJulio Merino             continued = true;
134a18eacbeSJulio Merino         }
135a18eacbeSJulio Merino     }
136a18eacbeSJulio Merino     ATF_REQUIRE(count == 0);
137a18eacbeSJulio Merino }
138a18eacbeSJulio Merino 
139a18eacbeSJulio Merino /** Compares a file against the given golden contents.
140a18eacbeSJulio Merino  *
141a18eacbeSJulio Merino  * \param name Name of the file to be compared.
142a18eacbeSJulio Merino  * \param contents Expected contents of the file.
143a18eacbeSJulio Merino  *
144a18eacbeSJulio Merino  * \return True if the file matches the contents; false otherwise. */
145a18eacbeSJulio Merino bool
atf_utils_compare_file(const char * name,const char * contents)146a18eacbeSJulio Merino atf_utils_compare_file(const char *name, const char *contents)
147a18eacbeSJulio Merino {
148a18eacbeSJulio Merino     const int fd = open(name, O_RDONLY);
149a18eacbeSJulio Merino     ATF_REQUIRE_MSG(fd != -1, "Cannot open %s", name);
150a18eacbeSJulio Merino 
151a18eacbeSJulio Merino     const char *pos = contents;
152a18eacbeSJulio Merino     ssize_t remaining = strlen(contents);
153a18eacbeSJulio Merino 
154a18eacbeSJulio Merino     char buffer[1024];
155a18eacbeSJulio Merino     ssize_t count;
156a18eacbeSJulio Merino     while ((count = read(fd, buffer, sizeof(buffer))) > 0 &&
157a18eacbeSJulio Merino            count <= remaining) {
158a18eacbeSJulio Merino         if (memcmp(pos, buffer, count) != 0) {
159a18eacbeSJulio Merino             close(fd);
160a18eacbeSJulio Merino             return false;
161a18eacbeSJulio Merino         }
162a18eacbeSJulio Merino         remaining -= count;
163a18eacbeSJulio Merino         pos += count;
164a18eacbeSJulio Merino     }
165a18eacbeSJulio Merino     close(fd);
166a18eacbeSJulio Merino     return count == 0 && remaining == 0;
167a18eacbeSJulio Merino }
168a18eacbeSJulio Merino 
169a18eacbeSJulio Merino /** Copies a file.
170a18eacbeSJulio Merino  *
171a18eacbeSJulio Merino  * \param source Path to the source file.
172a18eacbeSJulio Merino  * \param destination Path to the destination file. */
173a18eacbeSJulio Merino void
atf_utils_copy_file(const char * source,const char * destination)174a18eacbeSJulio Merino atf_utils_copy_file(const char *source, const char *destination)
175a18eacbeSJulio Merino {
176a18eacbeSJulio Merino     const int input = open(source, O_RDONLY);
177a18eacbeSJulio Merino     ATF_REQUIRE_MSG(input != -1, "Failed to open source file during "
178a18eacbeSJulio Merino                     "copy (%s)", source);
179a18eacbeSJulio Merino 
180a18eacbeSJulio Merino     const int output = open(destination, O_WRONLY | O_CREAT | O_TRUNC, 0777);
181a18eacbeSJulio Merino     ATF_REQUIRE_MSG(output != -1, "Failed to open destination file during "
182a18eacbeSJulio Merino                     "copy (%s)", destination);
183a18eacbeSJulio Merino 
184a18eacbeSJulio Merino     char buffer[1024];
185a18eacbeSJulio Merino     ssize_t length;
186a18eacbeSJulio Merino     while ((length = read(input, buffer, sizeof(buffer))) > 0)
187a18eacbeSJulio Merino         ATF_REQUIRE_MSG(write(output, buffer, length) == length,
188a18eacbeSJulio Merino                         "Failed to write to %s during copy", destination);
189a18eacbeSJulio Merino     ATF_REQUIRE_MSG(length != -1, "Failed to read from %s during copy", source);
190a18eacbeSJulio Merino 
191a18eacbeSJulio Merino     struct stat sb;
192a18eacbeSJulio Merino     ATF_REQUIRE_MSG(fstat(input, &sb) != -1,
193a18eacbeSJulio Merino                     "Failed to stat source file %s during copy", source);
194a18eacbeSJulio Merino     ATF_REQUIRE_MSG(fchmod(output, sb.st_mode) != -1,
195a18eacbeSJulio Merino                     "Failed to chmod destination file %s during copy",
196a18eacbeSJulio Merino                     destination);
197a18eacbeSJulio Merino 
198a18eacbeSJulio Merino     close(output);
199a18eacbeSJulio Merino     close(input);
200a18eacbeSJulio Merino }
201a18eacbeSJulio Merino 
202a18eacbeSJulio Merino /** Creates a file.
203a18eacbeSJulio Merino  *
204a18eacbeSJulio Merino  * \param name Name of the file to create.
205a18eacbeSJulio Merino  * \param contents Text to write into the created file.
206a18eacbeSJulio Merino  * \param ... Positional parameters to the contents. */
207a18eacbeSJulio Merino void
atf_utils_create_file(const char * name,const char * contents,...)208a18eacbeSJulio Merino atf_utils_create_file(const char *name, const char *contents, ...)
209a18eacbeSJulio Merino {
210a18eacbeSJulio Merino     va_list ap;
211a18eacbeSJulio Merino     atf_dynstr_t formatted;
212a18eacbeSJulio Merino     atf_error_t error;
213a18eacbeSJulio Merino 
214a18eacbeSJulio Merino     va_start(ap, contents);
215a18eacbeSJulio Merino     error = atf_dynstr_init_ap(&formatted, contents, ap);
216a18eacbeSJulio Merino     va_end(ap);
217a18eacbeSJulio Merino     ATF_REQUIRE(!atf_is_error(error));
218a18eacbeSJulio Merino 
219a18eacbeSJulio Merino     const int fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
220a18eacbeSJulio Merino     ATF_REQUIRE_MSG(fd != -1, "Cannot create file %s", name);
221a18eacbeSJulio Merino     ATF_REQUIRE(write(fd, atf_dynstr_cstring(&formatted),
222a18eacbeSJulio Merino                       atf_dynstr_length(&formatted)) != -1);
223a18eacbeSJulio Merino     close(fd);
224a18eacbeSJulio Merino 
225a18eacbeSJulio Merino     atf_dynstr_fini(&formatted);
226a18eacbeSJulio Merino }
227a18eacbeSJulio Merino 
228a18eacbeSJulio Merino /** Checks if a file exists.
229a18eacbeSJulio Merino  *
230a18eacbeSJulio Merino  * \param path Location of the file to check for.
231a18eacbeSJulio Merino  *
232a18eacbeSJulio Merino  * \return True if the file exists, false otherwise. */
233a18eacbeSJulio Merino bool
atf_utils_file_exists(const char * path)234a18eacbeSJulio Merino atf_utils_file_exists(const char *path)
235a18eacbeSJulio Merino {
236a18eacbeSJulio Merino     const int ret = access(path, F_OK);
237a18eacbeSJulio Merino     if (ret == -1) {
238a18eacbeSJulio Merino         if (errno != ENOENT)
239a18eacbeSJulio Merino             atf_tc_fail("Failed to check the existence of %s: %s", path,
240a18eacbeSJulio Merino                         strerror(errno));
241a18eacbeSJulio Merino         else
242a18eacbeSJulio Merino             return false;
243a18eacbeSJulio Merino     } else
244a18eacbeSJulio Merino         return true;
245a18eacbeSJulio Merino }
246a18eacbeSJulio Merino 
247a18eacbeSJulio Merino /** Spawns a subprocess and redirects its output to files.
248a18eacbeSJulio Merino  *
249a18eacbeSJulio Merino  * Use the atf_utils_wait() function to wait for the completion of the spawned
250a18eacbeSJulio Merino  * subprocess and validate its exit conditions.
251a18eacbeSJulio Merino  *
252a18eacbeSJulio Merino  * \return 0 in the new child; the PID of the new child in the parent.  Does
253a18eacbeSJulio Merino  * not return in error conditions. */
254a18eacbeSJulio Merino pid_t
atf_utils_fork(void)255a18eacbeSJulio Merino atf_utils_fork(void)
256a18eacbeSJulio Merino {
257a18eacbeSJulio Merino     const pid_t pid = fork();
258a18eacbeSJulio Merino     if (pid == -1)
259a18eacbeSJulio Merino         atf_tc_fail("fork failed");
260a18eacbeSJulio Merino 
261a18eacbeSJulio Merino     if (pid == 0) {
2620677dfd1SJulio Merino         atf_dynstr_t out_name;
2630677dfd1SJulio Merino         init_out_filename(&out_name, getpid(), "out", false);
2640677dfd1SJulio Merino 
2650677dfd1SJulio Merino         atf_dynstr_t err_name;
2660677dfd1SJulio Merino         init_out_filename(&err_name, getpid(), "err", false);
2670677dfd1SJulio Merino 
2680677dfd1SJulio Merino         atf_utils_redirect(STDOUT_FILENO, atf_dynstr_cstring(&out_name));
2690677dfd1SJulio Merino         atf_utils_redirect(STDERR_FILENO, atf_dynstr_cstring(&err_name));
2700677dfd1SJulio Merino 
2710677dfd1SJulio Merino         atf_dynstr_fini(&err_name);
2720677dfd1SJulio Merino         atf_dynstr_fini(&out_name);
273a18eacbeSJulio Merino     }
274a18eacbeSJulio Merino     return pid;
275a18eacbeSJulio Merino }
276a18eacbeSJulio Merino 
277*c203bd70SAlex Richardson void
atf_utils_reset_resultsfile(void)278*c203bd70SAlex Richardson atf_utils_reset_resultsfile(void)
279*c203bd70SAlex Richardson {
280*c203bd70SAlex Richardson 
281*c203bd70SAlex Richardson     atf_tc_set_resultsfile("/dev/null");
282*c203bd70SAlex Richardson }
283*c203bd70SAlex Richardson 
284a18eacbeSJulio Merino /** Frees an dynamically-allocated "argv" array.
285a18eacbeSJulio Merino  *
286a18eacbeSJulio Merino  * \param argv A dynamically-allocated array of dynamically-allocated
287a18eacbeSJulio Merino  *     strings. */
288c243e490SMarcel Moolenaar void
atf_utils_free_charpp(char ** argv)289c243e490SMarcel Moolenaar atf_utils_free_charpp(char **argv)
290c243e490SMarcel Moolenaar {
291c243e490SMarcel Moolenaar     char **ptr;
292c243e490SMarcel Moolenaar 
293c243e490SMarcel Moolenaar     for (ptr = argv; *ptr != NULL; ptr++)
294c243e490SMarcel Moolenaar         free(*ptr);
295c243e490SMarcel Moolenaar 
296c243e490SMarcel Moolenaar     free(argv);
297c243e490SMarcel Moolenaar }
298a18eacbeSJulio Merino 
299a18eacbeSJulio Merino /** Searches for a regexp in a file.
300a18eacbeSJulio Merino  *
301a18eacbeSJulio Merino  * \param regex The regexp to look for.
302a18eacbeSJulio Merino  * \param file The file in which to look for the expression.
303a18eacbeSJulio Merino  * \param ... Positional parameters to the regex.
304a18eacbeSJulio Merino  *
305a18eacbeSJulio Merino  * \return True if there is a match; false otherwise. */
306a18eacbeSJulio Merino bool
atf_utils_grep_file(const char * regex,const char * file,...)307a18eacbeSJulio Merino atf_utils_grep_file(const char *regex, const char *file, ...)
308a18eacbeSJulio Merino {
309a18eacbeSJulio Merino     int fd;
310a18eacbeSJulio Merino     va_list ap;
311a18eacbeSJulio Merino     atf_dynstr_t formatted;
312a18eacbeSJulio Merino     atf_error_t error;
313a18eacbeSJulio Merino 
314a18eacbeSJulio Merino     va_start(ap, file);
315a18eacbeSJulio Merino     error = atf_dynstr_init_ap(&formatted, regex, ap);
316a18eacbeSJulio Merino     va_end(ap);
317a18eacbeSJulio Merino     ATF_REQUIRE(!atf_is_error(error));
318a18eacbeSJulio Merino 
319a18eacbeSJulio Merino     ATF_REQUIRE((fd = open(file, O_RDONLY)) != -1);
320a18eacbeSJulio Merino     bool found = false;
321a18eacbeSJulio Merino     char *line = NULL;
322a18eacbeSJulio Merino     while (!found && (line = atf_utils_readline(fd)) != NULL) {
323a18eacbeSJulio Merino         found = grep_string(atf_dynstr_cstring(&formatted), line);
324a18eacbeSJulio Merino         free(line);
325a18eacbeSJulio Merino     }
326a18eacbeSJulio Merino     close(fd);
327a18eacbeSJulio Merino 
328a18eacbeSJulio Merino     atf_dynstr_fini(&formatted);
329a18eacbeSJulio Merino 
330a18eacbeSJulio Merino     return found;
331a18eacbeSJulio Merino }
332a18eacbeSJulio Merino 
333a18eacbeSJulio Merino /** Searches for a regexp in a string.
334a18eacbeSJulio Merino  *
335a18eacbeSJulio Merino  * \param regex The regexp to look for.
336a18eacbeSJulio Merino  * \param str The string in which to look for the expression.
337a18eacbeSJulio Merino  * \param ... Positional parameters to the regex.
338a18eacbeSJulio Merino  *
339a18eacbeSJulio Merino  * \return True if there is a match; false otherwise. */
340a18eacbeSJulio Merino bool
atf_utils_grep_string(const char * regex,const char * str,...)341a18eacbeSJulio Merino atf_utils_grep_string(const char *regex, const char *str, ...)
342a18eacbeSJulio Merino {
343a18eacbeSJulio Merino     bool res;
344a18eacbeSJulio Merino     va_list ap;
345a18eacbeSJulio Merino     atf_dynstr_t formatted;
346a18eacbeSJulio Merino     atf_error_t error;
347a18eacbeSJulio Merino 
348a18eacbeSJulio Merino     va_start(ap, str);
349a18eacbeSJulio Merino     error = atf_dynstr_init_ap(&formatted, regex, ap);
350a18eacbeSJulio Merino     va_end(ap);
351a18eacbeSJulio Merino     ATF_REQUIRE(!atf_is_error(error));
352a18eacbeSJulio Merino 
353a18eacbeSJulio Merino     res = grep_string(atf_dynstr_cstring(&formatted), str);
354a18eacbeSJulio Merino 
355a18eacbeSJulio Merino     atf_dynstr_fini(&formatted);
356a18eacbeSJulio Merino 
357a18eacbeSJulio Merino     return res;
358a18eacbeSJulio Merino }
359a18eacbeSJulio Merino 
360a18eacbeSJulio Merino /** Reads a line of arbitrary length.
361a18eacbeSJulio Merino  *
362a18eacbeSJulio Merino  * \param fd The descriptor from which to read the line.
363a18eacbeSJulio Merino  *
364a18eacbeSJulio Merino  * \return A pointer to the read line, which must be released with free(), or
365a18eacbeSJulio Merino  * NULL if there was nothing to read from the file. */
366a18eacbeSJulio Merino char *
atf_utils_readline(const int fd)367a18eacbeSJulio Merino atf_utils_readline(const int fd)
368a18eacbeSJulio Merino {
369a18eacbeSJulio Merino     char ch;
370a18eacbeSJulio Merino     ssize_t cnt;
371a18eacbeSJulio Merino     atf_dynstr_t temp;
372a18eacbeSJulio Merino     atf_error_t error;
373a18eacbeSJulio Merino 
374a18eacbeSJulio Merino     error = atf_dynstr_init(&temp);
375a18eacbeSJulio Merino     ATF_REQUIRE(!atf_is_error(error));
376a18eacbeSJulio Merino 
377a18eacbeSJulio Merino     while ((cnt = read(fd, &ch, sizeof(ch))) == sizeof(ch) &&
378a18eacbeSJulio Merino            ch != '\n') {
379a18eacbeSJulio Merino         error = atf_dynstr_append_fmt(&temp, "%c", ch);
380a18eacbeSJulio Merino         ATF_REQUIRE(!atf_is_error(error));
381a18eacbeSJulio Merino     }
382a18eacbeSJulio Merino     ATF_REQUIRE(cnt != -1);
383a18eacbeSJulio Merino 
384a18eacbeSJulio Merino     if (cnt == 0 && atf_dynstr_length(&temp) == 0) {
385a18eacbeSJulio Merino         atf_dynstr_fini(&temp);
386a18eacbeSJulio Merino         return NULL;
387a18eacbeSJulio Merino     } else
388a18eacbeSJulio Merino         return atf_dynstr_fini_disown(&temp);
389a18eacbeSJulio Merino }
390a18eacbeSJulio Merino 
391a18eacbeSJulio Merino /** Redirects a file descriptor to a file.
392a18eacbeSJulio Merino  *
393a18eacbeSJulio Merino  * \param target_fd The file descriptor to be replaced.
394a18eacbeSJulio Merino  * \param name The name of the file to direct the descriptor to.
395a18eacbeSJulio Merino  *
396a18eacbeSJulio Merino  * \pre Should only be called from the process spawned by fork_for_testing
397a18eacbeSJulio Merino  * because this exits uncontrolledly.
398a18eacbeSJulio Merino  * \post Terminates execution if the redirection fails. */
399a18eacbeSJulio Merino void
atf_utils_redirect(const int target_fd,const char * name)400a18eacbeSJulio Merino atf_utils_redirect(const int target_fd, const char *name)
401a18eacbeSJulio Merino {
402a18eacbeSJulio Merino     if (target_fd == STDOUT_FILENO)
403a18eacbeSJulio Merino         fflush(stdout);
404a18eacbeSJulio Merino     else if (target_fd == STDERR_FILENO)
405a18eacbeSJulio Merino         fflush(stderr);
406a18eacbeSJulio Merino 
407a18eacbeSJulio Merino     const int new_fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
408a18eacbeSJulio Merino     if (new_fd == -1)
409a18eacbeSJulio Merino         err(EXIT_FAILURE, "Cannot create %s", name);
410a18eacbeSJulio Merino     if (new_fd != target_fd) {
411a18eacbeSJulio Merino         if (dup2(new_fd, target_fd) == -1)
412a18eacbeSJulio Merino             err(EXIT_FAILURE, "Cannot redirect to fd %d", target_fd);
413a18eacbeSJulio Merino     }
414a18eacbeSJulio Merino     close(new_fd);
415a18eacbeSJulio Merino }
416a18eacbeSJulio Merino 
417a18eacbeSJulio Merino /** Waits for a subprocess and validates its exit condition.
418a18eacbeSJulio Merino  *
419a18eacbeSJulio Merino  * \param pid The process to be waited for.  Must have been started by
420a18eacbeSJulio Merino  *     testutils_fork().
421a18eacbeSJulio Merino  * \param exitstatus Expected exit status.
422a18eacbeSJulio Merino  * \param expout Expected contents of stdout.
423a18eacbeSJulio Merino  * \param experr Expected contents of stderr. */
424a18eacbeSJulio Merino void
atf_utils_wait(const pid_t pid,const int exitstatus,const char * expout,const char * experr)425a18eacbeSJulio Merino atf_utils_wait(const pid_t pid, const int exitstatus, const char *expout,
426a18eacbeSJulio Merino                const char *experr)
427a18eacbeSJulio Merino {
428a18eacbeSJulio Merino     int status;
429a18eacbeSJulio Merino     ATF_REQUIRE(waitpid(pid, &status, 0) != -1);
430a18eacbeSJulio Merino 
4310677dfd1SJulio Merino     atf_dynstr_t out_name;
4320677dfd1SJulio Merino     init_out_filename(&out_name, pid, "out", true);
4330677dfd1SJulio Merino 
4340677dfd1SJulio Merino     atf_dynstr_t err_name;
4350677dfd1SJulio Merino     init_out_filename(&err_name, pid, "err", true);
4360677dfd1SJulio Merino 
4370677dfd1SJulio Merino     atf_utils_cat_file(atf_dynstr_cstring(&out_name), "subprocess stdout: ");
4380677dfd1SJulio Merino     atf_utils_cat_file(atf_dynstr_cstring(&err_name), "subprocess stderr: ");
439a18eacbeSJulio Merino 
440a18eacbeSJulio Merino     ATF_REQUIRE(WIFEXITED(status));
441a18eacbeSJulio Merino     ATF_REQUIRE_EQ(exitstatus, WEXITSTATUS(status));
442a18eacbeSJulio Merino 
443a18eacbeSJulio Merino     const char *save_prefix = "save:";
444a18eacbeSJulio Merino     const size_t save_prefix_length = strlen(save_prefix);
445a18eacbeSJulio Merino 
446a18eacbeSJulio Merino     if (strlen(expout) > save_prefix_length &&
447a18eacbeSJulio Merino         strncmp(expout, save_prefix, save_prefix_length) == 0) {
4480677dfd1SJulio Merino         atf_utils_copy_file(atf_dynstr_cstring(&out_name),
449a18eacbeSJulio Merino                             expout + save_prefix_length);
450a18eacbeSJulio Merino     } else {
4510677dfd1SJulio Merino         ATF_REQUIRE(atf_utils_compare_file(atf_dynstr_cstring(&out_name),
4520677dfd1SJulio Merino                                            expout));
453a18eacbeSJulio Merino     }
454a18eacbeSJulio Merino 
455a18eacbeSJulio Merino     if (strlen(experr) > save_prefix_length &&
456a18eacbeSJulio Merino         strncmp(experr, save_prefix, save_prefix_length) == 0) {
4570677dfd1SJulio Merino         atf_utils_copy_file(atf_dynstr_cstring(&err_name),
458a18eacbeSJulio Merino                             experr + save_prefix_length);
459a18eacbeSJulio Merino     } else {
4600677dfd1SJulio Merino         ATF_REQUIRE(atf_utils_compare_file(atf_dynstr_cstring(&err_name),
4610677dfd1SJulio Merino                                            experr));
462a18eacbeSJulio Merino     }
463a18eacbeSJulio Merino 
4640677dfd1SJulio Merino     ATF_REQUIRE(unlink(atf_dynstr_cstring(&out_name)) != -1);
4650677dfd1SJulio Merino     ATF_REQUIRE(unlink(atf_dynstr_cstring(&err_name)) != -1);
466a18eacbeSJulio Merino }
467