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