1*00b67f09SDavid van Moolenbroek /* $NetBSD: utils.c,v 1.3 2014/12/10 04:38:03 christos Exp $ */
2*00b67f09SDavid van Moolenbroek
3*00b67f09SDavid van Moolenbroek /*
4*00b67f09SDavid van Moolenbroek * Automated Testing Framework (atf)
5*00b67f09SDavid van Moolenbroek *
6*00b67f09SDavid van Moolenbroek * Copyright (c) 2010 The NetBSD Foundation, Inc.
7*00b67f09SDavid van Moolenbroek * All rights reserved.
8*00b67f09SDavid van Moolenbroek *
9*00b67f09SDavid van Moolenbroek * Redistribution and use in source and binary forms, with or without
10*00b67f09SDavid van Moolenbroek * modification, are permitted provided that the following conditions
11*00b67f09SDavid van Moolenbroek * are met:
12*00b67f09SDavid van Moolenbroek * 1. Redistributions of source code must retain the above copyright
13*00b67f09SDavid van Moolenbroek * notice, this list of conditions and the following disclaimer.
14*00b67f09SDavid van Moolenbroek * 2. Redistributions in binary form must reproduce the above copyright
15*00b67f09SDavid van Moolenbroek * notice, this list of conditions and the following disclaimer in the
16*00b67f09SDavid van Moolenbroek * documentation and/or other materials provided with the distribution.
17*00b67f09SDavid van Moolenbroek *
18*00b67f09SDavid van Moolenbroek * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
19*00b67f09SDavid van Moolenbroek * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
20*00b67f09SDavid van Moolenbroek * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21*00b67f09SDavid van Moolenbroek * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22*00b67f09SDavid van Moolenbroek * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
23*00b67f09SDavid van Moolenbroek * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24*00b67f09SDavid van Moolenbroek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25*00b67f09SDavid van Moolenbroek * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26*00b67f09SDavid van Moolenbroek * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27*00b67f09SDavid van Moolenbroek * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28*00b67f09SDavid van Moolenbroek * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
29*00b67f09SDavid van Moolenbroek * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30*00b67f09SDavid van Moolenbroek */
31*00b67f09SDavid van Moolenbroek
32*00b67f09SDavid van Moolenbroek #include "atf-c/utils.h"
33*00b67f09SDavid van Moolenbroek
34*00b67f09SDavid van Moolenbroek #include <sys/stat.h>
35*00b67f09SDavid van Moolenbroek #include <sys/wait.h>
36*00b67f09SDavid van Moolenbroek
37*00b67f09SDavid van Moolenbroek #include <err.h>
38*00b67f09SDavid van Moolenbroek #include <errno.h>
39*00b67f09SDavid van Moolenbroek #include <fcntl.h>
40*00b67f09SDavid van Moolenbroek #include <regex.h>
41*00b67f09SDavid van Moolenbroek #include <stdio.h>
42*00b67f09SDavid van Moolenbroek #include <stdlib.h>
43*00b67f09SDavid van Moolenbroek #include <string.h>
44*00b67f09SDavid van Moolenbroek #include <unistd.h>
45*00b67f09SDavid van Moolenbroek
46*00b67f09SDavid van Moolenbroek #include <atf-c.h>
47*00b67f09SDavid van Moolenbroek
48*00b67f09SDavid van Moolenbroek #include "detail/dynstr.h"
49*00b67f09SDavid van Moolenbroek
50*00b67f09SDavid van Moolenbroek /** Searches for a regexp in a string.
51*00b67f09SDavid van Moolenbroek *
52*00b67f09SDavid van Moolenbroek * \param regex The regexp to look for.
53*00b67f09SDavid van Moolenbroek * \param str The string in which to look for the expression.
54*00b67f09SDavid van Moolenbroek *
55*00b67f09SDavid van Moolenbroek * \return True if there is a match; false otherwise. */
56*00b67f09SDavid van Moolenbroek static
57*00b67f09SDavid van Moolenbroek bool
grep_string(const char * regex,const char * str)58*00b67f09SDavid van Moolenbroek grep_string(const char *regex, const char *str)
59*00b67f09SDavid van Moolenbroek {
60*00b67f09SDavid van Moolenbroek int res;
61*00b67f09SDavid van Moolenbroek regex_t preg;
62*00b67f09SDavid van Moolenbroek
63*00b67f09SDavid van Moolenbroek printf("Looking for '%s' in '%s'\n", regex, str);
64*00b67f09SDavid van Moolenbroek ATF_REQUIRE(regcomp(&preg, regex, REG_EXTENDED) == 0);
65*00b67f09SDavid van Moolenbroek
66*00b67f09SDavid van Moolenbroek res = regexec(&preg, str, 0, NULL, 0);
67*00b67f09SDavid van Moolenbroek ATF_REQUIRE(res == 0 || res == REG_NOMATCH);
68*00b67f09SDavid van Moolenbroek
69*00b67f09SDavid van Moolenbroek regfree(&preg);
70*00b67f09SDavid van Moolenbroek
71*00b67f09SDavid van Moolenbroek return res == 0;
72*00b67f09SDavid van Moolenbroek }
73*00b67f09SDavid van Moolenbroek
74*00b67f09SDavid van Moolenbroek /** Prints the contents of a file to stdout.
75*00b67f09SDavid van Moolenbroek *
76*00b67f09SDavid van Moolenbroek * \param name The name of the file to be printed.
77*00b67f09SDavid van Moolenbroek * \param prefix An string to be prepended to every line of the printed
78*00b67f09SDavid van Moolenbroek * file. */
79*00b67f09SDavid van Moolenbroek void
atf_utils_cat_file(const char * name,const char * prefix)80*00b67f09SDavid van Moolenbroek atf_utils_cat_file(const char *name, const char *prefix)
81*00b67f09SDavid van Moolenbroek {
82*00b67f09SDavid van Moolenbroek const int fd = open(name, O_RDONLY);
83*00b67f09SDavid van Moolenbroek ATF_REQUIRE_MSG(fd != -1, "Cannot open %s", name);
84*00b67f09SDavid van Moolenbroek
85*00b67f09SDavid van Moolenbroek char buffer[1024];
86*00b67f09SDavid van Moolenbroek ssize_t count;
87*00b67f09SDavid van Moolenbroek bool continued = false;
88*00b67f09SDavid van Moolenbroek while ((count = read(fd, buffer, sizeof(buffer) - 1)) > 0) {
89*00b67f09SDavid van Moolenbroek buffer[count] = '\0';
90*00b67f09SDavid van Moolenbroek
91*00b67f09SDavid van Moolenbroek if (!continued)
92*00b67f09SDavid van Moolenbroek printf("%s", prefix);
93*00b67f09SDavid van Moolenbroek
94*00b67f09SDavid van Moolenbroek char *iter = buffer;
95*00b67f09SDavid van Moolenbroek char *end;
96*00b67f09SDavid van Moolenbroek while ((end = strchr(iter, '\n')) != NULL) {
97*00b67f09SDavid van Moolenbroek *end = '\0';
98*00b67f09SDavid van Moolenbroek printf("%s\n", iter);
99*00b67f09SDavid van Moolenbroek
100*00b67f09SDavid van Moolenbroek iter = end + 1;
101*00b67f09SDavid van Moolenbroek if (iter != buffer + count)
102*00b67f09SDavid van Moolenbroek printf("%s", prefix);
103*00b67f09SDavid van Moolenbroek else
104*00b67f09SDavid van Moolenbroek continued = false;
105*00b67f09SDavid van Moolenbroek }
106*00b67f09SDavid van Moolenbroek if (iter < buffer + count) {
107*00b67f09SDavid van Moolenbroek printf("%s", iter);
108*00b67f09SDavid van Moolenbroek continued = true;
109*00b67f09SDavid van Moolenbroek }
110*00b67f09SDavid van Moolenbroek }
111*00b67f09SDavid van Moolenbroek ATF_REQUIRE(count == 0);
112*00b67f09SDavid van Moolenbroek }
113*00b67f09SDavid van Moolenbroek
114*00b67f09SDavid van Moolenbroek /** Compares a file against the given golden contents.
115*00b67f09SDavid van Moolenbroek *
116*00b67f09SDavid van Moolenbroek * \param name Name of the file to be compared.
117*00b67f09SDavid van Moolenbroek * \param contents Expected contents of the file.
118*00b67f09SDavid van Moolenbroek *
119*00b67f09SDavid van Moolenbroek * \return True if the file matches the contents; false otherwise. */
120*00b67f09SDavid van Moolenbroek bool
atf_utils_compare_file(const char * name,const char * contents)121*00b67f09SDavid van Moolenbroek atf_utils_compare_file(const char *name, const char *contents)
122*00b67f09SDavid van Moolenbroek {
123*00b67f09SDavid van Moolenbroek const int fd = open(name, O_RDONLY);
124*00b67f09SDavid van Moolenbroek ATF_REQUIRE_MSG(fd != -1, "Cannot open %s", name);
125*00b67f09SDavid van Moolenbroek
126*00b67f09SDavid van Moolenbroek const char *pos = contents;
127*00b67f09SDavid van Moolenbroek ssize_t remaining = strlen(contents);
128*00b67f09SDavid van Moolenbroek
129*00b67f09SDavid van Moolenbroek char buffer[1024];
130*00b67f09SDavid van Moolenbroek ssize_t count;
131*00b67f09SDavid van Moolenbroek while ((count = read(fd, buffer, sizeof(buffer))) > 0 &&
132*00b67f09SDavid van Moolenbroek count <= remaining) {
133*00b67f09SDavid van Moolenbroek if (memcmp(pos, buffer, count) != 0) {
134*00b67f09SDavid van Moolenbroek close(fd);
135*00b67f09SDavid van Moolenbroek return false;
136*00b67f09SDavid van Moolenbroek }
137*00b67f09SDavid van Moolenbroek remaining -= count;
138*00b67f09SDavid van Moolenbroek pos += count;
139*00b67f09SDavid van Moolenbroek }
140*00b67f09SDavid van Moolenbroek close(fd);
141*00b67f09SDavid van Moolenbroek return count == 0 && remaining == 0;
142*00b67f09SDavid van Moolenbroek }
143*00b67f09SDavid van Moolenbroek
144*00b67f09SDavid van Moolenbroek /** Copies a file.
145*00b67f09SDavid van Moolenbroek *
146*00b67f09SDavid van Moolenbroek * \param source Path to the source file.
147*00b67f09SDavid van Moolenbroek * \param destination Path to the destination file. */
148*00b67f09SDavid van Moolenbroek void
atf_utils_copy_file(const char * source,const char * destination)149*00b67f09SDavid van Moolenbroek atf_utils_copy_file(const char *source, const char *destination)
150*00b67f09SDavid van Moolenbroek {
151*00b67f09SDavid van Moolenbroek const int input = open(source, O_RDONLY);
152*00b67f09SDavid van Moolenbroek ATF_REQUIRE_MSG(input != -1, "Failed to open source file during "
153*00b67f09SDavid van Moolenbroek "copy (%s)", source);
154*00b67f09SDavid van Moolenbroek
155*00b67f09SDavid van Moolenbroek const int output = open(destination, O_WRONLY | O_CREAT | O_TRUNC, 0777);
156*00b67f09SDavid van Moolenbroek ATF_REQUIRE_MSG(output != -1, "Failed to open destination file during "
157*00b67f09SDavid van Moolenbroek "copy (%s)", destination);
158*00b67f09SDavid van Moolenbroek
159*00b67f09SDavid van Moolenbroek char buffer[1024];
160*00b67f09SDavid van Moolenbroek ssize_t length;
161*00b67f09SDavid van Moolenbroek while ((length = read(input, buffer, sizeof(buffer))) > 0)
162*00b67f09SDavid van Moolenbroek ATF_REQUIRE_MSG(write(output, buffer, length) == length,
163*00b67f09SDavid van Moolenbroek "Failed to write to %s during copy", destination);
164*00b67f09SDavid van Moolenbroek ATF_REQUIRE_MSG(length != -1, "Failed to read from %s during copy", source);
165*00b67f09SDavid van Moolenbroek
166*00b67f09SDavid van Moolenbroek struct stat sb;
167*00b67f09SDavid van Moolenbroek ATF_REQUIRE_MSG(fstat(input, &sb) != -1,
168*00b67f09SDavid van Moolenbroek "Failed to stat source file %s during copy", source);
169*00b67f09SDavid van Moolenbroek ATF_REQUIRE_MSG(fchmod(output, sb.st_mode) != -1,
170*00b67f09SDavid van Moolenbroek "Failed to chmod destination file %s during copy",
171*00b67f09SDavid van Moolenbroek destination);
172*00b67f09SDavid van Moolenbroek
173*00b67f09SDavid van Moolenbroek close(output);
174*00b67f09SDavid van Moolenbroek close(input);
175*00b67f09SDavid van Moolenbroek }
176*00b67f09SDavid van Moolenbroek
177*00b67f09SDavid van Moolenbroek /** Creates a file.
178*00b67f09SDavid van Moolenbroek *
179*00b67f09SDavid van Moolenbroek * \param name Name of the file to create.
180*00b67f09SDavid van Moolenbroek * \param contents Text to write into the created file.
181*00b67f09SDavid van Moolenbroek * \param ... Positional parameters to the contents. */
182*00b67f09SDavid van Moolenbroek void
atf_utils_create_file(const char * name,const char * contents,...)183*00b67f09SDavid van Moolenbroek atf_utils_create_file(const char *name, const char *contents, ...)
184*00b67f09SDavid van Moolenbroek {
185*00b67f09SDavid van Moolenbroek va_list ap;
186*00b67f09SDavid van Moolenbroek atf_dynstr_t formatted;
187*00b67f09SDavid van Moolenbroek atf_error_t error;
188*00b67f09SDavid van Moolenbroek
189*00b67f09SDavid van Moolenbroek va_start(ap, contents);
190*00b67f09SDavid van Moolenbroek error = atf_dynstr_init_ap(&formatted, contents, ap);
191*00b67f09SDavid van Moolenbroek va_end(ap);
192*00b67f09SDavid van Moolenbroek ATF_REQUIRE(!atf_is_error(error));
193*00b67f09SDavid van Moolenbroek
194*00b67f09SDavid van Moolenbroek const int fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
195*00b67f09SDavid van Moolenbroek ATF_REQUIRE_MSG(fd != -1, "Cannot create file %s", name);
196*00b67f09SDavid van Moolenbroek ATF_REQUIRE(write(fd, atf_dynstr_cstring(&formatted),
197*00b67f09SDavid van Moolenbroek atf_dynstr_length(&formatted)) != -1);
198*00b67f09SDavid van Moolenbroek close(fd);
199*00b67f09SDavid van Moolenbroek
200*00b67f09SDavid van Moolenbroek atf_dynstr_fini(&formatted);
201*00b67f09SDavid van Moolenbroek }
202*00b67f09SDavid van Moolenbroek
203*00b67f09SDavid van Moolenbroek /** Checks if a file exists.
204*00b67f09SDavid van Moolenbroek *
205*00b67f09SDavid van Moolenbroek * \param path Location of the file to check for.
206*00b67f09SDavid van Moolenbroek *
207*00b67f09SDavid van Moolenbroek * \return True if the file exists, false otherwise. */
208*00b67f09SDavid van Moolenbroek bool
atf_utils_file_exists(const char * path)209*00b67f09SDavid van Moolenbroek atf_utils_file_exists(const char *path)
210*00b67f09SDavid van Moolenbroek {
211*00b67f09SDavid van Moolenbroek const int ret = access(path, F_OK);
212*00b67f09SDavid van Moolenbroek if (ret == -1) {
213*00b67f09SDavid van Moolenbroek if (errno != ENOENT)
214*00b67f09SDavid van Moolenbroek atf_tc_fail("Failed to check the existence of %s: %s", path,
215*00b67f09SDavid van Moolenbroek strerror(errno));
216*00b67f09SDavid van Moolenbroek else
217*00b67f09SDavid van Moolenbroek return false;
218*00b67f09SDavid van Moolenbroek } else
219*00b67f09SDavid van Moolenbroek return true;
220*00b67f09SDavid van Moolenbroek }
221*00b67f09SDavid van Moolenbroek
222*00b67f09SDavid van Moolenbroek /** Spawns a subprocess and redirects its output to files.
223*00b67f09SDavid van Moolenbroek *
224*00b67f09SDavid van Moolenbroek * Use the atf_utils_wait() function to wait for the completion of the spawned
225*00b67f09SDavid van Moolenbroek * subprocess and validate its exit conditions.
226*00b67f09SDavid van Moolenbroek *
227*00b67f09SDavid van Moolenbroek * \return 0 in the new child; the PID of the new child in the parent. Does
228*00b67f09SDavid van Moolenbroek * not return in error conditions. */
229*00b67f09SDavid van Moolenbroek pid_t
atf_utils_fork(void)230*00b67f09SDavid van Moolenbroek atf_utils_fork(void)
231*00b67f09SDavid van Moolenbroek {
232*00b67f09SDavid van Moolenbroek const pid_t pid = fork();
233*00b67f09SDavid van Moolenbroek if (pid == -1)
234*00b67f09SDavid van Moolenbroek atf_tc_fail("fork failed");
235*00b67f09SDavid van Moolenbroek
236*00b67f09SDavid van Moolenbroek if (pid == 0) {
237*00b67f09SDavid van Moolenbroek atf_utils_redirect(STDOUT_FILENO, "atf_utils_fork_out.txt");
238*00b67f09SDavid van Moolenbroek atf_utils_redirect(STDERR_FILENO, "atf_utils_fork_err.txt");
239*00b67f09SDavid van Moolenbroek }
240*00b67f09SDavid van Moolenbroek return pid;
241*00b67f09SDavid van Moolenbroek }
242*00b67f09SDavid van Moolenbroek
243*00b67f09SDavid van Moolenbroek /** Frees an dynamically-allocated "argv" array.
244*00b67f09SDavid van Moolenbroek *
245*00b67f09SDavid van Moolenbroek * \param argv A dynamically-allocated array of dynamically-allocated
246*00b67f09SDavid van Moolenbroek * strings. */
247*00b67f09SDavid van Moolenbroek void
atf_utils_free_charpp(char ** argv)248*00b67f09SDavid van Moolenbroek atf_utils_free_charpp(char **argv)
249*00b67f09SDavid van Moolenbroek {
250*00b67f09SDavid van Moolenbroek char **ptr;
251*00b67f09SDavid van Moolenbroek
252*00b67f09SDavid van Moolenbroek for (ptr = argv; *ptr != NULL; ptr++)
253*00b67f09SDavid van Moolenbroek free(*ptr);
254*00b67f09SDavid van Moolenbroek
255*00b67f09SDavid van Moolenbroek free(argv);
256*00b67f09SDavid van Moolenbroek }
257*00b67f09SDavid van Moolenbroek
258*00b67f09SDavid van Moolenbroek /** Searches for a regexp in a file.
259*00b67f09SDavid van Moolenbroek *
260*00b67f09SDavid van Moolenbroek * \param regex The regexp to look for.
261*00b67f09SDavid van Moolenbroek * \param file The file in which to look for the expression.
262*00b67f09SDavid van Moolenbroek * \param ... Positional parameters to the regex.
263*00b67f09SDavid van Moolenbroek *
264*00b67f09SDavid van Moolenbroek * \return True if there is a match; false otherwise. */
265*00b67f09SDavid van Moolenbroek bool
atf_utils_grep_file(const char * regex,const char * file,...)266*00b67f09SDavid van Moolenbroek atf_utils_grep_file(const char *regex, const char *file, ...)
267*00b67f09SDavid van Moolenbroek {
268*00b67f09SDavid van Moolenbroek int fd;
269*00b67f09SDavid van Moolenbroek va_list ap;
270*00b67f09SDavid van Moolenbroek atf_dynstr_t formatted;
271*00b67f09SDavid van Moolenbroek atf_error_t error;
272*00b67f09SDavid van Moolenbroek
273*00b67f09SDavid van Moolenbroek va_start(ap, file);
274*00b67f09SDavid van Moolenbroek error = atf_dynstr_init_ap(&formatted, regex, ap);
275*00b67f09SDavid van Moolenbroek va_end(ap);
276*00b67f09SDavid van Moolenbroek ATF_REQUIRE(!atf_is_error(error));
277*00b67f09SDavid van Moolenbroek
278*00b67f09SDavid van Moolenbroek ATF_REQUIRE((fd = open(file, O_RDONLY)) != -1);
279*00b67f09SDavid van Moolenbroek bool found = false;
280*00b67f09SDavid van Moolenbroek char *line = NULL;
281*00b67f09SDavid van Moolenbroek while (!found && (line = atf_utils_readline(fd)) != NULL) {
282*00b67f09SDavid van Moolenbroek found = grep_string(atf_dynstr_cstring(&formatted), line);
283*00b67f09SDavid van Moolenbroek free(line);
284*00b67f09SDavid van Moolenbroek }
285*00b67f09SDavid van Moolenbroek close(fd);
286*00b67f09SDavid van Moolenbroek
287*00b67f09SDavid van Moolenbroek atf_dynstr_fini(&formatted);
288*00b67f09SDavid van Moolenbroek
289*00b67f09SDavid van Moolenbroek return found;
290*00b67f09SDavid van Moolenbroek }
291*00b67f09SDavid van Moolenbroek
292*00b67f09SDavid van Moolenbroek /** Searches for a regexp in a string.
293*00b67f09SDavid van Moolenbroek *
294*00b67f09SDavid van Moolenbroek * \param regex The regexp to look for.
295*00b67f09SDavid van Moolenbroek * \param str The string in which to look for the expression.
296*00b67f09SDavid van Moolenbroek * \param ... Positional parameters to the regex.
297*00b67f09SDavid van Moolenbroek *
298*00b67f09SDavid van Moolenbroek * \return True if there is a match; false otherwise. */
299*00b67f09SDavid van Moolenbroek bool
atf_utils_grep_string(const char * regex,const char * str,...)300*00b67f09SDavid van Moolenbroek atf_utils_grep_string(const char *regex, const char *str, ...)
301*00b67f09SDavid van Moolenbroek {
302*00b67f09SDavid van Moolenbroek bool res;
303*00b67f09SDavid van Moolenbroek va_list ap;
304*00b67f09SDavid van Moolenbroek atf_dynstr_t formatted;
305*00b67f09SDavid van Moolenbroek atf_error_t error;
306*00b67f09SDavid van Moolenbroek
307*00b67f09SDavid van Moolenbroek va_start(ap, str);
308*00b67f09SDavid van Moolenbroek error = atf_dynstr_init_ap(&formatted, regex, ap);
309*00b67f09SDavid van Moolenbroek va_end(ap);
310*00b67f09SDavid van Moolenbroek ATF_REQUIRE(!atf_is_error(error));
311*00b67f09SDavid van Moolenbroek
312*00b67f09SDavid van Moolenbroek res = grep_string(atf_dynstr_cstring(&formatted), str);
313*00b67f09SDavid van Moolenbroek
314*00b67f09SDavid van Moolenbroek atf_dynstr_fini(&formatted);
315*00b67f09SDavid van Moolenbroek
316*00b67f09SDavid van Moolenbroek return res;
317*00b67f09SDavid van Moolenbroek }
318*00b67f09SDavid van Moolenbroek
319*00b67f09SDavid van Moolenbroek /** Reads a line of arbitrary length.
320*00b67f09SDavid van Moolenbroek *
321*00b67f09SDavid van Moolenbroek * \param fd The descriptor from which to read the line.
322*00b67f09SDavid van Moolenbroek *
323*00b67f09SDavid van Moolenbroek * \return A pointer to the read line, which must be released with free(), or
324*00b67f09SDavid van Moolenbroek * NULL if there was nothing to read from the file. */
325*00b67f09SDavid van Moolenbroek char *
atf_utils_readline(const int fd)326*00b67f09SDavid van Moolenbroek atf_utils_readline(const int fd)
327*00b67f09SDavid van Moolenbroek {
328*00b67f09SDavid van Moolenbroek char ch;
329*00b67f09SDavid van Moolenbroek ssize_t cnt;
330*00b67f09SDavid van Moolenbroek atf_dynstr_t temp;
331*00b67f09SDavid van Moolenbroek atf_error_t error;
332*00b67f09SDavid van Moolenbroek
333*00b67f09SDavid van Moolenbroek error = atf_dynstr_init(&temp);
334*00b67f09SDavid van Moolenbroek ATF_REQUIRE(!atf_is_error(error));
335*00b67f09SDavid van Moolenbroek
336*00b67f09SDavid van Moolenbroek while ((cnt = read(fd, &ch, sizeof(ch))) == sizeof(ch) &&
337*00b67f09SDavid van Moolenbroek ch != '\n') {
338*00b67f09SDavid van Moolenbroek error = atf_dynstr_append_fmt(&temp, "%c", ch);
339*00b67f09SDavid van Moolenbroek ATF_REQUIRE(!atf_is_error(error));
340*00b67f09SDavid van Moolenbroek }
341*00b67f09SDavid van Moolenbroek ATF_REQUIRE(cnt != -1);
342*00b67f09SDavid van Moolenbroek
343*00b67f09SDavid van Moolenbroek if (cnt == 0 && atf_dynstr_length(&temp) == 0) {
344*00b67f09SDavid van Moolenbroek atf_dynstr_fini(&temp);
345*00b67f09SDavid van Moolenbroek return NULL;
346*00b67f09SDavid van Moolenbroek } else
347*00b67f09SDavid van Moolenbroek return atf_dynstr_fini_disown(&temp);
348*00b67f09SDavid van Moolenbroek }
349*00b67f09SDavid van Moolenbroek
350*00b67f09SDavid van Moolenbroek /** Redirects a file descriptor to a file.
351*00b67f09SDavid van Moolenbroek *
352*00b67f09SDavid van Moolenbroek * \param target_fd The file descriptor to be replaced.
353*00b67f09SDavid van Moolenbroek * \param name The name of the file to direct the descriptor to.
354*00b67f09SDavid van Moolenbroek *
355*00b67f09SDavid van Moolenbroek * \pre Should only be called from the process spawned by fork_for_testing
356*00b67f09SDavid van Moolenbroek * because this exits uncontrolledly.
357*00b67f09SDavid van Moolenbroek * \post Terminates execution if the redirection fails. */
358*00b67f09SDavid van Moolenbroek void
atf_utils_redirect(const int target_fd,const char * name)359*00b67f09SDavid van Moolenbroek atf_utils_redirect(const int target_fd, const char *name)
360*00b67f09SDavid van Moolenbroek {
361*00b67f09SDavid van Moolenbroek if (target_fd == STDOUT_FILENO)
362*00b67f09SDavid van Moolenbroek fflush(stdout);
363*00b67f09SDavid van Moolenbroek else if (target_fd == STDERR_FILENO)
364*00b67f09SDavid van Moolenbroek fflush(stderr);
365*00b67f09SDavid van Moolenbroek
366*00b67f09SDavid van Moolenbroek const int new_fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
367*00b67f09SDavid van Moolenbroek if (new_fd == -1)
368*00b67f09SDavid van Moolenbroek err(EXIT_FAILURE, "Cannot create %s", name);
369*00b67f09SDavid van Moolenbroek if (new_fd != target_fd) {
370*00b67f09SDavid van Moolenbroek if (dup2(new_fd, target_fd) == -1)
371*00b67f09SDavid van Moolenbroek err(EXIT_FAILURE, "Cannot redirect to fd %d", target_fd);
372*00b67f09SDavid van Moolenbroek }
373*00b67f09SDavid van Moolenbroek close(new_fd);
374*00b67f09SDavid van Moolenbroek }
375*00b67f09SDavid van Moolenbroek
376*00b67f09SDavid van Moolenbroek /** Waits for a subprocess and validates its exit condition.
377*00b67f09SDavid van Moolenbroek *
378*00b67f09SDavid van Moolenbroek * \param pid The process to be waited for. Must have been started by
379*00b67f09SDavid van Moolenbroek * testutils_fork().
380*00b67f09SDavid van Moolenbroek * \param exitstatus Expected exit status.
381*00b67f09SDavid van Moolenbroek * \param expout Expected contents of stdout.
382*00b67f09SDavid van Moolenbroek * \param experr Expected contents of stderr. */
383*00b67f09SDavid van Moolenbroek void
atf_utils_wait(const pid_t pid,const int exitstatus,const char * expout,const char * experr)384*00b67f09SDavid van Moolenbroek atf_utils_wait(const pid_t pid, const int exitstatus, const char *expout,
385*00b67f09SDavid van Moolenbroek const char *experr)
386*00b67f09SDavid van Moolenbroek {
387*00b67f09SDavid van Moolenbroek int status;
388*00b67f09SDavid van Moolenbroek ATF_REQUIRE(waitpid(pid, &status, 0) != -1);
389*00b67f09SDavid van Moolenbroek
390*00b67f09SDavid van Moolenbroek atf_utils_cat_file("atf_utils_fork_out.txt", "subprocess stdout: ");
391*00b67f09SDavid van Moolenbroek atf_utils_cat_file("atf_utils_fork_err.txt", "subprocess stderr: ");
392*00b67f09SDavid van Moolenbroek
393*00b67f09SDavid van Moolenbroek ATF_REQUIRE(WIFEXITED(status));
394*00b67f09SDavid van Moolenbroek ATF_REQUIRE_EQ(exitstatus, WEXITSTATUS(status));
395*00b67f09SDavid van Moolenbroek
396*00b67f09SDavid van Moolenbroek const char *save_prefix = "save:";
397*00b67f09SDavid van Moolenbroek const size_t save_prefix_length = strlen(save_prefix);
398*00b67f09SDavid van Moolenbroek
399*00b67f09SDavid van Moolenbroek if (strlen(expout) > save_prefix_length &&
400*00b67f09SDavid van Moolenbroek strncmp(expout, save_prefix, save_prefix_length) == 0) {
401*00b67f09SDavid van Moolenbroek atf_utils_copy_file("atf_utils_fork_out.txt",
402*00b67f09SDavid van Moolenbroek expout + save_prefix_length);
403*00b67f09SDavid van Moolenbroek } else {
404*00b67f09SDavid van Moolenbroek ATF_REQUIRE(atf_utils_compare_file("atf_utils_fork_out.txt", expout));
405*00b67f09SDavid van Moolenbroek }
406*00b67f09SDavid van Moolenbroek
407*00b67f09SDavid van Moolenbroek if (strlen(experr) > save_prefix_length &&
408*00b67f09SDavid van Moolenbroek strncmp(experr, save_prefix, save_prefix_length) == 0) {
409*00b67f09SDavid van Moolenbroek atf_utils_copy_file("atf_utils_fork_err.txt",
410*00b67f09SDavid van Moolenbroek experr + save_prefix_length);
411*00b67f09SDavid van Moolenbroek } else {
412*00b67f09SDavid van Moolenbroek ATF_REQUIRE(atf_utils_compare_file("atf_utils_fork_err.txt", experr));
413*00b67f09SDavid van Moolenbroek }
414*00b67f09SDavid van Moolenbroek
415*00b67f09SDavid van Moolenbroek ATF_REQUIRE(unlink("atf_utils_fork_out.txt") != -1);
416*00b67f09SDavid van Moolenbroek ATF_REQUIRE(unlink("atf_utils_fork_err.txt") != -1);
417*00b67f09SDavid van Moolenbroek }
418