Code

97c8672fa668135f854243c68b01cb4528e08e1a
[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 = 0;
47 int services = 0;
48 char *remotecmd = "";
49 char *comm = SSH_COMMAND;
50 char *hostname = NULL;
51 char *outputfile = NULL;
52 char *host_shortname = NULL;
53 char **service;
54 int passive = FALSE;
55 int verbose = FALSE;
58 int
59 main (int argc, char **argv)
60 {
62         char input_buffer[MAX_INPUT_BUFFER] = "";
63         char *result_text = "";
64         char *status_text;
65         char *output = "";
66         char *summary = "";
67         char *eol = NULL;
68         char *srvc_desc = NULL;
69         int cresult;
70         int result = STATE_UNKNOWN;
71         time_t local_time;
72         FILE *fp = NULL;
75         /* process arguments */
76         if (process_arguments (argc, argv) == ERROR)
77                 usage ("Could not parse arguments\n");
80         /* Set signal handling and alarm timeout */
81         if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
82                 printf ("Cannot catch SIGALRM");
83                 return STATE_UNKNOWN;
84         }
85         alarm (timeout_interval);
88         /* run the command */
90         if (verbose)
91                 printf ("%s\n", comm);
93         child_process = spopen (comm);
95         if (child_process == NULL) {
96                 printf ("Unable to open pipe: %s", comm);
97                 return STATE_UNKNOWN;
98         }
101         /* open STDERR  for spopen */
102         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
103         if (child_stderr == NULL) {
104                 printf ("Could not open stderr for %s\n", SSH_COMMAND);
105         }
108         /* get results from remote command */
109         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process))
110                 asprintf (&result_text, "%s%s", 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                 commands = 0;
135                 while (result_text && strlen(result_text) > 0) {
136                         status_text = (strstr (result_text, "STATUS CODE: "));
137                         if (status_text == NULL) {
138                                 printf ("%s", result_text);
139                                 return result;
140                         }
141                         asprintf (&output, "%s", result_text);
142                         result_text = strnl (status_text);
143                         eol = strpbrk (output, "\r\n");
144                         if (eol != NULL)
145                                 eol[0] = 0;
146                         if (service[commands] && 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, service[commands++], cresult,
150                                                                  output);
151                         }
152                 }
154         }
156         /* print the first line from the remote command */
157         else {
158                 eol = strpbrk (result_text, "\r\n");
159                 if (eol)
160                         eol[0] = 0;
161                 printf ("%s\n", result_text);
162         }
164         /* return error status from remote command */
165         return result;
172 /* process command-line arguments */
173 int
174 process_arguments (int argc, char **argv)
176         int c, i;
177         char *p1, *p2;
178         size_t len;
180 #ifdef HAVE_GETOPT_H
181         int option_index = 0;
182         static struct option long_options[] = {
183                 {"version", no_argument, 0, 'V'},
184                 {"help", no_argument, 0, 'h'},
185                 {"verbose", no_argument, 0, 'v'},
186                 {"fork", no_argument, 0, 'f'},
187                 {"timeout", required_argument, 0, 't'},
188                 {"host", required_argument, 0, 'H'},
189                 {"port", required_argument,0,'p'},
190                 {"output", required_argument, 0, 'O'},
191                 {"name", required_argument, 0, 'n'},
192                 {"services", required_argument, 0, 's'},
193                 {"identity", required_argument, 0, 'i'},
194                 {"user", required_argument, 0, 'u'},
195                 {"logname", required_argument, 0, 'l'},
196                 {"command", required_argument, 0, 'C'},
197                 {"use-ipv4", no_argument, 0, '4'},
198                 {"use-ipv6", no_argument, 0, '6'},
199                 {0, 0, 0, 0}
200         };
201 #endif
203         if (argc < 2)
204                 return ERROR;
206         for (c = 1; c < argc; c++)
207                 if (strcmp ("-to", argv[c]) == 0)
208                         strcpy (argv[c], "-t");
210         while (1) {
211 #ifdef HAVE_GETOPT_H
212                 c =
213                         getopt_long (argc, argv, "+?Vvhft46H:O:p:i:u:l:C:n:s:", long_options,
214                                                                          &option_index);
215 #else
216                 c = getopt (argc, argv, "+?Vvhft46H:O:p:i:u:l:C:n:s:");
217 #endif
219                 if (c == -1 || c == EOF)
220                         break;
222                 switch (c) {
223                 case '?':                                                                       /* help */
224                         print_usage ();
225                         exit (STATE_UNKNOWN);
226                 case 'V':                                                                       /* version */
227                         print_revision (PROGNAME, "$Revision$");
228                         exit (STATE_OK);
229                 case 'h':                                                                       /* help */
230                         print_help (PROGNAME);
231                         exit (STATE_OK);
232                 case 'v':                                                                       /* help */
233                         verbose = TRUE;
234                         break;
235                 case 't':                                                                       /* timeout period */
236                         if (!is_integer (optarg))
237                                 usage2 ("timeout interval must be an integer", optarg);
238                         timeout_interval = atoi (optarg);
239                         break;
240                 case 'H':                                                                       /* host */
241                         if (!is_host (optarg))
242                                 usage2 ("invalid host name", optarg);
243                         hostname = optarg;
244                         break;
245                 case 'p': /* port number */
246                         if (!is_integer (optarg))
247                                 usage2 ("port must be an integer", optarg);
248                         asprintf (&comm,"%s -p %s", comm, optarg);
249                         break;
250                 case 'O':                                                                       /* output file */
251                         outputfile = optarg;
252                         passive = TRUE;
253                         break;
254                 case 's':                                                                       /* description of service to check */
255                         service = realloc (service, ++services);
256                         p1 = optarg;
257                         while (p2 = index (p1, ':')) {
258                                 *p2 = '\0';
259                                 asprintf (&service[services-1], "%s", p1);
260                                 service = realloc (service, ++services);
261                                 p1 = p2 + 1;
262                         }
263                         asprintf (&service[services-1], "%s", p1);
264                         break;
265                 case 'n':                                                                       /* short name of host in nagios configuration */
266                         host_shortname = optarg;
267                         break;
268                 case 'u':
269                         c = 'l';
270                 case 'l':                                                                       /* login name */
271                 case 'i':                                                                       /* identity */
272                         asprintf (&comm, "%s -%c %s", comm, c, optarg);
273                         break;
274                 case '4':                                                                       /* Pass these switches directly to ssh */
275                 case '6':                                                               /* -4 for IPv4, -6 for IPv6 */
276                 case 'f':                                                                       /* fork to background */
277                         asprintf (&comm, "%s -%c", comm, c);
278                         break;
279                 case 'C':                                                                       /* Command for remote machine */
280                         commands++;
281                         if (commands > 1)
282                                 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
283                         asprintf (&remotecmd, "%s%s", remotecmd, optarg);
284                 }
285         }
287         c = optind;
288         if (hostname == NULL) {
289                 if (!is_host (argv[c]))
290                         terminate (STATE_UNKNOWN, "%s: Invalid host name %s\n", PROGNAME, argv[c]);
291                 hostname = argv[c++];
292         }
294         if (strlen(remotecmd) == 0) {
295                 for (; c < argc; c++)
296                         asprintf (&remotecmd, "%s %s", remotecmd, argv[c]);
297         }
299         if (commands > 1)
300                 remotecmd = strscat (remotecmd, ";echo STATUS CODE: $?;");
302         if (remotecmd == NULL || strlen (remotecmd) <= 1)
303                 usage ("No remotecmd\n");
305         asprintf (&comm, "%s %s '%s'", comm, hostname, remotecmd);
307         return validate_arguments ();
314 int
315 validate_arguments (void)
317         if (remotecmd == NULL || hostname == NULL)
318                 return ERROR;
320         if (passive && commands != services)
321                 terminate (STATE_UNKNOWN, "%s: In passive mode, you must provide a service name for each command.\n", PROGNAME);
323         if (passive && host_shortname == NULL)
324                 terminate (STATE_UNKNOWN, "%s: In passive mode, you must provide the host short name from the nagios configs.\n", PROGNAME);
326         return OK;
333 void
334 print_help (char *cmd)
336         print_revision (cmd, "$Revision$");
338         printf
339                 ("Copyright (c) 1999    Karl DeBisschop (kdebisschop@alum.mit.edu)\n\n"
340                  "This plugin will execute a command on a remote host using SSH\n\n");
342         print_usage ();
344         printf
345                 ("\nOptions:\n"
346                  "-H, --hostname=HOST\n"
347                  "   name or IP address of remote host\n"
348                  "-C, --command='COMMAND STRING'\n"
349                  "   command to execute on the remote machine\n"
350                  "-f tells ssh to fork rather than create a tty\n"
351                  "-t, --timeout=INTEGER\n"
352                  "   specify timeout (default: %d seconds) [optional]\n"
353                  "-p, --port=PORT\n"
354                  "   port to connect to on remote system [optional]\n"
355          "-l, --logname=USERNAME\n"
356                  "   SSH user name on remote host [optional]\n"
357                  "-i, --identity=KEYFILE\n"
358                  "   identity of an authorized key [optional]\n"
359                  "-O, --output=FILE\n"
360                  "   external command file for nagios [optional]\n"
361                  "-s, --services=LIST\n"
362                  "   list of nagios service names, separated by ':' [optional]\n"
363                  "-n, --name=NAME\n"
364                  "   short name of host in nagios configuration [optional]\n"
365                  "-4, --use-ipv4\n"
366                  "   tell ssh to use IPv4\n"
367                  "-6, --use-ipv6\n"
368                  "   tell ssh to use IPv6\n"
369                  "\n"
370                  "The most common mode of use is to refer to a local identity file with\n"
371                  "the '-i' option. In this mode, the identity pair should have a null\n"
372                  "passphrase and the public key should be listed in the authorized_keys\n"
373                  "file of the remote host. Usually the key will be restricted to running\n"
374                  "only one command on the remote server. If the remote SSH server tracks\n"
375                  "invocation agruments, the one remote program may be an agent that can\n"
376                  "execute additional commands as proxy\n"
377                  "\n"
378                  "To use passive mode, provide multiple '-C' options, and provide\n"
379                  "all of -O, -s, and -n options (servicelist order must match '-C'\n"
380                  "options)\n", DEFAULT_SOCKET_TIMEOUT);
387 void
388 print_usage (void)
390         printf
391                 ("Usage:\n"
392                  "check_by_ssh [-f46] [-t timeout] [-i identity] [-l user] -H <host> -C <command>\n"
393                  "             [-n name] [-s servicelist] [-O outputfile] [-p port]\n"
394                  "check_by_ssh  -V prints version info\n"
395                  "check_by_ssh  -h prints more detailed help\n");