root/src/noit_check.c

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

merge dtrace branch to master

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