Make C plugin output format configurable

The development guidelines currently state that the plugin output
should be in the format "SERVICE STATUS: Information text".  However,
the SERVICE and STATUS information is redundant, as the plugin caller
(e.g., Nagios) should know which SERVICE is checked and the STATUS is
returned via the plugin's exit code.  After discussing this issue on
the development list, we decided to make the plugin output configurable
at compile time (separately for "normal" and "verbose" output).

 http://nagiosplug.sf.net/developer-guidelines.html
 http://thread.gmane.org/gmane.network.nagios.plugins.devel/5155

Signed-off-by: Holger Weiss <hweiss@users.sourceforge.net>
This commit is contained in:
Holger Weiss 2009-04-22 00:28:53 +02:00
parent dd52d7e5e7
commit 006597a12d
5 changed files with 263 additions and 19 deletions

View file

@ -39,6 +39,39 @@ INSTALL="$INSTALL $extra_install_args"
INSTALL_STRIP_PROGRAM="$INSTALL_STRIP_PROGRAM $extra_install_args"
AC_SUBST(INSTALL)
dnl Configure the plugin output format
default_output_format="%s %x: %i\n"
AC_ARG_WITH([standard_output_format],
[AS_HELP_STRING([--with-standard-output-format=FORMAT],
[specify the standard plugin output FORMAT; %p, %s, %x, and %m
will be replaced by the plugin name, the service name, the
status string, and the information message, respectively; tabs
or newlines can be inserted using \t or \n
@<:@default="%s %x: %m\n"@:>@])],
[standard_output_format=$withval],
[standard_output_format="yes"])
AC_ARG_WITH([verbose_output_format],
[AS_HELP_STRING([--with-verbose-output-format=FORMAT],
[specify the verbose plugin output FORMAT; %p, %s, %x, and %m
will be replaced by the plugin name, the service name, the
status string, and the information message, respectively; tabs
or newlines can be inserted using \t or \n
@<:@default="%s %x: %m\n"@:>@])],
[verbose_output_format=$withval],
[verbose_output_format="yes"])
AS_IF([test "$standard_output_format" = yes],
[standard_output_format=$default_output_format],
[test "$standard_output_format" = no],
[standard_output_format=""],
[test "$verbose_output_format" = yes],
[verbose_output_format=$default_output_format],
[test "$verbose_output_format" = no],
[verbose_output_format=""])
AC_DEFINE_UNQUOTED([STANDARD_OUTPUT_FORMAT], ["$standard_output_format"],
[Define the standard plugin output format.])
AC_DEFINE_UNQUOTED([VERBOSE_OUTPUT_FORMAT], ["$verbose_output_format"],
[Define the verbose plugin output format.])
AC_PROG_CC
gl_EARLY
AC_PROG_GCC_TRADITIONAL
@ -150,6 +183,11 @@ AC_CHECK_LIB(socket,socket,SOCKETLIBS="$SOCKETLIBS -lsocket")
AC_CHECK_LIB(resolv,main,SOCKETLIBS="$SOCKETLIBS -lresolv")
AC_SUBST(SOCKETLIBS)
dnl check for basename(3) which needs -lgen on some systems (e.g. IRIX)
AC_CHECK_HEADERS([libgen.h])
AC_SEARCH_LIBS([basename], [gen])
AC_CHECK_FUNCS([basename])
dnl
dnl check for math-related functions needing -lm
AC_CHECK_HEADERS(math.h)

View file

