GNUnet 0.28.1-dev.7-1-g2ce91c818
 
Loading...
Searching...
No Matches
pq_event.c
Go to the documentation of this file.
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021, 2023 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
60
61
68static void
71{
72 struct GNUNET_HashCode h_channel;
73
75 ntohs (es->size),
76 &h_channel);
77 GNUNET_static_assert (sizeof (*sh) <= sizeof (h_channel));
78 memcpy (sh,
79 &h_channel,
80 sizeof (*sh));
81}
82
83
92static char *
94 char identifier[64])
95{
96 char *end;
97
99 sizeof (*sh),
100 identifier,
101 63);
102 GNUNET_assert (NULL != end);
103 *end = '\0';
104 return end;
105}
106
107
116channel_to_sh (const char *identifier,
117 struct GNUNET_ShortHashCode *sh)
118{
119 return GNUNET_STRINGS_string_to_data (identifier,
120 strlen (identifier),
121 sh,
122 sizeof (*sh));
123}
124
125
134static char *
136 char identifier[64])
137{
139
140 es_to_sh (es,
141 &sh);
142 return sh_to_channel (&sh,
143 identifier);
144}
145
146
151{
155 void *extra;
156
161};
162
163
174do_notify (void *cls,
175 const struct GNUNET_ShortHashCode *sh,
176 void *value)
177{
178 struct NotifyContext *ctx = cls;
179 struct GNUNET_DB_EventHandler *eh = value;
180
181 eh->cb (eh->cb_cls,
182 ctx->extra,
183 ctx->extra_size);
184 return GNUNET_OK;
185}
186
187
188void
190{
191 PGnotify *n;
192 unsigned int cnt = 0;
193
195 "PG poll job active\n");
196 if (1 !=
197 PQconsumeInput (db->conn))
198 {
200 "Failed to read from Postgres: %s\n",
201 PQerrorMessage (db->conn));
202 if (CONNECTION_BAD != PQstatus (db->conn))
203 return;
205 return;
206 }
207 while (NULL != (n = PQnotifies (db->conn)))
208 {
210 struct NotifyContext ctx = {
211 .extra = NULL
212 };
213
214 cnt++;
215 if ('X' != toupper ((int) n->relname[0]))
216 {
218 "Ignoring notification for unsupported channel identifier `%s'\n",
219 n->relname);
220 PQfreemem (n);
221 continue;
222 }
223 if (GNUNET_OK !=
224 channel_to_sh (&n->relname[1],
225 &sh))
226 {
228 "Ignoring notification for unsupported channel identifier `%s'\n",
229 n->relname);
230 PQfreemem (n);
231 continue;
232 }
233 if ( (NULL != n->extra) &&
234 (GNUNET_OK !=
236 strlen (n->extra),
237 &ctx.extra,
238 &ctx.extra_size)))
239 {
241 "Ignoring notification for unsupported extra data `%s' on channel `%s'\n",
242 n->extra,
243 n->relname);
244 PQfreemem (n);
245 continue;
246 }
248 "Received notification %s with extra data `%.*s'\n",
249 n->relname,
250 (int) ctx.extra_size,
251 (const char *) ctx.extra);
253 &sh,
254 &do_notify,
255 &ctx);
256 GNUNET_free (ctx.extra);
257 PQfreemem (n);
258 }
260 "PG poll job finishes after %u events\n",
261 cnt);
262}
263
264
271static void
273{
274 struct GNUNET_PQ_Context *db = cls;
275
276 db->event_task = NULL;
277 if (NULL == db->rfd)
280 if (NULL != db->event_task)
281 {
282 /* GNUNET_PQ_reconnect_() above could have actually
283 created another event_task, stop it */
284 GNUNET_SCHEDULER_cancel (db->event_task);
285 db->event_task = NULL;
286 }
288 "Resubscribing\n");
289 if (NULL == db->rfd)
290 {
291 db->resubscribe_backoff
292 = GNUNET_TIME_relative_max (db->resubscribe_backoff,
294 db->resubscribe_backoff
295 = GNUNET_TIME_STD_BACKOFF (db->resubscribe_backoff);
298 db);
299 return;
300 }
301 db->resubscribe_backoff = GNUNET_TIME_UNIT_SECONDS;
302 db->event_task
304 db->rfd,
306 db);
307}
308
309
317static void
318do_poll (void *cls)
319{
320 struct GNUNET_PQ_Context *db = cls;
321
322 db->poller_task = NULL;
324}
325
326
327void
329{
330 if ( (NULL == db->conn) ||
331 (0 == GNUNET_CONTAINER_multishortmap_size (db->channel_map)) ||
332 (NULL != db->poller_task) )
333 return;
334 db->poller_task = GNUNET_SCHEDULER_add_now (&do_poll,
335 db);
336}
337
338
346static void
348 int fd)
349{
350 struct GNUNET_PQ_Context *db = cls;
351
353 "New poll FD is %d\n",
354 fd);
355 if (NULL != db->event_task)
356 {
357 GNUNET_SCHEDULER_cancel (db->event_task);
358 db->event_task = NULL;
359 }
360 GNUNET_free (db->rfd);
361 if (-1 == fd)
362 return;
363 if (0 == GNUNET_CONTAINER_multishortmap_size (db->channel_map))
364 return;
366 "Activating poll job on %d\n",
367 fd);
369 db->event_task
371 db->rfd,
373 db);
374}
375
376
384static void
386 const char *cmd,
387 struct GNUNET_DB_EventHandler *eh)
388{
389 char sql[16 + 64];
390 char *end;
391 PGresult *result;
392
393 if (NULL == db->conn)
394 return;
395 end = stpcpy (sql,
396 cmd);
397 end = sh_to_channel (&eh->sh,
398 end);
400 "Executing PQ command `%s'\n",
401 sql);
402 result = PQexec (db->conn,
403 sql);
404 if (PGRES_COMMAND_OK != PQresultStatus (result))
405 {
407 "pq",
408 "Failed to execute `%s': %s/%s/%s/%s/%s",
409 sql,
410 PQresultErrorField (result,
411 PG_DIAG_MESSAGE_PRIMARY),
412 PQresultErrorField (result,
413 PG_DIAG_MESSAGE_DETAIL),
414 PQresultErrorMessage (result),
415 PQresStatus (PQresultStatus (result)),
416 PQerrorMessage (db->conn));
417 }
418 PQclear (result);
419}
420
421
432 const struct GNUNET_ShortHashCode *sh,
433 void *value)
434{
435 struct GNUNET_PQ_Context *db = cls;
436 struct GNUNET_DB_EventHandler *eh = value;
437
439 "LISTEN X",
440 eh);
441 return GNUNET_OK;
442}
443
444
445void
447 int fd)
448{
450 "Change in PQ event FD to %d\n",
451 fd);
453 fd);
456 db);
457}
458
459
466static void
467event_timeout (void *cls)
468{
469 struct GNUNET_DB_EventHandler *eh = cls;
470
471 eh->timeout_task = NULL;
472 eh->cb (eh->cb_cls,
473 NULL,
474 0);
475}
476
477
480 const struct GNUNET_DB_EventHeaderP *es,
483 void *cb_cls)
484{
485 struct GNUNET_DB_EventHandler *eh;
486 bool sub;
487
489 eh->db = db;
490 es_to_sh (es,
491 &eh->sh);
492 eh->cb = cb;
493 eh->cb_cls = cb_cls;
494 sub = (NULL ==
496 &eh->sh));
499 &eh->sh,
500 eh,
502 if (NULL == db->event_task)
503 {
505 "Starting event scheduler\n");
507 PQsocket (db->conn));
508 }
509 if (sub)
511 "LISTEN X",
512 eh);
515 eh);
516 return eh;
517}
518
519
520void
522{
523 struct GNUNET_PQ_Context *db = eh->db;
524
527 &eh->sh,
528 eh));
529 if (NULL ==
531 &eh->sh))
533 "UNLISTEN X",
534 eh);
535 if (0 == GNUNET_CONTAINER_multishortmap_size (db->channel_map))
536 {
538 "Stopping PQ event scheduler job\n");
539 GNUNET_free (db->rfd);
540 if (NULL != db->event_task)
541 {
542 GNUNET_SCHEDULER_cancel (db->event_task);
543 db->event_task = NULL;
544 }
545 }
546 if (NULL != eh->timeout_task)
547 {
549 eh->timeout_task = NULL;
550 }
551 GNUNET_free (eh);
552}
553
554
555char *
557{
558 char sql[16 + 64 + 8];
559 char *end;
560
561 end = stpcpy (sql,
562 "X");
563 end = es_to_channel (es,
564 end);
565 GNUNET_assert (NULL != end);
566 return GNUNET_strdup (sql);
567}
568
569
570void
572 const struct GNUNET_DB_EventHeaderP *es,
573 const void *extra,
574 size_t extra_size)
575{
576 char sql[16 + 64 + extra_size * 8 / 5 + 8];
577 char *end;
578 PGresult *result;
579
580 end = stpcpy (sql,
581 "NOTIFY X");
582 end = es_to_channel (es,
583 end);
584 end = stpcpy (end,
585 ", '");
587 extra_size,
588 end,
589 sizeof (sql) - (end - sql) - 1);
590 GNUNET_assert (NULL != end);
591 *end = '\0';
592 end = stpcpy (end,
593 "'");
595 "Executing command `%s'\n",
596 sql);
597 result = PQexec (db->conn,
598 sql);
599 if (PGRES_COMMAND_OK != PQresultStatus (result))
600 {
602 "pq",
603 "Failed to execute `%s': %s/%s/%s/%s/%s",
604 sql,
605 PQresultErrorField (result,
606 PG_DIAG_MESSAGE_PRIMARY),
607 PQresultErrorField (result,
608 PG_DIAG_MESSAGE_DETAIL),
609 PQresultErrorMessage (result),
610 PQresStatus (PQresultStatus (result)),
611 PQerrorMessage (db->conn));
612 }
613 PQclear (result);
614 /* Make sure we do not miss this notification in case it was
615 for *us*, we need to trigger polling here.
616 Just waiting for the db socket to be readable won't work,
617 as postgres only queues notifications we triggered for
618 ourselves in an internal data structure. */
620}
621
622
623/* end of pq_event.c */
static struct GNUNET_TIME_Relative timeout
User defined timestamp for completing operations.
Definition gnunet-arm.c:118
static int end
Set if we are to shutdown all services (including ARM).
Definition gnunet-arm.c:33
static struct GNUNET_FS_Handle * ctx
static struct GNUNET_IDENTITY_Handle * sh
Handle to IDENTITY service.
static char * value
Value of the record to add/remove.
static int result
Global testing status.
static struct GNUNET_FS_DirectoryBuilder * db
void(* GNUNET_DB_EventCallback)(void *cls, const void *extra, size_t extra_size)
Function called on events received from Postgres.
void GNUNET_CRYPTO_hash(const void *block, size_t size, struct GNUNET_HashCode *ret)
Compute hash of a given block.
Definition crypto_hash.c:40
enum GNUNET_GenericReturnValue GNUNET_CONTAINER_multishortmap_put(struct GNUNET_CONTAINER_MultiShortmap *map, const struct GNUNET_ShortHashCode *key, void *value, enum GNUNET_CONTAINER_MultiHashMapOption opt)
Store a key-value pair in the map.
void * GNUNET_CONTAINER_multishortmap_get(const struct GNUNET_CONTAINER_MultiShortmap *map, const struct GNUNET_ShortHashCode *key)
Given a key find a value in the map matching the key.
int GNUNET_CONTAINER_multishortmap_iterate(struct GNUNET_CONTAINER_MultiShortmap *map, GNUNET_CONTAINER_ShortmapIterator it, void *it_cls)
Iterate over all entries in the map.
int GNUNET_CONTAINER_multishortmap_get_multiple(struct GNUNET_CONTAINER_MultiShortmap *map, const struct GNUNET_ShortHashCode *key, GNUNET_CONTAINER_ShortmapIterator it, void *it_cls)
Iterate over all entries in the map that match a particular key.
unsigned int GNUNET_CONTAINER_multishortmap_size(const struct GNUNET_CONTAINER_MultiShortmap *map)
Get the number of key-value pairs in the map.
int GNUNET_CONTAINER_multishortmap_remove(struct GNUNET_CONTAINER_MultiShortmap *map, const struct GNUNET_ShortHashCode *key, const void *value)
Remove the given key-value pair from the map.
@ GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE
Allow multiple values with the same key.
#define GNUNET_log(kind,...)
#define GNUNET_log_from(kind, comp,...)
GNUNET_GenericReturnValue
Named constants for return values.
#define GNUNET_static_assert(cond)
@ GNUNET_OK
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
@ GNUNET_ERROR_TYPE_WARNING
@ GNUNET_ERROR_TYPE_ERROR
@ GNUNET_ERROR_TYPE_DEBUG
@ GNUNET_ERROR_TYPE_INFO
#define GNUNET_strdup(a)
Wrapper around GNUNET_xstrdup_.
#define GNUNET_new(type)
Allocate a struct or union of the given type.
#define GNUNET_free(ptr)
Wrapper around free.
struct GNUNET_NETWORK_Handle * GNUNET_NETWORK_socket_box_native(int fd)
Box a native socket (and check that it is a socket).
Definition network.c:579
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_read_net(struct GNUNET_TIME_Relative delay, struct GNUNET_NETWORK_Handle *rfd, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay or when the specified file descriptor is ready f...
Definition scheduler.c:1517
void * GNUNET_SCHEDULER_cancel(struct GNUNET_SCHEDULER_Task *task)
Cancel the task with the specified identifier.
Definition scheduler.c:986
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_now(GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run as soon as possible.
Definition scheduler.c:1310
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_delayed(struct GNUNET_TIME_Relative delay, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay.
Definition scheduler.c:1283
enum GNUNET_GenericReturnValue GNUNET_STRINGS_string_to_data_alloc(const char *enc, size_t enclen, void **out, size_t *out_size)
Convert CrockfordBase32 encoding back to data.
Definition strings.c:909
char * GNUNET_STRINGS_data_to_string(const void *data, size_t size, char *out, size_t out_size)
Convert binary data to ASCII encoding using CrockfordBase32.
Definition strings.c:763
enum GNUNET_GenericReturnValue GNUNET_STRINGS_string_to_data(const char *enc, size_t enclen, void *out, size_t out_size)
Convert CrockfordBase32 encoding back to data.
Definition strings.c:843
#define GNUNET_TIME_UNIT_FOREVER_REL
Constant used to specify "forever".
struct GNUNET_TIME_Relative GNUNET_TIME_relative_max(struct GNUNET_TIME_Relative t1, struct GNUNET_TIME_Relative t2)
Return the maximum of two relative time values.
Definition time.c:352
#define GNUNET_TIME_UNIT_SECONDS
One second.
#define GNUNET_TIME_UNIT_ZERO
Relative time zero.
#define GNUNET_TIME_STD_BACKOFF(r)
Perform our standard exponential back-off calculation, starting at 1 ms and then going by a factor of...
shared internal data structures of libgnunetpq
void GNUNET_PQ_reconnect_(struct GNUNET_PQ_Context *db)
Reinitialize the database db.
Definition pq_connect.c:662
static void do_poll(void *cls)
The GNUnet scheduler notifies us that we need to trigger the DB event poller directly after having po...
Definition pq_event.c:318
void GNUNET_PQ_event_do_poll(struct GNUNET_PQ_Context *db)
Poll for events right now.
Definition pq_event.c:189
void GNUNET_PQ_event_poll_(struct GNUNET_PQ_Context *db)
Schedule polling for notifications that libpq may have queued while executing a synchronous query.
Definition pq_event.c:328
static enum GNUNET_GenericReturnValue channel_to_sh(const char *identifier, struct GNUNET_ShortHashCode *sh)
Convert sh to a Postgres identifier.
Definition pq_event.c:116
struct GNUNET_DB_EventHandler * GNUNET_PQ_event_listen(struct GNUNET_PQ_Context *db, const struct GNUNET_DB_EventHeaderP *es, struct GNUNET_TIME_Relative timeout, GNUNET_DB_EventCallback cb, void *cb_cls)
Register callback to be invoked on events of type es.
Definition pq_event.c:479
static void es_to_sh(const struct GNUNET_DB_EventHeaderP *es, struct GNUNET_ShortHashCode *sh)
Convert es to a short hash.
Definition pq_event.c:69
void GNUNET_PQ_event_notify(struct GNUNET_PQ_Context *db, const struct GNUNET_DB_EventHeaderP *es, const void *extra, size_t extra_size)
Notify all that listen on es of an event.
Definition pq_event.c:571
char * GNUNET_PQ_get_event_notify_channel(const struct GNUNET_DB_EventHeaderP *es)
Compute the channel that one should notify upon for the given event specification.
Definition pq_event.c:556
static void event_timeout(void *cls)
Function run on timeout for an event.
Definition pq_event.c:467
static enum GNUNET_GenericReturnValue register_notify(void *cls, const struct GNUNET_ShortHashCode *sh, void *value)
Re-subscribe to notifications after disconnect.
Definition pq_event.c:431
static char * sh_to_channel(struct GNUNET_ShortHashCode *sh, char identifier[64])
Convert sh to a Postgres identifier.
Definition pq_event.c:93
void GNUNET_PQ_event_reconnect_(struct GNUNET_PQ_Context *db, int fd)
Internal API.
Definition pq_event.c:446
static void manage_subscribe(struct GNUNET_PQ_Context *db, const char *cmd, struct GNUNET_DB_EventHandler *eh)
Helper function to trigger an SQL cmd on db.
Definition pq_event.c:385
static enum GNUNET_GenericReturnValue do_notify(void *cls, const struct GNUNET_ShortHashCode *sh, void *value)
Function called on every event handler that needs to be triggered.
Definition pq_event.c:174
static void scheduler_fd_cb(void *cls, int fd)
Function called when the Postgres FD changes and we need to update the scheduler event loop task.
Definition pq_event.c:347
static void do_scheduler_notify(void *cls)
The GNUnet scheduler notifies us that we need to trigger the DB event poller.
Definition pq_event.c:272
void GNUNET_PQ_event_listen_cancel(struct GNUNET_DB_EventHandler *eh)
Stop notifications.
Definition pq_event.c:521
static char * es_to_channel(const struct GNUNET_DB_EventHeaderP *es, char identifier[64])
Convert es to a Postgres identifier.
Definition pq_event.c:135
Handle for an active LISTENer to the database.
Definition pq_event.c:34
void * cb_cls
Closure for cb.
Definition pq_event.c:48
struct GNUNET_SCHEDULER_Task * timeout_task
Task to run on timeout.
Definition pq_event.c:58
struct GNUNET_ShortHashCode sh
Channel name.
Definition pq_event.c:38
struct GNUNET_PQ_Context * db
Database context this event handler is with.
Definition pq_event.c:53
GNUNET_DB_EventCallback cb
Function to call on events.
Definition pq_event.c:43
Header of a structure that describes an event channel we may subscribe to or notify on.
uint16_t size
The length of the struct (in bytes, including the length field itself), in big-endian format.
A 512-bit hashcode.
Handle to Postgres database.
Definition pq.h:36
Entry in list of pending tasks.
Definition scheduler.c:141
A 256-bit hashcode.
Time for relative time used by GNUnet, in microseconds.
Closure for do_notify().
Definition pq_event.c:151
void * extra
Extra argument of the notification, or NULL.
Definition pq_event.c:155
size_t extra_size
Number of bytes in extra.
Definition pq_event.c:160