root/src/noit_check.c

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

make collect work better and support watches

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