root/src/noit_check.c

Revision d5e61dbf0c7e4b34195decd6f2b28faf0b3df9c1, 50.4 kB (checked in by Brian Clapper <bclapper@omniti.com>, 2 years ago)

module_metadata should be module_configs here

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <ctype.h>
39 #include <assert.h>
40 #include <errno.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <time.h>
44
45 #include "dtrace_probes.h"
46 #include "utils/noit_log.h"
47 #include "utils/noit_hash.h"
48 #include "utils/noit_skiplist.h"
49 #include "noit_conf.h"
50 #include "noit_check.h"
51 #include "noit_module.h"
52 #include "noit_console.h"
53 #include "noit_check_tools.h"
54 #include "noit_check_resolver.h"
55 #include "eventer/eventer.h"
56
57 NOIT_HOOK_IMPL(check_stats_set_metric,
58   (noit_check_t *check, stats_t *stats, metric_t *m),
59   void *, closure,
60   (void *closure, noit_check_t *check, stats_t *stats, metric_t *m),
61   (closure,check,stats,m))
62
63 NOIT_HOOK_IMPL(check_passive_log_stats,
64   (noit_check_t *check),
65   void *, closure,
66   (void *closure, noit_check_t *check),
67   (closure,check))
68
69 NOIT_HOOK_IMPL(check_log_stats,
70   (noit_check_t *check),
71   void *, closure,
72   (void *closure, noit_check_t *check),
73   (closure,check))
74
75 /* 20 ms slots over 60 second for distribution */
76 #define SCHEDULE_GRANULARITY 20
77 #define SLOTS_PER_SECOND (1000/SCHEDULE_GRANULARITY)
78 #define MAX_MODULE_REGISTRATIONS 64
79
80 /* used to manage per-check generic module metadata */
81 struct vp_w_free {
82   void *ptr;
83   void (*freefunc)(void *);
84 };
85
86 static int reg_module_id = 0;
87 static char *reg_module_names[MAX_MODULE_REGISTRATIONS] = { NULL };
88 static int reg_module_used = -1;
89 static u_int64_t check_completion_count = 0;
90 static noit_hash_table polls = NOIT_HASH_EMPTY;
91 static noit_skiplist watchlist = { 0 };
92 static noit_skiplist polls_by_name = { 0 };
93 static u_int32_t __config_load_generation = 0;
94 static unsigned short check_slots_count[60000 / SCHEDULE_GRANULARITY] = { 0 },
95                       check_slots_seconds_count[60] = { 0 };
96
97 static int
98 noit_console_show_timing_slots(noit_console_closure_t ncct,
99                                int argc, char **argv,
100                                noit_console_state_t *dstate,
101                                void *closure) {
102   int i, j;
103   const int upl = (60000 / SCHEDULE_GRANULARITY) / 60;
104   for(i=0;i<60;i++) {
105     nc_printf(ncct, "[%02d] %04d: ", i, check_slots_seconds_count[i]);
106     for(j=i*upl;j<(i+1)*upl;j++) {
107       char cp = '!';
108       if(check_slots_count[j] < 10) cp = '0' + check_slots_count[j];
109       else if(check_slots_count[j] < 36) cp = 'a' + (check_slots_count[j] - 10);
110       nc_printf(ncct, "%c", cp);
111     }
112     nc_printf(ncct, "\n");
113   }
114   return 0;
115 }
116
117 u_int64_t noit_check_completion_count() {
118   return check_completion_count;
119 }
120 static void register_console_check_commands();
121 static int check_recycle_bin_processor(eventer_t, int, void *,
122                                        struct timeval *);
123
124 static int
125 check_slots_find_smallest(int sec) {
126   int i, j, cyclic, random_offset, jbase = 0, mini = 0, minj = 0;
127   unsigned short min_running_i = 0xffff, min_running_j = 0xffff;
128   for(i=0;i<60;i++) {
129     int adj_i = (i + sec) % 60;
130     if(check_slots_seconds_count[adj_i] < min_running_i) {
131       min_running_i = check_slots_seconds_count[adj_i];
132       mini = adj_i;
133     }
134   }
135   jbase = mini * (1000/SCHEDULE_GRANULARITY);
136   random_offset = drand48() * SLOTS_PER_SECOND;
137   for(cyclic=0;cyclic<SLOTS_PER_SECOND;cyclic++) {
138     j = jbase + ((random_offset + cyclic) % SLOTS_PER_SECOND);
139     if(check_slots_count[j] < min_running_j) {
140       min_running_j = check_slots_count[j];
141       minj = j;
142     }
143   }
144   return (minj * SCHEDULE_GRANULARITY) + drand48() * SCHEDULE_GRANULARITY;
145 }
146 static void
147 check_slots_adjust_tv(struct timeval *tv, short adj) {
148   int offset_ms, idx;
149   offset_ms = (tv->tv_sec % 60) * 1000 + (tv->tv_usec / 1000);
150   idx = offset_ms / SCHEDULE_GRANULARITY;
151   check_slots_count[idx] += adj;
152   check_slots_seconds_count[offset_ms / 1000] += adj;
153 }
154 void check_slots_inc_tv(struct timeval *tv) {
155   check_slots_adjust_tv(tv, 1);
156 }
157 void check_slots_dec_tv(struct timeval *tv) {
158   check_slots_adjust_tv(tv, -1);
159 }
160 const char *
161 noit_check_available_string(int16_t available) {
162   switch(available) {
163     case NP_AVAILABLE:    return "available";
164     case NP_UNAVAILABLE:  return "unavailable";
165     case NP_UNKNOWN:      return "unknown";
166   }
167   return NULL;
168 }
169 const char *
170 noit_check_state_string(int16_t state) {
171   switch(state) {
172     case NP_GOOD:         return "good";
173     case NP_BAD:          return "bad";
174     case NP_UNKNOWN:      return "unknown";
175   }
176   return NULL;
177 }
178 static int __check_name_compare(const void *a, const void *b) {
179   const noit_check_t *ac = a;
180   const noit_check_t *bc = b;
181   int rv;
182   if((rv = strcmp(ac->target, bc->target)) != 0) return rv;
183   if((rv = strcmp(ac->name, bc->name)) != 0) return rv;
184   return 0;
185 }
186 static int __watchlist_compare(const void *a, const void *b) {
187   const noit_check_t *ac = a;
188   const noit_check_t *bc = b;
189   int rv;
190   if((rv = memcmp(ac->checkid, bc->checkid, sizeof(ac->checkid))) != 0) return rv;
191   if(ac->period < bc->period) return -1;
192   if(ac->period == bc->period) return 0;
193   return 1;
194 }
195 int
196 noit_calc_rtype_flag(char *resolve_rtype) {
197   int flags = 0;
198   if(resolve_rtype) {
199     flags |= strcmp(resolve_rtype, PREFER_IPV6) == 0 ||
200              strcmp(resolve_rtype, FORCE_IPV6) == 0 ? NP_PREFER_IPV6 : 0;
201     flags |= strcmp(resolve_rtype, FORCE_IPV4) == 0 ||
202              strcmp(resolve_rtype, FORCE_IPV6) == 0 ? NP_SINGLE_RESOLVE : 0;
203   }
204   return flags;
205 }
206 void
207 noit_check_fake_last_check(noit_check_t *check,
208                            struct timeval *lc, struct timeval *_now) {
209   struct timeval now, period;
210   int balance_ms;
211
212   if(!_now) {
213     gettimeofday(&now, NULL);
214     _now = &now;
215   }
216   period.tv_sec = check->period / 1000;
217   period.tv_usec = (check->period % 1000) * 1000;
218   sub_timeval(*_now, period, lc);
219
220   if(!(check->flags & NP_TRANSIENT) && check->period) {
221     balance_ms = check_slots_find_smallest(_now->tv_sec+1);
222     lc->tv_sec = (lc->tv_sec / 60) * 60 + balance_ms / 1000;
223     lc->tv_usec = (balance_ms % 1000) * 1000;
224     if(compare_timeval(*_now, *lc) < 0) {
225       do {
226         sub_timeval(*lc, period, lc);
227       } while(compare_timeval(*_now, *lc) < 0);
228     }
229     else {
230       struct timeval test;
231       while(1) {
232         add_timeval(*lc, period, &test);
233         if(compare_timeval(*_now, test) < 0) break;
234         memcpy(lc, &test, sizeof(test));
235       }
236     }
237   }
238   /* now, we're going to do an even distribution using the slots */
239   if(!(check->flags & NP_TRANSIENT)) check_slots_inc_tv(lc);
240 }
241 void
242 noit_poller_process_checks(const char *xpath) {
243   int i, flags, cnt = 0;
244   noit_conf_section_t *sec;
245   __config_load_generation++;
246   sec = noit_conf_get_sections(NULL, xpath, &cnt);
247   for(i=0; i<cnt; i++) {
248     void *vcheck;
249     char uuid_str[37];
250     char target[256] = "";
251     char module[256] = "";
252     char name[256] = "";
253     char filterset[256] = "";
254     char oncheck[1024] = "";
255     char resolve_rtype[16] = "";
256     int ridx;
257     int no_period = 0;
258     int no_oncheck = 0;
259     int period = 0, timeout = 0;
260     noit_boolean disabled = noit_false, busted = noit_false;
261     uuid_t uuid, out_uuid;
262     noit_hash_table *options;
263     noit_hash_table **moptions = NULL;
264     noit_boolean moptions_used = noit_false;
265
266     if(reg_module_id > 0) {
267       moptions = alloca(reg_module_id * sizeof(noit_hash_table *));
268       memset(moptions, 0, reg_module_id * sizeof(noit_hash_table *));
269     }
270
271 #define NEXT(...) noitL(noit_stderr, __VA_ARGS__); continue
272 #define MYATTR(type,a,...) noit_conf_get_##type(sec[i], "@" #a, __VA_ARGS__)
273 #define INHERIT(type,a,...) \
274   noit_conf_get_##type(sec[i], "ancestor-or-self::node()/@" #a, __VA_ARGS__)
275
276     if(!MYATTR(stringbuf, uuid, uuid_str, sizeof(uuid_str))) {
277       noitL(noit_stderr, "check %d has no uuid\n", i+1);
278       continue;
279     }
280
281     if(uuid_parse(uuid_str, uuid)) {
282       noitL(noit_stderr, "check uuid: '%s' is invalid\n", uuid_str);
283       continue;
284     }
285
286     if(!INHERIT(stringbuf, target, target, sizeof(target))) {
287       noitL(noit_stderr, "check uuid: '%s' has no target\n", uuid_str);
288       busted = noit_true;
289     }
290     if(!INHERIT(stringbuf, module, module, sizeof(module))) {
291       noitL(noit_stderr, "check uuid: '%s' has no module\n", uuid_str);
292       busted = noit_true;
293     }
294
295     if(!INHERIT(stringbuf, filterset, filterset, sizeof(filterset)))
296       filterset[0] = '\0';
297    
298     if (!INHERIT(stringbuf, resolve_rtype, resolve_rtype, sizeof(resolve_rtype)))
299       strlcpy(resolve_rtype, PREFER_IPV4, sizeof(resolve_rtype));
300
301     if(!MYATTR(stringbuf, name, name, sizeof(name)))
302       strlcpy(name, module, sizeof(name));
303
304     if(!INHERIT(int, period, &period) || period == 0)
305       no_period = 1;
306
307     if(!INHERIT(stringbuf, oncheck, oncheck, sizeof(oncheck)) || !oncheck[0])
308       no_oncheck = 1;
309
310     if(no_period && no_oncheck) {
311       noitL(noit_stderr, "check uuid: '%s' has neither period nor oncheck\n",
312             uuid_str);
313       busted = noit_true;
314     }
315     if(!(no_period || no_oncheck)) {
316       noitL(noit_stderr, "check uuid: '%s' has oncheck and period.\n",
317             uuid_str);
318       busted = noit_true;
319     }
320     if(!INHERIT(int, timeout, &timeout)) {
321       noitL(noit_stderr, "check uuid: '%s' has no timeout\n", uuid_str);
322       busted = noit_true;
323     }
324     if(!no_period && timeout >= period) {
325       noitL(noit_stderr, "check uuid: '%s' timeout > period\n", uuid_str);
326       timeout = period/2;
327     }
328     options = noit_conf_get_hash(sec[i], "config");
329     for(ridx=0; ridx<reg_module_id; ridx++) {
330       moptions[ridx] = noit_conf_get_namespaced_hash(sec[i], "config",
331                                                      reg_module_names[ridx]);
332       if(moptions[ridx]) moptions_used = noit_true;
333     }
334
335     INHERIT(boolean, disable, &disabled);
336     flags = 0;
337     if(busted) flags |= (NP_UNCONFIG|NP_DISABLED);
338     else if(disabled) flags |= NP_DISABLED;
339
340     flags |= noit_calc_rtype_flag(resolve_rtype);
341
342     if(noit_hash_retrieve(&polls, (char *)uuid, UUID_SIZE,
343                           &vcheck)) {
344       noit_check_t *existing_check = (noit_check_t *)vcheck;
345       /* Once set, it cannot be checked if the check is live */
346       assert(!existing_check->module || !existing_check->module[0] ||
347              !strcmp(existing_check->module, module) ||
348              !NOIT_CHECK_LIVE(existing_check));
349       /* Set it if it is unset or being changed */
350       if(!existing_check->module || !existing_check->module[0] ||
351          strcmp(existing_check->module, module)) {
352         if(existing_check->module) free(existing_check->module);
353         existing_check->module = strdup(module);
354       }
355       noit_check_update(existing_check, target, name, filterset, options,
356                            moptions_used ? moptions : NULL,
357                            period, timeout, oncheck[0] ? oncheck : NULL,
358                            flags);
359       noitL(noit_debug, "reloaded uuid: %s\n", uuid_str);
360     }
361     else {
362       noit_poller_schedule(target, module, name, filterset, options,
363                            moptions_used ? moptions : NULL,
364                            period, timeout, oncheck[0] ? oncheck : NULL,
365                            flags, uuid, out_uuid);
366       noitL(noit_debug, "loaded uuid: %s\n", uuid_str);
367     }
368
369     for(ridx=0; ridx<reg_module_id; ridx++) {
370       if(moptions[ridx]) {
371         noit_hash_destroy(moptions[ridx], free, free);
372         free(moptions[ridx]);
373       }
374     }
375     noit_hash_destroy(options, free, free);
376     free(options);
377   }
378   if(sec) free(sec);
379 }
380
381 int
382 noit_check_activate(noit_check_t *check) {
383   noit_module_t *mod;
384   if(NOIT_CHECK_LIVE(check)) return 0;
385   mod = noit_module_lookup(check->module);
386   if(mod && mod->initiate_check) {
387     if((check->flags & NP_DISABLED) == 0) {
388       mod->initiate_check(mod, check, 0, NULL);
389       return 1;
390     }
391     else
392       noitL(noit_debug, "Skipping %s`%s, disabled.\n",
393             check->target, check->name);
394   }
395   else {
396     if(!mod) {
397       noitL(noit_stderr, "Cannot find module '%s'\n", check->module);
398       check->flags |= NP_DISABLED;
399     }
400   }
401   return 0;
402 }
403
404 void
405 noit_poller_initiate() {
406   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
407   uuid_t key_id;
408   int klen;
409   void *vcheck;
410   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
411                        &vcheck)) {
412     noit_check_activate((noit_check_t *)vcheck);
413   }
414 }
415
416 void
417 noit_poller_flush_epoch(int oldest_allowed) {
418   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
419   uuid_t key_id;
420   int klen;
421   noit_check_t *tofree = NULL;
422   void *vcheck;
423
424   /* Cleanup any previous causal map */
425   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
426                        &vcheck)) {
427     noit_check_t *check = (noit_check_t *)vcheck;
428     /* We don't free the one we're looking at... we free it on the next
429      * pass.  This leaves out iterator in good shape.  We just need to
430      * remember to free it one last time outside the while loop, down...
431      */
432     if(tofree) {
433       noit_poller_deschedule(tofree->checkid);
434       tofree = NULL;
435     }
436     if(check->generation < oldest_allowed) {
437       tofree = check;
438     }
439   }
440   /* ... here */
441   if(tofree) noit_poller_deschedule(tofree->checkid);
442 }
443
444 void
445 noit_poller_make_causal_map() {
446   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
447   uuid_t key_id;
448   int klen;
449   void *vcheck;
450
451   /* Cleanup any previous causal map */
452   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
453                        &vcheck)) {
454     noit_check_t *check = (noit_check_t *)vcheck;
455     dep_list_t *dep;
456     while((dep = check->causal_checks) != NULL) {
457       check->causal_checks = dep->next;
458       free(dep);
459     }
460   }
461
462   memset(&iter, 0, sizeof(iter));
463   /* Walk all checks and add check dependencies to their parents */
464   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
465                        &vcheck)) {
466     noit_check_t *check = (noit_check_t *)vcheck, *parent;
467     if(check->oncheck) {
468       /* This service is causally triggered by another service */
469       uuid_t id;
470       char fullcheck[1024];
471       char *name = check->oncheck;
472       char *target = NULL;
473
474       noitL(noit_debug, "Searching for upstream trigger on %s\n", name);
475       parent = NULL;
476       if(uuid_parse(check->oncheck, id) == 0) {
477         target = "";
478         parent = noit_poller_lookup(id);
479       }
480       if((target = strchr(check->oncheck, '`')) != NULL) {
481         strlcpy(fullcheck, check->oncheck, target + 1 - check->oncheck);
482         name = target + 1;
483         target = fullcheck;
484         parent = noit_poller_lookup_by_name(target, name);
485       }
486       else {
487         target = check->target;
488         parent = noit_poller_lookup_by_name(target, name);
489       }
490
491       if(!parent) {
492         check->flags |= NP_DISABLED;
493         noitL(noit_stderr, "Disabling check %s`%s, can't find oncheck %s`%s\n",
494               check->target, check->name, target, name);
495       }
496       else {
497         dep_list_t *dep;
498         dep = malloc(sizeof(*dep));
499         dep->check = check;
500         dep->next = parent->causal_checks;
501         parent->causal_checks = dep;
502         noitL(noit_debug, "Causal map %s`%s --> %s`%s\n",
503               parent->target, parent->name, check->target, check->name);
504       }
505     }
506   }
507 }
508 void
509 noit_poller_reload(const char *xpath)
510 {
511   noit_poller_process_checks(xpath ? xpath : "/noit/checks//check");
512   if(!xpath) {
513     /* Full reload, we need to wipe old checks */
514     noit_poller_flush_epoch(__config_load_generation);
515   }
516   noit_poller_make_causal_map();
517   noit_poller_initiate();
518 }
519 void
520 noit_poller_init() {
521   srand48((getpid() << 16) & time(NULL));
522   noit_check_resolver_init();
523   noit_check_tools_init();
524   noit_skiplist_init(&polls_by_name);
525   noit_skiplist_set_compare(&polls_by_name, __check_name_compare,
526                             __check_name_compare);
527   noit_skiplist_init(&watchlist);
528   noit_skiplist_set_compare(&watchlist, __watchlist_compare,
529                             __watchlist_compare);
530   register_console_check_commands();
531   eventer_name_callback("check_recycle_bin_processor",
532                         check_recycle_bin_processor);
533   eventer_add_in_s_us(check_recycle_bin_processor, NULL, 60, 0);
534   noit_poller_reload(NULL);
535 }
536
537 int
538 noit_poller_check_count() {
539   return polls_by_name.size;
540 }
541
542 int
543 noit_poller_transient_check_count() {
544   return watchlist.size;
545 }
546
547 noit_check_t *
548 noit_check_clone(uuid_t in) {
549   int i;
550   noit_check_t *checker, *new_check;
551   void *vcheck;
552   if(noit_hash_retrieve(&polls,
553                         (char *)in, UUID_SIZE,
554                         &vcheck) == 0) {
555     return NULL;
556   }
557   checker = (noit_check_t *)vcheck;
558   if(checker->oncheck) {
559     return NULL;
560   }
561   new_check = calloc(1, sizeof(*new_check));
562   memcpy(new_check, checker, sizeof(*new_check));
563   new_check->target = strdup(new_check->target);
564   new_check->module = strdup(new_check->module);
565   new_check->name = strdup(new_check->name);
566   new_check->filterset = strdup(new_check->filterset);
567   new_check->flags = 0;
568   new_check->fire_event = NULL;
569   memset(&new_check->last_fire_time, 0, sizeof(new_check->last_fire_time));
570   memset(&new_check->stats, 0, sizeof(new_check->stats));
571   new_check->closure = NULL;
572   new_check->config = calloc(1, sizeof(*new_check->config));
573   noit_hash_merge_as_dict(new_check->config, checker->config);
574   for(i=0; i<reg_module_id; i++) {
575     noit_hash_table *src_mconfig;
576     src_mconfig = noit_check_get_module_config(checker, i);
577     if(src_mconfig) {
578       noit_hash_table *t = calloc(1, sizeof(*new_check->config));
579       noit_hash_merge_as_dict(t, src_mconfig);
580       noit_check_set_module_config(new_check, i, t);
581     }
582   }
583   return new_check;
584 }
585
586 noit_check_t *
587 noit_check_watch(uuid_t in, int period) {
588   /* First look for a copy that is being watched */
589   int minimum_pi = 1000, granularity_pi = 500;
590   noit_conf_section_t check_node;
591   char uuid_str[UUID_STR_LEN + 1];
592   char xpath[1024];
593   noit_check_t n, *f;
594
595   uuid_unparse_lower(in, uuid_str);
596   /* Find the check */
597   snprintf(xpath, sizeof(xpath), "//checks//check[@uuid=\"%s\"]", uuid_str);
598   check_node = noit_conf_get_section(NULL, xpath);
599   noit_conf_get_int(NULL, "//checks/@transient_min_period", &minimum_pi);
600   noit_conf_get_int(NULL, "//checks/@transient_period_granularity", &granularity_pi);
601   if(check_node) {
602     noit_conf_get_int(check_node,
603                       "ancestor-or-self::node()/@transient_min_period",
604                       &minimum_pi);
605     noit_conf_get_int(check_node,
606                       "ancestor-or-self::node()/@transient_period_granularity",
607                       &granularity_pi);
608   }
609
610   /* apply the bounds */
611   period /= granularity_pi;
612   period *= granularity_pi;
613   period = MAX(period, minimum_pi);
614
615   uuid_copy(n.checkid, in);
616   n.period = period;
617
618   f = noit_skiplist_find(&watchlist, &n, NULL);
619   if(f) return f;
620   f = noit_check_clone(in);
621   if(!f) return NULL;
622   f->period = period;
623   f->timeout = period - 10;
624   f->flags |= NP_TRANSIENT;
625   noitL(noit_debug, "Watching %s@%d\n", uuid_str, period);
626   noit_skiplist_insert(&watchlist, f);
627   return f;
628 }
629
630 noit_check_t *
631 noit_check_get_watch(uuid_t in, int period) {
632   noit_check_t n, *f;
633
634   uuid_copy(n.checkid, in);
635   n.period = period;
636
637   f = noit_skiplist_find(&watchlist, &n, NULL);
638   return f;
639 }
640
641 void
642 noit_check_transient_add_feed(noit_check_t *check, const char *feed) {
643   char *feedcopy;
644   if(!check->feeds) {
645     check->feeds = calloc(1, sizeof(*check->feeds));
646     noit_skiplist_init(check->feeds);
647     noit_skiplist_set_compare(check->feeds,
648                               (noit_skiplist_comparator_t)strcmp,
649                               (noit_skiplist_comparator_t)strcmp);
650   }
651   feedcopy = strdup(feed);
652   /* No error on failure -- it's already there */
653   if(noit_skiplist_insert(check->feeds, feedcopy) == NULL) free(feedcopy);
654   noitL(noit_debug, "check %s`%s @ %dms has %d feed(s): %s.\n",
655         check->target, check->name, check->period, check->feeds->size, feed);
656 }
657 void
658 noit_check_transient_remove_feed(noit_check_t *check, const char *feed) {
659   if(!check->feeds) return;
660   if(feed) {
661     noitL(noit_debug, "check %s`%s @ %dms removing 1 of %d feeds: %s.\n",
662           check->target, check->name, check->period, check->feeds->size, feed);
663     noit_skiplist_remove(check->feeds, feed, free);
664   }
665   if(check->feeds->size == 0) {
666     char uuid_str[UUID_STR_LEN + 1];
667     uuid_unparse_lower(check->checkid, uuid_str);
668     noitL(noit_debug, "Unwatching %s@%d\n", uuid_str, check->period);
669     noit_skiplist_remove(&watchlist, check, NULL);
670     noit_skiplist_destroy(check->feeds, free);
671     free(check->feeds);
672     check->feeds = NULL;
673     if(check->flags & NP_TRANSIENT) {
674       noitL(noit_debug, "check %s`%s @ %dms has no more listeners.\n",
675             check->target, check->name, check->period);
676       check->flags |= NP_KILLED;
677     }
678   }
679 }
680
681 noit_boolean
682 noit_check_is_valid_target(const char *target) {
683   int8_t family;
684   int rv;
685   union {
686     struct in_addr addr4;
687     struct in6_addr addr6;
688   } a;
689
690   family = AF_INET;
691   rv = inet_pton(family, target, &a);
692   if(rv != 1) {
693     family = AF_INET6;
694     rv = inet_pton(family, target, &a);
695     if(rv != 1) {
696       return noit_false;
697     }
698   }
699   return noit_true;
700 }
701 int
702 noit_check_set_ip(noit_check_t *new_check,
703                   const char *ip_str) {
704   int8_t family;
705   int rv, failed = 0;
706   union {
707     struct in_addr addr4;
708     struct in6_addr addr6;
709   } a;
710
711   family = NOIT_CHECK_PREFER_V6(new_check) ? AF_INET6 : AF_INET;
712   rv = inet_pton(family, ip_str, &a);
713   if(rv != 1) {
714     if (!NOIT_CHECK_SINGLE_RESOLVE(new_check)) {
715       family = family == AF_INET ? AF_INET6 : AF_INET;
716       rv = inet_pton(family, ip_str, &a);
717       if(rv != 1) {
718         family = AF_INET;
719         memset(&a, 0, sizeof(a));
720         failed = -1;
721       }
722     } else {
723       failed = -1;
724     }
725   }
726
727   new_check->target_family = family;
728   memcpy(&new_check->target_addr, &a, sizeof(a));
729   new_check->target_ip[0] = '\0';
730   if(failed == 0)
731     if(inet_ntop(new_check->target_family,
732                  &new_check->target_addr,
733                  new_check->target_ip,
734                  sizeof(new_check->target_ip)) == NULL) {
735       noitL(noit_error, "inet_ntop failed [%s] -> %d\n", ip_str, errno);
736     }
737   return failed;
738 }
739 int
740 noit_check_resolve(noit_check_t *check) {
741   uint8_t family_pref = AF_INET;
742   char ipaddr[INET6_ADDRSTRLEN];
743   if(!NOIT_CHECK_SHOULD_RESOLVE(check)) return 1; /* success, not required */
744   noit_check_resolver_remind(check->target);
745   if(noit_check_resolver_fetch(check->target, ipaddr, sizeof(ipaddr),
746                                family_pref) >= 0) {
747     check->flags |= NP_RESOLVED;
748     noit_check_set_ip(check, ipaddr);
749     return 0;
750   }
751   check->flags &= ~NP_RESOLVED;
752   return -1;
753 }
754 int
755 noit_check_update(noit_check_t *new_check,
756                   const char *target,
757                   const char *name,
758                   const char *filterset,
759                   noit_hash_table *config,
760                   noit_hash_table **mconfigs,
761                   u_int32_t period,
762                   u_int32_t timeout,
763                   const char *oncheck,
764                   int flags) {
765   int mask = NP_DISABLED | NP_UNCONFIG;
766
767   new_check->generation = __config_load_generation;
768   if(new_check->target) free(new_check->target);
769   new_check->target = strdup(target);
770
771   // apply resolution flags to check.
772   if (flags & NP_PREFER_IPV6)
773     new_check->flags |= NP_PREFER_IPV6;
774   else
775     new_check->flags &= ~NP_PREFER_IPV6;
776   if (flags & NP_SINGLE_RESOLVE)
777     new_check->flags |= NP_SINGLE_RESOLVE;
778   else
779     new_check->flags &= ~NP_SINGLE_RESOLVE;
780
781   if(noit_check_set_ip(new_check, target)) {
782     noit_boolean should_resolve;
783     new_check->flags |= NP_RESOLVE;
784     new_check->flags &= ~NP_RESOLVED;
785     if(noit_conf_get_boolean(NULL, "//checks/@resolve_targets",
786                              &should_resolve) && should_resolve == noit_false)
787      
788       flags |= NP_DISABLED | NP_UNCONFIG;
789     noit_check_resolve(new_check);
790   }
791
792   if(new_check->name) free(new_check->name);
793   new_check->name = name ? strdup(name): NULL;
794   if(new_check->filterset) free(new_check->filterset);
795   new_check->filterset = filterset ? strdup(filterset): NULL;
796
797   if(config != NULL) {
798     noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
799     const char *k;
800     int klen;
801     void *data;
802     if(new_check->config) noit_hash_delete_all(new_check->config, free, free);
803     else new_check->config = calloc(1, sizeof(*new_check->config));
804     while(noit_hash_next(config, &iter, &k, &klen, &data)) {
805       noit_hash_store(new_check->config, strdup(k), klen, strdup((char *)data));
806     }
807   }
808   if(mconfigs != NULL) {
809     int i;
810     for(i=0; i<reg_module_id; i++) {
811       if(mconfigs[i]) {
812         noit_hash_table *t = calloc(1, sizeof(*new_check->config));
813         noit_hash_merge_as_dict(t, mconfigs[i]);
814         noit_check_set_module_config(new_check, i, t);
815       }
816     }
817   }
818   if(new_check->oncheck) free(new_check->oncheck);
819   new_check->oncheck = oncheck ? strdup(oncheck) : NULL;
820   new_check->period = period;
821   new_check->timeout = timeout;
822
823   /* Unset what could be set.. then set what should be set */
824   new_check->flags = (new_check->flags & ~mask) | flags;
825
826   if(!(new_check->flags & NP_TRANSIENT)) {
827     /* This remove could fail -- no big deal */
828     noit_skiplist_remove(&polls_by_name, new_check, NULL);
829
830     /* This insert could fail.. which means we have a conflict on
831      * target`name.  That should result in the check being disabled. */
832     if(!noit_skiplist_insert(&polls_by_name, new_check)) {
833       noitL(noit_stderr, "Check %s`%s disabled due to naming conflict\n",
834             new_check->target, new_check->name);
835       new_check->flags |= NP_DISABLED;
836     }
837   }
838   noit_check_log_check(new_check);
839   return 0;
840 }
841 int
842 noit_poller_schedule(const char *target,
843                      const char *module,
844                      const char *name,
845                      const char *filterset,
846                      noit_hash_table *config,
847                      noit_hash_table **mconfigs,
848                      u_int32_t period,
849                      u_int32_t timeout,
850                      const char *oncheck,
851                      int flags,
852                      uuid_t in,
853                      uuid_t out) {
854   noit_check_t *new_check;
855   new_check = calloc(1, sizeof(*new_check));
856   if(!new_check) return -1;
857
858   /* The module and the UUID can never be changed */
859   new_check->module = strdup(module);
860   if(uuid_is_null(in))
861     uuid_generate(new_check->checkid);
862   else
863     uuid_copy(new_check->checkid, in);
864
865   noit_check_update(new_check, target, name, filterset, config, mconfigs,
866                     period, timeout, oncheck, flags);
867   assert(noit_hash_store(&polls,
868                          (char *)new_check->checkid, UUID_SIZE,
869                          new_check));
870   uuid_copy(out, new_check->checkid);
871
872   return 0;
873 }
874
875 /* A quick little list of recycleable checks.  This list never really
876  * grows large, so no sense in thinking too hard about the algorithmic
877  * complexity.
878  */
879 struct _checker_rcb {
880   noit_check_t *checker;
881   struct _checker_rcb *next;
882 };
883 static struct _checker_rcb *checker_rcb = NULL;
884 static void recycle_check(noit_check_t *checker) {
885   struct _checker_rcb *n = malloc(sizeof(*n));
886   n->checker = checker;
887   n->next = checker_rcb;
888   checker_rcb = n;
889 }
890 void
891 noit_poller_free_check(noit_check_t *checker) {
892   noit_module_t *mod;
893
894   if(checker->flags & NP_RUNNING) {
895     recycle_check(checker);
896     return;
897   }
898
899   mod = noit_module_lookup(checker->module);
900   if(mod && mod->cleanup) mod->cleanup(mod, checker);
901   if(checker->fire_event) {
902      eventer_remove(checker->fire_event);
903      free(checker->fire_event->closure);
904      eventer_free(checker->fire_event);
905      checker->fire_event = NULL;
906   }
907   if(checker->closure) free(checker->closure);
908   if(checker->target) free(checker->target);
909   if(checker->module) free(checker->module);
910   if(checker->name) free(checker->name);
911   if(checker->config) {
912     noit_hash_destroy(checker->config, free, free);
913     free(checker->config);
914     checker->config = NULL;
915   }
916   if(checker->module_metadata) {
917     int i;
918     for(i=0; i<reg_module_id; i++) {
919       struct vp_w_free *tuple;
920       tuple = checker->module_metadata[i];
921       if(tuple && tuple->freefunc) tuple->freefunc(tuple->ptr);
922       free(tuple);
923     }
924     free(checker->module_metadata);
925   }
926   if(checker->module_configs) {
927     int i;
928     for(i=0; i<reg_module_id; i++) {
929       if(checker->module_configs[i]) {
930         noit_hash_destroy(checker->module_configs[i], free, free);
931         free(checker->module_configs[i]);
932       }
933     }
934     free(checker->module_configs);
935   }
936   free(checker);
937 }
938 static int
939 check_recycle_bin_processor(eventer_t e, int mask, void *closure,
940                             struct timeval *now) {
941   static struct timeval one_minute = { 60L, 0L };
942   struct _checker_rcb *prev = NULL, *curr = checker_rcb;
943   noitL(noit_debug, "Scanning check recycle bin\n");
944   while(curr) {
945     if(!(curr->checker->flags & NP_RUNNING)) {
946       noitL(noit_debug, "Check is ready to free.\n");
947       noit_poller_free_check(curr->checker);
948       if(prev) prev->next = curr->next;
949       else checker_rcb = curr->next;
950       free(curr);
951       curr = prev ? prev->next : checker_rcb;
952     }
953     else {
954       prev = curr;
955       curr = curr->next;
956     }
957   }
958   add_timeval(*now, one_minute, &e->whence);
959   return EVENTER_TIMER;
960 }
961
962 int
963 noit_poller_deschedule(uuid_t in) {
964   void *vcheck;
965   noit_check_t *checker;
966   if(noit_hash_retrieve(&polls,
967                         (char *)in, UUID_SIZE,
968                         &vcheck) == 0) {
969     return -1;
970   }
971   checker = (noit_check_t *)vcheck;
972   checker->flags |= (NP_DISABLED|NP_KILLED);
973
974   noit_check_log_delete(checker);
975
976   noit_skiplist_remove(&polls_by_name, checker, NULL);
977   noit_hash_delete(&polls, (char *)in, UUID_SIZE, NULL, NULL);
978
979   noit_poller_free_check(checker);
980   return 0;
981 }
982
983 noit_check_t *
984 noit_poller_lookup(uuid_t in) {
985   void *vcheck;
986   if(noit_hash_retrieve(&polls, (char *)in, UUID_SIZE, &vcheck))
987     return (noit_check_t *)vcheck;
988   return NULL;
989 }
990 noit_check_t *
991 noit_poller_lookup_by_name(char *target, char *name) {
992   noit_check_t *check, *tmp_check;
993   tmp_check = calloc(1, sizeof(*tmp_check));
994   tmp_check->target = target;
995   tmp_check->name = name;
996   check = noit_skiplist_find(&polls_by_name, tmp_check, NULL);
997   free(tmp_check);
998   return check;
999 }
1000 int
1001 noit_poller_target_do(char *target, int (*f)(noit_check_t *, void *),
1002                       void *closure) {
1003   int count = 0;
1004   noit_check_t pivot;
1005   noit_skiplist_node *next;
1006
1007   memset(&pivot, 0, sizeof(pivot));
1008   pivot.target = target;
1009   pivot.name = "";
1010   noit_skiplist_find_neighbors(&polls_by_name, &pivot, NULL, NULL, &next);
1011   while(next && next->data) {
1012     noit_check_t *check = next->data;
1013     if(strcmp(check->target, target)) break;
1014     count += f(check,closure);
1015     noit_skiplist_next(&polls_by_name, &next);
1016   }
1017   return count;
1018 }
1019
1020 int
1021 noit_check_xpath(char *xpath, int len,
1022                  const char *base, const char *arg) {
1023   uuid_t checkid;
1024   int base_trailing_slash;
1025   char argcopy[1024], *target, *module, *name;
1026
1027   base_trailing_slash = (base[strlen(base)-1] == '/');
1028   xpath[0] = '\0';
1029   argcopy[0] = '\0';
1030   if(arg) strlcpy(argcopy, arg, sizeof(argcopy));
1031
1032   if(uuid_parse(argcopy, checkid) == 0) {
1033     /* If they kill by uuid, we'll seek and destroy -- find it anywhere */
1034     snprintf(xpath, len, "/noit/checks%s%s/check[@uuid=\"%s\"]",
1035              base, base_trailing_slash ? "" : "/", argcopy);
1036   }
1037   else if((module = strchr(argcopy, '`')) != NULL) {
1038     noit_check_t *check;
1039     char uuid_str[37];
1040     target = argcopy;
1041     *module++ = '\0';
1042     if((name = strchr(module+1, '`')) == NULL)
1043       name = module;
1044     else
1045       name++;
1046     check = noit_poller_lookup_by_name(target, name);
1047     if(!check) {
1048       return -1;
1049     }
1050     uuid_unparse_lower(check->checkid, uuid_str);
1051     snprintf(xpath, len, "/noit/checks%s%s/check[@uuid=\"%s\"]",
1052              base, base_trailing_slash ? "" : "/", uuid_str);
1053   }
1054   return strlen(xpath);
1055 }
1056
1057 static int
1058 bad_check_initiate(noit_module_t *self, noit_check_t *check,
1059                    int once, noit_check_t *cause) {
1060   /* self is likely null here -- why it is bad, in fact */
1061   /* this is only suitable to call in one-offs */
1062   stats_t current;
1063   char buff[256];
1064   if(!once) return -1;
1065   if(!check) return -1;
1066   assert(!(check->flags & NP_RUNNING));
1067   check->flags |= NP_RUNNING;
1068   noit_check_stats_clear(check, &current);
1069   gettimeofday(&current.whence, NULL);
1070   current.duration = 0;
1071   current.available = NP_UNKNOWN;
1072   current.state = NP_UNKNOWN;
1073   snprintf(buff, sizeof(buff), "check[%s] implementation offline",
1074            check->module);
1075   current.status = buff;
1076   noit_check_set_stats(check, &current);
1077   check->flags &= ~NP_RUNNING;
1078   return 0;
1079 }
1080 void
1081 noit_check_stats_clear(noit_check_t *check, stats_t *s) {
1082   memset(s, 0, sizeof(*s));
1083   s->state = NP_UNKNOWN;
1084   s->available = NP_UNKNOWN;
1085 }
1086 void
1087 free_metric(metric_t *m) {
1088   if(!m) return;
1089   if(m->metric_name) free(m->metric_name);
1090   if(m->metric_value.i) free(m->metric_value.i);
1091   free(m);
1092 }
1093
1094 void
1095 __stats_add_metric(stats_t *newstate, metric_t *m) {
1096   noit_hash_replace(&newstate->metrics, m->metric_name, strlen(m->metric_name),
1097                     m, NULL, (void (*)(void *))free_metric);
1098 }
1099
1100 static size_t
1101 noit_metric_sizes(metric_type_t type, const void *value) {
1102   switch(type) {
1103     case METRIC_INT32:
1104     case METRIC_UINT32:
1105       return sizeof(int32_t);
1106     case METRIC_INT64:
1107     case METRIC_UINT64:
1108       return sizeof(int64_t);
1109     case METRIC_DOUBLE:
1110       return sizeof(double);
1111     case METRIC_STRING:
1112       return strlen((char *)value) + 1;
1113     case METRIC_GUESS:
1114       break;
1115   }
1116   assert(type != type);
1117   return 0;
1118 }
1119 static metric_type_t
1120 noit_metric_guess_type(const char *s, void **replacement) {
1121   char *copy, *cp, *trailer, *rpl;
1122   int negative = 0;
1123   metric_type_t type = METRIC_STRING;
1124
1125   if(!s) return METRIC_GUESS;
1126   copy = cp = strdup(s);
1127
1128   /* TRIM the string */
1129   while(*cp && isspace(*cp)) cp++; /* ltrim */
1130   s = cp; /* found a good starting point */
1131   while(*cp) cp++; /* advance to \0 */
1132   cp--; /* back up one */
1133   while(cp > s && isspace(*cp)) *cp-- = '\0'; /* rtrim */
1134
1135   /* Find the first space */
1136   cp = (char *)s;
1137   while(*cp && !isspace(*cp)) cp++;
1138   trailer = cp;
1139   cp--; /* backup one */
1140   if(cp > s && *cp == '%') *cp-- = '\0'; /* chop a last % is there is one */
1141
1142   while(*trailer && isspace(*trailer)) *trailer++ = '\0'; /* rtrim */
1143
1144   /* string was       '  -1.23e-01%  inodes used  ' */
1145   /* copy is (~ = \0) '  -1.23e-01~  inodes used~~' */
1146   /*                     ^           ^              */
1147   /*                     s           trailer        */
1148
1149   /* So, the trailer must not contain numbers */
1150   while(*trailer) { if(isdigit(*trailer)) goto notanumber; trailer++; }
1151
1152   /* And the 's' must be of the form:
1153    *  0) may start with a sign [-+]?
1154    *  1) [1-9][0-9]*
1155    *  2) [0]?.[0-9]+
1156    *  3) 0
1157    *  4) [1-9][0-9]*.[0-9]+
1158    *  5) all of the above ending with e[+-][0-9]+
1159    */
1160    rpl = (char *)s;
1161    /* CASE 0 */
1162    if(s[0] == '-' || s[0] == '+') {
1163      if(s[0] == '-') negative = 1;
1164      s++;
1165    }
1166
1167    if(s[0] == '.') goto decimal; /* CASE 2 */
1168    if(s[0] == '0') { /* CASE 2 & 3 */
1169      s++;
1170      if(!s[0]) goto scanint; /* CASE 3 */
1171      if(s[0] == '.') goto decimal; /* CASE 2 */
1172      goto notanumber;
1173    }
1174    if(s[0] >= '1' && s[0] <= '9') { /* CASE 1 & 4 */
1175      s++;
1176      while(isdigit(s[0])) s++; /* CASE 1 & 4 */
1177      if(!s[0]) goto scanint; /* CASE 1 */
1178      if(s[0] == '.') goto decimal; /* CASE 4 */
1179      goto notanumber;
1180    }
1181    /* Not case 1,2,3,4 */
1182    goto notanumber;
1183
1184   decimal:
1185    s++;
1186    if(!isdigit(s[0])) goto notanumber;
1187    s++;
1188    while(isdigit(s[0])) s++;
1189    if(!s[0]) goto scandouble;
1190    if(s[0] == 'e' || s[0] == 'E') goto exponent; /* CASE 5 */
1191    goto notanumber;
1192
1193   exponent:
1194    s++;
1195    if(s[0] != '-' && s[0] != '+') goto notanumber;
1196    s++;
1197    if(!isdigit(s[0])) goto notanumber;
1198    s++;
1199    while(isdigit(s[0])) s++;
1200    if(!s[0]) goto scandouble;
1201    goto notanumber;
1202
1203  scanint:
1204    if(negative) {
1205      int64_t *v;
1206      v = calloc(1, sizeof(*v));
1207      *v = strtoll(rpl, NULL, 10);
1208      *replacement = v;
1209      type = METRIC_INT64;
1210      goto alldone;
1211    }
1212    else {
1213      u_int64_t *v;
1214      v = calloc(1, sizeof(*v));
1215      *v = strtoull(rpl, NULL, 10);
1216      *replacement = v;
1217      type = METRIC_UINT64;
1218      goto alldone;
1219    }
1220  scandouble:
1221    {
1222      double *v;
1223      v = calloc(1, sizeof(*v));
1224      *v = strtod(rpl, NULL);
1225      *replacement = v;
1226      type = METRIC_DOUBLE;
1227      goto alldone;
1228    }
1229
1230  alldone:
1231  notanumber:
1232   free(copy);
1233   return type;
1234 }
1235 int
1236 noit_stats_populate_metric(metric_t *m, const char *name, metric_type_t type,
1237                            const void *value) {
1238   void *replacement = NULL;
1239   if(type == METRIC_GUESS)
1240     type = noit_metric_guess_type((char *)value, &replacement);
1241   if(type == METRIC_GUESS) return -1;
1242
1243   m->metric_name = strdup(name);
1244   m->metric_type = type;
1245   if(replacement)
1246     m->metric_value.vp = replacement;
1247   else if(value) {
1248     size_t len;
1249     len = noit_metric_sizes(type, value);
1250     m->metric_value.vp = calloc(1, len);
1251     memcpy(m->metric_value.vp, value, len);
1252   }
1253   return 0;
1254 }
1255 void
1256 noit_stats_set_metric(noit_check_t *check,
1257                       stats_t *newstate, const char *name, metric_type_t type,
1258                       const void *value) {
1259   metric_t *m = calloc(1, sizeof(*m));
1260   if(noit_stats_populate_metric(m, name, type, value)) {
1261     free_metric(m);
1262     return;
1263   }
1264   check_stats_set_metric_hook_invoke(check, newstate, m);
1265   __stats_add_metric(newstate, m);
1266 }
1267 void
1268 noit_stats_set_metric_coerce(noit_check_t *check,
1269                              stats_t *stat, const char *name, metric_type_t t,
1270                              const char *v) {
1271   char *endptr;
1272   if(v == NULL) {
1273    bogus:
1274     noit_stats_set_metric(check, stat, name, t, NULL);
1275     return;
1276   }
1277   switch(t) {
1278     case METRIC_STRING:
1279       noit_stats_set_metric(check, stat, name, t, v);
1280       break;
1281     case METRIC_INT32:
1282     {
1283       int32_t val;
1284       val = strtol(v, &endptr, 10);
1285       if(endptr == v) goto bogus;
1286       noit_stats_set_metric(check, stat, name, t, &val);
1287       break;
1288     }
1289     case METRIC_UINT32:
1290     {
1291       u_int32_t val;
1292       val = strtoul(v, &endptr, 10);
1293       if(endptr == v) goto bogus;
1294       noit_stats_set_metric(check, stat, name, t, &val);
1295       break;
1296     }
1297     case METRIC_INT64:
1298     {
1299       int64_t val;
1300       val = strtoll(v, &endptr, 10);
1301       if(endptr == v) goto bogus;
1302       noit_stats_set_metric(check, stat, name, t, &val);
1303       break;
1304     }
1305     case METRIC_UINT64:
1306     {
1307       u_int64_t val;
1308       val = strtoull(v, &endptr, 10);
1309       if(endptr == v) goto bogus;
1310       noit_stats_set_metric(check, stat, name, t, &val);
1311       break;
1312     }
1313     case METRIC_DOUBLE:
1314     {
1315       double val;
1316       val = strtod(v, &endptr);
1317       if(endptr == v) goto bogus;
1318       noit_stats_set_metric(check, stat, name, t, &val);
1319       break;
1320     }
1321     case METRIC_GUESS:
1322       noit_stats_set_metric(check, stat, name, t, v);
1323       break;
1324   }
1325 }
1326 void
1327 noit_stats_log_immediate_metric(noit_check_t *check,
1328                                 const char *name, metric_type_t type,
1329                                 void *value) {
1330   struct timeval now;
1331   metric_t *m = calloc(1, sizeof(*m));
1332   if(noit_stats_populate_metric(m, name, type, value)) {
1333     free_metric(m);
1334     return;
1335   }
1336   gettimeofday(&now, NULL);
1337   noit_check_log_metric(check, &now, m);
1338   free_metric(m);
1339 }
1340
1341 void
1342 noit_check_passive_set_stats(noit_check_t *check, stats_t *newstate) {
1343   noit_skiplist_node *next;
1344   noit_check_t n;
1345
1346   uuid_copy(n.checkid, check->checkid);
1347   n.period = 0;
1348
1349   noit_check_set_stats(check,newstate);
1350   noit_skiplist_find_neighbors(&watchlist, &n, NULL, NULL, &next);
1351   while(next && next->data) {
1352     stats_t backup;
1353     noit_check_t *wcheck = next->data;
1354     if(uuid_compare(n.checkid, wcheck->checkid)) break;
1355
1356     /* Swap the real check's stats into place */
1357     memcpy(&backup, &wcheck->stats.current, sizeof(stats_t));
1358     memcpy(&wcheck->stats.current, newstate, sizeof(stats_t));
1359
1360     if(check_passive_log_stats_hook_invoke(check) == NOIT_HOOK_CONTINUE) {
1361       /* Write out our status */
1362       noit_check_log_status(wcheck);
1363       /* Write out all metrics */
1364       noit_check_log_metrics(wcheck);
1365     }
1366     /* Swap them back out */
1367     memcpy(&wcheck->stats.current, &backup, sizeof(stats_t));
1368
1369     noit_skiplist_next(&watchlist, &next);
1370   }
1371 }
1372 void
1373 noit_check_set_stats(noit_check_t *check, stats_t *newstate) {
1374   int report_change = 0;
1375   char *cp;
1376   dep_list_t *dep;
1377   if(check->stats.previous.status)
1378     free(check->stats.previous.status);
1379   noit_hash_destroy(&check->stats.previous.metrics, NULL,
1380                     (void (*)(void *))free_metric);
1381   memcpy(&check->stats.previous, &check->stats.current, sizeof(stats_t));
1382   memcpy(&check->stats.current, newstate, sizeof(stats_t));
1383   if(check->stats.current.status)
1384     check->stats.current.status = strdup(check->stats.current.status);
1385   for(cp = check->stats.current.status; cp && *cp; cp++)
1386     if(*cp == '\r' || *cp == '\n') *cp = ' ';
1387
1388   /* check for state changes */
1389   if(check->stats.current.available != NP_UNKNOWN &&
1390      check->stats.previous.available != NP_UNKNOWN &&
1391      check->stats.current.available != check->stats.previous.available)
1392     report_change = 1;
1393   if(check->stats.current.state != NP_UNKNOWN &&
1394      check->stats.previous.state != NP_UNKNOWN &&
1395      check->stats.current.state != check->stats.previous.state)
1396     report_change = 1;
1397
1398   noitL(noit_debug, "%s`%s <- [%s]\n", check->target, check->name,
1399         check->stats.current.status);
1400   if(report_change) {
1401     noitL(noit_debug, "%s`%s -> [%s:%s]\n",
1402           check->target, check->name,
1403           noit_check_available_string(check->stats.current.available),
1404           noit_check_state_string(check->stats.current.state));
1405   }
1406
1407   if(NOIT_CHECK_STATUS_ENABLED()) {
1408     char id[UUID_STR_LEN+1];
1409     uuid_unparse_lower(check->checkid, id);
1410     NOIT_CHECK_STATUS(id, check->module, check->name, check->target,
1411                       check->stats.current.available,
1412                       check->stats.current.state,
1413                       check->stats.current.status);
1414   }
1415
1416   if(check_log_stats_hook_invoke(check) == NOIT_HOOK_CONTINUE) {
1417     /* Write out the bundled information */
1418     noit_check_log_bundle(check);
1419   }
1420   /* count the check as complete */
1421   check_completion_count++;
1422
1423   for(dep = check->causal_checks; dep; dep = dep->next) {
1424     noit_module_t *mod;
1425     mod = noit_module_lookup(dep->check->module);
1426     if(!mod) {
1427       bad_check_initiate(mod, dep->check, 1, check);
1428     }
1429     else {
1430       noitL(noit_debug, "Firing %s`%s in response to %s`%s\n",
1431             dep->check->target, dep->check->name,
1432             check->target, check->name);
1433       if((dep->check->flags & NP_DISABLED) == 0)
1434         if(mod->initiate_check)
1435           mod->initiate_check(mod, dep->check, 1, check);
1436     }
1437   }
1438 }
1439
1440 static int
1441 noit_console_show_watchlist(noit_console_closure_t ncct,
1442                             int argc, char **argv,
1443                             noit_console_state_t *dstate,
1444                             void *closure) {
1445   noit_skiplist_node *iter, *fiter;
1446   nc_printf(ncct, "%d active watches.\n", watchlist.size);
1447   for(iter = noit_skiplist_getlist(&watchlist); iter;
1448       noit_skiplist_next(&watchlist, &iter)) {
1449     char uuid_str[UUID_STR_LEN + 1];
1450     noit_check_t *check = iter->data;
1451
1452     uuid_unparse_lower(check->checkid, uuid_str);
1453     nc_printf(ncct, "%s:\n\t[%s`%s`%s]\n\tPeriod: %dms\n\tFeeds[%d]:\n",
1454               uuid_str, check->target, check->module, check->name,
1455               check->period, check->feeds ? check->feeds->size : 0);
1456     if(check->feeds && check->feeds->size) {
1457       for(fiter = noit_skiplist_getlist(check->feeds); fiter;
1458           noit_skiplist_next(check->feeds, &fiter)) {
1459         nc_printf(ncct, "\t\t%s\n", (const char *)fiter->data);
1460       }
1461     }
1462   }
1463   return 0;
1464 }
1465
1466 static void
1467 nc_printf_check_brief(noit_console_closure_t ncct,
1468                       noit_check_t *check) {
1469   char out[512];
1470   char uuid_str[37];
1471   snprintf(out, sizeof(out), "%s`%s", check->target, check->name);
1472   uuid_unparse_lower(check->checkid, uuid_str);
1473   nc_printf(ncct, "%s %s\n", uuid_str, out);
1474   if(check->stats.current.status)
1475     nc_printf(ncct, "\t%s\n", check->stats.current.status);
1476 }
1477
1478 char *
1479 noit_console_conf_check_opts(noit_console_closure_t ncct,
1480                              noit_console_state_stack_t *stack,
1481                              noit_console_state_t *dstate,
1482                              int argc, char **argv, int idx) {
1483   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
1484   uuid_t key_id;
1485   int klen, i = 0;
1486   void *vcheck;
1487
1488   if(argc == 1) {
1489     if(!strncmp("new", argv[0], strlen(argv[0]))) {
1490       if(idx == i) return strdup("new");
1491       i++;
1492     }
1493     while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
1494                          &vcheck)) {
1495       noit_check_t *check = (noit_check_t *)vcheck;
1496       char out[512];
1497       char uuid_str[37];
1498       snprintf(out, sizeof(out), "%s`%s", check->target, check->name);
1499       uuid_unparse_lower(check->checkid, uuid_str);
1500       if(!strncmp(out, argv[0], strlen(argv[0]))) {
1501         if(idx == i) return strdup(out);
1502         i++;
1503       }
1504       if(!strncmp(uuid_str, argv[0], strlen(argv[0]))) {
1505         if(idx == i) return strdup(uuid_str);
1506         i++;
1507       }
1508     }
1509   }
1510   if(argc == 2) {
1511     cmd_info_t *cmd;
1512     if(!strcmp("new", argv[0])) return NULL;
1513     cmd = noit_skiplist_find(&dstate->cmds, "attribute", NULL);
1514     if(!cmd) return NULL;
1515     return noit_console_opt_delegate(ncct, stack, cmd->dstate, argc-1, argv+1, idx);
1516   }
1517   return NULL;
1518 }
1519
1520 char *
1521 noit_console_check_opts(noit_console_closure_t ncct,
1522                         noit_console_state_stack_t *stack,
1523                         noit_console_state_t *dstate,
1524                         int argc, char **argv, int idx) {
1525   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
1526   uuid_t key_id;
1527   int klen, i = 0;
1528
1529   if(argc == 1) {
1530     void *vcheck;
1531     while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
1532                          &vcheck)) {
1533       char out[512];
1534       char uuid_str[37];
1535       noit_check_t *check = (noit_check_t *)vcheck;
1536       snprintf(out, sizeof(out), "%s`%s", check->target, check->name);
1537       uuid_unparse_lower(check->checkid, uuid_str);
1538       if(!strncmp(out, argv[0], strlen(argv[0]))) {
1539         if(idx == i) return strdup(out);
1540         i++;
1541       }
1542       if(!strncmp(uuid_str, argv[0], strlen(argv[0]))) {
1543         if(idx == i) return strdup(uuid_str);
1544         i++;
1545       }
1546     }
1547   }
1548   if(argc == 2) {
1549     return noit_console_opt_delegate(ncct, stack, dstate, argc-1, argv+1, idx);
1550   }
1551   return NULL;
1552 }
1553
1554 static int
1555 noit_console_show_checks(noit_console_closure_t ncct,
1556                          int argc, char **argv,
1557                          noit_console_state_t *dstate,
1558                          void *closure) {
1559   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
1560   uuid_t key_id;
1561   int klen;
1562   void *vcheck;
1563
1564   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
1565                        &vcheck)) {
1566     nc_printf_check_brief(ncct, (noit_check_t *)vcheck);
1567   }
1568   return 0;
1569 }
1570
1571 static void
1572 register_console_check_commands() {
1573   noit_console_state_t *tl;
1574   cmd_info_t *showcmd;
1575
1576   tl = noit_console_state_initial();
1577   showcmd = noit_console_state_get_cmd(tl, "show");
1578   assert(showcmd && showcmd->dstate);
1579
1580   noit_console_state_add_cmd(showcmd->dstate,
1581     NCSCMD("timing_slots", noit_console_show_timing_slots, NULL, NULL, NULL));
1582
1583   noit_console_state_add_cmd(showcmd->dstate,
1584     NCSCMD("checks", noit_console_show_checks, NULL, NULL, NULL));
1585
1586   noit_console_state_add_cmd(showcmd->dstate,
1587     NCSCMD("watches", noit_console_show_watchlist, NULL, NULL, NULL));
1588 }
1589
1590 int
1591 noit_check_register_module(const char *name) {
1592   int i;
1593   for(i=0; i<reg_module_id; i++)
1594     if(!strcmp(reg_module_names[i], name)) return i;
1595   if(reg_module_id >= MAX_MODULE_REGISTRATIONS) return -1;
1596   noitL(noit_debug, "Registered module %s as %d\n", name, i);
1597   i = reg_module_id++;
1598   reg_module_names[i] = strdup(name);
1599   noit_conf_set_namespace(reg_module_names[i]);
1600   return i;
1601 }
1602 int
1603 noit_check_registered_module_cnt() {
1604   return reg_module_id;
1605 }
1606 const char *
1607 noit_check_registered_module(int idx) {
1608   if(reg_module_used < 0) reg_module_used = reg_module_id;
1609   assert(reg_module_used == reg_module_id);
1610   if(idx >= reg_module_id || idx < 0) return NULL;
1611   return reg_module_names[idx];
1612 }
1613
1614 void
1615 noit_check_set_module_metadata(noit_check_t *c, int idx, void *md, void (*freefunc)(void *)) {
1616   struct vp_w_free *tuple;
1617   if(reg_module_used < 0) reg_module_used = reg_module_id;
1618   assert(reg_module_used == reg_module_id);
1619   if(idx >= reg_module_id || idx < 0) return;
1620   if(!c->module_metadata) c->module_metadata = calloc(reg_module_id, sizeof(void *));
1621   c->module_metadata[idx] = calloc(1, sizeof(struct vp_w_free));
1622   tuple = c->module_metadata[idx];
1623   tuple->ptr = md;
1624   tuple->freefunc = freefunc;
1625 }
1626 void
1627 noit_check_set_module_config(noit_check_t *c, int idx, noit_hash_table *config) {
1628   if(reg_module_used < 0) reg_module_used = reg_module_id;
1629   assert(reg_module_used == reg_module_id);
1630   if(idx >= reg_module_id || idx < 0) return;
1631   if(!c->module_configs) c->module_configs = calloc(reg_module_id, sizeof(noit_hash_table *));
1632   c->module_configs[idx] = config;
1633 }
1634 void *
1635 noit_check_get_module_metadata(noit_check_t *c, int idx) {
1636   struct vp_w_free *tuple;
1637   if(reg_module_used < 0) reg_module_used = reg_module_id;
1638   assert(reg_module_used == reg_module_id);
1639   if(idx >= reg_module_id || idx < 0 || !c->module_metadata) return NULL;
1640   tuple = c->module_metadata[idx];
1641   return tuple ? tuple->ptr : NULL;
1642 }
1643 noit_hash_table *
1644 noit_check_get_module_config(noit_check_t *c, int idx) {
1645   if(reg_module_used < 0) reg_module_used = reg_module_id;
1646   assert(reg_module_used == reg_module_id);
1647   if(idx >= reg_module_id || idx < 0 || !c->module_configs) return NULL;
1648   return c->module_configs[idx];
1649 }
Note: See TracBrowser for help on using the browser.