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 printf (_("%s: Unknown argument: %s\n\n"), progname, optarg);
217 print_usage ();
218 exit (STATE_UNKNOWN);
219 case 'V': /* version */
220 print_revision (progname, revision);
221 exit (STATE_OK);
222 case 'h': /* help */
223 print_help ();
224 exit (STATE_OK);
225 case 'v': /* help */
226 verbose = TRUE;
227 break;
228 case 't': /* timeout period */
229 if (!is_integer (optarg))
230 usage2 (_("Timeout interval must be a positive integer"), optarg);
231 else
232 timeout_interval = atoi (optarg);
233 break;
234 case 'H': /* host */
235 if (!is_host (optarg))
236 usage2 (_("Invalid hostname/address"), optarg);
237 hostname = optarg;
238 break;
239 case 'p': /* port number */
240 if (!is_integer (optarg))
241 usage2 (_("Port must be a positive integer"), optarg);
242 asprintf (&comm,"%s -p %s", comm, optarg);
243 break;
244 case 'O': /* output file */
245 outputfile = optarg;
246 passive = TRUE;
247 break;
248 case 's': /* description of service to check */
249 service = realloc (service, (++services) * sizeof(char *));
250 p1 = optarg;
251 while ((p2 = index (p1, ':'))) {
252 *p2 = '\0';
253 asprintf (&service[services-1], "%s", p1);
254 service = realloc (service, (++services) * sizeof(char *));
255 p1 = p2 + 1;
256 }
257 asprintf (&service[services-1], "%s", p1);
258 break;
259 case 'n': /* short name of host in nagios configuration */
260 host_shortname = optarg;
261 break;
262 case 'u':
263 c = 'l';
264 case 'l': /* login name */
265 case 'i': /* identity */
266 asprintf (&comm, "%s -%c %s", comm, c, optarg);
267 break;
268 case '1': /* Pass these switches directly to ssh */
269 case '2': /* 1 to force version 1, 2 to force version 2 */
270 case '4': /* -4 for IPv4 */
271 case '6': /* -6 for IPv6 */
272 case 'f': /* fork to background */
273 asprintf (&comm, "%s -%c", comm, c);
274 break;
275 case 'C': /* Command for remote machine */
276 commands++;
277 if (commands > 1)
278 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
279 asprintf (&remotecmd, "%s%s", remotecmd, optarg);
280 break;
281 case 'S': /* Skip n lines in the output to ignore system banner */
282 if (!is_integer (optarg))
283 usage2 (_("skip lines must be an integer"), optarg);
284 else
285 skip_lines = atoi (optarg);
286 break;
287 }
288 }
290 c = optind;
291 if (hostname == NULL) {
292 if (c <= argc) {
293 die (STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname);
294 } else if (!is_host (argv[c]))
295 die (STATE_UNKNOWN, "%s: %s %s\n", progname, _("Invalid hostname/address"), argv[c]);
296 hostname = argv[c++];
297 }
299 if (strlen(remotecmd) == 0) {
300 for (; c < argc; c++)
301 if (strlen(remotecmd) > 0)
302 asprintf (&remotecmd, "%s %s", remotecmd, argv[c]);
303 else
304 asprintf (&remotecmd, "%s", argv[c]);
305 }
307 if (commands > 1)
308 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
310 if (remotecmd == NULL || strlen (remotecmd) <= 1)
311 usage4 (_("No remotecmd"));
313 asprintf (&comm, "%s %s '%s'", comm, hostname, remotecmd);
315 return validate_arguments ();
316 }
320 int
321 validate_arguments (void)
322 {
323 if (remotecmd == NULL || hostname == NULL)
324 return ERROR;
326 if (passive && commands != services)
327 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname);
329 if (passive && host_shortname == NULL)
330 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the nagios configs.\n"), progname);
332 return OK;
333 }
336 void
337 print_help (void)
338 {
339 print_revision (progname, revision);
341 printf ("Copyright (c) 1999 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n");
342 printf (COPYRIGHT, copyright, email);
344 printf (_("This plugin uses SSH to execute commands on a remote host\n\n"));
346 print_usage ();
348 printf (_(UT_HELP_VRSN));
350 printf (_(UT_HOST_PORT), 'p', "none");
352 printf (_(UT_IPv46));
354 printf (_("\
355 -1, --proto1\n\
356 tell ssh to use Protocol 1\n\
357 -2, --proto2\n\
358 tell ssh to use Protocol 2\n\
359 -S, --skiplines=n\n\
360 Ignore first n lines on STDERR (to suppress a logon banner)\n\
361 -f\n\
362 tells ssh to fork rather than create a tty\n"));
364 printf (_("\
365 -C, --command='COMMAND STRING'\n\
366 command to execute on the remote machine\n\
367 -l, --logname=USERNAME\n\
368 SSH user name on remote host [optional]\n\
369 -i, --identity=KEYFILE\n\
370 identity of an authorized key [optional]\n\
371 -O, --output=FILE\n\
372 external command file for nagios [optional]\n\
373 -s, --services=LIST\n\
374 list of nagios service names, separated by ':' [optional]\n\
375 -n, --name=NAME\n\
376 short name of host in nagios configuration [optional]\n"));
378 printf (_(UT_WARN_CRIT));
380 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
382 printf (_("\n\
383 The most common mode of use is to refer to a local identity file with\n\
384 the '-i' option. In this mode, the identity pair should have a null\n\
385 passphrase and the public key should be listed in the authorized_keys\n\
386 file of the remote host. Usually the key will be restricted to running\n\
387 only one command on the remote server. If the remote SSH server tracks\n\
388 invocation agruments, the one remote program may be an agent that can\n\
389 execute additional commands as proxy\n"));
391 printf (_("\n\
392 To use passive mode, provide multiple '-C' options, and provide\n\
393 all of -O, -s, and -n options (servicelist order must match '-C'\n\
394 options)\n"));
396 printf ("\n\
397 $ check_by_ssh -H localhost -n lh -s c1:c2:c3 \\\n\
398 -C uptime -C uptime -C uptime -O /tmp/foo\n\
399 $ cat /tmp/foo\n\
400 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days...\n\
401 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days...\n\
402 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c3;0; up 2 days...\n");
404 printf (_(UT_SUPPORT));
405 }
409 void
410 print_usage (void)
411 {
412 printf ("\n\
413 Usage: %s [-f46] [-t timeout] [-i identity] [-l user] -H <host> -C <command>\n\
414 [-n name] [-s servicelist] [-O outputfile] [-p port]\n", progname);
415 }