more complete DISK API
[oweals/gnunet.git] / src / peerinfo / gnunet-service-peerinfo.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2004, 2005, 2007, 2009 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 2, 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 /**
22  * @file peerinfo/gnunet-service-peerinfo.c
23  * @brief maintains list of known peers
24  *
25  * Code to maintain the list of currently known hosts (in memory
26  * structure of data/hosts/ and data/credit/).
27  *
28  * @author Christian Grothoff
29  */
30
31 #include "platform.h"
32 #include "gnunet_crypto_lib.h"
33 #include "gnunet_disk_lib.h"
34 #include "gnunet_hello_lib.h"
35 #include "gnunet_protocols.h"
36 #include "gnunet_service_lib.h"
37 #include "peerinfo.h"
38
39 /**
40  * How often do we scan the HOST_DIR for new entries?
41  */
42 #define DATA_HOST_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
43
44 /**
45  * How often do we flush trust values to disk?
46  */
47 #define TRUST_FLUSH_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
48
49 /**
50  * How often do we discard old entries in data/hosts/?
51  */
52 #define DATA_HOST_CLEAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 60)
53
54 /**
55  * In-memory cache of known hosts.
56  */
57 struct HostEntry
58 {
59
60   /**
61    * This is a linked list.
62    */
63   struct HostEntry *next;
64
65   /**
66    * Identity of the peer.
67    */
68   struct GNUNET_PeerIdentity identity;
69
70   /**
71    * Hello for the peer (can be NULL)
72    */
73   struct GNUNET_HELLO_Message *hello;
74
75   /**
76    * Trust rating for this peer
77    */
78   uint32_t trust;
79
80   /**
81    * Trust rating for this peer on disk.
82    */
83   uint32_t disk_trust;
84
85 };
86
87 /**
88  * The in-memory list of known hosts.
89  */
90 static struct HostEntry *hosts;
91
92 /**
93  * Directory where the hellos are stored in (data/hosts)
94  */
95 static char *networkIdDirectory;
96
97 /**
98  * Where do we store trust information?
99  */
100 static char *trustDirectory;
101
102
103 /**
104  * Address iterator that causes expired entries to be discarded.
105  *
106  * @param cls pointer to the current time
107  * @return GNUNET_NO if expiration smaller than the current time
108  */
109 static int
110 discard_expired (void *cls,
111                  const char *tname,
112                  struct GNUNET_TIME_Absolute expiration,
113                  const void *addr, size_t addrlen)
114 {
115   const struct GNUNET_TIME_Absolute *now = cls;
116   if (now->value > expiration.value)
117     return GNUNET_NO;
118   return GNUNET_OK;
119 }
120
121
122 /**
123  * Get the filename under which we would store the GNUNET_HELLO_Message
124  * for the given host and protocol.
125  * @return filename of the form DIRECTORY/HOSTID
126  */
127 static char *
128 get_host_filename (const struct GNUNET_PeerIdentity *id)
129 {
130   struct GNUNET_CRYPTO_HashAsciiEncoded fil;
131   char *fn;
132
133   GNUNET_CRYPTO_hash_to_enc (&id->hashPubKey, &fil);
134   GNUNET_asprintf (&fn,
135                    "%s%s%s", networkIdDirectory, DIR_SEPARATOR_STR, &fil);
136   return fn;
137 }
138
139
140 /**
141  * Get the filename under which we would store the GNUNET_HELLO_Message
142  * for the given host and protocol.
143  * @return filename of the form DIRECTORY/HOSTID
144  */
145 static char *
146 get_trust_filename (const struct GNUNET_PeerIdentity *id)
147 {
148   struct GNUNET_CRYPTO_HashAsciiEncoded fil;
149   char *fn;
150
151   GNUNET_CRYPTO_hash_to_enc (&id->hashPubKey, &fil);
152   GNUNET_asprintf (&fn, "%s%s%s", trustDirectory, DIR_SEPARATOR_STR, &fil);
153   return fn;
154 }
155
156 /**
157  * Find the host entry for the given peer.  Call
158  * only when synchronized!
159  * @return NULL if not found
160  */
161 static struct HostEntry *
162 lookup_host_entry (const struct GNUNET_PeerIdentity *id)
163 {
164   struct HostEntry *pos;
165
166   pos = hosts;
167   while ((pos != NULL) &&
168          (0 !=
169           memcmp (id, &pos->identity, sizeof (struct GNUNET_PeerIdentity))))
170     pos = pos->next;
171   return pos;
172 }
173
174
175 /**
176  * Add a host to the list.
177  *
178  * @param identity the identity of the host
179  * @param protocol the protocol for the host
180  */
181 static void
182 add_host_to_known_hosts (const struct GNUNET_PeerIdentity *identity)
183 {
184   struct HostEntry *entry;
185   char *fn;
186   uint32_t trust;
187   char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
188   const struct GNUNET_HELLO_Message *hello;
189   struct GNUNET_HELLO_Message *hello_clean;
190   int size;
191   struct GNUNET_TIME_Absolute now;
192
193   entry = lookup_host_entry (identity);
194   if (entry != NULL)
195     return;
196   entry = GNUNET_malloc (sizeof (struct HostEntry));
197   entry->identity = *identity;
198   fn = get_trust_filename (identity);
199   if ((GNUNET_DISK_file_test (fn) == GNUNET_YES) &&
200       (sizeof (trust) == GNUNET_DISK_fn_read (fn, &trust, sizeof (trust))))
201     entry->disk_trust = entry->trust = ntohl (trust);
202   GNUNET_free (fn);
203
204   fn = get_host_filename (identity);
205   if (GNUNET_DISK_file_test (fn) == GNUNET_YES)
206     {
207       size = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer));
208       hello = (const struct GNUNET_HELLO_Message *) buffer;
209       now = GNUNET_TIME_absolute_get ();
210       hello_clean = GNUNET_HELLO_iterate_addresses (hello,
211                                                     GNUNET_YES,
212                                                     &discard_expired, &now);
213       entry->hello = hello_clean;
214     }
215   GNUNET_free (fn);
216   entry->next = hosts;
217   hosts = entry;
218 }
219
220
221 /**
222  * Increase the host credit by a value.
223  *
224  * @param hostId is the identity of the host
225  * @param value is the int value by which the
226  *  host credit is to be increased or decreased
227  * @returns the actual change in trust (positive or negative)
228  */
229 static int
230 change_host_trust (const struct GNUNET_PeerIdentity *hostId, int value)
231 {
232   struct HostEntry *host;
233
234   if (value == 0)
235     return 0;
236   host = lookup_host_entry (hostId);
237   if (host == NULL)
238     {
239       add_host_to_known_hosts (hostId);
240       host = lookup_host_entry (hostId);
241     }
242   GNUNET_assert (host != NULL);
243   if (value > 0)
244     {
245       if (host->trust + value < host->trust)
246         {
247           value = ((uint32_t) - 1) - host->trust;
248           host->trust = (uint32_t) - 1; /* maximized */
249         }
250       else
251         host->trust += value;
252     }
253   else
254     {
255       if (host->trust < -value)
256         {
257           value = -host->trust;
258           host->trust = 0;
259         }
260       else
261         host->trust += value;
262     }
263   return value;
264 }
265
266
267 /**
268  * Remove a file that should not be there.  LOG
269  * success or failure.
270  */
271 static void
272 remove_garbage (const char *fullname)
273 {
274   if (0 == UNLINK (fullname))
275     GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
276                 _
277                 ("File `%s' in directory `%s' does not match naming convention. "
278                  "Removed.\n"), fullname, networkIdDirectory);
279   else
280     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR |
281                               GNUNET_ERROR_TYPE_BULK, "unlink", fullname);
282 }
283
284
285 static int
286 hosts_directory_scan_callback (void *cls, const char *fullname)
287 {
288   unsigned int *matched = cls;
289   struct GNUNET_PeerIdentity identity;
290   const char *filename;
291
292   if (GNUNET_DISK_file_test (fullname) != GNUNET_YES)
293     return GNUNET_OK;           /* ignore non-files */
294   if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))
295     {
296       remove_garbage (fullname);
297       return GNUNET_OK;
298     }
299   filename =
300     &fullname[strlen (fullname) -
301               sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) + 1];
302   if (filename[-1] != DIR_SEPARATOR)
303     {
304       remove_garbage (fullname);
305       return GNUNET_OK;
306     }
307   if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (filename,
308                                                    &identity.hashPubKey))
309     {
310       remove_garbage (fullname);
311       return GNUNET_OK;
312     }
313   (*matched)++;
314   add_host_to_known_hosts (&identity);
315   return GNUNET_OK;
316 }
317
318
319 /**
320  * Call this method periodically to scan data/hosts for new hosts.
321  */
322 static void
323 cron_scan_directory_data_hosts (void *cls,
324                                 const struct GNUNET_SCHEDULER_TaskContext *tc)
325 {
326   static unsigned int retries;
327   unsigned int count;
328
329   count = 0;
330   GNUNET_DISK_directory_scan (networkIdDirectory,
331                               &hosts_directory_scan_callback, &count);
332   if ((0 == count) && (0 == (++retries & 31)))
333     GNUNET_log (GNUNET_ERROR_TYPE_WARNING |
334                 GNUNET_ERROR_TYPE_BULK,
335                 _("Still no peers found in `%s'!\n"), networkIdDirectory);
336   GNUNET_SCHEDULER_add_delayed (tc->sched,
337                                 GNUNET_NO,
338                                 GNUNET_SCHEDULER_PRIORITY_KEEP,
339                                 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
340                                 DATA_HOST_FREQ,
341                                 &cron_scan_directory_data_hosts, NULL);
342 }
343
344
345 /**
346  * Bind a host address (hello) to a hostId.
347  *
348  * @param peer the peer for which this is a hello
349  * @param hello the verified (!) hello message
350  */
351 static void
352 bind_address (const struct GNUNET_PeerIdentity *peer,
353               const struct GNUNET_HELLO_Message *hello)
354 {
355   char *fn;
356   struct HostEntry *host;
357   struct GNUNET_HELLO_Message *mrg;
358
359   add_host_to_known_hosts (peer);
360   host = lookup_host_entry (peer);
361   GNUNET_assert (host != NULL);
362   if (host->hello == NULL)
363     {
364       host->hello = GNUNET_malloc (GNUNET_HELLO_size (hello));
365       memcpy (host->hello, hello, GNUNET_HELLO_size (hello));
366     }
367   else
368     {
369       mrg = GNUNET_HELLO_merge (host->hello, hello);
370       GNUNET_free (host->hello);
371       host->hello = mrg;
372     }
373   fn = get_host_filename (peer);
374   GNUNET_DISK_fn_write (fn, host->hello, GNUNET_HELLO_size (hello),
375       GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
376           | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ);
377   GNUNET_free (fn);
378 }
379
380
381 /**
382  * Do transmit info either for only the host matching the given
383  * argument or for all known hosts and change their trust values by
384  * the given delta.
385  *
386  * @param only NULL to hit all hosts
387  */
388 static void
389 send_to_each_host (const struct GNUNET_PeerIdentity *only,
390                    int trust_change, struct GNUNET_SERVER_Client *client)
391 {
392   struct HostEntry *pos;
393   struct InfoMessage *im;
394   const struct GNUNET_MessageHeader *end;
395   uint16_t hs;
396   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
397   struct GNUNET_SERVER_TransmitContext *tc;
398
399   tc = GNUNET_SERVER_transmit_context_create (client);
400   pos = hosts;
401   while (pos != NULL)
402     {
403       if ((only == NULL) ||
404           (0 ==
405            memcmp (only, &pos->identity,
406                    sizeof (struct GNUNET_PeerIdentity))))
407         {
408           change_host_trust (&pos->identity, trust_change);
409           hs = 0;
410           im = (struct InfoMessage *) buf;
411           if (pos->hello != NULL)
412             {
413               hs = GNUNET_HELLO_size (pos->hello);
414               GNUNET_assert (hs <
415                              GNUNET_SERVER_MAX_MESSAGE_SIZE -
416                              sizeof (struct InfoMessage));
417               memcpy (&im[1], pos->hello, hs);
418             }
419           im->trust = htonl (pos->trust);
420           im->peer = pos->identity;
421           end = &im->header;
422           GNUNET_SERVER_transmit_context_append (tc,
423                                                  &end[1],
424                                                  hs +
425                                                  sizeof (struct InfoMessage) -
426                                                  sizeof (struct
427                                                          GNUNET_MessageHeader),
428                                                  GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
429         }
430       pos = pos->next;
431     }
432   GNUNET_SERVER_transmit_context_append (tc, NULL, 0,
433                                          GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
434   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
435 }
436
437
438 /**
439  * Write host-trust information to a file - flush the buffer entry!
440  * Assumes synchronized access.
441  */
442 static void
443 flush_trust (struct HostEntry *host)
444 {
445   char *fn;
446   uint32_t trust;
447
448   if (host->trust == host->disk_trust)
449     return;                     /* unchanged */
450   fn = get_trust_filename (&host->identity);
451   if (host->trust == 0)
452     {
453       if ((0 != UNLINK (fn)) && (errno != ENOENT))
454         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
455                                   GNUNET_ERROR_TYPE_BULK, "unlink", fn);
456     }
457   else
458     {
459       trust = htonl (host->trust);
460       if (GNUNET_OK == GNUNET_DISK_fn_write (fn, &trust, sizeof(uint32_t),
461           GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
462               | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ))
463         host->disk_trust = host->trust;
464     }
465   GNUNET_free (fn);
466 }
467
468 /**
469  * Call this method periodically to scan data/hosts for new hosts.
470  */
471 static void
472 cron_flush_trust (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
473 {
474   struct HostEntry *pos;
475
476   pos = hosts;
477   while (pos != NULL)
478     {
479       flush_trust (pos);
480       pos = pos->next;
481     }
482   if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
483     GNUNET_SCHEDULER_add_delayed (tc->sched,
484                                   GNUNET_YES,
485                                   GNUNET_SCHEDULER_PRIORITY_KEEP,
486                                   GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
487                                   TRUST_FLUSH_FREQ, &cron_flush_trust, NULL);
488 }
489
490
491 /**
492  * @brief delete expired HELLO entries in data/hosts/
493  */
494 static int
495 discard_hosts_helper (void *cls, const char *fn)
496 {
497   struct GNUNET_TIME_Absolute *now = cls;
498   char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
499   const struct GNUNET_HELLO_Message *hello;
500   struct GNUNET_HELLO_Message *new_hello;
501   int size;
502
503   size = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer));
504   if ((size < sizeof (struct GNUNET_MessageHeader)) && (0 != UNLINK (fn)))
505     {
506       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
507                                 GNUNET_ERROR_TYPE_BULK, "unlink", fn);
508       return GNUNET_OK;
509     }
510   hello = (const struct GNUNET_HELLO_Message *) buffer;
511   new_hello = GNUNET_HELLO_iterate_addresses (hello,
512                                               GNUNET_YES,
513                                               &discard_expired, now);
514   if ((new_hello == NULL) && (0 != UNLINK (fn)))
515     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
516                               GNUNET_ERROR_TYPE_BULK, "unlink", fn);
517   if (new_hello != NULL)
518     {
519       GNUNET_DISK_fn_write (fn, new_hello, GNUNET_HELLO_size (new_hello),
520           GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
521               | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ);
522       GNUNET_free (new_hello);
523     }
524   return GNUNET_OK;
525 }
526
527
528 /**
529  * Call this method periodically to scan data/hosts for new hosts.
530  */
531 static void
532 cron_clean_data_hosts (void *cls,
533                        const struct GNUNET_SCHEDULER_TaskContext *tc)
534 {
535   struct GNUNET_TIME_Absolute now;
536
537   now = GNUNET_TIME_absolute_get ();
538   GNUNET_DISK_directory_scan (networkIdDirectory,
539                               &discard_hosts_helper, &now);
540
541   GNUNET_SCHEDULER_add_delayed (tc->sched,
542                                 GNUNET_NO,
543                                 GNUNET_SCHEDULER_PRIORITY_KEEP,
544                                 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
545                                 DATA_HOST_CLEAN_FREQ,
546                                 &cron_clean_data_hosts, NULL);
547 }
548
549
550 /**
551  * Handle ADD-message.
552  *
553  * @param cls closure
554  * @param client identification of the client
555  * @param message the actual message
556  */
557 static void
558 handle_add (void *cls,
559             struct GNUNET_SERVER_Client *client,
560             const struct GNUNET_MessageHeader *message)
561 {
562   const struct PeerAddMessage *pam;
563   const struct GNUNET_MessageHeader *hello;
564   uint16_t size;
565
566   size = ntohs (message->size);
567   if (size <
568       sizeof (struct PeerAddMessage) + sizeof (struct GNUNET_MessageHeader))
569     {
570       GNUNET_break (0);
571       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
572       return;
573     }
574   pam = (const struct PeerAddMessage *) message;
575   hello = (const struct GNUNET_MessageHeader *) &pam[1];
576   if (size != sizeof (struct PeerAddMessage) + ntohs (hello->size))
577     {
578       GNUNET_break (0);
579       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
580       return;
581     }
582   bind_address (&pam->peer, (const struct GNUNET_HELLO_Message *) hello);
583   GNUNET_SERVER_receive_done (client, GNUNET_OK);
584 }
585
586
587 /**
588  * Handle GET-message.
589  *
590  * @param cls closure
591  * @param client identification of the client
592  * @param message the actual message
593  */
594 static void
595 handle_get (void *cls,
596             struct GNUNET_SERVER_Client *client,
597             const struct GNUNET_MessageHeader *message)
598 {
599   const struct ListPeerMessage *lpm;
600
601   lpm = (const struct ListPeerMessage *) message;
602   send_to_each_host (&lpm->peer, ntohl (lpm->trust_change), client);
603 }
604
605
606 /**
607  * Handle GET-ALL-message.
608  *
609  * @param cls closure
610  * @param client identification of the client
611  * @param message the actual message
612  */
613 static void
614 handle_get_all (void *cls,
615                 struct GNUNET_SERVER_Client *client,
616                 const struct GNUNET_MessageHeader *message)
617 {
618   const struct ListAllPeersMessage *lpm;
619
620   lpm = (const struct ListAllPeersMessage *) message;
621   send_to_each_host (NULL, ntohl (lpm->trust_change), client);
622 }
623
624
625 /**
626  * List of handlers for the messages understood by this
627  * service.
628  */
629 static struct GNUNET_SERVER_MessageHandler handlers[] = {
630   {&handle_add, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_ADD, 0},
631   {&handle_get, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET,
632    sizeof (struct ListPeerMessage)},
633   {&handle_get_all, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
634    sizeof (struct ListAllPeersMessage)},
635   {NULL, NULL, 0, 0}
636 };
637
638
639 /**
640  * Process statistics requests.
641  *
642  * @param cls closure
643  * @param sched scheduler to use
644  * @param server the initialized server
645  * @param cfg configuration to use
646  */
647 static void
648 run (void *cls,
649      struct GNUNET_SCHEDULER_Handle *sched,
650      struct GNUNET_SERVER_Handle *server,
651      struct GNUNET_CONFIGURATION_Handle *cfg)
652 {
653   GNUNET_assert (GNUNET_OK ==
654                  GNUNET_CONFIGURATION_get_value_filename (cfg,
655                                                           "peerinfo",
656                                                           "HOSTS",
657                                                           &networkIdDirectory));
658   GNUNET_assert (GNUNET_OK ==
659                  GNUNET_CONFIGURATION_get_value_filename (cfg,
660                                                           "peerinfo",
661                                                           "TRUST",
662                                                           &trustDirectory));
663   GNUNET_DISK_directory_create (networkIdDirectory);
664   GNUNET_DISK_directory_create (trustDirectory);
665   GNUNET_SCHEDULER_add_delayed (sched,
666                                 GNUNET_NO,
667                                 GNUNET_SCHEDULER_PRIORITY_IDLE,
668                                 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
669                                 GNUNET_TIME_UNIT_MILLISECONDS,
670                                 &cron_scan_directory_data_hosts, NULL);
671   GNUNET_SCHEDULER_add_delayed (sched,
672                                 GNUNET_YES,
673                                 GNUNET_SCHEDULER_PRIORITY_HIGH,
674                                 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
675                                 TRUST_FLUSH_FREQ, &cron_flush_trust, NULL);
676   GNUNET_SCHEDULER_add_delayed (sched,
677                                 GNUNET_NO,
678                                 GNUNET_SCHEDULER_PRIORITY_IDLE,
679                                 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
680                                 DATA_HOST_CLEAN_FREQ,
681                                 &cron_clean_data_hosts, NULL);
682   GNUNET_SERVER_add_handlers (server, handlers);
683 }
684
685
686 /**
687  * The main function for the statistics service.
688  *
689  * @param argc number of arguments from the command line
690  * @param argv command line arguments
691  * @return 0 ok, 1 on error
692  */
693 int
694 main (int argc, char *const *argv)
695 {
696   return (GNUNET_OK ==
697           GNUNET_SERVICE_run (argc,
698                               argv,
699                               "peerinfo", &run, NULL, NULL, NULL)) ? 0 : 1;
700 }
701
702
703 /* end of gnunet-service-peerinfo.c */