check_dbi.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  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 <dbi/dbi.h>
  38. #include <stdarg.h>
  39. typedef struct {
  40. char *key;
  41. char *value;
  42. } driver_option_t;
  43. char *host = NULL;
  44. int verbose = 0;
  45. char *warning_range = NULL;
  46. char *critical_range = NULL;
  47. thresholds *query_thresholds = NULL;
  48. char *np_dbi_driver = NULL;
  49. driver_option_t *np_dbi_options = NULL;
  50. int np_dbi_options_num = 0;
  51. char *np_dbi_database = NULL;
  52. char *np_dbi_query = NULL;
  53. int process_arguments (int, char **);
  54. int validate_arguments (void);
  55. void print_usage (void);
  56. void print_help (void);
  57. void np_dbi_print_error (dbi_conn, char *, ...);
  58. int do_query (dbi_conn, double *);
  59. int
  60. main (int argc, char **argv)
  61. {
  62. int status = STATE_UNKNOWN;
  63. dbi_driver driver;
  64. dbi_conn conn;
  65. struct timeval start_timeval, end_timeval;
  66. double elapsed_time;
  67. double query_val = 0.0;
  68. int i;
  69. setlocale (LC_ALL, "");
  70. bindtextdomain (PACKAGE, LOCALEDIR);
  71. textdomain (PACKAGE);
  72. /* Parse extra opts if any */
  73. argv = np_extra_opts (&argc, argv, progname);
  74. if (process_arguments (argc, argv) == ERROR)
  75. usage4 (_("Could not parse arguments"));
  76. /* Set signal handling and alarm */
  77. if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) {
  78. usage4 (_("Cannot catch SIGALRM"));
  79. }
  80. alarm (timeout_interval);
  81. if (verbose > 2)
  82. printf ("Initializing DBI\n");
  83. if (dbi_initialize (NULL) < 0) {
  84. printf ("UNKNOWN - failed to initialize DBI.\n");
  85. return STATE_UNKNOWN;
  86. }
  87. if (verbose)
  88. printf ("Opening DBI driver '%s'\n", np_dbi_driver);
  89. driver = dbi_driver_open (np_dbi_driver);
  90. if (! driver) {
  91. printf ("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n",
  92. np_dbi_driver);
  93. printf ("Known drivers:\n");
  94. for (driver = dbi_driver_list (NULL); driver; driver = dbi_driver_list (driver)) {
  95. printf (" - %s\n", dbi_driver_get_name (driver));
  96. }
  97. return STATE_UNKNOWN;
  98. }
  99. /* make a connection to the database */
  100. gettimeofday (&start_timeval, NULL);
  101. conn = dbi_conn_open (driver);
  102. if (! conn) {
  103. printf ("UNKNOWN - failed top open connection object.\n");
  104. dbi_conn_close (conn);
  105. return STATE_UNKNOWN;
  106. }
  107. for (i = 0; i < np_dbi_options_num; ++i) {
  108. const char *opt;
  109. if (verbose > 1)
  110. printf ("Setting DBI driver option '%s' to '%s'\n",
  111. np_dbi_options[i].key, np_dbi_options[i].value);
  112. if (! dbi_conn_set_option (conn, np_dbi_options[i].key, np_dbi_options[i].value))
  113. continue;
  114. /* else: status != 0 */
  115. np_dbi_print_error (conn, "UNKNOWN - failed to set option '%s' to '%s'",
  116. np_dbi_options[i].key, np_dbi_options[i].value);
  117. printf ("Known driver options:\n");
  118. for (opt = dbi_conn_get_option_list (conn, NULL); opt;
  119. opt = dbi_conn_get_option_list (conn, opt)) {
  120. printf (" - %s\n", opt);
  121. }
  122. dbi_conn_close (conn);
  123. return STATE_UNKNOWN;
  124. }
  125. if (host) {
  126. if (verbose > 1)
  127. printf ("Setting DBI driver option 'host' to '%s'\n", host);
  128. dbi_conn_set_option (conn, "host", host);
  129. }
  130. if (verbose) {
  131. const char *dbname, *host;
  132. dbname = dbi_conn_get_option (conn, "dbname");
  133. host = dbi_conn_get_option (conn, "host");
  134. if (! dbname)
  135. dbname = "<unspecified>";
  136. if (! host)
  137. host = "<unspecified>";
  138. printf ("Connecting to database '%s' at host '%s'\n",
  139. dbname, host);
  140. }
  141. if (dbi_conn_connect (conn) < 0) {
  142. np_dbi_print_error (conn, "UNKOWN - failed to connect to database");
  143. return STATE_UNKNOWN;
  144. }
  145. gettimeofday (&end_timeval, NULL);
  146. while (start_timeval.tv_usec > end_timeval.tv_usec) {
  147. --end_timeval.tv_sec;
  148. end_timeval.tv_usec += 1000000;
  149. }
  150. elapsed_time = (double)(end_timeval.tv_sec - start_timeval.tv_sec)
  151. + (double)(end_timeval.tv_usec - start_timeval.tv_usec) / 1000000.0;
  152. if (verbose)
  153. printf("Time elapsed: %f\n", elapsed_time);
  154. /* select a database */
  155. if (np_dbi_database) {
  156. if (verbose > 1)
  157. printf ("Selecting database '%s'\n", np_dbi_database);
  158. if (dbi_conn_select_db (conn, np_dbi_database)) {
  159. np_dbi_print_error (conn, "UNKOWN - failed to select database '%s'",
  160. np_dbi_database);
  161. return STATE_UNKNOWN;
  162. }
  163. }
  164. /* execute query */
  165. status = do_query (conn, &query_val);
  166. if (status != STATE_OK)
  167. /* do_query prints an error message in this case */
  168. return status;
  169. status = get_status (query_val, query_thresholds);
  170. if (verbose)
  171. printf("Closing connection\n");
  172. dbi_conn_close (conn);
  173. printf ("%s - connection time: %fs, '%s' returned %f",
  174. state_text (status), elapsed_time, np_dbi_query, query_val);
  175. printf (" | conntime=%fs;;;0 query=%f;%s;%s;0\n", elapsed_time, query_val,
  176. warning_range ? warning_range : "", critical_range ? critical_range : "");
  177. return status;
  178. }
  179. /* process command-line arguments */
  180. int
  181. process_arguments (int argc, char **argv)
  182. {
  183. int c;
  184. int option = 0;
  185. static struct option longopts[] = {
  186. STD_LONG_OPTS,
  187. {"driver", required_argument, 0, 'd'},
  188. {"option", required_argument, 0, 'o'},
  189. {"query", required_argument, 0, 'q'},
  190. {"database", required_argument, 0, 'D'},
  191. {0, 0, 0, 0}
  192. };
  193. while (1) {
  194. c = getopt_long (argc, argv, "Vvht:c:w:H:d:o:q:D:",
  195. longopts, &option);
  196. if (c == EOF)
  197. break;
  198. switch (c) {
  199. case '?': /* usage */
  200. usage5 ();
  201. case 'h': /* help */
  202. print_help ();
  203. exit (STATE_OK);
  204. case 'V': /* version */
  205. print_revision (progname, NP_VERSION);
  206. exit (STATE_OK);
  207. case 'c': /* critical range */
  208. critical_range = optarg;
  209. break;
  210. case 'w': /* warning range */
  211. warning_range = optarg;
  212. break;
  213. case 't': /* timeout */
  214. if (!is_intnonneg (optarg))
  215. usage2 (_("Timeout interval must be a positive integer"), optarg);
  216. else
  217. timeout_interval = atoi (optarg);
  218. case 'H': /* host */
  219. if (!is_host (optarg))
  220. usage2 (_("Invalid hostname/address"), optarg);
  221. else
  222. host = optarg;
  223. break;
  224. case 'v':
  225. verbose++;
  226. break;
  227. case 'd':
  228. np_dbi_driver = optarg;
  229. break;
  230. case 'o':
  231. {
  232. driver_option_t *new;
  233. char *k, *v;
  234. k = optarg;
  235. v = strchr (k, (int)'=');
  236. if (! v)
  237. usage2 (_("Option must be '<key>=<value>'"), optarg);
  238. *v = '\0';
  239. ++v;
  240. new = realloc (np_dbi_options,
  241. (np_dbi_options_num + 1) * sizeof (*new));
  242. if (! new) {
  243. printf ("UNKOWN - failed to reallocate memory\n");
  244. exit (STATE_UNKNOWN);
  245. }
  246. np_dbi_options = new;
  247. new = np_dbi_options + np_dbi_options_num;
  248. ++np_dbi_options_num;
  249. new->key = k;
  250. new->value = v;
  251. }
  252. break;
  253. case 'q':
  254. np_dbi_query = optarg;
  255. break;
  256. case 'D':
  257. np_dbi_database = optarg;
  258. break;
  259. }
  260. }
  261. set_thresholds (&query_thresholds, warning_range, critical_range);
  262. return validate_arguments ();
  263. }
  264. int
  265. validate_arguments ()
  266. {
  267. if (! np_dbi_driver)
  268. usage ("Must specify a DBI driver");
  269. if (! np_dbi_query)
  270. usage ("Must specify an SQL query to execute");
  271. return OK;
  272. }
  273. void
  274. print_help (void)
  275. {
  276. print_revision (progname, NP_VERSION);
  277. printf (COPYRIGHT, copyright, email);
  278. printf (_("This program checks a query result against threshold levels"));
  279. printf ("\n\n");
  280. print_usage ();
  281. printf (UT_HELP_VRSN);
  282. /* include this conditionally to avoid 'zero-length printf format string'
  283. * compiler warnings */
  284. #ifdef NP_EXTRA_OPTS
  285. printf (UT_EXTRA_OPTS);
  286. #endif
  287. printf ("\n");
  288. printf (" %s\n", "-d, --driver=STRING");
  289. printf (" %s\n", _("DBI driver to use"));
  290. printf (" %s\n", "-o, --option=STRING");
  291. printf (" %s\n", _("DBI driver options"));
  292. printf (" %s\n", "-q, --query=STRING");
  293. printf (" %s\n", _("SQL query to execute"));
  294. printf ("\n");
  295. printf (UT_WARN_CRIT_RANGE);
  296. printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
  297. printf (UT_VERBOSE);
  298. printf ("\n");
  299. printf (" %s\n", _("A DBI driver (-d option) and a query (-q option) are required."));
  300. printf (" %s\n", _("This plugin connects to an SQL database using libdbi and executes the"));
  301. printf (" %s\n", _("specified SQL query. The first column of the first row of the result"));
  302. printf (" %s\n", _("will be used as the check result and, if specified, compared with the"));
  303. printf (" %s\n", _("warning and critical ranges. The result from the query has to be numeric"));
  304. printf (" %s\n\n", _("(strings representing numbers are fine)."));
  305. printf (" %s\n", _("The number and type of required DBI driver options depends on the actual"));
  306. printf (" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/"));
  307. printf (" %s\n", _("for details."));
  308. printf (UT_SUPPORT);
  309. }
  310. void
  311. print_usage (void)
  312. {
  313. printf ("%s\n", _("Usage:"));
  314. printf ("%s -d <DBI driver> [-o <DBI driver option> [...]] -q <SQL query>\n", progname);
  315. printf (" [-H <host>] [-c <critical value>] [-w <warning value>]\n");
  316. }
  317. double
  318. get_field (dbi_conn conn, dbi_result res, unsigned short *field_type)
  319. {
  320. double val = 0.0;
  321. if (*field_type == DBI_TYPE_INTEGER) {
  322. val = (double)dbi_result_get_longlong_idx (res, 1);
  323. }
  324. else if (*field_type == DBI_TYPE_DECIMAL) {
  325. val = dbi_result_get_double_idx (res, 1);
  326. }
  327. else if (*field_type == DBI_TYPE_STRING) {
  328. const char *val_str;
  329. char *endptr = NULL;
  330. val_str = dbi_result_get_string_idx (res, 1);
  331. if ((! val_str) || (strcmp (val_str, "ERROR") == 0)) {
  332. np_dbi_print_error (conn, "CRITICAL - failed to fetch string value");
  333. *field_type = DBI_TYPE_ERROR;
  334. return 0.0;
  335. }
  336. if (verbose > 2)
  337. printf ("Query returned string '%s'\n", val_str);
  338. val = strtod (val_str, &endptr);
  339. if (endptr == val_str) {
  340. printf ("CRITICAL - result value is not a numeric: %s\n", val_str);
  341. *field_type = DBI_TYPE_ERROR;
  342. return 0.0;
  343. }
  344. else if ((endptr != NULL) && (*endptr != '\0')) {
  345. if (verbose)
  346. printf ("Garbage after value: %s\n", endptr);
  347. }
  348. }
  349. else {
  350. printf ("CRITICAL - cannot parse value of type %s (%i)\n",
  351. (*field_type == DBI_TYPE_BINARY)
  352. ? "BINARY"
  353. : (*field_type == DBI_TYPE_DATETIME)
  354. ? "DATETIME"
  355. : "<unknown>",
  356. *field_type);
  357. *field_type = DBI_TYPE_ERROR;
  358. return 0.0;
  359. }
  360. return val;
  361. }
  362. int
  363. do_query (dbi_conn conn, double *res_val)
  364. {
  365. dbi_result res;
  366. unsigned short field_type;
  367. double val = 0.0;
  368. if (verbose)
  369. printf ("Executing query '%s'\n", np_dbi_query);
  370. res = dbi_conn_query (conn, np_dbi_query);
  371. if (! res) {
  372. np_dbi_print_error (conn, "CRITICAL - failed to execute query '%s'", np_dbi_query);
  373. return STATE_CRITICAL;
  374. }
  375. if (dbi_result_get_numrows (res) == DBI_ROW_ERROR) {
  376. np_dbi_print_error (conn, "CRITICAL - failed to fetch rows");
  377. return STATE_CRITICAL;
  378. }
  379. if (dbi_result_get_numrows (res) < 1) {
  380. printf ("WARNING - no rows returned\n");
  381. return STATE_WARNING;
  382. }
  383. if (dbi_result_get_numfields (res) == DBI_FIELD_ERROR) {
  384. np_dbi_print_error (conn, "CRITICAL - failed to fetch fields");
  385. return STATE_CRITICAL;
  386. }
  387. if (dbi_result_get_numfields (res) < 1) {
  388. printf ("WARNING - no fields returned\n");
  389. return STATE_WARNING;
  390. }
  391. if (dbi_result_first_row (res) != 1) {
  392. np_dbi_print_error (conn, "CRITICAL - failed to fetch first row");
  393. return STATE_CRITICAL;
  394. }
  395. field_type = dbi_result_get_field_type_idx (res, 1);
  396. if (field_type != DBI_TYPE_ERROR)
  397. val = get_field (conn, res, &field_type);
  398. if (field_type == DBI_TYPE_ERROR) {
  399. np_dbi_print_error (conn, "CRITICAL - failed to fetch data");
  400. return STATE_CRITICAL;
  401. }
  402. *res_val = val;
  403. dbi_result_free (res);
  404. return STATE_OK;
  405. }
  406. void
  407. np_dbi_print_error (dbi_conn conn, char *fmt, ...)
  408. {
  409. const char *errmsg = NULL;
  410. va_list ap;
  411. va_start (ap, fmt);
  412. dbi_conn_error (conn, &errmsg);
  413. vprintf (fmt, ap);
  414. printf (": %s\n", errmsg);
  415. va_end (ap);
  416. }