root/src/noit_check.c

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

make noit_poller_free_check safe to call externally and expose it

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