Added option to run without the assumption of PlanetLab Vservers. We still assume...
[distributedratelimiting.git] / drl / config.c
1 /* See the DRL-LICENSE file for this file's software license. */
2
3 #include <arpa/inet.h>
4 #include <dirent.h>
5 #include <errno.h>
6 #include <libxml/xmlmemory.h>
7 #include <libxml/parser.h>
8 #include <netinet/in.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/socket.h>
13
14 #include "raterouter.h"
15 #include "ratetypes.h"
16 #include "config.h"
17 #include "util.h"
18 #include "logging.h"
19
20 void free_ident(ident_config *ident) {
21     /* Free peers. */
22     while (ident->peers) {
23         ident_peer *tofree = ident->peers;
24         ident->peers = ident->peers->next;
25         free(tofree);
26     }
27
28     /* Free members. */
29     while (ident->members) {
30         ident_member *tofree = ident->members;
31         ident->members = ident->members->next;
32         free(tofree);
33     }
34
35     /* Free ident. */
36     free(ident);
37 }
38
39 void free_ident_list(ident_config *list) {
40     ident_config *tofree;
41
42     while (list) {
43         tofree = list;
44         list = list->next;
45         free_ident(tofree);
46     }
47 }
48
49 static int xid_filter(const struct dirent *d) {
50     if (atoi(d->d_name) > 0)
51         return 1;
52     else
53         return 0;
54 }
55
56 int get_eligible_leaves(drl_instance_t *instance) {
57     struct dirent **names;
58     int count, i;
59     leaf_t *leaves = NULL;
60     map_handle leaf_map = allocate_map();
61
62     if (leaf_map == NULL) {
63         return ENOMEM;
64     }
65
66     count = scandir("/proc/virtual", &names, xid_filter, alphasort);
67
68     if (count < 1) {
69         return 1;
70     }
71
72     leaves = malloc(count * sizeof(leaf_t));
73     if (leaves == NULL) {
74         /* Couldn't allocate leaves array.  Need to free names memory. */
75         while (count--) {
76             free(names[count]);
77         }
78         free(names);
79
80         return ENOMEM;
81     }
82     memset(leaves, 0, count * sizeof(leaf_t));
83
84     for (i = 0; i < count; ++i) {
85         leaves[i].xid = atoi(names[i]->d_name);
86         leaves[i].parent = NULL;
87         leaves[i].drop_prob = 0.0;
88         leaves[i].delay = 0;
89         
90         free(names[i]);
91
92         map_insert(leaf_map, &leaves[i].xid, sizeof(leaves[i].xid), &leaves[i]);
93     }
94
95     free(names);
96
97     instance->leaf_map = leaf_map;
98     instance->leaves = leaves;
99     instance->leaf_count = count;
100
101     return 0;
102 }
103
104 int parse_leaves(drl_instance_t *instance, char *leafstring) {
105     int count = 1;
106     int i;
107     char *comma = leafstring;
108     leaf_t *leaves = NULL;
109     map_handle leaf_map;
110     
111     while ((comma = strchr(comma, ',')) != NULL) {
112         count += 1;
113         comma++;
114     }
115
116     leaf_map = allocate_map();
117     if (leaf_map == NULL) {
118         return ENOMEM;
119     }
120
121     leaves = malloc(count * sizeof(leaf_t));
122     if (leaves == NULL) {
123         return ENOMEM;
124     }
125     memset(leaves, 0, count * sizeof(leaf_t));
126
127     comma = leafstring;
128     for (i = 0; i < count; ++i) {
129         leaves[i].xid = (uint32_t) strtol(comma, NULL, 16);
130         leaves[i].parent = NULL;
131         leaves[i].drop_prob = 0.0;
132         leaves[i].delay = 0;
133
134         printlog(LOG_DEBUG, "Read leaf - hex: %x, dec: %d\n", leaves[i].xid, leaves[i].xid);
135
136         comma = strchr(comma, ',');
137         comma++;
138     }
139
140     instance->leaf_map = leaf_map;
141     instance->leaves = leaves;
142     instance->leaf_count = count;
143
144     return 0;
145 }
146
147 static int parse_common(xmlDocPtr doc, xmlNodePtr ident, ident_config *common) {
148     xmlChar *id;
149     xmlChar *limit;
150     xmlChar *commfabric;
151     xmlChar *branch;
152     xmlChar *membership;
153     xmlChar *failure_behavior;
154     xmlChar *accounting;
155     xmlChar *ewma;
156     xmlChar *mainloop_intervals;
157     xmlChar *communication_intervals;
158     xmlChar *independent;
159     xmlChar *htb_node;
160     xmlChar *htb_parent;
161     xmlNodePtr fields = ident->children;
162     ident_peer *current = NULL;
163
164     /* The struct has been memsetted to 0, this is just to be safe. */
165 #ifdef BUILD_ZOOKEEPER
166     common->zk_host = NULL;
167 #endif
168     common->peers = NULL;
169     common->members = NULL;
170     common->next = NULL;
171
172     /* Make sure no required fields are missing. */
173     id = xmlGetProp(ident, (const xmlChar *) "id");
174     if (id == NULL) {
175         printlog(LOG_CRITICAL, "Ident missing globally unique identifier.\n");
176         return EINVAL;
177     } else {
178         common->id = atoi((const char *) id);
179         xmlFree(id);
180     }
181
182     limit = xmlGetProp(ident, (const xmlChar *) "limit");
183     if (limit == NULL) {
184         printlog(LOG_CRITICAL, "Ident missing global rate limit.\n");
185         return EINVAL;
186     } else {
187         common->limit = atoi((const char *) limit);
188         xmlFree(limit);
189     }
190
191     commfabric = xmlGetProp(ident, (const xmlChar *) "commfabric");
192     if (commfabric == NULL) {
193         printlog(LOG_CRITICAL, "Ident missing comm fabric specifier.\n");
194         return EINVAL;
195     } else {
196         if (!xmlStrcmp(commfabric, (const xmlChar *) "MESH")) {
197             common->commfabric = COMM_MESH;
198         } else if (!xmlStrcmp(commfabric, (const xmlChar *) "GOSSIP")) {
199             common->commfabric = COMM_GOSSIP;
200         } else {
201             printlog(LOG_CRITICAL, "Unknown/invalid comm fabric.\n");
202             xmlFree(commfabric);
203             return EINVAL;
204         }
205         xmlFree(commfabric);
206     }
207
208     /* Only care about branching factor and failure detector if we're using gossip. */
209     if (common->commfabric == COMM_GOSSIP) {
210         branch = xmlGetProp(ident, (const xmlChar *) "branch");
211         if (branch == NULL) {
212             printlog(LOG_CRITICAL, "Ident missing gossip branch.\n");
213             return EINVAL;
214         } else {
215             common->branch = atoi((const char *) branch);
216             xmlFree(branch);
217         }
218
219         membership = xmlGetProp(ident, (const xmlChar *) "membership");
220         if (membership == NULL) {
221             printlog(LOG_CRITICAL, "Ident missing membership protocol selection.\n");
222             return EINVAL;
223         } else {
224             if (!xmlStrcmp(membership, (const xmlChar *) "SWIM")) {
225                 common->membership = SWIM;
226             } else if (!xmlStrcmp(membership, (const xmlChar *) "ZOOKEEPER")) {
227 #ifdef BUILD_ZOOKEEPER
228                 common->membership = ZOOKEEPER;
229 #else
230                 printlog(LOG_CRITICAL, "Zookeeper requested, but support not compiled into DRL at configure time.\n");
231                 xmlFree(membership);
232                 return EINVAL;
233 #endif
234             } else {
235                 printlog(LOG_CRITICAL, "Unknown/invalid gossip group membership protocol.\n");
236                 xmlFree(membership);
237                 return EINVAL;
238             }
239             xmlFree(membership);
240         }
241
242         failure_behavior = xmlGetProp(ident, (const xmlChar *) "failure_behavior");
243         if (failure_behavior == NULL) {
244             printlog(LOG_CRITICAL, "Ident missing failure handling behavior.\n");
245             return EINVAL;
246         } else {
247             if (!xmlStrcmp(failure_behavior, (const xmlChar *) "PANIC")) {
248                 common->failure_behavior = PANIC;
249             } else if (!xmlStrcmp(failure_behavior, (const xmlChar *) "QUORUM")) {
250                 common->failure_behavior = QUORUM;
251             } else {
252                 printlog(LOG_CRITICAL, "Unknown/invalid gossip failure behavior policy.\n");
253                 xmlFree(failure_behavior);
254                 return EINVAL;
255             }
256             xmlFree(failure_behavior);
257         }
258     }
259
260     accounting = xmlGetProp(ident, (const xmlChar *) "accounting");
261     if (accounting == NULL) {
262         printlog(LOG_CRITICAL, "Ident missing accounting.\n");
263         return EINVAL;
264     } else {
265         if (!xmlStrcmp(accounting, (const xmlChar *) "STANDARD")) {
266             common->accounting = ACT_STANDARD;
267         } else if (!xmlStrcmp(accounting, (const xmlChar *) "SAMPLEHOLD")) {
268             common->accounting = ACT_SAMPLEHOLD;
269         } else if (!xmlStrcmp(accounting, (const xmlChar *) "SIMPLE")) {
270             common->accounting = ACT_SIMPLE;
271         } else if (!xmlStrcmp(accounting, (const xmlChar *) "MULTIPLEINTERVAL")) {
272             common->accounting = ACT_MULTIPLE;
273         } else {
274             printlog(LOG_CRITICAL, "Unknown/invalid accounting table.\n");
275             xmlFree(accounting);
276             return EINVAL;
277         }
278         xmlFree(accounting);
279     }
280
281     ewma = xmlGetProp(ident, (const xmlChar *) "ewma");
282     if (ewma == NULL) {
283         printlog(LOG_CRITICAL, "Ident missing ewma weight.\n");
284         return EINVAL;
285     } else {
286         common->fixed_ewma_weight = atof((const char *) ewma);
287         xmlFree(ewma);
288     }
289
290     mainloop_intervals = xmlGetProp(ident, (const xmlChar *) "loop_intervals");
291     if (mainloop_intervals == NULL) {
292         printlog(LOG_WARN, "Ident id: %d missing loop_intervals, assuming 1.\n", common->id);
293         common->mainloop_intervals = 1;
294     } else {
295         common->mainloop_intervals = atoi((const char *) mainloop_intervals);
296         xmlFree(mainloop_intervals);
297     }
298
299     communication_intervals = xmlGetProp(ident, (const xmlChar *) "comm_intervals");
300     if (communication_intervals == NULL) {
301         printlog(LOG_WARN, "Ident id: %d missing comm_intervals, assuming 1.\n", common->id);
302         common->communication_intervals = 1;
303     } else {
304         common->communication_intervals = atoi((const char *) communication_intervals);
305         xmlFree(communication_intervals);
306     }
307
308     independent = xmlGetProp(ident, (const xmlChar *) "independent");
309     if (independent == NULL) {
310         common->independent = 0;
311     } else {
312         common->independent = atoi((const char *) independent);
313         xmlFree(independent);
314     }
315
316     htb_node = xmlGetProp(ident, (const xmlChar *) "htb_node");
317     htb_parent = xmlGetProp(ident, (const xmlChar *) "htb_parent");
318     if (htb_node == NULL) {
319         common->htb_node = -1;
320     } else {
321         sscanf((const char *)htb_node, "%x", &common->htb_node);
322         xmlFree(htb_node);
323     }
324     if (htb_parent == NULL) {
325         common->htb_parent = -1;
326     } else {
327         sscanf((const char *)htb_parent, "%x", &common->htb_parent);
328         xmlFree(htb_parent);
329     }
330
331     while (fields != NULL) {
332         if((!xmlStrcmp(fields->name, (const xmlChar *) "peer"))) {
333             xmlChar *ip = xmlNodeListGetString(doc, fields->children, 1);
334             if (current == NULL) {
335                 /* No peers yet. */
336                 common->peers = malloc(sizeof(ident_peer));
337                 if (common->peers == NULL) {
338                     return ENOMEM;
339                 }
340                 common->peers->ip = inet_addr((const char *) ip);
341                 common->peers->next = NULL;
342                 common->peer_count += 1;
343                 current = common->peers;
344             } else {
345                 /* Add it to the list. */
346                 current->next = malloc(sizeof(ident_peer));
347                 if (current->next == NULL) {
348                     return ENOMEM;
349                 }
350                 current = current->next;
351                 current->ip = inet_addr((const char *) ip);
352                 common->peer_count += 1;
353                 current->next = NULL;
354             }
355             xmlFree(ip);
356         } else if ((!xmlStrcmp(fields->name, (const xmlChar *) "zkhost"))) {
357             xmlChar *host = xmlNodeListGetString(doc, fields->children, 1);
358
359 #ifdef BUILD_ZOOKEEPER
360             common->zk_host = strdup((const char *) host);
361             if (common->zk_host == NULL) {
362                 return ENOMEM;
363             }
364 #endif
365             xmlFree(host);
366         }
367         fields = fields->next;
368     }
369
370     if (common->peers == NULL) {
371         printlog(LOG_CRITICAL, "Must have at least one peer.\n");
372         return EINVAL;
373     }
374
375 #ifdef BUILD_ZOOKEEPER
376     if (common->membership == ZOOKEEPER && common->zk_host == NULL) {
377         printlog(LOG_CRITICAL, "Group membership protocol ZOOKEEPER requires a zkhost field.\n");
378         return EINVAL;
379     }
380 #endif
381     /* No errors. */
382     return 0;
383 }
384
385 static ident_config *parse_machine(xmlDocPtr doc, xmlNodePtr ident, parsed_configs *configs) {
386     ident_config *common = malloc(sizeof(ident_config));
387
388     if (common == NULL) {
389         return NULL;
390     }
391
392     memset(common, 0, sizeof(ident_config));
393     if (parse_common(doc, ident, common)) {
394         free_ident(common);
395         return NULL;
396     }
397
398     /* No further information needed for machine-level identities. */
399     common->type = IDENT_MACHINE;
400     common->members = NULL;
401     common->next = NULL;
402
403     if (configs->last_machine == NULL) {
404         configs->machines = common;
405         configs->last_machine = common;
406     } else {
407         configs->last_machine->next = common;
408         configs->last_machine = common;
409     }
410
411     configs->machine_count += 1;
412
413     return common;
414 }
415
416 static ident_config *parse_set(xmlDocPtr doc, xmlNodePtr ident, parsed_configs *configs) {
417     xmlNodePtr fields = ident->children;
418     ident_config *common = malloc(sizeof(ident_config));
419     ident_member *current = NULL;
420
421     if (common == NULL) {
422         return NULL;
423     }
424
425     memset(common, 0, sizeof(ident_config));
426     if (parse_common(doc, ident, common)) {
427         free_ident(common);
428         return NULL;
429     }
430
431     while (fields != NULL) {
432         ident_member *member = NULL;
433
434         if (!xmlStrcmp(fields->name, (const xmlChar *) "xid")) {
435             xmlChar *xid = xmlNodeListGetString(doc, fields->children, 1);
436
437             if (atoi((const char *) xid) >= 0) {
438                 member = malloc(sizeof(ident_member));
439                 if (member == NULL) {
440                     free_ident(common);
441                     xmlFree(xid);
442                     return NULL;
443                 }
444                 member->type = MEMBER_XID;
445                 sscanf((const char *) xid, "%x", &member->value);
446                 member->next = NULL;
447             } else {
448                 free_ident(common);
449                 xmlFree(xid);
450                 return NULL;
451             }
452
453             xmlFree(xid);
454         } else if (!xmlStrcmp(fields->name, (const xmlChar *) "guid")) {
455             xmlChar *guid = xmlNodeListGetString(doc, fields->children, 1);
456
457             if (atoi((const char *) guid) >= 0) {
458                 member = malloc(sizeof(ident_member));
459                 if (member == NULL) {
460                     free_ident(common);
461                     xmlFree(guid);
462                     return NULL;
463                 }
464                 member->type = MEMBER_GUID;
465                 member->value = atoi((const char *) guid);
466                 member->next = NULL;
467             } else {
468                 free_ident(common);
469                 xmlFree(guid);
470                 return NULL;
471             }
472
473             xmlFree(guid);
474         }
475
476         if (member) {
477             if (common->members == NULL) {
478                 common->members = member;
479                 current = member;
480             } else {
481                 current->next = member;
482                 current = member;
483             }
484         }
485
486         fields = fields->next;
487     }
488
489     /* A sliver set must have at least one member (xid or guid) or else it is
490      * meaningless. */
491     if (common->members == NULL) {
492         free_ident(common);
493         return NULL;
494     }
495
496     common->type = IDENT_SET;
497     common->next = NULL;
498
499     if (configs->last_set == NULL) {
500         configs->sets = common;
501         configs->last_set = common;
502     } else {
503         configs->last_set->next = common;
504         configs->last_set = common;
505     }
506
507     configs->set_count += 1;
508
509     return common;
510 }
511
512 int parse_drl_config(const char *configfile, parsed_configs *configs) {
513     xmlDocPtr doc;
514     xmlNodePtr drl, ident;
515
516     configs->machines = NULL;
517     configs->sets = NULL;
518     configs->last_machine = NULL;
519     configs->last_set = NULL;
520     configs->machine_count = 0;
521     configs->set_count = 0;
522
523     if(!(doc = xmlParseFile(configfile))){
524         printlog(LOG_CRITICAL, "Config file (%s) not parsed successfully.\n", configfile);
525         xmlFreeDoc(doc);
526         return EIO;
527     }
528
529     if(!(drl = xmlDocGetRootElement(doc))){
530         printlog(LOG_CRITICAL, "Config file (%s) has no root element.\n", configfile);
531         xmlFreeDoc(doc);
532         return EIO;
533     }
534     if(xmlStrcmp(drl->name, (const xmlChar *) "drl")){
535         printlog(LOG_CRITICAL, "Config file (%s) of the wrong type, root node != drl\n", configfile);
536         xmlFreeDoc(doc);
537         return EIO;
538     }
539
540     ident = drl->children;
541     while(ident != NULL) {
542         ident_config *new = NULL;
543
544         if((!xmlStrcmp(ident->name, (const xmlChar *) "machine"))) {
545             new = parse_machine(doc, ident, configs);
546         } else if ((!xmlStrcmp(ident->name, (const xmlChar *) "set"))) {
547             new = parse_set(doc, ident, configs);
548         } else if ((!xmlStrcmp(ident->name, (const xmlChar *) "text"))) {
549             /* libxml seems to wrap everything inside two 'text's. */
550             ident = ident->next;
551             continue;
552         }
553
554         if (new == NULL) {
555             /* FIXME: Make this more descriptive. :) */
556             printlog(LOG_CRITICAL, "Error occurred while parsing...\n");
557
558             free_ident_list(configs->machines);
559             free_ident_list(configs->sets);
560
561             configs->machines = NULL;
562             configs->sets = NULL;
563             configs->last_machine = NULL;
564             configs->last_set = NULL;
565
566             xmlFreeDoc(doc);
567             return 1;
568         }
569
570         ident = ident->next;
571     }
572
573     xmlFreeDoc(doc);
574
575     /* Return the list of parsed identities. */
576     return 0;
577 }