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