This patch creates the contents of the support/ directory up to this upstream commit on the master branch: commit 401311cfba71b61d93d23aa17e5c9ac5fb047d48 Author: Florian Weimer Date: Mon Jan 8 14:33:17 2018 +0100 resolv: Support binary labels in test framework The old implementation based on hsearch_r used an ad-hoc C string encoding and produced an incorrect format on the wire for domain names which contained bytes which needed escaping when printed. This commit switches to ns_name_pton for the wire format conversion (now that we have separate tests for it) and uses a tsearch tree with a suitable comparison function to locate compression targets. diff --git a/scripts/backport-support.sh b/scripts/backport-support.sh new file mode 100644 index 0000000000..4057e42d3c --- /dev/null +++ b/scripts/backport-support.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# Create a patch which backports the support/ subdirectory. +# Copyright (C) 2017-2018 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# . + +# This script does not backport the Makefile tweaks outside the +# support/ directory (which need to be backported separately), or the +# changes to test-skeleton.c (which should not be backported). + +set -e + +export LC_ALL=C +export GIT_CONFIG=/dev/null +export GTT_CONFIG_NOSYSTEM=0 +export GIT_PAGER= + +usage () { + cat >&2 <&2 + echo "# rm -rf $patch_targets" >&2 +} + +command_commit () { + git status --porcelain | while read line ; do + echo "error: working copy is not clean, cannot commit" >&2 + exit 1 + done + for path in $patch_targets; do + echo "# Processing $path" >&2 + case "$path" in + [a-zA-Z0-9]*/) + # Directory. + git rm --cached --ignore-unmatch -r "$path" + rm -rf "$path" + git read-tree --prefix="$path" "$latest_commit":"$path" + git checkout "$path" + ;; + *) + # File. + git show "$latest_commit":"$path" > "$path" + git add "$path" + esac + done + git commit -m "Synchronize support/ infrastructure with $branch_name + +This commit updates the support/ subdirectory to +commit $latest_commit +on the $branch_name branch. +" +} + +command_$command diff --git a/support/Makefile b/support/Makefile new file mode 100644 index 0000000000..1bda81e55e --- /dev/null +++ b/support/Makefile @@ -0,0 +1,171 @@ +# Makefile for support library, used only at build and test time +# Copyright (C) 2016-2018 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# . + +subdir := support + +include ../Makeconfig + +extra-libs := libsupport +extra-libs-others = $(extra-libs) +extra-libs-noinstall := $(extra-libs) + +libsupport-routines = \ + check \ + check_addrinfo \ + check_dns_packet \ + check_hostent \ + check_netent \ + delayed_exit \ + ignore_stderr \ + next_to_fault \ + oom_error \ + resolv_test \ + set_fortify_handler \ + support-xfstat \ + support-xstat \ + support_become_root \ + support_can_chroot \ + support_capture_subprocess \ + support_capture_subprocess_check \ + support_chroot \ + support_enter_mount_namespace \ + support_enter_network_namespace \ + support_format_address_family \ + support_format_addrinfo \ + support_format_dns_packet \ + support_format_herrno \ + support_format_hostent \ + support_format_netent \ + support_isolate_in_subprocess \ + support_record_failure \ + support_run_diff \ + support_shared_allocate \ + support_test_compare_failure \ + support_write_file_string \ + support_test_main \ + support_test_verify_impl \ + temp_file \ + write_message \ + xaccept \ + xaccept4 \ + xasprintf \ + xbind \ + xcalloc \ + xchroot \ + xclose \ + xconnect \ + xdlfcn \ + xdup2 \ + xfclose \ + xfopen \ + xfork \ + xftruncate \ + xgetsockname \ + xlisten \ + xlseek \ + xmalloc \ + xmemstream \ + xmkdir \ + xmmap \ + xmprotect \ + xmunmap \ + xopen \ + xpipe \ + xpoll \ + xpthread_attr_destroy \ + xpthread_attr_init \ + xpthread_attr_setdetachstate \ + xpthread_attr_setguardsize \ + xpthread_attr_setstacksize \ + xpthread_barrier_destroy \ + xpthread_barrier_init \ + xpthread_barrier_wait \ + xpthread_cancel \ + xpthread_check_return \ + xpthread_cond_wait \ + xpthread_create \ + xpthread_detach \ + xpthread_join \ + xpthread_mutex_consistent \ + xpthread_mutex_destroy \ + xpthread_mutex_init \ + xpthread_mutex_lock \ + xpthread_mutex_unlock \ + xpthread_mutexattr_destroy \ + xpthread_mutexattr_init \ + xpthread_mutexattr_setprotocol \ + xpthread_mutexattr_setpshared \ + xpthread_mutexattr_setrobust \ + xpthread_mutexattr_settype \ + xpthread_once \ + xpthread_rwlock_init \ + xpthread_rwlock_rdlock \ + xpthread_rwlock_unlock \ + xpthread_rwlock_wrlock \ + xpthread_rwlockattr_init \ + xpthread_rwlockattr_setkind_np \ + xpthread_sigmask \ + xpthread_spin_lock \ + xpthread_spin_unlock \ + xraise \ + xreadlink \ + xrealloc \ + xrecvfrom \ + xsendto \ + xsetsockopt \ + xsigaction \ + xsignal \ + xsocket \ + xstrdup \ + xstrndup \ + xsysconf \ + xunlink \ + xwaitpid \ + xwrite \ + +libsupport-static-only-routines := $(libsupport-routines) +# Only build one variant of the library. +libsupport-inhibit-o := .os +ifeq ($(build-shared),yes) +libsupport-inhibit-o += .o +endif + +tests = \ + README-testing \ + tst-support-namespace \ + tst-support_capture_subprocess \ + tst-support_format_dns_packet \ + tst-support_record_failure \ + tst-test_compare \ + tst-xreadlink \ + +ifeq ($(run-built-tests),yes) +tests-special = \ + $(objpfx)tst-support_record_failure-2.out + +$(objpfx)tst-support_record_failure-2.out: tst-support_record_failure-2.sh \ + $(objpfx)tst-support_record_failure + $(SHELL) $< $(common-objpfx) '$(test-program-prefix-before-env)' \ + '$(run-program-env)' '$(test-program-prefix-after-env)' \ + > $@; \ + $(evaluate-test) +endif + +$(objpfx)tst-support_format_dns_packet: $(common-objpfx)resolv/libresolv.so + +include ../Rules diff --git a/support/README b/support/README new file mode 100644 index 0000000000..476cfcda59 --- /dev/null +++ b/support/README @@ -0,0 +1,29 @@ +This subdirectory contains infrastructure which is not put into +installed libraries, but may be linked into programs (installed or +not) and tests. + +# Error-checking wrappers + +These wrappers test for error return codes an terminate the process on +error. They are declared in these header files: + +* support.h +* xsignal.h +* xthread.h + +In general, new wrappers should be added to support.h if possible. +However, support.h must remain fully compatible with C90 and therefore +cannot include headers which use identifers not reserved in C90. If +the wrappers need additional types, additional headers such as +signal.h need to be introduced. + +# Test framework + +The test framework provides a main program for tests, including a +timeout for hanging tests. See README-testing.c for a minimal +example, and test-driver.c for details how to use it. The following +header files provide related declarations: + +* check.h +* temp_file.h +* test-driver.h diff --git a/support/README-testing.c b/support/README-testing.c new file mode 100644 index 0000000000..9d289c3020 --- /dev/null +++ b/support/README-testing.c @@ -0,0 +1,19 @@ +/* This file contains an example test case which shows minimal use of + the test framework. Additional testing hooks are described in + . */ + +/* This function will be called from the test driver. */ +static int +do_test (void) +{ + if (3 == 5) + /* Indicate failure. */ + return 1; + else + /* Indicate success. */ + return 0; +} + +/* This file references do_test above and contains the definition of + the main function. */ +#include diff --git a/support/capture_subprocess.h b/support/capture_subprocess.h new file mode 100644 index 0000000000..b0886ba1d1 --- /dev/null +++ b/support/capture_subprocess.h @@ -0,0 +1,61 @@ +/* Capture output from a subprocess. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_CAPTURE_SUBPROCESS_H +#define SUPPORT_CAPTURE_SUBPROCESS_H + +#include + +struct support_capture_subprocess +{ + struct xmemstream out; + struct xmemstream err; + int status; +}; + +/* Invoke CALLBACK (CLOSURE) in a subprocess and capture standard + output, standard error, and the exit status. The out.buffer and + err.buffer members in the result are null-terminated strings which + can be examined by the caller (out.out and err.out are NULL). */ +struct support_capture_subprocess support_capture_subprocess + (void (*callback) (void *), void *closure); + +/* Deallocate the subprocess data captured by + support_capture_subprocess. */ +void support_capture_subprocess_free (struct support_capture_subprocess *); + +enum support_capture_allow +{ + /* No output is allowed. */ + sc_allow_none = 0x01, + /* Output to stdout is permitted. */ + sc_allow_stdout = 0x02, + /* Output to standard error is permitted. */ + sc_allow_stderr = 0x04, +}; + +/* Check that the subprocess exited with STATUS and that only the + allowed outputs happened. ALLOWED is a combination of + support_capture_allow flags. Report errors under the CONTEXT + message. */ +void support_capture_subprocess_check (struct support_capture_subprocess *, + const char *context, int status, + int allowed) + __attribute__ ((nonnull (1, 2))); + +#endif /* SUPPORT_CAPTURE_SUBPROCESS_H */ diff --git a/support/check.c b/support/check.c new file mode 100644 index 0000000000..688ed569ac --- /dev/null +++ b/support/check.c @@ -0,0 +1,57 @@ +/* Support code for reporting test results. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include + +static void +print_failure (const char *file, int line, const char *format, va_list ap) +{ + printf ("error: %s:%d: ", file, line); + vprintf (format, ap); + puts (""); +} + +int +support_print_failure_impl (const char *file, int line, + const char *format, ...) +{ + support_record_failure (); + va_list ap; + va_start (ap, format); + print_failure (file, line, format, ap); + va_end (ap); + return 1; +} + +void +support_exit_failure_impl (int status, const char *file, int line, + const char *format, ...) +{ + if (status != EXIT_SUCCESS && status != EXIT_UNSUPPORTED) + support_record_failure (); + va_list ap; + va_start (ap, format); + print_failure (file, line, format, ap); + va_end (ap); + exit (status); +} diff --git a/support/check.h b/support/check.h new file mode 100644 index 0000000000..2192f38941 --- /dev/null +++ b/support/check.h @@ -0,0 +1,153 @@ +/* Functionality for reporting test results. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_CHECK_H +#define SUPPORT_CHECK_H + +#include + +__BEGIN_DECLS + +/* Record a test failure, print the failure message to standard output + and return 1. */ +#define FAIL_RET(...) \ + return support_print_failure_impl (__FILE__, __LINE__, __VA_ARGS__) + +/* Print the failure message and terminate the process with STATUS. + Record a the process as failed if STATUS is neither EXIT_SUCCESS + nor EXIT_UNSUPPORTED. */ +#define FAIL_EXIT(status, ...) \ + support_exit_failure_impl (status, __FILE__, __LINE__, __VA_ARGS__) + +/* Record a test failure, print the failure message and terminate with + exit status 1. */ +#define FAIL_EXIT1(...) \ + support_exit_failure_impl (1, __FILE__, __LINE__, __VA_ARGS__) + +/* Print failure message and terminate with as unsupported test (exit + status of 77). */ +#define FAIL_UNSUPPORTED(...) \ + support_exit_failure_impl (77, __FILE__, __LINE__, __VA_ARGS__) + +/* Record a test failure (but continue executing) if EXPR evaluates to + false. */ +#define TEST_VERIFY(expr) \ + ({ \ + if (expr) \ + ; \ + else \ + support_test_verify_impl (__FILE__, __LINE__, #expr); \ + }) + +/* Record a test failure and exit if EXPR evaluates to false. */ +#define TEST_VERIFY_EXIT(expr) \ + ({ \ + if (expr) \ + ; \ + else \ + support_test_verify_exit_impl \ + (1, __FILE__, __LINE__, #expr); \ + }) + +int support_print_failure_impl (const char *file, int line, + const char *format, ...) + __attribute__ ((nonnull (1), format (printf, 3, 4))); +void support_exit_failure_impl (int exit_status, + const char *file, int line, + const char *format, ...) + __attribute__ ((noreturn, nonnull (2), format (printf, 4, 5))); +void support_test_verify_impl (const char *file, int line, + const char *expr); +void support_test_verify_exit_impl (int status, const char *file, int line, + const char *expr) + __attribute__ ((noreturn)); + +/* Record a test failure. This function returns and does not + terminate the process. The failure counter is stored in a shared + memory mapping, so that failures reported in child processes are + visible to the parent process and test driver. This function + depends on initialization by an ELF constructor, so it can only be + invoked after the test driver has run. Note that this function + does not support reporting failures from a DSO. */ +void support_record_failure (void); + +/* Static assertion, under a common name for both C++ and C11. */ +#ifdef __cplusplus +# define support_static_assert static_assert +#else +# define support_static_assert _Static_assert +#endif + +/* Compare the two integers LEFT and RIGHT and report failure if they + are different. */ +#define TEST_COMPARE(left, right) \ + ({ \ + /* + applies the integer promotions, for bitfield support. */ \ + typedef __typeof__ (+ (left)) __left_type; \ + typedef __typeof__ (+ (right)) __right_type; \ + __left_type __left_value = (left); \ + __right_type __right_value = (right); \ + int __left_is_positive = __left_value > 0; \ + int __right_is_positive = __right_value > 0; \ + /* Prevent use with floating-point types. */ \ + support_static_assert ((__left_type) 1.0 == (__left_type) 1.5, \ + "left value has floating-point type"); \ + support_static_assert ((__right_type) 1.0 == (__right_type) 1.5, \ + "right value has floating-point type"); \ + /* Prevent accidental use with larger-than-long long types. */ \ + support_static_assert (sizeof (__left_value) <= sizeof (long long), \ + "left value fits into long long"); \ + support_static_assert (sizeof (__right_value) <= sizeof (long long), \ + "right value fits into long long"); \ + /* Compare the value. */ \ + if (__left_value != __right_value \ + || __left_is_positive != __right_is_positive) \ + /* Pass the sign for printing the correct value. */ \ + support_test_compare_failure \ + (__FILE__, __LINE__, \ + #left, __left_value, __left_is_positive, sizeof (__left_type), \ + #right, __right_value, __right_is_positive, sizeof (__right_type)); \ + }) + +/* Internal implementation of TEST_COMPARE. LEFT_POSITIVE and + RIGHT_POSITIVE are used to store the sign separately, so that both + unsigned long long and long long arguments fit into LEFT_VALUE and + RIGHT_VALUE, and the function can still print the original value. + LEFT_SIZE and RIGHT_SIZE specify the size of the argument in bytes, + for hexadecimal formatting. */ +void support_test_compare_failure (const char *file, int line, + const char *left_expr, + long long left_value, + int left_positive, + int left_size, + const char *right_expr, + long long right_value, + int right_positive, + int right_size); + + +/* Internal function called by the test driver. */ +int support_report_failure (int status) + __attribute__ ((weak, warn_unused_result)); + +/* Internal function used to test the failure recording framework. */ +void support_record_failure_reset (void); + +__END_DECLS + +#endif /* SUPPORT_CHECK_H */ diff --git a/support/check_addrinfo.c b/support/check_addrinfo.c new file mode 100644 index 0000000000..91ad7c56bd --- /dev/null +++ b/support/check_addrinfo.c @@ -0,0 +1,43 @@ +/* Compare struct addrinfo values against a formatted string. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include +#include +#include + +void +check_addrinfo (const char *query_description, struct addrinfo *ai, int ret, + const char *expected) +{ + char *formatted = support_format_addrinfo (ai, ret); + if (strcmp (formatted, expected) != 0) + { + support_record_failure (); + printf ("error: addrinfo comparison failure\n"); + if (query_description != NULL) + printf ("query: %s\n", query_description); + support_run_diff ("expected", expected, + "actual", formatted); + } + free (formatted); +} diff --git a/support/check_dns_packet.c b/support/check_dns_packet.c new file mode 100644 index 0000000000..6c1277bd67 --- /dev/null +++ b/support/check_dns_packet.c @@ -0,0 +1,43 @@ +/* Check that a DNS packet buffer has the expected contents. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include +#include +#include + +void +check_dns_packet (const char *query_description, + const unsigned char *buffer, size_t length, + const char *expected) +{ + char *formatted = support_format_dns_packet (buffer, length); + if (strcmp (formatted, expected) != 0) + { + support_record_failure (); + printf ("error: packet comparison failure\n"); + if (query_description != NULL) + printf ("query: %s\n", query_description); + support_run_diff ("expected", expected, "actual", formatted); + } + free (formatted); +} diff --git a/support/check_hostent.c b/support/check_hostent.c new file mode 100644 index 0000000000..56384f9b03 --- /dev/null +++ b/support/check_hostent.c @@ -0,0 +1,43 @@ +/* Compare struct hostent values against a formatted string. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include +#include +#include + +void +check_hostent (const char *query_description, struct hostent *h, + const char *expected) +{ + char *formatted = support_format_hostent (h); + if (strcmp (formatted, expected) != 0) + { + support_record_failure (); + printf ("error: hostent comparison failure\n"); + if (query_description != NULL) + printf ("query: %s\n", query_description); + support_run_diff ("expected", expected, + "actual", formatted); + } + free (formatted); +} diff --git a/support/check_netent.c b/support/check_netent.c new file mode 100644 index 0000000000..cbcbfb14e5 --- /dev/null +++ b/support/check_netent.c @@ -0,0 +1,43 @@ +/* Compare struct netent values against a formatted string. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include +#include +#include + +void +check_netent (const char *query_description, struct netent *e, + const char *expected) +{ + char *formatted = support_format_netent (e); + if (strcmp (formatted, expected) != 0) + { + support_record_failure (); + printf ("error: netent comparison failure\n"); + if (query_description != NULL) + printf ("query: %s\n", query_description); + support_run_diff ("expected", expected, + "actual", formatted); + } + free (formatted); +} diff --git a/support/check_nss.h b/support/check_nss.h new file mode 100644 index 0000000000..6aa28fa24e --- /dev/null +++ b/support/check_nss.h @@ -0,0 +1,42 @@ +/* Test verification functions for NSS- and DNS-related data. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_CHECK_NSS_H +#define SUPPORT_CHECK_NSS_H + +#include +#include + +__BEGIN_DECLS + +/* Compare the data structures against the expected values (which have + to be formatted according to the support_format_* functions in + ). If there is a difference, a delayed test + failure is recorded, and a diff is written to standard output. */ +void check_addrinfo (const char *query_description, + struct addrinfo *, int ret, const char *expected); +void check_dns_packet (const char *query_description, + const unsigned char *, size_t, const char *expected); +void check_hostent (const char *query_description, + struct hostent *, const char *expected); +void check_netent (const char *query_description, + struct netent *, const char *expected); + +__END_DECLS + +#endif /* SUPPORT_CHECK_NSS_H */ diff --git a/support/delayed_exit.c b/support/delayed_exit.c new file mode 100644 index 0000000000..2780d9a6fe --- /dev/null +++ b/support/delayed_exit.c @@ -0,0 +1,55 @@ +/* Time-triggered process termination. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +#include +#include +#include +#include +#include + +static void * +delayed_exit_thread (void *seconds_as_ptr) +{ + int seconds = (uintptr_t) seconds_as_ptr; + struct timespec delay = { seconds, 0 }; + struct timespec remaining = { 0 }; + if (nanosleep (&delay, &remaining) != 0) + FAIL_EXIT1 ("nanosleep: %m"); + /* Exit the process sucessfully. */ + exit (0); + return NULL; +} + +void +delayed_exit (int seconds) +{ + /* Create the new thread with all signals blocked. */ + sigset_t all_blocked; + sigfillset (&all_blocked); + sigset_t old_set; + xpthread_sigmask (SIG_SETMASK, &all_blocked, &old_set); + /* Create a detached thread. */ + pthread_t thr = xpthread_create + (NULL, delayed_exit_thread, (void *) (uintptr_t) seconds); + xpthread_detach (thr); + /* Restore the original signal mask. */ + xpthread_sigmask (SIG_SETMASK, &old_set, NULL); +} diff --git a/support/format_nss.h b/support/format_nss.h new file mode 100644 index 0000000000..e55354e788 --- /dev/null +++ b/support/format_nss.h @@ -0,0 +1,41 @@ +/* String formatting functions for NSS- and DNS-related data. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_FORMAT_NSS_H +#define SUPPORT_FORMAT_NSS_H + +#include +#include + +__BEGIN_DECLS + +/* The following functions format their arguments as human-readable + strings (which can span multiple lines). The caller must free the + returned buffer. For NULL pointers or failure status arguments, + error variables such as h_errno and errno are included in the + result. */ +char *support_format_address_family (int); +char *support_format_addrinfo (struct addrinfo *, int ret); +char *support_format_dns_packet (const unsigned char *buffer, size_t length); +char *support_format_herrno (int); +char *support_format_hostent (struct hostent *); +char *support_format_netent (struct netent *); + +__END_DECLS + +#endif /* SUPPORT_FORMAT_NSS_H */ diff --git a/support/ignore_stderr.c b/support/ignore_stderr.c new file mode 100644 index 0000000000..450333ad38 --- /dev/null +++ b/support/ignore_stderr.c @@ -0,0 +1,38 @@ +/* Avoid all the buffer overflow messages on stderr. + Copyright (C) 2015-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include + +void +ignore_stderr (void) +{ + int fd = open (_PATH_DEVNULL, O_WRONLY); + if (fd == -1) + close (STDERR_FILENO); + else + { + dup2 (fd, STDERR_FILENO); + close (fd); + } + setenv ("LIBC_FATAL_STDERR_", "1", 1); +} diff --git a/support/namespace.h b/support/namespace.h new file mode 100644 index 0000000000..3c3842a49b --- /dev/null +++ b/support/namespace.h @@ -0,0 +1,107 @@ +/* Entering namespaces for test case isolation. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_NAMESPACE_H +#define SUPPORT_NAMESPACE_H + +#include +#include + +__BEGIN_DECLS + +/* Attempts to become root (or acquire root-like privileges), possibly + with the help of user namespaces. Return true if (restricted) root + privileges could be attained in some way. Print diagnostics to + standard output. + + Note that this function generally has to be called before a process + becomes multi-threaded, otherwise it may fail with insufficient + privileges on systems which would support this operation for + single-threaded processes. */ +bool support_become_root (void); + +/* Return true if this process can perform a chroot operation. In + general, this is only possible if support_become_root has been + called. Note that the actual test is performed in a subprocess, + after fork, so that the file system root of the original process is + not changed. */ +bool support_can_chroot (void); + +/* Enter a network namespace (and a UTS namespace if possible) and + configure the loopback interface. Return true if a network + namespace could be created. Print diagnostics to standard output. + If a network namespace could be created, but networking in it could + not be configured, terminate the process. It is recommended to + call support_become_root before this function so that the process + has sufficient privileges. */ +bool support_enter_network_namespace (void); + +/* Enter a mount namespace and mark / as private (not shared). If + this function returns true, mount operations in this process will + not affect the host system afterwards. */ +bool support_enter_mount_namespace (void); + +/* Return true if support_enter_network_namespace managed to enter a + UTS namespace. */ +bool support_in_uts_namespace (void); + +/* Invoke CALLBACK (CLOSURE) in a subprocess created using fork. + Terminate the calling process if the subprocess exits with a + non-zero exit status. */ +void support_isolate_in_subprocess (void (*callback) (void *), void *closure); + +/* Describe the setup of a chroot environment, for + support_chroot_create below. */ +struct support_chroot_configuration +{ + /* File contents. The files are not created if the field is + NULL. */ + const char *resolv_conf; /* /etc/resolv.conf. */ + const char *hosts; /* /etc/hosts. */ + const char *host_conf; /* /etc/host.conf. */ +}; + +/* The result of the creation of a chroot. */ +struct support_chroot +{ + /* Path information. All these paths are relative to the parent + chroot. */ + + /* Path to the chroot directory. */ + char *path_chroot; + + /* Paths to files in the chroot. These are absolute and outside of + the chroot. */ + char *path_resolv_conf; /* /etc/resolv.conf. */ + char *path_hosts; /* /etc/hosts. */ + char *path_host_conf; /* /etc/host.conf. */ +}; + +/* Create a chroot environment. The returned data should be freed + using support_chroot_free below. The files will be deleted when + the process exits. This function does not enter the chroot. */ +struct support_chroot *support_chroot_create + (struct support_chroot_configuration); + +/* Deallocate the chroot information created by + support_chroot_create. */ +void support_chroot_free (struct support_chroot *); + +__END_DECLS + +#endif diff --git a/support/next_to_fault.c b/support/next_to_fault.c new file mode 100644 index 0000000000..1971bf7cd7 --- /dev/null +++ b/support/next_to_fault.c @@ -0,0 +1,52 @@ +/* Memory allocation next to an unmapped page. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include + +struct support_next_to_fault +support_next_to_fault_allocate (size_t size) +{ + long page_size = sysconf (_SC_PAGE_SIZE); + TEST_VERIFY_EXIT (page_size > 0); + struct support_next_to_fault result; + result.region_size = roundup (size, page_size) + page_size; + if (size + page_size <= size || result.region_size <= size) + FAIL_EXIT1 ("support_next_to_fault_allocate (%zu): overflow", size); + result.region_start + = xmmap (NULL, result.region_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1); + /* Unmap the page after the allocation. */ + xmprotect (result.region_start + (result.region_size - page_size), + page_size, PROT_NONE); + /* Align the allocation within the region so that it ends just + before the PROT_NONE page. */ + result.buffer = result.region_start + result.region_size - page_size - size; + result.length = size; + return result; +} + +void +support_next_to_fault_free (struct support_next_to_fault *ntf) +{ + xmunmap (ntf->region_start, ntf->region_size); + *ntf = (struct support_next_to_fault) { NULL, }; +} diff --git a/support/next_to_fault.h b/support/next_to_fault.h new file mode 100644 index 0000000000..75759b586c --- /dev/null +++ b/support/next_to_fault.h @@ -0,0 +1,48 @@ +/* Memory allocation next to an unmapped page. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_NEXT_TO_FAULT_H +#define SUPPORT_NEXT_TO_FAULT_H + +#include +#include + +__BEGIN_DECLS + +/* The memory region created by next_to_fault_allocate. */ +struct support_next_to_fault +{ + /* The user data. */ + char *buffer; + size_t length; + + /* The entire allocated region. */ + void *region_start; + size_t region_size; +}; + +/* Allocate a buffer of SIZE bytes just before a page which is mapped + with PROT_NONE (so that overrunning the buffer will cause a + fault). */ +struct support_next_to_fault support_next_to_fault_allocate (size_t size); + +/* Deallocate the memory region allocated by + next_to_fault_allocate. */ +void support_next_to_fault_free (struct support_next_to_fault *); + +#endif /* SUPPORT_NEXT_TO_FAULT_H */ diff --git a/support/oom_error.c b/support/oom_error.c new file mode 100644 index 0000000000..fd87fe2305 --- /dev/null +++ b/support/oom_error.c @@ -0,0 +1,29 @@ +/* Reporting out-of-memory errors. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include + +void +oom_error (const char *function, size_t size) +{ + printf ("%s: unable to allocate %zu bytes: %m\n", function, size); + exit (1); +} diff --git a/support/resolv_test.c b/support/resolv_test.c new file mode 100644 index 0000000000..3f2a09f36f --- /dev/null +++ b/support/resolv_test.c @@ -0,0 +1,1263 @@ +/* DNS test framework and libresolv redirection. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Response builder. */ + +enum + { + max_response_length = 65536 + }; + +/* Used for locating domain names containing for the purpose of + forming compression references. */ +struct compressed_name +{ + uint16_t offset; + unsigned char length; + unsigned char name[]; /* Without terminating NUL. */ +}; + +static struct compressed_name * +allocate_compressed_name (const unsigned char *encoded, unsigned int offset) +{ + /* Compute the length of the domain name. */ + size_t length; + { + const unsigned char *p; + for (p = encoded; *p != '\0';) + { + /* No compression references are allowed. */ + TEST_VERIFY (*p <= 63); + /* Skip over the label. */ + p += 1 + *p; + } + length = p - encoded; + ++length; /* For the terminating NUL byte. */ + } + TEST_VERIFY_EXIT (length <= 255); + + struct compressed_name *result + = xmalloc (offsetof (struct compressed_name, name) + length); + result->offset = offset; + result->length = length; + memcpy (result->name, encoded, length); + return result; +} + +/* Convert CH to lower case. Only change letters in the ASCII + range. */ +static inline unsigned char +ascii_tolower (unsigned char ch) +{ + if ('A' <= ch && ch <= 'Z') + return ch - 'A' + 'a'; + else + return ch; +} + +/* Compare both names, for use with tsearch. The order is arbitrary, + but the comparison is case-insenstive. */ +static int +compare_compressed_name (const void *left, const void *right) +{ + const struct compressed_name *crleft = left; + const struct compressed_name *crright = right; + + if (crleft->length != crright->length) + /* The operands are converted to int before the subtraction. */ + return crleft->length - crright->length; + + const unsigned char *nameleft = crleft->name; + const unsigned char *nameright = crright->name; + + while (true) + { + int lenleft = *nameleft++; + int lenright = *nameright++; + + /* Labels must not e compression references. */ + TEST_VERIFY (lenleft <= 63); + TEST_VERIFY (lenright <= 63); + + if (lenleft != lenright) + return left - right; + if (lenleft == 0) + /* End of name reached without spotting a difference. */ + return 0; + /* Compare the label in a case-insenstive manner. */ + const unsigned char *endnameleft = nameleft + lenleft; + while (nameleft < endnameleft) + { + int l = *nameleft++; + int r = *nameright++; + if (l != r) + { + l = ascii_tolower (l); + r = ascii_tolower (r); + if (l != r) + return l - r; + } + } + } +} + +struct resolv_response_builder +{ + const unsigned char *query_buffer; + size_t query_length; + + size_t offset; /* Bytes written so far in buffer. */ + ns_sect section; /* Current section in the DNS packet. */ + unsigned int truncate_bytes; /* Bytes to remove at end of response. */ + bool drop; /* Discard generated response. */ + bool close; /* Close TCP client connection. */ + + /* Offset of the two-byte RDATA length field in the currently + written RDATA sub-structure. 0 if no RDATA is being written. */ + size_t current_rdata_offset; + + /* tsearch tree for locating targets for label compression. */ + void *compression_offsets; + + /* Must be last. Not zeroed for performance reasons. */ + unsigned char buffer[max_response_length]; +}; + +/* Response builder. */ + +void +resolv_response_init (struct resolv_response_builder *b, + struct resolv_response_flags flags) +{ + if (b->offset > 0) + FAIL_EXIT1 ("response_init: called at offset %zu", b->offset); + if (b->query_length < 12) + FAIL_EXIT1 ("response_init called for a query of size %zu", + b->query_length); + if (flags.rcode > 15) + FAIL_EXIT1 ("response_init: invalid RCODE %u", flags.rcode); + + /* Copy the transaction ID. */ + b->buffer[0] = b->query_buffer[0]; + b->buffer[1] = b->query_buffer[1]; + + /* Initialize the flags. */ + b->buffer[2] = 0x80; /* Mark as response. */ + b->buffer[2] |= b->query_buffer[2] & 0x01; /* Copy the RD bit. */ + if (flags.tc) + b->buffer[2] |= 0x02; + b->buffer[3] = 0x80 | flags.rcode; /* Always set RA. */ + + /* Fill in the initial section count values. */ + b->buffer[4] = flags.qdcount >> 8; + b->buffer[5] = flags.qdcount; + b->buffer[6] = flags.ancount >> 8; + b->buffer[7] = flags.ancount; + b->buffer[8] = flags.nscount >> 8; + b->buffer[9] = flags.nscount; + b->buffer[10] = flags.adcount >> 8; + b->buffer[11] = flags.adcount; + + b->offset = 12; +} + +void +resolv_response_section (struct resolv_response_builder *b, ns_sect section) +{ + if (b->offset == 0) + FAIL_EXIT1 ("resolv_response_section: response_init not called before"); + if (section < b->section) + FAIL_EXIT1 ("resolv_response_section: cannot go back to previous section"); + b->section = section; +} + +/* Add a single byte to B. */ +static inline void +response_add_byte (struct resolv_response_builder *b, unsigned char ch) +{ + if (b->offset == max_response_length) + FAIL_EXIT1 ("DNS response exceeds 64 KiB limit"); + b->buffer[b->offset] = ch; + ++b->offset; +} + +/* Add a 16-bit word VAL to B, in big-endian format. */ +static void +response_add_16 (struct resolv_response_builder *b, uint16_t val) +{ + response_add_byte (b, val >> 8); + response_add_byte (b, val); +} + +/* Increment the pers-section record counter in the packet header. */ +static void +response_count_increment (struct resolv_response_builder *b) +{ + unsigned int offset = b->section; + offset = 4 + 2 * offset; + ++b->buffer[offset + 1]; + if (b->buffer[offset + 1] == 0) + { + /* Carry. */ + ++b->buffer[offset]; + if (b->buffer[offset] == 0) + /* Overflow. */ + FAIL_EXIT1 ("too many records in section"); + } +} + +void +resolv_response_add_question (struct resolv_response_builder *b, + const char *name, uint16_t class, uint16_t type) +{ + if (b->offset == 0) + FAIL_EXIT1 ("resolv_response_add_question: " + "resolv_response_init not called"); + if (b->section != ns_s_qd) + FAIL_EXIT1 ("resolv_response_add_question: " + "must be called in the question section"); + + resolv_response_add_name (b, name); + response_add_16 (b, type); + response_add_16 (b, class); + + response_count_increment (b); +} + +void +resolv_response_add_name (struct resolv_response_builder *b, + const char *const origname) +{ + unsigned char encoded_name[NS_MAXDNAME]; + if (ns_name_pton (origname, encoded_name, sizeof (encoded_name)) < 0) + FAIL_EXIT1 ("ns_name_pton (\"%s\"): %m", origname); + + /* Copy the encoded name into the output buffer, apply compression + where possible. */ + for (const unsigned char *name = encoded_name; ;) + { + if (*name == '\0') + { + /* We have reached the end of the name. Add the terminating + NUL byte. */ + response_add_byte (b, '\0'); + break; + } + + /* Set to the compression target if compression is possible. */ + struct compressed_name *crname_target; + + /* Compression references can only reach the beginning of the + packet. */ + enum { compression_limit = 1 << 12 }; + + { + /* The trailing part of the name to be looked up in the tree + with the compression targets. */ + struct compressed_name *crname + = allocate_compressed_name (name, b->offset); + + if (b->offset < compression_limit) + { + /* Add the name to the tree, for future compression + references. */ + void **ptr = tsearch (crname, &b->compression_offsets, + compare_compressed_name); + if (ptr == NULL) + FAIL_EXIT1 ("tsearch out of memory"); + crname_target = *ptr; + + if (crname_target != crname) + /* The new name was not actually added to the tree. + Deallocate it. */ + free (crname); + else + /* Signal that the tree did not yet contain the name, + but keep the allocation because it is now part of the + tree. */ + crname_target = NULL; + } + else + { + /* This name cannot be reached by a compression reference. + No need to add it to the tree for future reference. */ + void **ptr = tfind (crname, &b->compression_offsets, + compare_compressed_name); + if (ptr != NULL) + crname_target = *ptr; + else + crname_target = NULL; + TEST_VERIFY (crname_target != crname); + /* Not added to the tree. */ + free (crname); + } + } + + if (crname_target != NULL) + { + /* The name is known. Reference the previous location. */ + unsigned int old_offset = crname_target->offset; + TEST_VERIFY_EXIT (old_offset < compression_limit); + response_add_byte (b, 0xC0 | (old_offset >> 8)); + response_add_byte (b, old_offset); + break; + } + else + { + /* The name is new. Add this label. */ + unsigned int len = 1 + *name; + resolv_response_add_data (b, name, len); + name += len; + } + } +} + +void +resolv_response_open_record (struct resolv_response_builder *b, + const char *name, + uint16_t class, uint16_t type, uint32_t ttl) +{ + if (b->section == ns_s_qd) + FAIL_EXIT1 ("resolv_response_open_record called in question section"); + if (b->current_rdata_offset != 0) + FAIL_EXIT1 ("resolv_response_open_record called with open record"); + + resolv_response_add_name (b, name); + response_add_16 (b, type); + response_add_16 (b, class); + response_add_16 (b, ttl >> 16); + response_add_16 (b, ttl); + + b->current_rdata_offset = b->offset; + /* Add room for the RDATA length. */ + response_add_16 (b, 0); +} + + +void +resolv_response_close_record (struct resolv_response_builder *b) +{ + size_t rdata_offset = b->current_rdata_offset; + if (rdata_offset == 0) + FAIL_EXIT1 ("response_close_record called without open record"); + size_t rdata_length = b->offset - rdata_offset - 2; + if (rdata_length > 65535) + FAIL_EXIT1 ("RDATA length %zu exceeds limit", rdata_length); + b->buffer[rdata_offset] = rdata_length >> 8; + b->buffer[rdata_offset + 1] = rdata_length; + response_count_increment (b); + b->current_rdata_offset = 0; +} + +void +resolv_response_add_data (struct resolv_response_builder *b, + const void *data, size_t length) +{ + size_t remaining = max_response_length - b->offset; + if (remaining < length) + FAIL_EXIT1 ("resolv_response_add_data: not enough room for %zu bytes", + length); + memcpy (b->buffer + b->offset, data, length); + b->offset += length; +} + +void +resolv_response_drop (struct resolv_response_builder *b) +{ + b->drop = true; +} + +void +resolv_response_close (struct resolv_response_builder *b) +{ + b->close = true; +} + +void +resolv_response_truncate_data (struct resolv_response_builder *b, size_t count) +{ + if (count > 65535) + FAIL_EXIT1 ("resolv_response_truncate_data: argument too large: %zu", + count); + b->truncate_bytes = count; +} + + +size_t +resolv_response_length (const struct resolv_response_builder *b) +{ + return b->offset; +} + +unsigned char * +resolv_response_buffer (const struct resolv_response_builder *b) +{ + unsigned char *result = xmalloc (b->offset); + memcpy (result, b->buffer, b->offset); + return result; +} + +static struct resolv_response_builder * +response_builder_allocate + (const unsigned char *query_buffer, size_t query_length) +{ + struct resolv_response_builder *b = xmalloc (sizeof (*b)); + memset (b, 0, offsetof (struct resolv_response_builder, buffer)); + b->query_buffer = query_buffer; + b->query_length = query_length; + return b; +} + +static void +response_builder_free (struct resolv_response_builder *b) +{ + tdestroy (b->compression_offsets, free); + free (b); +} + +/* DNS query processing. */ + +/* Data extracted from the question section of a DNS packet. */ +struct query_info +{ + char qname[MAXDNAME]; + uint16_t qclass; + uint16_t qtype; + struct resolv_edns_info edns; +}; + +/* Update *INFO from the specified DNS packet. */ +static void +parse_query (struct query_info *info, + const unsigned char *buffer, size_t length) +{ + HEADER hd; + _Static_assert (sizeof (hd) == 12, "DNS header size"); + if (length < sizeof (hd)) + FAIL_EXIT1 ("malformed DNS query: too short: %zu bytes", length); + memcpy (&hd, buffer, sizeof (hd)); + + if (ntohs (hd.qdcount) != 1) + FAIL_EXIT1 ("malformed DNS query: wrong question count: %d", + (int) ntohs (hd.qdcount)); + if (ntohs (hd.ancount) != 0) + FAIL_EXIT1 ("malformed DNS query: wrong answer count: %d", + (int) ntohs (hd.ancount)); + if (ntohs (hd.nscount) != 0) + FAIL_EXIT1 ("malformed DNS query: wrong authority count: %d", + (int) ntohs (hd.nscount)); + if (ntohs (hd.arcount) > 1) + FAIL_EXIT1 ("malformed DNS query: wrong additional count: %d", + (int) ntohs (hd.arcount)); + + int ret = dn_expand (buffer, buffer + length, buffer + sizeof (hd), + info->qname, sizeof (info->qname)); + if (ret < 0) + FAIL_EXIT1 ("malformed DNS query: cannot uncompress QNAME"); + + /* Obtain QTYPE and QCLASS. */ + size_t remaining = length - (12 + ret); + struct + { + uint16_t qtype; + uint16_t qclass; + } qtype_qclass; + if (remaining < sizeof (qtype_qclass)) + FAIL_EXIT1 ("malformed DNS query: " + "query lacks QCLASS/QTYPE, QNAME: %s", info->qname); + memcpy (&qtype_qclass, buffer + 12 + ret, sizeof (qtype_qclass)); + info->qclass = ntohs (qtype_qclass.qclass); + info->qtype = ntohs (qtype_qclass.qtype); + + memset (&info->edns, 0, sizeof (info->edns)); + if (ntohs (hd.arcount) > 0) + { + /* Parse EDNS record. */ + struct __attribute__ ((packed, aligned (1))) + { + uint8_t root; + uint16_t rtype; + uint16_t payload; + uint8_t edns_extended_rcode; + uint8_t edns_version; + uint16_t flags; + uint16_t rdatalen; + } rr; + _Static_assert (sizeof (rr) == 11, "EDNS record size"); + + if (remaining < 4 + sizeof (rr)) + FAIL_EXIT1 ("mailformed DNS query: no room for EDNS record"); + memcpy (&rr, buffer + 12 + ret + 4, sizeof (rr)); + if (rr.root != 0) + FAIL_EXIT1 ("malformed DNS query: invalid OPT RNAME: %d\n", rr.root); + if (rr.rtype != htons (41)) + FAIL_EXIT1 ("malformed DNS query: invalid OPT type: %d\n", + ntohs (rr.rtype)); + info->edns.active = true; + info->edns.extended_rcode = rr.edns_extended_rcode; + info->edns.version = rr.edns_version; + info->edns.flags = ntohs (rr.flags); + info->edns.payload_size = ntohs (rr.payload); + } +} + + +/* Main testing framework. */ + +/* Per-server information. One struct is allocated for each test + server. */ +struct resolv_test_server +{ + /* Local address of the server. UDP and TCP use the same port. */ + struct sockaddr_in address; + + /* File descriptor of the UDP server, or -1 if this server is + disabled. */ + int socket_udp; + + /* File descriptor of the TCP server, or -1 if this server is + disabled. */ + int socket_tcp; + + /* Counter of the number of responses processed so far. */ + size_t response_number; + + /* Thread handles for the server threads (if not disabled in the + configuration). */ + pthread_t thread_udp; + pthread_t thread_tcp; +}; + +/* Main struct for keeping track of libresolv redirection and + testing. */ +struct resolv_test +{ + /* After initialization, any access to the struct must be performed + while this lock is acquired. */ + pthread_mutex_t lock; + + /* Data for each test server. */ + struct resolv_test_server servers[resolv_max_test_servers]; + + /* Used if config.single_thread_udp is true. */ + pthread_t thread_udp_single; + + struct resolv_redirect_config config; + bool termination_requested; +}; + +/* Function implementing a server thread. */ +typedef void (*thread_callback) (struct resolv_test *, int server_index); + +/* Storage for thread-specific data, for passing to the + thread_callback function. */ +struct thread_closure +{ + struct resolv_test *obj; /* Current test object. */ + thread_callback callback; /* Function to call. */ + int server_index; /* Index of the implemented server. */ +}; + +/* Wrap response_callback as a function which can be passed to + pthread_create. */ +static void * +thread_callback_wrapper (void *arg) +{ + struct thread_closure *closure = arg; + closure->callback (closure->obj, closure->server_index); + free (closure); + return NULL; +} + +/* Start a server thread for the specified SERVER_INDEX, implemented + by CALLBACK. */ +static pthread_t +start_server_thread (struct resolv_test *obj, int server_index, + thread_callback callback) +{ + struct thread_closure *closure = xmalloc (sizeof (*closure)); + *closure = (struct thread_closure) + { + .obj = obj, + .callback = callback, + .server_index = server_index, + }; + return xpthread_create (NULL, thread_callback_wrapper, closure); +} + +/* Process one UDP query. Return false if a termination requested has + been detected. */ +static bool +server_thread_udp_process_one (struct resolv_test *obj, int server_index) +{ + unsigned char query[512]; + struct sockaddr_storage peer; + socklen_t peerlen = sizeof (peer); + size_t length = xrecvfrom (obj->servers[server_index].socket_udp, + query, sizeof (query), 0, + (struct sockaddr *) &peer, &peerlen); + /* Check for termination. */ + { + bool termination_requested; + xpthread_mutex_lock (&obj->lock); + termination_requested = obj->termination_requested; + xpthread_mutex_unlock (&obj->lock); + if (termination_requested) + return false; + } + + + struct query_info qinfo; + parse_query (&qinfo, query, length); + if (test_verbose > 0) + { + if (test_verbose > 1) + printf ("info: UDP server %d: incoming query:" + " %zd bytes, %s/%u/%u, tnxid=0x%02x%02x\n", + server_index, length, qinfo.qname, qinfo.qclass, qinfo.qtype, + query[0], query[1]); + else + printf ("info: UDP server %d: incoming query:" + " %zd bytes, %s/%u/%u\n", + server_index, length, qinfo.qname, qinfo.qclass, qinfo.qtype); + } + + struct resolv_response_context ctx = + { + .query_buffer = query, + .query_length = length, + .server_index = server_index, + .tcp = false, + .edns = qinfo.edns, + }; + struct resolv_response_builder *b = response_builder_allocate (query, length); + obj->config.response_callback + (&ctx, b, qinfo.qname, qinfo.qclass, qinfo.qtype); + + if (b->drop) + { + if (test_verbose) + printf ("info: UDP server %d: dropping response to %s/%u/%u\n", + server_index, qinfo.qname, qinfo.qclass, qinfo.qtype); + } + else + { + if (test_verbose) + { + if (b->offset >= 12) + printf ("info: UDP server %d: sending response:" + " %zu bytes, RCODE %d (for %s/%u/%u)\n", + server_index, b->offset, b->buffer[3] & 0x0f, + qinfo.qname, qinfo.qclass, qinfo.qtype); + else + printf ("info: UDP server %d: sending response: %zu bytes" + " (for %s/%u/%u)\n", + server_index, b->offset, + qinfo.qname, qinfo.qclass, qinfo.qtype); + if (b->truncate_bytes > 0) + printf ("info: truncated by %u bytes\n", b->truncate_bytes); + } + size_t to_send = b->offset; + if (to_send < b->truncate_bytes) + to_send = 0; + else + to_send -= b->truncate_bytes; + + /* Ignore most errors here because the other end may have closed + the socket. */ + if (sendto (obj->servers[server_index].socket_udp, + b->buffer, to_send, 0, + (struct sockaddr *) &peer, peerlen) < 0) + TEST_VERIFY_EXIT (errno != EBADF); + } + response_builder_free (b); + return true; +} + +/* UDP thread_callback function. Variant for one thread per + server. */ +static void +server_thread_udp (struct resolv_test *obj, int server_index) +{ + while (server_thread_udp_process_one (obj, server_index)) + ; +} + +/* Single-threaded UDP processing function, for the single_thread_udp + case. */ +static void * +server_thread_udp_single (void *closure) +{ + struct resolv_test *obj = closure; + + struct pollfd fds[resolv_max_test_servers]; + for (int server_index = 0; server_index < resolv_max_test_servers; + ++server_index) + if (obj->config.servers[server_index].disable_udp) + fds[server_index] = (struct pollfd) {.fd = -1}; + else + { + fds[server_index] = (struct pollfd) + { + .fd = obj->servers[server_index].socket_udp, + .events = POLLIN + }; + + /* Make the socket non-blocking. */ + int flags = fcntl (obj->servers[server_index].socket_udp, F_GETFL, 0); + if (flags < 0) + FAIL_EXIT1 ("fcntl (F_GETFL): %m"); + flags |= O_NONBLOCK; + if (fcntl (obj->servers[server_index].socket_udp, F_SETFL, flags) < 0) + FAIL_EXIT1 ("fcntl (F_SETFL): %m"); + } + + while (true) + { + xpoll (fds, resolv_max_test_servers, -1); + for (int server_index = 0; server_index < resolv_max_test_servers; + ++server_index) + if (fds[server_index].revents != 0) + { + if (!server_thread_udp_process_one (obj, server_index)) + goto out; + fds[server_index].revents = 0; + } + } + + out: + return NULL; +} + +/* Start the single UDP handler thread (for the single_thread_udp + case). */ +static void +start_server_thread_udp_single (struct resolv_test *obj) +{ + obj->thread_udp_single + = xpthread_create (NULL, server_thread_udp_single, obj); +} + +/* Data describing a TCP client connect. */ +struct tcp_thread_closure +{ + struct resolv_test *obj; + int server_index; + int client_socket; +}; + +/* Read a complete DNS query packet. If EOF_OK, an immediate + end-of-file condition is acceptable. */ +static bool +read_fully (int fd, void *buf, size_t len, bool eof_ok) +{ + const void *const end = buf + len; + while (buf < end) + { + ssize_t ret = read (fd, buf, end - buf); + if (ret == 0) + { + if (!eof_ok) + { + support_record_failure (); + printf ("error: unexpected EOF on TCP connection\n"); + } + return false; + } + else if (ret < 0) + { + if (!eof_ok || errno != ECONNRESET) + { + support_record_failure (); + printf ("error: TCP read: %m\n"); + } + return false; + } + buf += ret; + eof_ok = false; + } + return true; +} + +/* Write an array of iovecs. Terminate the process on failure. */ +static void +writev_fully (int fd, struct iovec *buffers, size_t count) +{ + while (count > 0) + { + /* Skip zero-length write requests. */ + if (buffers->iov_len == 0) + { + ++buffers; + --count; + continue; + } + /* Try to rewrite the remaing buffers. */ + ssize_t ret = writev (fd, buffers, count); + if (ret < 0) + FAIL_EXIT1 ("writev: %m"); + if (ret == 0) + FAIL_EXIT1 ("writev: invalid return value zero"); + /* Find the buffers that were successfully written. */ + while (ret > 0) + { + if (count == 0) + FAIL_EXIT1 ("internal writev consistency failure"); + /* Current buffer was partially written. */ + if (buffers->iov_len > (size_t) ret) + { + buffers->iov_base += ret; + buffers->iov_len -= ret; + ret = 0; + } + else + { + ret -= buffers->iov_len; + buffers->iov_len = 0; + ++buffers; + --count; + } + } + } +} + +/* Thread callback for handling a single established TCP connection to + a client. */ +static void * +server_thread_tcp_client (void *arg) +{ + struct tcp_thread_closure *closure = arg; + + while (true) + { + /* Read packet length. */ + uint16_t query_length; + if (!read_fully (closure->client_socket, + &query_length, sizeof (query_length), true)) + break; + query_length = ntohs (query_length); + + /* Read the packet. */ + unsigned char *query_buffer = xmalloc (query_length); + read_fully (closure->client_socket, query_buffer, query_length, false); + + struct query_info qinfo; + parse_query (&qinfo, query_buffer, query_length); + if (test_verbose > 0) + { + if (test_verbose > 1) + printf ("info: UDP server %d: incoming query:" + " %d bytes, %s/%u/%u, tnxid=0x%02x%02x\n", + closure->server_index, query_length, + qinfo.qname, qinfo.qclass, qinfo.qtype, + query_buffer[0], query_buffer[1]); + else + printf ("info: TCP server %d: incoming query:" + " %u bytes, %s/%u/%u\n", + closure->server_index, query_length, + qinfo.qname, qinfo.qclass, qinfo.qtype); + } + + struct resolv_response_context ctx = + { + .query_buffer = query_buffer, + .query_length = query_length, + .server_index = closure->server_index, + .tcp = true, + .edns = qinfo.edns, + }; + struct resolv_response_builder *b = response_builder_allocate + (query_buffer, query_length); + closure->obj->config.response_callback + (&ctx, b, qinfo.qname, qinfo.qclass, qinfo.qtype); + + if (b->drop) + { + if (test_verbose) + printf ("info: TCP server %d: dropping response to %s/%u/%u\n", + closure->server_index, + qinfo.qname, qinfo.qclass, qinfo.qtype); + } + else + { + if (test_verbose) + printf ("info: TCP server %d: sending response: %zu bytes" + " (for %s/%u/%u)\n", + closure->server_index, b->offset, + qinfo.qname, qinfo.qclass, qinfo.qtype); + uint16_t length = htons (b->offset); + size_t to_send = b->offset; + if (to_send < b->truncate_bytes) + to_send = 0; + else + to_send -= b->truncate_bytes; + struct iovec buffers[2] = + { + {&length, sizeof (length)}, + {b->buffer, to_send} + }; + writev_fully (closure->client_socket, buffers, 2); + } + bool close_flag = b->close; + response_builder_free (b); + free (query_buffer); + if (close_flag) + break; + } + + xclose (closure->client_socket); + free (closure); + return NULL; +} + +/* thread_callback for the TCP case. Accept connections and create a + new thread for each client. */ +static void +server_thread_tcp (struct resolv_test *obj, int server_index) +{ + while (true) + { + /* Get the client conenction. */ + int client_socket = xaccept + (obj->servers[server_index].socket_tcp, NULL, NULL); + + /* Check for termination. */ + xpthread_mutex_lock (&obj->lock); + if (obj->termination_requested) + { + xpthread_mutex_unlock (&obj->lock); + xclose (client_socket); + break; + } + xpthread_mutex_unlock (&obj->lock); + + /* Spawn a new thread for handling this connection. */ + struct tcp_thread_closure *closure = xmalloc (sizeof (*closure)); + *closure = (struct tcp_thread_closure) + { + .obj = obj, + .server_index = server_index, + .client_socket = client_socket, + }; + + pthread_t thr + = xpthread_create (NULL, server_thread_tcp_client, closure); + /* TODO: We should keep track of this thread so that we can + block in resolv_test_end until it has exited. */ + xpthread_detach (thr); + } +} + +/* Create UDP and TCP server sockets. */ +static void +make_server_sockets (struct resolv_test_server *server) +{ + while (true) + { + server->socket_udp = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + server->socket_tcp = xsocket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + + /* Pick the address for the UDP socket. */ + server->address = (struct sockaddr_in) + { + .sin_family = AF_INET, + .sin_addr = {.s_addr = htonl (INADDR_LOOPBACK)} + }; + xbind (server->socket_udp, + (struct sockaddr *)&server->address, sizeof (server->address)); + + /* Retrieve the address. */ + socklen_t addrlen = sizeof (server->address); + xgetsockname (server->socket_udp, + (struct sockaddr *)&server->address, &addrlen); + + /* Bind the TCP socket to the same address. */ + { + int on = 1; + xsetsockopt (server->socket_tcp, SOL_SOCKET, SO_REUSEADDR, + &on, sizeof (on)); + } + if (bind (server->socket_tcp, + (struct sockaddr *)&server->address, + sizeof (server->address)) != 0) + { + /* Port collision. The UDP bind succeeded, but the TCP BIND + failed. We assume here that the kernel will pick the + next local UDP address randomly. */ + if (errno == EADDRINUSE) + { + xclose (server->socket_udp); + xclose (server->socket_tcp); + continue; + } + FAIL_EXIT1 ("TCP bind: %m"); + } + xlisten (server->socket_tcp, 5); + break; + } +} + +/* Like make_server_sockets, but the caller supplies the address to + use. */ +static void +make_server_sockets_for_address (struct resolv_test_server *server, + const struct sockaddr *addr) +{ + server->socket_udp = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + server->socket_tcp = xsocket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (addr->sa_family == AF_INET) + server->address = *(const struct sockaddr_in *) addr; + else + /* We cannot store the server address in the socket. This should + not matter if disable_redirect is used. */ + server->address = (struct sockaddr_in) { .sin_family = 0, }; + + xbind (server->socket_udp, + (struct sockaddr *)&server->address, sizeof (server->address)); + xbind (server->socket_tcp, + (struct sockaddr *)&server->address, sizeof (server->address)); + xlisten (server->socket_tcp, 5); +} + +/* One-time initialization of NSS. */ +static void +resolv_redirect_once (void) +{ + /* Only use nss_dns. */ + __nss_configure_lookup ("hosts", "dns"); + __nss_configure_lookup ("networks", "dns"); + /* Enter a network namespace for isolation and firewall state + cleanup. The tests will still work if these steps fail, but they + may be less reliable. */ + support_become_root (); + support_enter_network_namespace (); +} +pthread_once_t resolv_redirect_once_var = PTHREAD_ONCE_INIT; + +void +resolv_test_init (void) +{ + /* Perform one-time initialization of NSS. */ + xpthread_once (&resolv_redirect_once_var, resolv_redirect_once); +} + +/* Copy the search path from CONFIG.search to the _res object. */ +static void +set_search_path (struct resolv_redirect_config config) +{ + memset (_res.defdname, 0, sizeof (_res.defdname)); + memset (_res.dnsrch, 0, sizeof (_res.dnsrch)); + + char *current = _res.defdname; + char *end = current + sizeof (_res.defdname); + + for (unsigned int i = 0; + i < sizeof (config.search) / sizeof (config.search[0]); ++i) + { + if (config.search[i] == NULL) + continue; + + size_t length = strlen (config.search[i]) + 1; + size_t remaining = end - current; + TEST_VERIFY_EXIT (length <= remaining); + memcpy (current, config.search[i], length); + _res.dnsrch[i] = current; + current += length; + } +} + +struct resolv_test * +resolv_test_start (struct resolv_redirect_config config) +{ + /* Apply configuration defaults. */ + if (config.nscount == 0) + config.nscount = resolv_max_test_servers; + + struct resolv_test *obj = xmalloc (sizeof (*obj)); + *obj = (struct resolv_test) { + .config = config, + .lock = PTHREAD_MUTEX_INITIALIZER, + }; + + if (!config.disable_redirect) + resolv_test_init (); + + /* Create all the servers, to reserve the necessary ports. */ + for (int server_index = 0; server_index < config.nscount; ++server_index) + if (config.disable_redirect && config.server_address_overrides != NULL) + make_server_sockets_for_address + (obj->servers + server_index, + config.server_address_overrides[server_index]); + else + make_server_sockets (obj->servers + server_index); + + /* Start server threads. Disable the server ports, as + requested. */ + for (int server_index = 0; server_index < config.nscount; ++server_index) + { + struct resolv_test_server *server = obj->servers + server_index; + if (config.servers[server_index].disable_udp) + { + xclose (server->socket_udp); + server->socket_udp = -1; + } + else if (!config.single_thread_udp) + server->thread_udp = start_server_thread (obj, server_index, + server_thread_udp); + if (config.servers[server_index].disable_tcp) + { + xclose (server->socket_tcp); + server->socket_tcp = -1; + } + else + server->thread_tcp = start_server_thread (obj, server_index, + server_thread_tcp); + } + if (config.single_thread_udp) + start_server_thread_udp_single (obj); + + if (config.disable_redirect) + return obj; + + int timeout = 1; + + /* Initialize libresolv. */ + TEST_VERIFY_EXIT (res_init () == 0); + + /* Disable IPv6 name server addresses. The code below only + overrides the IPv4 addresses. */ + __res_iclose (&_res, true); + _res._u._ext.nscount = 0; + + /* Redirect queries to the server socket. */ + if (test_verbose) + { + printf ("info: old timeout value: %d\n", _res.retrans); + printf ("info: old retry attempt value: %d\n", _res.retry); + printf ("info: old _res.options: 0x%lx\n", _res.options); + printf ("info: old _res.nscount value: %d\n", _res.nscount); + printf ("info: old _res.ndots value: %d\n", _res.ndots); + } + _res.retrans = timeout; + _res.retry = 4; + _res.nscount = config.nscount; + _res.options = RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH; + _res.ndots = 1; + if (test_verbose) + { + printf ("info: new timeout value: %d\n", _res.retrans); + printf ("info: new retry attempt value: %d\n", _res.retry); + printf ("info: new _res.options: 0x%lx\n", _res.options); + printf ("info: new _res.nscount value: %d\n", _res.nscount); + printf ("info: new _res.ndots value: %d\n", _res.ndots); + } + for (int server_index = 0; server_index < config.nscount; ++server_index) + { + TEST_VERIFY_EXIT (obj->servers[server_index].address.sin_port != 0); + _res.nsaddr_list[server_index] = obj->servers[server_index].address; + if (test_verbose) + { + char buf[256]; + TEST_VERIFY_EXIT + (inet_ntop (AF_INET, &obj->servers[server_index].address.sin_addr, + buf, sizeof (buf)) != NULL); + printf ("info: server %d: %s/%u\n", + server_index, buf, + htons (obj->servers[server_index].address.sin_port)); + } + } + + set_search_path (config); + + return obj; +} + +void +resolv_test_end (struct resolv_test *obj) +{ + res_close (); + + xpthread_mutex_lock (&obj->lock); + obj->termination_requested = true; + xpthread_mutex_unlock (&obj->lock); + + /* Send trigger packets to unblock the server threads. */ + for (int server_index = 0; server_index < obj->config.nscount; + ++server_index) + { + if (!obj->config.servers[server_index].disable_udp) + { + int sock = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + xsendto (sock, "", 1, 0, + (struct sockaddr *) &obj->servers[server_index].address, + sizeof (obj->servers[server_index].address)); + xclose (sock); + } + if (!obj->config.servers[server_index].disable_tcp) + { + int sock = xsocket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + xconnect (sock, + (struct sockaddr *) &obj->servers[server_index].address, + sizeof (obj->servers[server_index].address)); + xclose (sock); + } + } + + if (obj->config.single_thread_udp) + xpthread_join (obj->thread_udp_single); + + /* Wait for the server threads to terminate. */ + for (int server_index = 0; server_index < obj->config.nscount; + ++server_index) + { + if (!obj->config.servers[server_index].disable_udp) + { + if (!obj->config.single_thread_udp) + xpthread_join (obj->servers[server_index].thread_udp); + xclose (obj->servers[server_index].socket_udp); + } + if (!obj->config.servers[server_index].disable_tcp) + { + xpthread_join (obj->servers[server_index].thread_tcp); + xclose (obj->servers[server_index].socket_tcp); + } + } + + free (obj); +} diff --git a/support/resolv_test.h b/support/resolv_test.h new file mode 100644 index 0000000000..4c2e6c1b41 --- /dev/null +++ b/support/resolv_test.h @@ -0,0 +1,190 @@ +/* DNS test framework and libresolv redirection. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_RESOLV_TEST_H +#define SUPPORT_RESOLV_TEST_H + +#include +#include +#include + +__BEGIN_DECLS + +/* Information about EDNS properties of a DNS query. */ +struct resolv_edns_info +{ + bool active; + uint8_t extended_rcode; + uint8_t version; + uint16_t flags; + uint16_t payload_size; +}; + +/* This struct provides context information when the response callback + specified in struct resolv_redirect_config is invoked. */ +struct resolv_response_context +{ + const unsigned char *query_buffer; + size_t query_length; + int server_index; + bool tcp; + struct resolv_edns_info edns; +}; + +/* This opaque struct is used to construct responses from within the + response callback function. */ +struct resolv_response_builder; + +/* This opaque struct collects information about the resolver testing + currently in progress. */ +struct resolv_test; + +enum + { + /* Maximum number of test servers supported by the framework. */ + resolv_max_test_servers = 3, + }; + +/* Configuration settings specific to individual test servers. */ +struct resolv_redirect_server_config +{ + bool disable_tcp; /* If true, no TCP server is listening. */ + bool disable_udp; /* If true, no UDP server is listening. */ +}; + +/* Instructions for setting up the libresolv redirection. */ +struct resolv_redirect_config +{ + /* The response_callback function is called for every incoming DNS + packet, over UDP or TCP. It must be specified, the other + configuration settings are optional. */ + void (*response_callback) (const struct resolv_response_context *, + struct resolv_response_builder *, + const char *qname, + uint16_t qclass, uint16_t qtype); + + /* Per-server configuration. */ + struct resolv_redirect_server_config servers[resolv_max_test_servers]; + + /* Search path entries. The first entry serves as the default + domain name as well. */ + const char *search[7]; + + /* Number of servers to activate in resolv. 0 means the default, + resolv_max_test_servers. */ + int nscount; + + /* If true, use a single thread to process all UDP queries. This + may results in more predictable ordering of queries and + responses. */ + bool single_thread_udp; + + /* Do not rewrite the _res variable or change NSS defaults. Use + server_address_overrides below to tell the testing framework on + which addresses to create the servers. */ + bool disable_redirect; + + /* Use these addresses for creating the DNS servers. The array must + have ns_count (or resolv_max_test_servers) sockaddr * elements if + not NULL. */ + const struct sockaddr *const *server_address_overrides; +}; + +/* Configure NSS to use, nss_dns only for aplicable databases, and try + to put the process into a network namespace for better isolation. + This may have to be called before resolv_test_start, before the + process creates any threads. Otherwise, initialization is + performed by resolv_test_start implicitly. */ +void resolv_test_init (void); + +/* Initiate resolver testing. This updates the _res variable as + needed. As a side effect, NSS is reconfigured to use nss_dns only + for aplicable databases, and the process may enter a network + namespace for better isolation. */ +struct resolv_test *resolv_test_start (struct resolv_redirect_config); + +/* Call this function at the end of resolver testing, to free + resources and report pending errors (if any). */ +void resolv_test_end (struct resolv_test *); + +/* The remaining facilities in this file are used for constructing + response packets from the response_callback function. */ + +/* Special settings for constructing responses from the callback. */ +struct resolv_response_flags +{ + /* 4-bit response code to incorporate into the response. */ + unsigned char rcode; + + /* If true, the TC (truncation) flag will be set. */ + bool tc; + + /* Initial section count values. Can be used to artificially + increase the counts, for malformed packet testing.*/ + unsigned short qdcount; + unsigned short ancount; + unsigned short nscount; + unsigned short adcount; +}; + +/* Begin a new response with the requested flags. Must be called + first. */ +void resolv_response_init (struct resolv_response_builder *, + struct resolv_response_flags); + +/* Switches to the section in the response packet. Only forward + movement is supported. */ +void resolv_response_section (struct resolv_response_builder *, ns_sect); + +/* Add a question record to the question section. */ +void resolv_response_add_question (struct resolv_response_builder *, + const char *name, uint16_t class, + uint16_t type); +/* Starts a new resource record with the specified owner name, class, + type, and TTL. Data is supplied with resolv_response_add_data or + resolv_response_add_name. */ +void resolv_response_open_record (struct resolv_response_builder *, + const char *name, uint16_t class, + uint16_t type, uint32_t ttl); + +/* Add unstructed bytes to the RDATA part of a resource record. */ +void resolv_response_add_data (struct resolv_response_builder *, + const void *, size_t); + +/* Add a compressed domain name to the RDATA part of a resource + record. */ +void resolv_response_add_name (struct resolv_response_builder *, + const char *name); + +/* Mark the end of the constructed record. Must be called last. */ +void resolv_response_close_record (struct resolv_response_builder *); + +/* Drop this query packet (that is, do not send a response, not even + an empty packet). */ +void resolv_response_drop (struct resolv_response_builder *); + +/* In TCP mode, close the connection after this packet (if a response + is sent). */ +void resolv_response_close (struct resolv_response_builder *); + +/* The size of the response packet built so far. */ +size_t resolv_response_length (const struct resolv_response_builder *); + +__END_DECLS + +#endif /* SUPPORT_RESOLV_TEST_H */ diff --git a/support/run_diff.h b/support/run_diff.h new file mode 100644 index 0000000000..6e949226fa --- /dev/null +++ b/support/run_diff.h @@ -0,0 +1,31 @@ +/* Invoke the system diff tool to compare two strings. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_RUN_DIFF_H +#define SUPPORT_RUN_DIFF_H + +/* Compare the two NUL-terminated strings LEFT and RIGHT using the + diff tool. Label the sides of the diff with LEFT_LABEL and + RIGHT_LABEL, respectively. + + This function assumes that LEFT and RIGHT are different + strings. */ +void support_run_diff (const char *left_label, const char *left, + const char *right_label, const char *right); + +#endif /* SUPPORT_RUN_DIFF_H */ diff --git a/support/set_fortify_handler.c b/support/set_fortify_handler.c new file mode 100644 index 0000000000..c2dacbb179 --- /dev/null +++ b/support/set_fortify_handler.c @@ -0,0 +1,34 @@ +/* Set signal handler for use in fortify tests. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include + +void +set_fortify_handler (void (*handler) (int sig)) +{ + struct sigaction sa; + + sa.sa_handler = handler; + sa.sa_flags = 0; + sigemptyset (&sa.sa_mask); + + sigaction (SIGABRT, &sa, NULL); + ignore_stderr (); +} diff --git a/support/support-xfstat.c b/support/support-xfstat.c new file mode 100644 index 0000000000..f69253af09 --- /dev/null +++ b/support/support-xfstat.c @@ -0,0 +1,28 @@ +/* fstat64 with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +void +xfstat (int fd, struct stat64 *result) +{ + if (fstat64 (fd, result) != 0) + FAIL_EXIT1 ("fstat64 (%d): %m", fd); +} diff --git a/support/support-xstat.c b/support/support-xstat.c new file mode 100644 index 0000000000..fc10c6dcb7 --- /dev/null +++ b/support/support-xstat.c @@ -0,0 +1,30 @@ +/* stat64 with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* NB: Non-standard file name to avoid sysdeps override for xstat. */ + +#include +#include +#include + +void +xstat (const char *path, struct stat64 *result) +{ + if (stat64 (path, result) != 0) + FAIL_EXIT1 ("stat64 (\"%s\"): %m", path); +} diff --git a/support/support.h b/support/support.h new file mode 100644 index 0000000000..bc5827ed87 --- /dev/null +++ b/support/support.h @@ -0,0 +1,75 @@ +/* Common extra functions. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* This header file should only contain definitions compatible with + C90. (Using __attribute__ is fine because provides a + fallback.) */ + +#ifndef SUPPORT_H +#define SUPPORT_H + +#include +#include + +__BEGIN_DECLS + +/* Write a message to standard output. Can be used in signal + handlers. */ +void write_message (const char *message) __attribute__ ((nonnull (1))); + +/* Avoid all the buffer overflow messages on stderr. */ +void ignore_stderr (void); + +/* Set fortification error handler. Used when tests want to verify that bad + code is caught by the library. */ +void set_fortify_handler (void (*handler) (int sig)); + +/* Report an out-of-memory error for the allocation of SIZE bytes in + FUNCTION, terminating the process. */ +void oom_error (const char *function, size_t size) + __attribute__ ((nonnull (1))); + +/* Return a pointer to a memory region of SIZE bytes. The memory is + initialized to zero and will be shared with subprocesses (across + fork). The returned pointer must be freed using + support_shared_free; it is not compatible with the malloc + functions. */ +void *support_shared_allocate (size_t size); + +/* Deallocate a pointer returned by support_shared_allocate. */ +void support_shared_free (void *); + +/* Write CONTENTS to the file PATH. Create or truncate the file as + needed. The file mode is 0666 masked by the umask. Terminate the + process on error. */ +void support_write_file_string (const char *path, const char *contents); + +/* Error-checking wrapper functions which terminate the process on + error. */ + +void *xmalloc (size_t) __attribute__ ((malloc)); +void *xcalloc (size_t n, size_t s) __attribute__ ((malloc)); +void *xrealloc (void *p, size_t n); +char *xasprintf (const char *format, ...) + __attribute__ ((format (printf, 1, 2), malloc)); +char *xstrdup (const char *); +char *xstrndup (const char *, size_t); + +__END_DECLS + +#endif /* SUPPORT_H */ diff --git a/support/support_become_root.c b/support/support_become_root.c new file mode 100644 index 0000000000..6947dbaa80 --- /dev/null +++ b/support/support_become_root.c @@ -0,0 +1,102 @@ +/* Acquire root privileges. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CLONE_NEWUSER +/* The necessary steps to allow file creation in user namespaces. */ +static void +setup_uid_gid_mapping (uid_t original_uid, gid_t original_gid) +{ + int fd = open64 ("/proc/self/uid_map", O_WRONLY); + if (fd < 0) + { + printf ("warning: could not open /proc/self/uid_map: %m\n" + "warning: file creation may not be possible\n"); + return; + } + + /* We map our original UID to the same UID in the container so we + own our own files normally. Without that, file creation could + fail with EOVERFLOW (sic!). */ + char buf[100]; + int ret = snprintf (buf, sizeof (buf), "%llu %llu 1\n", + (unsigned long long) original_uid, + (unsigned long long) original_uid); + TEST_VERIFY_EXIT (ret < sizeof (buf)); + xwrite (fd, buf, ret); + xclose (fd); + + /* Linux 3.19 introduced the setgroups file. We need write "deny" to this + file otherwise writing to gid_map will fail with EPERM. */ + fd = open64 ("/proc/self/setgroups", O_WRONLY, 0); + if (fd < 0) + { + if (errno != ENOENT) + FAIL_EXIT1 ("open64 (\"/proc/self/setgroups\", 0x%x, 0%o): %m", + O_WRONLY, 0); + /* This kernel doesn't expose the setgroups file so simply move on. */ + } + else + { + xwrite (fd, "deny\n", strlen ("deny\n")); + xclose (fd); + } + + /* Now map our own GID, like we did for the user ID. */ + fd = xopen ("/proc/self/gid_map", O_WRONLY, 0); + ret = snprintf (buf, sizeof (buf), "%llu %llu 1\n", + (unsigned long long) original_gid, + (unsigned long long) original_gid); + TEST_VERIFY_EXIT (ret < sizeof (buf)); + xwrite (fd, buf, ret); + xclose (fd); +} +#endif /* CLONE_NEWUSER */ + +bool +support_become_root (void) +{ +#ifdef CLONE_NEWUSER + uid_t original_uid = getuid (); + gid_t original_gid = getgid (); + + if (unshare (CLONE_NEWUSER | CLONE_NEWNS) == 0) + { + setup_uid_gid_mapping (original_uid, original_gid); + /* Even if we do not have UID zero, we have extended privileges at + this point. */ + return true; + } +#endif + if (setuid (0) != 0) + { + printf ("warning: could not become root outside namespace (%m)\n"); + return false; + } + return true; +} diff --git a/support/support_can_chroot.c b/support/support_can_chroot.c new file mode 100644 index 0000000000..8922576d19 --- /dev/null +++ b/support/support_can_chroot.c @@ -0,0 +1,65 @@ +/* Return true if the process can perform a chroot operation. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static void +callback (void *closure) +{ + int *result = closure; + struct stat64 before; + xstat ("/dev", &before); + if (chroot ("/dev") != 0) + { + *result = errno; + return; + } + struct stat64 after; + xstat ("/", &after); + TEST_VERIFY (before.st_dev == after.st_dev); + TEST_VERIFY (before.st_ino == after.st_ino); + *result = 0; +} + +bool +support_can_chroot (void) +{ + int *result = support_shared_allocate (sizeof (*result)); + *result = 0; + support_isolate_in_subprocess (callback, result); + bool ok = *result == 0; + if (!ok) + { + static bool already_warned; + if (!already_warned) + { + already_warned = true; + errno = *result; + printf ("warning: this process does not support chroot: %m\n"); + } + } + support_shared_free (result); + return ok; +} diff --git a/support/support_capture_subprocess.c b/support/support_capture_subprocess.c new file mode 100644 index 0000000000..6d2029e13b --- /dev/null +++ b/support/support_capture_subprocess.c @@ -0,0 +1,108 @@ +/* Capture output from a subprocess. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include +#include + +static void +transfer (const char *what, struct pollfd *pfd, struct xmemstream *stream) +{ + if (pfd->revents != 0) + { + char buf[1024]; + ssize_t ret = TEMP_FAILURE_RETRY (read (pfd->fd, buf, sizeof (buf))); + if (ret < 0) + { + support_record_failure (); + printf ("error: reading from subprocess %s: %m", what); + pfd->events = 0; + pfd->revents = 0; + } + else if (ret == 0) + { + /* EOF reached. Stop listening. */ + pfd->events = 0; + pfd->revents = 0; + } + else + /* Store the data just read. */ + TEST_VERIFY (fwrite (buf, ret, 1, stream->out) == 1); + } +} + +struct support_capture_subprocess +support_capture_subprocess (void (*callback) (void *), void *closure) +{ + struct support_capture_subprocess result; + xopen_memstream (&result.out); + xopen_memstream (&result.err); + + int stdout_pipe[2]; + xpipe (stdout_pipe); + int stderr_pipe[2]; + xpipe (stderr_pipe); + + TEST_VERIFY (fflush (stdout) == 0); + TEST_VERIFY (fflush (stderr) == 0); + + pid_t pid = xfork (); + if (pid == 0) + { + xclose (stdout_pipe[0]); + xclose (stderr_pipe[0]); + xdup2 (stdout_pipe[1], STDOUT_FILENO); + xdup2 (stderr_pipe[1], STDERR_FILENO); + callback (closure); + _exit (0); + } + xclose (stdout_pipe[1]); + xclose (stderr_pipe[1]); + + struct pollfd fds[2] = + { + { .fd = stdout_pipe[0], .events = POLLIN }, + { .fd = stderr_pipe[0], .events = POLLIN }, + }; + + do + { + xpoll (fds, 2, -1); + transfer ("stdout", &fds[0], &result.out); + transfer ("stderr", &fds[1], &result.err); + } + while (fds[0].events != 0 || fds[1].events != 0); + xclose (stdout_pipe[0]); + xclose (stderr_pipe[0]); + + xfclose_memstream (&result.out); + xfclose_memstream (&result.err); + xwaitpid (pid, &result.status, 0); + return result; +} + +void +support_capture_subprocess_free (struct support_capture_subprocess *p) +{ + free (p->out.buffer); + free (p->err.buffer); +} diff --git a/support/support_capture_subprocess_check.c b/support/support_capture_subprocess_check.c new file mode 100644 index 0000000000..ff5ee89fb0 --- /dev/null +++ b/support/support_capture_subprocess_check.c @@ -0,0 +1,67 @@ +/* Verify capture output from a subprocess. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +static void +print_context (const char *context, bool *failed) +{ + if (*failed) + /* Do not duplicate message. */ + return; + support_record_failure (); + printf ("error: subprocess failed: %s\n", context); +} + +void +support_capture_subprocess_check (struct support_capture_subprocess *proc, + const char *context, int status, + int allowed) +{ + TEST_VERIFY ((allowed & sc_allow_none) + || (allowed & sc_allow_stdout) + || (allowed & sc_allow_stderr)); + TEST_VERIFY (!((allowed & sc_allow_none) + && ((allowed & sc_allow_stdout) + || (allowed & sc_allow_stderr)))); + + bool failed = false; + if (proc->status != status) + { + print_context (context, &failed); + printf ("error: expected exit status: %d\n", status); + printf ("error: actual exit status: %d\n", proc->status); + } + if (!(allowed & sc_allow_stdout) && proc->out.length != 0) + { + print_context (context, &failed); + printf ("error: unexpected output from subprocess\n"); + fwrite (proc->out.buffer, proc->out.length, 1, stdout); + puts ("\n"); + } + if (!(allowed & sc_allow_stderr) && proc->err.length != 0) + { + print_context (context, &failed); + printf ("error: unexpected error output from subprocess\n"); + fwrite (proc->err.buffer, proc->err.length, 1, stdout); + puts ("\n"); + } +} diff --git a/support/support_chroot.c b/support/support_chroot.c new file mode 100644 index 0000000000..6356b1af6c --- /dev/null +++ b/support/support_chroot.c @@ -0,0 +1,81 @@ +/* Setup a chroot environment for use within tests. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include + +/* If CONTENTS is not NULL, write it to the file at DIRECTORY/RELPATH, + and store the name in *ABSPATH. If CONTENTS is NULL, store NULL in + *ABSPATH. */ +static void +write_file (const char *directory, const char *relpath, const char *contents, + char **abspath) +{ + if (contents != NULL) + { + *abspath = xasprintf ("%s/%s", directory, relpath); + add_temp_file (*abspath); + support_write_file_string (*abspath, contents); + } + else + *abspath = NULL; +} + +struct support_chroot * +support_chroot_create (struct support_chroot_configuration conf) +{ + struct support_chroot *chroot = xmalloc (sizeof (*chroot)); + chroot->path_chroot = support_create_temp_directory ("tst-resolv-res_init-"); + + /* Create the /etc directory in the chroot environment. */ + char *path_etc = xasprintf ("%s/etc", chroot->path_chroot); + xmkdir (path_etc, 0777); + add_temp_file (path_etc); + + write_file (path_etc, "resolv.conf", conf.resolv_conf, + &chroot->path_resolv_conf); + write_file (path_etc, "hosts", conf.hosts, &chroot->path_hosts); + write_file (path_etc, "host.conf", conf.host_conf, &chroot->path_host_conf); + + free (path_etc); + + /* valgrind needs a temporary directory in the chroot. */ + { + char *path_tmp = xasprintf ("%s/tmp", chroot->path_chroot); + xmkdir (path_tmp, 0777); + add_temp_file (path_tmp); + free (path_tmp); + } + + return chroot; +} + +void +support_chroot_free (struct support_chroot *chroot) +{ + free (chroot->path_chroot); + free (chroot->path_resolv_conf); + free (chroot->path_hosts); + free (chroot->path_host_conf); + free (chroot); +} diff --git a/support/support_enter_mount_namespace.c b/support/support_enter_mount_namespace.c new file mode 100644 index 0000000000..ba68e990f2 --- /dev/null +++ b/support/support_enter_mount_namespace.c @@ -0,0 +1,47 @@ +/* Enter a mount namespace. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#ifdef CLONE_NEWNS +# include +#endif /* CLONE_NEWNS */ + +bool +support_enter_mount_namespace (void) +{ +#ifdef CLONE_NEWNS + if (unshare (CLONE_NEWNS) == 0) + { + /* On some systems, / is marked as MS_SHARED, which means that + mounts within the namespace leak to the rest of the system, + which is not what we want. */ + if (mount ("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0) + { + printf ("warning: making the mount namespace private failed: %m\n"); + return false; + } + return true; + } + else + printf ("warning: unshare (CLONE_NEWNS) failed: %m\n"); +#endif /* CLONE_NEWNS */ + return false; +} diff --git a/support/support_enter_network_namespace.c b/support/support_enter_network_namespace.c new file mode 100644 index 0000000000..1d874df885 --- /dev/null +++ b/support/support_enter_network_namespace.c @@ -0,0 +1,75 @@ +/* Enter a network namespace. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool in_uts_namespace; + +bool +support_enter_network_namespace (void) +{ +#ifdef CLONE_NEWUTS + if (unshare (CLONE_NEWUTS) == 0) + in_uts_namespace = true; + else + printf ("warning: unshare (CLONE_NEWUTS) failed: %m\n"); +#endif + +#ifdef CLONE_NEWNET + if (unshare (CLONE_NEWNET) == 0) + { + /* Bring up the loopback interface. */ + int fd = xsocket (AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); + struct ifreq req; + strcpy (req.ifr_name, "lo"); + TEST_VERIFY_EXIT (ioctl (fd, SIOCGIFFLAGS, &req) == 0); + bool already_up = req.ifr_flags & IFF_UP; + if (already_up) + /* This means that we likely have not achieved isolation from + the parent namespace. */ + printf ("warning: loopback interface already exists" + " in new network namespace\n"); + else + { + req.ifr_flags |= IFF_UP | IFF_RUNNING; + TEST_VERIFY_EXIT (ioctl (fd, SIOCSIFFLAGS, &req) == 0); + } + xclose (fd); + + return !already_up; + } +#endif + printf ("warning: could not enter network namespace\n"); + return false; +} + +bool +support_in_uts_namespace (void) +{ + return in_uts_namespace; +} diff --git a/support/support_format_address_family.c b/support/support_format_address_family.c new file mode 100644 index 0000000000..2acb9afffd --- /dev/null +++ b/support/support_format_address_family.c @@ -0,0 +1,35 @@ +/* Convert an address family to a string. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include + +char * +support_format_address_family (int family) +{ + switch (family) + { + case AF_INET: + return xstrdup ("INET"); + case AF_INET6: + return xstrdup ("INET6"); + default: + return xasprintf ("", family); + } +} diff --git a/support/support_format_addrinfo.c b/support/support_format_addrinfo.c new file mode 100644 index 0000000000..c5e00e516a --- /dev/null +++ b/support/support_format_addrinfo.c @@ -0,0 +1,240 @@ +/* Convert struct addrinfo values to a string. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include +#include +#include + +static size_t +socket_address_length (int family) +{ + switch (family) + { + case AF_INET: + return sizeof (struct sockaddr_in); + case AF_INET6: + return sizeof (struct sockaddr_in6); + default: + return -1; + } +} + +static void +format_ai_flags_1 (FILE *out, struct addrinfo *ai, int flag, const char *name, + int * flags_printed) +{ + if ((ai->ai_flags & flag) != 0) + fprintf (out, " %s", name); + *flags_printed |= flag; +} + +static void +format_ai_flags (FILE *out, struct addrinfo *ai) +{ + if (ai == NULL) + return; + + if (ai->ai_flags != 0) + { + fprintf (out, "flags:"); + int flags_printed = 0; +#define FLAG(flag) format_ai_flags_1 (out, ai, flag, #flag, &flags_printed) + FLAG (AI_PASSIVE); + FLAG (AI_CANONNAME); + FLAG (AI_NUMERICHOST); + FLAG (AI_V4MAPPED); + FLAG (AI_ALL); + FLAG (AI_ADDRCONFIG); + FLAG (AI_IDN); + FLAG (AI_CANONIDN); + FLAG (AI_IDN_ALLOW_UNASSIGNED); + FLAG (AI_IDN_USE_STD3_ASCII_RULES); + FLAG (AI_NUMERICSERV); +#undef FLAG + int remaining = ai->ai_flags & ~flags_printed; + if (remaining != 0) + fprintf (out, " %08x", remaining); + fprintf (out, "\n"); + } + + /* Report flag mismatches within the list. */ + int flags = ai->ai_flags; + int index = 1; + ai = ai->ai_next; + while (ai != NULL) + { + if (ai->ai_flags != flags) + fprintf (out, "error: flags at %d: 0x%x expected, 0x%x actual\n", + index, flags, ai->ai_flags); + ai = ai->ai_next; + ++index; + } +} + +static void +format_ai_canonname (FILE *out, struct addrinfo *ai) +{ + if (ai == NULL) + return; + if (ai->ai_canonname != NULL) + fprintf (out, "canonname: %s\n", ai->ai_canonname); + + /* Report incorrectly set ai_canonname fields on subsequent list + entries. */ + int index = 1; + ai = ai->ai_next; + while (ai != NULL) + { + if (ai->ai_canonname != NULL) + fprintf (out, "error: canonname set at %d: %s\n", + index, ai->ai_canonname); + ai = ai->ai_next; + ++index; + } +} + +static void +format_ai_one (FILE *out, struct addrinfo *ai) +{ + { + char type_buf[32]; + const char *type_str; + char proto_buf[32]; + const char *proto_str; + + /* ai_socktype */ + switch (ai->ai_socktype) + { + case SOCK_RAW: + type_str = "RAW"; + break; + case SOCK_DGRAM: + type_str = "DGRAM"; + break; + case SOCK_STREAM: + type_str = "STREAM"; + break; + default: + snprintf (type_buf, sizeof (type_buf), "%d", ai->ai_socktype); + type_str = type_buf; + } + + /* ai_protocol */ + switch (ai->ai_protocol) + { + case IPPROTO_IP: + proto_str = "IP"; + break; + case IPPROTO_UDP: + proto_str = "UDP"; + break; + case IPPROTO_TCP: + proto_str = "TCP"; + break; + default: + snprintf (proto_buf, sizeof (proto_buf), "%d", ai->ai_protocol); + proto_str = proto_buf; + } + fprintf (out, "address: %s/%s", type_str, proto_str); + } + + /* ai_addrlen */ + if (ai->ai_addrlen != socket_address_length (ai->ai_family)) + { + char *family = support_format_address_family (ai->ai_family); + fprintf (out, "error: invalid address length %d for %s\n", + ai->ai_addrlen, family); + free (family); + } + + /* ai_addr */ + { + char buf[128]; + uint16_t port; + const char *ret; + switch (ai->ai_family) + { + case AF_INET: + { + struct sockaddr_in *sin = (struct sockaddr_in *) ai->ai_addr; + ret = inet_ntop (AF_INET, &sin->sin_addr, buf, sizeof (buf)); + port = sin->sin_port; + } + break; + case AF_INET6: + { + struct sockaddr_in6 *sin = (struct sockaddr_in6 *) ai->ai_addr; + ret = inet_ntop (AF_INET6, &sin->sin6_addr, buf, sizeof (buf)); + port = sin->sin6_port; + } + break; + default: + errno = EAFNOSUPPORT; + ret = NULL; + } + if (ret == NULL) + fprintf (out, "error: inet_top failed: %m\n"); + else + fprintf (out, " %s %u\n", buf, ntohs (port)); + } +} + +/* Format all the addresses in one address family. */ +static void +format_ai_family (FILE *out, struct addrinfo *ai, int family) +{ + while (ai) + { + if (ai->ai_family == family) + format_ai_one (out, ai); + ai = ai->ai_next; + } +} + +char * +support_format_addrinfo (struct addrinfo *ai, int ret) +{ + int errno_copy = errno; + + struct xmemstream mem; + xopen_memstream (&mem); + if (ret != 0) + { + fprintf (mem.out, "error: %s\n", gai_strerror (ret)); + if (ret == EAI_SYSTEM) + { + errno = errno_copy; + fprintf (mem.out, "error: %m\n"); + } + } + else + { + format_ai_flags (mem.out, ai); + format_ai_canonname (mem.out, ai); + format_ai_family (mem.out, ai, AF_INET); + format_ai_family (mem.out, ai, AF_INET6); + } + + xfclose_memstream (&mem); + return mem.buffer; +} diff --git a/support/support_format_dns_packet.c b/support/support_format_dns_packet.c new file mode 100644 index 0000000000..1170eafb0f --- /dev/null +++ b/support/support_format_dns_packet.c @@ -0,0 +1,223 @@ +/* Convert a DNS packet to a human-readable representation. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include +#include +#include + +struct in_buffer +{ + const unsigned char *data; + size_t size; +}; + +static inline bool +extract_8 (struct in_buffer *in, unsigned char *value) +{ + if (in->size == 0) + return false; + *value = in->data[0]; + ++in->data; + --in->size; + return true; +} + +static inline bool +extract_16 (struct in_buffer *in, unsigned short *value) +{ + if (in->size < 2) + return false; + *value = (in->data[0] << 8) | in->data[1]; + in->data += 2; + in->size -= 2; + return true; +} + +static inline bool +extract_32 (struct in_buffer *in, unsigned *value) +{ + if (in->size < 4) + return false; + unsigned a = in->data[0]; + unsigned b = in->data[1]; + unsigned c = in->data[2]; + unsigned d = in->data[3]; + *value = (a << 24) | (b << 16) | (c << 8) | d; + in->data += 4; + in->size -= 4; + return true; +} + +static inline bool +extract_bytes (struct in_buffer *in, size_t length, struct in_buffer *value) +{ + if (in->size < length) + return false; + *value = (struct in_buffer) {in->data, length}; + in->data += length; + in->size -= length; + return true; +} + +struct dname +{ + char name[MAXDNAME + 1]; +}; + +static bool +extract_name (struct in_buffer full, struct in_buffer *in, struct dname *value) +{ + const unsigned char *full_end = full.data + full.size; + /* Sanity checks; these indicate buffer misuse. */ + TEST_VERIFY_EXIT + (!(in->data < full.data || in->data > full_end + || in->size > (size_t) (full_end - in->data))); + int ret = dn_expand (full.data, full_end, in->data, + value->name, sizeof (value->name)); + if (ret < 0) + return false; + in->data += ret; + in->size -= ret; + return true; +} + +char * +support_format_dns_packet (const unsigned char *buffer, size_t length) +{ + struct in_buffer full = { buffer, length }; + struct in_buffer in = full; + struct xmemstream mem; + xopen_memstream (&mem); + + unsigned short txnid; + unsigned short flags; + unsigned short qdcount; + unsigned short ancount; + unsigned short nscount; + unsigned short adcount; + if (!(extract_16 (&in, &txnid) + && extract_16 (&in, &flags) + && extract_16 (&in, &qdcount) + && extract_16 (&in, &ancount) + && extract_16 (&in, &nscount) + && extract_16 (&in, &adcount))) + { + fprintf (mem.out, "error: could not parse DNS header\n"); + goto out; + } + if (qdcount != 1) + { + fprintf (mem.out, "error: question count is %d, not 1\n", qdcount); + goto out; + } + struct dname qname; + if (!extract_name (full, &in, &qname)) + { + fprintf (mem.out, "error: malformed QNAME\n"); + goto out; + } + unsigned short qtype; + unsigned short qclass; + if (!(extract_16 (&in, &qtype) + && extract_16 (&in, &qclass))) + { + fprintf (mem.out, "error: malformed question\n"); + goto out; + } + if (qtype != T_A && qtype != T_AAAA && qtype != T_PTR) + { + fprintf (mem.out, "error: unsupported QTYPE %d\n", qtype); + goto out; + } + + fprintf (mem.out, "name: %s\n", qname.name); + + for (int i = 0; i < ancount; ++i) + { + struct dname rname; + if (!extract_name (full, &in, &rname)) + { + fprintf (mem.out, "error: malformed record name\n"); + goto out; + } + unsigned short rtype; + unsigned short rclass; + unsigned ttl; + unsigned short rdlen; + struct in_buffer rdata; + if (!(extract_16 (&in, &rtype) + && extract_16 (&in, &rclass) + && extract_32 (&in, &ttl) + && extract_16 (&in, &rdlen) + && extract_bytes (&in, rdlen, &rdata))) + { + fprintf (mem.out, "error: malformed record header\n"); + goto out; + } + /* Skip non-matching record types. */ + if ((rtype != qtype && rtype != T_CNAME) || rclass != qclass) + continue; + switch (rtype) + { + case T_A: + if (rdlen == 4) + fprintf (mem.out, "address: %d.%d.%d.%d\n", + rdata.data[0], + rdata.data[1], + rdata.data[2], + rdata.data[3]); + else + fprintf (mem.out, "error: A record of size %d: %s\n", + rdlen, rname.name); + break; + case T_AAAA: + { + if (rdlen == 16) + { + char buf[100]; + if (inet_ntop (AF_INET6, rdata.data, buf, sizeof (buf)) == NULL) + fprintf (mem.out, "error: AAAA record decoding failed: %m\n"); + else + fprintf (mem.out, "address: %s\n", buf); + } + else + fprintf (mem.out, "error: AAAA record of size %d: %s\n", + rdlen, rname.name); + } + break; + case T_CNAME: + case T_PTR: + { + struct dname name; + if (extract_name (full, &rdata, &name)) + fprintf (mem.out, "name: %s\n", name.name); + else + fprintf (mem.out, "error: malformed CNAME/PTR record\n"); + } + } + } + + out: + xfclose_memstream (&mem); + return mem.buffer; +} diff --git a/support/support_format_herrno.c b/support/support_format_herrno.c new file mode 100644 index 0000000000..3d2dc8b27a --- /dev/null +++ b/support/support_format_herrno.c @@ -0,0 +1,45 @@ +/* Convert a h_errno error code to a string. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include + +char * +support_format_herrno (int code) +{ + const char *errstr; + switch (code) + { + case HOST_NOT_FOUND: + errstr = "HOST_NOT_FOUND"; + break; + case NO_ADDRESS: + errstr = "NO_ADDRESS"; + break; + case NO_RECOVERY: + errstr = "NO_RECOVERY"; + break; + case TRY_AGAIN: + errstr = "TRY_AGAIN"; + break; + default: + return xasprintf ("\n", code); + } + return xstrdup (errstr); +} diff --git a/support/support_format_hostent.c b/support/support_format_hostent.c new file mode 100644 index 0000000000..a4a62afe0a --- /dev/null +++ b/support/support_format_hostent.c @@ -0,0 +1,82 @@ +/* Convert a struct hostent object to a string. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include +#include +#include + +static int +address_length (int family) +{ + switch (family) + { + case AF_INET: + return 4; + case AF_INET6: + return 16; + } + return -1; +} + +char * +support_format_hostent (struct hostent *h) +{ + if (h == NULL) + { + if (h_errno == NETDB_INTERNAL) + return xasprintf ("error: NETDB_INTERNAL (errno %d, %m)\n", errno); + else + { + char *value = support_format_herrno (h_errno); + char *result = xasprintf ("error: %s\n", value); + free (value); + return result; + } + } + + struct xmemstream mem; + xopen_memstream (&mem); + + fprintf (mem.out, "name: %s\n", h->h_name); + for (char **alias = h->h_aliases; *alias != NULL; ++alias) + fprintf (mem.out, "alias: %s\n", *alias); + for (unsigned i = 0; h->h_addr_list[i] != NULL; ++i) + { + char buf[128]; + if (inet_ntop (h->h_addrtype, h->h_addr_list[i], + buf, sizeof (buf)) == NULL) + fprintf (mem.out, "error: inet_ntop failed: %m\n"); + else + fprintf (mem.out, "address: %s\n", buf); + } + if (h->h_length != address_length (h->h_addrtype)) + { + char *family = support_format_address_family (h->h_addrtype); + fprintf (mem.out, "error: invalid address length %d for %s\n", + h->h_length, family); + free (family); + } + + xfclose_memstream (&mem); + return mem.buffer; +} diff --git a/support/support_format_netent.c b/support/support_format_netent.c new file mode 100644 index 0000000000..0d15e78440 --- /dev/null +++ b/support/support_format_netent.c @@ -0,0 +1,53 @@ +/* Convert a struct netent object to a string. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include +#include + +char * +support_format_netent (struct netent *e) +{ + if (e == NULL) + { + char *value = support_format_herrno (h_errno); + char *result = xasprintf ("error: %s\n", value); + free (value); + return result; + } + + struct xmemstream mem; + xopen_memstream (&mem); + + if (e->n_name != NULL) + fprintf (mem.out, "name: %s\n", e->n_name); + for (char **ap = e->n_aliases; *ap != NULL; ++ap) + fprintf (mem.out, "alias: %s\n", *ap); + if (e->n_addrtype != AF_INET) + fprintf (mem.out, "addrtype: %d\n", e->n_addrtype); + /* On alpha, e->n_net is an unsigned long. */ + unsigned int n_net = e->n_net; + fprintf (mem.out, "net: 0x%08x\n", n_net); + + xfclose_memstream (&mem); + return mem.buffer; +} diff --git a/support/support_isolate_in_subprocess.c b/support/support_isolate_in_subprocess.c new file mode 100644 index 0000000000..25edc00385 --- /dev/null +++ b/support/support_isolate_in_subprocess.c @@ -0,0 +1,38 @@ +/* Run a function in a subprocess. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +void +support_isolate_in_subprocess (void (*callback) (void *), void *closure) +{ + pid_t pid = xfork (); + if (pid == 0) + { + /* Child process. */ + callback (closure); + _exit (0); + } + + /* Parent process. */ + int status; + xwaitpid (pid, &status, 0); + if (status != 0) + FAIL_EXIT1 ("child process exited with status %d", status); +} diff --git a/support/support_record_failure.c b/support/support_record_failure.c new file mode 100644 index 0000000000..356798f556 --- /dev/null +++ b/support/support_record_failure.c @@ -0,0 +1,106 @@ +/* Global test failure counter. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +/* This structure keeps track of test failures. The counter is + incremented on each failure. The failed member is set to true if a + failure is detected, so that even if the counter wraps around to + zero, the failure of a test can be detected. + + The init constructor function below puts *state on a shared + annonymous mapping, so that failure reports from subprocesses + propagate to the parent process. */ +struct test_failures +{ + unsigned int counter; + unsigned int failed; +}; +static struct test_failures *state; + +static __attribute__ ((constructor)) void +init (void) +{ + void *ptr = mmap (NULL, sizeof (*state), PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED, -1, 0); + if (ptr == MAP_FAILED) + { + printf ("error: could not map %zu bytes: %m\n", sizeof (*state)); + exit (1); + } + /* Zero-initialization of the struct is sufficient. */ + state = ptr; +} + +void +support_record_failure (void) +{ + if (state == NULL) + { + write_message + ("error: support_record_failure called without initialization\n"); + _exit (1); + } + /* Relaxed MO is sufficient because we are only interested in the + values themselves, in isolation. */ + __atomic_store_n (&state->failed, 1, __ATOMIC_RELEASE); + __atomic_add_fetch (&state->counter, 1, __ATOMIC_RELEASE); +} + +int +support_report_failure (int status) +{ + if (state == NULL) + { + write_message + ("error: support_report_failure called without initialization\n"); + return 1; + } + + /* Relaxed MO is sufficient because acquire test result reporting + assumes that exiting from the main thread happens before the + error reporting via support_record_failure, which requires some + form of external synchronization. */ + bool failed = __atomic_load_n (&state->failed, __ATOMIC_RELAXED); + if (failed) + printf ("error: %u test failures\n", + __atomic_load_n (&state->counter, __ATOMIC_RELAXED)); + + if ((status == 0 || status == EXIT_UNSUPPORTED) && failed) + /* If we have a recorded failure, it overrides a non-failure + report from the test function. */ + status = 1; + return status; +} + +void +support_record_failure_reset (void) +{ + /* Only used for testing the test framework, with external + synchronization, but use release MO for consistency. */ + __atomic_store_n (&state->failed, 0, __ATOMIC_RELAXED); + __atomic_add_fetch (&state->counter, 0, __ATOMIC_RELAXED); +} diff --git a/support/support_run_diff.c b/support/support_run_diff.c new file mode 100644 index 0000000000..f24f6c3281 --- /dev/null +++ b/support/support_run_diff.c @@ -0,0 +1,76 @@ +/* Invoke the system diff tool to compare two strings. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static char * +write_to_temp_file (const char *prefix, const char *str) +{ + char *template = xasprintf ("run_diff-%s", prefix); + char *name = NULL; + int fd = create_temp_file (template, &name); + TEST_VERIFY_EXIT (fd >= 0); + free (template); + xwrite (fd, str, strlen (str)); + xclose (fd); + return name; +} + +void +support_run_diff (const char *left_label, const char *left, + const char *right_label, const char *right) +{ + /* Ensure that the diff command output is ordered properly with + standard output. */ + TEST_VERIFY_EXIT (fflush (stdout) == 0); + + char *left_path = write_to_temp_file ("left-diff", left); + char *right_path = write_to_temp_file ("right-diff", right); + + pid_t pid = xfork (); + if (pid == 0) + { + execlp ("diff", "diff", "-u", + "--label", left_label, "--label", right_label, + "--", left_path, right_path, + NULL); + _exit (17); + } + else + { + int status; + xwaitpid (pid, &status, 0); + if (!WIFEXITED (status) || WEXITSTATUS (status) != 1) + printf ("warning: could not run diff, exit status: %d\n" + "*** %s ***\n%s\n" + "*** %s ***\n%s\n", + status, left_label, left, right_label, right); + } + + free (right_path); + free (left_path); +} diff --git a/support/support_shared_allocate.c b/support/support_shared_allocate.c new file mode 100644 index 0000000000..8ab43c4b38 --- /dev/null +++ b/support/support_shared_allocate.c @@ -0,0 +1,57 @@ +/* Allocate a memory region shared across processes. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include + +/* Header for the allocation. It contains the size of the allocation + for subsequent unmapping. */ +struct header +{ + size_t total_size; + char data[] __attribute__ ((aligned (__alignof__ (max_align_t)))); +}; + +void * +support_shared_allocate (size_t size) +{ + size_t total_size = size + offsetof (struct header, data); + if (total_size < size) + { + errno = ENOMEM; + oom_error (__func__, size); + return NULL; + } + else + { + struct header *result = xmmap (NULL, total_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED, -1); + result->total_size = total_size; + return &result->data; + } +} + +void +support_shared_free (void *data) +{ + struct header *header = data - offsetof (struct header, data); + xmunmap (header, header->total_size); +} diff --git a/support/support_test_compare_failure.c b/support/support_test_compare_failure.c new file mode 100644 index 0000000000..e5596fd121 --- /dev/null +++ b/support/support_test_compare_failure.c @@ -0,0 +1,55 @@ +/* Reporting a numeric comparison failure. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +static void +report (const char *which, const char *expr, long long value, int positive, + int size) +{ + printf (" %s: ", which); + if (positive) + printf ("%llu", (unsigned long long) value); + else + printf ("%lld", value); + unsigned long long mask + = (~0ULL) >> (8 * (sizeof (unsigned long long) - size)); + printf (" (0x%llx); from: %s\n", (unsigned long long) value & mask, expr); +} + +void +support_test_compare_failure (const char *file, int line, + const char *left_expr, + long long left_value, + int left_positive, + int left_size, + const char *right_expr, + long long right_value, + int right_positive, + int right_size) +{ + support_record_failure (); + if (left_size != right_size) + printf ("%s:%d: numeric comparison failure (widths %d and %d)\n", + file, line, left_size * 8, right_size * 8); + else + printf ("%s:%d: numeric comparison failure\n", file, line); + report (" left", left_expr, left_value, left_positive, left_size); + report ("right", right_expr, right_value, right_positive, right_size); +} diff --git a/support/support_test_main.c b/support/support_test_main.c new file mode 100644 index 0000000000..396385729b --- /dev/null +++ b/support/support_test_main.c @@ -0,0 +1,424 @@ +/* Main worker function for the test driver. + Copyright (C) 1998-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct option default_options[] = +{ + TEST_DEFAULT_OPTIONS + { NULL, 0, NULL, 0 } +}; + +/* Show people how to run the program. */ +static void +usage (const struct option *options) +{ + size_t i; + + printf ("Usage: %s [options]\n" + "\n" + "Environment Variables:\n" + " TIMEOUTFACTOR An integer used to scale the timeout\n" + " TMPDIR Where to place temporary files\n" + " TEST_COREDUMPS Do not disable coredumps if set\n" + "\n", + program_invocation_short_name); + printf ("Options:\n"); + for (i = 0; options[i].name; ++i) + { + int indent; + + indent = printf (" --%s", options[i].name); + if (options[i].has_arg == required_argument) + indent += printf (" "); + printf ("%*s", 25 - indent, ""); + switch (options[i].val) + { + case 'v': + printf ("Increase the output verbosity"); + break; + case OPT_DIRECT: + printf ("Run the test directly (instead of forking & monitoring)"); + break; + case OPT_TESTDIR: + printf ("Override the TMPDIR env var"); + break; + } + printf ("\n"); + } +} + +/* The PID of the test process. */ +static pid_t test_pid; + +/* The cleanup handler passed to test_main. */ +static void (*cleanup_function) (void); + +/* Timeout handler. We kill the child and exit with an error. */ +static void +__attribute__ ((noreturn)) +signal_handler (int sig) +{ + int killed; + int status; + + assert (test_pid > 1); + /* Kill the whole process group. */ + kill (-test_pid, SIGKILL); + /* In case setpgid failed in the child, kill it individually too. */ + kill (test_pid, SIGKILL); + + /* Wait for it to terminate. */ + int i; + for (i = 0; i < 5; ++i) + { + killed = waitpid (test_pid, &status, WNOHANG|WUNTRACED); + if (killed != 0) + break; + + /* Delay, give the system time to process the kill. If the + nanosleep() call return prematurely, all the better. We + won't restart it since this probably means the child process + finally died. */ + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 100000000; + nanosleep (&ts, NULL); + } + if (killed != 0 && killed != test_pid) + { + printf ("Failed to kill test process: %m\n"); + exit (1); + } + + if (cleanup_function != NULL) + cleanup_function (); + + if (sig == SIGINT) + { + signal (sig, SIG_DFL); + raise (sig); + } + + if (killed == 0 || (WIFSIGNALED (status) && WTERMSIG (status) == SIGKILL)) + puts ("Timed out: killed the child process"); + else if (WIFSTOPPED (status)) + printf ("Timed out: the child process was %s\n", + strsignal (WSTOPSIG (status))); + else if (WIFSIGNALED (status)) + printf ("Timed out: the child process got signal %s\n", + strsignal (WTERMSIG (status))); + else + printf ("Timed out: killed the child process but it exited %d\n", + WEXITSTATUS (status)); + + /* Exit with an error. */ + exit (1); +} + +/* Run test_function or test_function_argv. */ +static int +run_test_function (int argc, char **argv, const struct test_config *config) +{ + if (config->test_function != NULL) + return config->test_function (); + else if (config->test_function_argv != NULL) + return config->test_function_argv (argc, argv); + else + { + printf ("error: no test function defined\n"); + exit (1); + } +} + +static bool test_main_called; + +const char *test_dir = NULL; +unsigned int test_verbose = 0; + +/* If test failure reporting has been linked in, it may contribute + additional test failures. */ +static int +adjust_exit_status (int status) +{ + if (support_report_failure != NULL) + return support_report_failure (status); + return status; +} + +int +support_test_main (int argc, char **argv, const struct test_config *config) +{ + if (test_main_called) + { + printf ("error: test_main called for a second time\n"); + exit (1); + } + test_main_called = true; + const struct option *options; + if (config->options != NULL) + options = config->options; + else + options = default_options; + + cleanup_function = config->cleanup_function; + + int direct = 0; /* Directly call the test function? */ + int status; + int opt; + unsigned int timeoutfactor = 1; + pid_t termpid; + + if (!config->no_mallopt) + { + /* Make uses of freed and uninitialized memory known. Do not + pull in a definition for mallopt if it has not been defined + already. */ + extern __typeof__ (mallopt) mallopt __attribute__ ((weak)); + if (mallopt != NULL) + mallopt (M_PERTURB, 42); + } + + while ((opt = getopt_long (argc, argv, config->optstring, options, NULL)) + != -1) + switch (opt) + { + case '?': + usage (options); + exit (1); + case 'v': + ++test_verbose; + break; + case OPT_DIRECT: + direct = 1; + break; + case OPT_TESTDIR: + test_dir = optarg; + break; + default: + if (config->cmdline_function != NULL) + config->cmdline_function (opt); + } + + /* If set, read the test TIMEOUTFACTOR value from the environment. + This value is used to scale the default test timeout values. */ + char *envstr_timeoutfactor = getenv ("TIMEOUTFACTOR"); + if (envstr_timeoutfactor != NULL) + { + char *envstr_conv = envstr_timeoutfactor; + unsigned long int env_fact; + + env_fact = strtoul (envstr_timeoutfactor, &envstr_conv, 0); + if (*envstr_conv == '\0' && envstr_conv != envstr_timeoutfactor) + timeoutfactor = MAX (env_fact, 1); + } + + /* Set TMPDIR to specified test directory. */ + if (test_dir != NULL) + { + setenv ("TMPDIR", test_dir, 1); + + if (chdir (test_dir) < 0) + { + printf ("chdir: %m\n"); + exit (1); + } + } + else + { + test_dir = getenv ("TMPDIR"); + if (test_dir == NULL || test_dir[0] == '\0') + test_dir = "/tmp"; + } + if (support_set_test_dir != NULL) + support_set_test_dir (test_dir); + + int timeout = config->timeout; + if (timeout == 0) + timeout = DEFAULT_TIMEOUT; + + /* Make sure we see all message, even those on stdout. */ + setvbuf (stdout, NULL, _IONBF, 0); + + /* Make sure temporary files are deleted. */ + if (support_delete_temp_files != NULL) + atexit (support_delete_temp_files); + + /* Correct for the possible parameters. */ + argv[optind - 1] = argv[0]; + argv += optind - 1; + argc -= optind - 1; + + /* Call the initializing function, if one is available. */ + if (config->prepare_function != NULL) + config->prepare_function (argc, argv); + + const char *envstr_direct = getenv ("TEST_DIRECT"); + if (envstr_direct != NULL) + { + FILE *f = fopen (envstr_direct, "w"); + if (f == NULL) + { + printf ("cannot open TEST_DIRECT output file '%s': %m\n", + envstr_direct); + exit (1); + } + + fprintf (f, "timeout=%u\ntimeoutfactor=%u\n", + config->timeout, timeoutfactor); + if (config->expected_status != 0) + fprintf (f, "exit=%u\n", config->expected_status); + if (config->expected_signal != 0) + fprintf (f, "signal=%s\n", strsignal (config->expected_signal)); + + if (support_print_temp_files != NULL) + support_print_temp_files (f); + + fclose (f); + direct = 1; + } + + bool disable_coredumps; + { + const char *coredumps = getenv ("TEST_COREDUMPS"); + disable_coredumps = coredumps == NULL || coredumps[0] == '\0'; + } + + /* If we are not expected to fork run the function immediately. */ + if (direct) + return adjust_exit_status (run_test_function (argc, argv, config)); + + /* Set up the test environment: + - prevent core dumps + - set up the timer + - fork and execute the function. */ + + test_pid = fork (); + if (test_pid == 0) + { + /* This is the child. */ + if (disable_coredumps) + { + /* Try to avoid dumping core. This is necessary because we + run the test from the source tree, and the coredumps + would end up there (and not in the build tree). */ + struct rlimit core_limit; + core_limit.rlim_cur = 0; + core_limit.rlim_max = 0; + setrlimit (RLIMIT_CORE, &core_limit); + } + + /* We put the test process in its own pgrp so that if it bogusly + generates any job control signals, they won't hit the whole build. */ + if (setpgid (0, 0) != 0) + printf ("Failed to set the process group ID: %m\n"); + + /* Execute the test function and exit with the return value. */ + exit (run_test_function (argc, argv, config)); + } + else if (test_pid < 0) + { + printf ("Cannot fork test program: %m\n"); + exit (1); + } + + /* Set timeout. */ + signal (SIGALRM, signal_handler); + alarm (timeout * timeoutfactor); + + /* Make sure we clean up if the wrapper gets interrupted. */ + signal (SIGINT, signal_handler); + + /* Wait for the regular termination. */ + termpid = TEMP_FAILURE_RETRY (waitpid (test_pid, &status, 0)); + if (termpid == -1) + { + printf ("Waiting for test program failed: %m\n"); + exit (1); + } + if (termpid != test_pid) + { + printf ("Oops, wrong test program terminated: expected %ld, got %ld\n", + (long int) test_pid, (long int) termpid); + exit (1); + } + + /* Process terminated normaly without timeout etc. */ + if (WIFEXITED (status)) + { + if (config->expected_status == 0) + { + if (config->expected_signal == 0) + /* Exit with the return value of the test. */ + return adjust_exit_status (WEXITSTATUS (status)); + else + { + printf ("Expected signal '%s' from child, got none\n", + strsignal (config->expected_signal)); + exit (1); + } + } + else + { + /* Non-zero exit status is expected */ + if (WEXITSTATUS (status) != config->expected_status) + { + printf ("Expected status %d, got %d\n", + config->expected_status, WEXITSTATUS (status)); + exit (1); + } + } + return adjust_exit_status (0); + } + /* Process was killed by timer or other signal. */ + else + { + if (config->expected_signal == 0) + { + printf ("Didn't expect signal from child: got `%s'\n", + strsignal (WTERMSIG (status))); + exit (1); + } + else if (WTERMSIG (status) != config->expected_signal) + { + printf ("Incorrect signal from child: got `%s', need `%s'\n", + strsignal (WTERMSIG (status)), + strsignal (config->expected_signal)); + exit (1); + } + + return adjust_exit_status (0); + } +} diff --git a/support/support_test_verify_impl.c b/support/support_test_verify_impl.c new file mode 100644 index 0000000000..80311a8265 --- /dev/null +++ b/support/support_test_verify_impl.c @@ -0,0 +1,37 @@ +/* Implementation of the TEST_VERIFY and TEST_VERIFY_EXIT macros. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include + +void +support_test_verify_impl (const char *file, int line, const char *expr) +{ + support_record_failure (); + printf ("error: %s:%d: not true: %s\n", file, line, expr); +} + +void +support_test_verify_exit_impl (int status, const char *file, int line, + const char *expr) +{ + support_test_verify_impl (file, line, expr); + exit (status); +} diff --git a/support/support_write_file_string.c b/support/support_write_file_string.c new file mode 100644 index 0000000000..7505679401 --- /dev/null +++ b/support/support_write_file_string.c @@ -0,0 +1,39 @@ +/* Write a string to a file. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +void +support_write_file_string (const char *path, const char *contents) +{ + int fd = xopen (path, O_CREAT | O_TRUNC | O_WRONLY, 0666); + const char *end = contents + strlen (contents); + for (const char *p = contents; p < end; ) + { + ssize_t ret = write (fd, p, end - p); + if (ret < 0) + FAIL_EXIT1 ("cannot write to \"%s\": %m", path); + if (ret == 0) + FAIL_EXIT1 ("zero-length write to \"%s\"", path); + p += ret; + } + xclose (fd); +} diff --git a/support/temp_file-internal.h b/support/temp_file-internal.h new file mode 100644 index 0000000000..4cee3c0c35 --- /dev/null +++ b/support/temp_file-internal.h @@ -0,0 +1,31 @@ +/* Internal weak declarations for temporary file handling. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_TEMP_FILE_INTERNAL_H +#define SUPPORT_TEMP_FILE_INTERNAL_H + +/* These functions are called by the test driver if they are + defined. Tests should not call them directly. */ + +#include + +void support_set_test_dir (const char *name) __attribute__ ((weak)); +void support_delete_temp_files (void) __attribute__ ((weak)); +void support_print_temp_files (FILE *) __attribute__ ((weak)); + +#endif /* SUPPORT_TEMP_FILE_INTERNAL_H */ diff --git a/support/temp_file.c b/support/temp_file.c new file mode 100644 index 0000000000..0bbc7f9972 --- /dev/null +++ b/support/temp_file.c @@ -0,0 +1,145 @@ +/* Temporary file handling for tests. + Copyright (C) 1998-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* This is required to get an mkstemp which can create large files on + some 32-bit platforms. */ +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include + +#include +#include +#include +#include +#include + +/* List of temporary files. */ +static struct temp_name_list +{ + struct temp_name_list *next; + char *name; + pid_t owner; +} *temp_name_list; + +/* Location of the temporary files. Set by the test skeleton via + support_set_test_dir. The string is not be freed. */ +static const char *test_dir = _PATH_TMP; + +void +add_temp_file (const char *name) +{ + struct temp_name_list *newp + = (struct temp_name_list *) xcalloc (sizeof (*newp), 1); + char *newname = strdup (name); + if (newname != NULL) + { + newp->name = newname; + newp->next = temp_name_list; + newp->owner = getpid (); + temp_name_list = newp; + } + else + free (newp); +} + +int +create_temp_file (const char *base, char **filename) +{ + char *fname; + int fd; + + fname = (char *) xmalloc (strlen (test_dir) + 1 + strlen (base) + + sizeof ("XXXXXX")); + strcpy (stpcpy (stpcpy (stpcpy (fname, test_dir), "/"), base), "XXXXXX"); + + fd = mkstemp (fname); + if (fd == -1) + { + printf ("cannot open temporary file '%s': %m\n", fname); + free (fname); + return -1; + } + + add_temp_file (fname); + if (filename != NULL) + *filename = fname; + else + free (fname); + + return fd; +} + +char * +support_create_temp_directory (const char *base) +{ + char *path = xasprintf ("%s/%sXXXXXX", test_dir, base); + if (mkdtemp (path) == NULL) + { + printf ("error: mkdtemp (\"%s\"): %m", path); + exit (1); + } + add_temp_file (path); + return path; +} + +/* Helper functions called by the test skeleton follow. */ + +void +support_set_test_dir (const char *path) +{ + test_dir = path; +} + +void +support_delete_temp_files (void) +{ + pid_t pid = getpid (); + while (temp_name_list != NULL) + { + /* Only perform the removal if the path was registed in the same + process, as identified by the PID. (This assumes that the + parent process which registered the temporary file sticks + around, to prevent PID reuse.) */ + if (temp_name_list->owner == pid) + { + if (remove (temp_name_list->name) != 0) + printf ("warning: could not remove temporary file: %s: %m\n", + temp_name_list->name); + } + free (temp_name_list->name); + + struct temp_name_list *next = temp_name_list->next; + free (temp_name_list); + temp_name_list = next; + } +} + +void +support_print_temp_files (FILE *f) +{ + if (temp_name_list != NULL) + { + struct temp_name_list *n; + fprintf (f, "temp_files=(\n"); + for (n = temp_name_list; n != NULL; n = n->next) + fprintf (f, " '%s'\n", n->name); + fprintf (f, ")\n"); + } +} diff --git a/support/temp_file.h b/support/temp_file.h new file mode 100644 index 0000000000..c7795cc577 --- /dev/null +++ b/support/temp_file.h @@ -0,0 +1,42 @@ +/* Declarations for temporary file handling. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_TEMP_FILE_H +#define SUPPORT_TEMP_FILE_H + +#include + +__BEGIN_DECLS + +/* Schedule a temporary file for deletion on exit. */ +void add_temp_file (const char *name); + +/* Create a temporary file. Return the opened file descriptor on + success, or -1 on failure. Write the file name to *FILENAME if + FILENAME is not NULL. In this case, the caller is expected to free + *FILENAME. */ +int create_temp_file (const char *base, char **filename); + +/* Create a temporary directory and schedule it for deletion. BASE is + used as a prefix for the unique directory name, which the function + returns. The caller should free this string. */ +char *support_create_temp_directory (const char *base); + +__END_DECLS + +#endif /* SUPPORT_TEMP_FILE_H */ diff --git a/support/test-driver.c b/support/test-driver.c new file mode 100644 index 0000000000..09c8783e4f --- /dev/null +++ b/support/test-driver.c @@ -0,0 +1,165 @@ +/* Main function for test programs. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* This file should be included from test cases. It will define a + main function which provides the test wrapper. + + It assumes that the test case defines a function + + int do_test (void); + + and arranges for that function being called under the test wrapper. + The do_test function should return 0 to indicate a passing test, 1 + to indicate a failing test, or 77 to indicate an unsupported test. + Other result values could be used to indicate a failing test, but + the result of the expression is passed to exit and exit only + returns the lower 8 bits of its input. A non-zero return with some + values could cause a test to incorrectly be considered passing when + it really failed. For this reason, the function should always + return 0 (EXIT_SUCCESS), 1 (EXIT_FAILURE), or 77 + (EXIT_UNSUPPORTED). + + The test function may print out diagnostic or warning messages as well + as messages about failures. These messages should be printed to stdout + and not stderr so that the output is properly ordered with respect to + the rest of the glibc testsuite run output. + + Several preprocessors macros can be defined before including this + file. + + The name of the do_test function can be changed with the + TEST_FUNCTION macro. It must expand to the desired function name. + + If the test case needs access to command line parameters, it must + define the TEST_FUNCTION_ARGV macro with the name of the test + function. It must have the following type: + + int TEST_FUNCTION_ARGV (int argc, char **argv); + + This overrides the do_test default function and is incompatible + with the TEST_FUNCTION macro. + + If PREPARE is defined, it must expand to the name of a function of + the type + + void PREPARE (int argc, char **); + + This function will be called early, after parsing the command line, + but before running the test, in the parent process which acts as + the test supervisor. + + If CLEANUP_HANDLER is defined, it must expand to the name of a + function of the type + + void CLEANUP_HANDLER (void); + + This function will be called from the timeout (SIGALRM) signal + handler. + + If EXPECTED_SIGNAL is defined, it must expanded to a constant which + denotes the expected signal number. + + If EXPECTED_STATUS is defined, it must expand to the expected exit + status. + + If TIMEOUT is defined, it must be positive constant. It overrides + the default test timeout and is measured in seconds. + + If TEST_NO_MALLOPT is defined, the test wrapper will not call + mallopt. + + Custom command line handling can be implemented by defining the + CMDLINE_OPTION macro (after including the header; this + requires _GNU_SOURCE to be defined). This macro must expand to a + to a comma-separated list of braced initializers for struct option + from , with a trailing comma. CMDLINE_PROCESS can be + defined as the name of a function which is called to process these + options. The function is passed the option character/number and + has this type: + + void CMDLINE_PROCESS (int); + + If the program also to process custom default short command line + argument (similar to getopt) it must define CMDLINE_OPTSTRING + with the expected options (for instance "vb"). +*/ + +#include + +#include + +int +main (int argc, char **argv) +{ + struct test_config test_config; + memset (&test_config, 0, sizeof (test_config)); + +#ifdef PREPARE + test_config.prepare_function = (PREPARE); +#endif + +#if defined (TEST_FUNCTION) && defined (TEST_FUNCTON_ARGV) +# error TEST_FUNCTION and TEST_FUNCTION_ARGV cannot be defined at the same time +#endif +#if defined (TEST_FUNCTION) + test_config.test_function = TEST_FUNCTION; +#elif defined (TEST_FUNCTION_ARGV) + test_config.test_function_argv = TEST_FUNCTION_ARGV; +#else + test_config.test_function = do_test; +#endif + +#ifdef CLEANUP_HANDLER + test_config.cleanup_function = CLEANUP_HANDLER; +#endif + +#ifdef EXPECTED_SIGNAL + test_config.expected_signal = (EXPECTED_SIGNAL); +#endif + +#ifdef EXPECTED_STATUS + test_config.expected_status = (EXPECTED_STATUS); +#endif + +#ifdef TEST_NO_MALLOPT + test_config.no_mallopt = 1; +#endif + +#ifdef TIMEOUT + test_config.timeout = TIMEOUT; +#endif + +#ifdef CMDLINE_OPTIONS + struct option options[] = + { + CMDLINE_OPTIONS + TEST_DEFAULT_OPTIONS + }; + test_config.options = &options; +#endif +#ifdef CMDLINE_PROCESS + test_config.cmdline_function = CMDLINE_PROCESS; +#endif +#ifdef CMDLINE_OPTSTRING + test_config.optstring = "+" CMDLINE_OPTSTRING; +#else + test_config.optstring = "+"; +#endif + + return support_test_main (argc, argv, &test_config); +} diff --git a/support/test-driver.h b/support/test-driver.h new file mode 100644 index 0000000000..1708d68d60 --- /dev/null +++ b/support/test-driver.h @@ -0,0 +1,75 @@ +/* Interfaces for the test driver. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_TEST_DRIVER_H +#define SUPPORT_TEST_DRIVER_H + +#include + +__BEGIN_DECLS + +struct test_config +{ + void (*prepare_function) (int argc, char **argv); + int (*test_function) (void); + int (*test_function_argv) (int argc, char **argv); + void (*cleanup_function) (void); + void (*cmdline_function) (int); + const void *options; /* Custom options if not NULL. */ + int timeout; /* Test timeout in seconds. */ + int expected_status; /* Expected exit status. */ + int expected_signal; /* If non-zero, expect termination by signal. */ + char no_mallopt; /* Boolean flag to disable mallopt. */ + const char *optstring; /* Short command line options. */ +}; + +enum + { + /* Test exit status which indicates that the feature is + unsupported. */ + EXIT_UNSUPPORTED = 77, + + /* Default timeout is twenty seconds. Tests should normally + complete faster than this, but if they don't, that's abnormal + (a bug) anyways. */ + DEFAULT_TIMEOUT = 20, + + /* Used for command line argument parsing. */ + OPT_DIRECT = 1000, + OPT_TESTDIR, + }; + +/* Options provided by the test driver. */ +#define TEST_DEFAULT_OPTIONS \ + { "verbose", no_argument, NULL, 'v' }, \ + { "direct", no_argument, NULL, OPT_DIRECT }, \ + { "test-dir", required_argument, NULL, OPT_TESTDIR }, \ + +/* The directory the test should use for temporary files. */ +extern const char *test_dir; + +/* The number of --verbose arguments specified during program + invocation. This variable can be used to control the verbosity of + tests. */ +extern unsigned int test_verbose; + +int support_test_main (int argc, char **argv, const struct test_config *); + +__END_DECLS + +#endif /* SUPPORT_TEST_DRIVER_H */ diff --git a/support/tst-support-namespace.c b/support/tst-support-namespace.c new file mode 100644 index 0000000000..e20423c4a3 --- /dev/null +++ b/support/tst-support-namespace.c @@ -0,0 +1,114 @@ +/* Test entering namespaces. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include + +/* Check that the loopback interface provides multiple addresses which + can be used to run independent servers. */ +static void +test_localhost_bind (void) +{ + printf ("info: testing loopback interface with multiple addresses\n"); + + /* Create the two server addresses. */ + static const struct addrinfo hints = + { + .ai_family = AF_INET, + .ai_socktype = SOCK_DGRAM, + .ai_protocol = IPPROTO_UDP, + }; + struct addrinfo *ai[3]; + TEST_VERIFY_EXIT (getaddrinfo ("127.0.0.1", "53", &hints, ai + 0) == 0); + TEST_VERIFY_EXIT (getaddrinfo ("127.0.0.2", "53", &hints, ai + 1) == 0); + TEST_VERIFY_EXIT (getaddrinfo ("127.0.0.3", "53", &hints, ai + 2) == 0); + + /* Create the server scokets and bind them to these addresses. */ + int sockets[3]; + for (int i = 0; i < 3; ++i) + { + sockets[i] = xsocket + (ai[i]->ai_family, ai[i]->ai_socktype, ai[i]->ai_protocol); + xbind (sockets[i], ai[i]->ai_addr, ai[i]->ai_addrlen); + } + + /* Send two packets to each server. */ + int client = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + for (int i = 0; i < 3; ++i) + { + TEST_VERIFY (sendto (client, &i, sizeof (i), 0, + ai[i]->ai_addr, ai[i]->ai_addrlen) == sizeof (i)); + int j = i + 256; + TEST_VERIFY (sendto (client, &j, sizeof (j), 0, + ai[i]->ai_addr, ai[i]->ai_addrlen) == sizeof (j)); + } + + /* Check that the packets can be received with the expected + contents. Note that the receive calls interleave differently, + which hopefully proves that the sockets are, indeed, + independent. */ + for (int i = 0; i < 3; ++i) + { + int buf; + TEST_VERIFY (recv (sockets[i], &buf, sizeof (buf), 0) == sizeof (buf)); + TEST_VERIFY (buf == i); + } + for (int i = 0; i < 3; ++i) + { + int buf; + TEST_VERIFY (recv (sockets[i], &buf, sizeof (buf), 0) == sizeof (buf)); + TEST_VERIFY (buf == i + 256); + /* Check that there is no more data to receive. */ + TEST_VERIFY (recv (sockets[i], &buf, sizeof (buf), MSG_DONTWAIT) == -1); + TEST_VERIFY (errno == EWOULDBLOCK || errno == EAGAIN); + } + + /* Close all sockets and free the addresses. */ + for (int i = 0; i < 3; ++i) + { + freeaddrinfo (ai[i]); + xclose (sockets[i]); + } + xclose (client); +} + + +static int +do_test (void) +{ + bool root = support_become_root (); + if (root) + printf ("info: acquired root-like privileges\n"); + bool netns = support_enter_network_namespace (); + if (netns) + printf ("info: entered network namespace\n"); + if (support_in_uts_namespace ()) + printf ("info: also entered UTS namespace\n"); + + if (root && netns) + test_localhost_bind (); + + return 0; +} + +#include diff --git a/support/tst-support_capture_subprocess.c b/support/tst-support_capture_subprocess.c new file mode 100644 index 0000000000..a685256091 --- /dev/null +++ b/support/tst-support_capture_subprocess.c @@ -0,0 +1,188 @@ +/* Test capturing output from a subprocess. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Write one byte at *P to FD and advance *P. Do nothing if *P is + '\0'. */ +static void +transfer (const unsigned char **p, int fd) +{ + if (**p != '\0') + { + TEST_VERIFY (write (fd, *p, 1) == 1); + ++*p; + } +} + +/* Determine the order in which stdout and stderr are written. */ +enum write_mode { out_first, err_first, interleave, + write_mode_last = interleave }; + +/* Describe what to write in the subprocess. */ +struct test +{ + char *out; + char *err; + enum write_mode write_mode; + int signal; + int status; +}; + +/* For use with support_capture_subprocess. */ +static void +callback (void *closure) +{ + const struct test *test = closure; + bool mode_ok = false; + switch (test->write_mode) + { + case out_first: + TEST_VERIFY (fputs (test->out, stdout) >= 0); + TEST_VERIFY (fflush (stdout) == 0); + TEST_VERIFY (fputs (test->err, stderr) >= 0); + TEST_VERIFY (fflush (stderr) == 0); + mode_ok = true; + break; + case err_first: + TEST_VERIFY (fputs (test->err, stderr) >= 0); + TEST_VERIFY (fflush (stderr) == 0); + TEST_VERIFY (fputs (test->out, stdout) >= 0); + TEST_VERIFY (fflush (stdout) == 0); + mode_ok = true; + break; + case interleave: + { + const unsigned char *pout = (const unsigned char *) test->out; + const unsigned char *perr = (const unsigned char *) test->err; + do + { + transfer (&pout, STDOUT_FILENO); + transfer (&perr, STDERR_FILENO); + } + while (*pout != '\0' || *perr != '\0'); + } + mode_ok = true; + break; + } + TEST_VERIFY (mode_ok); + + if (test->signal != 0) + raise (test->signal); + exit (test->status); +} + +/* Create a heap-allocated random string of letters. */ +static char * +random_string (size_t length) +{ + char *result = xmalloc (length + 1); + for (size_t i = 0; i < length; ++i) + result[i] = 'a' + (rand () % 26); + result[length] = '\0'; + return result; +} + +/* Check that the specific stream from the captured subprocess matches + expectations. */ +static void +check_stream (const char *what, const struct xmemstream *stream, + const char *expected) +{ + if (strcmp (stream->buffer, expected) != 0) + { + support_record_failure (); + printf ("error: captured %s data incorrect\n" + " expected: %s\n" + " actual: %s\n", + what, expected, stream->buffer); + } + if (stream->length != strlen (expected)) + { + support_record_failure (); + printf ("error: captured %s data length incorrect\n" + " expected: %zu\n" + " actual: %zu\n", + what, strlen (expected), stream->length); + } +} + +static int +do_test (void) +{ + const int lengths[] = {0, 1, 17, 512, 20000, -1}; + + /* Test multiple combinations of support_capture_subprocess. + + length_idx_stdout: Index into the lengths array above, + controls how many bytes are written by the subprocess to + standard output. + length_idx_stderr: Same for standard error. + write_mode: How standard output and standard error writes are + ordered. + signal: Exit with no signal if zero, with SIGTERM if one. + status: Process exit status: 0 if zero, 3 if one. */ + for (int length_idx_stdout = 0; lengths[length_idx_stdout] >= 0; + ++length_idx_stdout) + for (int length_idx_stderr = 0; lengths[length_idx_stderr] >= 0; + ++length_idx_stderr) + for (int write_mode = 0; write_mode < write_mode_last; ++write_mode) + for (int signal = 0; signal < 2; ++signal) + for (int status = 0; status < 2; ++status) + { + struct test test = + { + .out = random_string (lengths[length_idx_stdout]), + .err = random_string (lengths[length_idx_stderr]), + .write_mode = write_mode, + .signal = signal * SIGTERM, /* 0 or SIGTERM. */ + .status = status * 3, /* 0 or 3. */ + }; + TEST_VERIFY (strlen (test.out) == lengths[length_idx_stdout]); + TEST_VERIFY (strlen (test.err) == lengths[length_idx_stderr]); + + struct support_capture_subprocess result + = support_capture_subprocess (callback, &test); + check_stream ("stdout", &result.out, test.out); + check_stream ("stderr", &result.err, test.err); + if (test.signal != 0) + { + TEST_VERIFY (WIFSIGNALED (result.status)); + TEST_VERIFY (WTERMSIG (result.status) == test.signal); + } + else + { + TEST_VERIFY (WIFEXITED (result.status)); + TEST_VERIFY (WEXITSTATUS (result.status) == test.status); + } + support_capture_subprocess_free (&result); + free (test.out); + free (test.err); + } + return 0; +} + +#include diff --git a/support/tst-support_format_dns_packet.c b/support/tst-support_format_dns_packet.c new file mode 100644 index 0000000000..b1135eebc6 --- /dev/null +++ b/support/tst-support_format_dns_packet.c @@ -0,0 +1,101 @@ +/* Tests for the support_format_dns_packet function. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +#include +#include +#include + +static void +check_packet (const void *buffer, size_t length, + const char *name, const char *expected) +{ + char *actual = support_format_dns_packet (buffer, length); + if (strcmp (actual, expected) != 0) + { + support_record_failure (); + printf ("error: formatted packet does not match: %s\n", name); + support_run_diff ("expected", expected, + "actual", actual); + } + free (actual); +} + +static void +test_aaaa_length (void) +{ + static const char packet[] = + /* Header: Response with two records. */ + "\x12\x34\x80\x00\x00\x01\x00\x02\x00\x00\x00\x00" + /* Question section. www.example/IN/AAAA. */ + "\x03www\x07""example\x00\x00\x1c\x00\x01" + /* Answer section. www.example AAAA [corrupted]. */ + "\xc0\x0c" + "\x00\x1c\x00\x01\x00\x00\x00\x00\x00\x10" + "\x20\x01\x0d\xb8\x05\x06\x07\x08" + "\x11\x12\x13\x14\x15\x16\x17\x18" + /* www.example AAAA [corrupted]. */ + "\xc0\x0c" + "\x00\x1c\x00\x01\x00\x00\x00\x00\x00\x11" + "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x11\x12\x13\x14\x15\x16\x17\x18" "\xff"; + check_packet (packet, sizeof (packet) - 1, __func__, + "name: www.example\n" + "address: 2001:db8:506:708:1112:1314:1516:1718\n" + "error: AAAA record of size 17: www.example\n"); +} + +static void +test_multiple_cnames (void) +{ + static const char packet[] = + /* Header: Response with three records. */ + "\x12\x34\x80\x00\x00\x01\x00\x03\x00\x00\x00\x00" + /* Question section. www.example/IN/A. */ + "\x03www\x07""example\x00\x00\x01\x00\x01" + /* Answer section. www.example CNAME www1.example. */ + "\xc0\x0c" + "\x00\x05\x00\x01\x00\x00\x00\x00\x00\x07" + "\x04www1\xc0\x10" + /* www1 CNAME www2. */ + "\x04www1\xc0\x10" + "\x00\x05\x00\x01\x00\x00\x00\x00\x00\x07" + "\x04www2\xc0\x10" + /* www2 A 192.0.2.1. */ + "\x04www2\xc0\x10" + "\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04" + "\xc0\x00\x02\x01"; + check_packet (packet, sizeof (packet) - 1, __func__, + "name: www.example\n" + "name: www1.example\n" + "name: www2.example\n" + "address: 192.0.2.1\n"); +} + +static int +do_test (void) +{ + test_aaaa_length (); + test_multiple_cnames (); + return 0; +} + +#include diff --git a/support/tst-support_record_failure-2.sh b/support/tst-support_record_failure-2.sh new file mode 100644 index 0000000000..09cd96290a --- /dev/null +++ b/support/tst-support_record_failure-2.sh @@ -0,0 +1,69 @@ +#!/bin/sh +# Test failure recording (with and without --direct). +# Copyright (C) 2016-2018 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# . */ + +set -e + +common_objpfx=$1; shift +test_program_prefix_before_env=$1; shift +run_program_env=$1; shift +test_program_prefix_after_env=$1; shift + +run_test () { + expected_status="$1" + expected_output="$2" + shift 2 + args="${common_objpfx}support/tst-support_record_failure $*" + echo "running: $args" + set +e + output="$(${test_program_prefix_before_env} \ + ${run_program} ${test_program_prefix_after_env} $args)" + status=$? + set -e + echo " exit status: $status" + if test "$output" != "$expected_output" ; then + echo "error: unexpected output: $output" + exit 1 + fi + if test "$status" -ne "$expected_status" ; then + echo "error: exit status $expected_status expected" + exit 1 + fi +} + +different_status () { + direct="$1" + run_test 1 "error: 1 test failures" $direct --status=0 + run_test 1 "error: 1 test failures" $direct --status=1 + run_test 2 "error: 1 test failures" $direct --status=2 + run_test 1 "error: 1 test failures" $direct --status=77 + run_test 2 "error: tst-support_record_failure.c:109: not true: false +error: 1 test failures" $direct --test-verify + run_test 2 "error: tst-support_record_failure.c:109: not true: false +info: execution passed failed TEST_VERIFY +error: 1 test failures" $direct --test-verify --verbose +} + +different_status +different_status --direct + +run_test 1 "error: tst-support_record_failure.c:116: not true: false +error: 1 test failures" --test-verify-exit +# --direct does not print the summary error message if exit is called. +run_test 1 "error: tst-support_record_failure.c:116: not true: false" \ + --direct --test-verify-exit diff --git a/support/tst-support_record_failure.c b/support/tst-support_record_failure.c new file mode 100644 index 0000000000..8757f2da02 --- /dev/null +++ b/support/tst-support_record_failure.c @@ -0,0 +1,153 @@ +/* Test support_record_failure state sharing. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static int exit_status_with_failure = -1; +static bool test_verify; +static bool test_verify_exit; +enum + { + OPT_STATUS = 10001, + OPT_TEST_VERIFY, + OPT_TEST_VERIFY_EXIT, + }; +#define CMDLINE_OPTIONS \ + { "status", required_argument, NULL, OPT_STATUS }, \ + { "test-verify", no_argument, NULL, OPT_TEST_VERIFY }, \ + { "test-verify-exit", no_argument, NULL, OPT_TEST_VERIFY_EXIT }, +static void +cmdline_process (int c) +{ + switch (c) + { + case OPT_STATUS: + exit_status_with_failure = atoi (optarg); + break; + case OPT_TEST_VERIFY: + test_verify = true; + break; + case OPT_TEST_VERIFY_EXIT: + test_verify_exit = true; + break; + } +} +#define CMDLINE_PROCESS cmdline_process + +static void +check_failure_reporting (int phase, int zero, int unsupported) +{ + int status = support_report_failure (0); + if (status != zero) + { + printf ("real-error (phase %d): support_report_failure (0) == %d\n", + phase, status); + exit (1); + } + status = support_report_failure (1); + if (status != 1) + { + printf ("real-error (phase %d): support_report_failure (1) == %d\n", + phase, status); + exit (1); + } + status = support_report_failure (2); + if (status != 2) + { + printf ("real-error (phase %d): support_report_failure (2) == %d\n", + phase, status); + exit (1); + } + status = support_report_failure (EXIT_UNSUPPORTED); + if (status != unsupported) + { + printf ("real-error (phase %d): " + "support_report_failure (EXIT_UNSUPPORTED) == %d\n", + phase, status); + exit (1); + } +} + +static int +do_test (void) +{ + if (exit_status_with_failure >= 0) + { + /* External invocation with requested error status. Used by + tst-support_report_failure-2.sh. */ + support_record_failure (); + return exit_status_with_failure; + } + TEST_VERIFY (true); + TEST_VERIFY_EXIT (true); + if (test_verify) + { + TEST_VERIFY (false); + if (test_verbose) + printf ("info: execution passed failed TEST_VERIFY\n"); + return 2; /* Expected exit status. */ + } + if (test_verify_exit) + { + TEST_VERIFY_EXIT (false); + return 3; /* Not reached. Expected exit status is 1. */ + } + + printf ("info: This test tests the test framework.\n" + "info: It reports some expected errors on stdout.\n"); + + /* Check that the status is passed through unchanged. */ + check_failure_reporting (1, 0, EXIT_UNSUPPORTED); + + /* Check state propagation from a subprocess. */ + pid_t pid = xfork (); + if (pid == 0) + { + support_record_failure (); + _exit (0); + } + int status; + xwaitpid (pid, &status, 0); + if (status != 0) + { + printf ("real-error: incorrect status from subprocess: %d\n", status); + return 1; + } + check_failure_reporting (2, 1, 1); + + /* Also test directly in the parent process. */ + support_record_failure_reset (); + check_failure_reporting (3, 0, EXIT_UNSUPPORTED); + support_record_failure (); + check_failure_reporting (4, 1, 1); + + /* We need to mask the failure above. */ + support_record_failure_reset (); + return 0; +} + +#include diff --git a/support/tst-test_compare.c b/support/tst-test_compare.c new file mode 100644 index 0000000000..123ba1bc3c --- /dev/null +++ b/support/tst-test_compare.c @@ -0,0 +1,116 @@ +/* Basic test for the TEST_COMPARE macro. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +static void +subprocess (void *closure) +{ + char ch = 1; + /* These tests should fail. */ + TEST_COMPARE (ch, -1); /* Line 28. */ + TEST_COMPARE (2LL, -2LL); /* Line 29. */ + TEST_COMPARE (3LL, (short) -3); /* Line 30. */ +} + +struct bitfield +{ + int i2 : 2; + int i3 : 3; + unsigned int u2 : 2; + unsigned int u3 : 3; + int i31 : 31; + unsigned int u31 : 31 ; + long long int i63 : 63; + unsigned long long int u63 : 63; +}; + +/* Functions which return signed sizes are common, so test that these + results can readily checked using TEST_COMPARE. */ + +static int +return_ssize_t (void) +{ + return 4; +} + +static int +return_int (void) +{ + return 4; +} + + +static int +do_test (void) +{ + /* This should succeed. */ + TEST_COMPARE (1, 1); + TEST_COMPARE (2LL, 2U); + { + char i8 = 3; + unsigned short u16 = 3; + TEST_COMPARE (i8, u16); + } + TEST_COMPARE (return_ssize_t (), sizeof (char[4])); + TEST_COMPARE (return_int (), sizeof (char[4])); + + struct bitfield bitfield = { 0 }; + TEST_COMPARE (bitfield.i2, bitfield.i3); + TEST_COMPARE (bitfield.u2, bitfield.u3); + TEST_COMPARE (bitfield.u2, bitfield.i3); + TEST_COMPARE (bitfield.u3, bitfield.i3); + TEST_COMPARE (bitfield.i2, bitfield.u3); + TEST_COMPARE (bitfield.i3, bitfield.u2); + TEST_COMPARE (bitfield.i63, bitfield.i63); + TEST_COMPARE (bitfield.u63, bitfield.u63); + TEST_COMPARE (bitfield.i31, bitfield.i63); + TEST_COMPARE (bitfield.i63, bitfield.i31); + + struct support_capture_subprocess proc = support_capture_subprocess + (&subprocess, NULL); + + /* Discard the reported error. */ + support_record_failure_reset (); + + puts ("info: *** subprocess output starts ***"); + fputs (proc.out.buffer, stdout); + puts ("info: *** subprocess output ends ***"); + + TEST_VERIFY + (strcmp (proc.out.buffer, + "tst-test_compare.c:28: numeric comparison failure\n" + " left: 1 (0x1); from: ch\n" + " right: -1 (0xffffffff); from: -1\n" + "tst-test_compare.c:29: numeric comparison failure\n" + " left: 2 (0x2); from: 2LL\n" + " right: -2 (0xfffffffffffffffe); from: -2LL\n" + "tst-test_compare.c:30: numeric comparison failure" + " (widths 64 and 32)\n" + " left: 3 (0x3); from: 3LL\n" + " right: -3 (0xfffffffd); from: (short) -3\n") == 0); + + /* Check that there is no output on standard error. */ + support_capture_subprocess_check (&proc, "TEST_COMPARE", 0, sc_allow_stdout); + + return 0; +} + +#include diff --git a/support/tst-xreadlink.c b/support/tst-xreadlink.c new file mode 100644 index 0000000000..b142207228 --- /dev/null +++ b/support/tst-xreadlink.c @@ -0,0 +1,72 @@ +/* Test the xreadlink function. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int +do_test (void) +{ + char *dir = support_create_temp_directory ("tst-xreadlink-"); + char *symlink_name = xasprintf ("%s/symlink", dir); + add_temp_file (symlink_name); + + /* The limit 10000 is arbitrary and simply there to prevent an + attempt to exhaust all available disk space. */ + for (int size = 1; size < 10000; ++size) + { + char *contents = xmalloc (size + 1); + for (int i = 0; i < size; ++i) + contents[i] = 'a' + (rand () % 26); + contents[size] = '\0'; + if (symlink (contents, symlink_name) != 0) + { + if (errno == ENAMETOOLONG) + { + printf ("info: ENAMETOOLONG failure at %d bytes\n", size); + free (contents); + break; + } + FAIL_EXIT1 ("symlink (%d bytes): %m", size); + } + + char *readlink_result = xreadlink (symlink_name); + TEST_VERIFY (strcmp (readlink_result, contents) == 0); + free (readlink_result); + xunlink (symlink_name); + free (contents); + } + + /* Create an empty file to suppress the temporary file deletion + warning. */ + xclose (xopen (symlink_name, O_WRONLY | O_CREAT, 0)); + + free (symlink_name); + free (dir); + + return 0; +} + +#include diff --git a/support/write_message.c b/support/write_message.c new file mode 100644 index 0000000000..9d0f267a2f --- /dev/null +++ b/support/write_message.c @@ -0,0 +1,29 @@ +/* Write a message to standard output. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include + +void +write_message (const char *message) +{ + ssize_t unused __attribute__ ((unused)); + unused = write (STDOUT_FILENO, message, strlen (message)); +} diff --git a/support/xaccept.c b/support/xaccept.c new file mode 100644 index 0000000000..fd65fc6c3a --- /dev/null +++ b/support/xaccept.c @@ -0,0 +1,32 @@ +/* accept with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include + +int +xaccept (int fd, struct sockaddr *sa, socklen_t *salen) +{ + int clientfd = accept (fd, sa, salen); + if (clientfd < 0) + FAIL_EXIT1 ("accept (%d): %m", fd); + return clientfd; +} diff --git a/support/xaccept4.c b/support/xaccept4.c new file mode 100644 index 0000000000..ada3c92267 --- /dev/null +++ b/support/xaccept4.c @@ -0,0 +1,32 @@ +/* accept4 with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include + +int +xaccept4 (int fd, struct sockaddr *sa, socklen_t *salen, int flags) +{ + int clientfd = accept4 (fd, sa, salen, flags); + if (clientfd < 0) + FAIL_EXIT1 ("accept4 (%d, 0x%x): %m", fd, flags); + return clientfd; +} diff --git a/support/xasprintf.c b/support/xasprintf.c new file mode 100644 index 0000000000..e593ec5e82 --- /dev/null +++ b/support/xasprintf.c @@ -0,0 +1,36 @@ +/* Error-checking wrapper for asprintf. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include + +char * +xasprintf (const char *format, ...) +{ + va_list ap; + va_start (ap, format); + char *result; + if (vasprintf (&result, format, ap) < 0) + FAIL_EXIT1 ("asprintf: %m"); + va_end (ap); + return result; +} diff --git a/support/xbind.c b/support/xbind.c new file mode 100644 index 0000000000..938e7c07d6 --- /dev/null +++ b/support/xbind.c @@ -0,0 +1,30 @@ +/* bind with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include + +void +xbind (int fd, const struct sockaddr *sa, socklen_t sa_len) +{ + if (bind (fd, sa, sa_len) != 0) + FAIL_EXIT1 ("bind (%d), family %d: %m", fd, sa->sa_family); +} diff --git a/support/xcalloc.c b/support/xcalloc.c new file mode 100644 index 0000000000..403569041f --- /dev/null +++ b/support/xcalloc.c @@ -0,0 +1,34 @@ +/* Error-checking wrapper for calloc. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include + +void * +xcalloc (size_t n, size_t s) +{ + void *p; + + p = calloc (n, s); + if (p == NULL) + oom_error ("calloc", n * s); + return p; +} diff --git a/support/xchroot.c b/support/xchroot.c new file mode 100644 index 0000000000..d4759a1bd1 --- /dev/null +++ b/support/xchroot.c @@ -0,0 +1,28 @@ +/* chroot with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +void +xchroot (const char *path) +{ + if (chroot (path) != 0) + FAIL_EXIT1 ("chroot (\"%s\"): %m", path); +} diff --git a/support/xclose.c b/support/xclose.c new file mode 100644 index 0000000000..702bef6c6e --- /dev/null +++ b/support/xclose.c @@ -0,0 +1,28 @@ +/* close with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +void +xclose (int fd) +{ + if (close (fd) < 0 && errno != EINTR) + FAIL_EXIT1 ("close of descriptor %d failed: %m", fd); +} diff --git a/support/xconnect.c b/support/xconnect.c new file mode 100644 index 0000000000..341805d80d --- /dev/null +++ b/support/xconnect.c @@ -0,0 +1,30 @@ +/* connect with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include + +void +xconnect (int fd, const struct sockaddr *sa, socklen_t sa_len) +{ + if (connect (fd, sa, sa_len) != 0) + FAIL_EXIT1 ("connect (%d), family %d: %m", fd, sa->sa_family); +} diff --git a/support/xdlfcn.c b/support/xdlfcn.c new file mode 100644 index 0000000000..f34bb059c0 --- /dev/null +++ b/support/xdlfcn.c @@ -0,0 +1,59 @@ +/* Support functionality for using dlopen/dlclose/dlsym. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +void * +xdlopen (const char *filename, int flags) +{ + void *dso = dlopen (filename, flags); + + if (dso == NULL) + FAIL_EXIT1 ("error: dlopen: %s\n", dlerror ()); + + /* Clear any errors. */ + dlerror (); + + return dso; +} + +void * +xdlsym (void *handle, const char *symbol) +{ + void *sym = dlsym (handle, symbol); + + if (sym == NULL) + FAIL_EXIT1 ("error: dlsym: %s\n", dlerror ()); + + /* Clear any errors. */ + dlerror (); + + return sym; +} + +void +xdlclose (void *handle) +{ + if (dlclose (handle) != 0) + FAIL_EXIT1 ("error: dlclose: %s\n", dlerror ()); + + /* Clear any errors. */ + dlerror (); +} diff --git a/support/xdlfcn.h b/support/xdlfcn.h new file mode 100644 index 0000000000..5ab7494e70 --- /dev/null +++ b/support/xdlfcn.h @@ -0,0 +1,34 @@ +/* Support functionality for using dlopen/dlclose/dlsym. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_DLOPEN_H +#define SUPPORT_DLOPEN_H + +#include + +__BEGIN_DECLS + +/* Each of these terminates process on failure with relevant error message. */ +void *xdlopen (const char *filename, int flags); +void *xdlsym (void *handle, const char *symbol); +void xdlclose (void *handle); + + +__END_DECLS + +#endif /* SUPPORT_DLOPEN_H */ diff --git a/support/xdup2.c b/support/xdup2.c new file mode 100644 index 0000000000..b8c4c223fb --- /dev/null +++ b/support/xdup2.c @@ -0,0 +1,28 @@ +/* dup2 with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include + +void +xdup2 (int from, int to) +{ + if (dup2 (from, to) < 0) + FAIL_EXIT1 ("dup2 (%d, %d): %m", from, to); +} diff --git a/support/xfclose.c b/support/xfclose.c new file mode 100644 index 0000000000..39d46f042a --- /dev/null +++ b/support/xfclose.c @@ -0,0 +1,33 @@ +/* fclose with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include + +void +xfclose (FILE *fp) +{ + if (ferror (fp)) + FAIL_EXIT1 ("stdio stream closed with pending errors"); + if (fflush (fp) != 0) + FAIL_EXIT1 ("fflush: %m"); + if (fclose (fp) != 0) + FAIL_EXIT1 ("fclose: %m"); +} diff --git a/support/xfopen.c b/support/xfopen.c new file mode 100644 index 0000000000..fb931cf22b --- /dev/null +++ b/support/xfopen.c @@ -0,0 +1,31 @@ +/* fopen with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include + +FILE * +xfopen (const char *path, const char *mode) +{ + FILE *fp = fopen (path, mode); + if (fp == NULL) + FAIL_EXIT1 ("could not open %s (mode \"%s\"): %m", path, mode); + return fp; +} diff --git a/support/xfork.c b/support/xfork.c new file mode 100644 index 0000000000..dffa2a13d0 --- /dev/null +++ b/support/xfork.c @@ -0,0 +1,32 @@ +/* fork with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include + +pid_t +xfork (void) +{ + pid_t result = fork (); + if (result < 0) + FAIL_EXIT1 ("fork: %m"); + return result; +} diff --git a/support/xftruncate.c b/support/xftruncate.c new file mode 100644 index 0000000000..27c7e12ba2 --- /dev/null +++ b/support/xftruncate.c @@ -0,0 +1,27 @@ +/* ftruncate with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +void +xftruncate (int fd, long long length) +{ + if (ftruncate64 (fd, length) != 0) + FAIL_EXIT1 ("ftruncate64 (%d, %lld): %m", fd, length); +} diff --git a/support/xgetsockname.c b/support/xgetsockname.c new file mode 100644 index 0000000000..797194f79d --- /dev/null +++ b/support/xgetsockname.c @@ -0,0 +1,30 @@ +/* getsockname with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include + +void +xgetsockname (int fd, struct sockaddr *sa, socklen_t *plen) +{ + if (getsockname (fd, sa, plen) != 0) + FAIL_EXIT1 ("setsockopt (%d): %m", fd); +} diff --git a/support/xlisten.c b/support/xlisten.c new file mode 100644 index 0000000000..aa70091adb --- /dev/null +++ b/support/xlisten.c @@ -0,0 +1,30 @@ +/* listen with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include + +void +xlisten (int fd, int backlog) +{ + if (listen (fd, backlog) != 0) + FAIL_EXIT1 ("listen (%d, %d): %m", fd, backlog); +} diff --git a/support/xlseek.c b/support/xlseek.c new file mode 100644 index 0000000000..2422939908 --- /dev/null +++ b/support/xlseek.c @@ -0,0 +1,29 @@ +/* lseek with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +long long +xlseek (int fd, long long offset, int whence) +{ + long long result = lseek64 (fd, offset, whence); + if (result < 0) + FAIL_EXIT1 ("lseek64 (%d, %lld, %d): %m", fd, offset, whence); + return result; +} diff --git a/support/xmalloc.c b/support/xmalloc.c new file mode 100644 index 0000000000..78317e2b12 --- /dev/null +++ b/support/xmalloc.c @@ -0,0 +1,34 @@ +/* Error-checking wrapper for malloc. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include + +void * +xmalloc (size_t n) +{ + void *p; + + p = malloc (n); + if (p == NULL) + oom_error ("malloc", n); + return p; +} diff --git a/support/xmemstream.c b/support/xmemstream.c new file mode 100644 index 0000000000..df5fbc489a --- /dev/null +++ b/support/xmemstream.c @@ -0,0 +1,42 @@ +/* Error-checking wrappers for memstream functions. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include + +void +xopen_memstream (struct xmemstream *stream) +{ + int old_errno = errno; + *stream = (struct xmemstream) {}; + stream->out = open_memstream (&stream->buffer, &stream->length); + if (stream->out == NULL) + FAIL_EXIT1 ("open_memstream: %m"); + errno = old_errno; +} + +void +xfclose_memstream (struct xmemstream *stream) +{ + xfclose (stream->out); + stream->out = NULL; +} diff --git a/support/xmemstream.h b/support/xmemstream.h new file mode 100644 index 0000000000..2d4a577f29 --- /dev/null +++ b/support/xmemstream.h @@ -0,0 +1,49 @@ +/* Error-checking wrappers for memstream functions. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_XMEMSTREAM_H +#define SUPPORT_XMEMSTREAM_H + +#include +#include + +__BEGIN_DECLS + +/* Wrappers for other libc functions. */ +struct xmemstream +{ + FILE *out; + char *buffer; + size_t length; +}; + +/* Create a new in-memory stream. Initializes *STREAM. After this + function returns, STREAM->out is a file descriptor open for + writing. errno is preserved, so that the %m format specifier can + be used for writing to STREAM->out. */ +void xopen_memstream (struct xmemstream *stream); + +/* Closes STREAM->OUT. After this function returns, STREAM->buffer + and STREAM->length denote a memory range which contains the bytes + written to the output stream. The caller should free + STREAM->buffer. */ +void xfclose_memstream (struct xmemstream *stream); + +__END_DECLS + +#endif /* SUPPORT_XMEMSTREAM_H */ diff --git a/support/xmkdir.c b/support/xmkdir.c new file mode 100644 index 0000000000..7e67f783de --- /dev/null +++ b/support/xmkdir.c @@ -0,0 +1,28 @@ +/* mkdir with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +void +xmkdir (const char *path, mode_t mode) +{ + if (mkdir (path, mode) != 0) + FAIL_EXIT1 ("mkdir (\"%s\", 0%o): %m", path, mode); +} diff --git a/support/xmmap.c b/support/xmmap.c new file mode 100644 index 0000000000..d580c07013 --- /dev/null +++ b/support/xmmap.c @@ -0,0 +1,31 @@ +/* mmap with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +void * +xmmap (void *addr, size_t length, int prot, int flags, int fd) +{ + void *result = mmap (addr, length, prot, flags, fd, 0); + if (result == MAP_FAILED) + FAIL_EXIT1 ("mmap of %zu bytes, prot=0x%x, flags=0x%x: %m", + length, prot, flags); + return result; +} diff --git a/support/xmprotect.c b/support/xmprotect.c new file mode 100644 index 0000000000..e6870988e0 --- /dev/null +++ b/support/xmprotect.c @@ -0,0 +1,28 @@ +/* mprotect with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +void +xmprotect (void *addr, size_t length, int prot) +{ + if (mprotect (addr, length, prot) != 0) + FAIL_EXIT1 ("mprotect (%p, %zu, 0x%x): %m", addr, length, prot); +} diff --git a/support/xmunmap.c b/support/xmunmap.c new file mode 100644 index 0000000000..e8e5bc2d59 --- /dev/null +++ b/support/xmunmap.c @@ -0,0 +1,28 @@ +/* munmap with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +void +xmunmap (void *addr, size_t length) +{ + if (munmap (addr, length) != 0) + FAIL_EXIT1 ("munmap of %zu bytes: %m", length); +} diff --git a/support/xopen.c b/support/xopen.c new file mode 100644 index 0000000000..3d888e8862 --- /dev/null +++ b/support/xopen.c @@ -0,0 +1,30 @@ +/* open64 with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +int +xopen (const char *path, int flags, mode_t mode) +{ + int ret = open64 (path, flags, mode); + if (ret < 0) + FAIL_EXIT1 ("open64 (\"%s\", 0x%x, 0%o): %m", path, flags, mode); + return ret; +} diff --git a/support/xpipe.c b/support/xpipe.c new file mode 100644 index 0000000000..b8529df620 --- /dev/null +++ b/support/xpipe.c @@ -0,0 +1,28 @@ +/* pipe with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include + +void +xpipe (int fds[2]) +{ + if (pipe (fds) < 0) + FAIL_EXIT1 ("pipe: %m"); +} diff --git a/support/xpoll.c b/support/xpoll.c new file mode 100644 index 0000000000..b24bf32fc5 --- /dev/null +++ b/support/xpoll.c @@ -0,0 +1,32 @@ +/* poll with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include + +int +xpoll (struct pollfd *fds, nfds_t nfds, int timeout) +{ + int ret = poll (fds, nfds, timeout); + if (ret < 0) + FAIL_EXIT1 ("poll: %m"); + return ret; +} diff --git a/support/xpthread_attr_destroy.c b/support/xpthread_attr_destroy.c new file mode 100644 index 0000000000..52aa2602fe --- /dev/null +++ b/support/xpthread_attr_destroy.c @@ -0,0 +1,26 @@ +/* pthread_attr_destroy with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_attr_destroy (pthread_attr_t *attr) +{ + xpthread_check_return ("pthread_attr_destroy", + pthread_attr_destroy (attr)); +} diff --git a/support/xpthread_attr_init.c b/support/xpthread_attr_init.c new file mode 100644 index 0000000000..c7aa903db2 --- /dev/null +++ b/support/xpthread_attr_init.c @@ -0,0 +1,25 @@ +/* pthread_attr_init with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_attr_init (pthread_attr_t *attr) +{ + xpthread_check_return ("pthread_attr_init", pthread_attr_init (attr)); +} diff --git a/support/xpthread_attr_setdetachstate.c b/support/xpthread_attr_setdetachstate.c new file mode 100644 index 0000000000..6e845acc97 --- /dev/null +++ b/support/xpthread_attr_setdetachstate.c @@ -0,0 +1,27 @@ +/* pthread_attr_setdetachstate with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate) +{ + xpthread_check_return ("pthread_attr_setdetachstate", + pthread_attr_setdetachstate (attr, + detachstate)); +} diff --git a/support/xpthread_attr_setguardsize.c b/support/xpthread_attr_setguardsize.c new file mode 100644 index 0000000000..964ec058c9 --- /dev/null +++ b/support/xpthread_attr_setguardsize.c @@ -0,0 +1,26 @@ +/* pthread_attr_setguardsize with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_attr_setguardsize (pthread_attr_t *attr, size_t guardsize) +{ + xpthread_check_return ("pthread_attr_setguardize", + pthread_attr_setguardsize (attr, guardsize)); +} diff --git a/support/xpthread_attr_setstacksize.c b/support/xpthread_attr_setstacksize.c new file mode 100644 index 0000000000..c5517c0c66 --- /dev/null +++ b/support/xpthread_attr_setstacksize.c @@ -0,0 +1,26 @@ +/* pthread_attr_setstacksize with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_attr_setstacksize (pthread_attr_t *attr, size_t stacksize) +{ + xpthread_check_return ("pthread_attr_setstacksize", + pthread_attr_setstacksize (attr, stacksize)); +} diff --git a/support/xpthread_barrier_destroy.c b/support/xpthread_barrier_destroy.c new file mode 100644 index 0000000000..1dae148b46 --- /dev/null +++ b/support/xpthread_barrier_destroy.c @@ -0,0 +1,26 @@ +/* pthread_barrier_destroy with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_barrier_destroy (pthread_barrier_t *barrier) +{ + xpthread_check_return ("pthread_barrier_destroy", + pthread_barrier_destroy (barrier)); +} diff --git a/support/xpthread_barrier_init.c b/support/xpthread_barrier_init.c new file mode 100644 index 0000000000..4a2975ad12 --- /dev/null +++ b/support/xpthread_barrier_init.c @@ -0,0 +1,27 @@ +/* pthread_barrier_init with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_barrier_init (pthread_barrier_t *barrier, + pthread_barrierattr_t *attr, unsigned int count) +{ + xpthread_check_return ("pthread_barrier_init", + pthread_barrier_init (barrier, attr, count)); +} diff --git a/support/xpthread_barrier_wait.c b/support/xpthread_barrier_wait.c new file mode 100644 index 0000000000..61690c5e7c --- /dev/null +++ b/support/xpthread_barrier_wait.c @@ -0,0 +1,28 @@ +/* pthread_barrier_wait with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +int +xpthread_barrier_wait (pthread_barrier_t *barrier) +{ + int ret = pthread_barrier_wait (barrier); + if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD) + xpthread_check_return ("pthread_barrier_wait", ret); + return ret == PTHREAD_BARRIER_SERIAL_THREAD; +} diff --git a/support/xpthread_cancel.c b/support/xpthread_cancel.c new file mode 100644 index 0000000000..26e864ea3e --- /dev/null +++ b/support/xpthread_cancel.c @@ -0,0 +1,25 @@ +/* pthread_cancel with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_cancel (pthread_t thr) +{ + xpthread_check_return ("pthread_cancel", pthread_cancel (thr)); +} diff --git a/support/xpthread_check_return.c b/support/xpthread_check_return.c new file mode 100644 index 0000000000..1658db4b62 --- /dev/null +++ b/support/xpthread_check_return.c @@ -0,0 +1,34 @@ +/* Return value checking for pthread functions, exit variant. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include + +void +xpthread_check_return (const char *function, int value) +{ + if (value != 0) + { + errno = value; + FAIL_EXIT1 ("%s: %m", function); + } +} diff --git a/support/xpthread_cond_wait.c b/support/xpthread_cond_wait.c new file mode 100644 index 0000000000..08ec683b66 --- /dev/null +++ b/support/xpthread_cond_wait.c @@ -0,0 +1,26 @@ +/* pthread_cond_wait with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + xpthread_check_return + ("pthread_cond_wait", pthread_cond_wait (cond, mutex)); +} diff --git a/support/xpthread_create.c b/support/xpthread_create.c new file mode 100644 index 0000000000..24b8456db9 --- /dev/null +++ b/support/xpthread_create.c @@ -0,0 +1,29 @@ +/* pthread_create with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +pthread_t +xpthread_create (pthread_attr_t *attr, + void *(*thread_func) (void *), void *closure) +{ + pthread_t thr; + xpthread_check_return + ("pthread_create", pthread_create (&thr, attr, thread_func, closure)); + return thr; +} diff --git a/support/xpthread_detach.c b/support/xpthread_detach.c new file mode 100644 index 0000000000..c65f2d7db1 --- /dev/null +++ b/support/xpthread_detach.c @@ -0,0 +1,25 @@ +/* pthread_detach with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_detach (pthread_t thr) +{ + xpthread_check_return ("pthread_detach", pthread_detach (thr)); +} diff --git a/support/xpthread_join.c b/support/xpthread_join.c new file mode 100644 index 0000000000..29acbd283a --- /dev/null +++ b/support/xpthread_join.c @@ -0,0 +1,27 @@ +/* pthread_join with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void * +xpthread_join (pthread_t thr) +{ + void *result; + xpthread_check_return ("pthread_join", pthread_join (thr, &result)); + return result; +} diff --git a/support/xpthread_mutex_consistent.c b/support/xpthread_mutex_consistent.c new file mode 100644 index 0000000000..d337163609 --- /dev/null +++ b/support/xpthread_mutex_consistent.c @@ -0,0 +1,26 @@ +/* pthread_mutex_consistent with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_mutex_consistent (pthread_mutex_t *mutex) +{ + xpthread_check_return ("pthread_mutex_consistent", + pthread_mutex_consistent (mutex)); +} diff --git a/support/xpthread_mutex_destroy.c b/support/xpthread_mutex_destroy.c new file mode 100644 index 0000000000..d03f016629 --- /dev/null +++ b/support/xpthread_mutex_destroy.c @@ -0,0 +1,26 @@ +/* pthread_mutex_destroy with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_mutex_destroy (pthread_mutex_t *mutex) +{ + xpthread_check_return ("pthread_mutex_destroy", + pthread_mutex_destroy (mutex)); +} diff --git a/support/xpthread_mutex_init.c b/support/xpthread_mutex_init.c new file mode 100644 index 0000000000..40855954a8 --- /dev/null +++ b/support/xpthread_mutex_init.c @@ -0,0 +1,26 @@ +/* pthread_mutex_init with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) +{ + xpthread_check_return ("pthread_mutex_init", + pthread_mutex_init (mutex, attr)); +} diff --git a/support/xpthread_mutex_lock.c b/support/xpthread_mutex_lock.c new file mode 100644 index 0000000000..4257960aaa --- /dev/null +++ b/support/xpthread_mutex_lock.c @@ -0,0 +1,25 @@ +/* pthread_mutex_lock with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_mutex_lock (pthread_mutex_t *mutex) +{ + xpthread_check_return ("pthread_mutex_lock", pthread_mutex_lock (mutex)); +} diff --git a/support/xpthread_mutex_unlock.c b/support/xpthread_mutex_unlock.c new file mode 100644 index 0000000000..5c385c6934 --- /dev/null +++ b/support/xpthread_mutex_unlock.c @@ -0,0 +1,25 @@ +/* pthread_mutex_unlock with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_mutex_unlock (pthread_mutex_t *mutex) +{ + xpthread_check_return ("pthread_mutex_unlock", pthread_mutex_unlock (mutex)); +} diff --git a/support/xpthread_mutexattr_destroy.c b/support/xpthread_mutexattr_destroy.c new file mode 100644 index 0000000000..f352a7ad93 --- /dev/null +++ b/support/xpthread_mutexattr_destroy.c @@ -0,0 +1,26 @@ +/* pthread_mutexattr_destroy with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_mutexattr_destroy (pthread_mutexattr_t *attr) +{ + xpthread_check_return ("pthread_mutexattr_destroy", + pthread_mutexattr_destroy (attr)); +} diff --git a/support/xpthread_mutexattr_init.c b/support/xpthread_mutexattr_init.c new file mode 100644 index 0000000000..fe231581e3 --- /dev/null +++ b/support/xpthread_mutexattr_init.c @@ -0,0 +1,25 @@ +/* pthread_mutexattr_init with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_mutexattr_init (pthread_mutexattr_t *attr) +{ + xpthread_check_return ("pthread_mutexattr_init", pthread_mutexattr_init (attr)); +} diff --git a/support/xpthread_mutexattr_setprotocol.c b/support/xpthread_mutexattr_setprotocol.c new file mode 100644 index 0000000000..e2f544d38c --- /dev/null +++ b/support/xpthread_mutexattr_setprotocol.c @@ -0,0 +1,26 @@ +/* pthread_mutexattr_setprotocol with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_mutexattr_setprotocol (pthread_mutexattr_t *attr, int flag) +{ + xpthread_check_return ("pthread_mutexattr_setprotocol", + pthread_mutexattr_setprotocol (attr, flag)); +} diff --git a/support/xpthread_mutexattr_setpshared.c b/support/xpthread_mutexattr_setpshared.c new file mode 100644 index 0000000000..2380560d79 --- /dev/null +++ b/support/xpthread_mutexattr_setpshared.c @@ -0,0 +1,26 @@ +/* pthread_mutexattr_setpshared with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_mutexattr_setpshared (pthread_mutexattr_t *attr, int flag) +{ + xpthread_check_return ("pthread_mutexattr_setpshared", + pthread_mutexattr_setpshared (attr, flag)); +} diff --git a/support/xpthread_mutexattr_setrobust.c b/support/xpthread_mutexattr_setrobust.c new file mode 100644 index 0000000000..7886c72a77 --- /dev/null +++ b/support/xpthread_mutexattr_setrobust.c @@ -0,0 +1,26 @@ +/* pthread_mutexattr_setrobust with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_mutexattr_setrobust (pthread_mutexattr_t *attr, int flag) +{ + xpthread_check_return ("pthread_mutexattr_setrobust", + pthread_mutexattr_setrobust (attr, flag)); +} diff --git a/support/xpthread_mutexattr_settype.c b/support/xpthread_mutexattr_settype.c new file mode 100644 index 0000000000..91f790c9e5 --- /dev/null +++ b/support/xpthread_mutexattr_settype.c @@ -0,0 +1,26 @@ +/* pthread_mutexattr_settype with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_mutexattr_settype (pthread_mutexattr_t *attr, int flag) +{ + xpthread_check_return ("pthread_mutexattr_settype", + pthread_mutexattr_settype (attr, flag)); +} diff --git a/support/xpthread_once.c b/support/xpthread_once.c new file mode 100644 index 0000000000..b22c1ce3dd --- /dev/null +++ b/support/xpthread_once.c @@ -0,0 +1,25 @@ +/* pthread_once with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_once (pthread_once_t *guard, void (*func) (void)) +{ + xpthread_check_return ("pthread_once", pthread_once (guard, func)); +} diff --git a/support/xpthread_rwlock_init.c b/support/xpthread_rwlock_init.c new file mode 100644 index 0000000000..e6c38115d8 --- /dev/null +++ b/support/xpthread_rwlock_init.c @@ -0,0 +1,27 @@ +/* pthread_rwlock_init with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_rwlock_init (pthread_rwlock_t *rwlock, + const pthread_rwlockattr_t *attr) +{ + xpthread_check_return ("pthread_rwlock_init", + pthread_rwlock_init (rwlock, attr)); +} diff --git a/support/xpthread_rwlock_rdlock.c b/support/xpthread_rwlock_rdlock.c new file mode 100644 index 0000000000..a88068fc86 --- /dev/null +++ b/support/xpthread_rwlock_rdlock.c @@ -0,0 +1,26 @@ +/* pthread_rwlock_rdlock with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_rwlock_rdlock (pthread_rwlock_t *rwlock) +{ + xpthread_check_return ("pthread_rwlock_rdlock", + pthread_rwlock_rdlock (rwlock)); +} diff --git a/support/xpthread_rwlock_unlock.c b/support/xpthread_rwlock_unlock.c new file mode 100644 index 0000000000..7eb282fd18 --- /dev/null +++ b/support/xpthread_rwlock_unlock.c @@ -0,0 +1,26 @@ +/* pthread_rwlock_unlock with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_rwlock_unlock (pthread_rwlock_t *rwlock) +{ + xpthread_check_return ("pthread_rwlock_unlock", + pthread_rwlock_unlock (rwlock)); +} diff --git a/support/xpthread_rwlock_wrlock.c b/support/xpthread_rwlock_wrlock.c new file mode 100644 index 0000000000..0de37146c3 --- /dev/null +++ b/support/xpthread_rwlock_wrlock.c @@ -0,0 +1,26 @@ +/* pthread_rwlock_wrlock with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_rwlock_wrlock (pthread_rwlock_t *rwlock) +{ + xpthread_check_return ("pthread_rwlock_wrlock", + pthread_rwlock_wrlock (rwlock)); +} diff --git a/support/xpthread_rwlockattr_init.c b/support/xpthread_rwlockattr_init.c new file mode 100644 index 0000000000..96ac47d777 --- /dev/null +++ b/support/xpthread_rwlockattr_init.c @@ -0,0 +1,26 @@ +/* pthread_rwlockattr_init with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_rwlockattr_init (pthread_rwlockattr_t *attr) +{ + xpthread_check_return ("pthread_rwlockattr_init", + pthread_rwlockattr_init (attr)); +} diff --git a/support/xpthread_rwlockattr_setkind_np.c b/support/xpthread_rwlockattr_setkind_np.c new file mode 100644 index 0000000000..34aa3df0a9 --- /dev/null +++ b/support/xpthread_rwlockattr_setkind_np.c @@ -0,0 +1,27 @@ +/* pthread_rwlockattr_setkind_np with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_rwlockattr_setkind_np (pthread_rwlockattr_t *attr, + int pref) +{ + xpthread_check_return ("pthread_rwlockattr_setkind_np", + pthread_rwlockattr_setkind_np (attr, pref)); +} diff --git a/support/xpthread_sigmask.c b/support/xpthread_sigmask.c new file mode 100644 index 0000000000..1fb91dcc20 --- /dev/null +++ b/support/xpthread_sigmask.c @@ -0,0 +1,34 @@ +/* pthread_sigmask with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +#include + +void +xpthread_sigmask (int how, const sigset_t *set, sigset_t *oldset) +{ + if (pthread_sigmask (how, set, oldset) != 0) + { + write_message ("error: pthread_setmask failed\n"); + /* Do not use exit because pthread_sigmask can be called from a + signal handler. */ + _exit (1); + } +} diff --git a/support/xpthread_spin_lock.c b/support/xpthread_spin_lock.c new file mode 100644 index 0000000000..4cadbf70f9 --- /dev/null +++ b/support/xpthread_spin_lock.c @@ -0,0 +1,25 @@ +/* pthread_spin_lock with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_spin_lock (pthread_spinlock_t *lock) +{ + xpthread_check_return ("pthread_spin_lock", pthread_spin_lock (lock)); +} diff --git a/support/xpthread_spin_unlock.c b/support/xpthread_spin_unlock.c new file mode 100644 index 0000000000..194613993a --- /dev/null +++ b/support/xpthread_spin_unlock.c @@ -0,0 +1,25 @@ +/* pthread_spin_unlock with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_spin_unlock (pthread_spinlock_t *lock) +{ + xpthread_check_return ("pthread_spin_unlock", pthread_spin_unlock (lock)); +} diff --git a/support/xraise.c b/support/xraise.c new file mode 100644 index 0000000000..1901e741eb --- /dev/null +++ b/support/xraise.c @@ -0,0 +1,27 @@ +/* Error-checking wrapper for raise. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +void +xraise (int sig) +{ + if (raise (sig) != 0) + FAIL_EXIT1 ("raise (%d): %m" , sig); +} diff --git a/support/xreadlink.c b/support/xreadlink.c new file mode 100644 index 0000000000..c6386491db --- /dev/null +++ b/support/xreadlink.c @@ -0,0 +1,44 @@ +/* Error-checking, allocating wrapper for readlink. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +char * +xreadlink (const char *path) +{ + struct scratch_buffer buf; + scratch_buffer_init (&buf); + + while (true) + { + ssize_t count = readlink (path, buf.data, buf.length); + if (count < 0) + FAIL_EXIT1 ("readlink (\"%s\"): %m", path); + if (count < buf.length) + { + char *result = xstrndup (buf.data, count); + scratch_buffer_free (&buf); + return result; + } + if (!scratch_buffer_grow (&buf)) + FAIL_EXIT1 ("scratch_buffer_grow in xreadlink"); + } +} diff --git a/support/xrealloc.c b/support/xrealloc.c new file mode 100644 index 0000000000..4d9987c9bb --- /dev/null +++ b/support/xrealloc.c @@ -0,0 +1,32 @@ +/* Error-checking wrapper for realloc. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include + +void * +xrealloc (void *p, size_t n) +{ + void *result = realloc (p, n); + if (result == NULL && (n > 0 || p == NULL)) + oom_error ("realloc", n); + return result; +} diff --git a/support/xrecvfrom.c b/support/xrecvfrom.c new file mode 100644 index 0000000000..a1011a5062 --- /dev/null +++ b/support/xrecvfrom.c @@ -0,0 +1,33 @@ +/* recvfrom with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include + +size_t +xrecvfrom (int fd, void *buf, size_t buflen, int flags, + struct sockaddr *sa, socklen_t *salen) +{ + ssize_t ret = recvfrom (fd, buf, buflen, flags, sa, salen); + if (ret < 0) + FAIL_EXIT1 ("error: recvfrom (%d), %zu bytes buffer: %m", fd, buflen); + return ret; +} diff --git a/support/xsendto.c b/support/xsendto.c new file mode 100644 index 0000000000..f51530793e --- /dev/null +++ b/support/xsendto.c @@ -0,0 +1,35 @@ +/* sendto with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include + +void +xsendto (int fd, const void *buf, size_t buflen, int flags, + const struct sockaddr *sa, socklen_t salen) +{ + ssize_t ret = sendto (fd, buf, buflen, flags, sa, salen); + if (ret < 0) + FAIL_EXIT1 ("sendto (%d), %zu bytes, family %d: %m", + fd, buflen, sa->sa_family); + if (ret != buflen) + FAIL_EXIT1 ("sendto (%d) sent %zd bytes instead of %zu", fd, ret, buflen); +} diff --git a/support/xsetsockopt.c b/support/xsetsockopt.c new file mode 100644 index 0000000000..b7c07f21f9 --- /dev/null +++ b/support/xsetsockopt.c @@ -0,0 +1,31 @@ +/* setsockopt with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include + +void +xsetsockopt (int fd, int level, int name, const void *val, socklen_t vallen) +{ + if (setsockopt (fd, level, name, val, vallen) != 0) + FAIL_EXIT1 ("setsockopt (%d, %d, %d), %zu bytes: %m", + fd, level, name, (size_t) vallen); +} diff --git a/support/xsigaction.c b/support/xsigaction.c new file mode 100644 index 0000000000..51657de2b7 --- /dev/null +++ b/support/xsigaction.c @@ -0,0 +1,27 @@ +/* Error-checking wrapper for sigaction. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +void +xsigaction (int sig, const struct sigaction *newact, struct sigaction *oldact) +{ + if (sigaction (sig, newact, oldact)) + FAIL_EXIT1 ("sigaction (%d): %m" , sig); +} diff --git a/support/xsignal.c b/support/xsignal.c new file mode 100644 index 0000000000..e7369f0324 --- /dev/null +++ b/support/xsignal.c @@ -0,0 +1,29 @@ +/* Error-checking wrapper for signal. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +sighandler_t +xsignal (int sig, sighandler_t handler) +{ + sighandler_t result = signal (sig, handler); + if (result == SIG_ERR) + FAIL_EXIT1 ("signal (%d, %p): %m", sig, handler); + return result; +} diff --git a/support/xsignal.h b/support/xsignal.h new file mode 100644 index 0000000000..9ab8d1bfdd --- /dev/null +++ b/support/xsignal.h @@ -0,0 +1,42 @@ +/* Support functionality for using signals. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_SIGNAL_H +#define SUPPORT_SIGNAL_H + +#include +#include + +__BEGIN_DECLS + +/* The following functions call the corresponding libc functions and + terminate the process on error. */ + +void xraise (int sig); +sighandler_t xsignal (int sig, sighandler_t handler); +void xsigaction (int sig, const struct sigaction *newact, + struct sigaction *oldact); + +/* The following functions call the corresponding libpthread functions + and terminate the process on error. */ + +void xpthread_sigmask (int how, const sigset_t *set, sigset_t *oldset); + +__END_DECLS + +#endif /* SUPPORT_SIGNAL_H */ diff --git a/support/xsocket.c b/support/xsocket.c new file mode 100644 index 0000000000..20282fb810 --- /dev/null +++ b/support/xsocket.c @@ -0,0 +1,32 @@ +/* socket with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include + +int +xsocket (int domain, int type, int protocol) +{ + int fd = socket (domain, type, protocol); + if (fd < 0) + FAIL_EXIT1 ("socket (%d, %d, %d): %m\n", domain, type, protocol); + return fd; +} diff --git a/support/xsocket.h b/support/xsocket.h new file mode 100644 index 0000000000..9673abdf54 --- /dev/null +++ b/support/xsocket.h @@ -0,0 +1,39 @@ +/* Error-checking wrappers for socket functions. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_XSOCKET_H +#define SUPPORT_XSOCKET_H + +#include +#include +#include + +int xsocket (int, int, int); +void xsetsockopt (int, int, int, const void *, socklen_t); +void xgetsockname (int, struct sockaddr *, socklen_t *); +void xconnect (int, const struct sockaddr *, socklen_t); +void xbind (int, const struct sockaddr *, socklen_t); +void xlisten (int, int); +int xaccept (int, struct sockaddr *, socklen_t *); +int xaccept4 (int, struct sockaddr *, socklen_t *, int); +void xsendto (int, const void *, size_t, int, + const struct sockaddr *, socklen_t); +size_t xrecvfrom (int, void *, size_t, int, struct sockaddr *, socklen_t *); +int xpoll (struct pollfd *, nfds_t, int); + +#endif /* SUPPORT_XSOCKET_H */ diff --git a/support/xstdio.h b/support/xstdio.h new file mode 100644 index 0000000000..e7d0274474 --- /dev/null +++ b/support/xstdio.h @@ -0,0 +1,32 @@ +/* Error-checking wrappers for stdio functions. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_XSTDIO_H +#define SUPPORT_XSTDIO_H + +#include +#include + +__BEGIN_DECLS + +FILE *xfopen (const char *path, const char *mode); +void xfclose (FILE *); + +__END_DECLS + +#endif /* SUPPORT_XSTDIO_H */ diff --git a/support/xstrdup.c b/support/xstrdup.c new file mode 100644 index 0000000000..89eee8584e --- /dev/null +++ b/support/xstrdup.c @@ -0,0 +1,30 @@ +/* strdup with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include + +char * +xstrdup (const char *s) +{ + char *p = strdup (s); + if (p == NULL) + oom_error ("strdup", strlen (s)); + return p; +} diff --git a/support/xstrndup.c b/support/xstrndup.c new file mode 100644 index 0000000000..e85fddd439 --- /dev/null +++ b/support/xstrndup.c @@ -0,0 +1,30 @@ +/* strndup with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include + +char * +xstrndup (const char *s, size_t length) +{ + char *p = strndup (s, length); + if (p == NULL) + oom_error ("strndup", length); + return p; +} diff --git a/support/xsysconf.c b/support/xsysconf.c new file mode 100644 index 0000000000..afefc2d098 --- /dev/null +++ b/support/xsysconf.c @@ -0,0 +1,36 @@ +/* Error-checking wrapper for sysconf. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +long +xsysconf (int name) +{ + /* Detect errors by a changed errno value, in case -1 is a valid + value. Make sure that the caller does not see the zero value for + errno. */ + int old_errno = errno; + errno = 0; + long result = sysconf (name); + if (errno != 0) + FAIL_EXIT1 ("sysconf (%d): %m", name); + errno = old_errno; + return result; +} diff --git a/support/xthread.h b/support/xthread.h new file mode 100644 index 0000000000..79358e7c99 --- /dev/null +++ b/support/xthread.h @@ -0,0 +1,87 @@ +/* Support functionality for using threads. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef SUPPORT_THREAD_H +#define SUPPORT_THREAD_H + +#include +#include + +__BEGIN_DECLS + +/* Terminate the process (with exit status 0) after SECONDS have + elapsed, from a helper thread. The process is terminated with the + exit function, so atexit handlers are executed. */ +void delayed_exit (int seconds); + +/* Terminate the process (with exit status 1) if VALUE is not zero. + In that case, print a failure message to standard output mentioning + FUNCTION. The process is terminated with the exit function, so + atexit handlers are executed. */ +void xpthread_check_return (const char *function, int value); + +/* The following functions call the corresponding libpthread functions + and terminate the process on error. */ + +void xpthread_barrier_init (pthread_barrier_t *barrier, + pthread_barrierattr_t *attr, unsigned int count); +void xpthread_barrier_destroy (pthread_barrier_t *barrier); +void xpthread_mutexattr_destroy (pthread_mutexattr_t *); +void xpthread_mutexattr_init (pthread_mutexattr_t *); +void xpthread_mutexattr_setprotocol (pthread_mutexattr_t *, int); +void xpthread_mutexattr_setpshared (pthread_mutexattr_t *, int); +void xpthread_mutexattr_setrobust (pthread_mutexattr_t *, int); +void xpthread_mutexattr_settype (pthread_mutexattr_t *, int); +void xpthread_mutex_init (pthread_mutex_t *, const pthread_mutexattr_t *); +void xpthread_mutex_destroy (pthread_mutex_t *); +void xpthread_mutex_lock (pthread_mutex_t *mutex); +void xpthread_mutex_unlock (pthread_mutex_t *mutex); +void xpthread_mutex_consistent (pthread_mutex_t *); +void xpthread_spin_lock (pthread_spinlock_t *lock); +void xpthread_spin_unlock (pthread_spinlock_t *lock); +void xpthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex); +pthread_t xpthread_create (pthread_attr_t *attr, + void *(*thread_func) (void *), void *closure); +void xpthread_detach (pthread_t thr); +void xpthread_cancel (pthread_t thr); +void *xpthread_join (pthread_t thr); +void xpthread_once (pthread_once_t *guard, void (*func) (void)); +void xpthread_attr_destroy (pthread_attr_t *attr); +void xpthread_attr_init (pthread_attr_t *attr); +void xpthread_attr_setdetachstate (pthread_attr_t *attr, + int detachstate); +void xpthread_attr_setstacksize (pthread_attr_t *attr, + size_t stacksize); +void xpthread_attr_setguardsize (pthread_attr_t *attr, + size_t guardsize); + +/* This function returns non-zero if pthread_barrier_wait returned + PTHREAD_BARRIER_SERIAL_THREAD. */ +int xpthread_barrier_wait (pthread_barrier_t *barrier); + +void xpthread_rwlock_init (pthread_rwlock_t *rwlock, + const pthread_rwlockattr_t *attr); +void xpthread_rwlockattr_init (pthread_rwlockattr_t *attr); +void xpthread_rwlockattr_setkind_np (pthread_rwlockattr_t *attr, int pref); +void xpthread_rwlock_wrlock (pthread_rwlock_t *rwlock); +void xpthread_rwlock_rdlock (pthread_rwlock_t *rwlock); +void xpthread_rwlock_unlock (pthread_rwlock_t *rwlock); + +__END_DECLS + +#endif /* SUPPORT_THREAD_H */ diff --git a/support/xunistd.h b/support/xunistd.h new file mode 100644 index 0000000000..5fe5dae818 --- /dev/null +++ b/support/xunistd.h @@ -0,0 +1,65 @@ +/* POSIX-specific extra functions. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* These wrapper functions use POSIX types and therefore cannot be + declared in . */ + +#ifndef SUPPORT_XUNISTD_H +#define SUPPORT_XUNISTD_H + +#include +#include +#include + +__BEGIN_DECLS + +struct stat64; + +pid_t xfork (void); +pid_t xwaitpid (pid_t, int *status, int flags); +void xpipe (int[2]); +void xdup2 (int, int); +int xopen (const char *path, int flags, mode_t); +void xstat (const char *path, struct stat64 *); +void xfstat (int fd, struct stat64 *); +void xmkdir (const char *path, mode_t); +void xchroot (const char *path); +void xunlink (const char *path); +long xsysconf (int name); +long long xlseek (int fd, long long offset, int whence); +void xftruncate (int fd, long long length); + +/* Read the link at PATH. The caller should free the returned string + with free. */ +char *xreadlink (const char *path); + +/* Close the file descriptor. Ignore EINTR errors, but terminate the + process on other errors. */ +void xclose (int); + +/* Write the buffer. Retry on short writes. */ +void xwrite (int, const void *, size_t); + +/* Invoke mmap with a zero file offset. */ +void *xmmap (void *addr, size_t length, int prot, int flags, int fd); +void xmprotect (void *addr, size_t length, int prot); +void xmunmap (void *addr, size_t length); + +__END_DECLS + +#endif /* SUPPORT_XUNISTD_H */ diff --git a/support/xunlink.c b/support/xunlink.c new file mode 100644 index 0000000000..2ff9296fca --- /dev/null +++ b/support/xunlink.c @@ -0,0 +1,27 @@ +/* Error-checking wrapper for unlink. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +void +xunlink (const char *path) +{ + if (unlink (path) != 0) + FAIL_EXIT1 ("unlink (\"%s\"): %m", path); +} diff --git a/support/xwaitpid.c b/support/xwaitpid.c new file mode 100644 index 0000000000..1cb039ca17 --- /dev/null +++ b/support/xwaitpid.c @@ -0,0 +1,33 @@ +/* waitpid with error checking. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include + +int +xwaitpid (int pid, int *status, int flags) +{ + pid_t result = waitpid (pid, status, flags); + if (result < 0) + FAIL_EXIT1 ("waitpid: %m\n"); + return result; +} diff --git a/support/xwrite.c b/support/xwrite.c new file mode 100644 index 0000000000..bbaae9130a --- /dev/null +++ b/support/xwrite.c @@ -0,0 +1,39 @@ +/* write with error checking and retries. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include + +void +xwrite (int fd, const void *buffer, size_t length) +{ + const char *p = buffer; + const char *end = p + length; + while (p < end) + { + ssize_t ret = write (fd, p, end - p); + if (ret < 0) + FAIL_EXIT1 ("write of %zu bytes failed after %td: %m", + length, p - (const char *) buffer); + if (ret == 0) + FAIL_EXIT1 ("write return 0 after writing %td bytes of %zu", + p - (const char *) buffer, length); + p += ret; + } +}