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;
49 \f
50 int
51 main (int argc, char **argv)
52 {
54 char input_buffer[MAX_INPUT_BUFFER];
55 char *result_text;
56 char *status_text;
57 char *output;
58 char *eol = NULL;
59 int cresult;
60 int result = STATE_UNKNOWN;
61 time_t local_time;
62 FILE *fp = NULL;
64 asprintf (&remotecmd, "%s", "");
65 asprintf (&comm, "%s", SSH_COMMAND);
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 /* get results from remote command */
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 return STATE_WARNING;
109 }
110 (void) fclose (child_stderr);
113 /* close the pipe */
114 result = spclose (child_process);
117 /* process output */
118 if (passive) {
120 if (!(fp = fopen (outputfile, "a"))) {
121 printf (_("SSH WARNING: could not open %s\n"), outputfile);
122 exit (STATE_UNKNOWN);
123 }
125 time (&local_time);
126 commands = 0;
127 while (result_text && strlen(result_text) > 0) {
128 status_text = strstr (result_text, _("STATUS CODE: "));
129 if (status_text == NULL) {
130 printf ("%s", result_text);
131 return result;
132 }
133 asprintf (&output, "%s", result_text);
134 result_text = strnl (status_text);
135 eol = strpbrk (output, "\r\n");
136 if (eol != NULL)
137 eol[0] = 0;
138 if (service[commands] && status_text
139 && sscanf (status_text, _("STATUS CODE: %d"), &cresult) == 1) {
140 fprintf (fp, _("[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n"),
141 (int) local_time, host_shortname, service[commands++], cresult,
142 output);
143 }
144 }
146 }
148 /* print the first line from the remote command */
149 else {
150 eol = strpbrk (result_text, "\r\n");
151 if (eol)
152 eol[0] = 0;
153 printf ("%s\n", result_text);
154 }
156 /* return error status from remote command */
157 return result;
158 }
164 \f
165 /* process command-line arguments */
166 int
167 process_arguments (int argc, char **argv)
168 {
169 int c;
170 char *p1, *p2;
172 int option_index = 0;
173 static struct option long_options[] = {
174 {"version", no_argument, 0, 'V'},
175 {"help", no_argument, 0, 'h'},
176 {"verbose", no_argument, 0, 'v'},
177 {"fork", no_argument, 0, 'f'},
178 {"timeout", required_argument, 0, 't'},
179 {"host", required_argument, 0, 'H'},
180 {"port", required_argument,0,'p'},
181 {"output", required_argument, 0, 'O'},
182 {"name", required_argument, 0, 'n'},
183 {"services", required_argument, 0, 's'},
184 {"identity", required_argument, 0, 'i'},
185 {"user", required_argument, 0, 'u'},
186 {"logname", required_argument, 0, 'l'},
187 {"command", required_argument, 0, 'C'},
188 {"proto1", no_argument, 0, '1'},
189 {"proto2", no_argument, 0, '2'},
190 {"use-ipv4", no_argument, 0, '4'},
191 {"use-ipv6", no_argument, 0, '6'},
192 {0, 0, 0, 0}
193 };
195 if (argc < 2)
196 return ERROR;
198 for (c = 1; c < argc; c++)
199 if (strcmp ("-to", argv[c]) == 0)
200 strcpy (argv[c], "-t");
202 while (1) {
203 c = getopt_long (argc, argv, "Vvh1246ft:H:O:p:i:u:l:C:n:s:", long_options,
204 &option_index);
206 if (c == -1 || c == EOF)
207 break;
209 switch (c) {
210 case '?': /* help */
211 print_usage ();
212 exit (STATE_UNKNOWN);
213 case 'V': /* version */
214 print_revision (progname, "$Revision$");
215 exit (STATE_OK);
216 case 'h': /* help */
217 print_help ();
218 exit (STATE_OK);
219 case 'v': /* help */
220 verbose = TRUE;
221 break;
222 case 't': /* timeout period */
223 if (!is_integer (optarg))
224 usage2 (_("timeout interval must be an integer"), optarg);
225 timeout_interval = atoi (optarg);
226 break;
227 case 'H': /* host */
228 if (!is_host (optarg))
229 usage2 (_("invalid host name"), optarg);
230 hostname = optarg;
231 break;
232 case 'p': /* port number */
233 if (!is_integer (optarg))
234 usage2 (_("port must be an integer"), optarg);
235 asprintf (&comm,"%s -p %s", comm, optarg);
236 break;
237 case 'O': /* output file */
238 outputfile = optarg;
239 passive = TRUE;
240 break;
241 case 's': /* description of service to check */
242 service = realloc (service, (++services) * sizeof(char *));
243 p1 = optarg;
244 while ((p2 = index (p1, ':'))) {
245 *p2 = '\0';
246 asprintf (&service[services-1], "%s", p1);
247 service = realloc (service, (++services) * sizeof(char *));
248 p1 = p2 + 1;
249 }
250 asprintf (&service[services-1], "%s", p1);
251 break;
252 case 'n': /* short name of host in nagios configuration */
253 host_shortname = optarg;
254 break;
255 case 'u':
256 c = 'l';
257 case 'l': /* login name */
258 case 'i': /* identity */
259 asprintf (&comm, "%s -%c %s", comm, c, optarg);
260 break;
261 case '1': /* Pass these switches directly to ssh */
262 case '2': /* 1 to force version 1, 2 to force version 2 */
263 case '4': /* -4 for IPv4 */
264 case '6': /* -6 for IPv6 */
265 case 'f': /* fork to background */
266 asprintf (&comm, "%s -%c", comm, c);
267 break;
268 case 'C': /* Command for remote machine */
269 commands++;
270 if (commands > 1)
271 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
272 asprintf (&remotecmd, "%s%s", remotecmd, optarg);
273 }
274 }
276 c = optind;
277 if (hostname == NULL) {
278 if (c <= argc) {
279 die (STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname);
280 } else if (!is_host (argv[c]))
281 die (STATE_UNKNOWN, _("%s: Invalid host name %s\n"), progname, argv[c]);
282 hostname = argv[c++];
283 }
285 if (strlen(remotecmd) == 0) {
286 for (; c < argc; c++)
287 if (strlen(remotecmd) > 0)
288 asprintf (&remotecmd, "%s %s", remotecmd, argv[c]);
289 else
290 asprintf (&remotecmd, "%s", argv[c]);
291 }
293 if (commands > 1)
294 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
296 if (remotecmd == NULL || strlen (remotecmd) <= 1)
297 usage (_("No remotecmd\n"));
299 asprintf (&comm, "%s %s '%s'", comm, hostname, remotecmd);
301 return validate_arguments ();
302 }
308 int
309 validate_arguments (void)
310 {
311 if (remotecmd == NULL || hostname == NULL)
312 return ERROR;
314 if (passive && commands != services)
315 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname);
317 if (passive && host_shortname == NULL)
318 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the nagios configs.\n"), progname);
320 return OK;
321 }
327 \f
328 void
329 print_help (void)
330 {
331 print_revision (progname, revision);
333 printf (_("Copyright (c) 1999 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n"));
334 printf (_(COPYRIGHT), copyright, email);
336 printf (_("This plugin uses SSH to execute commands on a remote host\n\n"));
338 print_usage ();
340 printf (_(UT_HELP_VRSN));
342 printf (_(UT_HOST_PORT), 'p', "none");
344 printf (_(UT_IPv46));
346 printf (_("\
347 -1, --proto1\n\
348 tell ssh to use Protocol 1\n\
349 -2, --proto2\n\
350 tell ssh to use Protocol 2\n\
351 -f\n\
352 tells ssh to fork rather than create a tty\n"));
354 printf (_("\
355 -C, --command='COMMAND STRING'\n\
356 command to execute on the remote machine\n\
357 -l, --logname=USERNAME\n\
358 SSH user name on remote host [optional]\n\
359 -i, --identity=KEYFILE\n\
360 identity of an authorized key [optional]\n\
361 -O, --output=FILE\n\
362 external command file for nagios [optional]\n\
363 -s, --services=LIST\n\
364 list of nagios service names, separated by ':' [optional]\n\
365 -n, --name=NAME\n\
366 short name of host in nagios configuration [optional]\n"));
368 printf (_(UT_WARN_CRIT));
370 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
372 printf (_("\n\
373 The most common mode of use is to refer to a local identity file with\n\
374 the '-i' option. In this mode, the identity pair should have a null\n\
375 passphrase and the public key should be listed in the authorized_keys\n\
376 file of the remote host. Usually the key will be restricted to running\n\
377 only one command on the remote server. If the remote SSH server tracks\n\
378 invocation agruments, the one remote program may be an agent that can\n\
379 execute additional commands as proxy\n"));
381 printf (_("\n\
382 To use passive mode, provide multiple '-C' options, and provide\n\
383 all of -O, -s, and -n options (servicelist order must match '-C'\n\
384 options)\n"));
385 }
391 void
392 print_usage (void)
393 {
394 printf (_("\n\
395 Usage: %s [-f46] [-t timeout] [-i identity] [-l user] -H <host> \n\
396 -C <command> [-n name] [-s servicelist] [-O outputfile] [-p port]\n"),
397 progname);
398 printf (_(UT_HLP_VRS), progname, progname);
399 }