Code

in short options, t no no argument
[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 validate_arguments (void);
41 void print_help (char *command_name);
42 void print_usage (void);
45 int commands = 0;
46 int services = 0;
47 char *remotecmd = "";
48 char *comm = SSH_COMMAND;
49 char *hostname = NULL;
50 char *outputfile = NULL;
51 char *host_shortname = NULL;
52 char **service;
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 = "";
63         char *status_text;
64         char *output = "";
65         char *summary = "";
66         char *eol = NULL;
67         char *srvc_desc = NULL;
68         int cresult;
69         int result = STATE_UNKNOWN;
70         time_t local_time;
71         FILE *fp = NULL;
74         /* process arguments */
75         if (process_arguments (argc, argv) == ERROR)
76                 usage ("Could not parse arguments\n");
79         /* Set signal handling and alarm timeout */
80         if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
81                 printf ("Cannot catch SIGALRM");
82                 return STATE_UNKNOWN;
83         }
84         alarm (timeout_interval);
87         /* run the command */
89         if (verbose)
90                 printf ("%s\n", comm);
92         child_process = spopen (comm);
94         if (child_process == NULL) {
95                 printf ("Unable to open pipe: %s", comm);
96                 return STATE_UNKNOWN;
97         }
100         /* open STDERR  for spopen */
101         child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
102         if (child_stderr == NULL) {
103                 printf ("Could not open stderr for %s\n", SSH_COMMAND);
104         }
107         /* get results from remote command */
108         while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process))
109                 asprintf (&result_text, "%s%s", result_text, input_buffer);
112         /* WARNING if output found on stderr */
113         if (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
114                 printf ("%s\n", input_buffer);
115                 return STATE_WARNING;
116         }
117         (void) fclose (child_stderr);
120         /* close the pipe */
121         result = spclose (child_process);
124         /* process output */
125         if (passive) {
127                 if (!(fp = fopen (outputfile, "a"))) {
128                         printf ("SSH WARNING: could not open %s\n", outputfile);
129                         exit (STATE_UNKNOWN);
130                 }
132                 time (&local_time);
133                 commands = 0;
134                 while (result_text && strlen(result_text) > 0) {
135                         status_text = (strstr (result_text, "STATUS CODE: "));
136                         if (status_text == NULL) {
137                                 printf ("%s", result_text);
138                                 return result;
139                         }
140                         asprintf (&output, "%s", result_text);
141                         result_text = strnl (status_text);
142                         eol = strpbrk (output, "\r\n");
143                         if (eol != NULL)
144                                 eol[0] = 0;
145                         if (service[commands] && status_text
146                                         && sscanf (status_text, "STATUS CODE: %d", &cresult) == 1) {
147                                 fprintf (fp, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n",
148                                                                  (int) local_time, host_shortname, service[commands++], cresult,
149                                                                  output);
150                         }
151                 }
153         }
155         /* print the first line from the remote command */
156         else {
157                 eol = strpbrk (result_text, "\r\n");
158                 if (eol)
159                         eol[0] = 0;
160                 printf ("%s\n", result_text);
161         }
163         /* return error status from remote command */
164         return result;
171 /* process command-line arguments */
172 int
173 process_arguments (int argc, char **argv)
175         int c, i;
176         char *p1, *p2;
177         size_t len;
179 #ifdef HAVE_GETOPT_H
180         int option_index = 0;
181         static struct option long_options[] = {
182                 {"version", no_argument, 0, 'V'},
183                 {"help", no_argument, 0, 'h'},
184                 {"verbose", no_argument, 0, 'v'},
185                 {"fork", no_argument, 0, 'f'},
186                 {"timeout", required_argument, 0, 't'},
187                 {"host", required_argument, 0, 'H'},
188                 {"port", required_argument,0,'p'},
189                 {"output", required_argument, 0, 'O'},
190                 {"name", required_argument, 0, 'n'},
191                 {"services", required_argument, 0, 's'},
192                 {"identity", required_argument, 0, 'i'},
193                 {"user", required_argument, 0, 'u'},
194                 {"logname", required_argument, 0, 'l'},
195                 {"command", required_argument, 0, 'C'},
196                 {"use-ipv4", no_argument, 0, '4'},
197                 {"use-ipv6", no_argument, 0, '6'},
198                 {0, 0, 0, 0}
199         };
200 #endif
202         if (argc < 2)
203                 return ERROR;
205         for (c = 1; c < argc; c++)
206                 if (strcmp ("-to", argv[c]) == 0)
207                         strcpy (argv[c], "-t");
209         while (1) {
210 #ifdef HAVE_GETOPT_H
211                 c =
212                         getopt_long (argc, argv, "Vvh46ft:H:O:p:i:u:l:C:n:s:", long_options,
213                                                                          &option_index);
214 #else
215                 c = getopt (argc, argv, "Vvh46ft:H:O:p:i:u:l:C:n:s:");
216 #endif
218                 if (c == -1 || c == EOF)
219                         break;
221                 switch (c) {
222                 case '?':                                                                       /* help */
223                         print_usage ();
224                         exit (STATE_UNKNOWN);
225                 case 'V':                                                                       /* version */
226                         print_revision (PROGNAME, "$Revision$");
227                         exit (STATE_OK);
228                 case 'h':                                                                       /* help */
229                         print_help (PROGNAME);
230                         exit (STATE_OK);
231                 case 'v':                                                                       /* help */
232                         verbose = TRUE;
233                         break;
234                 case 't':                                                                       /* timeout period */
235                         if (!is_integer (optarg))
236                                 usage2 ("timeout interval must be an integer", optarg);
237                         timeout_interval = atoi (optarg);
238                         break;
239                 case 'H':                                                                       /* host */
240                         if (!is_host (optarg))
241                                 usage2 ("invalid host name", optarg);
242                         hostname = optarg;
243                         break;
244                 case 'p': /* port number */
245                         if (!is_integer (optarg))
246                                 usage2 ("port must be an integer", optarg);
247                         asprintf (&comm,"%s -p %s", comm, optarg);
248                         break;
249                 case 'O':                                                                       /* output file */
250                         outputfile = optarg;
251                         passive = TRUE;
252                         break;
253                 case 's':                                                                       /* description of service to check */
254                         service = realloc (service, ++services);
255                         p1 = optarg;
256                         while (p2 = index (p1, ':')) {
257                                 *p2 = '\0';
258                                 asprintf (&service[services-1], "%s", p1);
259                                 service = realloc (service, ++services);
260                                 p1 = p2 + 1;
261                         }
262                         asprintf (&service[services-1], "%s", p1);
263                         break;
264                 case 'n':                                                                       /* short name of host in nagios configuration */
265                         host_shortname = optarg;
266                         break;
267                 case 'u':
268                         c = 'l';
269                 case 'l':                                                                       /* login name */
270                 case 'i':                                                                       /* identity */
271                         asprintf (&comm, "%s -%c %s", comm, c, optarg);
272                         break;
273                 case '4':                                                                       /* Pass these switches directly to ssh */
274                 case '6':                                                               /* -4 for IPv4, -6 for IPv6 */
275                 case 'f':                                                                       /* fork to background */
276                         asprintf (&comm, "%s -%c", comm, c);
277                         break;
278                 case 'C':                                                                       /* Command for remote machine */
279                         commands++;
280                         if (commands > 1)
281                                 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
282                         asprintf (&remotecmd, "%s%s", remotecmd, optarg);
283                 }
284         }
286         c = optind;
287         if (hostname == NULL) {
288                 if (!is_host (argv[c]))
289                         terminate (STATE_UNKNOWN, "%s: Invalid host name %s\n", PROGNAME, argv[c]);
290                 hostname = argv[c++];
291         }
293         if (strlen(remotecmd) == 0) {
294                 for (; c < argc; c++)
295                         asprintf (&remotecmd, "%s %s", remotecmd, 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 (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                  "-4, --use-ipv4\n"
365                  "   tell ssh to use IPv4\n"
366                  "-6, --use-ipv6\n"
367                  "   tell ssh to use IPv6\n"
368                  "\n"
369                  "The most common mode of use is to refer to a local identity file with\n"
370                  "the '-i' option. In this mode, the identity pair should have a null\n"
371                  "passphrase and the public key should be listed in the authorized_keys\n"
372                  "file of the remote host. Usually the key will be restricted to running\n"
373                  "only one command on the remote server. If the remote SSH server tracks\n"
374                  "invocation agruments, the one remote program may be an agent that can\n"
375                  "execute additional commands as proxy\n"
376                  "\n"
377                  "To use passive mode, provide multiple '-C' options, and provide\n"
378                  "all of -O, -s, and -n options (servicelist order must match '-C'\n"
379                  "options)\n", DEFAULT_SOCKET_TIMEOUT);
386 void
387 print_usage (void)
389         printf
390                 ("Usage:\n"
391                  "check_by_ssh [-f46] [-t timeout] [-i identity] [-l user] -H <host> -C <command>\n"
392                  "             [-n name] [-s servicelist] [-O outputfile] [-p port]\n"
393                  "check_by_ssh  -V prints version info\n"
394                  "check_by_ssh  -h prints more detailed help\n");