summaryrefslogtreecommitdiffstats
path: root/plugins/runcmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/runcmd.c')
-rw-r--r--plugins/runcmd.c257
1 files changed, 125 insertions, 132 deletions
diff --git a/plugins/runcmd.c b/plugins/runcmd.c
index ed49bb99..4429ceb0 100644
--- a/plugins/runcmd.c
+++ b/plugins/runcmd.c
@@ -1,63 +1,64 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring run command utilities 3 * Monitoring run command utilities
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2005-2006 Monitoring Plugins Development Team 6 * Copyright (c) 2005-2024 Monitoring Plugins Development Team
7* 7 *
8* Description : 8 * Description :
9* 9 *
10* A simple interface to executing programs from other programs, using an 10 * A simple interface to executing programs from other programs, using an
11* optimized and safe popen()-like implementation. It is considered safe 11 * optimized and safe popen()-like implementation. It is considered safe
12* in that no shell needs to be spawned and the environment passed to the 12 * in that no shell needs to be spawned and the environment passed to the
13* execve()'d program is essentially empty. 13 * execve()'d program is essentially empty.
14* 14 *
15* The code in this file is a derivative of popen.c which in turn was taken 15 * The code in this file is a derivative of popen.c which in turn was taken
16* from "Advanced Programming for the Unix Environment" by W. Richard Stevens. 16 * from "Advanced Programming for the Unix Environment" by W. Richard Stevens.
17* 17 *
18* Care has been taken to make sure the functions are async-safe. The one 18 * Care has been taken to make sure the functions are async-safe. The one
19* function which isn't is np_runcmd_init() which it doesn't make sense to 19 * function which isn't is np_runcmd_init() which it doesn't make sense to
20* call twice anyway, so the api as a whole should be considered async-safe. 20 * call twice anyway, so the api as a whole should be considered async-safe.
21* 21 *
22* 22 *
23* This program is free software: you can redistribute it and/or modify 23 * This program is free software: you can redistribute it and/or modify
24* it under the terms of the GNU General Public License as published by 24 * it under the terms of the GNU General Public License as published by
25* the Free Software Foundation, either version 3 of the License, or 25 * the Free Software Foundation, either version 3 of the License, or
26* (at your option) any later version. 26 * (at your option) any later version.
27* 27 *
28* This program is distributed in the hope that it will be useful, 28 * This program is distributed in the hope that it will be useful,
29* but WITHOUT ANY WARRANTY; without even the implied warranty of 29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31* GNU General Public License for more details. 31 * GNU General Public License for more details.
32* 32 *
33* You should have received a copy of the GNU General Public License 33 * You should have received a copy of the GNU General Public License
34* along with this program. If not, see <http://www.gnu.org/licenses/>. 34 * along with this program. If not, see <http://www.gnu.org/licenses/>.
35* 35 *
36* 36 *
37*****************************************************************************/ 37 *****************************************************************************/
38 38
39#define NAGIOSPLUG_API_C 1 39#define NAGIOSPLUG_API_C 1
40 40
41/** includes **/ 41/** includes **/
42#include "runcmd.h" 42#include "runcmd.h"
43#include "../lib/monitoringplug.h"
43#ifdef HAVE_SYS_WAIT_H 44#ifdef HAVE_SYS_WAIT_H
44# include <sys/wait.h> 45# include <sys/wait.h>
45#endif 46#endif
46 47
47#include "./utils.h" 48#include "./utils.h"
48 49
49/** macros **/ 50/** macros **/
50#ifndef WEXITSTATUS 51#ifndef WEXITSTATUS
51# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) 52# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
52#endif 53#endif
53 54
54#ifndef WIFEXITED 55#ifndef WIFEXITED
55# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) 56# define WIFEXITED(stat_val) (((stat_val)&255) == 0)
56#endif 57#endif
57 58
58/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */ 59/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */
59#if defined(SIG_IGN) && !defined(SIG_ERR) 60#if defined(SIG_IGN) && !defined(SIG_ERR)
60# define SIG_ERR ((Sigfunc *)-1) 61# define SIG_ERR ((Sigfunc *)-1)
61#endif 62#endif
62 63
63#include "../lib/maxfd.h" 64#include "../lib/maxfd.h"
@@ -72,33 +73,26 @@
72static pid_t *np_pids = NULL; 73static pid_t *np_pids = NULL;
73 74
74/** prototypes **/ 75/** prototypes **/
75static int np_runcmd_open(const char *, int *, int *) 76static int np_runcmd_open(const char *, int *, int *) __attribute__((__nonnull__(1, 2, 3)));
76 __attribute__((__nonnull__(1, 2, 3)));
77 77
78static int np_fetch_output(int, output *, int) 78static int np_fetch_output(int, output *, int) __attribute__((__nonnull__(2)));
79 __attribute__((__nonnull__(2)));
80 79
81static int np_runcmd_close(int); 80static int np_runcmd_close(int);
82 81
83/* prototype imported from utils.h */ 82/* prototype imported from utils.h */
84extern void die (int, const char *, ...) 83extern void die(int, const char *, ...) __attribute__((__noreturn__, __format__(__printf__, 2, 3)));
85 __attribute__((__noreturn__,__format__(__printf__, 2, 3)));
86
87 84
88/* this function is NOT async-safe. It is exported so multithreaded 85/* this function is NOT async-safe. It is exported so multithreaded
89 * plugins (or other apps) can call it prior to running any commands 86 * plugins (or other apps) can call it prior to running any commands
90 * through this api and thus achieve async-safeness throughout the api */ 87 * through this api and thus achieve async-safeness throughout the api */
91void np_runcmd_init(void) 88void np_runcmd_init(void) {
92{ 89 long maxfd = mp_open_max();
93 long maxfd = mp_open_max(); 90 if (!np_pids)
94 if(!np_pids) np_pids = calloc(maxfd, sizeof(pid_t)); 91 np_pids = calloc(maxfd, sizeof(pid_t));
95} 92}
96 93
97
98/* Start running a command */ 94/* Start running a command */
99static int 95static int np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr) {
100np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
101{
102 char *env[2]; 96 char *env[2];
103 char *cmd = NULL; 97 char *cmd = NULL;
104 char **argv = NULL; 98 char **argv = NULL;
@@ -112,7 +106,8 @@ np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
112 106
113 int i = 0; 107 int i = 0;
114 108
115 if(!np_pids) NP_RUNCMD_INIT; 109 if (!np_pids)
110 NP_RUNCMD_INIT;
116 111
117 env[0] = strdup("LC_ALL=C"); 112 env[0] = strdup("LC_ALL=C");
118 env[1] = NULL; 113 env[1] = NULL;
@@ -120,49 +115,50 @@ np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
120 /* make copy of command string so strtok() doesn't silently modify it */ 115 /* make copy of command string so strtok() doesn't silently modify it */
121 /* (the calling program may want to access it later) */ 116 /* (the calling program may want to access it later) */
122 cmdlen = strlen(cmdstring); 117 cmdlen = strlen(cmdstring);
123 if((cmd = malloc(cmdlen + 1)) == NULL) return -1; 118 if ((cmd = malloc(cmdlen + 1)) == NULL)
119 return -1;
124 memcpy(cmd, cmdstring, cmdlen); 120 memcpy(cmd, cmdstring, cmdlen);
125 cmd[cmdlen] = '\0'; 121 cmd[cmdlen] = '\0';
126 122
127 /* This is not a shell, so we don't handle "???" */ 123 /* This is not a shell, so we don't handle "???" */
128 if (strstr (cmdstring, "\"")) return -1; 124 if (strstr(cmdstring, "\""))
125 return -1;
129 126
130 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ 127 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */
131 if (strstr (cmdstring, " ' ") || strstr (cmdstring, "'''")) 128 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''"))
132 return -1; 129 return -1;
133 130
134 /* each arg must be whitespace-separated, so args can be a maximum 131 /* each arg must be whitespace-separated, so args can be a maximum
135 * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */ 132 * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */
136 argc = (cmdlen >> 1) + 2; 133 argc = (cmdlen >> 1) + 2;
137 argv = calloc(sizeof(char *), argc); 134 argv = calloc(argc, sizeof(char *));
138 135
139 if (argv == NULL) { 136 if (argv == NULL) {
140 printf ("%s\n", _("Could not malloc argv array in popen()")); 137 printf("%s\n", _("Could not malloc argv array in popen()"));
141 return -1; 138 return -1;
142 } 139 }
143 140
144 /* get command arguments (stupidly, but fairly quickly) */ 141 /* get command arguments (stupidly, but fairly quickly) */
145 while (cmd) { 142 while (cmd) {
146 str = cmd; 143 str = cmd;
147 str += strspn (str, " \t\r\n"); /* trim any leading whitespace */ 144 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */
148 145
149 if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */ 146 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */
150 str++; 147 str++;
151 if (!strstr (str, "'")) return -1; /* balanced? */ 148 if (!strstr(str, "'"))
152 cmd = 1 + strstr (str, "'"); 149 return -1; /* balanced? */
153 str[strcspn (str, "'")] = 0; 150 cmd = 1 + strstr(str, "'");
154 } 151 str[strcspn(str, "'")] = 0;
155 else { 152 } else {
156 if (strpbrk (str, " \t\r\n")) { 153 if (strpbrk(str, " \t\r\n")) {
157 cmd = 1 + strpbrk (str, " \t\r\n"); 154 cmd = 1 + strpbrk(str, " \t\r\n");
158 str[strcspn (str, " \t\r\n")] = 0; 155 str[strcspn(str, " \t\r\n")] = 0;
159 } 156 } else {
160 else {
161 cmd = NULL; 157 cmd = NULL;
162 } 158 }
163 } 159 }
164 160
165 if (cmd && strlen (cmd) == strspn (cmd, " \t\r\n")) 161 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n"))
166 cmd = NULL; 162 cmd = NULL;
167 163
168 argv[i++] = str; 164 argv[i++] = str;
@@ -173,33 +169,33 @@ np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
173 169
174 /* child runs exceve() and _exit. */ 170 /* child runs exceve() and _exit. */
175 if (pid == 0) { 171 if (pid == 0) {
176#ifdef RLIMIT_CORE 172#ifdef RLIMIT_CORE
177 /* the program we execve shouldn't leave core files */ 173 /* the program we execve shouldn't leave core files */
178 getrlimit (RLIMIT_CORE, &limit); 174 getrlimit(RLIMIT_CORE, &limit);
179 limit.rlim_cur = 0; 175 limit.rlim_cur = 0;
180 setrlimit (RLIMIT_CORE, &limit); 176 setrlimit(RLIMIT_CORE, &limit);
181#endif 177#endif
182 close (pfd[0]); 178 close(pfd[0]);
183 if (pfd[1] != STDOUT_FILENO) { 179 if (pfd[1] != STDOUT_FILENO) {
184 dup2 (pfd[1], STDOUT_FILENO); 180 dup2(pfd[1], STDOUT_FILENO);
185 close (pfd[1]); 181 close(pfd[1]);
186 } 182 }
187 close (pfderr[0]); 183 close(pfderr[0]);
188 if (pfderr[1] != STDERR_FILENO) { 184 if (pfderr[1] != STDERR_FILENO) {
189 dup2 (pfderr[1], STDERR_FILENO); 185 dup2(pfderr[1], STDERR_FILENO);
190 close (pfderr[1]); 186 close(pfderr[1]);
191 } 187 }
192 188
193 /* close all descriptors in np_pids[] 189 /* close all descriptors in np_pids[]
194 * This is executed in a separate address space (pure child), 190 * This is executed in a separate address space (pure child),
195 * so we don't have to worry about async safety */ 191 * so we don't have to worry about async safety */
196 long maxfd = mp_open_max(); 192 long maxfd = mp_open_max();
197 for (i = 0; i < maxfd; i++) 193 for (i = 0; i < maxfd; i++)
198 if(np_pids[i] > 0) 194 if (np_pids[i] > 0)
199 close (i); 195 close(i);
200 196
201 execve (argv[0], argv, env); 197 execve(argv[0], argv, env);
202 _exit (STATE_UNKNOWN); 198 _exit(STATE_UNKNOWN);
203 } 199 }
204 200
205 /* parent picks up execution here */ 201 /* parent picks up execution here */
@@ -213,49 +209,44 @@ np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
213 return pfd[0]; 209 return pfd[0];
214} 210}
215 211
216 212static int np_runcmd_close(int fd) {
217static int
218np_runcmd_close(int fd)
219{
220 int status; 213 int status;
221 pid_t pid; 214 pid_t pid;
222 215
223 /* make sure this fd was opened by popen() */ 216 /* make sure this fd was opened by popen() */
224 long maxfd = mp_open_max(); 217 long maxfd = mp_open_max();
225 if(fd < 0 || fd > maxfd || !np_pids || (pid = np_pids[fd]) == 0) 218 if (fd < 0 || fd > maxfd || !np_pids || (pid = np_pids[fd]) == 0)
226 return -1; 219 return -1;
227 220
228 np_pids[fd] = 0; 221 np_pids[fd] = 0;
229 if (close (fd) == -1) return -1; 222 if (close(fd) == -1)
223 return -1;
230 224
231 /* EINTR is ok (sort of), everything else is bad */ 225 /* EINTR is ok (sort of), everything else is bad */
232 while (waitpid (pid, &status, 0) < 0) 226 while (waitpid(pid, &status, 0) < 0)
233 if (errno != EINTR) return -1; 227 if (errno != EINTR)
228 return -1;
234 229
235 /* return child's termination status */ 230 /* return child's termination status */
236 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; 231 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1;
237} 232}
238 233
239 234void runcmd_timeout_alarm_handler(int signo) {
240void
241runcmd_timeout_alarm_handler (int signo)
242{
243 235
244 if (signo == SIGALRM) 236 if (signo == SIGALRM)
245 puts(_("CRITICAL - Plugin timed out while executing system call")); 237 puts(_("CRITICAL - Plugin timed out while executing system call"));
246 238
247 long maxfd = mp_open_max(); 239 long maxfd = mp_open_max();
248 if(np_pids) for(long int i = 0; i < maxfd; i++) { 240 if (np_pids)
249 if(np_pids[i] != 0) kill(np_pids[i], SIGKILL); 241 for (long int i = 0; i < maxfd; i++) {
250 } 242 if (np_pids[i] != 0)
243 kill(np_pids[i], SIGKILL);
244 }
251 245
252 exit (STATE_CRITICAL); 246 exit(STATE_CRITICAL);
253} 247}
254 248
255 249static int np_fetch_output(int fd, output *op, int flags) {
256static int
257np_fetch_output(int fd, output *op, int flags)
258{
259 size_t len = 0, i = 0, lineno = 0; 250 size_t len = 0, i = 0, lineno = 0;
260 size_t rsf = 6, ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */ 251 size_t rsf = 6, ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */
261 char *buf = NULL; 252 char *buf = NULL;
@@ -264,7 +255,7 @@ np_fetch_output(int fd, output *op, int flags)
264 255
265 op->buf = NULL; 256 op->buf = NULL;
266 op->buflen = 0; 257 op->buflen = 0;
267 while((ret = read(fd, tmpbuf, sizeof(tmpbuf))) > 0) { 258 while ((ret = read(fd, tmpbuf, sizeof(tmpbuf))) > 0) {
268 len = (size_t)ret; 259 len = (size_t)ret;
269 op->buf = realloc(op->buf, op->buflen + len + 1); 260 op->buf = realloc(op->buf, op->buflen + len + 1);
270 memcpy(op->buf + op->buflen, tmpbuf, len); 261 memcpy(op->buf + op->buflen, tmpbuf, len);
@@ -272,33 +263,33 @@ np_fetch_output(int fd, output *op, int flags)
272 i++; 263 i++;
273 } 264 }
274 265
275 if(ret < 0) { 266 if (ret < 0) {
276 printf("read() returned %d: %s\n", ret, strerror(errno)); 267 printf("read() returned %d: %s\n", ret, strerror(errno));
277 return ret; 268 return ret;
278 } 269 }
279 270
280 /* some plugins may want to keep output unbroken, and some commands 271 /* some plugins may want to keep output unbroken, and some commands
281 * will yield no output, so return here for those */ 272 * will yield no output, so return here for those */
282 if(flags & RUNCMD_NO_ARRAYS || !op->buf || !op->buflen) 273 if (flags & RUNCMD_NO_ARRAYS || !op->buf || !op->buflen)
283 return op->buflen; 274 return op->buflen;
284 275
285 /* and some may want both */ 276 /* and some may want both */
286 if(flags & RUNCMD_NO_ASSOC) { 277 if (flags & RUNCMD_NO_ASSOC) {
287 buf = malloc(op->buflen); 278 buf = malloc(op->buflen);
288 memcpy(buf, op->buf, op->buflen); 279 memcpy(buf, op->buf, op->buflen);
289 } 280 } else
290 else buf = op->buf; 281 buf = op->buf;
291 282
292 op->line = NULL; 283 op->line = NULL;
293 op->lens = NULL; 284 op->lens = NULL;
294 i = 0; 285 i = 0;
295 while(i < op->buflen) { 286 while (i < op->buflen) {
296 /* make sure we have enough memory */ 287 /* make sure we have enough memory */
297 if(lineno >= ary_size) { 288 if (lineno >= ary_size) {
298 /* ary_size must never be zero */ 289 /* ary_size must never be zero */
299 do { 290 do {
300 ary_size = op->buflen >> --rsf; 291 ary_size = op->buflen >> --rsf;
301 } while(!ary_size); 292 } while (!ary_size);
302 293
303 op->line = realloc(op->line, ary_size * sizeof(char *)); 294 op->line = realloc(op->line, ary_size * sizeof(char *));
304 op->lens = realloc(op->lens, ary_size * sizeof(size_t)); 295 op->lens = realloc(op->lens, ary_size * sizeof(size_t));
@@ -308,7 +299,8 @@ np_fetch_output(int fd, output *op, int flags)
308 op->line[lineno] = &buf[i]; 299 op->line[lineno] = &buf[i];
309 300
310 /* hop to next newline or end of buffer */ 301 /* hop to next newline or end of buffer */
311 while(buf[i] != '\n' && i < op->buflen) i++; 302 while (buf[i] != '\n' && i < op->buflen)
303 i++;
312 buf[i] = '\0'; 304 buf[i] = '\0';
313 305
314 /* calculate the string length using pointer difference */ 306 /* calculate the string length using pointer difference */
@@ -321,21 +313,22 @@ np_fetch_output(int fd, output *op, int flags)
321 return lineno; 313 return lineno;
322} 314}
323 315
324 316int np_runcmd(const char *cmd, output *out, output *err, int flags) {
325int
326np_runcmd(const char *cmd, output *out, output *err, int flags)
327{
328 int fd, pfd_out[2], pfd_err[2]; 317 int fd, pfd_out[2], pfd_err[2];
329 318
330 /* initialize the structs */ 319 /* initialize the structs */
331 if(out) memset(out, 0, sizeof(output)); 320 if (out)
332 if(err) memset(err, 0, sizeof(output)); 321 memset(out, 0, sizeof(output));
333 322 if (err)
334 if((fd = np_runcmd_open(cmd, pfd_out, pfd_err)) == -1) 323 memset(err, 0, sizeof(output));
335 die (STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd); 324
336 325 if ((fd = np_runcmd_open(cmd, pfd_out, pfd_err)) == -1)
337 if(out) out->lines = np_fetch_output(pfd_out[0], out, flags); 326 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd);
338 if(err) err->lines = np_fetch_output(pfd_err[0], err, flags); 327
328 if (out)
329 out->lines = np_fetch_output(pfd_out[0], out, flags);
330 if (err)
331 err->lines = np_fetch_output(pfd_err[0], err, flags);
339 332
340 return np_runcmd_close(fd); 333 return np_runcmd_close(fd);
341} 334}