Module Name: src Committed By: jmmv Date: Tue Dec 22 13:36:57 UTC 2009
Modified Files: src/external/bsd/atf/dist/atf-c: dynstr.c fs.c io.c tc.c Removed Files: src/external/bsd/atf/dist: ChangeLog ROADMAP src/external/bsd/atf/dist/tests/atf/atf-c: h_check.c h_macros.h src/external/bsd/atf/dist/tests/build: t_include_atf_c++_hpp.cpp t_include_atf_c_h.cpp src/external/bsd/atf/dist/tests/build/atf-c: t_include_check_h.c t_include_config_h.c t_include_dynstr_h.c t_include_env_h.c t_include_error_fwd_h.c t_include_error_h.c t_include_expand_h.c t_include_fs_h.c t_include_io_h.c t_include_list_h.c t_include_macros_h.c t_include_map_h.c t_include_object_h.c t_include_process_h.c t_include_sanity_h.c t_include_signals_h.c t_include_tc_h.c t_include_tcr_h.c t_include_text_h.c t_include_tp_h.c t_include_ui_h.c t_use_macros_h.c src/external/bsd/atf/dist/tests/build/atf-c++: t_include_application_hpp.cpp t_include_atffile_hpp.cpp t_include_check_hpp.cpp t_include_config_hpp.cpp t_include_env_hpp.cpp t_include_exceptions_hpp.cpp t_include_expand_hpp.cpp t_include_formats_hpp.cpp t_include_fs_hpp.cpp t_include_io_hpp.cpp t_include_macros_hpp.cpp t_include_parser_hpp.cpp t_include_process_hpp.cpp t_include_sanity_hpp.cpp t_include_signals_hpp.cpp t_include_tests_hpp.cpp t_include_text_hpp.cpp t_include_ui_hpp.cpp t_include_user_hpp.cpp t_include_utils_hpp.cpp t_use_macros_hpp.cpp Log Message: Merge atf 0.7. To generate a diff of this commit: cvs rdiff -u -r1.1.1.1 -r0 src/external/bsd/atf/dist/ChangeLog \ src/external/bsd/atf/dist/ROADMAP cvs rdiff -u -r1.2 -r1.3 src/external/bsd/atf/dist/atf-c/dynstr.c \ src/external/bsd/atf/dist/atf-c/fs.c src/external/bsd/atf/dist/atf-c/io.c \ src/external/bsd/atf/dist/atf-c/tc.c cvs rdiff -u -r1.1.1.1 -r0 \ src/external/bsd/atf/dist/tests/atf/atf-c/h_check.c \ src/external/bsd/atf/dist/tests/atf/atf-c/h_macros.h cvs rdiff -u -r1.1.1.1 -r0 \ src/external/bsd/atf/dist/tests/build/t_include_atf_c++_hpp.cpp \ src/external/bsd/atf/dist/tests/build/t_include_atf_c_h.cpp cvs rdiff -u -r1.1.1.1 -r0 \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_check_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_config_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_dynstr_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_env_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_error_fwd_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_error_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_expand_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_fs_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_io_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_list_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_macros_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_map_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_object_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_process_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_sanity_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_signals_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_tc_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_tcr_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_text_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_tp_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_include_ui_h.c \ src/external/bsd/atf/dist/tests/build/atf-c/t_use_macros_h.c cvs rdiff -u -r1.1.1.1 -r0 \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_application_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_atffile_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_check_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_config_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_env_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_exceptions_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_expand_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_formats_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_fs_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_io_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_macros_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_parser_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_process_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_sanity_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_signals_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_tests_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_text_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_ui_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_user_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_include_utils_hpp.cpp \ src/external/bsd/atf/dist/tests/build/atf-c++/t_use_macros_hpp.cpp Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/external/bsd/atf/dist/atf-c/dynstr.c diff -u src/external/bsd/atf/dist/atf-c/dynstr.c:1.2 src/external/bsd/atf/dist/atf-c/dynstr.c:1.3 --- src/external/bsd/atf/dist/atf-c/dynstr.c:1.2 Thu Feb 12 09:05:13 2009 +++ src/external/bsd/atf/dist/atf-c/dynstr.c Tue Dec 22 13:36:56 2009 @@ -1,7 +1,7 @@ /* * Automated Testing Framework (atf) * - * Copyright (c) 2008 The NetBSD Foundation, Inc. + * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -126,13 +126,18 @@ ad->m_data = (char *)malloc(sizeof(char)); if (ad->m_data == NULL) { err = atf_no_memory_error(); - } else { - ad->m_data[0] = '\0'; - ad->m_datasize = 1; - ad->m_length = 0; - err = atf_no_error(); + goto err_object; } + ad->m_data[0] = '\0'; + ad->m_datasize = 1; + ad->m_length = 0; + err = atf_no_error(); + goto out; + +err_object: + atf_object_fini(&ad->m_object); +out: return err; } @@ -146,31 +151,40 @@ ad->m_datasize = strlen(fmt) + 1; ad->m_length = 0; - err = atf_no_error(); do { + va_list ap2; + int ret; + ad->m_datasize *= 2; ad->m_data = (char *)malloc(ad->m_datasize); if (ad->m_data == NULL) { err = atf_no_memory_error(); - } else { - va_list ap2; - int ret; - - va_copy(ap2, ap); - ret = vsnprintf(ad->m_data, ad->m_datasize, fmt, ap2); - va_end(ap2); - if (ret < 0) { - err = atf_libc_error(errno, "Cannot format string"); - } else { - if ((size_t)ret >= ad->m_datasize) { - free(ad->m_data); - ad->m_data = NULL; - } - ad->m_length = ret; - } + goto err_object; } - } while (!atf_is_error(err) && ad->m_length >= ad->m_datasize); + va_copy(ap2, ap); + ret = vsnprintf(ad->m_data, ad->m_datasize, fmt, ap2); + va_end(ap2); + if (ret < 0) { + free(ad->m_data); + err = atf_libc_error(errno, "Cannot format string"); + goto err_object; + } + + INV(ret >= 0); + if ((size_t)ret >= ad->m_datasize) { + free(ad->m_data); + ad->m_data = NULL; + } + ad->m_length = ret; + } while (ad->m_length >= ad->m_datasize); + + err = atf_no_error(); + goto out; + +err_object: + atf_object_fini(&ad->m_object); +out: POST(atf_is_error(err) || ad->m_data != NULL); return err; } @@ -195,18 +209,28 @@ atf_object_init(&ad->m_object); + if (memlen >= SIZE_MAX - 1) { + err = atf_no_memory_error(); + goto err_object; + } + ad->m_data = (char *)malloc(memlen + 1); - if (ad->m_data == NULL) + if (ad->m_data == NULL) { err = atf_no_memory_error(); - else { - ad->m_datasize = memlen + 1; - memcpy(ad->m_data, mem, memlen); - ad->m_data[memlen] = '\0'; - ad->m_length = strlen(ad->m_data); - INV(ad->m_length <= memlen); - err = atf_no_error(); + goto err_object; } + ad->m_datasize = memlen + 1; + memcpy(ad->m_data, mem, memlen); + ad->m_data[memlen] = '\0'; + ad->m_length = strlen(ad->m_data); + INV(ad->m_length <= memlen); + err = atf_no_error(); + goto out; + +err_object: + atf_object_fini(&ad->m_object); +out: return err; } @@ -217,21 +241,27 @@ atf_object_init(&ad->m_object); - if (len == SIZE_MAX) + if (len == SIZE_MAX) { err = atf_no_memory_error(); - else { - ad->m_datasize = len + sizeof(char); - ad->m_data = (char *)malloc(ad->m_datasize); - if (ad->m_data == NULL) { - err = atf_no_memory_error(); - } else { - memset(ad->m_data, ch, len); - ad->m_data[len] = '\0'; - ad->m_length = len; - err = atf_no_error(); - } + goto err_object; + } + + ad->m_datasize = (len + 1) * sizeof(char); + ad->m_data = (char *)malloc(ad->m_datasize); + if (ad->m_data == NULL) { + err = atf_no_memory_error(); + goto err_object; } + memset(ad->m_data, ch, len); + ad->m_data[len] = '\0'; + ad->m_length = len; + err = atf_no_error(); + goto out; + +err_object: + atf_object_fini(&ad->m_object); +out: return err; } Index: src/external/bsd/atf/dist/atf-c/fs.c diff -u src/external/bsd/atf/dist/atf-c/fs.c:1.2 src/external/bsd/atf/dist/atf-c/fs.c:1.3 --- src/external/bsd/atf/dist/atf-c/fs.c:1.2 Thu Feb 12 09:05:13 2009 +++ src/external/bsd/atf/dist/atf-c/fs.c Tue Dec 22 13:36:56 2009 @@ -1,7 +1,7 @@ /* * Automated Testing Framework (atf) * - * Copyright (c) 2007, 2008 The NetBSD Foundation, Inc. + * Copyright (c) 2007, 2008, 2009 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,6 +54,78 @@ #include "atf-c/user.h" /* --------------------------------------------------------------------- + * Prototypes for auxiliary functions. + * --------------------------------------------------------------------- */ + +static bool check_umask(const mode_t, const mode_t); +static atf_error_t cleanup_aux(const atf_fs_path_t *, dev_t, bool); +static atf_error_t cleanup_aux_dir(const char *, const atf_fs_stat_t *, bool); +static atf_error_t copy_contents(const atf_fs_path_t *, char **); +static mode_t current_umask(void); +static atf_error_t do_mkdtemp(char *); +static atf_error_t do_unmount(const atf_fs_path_t *); +static atf_error_t normalize(atf_dynstr_t *, char *); +static atf_error_t normalize_ap(atf_dynstr_t *, const char *, va_list); +static void replace_contents(atf_fs_path_t *, const char *); +static const char *stat_type_to_string(const int); + +/* --------------------------------------------------------------------- + * The "invalid_umask" error type. + * --------------------------------------------------------------------- */ + +struct invalid_umask_error_data { + /* One of atf_fs_stat_*_type. */ + int m_type; + + /* The original path causing the error. */ + /* XXX: Ideally this would be an atf_fs_path_t, but if we create it + * from the error constructor, we cannot delete the path later on. + * Can't remember why atf_error_new does not take a hook for + * deletion. */ + char m_path[1024]; + + /* The umask that caused the error. */ + mode_t m_umask; +}; +typedef struct invalid_umask_error_data invalid_umask_error_data_t; + +static +void +invalid_umask_format(const atf_error_t err, char *buf, size_t buflen) +{ + const invalid_umask_error_data_t *data; + + PRE(atf_error_is(err, "invalid_umask")); + + data = atf_error_data(err); + snprintf(buf, buflen, "Could not create the temporary %s %s because " + "it will not have enough access rights due to the current " + "umask %05o", stat_type_to_string(data->m_type), + data->m_path, (unsigned int)data->m_umask); +} + +static +atf_error_t +invalid_umask_error(const atf_fs_path_t *path, const int type, + const mode_t failing_mask) +{ + atf_error_t err; + invalid_umask_error_data_t data; + + data.m_type = type; + + strncpy(data.m_path, atf_fs_path_cstring(path), sizeof(data.m_path)); + data.m_path[sizeof(data.m_path) - 1] = '\0'; + + data.m_umask = failing_mask; + + err = atf_error_new("invalid_umask", &data, sizeof(data), + invalid_umask_format); + + return err; +} + +/* --------------------------------------------------------------------- * The "unknown_file_type" error type. * --------------------------------------------------------------------- */ @@ -96,11 +168,13 @@ * Auxiliary functions. * --------------------------------------------------------------------- */ -static atf_error_t cleanup_aux(const atf_fs_path_t *, dev_t, bool); -static atf_error_t cleanup_aux_dir(const char *, dev_t, bool); -static atf_error_t do_unmount(const atf_fs_path_t *); -static atf_error_t normalize(atf_dynstr_t *, char *); -static atf_error_t normalize_ap(atf_dynstr_t *, const char *, va_list); +static +bool +check_umask(const mode_t exp_mode, const mode_t min_mode) +{ + const mode_t actual_mode = (~current_umask() & exp_mode); + return (actual_mode & min_mode) == min_mode; +} /* The erase parameter in this routine is to control nested mount points. * We want to descend into a mount point to unmount anything that is @@ -120,7 +194,7 @@ goto out; if (atf_fs_stat_get_type(&st) == atf_fs_stat_dir_type) { - err = cleanup_aux_dir(pstr, atf_fs_stat_get_device(&st), + err = cleanup_aux_dir(pstr, &st, atf_fs_stat_get_device(&st) == parent_device); if (atf_is_error(err)) goto out_st; @@ -134,11 +208,7 @@ if (erase) { if (atf_fs_stat_get_type(&st) == atf_fs_stat_dir_type) { - if (rmdir(pstr) == -1) - err = atf_libc_error(errno, "Cannot remove directory " - "%s", pstr); - else - INV(!atf_is_error(err)); + err = atf_fs_rmdir(p); } else { if (unlink(pstr) == -1) err = atf_libc_error(errno, "Cannot remove file %s", pstr); @@ -155,12 +225,20 @@ static atf_error_t -cleanup_aux_dir(const char *pstr, dev_t this_device, bool erase) +cleanup_aux_dir(const char *pstr, const atf_fs_stat_t *st, bool erase) { DIR *d; atf_error_t err; struct dirent *de; + if (erase && !(atf_fs_stat_get_mode(st) & S_IWUSR)) { + if (chmod(pstr, atf_fs_stat_get_mode(st) | S_IWUSR) == -1) { + err = atf_libc_error(errno, "Cannot grant write permissions " + "to %s", pstr); + goto out; + } + } + d = opendir(pstr); if (d == NULL) { err = atf_libc_error(errno, "Cannot open directory %s", pstr); @@ -175,7 +253,7 @@ if (!atf_is_error(err)) { if (strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0) - err = cleanup_aux(&p, this_device, erase); + err = cleanup_aux(&p, atf_fs_stat_get_device(st), erase); atf_fs_path_fini(&p); } @@ -207,6 +285,15 @@ } static +mode_t +current_umask(void) +{ + const mode_t current = umask(0); + (void)umask(current); + return current; +} + +static atf_error_t do_mkdtemp(char *tmpl) { @@ -274,12 +361,28 @@ * also have to update /etc/mtab to match what we did. It is * simpler to just leave the system-specific umount(8) tool deal * with it, at least for now. */ - char *cmd; + atf_fs_path_t prog; - err = atf_text_format(&cmd, "umount '%s'", pastr); + /* TODO: Should have an atf_fs_find_in_path function or similar to + * avoid relying on the automatic path lookup of exec, which I'd + * like to get rid of. */ + err = atf_fs_path_init_fmt(&prog, "umount"); if (!atf_is_error(err)) { - err = atf_process_system(cmd); - free(cmd); + atf_process_status_t status; + const char *argv[3] = { "unmount", pastr, NULL }; + + err = atf_process_exec_array(&status, &prog, argv, NULL, NULL); + if (!atf_is_error(err)) { + if (!atf_process_status_exited(&status) || + atf_process_status_exitstatus(&status) != EXIT_SUCCESS) { + /* XXX: This is the wrong error type. */ + err = atf_libc_error(EINVAL, "Failed to exec unmount"); + } + + atf_process_status_fini(&status); + } + + atf_fs_path_fini(&prog); } } #endif @@ -361,6 +464,36 @@ INV(!atf_is_error(err)); } +static +const char * +stat_type_to_string(const int type) +{ + const char *str; + + if (type == atf_fs_stat_blk_type) + str = "block device"; + else if (type == atf_fs_stat_chr_type) + str = "character device"; + else if (type == atf_fs_stat_dir_type) + str = "directory"; + else if (type == atf_fs_stat_fifo_type) + str = "named pipe"; + else if (type == atf_fs_stat_lnk_type) + str = "symbolic link"; + else if (type == atf_fs_stat_reg_type) + str = "regular file"; + else if (type == atf_fs_stat_sock_type) + str = "socket"; + else if (type == atf_fs_stat_wht_type) + str = "whiteout"; + else { + UNREACHABLE; + str = NULL; + } + + return str; +} + /* --------------------------------------------------------------------- * The "atf_fs_path" type. * --------------------------------------------------------------------- */ @@ -420,10 +553,10 @@ atf_error_t atf_fs_path_branch_path(const atf_fs_path_t *p, atf_fs_path_t *bp) { - const ssize_t endpos = atf_dynstr_rfind_ch(&p->m_data, '/'); + const size_t endpos = atf_dynstr_rfind_ch(&p->m_data, '/'); atf_error_t err; - if ((size_t)endpos == atf_dynstr_npos) + if (endpos == atf_dynstr_npos) err = atf_fs_path_init_fmt(bp, "."); else if (endpos == 0) err = atf_fs_path_init_fmt(bp, "/"); @@ -449,10 +582,10 @@ atf_error_t atf_fs_path_leaf_name(const atf_fs_path_t *p, atf_dynstr_t *ln) { - ssize_t begpos = atf_dynstr_rfind_ch(&p->m_data, '/'); + size_t begpos = atf_dynstr_rfind_ch(&p->m_data, '/'); atf_error_t err; - if ((size_t)begpos == atf_dynstr_npos) + if (begpos == atf_dynstr_npos) begpos = 0; else begpos++; @@ -640,6 +773,12 @@ return st->m_sb.st_ino; } +mode_t +atf_fs_stat_get_mode(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & ~S_IFMT; +} + off_t atf_fs_stat_get_size(const atf_fs_stat_t *st) { @@ -845,6 +984,11 @@ atf_error_t err; char *buf; + if (!check_umask(S_IRWXU, S_IRWXU)) { + err = invalid_umask_error(p, atf_fs_stat_dir_type, current_umask()); + goto out; + } + err = copy_contents(p, &buf); if (atf_is_error(err)) goto out; @@ -869,6 +1013,11 @@ char *buf; int fd; + if (!check_umask(S_IRWXU, S_IRWXU)) { + err = invalid_umask_error(p, atf_fs_stat_reg_type, current_umask()); + goto out; + } + err = copy_contents(p, &buf); if (atf_is_error(err)) goto out; @@ -888,6 +1037,26 @@ } atf_error_t +atf_fs_rmdir(const atf_fs_path_t *p) +{ + atf_error_t err; + + if (rmdir(atf_fs_path_cstring(p))) { + if (errno == EEXIST) { + /* Some operating systems (e.g. OpenSolaris 200906) return + * EEXIST instead of ENOTEMPTY for non-empty directories. + * Homogenize the return value so that callers don't need + * to bother about differences in operating systems. */ + errno = ENOTEMPTY; + } + err = atf_libc_error(errno, "Cannot remove directory"); + } else + err = atf_no_error(); + + return err; +} + +atf_error_t atf_fs_unlink(const atf_fs_path_t *p) { atf_error_t err; Index: src/external/bsd/atf/dist/atf-c/io.c diff -u src/external/bsd/atf/dist/atf-c/io.c:1.2 src/external/bsd/atf/dist/atf-c/io.c:1.3 --- src/external/bsd/atf/dist/atf-c/io.c:1.2 Thu Feb 12 09:05:13 2009 +++ src/external/bsd/atf/dist/atf-c/io.c Tue Dec 22 13:36:56 2009 @@ -1,7 +1,7 @@ /* * Automated Testing Framework (atf) * - * Copyright (c) 2008 The NetBSD Foundation, Inc. + * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,24 +43,29 @@ #include "atf-c/sanity.h" atf_error_t -atf_io_readline(int fd, atf_dynstr_t *dest) +atf_io_readline(int fd, atf_dynstr_t *dest, bool *eof) { - char ch[2]; + char ch; ssize_t cnt; atf_error_t err; - ch[1] = '\0'; - while ((cnt = read(fd, &ch[0], sizeof(ch[0]))) == sizeof(ch[0]) && - ch[0] != '\n') { - atf_dynstr_append_fmt(dest, ch); + while ((cnt = read(fd, &ch, sizeof(ch))) == sizeof(ch) && + ch != '\n') { + err = atf_dynstr_append_fmt(dest, "%c", ch); + if (atf_is_error(err)) + goto out; } - if (cnt == -1) + if (cnt == -1) { err = atf_libc_error(errno, "Failed to read line from file " "descriptor %d", fd); - else - err = atf_no_error(); + goto out; + } + + *eof = (cnt == 0); + err = atf_no_error(); +out: return err; } @@ -80,6 +85,7 @@ "descriptor %d", atf_dynstr_cstring(&str), fd); else { + INV(cnt >= 0); INV((size_t)cnt == atf_dynstr_length(&str)); err = atf_no_error(); } Index: src/external/bsd/atf/dist/atf-c/tc.c diff -u src/external/bsd/atf/dist/atf-c/tc.c:1.2 src/external/bsd/atf/dist/atf-c/tc.c:1.3 --- src/external/bsd/atf/dist/atf-c/tc.c:1.2 Tue Apr 14 15:41:11 2009 +++ src/external/bsd/atf/dist/atf-c/tc.c Tue Dec 22 13:36:56 2009 @@ -1,7 +1,7 @@ /* * Automated Testing Framework (atf) * - * Copyright (c) 2008 The NetBSD Foundation, Inc. + * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,6 +43,7 @@ #include <unistd.h> #include "atf-c/config.h" +#include "atf-c/defs.h" #include "atf-c/env.h" #include "atf-c/error.h" #include "atf-c/fs.h" @@ -58,39 +59,46 @@ * Auxiliary types and functions. * --------------------------------------------------------------------- */ +struct child_data { + const atf_tc_t *tc; + const atf_fs_path_t *workdir; +}; + /* Parent-only stuff. */ struct timeout_data; static atf_error_t body_parent(const atf_tc_t *, const atf_fs_path_t *, - pid_t, atf_tcr_t *); -static atf_error_t cleanup_parent(const atf_tc_t *, pid_t); -static atf_error_t fork_body(const atf_tc_t *, const atf_fs_path_t *, - atf_tcr_t *); -static atf_error_t fork_cleanup(const atf_tc_t *, const atf_fs_path_t *); + atf_process_child_t *, atf_tcr_t *); +static atf_error_t cleanup_parent(const atf_tc_t *, atf_process_child_t *); +static atf_error_t fork_body(const atf_tc_t *, int, int, + const atf_fs_path_t *, atf_tcr_t *); +static atf_error_t fork_cleanup(const atf_tc_t *, int, int, + const atf_fs_path_t *); static atf_error_t get_tc_result(const atf_fs_path_t *, atf_tcr_t *); -static atf_error_t program_timeout(pid_t, const atf_tc_t *, - struct timeout_data *); +static atf_error_t program_timeout(const atf_process_child_t *, + const atf_tc_t *, struct timeout_data *); static void unprogram_timeout(struct timeout_data *); static void sigalrm_handler(int); /* Child-only stuff. */ -static void body_child(const atf_tc_t *, const atf_fs_path_t *) - ATF_DEFS_ATTRIBUTE_NORETURN; +static void body_child(void *) ATF_DEFS_ATTRIBUTE_NORETURN; static atf_error_t check_arch(const char *, void *); static atf_error_t check_config(const char *, void *); static atf_error_t check_machine(const char *, void *); static atf_error_t check_prog(const char *, void *); static atf_error_t check_prog_in_dir(const char *, void *); static atf_error_t check_requirements(const atf_tc_t *); -static void cleanup_child(const atf_tc_t *, const atf_fs_path_t *) - ATF_DEFS_ATTRIBUTE_NORETURN; +static void cleanup_child(void *) ATF_DEFS_ATTRIBUTE_NORETURN; static void fail_internal(const char *, int, const char *, const char *, const char *, va_list, + void (*)(atf_dynstr_t *), void (*)(const char *, ...)); static void fatal_atf_error(const char *, atf_error_t) ATF_DEFS_ATTRIBUTE_NORETURN; static void fatal_libc_error(const char *, int) ATF_DEFS_ATTRIBUTE_NORETURN; -static atf_error_t prepare_child(const atf_tc_t *, const atf_fs_path_t *); +static atf_error_t prepare_child(const struct child_data *); +static void tc_fail(atf_dynstr_t *) ATF_DEFS_ATTRIBUTE_NORETURN; +static void tc_fail_nonfatal(atf_dynstr_t *); static void write_tcr(const atf_tcr_t *); /* --------------------------------------------------------------------- @@ -269,7 +277,7 @@ * --------------------------------------------------------------------- */ atf_error_t -atf_tc_run(const atf_tc_t *tc, atf_tcr_t *tcr, +atf_tc_run(const atf_tc_t *tc, atf_tcr_t *tcr, int fdout, int fderr, const atf_fs_path_t *workdirbase) { atf_error_t err, cleanuperr; @@ -287,8 +295,8 @@ if (atf_is_error(err)) goto out_workdir; - err = fork_body(tc, &workdir, tcr); - cleanuperr = fork_cleanup(tc, &workdir); + err = fork_body(tc, fdout, fderr, &workdir, tcr); + cleanuperr = fork_cleanup(tc, fdout, fderr, &workdir); if (!atf_is_error(cleanuperr)) (void)atf_fs_cleanup(&workdir); if (!atf_is_error(err)) @@ -328,7 +336,8 @@ static atf_error_t -program_timeout(pid_t pid, const atf_tc_t *tc, struct timeout_data *td) +program_timeout(const atf_process_child_t *child, const atf_tc_t *tc, + struct timeout_data *td) { atf_error_t err; long timeout; @@ -338,7 +347,7 @@ goto out; if (timeout != 0) { - sigalrm_pid = pid; + sigalrm_pid = atf_process_child_pid(child); sigalrm_killed = false; err = atf_signal_programmer_init(&td->m_sigalrm, SIGALRM, @@ -377,52 +386,62 @@ static atf_error_t -body_parent(const atf_tc_t *tc, const atf_fs_path_t *workdir, pid_t pid, - atf_tcr_t *tcr) +body_parent(const atf_tc_t *tc, const atf_fs_path_t *workdir, + atf_process_child_t *child, atf_tcr_t *tcr) { atf_error_t err; - int state; + atf_process_status_t status; struct timeout_data td; - err = program_timeout(pid, tc, &td); + err = program_timeout(child, tc, &td); if (atf_is_error(err)) { char buf[4096]; atf_error_format(err, buf, sizeof(buf)); fprintf(stderr, "Error programming test case's timeout: %s", buf); atf_error_free(err); - killpg(pid, SIGKILL); + killpg(atf_process_child_pid(child), SIGKILL); } - if (waitpid(pid, &state, 0) == -1) { - if (errno == EINTR && sigalrm_killed) +again: + err = atf_process_child_wait(child, &status); + if (atf_is_error(err)) { + if (atf_error_is(err, "libc") && atf_libc_error_code(err) == EINTR) { + atf_error_free(err); + goto again; + } else { + /* Propagate err */ + } + } else { + if (sigalrm_killed) err = atf_tcr_init_reason_fmt(tcr, atf_tcr_failed_state, "Test case timed out after %s " "seconds", atf_tc_get_md_var(tc, "timeout")); - else - err = atf_libc_error(errno, "Error waiting for child process " - "%d", pid); - } else { - if (!WIFEXITED(state) || WEXITSTATUS(state) != EXIT_SUCCESS) { - if (WIFEXITED(state)) - err = atf_tcr_init_reason_fmt(tcr, atf_tcr_failed_state, - "Test case did not exit cleanly; " - "exit status was %d", - WEXITSTATUS(state)); - else if (WIFSIGNALED(state)) + else { + if (atf_process_status_exited(&status)) { + const int exitstatus = atf_process_status_exitstatus(&status); + if (exitstatus == EXIT_SUCCESS) + err = get_tc_result(workdir, tcr); + else + err = atf_tcr_init_reason_fmt(tcr, atf_tcr_failed_state, + "Test case exited with " + "status %d", exitstatus); + } else if (atf_process_status_signaled(&status)) { + const int sig = atf_process_status_termsig(&status); + const bool wcore = atf_process_status_coredump(&status); err = atf_tcr_init_reason_fmt(tcr, atf_tcr_failed_state, - "Test case did not exit cleanly: " - "%s%s", - strsignal(WTERMSIG(state)), - WCOREDUMP(state) ? - " (core dumped)" : ""); - else + "Test case was signaled by " + "signal %d%s", sig, + wcore ? " (core dumped)" : ""); + } else { err = atf_tcr_init_reason_fmt(tcr, atf_tcr_failed_state, - "Test case did not exit cleanly; " - "state was %d", state); - } else - err = get_tc_result(workdir, tcr); + "Test case exited due to an " + "unexpected condition"); + } + } + + atf_process_status_fini(&status); } unprogram_timeout(&td); @@ -432,71 +451,97 @@ static atf_error_t -cleanup_parent(const atf_tc_t *tc, pid_t pid) +cleanup_parent(const atf_tc_t *tc, atf_process_child_t *child) { atf_error_t err; - int state; + atf_process_status_t status; - if (waitpid(pid, &state, 0) == -1) { - err = atf_libc_error(errno, "Error waiting for child process " - "%d", pid); + err = atf_process_child_wait(child, &status); + if (atf_is_error(err)) goto out; - } - if (!WIFEXITED(state) || WEXITSTATUS(state) != EXIT_SUCCESS) + if (!atf_process_status_exited(&status) || + atf_process_status_exitstatus(&status) != EXIT_SUCCESS) { /* XXX Not really a libc error. */ err = atf_libc_error(EINVAL, "Child process did not exit cleanly"); - else + } else err = atf_no_error(); + atf_process_status_fini(&status); out: return err; } static atf_error_t -fork_body(const atf_tc_t *tc, const atf_fs_path_t *workdir, atf_tcr_t *tcr) +fork_body(const atf_tc_t *tc, int fdout, int fderr, + const atf_fs_path_t *workdir, atf_tcr_t *tcr) { atf_error_t err; - pid_t pid; + atf_process_stream_t outsb, errsb; + atf_process_child_t child; + struct child_data data = { + tc, + workdir, + }; - err = atf_process_fork(&pid); + err = atf_process_stream_init_redirect_fd(&outsb, fdout); if (atf_is_error(err)) goto out; - if (pid == 0) { - body_child(tc, workdir); - UNREACHABLE; - abort(); - } else { - err = body_parent(tc, workdir, pid, tcr); - } + err = atf_process_stream_init_redirect_fd(&errsb, fderr); + if (atf_is_error(err)) + goto out_outsb; + + err = atf_process_fork(&child, body_child, &outsb, &errsb, &data); + if (atf_is_error(err)) + goto out_errsb; + + err = body_parent(tc, workdir, &child, tcr); +out_errsb: + atf_process_stream_fini(&errsb); +out_outsb: + atf_process_stream_fini(&outsb); out: return err; } static atf_error_t -fork_cleanup(const atf_tc_t *tc, const atf_fs_path_t *workdir) +fork_cleanup(const atf_tc_t *tc, int fdout, int fderr, + const atf_fs_path_t *workdir) { atf_error_t err; - pid_t pid; if (tc->m_cleanup == NULL) err = atf_no_error(); else { - err = atf_process_fork(&pid); + atf_process_stream_t outsb, errsb; + atf_process_child_t child; + struct child_data data = { + tc, + workdir, + }; + + err = atf_process_stream_init_redirect_fd(&outsb, fdout); if (atf_is_error(err)) goto out; - if (pid == 0) { - cleanup_child(tc, workdir); - UNREACHABLE; - abort(); - } else { - err = cleanup_parent(tc, pid); - } + err = atf_process_stream_init_redirect_fd(&errsb, fderr); + if (atf_is_error(err)) + goto out_outsb; + + err = atf_process_fork(&child, cleanup_child, &outsb, &errsb, &data); + if (atf_is_error(err)) + goto out_errsb; + + err = cleanup_parent(tc, &child); + +out_errsb: + atf_process_stream_fini(&errsb); +out_outsb: + atf_process_stream_fini(&outsb); } out: @@ -544,13 +589,13 @@ static atf_error_t -prepare_child(const atf_tc_t *tc, const atf_fs_path_t *workdir) +prepare_child(const struct child_data *cd) { atf_error_t err; int i, ret; - current_tc = tc; - current_workdir = workdir; + current_tc = cd->tc; + current_workdir = cd->workdir; current_tc_fail_count = 0; ret = setpgid(getpid(), 0); @@ -561,7 +606,7 @@ for (i = 1; i <= atf_signals_last_signo; i++) atf_signal_reset(i); - err = atf_env_set("HOME", atf_fs_path_cstring(workdir)); + err = atf_env_set("HOME", atf_fs_path_cstring(cd->workdir)); if (atf_is_error(err)) goto out; @@ -601,9 +646,9 @@ if (atf_is_error(err)) goto out; - if (chdir(atf_fs_path_cstring(workdir)) == -1) { + if (chdir(atf_fs_path_cstring(cd->workdir)) == -1) { err = atf_libc_error(errno, "Cannot enter work directory '%s'", - atf_fs_path_cstring(workdir)); + atf_fs_path_cstring(cd->workdir)); goto out; } @@ -615,19 +660,21 @@ static void -body_child(const atf_tc_t *tc, const atf_fs_path_t *workdir) +body_child(void *v) { + const struct child_data *cd = v; atf_error_t err; - atf_disable_exit_checks(); + atf_reset_exit_checks(); - err = prepare_child(tc, workdir); + err = prepare_child(cd); if (atf_is_error(err)) goto print_err; - err = check_requirements(tc); + err = check_requirements(cd->tc); if (atf_is_error(err)) goto print_err; - tc->m_body(tc); + + cd->tc->m_body(cd->tc); if (current_tc_fail_count == 0) atf_tc_pass(); @@ -636,7 +683,6 @@ current_tc_fail_count); UNREACHABLE; - abort(); print_err: INV(atf_is_error(err)); @@ -650,7 +696,6 @@ } UNREACHABLE; - abort(); } static @@ -704,8 +749,12 @@ goto out; if (atf_fs_path_is_absolute(&p)) { - if (atf_is_error(atf_fs_eaccess(&p, atf_fs_access_x))) + err = atf_fs_eaccess(&p, atf_fs_access_x); + if (atf_is_error(err)) { + atf_error_free(err); + atf_fs_path_fini(&p); atf_tc_skip("The required program %s could not be found", prog); + } } else { const char *path = atf_env_get("PATH"); struct prog_found_pair pf; @@ -715,9 +764,12 @@ if (atf_is_error(err)) goto out_p; - if (strcmp(atf_fs_path_cstring(&bp), ".") != 0) + if (strcmp(atf_fs_path_cstring(&bp), ".") != 0) { + atf_fs_path_fini(&bp); + atf_fs_path_fini(&p); atf_tc_fail("Relative paths are not allowed when searching for " "a program (%s)", prog); + } pf.prog = prog; pf.found = false; @@ -725,9 +777,12 @@ if (atf_is_error(err)) goto out_bp; - if (!pf.found) + if (!pf.found) { + atf_fs_path_fini(&bp); + atf_fs_path_fini(&p); atf_tc_skip("The required program %s could not be found in " "the PATH", prog); + } out_bp: atf_fs_path_fini(&bp); @@ -755,8 +810,14 @@ if (atf_is_error(err)) goto out_p; - if (!atf_is_error(atf_fs_eaccess(&p, atf_fs_access_x))) + err = atf_fs_eaccess(&p, atf_fs_access_x); + if (!atf_is_error(err)) pf->found = true; + else { + atf_error_free(err); + INV(!pf->found); + err = atf_no_error(); + } out_p: atf_fs_path_fini(&p); @@ -852,22 +913,22 @@ static void -cleanup_child(const atf_tc_t *tc, const atf_fs_path_t *workdir) +cleanup_child(void *v) { + const struct child_data *cd = v; atf_error_t err; - atf_disable_exit_checks(); - - err = prepare_child(tc, workdir); - if (atf_is_error(err)) + err = prepare_child(cd); + if (atf_is_error(err)) { + atf_reset_exit_checks(); exit(EXIT_FAILURE); - else { - tc->m_cleanup(tc); + } else { + atf_reset_exit_checks(); + cd->tc->m_cleanup(cd->tc); exit(EXIT_SUCCESS); } UNREACHABLE; - abort(); } static @@ -921,6 +982,39 @@ fatal_atf_error("Cannot write test case results", err); close(fd); + atf_fs_path_fini(&tcrfile); +} + +static +void +tc_fail(atf_dynstr_t *msg) +{ + atf_tcr_t tcr; + atf_error_t err; + + PRE(current_tc != NULL); + + err = atf_tcr_init_reason_fmt(&tcr, atf_tcr_failed_state, "%s", + atf_dynstr_cstring(msg)); + if (atf_is_error(err)) + abort(); + + write_tcr(&tcr); + + atf_tcr_fini(&tcr); + atf_dynstr_fini(msg); + + exit(EXIT_SUCCESS); +} + +static +void +tc_fail_nonfatal(atf_dynstr_t *msg) +{ + fprintf(stderr, "%s\n", atf_dynstr_cstring(msg)); + atf_dynstr_fini(msg); + + current_tc_fail_count++; } void @@ -965,7 +1059,7 @@ va_start(ap, fmt); fail_internal(file, line, "Check failed", "*** ", fmt, ap, - atf_tc_fail_nonfatal); + tc_fail_nonfatal, atf_tc_fail_nonfatal); va_end(ap); } @@ -974,9 +1068,11 @@ { va_list ap; + atf_reset_exit_checks(); + va_start(ap, fmt); fail_internal(file, line, "Requirement failed", "", fmt, ap, - atf_tc_fail); + tc_fail, atf_tc_fail); va_end(ap); UNREACHABLE; @@ -987,7 +1083,8 @@ void fail_internal(const char *file, int line, const char *reason, const char *prefix, const char *fmt, va_list ap, - void (*failfunc)(const char *, ...)) + void (*failfunc)(atf_dynstr_t *), + void (*backupfunc)(const char *, ...)) { va_list ap2; atf_error_t err; @@ -1007,13 +1104,13 @@ } va_copy(ap2, ap); - failfunc("%s", atf_dynstr_cstring(&msg)); - atf_dynstr_fini(&msg); + failfunc(&msg); return; backup: + atf_error_free(err); va_copy(ap2, ap); - failfunc(fmt, ap2); + backupfunc(fmt, ap2); va_end(ap2); } @@ -1042,8 +1139,10 @@ atf_error_t err; err = check_prog(prog, NULL); - if (atf_is_error(err)) + if (atf_is_error(err)) { + atf_error_free(err); atf_tc_fail("atf_tc_require_prog failed"); /* XXX Correct? */ + } } void