first step to remove plibc
[oweals/gnunet.git] / src / datastore / gnunet-datastore.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010, 2013 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 /**
22  * @file datastore/gnunet-datastore.c
23  * @brief tool to manipulate datastores
24  * @author Christian Grothoff
25  */
26 #include <inttypes.h>
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_datastore_service.h"
30
31 GNUNET_NETWORK_STRUCT_BEGIN
32
33 struct DataRecord
34 {
35   /**
36    * Number of bytes in the item (NBO).
37    */
38   uint32_t size GNUNET_PACKED;
39
40   /**
41    * Type of the item (NBO) (actually an enum GNUNET_BLOCK_Type)
42    */
43   uint32_t type GNUNET_PACKED;
44
45   /**
46    * Priority of the item (NBO).
47    */
48   uint32_t priority GNUNET_PACKED;
49
50   /**
51    * Desired anonymity level (NBO).
52    */
53   uint32_t anonymity GNUNET_PACKED;
54
55   /**
56    * Desired replication level (NBO).
57    */
58   uint32_t replication GNUNET_PACKED;
59
60   /**
61    * Expiration time (NBO).
62    */
63   struct GNUNET_TIME_AbsoluteNBO expiration;
64
65   /**
66    * Key under which the item can be found.
67    */
68   struct GNUNET_HashCode key;
69 };
70 GNUNET_NETWORK_STRUCT_END
71
72
73 /**
74  * Length of our magic header.
75  */
76 static const size_t MAGIC_LEN = 16;
77
78 /**
79  * Magic header bytes.
80  */
81 static const uint8_t MAGIC_BYTES[16] = "GNUNETDATASTORE1";
82
83 /**
84  * Dump the database.
85  */
86 static int dump;
87
88 /**
89  * Insert into the database.
90  */
91 static int insert;
92
93 /**
94  * Dump file name.
95  */
96 static char *file_name;
97
98 /**
99  * Dump file handle.
100  */
101 static struct GNUNET_DISK_FileHandle *file_handle;
102
103 /**
104  * Global return value.
105  */
106 static int ret;
107
108 /**
109  * Handle for datastore.
110  */
111 static struct GNUNET_DATASTORE_Handle *datastore;
112
113 /**
114  * Current operation.
115  */
116 static struct GNUNET_DATASTORE_QueueEntry *qe;
117
118 /**
119  * Record count.
120  */
121 static uint64_t record_count;
122
123
124 static void
125 do_shutdown (void *cls)
126 {
127   if (NULL != qe)
128     GNUNET_DATASTORE_cancel (qe);
129   if (NULL != datastore)
130     GNUNET_DATASTORE_disconnect (datastore, GNUNET_NO);
131   if (NULL != file_handle)
132     GNUNET_DISK_file_close (file_handle);
133 }
134
135
136 /**
137  * Begin dumping the database.
138  */
139 static void
140 start_dump (void);
141
142
143 /**
144  * Begin inserting into the database.
145  */
146 static void
147 start_insert (void);
148
149
150 /**
151  * Perform next GET operation.
152  */
153 static void
154 do_get (const uint64_t next_uid);
155
156
157 /**
158  * Process a datum that was stored in the datastore.
159  *
160  * @param cls closure
161  * @param key key for the content
162  * @param size number of bytes in data
163  * @param data content stored
164  * @param type type of the content
165  * @param priority priority of the content
166  * @param anonymity anonymity-level for the content
167  * @param replication replication-level for the content
168  * @param expiration expiration time for the content
169  * @param uid unique identifier for the datum;
170  *        maybe 0 if no unique identifier is available
171  */
172 static void
173 get_cb (void *cls,
174         const struct GNUNET_HashCode *key,
175         size_t size,
176         const void *data,
177         enum GNUNET_BLOCK_Type type,
178         uint32_t priority,
179         uint32_t anonymity,
180         uint32_t replication,
181         struct GNUNET_TIME_Absolute expiration,
182         uint64_t uid)
183 {
184   qe = NULL;
185   if (NULL == key)
186   {
187     fprintf (stderr, _ ("Dumped %" PRIu64 " records\n"), record_count);
188     GNUNET_DISK_file_close (file_handle);
189     file_handle = NULL;
190     if (insert)
191       start_insert ();
192     else
193     {
194       ret = 0;
195       GNUNET_SCHEDULER_shutdown ();
196     }
197     return;
198   }
199
200   struct DataRecord dr;
201   dr.size = htonl ((uint32_t) size);
202   dr.type = htonl (type);
203   dr.priority = htonl (priority);
204   dr.anonymity = htonl (anonymity);
205   dr.replication = htonl (replication);
206   dr.expiration = GNUNET_TIME_absolute_hton (expiration);
207   dr.key = *key;
208
209   ssize_t len;
210   len = GNUNET_DISK_file_write (file_handle, &dr, sizeof (dr));
211   if (sizeof (dr) != len)
212   {
213     fprintf (stderr,
214              _ ("Short write to file: %zd bytes expecting %zd\n"),
215              len,
216              sizeof (dr));
217     ret = 1;
218     GNUNET_SCHEDULER_shutdown ();
219     return;
220   }
221
222   len = GNUNET_DISK_file_write (file_handle, data, size);
223   if (size != len)
224   {
225     fprintf (stderr,
226              _ ("Short write to file: %zd bytes expecting %zd\n"),
227              len,
228              size);
229     ret = 1;
230     GNUNET_SCHEDULER_shutdown ();
231     return;
232   }
233
234   record_count++;
235   do_get (uid + 1);
236 }
237
238
239 /**
240  * Perform next GET operation.
241  */
242 static void
243 do_get (const uint64_t next_uid)
244 {
245   GNUNET_assert (NULL == qe);
246   qe = GNUNET_DATASTORE_get_key (datastore,
247                                  next_uid,
248                                  false /* random */,
249                                  NULL /* key */,
250                                  GNUNET_BLOCK_TYPE_ANY,
251                                  0 /* queue_priority */,
252                                  1 /* max_queue_size */,
253                                  &get_cb,
254                                  NULL /* proc_cls */);
255   if (NULL == qe)
256   {
257     fprintf (stderr, _ ("Error queueing datastore GET operation\n"));
258     ret = 1;
259     GNUNET_SCHEDULER_shutdown ();
260   }
261 }
262
263
264 /**
265  * Begin dumping the database.
266  */
267 static void
268 start_dump ()
269 {
270   record_count = 0;
271
272   if (NULL != file_name)
273   {
274     file_handle = GNUNET_DISK_file_open (file_name,
275                                          GNUNET_DISK_OPEN_WRITE |
276                                            GNUNET_DISK_OPEN_TRUNCATE |
277                                            GNUNET_DISK_OPEN_CREATE,
278                                          GNUNET_DISK_PERM_USER_READ |
279                                            GNUNET_DISK_PERM_USER_WRITE);
280     if (NULL == file_handle)
281     {
282       fprintf (stderr, _ ("Unable to open dump file: %s\n"), file_name);
283       ret = 1;
284       GNUNET_SCHEDULER_shutdown ();
285       return;
286     }
287   }
288   else
289   {
290     file_handle = GNUNET_DISK_get_handle_from_int_fd (STDOUT_FILENO);
291   }
292   GNUNET_DISK_file_write (file_handle, MAGIC_BYTES, MAGIC_LEN);
293   do_get (0);
294 }
295
296
297 /**
298  * Continuation called to notify client about result of the
299  * operation.
300  *
301  * @param cls closure
302  * @param success GNUNET_SYSERR on failure (including timeout/queue drop)
303  *                GNUNET_NO if content was already there
304  *                GNUNET_YES (or other positive value) on success
305  * @param min_expiration minimum expiration time required for 0-priority content to be stored
306  *                by the datacache at this time, zero for unknown, forever if we have no
307  *                space for 0-priority content
308  * @param msg NULL on success, otherwise an error message
309  */
310 static void
311 put_cb (void *cls,
312         int32_t success,
313         struct GNUNET_TIME_Absolute min_expiration,
314         const char *msg)
315 {
316   qe = NULL;
317   if (GNUNET_SYSERR == success)
318   {
319     fprintf (stderr, _ ("Failed to store item: %s, aborting\n"), msg);
320     ret = 1;
321     GNUNET_SCHEDULER_shutdown ();
322     return;
323   }
324
325   struct DataRecord dr;
326   ssize_t len;
327
328   len = GNUNET_DISK_file_read (file_handle, &dr, sizeof (dr));
329   if (0 == len)
330   {
331     fprintf (stderr, _ ("Inserted %" PRIu64 " records\n"), record_count);
332     ret = 0;
333     GNUNET_SCHEDULER_shutdown ();
334     return;
335   }
336   else if (sizeof (dr) != len)
337   {
338     fprintf (stderr,
339              _ ("Short read from file: %zd bytes expecting %zd\n"),
340              len,
341              sizeof (dr));
342     ret = 1;
343     GNUNET_SCHEDULER_shutdown ();
344     return;
345   }
346
347   const size_t size = ntohl (dr.size);
348   uint8_t data[size];
349   len = GNUNET_DISK_file_read (file_handle, data, size);
350   if (size != len)
351   {
352     fprintf (stderr,
353              _ ("Short read from file: %zd bytes expecting %zd\n"),
354              len,
355              size);
356     ret = 1;
357     GNUNET_SCHEDULER_shutdown ();
358     return;
359   }
360
361   record_count++;
362   qe = GNUNET_DATASTORE_put (datastore,
363                              0,
364                              &dr.key,
365                              size,
366                              data,
367                              ntohl (dr.type),
368                              ntohl (dr.priority),
369                              ntohl (dr.anonymity),
370                              ntohl (dr.replication),
371                              GNUNET_TIME_absolute_ntoh (dr.expiration),
372                              0,
373                              1,
374                              &put_cb,
375                              NULL);
376   if (NULL == qe)
377   {
378     fprintf (stderr, _ ("Error queueing datastore PUT operation\n"));
379     ret = 1;
380     GNUNET_SCHEDULER_shutdown ();
381   }
382 }
383
384
385 /**
386  * Begin inserting into the database.
387  */
388 static void
389 start_insert ()
390 {
391   record_count = 0;
392
393   if (NULL != file_name)
394   {
395     file_handle = GNUNET_DISK_file_open (file_name,
396                                          GNUNET_DISK_OPEN_READ,
397                                          GNUNET_DISK_PERM_NONE);
398     if (NULL == file_handle)
399     {
400       fprintf (stderr, _ ("Unable to open dump file: %s\n"), file_name);
401       ret = 1;
402       GNUNET_SCHEDULER_shutdown ();
403       return;
404     }
405   }
406   else
407   {
408     file_handle = GNUNET_DISK_get_handle_from_int_fd (STDIN_FILENO);
409   }
410
411   uint8_t buf[MAGIC_LEN];
412   ssize_t len;
413
414   len = GNUNET_DISK_file_read (file_handle, buf, MAGIC_LEN);
415   if (len != MAGIC_LEN || 0 != memcmp (buf, MAGIC_BYTES, MAGIC_LEN))
416   {
417     fprintf (stderr, _ ("Input file is not of a supported format\n"));
418     return;
419   }
420   put_cb (NULL, GNUNET_YES, GNUNET_TIME_UNIT_ZERO_ABS, NULL);
421 }
422
423
424 /**
425  * Main function that will be run by the scheduler.
426  *
427  * @param cls closure
428  * @param args remaining command-line arguments
429  * @param cfgfile name of the configuration file used
430  * @param cfg configuration
431  */
432 static void
433 run (void *cls,
434      char *const *args,
435      const char *cfgfile,
436      const struct GNUNET_CONFIGURATION_Handle *cfg)
437 {
438   GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
439   datastore = GNUNET_DATASTORE_connect (cfg);
440   if (NULL == datastore)
441   {
442     fprintf (stderr, _ ("Failed connecting to the datastore.\n"));
443     ret = 1;
444     GNUNET_SCHEDULER_shutdown ();
445     return;
446   }
447   if (dump)
448     start_dump ();
449   else if (insert)
450     start_insert ();
451   else
452   {
453     fprintf (stderr,
454              _ ("Please choose at least one operation: %s, %s\n"),
455              "dump",
456              "insert");
457     ret = 1;
458     GNUNET_SCHEDULER_shutdown ();
459   }
460 }
461
462
463 /**
464  * The main function to manipulate datastores.
465  *
466  * @param argc number of arguments from the command line
467  * @param argv command line arguments
468  * @return 0 ok, 1 on error
469  */
470 int
471 main (int argc, char *const *argv)
472 {
473   struct GNUNET_GETOPT_CommandLineOption options[] =
474     {GNUNET_GETOPT_option_flag ('d',
475                                 "dump",
476                                 gettext_noop (
477                                   "Dump all records from the datastore"),
478                                 &dump),
479      GNUNET_GETOPT_option_flag ('i',
480                                 "insert",
481                                 gettext_noop (
482                                   "Insert records into the datastore"),
483                                 &insert),
484      GNUNET_GETOPT_option_filename ('f',
485                                     "file",
486                                     "FILENAME",
487                                     gettext_noop ("File to dump or insert"),
488                                     &file_name),
489      GNUNET_GETOPT_OPTION_END};
490   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
491     return 2;
492
493   if (GNUNET_OK !=
494       GNUNET_PROGRAM_run (argc,
495                           argv,
496                           "gnunet-datastore",
497                           gettext_noop ("Manipulate GNUnet datastore"),
498                           options,
499                           &run,
500                           NULL))
501     ret = 1;
502   GNUNET_free ((void *) argv);
503   return ret;
504 }
505
506 /* end of gnunet-datastore.c */