GNUnet  0.19.2
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->load_path)
356  {
357  PGresult *res;
358  ExecStatusType est;
359 
360  res = PQexec (db->conn,
361  "SELECT"
362  " schema_name"
363  " FROM information_schema.schemata"
364  " WHERE schema_name='_v';");
365  est = PQresultStatus (res);
366  if ( (PGRES_COMMAND_OK != est) &&
367  (PGRES_TUPLES_OK != est) )
368  {
370  "Failed to run statement to check versioning schema. Bad!\n");
371  PQclear (res);
372  PQfinish (db->conn);
373  db->conn = NULL;
374  return;
375  }
376  if (0 == PQntuples (res))
377  {
379 
380  PQclear (res);
381  if (0 != (db->flags & GNUNET_PQ_FLAG_DROP))
382  {
384  "Versioning schema does not exist yet. Not attempting drop!\n");
385  PQfinish (db->conn);
386  db->conn = NULL;
387  return;
388  }
390  "versioning");
391  if (GNUNET_NO == ret)
392  {
394  "Failed to find SQL file to load database versioning logic\n");
395  PQfinish (db->conn);
396  db->conn = NULL;
397  return;
398  }
399  if (GNUNET_SYSERR == ret)
400  {
402  "Failed to run SQL logic to setup database versioning logic\n");
403  PQfinish (db->conn);
404  db->conn = NULL;
405  return;
406  }
407  }
408  else
409  {
410  PQclear (res);
411  }
412  }
413 
414  if (NULL != db->auto_suffix)
415  {
416  PGresult *res;
417 
418  GNUNET_assert (NULL != db->load_path);
419  res = PQprepare (db->conn,
420  "gnunet_pq_check_patch",
421  "SELECT"
422  " applied_by"
423  " FROM _v.patches"
424  " WHERE patch_name = $1"
425  " LIMIT 1",
426  1,
427  NULL);
428  if (PGRES_COMMAND_OK != PQresultStatus (res))
429  {
431  "Failed to run SQL logic to setup database versioning logic: %s/%s\n",
432  PQresultErrorMessage (res),
433  PQerrorMessage (db->conn));
434  PQclear (res);
435  PQfinish (db->conn);
436  db->conn = NULL;
437  return;
438  }
439  PQclear (res);
440 
441  if (GNUNET_SYSERR ==
443  db->auto_suffix))
444  {
446  "Failed to load SQL statements from `%s*'\n",
447  db->auto_suffix);
448  PQfinish (db->conn);
449  db->conn = NULL;
450  return;
451  }
452  }
453  if ( (NULL != db->es) &&
454  (GNUNET_OK !=
456  db->es)) )
457  {
458  PQfinish (db->conn);
459  db->conn = NULL;
460  return;
461  }
462  if ( (NULL != db->ps) &&
463  (GNUNET_OK !=
465  db->ps)) )
466  {
467  PQfinish (db->conn);
468  db->conn = NULL;
469  return;
470  }
472  PQsocket (db->conn));
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)
482 {
484  section,
485  load_path_suffix,
486  es,
487  ps,
489 }
490 
491 
492 struct GNUNET_PQ_Context *
494  const char *section,
495  const char *load_path_suffix,
496  const struct GNUNET_PQ_ExecuteStatement *es,
497  const struct GNUNET_PQ_PreparedStatement *ps,
499 {
500  struct GNUNET_PQ_Context *db;
501  char *conninfo;
502  char *load_path;
503 
504  if (GNUNET_OK !=
506  section,
507  "CONFIG",
508  &conninfo))
509  conninfo = NULL;
510  load_path = NULL;
511  if (GNUNET_OK !=
513  section,
514  "SQL_DIR",
515  &load_path))
516  {
518  section,
519  "SQL_DIR");
520  }
521  if ( (NULL != load_path_suffix) &&
522  (NULL == load_path) )
523  {
525  section,
526  "SQL_DIR");
527  return NULL;
528  }
529  db = GNUNET_PQ_connect2 (conninfo == NULL ? "" : conninfo,
530  load_path,
531  load_path_suffix,
532  es,
533  ps,
534  flags);
536  GNUNET_free (conninfo);
537  return db;
538 }
539 
540 
541 void
543 {
544  if (NULL == db)
545  return;
546  GNUNET_assert (0 ==
547  GNUNET_CONTAINER_multishortmap_size (db->channel_map));
549  GNUNET_free (db->es);
550  GNUNET_free (db->ps);
551  GNUNET_free (db->load_path);
552  GNUNET_free (db->auto_suffix);
553  GNUNET_free (db->config_str);
554  PQfinish (db->conn);
555  GNUNET_free (db);
556 }
557 
558 
559 /* 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:97
static char buf[2048]
GNUNET_DB_QueryStatus
Status code returned from functions running database commands.
Definition: gnunet_db_lib.h:37
@ GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
The transaction succeeded, and yielded one result.
Definition: gnunet_db_lib.h:60
@ GNUNET_DB_STATUS_HARD_ERROR
A hard error occurred, retrying will not help.
Definition: gnunet_db_lib.h:41
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:67
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:88
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:98
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:481
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.
@ GNUNET_OK
@ GNUNET_YES
@ GNUNET_NO
@ GNUNET_SYSERR
#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:633
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:923
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:273
int GNUNET_OS_process_kill(struct GNUNET_OS_Process *proc, int sig)
Sends a signal to the process.
Definition: os_priority.c:210
@ 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:412
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:542
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:477
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:493
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:100
char * load_path
Path to load SQL files from.
Definition: pq.h:70
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:65
char * auto_suffix
Suffix to append to path to load on startup.
Definition: pq.h:75
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:66
Description of a DB result cell.
enum GNUNET_TESTBED_UnderlayLinkModelType type
the type of this model