root/src/noit_check.c

Revision 01144b78af5408a407165c8b36e6a644cc9d3696, 37.5 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 years ago)

print an error if this occurs

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