9500cb3d464b74f2386579d695d2bad21c7f61f1
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 *****************************************************************************/
19 const char *progname = "check_by_ssh";
20 const char *revision = "$Revision$";
21 const char *copyright = "2000-2003";
22 const char *email = "nagiosplug-devel@lists.sourceforge.net";
24 #include "common.h"
25 #include "netutils.h"
26 #include "utils.h"
27 #include "popen.h"
29 int process_arguments (int, char **);
30 int validate_arguments (void);
31 void print_help (void);
32 void print_usage (void);
34 int commands = 0;
35 int services = 0;
36 int skip_lines = 0;
37 char *remotecmd = NULL;
38 char *comm = NULL;
39 char *hostname = NULL;
40 char *outputfile = NULL;
41 char *host_shortname = NULL;
42 char **service;
43 int passive = FALSE;
44 int verbose = FALSE;
46 int
47 main (int argc, char **argv)
48 {
50 char input_buffer[MAX_INPUT_BUFFER];
51 char *result_text;
52 char *status_text;
53 char *output;
54 char *eol = NULL;
55 int cresult;
56 int result = STATE_UNKNOWN;
57 time_t local_time;
58 FILE *fp = NULL;
60 remotecmd = strdup ("");
61 comm = strdup (SSH_COMMAND);
62 result_text = strdup ("");
64 setlocale (LC_ALL, "");
65 bindtextdomain (PACKAGE, LOCALEDIR);
66 textdomain (PACKAGE);
68 /* process arguments */
69 if (process_arguments (argc, argv) == ERROR)
70 usage (_("Could not parse arguments\n"));
73 /* Set signal handling and alarm timeout */
74 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
75 printf (_("Cannot catch SIGALRM"));
76 return STATE_UNKNOWN;
77 }
78 alarm (timeout_interval);
81 /* run the command */
83 if (verbose)
84 printf ("%s\n", comm);
86 child_process = spopen (comm);
88 if (child_process == NULL) {
89 printf (_("Could not open pipe: %s\n"), comm);
90 return STATE_UNKNOWN;
91 }
94 /* open STDERR for spopen */
95 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
96 if (child_stderr == NULL) {
97 printf (_("Could not open stderr for %s\n"), SSH_COMMAND);
98 }
101 /* build up results from remote command in result_text */
102 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process))
103 asprintf (&result_text, "%s%s", result_text, input_buffer);
105 /* WARNING if output found on stderr */
106 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
107 if (skip_lines > 0) {
108 if (input_buffer[strlen(input_buffer)-1] == '\n') {
109 skip_lines--;
110 }
111 } else {
112 printf ("%s", input_buffer);
113 result = STATE_WARNING;
114 }
115 }
116 (void) fclose (child_stderr);
117 if (result == STATE_WARNING)
118 return result;
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 local_time = time (NULL);
134 commands = 0;
135 while (result_text && strlen(result_text) > 0) {
136 status_text = strstr (result_text, "STATUS CODE: ");
137 if (status_text == NULL) {
138 printf ("%s", result_text);
139 return result;
140 }
141 asprintf (&output, "%s", result_text);
142 result_text = strnl (status_text);
143 eol = strpbrk (output, "\r\n");
144 if (eol != NULL)
145 eol[0] = 0;
146 if (service[commands] && 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, service[commands++], cresult,
150 output);
151 }
152 }
154 }
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);
163 }
166 /* return error status from remote command */
167 return result;
168 }
174 \f
175 /* process command-line arguments */
176 int
177 process_arguments (int argc, char **argv)
178 {
179 int c;
180 char *p1, *p2;
182 int option = 0;
183 static struct option longopts[] = {
184 {"version", no_argument, 0, 'V'},
185 {"help", no_argument, 0, 'h'},
186 {"verbose", no_argument, 0, 'v'},
187 {"fork", no_argument, 0, 'f'},
188 {"timeout", required_argument, 0, 't'},
189 {"host", required_argument, 0, 'H'},
190 {"port", required_argument,0,'p'},
191 {"output", required_argument, 0, 'O'},
192 {"name", required_argument, 0, 'n'},
193 {"services", required_argument, 0, 's'},
194 {"identity", required_argument, 0, 'i'},
195 {"user", required_argument, 0, 'u'},
196 {"logname", required_argument, 0, 'l'},
197 {"command", required_argument, 0, 'C'},
198 {"skip", required_argument, 0, 'S'},
199 {"proto1", no_argument, 0, '1'},
200 {"proto2", no_argument, 0, '2'},
201 {"use-ipv4", no_argument, 0, '4'},
202 {"use-ipv6", no_argument, 0, '6'},
203 {0, 0, 0, 0}
204 };
206 if (argc < 2)
207 return ERROR;
209 for (c = 1; c < argc; c++)
210 if (strcmp ("-to", argv[c]) == 0)
211 strcpy (argv[c], "-t");
213 while (1) {
214 c = getopt_long (argc, argv, "Vvh1246ft:H:O:p:i:u:l:C:S:n:s:", longopts,
215 &option);
217 if (c == -1 || c == EOF)
218 break;
220 switch (c) {
221 case '?': /* help */
222 print_usage ();
223 exit (STATE_UNKNOWN);
224 case 'V': /* version */
225 print_revision (progname, "$Revision$");
226 exit (STATE_OK);
227 case 'h': /* help */
228 print_help ();
229 exit (STATE_OK);
230 case 'v': /* help */
231 verbose = TRUE;
232 break;
233 case 't': /* timeout period */
234 if (!is_integer (optarg))
235 usage2 (_("Timeout interval must be a positive integer"), optarg);
236 else
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) * sizeof(char *));
255 p1 = optarg;
256 while ((p2 = index (p1, ':'))) {
257 *p2 = '\0';
258 asprintf (&service[services-1], "%s", p1);
259 service = realloc (service, (++services) * sizeof(char *));
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 '1': /* Pass these switches directly to ssh */
274 case '2': /* 1 to force version 1, 2 to force version 2 */
275 case '4': /* -4 for IPv4 */
276 case '6': /* -6 for IPv6 */
277 case 'f': /* fork to background */
278 asprintf (&comm, "%s -%c", comm, c);
279 break;
280 case 'C': /* Command for remote machine */
281 commands++;
282 if (commands > 1)
283 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
284 asprintf (&remotecmd, "%s%s", remotecmd, optarg);
285 break;
286 case 'S': /* Skip n lines in the output to ignore system banner */
287 if (!is_integer (optarg))
288 usage2 (_("skip lines must be an integer"), optarg);
289 else
290 skip_lines = atoi (optarg);
291 break;
292 }
293 }
295 c = optind;
296 if (hostname == NULL) {
297 if (c <= argc) {
298 die (STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname);
299 } else if (!is_host (argv[c]))
300 die (STATE_UNKNOWN, "%s: %s %s\n", progname, _("Invalid host name"), argv[c]);
301 hostname = argv[c++];
302 }
304 if (strlen(remotecmd) == 0) {
305 for (; c < argc; c++)
306 if (strlen(remotecmd) > 0)
307 asprintf (&remotecmd, "%s %s", remotecmd, argv[c]);
308 else
309 asprintf (&remotecmd, "%s", argv[c]);
310 }
312 if (commands > 1)
313 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
315 if (remotecmd == NULL || strlen (remotecmd) <= 1)
316 usage (_("No remotecmd\n"));
318 asprintf (&comm, "%s %s '%s'", comm, hostname, remotecmd);
320 return validate_arguments ();
321 }
327 int
328 validate_arguments (void)
329 {
330 if (remotecmd == NULL || hostname == NULL)
331 return ERROR;
333 if (passive && commands != services)
334 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname);
336 if (passive && host_shortname == NULL)
337 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the nagios configs.\n"), progname);
339 return OK;
340 }
346 \f
347 void
348 print_help (void)
349 {
350 print_revision (progname, revision);
352 printf (_("Copyright (c) 1999 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n"));
353 printf (_(COPYRIGHT), copyright, email);
355 printf (_("This plugin uses SSH to execute commands on a remote host\n\n"));
357 print_usage ();
359 printf (_(UT_HELP_VRSN));
361 printf (_(UT_HOST_PORT), 'p', "none");
363 printf (_(UT_IPv46));
365 printf (_("\
366 -1, --proto1\n\
367 tell ssh to use Protocol 1\n\
368 -2, --proto2\n\
369 tell ssh to use Protocol 2\n\
370 -S, --skiplines=n\n\
371 Ignore first n lines on STDERR (to suppress a logon banner)\n\
372 -f\n\
373 tells ssh to fork rather than create a tty\n"));
375 printf (_("\
376 -C, --command='COMMAND STRING'\n\
377 command to execute on the remote machine\n\
378 -l, --logname=USERNAME\n\
379 SSH user name on remote host [optional]\n\
380 -i, --identity=KEYFILE\n\
381 identity of an authorized key [optional]\n\
382 -O, --output=FILE\n\
383 external command file for nagios [optional]\n\
384 -s, --services=LIST\n\
385 list of nagios service names, separated by ':' [optional]\n\
386 -n, --name=NAME\n\
387 short name of host in nagios configuration [optional]\n"));
389 printf (_(UT_WARN_CRIT));
391 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
393 printf (_("\n\
394 The most common mode of use is to refer to a local identity file with\n\
395 the '-i' option. In this mode, the identity pair should have a null\n\
396 passphrase and the public key should be listed in the authorized_keys\n\
397 file of the remote host. Usually the key will be restricted to running\n\
398 only one command on the remote server. If the remote SSH server tracks\n\
399 invocation agruments, the one remote program may be an agent that can\n\
400 execute additional commands as proxy\n"));
402 printf (_("\n\
403 To use passive mode, provide multiple '-C' options, and provide\n\
404 all of -O, -s, and -n options (servicelist order must match '-C'\n\
405 options)\n"));
407 printf (_("\n\
408 $ check_by_ssh -H localhost -n lh -s c1:c2:c3 \\\n\
409 -C uptime -C uptime -C uptime -O /tmp/foo\n\
410 $ cat /tmp/foo\n\
411 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days...\n\
412 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days...\n\
413 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c3;0; up 2 days...\n"));
415 printf (_(UT_SUPPORT));
416 }
422 void
423 print_usage (void)
424 {
425 printf (_("\n\
426 Usage: %s [-f46] [-t timeout] [-i identity] [-l user] -H <host> \n\
427 -C <command> [-n name] [-s servicelist] [-O outputfile] [-p port]\n"),
428 progname);
429 printf (_(UT_HLP_VRS), progname, progname);
430 }