root/src/noit_check.c

Revision d10f13b10b05856e66c66dc5f7efb410316d9d6d, 39.0 kB (checked in by Dan Di Spaltro <dan@cloudkick.com>, 3 years ago)

Add Delete messages to noit so we get a message when we deschedule a check

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