Code

Added NLM test (Phil Randal)
[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 const char *progname = "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 int process_arguments (int, char **);
38 int validate_arguments (void);
39 void print_help (const char *command_name);
40 void print_usage (void);
43 int commands = 0;
44 int services = 0;
45 char *remotecmd = "";
46 char *comm = SSH_COMMAND;
47 char *hostname = NULL;
48 char *outputfile = NULL;
49 char *host_shortname = NULL;
50 char **service;
51 int passive = FALSE;
52 int verbose = FALSE;
55 int
56 main (int argc, char **argv)
57 {
59         char input_buffer[MAX_INPUT_BUFFER] = "";
60         char *result_text = "";
61         char *status_text;
62         char *output = "";
63         char *summary = "";
64         char *eol = NULL;
65         char *srvc_desc = NULL;
66         int cresult;
67         int result = STATE_UNKNOWN;
68         time_t local_time;
69         FILE *fp = NULL;
72         /* process arguments */
73         if (process_arguments (argc, argv) == ERROR)
74                 usage ("Could not parse arguments\n");
77         /* Set signal handling and alarm timeout */
78         if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
79                 printf ("Cannot catch SIGALRM");
80                 return STATE_UNKNOWN;
81         }
82         alarm (timeout_interval);
85         /* run the command */
87         if (verbose)
88                 printf ("%s\n", comm);
90         child_process = spopen (comm);
92         if (child_process == NULL) {
93                 printf ("Unable to open pipe: %s", comm);
94                 return STATE_UNKNOWN;
95         }
98         /* open STDERR  for spopen */
99         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
100         if (child_stderr == NULL) {
101                 printf ("Could not open stderr for %s\n", SSH_COMMAND);
102         }
105         /* get results from remote command */
106         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process))
107                 asprintf (&result_text, "%s%s", result_text, input_buffer);
110         /* WARNING if output found on stderr */
111         if (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
112                 printf ("%s\n", input_buffer);
113                 return STATE_WARNING;
114         }
115         (void) fclose (child_stderr);
118         /* close the pipe */
119         result = spclose (child_process);
122         /* process output */
123         if (passive) {
125                 if (!(fp = fopen (outputfile, "a"))) {
126                         printf ("SSH WARNING: could not open %s\n", outputfile);
127                         exit (STATE_UNKNOWN);
128                 }
130                 time (&local_time);
131                 commands = 0;
132                 while (result_text && strlen(result_text) > 0) {
133                         status_text = (strstr (result_text, "STATUS CODE: "));
134                         if (status_text == NULL) {
135                                 printf ("%s", result_text);
136                                 return result;
137                         }
138                         asprintf (&output, "%s", result_text);
139                         result_text = strnl (status_text);
140                         eol = strpbrk (output, "\r\n");
141                         if (eol != NULL)
142                                 eol[0] = 0;
143                         if (service[commands] && status_text
144                                         && sscanf (status_text, "STATUS CODE: %d", &cresult) == 1) {
145                                 fprintf (fp, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n",
146                                                                  (int) local_time, host_shortname, service[commands++], cresult,
147                                                                  output);
148                         }
149                 }
151         }
153         /* print the first line from the remote command */
154         else {
155                 eol = strpbrk (result_text, "\r\n");
156                 if (eol)
157                         eol[0] = 0;
158                 printf ("%s\n", result_text);
159         }
161         /* return error status from remote command */
162         return result;
169 /* process command-line arguments */
170 int
171 process_arguments (int argc, char **argv)
173         int c, i;
174         char *p1, *p2;
175         size_t len;
177         int option_index = 0;
178         static struct option long_options[] = {
179                 {"version", no_argument, 0, 'V'},
180                 {"help", no_argument, 0, 'h'},
181                 {"verbose", no_argument, 0, 'v'},
182                 {"fork", no_argument, 0, 'f'},
183                 {"timeout", required_argument, 0, 't'},
184                 {"host", required_argument, 0, 'H'},
185                 {"port", required_argument,0,'p'},
186                 {"output", required_argument, 0, 'O'},
187                 {"name", required_argument, 0, 'n'},
188                 {"services", required_argument, 0, 's'},
189                 {"identity", required_argument, 0, 'i'},
190                 {"user", required_argument, 0, 'u'},
191                 {"logname", required_argument, 0, 'l'},
192                 {"command", required_argument, 0, 'C'},
193                 {"proto1", no_argument, 0, '1'},
194                 {"proto2", no_argument, 0, '2'},
195                 {"use-ipv4", no_argument, 0, '4'},
196                 {"use-ipv6", no_argument, 0, '6'},
197                 {0, 0, 0, 0}
198         };
200         if (argc < 2)
201                 return ERROR;
203         for (c = 1; c < argc; c++)
204                 if (strcmp ("-to", argv[c]) == 0)
205                         strcpy (argv[c], "-t");
207         while (1) {
208                 c = getopt_long (argc, argv, "Vvh1246ft:H:O:p:i:u:l:C:n:s:", long_options,
209                                                                          &option_index);
211                 if (c == -1 || c == EOF)
212                         break;
214                 switch (c) {
215                 case '?':                                                                       /* help */
216                         print_usage ();
217                         exit (STATE_UNKNOWN);
218                 case 'V':                                                                       /* version */
219                         print_revision (progname, "$Revision$");
220                         exit (STATE_OK);
221                 case 'h':                                                                       /* help */
222                         print_help (progname);
223                         exit (STATE_OK);
224                 case 'v':                                                                       /* help */
225                         verbose = TRUE;
226                         break;
227                 case 't':                                                                       /* timeout period */
228                         if (!is_integer (optarg))
229                                 usage2 ("timeout interval must be an integer", optarg);
230                         timeout_interval = atoi (optarg);
231                         break;
232                 case 'H':                                                                       /* host */
233                         if (!is_host (optarg))
234                                 usage2 ("invalid host name", optarg);
235                         hostname = optarg;
236                         break;
237                 case 'p': /* port number */
238                         if (!is_integer (optarg))
239                                 usage2 ("port must be an integer", optarg);
240                         asprintf (&comm,"%s -p %s", comm, optarg);
241                         break;
242                 case 'O':                                                                       /* output file */
243                         outputfile = optarg;
244                         passive = TRUE;
245                         break;
246                 case 's':                                                                       /* description of service to check */
247                         service = realloc (service, (++services) * sizeof(char *));
248                         p1 = optarg;
249                         while (p2 = index (p1, ':')) {
250                                 *p2 = '\0';
251                                 asprintf (&service[services-1], "%s", p1);
252                                 service = realloc (service, (++services) * sizeof(char *));
253                                 p1 = p2 + 1;
254                         }
255                         asprintf (&service[services-1], "%s", p1);
256                         break;
257                 case 'n':                                                                       /* short name of host in nagios configuration */
258                         host_shortname = optarg;
259                         break;
260                 case 'u':
261                         c = 'l';
262                 case 'l':                                                                       /* login name */
263                 case 'i':                                                                       /* identity */
264                         asprintf (&comm, "%s -%c %s", comm, c, optarg);
265                         break;
266                 case '1':                                                                       /* Pass these switches directly to ssh */
267                 case '2':                                                                       /* 1 to force version 1, 2 to force version 2 */
268                 case '4':                                                                       /* -4 for IPv4 */
269                 case '6':                                                               /* -6 for IPv6 */
270                 case 'f':                                                                       /* fork to background */
271                         asprintf (&comm, "%s -%c", comm, c);
272                         break;
273                 case 'C':                                                                       /* Command for remote machine */
274                         commands++;
275                         if (commands > 1)
276                                 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
277                         asprintf (&remotecmd, "%s%s", remotecmd, optarg);
278                 }
279         }
281         c = optind;
282         if (hostname == NULL) {
283                 if (c <= argc) {
284                         terminate (STATE_UNKNOWN, "%s: You must provide a host name\n", progname);
285                 } else if (!is_host (argv[c]))
286                         terminate (STATE_UNKNOWN, "%s: Invalid host name %s\n", progname, argv[c]);
287                 hostname = argv[c++];
288         }
290         if (strlen(remotecmd) == 0) {
291                 for (; c < argc; c++)
292                         if (strlen(remotecmd) > 0)
293                                 asprintf (&remotecmd, "%s %s", remotecmd, argv[c]);
294                         else
295                                 asprintf (&remotecmd, "%s", argv[c]);
296         }
298         if (commands > 1)
299                 remotecmd = strscat (remotecmd, ";echo STATUS CODE: $?;");
301         if (remotecmd == NULL || strlen (remotecmd) <= 1)
302                 usage ("No remotecmd\n");
304         asprintf (&comm, "%s %s '%s'", comm, hostname, remotecmd);
306         return validate_arguments ();
313 int
314 validate_arguments (void)
316         if (remotecmd == NULL || hostname == NULL)
317                 return ERROR;
319         if (passive && commands != services)
320                 terminate (STATE_UNKNOWN, "%s: In passive mode, you must provide a service name for each command.\n", progname);
322         if (passive && host_shortname == NULL)
323                 terminate (STATE_UNKNOWN, "%s: In passive mode, you must provide the host short name from the nagios configs.\n", progname);
325         return OK;
332 void
333 print_help (const char *cmd)
335         print_revision (cmd, "$Revision$");
337         printf
338                 ("Copyright (c) 1999    Karl DeBisschop (kdebisschop@alum.mit.edu)\n\n"
339                  "This plugin will execute a command on a remote host using SSH\n\n");
341         print_usage ();
343         printf
344                 ("\nOptions:\n"
345                  "-H, --hostname=HOST\n"
346                  "   name or IP address of remote host\n"
347                  "-C, --command='COMMAND STRING'\n"
348                  "   command to execute on the remote machine\n"
349                  "-f tells ssh to fork rather than create a tty\n"
350                  "-t, --timeout=INTEGER\n"
351                  "   specify timeout (default: %d seconds) [optional]\n"
352                  "-p, --port=PORT\n"
353                  "   port to connect to on remote system [optional]\n"
354          "-l, --logname=USERNAME\n"
355                  "   SSH user name on remote host [optional]\n"
356                  "-i, --identity=KEYFILE\n"
357                  "   identity of an authorized key [optional]\n"
358                  "-O, --output=FILE\n"
359                  "   external command file for nagios [optional]\n"
360                  "-s, --services=LIST\n"
361                  "   list of nagios service names, separated by ':' [optional]\n"
362                  "-n, --name=NAME\n"
363                  "   short name of host in nagios configuration [optional]\n"
364                  "-1, --proto1\n"
365                  "   tell ssh to use Protocol 1\n"
366                  "-2, --proto2\n"
367                  "   tell ssh to use Protocol 2\n"
368                  "-4, --use-ipv4\n"
369                  "   tell ssh to use IPv4\n"
370                  "-6, --use-ipv6\n"
371                  "   tell ssh to use IPv6\n"
372                  "\n"
373                  "The most common mode of use is to refer to a local identity file with\n"
374                  "the '-i' option. In this mode, the identity pair should have a null\n"
375                  "passphrase and the public key should be listed in the authorized_keys\n"
376                  "file of the remote host. Usually the key will be restricted to running\n"
377                  "only one command on the remote server. If the remote SSH server tracks\n"
378                  "invocation agruments, the one remote program may be an agent that can\n"
379                  "execute additional commands as proxy\n"
380                  "\n"
381                  "To use passive mode, provide multiple '-C' options, and provide\n"
382                  "all of -O, -s, and -n options (servicelist order must match '-C'\n"
383                  "options)\n", DEFAULT_SOCKET_TIMEOUT);
390 void
391 print_usage (void)
393         printf
394                 ("Usage:\n"
395                  "check_by_ssh [-f46] [-t timeout] [-i identity] [-l user] -H <host> -C <command>\n"
396                  "             [-n name] [-s servicelist] [-O outputfile] [-p port]\n"
397                  "check_by_ssh  -V prints version info\n"
398                  "check_by_ssh  -h prints more detailed help\n");