first step to remove plibc
[oweals/gnunet.git] / src / datastore / gnunet-datastore.c
index 9e0ee205eced2e5a6b690b9144e8ef3b38f41809..4cf318777bc7a8ceeb0efd7304c134582013b75a 100644 (file)
@@ -2,20 +2,20 @@
      This file is part of GNUnet.
      Copyright (C) 2010, 2013 GNUnet e.V.
 
-     GNUnet is free software; you can redistribute it and/or modify
-     it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 3, or (at your
-     option) any later version.
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     General Public License for more details.
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-     You should have received a copy of the GNU General Public License
-     along with GNUnet; see the file COPYING.  If not, write to the
-     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-     Boston, MA 02110-1301, USA.
+     SPDX-License-Identifier: AGPL3.0-or-later
 */
 
 /**
  * @brief tool to manipulate datastores
  * @author Christian Grothoff
  */
+#include <inttypes.h>
 #include "platform.h"
 #include "gnunet_util_lib.h"
 #include "gnunet_datastore_service.h"
 
+GNUNET_NETWORK_STRUCT_BEGIN
+
+struct DataRecord
+{
+  /**
+   * Number of bytes in the item (NBO).
+   */
+  uint32_t size GNUNET_PACKED;
+
+  /**
+   * Type of the item (NBO) (actually an enum GNUNET_BLOCK_Type)
+   */
+  uint32_t type GNUNET_PACKED;
+
+  /**
+   * Priority of the item (NBO).
+   */
+  uint32_t priority GNUNET_PACKED;
+
+  /**
+   * Desired anonymity level (NBO).
+   */
+  uint32_t anonymity GNUNET_PACKED;
+
+  /**
+   * Desired replication level (NBO).
+   */
+  uint32_t replication GNUNET_PACKED;
+
+  /**
+   * Expiration time (NBO).
+   */
+  struct GNUNET_TIME_AbsoluteNBO expiration;
+
+  /**
+   * Key under which the item can be found.
+   */
+  struct GNUNET_HashCode key;
+};
+GNUNET_NETWORK_STRUCT_END
+
 
 /**
- * Name of the second configuration file.
+ * Length of our magic header.
  */
-static char *alternative_cfg;
+static const size_t MAGIC_LEN = 16;
 
 /**
- * Global return value.
+ * Magic header bytes.
  */
-static int ret;
+static const uint8_t MAGIC_BYTES[16] = "GNUNETDATASTORE1";
 
 /**
- * Our offset on 'get'.
+ * Dump the database.
  */
-static uint64_t offset;
+static int dump;
 
 /**
- * First UID ever returned.
+ * Insert into the database.
  */
-static uint64_t first_uid;
+static int insert;
 
 /**
- * Configuration for the source database.
+ * Dump file name.
  */
-static struct GNUNET_CONFIGURATION_Handle *scfg;
+static char *file_name;
 
 /**
- * Handle for database source.
+ * Dump file handle.
+ */
+static struct GNUNET_DISK_FileHandle *file_handle;
+
+/**
+ * Global return value.
  */
-static struct GNUNET_DATASTORE_Handle *db_src;
+static int ret;
 
 /**
- * Handle for database destination.
+ * Handle for datastore.
  */
-static struct GNUNET_DATASTORE_Handle *db_dst;
+static struct GNUNET_DATASTORE_Handle *datastore;
 
 /**
  * Current operation.
  */
 static struct GNUNET_DATASTORE_QueueEntry *qe;
 
+/**
+ * Record count.
+ */
+static uint64_t record_count;
+
 
 static void
 do_shutdown (void *cls)
 {
   if (NULL != qe)
     GNUNET_DATASTORE_cancel (qe);
-  GNUNET_DATASTORE_disconnect (db_src, GNUNET_NO);
-  GNUNET_DATASTORE_disconnect (db_dst, GNUNET_NO);
-  GNUNET_CONFIGURATION_destroy (scfg);
+  if (NULL != datastore)
+    GNUNET_DATASTORE_disconnect (datastore, GNUNET_NO);
+  if (NULL != file_handle)
+    GNUNET_DISK_file_close (file_handle);
 }
 
 
 /**
- * Perform next GET operation.
+ * Begin dumping the database.
  */
 static void
