root/src/noit_check.c

Revision 8c720f9a4bd5979520e9c7ec00d9bea8d305391b, 35.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 years ago)

this is a big patch. adds an optional extended-id. you should upgrade your iep first, then your stratcon, then your noits. This is most certainly a flag-day, *but* the feature is off by default... refs #331

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