root/src/noit_check.c

Revision 397f56dda527bb85b65aa7a4ab60c1f289361565, 50.3 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 years ago)

The spread of the checks across time was bunched to the beginning of
the second for no sound reason. More dastardly, every DNS based check
could tend to coallesce over time around the point of a DNS timeout.
Now checks stay true to their originally set period of their initial
schedule time.

This can be seen a runtime through the console command: "show timing_slots"

DNS-based checks still suffer from an immediate fire "at boot".

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