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