123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- /*
- This file is part of GNUnet
- Copyright (C) 2012 GNUnet e.V.
- GNUnet is free software: you can redistribute it and/or modify it
- under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License,
- or (at your option) any later version.
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: AGPL3.0-or-later
- */
- /**
- * @file mysql/mysql.c
- * @brief library to help with access to a MySQL database
- * @author Christian Grothoff
- */
- #include "platform.h"
- #include <mysql/mysql.h>
- #include "gnunet_mysql_lib.h"
- /**
- * Maximum number of supported parameters for a prepared
- * statement. Increase if needed.
- */
- #define MAX_PARAM 16
- /**
- * Die with an error message that indicates
- * a failure of the command 'cmd' with the message given
- * by strerror(errno).
- */
- #define DIE_MYSQL(cmd, dbh) \
- do \
- { \
- GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, \
- "mysql", \
- _ ("`%s' failed at %s:%d with error: %s\n"), \
- cmd, \
- __FILE__, \
- __LINE__, \
- mysql_error ((dbh)->dbf)); \
- GNUNET_assert (0); \
- } while (0);
- /**
- * Log an error message at log-level 'level' that indicates
- * a failure of the command 'cmd' on file 'filename'
- * with the message given by strerror(errno).
- */
- #define LOG_MYSQL(level, cmd, dbh) \
- do \
- { \
- GNUNET_log_from (level, \
- "mysql", \
- _ ("`%s' failed at %s:%d with error: %s\n"), \
- cmd, \
- __FILE__, \
- __LINE__, \
- mysql_error ((dbh)->dbf)); \
- } while (0);
- /**
- * Mysql context.
- */
- struct GNUNET_MYSQL_Context
- {
- /**
- * Our configuration.
- */
- const struct GNUNET_CONFIGURATION_Handle *cfg;
- /**
- * Our section.
- */
- const char *section;
- /**
- * Handle to the mysql database.
- */
- MYSQL *dbf;
- /**
- * Head of list of our prepared statements.
- */
- struct GNUNET_MYSQL_StatementHandle *shead;
- /**
- * Tail of list of our prepared statements.
- */
- struct GNUNET_MYSQL_StatementHandle *stail;
- /**
- * Filename of "my.cnf" (msyql configuration).
- */
- char *cnffile;
- };
- /**
- * Handle for a prepared statement.
- */
- struct GNUNET_MYSQL_StatementHandle
- {
- /**
- * Kept in a DLL.
- */
- struct GNUNET_MYSQL_StatementHandle *next;
- /**
- * Kept in a DLL.
- */
- struct GNUNET_MYSQL_StatementHandle *prev;
- /**
- * Mysql Context the statement handle belongs to.
- */
- struct GNUNET_MYSQL_Context *mc;
- /**
- * Original query string.
- */
- char *query;
- /**
- * Handle to MySQL prepared statement.
- */
- MYSQL_STMT *statement;
- /**
- * Is the MySQL prepared statement valid, or do we need to re-initialize it?
- */
- int valid;
- };
- /**
- * Obtain the location of ".my.cnf".
- *
- * @param cfg our configuration
- * @param section the section
- * @return NULL on error
- */
- static char *
- get_my_cnf_path (const struct GNUNET_CONFIGURATION_Handle *cfg,
- const char *section)
- {
- char *cnffile;
- char *home_dir;
- struct stat st;
- struct passwd *pw;
- int configured;
- pw = getpwuid (getuid ());
- if (! pw)
- {
- GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_ERROR, "mysql", "getpwuid");
- return NULL;
- }
- if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (cfg, section, "CONFIG"))
- {
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_filename (cfg,
- section,
- "CONFIG",
- &cnffile));
- configured = GNUNET_YES;
- }
- else
- {
- home_dir = GNUNET_strdup (pw->pw_dir);
- GNUNET_asprintf (&cnffile, "%s/.my.cnf", home_dir);
- GNUNET_free (home_dir);
- configured = GNUNET_NO;
- }
- GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
- "mysql",
- _ ("Trying to use file `%s' for MySQL configuration.\n"),
- cnffile);
- if ((0 != stat (cnffile, &st)) || (0 != access (cnffile, R_OK)) ||
- (! S_ISREG (st.st_mode)))
- {
- if (configured == GNUNET_YES)
- GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
- "mysql",
- _ ("Could not access file `%s': %s\n"),
- cnffile,
- strerror (errno));
- GNUNET_free (cnffile);
- return NULL;
- }
- return cnffile;
- }
- /**
- * Open the connection with the database (and initialize
- * our default options).
- *
- * @param mc database context to initialze
- * @return #GNUNET_OK on success
- */
- static int
- iopen (struct GNUNET_MYSQL_Context *mc)
- {
- char *mysql_dbname;
- char *mysql_server;
- char *mysql_user;
- char *mysql_password;
- unsigned long long mysql_port;
- MYSQL_BOOL reconnect;
- unsigned int timeout;
- mc->dbf = mysql_init (NULL);
- if (mc->dbf == NULL)
- return GNUNET_SYSERR;
- if (mc->cnffile != NULL)
- mysql_options (mc->dbf, MYSQL_READ_DEFAULT_FILE, mc->cnffile);
- mysql_options (mc->dbf, MYSQL_READ_DEFAULT_GROUP, "client");
- reconnect = 0;
- mysql_options (mc->dbf, MYSQL_OPT_RECONNECT, &reconnect);
- mysql_options (mc->dbf, MYSQL_OPT_CONNECT_TIMEOUT, (const void *) &timeout);
- mysql_options (mc->dbf, MYSQL_SET_CHARSET_NAME, "UTF8");
- timeout = 60; /* in seconds */
- mysql_options (mc->dbf, MYSQL_OPT_READ_TIMEOUT, (const void *) &timeout);
- mysql_options (mc->dbf, MYSQL_OPT_WRITE_TIMEOUT, (const void *) &timeout);
- mysql_dbname = NULL;
- if (GNUNET_YES ==
- GNUNET_CONFIGURATION_have_value (mc->cfg, mc->section, "DATABASE"))
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (mc->cfg,
- mc->section,
- "DATABASE",
- &mysql_dbname));
- else
- mysql_dbname = GNUNET_strdup ("gnunet");
- mysql_user = NULL;
- if (GNUNET_YES ==
- GNUNET_CONFIGURATION_have_value (mc->cfg, mc->section, "USER"))
- {
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (mc->cfg,
- mc->section,
- "USER",
- &mysql_user));
- }
- mysql_password = NULL;
- if (GNUNET_YES ==
- GNUNET_CONFIGURATION_have_value (mc->cfg, mc->section, "PASSWORD"))
- {
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (mc->cfg,
- mc->section,
- "PASSWORD",
- &mysql_password));
- }
- mysql_server = NULL;
- if (GNUNET_YES ==
- GNUNET_CONFIGURATION_have_value (mc->cfg, mc->section, "HOST"))
- {
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (mc->cfg,
- mc->section,
- "HOST",
- &mysql_server));
- }
- mysql_port = 0;
- if (GNUNET_YES ==
- GNUNET_CONFIGURATION_have_value (mc->cfg, mc->section, "PORT"))
- {
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_number (mc->cfg,
- mc->section,
- "PORT",
- &mysql_port));
- }
- GNUNET_assert (mysql_dbname != NULL);
- mysql_real_connect (mc->dbf,
- mysql_server,
- mysql_user,
- mysql_password,
- mysql_dbname,
- (unsigned int) mysql_port,
- NULL,
- CLIENT_IGNORE_SIGPIPE);
- GNUNET_free (mysql_server);
- GNUNET_free (mysql_user);
- GNUNET_free (mysql_password);
- GNUNET_free (mysql_dbname);
- if (mysql_error (mc->dbf)[0])
- {
- LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, "mysql_real_connect", mc);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
- }
- /**
- * Create a mysql context.
- *
- * @param cfg configuration
- * @param section configuration section to use to get MySQL configuration options
- * @return the mysql context
- */
- struct GNUNET_MYSQL_Context *
- GNUNET_MYSQL_context_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
- const char *section)
- {
- struct GNUNET_MYSQL_Context *mc;
- mc = GNUNET_new (struct GNUNET_MYSQL_Context);
- mc->cfg = cfg;
- mc->section = section;
- mc->cnffile = get_my_cnf_path (cfg, section);
- return mc;
- }
- /**
- * Close database connection and all prepared statements (we got a DB
- * error).
- *
- * @param mc mysql context
- */
- void
- GNUNET_MYSQL_statements_invalidate (struct GNUNET_MYSQL_Context *mc)
- {
- struct GNUNET_MYSQL_StatementHandle *sh;
- for (sh = mc->shead; NULL != sh; sh = sh->next)
- {
- if (GNUNET_YES == sh->valid)
- {
- mysql_stmt_close (sh->statement);
- sh->valid = GNUNET_NO;
- }
- sh->statement = NULL;
- }
- if (NULL != mc->dbf)
- {
- mysql_close (mc->dbf);
- mc->dbf = NULL;
- }
- }
- /**
- * Destroy a mysql context. Also frees all associated prepared statements.
- *
- * @param mc context to destroy
- */
- void
- GNUNET_MYSQL_context_destroy (struct GNUNET_MYSQL_Context *mc)
- {
- struct GNUNET_MYSQL_StatementHandle *sh;
- GNUNET_MYSQL_statements_invalidate (mc);
- while (NULL != (sh = mc->shead))
- {
- GNUNET_CONTAINER_DLL_remove (mc->shead, mc->stail, sh);
- GNUNET_free (sh->query);
- GNUNET_free (sh);
- }
- GNUNET_free (mc);
- mysql_library_end ();
- }
- /**
- * Prepare a statement. Prepared statements are automatically discarded
- * when the MySQL context is destroyed.
- *
- * @param mc mysql context
- * @param query query text
- * @return prepared statement, NULL on error
- */
- struct GNUNET_MYSQL_StatementHandle *
- GNUNET_MYSQL_statement_prepare (struct GNUNET_MYSQL_Context *mc,
- const char *query)
- {
- struct GNUNET_MYSQL_StatementHandle *sh;
- sh = GNUNET_new (struct GNUNET_MYSQL_StatementHandle);
- sh->mc = mc;
- sh->query = GNUNET_strdup (query);
- GNUNET_CONTAINER_DLL_insert (mc->shead, mc->stail, sh);
- return sh;
- }
- /**
- * Run a SQL statement.
- *
- * @param mc mysql context
- * @param sql SQL statement to run
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR if there was a problem
- */
- int
- GNUNET_MYSQL_statement_run (struct GNUNET_MYSQL_Context *mc, const char *sql)
- {
- if ((NULL == mc->dbf) && (GNUNET_OK != iopen (mc)))
- return GNUNET_SYSERR;
- mysql_query (mc->dbf, sql);
- if (mysql_error (mc->dbf)[0])
- {
- LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, "mysql_query", mc);
- GNUNET_MYSQL_statements_invalidate (mc);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
- }
- /**
- * Prepare a statement for running.
- *
- * @param mc mysql context
- * @param sh statement handle to prepare
- * @return #GNUNET_OK on success
- */
- static int
- prepare_statement (struct GNUNET_MYSQL_StatementHandle *sh)
- {
- struct GNUNET_MYSQL_Context *mc = sh->mc;
- if (GNUNET_YES == sh->valid)
- return GNUNET_OK;
- if ((NULL == mc->dbf) && (GNUNET_OK != iopen (mc)))
- return GNUNET_SYSERR;
- sh->statement = mysql_stmt_init (mc->dbf);
- if (NULL == sh->statement)
- {
- GNUNET_MYSQL_statements_invalidate (mc);
- return GNUNET_SYSERR;
- }
- if (0 != mysql_stmt_prepare (sh->statement, sh->query, strlen (sh->query)))
- {
- GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
- "mysql",
- "prepare_statement: %s\n",
- sh->query);
- LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, "mysql_stmt_prepare", mc);
- mysql_stmt_close (sh->statement);
- sh->statement = NULL;
- GNUNET_MYSQL_statements_invalidate (mc);
- return GNUNET_SYSERR;
- }
- sh->valid = GNUNET_YES;
- return GNUNET_OK;
- }
- /**
- * Get internal handle for a prepared statement. This function should rarely
- * be used, and if, with caution! On failures during the interaction with
- * the handle, you must call 'GNUNET_MYSQL_statements_invalidate'!
- *
- * @param sh prepared statement to introspect
- * @return MySQL statement handle, NULL on error
- */
- MYSQL_STMT *
- GNUNET_MYSQL_statement_get_stmt (struct GNUNET_MYSQL_StatementHandle *sh)
- {
- (void) prepare_statement (sh);
- return sh->statement;
- }
- /* end of mysql.c */
|