root/src/noit_conf.c

Revision 129a0bae7c19dce74dfb70efd8e069b47209c6d8, 33.8 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

show checks

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  */
5
6 #include "noit_defines.h"
7
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 #include <assert.h>
13 #include <libxml/parser.h>
14 #include <libxml/tree.h>
15 #include <libxml/xpath.h>
16
17 #include "noit_conf.h"
18 #include "noit_check.h"
19 #include "noit_console.h"
20 #include "utils/noit_hash.h"
21 #include "utils/noit_log.h"
22
23 /* tmp hash impl, replace this with something nice */
24 static noit_hash_table _tmp_config = NOIT_HASH_EMPTY;
25 static xmlDocPtr master_config = NULL;
26 static char master_config_file[PATH_MAX] = "";
27 static xmlXPathContextPtr xpath_ctxt = NULL;
28
29 static noit_hash_table _compiled_fallback = NOIT_HASH_EMPTY;
30 static struct {
31   const char *key;
32   const char *val;
33 } config_info[] = {
34   /*
35    * These are compile-time fallbacks to be used in the event
36    * that the current running config does not have values for
37    * these config paths.
38    *
39    * PLEASE: keep them alphabetically sorted.
40    */
41   { "/noit/modules/directory", MODULES_DIR },
42
43   { NULL, NULL }
44 };
45
46 static void register_console_config_commands();
47
48 void noit_conf_init() {
49   int i;
50   for(i = 0; config_info[i].key != NULL; i++) {
51     noit_hash_store(&_compiled_fallback,
52                     strdup(config_info[i].key), strlen(config_info[i].key),
53                     (void *)strdup(config_info[i].val));
54   }
55   xmlInitParser();
56   xmlXPathInit();
57   register_console_config_commands();
58 }
59
60 int noit_conf_load(const char *path) {
61   xmlDocPtr new_config;
62   new_config = xmlParseFile(path);
63   if(new_config) {
64     if(master_config) xmlFreeDoc(master_config);
65     if(xpath_ctxt) xmlXPathFreeContext(xpath_ctxt);
66
67     master_config = new_config;
68     xpath_ctxt = xmlXPathNewContext(master_config);
69     realpath(path, master_config_file);
70     return 0;
71   }
72   return -1;
73 }
74 int noit_conf_save(const char *path) {
75   return -1;
76 }
77
78 noit_hash_table *noit_conf_get_hash(noit_conf_section_t section,
79                                     const char *path) {
80   int i, cnt;
81   noit_hash_table *table = NULL;
82   xmlXPathObjectPtr pobj = NULL;
83   xmlXPathContextPtr current_ctxt;
84   xmlNodePtr current_node = (xmlNodePtr)section;
85   xmlNodePtr node;
86
87   current_ctxt = xpath_ctxt;
88   if(current_node) {
89     current_ctxt = xmlXPathNewContext(master_config);
90     current_ctxt->node = current_node;
91   }
92   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
93   if(!pobj) goto out;
94   if(pobj->type != XPATH_NODESET) goto out;
95   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
96   table = calloc(1, sizeof(*table));
97   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
98   for(i=0; i<cnt; i++) {
99     char *value;
100     node = xmlXPathNodeSetItem(pobj->nodesetval, i);
101     value = (char *)xmlXPathCastNodeToString(node);
102     noit_hash_replace(table,
103                       strdup((char *)node->name), strlen((char *)node->name),
104                       strdup(value), free, free);
105   }
106  out:
107   if(pobj) xmlXPathFreeObject(pobj);
108   if(current_ctxt && current_ctxt != xpath_ctxt)
109     xmlXPathFreeContext(current_ctxt);
110   return table;
111 }
112 noit_conf_section_t noit_conf_get_section(noit_conf_section_t section,
113                                           const char *path) {
114   noit_conf_section_t subsection = NULL;
115   xmlXPathObjectPtr pobj = NULL;
116   xmlXPathContextPtr current_ctxt;
117   xmlNodePtr current_node = (xmlNodePtr)section;
118
119   current_ctxt = xpath_ctxt;
120   if(current_node) {
121     current_ctxt = xmlXPathNewContext(master_config);
122     current_ctxt->node = current_node;
123   }
124   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
125   if(!pobj) goto out;
126   if(pobj->type != XPATH_NODESET) goto out;
127   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
128   subsection = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
129  out:
130   if(pobj) xmlXPathFreeObject(pobj);
131   if(current_ctxt && current_ctxt != xpath_ctxt)
132     xmlXPathFreeContext(current_ctxt);
133   return subsection;
134 }
135 noit_conf_section_t *noit_conf_get_sections(noit_conf_section_t section,
136                                             const char *path,
137                                             int *cnt) {
138   int i;
139   noit_conf_section_t *sections = NULL;
140   xmlXPathObjectPtr pobj = NULL;
141   xmlXPathContextPtr current_ctxt;
142   xmlNodePtr current_node = (xmlNodePtr)section;
143
144   *cnt = 0;
145   current_ctxt = xpath_ctxt;
146   if(current_node) {
147     current_ctxt = xmlXPathNewContext(master_config);
148     current_ctxt->node = current_node;
149   }
150   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
151   if(!pobj) goto out;
152   if(pobj->type != XPATH_NODESET) goto out;
153   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
154   *cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
155   sections = calloc(*cnt, sizeof(*sections));
156   for(i=0; i<*cnt; i++)
157     sections[i] = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
158  out:
159   if(pobj) xmlXPathFreeObject(pobj);
160   if(current_ctxt && current_ctxt != xpath_ctxt)
161     xmlXPathFreeContext(current_ctxt);
162   return sections;
163 }
164 int _noit_conf_get_string(noit_conf_section_t section, xmlNodePtr *vnode,
165                           const char *path, char **value) {
166   char *str;
167   int i;
168   xmlXPathObjectPtr pobj;
169   xmlXPathContextPtr current_ctxt;
170   xmlNodePtr current_node = (xmlNodePtr)section;
171
172   current_ctxt = xpath_ctxt;
173   if(current_node) {
174     current_ctxt = xmlXPathNewContext(master_config);
175     current_ctxt->node = current_node;
176   }
177   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
178   if(pobj) {
179     xmlNodePtr node;
180     switch(pobj->type) {
181       case XPATH_NODESET:
182         if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) return 0;
183         i = xmlXPathNodeSetGetLength(pobj->nodesetval);
184         node = xmlXPathNodeSetItem(pobj->nodesetval, i-1);
185         if(vnode) *vnode = node;
186         *value = (char *)xmlXPathCastNodeToString(node);
187         break;
188       default:
189         *value = (char *)xmlXPathCastToString(pobj);
190     }
191     goto found;
192   }
193   if(noit_hash_retrieve(&_compiled_fallback,
194                         path, strlen(path), (void **)&str)) {
195     *value = str;
196     goto found;
197   }
198   return 0;
199  found:
200   if(current_ctxt && current_ctxt != xpath_ctxt)
201     xmlXPathFreeContext(current_ctxt);
202   return 1;
203 }
204 int noit_conf_get_string(noit_conf_section_t section,
205                          const char *path, char **value) {
206   char *str;
207   if(_noit_conf_get_string(section,NULL,path,&str)) {
208     *value = strdup(str);
209     return 1;
210   }
211   return 0;
212 }
213 int noit_conf_get_stringbuf(noit_conf_section_t section,
214                             const char *path, char *buf, int len) {
215   char *str;
216   if(_noit_conf_get_string(section,NULL,path,&str)) {
217     strlcpy(buf, str, len);
218     return 1;
219   }
220   return 0;
221 }
222 int noit_conf_set_string(noit_conf_section_t section,
223                          const char *path, const char *value) {
224   noit_hash_replace(&_tmp_config,
225                     strdup(path), strlen(path), (void *)strdup(value),
226                     free, free);
227   return 1;
228 }
229 int noit_conf_get_int(noit_conf_section_t section,
230                       const char *path, int *value) {
231   char *str;
232   long longval;
233   if(_noit_conf_get_string(section,NULL,path,&str)) {
234     int base = 10;
235     if(str[0] == '0') {
236       if(str[1] == 'x') base = 16;
237       else base = 8;
238     }
239     longval = strtol(str, NULL, base);
240     *value = (int)longval;
241     return 1;
242   }
243   return 0;
244 }
245 int noit_conf_set_int(noit_conf_section_t section,
246                       const char *path, int value) {
247   char buffer[32];
248   snprintf(buffer, 32, "%d", value);
249   return noit_conf_set_string(section,path,buffer);
250 }
251 int noit_conf_get_float(noit_conf_section_t section,
252                         const char *path, float *value) {
253   char *str;
254   if(_noit_conf_get_string(section,NULL,path,&str)) {
255     *value = atof(str);
256     return 1;
257   }
258   return 0;
259 }
260 int noit_conf_set_float(noit_conf_section_t section,
261                         const char *path, float value) {
262   char buffer[32];
263   snprintf(buffer, 32, "%f", value);
264   return noit_conf_set_string(section,path,buffer);
265 }
266 int noit_conf_get_boolean(noit_conf_section_t section,
267                           const char *path, noit_conf_boolean *value) {
268   char *str;
269   if(_noit_conf_get_string(section,NULL,path,&str)) {
270     if(!strcasecmp(str, "true")) *value = noit_true;
271     else *value = noit_false;
272     return 1;
273   }
274   return 0;
275 }
276 int noit_conf_set_boolean(noit_conf_section_t section,
277                           const char *path, noit_conf_boolean value) {
278   if(value == noit_true)
279     return noit_conf_set_string(section,path,"true");
280   return noit_conf_set_string(section,path,"false");
281 }
282
283 static void
284 conf_t_userdata_free(void *data) {
285   noit_conf_t_userdata_t *info = data;
286   if(info) {
287     if(info->path) free(info->path);
288     free(info);
289   }
290 }
291 static int
292 noit_console_state_conf_terminal(noit_console_closure_t ncct,
293                                  int argc, char **argv,
294                                  noit_console_state_t *state, void *closure) {
295   noit_conf_t_userdata_t *info;
296   if(argc) {
297     nc_printf(ncct, "extra arguments not expected.\n");
298     return -1;
299   }
300   info = calloc(1, sizeof(*info));
301   info->path = strdup("/");
302   noit_console_userdata_set(ncct, NOIT_CONF_T_USERDATA, info,
303                             conf_t_userdata_free);
304   noit_console_state_push_state(ncct, state);
305   noit_console_state_init(ncct);
306   return 0;
307 }
308 static int
309 noit_console_mkcheck_xpath(char *xpath, int len,
310                            noit_conf_t_userdata_t *info,
311                            const char *arg) {
312   uuid_t checkid;
313   char argcopy[1024], *target, *name;
314
315   strlcpy(argcopy, arg, sizeof(argcopy));
316   if(uuid_parse(argcopy, checkid) == 0) {
317     /* If they kill by uuid, we'll seek and destroy -- find it anywhere */
318     snprintf(xpath, len, "/noit/checks//check[@uuid=\"%s\"]",
319              argcopy);
320   }
321   else if((name = strchr(argcopy, '`')) != NULL) {
322     noit_check_t *check;
323     char uuid_str[37];
324     target = argcopy;
325     *name++ = '\0';
326     check = noit_poller_lookup_by_name(target, name);
327     if(!check) {
328       return -1;
329     }
330     uuid_unparse_lower(check->checkid, uuid_str);
331     snprintf(xpath, len, "/noit/checks//check[@uuid=\"%s\"]",
332              uuid_str);
333   }
334   else {
335     char *path = (!info || !strcmp(info->path, "/")) ? "" : info->path;
336     snprintf(xpath, len, "/noit%s/%s[@uuid]", path, arg);
337   }
338   return 0;
339 }
340 static void
341 nc_attr_show(noit_console_closure_t ncct, const char *name, xmlNodePtr cnode,
342              xmlNodePtr anode, const char *value) {
343   const char *cpath, *apath;
344   cpath = cnode ? (char *)xmlGetNodePath(cnode) : "";
345   apath = anode ? (char *)xmlGetNodePath(anode) : "";
346   nc_printf(ncct, " %s: %s", name, value ? value : "[undef]");
347   if(value && cpath && apath) {
348     int clen = strlen(cpath);
349     int plen = strlen("/noit/checks/");
350     if(!strncmp(cpath, apath, clen) && apath[clen] == '/') {
351       /* we have a match, which means it isn't inherited */
352     }
353     else {
354       nc_printf(ncct, " [inherited from %s]",
355                 strlen(apath) > plen ? apath + plen : apath);
356     }
357   }
358   nc_write(ncct, "\n", 1);
359 }
360 static int
361 noit_console_check(noit_console_closure_t ncct,
362                    int argc, char **argv,
363                    noit_console_state_t *state, void *closure) {
364   int i, cnt;
365   noit_conf_t_userdata_t *info;
366   char xpath[1024];
367   xmlXPathObjectPtr pobj = NULL;
368
369   if(argc > 1) {
370     nc_printf(ncct, "requires zero or one arguments\n");
371     return -1;
372   }
373
374   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
375   /* We many not be in conf-t mode -- that's fine */
376   if(noit_console_mkcheck_xpath(xpath, sizeof(xpath), info,
377                                 argc ? argv[0] : ".")) {
378     nc_printf(ncct, "could not find check '%s'\n", argv[0]);
379     return -1;
380   }
381
382   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
383   if(!pobj || pobj->type != XPATH_NODESET ||
384      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
385     nc_printf(ncct, "no checks found\n");
386     goto out;
387   }
388   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
389   if(info && cnt != 1) {
390     nc_printf(ncct, "Ambiguous check specified\n");
391     goto out;
392   }
393   for(i=0; i<cnt; i++) {
394     uuid_t checkid;
395     xmlNodePtr node, anode, mnode = NULL;
396     char *uuid_conf;
397     char *module, *value;
398
399     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
400     if(info) {
401       if(info->path) free(info->path);
402       info->path = strdup((char *)xmlGetNodePath(node) + strlen("/noit"));
403     }
404     uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
405     if(!uuid_conf || uuid_parse(uuid_conf, checkid)) {
406       nc_printf(ncct, "%s has invalid or missing UUID!\n",
407                 (char *)xmlGetNodePath(node) + strlen("/noit"));
408       continue;
409     }
410     nc_printf(ncct, "==== %s ====\n", uuid_conf);
411
412 #define MYATTR(a,n,b) _noit_conf_get_string(node, &(n), "@" #a, &(b))
413 #define INHERIT(a,n,b) \
414   _noit_conf_get_string(node, &(n), "ancestor-or-self::node()/@" #a, &(b))
415 #define SHOW_ATTR(a) do { \
416   anode = NULL; \
417   value = NULL; \
418   INHERIT(a, anode, value); \
419   nc_attr_show(ncct, #a, node, anode, value); \
420 } while(0)
421
422     if(!INHERIT(module, mnode, module)) module = NULL;
423     if(MYATTR(name, anode, value))
424       nc_printf(ncct, " name: %s\n", value);
425     else
426       nc_printf(ncct, " name: %s [from module]\n", module ? module : "[undef]");
427     nc_attr_show(ncct, "module", node, mnode, module);
428     SHOW_ATTR(target);
429     SHOW_ATTR(period);
430     SHOW_ATTR(timeout);
431     SHOW_ATTR(oncheck);
432   }
433  out:
434   if(pobj) xmlXPathFreeObject(pobj);
435   return 0;
436 }
437 static int
438 noit_console_config_nocheck(noit_console_closure_t ncct,
439                             int argc, char **argv,
440                             noit_console_state_t *state, void *closure) {
441   int i, cnt;
442   const char *err = "internal error";
443   noit_conf_t_userdata_t *info;
444   xmlXPathObjectPtr pobj = NULL;
445   char xpath[1024];
446   uuid_t checkid;
447
448   if(argc != 1) {
449     nc_printf(ncct, "requires one argument\n");
450     return -1;
451   }
452
453   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
454   if(noit_console_mkcheck_xpath(xpath, sizeof(xpath), info, argv[0])) {
455     nc_printf(ncct, "could not find check '%s'\n", argv[0]);
456     return -1;
457   }
458   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
459   if(!pobj || pobj->type != XPATH_NODESET ||
460      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
461     err = "no checks found";
462     goto bad;
463   }
464   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
465   for(i=0; i<cnt; i++) {
466     xmlNodePtr node;
467     char *uuid_conf;
468     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
469     uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
470     if(!uuid_conf || uuid_parse(uuid_conf, checkid)) {
471       nc_printf(ncct, "%s has invalid or missing UUID!\n",
472                 (char *)xmlGetNodePath(node) + strlen("/noit"));
473     }
474     else {
475       nc_printf(ncct, "descheduling %s\n", uuid_conf);
476       noit_poller_deschedule(checkid);
477       xmlUnlinkNode(node);
478     }
479   }
480   nc_printf(ncct, "rebuilding causal map...\n");
481   noit_poller_make_causal_map();
482   if(pobj) xmlXPathFreeObject(pobj);
483   return 0;
484  bad:
485   if(pobj) xmlXPathFreeObject(pobj);
486   nc_printf(ncct, "%s\n", err);
487   return -1;
488 }
489 static int
490 noit_console_config_section(noit_console_closure_t ncct,
491                             int argc, char **argv,
492                             noit_console_state_t *state, void *closure) {
493   const char *err = "internal error";
494   char *path, xpath[1024];
495   noit_conf_t_userdata_t *info;
496   xmlXPathObjectPtr pobj = NULL;
497   xmlNodePtr node = NULL, newnode;
498   vpsized_int delete = (vpsized_int)closure;
499
500   if(argc != 1) {
501     nc_printf(ncct, "requires one argument\n");
502     return -1;
503   }
504   if(strchr(argv[0], '/')) {
505     nc_printf(ncct, "invalid section name\n");
506     return -1;
507   }
508   if(!strcmp(argv[0], "check")) {
509     nc_printf(ncct, "use 'check' to create checks\n");
510     return -1;
511   }
512   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
513   if(!strcmp(info->path, "/")) {
514     nc_printf(ncct, "manipulation of toplevel section disallowed\n");
515     return -1;
516   }
517
518   if(delete) {
519     /* We cannot delete if we have checks */
520     snprintf(xpath, sizeof(xpath), "/noit%s/%s//check", info->path, argv[0]);
521     pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
522     if(!pobj || pobj->type != XPATH_NODESET ||
523        !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
524       err = "cannot delete section, has checks";
525       goto bad;
526     }
527     if(pobj) xmlXPathFreeObject(pobj);
528   }
529
530   snprintf(xpath, sizeof(xpath), "/noit%s/%s", info->path, argv[0]);
531   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
532   if(!pobj || pobj->type != XPATH_NODESET) {
533     err = "internal error: cannot detect section";
534     goto bad;
535   }
536   if(!delete && !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
537     err = "cannot create section";
538     goto bad;
539   }
540   if(delete && xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
541     err = "no such section";
542     goto bad;
543   }
544   if(delete) {
545     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
546     xmlUnlinkNode(node);
547     return 0;
548   }
549   if(pobj) xmlXPathFreeObject(pobj);
550
551   path = strcmp(info->path, "/") ? info->path : "";
552   snprintf(xpath, sizeof(xpath), "/noit%s", path);
553   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
554   if(!pobj || pobj->type != XPATH_NODESET ||
555      xmlXPathNodeSetGetLength(pobj->nodesetval) != 1) {
556     err = "path invalid?";
557     goto bad;
558   }
559   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
560   if((newnode = xmlNewChild(node, NULL, (xmlChar *)argv[0], NULL)) != NULL)
561     info->path = strdup((char *)xmlGetNodePath(newnode) + strlen("/noit"));
562   else {
563     err = "failed to create section";
564     goto bad;
565   }
566   if(pobj) xmlXPathFreeObject(pobj);
567   return 0;
568  bad:
569   if(pobj) xmlXPathFreeObject(pobj);
570   nc_printf(ncct, "%s\n", err);
571   return -1;
572 }
573
574 static int
575 noit_console_config_cd(noit_console_closure_t ncct,
576                        int argc, char **argv,
577                        noit_console_state_t *state, void *closure) {
578   const char *err = "internal error";
579   char *path, xpath[1024];
580   noit_conf_t_userdata_t *info;
581   xmlXPathObjectPtr pobj = NULL;
582   xmlXPathContextPtr current_ctxt;
583   xmlNodePtr node = NULL;
584
585   if(argc != 1) {
586     nc_printf(ncct, "requires one argument\n");
587     return -1;
588   }
589   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
590   if(argv[0][0] == '/')
591     snprintf(xpath, sizeof(xpath), "/noit%s", argv[0]);
592   else {
593     snprintf(xpath, sizeof(xpath), "/noit%s/%s", info->path, argv[0]);
594   }
595   if(xpath[strlen(xpath)-1] == '/') xpath[strlen(xpath)-1] = '\0';
596
597   current_ctxt = xpath_ctxt;
598   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
599   if(!pobj || pobj->type != XPATH_NODESET ||
600      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
601     err = "no such section";
602     goto bad;
603   }
604   if(xmlXPathNodeSetGetLength(pobj->nodesetval) > 1) {
605     err = "ambiguous section";
606     goto bad;
607   }
608
609   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
610   if(!strcmp((char *)node->name, "check")) {
611     err = "can't cd into a check, use 'check' instead";
612     goto bad;
613   }
614   path = (char *)xmlGetNodePath(node);
615   if(strncmp(path, "/noit/", strlen("/noit/")) && strcmp(path, "/noit")) {
616     err = "new path outside out tree";
617     goto bad;
618   }
619   free(info->path);
620   if(!strcmp(path, "/noit"))
621     info->path = strdup("/");
622   else
623     info->path = strdup((char *)xmlGetNodePath(node) + strlen("/noit"));
624   if(pobj) xmlXPathFreeObject(pobj);
625   return 0;
626  bad:
627   if(pobj) xmlXPathFreeObject(pobj);
628   nc_printf(ncct, "%s\n", err);
629   return -1;
630 }
631 static int
632 noit_console_config_show(noit_console_closure_t ncct,
633                          int argc, char **argv,
634                          noit_console_state_t *state, void *closure) {
635   int i, cnt, titled = 0, cliplen = 0;
636   const char *path, *basepath = NULL;
637   char xpath[1024];
638   noit_conf_t_userdata_t *info = NULL;
639   xmlXPathObjectPtr pobj = NULL;
640   xmlXPathContextPtr current_ctxt;
641   xmlNodePtr node;
642
643   if(argc > 1) {
644     nc_printf(ncct, "too many arguments\n");
645     return -1;
646   }
647
648   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
649   if(info) path = basepath = info->path;
650   if(!info && argc == 0) {
651     nc_printf(ncct, "argument required when not in configuration mode\n");
652     return -1;
653   }
654
655   if(argc == 1) path = argv[0];
656   if(!basepath) basepath = path;
657
658   /* { / } is a special case */
659   if(!strcmp(basepath, "/")) basepath = "";
660   if(!strcmp(path, "/")) path = "";
661
662   if(!master_config) {
663     nc_printf(ncct, "no config\n");
664     return -1;
665   }
666
667   /* { / } is the only path that will end with a /
668    * in XPath { / / * } means something _entirely different than { / * }
669    * Ever notice how it is hard to describe xpath in C comments?
670    */
671   /* We don't want to show the root node */
672   cliplen = strlen("/noit/");
673
674   /* If we are in configuration mode
675    * and we are without an argument or the argument is absolute,
676    * clip the current path off */
677   if(info && (argc == 0 || path[0] != '/')) cliplen += strlen(basepath);
678   if(!path[0] || path[0] == '/') /* base only, or absolute path requested */
679     snprintf(xpath, sizeof(xpath), "/noit%s/@*", path);
680   else
681     snprintf(xpath, sizeof(xpath), "/noit%s/%s/@*", basepath, path);
682
683   current_ctxt = xpath_ctxt;
684   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
685   if(!pobj || pobj->type != XPATH_NODESET) {
686     nc_printf(ncct, "no such object\n");
687     goto bad;
688   }
689   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
690   titled = 0;
691   for(i=0; i<cnt; i++) {
692     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
693     if(!strcmp((char *)node->name, "check")) continue;
694     if(node->children && node->children == xmlGetLastChild(node) &&
695       xmlNodeIsText(node->children)) {
696       if(!titled++) nc_printf(ncct, "== Section Settings ==\n");
697       nc_printf(ncct, "%s: %s\n", xmlGetNodePath(node) + cliplen,
698                 xmlXPathCastNodeToString(node->children));
699     }
700   }
701   xmlXPathFreeObject(pobj);
702
703   /* _shorten string_ turning last { / @ * } to { / * } */
704   strlcpy(xpath + strlen(xpath) - 2, "*", 2);
705 nc_printf(ncct, "Looking up path '%s'\n", xpath);
706   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
707   if(!pobj || pobj->type != XPATH_NODESET) {
708     nc_printf(ncct, "no such object\n");
709     goto bad;
710   }
711   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
712   titled = 0;
713   for(i=0; i<cnt; i++) {
714     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
715     if(!strcmp((char *)node->name, "check")) continue;
716     if(!(node->children && node->children == xmlGetLastChild(node) &&
717          xmlNodeIsText(node->children))) {
718       if(!titled++) nc_printf(ncct, "== Subsections ==\n");
719       nc_printf(ncct, "%s\n", xmlGetNodePath(node) + cliplen);
720     }
721   }
722
723   titled = 0;
724   for(i=0; i<cnt; i++) {
725     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
726     if(!strcmp((char *)node->name, "check")) {
727       int busted = 1;
728       xmlAttr *attr;
729       char *uuid_str = "undefined";;
730
731       if(!titled++) nc_printf(ncct, "== Checks ==\n");
732
733       for(attr=node->properties; attr; attr = attr->next) {
734         if(!strcmp((char *)attr->name, "uuid"))
735           uuid_str = (char *)xmlXPathCastNodeToString(attr->children);
736       }
737       if(uuid_str) {
738         uuid_t checkid;
739         nc_printf(ncct, "check[@uuid=\"%s\"] ", uuid_str);
740         if(uuid_parse(uuid_str, checkid) == 0) {
741           noit_check_t *check;
742           check = noit_poller_lookup(checkid);
743           if(check) {
744             busted = 0;
745             nc_printf(ncct, "%s`%s", check->target, check->name);
746           }
747         }
748       }
749       else
750         nc_printf(ncct, "%s ", xmlGetNodePath(node) + cliplen);
751       if(busted) nc_printf(ncct, "[check not in running system]");
752       nc_write(ncct, "\n", 1);
753     }
754   }
755   xmlXPathFreeObject(pobj);
756   return 0;
757  bad:
758   if(pobj) xmlXPathFreeObject(pobj);
759   return -1;
760 }
761
762 static char *
763 conf_t_prompt(EditLine *el) {
764   noit_console_closure_t ncct;
765   noit_conf_t_userdata_t *info;
766   static char *tl = "noit(conf)# ";
767   static char *pfmt = "noit(conf:%s%s)# ";
768   int path_len, max_len;
769
770   el_get(el, EL_USERDATA, (void *)&ncct);
771   if(!ncct) return tl;
772   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
773   if(!info) return tl;
774
775   path_len = strlen(info->path);
776   max_len = sizeof(info->prompt) - (strlen(pfmt) - 4 /* %s%s */) - 1 /* \0 */;
777   if(path_len > max_len)
778     snprintf(info->prompt, sizeof(info->prompt),
779              pfmt, "...", info->path + path_len - max_len + 3 /* ... */);
780   else
781     snprintf(info->prompt, sizeof(info->prompt), pfmt, "", info->path);
782   return info->prompt;
783 }
784 static int
785 noit_conf_reload(noit_console_closure_t ncct,
786                  int argc, char **argv,
787                  noit_console_state_t *state, void *closure) {
788   if(noit_conf_load(master_config_file)) {
789     nc_printf(ncct, "error loading config\n");
790     return -1;
791   }
792   noit_poller_reload(NULL);
793   return 0;
794 }
795 static int
796 noit_console_write_xml(void *vncct, const char *buffer, int len) {
797   noit_console_closure_t ncct = vncct;
798   return nc_write(ncct, buffer, len);
799 }
800 static int
801 noit_console_close_xml(void *vncct) {
802   return 0;
803 }
804 static int
805 noit_conf_write_terminal(noit_console_closure_t ncct,
806                          int argc, char **argv,
807                          noit_console_state_t *state, void *closure) {
808   xmlOutputBufferPtr out;
809   xmlCharEncodingHandlerPtr enc;
810   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
811   out = xmlOutputBufferCreateIO(noit_console_write_xml,
812                                 noit_console_close_xml,
813                                 ncct, enc);
814   xmlSaveFileTo(out, master_config, "utf8");
815   return 0;
816 }
817 static int
818 noit_conf_write_file(noit_console_closure_t ncct,
819                      int argc, char **argv,
820                      noit_console_state_t *state, void *closure) {
821   int fd, len;
822   char master_file_tmp[PATH_MAX];
823   xmlOutputBufferPtr out;
824   xmlCharEncodingHandlerPtr enc;
825
826   snprintf(master_file_tmp, sizeof(master_file_tmp),
827            "%s.tmp", master_config_file);
828   unlink(master_file_tmp);
829   fd = open(master_file_tmp, O_CREAT|O_EXCL|O_WRONLY, 0640);
830   if(fd < 0) {
831     nc_printf(ncct, "Failed to open tmp file: %s\n", strerror(errno));
832     return -1;
833   }
834   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
835   out = xmlOutputBufferCreateFd(fd, enc);
836   if(!out) {
837     close(fd);
838     unlink(master_file_tmp);
839     nc_printf(ncct, "internal error: OutputBufferCreate failed\n");
840     return -1;
841   }
842   len = xmlSaveFileTo(out, master_config, "utf8");
843   close(fd);
844   if(len <= 0) {
845     nc_printf(ncct, "internal error: writing to tmp file failed.\n");
846     return -1;
847   }
848   if(rename(master_file_tmp, master_config_file) != 0) {
849     nc_printf(ncct, "Failed to replace file: %s\n", strerror(errno));
850     return -1;
851   }
852   nc_printf(ncct, "%d bytes written.\n", len);
853   return 0;
854 }
855
856 static struct _valid_attr_t {
857   const char *scope;
858   const char *name;
859   const char *xpath;
860   int checks_fixate;
861 } valid_attrs[] = {
862   { "/checks", "name", "@name", 0 },
863   { "/checks", "target", "@target", 0 },
864   { "/checks", "period", "@period", 0 },
865   { "/checks", "timeout", "@timeout", 0 },
866   { "/checks", "oncheck", "@oncheck", 0 },
867   { "/checks", "module", "@module", 1 },
868 };
869
870 void
871 noit_console_state_add_check_attrs(noit_console_state_t *state,
872                                    console_cmd_func_t f) {
873   int i;
874   for(i = 0;
875       i < sizeof(valid_attrs)/sizeof(valid_attrs[0]);
876       i++) {
877     noit_console_state_add_cmd(state,
878       NCSCMD(valid_attrs[i].name, f,
879              NULL, &valid_attrs[i]));
880   }
881 }
882
883 static int
884 validate_attr_set_scope(noit_conf_t_userdata_t *info,
885                         struct _valid_attr_t *attrinfo) {
886   int len;
887   len = strlen(attrinfo->scope);
888   if(strncmp(info->path, attrinfo->scope, len) ||
889      (info->path[len] != '\0' && info->path[len] != '/')) {
890     return -1;
891   }
892   return 0;
893 }
894 static int
895 replace_attr(noit_console_closure_t ncct,
896              noit_conf_t_userdata_t *info, struct _valid_attr_t *attrinfo,
897              const char *value) {
898   int cnt, rv = -1;
899   xmlXPathObjectPtr pobj = NULL;
900   xmlNodePtr node;
901   char xpath[1024], *path;
902
903   path = info->path;
904   if(!strcmp(path, "/")) path = "";
905
906   if(attrinfo->checks_fixate) {
907     /* Only if checks will fixate this attribute shall we check for
908      * child <check> nodes.
909      * NOTE: this return nothing and "seems" okay if we are _in_
910      *       a <check> node.  That case is handled below.
911      */
912     snprintf(xpath, sizeof(xpath), "/noit/%s//check[@uuid]", path);
913     pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
914     if(pobj && pobj->type == XPATH_NODESET &&
915        !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
916       nc_printf(ncct, "Cannot set '%s', it would effect live checks\n",
917                 attrinfo->name);
918       goto out;
919     }
920     if(pobj) xmlXPathFreeObject(pobj);
921   }
922   snprintf(xpath, sizeof(xpath), "/noit/%s", path);
923   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
924   if(!pobj || pobj->type != XPATH_NODESET) goto out;
925   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
926   if(cnt != 1) {
927     nc_printf(ncct, "Internal error: context node disappeared\n");
928     goto out;
929   }
930   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
931   if(attrinfo->checks_fixate &&
932      !strcmp((const char *)node->name, "check")) {
933     /* Detect if  we are actually a <check> node and attempting to
934      * change something we shouldn't.
935      * This is the counterpart noted above.
936      */
937     nc_printf(ncct, "Cannot set '%s', it would effect live checks\n");
938     goto out;
939   }
940   xmlUnsetProp(node, (xmlChar *)attrinfo->name);
941   if(value)
942     xmlSetProp(node, (xmlChar *)attrinfo->name, (xmlChar *)value);
943   rv = 0;
944  out:
945   if(pobj) xmlXPathFreeObject(pobj);
946   return rv;
947 }
948 static void
949 refresh_subchecks(noit_console_closure_t ncct,
950                   noit_conf_t_userdata_t *info) {
951   char *path;
952   char xpath[1024];
953  
954   path = info->path;
955   if(!strcmp(path, "/")) path = "";
956
957   /* The first one is just a process_checks, the second is the reload.
958    * Reload does a lot of work and there is no need to do it twice.
959    */
960   snprintf(xpath, sizeof(xpath), "/noit/%s[@uuid]", path);
961   noit_poller_process_checks(xpath);
962   snprintf(xpath, sizeof(xpath), "/noit/%s//check[@uuid]", path);
963   noit_poller_reload(xpath);
964 }
965 int
966 noit_conf_check_set_attr(noit_console_closure_t ncct,
967                          int argc, char **argv,
968                          noit_console_state_t *state, void *closure) {
969   struct _valid_attr_t *attrinfo = closure;
970   noit_conf_t_userdata_t *info;
971
972   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
973   if(!info || validate_attr_set_scope(info, attrinfo)) {
974     nc_printf(ncct, "'%s' attribute only valid in %s scope\n",
975               attrinfo->name, attrinfo->scope);
976     return -1;
977   }
978
979   if(argc != 1) {
980     nc_printf(ncct, "set requires exactly one value\n");
981     return -1;
982   }
983   /* Okay, we have an attribute and it should be set/replaced on the
984    * current path.
985    */
986   if(replace_attr(ncct, info, attrinfo, argv[0])) {
987     return -1;
988   }
989
990   /* So, we updated an attribute, so we need to reload all checks
991    * that are descendent-or-self of this node.
992    */
993   refresh_subchecks(ncct, info);
994   return 0;
995 }
996
997 int
998 noit_conf_check_unset_attr(noit_console_closure_t ncct,
999                            int argc, char **argv,
1000                            noit_console_state_t *state, void *closure) {
1001   struct _valid_attr_t *attrinfo = closure;
1002   noit_conf_t_userdata_t *info;
1003
1004   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
1005   if(!info || validate_attr_set_scope(info, attrinfo)) {
1006     nc_printf(ncct, "'%s' attribute only valid in %s scope\n",
1007               attrinfo->name, attrinfo->scope);
1008     return -1;
1009   }
1010
1011   if(argc != 0) {
1012     nc_printf(ncct, "no arguments allowed to this command.\n");
1013     return -1;
1014   }
1015   /* Okay, we have an attribute and it should be set/replaced on the
1016    * current path.
1017    */
1018   if(replace_attr(ncct, info, attrinfo, NULL)) {
1019     return -1;
1020   }
1021
1022   /* So, we updated an attribute, so we need to reload all checks
1023    * that are descendent-or-self of this node.
1024    */
1025   refresh_subchecks(ncct, info);
1026   return 0;
1027 }
1028
1029 #define NEW_STATE(a) (a) = calloc(1, sizeof(*(a)))
1030 #define ADD_CMD(a,cmd,func,ss,c) \
1031   noit_console_state_add_cmd((a), \
1032     NCSCMD(cmd, func, ss, c))
1033 #define DELEGATE_CMD(a,cmd,ss) \
1034   noit_console_state_add_cmd((a), \
1035     NCSCMD(cmd, noit_console_state_delegate, ss, NULL))
1036
1037 static
1038 void register_console_config_commands() {
1039   cmd_info_t *showcmd;
1040   noit_console_state_t *tl, *_conf_state, *_conf_t_state,
1041                        *_write_state, *_attr_state,
1042                        *_unset_state, *_uattr_state;
1043
1044   tl = noit_console_state_initial();
1045
1046   /* write <terimal|memory|file> */
1047   NEW_STATE(_write_state);
1048   ADD_CMD(_write_state, "terminal", noit_conf_write_terminal, NULL, NULL);
1049   ADD_CMD(_write_state, "file", noit_conf_write_file, NULL, NULL);
1050   /* write memory?  It's to a file, but I like router syntax */
1051   ADD_CMD(_write_state, "memory", noit_conf_write_file, NULL, NULL);
1052
1053   /* attribute <attrname> <value> */
1054   NEW_STATE(_attr_state);
1055   noit_console_state_add_check_attrs(_attr_state, noit_conf_check_set_attr);
1056  
1057   /* no attribute <attrname> <value> */
1058   NEW_STATE(_uattr_state);
1059   noit_console_state_add_check_attrs(_uattr_state, noit_conf_check_unset_attr);
1060   NEW_STATE(_unset_state);
1061   DELEGATE_CMD(_unset_state, "attribute", _uattr_state);
1062   ADD_CMD(_unset_state, "section", noit_console_config_section, NULL, (void *)1);
1063   ADD_CMD(_unset_state, "check", noit_console_config_nocheck, NULL, NULL);
1064  
1065   NEW_STATE(_conf_t_state);
1066   _conf_t_state->console_prompt_function = conf_t_prompt;
1067   noit_console_state_add_cmd(_conf_t_state, &console_command_exit);
1068   ADD_CMD(_conf_t_state, "ls", noit_console_config_show, NULL, NULL);
1069   ADD_CMD(_conf_t_state, "cd", noit_console_config_cd, NULL, NULL);
1070   ADD_CMD(_conf_t_state, "check", noit_console_check, NULL, NULL);
1071   ADD_CMD(_conf_t_state, "section", noit_console_config_section, NULL, (void *)0);
1072
1073   showcmd = noit_console_state_get_cmd(tl, "show");
1074   ADD_CMD(showcmd->dstate, "check", noit_console_check, NULL, NULL);
1075
1076   DELEGATE_CMD(_conf_t_state, "write", _write_state);
1077   DELEGATE_CMD(_conf_t_state, "attribute", _attr_state);
1078   DELEGATE_CMD(_conf_t_state, "no", _unset_state);
1079
1080   NEW_STATE(_conf_state);
1081   ADD_CMD(_conf_state, "terminal", noit_console_state_conf_terminal, _conf_t_state, NULL);
1082
1083   ADD_CMD(tl, "configure", noit_console_state_delegate, _conf_state, NULL);
1084   ADD_CMD(tl, "write", noit_console_state_delegate, _write_state, NULL);
1085   ADD_CMD(tl, "reload", noit_conf_reload, NULL, NULL);
1086 }
Note: See TracBrowser for help on using the browser.