root/src/noit_check.c

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

fixes #175

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