@ -24,10 +24,151 @@
*
*****************************************************************************/
#if HAVE_LIBGEN_H
#include <libgen.h> /* basename(3) */
#endif
#include <stdarg.h>
#include "common.h"
#include <stdarg.h>
#include "utils_base.h"
#define PRINT_OUTPUT(fmt, ap) \
do { \
fmt = insert_syserr(fmt); \
va_start(ap, fmt); \
vprintf(fmt, ap); \
va_end(ap); \
} while (/* CONSTCOND */ 0)
static char *insert_syserr(const char *);
extern int errno;
static int verbosity_level = -2;
static const char *program_name = NULL;
static const char *service_name = NULL;
/*
* Set static variables for use in output functions. Usually, argv[0] may be
* used as progname, since we call basename(3) ourselves. If a verbosity value
* of -2 is specified, the verbosity_level won't be set. Currently, no flags
* are implemented.
*/
void
np_set_output(const char *progname, const char *servname, int verbosity,
int flags __attribute__((unused)))
{
static char pathbuf[128], progbuf[128], servbuf[32];
if (progname != NULL) {
#if HAVE_BASENAME
/*
* Copy the progname into a temporary buffer in order to cope
* with basename(3) implementations which modify their argument.
* TODO: Maybe we should implement an np_basename()? Gnulib's
* base_name() dies on error, writing a message to stderr, which
* is probably not what we want. Once we have some replacement,
* the libgen-/basename(3)-related checks can be removed from
* configure.in.
*/
strncpy(pathbuf, progname, sizeof(pathbuf) - 1);
pathbuf[sizeof(pathbuf) - 1] = '\0';
progname = basename(pathbuf);
#endif
strncpy(progbuf, progname, sizeof(progbuf) - 1);
progbuf[sizeof(progbuf) - 1] = '\0';
program_name = progbuf;
}
if (servname != NULL) {
strncpy(servbuf, servname, sizeof(servbuf) - 1);
servbuf[sizeof(servbuf) - 1] = '\0';
service_name = servbuf;
}
if (verbosity != -2)
verbosity_level = verbosity;
}
int
np_adjust_verbosity(int by)
{
if (verbosity_level == -2)
verbosity_level = by;
else
verbosity_level += by;
/* We don't support verbosity levels < -1. */
if (verbosity_level < -1)
verbosity_level = -1;
return verbosity_level;
}
void
np_debug(int verbosity, const char *fmt, ...)
{
va_list ap;
if (verbosity_level != -1 && verbosity >= verbosity_level)
PRINT_OUTPUT(fmt, ap);
}
void
np_verbose(const char *fmt, ...)
{
va_list ap;
if (verbosity_level >= 1) {
PRINT_OUTPUT(fmt, ap);
putchar('\n');
}
}
void
np_die(int status, const char *fmt, ...)
{
va_list ap;
const char *p;
if (program_name == NULL || service_name == NULL)
PRINT_OUTPUT(fmt, ap);
for (p = (verbosity_level > 0) ?
VERBOSE_OUTPUT_FORMAT : STANDARD_OUTPUT_FORMAT;
*p != '\0'; p++) {
if (*p == '%') {
if (*++p == '\0')
break;
switch (*p) {
case 'm':
PRINT_OUTPUT(fmt, ap);
continue;
case 'p':
fputs(program_name, stdout);
continue;
case 's':
fputs(service_name, stdout);
continue;
case 'x':
fputs(state_text(status), stdout);
continue;
}
} else if (*p == '\\') {
if (*++p == '\0')
break;
switch (*p) {
case 'n':
putchar('\n');
continue;
case 't':
putchar('\t');
continue;
}
}
putchar(*p);
}
exit(status);
}
/* TODO: die() can be removed as soon as all plugins use np_die() instead. */
void
die (int result, const char *fmt, ...)
{
@ -308,3 +449,67 @@ char *np_extract_value(const char *varlist, const char *name, char sep) {
return value;
}
/*
* Replace occurrences of "%m" by strerror(errno). Other printf(3)-style
* conversion specifications will be copied verbatim, including "%%", even if
* followed by an "m". Returns a pointer to a static buffer in order to not
* fail on memory allocation error.
*/
static char *
insert_syserr(const char *buf)
{
static char newbuf[8192];
char *errstr = strerror(errno);
size_t errlen = strlen(errstr);
size_t copylen;
unsigned i, j;
for (i = 0, j = 0; buf[i] != '\0' && j < sizeof(newbuf) - 2; i++, j++) {
if (buf[i] == '%') {
if (buf[++i] == 'm') {
copylen = (errlen > sizeof(newbuf) - j - 1) ?
sizeof(newbuf) - j - 1 : errlen;
memcpy(newbuf + j, errstr, copylen);
/*
* As we'll increment j by 1 after the iteration
* anyway, we only increment j by the number of
* copied bytes - 1.
*/
j += copylen - 1;
continue;
} else {
/*
* The possibility to run into this block is the
* reason we checked for j < sizeof(newbuf) - 2
* instead of j < sizeof(newbuf) - 1.
*/
newbuf[j++] = '%';
if (buf[i] == '\0')
break;
}
}
newbuf[j] = buf[i];
}
newbuf[j] = '\0';
return newbuf;
}
/*
* Given a numerical status, return a pointer to the according string.
*/
const char *
state_text(int result)
{
switch (result) {
case STATE_OK:
return "OK";
case STATE_WARNING:
return "WARNING";
case STATE_CRITICAL:
return "CRITICAL";
case STATE_DEPENDENT:
return "DEPENDENT";
default:
return "UNKNOWN";
}
}

View file

@ -37,6 +37,23 @@ int get_status(double, thresholds *);
char *np_escaped_string (const char *);
void np_set_output(const char *, const char *, int, int);
int np_adjust_verbosity(int);
void np_debug(int, const char *, ...)
__attribute__((format(printf, 2, 3)));
void np_verbose(const char *, ...)
__attribute__((format(printf, 1, 2)));
void np_die(int, const char *, ...)
__attribute__((noreturn, format(printf, 2, 3)));
#define np_verbatim(s) np_verbose("%s", s)
#define np_increase_verbosity(i) np_adjust_verbosity(i)
#define np_decrease_verbosity(i) np_adjust_verbosity(-i)
#define np_get_verbosity() np_adjust_verbosity(0)
#define np_set_verbosity(v) np_set_output(NULL, NULL, v, 0)
#define np_set_mynames(p, s) np_set_output(p, s, -2, 0)
/* TODO: die() can be removed as soon as all plugins use np_die() instead. */
void die (int, const char *, ...) __attribute__((noreturn,format(printf, 2, 3)));
/* Return codes for _set_thresholds */
@ -64,4 +81,7 @@ char *np_extract_value(const char*, const char*, char);
*/
#define np_extract_ntpvar(l, n) np_extract_value(l, n, ',')
/* Given a numerical status, return a pointer to the according string. */
const char *state_text(int);
#endif /* _UTILS_BASE_ */

View file

@ -147,23 +147,6 @@ print_revision (const char *command_name, const char *revision)
command_name, revision, PACKAGE, VERSION);
}
const char *
state_text (int result)
{
switch (result) {
case STATE_OK:
return "OK";
case STATE_WARNING:
return "WARNING";
case STATE_CRITICAL:
return "CRITICAL";
case STATE_DEPENDENT:
return "DEPENDENT";
default:
return "UNKNOWN";
}
}
void
timeout_alarm_handler (int signo)
{

View file

@ -92,8 +92,6 @@ void usage4(const char *) __attribute__((noreturn));
void usage5(void) __attribute__((noreturn));
void usage_va(const char *fmt, ...) __attribute__((noreturn));
const char *state_text (int);
#define max(a,b) (((a)>(b))?(a):(b))
#define min(a,b) (((a)<(b))?(a):(b))