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