Back to index

nagios-plugins  1.4.16
check_by_ssh.c
Go to the documentation of this file.
00001 /*****************************************************************************
00002 * 
00003 * Nagios check_by_ssh plugin
00004 * 
00005 * License: GPL
00006 * Copyright (c) 2000-2008 Nagios Plugins Development Team
00007 * 
00008 * Description:
00009 * 
00010 * This file contains the check_by_ssh plugin
00011 * 
00012 * 
00013 * This program is free software: you can redistribute it and/or modify
00014 * it under the terms of the GNU General Public License as published by
00015 * the Free Software Foundation, either version 3 of the License, or
00016 * (at your option) any later version.
00017 * 
00018 * This program is distributed in the hope that it will be useful,
00019 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00021 * GNU General Public License for more details.
00022 * 
00023 * You should have received a copy of the GNU General Public License
00024 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00025 * 
00026 * 
00027 *****************************************************************************/
00028 
00029 const char *progname = "check_by_ssh";
00030 const char *copyright = "2000-2008";
00031 const char *email = "nagiosplug-devel@lists.sourceforge.net";
00032 
00033 #include "common.h"
00034 #include "utils.h"
00035 #include "netutils.h"
00036 #include "utils_cmd.h"
00037 
00038 #ifndef NP_MAXARGS
00039 #define NP_MAXARGS 1024
00040 #endif
00041 
00042 int process_arguments (int, char **);
00043 int validate_arguments (void);
00044 void comm_append (const char *);
00045 void print_help (void);
00046 void print_usage (void);
00047 
00048 unsigned int commands = 0;
00049 unsigned int services = 0;
00050 int skip_stdout = 0;
00051 int skip_stderr = 0;
00052 char *remotecmd = NULL;
00053 char **commargv = NULL;
00054 int commargc = 0;
00055 char *hostname = NULL;
00056 char *outputfile = NULL;
00057 char *host_shortname = NULL;
00058 char **service;
00059 int passive = FALSE;
00060 int verbose = FALSE;
00061 
00062 int
00063 main (int argc, char **argv)
00064 {
00065 
00066        char *status_text;
00067        int cresult;
00068        int result = STATE_UNKNOWN;
00069        int i;
00070        time_t local_time;
00071        FILE *fp = NULL;
00072        output chld_out, chld_err;
00073 
00074        remotecmd = "";
00075        comm_append(SSH_COMMAND);
00076 
00077        setlocale (LC_ALL, "");
00078        bindtextdomain (PACKAGE, LOCALEDIR);
00079        textdomain (PACKAGE);
00080 
00081        /* Parse extra opts if any */
00082        argv=np_extra_opts (&argc, argv, progname);
00083 
00084        /* process arguments */
00085        if (process_arguments (argc, argv) == ERROR)
00086               usage_va(_("Could not parse arguments"));
00087 
00088        /* Set signal handling and alarm timeout */
00089        if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) {
00090               usage_va(_("Cannot catch SIGALRM"));
00091        }
00092        alarm (timeout_interval);
00093 
00094        /* run the command */
00095        if (verbose) {
00096               printf ("Command: %s\n", commargv[0]);
00097               for (i=1; i<commargc; i++)
00098                      printf ("Argument %i: %s\n", i, commargv[i]);
00099        }
00100 
00101        result = cmd_run_array (commargv, &chld_out, &chld_err, 0);
00102 
00103        if (skip_stdout == -1) /* --skip-stdout specified without argument */
00104               skip_stdout = chld_out.lines;
00105        if (skip_stderr == -1) /* --skip-stderr specified without argument */
00106               skip_stderr = chld_err.lines;
00107 
00108        /* UNKNOWN or worse if (non-skipped) output found on stderr */
00109        if(chld_err.lines > skip_stderr) {
00110               printf (_("Remote command execution failed: %s\n"),
00111                       chld_err.line[skip_stderr]);
00112               return max_state_alt(result, STATE_UNKNOWN);
00113        }
00114 
00115        /* this is simple if we're not supposed to be passive.
00116         * Wrap up quickly and keep the tricks below */
00117        if(!passive) {
00118               if (chld_out.lines > skip_stdout)
00119                      for (i = skip_stdout; i < chld_out.lines; i++)
00120                             puts (chld_out.line[i]);
00121               else
00122                      printf (_("%s - check_by_ssh: Remote command '%s' returned status %d\n"),
00123                              state_text(result), remotecmd, result);
00124               return result;       /* return error status from remote command */
00125        }
00126 
00127 
00128        /*
00129         * Passive mode
00130         */
00131 
00132        /* process output */
00133        if (!(fp = fopen (outputfile, "a"))) {
00134               printf (_("SSH WARNING: could not open %s\n"), outputfile);
00135               exit (STATE_UNKNOWN);
00136        }
00137 
00138        local_time = time (NULL);
00139        commands = 0;
00140        for(i = skip_stdout; i < chld_out.lines; i++) {
00141               status_text = chld_out.line[i++];
00142               if (i == chld_out.lines || strstr (chld_out.line[i], "STATUS CODE: ") == NULL)
00143                      die (STATE_UNKNOWN, _("%s: Error parsing output\n"), progname);
00144 
00145               if (service[commands] && status_text
00146                      && sscanf (chld_out.line[i], "STATUS CODE: %d", &cresult) == 1)
00147               {
00148                      fprintf (fp, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n",
00149                               (int) local_time, host_shortname, service[commands++],
00150                               cresult, status_text);
00151               }
00152        }
00153        
00154        /* Multiple commands and passive checking should always return OK */
00155        return result;
00156 }
00157 
00158 /* process command-line arguments */
00159 int
00160 process_arguments (int argc, char **argv)
00161 {
00162        int c;
00163        char *p1, *p2;
00164 
00165        int option = 0;
00166        static struct option longopts[] = {
00167               {"version", no_argument, 0, 'V'},
00168               {"help", no_argument, 0, 'h'},
00169               {"verbose", no_argument, 0, 'v'},
00170               {"fork", no_argument, 0, 'f'},
00171               {"timeout", required_argument, 0, 't'},
00172               {"host", required_argument, 0, 'H'},
00173               {"port", required_argument,0,'p'},
00174               {"output", required_argument, 0, 'O'},
00175               {"name", required_argument, 0, 'n'},
00176               {"services", required_argument, 0, 's'},
00177               {"identity", required_argument, 0, 'i'},
00178               {"user", required_argument, 0, 'u'},
00179               {"logname", required_argument, 0, 'l'},
00180               {"command", required_argument, 0, 'C'},
00181               {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */
00182               {"skip-stdout", optional_argument, 0, 'S'},
00183               {"skip-stderr", optional_argument, 0, 'E'},
00184               {"proto1", no_argument, 0, '1'},
00185               {"proto2", no_argument, 0, '2'},
00186               {"use-ipv4", no_argument, 0, '4'},
00187               {"use-ipv6", no_argument, 0, '6'},
00188               {"ssh-option", required_argument, 0, 'o'},
00189               {"quiet", no_argument, 0, 'q'},
00190               {"configfile", optional_argument, 0, 'F'},
00191               {0, 0, 0, 0}
00192        };
00193 
00194        if (argc < 2)
00195               return ERROR;
00196 
00197        for (c = 1; c < argc; c++)
00198               if (strcmp ("-to", argv[c]) == 0)
00199                      strcpy (argv[c], "-t");
00200 
00201        while (1) {
00202               c = getopt_long (argc, argv, "Vvh1246fqt:H:O:p:i:u:l:C:S::E::n:s:o:F:", longopts,
00203                                &option);
00204 
00205               if (c == -1 || c == EOF)
00206                      break;
00207 
00208               switch (c) {
00209               case 'V':                                                             /* version */
00210                      print_revision (progname, NP_VERSION);
00211                      exit (STATE_OK);
00212               case 'h':                                                             /* help */
00213                      print_help ();
00214                      exit (STATE_OK);
00215               case 'v':                                                             /* help */
00216                      verbose = TRUE;
00217                      break;
00218               case 't':                                                             /* timeout period */
00219                      if (!is_integer (optarg))
00220                             usage_va(_("Timeout interval must be a positive integer"));
00221                      else
00222                             timeout_interval = atoi (optarg);
00223                      break;
00224               case 'H':                                                             /* host */
00225                      host_or_die(optarg);
00226                      hostname = optarg;
00227                      break;
00228               case 'p': /* port number */
00229                      if (!is_integer (optarg))
00230                             usage_va(_("Port must be a positive integer"));
00231                      comm_append("-p");
00232                      comm_append(optarg);
00233                      break;
00234               case 'O':                                                             /* output file */
00235                      outputfile = optarg;
00236                      passive = TRUE;
00237                      break;
00238               case 's':                                                             /* description of service to check */
00239                      p1 = optarg;
00240                      service = realloc (service, (++services) * sizeof(char *));
00241                      while ((p2 = index (p1, ':'))) {
00242                             *p2 = '\0';
00243                             service[services - 1] = p1;
00244                             service = realloc (service, (++services) * sizeof(char *));
00245                             p1 = p2 + 1;
00246                      }
00247                      service[services - 1] = p1;
00248                      break;
00249               case 'n':                                                             /* short name of host in nagios configuration */
00250                      host_shortname = optarg;
00251                      break;
00252 
00253               case 'u':
00254                      comm_append("-l");
00255                      comm_append(optarg);
00256                      break;
00257               case 'l':                                                             /* login name */
00258                      comm_append("-l");
00259                      comm_append(optarg);
00260                      break;
00261               case 'i':                                                             /* identity */
00262                      comm_append("-i");
00263                      comm_append(optarg);
00264                      break;
00265 
00266               case '1':                                                             /* Pass these switches directly to ssh */
00267                      comm_append("-1");
00268                      break;
00269               case '2':                                                             /* 1 to force version 1, 2 to force version 2 */
00270                      comm_append("-2");
00271                      break;
00272               case '4':                                                             /* -4 for IPv4 */
00273                      comm_append("-4");
00274                      break;
00275               case '6':                                                      /* -6 for IPv6 */
00276                      comm_append("-6");
00277                      break;
00278               case 'f':                                                             /* fork to background */
00279                      comm_append("-f");
00280                      break;
00281               case 'C':                                                             /* Command for remote machine */
00282                      commands++;
00283                      if (commands > 1)
00284                             asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
00285                      asprintf (&remotecmd, "%s%s", remotecmd, optarg);
00286                      break;
00287               case 'S':                                                             /* skip n (or all) lines on stdout */
00288                      if (optarg == NULL)
00289                             skip_stdout = -1; /* skip all output on stdout */
00290                      else if (!is_integer (optarg))
00291                             usage_va(_("skip-stdout argument must be an integer"));
00292                      else
00293                             skip_stdout = atoi (optarg);
00294                      break;
00295               case 'E':                                                             /* skip n (or all) lines on stderr */
00296                      if (optarg == NULL)
00297                             skip_stderr = -1; /* skip all output on stderr */
00298                      else if (!is_integer (optarg))
00299                             usage_va(_("skip-stderr argument must be an integer"));
00300                      else
00301                             skip_stderr = atoi (optarg);
00302                      break;
00303               case 'o':                                                             /* Extra options for the ssh command */
00304                      comm_append("-o");
00305                      comm_append(optarg);
00306                      break;
00307               case 'q':                                                             /* Tell the ssh command to be quiet */
00308                      comm_append("-q");
00309                      break;
00310               case 'F':                                                             /* ssh configfile */
00311                      comm_append("-F");
00312                      comm_append(optarg);
00313                      break;
00314               default:                                                              /* help */
00315                      usage5();
00316               }
00317        }
00318 
00319        c = optind;
00320        if (hostname == NULL) {
00321               if (c <= argc) {
00322                      die (STATE_UNKNOWN, _("%s: You must provide a host name\n"), progname);
00323               }
00324               host_or_die(argv[c]);
00325               hostname = argv[c++];
00326        }
00327 
00328        if (strlen(remotecmd) == 0) {
00329               for (; c < argc; c++)
00330                      if (strlen(remotecmd) > 0)
00331                             asprintf (&remotecmd, "%s %s", remotecmd, argv[c]);
00332                      else
00333                             asprintf (&remotecmd, "%s", argv[c]);
00334        }
00335 
00336        if (commands > 1 || passive)
00337               asprintf (&remotecmd, "%s;echo STATUS CODE: $?;", remotecmd);
00338 
00339        if (remotecmd == NULL || strlen (remotecmd) <= 1)
00340               usage_va(_("No remotecmd"));
00341 
00342        comm_append(hostname);
00343        comm_append(remotecmd);
00344 
00345        return validate_arguments ();
00346 }
00347 
00348 
00349 void
00350 comm_append (const char *str)
00351 {
00352 
00353        if (++commargc > NP_MAXARGS)
00354               die(STATE_UNKNOWN, _("%s: Argument limit of %d exceeded\n"), progname, NP_MAXARGS);
00355 
00356        if ((commargv = (char **)realloc(commargv, (commargc+1) * sizeof(char *))) == NULL)
00357               die(STATE_UNKNOWN, _("Can not (re)allocate 'commargv' buffer\n"));
00358 
00359        commargv[commargc-1] = strdup(str);
00360        commargv[commargc] = NULL;
00361 
00362 }
00363 
00364 int
00365 validate_arguments (void)
00366 {
00367        if (remotecmd == NULL || hostname == NULL)
00368               return ERROR;
00369 
00370        if (passive && commands != services)
00371               die (STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname);
00372 
00373        if (passive && host_shortname == NULL)
00374               die (STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the nagios configs.\n"), progname);
00375 
00376        return OK;
00377 }
00378 
00379 
00380 void
00381 print_help (void)
00382 {
00383        print_revision (progname, NP_VERSION);
00384 
00385        printf ("Copyright (c) 1999 Karl DeBisschop <kdebisschop@users.sourceforge.net>\n");
00386        printf (COPYRIGHT, copyright, email);
00387 
00388        printf (_("This plugin uses SSH to execute commands on a remote host"));
00389 
00390   printf ("\n\n");
00391 
00392        print_usage ();
00393 
00394        printf (UT_HELP_VRSN);
00395 
00396        printf (UT_EXTRA_OPTS);
00397 
00398        printf (UT_HOST_PORT, 'p', "none");
00399 
00400        printf (UT_IPv46);
00401 
00402   printf (" %s\n", "-1, --proto1");
00403   printf ("    %s\n", _("tell ssh to use Protocol 1 [optional]"));
00404   printf (" %s\n", "-2, --proto2");
00405   printf ("    %s\n", _("tell ssh to use Protocol 2 [optional]"));
00406   printf (" %s\n", "-S, --skip-stdout[=n]");
00407   printf ("    %s\n", _("Ignore all or (if specified) first n lines on STDOUT [optional]"));
00408   printf (" %s\n", "-E, --skip-stderr[=n]");
00409   printf ("    %s\n", _("Ignore all or (if specified) first n lines on STDERR [optional]"));
00410   printf (" %s\n", "-f");
00411   printf ("    %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always return OK if ssh is executed"));
00412   printf (" %s\n","-C, --command='COMMAND STRING'");
00413   printf ("    %s\n", _("command to execute on the remote machine"));
00414   printf (" %s\n","-l, --logname=USERNAME");
00415   printf ("    %s\n", _("SSH user name on remote host [optional]"));
00416   printf (" %s\n","-i, --identity=KEYFILE");
00417   printf ("    %s\n", _("identity of an authorized key [optional]"));
00418   printf (" %s\n","-O, --output=FILE");
00419   printf ("    %s\n", _("external command file for nagios [optional]"));
00420   printf (" %s\n","-s, --services=LIST");
00421   printf ("    %s\n", _("list of nagios service names, separated by ':' [optional]"));
00422   printf (" %s\n","-n, --name=NAME");
00423   printf ("    %s\n", _("short name of host in nagios configuration [optional]"));
00424   printf (" %s\n","-o, --ssh-option=OPTION");
00425   printf ("    %s\n", _("Call ssh with '-o OPTION' (may be used multiple times) [optional]"));
00426   printf (" %s\n","-F, --configfile");
00427   printf ("    %s\n", _("Tell ssh to use this configfile [optional]"));
00428   printf (" %s\n","-q, --quiet");
00429   printf ("    %s\n", _("Tell ssh to suppress warning and diagnostic messages [optional]"));
00430        printf (UT_WARN_CRIT);
00431        printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
00432        printf (UT_VERBOSE);
00433        printf("\n");
00434   printf (" %s\n", _("The most common mode of use is to refer to a local identity file with"));
00435   printf (" %s\n", _("the '-i' option. In this mode, the identity pair should have a null"));
00436   printf (" %s\n", _("passphrase and the public key should be listed in the authorized_keys"));
00437   printf (" %s\n", _("file of the remote host. Usually the key will be restricted to running"));
00438   printf (" %s\n", _("only one command on the remote server. If the remote SSH server tracks"));
00439   printf (" %s\n", _("invocation arguments, the one remote program may be an agent that can"));
00440   printf (" %s\n", _("execute additional commands as proxy"));
00441   printf("\n");
00442   printf (" %s\n", _("To use passive mode, provide multiple '-C' options, and provide"));
00443   printf (" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)"));
00444   printf ("\n");
00445   printf ("%s\n", _("Examples:"));
00446   printf (" %s\n", "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo");
00447   printf (" %s\n", "$ cat /tmp/foo");
00448   printf (" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days");
00449   printf (" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days");
00450   printf (" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c3;0; up 2 days");
00451 
00452        printf(UT_SUPPORT);
00453 }
00454 
00455 
00456 
00457 void
00458 print_usage (void)
00459 {
00460        printf ("%s\n", _("Usage:"));
00461        printf (" %s -H <host> -C <command> [-fqv] [-1|-2] [-4|-6]\n"
00462                "       [-S [lines]] [-E [lines]] [-t timeout] [-i identity]\n"
00463                "       [-l user] [-n name] [-s servicelist] [-O outputfile]\n"
00464                "       [-p port] [-o ssh-option] [-F configfile]\n",
00465                progname);
00466 }