root/src/noit_check.c

Revision 85e32ea88e1d7ad88a960783bb3033f7c1844de3, 50.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

meta information on the timing slot allocations via console

  • 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 MAX_MODULE_REGISTRATIONS 64
78
79 /* used to manage per-check generic module metadata */
80 struct vp_w_free {
81   void *ptr;
82   void (*freefunc)(void *);
83 };
84
85 static int reg_module_id = 0;
86 static char *reg_module_names[MAX_MODULE_REGISTRATIONS] = { NULL };
87 static int reg_module_used = -1;
88 static u_int64_t check_completion_count = 0;
89 static noit_hash_table polls = NOIT_HASH_EMPTY;
90 static noit_skiplist watchlist = { 0 };
91 static noit_skiplist polls_by_name = { 0 };
92 static u_int32_t __config_load_generation = 0;
93 static unsigned short check_slots_count[60000 / SCHEDULE_GRANULARITY] = { 0 },
94                       check_slots_seconds_count[60] = { 0 };
95
96 void
97 debug_print_slots(noit_console_closure_t ncct) {
98 }
99 static int
100 noit_console_show_timing_slots(noit_console_closure_t ncct,
101                                int argc, char **argv,
102                                noit_console_state_t *dstate,
103                                void *closure) {
104   int i, j;
105   const int upl = (60000 / SCHEDULE_GRANULARITY) / 60;
106   for(i=0;i<60;i++) {
107     nc_printf(ncct, "[%02d] %04d: ", i, check_slots_seconds_count[i]);
108     for(j=i*upl;j<(i+1)*upl;j++) {
109       char cp = '!';
110       if(check_slots_count[j] < 10) cp = '0' + check_slots_count[j];
111       else if(check_slots_count[j] < 36) cp = 'a' + (check_slots_count[j] - 10);
112       nc_printf(ncct, "%c", cp);
113     }
114     nc_printf(ncct, "\n");
115   }
116   return 0;
117 }
118
119 u_int64_t noit_check_completion_count() {
120   return check_completion_count;
121 }
122 static void register_console_check_commands();
123 static int check_recycle_bin_processor(eventer_t, int, void *,
124                                        struct timeval *);
125
126 static int
127 check_slots_find_smallest(int sec) {
128   int i, j, jbase = 0, mini = 0, minj = 0;
129   unsigned short min_running_i = 0xffff, min_running_j = 0xffff;
130   for(i=0;i<60;i++) {
131     int adj_i = (i + sec) % 60;
132     if(check_slots_seconds_count[adj_i] < min_running_i) {
133       min_running_i = check_slots_seconds_count[adj_i];
134       mini = adj_i;
135     }
136   }
137   jbase = mini * (1000/SCHEDULE_GRANULARITY);
138   for(j=jbase;j<jbase+(1000/SCHEDULE_GRANULARITY);j++) {
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.