add '-m' option to gnunet-namestore, extend namestore monitor API with notification...
[oweals/gnunet.git] / src / namestore / gnunet-namestore.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file gnunet-namestore.c
22  * @brief command line tool to manipulate the local zone
23  * @author Christian Grothoff
24  *
25  * TODO:
26  * - allow users to set record options (not just 'RF_AUTHORITY')
27  * - test
28  * - add options to list/lookup individual records
29  */
30 #include "platform.h"
31 #include <gnunet_util_lib.h>
32 #include <gnunet_dnsparser_lib.h>
33 #include <gnunet_namestore_service.h>
34
35
36 /**
37  * Hostkey generation context
38  */
39 struct GNUNET_CRYPTO_EccKeyGenerationContext * keygen;
40
41 /**
42  * Handle to the namestore.
43  */
44 static struct GNUNET_NAMESTORE_Handle *ns;
45
46 /**
47  * Hash of the public key of our zone.
48  */
49 static struct GNUNET_CRYPTO_ShortHashCode zone;
50
51 /**
52  * Private key for the our zone.
53  */
54 static struct GNUNET_CRYPTO_EccPrivateKey *zone_pkey;
55
56 /**
57  * Keyfile to manipulate.
58  */
59 static char *keyfile;   
60
61 /**
62  * Desired action is to add a record.
63  */
64 static int add;
65
66 /**
67  * Queue entry for the 'add' operation.
68  */
69 static struct GNUNET_NAMESTORE_QueueEntry *add_qe;
70
71 /**
72  * Queue entry for the 'add-uri' operation.
73  */
74 static struct GNUNET_NAMESTORE_QueueEntry *add_qe_uri;
75
76 /**
77  * Desired action is to list records.
78  */
79 static int list;
80
81 /**
82  * List iterator for the 'list' operation.
83  */
84 static struct GNUNET_NAMESTORE_ZoneIterator *list_it;
85
86 /**
87  * Desired action is to remove a record.
88  */
89 static int del;
90
91 /**
92  * Is record public
93  */
94 static int public;
95
96 /**
97  * Is record authority
98  */
99 static int nonauthority;
100
101 /**
102  * Queue entry for the 'del' operation.
103  */
104 static struct GNUNET_NAMESTORE_QueueEntry *del_qe;
105
106 /**
107  * Name of the records to add/list/remove.
108  */
109 static char *name;
110
111 /**
112  * Value of the record to add/remove.
113  */
114 static char *value;
115
116 /**
117  * URI to import.
118  */
119 static char *uri;
120
121 /**
122  * Type of the record to add/remove, NULL to remove all.
123  */
124 static char *typestring;
125
126 /**
127  * Desired expiration time.
128  */
129 static char *expirationstring;
130
131 /**
132  * Global return value
133  */
134 static int ret;
135
136 /**
137  * Type string converted to DNS type value.
138  */
139 static uint32_t type;
140
141 /**
142  * Value in binary format.
143  */
144 static void *data;
145
146 /**
147  * Number of bytes in 'data'.
148  */
149 static size_t data_size;
150
151 /**
152  * Expirationstring converted to relative time.
153  */
154 static struct GNUNET_TIME_Relative etime_rel;
155
156 /**
157  * Expirationstring converted to absolute time.
158  */
159 static struct GNUNET_TIME_Absolute etime_abs;
160
161 /**
162  * Is expiration time relative or absolute time?
163  */
164 static int etime_is_rel = GNUNET_SYSERR;
165
166 /**
167  * Monitor handle.
168  */
169 static struct GNUNET_NAMESTORE_ZoneMonitor *zm;
170
171 /**
172  * Enables monitor mode.
173  */
174 static int monitor;
175
176 /**
177  * Task run on shutdown.  Cleans up everything.
178  *
179  * @param cls unused
180  * @param tc scheduler context
181  */
182 static void
183 do_shutdown (void *cls,
184              const struct GNUNET_SCHEDULER_TaskContext *tc)
185 {
186   if (NULL != keygen)
187   {
188     GNUNET_CRYPTO_ecc_key_create_stop (keygen);
189     keygen = NULL;
190   }
191
192   if (NULL != list_it)
193   {
194     GNUNET_NAMESTORE_zone_iteration_stop (list_it);
195     list_it = NULL;
196   }
197   if (NULL != add_qe)
198   {
199     GNUNET_NAMESTORE_cancel (add_qe);
200     add_qe = NULL;
201   }
202   if (NULL != add_qe_uri)
203   {
204     GNUNET_NAMESTORE_cancel (add_qe_uri);
205     add_qe_uri = NULL;
206   }
207   if (NULL != del_qe)
208   {
209     GNUNET_NAMESTORE_cancel (del_qe);
210     del_qe = NULL;
211   }
212   if (NULL != ns)
213   {
214     GNUNET_NAMESTORE_disconnect (ns);
215     ns = NULL;
216   }
217   if (NULL != zone_pkey)
218   {
219     GNUNET_CRYPTO_ecc_key_free (zone_pkey);
220     zone_pkey = NULL;
221   }
222   if (NULL != uri)
223   {
224     GNUNET_free (uri);
225     uri = NULL;
226   }
227   if (NULL != zm)
228   {
229     GNUNET_NAMESTORE_zone_monitor_stop (zm);
230     zm = NULL;
231   }
232 }
233
234
235 /**
236  * Continuation called to notify client about result of the
237  * operation.
238  *
239  * @param cls closure, location of the QueueEntry pointer to NULL out
240  * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
241  *                GNUNET_NO if content was already there
242  *                GNUNET_YES (or other positive value) on success
243  * @param emsg NULL on success, otherwise an error message
244  */
245 static void
246 add_continuation (void *cls,
247                   int32_t success,
248                   const char *emsg)
249 {
250   struct GNUNET_NAMESTORE_QueueEntry **qe = cls;
251
252   *qe = NULL;
253   if (GNUNET_YES != success)
254   {
255     fprintf (stderr,
256              _("Adding record failed: %s\n"),
257              (GNUNET_NO == success) ? "record exists" : emsg);
258     if (GNUNET_NO != success)
259       ret = 1;
260   }
261   if ( (NULL == add_qe) &&
262        (NULL == add_qe_uri) &&
263        (NULL == del_qe) &&
264        (NULL == list_it) )
265     GNUNET_SCHEDULER_shutdown ();
266 }
267
268
269 /**
270  * Continuation called to notify client about result of the
271  * operation.
272  *
273  * @param cls closure, unused
274  * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
275  *                GNUNET_NO if content was already there
276  *                GNUNET_YES (or other positive value) on success
277  * @param emsg NULL on success, otherwise an error message
278  */
279 static void
280 del_continuation (void *cls,
281                   int32_t success,
282                   const char *emsg)
283 {
284   del_qe = NULL;
285   if (success != GNUNET_YES)
286     fprintf (stderr,
287              _("Deleting record failed: %s\n"),
288              emsg);
289   if ( (NULL == add_qe) &&
290        (NULL == add_qe_uri) &&
291        (NULL == list_it) )
292     GNUNET_SCHEDULER_shutdown ();
293 }
294
295
296 /**
297  * Process a record that was stored in the namestore.
298  *
299  * @param cls closure
300  * @param zone_key public key of the zone
301  * @param expire when does the corresponding block in the DHT expire (until
302  *               when should we never do a DHT lookup for the same name again)?; 
303  *               GNUNET_TIME_UNIT_ZERO_ABS if there are no records of any type in the namestore,
304  *               or the expiration time of the block in the namestore (even if there are zero
305  *               records matching the desired record type)
306  * @param name name that is being mapped (at most 255 characters long)
307  * @param rd_len number of entries in 'rd' array
308  * @param rd array of records with data to store
309  * @param signature signature of the record block, NULL if signature is unavailable (i.e. 
310  *        because the user queried for a particular record type only)
311  */
312 static void
313 display_record (void *cls,
314                 const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *zone_key,
315                 struct GNUNET_TIME_Absolute expire,                         
316                 const char *name,
317                 unsigned int rd_len,
318                 const struct GNUNET_NAMESTORE_RecordData *rd,
319                 const struct GNUNET_CRYPTO_EccSignature *signature)
320 {
321   const char *typestring;
322   char *s;
323   unsigned int i;
324   const char *etime;
325   struct GNUNET_TIME_Absolute aex;
326   struct GNUNET_TIME_Relative rex;
327
328   if (NULL == name)
329   {
330     list_it = NULL;
331     if ( (NULL == del_qe) &&
332          (NULL == add_qe_uri) &&
333          (NULL == add_qe) )
334       GNUNET_SCHEDULER_shutdown ();
335     return;
336   }
337   FPRINTF (stdout,
338            "%s:\n",
339            name);
340   for (i=0;i<rd_len;i++)
341   {
342     typestring = GNUNET_NAMESTORE_number_to_typename (rd[i].record_type);
343     s = GNUNET_NAMESTORE_value_to_string (rd[i].record_type,
344                                           rd[i].data,
345                                           rd[i].data_size);
346     if (NULL == s)
347     {
348       FPRINTF (stdout, _("\tCorrupt or unsupported record of type %u\n"),
349                (unsigned int) rd[i].record_type);
350       continue;
351     }
352     if (0 != (rd[i].flags & GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION))
353     {
354       rex.rel_value = rd[i].expiration_time;
355       etime = GNUNET_STRINGS_relative_time_to_string (rex, GNUNET_YES);
356     }
357     else
358     {
359       aex.abs_value = rd[i].expiration_time;
360       etime = GNUNET_STRINGS_absolute_time_to_string (aex);
361     }
362     FPRINTF (stdout, "\t%s: %s (%s %s)\n", typestring, s, 
363              (0 != (rd[i].flags & GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION)) 
364              ? _(/* what follows is relative expiration */ "for at least")
365              : _(/* what follows is absolute expiration */ "until"),
366              etime);
367     GNUNET_free (s);    
368   }
369   FPRINTF (stdout, "%s", "\n");
370   GNUNET_NAMESTORE_zone_iterator_next (list_it);
371 }
372
373
374 /**
375  * Function called once we are in sync in monitor mode.
376  *
377  * @param cls NULL
378  */
379 static void
380 sync_cb (void *cls)
381 {
382   FPRINTF (stdout, "%s", "Monitor is now in sync.\n");
383 }
384
385
386 /**
387  * We're storing a record; this function is given the existing record
388  * so that we can merge the information.
389  *
390  * @param cls closure, unused
391  * @param zone_key public key of the zone
392  * @param freshness when does the corresponding block in the DHT expire (until
393  *               when should we never do a DHT lookup for the same name again)?; 
394  *               GNUNET_TIME_UNIT_ZERO_ABS if there are no records of any type in the namestore,
395  *               or the expiration time of the block in the namestore (even if there are zero
396  *               records matching the desired record type)
397  * @param name name that is being mapped (at most 255 characters long)
398  * @param rd_count number of entries in 'rd' array
399  * @param rd array of records with data to store
400  * @param signature signature of the record block, NULL if signature is unavailable (i.e. 
401  *        because the user queried for a particular record type only)
402  */
403 static void
404 get_existing_record (void *cls,
405                      const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *zone_key,
406                      struct GNUNET_TIME_Absolute freshness,                         
407                      const char *name,
408                      unsigned int rd_count,
409                      const struct GNUNET_NAMESTORE_RecordData *rd,
410                      const struct GNUNET_CRYPTO_EccSignature *signature)
411 {
412   struct GNUNET_NAMESTORE_RecordData rdn[rd_count + 1];
413   struct GNUNET_NAMESTORE_RecordData *rde;
414   
415   add_qe = NULL;
416   memset (rdn, 0, sizeof (struct GNUNET_NAMESTORE_RecordData));
417   memcpy (&rdn[1], rd, rd_count * sizeof (struct GNUNET_NAMESTORE_RecordData));
418   /* FIXME: should add some logic to overwrite records if there
419      can only be one record of a particular type, and to check
420      if the combination of records is valid to begin with... */
421   rde = &rdn[0];
422   rde->data = data;
423   rde->data_size = data_size;
424   rde->record_type = type;
425   if (GNUNET_YES == etime_is_rel)
426   {
427     rde->expiration_time = etime_rel.rel_value;
428     rde->flags |= GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION;
429   }
430   else if (GNUNET_NO == etime_is_rel)
431   {
432     rde->expiration_time = etime_abs.abs_value;
433   }
434   if (1 != nonauthority)
435     rde->flags |= GNUNET_NAMESTORE_RF_AUTHORITY;
436   if (1 != public)
437     rde->flags |= GNUNET_NAMESTORE_RF_PRIVATE;
438     
439   add_qe = GNUNET_NAMESTORE_record_put_by_authority (ns,
440                                                      zone_pkey,
441                                                      name,
442                                                      rd_count + 1,
443                                                      rde,
444                                                      &add_continuation,
445                                                      &add_qe);
446 }
447
448
449 /**
450  * Function called with the result of the ECC key generation.
451  *
452  * @param cls our configuration
453  * @param pk our private key, NULL on failure
454  * @param emsg NULL on success, otherwise error message
455  */
456 static void
457 key_generation_cb (void *cls,
458                    struct GNUNET_CRYPTO_EccPrivateKey *pk,
459                    const char *emsg)
460 {
461   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
462   struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded pub;
463   struct GNUNET_NAMESTORE_RecordData rd;
464
465   keygen = NULL;
466   if (NULL == pk)
467   {
468     GNUNET_SCHEDULER_shutdown ();
469     return;
470   }
471   zone_pkey = pk;
472
473   if (! (add|del|list|(NULL != uri)))
474   {
475     /* nothing more to be done */  
476     fprintf (stderr,
477              _("No options given\n"));
478     GNUNET_CRYPTO_ecc_key_free (zone_pkey);
479     zone_pkey = NULL;
480     return; 
481   }
482   if (NULL == zone_pkey)
483   {
484     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
485                 _("Failed to read or create private zone key\n"));
486     return;
487   }
488   GNUNET_CRYPTO_ecc_key_get_public (zone_pkey,
489                                     &pub);
490   GNUNET_CRYPTO_short_hash (&pub, sizeof (pub), &zone);
491
492   ns = GNUNET_NAMESTORE_connect (cfg);
493   if (NULL == ns)
494   {
495     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
496                 _("Failed to connect to namestore\n"));
497     return;
498   }
499   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
500                                 &do_shutdown, NULL);
501   if (add)
502   {
503     if (NULL == name)
504     {
505       fprintf (stderr,
506                _("Missing option `%s' for operation `%s'\n"),
507                "-n", _("add"));
508       GNUNET_SCHEDULER_shutdown ();
509       ret = 1;    
510       return;     
511     }
512     if (NULL == typestring)
513     {
514       fprintf (stderr,
515                _("Missing option `%s' for operation `%s'\n"),
516                "-t", _("add"));
517       GNUNET_SCHEDULER_shutdown ();
518       ret = 1;
519       return;     
520     }
521     type = GNUNET_NAMESTORE_typename_to_number (typestring);
522     if (UINT32_MAX == type)
523     {
524       fprintf (stderr, _("Unsupported type `%s'\n"), typestring);
525       GNUNET_SCHEDULER_shutdown ();
526       ret = 1;
527       return;
528     }
529     if (NULL == value)
530     {
531       fprintf (stderr,
532                _("Missing option `%s' for operation `%s'\n"),
533                "-V", _("add"));
534       ret = 1;   
535       GNUNET_SCHEDULER_shutdown ();
536       return;     
537     }
538     if (GNUNET_OK !=
539         GNUNET_NAMESTORE_string_to_value (type,
540                                           value,
541                                           &data,
542                                           &data_size))
543     {
544       fprintf (stderr, _("Value `%s' invalid for record type `%s'\n"),
545                value,
546                typestring);
547       GNUNET_SCHEDULER_shutdown ();
548       ret = 1;
549       return;
550     }
551     if (NULL == expirationstring)
552     {
553       fprintf (stderr,
554                _("Missing option `%s' for operation `%s'\n"),
555                "-e", _("add"));
556       GNUNET_SCHEDULER_shutdown ();
557       ret = 1;    
558       return;     
559     }
560     if (0 == strcmp (expirationstring, "never"))
561     {
562       etime_abs = GNUNET_TIME_UNIT_FOREVER_ABS;
563       etime_is_rel = GNUNET_NO;
564     }
565     else if (GNUNET_OK ==
566              GNUNET_STRINGS_fancy_time_to_relative (expirationstring,
567                                                     &etime_rel))
568     {
569       etime_is_rel = GNUNET_YES;
570     }
571     else if (GNUNET_OK == 
572              GNUNET_STRINGS_fancy_time_to_absolute (expirationstring,
573                                                     &etime_abs))
574     {
575       etime_is_rel = GNUNET_NO;
576     }
577     else
578     {
579       fprintf (stderr,
580                _("Invalid time format `%s'\n"),
581                expirationstring);
582       GNUNET_SCHEDULER_shutdown ();
583       ret = 1;
584       return;     
585     }
586     add_qe = GNUNET_NAMESTORE_lookup_record (ns,
587                                              &zone,
588                                              name,
589                                              0, 
590                                              &get_existing_record,
591                                              NULL);
592   }
593   if (del)
594   {
595     if (NULL == name)
596     {
597       fprintf (stderr,
598                _("Missing option `%s' for operation `%s'\n"),
599                "-n", _("del"));
600       GNUNET_SCHEDULER_shutdown ();
601       ret = 1;
602       return;     
603     }
604     del_qe = GNUNET_NAMESTORE_record_put_by_authority (ns,
605                                                        zone_pkey,
606                                                        name,
607                                                        0, NULL,
608                                                        &del_continuation,
609                                                        NULL);
610   }
611   if (list)
612   {
613     uint32_t must_not_flags = 0;
614
615     if (1 == nonauthority) /* List non-authority records */
616       must_not_flags |= GNUNET_NAMESTORE_RF_AUTHORITY;
617
618     if (1 == public)
619       must_not_flags |= GNUNET_NAMESTORE_RF_PRIVATE;
620
621     list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
622                                                      &zone,
623                                                      GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION,
624                                                      must_not_flags,
625                                                      &display_record,
626                                                      NULL);
627   }
628   if (NULL != uri)
629   {
630     char sh[53];
631     char name[64];
632     struct GNUNET_CRYPTO_ShortHashCode sc;
633
634     if ( (2 != (sscanf (uri,
635                         "gnunet://gns/%52s/%63s",
636                         sh,
637                         name)) ) ||
638          (GNUNET_OK !=
639           GNUNET_CRYPTO_short_hash_from_string (sh, &sc)) )
640     {
641       fprintf (stderr, 
642                _("Invalid URI `%s'\n"),
643                uri);
644       GNUNET_SCHEDULER_shutdown ();
645       ret = 1;
646       return;
647     }
648     memset (&rd, 0, sizeof (rd));
649     rd.data = &sc;
650     rd.data_size = sizeof (struct GNUNET_CRYPTO_ShortHashCode);
651     rd.record_type = GNUNET_NAMESTORE_TYPE_PKEY;
652     if (GNUNET_YES == etime_is_rel)
653     {
654       rd.expiration_time = etime_rel.rel_value;
655       rd.flags |= GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION;
656     }
657     else if (GNUNET_NO == etime_is_rel)
658       rd.expiration_time = etime_abs.abs_value;
659     else    
660       rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value;
661     if (1 != nonauthority)
662       rd.flags |= GNUNET_NAMESTORE_RF_AUTHORITY;
663     add_qe_uri = GNUNET_NAMESTORE_record_put_by_authority (ns,
664                                                            zone_pkey,
665                                                            name,
666                                                            1,
667                                                            &rd,
668                                                            &add_continuation,
669                                                            &add_qe_uri);
670   }
671   GNUNET_free_non_null (data);
672   if (monitor)
673   {
674     zm = GNUNET_NAMESTORE_zone_monitor_start (cfg,
675                                               &zone,
676                                               &display_record,
677                                               &sync_cb,
678                                               NULL);
679   }
680 }
681
682
683 /**
684  * Function called with the result from the check if the namestore
685  * service is actually running.  If it is, we start the actual
686  * operation.
687  *
688  * @param cls closure with our configuration
689  * @param result GNUNET_YES if the namestore service is running
690  */
691 static void
692 testservice_task (void *cls,
693                   int result)
694 {
695   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
696
697   if (GNUNET_YES != result)
698   {
699     FPRINTF (stderr, _("Service `%s' is not running\n"), 
700              "namestore");
701     return;
702   }
703   if (NULL == keyfile)
704   {
705     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
706                                                               "ZONEKEY", &keyfile))
707     {
708       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
709                                  "gns", "ZONEKEY");
710       return;
711     }
712     fprintf (stderr,
713              _("Using default zone file `%s'\n"),
714              keyfile);
715   }
716   keygen = GNUNET_CRYPTO_ecc_key_create_start (keyfile,
717                                                &key_generation_cb, (void *) cfg);
718   GNUNET_free (keyfile);
719   keyfile = NULL;
720   if (NULL == keygen)
721   {
722     GNUNET_SCHEDULER_shutdown ();
723     ret = 1;
724   }
725 }
726
727
728 /**
729  * Main function that will be run.
730  *
731  * @param cls closure
732  * @param args remaining command-line arguments
733  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
734  * @param cfg configuration
735  */
736 static void
737 run (void *cls, char *const *args, const char *cfgfile,
738      const struct GNUNET_CONFIGURATION_Handle *cfg)
739 {
740
741   if ( (NULL != args[0]) && (NULL == uri) )
742     uri = GNUNET_strdup (args[0]);
743
744   GNUNET_CLIENT_service_test ("namestore", cfg,
745                               GNUNET_TIME_UNIT_SECONDS,
746                               &testservice_task,
747                               (void *) cfg);
748 }
749
750
751 /**
752  * The main function for gnunet-namestore.
753  *
754  * @param argc number of arguments from the command line
755  * @param argv command line arguments
756  * @return 0 ok, 1 on error
757  */
758 int
759 main (int argc, char *const *argv)
760 {
761   nonauthority = -1;
762   public = -1;
763
764   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
765     {'a', "add", NULL,
766      gettext_noop ("add record"), 0,
767      &GNUNET_GETOPT_set_one, &add},
768     {'d', "delete", NULL,
769      gettext_noop ("delete record"), 0,
770      &GNUNET_GETOPT_set_one, &del},   
771     {'D', "display", NULL,
772      gettext_noop ("display records"), 0,
773      &GNUNET_GETOPT_set_one, &list},   
774     {'e', "expiration", "TIME",
775      gettext_noop ("expiration time for record to use (for adding only), \"never\" is possible"), 1,
776      &GNUNET_GETOPT_set_string, &expirationstring},   
777     {'m', "monitor", NULL,
778      gettext_noop ("monitor changes in the namestore"), 0,
779      &GNUNET_GETOPT_set_one, &monitor},   
780     {'n', "name", "NAME",
781      gettext_noop ("name of the record to add/delete/display"), 1,
782      &GNUNET_GETOPT_set_string, &name},   
783     {'t', "type", "TYPE",
784      gettext_noop ("type of the record to add/delete/display"), 1,
785      &GNUNET_GETOPT_set_string, &typestring},   
786     {'u', "uri", "URI",
787      gettext_noop ("URI to import into our zone"), 1,
788      &GNUNET_GETOPT_set_string, &uri},   
789     {'V', "value", "VALUE",
790      gettext_noop ("value of the record to add/delete"), 1,
791      &GNUNET_GETOPT_set_string, &value},   
792     {'p', "public", NULL,
793      gettext_noop ("create or list public record"), 0,
794      &GNUNET_GETOPT_set_one, &public},
795     {'N', "non-authority", NULL,
796      gettext_noop ("create or list non-authority record"), 0,
797      &GNUNET_GETOPT_set_one, &nonauthority},
798     {'z', "zonekey", "FILENAME",
799      gettext_noop ("filename with the zone key"), 1,
800      &GNUNET_GETOPT_set_string, &keyfile},   
801     GNUNET_GETOPT_OPTION_END
802   };
803
804   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
805     return 2;
806
807   GNUNET_log_setup ("gnunet-namestore", "WARNING", NULL);
808   if (GNUNET_OK !=
809       GNUNET_PROGRAM_run (argc, argv, "gnunet-namestore",
810                           _("GNUnet zone manipulation tool"), 
811                           options,
812                           &run, NULL))
813   {
814     GNUNET_free ((void*) argv);
815     return 1;
816   }
817   GNUNET_free ((void*) argv);
818   return ret;
819 }
820
821 /* end of gnunet-namestore.c */