root/src/noit_check.c

Revision 899f2d2b74c6a8e1d8fc9a0ede60f226f83d551d, 42.8 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 2 years ago)

Performance enhancement populating check metrics from json in lua.
(as in, don't do it in lua, do it all in C). 1.6x speedup.

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