root/src/noit_check.c

Revision 204ecdc5ad373fb076d81ab8a63fc09df931be4e, 49.3 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

hookable log phases

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