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 const char *progname = "check_by_ssh";
26 const char *revision = "$Revision$";
27 const char *copyright = "2000-2003";
28 const char *email = "nagiosplug-devel@lists.sourceforge.net";
30 #include "config.h"
31 #include "common.h"
32 #include "netutils.h"
33 #include "utils.h"
34 #include "popen.h"
36 int process_arguments (int, char **);
37 int validate_arguments (void);
38 void print_help (void);
39 void print_usage (void);
41 void
42 print_help (void)
43 {
44 print_revision (progname, revision);
46 printf (_(COPYRIGHT), copyright, email);
48 printf (_("This plugin uses SSH to execute commands on a remote host\n\n"));
50 print_usage ();
52 printf (_(UT_HELP_VRSN));
54 printf (_(UT_HOST_PORT), 'p', "none");
56 printf (_(UT_IPv46));
58 printf (_("\
59 -1, --proto1\n\
60 tell ssh to use Protocol 1\n\
61 -2, --proto2\n\
62 tell ssh to use Protocol 2\n\
63 -f\n\
64 tells ssh to fork rather than create a tty\n"));
66 printf (_("\
67 -C, --command='COMMAND STRING'\n\
68 command to execute on the remote machine\n\
69 -l, --logname=USERNAME\n\
70 SSH user name on remote host [optional]\n\
71 -i, --identity=KEYFILE\n\
72 identity of an authorized key [optional]\n\
73 -O, --output=FILE\n\
74 external command file for nagios [optional]\n\
75 -s, --services=LIST\n\
76 list of nagios service names, separated by ':' [optional]\n\
77 -n, --name=NAME\n\
78 short name of host in nagios configuration [optional]\n"));
80 printf (_(UT_WARN_CRIT));
82 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
84 printf (_("\n\
85 The most common mode of use is to refer to a local identity file with\n\
86 the '-i' option. In this mode, the identity pair should have a null\n\
87 passphrase and the public key should be listed in the authorized_keys\n\
88 file of the remote host. Usually the key will be restricted to running\n\
89 only one command on the remote server. If the remote SSH server tracks\n\
90 invocation agruments, the one remote program may be an agent that can\n\
91 execute additional commands as proxy\n"));
93 printf (_("\n\
94 To use passive mode, provide multiple '-C' options, and provide\n\
95 all of -O, -s, and -n options (servicelist order must match '-C'\n\
96 options)\n"));
97 }
103 void
104 print_usage (void)
105 {
106 printf (_("Usage:\n\
107 check_by_ssh [-f46] [-t timeout] [-i identity] [-l user] -H <host> -C <command>\n\
108 [-n name] [-s servicelist] [-O outputfile] [-p port]\n\
109 check_by_ssh -V prints version info\n\
110 check_by_ssh -h prints more detailed help\n"));
111 }
112 \f
114 int commands = 0;
115 int services = 0;
116 char *remotecmd = "";
117 char *comm = SSH_COMMAND;
118 char *hostname = NULL;
119 char *outputfile = NULL;
120 char *host_shortname = NULL;
121 char **service;
122 int passive = FALSE;
123 int verbose = FALSE;
126 int
127 main (int argc, char **argv)
128 {
130 char input_buffer[MAX_INPUT_BUFFER];
131 char *result_text = "";
132 char *status_text;
133 char *output = "";
134 char *eol = NULL;
135 int cresult;
136 int result = STATE_UNKNOWN;
137 time_t local_time;
138 FILE *fp = NULL;
141 /* process arguments */
142 if (process_arguments (argc, argv) == ERROR)
143 usage (_("Could not parse arguments\n"));
146 /* Set signal handling and alarm timeout */
147 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
148 printf ("Cannot catch SIGALRM");
149 return STATE_UNKNOWN;
150 }
151 alarm (timeout_interval);
154 /* run the command */
156 if (verbose)
157 printf ("%s\n", comm);
159 child_process = spopen (comm);
161 if (child_process == NULL) {
162 printf (_("Unable to open pipe: %s"), comm);
163 return STATE_UNKNOWN;
164 }
167 /* open STDERR for spopen */
168 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
169 if (child_stderr == NULL) {
170 printf (_("Could not open stderr for %s\n"), SSH_COMMAND);
171 }
174 /* get results from remote command */
175 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process))
176 asprintf (&result_text, "%s%s", result_text, input_buffer);
179 /* WARNING if output found on stderr */
180 if (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
181 printf ("%s\n", input_buffer);
182 return STATE_WARNING;
183 }
184 (void) fclose (child_stderr);
187 /* close the pipe */
188 result = spclose (child_process);
191 /* process output */
192 if (passive) {
194 if (!(fp = fopen (outputfile, "a"))) {
195 printf (_("SSH WARNING: could not open %s\n"), outputfile);
196 exit (STATE_UNKNOWN);
197 }
199 time (&local_time);
200 commands = 0;
201 while (result_text && strlen(result_text) > 0) {
202 status_text = strstr (result_text, _("STATUS CODE: "));
203 if (status_text == NULL) {
204 printf ("%s", result_text);
205 return result;
206 }
207 asprintf (&output, "%s", result_text);
208 result_text = strnl (status_text);
209 eol = strpbrk (output, "\r\n");
210 if (eol != NULL)
211 eol[0] = 0;
212 if (service[commands] && status_text
213 && sscanf (status_text, _("STATUS CODE: %d"), &cresult) == 1) {
214 fprintf (fp, _("[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n"),
215 (int) local_time, host_shortname, service[commands++], cresult,
216 output);
217 }
218 }
220 }
222 /* print the first line from the remote command */
223 else {
224 eol = strpbrk (result_text, "\r\n");
225 if (eol)
226 eol[0] = 0;
227 printf ("%s\n", result_text);
228 }
230 /* return error status from remote command */
231 return result;
232 }
238 /* process command-line arguments */
239 int
240 process_arguments (int argc, char **argv)
241 {
242 int c;
243 char *p1, *p2;
245 int option_index = 0;
246 static struct option long_options[] = {
247 {"version", no_argument, 0, 'V'},
248 {"help", no_argument, 0, 'h'},
249 {"verbose", no_argument, 0, 'v'},
250 {"fork", no_argument, 0, 'f'},
251 {"timeout", required_argument, 0, 't'},
252 {"host", required_argument, 0, 'H'},
253 {"port", required_argument,0,'p'},
254 {"output", required_argument, 0, 'O'},
255 {"name", required_argument, 0, 'n'},
256 {"services", required_argument, 0, 's'},
257 {"identity", required_argument, 0, 'i'},
258 {"user", required_argument, 0, 'u'},
259 {"logname", required_argument, 0, 'l'},
260 {"command", required_argument, 0, 'C'},
261 {"proto1", no_argument, 0, '1'},
262 {"proto2", no_argument, 0, '2'},
263 {"use-ipv4", no_argument, 0, '4'},
264 {"use-ipv6", no_argument, 0, '6'},
265 {0, 0, 0, 0}
266 };
268 if (argc < 2)
269 return ERROR;
271 for (c = 1; c < argc; c++)
272 if (strcmp ("-to", argv[c]) == 0)
273 strcpy (argv[c], "-t");
275 while (1) {
276 c = getopt_long (argc, argv, "Vvh1246ft:H:O:p:i:u:l:C:n:s:", long_options,
277 &option_index);
279 if (c == -1 || c == EOF)
280 break;
282 switch (c) {
283 case '?': /* help */
284 print_usage ();
285 exit (STATE_UNKNOWN);
286 case 'V': /* version */
287 print_revision (progname, "$Revision$");
288 exit (STATE_OK);
289 case 'h': /* help */
290 print_help ();
291 exit (STATE_OK);
292 case 'v': /* help */
293 verbose = TRUE;
294 break;
295 case 't': /* timeout period */
296 if (!is_integer (optarg))
297 usage2 (_("timeout interval must be an integer"), optarg);
298 timeout_interval = atoi (optarg);
299 break;
300 case 'H': /* host */
301 if (!is_host (optarg))
302 usage2 (_("invalid host name"), optarg);
303 hostname = optarg;
304 break;
305 case 'p': /* port number */
306 if (!is_integer (optarg))
307 usage2 (_("port must be an integer"), optarg);
308 asprintf (&comm,"%s -p %s", comm, optarg);
309 break;
310 case 'O': /* output file */
311 outputfile = optarg;
312 passive = TRUE;
313 break;
314 case 's': /* description of service to check */
315 service = realloc (service, (++services) * sizeof(char *));
316 p1 = optarg;
317 while ((p2 = index (p1, ':'))) {
318 *p2 = '\0';
319 asprintf (&service[services-1], "%s", p1);
320 service = realloc (service, (++services) * sizeof(char *));
321 p1 = p2 + 1;
322 }
323 asprintf (&service[services-1], "%s", p1);
324 break;
325 case 'n': /* short name of host in nagios configuration */
326 host_shortname = optarg;
327 break;
328 case 'u':
329 c = 'l';
330 case 'l': /* login name */
331 case 'i': /* identity */
332 asprintf (&comm, "%s -%c %s", comm, c, optarg);
333 break;
334 case '1': /* Pass these switches directly to ssh */
335 case '2': /* 1 to force version 1, 2 to force version 2 */
336 case '4': /* -4 for IPv4 */
337 case '6': /* -6 for IPv6 */
338 case 'f': /* fork to background */
339 asprintf (&comm, "%s -%c", comm, c);
340 break;
341 case 'C': /* Command for remote machine */
342 commands++;
343 if (commands > 1)
344 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
345 asprintf (&remotecmd, "%s%s", remotecmd, optarg);
346 }
347 }
349 c = optind;
350 if (hostname == NULL) {
351 if (c <= argc) {
352 die (STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname);
353 } else if (!is_host (argv[c]))
354 die (STATE_UNKNOWN, _("%s: Invalid host name %s\n"), progname, argv[c]);
355 hostname = argv[c++];
356 }
358 if (strlen(remotecmd) == 0) {
359 for (; c < argc; c++)
360 if (strlen(remotecmd) > 0)
361 asprintf (&remotecmd, "%s %s", remotecmd, argv[c]);
362 else
363 asprintf (&remotecmd, "%s", argv[c]);
364 }
366 if (commands > 1)
367 remotecmd = strscat (remotecmd, ";echo STATUS CODE: $?;");
369 if (remotecmd == NULL || strlen (remotecmd) <= 1)
370 usage (_("No remotecmd\n"));
372 asprintf (&comm, "%s %s '%s'", comm, hostname, remotecmd);
374 return validate_arguments ();
375 }
381 int
382 validate_arguments (void)
383 {
384 if (remotecmd == NULL || hostname == NULL)
385 return ERROR;
387 if (passive && commands != services)
388 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname);
390 if (passive && host_shortname == NULL)
391 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the nagios configs.\n"), progname);
393 return OK;
394 }