93f1ead53173db1d348db022c74b393f0de4d276
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 *****************************************************************************/
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;
165 }
171 /* process command-line arguments */
172 int
173 process_arguments (int argc, char **argv)
174 {
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, "+?Vvhft46H:O:p:i:u:l:C:n:s:", long_options,
213 &option_index);
214 #else
215 c = getopt (argc, argv, "+?Vvhft46H: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 ();
307 }
313 int
314 validate_arguments (void)
315 {
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;
326 }
332 void
333 print_help (char *cmd)
334 {
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);
380 }
386 void
387 print_usage (void)
388 {
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");
395 }