starting with cleaning up gnunet-service-ats_preferences.c
[oweals/gnunet.git] / src / ats / gnunet-service-ats_preferences.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011-2015 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 ats/gnunet-service-ats_preferences.c
22  * @brief manage preferences expressed by clients
23  * @author Matthias Wachs
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet-service-ats.h"
28 #include "gnunet-service-ats_addresses.h"
29 #include "gnunet-service-ats_performance.h"
30 #include "gnunet-service-ats_plugins.h"
31 #include "gnunet-service-ats_preferences.h"
32 #include "gnunet-service-ats_reservations.h"
33 #include "ats.h"
34
35 #define LOG(kind,...) GNUNET_log_from (kind, "ats-preferencesx",__VA_ARGS__)
36
37 /**
38  *
39  */
40 #define PREF_AGING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
41
42 /**
43  *
44  */
45 #define PREF_AGING_FACTOR 0.95
46
47 /**
48  *
49  */
50 #define PREF_EPSILON 0.01
51
52
53 /**
54  * Relative preferences for a peer
55  */
56 struct PeerRelative
57 {
58   /**
59    * Relative preference values
60    */
61   double f_rel[GNUNET_ATS_PreferenceCount];
62
63   /**
64    * Peer identity for which we have these preferences.
65    */
66   struct GNUNET_PeerIdentity id;
67 };
68
69
70 /**
71  * Default values
72  */
73 static struct PeerRelative defvalues;
74
75
76 /**
77  * Preference client, information we keep track of per client.
78  */
79 struct PreferenceClient
80 {
81   /**
82    * Previous in DLL
83    */
84   struct PreferenceClient *prev;
85
86   /**
87    * Next in DLL
88    */
89   struct PreferenceClient *next;
90
91   /**
92    * Head of peer list
93    */
94   struct PreferencePeer *p_head;
95
96   /**
97    * Tail of peer list
98    */
99   struct PreferencePeer *p_tail;
100
101   /**
102    * Client handle
103    */
104   struct GNUNET_SERVER_Client *client;
105
106   /**
107    * Array of sum of absolute preferences for this client
108    */
109   double f_abs_sum[GNUNET_ATS_PreferenceCount];
110
111   /**
112    * Array of sum of relative preferences for this client
113    */
114   double f_rel_sum[GNUNET_ATS_PreferenceCount];
115 };
116
117
118 /**
119  * Preference information per peer and client.
120  */
121 struct PreferencePeer
122 {
123   /**
124    * Next in DLL of preference entries for the same client.
125    */
126   struct PreferencePeer *next;
127
128   /**
129    * Previous in DLL of preference entries for the same client.
130    */
131   struct PreferencePeer *prev;
132
133   /**
134    * Client that expressed this preferences.
135    */
136   struct PreferenceClient *client;
137
138   /**
139    * Peer identity for which the preference was expressed.
140    */
141   struct GNUNET_PeerIdentity id;
142
143   /**
144    * Absolute preference values for all preference types
145    */
146   double f_abs[GNUNET_ATS_PreferenceCount];
147
148   /**
149    * Relative preference values for all preference types
150    */
151   double f_rel[GNUNET_ATS_PreferenceCount];
152
153   /**
154    * Absolute point of time of next aging process
155    */
156   struct GNUNET_TIME_Absolute next_aging[GNUNET_ATS_PreferenceCount];
157 };
158
159
160 /**
161  * Hashmap to store peer information for preference normalization
162  */
163 static struct GNUNET_CONTAINER_MultiPeerMap *preference_peers;
164
165 /**
166  * Clients in DLL: head
167  */
168 static struct PreferenceClient *pc_head;
169
170 /**
171  * Clients in DLL: tail
172  */
173 static struct PreferenceClient *pc_tail;
174
175 /**
176  * Handle for task we run periodically to age preferences over time.
177  */
178 static struct GNUNET_SCHEDULER_Task *aging_task;
179
180
181 /**
182  * Update a peer
183  *
184  * @param id peer id
185  * @param kind the kind
186  * @param rp the relative peer struct
187  * @return the new relative preference
188  */
189 static void
190 update_relative_values_for_peer (const struct GNUNET_PeerIdentity *id,
191                                  enum GNUNET_ATS_PreferenceKind kind,
192                                  struct PeerRelative *rp)
193 {
194   struct PreferenceClient *c_cur;
195   struct PreferencePeer *p_cur;
196   double f_rel_total;
197   double f_rel_sum;
198   double backup;
199   unsigned int peer_count;
200
201   f_rel_sum = 0.0;
202   f_rel_total = 0.0;
203   peer_count = 0;
204
205   /* For all clients */
206   for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
207   {
208     /* For peer entries with this id */
209     for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
210     {
211       f_rel_sum += p_cur->f_rel[kind];
212       if (0 == memcmp (id, &p_cur->id, sizeof(struct GNUNET_PeerIdentity)))
213       {
214         peer_count ++;
215         f_rel_total += p_cur->f_rel[kind];
216       }
217     }
218   }
219
220   LOG (GNUNET_ERROR_TYPE_DEBUG,
221        "%u clients have a total relative preference for peer `%s' `%s' of %.3f and for %s in total %.3f\n",
222        peer_count,
223        GNUNET_i2s (id),
224        GNUNET_ATS_print_preference_type (kind),
225        f_rel_total,
226        GNUNET_ATS_print_preference_type (kind),
227        f_rel_sum);
228
229   /* Find entry for the peer containing relative values in the hashmap */
230   if (NULL != rp)
231   {
232     backup = rp->f_rel[kind];
233     if (f_rel_sum > 0)
234       rp->f_rel[kind] = f_rel_total / f_rel_sum;
235     else
236     {
237       /* No client had any preferences for this type and any peer */
238       rp->f_rel[kind] = DEFAULT_REL_PREFERENCE;
239     }
240     if (backup != rp->f_rel[kind])
241       GAS_normalized_preference_changed (&rp->id, kind,
242                                          rp->f_rel[kind]);
243   }
244 }
245
246
247 /**
248  * FIXME
249  */
250 static int
251 update_iterator (void *cls,
252                  const struct GNUNET_PeerIdentity *key,
253                  void *value)
254 {
255   enum GNUNET_ATS_PreferenceKind *kind = cls;
256   struct PeerRelative *pr = value;
257
258   update_relative_values_for_peer (key,
259                                    *kind,
260                                    pr);
261   return GNUNET_OK;
262 }
263
264
265 /**
266  * Recalculate preference for a specific ATS property
267  *
268  * @param c the preference client
269  * @param kind the preference kind
270  * @return the result
271  */
272 static void
273 recalculate_relative_preferences (struct PreferenceClient *c,
274                                   enum GNUNET_ATS_PreferenceKind kind)
275 {
276   struct PreferencePeer *p_cur;
277
278   /* For this client: sum of absolute preference values for this preference */
279   c->f_abs_sum[kind] = 0.0;
280   /* For this client: sum of relative preference values for this preference
281    *
282    * Note: this value should also be 1.0, but:
283    * if no preferences exist due to aging, this value can be 0.0
284    * and the client can be removed */
285   c->f_rel_sum[kind] = 0.0;
286
287   for (p_cur = c->p_head; NULL != p_cur; p_cur = p_cur->next)
288     c->f_abs_sum[kind] += p_cur->f_abs[kind];
289   LOG (GNUNET_ERROR_TYPE_DEBUG,
290       "Client %p has sum of total preferences for %s of %.3f\n",
291       c->client, GNUNET_ATS_print_preference_type (kind), c->f_abs_sum[kind]);
292
293   /* For all peers: calculate relative preference */
294   for (p_cur = c->p_head; NULL != p_cur; p_cur = p_cur->next)
295   {
296     /* Calculate relative preference for specific kind */
297
298     /* Every application has a preference for each peer between
299      * [0 .. 1] in relative values
300      * and [0 .. inf] in absolute values */
301     p_cur->f_rel[kind] =  p_cur->f_abs[kind] / c->f_abs_sum[kind];
302     c->f_rel_sum[kind] += p_cur->f_rel[kind];
303
304     LOG (GNUNET_ERROR_TYPE_DEBUG,
305         "Client %p has relative preference for %s for peer `%s' of %.3f\n",
306         c->client,
307         GNUNET_ATS_print_preference_type (kind),
308         GNUNET_i2s (&p_cur->id),
309         p_cur->f_rel[kind]);
310   }
311
312 }
313
314
315 /**
316  * FIXME.
317  *
318  */
319 static void
320 run_preference_update (struct PreferenceClient *c_cur,
321                        struct PreferencePeer *p_cur,
322                        enum GNUNET_ATS_PreferenceKind kind,
323                        float score_abs)
324 {
325   double old_value;
326
327   /* Update relative value */
328   old_value = p_cur->f_rel[kind];
329   recalculate_relative_preferences (c_cur, kind);
330   if (p_cur->f_rel[kind] == old_value)
331     return;
332
333   /* Relative preference value changed, recalculate for all peers */
334   GNUNET_CONTAINER_multipeermap_iterate (preference_peers,
335                                          &update_iterator,
336                                          &kind);
337 }
338
339
340 /**
341  * Reduce absolute preferences since they got old
342  *
343  * @param cls the PreferencePeer
344  * @param tc context
345  */
346 static void
347 preference_aging (void *cls,
348                   const struct GNUNET_SCHEDULER_TaskContext *tc)
349 {
350   struct PreferencePeer *p;
351   struct PreferenceClient *cur_client;
352   unsigned int i;
353   unsigned int values_to_update;
354   double backup;
355
356   aging_task = NULL;
357   values_to_update = 0;
358   cur_client = NULL;
359
360   for (cur_client = pc_head; NULL != cur_client; cur_client = cur_client->next)
361   {
362     for (p = cur_client->p_head; NULL != p; p = p->next)
363     {
364       /* Aging absolute values: */
365       for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
366       {
367         if (0
368             == GNUNET_TIME_absolute_get_remaining (p->next_aging[i]).rel_value_us)
369         {
370           GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
371               "Aging preference for peer `%s'\n", GNUNET_i2s (&p->id));
372           backup = p->f_abs[i];
373           if (p->f_abs[i] > DEFAULT_ABS_PREFERENCE)
374             p->f_abs[i] *= PREF_AGING_FACTOR;
375
376           if (p->f_abs[i] <= DEFAULT_ABS_PREFERENCE + PREF_EPSILON)
377             p->f_abs[i] = DEFAULT_ABS_PREFERENCE;
378
379           if ( (p->f_abs[i] != DEFAULT_ABS_PREFERENCE) &&
380                (backup != p->f_abs[i]) )
381           {
382             GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
383                 "Aged preference for peer `%s' from %.3f to %.3f\n",
384                 GNUNET_i2s (&p->id), backup, p->f_abs[i]);
385
386             run_preference_update(cur_client, p, i, p->f_abs[i]);
387
388             p->next_aging[i] = GNUNET_TIME_absolute_add (
389                 GNUNET_TIME_absolute_get (), PREF_AGING_INTERVAL);
390             values_to_update++;
391           }
392         }
393       }
394     }
395   }
396
397   if (values_to_update > 0)
398   {
399     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
400                 "Rescheduling aging task due to %u elements to age\n",
401                 values_to_update);
402     aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
403                                                &preference_aging,
404                                                NULL);
405   }
406   else
407   {
408     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
409                 "No values to age left, not rescheduling aging task\n");
410   }
411 }
412
413
414 /**
415  * Update the absolute preference value for a peer
416  * @param c the client
417  * @param p the peer
418  * @param kind the preference kind
419  * @param score_abs the absolute value
420  * @return the new relative preference value
421  */
422 static void
423 update_abs_preference (struct PreferenceClient *c,
424                        struct PreferencePeer *p,
425                        enum GNUNET_ATS_PreferenceKind kind,
426                        float score_abs)
427 {
428   double score = score_abs;
429
430   /* Update preference value according to type */
431   switch (kind)
432   {
433   case GNUNET_ATS_PREFERENCE_BANDWIDTH:
434   case GNUNET_ATS_PREFERENCE_LATENCY:
435     p->f_abs[kind] = score;
436     /* p->f_abs[kind] = (p->f_abs[kind] + score) / 2;  */
437     p->next_aging[kind] = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
438         PREF_AGING_INTERVAL);
439     break;
440   case GNUNET_ATS_PREFERENCE_END:
441     break;
442   default:
443     break;
444   }
445 }
446
447
448 /**
449  * Change the preference for a peer
450  *
451  * @param client the client sending this request
452  * @param peer the peer id
453  * @param kind the preference kind to change
454  * @param score_abs the new preference score
455  */
456 static void
457 preference_change (struct GNUNET_SERVER_Client *client,
458                    const struct GNUNET_PeerIdentity *peer,
459                    enum GNUNET_ATS_PreferenceKind kind,
460                    float score_abs)
461 {
462   if (GNUNET_NO ==
463       GNUNET_CONTAINER_multipeermap_contains (GSA_addresses,
464                                               peer))
465   {
466     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
467                 "Received CHANGE_PREFERENCE for unknown peer `%s'\n",
468                 GNUNET_i2s (peer));
469     return;
470   }
471   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
472               "Received CHANGE_PREFERENCE for peer `%s'\n",
473               GNUNET_i2s (peer));
474   GAS_plugin_update_preferences (client,
475                                  peer,
476                                  kind,
477                                  score_abs);
478 }
479
480
481 /**
482  * Handle 'preference change' messages from clients.
483  *
484  * @param cls unused, NULL
485  * @param client client that sent the request
486  * @param message the request message
487  */
488 void
489 GAS_handle_preference_change (void *cls,
490                               struct GNUNET_SERVER_Client *client,
491                               const struct GNUNET_MessageHeader *message)
492 {
493   const struct ChangePreferenceMessage *msg;
494   const struct PreferenceInformation *pi;
495   uint16_t msize;
496   uint32_t nump;
497   uint32_t i;
498
499   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
500               "Received `%s' message\n",
501               "PREFERENCE_CHANGE");
502   msize = ntohs (message->size);
503   if (msize < sizeof (struct ChangePreferenceMessage))
504   {
505     GNUNET_break (0);
506     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
507     return;
508   }
509   msg = (const struct ChangePreferenceMessage *) message;
510   nump = ntohl (msg->num_preferences);
511   if (msize !=
512       sizeof (struct ChangePreferenceMessage) +
513       nump * sizeof (struct PreferenceInformation))
514   {
515     GNUNET_break (0);
516     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
517     return;
518   }
519   GNUNET_STATISTICS_update (GSA_stats,
520                             "# preference change requests processed",
521                             1, GNUNET_NO);
522   pi = (const struct PreferenceInformation *) &msg[1];
523   for (i = 0; i < nump; i++)
524     preference_change (client,
525                        &msg->peer,
526                        (enum GNUNET_ATS_PreferenceKind)
527                        ntohl (pi[i].preference_kind),
528                        pi[i].preference_value);
529   GNUNET_SERVER_receive_done (client, GNUNET_OK);
530 }
531
532
533 /**
534  * Initialize preferences subsystem.
535  */
536 void
537 GAS_preference_init ()
538 {
539   int i;
540
541   preference_peers = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
542   for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
543     defvalues.f_rel[i] = DEFAULT_REL_PREFERENCE;
544 }
545
546
547 /**
548  * Free a peer
549  *
550  * @param cls unused
551  * @param key the key
552  * @param value RelativePeer
553  * @return #GNUNET_OK to continue
554  */
555 static int
556 free_peer (void *cls,
557            const struct GNUNET_PeerIdentity *key,
558            void *value)
559 {
560   struct PeerRelative *rp = value;
561
562   if (GNUNET_YES ==
563       GNUNET_CONTAINER_multipeermap_remove (preference_peers,
564                                             key,
565                                             value))
566     GNUNET_free (rp);
567   else
568     GNUNET_break (0);
569   return GNUNET_OK;
570 }
571
572
573 static void
574 free_client (struct PreferenceClient *pc)
575 {
576   struct PreferencePeer *next_p;
577   struct PreferencePeer *p;
578
579   next_p = pc->p_head;
580   while (NULL != (p = next_p))
581   {
582     next_p = p->next;
583     GNUNET_CONTAINER_DLL_remove(pc->p_head, pc->p_tail, p);
584     GNUNET_free(p);
585   }
586   GNUNET_free(pc);
587 }
588
589
590 /**
591  * Shutdown preferences subsystem.
592  */
593 void
594 GAS_preference_done ()
595 {
596   struct PreferenceClient *pc;
597   struct PreferenceClient *next_pc;
598
599   if (NULL != aging_task)
600   {
601     GNUNET_SCHEDULER_cancel (aging_task);
602     aging_task = NULL;
603   }
604   next_pc = pc_head;
605   while (NULL != (pc = next_pc))
606   {
607     next_pc = pc->next;
608     GNUNET_CONTAINER_DLL_remove (pc_head,
609                                  pc_tail,
610                                  pc);
611     free_client (pc);
612   }
613   GNUNET_CONTAINER_multipeermap_iterate (preference_peers,
614                                          &free_peer,
615                                          NULL);
616   GNUNET_CONTAINER_multipeermap_destroy (preference_peers);
617
618 }
619
620
621 /**
622  * Normalize an updated preference value
623  *
624  * @param client the client with this preference
625  * @param peer the peer to change the preference for
626  * @param kind the kind to change the preference
627  * @param score_abs the normalized score
628  */
629 void
630 GAS_normalization_normalize_preference (struct GNUNET_SERVER_Client *client,
631                                         const struct GNUNET_PeerIdentity *peer,
632                                         enum GNUNET_ATS_PreferenceKind kind,
633                                         float score_abs)
634 {
635   struct PreferenceClient *c_cur;
636   struct PreferencePeer *p_cur;
637   struct PeerRelative *r_cur;
638   double old_value;
639   int i;
640
641   LOG (GNUNET_ERROR_TYPE_DEBUG,
642        "Client changes preference for peer `%s' for `%s' to %.2f\n",
643        GNUNET_i2s (peer),
644        GNUNET_ATS_print_preference_type (kind),
645        score_abs);
646
647   if (kind >= GNUNET_ATS_PreferenceCount)
648   {
649     GNUNET_break(0);
650     return;
651   }
652
653   /* Find preference client */
654   for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
655     if (client == c_cur->client)
656       break;
657   /* Not found: create new preference client */
658   if (NULL == c_cur)
659   {
660     c_cur = GNUNET_new (struct PreferenceClient);
661     c_cur->client = client;
662     for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
663     {
664       c_cur->f_abs_sum[i] = DEFAULT_ABS_PREFERENCE;
665       c_cur->f_rel_sum[i] = DEFAULT_REL_PREFERENCE;
666     }
667
668     GNUNET_CONTAINER_DLL_insert (pc_head,
669                                  pc_tail,
670                                  c_cur);
671     LOG (GNUNET_ERROR_TYPE_DEBUG,
672          "Adding new client %p\n",
673          c_cur);
674   }
675
676   /* Find entry for peer */
677   for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
678     if (0 == memcmp (&p_cur->id,
679                      peer,
680                      sizeof (p_cur->id)))
681       break;
682
683   /* Not found: create new peer entry */
684   if (NULL == p_cur)
685   {
686     p_cur = GNUNET_new (struct PreferencePeer);
687     p_cur->client = c_cur;
688     p_cur->id = (*peer);
689     for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
690     {
691       /* Default value per peer absolute preference for a preference: 0 */
692       p_cur->f_abs[i] = DEFAULT_ABS_PREFERENCE;
693       /* Default value per peer relative preference for a quality: 1.0 */
694       p_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
695       p_cur->next_aging[i] = GNUNET_TIME_UNIT_FOREVER_ABS;
696     }
697     LOG (GNUNET_ERROR_TYPE_DEBUG,
698          "Adding new peer %p for client %p\n",
699          p_cur,
700          c_cur);
701     GNUNET_CONTAINER_DLL_insert (c_cur->p_head,
702                                  c_cur->p_tail,
703                                  p_cur);
704   }
705
706   /* Create struct for peer */
707   if (NULL ==
708       GNUNET_CONTAINER_multipeermap_get (preference_peers,
709                                          peer))
710   {
711     r_cur = GNUNET_new (struct PeerRelative);
712     r_cur->id = *peer;
713     for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
714       r_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
715     GNUNET_assert(GNUNET_OK ==
716                   GNUNET_CONTAINER_multipeermap_put (preference_peers,
717                                                      &r_cur->id,
718                                                      r_cur,
719                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
720   }
721
722   /* Update absolute value */
723   old_value = p_cur->f_abs[kind];
724   update_abs_preference (c_cur, p_cur, kind, score_abs);
725   if (p_cur->f_abs[kind] == old_value)
726     return;
727
728   run_preference_update (c_cur,
729                          p_cur,
730                          kind,
731                          score_abs);
732
733   if (NULL == aging_task)
734     aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
735                                                &preference_aging,
736                                                NULL);
737 }
738
739
740 /**
741  * Get the normalized preference values for a specific peer or
742  * the default values if
743  *
744  * @param cls ignored
745  * @param id the peer
746  * @return pointer to the values, can be indexed with GNUNET_ATS_PreferenceKind,
747  * default preferences if peer does not exist
748  */
749 const double *
750 GAS_normalization_get_preferences_by_peer (void *cls,
751                                            const struct GNUNET_PeerIdentity *id)
752 {
753   struct PeerRelative *rp;
754
755   if (NULL ==
756       (rp = GNUNET_CONTAINER_multipeermap_get (preference_peers,
757                                                id)))
758   {
759     return defvalues.f_rel;
760   }
761   return rp->f_rel;
762 }
763
764
765 /**
766  * A performance client disconnected
767  *
768  * @param client the client
769  */
770 void
771 GAS_normalization_preference_client_disconnect (struct GNUNET_SERVER_Client *client)
772 {
773   struct PreferenceClient *c_cur;
774
775   for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
776     if (client == c_cur->client)
777       break;
778   if (NULL == c_cur)
779     return;
780   GNUNET_CONTAINER_DLL_remove (pc_head,
781                                pc_tail,
782                                c_cur);
783   free_client (c_cur);
784 }
785
786
787 /* end of gnunet-service-ats_preferences.c */