-do_get (void);
+start_dump (void);
 
 
 /**
- * Continuation called to notify client about result of the
- * operation.
- *
- * @param cls closure
- * @param success GNUNET_SYSERR on failure (including timeout/queue drop)
- *                GNUNET_NO if content was already there
- *                GNUNET_YES (or other positive value) on success
- * @param min_expiration minimum expiration time required for 0-priority content to be stored
- *                by the datacache at this time, zero for unknown, forever if we have no
- *                space for 0-priority content
- * @param msg NULL on success, otherwise an error message
+ * Begin inserting into the database.
  */
 static void
-do_finish (void *cls,
-          int32_t success,
-          struct GNUNET_TIME_Absolute min_expiration,
-          const char *msg)
-{
-  qe = NULL;
-  if (GNUNET_SYSERR == success)
-  {
-    fprintf (stderr,
-            _("Failed to store item: %s, aborting\n"),
-            msg);
-    ret = 1;
-    GNUNET_SCHEDULER_shutdown ();
-    return;
-  }
-  do_get ();
-}
+start_insert (void);
+
+
+/**
+ * Perform next GET operation.
+ */
+static void
+do_get (const uint64_t next_uid);
 
 
 /**
@@ -136,7 +170,7 @@ do_finish (void *cls,
  *        maybe 0 if no unique identifier is available
  */
 static void
