Send triggered updates (not really tested).
[trust_router.git] / trp / trp_rtable.c
1 #include <stdlib.h>
2
3 #include <glib.h>
4 #include <talloc.h>
5 #include <time.h>
6
7 #include <trust_router/tr_name.h>
8 #include <trp_internal.h>
9 #include <trp_rtable.h>
10 #include <tr_debug.h>
11
12 /* Note: be careful mixing talloc with glib. */
13
14 static int trp_rentry_destructor(void *obj)
15 {
16   TRP_RENTRY *entry=talloc_get_type_abort(obj, TRP_RENTRY);
17   if (entry->apc!=NULL)
18     tr_free_name(entry->apc);
19   if (entry->realm!=NULL)
20     tr_free_name(entry->realm);
21   if (entry->trust_router!=NULL)
22     tr_free_name(entry->trust_router);
23   if (entry->peer!=NULL)
24     tr_free_name(entry->peer);
25   if (entry->next_hop!=NULL)
26     tr_free_name(entry->next_hop);
27   return 0;
28 }
29
30 TRP_RENTRY *trp_rentry_new(TALLOC_CTX *mem_ctx)
31 {
32   TRP_RENTRY *entry=talloc(mem_ctx, TRP_RENTRY);
33   if (entry!=NULL) {
34     entry->apc=NULL;
35     entry->realm=NULL;
36     entry->trust_router=NULL;
37     entry->peer=NULL;
38     entry->next_hop=NULL;
39     entry->selected=0;
40     entry->interval=0;
41     entry->expiry=talloc(entry, struct timespec);
42     if (entry->expiry==NULL) {
43       talloc_free(entry);
44       return NULL;
45     }
46     talloc_set_destructor((void *)entry, trp_rentry_destructor);
47   }
48   return entry;
49 }
50
51 void trp_rentry_free(TRP_RENTRY *entry)
52 {
53   if (entry!=NULL)
54     talloc_free(entry);
55 }
56
57 void trp_rentry_set_apc(TRP_RENTRY *entry, TR_NAME *apc)
58 {
59   entry->apc=apc;
60 }
61
62 TR_NAME *trp_rentry_get_apc(TRP_RENTRY *entry)
63 {
64   return entry->apc;
65 }
66
67 TR_NAME *trp_rentry_dup_apc(TRP_RENTRY *entry)
68 {
69   return tr_dup_name(trp_rentry_get_apc(entry));
70 }
71
72 void trp_rentry_set_realm(TRP_RENTRY *entry, TR_NAME *realm)
73 {
74   entry->realm=realm;
75 }
76
77 TR_NAME *trp_rentry_get_realm(TRP_RENTRY *entry)
78 {
79   return entry->realm;
80 }
81
82 TR_NAME *trp_rentry_dup_realm(TRP_RENTRY *entry)
83 {
84   return tr_dup_name(trp_rentry_get_realm(entry));
85 }
86
87 void trp_rentry_set_trust_router(TRP_RENTRY *entry, TR_NAME *tr)
88 {
89   entry->trust_router=tr;
90 }
91
92 TR_NAME *trp_rentry_get_trust_router(TRP_RENTRY *entry)
93 {
94   return entry->trust_router;
95 }
96
97 TR_NAME *trp_rentry_dup_trust_router(TRP_RENTRY *entry)
98 {
99   return tr_dup_name(trp_rentry_get_trust_router(entry));
100 }
101
102 void trp_rentry_set_peer(TRP_RENTRY *entry, TR_NAME *peer)
103 {
104   entry->peer=peer;
105 }
106
107 TR_NAME *trp_rentry_get_peer(TRP_RENTRY *entry)
108 {
109   return entry->peer;
110 }
111
112 TR_NAME *trp_rentry_dup_peer(TRP_RENTRY *entry)
113 {
114   return tr_dup_name(trp_rentry_get_peer(entry));
115 }
116
117 void trp_rentry_set_metric(TRP_RENTRY *entry, unsigned int metric)
118 {
119   entry->metric=metric;
120 }
121
122 unsigned int trp_rentry_get_metric(TRP_RENTRY *entry)
123 {
124   return entry->metric;
125 }
126
127 void trp_rentry_set_next_hop(TRP_RENTRY *entry, TR_NAME *next_hop)
128 {
129   entry->next_hop=next_hop;
130 }
131
132 TR_NAME *trp_rentry_get_next_hop(TRP_RENTRY *entry)
133 {
134   return entry->next_hop;
135 }
136
137 TR_NAME *trp_rentry_dup_next_hop(TRP_RENTRY *entry)
138 {
139   return tr_dup_name(trp_rentry_get_next_hop(entry));
140 }
141
142 void trp_rentry_set_selected(TRP_RENTRY *entry, int sel)
143 {
144   entry->selected=sel;
145 }
146
147 int trp_rentry_get_selected(TRP_RENTRY *entry)
148 {
149   return entry->selected;
150 }
151
152 void trp_rentry_set_interval(TRP_RENTRY *entry, int interval)
153 {
154   entry->interval=interval;
155 }
156
157 int trp_rentry_get_interval(TRP_RENTRY *entry)
158 {
159   return entry->interval;
160 }
161
162 /* copies incoming value, does not assume responsibility for freeing */
163 void trp_rentry_set_expiry(TRP_RENTRY *entry, struct timespec *exp)
164 {
165   entry->expiry->tv_sec=exp->tv_sec;
166   entry->expiry->tv_nsec=exp->tv_nsec;
167 }
168
169 struct timespec *trp_rentry_get_expiry(TRP_RENTRY *entry)
170 {
171   return entry->expiry;
172 }
173
174 void trp_rentry_set_triggered(TRP_RENTRY *entry, int trig)
175 {
176   entry->triggered=trig;
177 }
178
179 int trp_rentry_get_triggered(TRP_RENTRY *entry)
180 {
181   return entry->triggered;
182 }
183
184
185 /* result must be freed with g_free */
186 static gchar *tr_name_to_g_str(const TR_NAME *n)
187 {
188   gchar *s=g_strndup(n->buf, n->len);
189   return s;
190 }
191
192 /* hash function for TR_NAME keys */
193 static guint trp_tr_name_hash(gconstpointer key)
194 {
195   const TR_NAME *name=(TR_NAME *)key;
196   gchar *s=tr_name_to_g_str(name);
197   guint hash=g_str_hash(s);
198   g_free(s);
199   return hash;
200 }
201
202 /* hash equality function for TR_NAME keys */
203 static gboolean trp_tr_name_equal(gconstpointer key1, gconstpointer key2)
204 {
205   const TR_NAME *n1=(TR_NAME *)key1;
206   const TR_NAME *n2=(TR_NAME *)key2;
207   gchar *s1=tr_name_to_g_str(n1);
208   gchar *s2=tr_name_to_g_str(n2);
209   gboolean equal=g_str_equal(s1, s2);
210   g_free(s1);
211   g_free(s2);
212   return equal;
213 }
214
215 /* free a value to the top level rtable (a hash of all entries in the apc) */
216 static void trp_rtable_destroy_table(gpointer data)
217 {
218   g_hash_table_destroy(data);
219 }
220
221 static void trp_rtable_destroy_rentry(gpointer data)
222 {
223   trp_rentry_free(data);
224 }
225
226 static void trp_rtable_destroy_tr_name(gpointer data)
227 {
228   tr_free_name(data);
229 }
230
231 TRP_RTABLE *trp_rtable_new(void)
232 {
233   GHashTable *new=g_hash_table_new_full(trp_tr_name_hash,
234                                         trp_tr_name_equal,
235                                         trp_rtable_destroy_tr_name,
236                                         trp_rtable_destroy_table);
237   return new;
238 }
239
240 void trp_rtable_free(TRP_RTABLE *rtbl)
241 {
242   g_hash_table_destroy(rtbl);
243 }
244
245 static GHashTable *trp_rtbl_get_or_add_table(GHashTable *tbl, TR_NAME *key, GDestroyNotify destroy)
246 {
247   GHashTable *val_tbl=NULL;
248
249   val_tbl=g_hash_table_lookup(tbl, key);
250   if (val_tbl==NULL) {
251     val_tbl=g_hash_table_new_full(trp_tr_name_hash,
252                                   trp_tr_name_equal,
253                                   trp_rtable_destroy_tr_name,
254                                   destroy);
255     g_hash_table_insert(tbl, tr_dup_name(key), val_tbl);
256   }
257   return val_tbl;
258 }
259
260 void trp_rtable_add(TRP_RTABLE *rtbl, TRP_RENTRY *entry)
261 {
262   GHashTable *apc_tbl=NULL;
263   GHashTable *realm_tbl=NULL;
264
265   apc_tbl=trp_rtbl_get_or_add_table(rtbl, entry->apc, trp_rtable_destroy_table);
266   realm_tbl=trp_rtbl_get_or_add_table(apc_tbl, entry->realm, trp_rtable_destroy_rentry);
267   g_hash_table_insert(realm_tbl, tr_dup_name(entry->peer), entry); /* destroys and replaces a duplicate */
268   /* the route entry should not belong to any context, we will manage it ourselves */
269   talloc_steal(NULL, entry);
270 }
271
272 /* note: the entry pointer passed in is invalid after calling this because the entry is freed */
273 void trp_rtable_remove(TRP_RTABLE *rtbl, TRP_RENTRY *entry)
274 {
275   GHashTable *apc_tbl=NULL;
276   GHashTable *realm_tbl=NULL;
277
278   apc_tbl=g_hash_table_lookup(rtbl, entry->apc);
279   if (apc_tbl==NULL)
280     return;
281
282   realm_tbl=g_hash_table_lookup(apc_tbl, entry->realm);
283   if (realm_tbl==NULL)
284     return;
285
286   /* remove the element */
287   g_hash_table_remove(realm_tbl, entry->peer);
288   /* if that was the last entry in the realm, remove the realm table */
289   if (g_hash_table_size(realm_tbl)==0)
290     g_hash_table_remove(apc_tbl, entry->realm);
291   /* if that was the last realm in the apc, remove the apc table */
292   if (g_hash_table_size(apc_tbl)==0)
293     g_hash_table_remove(rtbl, entry->apc);
294 }
295
296 void trp_rtable_clear(TRP_RTABLE *rtbl)
297 {
298   g_hash_table_remove_all(rtbl); /* destructors should do all the cleanup */
299 }
300
301 /* gets the actual hash table, for internal use only */
302 static GHashTable *trp_rtable_get_apc_table(TRP_RTABLE *rtbl, TR_NAME *apc)
303 {
304   return g_hash_table_lookup(rtbl, apc);
305 }
306
307 /* gets the actual hash table, for internal use only */
308 static GHashTable *trp_rtable_get_realm_table(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm)
309 {
310   GHashTable *apc_tbl=trp_rtable_get_apc_table(rtbl, apc);
311   if (apc_tbl==NULL)
312     return NULL;
313   else
314     return g_hash_table_lookup(apc_tbl, realm);
315 }
316
317 struct table_size_cookie {
318   TRP_RTABLE *rtbl;
319   size_t size;
320 };
321 static void trp_rtable_size_helper(gpointer key, gpointer value, gpointer user_data)
322 {
323   struct table_size_cookie *data=(struct table_size_cookie *)user_data;
324   data->size += trp_rtable_apc_size(data->rtbl, (TR_NAME *)key);
325 };
326 size_t trp_rtable_size(TRP_RTABLE *rtbl)
327 {
328   struct table_size_cookie data={rtbl, 0};
329   g_hash_table_foreach(rtbl, trp_rtable_size_helper, &data);
330   return data.size;
331 }
332
333 struct table_apc_size_cookie {
334   TR_NAME *apc;
335   TRP_RTABLE *rtbl;
336   size_t size;
337 };
338 static void table_apc_size_helper(gpointer key, gpointer value, gpointer user_data)
339 {
340   struct table_apc_size_cookie *data=(struct table_apc_size_cookie *)user_data;
341   data->size += trp_rtable_realm_size(data->rtbl, data->apc, (TR_NAME *)key);
342 }
343 size_t trp_rtable_apc_size(TRP_RTABLE *rtbl, TR_NAME *apc)
344 {
345   struct table_apc_size_cookie data={apc, rtbl, 0};
346   GHashTable *apc_tbl=trp_rtable_get_apc_table(rtbl, apc);
347   if (apc_tbl==NULL)
348     return 0;;
349   g_hash_table_foreach(apc_tbl, table_apc_size_helper, &data);
350   return data.size;
351 }
352
353 size_t trp_rtable_realm_size(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm)
354 {
355   GHashTable *realm_tbl=trp_rtable_get_realm_table(rtbl, apc, realm);
356   if (realm_tbl==NULL)
357     return 0;
358   else
359     return g_hash_table_size(g_hash_table_lookup(
360                                g_hash_table_lookup(rtbl, apc),
361                                realm));
362 }
363
364 /* Returns an array of pointers to TRP_RENTRY, length of array in n_out.
365  * Caller must free the array (in the talloc NULL context), but must
366  * not free its contents. */
367 TRP_RENTRY **trp_rtable_get_entries(TRP_RTABLE *rtbl, size_t *n_out)
368 {
369   TRP_RENTRY **ret=NULL;
370   TR_NAME **apc=NULL;
371   size_t n_apc=0;
372   TRP_RENTRY **apc_entries=NULL;
373   size_t n_entries=0;
374   size_t ii_ret=0;
375
376   *n_out=trp_rtable_size(rtbl);
377   if (*n_out==0)
378     return NULL;
379
380   ret=talloc_array(NULL, TRP_RENTRY *, *n_out);
381   if (ret==NULL) {
382     tr_crit("trp_rtable_get_entries: unable to allocate return array.");
383     *n_out=0;
384     return NULL;
385   }
386
387   ii_ret=0; /* counts output entries */
388   apc=trp_rtable_get_apcs(rtbl, &n_apc);
389   while(n_apc--) {
390     apc_entries=trp_rtable_get_apc_entries(rtbl, apc[n_apc], &n_entries);
391     while (n_entries--)
392       ret[ii_ret++]=apc_entries[n_entries];
393     talloc_free(apc_entries);
394   }
395   talloc_free(apc);
396
397   if (ii_ret!=*n_out) {
398     tr_crit("trp_rtable_get_entries: found incorrect number of entries.");
399     talloc_free(ret);
400     *n_out=0;
401     return NULL;
402   }
403   return ret;
404 }
405
406 /* Returns an array of pointers to TR_NAME, length of array in n_out.
407  * Caller must free the array (in the talloc NULL context). */
408 TR_NAME **trp_rtable_get_apcs(TRP_RTABLE *rtbl, size_t *n_out)
409 {
410   size_t len=g_hash_table_size(rtbl); /* known apcs are keys in top level hash table */
411   size_t ii=0;
412   GList *apcs=NULL;;
413   GList *p=NULL;
414   TR_NAME **ret=NULL;
415
416   if (len==0) {
417     *n_out=0;
418     return NULL;
419   }
420     
421   ret=talloc_array(NULL, TR_NAME *, len);
422   if (ret==NULL) {
423     tr_crit("trp_rtable_get_apcs: unable to allocate return array.");
424     *n_out=0;
425     return NULL;
426   }
427   apcs=g_hash_table_get_keys(rtbl);
428   for (ii=0,p=apcs; p!=NULL; ii++,p=g_list_next(p))
429     ret[ii]=(TR_NAME *)p->data;
430
431   g_list_free(apcs);
432
433   *n_out=len;
434   return ret;
435 }
436
437 /* Returns an array of pointers to TR_NAME, length of array in n_out.
438  * Caller must free the array (in the talloc NULL context). */
439 TR_NAME **trp_rtable_get_apc_realms(TRP_RTABLE *rtbl, TR_NAME *apc, size_t *n_out)
440 {
441   size_t ii=0;
442   TRP_RTABLE *apc_tbl=g_hash_table_lookup(rtbl, apc);;
443   GList *entries=NULL;
444   GList *p=NULL;
445   TR_NAME **ret=NULL;
446
447   if (apc_tbl==NULL) {
448     *n_out=0;
449     return NULL;
450   }
451   *n_out=g_hash_table_size(apc_tbl); /* set output length */
452   ret=talloc_array(NULL, TR_NAME *, *n_out);
453   entries=g_hash_table_get_keys(apc_tbl);
454   for (ii=0,p=entries; p!=NULL; ii++,p=g_list_next(p))
455     ret[ii]=(TR_NAME *)p->data;
456
457   g_list_free(entries);
458   return ret;
459 }
460
461 /* Get all entries in an apc. Returns an array of pointers in NULL talloc context.
462  * Caller must free this list with talloc_free, but must not free the entries in the
463  * list.. */
464 TRP_RENTRY **trp_rtable_get_apc_entries(TRP_RTABLE *rtbl, TR_NAME *apc, size_t *n_out)
465 {
466   size_t ii=0, jj=0;
467   TR_NAME **realm=NULL;
468   size_t n_realms=0;
469   TRP_RENTRY **realm_entries=NULL;
470   size_t n_entries=0;
471   TRP_RENTRY **ret=NULL;
472   size_t ii_ret=0;
473
474   *n_out=trp_rtable_apc_size(rtbl, apc);
475   if (*n_out==0)
476     return NULL;
477
478   ret=talloc_array(NULL, TRP_RENTRY *, *n_out);
479   if (ret==NULL) {
480     tr_crit("trp_rtable_get_apc_entries: could not allocate return array.");
481     *n_out=0;
482     return NULL;
483   }
484   
485   ii_ret=0; /* counts entries in the output array */
486   realm=trp_rtable_get_apc_realms(rtbl, apc, &n_realms);
487   for (ii=0; ii<n_realms; ii++) {
488     realm_entries=trp_rtable_get_realm_entries(rtbl, apc, realm[ii], &n_entries);
489     for (jj=0; jj<n_entries; jj++)
490       ret[ii_ret++]=realm_entries[jj];
491     talloc_free(realm_entries);
492   }
493   talloc_free(realm);
494
495   if (ii_ret!=*n_out) {
496     tr_crit("trp_rtable_get_apc_entries: found incorrect number of entries.");
497     talloc_free(ret);
498     *n_out=0;
499     return NULL;
500   }
501
502   return ret;
503 }
504
505 /* Get all entries in an apc/realm. Returns an array of pointers in NULL talloc context.
506  * Caller must free this list with talloc_free, but must not free the entries in the
507  * list.. */
508 TRP_RENTRY **trp_rtable_get_realm_entries(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, size_t *n_out)
509 {
510   size_t ii=0;
511   TRP_RENTRY **ret=NULL;
512   TR_NAME **peer=NULL;
513
514   peer=trp_rtable_get_apc_realm_peers(rtbl, apc, realm, n_out);
515   ret=talloc_array(NULL, TRP_RENTRY *, *n_out);
516   if (ret==NULL) {
517     tr_crit("trp_rtable_get_realm_entries: could not allocate return array.");
518     talloc_free(peer);
519     n_out=0;
520     return NULL;
521   }
522   for (ii=0; ii<*n_out; ii++)
523     ret[ii]=trp_rtable_get_entry(rtbl, apc, realm, peer[ii]);
524   talloc_free(peer);
525   return ret;
526 }
527
528 TR_NAME **trp_rtable_get_apc_realm_peers(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, size_t *n_out)
529 {
530   TR_NAME **ret=NULL;
531   GHashTable *realm_tbl=NULL;
532   GList *keys=NULL;
533   GList *p=NULL;
534   size_t ii=0;
535
536   *n_out=trp_rtable_realm_size(rtbl, apc, realm);
537   if (*n_out==0)
538     return NULL;
539   realm_tbl=trp_rtable_get_realm_table(rtbl, apc, realm);
540   ret=talloc_array(NULL, TR_NAME *, *n_out);
541   if (ret==NULL) {
542     tr_crit("trp_rtable_get_apc_realm_peers: could not allocate return array.");
543     *n_out=0;
544     return NULL;
545   }
546   keys=g_hash_table_get_keys(realm_tbl);
547   for (ii=0,p=keys; p!=NULL; ii++,p=g_list_next(p))
548     ret[ii]=(TR_NAME *)p->data;
549   g_list_free(keys);
550   return ret;
551 }
552
553 /* Gets a single entry. Do not free it. */
554 TRP_RENTRY *trp_rtable_get_entry(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, TR_NAME *peer)
555 {
556   GHashTable *realm_tbl=NULL;
557
558   realm_tbl=trp_rtable_get_realm_table(rtbl, apc, realm);
559   if (realm_tbl==NULL)
560     return NULL;
561
562   return g_hash_table_lookup(realm_tbl, peer); /* does not copy or increment ref count */
563 }
564
565 static char *timespec_to_str(struct timespec *ts)
566 {
567   struct tm tm;
568   char *s=NULL;
569
570   if (localtime_r(&(ts->tv_sec), &tm)==NULL)
571     return NULL;
572
573   s=malloc(40); /* long enough to contain strftime result */
574   if (s==NULL)
575     return NULL;
576
577   if (strftime(s, 40, "%F %T", &tm)==0) {
578     free(s);
579     return NULL;
580   }
581   return s;
582 }
583
584 TRP_RENTRY *trp_rtable_get_selected_entry(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm)
585 {
586   size_t n=0;
587   TRP_RENTRY **entry=trp_rtable_get_realm_entries(rtbl, apc, realm, &n);
588   TRP_RENTRY *selected=NULL;
589
590   if (n==0)
591     return NULL;
592
593   while(n-- && !trp_rentry_get_selected(entry[n])) { }
594   selected=entry[n];
595   talloc_free(entry);
596   return selected;
597 }
598
599 /* Pretty print a route table entry to a newly allocated string. If sep is NULL,
600  * returns comma+space separated string. */
601 char *trp_rentry_to_str(TALLOC_CTX *mem_ctx, TRP_RENTRY *entry, const char *sep)
602 {
603   char *apc=tr_name_strdup(entry->apc);
604   char *realm=tr_name_strdup(entry->realm);
605   char *peer=tr_name_strdup(entry->peer);
606   char *trust_router=tr_name_strdup(entry->trust_router);
607   char *next_hop=tr_name_strdup(entry->next_hop);
608   char *expiry=timespec_to_str(entry->expiry);
609   char *result=NULL;
610
611   if (sep==NULL)
612     sep=", ";
613
614   result=talloc_asprintf(mem_ctx,
615                          "%s%s%s%s%s%s%u%s%s%s%s%s%d%s%s",
616                          apc, sep,
617                          realm, sep,
618                          peer, sep,
619                          entry->metric, sep,
620                          trust_router, sep,
621                          next_hop, sep,
622                          entry->selected, sep,
623                          expiry);
624   free(apc);
625   free(realm);
626   free(peer);
627   free(trust_router);
628   free(next_hop);
629   free(expiry);
630   return result;
631 }
632
633 void trp_rtable_clear_triggered(TRP_RTABLE *rtbl)
634 {
635   size_t n_entries=0;
636   TRP_RENTRY **entries=trp_rtable_get_entries(rtbl, &n_entries);
637   size_t ii=0;
638
639   if (entries!=NULL) {
640     for (ii=0; ii<n_entries; ii++)
641       trp_rentry_set_triggered(entries[ii], 0);
642     talloc_free(entries);
643   }
644 }
645
646 static int sort_tr_names_cmp(const void *a, const void *b)
647 {
648   TR_NAME **n1=(TR_NAME **)a;
649   TR_NAME **n2=(TR_NAME **)b;
650   return tr_name_cmp(*n1, *n2);
651 }
652
653 static void sort_tr_names(TR_NAME **names, size_t n_names)
654 {
655   qsort(names, n_names, sizeof(TR_NAME *), sort_tr_names_cmp);
656 }
657
658 char *trp_rtable_to_str(TALLOC_CTX *mem_ctx, TRP_RTABLE *rtbl, const char *sep, const char *lineterm)
659 {
660   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
661   TR_NAME **apcs=NULL;
662   size_t n_apcs=0;
663   TR_NAME **realms=NULL;
664   size_t n_realms=0;
665   TRP_RENTRY **entries=NULL;
666   size_t n_entries=0;
667   char **tbl_strings=NULL;
668   size_t ii_tbl=0; /* counts tbl_strings */
669   size_t tbl_size=0;
670   size_t len=0;
671   size_t ii=0, jj=0, kk=0;
672   char *p=NULL;
673   char *result=NULL;
674
675   if (lineterm==NULL)
676     lineterm="\n";
677
678   tbl_size=trp_rtable_size(rtbl);
679   if (tbl_size==0) {
680     result=talloc_strdup(mem_ctx, lineterm);
681     goto cleanup;
682   }
683
684   tbl_strings=talloc_array(tmp_ctx, char *, tbl_size);
685   if (tbl_strings==NULL) {
686     result=talloc_strdup(mem_ctx, "error");
687     goto cleanup;
688   }
689   
690   apcs=trp_rtable_get_apcs(rtbl, &n_apcs);
691   talloc_steal(tmp_ctx, apcs);
692   sort_tr_names(apcs, n_apcs);
693   ii_tbl=0;
694   len=0;
695   for (ii=0; ii<n_apcs; ii++) {
696     realms=trp_rtable_get_apc_realms(rtbl, apcs[ii], &n_realms);
697     talloc_steal(tmp_ctx, realms);
698     sort_tr_names(realms, n_realms);
699     for (jj=0; jj<n_realms; jj++) {
700       entries=trp_rtable_get_realm_entries(rtbl, apcs[ii], realms[jj], &n_entries);
701       talloc_steal(tmp_ctx, entries);
702       for (kk=0; kk<n_entries; kk++) {
703         tbl_strings[ii_tbl]=trp_rentry_to_str(tmp_ctx, entries[kk], sep);
704         len+=strlen(tbl_strings[ii_tbl]);
705         ii_tbl++;
706       }
707       talloc_free(entries);
708     }
709     talloc_free(realms);
710   }
711   talloc_free(apcs);
712
713   /* now combine all the strings */
714   len += tbl_size*strlen(lineterm); /* space for line terminations*/
715   len += 1; /* nul terminator */
716   result=(char *)talloc_size(tmp_ctx, len);
717   for (p=result,ii=0; ii < tbl_size; ii++) {
718     p+=sprintf(p, "%s%s", tbl_strings[ii], lineterm);
719   }
720   talloc_steal(mem_ctx, result);
721   
722 cleanup:
723   talloc_free(tmp_ctx);
724   return result;
725 }