261ce0c5c7185883b7e745b0fe4028a52c35712b
[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 static int parse_common(xmlDocPtr doc, xmlNodePtr ident, ident_config *common) {
105     xmlChar *id;
106     xmlChar *limit;
107     xmlChar *commfabric;
108     xmlChar *branch;
109     xmlChar *membership;
110     xmlChar *failure_behavior;
111     xmlChar *accounting;
112     xmlChar *ewma;
113     xmlChar *mainloop_intervals;
114     xmlChar *communication_intervals;
115     xmlChar *independent;
116     xmlChar *htb_node;
117     xmlChar *htb_parent;
118     xmlNodePtr fields = ident->children;
119     ident_peer *current = NULL;
120
121     /* The struct has been memsetted to 0, this is just to be safe. */
122     common->zk_host = NULL;
123     common->peers = NULL;
124     common->members = NULL;
125     common->next = NULL;
126
127     /* Make sure no required fields are missing. */
128     id = xmlGetProp(ident, (const xmlChar *) "id");
129     if (id == NULL) {
130         printlog(LOG_CRITICAL, "Ident missing globally unique identifier.\n");
131         return EINVAL;
132     } else {
133         common->id = atoi((const char *) id);
134         xmlFree(id);
135     }
136
137     limit = xmlGetProp(ident, (const xmlChar *) "limit");
138     if (limit == NULL) {
139         printlog(LOG_CRITICAL, "Ident missing global rate limit.\n");
140         return EINVAL;
141     } else {
142         common->limit = atoi((const char *) limit);
143         xmlFree(limit);
144     }
145
146     commfabric = xmlGetProp(ident, (const xmlChar *) "commfabric");
147     if (commfabric == NULL) {
148         printlog(LOG_CRITICAL, "Ident missing comm fabric specifier.\n");
149         return EINVAL;
150     } else {
151         if (!xmlStrcmp(commfabric, (const xmlChar *) "MESH")) {
152             common->commfabric = COMM_MESH;
153         } else if (!xmlStrcmp(commfabric, (const xmlChar *) "GOSSIP")) {
154             common->commfabric = COMM_GOSSIP;
155         } else {
156             printlog(LOG_CRITICAL, "Unknown/invalid comm fabric.\n");
157             xmlFree(commfabric);
158             return EINVAL;
159         }
160         xmlFree(commfabric);
161     }
162
163     /* Only care about branching factor and failure detector if we're using gossip. */
164     if (common->commfabric == COMM_GOSSIP) {
165         branch = xmlGetProp(ident, (const xmlChar *) "branch");
166         if (branch == NULL) {
167             printlog(LOG_CRITICAL, "Ident missing gossip branch.\n");
168             return EINVAL;
169         } else {
170             common->branch = atoi((const char *) branch);
171             xmlFree(branch);
172         }
173
174         membership = xmlGetProp(ident, (const xmlChar *) "membership");
175         if (membership == NULL) {
176             printlog(LOG_CRITICAL, "Ident missing membership protocol selection.\n");
177             return EINVAL;
178         } else {
179             if (!xmlStrcmp(membership, (const xmlChar *) "SWIM")) {
180                 common->membership = SWIM;
181             } else if (!xmlStrcmp(membership, (const xmlChar *) "ZOOKEEPER")) {
182 #ifdef BUILD_ZOOKEEPER
183                 common->membership = ZOOKEEPER;
184 #else
185                 printlog(LOG_CRITICAL, "Zookeeper requested, but support not compiled into DRL at configure time.\n");
186                 xmlFree(membership);
187                 return EINVAL;
188 #endif
189             } else {
190                 printlog(LOG_CRITICAL, "Unknown/invalid gossip group membership protocol.\n");
191                 xmlFree(membership);
192                 return EINVAL;
193             }
194             xmlFree(membership);
195         }
196
197         failure_behavior = xmlGetProp(ident, (const xmlChar *) "failure_behavior");
198         if (failure_behavior == NULL) {
199             printlog(LOG_CRITICAL, "Ident missing failure handling behavior.\n");
200             return EINVAL;
201         } else {
202             if (!xmlStrcmp(failure_behavior, (const xmlChar *) "PANIC")) {
203                 common->failure_behavior = PANIC;
204             } else if (!xmlStrcmp(failure_behavior, (const xmlChar *) "QUORUM")) {
205                 common->failure_behavior = QUORUM;
206             } else {
207                 printlog(LOG_CRITICAL, "Unknown/invalid gossip failure behavior policy.\n");
208                 xmlFree(failure_behavior);
209                 return EINVAL;
210             }
211             xmlFree(failure_behavior);
212         }
213     }
214
215     accounting = xmlGetProp(ident, (const xmlChar *) "accounting");
216     if (accounting == NULL) {
217         printlog(LOG_CRITICAL, "Ident missing accounting.\n");
218         return EINVAL;
219     } else {
220         if (!xmlStrcmp(accounting, (const xmlChar *) "STANDARD")) {
221             common->accounting = ACT_STANDARD;
222         } else if (!xmlStrcmp(accounting, (const xmlChar *) "SAMPLEHOLD")) {
223             common->accounting = ACT_SAMPLEHOLD;
224         } else if (!xmlStrcmp(accounting, (const xmlChar *) "SIMPLE")) {
225             common->accounting = ACT_SIMPLE;
226         } else if (!xmlStrcmp(accounting, (const xmlChar *) "MULTIPLEINTERVAL")) {
227             common->accounting = ACT_MULTIPLE;
228         } else {
229             printlog(LOG_CRITICAL, "Unknown/invalid accounting table.\n");
230             xmlFree(accounting);
231             return EINVAL;
232         }
233         xmlFree(accounting);
234     }
235
236     ewma = xmlGetProp(ident, (const xmlChar *) "ewma");
237     if (ewma == NULL) {
238         printlog(LOG_CRITICAL, "Ident missing ewma weight.\n");
239         return EINVAL;
240     } else {
241         common->fixed_ewma_weight = atof((const char *) ewma);
242         xmlFree(ewma);
243     }
244
245     mainloop_intervals = xmlGetProp(ident, (const xmlChar *) "loop_intervals");
246     if (mainloop_intervals == NULL) {
247         printlog(LOG_WARN, "Ident id: %d missing loop_intervals, assuming 1.\n", common->id);
248         common->mainloop_intervals = 1;
249     } else {
250         common->mainloop_intervals = atoi((const char *) mainloop_intervals);
251         xmlFree(mainloop_intervals);
252     }
253
254     communication_intervals = xmlGetProp(ident, (const xmlChar *) "comm_intervals");
255     if (communication_intervals == NULL) {
256         printlog(LOG_WARN, "Ident id: %d missing comm_intervals, assuming 1.\n", common->id);
257         common->communication_intervals = 1;
258     } else {
259         common->communication_intervals = atoi((const char *) communication_intervals);
260         xmlFree(communication_intervals);
261     }
262
263     independent = xmlGetProp(ident, (const xmlChar *) "independent");
264     if (independent == NULL) {
265         common->independent = 0;
266     } else {
267         common->independent = atoi((const char *) independent);
268         xmlFree(independent);
269     }
270
271     htb_node = xmlGetProp(ident, (const xmlChar *) "htb_node");
272     htb_parent = xmlGetProp(ident, (const xmlChar *) "htb_parent");
273     if (htb_node == NULL) {
274         common->htb_node = -1;
275     } else {
276         sscanf((const char *)htb_node, "%x", &common->htb_node);
277         xmlFree(htb_node);
278     }
279     if (htb_parent == NULL) {
280         common->htb_parent = -1;
281     } else {
282         sscanf((const char *)htb_parent, "%x", &common->htb_parent);
283         xmlFree(htb_parent);
284     }
285
286     while (fields != NULL) {
287         if((!xmlStrcmp(fields->name, (const xmlChar *) "peer"))) {
288             xmlChar *ip = xmlNodeListGetString(doc, fields->children, 1);
289             if (current == NULL) {
290                 /* No peers yet. */
291                 common->peers = malloc(sizeof(ident_peer));
292                 if (common->peers == NULL) {
293                     return ENOMEM;
294                 }
295                 common->peers->ip = inet_addr((const char *) ip);
296                 common->peers->next = NULL;
297                 common->peer_count += 1;
298                 current = common->peers;
299             } else {
300                 /* Add it to the list. */
301                 current->next = malloc(sizeof(ident_peer));
302                 if (current->next == NULL) {
303                     return ENOMEM;
304                 }
305                 current = current->next;
306                 current->ip = inet_addr((const char *) ip);
307                 common->peer_count += 1;
308                 current->next = NULL;
309             }
310             xmlFree(ip);
311         } else if ((!xmlStrcmp(fields->name, (const xmlChar *) "zkhost"))) {
312             xmlChar *host = xmlNodeListGetString(doc, fields->children, 1);
313
314             common->zk_host = strdup((const char *) host);
315             if (common->zk_host == NULL) {
316                 return ENOMEM;
317             }
318
319             xmlFree(host);
320         }
321         fields = fields->next;
322     }
323
324     if (common->peers == NULL) {
325         printlog(LOG_CRITICAL, "Must have at least one peer.\n");
326         return EINVAL;
327     }
328
329     if (common->membership == ZOOKEEPER && common->zk_host == NULL) {
330         printlog(LOG_CRITICAL, "Group membership protocol ZOOKEEPER requires a zkhost field.\n");
331         return EINVAL;
332     }
333
334     /* No errors. */
335     return 0;
336 }
337
338 static ident_config *parse_machine(xmlDocPtr doc, xmlNodePtr ident, parsed_configs *configs) {
339     ident_config *common = malloc(sizeof(ident_config));
340
341     if (common == NULL) {
342         return NULL;
343     }
344
345     memset(common, 0, sizeof(ident_config));
346     if (parse_common(doc, ident, common)) {
347         free_ident(common);
348         return NULL;
349     }
350
351     /* No further information needed for machine-level identities. */
352     common->type = IDENT_MACHINE;
353     common->members = NULL;
354     common->next = NULL;
355
356     if (configs->last_machine == NULL) {
357         configs->machines = common;
358         configs->last_machine = common;
359     } else {
360         configs->last_machine->next = common;
361         configs->last_machine = common;
362     }
363
364     configs->machine_count += 1;
365
366     return common;
367 }
368
369 static ident_config *parse_set(xmlDocPtr doc, xmlNodePtr ident, parsed_configs *configs) {
370     xmlNodePtr fields = ident->children;
371     ident_config *common = malloc(sizeof(ident_config));
372     ident_member *current = NULL;
373
374     if (common == NULL) {
375         return NULL;
376     }
377
378     memset(common, 0, sizeof(ident_config));
379     if (parse_common(doc, ident, common)) {
380         free_ident(common);
381         return NULL;
382     }
383
384     while (fields != NULL) {
385         ident_member *member = NULL;
386
387         if (!xmlStrcmp(fields->name, (const xmlChar *) "xid")) {
388             xmlChar *xid = xmlNodeListGetString(doc, fields->children, 1);
389
390             if (atoi((const char *) xid) >= 0) {
391                 member = malloc(sizeof(ident_member));
392                 if (member == NULL) {
393                     free_ident(common);
394                     xmlFree(xid);
395                     return NULL;
396                 }
397                 member->type = MEMBER_XID;
398                 sscanf((const char *) xid, "%x", &member->value);
399                 member->next = NULL;
400             } else {
401                 free_ident(common);
402                 xmlFree(xid);
403                 return NULL;
404             }
405
406             xmlFree(xid);
407         } else if (!xmlStrcmp(fields->name, (const xmlChar *) "guid")) {
408             xmlChar *guid = xmlNodeListGetString(doc, fields->children, 1);
409
410             if (atoi((const char *) guid) >= 0) {
411                 member = malloc(sizeof(ident_member));
412                 if (member == NULL) {
413                     free_ident(common);
414                     xmlFree(guid);
415                     return NULL;
416                 }
417                 member->type = MEMBER_GUID;
418                 member->value = atoi((const char *) guid);
419                 member->next = NULL;
420             } else {
421                 free_ident(common);
422                 xmlFree(guid);
423                 return NULL;
424             }
425
426             xmlFree(guid);
427         }
428
429         if (member) {
430             if (common->members == NULL) {
431                 common->members = member;
432                 current = member;
433             } else {
434                 current->next = member;
435                 current = member;
436             }
437         }
438
439         fields = fields->next;
440     }
441
442     /* A sliver set must have at least one member (xid or guid) or else it is
443      * meaningless. */
444     if (common->members == NULL) {
445         free_ident(common);
446         return NULL;
447     }
448
449     common->type = IDENT_SET;
450     common->next = NULL;
451
452     if (configs->last_set == NULL) {
453         configs->sets = common;
454         configs->last_set = common;
455     } else {
456         configs->last_set->next = common;
457         configs->last_set = common;
458     }
459
460     configs->set_count += 1;
461
462     return common;
463 }
464
465 int parse_drl_config(const char *configfile, parsed_configs *configs) {
466     xmlDocPtr doc;
467     xmlNodePtr drl, ident;
468
469     configs->machines = NULL;
470     configs->sets = NULL;
471     configs->last_machine = NULL;
472     configs->last_set = NULL;
473     configs->machine_count = 0;
474     configs->set_count = 0;
475
476     if(!(doc = xmlParseFile(configfile))){
477         printlog(LOG_CRITICAL, "Config file (%s) not parsed successfully.\n", configfile);
478         xmlFreeDoc(doc);
479         return EIO;
480     }
481
482     if(!(drl = xmlDocGetRootElement(doc))){
483         printlog(LOG_CRITICAL, "Config file (%s) has no root element.\n", configfile);
484         xmlFreeDoc(doc);
485         return EIO;
486     }
487     if(xmlStrcmp(drl->name, (const xmlChar *) "drl")){
488         printlog(LOG_CRITICAL, "Config file (%s) of the wrong type, root node != drl\n", configfile);
489         xmlFreeDoc(doc);
490         return EIO;
491     }
492
493     ident = drl->children;
494     while(ident != NULL) {
495         ident_config *new = NULL;
496
497         if((!xmlStrcmp(ident->name, (const xmlChar *) "machine"))) {
498             new = parse_machine(doc, ident, configs);
499         } else if ((!xmlStrcmp(ident->name, (const xmlChar *) "set"))) {
500             new = parse_set(doc, ident, configs);
501         } else if ((!xmlStrcmp(ident->name, (const xmlChar *) "text"))) {
502             /* libxml seems to wrap everything inside two 'text's. */
503             ident = ident->next;
504             continue;
505         }
506
507         if (new == NULL) {
508             /* FIXME: Make this more descriptive. :) */
509             printlog(LOG_CRITICAL, "Error occurred while parsing...\n");
510
511             free_ident_list(configs->machines);
512             free_ident_list(configs->sets);
513
514             configs->machines = NULL;
515             configs->sets = NULL;
516             configs->last_machine = NULL;
517             configs->last_set = NULL;
518
519             xmlFreeDoc(doc);
520             return 1;
521         }
522
523         ident = ident->next;
524     }
525
526     xmlFreeDoc(doc);
527
528     /* Return the list of parsed identities. */
529     return 0;
530 }