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 "runcmd.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 = 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 *status_text;
53 int cresult;
54 int result = STATE_UNKNOWN;
55 int i;
56 time_t local_time;
57 FILE *fp = NULL;
58 struct output chld_out, chld_err;
60 remotecmd = "";
61 comm = strdup (SSH_COMMAND);
63 setlocale (LC_ALL, "");
64 bindtextdomain (PACKAGE, LOCALEDIR);
65 textdomain (PACKAGE);
67 /* process arguments */
68 if (process_arguments (argc, argv) == ERROR)
69 usage_va(_("Could not parse arguments"));
71 /* Set signal handling and alarm timeout */
72 if (signal (SIGALRM, popen_timeout_alarm_handler) == SIG_ERR) {
73 usage_va(_("Cannot catch SIGALRM"));
74 }
75 alarm (timeout_interval);
77 /* run the command */
78 if (verbose)
79 printf ("%s\n", comm);
81 result = np_runcmd(comm, &chld_out, &chld_err, 0);
82 /* UNKNOWN if output found on stderr */
83 if(chld_err.buflen) {
84 printf(_("Remote command execution failed: %s\n"),
85 chld_err.buflen ? chld_err.buf : _("Unknown error"));
86 return STATE_UNKNOWN;
87 }
89 /* this is simple if we're not supposed to be passive.
90 * Wrap up quickly and keep the tricks below */
91 if(!passive) {
92 printf ("%s\n", skip < chld_out.lines ? chld_out.line[skip] : chld_out.buf);
93 return result; /* return error status from remote command */
94 }
97 /*
98 * Passive mode
99 */
101 /* process output */
102 if (!(fp = fopen (outputfile, "a"))) {
103 printf (_("SSH WARNING: could not open %s\n"), outputfile);
104 exit (STATE_UNKNOWN);
105 }
107 local_time = time (NULL);
108 commands = 0;
109 for(i = skip; chld_out.line[i]; i++) {
110 status_text = strstr (chld_out.line[i], "STATUS CODE: ");
111 if (status_text == NULL) {
112 printf ("%s", chld_out.line[i]);
113 return result;
114 }
115 if (service[commands] && status_text
116 && sscanf (status_text, "STATUS CODE: %d", &cresult) == 1)
117 {
118 fprintf (fp, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n",
119 (int) local_time, host_shortname, service[commands++],
120 cresult, chld_out.line[i]);
121 }
122 }
124 /* force an OK state */
125 return result;
126 }
128 /* process command-line arguments */
129 int
130 process_arguments (int argc, char **argv)
131 {
132 int c;
133 char *p1, *p2;
135 int option = 0;
136 static struct option longopts[] = {
137 {"version", no_argument, 0, 'V'},
138 {"help", no_argument, 0, 'h'},
139 {"verbose", no_argument, 0, 'v'},
140 {"fork", no_argument, 0, 'f'},
141 {"timeout", required_argument, 0, 't'},
142 {"host", required_argument, 0, 'H'},
143 {"port", required_argument,0,'p'},
144 {"output", required_argument, 0, 'O'},
145 {"name", required_argument, 0, 'n'},
146 {"services", required_argument, 0, 's'},
147 {"identity", required_argument, 0, 'i'},
148 {"user", required_argument, 0, 'u'},
149 {"logname", required_argument, 0, 'l'},
150 {"command", required_argument, 0, 'C'},
151 {"skip", required_argument, 0, 'S'},
152 {"proto1", no_argument, 0, '1'},
153 {"proto2", no_argument, 0, '2'},
154 {"use-ipv4", no_argument, 0, '4'},
155 {"use-ipv6", no_argument, 0, '6'},
156 {0, 0, 0, 0}
157 };
159 if (argc < 2)
160 return ERROR;
162 for (c = 1; c < argc; c++)
163 if (strcmp ("-to", argv[c]) == 0)
164 strcpy (argv[c], "-t");
166 while (1) {
167 c = getopt_long (argc, argv, "Vvh1246ft:H:O:p:i:u:l:C:S:n:s:", longopts,
168 &option);
170 if (c == -1 || c == EOF)
171 break;
173 switch (c) {
174 case 'V': /* version */
175 print_revision (progname, revision);
176 exit (STATE_OK);
177 case 'h': /* help */
178 print_help ();
179 exit (STATE_OK);
180 case 'v': /* help */
181 verbose = TRUE;
182 break;
183 case 't': /* timeout period */
184 if (!is_integer (optarg))
185 usage_va(_("Timeout interval must be a positive integer"));
186 else
187 timeout_interval = atoi (optarg);
188 break;
189 case 'H': /* host */
190 host_or_die(optarg);
191 hostname = optarg;
192 break;
193 case 'p': /* port number */
194 if (!is_integer (optarg))
195 usage_va(_("Port must be a positive integer"));
196 asprintf (&comm,"%s -p %s", comm, optarg);
197 break;
198 case 'O': /* output file */
199 outputfile = optarg;
200 passive = TRUE;
201 break;
202 case 's': /* description of service to check */
203 p1 = optarg;
204 service = realloc (service, (++services) * sizeof(char *));
205 while ((p2 = index (p1, ':'))) {
206 *p2 = '\0';
207 service[services - 1] = p1;
208 service = realloc (service, (++services) * sizeof(char *));
209 p1 = p2 + 1;
210 }
211 service[services - 1] = p1;
212 break;
213 case 'n': /* short name of host in nagios configuration */
214 host_shortname = optarg;
215 break;
217 case 'u':
218 c = 'l';
219 case 'l': /* login name */
220 case 'i': /* identity */
221 asprintf (&comm, "%s -%c %s", comm, c, optarg);
222 break;
224 case '1': /* Pass these switches directly to ssh */
225 case '2': /* 1 to force version 1, 2 to force version 2 */
226 case '4': /* -4 for IPv4 */
227 case '6': /* -6 for IPv6 */
228 case 'f': /* fork to background */
229 asprintf (&comm, "%s -%c", comm, c);
230 break;
231 case 'C': /* Command for remote machine */
232 commands++;
233 if (commands > 1)
234 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
235 asprintf (&remotecmd, "%s%s", remotecmd, optarg);
236 break;
237 case 'S': /* Skip n lines in the output to ignore system banner */
238 if (!is_integer (optarg))
239 usage_va(_("skip lines must be an integer"));
240 else
241 skip = atoi (optarg);
242 break;
243 default: /* help */
244 usage_va(_("Unknown argument - %s"), optarg);
245 }
246 }
248 c = optind;
249 if (hostname == NULL) {
250 if (c <= argc) {
251 die (STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname);
252 }
253 host_or_die(argv[c]);
254 hostname = argv[c++];
255 }
257 if (strlen(remotecmd) == 0) {
258 for (; c < argc; c++)
259 if (strlen(remotecmd) > 0)
260 asprintf (&remotecmd, "%s %s", remotecmd, argv[c]);
261 else
262 asprintf (&remotecmd, "%s", argv[c]);
263 }
265 if (commands > 1)
266 asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
268 if (remotecmd == NULL || strlen (remotecmd) <= 1)
269 usage_va(_("No remotecmd"));
271 asprintf (&comm, "%s %s '%s'", comm, hostname, remotecmd);
273 return validate_arguments ();
274 }
278 int
279 validate_arguments (void)
280 {
281 if (remotecmd == NULL || hostname == NULL)
282 return ERROR;
284 if (passive && commands != services)
285 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname);
287 if (passive && host_shortname == NULL)
288 die (STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the nagios configs.\n"), progname);
290 return OK;
291 }
294 void
295 print_help (void)
296 {
297 print_revision (progname, revision);
299 printf ("Copyright (c) 1999 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n");
300 printf (COPYRIGHT, copyright, email);
302 printf (_("This plugin uses SSH to execute commands on a remote host\n\n"));
304 print_usage ();
306 printf (_(UT_HELP_VRSN));
308 printf (_(UT_HOST_PORT), 'p', "none");
310 printf (_(UT_IPv46));
312 printf (_("\
313 -1, --proto1\n\
314 tell ssh to use Protocol 1\n\
315 -2, --proto2\n\
316 tell ssh to use Protocol 2\n\
317 -S, --skiplines=n\n\
318 Ignore first n lines on STDERR (to suppress a logon banner)\n\
319 -f\n\
320 tells ssh to fork rather than create a tty\n"));
322 printf (_("\
323 -C, --command='COMMAND STRING'\n\
324 command to execute on the remote machine\n\
325 -l, --logname=USERNAME\n\
326 SSH user name on remote host [optional]\n\
327 -i, --identity=KEYFILE\n\
328 identity of an authorized key [optional]\n\
329 -O, --output=FILE\n\
330 external command file for nagios [optional]\n\
331 -s, --services=LIST\n\
332 list of nagios service names, separated by ':' [optional]\n\
333 -n, --name=NAME\n\
334 short name of host in nagios configuration [optional]\n"));
336 printf (_(UT_WARN_CRIT));
338 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
340 printf (_("\n\
341 The most common mode of use is to refer to a local identity file with\n\
342 the '-i' option. In this mode, the identity pair should have a null\n\
343 passphrase and the public key should be listed in the authorized_keys\n\
344 file of the remote host. Usually the key will be restricted to running\n\
345 only one command on the remote server. If the remote SSH server tracks\n\
346 invocation agruments, the one remote program may be an agent that can\n\
347 execute additional commands as proxy\n"));
349 printf (_("\n\
350 To use passive mode, provide multiple '-C' options, and provide\n\
351 all of -O, -s, and -n options (servicelist order must match '-C'\n\
352 options)\n"));
354 printf ("\n\
355 $ check_by_ssh -H localhost -n lh -s c1:c2:c3 \\\n\
356 -C uptime -C uptime -C uptime -O /tmp/foo\n\
357 $ cat /tmp/foo\n\
358 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days...\n\
359 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days...\n\
360 [1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c3;0; up 2 days...\n");
362 printf (_(UT_SUPPORT));
363 }
367 void
368 print_usage (void)
369 {
370 printf ("\n\
371 Usage: %s [-f46] [-t timeout] [-i identity] [-l user] -H <host> -C <command>\n\
372 [-n name] [-s servicelist] [-O outputfile] [-p port]\n", progname);
373 }