diff options
Diffstat (limited to 'plugins/check_by_ssh.c')
-rw-r--r-- | plugins/check_by_ssh.c | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c new file mode 100644 index 00000000..a81b333f --- /dev/null +++ b/plugins/check_by_ssh.c | |||
@@ -0,0 +1,412 @@ | |||
1 | /****************************************************************************** | ||
2 | * | ||
3 | * This file is part of the Nagios Plugins. | ||
4 | * | ||
5 | * Copyright (c) 1999, 2000, 2001 Karl DeBisschop <karl@debisschop.net> | ||
6 | * | ||
7 | * The Nagios Plugins are free software; you can redistribute them | ||
8 | * and/or modify them under the terms of the GNU General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | * | ||
21 | * $Id$ | ||
22 | * | ||
23 | *****************************************************************************/ | ||
24 | |||
25 | #define PROGRAM check_by_ssh | ||
26 | #define DESCRIPTION "Run checks on a remote system using ssh, wrapping the proper timeout around the ssh invocation." | ||
27 | #define AUTHOR "Karl DeBisschop" | ||
28 | #define EMAIL "karl@debisschop.net" | ||
29 | #define COPYRIGHTDATE "1999, 2000, 2001" | ||
30 | |||
31 | #include "config.h" | ||
32 | #include "common.h" | ||
33 | #include "popen.h" | ||
34 | #include "utils.h" | ||
35 | #include <time.h> | ||
36 | |||
37 | #define PROGNAME "check_by_ssh" | ||
38 | |||
39 | int process_arguments (int, char **); | ||
40 | int call_getopt (int, char **); | ||
41 | int validate_arguments (void); | ||
42 | void print_help (char *command_name); | ||
43 | void print_usage (void); | ||
44 | |||
45 | |||
46 | int commands; | ||
47 | char *remotecmd = NULL; | ||
48 | char *comm = NULL; | ||
49 | char *hostname = NULL; | ||
50 | char *outputfile = NULL; | ||
51 | char *host_shortname = NULL; | ||
52 | char *servicelist = NULL; | ||
53 | int passive = FALSE; | ||
54 | int verbose = FALSE; | ||
55 | |||
56 | |||
57 | int | ||
58 | main (int argc, char **argv) | ||
59 | { | ||
60 | |||
61 | char input_buffer[MAX_INPUT_BUFFER] = ""; | ||
62 | char *result_text = NULL; | ||
63 | char *status_text; | ||
64 | char *output = NULL; | ||
65 | char *eol = NULL; | ||
66 | char *srvc_desc = NULL; | ||
67 | int cresult; | ||
68 | int result = STATE_UNKNOWN; | ||
69 | time_t local_time; | ||
70 | FILE *fp = NULL; | ||
71 | |||
72 | |||
73 | /* process arguments */ | ||
74 | if (process_arguments (argc, argv) == ERROR) | ||
75 | usage ("Could not parse arguments\n"); | ||
76 | |||
77 | |||
78 | /* Set signal handling and alarm timeout */ | ||
79 | if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) { | ||
80 | printf ("Cannot catch SIGALRM"); | ||
81 | return STATE_UNKNOWN; | ||
82 | } | ||
83 | alarm (timeout_interval); | ||
84 | |||
85 | |||
86 | /* run the command */ | ||
87 | |||
88 | if (verbose) | ||
89 | printf ("%s\n", comm); | ||
90 | |||
91 | child_process = spopen (comm); | ||
92 | |||
93 | if (child_process == NULL) { | ||
94 | printf ("Unable to open pipe: %s", comm); | ||
95 | return STATE_UNKNOWN; | ||
96 | } | ||
97 | |||
98 | |||
99 | /* open STDERR for spopen */ | ||
100 | child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); | ||
101 | if (child_stderr == NULL) { | ||
102 | printf ("Could not open stderr for %s\n", SSH_COMMAND); | ||
103 | } | ||
104 | |||
105 | |||
106 | /* get results from remote command */ | ||
107 | result_text = realloc (result_text, 1); | ||
108 | result_text[0] = 0; | ||
109 | while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) | ||
110 | result_text = strscat (result_text, input_buffer); | ||
111 | |||
112 | |||
113 | /* WARNING if output found on stderr */ | ||
114 | if (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { | ||
115 | printf ("%s\n", input_buffer); | ||
116 | return STATE_WARNING; | ||
117 | } | ||
118 | (void) fclose (child_stderr); | ||
119 | |||
120 | |||
121 | /* close the pipe */ | ||
122 | result = spclose (child_process); | ||
123 | |||
124 | |||
125 | /* process output */ | ||
126 | if (passive) { | ||
127 | |||
128 | if (!(fp = fopen (outputfile, "a"))) { | ||
129 | printf ("SSH WARNING: could not open %s\n", outputfile); | ||
130 | exit (STATE_UNKNOWN); | ||
131 | } | ||
132 | |||
133 | time (&local_time); | ||
134 | srvc_desc = strtok (servicelist, ":"); | ||
135 | while (result_text != NULL) { | ||
136 | status_text = (strstr (result_text, "STATUS CODE: ")); | ||
137 | if (status_text == NULL) { | ||
138 | printf ("%s", result_text); | ||
139 | return result; | ||
140 | } | ||
141 | output = result_text; | ||
142 | result_text = strnl (status_text); | ||
143 | eol = strpbrk (output, "\r\n"); | ||
144 | if (eol != NULL) | ||
145 | eol[0] = 0; | ||
146 | if (srvc_desc && status_text | ||
147 | && sscanf (status_text, "STATUS CODE: %d", &cresult) == 1) { | ||
148 | fprintf (fp, "%d PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", | ||
149 | (int) local_time, host_shortname, srvc_desc, cresult, | ||
150 | output); | ||
151 | srvc_desc = strtok (NULL, ":"); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | } | ||
156 | |||
157 | /* print the first line from the remote command */ | ||
158 | else { | ||
159 | eol = strpbrk (result_text, "\r\n"); | ||
160 | if (eol) | ||
161 | eol[0] = 0; | ||
162 | printf ("%s\n", result_text); | ||
163 | |||
164 | } | ||
165 | |||
166 | |||
167 | /* return error status from remote command */ | ||
168 | return result; | ||
169 | } | ||
170 | |||
171 | |||
172 | |||
173 | |||
174 | |||
175 | /* process command-line arguments */ | ||
176 | int | ||
177 | process_arguments (int argc, char **argv) | ||
178 | { | ||
179 | int c; | ||
180 | |||
181 | if (argc < 2) | ||
182 | return ERROR; | ||
183 | |||
184 | remotecmd = realloc (remotecmd, 1); | ||
185 | remotecmd[0] = 0; | ||
186 | |||
187 | for (c = 1; c < argc; c++) | ||
188 | if (strcmp ("-to", argv[c]) == 0) | ||
189 | strcpy (argv[c], "-t"); | ||
190 | |||
191 | comm = strscpy (comm, SSH_COMMAND); | ||
192 | |||
193 | c = 0; | ||
194 | while (c += (call_getopt (argc - c, &argv[c]))) { | ||
195 | |||
196 | if (argc <= c) | ||
197 | break; | ||
198 | |||
199 | if (hostname == NULL) { | ||
200 | if (!is_host (argv[c])) | ||
201 | terminate (STATE_UNKNOWN, "%s: Invalid host name %s\n", PROGNAME, | ||
202 | argv[c]); | ||
203 | hostname = argv[c]; | ||
204 | } | ||
205 | else if (remotecmd == NULL) { | ||
206 | remotecmd = strscpy (remotecmd, argv[c++]); | ||
207 | for (; c < argc; c++) | ||
208 | remotecmd = ssprintf (remotecmd, "%s %s", remotecmd, argv[c]); | ||
209 | } | ||
210 | |||
211 | } | ||
212 | |||
213 | if (commands > 1) | ||
214 | remotecmd = strscat (remotecmd, ";echo STATUS CODE: $?;"); | ||
215 | |||
216 | if (remotecmd == NULL || strlen (remotecmd) <= 1) | ||
217 | usage ("No remotecmd\n"); | ||
218 | |||
219 | comm = ssprintf (comm, "%s %s '%s'", comm, hostname, remotecmd); | ||
220 | |||
221 | return validate_arguments (); | ||
222 | } | ||
223 | |||
224 | |||
225 | |||
226 | |||
227 | |||
228 | /* Call getopt */ | ||
229 | int | ||
230 | call_getopt (int argc, char **argv) | ||
231 | { | ||
232 | int c, i = 1; | ||
233 | |||
234 | #ifdef HAVE_GETOPT_H | ||
235 | int option_index = 0; | ||
236 | static struct option long_options[] = { | ||
237 | {"version", no_argument, 0, 'V'}, | ||
238 | {"help", no_argument, 0, 'h'}, | ||
239 | {"verbose", no_argument, 0, 'v'}, | ||
240 | {"fork", no_argument, 0, 'f'}, | ||
241 | {"timeout", required_argument, 0, 't'}, | ||
242 | {"host", required_argument, 0, 'H'}, | ||
243 | {"port", required_argument,0,'P'}, | ||
244 | {"output", required_argument, 0, 'O'}, | ||
245 | {"name", required_argument, 0, 'n'}, | ||
246 | {"services", required_argument, 0, 's'}, | ||
247 | {"identity", required_argument, 0, 'i'}, | ||
248 | {"user", required_argument, 0, 'u'}, | ||
249 | {"logname", required_argument, 0, 'l'}, | ||
250 | {"command", required_argument, 0, 'C'}, | ||
251 | {0, 0, 0, 0} | ||
252 | }; | ||
253 | #endif | ||
254 | |||
255 | while (1) { | ||
256 | #ifdef HAVE_GETOPT_H | ||
257 | c = | ||
258 | getopt_long (argc, argv, "+?Vvhft:H:O:P:p:i:u:l:C:n:s:", long_options, | ||
259 | &option_index); | ||
260 | #else | ||
261 | c = getopt (argc, argv, "+?Vvhft:H:O:P:p:i:u:l:C:n:s:"); | ||
262 | #endif | ||
263 | |||
264 | if (c == -1 || c == EOF) | ||
265 | break; | ||
266 | |||
267 | i++; | ||
268 | switch (c) { | ||
269 | case 't': | ||
270 | case 'H': | ||
271 | case 'O': | ||
272 | case 'p': | ||
273 | case 'i': | ||
274 | case 'u': | ||
275 | case 'l': | ||
276 | case 'n': | ||
277 | case 's': | ||
278 | i++; | ||
279 | } | ||
280 | |||
281 | switch (c) { | ||
282 | case '?': /* help */ | ||
283 | print_usage (); | ||
284 | exit (STATE_UNKNOWN); | ||
285 | case 'V': /* version */ | ||
286 | print_revision (PROGNAME, "$Revision$"); | ||
287 | exit (STATE_OK); | ||
288 | case 'h': /* help */ | ||
289 | print_help (PROGNAME); | ||
290 | exit (STATE_OK); | ||
291 | case 'v': /* help */ | ||
292 | verbose = TRUE; | ||
293 | break; | ||
294 | case 'f': /* fork to background */ | ||
295 | comm = ssprintf (comm, "%s -f", comm); | ||
296 | break; | ||
297 | case 't': /* timeout period */ | ||
298 | if (!is_integer (optarg)) | ||
299 | usage2 ("timeout interval must be an integer", optarg); | ||
300 | timeout_interval = atoi (optarg); | ||
301 | break; | ||
302 | case 'H': /* host */ | ||
303 | if (!is_host (optarg)) | ||
304 | usage2 ("invalid host name", optarg); | ||
305 | hostname = optarg; | ||
306 | break; | ||
307 | case 'P': /* port number */ | ||
308 | case 'p': /* port number */ | ||
309 | if (!is_integer (optarg)) | ||
310 | usage2 ("port must be an integer", optarg); | ||
311 | comm = ssprintf (comm,"%s -p %s", comm, optarg); | ||
312 | break; | ||
313 | case 'O': /* output file */ | ||
314 | outputfile = optarg; | ||
315 | passive = TRUE; | ||
316 | break; | ||
317 | case 's': /* description of service to check */ | ||
318 | servicelist = optarg; | ||
319 | break; | ||
320 | case 'n': /* short name of host in nagios configuration */ | ||
321 | host_shortname = optarg; | ||
322 | break; | ||
323 | case 'u': | ||
324 | c = 'l'; | ||
325 | case 'l': /* login name */ | ||
326 | case 'i': /* identity */ | ||
327 | comm = ssprintf (comm, "%s -%c %s", comm, c, optarg); | ||
328 | break; | ||
329 | case 'C': /* Command for remote machine */ | ||
330 | commands++; | ||
331 | if (commands > 1) | ||
332 | remotecmd = strscat (remotecmd, ";echo STATUS CODE: $?;"); | ||
333 | remotecmd = strscat (remotecmd, optarg); | ||
334 | } | ||
335 | } | ||
336 | return i; | ||
337 | } | ||
338 | |||
339 | |||
340 | |||
341 | |||
342 | |||
343 | int | ||
344 | validate_arguments (void) | ||
345 | { | ||
346 | if (remotecmd == NULL || hostname == NULL) | ||
347 | return ERROR; | ||
348 | return OK; | ||
349 | } | ||
350 | |||
351 | |||
352 | |||
353 | |||
354 | |||
355 | void | ||
356 | print_help (char *cmd) | ||
357 | { | ||
358 | print_revision (cmd, "$Revision$"); | ||
359 | |||
360 | printf | ||
361 | ("Copyright (c) 1999 Karl DeBisschop (kdebisschop@alum.mit.edu)\n\n" | ||
362 | "This plugin will execute a command on a remote host using SSH\n\n"); | ||
363 | |||
364 | print_usage (); | ||
365 | |||
366 | printf | ||
367 | ("\nOptions:\n" | ||
368 | "-H, --hostname=HOST\n" | ||
369 | " name or IP address of remote host\n" | ||
370 | "-C, --command='COMMAND STRING'\n" | ||
371 | " command to execute on the remote machine\n" | ||
372 | "-f tells ssh to fork rather than create a tty\n" | ||
373 | "-t, --timeout=INTEGER\n" | ||
374 | " specify timeout (default: %d seconds) [optional]\n" | ||
375 | "-l, --logname=USERNAME\n" | ||
376 | " SSH user name on remote host [optional]\n" | ||
377 | "-i, --identity=KEYFILE\n" | ||
378 | " identity of an authorized key [optional]\n" | ||
379 | "-O, --output=FILE\n" | ||
380 | " external command file for nagios [optional]\n" | ||
381 | "-s, --services=LIST\n" | ||
382 | " list of nagios service names, separated by ':' [optional]\n" | ||
383 | "-n, --name=NAME\n" | ||
384 | " short name of host in nagios configuration [optional]\n" | ||
385 | "\n" | ||
386 | "The most common mode of use is to refer to a local identity file with\n" | ||
387 | "the '-i' option. In this mode, the identity pair should have a null\n" | ||
388 | "passphrase and the public key should be listed in the authorized_keys\n" | ||
389 | "file of the remote host. Usually the key will be restricted to running\n" | ||
390 | "only one command on the remote server. If the remote SSH server tracks\n" | ||
391 | "invocation agruments, the one remote program may be an agent that can\n" | ||
392 | "execute additional commands as proxy\n" | ||
393 | "\n" | ||
394 | "To use passive mode, provide multiple '-C' options, and provide\n" | ||
395 | "all of -O, -s, and -n options (servicelist order must match '-C'\n" | ||
396 | "options)\n", DEFAULT_SOCKET_TIMEOUT); | ||
397 | } | ||
398 | |||
399 | |||
400 | |||
401 | |||
402 | |||
403 | void | ||
404 | print_usage (void) | ||
405 | { | ||
406 | printf | ||
407 | ("Usage:\n" | ||
408 | "check_by_ssh [-f] [-t timeout] [-i identity] [-l user] -H <host> <command>\n" | ||
409 | " [-n name] [-s servicelist] [-O outputfile] [-P port]\n" | ||
410 | "check_by_ssh -V prints version info\n" | ||
411 | "check_by_ssh -h prints more detailed help\n"); | ||
412 | } | ||