Revision: 1784
http://nagiosplug.svn.sourceforge.net/nagiosplug/?rev=1784&view=rev
Author: tonvoon
Date: 2007-09-21 16:01:28 -0700 (Fri, 21 Sep 2007)
Log Message:
-----------
Stop double expansion of parameters for negate - works like
time command now
Modified Paths:
--------------
nagiosplug/trunk/NEWS
nagiosplug/trunk/configure.in
nagiosplug/trunk/lib/Makefile.am
nagiosplug/trunk/lib/tests/Makefile.am
nagiosplug/trunk/plugins/Makefile.am
nagiosplug/trunk/plugins/negate.c
Added Paths:
-----------
nagiosplug/trunk/lib/tests/test_cmd.c
nagiosplug/trunk/lib/tests/test_cmd.t
nagiosplug/trunk/lib/utils_cmd.c
nagiosplug/trunk/lib/utils_cmd.h
nagiosplug/trunk/plugins/t/negate.t
Removed Paths:
-------------
nagiosplug/trunk/plugins/t/negate.pl
Modified: nagiosplug/trunk/NEWS
===================================================================
--- nagiosplug/trunk/NEWS 2007-09-21 05:05:58 UTC (rev 1783)
+++ nagiosplug/trunk/NEWS 2007-09-21 23:01:28 UTC (rev 1784)
@@ -16,6 +16,9 @@
The check_dhcp -r and -s options now accept host names, too
Fix possible check_icmp bus errors on some (non-x86/AMD64) platforms
Fix check_smtp's handling of multiple-packet server responses
+ WARNING: Fix for negate which may break existing commands:
+ - stop evaluating command line options through shell twice
+ - enforce a full path for the command to run
1.4.9 4th June 2006
Inclusion of contrib/check_cluster2 as check_cluster with some improvements
Modified: nagiosplug/trunk/configure.in
===================================================================
--- nagiosplug/trunk/configure.in 2007-09-21 05:05:58 UTC (rev 1783)
+++ nagiosplug/trunk/configure.in 2007-09-21 23:01:28 UTC (rev 1784)
@@ -155,7 +155,7 @@
dnl Check for libtap, to run perl-like tests
AC_CHECK_LIB(tap, plan_tests,
- EXTRA_TEST="test_utils test_disk test_tcp"
+ EXTRA_TEST="test_utils test_disk test_tcp test_cmd"
AC_SUBST(EXTRA_TEST)
)
Modified: nagiosplug/trunk/lib/Makefile.am
===================================================================
--- nagiosplug/trunk/lib/Makefile.am 2007-09-21 05:05:58 UTC (rev 1783)
+++ nagiosplug/trunk/lib/Makefile.am 2007-09-21 23:01:28 UTC (rev 1784)
@@ -5,8 +5,8 @@
noinst_LIBRARIES = libnagiosplug.a
-libnagiosplug_a_SOURCES = utils_base.c utils_disk.c utils_tcp.c
-EXTRA_DIST = utils_base.h utils_disk.h utils_tcp.h
+libnagiosplug_a_SOURCES = utils_base.c utils_disk.c utils_tcp.c utils_cmd.c
+EXTRA_DIST = utils_base.h utils_disk.h utils_tcp.h utils_cmd.h
INCLUDES = -I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins
Modified: nagiosplug/trunk/lib/tests/Makefile.am
===================================================================
--- nagiosplug/trunk/lib/tests/Makefile.am 2007-09-21 05:05:58 UTC (rev 1783)
+++ nagiosplug/trunk/lib/tests/Makefile.am 2007-09-21 23:01:28 UTC (rev 1784)
@@ -7,9 +7,9 @@
INCLUDES = -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins
-EXTRA_PROGRAMS = test_utils test_disk test_tcp
+EXTRA_PROGRAMS = test_utils test_disk test_tcp test_cmd
-EXTRA_DIST = test_utils.t test_disk.t test_tcp.t
+EXTRA_DIST = test_utils.t test_disk.t test_tcp.t test_cmd.t
LIBS = @LIBINTL@
@@ -28,6 +28,11 @@
test_tcp_LDFLAGS = -L/usr/local/lib -ltap
test_tcp_LDADD = ../utils_tcp.o
+test_cmd_SOURCES = test_cmd.c
+test_cmd_CFLAGS = -g -I..
+test_cmd_LDFLAGS = -L/usr/local/lib -ltap
+test_cmd_LDADD = ../utils_cmd.o ../utils_base.o
+
test: ${noinst_PROGRAMS}
perl -MTest::Harness -e '$$Test::Harness::switches=""; runtests(map {$$_ .= ".t"} @ARGV)' $(EXTRA_PROGRAMS)
Added: nagiosplug/trunk/lib/tests/test_cmd.c
===================================================================
--- nagiosplug/trunk/lib/tests/test_cmd.c (rev 0)
+++ nagiosplug/trunk/lib/tests/test_cmd.c 2007-09-21 23:01:28 UTC (rev 1784)
@@ -0,0 +1,210 @@
+/******************************************************************************
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: test_cmd.c 1732 2007-06-03 15:58:22Z psychotrahe $
+
+******************************************************************************/
+
+#include "common.h"
+#include "utils_cmd.h"
+#include "utils_base.h"
+#include "tap.h"
+
+#define COMMAND_LINE 1024
+#define UNSET 65530
+
+char *
+get_command (char *const *line)
+{
+ char *cmd;
+ int i = 0;
+
+ asprintf (&cmd, " %s", line[i++]);
+ while (line[i] != NULL) {
+ asprintf (&cmd, "%s %s", cmd, line[i]);
+ i++;
+ }
+
+ return cmd;
+}
+
+int
+main (int argc, char **argv)
+{
+ char **command_line = malloc (sizeof (char *) * COMMAND_LINE);
+ char *command = NULL;
+ char *perl;
+ output chld_out, chld_err;
+ int c;
+ int result = UNSET;
+
+ plan_tests(47);
+
+ diag ("Running plain echo command, set one");
+
+ /* ensure everything is empty before we begin */
+ memset (&chld_out, 0, sizeof (output));
+ memset (&chld_err, 0, sizeof (output));
+ ok (chld_out.lines == 0, "(initialised) Checking stdout is reset");
+ ok (chld_err.lines == 0, "(initialised) Checking stderr is reset");
+ ok (result == UNSET, "(initialised) Checking exit code is reset");
+
+ command_line[0] = strdup ("/bin/echo");
+ command_line[1] = strdup ("this");
+ command_line[2] = strdup ("is");
+ command_line[3] = strdup ("test");
+ command_line[4] = strdup ("one");
+
+ command = get_command (command_line);
+
+ result = cmd_run_array (command_line, &chld_out, &chld_err, 0);
+ ok (chld_out.lines == 1,
+ "(array) Check for expected number of stdout lines");
+ ok (chld_err.lines == 0,
+ "(array) Check for expected number of stderr lines");
+ ok (strcmp (chld_out.line[0], "this is test one") == 0,
+ "(array) Check for expected stdout output");
+ ok (result == 0, "(array) Checking exit code");
+
+ /* ensure everything is empty again */
+ memset (&chld_out, 0, sizeof (output));
+ memset (&chld_err, 0, sizeof (output));
+ result = UNSET;
+ ok (chld_out.lines == 0, "(initialised) Checking stdout is reset");
+ ok (chld_err.lines == 0, "(initialised) Checking stderr is reset");
+ ok (result == UNSET, "(initialised) Checking exit code is reset");
+
+ result = cmd_run (command, &chld_out, &chld_err, 0);
+
+ ok (chld_out.lines == 1,
+ "(string) Check for expected number of stdout lines");
+ ok (chld_err.lines == 0,
+ "(string) Check for expected number of stderr lines");
+ ok (strcmp (chld_out.line[0], "this is test one") == 0,
+ "(string) Check for expected stdout output");
+ ok (result == 0, "(string) Checking exit code");
+
+ diag ("Running plain echo command, set two");
+
+ /* ensure everything is empty again */
+ memset (&chld_out, 0, sizeof (output));
+ memset (&chld_err, 0, sizeof (output));
+ result = UNSET;
+ ok (chld_out.lines == 0, "(initialised) Checking stdout is reset");
+ ok (chld_err.lines == 0, "(initialised) Checking stderr is reset");
+ ok (result == UNSET, "(initialised) Checking exit code is reset");
+
+ command_line[0] = strdup ("/bin/echo");
+ command_line[1] = strdup ("this is test two");
+ command_line[2] = NULL;
+ command_line[3] = NULL;
+ command_line[4] = NULL;
+
+ result = cmd_run_array (command_line, &chld_out, &chld_err, 0);
+ ok (chld_out.lines == 1,
+ "(array) Check for expected number of stdout lines");
+ ok (chld_err.lines == 0,
+ "(array) Check for expected number of stderr lines");
+ ok (strcmp (chld_out.line[0], "this is test two") == 0,
+ "(array) Check for expected stdout output");
+ ok (result == 0, "(array) Checking exit code");
+
+ /* ensure everything is empty again */
+ memset (&chld_out, 0, sizeof (output));
+ memset (&chld_err, 0, sizeof (output));
+ result = UNSET;
+ ok (chld_out.lines == 0, "(initialised) Checking stdout is reset");
+ ok (chld_err.lines == 0, "(initialised) Checking stderr is reset");
+ ok (result == UNSET, "(initialised) Checking exit code is reset");
+
+ result = cmd_run (command, &chld_out, &chld_err, 0);
+
+ ok (chld_out.lines == 1,
+ "(string) Check for expected number of stdout lines");
+ ok (chld_err.lines == 0,
+ "(string) Check for expected number of stderr lines");
+ ok (strcmp (chld_out.line[0], "this is test one") == 0,
+ "(string) Check for expected stdout output");
+ ok (result == 0, "(string) Checking exit code");
+
+
+ /* ensure everything is empty again */
+ memset (&chld_out, 0, sizeof (output));
+ memset (&chld_err, 0, sizeof (output));
+ result = UNSET;
+ ok (chld_out.lines == 0, "(initialised) Checking stdout is reset");
+ ok (chld_err.lines == 0, "(initialised) Checking stderr is reset");
+ ok (result == UNSET, "(initialised) Checking exit code is reset");
+
+ /* Pass linefeeds via parameters through - those should be evaluated by echo to give multi line output */
+ command_line[0] = strdup("/bin/echo");
+ command_line[1] = strdup("this is a test via echo\nline two\nit's line 3");
+ command_line[2] = strdup("and (note space between '3' and 'and') $$ will not get evaluated");
+
+ result = cmd_run_array (command_line, &chld_out, &chld_err, 0);
+ ok (chld_out.lines == 3,
+ "(array) Check for expected number of stdout lines");
+ ok (chld_err.lines == 0,
+ "(array) Check for expected number of stderr lines");
+ ok (strcmp (chld_out.line[0], "this is a test via echo") == 0,
+ "(array) Check line 1 for expected stdout output");
+ ok (strcmp (chld_out.line[1], "line two") == 0,
+ "(array) Check line 2 for expected stdout output");
+ ok (strcmp (chld_out.line[2], "it's line 3 and (note space between '3' and 'and') $$ will not get evaluated") == 0,
+ "(array) Check line 3 for expected stdout output");
+ ok (result == 0, "(array) Checking exit code");
+
+
+
+ /* ensure everything is empty again */
+ memset (&chld_out, 0, sizeof (output));
+ memset (&chld_err, 0, sizeof (output));
+ result = UNSET;
+ ok (chld_out.lines == 0, "(initialised) Checking stdout is reset");
+ ok (chld_err.lines == 0, "(initialised) Checking stderr is reset");
+ ok (result == UNSET, "(initialised) Checking exit code is reset");
+
+ command = (char *)malloc(COMMAND_LINE);
+ strcpy(command, "/bin/echo3456 non-existant command");
+ result = cmd_run (command, &chld_out, &chld_err, 0);
+
+ ok (chld_out.lines == 0,
+ "Non existant command, so no output");
+ ok (chld_err.lines == 0,
+ "No stderr either");
+ ok (result == 3, "Get return code 3 (?) for non-existant command");
+
+
+ /* ensure everything is empty again */
+ memset (&chld_out, 0, sizeof (output));
+ memset (&chld_err, 0, sizeof (output));
+ result = UNSET;
+
+ command = (char *)malloc(COMMAND_LINE);
+ strcpy(command, "/bin/grep pattern non-existant-file");
+ result = cmd_run (command, &chld_out, &chld_err, 0);
+
+ ok (chld_out.lines == 0,
+ "Grep returns no stdout when file is missing...");
+ ok (chld_err.lines == 1,
+ "...but does give an error line");
+ ok (strstr(chld_err.line[0],"non-existant-file") != NULL, "And missing filename is in error message");
+ ok (result == 2, "Get return code 2 from grep");
+
+
+
+ return exit_status ();
+}
Added: nagiosplug/trunk/lib/tests/test_cmd.t
===================================================================
--- nagiosplug/trunk/lib/tests/test_cmd.t (rev 0)
+++ nagiosplug/trunk/lib/tests/test_cmd.t 2007-09-21 23:01:28 UTC (rev 1784)
@@ -0,0 +1,6 @@
+#!/usr/bin/perl
+use Test::More;
+if (! -e "./test_cmd") {
+ plan skip_all => "./test_cmd not compiled - please install tap library to test";
+}
+exec "./test_cmd";
Added: nagiosplug/trunk/lib/utils_cmd.c
===================================================================
--- nagiosplug/trunk/lib/utils_cmd.c (rev 0)
+++ nagiosplug/trunk/lib/utils_cmd.c 2007-09-21 23:01:28 UTC (rev 1784)
@@ -0,0 +1,378 @@
+/****************************************************************************
+ * Nagios run command utilities
+ *
+ * License: GPL
+ * Copyright (c) 2005 nagios-plugins team
+ *
+ * $Id: utils_cmd.c 1434 2006-06-18 19:36:48Z opensides $
+ *
+ * Description :
+ *
+ * A simple interface to executing programs from other programs, using an
+ * optimized and safe popen()-like implementation. It is considered safe
+ * in that no shell needs to be spawned and the environment passed to the
+ * execve()'d program is essentially empty.
+ *
+ *
+ * The code in this file is a derivative of popen.c which in turn was taken
+ * from "Advanced Programming for the Unix Environment" by W. Richard Stevens.
+ *
+ * Care has been taken to make sure the functions are async-safe. The one
+ * function which isn't is cmd_init() which it doesn't make sense to
+ * call twice anyway, so the api as a whole should be considered async-safe.
+ *
+ * License Information:
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define NAGIOSPLUG_API_C 1
+
+/** includes **/
+#include "common.h"
+#include "utils_cmd.h"
+#include "utils_base.h"
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+/** macros **/
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */
+#if defined(SIG_IGN) && !defined(SIG_ERR)
+# define SIG_ERR ((Sigfunc *)-1)
+#endif
+
+/* This variable must be global, since there's no way the caller
+ * can forcibly slay a dead or ungainly running program otherwise.
+ * Multithreading apps and plugins can initialize it (via CMD_INIT)
+ * in an async safe manner PRIOR to calling cmd_run() or cmd_run_array()
+ * for the first time.
+ *
+ * The check for initialized values is atomic and can
+ * occur in any number of threads simultaneously. */
+static pid_t *_cmd_pids = NULL;
+
+/* Try sysconf(_SC_OPEN_MAX) first, as it can be higher than OPEN_MAX.
+ * If that fails and the macro isn't defined, we fall back to an educated
+ * guess. There's no guarantee that our guess is adequate and the program
+ * will die with SIGSEGV if it isn't and the upper boundary is breached. */
+#ifdef _SC_OPEN_MAX
+static long maxfd = 0;
+#elif defined(OPEN_MAX)
+# define maxfd OPEN_MAX
+#else /* sysconf macro unavailable, so guess (may be wildly inaccurate) */
+# define maxfd 256
+#endif
+
+
+/** prototypes **/
+static int _cmd_open (char *const *, int *, int *)
+ __attribute__ ((__nonnull__ (1, 2, 3)));
+
+static int _cmd_fetch_output (int, output *, int)
+ __attribute__ ((__nonnull__ (2)));
+
+static int _cmd_close (int);
+
+/* prototype imported from utils.h */
+extern void die (int, const char *, ...)
+ __attribute__ ((__noreturn__, __format__ (__printf__, 2, 3)));
+
+
+/* this function is NOT async-safe. It is exported so multithreaded
+ * plugins (or other apps) can call it prior to running any commands
+ * through this api and thus achieve async-safeness throughout the api */
+void
+cmd_init (void)
+{
+#ifndef maxfd
+ if (!maxfd && (maxfd = sysconf (_SC_OPEN_MAX)) < 0) {
+ /* possibly log or emit a warning here, since there's no
+ * guarantee that our guess at maxfd will be adequate */
+ maxfd = 256;
+ }
+#endif
+
+ if (!_cmd_pids)
+ _cmd_pids = calloc (maxfd, sizeof (pid_t));
+}
+
+
+/* Start running a command, array style */
+static int
+_cmd_open (char *const *argv, int *pfd, int *pfderr)
+{
+ char *env[2];
+ pid_t pid;
+#ifdef RLIMIT_CORE
+ struct rlimit limit;
+#endif
+
+ int i = 0;
+
+ /* if no command was passed, return with no error */
+ if (argv == NULL)
+ return -1;
+
+ if (!_cmd_pids)
+ CMD_INIT;
+
+ env[0] = strdup ("LC_ALL=C");
+ env[1] = '\0';
+
+ if (pipe (pfd) < 0 || pipe (pfderr) < 0 || (pid = fork ()) < 0)
+ return -1; /* errno set by the failing function */
+
+ /* child runs exceve() and _exit. */
+ if (pid == 0) {
+#ifdef RLIMIT_CORE
+ /* the program we execve shouldn't leave core files */
+ getrlimit (RLIMIT_CORE, &limit);
+ limit.rlim_cur = 0;
+ setrlimit (RLIMIT_CORE, &limit);
+#endif
+ close (pfd[0]);
+ if (pfd[1] != STDOUT_FILENO) {
+ dup2 (pfd[1], STDOUT_FILENO);
+ close (pfd[1]);
+ }
+ close (pfderr[0]);
+ if (pfderr[1] != STDERR_FILENO) {
+ dup2 (pfderr[1], STDERR_FILENO);
+ close (pfderr[1]);
+ }
+
+ /* close all descriptors in _cmd_pids[]
+ * This is executed in a separate address space (pure child),
+ * so we don't have to worry about async safety */
+ for (i = 0; i < maxfd; i++)
+ if (_cmd_pids[i] > 0)
+ close (i);
+
+ execve (argv[0], argv, env);
+ _exit (STATE_UNKNOWN);
+ }
+
+ /* parent picks up execution here */
+ /* close childs descriptors in our address space */
+ close (pfd[1]);
+ close (pfderr[1]);
+
+ /* tag our file's entry in the pid-list and return it */
+ _cmd_pids[pfd[0]] = pid;
+
+ return pfd[0];
+}
+
+static int
+_cmd_close (int fd)
+{
+ int status;
+ pid_t pid;
+
+ /* make sure the provided fd was opened */
+ if (fd < 0 || fd > maxfd || !_cmd_pids || (pid = _cmd_pids[fd]) == 0)
+ return -1;
+
+ _cmd_pids[fd] = 0;
+ if (close (fd) == -1)
+ return -1;
+
+ /* EINTR is ok (sort of), everything else is bad */
+ while (waitpid (pid, &status, 0) < 0)
+ if (errno != EINTR)
+ return -1;
+
+ /* return child's termination status */
+ return (WIFEXITED (status)) ? WEXITSTATUS (status) : -1;
+}
+
+
+static int
+_cmd_fetch_output (int fd, output * op, int flags)
+{
+ size_t len = 0, i = 0, lineno = 0;
+ size_t rsf = 6, ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */
+ char *buf = NULL;
+ int ret;
+ char tmpbuf[4096];
+
+ op->buf = NULL;
+ op->buflen = 0;
+ while ((ret = read (fd, tmpbuf, sizeof (tmpbuf))) > 0) {
+ len = (size_t) ret;
+ op->buf = realloc (op->buf, op->buflen + len + 1);
+ memcpy (op->buf + op->buflen, tmpbuf, len);
+ op->buflen += len;
+ i++;
+ }
+
+ if (ret < 0) {
+ printf ("read() returned %d: %s\n", ret, strerror (errno));
+ return ret;
+ }
+
+ /* some plugins may want to keep output unbroken, and some commands
+ * will yield no output, so return here for those */
+ if (flags & CMD_NO_ARRAYS || !op->buf || !op->buflen)
+ return op->buflen;
+
+ /* and some may want both */
+ if (flags & CMD_NO_ASSOC) {
+ buf = malloc (op->buflen);
+ memcpy (buf, op->buf, op->buflen);
+ }
+ else
+ buf = op->buf;
+
+ op->line = NULL;
+ op->lens = NULL;
+ i = 0;
+ while (i < op->buflen) {
+ /* make sure we have enough memory */
+ if (lineno >= ary_size) {
+ /* ary_size must never be zero */
+ do {
+ ary_size = op->buflen >> --rsf;
+ } while (!ary_size);
+
+ op->line = realloc (op->line, ary_size * sizeof (char *));
+ op->lens = realloc (op->lens, ary_size * sizeof (size_t));
+ }
+
+ /* set the pointer to the string */
+ op->line[lineno] = &buf[i];
+
+ /* hop to next newline or end of buffer */
+ while (buf[i] != '\n' && i < op->buflen)
+ i++;
+ buf[i] = '\0';
+
+ /* calculate the string length using pointer difference */
+ op->lens[lineno] = (size_t) & buf[i] - (size_t) op->line[lineno];
+
+ lineno++;
+ i++;
+ }
+
+ return lineno;
+}
+
+
+int
+cmd_run (const char *cmdstring, output * out, output * err, int flags)
+{
+ int fd, pfd_out[2], pfd_err[2];
+ int i = 0, argc;
+ size_t cmdlen;
+ char **argv = NULL;
+ char *cmd = NULL;
+ char *str = NULL;
+
+ if (cmdstring == NULL)
+ return -1;
+
+ /* initialize the structs */
+ if (out)
+ memset (out, 0, sizeof (output));
+ if (err)
+ memset (err, 0, sizeof (output));
+
+ /* make copy of command string so strtok() doesn't silently modify it */
+ /* (the calling program may want to access it later) */
+ cmdlen = strlen (cmdstring);
+ if ((cmd = malloc (cmdlen + 1)) == NULL)
+ return -1;
+ memcpy (cmd, cmdstring, cmdlen);
+ cmd[cmdlen] = '\0';
+
+ /* This is not a shell, so we don't handle "???" */
+ if (strstr (cmdstring, "\"")) return -1;
+
+ /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */
+ if (strstr (cmdstring, " ' ") || strstr (cmdstring, "'''"))
+ return -1;
+
+ /* each arg must be whitespace-separated, so args can be a maximum
+ * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */
+ argc = (cmdlen >> 1) + 2;
+ argv = calloc (sizeof (char *), argc);
+
+ if (argv == NULL) {
+ printf ("%s\n", _("Could not malloc argv array in popen()"));
+ return -1;
+ }
+
+ /* get command arguments (stupidly, but fairly quickly) */
+ while (cmd) {
+ str = cmd;
+ str += strspn (str, " \t\r\n"); /* trim any leading whitespace */
+
+ if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */
+ str++;
+ if (!strstr (str, "'"))
+ return -1; /* balanced? */
+ cmd = 1 + strstr (str, "'");
+ str[strcspn (str, "'")] = 0;
+ }
+ else {
+ if (strpbrk (str, " \t\r\n")) {
+ cmd = 1 + strpbrk (str, " \t\r\n");
+ str[strcspn (str, " \t\r\n")] = 0;
+ }
+ else {
+ cmd = NULL;
+ }
+ }
+
+ if (cmd && strlen (cmd) == strspn (cmd, " \t\r\n"))
+ cmd = NULL;
+
+ argv[i++] = str;
+ }
+
+ return cmd_run_array (argv, out, err, flags);
+}
+
+int
+cmd_run_array (char *const *argv, output * out, output * err, int flags)
+{
+ int fd, pfd_out[2], pfd_err[2];
+
+ /* initialize the structs */
+ if (out)
+ memset (out, 0, sizeof (output));
+ if (err)
+ memset (err, 0, sizeof (output));
+
+ if ((fd = _cmd_open (argv, pfd_out, pfd_err)) == -1)
+ die (STATE_UNKNOWN, _("Could not open pipe: %s\n"), argv[0]);
+
+ if (out)
+ out->lines = _cmd_fetch_output (pfd_out[0], out, flags);
+ if (err)
+ err->lines = _cmd_fetch_output (pfd_err[0], err, flags);
+
+ return _cmd_close (fd);
+}
Added: nagiosplug/trunk/lib/utils_cmd.h
===================================================================
--- nagiosplug/trunk/lib/utils_cmd.h (rev 0)
+++ nagiosplug/trunk/lib/utils_cmd.h 2007-09-21 23:01:28 UTC (rev 1784)
@@ -0,0 +1,34 @@
+#ifndef _UTILS_CMD_
+#define _UTILS_CMD_
+
+/*
+ * Header file for nagios plugins utils_cmd.c
+ *
+ *
+ */
+
+/** types **/
+struct output
+{
+ char *buf; /* output buffer */
+ size_t buflen; /* output buffer content length */
+ char **line; /* array of lines (points to buf) */
+ size_t *lens; /* string lengths */
+ size_t lines; /* lines of output */
+};
+
+typedef struct output output;
+
+/** prototypes **/
+int cmd_run (const char *, output *, output *, int);
+int cmd_run_array (char *const *, output *, output *, int);
+
+/* only multi-threaded plugins need to bother with this */
+void cmd_init (void);
+#define CMD_INIT cmd_init()
+
+/* possible flags for cmd_run()'s fourth argument */
+#define CMD_NO_ARRAYS 0x01 /* don't populate arrays at all */
+#define CMD_NO_ASSOC 0x02 /* output.line won't point to buf */
+
+#endif /* _UTILS_CMD_ */
Modified: nagiosplug/trunk/plugins/Makefile.am
===================================================================
--- nagiosplug/trunk/plugins/Makefile.am 2007-09-21 05:05:58 UTC (rev 1783)
+++ nagiosplug/trunk/plugins/Makefile.am 2007-09-21 23:01:28 UTC (rev 1784)
@@ -87,7 +87,7 @@
check_users_LDADD = $(BASEOBJS) popen.o
check_by_ssh_LDADD = $(NETLIBS) runcmd.o
check_ide_smart_LDADD = $(BASEOBJS)
-negate_LDADD = $(BASEOBJS) popen.o
+negate_LDADD = $(BASEOBJS)
urlize_LDADD = $(BASEOBJS) popen.o
check_apt_DEPENDENCIES = check_apt.c $(BASEOBJS) runcmd.o $(DEPLIBS)
@@ -126,7 +126,7 @@
check_ups_DEPENDENCIES = check_ups.c $(NETOBJS) $(DEPLIBS)
check_users_DEPENDENCIES = check_users.c $(BASEOBJS) popen.o $(DEPLIBS)
check_by_ssh_DEPENDENCIES = check_by_ssh.c $(NETOBJS) runcmd.o $(DEPLIBS)
-negate_DEPENDENCIES = negate.c $(BASEOBJS) popen.o $(DEPLIBS)
+negate_DEPENDENCIES = negate.c $(BASEOBJS) $(DEPLIBS)
urlize_DEPENDENCIES = urlize.c $(BASEOBJS) popen.o $(DEPLIBS)
##############################################################################
Modified: nagiosplug/trunk/plugins/negate.c
===================================================================
--- nagiosplug/trunk/plugins/negate.c 2007-09-21 05:05:58 UTC (rev 1783)
+++ nagiosplug/trunk/plugins/negate.c 2007-09-21 23:01:28 UTC (rev 1784)
@@ -77,12 +77,12 @@
#include "common.h"
#include "utils.h"
-#include "popen.h"
+#include "utils_cmd.h"
-char *command_line;
+//char *command_line;
-int process_arguments (int, char **);
-int validate_arguments (void);
+static const char **process_arguments (int, char **);
+int validate_arguments (char **);
void print_help (void);
void print_usage (void);
@@ -93,13 +93,15 @@
{
int found = 0, result = STATE_UNKNOWN;
char *buf;
+ char **command_line;
+ output chld_out, chld_err;
+ int i;
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
- if (process_arguments (argc, argv) == ERROR)
- usage4 (_("Could not parse arguments"));
+ command_line = (char **) process_arguments (argc, argv);
/* Set signal handling and alarm */
if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR)
@@ -107,37 +109,27 @@
(void) alarm ((unsigned) timeout_interval);
- child_process = spopen (command_line);
- if (child_process == NULL)
- die (STATE_UNKNOWN, _("Could not open pipe: %s\n"), command_line);
-
- child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
-
- if (child_stderr == NULL) {
- printf (_("Could not open stderr for %s\n"), command_line);
+ /* catch when the command is quoted */
+ if(command_line[1] == NULL) {
+ result = cmd_run (command_line[0], &chld_out, &chld_err, 0);
+ } else {
+ result = cmd_run_array (command_line, &chld_out, &chld_err, 0);
}
-
- buf = malloc(MAX_INPUT_BUFFER);
- while (fgets (buf, MAX_INPUT_BUFFER - 1, child_process)) {
- found++;
- printf ("%s", buf);
+ if (chld_err.lines > 0) {
+ printf ("Error output from command:\n");
+ for (i = 0; i < chld_err.lines; i++) {
+ printf ("%s\n", chld_err.line[i]);
+ }
+ exit (STATE_WARNING);
}
- if (!found)
- die (STATE_UNKNOWN,
- _("%s problem - No data received from host\nCMD: %s\n"),\
- argv[0], command_line);
+ if (chld_out.lines == 0)
+ die (STATE_UNKNOWN, _("No data returned from command\n"));
- /* close the pipe */
- result = spclose (child_process);
+ for (i = 0; i < chld_out.lines; i++) {
+ printf ("%s\n", chld_out.line[i]);
+ }
- /* WARNING if output found on stderr */
- if (fgets (buf, MAX_INPUT_BUFFER - 1, child_stderr))
- result = max_state (result, STATE_WARNING);
-
- /* close stderr */
- (void) fclose (child_stderr);
-
if (result == STATE_OK)
exit (STATE_CRITICAL);
else if (result == STATE_CRITICAL)
@@ -167,7 +159,7 @@
/* process command-line arguments */
-int
+static const char **
process_arguments (int argc, char **argv)
{
int c;
@@ -181,8 +173,7 @@
};
while (1) {
- c = getopt_long (argc, argv, "+hVt:",
- longopts, &option);
+ c = getopt_long (argc, argv, "+hVt:", longopts, &option);
if (c == -1 || c == EOF)
break;
@@ -207,12 +198,9 @@
}
}
- asprintf (&command_line, "%s", argv[optind]);
- for (c = optind+1; c < argc; c++) {
- asprintf (&command_line, "%s %s", command_line, argv[c]);
- }
+ validate_arguments (&argv[optind]);
- return validate_arguments ();
+ return (const char **) &argv[optind];
}
@@ -230,11 +218,13 @@
int
-validate_arguments ()
+validate_arguments (char **command_line)
{
- if (command_line == NULL)
- return ERROR;
- return STATE_OK;
+ if (command_line[0] == NULL)
+ usage4 (_("Could not parse arguments"));
+
+ if (strncmp(command_line[0],"/",1) != 0 && strncmp(command_line[0],"./",2) != 0)
+ usage4 (_("Require path to command"));
}
/******************************************************************************
@@ -256,7 +246,7 @@
printf ("%s\n", _("Negates the status of a plugin (returns OK for CRITICAL, and vice-versa)."));
- printf ("\n\n");
+ printf ("\n\n");
print_usage ();
@@ -265,19 +255,20 @@
printf (_(UT_TIMEOUT), DEFAULT_TIMEOUT);
printf (" %s\n", _("[keep timeout than the plugin timeout to retain CRITICAL status]"));
- printf ("\n");
- printf ("%s\n", _("Examples:"));
- printf (" %s\n", "negate \"/usr/local/nagios/libexec/check_ping -H host\"");
- printf (" %s\n", _("Run check_ping and invert result. Must use full path to plugin"));
- printf (" %s\n", "negate \"/usr/local/nagios/libexec/check_procs -a 'vi negate.c'\"");
- printf (" %s\n", _("Use single quotes if you need to retain spaces"));
- printf (_(UT_VERBOSE));
- printf ("\n");
- printf ("%s\n", _("Notes:"));
+ printf ("\n");
+ printf ("%s\n", _("Examples:"));
+ printf (" %s\n", "negate /usr/local/nagios/libexec/check_ping -H host");
+ printf (" %s\n", _("Run check_ping and invert result. Must use full path to plugin"));
+ printf (" %s\n", "negate /usr/local/nagios/libexec/check_procs -a 'vi negate.c'");
+ printf (" %s\n", _("Use single quotes if you need to retain spaces"));
+ printf (_(UT_VERBOSE));
+ printf ("\n");
+ printf ("%s\n", _("Notes:"));
printf ("%s\n", _("This plugin is a wrapper to take the output of another plugin and invert it."));
- printf ("%s\n", _("If the wrapped plugin returns STATE_OK, the wrapper will return STATE_CRITICAL."));
- printf ("%s\n", _("If the wrapped plugin returns STATE_CRITICAL, the wrapper will return STATE_OK."));
- printf ("%s\n", _("Otherwise, the output state of the wrapped plugin is unchanged."));
+ printf ("%s\n", _("The full path of the plugin must be provided."));
+ printf ("%s\n", _("If the wrapped plugin returns STATE_OK, the wrapper will return STATE_CRITICAL."));
+ printf ("%s\n", _("If the wrapped plugin returns STATE_CRITICAL, the wrapper will return STATE_OK."));
+ printf ("%s\n", _("Otherwise, the output state of the wrapped plugin is unchanged."));
printf (_(UT_SUPPORT));
}
@@ -287,6 +278,6 @@
void
print_usage (void)
{
- printf (_("Usage:"));
+ printf (_("Usage:"));
printf ("%s [-t timeout] <definition of wrapped plugin>\n",progname);
}
Deleted: nagiosplug/trunk/plugins/t/negate.pl
===================================================================
--- nagiosplug/trunk/plugins/t/negate.pl 2007-09-21 05:05:58 UTC (rev 1783)
+++ nagiosplug/trunk/plugins/t/negate.pl 2007-09-21 23:01:28 UTC (rev 1784)
@@ -1,48 +0,0 @@
-#! /usr/bin/perl -w -I ..
-#
-# negate checks
-# Need check_dummy to work for testing
-#
-# $Id$
-#
-
-use strict;
-use Test::More;
-use NPTest;
-
-plan tests => 40;
-
-my $res;
-
-$res = NPTest->testCmd( "./negate" );
-is( $res->return_code, 3, "Not enough parameters");
-like( $res->output, "/Could not parse arguments/", "Could not parse arguments");
-
-$res = NPTest->testCmd( "./negate ./check_dummy 0 'a dummy okay'" );
-is( $res->return_code, 2, "OK changed to CRITICAL" );
-is( $res->output, "OK: a dummy okay" );
-
-$res = NPTest->testCmd( "./negate './check_dummy 0 redsweaterblog'");
-is( $res->return_code, 2, "OK => CRIT with a single quote for command to run" );
-is( $res->output, "OK: redsweaterblog" );
-
-$res = NPTest->testCmd( "./negate ./check_dummy 1 'a warn a day keeps the managers at bay'" );
-is( $res->return_code, 2, "WARN stays same" );
-
-$res = NPTest->testCmd( "./negate ./check_dummy 3 mysterious");
-is( $res->return_code, 3, "UNKNOWN stays same" );
-
-my %state = (
- ok => 0,
- warning => 1,
- critical => 2,
- unknown => 3,
- );
-foreach my $current_state (qw(ok warning critical unknown)) {
- foreach my $new_state (qw(ok warning critical unknown)) {
- $res = NPTest->testCmd( "./negate --$current_state=$new_state ./check_dummy ".$state{$current_state}." 'Fake $new_state'" );
- is( $res->return_code, $state{$new_state}, "Got fake $new_state" );
- is( $res->output, uc($current_state).": Fake $new_state" );
- }
-}
-
Added: nagiosplug/trunk/plugins/t/negate.t
===================================================================
--- nagiosplug/trunk/plugins/t/negate.t (rev 0)
+++ nagiosplug/trunk/plugins/t/negate.t 2007-09-21 23:01:28 UTC (rev 1784)
@@ -0,0 +1,79 @@
+#! /usr/bin/perl -w -I ..
+#
+# negate checks
+# Need check_dummy to work for testing
+#
+# $Id: negate.pl 1717 2007-05-24 08:53:50Z tonvoon $
+#
+
+use strict;
+use Test::More;
+use NPTest;
+
+# 47 tests if the "map changes to return codes" patch is applied
+#plan tests => 47;
+plan tests => 15;
+
+my $res;
+
+my $PWD = $ENV{PWD};
+
+$res = NPTest->testCmd( "./negate" );
+is( $res->return_code, 3, "Not enough parameters");
+like( $res->output, "/Could not parse arguments/", "Could not parse arguments");
+
+$res = NPTest->testCmd( "./negate bobthebuilder" );
+is( $res->return_code, 3, "Require full path" );
+like( $res->output, "/Require path to command/", "Appropriate error message");
+
+$res = NPTest->testCmd( "./negate $PWD/check_dummy 0 'a dummy okay'" );
+is( $res->return_code, 2, "OK changed to CRITICAL" );
+is( $res->output, "OK: a dummy okay", "Output as expected" );
+
+$res = NPTest->testCmd( "./negate '$PWD/check_dummy 0 redsweaterblog'");
+is( $res->return_code, 2, "OK => CRIT with a single quote for command to run" );
+is( $res->output, "OK: redsweaterblog", "Output as expected" );
+
+$res = NPTest->testCmd( "./negate $PWD/check_dummy 1 'a warn a day keeps the managers at bay'" );
+is( $res->return_code, 1, "WARN stays same" );
+
+$res = NPTest->testCmd( "./negate $PWD/check_dummy 3 mysterious");
+is( $res->return_code, 3, "UNKNOWN stays same" );
+
+$res = NPTest->testCmd( "./negate \"$PWD/check_dummy 0 'a dummy okay'\"" );
+is( $res->output, "OK: a dummy okay", "Checking slashed quotes - the single quotes are re-evaluated at shell" );
+
+# Output is "OK: a" because check_dummy only returns the first arg
+$res = NPTest->testCmd( "./negate $PWD/check_dummy 0 a dummy okay" );
+is( $res->output, "OK: a", "Multiple args passed as arrays" );
+
+$res = NPTest->testCmd( "./negate $PWD/check_dummy 0 'a dummy okay'" );
+is( $res->output, "OK: a dummy okay", "The quoted string is passed through to subcommand correctly" );
+
+$res = NPTest->testCmd( "./negate '$PWD/check_dummy 0' 'a dummy okay'" );
+is( $res->output, "No data returned from command", "Bad command, as expected (trying to execute './check_dummy 0')");
+
+$res = NPTest->testCmd( './negate $PWD/check_dummy 0 \'$$ a dummy okay\'' );
+is( $res->output, 'OK: $$ a dummy okay', 'Proves that $$ is not being expanded again' );
+
+
+# Remove __DATA__ to run tests with future patch
+__DATA__
+
+TODO: {
+ local $TODO = "Codes can be switched";
+ my %state = (
+ ok => 0,
+ warning => 1,
+ critical => 2,
+ unknown => 3,
+ );
+ foreach my $current_state (qw(ok warning critical unknown)) {
+ foreach my $new_state (qw(ok warning critical unknown)) {
+ $res = NPTest->testCmd( "./negate --$current_state=$new_state ./check_dummy ".$state{$current_state}." 'Fake $new_state'" );
+ is( $res->return_code, $state{$new_state}, "Got fake $new_state" );
+ is( $res->output, uc($current_state).": Fake $new_state" );
+ }
+ }
+}
+
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.