check_dbi.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. /*****************************************************************************
  2. *
  3. * Nagios check_dbi plugin
  4. *
  5. * License: GPL
  6. * Copyright (c) 2011 Nagios Plugins Development Team
  7. * Author: Sebastian 'tokkee' Harl <sh@teamix.net>
  8. *
  9. * Description:
  10. *
  11. * This file contains the check_dbi plugin
  12. *
  13. * Runs an arbitrary (SQL) command and checks the result.
  14. *
  15. *
  16. * This program is free software: you can redistribute it and/or modify
  17. * it under the terms of the GNU General Public License as published by
  18. * the Free Software Foundation, either version 3 of the License, or
  19. * (at your option) any later version.
  20. *
  21. * This program is distributed in the hope that it will be useful,
  22. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. * GNU General Public License for more details.
  25. *
  26. * You should have received a copy of the GNU General Public License
  27. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  28. *
  29. *
  30. *****************************************************************************/
  31. const char *progname = "check_dbi";
  32. const char *copyright = "2011";
  33. const char *email = "nagiosplug-devel@lists.sourceforge.net";
  34. #include "common.h"
  35. #include "utils.h"
  36. #include "netutils.h"
  37. #include <assert.h>
  38. #include <dbi/dbi.h>
  39. #include <stdarg.h>
  40. typedef enum {
  41. METRIC_CONN_TIME,
  42. METRIC_QUERY_RESULT,
  43. } np_dbi_metric_t;
  44. typedef struct {
  45. char *key;
  46. char *value;
  47. } driver_option_t;
  48. char *host = NULL;
  49. int verbose = 0;
  50. char *warning_range = NULL;
  51. char *critical_range = NULL;
  52. thresholds *dbi_thresholds = NULL;
  53. np_dbi_metric_t metric = METRIC_QUERY_RESULT;
  54. char *np_dbi_driver = NULL;
  55. driver_option_t *np_dbi_options = NULL;
  56. int np_dbi_options_num = 0;
  57. char *np_dbi_database = NULL;
  58. char *np_dbi_query = NULL;
  59. int process_arguments (int, char **);
  60. int validate_arguments (void);
  61. void print_usage (void);
  62. void print_help (void);
  63. double timediff (struct timeval, struct timeval);
  64. void np_dbi_print_error (dbi_conn, char *, ...);
  65. int do_query (dbi_conn, double *, double *);
  66. int
  67. main (int argc, char **argv)
  68. {
  69. int status = STATE_UNKNOWN;
  70. dbi_driver driver;
  71. dbi_conn conn;
  72. struct timeval start_timeval, end_timeval;
  73. double conn_time = 0.0;
  74. double query_time = 0.0;
  75. double query_val = 0.0;
  76. int i;
  77. setlocale (LC_ALL, "");
  78. bindtextdomain (PACKAGE, LOCALEDIR);
  79. textdomain (PACKAGE);
  80. /* Parse extra opts if any */
  81. argv = np_extra_opts (&argc, argv, progname);
  82. if (process_arguments (argc, argv) == ERROR)
  83. usage4 (_("Could not parse arguments"));
  84. /* Set signal handling and alarm */
  85. if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) {
  86. usage4 (_("Cannot catch SIGALRM"));
  87. }
  88. alarm (timeout_interval);
  89. if (verbose > 2)
  90. printf ("Initializing DBI\n");
  91. if (dbi_initialize (NULL) < 0) {
  92. printf ("UNKNOWN - failed to initialize DBI.\n");
  93. return STATE_UNKNOWN;
  94. }
  95. if (verbose)
  96. printf ("Opening DBI driver '%s'\n", np_dbi_driver);
  97. driver = dbi_driver_open (np_dbi_driver);
  98. if (! driver) {
  99. printf ("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n",
  100. np_dbi_driver);
  101. printf ("Known drivers:\n");
  102. for (driver = dbi_driver_list (NULL); driver; driver = dbi_driver_list (driver)) {
  103. printf (" - %s\n", dbi_driver_get_name (driver));
  104. }
  105. return STATE_UNKNOWN;
  106. }
  107. /* make a connection to the database */
  108. gettimeofday (&start_timeval, NULL);
  109. conn = dbi_conn_open (driver);
  110. if (! conn) {
  111. printf ("UNKNOWN - failed top open connection object.\n");
  112. dbi_conn_close (conn);
  113. return STATE_UNKNOWN;
  114. }
  115. for (i = 0; i < np_dbi_options_num; ++i) {
  116. const char *opt;
  117. if (verbose > 1)
  118. printf ("Setting DBI driver option '%s' to '%s'\n",
  119. np_dbi_options[i].key, np_dbi_options[i].value);
  120. if (! dbi_conn_set_option (conn, np_dbi_options[i].key, np_dbi_options[i].value))
  121. continue;
  122. /* else: status != 0 */
  123. np_dbi_print_error (conn, "UNKNOWN - failed to set option '%s' to '%s'",
  124. np_dbi_options[i].key, np_dbi_options[i].value);
  125. printf ("Known driver options:\n");
  126. for (opt = dbi_conn_get_option_list (conn, NULL); opt;
  127. opt = dbi_conn_get_option_list (conn, opt)) {
  128. printf (" - %s\n", opt);
  129. }
  130. dbi_conn_close (conn);
  131. return STATE_UNKNOWN;
  132. }
  133. if (host) {
  134. if (verbose > 1)
  135. printf ("Setting DBI driver option 'host' to '%s'\n", host);
  136. dbi_conn_set_option (conn, "host", host);
  137. }
  138. if (verbose) {
  139. const char *dbname, *host;
  140. dbname = dbi_conn_get_option (conn, "dbname");
  141. host = dbi_conn_get_option (conn, "host");
  142. if (! dbname)
  143. dbname = "<unspecified>";
  144. if (! host)
  145. host = "<unspecified>";
  146. printf ("Connecting to database '%s' at host '%s'\n",
  147. dbname, host);
  148. }
  149. if (dbi_conn_connect (conn) < 0) {
  150. np_dbi_print_error (conn, "UNKOWN - failed to connect to database");
  151. return STATE_UNKNOWN;
  152. }
  153. gettimeofday (&end_timeval, NULL);
  154. conn_time = timediff (start_timeval, end_timeval);
  155. if (verbose)
  156. printf("Time elapsed: %f\n", conn_time);
  157. if (metric == METRIC_CONN_TIME)
  158. status = get_status (conn_time, dbi_thresholds);
  159. /* select a database */
  160. if (np_dbi_database) {
  161. if (verbose > 1)
  162. printf ("Selecting database '%s'\n", np_dbi_database);
  163. if (dbi_conn_select_db (conn, np_dbi_database)) {
  164. np_dbi_print_error (conn, "UNKOWN - failed to select database '%s'",
  165. np_dbi_database);
  166. return STATE_UNKNOWN;
  167. }
  168. }
  169. if (np_dbi_query) {
  170. /* execute query */
  171. status = do_query (conn, &query_val, &query_time);
  172. if (status != STATE_OK)
  173. /* do_query prints an error message in this case */
  174. return status;
  175. if (metric == METRIC_QUERY_RESULT)
  176. status = get_status (query_val, dbi_thresholds);
  177. }
  178. if (verbose)
  179. printf("Closing connection\n");
  180. dbi_conn_close (conn);
  181. printf ("%s - connection time: %fs", state_text (status), conn_time);
  182. if (np_dbi_query)
  183. printf (", '%s' returned %f in %fs", np_dbi_query, query_val, query_time);
  184. printf (" | conntime=%fs;%s;%s;0;", conn_time,
  185. ((metric == METRIC_CONN_TIME) && warning_range) ? warning_range : "",
  186. ((metric == METRIC_CONN_TIME) && critical_range) ? critical_range : "");
  187. if (np_dbi_query)
  188. printf (" query=%f;%s;%s;; querytime=%fs;;;0;", query_val,
  189. ((metric == METRIC_QUERY_RESULT) && warning_range) ? warning_range : "",
  190. ((metric == METRIC_QUERY_RESULT) && critical_range) ? critical_range : "",
  191. query_time);
  192. printf ("\n");
  193. return status;
  194. }
  195. /* process command-line arguments */
  196. int
  197. process_arguments (int argc, char **argv)
  198. {
  199. int c;
  200. int option = 0;
  201. static struct option longopts[] = {
  202. STD_LONG_OPTS,
  203. {"metric", required_argument, 0, 'm'},
  204. {"driver", required_argument, 0, 'd'},
  205. {"option", required_argument, 0, 'o'},
  206. {"query", required_argument, 0, 'q'},
  207. {"database", required_argument, 0, 'D'},
  208. {0, 0, 0, 0}
  209. };
  210. while (1) {
  211. c = getopt_long (argc, argv, "Vvht:c:w:m:H:d:o:q:D:",
  212. longopts, &option);
  213. if (c == EOF)
  214. break;
  215. switch (c) {
  216. case '?': /* usage */
  217. usage5 ();
  218. case 'h': /* help */
  219. print_help ();
  220. exit (STATE_OK);
  221. case 'V': /* version */
  222. print_revision (progname, NP_VERSION);
  223. exit (STATE_OK);
  224. case 'c': /* critical range */
  225. critical_range = optarg;
  226. break;
  227. case 'w': /* warning range */
  228. warning_range = optarg;
  229. break;
  230. case 'm':
  231. if (! strcasecmp (optarg, "CONN_TIME"))
  232. metric = METRIC_CONN_TIME;
  233. else if (! strcasecmp (optarg, "QUERY_RESULT"))
  234. metric = METRIC_QUERY_RESULT;
  235. else
  236. usage2 (_("Invalid metric"), optarg);
  237. break;
  238. case 't': /* timeout */
  239. if (!is_intnonneg (optarg))
  240. usage2 (_("Timeout interval must be a positive integer"), optarg);
  241. else
  242. timeout_interval = atoi (optarg);
  243. case 'H': /* host */
  244. if (!is_host (optarg))
  245. usage2 (_("Invalid hostname/address"), optarg);
  246. else
  247. host = optarg;
  248. break;
  249. case 'v':
  250. verbose++;
  251. break;
  252. case 'd':
  253. np_dbi_driver = optarg;
  254. break;
  255. case 'o':
  256. {
  257. driver_option_t *new;
  258. char *k, *v;
  259. k = optarg;
  260. v = strchr (k, (int)'=');
  261. if (! v)
  262. usage2 (_("Option must be '<key>=<value>'"), optarg);
  263. *v = '\0';
  264. ++v;
  265. new = realloc (np_dbi_options,
  266. (np_dbi_options_num + 1) * sizeof (*new));
  267. if (! new) {
  268. printf ("UNKOWN - failed to reallocate memory\n");
  269. exit (STATE_UNKNOWN);
  270. }
  271. np_dbi_options = new;
  272. new = np_dbi_options + np_dbi_options_num;
  273. ++np_dbi_options_num;
  274. new->key = k;
  275. new->value = v;
  276. }
  277. break;
  278. case 'q':
  279. np_dbi_query = optarg;
  280. break;
  281. case 'D':
  282. np_dbi_database = optarg;
  283. break;
  284. }
  285. }
  286. set_thresholds (&dbi_thresholds, warning_range, critical_range);
  287. return validate_arguments ();
  288. }
  289. int
  290. validate_arguments ()
  291. {
  292. if (! np_dbi_driver)
  293. usage ("Must specify a DBI driver");
  294. if ((metric == METRIC_QUERY_RESULT) && (! np_dbi_query))
  295. usage ("Must specify a query to execute (metric == QUERY_RESULT)");
  296. if ((metric != METRIC_CONN_TIME)
  297. && (metric != METRIC_QUERY_RESULT))
  298. usage ("Invalid metric specified");
  299. return OK;
  300. }
  301. void
  302. print_help (void)
  303. {
  304. print_revision (progname, NP_VERSION);
  305. printf (COPYRIGHT, copyright, email);
  306. printf (_("This program connects to an (SQL) database using DBI and checks the\n"
  307. "specified metric against threshold levels. The default metric is\n"
  308. "the result of the specified query.\n"));
  309. printf ("\n\n");
  310. print_usage ();
  311. printf (UT_HELP_VRSN);
  312. /* include this conditionally to avoid 'zero-length printf format string'
  313. * compiler warnings */
  314. #ifdef NP_EXTRA_OPTS
  315. printf (UT_EXTRA_OPTS);
  316. #endif
  317. printf ("\n");
  318. printf (" %s\n", "-d, --driver=STRING");
  319. printf (" %s\n", _("DBI driver to use"));
  320. printf (" %s\n", "-o, --option=STRING");
  321. printf (" %s\n", _("DBI driver options"));
  322. printf (" %s\n", "-q, --query=STRING");
  323. printf (" %s\n", _("query to execute"));
  324. printf ("\n");
  325. printf (UT_WARN_CRIT_RANGE);
  326. printf (" %s\n", "-m, --metric=METRIC");
  327. printf (" %s\n", _("Metric to check thresholds against. Available metrics:"));
  328. printf (" CONN_TIME - %s\n", _("time used for setting up the database connection"));
  329. printf (" QUERY_RESULT - %s\n", _("result (first column of first row) of the query"));
  330. printf ("\n");
  331. printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
  332. printf (UT_VERBOSE);
  333. printf ("\n");
  334. printf (" %s\n", _("A DBI driver (-d option) is required. If the specified metric operates"));
  335. printf (" %s\n\n", _("on a query, one has to be specified (-q option)."));
  336. printf (" %s\n", _("This plugin connects to an (SQL) database using libdbi and, optionally,"));
  337. printf (" %s\n", _("executes the specified query. The first column of the first row of the"));
  338. printf (" %s\n", _("result will be parsed and, in QUERY_RESULT mode, compared with the"));
  339. printf (" %s\n", _("warning and critical ranges. The result from the query has to be numeric"));
  340. printf (" %s\n\n", _("(strings representing numbers are fine)."));
  341. printf (" %s\n", _("The number and type of required DBI driver options depends on the actual"));
  342. printf (" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/"));
  343. printf (" %s\n", _("for details."));
  344. printf (UT_SUPPORT);
  345. }
  346. void
  347. print_usage (void)
  348. {
  349. printf ("%s\n", _("Usage:"));
  350. printf ("%s -d <DBI driver> [-o <DBI driver option> [...]] [-q <query>]\n", progname);
  351. printf (" [-H <host>] [-c <critical range>] [-w <warning range>] [-m <metric>]\n");
  352. }
  353. double
  354. get_field (dbi_conn conn, dbi_result res, unsigned short *field_type)
  355. {
  356. double val = 0.0;
  357. if (*field_type == DBI_TYPE_INTEGER) {
  358. val = (double)dbi_result_get_longlong_idx (res, 1);
  359. }
  360. else if (*field_type == DBI_TYPE_DECIMAL) {
  361. val = dbi_result_get_double_idx (res, 1);
  362. }
  363. else if (*field_type == DBI_TYPE_STRING) {
  364. const char *val_str;
  365. char *endptr = NULL;
  366. val_str = dbi_result_get_string_idx (res, 1);
  367. if ((! val_str) || (strcmp (val_str, "ERROR") == 0)) {
  368. np_dbi_print_error (conn, "CRITICAL - failed to fetch string value");
  369. *field_type = DBI_TYPE_ERROR;
  370. return 0.0;
  371. }
  372. if (verbose > 2)
  373. printf ("Query returned string '%s'\n", val_str);
  374. val = strtod (val_str, &endptr);
  375. if (endptr == val_str) {
  376. printf ("CRITICAL - result value is not a numeric: %s\n", val_str);
  377. *field_type = DBI_TYPE_ERROR;
  378. return 0.0;
  379. }
  380. else if ((endptr != NULL) && (*endptr != '\0')) {
  381. if (verbose)
  382. printf ("Garbage after value: %s\n", endptr);
  383. }
  384. }
  385. else {
  386. printf ("CRITICAL - cannot parse value of type %s (%i)\n",
  387. (*field_type == DBI_TYPE_BINARY)
  388. ? "BINARY"
  389. : (*field_type == DBI_TYPE_DATETIME)
  390. ? "DATETIME"
  391. : "<unknown>",
  392. *field_type);
  393. *field_type = DBI_TYPE_ERROR;
  394. return 0.0;
  395. }
  396. return val;
  397. }
  398. int
  399. do_query (dbi_conn conn, double *res_val, double *res_time)
  400. {
  401. dbi_result res;
  402. unsigned short field_type;
  403. double val = 0.0;
  404. struct timeval timeval_start, timeval_end;
  405. assert (np_dbi_query);
  406. if (verbose)
  407. printf ("Executing query '%s'\n", np_dbi_query);
  408. gettimeofday (&timeval_start, NULL);
  409. res = dbi_conn_query (conn, np_dbi_query);
  410. if (! res) {
  411. np_dbi_print_error (conn, "CRITICAL - failed to execute query '%s'", np_dbi_query);
  412. return STATE_CRITICAL;
  413. }
  414. if (dbi_result_get_numrows (res) == DBI_ROW_ERROR) {
  415. np_dbi_print_error (conn, "CRITICAL - failed to fetch rows");
  416. return STATE_CRITICAL;
  417. }
  418. if (dbi_result_get_numrows (res) < 1) {
  419. printf ("WARNING - no rows returned\n");
  420. return STATE_WARNING;
  421. }
  422. if (dbi_result_get_numfields (res) == DBI_FIELD_ERROR) {
  423. np_dbi_print_error (conn, "CRITICAL - failed to fetch fields");
  424. return STATE_CRITICAL;
  425. }
  426. if (dbi_result_get_numfields (res) < 1) {
  427. printf ("WARNING - no fields returned\n");
  428. return STATE_WARNING;
  429. }
  430. if (dbi_result_first_row (res) != 1) {
  431. np_dbi_print_error (conn, "CRITICAL - failed to fetch first row");
  432. return STATE_CRITICAL;
  433. }
  434. field_type = dbi_result_get_field_type_idx (res, 1);
  435. if (field_type != DBI_TYPE_ERROR)
  436. val = get_field (conn, res, &field_type);
  437. gettimeofday (&timeval_end, NULL);
  438. *res_time = timediff (timeval_start, timeval_end);
  439. if (field_type == DBI_TYPE_ERROR) {
  440. np_dbi_print_error (conn, "CRITICAL - failed to fetch data");
  441. return STATE_CRITICAL;
  442. }
  443. *res_val = val;
  444. dbi_result_free (res);
  445. return STATE_OK;
  446. }
  447. double
  448. timediff (struct timeval start, struct timeval end)
  449. {
  450. double diff;
  451. while (start.tv_usec > end.tv_usec) {
  452. --end.tv_sec;
  453. end.tv_usec += 1000000;
  454. }
  455. diff = (double)(end.tv_sec - start.tv_sec)
  456. + (double)(end.tv_usec - start.tv_usec) / 1000000.0;
  457. return diff;
  458. }
  459. void
  460. np_dbi_print_error (dbi_conn conn, char *fmt, ...)
  461. {
  462. const char *errmsg = NULL;
  463. va_list ap;
  464. va_start (ap, fmt);
  465. dbi_conn_error (conn, &errmsg);
  466. vprintf (fmt, ap);
  467. printf (": %s\n", errmsg);
  468. va_end (ap);
  469. }