Code

fixes for using POSIX return codes
[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                 {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;
343 int
344 validate_arguments (void)
346         if (remotecmd == NULL || hostname == NULL)
347                 return ERROR;
348         return OK;
355 void
356 print_help (char *cmd)
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);
403 void
404 print_usage (void)
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");