GNUnet  0.11.x
pq_connect.c
Go to the documentation of this file.
1 /*
2  This file is part of GNUnet
3  Copyright (C) 2017, 2019, 2020 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  */
25 #include "platform.h"
26 #include "pq.h"
27 #include <pthread.h>
28 
29 
38 static void
40  const PGresult *res)
41 {
42  /* do nothing, intentionally */
43  (void) arg;
44  (void) res;
45 }
46 
47 
55 static void
57  const char *message)
58 {
59  (void) arg;
61  "pq",
62  "%s",
63  message);
64 }
65 
66 
67 struct GNUNET_PQ_Context *
69  const char *load_path,
70  const struct GNUNET_PQ_ExecuteStatement *es,
71  const struct GNUNET_PQ_PreparedStatement *ps)
72 {
73  struct GNUNET_PQ_Context *db;
74  unsigned int elen = 0;
75  unsigned int plen = 0;
76 
77  if (NULL != es)
78  while (NULL != es[elen].sql)
79  elen++;
80  if (NULL != ps)
81  while (NULL != ps[plen].name)
82  plen++;
83 
84  db = GNUNET_new (struct GNUNET_PQ_Context);
85  db->config_str = GNUNET_strdup (config_str);
86  if (NULL != load_path)
87  db->load_path = GNUNET_strdup (load_path);
88  if (0 != elen)
89  {
90  db->es = GNUNET_new_array (elen + 1,
92  memcpy (db->es,
93  es,
94  elen * sizeof (struct GNUNET_PQ_ExecuteStatement));
95  }
96  if (0 != plen)
97  {
98  db->ps = GNUNET_new_array (plen + 1,
100  memcpy (db->ps,
101  ps,
102  plen * sizeof (struct GNUNET_PQ_PreparedStatement));
103  }
105  GNUNET_YES);
106  GNUNET_assert (0 ==
107  pthread_mutex_init (&db->notify_lock,
108  NULL));
109  GNUNET_PQ_reconnect (db);
110  if (NULL == db->conn)
111  {
112  GNUNET_free (db->load_path);
113  GNUNET_free (db->config_str);
114  GNUNET_free (db);
115  return NULL;
116  }
117  return db;
118 }
119 
120 
129 static enum GNUNET_GenericReturnValue
131  const char *load_path,
132  unsigned int i)
133 {
134  struct GNUNET_OS_Process *psql;
136  unsigned long code;
137  size_t slen = strlen (load_path) + 10;
138  char buf[slen];
139 
140  GNUNET_snprintf (buf,
141  sizeof (buf),
142  "%s%04u.sql",
143  load_path,
144  i);
146  "Applying SQL file `%s' on database %s\n",
147  buf,
148  db->config_str);
150  NULL,
151  NULL,
152  NULL,
153  "psql",
154  "psql",
155  db->config_str,
156  "-f",
157  buf,
158  "-q",
159  "--set",
160  "ON_ERROR_STOP=1",
161  NULL);
162  if (NULL == psql)
163  {
165  "exec",
166  "psql");
167  return GNUNET_SYSERR;
168  }
171  &type,
172  &code));
174  if ( (GNUNET_OS_PROCESS_EXITED != type) ||
175  (0 != code) )
176  {
178  "Could not run PSQL on file %s: %d\n",
179  buf,
180  (int) code);
181  return GNUNET_SYSERR;
182  }
183  return GNUNET_OK;
184 }
185 
186 
189  const char *load_path)
190 {
191  const char *load_path_suffix;
192  size_t slen = strlen (load_path) + 10;
193 
194  load_path_suffix = strrchr (load_path, '/');
195  if (NULL == load_path_suffix)
196  {
197  GNUNET_break (0);
198  return GNUNET_SYSERR;
199  }
200  load_path_suffix++; /* skip '/' */
202  "Loading SQL resources from `%s'\n",
203  load_path);
204  for (unsigned int i = 1; i<10000; i++)
205  {
206  enum GNUNET_DB_QueryStatus qs;
207  {
208  char buf[slen];
209 
210  /* First, check patch actually exists */
211  GNUNET_snprintf (buf,
212  sizeof (buf),
213  "%s%04u.sql",
214  load_path,
215  i);
216  if (GNUNET_YES !=
217  GNUNET_DISK_file_test (buf))
218  return GNUNET_OK; /* We are done */
219  }
220 
221  /* Second, check with DB versioning schema if this patch was already applied,
222  if so, skip it. */
223  {
224  char patch_name[slen];
225 
226  GNUNET_snprintf (patch_name,
227  sizeof (patch_name),
228  "%s%04u",
229  load_path_suffix,
230  i);
231  {
232  char *applied_by;
233  struct GNUNET_PQ_QueryParam params[] = {
234  GNUNET_PQ_query_param_string (patch_name),
236  };
237  struct GNUNET_PQ_ResultSpec rs[] = {
238  GNUNET_PQ_result_spec_string ("applied_by",
239  &applied_by),
241  };
242 
244  "gnunet_pq_check_patch",
245  params,
246  rs);
248  {
250  "Database version %s already applied by %s, skipping\n",
251  patch_name,
252  applied_by);
254  }
255  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
256  {
257  GNUNET_break (0);
258  return GNUNET_SYSERR;
259  }
260  }
261  }
263  continue; /* patch already applied, skip it */
264 
265  /* patch not yet applied, run it! */
266  {
267  int ret;
268 
269  ret = apply_patch (db,
270  load_path,
271  i);
272  if (GNUNET_NO == ret)
273  break;
274  if (GNUNET_SYSERR == ret)
275  return GNUNET_SYSERR;
276  }
277  }
278  return GNUNET_OK;
279 }
280 
281 
282 void
284 {
285  if (1 ==
286  PQconsumeInput (db->conn))
287  return;
288  if (CONNECTION_BAD != PQstatus (db->conn))
289  return;
290  GNUNET_PQ_reconnect (db);
291 }
292 
293 
294 void
296 {
297  if (NULL != db->sc)
298  db->sc (db->sc_cls,
299  -1);
300  if (NULL != db->conn)
301  PQfinish (db->conn);
302  db->conn = PQconnectdb (db->config_str);
303  if ( (NULL == db->conn) ||
304  (CONNECTION_OK != PQstatus (db->conn)) )
305  {
307  "pq",
308  "Database connection to '%s' failed: %s\n",
309  db->config_str,
310  (NULL != db->conn) ?
311  PQerrorMessage (db->conn)
312  : "PQconnectdb returned NULL");
313  if (NULL != db->conn)
314  {
315  PQfinish (db->conn);
316  db->conn = NULL;
317  }
318  return;
319  }
320  PQsetNoticeReceiver (db->conn,
322  db);
323  PQsetNoticeProcessor (db->conn,
325  db);
326  if (NULL != db->load_path)
327  {
328  PGresult *res;
329 
330  res = PQprepare (db->conn,
331  "gnunet_pq_check_patch",
332  "SELECT"
333  " applied_by"
334  " FROM _v.patches"
335  " WHERE patch_name = $1"
336  " LIMIT 1",
337  1,
338  NULL);
339  if (PGRES_COMMAND_OK != PQresultStatus (res))
340  {
341  int ret;
342 
343  PQclear (res);
345  "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet, loading patch level 0000!\n");
346  ret = apply_patch (db,
347  db->load_path,
348  0);
349  if (GNUNET_NO == ret)
350  {
352  "Failed to find SQL file to load database versioning logic\n");
353  PQfinish (db->conn);
354  db->conn = NULL;
355  return;
356  }
357  if (GNUNET_SYSERR == ret)
358  {
360  "Failed to run SQL logic to setup database versioning logic\n");
361  PQfinish (db->conn);
362  db->conn = NULL;
363  return;
364  }
365  /* try again to prepare our statement! */
366  res = PQprepare (db->conn,
367  "gnunet_pq_check_patch",
368  "SELECT"
369  " applied_by"
370  " FROM _v.patches"
371  " WHERE patch_name = $1"
372  " LIMIT 1",
373  1,
374  NULL);
375  if (PGRES_COMMAND_OK != PQresultStatus (res))
376  {
378  "Failed to run SQL logic to setup database versioning logic: %s/%s\n",
379  PQresultErrorMessage (res),
380  PQerrorMessage (db->conn));
381  PQclear (res);
382  PQfinish (db->conn);
383  db->conn = NULL;
384  return;
385  }
386  }
387  PQclear (res);
388 
389  if (GNUNET_SYSERR ==
390  GNUNET_PQ_run_sql (db,
391  db->load_path))
392  {
394  "Failed to load SQL statements from `%s*'\n",
395  db->load_path);
396  PQfinish (db->conn);
397  db->conn = NULL;
398  return;
399  }
400  }
401  if ( (NULL != db->es) &&
402  (GNUNET_OK !=
404  db->es)) )
405  {
406  PQfinish (db->conn);
407  db->conn = NULL;
408  return;
409  }
410  if ( (NULL != db->ps) &&
411  (GNUNET_OK !=
413  db->ps)) )
414  {
415  PQfinish (db->conn);
416  db->conn = NULL;
417  return;
418  }
420  if ( (NULL != db->sc) &&
422  db->sc (db->sc_cls,
423  PQsocket (db->conn));
424 }
425 
426 
427 struct GNUNET_PQ_Context *
429  const char *section,
430  const char *load_path_suffix,
431  const struct GNUNET_PQ_ExecuteStatement *es,
432  const struct GNUNET_PQ_PreparedStatement *ps)
433 {
434  struct GNUNET_PQ_Context *db;
435  char *conninfo;
436  char *load_path;
437  char *sp;
438 
439  if (GNUNET_OK !=
441  section,
442  "CONFIG",
443  &conninfo))
444  conninfo = NULL;
445  load_path = NULL;
446  sp = NULL;
447  if ( (NULL != load_path_suffix) &&
448  (GNUNET_OK ==
450  section,
451  "SQL_DIR",
452  &sp)) )
453  GNUNET_asprintf (&load_path,
454  "%s%s",
455  sp,
456  load_path_suffix);
457  db = GNUNET_PQ_connect (conninfo == NULL ? "" : conninfo,
458  load_path,
459  es,
460  ps);
461  GNUNET_free (load_path);
462  GNUNET_free (sp);
463  GNUNET_free (conninfo);
464  return db;
465 }
466 
467 
468 void
470 {
471  GNUNET_assert (0 ==
474  GNUNET_assert (0 ==
475  pthread_mutex_destroy (&db->notify_lock));
476  GNUNET_free (db->es);
477  GNUNET_free (db->ps);
478  GNUNET_free (db->load_path);
479  GNUNET_free (db->config_str);
480  PQfinish (db->conn);
481  GNUNET_free (db);
482 }
483 
484 
485 /* end of pq/pq_connect.c */
GNUNET_PQ_SocketCallback sc
Function to call on Postgres FDs.
Definition: pq.h:64
void GNUNET_PQ_event_reconnect_(struct GNUNET_PQ_Context *db)
Internal API.
Definition: pq_event.c:392
void GNUNET_PQ_disconnect(struct GNUNET_PQ_Context *db)
Disconnect from the database, destroying the prepared statements and releasing other associated resou...
Definition: pq_connect.c:469
struct GNUNET_OS_Process * GNUNET_OS_start_process(enum GNUNET_OS_InheritStdioFlags std_inheritance, struct GNUNET_DISK_PipeHandle *pipe_stdin, struct GNUNET_DISK_PipeHandle *pipe_stdout, struct GNUNET_DISK_PipeHandle *pipe_stderr, const char *filename,...)
Start a process.
Definition: os_priority.c:678
void GNUNET_PQ_reconnect(struct GNUNET_PQ_Context *db)
Reinitialize the database db.
Definition: pq_connect.c:295
static const struct GNUNET_CONFIGURATION_Handle * cfg
Configuration we are using.
Definition: gnunet-abd.c:36
struct GNUNET_PQ_PreparedStatement * ps
Prepared statements.
Definition: pq.h:49
unsigned int GNUNET_CONTAINER_multishortmap_size(const struct GNUNET_CONTAINER_MultiShortmap *map)
Get the number of key-value pairs in the map.
void GNUNET_OS_process_destroy(struct GNUNET_OS_Process *proc)
Cleans up process structure contents (OS-dependent) and deallocates it.
Definition: os_priority.c:287
struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_string(const char *ptr)
Generate query parameter for a string.
char * load_path
Path to load SQL files from.
Definition: pq.h:59
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
Information needed to run a list of SQL statements using GNUNET_PQ_exec_statements().
pthread_mutex_t notify_lock
Lock to access channel_map.
Definition: pq.h:79
Information needed to prepare a list of SQL statements using GNUNET_PQ_prepare_statements().
struct GNUNET_PQ_ExecuteStatement * es
Statements to execute upon connection.
Definition: pq.h:44
Description of a DB result cell.
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
GNUNET_GenericReturnValue
Named constants for return values.
Definition: gnunet_common.h:83
int GNUNET_PQ_prepare_statements(struct GNUNET_PQ_Context *db, const struct GNUNET_PQ_PreparedStatement *ps)
Request creation of prepared statements ps from Postgres.
Definition: pq_prepare.c:62
#define GNUNET_new(type)
Allocate a struct or union of the given type.
enum GNUNET_GenericReturnValue GNUNET_CONFIGURATION_get_value_filename(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option, char **value)
Get a configuration value that should be the name of a file or directory.
enum GNUNET_GenericReturnValue GNUNET_CONFIGURATION_get_value_string(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option, char **value)
Get a configuration value that should be a string.
A hard error occurred, retrying will not help.
Definition: gnunet_db_lib.h:39
#define GNUNET_strdup(a)
Wrapper around GNUNET_xstrdup_.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur...
static char * section
Name of the section.
Definition: gnunet-config.c:33
int GNUNET_snprintf(char *buf, size_t size, const char *format,...) __attribute__((format(printf
Like snprintf, just aborts if the buffer is of insufficient size.
PGconn * conn
Actual connection.
Definition: pq.h:39
struct GNUNET_PQ_Context * GNUNET_PQ_connect(const char *config_str, const char *load_path, const struct GNUNET_PQ_ExecuteStatement *es, const struct GNUNET_PQ_PreparedStatement *ps)
Create a connection to the Postgres database using config_str for the configuration.
Definition: pq_connect.c:68
shared internal data structures of libgnunetpq
struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_string(const char *name, char **dst)
0-terminated string expected.
#define GNUNET_PQ_result_spec_end
End of result parameter specification.
#define GNUNET_PQ_query_param_end
End of query parameter specification.
Definition: gnunet_pq_lib.h:97
enum GNUNET_DB_QueryStatus GNUNET_PQ_eval_prepared_singleton_select(struct GNUNET_PQ_Context *db, const char *statement_name, const struct GNUNET_PQ_QueryParam *params, struct GNUNET_PQ_ResultSpec *rs)
Execute a named prepared statement that is a SELECT statement which must return a single result in co...
Definition: pq_eval.c:258
int GNUNET_PQ_exec_statements(struct GNUNET_PQ_Context *db, const struct GNUNET_PQ_ExecuteStatement *es)
Request execution of an array of statements es from Postgres.
Definition: pq_exec.c:76
char * config_str
Configuration to use to connect to the DB.
Definition: pq.h:54
#define GNUNET_log_strerror_file(level, cmd, filename)
Log an error message at log-level &#39;level&#39; that indicates a failure of the command &#39;cmd&#39; with the mess...
No standard streams should be inherited.
Definition: gnunet_os_lib.h:73
GNUNET_OS_ProcessStatusType
Process status types.
int GNUNET_OS_process_wait_status(struct GNUNET_OS_Process *proc, enum GNUNET_OS_ProcessStatusType *type, unsigned long *code)
Retrieve the status of a process, waiting on it if dead.
Definition: os_priority.c:964
static char buf[2048]
#define GNUNET_new_array(n, type)
Allocate a size n array with structs or unions of the given type.
The process exited with a return code.
struct GNUNET_CONTAINER_MultiShortmap * GNUNET_CONTAINER_multishortmap_create(unsigned int len, int do_not_copy_keys)
Create a multi peer map (hash map for public keys of peers).
void * sc_cls
Closure for sc.
Definition: pq.h:69
static int res
int int GNUNET_asprintf(char **buf, const char *format,...) __attribute__((format(printf
Like asprintf, just portable.
static enum GNUNET_GenericReturnValue apply_patch(struct GNUNET_PQ_Context *db, const char *load_path, unsigned int i)
Apply patch number from path load_path.
Definition: pq_connect.c:130
struct GNUNET_PQ_Context * GNUNET_PQ_connect_with_cfg(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *load_path_suffix, const struct GNUNET_PQ_ExecuteStatement *es, const struct GNUNET_PQ_PreparedStatement *ps)
Connect to a postgres database using the configuration option "CONFIG" in section.
Definition: pq_connect.c:428
void GNUNET_CONTAINER_multishortmap_destroy(struct GNUNET_CONTAINER_MultiShortmap *map)
Destroy a hash map.
The transaction succeeded, and yielded one result.
Definition: gnunet_db_lib.h:58
static struct GNUNET_FS_DirectoryBuilder * db
Definition: gnunet-search.c:41
void GNUNET_PQ_reconnect_if_down(struct GNUNET_PQ_Context *db)
Reinitialize the database db if the connection is down.
Definition: pq_connect.c:283
configuration data
enum GNUNET_GenericReturnValue GNUNET_PQ_run_sql(struct GNUNET_PQ_Context *db, const char *load_path)
Within the db context, run all the SQL files from the load_path from 0000-9999.sql (as long as the fi...
Definition: pq_connect.c:188
const char * name
Description of a DB query parameter.
Definition: gnunet_pq_lib.h:64
#define GNUNET_log(kind,...)
enum GNUNET_TESTBED_UnderlayLinkModelType type
the type of this model
static void pq_notice_receiver_cb(void *arg, const PGresult *res)
Function called by libpq whenever it wants to log something.
Definition: pq_connect.c:39
static void pq_notice_processor_cb(void *arg, const char *message)
Function called by libpq whenever it wants to log something.
Definition: pq_connect.c:56
GNUNET_DB_QueryStatus
Status code returned from functions running database commands.
Definition: gnunet_db_lib.h:34
#define GNUNET_log_from(kind, comp,...)
struct GNUNET_CONTAINER_MultiShortmap * channel_map
Map managing event subscriptions.
Definition: pq.h:74
Handle to Postgres database.
Definition: pq.h:34
enum GNUNET_GenericReturnValue GNUNET_DISK_file_test(const char *fil)
Check that fil corresponds to a filename (of a file that exists and that is not a directory)...
Definition: disk.c:482
#define GNUNET_free(ptr)
Wrapper around free.
void GNUNET_PQ_cleanup_result(struct GNUNET_PQ_ResultSpec *rs)
Free all memory that was allocated in rs during GNUNET_PQ_extract_result().
Definition: pq.c:117