Merge "Clear pthread_internal_t allocated on user provided stack."
diff --git a/libc/bionic/system_properties.cpp b/libc/bionic/system_properties.cpp
index 9fe982a..3fd41d7 100644
--- a/libc/bionic/system_properties.cpp
+++ b/libc/bionic/system_properties.cpp
@@ -25,34 +25,38 @@
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
-#include <new>
-#include <stdatomic.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <stddef.h>
+#include <ctype.h>
#include <errno.h>
-#include <poll.h>
#include <fcntl.h>
+#include <poll.h>
+#include <stdatomic.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
+#include <new>
+#include <linux/xattr.h>
+#include <netinet/in.h>
#include <sys/mman.h>
-
-#include <sys/socket.h>
-#include <sys/un.h>
#include <sys/select.h>
+#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <netinet/in.h>
+#include <sys/un.h>
+#include <sys/xattr.h>
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
#include <sys/system_properties.h>
#include "private/bionic_futex.h"
+#include "private/bionic_lock.h"
#include "private/bionic_macros.h"
+#include "private/libc_logging.h"
static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
@@ -192,7 +196,7 @@
}
};
-static char property_filename[PATH_MAX] = PROP_FILENAME;
+static char property_filename[PROP_FILENAME_MAX] = PROP_FILENAME;
static bool compat_mode = false;
static size_t pa_data_size;
static size_t pa_size;
@@ -216,13 +220,12 @@
return atoi(env);
}
-static int map_prop_area_rw()
-{
+static prop_area* map_prop_area_rw(const char* filename, const char* context,
+ bool* fsetxattr_failed) {
/* dev is a tmpfs that we can use to carve a shared workspace
* out of, so let's do that...
*/
- const int fd = open(property_filename,
- O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
+ const int fd = open(filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
if (fd < 0) {
if (errno == EACCES) {
@@ -231,12 +234,31 @@
*/
abort();
}
- return -1;
+ return nullptr;
+ }
+
+ if (context) {
+ if (fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) {
+ __libc_format_log(ANDROID_LOG_ERROR, "libc",
+ "fsetxattr failed to set context (%s) for \"%s\"", context, filename);
+ /*
+ * fsetxattr() will fail during system properties tests due to selinux policy.
+ * We do not want to create a custom policy for the tester, so we will continue in
+ * this function but set a flag that an error has occurred.
+ * Init, which is the only daemon that should ever call this function will abort
+ * when this error occurs.
+ * Otherwise, the tester will ignore it and continue, albeit without any selinux
+ * property separation.
+ */
+ if (fsetxattr_failed) {
+ *fsetxattr_failed = true;
+ }
+ }
}
if (ftruncate(fd, PA_SIZE) < 0) {
close(fd);
- return -1;
+ return nullptr;
}
pa_size = PA_SIZE;
@@ -246,29 +268,26 @@
void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (memory_area == MAP_FAILED) {
close(fd);
- return -1;
+ return nullptr;
}
prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);
- /* plug into the lib property services */
- __system_property_area__ = pa;
-
close(fd);
- return 0;
+ return pa;
}
-static int map_fd_ro(const int fd) {
+static prop_area* map_fd_ro(const int fd) {
struct stat fd_stat;
if (fstat(fd, &fd_stat) < 0) {
- return -1;
+ return nullptr;
}
if ((fd_stat.st_uid != 0)
|| (fd_stat.st_gid != 0)
|| ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0)
|| (fd_stat.st_size < static_cast<off_t>(sizeof(prop_area))) ) {
- return -1;
+ return nullptr;
}
pa_size = fd_stat.st_size;
@@ -276,7 +295,7 @@
void* const map_result = mmap(NULL, pa_size, PROT_READ, MAP_SHARED, fd, 0);
if (map_result == MAP_FAILED) {
- return -1;
+ return nullptr;
}
prop_area* pa = reinterpret_cast<prop_area*>(map_result);
@@ -284,22 +303,20 @@
(pa->version() != PROP_AREA_VERSION &&
pa->version() != PROP_AREA_VERSION_COMPAT)) {
munmap(pa, pa_size);
- return -1;
+ return nullptr;
}
if (pa->version() == PROP_AREA_VERSION_COMPAT) {
compat_mode = true;
}
- __system_property_area__ = pa;
- return 0;
+ return pa;
}
-static int map_prop_area()
-{
- int fd = open(property_filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
+static prop_area* map_prop_area(const char* filename, bool is_legacy) {
+ int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
bool close_fd = true;
- if (fd == -1 && errno == ENOENT) {
+ if (fd == -1 && errno == ENOENT && is_legacy) {
/*
* For backwards compatibility, if the file doesn't
* exist, we use the environment to get the file descriptor.
@@ -308,16 +325,18 @@
* returns other errors such as ENOMEM or ENFILE, since it
* might be possible for an external program to trigger this
* condition.
+ * Only do this for the legacy prop file, secured prop files
+ * do not have a backup
*/
fd = get_fd_from_env();
close_fd = false;
}
if (fd < 0) {
- return -1;
+ return nullptr;
}
- const int map_result = map_fd_ro(fd);
+ prop_area* map_result = map_fd_ro(fd);
if (close_fd) {
close(fd);
}
@@ -623,9 +642,325 @@
return foreach_property(root_node(), propfn, cookie);
}
+struct context_node {
+ context_node(struct context_node* next, const char* context, prop_area* pa)
+ : context(strdup(context)), pa(pa), checked_access(false), next(next) {
+ lock.init(false);
+ }
+ ~context_node() {
+ if (pa) {
+ munmap(pa, pa_size);
+ }
+ free(context);
+ }
+ Lock lock;
+ char* context;
+ prop_area* pa;
+ bool checked_access;
+ struct context_node* next;
+};
+
+struct prefix_node {
+ prefix_node(struct prefix_node* next, const char* prefix, context_node* context)
+ : prefix(strdup(prefix)), prefix_len(strlen(prefix)), context(context), next(next) {
+ }
+ ~prefix_node() {
+ free(prefix);
+ }
+ char* prefix;
+ const size_t prefix_len;
+ context_node* context;
+ struct prefix_node* next;
+};
+
+template <typename List, typename... Args>
+static inline void list_add(List** list, Args... args) {
+ *list = new List(*list, args...);
+}
+
+static void list_add_after_len(prefix_node** list, const char* prefix, context_node* context) {
+ size_t prefix_len = strlen(prefix);
+
+ auto next_list = list;
+
+ while (*next_list) {
+ if ((*next_list)->prefix_len < prefix_len || (*next_list)->prefix[0] == '*') {
+ list_add(next_list, prefix, context);
+ return;
+ }
+ next_list = &(*next_list)->next;
+ }
+ list_add(next_list, prefix, context);
+}
+
+template <typename List, typename Func>
+static void list_foreach(List* list, Func func) {
+ while (list) {
+ func(list);
+ list = list->next;
+ }
+}
+
+template <typename List, typename Func>
+static List* list_find(List* list, Func func) {
+ while (list) {
+ if (func(list)) {
+ return list;
+ }
+ list = list->next;
+ }
+ return nullptr;
+}
+
+template <typename List>
+static void list_free(List** list) {
+ while (*list) {
+ auto old_list = *list;
+ *list = old_list->next;
+ delete old_list;
+ }
+}
+
+static prefix_node* prefixes = nullptr;
+static context_node* contexts = nullptr;
+
+/*
+ * pthread_mutex_lock() calls into system_properties in the case of contention.
+ * This creates a risk of dead lock if any system_properties functions
+ * use pthread locks after system_property initialization.
+ *
+ * For this reason, the below three functions use a bionic Lock and static
+ * allocation of memory for each filename.
+ */
+
+static bool open_prop_file(context_node* cnode, bool access_rw, bool* fsetxattr_failed) {
+ cnode->lock.lock();
+ if (cnode->pa) {
+ cnode->lock.unlock();
+ return true;
+ }
+
+ char filename[PROP_FILENAME_MAX];
+ int len = snprintf(filename, sizeof(filename), "%s/%s", property_filename, cnode->context);
+ if (len < 0 || len > PROP_FILENAME_MAX) {
+ cnode->lock.unlock();
+ return false;
+ }
+
+ if (access_rw) {
+ cnode->pa = map_prop_area_rw(filename, cnode->context, fsetxattr_failed);
+ } else {
+ cnode->pa = map_prop_area(filename, false);
+ }
+ cnode->lock.unlock();
+ return cnode->pa;
+}
+
+static bool check_access(context_node* cnode) {
+ char filename[PROP_FILENAME_MAX];
+ int len = snprintf(filename, sizeof(filename), "%s/%s", property_filename, cnode->context);
+ if (len < 0 || len > PROP_FILENAME_MAX) {
+ return false;
+ }
+
+ return access(filename, R_OK) == 0;
+}
+
+static bool map_system_property_area(bool access_rw, bool* fsetxattr_failed) {
+ char filename[PROP_FILENAME_MAX];
+ int len = snprintf(filename, sizeof(filename), "%s/properties_serial", property_filename);
+ if (len < 0 || len > PROP_FILENAME_MAX) {
+ __system_property_area__ = nullptr;
+ return false;
+ }
+
+ if (access_rw) {
+ __system_property_area__ =
+ map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed);
+ } else {
+ __system_property_area__ = map_prop_area(filename, false);
+ }
+ return __system_property_area__;
+}
+
+static prop_area* get_prop_area_for_name(const char* name) {
+ auto entry = list_find(prefixes, [name](prefix_node* l) {
+ return l->prefix[0] == '*' || !strncmp(l->prefix, name, l->prefix_len);
+ });
+ if (!entry) {
+ return nullptr;
+ }
+
+ auto cnode = entry->context;
+ if (!cnode->pa) {
+ open_prop_file(cnode, false, nullptr);
+ }
+ return cnode->pa;
+}
+
+/*
+ * The below two functions are duplicated from label_support.c in libselinux.
+ * TODO: Find a location suitable for these functions such that both libc and
+ * libselinux can share a common source file.
+ */
+
+/*
+ * The read_spec_entries and read_spec_entry functions may be used to
+ * replace sscanf to read entries from spec files. The file and
+ * property services now use these.
+ */
+
+/* Read an entry from a spec file (e.g. file_contexts) */
+static inline int read_spec_entry(char **entry, char **ptr, int *len)
+{
+ *entry = NULL;
+ char *tmp_buf = NULL;
+
+ while (isspace(**ptr) && **ptr != '\0')
+ (*ptr)++;
+
+ tmp_buf = *ptr;
+ *len = 0;
+
+ while (!isspace(**ptr) && **ptr != '\0') {
+ (*ptr)++;
+ (*len)++;
+ }
+
+ if (*len) {
+ *entry = strndup(tmp_buf, *len);
+ if (!*entry)
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * line_buf - Buffer containing the spec entries .
+ * num_args - The number of spec parameter entries to process.
+ * ... - A 'char **spec_entry' for each parameter.
+ * returns - The number of items processed.
+ *
+ * This function calls read_spec_entry() to do the actual string processing.
+ */
+static int read_spec_entries(char *line_buf, int num_args, ...)
+{
+ char **spec_entry, *buf_p;
+ int len, rc, items, entry_len = 0;
+ va_list ap;
+
+ len = strlen(line_buf);
+ if (line_buf[len - 1] == '\n')
+ line_buf[len - 1] = '\0';
+ else
+ /* Handle case if line not \n terminated by bumping
+ * the len for the check below (as the line is NUL
+ * terminated by getline(3)) */
+ len++;
+
+ buf_p = line_buf;
+ while (isspace(*buf_p))
+ buf_p++;
+
+ /* Skip comment lines and empty lines. */
+ if (*buf_p == '#' || *buf_p == '\0')
+ return 0;
+
+ /* Process the spec file entries */
+ va_start(ap, num_args);
+
+ items = 0;
+ while (items < num_args) {
+ spec_entry = va_arg(ap, char **);
+
+ if (len - 1 == buf_p - line_buf) {
+ va_end(ap);
+ return items;
+ }
+
+ rc = read_spec_entry(spec_entry, &buf_p, &entry_len);
+ if (rc < 0) {
+ va_end(ap);
+ return rc;
+ }
+ if (entry_len)
+ items++;
+ }
+ va_end(ap);
+ return items;
+}
+
+static bool initialize_properties() {
+ list_free(&prefixes);
+ list_free(&contexts);
+
+ FILE* file = fopen("/property_contexts", "re");
+
+ if (!file) {
+ return false;
+ }
+
+ char* buffer = nullptr;
+ size_t line_len;
+ char* prop_prefix = nullptr;
+ char* context = nullptr;
+
+ while (getline(&buffer, &line_len, file) > 0) {
+ int items = read_spec_entries(buffer, 2, &prop_prefix, &context);
+ if (items <= 0) {
+ continue;
+ }
+ if (items == 1) {
+ free(prop_prefix);
+ continue;
+ }
+
+ auto old_context = list_find(
+ contexts, [context](context_node* l) { return !strcmp(l->context, context); });
+ if (old_context) {
+ list_add_after_len(&prefixes, prop_prefix, old_context);
+ } else {
+ list_add(&contexts, context, nullptr);
+ list_add_after_len(&prefixes, prop_prefix, contexts);
+ }
+ free(prop_prefix);
+ free(context);
+ }
+
+ free(buffer);
+ fclose(file);
+ return true;
+}
+
+static bool is_dir(const char* pathname) {
+ struct stat info;
+ if (stat(pathname, &info) == -1) {
+ return false;
+ }
+ return S_ISDIR(info.st_mode);
+}
+
int __system_properties_init()
{
- return map_prop_area();
+ if (is_dir(property_filename)) {
+ if (!initialize_properties()) {
+ return -1;
+ }
+ if (!map_system_property_area(false, nullptr)) {
+ list_free(&prefixes);
+ list_free(&contexts);
+ return -1;
+ }
+ } else {
+ __system_property_area__ = map_prop_area(property_filename, true);
+ if (!__system_property_area__) {
+ return -1;
+ }
+ list_add(&contexts, "legacy_system_prop_area", __system_property_area__);
+ list_add_after_len(&prefixes, "*", contexts);
+ }
+ return 0;
}
int __system_property_set_filename(const char *filename)
@@ -640,7 +975,23 @@
int __system_property_area_init()
{
- return map_prop_area_rw();
+ mkdir(property_filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+ if (!initialize_properties()) {
+ return -1;
+ }
+ bool open_prop_file_failed = false;
+ bool fsetxattr_failed = false;
+ list_foreach(contexts, [&fsetxattr_failed, &open_prop_file_failed](context_node* l) {
+ if (!open_prop_file(l, true, &fsetxattr_failed)) {
+ open_prop_file_failed = true;
+ }
+ });
+ if (open_prop_file_failed || !map_system_property_area(true, &fsetxattr_failed)) {
+ list_free(&prefixes);
+ list_free(&contexts);
+ return -1;
+ }
+ return fsetxattr_failed ? -2 : 0;
}
unsigned int __system_property_area_serial()
@@ -659,11 +1010,13 @@
return __system_property_find_compat(name);
}
- if (!__system_property_area__) {
+ prop_area* pa = get_prop_area_for_name(name);
+ if (!pa) {
+ __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied finding property \"%s\"", name);
return nullptr;
}
- return __system_property_area__->find(name);
+ return pa->find(name);
}
// The C11 standard doesn't allow atomic loads from const fields,
@@ -769,8 +1122,6 @@
int __system_property_add(const char *name, unsigned int namelen,
const char *value, unsigned int valuelen)
{
- prop_area *pa = __system_property_area__;
-
if (namelen >= PROP_NAME_MAX)
return -1;
if (valuelen >= PROP_VALUE_MAX)
@@ -778,21 +1129,24 @@
if (namelen < 1)
return -1;
- if (!__system_property_area__) {
+ prop_area* pa = get_prop_area_for_name(name);
+
+ if (!pa) {
+ __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied adding property \"%s\"", name);
return -1;
}
- bool ret = __system_property_area__->add(name, namelen, value, valuelen);
+ bool ret = pa->add(name, namelen, value, valuelen);
if (!ret)
return -1;
// There is only a single mutator, but we want to make sure that
// updates are visible to a reader waiting for the update.
atomic_store_explicit(
- pa->serial(),
- atomic_load_explicit(pa->serial(), memory_order_relaxed) + 1,
+ __system_property_area__->serial(),
+ atomic_load_explicit(__system_property_area__->serial(), memory_order_relaxed) + 1,
memory_order_release);
- __futex_wake(pa->serial(), INT32_MAX);
+ __futex_wake(__system_property_area__->serial(), INT32_MAX);
return 0;
}
@@ -841,9 +1195,16 @@
return __system_property_foreach_compat(propfn, cookie);
}
- if (!__system_property_area__) {
- return -1;
- }
-
- return __system_property_area__->foreach(propfn, cookie) ? 0 : -1;
+ list_foreach(contexts, [propfn, cookie](context_node* l) {
+ if (!l->pa && !l->checked_access) {
+ if (check_access(l)) {
+ open_prop_file(l, false, nullptr);
+ }
+ l->checked_access = true;
+ }
+ if (l->pa) {
+ l->pa->foreach(propfn, cookie);
+ }
+ });
+ return 0;
}
diff --git a/libc/include/sys/_system_properties.h b/libc/include/sys/_system_properties.h
index a0315b5..3b1f7d0 100644
--- a/libc/include/sys/_system_properties.h
+++ b/libc/include/sys/_system_properties.h
@@ -41,6 +41,7 @@
#define PROP_AREA_VERSION_COMPAT 0x45434f76
#define PROP_SERVICE_NAME "property_service"
+#define PROP_FILENAME_MAX 1024
#define PROP_FILENAME "/dev/__properties__"
#define PA_SIZE (128 * 1024)
diff --git a/libm/libm.arm.map b/libm/libm.arm.map
index f44cf90..e781f2d 100644
--- a/libm/libm.arm.map
+++ b/libm/libm.arm.map
@@ -272,8 +272,8 @@
*;
};
-LIBC_PRIVATE { # arm x86 mips
- global: # arm x86 mips
+LIBC_PRIVATE { # arm mips
+ global: # arm mips
___Unwind_Backtrace; # arm
___Unwind_ForcedUnwind; # arm
___Unwind_RaiseException; # arm
@@ -354,7 +354,6 @@
__lesf2; # arm
__ltdf2; # arm
__ltsf2; # arm
- __muldc3; # arm x86 mips
__muldf3; # arm
__nedf2; # arm
__nesf2; # arm
@@ -376,4 +375,4 @@
_Unwind_VRS_Pop; # arm
_Unwind_VRS_Set; # arm
restore_core_regs; # arm
-} LIBC; # arm x86 mips
+} LIBC; # arm mips
diff --git a/libm/libm.map.txt b/libm/libm.map.txt
index 3bd081f..075ebd5 100644
--- a/libm/libm.map.txt
+++ b/libm/libm.map.txt
@@ -271,8 +271,8 @@
*;
};
-LIBC_PRIVATE { # arm x86 mips
- global: # arm x86 mips
+LIBC_PRIVATE { # arm mips
+ global: # arm mips
___Unwind_Backtrace; # arm
___Unwind_ForcedUnwind; # arm
___Unwind_RaiseException; # arm
@@ -353,7 +353,6 @@
__lesf2; # arm
__ltdf2; # arm
__ltsf2; # arm
- __muldc3; # arm x86 mips
__muldf3; # arm
__nedf2; # arm
__nesf2; # arm
@@ -375,4 +374,4 @@
_Unwind_VRS_Pop; # arm
_Unwind_VRS_Set; # arm
restore_core_regs; # arm
-} LIBC; # arm x86 mips
+} LIBC; # arm mips
diff --git a/libm/libm.mips.map b/libm/libm.mips.map
index 78ed1ea..476c6ad 100644
--- a/libm/libm.mips.map
+++ b/libm/libm.mips.map
@@ -272,11 +272,10 @@
*;
};
-LIBC_PRIVATE { # arm x86 mips
- global: # arm x86 mips
+LIBC_PRIVATE { # arm mips
+ global: # arm mips
__fixdfdi; # arm mips
__fixsfdi; # arm mips
__fixunsdfdi; # arm mips
__fixunssfdi; # arm mips
- __muldc3; # arm x86 mips
-} LIBC; # arm x86 mips
+} LIBC; # arm mips
diff --git a/libm/libm.x86.map b/libm/libm.x86.map
index 8aa6d88..1623ea0 100644
--- a/libm/libm.x86.map
+++ b/libm/libm.x86.map
@@ -272,7 +272,3 @@
*;
};
-LIBC_PRIVATE { # arm x86 mips
- global: # arm x86 mips
- __muldc3; # arm x86 mips
-} LIBC; # arm x86 mips
diff --git a/tests/gtest_main.cpp b/tests/gtest_main.cpp
index 3b9f6b9..a662c73 100644
--- a/tests/gtest_main.cpp
+++ b/tests/gtest_main.cpp
@@ -575,27 +575,15 @@
exit(result);
}
-#if defined(__APPLE__)
-
-static int pipe2(int pipefd[2], int flags) {
- int ret = pipe(pipefd);
- if (ret != -1) {
- ret = fcntl(pipefd[0], F_SETFL, flags);
- }
- if (ret != -1) {
- ret = fcntl(pipefd[1], F_SETFL, flags);
- }
- return ret;
-}
-
-#endif
-
static ChildProcInfo RunChildProcess(const std::string& test_name, int testcase_id, int test_id,
int argc, char** argv) {
int pipefd[2];
- int ret = pipe2(pipefd, O_NONBLOCK);
- if (ret == -1) {
- perror("pipe2 in RunTestInSeparateProc");
+ if (pipe(pipefd) == -1) {
+ perror("pipe in RunTestInSeparateProc");
+ exit(1);
+ }
+ if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) == -1) {
+ perror("fcntl in RunTestInSeparateProc");
exit(1);
}
pid_t pid = fork();
@@ -685,6 +673,30 @@
return timeout_child_count;
}
+static void ReadChildProcOutput(std::vector<TestCase>& testcase_list,
+ std::vector<ChildProcInfo>& child_proc_list) {
+ for (const auto& child_proc : child_proc_list) {
+ TestCase& testcase = testcase_list[child_proc.testcase_id];
+ int test_id = child_proc.test_id;
+ while (true) {
+ char buf[1024];
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(child_proc.child_read_fd, buf, sizeof(buf) - 1));
+ if (bytes_read > 0) {
+ buf[bytes_read] = '\0';
+ testcase.GetTest(test_id).AppendTestOutput(buf);
+ } else if (bytes_read == 0) {
+ break; // Read end.
+ } else {
+ if (errno == EAGAIN) {
+ break;
+ }
+ perror("failed to read child_read_fd");
+ exit(1);
+ }
+ }
+ }
+}
+
static void WaitChildProcs(std::vector<TestCase>& testcase_list,
std::vector<ChildProcInfo>& child_proc_list) {
size_t finished_child_count = 0;
@@ -709,6 +721,7 @@
finished_child_count += CheckChildProcTimeout(child_proc_list);
}
+ ReadChildProcOutput(testcase_list, child_proc_list);
if (finished_child_count > 0) {
return;
}
@@ -742,26 +755,6 @@
kill(child_proc.pid, SIGKILL);
WaitForOneChild(child_proc.pid);
}
-
- while (true) {
- char buf[1024];
- ssize_t bytes_read = TEMP_FAILURE_RETRY(read(child_proc.child_read_fd, buf, sizeof(buf) - 1));
- if (bytes_read > 0) {
- buf[bytes_read] = '\0';
- testcase.GetTest(test_id).AppendTestOutput(buf);
- } else if (bytes_read == 0) {
- break; // Read end.
- } else {
- if (errno == EAGAIN) {
- // No data is available. This rarely happens, only when the child process created other
- // processes which have not exited so far. But the child process has already exited or
- // been killed, so the test has finished, and we shouldn't wait further.
- break;
- }
- perror("read child_read_fd in RunTestInSeparateProc");
- exit(1);
- }
- }
close(child_proc.child_read_fd);
if (child_proc.timed_out) {
@@ -780,8 +773,14 @@
testcase.GetTest(test_id).AppendTestOutput(buf);
} else {
- testcase.SetTestResult(test_id, WEXITSTATUS(child_proc.exit_status) == 0 ?
- TEST_SUCCESS : TEST_FAILED);
+ int exitcode = WEXITSTATUS(child_proc.exit_status);
+ testcase.SetTestResult(test_id, exitcode == 0 ? TEST_SUCCESS : TEST_FAILED);
+ if (exitcode != 0) {
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "%s exited with exitcode %d.\n",
+ testcase.GetTestName(test_id).c_str(), exitcode);
+ testcase.GetTest(test_id).AppendTestOutput(buf);
+ }
}
}
diff --git a/tests/system_properties_test.cpp b/tests/system_properties_test.cpp
index c7bfee6..09eac3f 100644
--- a/tests/system_properties_test.cpp
+++ b/tests/system_properties_test.cpp
@@ -41,9 +41,6 @@
return;
}
- old_pa = __system_property_area__;
- __system_property_area__ = NULL;
-
pa_dirname = dirname;
pa_filename = pa_dirname + "/__properties__";
@@ -57,9 +54,8 @@
return;
}
- __system_property_area__ = old_pa;
-
__system_property_set_filename(PROP_FILENAME);
+ __system_properties_init();
unlink(pa_filename.c_str());
rmdir(pa_dirname.c_str());
}
@@ -68,7 +64,6 @@
private:
std::string pa_dirname;
std::string pa_filename;
- void *old_pa;
};
static void foreach_test_callback(const prop_info *pi, void* cookie) {