3 This file is part of GNUnet
4 Copyright (C) 2012 GNUnet e.V.
6 GNUnet is free software: you can redistribute it and/or modify it
7 under the terms of the GNU Affero General Public License as published
8 by the Free Software Foundation, either version 3 of the License,
9 or (at your option) any later version.
11 GNUnet is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Affero General Public License for more details.
16 You should have received a copy of the GNU Affero General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 SPDX-License-Identifier: AGPL3.0-or-later
23 * @brief library to help with access to a MySQL database
24 * @author Christian Grothoff
27 #include <mysql/mysql.h>
28 #include "gnunet_mysql_lib.h"
31 * Maximum number of supported parameters for a prepared
32 * statement. Increase if needed.
38 * Die with an error message that indicates
39 * a failure of the command 'cmd' with the message given
42 #define DIE_MYSQL(cmd, dbh) \
45 GNUNET_log_from(GNUNET_ERROR_TYPE_ERROR, \
47 _("`%s' failed at %s:%d with error: %s\n"), \
51 mysql_error((dbh)->dbf)); \
56 * Log an error message at log-level 'level' that indicates
57 * a failure of the command 'cmd' on file 'filename'
58 * with the message given by strerror(errno).
60 #define LOG_MYSQL(level, cmd, dbh) \
63 GNUNET_log_from(level, \
65 _("`%s' failed at %s:%d with error: %s\n"), \
69 mysql_error((dbh)->dbf)); \
76 struct GNUNET_MYSQL_Context {
80 const struct GNUNET_CONFIGURATION_Handle *cfg;
88 * Handle to the mysql database.
93 * Head of list of our prepared statements.
95 struct GNUNET_MYSQL_StatementHandle *shead;
98 * Tail of list of our prepared statements.
100 struct GNUNET_MYSQL_StatementHandle *stail;
103 * Filename of "my.cnf" (msyql configuration).
110 * Handle for a prepared statement.
112 struct GNUNET_MYSQL_StatementHandle {
116 struct GNUNET_MYSQL_StatementHandle *next;
121 struct GNUNET_MYSQL_StatementHandle *prev;
124 * Mysql Context the statement handle belongs to.
126 struct GNUNET_MYSQL_Context *mc;
129 * Original query string.
134 * Handle to MySQL prepared statement.
136 MYSQL_STMT *statement;
139 * Is the MySQL prepared statement valid, or do we need to re-initialize it?
146 * Obtain the location of ".my.cnf".
148 * @param cfg our configuration
149 * @param section the section
150 * @return NULL on error
153 get_my_cnf_path(const struct GNUNET_CONFIGURATION_Handle *cfg,
166 pw = getpwuid(getuid());
169 GNUNET_log_from_strerror(GNUNET_ERROR_TYPE_ERROR, "mysql", "getpwuid");
172 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value(cfg, section, "CONFIG"))
174 GNUNET_assert(GNUNET_OK ==
175 GNUNET_CONFIGURATION_get_value_filename(cfg,
179 configured = GNUNET_YES;
183 home_dir = GNUNET_strdup(pw->pw_dir);
184 GNUNET_asprintf(&cnffile, "%s/.my.cnf", home_dir);
185 GNUNET_free(home_dir);
186 configured = GNUNET_NO;
189 home_dir = (char *)GNUNET_malloc(_MAX_PATH + 1);
190 plibc_conv_to_win_path("~/", home_dir);
191 GNUNET_asprintf(&cnffile, "%s/.my.cnf", home_dir);
192 GNUNET_free(home_dir);
193 configured = GNUNET_NO;
195 GNUNET_log_from(GNUNET_ERROR_TYPE_INFO,
197 _("Trying to use file `%s' for MySQL configuration.\n"),
199 if ((0 != STAT(cnffile, &st)) || (0 != ACCESS(cnffile, R_OK)) ||
200 (!S_ISREG(st.st_mode)))
202 if (configured == GNUNET_YES)
203 GNUNET_log_from(GNUNET_ERROR_TYPE_ERROR,
205 _("Could not access file `%s': %s\n"),
208 GNUNET_free(cnffile);
216 * Open the connection with the database (and initialize
217 * our default options).
219 * @param mc database context to initialze
220 * @return #GNUNET_OK on success
223 iopen(struct GNUNET_MYSQL_Context *mc)
228 char *mysql_password;
229 unsigned long long mysql_port;
231 unsigned int timeout;
233 mc->dbf = mysql_init(NULL);
235 return GNUNET_SYSERR;
236 if (mc->cnffile != NULL)
237 mysql_options(mc->dbf, MYSQL_READ_DEFAULT_FILE, mc->cnffile);
238 mysql_options(mc->dbf, MYSQL_READ_DEFAULT_GROUP, "client");
240 mysql_options(mc->dbf, MYSQL_OPT_RECONNECT, &reconnect);
241 mysql_options(mc->dbf, MYSQL_OPT_CONNECT_TIMEOUT, (const void *)&timeout);
242 mysql_options(mc->dbf, MYSQL_SET_CHARSET_NAME, "UTF8");
243 timeout = 60; /* in seconds */
244 mysql_options(mc->dbf, MYSQL_OPT_READ_TIMEOUT, (const void *)&timeout);
245 mysql_options(mc->dbf, MYSQL_OPT_WRITE_TIMEOUT, (const void *)&timeout);
248 GNUNET_CONFIGURATION_have_value(mc->cfg, mc->section, "DATABASE"))
249 GNUNET_assert(GNUNET_OK ==
250 GNUNET_CONFIGURATION_get_value_string(mc->cfg,
255 mysql_dbname = GNUNET_strdup("gnunet");
258 GNUNET_CONFIGURATION_have_value(mc->cfg, mc->section, "USER"))
260 GNUNET_assert(GNUNET_OK ==
261 GNUNET_CONFIGURATION_get_value_string(mc->cfg,
266 mysql_password = NULL;
268 GNUNET_CONFIGURATION_have_value(mc->cfg, mc->section, "PASSWORD"))
270 GNUNET_assert(GNUNET_OK ==
271 GNUNET_CONFIGURATION_get_value_string(mc->cfg,
278 GNUNET_CONFIGURATION_have_value(mc->cfg, mc->section, "HOST"))
280 GNUNET_assert(GNUNET_OK ==
281 GNUNET_CONFIGURATION_get_value_string(mc->cfg,
288 GNUNET_CONFIGURATION_have_value(mc->cfg, mc->section, "PORT"))
290 GNUNET_assert(GNUNET_OK ==
291 GNUNET_CONFIGURATION_get_value_number(mc->cfg,
297 GNUNET_assert(mysql_dbname != NULL);
298 mysql_real_connect(mc->dbf,
303 (unsigned int)mysql_port,
305 CLIENT_IGNORE_SIGPIPE);
306 GNUNET_free_non_null(mysql_server);
307 GNUNET_free_non_null(mysql_user);
308 GNUNET_free_non_null(mysql_password);
309 GNUNET_free(mysql_dbname);
310 if (mysql_error(mc->dbf)[0])
312 LOG_MYSQL(GNUNET_ERROR_TYPE_ERROR, "mysql_real_connect", mc);
313 return GNUNET_SYSERR;
320 * Create a mysql context.
322 * @param cfg configuration
323 * @param section configuration section to use to get MySQL configuration options
324 * @return the mysql context
326 struct GNUNET_MYSQL_Context *
327 GNUNET_MYSQL_context_create(const struct GNUNET_CONFIGURATION_Handle *cfg,
330 struct GNUNET_MYSQL_Context *mc;
332 mc = GNUNET_new(struct GNUNET_MYSQL_Context);
334 mc->section = section;
335 mc->cnffile = get_my_cnf_path(cfg, section);
342 * Close database connection and all prepared statements (we got a DB
345 * @param mc mysql context
348 GNUNET_MYSQL_statements_invalidate(struct GNUNET_MYSQL_Context *mc)
350 struct GNUNET_MYSQL_StatementHandle *sh;
352 for (sh = mc->shead; NULL != sh; sh = sh->next)
354 if (GNUNET_YES == sh->valid)
356 mysql_stmt_close(sh->statement);
357 sh->valid = GNUNET_NO;
359 sh->statement = NULL;
363 mysql_close(mc->dbf);
370 * Destroy a mysql context. Also frees all associated prepared statements.
372 * @param mc context to destroy
375 GNUNET_MYSQL_context_destroy(struct GNUNET_MYSQL_Context *mc)
377 struct GNUNET_MYSQL_StatementHandle *sh;
379 GNUNET_MYSQL_statements_invalidate(mc);
380 while (NULL != (sh = mc->shead))
382 GNUNET_CONTAINER_DLL_remove(mc->shead, mc->stail, sh);
383 GNUNET_free(sh->query);
392 * Prepare a statement. Prepared statements are automatically discarded
393 * when the MySQL context is destroyed.
395 * @param mc mysql context
396 * @param query query text
397 * @return prepared statement, NULL on error
399 struct GNUNET_MYSQL_StatementHandle *
400 GNUNET_MYSQL_statement_prepare(struct GNUNET_MYSQL_Context *mc,
403 struct GNUNET_MYSQL_StatementHandle *sh;
405 sh = GNUNET_new(struct GNUNET_MYSQL_StatementHandle);
407 sh->query = GNUNET_strdup(query);
408 GNUNET_CONTAINER_DLL_insert(mc->shead, mc->stail, sh);
414 * Run a SQL statement.
416 * @param mc mysql context
417 * @param sql SQL statement to run
418 * @return #GNUNET_OK on success
419 * #GNUNET_SYSERR if there was a problem
422 GNUNET_MYSQL_statement_run(struct GNUNET_MYSQL_Context *mc, const char *sql)
424 if ((NULL == mc->dbf) && (GNUNET_OK != iopen(mc)))
425 return GNUNET_SYSERR;
426 mysql_query(mc->dbf, sql);
427 if (mysql_error(mc->dbf)[0])
429 LOG_MYSQL(GNUNET_ERROR_TYPE_ERROR, "mysql_query", mc);
430 GNUNET_MYSQL_statements_invalidate(mc);
431 return GNUNET_SYSERR;
438 * Prepare a statement for running.
440 * @param mc mysql context
441 * @param sh statement handle to prepare
442 * @return #GNUNET_OK on success
445 prepare_statement(struct GNUNET_MYSQL_StatementHandle *sh)
447 struct GNUNET_MYSQL_Context *mc = sh->mc;
449 if (GNUNET_YES == sh->valid)
451 if ((NULL == mc->dbf) && (GNUNET_OK != iopen(mc)))
452 return GNUNET_SYSERR;
453 sh->statement = mysql_stmt_init(mc->dbf);
454 if (NULL == sh->statement)
456 GNUNET_MYSQL_statements_invalidate(mc);
457 return GNUNET_SYSERR;
459 if (0 != mysql_stmt_prepare(sh->statement, sh->query, strlen(sh->query)))
461 GNUNET_log_from(GNUNET_ERROR_TYPE_ERROR,
463 "prepare_statement: %s\n",
465 LOG_MYSQL(GNUNET_ERROR_TYPE_ERROR, "mysql_stmt_prepare", mc);
466 mysql_stmt_close(sh->statement);
467 sh->statement = NULL;
468 GNUNET_MYSQL_statements_invalidate(mc);
469 return GNUNET_SYSERR;
471 sh->valid = GNUNET_YES;
477 * Get internal handle for a prepared statement. This function should rarely
478 * be used, and if, with caution! On failures during the interaction with
479 * the handle, you must call 'GNUNET_MYSQL_statements_invalidate'!
481 * @param sh prepared statement to introspect
482 * @return MySQL statement handle, NULL on error
485 GNUNET_MYSQL_statement_get_stmt(struct GNUNET_MYSQL_StatementHandle *sh)
487 (void)prepare_statement(sh);
488 return sh->statement;