aboutsummaryrefslogtreecommitdiff
path: root/src/pq/pq_connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pq/pq_connect.c')
-rw-r--r--src/pq/pq_connect.c543
1 files changed, 0 insertions, 543 deletions
diff --git a/src/pq/pq_connect.c b/src/pq/pq_connect.c
deleted file mode 100644
index 2e36f58f1..000000000
--- a/src/pq/pq_connect.c
+++ /dev/null
@@ -1,543 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017, 2019, 2020, 2021 GNUnet e.V.
4
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.
9
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.
14
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/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file pq/pq_connect.c
22 * @brief functions to connect to libpq (PostGres)
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "pq.h"
27#include <pthread.h>
28
29
30/**
31 * Function called by libpq whenever it wants to log something.
32 * We already log whenever we care, so this function does nothing
33 * and merely exists to silence the libpq logging.
34 *
35 * @param arg the SQL connection that was used
36 * @param res information about some libpq event
37 */
38static void
39pq_notice_receiver_cb (void *arg,
40 const PGresult *res)
41{
42 /* do nothing, intentionally */
43 (void) arg;
44 (void) res;
45}
46
47
48/**
49 * Function called by libpq whenever it wants to log something.
50 * We log those using the GNUnet logger.
51 *
52 * @param arg the SQL connection that was used
53 * @param message information about some libpq event
54 */
55static void
56pq_notice_processor_cb (void *arg,
57 const char *message)
58{
59 (void) arg;
60 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
61 "pq",
62 "%s",
63 message);
64}
65
66
67struct GNUNET_PQ_Context *
68GNUNET_PQ_connect (const char *config_str,
69 const char *load_path,
70 const struct GNUNET_PQ_ExecuteStatement *es,
71 const struct GNUNET_PQ_PreparedStatement *ps)
72{
73 return GNUNET_PQ_connect2 (config_str,
74 load_path,
75 NULL == load_path
76 ? NULL
77 : "",
78 es,
79 ps,
80 GNUNET_PQ_FLAG_NONE);
81}
82
83
84struct GNUNET_PQ_Context *
85GNUNET_PQ_connect2 (const char *config_str,
86 const char *load_path,
87 const char *auto_suffix,
88 const struct GNUNET_PQ_ExecuteStatement *es,
89 const struct GNUNET_PQ_PreparedStatement *ps,
90 enum GNUNET_PQ_Options flags)
91{
92 struct GNUNET_PQ_Context *db;
93 unsigned int elen = 0;
94 unsigned int plen = 0;
95
96 if (NULL != es)
97 while (NULL != es[elen].sql)
98 elen++;
99 if (NULL != ps)
100 while (NULL != ps[plen].name)
101 plen++;
102
103 db = GNUNET_new (struct GNUNET_PQ_Context);
104 db->flags = flags;
105 db->config_str = GNUNET_strdup (config_str);
106 if (NULL != load_path)
107 db->load_path = GNUNET_strdup (load_path);
108 if (NULL != auto_suffix)
109 db->auto_suffix = GNUNET_strdup (auto_suffix);
110 if (0 != elen)
111 {
112 db->es = GNUNET_new_array (elen + 1,
113 struct GNUNET_PQ_ExecuteStatement);
114 memcpy (db->es,
115 es,
116 elen * sizeof (struct GNUNET_PQ_ExecuteStatement));
117 }
118 if (0 != plen)
119 {
120 db->ps = GNUNET_new_array (plen + 1,
121 struct GNUNET_PQ_PreparedStatement);
122 memcpy (db->ps,
123 ps,
124 plen * sizeof (struct GNUNET_PQ_PreparedStatement));
125 }
126 db->channel_map = GNUNET_CONTAINER_multishortmap_create (16,
127 GNUNET_YES);
128 GNUNET_PQ_reconnect (db);
129 if (NULL == db->conn)
130 {
131 GNUNET_CONTAINER_multishortmap_destroy (db->channel_map);
132 GNUNET_free (db->load_path);
133 GNUNET_free (db->auto_suffix);
134 GNUNET_free (db->config_str);
135 GNUNET_free (db);
136 return NULL;
137 }
138 return db;
139}
140
141
142enum GNUNET_GenericReturnValue
143GNUNET_PQ_exec_sql (struct GNUNET_PQ_Context *db,
144 const char *buf)
145{
146 struct GNUNET_OS_Process *psql;
147 enum GNUNET_OS_ProcessStatusType type;
148 unsigned long code;
149 enum GNUNET_GenericReturnValue ret;
150 char *fn;
151
152 GNUNET_asprintf (&fn,
153 "%s%s.sql",
154 db->load_path,
155 buf);
156 if (GNUNET_YES !=
157 GNUNET_DISK_file_test (fn))
158 {
159 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
160 "SQL resource `%s' does not exist\n",
161 fn);
162 GNUNET_free (fn);
163 return GNUNET_NO;
164 }
165 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
166 "Applying SQL file `%s' on database %s\n",
167 fn,
168 db->config_str);
169 psql = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
170 NULL,
171 NULL,
172 NULL,
173 "psql",
174 "psql",
175 db->config_str,
176 "-f",
177 fn,
178 "-q",
179 "--set",
180 "ON_ERROR_STOP=1",
181 NULL);
182 if (NULL == psql)
183 {
184 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
185 "exec",
186 "psql");
187 GNUNET_free (fn);
188 return GNUNET_SYSERR;
189 }
190 ret = GNUNET_OS_process_wait_status (psql,
191 &type,
192 &code);
193 if (GNUNET_OK != ret)
194 {
195 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
196 "psql on file %s did not finish, killed it!\n",
197 fn);
198 /* can happen if we got a signal, like CTRL-C, before
199 psql was complete */
200 (void) GNUNET_OS_process_kill (psql,
201 SIGKILL);
202 GNUNET_OS_process_destroy (psql);
203 GNUNET_free (fn);
204 return GNUNET_SYSERR;
205 }
206 GNUNET_OS_process_destroy (psql);
207 if ( (GNUNET_OS_PROCESS_EXITED != type) ||
208 (0 != code) )
209 {
210 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
211 "Could not run PSQL on file %s: psql exit code was %d\n",
212 fn,
213 (int) code);
214 GNUNET_free (fn);
215 return GNUNET_SYSERR;
216 }
217 GNUNET_free (fn);
218 return GNUNET_OK;
219}
220
221
222enum GNUNET_GenericReturnValue
223GNUNET_PQ_run_sql (struct GNUNET_PQ_Context *db,
224 const char *load_path)
225{
226 const char *load_path_suffix;
227 size_t slen = strlen (load_path) + 10;
228
229 load_path_suffix = strrchr (load_path, '/');
230 if (NULL == load_path_suffix)
231 load_path_suffix = load_path;
232 else
233 load_path_suffix++; /* skip '/' */
234 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
235 "Loading SQL resources from `%s'\n",
236 load_path);
237 for (unsigned int i = 1; i<10000; i++)
238 {
239 char patch_name[slen];
240 enum GNUNET_DB_QueryStatus qs;
241
242 /* Check with DB versioning schema if this patch was already applied,
243 if so, skip it. */
244 GNUNET_snprintf (patch_name,
245 sizeof (patch_name),
246 "%s%04u",
247 load_path_suffix,
248 i);
249 {
250 char *applied_by;
251 struct GNUNET_PQ_QueryParam params[] = {
252 GNUNET_PQ_query_param_string (patch_name),
253 GNUNET_PQ_query_param_end
254 };
255 struct GNUNET_PQ_ResultSpec rs[] = {
256 GNUNET_PQ_result_spec_string ("applied_by",
257 &applied_by),
258 GNUNET_PQ_result_spec_end
259 };
260
261 qs = GNUNET_PQ_eval_prepared_singleton_select (db,
262 "gnunet_pq_check_patch",
263 params,
264 rs);
265 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
266 {
267 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
268 "Database version %s already applied by %s, skipping\n",
269 patch_name,
270 applied_by);
271 GNUNET_PQ_cleanup_result (rs);
272 }
273 if (GNUNET_DB_STATUS_HARD_ERROR == qs)
274 {
275 GNUNET_break (0);
276 return GNUNET_SYSERR;
277 }
278 }
279 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
280 continue; /* patch already applied, skip it */
281
282 if (0 != (GNUNET_PQ_FLAG_CHECK_CURRENT & db->flags))
283 {
284 /* We are only checking, found unapplied patch, bad! */
285 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
286 "Database outdated, patch %s missing. Aborting!\n",
287 patch_name);
288 return GNUNET_SYSERR;
289 }
290 else
291 {
292 /* patch not yet applied, run it! */
293 enum GNUNET_GenericReturnValue ret;
294
295 GNUNET_snprintf (patch_name,
296 sizeof (patch_name),
297 "%s%04u",
298 load_path,
299 i);
300 ret = GNUNET_PQ_exec_sql (db,
301 patch_name);
302 if (GNUNET_NO == ret)
303 break;
304 if (GNUNET_SYSERR == ret)
305 return GNUNET_SYSERR;
306 }
307 }
308 return GNUNET_OK;
309}
310
311
312void
313GNUNET_PQ_reconnect_if_down (struct GNUNET_PQ_Context *db)
314{
315 if (1 ==
316 PQconsumeInput (db->conn))
317 return;
318 if (CONNECTION_BAD != PQstatus (db->conn))
319 return;
320 GNUNET_PQ_reconnect (db);
321}
322
323
324void
325GNUNET_PQ_reconnect (struct GNUNET_PQ_Context *db)
326{
327 GNUNET_PQ_event_reconnect_ (db,
328 -1);
329 if (NULL != db->conn)
330 PQfinish (db->conn);
331 db->conn = PQconnectdb (db->config_str);
332 if ( (NULL == db->conn) ||
333 (CONNECTION_OK != PQstatus (db->conn)) )
334 {
335 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
336 "pq",
337 "Database connection to '%s' failed: %s\n",
338 db->config_str,
339 (NULL != db->conn)
340 ? PQerrorMessage (db->conn)
341 : "PQconnectdb returned NULL");
342 if (NULL != db->conn)
343 {
344 PQfinish (db->conn);
345 db->conn = NULL;
346 }
347 return;
348 }
349 PQsetNoticeReceiver (db->conn,
350 &pq_notice_receiver_cb,
351 db);
352 PQsetNoticeProcessor (db->conn,
353 &pq_notice_processor_cb,
354 db);
355 if (NULL != db->auto_suffix)
356 {
357 PGresult *res;
358
359 res = PQprepare (db->conn,
360 "gnunet_pq_check_patch",
361 "SELECT"
362 " applied_by"
363 " FROM _v.patches"
364 " WHERE patch_name = $1"
365 " LIMIT 1",
366 1,
367 NULL);
368 if (PGRES_COMMAND_OK != PQresultStatus (res))
369 {
370 enum GNUNET_GenericReturnValue ret;
371
372 PQclear (res);
373 if (0 != (db->flags & GNUNET_PQ_FLAG_DROP))
374 {
375 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
376 "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet. Not attempting drop!\n");
377 PQfinish (db->conn);
378 db->conn = NULL;
379 return;
380 }
381 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
382 "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet, loading versioning!\n");
383 ret = GNUNET_PQ_exec_sql (db,
384 "versioning");
385 if (GNUNET_NO == ret)
386 {
387 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
388 "Failed to find SQL file to load database versioning logic\n");
389 PQfinish (db->conn);
390 db->conn = NULL;
391 return;
392 }
393 if (GNUNET_SYSERR == ret)
394 {
395 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
396 "Failed to run SQL logic to setup database versioning logic\n");
397 PQfinish (db->conn);
398 db->conn = NULL;
399 return;
400 }
401 /* try again to prepare our statement! */
402 res = PQprepare (db->conn,
403 "gnunet_pq_check_patch",
404 "SELECT"
405 " applied_by"
406 " FROM _v.patches"
407 " WHERE patch_name = $1"
408 " LIMIT 1",
409 1,
410 NULL);
411 if (PGRES_COMMAND_OK != PQresultStatus (res))
412 {
413 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
414 "Failed to run SQL logic to setup database versioning logic: %s/%s\n",
415 PQresultErrorMessage (res),
416 PQerrorMessage (db->conn));
417 PQclear (res);
418 PQfinish (db->conn);
419 db->conn = NULL;
420 return;
421 }
422 }
423 PQclear (res);
424
425 if (GNUNET_SYSERR ==
426 GNUNET_PQ_run_sql (db,
427 db->auto_suffix))
428 {
429 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
430 "Failed to load SQL statements from `%s*'\n",
431 db->auto_suffix);
432 PQfinish (db->conn);
433 db->conn = NULL;
434 return;
435 }
436 }
437 if ( (NULL != db->es) &&
438 (GNUNET_OK !=
439 GNUNET_PQ_exec_statements (db,
440 db->es)) )
441 {
442 PQfinish (db->conn);
443 db->conn = NULL;
444 return;
445 }
446 if ( (NULL != db->ps) &&
447 (GNUNET_OK !=
448 GNUNET_PQ_prepare_statements (db,
449 db->ps)) )
450 {
451 PQfinish (db->conn);
452 db->conn = NULL;
453 return;
454 }
455 GNUNET_PQ_event_reconnect_ (db,
456 PQsocket (db->conn));
457}
458
459
460struct GNUNET_PQ_Context *
461GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
462 const char *section,
463 const char *load_path_suffix,
464 const struct GNUNET_PQ_ExecuteStatement *es,
465 const struct GNUNET_PQ_PreparedStatement *ps)
466{
467 return GNUNET_PQ_connect_with_cfg2 (cfg,
468 section,
469 load_path_suffix,
470 es,
471 ps,
472 GNUNET_PQ_FLAG_NONE);
473}
474
475
476struct GNUNET_PQ_Context *
477GNUNET_PQ_connect_with_cfg2 (const struct GNUNET_CONFIGURATION_Handle *cfg,
478 const char *section,
479 const char *load_path_suffix,
480 const struct GNUNET_PQ_ExecuteStatement *es,
481 const struct GNUNET_PQ_PreparedStatement *ps,
482 enum GNUNET_PQ_Options flags)
483{
484 struct GNUNET_PQ_Context *db;
485 char *conninfo;
486 char *load_path;
487
488 if (GNUNET_OK !=
489 GNUNET_CONFIGURATION_get_value_string (cfg,
490 section,
491 "CONFIG",
492 &conninfo))
493 conninfo = NULL;
494 load_path = NULL;
495 if (GNUNET_OK !=
496 GNUNET_CONFIGURATION_get_value_filename (cfg,
497 section,
498 "SQL_DIR",
499 &load_path))
500 {
501 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
502 section,
503 "SQL_DIR");
504 }
505 if ( (NULL != load_path_suffix) &&
506 (NULL == load_path) )
507 {
508 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
509 section,
510 "SQL_DIR");
511 return NULL;
512 }
513 db = GNUNET_PQ_connect2 (conninfo == NULL ? "" : conninfo,
514 load_path,
515 load_path_suffix,
516 es,
517 ps,
518 flags);
519 GNUNET_free (load_path);
520 GNUNET_free (conninfo);
521 return db;
522}
523
524
525void
526GNUNET_PQ_disconnect (struct GNUNET_PQ_Context *db)
527{
528 if (NULL == db)
529 return;
530 GNUNET_assert (0 ==
531 GNUNET_CONTAINER_multishortmap_size (db->channel_map));
532 GNUNET_CONTAINER_multishortmap_destroy (db->channel_map);
533 GNUNET_free (db->es);
534 GNUNET_free (db->ps);
535 GNUNET_free (db->load_path);
536 GNUNET_free (db->auto_suffix);
537 GNUNET_free (db->config_str);
538 PQfinish (db->conn);
539 GNUNET_free (db);
540}
541
542
543/* end of pq/pq_connect.c */