2 * collectd - src/oracle.c
3 * Copyright (C) 2008 Florian octo Forster
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; only version 2 of the License is applicable.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 * Florian octo Forster <octo at noris.net>
41 OCIStmt *oci_statement;
43 typedef struct o_query_s o_query_t;
55 OCISvcCtx *oci_service_context;
57 typedef struct o_database_s o_database_t;
62 static o_query_t **queries = NULL;
63 static size_t queries_num = 0;
64 static o_database_t **databases = NULL;
65 static size_t databases_num = 0;
67 OCIEnv *oci_env = NULL;
68 OCIError *oci_error = NULL;
73 static void o_report_error (const char *where, /* {{{ */
74 const char *what, OCIError *eh)
80 status = OCIErrorGet (eh, /* record number = */ 1,
81 /* sqlstate = */ NULL,
84 (ub4) sizeof (buffer),
86 buffer[sizeof (buffer) - 1] = 0;
88 if (status == OCI_SUCCESS)
92 buffer_length = strlen (buffer);
93 while ((buffer_length > 0) && (buffer[buffer_length - 1] < 32))
96 buffer[buffer_length] = 0;
99 ERROR ("oracle plugin: %s: %s failed: %s",
100 where, what, buffer);
104 ERROR ("oracle plugin: %s: %s failed. Additionally, OCIErrorGet failed with status %i.",
105 where, what, status);
107 } /* }}} void o_report_error */
109 static void o_query_free (o_query_t *q) /* {{{ */
117 sfree (q->statement);
120 for (i = 0; i < q->instances_num; i++)
121 sfree (q->instances[i]);
122 sfree (q->instances);
124 for (i = 0; i < q->values_num; i++)
125 sfree (q->values[i]);
129 } /* }}} void o_query_free */
131 static void o_database_free (o_database_t *db) /* {{{ */
137 sfree (db->connect_id);
138 sfree (db->username);
139 sfree (db->password);
143 } /* }}} void o_database_free */
145 /* Configuration handling functions {{{
148 * <Query "plugin_instance0">
149 * Statement "SELECT name, value FROM table"
151 * InstancesFrom "name"
155 * <Database "plugin_instance1">
156 * Hostname "localhost"
159 * Query "plugin_instance0"
164 static int o_config_set_string (char **ret_string, /* {{{ */
169 if ((ci->values_num != 1)
170 || (ci->values[0].type != OCONFIG_TYPE_STRING))
172 WARNING ("oracle plugin: The `%s' config option "
173 "needs exactly one string argument.", ci->key);
177 string = strdup (ci->values[0].value.string);
180 ERROR ("oracle plugin: strdup failed.");
184 if (*ret_string != NULL)
186 *ret_string = string;
189 } /* }}} int o_config_set_string */
191 static int o_config_add_string (char ***ret_array, /* {{{ */
192 size_t *ret_array_len, oconfig_item_t *ci)
198 if (ci->values_num < 1)
200 WARNING ("oracle plugin: The `%s' config option "
201 "needs at least one argument.", ci->key);
205 for (i = 0; i < ci->values_num; i++)
207 if (ci->values[i].type != OCONFIG_TYPE_STRING)
209 WARNING ("oracle plugin: Argument %i to the `%s' option "
210 "is not a string.", i + 1, ci->key);
215 array_len = *ret_array_len;
216 array = (char **) realloc (*ret_array,
217 sizeof (char *) * (array_len + ci->values_num));
220 ERROR ("oracle plugin: realloc failed.");
225 for (i = 0; i < ci->values_num; i++)
227 array[array_len] = strdup (ci->values[i].value.string);
228 if (array[array_len] == NULL)
230 ERROR ("oracle plugin: strdup failed.");
231 *ret_array_len = array_len;
237 *ret_array_len = array_len;
239 } /* }}} int o_config_add_string */
241 static int o_config_add_query (oconfig_item_t *ci) /* {{{ */
247 if ((ci->values_num != 1)
248 || (ci->values[0].type != OCONFIG_TYPE_STRING))
250 WARNING ("oracle plugin: The `Query' block "
251 "needs exactly one string argument.");
255 q = (o_query_t *) malloc (sizeof (*q));
258 ERROR ("oracle plugin: malloc failed.");
261 memset (q, 0, sizeof (*q));
263 status = o_config_set_string (&q->name, ci);
270 /* Fill the `o_query_t' structure.. */
271 for (i = 0; i < ci->children_num; i++)
273 oconfig_item_t *child = ci->children + i;
275 if (strcasecmp ("Statement", child->key) == 0)
276 status = o_config_set_string (&q->statement, child);
277 else if (strcasecmp ("Type", child->key) == 0)
278 status = o_config_set_string (&q->type, child);
279 else if (strcasecmp ("InstancesFrom", child->key) == 0)
280 status = o_config_add_string (&q->instances, &q->instances_num, child);
281 else if (strcasecmp ("ValuesFrom", child->key) == 0)
282 status = o_config_add_string (&q->values, &q->values_num, child);
285 WARNING ("oracle plugin: Option `%s' not allowed here.", child->key);
293 /* Check that all necessary options have been given. */
296 if (q->statement == NULL)
298 WARNING ("oracle plugin: `Statement' not given for query `%s'", q->name);
303 WARNING ("oracle plugin: `Type' not given for query `%s'", q->name);
306 if (q->instances == NULL)
308 WARNING ("oracle plugin: `InstancesFrom' not given for query `%s'", q->name);
311 if (q->values == NULL)
313 WARNING ("oracle plugin: `ValuesFrom' not given for query `%s'", q->name);
318 } /* while (status == 0) */
320 /* If all went well, add this query to the list of queries within the
321 * database structure. */
326 temp = (o_query_t **) realloc (queries,
327 sizeof (*queries) * (queries_num + 1));
330 ERROR ("oracle plugin: realloc failed");
336 queries[queries_num] = q;
348 } /* }}} int o_config_add_query */
350 static int o_config_add_database_query (o_database_t *db, /* {{{ */
357 if ((ci->values_num != 1)
358 || (ci->values[0].type != OCONFIG_TYPE_STRING))
360 WARNING ("oracle plugin: The `Query' config option "
361 "needs exactly one string argument.");
366 for (i = 0; i < queries_num; i++)
368 if (strcasecmp (queries[i]->name, ci->values[0].value.string) == 0)
377 WARNING ("oracle plugin: Database `%s': Unknown query `%s'. "
378 "Please make sure that the <Query \"%s\"> block comes before "
379 "the <Database \"%s\"> block.",
380 db->name, ci->values[0].value.string,
381 ci->values[0].value.string, db->name);
385 temp = (o_query_t **) realloc (db->queries,
386 sizeof (*db->queries) * (db->queries_num + 1));
389 ERROR ("oracle plugin: realloc failed");
395 db->queries[db->queries_num] = q;
400 } /* }}} int o_config_add_database_query */
402 static int o_config_add_database (oconfig_item_t *ci) /* {{{ */
408 if ((ci->values_num != 1)
409 || (ci->values[0].type != OCONFIG_TYPE_STRING))
411 WARNING ("oracle plugin: The `Database' block "
412 "needs exactly one string argument.");
416 db = (o_database_t *) malloc (sizeof (*db));
419 ERROR ("oracle plugin: malloc failed.");
422 memset (db, 0, sizeof (*db));
424 status = o_config_set_string (&db->name, ci);
431 /* Fill the `o_database_t' structure.. */
432 for (i = 0; i < ci->children_num; i++)
434 oconfig_item_t *child = ci->children + i;
436 if (strcasecmp ("ConnectID", child->key) == 0)
437 status = o_config_set_string (&db->connect_id, child);
438 else if (strcasecmp ("Username", child->key) == 0)
439 status = o_config_set_string (&db->username, child);
440 else if (strcasecmp ("Password", child->key) == 0)
441 status = o_config_set_string (&db->password, child);
442 else if (strcasecmp ("Query", child->key) == 0)
443 status = o_config_add_database_query (db, child);
446 WARNING ("oracle plugin: Option `%s' not allowed here.", child->key);
454 /* Check that all necessary options have been given. */
457 if (db->connect_id == NULL)
459 WARNING ("oracle plugin: `ConnectID' not given for query `%s'", db->name);
462 if (db->username == NULL)
464 WARNING ("oracle plugin: `Username' not given for query `%s'", db->name);
467 if (db->password == NULL)
469 WARNING ("oracle plugin: `Password' not given for query `%s'", db->name);
474 } /* while (status == 0) */
476 /* If all went well, add this query to the list of queries within the
477 * database structure. */
482 temp = (o_database_t **) realloc (databases,
483 sizeof (*databases) * (databases_num + 1));
486 ERROR ("oracle plugin: realloc failed");
492 databases[databases_num] = db;
499 o_database_free (db);
504 } /* }}} int o_config_add_database */
506 static int o_config (oconfig_item_t *ci) /* {{{ */
510 for (i = 0; i < ci->children_num; i++)
512 oconfig_item_t *child = ci->children + i;
513 if (strcasecmp ("Query", child->key) == 0)
514 o_config_add_query (child);
515 else if (strcasecmp ("Database", child->key) == 0)
516 o_config_add_database (child);
519 WARNING ("snmp plugin: Ignoring unknown config option `%s'.", child->key);
521 } /* for (ci->children) */
524 } /* }}} int o_config */
526 /* }}} End of configuration handling functions */
528 static int o_init (void) /* {{{ */
535 status = OCIEnvCreate (&oci_env,
536 /* mode = */ OCI_THREADED,
537 /* context = */ NULL,
539 /* realloc = */ NULL,
541 /* user_data_size = */ 0,
542 /* user_data_ptr = */ NULL);
545 ERROR ("oracle plugin: OCIEnvCreate failed with status %i.", status);
549 status = OCIHandleAlloc (oci_env, (void *) &oci_error, OCI_HTYPE_ERROR,
550 /* user_data_size = */ 0, /* user_data = */ NULL);
551 if (status != OCI_SUCCESS)
553 ERROR ("oracle plugin: OCIHandleAlloc (OCI_HTYPE_ERROR) failed "
554 "with status %i.", status);
559 } /* }}} int o_init */
561 static void o_submit (o_database_t *db, o_query_t *q, /* {{{ */
562 const data_set_t *ds, char **buffer_instances, char **buffer_values)
564 value_list_t vl = VALUE_LIST_INIT;
567 assert (((size_t) ds->ds_num) == q->values_num);
569 vl.values = (value_t *) malloc (sizeof (value_t) * q->values_num);
570 if (vl.values == NULL)
572 ERROR ("oracle plugin: malloc failed.");
575 vl.values_len = ds->ds_num;
577 for (i = 0; i < q->values_num; i++)
583 if (ds->ds[i].type == DS_TYPE_COUNTER)
584 vl.values[i].counter = (counter_t) strtoll (buffer_values[i],
585 &endptr, /* base = */ 0);
586 else if (ds->ds[i].type == DS_TYPE_GAUGE)
587 vl.values[i].gauge = (gauge_t) strtod (buffer_values[i], &endptr);
591 if ((endptr == buffer_values[i]) || (errno != 0))
593 WARNING ("oracle plugin: o_submit: Parsing `%s' as %s failed.",
595 (ds->ds[i].type == DS_TYPE_COUNTER) ? "counter" : "gauge");
596 vl.values[i].gauge = NAN;
600 vl.time = time (NULL);
601 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
602 sstrncpy (vl.plugin, "oracle", sizeof (vl.plugin));
603 sstrncpy (vl.plugin_instance, db->name, sizeof (vl.type_instance));
604 sstrncpy (vl.type, q->type, sizeof (vl.type));
605 strjoin (vl.type_instance, sizeof (vl.type_instance),
606 buffer_instances, q->instances_num, "-");
607 vl.type_instance[sizeof (vl.type_instance) - 1] = 0;
609 plugin_dispatch_values (&vl);
610 } /* }}} void o_submit */
612 static int o_read_database_query (o_database_t *db, /* {{{ */
615 const data_set_t *ds;
616 ub4 param_counter; /* == number of columns */
621 /* Scratch area for OCI to write values to */
622 char **buffer_instances;
623 char **buffer_values;
625 /* List of indizes of the instance and value columns. Only used for error
627 size_t *index_instances;
628 size_t *index_values;
630 /* List of `OCIDefine' pointers. These defines map columns to the buffer
631 * space declared above. */
632 OCIDefine **oci_defines;
634 ds = plugin_get_ds (q->type); /* {{{ */
637 WARNING ("oracle plugin: o_read_database_query (%s, %s): "
638 "plugin_get_ds (%s) failed. Please check if the type exists, "
640 db->name, q->name, q->type);
644 if (((size_t) ds->ds_num) != q->values_num)
646 ERROR ("oracle plugin: o_read_database_query (%s, %s): "
647 "The query `%s' uses the type `%s' which requires exactly "
648 "%i value%s, but you specified %zu value-column%s. "
649 "See types.db(5) for details.",
652 ds->ds_num, (ds->ds_num == 1) ? "" : "s",
653 q->values_num, (q->values_num == 1) ? "" : "s");
657 /* Prepare the statement */
658 if (q->oci_statement == NULL) /* {{{ */
660 status = OCIHandleAlloc (oci_env, (void *) &q->oci_statement,
661 OCI_HTYPE_STMT, /* user_data_size = */ 0, /* user_data = */ NULL);
662 if (status != OCI_SUCCESS)
664 o_report_error ("o_read_database_query", "OCIHandleAlloc", oci_error);
665 q->oci_statement = NULL;
669 status = OCIStmtPrepare (q->oci_statement, oci_error,
670 (text *) q->statement, (ub4) strlen (q->statement),
671 /* language = */ OCI_NTV_SYNTAX,
672 /* mode = */ OCI_DEFAULT);
673 if (status != OCI_SUCCESS)
675 o_report_error ("o_read_database_query", "OCIStmtPrepare", oci_error);
676 OCIHandleFree (q->oci_statement, OCI_HTYPE_STMT);
677 q->oci_statement = NULL;
680 assert (q->oci_statement != NULL);
683 /* Execute the statement */
684 status = OCIStmtExecute (db->oci_service_context, /* {{{ */
689 /* snap_in = */ NULL, /* snap_out = */ NULL,
690 /* mode = */ OCI_DEFAULT);
691 if (status != OCI_SUCCESS)
693 o_report_error ("o_read_database_query", "OCIStmtExecute", oci_error);
694 ERROR ("oracle plugin: o_read_database_query: "
695 "Failing statement was: %s", q->statement);
699 /* Acquire the number of columns returned. */
701 status = OCIAttrGet (q->oci_statement, OCI_HTYPE_STMT, /* {{{ */
702 ¶m_counter, /* size pointer = */ NULL,
703 OCI_ATTR_PARAM_COUNT, oci_error);
704 if (status != OCI_SUCCESS)
706 o_report_error ("o_read_database_query", "OCIAttrGet", oci_error);
710 /* Allocate the following buffers:
712 * - buffer_instances q->instances_num x DATA_MAX_NAME_LEN
713 * - buffer_values q->values_num x NUMBER_BUFFER_SIZE
714 * - index_instances q->instances_num
715 * - index_values q->values_num
716 * - oci_defines q->instances_num+q->values_num
718 #define NUMBER_BUFFER_SIZE 64
721 if (buffer_instances != NULL) { \
722 sfree (buffer_instances[0]); \
723 sfree (buffer_instances); \
725 if (buffer_values != NULL) { \
726 sfree (buffer_values[0]); \
727 sfree (buffer_values); \
729 sfree (index_instances); \
730 sfree (index_values); \
733 #define ALLOC_OR_FAIL(ptr, ptr_size) \
735 size_t alloc_size = (size_t) ((ptr_size)); \
736 (ptr) = malloc (alloc_size); \
737 if ((ptr) == NULL) { \
739 ERROR ("oracle plugin: o_read_database_query: malloc failed."); \
742 memset ((ptr), 0, alloc_size); \
745 /* Initialize everything to NULL so the above works. */
746 buffer_instances = NULL;
747 buffer_values = NULL;
748 index_instances = NULL;
752 ALLOC_OR_FAIL (buffer_instances, q->instances_num * sizeof (char *));
753 ALLOC_OR_FAIL (buffer_instances[0], q->instances_num * DATA_MAX_NAME_LEN
755 for (i = 1; i < q->instances_num; i++)
756 buffer_instances[i] = buffer_instances[i - 1] + DATA_MAX_NAME_LEN;
758 ALLOC_OR_FAIL (buffer_values, q->values_num * sizeof (char *));
759 ALLOC_OR_FAIL (buffer_values[0], q->values_num * NUMBER_BUFFER_SIZE
761 for (i = 1; i < q->values_num; i++)
762 buffer_values[i] = buffer_values[i - 1] + NUMBER_BUFFER_SIZE;
764 ALLOC_OR_FAIL (index_instances, q->instances_num * sizeof (size_t));
765 ALLOC_OR_FAIL (index_values, q->values_num * sizeof (size_t));
767 ALLOC_OR_FAIL (oci_defines, (q->instances_num + q->values_num)
768 * sizeof (OCIDefine *));
769 /* }}} End of buffer allocations. */
771 /* ``Define'' the returned data, i. e. bind the columns to the buffers
773 for (j = 1; j <= param_counter; j++) /* {{{ */
776 size_t column_name_length;
777 char column_name_copy[DATA_MAX_NAME_LEN];
783 status = OCIParamGet (q->oci_statement, OCI_HTYPE_STMT, oci_error,
784 (void *) &oci_param, j);
785 if (status != OCI_SUCCESS)
787 /* This is probably alright */
788 DEBUG ("oracle plugin: o_read_database_query: status = %#x (= %i);", status, status);
789 o_report_error ("o_read_database_query", "OCIParamGet", oci_error);
790 status = OCI_SUCCESS;
795 column_name_length = 0;
796 status = OCIAttrGet (oci_param, OCI_DTYPE_PARAM,
797 &column_name, &column_name_length, OCI_ATTR_NAME, oci_error);
798 if (status != OCI_SUCCESS)
800 o_report_error ("o_read_database_query", "OCIAttrGet (OCI_ATTR_NAME)",
805 /* Ensure null-termination. */
806 memset (column_name_copy, 0, sizeof (column_name_copy));
807 if (column_name_length >= sizeof (column_name_copy))
808 column_name_length = sizeof (column_name_copy) - 1;
809 memcpy (column_name_copy, column_name, column_name_length);
810 column_name_copy[column_name_length] = 0;
812 DEBUG ("oracle plugin: o_read_database_query: column_name[%u] = %s; column_name_length = %zu;",
813 (unsigned int) j, column_name_copy, column_name_length);
815 for (i = 0; i < q->instances_num; i++)
817 if (strcasecmp (q->instances[i], column_name_copy) != 0)
820 status = OCIDefineByPos (q->oci_statement,
821 &oci_defines[i], oci_error, j,
822 buffer_instances[i], DATA_MAX_NAME_LEN, SQLT_STR,
823 NULL, NULL, NULL, OCI_DEFAULT);
824 index_instances[i] = j;
826 DEBUG ("oracle plugin: o_read_database_query: column[%u] (%s) -> instances[%zu]",
827 (unsigned int) j, column_name_copy, i);
831 for (i = 0; i < q->values_num; i++)
833 if (strcasecmp (q->values[i], column_name_copy) != 0)
836 status = OCIDefineByPos (q->oci_statement,
837 &oci_defines[q->instances_num + i], oci_error, j,
838 buffer_values[i], NUMBER_BUFFER_SIZE, SQLT_STR,
839 NULL, NULL, NULL, OCI_DEFAULT);
842 DEBUG ("oracle plugin: o_read_database_query: column[%u] (%s) -> values[%zu]",
843 (unsigned int) j, column_name_copy, i);
846 } /* for (j = 1; j <= param_counter; j++) */
847 /* }}} End of the ``define'' stuff. */
849 /* Iterate over all indizes and check that all columns from which we're
850 * supposed to read instances or values have been found. */
853 for (i = 0; i < q->instances_num; i++)
855 if (index_instances[i] > 0)
858 ERROR ("oracle plugin: o_read_database_query (%s, %s): "
859 "Instance %zu of the `%s' query should be read from the column `%s', "
860 "but that column wasn't returned by the SQL statement. Please check "
861 "your configuration.",
862 db->name, q->name, (i + 1), q->name, q->instances[i]);
866 for (i = 0; i < q->values_num; i++)
868 if (index_values[i] > 0)
871 ERROR ("oracle plugin: o_read_database_query (%s, %s): "
872 "Value %zu of the `%s' query should be read from the column `%s', "
873 "but that column wasn't returned by the SQL statement. Please check "
874 "your configuration.",
875 db->name, q->name, (i + 1), q->name, q->values[i]);
886 /* Fetch and handle all the rows that matched the query. */
889 status = OCIStmtFetch2 (q->oci_statement, oci_error,
890 /* nrows = */ 1, /* orientation = */ OCI_FETCH_NEXT,
891 /* fetch offset = */ 0, /* mode = */ OCI_DEFAULT);
892 if (status == OCI_NO_DATA)
894 status = OCI_SUCCESS;
897 else if ((status != OCI_SUCCESS) && (status != OCI_SUCCESS_WITH_INFO))
899 o_report_error ("o_read_database_query", "OCIStmtFetch2", oci_error);
903 for (i = 0; i < q->instances_num; i++)
905 DEBUG ("oracle plugin: o_read_database_query: "
906 "buffer_instances[%zu] = %s;",
907 i, buffer_instances[i]);
910 for (i = 0; i < q->values_num; i++)
912 DEBUG ("oracle plugin: o_read_database_query: "
913 "buffer_values[%zu] = %s;",
914 i, buffer_values[i]);
917 o_submit (db, q, ds, buffer_instances, buffer_values);
918 } /* }}} while (42) */
920 /* DEBUG ("oracle plugin: o_read_database_query: This statement succeeded: %s", q->statement); */
926 } /* }}} int o_read_database_query */
928 static int o_read_database (o_database_t *db) /* {{{ */
933 if (db->oci_service_context == NULL)
935 status = OCILogon (oci_env, oci_error,
936 &db->oci_service_context,
937 (OraText *) db->username, (ub4) strlen (db->username),
938 (OraText *) db->password, (ub4) strlen (db->password),
939 (OraText *) db->connect_id, (ub4) strlen (db->connect_id));
940 if (status != OCI_SUCCESS)
942 o_report_error ("o_read_database", "OCILogon", oci_error);
943 DEBUG ("oracle plugin: OCILogon (%s): db->oci_service_context = %p;",
944 db->connect_id, db->oci_service_context);
945 db->oci_service_context = NULL;
948 assert (db->oci_service_context != NULL);
951 DEBUG ("oracle plugin: o_read_database: db->connect_id = %s; db->oci_service_context = %p;",
952 db->connect_id, db->oci_service_context);
954 for (i = 0; i < db->queries_num; i++)
955 o_read_database_query (db, db->queries[i]);
958 } /* }}} int o_read_database */
960 static int o_read (void) /* {{{ */
964 for (i = 0; i < databases_num; i++)
965 o_read_database (databases[i]);
968 } /* }}} int o_read */
970 static int o_shutdown (void) /* {{{ */
974 for (i = 0; i < databases_num; i++)
975 if (databases[i]->oci_service_context != NULL)
977 OCIHandleFree (databases[i]->oci_service_context, OCI_HTYPE_SVCCTX);
978 databases[i]->oci_service_context = NULL;
981 for (i = 0; i < queries_num; i++)
982 if (queries[i]->oci_statement != NULL)
984 OCIHandleFree (queries[i]->oci_statement, OCI_HTYPE_STMT);
985 queries[i]->oci_statement = NULL;
988 OCIHandleFree (oci_env, OCI_HTYPE_ENV);
990 } /* }}} int o_shutdown */
992 void module_register (void) /* {{{ */
994 plugin_register_complex_config ("oracle", o_config);
995 plugin_register_init ("oracle", o_init);
996 plugin_register_read ("oracle", o_read);
997 plugin_register_shutdown ("oracle", o_shutdown);
998 } /* }}} void module_register */
1001 * vim: shiftwidth=2 softtabstop=2 et fdm=marker