root/src/noit_check.c

Revision d38990d06dbc481418b2317122b36d481acdae86, 31.7 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

fixes #213 w/ examples

  • 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 <netinet/in.h>
41 #include <arpa/inet.h>
42
43 #include "utils/noit_log.h"
44 #include "utils/noit_hash.h"
45 #include "utils/noit_skiplist.h"
46 #include "noit_conf.h"
47 #include "noit_check.h"
48 #include "noit_module.h"
49 #include "noit_console.h"
50 #include "noit_check_tools.h"
51 #include "eventer/eventer.h"
52
53 /* 60 seconds of possible stutter */
54 #define MAX_INITIAL_STUTTER 60000
55
56 static noit_hash_table polls = NOIT_HASH_EMPTY;
57 static noit_skiplist watchlist = { 0 };
58 static noit_skiplist polls_by_name = { 0 };
59 static u_int32_t __config_load_generation = 0;
60 struct uuid_dummy {
61   uuid_t foo;
62 };
63
64 static void register_console_check_commands();
65 static int check_recycle_bin_processor(eventer_t, int, void *,
66                                        struct timeval *);
67 #define UUID_SIZE sizeof(struct uuid_dummy)
68
69 const char *
70 noit_check_available_string(int16_t available) {
71   switch(available) {
72     case NP_AVAILABLE:    return "available";
73     case NP_UNAVAILABLE:  return "unavailable";
74     case NP_UNKNOWN:      return "unknown";
75   }
76   return NULL;
77 }
78 const char *
79 noit_check_state_string(int16_t state) {
80   switch(state) {
81     case NP_GOOD:         return "good";
82     case NP_BAD:          return "bad";
83     case NP_UNKNOWN:      return "unknown";
84   }
85   return NULL;
86 }
87 static int __check_name_compare(const void *a, const void *b) {
88   const noit_check_t *ac = a;
89   const noit_check_t *bc = b;
90   int rv;
91   if((rv = strcmp(ac->target, bc->target)) != 0) return rv;
92   if((rv = strcmp(ac->name, bc->name)) != 0) return rv;
93   return 0;
94 }
95 static int __watchlist_compare(const void *a, const void *b) {
96   const noit_check_t *ac = a;
97   const noit_check_t *bc = b;
98   int rv;
99   if((rv = memcmp(ac->checkid, bc->checkid, sizeof(ac->checkid))) != 0) return rv;
100   if(ac->period < bc->period) return -1;
101   if(ac->period == bc->period) return 0;
102   return 1;
103 }
104 int
105 noit_check_max_initial_stutter() {
106   int stutter;
107   if(!noit_conf_get_int(NULL, "/noit/checks/@max_initial_stutter", &stutter))
108     stutter = MAX_INITIAL_STUTTER;
109   return stutter;
110 }
111 void
112 noit_check_fake_last_check(noit_check_t *check,
113                            struct timeval *lc, struct timeval *_now) {
114   struct timeval now, period;
115   double r;
116   int offset = 0, max;
117
118   if(!(check->flags & NP_TRANSIENT)) {
119     r = drand48();
120     max = noit_check_max_initial_stutter();
121     offset = r * (MIN(max, check->period));
122   }
123   period.tv_sec = (check->period - offset) / 1000;
124   period.tv_usec = ((check->period - offset) % 1000) * 1000;
125   if(!_now) {
126     gettimeofday(&now, NULL);
127     _now = &now;
128   }
129   sub_timeval(*_now, period, lc);
130 }
131 void
132 noit_poller_process_checks(const char *xpath) {
133   int i, flags, cnt = 0;
134   noit_conf_section_t *sec;
135   __config_load_generation++;
136   sec = noit_conf_get_sections(NULL, xpath, &cnt);
137   for(i=0; i<cnt; i++) {
138     void *vcheck;
139     char uuid_str[37];
140     char target[256] = "";
141     char module[256] = "";
142     char name[256] = "";
143     char filterset[256] = "";
144     char oncheck[1024] = "";
145     int no_period = 0;
146     int no_oncheck = 0;
147     int period = 0, timeout = 0;
148     noit_boolean disabled = noit_false, busted = noit_false;
149     uuid_t uuid, out_uuid;
150     noit_hash_table *options;
151
152 #define NEXT(...) noitL(noit_stderr, __VA_ARGS__); continue
153 #define MYATTR(type,a,...) noit_conf_get_##type(sec[i], "@" #a, __VA_ARGS__)
154 #define INHERIT(type,a,...) \
155   noit_conf_get_##type(sec[i], "ancestor-or-self::node()/@" #a, __VA_ARGS__)
156
157     if(!MYATTR(stringbuf, uuid, uuid_str, sizeof(uuid_str))) {
158       noitL(noit_stderr, "check %d has no uuid\n", i+1);
159       continue;
160     }
161
162     if(uuid_parse(uuid_str, uuid)) {
163       noitL(noit_stderr, "check uuid: '%s' is invalid\n", uuid_str);
164       continue;
165     }
166
167     if(!INHERIT(stringbuf, target, target, sizeof(target))) {
168       noitL(noit_stderr, "check uuid: '%s' has no target\n", uuid_str);
169       busted = noit_true;
170     }
171     if(!INHERIT(stringbuf, module, module, sizeof(module))) {
172       noitL(noit_stderr, "check uuid: '%s' has no module\n", uuid_str);
173       busted = noit_true;
174     }
175
176     if(!INHERIT(stringbuf, filterset, filterset, sizeof(filterset)))
177       filterset[0] = '\0';
178
179     if(!MYATTR(stringbuf, name, name, sizeof(name)))
180       strlcpy(name, module, sizeof(name));
181
182     if(!INHERIT(int, period, &period) || period == 0)
183       no_period = 1;
184
185     if(!INHERIT(stringbuf, oncheck, oncheck, sizeof(oncheck)) || !oncheck[0])
186       no_oncheck = 1;
187
188     if(no_period && no_oncheck) {
189       noitL(noit_stderr, "check uuid: '%s' has neither period nor oncheck\n",
190             uuid_str);
191       busted = noit_true;
192     }
193     if(!(no_period || no_oncheck)) {
194       noitL(noit_stderr, "check uuid: '%s' has oncheck and period.\n",
195             uuid_str);
196       busted = noit_true;
197     }
198     if(!INHERIT(int, timeout, &timeout)) {
199       noitL(noit_stderr, "check uuid: '%s' has no timeout\n", uuid_str);
200       busted = noit_true;
201     }
202     if(!no_period && timeout >= period) {
203       noitL(noit_stderr, "check uuid: '%s' timeout > period\n", uuid_str);
204       timeout = period/2;
205     }
206     options = noit_conf_get_hash(sec[i], "config");
207
208     INHERIT(boolean, disable, &disabled);
209     flags = 0;
210     if(busted) flags |= NP_UNCONFIG;
211     if(disabled) flags |= NP_DISABLED;
212
213     if(noit_hash_retrieve(&polls, (char *)uuid, UUID_SIZE,
214                           &vcheck)) {
215       noit_check_t *existing_check = (noit_check_t *)vcheck;
216       /* Once set, we can never change it. */
217       assert(!existing_check->module || !existing_check->module[0] ||
218              !strcmp(existing_check->module, module));
219       /* Only set it if it is not yet set */
220       if(!existing_check->module || !existing_check->module[0]) {
221         if(existing_check->module) free(existing_check->module);
222         existing_check->module = strdup(module);
223       }
224       noit_check_update(existing_check, target, name, filterset, options,
225                            period, timeout, oncheck[0] ? oncheck : NULL,
226                            flags);
227       noitL(noit_debug, "reloaded uuid: %s\n", uuid_str);
228     }
229     else {
230       noit_poller_schedule(target, module, name, filterset, options,
231                            period, timeout, oncheck[0] ? oncheck : NULL,
232                            flags, uuid, out_uuid);
233       noitL(noit_debug, "loaded uuid: %s\n", uuid_str);
234     }
235
236     noit_hash_destroy(options, free, free);
237     free(options);
238   }
239   if(sec) free(sec);
240 }
241
242 int
243 noit_check_activate(noit_check_t *check) {
244   noit_module_t *mod;
245   if(NOIT_CHECK_LIVE(check)) return 0;
246   mod = noit_module_lookup(check->module);
247   if(mod && mod->initiate_check) {
248     if((check->flags & NP_DISABLED) == 0) {
249       mod->initiate_check(mod, check, 0, NULL);
250       return 1;
251     }
252     else
253       noitL(noit_debug, "Skipping %s`%s, disabled.\n",
254             check->target, check->name);
255   }
256   else {
257     if(!mod) {
258       noitL(noit_stderr, "Cannot find module '%s'\n", check->module);
259       check->flags |= NP_DISABLED;
260     }
261   }
262   return 0;
263 }
264
265 void
266 noit_poller_initiate() {
267   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
268   uuid_t key_id;
269   int klen;
270   void *vcheck;
271   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
272                        &vcheck)) {
273     noit_check_activate((noit_check_t *)vcheck);
274   }
275 }
276
277 void
278 noit_poller_flush_epoch(int oldest_allowed) {
279   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
280   uuid_t key_id;
281   int klen;
282   noit_check_t *tofree = NULL;
283   void *vcheck;
284
285   /* Cleanup any previous causal map */
286   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
287                        &vcheck)) {
288     noit_check_t *check = (noit_check_t *)vcheck;
289     /* We don't free the one we're looking at... we free it on the next
290      * pass.  This leaves out iterator in good shape.  We just need to
291      * remember to free it one last time outside the while loop, down...
292      */
293     if(tofree) {
294       noit_poller_deschedule(tofree->checkid);
295       tofree = NULL;
296     }
297     if(check->generation < oldest_allowed) {
298       tofree = check;
299     }
300   }
301   /* ... here */
302   if(tofree) noit_poller_deschedule(tofree->checkid);
303 }
304
305 void
306 noit_poller_make_causal_map() {
307   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
308   uuid_t key_id;
309   int klen;
310   void *vcheck;
311
312   /* Cleanup any previous causal map */
313   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
314                        &vcheck)) {
315     noit_check_t *check = (noit_check_t *)vcheck;
316     dep_list_t *dep;
317     while((dep = check->causal_checks) != NULL) {
318       check->causal_checks = dep->next;
319       free(dep);
320     }
321   }
322
323   memset(&iter, 0, sizeof(iter));
324   /* Walk all checks and add check dependencies to their parents */
325   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
326                        &vcheck)) {
327     noit_check_t *check = (noit_check_t *)vcheck, *parent;
328     if(check->oncheck) {
329       /* This service is causally triggered by another service */
330       char fullcheck[1024];
331       char *name = check->oncheck;
332       char *target = NULL;
333
334       noitL(noit_debug, "Searching for upstream trigger on %s\n", name);
335       if((target = strchr(check->oncheck, '`')) != NULL) {
336         strlcpy(fullcheck, check->oncheck, target - check->oncheck);
337         name = target + 1;
338         target = fullcheck;
339       }
340       else
341        target = check->target;
342
343       parent = noit_poller_lookup_by_name(target, name);
344       if(!parent) {
345         check->flags |= NP_DISABLED;
346         noitL(noit_stderr, "Disabling check %s`%s, can't find oncheck %s`%s\n",
347               check->target, check->name, target, name);
348       }
349       else {
350         dep_list_t *dep;
351         dep = malloc(sizeof(*dep));
352         dep->check = check;
353         dep->next = parent->causal_checks;
354         parent->causal_checks = dep;
355         noitL(noit_debug, "Causal map %s`%s --> %s`%s\n",
356               parent->target, parent->name, check->target, check->name);
357       }
358     }
359   }
360 }
361 void
362 noit_poller_reload(const char *xpath)
363 {
364   noit_poller_process_checks(xpath ? xpath : "/noit/checks//check");
365   if(!xpath) {
366     /* Full reload, we need to wipe old checks */
367     noit_poller_flush_epoch(__config_load_generation);
368   }
369   noit_poller_make_causal_map();
370   noit_poller_initiate();
371 }
372 void
373 noit_poller_init() {
374   noit_check_tools_init();
375   noit_skiplist_init(&polls_by_name);
376   noit_skiplist_set_compare(&polls_by_name, __check_name_compare,
377                             __check_name_compare);
378   noit_skiplist_init(&watchlist);
379   noit_skiplist_set_compare(&watchlist, __watchlist_compare,
380                             __watchlist_compare);
381   register_console_check_commands();
382   eventer_name_callback("check_recycle_bin_processor",
383                         check_recycle_bin_processor);
384   eventer_add_in_s_us(check_recycle_bin_processor, NULL, 60, 0);
385   noit_poller_reload(NULL);
386 }
387
388 int
389 noit_poller_check_count() {
390   return polls_by_name.size;
391 }
392
393 int
394 noit_poller_transient_check_count() {
395   return watchlist.size;
396 }
397
398 noit_check_t *
399 noit_check_clone(uuid_t in) {
400   noit_check_t *checker, *new_check;
401   void *vcheck;
402   if(noit_hash_retrieve(&polls,
403                         (char *)in, UUID_SIZE,
404                         &vcheck) == 0) {
405     return NULL;
406   }
407   checker = (noit_check_t *)vcheck;
408   if(checker->oncheck) {
409     return NULL;
410   }
411   new_check = calloc(1, sizeof(*new_check));
412   memcpy(new_check, checker, sizeof(*new_check));
413   new_check->target = strdup(new_check->target);
414   new_check->module = strdup(new_check->module);
415   new_check->name = strdup(new_check->name);
416   new_check->filterset = strdup(new_check->filterset);
417   new_check->flags = 0;
418   new_check->fire_event = NULL;
419   memset(&new_check->last_fire_time, 0, sizeof(new_check->last_fire_time));
420   memset(&new_check->stats, 0, sizeof(new_check->stats));
421   new_check->closure = NULL;
422   new_check->config = calloc(1, sizeof(*new_check->config));
423   noit_hash_merge_as_dict(new_check->config, checker->config);
424   return new_check;
425 }
426
427 noit_check_t *
428 noit_check_watch(uuid_t in, int period) {
429   /* First look for a copy that is being watched */
430   noit_check_t n, *f;
431
432   uuid_copy(n.checkid, in);
433   n.period = period;
434
435   f = noit_skiplist_find(&watchlist, &n, NULL);
436   if(f) return f;
437   f = noit_check_clone(in);
438   if(!f) return NULL;
439   f->period = period;
440   f->timeout = period - 10;
441   f->flags |= NP_TRANSIENT;
442   noit_skiplist_insert(&watchlist, f);
443   return f;
444 }
445
446 noit_check_t *
447 noit_check_get_watch(uuid_t in, int period) {
448   noit_check_t n, *f;
449
450   uuid_copy(n.checkid, in);
451   n.period = period;
452
453   f = noit_skiplist_find(&watchlist, &n, NULL);
454   return f;
455 }
456
457 void
458 noit_check_transient_add_feed(noit_check_t *check, const char *feed) {
459   char *feedcopy;
460   if(!check->feeds) {
461     check->feeds = calloc(1, sizeof(*check->feeds));
462     noit_skiplist_init(check->feeds);
463     noit_skiplist_set_compare(check->feeds,
464                               (noit_skiplist_comparator_t)strcmp,
465                               (noit_skiplist_comparator_t)strcmp);
466   }
467   feedcopy = strdup(feed);
468   /* No error on failure -- it's already there */
469   if(noit_skiplist_insert(check->feeds, feedcopy) == NULL) free(feedcopy);
470   noitL(noit_debug, "check %s`%s @ %dms has %d feed(s): %s.\n",
471         check->target, check->name, check->period, check->feeds->size, feed);
472 }
473 void
474 noit_check_transient_remove_feed(noit_check_t *check, const char *feed) {
475   if(!check->feeds) return;
476   if(feed) {
477     noitL(noit_debug, "check %s`%s @ %dms removing 1 of %d feeds: %s.\n",
478           check->target, check->name, check->period, check->feeds->size, feed);
479     noit_skiplist_remove(check->feeds, feed, free);
480   }
481   if(check->feeds->size == 0) {
482     noit_skiplist_remove(&watchlist, check, NULL);
483     noit_skiplist_destroy(check->feeds, free);
484     free(check->feeds);
485     check->feeds = NULL;
486     if(check->flags & NP_TRANSIENT) {
487       noitL(noit_debug, "check %s`%s @ %dms has no more listeners.\n",
488             check->target, check->name, check->period);
489       check->flags |= NP_KILLED;
490     }
491   }
492 }
493
494 noit_boolean
495 noit_check_is_valid_target(const char *target) {
496   int8_t family;
497   int rv;
498   union {
499     struct in_addr addr4;
500     struct in6_addr addr6;
501   } a;
502
503   family = AF_INET;
504   rv = inet_pton(family, target, &a);
505   if(rv != 1) {
506     family = AF_INET6;
507     rv = inet_pton(family, target, &a);
508     if(rv != 1) {
509       return noit_false;
510     }
511   }
512   return noit_true;
513 }
514 int
515 noit_check_update(noit_check_t *new_check,
516                   const char *target,
517                   const char *name,
518                   const char *filterset,
519                   noit_hash_table *config,
520                   u_int32_t period,
521                   u_int32_t timeout,
522                   const char *oncheck,
523                   int flags) {
524   int8_t family;
525   int rv;
526   int mask = NP_DISABLED | NP_UNCONFIG;
527   union {
528     struct in_addr addr4;
529     struct in6_addr addr6;
530   } a;
531
532
533   family = AF_INET;
534   rv = inet_pton(family, target, &a);
535   if(rv != 1) {
536     family = AF_INET6;
537     rv = inet_pton(family, target, &a);
538     if(rv != 1) {
539       noitL(noit_stderr, "Cannot translate '%s' to IP\n", target);
540       memset(&a, 0, sizeof(a));
541       flags |= (NP_UNCONFIG & NP_DISABLED);
542     }
543   }
544
545   new_check->generation = __config_load_generation;
546   new_check->target_family = family;
547   memcpy(&new_check->target_addr, &a, sizeof(a));
548   if(new_check->target) free(new_check->target);
549   new_check->target = strdup(target);
550   if(new_check->name) free(new_check->name);
551   new_check->name = name ? strdup(name): NULL;
552   if(new_check->filterset) free(new_check->filterset);
553   new_check->filterset = filterset ? strdup(filterset): NULL;
554
555   if(config != NULL) {
556     noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
557     const char *k;
558     int klen;
559     void *data;
560     if(new_check->config) noit_hash_delete_all(new_check->config, free, free);
561     else new_check->config = calloc(1, sizeof(*new_check->config));
562     while(noit_hash_next(config, &iter, &k, &klen, &data)) {
563       noit_hash_store(new_check->config, strdup(k), klen, strdup((char *)data));
564     }
565   }
566   if(new_check->oncheck) free(new_check->oncheck);
567   new_check->oncheck = oncheck ? strdup(oncheck) : NULL;
568   new_check->period = period;
569   new_check->timeout = timeout;
570
571   /* Unset what could be set.. then set what should be set */
572   new_check->flags = (new_check->flags & ~mask) | flags;
573
574   if(!(new_check->flags & NP_TRANSIENT)) {
575     /* This remove could fail -- no big deal */
576     noit_skiplist_remove(&polls_by_name, new_check, NULL);
577
578     /* This insert could fail.. which means we have a conflict on
579      * target`name.  That should result in the check being disabled. */
580     if(!noit_skiplist_insert(&polls_by_name, new_check)) {
581       noitL(noit_stderr, "Check %s`%s disabled due to naming conflict\n",
582             new_check->target, new_check->name);
583       new_check->flags |= NP_DISABLED;
584     }
585   }
586   noit_check_log_check(new_check);
587   return 0;
588 }
589 int
590 noit_poller_schedule(const char *target,
591                      const char *module,
592                      const char *name,
593                      const char *filterset,
594                      noit_hash_table *config,
595                      u_int32_t period,
596                      u_int32_t timeout,
597                      const char *oncheck,
598                      int flags,
599                      uuid_t in,
600                      uuid_t out) {
601   noit_check_t *new_check;
602   new_check = calloc(1, sizeof(*new_check));
603   if(!new_check) return -1;
604
605   /* The module and the UUID can never be changed */
606   new_check->module = strdup(module);
607   if(uuid_is_null(in))
608     uuid_generate(new_check->checkid);
609   else
610     uuid_copy(new_check->checkid, in);
611
612   noit_check_update(new_check, target, name, filterset, config,
613                     period, timeout, oncheck, flags);
614   assert(noit_hash_store(&polls,
615                          (char *)new_check->checkid, UUID_SIZE,
616                          new_check));
617   uuid_copy(out, new_check->checkid);
618
619   return 0;
620 }
621
622 /* A quick little list of recycleable checks.  This list never really
623  * grows large, so no sense in thinking too hard about the algorithmic
624  * complexity.
625  */
626 struct _checker_rcb {
627   noit_check_t *checker;
628   struct _checker_rcb *next;
629 };
630 static struct _checker_rcb *checker_rcb = NULL;
631 static void recycle_check(noit_check_t *checker) {
632   struct _checker_rcb *n = malloc(sizeof(*n));
633   n->checker = checker;
634   n->next = checker_rcb;
635   checker_rcb = n;
636 }
637 void
638 noit_poller_free_check(noit_check_t *checker) {
639   noit_module_t *mod;
640
641   if(checker->flags & NP_RUNNING) {
642     recycle_check(checker);
643     return;
644   }
645
646   mod = noit_module_lookup(checker->module);
647   if(mod->cleanup) mod->cleanup(mod, checker);
648   if(checker->fire_event) {
649      eventer_remove(checker->fire_event);
650      free(checker->fire_event->closure);
651      eventer_free(checker->fire_event);
652      checker->fire_event = NULL;
653   }
654   if(checker->closure) free(checker->closure);
655   if(checker->target) free(checker->target);
656   if(checker->module) free(checker->module);
657   if(checker->name) free(checker->name);
658   if(checker->config) {
659     noit_hash_destroy(checker->config, free, free);
660     free(checker->config);
661     checker->config = NULL;
662   }
663   free(checker);
664 }
665 static int
666 check_recycle_bin_processor(eventer_t e, int mask, void *closure,
667                             struct timeval *now) {
668   static struct timeval one_minute = { 60L, 0L };
669   struct _checker_rcb *prev = NULL, *curr = checker_rcb;
670   noitL(noit_debug, "Scanning check recycle bin\n");
671   while(curr) {
672     if(!(curr->checker->flags & NP_RUNNING)) {
673       noitL(noit_debug, "Check is ready to free.\n");
674       noit_poller_free_check(curr->checker);
675       if(prev) prev->next = curr->next;
676       else checker_rcb = curr->next;
677       free(curr);
678       curr = prev ? prev->next : checker_rcb;
679     }
680     else {
681       prev = curr;
682       curr = curr->next;
683     }
684   }
685   add_timeval(*now, one_minute, &e->whence);
686   return EVENTER_TIMER;
687 }
688
689 int
690 noit_poller_deschedule(uuid_t in) {
691   void *vcheck;
692   noit_check_t *checker;
693   if(noit_hash_retrieve(&polls,
694                         (char *)in, UUID_SIZE,
695                         &vcheck) == 0) {
696     return -1;
697   }
698   checker = (noit_check_t *)vcheck;
699   checker->flags |= (NP_DISABLED|NP_KILLED);
700
701   noit_skiplist_remove(&polls_by_name, checker, NULL);
702   noit_hash_delete(&polls, (char *)in, UUID_SIZE, NULL, NULL);
703
704   noit_poller_free_check(checker);
705   return 0;
706 }
707
708 noit_check_t *
709 noit_poller_lookup(uuid_t in) {
710   void *vcheck;
711   if(noit_hash_retrieve(&polls, (char *)in, UUID_SIZE, &vcheck))
712     return (noit_check_t *)vcheck;
713   return NULL;
714 }
715 noit_check_t *
716 noit_poller_lookup_by_name(char *target, char *name) {
717   noit_check_t *check, *tmp_check;
718   tmp_check = calloc(1, sizeof(*tmp_check));
719   tmp_check->target = target;
720   tmp_check->name = name;
721   check = noit_skiplist_find(&polls_by_name, tmp_check, NULL);
722   free(tmp_check);
723   return check;
724 }
725
726 void
727 noit_check_stats_clear(stats_t *s) {
728   memset(s, 0, sizeof(*s));
729   s->state = NP_UNKNOWN;
730   s->available = NP_UNKNOWN;
731 }
732 static void
733 __free_metric(void *vm) {
734   metric_t *m = vm;
735   free(m->metric_name);
736   if(m->metric_value.i) free(m->metric_value.i);
737   free(m);
738 }
739
740 void
741 __stats_add_metric(stats_t *newstate, metric_t *m) {
742   noit_hash_replace(&newstate->metrics, m->metric_name, strlen(m->metric_name),
743                     m, NULL, __free_metric);
744 }
745
746 static size_t
747 noit_metric_sizes(metric_type_t type, void *value) {
748   switch(type) {
749     case METRIC_INT32:
750     case METRIC_UINT32:
751       return sizeof(int32_t);
752     case METRIC_INT64:
753     case METRIC_UINT64:
754       return sizeof(int64_t);
755     case METRIC_DOUBLE:
756       return sizeof(double);
757     case METRIC_STRING:
758       return strlen((char *)value) + 1;
759     case METRIC_GUESS:
760       break;
761   }
762   assert(type != type);
763   return 0;
764 }
765 static metric_type_t
766 noit_metric_guess_type(const char *s, void **replacement) {
767   char *copy, *cp, *trailer, *rpl;
768   int negative = 0, bigF = 0;
769   metric_type_t type = METRIC_STRING;
770
771   if(!s) return METRIC_GUESS;
772   copy = cp = strdup(s);
773
774   /* TRIM the string */
775   while(*cp && isspace(*cp)) cp++; /* ltrim */
776   s = cp; /* found a good starting point */
777   while(*cp) cp++; /* advance to \0 */
778   cp--; /* back up one */
779   while(cp > s && isspace(*cp)) *cp-- = '\0'; /* rtrim */
780
781   /* Find the first space */
782   cp = (char *)s;
783   while(*cp && !isspace(*cp)) cp++;
784   trailer = cp;
785   cp--; /* backup one */
786   if(cp > s && *cp == '%') *cp-- = '\0'; /* chop a last % is there is one */
787
788   while(*trailer && isspace(*trailer)) *trailer++ = '\0'; /* rtrim */
789
790   /* string was       '  -1.23e-01%  inodes used  ' */
791   /* copy is (~ = \0) '  -1.23e-01~  inodes used~~' */
792   /*                     ^           ^              */
793   /*                     s           trailer        */
794
795   /* So, the trailer must not contain numbers */
796   while(*trailer) { if(isdigit(*trailer)) goto notanumber; trailer++; }
797
798   /* And the 's' must be of the form:
799    *  0) may start with a sign [-+]?
800    *  1) [1-9][0-9]*
801    *  2) [0]?.[0-9]+
802    *  3) 0
803    *  4) [1-9][0-9]*.[0-9]+
804    *  5) all of the above ending with e[+-][0-9]+
805    */
806    rpl = (char *)s;
807    /* CASE 0 */
808    if(s[0] == '-' || s[0] == '+') {
809      if(s[0] == '-') negative = 1;
810      s++;
811    }
812
813    if(s[0] == '.') goto decimal; /* CASE 2 */
814    if(s[0] == '0') { /* CASE 2 & 3 */
815      s++;
816      if(!s[0]) goto scanint; /* CASE 3 */
817      if(s[0] == '.') goto decimal; /* CASE 2 */
818      goto notanumber;
819    }
820    if(s[0] >= '1' && s[0] <= '9') { /* CASE 1 & 4 */
821      s++;
822      while(isdigit(s[0])) s++; /* CASE 1 & 4 */
823      if(!s[0]) goto scanint; /* CASE 1 */
824      if(s[0] == '.') goto decimal; /* CASE 4 */
825      goto notanumber;
826    }
827    /* Not case 1,2,3,4 */
828    goto notanumber;
829
830   decimal:
831    s++;
832    if(!isdigit(s[0])) goto notanumber;
833    s++;
834    while(isdigit(s[0])) s++;
835    if(!s[0]) goto scandouble;
836    if(s[0] == 'e' || s[0] == 'E') goto exponent; /* CASE 5 */
837    goto notanumber;
838
839   exponent:
840    if(s[0] == 'E') bigF = 1; /* We want the caps variant */
841    s++;
842    if(s[0] != '-' && s[0] != '+') goto notanumber;
843    s++;
844    if(!isdigit(s[0])) goto notanumber;
845    s++;
846    while(isdigit(s[0])) s++;
847    if(!s[0]) goto scandouble;
848    goto notanumber;
849
850  scanint:
851    if(negative) {
852      int64_t *v;
853      v = calloc(1, sizeof(*v));
854      *v = strtoll(rpl, NULL, 10);
855      *replacement = v;
856      type = METRIC_INT64;
857      goto alldone;
858    }
859    else {
860      u_int64_t *v;
861      v = calloc(1, sizeof(*v));
862      *v = strtoull(rpl, NULL, 10);
863      *replacement = v;
864      type = METRIC_UINT64;
865      goto alldone;
866    }
867  scandouble:
868    {
869      double *v;
870      v = calloc(1, sizeof(*v));
871      *v = strtod(rpl, NULL);
872      *replacement = v;
873      type = METRIC_DOUBLE;
874      goto alldone;
875    }
876
877  alldone:
878  notanumber:
879   free(copy);
880   return type;
881 }
882 void
883 noit_stats_set_metric(stats_t *newstate, const char *name, metric_type_t type,
884                       void *value) {
885   metric_t *m;
886   void *replacement = NULL;
887   if(type == METRIC_GUESS)
888     type = noit_metric_guess_type((char *)value, &replacement);
889   if(type == METRIC_GUESS) return;
890
891   m = calloc(1, sizeof(*m));
892   m->metric_name = strdup(name);
893   m->metric_type = type;
894   if(replacement)
895     m->metric_value.vp = replacement;
896   else if(value) {
897     size_t len;
898     len = noit_metric_sizes(type, value);
899     m->metric_value.vp = calloc(1, len);
900     memcpy(m->metric_value.vp, value, len);
901   }
902   __stats_add_metric(newstate, m);
903 }
904
905 void
906 noit_check_set_stats(struct _noit_module *module,
907                      noit_check_t *check, stats_t *newstate) {
908   int report_change = 0;
909   dep_list_t *dep;
910   if(check->stats.previous.status)
911     free(check->stats.previous.status);
912   noit_hash_destroy(&check->stats.previous.metrics, NULL, __free_metric);
913   memcpy(&check->stats.previous, &check->stats.current, sizeof(stats_t));
914   memcpy(&check->stats.current, newstate, sizeof(stats_t));
915   if(check->stats.current.status)
916     check->stats.current.status = strdup(check->stats.current.status);
917
918   /* check for state changes */
919   if(check->stats.current.available != NP_UNKNOWN &&
920      check->stats.previous.available != NP_UNKNOWN &&
921      check->stats.current.available != check->stats.previous.available)
922     report_change = 1;
923   if(check->stats.current.state != NP_UNKNOWN &&
924      check->stats.previous.state != NP_UNKNOWN &&
925      check->stats.current.state != check->stats.previous.state)
926     report_change = 1;
927
928   noitL(noit_debug, "%s`%s <- [%s]\n", check->target, check->name,
929         check->stats.current.status);
930   if(report_change) {
931     noitL(noit_debug, "%s`%s -> [%s:%s]\n",
932           check->target, check->name,
933           noit_check_available_string(check->stats.current.available),
934           noit_check_state_string(check->stats.current.state));
935   }
936
937   /* Write out our status */
938   noit_check_log_status(check);
939   /* Write out all metrics */
940   noit_check_log_metrics(check);
941
942   for(dep = check->causal_checks; dep; dep = dep->next) {
943     noit_module_t *mod;
944     mod = noit_module_lookup(dep->check->module);
945     assert(mod);
946     noitL(noit_debug, "Firing %s`%s in response to %s`%s\n",
947           dep->check->target, dep->check->name,
948           check->target, check->name);
949     if((dep->check->flags & NP_DISABLED) == 0)
950       if(mod->initiate_check)
951         mod->initiate_check(mod, dep->check, 1, check);
952   }
953 }
954
955 static void
956 nc_printf_check_brief(noit_console_closure_t ncct,
957                       noit_check_t *check) {
958   char out[512];
959   char uuid_str[37];
960   snprintf(out, sizeof(out), "%s`%s", check->target, check->name);
961   uuid_unparse_lower(check->checkid, uuid_str);
962   nc_printf(ncct, "%s %s\n", uuid_str, out);
963   if(check->stats.current.status)
964     nc_printf(ncct, "\t%s\n", check->stats.current.status);
965 }
966
967 char *
968 noit_console_conf_check_opts(noit_console_closure_t ncct,
969                              noit_console_state_stack_t *stack,
970                              noit_console_state_t *dstate,
971                              int argc, char **argv, int idx) {
972   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
973   uuid_t key_id;
974   int klen, i = 0;
975   void *vcheck;
976
977   if(argc == 1) {
978     if(!strncmp("new", argv[0], strlen(argv[0]))) {
979       if(idx == i) return strdup("new");
980       i++;
981     }
982     while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
983                          &vcheck)) {
984       noit_check_t *check = (noit_check_t *)vcheck;
985       char out[512];
986       char uuid_str[37];
987       snprintf(out, sizeof(out), "%s`%s", check->target, check->name);
988       uuid_unparse_lower(check->checkid, uuid_str);
989       if(!strncmp(out, argv[0], strlen(argv[0]))) {
990         if(idx == i) return strdup(out);
991         i++;
992       }
993       if(!strncmp(uuid_str, argv[0], strlen(argv[0]))) {
994         if(idx == i) return strdup(uuid_str);
995         i++;
996       }
997     }
998   }
999   if(argc == 2) {
1000     cmd_info_t *cmd;
1001     if(!strcmp("new", argv[0])) return NULL;
1002     cmd = noit_skiplist_find(&dstate->cmds, "attribute", NULL);
1003     if(!cmd) return NULL;
1004     return noit_console_opt_delegate(ncct, stack, cmd->dstate, argc-1, argv+1, idx);
1005   }
1006   return NULL;
1007 }
1008
1009 char *
1010 noit_console_check_opts(noit_console_closure_t ncct,
1011                         noit_console_state_stack_t *stack,
1012                         noit_console_state_t *dstate,
1013                         int argc, char **argv, int idx) {
1014   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
1015   uuid_t key_id;
1016   int klen, i = 0;
1017
1018   if(argc == 1) {
1019     void *vcheck;
1020     while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
1021                          &vcheck)) {
1022       char out[512];
1023       char uuid_str[37];
1024       noit_check_t *check = (noit_check_t *)vcheck;
1025       snprintf(out, sizeof(out), "%s`%s", check->target, check->name);
1026       uuid_unparse_lower(check->checkid, uuid_str);
1027       if(!strncmp(out, argv[0], strlen(argv[0]))) {
1028         if(idx == i) return strdup(out);
1029         i++;
1030       }
1031       if(!strncmp(uuid_str, argv[0], strlen(argv[0]))) {
1032         if(idx == i) return strdup(uuid_str);
1033         i++;
1034       }
1035     }
1036   }
1037   if(argc == 2) {
1038     return noit_console_opt_delegate(ncct, stack, dstate, argc-1, argv+1, idx);
1039   }
1040   return NULL;
1041 }
1042
1043 static int
1044 noit_console_show_checks(noit_console_closure_t ncct,
1045                          int argc, char **argv,
1046                          noit_console_state_t *dstate,
1047                          void *closure) {
1048   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
1049   uuid_t key_id;
1050   int klen;
1051   void *vcheck;
1052
1053   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
1054                        &vcheck)) {
1055     nc_printf_check_brief(ncct, (noit_check_t *)vcheck);
1056   }
1057   return 0;
1058 }
1059
1060 static void
1061 register_console_check_commands() {
1062   noit_console_state_t *tl;
1063   cmd_info_t *showcmd;
1064
1065   tl = noit_console_state_initial();
1066   showcmd = noit_console_state_get_cmd(tl, "show");
1067   assert(showcmd && showcmd->dstate);
1068
1069   noit_console_state_add_cmd(showcmd->dstate,
1070     NCSCMD("checks", noit_console_show_checks, NULL, NULL, NULL));
1071 }
1072
Note: See TracBrowser for help on using the browser.