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 *****************************************************************************/
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"
31 #include "config.h"
32 #include "common.h"
33 #include "popen.h"
34 #include "utils.h"
35 #include <time.h>
37 #define PROGNAME "check_by_ssh"
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);
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;
57 int
58 main (int argc, char **argv)
59 {
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;
73 /* process arguments */
74 if (process_arguments (argc, argv) == ERROR)
75 usage ("Could not parse arguments\n");
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);
86 /* run the command */
88 if (verbose)
89 printf ("%s\n", comm);
91 child_process = spopen (comm);
93 if (child_process == NULL) {
94 printf ("Unable to open pipe: %s", comm);
95 return STATE_UNKNOWN;
96 }
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 }
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);
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);
121 /* close the pipe */
122 result = spclose (child_process);
125 /* process output */
126 if (passive) {
128 if (!(fp = fopen (outputfile, "a"))) {
129 printf ("SSH WARNING: could not open %s\n", outputfile);
130 exit (STATE_UNKNOWN);
131 }
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 }
155 }
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);
164 }
167 /* return error status from remote command */
168 return result;
169 }
175 /* process command-line arguments */
176 int
177 process_arguments (int argc, char **argv)
178 {
179 int c;
181 if (argc < 2)
182 return ERROR;
184 remotecmd = realloc (remotecmd, 1);
185 remotecmd[0] = 0;
187 for (c = 1; c < argc; c++)
188 if (strcmp ("-to", argv[c]) == 0)
189 strcpy (argv[c], "-t");
191 comm = strscpy (comm, SSH_COMMAND);
193 c = 0;
194 while (c += (call_getopt (argc - c, &argv[c]))) {
196 if (argc <= c)
197 break;
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 }
211 }
213 if (commands > 1)
214 remotecmd = strscat (remotecmd, ";echo STATUS CODE: $?;");
216 if (remotecmd == NULL || strlen (remotecmd) <= 1)
217 usage ("No remotecmd\n");
219 comm = ssprintf (comm, "%s %s '%s'", comm, hostname, remotecmd);
221 return validate_arguments ();
222 }
228 /* Call getopt */
229 int
230 call_getopt (int argc, char **argv)
231 {
232 int c, i = 1;
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 {"use-ipv4", no_argument, 0, '4'},
252 {"use-ipv6", no_argument, 0, '6'},
253 {0, 0, 0, 0}
254 };
255 #endif
257 while (1) {
258 #ifdef HAVE_GETOPT_H
259 c =
260 getopt_long (argc, argv, "+?Vvhft46:H:O:p:i:u:l:C:n:s:", long_options,
261 &option_index);
262 #else
263 c = getopt (argc, argv, "+?Vvhft46:H:O:p:i:u:l:C:n:s:");
264 #endif
266 if (c == -1 || c == EOF)
267 break;
269 i++;
270 switch (c) {
271 case 't':
272 case 'H':
273 case 'O':
274 case 'p':
275 case 'i':
276 case 'u':
277 case 'l':
278 case 'n':
279 case 's':
280 i++;
281 }
283 switch (c) {
284 case '?': /* help */
285 print_usage ();
286 exit (STATE_UNKNOWN);
287 case 'V': /* version */
288 print_revision (PROGNAME, "$Revision$");
289 exit (STATE_OK);
290 case 'h': /* help */
291 print_help (PROGNAME);
292 exit (STATE_OK);
293 case 'v': /* help */
294 verbose = TRUE;
295 break;
296 case 'f': /* fork to background */
297 comm = ssprintf (comm, "%s -f", comm);
298 break;
299 case 't': /* timeout period */
300 if (!is_integer (optarg))
301 usage2 ("timeout interval must be an integer", optarg);
302 timeout_interval = atoi (optarg);
303 break;
304 case 'H': /* host */
305 if (!is_host (optarg))
306 usage2 ("invalid host name", optarg);
307 hostname = optarg;
308 break;
309 case 'p': /* port number */
310 if (!is_integer (optarg))
311 usage2 ("port must be an integer", optarg);
312 comm = ssprintf (comm,"%s -p %s", comm, optarg);
313 break;
314 case 'O': /* output file */
315 outputfile = optarg;
316 passive = TRUE;
317 break;
318 case 's': /* description of service to check */
319 servicelist = optarg;
320 break;
321 case 'n': /* short name of host in nagios configuration */
322 host_shortname = optarg;
323 break;
324 case 'u':
325 c = 'l';
326 case 'l': /* login name */
327 case 'i': /* identity */
328 comm = ssprintf (comm, "%s -%c %s", comm, c, optarg);
329 break;
330 case '4': /* IPv4 */
331 comm = ssprintf (comm, "%s -4", comm);
332 break;
333 case '6': /* IPv6 */
334 comm = ssprintf (comm, "%s -4", comm);
335 break;
336 case 'C': /* Command for remote machine */
337 commands++;
338 if (commands > 1)
339 remotecmd = strscat (remotecmd, ";echo STATUS CODE: $?;");
340 remotecmd = strscat (remotecmd, optarg);
341 }
342 }
343 return i;
344 }
350 int
351 validate_arguments (void)
352 {
353 if (remotecmd == NULL || hostname == NULL)
354 return ERROR;
355 return OK;
356 }
362 void
363 print_help (char *cmd)
364 {
365 print_revision (cmd, "$Revision$");
367 printf
368 ("Copyright (c) 1999 Karl DeBisschop (kdebisschop@alum.mit.edu)\n\n"
369 "This plugin will execute a command on a remote host using SSH\n\n");
371 print_usage ();
373 printf
374 ("\nOptions:\n"
375 "-H, --hostname=HOST\n"
376 " name or IP address of remote host\n"
377 "-C, --command='COMMAND STRING'\n"
378 " command to execute on the remote machine\n"
379 "-f tells ssh to fork rather than create a tty\n"
380 "-t, --timeout=INTEGER\n"
381 " specify timeout (default: %d seconds) [optional]\n"
382 "-p, --port=PORT\n"
383 " port to connect to on remote system [optional]\n"
384 "-l, --logname=USERNAME\n"
385 " SSH user name on remote host [optional]\n"
386 "-i, --identity=KEYFILE\n"
387 " identity of an authorized key [optional]\n"
388 "-O, --output=FILE\n"
389 " external command file for nagios [optional]\n"
390 "-s, --services=LIST\n"
391 " list of nagios service names, separated by ':' [optional]\n"
392 "-n, --name=NAME\n"
393 " short name of host in nagios configuration [optional]\n"
394 "-4, --use-ipv4\n"
395 " tell ssh to use IPv4\n"
396 "-6, --use-ipv6\n"
397 " tell ssh to use IPv6\n"
398 "\n"
399 "The most common mode of use is to refer to a local identity file with\n"
400 "the '-i' option. In this mode, the identity pair should have a null\n"
401 "passphrase and the public key should be listed in the authorized_keys\n"
402 "file of the remote host. Usually the key will be restricted to running\n"
403 "only one command on the remote server. If the remote SSH server tracks\n"
404 "invocation agruments, the one remote program may be an agent that can\n"
405 "execute additional commands as proxy\n"
406 "\n"
407 "To use passive mode, provide multiple '-C' options, and provide\n"
408 "all of -O, -s, and -n options (servicelist order must match '-C'\n"
409 "options)\n", DEFAULT_SOCKET_TIMEOUT);
410 }
416 void
417 print_usage (void)
418 {
419 printf
420 ("Usage:\n"
421 "check_by_ssh [-f46] [-t timeout] [-i identity] [-l user] -H <host> -C <command>\n"
422 " [-n name] [-s servicelist] [-O outputfile] [-p port]\n"
423 "check_by_ssh -V prints version info\n"
424 "check_by_ssh -h prints more detailed help\n");
425 }