2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @brief library to help with access to a MySQL database
23 * @author Christian Grothoff
26 #include <mysql/mysql.h>
27 #include "gnunet_mysql_lib.h"
30 * Maximum number of supported parameters for a prepared
31 * statement. Increase if needed.
37 * Die with an error message that indicates
38 * a failure of the command 'cmd' with the message given
41 #define DIE_MYSQL(cmd, dbh) \
44 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, \
46 _ ("`%s' failed at %s:%d with error: %s\n"), \
50 mysql_error ((dbh)->dbf)); \
55 * Log an error message at log-level 'level' that indicates
56 * a failure of the command 'cmd' on file 'filename'
57 * with the message given by strerror(errno).
59 #define LOG_MYSQL(level, cmd, dbh) \
62 GNUNET_log_from (level, \
64 _ ("`%s' failed at %s:%d with error: %s\n"), \
68 mysql_error ((dbh)->dbf)); \
75 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
117 struct GNUNET_MYSQL_StatementHandle *next;
122 struct GNUNET_MYSQL_StatementHandle *prev;
125 * Mysql Context the statement handle belongs to.
127 struct GNUNET_MYSQL_Context *mc;
130 * Original query string.
135 * Handle to MySQL prepared statement.
137 MYSQL_STMT *statement;
140 * Is the MySQL prepared statement valid, or do we need to re-initialize it?
147 * Obtain the location of ".my.cnf".
149 * @param cfg our configuration
150 * @param section the section
151 * @return NULL on error
154 get_my_cnf_path (const struct GNUNET_CONFIGURATION_Handle *cfg,
165 pw = getpwuid (getuid ());
168 GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_ERROR, "mysql", "getpwuid");
171 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (cfg, section, "CONFIG"))
173 GNUNET_assert (GNUNET_OK ==
174 GNUNET_CONFIGURATION_get_value_filename (cfg,
178 configured = GNUNET_YES;
182 home_dir = GNUNET_strdup (pw->pw_dir);
183 GNUNET_asprintf (&cnffile, "%s/.my.cnf", home_dir);
184 GNUNET_free (home_dir);
185 configured = GNUNET_NO;
188 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
190 _ ("Trying to use file `%s' for MySQL configuration.\n"),
192 if ((0 != stat (cnffile, &st)) || (0 != access (cnffile, R_OK)) ||
193 (! S_ISREG (st.st_mode)))
195 if (configured == GNUNET_YES)
196 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
198 _ ("Could not access file `%s': %s\n"),
201 GNUNET_free (cnffile);
209 * Open the connection with the database (and initialize
210 * our default options).
212 * @param mc database context to initialze
213 * @return #GNUNET_OK on success
216 iopen (struct GNUNET_MYSQL_Context *mc)
221 char *mysql_password;
222 unsigned long long mysql_port;
224 unsigned int timeout;
226 mc->dbf = mysql_init (NULL);
228 return GNUNET_SYSERR;
229 if (mc->cnffile != NULL)
230 mysql_options (mc->dbf, MYSQL_READ_DEFAULT_FILE, mc->cnffile);
231 mysql_options (mc->dbf, MYSQL_READ_DEFAULT_GROUP, "client");
233 mysql_options (mc->dbf, MYSQL_OPT_RECONNECT, &reconnect);
234 mysql_options (mc->dbf, MYSQL_OPT_CONNECT_TIMEOUT, (const void *) &timeout);
235 mysql_options (mc->dbf, MYSQL_SET_CHARSET_NAME, "UTF8");
236 timeout = 60; /* in seconds */
237 mysql_options (mc->dbf, MYSQL_OPT_READ_TIMEOUT, (const void *) &timeout);
238 mysql_options (mc->dbf, MYSQL_OPT_WRITE_TIMEOUT, (const void *) &timeout);
241 GNUNET_CONFIGURATION_have_value (mc->cfg, mc->section, "DATABASE"))
242 GNUNET_assert (GNUNET_OK ==
243 GNUNET_CONFIGURATION_get_value_string (mc->cfg,
248 mysql_dbname = GNUNET_strdup ("gnunet");
251 GNUNET_CONFIGURATION_have_value (mc->cfg, mc->section, "USER"))
253 GNUNET_assert (GNUNET_OK ==
254 GNUNET_CONFIGURATION_get_value_string (mc->cfg,
259 mysql_password = NULL;
261 GNUNET_CONFIGURATION_have_value (mc->cfg, mc->section, "PASSWORD"))
263 GNUNET_assert (GNUNET_OK ==
264 GNUNET_CONFIGURATION_get_value_string (mc->cfg,
271 GNUNET_CONFIGURATION_have_value (mc->cfg, mc->section, "HOST"))
273 GNUNET_assert (GNUNET_OK ==
274 GNUNET_CONFIGURATION_get_value_string (mc->cfg,
281 GNUNET_CONFIGURATION_have_value (mc->cfg, mc->section, "PORT"))
283 GNUNET_assert (GNUNET_OK ==
284 GNUNET_CONFIGURATION_get_value_number (mc->cfg,
290 GNUNET_assert (mysql_dbname != NULL);
291 mysql_real_connect (mc->dbf,
296 (unsigned int) mysql_port,
298 CLIENT_IGNORE_SIGPIPE);
299 GNUNET_free_non_null (mysql_server);
300 GNUNET_free_non_null (mysql_user);
301 GNUNET_free_non_null (mysql_password);
302 GNUNET_free (mysql_dbname);
303 if (mysql_error (mc->dbf)[0])
305 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, "mysql_real_connect", mc);
306 return GNUNET_SYSERR;
313 * Create a mysql context.
315 * @param cfg configuration
316 * @param section configuration section to use to get MySQL configuration options
317 * @return the mysql context
319 struct GNUNET_MYSQL_Context *
320 GNUNET_MYSQL_context_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
323 struct GNUNET_MYSQL_Context *mc;
325 mc = GNUNET_new (struct GNUNET_MYSQL_Context);
327 mc->section = section;
328 mc->cnffile = get_my_cnf_path (cfg, section);
335 * Close database connection and all prepared statements (we got a DB
338 * @param mc mysql context
341 GNUNET_MYSQL_statements_invalidate (struct GNUNET_MYSQL_Context *mc)
343 struct GNUNET_MYSQL_StatementHandle *sh;
345 for (sh = mc->shead; NULL != sh; sh = sh->next)
347 if (GNUNET_YES == sh->valid)
349 mysql_stmt_close (sh->statement);
350 sh->valid = GNUNET_NO;
352 sh->statement = NULL;
356 mysql_close (mc->dbf);
363 * Destroy a mysql context. Also frees all associated prepared statements.
365 * @param mc context to destroy
368 GNUNET_MYSQL_context_destroy (struct GNUNET_MYSQL_Context *mc)
370 struct GNUNET_MYSQL_StatementHandle *sh;
372 GNUNET_MYSQL_statements_invalidate (mc);
373 while (NULL != (sh = mc->shead))
375 GNUNET_CONTAINER_DLL_remove (mc->shead, mc->stail, sh);
376 GNUNET_free (sh->query);
380 mysql_library_end ();
385 * Prepare a statement. Prepared statements are automatically discarded
386 * when the MySQL context is destroyed.
388 * @param mc mysql context
389 * @param query query text
390 * @return prepared statement, NULL on error
392 struct GNUNET_MYSQL_StatementHandle *
393 GNUNET_MYSQL_statement_prepare (struct GNUNET_MYSQL_Context *mc,
396 struct GNUNET_MYSQL_StatementHandle *sh;
398 sh = GNUNET_new (struct GNUNET_MYSQL_StatementHandle);
400 sh->query = GNUNET_strdup (query);
401 GNUNET_CONTAINER_DLL_insert (mc->shead, mc->stail, sh);
407 * Run a SQL statement.
409 * @param mc mysql context
410 * @param sql SQL statement to run
411 * @return #GNUNET_OK on success
412 * #GNUNET_SYSERR if there was a problem
415 GNUNET_MYSQL_statement_run (struct GNUNET_MYSQL_Context *mc, const char *sql)
417 if ((NULL == mc->dbf) && (GNUNET_OK != iopen (mc)))
418 return GNUNET_SYSERR;
419 mysql_query (mc->dbf, sql);
420 if (mysql_error (mc->dbf)[0])
422 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, "mysql_query", mc);
423 GNUNET_MYSQL_statements_invalidate (mc);
424 return GNUNET_SYSERR;
431 * Prepare a statement for running.
433 * @param mc mysql context
434 * @param sh statement handle to prepare
435 * @return #GNUNET_OK on success
438 prepare_statement (struct GNUNET_MYSQL_StatementHandle *sh)
440 struct GNUNET_MYSQL_Context *mc = sh->mc;
442 if (GNUNET_YES == sh->valid)
444 if ((NULL == mc->dbf) && (GNUNET_OK != iopen (mc)))
445 return GNUNET_SYSERR;
446 sh->statement = mysql_stmt_init (mc->dbf);
447 if (NULL == sh->statement)
449 GNUNET_MYSQL_statements_invalidate (mc);
450 return GNUNET_SYSERR;
452 if (0 != mysql_stmt_prepare (sh->statement, sh->query, strlen (sh->query)))
454 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
456 "prepare_statement: %s\n",
458 LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, "mysql_stmt_prepare", mc);
459 mysql_stmt_close (sh->statement);
460 sh->statement = NULL;
461 GNUNET_MYSQL_statements_invalidate (mc);
462 return GNUNET_SYSERR;
464 sh->valid = GNUNET_YES;
470 * Get internal handle for a prepared statement. This function should rarely
471 * be used, and if, with caution! On failures during the interaction with
472 * the handle, you must call 'GNUNET_MYSQL_statements_invalidate'!
474 * @param sh prepared statement to introspect
475 * @return MySQL statement handle, NULL on error
478 GNUNET_MYSQL_statement_get_stmt (struct GNUNET_MYSQL_StatementHandle *sh)
480 (void) prepare_statement (sh);
481 return sh->statement;