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