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;
9 char *merlin_config_file
= NULL
;
10 merlin_nodeinfo
*self
= NULL
;
11 char *binlog_dir
= "/opt/monitor/op5/merlin/binlogs";
14 # define ISSPACE(c) (c == ' ' || c == '\t')
18 char *next_word(char *str
)
20 while (!ISSPACE(*str
) && *str
!= 0)
23 while (ISSPACE(*str
) || *str
== ',')
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;
39 struct strvec
*ret
= NULL
;
45 while ((p
= strchr(p
+ 1, delim
))) {
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
))) {
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
);
72 if (c
== 'y' || c
== 't' || c
== '1')
74 if (c
== 'o' && tolower(str
[1]) == 'n')
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 };
100 real_end
= p
+ strlen(p
);
102 while (p
&& *p
&& p
< real_end
) {
107 /* skip whitespace between one suffix and the next value */
108 while (*p
== ' ' || *p
== '\t')
111 /* trailing whitespace in *p */
117 val
= strtod(p
, &endp
);
118 if (!val
&& endp
== p
) {
123 /* valid value. set p to endp and look for a suffix */
125 while (*p
== ' ' || *p
== '\t')
128 /* trailing whitespace (again) */
135 /* if there's no suffix we just move on */
136 pos
= strchr(suffix
, *p
);
142 factor
= pos
- suffix
;
143 val
*= factors
[factor
];
146 while (*p
&& *p
!= ' ' && *p
!= '\t' && (*p
< '0' || *p
> '9'))
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
165 const char *isotime(struct timeval
*tv
, int precision
)
167 static char buffers
[2][32];
175 bufsize
= sizeof(buffers
[0]) - 1;
178 gettimeofday(&now
, NULL
);
182 buf
= buffers
[bufi
++ % ARRAY_SIZE(buffers
)];
183 localtime_r(&tv
->tv_sec
, &tm
);
186 case ISOTIME_PREC_YEAR
:
187 len
= strftime(buf
, sizeof(buffers
[0]) - 1, "%Y", &tm
);
189 case ISOTIME_PREC_MONTH
:
190 len
= strftime(buf
, sizeof(buffers
[0]) - 1, "%Y-%m", &tm
);
192 case ISOTIME_PREC_DAY
:
193 len
= strftime(buf
, sizeof(buffers
[0]) - 1, "%F", &tm
);
195 case ISOTIME_PREC_HOUR
:
196 len
= strftime(buf
, sizeof(buffers
[0]) - 1, "%F %H", &tm
);
198 case ISOTIME_PREC_MINUTE
:
199 len
= strftime(buf
, sizeof(buffers
[0]) - 1, "%F %H:%M", &tm
);
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
);
208 if (precision
!= ISOTIME_PREC_USECOND
)
210 snprintf(&buf
[len
], bufsize
- len
, ".%4lu", (unsigned long)tv
->tv_usec
);
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";
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];
236 return bufs
[bufno
++ & (ARRAY_SIZE(bufs
) - 1)];
239 #define CB_ENTRY(s) { NEBCALLBACK_##s##_DATA, #s, sizeof(#s) - 1 }
244 } callback_list
[NEBCALLBACK_NUMITEMS
] = {
246 CB_ENTRY(TIMED_EVENT
),
248 CB_ENTRY(SYSTEM_COMMAND
),
249 CB_ENTRY(EVENT_HANDLER
),
250 CB_ENTRY(NOTIFICATION
),
251 CB_ENTRY(SERVICE_CHECK
),
252 CB_ENTRY(HOST_CHECK
),
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
),
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";
281 asprintf(&num_name
, "(invalid/unknown %d)", id
);
285 return callback_list
[id
].name
;
288 int callback_id(const char *orig_name
)
296 len
= strlen(orig_name
);
297 if (len
> sizeof(name
))
300 for (i
= 0; i
< len
; i
++) {
301 name
[i
] = toupper(orig_name
[i
]);
305 for (i
= 0; i
< ARRAY_SIZE(callback_list
); i
++) {
306 if (len
!= callback_list
[i
].name_len
)
309 if (!strcmp(callback_list
[i
].name
, name
)) {
310 return callback_list
[i
].id
;
318 #define CTRL_ENTRY(s) "CTRL_"#s
319 static const char *control_names
[] = {
322 CTRL_ENTRY(INACTIVE
),
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
)
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
356 void *trace
[TRACE_SIZE
];
357 int i
, bt_count
, have_mark
= 0;
359 bt_count
= backtrace(trace
, TRACE_SIZE
);
362 strings
= backtrace_symbols(trace
, bt_count
);
366 for (i
= 0; i
< bt_count
; i
++) {
369 if (mark
&& !have_mark
) {
370 if (strstr(strings
[i
], mark
))
374 if (mark
&& count
&& i
>= have_mark
+ count
)
376 paren
= strchr(strings
[i
], '(');
377 paren
= paren
? paren
: strings
[i
];
378 ldebug("%2d: %s", i
, paren
);
383 void bt_scan(const char *mark
, int count
) {}
386 static const char *config_key_expires(const char *var
)
388 if (!strcmp(var
, "ipc_debug_write"))
390 if (!strcmp(var
, "ipc_debug_read"))
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];
402 unsigned int shift
= 1;
407 sprintf(tbuf
[t
], "%llu bytes", n
);
411 while (n
>> (shift
* 10) > 1024 && shift
< strlen(suffix
) - 1)
414 sprintf(tbuf
[t
], "%0.2f %ciB",
415 (float)n
/ (float)(1L << (shift
* 10)), suffix
[shift
- 1]);
420 linked_item
*add_linked_item(linked_item
*list
, void *item
)
422 struct linked_item
*entry
= malloc(sizeof(linked_item
));
425 lerr("Failed to malloc(%u): %s", sizeof(linked_item
), strerror(errno
));
430 entry
->next_item
= list
;
434 const char *tv_delta(const struct timeval
*start
, const struct timeval
*stop
)
437 unsigned long weeks
, days
, hours
, mins
, secs
, usecs
;
440 secs
= stop
->tv_sec
- start
->tv_sec
;
441 stop_usec
= stop
->tv_usec
;
442 if (stop_usec
< start
->tv_usec
) {
444 stop_usec
+= 1000000;
446 usecs
= stop_usec
- start
->tv_usec
;
448 /* we only want 2 decimals */
452 weeks
= secs
/ 604800;
453 secs
-= weeks
* 604800;
455 secs
-= days
* 86400;
457 secs
-= hours
* 3600;
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
);
466 sprintf(buf
, "%luh %lum %lu.%03lus", hours
, mins
, secs
, usecs
);
468 sprintf(buf
, "%lud %luh %lum %lu.%03lus", days
, hours
, mins
, secs
, usecs
);
470 sprintf(buf
, "%luw %lud %luh %lum %lu.%03lus",
471 weeks
, days
, hours
, mins
, secs
, usecs
);
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
)
486 if (!comp
|| !csync
) {
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
);
502 if (!strcmp(v
->key
, "fetch") || !strcmp(v
->key
, "pull")) {
503 csync
->fetch
.cmd
= strdup(v
->value
);
507 * we ignore additional variables here, since the
508 * config sync script may want to add additional
516 static int grok_binlog_var(const char *key
, const char *value
)
518 if (!strcmp(key
, "binlog_dir")) {
519 binlog_dir
= strdup(value
);
526 int grok_common_var(struct cfg_comp
*config
, struct cfg_var
*v
)
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.");
539 expires
= config_key_expires(v
->key
);
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 */
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");
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");
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");
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
);
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
));