a81b333f1ac2c36b48397f042f6a3a04dd528444
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 {0, 0, 0, 0}
252 };
253 #endif
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
264 if (c == -1 || c == EOF)
265 break;
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 }
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 }
343 int
344 validate_arguments (void)
345 {
346 if (remotecmd == NULL || hostname == NULL)
347 return ERROR;
348 return OK;
349 }
355 void
356 print_help (char *cmd)
357 {
358 print_revision (cmd, "$Revision$");
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");
364 print_usage ();
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 }
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 }