Code

convert PROGANE from a define to a const char
[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 #ifdef HAVE_GETOPT_H
178         int option_index = 0;
179         static struct option long_options[] = {
180                 {"version", no_argument, 0, 'V'},
181                 {"help", no_argument, 0, 'h'},
182                 {"verbose", no_argument, 0, 'v'},
183                 {"fork", no_argument, 0, 'f'},
184                 {"timeout", required_argument, 0, 't'},
185                 {"host", required_argument, 0, 'H'},
186                 {"port", required_argument,0,'p'},
187                 {"output", required_argument, 0, 'O'},
188                 {"name", required_argument, 0, 'n'},
189                 {"services", required_argument, 0, 's'},
190                 {"identity", required_argument, 0, 'i'},
191                 {"user", required_argument, 0, 'u'},
192                 {"logname", required_argument, 0, 'l'},
193                 {"command", required_argument, 0, 'C'},
194                 {"use-ipv4", no_argument, 0, '4'},
195                 {"use-ipv6", no_argument, 0, '6'},
196                 {0, 0, 0, 0}
197         };
198 #endif
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 #ifdef HAVE_GETOPT_H
209                 c =
210                         getopt_long (argc, argv, "Vvh46ft:H:O:p:i:u:l:C:n:s:", long_options,
211                                                                          &option_index);
212 #else
213                 c = getopt (argc, argv, "Vvh46ft:H:O:p:i:u:l:C:n:s:");
214 #endif
216                 if (c == -1 || c == EOF)
217                         break;
219                 switch (c) {
220                 case '?':                                                                       /* help */
221                         print_usage ();
222                         exit (STATE_UNKNOWN);
223                 case 'V':                                                                       /* version */
224                         print_revision (progname, "$Revision$");
225                         exit (STATE_OK);
226                 case 'h':                                                                       /* help */
227                         print_help (progname);
228                         exit (STATE_OK);
229                 case 'v':                                                                       /* help */
230                         verbose = TRUE;
231                         break;
232                 case 't':                                                                       /* timeout period */
233                         if (!is_integer (optarg))
234                                 usage2 ("timeout interval must be an integer", optarg);
235                         timeout_interval = atoi (optarg);
236                         break;
237                 case 'H':                                                                       /* host */
238                         if (!is_host (optarg))
239                                 usage2 ("invalid host name", optarg);
240                         hostname = optarg;
241                         break;
242                 case 'p': /* port number */
243                         if (!is_integer (optarg))
244                                 usage2 ("port must be an integer", optarg);
245                         asprintf (&comm,"%s -p %s", comm, optarg);
246                         break;
247                 case 'O':                                                                       /* output file */
248                         outputfile = optarg;
249                         passive = TRUE;
250                         break;
251                 case 's':                                                                       /* description of service to check */
252                         service = realloc (service, ++services);
253                         p1 = optarg;
254                         while (p2 = index (p1, ':')) {
255                                 *p2 = '\0';
256                                 asprintf (&service[services-1], "%s", p1);
257                                 service = realloc (service, ++services);
258                                 p1 = p2 + 1;
259                         }
260                         asprintf (&service[services-1], "%s", p1);
261                         break;
262                 case 'n':                                                                       /* short name of host in nagios configuration */
263                         host_shortname = optarg;
264                         break;
265                 case 'u':
266                         c = 'l';
267                 case 'l':                                                                       /* login name */
268                 case 'i':                                                                       /* identity */
269                         asprintf (&comm, "%s -%c %s", comm, c, optarg);
270                         break;
271                 case '4':                                                                       /* Pass these switches directly to ssh */
272                 case '6':                                                               /* -4 for IPv4, -6 for IPv6 */
273                 case 'f':                                                                       /* fork to background */
274                         asprintf (&comm, "%s -%c", comm, c);
275                         break;
276                 case 'C':                                                                       /* Command for remote machine */
277                         commands++;
278                         if (commands > 1)
279                                 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
280                         asprintf (&remotecmd, "%s%s", remotecmd, optarg);
281                 }
282         }
284         c = optind;
285         if (hostname == NULL) {
286                 if (!is_host (argv[c]))
287                         terminate (STATE_UNKNOWN, "%s: Invalid host name %s\n", progname, argv[c]);
288                 hostname = argv[c++];
289         }
291         if (strlen(remotecmd) == 0) {
292                 for (; c < argc; c++)
293                         asprintf (&remotecmd, "%s %s", remotecmd, argv[c]);
294         }
296         if (commands > 1)
297                 remotecmd = strscat (remotecmd, ";echo STATUS CODE: $?;");
299         if (remotecmd == NULL || strlen (remotecmd) <= 1)
300                 usage ("No remotecmd\n");
302         asprintf (&comm, "%s %s '%s'", comm, hostname, remotecmd);
304         return validate_arguments ();
311 int
312 validate_arguments (void)
314         if (remotecmd == NULL || hostname == NULL)
315                 return ERROR;
317         if (passive && commands != services)
318                 terminate (STATE_UNKNOWN, "%s: In passive mode, you must provide a service name for each command.\n", progname);
320         if (passive && host_shortname == NULL)
321                 terminate (STATE_UNKNOWN, "%s: In passive mode, you must provide the host short name from the nagios configs.\n", progname);
323         return OK;
330 void
331 print_help (const char *cmd)
333         print_revision (cmd, "$Revision$");
335         printf
336                 ("Copyright (c) 1999    Karl DeBisschop (kdebisschop@alum.mit.edu)\n\n"
337                  "This plugin will execute a command on a remote host using SSH\n\n");
339         print_usage ();
341         printf
342                 ("\nOptions:\n"
343                  "-H, --hostname=HOST\n"
344                  "   name or IP address of remote host\n"
345                  "-C, --command='COMMAND STRING'\n"
346                  "   command to execute on the remote machine\n"
347                  "-f tells ssh to fork rather than create a tty\n"
348                  "-t, --timeout=INTEGER\n"
349                  "   specify timeout (default: %d seconds) [optional]\n"
350                  "-p, --port=PORT\n"
351                  "   port to connect to on remote system [optional]\n"
352          "-l, --logname=USERNAME\n"
353                  "   SSH user name on remote host [optional]\n"
354                  "-i, --identity=KEYFILE\n"
355                  "   identity of an authorized key [optional]\n"
356                  "-O, --output=FILE\n"
357                  "   external command file for nagios [optional]\n"
358                  "-s, --services=LIST\n"
359                  "   list of nagios service names, separated by ':' [optional]\n"
360                  "-n, --name=NAME\n"
361                  "   short name of host in nagios configuration [optional]\n"
362                  "-4, --use-ipv4\n"
363                  "   tell ssh to use IPv4\n"
364                  "-6, --use-ipv6\n"
365                  "   tell ssh to use IPv6\n"
366                  "\n"
367                  "The most common mode of use is to refer to a local identity file with\n"
368                  "the '-i' option. In this mode, the identity pair should have a null\n"
369                  "passphrase and the public key should be listed in the authorized_keys\n"
370                  "file of the remote host. Usually the key will be restricted to running\n"
371                  "only one command on the remote server. If the remote SSH server tracks\n"
372                  "invocation agruments, the one remote program may be an agent that can\n"
373                  "execute additional commands as proxy\n"
374                  "\n"
375                  "To use passive mode, provide multiple '-C' options, and provide\n"
376                  "all of -O, -s, and -n options (servicelist order must match '-C'\n"
377                  "options)\n", DEFAULT_SOCKET_TIMEOUT);
384 void
385 print_usage (void)
387         printf
388                 ("Usage:\n"
389                  "check_by_ssh [-f46] [-t timeout] [-i identity] [-l user] -H <host> -C <command>\n"
390                  "             [-n name] [-s servicelist] [-O outputfile] [-p port]\n"
391                  "check_by_ssh  -V prints version info\n"
392                  "check_by_ssh  -h prints more detailed help\n");