diff options
Diffstat (limited to 'src/mysql.c')
-rw-r--r-- | src/mysql.c | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/src/mysql.c b/src/mysql.c new file mode 100644 index 0000000..9f304a6 --- /dev/null +++ b/src/mysql.c @@ -0,0 +1,263 @@ +#include "dbrw.h" +#include <mysql/mysql.h> +#include <mysql/errmsg.h> +#include <mysql/mysqld_error.h> + +struct vmod_mysql_data +{ + MYSQL *mysql; + MYSQL_RES *result; + char *buffer; + size_t bufsize; +}; + + +static void +check_errno(struct dbrw_connection *conn) +{ + struct vmod_mysql_data *mp = conn->data; + + switch (mysql_errno(mp->mysql)) { + case CR_SERVER_GONE_ERROR: + case CR_SERVER_LOST: + case ER_SERVER_SHUTDOWN: + case ER_ABORTING_CONNECTION: + sql_disconnect(conn); + if (conn->state == state_error) { + conn->state = state_disabled; + syslog(LOG_DAEMON|LOG_NOTICE, + "disabling MySQL connection"); + } + break; + case ER_UNKNOWN_COM_ERROR: + case ER_ACCESS_DENIED_ERROR: + case ER_BAD_DB_ERROR: + case ER_WRONG_DB_NAME: + case ER_BAD_FIELD_ERROR: + case ER_BAD_HOST_ERROR: + case ER_BAD_TABLE_ERROR: + case ER_WRONG_FIELD_SPEC: + case ER_PARSE_ERROR: + case ER_EMPTY_QUERY: + case ER_FIELD_SPECIFIED_TWICE: + case ER_NO_SUCH_TABLE: + case ER_NOT_ALLOWED_COMMAND: + sql_disconnect(conn); + conn->state = state_disabled; + syslog(LOG_DAEMON|LOG_NOTICE, "disabling MySQL connection"); + } +} + +/* ************************************************************************* */ +/* Interface routines */ + +static int +s_mysql_init(struct dbrw_connection *conn) +{ + struct vmod_mysql_data *mp = calloc(1, sizeof (*mp)); + if (!mp) { + dbrw_error("not enough memory"); + return -1; + } + conn->data = mp; + return 0; +} + +static void +s_mysql_destroy(struct dbrw_connection *conn) +{ + struct vmod_mysql_data *mp = conn->data; + free(mp->buffer); + free(mp->mysql); + free(mp); + conn->data = NULL; +} + + +static int +s_mysql_connect(struct dbrw_connection *conn) +{ + struct vmod_mysql_data *mp = conn->data; + char *host, *socket_name = NULL; + char *s; + int port; + + mp->mysql = malloc(sizeof(MYSQL)); + if (!mp->mysql) { + dbrw_error("not enough memory"); + conn->state = state_disabled; + return -1; + } + + mysql_init(mp->mysql); + + host = findparam(conn->conf->param, "server"); + if (host && host[0] == '/') { + host = "localhost"; + socket_name = host; + } + + s = findparam(conn->conf->param, "port"); + if (s) + port = atoi(s); + + s = findparam(conn->conf->param, "config"); + if (s) + mysql_options(mp->mysql, MYSQL_READ_DEFAULT_FILE, s); + + s = findparam(conn->conf->param, "group"); + if (s) + mysql_options(mp->mysql, MYSQL_READ_DEFAULT_GROUP, s); + + s = findparam(conn->conf->param, "cacert"); + if (s) + mysql_ssl_set(mp->mysql, NULL, NULL, s, NULL, NULL); + debug(conn->conf, 1, ("connecting to database")); + if (!mysql_real_connect(mp->mysql, + host, + findparam(conn->conf->param, "user"), + findparam(conn->conf->param, "password"), + findparam(conn->conf->param, "database"), + port, + socket_name, + CLIENT_MULTI_RESULTS)) + return -1; + debug(conn->conf, 1, ("connected to database")); + + return 0; +} + +static int +s_mysql_disconnect(struct dbrw_connection *conn) +{ + struct vmod_mysql_data *mp = conn->data; + mysql_close(mp->mysql); + return 0; +} + +static int +s_mysql_query(struct dbrw_connection *conn, const char *query) +{ + struct vmod_mysql_data *mp = conn->data; + int rc; + int i; + MYSQL *mysql; + + for (i = 0; i < 10; i++) { + mysql = mp->mysql; + rc = mysql_query(mysql, query); + if (rc) { + check_errno(conn); + if (conn->state != state_init) + return -1; + sql_connect(conn); + if (conn->state != state_connected) + return -1; + continue; + } + if (!(mp->result = mysql_store_result(mp->mysql))) { + dbrw_error("cannot store result: %s", + mysql_error (mp->mysql)); + conn->state = state_error; + rc = 1; + } + break; + } + return rc; +} + +static int +s_mysql_free_result(struct dbrw_connection *conn) +{ + struct vmod_mysql_data *mp = conn->data; + mysql_free_result(mp->result); + mp->result = NULL; + return 0; +} + +static unsigned +s_mysql_num_fields(struct dbrw_connection *conn) +{ + struct vmod_mysql_data *mp = conn->data; + return mysql_num_fields(mp->result); +} + +static unsigned +s_mysql_num_tuples(struct dbrw_connection *conn) +{ + struct vmod_mysql_data *mp = conn->data; + return mysql_num_rows(mp->result); +} + +static const char * +s_mysql_get_column(struct dbrw_connection *conn, size_t nrow, size_t ncol) +{ + struct vmod_mysql_data *mp = conn->data; + MYSQL_ROW row; + + if (nrow >= mysql_num_rows(mp->result) || + ncol >= mysql_num_fields (mp->result)) + return NULL; + + mysql_data_seek(mp->result, nrow); + row = mysql_fetch_row(mp->result); + if (!row) + return NULL; + return row[ncol]; +} + +static char * +s_mysql_escape (struct dbrw_connection *conn, const char *arg) +{ + struct vmod_mysql_data *mp = conn->data; + size_t len, size; + char *p; + + switch (conn->state) { + case state_connected: + case state_result: + break; + + case state_error: + case state_init: + if (sql_connect(conn)) + return NULL; + break; + + default: + return NULL; + } + + len = strlen(arg); + size = 2 * len + 1; + if (mp->bufsize < size) { + p = realloc(mp->buffer, size); + if (!p) { + dbrw_error("not enough memory"); + return NULL; + } + mp->buffer = p; + mp->bufsize = size; + } + + mysql_real_escape_string(mp->mysql, mp->buffer, arg, len); + + p = strdup(mp->buffer); + if (!p) + dbrw_error("not enough memory"); + return p; +} + +struct dbrw_backend mysql_backend = { + "mysql", + s_mysql_init, + s_mysql_destroy, + s_mysql_connect, + s_mysql_disconnect, + s_mysql_escape, + s_mysql_query, + s_mysql_num_tuples, + s_mysql_num_fields, + s_mysql_free_result, + s_mysql_get_column +}; |