GNUnet  0.17.5
pq_connect.c
Go to the documentation of this file.
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  */
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 {
74  load_path,
75  NULL == load_path
76  ? NULL
77  : "",
78  es,
79  ps,
81 }
82 
83 
84 struct GNUNET_PQ_Context *
86  const char *load_path,
87  const char *auto_suffix,
88  const struct GNUNET_PQ_ExecuteStatement *es,
89  const struct GNUNET_PQ_PreparedStatement *ps,
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,
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,
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);
129  if (NULL == db->conn)
130  {
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 
144  const char *buf)
145 {
146  struct GNUNET_OS_Process *psql;
148  unsigned long code;
150  char *fn;
151 
152  GNUNET_asprintf (&fn,
153  "%s%s.sql",
154  db->load_path,
155  buf);
156  if (GNUNET_YES !=
158  {
160  "SQL resource `%s' does not exist\n",
161  fn);
162  GNUNET_free (fn);
163  return GNUNET_NO;
164  }
166  "Applying SQL file `%s' on database %s\n",
167  fn,
168  db->config_str);
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  {
185  "exec",
186  "psql");
187  GNUNET_free (fn);
188  return GNUNET_SYSERR;
189  }
191  &type,
192  &code);
193  if (GNUNET_OK != ret)
194  {
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);
203  GNUNET_free (fn);
204  return GNUNET_SYSERR;
205  }
207  if ( (GNUNET_OS_PROCESS_EXITED != type) ||
208  (0 != code) )
209  {
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 
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 '/' */
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),
254  };
255  struct GNUNET_PQ_ResultSpec rs[] = {
256  GNUNET_PQ_result_spec_string ("applied_by",
257  &applied_by),
259  };
260 
262  "gnunet_pq_check_patch",
263  params,
264  rs);
266  {
268  "Database version %s already applied by %s, skipping\n",
269  patch_name,
270  applied_by);
272  }
273  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
274  {
275  GNUNET_break (0);
276  return GNUNET_SYSERR;
277  }
278  }
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! */
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! */
294 
295  GNUNET_snprintf (patch_name,
296  sizeof (patch_name),
297  "%s%04u",
298  load_path,
299  i);
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 
312 void
314 {
315  if (1 ==
316  PQconsumeInput (db->conn))
317  return;
318  if (CONNECTION_BAD != PQstatus (db->conn))
319  return;
321 }
322 
323 
324 void
326 {
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  {
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,
351  db);
352  PQsetNoticeProcessor (db->conn,
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  {
371 
372  PQclear (res);
373  if (0 != (db->flags & GNUNET_PQ_FLAG_DROP))
374  {
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  }
382  "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet, loading versioning!\n");
384  "versioning");
385  if (GNUNET_NO == ret)
386  {
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  {
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  {
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 ==
427  db->auto_suffix))
428  {
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 !=
440  db->es)) )
441  {
442  PQfinish (db->conn);
443  db->conn = NULL;
444  return;
445  }
446  if ( (NULL != db->ps) &&
447  (GNUNET_OK !=
449  db->ps)) )
450  {
451  PQfinish (db->conn);
452  db->conn = NULL;
453  return;
454  }
456  PQsocket (db->conn));
457 }
458 
459 
460 struct GNUNET_PQ_Context *
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 {
468  section,
469  load_path_suffix,
470  es,
471  ps,
473 }
474 
475 
476 struct GNUNET_PQ_Context *
478  const char *section,
479  const char *load_path_suffix,
480  const struct GNUNET_PQ_ExecuteStatement *es,
481  const struct GNUNET_PQ_PreparedStatement *ps,
483 {
484  struct GNUNET_PQ_Context *db;
485  char *conninfo;
486  char *load_path;
487 
488  if (GNUNET_OK !=
490  section,
491  "CONFIG",
492  &conninfo))
493  conninfo = NULL;
494  load_path = NULL;
495  if (GNUNET_OK !=
497  section,
498  "SQL_DIR",
499  &load_path))
500  {
502  section,
503  "SQL_DIR");
504  }
505  if ( (NULL != load_path_suffix) &&
506  (NULL == load_path) )
507  {
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);
520  GNUNET_free (conninfo);
521  return db;
522 }
523 
524 
525 void
527 {
528  if (NULL == db)
529  return;
530  GNUNET_assert (0 ==
531  GNUNET_CONTAINER_multishortmap_size (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 */
static const struct GNUNET_CONFIGURATION_Handle * cfg
Configuration we are using.
Definition: gnunet-abd.c:36
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
static int res
static struct GNUNET_FS_DirectoryBuilder * db
Definition: gnunet-search.c:94
static char buf[2048]
GNUNET_DB_QueryStatus
Status code returned from functions running database commands.
Definition: gnunet_db_lib.h:36
@ GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
The transaction succeeded, and yielded one result.
Definition: gnunet_db_lib.h:59
@ GNUNET_DB_STATUS_HARD_ERROR
A hard error occurred, retrying will not help.
Definition: gnunet_db_lib.h:40
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
enum GNUNET_GenericReturnValue 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
enum GNUNET_GenericReturnValue 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
struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_string(const char *name, char **dst)
0-terminated string expected.
#define GNUNET_PQ_query_param_end
End of query parameter specification.
Definition: gnunet_pq_lib.h:97
GNUNET_PQ_Options
Flags to control PQ operation.
@ GNUNET_PQ_FLAG_DROP
Dropping database.
@ GNUNET_PQ_FLAG_CHECK_CURRENT
Check database version is current.
@ GNUNET_PQ_FLAG_NONE
Traditional default.
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:122
struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_string(const char *ptr)
Generate query parameter for a string.
#define GNUNET_PQ_result_spec_end
End of result parameter specification.
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.
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
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 GNUNET_CONTAINER_multishortmap_destroy(struct GNUNET_CONTAINER_MultiShortmap *map)
Destroy a hash map.
unsigned int GNUNET_CONTAINER_multishortmap_size(const struct GNUNET_CONTAINER_MultiShortmap *map)
Get the number of key-value pairs in the map.
#define GNUNET_log(kind,...)
#define GNUNET_log_from(kind, comp,...)
GNUNET_GenericReturnValue
Named constants for return values.
Definition: gnunet_common.h:96
@ GNUNET_OK
Definition: gnunet_common.h:99
@ GNUNET_YES
@ GNUNET_NO
Definition: gnunet_common.h:98
@ GNUNET_SYSERR
Definition: gnunet_common.h:97
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur.
void GNUNET_log_config_missing(enum GNUNET_ErrorType kind, const char *section, const char *option)
Log error message about missing configuration option.
#define GNUNET_log_strerror_file(level, cmd, filename)
Log an error message at log-level 'level' that indicates a failure of the command 'cmd' with the mess...
@ GNUNET_ERROR_TYPE_WARNING
@ GNUNET_ERROR_TYPE_ERROR
@ GNUNET_ERROR_TYPE_DEBUG
@ GNUNET_ERROR_TYPE_INFO
int int GNUNET_asprintf(char **buf, const char *format,...) __attribute__((format(printf
Like asprintf, just portable.
#define GNUNET_strdup(a)
Wrapper around GNUNET_xstrdup_.
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.
#define GNUNET_new(type)
Allocate a struct or union of the given type.
#define GNUNET_new_array(n, type)
Allocate a size n array with structs or unions of the given type.
#define GNUNET_free(ptr)
Wrapper around free.
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:672
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:962
GNUNET_OS_ProcessStatusType
Process status types.
void GNUNET_OS_process_destroy(struct GNUNET_OS_Process *proc)
Cleans up process structure contents (OS-dependent) and deallocates it.
Definition: os_priority.c:280
int GNUNET_OS_process_kill(struct GNUNET_OS_Process *proc, int sig)
Sends a signal to the process.
Definition: os_priority.c:217
@ GNUNET_OS_INHERIT_STD_ERR
When this flag is set, the child process will inherit stderr of the parent.
Definition: gnunet_os_lib.h:95
@ GNUNET_OS_PROCESS_EXITED
The process exited with a return code.
const char * name
shared internal data structures of libgnunetpq
void GNUNET_PQ_event_reconnect_(struct GNUNET_PQ_Context *db, int fd)
Internal API.
Definition: pq_event.c:406
void GNUNET_PQ_reconnect(struct GNUNET_PQ_Context *db)
Reinitialize the database db.
Definition: pq_connect.c:325
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:223
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
struct GNUNET_PQ_Context * GNUNET_PQ_connect2(const char *config_str, const char *load_path, const char *auto_suffix, const struct GNUNET_PQ_ExecuteStatement *es, const struct GNUNET_PQ_PreparedStatement *ps, enum GNUNET_PQ_Options flags)
Create a connection to the Postgres database using config_str for the configuration.
Definition: pq_connect.c:85
void GNUNET_PQ_reconnect_if_down(struct GNUNET_PQ_Context *db)
Reinitialize the database db if the connection is down.
Definition: pq_connect.c:313
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:526
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
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:461
struct GNUNET_PQ_Context * GNUNET_PQ_connect_with_cfg2(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, enum GNUNET_PQ_Options flags)
Connect to a postgres database using the configuration option "CONFIG" in section.
Definition: pq_connect.c:477
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
enum GNUNET_GenericReturnValue GNUNET_PQ_exec_sql(struct GNUNET_PQ_Context *db, const char *buf)
Execute SQL statements from buf against db.
Definition: pq_connect.c:143
Handle to Postgres database.
Definition: pq.h:36
struct GNUNET_PQ_ExecuteStatement * es
Statements to execute upon connection.
Definition: pq.h:45
enum GNUNET_PQ_Options flags
Flags controlling the connection.
Definition: pq.h:85
char * load_path
Path to load SQL files from.
Definition: pq.h:60
struct GNUNET_PQ_PreparedStatement * ps
Prepared statements.
Definition: pq.h:50
char * config_str
Configuration to use to connect to the DB.
Definition: pq.h:55
char * auto_suffix
Suffix to append to path to load on startup.
Definition: pq.h:65
Information needed to run a list of SQL statements using GNUNET_PQ_exec_statements().
Information needed to prepare a list of SQL statements using GNUNET_PQ_prepare_statements().
Description of a DB query parameter.
Definition: gnunet_pq_lib.h:65
Description of a DB result cell.
enum GNUNET_TESTBED_UnderlayLinkModelType type
the type of this model