root/src/noit_check.c

Revision 6210da7ee0e2ed143d71a8e00b709f16e71059f8, 28.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 9 years ago)

various changes to avoid dereferencing type-punned pointers and breaking strict-aliasing rules, refs #34

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