root/src/noit_conf_checks.c

Revision 7075a66d0bf9d872f936eee678a49cfbc5cd46c5, 30.3 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

Make sure that checks can be selected as both targetname and targetmodule`name.
Show full targetmodulename when listing checks.
When creating new checks, create them disabled. Creating a check now looks like:

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