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$
19 *****************************************************************************/
21 const char *progname = "check_by_ssh";
22 const char *revision = "$Revision$";
23 const char *copyright = "2000-2003";
24 const char *email = "nagiosplug-devel@lists.sourceforge.net";
26 #include "common.h"
27 #include "netutils.h"
28 #include "utils.h"
29 #include "popen.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_lines = 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 input_buffer[MAX_INPUT_BUFFER];
53 char *result_text;
54 char *status_text;
55 char *output;
56 char *eol = NULL;
57 int cresult;
58 int result = STATE_UNKNOWN;
59 time_t local_time;
60 FILE *fp = NULL;
62 remotecmd = strdup ("");
63 comm = strdup (SSH_COMMAND);
64 result_text = strdup ("");
66 setlocale (LC_ALL, "");
67 bindtextdomain (PACKAGE, LOCALEDIR);
68 textdomain (PACKAGE);
70 /* process arguments */
71 if (process_arguments (argc, argv) == ERROR)
72 usage (_("check_by_ssh: could not parse arguments\n"));
75 /* Set signal handling and alarm timeout */
76 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
77 printf (_("Cannot catch SIGALRM"));
78 return STATE_UNKNOWN;
79 }
80 alarm (timeout_interval);
83 /* run the command */
85 if (verbose)
86 printf ("%s\n", comm);
88 child_process = spopen (comm);
90 if (child_process == NULL) {
91 printf (_("Could not open pipe: %s\n"), comm);
92 return STATE_UNKNOWN;
93 }
96 /* open STDERR for spopen */
97 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
98 if (child_stderr == NULL) {
99 printf (_("Could not open stderr for %s\n"), SSH_COMMAND);
100 }
103 /* build up results from remote command in result_text */
104 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process))
105 asprintf (&result_text, "%s%s", result_text, input_buffer);
107 /* WARNING if output found on stderr */
108 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
109 if (skip_lines > 0) {
110 if (input_buffer[strlen(input_buffer)-1] == '\n') {
111 skip_lines--;
112 }
113 } else {
114 printf ("%s", input_buffer);
115 result = STATE_WARNING;
116 }
117 }
118 (void) fclose (child_stderr);
119 if (result == STATE_WARNING)
120 return result;
123 /* close the pipe */
124 result = spclose (child_process);
127 /* process output */
128 if (passive) {
130 if (!(fp = fopen (outputfile, "a"))) {
131 printf (_("SSH WARNING: could not open %s\n"), outputfile);
132 exit (STATE_UNKNOWN);
133 }
135 local_time = time (NULL);
136 commands = 0;
137 while (result_text && strlen(result_text) > 0) {
138 status_text = strstr (result_text, "STATUS CODE: ");
139 if (status_text == NULL) {
140 printf ("%s", result_text);
141 return result;
142 }
143 asprintf (&output, "%s", result_text);
144 result_text = strnl (status_text);
145 eol = strpbrk (output, "\r\n");
146 if (eol != NULL)
147 eol[0] = 0;
148 if (service[commands] && status_text
149 && sscanf (status_text, "STATUS CODE: %d", &cresult) == 1) {
150 fprintf (fp, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n",
151 (int) local_time, host_shortname, service[commands++], cresult,
152 output);
153 }
154 }
156 }
159 /* print the first line from the remote command */
160 else {
161 eol = strpbrk (result_text, "\r\n");
162 if (eol)
163 eol[0] = 0;
164 printf ("%s\n", result_text);
165 }
168 /* return error status from remote command */
169 return result;
170 }
172 /* process command-line arguments */
173 int
174 process_arguments (int argc, char **argv)
175 {
176 int c;
177 char *p1, *p2;
179 int option = 0;
180 static struct option longopts[] = {
181 {"version", no_argument, 0, 'V'},
182 {"help", no_argument, 0, 'h'},
183 {"verbose", no_argument, 0, 'v'},
184 {"fork", no_argument, 0, 'f'},
185 {"timeout", required_argument, 0, 't'},
186 {"host", required_argument, 0, 'H'},
187 {"port", required_argument,0,'p'},
188 {"output", required_argument, 0, 'O'},
189 {"name", required_argument, 0, 'n'},
190 {"services", required_argument, 0, 's'},
191 {"identity", required_argument, 0, 'i'},
192 {"user", required_argument, 0, 'u'},
193 {"logname", required_argument, 0, 'l'},
194 {"command", required_argument, 0, 'C'},
195 {"skip", required_argument, 0, 'S'},
196 {"proto1", no_argument, 0, '1'},
197 {"proto2", no_argument, 0, '2'},
198 {"use-ipv4", no_argument, 0, '4'},
199 {"use-ipv6", no_argument, 0, '6'},
200 {0, 0, 0, 0}
201 };
203 if (argc < 2)
204 return ERROR;
206 for (c = 1; c < argc; c++)
207 if (strcmp ("-to", argv[c]) == 0)
208 strcpy (argv[c], "-t");
210 while (1) {
211 c = getopt_long (argc, argv, "Vvh1246ft:H:O:p:i:u:l:C:S:n:s:", longopts,
212 &option);
214 if (c == -1 || c == EOF)
215 break;
217 switch (c) {
218 case '?': /* help */
219 printf (_("%s: Unknown argument: %s\n\n"), progname, optarg);
220 print_usage ();
221 exit (STATE_UNKNOWN);
222 case 'V': /* version */
223 print_revision (progname, revision);
224 exit (STATE_OK);
225 case 'h': /* help */
226 print_help ();
227 exit (STATE_OK);
228 case 'v': /* help */
229 verbose = TRUE;
230 break;
231 case 't': /* timeout period */
232 if (!is_integer (optarg))
233 usage2 (_("Timeout interval must be a positive integer"), optarg);
234 else
235 timeout_interval = atoi (optarg);
236 break;
237 case 'H': /* host */
238 if (!is_host (optarg))
239 usage2 (_("Invalid hostname/address"), optarg);
240 hostname = optarg;
241 break;
242 case 'p': /* port number */
243 if (!is_integer (optarg))
244 usage2 (_("Port must be a positive 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) * sizeof(char *));
253 p1 = optarg;
254 while ((p2 = index (p1, ':'))) {
255 *p2 = '\0';
256 asprintf (&service[services-1], "%s", p1);
257 service = realloc (service, (++services) * sizeof(char *));
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 '1': /* Pass these switches directly to ssh */
272 case '2': /* 1 to force version 1, 2 to force version 2 */
273 case '4': /* -4 for IPv4 */
274 case '6': /* -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 break;
284 case 'S': /* Skip n lines in the output to ignore system banner */
285 if (!is_integer (optarg))
286 usage2 (_("skip lines must be an integer"), optarg);
287 else
288 skip_lines = atoi (optarg);
289 break;
290 }
291 }
293 c = optind;
294 if (hostname == NULL) {
295 if (c <= argc) {
296 die (STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname);
297 } else if (!is_host (argv[c]))
298 die (STATE_UNKNOWN, "%s: %s %s\n", progname, _("Invalid hostname/address"), argv[c]);
299 hostname = argv[c++];
300 }
302 if (strlen(remotecmd) == 0) {
303 for (; c < argc; c++)
304 if (strlen(remotecmd) > 0)
305 asprintf (&remotecmd, "%s %s", remotecmd, argv[c]);
306 else
307 asprintf (&remotecmd, "%s", argv[c]);
308 }
310 if (commands > 1)
311 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
313 if (remotecmd == NULL || strlen (remotecmd) <= 1)
314 usage (_("No remotecmd\n"));
316 asprintf (&comm, "%s %s '%s'", comm, hostname, remotecmd);
318 return validate_arguments ();
319 }
323 int
324 validate_arguments (void)
325 {
326 if (remotecmd == NULL || hostname == NULL)
327 return ERROR;
329 if (passive && commands != services)
330 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname);
332 if (passive && host_shortname == NULL)
333 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the nagios configs.\n"), progname);
335 return OK;
336 }
339 void
340 print_help (void)
341 {
342 print_revision (progname, revision);
344 printf (_("Copyright (c) 1999 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n"));
345 printf (_(COPYRIGHT), copyright, email);
347 printf (_("This plugin uses SSH to execute commands on a remote host\n\n"));
349 print_usage ();
351 printf (_(UT_HELP_VRSN));
353 printf (_(UT_HOST_PORT), 'p', "none");
355 printf (_(UT_IPv46));
357 printf (_("\
358 -1, --proto1\n\
359 tell ssh to use Protocol 1\n\
360 -2, --proto2\n\
361 tell ssh to use Protocol 2\n\
362 -S, --skiplines=n\n\
363 Ignore first n lines on STDERR (to suppress a logon banner)\n\
364 -f\n\
365 tells ssh to fork rather than create a tty\n"));
367 printf (_("\
368 -C, --command='COMMAND STRING'\n\
369 command to execute on the remote machine\n\
370 -l, --logname=USERNAME\n\
371 SSH user name on remote host [optional]\n\
372 -i, --identity=KEYFILE\n\
373 identity of an authorized key [optional]\n\
374 -O, --output=FILE\n\
375 external command file for nagios [optional]\n\
376 -s, --services=LIST\n\
377 list of nagios service names, separated by ':' [optional]\n\
378 -n, --name=NAME\n\
379 short name of host in nagios configuration [optional]\n"));
381 printf (_(UT_WARN_CRIT));
383 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
385 printf (_("\n\
386 The most common mode of use is to refer to a local identity file with\n\
387 the '-i' option. In this mode, the identity pair should have a null\n\
388 passphrase and the public key should be listed in the authorized_keys\n\
389 file of the remote host. Usually the key will be restricted to running\n\
390 only one command on the remote server. If the remote SSH server tracks\n\
391 invocation agruments, the one remote program may be an agent that can\n\
392 execute additional commands as proxy\n"));
394 printf (_("\n\
395 To use passive mode, provide multiple '-C' options, and provide\n\
396 all of -O, -s, and -n options (servicelist order must match '-C'\n\
397 options)\n"));
399 printf (_("\n\
400 $ check_by_ssh -H localhost -n lh -s c1:c2:c3 \\\n\
401 -C uptime -C uptime -C uptime -O /tmp/foo\n\
402 $ cat /tmp/foo\n\
403 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days...\n\
404 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days...\n\
405 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c3;0; up 2 days...\n"));
407 printf (_(UT_SUPPORT));
408 }
412 void
413 print_usage (void)
414 {
415 printf (_("\n\
416 Usage: %s [-f46] [-t timeout] [-i identity] [-l user] -H <host> \n\
417 -C <command> [-n name] [-s servicelist] [-O outputfile] [-p port]\n"),
418 progname);
419 printf (_(UT_HLP_VRS), progname, progname);
420 }