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