Close file descriptor on error handling.
[oweals/gnunet.git] / src / zonemaster / gnunet-service-zonemaster.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012, 2013, 2014, 2017 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file zonemaster/gnunet-service-zonemaster.c
23  * @brief publish records from namestore to GNUnet name system
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_dnsparser_lib.h"
29 #include "gnunet_dht_service.h"
30 #include "gnunet_namestore_service.h"
31 #include "gnunet_statistics_service.h"
32 #include "gnunet_namestore_plugin.h"
33 #include "gnunet_signatures.h"
34
35
36 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
37
38
39
40 /**
41  * The initial interval in milliseconds btween puts in
42  * a zone iteration
43  */
44 #define INITIAL_PUT_INTERVAL GNUNET_TIME_UNIT_MILLISECONDS
45
46 /**
47  * The lower bound for the zone iteration interval
48  */
49 #define MINIMUM_ZONE_ITERATION_INTERVAL GNUNET_TIME_UNIT_SECONDS
50
51 /**
52  * The upper bound for the zone iteration interval
53  */
54 #define MAXIMUM_ZONE_ITERATION_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
55
56 /**
57  * The default put interval for the zone iteration. In case
58  * no option is found
59  */
60 #define DEFAULT_ZONE_PUBLISH_TIME_WINDOW GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
61
62 /**
63  * The factor the current zone iteration interval is divided by for each
64  * additional new record
65  */
66 #define LATE_ITERATION_SPEEDUP_FACTOR 2
67
68 /**
69  * How long until a DHT PUT attempt should time out?
70  */
71 #define DHT_OPERATION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
72
73 /**
74  * What replication level do we use for DHT PUT operations?
75  */
76 #define DHT_GNS_REPLICATION_LEVEL 5
77
78
79 /**
80  * Handle for DHT PUT activity triggered from the namestore monitor.
81  */
82 struct MonitorActivity
83 {
84   /**
85    * Kept in a DLL.
86    */
87   struct MonitorActivity *next;
88
89   /**
90    * Kept in a DLL.
91    */
92   struct MonitorActivity *prev;
93
94   /**
95    * Handle for the DHT PUT operation.
96    */
97   struct GNUNET_DHT_PutHandle *ph;
98 };
99
100
101 /**
102  * Handle to the statistics service
103  */
104 static struct GNUNET_STATISTICS_Handle *statistics;
105
106 /**
107  * Our handle to the DHT
108  */
109 static struct GNUNET_DHT_Handle *dht_handle;
110
111 /**
112  * Active DHT put operation (or NULL)
113  */
114 static struct GNUNET_DHT_PutHandle *active_put;
115
116 /**
117  * Our handle to the namestore service
118  */
119 static struct GNUNET_NAMESTORE_Handle *namestore_handle;
120
121 /**
122  * Handle to iterate over our authoritative zone in namestore
123  */
124 static struct GNUNET_NAMESTORE_ZoneIterator *namestore_iter;
125
126 /**
127  * Handle to monitor namestore changes to instant propagation.
128  */
129 static struct GNUNET_NAMESTORE_ZoneMonitor *zmon;
130
131 /**
132  * Head of monitor activities; kept in a DLL.
133  */
134 static struct MonitorActivity *ma_head;
135
136 /**
137  * Tail of monitor activities; kept in a DLL.
138  */
139 static struct MonitorActivity *ma_tail;
140
141 /**
142  * Useful for zone update for DHT put
143  */
144 static unsigned long long num_public_records;
145
146 /**
147  * Last seen record count
148  */
149 static unsigned long long last_num_public_records;
150
151 /**
152  * Minimum relative expiration time of records seem during the current
153  * zone iteration.
154  */
155 static struct GNUNET_TIME_Relative min_relative_record_time;
156
157 /**
158  * Zone iteration PUT interval.
159  */
160 static struct GNUNET_TIME_Relative put_interval;
161
162 /**
163  * Default time window for zone iteration
164  */
165 static struct GNUNET_TIME_Relative zone_publish_time_window_default;
166
167 /**
168  * Time window for zone iteration, adjusted based on relative record
169  * expiration times in our zone.
170  */
171 static struct GNUNET_TIME_Relative zone_publish_time_window;
172
173 /**
174  * zone publish task
175  */
176 static struct GNUNET_SCHEDULER_Task *zone_publish_task;
177
178 /**
179  * #GNUNET_YES if zone has never been published before
180  */
181 static int first_zone_iteration;
182
183 /**
184  * Task run during shutdown.
185  *
186  * @param cls unused
187  * @param tc unused
188  */
189 static void
190 shutdown_task (void *cls)
191 {
192   struct MonitorActivity *ma;
193
194   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
195               "Shutting down!\n");
196   while (NULL != (ma = ma_head))
197   {
198     GNUNET_DHT_put_cancel (ma->ph);
199     GNUNET_CONTAINER_DLL_remove (ma_head,
200                                  ma_tail,
201                                  ma);
202     GNUNET_free (ma);
203   }
204   if (NULL != statistics)
205   {
206     GNUNET_STATISTICS_destroy (statistics,
207                                GNUNET_NO);
208     statistics = NULL;
209   }
210   if (NULL != zone_publish_task)
211   {
212     GNUNET_SCHEDULER_cancel (zone_publish_task);
213     zone_publish_task = NULL;
214   }
215   if (NULL != namestore_iter)
216   {
217     GNUNET_NAMESTORE_zone_iteration_stop (namestore_iter);
218     namestore_iter = NULL;
219   }
220   if (NULL != zmon)
221   {
222     GNUNET_NAMESTORE_zone_monitor_stop (zmon);
223     zmon = NULL;
224   }
225   if (NULL != namestore_handle)
226   {
227     GNUNET_NAMESTORE_disconnect (namestore_handle);
228     namestore_handle = NULL;
229   }
230   if (NULL != active_put)
231   {
232     GNUNET_DHT_put_cancel (active_put);
233     active_put = NULL;
234   }
235   if (NULL != dht_handle)
236   {
237     GNUNET_DHT_disconnect (dht_handle);
238     dht_handle = NULL;
239   }
240 }
241
242
243 /**
244  * Method called periodically that triggers iteration over authoritative records
245  *
246  * @param cls closure
247  */
248 static void
249 publish_zone_dht_next (void *cls)
250 {
251   zone_publish_task = NULL;
252   GNUNET_assert (NULL != namestore_iter);
253   GNUNET_NAMESTORE_zone_iterator_next (namestore_iter);
254 }
255
256
257 /**
258  * Periodically iterate over our zone and store everything in dht
259  *
260  * @param cls NULL
261  */
262 static void
263 publish_zone_dht_start (void *cls);
264
265
266 /**
267  * Continuation called from DHT once the PUT operation is done.
268  *
269  * @param cls closure, NULL if called from regular iteration,
270  *        `struct MonitorActivity` if called from #handle_monitor_event.
271  * @param success #GNUNET_OK on success
272  */
273 static void
274 dht_put_continuation (void *cls,
275                       int success)
276 {
277   struct MonitorActivity *ma = cls;
278   struct GNUNET_TIME_Relative next_put_interval;
279
280   num_public_records++;
281   if (NULL == ma)
282   {
283     active_put = NULL;
284     if ( (num_public_records > last_num_public_records) &&
285          (GNUNET_NO == first_zone_iteration) )
286     {
287       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
288                   "Last record count was lower than current record count.  Reducing interval.\n");
289       put_interval = GNUNET_TIME_relative_divide (zone_publish_time_window,
290                                                   num_public_records);
291       next_put_interval = GNUNET_TIME_relative_divide (put_interval,
292                                                        LATE_ITERATION_SPEEDUP_FACTOR);
293     }
294     else
295       next_put_interval = put_interval;
296     next_put_interval = GNUNET_TIME_relative_min (next_put_interval,
297                                                   MAXIMUM_ZONE_ITERATION_INTERVAL);
298     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
299                 "PUT complete, next PUT in %s!\n",
300                 GNUNET_STRINGS_relative_time_to_string (next_put_interval,
301                                                         GNUNET_YES));
302
303     GNUNET_STATISTICS_set (statistics,
304                            "Current zone iteration interval (ms)",
305                            next_put_interval.rel_value_us / 1000LL,
306                            GNUNET_NO);
307     GNUNET_assert (NULL == zone_publish_task);
308     zone_publish_task = GNUNET_SCHEDULER_add_delayed (next_put_interval,
309                                                       &publish_zone_dht_next,
310                                                       NULL);
311   }
312   else
313   {
314     GNUNET_CONTAINER_DLL_remove (ma_head,
315                                  ma_tail,
316                                  ma);
317     GNUNET_free (ma);
318   }
319 }
320
321
322 /**
323  * Convert namestore records from the internal format to that
324  * suitable for publication (removes private records, converts
325  * to absolute expiration time).
326  *
327  * @param rd input records
328  * @param rd_count size of the @a rd and @a rd_public arrays
329  * @param rd_public where to write the converted records
330  * @return number of records written to @a rd_public
331  */
332 static unsigned int
333 convert_records_for_export (const struct GNUNET_GNSRECORD_Data *rd,
334                             unsigned int rd_count,
335                             struct GNUNET_GNSRECORD_Data *rd_public)
336 {
337   struct GNUNET_TIME_Absolute now;
338   unsigned int rd_public_count;
339   unsigned int i;
340
341   rd_public_count = 0;
342   now = GNUNET_TIME_absolute_get ();
343   for (i=0;i<rd_count;i++)
344     if (0 == (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE))
345     {
346       rd_public[rd_public_count] = rd[i];
347       if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
348       {
349         /* GNUNET_GNSRECORD_block_create will convert to absolute time;
350            we just need to adjust our iteration frequency */
351         min_relative_record_time.rel_value_us =
352           GNUNET_MIN (rd_public[rd_public_count].expiration_time,
353                       min_relative_record_time.rel_value_us);
354       }
355       else if (rd_public[rd_public_count].expiration_time < now.abs_value_us)
356       {
357         /* record already expired, skip it */
358         continue;
359       }
360       rd_public_count++;
361     }
362   return rd_public_count;
363 }
364
365
366 /**
367  * Store GNS records in the DHT.
368  *
369  * @param key key of the zone
370  * @param label label to store under
371  * @param rd_public public record data
372  * @param rd_public_count number of records in @a rd_public
373  * @param pc_arg closure argument to pass to the #dht_put_continuation
374  * @return DHT PUT handle, NULL on error
375  */
376 static struct GNUNET_DHT_PutHandle *
377 perform_dht_put (const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
378                  const char *label,
379                  const struct GNUNET_GNSRECORD_Data *rd_public,
380                  unsigned int rd_public_count,
381                  void *pc_arg)
382 {
383   struct GNUNET_GNSRECORD_Block *block;
384   struct GNUNET_HashCode query;
385   struct GNUNET_TIME_Absolute expire;
386   size_t block_size;
387   struct GNUNET_DHT_PutHandle *ret;
388
389   expire = GNUNET_GNSRECORD_record_get_expiration_time (rd_public_count,
390                                                         rd_public);
391   block = GNUNET_GNSRECORD_block_create (key,
392                                          expire,
393                                          label,
394                                          rd_public,
395                                          rd_public_count);
396   if (NULL == block)
397     return NULL; /* whoops */
398   block_size = ntohl (block->purpose.size)
399     + sizeof (struct GNUNET_CRYPTO_EcdsaSignature)
400     + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
401   GNUNET_GNSRECORD_query_from_private_key (key,
402                                            label,
403                                            &query);
404   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
405               "Storing %u record(s) for label `%s' in DHT with expiration `%s' under key %s\n",
406               rd_public_count,
407               label,
408               GNUNET_STRINGS_absolute_time_to_string (expire),
409               GNUNET_h2s (&query));
410   ret = GNUNET_DHT_put (dht_handle,
411                         &query,
412                         DHT_GNS_REPLICATION_LEVEL,
413                         GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
414                         GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
415                         block_size,
416                         block,
417                         expire,
418                         &dht_put_continuation,
419                         pc_arg);
420   GNUNET_free (block);
421   return ret;
422 }
423
424
425 /**
426  * We encountered an error in our zone iteration.
427  */
428 static void
429 zone_iteration_error (void *cls)
430 {
431   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
432               "Got disconnected from namestore database, retrying.\n");
433   namestore_iter = NULL;
434   /* We end up here on error/disconnect/shutdown, so potentially
435      while a zone publish task or a DHT put is still running; hence
436      we need to cancel those. */
437   if (NULL != zone_publish_task)
438   {
439     GNUNET_SCHEDULER_cancel (zone_publish_task);
440     zone_publish_task = NULL;
441   }
442   if (NULL != active_put)
443   {
444     GNUNET_DHT_put_cancel (active_put);
445     active_put = NULL;
446   }
447   zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_start,
448                                                 NULL);
449 }
450
451
452 /**
453  * Zone iteration is completed.
454  */
455 static void
456 zone_iteration_finished (void *cls)
457 {
458   /* we're done with one iteration, calculate when to do the next one */
459   namestore_iter = NULL;
460   last_num_public_records = num_public_records;
461   first_zone_iteration = GNUNET_NO;
462   if (0 == num_public_records)
463   {
464     /**
465      * If no records are known (startup) or none present
466      * we can safely set the interval to the value for a single
467      * record
468      */
469     put_interval = zone_publish_time_window;
470     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
471                 "No records in namestore database.\n");
472   }
473   else
474   {
475     /* If records are present, next publication is based on the minimum
476      * relative expiration time of the records published divided by 4
477      */
478     zone_publish_time_window
479       = GNUNET_TIME_relative_min (GNUNET_TIME_relative_divide (min_relative_record_time, 4),
480                                   zone_publish_time_window_default);
481     put_interval = GNUNET_TIME_relative_divide (zone_publish_time_window,
482                                                 num_public_records);
483   }
484   /* reset for next iteration */
485   min_relative_record_time = GNUNET_TIME_UNIT_FOREVER_REL;
486   put_interval = GNUNET_TIME_relative_max (MINIMUM_ZONE_ITERATION_INTERVAL,
487                                            put_interval);
488   put_interval = GNUNET_TIME_relative_min (put_interval,
489                                            MAXIMUM_ZONE_ITERATION_INTERVAL);
490   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
491               "Zone iteration finished. Adjusted zone iteration interval to %s\n",
492               GNUNET_STRINGS_relative_time_to_string (put_interval,
493                                                       GNUNET_YES));
494   GNUNET_STATISTICS_set (statistics,
495                          "Current zone iteration interval (in ms)",
496                          put_interval.rel_value_us / 1000LL,
497                          GNUNET_NO);
498   GNUNET_STATISTICS_update (statistics,
499                             "Number of zone iterations",
500                             1,
501                             GNUNET_NO);
502   GNUNET_STATISTICS_set (statistics,
503                          "Number of public records in DHT",
504                          last_num_public_records,
505                          GNUNET_NO);
506   GNUNET_assert (NULL == zone_publish_task);
507   if (0 == num_public_records)
508     zone_publish_task = GNUNET_SCHEDULER_add_delayed (put_interval,
509                                                       &publish_zone_dht_start,
510                                                       NULL);
511   else
512     zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_start,
513                                                   NULL);
514 }
515
516
517 /**
518  * Function used to put all records successively into the DHT.
519  *
520  * @param cls the closure (NULL)
521  * @param key the private key of the authority (ours)
522  * @param label the name of the records, NULL once the iteration is done
523  * @param rd_count the number of records in @a rd
524  * @param rd the record data
525  */
526 static void
527 put_gns_record (void *cls,
528                 const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
529                 const char *label,
530                 unsigned int rd_count,
531                 const struct GNUNET_GNSRECORD_Data *rd)
532 {
533   struct GNUNET_GNSRECORD_Data rd_public[rd_count];
534   unsigned int rd_public_count;
535
536   rd_public_count = convert_records_for_export (rd,
537                                                 rd_count,
538                                                 rd_public);
539
540   if (0 == rd_public_count)
541   {
542     GNUNET_assert (NULL == zone_publish_task);
543     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
544                 "Record set empty, moving to next record set\n");
545     zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_next,
546                                                   NULL);
547     return;
548   }
549   /* We got a set of records to publish */
550   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
551               "Starting DHT PUT\n");
552   active_put = perform_dht_put (key,
553                                 label,
554                                 rd_public,
555                                 rd_public_count,
556                                 NULL);
557   if (NULL == active_put)
558   {
559     GNUNET_break (0);
560     dht_put_continuation (NULL, GNUNET_NO);
561   }
562 }
563
564
565 /**
566  * Periodically iterate over all zones and store everything in DHT
567  *
568  * @param cls NULL
569  */
570 static void
571 publish_zone_dht_start (void *cls)
572 {
573   zone_publish_task = NULL;
574
575   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
576               "Starting DHT zone update!\n");
577   /* start counting again */
578   num_public_records = 0;
579   GNUNET_assert (NULL == namestore_iter);
580   namestore_iter
581     = GNUNET_NAMESTORE_zone_iteration_start (namestore_handle,
582                                              NULL, /* All zones */
583                                              &zone_iteration_error,
584                                              NULL,
585                                              &put_gns_record,
586                                              NULL,
587                                              &zone_iteration_finished,
588                                              NULL);
589 }
590
591
592 /**
593  * Process a record that was stored in the namestore
594  * (invoked by the monitor).
595  *
596  * @param cls closure, NULL
597  * @param zone private key of the zone; NULL on disconnect
598  * @param label label of the records; NULL on disconnect
599  * @param rd_count number of entries in @a rd array, 0 if label was deleted
600  * @param rd array of records with data to store
601  */
602 static void
603 handle_monitor_event (void *cls,
604                       const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
605                       const char *label,
606                       unsigned int rd_count,
607                       const struct GNUNET_GNSRECORD_Data *rd)
608 {
609   struct GNUNET_GNSRECORD_Data rd_public[rd_count];
610   unsigned int rd_public_count;
611   struct MonitorActivity *ma;
612
613   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
614               "Received %u records for label `%s' via namestore monitor\n",
615               rd_count,
616               label);
617   /* filter out records that are not public, and convert to
618      absolute expiration time. */
619   rd_public_count = convert_records_for_export (rd,
620                                                 rd_count,
621                                                 rd_public);
622   if (0 == rd_public_count)
623     return; /* nothing to do */
624   ma = GNUNET_new (struct MonitorActivity);
625   ma->ph = perform_dht_put (zone,
626                             label,
627                             rd,
628                             rd_count,
629                             ma);
630   if (NULL == ma->ph)
631   {
632     /* PUT failed, do not remember operation */
633     GNUNET_free (ma);
634     return;
635   }
636   GNUNET_CONTAINER_DLL_insert (ma_head,
637                                ma_tail,
638                                ma);
639 }
640
641
642 /**
643  * The zone monitor is now in SYNC with the current state of the
644  * name store.  Start to perform periodic iterations.
645  *
646  * @param cls NULL
647  */
648 static void
649 monitor_sync_event (void *cls)
650 {
651   if ( (NULL == zone_publish_task) &&
652        (NULL == namestore_iter) )
653   zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_start,
654                                                 NULL);
655 }
656
657
658 /**
659  * The zone monitor is now in SYNC with the current state of the
660  * name store.  Start to perform periodic iterations.
661  *
662  * @param cls NULL
663  */
664 static void
665 handle_monitor_error (void *cls)
666 {
667   if (NULL != zone_publish_task)
668   {
669     GNUNET_SCHEDULER_cancel (zone_publish_task);
670     zone_publish_task = NULL;
671   }
672   if (NULL != namestore_iter)
673   {
674     GNUNET_NAMESTORE_zone_iteration_stop (namestore_iter);
675     namestore_iter = NULL;
676   }
677   if (NULL != active_put)
678   {
679     GNUNET_DHT_put_cancel (active_put);
680     active_put = NULL;
681   }
682   zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_start,
683                                                 NULL);
684 }
685
686
687 /**
688  * Performe zonemaster duties: watch namestore, publish records.
689  *
690  * @param cls closure
691  * @param server the initialized server
692  * @param c configuration to use
693  */
694 static void
695 run (void *cls,
696      const struct GNUNET_CONFIGURATION_Handle *c,
697      struct GNUNET_SERVICE_Handle *service)
698 {
699   unsigned long long max_parallel_bg_queries = 128;
700
701   min_relative_record_time = GNUNET_TIME_UNIT_FOREVER_REL;
702   namestore_handle = GNUNET_NAMESTORE_connect (c);
703   if (NULL == namestore_handle)
704   {
705     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
706                 _("Failed to connect to the namestore!\n"));
707     GNUNET_SCHEDULER_shutdown ();
708     return;
709   }
710
711   put_interval = INITIAL_PUT_INTERVAL;
712   zone_publish_time_window_default = DEFAULT_ZONE_PUBLISH_TIME_WINDOW;
713   if (GNUNET_OK ==
714       GNUNET_CONFIGURATION_get_value_time (c,
715                                            "zonemaster",
716                                            "ZONE_PUBLISH_TIME_WINDOW",
717                                            &zone_publish_time_window_default))
718   {
719     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
720                 "Time window for zone iteration: %s\n",
721                 GNUNET_STRINGS_relative_time_to_string (zone_publish_time_window,
722                                                         GNUNET_YES));
723   }
724   zone_publish_time_window = zone_publish_time_window_default;
725   if (GNUNET_OK ==
726       GNUNET_CONFIGURATION_get_value_number (c,
727                                              "zonemaster",
728                                              "MAX_PARALLEL_BACKGROUND_QUERIES",
729                                              &max_parallel_bg_queries))
730   {
731     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
732                 "Number of allowed parallel background queries: %llu\n",
733                 max_parallel_bg_queries);
734   }
735   if (0 == max_parallel_bg_queries)
736     max_parallel_bg_queries = 1;          
737   dht_handle = GNUNET_DHT_connect (c,
738                                    (unsigned int) max_parallel_bg_queries);
739   if (NULL == dht_handle)
740   {
741     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
742                 _("Could not connect to DHT!\n"));
743     GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
744     return;
745   }
746
747   /* Schedule periodic put for our records. */
748   first_zone_iteration = GNUNET_YES;\
749   statistics = GNUNET_STATISTICS_create ("zonemaster", c);
750   zmon = GNUNET_NAMESTORE_zone_monitor_start (c,
751                                               NULL,
752                                               GNUNET_NO,
753                                               &handle_monitor_error,
754                                               NULL,
755                                               &handle_monitor_event,
756                                               NULL,
757                                               &monitor_sync_event,
758                                               NULL);
759   GNUNET_break (NULL != zmon);
760   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
761 }
762
763
764 /**
765  * Define "main" method using service macro.
766  */
767 GNUNET_SERVICE_MAIN
768 ("zonemaster",
769  GNUNET_SERVICE_OPTION_NONE,
770  &run,
771  NULL,
772  NULL,
773  NULL,
774  GNUNET_MQ_handler_end());
775
776
777 /* end of gnunet-service-zonemaster.c */