Code

initial merging of ae's np_runcmd code into selected plugins.
[nagiosplug.git] / plugins / check_by_ssh.c
1 /******************************************************************************
3  The Nagios Plugins are free software; you can redistribute them
4  and/or modify them under the terms of the GNU General Public
5  License as published by the Free Software Foundation; either
6  version 2 of the License, or (at your option) any later version.
8  This program is distributed in the hope that it will be useful, but
9  WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  General Public License for more details.
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  $Id$
18  
19  *****************************************************************************/
20  
21 const char *progname = "check_by_ssh";
22 const char *revision = "$Revision$";
23 const char *copyright = "2000-2004";
24 const char *email = "nagiosplug-devel@lists.sourceforge.net";
26 #include "common.h"
27 #include "netutils.h"
28 #include "utils.h"
29 #include "runcmd.h"
31 int process_arguments (int, char **);
32 int validate_arguments (void);
33 void print_help (void);
34 void print_usage (void);
36 int commands = 0;
37 int services = 0;
38 int skip = 0;
39 char *remotecmd = NULL;
40 char *comm = NULL;
41 char *hostname = NULL;
42 char *outputfile = NULL;
43 char *host_shortname = NULL;
44 char **service;
45 int passive = FALSE;
46 int verbose = FALSE;
48 int
49 main (int argc, char **argv)
50 {
52         char *status_text;
53         int cresult;
54         int result = STATE_UNKNOWN;
55         int i;
56         time_t local_time;
57         FILE *fp = NULL;
58         struct output chld_out, chld_err;
60         remotecmd = "";
61         comm = strdup (SSH_COMMAND);
63         setlocale (LC_ALL, "");
64         bindtextdomain (PACKAGE, LOCALEDIR);
65         textdomain (PACKAGE);
67         /* process arguments */
68         if (process_arguments (argc, argv) == ERROR)
69                 usage_va(_("Could not parse arguments"));
71         /* Set signal handling and alarm timeout */
72         if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
73                 usage_va(_("Cannot catch SIGALRM"));
74         }
75         alarm (timeout_interval);
77         /* run the command */
78         if (verbose)
79                 printf ("%s\n", comm);
81         result = np_runcmd(comm, &chld_out, &chld_err, 0);
82         /* UNKNOWN if output found on stderr */
83         if(chld_err.buflen) {
84                 printf(_("Remote command execution failed: %s\n"),
85                            chld_err.buflen ? chld_err.buf : _("Unknown error"));
86                 return STATE_UNKNOWN;
87         }
89         /* this is simple if we're not supposed to be passive.
90          * Wrap up quickly and keep the tricks below */
91         if(!passive) {
92                 printf ("%s\n", skip < chld_out.lines ? chld_out.line[skip] : chld_out.buf);
93                 return result;  /* return error status from remote command */
94         }
97         /*
98          * Passive mode
99          */
101         /* process output */
102         if (!(fp = fopen (outputfile, "a"))) {
103                 printf (_("SSH WARNING: could not open %s\n"), outputfile);
104                 exit (STATE_UNKNOWN);
105         }
107         local_time = time (NULL);
108         commands = 0;
109         for(i = skip; chld_out.line[i]; i++) {
110                 status_text = strstr (chld_out.line[i], "STATUS CODE: ");
111                 if (status_text == NULL) {
112                         printf ("%s", chld_out.line[i]);
113                         return result;
114                 }
115                 if (service[commands] && status_text
116                         && sscanf (status_text, "STATUS CODE: %d", &cresult) == 1)
117                 {
118                         fprintf (fp, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n",
119                                  (int) local_time, host_shortname, service[commands++],
120                                  cresult, chld_out.line[i]);
121                 }
122         }
123         
124         /* force an OK state */
125         return result;
128 /* process command-line arguments */
129 int
130 process_arguments (int argc, char **argv)
132         int c;
133         char *p1, *p2;
135         int option = 0;
136         static struct option longopts[] = {
137                 {"version", no_argument, 0, 'V'},
138                 {"help", no_argument, 0, 'h'},
139                 {"verbose", no_argument, 0, 'v'},
140                 {"fork", no_argument, 0, 'f'},
141                 {"timeout", required_argument, 0, 't'},
142                 {"host", required_argument, 0, 'H'},
143                 {"port", required_argument,0,'p'},
144                 {"output", required_argument, 0, 'O'},
145                 {"name", required_argument, 0, 'n'},
146                 {"services", required_argument, 0, 's'},
147                 {"identity", required_argument, 0, 'i'},
148                 {"user", required_argument, 0, 'u'},
149                 {"logname", required_argument, 0, 'l'},
150                 {"command", required_argument, 0, 'C'},
151                 {"skip", required_argument, 0, 'S'},
152                 {"proto1", no_argument, 0, '1'},
153                 {"proto2", no_argument, 0, '2'},
154                 {"use-ipv4", no_argument, 0, '4'},
155                 {"use-ipv6", no_argument, 0, '6'},
156                 {0, 0, 0, 0}
157         };
159         if (argc < 2)
160                 return ERROR;
162         for (c = 1; c < argc; c++)
163                 if (strcmp ("-to", argv[c]) == 0)
164                         strcpy (argv[c], "-t");
166         while (1) {
167                 c = getopt_long (argc, argv, "Vvh1246ft:H:O:p:i:u:l:C:S:n:s:", longopts,
168                                  &option);
170                 if (c == -1 || c == EOF)
171                         break;
173                 switch (c) {
174                 case 'V':                                                                       /* version */
175                         print_revision (progname, revision);
176                         exit (STATE_OK);
177                 case 'h':                                                                       /* help */
178                         print_help ();
179                         exit (STATE_OK);
180                 case 'v':                                                                       /* help */
181                         verbose = TRUE;
182                         break;
183                 case 't':                                                                       /* timeout period */
184                         if (!is_integer (optarg))
185                                 usage_va(_("Timeout interval must be a positive integer"));
186                         else
187                                 timeout_interval = atoi (optarg);
188                         break;
189                 case 'H':                                                                       /* host */
190                         host_or_die(optarg);
191                         hostname = optarg;
192                         break;
193                 case 'p': /* port number */
194                         if (!is_integer (optarg))
195                                 usage_va(_("Port must be a positive integer"));
196                         asprintf (&comm,"%s -p %s", comm, optarg);
197                         break;
198                 case 'O':                                                                       /* output file */
199                         outputfile = optarg;
200                         passive = TRUE;
201                         break;
202                 case 's':                                                                       /* description of service to check */
203                         p1 = optarg;
204                         service = realloc (service, (++services) * sizeof(char *));
205                         while ((p2 = index (p1, ':'))) {
206                                 *p2 = '\0';
207                                 service[services - 1] = p1;
208                                 service = realloc (service, (++services) * sizeof(char *));
209                                 p1 = p2 + 1;
210                         }
211                         service[services - 1] = p1;
212                         break;
213                 case 'n':                                                                       /* short name of host in nagios configuration */
214                         host_shortname = optarg;
215                         break;
217                 case 'u':
218                         c = 'l';
219                 case 'l':                                                                       /* login name */
220                 case 'i':                                                                       /* identity */
221                         asprintf (&comm, "%s -%c %s", comm, c, optarg);
222                         break;
224                 case '1':                                                                       /* Pass these switches directly to ssh */
225                 case '2':                                                                       /* 1 to force version 1, 2 to force version 2 */
226                 case '4':                                                                       /* -4 for IPv4 */
227                 case '6':                                                               /* -6 for IPv6 */
228                 case 'f':                                                                       /* fork to background */
229                         asprintf (&comm, "%s -%c", comm, c);
230                         break;
231                 case 'C':                                                                       /* Command for remote machine */
232                         commands++;
233                         if (commands > 1)
234                                 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
235                         asprintf (&remotecmd, "%s%s", remotecmd, optarg);
236                         break;
237                 case 'S':                                                                       /* Skip n lines in the output to ignore system banner */
238                         if (!is_integer (optarg))
239                                 usage_va(_("skip lines must be an integer"));
240                         else
241                                 skip = atoi (optarg);
242                         break;
243                 default:                                                                        /* help */
244                         usage_va(_("Unknown argument - %s"), optarg);
245                 }
246         }
248         c = optind;
249         if (hostname == NULL) {
250                 if (c <= argc) {
251                         die (STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname);
252                 }
253                 host_or_die(argv[c]);
254                 hostname = argv[c++];
255         }
257         if (strlen(remotecmd) == 0) {
258                 for (; c < argc; c++)
259                         if (strlen(remotecmd) > 0)
260                                 asprintf (&remotecmd, "%s %s", remotecmd, argv[c]);
261                         else
262                                 asprintf (&remotecmd, "%s", argv[c]);
263         }
265         if (commands > 1)
266                 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
268         if (remotecmd == NULL || strlen (remotecmd) <= 1)
269                 usage_va(_("No remotecmd"));
271         asprintf (&comm, "%s %s '%s'", comm, hostname, remotecmd);
273         return validate_arguments ();
278 int
279 validate_arguments (void)
281         if (remotecmd == NULL || hostname == NULL)
282                 return ERROR;
284         if (passive && commands != services)
285                 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname);
287         if (passive && host_shortname == NULL)
288                 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the nagios configs.\n"), progname);
290         return OK;
294 void
295 print_help (void)
297         print_revision (progname, revision);
299         printf ("Copyright (c) 1999 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n");
300         printf (COPYRIGHT, copyright, email);
302         printf (_("This plugin uses SSH to execute commands on a remote host\n\n"));
304         print_usage ();
306         printf (_(UT_HELP_VRSN));
308         printf (_(UT_HOST_PORT), 'p', "none");
310         printf (_(UT_IPv46));
312         printf (_("\
313  -1, --proto1\n\
314     tell ssh to use Protocol 1\n\
315  -2, --proto2\n\
316     tell ssh to use Protocol 2\n\
317  -S, --skiplines=n\n\
318     Ignore first n lines on STDERR (to suppress a logon banner)\n\
319  -f\n\
320     tells ssh to fork rather than create a tty\n"));
322         printf (_("\
323  -C, --command='COMMAND STRING'\n\
324     command to execute on the remote machine\n\
325  -l, --logname=USERNAME\n\
326     SSH user name on remote host [optional]\n\
327  -i, --identity=KEYFILE\n\
328     identity of an authorized key [optional]\n\
329  -O, --output=FILE\n\
330     external command file for nagios [optional]\n\
331  -s, --services=LIST\n\
332     list of nagios service names, separated by ':' [optional]\n\
333  -n, --name=NAME\n\
334     short name of host in nagios configuration [optional]\n"));
336         printf (_(UT_WARN_CRIT));
338         printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
340         printf (_("\n\
341 The most common mode of use is to refer to a local identity file with\n\
342 the '-i' option. In this mode, the identity pair should have a null\n\
343 passphrase and the public key should be listed in the authorized_keys\n\
344 file of the remote host. Usually the key will be restricted to running\n\
345 only one command on the remote server. If the remote SSH server tracks\n\
346 invocation agruments, the one remote program may be an agent that can\n\
347 execute additional commands as proxy\n"));
349         printf (_("\n\
350 To use passive mode, provide multiple '-C' options, and provide\n\
351 all of -O, -s, and -n options (servicelist order must match '-C'\n\
352 options)\n"));
354         printf ("\n\
355 $ check_by_ssh -H localhost -n lh -s c1:c2:c3 \\\n\
356     -C uptime -C uptime -C uptime -O /tmp/foo\n\
357 $ cat /tmp/foo\n\
358 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days...\n\
359 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days...\n\
360 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c3;0; up 2 days...\n");
362         printf (_(UT_SUPPORT));
367 void
368 print_usage (void)
370         printf ("\n\
371 Usage: %s [-f46] [-t timeout] [-i identity] [-l user] -H <host> -C <command>\n\
372                   [-n name] [-s servicelist] [-O outputfile] [-p port]\n", progname);