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-2004";
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 usage4 (_("Could not parse arguments"));
74 /* Set signal handling and alarm timeout */
75 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
76 usage4 (_("Cannot catch SIGALRM"));
77 }
78 alarm (timeout_interval);
80 /* run the command */
82 if (verbose)
83 printf ("%s\n", comm);
85 child_process = spopen (comm);
87 if (child_process == NULL) {
88 printf (_("Could not open pipe: %s\n"), comm);
89 return STATE_UNKNOWN;
90 }
93 /* open STDERR for spopen */
94 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
95 if (child_stderr == NULL) {
96 printf (_("Could not open stderr for %s\n"), SSH_COMMAND);
97 }
100 /* build up results from remote command in result_text */
101 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process))
102 asprintf (&result_text, "%s%s", result_text, input_buffer);
104 /* WARNING if output found on stderr */
105 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
106 if (skip_lines > 0) {
107 if (input_buffer[strlen(input_buffer)-1] == '\n') {
108 skip_lines--;
109 }
110 } else {
111 printf ("%s", input_buffer);
112 result = STATE_WARNING;
113 }
114 }
115 (void) fclose (child_stderr);
116 if (result == STATE_WARNING)
117 return result;
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 local_time = time (NULL);
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 }
156 /* print the first line from the remote command */
157 else {
158 eol = strpbrk (result_text, "\r\n");
159 if (eol)
160 eol[0] = 0;
161 printf ("%s\n", result_text);
162 }
165 /* return error status from remote command */
166 return result;
167 }
169 /* process command-line arguments */
170 int
171 process_arguments (int argc, char **argv)
172 {
173 int c;
174 char *p1, *p2;
176 int option = 0;
177 static struct option longopts[] = {
178 {"version", no_argument, 0, 'V'},
179 {"help", no_argument, 0, 'h'},
180 {"verbose", no_argument, 0, 'v'},
181 {"fork", no_argument, 0, 'f'},
182 {"timeout", required_argument, 0, 't'},
183 {"host", required_argument, 0, 'H'},
184 {"port", required_argument,0,'p'},
185 {"output", required_argument, 0, 'O'},
186 {"name", required_argument, 0, 'n'},
187 {"services", required_argument, 0, 's'},
188 {"identity", required_argument, 0, 'i'},
189 {"user", required_argument, 0, 'u'},
190 {"logname", required_argument, 0, 'l'},
191 {"command", required_argument, 0, 'C'},
192 {"skip", required_argument, 0, 'S'},
193 {"proto1", no_argument, 0, '1'},
194 {"proto2", no_argument, 0, '2'},
195 {"use-ipv4", no_argument, 0, '4'},
196 {"use-ipv6", no_argument, 0, '6'},
197 {0, 0, 0, 0}
198 };
200 if (argc < 2)
201 return ERROR;
203 for (c = 1; c < argc; c++)
204 if (strcmp ("-to", argv[c]) == 0)
205 strcpy (argv[c], "-t");
207 while (1) {
208 c = getopt_long (argc, argv, "Vvh1246ft:H:O:p:i:u:l:C:S:n:s:", longopts,
209 &option);
211 if (c == -1 || c == EOF)
212 break;
214 switch (c) {
215 case '?': /* help */
216 usage2 (_("Unknown argument"), optarg);
217 case 'V': /* version */
218 print_revision (progname, revision);
219 exit (STATE_OK);
220 case 'h': /* help */
221 print_help ();
222 exit (STATE_OK);
223 case 'v': /* help */
224 verbose = TRUE;
225 break;
226 case 't': /* timeout period */
227 if (!is_integer (optarg))
228 usage2 (_("Timeout interval must be a positive integer"), optarg);
229 else
230 timeout_interval = atoi (optarg);
231 break;
232 case 'H': /* host */
233 if (!is_host (optarg))
234 usage2 (_("Invalid hostname/address"), optarg);
235 hostname = optarg;
236 break;
237 case 'p': /* port number */
238 if (!is_integer (optarg))
239 usage2 (_("Port must be a positive integer"), optarg);
240 asprintf (&comm,"%s -p %s", comm, optarg);
241 break;
242 case 'O': /* output file */
243 outputfile = optarg;
244 passive = TRUE;
245 break;
246 case 's': /* description of service to check */
247 service = realloc (service, (++services) * sizeof(char *));
248 p1 = optarg;
249 while ((p2 = index (p1, ':'))) {
250 *p2 = '\0';
251 asprintf (&service[services-1], "%s", p1);
252 service = realloc (service, (++services) * sizeof(char *));
253 p1 = p2 + 1;
254 }
255 asprintf (&service[services-1], "%s", p1);
256 break;
257 case 'n': /* short name of host in nagios configuration */
258 host_shortname = optarg;
259 break;
260 case 'u':
261 c = 'l';
262 case 'l': /* login name */
263 case 'i': /* identity */
264 asprintf (&comm, "%s -%c %s", comm, c, optarg);
265 break;
266 case '1': /* Pass these switches directly to ssh */
267 case '2': /* 1 to force version 1, 2 to force version 2 */
268 case '4': /* -4 for IPv4 */
269 case '6': /* -6 for IPv6 */
270 case 'f': /* fork to background */
271 asprintf (&comm, "%s -%c", comm, c);
272 break;
273 case 'C': /* Command for remote machine */
274 commands++;
275 if (commands > 1)
276 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
277 asprintf (&remotecmd, "%s%s", remotecmd, optarg);
278 break;
279 case 'S': /* Skip n lines in the output to ignore system banner */
280 if (!is_integer (optarg))
281 usage2 (_("skip lines must be an integer"), optarg);
282 else
283 skip_lines = atoi (optarg);
284 break;
285 }
286 }
288 c = optind;
289 if (hostname == NULL) {
290 if (c <= argc) {
291 die (STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname);
292 } else if (!is_host (argv[c]))
293 die (STATE_UNKNOWN, "%s: %s %s\n", progname, _("Invalid hostname/address"), argv[c]);
294 hostname = argv[c++];
295 }
297 if (strlen(remotecmd) == 0) {
298 for (; c < argc; c++)
299 if (strlen(remotecmd) > 0)
300 asprintf (&remotecmd, "%s %s", remotecmd, argv[c]);
301 else
302 asprintf (&remotecmd, "%s", argv[c]);
303 }
305 if (commands > 1)
306 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
308 if (remotecmd == NULL || strlen (remotecmd) <= 1)
309 usage4 (_("No remotecmd"));
311 asprintf (&comm, "%s %s '%s'", comm, hostname, remotecmd);
313 return validate_arguments ();
314 }
318 int
319 validate_arguments (void)
320 {
321 if (remotecmd == NULL || hostname == NULL)
322 return ERROR;
324 if (passive && commands != services)
325 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname);
327 if (passive && host_shortname == NULL)
328 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the nagios configs.\n"), progname);
330 return OK;
331 }
334 void
335 print_help (void)
336 {
337 print_revision (progname, revision);
339 printf ("Copyright (c) 1999 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n");
340 printf (COPYRIGHT, copyright, email);
342 printf (_("This plugin uses SSH to execute commands on a remote host\n\n"));
344 print_usage ();
346 printf (_(UT_HELP_VRSN));
348 printf (_(UT_HOST_PORT), 'p', "none");
350 printf (_(UT_IPv46));
352 printf (_("\
353 -1, --proto1\n\
354 tell ssh to use Protocol 1\n\
355 -2, --proto2\n\
356 tell ssh to use Protocol 2\n\
357 -S, --skiplines=n\n\
358 Ignore first n lines on STDERR (to suppress a logon banner)\n\
359 -f\n\
360 tells ssh to fork rather than create a tty\n"));
362 printf (_("\
363 -C, --command='COMMAND STRING'\n\
364 command to execute on the remote machine\n\
365 -l, --logname=USERNAME\n\
366 SSH user name on remote host [optional]\n\
367 -i, --identity=KEYFILE\n\
368 identity of an authorized key [optional]\n\
369 -O, --output=FILE\n\
370 external command file for nagios [optional]\n\
371 -s, --services=LIST\n\
372 list of nagios service names, separated by ':' [optional]\n\
373 -n, --name=NAME\n\
374 short name of host in nagios configuration [optional]\n"));
376 printf (_(UT_WARN_CRIT));
378 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
380 printf (_("\n\
381 The most common mode of use is to refer to a local identity file with\n\
382 the '-i' option. In this mode, the identity pair should have a null\n\
383 passphrase and the public key should be listed in the authorized_keys\n\
384 file of the remote host. Usually the key will be restricted to running\n\
385 only one command on the remote server. If the remote SSH server tracks\n\
386 invocation agruments, the one remote program may be an agent that can\n\
387 execute additional commands as proxy\n"));
389 printf (_("\n\
390 To use passive mode, provide multiple '-C' options, and provide\n\
391 all of -O, -s, and -n options (servicelist order must match '-C'\n\
392 options)\n"));
394 printf ("\n\
395 $ check_by_ssh -H localhost -n lh -s c1:c2:c3 \\\n\
396 -C uptime -C uptime -C uptime -O /tmp/foo\n\
397 $ cat /tmp/foo\n\
398 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days...\n\
399 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days...\n\
400 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c3;0; up 2 days...\n");
402 printf (_(UT_SUPPORT));
403 }
407 void
408 print_usage (void)
409 {
410 printf ("\n\
411 Usage: %s [-f46] [-t timeout] [-i identity] [-l user] -H <host> -C <command>\n\
412 [-n name] [-s servicelist] [-O outputfile] [-p port]\n", progname);
413 }