1 /*****************************************************************************
3 * Monitoring Plugins popen
6 * Copyright (c) 2005-2007 Monitoring Plugins Development Team
10 * A safe alternative to popen
12 * Provides spopen and spclose
14 * FILE * spopen(const char *);
15 * int spclose(FILE *);
17 * Code taken with little modification from "Advanced Programming for the Unix
18 * Environment" by W. Richard Stevens
20 * This is considered safe in that no shell is spawned, and the environment
21 * and path passed to the exec'd program are essentially empty. (popen create
22 * a shell and passes the environment to it).
25 * This program is free software: you can redistribute it and/or modify
26 * it under the terms of the GNU General Public License as published by
27 * the Free Software Foundation, either version 3 of the License, or
28 * (at your option) any later version.
30 * This program is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
35 * You should have received a copy of the GNU General Public License
36 * along with this program. If not, see <http://www.gnu.org/licenses/>.
39 *****************************************************************************/
43 #include "../lib/maxfd.h"
45 /* extern so plugin has pid to kill exec'd process on timeouts */
46 extern pid_t
*childpid
;
47 extern int *child_stderr_array
;
48 extern FILE *child_process
;
50 FILE *spopen (const char *);
52 #ifdef REDHAT_SPOPEN_ERROR
53 void popen_sigchld_handler (int);
55 void popen_timeout_alarm_handler (int);
57 #include <stdarg.h> /* ANSI C header file */
61 #include <sys/resource.h>
63 #ifdef HAVE_SYS_WAIT_H
68 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
72 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
75 /* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */
76 #if defined(SIG_IGN) && !defined(SIG_ERR)
77 #define SIG_ERR ((Sigfunc *)-1)
81 char *pname
= NULL
; /* caller can set this from argv[0] */
83 #ifdef REDHAT_SPOPEN_ERROR
84 static volatile int childtermd
= 0;
88 spopen (const char *cmdstring
)
96 int i
= 0, pfd
[2], pfderr
[2];
100 /* do not leave core files */
102 getrlimit (RLIMIT_CORE
, &limit
);
104 setrlimit (RLIMIT_CORE
, &limit
);
107 env
[0] = strdup("LC_ALL=C");
110 /* if no command was passed, return with no error */
111 if (cmdstring
== NULL
)
114 /* make copy of command string so strtok() doesn't silently modify it */
115 /* (the calling program may want to access it later) */
116 cmd
= malloc (strlen (cmdstring
) + 1);
119 strcpy (cmd
, cmdstring
);
121 /* This is not a shell, so we don't handle "???" */
122 if (strstr (cmdstring
, "\""))
125 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */
126 if (strstr (cmdstring
, " ' ") || strstr (cmdstring
, "'''"))
129 /* there cannot be more args than characters */
130 argc
= strlen (cmdstring
) + 1; /* add 1 for NULL termination */
131 argv
= malloc (sizeof(char*)*argc
);
134 printf ("%s\n", _("Could not malloc argv array in popen()"));
138 /* loop to get arguments to command */
141 str
+= strspn (str
, " \t\r\n"); /* trim any leading whitespace */
144 printf ("%s\n",_("CRITICAL - You need more args!!!"));
148 if (strstr (str
, "'") == str
) { /* handle SIMPLE quoted strings */
150 if (!strstr (str
, "'"))
151 return NULL
; /* balanced? */
152 cmd
= 1 + strstr (str
, "'");
153 str
[strcspn (str
, "'")] = 0;
155 else if (strcspn(str
,"'") < strcspn (str
, " \t\r\n")) {
156 /* handle --option='foo bar' strings */
157 tmp
= str
+ strcspn(str
, "'") + 1;
158 if (!strstr (tmp
, "'"))
159 return NULL
; /* balanced? */
160 tmp
+= strcspn(tmp
,"'") + 1;
164 if (strpbrk (str
, " \t\r\n")) {
165 cmd
= 1 + strpbrk (str
, " \t\r\n");
166 str
[strcspn (str
, " \t\r\n")] = 0;
173 if (cmd
&& strlen (cmd
) == strspn (cmd
, " \t\r\n"))
181 long maxfd
= mp_open_max();
183 if (childpid
== NULL
) { /* first time through */
184 if ((childpid
= calloc ((size_t)maxfd
, sizeof (pid_t
))) == NULL
)
188 if (child_stderr_array
== NULL
) { /* first time through */
189 if ((child_stderr_array
= calloc ((size_t)maxfd
, sizeof (int))) == NULL
)
194 return (NULL
); /* errno set by pipe() */
196 if (pipe (pfderr
) < 0)
197 return (NULL
); /* errno set by pipe() */
199 #ifdef REDHAT_SPOPEN_ERROR
200 if (signal (SIGCHLD
, popen_sigchld_handler
) == SIG_ERR
) {
201 usage4 (_("Cannot catch SIGCHLD"));
205 if ((pid
= fork ()) < 0)
206 return (NULL
); /* errno set by fork() */
207 else if (pid
== 0) { /* child */
209 if (pfd
[1] != STDOUT_FILENO
) {
210 dup2 (pfd
[1], STDOUT_FILENO
);
214 if (pfderr
[1] != STDERR_FILENO
) {
215 dup2 (pfderr
[1], STDERR_FILENO
);
218 /* close all descriptors in childpid[] */
219 for (i
= 0; i
< maxfd
; i
++)
223 execve (argv
[0], argv
, env
);
227 close (pfd
[1]); /* parent */
228 if ((child_process
= fdopen (pfd
[0], "r")) == NULL
)
232 childpid
[fileno (child_process
)] = pid
; /* remember child pid for this fd */
233 child_stderr_array
[fileno (child_process
)] = pfderr
[0]; /* remember STDERR */
234 return (child_process
);
243 if (childpid
== NULL
)
244 return (1); /* popen() has never been called */
247 if ((pid
= childpid
[fd
]) == 0)
248 return (1); /* fp wasn't opened by popen() */
251 if (fclose (fp
) == EOF
)
254 #ifdef REDHAT_SPOPEN_ERROR
255 while (!childtermd
); /* wait until SIGCHLD */
258 while (waitpid (pid
, &status
, 0) < 0)
260 return (1); /* error other than EINTR from waitpid() */
262 if (WIFEXITED (status
))
263 return (WEXITSTATUS (status
)); /* return child's termination status */
268 #ifdef REDHAT_SPOPEN_ERROR
270 popen_sigchld_handler (int signo
)
272 if (signo
== SIGCHLD
)
278 popen_timeout_alarm_handler (int signo
)
281 if (signo
== SIGALRM
) {
282 if (child_process
!= NULL
) {
283 fh
=fileno (child_process
);
285 kill (childpid
[fh
], SIGKILL
);
287 printf (_("CRITICAL - Plugin timed out after %d seconds\n"),
290 printf ("%s\n", _("CRITICAL - popen timeout received, but no child process"));
292 exit (STATE_CRITICAL
);