root/src/noit_check.c

Revision 16b1bdd1a6ac2cff3a9d3145c7d31de06255c661, 41.4 kB (checked in by gdusbabek <gdusbabek@gmail.com>, 3 years ago)

move resolve_rtypes flag computation into a helper

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