-do_put (void *cls,
+get_cb (void *cls,
         const struct GNUNET_HashCode *key,
         size_t size,
         const void *data,
@@ -144,33 +178,61 @@ do_put (void *cls,
         uint32_t priority,
         uint32_t anonymity,
         uint32_t replication,
-        struct GNUNET_TIME_Absolute
-        expiration,
+        struct GNUNET_TIME_Absolute expiration,
         uint64_t uid)
 {
   qe = NULL;
-  if ( (0 != offset) &&
-       (uid == first_uid) )
+  if (NULL == key)
+  {
+    fprintf (stderr, _ ("Dumped %" PRIu64 " records\n"), record_count);
+    GNUNET_DISK_file_close (file_handle);
+    file_handle = NULL;
+    if (insert)
+      start_insert ();
+    else
+    {
+      ret = 0;
+      GNUNET_SCHEDULER_shutdown ();
+    }
+    return;
+  }
+
+  struct DataRecord dr;
+  dr.size = htonl ((uint32_t) size);
+  dr.type = htonl (type);
+  dr.priority = htonl (priority);
+  dr.anonymity = htonl (anonymity);
+  dr.replication = htonl (replication);
+  dr.expiration = GNUNET_TIME_absolute_hton (expiration);
+  dr.key = *key;
+
+  ssize_t len;
+  len = GNUNET_DISK_file_write (file_handle, &dr, sizeof (dr));
+  if (sizeof (dr) != len)
   {
+    fprintf (stderr,
+             _ ("Short write to file: %zd bytes expecting %zd\n"),
+             len,
+             sizeof (dr));
+    ret = 1;
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
-  if (0 == offset)
-    first_uid = uid;
-  qe = GNUNET_DATASTORE_put (db_dst,
-                             0,
-                             key,
-                             size,
-                             data,
-                             type,
-                             priority,
-                             anonymity,
-                             replication,
-                             expiration,
-                             0,
-                             1,
-                             &do_finish,
-                             NULL);
+
+  len = GNUNET_DISK_file_write (file_handle, data, size);
+  if (size != len)
+  {
+    fprintf (stderr,
+             _ ("Short write to file: %zd bytes expecting %zd\n"),
+             len,
+             size);
+    ret = 1;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+
+  record_count++;
+  do_get (uid + 1);
 }
 
 
@@ -178,64 +240,223 @@ do_put (void *cls,
  * Perform next GET operation.
  */
 static void
-do_get ()
+do_get (const uint64_t next_uid)
 {
-  qe = GNUNET_DATASTORE_get_key (db_src,
-                                0, false,
-                                NULL, GNUNET_BLOCK_TYPE_ANY,
-                                0, 1,
-                                &do_put, NULL);
+  GNUNET_assert (NULL == qe);
+  qe = GNUNET_DATASTORE_get_key (datastore,
+                                 next_uid,
+                                 false /* random */,
+                                 NULL /* key */,
+                                 GNUNET_BLOCK_TYPE_ANY,
+                                 0 /* queue_priority */,
+                                 1 /* max_queue_size */,
+                                 &get_cb,
+                                 NULL /* proc_cls */);
+  if (NULL == qe)
+  {
+    fprintf (stderr, _ ("Error queueing datastore GET operation\n"));
+    ret = 1;
+    GNUNET_SCHEDULER_shutdown ();
+  }
 }
 
 
+/**
+ * Begin dumping the database.
+ */
+static void
+start_dump ()
+{
+  record_count = 0;
+
+  if (NULL != file_name)
+  {
+    file_handle = GNUNET_DISK_file_open (file_name,
+                                         GNUNET_DISK_OPEN_WRITE |
+                                           GNUNET_DISK_OPEN_TRUNCATE |
+                                           GNUNET_DISK_OPEN_CREATE,
+                                         GNUNET_DISK_PERM_USER_READ |
+                                           GNUNET_DISK_PERM_USER_WRITE);
+    if (NULL == file_handle)
+    {
+      fprintf (stderr, _ ("Unable to open dump file: %s\n"), file_name);
+      ret = 1;
+      GNUNET_SCHEDULER_shutdown ();
+      return;
+    }
+  }
+  else
+  {
+    file_handle = GNUNET_DISK_get_handle_from_int_fd (STDOUT_FILENO);
+  }
+  GNUNET_DISK_file_write (file_handle, MAGIC_BYTES, MAGIC_LEN);
+  do_get (0);
+}
+
 
 /**
- * Main function that will be run by the scheduler.
+ * Continuation called to notify client about result of the
+ * operation.
  *
  * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used
- * @param cfg configuration -- for destination datastore
+ * @param success GNUNET_SYSERR on failure (including timeout/queue drop)
+ *                GNUNET_NO if content was already there
+ *                GNUNET_YES (or other positive value) on success
+ * @param min_expiration minimum expiration time required for 0-priority content to be stored
+ *                by the datacache at this time, zero for unknown, forever if we have no
+ *                space for 0-priority content
+ * @param msg NULL on success, otherwise an error message
  */
 static void
-run (void *cls, char *const *args, const char *cfgfile,
-     const struct GNUNET_CONFIGURATION_Handle *cfg)
+put_cb (void *cls,
+        int32_t success,
+        struct GNUNET_TIME_Absolute min_expiration,
+        const char *msg)
 {
-  if (NULL == alternative_cfg)
-    return; /* nothing to be done */
-  if (0 == strcmp (cfgfile, alternative_cfg))
+  qe = NULL;
+  if (GNUNET_SYSERR == success)
   {
-    fprintf (stderr,
-            _("Cannot use the same configuration for source and destination\n"));
+    fprintf (stderr, _ ("Failed to store item: %s, aborting\n"), msg);
     ret = 1;
+    GNUNET_SCHEDULER_shutdown ();
     return;
   }
-  scfg = GNUNET_CONFIGURATION_create ();
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_load (scfg,
-                                alternative_cfg))
+
+  struct DataRecord dr;
+  ssize_t len;
+
+  len = GNUNET_DISK_file_read (file_handle, &dr, sizeof (dr));
+  if (0 == len)
+  {
+    fprintf (stderr, _ ("Inserted %" PRIu64 " records\n"), record_count);
+    ret = 0;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  else if (sizeof (dr) != len)
   {
-    GNUNET_CONFIGURATION_destroy (scfg);
+    fprintf (stderr,
+             _ ("Short read from file: %zd bytes expecting %zd\n"),
+             len,
+             sizeof (dr));
     ret = 1;
+    GNUNET_SCHEDULER_shutdown ();
     return;
   }
-  db_src = GNUNET_DATASTORE_connect (scfg);
-  if (NULL == db_src)
+
+  const size_t size = ntohl (dr.size);
+  uint8_t data[size];
+  len = GNUNET_DISK_file_read (file_handle, data, size);
+  if (size != len)
   {
-    GNUNET_CONFIGURATION_destroy (scfg);
+    fprintf (stderr,
+             _ ("Short read from file: %zd bytes expecting %zd\n"),
+             len,
+             size);
     ret = 1;
+    GNUNET_SCHEDULER_shutdown ();
     return;
   }
-  db_dst = GNUNET_DATASTORE_connect (cfg);
-  if (NULL == db_dst)
+
+  record_count++;
+  qe = GNUNET_DATASTORE_put (datastore,
+                             0,
+                             &dr.key,
+                             size,
+                             data,
+                             ntohl (dr.type),
+                             ntohl (dr.priority),
+                             ntohl (dr.anonymity),
+                             ntohl (dr.replication),
+                             GNUNET_TIME_absolute_ntoh (dr.expiration),
+                             0,
+                             1,
+                             &put_cb,
+                             NULL);
+  if (NULL == qe)
   {
-    GNUNET_DATASTORE_disconnect (db_src, GNUNET_NO);
-    GNUNET_CONFIGURATION_destroy (scfg);
+    fprintf (stderr, _ ("Error queueing datastore PUT operation\n"));
     ret = 1;
+    GNUNET_SCHEDULER_shutdown ();
+  }
+}
+
+
+/**
+ * Begin inserting into the database.
+ */
+static void
+start_insert ()
+{
+  record_count = 0;
+
+  if (NULL != file_name)
+  {
+    file_handle = GNUNET_DISK_file_open (file_name,
+                                         GNUNET_DISK_OPEN_READ,
+                                         GNUNET_DISK_PERM_NONE);
+    if (NULL == file_handle)
+    {
+      fprintf (stderr, _ ("Unable to open dump file: %s\n"), file_name);
+      ret = 1;
+      GNUNET_SCHEDULER_shutdown ();
+      return;
+    }
+  }
+  else
+  {
+    file_handle = GNUNET_DISK_get_handle_from_int_fd (STDIN_FILENO);
+  }
+
+  uint8_t buf[MAGIC_LEN];
+  ssize_t len;
+
+  len = GNUNET_DISK_file_read (file_handle, buf, MAGIC_LEN);
+  if (len != MAGIC_LEN || 0 != memcmp (buf, MAGIC_BYTES, MAGIC_LEN))
+  {
+    fprintf (stderr, _ ("Input file is not of a supported format\n"));
     return;
   }
+  put_cb (NULL, GNUNET_YES, GNUNET_TIME_UNIT_ZERO_ABS, NULL);
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used
+ * @param cfg configuration
+ */
+static void
+run (void *cls,
+     char *const *args,
+     const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
   GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
-  do_get ();
+  datastore = GNUNET_DATASTORE_connect (cfg);
+  if (NULL == datastore)
+  {
+    fprintf (stderr, _ ("Failed connecting to the datastore.\n"));
+    ret = 1;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  if (dump)
+    start_dump ();
+  else if (insert)
+    start_insert ();
+  else
+  {
+    fprintf (stderr,
+             _ ("Please choose at least one operation: %s, %s\n"),
+             "dump",
+             "insert");
+    ret = 1;
+    GNUNET_SCHEDULER_shutdown ();
+  }
 }
 
 
@@ -249,23 +470,36 @@ run (void *cls, char *const *args, const char *cfgfile,
 int
 main (int argc, char *const *argv)
 {
-  struct GNUNET_GETOPT_CommandLineOption options[] = {
-    GNUNET_GETOPT_option_filename ('s',
-                                   "sourcecfg",
-                                   "FILENAME",
-                                   gettext_noop ("specifies the configuration to use to access an alternative datastore; will merge that datastore into our current datastore"),
-                                       &alternative_cfg),
-    GNUNET_GETOPT_OPTION_END
-  };
+  struct GNUNET_GETOPT_CommandLineOption options[] =
+    {GNUNET_GETOPT_option_flag ('d',
+                                "dump",
+                                gettext_noop (
+                                  "Dump all records from the datastore"),
+                                &dump),
+     GNUNET_GETOPT_option_flag ('i',
+                                "insert",
+                                gettext_noop (
+                                  "Insert records into the datastore"),
+                                &insert),
+     GNUNET_GETOPT_option_filename ('f',
+                                    "file",
+                                    "FILENAME",
+                                    gettext_noop ("File to dump or insert"),
+                                    &file_name),
+     GNUNET_GETOPT_OPTION_END};
   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
     return 2;
 
   if (GNUNET_OK !=
-      GNUNET_PROGRAM_run (argc, argv, "gnunet-datastore",
-                         gettext_noop ("Manipulate GNUnet datastore"),
-                         options, &run, NULL))
+      GNUNET_PROGRAM_run (argc,
+                          argv,
+                          "gnunet-datastore",
+                          gettext_noop ("Manipulate GNUnet datastore"),
+                          options,
+                          &run,
+                          NULL))
     ret = 1;
-  GNUNET_free ((void*) argv);
+  GNUNET_free ((void *) argv);
   return ret;
 }