root/src/noit_conf_checks.c

Revision 125a593dd802ff9e00102dcfa8de54c1422c307e, 32.3 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 7 years ago)

implementation of the apply stuff

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