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 char *remotecmd = NULL;
37 char *comm = NULL;
38 char *hostname = NULL;
39 char *outputfile = NULL;
40 char *host_shortname = NULL;
41 char **service;
42 int passive = FALSE;
43 int verbose = FALSE;
45 int
46 main (int argc, char **argv)
47 {
49 char input_buffer[MAX_INPUT_BUFFER];
50 char *result_text;
51 char *status_text;
52 char *output;
53 char *eol = NULL;
54 int cresult;
55 int result = STATE_UNKNOWN;
56 time_t local_time;
57 FILE *fp = NULL;
59 remotecmd = strdup ("");
60 comm = strdup (SSH_COMMAND);
61 result_text = strdup ("");
63 setlocale (LC_ALL, "");
64 bindtextdomain (PACKAGE, LOCALEDIR);
65 textdomain (PACKAGE);
67 /* process arguments */
68 if (process_arguments (argc, argv) == ERROR)
69 usage (_("Could not parse arguments\n"));
72 /* Set signal handling and alarm timeout */
73 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
74 printf (_("Cannot catch SIGALRM"));
75 return STATE_UNKNOWN;
76 }
77 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 (_("Unable to open pipe: %s"), 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);
105 /* WARNING if output found on stderr */
106 if (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
107 printf ("%s\n", input_buffer);
108 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process))
109 printf ("%s\n", input_buffer);
110 return STATE_WARNING;
111 }
112 (void) fclose (child_stderr);
115 /* close the pipe */
116 result = spclose (child_process);
119 /* process output */
120 if (passive) {
122 if (!(fp = fopen (outputfile, "a"))) {
123 printf (_("SSH WARNING: could not open %s\n"), outputfile);
124 exit (STATE_UNKNOWN);
125 }
127 local_time = time (NULL);
128 commands = 0;
129 while (result_text && strlen(result_text) > 0) {
130 status_text = strstr (result_text, "STATUS CODE: ");
131 if (status_text == NULL) {
132 printf ("%s", result_text);
133 return result;
134 }
135 asprintf (&output, "%s", result_text);
136 result_text = strnl (status_text);
137 eol = strpbrk (output, "\r\n");
138 if (eol != NULL)
139 eol[0] = 0;
140 if (service[commands] && status_text
141 && sscanf (status_text, "STATUS CODE: %d", &cresult) == 1) {
142 fprintf (fp, _("[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n"),
143 (int) local_time, host_shortname, service[commands++], cresult,
144 output);
145 }
146 }
148 }
151 /* print the first line from the remote command */
152 else {
153 eol = strpbrk (result_text, "\r\n");
154 if (eol)
155 eol[0] = 0;
156 printf ("%s\n", result_text);
157 }
160 /* return error status from remote command */
161 return result;
162 }
168 \f
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 {"proto1", no_argument, 0, '1'},
193 {"proto2", no_argument, 0, '2'},
194 {"use-ipv4", no_argument, 0, '4'},
195 {"use-ipv6", no_argument, 0, '6'},
196 {0, 0, 0, 0}
197 };
199 if (argc < 2)
200 return ERROR;
202 for (c = 1; c < argc; c++)
203 if (strcmp ("-to", argv[c]) == 0)
204 strcpy (argv[c], "-t");
206 while (1) {
207 c = getopt_long (argc, argv, "Vvh1246ft:H:O:p:i:u:l:C:n:s:", longopts,
208 &option);
210 if (c == -1 || c == EOF)
211 break;
213 switch (c) {
214 case '?': /* help */
215 print_usage ();
216 exit (STATE_UNKNOWN);
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 an integer"), optarg);
229 else
230 timeout_interval = atoi (optarg);
231 break;
232 case 'H': /* host */
233 if (!is_host (optarg))
234 usage2 (_("invalid host name"), optarg);
235 hostname = optarg;
236 break;
237 case 'p': /* port number */
238 if (!is_integer (optarg))
239 usage2 (_("port must be an 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 }
279 }
281 c = optind;
282 if (hostname == NULL) {
283 if (c <= argc) {
284 die (STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname);
285 } else if (!is_host (argv[c]))
286 die (STATE_UNKNOWN, _("%s: Invalid host name %s\n"), progname, argv[c]);
287 hostname = argv[c++];
288 }
290 if (strlen(remotecmd) == 0) {
291 for (; c < argc; c++)
292 if (strlen(remotecmd) > 0)
293 asprintf (&remotecmd, "%s %s", remotecmd, argv[c]);
294 else
295 asprintf (&remotecmd, "%s", argv[c]);
296 }
298 if (commands > 1)
299 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
301 if (remotecmd == NULL || strlen (remotecmd) <= 1)
302 usage (_("No remotecmd\n"));
304 asprintf (&comm, "%s %s '%s'", comm, hostname, remotecmd);
306 return validate_arguments ();
307 }
313 int
314 validate_arguments (void)
315 {
316 if (remotecmd == NULL || hostname == NULL)
317 return ERROR;
319 if (passive && commands != services)
320 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname);
322 if (passive && host_shortname == NULL)
323 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the nagios configs.\n"), progname);
325 return OK;
326 }
332 \f
333 void
334 print_help (void)
335 {
336 print_revision (progname, revision);
338 printf (_("Copyright (c) 1999 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n"));
339 printf (_(COPYRIGHT), copyright, email);
341 printf (_("This plugin uses SSH to execute commands on a remote host\n\n"));
343 print_usage ();
345 printf (_(UT_HELP_VRSN));
347 printf (_(UT_HOST_PORT), 'p', "none");
349 printf (_(UT_IPv46));
351 printf (_("\
352 -1, --proto1\n\
353 tell ssh to use Protocol 1\n\
354 -2, --proto2\n\
355 tell ssh to use Protocol 2\n\
356 -f\n\
357 tells ssh to fork rather than create a tty\n"));
359 printf (_("\
360 -C, --command='COMMAND STRING'\n\
361 command to execute on the remote machine\n\
362 -l, --logname=USERNAME\n\
363 SSH user name on remote host [optional]\n\
364 -i, --identity=KEYFILE\n\
365 identity of an authorized key [optional]\n\
366 -O, --output=FILE\n\
367 external command file for nagios [optional]\n\
368 -s, --services=LIST\n\
369 list of nagios service names, separated by ':' [optional]\n\
370 -n, --name=NAME\n\
371 short name of host in nagios configuration [optional]\n"));
373 printf (_(UT_WARN_CRIT));
375 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
377 printf (_("\n\
378 The most common mode of use is to refer to a local identity file with\n\
379 the '-i' option. In this mode, the identity pair should have a null\n\
380 passphrase and the public key should be listed in the authorized_keys\n\
381 file of the remote host. Usually the key will be restricted to running\n\
382 only one command on the remote server. If the remote SSH server tracks\n\
383 invocation agruments, the one remote program may be an agent that can\n\
384 execute additional commands as proxy\n"));
386 printf (_("\n\
387 To use passive mode, provide multiple '-C' options, and provide\n\
388 all of -O, -s, and -n options (servicelist order must match '-C'\n\
389 options)\n"));
391 printf (_(UT_SUPPORT));
392 }
398 void
399 print_usage (void)
400 {
401 printf (_("\n\
402 Usage: %s [-f46] [-t timeout] [-i identity] [-l user] -H <host> \n\
403 -C <command> [-n name] [-s servicelist] [-O outputfile] [-p port]\n"),
404 progname);
405 printf (_(UT_HLP_VRS), progname, progname);
406 }