Code

change exit status to be POSIX compliant
[nagiosplug.git] / plugins / popen.c
1 /******************************************************************************
2  * popen.c
3  *
4  * A safe alternative to popen
5  * 
6  * Provides spopen and spclose
8 FILE * spopen(const char *);
9 int spclose(FILE *);
11  *
12  * Code taken with liitle modification from "Advanced Programming for the Unix
13  * Environment" by W. Richard Stevens
14  *
15  * This is considered safe in that no shell is spawned, and the environment and
16  * path passed to the exec'd program are esstially empty. (popen create a shell
17  * and passes the environment to it).
18  *
19  ******************************************************************************/
21 #include <config.h>
22 #include <common.h>
24 /* extern so plugin has pid to kill exec'd process on timeouts */
25 extern int timeout_interval;
26 extern pid_t *childpid;
27 extern int *child_stderr_array;
28 extern FILE *child_process;
30 FILE *spopen (const char *);
31 int spclose (FILE *);
32 RETSIGTYPE popen_timeout_alarm_handler (int);
34 #include <stdarg.h>                                                     /* ANSI C header file */
35 #include <fcntl.h>
37 #include <limits.h>
38 #include <sys/resource.h>
40 #ifdef HAVE_SYS_WAIT_H
41 #include <sys/wait.h>
42 #endif
44 #ifndef WEXITSTATUS
45 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
46 #endif
48 #ifndef WIFEXITED
49 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
50 #endif
52 /* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */
53 #if defined(SIG_IGN) && !defined(SIG_ERR)
54 #define SIG_ERR ((Sigfunc *)-1)
55 #endif
57 #define min(a,b)        ((a) < (b) ? (a) : (b))
58 #define max(a,b)        ((a) > (b) ? (a) : (b))
59 int open_max (void);                                            /* {Prog openmax} */
60 void err_sys (const char *, ...);
61 char *rtrim (char *, const char *);
63 /*int *childerr = NULL;*//* ptr to array allocated at run-time */
64 /*extern pid_t *childpid = NULL; *//* ptr to array allocated at run-time */
65 static int maxfd;                                                               /* from our open_max(), {Prog openmax} */
67 FILE *
68 spopen (const char *cmdstring)
69 {
70         char *environ[] = { NULL };
71         char *cmd = NULL;
72         char **argv = NULL;
73         char *str;
74         int argc;
76         int i = 0, pfd[2], pfderr[2];
77         pid_t pid;
79 #ifdef  RLIMIT_CORE
80         /* do not leave core files */
81         struct rlimit limit;
82         getrlimit (RLIMIT_CORE, &limit);
83         limit.rlim_cur = 0;
84         setrlimit (RLIMIT_CORE, &limit);
85 #endif
87         /* if no command was passed, return with no error */
88         if (cmdstring == NULL)
89                 return (NULL);
91         /* make copy of command string so strtok() doesn't silently modify it */
92         /* (the calling program may want to access it later) */
93         cmd = malloc (strlen (cmdstring) + 1);
94         if (cmd == NULL)
95                 return NULL;
96         strcpy (cmd, cmdstring);
98         /* This is not a shell, so we don't handle "???" */
99         if (strstr (cmdstring, "\""))
100                 return NULL;
102         /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */
103         if (strstr (cmdstring, " ' ") || strstr (cmdstring, "'''"))
104                 return NULL;
106         /* there cannot be more args than characters */
107         argc = strlen (cmdstring) + 1;  /* add 1 for NULL termination */
108         argv = malloc (sizeof(char*)*argc);
109         if (argv == NULL) {
110                 printf ("Could not malloc argv array in popen()\n");
111                 return NULL;
112         }
114         /* loop to get arguments to command */
115         while (cmd) {
116                 str = cmd;
117                 str += strspn (str, " \t\r\n"); /* trim any leading whitespace */
119                 if (i >= argc - 2) {
120                         printf ("You've got a big problem buddy! You need more args!!!\n");
121                         return (NULL);
122                 }
124                 if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */
125                         str++;
126                         if (!strstr (str, "'"))
127                                 return NULL;                                            /* balanced? */
128                         cmd = 1 + strstr (str, "'");
129                         str[strcspn (str, "'")] = 0;
130                 }
131                 else {
132                         if (strpbrk (str, " \t\r\n")) {
133                                 cmd = 1 + strpbrk (str, " \t\r\n");
134                                 str[strcspn (str, " \t\r\n")] = 0;
135                         }
136                         else {
137                                 cmd = NULL;
138                         }
139                 }
141                 if (cmd && strlen (cmd) == strspn (cmd, " \t\r\n"))
142                         cmd = NULL;
144                 argv[i++] = str;
146         }
147         argv[i] = NULL;
149         if (childpid == NULL) {                         /* first time through */
150                 maxfd = open_max ();                            /* allocate zeroed out array for child pids */
151                 if ((childpid = calloc (maxfd, sizeof (pid_t))) == NULL)
152                         return (NULL);
153         }
155         if (child_stderr_array == NULL) {       /* first time through */
156                 maxfd = open_max ();                            /* allocate zeroed out array for child pids */
157                 if ((child_stderr_array = calloc (maxfd, sizeof (int))) == NULL)
158                         return (NULL);
159         }
161         if (pipe (pfd) < 0)
162                 return (NULL);                                                  /* errno set by pipe() */
164         if (pipe (pfderr) < 0)
165                 return (NULL);                                                  /* errno set by pipe() */
167         if ((pid = fork ()) < 0)
168                 return (NULL);                                                  /* errno set by fork() */
169         else if (pid == 0) {                                    /* child */
170                 close (pfd[0]);
171                 if (pfd[1] != STDOUT_FILENO) {
172                         dup2 (pfd[1], STDOUT_FILENO);
173                         close (pfd[1]);
174                 }
175                 close (pfderr[0]);
176                 if (pfderr[1] != STDERR_FILENO) {
177                         dup2 (pfderr[1], STDERR_FILENO);
178                         close (pfderr[1]);
179                 }
180                 /* close all descriptors in childpid[] */
181                 for (i = 0; i < maxfd; i++)
182                         if (childpid[i] > 0)
183                                 close (i);
185                 execve (argv[0], argv, environ);
186                 _exit (0);
187         }
189         close (pfd[1]);                                                         /* parent */
190         if ((child_process = fdopen (pfd[0], "r")) == NULL)
191                 return (NULL);
192         close (pfderr[1]);
194         childpid[fileno (child_process)] = pid; /* remember child pid for this fd */
195         child_stderr_array[fileno (child_process)] = pfderr[0]; /* remember STDERR */
196         return (child_process);
199 int
200 spclose (FILE * fp)
202         int fd, stat;
203         pid_t pid;
205         if (childpid == NULL)
206                 return (1);                                                             /* popen() has never been called */
208         fd = fileno (fp);
209         if ((pid = childpid[fd]) == 0)
210                 return (1);                                                             /* fp wasn't opened by popen() */
212         childpid[fd] = 0;
213         if (fclose (fp) == EOF)
214                 return (1);
216         while (waitpid (pid, &stat, 0) < 0)
217                 if (errno != EINTR)
218                         return (1);                                                     /* error other than EINTR from waitpid() */
220         if (WIFEXITED (stat))
221                 return (WEXITSTATUS (stat));    /* return child's termination status */
223         return (1);
226 #ifdef  OPEN_MAX
227 static int openmax = OPEN_MAX;
228 #else
229 static int openmax = 0;
230 #endif
232 #define OPEN_MAX_GUESS  256                     /* if OPEN_MAX is indeterminate */
233                                 /* no guarantee this is adequate */
235 void
236 popen_timeout_alarm_handler (int signo)
238         if (signo == SIGALRM) {
239                 kill (childpid[fileno (child_process)], SIGKILL);
240                 printf ("CRITICAL - Plugin timed out after %d seconds\n",
241                                                 timeout_interval);
242                 exit (STATE_CRITICAL);
243         }
246 int
247 open_max (void)
249         if (openmax == 0) {                                             /* first time through */
250                 errno = 0;
251                 if ((openmax = sysconf (_SC_OPEN_MAX)) < 0) {
252                         if (errno == 0)
253                                 openmax = OPEN_MAX_GUESS;       /* it's indeterminate */
254                         else
255                                 err_sys ("sysconf error for _SC_OPEN_MAX");
256                 }
257         }
258         return (openmax);
262 static void err_doit (int, const char *, va_list);
264 char *pname = NULL;                                                     /* caller can set this from argv[0] */
266 /* Fatal error related to a system call.
267  * Print a message and terminate. */
269 void
270 err_sys (const char *fmt, ...)
272         va_list ap;
274         va_start (ap, fmt);
275         err_doit (1, fmt, ap);
276         va_end (ap);
277         exit (1);
280 /* Print a message and return to caller.
281  * Caller specifies "errnoflag". */
283 #define MAXLINE 2048
284 static void
285 err_doit (int errnoflag, const char *fmt, va_list ap)
287         int errno_save;
288         char buf[MAXLINE];
290         errno_save = errno;                                             /* value caller might want printed */
291         vsprintf (buf, fmt, ap);
292         if (errnoflag)
293                 sprintf (buf + strlen (buf), ": %s", strerror (errno_save));
294         strcat (buf, "\n");
295         fflush (stdout);                                                        /* in case stdout and stderr are the same */
296         fputs (buf, stderr);
297         fflush (NULL);                                                          /* flushes all stdio output streams */
298         return;
301 char *
302 rtrim (char *str, const char *tok)
304         int i = 0;
305         int j = sizeof (str);
307         while (str != NULL && i < j) {
308                 if (*(str + i) == *tok) {
309                         sprintf (str + i, "%s", "\0");
310                         return str;
311                 }
312                 i++;
313         }
314         return str;