root/src/noit_conf_checks.c

Revision 5f388ee20a06c319202cb070628c7bc827ffbb0a, 30.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

split out the noit(check) specific conf stuff into its own file

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