From 941370dda9f22ca42e1d5b3cd580d7e7091d40bc Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Sat, 15 Jun 2013 02:39:52 +0200 Subject: [PATCH] Hook BSD err(), errx(), warn(), warnx(), etc. functions. err(), errx(), warn(), warnx(), verr(), verrx(), vwarn(), vwarnx() are hooked. --- .gitignore | 4 +++ configure.ac | 3 ++- src/coloredstderr.c | 30 ++++++++++++++++++++++ src/hookmacros.h | 32 ++++++++++++++++++++++++ tests/Makefile.am | 9 ++++++- tests/example.h | 38 ++++++++++++++++++++++++++++ tests/example_err.c | 51 ++++++++++++++++++++++++++++++++++++++ tests/example_err.expected | 11 ++++++++ tests/test_err.sh | 23 +++++++++++++++++ 9 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 tests/example.h create mode 100644 tests/example_err.c create mode 100644 tests/example_err.expected create mode 100755 tests/test_err.sh diff --git a/.gitignore b/.gitignore index 989a240..a279991 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,8 @@ /tests/Makefile.in /tests/example /tests/example.o +/tests/example_err +/tests/example_err.o /tests/example_error /tests/example_error.o /tests/example_exec @@ -31,6 +33,8 @@ /tests/example_vfork.o /tests/test_environment.sh.log /tests/test_environment.sh.trs +/tests/test_err.sh.log +/tests/test_err.sh.trs /tests/test_error.sh.log /tests/test_error.sh.trs /tests/test_example.sh.log diff --git a/configure.ac b/configure.ac index 0a8f2a8..a96edb4 100644 --- a/configure.ac +++ b/configure.ac @@ -36,7 +36,7 @@ if test "x$GCC" = xyes; then CFLAGS="-Wall -Wextra -Wconversion $CFLAGS" fi -AC_CHECK_HEADERS([error.h]) +AC_CHECK_HEADERS([err.h error.h]) AC_CHECK_HEADERS([fcntl.h], [],[AC_MSG_ERROR([header is required])]) @@ -81,6 +81,7 @@ AC_ARG_ENABLE([debug], fi]) dnl Used in tests/Makefile.am to build the test only if function is available. +AM_CONDITIONAL([HAVE_ERR_H],[test "x$ac_cv_header_err_h" = xyes]) AM_CONDITIONAL([HAVE_ERROR_H],[test "x$ac_cv_header_error_h" = xyes]) AM_CONDITIONAL([HAVE_VFORK],[test "x$ac_cv_func_vfork_works" = xyes]) diff --git a/src/coloredstderr.c b/src/coloredstderr.c index eb9d92c..247bd06 100644 --- a/src/coloredstderr.c +++ b/src/coloredstderr.c @@ -38,6 +38,9 @@ #include #include +#ifdef HAVE_ERR_H +# include +#endif #ifdef HAVE_ERROR_H # include #endif @@ -265,6 +268,33 @@ HOOK_FD2(int, __overflow, f->_fileno, _IO_FILE *, f, int, ch) HOOK_VOID1(void, perror, STDERR_FILENO, char const *, s) +/* err(3), non standard BSD extension */ +#ifdef HAVE_ERR_H +HOOK_VAR_VOID2(void, err, STDERR_FILENO, verr, + int, eval, char const *, fmt) +HOOK_VAR_VOID2(void, errx, STDERR_FILENO, verrx, + int, eval, char const *, fmt) +HOOK_VAR_VOID1(void, warn, STDERR_FILENO, vwarn, + char const *, fmt) +HOOK_VAR_VOID1(void, warnx, STDERR_FILENO, vwarnx, + char const *, fmt) +HOOK_FUNC_SIMPLE3(void, verr, int, eval, const char *, fmt, va_list, args) { + /* Can't use verr() directly as it terminates the process which prevents + * the post string from being printed. */ + vwarn(fmt, args); + exit(eval); +} +HOOK_FUNC_SIMPLE3(void, verrx, int, eval, const char *, fmt, va_list, args) { + /* See verr(). */ + vwarnx(fmt, args); + exit(eval); +} +HOOK_VOID2(void, vwarn, STDERR_FILENO, + char const *, fmt, va_list, args) +HOOK_VOID2(void, vwarnx, STDERR_FILENO, + char const *, fmt, va_list, args) +#endif + /* error(3), non-standard GNU extension */ #ifdef HAVE_ERROR_H static void error_vararg(int status, int errnum, diff --git a/src/hookmacros.h b/src/hookmacros.h index 9ee05db..18a828e 100644 --- a/src/hookmacros.h +++ b/src/hookmacros.h @@ -115,6 +115,9 @@ static type (*real_ ## name)(type1, type2, ...); \ type name(type1 arg1, type2 arg2, ...) +#define HOOK_FUNC_SIMPLE3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type name(type1 arg1, type2 arg2, type3 arg3) + #define HOOK_VOID1(type, name, fd, type1, arg1) \ static type (*real_ ## name)(type1); \ type name(type1 arg1) { \ @@ -122,6 +125,35 @@ real_ ## name(arg1); \ _HOOK_POST_FD_(fd) \ } +#define HOOK_VOID2(type, name, fd, type1, arg1, type2, arg2) \ + static type (*real_ ## name)(type1, type2); \ + type name(type1 arg1, type2 arg2) { \ + _HOOK_PRE_FD_(type, name, fd) \ + real_ ## name(arg1, arg2); \ + _HOOK_POST_FD_(fd) \ + } +#define HOOK_VOID3(type, name, fd, type1, arg1, type2, arg2, type3, arg3) \ + static type (*real_ ## name)(type1, type2, type3); \ + type name(type1 arg1, type2 arg2, type3 arg3) { \ + _HOOK_PRE_FD_(type, name, fd) \ + real_ ## name(arg1, arg2, arg3); \ + _HOOK_POST_FD_(fd) \ + } + +#define HOOK_VAR_VOID1(type, name, fd, func, type1, arg1) \ + type name(type1 arg1, ...) { \ + va_list ap; \ + va_start(ap, arg1); \ + func(arg1, ap); \ + va_end(ap); \ + } +#define HOOK_VAR_VOID2(type, name, fd, func, type1, arg1, type2, arg2) \ + type name(type1 arg1, type2 arg2, ...) { \ + va_list ap; \ + va_start(ap, arg2); \ + func(arg1, arg2, ap); \ + va_end(ap); \ + } #define HOOK_FD2(type, name, fd, type1, arg1, type2, arg2) \ static type (*real_ ## name)(type1, type2); \ diff --git a/tests/Makefile.am b/tests/Makefile.am index 80d0cdd..72e822a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -12,6 +12,11 @@ check_PROGRAMS = example example_exec example_SOURCES = example.c example_exec_SOURCES = example_exec.c +if HAVE_ERR_H + TESTS += test_err.sh + check_PROGRAMS += example_err + example_error_SOURCES = example_err.c +endif if HAVE_ERROR_H TESTS += test_error.sh check_PROGRAMS += example_error @@ -24,9 +29,11 @@ if HAVE_VFORK endif dist_check_SCRIPTS = $(TESTS) lib.sh -dist_check_DATA = example.expected \ +dist_check_DATA = example.h \ + example.expected \ example_environment.expected \ example_environment_empty.expected \ + example_err.expected \ example_error.expected \ example_exec.expected \ example_noforce.sh \ diff --git a/tests/example.h b/tests/example.h new file mode 100644 index 0000000..facbc30 --- /dev/null +++ b/tests/example.h @@ -0,0 +1,38 @@ +/* + * Helper functions/macros for example files. + * + * Copyright (C) 2013 Simon Ruderich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#define FORKED_TEST(pid) \ + pid = fork(); \ + if (pid == -1) { \ + perror("fork"); \ + exit(EXIT_FAILURE); \ + } else if (pid != 0) { \ + int status; \ + if (waitpid(pid, &status, 0) == -1) { \ + perror("waitpid"); \ + exit(EXIT_FAILURE); \ + } \ + if (WIFEXITED(status)) { \ + printf("exit code: %d\n", WEXITSTATUS(status)); \ + } else { \ + printf("child terminated!\n"); \ + } \ + fflush(stdout); \ + } else diff --git a/tests/example_err.c b/tests/example_err.c new file mode 100644 index 0000000..90072c4 --- /dev/null +++ b/tests/example_err.c @@ -0,0 +1,51 @@ +/* + * Test err(), verr(), ... Non-standard, BSD only. + * + * Copyright (C) 2013 Simon Ruderich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../src/compiler.h" +#include "example.h" + + +int main(int argc unused, char **argv unused) { + pid_t pid; + + FORKED_TEST(pid) { errno = ENOMEM; err(0, "error: %s", "message"); } + FORKED_TEST(pid) { errno = ENOMEM; err(1, "error: %s", "message"); } + + FORKED_TEST(pid) { errx(0, "error: %s", "message"); } + FORKED_TEST(pid) { errx(1, "error: %s", "message"); } + + errno = ENOMEM; + warn("warning: %s", "message"); + warnx("warning: %s", "message"); + + /* v*() functions are implicitly tested - the implementation uses them. */ + + printf("\n"); + return EXIT_SUCCESS; +} diff --git a/tests/example_err.expected b/tests/example_err.expected new file mode 100644 index 0000000..652cd81 --- /dev/null +++ b/tests/example_err.expected @@ -0,0 +1,11 @@ +>STDERR>example_err: error: message: Cannot allocate memory +STDERR>example_err: error: message: Cannot allocate memory +STDERR>example_err: error: message +STDERR>example_err: error: message +STDERR>example_err: warning: message: Cannot allocate memory +STDERR>example_err: warning: message +. + + +test "x$srcdir" = x && srcdir=. +. "$srcdir/lib.sh" + +test_program example_err +test_program_subshell example_err -- 2.45.2