mysql.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. /*
  2. This file is part of GNUnet
  3. Copyright (C) 2012 GNUnet e.V.
  4. GNUnet is free software: you can redistribute it and/or modify it
  5. under the terms of the GNU Affero General Public License as published
  6. by the Free Software Foundation, either version 3 of the License,
  7. or (at your option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Affero General Public License for more details.
  12. You should have received a copy of the GNU Affero General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. SPDX-License-Identifier: AGPL3.0-or-later
  15. */
  16. /**
  17. * @file mysql/mysql.c
  18. * @brief library to help with access to a MySQL database
  19. * @author Christian Grothoff
  20. */
  21. #include "platform.h"
  22. #include <mysql/mysql.h>
  23. #include "gnunet_mysql_lib.h"
  24. /**
  25. * Maximum number of supported parameters for a prepared
  26. * statement. Increase if needed.
  27. */
  28. #define MAX_PARAM 16
  29. /**
  30. * Die with an error message that indicates
  31. * a failure of the command 'cmd' with the message given
  32. * by strerror(errno).
  33. */
  34. #define DIE_MYSQL(cmd, dbh) \
  35. do \
  36. { \
  37. GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, \
  38. "mysql", \
  39. _ ("`%s' failed at %s:%d with error: %s\n"), \
  40. cmd, \
  41. __FILE__, \
  42. __LINE__, \
  43. mysql_error ((dbh)->dbf)); \
  44. GNUNET_assert (0); \
  45. } while (0);
  46. /**
  47. * Log an error message at log-level 'level' that indicates
  48. * a failure of the command 'cmd' on file 'filename'
  49. * with the message given by strerror(errno).
  50. */
  51. #define LOG_MYSQL(level, cmd, dbh) \
  52. do \
  53. { \
  54. GNUNET_log_from (level, \
  55. "mysql", \
  56. _ ("`%s' failed at %s:%d with error: %s\n"), \
  57. cmd, \
  58. __FILE__, \
  59. __LINE__, \
  60. mysql_error ((dbh)->dbf)); \
  61. } while (0);
  62. /**
  63. * Mysql context.
  64. */
  65. struct GNUNET_MYSQL_Context
  66. {
  67. /**
  68. * Our configuration.
  69. */
  70. const struct GNUNET_CONFIGURATION_Handle *cfg;
  71. /**
  72. * Our section.
  73. */
  74. const char *section;
  75. /**
  76. * Handle to the mysql database.
  77. */
  78. MYSQL *dbf;
  79. /**
  80. * Head of list of our prepared statements.
  81. */
  82. struct GNUNET_MYSQL_StatementHandle *shead;
  83. /**
  84. * Tail of list of our prepared statements.
  85. */
  86. struct GNUNET_MYSQL_StatementHandle *stail;
  87. /**
  88. * Filename of "my.cnf" (msyql configuration).
  89. */
  90. char *cnffile;
  91. };
  92. /**
  93. * Handle for a prepared statement.
  94. */
  95. struct GNUNET_MYSQL_StatementHandle
  96. {
  97. /**
  98. * Kept in a DLL.
  99. */
  100. struct GNUNET_MYSQL_StatementHandle *next;
  101. /**
  102. * Kept in a DLL.
  103. */
  104. struct GNUNET_MYSQL_StatementHandle *prev;
  105. /**
  106. * Mysql Context the statement handle belongs to.
  107. */
  108. struct GNUNET_MYSQL_Context *mc;
  109. /**
  110. * Original query string.
  111. */
  112. char *query;
  113. /**
  114. * Handle to MySQL prepared statement.
  115. */
  116. MYSQL_STMT *statement;
  117. /**
  118. * Is the MySQL prepared statement valid, or do we need to re-initialize it?
  119. */
  120. int valid;
  121. };
  122. /**
  123. * Obtain the location of ".my.cnf".
  124. *
  125. * @param cfg our configuration
  126. * @param section the section
  127. * @return NULL on error
  128. */
  129. static char *
  130. get_my_cnf_path (const struct GNUNET_CONFIGURATION_Handle *cfg,
  131. const char *section)
  132. {
  133. char *cnffile;
  134. char *home_dir;
  135. struct stat st;
  136. struct passwd *pw;
  137. int configured;
  138. pw = getpwuid (getuid ());
  139. if (! pw)
  140. {
  141. GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_ERROR, "mysql", "getpwuid");
  142. return NULL;
  143. }
  144. if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (cfg, section, "CONFIG"))
  145. {
  146. GNUNET_assert (GNUNET_OK ==
  147. GNUNET_CONFIGURATION_get_value_filename (cfg,
  148. section,
  149. "CONFIG",
  150. &cnffile));
  151. configured = GNUNET_YES;
  152. }
  153. else
  154. {
  155. home_dir = GNUNET_strdup (pw->pw_dir);
  156. GNUNET_asprintf (&cnffile, "%s/.my.cnf", home_dir);
  157. GNUNET_free (home_dir);
  158. configured = GNUNET_NO;
  159. }
  160. GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
  161. "mysql",
  162. _ ("Trying to use file `%s' for MySQL configuration.\n"),
  163. cnffile);
  164. if ((0 != stat (cnffile, &st)) || (0 != access (cnffile, R_OK)) ||
  165. (! S_ISREG (st.st_mode)))
  166. {
  167. if (configured == GNUNET_YES)
  168. GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
  169. "mysql",
  170. _ ("Could not access file `%s': %s\n"),
  171. cnffile,
  172. strerror (errno));
  173. GNUNET_free (cnffile);
  174. return NULL;
  175. }
  176. return cnffile;
  177. }
  178. /**
  179. * Open the connection with the database (and initialize
  180. * our default options).
  181. *
  182. * @param mc database context to initialze
  183. * @return #GNUNET_OK on success
  184. */
  185. static int
  186. iopen (struct GNUNET_MYSQL_Context *mc)
  187. {
  188. char *mysql_dbname;
  189. char *mysql_server;
  190. char *mysql_user;
  191. char *mysql_password;
  192. unsigned long long mysql_port;
  193. MYSQL_BOOL reconnect;
  194. unsigned int timeout;
  195. mc->dbf = mysql_init (NULL);
  196. if (mc->dbf == NULL)
  197. return GNUNET_SYSERR;
  198. if (mc->cnffile != NULL)
  199. mysql_options (mc->dbf, MYSQL_READ_DEFAULT_FILE, mc->cnffile);
  200. mysql_options (mc->dbf, MYSQL_READ_DEFAULT_GROUP, "client");
  201. reconnect = 0;
  202. mysql_options (mc->dbf, MYSQL_OPT_RECONNECT, &reconnect);
  203. mysql_options (mc->dbf, MYSQL_OPT_CONNECT_TIMEOUT, (const void *) &timeout);
  204. mysql_options (mc->dbf, MYSQL_SET_CHARSET_NAME, "UTF8");
  205. timeout = 60; /* in seconds */
  206. mysql_options (mc->dbf, MYSQL_OPT_READ_TIMEOUT, (const void *) &timeout);
  207. mysql_options (mc->dbf, MYSQL_OPT_WRITE_TIMEOUT, (const void *) &timeout);
  208. mysql_dbname = NULL;
  209. if (GNUNET_YES ==
  210. GNUNET_CONFIGURATION_have_value (mc->cfg, mc->section, "DATABASE"))
  211. GNUNET_assert (GNUNET_OK ==
  212. GNUNET_CONFIGURATION_get_value_string (mc->cfg,
  213. mc->section,
  214. "DATABASE",
  215. &mysql_dbname));
  216. else
  217. mysql_dbname = GNUNET_strdup ("gnunet");
  218. mysql_user = NULL;
  219. if (GNUNET_YES ==
  220. GNUNET_CONFIGURATION_have_value (mc->cfg, mc->section, "USER"))
  221. {
  222. GNUNET_assert (GNUNET_OK ==
  223. GNUNET_CONFIGURATION_get_value_string (mc->cfg,
  224. mc->section,
  225. "USER",
  226. &mysql_user));
  227. }
  228. mysql_password = NULL;
  229. if (GNUNET_YES ==
  230. GNUNET_CONFIGURATION_have_value (mc->cfg, mc->section, "PASSWORD"))
  231. {
  232. GNUNET_assert (GNUNET_OK ==
  233. GNUNET_CONFIGURATION_get_value_string (mc->cfg,
  234. mc->section,
  235. "PASSWORD",
  236. &mysql_password));
  237. }
  238. mysql_server = NULL;
  239. if (GNUNET_YES ==
  240. GNUNET_CONFIGURATION_have_value (mc->cfg, mc->section, "HOST"))
  241. {
  242. GNUNET_assert (GNUNET_OK ==
  243. GNUNET_CONFIGURATION_get_value_string (mc->cfg,
  244. mc->section,
  245. "HOST",
  246. &mysql_server));
  247. }
  248. mysql_port = 0;
  249. if (GNUNET_YES ==
  250. GNUNET_CONFIGURATION_have_value (mc->cfg, mc->section, "PORT"))
  251. {
  252. GNUNET_assert (GNUNET_OK ==
  253. GNUNET_CONFIGURATION_get_value_number (mc->cfg,
  254. mc->section,
  255. "PORT",
  256. &mysql_port));
  257. }
  258. GNUNET_assert (mysql_dbname != NULL);
  259. mysql_real_connect (mc->dbf,
  260. mysql_server,
  261. mysql_user,
  262. mysql_password,
  263. mysql_dbname,
  264. (unsigned int) mysql_port,
  265. NULL,
  266. CLIENT_IGNORE_SIGPIPE);
  267. GNUNET_free (mysql_server);
  268. GNUNET_free (mysql_user);
  269. GNUNET_free (mysql_password);
  270. GNUNET_free (mysql_dbname);
  271. if (mysql_error (mc->dbf)[0])
  272. {
  273. LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, "mysql_real_connect", mc);
  274. return GNUNET_SYSERR;
  275. }
  276. return GNUNET_OK;
  277. }
  278. /**
  279. * Create a mysql context.
  280. *
  281. * @param cfg configuration
  282. * @param section configuration section to use to get MySQL configuration options
  283. * @return the mysql context
  284. */
  285. struct GNUNET_MYSQL_Context *
  286. GNUNET_MYSQL_context_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
  287. const char *section)
  288. {
  289. struct GNUNET_MYSQL_Context *mc;
  290. mc = GNUNET_new (struct GNUNET_MYSQL_Context);
  291. mc->cfg = cfg;
  292. mc->section = section;
  293. mc->cnffile = get_my_cnf_path (cfg, section);
  294. return mc;
  295. }
  296. /**
  297. * Close database connection and all prepared statements (we got a DB
  298. * error).
  299. *
  300. * @param mc mysql context
  301. */
  302. void
  303. GNUNET_MYSQL_statements_invalidate (struct GNUNET_MYSQL_Context *mc)
  304. {
  305. struct GNUNET_MYSQL_StatementHandle *sh;
  306. for (sh = mc->shead; NULL != sh; sh = sh->next)
  307. {
  308. if (GNUNET_YES == sh->valid)
  309. {
  310. mysql_stmt_close (sh->statement);
  311. sh->valid = GNUNET_NO;
  312. }
  313. sh->statement = NULL;
  314. }
  315. if (NULL != mc->dbf)
  316. {
  317. mysql_close (mc->dbf);
  318. mc->dbf = NULL;
  319. }
  320. }
  321. /**
  322. * Destroy a mysql context. Also frees all associated prepared statements.
  323. *
  324. * @param mc context to destroy
  325. */
  326. void
  327. GNUNET_MYSQL_context_destroy (struct GNUNET_MYSQL_Context *mc)
  328. {
  329. struct GNUNET_MYSQL_StatementHandle *sh;
  330. GNUNET_MYSQL_statements_invalidate (mc);
  331. while (NULL != (sh = mc->shead))
  332. {
  333. GNUNET_CONTAINER_DLL_remove (mc->shead, mc->stail, sh);
  334. GNUNET_free (sh->query);
  335. GNUNET_free (sh);
  336. }
  337. GNUNET_free (mc);
  338. mysql_library_end ();
  339. }
  340. /**
  341. * Prepare a statement. Prepared statements are automatically discarded
  342. * when the MySQL context is destroyed.
  343. *
  344. * @param mc mysql context
  345. * @param query query text
  346. * @return prepared statement, NULL on error
  347. */
  348. struct GNUNET_MYSQL_StatementHandle *
  349. GNUNET_MYSQL_statement_prepare (struct GNUNET_MYSQL_Context *mc,
  350. const char *query)
  351. {
  352. struct GNUNET_MYSQL_StatementHandle *sh;
  353. sh = GNUNET_new (struct GNUNET_MYSQL_StatementHandle);
  354. sh->mc = mc;
  355. sh->query = GNUNET_strdup (query);
  356. GNUNET_CONTAINER_DLL_insert (mc->shead, mc->stail, sh);
  357. return sh;
  358. }
  359. /**
  360. * Run a SQL statement.
  361. *
  362. * @param mc mysql context
  363. * @param sql SQL statement to run
  364. * @return #GNUNET_OK on success
  365. * #GNUNET_SYSERR if there was a problem
  366. */
  367. int
  368. GNUNET_MYSQL_statement_run (struct GNUNET_MYSQL_Context *mc, const char *sql)
  369. {
  370. if ((NULL == mc->dbf) && (GNUNET_OK != iopen (mc)))
  371. return GNUNET_SYSERR;
  372. mysql_query (mc->dbf, sql);
  373. if (mysql_error (mc->dbf)[0])
  374. {
  375. LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, "mysql_query", mc);
  376. GNUNET_MYSQL_statements_invalidate (mc);
  377. return GNUNET_SYSERR;
  378. }
  379. return GNUNET_OK;
  380. }
  381. /**
  382. * Prepare a statement for running.
  383. *
  384. * @param mc mysql context
  385. * @param sh statement handle to prepare
  386. * @return #GNUNET_OK on success
  387. */
  388. static int
  389. prepare_statement (struct GNUNET_MYSQL_StatementHandle *sh)
  390. {
  391. struct GNUNET_MYSQL_Context *mc = sh->mc;
  392. if (GNUNET_YES == sh->valid)
  393. return GNUNET_OK;
  394. if ((NULL == mc->dbf) && (GNUNET_OK != iopen (mc)))
  395. return GNUNET_SYSERR;
  396. sh->statement = mysql_stmt_init (mc->dbf);
  397. if (NULL == sh->statement)
  398. {
  399. GNUNET_MYSQL_statements_invalidate (mc);
  400. return GNUNET_SYSERR;
  401. }
  402. if (0 != mysql_stmt_prepare (sh->statement, sh->query, strlen (sh->query)))
  403. {
  404. GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
  405. "mysql",
  406. "prepare_statement: %s\n",
  407. sh->query);
  408. LOG_MYSQL (GNUNET_ERROR_TYPE_ERROR, "mysql_stmt_prepare", mc);
  409. mysql_stmt_close (sh->statement);
  410. sh->statement = NULL;
  411. GNUNET_MYSQL_statements_invalidate (mc);
  412. return GNUNET_SYSERR;
  413. }
  414. sh->valid = GNUNET_YES;
  415. return GNUNET_OK;
  416. }
  417. /**
  418. * Get internal handle for a prepared statement. This function should rarely
  419. * be used, and if, with caution! On failures during the interaction with
  420. * the handle, you must call 'GNUNET_MYSQL_statements_invalidate'!
  421. *
  422. * @param sh prepared statement to introspect
  423. * @return MySQL statement handle, NULL on error
  424. */
  425. MYSQL_STMT *
  426. GNUNET_MYSQL_statement_get_stmt (struct GNUNET_MYSQL_StatementHandle *sh)
  427. {
  428. (void) prepare_statement (sh);
  429. return sh->statement;
  430. }
  431. /* end of mysql.c */