root/src/noit_check.c

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

this can be null -- which simply doesn't change flags

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