INN commit: trunk (7 files)
INN Commit
rra at isc.org
Sun Sep 7 10:44:12 UTC 2014
Date: Sunday, September 7, 2014 @ 03:44:11
Author: iulius
Revision: 9683
Sync the messages test suite with upstream rra-c-util
- Use calloc in preference to calculating a malloc size with
multiplication everywhere. In most places this caution was probably
not necessary, but uniformity is easier to audit and no one will ever
notice the speed difference between malloc and calloc.
- Add new message_handlers_reset function to the messages utility API.
This function resets all handlers to their defaults and frees any
memory allocated by the message_handlers functions. This is primarily
useful to allow freeing all memory when doing exhaustive memory
allocation testing.
- Check the return status of vsnprintf in the syslog message handlers
for die, warn, and friends, and report an error with warn if vsnprintf
fails.
- Drop concat from the messages library. asprintf or xasprintf, provided
by the portability and util libraries respectively, are entirely superior
alternatives to concat.
- Improve the is_function_output TAP add-on interface to take an opaque
data pointer and pass it into the called function.
- Add a new run_setup TAP add-on function that runs a given command and
calls bail if it fails, used for doing test setup in an external
command. (Some things are easier to do in shell than in C.)
Added:
trunk/tests/tap/process.c
trunk/tests/tap/process.h
Modified:
trunk/MANIFEST
trunk/include/inn/messages.h
trunk/lib/messages.c
trunk/tests/Makefile
trunk/tests/lib/messages-t.c
------------------------+
MANIFEST | 2
include/inn/messages.h | 157 ++++++++------
lib/messages.c | 255 +++++++++++++++-------
tests/Makefile | 4
tests/lib/messages-t.c | 192 +++++------------
tests/tap/process.c | 524 +++++++++++++++++++++++++++++++++++++++++++++++
tests/tap/process.h | 95 ++++++++
7 files changed, 944 insertions(+), 285 deletions(-)
Modified: MANIFEST
===================================================================
--- MANIFEST 2014-09-07 10:39:42 UTC (rev 9682)
+++ MANIFEST 2014-09-07 10:44:11 UTC (rev 9683)
@@ -928,6 +928,8 @@
tests/tap/float.h Header file for floating point routines
tests/tap/libtap.sh Helper shell library for writing tests
tests/tap/macros.h Helpful macros for TAP header files
+tests/tap/process.c Subprocess manipulation utilities for TAP
+tests/tap/process.h Header file for subprocess manipulation
tests/tap/string.c String utilities for the TAP protocol
tests/tap/string.h Header file for string utilities
tests/util Test suite for utilities (Directory)
Modified: include/inn/messages.h
===================================================================
--- include/inn/messages.h 2014-09-07 10:39:42 UTC (rev 9682)
+++ include/inn/messages.h 2014-09-07 10:44:11 UTC (rev 9683)
@@ -1,75 +1,102 @@
-/* $Id$
-**
-** Logging, debugging, and error reporting functions.
-**
-** This collection of functions facilitate logging, debugging, and error
-** reporting in a flexible manner that can be used by libraries as well as by
-** programs. The functions are based around the idea of handlers, which take
-** a message and do something appropriate with it. The program can set the
-** appropriate handlers for all the message reporting functions, and then
-** library code can use them with impunity and know the right thing will
-** happen with the messages.
-*/
+/* $Id$
+ *
+ * Prototypes for message and error reporting (possibly fatal).
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Copyright 2008, 2010, 2013, 2014
+ * The Board of Trustees of the Leland Stanford Junior University
+ * Copyright (c) 2004, 2005, 2006
+ * by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ * 2002, 2003 by The Internet Software Consortium and Rich Salz
+ *
+ * This code is derived from software contributed to the Internet Software
+ * Consortium by Rich Salz.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
#ifndef INN_MESSAGES_H
#define INN_MESSAGES_H 1
-#include <inn/defines.h>
+#include "config.h"
+#include "portable/macros.h"
+
#include <stdarg.h>
+#include <stddef.h>
BEGIN_DECLS
-/* The reporting functions. The ones prefaced by "sys" add a colon, a space,
- and the results of strerror(errno) to the output and are intended for
- reporting failures of system calls. */
-extern void notice(const char *, ...)
- __attribute__((__format__(printf, 1, 2)));
-extern void sysnotice(const char *, ...)
- __attribute__((__format__(printf, 1, 2)));
-extern void warn(const char *, ...)
- __attribute__((__format__(printf, 1, 2)));
-extern void syswarn(const char *, ...)
- __attribute__((__format__(printf, 1, 2)));
-extern void die(const char *, ...)
- __attribute__((__noreturn__, __format__(printf, 1, 2)));
-extern void sysdie(const char *, ...)
- __attribute__((__noreturn__, __format__(printf, 1, 2)));
+/*
+ * The reporting functions. The ones prefaced by "sys" add a colon, a space,
+ * and the results of strerror(errno) to the output and are intended for
+ * reporting failures of system calls.
+ */
+void debug(const char *, ...)
+ __attribute__((__nonnull__, __format__(printf, 1, 2)));
+void notice(const char *, ...)
+ __attribute__((__nonnull__, __format__(printf, 1, 2)));
+void sysnotice(const char *, ...)
+ __attribute__((__nonnull__, __format__(printf, 1, 2)));
+void warn(const char *, ...)
+ __attribute__((__nonnull__, __format__(printf, 1, 2)));
+void syswarn(const char *, ...)
+ __attribute__((__nonnull__, __format__(printf, 1, 2)));
+void die(const char *, ...)
+ __attribute__((__nonnull__, __noreturn__, __format__(printf, 1, 2)));
+void sysdie(const char *, ...)
+ __attribute__((__nonnull__, __noreturn__, __format__(printf, 1, 2)));
-/* Debug is handled specially, since we want to make the code disappear
- completely unless we're built with -DDEBUG. We can only do that with
- support for variadic macros, though; otherwise, the function just won't do
- anything. */
-#if !defined(DEBUG) && (INN_HAVE_C99_VAMACROS || INN_HAVE_GNU_VAMACROS)
-# if INN_HAVE_C99_VAMACROS
-# define debug(format, ...) /* empty */
-# elif INN_HAVE_GNU_VAMACROS
-# define debug(format, args...) /* empty */
-# endif
-#else
-extern void debug(const char *, ...)
- __attribute__((__format__(printf, 1, 2)));
-#endif
+/*
+ * Set the handlers for various message functions. All of these functions
+ * take a count of the number of handlers and then function pointers for each
+ * of those handlers. These functions are not thread-safe; they set global
+ * variables.
+ */
+void message_handlers_debug(unsigned int count, ...);
+void message_handlers_notice(unsigned int count, ...);
+void message_handlers_warn(unsigned int count, ...);
+void message_handlers_die(unsigned int count, ...);
-/* Set the handlers for various message functions. All of these functions
- take a count of the number of handlers and then function pointers for each
- of those handlers. These functions are not thread-safe; they set global
- variables. */
-extern void message_handlers_debug(size_t count, ...);
-extern void message_handlers_notice(size_t count, ...);
-extern void message_handlers_warn(size_t count, ...);
-extern void message_handlers_die(size_t count, ...);
+/*
+ * Reset all message handlers back to the defaults and free any memory that
+ * was allocated by the other message_handlers_* functions.
+ */
+void message_handlers_reset(void);
-/* Some useful handlers, intended to be passed to message_handlers_*. All
- handlers take the length of the formatted message, the format, a variadic
- argument list, and the errno setting if any. */
-extern void message_log_stdout(size_t, const char *, va_list, int);
-extern void message_log_stderr(size_t, const char *, va_list, int);
-extern void message_log_syslog_debug(size_t, const char *, va_list, int);
-extern void message_log_syslog_info(size_t, const char *, va_list, int);
-extern void message_log_syslog_notice(size_t, const char *, va_list, int);
-extern void message_log_syslog_warning(size_t, const char *, va_list, int);
-extern void message_log_syslog_err(size_t, const char *, va_list, int);
-extern void message_log_syslog_crit(size_t, const char *, va_list, int);
+/*
+ * Some useful handlers, intended to be passed to message_handlers_*. All
+ * handlers take the length of the formatted message, the format, a variadic
+ * argument list, and the errno setting if any.
+ */
+void message_log_stdout(size_t, const char *, va_list, int)
+ __attribute__((__nonnull__));
+void message_log_stderr(size_t, const char *, va_list, int)
+ __attribute__((__nonnull__));
+void message_log_syslog_debug(size_t, const char *, va_list, int)
+ __attribute__((__nonnull__));
+void message_log_syslog_info(size_t, const char *, va_list, int)
+ __attribute__((__nonnull__));
+void message_log_syslog_notice(size_t, const char *, va_list, int)
+ __attribute__((__nonnull__));
+void message_log_syslog_warning(size_t, const char *, va_list, int)
+ __attribute__((__nonnull__));
+void message_log_syslog_err(size_t, const char *, va_list, int)
+ __attribute__((__nonnull__));
+void message_log_syslog_crit(size_t, const char *, va_list, int)
+ __attribute__((__nonnull__));
/* The type of a message handler. */
typedef void (*message_handler_func)(size_t, const char *, va_list, int);
@@ -77,10 +104,12 @@
/* If non-NULL, called before exit and its return value passed to exit. */
extern int (*message_fatal_cleanup)(void);
-/* If non-NULL, prepended (followed by ": ") to all messages printed by either
- message_log_stdout or message_log_stderr. */
+/*
+ * If non-NULL, prepended (followed by ": ") to all messages printed by either
+ * message_log_stdout or message_log_stderr.
+ */
extern const char *message_program_name;
END_DECLS
-#endif /* INN_MESSAGE_H */
+#endif /* INN_MESSAGES_H */
Modified: lib/messages.c
===================================================================
--- lib/messages.c 2014-09-07 10:39:42 UTC (rev 9682)
+++ lib/messages.c 2014-09-07 10:44:11 UTC (rev 9683)
@@ -1,64 +1,104 @@
/* $Id$
-**
-** Message and error reporting (possibly fatal).
-**
-** Usage:
-**
-** extern int cleanup(void);
-** extern void log(int, const char *, va_list, int);
-**
-** message_fatal_cleanup = cleanup;
-** message_program_name = argv[0];
-**
-** warn("Something horrible happened at %lu", time);
-** syswarn("Couldn't unlink temporary file %s", tmpfile);
-**
-** die("Something fatal happened at %lu", time);
-** sysdie("open of %s failed", filename);
-**
-** debug("Some debugging message about %s", string);
-** notice("Informational notices");
-**
-** message_handlers_warn(1, log);
-** warn("This now goes through our log function");
-**
-** These functions implement message reporting through user-configurable
-** handler functions. debug() only does something if DEBUG is defined, and
-** notice() and warn() just output messages as configured. die() similarly
-** outputs a message but then exits, normally with a status of 1.
-**
-** The sys* versions do the same, but append a colon, a space, and the
-** results of strerror(errno) to the end of the message. All functions
-** accept printf-style formatting strings and arguments.
-**
-** If message_fatal_cleanup is non-NULL, it is called before exit by die and
-** sysdie and its return value is used as the argument to exit. It is a
-** pointer to a function taking no arguments and returning an int, and can be
-** used to call cleanup functions or to exit in some alternate fashion (such
-** as by calling _exit).
-**
-** If message_program_name is non-NULL, the string it points to, followed by
-** a colon and a space, is prepended to all error messages logged through the
-** message_log_stdout and message_log_stderr message handlers (the former is
-** the default for notice, and the latter is the default for warn and die).
-**
-** Honoring error_program_name and printing to stderr is just the default
-** handler; with message_handlers_* the handlers for any message function can
-** be changed. By default, notice prints to stdout, warn and die print to
-** stderr, and the others don't do anything at all. These functions take a
-** count of handlers and then that many function pointers, each one to a
-** function that takes a message length (the number of characters snprintf
-** generates given the format and arguments), a format, an argument list as a
-** va_list, and the applicable errno value (if any).
-*/
+ *
+ * Message and error reporting (possibly fatal).
+ *
+ * Usage:
+ *
+ * extern int cleanup(void);
+ * extern void log(int, const char *, va_list, int);
+ *
+ * message_fatal_cleanup = cleanup;
+ * message_program_name = argv[0];
+ *
+ * warn("Something horrible happened at %lu", time);
+ * syswarn("Couldn't unlink temporary file %s", tmpfile);
+ *
+ * die("Something fatal happened at %lu", time);
+ * sysdie("open of %s failed", filename);
+ *
+ * debug("Some debugging message about %s", string);
+ * notice("Informational notices");
+ *
+ * message_handlers_warn(1, log);
+ * warn("This now goes through our log function");
+ *
+ * These functions implement message reporting through user-configurable
+ * handler functions. debug() only does something if DEBUG is defined, and
+ * notice() and warn() just output messages as configured. die() similarly
+ * outputs a message but then exits, normally with a status of 1.
+ *
+ * The sys* versions do the same, but append a colon, a space, and the results
+ * of strerror(errno) to the end of the message. All functions accept
+ * printf-style formatting strings and arguments.
+ *
+ * If message_fatal_cleanup is non-NULL, it is called before exit by die and
+ * sysdie and its return value is used as the argument to exit. It is a
+ * pointer to a function taking no arguments and returning an int, and can be
+ * used to call cleanup functions or to exit in some alternate fashion (such
+ * as by calling _exit).
+ *
+ * If message_program_name is non-NULL, the string it points to, followed by a
+ * colon and a space, is prepended to all error messages logged through the
+ * message_log_stdout and message_log_stderr message handlers (the former is
+ * the default for notice, and the latter is the default for warn and die).
+ *
+ * Honoring error_program_name and printing to stderr is just the default
+ * handler; with message_handlers_* the handlers for any message function can
+ * be changed. By default, notice prints to stdout, warn and die print to
+ * stderr, and the others don't do anything at all. These functions take a
+ * count of handlers and then that many function pointers, each one to a
+ * function that takes a message length (the number of characters snprintf
+ * generates given the format and arguments), a format, an argument list as a
+ * va_list, and the applicable errno value (if any).
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <eagle at eyrie.org>
+ * Copyright 2008, 2009, 2010, 2013
+ * The Board of Trustees of the Leland Stanford Junior University
+ * Copyright (c) 2004, 2005, 2006
+ * by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ * 2002, 2003 by The Internet Software Consortium and Rich Salz
+ *
+ * This code is derived from software contributed to the Internet Software
+ * Consortium by Rich Salz.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
#include "config.h"
#include "clibrary.h"
+
#include <errno.h>
-#include <syslog.h>
+#ifdef HAVE_SYSLOG_H
+# include <syslog.h>
+#endif
+#ifdef _WIN32
+# include <windows.h>
+# define LOG_DEBUG EVENTLOG_SUCCESS
+# define LOG_INFO EVENTLOG_INFORMATION_TYPE
+# define LOG_NOTICE EVENTLOG_INFORMATION_TYPE
+# define LOG_WARNING EVENTLOG_WARNING_TYPE
+# define LOG_ERR EVENTLOG_ERROR_TYPE
+# define LOG_CRIT EVENTLOG_ERROR_TYPE
+#endif
+
+#include "inn/macros.h"
#include "inn/messages.h"
-#include "inn/libinn.h"
+#include "inn/xmalloc.h"
/* The default handler lists. */
static message_handler_func stdout_handlers[2] = {
@@ -82,17 +122,17 @@
/*
-** Set the handlers for a particular message function. Takes a pointer to
-** the handler list, the count of handlers, and the argument list.
-*/
+ * Set the handlers for a particular message function. Takes a pointer to the
+ * handler list, the count of handlers, and the argument list.
+ */
static void
-message_handlers(message_handler_func **list, size_t count, va_list args)
+message_handlers(message_handler_func **list, unsigned int count, va_list args)
{
- size_t i;
+ unsigned int i;
if (*list != stdout_handlers && *list != stderr_handlers)
free(*list);
- *list = xmalloc(sizeof(message_handler_func) * (count + 1));
+ *list = xcalloc(count + 1, sizeof(message_handler_func));
for (i = 0; i < count; i++)
(*list)[i] = (message_handler_func) va_arg(args, message_handler_func);
(*list)[count] = NULL;
@@ -100,13 +140,13 @@
/*
-** There's no good way of writing these handlers without a bunch of code
-** duplication since we can't assume variadic macros, but I can at least make
-** it easier to write and keep them consistent.
-*/
+ * There's no good way of writing these handlers without a bunch of code
+ * duplication since we can't assume variadic macros, but I can at least make
+ * it easier to write and keep them consistent.
+ */
#define HANDLER_FUNCTION(type) \
void \
- message_handlers_ ## type(size_t count, ...) \
+ message_handlers_ ## type(unsigned int count, ...) \
{ \
va_list args; \
\
@@ -121,9 +161,34 @@
/*
-** Print a message to stdout, supporting message_program_name.
-*/
+ * Reset all handlers back to the defaults and free all allocated memory.
+ * This is primarily useful for programs that undergo comprehensive memory
+ * allocation analysis.
+ */
void
+message_handlers_reset(void)
+{
+ free(debug_handlers);
+ debug_handlers = NULL;
+ if (notice_handlers != stdout_handlers) {
+ free(notice_handlers);
+ notice_handlers = stdout_handlers;
+ }
+ if (warn_handlers != stderr_handlers) {
+ free(warn_handlers);
+ warn_handlers = stderr_handlers;
+ }
+ if (die_handlers != stderr_handlers) {
+ free(die_handlers);
+ die_handlers = stderr_handlers;
+ }
+}
+
+
+/*
+ * Print a message to stdout, supporting message_program_name.
+ */
+void
message_log_stdout(size_t len UNUSED, const char *fmt, va_list args, int err)
{
if (message_program_name != NULL)
@@ -137,9 +202,9 @@
/*
-** Print a message to stderr, supporting message_program_name. Also flush
-** stdout so that errors and regular output occur in the right order.
-*/
+ * Print a message to stderr, supporting message_program_name. Also flush
+ * stdout so that errors and regular output occur in the right order.
+ */
void
message_log_stderr(size_t len UNUSED, const char *fmt, va_list args, int err)
{
@@ -154,15 +219,18 @@
/*
-** Log a message to syslog. This is a helper function used to implement all
-** of the syslog message log handlers. It takes the same arguments as a
-** regular message handler function but with an additional priority
-** argument.
-*/
+ * Log a message to syslog. This is a helper function used to implement all
+ * of the syslog message log handlers. It takes the same arguments as a
+ * regular message handler function but with an additional priority argument.
+ *
+ * This needs further attention on Windows. For example, it currently doesn't
+ * log the errno information.
+ */
static void
message_log_syslog(int pri, size_t len, const char *fmt, va_list args, int err)
{
char *buffer;
+ int status;
buffer = malloc(len + 1);
if (buffer == NULL) {
@@ -170,19 +238,36 @@
(unsigned long) len + 1, __FILE__, __LINE__, strerror(errno));
exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
}
- vsnprintf(buffer, len + 1, fmt, args);
+ status = vsnprintf(buffer, len + 1, fmt, args);
+ if (status < 0) {
+ warn("failed to format output with vsnprintf in syslog handler");
+ free(buffer);
+ return;
+ }
+#ifdef _WIN32
+ {
+ HANDLE eventlog;
+
+ eventlog = RegisterEventSource(NULL, message_program_name);
+ if (eventlog != NULL) {
+ ReportEvent(eventlog, (WORD) pri, 0, 0, NULL, 1, 0, &buffer, NULL);
+ CloseEventLog(eventlog);
+ }
+ }
+#else /* !_WIN32 */
if (err == 0)
syslog(pri, "%s", buffer);
else
syslog(pri, "%s: %s", buffer, strerror(err));
+#endif /* !_WIN32 */
free(buffer);
}
/*
-** Do the same sort of wrapper to generate all of the separate syslog logging
-** functions.
-*/
+ * Do the same sort of wrapper to generate all of the separate syslog logging
+ * functions.
+ */
#define SYSLOG_FUNCTION(name, type) \
void \
message_log_syslog_ ## name(size_t l, const char *f, va_list a, int e) \
@@ -198,12 +283,11 @@
/*
-** All of the message handlers. There's a lot of code duplication here too,
-** but each one is still *slightly* different and va_start has to be called
-** multiple times, so it's hard to get rid of the duplication.
-*/
+ * All of the message handlers. There's a lot of code duplication here too,
+ * but each one is still *slightly* different and va_start has to be called
+ * multiple times, so it's hard to get rid of the duplication.
+ */
-#ifdef DEBUG
void
debug(const char *format, ...)
{
@@ -224,9 +308,6 @@
va_end(args);
}
}
-#elif !INN_HAVE_C99_VAMACROS && !INN_HAVE_GNU_VAMACROS
-void debug(const char *format UNUSED, ...) { }
-#endif
void
notice(const char *format, ...)
Modified: tests/Makefile
===================================================================
--- tests/Makefile 2014-09-07 10:39:42 UTC (rev 9682)
+++ tests/Makefile 2014-09-07 10:44:11 UTC (rev 9683)
@@ -167,8 +167,8 @@
lib/messages-t.o: lib/messages-t.c
$(CC) $(CFLAGS) -DDEBUG -c -o $@ lib/messages-t.c
-lib/messages.t: lib/messages.o lib/messages-t.o tap/basic.o $(LIBINN)
- $(LINK) lib/messages-t.o lib/messages.o tap/basic.o $(LIBINN)
+lib/messages.t: lib/messages.o lib/messages-t.o tap/basic.o tap/process.o tap/string.o $(LIBINN)
+ $(LINK) lib/messages-t.o lib/messages.o tap/basic.o tap/process.o tap/string.o $(LIBINN)
lib/mkstemp.o: ../lib/mkstemp.c
$(CC) $(CFLAGS) -DTESTING -c -o $@ ../lib/mkstemp.c
Modified: tests/lib/messages-t.c
===================================================================
--- tests/lib/messages-t.c 2014-09-07 10:39:42 UTC (rev 9682)
+++ tests/lib/messages-t.c 2014-09-07 10:44:11 UTC (rev 9683)
@@ -1,14 +1,13 @@
-/*
+/* $Id$
+ *
* Test suite for error handling routines.
*
- * $Id$
- *
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
*
- * Written by Russ Allbery <rra at stanford.edu>
- * Copyright 2002, 2004, 2005 Russ Allbery <rra at stanford.edu>
- * Copyright 2009, 2010
+ * Written by Russ Allbery <eagle at eyrie.org>
+ * Copyright 2002, 2004, 2005 Russ Allbery <eagle at eyrie.org>
+ * Copyright 2009, 2010, 2011, 2012
* The Board of Trustees of the Leland Stanford Junior University
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -34,114 +33,43 @@
#include "config.h"
#include "clibrary.h"
+
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
-#include <sys/wait.h>
+#include "portable/wait.h"
-#include "inn/messages.h"
-#include "inn/libinn.h"
#include "tap/basic.h"
+#include "tap/process.h"
+#include "inn/macros.h"
+#include "inn/messages.h"
+#include "inn/xmalloc.h"
-typedef void (*test_function_type)(void);
-void is_function_output(test_function_type, int status, const char *output,
- const char *format, ...)
- __attribute__((__format__(printf, 4, 5)));
/*
- * Given a function, an expected exit status, and expected output, runs that
- * function in a subprocess, capturing stdout and stderr via a pipe, and
- * compare the combination of stdout and stderr with the expected output and
- * the exit status with the expected status. Expects the function to always
- * exit (not die from a signal).
- */
-void
-is_function_output(test_function_type function, int status, const char *output,
- const char *format, ...)
-{
- int fds[2];
- pid_t child;
- char *buf, *msg;
- ssize_t count, ret, buflen;
- int rval;
- va_list args;
-
- /* Flush stdout before we start to avoid odd forking issues. */
- fflush(stdout);
-
- /* Set up the pipe and call the function, collecting its output. */
- if (pipe(fds) == -1)
- sysbail("can't create pipe");
- child = fork();
- if (child == (pid_t) -1) {
- sysbail("can't fork");
- } else if (child == 0) {
- /* In child. Set up our stdout and stderr. */
- close(fds[0]);
- if (dup2(fds[1], 1) == -1)
- _exit(255);
- if (dup2(fds[1], 2) == -1)
- _exit(255);
-
- /* Now, run the function and exit successfully if it returns. */
- (*function)();
- fflush(stdout);
- _exit(0);
- } else {
- /*
- * In the parent; close the extra file descriptor, read the output if
- * any, and then collect the exit status.
- */
- close(fds[1]);
- buflen = BUFSIZ;
- buf = xmalloc(buflen);
- count = 0;
- do {
- ret = read(fds[0], buf + count, buflen - count - 1);
- if (ret > 0)
- count += ret;
- if (count >= buflen - 1) {
- buflen += BUFSIZ;
- buf = xrealloc(buf, buflen);
- }
- } while (ret > 0);
- buf[count < 0 ? 0 : count] = '\0';
- if (waitpid(child, &rval, 0) == (pid_t) -1)
- sysbail("waitpid failed");
- }
-
- /* Now, check the results against what we expected. */
- va_start(args, format);
- xvasprintf(&msg, format, args);
- va_end(args);
- ok(WIFEXITED(rval), "%s (exited)", msg);
- is_int(status, WEXITSTATUS(rval), "%s (status)", msg);
- is_string(output, buf, "%s (output)", msg);
- free(buf);
- free(msg);
-}
-
-/*
* Test functions.
*/
-static void test1(void) { warn("warning"); }
-static void test2(void) { die("fatal"); }
-static void test3(void) { errno = EPERM; syswarn("permissions"); }
-static void test4(void) { errno = EACCES; sysdie("fatal access"); }
-static void test5(void) {
+static void test1(void *data UNUSED) { warn("warning"); }
+static void test2(void *data UNUSED) { die("fatal"); }
+static void test3(void *data UNUSED) { errno = EPERM; syswarn("permissions"); }
+static void test4(void *data UNUSED) {
+ errno = EACCES;
+ sysdie("fatal access");
+}
+static void test5(void *data UNUSED) {
message_program_name = "test5";
warn("warning");
}
-static void test6(void) {
+static void test6(void *data UNUSED) {
message_program_name = "test6";
die("fatal");
}
-static void test7(void) {
+static void test7(void *data UNUSED) {
message_program_name = "test7";
errno = EPERM;
syswarn("perms %d", 7);
}
-static void test8(void) {
+static void test8(void *data UNUSED) {
message_program_name = "test8";
errno = EACCES;
sysdie("%st%s", "fa", "al");
@@ -149,17 +77,17 @@
static int return10(void) { return 10; }
-static void test9(void) {
+static void test9(void *data UNUSED) {
message_fatal_cleanup = return10;
die("fatal");
}
-static void test10(void) {
+static void test10(void *data UNUSED) {
message_program_name = 0;
message_fatal_cleanup = return10;
errno = EPERM;
sysdie("fatal perm");
}
-static void test11(void) {
+static void test11(void *data UNUSED) {
message_program_name = "test11";
message_fatal_cleanup = return10;
errno = EPERM;
@@ -167,61 +95,61 @@
sysdie("fatal");
}
-static void log_msg(int len, const char *format, va_list args, int error) {
- fprintf(stderr, "%d %d ", len, error);
+static void log_msg(size_t len, const char *format, va_list args, int error) {
+ fprintf(stderr, "%lu %d ", (unsigned long) len, error);
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
}
-static void test12(void) {
+static void test12(void *data UNUSED) {
message_handlers_warn(1, log_msg);
warn("warning");
}
-static void test13(void) {
+static void test13(void *data UNUSED) {
message_handlers_die(1, log_msg);
die("fatal");
}
-static void test14(void) {
+static void test14(void *data UNUSED) {
message_handlers_warn(2, log_msg, log_msg);
errno = EPERM;
syswarn("warning");
}
-static void test15(void) {
+static void test15(void *data UNUSED) {
message_handlers_die(2, log_msg, log_msg);
message_fatal_cleanup = return10;
errno = EPERM;
sysdie("fatal");
}
-static void test16(void) {
+static void test16(void *data UNUSED) {
message_handlers_warn(2, message_log_stderr, log_msg);
message_program_name = "test16";
errno = EPERM;
syswarn("warning");
}
-static void test17(void) { notice("notice"); }
-static void test18(void) {
+static void test17(void *data UNUSED) { notice("notice"); }
+static void test18(void *data UNUSED) {
message_program_name = "test18";
notice("notice");
}
-static void test19(void) { debug("debug"); }
-static void test20(void) {
+static void test19(void *data UNUSED) { debug("debug"); }
+static void test20(void *data UNUSED) {
message_handlers_notice(1, log_msg);
notice("foo");
}
-static void test21(void) {
+static void test21(void *data UNUSED) {
message_handlers_debug(1, message_log_stdout);
message_program_name = "test23";
debug("baz");
}
-static void test22(void) {
+static void test22(void *data UNUSED) {
message_handlers_die(0);
die("hi mom!");
}
-static void test23(void) {
+static void test23(void *data UNUSED) {
message_handlers_warn(0);
warn("this is a test");
}
-static void test24(void) {
+static void test24(void *data UNUSED) {
notice("first");
message_handlers_notice(0);
notice("second");
@@ -240,9 +168,9 @@
{
char *full_output, *name;
- full_output = concat(output, ": ", strerror(error), "\n", (char *) NULL);
+ xasprintf(&full_output, "%s: %s\n", output, strerror(error));
xasprintf(&name, "strerror %lu", testnum / 3 + 1);
- is_function_output(function, status, full_output, "%s", name);
+ is_function_output(function, NULL, status, full_output, "%s", name);
free(full_output);
free(name);
}
@@ -259,43 +187,43 @@
plan(24 * 3);
- is_function_output(test1, 0, "warning\n", "test1");
- is_function_output(test2, 1, "fatal\n", "test2");
+ is_function_output(test1, NULL, 0, "warning\n", "test1");
+ is_function_output(test2, NULL, 1, "fatal\n", "test2");
test_strerror(0, "permissions", EPERM, test3);
test_strerror(1, "fatal access", EACCES, test4);
- is_function_output(test5, 0, "test5: warning\n", "test5");
- is_function_output(test6, 1, "test6: fatal\n", "test6");
+ is_function_output(test5, NULL, 0, "test5: warning\n", "test5");
+ is_function_output(test6, NULL, 1, "test6: fatal\n", "test6");
test_strerror(0, "test7: perms 7", EPERM, test7);
test_strerror(1, "test8: fatal", EACCES, test8);
- is_function_output(test9, 10, "fatal\n", "test9");
+ is_function_output(test9, NULL, 10, "fatal\n", "test9");
test_strerror(10, "fatal perm", EPERM, test10);
test_strerror(10, "1st test11: fatal", EPERM, test11);
- is_function_output(test12, 0, "7 0 warning\n", "test12");
- is_function_output(test13, 1, "5 0 fatal\n", "test13");
+ is_function_output(test12, NULL, 0, "7 0 warning\n", "test12");
+ is_function_output(test13, NULL, 1, "5 0 fatal\n", "test13");
sprintf(buff, "%d", EPERM);
xasprintf(&output, "7 %d warning\n7 %d warning\n", EPERM, EPERM);
- is_function_output(test14, 0, output, "test14");
+ is_function_output(test14, NULL, 0, output, "test14");
free(output);
xasprintf(&output, "5 %d fatal\n5 %d fatal\n", EPERM, EPERM);
- is_function_output(test15, 10, output, "test15");
+ is_function_output(test15, NULL, 10, output, "test15");
free(output);
xasprintf(&output, "test16: warning: %s\n7 %d warning\n", strerror(EPERM),
EPERM);
- is_function_output(test16, 0, output, "test16");
+ is_function_output(test16, NULL, 0, output, "test16");
free(output);
- is_function_output(test17, 0, "notice\n", "test17");
- is_function_output(test18, 0, "test18: notice\n", "test18");
- is_function_output(test19, 0, "", "test19");
- is_function_output(test20, 0, "3 0 foo\n", "test20");
- is_function_output(test21, 0, "test23: baz\n", "test21");
+ is_function_output(test17, NULL, 0, "notice\n", "test17");
+ is_function_output(test18, NULL, 0, "test18: notice\n", "test18");
+ is_function_output(test19, NULL, 0, "", "test19");
+ is_function_output(test20, NULL, 0, "3 0 foo\n", "test20");
+ is_function_output(test21, NULL, 0, "test23: baz\n", "test21");
/* Make sure that it's possible to turn off a message type entirely. */
- is_function_output(test22, 1, "", "test22");
- is_function_output(test23, 0, "", "test23");
- is_function_output(test24, 0, "first\nthird\n", "test24");
+ is_function_output(test22, NULL, 1, "", "test22");
+ is_function_output(test23, NULL, 0, "", "test23");
+ is_function_output(test24, NULL, 0, "first\nthird\n", "test24");
return 0;
}
Added: tests/tap/process.c
===================================================================
--- tests/tap/process.c (rev 0)
+++ tests/tap/process.c 2014-09-07 10:44:11 UTC (rev 9683)
@@ -0,0 +1,524 @@
+/* $Id$
+ *
+ * Utility functions for tests that use subprocesses.
+ *
+ * Provides utility functions for subprocess manipulation. Specifically,
+ * provides a function, run_setup, which runs a command and bails if it fails,
+ * using its error message as the bail output, and is_function_output, which
+ * runs a function in a subprocess and checks its output and exit status
+ * against expected values.
+ *
+ * Requires an Autoconf probe for sys/select.h and a replacement for a missing
+ * mkstemp.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <eagle at eyrie.org>
+ * Copyright 2002, 2004, 2005, 2013 Russ Allbery <eagle at eyrie.org>
+ * Copyright 2009, 2010, 2011, 2013, 2014
+ * The Board of Trustees of the Leland Stanford Junior University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#define LIBTEST_NEW_FORMAT 1
+
+#include "config.h"
+#include "clibrary.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#include <sys/stat.h>
+#include "portable/time.h"
+#include "portable/wait.h"
+
+#include "tap/basic.h"
+#include "tap/process.h"
+#include "tap/string.h"
+
+/* May be defined by the build system. */
+#ifndef PATH_FAKEROOT
+# define PATH_FAKEROOT ""
+#endif
+
+/* How long to wait for the process to start in seconds. */
+#define PROCESS_WAIT 10
+
+/*
+ * Used to store information about a background process. This contains
+ * everything required to stop the process and clean up after it.
+ */
+struct process {
+ pid_t pid; /* PID of child process */
+ char *pidfile; /* PID file to delete on process stop */
+ char *tmpdir; /* Temporary directory for log file */
+ char *logfile; /* Log file of process output */
+ bool is_child; /* Whether we can waitpid for process */
+ struct process *next; /* Next process in global list */
+};
+
+/*
+ * Global list of started processes, which will be cleaned up automatically on
+ * program exit if they haven't been explicitly stopped with process_stop
+ * prior to that point.
+ */
+static struct process *processes = NULL;
+
+
+/*
+ * Given a function, an expected exit status, and expected output, runs that
+ * function in a subprocess, capturing stdout and stderr via a pipe, and
+ * returns the function output in newly allocated memory. Also captures the
+ * process exit status.
+ */
+static void
+run_child_function(test_function_type function, void *data, int *status,
+ char **output)
+{
+ int fds[2];
+ pid_t child;
+ char *buf;
+ ssize_t count, ret, buflen;
+ int rval;
+
+ /* Flush stdout before we start to avoid odd forking issues. */
+ fflush(stdout);
+
+ /* Set up the pipe and call the function, collecting its output. */
+ if (pipe(fds) == -1)
+ sysbail("can't create pipe");
+ child = fork();
+ if (child == (pid_t) -1) {
+ sysbail("can't fork");
+ } else if (child == 0) {
+ /* In child. Set up our stdout and stderr. */
+ close(fds[0]);
+ if (dup2(fds[1], 1) == -1)
+ _exit(255);
+ if (dup2(fds[1], 2) == -1)
+ _exit(255);
+
+ /* Now, run the function and exit successfully if it returns. */
+ (*function)(data);
+ fflush(stdout);
+ _exit(0);
+ } else {
+ /*
+ * In the parent; close the extra file descriptor, read the output if
+ * any, and then collect the exit status.
+ */
+ close(fds[1]);
+ buflen = BUFSIZ;
+ buf = bmalloc(buflen);
+ count = 0;
+ do {
+ ret = read(fds[0], buf + count, buflen - count - 1);
+ if (ret > 0)
+ count += ret;
+ if (count >= buflen - 1) {
+ buflen += BUFSIZ;
+ buf = brealloc(buf, buflen);
+ }
+ } while (ret > 0);
+ buf[count < 0 ? 0 : count] = '\0';
+ if (waitpid(child, &rval, 0) == (pid_t) -1)
+ sysbail("waitpid failed");
+ close(fds[0]);
+ }
+
+ /* Store the output and return. */
+ *status = rval;
+ *output = buf;
+}
+
+
+/*
+ * Given a function, data to pass to that function, an expected exit status,
+ * and expected output, runs that function in a subprocess, capturing stdout
+ * and stderr via a pipe, and compare the combination of stdout and stderr
+ * with the expected output and the exit status with the expected status.
+ * Expects the function to always exit (not die from a signal).
+ */
+void
+is_function_output(test_function_type function, void *data, int status,
+ const char *output, const char *format, ...)
+{
+ char *buf, *msg;
+ int rval;
+ va_list args;
+
+ run_child_function(function, data, &rval, &buf);
+
+ /* Now, check the results against what we expected. */
+ va_start(args, format);
+ bvasprintf(&msg, format, args);
+ va_end(args);
+ ok(WIFEXITED(rval), "%s (exited)", msg);
+ is_int(status, WEXITSTATUS(rval), "%s (status)", msg);
+ is_string(output, buf, "%s (output)", msg);
+ free(buf);
+ free(msg);
+}
+
+
+/*
+ * A helper function for run_setup. This is a function to run an external
+ * command, suitable for passing into run_child_function. The expected
+ * argument must be an argv array, with argv[0] being the command to run.
+ */
+static void
+exec_command(void *data)
+{
+ char *const *argv = data;
+
+ execvp(argv[0], argv);
+}
+
+
+/*
+ * Given a command expressed as an argv struct, with argv[0] the name or path
+ * to the command, run that command. If it exits with a non-zero status, use
+ * the part of its output up to the first newline as the error message when
+ * calling bail.
+ */
+void
+run_setup(const char *const argv[])
+{
+ char *output, *p;
+ int status;
+
+ run_child_function(exec_command, (void *) argv, &status, &output);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ p = strchr(output, '\n');
+ if (p != NULL)
+ *p = '\0';
+ if (output[0] != '\0')
+ bail("%s", output);
+ else
+ bail("setup command failed with no output");
+ }
+ free(output);
+}
+
+
+/*
+ * Free the resources associated with tracking a process, without doing
+ * anything to the process. This is kept separate so that we can free
+ * resources during shutdown in a non-primary process.
+ */
+static void
+process_free(struct process *process)
+{
+ struct process **prev;
+
+ /* Remove the process from the global list. */
+ prev = &processes;
+ while (*prev != NULL && *prev != process)
+ prev = &(*prev)->next;
+ if (*prev == process)
+ *prev = process->next;
+
+ /* Free resources. */
+ free(process->pidfile);
+ free(process->logfile);
+ test_tmpdir_free(process->tmpdir);
+ free(process);
+}
+
+
+/*
+ * Kill a process and wait for it to exit. Returns the status of the process.
+ * Calls bail on a system failure or a failure of the process to exit.
+ *
+ * We are quite aggressive with error reporting here because child processes
+ * that don't exit or that don't exist often indicate some form of test
+ * failure.
+ */
+static int
+process_kill(struct process *process)
+{
+ int result, i;
+ int status = -1;
+ struct timeval tv;
+ unsigned long pid = process->pid;
+
+ /* If the process is not a child, just kill it and hope. */
+ if (!process->is_child) {
+ if (kill(process->pid, SIGTERM) < 0 && errno != ESRCH)
+ sysbail("cannot send SIGTERM to process %lu", pid);
+ return 0;
+ }
+
+ /* Check if the process has already exited. */
+ result = waitpid(process->pid, &status, WNOHANG);
+ if (result < 0)
+ sysbail("cannot wait for child process %lu", pid);
+ else if (result > 0)
+ return status;
+
+ /*
+ * Kill the process and wait for it to exit. I don't want to go to the
+ * work of setting up a SIGCHLD handler or a full event loop here, so we
+ * effectively poll every tenth of a second for process exit (and
+ * hopefully faster when it does since the SIGCHLD may interrupt our
+ * select, although we're racing with it.
+ */
+ if (kill(process->pid, SIGTERM) < 0 && errno != ESRCH)
+ sysbail("cannot send SIGTERM to child process %lu", pid);
+ for (i = 0; i < PROCESS_WAIT * 10; i++) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+ select(0, NULL, NULL, NULL, &tv);
+ result = waitpid(process->pid, &status, WNOHANG);
+ if (result < 0)
+ sysbail("cannot wait for child process %lu", pid);
+ else if (result > 0)
+ return status;
+ }
+
+ /* The process still hasn't exited. Bail. */
+ bail("child process %lu did not exit on SIGTERM", pid);
+
+ /* Not reached, but some compilers may get confused. */
+ return status;
+}
+
+
+/*
+ * Stop a particular process given its process struct. This kills the
+ * process, waits for it to exit if possible (giving it at most five seconds),
+ * and then removes it from the global processes struct so that it isn't
+ * stopped again during global shutdown.
+ */
+void
+process_stop(struct process *process)
+{
+ int status;
+ unsigned long pid = process->pid;
+
+ /* Stop the process. */
+ status = process_kill(process);
+
+ /* Call diag to flush logs as well as provide exit status. */
+ if (process->is_child)
+ diag("stopped process %lu (exit status %d)", pid, status);
+ else
+ diag("stopped process %lu", pid);
+
+ /* Remove the log and PID file. */
+ diag_file_remove(process->logfile);
+ unlink(process->pidfile);
+ unlink(process->logfile);
+
+ /* Free resources. */
+ process_free(process);
+}
+
+
+/*
+ * Stop all running processes. This is called as a cleanup handler during
+ * process shutdown. The first argument, which says whether the test was
+ * successful, is ignored, since the same actions should be performed
+ * regardless. The second argument says whether this is the primary process,
+ * in which case we do the full shutdown. Otherwise, we only free resources
+ * but don't stop the process.
+ */
+static void
+process_stop_all(int success UNUSED, int primary)
+{
+ while (processes != NULL) {
+ if (primary)
+ process_stop(processes);
+ else
+ process_free(processes);
+ }
+}
+
+
+/*
+ * Read the PID of a process from a file. This is necessary when running
+ * under fakeroot to get the actual PID of the remctld process.
+ */
+static long
+read_pidfile(const char *path)
+{
+ FILE *file;
+ char buffer[BUFSIZ];
+ long pid;
+
+ file = fopen(path, "r");
+ if (file == NULL)
+ sysbail("cannot open %s", path);
+ if (fgets(buffer, sizeof(buffer), file) == NULL)
+ sysbail("cannot read from %s", path);
+ fclose(file);
+ pid = strtol(buffer, NULL, 10);
+ if (pid <= 0)
+ bail("cannot read PID from %s", path);
+ return pid;
+}
+
+
+/*
+ * Start a process and return its status information. The status information
+ * is also stored in the global processes linked list so that it can be
+ * stopped automatically on program exit.
+ *
+ * The boolean argument says whether to start the process under fakeroot. If
+ * true, PATH_FAKEROOT must be defined, generally by Autoconf. If it's not
+ * found, call skip_all.
+ *
+ * This is a helper function for process_start and process_start_fakeroot.
+ */
+static struct process *
+process_start_internal(const char *const argv[], const char *pidfile,
+ bool fakeroot)
+{
+ size_t i;
+ int log_fd;
+ const char *name;
+ struct timeval tv;
+ struct process *process;
+ const char **fakeroot_argv = NULL;
+ const char *path_fakeroot = PATH_FAKEROOT;
+
+ /* Check prerequisites. */
+ if (fakeroot && path_fakeroot[0] == '\0')
+ skip_all("fakeroot not found");
+
+ /* Create the process struct and log file. */
+ process = bcalloc(1, sizeof(struct process));
+ process->pidfile = bstrdup(pidfile);
+ process->tmpdir = test_tmpdir();
+ name = strrchr(argv[0], '/');
+ if (name != NULL)
+ name++;
+ else
+ name = argv[0];
+ basprintf(&process->logfile, "%s/%s.log.XXXXXX", process->tmpdir, name);
+ log_fd = mkstemp(process->logfile);
+ if (log_fd < 0)
+ sysbail("cannot create log file for %s", argv[0]);
+
+ /* If using fakeroot, rewrite argv accordingly. */
+ if (fakeroot) {
+ for (i = 0; argv[i] != NULL; i++)
+ ;
+ fakeroot_argv = bcalloc(2 + i + 1, sizeof(const char *));
+ fakeroot_argv[0] = path_fakeroot;
+ fakeroot_argv[1] = "--";
+ for (i = 0; argv[i] != NULL; i++)
+ fakeroot_argv[i + 2] = argv[i];
+ fakeroot_argv[i + 2] = NULL;
+ argv = fakeroot_argv;
+ }
+
+ /*
+ * Fork off the child process, redirect its standard output and standard
+ * error to the log file, and then exec the program.
+ */
+ process->pid = fork();
+ if (process->pid < 0)
+ sysbail("fork failed");
+ else if (process->pid == 0) {
+ if (dup2(log_fd, STDOUT_FILENO) < 0)
+ sysbail("cannot redirect standard output");
+ if (dup2(log_fd, STDERR_FILENO) < 0)
+ sysbail("cannot redirect standard error");
+ close(log_fd);
+ if (execv(argv[0], (char *const *) argv) < 0)
+ sysbail("exec of %s failed", argv[0]);
+ }
+ close(log_fd);
+ free(fakeroot_argv);
+
+ /*
+ * In the parent. Wait for the child to start by watching for the PID
+ * file to appear in 100ms intervals.
+ */
+ for (i = 0; i < PROCESS_WAIT * 10 && access(pidfile, F_OK) != 0; i++) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+ select(0, NULL, NULL, NULL, &tv);
+ }
+
+ /*
+ * If the PID file still hasn't appeared after ten seconds, attempt to
+ * kill the process and then bail.
+ */
+ if (access(pidfile, F_OK) != 0) {
+ kill(process->pid, SIGTERM);
+ alarm(5);
+ waitpid(process->pid, NULL, 0);
+ alarm(0);
+ bail("cannot start %s", argv[0]);
+ }
+
+ /*
+ * Read the PID back from the PID file. This usually isn't necessary for
+ * non-forking daemons, but always doing this makes this function general,
+ * and it's required when running under fakeroot.
+ */
+ if (fakeroot)
+ process->pid = read_pidfile(pidfile);
+ process->is_child = !fakeroot;
+
+ /* Register the log file as a source of diag messages. */
+ diag_file_add(process->logfile);
+
+ /*
+ * Add the process to our global list and set our cleanup handler if this
+ * is the first process we started.
+ */
+ if (processes == NULL)
+ test_cleanup_register(process_stop_all);
+ process->next = processes;
+ processes = process;
+
+ /* All done. */
+ return process;
+}
+
+
+/*
+ * Start a process and return the opaque process struct. The process must
+ * create pidfile with its PID when startup is complete.
+ */
+struct process *
+process_start(const char *const argv[], const char *pidfile)
+{
+ return process_start_internal(argv, pidfile, false);
+}
+
+
+/*
+ * Start a process under fakeroot and return the opaque process struct. If
+ * fakeroot is not available, calls skip_all. The process must create pidfile
+ * with its PID when startup is complete.
+ */
+struct process *
+process_start_fakeroot(const char *const argv[], const char *pidfile)
+{
+ return process_start_internal(argv, pidfile, true);
+}
Property changes on: trunk/tests/tap/process.c
___________________________________________________________________
Added: svn:keywords
+ Author Date Id Revision
Added: svn:eol-style
+ native
Added: tests/tap/process.h
===================================================================
--- tests/tap/process.h (rev 0)
+++ tests/tap/process.h 2014-09-07 10:44:11 UTC (rev 9683)
@@ -0,0 +1,95 @@
+/* $Id$
+ *
+ * Utility functions for tests that use subprocesses.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <eagle at eyrie.org>
+ * Copyright 2009, 2010, 2013
+ * The Board of Trustees of the Leland Stanford Junior University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef TAP_PROCESS_H
+#define TAP_PROCESS_H 1
+
+#include "config.h"
+#include "tap/macros.h"
+
+/* Opaque data type for process_start and friends. */
+struct process;
+
+BEGIN_DECLS
+
+/*
+ * Run a function in a subprocess and check the exit status and expected
+ * output (stdout and stderr combined) against the provided values. Expects
+ * the function to always exit (not die from a signal). data is optional data
+ * that's passed into the function as its only argument.
+ *
+ * This reports as three separate tests: whether the function exited rather
+ * than was killed, whether the exit status was correct, and whether the
+ * output was correct.
+ */
+typedef void (*test_function_type)(void *);
+void is_function_output(test_function_type, void *data, int status,
+ const char *output, const char *format, ...)
+ __attribute__((__format__(printf, 5, 6), __nonnull__(1)));
+
+/*
+ * Run a setup program. Takes the program to run and its arguments as an argv
+ * vector, where argv[0] must be either the full path to the program or the
+ * program name if the PATH should be searched. If the program does not exit
+ * successfully, call bail, with the error message being the output from the
+ * program.
+ */
+void run_setup(const char *const argv[])
+ __attribute__((__nonnull__));
+
+/*
+ * process_start starts a process in the background, returning an opaque data
+ * struct that can be used to stop the process later. The standard output and
+ * standard error of the process will be sent to a log file registered with
+ * diag_file_add, so its output will be properly interleaved with the test
+ * case output.
+ *
+ * The process should create a PID file in the path given as the second
+ * argument when it's finished initialization.
+ *
+ * process_start_fakeroot is the same but starts the process under fakeroot.
+ * PATH_FAKEROOT must be defined (generally by Autoconf). If fakeroot is not
+ * found, process_start_fakeroot will call skip_all, so be sure to call this
+ * function before plan.
+ *
+ * process_stop can be called to explicitly stop the process. If it isn't
+ * called by the test program, it will be called automatically when the
+ * program exits.
+ */
+struct process *process_start(const char *const argv[], const char *pidfile)
+ __attribute__((__nonnull__));
+struct process *process_start_fakeroot(const char *const argv[],
+ const char *pidfile)
+ __attribute__((__nonnull__));
+void process_stop(struct process *);
+
+END_DECLS
+
+#endif /* TAP_PROCESS_H */
Property changes on: trunk/tests/tap/process.h
___________________________________________________________________
Added: svn:keywords
+ Author Date Id Revision
Added: svn:eol-style
+ native
More information about the inn-committers
mailing list