summaryrefslogtreecommitdiffstats
path: root/plugins/runcmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/runcmd.c')
-rw-r--r--plugins/runcmd.c283
1 files changed, 148 insertions, 135 deletions
diff --git a/plugins/runcmd.c b/plugins/runcmd.c
index ed49bb99..be6691d2 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,27 @@
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));
92 }
95} 93}
96 94
97
98/* Start running a command */ 95/* Start running a command */
99static int 96static 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]; 97 char *env[2];
103 char *cmd = NULL; 98 char *cmd = NULL;
104 char **argv = NULL; 99 char **argv = NULL;
@@ -112,7 +107,9 @@ np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
112 107
113 int i = 0; 108 int i = 0;
114 109
115 if(!np_pids) NP_RUNCMD_INIT; 110 if (!np_pids) {
111 NP_RUNCMD_INIT;
112 }
116 113
117 env[0] = strdup("LC_ALL=C"); 114 env[0] = strdup("LC_ALL=C");
118 env[1] = NULL; 115 env[1] = NULL;
@@ -120,86 +117,95 @@ np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
120 /* make copy of command string so strtok() doesn't silently modify it */ 117 /* make copy of command string so strtok() doesn't silently modify it */
121 /* (the calling program may want to access it later) */ 118 /* (the calling program may want to access it later) */
122 cmdlen = strlen(cmdstring); 119 cmdlen = strlen(cmdstring);
123 if((cmd = malloc(cmdlen + 1)) == NULL) return -1; 120 if ((cmd = malloc(cmdlen + 1)) == NULL) {
121 return -1;
122 }
124 memcpy(cmd, cmdstring, cmdlen); 123 memcpy(cmd, cmdstring, cmdlen);
125 cmd[cmdlen] = '\0'; 124 cmd[cmdlen] = '\0';
126 125
127 /* This is not a shell, so we don't handle "???" */ 126 /* This is not a shell, so we don't handle "???" */
128 if (strstr (cmdstring, "\"")) return -1; 127 if (strstr(cmdstring, "\"")) {
128 return -1;
129 }
129 130
130 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ 131 /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */
131 if (strstr (cmdstring, " ' ") || strstr (cmdstring, "'''")) 132 if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) {
132 return -1; 133 return -1;
134 }
133 135
134 /* each arg must be whitespace-separated, so args can be a maximum 136 /* 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 */ 137 * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */
136 argc = (cmdlen >> 1) + 2; 138 argc = (cmdlen >> 1) + 2;
137 argv = calloc(sizeof(char *), argc); 139 argv = calloc(argc, sizeof(char *));
138 140
139 if (argv == NULL) { 141 if (argv == NULL) {
140 printf ("%s\n", _("Could not malloc argv array in popen()")); 142 printf("%s\n", _("Could not malloc argv array in popen()"));
141 return -1; 143 return -1;
142 } 144 }
143 145
144 /* get command arguments (stupidly, but fairly quickly) */ 146 /* get command arguments (stupidly, but fairly quickly) */
145 while (cmd) { 147 while (cmd) {
146 str = cmd; 148 str = cmd;
147 str += strspn (str, " \t\r\n"); /* trim any leading whitespace */ 149 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */
148 150
149 if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */ 151 if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */
150 str++; 152 str++;
151 if (!strstr (str, "'")) return -1; /* balanced? */ 153 if (!strstr(str, "'")) {
152 cmd = 1 + strstr (str, "'"); 154 return -1; /* balanced? */
153 str[strcspn (str, "'")] = 0;
154 }
155 else {
156 if (strpbrk (str, " \t\r\n")) {
157 cmd = 1 + strpbrk (str, " \t\r\n");
158 str[strcspn (str, " \t\r\n")] = 0;
159 } 155 }
160 else { 156 cmd = 1 + strstr(str, "'");
157 str[strcspn(str, "'")] = 0;
158 } else {
159 if (strpbrk(str, " \t\r\n")) {
160 cmd = 1 + strpbrk(str, " \t\r\n");
161 str[strcspn(str, " \t\r\n")] = 0;
162 } else {
161 cmd = NULL; 163 cmd = NULL;
162 } 164 }
163 } 165 }
164 166
165 if (cmd && strlen (cmd) == strspn (cmd, " \t\r\n")) 167 if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) {
166 cmd = NULL; 168 cmd = NULL;
169 }
167 170
168 argv[i++] = str; 171 argv[i++] = str;
169 } 172 }
170 173
171 if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) 174 if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) {
172 return -1; /* errno set by the failing function */ 175 return -1; /* errno set by the failing function */
176 }
173 177
174 /* child runs exceve() and _exit. */ 178 /* child runs exceve() and _exit. */
175 if (pid == 0) { 179 if (pid == 0) {
176#ifdef RLIMIT_CORE 180#ifdef RLIMIT_CORE
177 /* the program we execve shouldn't leave core files */ 181 /* the program we execve shouldn't leave core files */
178 getrlimit (RLIMIT_CORE, &limit); 182 getrlimit(RLIMIT_CORE, &limit);
179 limit.rlim_cur = 0; 183 limit.rlim_cur = 0;
180 setrlimit (RLIMIT_CORE, &limit); 184 setrlimit(RLIMIT_CORE, &limit);
181#endif 185#endif
182 close (pfd[0]); 186 close(pfd[0]);
183 if (pfd[1] != STDOUT_FILENO) { 187 if (pfd[1] != STDOUT_FILENO) {
184 dup2 (pfd[1], STDOUT_FILENO); 188 dup2(pfd[1], STDOUT_FILENO);
185 close (pfd[1]); 189 close(pfd[1]);
186 } 190 }
187 close (pfderr[0]); 191 close(pfderr[0]);
188 if (pfderr[1] != STDERR_FILENO) { 192 if (pfderr[1] != STDERR_FILENO) {
189 dup2 (pfderr[1], STDERR_FILENO); 193 dup2(pfderr[1], STDERR_FILENO);
190 close (pfderr[1]); 194 close(pfderr[1]);
191 } 195 }
192 196
193 /* close all descriptors in np_pids[] 197 /* close all descriptors in np_pids[]
194 * This is executed in a separate address space (pure child), 198 * This is executed in a separate address space (pure child),
195 * so we don't have to worry about async safety */ 199 * so we don't have to worry about async safety */
196 long maxfd = mp_open_max(); 200 long maxfd = mp_open_max();
197 for (i = 0; i < maxfd; i++) 201 for (i = 0; i < maxfd; i++) {
198 if(np_pids[i] > 0) 202 if (np_pids[i] > 0) {
199 close (i); 203 close(i);
204 }
205 }
200 206
201 execve (argv[0], argv, env); 207 execve(argv[0], argv, env);
202 _exit (STATE_UNKNOWN); 208 _exit(STATE_UNKNOWN);
203 } 209 }
204 210
205 /* parent picks up execution here */ 211 /* parent picks up execution here */
@@ -213,49 +219,51 @@ np_runcmd_open(const char *cmdstring, int *pfd, int *pfderr)
213 return pfd[0]; 219 return pfd[0];
214} 220}
215 221
216 222static int np_runcmd_close(int fd) {
217static int
218np_runcmd_close(int fd)
219{
220 int status; 223 int status;
221 pid_t pid; 224 pid_t pid;
222 225
223 /* make sure this fd was opened by popen() */ 226 /* make sure this fd was opened by popen() */
224 long maxfd = mp_open_max(); 227 long maxfd = mp_open_max();
225 if(fd < 0 || fd > maxfd || !np_pids || (pid = np_pids[fd]) == 0) 228 if (fd < 0 || fd > maxfd || !np_pids || (pid = np_pids[fd]) == 0) {
226 return -1; 229 return -1;
230 }
227 231
228 np_pids[fd] = 0; 232 np_pids[fd] = 0;
229 if (close (fd) == -1) return -1; 233 if (close(fd) == -1) {
234 return -1;
235 }
230 236
231 /* EINTR is ok (sort of), everything else is bad */ 237 /* EINTR is ok (sort of), everything else is bad */
232 while (waitpid (pid, &status, 0) < 0) 238 while (waitpid(pid, &status, 0) < 0) {
233 if (errno != EINTR) return -1; 239 if (errno != EINTR) {
240 return -1;
241 }
242 }
234 243
235 /* return child's termination status */ 244 /* return child's termination status */
236 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; 245 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1;
237} 246}
238 247
248void runcmd_timeout_alarm_handler(int signo) {
239 249
240void 250 if (signo == SIGALRM) {
241runcmd_timeout_alarm_handler (int signo)
242{
243
244 if (signo == SIGALRM)
245 puts(_("CRITICAL - Plugin timed out while executing system call")); 251 puts(_("CRITICAL - Plugin timed out while executing system call"));
252 }
246 253
247 long maxfd = mp_open_max(); 254 long maxfd = mp_open_max();
248 if(np_pids) for(long int i = 0; i < maxfd; i++) { 255 if (np_pids) {
249 if(np_pids[i] != 0) kill(np_pids[i], SIGKILL); 256 for (long int i = 0; i < maxfd; i++) {
257 if (np_pids[i] != 0) {
258 kill(np_pids[i], SIGKILL);
259 }
260 }
250 } 261 }
251 262
252 exit (STATE_CRITICAL); 263 exit(STATE_CRITICAL);
253} 264}
254 265
255 266static 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; 267 size_t len = 0, i = 0, lineno = 0;
260 size_t rsf = 6, ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */ 268 size_t rsf = 6, ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */
261 char *buf = NULL; 269 char *buf = NULL;
@@ -264,7 +272,7 @@ np_fetch_output(int fd, output *op, int flags)
264 272
265 op->buf = NULL; 273 op->buf = NULL;
266 op->buflen = 0; 274 op->buflen = 0;
267 while((ret = read(fd, tmpbuf, sizeof(tmpbuf))) > 0) { 275 while ((ret = read(fd, tmpbuf, sizeof(tmpbuf))) > 0) {
268 len = (size_t)ret; 276 len = (size_t)ret;
269 op->buf = realloc(op->buf, op->buflen + len + 1); 277 op->buf = realloc(op->buf, op->buflen + len + 1);
270 memcpy(op->buf + op->buflen, tmpbuf, len); 278 memcpy(op->buf + op->buflen, tmpbuf, len);
@@ -272,48 +280,47 @@ np_fetch_output(int fd, output *op, int flags)
272 i++; 280 i++;
273 } 281 }
274 282
275 if(ret < 0) { 283 if (ret < 0) {
276 printf("read() returned %d: %s\n", ret, strerror(errno)); 284 printf("read() returned %d: %s\n", ret, strerror(errno));
277 return ret; 285 return ret;
278 } 286 }
279 287
280 /* some plugins may want to keep output unbroken, and some commands 288 /* some plugins may want to keep output unbroken, and some commands
281 * will yield no output, so return here for those */ 289 * will yield no output, so return here for those */
282 if(flags & RUNCMD_NO_ARRAYS || !op->buf || !op->buflen) 290 if (flags & RUNCMD_NO_ARRAYS || !op->buf || !op->buflen) {
283 return op->buflen; 291 return op->buflen;
292 }
284 293
285 /* and some may want both */ 294 /* and some may want both */
286 if(flags & RUNCMD_NO_ASSOC) { 295 if (flags & RUNCMD_NO_ASSOC) {
287 buf = malloc(op->buflen); 296 buf = malloc(op->buflen);
288 memcpy(buf, op->buf, op->buflen); 297 memcpy(buf, op->buf, op->buflen);
298 } else {
299 buf = op->buf;
289 } 300 }
290 else buf = op->buf;
291 301
292 op->line = NULL; 302 op->line = NULL;
293 op->lens = NULL;
294 i = 0; 303 i = 0;
295 while(i < op->buflen) { 304 while (i < op->buflen) {
296 /* make sure we have enough memory */ 305 /* make sure we have enough memory */
297 if(lineno >= ary_size) { 306 if (lineno >= ary_size) {
298 /* ary_size must never be zero */ 307 /* ary_size must never be zero */
299 do { 308 do {
300 ary_size = op->buflen >> --rsf; 309 ary_size = op->buflen >> --rsf;
301 } while(!ary_size); 310 } while (!ary_size);
302 311
303 op->line = realloc(op->line, ary_size * sizeof(char *)); 312 op->line = realloc(op->line, ary_size * sizeof(char *));
304 op->lens = realloc(op->lens, ary_size * sizeof(size_t));
305 } 313 }
306 314
307 /* set the pointer to the string */ 315 /* set the pointer to the string */
308 op->line[lineno] = &buf[i]; 316 op->line[lineno] = &buf[i];
309 317
310 /* hop to next newline or end of buffer */ 318 /* hop to next newline or end of buffer */
311 while(buf[i] != '\n' && i < op->buflen) i++; 319 while (buf[i] != '\n' && i < op->buflen) {
320 i++;
321 }
312 buf[i] = '\0'; 322 buf[i] = '\0';
313 323
314 /* calculate the string length using pointer difference */
315 op->lens[lineno] = (size_t)&buf[i] - (size_t)op->line[lineno];
316
317 lineno++; 324 lineno++;
318 i++; 325 i++;
319 } 326 }
@@ -321,21 +328,27 @@ np_fetch_output(int fd, output *op, int flags)
321 return lineno; 328 return lineno;
322} 329}
323 330
324 331int 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]; 332 int fd, pfd_out[2], pfd_err[2];
329 333
330 /* initialize the structs */ 334 /* initialize the structs */
331 if(out) memset(out, 0, sizeof(output)); 335 if (out) {
332 if(err) memset(err, 0, sizeof(output)); 336 memset(out, 0, sizeof(output));
337 }
338 if (err) {
339 memset(err, 0, sizeof(output));
340 }
333 341
334 if((fd = np_runcmd_open(cmd, pfd_out, pfd_err)) == -1) 342 if ((fd = np_runcmd_open(cmd, pfd_out, pfd_err)) == -1) {
335 die (STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd); 343 die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd);
344 }
336 345
337 if(out) out->lines = np_fetch_output(pfd_out[0], out, flags); 346 if (out) {
338 if(err) err->lines = np_fetch_output(pfd_err[0], err, flags); 347 out->lines = np_fetch_output(pfd_out[0], out, flags);
348 }
349 if (err) {
350 err->lines = np_fetch_output(pfd_err[0], err, flags);
351 }
339 352
340 return np_runcmd_close(fd); 353 return np_runcmd_close(fd);
341} 354}