Code

ipv4/ipv6 switch added
[nagiosplug.git] / plugins / check_by_ssh.c
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"
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;
175 /* process command-line arguments */
176 int
177 process_arguments (int argc, char **argv)
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 ();
228 /* Call getopt */
229 int
230 call_getopt (int argc, char **argv)
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;
350 int
351 validate_arguments (void)
353         if (remotecmd == NULL || hostname == NULL)
354                 return ERROR;
355         return OK;
362 void
363 print_help (char *cmd)
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);
416 void
417 print_usage (void)
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");