root/src/noit_conf.c

Revision f4eb13351ea99a7259b819e95b6c17a50b9e5696, 27.3 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 11 years ago)

removal of sections too

  • 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,
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         *value = (char *)xmlXPathCastNodeToString(node);
186         break;
187       default:
188         *value = (char *)xmlXPathCastToString(pobj);
189     }
190     goto found;
191   }
192   if(noit_hash_retrieve(&_compiled_fallback,
193                         path, strlen(path), (void **)&str)) {
194     *value = str;
195     goto found;
196   }
197   return 0;
198  found:
199   if(current_ctxt && current_ctxt != xpath_ctxt)
200     xmlXPathFreeContext(current_ctxt);
201   return 1;
202 }
203 int noit_conf_get_string(noit_conf_section_t section,
204                          const char *path, char **value) {
205   char *str;
206   if(_noit_conf_get_string(section,path,&str)) {
207     *value = strdup(str);
208     return 1;
209   }
210   return 0;
211 }
212 int noit_conf_get_stringbuf(noit_conf_section_t section,
213                             const char *path, char *buf, int len) {
214   char *str;
215   if(_noit_conf_get_string(section,path,&str)) {
216     strlcpy(buf, str, len);
217     return 1;
218   }
219   return 0;
220 }
221 int noit_conf_set_string(noit_conf_section_t section,
222                          const char *path, const char *value) {
223   noit_hash_replace(&_tmp_config,
224                     strdup(path), strlen(path), (void *)strdup(value),
225                     free, free);
226   return 1;
227 }
228 int noit_conf_get_int(noit_conf_section_t section,
229                       const char *path, int *value) {
230   char *str;
231   long longval;
232   if(noit_conf_get_string(section,path,&str)) {
233     int base = 10;
234     if(str[0] == '0') {
235       if(str[1] == 'x') base = 16;
236       else base = 8;
237     }
238     longval = strtol(str, NULL, base);
239     free(str);
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,path,&str)) {
255     *value = atof(str);
256     free(str);
257     return 1;
258   }
259   return 0;
260 }
261 int noit_conf_set_float(noit_conf_section_t section,
262                         const char *path, float value) {
263   char buffer[32];
264   snprintf(buffer, 32, "%f", value);
265   return noit_conf_set_string(section,path,buffer);
266 }
267 int noit_conf_get_boolean(noit_conf_section_t section,
268                           const char *path, noit_conf_boolean *value) {
269   char *str;
270   if(noit_conf_get_string(section,path,&str)) {
271     if(!strcasecmp(str, "true")) *value = noit_true;
272     else *value = noit_false;
273     free(str);
274     return 1;
275   }
276   return 0;
277 }
278 int noit_conf_set_boolean(noit_conf_section_t section,
279                           const char *path, noit_conf_boolean value) {
280   if(value == noit_true)
281     return noit_conf_set_string(section,path,"true");
282   return noit_conf_set_string(section,path,"false");
283 }
284
285 static void
286 conf_t_userdata_free(void *data) {
287   noit_conf_t_userdata_t *info = data;
288   if(info) {
289     if(info->path) free(info->path);
290     free(info);
291   }
292 }
293 static int
294 noit_console_state_conf_terminal(noit_console_closure_t ncct,
295                                  int argc, char **argv,
296                                  noit_console_state_t *state, void *closure) {
297   noit_conf_t_userdata_t *info;
298   if(argc) {
299     nc_printf(ncct, "extra arguments not expected.\n");
300     return -1;
301   }
302   info = calloc(1, sizeof(*info));
303   info->path = strdup("/");
304   noit_console_userdata_set(ncct, NOIT_CONF_T_USERDATA, info,
305                             conf_t_userdata_free);
306   noit_console_state_push_state(ncct, state);
307   noit_console_state_init(ncct);
308   return 0;
309 }
310
311 static int
312 noit_console_config_section(noit_console_closure_t ncct,
313                             int argc, char **argv,
314                             noit_console_state_t *state, void *closure) {
315   const char *err = "internal error";
316   char *path, xpath[1024];
317   noit_conf_t_userdata_t *info;
318   xmlXPathObjectPtr pobj = NULL;
319   xmlNodePtr node = NULL, newnode;
320   vpsized_int delete = (vpsized_int)closure;
321
322   if(argc != 1) {
323     nc_printf(ncct, "requires one argument\n");
324     return -1;
325   }
326   if(strchr(argv[0], '/')) {
327     nc_printf(ncct, "invalid section name\n");
328     return -1;
329   }
330   if(!strcmp(argv[0], "check")) {
331     nc_printf(ncct, "use 'check' to create checks\n");
332     return -1;
333   }
334   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
335   if(!strcmp(info->path, "/")) {
336     nc_printf(ncct, "manipulation of toplevel section disallowed\n");
337     return -1;
338   }
339
340   if(delete) {
341     /* We cannot delete if we have checks */
342     snprintf(xpath, sizeof(xpath), "/noit%s/%s//check", info->path, argv[0]);
343     pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
344     if(!pobj || pobj->type != XPATH_NODESET ||
345        !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
346       err = "cannot delete section, has checks";
347       goto bad;
348     }
349     if(pobj) xmlXPathFreeObject(pobj);
350   }
351
352   snprintf(xpath, sizeof(xpath), "/noit%s/%s", info->path, argv[0]);
353   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
354   if(!pobj || pobj->type != XPATH_NODESET) {
355     err = "internal error: cannot detect section";
356     goto bad;
357   }
358   if(!delete && !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
359     err = "cannot create section";
360     goto bad;
361   }
362   if(delete && xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
363     err = "no such section";
364     goto bad;
365   }
366   if(delete) {
367     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
368     xmlUnlinkNode(node);
369     return 0;
370   }
371   if(pobj) xmlXPathFreeObject(pobj);
372
373   path = strcmp(info->path, "/") ? info->path : "";
374   snprintf(xpath, sizeof(xpath), "/noit%s", path);
375   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
376   if(!pobj || pobj->type != XPATH_NODESET ||
377      xmlXPathNodeSetGetLength(pobj->nodesetval) != 1) {
378     err = "path invalid?";
379     goto bad;
380   }
381   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
382   if((newnode = xmlNewChild(node, NULL, (xmlChar *)argv[0], NULL)) != NULL)
383     info->path = strdup((char *)xmlGetNodePath(newnode) + strlen("/noit"));
384   else {
385     err = "failed to create section";
386     goto bad;
387   }
388   if(pobj) xmlXPathFreeObject(pobj);
389   return 0;
390  bad:
391   if(pobj) xmlXPathFreeObject(pobj);
392   nc_printf(ncct, "%s\n", err);
393   return -1;
394 }
395
396 static int
397 noit_console_config_cd(noit_console_closure_t ncct,
398                        int argc, char **argv,
399                        noit_console_state_t *state, void *closure) {
400   const char *err = "internal error";
401   char *path, xpath[1024];
402   noit_conf_t_userdata_t *info;
403   xmlXPathObjectPtr pobj = NULL;
404   xmlXPathContextPtr current_ctxt;
405   xmlNodePtr node = NULL;
406
407   if(argc != 1) {
408     nc_printf(ncct, "requires one argument\n");
409     return -1;
410   }
411   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
412   if(argv[0][0] == '/')
413     snprintf(xpath, sizeof(xpath), "/noit%s", argv[0]);
414   else {
415     snprintf(xpath, sizeof(xpath), "/noit%s/%s", info->path, argv[0]);
416   }
417   if(xpath[strlen(xpath)-1] == '/') xpath[strlen(xpath)-1] = '\0';
418
419   current_ctxt = xpath_ctxt;
420   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
421   if(!pobj || pobj->type != XPATH_NODESET ||
422      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
423     err = "no such section";
424     goto bad;
425   }
426   if(xmlXPathNodeSetGetLength(pobj->nodesetval) > 1) {
427     err = "ambiguous section";
428     goto bad;
429   }
430
431   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
432   if(!strcmp((char *)node->name, "check")) {
433     err = "can't cd into a check, use 'check' instead";
434     goto bad;
435   }
436   path = (char *)xmlGetNodePath(node);
437   if(strncmp(path, "/noit/", strlen("/noit/")) && strcmp(path, "/noit")) {
438     err = "new path outside out tree";
439     goto bad;
440   }
441   free(info->path);
442   if(!strcmp(path, "/noit"))
443     info->path = strdup("/");
444   else
445     info->path = strdup((char *)xmlGetNodePath(node) + strlen("/noit"));
446   if(pobj) xmlXPathFreeObject(pobj);
447   return 0;
448  bad:
449   if(pobj) xmlXPathFreeObject(pobj);
450   nc_printf(ncct, "%s\n", err);
451   return -1;
452 }
453 static int
454 noit_console_config_show(noit_console_closure_t ncct,
455                          int argc, char **argv,
456                          noit_console_state_t *state, void *closure) {
457   int i, cnt, titled = 0, cliplen = 0;
458   const char *path, *basepath = NULL;
459   char xpath[1024];
460   noit_conf_t_userdata_t *info = NULL;
461   xmlXPathObjectPtr pobj = NULL;
462   xmlXPathContextPtr current_ctxt;
463   xmlNodePtr node;
464
465   if(argc > 1) {
466     nc_printf(ncct, "too many arguments\n");
467     return -1;
468   }
469
470   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
471   if(info) path = basepath = info->path;
472   if(!info && argc == 0) {
473     nc_printf(ncct, "argument required when not in configuration mode\n");
474     return -1;
475   }
476
477   if(argc == 1) path = argv[0];
478   if(!basepath) basepath = path;
479
480   /* { / } is a special case */
481   if(!strcmp(basepath, "/")) basepath = "";
482   if(!strcmp(path, "/")) path = "";
483
484   if(!master_config) {
485     nc_printf(ncct, "no config\n");
486     return -1;
487   }
488
489   /* { / } is the only path that will end with a /
490    * in XPath { / / * } means something _entirely different than { / * }
491    * Ever notice how it is hard to describe xpath in C comments?
492    */
493   /* We don't want to show the root node */
494   cliplen = strlen("/noit/");
495
496   /* If we are in configuration mode
497    * and we are without an argument or the argument is absolute,
498    * clip the current path off */
499   if(info && (argc == 0 || path[0] != '/')) cliplen += strlen(basepath);
500   if(!path[0] || path[0] == '/') /* base only, or absolute path requested */
501     snprintf(xpath, sizeof(xpath), "/noit%s/@*", path);
502   else
503     snprintf(xpath, sizeof(xpath), "/noit%s/%s/@*", basepath, path);
504
505   current_ctxt = xpath_ctxt;
506   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
507   if(!pobj || pobj->type != XPATH_NODESET) {
508     nc_printf(ncct, "no such object\n");
509     goto bad;
510   }
511   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
512   titled = 0;
513   for(i=0; i<cnt; i++) {
514     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
515     if(!strcmp((char *)node->name, "check")) continue;
516     if(node->children && node->children == xmlGetLastChild(node) &&
517       xmlNodeIsText(node->children)) {
518       if(!titled++) nc_printf(ncct, "== Section Settings ==\n");
519       nc_printf(ncct, "%s: %s\n", xmlGetNodePath(node) + cliplen,
520                 xmlXPathCastNodeToString(node->children));
521     }
522   }
523   xmlXPathFreeObject(pobj);
524
525   /* _shorten string_ turning last { / @ * } to { / * } */
526   strlcpy(xpath + strlen(xpath) - 2, "*", 2);
527 nc_printf(ncct, "Looking up path '%s'\n", xpath);
528   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
529   if(!pobj || pobj->type != XPATH_NODESET) {
530     nc_printf(ncct, "no such object\n");
531     goto bad;
532   }
533   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
534   titled = 0;
535   for(i=0; i<cnt; i++) {
536     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
537     if(!strcmp((char *)node->name, "check")) continue;
538     if(!(node->children && node->children == xmlGetLastChild(node) &&
539          xmlNodeIsText(node->children))) {
540       if(!titled++) nc_printf(ncct, "== Subsections ==\n");
541       nc_printf(ncct, "%s\n", xmlGetNodePath(node) + cliplen);
542     }
543   }
544
545   titled = 0;
546   for(i=0; i<cnt; i++) {
547     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
548     if(!strcmp((char *)node->name, "check")) {
549       int busted = 1;
550       xmlAttr *attr;
551       char *uuid_str = "undefined";;
552
553       if(!titled++) nc_printf(ncct, "== Checks ==\n");
554
555       for(attr=node->properties; attr; attr = attr->next) {
556         if(!strcmp((char *)attr->name, "uuid"))
557           uuid_str = (char *)xmlXPathCastNodeToString(attr->children);
558       }
559       if(uuid_str) {
560         uuid_t checkid;
561         nc_printf(ncct, "check[@uuid=\"%s\"] ", uuid_str);
562         if(uuid_parse(uuid_str, checkid) == 0) {
563           noit_check_t *check;
564           check = noit_poller_lookup(checkid);
565           if(check) {
566             busted = 0;
567             nc_printf(ncct, "%s`%s", check->target, check->name);
568           }
569         }
570       }
571       else
572         nc_printf(ncct, "%s ", xmlGetNodePath(node) + cliplen);
573       if(busted) nc_printf(ncct, "[check not in running system]");
574       nc_write(ncct, "\n", 1);
575     }
576   }
577   xmlXPathFreeObject(pobj);
578   return 0;
579  bad:
580   if(pobj) xmlXPathFreeObject(pobj);
581   return -1;
582 }
583
584 static char *
585 conf_t_prompt(EditLine *el) {
586   noit_console_closure_t ncct;
587   noit_conf_t_userdata_t *info;
588   static char *tl = "noit(conf)# ";
589   static char *pfmt = "noit(conf:%s%s)# ";
590   int path_len, max_len;
591
592   el_get(el, EL_USERDATA, (void *)&ncct);
593   if(!ncct) return tl;
594   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
595   if(!info) return tl;
596
597   path_len = strlen(info->path);
598   max_len = sizeof(info->prompt) - (strlen(pfmt) - 4 /* %s%s */) - 1 /* \0 */;
599   if(path_len > max_len)
600     snprintf(info->prompt, sizeof(info->prompt),
601              pfmt, "...", info->path + max_len - 3 /* ... */);
602   else
603     snprintf(info->prompt, sizeof(info->prompt), pfmt, "", info->path);
604   return info->prompt;
605 }
606
607 static int
608 noit_console_write_xml(void *vncct, const char *buffer, int len) {
609   noit_console_closure_t ncct = vncct;
610   return nc_write(ncct, buffer, len);
611 }
612 static int
613 noit_console_close_xml(void *vncct) {
614   return 0;
615 }
616 static int
617 noit_conf_write_terminal(noit_console_closure_t ncct,
618                          int argc, char **argv,
619                          noit_console_state_t *state, void *closure) {
620   xmlOutputBufferPtr out;
621   xmlCharEncodingHandlerPtr enc;
622   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
623   out = xmlOutputBufferCreateIO(noit_console_write_xml,
624                                 noit_console_close_xml,
625                                 ncct, enc);
626   xmlSaveFileTo(out, master_config, "utf8");
627   return 0;
628 }
629 static int
630 noit_conf_write_file(noit_console_closure_t ncct,
631                      int argc, char **argv,
632                      noit_console_state_t *state, void *closure) {
633   int fd, len;
634   char master_file_tmp[PATH_MAX];
635   xmlOutputBufferPtr out;
636   xmlCharEncodingHandlerPtr enc;
637
638   snprintf(master_file_tmp, sizeof(master_file_tmp),
639            "%s.tmp", master_config_file);
640   unlink(master_file_tmp);
641   fd = open(master_file_tmp, O_CREAT|O_EXCL|O_WRONLY, 0640);
642   if(fd < 0) {
643     nc_printf(ncct, "Failed to open tmp file: %s\n", strerror(errno));
644     return -1;
645   }
646   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
647   out = xmlOutputBufferCreateFd(fd, enc);
648   if(!out) {
649     close(fd);
650     unlink(master_file_tmp);
651     nc_printf(ncct, "internal error: OutputBufferCreate failed\n");
652     return -1;
653   }
654   len = xmlSaveFileTo(out, master_config, "utf8");
655   close(fd);
656   if(len <= 0) {
657     nc_printf(ncct, "internal error: writing to tmp file failed.\n");
658     return -1;
659   }
660   if(rename(master_file_tmp, master_config_file) != 0) {
661     nc_printf(ncct, "Failed to replace file: %s\n", strerror(errno));
662     return -1;
663   }
664   nc_printf(ncct, "%d bytes written.\n", len);
665   return 0;
666 }
667
668 static struct _valid_attr_t {
669   const char *scope;
670   const char *name;
671   const char *xpath;
672   int checks_fixate;
673 } valid_attrs[] = {
674   { "/checks", "name", "@name", 0 },
675   { "/checks", "target", "@target", 0 },
676   { "/checks", "period", "@period", 0 },
677   { "/checks", "timeout", "@timeout", 0 },
678   { "/checks", "oncheck", "@oncheck", 0 },
679   { "/checks", "module", "@module", 1 },
680 };
681
682 void
683 noit_console_state_add_check_attrs(noit_console_state_t *state,
684                                    console_cmd_func_t f) {
685   int i;
686   for(i = 0;
687       i < sizeof(valid_attrs)/sizeof(valid_attrs[0]);
688       i++) {
689     noit_console_state_add_cmd(state,
690       NCSCMD(valid_attrs[i].name, f,
691              NULL, &valid_attrs[i]));
692   }
693 }
694
695 static int
696 validate_attr_set_scope(noit_conf_t_userdata_t *info,
697                         struct _valid_attr_t *attrinfo) {
698   int len;
699   len = strlen(attrinfo->scope);
700   if(strncmp(info->path, attrinfo->scope, len) ||
701      (info->path[len] != '\0' && info->path[len] != '/')) {
702     return -1;
703   }
704   return 0;
705 }
706 static int
707 replace_attr(noit_console_closure_t ncct,
708              noit_conf_t_userdata_t *info, struct _valid_attr_t *attrinfo,
709              const char *value) {
710   int cnt, rv = -1;
711   xmlXPathObjectPtr pobj = NULL;
712   xmlNodePtr node;
713   char xpath[1024], *path;
714
715   path = info->path;
716   if(!strcmp(path, "/")) path = "";
717
718   if(attrinfo->checks_fixate) {
719     /* Only if checks will fixate this attribute shall we check for
720      * child <check> nodes.
721      * NOTE: this return nothing and "seems" okay if we are _in_
722      *       a <check> node.  That case is handled below.
723      */
724     snprintf(xpath, sizeof(xpath), "/noit/%s//check[@uuid]", path);
725     pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
726     if(pobj && pobj->type == XPATH_NODESET &&
727        !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
728       nc_printf(ncct, "Cannot set '%s', it would effect live checks\n",
729                 attrinfo->name);
730       goto out;
731     }
732     if(pobj) xmlXPathFreeObject(pobj);
733   }
734   snprintf(xpath, sizeof(xpath), "/noit/%s", path);
735   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
736   if(!pobj || pobj->type != XPATH_NODESET) goto out;
737   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
738   if(cnt != 1) {
739     nc_printf(ncct, "Internal error: context node disappeared\n");
740     goto out;
741   }
742   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
743   if(attrinfo->checks_fixate &&
744      !strcmp((const char *)node->name, "check")) {
745     /* Detect if  we are actually a <check> node and attempting to
746      * change something we shouldn't.
747      * This is the counterpart noted above.
748      */
749     nc_printf(ncct, "Cannot set '%s', it would effect live checks\n");
750     goto out;
751   }
752   xmlUnsetProp(node, (xmlChar *)attrinfo->name);
753   if(value)
754     xmlSetProp(node, (xmlChar *)attrinfo->name, (xmlChar *)value);
755   rv = 0;
756  out:
757   if(pobj) xmlXPathFreeObject(pobj);
758   return rv;
759 }
760 static void
761 refresh_subchecks(noit_console_closure_t ncct,
762                   noit_conf_t_userdata_t *info) {
763   char *path;
764   char xpath[1024];
765  
766   path = info->path;
767   if(!strcmp(path, "/")) path = "";
768
769   snprintf(xpath, sizeof(xpath), "/noit/%s[@uuid]", path);
770   noit_poller_process_checks(xpath);
771   snprintf(xpath, sizeof(xpath), "/noit/%s//check[@uuid]", path);
772   noit_poller_process_checks(xpath);
773 }
774 int
775 noit_conf_check_set_attr(noit_console_closure_t ncct,
776                          int argc, char **argv,
777                          noit_console_state_t *state, void *closure) {
778   struct _valid_attr_t *attrinfo = closure;
779   noit_conf_t_userdata_t *info;
780
781   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
782   if(!info || validate_attr_set_scope(info, attrinfo)) {
783     nc_printf(ncct, "'%s' attribute only valid in %s scope\n",
784               attrinfo->name, attrinfo->scope);
785     return -1;
786   }
787
788   if(argc != 1) {
789     nc_printf(ncct, "set requires exactly one value\n");
790     return -1;
791   }
792   /* Okay, we have an attribute and it should be set/replaced on the
793    * current path.
794    */
795   if(replace_attr(ncct, info, attrinfo, argv[0])) {
796     return -1;
797   }
798
799   /* So, we updated an attribute, so we need to reload all checks
800    * that are descendent-or-self of this node.
801    */
802   refresh_subchecks(ncct, info);
803   return 0;
804 }
805
806 int
807 noit_conf_check_unset_attr(noit_console_closure_t ncct,
808                            int argc, char **argv,
809                            noit_console_state_t *state, void *closure) {
810   struct _valid_attr_t *attrinfo = closure;
811   noit_conf_t_userdata_t *info;
812
813   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
814   if(!info || validate_attr_set_scope(info, attrinfo)) {
815     nc_printf(ncct, "'%s' attribute only valid in %s scope\n",
816               attrinfo->name, attrinfo->scope);
817     return -1;
818   }
819
820   if(argc != 0) {
821     nc_printf(ncct, "no arguments allowed to this command.\n");
822     return -1;
823   }
824   /* Okay, we have an attribute and it should be set/replaced on the
825    * current path.
826    */
827   if(replace_attr(ncct, info, attrinfo, NULL)) {
828     return -1;
829   }
830
831   /* So, we updated an attribute, so we need to reload all checks
832    * that are descendent-or-self of this node.
833    */
834   refresh_subchecks(ncct, info);
835   return 0;
836 }
837
838 #define NEW_STATE(a) (a) = calloc(1, sizeof(*(a)))
839 #define ADD_CMD(a,cmd,func,ss,c) \
840   noit_console_state_add_cmd((a), \
841     NCSCMD(cmd, func, ss, c))
842 #define DELEGATE_CMD(a,cmd,ss) \
843   noit_console_state_add_cmd((a), \
844     NCSCMD(cmd, noit_console_state_delegate, ss, NULL))
845
846 static
847 void register_console_config_commands() {
848   noit_console_state_t *tl, *_conf_state, *_conf_t_state,
849                        *_write_state, *_attr_state,
850                        *_unset_state, *_uattr_state;
851
852   tl = noit_console_state_initial();
853
854   /* write <terimal|memory|file> */
855   NEW_STATE(_write_state);
856   ADD_CMD(_write_state, "terminal", noit_conf_write_terminal, NULL, NULL);
857   ADD_CMD(_write_state, "file", noit_conf_write_file, NULL, NULL);
858   /* write memory?  It's to a file, but I like router syntax */
859   ADD_CMD(_write_state, "memory", noit_conf_write_file, NULL, NULL);
860
861   /* attribute <attrname> <value> */
862   NEW_STATE(_attr_state);
863   noit_console_state_add_check_attrs(_attr_state, noit_conf_check_set_attr);
864  
865   /* no attribute <attrname> <value> */
866   NEW_STATE(_uattr_state);
867   noit_console_state_add_check_attrs(_uattr_state, noit_conf_check_unset_attr);
868   NEW_STATE(_unset_state);
869   DELEGATE_CMD(_unset_state, "attribute", _uattr_state);
870   ADD_CMD(_unset_state, "section", noit_console_config_section, NULL, (void *)1);
871  
872   NEW_STATE(_conf_t_state);
873   _conf_t_state->console_prompt_function = conf_t_prompt;
874   noit_console_state_add_cmd(_conf_t_state, &console_command_exit);
875   ADD_CMD(_conf_t_state, "ls", noit_console_config_show, NULL, NULL);
876   ADD_CMD(_conf_t_state, "cd", noit_console_config_cd, NULL, NULL);
877   ADD_CMD(_conf_t_state, "section", noit_console_config_section, NULL, (void *)0);
878   DELEGATE_CMD(_conf_t_state, "write", _write_state);
879   DELEGATE_CMD(_conf_t_state, "attribute", _attr_state);
880   DELEGATE_CMD(_conf_t_state, "no", _unset_state);
881
882   NEW_STATE(_conf_state);
883   ADD_CMD(_conf_state, "terminal", noit_console_state_conf_terminal, _conf_t_state, NULL);
884
885   ADD_CMD(tl, "configure", noit_console_state_delegate, _conf_state, NULL);
886   ADD_CMD(tl, "write", noit_console_state_delegate, _write_state, NULL);
887 }
Note: See TracBrowser for help on using the browser.