root/src/noit_check.c

Revision 46b9cf0be63c479e256c74b79f413f940c8f0563, 48.5 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

make setting of metrcis hookable

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