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