Lots of changes. In no particular order:
[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
83     for (i = 0; i < count; ++i) {
84         leaves[i].xid = atoi(names[i]->d_name);
85         leaves[i].parent = NULL;
86         
87         free(names[i]);
88
89         map_insert(leaf_map, &leaves[i].xid, sizeof(leaves[i].xid), &leaves[i]);
90     }
91
92     free(names);
93
94     instance->leaf_map = leaf_map;
95     instance->leaves = leaves;
96     instance->leaf_count = count;
97
98     return 0;
99 }
100
101 static int parse_common(xmlDocPtr doc, xmlNodePtr ident, ident_config *common) {
102     xmlChar *id;
103     xmlChar *limit;
104     xmlChar *commfabric;
105     xmlChar *branch;
106     xmlChar *accounting;
107     xmlChar *ewma;
108     xmlChar *mainloop_intervals;
109     xmlChar *communication_intervals;
110     xmlNodePtr fields = ident->children;
111     ident_peer *current = NULL;
112
113     /* Make sure no required fields are missing. */
114     id = xmlGetProp(ident, (const xmlChar *) "id");
115     if (id == NULL) {
116         printlog(LOG_CRITICAL, "Ident missing globally unique identifier.\n");
117         return EINVAL;
118     } else {
119         common->id = atoi((const char *) id);
120         xmlFree(id);
121     }
122
123     limit = xmlGetProp(ident, (const xmlChar *) "limit");
124     if (limit == NULL) {
125         printlog(LOG_CRITICAL, "Ident missing global rate limit.\n");
126         return EINVAL;
127     } else {
128         common->limit = atoi((const char *) limit);
129         xmlFree(limit);
130     }
131
132     commfabric = xmlGetProp(ident, (const xmlChar *) "commfabric");
133     if (commfabric == NULL) {
134         printlog(LOG_CRITICAL, "Ident missing comm fabric specifier.\n");
135         return EINVAL;
136     } else {
137         if (!xmlStrcmp(commfabric, (const xmlChar *) "MESH")) {
138             common->commfabric = COMM_MESH;
139         } else if (!xmlStrcmp(commfabric, (const xmlChar *) "GOSSIP")) {
140             common->commfabric = COMM_GOSSIP;
141         } else {
142             printlog(LOG_CRITICAL, "Unknown/invalid comm fabric.\n");
143             xmlFree(commfabric);
144             return EINVAL;
145         }
146         xmlFree(commfabric);
147     }
148
149     /* Only care about branching factor if we're using gossip. */
150     if (common->commfabric == COMM_GOSSIP) {
151         branch = xmlGetProp(ident, (const xmlChar *) "branch");
152         if (branch == NULL) {
153             printlog(LOG_CRITICAL, "Ident missing gossip branch.\n");
154             return EINVAL;
155         } else {
156             common->branch = atoi((const char *) branch);
157             xmlFree(branch);
158         }
159     }
160
161     accounting = xmlGetProp(ident, (const xmlChar *) "accounting");
162     if (accounting == NULL) {
163         printlog(LOG_CRITICAL, "Ident missing accounting.\n");
164         return EINVAL;
165     } else {
166         if (!xmlStrcmp(accounting, (const xmlChar *) "STANDARD")) {
167             common->accounting = ACT_STANDARD;
168         } else if (!xmlStrcmp(accounting, (const xmlChar *) "SAMPLEHOLD")) {
169             common->accounting = ACT_SAMPLEHOLD;
170         } else if (!xmlStrcmp(accounting, (const xmlChar *) "SIMPLE")) {
171             common->accounting = ACT_SIMPLE;
172         } else if (!xmlStrcmp(accounting, (const xmlChar *) "MULTIPLEINTERVAL")) {
173             common->accounting = ACT_MULTIPLE;
174         } else {
175             printlog(LOG_CRITICAL, "Unknown/invalid accounting table.\n");
176             xmlFree(accounting);
177             return EINVAL;
178         }
179         xmlFree(accounting);
180     }
181
182     ewma = xmlGetProp(ident, (const xmlChar *) "ewma");
183     if (ewma == NULL) {
184         printlog(LOG_CRITICAL, "Ident missing ewma weight.\n");
185         return EINVAL;
186     } else {
187         common->fixed_ewma_weight = atof((const char *) ewma);
188         xmlFree(ewma);
189     }
190
191     mainloop_intervals = xmlGetProp(ident, (const xmlChar *) "loop_intervals");
192     if (mainloop_intervals == NULL) {
193         printlog(LOG_WARN, "Ident id: %d missing loop_intervals, assuming 1.\n", common->id);
194         common->mainloop_intervals = 1;
195     } else {
196         common->mainloop_intervals = atoi((const char *) mainloop_intervals);
197         xmlFree(mainloop_intervals);
198     }
199
200     communication_intervals = xmlGetProp(ident, (const xmlChar *) "comm_intervals");
201     if (communication_intervals == NULL) {
202         printlog(LOG_WARN, "Ident id: %d missing comm_intervals, assuming 1.\n", common->id);
203         common->communication_intervals = 1;
204     } else {
205         common->communication_intervals = atoi((const char *) communication_intervals);
206         xmlFree(communication_intervals);
207     }
208
209     while (fields != NULL) {
210         if((!xmlStrcmp(fields->name, (const xmlChar *) "peer"))) {
211             xmlChar *ip = xmlNodeListGetString(doc, fields->children, 1);
212             if (current == NULL) {
213                 /* No peers yet. */
214                 common->peers = malloc(sizeof(ident_peer));
215                 if (common->peers == NULL) {
216                     return ENOMEM;
217                 }
218                 common->peers->ip = inet_addr((const char *) ip);
219                 common->peers->next = NULL;
220                 common->peer_count += 1;
221                 current = common->peers;
222             } else {
223                 /* Add it to the list. */
224                 current->next = malloc(sizeof(ident_peer));
225                 if (current->next == NULL) {
226                     return ENOMEM;
227                 }
228                 current = current->next;
229                 current->ip = inet_addr((const char *) ip);
230                 common->peer_count += 1;
231                 current->next = NULL;
232             }
233             xmlFree(ip);
234         }
235         fields = fields->next;
236     }
237
238     if (common->peers == NULL) {
239         printlog(LOG_CRITICAL, "Must have at least one peer.\n");
240         return EINVAL;
241     }
242
243     /* No errors. */
244     return 0;
245 }
246
247 static ident_config *parse_machine(xmlDocPtr doc, xmlNodePtr ident, parsed_configs *configs) {
248     ident_config *common = malloc(sizeof(ident_config));
249
250     if (common == NULL) {
251         return NULL;
252     }
253
254     memset(common, 0, sizeof(ident_config));
255     if (parse_common(doc, ident, common)) {
256         free_ident(common);
257         return NULL;
258     }
259
260     /* No further information needed for machine-level identities. */
261     common->type = IDENT_MACHINE;
262     common->members = NULL;
263     common->next = NULL;
264
265     if (configs->last_machine == NULL) {
266         configs->machines = common;
267         configs->last_machine = common;
268     } else {
269         configs->last_machine->next = common;
270         configs->last_machine = common;
271     }
272
273     configs->machine_count += 1;
274
275     return common;
276 }
277
278 static ident_config *parse_set(xmlDocPtr doc, xmlNodePtr ident, parsed_configs *configs) {
279     xmlNodePtr fields = ident->children;
280     ident_config *common = malloc(sizeof(ident_config));
281     ident_member *current = NULL;
282
283     if (common == NULL) {
284         return NULL;
285     }
286
287     memset(common, 0, sizeof(ident_config));
288     if (parse_common(doc, ident, common)) {
289         free_ident(common);
290         return NULL;
291     }
292
293     while (fields != NULL) {
294         ident_member *member = NULL;
295
296         if (!xmlStrcmp(fields->name, (const xmlChar *) "xid")) {
297             xmlChar *xid = xmlNodeListGetString(doc, fields->children, 1);
298
299             if (atoi((const char *) xid) >= 0) {
300                 member = malloc(sizeof(ident_member));
301                 if (member == NULL) {
302                     free_ident(common);
303                     xmlFree(xid);
304                     return NULL;
305                 }
306                 member->type = MEMBER_XID;
307                 sscanf((const char *) xid, "%x", &member->value);
308                 member->next = NULL;
309             } else {
310                 free_ident(common);
311                 xmlFree(xid);
312                 return NULL;
313             }
314
315             xmlFree(xid);
316         } else if (!xmlStrcmp(fields->name, (const xmlChar *) "guid")) {
317             xmlChar *guid = xmlNodeListGetString(doc, fields->children, 1);
318
319             if (atoi((const char *) guid) >= 0) {
320                 member = malloc(sizeof(ident_member));
321                 if (member == NULL) {
322                     free_ident(common);
323                     xmlFree(guid);
324                     return NULL;
325                 }
326                 member->type = MEMBER_GUID;
327                 member->value = atoi((const char *) guid);
328                 member->next = NULL;
329             } else {
330                 free_ident(common);
331                 xmlFree(guid);
332                 return NULL;
333             }
334
335             xmlFree(guid);
336         }
337
338         if (member) {
339             if (common->members == NULL) {
340                 common->members = member;
341                 current = member;
342             } else {
343                 current->next = member;
344                 current = member;
345             }
346         }
347
348         fields = fields->next;
349     }
350
351     /* A sliver set must have at least one member (xid or guid) or else it is
352      * meaningless. */
353     if (common->members == NULL) {
354         free_ident(common);
355         return NULL;
356     }
357
358     common->type = IDENT_SET;
359     common->next = NULL;
360
361     if (configs->last_set == NULL) {
362         configs->sets = common;
363         configs->last_set = common;
364     } else {
365         configs->last_set->next = common;
366         configs->last_set = common;
367     }
368
369     configs->set_count += 1;
370
371     return common;
372 }
373
374 int parse_drl_config(const char *configfile, parsed_configs *configs) {
375     xmlDocPtr doc;
376     xmlNodePtr drl, ident;
377
378     configs->machines = NULL;
379     configs->sets = NULL;
380     configs->last_machine = NULL;
381     configs->last_set = NULL;
382     configs->machine_count = 0;
383     configs->set_count = 0;
384
385     if(!(doc = xmlParseFile(configfile))){
386         printlog(LOG_CRITICAL, "Config file (%s) not parsed successfully.\n", configfile);
387         xmlFreeDoc(doc);
388         return EIO;
389     }
390
391     if(!(drl = xmlDocGetRootElement(doc))){
392         printlog(LOG_CRITICAL, "Config file (%s) has no root element.\n", configfile);
393         xmlFreeDoc(doc);
394         return EIO;
395     }
396     if(xmlStrcmp(drl->name, (const xmlChar *) "drl")){
397         printlog(LOG_CRITICAL, "Config file (%s) of the wrong type, root node != drl\n", configfile);
398         xmlFreeDoc(doc);
399         return EIO;
400     }
401
402     ident = drl->children;
403     while(ident != NULL) {
404         ident_config *new = NULL;
405
406         if((!xmlStrcmp(ident->name, (const xmlChar *) "machine"))) {
407             new = parse_machine(doc, ident, configs);
408         } else if ((!xmlStrcmp(ident->name, (const xmlChar *) "set"))) {
409             new = parse_set(doc, ident, configs);
410         } else if ((!xmlStrcmp(ident->name, (const xmlChar *) "text"))) {
411             /* libxml seems to wrap everything inside two 'text's. */
412             ident = ident->next;
413             continue;
414         }
415
416         if (new == NULL) {
417             /* FIXME: Make this more descriptive. :) */
418             printlog(LOG_CRITICAL, "Error occurred while parsing...\n");
419
420             free_ident_list(configs->machines);
421             free_ident_list(configs->sets);
422
423             configs->machines = NULL;
424             configs->sets = NULL;
425             configs->last_machine = NULL;
426             configs->last_set = NULL;
427
428             xmlFreeDoc(doc);
429             return 1;
430         }
431
432         ident = ident->next;
433     }
434
435     xmlFreeDoc(doc);
436
437     /* Return the list of parsed identities. */
438     return 0;
439 }