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, "+?Vvhft46H:O:p:i:u:l:C:n:s:", long_options,
261 &option_index);
262 #else
263 c = getopt (argc, argv, "+?Vvhft46H: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 't': /* timeout period */
297 if (!is_integer (optarg))
298 usage2 ("timeout interval must be an integer", optarg);
299 timeout_interval = atoi (optarg);
300 break;
301 case 'H': /* host */
302 if (!is_host (optarg))
303 usage2 ("invalid host name", optarg);
304 hostname = optarg;
305 break;
306 case 'p': /* port number */
307 if (!is_integer (optarg))
308 usage2 ("port must be an integer", optarg);
309 comm = ssprintf (comm,"%s -p %s", comm, optarg);
310 break;
311 case 'O': /* output file */
312 outputfile = optarg;
313 passive = TRUE;
314 break;
315 case 's': /* description of service to check */
316 servicelist = optarg;
317 break;
318 case 'n': /* short name of host in nagios configuration */
319 host_shortname = optarg;
320 break;
321 case 'u':
322 c = 'l';
323 case 'l': /* login name */
324 case 'i': /* identity */
325 comm = ssprintf (comm, "%s -%c %s", comm, c, optarg);
326 break;
327 case '4': /* Pass these switches directly to ssh */
328 case '6': /* -4 for IPv4, -6 for IPv6 */
329 case 'f': /* fork to background */
330 comm = ssprintf (comm, "%s -%c", comm, c);
331 break;
332 case 'C': /* Command for remote machine */
333 commands++;
334 if (commands > 1)
335 remotecmd = strscat (remotecmd, ";echo STATUS CODE: $?;");
336 remotecmd = strscat (remotecmd, optarg);
337 }
338 }
339 return i;
340 }
346 int
347 validate_arguments (void)
348 {
349 if (remotecmd == NULL || hostname == NULL)
350 return ERROR;
351 return OK;
352 }
358 void
359 print_help (char *cmd)
360 {
361 print_revision (cmd, "$Revision$");
363 printf
364 ("Copyright (c) 1999 Karl DeBisschop (kdebisschop@alum.mit.edu)\n\n"
365 "This plugin will execute a command on a remote host using SSH\n\n");
367 print_usage ();
369 printf
370 ("\nOptions:\n"
371 "-H, --hostname=HOST\n"
372 " name or IP address of remote host\n"
373 "-C, --command='COMMAND STRING'\n"
374 " command to execute on the remote machine\n"
375 "-f tells ssh to fork rather than create a tty\n"
376 "-t, --timeout=INTEGER\n"
377 " specify timeout (default: %d seconds) [optional]\n"
378 "-p, --port=PORT\n"
379 " port to connect to on remote system [optional]\n"
380 "-l, --logname=USERNAME\n"
381 " SSH user name on remote host [optional]\n"
382 "-i, --identity=KEYFILE\n"
383 " identity of an authorized key [optional]\n"
384 "-O, --output=FILE\n"
385 " external command file for nagios [optional]\n"
386 "-s, --services=LIST\n"
387 " list of nagios service names, separated by ':' [optional]\n"
388 "-n, --name=NAME\n"
389 " short name of host in nagios configuration [optional]\n"
390 "-4, --use-ipv4\n"
391 " tell ssh to use IPv4\n"
392 "-6, --use-ipv6\n"
393 " tell ssh to use IPv6\n"
394 "\n"
395 "The most common mode of use is to refer to a local identity file with\n"
396 "the '-i' option. In this mode, the identity pair should have a null\n"
397 "passphrase and the public key should be listed in the authorized_keys\n"
398 "file of the remote host. Usually the key will be restricted to running\n"
399 "only one command on the remote server. If the remote SSH server tracks\n"
400 "invocation agruments, the one remote program may be an agent that can\n"
401 "execute additional commands as proxy\n"
402 "\n"
403 "To use passive mode, provide multiple '-C' options, and provide\n"
404 "all of -O, -s, and -n options (servicelist order must match '-C'\n"
405 "options)\n", DEFAULT_SOCKET_TIMEOUT);
406 }
412 void
413 print_usage (void)
414 {
415 printf
416 ("Usage:\n"
417 "check_by_ssh [-f46] [-t timeout] [-i identity] [-l user] -H <host> -C <command>\n"
418 " [-n name] [-s servicelist] [-O outputfile] [-p port]\n"
419 "check_by_ssh -V prints version info\n"
420 "check_by_ssh -h prints more detailed help\n");
421 }