/*
This file is part of GNUnet.
Copyright (C) 2012, 2013, 2014 GNUnet e.V.
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
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 .
SPDX-License-Identifier: AGPL3.0-or-later
*/
/**
* @file gnunet-namestore.c
* @brief command line tool to manipulate the local zone
* @author Christian Grothoff
*
* TODO:
* - test
*/
#include "platform.h"
#include
#include
#include
#include
#include
#include
/**
* Entry in record set for bulk processing.
*/
struct RecordSetEntry
{
/**
* Kept in a linked list.
*/
struct RecordSetEntry *next;
/**
* The record to add/remove.
*/
struct GNUNET_GNSRECORD_Data record;
};
/**
* Handle to the namestore.
*/
static struct GNUNET_NAMESTORE_Handle *ns;
/**
* Private key for the our zone.
*/
static struct GNUNET_CRYPTO_EcdsaPrivateKey zone_pkey;
/**
* Handle to identity lookup.
*/
static struct GNUNET_IDENTITY_EgoLookup *el;
/**
* Identity service handle
*/
static struct GNUNET_IDENTITY_Handle *idh;
/**
* Obtain default ego
*/
struct GNUNET_IDENTITY_Operation *get_default;
/**
* Name of the ego controlling the zone.
*/
static char *ego_name;
/**
* Desired action is to add a record.
*/
static int add;
/**
* Queue entry for the 'add-uri' operation.
*/
static struct GNUNET_NAMESTORE_QueueEntry *add_qe_uri;
/**
* Queue entry for the 'add' operation.
*/
static struct GNUNET_NAMESTORE_QueueEntry *add_qe;
/**
* Queue entry for the 'lookup' operation.
*/
static struct GNUNET_NAMESTORE_QueueEntry *get_qe;
/**
* Queue entry for the 'reverse lookup' operation (in combination with a name).
*/
static struct GNUNET_NAMESTORE_QueueEntry *reverse_qe;
/**
* Desired action is to list records.
*/
static int list;
/**
* List iterator for the 'list' operation.
*/
static struct GNUNET_NAMESTORE_ZoneIterator *list_it;
/**
* Desired action is to remove a record.
*/
static int del;
/**
* Is record public (opposite of #GNUNET_GNSRECORD_RF_PRIVATE)
*/
static int is_public;
/**
* Is record a shadow record (#GNUNET_GNSRECORD_RF_SHADOW_RECORD)
*/
static int is_shadow;
/**
* Queue entry for the 'del' operation.
*/
static struct GNUNET_NAMESTORE_QueueEntry *del_qe;
/**
* Queue entry for the 'set/replace' operation.
*/
static struct GNUNET_NAMESTORE_QueueEntry *set_qe;
/**
* Name of the records to add/list/remove.
*/
static char *name;
/**
* Value of the record to add/remove.
*/
static char *value;
/**
* URI to import.
*/
static char *uri;
/**
* Reverse lookup to perform.
*/
static char *reverse_pkey;
/**
* Type of the record to add/remove, NULL to remove all.
*/
static char *typestring;
/**
* Desired expiration time.
*/
static char *expirationstring;
/**
* Desired nick name.
*/
static char *nickstring;
/**
* Global return value
*/
static int ret;
/**
* Type string converted to DNS type value.
*/
static uint32_t type;
/**
* Value in binary format.
*/
static void *data;
/**
* Number of bytes in #data.
*/
static size_t data_size;
/**
* Expiration string converted to numeric value.
*/
static uint64_t etime;
/**
* Is expiration time relative or absolute time?
*/
static int etime_is_rel = GNUNET_SYSERR;
/**
* Monitor handle.
*/
static struct GNUNET_NAMESTORE_ZoneMonitor *zm;
/**
* Enables monitor mode.
*/
static int monitor;
/**
* Entry in record set for processing records in bulk.
*/
static struct RecordSetEntry *recordset;
/**
* Task run on shutdown. Cleans up everything.
*
* @param cls unused
*/
static void
do_shutdown (void *cls)
{
(void) cls;
if (NULL != get_default)
{
GNUNET_IDENTITY_cancel (get_default);
get_default = NULL;
}
if (NULL != idh)
{
GNUNET_IDENTITY_disconnect (idh);
idh = NULL;
}
if (NULL != el)
{
GNUNET_IDENTITY_ego_lookup_cancel (el);
el = NULL;
}
if (NULL != list_it)
{
GNUNET_NAMESTORE_zone_iteration_stop (list_it);
list_it = NULL;
}
if (NULL != add_qe)
{
GNUNET_NAMESTORE_cancel (add_qe);
add_qe = NULL;
}
if (NULL != set_qe)
{
GNUNET_NAMESTORE_cancel (set_qe);
set_qe = NULL;
}
if (NULL != add_qe_uri)
{
GNUNET_NAMESTORE_cancel (add_qe_uri);
add_qe_uri = NULL;
}
if (NULL != get_qe)
{
GNUNET_NAMESTORE_cancel (get_qe);
get_qe = NULL;
}
if (NULL != del_qe)
{
GNUNET_NAMESTORE_cancel (del_qe);
del_qe = NULL;
}
if (NULL != ns)
{
GNUNET_NAMESTORE_disconnect (ns);
ns = NULL;
}
memset (&zone_pkey, 0, sizeof (zone_pkey));
if (NULL != uri)
{
GNUNET_free (uri);
uri = NULL;
}
if (NULL != zm)
{
GNUNET_NAMESTORE_zone_monitor_stop (zm);
zm = NULL;
}
if (NULL != data)
{
GNUNET_free (data);
data = NULL;
}
}
/**
* Check if we are finished, and if so, perform shutdown.
*/
static void
test_finished ()
{
if ( (NULL == add_qe) &&
(NULL == add_qe_uri) &&
(NULL == get_qe) &&
(NULL == del_qe) &&
(NULL == reverse_qe) &&
(NULL == list_it) )
GNUNET_SCHEDULER_shutdown ();
}
/**
* Continuation called to notify client about result of the
* operation.
*
* @param cls closure, location of the QueueEntry pointer to NULL out
* @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
* #GNUNET_NO if content was already there
* #GNUNET_YES (or other positive value) on success
* @param emsg NULL on success, otherwise an error message
*/
static void
add_continuation (void *cls,
int32_t success,
const char *emsg)
{
struct GNUNET_NAMESTORE_QueueEntry **qe = cls;
*qe = NULL;
if (GNUNET_YES != success)
{
fprintf (stderr,
_("Adding record failed: %s\n"),
(GNUNET_NO == success) ? "record exists" : emsg);
if (GNUNET_NO != success)
ret = 1;
}
ret = 0;
test_finished ();
}
/**
* Continuation called to notify client about result of the
* operation.
*
* @param cls closure, unused
* @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
* #GNUNET_NO if content was already there
* #GNUNET_YES (or other positive value) on success
* @param emsg NULL on success, otherwise an error message
*/
static void
del_continuation (void *cls,
int32_t success,
const char *emsg)
{
(void) cls;
del_qe = NULL;
if (GNUNET_NO == success)
{
fprintf (stderr,
_("Deleting record failed, record does not exist%s%s\n"),
(NULL != emsg) ? ": " : "",
(NULL != emsg) ? emsg : "");
}
if (GNUNET_SYSERR == success)
{
fprintf (stderr,
_("Deleting record failed%s%s\n"),
(NULL != emsg) ? ": " : "",
(NULL != emsg) ? emsg : "");
}
test_finished ();
}
/**
* Function called when we are done with a zone iteration.
*/
static void
zone_iteration_finished (void *cls)
{
(void) cls;
list_it = NULL;
test_finished ();
}
/**
* Function called when we encountered an error in a zone iteration.
*/
static void
zone_iteration_error_cb (void *cls)
{
(void) cls;
list_it = NULL;
fprintf (stderr,
"Error iterating over zone\n");
ret = 1;
test_finished ();
}
/**
* Process a record that was stored in the namestore.
*
* @param rname name that is being mapped (at most 255 characters long)
* @param rd_len number of entries in @a rd array
* @param rd array of records with data to store
*/
static void
display_record (const char *rname,
unsigned int rd_len,
const struct GNUNET_GNSRECORD_Data *rd)
{
const char *typestring;
char *s;
const char *ets;
struct GNUNET_TIME_Absolute at;
struct GNUNET_TIME_Relative rt;
if ( (NULL != name) &&
(0 != strcmp (name, rname)) )
{
GNUNET_NAMESTORE_zone_iterator_next (list_it,
1);
return;
}
FPRINTF (stdout,
"%s:\n",
rname);
for (unsigned int i=0;idata = data;
rde->data_size = data_size;
rde->record_type = type;
if (1 == is_shadow)
rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
if (1 != is_public)
rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
rde->expiration_time = etime;
if (GNUNET_YES == etime_is_rel)
rde->flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
else if (GNUNET_NO != etime_is_rel)
rde->expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
GNUNET_assert (NULL != name);
add_qe = GNUNET_NAMESTORE_records_store (ns,
&zone_pkey,
name,
rd_count + 1,
rde,
&add_continuation,
&add_qe);
}
/**
* Function called if we encountered an error in zone-to-name.
*/
static void
reverse_error_cb (void *cls)
{
(void) cls;
reverse_qe = NULL;
FPRINTF (stdout,
"%s.zkey\n",
reverse_pkey);
}
/**
* Function called with the result of our attempt to obtain a name for a given
* public key.
*
* @param cls NULL
* @param zone private key of the zone; NULL on disconnect
* @param label label of the records; NULL on disconnect
* @param rd_count number of entries in @a rd array, 0 if label was deleted
* @param rd array of records with data to store
*/
static void
handle_reverse_lookup (void *cls,
const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
const char *label,
unsigned int rd_count,
const struct GNUNET_GNSRECORD_Data *rd)
{
(void) cls;
(void) zone;
(void) rd_count;
(void) rd;
reverse_qe = NULL;
if (NULL == label)
FPRINTF (stdout,
"%s\n",
reverse_pkey);
else
FPRINTF (stdout,
"%s.%s\n",
label,
ego_name);
test_finished ();
}
/**
* Function called if lookup for deletion fails.
*/
static void
del_lookup_error_cb (void *cls)
{
(void) cls;
del_qe = NULL;
GNUNET_break (0);
ret = 1;
test_finished ();
}
/**
* We were asked to delete something; this function is called with
* the existing records. Now we should determine what should be
* deleted and then issue the deletion operation.
*
* @param cls NULL
* @param zone private key of the zone we are deleting from
* @param label name of the records we are editing
* @param rd_count size of the @a rd array
* @param rd existing records
*/
static void
del_monitor (void *cls,
const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
const char *label,
unsigned int rd_count,
const struct GNUNET_GNSRECORD_Data *rd)
{
struct GNUNET_GNSRECORD_Data rdx[rd_count];
unsigned int rd_left;
uint32_t type;
char *vs;
(void) cls;
(void) zone;
del_qe = NULL;
if (0 == rd_count)
{
FPRINTF (stderr,
_("There are no records under label `%s' that could be deleted.\n"),
label);
ret = 1;
test_finished ();
return;
}
if ( (NULL == value) &&
(NULL == typestring) )
{
/* delete everything */
del_qe = GNUNET_NAMESTORE_records_store (ns,
&zone_pkey,
name,
0,
NULL,
&del_continuation,
NULL);
return;
}
rd_left = 0;
if (NULL != typestring)
type = GNUNET_GNSRECORD_typename_to_number (typestring);
else
type = GNUNET_GNSRECORD_TYPE_ANY;
for (unsigned int i=0;inext)
rd_count++;
rd = GNUNET_new_array (rd_count,
struct GNUNET_GNSRECORD_Data);
rd_count = 0;
for (struct RecordSetEntry *e = recordset; NULL != e; e = e->next)
{
rd[rd_count] = e->record;
rd_count++;
}
set_qe = GNUNET_NAMESTORE_records_store (ns,
&zone_pkey,
name,
rd_count,
rd,
&replace_cont,
NULL);
GNUNET_free (rd);
return;
}
if (add)
{
if (NULL == name)
{
fprintf (stderr,
_("Missing option `%s' for operation `%s'\n"),
"-n", _("add"));
GNUNET_SCHEDULER_shutdown ();
ret = 1;
return;
}
if (NULL == typestring)
{
fprintf (stderr,
_("Missing option `%s' for operation `%s'\n"),
"-t", _("add"));
GNUNET_SCHEDULER_shutdown ();
ret = 1;
return;
}
type = GNUNET_GNSRECORD_typename_to_number (typestring);
if (UINT32_MAX == type)
{
fprintf (stderr,
_("Unsupported type `%s'\n"),
typestring);
GNUNET_SCHEDULER_shutdown ();
ret = 1;
return;
}
if (NULL == value)
{
fprintf (stderr,
_("Missing option `%s' for operation `%s'\n"),
"-V", _("add"));
ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
if (GNUNET_OK !=
GNUNET_GNSRECORD_string_to_value (type,
value,
&data,
&data_size))
{
fprintf (stderr,
_("Value `%s' invalid for record type `%s'\n"),
value,
typestring);
GNUNET_SCHEDULER_shutdown ();
ret = 1;
return;
}
if (NULL == expirationstring)
{
fprintf (stderr,
_("Missing option `%s' for operation `%s'\n"),
"-e",
_("add"));
GNUNET_SCHEDULER_shutdown ();
ret = 1;
return;
}
if (GNUNET_OK !=
parse_expiration (expirationstring,
&etime_is_rel,
&etime))
{
fprintf (stderr,
_("Invalid time format `%s'\n"),
expirationstring);
GNUNET_SCHEDULER_shutdown ();
ret = 1;
return;
}
add_qe = GNUNET_NAMESTORE_records_lookup (ns,
&zone_pkey,
name,
&add_error_cb,
NULL,
&get_existing_record,
NULL);
}
if (del)
{
if (NULL == name)
{
fprintf (stderr,
_("Missing option `%s' for operation `%s'\n"),
"-n", _("del"));
GNUNET_SCHEDULER_shutdown ();
ret = 1;
return;
}
del_qe = GNUNET_NAMESTORE_records_lookup (ns,
&zone_pkey,
name,
&del_lookup_error_cb,
NULL,
&del_monitor,
NULL);
}
if (list)
{
if (NULL != name)
get_qe = GNUNET_NAMESTORE_records_lookup (ns,
&zone_pkey,
name,
&lookup_error_cb,
NULL,
&display_record_lookup,
NULL);
else
list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
&zone_pkey,
&zone_iteration_error_cb,
NULL,
&display_record_iterator,
NULL,
&zone_iteration_finished,
NULL);
}
if (NULL != reverse_pkey)
{
struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
if (GNUNET_OK !=
GNUNET_CRYPTO_ecdsa_public_key_from_string (reverse_pkey,
strlen (reverse_pkey),
&pubkey))
{
fprintf (stderr,
_("Invalid public key for reverse lookup `%s'\n"),
reverse_pkey);
GNUNET_SCHEDULER_shutdown ();
}
reverse_qe = GNUNET_NAMESTORE_zone_to_name (ns,
&zone_pkey,
&pubkey,
&reverse_error_cb,
NULL,
&handle_reverse_lookup,
NULL);
}
if (NULL != uri)
{
char sh[105];
char sname[64];
struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
GNUNET_STRINGS_utf8_tolower (uri, uri);
if ( (2 != (sscanf (uri,
"gnunet://gns/%52s/%63s",
sh,
sname)) ) ||
(GNUNET_OK !=
GNUNET_CRYPTO_ecdsa_public_key_from_string (sh,
strlen (sh),
&pkey)) )
{
fprintf (stderr,
_("Invalid URI `%s'\n"),
uri);
GNUNET_SCHEDULER_shutdown ();
ret = 1;
return;
}
memset (&rd, 0, sizeof (rd));
rd.data = &pkey;
rd.data_size = sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
rd.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
rd.expiration_time = etime;
if (GNUNET_YES == etime_is_rel)
rd.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
if (1 == is_shadow)
rd.flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
add_qe_uri = GNUNET_NAMESTORE_records_store (ns,
&zone_pkey,
sname,
1,
&rd,
&add_continuation,
&add_qe_uri);
}
if (NULL != nickstring)
{
if (0 == strlen(nickstring))
{
fprintf (stderr,
_("Invalid nick `%s'\n"),
nickstring);
GNUNET_SCHEDULER_shutdown ();
ret = 1;
return;
}
add_qe_uri = GNUNET_NAMESTORE_set_nick (ns,
&zone_pkey,
nickstring,
&add_continuation,
&add_qe_uri);
}
if (monitor)
{
zm = GNUNET_NAMESTORE_zone_monitor_start (cfg,
&zone_pkey,
GNUNET_YES,
&monitor_error_cb,
NULL,
&display_record_monitor,
NULL,
&sync_cb,
NULL);
}
}
static void
default_ego_cb (void *cls,
struct GNUNET_IDENTITY_Ego *ego,
void **ctx,
const char *name)
{
(void) cls;
(void) ctx;
(void) name;
get_default = NULL;
if (NULL == ego)
{
fprintf (stderr,
_("No default ego configured in identity service\n"));
GNUNET_SCHEDULER_shutdown ();
ret = -1;
return;
}
else
{
identity_cb (cls, ego);
}
}
static void
id_connect_cb (void *cls,
struct GNUNET_IDENTITY_Ego *ego,
void **ctx,
const char *name)
{
const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
(void) cls;
(void) ctx;
(void) name;
if (NULL == ego)
{
get_default = GNUNET_IDENTITY_get (idh,
"namestore",
&default_ego_cb,
(void *) cfg);
}
}
/**
* Main function that will be run.
*
* @param cls closure
* @param args remaining command-line arguments
* @param cfgfile name of the configuration file used (for saving, can be NULL!)
* @param cfg configuration
*/
static void
run (void *cls,
char *const *args,
const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *cfg)
{
(void) cls;
(void) args;
(void) cfgfile;
if (NULL != args[0])
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_("Superfluous command line arguments (starting with `%s') ignored\n"),
args[0]);
if ( (NULL != args[0]) &&
(NULL == uri) )
uri = GNUNET_strdup (args[0]);
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
(void *) cfg);
if (NULL == ego_name)
{
idh = GNUNET_IDENTITY_connect (cfg,
&id_connect_cb,
(void *) cfg);
if (NULL == idh)
fprintf (stderr,
_("Cannot connect to identity service\n"));
ret = -1;
return;
}
el = GNUNET_IDENTITY_ego_lookup (cfg,
ego_name,
&identity_cb,
(void *) cfg);
}
/**
* Command-line option parser function that allows the user to specify
* a complete record as one argument for adding/removing. A pointer
* to the head of the list of record sets must be passed as the "scls"
* argument.
*
* @param ctx command line processor context
* @param scls must be of type "struct GNUNET_FS_Uri **"
* @param option name of the option (typically 'R')
* @param value command line argument given; format is
* "TTL TYPE FLAGS VALUE" where TTL is an expiration time (rel or abs),
* always given in seconds (without the unit),
* TYPE is a DNS/GNS record type, FLAGS is either "n" for no flags or
* a combination of 's' (shadow) and 'p' (public) and VALUE is the
* value (in human-readable format)
* @return #GNUNET_OK on success
*/
static int
multirecord_process (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
void *scls,
const char *option,
const char *value)
{
struct RecordSetEntry **head = scls;
struct RecordSetEntry *r;
struct GNUNET_GNSRECORD_Data record;
char *cp;
char *tok;
char *saveptr;
int etime_is_rel;
void *raw_data;
(void) ctx;
(void) option;
cp = GNUNET_strdup (value);
tok = strtok_r (cp, " ", &saveptr);
if (NULL == tok)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Empty record line argument is not allowed.\n"));
GNUNET_free (cp);
return GNUNET_SYSERR;
}
{
char *etime_in_s;
GNUNET_asprintf (&etime_in_s,
"%s s",
tok);
if (GNUNET_OK !=
parse_expiration (etime_in_s,
&etime_is_rel,
&record.expiration_time))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Invalid expiration time `%s' (must be without unit)\n"),
tok);
GNUNET_free (cp);
GNUNET_free (etime_in_s);
return GNUNET_SYSERR;
}
GNUNET_free (etime_in_s);
}
tok = strtok_r (NULL, " ", &saveptr);
if (NULL == tok)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Missing entries in record line `%s'.\n"),
value);
GNUNET_free (cp);
return GNUNET_SYSERR;
}
record.record_type = GNUNET_GNSRECORD_typename_to_number (tok);
if (UINT32_MAX == record.record_type)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Unknown record type `%s'\n"),
tok);
GNUNET_free (cp);
return GNUNET_SYSERR;
}
tok = strtok_r (NULL, " ", &saveptr);
if (NULL == tok)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Missing entries in record line `%s'.\n"),
value);
GNUNET_free (cp);
return GNUNET_SYSERR;
}
record.flags = GNUNET_GNSRECORD_RF_NONE;
if (etime_is_rel)
record.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
if (NULL == strchr (tok, (unsigned char) 'p')) /* p = public */
record.flags |= GNUNET_GNSRECORD_RF_PRIVATE;
if (NULL != strchr (tok, (unsigned char) 's'))
record.flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
/* find beginning of record value */
tok = strchr (&value[tok - cp], (unsigned char) ' ');
if (NULL == tok)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Missing entries in record line `%s'.\n"),
value);
GNUNET_free (cp);
return GNUNET_SYSERR;
}
GNUNET_free (cp);
tok++; /* skip space */
if (GNUNET_OK !=
GNUNET_GNSRECORD_string_to_value (record.record_type,
tok,
&raw_data,
&record.data_size))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Invalid record data for type %s: `%s'.\n"),
GNUNET_GNSRECORD_number_to_typename (record.record_type),
tok);
return GNUNET_SYSERR;
}
r = GNUNET_malloc (sizeof (struct RecordSetEntry) + record.data_size);
r->next = *head;
record.data = &r[1];
memcpy (&r[1],
raw_data,
record.data_size);
GNUNET_free (raw_data);
r->record = record;
*head = r;
return GNUNET_OK;
}
/**
* Allow user to specify keywords.
*
* @param shortName short name of the option
* @param name long name of the option
* @param argumentHelp help text for the option argument
* @param description long help text for the option
* @param[out] topKeywords set to the desired value
*/
struct GNUNET_GETOPT_CommandLineOption
multirecord_option (char shortName,
const char *name,
const char *argumentHelp,
const char *description,
struct RecordSetEntry **rs)
{
struct GNUNET_GETOPT_CommandLineOption clo = {
.shortName = shortName,
.name = name,
.argumentHelp = argumentHelp,
.description = description,
.require_argument = 1,
.processor = &multirecord_process,
.scls = (void *) rs
};
return clo;
}
/**
* The main function for gnunet-namestore.
*
* @param argc number of arguments from the command line
* @param argv command line arguments
* @return 0 ok, 1 on error
*/
int
main (int argc,
char *const *argv)
{
struct GNUNET_GETOPT_CommandLineOption options[] = {
GNUNET_GETOPT_option_flag ('a',
"add",
gettext_noop ("add record"),
&add),
GNUNET_GETOPT_option_flag ('d',
"delete",
gettext_noop ("delete record"),
&del),
GNUNET_GETOPT_option_flag ('D',
"display",
gettext_noop ("display records"),
&list),
GNUNET_GETOPT_option_string ('e',
"expiration",
"TIME",
gettext_noop ("expiration time for record to use (for adding only), \"never\" is possible"),
&expirationstring),
GNUNET_GETOPT_option_string ('i',
"nick",
"NICKNAME",
gettext_noop ("set the desired nick name for the zone"),
&nickstring),
GNUNET_GETOPT_option_flag ('m',
"monitor",
gettext_noop ("monitor changes in the namestore"),
&monitor),
GNUNET_GETOPT_option_string ('n',
"name",
"NAME",
gettext_noop ("name of the record to add/delete/display"),
&name),
GNUNET_GETOPT_option_string ('r',
"reverse",
"PKEY",
gettext_noop ("determine our name for the given PKEY"),
&reverse_pkey),
multirecord_option ('R',
"replace",
"RECORDLINE",
gettext_noop ("set record set to values given by (possibly multiple) RECORDLINES; can be specified multiple times"),
&recordset),
GNUNET_GETOPT_option_string ('t',
"type",
"TYPE",
gettext_noop ("type of the record to add/delete/display"),
&typestring),
GNUNET_GETOPT_option_string ('u',
"uri",
"URI",
gettext_noop ("URI to import into our zone"),
&uri),
GNUNET_GETOPT_option_string ('V',
"value",
"VALUE",
gettext_noop ("value of the record to add/delete"),
&value),
GNUNET_GETOPT_option_flag ('p',
"public",
gettext_noop ("create or list public record"),
&is_public),
GNUNET_GETOPT_option_flag ('s',
"shadow",
gettext_noop ("create shadow record (only valid if all other records of the same type have expired"),
&is_shadow),
GNUNET_GETOPT_option_string ('z',
"zone",
"EGO",
gettext_noop ("name of the ego controlling the zone"),
&ego_name),
GNUNET_GETOPT_OPTION_END
};
if (GNUNET_OK !=
GNUNET_STRINGS_get_utf8_args (argc, argv,
&argc, &argv))
return 2;
is_public = -1;
is_shadow = -1;
GNUNET_log_setup ("gnunet-namestore",
"WARNING",
NULL);
if (GNUNET_OK !=
GNUNET_PROGRAM_run (argc,
argv,
"gnunet-namestore",
_("GNUnet zone manipulation tool"),
options,
&run, NULL))
{
GNUNET_free ((void*) argv);
GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey);
return 1;
}
GNUNET_free ((void*) argv);
GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey);
return ret;
}
/* end of gnunet-namestore.c */