db_updater: Put parentheses back
[merlin.git] / shared.c
blob6d46a45ff70c4bafc70a1da6c9b2b268f17333a4
1 #include "shared.h"
2 #include "ipc.h"
4 /** global variables present in both daemon and module **/
5 int debug = 0; /* doesn't actually do anything right now */
6 int is_module = 1; /* the daemon sets this to 0 immediately */
7 int pulse_interval = 10;
8 int use_database = 0;
9 char *merlin_config_file = NULL;
10 merlin_nodeinfo *self = NULL;
11 char *binlog_dir = "/opt/monitor/op5/merlin/binlogs";
13 #ifndef ISSPACE
14 # define ISSPACE(c) (c == ' ' || c == '\t')
15 #endif
18 char *next_word(char *str)
20 while (!ISSPACE(*str) && *str != 0)
21 str++;
23 while (ISSPACE(*str) || *str == ',')
24 str++;
26 if (*str)
27 return str;
29 return NULL;
33 * Crack a string into several pieces based on the delimiter
35 strvec *str_explode(char *str, int delim)
37 int i = 0, entries = 1;
38 char *p;
39 struct strvec *ret = NULL;
41 if (!str || !*str)
42 return NULL;
44 p = str;
45 while ((p = strchr(p + 1, delim))) {
46 entries++;
49 ret = malloc(sizeof(*ret));
50 ret->entries = entries;
51 ret->str = malloc(entries * sizeof(char *));
52 ret->str[i++] = p = str;
53 while ((p = strchr(p, delim))) {
54 *p++ = 0;
55 ret->str[i++] = p;
58 return ret;
62 * "yes", "true", "on" and any non-zero integer makes us return 1
63 * For every other case, we return 0.
65 int strtobool(const char *str)
67 int c = tolower(*str);
69 if (!str || !*str)
70 return 0;
72 if (c == 'y' || c == 't' || c == '1')
73 return 1;
74 if (c == 'o' && tolower(str[1]) == 'n')
75 return 1;
77 return !!atoi(str);
81 * grok second-based intervals, with suffixes.
82 * "1h 3m 4s" should return 3600 + 180 + 3 = 3783.
83 * "0.5h 0.5m" should return 1800 + 30 = 1830
84 * Subsecond precision is not possible (obviously...)
86 * Limited to "week" as its highest possible suffix and
87 * quite clumsy and forgiving in its parsing. Since we'll
88 * most likely be dealing with very short strings I don't
89 * care overly about that though.
91 int grok_seconds(const char *p, long *result)
93 const char *real_end, suffix[] = "smhdw";
94 int factors[] = { 1, 60, 3600, 3600 * 24, 3600 * 24 * 7 };
95 long res = 0;
97 if (!p)
98 return -1;
100 real_end = p + strlen(p);
102 while (p && *p && p < real_end) {
103 int factor;
104 double val;
105 char *endp, *pos;
107 /* skip whitespace between one suffix and the next value */
108 while (*p == ' ' || *p == '\t')
109 p++;
111 /* trailing whitespace in *p */
112 if (!*p) {
113 *result = res;
114 return 0;
117 val = strtod(p, &endp);
118 if (!val && endp == p) {
119 /* invalid value */
120 return -1;
123 /* valid value. set p to endp and look for a suffix */
124 p = endp;
125 while (*p == ' ' || *p == '\t')
126 p++;
128 /* trailing whitespace (again) */
129 if (!*p) {
130 res += val;
131 *result = res;
132 return 0;
135 /* if there's no suffix we just move on */
136 pos = strchr(suffix, *p);
137 if (!pos) {
138 res += val;
139 continue;
142 factor = pos - suffix;
143 val *= factors[factor];
144 res += val;
146 while (*p && *p != ' ' && *p != '\t' && (*p < '0' || *p > '9'))
147 p++;
149 if (!*p)
150 break;
153 *result = res;
155 return 0;
160 * Returns an ISO 8601 formatted date string (YYYY-MM-DD HH:MM:SS.UUU)
161 * with the desired precision.
162 * Semi-threadsafe since we round-robin rotate between two buffers for
163 * the return value
165 const char *isotime(struct timeval *tv, int precision)
167 static char buffers[2][32];
168 static int bufi = 0;
169 struct timeval now;
170 struct tm tm;
171 char *buf;
172 int bufsize;
173 size_t len;
175 bufsize = sizeof(buffers[0]) - 1;
177 if (!tv) {
178 gettimeofday(&now, NULL);
179 tv = &now;
182 buf = buffers[bufi++ % ARRAY_SIZE(buffers)];
183 localtime_r(&tv->tv_sec, &tm);
185 switch (precision) {
186 case ISOTIME_PREC_YEAR:
187 len = strftime(buf, sizeof(buffers[0]) - 1, "%Y", &tm);
188 break;
189 case ISOTIME_PREC_MONTH:
190 len = strftime(buf, sizeof(buffers[0]) - 1, "%Y-%m", &tm);
191 break;
192 case ISOTIME_PREC_DAY:
193 len = strftime(buf, sizeof(buffers[0]) - 1, "%F", &tm);
194 break;
195 case ISOTIME_PREC_HOUR:
196 len = strftime(buf, sizeof(buffers[0]) - 1, "%F %H", &tm);
197 break;
198 case ISOTIME_PREC_MINUTE:
199 len = strftime(buf, sizeof(buffers[0]) - 1, "%F %H:%M", &tm);
200 break;
201 case ISOTIME_PREC_SECOND:
202 case ISOTIME_PREC_USECOND:
203 default: /* second precision is the default */
204 len = strftime(buf, sizeof(buffers[0]) - 1, "%F %H:%M:%S", &tm);
205 break;
208 if (precision != ISOTIME_PREC_USECOND)
209 return buf;
210 snprintf(&buf[len], bufsize - len, ".%4lu", (unsigned long)tv->tv_usec);
212 return buf;
217 * converts an arbitrarily long string of data into its
218 * hexadecimal representation
220 char *tohex(const unsigned char *data, int len)
222 /* number of bufs must be a power of 2 */
223 static char bufs[4][41], hex[] = "0123456789abcdef";
224 static int bufno;
225 char *buf;
226 int i;
228 buf = bufs[bufno & (ARRAY_SIZE(bufs) - 1)];
229 for (i = 0; i < 20 && i < len; i++) {
230 unsigned int val = *data++;
231 *buf++ = hex[val >> 4];
232 *buf++ = hex[val & 0xf];
234 *buf = '\0';
236 return bufs[bufno++ & (ARRAY_SIZE(bufs) - 1)];
239 #define CB_ENTRY(s) { NEBCALLBACK_##s##_DATA, #s, sizeof(#s) - 1 }
240 static struct {
241 int id;
242 char *name;
243 uint name_len;
244 } callback_list[NEBCALLBACK_NUMITEMS] = {
245 CB_ENTRY(PROCESS),
246 CB_ENTRY(TIMED_EVENT),
247 CB_ENTRY(LOG),
248 CB_ENTRY(SYSTEM_COMMAND),
249 CB_ENTRY(EVENT_HANDLER),
250 CB_ENTRY(NOTIFICATION),
251 CB_ENTRY(SERVICE_CHECK),
252 CB_ENTRY(HOST_CHECK),
253 CB_ENTRY(COMMENT),
254 CB_ENTRY(DOWNTIME),
255 CB_ENTRY(FLAPPING),
256 CB_ENTRY(PROGRAM_STATUS),
257 CB_ENTRY(HOST_STATUS),
258 CB_ENTRY(SERVICE_STATUS),
259 CB_ENTRY(ADAPTIVE_PROGRAM),
260 CB_ENTRY(ADAPTIVE_HOST),
261 CB_ENTRY(ADAPTIVE_SERVICE),
262 CB_ENTRY(EXTERNAL_COMMAND),
263 CB_ENTRY(AGGREGATED_STATUS),
264 CB_ENTRY(RETENTION),
265 CB_ENTRY(CONTACT_NOTIFICATION),
266 CB_ENTRY(CONTACT_NOTIFICATION_METHOD),
267 CB_ENTRY(ACKNOWLEDGEMENT),
268 CB_ENTRY(STATE_CHANGE),
269 CB_ENTRY(CONTACT_STATUS),
270 CB_ENTRY(ADAPTIVE_CONTACT)
273 const char *callback_name(int id)
275 static char *num_name = NULL;
276 if (id < 0 || id > NEBCALLBACK_NUMITEMS - 1) {
277 if (id == CTRL_PACKET)
278 return "CTRL_PACKET";
279 if (num_name)
280 free(num_name);
281 asprintf(&num_name, "(invalid/unknown %d)", id);
282 return num_name;
285 return callback_list[id].name;
288 int callback_id(const char *orig_name)
290 uint i, len;
291 char name[100];
293 if (!orig_name)
294 return -1;
296 len = strlen(orig_name);
297 if (len > sizeof(name))
298 return -1;
300 for (i = 0; i < len; i++) {
301 name[i] = toupper(orig_name[i]);
303 name[i] = '\0';
305 for (i = 0; i < ARRAY_SIZE(callback_list); i++) {
306 if (len != callback_list[i].name_len)
307 continue;
309 if (!strcmp(callback_list[i].name, name)) {
310 return callback_list[i].id;
314 /* not found */
315 return -1;
318 #define CTRL_ENTRY(s) "CTRL_"#s
319 static const char *control_names[] = {
320 CTRL_ENTRY(NOTHING),
321 CTRL_ENTRY(PULSE),
322 CTRL_ENTRY(INACTIVE),
323 CTRL_ENTRY(ACTIVE),
324 CTRL_ENTRY(PATHS),
325 CTRL_ENTRY(STALL),
326 CTRL_ENTRY(RESUME),
327 CTRL_ENTRY(STOP),
329 const char *ctrl_name(uint code)
331 if (code >= ARRAY_SIZE(control_names))
332 return "(invalid/unknown)";
333 if (code == CTRL_GENERIC)
334 return "CTRL_GENERIC";
335 return control_names[code];
338 const char *node_state_name(int state)
340 switch (state) {
341 case STATE_NONE: return "STATE_NONE";
342 case STATE_PENDING: return "STATE_PENDING";
343 case STATE_NEGOTIATING: return "STATE_NEGOTIATING";
344 case STATE_CONNECTED: return "STATE_CONNECTED";
347 return "STATE_unknown_voodoo";
350 #if (defined(__GLIBC__) && (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1))
351 #include <execinfo.h>
352 void bt_scan(const char *mark, int count)
354 #define TRACE_SIZE 100
355 char **strings;
356 void *trace[TRACE_SIZE];
357 int i, bt_count, have_mark = 0;
359 bt_count = backtrace(trace, TRACE_SIZE);
360 if (!bt_count)
361 return;
362 strings = backtrace_symbols(trace, bt_count);
363 if (!strings)
364 return;
366 for (i = 0; i < bt_count; i++) {
367 char *paren;
369 if (mark && !have_mark) {
370 if (strstr(strings[i], mark))
371 have_mark = i;
372 continue;
374 if (mark && count && i >= have_mark + count)
375 break;
376 paren = strchr(strings[i], '(');
377 paren = paren ? paren : strings[i];
378 ldebug("%2d: %s", i, paren);
380 free(strings);
382 #else
383 void bt_scan(const char *mark, int count) {}
384 #endif
386 static const char *config_key_expires(const char *var)
388 if (!strcmp(var, "ipc_debug_write"))
389 return "2011-05";
390 if (!strcmp(var, "ipc_debug_read"))
391 return "2011-05";
393 return NULL;
396 /* converts huge values to a more humanfriendly byte-representation */
397 const char *human_bytes(unsigned long long n)
399 const char *suffix = "KMGTP";
400 static char tbuf[8][16];
401 static int t = 0;
402 unsigned int shift = 1;
404 t++;
405 t &= 0x7;
406 if (n < 1024) {
407 sprintf(tbuf[t], "%llu bytes", n);
408 return tbuf[t];
411 while (n >> (shift * 10) > 1024 && shift < strlen(suffix) - 1)
412 shift++;
414 sprintf(tbuf[t], "%0.2f %ciB",
415 (float)n / (float)(1L << (shift * 10)), suffix[shift - 1]);
417 return tbuf[t];
420 linked_item *add_linked_item(linked_item *list, void *item)
422 struct linked_item *entry = malloc(sizeof(linked_item));
424 if (!entry) {
425 lerr("Failed to malloc(%u): %s", sizeof(linked_item), strerror(errno));
426 return NULL;
429 entry->item = item;
430 entry->next_item = list;
431 return entry;
434 const char *tv_delta(const struct timeval *start, const struct timeval *stop)
436 static char buf[50];
437 unsigned long weeks, days, hours, mins, secs, usecs;
438 time_t stop_usec;
440 secs = stop->tv_sec - start->tv_sec;
441 stop_usec = stop->tv_usec;
442 if (stop_usec < start->tv_usec) {
443 secs--;
444 stop_usec += 1000000;
446 usecs = stop_usec - start->tv_usec;
448 /* we only want 2 decimals */
449 while (usecs > 1000)
450 usecs /= 1000;
452 weeks = secs / 604800;
453 secs -= weeks * 604800;
454 days = secs / 86400;
455 secs -= days * 86400;
456 hours = secs / 3600;
457 secs -= hours * 3600;
458 mins = secs / 60;
459 secs -= mins * 60;
461 if (!mins && !hours && !days) {
462 sprintf(buf, "%lu.%03lus", secs, usecs);
463 } else if (!hours && !days) {
464 sprintf(buf, "%lum %lu.%03lus", mins, secs, usecs);
465 } else if (!days) {
466 sprintf(buf, "%luh %lum %lu.%03lus", hours, mins, secs, usecs);
467 } else if (!weeks){
468 sprintf(buf, "%lud %luh %lum %lu.%03lus", days, hours, mins, secs, usecs);
469 } else {
470 sprintf(buf, "%luw %lud %luh %lum %lu.%03lus",
471 weeks, days, hours, mins, secs, usecs);
474 return buf;
478 * Parse config sync options.
480 * This is used for each node and also in the daemon compound
482 int grok_confsync_compound(struct cfg_comp *comp, merlin_confsync *csync)
484 unsigned i;
486 if (!comp || !csync) {
487 return -1;
491 * first we reset it. An empty compound in the configuration
492 * means "reset the defaults and don't bother syncing this
493 * server automagically"
495 memset(csync, 0, sizeof(*csync));
496 for (i = 0; i < comp->vars; i++) {
497 struct cfg_var *v = comp->vlist[i];
498 if (!strcmp(v->key, "push")) {
499 csync->push.cmd = strdup(v->value);
500 continue;
502 if (!strcmp(v->key, "fetch") || !strcmp(v->key, "pull")) {
503 csync->fetch.cmd = strdup(v->value);
504 continue;
507 * we ignore additional variables here, since the
508 * config sync script may want to add additional
509 * stuff to handle
513 return 0;
516 static int grok_binlog_var(const char *key, const char *value)
518 if (!strcmp(key, "binlog_dir")) {
519 binlog_dir = strdup(value);
520 return 1;
523 return 0;
526 int grok_common_var(struct cfg_comp *config, struct cfg_var *v)
528 const char *expires;
530 if (!strcmp(v->key, "pulse_interval")) {
531 pulse_interval = (unsigned)strtoul(v->value, NULL, 10);
532 if (!pulse_interval) {
533 cfg_warn(config, v, "Illegal pulse_interval. Using default.");
534 pulse_interval = 10;
536 return 1;
539 expires = config_key_expires(v->key);
540 if (expires) {
541 cfg_warn(config, v, "'%s' is a deprecated variable, scheduled for "
542 "removal at the first release after %s", v->key, expires);
543 /* it's understood, in a way */
544 return 1;
547 if (!prefixcmp(v->key, "ipc_")) {
548 if (!ipc_grok_var(v->key, v->value))
549 cfg_error(config, v, "Failed to grok IPC option");
551 return 1;
554 if (!prefixcmp(v->key, "log_")) {
555 if (!log_grok_var(v->key, v->value))
556 cfg_error(config, v, "Failed to grok logging option");
558 return 1;
561 if (!prefixcmp(v->key, "binlog_")) {
562 if (!grok_binlog_var(v->key, v->value))
563 cfg_error(config, v, "Failed to grok binlog option");
565 return 1;
567 return 0;
571 * Set some common socket options
573 int merlin_set_socket_options(int sd, int bufsize)
576 * make sure random output from import programs and whatnot
577 * doesn't carry over into the net_sock
579 fcntl(sd, F_SETFD, FD_CLOEXEC);
581 /* make socket non-blocking */
582 fcntl(sd, F_SETFL, O_NONBLOCK);
584 if (bufsize) {
585 if (setsockopt(sd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(int)) < 0) {
586 ldebug("Failed to set sendbuffer for %d to %d bytes: %s",
587 sd, bufsize, strerror(errno));
589 if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(int)) < 0) {
590 ldebug("Failed to set recvbuffer for %d to %d bytes: %s",
591 sd, bufsize, strerror(errno));
595 return 0;