4
0

check_dbi.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  1. /*****************************************************************************
  2. *
  3. * Nagios check_dbi plugin
  4. *
  5. * License: GPL
  6. * Copyright (c) 2011-2014 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 = "2014";
  33. const char *email = "devel@nagios-plugins.org";
  34. #include "common.h"
  35. #include "utils.h"
  36. #include "netutils.h"
  37. #include "regex.h"
  38. /* required for NAN */
  39. #ifndef _ISOC99_SOURCE
  40. #define _ISOC99_SOURCE
  41. #endif
  42. #include <assert.h>
  43. #include <math.h>
  44. #include <dbi/dbi.h>
  45. #include <stdarg.h>
  46. typedef enum {
  47. METRIC_CONN_TIME,
  48. METRIC_SERVER_VERSION,
  49. METRIC_QUERY_RESULT,
  50. METRIC_QUERY_TIME,
  51. } np_dbi_metric_t;
  52. typedef enum {
  53. TYPE_NUMERIC,
  54. TYPE_STRING,
  55. } np_dbi_type_t;
  56. typedef struct {
  57. char *key;
  58. char *value;
  59. } driver_option_t;
  60. char *host = NULL;
  61. int verbose = 0;
  62. char *warning_range = NULL;
  63. char *critical_range = NULL;
  64. thresholds *dbi_thresholds = NULL;
  65. char *expect = NULL;
  66. regex_t expect_re;
  67. char *expect_re_str = NULL;
  68. int expect_re_cflags = 0;
  69. np_dbi_metric_t metric = METRIC_QUERY_RESULT;
  70. np_dbi_type_t type = TYPE_NUMERIC;
  71. char *np_dbi_driver = NULL;
  72. driver_option_t *np_dbi_options = NULL;
  73. int np_dbi_options_num = 0;
  74. char *np_dbi_database = NULL;
  75. char *np_dbi_query = NULL;
  76. int process_arguments (int, char **);
  77. int validate_arguments (void);
  78. void print_usage (void);
  79. void print_help (void);
  80. double timediff (struct timeval, struct timeval);
  81. void np_dbi_print_error (dbi_conn, char *, ...);
  82. int do_query (dbi_conn, const char **, double *, double *);
  83. int
  84. main (int argc, char **argv)
  85. {
  86. int status = STATE_UNKNOWN;
  87. dbi_driver driver;
  88. dbi_conn conn;
  89. unsigned int server_version;
  90. struct timeval start_timeval, end_timeval;
  91. double conn_time = 0.0;
  92. double query_time = 0.0;
  93. const char *query_val_str = NULL;
  94. double query_val = 0.0;
  95. int i;
  96. setlocale (LC_ALL, "");
  97. bindtextdomain (PACKAGE, LOCALEDIR);
  98. textdomain (PACKAGE);
  99. /* Parse extra opts if any */
  100. argv = np_extra_opts (&argc, argv, progname);
  101. if (process_arguments (argc, argv) == ERROR)
  102. usage4 (_("Could not parse arguments"));
  103. /* Set signal handling and alarm */
  104. if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) {
  105. usage4 (_("Cannot catch SIGALRM"));
  106. }
  107. alarm (timeout_interval);
  108. if (verbose > 2)
  109. printf ("Initializing DBI\n");
  110. if (dbi_initialize (NULL) < 0) {
  111. printf ("UNKNOWN - failed to initialize DBI; possibly you don't have any drivers installed.\n");
  112. return STATE_UNKNOWN;
  113. }
  114. if (verbose)
  115. printf ("Opening DBI driver '%s'\n", np_dbi_driver);
  116. driver = dbi_driver_open (np_dbi_driver);
  117. if (! driver) {
  118. printf ("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n",
  119. np_dbi_driver);
  120. printf ("Known drivers:\n");
  121. for (driver = dbi_driver_list (NULL); driver; driver = dbi_driver_list (driver)) {
  122. printf (" - %s\n", dbi_driver_get_name (driver));
  123. }
  124. return STATE_UNKNOWN;
  125. }
  126. /* make a connection to the database */
  127. gettimeofday (&start_timeval, NULL);
  128. conn = dbi_conn_open (driver);
  129. if (! conn) {
  130. printf ("UNKNOWN - failed top open connection object.\n");
  131. dbi_conn_close (conn);
  132. return STATE_UNKNOWN;
  133. }
  134. for (i = 0; i < np_dbi_options_num; ++i) {
  135. const char *opt;
  136. if (verbose > 1)
  137. printf ("Setting DBI driver option '%s' to '%s'\n",
  138. np_dbi_options[i].key, np_dbi_options[i].value);
  139. if (! dbi_conn_set_option (conn, np_dbi_options[i].key, np_dbi_options[i].value))
  140. continue;
  141. /* else: status != 0 */
  142. np_dbi_print_error (conn, "UNKNOWN - failed to set option '%s' to '%s'",
  143. np_dbi_options[i].key, np_dbi_options[i].value);
  144. printf ("Known driver options:\n");
  145. for (opt = dbi_conn_get_option_list (conn, NULL); opt;
  146. opt = dbi_conn_get_option_list (conn, opt)) {
  147. printf (" - %s\n", opt);
  148. }
  149. dbi_conn_close (conn);
  150. return STATE_UNKNOWN;
  151. }
  152. if (host) {
  153. if (verbose > 1)
  154. printf ("Setting DBI driver option 'host' to '%s'\n", host);
  155. dbi_conn_set_option (conn, "host", host);
  156. }
  157. if (verbose) {
  158. const char *dbname, *host;
  159. dbname = dbi_conn_get_option (conn, "dbname");
  160. host = dbi_conn_get_option (conn, "host");
  161. if (! dbname)
  162. dbname = "<unspecified>";
  163. if (! host)
  164. host = "<unspecified>";
  165. printf ("Connecting to database '%s' at host '%s'\n",
  166. dbname, host);
  167. }
  168. if (dbi_conn_connect (conn) < 0) {
  169. np_dbi_print_error (conn, "UNKNOWN - failed to connect to database");
  170. return STATE_UNKNOWN;
  171. }
  172. gettimeofday (&end_timeval, NULL);
  173. conn_time = timediff (start_timeval, end_timeval);
  174. server_version = dbi_conn_get_engine_version (conn);
  175. if (verbose)
  176. printf ("Connected to server version %u\n", server_version);
  177. if (metric == METRIC_SERVER_VERSION)
  178. status = get_status (server_version, dbi_thresholds);
  179. if (verbose)
  180. printf ("Time elapsed: %f\n", conn_time);
  181. if (metric == METRIC_CONN_TIME)
  182. status = get_status (conn_time, dbi_thresholds);
  183. /* select a database */
  184. if (np_dbi_database) {
  185. if (verbose > 1)
  186. printf ("Selecting database '%s'\n", np_dbi_database);
  187. if (dbi_conn_select_db (conn, np_dbi_database)) {
  188. np_dbi_print_error (conn, "UNKNOWN - failed to select database '%s'",
  189. np_dbi_database);
  190. return STATE_UNKNOWN;
  191. }
  192. }
  193. if (np_dbi_query) {
  194. /* execute query */
  195. status = do_query (conn, &query_val_str, &query_val, &query_time);
  196. if (status != STATE_OK)
  197. /* do_query prints an error message in this case */
  198. return status;
  199. if (metric == METRIC_QUERY_RESULT) {
  200. if (expect) {
  201. if ((! query_val_str) || strcmp (query_val_str, expect))
  202. status = STATE_CRITICAL;
  203. else
  204. status = STATE_OK;
  205. }
  206. else if (expect_re_str) {
  207. int err;
  208. err = regexec (&expect_re, query_val_str, 0, NULL, /* flags = */ 0);
  209. if (! err)
  210. status = STATE_OK;
  211. else if (err == REG_NOMATCH)
  212. status = STATE_CRITICAL;
  213. else {
  214. char errmsg[1024];
  215. regerror (err, &expect_re, errmsg, sizeof (errmsg));
  216. printf ("ERROR - failed to execute regular expression: %s\n",
  217. errmsg);
  218. status = STATE_CRITICAL;
  219. }
  220. }
  221. else
  222. status = get_status (query_val, dbi_thresholds);
  223. }
  224. else if (metric == METRIC_QUERY_TIME)
  225. status = get_status (query_time, dbi_thresholds);
  226. }
  227. if (verbose)
  228. printf("Closing connection\n");
  229. dbi_conn_close (conn);
  230. /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error
  231. * which should have been reported and handled (abort) before
  232. * ... unless we expected a string to be returned */
  233. assert ((metric != METRIC_QUERY_RESULT) || (! isnan (query_val))
  234. || (type == TYPE_STRING));
  235. assert ((type != TYPE_STRING) || (expect || expect_re_str));
  236. printf ("%s - connection time: %fs", state_text (status), conn_time);
  237. if (np_dbi_query) {
  238. if (type == TYPE_STRING) {
  239. assert (expect || expect_re_str);
  240. printf (", '%s' returned '%s' in %fs", np_dbi_query,
  241. query_val_str ? query_val_str : "<nothing>", query_time);
  242. if (status != STATE_OK) {
  243. if (expect)
  244. printf (" (expected '%s')", expect);
  245. else if (expect_re_str)
  246. printf (" (expected regex /%s/%s)", expect_re_str,
  247. ((expect_re_cflags & REG_ICASE) ? "i" : ""));
  248. }
  249. }
  250. else if (isnan (query_val))
  251. printf (", '%s' query execution time: %fs", np_dbi_query, query_time);
  252. else
  253. printf (", '%s' returned %f in %fs", np_dbi_query, query_val, query_time);
  254. }
  255. printf (" | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time,
  256. ((metric == METRIC_CONN_TIME) && warning_range) ? warning_range : "",
  257. ((metric == METRIC_CONN_TIME) && critical_range) ? critical_range : "",
  258. server_version,
  259. ((metric == METRIC_SERVER_VERSION) && warning_range) ? warning_range : "",
  260. ((metric == METRIC_SERVER_VERSION) && critical_range) ? critical_range : "");
  261. if (np_dbi_query) {
  262. if (! isnan (query_val)) /* this is also true when -e is used */
  263. printf (" query=%f;%s;%s;;", query_val,
  264. ((metric == METRIC_QUERY_RESULT) && warning_range) ? warning_range : "",
  265. ((metric == METRIC_QUERY_RESULT) && critical_range) ? critical_range : "");
  266. printf (" querytime=%fs;%s;%s;0;", query_time,
  267. ((metric == METRIC_QUERY_TIME) && warning_range) ? warning_range : "",
  268. ((metric == METRIC_QUERY_TIME) && critical_range) ? critical_range : "");
  269. }
  270. printf ("\n");
  271. return status;
  272. }
  273. /* process command-line arguments */
  274. int
  275. process_arguments (int argc, char **argv)
  276. {
  277. int c;
  278. int option = 0;
  279. static struct option longopts[] = {
  280. STD_LONG_OPTS,
  281. {"expect", required_argument, 0, 'e'},
  282. {"regex", required_argument, 0, 'r'},
  283. {"regexi", required_argument, 0, 'R'},
  284. {"metric", required_argument, 0, 'm'},
  285. {"driver", required_argument, 0, 'd'},
  286. {"option", required_argument, 0, 'o'},
  287. {"query", required_argument, 0, 'q'},
  288. {"database", required_argument, 0, 'D'},
  289. {0, 0, 0, 0}
  290. };
  291. while (1) {
  292. c = getopt_long (argc, argv, "Vvht:c:w:e:r:R:m:H:d:o:q:D:",
  293. longopts, &option);
  294. if (c == EOF)
  295. break;
  296. switch (c) {
  297. case '?': /* usage */
  298. usage5 ();
  299. case 'h': /* help */
  300. print_help ();
  301. exit (STATE_OK);
  302. case 'V': /* version */
  303. print_revision (progname, NP_VERSION);
  304. exit (STATE_OK);
  305. case 'c': /* critical range */
  306. critical_range = optarg;
  307. type = TYPE_NUMERIC;
  308. break;
  309. case 'w': /* warning range */
  310. warning_range = optarg;
  311. type = TYPE_NUMERIC;
  312. break;
  313. case 'e':
  314. expect = optarg;
  315. type = TYPE_STRING;
  316. break;
  317. case 'R':
  318. expect_re_cflags = REG_ICASE;
  319. /* fall through */
  320. case 'r':
  321. {
  322. int err;
  323. expect_re_cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
  324. expect_re_str = optarg;
  325. type = TYPE_STRING;
  326. err = regcomp (&expect_re, expect_re_str, expect_re_cflags);
  327. if (err) {
  328. char errmsg[1024];
  329. regerror (err, &expect_re, errmsg, sizeof (errmsg));
  330. printf ("ERROR - failed to compile regular expression: %s\n",
  331. errmsg);
  332. return ERROR;
  333. }
  334. break;
  335. }
  336. case 'm':
  337. if (! strcasecmp (optarg, "CONN_TIME"))
  338. metric = METRIC_CONN_TIME;
  339. else if (! strcasecmp (optarg, "SERVER_VERSION"))
  340. metric = METRIC_SERVER_VERSION;
  341. else if (! strcasecmp (optarg, "QUERY_RESULT"))
  342. metric = METRIC_QUERY_RESULT;
  343. else if (! strcasecmp (optarg, "QUERY_TIME"))
  344. metric = METRIC_QUERY_TIME;
  345. else
  346. usage2 (_("Invalid metric"), optarg);
  347. break;
  348. case 't': /* timeout */
  349. timeout_interval = parse_timeout_string(optarg);
  350. break;
  351. case 'H': /* host */
  352. if (!is_host (optarg))
  353. usage2 (_("Invalid hostname/address"), optarg);
  354. else
  355. host = optarg;
  356. break;
  357. case 'v':
  358. verbose++;
  359. break;
  360. case 'd':
  361. np_dbi_driver = optarg;
  362. break;
  363. case 'o':
  364. {
  365. driver_option_t *new;
  366. char *k, *v;
  367. k = optarg;
  368. v = strchr (k, (int)'=');
  369. if (! v)
  370. usage2 (_("Option must be '<key>=<value>'"), optarg);
  371. *v = '\0';
  372. ++v;
  373. new = realloc (np_dbi_options,
  374. (np_dbi_options_num + 1) * sizeof (*new));
  375. if (! new) {
  376. printf ("UNKNOWN - failed to reallocate memory\n");
  377. exit (STATE_UNKNOWN);
  378. }
  379. np_dbi_options = new;
  380. new = np_dbi_options + np_dbi_options_num;
  381. ++np_dbi_options_num;
  382. new->key = k;
  383. new->value = v;
  384. }
  385. break;
  386. case 'q':
  387. np_dbi_query = optarg;
  388. break;
  389. case 'D':
  390. np_dbi_database = optarg;
  391. break;
  392. }
  393. }
  394. set_thresholds (&dbi_thresholds, warning_range, critical_range);
  395. return validate_arguments ();
  396. }
  397. int
  398. validate_arguments ()
  399. {
  400. if (! np_dbi_driver)
  401. usage ("Must specify a DBI driver");
  402. if (((metric == METRIC_QUERY_RESULT) || (metric == METRIC_QUERY_TIME))
  403. && (! np_dbi_query))
  404. usage ("Must specify a query to execute (metric == QUERY_RESULT)");
  405. if ((metric != METRIC_CONN_TIME)
  406. && (metric != METRIC_SERVER_VERSION)
  407. && (metric != METRIC_QUERY_RESULT)
  408. && (metric != METRIC_QUERY_TIME))
  409. usage ("Invalid metric specified");
  410. if (expect && (warning_range || critical_range || expect_re_str))
  411. usage ("Do not mix -e and -w/-c/-r/-R");
  412. if (expect_re_str && (warning_range || critical_range || expect))
  413. usage ("Do not mix -r/-R and -w/-c/-e");
  414. if (expect && (metric != METRIC_QUERY_RESULT))
  415. usage ("Option -e requires metric QUERY_RESULT");
  416. if (expect_re_str && (metric != METRIC_QUERY_RESULT))
  417. usage ("Options -r/-R require metric QUERY_RESULT");
  418. return OK;
  419. }
  420. void
  421. print_help (void)
  422. {
  423. print_revision (progname, NP_VERSION);
  424. printf (COPYRIGHT, copyright, email);
  425. printf (_("This program connects to an (SQL) database using DBI and checks the\n"
  426. "specified metric against threshold levels. The default metric is\n"
  427. "the result of the specified query.\n"));
  428. printf ("\n\n");
  429. print_usage ();
  430. printf (UT_HELP_VRSN);
  431. /* include this conditionally to avoid 'zero-length printf format string'
  432. * compiler warnings */
  433. #ifdef NP_EXTRA_OPTS
  434. printf (UT_EXTRA_OPTS);
  435. #endif
  436. printf ("\n");
  437. printf (" %s\n", "-d, --driver=STRING");
  438. printf (" %s\n", _("DBI driver to use"));
  439. printf (" %s\n", "-o, --option=STRING");
  440. printf (" %s\n", _("DBI driver options"));
  441. printf (" %s\n", "-q, --query=STRING");
  442. printf (" %s\n", _("query to execute"));
  443. printf ("\n");
  444. printf (UT_WARN_CRIT_RANGE);
  445. printf (" %s\n", "-e, --expect=STRING");
  446. printf (" %s\n", _("String to expect as query result"));
  447. printf (" %s\n", _("Do not mix with -w, -c, -r, or -R!"));
  448. printf (" %s\n", "-r, --regex=REGEX");
  449. printf (" %s\n", _("Extended POSIX regular expression to check query result against"));
  450. printf (" %s\n", _("Do not mix with -w, -c, -e, or -R!"));
  451. printf (" %s\n", "-R, --regexi=REGEX");
  452. printf (" %s\n", _("Case-insensitive extended POSIX regex to check query result against"));
  453. printf (" %s\n", _("Do not mix with -w, -c, -e, or -r!"));
  454. printf (" %s\n", "-m, --metric=METRIC");
  455. printf (" %s\n", _("Metric to check thresholds against. Available metrics:"));
  456. printf (" CONN_TIME - %s\n", _("time used for setting up the database connection"));
  457. printf (" QUERY_RESULT - %s\n", _("result (first column of first row) of the query"));
  458. printf (" QUERY_TIME - %s\n", _("time used to execute the query"));
  459. printf (" %s\n", _("(ignore the query result)"));
  460. printf ("\n");
  461. printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
  462. printf (UT_VERBOSE);
  463. printf ("\n");
  464. printf (" %s\n", _("A DBI driver (-d option) is required. If the specified metric operates"));
  465. printf (" %s\n\n", _("on a query, one has to be specified (-q option)."));
  466. printf (" %s\n", _("This plugin connects to an (SQL) database using libdbi and, optionally,"));
  467. printf (" %s\n", _("executes the specified query. The first column of the first row of the"));
  468. printf (" %s\n", _("result will be parsed and, in QUERY_RESULT mode, compared with the"));
  469. printf (" %s\n", _("warning and critical ranges. The result from the query has to be numeric"));
  470. printf (" %s\n\n", _("(strings representing numbers are fine)."));
  471. printf (" %s\n", _("The number and type of required DBI driver options depends on the actual"));
  472. printf (" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/"));
  473. printf (" %s\n\n", _("for details."));
  474. printf (" %s\n", _("Examples:"));
  475. printf (" check_dbi -d pgsql -o username=postgres -m QUERY_RESULT \\\n");
  476. printf (" -q 'SELECT COUNT(*) FROM pg_stat_activity' -w 5 -c 10\n");
  477. printf (" Warning if more than five connections; critical if more than ten.\n\n");
  478. printf (" check_dbi -d mysql -H localhost -o username=user -o password=secret \\\n");
  479. printf (" -q 'SELECT COUNT(*) FROM logged_in_users -w 5:20 -c 0:50\n");
  480. printf (" Warning if less than 5 or more than 20 users are logged in; critical\n");
  481. printf (" if more than 50 users.\n\n");
  482. printf (" check_dbi -d firebird -o username=user -o password=secret -o dbname=foo \\\n");
  483. printf (" -m CONN_TIME -w 0.5 -c 2\n");
  484. printf (" Warning if connecting to the database takes more than half of a second;\n");
  485. printf (" critical if it takes more than 2 seconds.\n\n");
  486. printf (" check_dbi -d mysql -H localhost -o username=user \\\n");
  487. printf (" -q 'SELECT concat(@@version, \" \", @@version_comment)' \\\n");
  488. printf (" -r '^5\\.[01].*MySQL Enterprise Server'\n");
  489. printf (" Critical if the database server is not a MySQL enterprise server in either\n");
  490. printf (" version 5.0.x or 5.1.x.\n\n");
  491. printf (" check_dbi -d pgsql -u username=user -m SERVER_VERSION \\\n");
  492. printf (" -w 090000:090099 -c 090000:090199\n");
  493. printf (" Warn if the PostgreSQL server version is not 9.0.x; critical if the version\n");
  494. printf (" is less than 9.x or higher than 9.1.x.\n");
  495. printf (UT_SUPPORT);
  496. }
  497. void
  498. print_usage (void)
  499. {
  500. printf ("%s\n", _("Usage:"));
  501. printf ("%s -d <DBI driver> [-o <DBI driver option> [...]] [-q <query>]\n", progname);
  502. printf (" [-H <host>] [-c <critical range>] [-w <warning range>] [-m <metric>]\n");
  503. printf (" [-e <string>] [-r|-R <regex>]\n");
  504. }
  505. #define CHECK_IGNORE_ERROR(s) \
  506. do { \
  507. if (metric != METRIC_QUERY_RESULT) \
  508. return (s); \
  509. } while (0)
  510. const char *
  511. get_field_str (dbi_conn conn, dbi_result res, unsigned short field_type)
  512. {
  513. const char *str;
  514. if (field_type != DBI_TYPE_STRING) {
  515. printf ("CRITICAL - result value is not a string\n");
  516. return NULL;
  517. }
  518. str = dbi_result_get_string_idx (res, 1);
  519. if ((! str) || (strcmp (str, "ERROR") == 0)) {
  520. CHECK_IGNORE_ERROR (NULL);
  521. np_dbi_print_error (conn, "CRITICAL - failed to fetch string value");
  522. return NULL;
  523. }
  524. if ((verbose && (type == TYPE_STRING)) || (verbose > 2))
  525. printf ("Query returned string '%s'\n", str);
  526. return str;
  527. }
  528. double
  529. get_field (dbi_conn conn, dbi_result res, unsigned short *field_type)
  530. {
  531. double val = NAN;
  532. if (*field_type == DBI_TYPE_INTEGER) {
  533. val = (double)dbi_result_get_longlong_idx (res, 1);
  534. }
  535. else if (*field_type == DBI_TYPE_DECIMAL) {
  536. val = dbi_result_get_double_idx (res, 1);
  537. }
  538. else if (*field_type == DBI_TYPE_STRING) {
  539. const char *val_str;
  540. char *endptr = NULL;
  541. val_str = get_field_str (conn, res, *field_type);
  542. if (! val_str) {
  543. CHECK_IGNORE_ERROR (NAN);
  544. *field_type = DBI_TYPE_ERROR;
  545. return NAN;
  546. }
  547. val = strtod (val_str, &endptr);
  548. if (endptr == val_str) {
  549. CHECK_IGNORE_ERROR (NAN);
  550. printf ("CRITICAL - result value is not a numeric: %s\n", val_str);
  551. *field_type = DBI_TYPE_ERROR;
  552. return NAN;
  553. }
  554. else if ((endptr != NULL) && (*endptr != '\0')) {
  555. if (verbose)
  556. printf ("Garbage after value: %s\n", endptr);
  557. }
  558. }
  559. else {
  560. CHECK_IGNORE_ERROR (NAN);
  561. printf ("CRITICAL - cannot parse value of type %s (%i)\n",
  562. (*field_type == DBI_TYPE_BINARY)
  563. ? "BINARY"
  564. : (*field_type == DBI_TYPE_DATETIME)
  565. ? "DATETIME"
  566. : "<unknown>",
  567. *field_type);
  568. *field_type = DBI_TYPE_ERROR;
  569. return NAN;
  570. }
  571. return val;
  572. }
  573. double
  574. get_query_result (dbi_conn conn, dbi_result res, const char **res_val_str, double *res_val)
  575. {
  576. unsigned short field_type;
  577. double val = NAN;
  578. if (dbi_result_get_numrows (res) == DBI_ROW_ERROR) {
  579. CHECK_IGNORE_ERROR (STATE_OK);
  580. np_dbi_print_error (conn, "CRITICAL - failed to fetch rows");
  581. return STATE_CRITICAL;
  582. }
  583. if (dbi_result_get_numrows (res) < 1) {
  584. CHECK_IGNORE_ERROR (STATE_OK);
  585. printf ("WARNING - no rows returned\n");
  586. return STATE_WARNING;
  587. }
  588. if (dbi_result_get_numfields (res) == DBI_FIELD_ERROR) {
  589. CHECK_IGNORE_ERROR (STATE_OK);
  590. np_dbi_print_error (conn, "CRITICAL - failed to fetch fields");
  591. return STATE_CRITICAL;
  592. }
  593. if (dbi_result_get_numfields (res) < 1) {
  594. CHECK_IGNORE_ERROR (STATE_OK);
  595. printf ("WARNING - no fields returned\n");
  596. return STATE_WARNING;
  597. }
  598. if (dbi_result_first_row (res) != 1) {
  599. CHECK_IGNORE_ERROR (STATE_OK);
  600. np_dbi_print_error (conn, "CRITICAL - failed to fetch first row");
  601. return STATE_CRITICAL;
  602. }
  603. field_type = dbi_result_get_field_type_idx (res, 1);
  604. if (field_type != DBI_TYPE_ERROR) {
  605. if (type == TYPE_STRING)
  606. /* the value will be freed in dbi_result_free */
  607. *res_val_str = strdup (get_field_str (conn, res, field_type));
  608. else
  609. val = get_field (conn, res, &field_type);
  610. }
  611. *res_val = val;
  612. if (field_type == DBI_TYPE_ERROR) {
  613. CHECK_IGNORE_ERROR (STATE_OK);
  614. np_dbi_print_error (conn, "CRITICAL - failed to fetch data");
  615. return STATE_CRITICAL;
  616. }
  617. dbi_result_free (res);
  618. return STATE_OK;
  619. }
  620. #undef CHECK_IGNORE_ERROR
  621. int
  622. do_query (dbi_conn conn, const char **res_val_str, double *res_val, double *res_time)
  623. {
  624. dbi_result res;
  625. struct timeval timeval_start, timeval_end;
  626. int status = STATE_OK;
  627. assert (np_dbi_query);
  628. if (verbose)
  629. printf ("Executing query '%s'\n", np_dbi_query);
  630. gettimeofday (&timeval_start, NULL);
  631. res = dbi_conn_query (conn, np_dbi_query);
  632. if (! res) {
  633. np_dbi_print_error (conn, "CRITICAL - failed to execute query '%s'", np_dbi_query);
  634. return STATE_CRITICAL;
  635. }
  636. status = get_query_result (conn, res, res_val_str, res_val);
  637. gettimeofday (&timeval_end, NULL);
  638. *res_time = timediff (timeval_start, timeval_end);
  639. if (verbose)
  640. printf ("Time elapsed: %f\n", *res_time);
  641. return status;
  642. }
  643. double
  644. timediff (struct timeval start, struct timeval end)
  645. {
  646. double diff;
  647. while (start.tv_usec > end.tv_usec) {
  648. --end.tv_sec;
  649. end.tv_usec += 1000000;
  650. }
  651. diff = (double)(end.tv_sec - start.tv_sec)
  652. + (double)(end.tv_usec - start.tv_usec) / 1000000.0;
  653. return diff;
  654. }
  655. void
  656. np_dbi_print_error (dbi_conn conn, char *fmt, ...)
  657. {
  658. const char *errmsg = NULL;
  659. va_list ap;
  660. va_start (ap, fmt);
  661. dbi_conn_error (conn, &errmsg);
  662. vprintf (fmt, ap);
  663. printf (": %s\n", errmsg);
  664. va_end (ap);
  665. }